diff --git a/DBXUpdate-20100307-x64.cab b/DBXUpdate-20100307-x64.cab new file mode 100644 index 0000000000000000000000000000000000000000..b9159889515ee4691a86914c8d2a3d59b12bc585 Binary files /dev/null and b/DBXUpdate-20100307-x64.cab differ diff --git a/DBXUpdate-20140413-x64.cab b/DBXUpdate-20140413-x64.cab new file mode 100644 index 0000000000000000000000000000000000000000..646b53e3a3f7f0a90b6af877e67f5fe9f4f98208 Binary files /dev/null and b/DBXUpdate-20140413-x64.cab differ diff --git a/DBXUpdate-20160809-x64.cab b/DBXUpdate-20160809-x64.cab new file mode 100644 index 0000000000000000000000000000000000000000..c2b1e0a721ae51234749079b02c7c3038cbde0c5 Binary files /dev/null and b/DBXUpdate-20160809-x64.cab differ diff --git a/DBXUpdate-20200729-aa64.cab b/DBXUpdate-20200729-aa64.cab new file mode 100644 index 0000000000000000000000000000000000000000..cc8220493512b18e8aaced2292f989c3f677eb88 Binary files /dev/null and b/DBXUpdate-20200729-aa64.cab differ diff --git a/DBXUpdate-20200729-ia32.cab b/DBXUpdate-20200729-ia32.cab new file mode 100644 index 0000000000000000000000000000000000000000..c394f75c1498749219a8b1f5ebe70118f0d164c5 Binary files /dev/null and b/DBXUpdate-20200729-ia32.cab differ diff --git a/DBXUpdate-20200729-x64.cab b/DBXUpdate-20200729-x64.cab new file mode 100644 index 0000000000000000000000000000000000000000..cfb272a49af72fb7817818144dd4264dc0e15105 Binary files /dev/null and b/DBXUpdate-20200729-x64.cab differ diff --git a/centos-ca-secureboot.der b/centos-ca-secureboot.der new file mode 100644 index 0000000000000000000000000000000000000000..44a2563dee3f8eeecd5026306be46d2a8d89970d Binary files /dev/null and b/centos-ca-secureboot.der differ diff --git a/centossecureboot001.der b/centossecureboot001.der new file mode 100644 index 0000000000000000000000000000000000000000..e8216b1db725cd132b515a277368fc89a9d8fa3d Binary files /dev/null and b/centossecureboot001.der differ diff --git a/centossecureboot203.der b/centossecureboot203.der new file mode 100644 index 0000000000000000000000000000000000000000..5df41c254e31fd3a0f878bed4db4e851729e6ec5 Binary files /dev/null and b/centossecureboot203.der differ diff --git a/centossecurebootca2.der b/centossecurebootca2.der new file mode 100644 index 0000000000000000000000000000000000000000..42bdfcfbcda649796099fdc87bbe9dc58e2348d5 Binary files /dev/null and b/centossecurebootca2.der differ diff --git a/fwupd-1.7.4.tar.xz b/fwupd-1.7.4.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..897ab0d50ee6ff86ee140d533c910e677968b22c Binary files /dev/null and b/fwupd-1.7.4.tar.xz differ diff --git a/fwupd-1.8.6.tar.xz b/fwupd-1.8.6.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..f0385284d01474fdc81d7448b7eb57908183ec29 Binary files /dev/null and b/fwupd-1.8.6.tar.xz differ diff --git a/fwupd-1.8.6/.circleci/config.yml b/fwupd-1.8.6/.circleci/config.yml new file mode 100644 index 0000000000000000000000000000000000000000..1fa92e74fb0e3fa1b6ed5995e6e2f12b276bef35 --- /dev/null +++ b/fwupd-1.8.6/.circleci/config.yml @@ -0,0 +1,111 @@ +version: 2 +jobs: + build-ubuntu-x86_64: + machine: + image: ubuntu-2204:edge + steps: + - checkout + - run: + name: "Build container" + command: OS=ubuntu-x86_64 ./contrib/ci/generate_docker.py build + - run: + name: "Run build script" + command: docker run --privileged -e CI=true -t -v `pwd`:/github/workspace fwupd-ubuntu-x86_64 + - persist_to_workspace: + root: . + paths: + - "dist/share/doc" + + build-windows: + docker: + - image: fedora:36 + steps: + - checkout + - run: + name: "Build Win32" + command: ./contrib/ci/build_windows.sh + - persist_to_workspace: + root: . + paths: + - "dist/setup/*.msi" + - "dist/VERSION" + - "dist/news.txt" + - store_artifacts: + path: dist/setup + + publish-docs: + machine: + image: ubuntu-2204:edge + steps: + - attach_workspace: + at: . + - add_ssh_keys: + fingerprints: + - "d8:73:05:1b:7c:93:8c:12:41:78:15:3d:5d:af:b4:c2" + - run: + name: Clone docs + working_directory: dist/share/doc/fwupd + command: | + git clone --depth 1 git@github.com:fwupd/fwupd.github.io.git + - deploy: + name: Trigger docs deployment + working_directory: dist/share/doc/fwupd/fwupd.github.io + command: | + git config credential.helper 'cache --timeout=120' + git config user.email "info@fwupd.org" + git config user.name "Documentation deployment Bot" + rm -rf * + cp ../../libfwupd* ../*html . -R + git add . + git commit -a --allow-empty -m "Trigger deployment" + git push git@github.com:fwupd/fwupd.github.io.git + + publish-github-exe-release: + docker: + - image: circleci/golang:1.17.5 + steps: + - attach_workspace: + at: . + - run: + name: "Publish Release on GitHub" + command: | + go get github.com/tcnksm/ghr + VERSION=$(cat dist/VERSION) + BODY=$(cat dist/news.txt) + ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -b "${BODY}" ${VERSION} ./dist/setup/ +workflows: + version: 2 + main: + jobs: + - build-windows + - build-ubuntu-x86_64 + deploy: + jobs: + - build-ubuntu-x86_64: + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ + - build-windows: + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ + - publish-github-exe-release: + requires: + - build-windows + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ + - publish-docs: + requires: + - build-ubuntu-x86_64 + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ diff --git a/fwupd-1.8.6/.clang-format b/fwupd-1.8.6/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..27d9025cd780a65ea056a27e09fde425d8f646ab --- /dev/null +++ b/fwupd-1.8.6/.clang-format @@ -0,0 +1,51 @@ +--- +AlignAfterOpenBracket: 'Align' +AlignConsecutiveAssignments: 'false' +AlignConsecutiveDeclarations: 'false' +AlignConsecutiveMacros: 'true' +AlignOperands: 'true' +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AllowShortBlocksOnASingleLine: 'false' +AllowShortCaseLabelsOnASingleLine: 'false' +AllowShortFunctionsOnASingleLine: 'Inline' +AllowShortIfStatementsOnASingleLine: 'false' +AlwaysBreakAfterReturnType: 'All' +BinPackParameters: 'false' +BinPackArguments: 'false' +BreakBeforeBraces: 'Linux' +ColumnLimit: '100' +DerivePointerAlignment: 'false' +IndentCaseLabels: 'false' +IndentWidth: '8' +IncludeBlocks: 'Regroup' +KeepEmptyLinesAtTheStartOfBlocks: 'false' +MaxEmptyLinesToKeep: '1' +PointerAlignment: 'Right' +SortIncludes: 'true' +SpaceAfterCStyleCast: 'false' +SpaceBeforeAssignmentOperators : 'true' +SpaceBeforeParens: 'ControlStatements' +SpaceInEmptyParentheses: 'false' +SpacesInSquareBrackets: 'false' +TabWidth: '8' +UseTab: 'Always' +PenaltyBreakAssignment: '3' +PenaltyBreakBeforeFirstCallParameter: '15' +--- +Language: 'Proto' +--- +Language: 'Cpp' +IncludeCategories: + - Regex: '^"config.h"$' + Priority: '0' + - Regex: '' + Priority: '1' + - Regex: '^<' + Priority: '2' + - Regex: 'fwupd' + Priority: '3' + - Regex: '.*' + Priority: '4' +... diff --git a/fwupd-1.8.6/.editorconfig b/fwupd-1.8.6/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..8abc7283ffdc053a7c69a46e6edbb295b7bc2b3a --- /dev/null +++ b/fwupd-1.8.6/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf + +[*.py] +indent_style = space +indent_size = 4 + +[*.{c|h}] +indent_style = tab +indent_size = 8 + diff --git a/fwupd-1.8.6/.git-blame-ignore-revs b/fwupd-1.8.6/.git-blame-ignore-revs new file mode 100644 index 0000000000000000000000000000000000000000..9707d0013f9076ff917c2127258fd0348382640a --- /dev/null +++ b/fwupd-1.8.6/.git-blame-ignore-revs @@ -0,0 +1 @@ +72819f91c19e076ca383e6e9fd7c7a510c62792e diff --git a/fwupd-1.8.6/.gitconfig b/fwupd-1.8.6/.gitconfig new file mode 100644 index 0000000000000000000000000000000000000000..c78c54fdca17f4bd6a894c2ee06af273174de3c5 --- /dev/null +++ b/fwupd-1.8.6/.gitconfig @@ -0,0 +1,2 @@ +[blame] + ignoreRevsFile = .git-blame-ignore-revs diff --git a/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-general.md b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-general.md new file mode 100644 index 0000000000000000000000000000000000000000..a95cdb14d76c9058250c61b8b4bdadc995e8a8b5 --- /dev/null +++ b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-general.md @@ -0,0 +1,44 @@ +--- +name: Bug report (General) +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Steps to Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**fwupd version information** +Please provide the version of the daemon and client. + +```shell +fwupdmgr --version +``` + +Please note how you installed it (`apt`, `dnf`, `pacman`, source, etc): + +
+ +**fwupd device information** + +Please provide the output of the fwupd devices recognized in your system. + +```shell +fwupdmgr get-devices --show-all-devices +``` + +
+ +**Additional questions** + +- Operating system and version: +- Have you tried rebooting? +- Is this a regression? diff --git a/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-uefi.md b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-uefi.md new file mode 100644 index 0000000000000000000000000000000000000000..966b02682a7e44e6722c489dca550fa652828d04 --- /dev/null +++ b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-uefi.md @@ -0,0 +1,62 @@ +--- +name: Bug report (UEFI Updates) +about: Issues involving UEFI device updates +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Steps to Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**fwupd version information** +Please provide the version of the daemon and client. + +```shell +fwupdmgr --version +``` + +Please note how you installed it (`apt`, `dnf`, `pacman`, source, etc): + +
+ +**fwupd device information** + +Please provide the output of the fwupd devices recognized in your system. + +```shell +fwupdmgr get-devices --show-all-devices +``` + +
+ +**System UEFI configuration** +Please provide the output of the following commands: + +```shell +efibootmgr -v +``` + +```shell +efivar -l | grep fw +``` + +```shell +tree /boot +``` + +**Additional questions** + +- Operating system and version: +- Have you tried rebooting? +- Is this a regression? +- Are you using an NVMe disk? +- Is secure boot enabled? +- Is this a Lenovo system with 'Boot Order Lock' turned on in the BIOS? diff --git a/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-wd19.md b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-wd19.md new file mode 100644 index 0000000000000000000000000000000000000000..f8a1f926430a71696e0ce09ecf336e870ef1efe4 --- /dev/null +++ b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/bug-report-wd19.md @@ -0,0 +1,70 @@ +--- +name: Bug report (Dell WD19) +about: Create a report to help us improve +title: 'Dell WD19 upgrade issue' +labels: bug +assignees: 'cragw' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Steps to Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**fwupd version information** +Please provide the version of the daemon and client. + +```shell +fwupdmgr --version +``` + +Please note how you installed it (`apt`, `dnf`, `pacman`, source, etc): + +
+ +**fwupd device information** + +Please provide the output of the external fwupd devices recognized in your system. + +```shell +fwupdmgr get-devices --filter=~internal +``` + +
+ +**Dock SKU** +Please mention which module is installed in your WD19. + +- [ ] WD19 (Single-C) +- [ ] WD19TB (Thunderbolt) +- [ ] WD19DC (Dual-C) + +**Peripherals connected to the dock** +Please describe all devices connected to the dock. Be as specific as possible, +including USB devices, hubs, monitors, and downstream type-C devices. + +**Verbose daemon logs** +First enable daemon verbose logs collection. + +```shell +fwupdmgr modify-config "VerboseDomains" "*" +``` + +Then try to reproduce the issue. Even if it doesn't reproduce, please attach the +daemon verbose logs collected from the system journal. + +```shell +journalctl -b -u fwupd.service +``` + +**Additional questions** + +- Operating system and version: +- Have you tried unplugging the dock or any peripherals from your machine? +- Have you tried to power cycle the dock from the AC adapter? +- Is this a regression? diff --git a/fwupd-1.8.6/.github/ISSUE_TEMPLATE/feature_request.md b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000000000000000000000000000000..11fc491ef1dae316f2b06bbb40eaba9c757fdfd1 --- /dev/null +++ b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/fwupd-1.8.6/.github/ISSUE_TEMPLATE/question.md b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000000000000000000000000000000000000..d0b97137694ec5141549ae37e6f95a4c14bc7afd --- /dev/null +++ b/fwupd-1.8.6/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,18 @@ +--- +name: Question +about: Ask a question about the project or how to do something +title: '' +labels: question +assignees: '' + +--- + +**Describe the question** +A clear and concise description of what you are wondering about. + +**fwupd version information** +Please provide the version of the daemon and client if applicable to your current installation of `fwupd`. + +```shell +fwupdmgr --version +``` diff --git a/fwupd-1.8.6/.github/dependabot.yml b/fwupd-1.8.6/.github/dependabot.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ace4600a1f26e6892982f3e2f069ebfab108d87 --- /dev/null +++ b/fwupd-1.8.6/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/fwupd-1.8.6/.github/pull_request_template.md b/fwupd-1.8.6/.github/pull_request_template.md new file mode 100644 index 0000000000000000000000000000000000000000..a7e1e6e2e1d1aee819eda36f82c2318d9dccbf1f --- /dev/null +++ b/fwupd-1.8.6/.github/pull_request_template.md @@ -0,0 +1,6 @@ +Type of pull request: + +- [ ] New plugin (Please include [new plugin checklist](https://github.com/fwupd/fwupd/wiki/New-plugin-checklist)) +- [ ] Code fix +- [ ] Feature +- [ ] Documentation diff --git a/fwupd-1.8.6/.github/stale.yml b/fwupd-1.8.6/.github/stale.yml new file mode 100644 index 0000000000000000000000000000000000000000..c398d030201bfa727e51fd1ddcc31cb0a1aa2717 --- /dev/null +++ b/fwupd-1.8.6/.github/stale.yml @@ -0,0 +1,27 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 30 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - enhancement + - regression +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. + Please note: We are just a few people who contribute to a shared project, + and it's impossible for us to fix every bug with such limited resources. + + If you want to investigate and try to help solve this yourself, we will + review all pull requests from new contributors. + + If this is issue is important to you for your business please talk with + your technical account manager about arranging resources to solve this issue. + You might even consider hiring someone to write the code if you're unable + to do so yourself, e.g. see: https://fwupd.org/lvfs/docs/consulting + +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/fwupd-1.8.6/.github/workflows/codeql-analysis.yml b/fwupd-1.8.6/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000000000000000000000000000000000..57e4b2a585cc75605d9515e11ff3e1a06744a81d --- /dev/null +++ b/fwupd-1.8.6/.github/workflows/codeql-analysis.yml @@ -0,0 +1,46 @@ +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-20.04 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp', 'python' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Install dependencies + run: > + sudo apt-get update && + sudo ./contrib/ci/fwupd_setup_helpers.py install-dependencies --yes -o ubuntu && + python3 -m pip install --user "meson >= 0.60.0" + + - name: Build + run: | + mkdir -p $GITHUB_WORKSPACE/build + cd $GITHUB_WORKSPACE/build + meson .. -Dman=false -Defi_binary=false -Dplugin_uefi_capsule_splash=false --prefix=$GITHUB_WORKSPACE/dist + ninja + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/fwupd-1.8.6/.github/workflows/create_containers.yml b/fwupd-1.8.6/.github/workflows/create_containers.yml new file mode 100644 index 0000000000000000000000000000000000000000..79b9eb2a89fb97f2d2ae5341acce5222e8634dec --- /dev/null +++ b/fwupd-1.8.6/.github/workflows/create_containers.yml @@ -0,0 +1,41 @@ +name: Create containers +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +permissions: + contents: read + +jobs: + push_to_registry: + permissions: + packages: write # for docker/build-push-action + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [fedora, debian-x86_64, arch, debian-i386, void] + + steps: + - name: Check out the repo + uses: actions/checkout@v3 + - name: "Generate Dockerfile" + env: + OS: ${{ matrix.os }} + run: ./contrib/ci/generate_docker.py + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Push to GitHub Packages + uses: docker/build-push-action@v3 + with: + context: . + push: true + tags: ghcr.io/fwupd/fwupd/fwupd-${{matrix.os}}:latest diff --git a/fwupd-1.8.6/.github/workflows/main.yml b/fwupd-1.8.6/.github/workflows/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..012568a992908bc1041223ef9d59c77f66fa19ca --- /dev/null +++ b/fwupd-1.8.6/.github/workflows/main.yml @@ -0,0 +1,114 @@ +name: Continuous Integration +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + pre-commit: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Refresh dependencies + run: sudo apt update + - name: Install dependencies + run: sudo apt install shellcheck clang-format -y + - name: Run pre-commit hooks + run: | + ./contrib/setup + source venv/bin/activate + sed -i "/no-commit-to-branch/,+1d" .pre-commit-config.yaml + pre-commit run --hook-stage commit --all-files + abi: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Refresh dependencies + run: sudo apt update + - name: Install dependencies + run: | + sudo ./contrib/ci/fwupd_setup_helpers.py install-dependencies -o ubuntu --yes + python3 -m pip install --user "meson >= 0.60.0" + - name: Check ABI + run: ./contrib/ci/check-abi $(git describe --abbrev=0 --tags) $(git rev-parse HEAD) + + openbmc: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Refresh dependencies + run: sudo apt update + - name: Install dependencies + run: | + sudo ./contrib/ci/fwupd_setup_helpers.py install-dependencies -o ubuntu --yes + python3 -m pip install --user "meson >= 0.60.0" + - name: Build + run: | + ./contrib/build-openbmc.sh + ninja -C .. + + build: + runs-on: ubuntu-latest + strategy: + matrix: + os: [fedora, debian-x86_64, arch, debian-i386] + steps: + - uses: actions/checkout@v3 + - name: Docker login + run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $GITHUB_TOKEN + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Build in container + env: + CI_NETWORK: true + CI: true + run: | + echo $GITHUB_WORKSPACE + docker run --privileged -e CI=true -t -v $GITHUB_WORKSPACE:/github/workspace docker.pkg.github.com/fwupd/fwupd/fwupd-${{matrix.os}}:latest + + macos: + runs-on: macos-12 + steps: + - name: install dependencies + run: brew install meson gcab libusb gobject-introspection sqlite libarchive json-glib curl gnutls protobuf-c vala + - uses: actions/checkout@v3 + - name: configure + run: ./contrib/ci/build_macos.sh + - name: build + run: ninja -C build-macos + + fuzzing: + permissions: + actions: read # to fetch the artifacts (google/oss-fuzz/infra/cifuzz/actions/run_fuzzers) + contents: read # to clone the repo (google/oss-fuzz/infra/cifuzz/actions/run_fuzzers) + + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'fwupd' + dry-run: false + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'fwupd' + fuzz-seconds: 150 + dry-run: false + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts + diff --git a/fwupd-1.8.6/.gitignore b/fwupd-1.8.6/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3858bb7b8e00d18bf1c953706bcf33c022b8ad01 --- /dev/null +++ b/fwupd-1.8.6/.gitignore @@ -0,0 +1,38 @@ +/build +/build-win32 +/dist +/.vscode +.vscode-ctags +/build-dir +/.flatpak-builder +/repo +*.flatpak +*.snap +/fwupd_source.tar.bz2 +/parts +/prime +/stage +/snap/.snapcraft +/libxmlb +/*.deb +/*.ddeb +/*.changes +/*.buildinfo +/fwupd*.build +/*.dsc +/*.xz +/*.gz +/venv +__pycache__ +plugins/acpi-dmar/tests/ +plugins/acpi-facp/tests/ +plugins/ata/tests/ +plugins/dfu/tests/ +plugins/nvme/tests/ +plugins/synaptics-mst/tests/ +plugins/synaptics-prometheus/tests/ +plugins/tpm-eventlog/tests/ +plugins/uefi-dbx/tests/ +.buildconfig +.ossfuzz +*.rej diff --git a/fwupd-1.8.6/.gitmodules b/fwupd-1.8.6/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..70f1092f0475c68d744b9ccf9a3ae6b47946e892 --- /dev/null +++ b/fwupd-1.8.6/.gitmodules @@ -0,0 +1,3 @@ +[submodule "contrib/flatpak"] + path = contrib/flatpak + url = https://github.com/flathub/org.freedesktop.fwupd diff --git a/fwupd-1.8.6/.markdownlint.json b/fwupd-1.8.6/.markdownlint.json new file mode 100644 index 0000000000000000000000000000000000000000..41b3c04fdf283a2abac1e14a82cc77a2d5508b94 --- /dev/null +++ b/fwupd-1.8.6/.markdownlint.json @@ -0,0 +1,8 @@ +{ + "default": true, + "MD033": false, + "MD013": { + "tables": false, + "line_length": 1000 + } +} diff --git a/fwupd-1.8.6/.pre-commit-config.yaml b/fwupd-1.8.6/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c762a87789f57bc41d87fafa93a301fc1fff8481 --- /dev/null +++ b/fwupd-1.8.6/.pre-commit-config.yaml @@ -0,0 +1,84 @@ +default_stages: [commit] +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: no-commit-to-branch + args: [--branch, main, --pattern, 1_.*_X] + - id: check-added-large-files + - id: check-byte-order-marker + - id: check-executables-have-shebangs + - id: forbid-new-submodules + - id: check-yaml + exclude: '.clang-format' + - id: check-json + - id: check-symlinks + - id: check-xml + - id: end-of-file-fixer + types_or: [c, shell, python, proto] + - id: trailing-whitespace + types_or: [c, shell, python, xml] + - id: check-docstring-first + - id: check-merge-conflict + - id: mixed-line-ending + args: [--fix=lf] +- repo: https://github.com/codespell-project/codespell + rev: v2.1.0 + hooks: + - id: codespell + args: ['--config', './contrib/codespell.cfg', --write-changes] +- repo: https://github.com/ambv/black + rev: 22.3.0 + hooks: + - id: black +- repo: local + hooks: + - id: check-deprecated + name: check for use of any deprecated items + language: script + entry: ./contrib/ci/check-deprecated.sh + - id: check-null-false-returns + name: check for null / false return mistmatch + language: script + entry: ./contrib/ci/check-null-false-returns.py + - id: check-potfiles + name: check for missing translated files from potfiles + language: script + entry: ./contrib/ci/check-potfiles.py + - id: check-finalizers + name: check for missing GObject parent finalize + language: script + entry: ./contrib/ci/check-finalizers.py + - id: check-headers + name: check for superfluous includes + language: script + entry: ./contrib/ci/check-headers.py + - id: check-quirks + name: check quirk style + language: script + entry: ./contrib/ci/check-quirks.py + - id: shellcheck + name: check shellscript style + language: system + entry: shellcheck --severity=warning -e SC2068 + types: [shell] + - id: run-tests + name: run tests before pushing + language: script + entry: ./contrib/run-tests.sh + stages: [push] + - id: clang-format + name: clang-format + language: script + entry: ./contrib/reformat-code.py + types: [c] + - id: check-license + name: Check license header + types_or: [shell, c, python] + language: script + entry: ./contrib/ci/check-license.py +- repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.27.1 + hooks: + - id: markdownlint + args: ['--fix', '--ignore', '.github'] diff --git a/fwupd-1.8.6/.tx/config b/fwupd-1.8.6/.tx/config new file mode 100644 index 0000000000000000000000000000000000000000..ff5e554ac6ab4398d3d50a626ba57b9610c90147 --- /dev/null +++ b/fwupd-1.8.6/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[fwupd.main] +file_filter = po/.po +source_file = po/fwupd.pot +source_lang = en +type = PO diff --git a/fwupd-1.8.6/AUTHORS b/fwupd-1.8.6/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..026b70589356aa8296495aca48d60565804b001f --- /dev/null +++ b/fwupd-1.8.6/AUTHORS @@ -0,0 +1 @@ +Richard Hughes diff --git a/fwupd-1.8.6/CODE_OF_CONDUCT.md b/fwupd-1.8.6/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000000000000000000000000000000..0bc4fc038c77b646c4971e55d7dd5eea3d6b2f34 --- /dev/null +++ b/fwupd-1.8.6/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fwupd@googlegroups.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/fwupd-1.8.6/COMMITMENT b/fwupd-1.8.6/COMMITMENT new file mode 100644 index 0000000000000000000000000000000000000000..bfa237f419b4dfa0c6c9c0f0979779feefa2bc52 --- /dev/null +++ b/fwupd-1.8.6/COMMITMENT @@ -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 --git a/fwupd-1.8.6/CONTRIBUTING.md b/fwupd-1.8.6/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..48cd2355419f2946e5bb946d7e790b34d2d4899a --- /dev/null +++ b/fwupd-1.8.6/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributor Guidelines + +## Getting started + +To set up your local environment, from the top level of the checkout run + +```shell +./contrib/setup +``` + +This will create pre-commit hooks to fixup many code style issues before your +code is submitted. + +On some Linux distributions this will install all build dependencies needed +to compile fwupd as well. + +## Coding Style + +The coding style to respect in this project is very similar to most +GLib projects. In particular, the following rules are largely adapted +from the PackageKit Coding Style. + +* 8-space tabs for indentation + +* Prefer lines of less than <= 100 columns + +* No spaces between function name and braces (both calls and macro + declarations) + +* If function signature/call fits in a single line, do not break it + into multiple lines + +* Prefer descriptive names over abbreviations (unless well-known) + and shortening of names. e.g `device` not `dev` + +* 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: + + // Fetch the document + fetch_the_document(); + +* Comments should not start with a capital letter or end with a full stop and + should be C-style, not C++-style, e.g. `/* this */` not `// this` + +* Each object should go in a separate .c file and be named according + to the class + +* Use g_autoptr() and g_autofree whenever possible, and avoid `goto out` + error handling + +* Failing methods should return FALSE with a suitable `GError` set + +* Trailing whitespace is forbidden + +* Pointers should be checked for NULL explicitly, e.g. `foo != NULL` not `!foo` + +`./contrib/reformat-code.py` can be used in order to get automated +formatting. Calling the script without arguments formats the current +patch while passing commits will do formatting on everything changed since that +commit. diff --git a/fwupd-1.8.6/COPYING b/fwupd-1.8.6/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..4362b49151d7b34ef83b3067a8f9c9f877d72a0e --- /dev/null +++ b/fwupd-1.8.6/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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 library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/fwupd-1.8.6/MAINTAINERS b/fwupd-1.8.6/MAINTAINERS new file mode 100644 index 0000000000000000000000000000000000000000..0634d575e015e6fce25e90e3b28b7a8293f29728 --- /dev/null +++ b/fwupd-1.8.6/MAINTAINERS @@ -0,0 +1,2 @@ +Richard Hughes +Mario Limonciello diff --git a/fwupd-1.8.6/README.md b/fwupd-1.8.6/README.md new file mode 100644 index 0000000000000000000000000000000000000000..74aaa367d425c073ce651a0221f2de5dbd22ccb0 --- /dev/null +++ b/fwupd-1.8.6/README.md @@ -0,0 +1,195 @@ +# fwupd + +[![Build Status](https://github.com/fwupd/fwupd/actions/workflows/main.yml/badge.svg)](https://github.com/fwupd/fwupd/actions/workflows/main.yml) +[![CodeQL](https://github.com/fwupd/fwupd/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/fwupd/fwupd/actions/workflows/codeql-analysis.yml) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/10744/badge.svg)](https://scan.coverity.com/projects/10744) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fwupd.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fwupd) +[![CircleCI](https://circleci.com/gh/fwupd/fwupd/tree/main.svg?style=svg)](https://circleci.com/gh/fwupd/fwupd/tree/main) + +This project aims to make updating firmware on Linux automatic, safe and reliable. + +Additional information is available [at the website](https://fwupd.org/). + +## Where to get help? + +- GitHub issues & discussions in [this repository](https://github.com/fwupd/fwupd) +- Libera IRC channel `#fwupd`. + You can join through Libera's [IRC](https://libera.chat/) + or via the IRC bridge on [Matrix](https://matrix.to/#/#fwupd:libera.chat). + +## Compiling + +The most up to date compilation instructions are available in the [Wiki](https://github.com/fwupd/fwupd/wiki/Compilation). + +**NOTE:** In most cases end users should never compile fwupd from scratch; it's a +complicated project with dozens of dependencies (and as many configuration options) +and there's just too many things that can go wrong. + +Users should just have fwupd installed and updated by their distro, managed and +tested by the package maintainer. +The distribution will have also done some testing with how fwupd interacts with +other software on your system, for instance using GNOME Software. + +Installing fwupd using [Snap](https://github.com/fwupd/fwupd/wiki/fwupd-snap) +or using [Flatpak](https://github.com/fwupd/fwupd/wiki/fwupd-flatpak) might be +useful to update a specific device on the command line that needs a bleeding +edge fwupd version, but it should not be considered as a replacement to the +distro-provided system version. + +## LVFS + +This project is configured by default to download firmware from the [Linux Vendor +Firmware Service (LVFS)](https://fwupd.org/). + +This service is available to all OEMs and firmware creators who would like to make +their firmware available to Linux users. + +You can find more information about the technical details of creating a firmware +capsule in the hardware vendors section of the [fwupd website](https://fwupd.org). + +## Basic usage flow (command line) + +If you have a device with firmware supported by fwupd, this is how you will check +for updates and apply them using fwupd's command line tools. + +`# fwupdmgr get-devices` + +This will display all devices detected by fwupd. + +`# fwupdmgr refresh` + +This will download the latest metadata from LVFS. + +`# fwupdmgr get-updates` + +If updates are available for any devices on the system, they'll be displayed. + +`# fwupdmgr update` + +This will download and apply all updates for your system. + +- Updates that can be applied live will be done immediately. +- Updates that run at bootup will be staged for the next reboot. + +You can find more information about the update workflow in the end +users section of the [fwupd website](https://fwupd.org). + +## Reporting status + +fwupd will encourage users to report both successful and failed updates back +to LVFS. This is an optional feature, but encouraged as it provides valuable +feedback to LVFS administrators and OEM developers regarding firmware update +process efficacy. + +The privacy policy regarding this data can be viewed on the [fwupd website](https://fwupd.org/privacy). + +To report the status of an update run: + +`# fwupdmgr report-history` + + 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 organization. + +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. + +## Local metadata + +Local metadata can be saved in `/var/lib/fwupd/local.d` or `/usr/share/fwupd/local.d` +which are scanned at daemon startup. This can be used to add site-specific BKC +tags to existing metadata stores. For instance: + + + + + + 3ef35d3b-ceeb-5e27-8c0a-ac25f90367ab + 2ef35d3b-ceeb-5e27-8c0a-ac25f90367ac + 1ef35d3b-ceeb-5e27-8c0a-ac25f90367ad + + + + + + + mycompanyname-2022q1 + + + + +This then appears when getting the releases for that specific GUID: + + fwupdmgr get-releases --json 3ef35d3b-ceeb-5e27-8c0a-ac25f90367ab + { + "Releases" : [ + { + ... + "Version" : "225.53.1649", + "Tags" : [ + "mycompanyname-2022q1" + ], + ... + }, + { + ... + "Version" : "224.48.1605", + "Tags" : [ + "mycompanyname-2022q1" + ], + ... + }, + { + ... + "Version" : "224.45.1389", + ... + } + ] + } + +## Other frontends + +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 center, + 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. + +## Fuzzing + +There are several automated fuzzing tests in fwupd. These take some time to run: + + CC=hfuzz-clang meson --default-library=static \ + -Dudevdir=/tmp -Dsystemd_root_prefix=/tmp \ + -Dplugin_redfish=disabled -Dcurl=disabled \ + -Dintrospection=false ../ + ninja install + ninja fuzz-firmware + ninja fuzz-tpm-eventlog diff --git a/fwupd-1.8.6/RELEASE b/fwupd-1.8.6/RELEASE new file mode 100644 index 0000000000000000000000000000000000000000..633706746fd2365ba94b1065d9629ba179299341 --- /dev/null +++ b/fwupd-1.8.6/RELEASE @@ -0,0 +1,64 @@ +fwupd Release Notes + +Forking stable branch: + +When forking main into a stable 1_7_X, be sure to disable the following CI jobs: + * publish-docs + * publish-stable + +To make sure it's done right, you can reference commit 433e809318c68c9ab6d4ae50ee9c4312503185d8 + +Write release entries: + +git log --format="%s" --cherry-pick --right-only 1.8.5... | 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 THIS IS CORRECT +export release_ver="1.8.6" + +git commit -a -m "Release fwupd ${release_ver}" --no-verify +git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" + +git push --tags +git push + +3. Generate the tarball: + +ninja dist + +3a. Generate the additional verification metadata + +gpg -b -a meson-dist/fwupd-${release_ver}.tar.xz + +4. Upload tarball: + +scp meson-dist/fwupd-${release_ver}.tar.* hughsient@people.freedesktop.org:~/public_html/releases + +5. Do post release version bump in meson.build + +6. Commit changes: + +git commit -a -m "trivial: post release version bump" --no-verify +git push + +7. Update flatpak package for new release: + +https://github.com/flathub/org.freedesktop.fwupd + +8. Promote snap package from edge to stable: + +https://snapcraft.io/fwupd/releases +or + +snapcraft promote fwupd --from-channel edge --to-channel stable diff --git a/fwupd-1.8.6/SECURITY.md b/fwupd-1.8.6/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..16095665462e49311589f597817f2acce9a63cd1 --- /dev/null +++ b/fwupd-1.8.6/SECURITY.md @@ -0,0 +1,40 @@ +# 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 `main`, and `1.7.x`, branches are fully supported by the upstream authors. +Additionally, the `1.5.x` and `1.6.x` branches are supported for security fixes. + +| Version | Supported | +| ------- | ------------------ | +| 1.8.x | :heavy_check_mark: | +| 1.7.x | :heavy_check_mark: | +| 1.6.x | :white_check_mark: | +| 1.5.x | :white_check_mark: | +| 1.4.x | :x: | +| 1.3.x | :x: | +| 1.2.x | :x: | +| 1.1.x | :x: | +| 1.0.x | :x: | +| 0.9.x | :x: | +| 0.8.x | :x: | + +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. + +## 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 bounty program, but we're happy to credit you in updates +if this is what you would like us to do. diff --git a/fwupd-1.8.6/contrib/PKGBUILD b/fwupd-1.8.6/contrib/PKGBUILD new file mode 100644 index 0000000000000000000000000000000000000000..8577f43b7c6db073c0edc4e1a814b350835e6560 --- /dev/null +++ b/fwupd-1.8.6/contrib/PKGBUILD @@ -0,0 +1,46 @@ +# Maintainer: Bruno Pagani (a.k.a. ArchangeGabriel) +# Contributor: Mirco Tischler + +pkgname=fwupd +pkgver=dummy +pkgrel=1 +pkgdesc='A system daemon to allow session software to update firmware' +arch=('i686' 'x86_64') +url='https://github.com/fwupd/fwupd' +license=('GPL2') +depends=('libgusb' 'modemmanager' 'tpm2-tss') +makedepends=('meson' 'valgrind' 'gobject-introspection' 'gi-docgen' 'git' + 'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala' + 'curl' 'polkit' 'gcab' 'xz') + +pkgver() { + cd ${pkgname} + + VERSION=$(git describe | sed 's/-/.r/;s/-/./') + [ -z $VERSION ] && VERSION=$(head meson.build | grep ' version:' | cut -d \' -f2) + + echo $VERSION +} + +build() { + cd ${pkgname} + if [ -n "$CI" ]; then + export CI="--wrap-mode=default" + fi + arch-meson -D b_lto=false $CI ../build \ + -Dplugin_intel_spi=true \ + -Dplugin_powerd=disabled \ + -Ddocs=enabled \ + -Defi_binary=false \ + -Dsupported_build=enabled + + ninja -v -C ../build +} + +check() { + CACHE_DIRECTORY=/tmp ninja -C build test +} + +package() { + DESTDIR="${pkgdir}" ninja -C build install +} diff --git a/fwupd-1.8.6/contrib/README.md b/fwupd-1.8.6/contrib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c1f1e26a299082ec7dfc9df8bd93442e042e456b --- /dev/null +++ b/fwupd-1.8.6/contrib/README.md @@ -0,0 +1,89 @@ +# Distribution packages + +The relevant packaging necessary to generate *RPM*, *DEB* and *PKG* distribution packages is contained here. +It is used regularly for continuous integration using [Travis CI](http://travis-ci.org). The generated packages can be used on a distribution such as Fedora, Debian, Ubuntu or Arch Linux. + +The build can be performed using Linux containers with [Docker](https://www.docker.com). + +## RPM packages + +A Dockerfile for Fedora can be generated in `contrib`. + +To prepare the Docker container run this command: + +```shell +OS=fedora ./generate_docker.py build +``` + +To build the RPMs run this command (from the root of your git checkout): + +```shell +docker run --privileged -t -v `pwd`:/github/workspace fwupd-fedora +``` + +RPMs will be made available in your working directory when complete. + +To build additional RPM packages for Qubes OS (fwupd-qubes-dom0 and +fwupd-qubes-vm) add `QUBES=true` environment variable: + +```shell +docker run --privileged -e QUBES=true -t -v `pwd`:/github/workspace fwupd-fedora +``` + +## DEB packages + +A Dockerfile for Debian or Ubuntu can be generated in `contrib`. + +To prepare the Docker container run one of these commands: + +```shell +OS=debian-x86_64 ./generate_docker.py build +OS=debian-i386 ./generate_docker.py build +OS=ubuntu-x86_64 ./generate_docker.py build +``` + +To build the DEBs run one of these commands (from the root of your git checkout): + +```shell +docker run --privileged -t -v `pwd`:/github/workspace fwupd-debian-x86_64 +docker run --privileged -t -v `pwd`:/github/workspace fwupd-debian-i386 +docker run --privileged -t -v `pwd`:/github/workspace fwupd-ubuntu-x86_64 +``` + +DEBs will be made available in your working directory when complete. + +To build additional DEB package for Qubes OS (fwupd-qubes-vm-whonix) +add `QUBES=true` environment variable: + +```shell +docker run --privileged -t -v `pwd`:/github/workspace fwupd-debian-x86_64-qubes +``` + +## PKG packages + +A Dockerfile for Arch can be generated in `contrib`. + +To prepare the Docker container run this command: + +```shell +OS=arch ./generate_docker.py +``` + +To build the PKGs run this command (from the root of your git checkout): + +```shell +docker run -t -v `pwd`:/build fwupd-arch +``` + +PKGs will be made available in your working directory when complete. + +## Additional packages + +Submissions for generating additional packages for other distribution mechanisms are also welcome. +All builds should occur in Docker containers. + +Please feel free to submit the following: + +* Dockerfile for the container for your distro +* Relevant technical packaging scripts (such as ebuilds, spec file etc) +* A shell script that can be launched in the container to generate distribution packages diff --git a/fwupd-1.8.6/contrib/build-openbmc.sh b/fwupd-1.8.6/contrib/build-openbmc.sh new file mode 100755 index 0000000000000000000000000000000000000000..4427ebd09af9efa8f045e34dd6e279489bd970ab --- /dev/null +++ b/fwupd-1.8.6/contrib/build-openbmc.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +meson ../ \ + -Dauto_features=disabled \ + -Dbash_completion=false \ + -Dcompat_cli=false \ + -Dfish_completion=false \ + -Dfirmware-packager=false \ + -Dhsi=disabled \ + -Dman=false \ + -Dmetainfo=false \ + -Dtests=false \ + -Dudevdir=/tmp \ + -Dsystemd_root_prefix=/tmp \ + $@ diff --git a/fwupd-1.8.6/contrib/build-windows.sh b/fwupd-1.8.6/contrib/build-windows.sh new file mode 100755 index 0000000000000000000000000000000000000000..c60a8024e36a0d1b8c6eb9e8404f04e882c3361a --- /dev/null +++ b/fwupd-1.8.6/contrib/build-windows.sh @@ -0,0 +1,93 @@ +#!/bin/sh +set -e + +root=$(pwd) +export DESTDIR=${root}/dist +build=$root/build-win32 +mkdir -p "$build" && cd "$build" + +# install deps +if [ ! -f /usr/share/mingw/toolchain-mingw64.meson ]; then + ./contrib/ci/fwupd_setup_helpers.py -v mingw64 install-dependencies +fi + +# try to keep this and ../contrib/ci/build_windows.sh in sync as much as makes sense +meson .. \ + --cross-file=/usr/share/mingw/toolchain-mingw64.meson \ + --cross-file=../contrib/mingw64.cross \ + --prefix=/ \ + --sysconfdir="etc" \ + --libexecdir="bin" \ + --bindir="bin" \ + -Dbuild=all \ + -Dman=false \ + -Dfish_completion=false \ + -Dbash_completion=false \ + -Dfirmware-packager=false \ + -Dmetainfo=false \ + -Dcompat_cli=false \ + -Dsoup_session_compat=false \ + -Dgcab:introspection=false \ + -Dgcab:docs=false \ + -Dgcab:nls=false \ + -Dgcab:vapi=false \ + -Dgcab:tests=false \ + -Dlibxmlb:introspection=false \ + -Dlibxmlb:gtkdoc=false \ + -Dlibjcat:man=false \ + -Dlibjcat:gpg=false \ + -Dlibjcat:tests=false \ + -Dlibjcat:introspection=false \ + -Dgusb:tests=false \ + -Dgusb:docs=false \ + -Dgusb:introspection=false \ + -Dgusb:vapi=false + +# run tests +export WINEPATH="/usr/x86_64-w64-mingw32/sys-root/mingw/bin/;$build/libfwupd/;$build/libfwupdplugin/;$build/subprojects/libxmlb/src/;$build/subprojects/gcab/libgcab/;$build/subprojects/libjcat/libjcat/;$build/subprojects/gusb/gusb/" +ninja -C "$build" install +ninja -C "$build" test + +MINGW32BINDIR=/usr/x86_64-w64-mingw32/sys-root/mingw/bin + +#disable motd for Windows +sed -i 's,UpdateMotd=.*,UpdateMotd=false,' "$DESTDIR/etc/fwupd/daemon.conf" + +# copy deps +cp -f -v \ + $MINGW32BINDIR/gspawn-win64-helper-console.exe \ + $MINGW32BINDIR/gspawn-win64-helper.exe \ + $MINGW32BINDIR/iconv.dll \ + $MINGW32BINDIR/libarchive-13.dll \ + $MINGW32BINDIR/libbrotlicommon.dll \ + $MINGW32BINDIR/libbrotlidec.dll \ + $MINGW32BINDIR/libbz2-1.dll \ + $MINGW32BINDIR/libcrypto-1_1-x64.dll \ + $MINGW32BINDIR/libcurl-4.dll \ + $MINGW32BINDIR/libffi-*.dll \ + $MINGW32BINDIR/libgcc_s_seh-1.dll \ + $MINGW32BINDIR/libgio-2.0-0.dll \ + $MINGW32BINDIR/libglib-2.0-0.dll \ + $MINGW32BINDIR/libgmodule-2.0-0.dll \ + $MINGW32BINDIR/libgmp-10.dll \ + $MINGW32BINDIR/libgnutls-30.dll \ + $MINGW32BINDIR/libgobject-2.0-0.dll \ + $MINGW32BINDIR/libgusb-2.dll \ + $MINGW32BINDIR/libhogweed-*.dll \ + $MINGW32BINDIR/libidn2-0.dll \ + $MINGW32BINDIR/libintl-8.dll \ + $MINGW32BINDIR/libjson-glib-1.0-0.dll \ + $MINGW32BINDIR/liblzma-5.dll \ + $MINGW32BINDIR/libnettle-*.dll \ + $MINGW32BINDIR/libp11-kit-0.dll \ + $MINGW32BINDIR/libpcre-1.dll \ + $MINGW32BINDIR/libsqlite3-0.dll \ + $MINGW32BINDIR/libssh2-1.dll \ + $MINGW32BINDIR/libssl-1_1-x64.dll \ + $MINGW32BINDIR/libssp-0.dll \ + $MINGW32BINDIR/libtasn1-6.dll \ + $MINGW32BINDIR/libusb-1.0.dll \ + $MINGW32BINDIR/libwinpthread-1.dll \ + $MINGW32BINDIR/libxml2-2.dll \ + $MINGW32BINDIR/zlib1.dll \ + "$DESTDIR/bin/" diff --git a/fwupd-1.8.6/contrib/ci/Dockerfile-arch.in b/fwupd-1.8.6/contrib/ci/Dockerfile-arch.in new file mode 100644 index 0000000000000000000000000000000000000000..e30765d2a85b1863148f66f5e35cd3f14b957785 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/Dockerfile-arch.in @@ -0,0 +1,13 @@ +FROM archlinux:latest +%%%OS%%% +ENV LANG en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 +ENV CI_NETWORK true +RUN echo fubar > /etc/machine-id +RUN sed "s,#en_US.UTF-8,en_US.UTF-8," /etc/locale.gen -i +RUN echo "LANG=en_US.UTF-8" > /etc/locale.conf +RUN locale-gen +RUN pacman -Syu --noconfirm archlinux-keyring +%%%INSTALL_DEPENDENCIES_COMMAND%%% +WORKDIR /github/workspace +CMD ["./contrib/ci/arch.sh"] diff --git a/fwupd-1.8.6/contrib/ci/Dockerfile-debian.in b/fwupd-1.8.6/contrib/ci/Dockerfile-debian.in new file mode 100644 index 0000000000000000000000000000000000000000..82e4c189862cbba7471051f353a7c7111f92d565 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/Dockerfile-debian.in @@ -0,0 +1,9 @@ +FROM %%%ARCH_PREFIX%%%debian:testing +%%%OS%%% +ENV CI_NETWORK true +RUN echo fubar > /etc/machine-id +%%%ARCH_SPECIFIC_COMMAND%%% +%%%INSTALL_DEPENDENCIES_COMMAND%%% +RUN apt install -yq --no-install-recommends python3-apt +WORKDIR /github/workspace +CMD ["./contrib/ci/debian.sh"] diff --git a/fwupd-1.8.6/contrib/ci/Dockerfile-fedora.in b/fwupd-1.8.6/contrib/ci/Dockerfile-fedora.in new file mode 100644 index 0000000000000000000000000000000000000000..6b1b9742960d89a939d2fc8711b7ae12160d6e87 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/Dockerfile-fedora.in @@ -0,0 +1,11 @@ +FROM fedora:36 +%%%OS%%% +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 +RUN echo fubar > /etc/machine-id +RUN dnf -y update +RUN echo fubar > /etc/machine-id +%%%INSTALL_DEPENDENCIES_COMMAND%%% +WORKDIR /github/workspace +CMD ["./contrib/ci/fedora.sh"] diff --git a/fwupd-1.8.6/contrib/ci/Dockerfile-snap.in b/fwupd-1.8.6/contrib/ci/Dockerfile-snap.in new file mode 100644 index 0000000000000000000000000000000000000000..a440cf6b23173017a2fedac5f05dca0edc79e272 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/Dockerfile-snap.in @@ -0,0 +1,25 @@ +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 --git a/fwupd-1.8.6/contrib/ci/Dockerfile-ubuntu.in b/fwupd-1.8.6/contrib/ci/Dockerfile-ubuntu.in new file mode 100644 index 0000000000000000000000000000000000000000..826be84b970a51181da8c5f1621103ef6f0bafad --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/Dockerfile-ubuntu.in @@ -0,0 +1,10 @@ +FROM ubuntu:rolling +%%%OS%%% +ENV CI_NETWORK true +ENV CC clang +ENV DEBIAN_FRONTEND noninteractive +RUN echo fubar > /etc/machine-id +%%%ARCH_SPECIFIC_COMMAND%%% +RUN apt update -qq && apt install -yq --no-install-recommends python3-apt +WORKDIR /github/workspace +CMD ["./contrib/ci/ubuntu.sh"] diff --git a/fwupd-1.8.6/contrib/ci/Dockerfile-void.in b/fwupd-1.8.6/contrib/ci/Dockerfile-void.in new file mode 100644 index 0000000000000000000000000000000000000000..bb6ece3e29a58eb40dc35393f770759709a6a940 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/Dockerfile-void.in @@ -0,0 +1,9 @@ +FROM ghcr.io/void-linux/void-linux:latest-full-x86_64-musl +%%%OS%%% +ENV LANG en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 +ENV CI_NETWORK true +RUN echo fubar > /etc/machine-id +%%%INSTALL_DEPENDENCIES_COMMAND%%% +WORKDIR /github/workspace +CMD ["./contrib/ci/void.sh"] diff --git a/fwupd-1.8.6/contrib/ci/README.md b/fwupd-1.8.6/contrib/ci/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f0a9e891a58e3f45948bfb83a2daba5134fa1012 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/README.md @@ -0,0 +1,106 @@ +# Continuous Integration + +By using CI, builds are exercised across a variety of environments attempting to maximize code coverage. +For every commit or pull request 6 builds are performed: + +## Fedora (x86_64) + +* A fully packaged RPM build with all plugins enabled +* 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) + +* A fully packaged DEB build with all plugins enabled +* Compiled under gcc +* 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. +* All packages are removed + +## Debian testing (i386) + +* A fully packaged DEB build with all plugins enabled +* Compiled under gcc +* 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. +* All packages are removed + +## Ubuntu devel release (x86_64) + +* A fully packaged DEB build with all plugins enabled +* Compiled under clang +* Tests without -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. +* All packages are removed + +## Debian testing (cross compile s390x) + +* 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) + +* A fully packaged pkg build with all plugins enabled +* Compiled under gcc +* Tests with -Werror enabled +* Compile with the deprecated USB plugin enabled +* Tests with the built in local test suite for all plugins. +* All packages are installed + +## Flatpak + +* A flatpak bundle with all plugins enabled +* Compiled under gcc with the org.gnome.Sdk/x86_64/3.28 runtime +* Builds without the daemon, so only fwupdtool is available +* No GPG, PKCS-7, GObjectIntrospection, systemd or ConsoleKit support +* No tests + +## Adding a new target + +Dockerfiles are generated dynamically by the python script ```generate_dockerfile.py```. +The python script will recognize the environment variable `OS` to determine what target to generate a Dockerfile for. + +### dependencies.xml + +Initially the python script will read in `dependencies.xml` to generate a dependency list for that target. +The XML is organized by a top level element representing the dependencies needed for building fwupd. + +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 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. + +Each distribution will have `package` elements and `control` elements. +`Package` elements represent the name of the package needed for the distribution. + +* An optional attribute `variant` represents one deviation of that distribution. For example building a specific architecture or with a different compiler. +* If the `package` element is empty the `id` of the `dependency` element will be used. +* `Control` elements represent specific requirements associated to a dependency. They will contain elements with individual details. +* `version` elements represent a minimum version to be installed +* `inclusive` elements represent an inclusive list of architectures to be installed on +* `exclusive` elements represent an exclusive list of architectures to not be installed on + +For convenience there is also a helper script `./contrib/ci/fwupd_setup_helpers.p install-dependencies` that parses `dependencies.xml`. + +### Dockerfile.in + +The `Dockerfile.in` file will be used as a template to build the container. No hardcoded dependencies should be put in this file. They should be stored in `dependencies.xml`. diff --git a/fwupd-1.8.6/contrib/ci/abidiff.suppr b/fwupd-1.8.6/contrib/ci/abidiff.suppr new file mode 100644 index 0000000000000000000000000000000000000000..114636b5baa5ae95b7d332176c3a887dce59ef8d --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/abidiff.suppr @@ -0,0 +1,3 @@ +[suppress_type] + type_kind = enum + changed_enumerators = FWUPD_ERROR_LAST,FWUPD_GUID_FLAG_LAST,FWUPD_INSTALL_FLAG_LAST,FWUPD_KEYRING_KIND_LAST,FWUPD_REMOTE_KIND_LAST,FWUPD_SELF_SIGN_FLAG_LAST,FWUPD_STATUS_LAST,FWUPD_TRUST_FLAG_LAST,FWUPD_UPDATE_STATE_LAST,FWUPD_VERSION_FORMAT_LAST,FWUPD_CLIENT_DOWNLOAD_FLAG_LAST,FWUPD_FEATURE_FLAG_LAST diff --git a/fwupd-1.8.6/contrib/ci/arch.sh b/fwupd-1.8.6/contrib/ci/arch.sh new file mode 100755 index 0000000000000000000000000000000000000000..3392918bb54cbd4c58663f98780bf63cc8d818fb --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/arch.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e +set -x +shopt -s extglob + +#clone test firmware if necessary +. ./contrib/ci/get_test_firmware.sh + +#refresh package cache and update image +pacman -Syu --noconfirm + +#install anything missing from the container +./contrib/ci/fwupd_setup_helpers.py install-dependencies -o arch + +# prepare the build tree +rm -rf build +mkdir build && pushd build +cp ../contrib/PKGBUILD . +mkdir -p src/fwupd && pushd src/fwupd +cp -R ../../../!(build|dist) . +popd +chown nobody . -R + +# install and run the custom redfish simulator +pacman -S --noconfirm python-flask +../plugins/redfish/tests/redfish.py & + +# install and run TPM simulator necessary for plugins/uefi-capsule/uefi-self-test +pacman -S --noconfirm swtpm tpm2-tools +swtpm socket --tpm2 --server port=2321 --ctrl type=tcp,port=2322 --flags not-need-init --tpmstate "dir=$PWD" & +trap 'kill $!' EXIT +# extend a PCR0 value for test suite +sleep 2 +tpm2_startup -c +tpm2_pcrextend 0:sha1=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 +export TPM_SERVER_RUNNING=1 + +# build the package and install it +sudo -E -u nobody PKGEXT='.pkg.tar' makepkg -e --noconfirm +pacman -U --noconfirm *.pkg.* + +#run the CI tests for Qt5 +pacman -S --noconfirm qt5-base +meson qt5-thread-test ../contrib/ci/qt5-thread-test +ninja -C qt5-thread-test test + +# move the package to working dir +mv *.pkg.* ../dist + +# no testing here because gnome-desktop-testing isn’t available in Arch diff --git a/fwupd-1.8.6/contrib/ci/build_freebsd_package.sh b/fwupd-1.8.6/contrib/ci/build_freebsd_package.sh new file mode 100755 index 0000000000000000000000000000000000000000..0b04893ef3cefea9aa3d36bdf461bfcf5c3c45fa --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/build_freebsd_package.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +GITHUB_SHA= +GITHUB_REPOSITORY= +GITHUB_REPOSITORY_OWNER= +GITHUB_TAG= + +while [ -n "$1" ]; do + case $1 in + --GITHUB_SHA=*) + GITHUB_SHA=${1#--GITHUB_SHA=} + ;; + --GITHUB_REPOSITORY=*) + GITHUB_REPOSITORY=${1#--GITHUB_REPOSITORY=} + ;; + --GITHUB_REPOSITORY_OWNER=*) + GITHUB_REPOSITORY_OWNER=${1#--GITHUB_REPOSITORY_OWNER=} + ;; + --GITHUB_TAG=*) + GITHUB_TAG=${1#--GITHUB_TAG=} + ;; + *) + echo "Command $1 unknown. exiting..." + exit 1 + ;; + esac + shift +done + +if [ -z "$GITHUB_SHA" ] || [ -z "$GITHUB_REPOSITORY" ] || \ + [ -z "$GITHUB_REPOSITORY_OWNER" ] || [ -z "$GITHUB_TAG" ]; then + exit 1 +fi + +# Include-file of libefivar port uses GCC-specific builtin function +export CC=gcc + +set -xe +mkdir -p /usr/local/etc/pkg/repos/ +# Fix meson flag problem https://www.mail-archive.com/freebsd-ports@freebsd.org/msg86617.html +cp /etc/pkg/FreeBSD.conf /usr/local/etc/pkg/repos/FreeBSD.conf +# Use latest pkg repo instead of quaterly https://wiki.freebsd.org/Ports/QuarterlyBranch +sed -i .old 's|url: "pkg+http://pkg.FreeBSD.org/${ABI}/quarterly"|url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest"|' \ +/usr/local/etc/pkg/repos/FreeBSD.conf +pkg install -y meson efivar +pkg upgrade -y meson +cd /usr +git clone https://github.com/3mdeb/freebsd-ports.git --depth 1 -b fwupd ports +cd /usr/ports/sysutils/fwupd +rm -rf ./* +ls . +cp -r ~/work/fwupd/fwupd/contrib/freebsd/* . +ls . +sed -i .old "s/GH_TAGNAME=.*$/GH_TAGNAME=\t${GITHUB_SHA}/" Makefile +sed -i .old "s/GH_ACCOUNT=.*$/GH_ACCOUNT=\t${GITHUB_REPOSITORY_OWNER}/" Makefile +sed -i .old "s/DISTVERSION=.*$/DISTVERSION=\t${GITHUB_TAG}/" Makefile +make makesum +make clean +make +# Generate current list of files in the pkg-plist +make makeplist > pkg-plist +sed -i "" "1d" pkg-plist +sed -i "" "s/%%PORTDOCS%%%%DOCSDIR%%/%%DOCSDIR%%/g" pkg-plist +# Build artifact +make clean +make package +make install +cp /usr/ports/sysutils/fwupd/work/pkg/fwupd*.pkg \ +~/work/fwupd/fwupd/fwupd-freebsd-${GITHUB_TAG}-${GITHUB_SHA}.pkg || exit 1 diff --git a/fwupd-1.8.6/contrib/ci/build_macos.sh b/fwupd-1.8.6/contrib/ci/build_macos.sh new file mode 100755 index 0000000000000000000000000000000000000000..80ad4a5cd37b7a4c1ba5b7b8a65a3c837d5edd01 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/build_macos.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +set -x + +mkdir -p build-macos && cd build-macos +meson .. \ + -Dbuild=standalone \ + -Dsoup_session_compat=false \ + -Dgusb:docs=false \ + -Dlibjcat:gpg=false \ + -Dlibxmlb:gtkdoc=false \ + $@ diff --git a/fwupd-1.8.6/contrib/ci/build_windows.sh b/fwupd-1.8.6/contrib/ci/build_windows.sh new file mode 100755 index 0000000000000000000000000000000000000000..9bb589a326a9259339bccbf96367bbdef5875c67 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/build_windows.sh @@ -0,0 +1,175 @@ +#!/bin/sh +set -e + +# if invoked outside of CI +if [ "$CI" != "true" ]; then + echo "Not running in CI, please manually configure Windows build" + exit 1 +fi + +# install deps +./contrib/ci/fwupd_setup_helpers.py --yes -o fedora -v mingw64 install-dependencies + +# update to latest version of meson +if [ "$(id -u)" -eq 0 ]; then + dnf install -y python-pip + pip install meson --force-reinstall +fi + +#prep +export LC_ALL=C.UTF-8 +root=$(pwd) +export DESTDIR=${root}/dist +build=$root/build-win32 + +rm -rf $DESTDIR $build + +# For logitech bulk controller being disabled (-Dplugin_logitech_bulkcontroller=disabled): +# See https://bugzilla.redhat.com/show_bug.cgi?id=1991749 +# When fixed need to do the following to enable: +# 1. need to add mingw64-protobuf mingw64-protobuf-tools to CI build deps +# 2. add protoc = /path/to/protoc-c.exe in mingw64.cross +# 3. Only enable when not a tagged release (Unsupported by Logitech) + +# try to keep this and ../contrib/build-windows.sh in sync as much as makes sense +mkdir -p $build $DESTDIR && cd $build +meson .. \ + --cross-file=/usr/share/mingw/toolchain-mingw64.meson \ + --cross-file=../contrib/mingw64.cross \ + --prefix=/ \ + --sysconfdir="etc" \ + --libexecdir="bin" \ + --bindir="bin" \ + -Dbuild=all \ + -Dman=false \ + -Dfish_completion=false \ + -Dbash_completion=false \ + -Dfirmware-packager=false \ + -Dmetainfo=false \ + -Dcompat_cli=false \ + -Dsoup_session_compat=false \ + -Dgcab:introspection=false \ + -Dgcab:docs=false \ + -Dgcab:nls=false \ + -Dgcab:vapi=false \ + -Dgcab:tests=false \ + -Dlibxmlb:introspection=false \ + -Dlibxmlb:gtkdoc=false \ + -Dlibjcat:man=false \ + -Dlibjcat:gpg=false \ + -Dlibjcat:tests=false \ + -Dlibjcat:introspection=false \ + -Dgusb:tests=false \ + -Dgusb:docs=false \ + -Dgusb:introspection=false \ + -Dgusb:vapi=false $@ +VERSION=$(meson introspect . --projectinfo | jq -r .version) + +# run tests +export WINEPATH="/usr/x86_64-w64-mingw32/sys-root/mingw/bin/;$build/libfwupd/;$build/libfwupdplugin/;$build/subprojects/libxmlb/src/;$build/subprojects/gcab/libgcab/;$build/subprojects/libjcat/libjcat/;$build/subprojects/gusb/gusb/" +ninja --verbose -C "$build" -v +ninja -C "$build" test + +# switch to release optimizations +meson configure -Dtests=false -Dbuildtype=release +ninja -C "$build" -v install + +#disable motd for Windows +cd $root +sed -i 's,UpdateMotd=.*,UpdateMotd=false,' "$DESTDIR/etc/fwupd/daemon.conf" + +# create a setup binary +CERTDIR=/etc/pki/tls/certs +MINGW32BINDIR=/usr/x86_64-w64-mingw32/sys-root/mingw/bin + +# deps +find $MINGW32BINDIR \ + -name gspawn-win64-helper-console.exe \ + -o -name gspawn-win64-helper.exe \ + -o -name iconv.dll \ + -o -name libarchive-13.dll \ + -o -name libbrotlicommon.dll \ + -o -name libbrotlidec.dll \ + -o -name libbz2-1.dll \ + -o -name libcrypto-1_1-x64.dll \ + -o -name libcurl-4.dll \ + -o -name "libffi-*.dll" \ + -o -name libgcc_s_seh-1.dll \ + -o -name libgio-2.0-0.dll \ + -o -name libglib-2.0-0.dll \ + -o -name libgmodule-2.0-0.dll \ + -o -name libgmp-10.dll \ + -o -name libgnutls-30.dll \ + -o -name libgobject-2.0-0.dll \ + -o -name libgusb-2.dll \ + -o -name "libhogweed-*.dll" \ + -o -name libidn2-0.dll \ + -o -name libintl-8.dll \ + -o -name libjson-glib-1.0-0.dll \ + -o -name liblzma-5.dll \ + -o -name "libnettle-*.dll" \ + -o -name libp11-kit-0.dll \ + -o -name libpcre-1.dll \ + -o -name libsqlite3-0.dll \ + -o -name libssh2-1.dll \ + -o -name libssl-1_1-x64.dll \ + -o -name libssp-0.dll \ + -o -name libtasn1-6.dll \ + -o -name libusb-1.0.dll \ + -o -name libwinpthread-1.dll \ + -o -name libxml2-2.dll \ + -o -name zlib1.dll \ + | wixl-heat \ + -p $MINGW32BINDIR/ \ + --win64 \ + --directory-ref BINDIR \ + --var "var.MINGW32BINDIR" \ + --component-group "CG.fwupd-deps" | \ + tee $build/contrib/fwupd-deps.wxs + +# no static libraries +find "$DESTDIR/" -type f -name "*.a" -print0 | xargs rm -f + +# our files +find "$DESTDIR" | \ + wixl-heat \ + -p "$DESTDIR/" \ + -x include/ \ + -x share/fwupd/device-tests/ \ + -x share/tests/ \ + -x share/man/ \ + -x share/doc/ \ + -x lib/pkgconfig/ \ + --win64 \ + --directory-ref INSTALLDIR \ + --var "var.DESTDIR" \ + --component-group "CG.fwupd-files" | \ + tee "$build/contrib/fwupd-files.wxs" + +#add service install key +sed -i "$build/contrib/fwupd-files.wxs" -f - << EOF +s,fwupd.exe"/>,fwupd.exe"/>\\ + , +EOF + +MSI_FILENAME="$DESTDIR/setup/fwupd-$VERSION-setup-x86_64.msi" +mkdir -p "$DESTDIR/setup" +wixl -v \ + "$build/contrib/fwupd.wxs" \ + "$build/contrib/fwupd-deps.wxs" \ + "$build/contrib/fwupd-files.wxs" \ + -D CRTDIR=$CERTDIR \ + -D MINGW32BINDIR=$MINGW32BINDIR \ + -D Win64="yes" \ + -D DESTDIR="$DESTDIR" \ + -o "${MSI_FILENAME}" + +# check the msi archive can be installed and removed (use "wine uninstaller" to do manually) +# wine msiexec /i "${MSI_FILENAME}" +# ls -R ~/.wine/drive_c/Program\ Files/fwupd/ +# wine ~/.wine/drive_c/Program\ Files/fwupd/bin/fwupdtool get-plugins --json +# wine msiexec /x "${MSI_FILENAME}" + +#generate news release +contrib/ci/generate_news.py $VERSION > $DESTDIR/news.txt +echo $VERSION > $DESTDIR/VERSION diff --git a/fwupd-1.8.6/contrib/ci/centos.sh b/fwupd-1.8.6/contrib/ci/centos.sh new file mode 100755 index 0000000000000000000000000000000000000000..14e5e23f9bb6663faa1fbe75930ce89d936aeb41 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/centos.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -e +set -x + +#clone test firmware if necessary +. ./contrib/ci/get_test_firmware.sh + +#build +rm -rf build +mkdir -p build +cd build +meson .. \ + --werror \ + -Dplugin_uefi_capsule=disabled \ + -Dplugin_dell=disabled \ + -Dplugin_modem_manager=disabled \ + -Dplugin_synaptics_mst=enabled \ + -Dplugin_flashrom=enabled \ + -Dintrospection=true \ + -Dpkcs7=false \ + -Dman=true +ninja-build -v +ninja-build test -v +cd .. diff --git a/fwupd-1.8.6/contrib/ci/check-abi b/fwupd-1.8.6/contrib/ci/check-abi new file mode 100755 index 0000000000000000000000000000000000000000..4a1f6c8a350adbcfca3f012ba38b39cedb9cf6f3 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-abi @@ -0,0 +1,152 @@ +#!/usr/bin/python3 + + +import argparse +import contextlib +import os +import shutil +import subprocess +import sys + + +def format_title(title): + box = { + "tl": "╔", + "tr": "╗", + "bl": "╚", + "br": "╝", + "h": "═", + "v": "║", + } + hline = box["h"] * (len(title) + 2) + + return "\n".join( + [ + f"{box['tl']}{hline}{box['tr']}", + f"{box['v']} {title} {box['v']}", + f"{box['bl']}{hline}{box['br']}", + ] + ) + + +def rm_rf(path): + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + + +def sanitize_path(name): + return name.replace("/", "-") + + +def get_current_revision(): + revision = subprocess.check_output( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], encoding="utf-8" + ).strip() + + if revision == "HEAD": + # This is a detached HEAD, get the commit hash + revision = ( + subprocess.check_output(["git", "rev-parse", "HEAD"]) + .strip() + .decode("utf-8") + ) + + return revision + + +@contextlib.contextmanager +def checkout_git_revision(revision): + current_revision = get_current_revision() + subprocess.check_call(["git", "checkout", "-q", revision]) + + try: + yield + finally: + subprocess.check_call(["git", "checkout", "-q", current_revision]) + + +def build_install(revision): + build_dir = "_build" + dest_dir = os.path.abspath(sanitize_path(revision)) + print( + format_title(f"# Building and installing {revision} in {dest_dir}"), + end="\n\n", + flush=True, + ) + + with checkout_git_revision(revision): + rm_rf(build_dir) + rm_rf(revision) + + subprocess.check_call( + [ + "meson", + build_dir, + "--prefix=/usr", + "--libdir=lib", + "-Dauto_features=disabled", + "-Ddocs=none", + "-Db_coverage=false", + "-Dgusb:docs=false", + "-Dtests=false", + ] + ) + subprocess.check_call(["ninja", "-v", "-C", build_dir]) + subprocess.check_call( + ["ninja", "-v", "-C", build_dir, "install"], env={"DESTDIR": dest_dir} + ) + + return dest_dir + + +def compare(old_tree, new_tree): + print(format_title(f"# Comparing the two ABIs"), end="\n\n", flush=True) + + old_headers = os.path.join(old_tree, "usr", "include") + old_lib = os.path.join(old_tree, "usr", "lib", "libfwupd.so") + + new_headers = os.path.join(new_tree, "usr", "include") + new_lib = os.path.join(new_tree, "usr", "lib", "libfwupd.so") + + subprocess.check_call( + [ + "abidiff", + "--headers-dir1", + old_headers, + "--headers-dir2", + new_headers, + "--drop-private-types", + "--suppressions", + "contrib/ci/abidiff.suppr", + "--fail-no-debug-info", + "--no-added-syms", + old_lib, + new_lib, + ] + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument("old", help="the previous revision, considered the reference") + parser.add_argument("new", help="the new revision, to compare to the reference") + + args = parser.parse_args() + + if args.old == args.new: + print("Let's not waste time comparing something to itself") + sys.exit(0) + + old_tree = build_install(args.old) + new_tree = build_install(args.new) + + try: + compare(old_tree, new_tree) + + except subprocess.CalledProcessError: + sys.exit(1) + + print(f"Hurray! {args.old} and {args.new} are ABI-compatible!") diff --git a/fwupd-1.8.6/contrib/ci/check-deprecated.sh b/fwupd-1.8.6/contrib/ci/check-deprecated.sh new file mode 100755 index 0000000000000000000000000000000000000000..78b0d1915190c230fd9ebc71d3a71dc224a9132d --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-deprecated.sh @@ -0,0 +1,15 @@ +#!/bin/sh -e +set -e + +# these are deprecated in favor of INTERNAL flags +deprecated="FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS + FWUPD_DEVICE_FLAG_ONLY_SUPPORTED + FWUPD_DEVICE_FLAG_MD_SET_NAME + FWUPD_DEVICE_FLAG_MD_SET_VERFMT + FWUPD_DEVICE_FLAG_NO_GUID_MATCHING + FWUPD_DEVICE_FLAG_MD_SET_ICON" +for val in $deprecated; do + if grep -- $val plugins/*/*.c ; then + exit 1 + fi +done diff --git a/fwupd-1.8.6/contrib/ci/check-finalizers.py b/fwupd-1.8.6/contrib/ci/check-finalizers.py new file mode 100755 index 0000000000000000000000000000000000000000..3a0023d5da6d3370fb751560322075b513bee116 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-finalizers.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring,consider-using-f-string +# pylint: disable=too-few-public-methods +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import glob +import sys +from typing import List + + +class ReturnValidator: + def __init__(self): + self.warnings: List[str] = [] + + def parse(self, fn: str) -> None: + + with open(fn, "rb") as f: + infunc = False + has_parent_finalize = False + for line in f.read().decode().split("\n"): + + # found the function, but ignore the prototype + if line.find("_finalize(") != -1: + if line.endswith(";"): + continue + infunc = True + continue + + # got it + if line.find("->finalize(") != -1: + has_parent_finalize = True + continue + + # finalize is done + if infunc and line.startswith("}"): + if not has_parent_finalize: + self.warnings.append( + "{} did not have parent ->finalize()".format(fn) + ) + break + + +def test_files(): + + # test all C source files + validator = ReturnValidator() + for fn in glob.glob("**/*.c", recursive=True): + if fn.startswith("dist/") or fn.startswith("subprojects/"): + continue + validator.parse(fn) + for warning in validator.warnings: + print(warning) + + return 1 if validator.warnings else 0 + + +if __name__ == "__main__": + + # all done! + sys.exit(test_files()) diff --git a/fwupd-1.8.6/contrib/ci/check-headers.py b/fwupd-1.8.6/contrib/ci/check-headers.py new file mode 100755 index 0000000000000000000000000000000000000000..897b27e129f5cf626abe49270899017856149957 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-headers.py @@ -0,0 +1,151 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring +# +# Copyright (C) 2021 Richard Hughes +# Copyright (C) 2021 Mario Limonciello +# +# SPDX-License-Identifier: LGPL-2.1+ + +import glob +import sys +import os +from typing import List + + +def __get_includes(fn: str) -> List[str]: + includes: List[str] = [] + with open(fn, "r") as f: + for line in f.read().split("\n"): + if line.find("#include") == -1: + continue + if line.find("waive-pre-commit") > 0: + continue + for char in ["<", ">", '"']: + line = line.replace(char, "") + for char in ["\t"]: + line = line.replace(char, " ") + includes.append(line.split(" ")[-1]) + return includes + + +def test_files() -> int: + + rc: int = 0 + + lib_headers1 = glob.glob("libfwupd/*.h") + lib_headers1.remove("libfwupd/fwupd.h") + + lib_headers2 = glob.glob("libfwupdplugin/*.h") + lib_headers2.remove("libfwupdplugin/fwupdplugin.h") + + toplevel_headers = ["libfwupd/fwupd.h", "libfwupdplugin/fwupdplugin.h"] + toplevel_headers_nopath = [os.path.basename(fn) for fn in toplevel_headers] + lib_headers = lib_headers1 + lib_headers2 + lib_headers_nopath = [os.path.basename(fn) for fn in lib_headers] + + # test all C and H files + for fn in glob.glob("**/*.[c|h]", recursive=True): + includes = __get_includes(fn) + + # we do not care + if fn.startswith("subprojects"): + continue + if fn.startswith("build"): + continue + if fn.startswith("dist"): + continue + if fn.startswith("contrib/ci"): + continue + if fn in [ + "libfwupd/fwupd-context-test.c", + "libfwupd/fwupd-thread-test.c", + "libfwupdplugin/fu-fuzzer-main.c", + ]: + continue + + if ( + fn.startswith("plugins") + and not fn.endswith("self-test.c") + and not fn.endswith("tool.c") + ): + for include in includes: + # check for using private header use in plugins + if include.endswith("private.h"): + print("{} uses private header {}".format(fn, include)) + rc = 1 + continue + + # check for referring to anything but top level header + if include in lib_headers or include in lib_headers_nopath: + print( + "{} contains {}, should only use top level includes".format( + fn, include + ) + ) + rc = 1 + + # check for double top level headers + for toplevel_header in toplevel_headers: + + toplevel_fn = os.path.basename(toplevel_header) + toplevel_includes = __get_includes(toplevel_header) + toplevel_includes_nopath = [ + os.path.basename(fn) for fn in toplevel_includes + ] + + # we do not need both toplevel headers + if set(toplevel_headers_nopath).issubset(set(includes)): + print( + "{} contains both {}".format(fn, ", ".join(toplevel_headers_nopath)) + ) + + # toplevel not listed + if toplevel_fn not in includes: + continue + + # includes toplevel and *also* something listed in the toplevel + for include in includes: + if include in toplevel_includes or include in toplevel_includes_nopath: + print( + "{} contains {} but also includes {}".format( + fn, toplevel_fn, include + ) + ) + rc = 1 + + # check for missing config.h + if fn.endswith(".c") and "config.h" not in includes: + print("{} does not include config.h".format(fn)) + rc = 1 + + # check for one header implying the other + implied_headers = { + "fu-common.h": ["xmlb.h"], + "fwupdplugin.h": [ + "gio/gio.h", + "glib.h", + "glib-object.h", + "xmlb.h", + "fwupd.h", + ] + + lib_headers1, + "gio/gio.h": ["glib.h", "glib-object.h"], + "glib-object.h": ["glib.h"], + "json-glib/json-glib.h": ["glib.h", "glib-object.h"], + "xmlb.h": ["gio/gio.h"], + } + for key, values in implied_headers.items(): + for value in values: + if key in includes and value in includes: + print( + "{} contains {} which is implied by {}".format(fn, value, key) + ) + rc = 1 + + return rc + + +if __name__ == "__main__": + + # all done! + sys.exit(test_files()) diff --git a/fwupd-1.8.6/contrib/ci/check-license.py b/fwupd-1.8.6/contrib/ci/check-license.py new file mode 100755 index 0000000000000000000000000000000000000000..5f5b80512612cf6d92c141c2c1f8fc76d20f5863 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-license.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2021 Richard Hughes +# Copyright (C) 2021 Mario Limonciello +# +# SPDX-License-Identifier: LGPL-2.1+ + +import glob +import os +import sys + + +def __get_license(fn: str) -> str: + with open(fn, "r") as f: + for line in f.read().split("\n"): + if line.find("SPDX-License-Identifier:") > 0: + return line.split(":")[1] + return "" + + +def test_files() -> int: + + rc: int = 0 + build_dirs = [os.path.dirname(cf) for cf in glob.glob("**/config.h")] + + for fn in glob.glob("**/*.[c|h|py|sh]", recursive=True): + if "meson-private" in fn: + continue + if os.path.isdir(fn): + continue + if fn.startswith(tuple(build_dirs)): + continue + if fn.startswith("subprojects"): + continue + if fn.startswith("dist"): + continue + lic = __get_license(fn) + if not lic: + print("{} does not specify a license".format(fn)) + rc = 1 + continue + if not "GPL" in lic: + print("{} does not contain LGPL or GPL ({})".format(fn, lic)) + rc = 1 + continue + return rc + + +if __name__ == "__main__": + + # all done! + sys.exit(test_files()) diff --git a/fwupd-1.8.6/contrib/ci/check-null-false-returns.py b/fwupd-1.8.6/contrib/ci/check-null-false-returns.py new file mode 100755 index 0000000000000000000000000000000000000000..48fbbe85bbe20c566ac742151f60e6ba749b9531 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-null-false-returns.py @@ -0,0 +1,240 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring,too-many-branches +# pylint: disable=too-many-statements,too-many-return-statements,too-few-public-methods +# +# Copyright (C) 2021 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import glob +import sys +from typing import List + + +def _tokenize(line: str) -> List[str]: + + # remove whitespace + line = line.strip() + line = line.replace("\t", "") + line = line.replace(";", "") + + # find value + line = line.replace(" ", "|") + line = line.replace(",", "|") + line = line.replace(")", "|") + line = line.replace("(", "|") + + # return empty tokens + tokens = [] + for token in line.rsplit("|"): + if token: + tokens.append(token) + return tokens + + +class ReturnValidator: + def __init__(self): + self.warnings: List[str] = [] + + # internal state + self._fn = None + self._line_num = None + self._value = None + self._nret = None + self._rvif = None + self._line = None + + @property + def _tokens(self) -> List[str]: + return _tokenize(self._line) + + @property + def _value_relaxed(self) -> str: + if self._value in ["0x0", "0x00", "0x0000"]: + return "0" + if self._value in ["0xffffffff"]: + return "G_MAXUINT32" + if self._value in ["0xffff"]: + return "G_MAXUINT16" + if self._value in ["0xff"]: + return "G_MAXUINT8" + if self._value in ["G_SOURCE_REMOVE"]: + return "FALSE" + if self._value in ["G_SOURCE_CONTINUE"]: + return "TRUE" + return self._value + + def _test_rvif(self) -> None: + + # parse "g_return_val_if_fail (SOMETHING (foo), NULL);" + self._value = self._tokens[-1] + + # enumerated enum, so ignore + if self._value.find("_") != -1: + return + + # is invalid + if self._rvif and self._value_relaxed not in self._rvif: + self.warnings.append( + "{} line {} got {}, expected {}".format( + self._fn, self._line_num, self._value, ", ".join(self._rvif) + ) + ) + + def _test_return(self) -> None: + + # parse "return 0x0;" + self._value = self._tokens[-1] + + # is invalid + if self._nret and self._value_relaxed in self._nret: + self.warnings.append( + "{} line {} got {}, which is not valid".format( + self._fn, self._line_num, self._value + ) + ) + + def parse(self, fn: str) -> None: + + self._fn = fn + with open(fn) as f: + self._rvif = None + self._nret = None + self._line_num = 0 + for line in f.readlines(): + self._line_num += 1 + line = line.rstrip() + if not line: + continue + if line.endswith("\\"): + continue + if line.endswith("&&"): + continue + self._line = line + idx = line.find("g_return_val_if_fail") + if idx != -1: + self._test_rvif() + continue + idx = line.find("return") + if idx != -1: + # continue + if len(self._tokens) == 2: + self._test_return() + continue + + # not a function header + if line[0] in ["#", " ", "\t", "{", "}", "/"]: + continue + + # label + if line.endswith(":"): + continue + + # remove prefixes + if line.startswith("static"): + line = line[7:] + if line.startswith("inline"): + line = line[7:] + + # a pointer + if line.endswith("*"): + self._rvif = ["NULL"] + self._nret = ["FALSE"] + continue + + # not a leading line + if line.find(" ") != -1: + continue + + # a type we know + if line in ["void"]: + self._rvif = [] + self._nret = [] + continue + if line in ["gpointer"]: + self._rvif = ["NULL"] + self._nret = ["FALSE"] + continue + if line in ["gboolean"]: + self._rvif = ["TRUE", "FALSE"] + self._nret = ["NULL", "0"] + continue + if line in ["guint32"]: + self._rvif = ["0", "G_MAXUINT32"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["GQuark", "GType"]: + self._rvif = ["0"] + self._nret = ["NULL", "0", "TRUE", "FALSE"] + continue + if line in ["guint64"]: + self._rvif = ["0", "G_MAXUINT64"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["guint16"]: + self._rvif = ["0", "G_MAXUINT16"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["guint8"]: + self._rvif = ["0", "G_MAXUINT8"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gint64"]: + self._rvif = ["0", "-1", "G_MAXINT64"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gint32"]: + self._rvif = ["0", "-1", "G_MAXINT32"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gint16"]: + self._rvif = ["0", "-1", "G_MAXINT16"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gint8"]: + self._rvif = ["0", "-1", "G_MAXINT8"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gint", "int"]: + self._rvif = ["0", "-1", "G_MAXINT"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["guint"]: + self._rvif = ["0", "G_MAXUINT"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gulong"]: + self._rvif = ["0", "G_MAXLONG"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gsize", "size_t"]: + self._rvif = ["0", "G_MAXSIZE"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + if line in ["gssize", "ssize_t"]: + self._rvif = ["0", "-1", "G_MAXSSIZE"] + self._nret = ["NULL", "TRUE", "FALSE"] + continue + # print('unknown return type {}'.format(line)) + self._rvif = None + self._nret = None + + +def test_files(): + + # test all C source files + validator = ReturnValidator() + for fn in glob.glob("**/*.c", recursive=True): + if fn.startswith("dist/") or fn.startswith("subprojects/"): + continue + validator.parse(fn) + for warning in validator.warnings: + print(warning) + + return 1 if validator.warnings else 0 + + +if __name__ == "__main__": + + # all done! + sys.exit(test_files()) diff --git a/fwupd-1.8.6/contrib/ci/check-potfiles.py b/fwupd-1.8.6/contrib/ci/check-potfiles.py new file mode 100755 index 0000000000000000000000000000000000000000..15dda5341d165ac065e7d56e0193881be3a5280a --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-potfiles.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring,consider-using-f-string +# pylint: disable=too-few-public-methods +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import glob +import sys + +from typing import List + + +def test_files(): + + # compare with POTFILES.in + with open("po/POTFILES.in", "rb") as f: + potfiles_fns: List[str] = f.read().decode().split("\n") + for fn in sorted( + glob.glob("src/*.c") + + glob.glob("plugins/*/*.c") + + glob.glob("policy/*.policy.in") + + glob.glob("data/*/*.xml") + + glob.glob("libfwupdplugin/tests/bios-attrs/*/*.txt") + ): + if ( + fn.startswith("dist/") + or fn.startswith("subprojects/") + or fn.startswith("build/") + ): + continue + with open(fn, "rb") as f: + blob = f.read().decode() + if blob.find('_("') != -1 or blob.find("TRANSLATORS") != -1: + if fn not in potfiles_fns: + print("{} is missing from po/POTFILES.in".format(fn)) + return 1 + + # success + return 0 + + +if __name__ == "__main__": + + # all done! + sys.exit(test_files()) diff --git a/fwupd-1.8.6/contrib/ci/check-quirks.py b/fwupd-1.8.6/contrib/ci/check-quirks.py new file mode 100755 index 0000000000000000000000000000000000000000..83b09fac8f854e34927b626f67a1c5050334c281 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check-quirks.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2021 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import glob +import sys + + +def test_files() -> int: + + rc: int = 0 + + for fn in glob.glob("**/*.quirk", recursive=True): + with open(fn, "r") as f: + for line in f.read().split("\n"): + if line.startswith(" ") or line.endswith(" "): + print("{} has leading or trailing whitespace: {}".format(fn, line)) + rc = 1 + continue + if not line or line.startswith("#"): + continue + if line.startswith("["): + if not line.endswith("]"): + print("{} has invalid section header: {}".format(fn, line)) + rc = 1 + continue + for deprecated in ["DeviceInstanceId", "Guid"]: + if line.find(deprecated) != -1: + print("{} has deprecated prefix: {}".format(fn, deprecated)) + rc = 1 + continue + else: + sections = line.split(" = ") + if len(sections) != 2: + print("{} has invalid line: {}".format(fn, line)) + rc = 1 + continue + for section in sections: + if section.strip() != section: + print("{} has invalid spacing: {}".format(fn, line)) + rc = 1 + break + return rc + + +if __name__ == "__main__": + + # all done! + sys.exit(test_files()) diff --git a/fwupd-1.8.6/contrib/ci/check_missing_translations.sh b/fwupd-1.8.6/contrib/ci/check_missing_translations.sh new file mode 100755 index 0000000000000000000000000000000000000000..0139ff3535abffb813a9ac7906cc0701e32d9dde --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/check_missing_translations.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +cd po +intltool-update -m +if [ -f missing ]; then + exit 1 +fi diff --git a/fwupd-1.8.6/contrib/ci/debian.sh b/fwupd-1.8.6/contrib/ci/debian.sh new file mode 100755 index 0000000000000000000000000000000000000000..bc7e888a94955c2366d3986e9a7b90c044959000 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/debian.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -e +set -x + +export QUBES_OPTION= + + +#although it's debian, we don't build packages +if [ "$OS" = "debian-s390x" ]; then + ./contrib/ci/debian_s390x.sh + exit 0 +fi + +# Set Qubes Os vars if -Dqubes=true is parameter +if [ "$QUBES" = "true" ]; then + export QUBES_OPTION='-Dqubes=true' +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|venv) build/ +pushd build +mv contrib/debian . +sed s/quilt/native/ debian/source/format -i +#generate control file +./contrib/ci/generate_debian.py + +#check if we have all deps available +#if some are missing, we're going to use subproject instead and +#packaging CI will fail +apt update -qq && apt install python3-apt -y +./contrib/ci/fwupd_setup_helpers.py install-dependencies -o debian --yes || true +if ! dpkg-checkbuilddeps; then + ./contrib/ci/ubuntu.sh + exit 0 +fi + +#clone test firmware if necessary +. ./contrib/ci/get_test_firmware.sh + +#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 \ + --preserve-envvar QUBES_OPTION + +#check lintian output +#suppress tags that are side effects of building in docker this way +lintian ../*changes \ + -IE \ + --pedantic \ + --no-tag-display-limit \ + --suppress-tags library-not-linked-against-libc \ + --suppress-tags bad-distribution-in-changes-file \ + --suppress-tags debian-watch-file-in-native-package \ + --suppress-tags source-nmu-has-incorrect-version-number \ + --suppress-tags no-symbols-control-file \ + --suppress-tags gzip-file-is-not-multi-arch-same-safe \ + --suppress-tags missing-dependency-on-libc \ + --suppress-tags arch-dependent-file-not-in-arch-specific-directory \ + --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=$(find .. -type f -name "*.deb" | grep -v 'fwupd-tests\|dbgsym') +dpkg -i $PACKAGES + +# copy in more non-generated data +mkdir -p /usr/share/installed-tests/fwupd +cp fwupd-test-firmware/installed-tests/* /usr/share/installed-tests/fwupd/ -LRv + +# 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 diff --git a/fwupd-1.8.6/contrib/ci/debian_s390x.sh b/fwupd-1.8.6/contrib/ci/debian_s390x.sh new file mode 100755 index 0000000000000000000000000000000000000000..4bff8baba2df6fdadadaf567f9152813d9c52e53 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/debian_s390x.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -e +set -x + +export LC_ALL=C.UTF-8 + +#evaluate using Debian's build flags +eval "$(dpkg-buildflags --export=sh)" +#filter out -Bsymbolic-functions +LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") +export LDFLAGS + +rm -rf build +mkdir -p build +cp contrib/ci/s390x_cross.txt build/ +cd build +meson .. \ + --cross-file s390x_cross.txt \ + --werror \ + -Dplugin_flashrom=disabled \ + -Dplugin_uefi_capsule=disabled \ + -Dplugin_dell=disabled \ + -Dplugin_modem_manager=disabled \ + -Dplugin_msr=disabled \ + -Dplugin_mtd=false \ + -Dplugin_powerd=disabled \ + -Dintrospection=false \ + -Dlibxmlb:introspection=false \ + -Dlibxmlb:gtkdoc=false \ + -Dman=false +ninja -v +ninja test -v +cd .. + + +#test for missing translation files +./contrib/ci/check_missing_translations.sh diff --git a/fwupd-1.8.6/contrib/ci/dependencies.xml b/fwupd-1.8.6/contrib/ci/dependencies.xml new file mode 100644 index 0000000000000000000000000000000000000000..acef65818cc999f288907dd2ea2bbb2f0a58c27f --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/dependencies.xml @@ -0,0 +1,1759 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + amd64 + arm64 + armhf + i386 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clang-tools-extra + + + + + + + + + + + + + + mingw64-gcc + + + + + + + + + + cairo-devel + + + cairo-devel + cairo-devel + + + cairo-devel + + + + + libcairo-dev:s390x + + + + + + + + + + cairo-gobject-devel + + + cairo-gobject-devel + cairo-gobject-devel + + + + + libcairo-gobject2:s390x + + + + + + + + + + json-glib + + + json-glib-devel + + + json-glib-devel + json-glib-devel + mingw64-json-glib + + + json-glib-devel + + + + (>= 1.1.1) + + + libjson-glib-dev:s390x + + + + + (>= 1.1.1) + + + + + + + libftdi + + + libftdi-devel + + + libftdi-devel + libftdi-devel + + + + + + + + + + + + + pciutils + + + pciutils-devel + + + pciutils-devel + pciutils-devel + + + + + + + + + + + + + + noto-fonts + + + google-noto-sans-cjk-ttc-fonts + + + google-noto-sans-cjk-ttc-fonts + google-noto-sans-cjk-ttc-fonts + + + noto-fonts-cjk + + + + + + + + + + + + + + + + + + + + + + + + (>= 12) + + + + + + + (>= 12) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + freetype + + + freetype + freetype + + + + + libfreetype6-dev:s390x + + + + + + + + + + + + + + + + + + + + + + + + + + + + flashrom-devel + flashrom-devel + + + + ia64 + + + + + + + ia64 + + + + + + + + + + + + + gcab + + + + + + + + + + + + + + + + + + + + + + + + gettext + mingw64-gettext + + + + + + + (>= 0.19.8.1) + + + + + + + + (>= 0.19.8.1) + + + + + + + + + + + + + + + + gnu-efi-libs + + + + + + + + + + gnu-efi-libs + + + + amd64 + arm64 + armhf + i386 + + gnu-efi + gnu-efi + + + + amd64 + arm64 + armhf + i386 + + gnu-efi + gnu-efi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + glib2-devel + + + glib2-devel + glib2-devel + + glib2-devel + mingw64-glib2 + + + glib-devel + + + + (>= 2.45.8) + + + libglib2.0-dev:s390x + + + + + (>= 2.45.8) + + + + + + + glibc-langpack-en + glibc-langpack-en + + + + + + + + gobject-introspection-devel + + + gobject-introspection-devel + gobject-introspection-devel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gnutls-devel + + + gnutls-devel + gnutls-devel + mingw64-gnutls + + + gnutls-devel + + + + + libgnutls28-dev:s390x + libgnutls28-dev + + + + + + + + + gnutls-utils + + + gnutls-utils + gnutls-utils + + + gnutls-tools + + + + + + + + + + + + + + + + + + + + + + + + + + + + + libxmlb + + + + libxmlb-devel + libxmlb-devel + + + libxmlb-devel + + + + (>= 0.1.13) + + + libxmlb-dev:s390x + + + + + (>= 0.1.13) + + + + + + + libjcat-devel + libjcat-devel + + + + + + + + + + + + + libjcat-devel + + + + + xz-devel + xz-devel + + + + + + + + + + + + + + + libarchive-devel + + + libarchive-devel + libarchive-devel + mingw64-libarchive + + + + + libarchive-dev:s390x + + + + + + + + libarchive-devel + + + + + libcbor + + + libcbor-devel + + + libcbor-devel + libcbor-devel + + + + + libcbor-dev:s390x + + + + + + + + libcbor-devel + + + + + efivar + + + efivar-devel + + + efivar-devel + efivar-devel + + + libefivar-devel + + + + amd64 + arm64 + armhf + i386 + + + + + + + amd64 + arm64 + armhf + i386 + + + + + + + + amd64 + arm64 + armhf + i386 + + + + + + + amd64 + arm64 + armhf + i386 + + + + + + + gcab + + + libgcab1-devel + + + libgcab1-devel + libgcab1-devel + + + + + libgcab-dev:s390x + + + + + + + + gcab-devel + + + + + + + + + + + + + + + + + libgudev1-devel + + + libgudev1-devel + libgudev1-devel + + + + + libgudev-1.0-dev:s390x + + + + + + + + + + libgusb + + + libgusb-devel + + + libgusb-devel + libgusb-devel + mingw64-libgusb + + + libgusb-devel + + + + (>= 0.3.5) + + + libgusb-dev:s390x + + + + + (>= 0.3.3) + + + + + + + libicu-dev:s390x + + + + + + libidn2-0-dev:s390x + + + + + + libsmbios + + + libsmbios-devel + + + libsmbios-devel + + + libsmbios-devel + + + + i386 + amd64 + + + + + + + i386 + amd64 + + + + + + + + + + + libsoup-devel + + + + + curl + + + libcurl-devel + + + libcurl-devel + libcurl-devel + mingw64-curl + + + libcurl-devel + + + + + libcurl4-gnutls-dev:s390x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + amd64 + arm64 + armhf + i386 + + + + + + + amd64 + arm64 + armhf + i386 + + + + + + + pango-devel + + + pango-devel + pango-devel + + + pango-devel + + + + + + + + + + + + + + + + + + + + + + + mingw64-pkg-config + + + + + + + + polkit + + + polkit + + + polkit + polkit + + + polkit + + + + (>> 0.105-14) + + + + + + + + (>> 0.105-14) + + + + + + + ModemManager-glib-devel + + + ModemManager-glib-devel + ModemManager-glib-devel + + + modemmanager + + + + + libmm-glib-dev:s390x + + + + + + + + + + libqmi-devel + + + libqmi-devel + libqmi-devel + + + libqmi + + + + + libqmi-glib-dev:s390x + + + + + + + + + + libmbim-devel + + + libmbim-devel + libmbim-devel + + + libmbim + + + + + libmbim-glib-dev:s390x + + + + + + + + + + + + libqrtr-glib-dev:s390x + + + + + + polkit-devel + + + polkit-devel + polkit-devel + + + polkit-devel + + + + + libpolkit-gobject-1-dev:s390x + + + + + + + + + + + + + + + python34-devel + + + + + + + + + + + + python-cairo + + + python3-cairo + python3-cairo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + python-gobject + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + python-packaging + + + + + + + + + + + + + + qemu-user + + + + + + + + + + + + + + + + + + + + + + + sqlite-devel + + + sqlite-devel + sqlite-devel + mingw64-sqlite + + + + + libsqlite3-dev:s390x + + + + + + + + sqlite-devel + + + + + elogind-devel + + + + + + + + + + + + + (>= 231) + + + + + + + + (>= 231) + + + + + + + systemd-devel + + + systemd-devel + systemd-devel + + + + + libsystemd-dev:s390x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + umockdev-devel + + + umockdev-devel + umockdev-devel + + + + + + + + + + + + + + vala + + + vala + + + vala + vala + + + vala + + + + + + + + + + + + + + + + + + valgrind-devel + + + valgrind-devel + valgrind-devel + + + + ia64 + riscv64 + x32 + mips + sparc64 + sh4 + ppc64 + powerpcspe + hppa + alpha + mips64el + armhf + armel + mipsel + m68k + + + + + + + ia64 + riscv64 + x32 + mips + sparc64 + sh4 + ppc64 + powerpcspe + hppa + alpha + mips64el + armhf + armel + mipsel + m68k + + + + + + + + + + + + + + + + + + + + + tpm2-tss + + + tpm2-tss-devel + + + tpm2-tss-devel + tpm2-tss-devel + + + + + libtss2-dev:s390x + + + + + amd64 + arm64 + armhf + i386 + + + + + tpm2-tss-devel + + + + + + + + + + + + + + ShellCheck + + + + + protobuf-c + + + + + + + + + + + + protobuf-c-devel + protobuf-c-devel + + + protobuf-c-devel + + + + + + + + + + + + + + + + + + protobuf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wine + + + + + + msitools + + + diff --git a/fwupd-1.8.6/contrib/ci/fedora.sh b/fwupd-1.8.6/contrib/ci/fedora.sh new file mode 100755 index 0000000000000000000000000000000000000000..cdb398a10a61905c05ae3232551444fed622ba01 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/fedora.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -e +set -x + +#get any missing deps from the container +./contrib/ci/fwupd_setup_helpers.py install-dependencies --yes -o fedora + +# disable the safe directory feature +git config --global safe.directory "*" + +#generate a tarball +mkdir -p build && pushd build +rm -rf * + +if [ "$QUBES" = "true" ]; then + QUBES_MACRO=(--define "qubes_packages 1") +fi + +meson .. \ + -Ddocs=disabled \ + -Dman=true \ + -Dtests=true \ + -Db_sanitize=address,undefined \ + -Dgusb:tests=false \ + -Dplugin_dummy=true \ + -Dplugin_flashrom=enabled \ + -Dplugin_modem_manager=disabled \ + -Dplugin_uefi_capsule=enabled \ + -Dplugin_dell=enabled \ + -Dplugin_synaptics_mst=enabled $@ +ninja-build dist +popd +VERSION=`meson introspect build --projectinfo | jq -r .version` +RPMVERSION=${VERSION//-/.} +mkdir -p $HOME/rpmbuild/SOURCES/ +mv build/meson-dist/fwupd-$VERSION.tar.xz $HOME/rpmbuild/SOURCES/ + +#generate a spec file +sed "s,#VERSION#,$RPMVERSION,; + s,#BUILD#,1,; + s,#LONGDATE#,`date '+%a %b %d %Y'`,; + s,#ALPHATAG#,alpha,; + s,enable_dummy 0,enable_dummy 1,; + s,Source0.*,Source0:\tfwupd-$VERSION.tar.xz," \ + build/contrib/fwupd.spec.in > build/fwupd.spec + +if [ -n "$CI" ]; then + sed -i "s,enable_ci 0,enable_ci 1,;" build/fwupd.spec +fi + +#build RPM packages +rpmbuild -ba "${QUBES_MACRO[@]}" build/fwupd.spec + +#if invoked outside of CI +if [ ! -f /.dockerenv ]; then + echo "Not running in a container, please manually install packages" + exit 0 +fi + +#install RPM packages +dnf install -y $HOME/rpmbuild/RPMS/*/*.rpm + +mkdir -p dist +cp $HOME/rpmbuild/RPMS/*/*.rpm dist + +if [ "$CI" = "true" ]; then + sed "s,^DisabledPlugins=.*,DisabledPlugins=," -i /etc/fwupd/daemon.conf + + # set up enough PolicyKit and D-Bus to run the daemon + mkdir -p /run/dbus + mkdir -p /var + ln -s /var/run /run + dbus-daemon --system --fork + /usr/lib/polkit-1/polkitd & + sleep 5 + + # run the daemon startup to check it can start + /usr/libexec/fwupd/fwupd --immediate-exit --verbose + + # run the installed tests whilst the daemon debugging + /usr/libexec/fwupd/fwupd --verbose & + sleep 10 + gnome-desktop-testing-runner fwupd +fi diff --git a/fwupd-1.8.6/contrib/ci/flatpak.py b/fwupd-1.8.6/contrib/ci/flatpak.py new file mode 100755 index 0000000000000000000000000000000000000000..28c3ce0d8d01148f996bd435ec4776a4c1d49772 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/flatpak.py @@ -0,0 +1,90 @@ +#!/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 main + data["branch"] = "main" + 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 -i flatpak run org.freedesktop.fwupd --verbose get-devices diff --git a/fwupd-1.8.6/contrib/ci/fwupd_setup_helpers.py b/fwupd-1.8.6/contrib/ci/fwupd_setup_helpers.py new file mode 100755 index 0000000000000000000000000000000000000000..936589851cd6be3c10b920f3f8965f167ab90c91 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/fwupd_setup_helpers.py @@ -0,0 +1,244 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell, Inc. +# Copyright (C) 2020 Intel, Inc. +# Copyright (C) 2021 Mario Limonciello +# +# SPDX-License-Identifier: LGPL-2.1+ +# +import os +import sys +import argparse + +WARNING = "\033[93m" +ENDC = "\033[0m" + +# Minimum version of markdown required +MINIMUM_MARKDOWN = (3, 2, 0) + +# Minimum meson required +MINIMUM_MESON = "0.61.0" + + +def get_possible_profiles(): + return ["fedora", "centos", "debian", "ubuntu", "arch", "void"] + + +def detect_profile(): + try: + import distro + + target = distro.id() + if not target in get_possible_profiles(): + target = distro.like() + except ModuleNotFoundError: + target = "" + return target + + +def pip_install_package(debug, name): + import subprocess + + cmd = ["python3", "-m", "pip", "install", "--upgrade", name] + if debug: + print(cmd) + subprocess.call(cmd) + + +def test_markdown(debug): + try: + import markdown + + new_enough = markdown.__version_info__ >= MINIMUM_MARKDOWN + except ModuleNotFoundError: + new_enough = False + if not new_enough: + print("python3-markdown must be installed/upgraded") + pip_install_package(debug, "markdown") + + +def test_meson(debug): + from importlib.metadata import version, PackageNotFoundError + + try: + new_enough = version("meson") >= MINIMUM_MESON + except PackageNotFoundError: + import subprocess + + try: + ver = ( + subprocess.check_output(["meson", "--version"]).strip().decode("utf-8") + ) + new_enough = ver >= MINIMUM_MESON + except FileNotFoundError: + new_enough = False + if not new_enough: + print("meson must be installed/upgraded") + pip_install_package(debug, "meson") + + +def parse_dependencies(OS, variant, add_control): + import xml.etree.ElementTree as etree + + deps = [] + dep = "" + directory = os.path.dirname(sys.argv[0]) + tree = etree.parse(os.path.join(directory, "dependencies.xml")) + root = tree.getroot() + for child in root: + if "id" not in child.attrib: + continue + for distro in child: + if "id" not in distro.attrib: + continue + if distro.attrib["id"] != OS: + continue + control = "" + if add_control: + inclusive = [] + exclusive = [] + if not distro.findall("control"): + continue + for control_parent in distro.findall("control"): + for obj in control_parent.findall("inclusive"): + inclusive.append(obj.text) + for obj in control_parent.findall("exclusive"): + exclusive.append(obj.text) + if inclusive or exclusive: + inclusive = " ".join(inclusive).strip() + exclusive = " !".join(exclusive).strip() + if exclusive: + exclusive = "!%s" % exclusive + control = " [%s%s]" % (inclusive, exclusive) + for package in distro.findall("package"): + if variant: + if "variant" not in package.attrib: + continue + if package.attrib["variant"] != variant: + continue + if package.text: + dep = package.text + else: + dep = child.attrib["id"] + dep += control + if dep: + deps.append(dep) + return deps + + +def _validate_deps(os, deps): + validated = deps + if os == "debian" or os == "ubuntu": + try: + from apt import cache + + cache = cache.Cache() + for pkg in deps: + if not cache.has_key(pkg) and not cache.is_virtual_package(pkg): + print( + f"{WARNING}WARNING:{ENDC} ignoring unavailable package %s" % pkg + ) + validated.remove(pkg) + except ModuleNotFoundError: + print( + f"{WARNING}WARNING:{ENDC} Unable to validate package dependency list without python3-apt" + ) + return validated + + +def get_build_dependencies(os, variant): + parsed = parse_dependencies(os, variant, False) + return _validate_deps(os, parsed) + + +def _get_installer_cmd(os, yes): + if os == "debian" or os == "ubuntu": + installer = ["apt", "install"] + elif os == "fedora": + installer = ["dnf", "install"] + elif os == "arch": + installer = ["pacman", "-Syu", "--noconfirm", "--needed"] + elif os == "void": + installer = ["xbps-install", "-Syu"] + else: + print("unable to detect OS profile, use --os= to specify") + print("\tsupported profiles: %s" % get_possible_profiles()) + sys.exit(1) + if yes: + installer += ["-y"] + return installer + + +def install_packages(os, variant, yes, debugging, packages): + import subprocess + + if packages == "build-dependencies": + packages = get_build_dependencies(os, variant) + installer = _get_installer_cmd(os, yes) + installer += packages + if debugging: + print(installer) + subprocess.call(installer) + + +if __name__ == "__main__": + + command = None + # compat mode for old training documentation + if "generate_dependencies.py" in sys.argv[0]: + command = "get-dependencies" + + parser = argparse.ArgumentParser() + if not command: + parser.add_argument( + "command", + choices=[ + "get-dependencies", + "test-markdown", + "test-meson", + "detect-profile", + "install-dependencies", + "install-pip", + ], + help="command to run", + ) + parser.add_argument( + "-o", + "--os", + default=detect_profile(), + choices=get_possible_profiles(), + help="calculate dependencies for OS profile", + ) + parser.add_argument( + "-v", "--variant", help="optional machine variant for the OS profile" + ) + parser.add_argument( + "-y", "--yes", action="store_true", help="Don't prompt to install" + ) + parser.add_argument( + "-d", "--debug", action="store_true", help="Display all launched commands" + ) + args = parser.parse_args() + + # fall back in all cases + if not args.variant: + args.variant = os.uname().machine + if not command: + command = args.command + + # command to run + if command == "test-markdown": + test_markdown(args.debug) + elif command == "test-meson": + test_meson(args.debug) + elif command == "detect-profile": + print(detect_profile()) + elif command == "get-dependencies": + dependencies = get_build_dependencies(args.os, args.variant) + print(*dependencies, sep="\n") + elif command == "install-dependencies": + install_packages( + args.os, args.variant, args.yes, args.debug, "build-dependencies" + ) + elif command == "install-pip": + install_packages(args.os, args.variant, args.yes, args.debug, ["python3-pip"]) diff --git a/fwupd-1.8.6/contrib/ci/generate_debian.py b/fwupd-1.8.6/contrib/ci/generate_debian.py new file mode 100755 index 0000000000000000000000000000000000000000..3741f753ae85e333db30ca9b6c621fbafeaec266 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/generate_debian.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell, Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# +import os +import sys +from fwupd_setup_helpers import parse_dependencies + + +def parse_control_dependencies(): + QUBES = os.getenv("QUBES") + return parse_dependencies("debian", "x86_64", True), QUBES + + +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, QUBES = parse_control_dependencies() + deps.sort() + + if QUBES: + lines += "\n" + control_qubes_in = os.path.join(target, "control.qubes.in") + with open(control_qubes_in, "r") as rfd: + lines += rfd.readlines() + + with open(control_out, "w") as wfd: + for line in lines: + if "Build-Depends:" in line and "%%%DYNAMIC%%%" in line: + wfd.write("Build-Depends:\n") + for i in range(0, len(deps)): + wfd.write("\t%s,\n" % deps[i]) + elif "fwupd-qubes-vm-whonix" in line and not QUBES: + break + 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 + except FileNotFoundError: + 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 --git a/fwupd-1.8.6/contrib/ci/generate_dependencies.py b/fwupd-1.8.6/contrib/ci/generate_dependencies.py new file mode 120000 index 0000000000000000000000000000000000000000..cc1f5adae6f2c45841c726c1ae9e2b0bb699406a --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/generate_dependencies.py @@ -0,0 +1 @@ +fwupd_setup_helpers.py \ No newline at end of file diff --git a/fwupd-1.8.6/contrib/ci/generate_docker.py b/fwupd-1.8.6/contrib/ci/generate_docker.py new file mode 100755 index 0000000000000000000000000000000000000000..ae43eae2b3f0de237b9db3dfad6437bc0a075525 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/generate_docker.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell, Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# +import os +import subprocess +import sys +import shutil +from fwupd_setup_helpers import parse_dependencies + + +def get_container_cmd(): + """return docker or podman as container manager""" + + if shutil.which("docker"): + return "docker" + if shutil.which("podman"): + return "podman" + + +directory = os.path.dirname(sys.argv[0]) +TARGET = os.getenv("OS") + +if TARGET is None: + print("Missing OS environment variable") + sys.exit(1) +OS = TARGET +SUBOS = "" +split = TARGET.split("-") +if len(split) >= 2: + OS = split[0] + SUBOS = split[1] + +deps = parse_dependencies(OS, SUBOS, False) + +f = os.path.join(directory, "Dockerfile-%s.in" % OS) +if not os.path.exists(f): + print("Missing input file %s for %s" % (f, OS)) + sys.exit(1) + +with open(f, "r") as rfd: + lines = rfd.readlines() + +with open("Dockerfile", "w") as wfd: + for line in lines: + if line.startswith("FROM %%%ARCH_PREFIX%%%"): + if (OS == "debian" or OS == "ubuntu") and SUBOS == "i386": + replace = SUBOS + "/" + else: + replace = "" + wfd.write(line.replace("%%%ARCH_PREFIX%%%", replace)) + elif line == "%%%INSTALL_DEPENDENCIES_COMMAND%%%\n": + if OS == "fedora": + wfd.write("RUN dnf --enablerepo=updates-testing -y install \\\n") + elif OS == "centos": + wfd.write("RUN yum -y install \\\n") + elif OS == "debian" or OS == "ubuntu": + wfd.write("RUN apt update -qq && \\\n") + wfd.write( + "\tDEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends\\\n" + ) + elif OS == "arch": + wfd.write("RUN pacman -Syu --noconfirm --needed\\\n") + elif OS == "void": + wfd.write( + "RUN xbps-install -Suy xbps && xbps-install -uy && xbps-install -y \\\n" + ) + for i in range(0, len(deps)): + if i < len(deps) - 1: + wfd.write("\t%s \\\n" % deps[i]) + else: + wfd.write("\t%s || true\n" % deps[i]) + elif line == "%%%ARCH_SPECIFIC_COMMAND%%%\n": + if OS == "debian" and SUBOS == "s390x": + # add sources + wfd.write( + 'RUN cat /etc/apt/sources.list | sed "s/deb/deb-src/" >> /etc/apt/sources.list\n' + ) + # add new architecture + wfd.write("RUN dpkg --add-architecture %s\n" % SUBOS) + elif line == "%%%OS%%%\n": + wfd.write("ENV OS %s\n" % TARGET) + else: + wfd.write(line) + wfd.flush() + +if len(sys.argv) == 2 and sys.argv[1] == "build": + cmd = get_container_cmd() + args = [cmd, "build", "-t", "fwupd-%s" % TARGET] + if "http_proxy" in os.environ: + args += ["--build-arg=http_proxy=%s" % os.environ["http_proxy"]] + if "https_proxy" in os.environ: + args += ["--build-arg=https_proxy=%s" % os.environ["https_proxy"]] + args += ["-f", "./Dockerfile", "."] + subprocess.check_call(args) diff --git a/fwupd-1.8.6/contrib/ci/generate_news.py b/fwupd-1.8.6/contrib/ci/generate_news.py new file mode 100755 index 0000000000000000000000000000000000000000..9dbf0d6d78efe083ac2d54e206510b557ac8258a --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/generate_news.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2020 Dell Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# +import os +import argparse +import xml.etree.ElementTree as etree + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("version", help="Generate news for release") + args = parser.parse_args() + + tree = etree.parse(os.path.join("data", "org.freedesktop.fwupd.metainfo.xml")) + root = tree.getroot() + for release in root.iter("release"): + if "version" not in release.attrib: + continue + if release.attrib["version"] != args.version: + continue + description = release.find("description") + result = etree.tostring(description, encoding="unicode", method="text") + print(result.strip()) diff --git a/fwupd-1.8.6/contrib/ci/get_test_firmware.sh b/fwupd-1.8.6/contrib/ci/get_test_firmware.sh new file mode 100755 index 0000000000000000000000000000000000000000..5a5b10271aa5791b04c4a3ca63f68df79db14036 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/get_test_firmware.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "$CI_NETWORK" = "true" ]; then + #clone fwupd-test-firmware + rm -rf fwupd-test-firmware + git clone https://github.com/fwupd/fwupd-test-firmware + #copy data for self-tests into the source tree + cp fwupd-test-firmware/ci-tests/* . -R +fi diff --git a/fwupd-1.8.6/contrib/ci/oss-fuzz.py b/fwupd-1.8.6/contrib/ci/oss-fuzz.py new file mode 100755 index 0000000000000000000000000000000000000000..ed850511230572d5fecf096ebec22746d5109381 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/oss-fuzz.py @@ -0,0 +1,459 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2021 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# +# pylint: disable=too-many-instance-attributes,no-self-use + +import os +import sys +import subprocess +import glob +from typing import Dict, Optional, List, Union + +DEFAULT_BUILDDIR = ".ossfuzz" + + +class Builder: + def __init__(self) -> None: + + self.cc = self._ensure_environ("CC", "gcc") + self.cxx = self._ensure_environ("CXX", "g++") + self.builddir = self._ensure_environ("WORK", os.path.realpath(DEFAULT_BUILDDIR)) + self.installdir = self._ensure_environ( + "OUT", os.path.realpath(os.path.join(DEFAULT_BUILDDIR, "out")) + ) + self.srcdir = self._ensure_environ("SRC", os.path.realpath("..")) + self.ldflags = [ + "-lpthread", + "-lresolv", + "-ldl", + "-lffi", + "-lz", + "-llzma", + ] + + # defined in env + self.cflags = ["-Wno-deprecated-declarations", "-g"] + if "CFLAGS" in os.environ: + self.cflags += os.environ["CFLAGS"].split(" ") + self.cxxflags = [] + if "CXXFLAGS" in os.environ: + self.cxxflags += os.environ["CXXFLAGS"].split(" ") + + # set up shared / static + os.environ["PKG_CONFIG"] = "pkg-config --static" + if "PATH" in os.environ: + os.environ["PATH"] = "{}:{}".format( + os.environ["PATH"], os.path.join(self.builddir, "bin") + ) + else: + os.environ["PATH"] = os.path.join(self.builddir, "bin") + os.environ["PKG_CONFIG_PATH"] = os.path.join(self.builddir, "lib", "pkgconfig") + + # writable + os.makedirs(self.builddir, exist_ok=True) + os.makedirs(self.installdir, exist_ok=True) + + def _ensure_environ(self, key: str, value: str) -> str: + """set the environment unless already set""" + if key not in os.environ: + os.environ[key] = value + return os.environ[key] + + def checkout_source(self, name: str, url: str, commit: Optional[str] = None) -> str: + """checkout source tree, optionally to a specific commit""" + srcdir_name = os.path.join(self.srcdir, name) + if os.path.exists(srcdir_name): + return srcdir_name + subprocess.run(["git", "clone", url], cwd=self.srcdir, check=True) + if commit: + subprocess.run(["git", "checkout", commit], cwd=srcdir_name, check=True) + return srcdir_name + + def build_meson_project(self, srcdir: str, argv) -> None: + """configure and build the meson project""" + srcdir_build = os.path.join(srcdir, DEFAULT_BUILDDIR) + if not os.path.exists(srcdir_build): + subprocess.run( + [ + "meson", + "--prefix", + self.builddir, + "--libdir", + "lib", + "--default-library", + "static", + ] + + argv + + [DEFAULT_BUILDDIR], + cwd=srcdir, + check=True, + ) + subprocess.run(["ninja", "install"], cwd=srcdir_build, check=True) + + def build_cmake_project(self, srcdir: str, argv=None) -> None: + """configure and build the meson project""" + if not argv: + argv = [] + srcdir_build = os.path.join(srcdir, DEFAULT_BUILDDIR) + if not os.path.exists(srcdir_build): + os.makedirs(srcdir_build, exist_ok=True) + subprocess.run( + [ + "cmake", + "-DCMAKE_INSTALL_PREFIX:PATH={}".format(self.builddir), + "-DCMAKE_INSTALL_LIBDIR={}".format("lib"), + ] + + argv + + [".."], + cwd=srcdir_build, + check=True, + ) + subprocess.run(["make", "all", "install"], cwd=srcdir_build, check=True) + + def add_work_includedir(self, value: str) -> None: + """add a CFLAG""" + self.cflags.append("-I{}/{}".format(self.builddir, value)) + + def add_src_includedir(self, value: str) -> None: + """add a CFLAG""" + self.cflags.append("-I{}/{}".format(self.srcdir, value)) + + def add_build_ldflag(self, value: str) -> None: + """add a LDFLAG""" + self.ldflags.append(os.path.join(self.builddir, value)) + + def substitute(self, src: str, replacements: Dict[str, str]) -> str: + """map changes""" + + dst = os.path.basename(src).replace(".in", "") + with open(os.path.join(self.srcdir, src), "r") as f: + blob = f.read() + for key in replacements: + blob = blob.replace(key, replacements[key]) + with open(os.path.join(self.builddir, dst), "w") as out: + out.write(blob) + return dst + + def compile(self, src: str) -> str: + """compile a specific source file""" + argv = [self.cc] + argv.extend(self.cflags) + fullsrc = os.path.join(self.srcdir, src) + if not os.path.exists(fullsrc): + fullsrc = os.path.join(self.builddir, src) + dst = os.path.basename(src).replace(".c", ".o") + argv.extend(["-c", fullsrc, "-o", os.path.join(self.builddir, dst)]) + print("building {} into {}".format(src, dst)) + try: + subprocess.run(argv, cwd=self.srcdir, check=True) + except subprocess.CalledProcessError as e: + print(e) + sys.exit(1) + return os.path.join(self.builddir, "{}".format(dst)) + + def link(self, objs: List[str], dst: str) -> None: + """link multiple obects into a binary""" + argv = [self.cxx] + self.cxxflags + for obj in objs: + if obj.startswith("-"): + argv.append(obj) + else: + argv.append(os.path.join(self.builddir, obj)) + argv += ["-o", os.path.join(self.installdir, dst)] + argv += self.ldflags + print("building {} into {}".format(",".join(objs), dst)) + subprocess.run(argv, cwd=self.srcdir, check=True) + + def mkfuzztargets(self, globstr: str) -> None: + """make binary fuzzing targets from builder.xml files""" + builder_xmls = glob.glob(globstr) + if not builder_xmls: + print("failed to find {}".format(globstr)) + sys.exit(1) + for fn_src in builder_xmls: + fn_dst = fn_src.replace(".builder.xml", ".bin") + if os.path.exists(fn_dst): + continue + print("building {} into {}".format(fn_src, fn_dst)) + try: + argv = [ + "build/src/fwupdtool", + "firmware-build", + fn_src, + fn_dst, + ] + subprocess.run(argv, check=True) + except subprocess.CalledProcessError as e: + print("tried to run: `{}` and got {}".format(" ".join(argv), str(e))) + sys.exit(1) + + def write_header( + self, dst: str, defines: Dict[str, Optional[Union[str, int]]] + ) -> None: + """write a header file""" + dstdir = os.path.join(self.builddir, os.path.dirname(dst)) + os.makedirs(dstdir, exist_ok=True) + print("writing {}".format(dst)) + with open(os.path.join(dstdir, os.path.basename(dst)), "w") as f: + for key in defines: + value = defines[key] + if value is not None: + if isinstance(value, int): + f.write("#define {} {}\n".format(key, value)) + else: + f.write('#define {} "{}"\n'.format(key, value)) + else: + f.write("#define {}\n".format(key)) + self.add_work_includedir(os.path.dirname(dst)) + + def makezip(self, dst: str, globstr: str) -> None: + """create a zip file archive from a glob""" + argv = ["zip", "--junk-paths", os.path.join(self.installdir, dst)] + glob.glob( + os.path.join(self.srcdir, globstr) + ) + print("assembling {}".format(dst)) + subprocess.run(argv, cwd=self.srcdir, check=True) + + def grep_meson(self, src: str, token: str = "fuzzing") -> List[str]: + """find source files tagged with a specific comment""" + srcs = [] + with open(os.path.join(self.srcdir, src, "meson.build"), "r") as f: + for line in f.read().split("\n"): + if line.find(token) == -1: + continue + + # get rid of token + line = line.split("#")[0] + + # get rid of variable + try: + line = line.split("=")[1] + except IndexError: + pass + + # get rid of whitespace + for char in ["'", ",", " "]: + line = line.replace(char, "") + + # all done + srcs.append(os.path.join(src, line)) + return srcs + + +class Fuzzer: + def __init__(self, name, srcdir=None, globstr=None, pattern=None) -> None: + + self.name = name + self.srcdir = srcdir or name + self.globstr = globstr or "{}*.bin".format(name) + self.pattern = pattern or "{}-firmware".format(name) + + @property + def new_gtype(self) -> str: + return "fu_{}_new".format(self.pattern).replace("-", "_") + + @property + def header(self) -> str: + return "fu-{}.h".format(self.pattern) + + +def _build(bld: Builder) -> None: + + # CBOR + src = bld.checkout_source( + "libcbor", url="https://github.com/PJK/libcbor.git", commit="v0.9.0" + ) + bld.build_cmake_project(src) + bld.add_build_ldflag("lib/libcbor.a") + + # GLib + src = bld.checkout_source( + "glib", url="https://gitlab.gnome.org/GNOME/glib.git", commit="glib-2-68" + ) + bld.build_meson_project( + src, + [ + "-Dlibmount=disabled", + "-Dselinux=disabled", + "-Dnls=disabled", + "-Dlibelf=disabled", + "-Dbsymbolic_functions=false", + "-Dtests=false", + "-Dinternal_pcre=true", + "--force-fallback-for=libpcre", + ], + ) + bld.add_work_includedir("include/glib-2.0") + bld.add_work_includedir("lib/glib-2.0/include") + bld.add_build_ldflag("lib/libgio-2.0.a") + bld.add_build_ldflag("lib/libgmodule-2.0.a") + bld.add_build_ldflag("lib/libgobject-2.0.a") + bld.add_build_ldflag("lib/libglib-2.0.a") + bld.add_build_ldflag("lib/libgthread-2.0.a") + + # JSON-GLib + src = bld.checkout_source( + "json-glib", url="https://gitlab.gnome.org/GNOME/json-glib.git" + ) + bld.build_meson_project( + src, ["-Dgtk_doc=disabled", "-Dtests=false", "-Dintrospection=disabled"] + ) + bld.add_work_includedir("include/json-glib-1.0/json-glib") + bld.add_work_includedir("include/json-glib-1.0") + bld.add_build_ldflag("lib/libjson-glib-1.0.a") + + # libxmlb + src = bld.checkout_source("libxmlb", url="https://github.com/hughsie/libxmlb.git") + bld.build_meson_project( + src, ["-Dgtkdoc=false", "-Dintrospection=false", "-Dtests=false"] + ) + bld.add_work_includedir("include/libxmlb-2") + bld.add_work_includedir("include/libxmlb-2/libxmlb") + bld.add_build_ldflag("lib/libxmlb.a") + + # write required headers + bld.write_header("libfwupd/fwupd-version.h", {}) + bld.write_header( + "config.h", + { + "FWUPD_DATADIR": "/tmp", + "FWUPD_LOCALSTATEDIR": "/tmp", + "FWUPD_LIBDIR_PKG": "/tmp", + "FWUPD_SYSCONFDIR": "/tmp", + "HAVE_REALPATH": None, + "PACKAGE_NAME": "fwupd", + "PACKAGE_VERSION": "0.0.0", + }, + ) + bld.write_header( + "libfwupdplugin/fu-version.h", + { + "FU_MAJOR_VERSION": 0, + "FU_MINOR_VERSION": 0, + "FU_MICRO_VERSION": 0, + }, + ) + + # libfwupd + libfwupdplugin + built_objs: List[str] = [] + bld.add_src_includedir("fwupd") + for path in ["fwupd/libfwupd", "fwupd/libfwupdplugin"]: + bld.add_src_includedir(path) + for src in bld.grep_meson(path): + built_objs.append(bld.compile(src)) + + # dummy binary entrypoint + if "LIB_FUZZING_ENGINE" in os.environ: + built_objs.append(os.environ["LIB_FUZZING_ENGINE"]) + else: + built_objs.append(bld.compile("fwupd/libfwupdplugin/fu-fuzzer-main.c")) + + # built in formats + for fzr in [ + Fuzzer("dfuse"), + Fuzzer("fdt"), + Fuzzer("fit"), + Fuzzer("fmap"), + Fuzzer("ihex"), + Fuzzer("srec"), + Fuzzer("intel-thunderbolt"), + Fuzzer("ifwi-cpd"), + Fuzzer("ifwi-fpt"), + Fuzzer("oprom"), + Fuzzer("uswid"), + Fuzzer("efi-firmware-filesystem", pattern="efi-firmware-filesystem"), + Fuzzer("efi-firmware-volume", pattern="efi-firmware-volume"), + Fuzzer("ifd"), + ]: + src = bld.substitute( + "fwupd/libfwupdplugin/fu-fuzzer-firmware.c.in", + { + "@FIRMWARENEW@": fzr.new_gtype, + "@INCLUDE@": os.path.join("libfwupdplugin", fzr.header), + }, + ) + bld.link([bld.compile(src)] + built_objs, "{}_fuzzer".format(fzr.name)) + bld.mkfuzztargets( + os.path.join( + bld.srcdir, + "fwupd", + "libfwupdplugin", + "tests", + "{}*.builder.xml".format(fzr.name), + ) + ) + bld.makezip( + "{}_fuzzer_seed_corpus.zip".format(fzr.name), + "fwupd/libfwupdplugin/tests/{}".format(fzr.globstr), + ) + + # plugins + for fzr in [ + Fuzzer("acpi-phat", pattern="acpi-phat"), + Fuzzer("bcm57xx"), + Fuzzer("ccgx-dmc", srcdir="ccgx", globstr="ccgx-dmc*.bin"), + Fuzzer("ccgx"), + Fuzzer("cros-ec"), + Fuzzer("ebitdo"), + Fuzzer("elanfp"), + Fuzzer("elantp"), + Fuzzer("genesys-scaler", srcdir="genesys", pattern="genesys-scaler-firmware"), + Fuzzer("genesys-usbhub", srcdir="genesys", pattern="genesys-usbhub-firmware"), + Fuzzer("pixart", srcdir="pixart-rf", pattern="pxi-firmware"), + Fuzzer("redfish-smbios", srcdir="redfish", pattern="redfish-smbios"), + Fuzzer("synaptics-prometheus", pattern="synaprom-firmware"), + Fuzzer("synaptics-cape"), + Fuzzer("synaptics-mst"), + Fuzzer("synaptics-rmi"), + Fuzzer("uf2"), + Fuzzer("wacom-usb", pattern="wac-firmware"), + ]: + fuzz_objs = [] + for obj in bld.grep_meson("fwupd/plugins/{}".format(fzr.srcdir)): + fuzz_objs.append(bld.compile(obj)) + src = bld.substitute( + "fwupd/libfwupdplugin/fu-fuzzer-firmware.c.in", + { + "@FIRMWARENEW@": fzr.new_gtype, + "@INCLUDE@": os.path.join("plugins", fzr.srcdir, fzr.header), + }, + ) + fuzz_objs.append(bld.compile(src)) + bld.link(fuzz_objs + built_objs, "{}_fuzzer".format(fzr.name)) + bld.mkfuzztargets( + os.path.join( + bld.srcdir, + "fwupd", + "plugins", + fzr.srcdir, + "tests", + "{}*.builder.xml".format(fzr.name), + ) + ) + bld.makezip( + "{}_fuzzer_seed_corpus.zip".format(fzr.name), + "fwupd/plugins/{}/tests/{}".format(fzr.srcdir, fzr.globstr), + ) + + +if __name__ == "__main__": + + # install missing deps here rather than patching the Dockerfile in oss-fuzz + try: + subprocess.check_call( + ["apt-get", "install", "-y", "liblzma-dev", "libcbor-dev"], + stdout=open(os.devnull, "wb"), + ) + except FileNotFoundError: + pass + except subprocess.CalledProcessError as e: + print(e.output) + sys.exit(1) + + _builder = Builder() + _build(_builder) + sys.exit(0) diff --git a/fwupd-1.8.6/contrib/ci/populate-bios-setting-translations.py b/fwupd-1.8.6/contrib/ci/populate-bios-setting-translations.py new file mode 100755 index 0000000000000000000000000000000000000000..126ae72264ab998ea3573da56025de3b51ad092b --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/populate-bios-setting-translations.py @@ -0,0 +1,37 @@ +#!/usr/bin/python3 +# +# Helper script to generate a list of translations +# Sample call: +# ./contrib/ci/populate-bios-attr-translations.py ./libfwupdplugin/tests/bios-attrs/dell-xps13-9310/ +# will lead to ./libfwupdplugin/tests/bios-attrs/dell-xps13-9310/strings.txt +# which can be added to po/POTFILES.in +# +# Copyright (C) 2022 Mario Limonciello +# +# SPDX-License-Identifier: LGPL-2.1+ + +import os +import sys + + +def populate_translations(path): + output = open(os.path.join(path, "strings.txt"), "w") + for root, _, files in os.walk(path): + for file in files: + val: str = "" + if not file.endswith("display_name"): + continue + with open(os.path.join(root, file), "r") as f: + val = f.read().replace('"', "").strip() + if not val: + continue + output.write("#TRANSLATORS: Description of BIOS setting\n") + output.write("%s\n\n" % val) + output.close() + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("path to bios settings directory required") + sys.exit(1) + populate_translations(sys.argv[1]) diff --git a/fwupd-1.8.6/contrib/ci/qt5-thread-test/meson.build b/fwupd-1.8.6/contrib/ci/qt5-thread-test/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..6d1525dbd63f3c128447f7867f1c280c067b5ee7 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/qt5-thread-test/meson.build @@ -0,0 +1,25 @@ +project('qt5-thread-test', 'cpp', + license: 'LGPL-2.1+', +) +add_project_arguments('-fPIC', language: 'cpp') +qt5core = dependency('Qt5Core') +qt5concurrent = dependency('Qt5Concurrent') +glib2 = dependency('glib-2.0') +gio2 = dependency('gio-2.0') +fwupd = dependency('fwupd') +env = environment() +env.set('G_DEBUG', 'fatal-criticals') +e = executable( + 'qt-thread-test', + sources: [ + 'qt-thread-test.cpp' + ], + dependencies: [ + qt5core, + qt5concurrent, + glib2, + gio2, + fwupd, + ], +) +test('qt-thread-test', e, timeout: 60, env: env) diff --git a/fwupd-1.8.6/contrib/ci/qt5-thread-test/qt-thread-test.cpp b/fwupd-1.8.6/contrib/ci/qt5-thread-test/qt-thread-test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..daa6cb2e44d894720157377d201e9c373c683721 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/qt5-thread-test/qt-thread-test.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 Aleix Pol + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +#include +#include +#include + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + + auto client = fwupd_client_new(); + auto cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + + auto fw = new QFutureWatcher(&app); + QObject::connect(fw, &QFutureWatcher::finished, [fw]() { + QCoreApplication::exit(0); + }); + fw->setFuture(QtConcurrent::run([&] { + return fwupd_client_get_devices(client, cancellable, &error); + })); + + return app.exec(); +} diff --git a/fwupd-1.8.6/contrib/ci/s390x_cross.txt b/fwupd-1.8.6/contrib/ci/s390x_cross.txt new file mode 100644 index 0000000000000000000000000000000000000000..c7c0e143a7d9b9dc1f37faa619951af7e98e1aa6 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/s390x_cross.txt @@ -0,0 +1,13 @@ +[binaries] +c = 's390x-linux-gnu-gcc' +cpp = 's390x-linux-gnu-cpp' +ar = 's390x-linux-gnu-ar' +strip = 's390x-linux-gnu-strip' +pkgconfig = 's390x-linux-gnu-pkg-config' +exe_wrapper = 'qemu-s390x' + +[host_machine] +system = 'linux' +cpu_family = 's390x' +cpu = 's390x' +endian = 'big' diff --git a/fwupd-1.8.6/contrib/ci/snap.sh b/fwupd-1.8.6/contrib/ci/snap.sh new file mode 100755 index 0000000000000000000000000000000000000000..997660e226122cca918524109a39d36d33127152 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/snap.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e + +mkdir -p /build +cd /build +snapcraft diff --git a/fwupd-1.8.6/contrib/ci/snapcraft-wrapper b/fwupd-1.8.6/contrib/ci/snapcraft-wrapper new file mode 100755 index 0000000000000000000000000000000000000000..0072ed079689439b4b3f9f5b23ed251a6668fb11 --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/snapcraft-wrapper @@ -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 --git a/fwupd-1.8.6/contrib/ci/ubuntu.sh b/fwupd-1.8.6/contrib/ci/ubuntu.sh new file mode 100755 index 0000000000000000000000000000000000000000..c70a1b6eb7152c765f8dbc5989de0e8a8659960b --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/ubuntu.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -e +set -x + +#check for and install missing dependencies +./contrib/ci/fwupd_setup_helpers.py install-dependencies --yes -o ubuntu + +#check we have pip +./contrib/ci/fwupd_setup_helpers.py install-pip --yes -o ubuntu + +#check meson is new enough +./contrib/ci/fwupd_setup_helpers.py test-meson + +#check markdown is new enough +./contrib/ci/fwupd_setup_helpers.py test-markdown + +#clone test firmware if necessary +. ./contrib/ci/get_test_firmware.sh + +#evaluate using Ubuntu's buildflags +#evaluate using Debian/Ubuntu's buildflags +#disable link time optimization, Ubuntu currently only sets it for GCC +export DEB_BUILD_MAINT_OPTIONS="optimize=-lto" +eval "$(dpkg-buildflags --export=sh)" +#filter out -Bsymbolic-functions +LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") +export LDFLAGS + +root=$(pwd) +rm -rf ${root}/build +chown -R nobody ${root} +sudo -u nobody meson ${root}/build -Dman=false -Ddocs=enabled -Dgusb:tests=false --prefix=${root}/dist +#build with clang +sudo -u nobody ninja -C ${root}/build test -v + +#make docs available outside of docker +ninja -C ${root}/build install -v diff --git a/fwupd-1.8.6/contrib/ci/void.sh b/fwupd-1.8.6/contrib/ci/void.sh new file mode 100755 index 0000000000000000000000000000000000000000..f1957e3cee5029823d79788d0a48763b103e791e --- /dev/null +++ b/fwupd-1.8.6/contrib/ci/void.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e +set -x + +#install dependencies +xbps-install -Suy python3 +./contrib/ci/fwupd_setup_helpers.py install-dependencies --yes -o void + +#clone test firmware if necessary +. ./contrib/ci/get_test_firmware.sh + +#build +rm -rf build +meson build \ + -Dgusb:tests=false \ + -Dgcab:docs=false \ + -Dconsolekit=disabled \ + -Dsystemd=disabled \ + -Doffline=disabled \ + -Delogind=enabled +ninja -C build test -v diff --git a/fwupd-1.8.6/contrib/codespell.cfg b/fwupd-1.8.6/contrib/codespell.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0de0d9896c1a784721162ce8d030f6d131b6646d --- /dev/null +++ b/fwupd-1.8.6/contrib/codespell.cfg @@ -0,0 +1,4 @@ +[codespell] +builtin = clear,informal,en-GB_to_en-US +skip = *.po,*.csv,*.svg,*.p7c,subprojects,.git,pcrs,build*,.ossfuzz,*/tests/* +ignore-words-list = conexant,Conexant,gir,GIR,hsi,HSI,cancelled,Cancelled,te diff --git a/fwupd-1.8.6/contrib/debian/README.Debian b/fwupd-1.8.6/contrib/debian/README.Debian new file mode 100644 index 0000000000000000000000000000000000000000..9dc82b53429790e88345fbf91d88190539038a41 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/README.Debian @@ -0,0 +1,18 @@ +signed vs unsigned fwupd programs +------------------------------------ + +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. + diff --git a/fwupd-1.8.6/contrib/debian/README.source b/fwupd-1.8.6/contrib/debian/README.source new file mode 100644 index 0000000000000000000000000000000000000000..85620815eb492707da42bf299ade020986990012 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/README.source @@ -0,0 +1,9 @@ +fwupd for Debian +---------------- + +To build from the git tree, run: + git-buildpackage -us -uc -S +Then, if using sbuild, you can use something like: + sbuild -s -c sid-amd64 -d unstable + + -- Daniel Jared Dominguez Thu, 21 May 2015 13:44:16 -0500 diff --git a/fwupd-1.8.6/contrib/debian/clean b/fwupd-1.8.6/contrib/debian/clean new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/clean @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/contrib/debian/compat b/fwupd-1.8.6/contrib/debian/compat new file mode 100644 index 0000000000000000000000000000000000000000..48082f72f087ce7e6fa75b9c41d7387daecd447b --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/compat @@ -0,0 +1 @@ +12 diff --git a/fwupd-1.8.6/contrib/debian/control.in b/fwupd-1.8.6/contrib/debian/control.in new file mode 100644 index 0000000000000000000000000000000000000000..85d1af4b42d81a868ee4aa0b5647bc5b43d8b6c0 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/control.in @@ -0,0 +1,140 @@ +Source: fwupd +Priority: optional +Maintainer: Debian EFI +Uploaders: Steve McIntyre <93sam@debian.org>, + Matthias Klumpp , + Mario Limonciello +Build-Depends: %%%DYNAMIC%%% +Build-Depends-Indep: gi-docgen , + libglib2.0-doc , +Rules-Requires-Root: no +Standards-Version: 4.6.0.1 +Section: admin +Homepage: https://github.com/fwupd/fwupd +Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git +Vcs-Browser: https://salsa.debian.org/efi-team/fwupd + +Package: libfwupd2 +Section: libs +Architecture: linux-any +Depends: ${misc:Depends}, + ${shlibs:Depends} +Multi-Arch: same +Description: Firmware update daemon library + fwupd is a daemon to allow session software to update device firmware. + You can either use a GUI software manager like GNOME Software to view and + apply updates, the command-line tool or the system D-Bus interface directly. + Firmware updates are supported for a variety of technologies. + See for details + . + This package provides the library used by the daemon. + +Package: fwupd +Architecture: linux-any +Depends: ${misc:Depends}, + ${shlibs:Depends}, + adduser, + shared-mime-info +Recommends: python3, + bolt, + dbus, + secureboot-db, + udisks2, + fwupd-signed, + jq +Suggests: gir1.2-fwupd-2.0 +Provides: fwupdate +Conflicts: fwupdate-amd64-signed, + fwupdate-i386-signed, + fwupdate-arm64-signed, + fwupdate-armhf-signed +Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), + libdfu1 (<< 0.9.7-1), + fwupdate (<< 12-7), + libdfu-dev (<< 0.9.7-1) +Replaces: gir1.2-dfu-1.0 (<< 0.9.7-1), + libdfu1 (<< 0.9.7-1), + libdfu-dev (<< 0.9.7-1), + fwupdate (<< 12-7) +Multi-Arch: foreign +Description: Firmware update daemon + fwupd is a daemon to allow session software to update device firmware. + You can either use a GUI software manager like GNOME Software to view and + apply updates, the command-line tool or the system D-Bus interface directly. + Firmware updates are supported for a variety of technologies. + See for details + +Package: fwupd-tests +Architecture: linux-any +Depends: ${misc:Depends}, + ${shlibs:Depends}, + ca-certificates, + dbus-x11, + fwupd, + gnome-desktop-testing, + policykit-1, + python3, + python3-gi, + python3-requests, +Breaks: fwupd (<< 0.9.4-1) +Replaces: fwupd (<< 0.9.4-1) +Multi-Arch: foreign +Description: Test suite for firmware update daemon + fwupd is a daemon to allow session software to update device firmware. + You can either use a GUI software manager like GNOME Software to view and + apply updates, the command-line tool or the system D-Bus interface directly. + 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 + the daemon in a continuous integration system. + +Package: fwupd-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, +Build-Profiles: +Description: Firmware update daemon documentation (HTML format) + fwupd is a daemon to allow session software to update device firmware. + You can either use a GUI software manager like GNOME Software to view and + apply updates, the command-line tool or the system D-Bus interface directly. + Firmware updates are supported for a variety of technologies. + See for details + . + This package provides development documentation for creating a package that + uses fwupd. + +Package: libfwupd-dev +Architecture: linux-any +Multi-Arch: same +Depends: libfwupd2 (= ${binary:Version}), + gir1.2-fwupd-2.0 (= ${binary:Version}), + libcurl4-gnutls-dev, + libglib2.0-dev (>= 2.45.8), + libjcat-dev, + libjson-glib-dev (>= 1.1.1), + ${misc:Depends} +Breaks: fwupd-dev (<< 0.5.4-2~) +Replaces: fwupd-dev (<< 0.5.4-2~) +Section: libdevel +Description: development files for libfwupd + fwupd is a daemon to allow session software to update device firmware. + You can either use a GUI software manager like GNOME Software to view and + apply updates, the command-line tool or the system D-Bus interface directly. + Firmware updates are supported for a variety of technologies. + See for details + . + This package provides the development files for libfwupd + +Package: gir1.2-fwupd-2.0 +Architecture: linux-any +Multi-Arch: same +Depends: ${misc:Depends}, + ${gir:Depends} +Section: introspection +Description: GObject introspection data for libfwupd + This package provides the introspection data for libfwupd. + . + It can be used by packages using the GIRepository format to generate + dynamic bindings. \ No newline at end of file diff --git a/fwupd-1.8.6/contrib/debian/control.qubes.in b/fwupd-1.8.6/contrib/debian/control.qubes.in new file mode 100644 index 0000000000000000000000000000000000000000..03ab18b90b4669517ff0008390390fe706994844 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/control.qubes.in @@ -0,0 +1,4 @@ +Package: fwupd-qubes-vm-whonix +Architecture: amd64 +Description: Whonix support for Qubes OS + This package is used to download firmware updates and metadata via TOR. diff --git a/fwupd-1.8.6/contrib/debian/copyright.in b/fwupd-1.8.6/contrib/debian/copyright.in new file mode 100644 index 0000000000000000000000000000000000000000..2849465834728519ab7721fab59818f2e4c92df2 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/copyright.in @@ -0,0 +1,140 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fwupd +Source: https://github.com/fwupd/fwupd + +%%%DYNAMIC%%% +Files: *.metainfo.xml +Copyright: 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 --git a/fwupd-1.8.6/contrib/debian/docs b/fwupd-1.8.6/contrib/debian/docs new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/docs @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/contrib/debian/fwupd-doc.links b/fwupd-1.8.6/contrib/debian/fwupd-doc.links new file mode 100644 index 0000000000000000000000000000000000000000..9d0a00d2b409e3e4f6542d9cf35df9d238f18d56 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-doc.links @@ -0,0 +1,2 @@ +usr/share/doc/libfwupd /usr/share/gtk-doc/html/libfwupd +usr/share/doc/libfwupdplugin /usr/share/gtk-doc/html/libfwupdplugin diff --git a/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.install b/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.install new file mode 100644 index 0000000000000000000000000000000000000000..6f64b6ca16caaadb6de3f06ab81e0c74b8e2e486 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.install @@ -0,0 +1,2 @@ +usr/libexec/qubes-fwupd/fwupd_download_updates.py +usr/libexec/qubes-fwupd/fwupd_common_vm.py diff --git a/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.postinst b/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.postinst new file mode 100644 index 0000000000000000000000000000000000000000..6d8d203b34ca212c7d858dc6d40c5b64d5de3fdf --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.postinst @@ -0,0 +1,11 @@ +#!/bin/bash + +HOME_DIR=`getent passwd user | awk '{ split($$0,a,":"); print a[6]}'` + +if [ -z "$HOME_DIR" ]; then + echo "Default user does not exist!!" >&2 + echo "Package does not create fwupd directories" >&2 +else + mkdir -p $HOME_DIR/.cache/fwupd_download_updates + chown -R user:user $HOME_DIR/.cache/fwupd_download_updates +fi diff --git a/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.postrm b/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.postrm new file mode 100644 index 0000000000000000000000000000000000000000..ac210c073f62c2149b5c787b88f5d37c0d6fa8aa --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-qubes-vm-whonix.postrm @@ -0,0 +1,7 @@ +#!/bin/bash + +HOME_DIR=`getent passwd user | awk '{ split($$0,a,":"); print a[6]}'` + +if [ -z "$HOME_DIR" ] && [ "$1" = "purge" ]; then + rm -rf $HOME_DIR/.cache/fwupd +fi diff --git a/fwupd-1.8.6/contrib/debian/fwupd-tests.install b/fwupd-1.8.6/contrib/debian/fwupd-tests.install new file mode 100644 index 0000000000000000000000000000000000000000..f1bd212994d98625e848a23ec96e3f233c262670 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-tests.install @@ -0,0 +1,10 @@ +#These are in a generic looking directory because +#that is where gnome-desktop-testing expects to +#find them. for more information see: +#https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=872458 +usr/share/installed-tests/* +usr/share/fwupd/device-tests/*.json +usr/libexec/installed-tests/fwupd/fwupd.sh +usr/libexec/installed-tests/fwupd/*-self-test +debian/lintian/fwupd-tests usr/share/lintian/overrides +etc/fwupd/remotes.d/fwupd-tests.conf diff --git a/fwupd-1.8.6/contrib/debian/fwupd-tests.postinst b/fwupd-1.8.6/contrib/debian/fwupd-tests.postinst new file mode 100644 index 0000000000000000000000000000000000000000..81a462c455de72afaa964d3bee0b9860f71f07f3 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-tests.postinst @@ -0,0 +1,23 @@ +#!/bin/sh +set -e + +#DEBHELPER# + +#only enable on installation not upgrade +if [ "$1" = configure ] && [ -z "$2" ]; then + if [ -f /etc/fwupd/daemon.conf ]; then + if [ "$CI" = "true" ]; then + sed "s,^DisabledPlugins=.*,DisabledPlugins=," -i /etc/fwupd/daemon.conf + else + echo "To enable test suite, modify /etc/fwupd/daemon.conf" + fi + fi + if [ -f /etc/fwupd/remotes.d/fwupd-tests.conf ]; then + if [ "$CI" = "true" ]; then + sed "s,^Enabled=false,Enabled=true," -i /etc/fwupd/remotes.d/fwupd-tests.conf + else + echo "To enable test suite, enable fwupd-tests remote" + fi + + fi +fi diff --git a/fwupd-1.8.6/contrib/debian/fwupd-tests.postrm b/fwupd-1.8.6/contrib/debian/fwupd-tests.postrm new file mode 100644 index 0000000000000000000000000000000000000000..c43835526d90892b53672e69e99147db4d5d108c --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd-tests.postrm @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +#DEBHELPER# + +if [ "$1" = remove -o "$1" = purge ]; then + if [ -f /etc/fwupd/daemon.conf ]; then + if [ "$CI" = "true" ]; then + sed "s,^DisabledPlugins=,DisabledPlugins=test," -i /etc/fwupd/daemon.conf + else + echo "To disable test suite, modify /etc/fwupd/daemon.conf" + fi + fi +fi diff --git a/fwupd-1.8.6/contrib/debian/fwupd.dirs b/fwupd-1.8.6/contrib/debian/fwupd.dirs new file mode 100644 index 0000000000000000000000000000000000000000..a7c32e1dcd3d7708ca1e4e5df70e3ac5a856f6bf --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.dirs @@ -0,0 +1 @@ +var/cache/app-info/xmls diff --git a/fwupd-1.8.6/contrib/debian/fwupd.install b/fwupd-1.8.6/contrib/debian/fwupd.install new file mode 100644 index 0000000000000000000000000000000000000000..e1d5abcd914bd67771e9bb717f746af3a00124ec --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.install @@ -0,0 +1,23 @@ +usr/bin/* +etc/* +usr/share/bash-completion +usr/share/fish/vendor_completions.d +usr/share/fwupd/*.* +usr/share/fwupd/metainfo/* +usr/share/fwupd/remotes.d/* +usr/share/dbus-1/* +usr/share/icons/* +usr/share/polkit-1/* +usr/share/locale +usr/share/metainfo/* +usr/libexec/fwupd/* +usr/share/man/man1/* +lib/systemd/system/* +lib/systemd/system-preset/* +lib/systemd/system-shutdown/* +lib/udev/rules.d/* +data/daemon.conf etc/fwupd +debian/fwupd.pkla /var/lib/polkit-1/localauthority/10-vendor.d +usr/lib/*/fwupd-*/*.so +debian/lintian/fwupd usr/share/lintian/overrides +obj*/data/motd/85-fwupd /etc/update-motd.d diff --git a/fwupd-1.8.6/contrib/debian/fwupd.maintscript b/fwupd-1.8.6/contrib/debian/fwupd.maintscript new file mode 100644 index 0000000000000000000000000000000000000000..fb26d1bd79978db04c0808e19a714db74db0036c --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.maintscript @@ -0,0 +1,6 @@ + +rm_conffile /etc/fwupd.conf 1.0.0~ +rm_conffile /etc/fwupd/remotes.d/fwupd.conf 1.2.7~ +rm_conffile /etc/dbus-1/system.d/org.freedesktop.fwupd.conf 1.3.2~ +rm_conffile /etc/modules-load.d/fwupd-msr.conf 1.5.3~ +rm_conffile /etc/modules-load.d/fwupd-platform-integrity.conf 1.5.3~ diff --git a/fwupd-1.8.6/contrib/debian/fwupd.pkla b/fwupd-1.8.6/contrib/debian/fwupd.pkla new file mode 100644 index 0000000000000000000000000000000000000000..834fda570fb408b59ccb0b14acaa170ceb79c128 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.pkla @@ -0,0 +1,5 @@ +[Call internal fwupd actions] +Identity=unix-group:admin;unix-group:sudo +Action=org.freedesktop.fwupd.update-internal +ResultActive=yes + diff --git a/fwupd-1.8.6/contrib/debian/fwupd.postinst b/fwupd-1.8.6/contrib/debian/fwupd.postinst new file mode 100644 index 0000000000000000000000000000000000000000..02f2827830aa103cb50875888f850501d179c46f --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.postinst @@ -0,0 +1,57 @@ +#!/bin/sh +set -e + +#DEBHELPER# + +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~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/dbus-1/system.d/org.freedesktop.fwupd.conf 1.3.2~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/fwupd/ata.conf 1.5.5~ -- "$@" +fi + +#Perform transition from /etc/fwupd/uefi.conf to /etc/fwupd/uefi_capsule.conf +if dpkg-maintscript-helper supports mv_conffile 2>/dev/null; then + ORIGINAL=/etc/fwupd/uefi.conf + NEW=/etc/fwupd/uefi_capsule.conf + #If already upgraded this file won't exist + #If in the middle of an upgrade: + # -> If unmodified then preinst would have renamed to /etc/fwupd/uefi.conf.dpkg-remove + # -> If modified, we need to do an in-place upgrade with sed + if [ -f $ORIGINAL ]; then + sed "s,\[uefi\],\[uefi_capsule\]," -i $ORIGINAL + fi + dpkg-maintscript-helper mv_conffile $ORIGINAL $NEW 1.5.5~ -- "$@" +fi + +# Clean up from fwupdate->fwupd transition +# This can be removed after bullseye and focal are released +EFIDIR=$(awk '/^ID=/ {gsub(/"/,""); split($$0,a,"="); print tolower(a[2])}' /etc/os-release) +if [ "${DPKG_MAINTSCRIPT_ARCH}" = "amd64" ]; then + EFI_NAME=x64 +elif [ "${DPKG_MAINTSCRIPT_ARCH}" = "i386" ]; then + EFI_NAME=ia32 +elif [ "${DPKG_MAINTSCRIPT_ARCH}" = "arm64" ]; then + EFI_NAME=aa64 +elif [ "${DPKG_MAINTSCRIPT_ARCH}" = "armhf" ]; then + EFI_NAME=arm +fi +rm -f /boot/efi/EFI/$EFIDIR/fwup$EFI_NAME.efi +rm -f /var/lib/fwupdate/done +rm -f /var/cache/fwupdate/done +for dir in /var/cache/fwupdate /var/lib/fwupdate; do + if [ -d $dir ]; then + rmdir --ignore-fail-on-non-empty $dir || true + fi +done + +#create a user for fwupd-refresh.service/fwupd-refresh.timer +adduser --quiet --system --group --no-create-home --home /run/systemd \ + --gecos "fwupd-refresh user" fwupd-refresh +if [ -d /run/systemd/system ]; then + deb-systemd-invoke reload dbus || true +fi diff --git a/fwupd-1.8.6/contrib/debian/fwupd.postrm b/fwupd-1.8.6/contrib/debian/fwupd.postrm new file mode 100644 index 0000000000000000000000000000000000000000..2610c625f1d22022130983d1b7e1bf1f662a45ee --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.postrm @@ -0,0 +1,27 @@ +#!/bin/sh +set -e + +#DEBHELPER# + +if [ "$1" = purge ]; then + rm -rf /var/lib/fwupd/gnupg + rm -f /var/cache/app-info/xmls/fwupd.xml +fi + +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~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/dbus-1/system.d/org.freedesktop.fwupd.conf 1.3.2~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/fwupd/ata.conf 1.5.5~ -- "$@" +fi + +#Perform transition from /etc/fwupd/uefi.conf to /etc/fwupd/uefi_capsule.conf +if dpkg-maintscript-helper supports mv_conffile 2>/dev/null; then + ORIGINAL=/etc/fwupd/uefi.conf + NEW=/etc/fwupd/uefi_capsule.conf + dpkg-maintscript-helper mv_conffile $ORIGINAL $NEW 1.5.5~ -- "$@" +fi diff --git a/fwupd-1.8.6/contrib/debian/fwupd.preinst b/fwupd-1.8.6/contrib/debian/fwupd.preinst new file mode 100644 index 0000000000000000000000000000000000000000..51ee2e2bf7552aa6710b37276615017321beefae --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/fwupd.preinst @@ -0,0 +1,29 @@ +#!/bin/sh +set -e + +#DEBHELPER# + +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~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/dbus-1/system.d/org.freedesktop.fwupd.conf 1.3.2~ -- "$@" + dpkg-maintscript-helper rm_conffile \ + /etc/fwupd/ata.conf 1.5.5~ -- "$@" +fi + +#Perform transition from /etc/fwupd/uefi.conf to /etc/fwupd/uefi_capsule.conf +if dpkg-maintscript-helper supports mv_conffile 2>/dev/null; then + ORIGINAL=/etc/fwupd/uefi.conf + NEW=/etc/fwupd/uefi_capsule.conf + dpkg-maintscript-helper mv_conffile $ORIGINAL $NEW 1.5.5~ -- "$@" +fi + +# 1.3.2 had fwupd-refresh.service and fwupd.service both claiming +# this directory, but fwupd-refresh.service used DynamicUser directive +# meaning no other unit could access it. +if [ -L /var/cache/fwupd ]; then + rm -f /var/cache/fwupd +fi diff --git a/fwupd-1.8.6/contrib/debian/gbp.conf b/fwupd-1.8.6/contrib/debian/gbp.conf new file mode 100644 index 0000000000000000000000000000000000000000..980ac86f60b002a2478a5c68571d00065011d09e --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/gbp.conf @@ -0,0 +1,7 @@ +[DEFAULT] +debian-branch = debian +upstream-tag = %(version)s + +[buildpackage] +sign-tags = True +dist = experimental diff --git a/fwupd-1.8.6/contrib/debian/gir1.2-fwupd-2.0.install b/fwupd-1.8.6/contrib/debian/gir1.2-fwupd-2.0.install new file mode 100644 index 0000000000000000000000000000000000000000..341e8490034cd497cdc82b3572e26fc534ff6464 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/gir1.2-fwupd-2.0.install @@ -0,0 +1 @@ +usr/lib/*/girepository-1.0/Fwupd-*.typelib diff --git a/fwupd-1.8.6/contrib/debian/libfwupd-dev.install b/fwupd-1.8.6/contrib/debian/libfwupd-dev.install new file mode 100644 index 0000000000000000000000000000000000000000..97661f753782ab8868b813478e0c797eca60e5d8 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/libfwupd-dev.install @@ -0,0 +1,7 @@ +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/share/vala/vapi/fwupd.deps +usr/share/vala/vapi/fwupd.vapi diff --git a/fwupd-1.8.6/contrib/debian/libfwupd2.install b/fwupd-1.8.6/contrib/debian/libfwupd2.install new file mode 100644 index 0000000000000000000000000000000000000000..927e37b5070291994c3f6833422c1a49f364b5ce --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/libfwupd2.install @@ -0,0 +1 @@ +usr/lib/*/libfwupd.so.* diff --git a/fwupd-1.8.6/contrib/debian/lintian/fwupd b/fwupd-1.8.6/contrib/debian/lintian/fwupd new file mode 100644 index 0000000000000000000000000000000000000000..7d69af79fe13248fa40d0981c3e3ae4c03d64b9f --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/lintian/fwupd @@ -0,0 +1,2 @@ +#see Debian bug 896012 +fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-*/* diff --git a/fwupd-1.8.6/contrib/debian/lintian/fwupd-tests b/fwupd-1.8.6/contrib/debian/lintian/fwupd-tests new file mode 100644 index 0000000000000000000000000000000000000000..43863ac40b7fa8c02771edae3d6f122dc51e28ce --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/lintian/fwupd-tests @@ -0,0 +1,2 @@ +#see Debian bug 896012 +fwupd-tests: library-not-linked-against-libc usr/lib/*/fwupd-plugins-*/* diff --git a/fwupd-1.8.6/contrib/debian/not-installed b/fwupd-1.8.6/contrib/debian/not-installed new file mode 100644 index 0000000000000000000000000000000000000000..0a4eedd8636ddaf7fc0d3caf0515dd1ef5056c4d --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/not-installed @@ -0,0 +1,4 @@ +usr/libexec/qubes-fwupd/fwupd_usbvm_validate.py +usr/sbin/qubes-fwupdmgr +usr/share/qubes-fwupd/src/* +usr/share/qubes-fwupd/test/* diff --git a/fwupd-1.8.6/contrib/debian/rules b/fwupd-1.8.6/contrib/debian/rules new file mode 100755 index 0000000000000000000000000000000000000000..2b3b6b48a523d30edcdd8ebc22b4489b27e30fc4 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/rules @@ -0,0 +1,94 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +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) + export DEB_CFLAGS_MAINT_APPEND = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +endif + +CONFARGS = + +ifneq ($(CI),) + CONFARGS += --werror +endif + +ifneq ($(DEB_HOST_ARCH_CPU),ia64) + CONFARGS += -Dplugin_flashrom=enabled +else + CONFARGS += -Dplugin_flashrom=disabled +endif + +ifeq (yes,$(shell pkg-config --exists libsmbios_c && echo yes)) + CONFARGS += -Dplugin_dell=enabled +else + CONFARGS += -Dplugin_dell=disabled +endif + +ifeq (yes,$(shell pkg-config --exists efivar && echo yes)) + CONFARGS += -Dplugin_uefi_capsule=enabled -Defi_binary=false +else + CONFARGS += -Dplugin_uefi_capsule=disabled +endif + +ifneq ($(filter $(DEB_HOST_ARCH_CPU),i386 amd64),) + CONFARGS += -Dplugin_msr=enabled +else + CONFARGS += -Dplugin_msr=disabled +endif + +ifneq ($(QUBES_OPTION),) + CONFARGS += -Dqubes=true +endif + +ifneq ($(filter nodoc,$(DEB_BUILD_PROFILES)),) + CONFARGS += -Ddocs=disabled +endif + +CONFARGS += -Dplugin_dummy=true -Dplugin_powerd=disabled -Dsupported_build=enabled -Dplugin_modem_manager=enabled -Dsystemd_unit_user=fwupd-refresh + +%: + dh $@ --with gir + +override_dh_auto_clean: + rm -fr obj-* + rm -fr debian/build + +override_dh_auto_configure: + dh_auto_configure -- $(CONFARGS) + +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 + #install MSR conf if needed (depending on distro) + [ ! -d debian/tmp/usr/lib/modules-load.d ] || dh_install -pfwupd usr/lib/modules-load.d + [ ! -d debian/tmp/lib/modules-load.d ] || dh_install -pfwupd lib/modules-load.d + [ ! -d debian/tmp/usr/share/fwupd/quirks.d ] || dh_install -pfwupd usr/share/fwupd/quirks.d + + #install docs (maybe) + [ ! -d debian/tmp/usr/share/doc ] || dh_install -pfwupd-doc usr/share/doc + + dh_missing -a --fail-missing + + #this is placed in fwupd-tests + rm -f debian/fwupd/usr/lib/*/fwupd-plugins-*/libfu_plugin_test.so + rm -f debian/fwupd/usr/lib/*/fwupd-plugins-*/libfu_plugin_test_ble.so + rm -f debian/fwupd/etc/fwupd/remotes.d/fwupd-tests.conf + + #enable fwupd-refresh.service by default (we have a dedicated user) + rm -f debian/fwupd/lib/systemd/system-preset/fwupd-refresh.preset + +override_dh_strip_nondeterminism: + dh_strip_nondeterminism -Xfirmware-example.xml.gz + +ifneq (yes,$(shell command -v valgrind >/dev/null 2>&1 && echo yes)) +override_dh_auto_test: + : +endif + +override_dh_builddeb: + dh_builddeb diff --git a/fwupd-1.8.6/contrib/debian/source/format b/fwupd-1.8.6/contrib/debian/source/format new file mode 100644 index 0000000000000000000000000000000000000000..163aaf8d82b6c54f23c45f32895dbdfdcc27b047 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/fwupd-1.8.6/contrib/debian/source/lintian-overrides b/fwupd-1.8.6/contrib/debian/source/lintian-overrides new file mode 100644 index 0000000000000000000000000000000000000000..1e5cac9aae79b01aefeef5010d96999ef499b3cc --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/source/lintian-overrides @@ -0,0 +1,2 @@ +#github doesn't have these +fwupd source: debian-watch-does-not-check-gpg-signature diff --git a/fwupd-1.8.6/contrib/debian/source/options b/fwupd-1.8.6/contrib/debian/source/options new file mode 100644 index 0000000000000000000000000000000000000000..9e1e2a2eef034217472ee407222de5df5da7b6c0 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/source/options @@ -0,0 +1 @@ +extend-diff-ignore=".vscode|venv|subprojects|build" diff --git a/fwupd-1.8.6/contrib/debian/tests/ci b/fwupd-1.8.6/contrib/debian/tests/ci new file mode 100755 index 0000000000000000000000000000000000000000..2ecab73f25142830eec5ab86fac6379f64a02b18 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/tests/ci @@ -0,0 +1,11 @@ +#!/bin/sh +set -e +# try loading the mtdram module to run our mtd tests +modprobe mtdram 2>&1 || true +sed "s,^DisabledPlugins=.*,DisabledPlugins=," -i /etc/fwupd/daemon.conf +sed "s,^VerboseDomains=.*,VerboseDomains=*," -i /etc/fwupd/daemon.conf +sed "s,ConditionVirtualization=.*,," \ + /lib/systemd/system/fwupd.service > \ + /etc/systemd/system/fwupd.service +systemctl daemon-reload +gnome-desktop-testing-runner fwupd diff --git a/fwupd-1.8.6/contrib/debian/tests/control b/fwupd-1.8.6/contrib/debian/tests/control new file mode 100644 index 0000000000000000000000000000000000000000..456bc583a1c5631815f1b76661298eeb35b60ebf --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/tests/control @@ -0,0 +1,6 @@ +Tests: ci +Restrictions: needs-root + +Tests: libfwupd-dev +Depends: build-essential, libfwupd-dev, pkg-config +Restrictions: allow-stderr, superficial diff --git a/fwupd-1.8.6/contrib/debian/tests/libfwupd-dev b/fwupd-1.8.6/contrib/debian/tests/libfwupd-dev new file mode 100755 index 0000000000000000000000000000000000000000..92a95c87524c17ec24c1954fbf82992e82bf4897 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/tests/libfwupd-dev @@ -0,0 +1,38 @@ +#!/bin/sh +# Copyright 2020 Collabora Ltd. +# Copyright 2021 Simon McVittie +# SPDX-License-Identifier: LGPL-2.1-or-later + +set -eux + +WORKDIR="$(mktemp -d)" +trap 'cd /; rm -fr "$WORKDIR"' 0 INT QUIT ABRT PIPE TERM + +if [ -n "${DEB_HOST_GNU_TYPE:-}" ]; then + CROSS_COMPILE="$DEB_HOST_GNU_TYPE-" +else + CROSS_COMPILE= +fi + +CC="${CROSS_COMPILE}gcc" +PKG_CONFIG="${CROSS_COMPILE}pkg-config" + +cd "$WORKDIR" + +cat > trivial.c <<'EOF' +#undef NDEBUG +#include + +#include + +int main (void) +{ + assert (fwupd_error_to_string (FWUPD_ERROR_NOTHING_TO_DO) != NULL); + return 0; +} +EOF + +# Deliberately word-splitting pkg-config's output: +# shellcheck disable=SC2046 +"${CC}" -otrivial trivial.c $("${PKG_CONFIG}" --cflags --libs fwupd) +./trivial diff --git a/fwupd-1.8.6/contrib/debian/watch b/fwupd-1.8.6/contrib/debian/watch new file mode 100644 index 0000000000000000000000000000000000000000..4350c7f6314260ae8ebed174b65b4afa385a0856 --- /dev/null +++ b/fwupd-1.8.6/contrib/debian/watch @@ -0,0 +1,6 @@ +# You can run the "uscan" command to check for upstream updates and more. +# See uscan(1) for format + +version=3 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/fwupd-$1\.tar\.gz/ \ +https://github.com/fwupd/fwupd/tags .*/v?(\d\S*)\.tar\.gz diff --git a/fwupd-1.8.6/contrib/firmware_packager/README.md b/fwupd-1.8.6/contrib/firmware_packager/README.md new file mode 100644 index 0000000000000000000000000000000000000000..21015234c606d6e6e65bce708f672c2ba3690153 --- /dev/null +++ b/fwupd-1.8.6/contrib/firmware_packager/README.md @@ -0,0 +1,87 @@ +# Firmware Packager + +This script is intended to make firmware updating easier until OEMs upload their firmware packages to the LVFS. It works by extracting the firmware binary contained in a Microsoft .exe file (intended for performing the firmware update from a Windows system) and repackaging it in a cab file usable by fwupd. The cab file can then be install using `fwupdmgr install` + +## Prerequisites + +To run this script you will need + +1. Python3.5, a standard install should include all packages you need +2. 7z (for extracting .exe files) +3. gcab (for creating the cab file) + +## Usage + +To create a firmware package, you must supply, at a minimum: + +1. A string ID to name the firmware (`--firmware-id`). You are free to choose this, but [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) +2. A short name for the firmware package, again you are free to choose this (`--firmware-name`). +3. The unique ID of the device that the firmware is intended for (`--device-unique-id`). This *must* match the unique ID from `fwupdmgr get-devices` +4. The firmware version (`--release-version`), try to match the manufacturers versioning scheme +5. The path to the executable file to repackage (`--exe`) +6. The path *relative to the root of the exe archive* of the .bin file to package (`--bin`). Use 7z or archive-manager to inspect the .exe file and find this path. +For example, if I want to package `dell-thunderbolt-firmware.exe` and I open the .exe with archive-manager and find that `Intel/tbt.bin` is the path to the +bin file inside the archive, I would pass `--exe dell-thunderbolt-firmware.exe --bin Intel/tbt.bin` +7. The path to the cab file to output (`--out`). + +## Documentation + +`--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-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. ) + +`-contact-info` Email address of the firmware developer (e.g. someone@something.net) + +`--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 didn't try it. + +`--exe` Executable file to extract firmware from (e.g. `dell-thunderbolt-firmware.exe`) **REQUIRED** + +`--bin` Path to the .bin file inside the executable to use as the firmware image', relative to the root of the archive (e.g. `Intel/tbt.bin`) **REQUIRED** + +`--out` Output cab file path (e.g. `updates/firmware.cab`) **REQUIRED** + +## Example + +Let's say we downloaded `Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe` (available [here](https://downloads.dell.com/FOLDER04421073M/1/Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe)) containing updated firmware for Dell laptops thunderbolt controllers. Since Dell hasn't made this available on the LVFS yet, we want to package and install it ourselves. + +Opening the .exe with archive manager, we see it has a single folder: `Intel` and inside that, a set of firmware binaries (along with some microsoft junk). We pick the file `0x07BE_secure.bin` since we have a Dell XPS 9560 and that is its device string. + +Next we use `fwupdmgr` to get the device ID for the thunderbolt controller: + +```shell +$ fwupdmgr get-devices +Thunderbolt Controller + Guid: 72533768-6a6c-5c06-994a-367374336810 + DeviceID: 08001575 + Plugin: thunderbolt + Flags: internal|allow-online + DeviceVendor: Intel + Version: 21.00 + Created: 2017-08-16 +``` + +The GUID field contains what we are looking for + +We can then run the firmware-packager with the following arguments: + +```shell +$ firmware-packager --firmware-id net.queuecumber.DellTBT.firmware --firmware-name DellTBT --device-unique-id 72533768-6a6c-5c06-994a-367374336810 --release-version 4.21.01.002 --exe ~/Downloads/Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe --bin Intel/0x07BE_secure.bin --out firmware.cab +Using temp directory /tmp/tmpoey6_zx_ +Extracting firmware exe +Locating firmware bin +Creating metainfo +Cabbing firmware files +Done +``` + +And we should have a firmware.cab that contains the packaged firmware. +We can then install this firmware with `fwupdmgr install firmware.cab`. diff --git a/fwupd-1.8.6/contrib/firmware_packager/__init__.py b/fwupd-1.8.6/contrib/firmware_packager/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/contrib/firmware_packager/add_capsule_header.py b/fwupd-1.8.6/contrib/firmware_packager/add_capsule_header.py new file mode 100755 index 0000000000000000000000000000000000000000..92e26c9dccdd6037d0fabd50babf8506ecf43ac2 --- /dev/null +++ b/fwupd-1.8.6/contrib/firmware_packager/add_capsule_header.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2019 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import sys +import uuid +import argparse +import ctypes + +CAPSULE_FLAGS_PERSIST_ACROSS_RESET = 0x00010000 +CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE = 0x00020000 +CAPSULE_FLAGS_INITIATE_RESET = 0x00040000 + + +def add_header(infile, outfile, gd, fl=None): + # parse GUID from command line + try: + guid = uuid.UUID(gd) + except ValueError as e: + print(e) + return 1 + import struct + + try: + with open(infile, "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 + | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE + ) + if fl: + flags = int(fl, 16) + + # build update capsule header + hdrsz = 4096 + imgsz = hdrsz + len(bin_data) + hdr = ctypes.create_string_buffer(hdrsz) + struct.pack_into("<16sIII", hdr, 0, guid.bytes_le, hdrsz, flags, imgsz) + with open(outfile, "wb") as f: + f.write(hdr) + f.write(bin_data) + print("Wrote capsule %s" % outfile) + print("GUID: %s" % guid) + print("HdrSz: 0x%04x" % hdrsz) + print("Flags: 0x%04x" % flags) + print("PayloadSz: 0x%04x" % imgsz) + return 0 + + +if __name__ == "__main__": + 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(add_header(args.bin, args.cap, args.guid, args.flags)) diff --git a/fwupd-1.8.6/contrib/firmware_packager/add_dfu_header.py b/fwupd-1.8.6/contrib/firmware_packager/add_dfu_header.py new file mode 100755 index 0000000000000000000000000000000000000000..749c99e75e427a9c06c4cdc185fb8c1523391d28 --- /dev/null +++ b/fwupd-1.8.6/contrib/firmware_packager/add_dfu_header.py @@ -0,0 +1,55 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 2020 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import struct +import zlib +import argparse + + +def main(bin_fn, dfu_fn, pad, vid, pid, rev): + + # read binary file + with open(bin_fn, "rb") as f: + blob = f.read() + + # pad blob to a specific size + if pad: + while len(blob) < int(pad, 16): + blob += b"\0" + + # create DFU footer with checksum + blob += struct.pack( + " + + org.{developer_name}.guid{firmware_id} + {firmware_name} + {firmware_summary} + + {firmware_description} + + + {device_guid} + + {firmware_homepage} + CC0-1.0 + proprietary + {contact_info} + {developer_name} + + + + {release_description} + + + + + {version_format} + {update_protocol} + + +""" + + +def make_firmware_metainfo(firmware_info, dst): + local_info = vars(firmware_info) + local_info["firmware_id"] = local_info["device_guid"][0:8] + firmware_metainfo = firmware_metainfo_template.format( + **local_info, timestamp=time.time() + ) + + with open(os.path.join(dst, "firmware.metainfo.xml"), "w") as f: + f.write(firmware_metainfo) + + +def extract_exe(exe, dst): + command = ["7z", "x", "-o{}".format(dst), exe] + subprocess.check_call(command, stdout=subprocess.DEVNULL) + + +def get_firmware_bin(root, bin_path, dst): + with cd(root): + shutil.copy(bin_path, os.path.join(dst, "firmware.bin")) + + +def create_firmware_cab(exe, folder): + with cd(folder): + 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) + + +def main(args): + with tempfile.TemporaryDirectory() as d: + print("Using temp directory {}".format(d)) + + if args.exe: + print("Extracting firmware exe") + extract_exe(args.exe, d) + + print("Locating firmware bin") + get_firmware_bin(d, args.bin, d) + + print("Creating metainfo") + make_firmware_metainfo(args, d) + + print("Creating cabinet file") + create_firmware_cab(args, d) + + print("Done") + shutil.copy(os.path.join(d, "firmware.cab"), args.out) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Create fwupd packaged from windows executables" + ) + 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-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", required=True + ) + parser.add_argument( + "--release-version", + help="Version number of the firmware package", + required=True, + ) + parser.add_argument( + "--version-format", + help="Version format, e.g. quad or triplet", + required=True, + ) + parser.add_argument( + "--update-protocol", + help="Update protocol, e.g. org.uefi.capsule", + required=True, + ) + parser.add_argument( + "--release-description", help="Description of the firmware release" + ) + 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() + + main(args) diff --git a/fwupd-1.8.6/contrib/firmware_packager/install_dell_bios_exe.py b/fwupd-1.8.6/contrib/firmware_packager/install_dell_bios_exe.py new file mode 100755 index 0000000000000000000000000000000000000000..209c99b0c6fb26ff82ee15c7c5782800ba614c1a --- /dev/null +++ b/fwupd-1.8.6/contrib/firmware_packager/install_dell_bios_exe.py @@ -0,0 +1,128 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2019 Mario Limonciello +# +# SPDX-License-Identifier: LGPL-2.1+ + +import dbus +import os.path +import sys +import tempfile +import gi + +try: + gi.require_version("Fwupd", "2.0") +except ValueError: + print("Missing gobject-introspection packages. Try to install gir1.2-fwupd-2.0.") + sys.exit(1) +from gi.repository import Fwupd # pylint: disable=wrong-import-position +from simple_client import get_daemon_property, install, check_exists, modify_config +from add_capsule_header import add_header +from firmware_packager import make_firmware_metainfo, create_firmware_cab + + +class Variables: + def __init__(self, device_guid, version): + self.device_guid = device_guid + self.developer_name = "Dell Inc" + self.firmware_name = "New firmware" + self.firmware_summary = "Unknown" + self.firmware_description = "Unknown" + self.firmware_homepage = "https://support.dell.com" + self.contact_info = "Unknown" + self.release_version = version + self.release_description = "Unknown" + self.update_protocol = "org.uefi.capsule" + self.version_format = "dell-bios" + + +def parse_args(): + """Parse arguments for this client""" + import argparse + + parser = argparse.ArgumentParser(description="Interact with fwupd daemon") + parser.add_argument("exe", nargs="?", help="exe file") + parser.add_argument("deviceid", nargs="?", help="DeviceID to operate on(optional)") + args = parser.parse_args() + return args + + +def generate_cab(infile, directory, guid, version): + output = os.path.join(directory, "firmware.bin") + ret = add_header(infile, output, guid) + if ret: + sys.exit(ret) + variables = Variables(guid, version) + make_firmware_metainfo(variables, directory) + create_firmware_cab(variables, directory) + cab = os.path.join(directory, "firmware.cab") + print("Generated CAB file %s" % cab) + return cab + + +def find_uefi_device(client, deviceid): + devices = client.get_devices() + for item in devices: + # match the device we were given + if deviceid: + if item.get_id() != deviceid: + continue + # internal + if not item.has_flag(1 << 0): + continue + # needs reboot + if not item.has_flag(1 << 8): + continue + # return the first hit for UEFI plugin + if item.get_plugin() == "uefi" or item.get_plugin() == "uefi_capsule": + print("Installing to %s" % item.get_name()) + return item.get_guid_default(), item.get_id(), item.get_version() + print("Couldn't find any UEFI devices") + sys.exit(1) + + +def set_conf_only_trusted(client, setval): + prop = "OnlyTrusted" + current_val = get_daemon_property(prop) + if current_val: + pass + elif setval: + pass + else: + return False + modify_config(client, prop, str(setval).lower()) + return get_daemon_property(prop) == setval + + +def prompt_reboot(): + print("An update requires a reboot to complete") + while True: + res = input("Restart now? (Y/N) ") + if res.lower() == "n": + print("Reboot your machine manually to finish the update.") + break + if res.lower() != "y": + continue + # reboot using logind + obj = dbus.SystemBus().get_object( + "org.freedesktop.login1", "/org/freedesktop/login1" + ) + obj.Reboot(True, dbus_interface="org.freedesktop.login1.Manager") + + +if __name__ == "__main__": + ARGS = parse_args() + CLIENT = Fwupd.Client() + check_exists(ARGS.exe) + try: + is_restore_required = set_conf_only_trusted(CLIENT, False) + directory = tempfile.mkdtemp() + guid, deviceid, version = find_uefi_device(CLIENT, ARGS.deviceid) + cab = generate_cab(ARGS.exe, directory, guid, version) + install(CLIENT, cab, deviceid, True, True) + except Exception as e: + print(e) + + if is_restore_required: + set_conf_only_trusted(CLIENT, True) + prompt_reboot() diff --git a/fwupd-1.8.6/contrib/firmware_packager/meson.build b/fwupd-1.8.6/contrib/firmware_packager/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..8b8709a3c1e726e91074ee8454612b582b481dc0 --- /dev/null +++ b/fwupd-1.8.6/contrib/firmware_packager/meson.build @@ -0,0 +1,17 @@ +if get_option('firmware-packager') + install_data('firmware_packager.py', + install_dir: 'share/fwupd') + install_data('add_capsule_header.py', + install_dir: 'share/fwupd') + install_data('install_dell_bios_exe.py', + install_dir: 'share/fwupd') + con2 = configuration_data() + con2.set('FWUPD_VERSION', fwupd_version) + configure_file( + input: 'simple_client.py', + output: 'simple_client.py', + configuration: con2, + install: true, + install_dir: 'share/fwupd', + ) +endif diff --git a/fwupd-1.8.6/contrib/firmware_packager/simple_client.py b/fwupd-1.8.6/contrib/firmware_packager/simple_client.py new file mode 100755 index 0000000000000000000000000000000000000000..9f1b860f4b5616f11a077309d08749c3893175cd --- /dev/null +++ b/fwupd-1.8.6/contrib/firmware_packager/simple_client.py @@ -0,0 +1,207 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ +"""A simple fwupd frontend""" +import sys +import os +import dbus +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) + self.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", + "refresh", + "get-bios-setting", + ], + help="What to do", + ) + parser.add_argument("cab", nargs="?", help="CAB file") + parser.add_argument("deviceid", nargs="?", help="DeviceID to operate on(optional)") + parser.add_argument("--setting", help="BIOS setting to operate on(optional)") + args = parser.parse_args() + return args + + +def refresh(client): + """Uses fwupd client to refresh metadata""" + remotes = client.get_remotes() + client.set_user_agent_for_package("simple_client", "@FWUPD_VERSION@") + for remote in remotes: + if not remote.get_enabled(): + continue + if remote.get_kind() != Fwupd.RemoteKind.DOWNLOAD: + continue + client.refresh_remote(remote) + + +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 get_bios_settings(client, setting): + """Use fwupd client to get BIOS settings""" + settings = client.get_bios_settings() + for i in settings: + if not setting or setting == i.get_name() or setting == i.get_id(): + print(i.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 modify_config(client, key, value): + """Use fwupd client to modify daemon configuration value""" + try: + print("setting configuration key %s to %s" % (key, value)) + client.modify_config(key, value, None) + except Exception as e: + print("%s" % str(e)) + sys.exit(1) + + +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) + print("\n") + + +def get_daemon_property(key: str): + try: + bus = dbus.SystemBus() + proxy = bus.get_object(bus_name="org.freedesktop.fwupd", object_path="/") + iface = dbus.Interface(proxy, "org.freedesktop.DBus.Properties") + val = iface.Get("org.freedesktop.fwupd", key) + if isinstance(val, dbus.Boolean): + print( + "org.freedesktop.fwupd property %s, current value is %s" + % (key, bool(val)) + ) + else: + print("org.freedesktop.fwupd property %s, current value is %s" % (key, val)) + return val + except dbus.DBusException as e: + print(e) + return None + + +def check_exists(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() + + if ARGS.command == "get-devices": + get_devices(CLIENT) + elif ARGS.command == "get-details": + check_exists(ARGS.cab) + get_details(CLIENT, ARGS.cab) + elif ARGS.command == "refresh": + refresh(CLIENT) + elif ARGS.command == "install": + check_exists(ARGS.cab) + install(CLIENT, ARGS.cab, ARGS.deviceid, ARGS.allow_older, ARGS.allow_reinstall) + elif ARGS.command == "get-bios-setting": + get_bios_settings(CLIENT, ARGS.setting) diff --git a/fwupd-1.8.6/contrib/fix_translations.py b/fwupd-1.8.6/contrib/fix_translations.py new file mode 100755 index 0000000000000000000000000000000000000000..07a45e96711bedad60a749f34396b158db948cb3 --- /dev/null +++ b/fwupd-1.8.6/contrib/fix_translations.py @@ -0,0 +1,56 @@ +#!/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: + print("path required") + sys.exit(2) diff --git a/fwupd-1.8.6/contrib/flatpak/.gitmodules b/fwupd-1.8.6/contrib/flatpak/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..1e7a9909148311bdc9fe9b00fb1102909dd4fb42 --- /dev/null +++ b/fwupd-1.8.6/contrib/flatpak/.gitmodules @@ -0,0 +1,3 @@ +[submodule "shared-modules"] + path = shared-modules + url = https://github.com/flathub/shared-modules.git diff --git a/fwupd-1.8.6/contrib/flatpak/flathub.json b/fwupd-1.8.6/contrib/flatpak/flathub.json new file mode 100644 index 0000000000000000000000000000000000000000..96509113c6f42faab10c335bd10b94fe7540bbb3 --- /dev/null +++ b/fwupd-1.8.6/contrib/flatpak/flathub.json @@ -0,0 +1,3 @@ +{ + "skip-appstream-check": true +} diff --git a/fwupd-1.8.6/contrib/flatpak/org.freedesktop.fwupd.json b/fwupd-1.8.6/contrib/flatpak/org.freedesktop.fwupd.json new file mode 100644 index 0000000000000000000000000000000000000000..4f81f96ce43c453eae284bc030421fa8ffbbbcfb --- /dev/null +++ b/fwupd-1.8.6/contrib/flatpak/org.freedesktop.fwupd.json @@ -0,0 +1,220 @@ +{ + "app-id": "org.freedesktop.fwupd", + "runtime": "org.gnome.Platform", + "runtime-version": "3.30", + "branch": "stable", + "sdk": "org.gnome.Sdk", + "command": "/app/libexec/fwupd/fwupdtool", + "finish-args": [ + "--device=all", + "--filesystem=/boot", + "--filesystem=/sys", + "--filesystem=xdg-download", + "--share=network", + "--system-talk-name=org.freedesktop.fwupd", + "--system-talk-name=org.freedesktop.UPower" + ], + "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": [ + "shared-modules/udev/udev-175.json", + { + "name": "libusb", + "config-opts": [ + "--disable-static" + ], + "sources": [ + { + "type": "archive", + "url": "https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2", + "sha256": "75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157" + } + ] + }, + { + "name": "gusb", + "buildsystem": "meson", + "config-opts": [ + "-Ddocs=false", + "-Dvapi=false", + "-Dtests=false" + ], + "cleanup": [ + "/bin/gusbcmd" + ], + "sources": [ + { + "type": "archive", + "url": "https://people.freedesktop.org/~hughsient/releases/libgusb-0.3.0.tar.xz", + "sha256": "d8e7950f99b6ae4c3e9b8c65f3692b9635289e6cff8de40c4af41b2e9b348edc" + } + ] + }, + { + "name": "efivar", + "buildsystem": "simple", + "build-commands": [ + "make prefix=/app libdir=/app/lib", + "make install prefix=/app libdir=/app/lib" + ], + "cleanup": [ + "/bin/efivar" + ], + "sources": [ + { + "type": "archive", + "url": "https://github.com/rhboot/efivar/releases/download/35/efivar-35.tar.bz2", + "sha256": "1e033dc5d099a44fd473b0887dbcc4b105613efab0fb3c5df9f111ea5d147394" + } + ] + }, + { + "name": "libsmbios_c", + "only-arches": [ + "x86_64" + ], + "config-opts": [ + "--disable-doxygen", + "--disable-graphviz", + "--disable-python" + ], + "cleanup": [ + "/sbin/smbios*", + "/share/locale/*/LC_MESSAGES/libsmbios.mo" + ], + "sources": [ + { + "type": "archive", + "url": "https://github.com/dell/libsmbios/archive/v2.4.2.tar.gz", + "sha256": "ebfe18415e24bbec06d0a9ea1066c8dcd82982555373712713d7e194138650de" + } + ] + }, + { + "name": "gnu-efi", + "only-arches": [ + "aarch64", + "x86_64" + ], + "buildsystem": "simple", + "build-commands": [ + "make", + "make PREFIX=/app install" + ], + "no-autogen": true, + "cleanup": [ + "/bin/efivar" + ], + "sources": [ + { + "type": "archive", + "url": "http://superb-dca2.dl.sourceforge.net/project/gnu-efi/gnu-efi-3.0.9.tar.bz2", + "sha256": "6715ea7eae1c7e4fc5041034bd3f107ec2911962ed284a081e491646b12277f0" + } + ] + }, + { + "name": "python3-olefile", + "only-arches": [ + "aarch64", + "x86_64" + ], + "buildsystem": "simple", + "build-commands": [ + "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} olefile" + ], + "sources": [ + { + "type": "file", + "url": "https://pypi.python.org/packages/35/17/c15d41d5a8f8b98cc3df25eb00c5cee76193114c78e5674df6ef4ac92647/olefile-0.44.zip", + "sha256": "61f2ca0cd0aa77279eb943c07f607438edf374096b66332fae1ee64a6f0f73ad" + } + ] + }, + { + "name": "python3-pillow", + "only-arches": [ + "aarch64", + "x86_64" + ], + "buildsystem": "simple", + "build-commands": [ + "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} Pillow" + ], + "cleanup": [ + "/bin/*.py" + ], + "sources": [ + { + "type": "file", + "url": "https://pypi.python.org/packages/93/73/66854f63b1941aad9af18a1de59f9cf95ad1a87c801540222e332f6688d7/Pillow-4.1.1.tar.gz", + "sha256": "00b6a5f28d00f720235a937ebc2f50f4292a5c7e2d6ab9a8b26153b625c4f431" + } + ] + }, + { + "name": "fwupd", + "buildsystem": "meson", + "config-opts": [ + "-Dconsolekit=false", + "-Ddaemon=false", + "-Dgpg=false", + "-Dgtkdoc=false", + "-Dintrospection=false", + "-Dman=false", + "-Dpkcs7=false", + "-Dsystemd=false", + "-Dtests=false", + "-Defi-includedir=/app/include/efi", + "-Defi-ldsdir=/app/lib", + "-Defi-libdir=/app/lib", + "--sysconfdir=/app/etc", + "--localstatedir=/var/data" + ], + "build-options" : { + "arch": { + "i386": { + "config-opts": [ + "-Dplugin_dell=false", + "-Dplugin_uefi=false" + ] + }, + "arm": { + "config-opts": [ + "-Dplugin_dell=false", + "-Dplugin_uefi=false" + ] + }, + "aarch64": { + "config-opts": [ + "-Dplugin_dell=false" + ] + } + } + }, + "cleanup": [ + "/etc/dbus-1/system.d/org.freedesktop.fwupd.conf", + "/share/fwupd/remotes.d/vendor" + ], + "sources": [ + { + "type": "archive", + "url": "https://people.freedesktop.org/~hughsient/releases/fwupd-1.1.3.tar.xz", + "sha256": "474568a98af8ae82255c18da90ce2e4d290875da98638c4b1c44632c758314af" + } + ] + } + ] +} diff --git a/fwupd-1.8.6/contrib/freebsd/Makefile b/fwupd-1.8.6/contrib/freebsd/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..79a47eb606c2830960f058c7cfddea8a77824831 --- /dev/null +++ b/fwupd-1.8.6/contrib/freebsd/Makefile @@ -0,0 +1,53 @@ +# Created by: Norbert Kamiński +# $FreeBSD$ + +PORTNAME= fwupd +DISTVERSION= +GH_TAGNAME= +CATEGORIES= sysutils + +MAINTAINER= norbert.kaminski@3mdeb.com +COMMENT= Update firmware automatically, safely, and reliably + +LICENSE= LGPL21 + +BUILD_DEPENDS= gi-docgen:textproc/gi-docgen \ + help2man:misc/help2man \ + vala:lang/vala \ + ${LOCALBASE}/libexec/fwupd/efi/fwupdx64.efi:sysutils/fwupd-efi \ + ${PYTHON_PKGNAMEPREFIX}gobject3>0:devel/py-gobject3@${PY_FLAVOR} +LIB_DEPENDS= libcurl.so:ftp/curl \ + libefiboot.so:devel/libefiboot \ + libgcab-1.0.so:archivers/gcab \ + libgnutls.so:security/gnutls \ + libgpg-error.so:security/libgpg-error \ + libgpgme.so:security/gpgme \ + libgusb.so:devel/libgusb \ + libjcat.so:textproc/libjcat \ + libjson-glib-1.0.so:devel/json-glib \ + libprotobuf-c.so:devel/protobuf-c \ + libcbor.so:devel/libcbor \ + libxmlb.so:textproc/libxmlb \ + libefiboot.so:devel/gnu-efi + +RUN_DEPENDS= ${LOCALBASE}/libexec/fwupd/efi/fwupdx64.efi:sysutils/fwupd-efi + +USES= gnome libarchive meson pkgconfig python:3.8+ shebangfix sqlite +USE_GITHUB= yes +USE_GNOME= glib20 introspection:build +GH_ACCOUNT= +INSTALLS_ICONS= yes +USE_LDCONFIG= yes + +SHEBANG_GLOB= *.py + +MESON_ARGS= -Dgudev=disabled \ + -Dplugin_amt=false \ + -Dpolkit=disabled \ + -Dsystemd=disabled \ + -Doffline=false \ + -Dtests=false \ + -Ddocs=enabled \ + -Defi_binary=false + +.include diff --git a/fwupd-1.8.6/contrib/freebsd/pkg-descr b/fwupd-1.8.6/contrib/freebsd/pkg-descr new file mode 100644 index 0000000000000000000000000000000000000000..cb60b1cd5ea4f33f3f3f70c3b01ffb188692c4f2 --- /dev/null +++ b/fwupd-1.8.6/contrib/freebsd/pkg-descr @@ -0,0 +1,9 @@ +Make firmware updates automatic, safe, and reliable. + +fwupd is a system daemon to allow session software to update device firmware on +your local machine. It is designed for desktops, but also usable on phones and +headless servers. 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. + +WWW: https://fwupd.org/ diff --git a/fwupd-1.8.6/contrib/fwupd.spec.in b/fwupd-1.8.6/contrib/fwupd.spec.in new file mode 100644 index 0000000000000000000000000000000000000000..aa18b34b4b965efa149bfbf8093ab3406fac00a6 --- /dev/null +++ b/fwupd-1.8.6/contrib/fwupd.spec.in @@ -0,0 +1,497 @@ +%global glib2_version 2.45.8 +%global libxmlb_version 0.1.3 +%global libgusb_version 0.3.5 +%global libcurl_version 7.61.0 +%global libjcat_version 0.1.0 +%global systemd_version 231 +%global json_glib_version 1.1.1 + +# although we ship a few tiny python files these are utilities that 99.99% +# of users do not need -- use this to avoid dragging python onto CoreOS +%global __requires_exclude ^%{python3}$ + +%define alphatag #ALPHATAG# + +%global enable_ci 0 +%global enable_tests 1 +%global enable_dummy 1 +%global __meson_wrap_mode nodownload + +# fwupd.efi is only available on these arches +%ifarch x86_64 aarch64 +%global have_uefi 1 +%endif + +# gpio.h is only available on these arches +%ifarch x86_64 aarch64 +%global have_gpio 1 +%endif + +# flashrom is only available on these arches +%ifarch i686 x86_64 armv7hl aarch64 ppc64le +%global have_flashrom 1 +%endif + +%ifarch i686 x86_64 +%global have_msr 1 +%endif + +# libsmbios is only available on x86 +%ifarch x86_64 +%global have_dell 1 +%endif + +# Until we actually have seen it outside x86 +%ifarch i686 x86_64 +%global have_thunderbolt 1 +%endif + +# only available recently +%if 0%{?fedora} >= 30 +%global have_modem_manager 1 +%endif + +Summary: Firmware update daemon +Name: fwupd +Version: #VERSION# +Release: 0.#BUILD#%{?alphatag}%{?dist} +License: LGPLv2+ +URL: https://github.com/fwupd/fwupd +Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz + +BuildRequires: gettext +BuildRequires: glib2-devel >= %{glib2_version} +BuildRequires: libxmlb-devel >= %{libxmlb_version} +BuildRequires: libgcab1-devel +BuildRequires: libgudev1-devel +BuildRequires: libgusb-devel >= %{libgusb_version} +BuildRequires: libcurl-devel >= %{libcurl_version} +BuildRequires: libjcat-devel >= %{libjcat_version} +BuildRequires: polkit-devel >= 0.103 +BuildRequires: protobuf-c-devel +BuildRequires: python3-packaging +BuildRequires: sqlite-devel +BuildRequires: systemd >= %{systemd_version} +BuildRequires: systemd-devel +BuildRequires: libarchive-devel +BuildRequires: libcbor-devel +BuildRequires: gobject-introspection-devel +BuildRequires: gcab +%ifarch %{valgrind_arches} +BuildRequires: valgrind +BuildRequires: valgrind-devel +%endif +BuildRequires: gi-docgen +BuildRequires: gnutls-devel +BuildRequires: gnutls-utils +BuildRequires: meson +BuildRequires: json-glib-devel >= %{json_glib_version} +BuildRequires: vala +BuildRequires: bash-completion +BuildRequires: git-core +%if 0%{?have_flashrom} +BuildRequires: flashrom-devel >= 1.2-2 +%endif + +%if 0%{?have_modem_manager} +BuildRequires: ModemManager-glib-devel >= 1.10.0 +BuildRequires: libqmi-devel >= 1.22.0 +BuildRequires: libmbim-devel +%endif + +%if 0%{?have_uefi} +BuildRequires: efivar-devel >= 33 +BuildRequires: python3 python3-cairo python3-gobject +BuildRequires: pango-devel +BuildRequires: cairo-devel cairo-gobject-devel +BuildRequires: freetype +BuildRequires: fontconfig +BuildRequires: google-noto-sans-cjk-ttc-fonts +BuildRequires: tpm2-tss-devel >= 2.2.3 +%endif + +%if 0%{?have_dell} +BuildRequires: efivar-devel >= 33 +BuildRequires: libsmbios-devel >= 2.3.0 +%endif + +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd + +Requires: glib2%{?_isa} >= %{glib2_version} +Requires: libxmlb%{?_isa} >= %{libxmlb_version} +Requires: libgusb%{?_isa} >= %{libgusb_version} +Requires: shared-mime-info + +%if 0%{?rhel} > 7 || 0%{?fedora} > 28 +Recommends: python3 +%endif + +Obsoletes: fwupd-sign < 0.1.6 +Obsoletes: libebitdo < 0.7.5-3 +Obsoletes: libdfu < 1.0.0 +Obsoletes: fwupd-labels < 1.1.0-1 + +Obsoletes: dbxtool < 9 +Provides: dbxtool + +%if 0%{?rhel} > 7 +Obsoletes: fwupdate < 11-4 +Obsoletes: fwupdate-efi < 11-4 + +Provides: fwupdate +Provides: fwupdate-efi +%endif + +# optional, but a really good idea +Recommends: udisks2 +Recommends: bluez +Recommends: jq + +%if 0%{?have_modem_manager} +Recommends: %{name}-plugin-modem-manager +%endif +%if 0%{?have_flashrom} +Recommends: %{name}-plugin-flashrom +%endif +%if 0%{?have_uefi} +Recommends: %{name}-efi +Recommends: %{name}-plugin-uefi-capsule-data +%endif + +%description +fwupd is a daemon to allow session software to update device firmware. + +%package devel +Summary: Development package for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +Obsoletes: libebitdo-devel < 0.7.5-3 +Obsoletes: libdfu-devel < 1.0.0 + +%description devel +Files for development with %{name}. + +%package tests +Summary: Data files for installed tests +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description tests +Data files for installed tests. + +%if 0%{?have_modem_manager} +%package plugin-modem-manager +Summary: fwupd plugin using ModemManger +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description plugin-modem-manager +This provides the optional package which is only required on hardware that +might have mobile broadband hardware. It is probably not required on servers. +%endif + +%if 0%{?have_flashrom} +%package plugin-flashrom +Summary: fwupd plugin using flashrom +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description plugin-flashrom +This provides the optional package which is only required on hardware that +can be flashed using flashrom. It is probably not required on servers. +%endif + +%if 0%{?have_uefi} +%package plugin-uefi-capsule-data +Summary: Localized data for the UEFI UX capsule +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description plugin-uefi-capsule-data +This provides the pregenerated BMP artwork for the UX capsule, which allows the +"Installing firmware update…" localized text to be shown during a UEFI firmware +update operation. This subpackage is probably not required on embedded hardware +or server machines. +%endif + +%if 0%{?qubes_packages} +%package qubes-dom0 +Summary: fwupd wrapper for Qubes OS - dom0 scripts +Requires: gcab +Requires: fwupd >= 1.5.7 +Requires: libjcat >= 0.1.6 + +%description qubes-dom0 +fwupd wrapper for Qubes OS + +%package qubes-vm +Summary: fwupd wrapper for Qubes OS - VM scripts +Requires: gcab +Requires: fwupd >= 1.5.7 +Requires: libjcat >= 0.1.6 + +%description qubes-vm +fwupd wrapper for Qubes OS +%endif + +%prep +%autosetup -p1 + +%build + +%meson \ +%if 0%{?enable_ci} + --werror \ +%endif + -Ddocs=enabled \ +%if 0%{?enable_tests} + -Dtests=true \ +%else + -Dtests=false \ +%endif +%if 0%{?enable_dummy} + -Dplugin_dummy=true \ +%else + -Dplugin_dummy=false \ +%endif +%if 0%{?have_flashrom} + -Dplugin_flashrom=enabled \ +%else + -Dplugin_flashrom=disabled \ +%endif +%if 0%{?have_msr} + -Dplugin_msr=enabled \ +%else + -Dplugin_msr=disabled \ +%endif +%if 0%{?have_gpio} + -Dplugin_gpio=enabled \ +%else + -Dplugin_gpio=disabled \ +%endif +%if 0%{?have_uefi} + -Dplugin_uefi_capsule=enabled \ + -Dplugin_uefi_pk=enabled \ + -Dplugin_tpm=enabled \ + -Defi_binary=false \ +%else + -Dplugin_uefi_capsule=disabled \ + -Dplugin_uefi_pk=disabled \ + -Dplugin_tpm=disabled \ +%endif +%if 0%{?have_dell} + -Dplugin_dell=enabled \ +%else + -Dplugin_dell=disabled \ +%endif +%if 0%{?have_modem_manager} + -Dplugin_modem_manager=enabled \ +%else + -Dplugin_modem_manager=disabled \ +%endif +%if 0%{?qubes_packages} + -Dqubes=true \ +%endif + -Dman=true \ + -Dbluez=enabled \ + -Dplugin_powerd=disabled \ + -Dsupported_build=enabled + +%meson_build + +%if 0%{?enable_tests} +%if 0%{?enable_ci} + ./contrib/ci/get_test_firmware.sh +%endif +%check +%meson_test +%endif + +%install +%meson_install + +mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg + +# workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1757948 +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/cache/fwupd + +%find_lang %{name} + +%post +%systemd_post fwupd.service + +# change vendor-installed remotes to use the default keyring type +for fn in /etc/fwupd/remotes.d/*.conf; do + if grep -q "Keyring=gpg" "$fn"; then + sed -i 's/Keyring=gpg/#Keyring=pkcs/g' "$fn"; + fi +done + +%preun +%systemd_preun fwupd.service + +%postun +%systemd_postun_with_restart fwupd.service + +%files -f %{name}.lang +%doc README.md AUTHORS +%license COPYING +%config(noreplace)%{_sysconfdir}/fwupd/daemon.conf +%if 0%{?have_uefi} +%config(noreplace)%{_sysconfdir}/fwupd/uefi_capsule.conf +%endif +%config(noreplace)%{_sysconfdir}/fwupd/redfish.conf +%if 0%{?have_thunderbolt} +%config(noreplace)%{_sysconfdir}/fwupd/thunderbolt.conf +%endif +%dir %{_libexecdir}/fwupd +%{_libexecdir}/fwupd/fwupd +%ifarch i686 x86_64 +%{_libexecdir}/fwupd/fwupd-detect-cet +%endif +%{_libexecdir}/fwupd/fwupdoffline +%if 0%{?have_uefi} +%{_bindir}/fwupdate +%endif +%{_bindir}/dfu-tool +%if 0%{?have_uefi} +%{_bindir}/dbxtool +%endif +%{_bindir}/fwupdmgr +%{_bindir}/fwupdtool +%{_bindir}/fwupdagent +%dir %{_sysconfdir}/fwupd +%dir %{_sysconfdir}/fwupd/bios-settings.d +%config%(noreplace)%{_sysconfdir}/fwupd/bios-settings.d/README.md +%dir %{_sysconfdir}/fwupd/remotes.d +%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 +%if 0%{?have_msr} +/usr/lib/modules-load.d/fwupd-msr.conf +%config(noreplace)%{_sysconfdir}/fwupd/msr.conf +%endif +%{_datadir}/dbus-1/system.d/org.freedesktop.fwupd.conf +%{_datadir}/bash-completion/completions/fwupdmgr +%{_datadir}/bash-completion/completions/fwupdtool +%{_datadir}/bash-completion/completions/fwupdagent +%{_datadir}/fish/vendor_completions.d/fwupdmgr.fish +%{_datadir}/fwupd/metainfo/org.freedesktop.fwupd*.metainfo.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 +%{_datadir}/polkit-1/rules.d/org.freedesktop.fwupd.rules +%{_datadir}/dbus-1/system-services/org.freedesktop.fwupd.service +%{_mandir}/man1/fwupdtool.1* +%{_mandir}/man1/fwupdagent.1* +%{_mandir}/man1/dfu-tool.1* +%if 0%{?have_uefi} +%{_mandir}/man1/dbxtool.* +%endif +%{_mandir}/man1/fwupdmgr.1* +%if 0%{?have_uefi} +%{_mandir}/man1/fwupdate.1* +%endif +%{_datadir}/metainfo/org.freedesktop.fwupd.metainfo.xml +%{_datadir}/icons/hicolor/scalable/apps/org.freedesktop.fwupd.svg +%{_datadir}/fwupd/firmware_packager.py +%{_datadir}/fwupd/simple_client.py +%{_datadir}/fwupd/add_capsule_header.py +%{_datadir}/fwupd/install_dell_bios_exe.py +%{_unitdir}/fwupd-offline-update.service +%{_unitdir}/fwupd.service +%{_unitdir}/fwupd-refresh.service +%{_unitdir}/fwupd-refresh.timer +%{_presetdir}/fwupd-refresh.preset +%{_unitdir}/system-update.target.wants/ +%dir %{_localstatedir}/lib/fwupd +%dir %{_localstatedir}/cache/fwupd +%dir %{_datadir}/fwupd/quirks.d +%{_datadir}/fwupd/quirks.d/builtin.quirk.gz +%{_datadir}/doc/fwupd/*.html +%if 0%{?have_uefi} +%{_sysconfdir}/grub.d/35_fwupd +%endif +%{_libdir}/libfwupd.so.2* +%{_libdir}/girepository-1.0/Fwupd-2.0.typelib +/usr/lib/udev/rules.d/*.rules +/usr/lib/systemd/system-shutdown/fwupd.shutdown +%dir %{_libdir}/fwupd-%{version} +%{_libdir}/fwupd-%{version}/libfwupd*.so +%ghost %{_localstatedir}/lib/fwupd/gnupg + +%if 0%{?have_modem_manager} +%files plugin-modem-manager +%{_libdir}/fwupd-%{version}/libfu_plugin_modem_manager.so +%endif +%if 0%{?have_flashrom} +%files plugin-flashrom +%{_libdir}/fwupd-%{version}/libfu_plugin_flashrom.so +%endif +%if 0%{?have_uefi} +%files plugin-uefi-capsule-data +%{_datadir}/fwupd/uefi-capsule-ux.tar.xz +%endif + +%files devel +%{_datadir}/gir-1.0/Fwupd-2.0.gir +%{_datadir}/doc/fwupd/libfwupdplugin +%{_datadir}/doc/fwupd/libfwupd +%{_datadir}/doc/libfwupdplugin +%{_datadir}/doc/libfwupd +%{_datadir}/vala/vapi +%{_includedir}/fwupd-1 +%{_libdir}/libfwupd*.so +%{_libdir}/pkgconfig/fwupd.pc + +%files tests +%if 0%{?enable_tests} +%{_datadir}/fwupd/host-emulate.d/*.json.gz +%dir %{_datadir}/installed-tests/fwupd +%{_datadir}/installed-tests/fwupd/tests/* +%{_datadir}/installed-tests/fwupd/fwupd-tests.xml +%{_datadir}/installed-tests/fwupd/*.test +%{_datadir}/installed-tests/fwupd/*.cab +%{_datadir}/installed-tests/fwupd/*.sh +%if 0%{?have_uefi} +%{_datadir}/installed-tests/fwupd/efi +%endif +%{_datadir}/fwupd/device-tests/*.json +%{_libexecdir}/installed-tests/fwupd/* +%dir %{_sysconfdir}/fwupd/remotes.d +%config(noreplace)%{_sysconfdir}/fwupd/remotes.d/fwupd-tests.conf +%endif + +%if 0%{?qubes_packages} +%files qubes-vm +%{_libexecdir}/qubes-fwupd/fwupd_common_vm.py +%{_libexecdir}/qubes-fwupd/fwupd_download_updates.py +%{_libexecdir}/qubes-fwupd/fwupd_usbvm_validate.py + +%files qubes-dom0 +%{_datadir}/qubes-fwupd/src/fwupd_receive_updates.py +/usr/sbin/qubes-fwupdmgr +%{_datadir}/qubes-fwupd/src/qubes_fwupd_heads.py +%{_datadir}/qubes-fwupd/src/qubes_fwupd_update.py +%{_datadir}/qubes-fwupd/src/__init__.py +%{_datadir}/qubes-fwupd/test/fwupd_logs.py +%{_datadir}/qubes-fwupd/test/test_qubes_fwupdmgr.py +%{_datadir}/qubes-fwupd/test/test_qubes_fwupd_heads.py +%{_datadir}/qubes-fwupd/test/__init__.py +%{_datadir}/qubes-fwupd/test/logs/get_devices.log +%{_datadir}/qubes-fwupd/test/logs/get_updates.log +%{_datadir}/qubes-fwupd/test/logs/help.log +%{_datadir}/qubes-fwupd/test/logs/firmware.metainfo.xml +%{_datadir}/qubes-fwupd/test/logs/metainfo_name/firmware.metainfo.xml +%{_datadir}/qubes-fwupd/test/logs/metainfo_version/firmware.metainfo.xml +%endif + +%changelog +* #LONGDATE# Richard Hughes #VERSION#-0.#BUILD##ALPHATAG# +- Update from git diff --git a/fwupd-1.8.6/contrib/fwupd.wxs.in b/fwupd-1.8.6/contrib/fwupd.wxs.in new file mode 100644 index 0000000000000000000000000000000000000000..6414d9f51ad65a064e46fd2b35a95d8782999deb --- /dev/null +++ b/fwupd-1.8.6/contrib/fwupd.wxs.in @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + NOT NEWERVERSIONDETECTED + + + + + + + + + + + + + + + + + diff --git a/fwupd-1.8.6/contrib/generate-ds20.py b/fwupd-1.8.6/contrib/generate-ds20.py new file mode 100755 index 0000000000000000000000000000000000000000..d3bdc9d65249215aa22a0039900e9fadde0929d6 --- /dev/null +++ b/fwupd-1.8.6/contrib/generate-ds20.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# +# pylint: disable=consider-using-f-string + +import sys +import argparse +import configparser +import base64 +from typing import List + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + parser.add_argument( + "-b", + "--bufsz", + type=int, + help="Buffer size in bytes", + ) + parser.add_argument("-i", "--instance-id", type=str, help="Device instance ID") + parser.add_argument("filename", action="store", type=str, help="Quirk filename") + + args = parser.parse_args() + if not args.bufsz: + parser.print_help() + sys.exit(1) + + config = configparser.ConfigParser() + config.optionxform = str + try: + config.read(args.filename) + except configparser.MissingSectionHeaderError: + print("Not a quirk file") + sys.exit(1) + + # fall back to the default if there is only one device in the quirk file + if not args.instance_id: + sections = config.sections() + if len(sections) != 1: + print("Multiple devices found, use --instance-id to choose between:") + for section in sections: + print(" • {}".format(section)) + sys.exit(1) + args.instance_id = sections[0] + + # create the smallest kv store possible + lines: List[str] = [] + try: + for key in config[args.instance_id]: + if key in ["Inhibit", "Issue"]: + print("WARNING: skipping key {}".format(key)) + continue + value = config[args.instance_id][key] + lines.append("{}={}".format(key, value)) + except KeyError: + print("No {} section".format(args.instance_id)) + sys.exit(1) + + # pad to the buffer size + buf: bytes = "\n".join(lines).encode() + if len(buf) > args.bufsz: + print("Quirk data is larger than bufsz") + sys.exit(1) + buf = buf.ljust(args.bufsz, b"\0") + + # success + print("DS20 descriptor control transfer data:") + print(" ".join(["{:02x}".format(val) for val in list(buf)])) + print(base64.b64encode(buf).decode()) diff --git a/fwupd-1.8.6/contrib/generate-emulation.py b/fwupd-1.8.6/contrib/generate-emulation.py new file mode 100755 index 0000000000000000000000000000000000000000..5af3ec7671943fbb75a5572fdc81a092b1f405ed --- /dev/null +++ b/fwupd-1.8.6/contrib/generate-emulation.py @@ -0,0 +1,136 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# +# pylint: disable=invalid-name,missing-docstring,consider-using-f-string + +import json +import sys + +from typing import Dict, List, Any + +import gi +from gi.repository import GLib + +gi.require_version("Fwupd", "2.0") +gi.require_version("Json", "1.0") + +from gi.repository import Fwupd # pylint: disable=wrong-import-position +from gi.repository import Json # pylint: disable=wrong-import-position + + +def _minimize_json(json_str: str) -> str: + nodes = json.loads(json_str) + new_attrs: List[Dict[str, Any]] = [] + new_devices: List[Dict[str, Any]] = [] + new_bios_settings: List[Dict[str, Any]] = [] + try: + for attr in nodes["SecurityAttributes"]: + new_attr: Dict[str, Any] = {} + for key in attr: + if key in ["AppstreamId", "HsiResult", "Flags", "Plugin"]: + new_attr[key] = attr[key] + new_attrs.append(new_attr) + except KeyError: + pass + try: + for device in nodes["Devices"]: + new_device: Dict[str, Any] = {} + for key in device: + if key not in ["Created", "Modified", "Releases", "Plugin"]: + new_device[key] = device[key] + new_devices.append(new_device) + except KeyError: + pass + try: + for device in nodes["BiosSettings"]: + new_attr: Dict[str, Any] = {} + for key in device: + if key not in ["Filename"]: + new_attr[key] = device[key] + new_bios_settings.append(new_attr) + except KeyError: + pass + return json.dumps( + { + "SecurityAttributes": new_attrs, + "Devices": new_devices, + "BiosSettings": new_bios_settings, + }, + indent=2, + separators=(",", " : "), + ) + + +def _get_host_devices_and_attrs() -> str: + + # connect to the running daemon + client = Fwupd.Client() + builder = Json.Builder() + builder.begin_object() + + # add devices + try: + devices = client.get_devices() + except GLib.GError as e: + print("ignoring {}".format(e)) + else: + builder.set_member_name("Devices") + builder.begin_array() + for device in devices: + builder.begin_object() + device.to_json_full(builder, Fwupd.DEVICE_FLAG_TRUSTED) + builder.end_object() + builder.end_array() + + # add security attributes + try: + attrs = client.get_host_security_attrs() + except GLib.GError as e: + print("ignoring {}".format(e)) + else: + builder.set_member_name("SecurityAttributes") + builder.begin_array() + for attr in attrs: + builder.begin_object() + attr.to_json(builder) + builder.end_object() + builder.end_array() + + # add BIOS settings + try: + attrs = client.get_bios_settings() + except GLib.GError as e: + print("ignoring {}".format(e)) + else: + builder.set_member_name("BiosSettings") + builder.begin_array() + for attr in attrs: + builder.begin_object() + attr.to_json(builder) + builder.end_object() + builder.end_array() + + # export to JSON + builder.end_object() + generator = Json.Generator() + generator.set_pretty(True) + generator.set_root(builder.get_root()) + return generator.to_data()[0] + + +if len(sys.argv) < 2: + sys.stdout.write(_minimize_json(sys.stdin.read())) +else: + for fn in sys.argv[1:]: + + try: + with open(fn, "rb") as f_in: + json_in = f_in.read().decode() + except FileNotFoundError: + json_in = _get_host_devices_and_attrs() + json_out = _minimize_json(json_in).encode() + with open(fn, "wb") as f_out: + f_out.write(json_out) diff --git a/fwupd-1.8.6/contrib/generate-metainfo.py b/fwupd-1.8.6/contrib/generate-metainfo.py new file mode 100755 index 0000000000000000000000000000000000000000..44dc90fa0d83672b91742617d3de7cd067ebea73 --- /dev/null +++ b/fwupd-1.8.6/contrib/generate-metainfo.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import argparse +import xml.etree.ElementTree as ET + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + parser.add_argument( + "-r", + "--releases", + type=int, + default=5, + ) + parser.add_argument( + "filename_src", action="store", type=str, help="metainfo source" + ) + parser.add_argument( + "filename_dst", action="store", type=str, help="metainfo destination" + ) + args = parser.parse_args() + + tree = ET.parse(args.filename_src) + root = tree.getroot().findall("releases")[0] + for release in root.findall("release")[args.releases :]: + root.remove(release) + + with open(args.filename_dst, "wb") as f: + tree.write(f, encoding="UTF-8", xml_declaration=True) diff --git a/fwupd-1.8.6/contrib/generate-plugins-header.py b/fwupd-1.8.6/contrib/generate-plugins-header.py new file mode 100644 index 0000000000000000000000000000000000000000..54d549bd74faf92565ccaf0a7d965b7d131764a5 --- /dev/null +++ b/fwupd-1.8.6/contrib/generate-plugins-header.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import sys + +if len(sys.argv) < 3: + print("not enough arguments") + sys.exit(1) + +with open(sys.argv[1], "w") as f: + + # empty argument is no plugins + plugin_names = [] + if sys.argv[3]: + for fullpath in sys.argv[3].split(","): + parts = fullpath.split("/") + name = parts[-1] + if name.startswith("libfu_plugin_"): + name = name[13:] + if name.endswith(".a"): + name = name[:-2] + plugin_names.append((parts[-2], name)) + + # includes + for dirname, name in plugin_names: + f.write( + '#include "{srcdir}/plugins/{dirname}/fu-{name}-plugin.h"\n'.format( + srcdir=sys.argv[2], dirname=dirname, name=name.replace("_", "-") + ) + ) + + # GTypes + gtypes = ["fu_{}_plugin_get_type".format(name) for _, name in plugin_names] + f.write( + "GType (*fu_plugin_externals[])(void) = { %s };\n" + % ", ".join(gtypes + ["NULL"]) + ) + +sys.exit(0) diff --git a/fwupd-1.8.6/contrib/generate-version-script.py b/fwupd-1.8.6/contrib/generate-version-script.py new file mode 100755 index 0000000000000000000000000000000000000000..7b0ecdf3f53ab446014f281ab463f2c1b7121568 --- /dev/null +++ b/fwupd-1.8.6/contrib/generate-version-script.py @@ -0,0 +1,142 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2017 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import sys +import argparse +import xml.etree.ElementTree as ET + +XMLNS = "{http://www.gtk.org/introspection/core/1.0}" +XMLNS_C = "{http://www.gtk.org/introspection/c/1.0}" + + +def parse_version(ver): + return tuple(map(int, ver.split("."))) + + +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: %s \n" % sys.argv[0]) + sys.exit(return_code) + + +class LdVersionScript: + """Rasterize some text""" + + def __init__(self, library_name): + self.library_name = library_name + self.releases = {} + self.overrides = {} + + def _add_node(self, node): + identifier = node.attrib[XMLNS_C + "identifier"] + introspectable = int(node.get("introspectable", 1)) + version = node.get("version", None) + if introspectable and not version: + print("No version for", identifier) + sys.exit(1) + if not version: + return None + version = node.attrib["version"] + if version not in self.releases: + self.releases[version] = [] + release = self.releases[version] + if identifier not in release: + 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) + + # choose the lowest version method for the _get_type symbol + version_lowest = None + + # 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 parse_version(version_tmp) < parse_version( + version_lowest + ): + version_lowest = version_tmp + + # add the constructor + for node in cls.findall(XMLNS + "constructor"): + version_tmp = self._add_node(node) + if version_tmp: + if not version_lowest or parse_version(version_tmp) < parse_version( + version_lowest + ): + version_lowest = version_tmp + + 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"] + + # finally add the get_type symbol + version = self.overrides.get(type_name, version_lowest) + if version: + self.releases[version].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"): + self._add_cls(cls) + + def render(self): + + # get a sorted list of all the versions + versions = [] + for version in self.releases: + versions.append(version) + + # output the version data to a file + verout = "# generated automatically, do not edit!\n" + oldversion = None + 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" + for symbol in symbols: + verout += " %s;\n" % symbol + verout += " local: *;\n" + if oldversion: + verout += "} %s_%s;\n" % (self.library_name, oldversion) + else: + verout += "};\n" + oldversion = version + return verout + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + parser.add_argument( + "-r", "--override", action="append", nargs=2, metavar=("symbol", "version") + ) + args, argv = parser.parse_known_args() + if len(argv) != 3: + usage(1) + + ld = LdVersionScript(library_name=argv[0]) + if args.override: + for override_symbol, override_version in args.override: + ld.overrides[override_symbol] = override_version + ld.import_gir(argv[1]) + open(argv[2], "w").write(ld.render()) diff --git a/fwupd-1.8.6/contrib/meson.build b/fwupd-1.8.6/contrib/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..e4ca0958e8a149e918862f0266e2fa5d07b6ac39 --- /dev/null +++ b/fwupd-1.8.6/contrib/meson.build @@ -0,0 +1,21 @@ +subdir('firmware_packager') +if get_option('qubes') + subdir('qubes') +endif + +con2 = configuration_data() +con2.set('FWUPD_VERSION', fwupd_version) + +configure_file( + input: 'fwupd.spec.in', + output: 'fwupd.spec.in', + configuration: con2, +) + +if host_machine.system() == 'windows' + configure_file( + input: 'fwupd.wxs.in', + output: 'fwupd.wxs', + configuration: conf + ) +endif diff --git a/fwupd-1.8.6/contrib/migrate.py b/fwupd-1.8.6/contrib/migrate.py new file mode 100755 index 0000000000000000000000000000000000000000..f4e90a42f0308623ecd1ab41273909ed81854e27 --- /dev/null +++ b/fwupd-1.8.6/contrib/migrate.py @@ -0,0 +1,142 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +# import os +import sys +import glob + + +if __name__ == "__main__": + + fns = [] + + if len(sys.argv) > 1: + fns.extend(sys.argv[1:]) + else: + exts = ["c", "h", "map"] + for ext in exts: + for fn in glob.glob("**/*.{}".format(ext), recursive=True): + if fn.startswith("build"): + continue + if fn.startswith("subprojects"): + continue + if fn.startswith(".git"): + continue + fns.append(fn) + + for fn in fns: + modified: bool = False + with open(fn, "r") as f: + buf = f.read() + for old, new in { + "fu_common_sum8": "fu_sum8", + "fu_common_sum8_bytes": "fu_sum8_bytes", + "fu_common_sum16": "fu_sum16", + "fu_common_sum16_bytes": "fu_sum16_bytes", + "fu_common_sum16w": "fu_sum16w", + "fu_common_sum16w_bytes": "fu_sum16w_bytes", + "fu_common_sum32": "fu_sum32", + "fu_common_sum32_bytes": "fu_sum32_bytes", + "fu_common_sum32w": "fu_sum32w", + "fu_common_sum32w_bytes": "fu_sum32w_bytes", + "fu_common_crc8": "fu_crc8", + "fu_common_crc8_full": "fu_crc8_full", + "fu_common_crc16": "fu_crc16", + "fu_common_crc16_full": "fu_crc16_full", + "fu_common_crc32": "fu_crc32", + "fu_common_crc32_full": "fu_crc32_full", + "fu_byte_array_set_size_full": "fu_byte_array_set_size", + "fu_common_string_replace": "fu_string_replace", + "fu_common_string_append_kv": "fu_string_append", + "fu_common_string_append_ku": "fu_string_append_ku", + "fu_common_string_append_kx": "fu_string_append_kx", + "fu_common_string_append_kb": "fu_string_append_kb", + "fu_common_strnsplit": "fu_strsplit", + "fu_common_strnsplit_full": "fu_strsplit_full", + "fu_common_strjoin_array": "fu_strjoin", + "fu_common_strsafe": "fu_strsafe", + "fu_common_strwidth": "fu_strwidth", + "fu_common_strstrip": "fu_strstrip", + "fu_common_strtoull": "fu_strtoull", + "fu_common_strtoull_full": "fu_strtoull", + "FuCommonStrsplitFunc": "FuStrsplitFunc", + "fu_common_bytes_pad": "fu_bytes_pad", + "fu_common_bytes_new_offset": "fu_bytes_new_offset", + "fu_common_bytes_align": "fu_bytes_align", + "fu_common_bytes_is_empty": "fu_bytes_is_empty", + "fu_common_bytes_compare(": "fu_bytes_compare(", + "fu_common_set_contents_bytes": "fu_bytes_set_contents", + "fu_common_get_contents_bytes": "fu_bytes_get_contents", + "fu_common_get_contents_stream": "fu_bytes_get_contents_stream", + "fu_common_get_contents_fd": "fu_bytes_get_contents_fd", + "fu_common_read_uint8_safe": "fu_memread_uint8_safe", + "fu_common_read_uint16_safe": "fu_memread_uint16_safe", + "fu_common_read_uint32_safe": "fu_memread_uint32_safe", + "fu_common_read_uint64_safe": "fu_memread_uint64_safe", + "fu_common_write_uint8_safe": "fu_memwrite_uint8_safe", + "fu_common_write_uint16_safe": "fu_memwrite_uint16_safe", + "fu_common_write_uint32_safe": "fu_memwrite_uint32_safe", + "fu_common_write_uint64_safe": "fu_memwrite_uint64_safe", + "fu_common_write_uint16": "fu_memwrite_uint16", + "fu_common_write_uint24": "fu_memwrite_uint24", + "fu_common_write_uint32": "fu_memwrite_uint32", + "fu_common_write_uint64": "fu_memwrite_uint64", + "fu_common_read_uint16": "fu_memread_uint16", + "fu_common_read_uint24": "fu_memread_uint24", + "fu_common_read_uint32": "fu_memread_uint32", + "fu_common_read_uint64": "fu_memread_uint64", + "fu_common_bytes_compare_raw": "fu_memcmp_safe", + "FuOutputHandler": "FuSpawnOutputHandler", + "fu_common_spawn_sync": "fu_spawn_sync", + "fu_common_kernel_locked_down": "fu_kernel_locked_down", + "fu_common_check_kernel_version": "fu_kernel_check_version", + "fu_common_get_firmware_search_path": "fu_kernel_get_firmware_search_path", + "fu_common_set_firmware_search_path": "fu_kernel_set_firmware_search_path", + "fu_common_reset_firmware_search_path": "fu_kernel_reset_firmware_search_path", + "fu_common_firmware_builder": "fu_firmware_builder_process", + "fu_common_uri_get_scheme": "fu_release_uri_get_scheme", + "fu_common_dump_raw": "fu_dump_raw", + "fu_common_dump_full": "fu_dump_full", + "fu_common_dump_bytes": "fu_dump_bytes", + "fu_common_error_array_get_best": "fu_engine_error_array_get_best", + "fu_common_get_path": "fu_path_from_kind", + "fu_common_filename_glob": "fu_path_glob", + "fu_common_fnmatch": "fu_path_fnmatch", + "fu_common_rmtree": "fu_path_rmtree", + "fu_common_get_files_recursive": "fu_path_get_files", + "fu_common_mkdir": "fu_path_mkdir", + "fu_common_mkdir_parent": "fu_path_mkdir_parent", + "fu_common_find_program_in_path": "fu_path_find_program", + "fu_common_cpuid": "fu_cpuid", + "fu_common_get_cpu_vendor": "fu_cpu_get_vendor", + "fu_common_vercmp_full": "fu_version_compare", + "fu_common_version_ensure_semver_full": "fu_version_ensure_semver", + "fu_common_version_from_uint16": "fu_version_from_uint16", + "fu_common_version_from_uint32": "fu_version_from_uint32", + "fu_common_version_from_uint64": "fu_version_from_uint64", + "fu_common_version_guess_format": "fu_version_guess_format", + "fu_common_version_parse_from_format": "fu_version_parse_from_format", + "fu_common_version_verify_format": "fu_version_verify_format", + "fu_common_get_volumes_by_kind": "fu_volume_new_by_kind", + "fu_common_get_volume_by_device": "fu_volume_new_by_device", + "fu_common_get_volume_by_devnum": "fu_volume_new_by_devnum", + "fu_common_get_esp_for_path": "fu_volume_new_esp_for_path", + "fu_common_get_esp_default": "fu_context_get_esp_volumes", + "fu_smbios_to_string": "fu_firmware_to_string", + "fu_i2c_device_read_full": "fu_i2c_device_read", + "fu_i2c_device_write_full": "fu_i2c_device_write", + }.items(): + if buf.find(old) == -1: + continue + buf = buf.replace(old, new) + modified = True + if modified: + print("MODIFIED: {}".format(fn)) + with open(fn, "w") as f: + f.write(buf) + + sys.exit(0) diff --git a/fwupd-1.8.6/contrib/mingw64.cross b/fwupd-1.8.6/contrib/mingw64.cross new file mode 100644 index 0000000000000000000000000000000000000000..31a289932e78e1d1003996732fc9268cc926644c --- /dev/null +++ b/fwupd-1.8.6/contrib/mingw64.cross @@ -0,0 +1,2 @@ +[binaries] +windmc = '/usr/bin/x86_64-w64-mingw32-windmc' diff --git a/fwupd-1.8.6/contrib/prepare-system b/fwupd-1.8.6/contrib/prepare-system new file mode 100755 index 0000000000000000000000000000000000000000..0d8b6c6d472521da2f46d830a7808f3b10323149 --- /dev/null +++ b/fwupd-1.8.6/contrib/prepare-system @@ -0,0 +1,48 @@ +#!/bin/bash -e +# Setup local system for running development version + +PREFIX=$1 +ACTION=$2 + +cleanup () +{ + sudo rm -f /etc/dbus-1/system-local.conf \ + /usr/share/polkit-1/actions/org.freedesktop.fwupd.policy \ + /usr/share/polkit-1/rules.d/org.freedesktop.fwupd.rules \ + /etc/grub.d/35_fwupd +} + +install () +{ + cat > system-local.conf << EOF + + PREFIX/share/dbus-1/system.d + +EOF + sed -i s,PREFIX,$1, system-local.conf + sudo mv system-local.conf /etc/dbus-1/system-local.conf + sudo ln -s $1/share/polkit-1/actions/org.freedesktop.fwupd.policy \ + /usr/share/polkit-1/actions/org.freedesktop.fwupd.policy + sudo ln -s $1/polkit-1/rules.d/org.freedesktop.fwupd.rules \ + /usr/share/polkit-1/rules.d/org.freedesktop.fwupd.rules + sudo ln -s /usr/local/etc/grub.d/35_fwupd /etc/grub.d/35_fwupd +} + +if [ "$PREFIX" = "/" ]; then + echo "Invalid prefix: $PREFIX" + exit 1 +fi +case $ACTION in + remove) + cleanup + ;; + install) + cleanup + install $PREFIX + ;; + *) + echo "Unknown action $ACTION" + exit 1 + ;; +esac +sudo systemctl reload dbus.service diff --git a/fwupd-1.8.6/contrib/qubes/README.md b/fwupd-1.8.6/contrib/qubes/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4b3aa2daf8d9b80eea0e710511e580cd1d2118b1 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/README.md @@ -0,0 +1,222 @@ +# qubes-fwupd + +fwupd wrapper for QubesOS + +## Table of Contents + +* [Requirements](#Requirements) +* [Usage](#Usage) +* [Installation](#Installation) +* [Testing](#Testing) +* [Whonix support](doc/whonix.md) +* [UEFI capsule update](doc/uefi_capsule_update.md) +* [Heads update](doc/heads_update.md) + +## OS Requirements + +**Operating System:** Qubes OS R4.1 + +**Admin VM (dom0):** Fedora 32 + +**Template VM:** Fedora 32 + +**Whonix VM:** whonix-gw-15 + +## Usage + +```text +========================================================================================== +Usage: +========================================================================================== + Command: qubes-fwupdmgr [OPTION…][FLAG..] + Example: qubes-fwupdmgr refresh --whonix --url= + +Options: +========================================================================================== + get-devices: Get all devices that support firmware updates + get-updates: Get the list of updates for connected hardware + refresh: Refresh metadata from remote server + update: Update chosen device to latest firmware version + update-heads: Updates heads firmware to the latest version + downgrade: Downgrade chosen device to chosen firmware version + clean: Delete all cached update files + +Flags: +========================================================================================== + --whonix: Download firmware updates via Tor + --device: Specify device for heads update (default - x230) + --url: Address of the custom metadata remote server + +Help: +========================================================================================== + -h --help: Show help options +``` + +## Installation + +For development purpose: + +* Build the package for fedora and debian as it is shown in the contrib +[README](../README.md). +* The build artifacts are placed in `dist` directory: + -- dom0 package - `dist/fwupd-qubes-dom0--0.1alpha.fc32.x86_64.rpm` + -- vm package - `dist/fwupd-qubes-vm--0.1alpha.fc32.x86_64.rpm` + -- whonix package - `dist/fwupd-qubes-vm-whonix-_amd64.deb` + +* Copy packages to the Qubes OS. +* Move the `fwupd-qubes-vm--0.1alpha.fc32.x86_64.rpm` to the Fedora 32 +template VM (replace `` with the current version) + +```shell +qvm-copy fwupd-qubes-vm--0.1alpha.fc32.x86_64.rpm +``` + +* Install package dependencies + +```shell +# dnf install gcab fwupd +``` + +* Run terminal in the template VM and go to +`~/QubesIncoming/`. Compare SHA sums of the package in +TemplateVM and qubes-builder VM. If they match, install the package: + +```shell +# rpm -U fwupd-qubes-vm--0.1alpha.fc32.x86_64.rpm +``` + +* Shutdown TemplateVM + +* Run whonix-gw-15 and copy whonix a package from qubes builder VM + +```shell +qvm-copy fwupd-qubes-vm-whonix-_amd64.deb +``` + +* Install dependencies + +```shell +# apt install gcab fwupd +``` + +* Run terminal in the whonix-gw-15 and go to `~/QubesIncoming/qubes-builder`. + Compare SHA sums of the package in TemplateVM and qubes-builder VM. If they + match, install the package: + + ```shell + # dpkg -i fwupd-qubes-vm-whonix-_amd64.deb + ``` + +* Shutdown whonix-gw-15 + +* Run dom0 terminal in the dom0 and copy package: + + ```shell + $ qvm-run --pass-io \ + 'cat /qubes-src/fwupd/pkgs/dom0-fc32/x86_64/fwupd-qubes-dom0--0.1alpha.fc32.x86_64.rpm' > \ + fwupd-qubes-dom0--0.1alpha.fc32.x86_64.rpm + ``` + +* Install package dependencies: + + ```shell + # qubes-dom0-update gcab fwupd python36 + ``` + +* Make sure that sys-firewall, sys-whonix, and sys-usb (if exists) are running. + +* Compare the SHA sums of the package in dom0 and qubes-builder VM. + If they match, install the package: + + ```shell + # rpm -U qubes-fwupd-dom0-0.2.0-1.fc32.x86_64.rpm + ``` + +* Reboot system (or reboot sys-firewall, sys-whonix, and sys-usb) + +* Run the tests to verify the installation process + +## Testing + +### Outside the Qubes OS + +A test case covers the whole qubes_fwupdmgr script. It could be run outside the +Qubes OS. If the requirements of a single test are not met, it will be omitted. +To run the tests, move to the repo directory and type the following: + +```shell +$ python3 -m unittest -v test.test_qubes_fwupdmgr + +test_clean_cache (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_downgrade_firmware (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'Required device not connected' +test_download_firmware_updates (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_download_metadata (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_get_devices (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_get_devices_qubes (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_get_updates (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_get_updates_qubes (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_help (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_output_crawler (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_parse_downgrades (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_parse_parameters (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_parse_updates_info (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_refresh_metadata (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... skipped 'requires Qubes OS' +test_user_input_choice (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_user_input_downgrade (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_user_input_empty_list (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_user_input_n (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_verify_dmi (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_verify_dmi_argument_version (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_verify_dmi_version (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok +test_verify_dmi_wrong_vendor (test.test_qubes_fwupdmgr.TestQubesFwupdmgr) ... ok + +---------------------------------------------------------------------- +Ran 22 tests in 0.003s + +OK (skipped=8) +``` + +### In the Qubes OS + +In the dom0, move to: + +```shell +cd /usr/share/qubes-fwupd/ +``` + +#### Qubes OS 4.1 + +Run the tests with sudo privileges: + +```shell +# python3 -m unittest -v test.test_qubes_fwupdmgr +``` + +Note: If the whonix tests failed, make sure that you are connected to the Tor + +## Whonix support + +```shell +# qubes-fwupdmgr [refresh/update/downgrade] --whonix [FLAG] +``` + +More specified information you will find in the +[whonix documentation](doc/whonix.md). + +## UEFI capsule update + +```shell +# qubes-fwupdmgr [update/downgrade] +``` + +Requirements and more specified information you will find in the +[UEFI capsule update documentation](doc/uefi_capsule_update.md). + +## Heads update + +```shell +# qubes-fwupdmgr update-heads --device=x230 --url= +``` + +Requirements and more specified information you will find in the +[heads update documentation](doc/heads_update.md). diff --git a/fwupd-1.8.6/contrib/qubes/doc/heads_update.md b/fwupd-1.8.6/contrib/qubes/doc/heads_update.md new file mode 100644 index 0000000000000000000000000000000000000000..a9559443f88941f29946dd91e63c00ca914bc079 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/doc/heads_update.md @@ -0,0 +1,63 @@ +# Heads update + +The Heads update was tested on the `Lenovo ThinkPad x230`. + +## Requirements + +You need to build and flash Heads ROM from the +[3mdeb fork](https://github.com/3mdeb/heads/tree/qubes-fwupd). +You will find there Heads ROMs for ThinkPad x230. + +## Update process + +ThinkPad x230 is now the only laptop that has Heads ROM in the custom LVFS +storage. Nevertheless, qubes-fwupd has already implemented a `device` flag, that +will allow updates for other hardware. + +At first run the qubes-fwupd Heads update. + +```shell +sudo qubes-fwupdmgr update-heads --device=x230 +``` + +Press Y to reboot the device. + +In the main menu, choose `options` and then go to `Flash/Update the BIOS` + +![img](img/heads_options.jpg) + +Decide to retain or erase the settings. + +![img](img/heads_firmware_managment_menu.jpg) + +The tool will inform you that heads update has been detected in `/boot` +directory. If you will decide not to update, you will be asked to attach the +USB drive. + +![img](img/heads_detected.jpg) + +Select a ROM file. + +![img](img/heads_selecting_rom.jpg) + +Press yes to confirm the choice. The Heads update will begin. + +![img](img/heads_flash_rom.jpg) + +Wait until the end of the update process. + +![img](img/heads_update_process.jpg) + +Press OK to reboot the system. + +![img](img/heads_success.jpg) + +## Test + +Change directory to `/usr/share/qubes-fwupd` and run test case with sudo +privileges. + +### Qubes OS R4.1 + +```shell +# python3 -m unittest -v test.test_qubes_fwupd_heads diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_detected.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_detected.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c322f90e1ec8e89408bd3cda72e0183be39b49c Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_detected.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_firmware_managment_menu.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_firmware_managment_menu.jpg new file mode 100644 index 0000000000000000000000000000000000000000..74da7bd16b6825262722b70e707a8629f040fe4c Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_firmware_managment_menu.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_flash_rom.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_flash_rom.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7770da5dfdbe4827b729c4edd1e40963fce6a49b Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_flash_rom.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_options.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_options.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8f55d57686c353c41a6bcb62291f31d7f47e7e0e Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_options.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_selecting_rom.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_selecting_rom.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10066b1208f95a7333db3b79c7de9c2ea3ee655f Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_selecting_rom.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_success.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_success.jpg new file mode 100644 index 0000000000000000000000000000000000000000..987be595b8d917c2c8edad1c904561763e5381b2 Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_success.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/heads_update_process.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/heads_update_process.jpg new file mode 100644 index 0000000000000000000000000000000000000000..668b1d717e9987cbb1e704176456f2674532f9df Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/heads_update_process.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/qubes_manager.png b/fwupd-1.8.6/contrib/qubes/doc/img/qubes_manager.png new file mode 100644 index 0000000000000000000000000000000000000000..8775127ffce6d2e803756326283a21e9d79b3106 Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/qubes_manager.png differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/uefi_ME.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/uefi_ME.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89790da0e499fbb0272a40728dfe59524b323e49 Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/uefi_ME.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/uefi_capsule_found.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/uefi_capsule_found.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fba123330de0e72e2b83394a54b00ebb0fecb95f Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/uefi_capsule_found.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/img/uefi_success.jpg b/fwupd-1.8.6/contrib/qubes/doc/img/uefi_success.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a329a45c8c36b6515cdb9f16dbe434896ca304c7 Binary files /dev/null and b/fwupd-1.8.6/contrib/qubes/doc/img/uefi_success.jpg differ diff --git a/fwupd-1.8.6/contrib/qubes/doc/uefi_capsule_update.md b/fwupd-1.8.6/contrib/qubes/doc/uefi_capsule_update.md new file mode 100644 index 0000000000000000000000000000000000000000..932cb23b841bca67477d18d092a8749e80181e6c --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/doc/uefi_capsule_update.md @@ -0,0 +1,47 @@ +# UEFI capsule update + +The qubes-fwupd handle the UEFI capsule update under several conditions. +The fwupd uses ESRT tables to read GUID, and that causes trouble when the OS +is running under a hypervisor. The Xen does not pass the ESRT tables to +paravirtualized dom0, so the Qubes is not able to provide sysfs information. +More information you can find it this thread: + + + +## Requirements + +### Qubes OS + +You need Qubes R4.1 to use the UEFI capsule update. + +### Hardware + +Make sure that your hardware has available firmware updates in the [LVFS](https://fwupd.org/) + +## UEFI capsule update - downgrade + +UEFI capsule updates and downgrades were tested on DELL XPS 15 9560. + +```shell +sudo qubes-fwupdmgr downgrade +``` + +## UEFI capsule update - update + +```shell +sudo qubes-fwupdmgr update +``` + +## Update process + +### Capsule found + +![img](img/uefi_capsule_found.jpg) + +### ME updated + +![img](img/uefi_ME.jpg) + +### Success + +![img](img/uefi_success.jpg) diff --git a/fwupd-1.8.6/contrib/qubes/doc/whonix.md b/fwupd-1.8.6/contrib/qubes/doc/whonix.md new file mode 100644 index 0000000000000000000000000000000000000000..a2064b885f5184fbae5c8dc303c08dc51de522c2 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/doc/whonix.md @@ -0,0 +1,23 @@ +# Whonix support + +The qubes-fwupd uses the sys-whonix VM as the update VM to handle downloading +updates and metadata via Tor. The tests detect if sys-whonix is running, but +do not check if you are connected with Tor. So before running the test make sure +that sys-whonix has access to the network. + +## Refresh + +```shell +sudo qubes-fwupdmgr refresh --whonix +``` + +## Update + +```shell +sudo qubes-fwupdmgr update --whonix +``` + +## Downgrade + +```shell +sudo qubes-fwupdmgr downgrade --whonix diff --git a/fwupd-1.8.6/contrib/qubes/meson.build b/fwupd-1.8.6/contrib/qubes/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..639ae9e14c2527b3e4fed2e40311662d29b5bd34 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/meson.build @@ -0,0 +1,53 @@ +install_data([ + 'src/__init__.py', + 'src/fwupd_receive_updates.py', + 'src/qubes_fwupd_heads.py', + 'src/qubes_fwupd_update.py', + ], + install_dir: 'share/qubes-fwupd/src', +) + +install_data([ + 'test/__init__.py', + 'test/fwupd_logs.py', + 'test/test_qubes_fwupd_heads.py', + 'test/test_qubes_fwupdmgr.py', + ], + install_dir: 'share/qubes-fwupd/test', +) + +install_data([ + 'test/logs/firmware.metainfo.xml', + 'test/logs/get_devices.log', + 'test/logs/get_updates.log', + 'test/logs/help.log', + ], + install_dir: 'share/qubes-fwupd/test/logs', +) + +install_data([ + 'src/vms/fwupd_common_vm.py', + 'src/vms/fwupd_download_updates.py', + 'src/vms/fwupd_usbvm_validate.py', + ], + install_dir: 'libexec/qubes-fwupd', + install_mode: 'rwxrwxr-x', +) + +install_data([ + 'test/logs/metainfo_name/firmware.metainfo.xml', + ], + install_dir: 'share/qubes-fwupd/test/logs/metainfo_name', +) + +install_data( + 'test/logs/metainfo_version/firmware.metainfo.xml', + install_dir: 'share/qubes-fwupd/test/logs/metainfo_version', +) + +install_data( + 'src/qubes_fwupdmgr.py', + install_dir: 'sbin', + rename: 'qubes-fwupdmgr', + install_mode: 'rwxrwxr-x', +) diff --git a/fwupd-1.8.6/contrib/qubes/src/__init__.py b/fwupd-1.8.6/contrib/qubes/src/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/contrib/qubes/src/fwupd_receive_updates.py b/fwupd-1.8.6/contrib/qubes/src/fwupd_receive_updates.py new file mode 100644 index 0000000000000000000000000000000000000000..edf1de9f028758100cc4a72f0b99570042d60cee --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/fwupd_receive_updates.py @@ -0,0 +1,258 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Rafal Wojtczuk +# 2020 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import glob +import grp +import hashlib +import os +import re +import shutil +import subprocess + +FWUPD_DOM0_DIR = "/root/.cache/fwupd" +FWUPD_DOM0_UPDATES_DIR = os.path.join(FWUPD_DOM0_DIR, "updates") +FWUPD_DOM0_UNTRUSTED_DIR = os.path.join(FWUPD_DOM0_UPDATES_DIR, "untrusted") +FWUPD_DOM0_METADATA_DIR = os.path.join(FWUPD_DOM0_DIR, "metadata") +FWUPD_DOM0_METADATA_FILE = os.path.join(FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz") +FWUPD_DOM0_METADATA_JCAT = os.path.join(FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz.jcat") + +FWUPD_VM_DIR = "/home/user/.cache/fwupd" +FWUPD_VM_UPDATES_DIR = os.path.join(FWUPD_VM_DIR, "updates") +FWUPD_VM_METADATA_DIR = os.path.join(FWUPD_VM_DIR, "metadata") +FWUPD_VM_METADATA_FILE = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz") +FWUPD_VM_METADATA_JCAT = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz.jcat") +FWUPD_PKI = "/etc/pki/fwupd" +FWUPD_DOWNLOAD_PREFIX = "https://fwupd.org/downloads/" +FWUPD_METADATA_FLAG_REGEX = re.compile(r"^metaflag") +FWUPD_METADATA_FILES_REGEX = re.compile( + r"^firmware[a-z0-9\[\]\@\<\>\.\"\-]{0,128}.xml.gz.?[aj]?[sc]?[ca]?t?$" +) +HEADS_UPDATES_DIR = "/boot/updates" +WARNING_COLOR = "\033[93m" + + +class FwupdReceiveUpdates: + def _check_shasum(self, file_path, sha): + """Compares computed SHA256 checksum with `sha` parameter. + + Keyword arguments: + file_path -- absolute path to the file + sha -- SHA256 checksum of the file + """ + with open(file_path, "rb") as f: + c_sha = hashlib.sha256(f.read()).hexdigest() + if c_sha != sha: + self.clean_cache() + raise ValueError(f"Computed checksum {c_sha} did NOT match {sha}.") + + def _check_domain(self, updatevm): + """Checks if domain given as `updatevm` is allowed to send update + files. + + Keyword argument: + updatevm - domain to be checked + """ + cmd = ["qubes-prefs", "--force-root", "updatevm"] + p = subprocess.check_output(cmd) + source = p.decode("ascii").rstrip() + if source != updatevm and "sys-whonix" != updatevm: + raise Exception(f"Domain {updatevm} not allowed to send dom0 updates") + + def _verify_received(self, files_path, regex_pattern, updatevm): + """Checks if sent files match regex filename pattern. + + Keyword arguments: + + files_path -- absolute path to inspected directory + regex_pattern -- pattern of the expected files + updatevm - domain to be checked + """ + for untrusted_f in os.listdir(files_path): + if not regex_pattern.match(untrusted_f): + raise Exception(f"Domain {updatevm} sent unexpected file") + f = untrusted_f + assert "/" not in f + assert "\0" not in f + assert "\x1b" not in f + path_f = os.path.join(files_path, f) + if os.path.islink(path_f) or not os.path.isfile(path_f): + raise Exception(f"Domain {updatevm} sent not regular file") + + def _create_dirs(self, *args): + """Method creates directories. + + Keyword arguments: + *args -- paths to be created + """ + qubes_gid = grp.getgrnam("qubes").gr_gid + self.old_umask = os.umask(0o002) + if args is None: + raise Exception("Creating directories failed, no paths given.") + for file_path in args: + if not os.path.exists(file_path): + os.mkdir(file_path) + os.chown(file_path, -1, qubes_gid) + os.chmod(file_path, 0o0775) + elif os.stat(file_path).st_gid != qubes_gid: + print( + f"{WARNING_COLOR}Warning: You should move a personal files" + f" from {file_path}. Cleaning cache will cause lose of " + f"the personal data!!{WARNING_COLOR}" + ) + + def _extract_archive(self, archive_path, output_path): + """Extracts archive file to the specified directory. + + Keyword arguments: + archive_path -- absolute path to archive file + output_path -- absolute path to the output directory + """ + cmd_extract = ["gcab", "-x", f"--directory={output_path}", f"{archive_path}"] + shutil.copy(archive_path, FWUPD_DOM0_UPDATES_DIR) + p = subprocess.Popen(cmd_extract, stdout=subprocess.PIPE) + p.communicate()[0].decode("ascii") + if p.returncode != 0: + raise Exception(f"gcab: Error while extracting {archive_path}.") + + def _jcat_verification(self, file_path, file_directory): + """Verifies sha1 and sha256 checksum, GPG signature, + and PKCS#7 signature. + + Keyword argument: + file_path -- absolute path to jcat file + file_directory -- absolute path to the directory to jcat file location + """ + cmd_jcat = ["jcat-tool", "verify", f"{file_path}", "--public-keys", FWUPD_PKI] + p = subprocess.Popen( + cmd_jcat, cwd=file_directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, __ = p.communicate() + verification = stdout.decode("utf-8") + print(verification) + if p.returncode != 0: + self.clean_cache() + raise Exception("jcat-tool: Verification failed") + + def handle_fw_update(self, updatevm, sha, filename): + """Copies firmware update archives from the updateVM. + + Keyword arguments: + updatevm -- update VM name + sha -- SHA256 checksum of the firmware update archive + filename -- name of the firmware update archive + """ + fwupd_firmware_file_regex = re.compile(filename) + dom0_firmware_untrusted_path = os.path.join(FWUPD_DOM0_UNTRUSTED_DIR, filename) + updatevm_firmware_file_path = os.path.join(FWUPD_VM_UPDATES_DIR, filename) + + self._check_domain(updatevm) + if os.path.exists(FWUPD_DOM0_UNTRUSTED_DIR): + shutil.rmtree(FWUPD_DOM0_UNTRUSTED_DIR) + self._create_dirs(FWUPD_DOM0_UPDATES_DIR, FWUPD_DOM0_UNTRUSTED_DIR) + + cmd_copy = "qvm-run --pass-io %s %s > %s" % ( + updatevm, + "'cat %s'" % updatevm_firmware_file_path, + dom0_firmware_untrusted_path, + ) + p = subprocess.Popen(cmd_copy, shell=True) + p.wait() + if p.returncode != 0: + raise Exception("qvm-run: Copying firmware file failed!!") + + self._verify_received( + FWUPD_DOM0_UNTRUSTED_DIR, fwupd_firmware_file_regex, updatevm + ) + self._check_shasum(dom0_firmware_untrusted_path, sha) + untrusted_dir_name = filename.replace(".cab", "") + self._extract_archive(dom0_firmware_untrusted_path, FWUPD_DOM0_UNTRUSTED_DIR) + signature_name = os.path.join(FWUPD_DOM0_UNTRUSTED_DIR, "firmware*.jcat") + file_path = glob.glob(signature_name) + if not file_path: + raise FileNotFoundError("jcat file not found!") + self._jcat_verification(file_path[0], FWUPD_DOM0_UNTRUSTED_DIR) + os.umask(self.old_umask) + if untrusted_dir_name == "untrusted": + untrusted_dir_name = "trusted" + verified_file = os.path.join(FWUPD_DOM0_UPDATES_DIR, filename) + self.arch_name = "trusted.cab" + self.arch_path = os.path.join(FWUPD_DOM0_UPDATES_DIR, self.arch_name) + shutil.move(verified_file, self.arch_path) + else: + self.arch_path = os.path.join(FWUPD_DOM0_UPDATES_DIR, filename) + dir_name = os.path.join(FWUPD_DOM0_UPDATES_DIR, untrusted_dir_name) + os.remove(dom0_firmware_untrusted_path) + shutil.move(FWUPD_DOM0_UNTRUSTED_DIR, dir_name) + + def handle_metadata_update(self, updatevm, metadata_url=None): + """Copies metadata files from the updateVM. + + Keyword argument: + updatevm -- update VM name + """ + if metadata_url: + metadata_name = metadata_url.replace(FWUPD_DOWNLOAD_PREFIX, "") + self.metadata_file = os.path.join(FWUPD_DOM0_METADATA_DIR, metadata_name) + self.metadata_file_jcat = self.metadata_file + ".jcat" + else: + self.metadata_file = FWUPD_DOM0_METADATA_FILE + self.metadata_file_jcat = FWUPD_DOM0_METADATA_JCAT + self.metadata_file_updatevm = self.metadata_file.replace( + FWUPD_DOM0_METADATA_DIR, FWUPD_VM_METADATA_DIR + ) + self.metadata_file_jcat_updatevm = self.metadata_file_jcat.replace( + FWUPD_DOM0_METADATA_DIR, FWUPD_VM_METADATA_DIR + ) + self._check_domain(updatevm) + self._create_dirs(FWUPD_DOM0_METADATA_DIR) + cmd_file = "'cat %s'" % self.metadata_file_updatevm + cmd_jcat = "'cat %s'" % self.metadata_file_jcat_updatevm + cmd_copy_metadata_file = "qvm-run --pass-io %s %s > %s" % ( + updatevm, + cmd_file, + self.metadata_file, + ) + cmd_copy_metadata_jcat = "qvm-run --pass-io %s %s > %s" % ( + updatevm, + cmd_jcat, + self.metadata_file_jcat, + ) + + p = subprocess.Popen(cmd_copy_metadata_file, shell=True) + p.wait() + if p.returncode != 0: + raise Exception("qvm-run: Copying metadata file failed!!") + p = subprocess.Popen(cmd_copy_metadata_jcat, shell=True) + p.wait() + if p.returncode != 0: + raise Exception('qvm-run": Copying metadata jcat failed!!') + + self._verify_received( + FWUPD_DOM0_METADATA_DIR, FWUPD_METADATA_FILES_REGEX, updatevm + ) + self._jcat_verification(self.metadata_file_jcat, FWUPD_DOM0_METADATA_DIR) + os.umask(self.old_umask) + + def clean_cache(self, usbvm=False): + """Removes updates data + + Keyword arguments: + usbvm -- usbvm support flag + """ + print("Cleaning dom0 cache directories") + if os.path.exists(FWUPD_DOM0_METADATA_DIR): + shutil.rmtree(FWUPD_DOM0_METADATA_DIR) + if os.path.exists(FWUPD_DOM0_UPDATES_DIR): + shutil.rmtree(FWUPD_DOM0_UPDATES_DIR) + if os.path.exists(HEADS_UPDATES_DIR): + shutil.rmtree(HEADS_UPDATES_DIR) + if usbvm: + print("Cleaning usbvm cache directories") + self._clean_usbvm() diff --git a/fwupd-1.8.6/contrib/qubes/src/qubes_fwupd_heads.py b/fwupd-1.8.6/contrib/qubes/src/qubes_fwupd_heads.py new file mode 100644 index 0000000000000000000000000000000000000000..d551c73dcaea70661177236d6a180334320c1d26 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/qubes_fwupd_heads.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import subprocess +import os +import shutil +import xml.etree.ElementTree as ET +from packaging.version import Version + +FWUPDTOOL = "/bin/fwupdtool" + +BOOT = "/boot" +HEADS_UPDATES_DIR = os.path.join(BOOT, "updates") + +EXIT_CODES = {"ERROR": 1, "SUCCESS": 0, "NOTHING_TO_DO": 2} + + +class FwupdHeads: + def _get_hwids(self): + cmd_hwids = [FWUPDTOOL, "hwids"] + p = subprocess.Popen(cmd_hwids, stdout=subprocess.PIPE) + self.dom0_hwids_info = p.communicate()[0].decode() + if p.returncode != 0: + raise Exception("fwupd-qubes: Getting hwids info failed") + + def _gather_firmware_version(self): + """ + Checks if Qubes works under heads + """ + if "Heads" in self.dom0_hwids_info: + self.heads_version = None + hwids = self.dom0_hwids_info.split("\n") + for line in hwids: + if "Heads" in line: + self.heads_version = line.split("Heads-v")[1] + else: + print("Device is not running under the heads firmware!!") + print("Exiting...") + return EXIT_CODES["NOTHING_TO_DO"] + + def _parse_metadata(self, metadata_file): + """ + Parse metadata info. + """ + cmd_metadata = ["zcat", metadata_file] + p = subprocess.Popen(cmd_metadata, stdout=subprocess.PIPE) + self.metadata_info = p.communicate()[0].decode() + if p.returncode != 0: + raise Exception("fwupd-qubes: Parsing metadata failed") + + def _parse_heads_updates(self, device): + """ + Parses heads updates info. + + Keyword arguments: + device -- Model of the updated device + """ + self.heads_update_url = None + self.heads_update_sha = None + self.heads_update_version = None + heads_metadata_info = None + root = ET.fromstring(self.metadata_info) + for component in root.findall("component"): + if f"heads.{device}" in component.find("id").text: + heads_metadata_info = component + if not heads_metadata_info: + print("No metadata info for chosen board") + return EXIT_CODES["NOTHING_TO_DO"] + for release in heads_metadata_info.find("releases").findall("release"): + release_ver = release.get("version") + if self.heads_version == "heads" or Version(release_ver) > Version( + self.heads_version + ): + if not self.heads_update_version or Version(release_ver) > Version( + self.heads_update_version + ): + self.heads_update_url = release.find("location").text + for sha in release.findall("checksum"): + if ( + ".cab" in sha.attrib["filename"] + and sha.attrib["type"] == "sha256" + ): + self.heads_update_sha = sha.text + self.heads_update_version = release_ver + if self.heads_update_url: + return EXIT_CODES["SUCCESS"] + else: + print("Heads firmware is up to date.") + return EXIT_CODES["NOTHING_TO_DO"] + + def _copy_heads_firmware(self, arch_path): + """ + Copies heads update to the boot path + """ + heads_boot_path = os.path.join(HEADS_UPDATES_DIR, self.heads_update_version) + update_path = arch_path.replace(".cab", "/firmware.rom") + + heads_update_path = os.path.join(heads_boot_path, "firmware.rom") + if not os.path.exists(HEADS_UPDATES_DIR): + os.mkdir(HEADS_UPDATES_DIR) + if os.path.exists(heads_update_path): + print(f"Heads Update == {self.heads_update_version} " "already downloaded.") + return EXIT_CODES["NOTHING_TO_DO"] + else: + os.mkdir(heads_boot_path) + shutil.copyfile(update_path, heads_update_path) + print( + f"Heads Update == {self.heads_update_version} " + f"available at {heads_boot_path}" + ) + return EXIT_CODES["SUCCESS"] diff --git a/fwupd-1.8.6/contrib/qubes/src/qubes_fwupd_update.py b/fwupd-1.8.6/contrib/qubes/src/qubes_fwupd_update.py new file mode 100644 index 0000000000000000000000000000000000000000..6ae4f965d99e943856585256472d136f7b395c98 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/qubes_fwupd_update.py @@ -0,0 +1,156 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kaminski +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import grp +import os +import re +import subprocess + +FWUPD_DOM0_DIR = "/root/.cache/fwupd" +FWUPD_VM_DOWNLOAD = "/usr/libexec/qubes-fwupd/fwupd_download_updates.py" +FWUPD_DOM0_UPDATES_DIR = os.path.join(FWUPD_DOM0_DIR, "updates") +FWUPD_DOWNLOAD_PREFIX = "https://fwupd.org/downloads/" + +SPECIAL_CHAR_REGEX = re.compile(r"%20|&|\||#") +UPDATEVM_REGEX = re.compile(r"^sys-") + +WARNING_COLOR = "\033[93m" + + +class FwupdUpdate: + def _create_dirs(self, *args): + """Method creates directories. + + Keyword arguments: + *args -- paths to be created + """ + qubes_gid = grp.getgrnam("qubes").gr_gid + self.old_umask = os.umask(0o002) + if args is None: + raise Exception("Creating directories failed, no paths given.") + for file_path in args: + if not os.path.exists(file_path): + os.mkdir(file_path) + os.chown(file_path, -1, qubes_gid) + elif os.stat(file_path).st_gid != qubes_gid: + print( + f"{WARNING_COLOR}Warning: You should move a personal files" + f" from {file_path}. Cleaning cache will cause lose of " + f"the personal data!!{WARNING_COLOR}" + ) + + def _specify_updatevm(self): + cmd_updatevm = ["qubes-prefs", "--force-root", "updatevm"] + p = subprocess.Popen(cmd_updatevm, stdout=subprocess.PIPE) + self.updatevm = p.communicate()[0].decode().split("\n")[0] + if p.returncode != 0 and not UPDATEVM_REGEX.match(self.updatevm): + self.updatevm = None + raise Exception("Specifying updatevm failed") + + def _check_updatevm(self): + """Checks if usbvm is running""" + cmd_xl_list = ["xl", "list"] + p = subprocess.Popen(cmd_xl_list, stdout=subprocess.PIPE) + output = p.communicate()[0].decode() + if p.returncode != 0: + raise Exception("fwupd-qubes: Firmware downgrade failed") + return self.updatevm in output + + def _encrypt_update_url(self, url): + self.enc_url = url + self.arch_name = url.replace(FWUPD_DOWNLOAD_PREFIX, "") + self.arch_path = os.path.join(FWUPD_DOM0_UPDATES_DIR, self.arch_name) + if "&" in url: + self.enc_url = self.enc_url.replace("&", "--and--") + self.arch_name = "untrusted.cab" + if "|" in url: + self.enc_url = self.enc_url.replace("|", "--or--") + self.arch_name = "untrusted.cab" + if "#" in url: + self.enc_url = self.enc_url.replace("#", "--hash--") + self.arch_name = "untrusted.cab" + if "%20" in url: + self.arch_name = "untrusted.cab" + + def download_metadata(self, whonix=False, metadata_url=None): + """Initialize downloading metadata files. + + Keywords arguments: + whonix -- Flag enforces downloading the metadata updates via Tor + metadata_url -- Download metadata from the custom url + """ + if not whonix: + self._specify_updatevm() + else: + self.updatevm = "sys-whonix" + if not self._check_updatevm(): + raise Exception(f"{self.updatevm} is not running!!") + if not os.path.exists(FWUPD_DOM0_DIR): + self._create_dirs(FWUPD_DOM0_DIR) + if metadata_url: + cmd_metadata = [ + "qvm-run", + "--pass-io", + self.updatevm, + ( + "script --quiet --return --command " + f'"{FWUPD_VM_DOWNLOAD} --metadata' + f' --url={metadata_url}"' + ), + ] + else: + cmd_metadata = [ + "qvm-run", + "--pass-io", + self.updatevm, + ( + "script --quiet --return --command " + f'"{FWUPD_VM_DOWNLOAD} --metadata"' + ), + ] + p = subprocess.Popen(cmd_metadata) + p.wait() + if p.returncode != 0: + raise Exception("Metadata download failed.") + + def download_firmware_updates(self, url, sha, whonix=False): + """Initializes downloading firmware update archive. + + Keywords arguments: + url -- url path to the firmware update archive + sha -- SHA256 checksum of the firmware update archive + whonix -- Flag enforces downloading the updates via Tor + """ + if not whonix: + self._specify_updatevm() + else: + self.updatevm = "sys-whonix" + if not self._check_updatevm(): + raise Exception(f"{self.updatevm} is not running!!") + if not os.path.exists(FWUPD_DOM0_DIR): + self._create_dirs(FWUPD_DOM0_DIR) + self._encrypt_update_url(url) + if not os.path.exists(self.arch_path): + cmd_firmware_download = [ + "qvm-run", + "--pass-io", + self.updatevm, + ( + "script --quiet --return --command " + f'"{FWUPD_VM_DOWNLOAD} --url={self.enc_url}' + f' --sha={sha}"' + ), + ] + p = subprocess.Popen(cmd_firmware_download) + p.wait() + if p.returncode != 0: + raise Exception("Firmware download failed.") + else: + self.cached = True + print("Firmware already downloaded. Using cached files.") diff --git a/fwupd-1.8.6/contrib/qubes/src/qubes_fwupdmgr.py b/fwupd-1.8.6/contrib/qubes/src/qubes_fwupdmgr.py new file mode 100755 index 0000000000000000000000000000000000000000..93dd9d0b9549e9611fce1d1be274518ea03c9e94 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/qubes_fwupdmgr.py @@ -0,0 +1,1020 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kaminski +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import json +import os +import re +import shutil +import subprocess +import sys +import xml.etree.ElementTree as ET + +from pathlib import Path +from packaging.version import Version + +FWUPD_QUBES_DIR = "/usr/share/qubes-fwupd" + +# Check if script is run by tests and append sys path properly +if __name__ == "__main__": + sys.path.append(os.path.join(FWUPD_QUBES_DIR, "src")) +else: + sys.path.append("./src") + +try: + from qubes_fwupd_heads import FwupdHeads + from qubes_fwupd_update import FwupdUpdate + from fwupd_receive_updates import FwupdReceiveUpdates +except ModuleNotFoundError: + raise ModuleNotFoundError( + "qubes-fwupd modules not found. " "You may need to reinstall package." + ) + +FWUPD_DOM0_DIR = "/root/.cache/fwupd" +FWUPD_DOM0_METADATA_DIR = os.path.join(FWUPD_DOM0_DIR, "metadata") +FWUPD_DOM0_UPDATES_DIR = os.path.join(FWUPD_DOM0_DIR, "updates") +FWUPD_DOM0_METADATA_SIGNATURE = os.path.join( + FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz.asc" +) +FWUPD_DOM0_METADATA_FILE = os.path.join(FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz") +FWUPD_DOM0_METADATA_JCAT = os.path.join(FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz.jcat") +FWUPD_VM_LOG = os.path.join(FWUPD_DOM0_DIR, "usbvm-devices.log") +FWUPD_VM_VALIDATE = "/usr/libexec/qubes-fwupd/fwupd_usbvm_validate.py" +FWUPD_VM_DIR = "/home/user/.cache/fwupd" +FWUPD_VM_UPDATES_DIR = os.path.join(FWUPD_VM_DIR, "updates") +FWUPD_VM_METADATA_DIR = os.path.join(FWUPD_VM_DIR, "metadata") +FWUPD_VM_METADATA_SIGNATURE = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz.asc") +FWUPD_VM_METADATA_FILE = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz") +FWUPD_VM_METADATA_JCAT = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz.jcat") +FWUPD_DOWNLOAD_PREFIX = "https://fwupd.org/downloads/" + +FWUPDMGR = "/bin/fwupdmgr" +FWUPDAGENT = "/bin/fwupdagent" + +USBVM_N = "sys-usb" + +BIOS_UPDATE_FLAG = os.path.join(FWUPD_DOM0_DIR, "bios_update") +LVFS_TESTING_DOM0_FLAG = os.path.join(FWUPD_DOM0_DIR, "lvfs_testing") +LVFS_TESTING_USBVM_FLAG = os.path.join(FWUPD_VM_DIR, "lvfs_testing") +METADATA_REFRESH_REGEX = re.compile(r"^Successfully refreshed metadata manually$") + +SPECIAL_CHAR_REGEX = re.compile(r"%20|&|\||#") + + +HELP = { + "Usage": [ + { + "Command": "qubes-fwupdmgr [OPTION…][FLAG..]", + "Example": "qubes-fwupdmgr refresh --whonix --url=\n", + } + ], + "Options": [ + { + "get-devices": "Get all devices that support firmware updates", + "get-updates": "Get the list of updates for connected hardware", + "refresh": "Refresh metadata from remote server", + "update": "Update chosen device to latest firmware version", + "update-heads": "Updates heads firmware to the latest version", + "downgrade": "Downgrade chosen device to chosen firmware version", + "clean": "Delete all cached update files\n", + } + ], + "Flags": [ + { + "--whonix": "Download firmware updates via Tor", + "--device": "Specify device for heads update (default - x230)", + "--url": "Address of the custom metadata remote server\n", + } + ], + "Help": [{"-h --help": "Show help options\n"}], +} + +EXIT_CODES = {"ERROR": 1, "SUCCESS": 0, "NOTHING_TO_DO": 2} + + +class QubesFwupdmgr(FwupdHeads, FwupdUpdate, FwupdReceiveUpdates): + def _download_metadata(self, whonix=False, metadata_url=None): + """Initialize downloading metadata files. + + Keywords arguments: + whonix -- Flag enforces downloading the metadata updates via Tor + metadata_url -- Download metadata from the custom url + """ + self.download_metadata(whonix=whonix, metadata_url=metadata_url) + self.handle_metadata_update(self.updatevm, metadata_url=metadata_url) + if not os.path.exists(self.metadata_file): + raise FileNotFoundError("Metadata file does not exist") + + def _validate_usbvm_dirs(self): + """Validates if sys-ubs updates and metadata directories exist.""" + cmd_validate_dirs = [ + "qvm-run", + "--pass-io", + USBVM_N, + f'script --quiet --return --command "{FWUPD_VM_VALIDATE} dirs"', + ] + p = subprocess.Popen(cmd_validate_dirs) + p.wait() + if p.returncode != 0: + raise Exception("Validation of usbvm directories failed.") + + def _validate_usbvm_archive(self, arch_name, sha): + """Validates checksum and gpg signature of the archive file.""" + arch_path = os.path.join(FWUPD_VM_UPDATES_DIR, arch_name) + arch_validate = f"{FWUPD_VM_VALIDATE} updates {arch_path} {sha}" + cmd_validate_arch = [ + "qvm-run", + "--pass-io", + USBVM_N, + f'script --quiet --return --command "{arch_validate}"', + ] + p = subprocess.Popen(cmd_validate_arch) + p.wait() + if p.returncode != 0: + raise Exception("Validation of the archive file failed.") + + def _copy_usbvm_metadata(self): + """Copies metadata files to usbvm.""" + self.metadata_file_usbvm = self.metadata_file.replace( + FWUPD_DOM0_METADATA_DIR, FWUPD_VM_METADATA_DIR + ) + self.metadata_file_jcat_usbvm = self.metadata_file_usbvm + ".jcat" + cat_file = f"cat > {self.metadata_file_usbvm}" + cmd_copy_file = ( + f"cat {self.metadata_file} | " + f'qvm-run --nogui --pass-io {USBVM_N} "{cat_file}"' + ) + cat_jcat = f"cat > {self.metadata_file_jcat_usbvm}" + cmd_copy_jcat = ( + f"cat {self.metadata_file_jcat} | " + f'qvm-run --nogui --pass-io {USBVM_N} "{cat_jcat}"' + ) + p = subprocess.Popen(cmd_copy_file, shell=True) + p.wait() + if p.returncode != 0: + raise Exception("Copying metadata file failed.") + p = subprocess.Popen(cmd_copy_jcat, shell=True) + p.wait() + if p.returncode != 0: + raise Exception("Copying metadata jcat failed.") + + def _validate_usbvm_metadata(self, metadata_url=None): + """Checks GPG signature of metadata files in usbvm.""" + usbvm_cmd = f'"{FWUPD_VM_VALIDATE} metadata"' + if metadata_url: + usbvm_cmd = f'"{FWUPD_VM_VALIDATE} metadata --url={metadata_url}"' + cmd_validate_metadata = [ + "qvm-run", + "--pass-io", + USBVM_N, + "script --quiet --return --command" f" {usbvm_cmd}", + ] + p = subprocess.Popen(cmd_validate_metadata) + p.wait() + if p.returncode != 0: + raise Exception("Metadata validation failed") + + def _refresh_usbvm_metadata(self): + """Refreshes metadata in usbvm.""" + sig_metadata_file = self.metadata_file_jcat_usbvm + cmd_refresh_metadata = [ + "qvm-run", + "--pass-io", + USBVM_N, + ( + "script --quiet --return --command " + f'"{FWUPDMGR} refresh {self.metadata_file_usbvm} ' + f'{sig_metadata_file} {self.lvfs}"' + ), + ] + p = subprocess.Popen(cmd_refresh_metadata) + p.wait() + if p.returncode != 0: + raise Exception("Metadata refresh in usbvm failed") + + def _copy_firmware_updates(self, arch_name): + """Copies updates files to usbvm. + + Keywords arguments: + arch_name - name of the archive file + """ + arch_path = os.path.join(FWUPD_DOM0_UPDATES_DIR, arch_name) + output_path = os.path.join(FWUPD_VM_UPDATES_DIR, arch_name) + cat_file = f"cat > {output_path}" + cmd_copy_file = ( + f"cat {arch_path} | " f'qvm-run --nogui --pass-io {USBVM_N} "{cat_file}"' + ) + p = subprocess.Popen(cmd_copy_file, shell=True) + p.wait() + if p.returncode != 0: + raise Exception("Copying metadata file failed.") + + def _install_usbvm_firmware_update(self, arch_name): + """Installs firmware update for specified device in dom0. + + Keywords arguments: + arch_name - name of the archive file + """ + arch_path = os.path.join(FWUPD_VM_UPDATES_DIR, arch_name) + CMD_update = [ + "qvm-run", + "--pass-io", + USBVM_N, + f"script --quiet --return --command" + f' "{FWUPDMGR} install {arch_path}" /dev/null', + ] + p = subprocess.Popen(CMD_update) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Firmware update failed") + + def _install_usbvm_firmware_downgrade(self, arch_name): + """Installs firmware downgrades for specified device in dom0. + + Keywords arguments: + arch_name - name of the archive file + """ + arch_path = os.path.join(FWUPD_VM_UPDATES_DIR, arch_name) + CMD_downgrade = [ + "qvm-run", + "--pass-io", + USBVM_N, + f"script --quiet --return --command" + f' "{FWUPDMGR} --allow-older install {arch_path}" /dev/null', + ] + p = subprocess.Popen(CMD_downgrade) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Firmware downgrade failed") + + def _clean_usbvm(self): + """Cleans usbvm directories.""" + cmd_clean = [ + "qvm-run", + "--pass-io", + USBVM_N, + f'script --quiet --return --command "{FWUPD_VM_VALIDATE} clean"', + ] + p = subprocess.Popen(cmd_clean) + p.wait() + if p.returncode != 0: + raise Exception("Cleaning usbvm directories failed") + + def _enable_lvfs_testing_dom0(self): + """Checks and enable lvfs-testing for custom metadata in dom0""" + cmd_lvfs_testing = [FWUPDMGR, "enable-remote", "-y", "lvfs-testing"] + if not os.path.exists(LVFS_TESTING_DOM0_FLAG): + p = subprocess.Popen(cmd_lvfs_testing) + p.wait() + if p.returncode != 0: + raise Exception("Enabling dom0 lvfs-testing failed!!") + Path(LVFS_TESTING_DOM0_FLAG).touch(mode=0o644, exist_ok=False) + + def _enable_lvfs_testing_usbvm(self, usbvm=False): + """Checks and enable lvfs-testing for custom metadata in usbvm""" + if not usbvm: + return 0 + cmd_refresh_metadata = [ + "qvm-run", + "--pass-io", + USBVM_N, + ( + "script --quiet --return --command " + f'"{FWUPDMGR} enable-remote -y lvfs-testing"' + ), + ] + cmd_validate_flag = [ + "qvm-run", + "--pass-io", + USBVM_N, + ( + "script --quiet --return --command " + f'"ls {LVFS_TESTING_USBVM_FLAG} &>/dev/null"' + ), + ] + cmd_touch_flag = [ + "qvm-run", + "--pass-io", + USBVM_N, + ("script --quiet --return --command " f'"touch {LVFS_TESTING_USBVM_FLAG}"'), + ] + flag = subprocess.Popen(cmd_validate_flag) + flag.wait() + if flag.returncode != 0: + p = subprocess.Popen(cmd_refresh_metadata) + p.wait() + if p.returncode != 0: + raise Exception("Enabling usbvm lvfs-testing failed!!") + p = subprocess.Popen(cmd_touch_flag) + p.wait() + if p.returncode != 0: + raise Exception("Creating flag failed!!") + + def refresh_metadata(self, usbvm=False, whonix=False, metadata_url=None): + """Updates metadata with downloaded files. + + Keyword arguments: + usbvm -- usbvm support flag + whonix -- Flag enforces downloading the metadata updates via Tor + metadata_url -- Use custom metadata from the url + """ + if metadata_url: + metadata_name = metadata_url.replace(FWUPD_DOWNLOAD_PREFIX, "") + self.metadata_file = os.path.join(FWUPD_DOM0_METADATA_DIR, metadata_name) + self.metadata_file_jcat = self.metadata_file + ".jcat" + self.lvfs = "lvfs-testing" + self._enable_lvfs_testing_dom0() + self._enable_lvfs_testing_usbvm(usbvm=usbvm) + else: + self.metadata_file = FWUPD_DOM0_METADATA_FILE + self.metadata_file_jcat = FWUPD_DOM0_METADATA_JCAT + self.lvfs = "lvfs" + self._download_metadata(whonix=whonix, metadata_url=metadata_url) + if usbvm: + self._validate_usbvm_dirs() + self._copy_usbvm_metadata() + self._validate_usbvm_metadata(metadata_url=metadata_url) + self._refresh_usbvm_metadata() + cmd_refresh = [ + FWUPDMGR, + "refresh", + self.metadata_file, + self.metadata_file_jcat, + self.lvfs, + ] + p = subprocess.Popen(cmd_refresh, stdout=subprocess.PIPE) + self.output = p.communicate()[0].decode() + print(self.output) + if p.returncode != 0: + raise Exception("fwupd-qubes: Refresh failed") + if not METADATA_REFRESH_REGEX.match(self.output): + raise Exception("Manual metadata refresh failed!!!") + + def _get_dom0_updates(self): + """Gathers infromations about available updates.""" + cmd_get_dom0_updates = [FWUPDAGENT, "get-updates"] + p = subprocess.Popen(cmd_get_dom0_updates, stdout=subprocess.PIPE) + self.dom0_updates_info = p.communicate()[0].decode() + if p.returncode != 0 and p.returncode != 2: + raise Exception("fwupd-qubes: Getting available updates failed") + + def _parse_dom0_updates_info(self, updates_info): + """Creates dictionary and list with information about updates. + + Keywords argument: + updates_info - gathered update information + """ + self.dom0_updates_info_dict = json.loads(updates_info) + self.dom0_updates_list = [ + { + "Name": device["Name"], + "Version": device["Version"], + "Releases": [ + { + "Version": update["Version"], + "Url": update["Uri"], + "Checksum": update["Checksum"][-1], + "Description": update["Description"], + } + for update in device["Releases"] + ], + } + for device in self.dom0_updates_info_dict["Devices"] + ] + + def _download_firmware_updates(self, url, sha, whonix=False): + """Initializes downloading firmware update archive. + + Keywords arguments: + url -- url path to the firmware update archive + sha -- SHA256 checksum of the firmware update archive + whonix -- Flag enforces downloading the updates via Tor + """ + self.cached = False + self.download_firmware_updates(url, sha, whonix=whonix) + if not self.cached: + self.handle_fw_update(self.updatevm, sha, self.arch_name) + update_path = self.arch_path.replace(".cab", "") + if not os.path.exists(update_path): + raise NotADirectoryError("Firmware update files do not exist") + + def _user_input(self, updates_dict, downgrade=False, usbvm=False): + """UI for update process. + + Keywords arguments: + updates_dict - list of updates for specified device + downgrade -- downgrade flag + """ + decorator = "======================================================" + if usbvm: + updates_list = updates_dict["dom0"] + updates_dict["usbvm"] + else: + updates_list = updates_dict["dom0"] + dom0_updates_num = len(updates_dict["dom0"]) + if len(updates_list) == 0: + print("No updates available.") + return EXIT_CODES["NOTHING_TO_DO"] + if downgrade: + print("Available downgrades:") + else: + print("Available updates:") + self._updates_crawler(updates_dict["dom0"]) + if usbvm: + self._updates_crawler( + updates_dict["usbvm"], usbvm=True, prefix=dom0_updates_num + ) + + while True: + try: + print("If you want to abandon process press 'N'.") + choice = input("Otherwise choose a device number: ") + if choice == "N" or choice == "n": + return EXIT_CODES["NOTHING_TO_DO"] + device_num = int(choice) - 1 + if 0 <= device_num < len(updates_list): + if not downgrade: + if device_num >= dom0_updates_num: + return "usbvm", device_num - dom0_updates_num + else: + return "dom0", device_num + break + else: + raise ValueError() + except ValueError: + print("Invalid choice.") + + if downgrade: + while True: + try: + releases = updates_list[device_num]["Releases"] + for i, fw_dngd in enumerate(releases): + print(decorator) + print( + f" {i+1}. Firmware downgrade version:" + f"\t {fw_dngd['Version']}" + ) + description = fw_dngd["Description"].replace("

", "") + description = description.replace("

  • ", "") + description = description.replace("
      ", "") + description = description.replace("
    ", "") + description = description.replace("

    ", "\n ") + description = description.replace("
  • ", "\n ") + print(f" Description:{description}") + print("If you want to abandon downgrade process press N.") + choice = input("Otherwise choose downgrade number: ") + if choice == "N" or choice == "n": + return EXIT_CODES["NOTHING_TO_DO"] + downgrade_num = int(choice) - 1 + if 0 <= downgrade_num < len(releases): + if device_num >= dom0_updates_num: + device_abs_num = device_num - dom0_updates_num + return "usbvm", device_abs_num, downgrade_num + else: + return "dom0", device_num, downgrade_num + else: + raise ValueError() + except ValueError: + print("Invalid choice.") + + def _parse_parameters(self, updates_dict, vm_name, choice): + """Parses device name, url, version and SHA256 checksum of the file list. + + Keywords arguments: + updates_dict - dictionary of updates for dom0 and usbvm + vm_name - VM name + choice -- number of device to be updated + """ + self.name = updates_dict[vm_name][choice]["Name"] + self.version = updates_dict[vm_name][choice]["Releases"][0]["Version"] + for ver_check in updates_dict[vm_name][choice]["Releases"]: + if Version(ver_check["Version"]) >= Version(self.version): + self.version = ver_check["Version"] + self.url = ver_check["Url"] + self.sha = ver_check["Checksum"] + + def _install_dom0_firmware_update(self, arch_path): + """Installs firmware update for specified device in dom0. + + Keywords arguments: + arch_path - absolute path to firmware update archive + """ + cmd_install = [FWUPDMGR, "install", arch_path] + p = subprocess.Popen(cmd_install) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Firmware update failed") + + def _read_dmi(self): + """Reads BIOS information from DMI.""" + cmd_dmidecode_version = ["dmidecode", "-s", "bios-version"] + p = subprocess.Popen(cmd_dmidecode_version, stdout=subprocess.PIPE) + p.wait() + self.dmi_version = p.communicate()[0].decode() + cmd_dmidecode = ["dmidecode", "-t", "bios"] + p = subprocess.Popen(cmd_dmidecode, stdout=subprocess.PIPE) + p.wait() + if p.returncode != 0: + raise Exception("dmidecode: Reading DMI failed") + return p.communicate()[0].decode() + + def _verify_dmi(self, path, version, downgrade=False): + """Verifies DMI tables for BIOS updates. + + Keywords arguments: + path -- absolute path of the updates files + version -- version of the update + downgrade -- downgrade flag + """ + dmi_info = self._read_dmi() + path_metainfo = os.path.join(path, "firmware.metainfo.xml") + tree = ET.parse(path_metainfo) + root = tree.getroot() + vendor = root.find("developer_name").text + if vendor is None: + raise ValueError("No vendor information in firmware metainfo.") + if vendor not in dmi_info: + raise ValueError("Wrong firmware provider.") + if not downgrade and Version(version) <= Version(self.dmi_version): + raise ValueError(f"{version} < {self.dmi_version} Downgrade not allowed") + + def _get_dom0_devices(self): + """Gathers information about devices connected in dom0.""" + cmd_get_dom0_devices = [FWUPDAGENT, "get-devices"] + p = subprocess.Popen(cmd_get_dom0_devices, stdout=subprocess.PIPE) + self.dom0_devices_info = p.communicate()[0].decode() + if p.returncode != 0: + raise Exception("fwupd-qubes: Getting devices info failed") + + def _get_usbvm_devices(self): + """Gathers information about devices connected in usbvm.""" + if os.path.exists(FWUPD_VM_LOG): + os.remove(FWUPD_VM_LOG) + usbvm_cmd = f'"{FWUPDAGENT} get-devices"' + log_file = f" > {FWUPD_VM_LOG}" + cmd_get_usbvm_devices = ( + f"qvm-run --nogui --pass-io {USBVM_N} {usbvm_cmd}{log_file}" + ) + p = subprocess.Popen(cmd_get_usbvm_devices, shell=True) + p.wait() + if p.returncode != 0 and p.returncode != 2 and not os.path.exists(FWUPD_VM_LOG): + raise Exception("fwupd-qubes: Getting usbvm devices info failed") + if not os.path.exists(FWUPD_VM_LOG): + raise Exception("usbvm device info log does not exist") + + def _parse_usbvm_updates(self, usbvm_devices_info): + """Creates dictionary and list with information about updates. + + Keywords argument: + usbvm_devices_info - gathered usbvm information + """ + self.usbvm_updates_list = [] + if "No detected devices" in usbvm_devices_info: + return EXIT_CODES["NOTHING_TO_DO"] + usbvm_device_info_dict = json.loads(usbvm_devices_info) + for device in usbvm_device_info_dict["Devices"]: + if "Releases" in device: + self.usbvm_updates_list.append( + { + "Name": device["Name"], + "Version": device["Version"], + "Releases": [], + } + ) + current_version = device["Version"] + for update in device["Releases"]: + if Version(update["Version"]) > current_version: + self.usbvm_updates_list[-1]["Releases"].append( + { + "Version": update["Version"], + "Url": update["Uri"], + "Checksum": update["Checksum"][-1], + "Description": update["Description"], + } + ) + if not self.usbvm_updates_list[-1]["Releases"]: + self.usbvm_updates_list.pop() + + def update_firmware(self, usbvm=False, whonix=False): + """Updates firmware of the specified device. + + Keyword arguments: + usbvm -- usbvm support flag + whonix -- Flag enforces downloading the metadata updates via Tor + """ + self._get_dom0_updates() + self._parse_dom0_updates_info(self.dom0_updates_info) + if usbvm: + self._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + self._parse_usbvm_updates(raw) + update_dict = { + "usbvm": self.usbvm_updates_list, + "dom0": self.dom0_updates_list, + } + ret_input = self._user_input(update_dict, usbvm=True) + else: + update_dict = {"dom0": self.dom0_updates_list} + ret_input = self._user_input(update_dict) + if ret_input == EXIT_CODES["NOTHING_TO_DO"]: + exit(EXIT_CODES["NOTHING_TO_DO"]) + vm_name, choice = ret_input + self._parse_parameters(update_dict, vm_name, choice) + self._download_firmware_updates(self.url, self.sha, whonix=whonix) + if self.name == "System Firmware": + Path(BIOS_UPDATE_FLAG).touch(mode=0o644, exist_ok=True) + extracted_path = self.arch_path.replace(".cab", "") + self._verify_dmi(extracted_path, self.version) + if vm_name == "dom0": + self._install_dom0_firmware_update(self.arch_path) + if vm_name == "usbvm": + self._validate_usbvm_dirs() + self._copy_firmware_updates(self.arch_name) + self._validate_usbvm_archive(self.arch_name, self.sha) + self._install_usbvm_firmware_update(self.arch_name) + + def _parse_downgrades(self, device_list): + """Parses information about possible downgrades. + + Keywords argument: + device_list -- list of connected devices + """ + downgrades = [] + if "No detected devices" in device_list: + return downgrades + dom0_devices_info_dict = json.loads(device_list) + for device in dom0_devices_info_dict["Devices"]: + if "Releases" in device: + try: + version = device["Version"] + except KeyError: + continue + downgrades.append( + { + "Name": device["Name"], + "Version": device["Version"], + "Releases": [ + { + "Version": downgrade["Version"], + "Description": downgrade["Description"], + "Url": downgrade["Uri"], + "Checksum": downgrade["Checksum"][-1], + } + for downgrade in device["Releases"] + if Version(downgrade["Version"]) < Version(version) + ], + } + ) + return downgrades + + def _install_dom0_firmware_downgrade(self, arch_path): + """Installs firmware downgrade for specified device. + + Keywords arguments: + arch_path - absolute path to firmware downgrade archive + """ + cmd_install = [FWUPDMGR, "--allow-older", "install", arch_path] + p = subprocess.Popen(cmd_install) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Firmware downgrade failed") + + def downgrade_firmware(self, usbvm=False, whonix=False): + """Downgrades firmware of the specified device. + + Keyword arguments: + usbvm -- usbvm support flag + whonix -- Flag enforces downloading the metadata updates via Tor + """ + self._get_dom0_devices() + dom0_downgrades = self._parse_downgrades(self.dom0_devices_info) + if usbvm: + self._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + usbvm_downgrades = self._parse_downgrades(raw) + downgrade_dict = {"usbvm": usbvm_downgrades, "dom0": dom0_downgrades} + ret_input = self._user_input(downgrade_dict, downgrade=True, usbvm=True) + else: + downgrade_dict = {"dom0": dom0_downgrades} + ret_input = self._user_input(downgrade_dict, downgrade=True) + if ret_input == EXIT_CODES["NOTHING_TO_DO"]: + exit(EXIT_CODES["NOTHING_TO_DO"]) + vm_name, device_choice, downgrade_choice = ret_input + releases = downgrade_dict[vm_name][device_choice]["Releases"] + downgrade_url = releases[downgrade_choice]["Url"] + downgrade_sha = releases[downgrade_choice]["Checksum"] + self._download_firmware_updates(downgrade_url, downgrade_sha, whonix=whonix) + if downgrade_dict[vm_name][device_choice]["Name"] == "System Firmware": + Path(BIOS_UPDATE_FLAG).touch(mode=0o644, exist_ok=True) + extracted_path = self.arch_path.replace(".cab", "") + self._verify_dmi( + extracted_path, + downgrade_dict[vm_name][device_choice]["Version"], + downgrade=True, + ) + if vm_name == "dom0": + self._install_dom0_firmware_downgrade(self.arch_path) + if vm_name == "usbvm": + self._validate_usbvm_dirs() + self._copy_firmware_updates(self.arch_name) + self._validate_usbvm_archive(self.arch_name, downgrade_sha) + self._install_usbvm_firmware_downgrade(self.arch_name) + + def _output_crawler(self, updev_dict, level, help_f=False, dom0=True): + """Prints device and updates information as a tree. + + Keywords arguments: + updev_dict -- update/device information dictionary + level -- level of the tree + """ + + def _tabs(key_word): + return key_word + "\t" * (4 - int(len(key_word) / 8)) + + decorator = "===================================" + print(2 * decorator) + for updev_key in updev_dict: + style = "\t" * level + output = style + _tabs(updev_key + ":") + if len(updev_key) > 12: + continue + if updev_key == "Icons": + continue + if updev_key == "Releases": + continue + if updev_key == "Name": + print(style + updev_dict["Name"]) + print(2 * decorator) + continue + if isinstance(updev_dict[updev_key], str): + print(output + updev_dict[updev_key]) + elif isinstance(updev_dict[updev_key], int): + print(output + str(updev_dict[updev_key])) + elif isinstance(updev_dict[updev_key][0], str): + for i, data in enumerate(updev_dict[updev_key]): + if i == 0: + print(output + "\u00B7" + data) + continue + print(style + _tabs(" ") + "\u00B7" + data) + elif isinstance(updev_dict[updev_key][0], dict): + if level == 0 and help_f is True: + print(output) + else: + if level == 0 and dom0 is True: + print(f"Dom0 {output}") + elif level == 0 and dom0 is False: + print(f"{USBVM_N} {output}") + + for nested_dict in updev_dict[updev_key]: + self._output_crawler(nested_dict, level + 1) + + def _updates_crawler(self, updates_list, usbvm=False, prefix=0): + """Prints updates information for dom0 and usbvm + + Keywords arguments: + updates_list -- list of devices updates + usbvm -- usbvm support flag + prefix -- device number prefix + """ + available_updates = False + decorator = "======================================================" + print(decorator) + if usbvm: + print(f"{USBVM_N} updates:") + else: + print("Dom0 updates:") + print(decorator) + if len(updates_list) == 0: + print("No updates available.") + return EXIT_CODES["NOTHING_TO_DO"] + else: + for i, device in enumerate(updates_list): + if len(device["Releases"]) == 0: + continue + if not available_updates: + print("Available updates:") + print(decorator) + print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^") + print(f"{i+1+prefix}. Device: {device['Name']}") + print(f" Current firmware version:\t {device['Version']}") + for update in device["Releases"]: + print(decorator) + print(" Firmware update " f"version:\t {update['Version']}") + print(f" URL:\t {update['Url']}") + print(f" SHA256 checksum:\t {update['Checksum']}") + description = update["Description"].replace("

    ", "") + description = description.replace("

  • ", "") + description = description.replace("
      ", "") + description = description.replace("
    ", "") + description = description.replace("

    ", "\n\t") + description = description.replace("
  • ", "\n\t") + print(f" Description: {description}") + print(decorator) + available_updates = True + if not available_updates: + print("No updates available.") + return EXIT_CODES["NOTHING_TO_DO"] + + def get_devices_qubes(self, usbvm=False): + """Gathers and prints devices information. + + Keyword arguments: + usbvm -- usbvm support flag + """ + self._get_dom0_devices() + dom0_devices_info_dict = json.loads(self.dom0_devices_info) + self._output_crawler(dom0_devices_info_dict, 0) + if usbvm: + self._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + if "No detected devices" not in raw: + usbvm_device_info_dict = json.loads(raw) + else: + print(f"No detected devices in {USBVM_N}") + return EXIT_CODES["NOTHING_TO_DO"] + self._output_crawler(usbvm_device_info_dict, 0, dom0=False) + + def get_updates_qubes(self, usbvm=False): + """Gathers and prints updates information. + + Keyword arguments: + usbvm -- usbvm support flag + """ + self._get_dom0_updates() + self._parse_dom0_updates_info(self.dom0_updates_info) + self._updates_crawler(self.dom0_updates_list) + if usbvm: + self._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + self._parse_usbvm_updates(raw) + self._updates_crawler(self.usbvm_updates_list, usbvm=True) + + def help(self): + """Prints help information""" + self._output_crawler(HELP, 0, help_f=True) + + def check_usbvm(self): + """Checks if usbvm is running""" + cmd_xl_list = ["xl", "list"] + p = subprocess.Popen(cmd_xl_list, stdout=subprocess.PIPE) + self.output = p.communicate()[0].decode() + if p.returncode != 0: + raise Exception("fwupd-qubes: Firmware downgrade failed") + return USBVM_N in self.output + + def trusted_cleanup(self, usbvm=False): + """Deletes trusted directory. + + Keyword arguments: + usbvm -- usbvm support flag + """ + trusted_path = os.path.join(FWUPD_DOM0_UPDATES_DIR, "trusted.cab") + if os.path.exists(trusted_path): + os.remove(trusted_path) + shutil.rmtree(trusted_path.replace(".cab", "")) + if usbvm: + self._clean_usbvm() + + def refresh_metadata_after_bios_update(self, usbvm=False): + """Refreshes metadata after bios update + + Keyword arguments: + usbvm -- usbvm support flag + """ + if os.path.exists(BIOS_UPDATE_FLAG): + print("BIOS was updated. Refreshing metadata...") + if "--whonix" in sys.argv: + self.refresh_metadata(usbvm=usbvm, whonix=True) + else: + self.refresh_metadata(usbvm=usbvm) + os.remove(BIOS_UPDATE_FLAG) + + def heads_update(self, device="x230", whonix=False, metadata_url=None): + """ + Updates heads firmware + + Keyword arguments: + device -- Model of the updated device + whonix -- Flag enforces downloading the metadata updates via Tor + metadata_url -- Use custom metadata from the url + """ + if metadata_url: + custom_metadata_name = metadata_url.replace(FWUPD_DOWNLOAD_PREFIX, "") + self.metadata_file = os.path.join( + FWUPD_DOM0_METADATA_DIR, custom_metadata_name + ) + else: + self.metadata_file = FWUPD_DOM0_METADATA_FILE + self._get_hwids() + self._download_metadata(whonix=whonix, metadata_url=metadata_url) + self._parse_metadata(self.metadata_file) + if self._gather_firmware_version() == EXIT_CODES["NOTHING_TO_DO"]: + return EXIT_CODES["NOTHING_TO_DO"] + if self._parse_heads_updates(device) == EXIT_CODES["NOTHING_TO_DO"]: + return EXIT_CODES["NOTHING_TO_DO"] + self._download_firmware_updates(self.heads_update_url, self.heads_update_sha) + return_code = self._copy_heads_firmware(self.arch_path) + if return_code == EXIT_CODES["NOTHING_TO_DO"]: + exit(EXIT_CODES["NOTHING_TO_DO"]) + elif return_code == EXIT_CODES["SUCCESS"]: + print() + while True: + try: + print("An update requires a reboot to complete.") + choice = input("Do you want to restart now? (Y|N)\n") + if choice == "N" or choice == "n": + return EXIT_CODES["SUCCESS"] + elif choice == "Y" or choice == "y": + print("Rebooting...") + os.system("reboot") + else: + raise ValueError() + except ValueError: + print("Invalid choice.") + else: + raise Exception("Copying heads update failed!!") + + def validate_dom0_dirs(self): + """Validates and creates directories""" + if not os.path.exists(FWUPD_DOM0_DIR): + self._create_dirs(FWUPD_DOM0_DIR) + if os.path.exists(FWUPD_DOM0_METADATA_DIR): + shutil.rmtree(FWUPD_DOM0_METADATA_DIR) + self._create_dirs(FWUPD_DOM0_METADATA_DIR) + else: + self._create_dirs(FWUPD_DOM0_METADATA_DIR) + if not os.path.exists(FWUPD_DOM0_UPDATES_DIR): + self._create_dirs(FWUPD_DOM0_UPDATES_DIR) + os.umask(self.old_umask) + + +def main(): + if os.geteuid() != 0: + print("You need to have root privileges to run this script.\n") + exit(EXIT_CODES["ERROR"]) + + q = QubesFwupdmgr() + sys_usb = q.check_usbvm() + q.validate_dom0_dirs() + q.trusted_cleanup(usbvm=sys_usb) + q.refresh_metadata_after_bios_update(usbvm=sys_usb) + + metadata_url = None + device = "x230" + + if not os.path.exists(FWUPD_DOM0_DIR): + q.refresh_metadata(usbvm=sys_usb) + + if len(sys.argv) < 2: + q.help() + exit(1) + for arg in sys.argv: + if "--url=" in arg: + metadata_url = arg.replace("--url=", "") + if FWUPD_DOWNLOAD_PREFIX not in metadata_url: + print( + "Metadata must be stored in the Linux" + " Vendor Firmware Service (https://fwupd.org/)" + ) + print("Exiting...") + exit(1) + if "--device=" in arg: + device = arg.replace("--device=", "") + + if sys.argv[1] == "get-updates": + q.get_updates_qubes(usbvm=sys_usb) + elif sys.argv[1] == "get-devices": + q.get_devices_qubes(usbvm=sys_usb) + elif sys.argv[1] == "update" and "--whonix" in sys.argv: + q.update_firmware(usbvm=sys_usb, whonix=True) + elif sys.argv[1] == "update" and "--whonix" not in sys.argv: + q.update_firmware(usbvm=sys_usb) + elif sys.argv[1] == "downgrade" and "--whonix" in sys.argv: + q.downgrade_firmware(usbvm=sys_usb, whonix=True) + elif sys.argv[1] == "downgrade" and "--whonix" not in sys.argv: + q.downgrade_firmware(usbvm=sys_usb) + elif sys.argv[1] == "clean": + q.clean_cache(usbvm=sys_usb) + elif sys.argv[1] == "refresh" and "--whonix" not in sys.argv: + q.refresh_metadata(usbvm=sys_usb, metadata_url=metadata_url) + elif sys.argv[1] == "refresh" and "--whonix" in sys.argv: + q.refresh_metadata(usbvm=sys_usb, whonix=True, metadata_url=metadata_url) + elif sys.argv[1] == "update-heads" and "--whonix" not in sys.argv: + q.heads_update(device=device, metadata_url=metadata_url) + elif sys.argv[1] == "update-heads" and "--whonix" in sys.argv: + q.heads_update(device=device, metadata_url=metadata_url, whonix=True) + else: + q.help() + exit(1) + + +if __name__ == "__main__": + main() diff --git a/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_common_vm.py b/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_common_vm.py new file mode 100644 index 0000000000000000000000000000000000000000..c8eda536d77ca0188ffa55c33e0e7ea832679902 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_common_vm.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import grp +import hashlib +import os +import shutil +import subprocess + +FWUPD_VM_DIR = "/home/user/.cache/fwupd" +FWUPD_VM_UPDATES_DIR = os.path.join(FWUPD_VM_DIR, "updates") +FWUPD_VM_METADATA_DIR = os.path.join(FWUPD_VM_DIR, "metadata") +WARNING_COLOR = "\033[93m" +FWUPD_PKI = "/etc/pki/fwupd" + + +class FwupdVmCommon: + def _create_dirs(self, *args): + """Method creates directories. + + Keyword arguments: + *args -- paths to be created + """ + qubes_gid = grp.getgrnam("qubes").gr_gid + self.old_umask = os.umask(0o002) + if args is None: + raise Exception("Creating directories failed, no paths given.") + for file_path in args: + if not os.path.exists(file_path): + os.mkdir(file_path) + os.chown(file_path, -1, qubes_gid) + elif os.stat(file_path).st_gid != qubes_gid: + print( + f"{WARNING_COLOR}Warning: You should move a personal files" + f" from {file_path}. Cleaning cache will cause lose of " + f"the personal data!!{WARNING_COLOR}" + ) + + def check_shasum(self, file_path, sha): + """Compares computed SHA256 checksum with `sha` parameter. + + Keyword arguments: + file_path -- absolute path to the file + sha -- SHA256 checksum of the file + """ + with open(file_path, "rb") as f: + c_sha = hashlib.sha256(f.read()).hexdigest() + if c_sha != sha: + self.clean_vm_cache() + raise ValueError("Computed checksum %s did NOT match %s. " % (c_sha, sha)) + + def validate_vm_dirs(self): + """Validates and creates directories""" + print("Validating directories") + if not os.path.exists(FWUPD_VM_DIR): + self._create_dirs(FWUPD_VM_DIR) + if os.path.exists(FWUPD_VM_METADATA_DIR): + shutil.rmtree(FWUPD_VM_METADATA_DIR) + self._create_dirs(FWUPD_VM_METADATA_DIR) + else: + self._create_dirs(FWUPD_VM_METADATA_DIR) + if not os.path.exists(FWUPD_VM_UPDATES_DIR): + self._create_dirs(FWUPD_VM_UPDATES_DIR) + os.umask(self.old_umask) + + def _jcat_verification(self, file_path, file_directory): + """Verifies sha1 and sha256 checksum, GPG signature, + and PKCS#7 signature. + + Keyword argument: + file_path -- absolute path to jcat file + file_directory -- absolute path to the directory to jcat file location + """ + cmd_jcat = ["jcat-tool", "verify", f"{file_path}", "--public-keys", FWUPD_PKI] + p = subprocess.Popen( + cmd_jcat, cwd=file_directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, __ = p.communicate() + verification = stdout.decode("utf-8") + print(verification) + if p.returncode != 0: + self.clean_vm_cache() + raise Exception("jcat-tool: Verification failed") + + def clean_vm_cache(self): + """Removes updates data""" + print("Cleaning cache directories") + if os.path.exists(FWUPD_VM_METADATA_DIR): + shutil.rmtree(FWUPD_VM_METADATA_DIR) + if os.path.exists(FWUPD_VM_UPDATES_DIR): + shutil.rmtree(FWUPD_VM_UPDATES_DIR) diff --git a/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_download_updates.py b/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_download_updates.py new file mode 100644 index 0000000000000000000000000000000000000000..0844b6e7aceb92d6646edfbea1062b17091f30ca --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_download_updates.py @@ -0,0 +1,127 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# +import sys +import subprocess +import os + +from fwupd_common_vm import FwupdVmCommon + +FWUPD_VM_DIR = "/home/user/.cache/fwupd" +FWUPD_VM_UPDATES_DIR = os.path.join(FWUPD_VM_DIR, "updates") +FWUPD_VM_METADATA_DIR = os.path.join(FWUPD_VM_DIR, "metadata") +FWUPD_DOWNLOAD_PREFIX = "https://fwupd.org/downloads/" +METADATA_URL = "https://fwupd.org/downloads/firmware.xml.gz" +METADATA_URL_JCAT = "https://fwupd.org/downloads/firmware.xml.gz.jcat" + + +class DownloadData(FwupdVmCommon): + def _decrypt_update_url(self, url): + self.dec_url = url + if "--and--" in url: + self.dec_url = self.dec_url.replace("--and--", "&") + self.arch_name = "untrusted.cab" + if "--or--" in url: + self.dec_url = self.dec_url.replace("--or--", "|") + self.arch_name = "untrusted.cab" + if "--hash--" in url: + self.dec_url = self.dec_url.replace("--hash--", "#") + self.arch_name = "untrusted.cab" + if "%20" in url: + self.arch_name = "untrusted.cab" + + def _download_metadata_file(self): + """Download metadata file""" + if self.custom_url is None: + metadata_url = METADATA_URL + else: + metadata_url = self.custom_url + cmd_metadata = ["wget", "-P", FWUPD_VM_METADATA_DIR, metadata_url] + p = subprocess.Popen(cmd_metadata) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Downloading metadata file failed") + if not os.path.exists(self.metadata_file): + raise FileNotFoundError( + "fwupd-qubes: Downloaded metadata file does not exist" + ) + + def _download_metadata_jcat(self): + """Download metadata jcat signature""" + if self.custom_url is None: + metadata_url = METADATA_URL + else: + metadata_url = self.custom_url + cmd_metadata = ["wget", "-P", FWUPD_VM_METADATA_DIR, f"{metadata_url}.jcat"] + p = subprocess.Popen(cmd_metadata) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Downloading metadata file failed") + if not os.path.exists(f"{self.metadata_file}.jcat"): + raise FileNotFoundError( + "fwupd-qubes: Downloaded metadata file does not exist" + ) + + def download_metadata(self, url=None): + """Downloads default metadata and its signatures""" + if url is not None: + self.custom_url = url + custom_metadata_name = url.replace(FWUPD_DOWNLOAD_PREFIX, "") + self.metadata_file = os.path.join( + FWUPD_VM_METADATA_DIR, custom_metadata_name + ) + else: + self.custom_url = None + self.metadata_file = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz") + self.validate_vm_dirs() + self._download_metadata_file() + self._download_metadata_jcat() + + def download_updates(self, url, sha): + """ + Downloads update form given url + + Keyword argument: + url - url address of the update + """ + self.validate_vm_dirs() + self.arch_name = url.replace("https://fwupd.org/downloads/", "") + self._decrypt_update_url(url) + update_path = os.path.join(FWUPD_VM_UPDATES_DIR, self.arch_name) + cmd_update = ["wget", "-O", update_path, self.dec_url] + p = subprocess.Popen(cmd_update) + p.wait() + if p.returncode != 0: + raise Exception("fwupd-qubes: Downloading update file failed") + if not os.path.exists(update_path): + raise FileNotFoundError( + "fwupd-qubes: Downloaded update file does not exist" + ) + self.check_shasum(update_path, sha) + print("Update file downloaded successfully") + + +def main(): + url = None + sha = None + dn = DownloadData() + for arg in sys.argv: + if "--url=" in arg: + url = arg.replace("--url=", "") + if "--sha=" in arg: + sha = arg.replace("--sha=", "") + if "--metadata" in sys.argv: + dn.download_metadata(url=url) + elif url and sha: + dn.download_updates(url, sha) + else: + raise Exception("Invalid command!!!") + + +if __name__ == "__main__": + main() diff --git a/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_usbvm_validate.py b/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_usbvm_validate.py new file mode 100644 index 0000000000000000000000000000000000000000..973cd0f1485bb1a9d94786c3855e05e8170c98b7 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/src/vms/fwupd_usbvm_validate.py @@ -0,0 +1,127 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import glob +import os +import shutil +import subprocess +import sys + +from fwupd_common_vm import FwupdVmCommon + +FWUPD_VM_DIR = "/home/user/.cache/fwupd" +FWUPD_VM_UPDATES_DIR = os.path.join(FWUPD_VM_DIR, "updates") +FWUPD_VM_METADATA_DIR = os.path.join(FWUPD_VM_DIR, "metadata") +FWUPD_VM_METADATA_JCAT = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz.jcat") +FWUPD_VM_METADATA_FILE = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz") +FWUPDMGR = "/bin/fwupdmgr" +FWUPD_DOWNLOAD_PREFIX = "https://fwupd.org/downloads/" + + +class FwupdUsbvmUpdates(FwupdVmCommon): + def _verify_received(self, files_path, regex_pattern): + """Checks if sent files match regex filename pattern. + + Keyword arguments: + + files_path -- absolute path to inspected directory + regex_pattern -- pattern of the expected files + """ + for untrusted_f in os.listdir(files_path): + if not regex_pattern.match(untrusted_f): + raise Exception("Dom0 sent unexpected file") + f = untrusted_f + assert "/" not in f + assert "\0" not in f + assert "\x1b" not in f + path_f = os.path.join(files_path, f) + if os.path.islink(path_f) or not os.path.isfile(path_f): + raise Exception("Dom0 sent not regular file") + + def _extract_archive(self, archive_path, output_path): + """Extracts archive file to the specified directory. + + Keyword arguments: + archive_path -- absolute path to archive file + output_path -- absolute path to the output directory + """ + cmd_extract = ["gcab", "-x", f"--directory={output_path}", f"{archive_path}"] + p = subprocess.Popen(cmd_extract, stdout=subprocess.PIPE) + p.communicate()[0].decode("ascii") + if p.returncode != 0: + raise Exception("gcab: Error while extracting %s." % archive_path) + + def validate_metadata(self, metadata_url=None): + """Validates received the metadata files.""" + print("Running validation of the metadata files") + if metadata_url: + metadata_name = metadata_url.replace(FWUPD_DOWNLOAD_PREFIX, "") + metadata_file = os.path.join(FWUPD_VM_METADATA_DIR, metadata_name) + else: + metadata_file = FWUPD_VM_METADATA_FILE + try: + self._jcat_verification(f"{metadata_file}.jcat", FWUPD_VM_METADATA_DIR) + except Exception as e: + print(str(e), file=sys.stderr) + self.clean_vm_cache() + exit(1) + + def validate_updates(self, archive_path, sha): + """Validates received an update file. + + Keyword arguments: + archive_path - path to the firmware update archive + sha -- SHA256 checksum of the firmware update archive + """ + print("Running validation of the update archive") + self.check_shasum(archive_path, sha) + archive_name = archive_path.replace(f"{FWUPD_VM_UPDATES_DIR}/", "") + output_path = archive_path.replace(".cab", "") + arch_temp = os.path.join(output_path, archive_name) + os.mkdir(output_path) + shutil.copyfile(archive_path, arch_temp) + self._extract_archive(arch_temp, output_path) + signature_name = os.path.join(output_path, "firmware*.jcat") + file_path = glob.glob(signature_name) + try: + self._jcat_verification(file_path[0], output_path) + shutil.rmtree(output_path) + except Exception as e: + print(str(e), file=sys.stderr) + self.clean_vm_cache() + exit(1) + + +def main(): + f = FwupdUsbvmUpdates() + f_val = FwupdVmCommon() + metadata_url = None + if len(sys.argv) < 2: + raise Exception("Invalid number of arguments.") + for arg in sys.argv: + if "--url=" in arg: + metadata_url = arg.replace("--url=", "") + if sys.argv[1] == "metadata": + f.validate_metadata(metadata_url=metadata_url) + elif sys.argv[1] == "dirs": + f_val.validate_vm_dirs() + elif sys.argv[1] == "clean": + f.clean_vm_cache() + elif sys.argv[1] == "updates" and len(sys.argv) < 4: + raise Exception( + "Invalid number of arguments.\n" "Expected archive path and checksum." + ) + elif sys.argv[1] == "updates" and not len(sys.argv) < 4: + f.validate_updates(sys.argv[2], sys.argv[3]) + else: + raise Exception("Invalid command") + + +if __name__ == "__main__": + main() diff --git a/fwupd-1.8.6/contrib/qubes/test/__init__.py b/fwupd-1.8.6/contrib/qubes/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/contrib/qubes/test/fwupd_logs.py b/fwupd-1.8.6/contrib/qubes/test/fwupd_logs.py new file mode 100644 index 0000000000000000000000000000000000000000..fc876bb1685d495dcdf7d2eaf116481f5aeb49ed --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/fwupd_logs.py @@ -0,0 +1,850 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +UPDATE_INFO = """{ + "Devices" : [ + { + "Name" : "ColorHug2", + "DeviceId" : "b0a78eb71f4eeea7df8fb114522556ba8ce22074", + "Guid" : [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad", + "aa4b4156-9732-55db-9500-bf6388508ee3", + "101ee86a-7bea-59fb-9f89-6b6297ceed3b", + "2fa8891f-3ece-53a4-adc4-0dd875685f30" + ], + "Summary" : "An open source display colorimeter", + "Plugin" : "colorhug", + "Protocol" : "com.hughski.colorhug", + "Flags" : [ + "updatable", + "supported", + "registered", + "self-recovery", + "add-counterpart-guids" + ], + "Vendor" : "Hughski Ltd.", + "VendorId" : "USB:0x273F", + "Version" : "2.0.6", + "VersionFormat" : "triplet", + "Icons" : [ + "colorimeter-colorhug" + ], + "InstallDuration" : 8, + "Created" : 1614224175, + "Releases" : [ + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

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

    ", + "Version" : "2.0.7", + "Filename" : "hughski-colorhug2-2.0.7.cab", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "80bddeb898cda5b87d9837e13a9ace19846053bf", + "32c4a2c9be787cdf1d757c489d6455bd7bb14053425180b6d331c37e1ccc1cda" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1482901200, + "Locations" : [ + "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "ipfs://QmUByRuHG9Gb2s8gKKVqDcjhUrn8vy62B4WqjbpWDD42cf" + ], + "Uri" : "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-upgrade" + ], + "InstallDuration" : 8 + } + ] + } + ] + } +""" + +DMI_DECODE = """# dmidecode 3.1 +Getting SMBIOS data from sysfs. +SMBIOS 3.1.1 present. + +Handle 0x0000, DMI type 0, 26 bytes +BIOS Information + Vendor: Dell Inc. + Version: P1.00 + Release Date: 02/09/2018 + Address: 0xF0000 + Runtime Size: 64 kB + ROM Size: 16 MB + Characteristics: + PCI is supported + BIOS is upgradeable + BIOS shadowing is allowed + Boot from CD is supported + Selectable boot is supported + BIOS ROM is socketed + EDD is supported + 5.25"/1.2 MB floppy services are supported (int 13h) + 3.5"/720 kB floppy services are supported (int 13h) + 3.5"/2.88 MB floppy services are supported (int 13h) + Print screen service is supported (int 5h) + 8042 keyboard services are supported (int 9h) + Serial services are supported (int 14h) + Printer services are supported (int 17h) + ACPI is supportedUSB legacy is supported + BIOS boot specification is supported + Targeted content distribution is supported + UEFI is supported + BIOS Revision: 5.13 +""" + +GET_DEVICES = """{ + "Devices" : [ + { + "Name" : "ColorHug2", + "DeviceId" : "cf294bf55b333004beb7c41f952c1838c23e1f4a", + "Guid" : [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad", + "aa4b4156-9732-55db-9500-bf6388508ee3", + "101ee86a-7bea-59fb-9f89-6b6297ceed3b", + "2fa8891f-3ece-53a4-adc4-0dd875685f30" + ], + "Summary" : "An open source display colorimeter", + "Plugin" : "colorhug", + "Protocol" : "com.hughski.colorhug", + "Flags" : [ + "updatable", + "supported", + "registered", + "self-recovery", + "add-counterpart-guids" + ], + "Vendor" : "Hughski Ltd.", + "VendorId" : "USB:0x273F", + "Version" : "2.0.6", + "VersionFormat" : "triplet", + "Icons" : [ + "colorimeter-colorhug" + ], + "InstallDuration" : 8, + "Created" : 1614246373, + "Releases" : [ + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

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

    ", + "Version" : "2.0.7", + "Filename" : "hughski-colorhug2-2.0.7.cab", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "80bddeb898cda5b87d9837e13a9ace19846053bf", + "32c4a2c9be787cdf1d757c489d6455bd7bb14053425180b6d331c37e1ccc1cda" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1482901200, + "Locations" : [ + "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "ipfs://QmUByRuHG9Gb2s8gKKVqDcjhUrn8vy62B4WqjbpWDD42cf" + ], + "Uri" : "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-upgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on the second half of batch 16
    • Fix the firmware upgrade process using new versions of fwupd
    ", + "Version" : "2.0.6", + "Filename" : "hughski-colorhug2-2.0.6.cab", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "60e28bb402b427dbce19e150d63987f5e18c1880", + "a646b1798ce7f5ac26229aa85c35cc4f44a5bd8bfc9e5332a8ec815aef075566" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1450792062, + "Locations" : [ + "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "ipfs://QmdWFrYo1YJxgGU37Qy7LkwPQM26vPMVxLRANUga6TzSjW" + ], + "Uri" : "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on batch 16
    • Make the self test more sensitive to detect floating pins
    ", + "Version" : "2.0.5", + "Filename" : "hughski-colorhug2-2.0.5.cab", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "e37b9d360d61157657335d80585a005ff2593108", + "8cd379eb2e1467e4fda92c20650306dc7e598b1d421841bbe19d9ed6ea01e3ee" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1444059405, + "Locations" : [ + "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + "ipfs://QmQ648kwvv52wuqPoKjm5zLGXngQnmuJzp1xtJmTEbzgz5" + ], + "Uri" : "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This unstable release adds the following features:

    • Add TakeReadingArray to enable panel latency measurements
    • Speed up the auto-scaled measurements considerably, using 256ms as the smallest sample duration
    ", + "Version" : "2.0.2", + "Filename" : "hughski-colorhug2-2.0.2.cab", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "1b43bd71bbed2cf0e9c9efcca79799f07b3d0dd2", + "c09674fb818d4a1033dbde2fab5885716aed1d8b751b428f16687a78f2a4d61f" + ], + "License" : "GPL-2.0+", + "Size" : 15680, + "Created" : 1416675439, + "Locations" : [ + "https://fwupd.org/downloads/30a121f26c039745aeb5585252d4a9b5386d71cb-hughski-colorhug2-2.0.2.cab", + "ipfs://QmZ1DKKsWZQuvnff2DJTDJESMaXTpsc5zfNGX7Sb2HibAn" + ], + "Uri" : "https://fwupd.org/downloads/30a121f26c039745aeb5585252d4a9b5386d71cb-hughski-colorhug2-2.0.2.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + } + ] + }, + { + "Name" : "Display controller", + "DeviceId" : "ecf0d22adf39a244a723466378a8884aa22b7e78", + "Guid" : [ + "e358a53d-98bc-5565-b55e-7df8e0d06c5e", + "7365091f-756a-5c83-878c-edd1120ca718", + "06208e9f-1dd0-5857-b700-3d77525793aa", + "af9ff5a0-c613-5da3-bab8-5d411adebbca" + ], + "Plugin" : "optionrom", + "Flags" : [ + "internal", + "registered", + "can-verify", + "can-verify-image" + ], + "VendorId" : "PCI:0x1234", + "Version" : "02", + "VersionFormat" : "plain", + "Created" : 1614209932 + }, + { + "Name" : "Intel(R) Core™ i7-7700HQ CPU @ 2.80GHz", + "DeviceId" : "4bde70ba4e39b28f9eab1628f9dd6e6244c03027", + "Guid" : [ + "b9a2dd81-159e-5537-a7db-e7101d164d3f", + "30249f37-d140-5d3e-9319-186b1bd5cac3", + "809a0b93-8a12-5338-a571-ad5583acf896", + "d0f754d5-1395-5573-bc83-85ba955da70a" + ], + "Plugin" : "cpu", + "Flags" : [ + "internal", + "registered" + ], + "Vendor" : "Intel", + "Version" : "0x000000de", + "VersionFormat" : "hex", + "VersionRaw" : 222, + "Icons" : [ + "computer" + ], + "Created" : 1614209932 + } + ] +} +""" + +GET_DEVICES_NO_UPDATES = """{ + "Devices" : [ + { + "Name" : "ColorHug2", + "DeviceId" : "203f56e4e186d078ce76725e708400aafc253aac", + "Guid" : [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad", + "aa4b4156-9732-55db-9500-bf6388508ee3", + "101ee86a-7bea-59fb-9f89-6b6297ceed3b", + "2fa8891f-3ece-53a4-adc4-0dd875685f30" + ], + "Summary" : "An open source display colorimeter", + "Plugin" : "colorhug", + "Protocol" : "com.hughski.colorhug", + "Flags" : [ + "updatable", + "supported", + "registered", + "self-recovery", + "add-counterpart-guids" + ], + "Vendor" : "Hughski Ltd.", + "VendorId" : "USB:0x273F", + "Version" : "2.0.7", + "VersionFormat" : "triplet", + "Icons" : [ + "colorimeter-colorhug" + ], + "InstallDuration" : 8, + "Created" : 1592916092, + "Releases" : [ + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

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

    ", + "Version" : "2.0.7", + "Filename" : "658851e6f27c4d87de19cd66b97b610d100efe09", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "490be5c0b13ca4a3f169bf8bc682ba127b8f7b96" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1482901200, + "Uri" : "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on the second half of batch 16
    • Fix the firmware upgrade process using new versions of fwupd
    ", + "Version" : "2.0.6", + "Filename" : "f038b5ca40e6d7c1c0299a9e1dcc129d5f6371b6", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "03c9c14db1894a00035ececcfae192865a710e52" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1450792062, + "Uri" : "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on batch 16
    • Make the self test more sensitive to detect floating pins
    ", + "Version" : "2.0.5", + "Filename" : "ae76c6b704b60f9d1d88dc2c8ec8a62d7b2331dc", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "4ee9dfa38df3b810f739d8a19d13da1b3175fb87" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1444059405, + "Uri" : "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This unstable release adds the following features:

    • Add TakeReadingArray to enable panel latency measurements
    • Speed up the auto-scaled measurements considerably, using 256ms as the smallest sample duration
    ", + "Version" : "2.0.2", + "Filename" : "d4b3144daeb2418634f9d464d88d55590bcd9ac7", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "448527af3ce019d03dbb77aaebaa7eb893f1ea20" + ], + "License" : "GPL-2.0+", + "Size" : 15680, + "Created" : 1416675439, + "Uri" : "https://fwupd.org/downloads/30a121f26c039745aeb5585252d4a9b5386d71cb-hughski-colorhug2-2.0.2.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + } + ] + }, + { + "Name" : "GP106 [GeForce GTX 1060 6GB]", + "DeviceId" : "71b677ca0f1bc2c5b804fa1d59e52064ce589293", + "Guid" : [ + "b080a9ba-fff8-5de0-b641-26f782949f94", + "f95bfce3-18e4-58b0-bd81-136457521383" + ], + "Plugin" : "optionrom", + "Flags" : [ + "internal", + "registered", + "can-verify", + "can-verify-image" + ], + "Vendor" : "NVIDIA Corporation", + "VendorId" : "PCI:0x10DE", + "Version" : "a1", + "VersionFormat" : "plain", + "Created" : 1592899254 + }, + { + "Name" : "Intel(R) Core™ i5-8400 CPU @ 2.80GHz", + "DeviceId" : "4bde70ba4e39b28f9eab1628f9dd6e6244c03027", + "Guid" : [ + "b9a2dd81-159e-5537-a7db-e7101d164d3f" + ], + "Plugin" : "cpu", + "Flags" : [ + "internal", + "registered" + ], + "Vendor" : "GenuineIntel", + "Version" : "0xd6", + "VersionFormat" : "hex", + "Icons" : [ + "computer" + ], + "Created" : 1592899249 + }, + { + "Name" : "SSDPR-CX400-256", + "DeviceId" : "948241a24320627284597ec95079cc1341c90518", + "Guid" : [ + "09fa3842-45bc-5226-a8ec-1668fc61f88f", + "57d6b2ff-710d-5cd2-98be-4f6b8b7c5287", + "36bebd37-b680-5d56-83a1-6693033d4098" + ], + "Summary" : "ATA Drive", + "Plugin" : "ata", + "Protocol" : "org.t13.ata", + "Flags" : [ + "internal", + "updatable", + "require-ac", + "registered", + "needs-reboot", + "usable-during-update" + ], + "Vendor" : "Phison", + "VendorId" : "ATA:0x1987", + "Version" : "SBFM61.3", + "VersionFormat" : "plain", + "Icons" : [ + "drive-harddisk" + ], + "Created" : 1592899254 + } + ] +} +""" + + +GET_DEVICES_NO_VERSION = """{ + "Devices" : [ + { + "Name" : "ColorHug2", + "DeviceId" : "203f56e4e186d078ce76725e708400aafc253aac", + "Guid" : [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad", + "aa4b4156-9732-55db-9500-bf6388508ee3", + "101ee86a-7bea-59fb-9f89-6b6297ceed3b", + "2fa8891f-3ece-53a4-adc4-0dd875685f30" + ], + "Summary" : "An open source display colorimeter", + "Plugin" : "colorhug", + "Protocol" : "com.hughski.colorhug", + "Flags" : [ + "updatable", + "supported", + "registered", + "self-recovery", + "add-counterpart-guids" + ], + "Vendor" : "Hughski Ltd.", + "VendorId" : "USB:0x273F", + "VersionFormat" : "triplet", + "Icons" : [ + "colorimeter-colorhug" + ], + "InstallDuration" : 8, + "Created" : 1592916092, + "Releases" : [ + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

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

    ", + "Version" : "2.0.7", + "Filename" : "658851e6f27c4d87de19cd66b97b610d100efe09", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "490be5c0b13ca4a3f169bf8bc682ba127b8f7b96" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1482901200, + "Uri" : "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on the second half of batch 16
    • Fix the firmware upgrade process using new versions of fwupd
    ", + "Filename" : "f038b5ca40e6d7c1c0299a9e1dcc129d5f6371b6", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "03c9c14db1894a00035ececcfae192865a710e52" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1450792062, + "Uri" : "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on batch 16
    • Make the self test more sensitive to detect floating pins
    ", + "Version" : "2.0.5", + "Filename" : "ae76c6b704b60f9d1d88dc2c8ec8a62d7b2331dc", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "4ee9dfa38df3b810f739d8a19d13da1b3175fb87" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1444059405, + "Uri" : "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This unstable release adds the following features:

    • Add TakeReadingArray to enable panel latency measurements
    • Speed up the auto-scaled measurements considerably, using 256ms as the smallest sample duration
    ", + "Version" : "2.0.2", + "Filename" : "d4b3144daeb2418634f9d464d88d55590bcd9ac7", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "448527af3ce019d03dbb77aaebaa7eb893f1ea20" + ], + "License" : "GPL-2.0+", + "Size" : 15680, + "Created" : 1416675439, + "Uri" : "https://fwupd.org/downloads/30a121f26c039745aeb5585252d4a9b5386d71cb-hughski-colorhug2-2.0.2.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + } + ] + }, + { + "Name" : "ColorHug2", + "DeviceId" : "203f56e4e186d078ce76725e708400aafc253aac", + "Guid" : [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad", + "aa4b4156-9732-55db-9500-bf6388508ee3", + "101ee86a-7bea-59fb-9f89-6b6297ceed3b", + "2fa8891f-3ece-53a4-adc4-0dd875685f30" + ], + "Summary" : "An open source display colorimeter", + "Plugin" : "colorhug", + "Protocol" : "com.hughski.colorhug", + "Flags" : [ + "updatable", + "supported", + "registered", + "self-recovery", + "add-counterpart-guids" + ], + "Vendor" : "Hughski Ltd.", + "Version" : "2.0.6", + "VendorId" : "USB:0x273F", + "VersionFormat" : "triplet", + "Icons" : [ + "colorimeter-colorhug" + ], + "InstallDuration" : 8, + "Created" : 1592916092, + "Releases" : [ + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

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

    ", + "Version" : "2.0.7", + "Filename" : "658851e6f27c4d87de19cd66b97b610d100efe09", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "490be5c0b13ca4a3f169bf8bc682ba127b8f7b96" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1482901200, + "Uri" : "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on the second half of batch 16
    • Fix the firmware upgrade process using new versions of fwupd
    ", + "Version" : "2.0.6", + "Filename" : "f038b5ca40e6d7c1c0299a9e1dcc129d5f6371b6", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "03c9c14db1894a00035ececcfae192865a710e52" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1450792062, + "Uri" : "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This stable release fixes the following problems:

    • Fix the swapped LEDs on batch 16
    • Make the self test more sensitive to detect floating pins
    ", + "Version" : "2.0.5", + "Filename" : "ae76c6b704b60f9d1d88dc2c8ec8a62d7b2331dc", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "4ee9dfa38df3b810f739d8a19d13da1b3175fb87" + ], + "License" : "GPL-2.0+", + "Size" : 16384, + "Created" : 1444059405, + "Uri" : "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + }, + { + "AppstreamId" : "com.hughski.ColorHug2.firmware", + "RemoteId" : "lvfs", + "Summary" : "Firmware for the Hughski ColorHug2 Colorimeter", + "Description" : "

    This unstable release adds the following features:

    • Add TakeReadingArray to enable panel latency measurements
    • Speed up the auto-scaled measurements considerably, using 256ms as the smallest sample duration
    ", + "Version" : "2.0.2", + "Filename" : "d4b3144daeb2418634f9d464d88d55590bcd9ac7", + "Protocol" : "com.hughski.colorhug", + "Checksum" : [ + "448527af3ce019d03dbb77aaebaa7eb893f1ea20" + ], + "License" : "GPL-2.0+", + "Size" : 15680, + "Created" : 1416675439, + "Uri" : "https://fwupd.org/downloads/30a121f26c039745aeb5585252d4a9b5386d71cb-hughski-colorhug2-2.0.2.cab", + "Homepage" : "http://www.hughski.com/", + "SourceUrl" : "https://github.com/hughski/colorhug2-firmware", + "Vendor" : "Hughski Limited", + "Flags" : [ + "is-downgrade" + ], + "InstallDuration" : 8 + } + ] + }, + { + "Name" : "GP106 [GeForce GTX 1060 6GB]", + "DeviceId" : "71b677ca0f1bc2c5b804fa1d59e52064ce589293", + "Guid" : [ + "b080a9ba-fff8-5de0-b641-26f782949f94", + "f95bfce3-18e4-58b0-bd81-136457521383" + ], + "Plugin" : "optionrom", + "Flags" : [ + "internal", + "registered", + "can-verify", + "can-verify-image" + ], + "Vendor" : "NVIDIA Corporation", + "VendorId" : "PCI:0x10DE", + "VersionFormat" : "plain", + "Created" : 1592899254 + }, + { + "Name" : "Intel(R) Core™ i5-8400 CPU @ 2.80GHz", + "DeviceId" : "4bde70ba4e39b28f9eab1628f9dd6e6244c03027", + "Guid" : [ + "b9a2dd81-159e-5537-a7db-e7101d164d3f" + ], + "Plugin" : "cpu", + "Flags" : [ + "internal", + "registered" + ], + "Vendor" : "GenuineIntel", + "Version" : "0xd6", + "VersionFormat" : "hex", + "Icons" : [ + "computer" + ], + "Created" : 1592899249 + }, + { + "Name" : "SSDPR-CX400-256", + "DeviceId" : "948241a24320627284597ec95079cc1341c90518", + "Guid" : [ + "09fa3842-45bc-5226-a8ec-1668fc61f88f", + "57d6b2ff-710d-5cd2-98be-4f6b8b7c5287", + "36bebd37-b680-5d56-83a1-6693033d4098" + ], + "Summary" : "ATA Drive", + "Plugin" : "ata", + "Protocol" : "org.t13.ata", + "Flags" : [ + "internal", + "updatable", + "require-ac", + "registered", + "needs-reboot", + "usable-during-update" + ], + "Vendor" : "Phison", + "VendorId" : "ATA:0x1987", + "Version" : "SBFM61.3", + "VersionFormat" : "plain", + "Icons" : [ + "drive-harddisk" + ], + "Created" : 1592899254 + } + ] +} +""" + +HEADS_XML = """ + + + com.3mdeb.heads.x230.firmware + Heads x230 System Update + x230 heads system firmware + +

    x230 heads system firmware

    +
    + + 596c3466-0506-5ca5-a68f-dc34532a93d3 + + http://osresearch.net/ + CC0-1.0 + GPLv2 + coreboot + + X-System + + + + https://fwupd.org/downloads/e747a435bf24fd6081b77b6704b39cec5fa2dcf62e0ca6b86d8a6460121a1d07-heads_coreboot_x230-v0_2_3.cab + 1a54e69ca2b58d1218035115d481480eaf4c66e4 + ba519a7a5d8136c8ade0cf0c775c58f3165f42798ff631c3f57f075897ef1586 + 76373f1b5a157b6563d3605271472901b03f57f3 + 9a9c5dbd3faf90ff7a1f4c9be8d71c4db93dd69fa690f8722fec19c5a51aed9e + +

    Fixes flash-gui issue.

    +
    + 12582912 + 12591670 +
    + + https://fwupd.org/downloads/1a0f0ad487a40bb27a49db55e256a207a33dac92c5c53761501c9fb89e4fd115-heads_coreboot_x230-v0_2_2.cab + 58e85d012ad1d5c6f98e8fe65202b4d6c8a6ec03 + 94430160d35cf74adf29c7fc1490b44497e1a3f0fff72733efe2982c61c9a772 + 8e97ce38396e281fcf9a5a248819925a2fa04265 + a6774661407622f345bf0ac2f113540507f0288bb97bf5dba586059c0653f659 + +

    Lenovo x230 heads system firmware

    +
    + 12582912 + 12591680 +
    +
    +
    +
    +""" diff --git a/fwupd-1.8.6/contrib/qubes/test/logs/firmware.metainfo.xml b/fwupd-1.8.6/contrib/qubes/test/logs/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..fbb384f6160786061ba447a17a80065d8c41041c --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/logs/firmware.metainfo.xml @@ -0,0 +1,44 @@ + + + com.dell.uefi6180aaaa.firmware + Latitude 7390 2-in-1 + Firmware for the Dell Latitude 7390 2-in-1 + +

    Updating the system firmware improves performance.

    +
    + + 6180aaaa-5529-4bbf-b4fd-65b4f788de5b + + http://support.dell.com/ + CC0-1.0 + proprietary + Dell Inc. + + X-System + + + quad + dell-bios + org.uefi.capsule + + + + b03252481573f600c7f530d7a4c24bd62c810412 + bd866bcd2b5964da4b20d3f57128746efb7afefe648cf7284f2fae399225f1ac + +

    This stable release fixes the following issues:

    +
      +
    • Firmware updates to address the Intel Security Advisories.
    • +
    • Enhanced the system firmware auto recovery function when system firmware does not work.
    • +
    • Updated the BIOS warning message that is displayed when an AC adapter with low wattage is connected to the system.
    • +
    • Updated the Intel CPU Microcode.
    • +
    +
    + 14699068 + + CVE-2019-14607 + CVE-2019-11157 + +
    +
    +
    diff --git a/fwupd-1.8.6/contrib/qubes/test/logs/get_devices.log b/fwupd-1.8.6/contrib/qubes/test/logs/get_devices.log new file mode 100644 index 0000000000000000000000000000000000000000..5195522d87c14263f1e1421b36dac97cd3355bd5 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/logs/get_devices.log @@ -0,0 +1,22 @@ +====================================================================== +Dom0 Devices: +====================================================================== + ColorHug2 +====================================================================== + DeviceId: b0a78eb71f4eeea7df8fb114522556ba8ce22074 + Guid: ·2082b5e0-7a64-478a-b1b2-e3404fab6dad + ·aa4b4156-9732-55db-9500-bf6388508ee3 + ·101ee86a-7bea-59fb-9f89-6b6297ceed3b + ·2fa8891f-3ece-53a4-adc4-0dd875685f30 + Summary: An open source display colorimeter + Plugin: colorhug + Protocol: com.hughski.colorhug + Flags: ·updatable + ·supported + ·registered + ·self-recovery + ·add-counterpart-guids + Vendor: Hughski Ltd. + VendorId: USB:0x273F + Version: 2.0.6 + Created: 1614224175 diff --git a/fwupd-1.8.6/contrib/qubes/test/logs/get_updates.log b/fwupd-1.8.6/contrib/qubes/test/logs/get_updates.log new file mode 100644 index 0000000000000000000000000000000000000000..3986f594444d3e56a14962c3045f7cf82ba74f7a --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/logs/get_updates.log @@ -0,0 +1,15 @@ +====================================================== +sys-usb updates: +====================================================== +Available updates: +====================================================== +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1. Device: ColorHug2 + Current firmware version: 2.0.6 +====================================================== + Firmware update version: 2.0.7 + URL: https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab + SHA256 checksum: 32c4a2c9be787cdf1d757c489d6455bd7bb14053425180b6d331c37e1ccc1cda + Description: This release fixes prevents the firmware returning an error when the remote SHA1 hash was never sent. + +====================================================== diff --git a/fwupd-1.8.6/contrib/qubes/test/logs/help.log b/fwupd-1.8.6/contrib/qubes/test/logs/help.log new file mode 100644 index 0000000000000000000000000000000000000000..193934e61b823bf18fdcfba3fa999581648611e8 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/logs/help.log @@ -0,0 +1,25 @@ +====================================================================== +Usage: +====================================================================== + Command: qubes-fwupdmgr [OPTION…][FLAG..] + Example: qubes-fwupdmgr refresh --whonix --url= + +Options: +====================================================================== + get-devices: Get all devices that support firmware updates + get-updates: Get the list of updates for connected hardware + refresh: Refresh metadata from remote server + update: Update chosen device to latest firmware version + update-heads: Updates heads firmware to the latest version + downgrade: Downgrade chosen device to chosen firmware version + clean: Delete all cached update files + +Flags: +====================================================================== + --whonix: Download firmware updates via Tor + --device: Specify device for heads update (default - x230) + --url: Address of the custom metadata remote server + +Help: +====================================================================== + -h --help: Show help options diff --git a/fwupd-1.8.6/contrib/qubes/test/logs/metainfo_name/firmware.metainfo.xml b/fwupd-1.8.6/contrib/qubes/test/logs/metainfo_name/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..29cb95e4972cb0c0b000fb9bcfda7363cf8864ff --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/logs/metainfo_name/firmware.metainfo.xml @@ -0,0 +1,44 @@ + + + com.dell.uefi6180aaaa.firmware + Latitude 7390 2-in-1 + Firmware for the Dell Latitude 7390 2-in-1 + +

    Updating the system firmware improves performance.

    +
    + + 6180aaaa-5529-4bbf-b4fd-65b4f788de5b + + http://support.dell.com/ + CC0-1.0 + proprietary + Wrong name + + X-System + + + quad + dell-bios + org.uefi.capsule + + + + b03252481573f600c7f530d7a4c24bd62c810412 + bd866bcd2b5964da4b20d3f57128746efb7afefe648cf7284f2fae399225f1ac + +

    This stable release fixes the following issues:

    +
      +
    • Firmware updates to address the Intel Security Advisories.
    • +
    • Enhanced the system firmware auto recovery function when system firmware does not work.
    • +
    • Updated the BIOS warning message that is displayed when an AC adapter with low wattage is connected to the system.
    • +
    • Updated the Intel CPU Microcode.
    • +
    +
    + 14699068 + + CVE-2019-14607 + CVE-2019-11157 + +
    +
    +
    diff --git a/fwupd-1.8.6/contrib/qubes/test/logs/metainfo_version/firmware.metainfo.xml b/fwupd-1.8.6/contrib/qubes/test/logs/metainfo_version/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..f59ace4ed11c3977273b00b9f59ba2f9f4b37ac8 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/logs/metainfo_version/firmware.metainfo.xml @@ -0,0 +1,44 @@ + + + com.dell.uefi6180aaaa.firmware + Latitude 7390 2-in-1 + Firmware for the Dell Latitude 7390 2-in-1 + +

    Updating the system firmware improves performance.

    +
    + + 6180aaaa-5529-4bbf-b4fd-65b4f788de5b + + http://support.dell.com/ + CC0-1.0 + proprietary + Dell Inc. + + X-System + + + quad + dell-bios + org.uefi.capsule + + + + b03252481573f600c7f530d7a4c24bd62c810412 + bd866bcd2b5964da4b20d3f57128746efb7afefe648cf7284f2fae399225f1ac + +

    This stable release fixes the following issues:

    +
      +
    • Firmware updates to address the Intel Security Advisories.
    • +
    • Enhanced the system firmware auto recovery function when system firmware does not work.
    • +
    • Updated the BIOS warning message that is displayed when an AC adapter with low wattage is connected to the system.
    • +
    • Updated the Intel CPU Microcode.
    • +
    +
    + 14699068 + + CVE-2019-14607 + CVE-2019-11157 + +
    +
    +
    diff --git a/fwupd-1.8.6/contrib/qubes/test/test_qubes_fwupd_heads.py b/fwupd-1.8.6/contrib/qubes/test/test_qubes_fwupd_heads.py new file mode 100644 index 0000000000000000000000000000000000000000..aacc544e27e19bb44c1aa1b1b146b998aa2be616 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/test_qubes_fwupd_heads.py @@ -0,0 +1,121 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import io +import os +import platform +import shutil +import imp +import src.qubes_fwupd_heads as qf_heads +import sys +import unittest + +from test.fwupd_logs import HEADS_XML + +CUSTOM_METADATA = "https://fwupd.org/downloads/firmware-3c81bfdc9db5c8a42c09d38091944bc1a05b27b0.xml.gz" +QUBES_FWUPDMGR_REPO = "./src/qubes_fwupdmgr.py" +QUBES_FWUPDMGR_BINDIR = "/usr/sbin/qubes-fwupdmgr" + + +class TestQubesFwupdHeads(unittest.TestCase): + def setUp(self): + if os.path.exists(QUBES_FWUPDMGR_REPO): + self.qfwupd = imp.load_source("qubes_fwupdmgr", QUBES_FWUPDMGR_REPO) + elif os.path.exists(QUBES_FWUPDMGR_BINDIR): + self.qfwupd = imp.load_source("qubes_fwupdmgr", QUBES_FWUPDMGR_BINDIR) + self.q = qf_heads.FwupdHeads() + self.maxDiff = 2000 + self.captured_output = io.StringIO() + sys.stdout = self.captured_output + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_get_hwids(self): + self.q._get_hwids() + self.assertNotEqual(self.q.dom0_hwids_info, "") + + def test_gather_firmware_version_empty(self): + self.q.dom0_hwids_info = "" + return_code = self.q._gather_firmware_version() + self.assertEqual(return_code, self.qfwupd.EXIT_CODES["NOTHING_TO_DO"]) + + def test_gather_firmware_version(self): + self.q.dom0_hwids_info = "CBET4000 Heads-v0.2.2-917-g19f0e65" + self.q._gather_firmware_version() + self.assertEqual(self.q.heads_version, "0.2.2-917-g19f0e65") + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_parse_metadata(self): + qmgr = self.qfwupd.QubesFwupdmgr() + qmgr.metadata_file = CUSTOM_METADATA.replace( + "https://fwupd.org/downloads", self.qfwupd.FWUPD_DOM0_METADATA_DIR + ) + qmgr._download_metadata(metadata_url=CUSTOM_METADATA) + self.q._parse_metadata(qmgr.metadata_file) + self.assertTrue(self.q.metadata_info) + + def test_check_heads_updates_default_heads(self): + self.q.metadata_info = HEADS_XML + self.q.heads_version = "heads" + return_code = self.q._parse_heads_updates("x230") + self.assertEqual(return_code, self.qfwupd.EXIT_CODES["SUCCESS"]) + self.assertEqual( + self.q.heads_update_url, + "https://fwupd.org/downloads/e747a435bf24fd6081b77b6704b39cec5fa2dcf62e0ca6b86d8a6460121a1d07-heads_coreboot_x230-v0_2_3.cab", + ) + self.assertEqual( + self.q.heads_update_sha, + "ba519a7a5d8136c8ade0cf0c775c58f3165f42798ff631c3f57f075897ef1586", + ) + self.assertEqual(self.q.heads_update_version, "0.2.3") + + def test_check_heads_updates_no_updates(self): + self.q.metadata_info = HEADS_XML + self.q.heads_version = "0.2.3" + return_code = self.q._parse_heads_updates("x230") + self.assertEqual(return_code, self.qfwupd.EXIT_CODES["NOTHING_TO_DO"]) + + def test_check_heads_updates_lower_version(self): + self.q.metadata_info = HEADS_XML + self.q.heads_version = "0.2.2" + return_code = self.q._parse_heads_updates("x230") + self.assertEqual(return_code, self.qfwupd.EXIT_CODES["SUCCESS"]) + self.assertEqual( + self.q.heads_update_url, + "https://fwupd.org/downloads/e747a435bf24fd6081b77b6704b39cec5fa2dcf62e0ca6b86d8a6460121a1d07-heads_coreboot_x230-v0_2_3.cab", + ) + self.assertEqual( + self.q.heads_update_sha, + "ba519a7a5d8136c8ade0cf0c775c58f3165f42798ff631c3f57f075897ef1586", + ) + self.assertEqual(self.q.heads_update_version, "0.2.3") + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_copy_heads_firmware(self): + qmgr = self.qfwupd.QubesFwupdmgr() + self.q.heads_update_url = "https://fwupd.org/downloads/e747a435bf24fd6081b77b6704b39cec5fa2dcf62e0ca6b86d8a6460121a1d07-heads_coreboot_x230-v0_2_3.cab" + self.q.heads_update_sha = ( + "ba519a7a5d8136c8ade0cf0c775c58f3165f42798ff631c3f57f075897ef1586" + ) + self.q.heads_update_version = "0.2.3" + qmgr._download_firmware_updates( + self.q.heads_update_url, self.q.heads_update_sha + ) + heads_boot_path = os.path.join( + qf_heads.HEADS_UPDATES_DIR, self.q.heads_update_version + ) + if os.path.exists(heads_boot_path): + shutil.rmtree(heads_boot_path) + ret_code = self.q._copy_heads_firmware(qmgr.arch_path) + self.assertNotEqual(ret_code, self.qfwupd.EXIT_CODES["NOTHING_TO_DO"]) + firmware_path = os.path.join(heads_boot_path, "firmware.rom") + self.assertTrue(os.path.exists(firmware_path)) + + +if __name__ == "__main__": + unittest.main() diff --git a/fwupd-1.8.6/contrib/qubes/test/test_qubes_fwupdmgr.py b/fwupd-1.8.6/contrib/qubes/test/test_qubes_fwupdmgr.py new file mode 100755 index 0000000000000000000000000000000000000000..ec9e42b58c8e461558567fe89993f019f629fa10 --- /dev/null +++ b/fwupd-1.8.6/contrib/qubes/test/test_qubes_fwupdmgr.py @@ -0,0 +1,873 @@ +#!/usr/bin/python3 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2021 Norbert Kamiński +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import json +import unittest +import os +import subprocess +import sys +import imp +import io +import platform +from packaging.version import Version +from pathlib import Path +from test.fwupd_logs import UPDATE_INFO, GET_DEVICES, DMI_DECODE +from test.fwupd_logs import GET_DEVICES_NO_UPDATES, GET_DEVICES_NO_VERSION +from unittest.mock import patch + + +QUBES_FWUPDMGR_REPO = "./src/qubes_fwupdmgr.py" +QUBES_FWUPDMGR_BINDIR = "/usr/sbin/qubes-fwupdmgr" + +if os.path.exists(QUBES_FWUPDMGR_REPO): + qfwupd = imp.load_source("qubes_fwupdmgr", QUBES_FWUPDMGR_REPO) +elif os.path.exists(QUBES_FWUPDMGR_BINDIR): + qfwupd = imp.load_source("qubes_fwupdmgr", QUBES_FWUPDMGR_BINDIR) + +FWUPD_DOM0_DIR = "/root/.cache/fwupd" +FWUPD_DOM0_UPDATES_DIR = os.path.join(FWUPD_DOM0_DIR, "updates") +FWUPD_DOM0_UNTRUSTED_DIR = os.path.join(FWUPD_DOM0_UPDATES_DIR, "untrusted") +FWUPD_VM_LOG = os.path.join(FWUPD_DOM0_DIR, "usbvm-devices.log") +FWUPD_DOM0_METADATA_DIR = os.path.join(FWUPD_DOM0_DIR, "metadata") +FWUPD_DOM0_METADATA_FILE = os.path.join(FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz") +FWUPD_DOM0_METADATA_FILE_JCAT = os.path.join(FWUPD_DOM0_METADATA_DIR, "firmware.xml.gz") +FWUPD_VM_DIR = "/home/user/.cache/fwupd" +FWUPD_VM_UPDATES_DIR = os.path.join(FWUPD_VM_DIR, "updates") +FWUPD_VM_METADATA_DIR = os.path.join(FWUPD_VM_DIR, "metadata") +FWUPD_VM_METADATA_FILE = os.path.join(FWUPD_VM_METADATA_DIR, "firmware.xml.gz") +FWUPD_VM_METADATA_FILE_JCAT = os.path.join( + FWUPD_VM_METADATA_DIR, "firmware.xml.gz.jcat" +) +REQUIRED_DEV = "Requires device not connected" +REQUIRED_USBVM = "Requires sys-usb" +XL_LIST_LOG = "Name ID Mem VCPUs State Time(s)" +USBVM_N = "sys-usb" +FWUPDMGR = "/bin/fwupdmgr" +BIOS_UPDATE_FLAG = os.path.join(FWUPD_DOM0_DIR, "bios_update") +LVFS_TESTING_DOM0_FLAG = os.path.join(FWUPD_DOM0_DIR, "lvfs_testing") +LVFS_TESTING_USBVM_FLAG = os.path.join(FWUPD_VM_DIR, "lvfs_testing") +CUSTOM_METADATA = "https://fwupd.org/downloads/firmware-3c81bfdc9db5c8a42c09d38091944bc1a05b27b0.xml.gz" + + +def check_usbvm(): + """Checks if sys-usb is running""" + if "qubes" not in platform.release(): + return False + q = qfwupd.QubesFwupdmgr() + q.check_usbvm() + return "sys-usb" in q.output + + +def device_connected_dom0(): + """Checks if the testing device is connected in dom0""" + if "qubes" not in platform.release(): + return False + q = qfwupd.QubesFwupdmgr() + q._get_dom0_devices() + return "ColorHug2" in q.dom0_devices_info + + +def device_connected_usbvm(): + """Checks if the testing device is connected in usbvm""" + if not check_usbvm(): + return False + q = qfwupd.QubesFwupdmgr() + q._validate_usbvm_dirs() + if not os.path.exists(FWUPD_DOM0_DIR): + q.refresh_metadata() + q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + return "ColorHug2" in usbvm_device_info.read() + + +def check_whonix_updatevm(): + """Checks if the sys-whonix is running""" + if "qubes" not in platform.release(): + return False + q = qfwupd.QubesFwupdmgr() + q.check_usbvm() + return "sys-whonix" in q.output + + +class TestQubesFwupdmgr(unittest.TestCase): + def setUp(self): + self.q = qfwupd.QubesFwupdmgr() + self.maxDiff = 2000 + self.captured_output = io.StringIO() + sys.stdout = self.captured_output + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_download_metadata(self): + self.q.metadata_file = FWUPD_DOM0_METADATA_FILE + self.q._download_metadata() + self.assertTrue( + os.path.exists(FWUPD_DOM0_METADATA_FILE), + msg="Metadata update file does not exist", + ) + self.assertTrue( + os.path.exists(FWUPD_DOM0_METADATA_FILE_JCAT), + msg="Metadata signature does not exist", + ) + + @unittest.skipUnless(check_whonix_updatevm(), "Requires sys-whonix") + def test_download_metadata_whonix(self): + self.q.metadata_file = FWUPD_DOM0_METADATA_FILE + self.q._download_metadata(whonix=True) + self.assertTrue( + os.path.exists(FWUPD_DOM0_METADATA_FILE), + msg="Metadata update file does not exist", + ) + self.assertTrue( + os.path.exists(FWUPD_DOM0_METADATA_FILE_JCAT), + msg="Metadata signature does not exist", + ) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_download_custom_metadata(self): + self.q.metadata_file = CUSTOM_METADATA.replace( + "https://fwupd.org/downloads", FWUPD_DOM0_METADATA_DIR + ) + self.q.metadata_file_jcat = self.q.metadata_file + ".jcat" + self.q._download_metadata(metadata_url=CUSTOM_METADATA) + self.assertTrue( + os.path.exists(self.q.metadata_file), + msg="Metadata update file does not exist", + ) + self.assertTrue( + os.path.exists(self.q.metadata_file_jcat), + msg="Metadata signature does not exist", + ) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_refresh_metadata_dom0(self): + self.q.refresh_metadata() + self.assertEqual( + self.q.output, + "Successfully refreshed metadata manually\n", + msg="Metadata refresh failed.", + ) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_refresh_metadata_dom0_custom(self): + self.q.refresh_metadata(metadata_url=CUSTOM_METADATA) + self.assertEqual( + self.q.output, + "Successfully refreshed metadata manually\n", + msg="Metadata refresh failed.", + ) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_refresh_metadata_usbvm(self): + self.q.refresh_metadata(usbvm=True) + self.assertEqual( + self.q.output, + "Successfully refreshed metadata manually\n", + msg="Metadata refresh failed.", + ) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_refresh_metadata_usbvm_custom(self): + self.q.refresh_metadata(usbvm=True, metadata_url=CUSTOM_METADATA) + self.assertEqual( + self.q.output, + "Successfully refreshed metadata manually\n", + msg="Metadata refresh failed.", + ) + + @unittest.skipUnless(check_whonix_updatevm(), "Requires sys-whonix") + def test_refresh_metadata_whonix(self): + self.q.refresh_metadata(whonix=True) + self.assertEqual( + self.q.output, + "Successfully refreshed metadata manually\n", + msg="Metadata refresh failed.", + ) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_get_dom0_updates(self): + self.q._get_dom0_updates() + self.assertIn( + "Devices", self.q.dom0_updates_info, msg="Getting available updates failed" + ) + + def test_parse_updates_info(self): + self.q._parse_dom0_updates_info(UPDATE_INFO) + self.assertEqual( + self.q.dom0_updates_list[0]["Name"], "ColorHug2", msg="Wrong device name" + ) + self.assertEqual( + self.q.dom0_updates_list[0]["Version"], "2.0.6", msg="Wrong update version" + ) + self.assertEqual( + self.q.dom0_updates_list[0]["Releases"][0]["Url"], + "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + msg="Wrong update URL", + ) + self.assertEqual( + self.q.dom0_updates_list[0]["Releases"][0]["Checksum"], + "32c4a2c9be787cdf1d757c489d6455bd7bb14053425180b6d331c37e1ccc1cda", + msg="Wrong checksum", + ) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_download_firmware_updates(self): + self.q._download_firmware_updates( + "https://fwupd.org/downloads/e5ad222bdbd3d3d48d8613e67c7e0a0e194f" + "8cd828e33c554d9f05d933e482c7-hughski-colorhug2-2.0.7.cab", + "e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7", + ) + update_path = os.path.join( + FWUPD_DOM0_UPDATES_DIR, + "e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7" + "-hughski-colorhug2-2.0.7.cab", + ) + self.assertTrue(os.path.exists(update_path)) + + @unittest.skipUnless(check_whonix_updatevm(), "Requires sys-whonix") + def test_download_firmware_updates_whonix(self): + self.q._download_firmware_updates( + "https://fwupd.org/downloads/e5ad222bdbd3d3d48d8613e67c7e0a0e194f" + "8cd828e33c554d9f05d933e482c7-hughski-colorhug2-2.0.7.cab", + "e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7", + whonix=True, + ) + update_path = os.path.join( + FWUPD_DOM0_UPDATES_DIR, + "e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7" + "-hughski-colorhug2-2.0.7.cab", + ) + self.assertTrue(os.path.exists(update_path)) + + def test_user_input_empty_dict(self): + downgrade_dict = {"usbvm": [], "dom0": []} + self.assertEqual(self.q._user_input(downgrade_dict), 2) + + def test_user_input_n(self): + user_input = ["sth", "n"] + with patch("builtins.input", side_effect=user_input): + self.q._parse_dom0_updates_info(UPDATE_INFO) + downgrade_dict = { + "usbvm": self.q.dom0_updates_list, + "dom0": self.q.dom0_updates_list, + } + choice = self.q._user_input(downgrade_dict, usbvm=True) + self.assertEqual(choice, 2) + user_input = ["sth", "N"] + with patch("builtins.input", side_effect=user_input): + self.q._parse_dom0_updates_info(UPDATE_INFO) + downgrade_dict = { + "usbvm": self.q.dom0_updates_list, + "dom0": self.q.dom0_updates_list, + } + choice = self.q._user_input(downgrade_dict, usbvm=True) + self.assertEqual(choice, 2) + + def test_user_input_choice(self): + user_input = ["6", "1"] + with patch("builtins.input", side_effect=user_input): + self.q._parse_dom0_updates_info(UPDATE_INFO) + updates_dict = { + "usbvm": self.q.dom0_updates_list, + "dom0": self.q.dom0_updates_list, + } + key, choice = self.q._user_input(updates_dict) + self.assertEqual(key, "dom0") + self.assertEqual(choice, 0) + + def test_user_input_choice_usbvm(self): + user_input = ["6", "2"] + with patch("builtins.input", side_effect=user_input): + self.q._parse_dom0_updates_info(UPDATE_INFO) + updates_dict = { + "usbvm": self.q.dom0_updates_list, + "dom0": self.q.dom0_updates_list, + } + key, choice = self.q._user_input(updates_dict, usbvm=True) + self.assertEqual(key, "usbvm") + self.assertEqual(choice, 0) + + def test_parse_parameters(self): + self.q._parse_dom0_updates_info(UPDATE_INFO) + update_dict = {"dom0": self.q.dom0_updates_list} + self.q._parse_parameters(update_dict, "dom0", 0) + self.assertEqual( + self.q.url, + "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + ) + self.assertEqual( + self.q.sha, + "32c4a2c9be787cdf1d757c489d6455bd7bb14053425180b6d331c37e1ccc1cda", + ) + self.assertEqual(self.q.version, "2.0.7") + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_clean_cache_dom0(self): + self.q.clean_cache() + self.assertFalse(os.path.exists(FWUPD_DOM0_METADATA_DIR)) + self.assertFalse(os.path.exists(FWUPD_DOM0_UNTRUSTED_DIR)) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_clean_cache_dom0_n_usbvm(self): + self.q._validate_usbvm_dirs() + self.q.clean_cache(usbvm=True) + self.assertFalse(os.path.exists(FWUPD_DOM0_METADATA_DIR)) + self.assertFalse(os.path.exists(FWUPD_DOM0_UNTRUSTED_DIR)) + cmd_validate_metadata = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"! [ -d {FWUPD_VM_METADATA_DIR} ]", + ] + p = subprocess.Popen(cmd_validate_metadata) + p.wait() + self.assertEqual(p.returncode, 0, msg="Creating metadata directory failed") + cmd_validate_udpdate = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"! [ -d {FWUPD_VM_UPDATES_DIR} ]", + ] + p = subprocess.Popen(cmd_validate_udpdate) + p.wait() + self.assertEqual(p.returncode, 0, msg="Cleaning update directory failed") + + def test_output_crawler(self): + crawler_output = io.StringIO() + sys.stdout = crawler_output + self.q._output_crawler(json.loads(UPDATE_INFO), 0) + with open("test/logs/get_devices.log", "r") as get_devices: + self.assertEqual( + get_devices.read(), crawler_output.getvalue().strip() + "\n" + ) + sys.stdout = self.captured_output + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_get_dom0_devices(self): + self.q._get_dom0_devices() + self.assertIsNotNone(self.q.dom0_devices_info) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_get_devices_qubes_dom0(self): + get_devices_output = io.StringIO() + sys.stdout = get_devices_output + self.q.get_devices_qubes() + self.assertNotEqual(get_devices_output.getvalue().strip(), "") + sys.stdout = self.captured_output + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_get_devices_qubes_usbvm(self): + get_devices_output = io.StringIO() + sys.stdout = get_devices_output + self.q.get_devices_qubes(usbvm=True) + self.assertNotEqual(get_devices_output.getvalue().strip(), "") + sys.stdout = self.captured_output + + @unittest.skipUnless(device_connected_dom0(), REQUIRED_DEV) + def test_get_updates_qubes_dom0(self): + get_updates_output = io.StringIO() + sys.stdout = get_updates_output + self.q.get_updates_qubes() + self.assertNotEqual(get_updates_output.getvalue().strip(), "") + sys.stdout = self.captured_output + + @unittest.skipUnless(device_connected_usbvm(), REQUIRED_DEV) + def test_get_updates_qubes_usbvm(self): + get_updates_output = io.StringIO() + sys.stdout = get_updates_output + self.q.get_updates_qubes(usbvm=True) + self.assertNotEqual(get_updates_output.getvalue().strip(), "") + sys.stdout = self.captured_output + + def test_help(self): + help_output = io.StringIO() + sys.stdout = help_output + self.q.help() + with open("test/logs/help.log", "r") as help_log: + self.assertEqual(help_log.read(), help_output.getvalue().strip() + "\n") + sys.stdout = self.captured_output + + @patch( + "test.test_qubes_fwupdmgr.qfwupd.QubesFwupdmgr._read_dmi", + return_value=DMI_DECODE, + ) + def test_verify_dmi(self, output): + self.q.dmi_version = "P.1.0" + self.q._verify_dmi("test/logs/", "P1.1") + + @patch( + "test.test_qubes_fwupdmgr.qfwupd.QubesFwupdmgr._read_dmi", + return_value=DMI_DECODE, + ) + def test_verify_dmi_wrong_vendor(self, output): + with self.assertRaises(ValueError) as wrong_vendor: + self.q.dmi_version = "P.1.0" + self.q._verify_dmi("test/logs/metainfo_name/", "P1.1") + self.assertIn("Wrong firmware provider.", str(wrong_vendor.exception)) + + @patch( + "test.test_qubes_fwupdmgr.qfwupd.QubesFwupdmgr._read_dmi", + return_value=DMI_DECODE, + ) + def test_verify_dmi_version(self, output): + self.q.dmi_version = "P1.0" + with self.assertRaises(ValueError) as downgrade: + self.q._verify_dmi("test/logs/metainfo_version/", "P0.1") + self.assertIn("P0.1 < P1.0 Downgrade not allowed", str(downgrade.exception)) + + @unittest.skipUnless(device_connected_dom0(), REQUIRED_DEV) + def test_downgrade_firmware_dom0(self): + old_version = None + self.q._get_dom0_devices() + downgrades = self.q._parse_downgrades(self.q.dom0_devices_info) + for number, device in enumerate(downgrades): + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + old_version = device["Version"] + break + if old_version is None: + self.fail("Test device not found") + user_input = [str(number + 1), "1"] + with patch("builtins.input", side_effect=user_input): + self.q.downgrade_firmware() + self.q._get_dom0_devices() + downgrades = self.q._parse_downgrades(self.q.dom0_devices_info) + new_version = downgrades[number]["Version"] + self.assertGreater(Version(old_version), Version(new_version)) + + @unittest.skipUnless( + check_whonix_updatevm() and device_connected_usbvm(), REQUIRED_DEV + ) + def test_update_n_downgrade_firmware_whonix(self): + old_version = None + self.q.clean_cache(usbvm=True) + self.q._get_dom0_devices() + dom0_downgrades = self.q._parse_downgrades(self.q.dom0_devices_info) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + downgrades = self.q._parse_downgrades(raw) + for number, device in enumerate(downgrades): + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + old_version = device["Version"] + break + if old_version is None: + self.fail("Test device not found") + user_input = [str(number + 1 + len(dom0_downgrades)), "1"] + with patch("builtins.input", side_effect=user_input): + self.q.downgrade_firmware(usbvm=True, whonix=True) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + downgrades = self.q._parse_downgrades(raw) + new_version = downgrades[number]["Version"] + self.assertGreater(Version(old_version), Version(new_version)) + old_version = None + new_version = None + self.q._get_dom0_updates() + self.q._parse_dom0_updates_info(self.q.dom0_updates_info) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + self.q._parse_usbvm_updates(raw) + for number, device in enumerate(self.q.usbvm_updates_list): + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + old_version = device["Version"] + break + if old_version is None: + self.fail("Test device not found") + user_input = [str(number + 1 + len(self.q.dom0_updates_list)), "1"] + with patch("builtins.input", side_effect=user_input): + self.q.update_firmware(usbvm=True, whonix=True) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + usbvm_devices_info_dict = json.loads(raw) + for device in usbvm_devices_info_dict["Devices"]: + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + new_version = device["Version"] + break + if new_version is None: + self.fail("Test device not found") + self.assertLess(Version(old_version), Version(new_version)) + + @unittest.skipUnless(device_connected_usbvm(), REQUIRED_DEV) + def test_downgrade_firmware_usbvm(self): + old_version = None + self.q._get_dom0_devices() + dom0_downgrades = self.q._parse_downgrades(self.q.dom0_devices_info) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + downgrades = self.q._parse_downgrades(raw) + for number, device in enumerate(downgrades): + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + old_version = device["Version"] + break + if old_version is None: + self.fail("Test device not found") + user_input = [str(number + 1 + len(dom0_downgrades)), "1"] + with patch("builtins.input", side_effect=user_input): + self.q.downgrade_firmware(usbvm=True) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + downgrades = self.q._parse_downgrades(raw) + new_version = downgrades[number]["Version"] + self.assertGreater(Version(old_version), Version(new_version)) + + def test_parse_downgrades(self): + downgrades = self.q._parse_downgrades(GET_DEVICES) + self.assertEqual(downgrades[0]["Name"], "ColorHug2") + self.assertEqual(downgrades[0]["Version"], "2.0.6") + self.assertEqual(downgrades[0]["Releases"][0]["Version"], "2.0.5") + self.assertEqual( + downgrades[0]["Releases"][0]["Url"], + "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + ) + self.assertEqual( + downgrades[0]["Releases"][0]["Checksum"], + "8cd379eb2e1467e4fda92c20650306dc7e598b1d421841bbe19d9ed6ea01e3ee", + ) + + def test_parse_downgrades_no_version(self): + downgrades = self.q._parse_downgrades(GET_DEVICES_NO_VERSION) + self.assertEqual(downgrades[0]["Name"], "ColorHug2") + self.assertEqual(downgrades[0]["Version"], "2.0.6") + self.assertEqual(downgrades[0]["Releases"][0]["Version"], "2.0.5") + self.assertEqual( + downgrades[0]["Releases"][0]["Url"], + "https://fwupd.org/downloads/f7dd4ab29fa610438571b8b62b26b0b0e57bb35b-hughski-colorhug2-2.0.5.cab", + ) + self.assertEqual( + downgrades[0]["Releases"][0]["Checksum"], + "4ee9dfa38df3b810f739d8a19d13da1b3175fb87", + ) + + def test_user_input_downgrade_usbvm(self): + user_input = ["2", "6", "sth", "2.2.1", "", " ", "\0", "2"] + with patch("builtins.input", side_effect=user_input): + downgrade_list = self.q._parse_downgrades(GET_DEVICES) + downgrade_dict = {"usbvm": downgrade_list, "dom0": downgrade_list} + key, device_choice, downgrade_choice = self.q._user_input( + downgrade_dict, downgrade=True, usbvm=True + ) + self.assertEqual(key, "usbvm") + self.assertEqual(device_choice, 0) + self.assertEqual(downgrade_choice, 1) + + def test_user_input_downgrade_dom0(self): + user_input = ["1", "6", "sth", "2.2.1", "", " ", "\0", "2"] + with patch("builtins.input", side_effect=user_input): + downgrade_list = self.q._parse_downgrades(GET_DEVICES) + downgrade_dict = {"dom0": downgrade_list} + key, device_choice, downgrade_choice = self.q._user_input( + downgrade_dict, downgrade=True + ) + self.assertEqual(key, "dom0") + self.assertEqual(device_choice, 0) + self.assertEqual(downgrade_choice, 1) + + def test_user_input_downgrade_N(self): + user_input = ["N"] + with patch("builtins.input", side_effect=user_input): + downgrade_list = self.q._parse_downgrades(GET_DEVICES) + downgrade_dict = {"usbvm": downgrade_list, "dom0": downgrade_list} + N_choice = self.q._user_input(downgrade_dict, downgrade=True, usbvm=True) + self.assertEqual(N_choice, 2) + + @unittest.skipUnless(device_connected_dom0(), REQUIRED_DEV) + def test_update_firmware_dom0(self): + old_version = None + new_version = None + self.q._get_dom0_updates() + self.q._parse_dom0_updates_info(self.q.dom0_updates_info) + for number, device in enumerate(self.q.dom0_updates_list): + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + old_version = device["Version"] + break + if old_version is None: + self.fail("Test device not found") + user_input = [str(number + 1)] + with patch("builtins.input", side_effect=user_input): + self.q.update_firmware() + self.q._get_dom0_devices() + dom0_devices_info_dict = json.loads(self.q.dom0_devices_info) + for device in dom0_devices_info_dict["Devices"]: + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + new_version = device["Version"] + break + if new_version is None: + self.fail("Test device not found") + self.assertLess(Version(old_version), Version(new_version)) + + @unittest.skipUnless(device_connected_usbvm(), REQUIRED_DEV) + def test_update_firmware_usbvm(self): + old_version = None + new_version = None + self.q._get_dom0_updates() + self.q._parse_dom0_updates_info(self.q.dom0_updates_info) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + self.q._parse_usbvm_updates(raw) + for number, device in enumerate(self.q.usbvm_updates_list): + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + old_version = device["Version"] + break + if old_version is None: + self.fail("Test device not found") + user_input = [str(number + 1 + len(self.q.dom0_updates_list)), "1"] + with patch("builtins.input", side_effect=user_input): + self.q.update_firmware(usbvm=True) + self.q._get_usbvm_devices() + with open(FWUPD_VM_LOG) as usbvm_device_info: + raw = usbvm_device_info.read() + usbvm_devices_info_dict = json.loads(raw) + for device in usbvm_devices_info_dict["Devices"]: + if "Name" not in device: + continue + if device["Name"] == "ColorHug2": + new_version = device["Version"] + break + if new_version is None: + self.fail("Test device not found") + self.assertLess(Version(old_version), Version(new_version)) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_get_usbvm_devices(self): + self.q._get_usbvm_devices() + self.assertTrue(os.path.exists(FWUPD_VM_LOG)) + + def test_parse_usbvm_updates(self): + self.q._parse_usbvm_updates(GET_DEVICES) + self.assertEqual(self.q.usbvm_updates_list[0]["Name"], "ColorHug2") + self.assertEqual(self.q.usbvm_updates_list[0]["Version"], "2.0.6") + self.assertListEqual( + self.q.usbvm_updates_list[0]["Releases"], + [ + { + "Checksum": "32c4a2c9be787cdf1d757c489d6455bd7bb14053425180b6d331c37e1ccc1cda", + "Description": "

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

    ", + "Url": "https://fwupd.org/downloads/0a29848de74d26348bc5a6e24fc9f03778eddf0e-hughski-colorhug2-2.0.7.cab", + "Version": "2.0.7", + } + ], + ) + + def test_parse_usbvm_updates_no_updates_available(self): + self.q._parse_usbvm_updates(GET_DEVICES_NO_UPDATES) + self.assertListEqual(self.q.usbvm_updates_list, []) + + def test_updates_crawler(self): + crawler_output = io.StringIO() + sys.stdout = crawler_output + self.q._parse_usbvm_updates(GET_DEVICES) + self.q._updates_crawler(self.q.usbvm_updates_list, usbvm=True) + with open("test/logs/get_updates.log", "r") as getupdates: + self.assertEqual( + getupdates.read(), crawler_output.getvalue().strip() + "\n" + ) + sys.stdout = self.captured_output + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_validate_usbvm_dirs(self): + self.q._validate_usbvm_dirs() + cmd_validate_metadata = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"[ -d {FWUPD_VM_METADATA_DIR} ]", + ] + p = subprocess.Popen(cmd_validate_metadata) + p.wait() + self.assertEqual(p.returncode, 0, msg="Creating metadata directory failed") + cmd_validate_udpdate = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"[ -d {FWUPD_VM_UPDATES_DIR} ]", + ] + p = subprocess.Popen(cmd_validate_udpdate) + p.wait() + self.assertEqual(p.returncode, 0, msg="Creating update directory failed") + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_copy_usbvm_metadata(self): + self.q.metadata_file = FWUPD_DOM0_METADATA_FILE + self.q.metadata_file_jcat = self.q.metadata_file + ".jcat" + self.q._download_metadata() + self.q._validate_usbvm_dirs() + self.q._copy_usbvm_metadata() + cmd_validate_metadata_file = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"[ -f {FWUPD_VM_METADATA_FILE} ]", + ] + p = subprocess.Popen(cmd_validate_metadata_file) + p.wait() + self.assertEqual(p.returncode, 0, msg="Metadata file does not exist") + cmd_validate_metadata_jcat = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"[ -f {FWUPD_VM_METADATA_FILE_JCAT} ]", + ] + p = subprocess.Popen(cmd_validate_metadata_jcat) + p.wait() + self.assertEqual(p.returncode, 0, msg="Metadata jcat signature does not exist") + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_enable_lvfs_testing_dom0(self): + if os.path.exists(LVFS_TESTING_DOM0_FLAG): + os.remove(LVFS_TESTING_DOM0_FLAG) + self.q._enable_lvfs_testing_dom0() + self.assertTrue(os.path.exists(LVFS_TESTING_DOM0_FLAG)) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_enable_lvfs_testing_usbvm(self): + cmd_validate_flag = [ + "qvm-run", + "--pass-io", + USBVM_N, + ( + "script --quiet --return --command " + f'"ls {LVFS_TESTING_USBVM_FLAG} &>/dev/null"' + ), + ] + cmd_rm_flag = [ + "qvm-run", + "--pass-io", + USBVM_N, + ("script --quiet --return --command " f'"rm {LVFS_TESTING_USBVM_FLAG}"'), + ] + flag = subprocess.Popen(cmd_validate_flag) + flag.wait() + if flag.returncode == 0: + rm_flag = subprocess.Popen(cmd_rm_flag) + rm_flag.wait() + if rm_flag.returncode != 0: + raise Exception("Removing lvfs-testing flag failed!!") + self.q._enable_lvfs_testing_usbvm(usbvm=True) + flag = subprocess.Popen(cmd_validate_flag) + flag.wait() + self.assertEqual(flag.returncode, 0) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_validate_usbvm_metadata(self): + self.q.metadata_file = FWUPD_DOM0_METADATA_FILE + self.q.metadata_file_jcat = self.q.metadata_file + ".jcat" + self.q._download_metadata() + self.q._validate_usbvm_dirs() + self.q._copy_usbvm_metadata() + self.q._validate_usbvm_metadata() + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_refresh_usbvm_metadata(self): + self.q.metadata_file = FWUPD_DOM0_METADATA_FILE + self.q.metadata_file_jcat = self.q.metadata_file + ".jcat" + self.q.lvfs = "lvfs" + self.q._download_metadata() + self.q._validate_usbvm_dirs() + self.q._copy_usbvm_metadata() + self.q._validate_usbvm_metadata() + self.q._refresh_usbvm_metadata() + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_clean_usbvm(self): + self.q._validate_usbvm_dirs() + self.q._clean_usbvm() + cmd_validate_metadata = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"! [ -d {FWUPD_VM_METADATA_DIR} ]", + ] + p = subprocess.Popen(cmd_validate_metadata) + p.wait() + self.assertEqual(p.returncode, 0, msg="Cleaning metadata directory failed") + cmd_validate_udpdate = [ + "qvm-run", + "--pass-io", + "sys-usb", + f"! [ -d {FWUPD_VM_METADATA_DIR} ]", + ] + p = subprocess.Popen(cmd_validate_udpdate) + p.wait() + self.assertEqual(p.returncode, 0, msg="Cleaning update directory failed") + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_validate_usbvm_archive(self): + url = ( + "https://fwupd.org/downloads/e5ad222bdbd3d3d48d8613e67c7e0a0e1" + "94f8cd828e33c554d9f05d933e482c7-hughski-colorhug2-2.0.7.cab" + ) + sha = "e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7" + name = url.replace("https://fwupd.org/downloads/", "") + self.q._clean_usbvm() + self.q._validate_usbvm_dirs() + self.q._download_firmware_updates(url, sha) + self.q._copy_firmware_updates(name) + self.q._validate_usbvm_archive(name, sha) + cmd_validate_udpdate = [ + "qvm-run", + "--pass-io", + "sys-usb", + "[ -f %s ]" % os.path.join(FWUPD_VM_UPDATES_DIR, name), + ] + p = subprocess.Popen(cmd_validate_udpdate) + p.wait() + self.assertEqual(p.returncode, 0, msg="Archive validation failed") + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_check_usbvm(self): + self.q.check_usbvm() + self.assertIn(XL_LIST_LOG, self.q.output) + + @unittest.skipUnless("qubes" in platform.release(), "Requires Qubes OS") + def test_bios_refresh_metadata(self): + sys_usb = self.q.check_usbvm() + Path(BIOS_UPDATE_FLAG).touch(mode=0o644, exist_ok=True) + self.q.refresh_metadata_after_bios_update(usbvm=sys_usb) + self.assertEqual( + self.q.output, + "Successfully refreshed metadata manually\n", + msg="Metadata refresh failed.", + ) + + @unittest.skipUnless(check_usbvm(), REQUIRED_USBVM) + def test_trusted_cleanup(self): + trusted_path = os.path.join(FWUPD_DOM0_UPDATES_DIR, "trusted.cab") + if not os.path.exists(trusted_path): + Path(FWUPD_DOM0_UPDATES_DIR).mkdir(exist_ok=True) + Path(trusted_path).touch(mode=0o644, exist_ok=True) + os.mkdir(trusted_path.replace(".cab", "")) + self.q.trusted_cleanup(usbvm=True) + self.assertFalse(os.path.exists(trusted_path)) + self.assertFalse(os.path.exists(trusted_path.replace(".cab", ""))) + + +if __name__ == "__main__": + unittest.main() diff --git a/fwupd-1.8.6/contrib/reformat-code.py b/fwupd-1.8.6/contrib/reformat-code.py new file mode 100755 index 0000000000000000000000000000000000000000..65a4ac4ac7c3fc8f66c6ec177a0529a01d18f08b --- /dev/null +++ b/fwupd-1.8.6/contrib/reformat-code.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2017 Dell Inc. +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +import os +import sys +import subprocess +import argparse + +CLANG_DIFF_FORMATTERS = [ + "clang-format-diff-11", + "clang-format-diff-13", + "clang-format-diff", +] + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Reformat C code to match project style", + epilog="Call with no argument to reformat uncommitted code.", + ) + parser.add_argument( + "commit", nargs="*", default="", help="Reformat all changes since this commit" + ) + parser.add_argument( + "--debug", action="store_true", help="Display all launched commands" + ) + return parser.parse_args() + + +def select_clang_version(formatters): + for formatter in formatters: + try: + ret = subprocess.check_call( + [formatter, "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + if ret == 0: + return formatter + except FileNotFoundError: + continue + print("No clang formatter installed") + sys.exit(1) + + +## Entry Point ## +if __name__ == "__main__": + args = parse_args() + base = os.getenv("GITHUB_BASE_REF") + if base: + base = "origin/%s" % base + else: + if args.commit: + base = args.commit[0] + else: + base = "HEAD" + cmd = ["git", "describe", base] + if args.debug: + print(cmd) + ret = subprocess.run(cmd, capture_output=True) + if ret.returncode: + if args.debug: + print(ret.stderr) + base = "HEAD" + print("Reformatting code against %s" % base) + formatter = select_clang_version(CLANG_DIFF_FORMATTERS) + cmd = ["git", "diff", "-U0", base] + if args.debug: + print(cmd) + ret = subprocess.run(cmd, capture_output=True, text=True) + if ret.returncode: + print("Failed to run %s\n%s" % (cmd, ret.stderr.strip())) + sys.exit(1) + cmd = [formatter, "-p1"] + if args.debug: + print(cmd) + ret = subprocess.run(cmd, input=ret.stdout, capture_output=True, text=True) + if ret.returncode: + print("Failed to run %s\n%s" % (cmd, ret.stderr.strip())) + sys.exit(1) + cmd = ["patch", "-p0"] + if args.debug: + print(cmd) + ret = subprocess.run(cmd, input=ret.stdout, capture_output=True, text=True) + if ret.returncode: + print("Failed to run %s\n%s" % (cmd, ret.stderr.strip())) + sys.exit(1) + sys.exit(0) diff --git a/fwupd-1.8.6/contrib/run-tests.sh b/fwupd-1.8.6/contrib/run-tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..9a22092231bc265e1b9edd57bd8eede1ddf6b5be --- /dev/null +++ b/fwupd-1.8.6/contrib/run-tests.sh @@ -0,0 +1,2 @@ +#!/bin/sh +meson build && ninja -C build test diff --git a/fwupd-1.8.6/contrib/setup b/fwupd-1.8.6/contrib/setup new file mode 100755 index 0000000000000000000000000000000000000000..71fb82a8c03a19a66aa2ed3c0419ef393c3a6a3d --- /dev/null +++ b/fwupd-1.8.6/contrib/setup @@ -0,0 +1,184 @@ +#!/bin/bash -e +# Setup the repository and local system for development + +cd "$(dirname "$0")/.." + +HELPER=./contrib/ci/fwupd_setup_helpers.py +HELPER_ARGS="-y" + +rename_branch() +{ + OLD=master + NEW=main + if git log $OLD >/dev/null 2>&1 && + git remote get-url origin 2>&1 | grep fwupd/fwupd.git >/dev/null 2>&1; then + read -p "Rename existing $OLD branch to $NEW? (y/N) " question + if [ "$question" = "y" ]; then + git branch -m $OLD $NEW + git fetch origin + git branch -u origin/$NEW $NEW + git remote set-head origin -a + fi + fi +} + +detect_selinux() +{ + SELINUX="0" + if which getenforce &> /dev/null; then + if getenforce | grep -i "enforcing" >/dev/null; then + SELINUX="1" + fi + fi +} + +setup_deps() +{ + read -p "Install build dependencies? (y/N) " question + if [ "$question" = "y" ]; then + $(which sudo) python3 $HELPER install-dependencies $HELPER_ARGS -y + fi +} + +setup_run_dev() +{ + if [ "$SELINUX" -eq "1" ]; then + echo "SELinux is enabled, you won't be able to run the daemon from default prefix /usr/local" + return + fi + read -p "Set up dbus activated daemon and PolicyKit actions from /usr/local? (y/N) " question + if [ "$question" = "y" ]; then + ./contrib/prepare-system /usr/local install + fi +} + +setup_unsafe_polkit_rules() +{ + read -p "Install developer-friendly **unsafe** PolicyKit rules into /etc/polkit-1/rules.d? (y/N) " question + if [ "$question" = "y" ]; then + sudo mkdir -p /etc/polkit-1/rules.d + sudo cp ./policy/org.freedesktop.fwupd-unsafe.rules /etc/polkit-1/rules.d/ + fi +} + +setup_vscode() +{ + # Add default vscode settings if not existing + SETTINGS_FILE=./.vscode/settings.json + SETTINGS_TEMPLATE_FILE=./contrib/vscode/settings.json + if [ ! -f "$SETTINGS_FILE" ]; then + mkdir ./.vscode + echo "Copy $SETTINGS_TEMPLATE_FILE to $SETTINGS_FILE." + cp "$SETTINGS_TEMPLATE_FILE" "$SETTINGS_FILE" + fi +} + +setup_git() +{ + echo "Configuring git environment" + git config include.path ../.gitconfig +} + +install_pip() +{ + package=$1 + args=$2 + if ! python3 -m pip install $package $args; then + $(which sudo) python3 $HELPER install-pip $HELPER_ARGS -y + fi + #try once more + python3 -m pip install $package +} + +setup_precommit() +{ + echo "Configuring pre-commit hooks" + python3 -m venv venv + source venv/bin/activate + + install_pip pre-commit + pre-commit install +} + +setup_prepush() +{ + read -p "Run tests locally before pushing to remote branches? THIS WILL SLOW DOWN EVERY PUSH but reduce the risk of failing CI. (y/N) " question + if [ "$question" = "y" ]; then + pre-commit install -t pre-push + else + pre-commit uninstall -t pre-push + fi +} + +check_markdown() +{ + python3 $HELPER test-markdown +} + +check_meson() +{ + python3 $HELPER test-meson +} + +detect_os() +{ + for i in "$@"; do + case $i in + --os=*) + OS="${i#*=}" + shift + ;; + --debug) + DEBUG=1 + shift + ;; + *) + ;; + esac + done + if [ -z $OS ]; then + OS=$(python3 $HELPER detect-profile) + if [ -z "$OS" ]; then + install_pip distro + OS=$(python3 $HELPER detect-profile) + fi + echo "Using OS profile $OS to setup" + fi + if [ -n "$OS" ];then + HELPER_ARGS="$HELPER_ARGS --os $OS" + fi + if [ -n "$DEBUG" ]; then + set -x + HELPER_ARGS="$HELPER_ARGS --debug" + fi +} + +#needed for arguments for some commands +detect_os "$@" + +detect_selinux + +#if interactive install build deps and prepare environment +if [ -t 2 ]; then + case $OS in + debian|ubuntu|arch|fedora) + setup_deps + setup_run_dev + ;; + void) + setup_deps + ;; + esac + setup_unsafe_polkit_rules + rename_branch +fi +check_markdown +setup_vscode +setup_git +check_meson +setup_precommit + +#needs to be after pre-commit is sourced +if [ -t 2 ]; then + setup_prepush +fi diff --git a/fwupd-1.8.6/contrib/snap/README.md b/fwupd-1.8.6/contrib/snap/README.md new file mode 100644 index 0000000000000000000000000000000000000000..eb2cd9dc115c9932ef70a6544188174127143a69 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/README.md @@ -0,0 +1,32 @@ +# Snap support + +Snaps are containerised software packages that are simple to create and install. They auto-update and are safe to run. And because they bundle their dependencies, they work on all major Linux systems without modification. + +## stable vs unstable + +Two yaml files are distributed: + +* snapcraft.yaml +This uses tarball releases for all dependencies and what is currently in tree for fwupd. + +* snapcraft-master.yaml +This uses git for most dependencies and may be considered unstable. + +## Building + +Builds can be performed using snapcraft: + +```shell +# snapcraft cleanbuild +``` + +## Installing + +A "classic" snap is produced, and locally built snaps can be installed like this: + +```shell +# snap install fwupd_daily_amd64.snap --dangerous --classic +``` + +The `--dangerous` flag is because snaps built locally are not signed. +Snaps distributed by a store will not need this flag. diff --git a/fwupd-1.8.6/contrib/snap/activate-shutdown/Makefile b/fwupd-1.8.6/contrib/snap/activate-shutdown/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a219d193d56072988c7826480b7b70904fb1b343 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/activate-shutdown/Makefile @@ -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 --git a/fwupd-1.8.6/contrib/snap/activate-shutdown/fwupd-activate.service b/fwupd-1.8.6/contrib/snap/activate-shutdown/fwupd-activate.service new file mode 100644 index 0000000000000000000000000000000000000000..79739bebcd256fea32c8460481eda3ecd3bb3bc9 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/activate-shutdown/fwupd-activate.service @@ -0,0 +1,12 @@ +[Unit] +Description=Activate fwupd updates +After=snapd.service + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStop=/snap/bin/fwupd.fwupdtool activate +SuccessExitStatus=0 2 + +[Install] +WantedBy=multi-user.target diff --git a/fwupd-1.8.6/contrib/snap/dbxtool.wrapper b/fwupd-1.8.6/contrib/snap/dbxtool.wrapper new file mode 100755 index 0000000000000000000000000000000000000000..d29ab99015d68f24b7f185fd72b65779821684d4 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/dbxtool.wrapper @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/dbxtool $@ diff --git a/fwupd-1.8.6/contrib/snap/dfu-tool.wrapper b/fwupd-1.8.6/contrib/snap/dfu-tool.wrapper new file mode 100755 index 0000000000000000000000000000000000000000..0b20a913a9f1b172f8c3e54035671c87e90cae8a --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/dfu-tool.wrapper @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/dfu-tool $@ diff --git a/fwupd-1.8.6/contrib/snap/fix-bash-completion/Makefile b/fwupd-1.8.6/contrib/snap/fix-bash-completion/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..add3629496c44e9d508110ff5ed43887ddc26469 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fix-bash-completion/Makefile @@ -0,0 +1,11 @@ +build: + true +install: + #fixes up fwupdtool -> fwupd.fwupdtool + sed -i "s,\(complete -F _fwupd[a-z]*\) \(fwupd.*\),\1 fwupd.\2,; \ + s,\(command.*\)\(fwupdtool\),\1fwupd.\2,; \ + s,\(command.*\)\(fwupdagent\),\1fwupd.\2," \ + ${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 --git a/fwupd-1.8.6/contrib/snap/fwup-efi-signed/Makefile b/fwupd-1.8.6/contrib/snap/fwup-efi-signed/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f72ae7d9e07b03838da7046dbf7b5d328e3036c4 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwup-efi-signed/Makefile @@ -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 --git a/fwupd-1.8.6/contrib/snap/fwup-efi-signed/download-fwupd b/fwupd-1.8.6/contrib/snap/fwup-efi-signed/download-fwupd new file mode 100755 index 0000000000000000000000000000000000000000..5717ddc975d20c57f04d248505e56a6b7cc1a8c1 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwup-efi-signed/download-fwupd @@ -0,0 +1,35 @@ +#! /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 --git a/fwupd-1.8.6/contrib/snap/fwupd-command b/fwupd-1.8.6/contrib/snap/fwupd-command new file mode 100755 index 0000000000000000000000000000000000000000..8afd87a442b5c37ba14812c275de6abe09565943 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwupd-command @@ -0,0 +1,42 @@ +#!/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" ] && [ -d "$SNAP/etc" ] && cp -R "$SNAP/etc" "$SNAP_USER_DATA" +[ ! -d "$SNAP_USER_DATA/var" ] && [ -d "$SNAP/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 + # shellcheck source=/dev/null + . $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 --git a/fwupd-1.8.6/contrib/snap/fwupd.wrapper b/fwupd-1.8.6/contrib/snap/fwupd.wrapper new file mode 100755 index 0000000000000000000000000000000000000000..3ad73086b15175790fe74163a05f713c55a82340 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwupd.wrapper @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/libexec/fwupd/fwupd $@ diff --git a/fwupd-1.8.6/contrib/snap/fwupdagent.wrapper b/fwupd-1.8.6/contrib/snap/fwupdagent.wrapper new file mode 100755 index 0000000000000000000000000000000000000000..303a799d139650a6009c3f5ef376edfac6b60b81 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwupdagent.wrapper @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/fwupdagent $@ diff --git a/fwupd-1.8.6/contrib/snap/fwupdmgr.wrapper b/fwupd-1.8.6/contrib/snap/fwupdmgr.wrapper new file mode 100755 index 0000000000000000000000000000000000000000..a7488263e9323209e2e66a48ad4f02f6c45bba3a --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwupdmgr.wrapper @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/fwupdmgr $@ diff --git a/fwupd-1.8.6/contrib/snap/fwupdtool.wrapper b/fwupd-1.8.6/contrib/snap/fwupdtool.wrapper new file mode 100755 index 0000000000000000000000000000000000000000..4006c6aef9b40e4abe0d2d757e2f5172bb594d8b --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/fwupdtool.wrapper @@ -0,0 +1,2 @@ +#!/bin/sh +exec "$SNAP/fwupd-command" $SNAP/bin/fwupdtool $@ diff --git a/fwupd-1.8.6/contrib/snap/libefivar-fixpkgconfig/Makefile b/fwupd-1.8.6/contrib/snap/libefivar-fixpkgconfig/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..614459e4e0f09da388dda8c2dbbd2498a12c6950 --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/libefivar-fixpkgconfig/Makefile @@ -0,0 +1,10 @@ +build: + true + +install: + 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 --git a/fwupd-1.8.6/contrib/snap/update-mime/Makefile b/fwupd-1.8.6/contrib/snap/update-mime/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1b07f726d57b06b6812bd6327875ca7495f4668d --- /dev/null +++ b/fwupd-1.8.6/contrib/snap/update-mime/Makefile @@ -0,0 +1,5 @@ +build: + true +install: + update-mime-database ../install/usr/share/mime + glib-compile-schemas ../install/usr/share/glib-2.0/schemas diff --git a/fwupd-1.8.6/contrib/standalone-installer/README.md b/fwupd-1.8.6/contrib/standalone-installer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..04a6eb9ccd4cc68c684b5006a169fdb48b38cf49 --- /dev/null +++ b/fwupd-1.8.6/contrib/standalone-installer/README.md @@ -0,0 +1,10 @@ +# 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: + +```shell +./make.py --help +``` diff --git a/fwupd-1.8.6/contrib/standalone-installer/assets/header.py b/fwupd-1.8.6/contrib/standalone-installer/assets/header.py new file mode 100644 index 0000000000000000000000000000000000000000..6f18ccd5768eff2246fb9f394f99aec1f275dd9b --- /dev/null +++ b/fwupd-1.8.6/contrib/standalone-installer/assets/header.py @@ -0,0 +1,369 @@ +#!/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( + "Using existing fwupd version %s already installed on system." + % version.version + ) + return False + else: + print("fwupd %s is installed and must be removed" % version.version) + return remove_packaged_version(pkg, cache) + + +def remove_packaged_version(pkg, cache): + res = False + while True: + res = input("Remove now (Y/N)? ") + if res.lower() == "n": + res = False + break + if res.lower() == "y": + res = True + break + if res: + pkg.mark_delete() + res = cache.commit() + if not res: + raise Exception("Need to remove packaged version") + return True + + +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 not 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: + 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 --git a/fwupd-1.8.6/contrib/standalone-installer/make.py b/fwupd-1.8.6/contrib/standalone-installer/make.py new file mode 100755 index 0000000000000000000000000000000000000000..93a01c9806a12d19506c43cbe1b479bd9d8332c7 --- /dev/null +++ b/fwupd-1.8.6/contrib/standalone-installer/make.py @@ -0,0 +1,173 @@ +#!/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 --git a/fwupd-1.8.6/contrib/vscode/README.md b/fwupd-1.8.6/contrib/vscode/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ce3cee7c177ee1efce5d522cc304d68153d96e91 --- /dev/null +++ b/fwupd-1.8.6/contrib/vscode/README.md @@ -0,0 +1,46 @@ +# 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: + +```shell +sudo DEBUG=1 ./dist/fwupdtool.sh get-devices +``` + +This will configure `gdbserver` to listen on a local port waiting for a debugger to connect. + +## Using Visual Studio code + +During build time a set of launch targets will have been created for use with Visual Studio Code. + +Press the debugging button on the left and 3 targets will be listed at the top. + +* gdbserver (fwupdtool) +* gdbserver (fwupd) +* gdbserver (fwupdmgr) + +Select the appropriate target and press the green arrow to connect to `gdbserver` and start debugging. diff --git a/fwupd-1.8.6/contrib/vscode/build.sh b/fwupd-1.8.6/contrib/vscode/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..de332ba7d256acc5350b725e15ce6fdf8751351d --- /dev/null +++ b/fwupd-1.8.6/contrib/vscode/build.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# Copyright (C) 2018 Dell, Inc. + +SOURCE=$(dirname $0) +ROOT=$1 +if [ -z "$ROOT" ]; then + ROOT=`pwd` +fi + +# build in tree +sudo rm -rf build ${ROOT}/dist +meson build --prefix=${ROOT}/dist -Dsystemd=disabled -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#,bin/fwupdtool," \ + ${TEMPLATE} > ${ROOT}/dist/fwupdtool.sh +sed "s,#ROOT#,${ROOT},; s,#EXECUTABLE#,bin/fwupdmgr," \ + ${TEMPLATE} > ${ROOT}/dist/fwupdmgr.sh +chmod +x ${ROOT}/dist/*.sh + +#create debugging targets +TARGET=${ROOT}/.vscode +mkdir -p ${TARGET} +if [ -f ${TARGET}/launch.json ]; then + echo "${TARGET}/launch.json already exists, not overwriting" +else + cp ${SOURCE}/launch.json ${TARGET} +fi diff --git a/fwupd-1.8.6/contrib/vscode/launch.json b/fwupd-1.8.6/contrib/vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..ebb75559660ee5321c32336b9bec81a9558c253a --- /dev/null +++ b/fwupd-1.8.6/contrib/vscode/launch.json @@ -0,0 +1,65 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "gdbserver (fwupdtool)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/libexec/fwupd/fwupdtool", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "miDebuggerServerAddress": "localhost:9091", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "gdbserver (fwupd)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/libexec/fwupd/fwupd", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "miDebuggerServerAddress": "localhost:9091", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "gdbserver (fwupdmgr)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/dist/bin/fwupdmgr", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "miDebuggerServerAddress": "localhost:9091", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} diff --git a/fwupd-1.8.6/contrib/vscode/launcher.sh b/fwupd-1.8.6/contrib/vscode/launcher.sh new file mode 100755 index 0000000000000000000000000000000000000000..8cea6bdce52993969a7882858509dc427cac7924 --- /dev/null +++ b/fwupd-1.8.6/contrib/vscode/launcher.sh @@ -0,0 +1,10 @@ +#!/bin/sh +gcc=$(gcc -dumpmachine) +export ROOT=#ROOT# +export FWUPD_LOCALSTATEDIR=${ROOT}/dist +export FWUPD_SYSCONFDIR=${ROOT}/dist/etc +export LD_LIBRARY_PATH=${ROOT}/dist/lib/${gcc} +if [ -n "${DEBUG}" ]; then + DEBUG="gdbserver localhost:9091" +fi +${DEBUG} ${ROOT}/dist/#EXECUTABLE# "$@" diff --git a/fwupd-1.8.6/contrib/vscode/settings.json b/fwupd-1.8.6/contrib/vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..2d79621558a9d8d8d938fae42861ff9f9201863f --- /dev/null +++ b/fwupd-1.8.6/contrib/vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.tabSize": 8, + "mesonbuild.buildFolder": "build" +} diff --git a/fwupd-1.8.6/data/90-fwupd-devices.rules b/fwupd-1.8.6/data/90-fwupd-devices.rules new file mode 100644 index 0000000000000000000000000000000000000000..2bc9efc49771d19455e559a931c93ebd43f0be0a --- /dev/null +++ b/fwupd-1.8.6/data/90-fwupd-devices.rules @@ -0,0 +1,8 @@ +######################################################################## +# Copyright (C) 2015 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# + +# NVMe hardware +SUBSYSTEM=="nvme", ENV{ID_VENDOR_FROM_DATABASE}=="", IMPORT{builtin}="hwdb --subsystem=pci" diff --git a/fwupd-1.8.6/data/bash-completion/fwupdagent b/fwupd-1.8.6/data/bash-completion/fwupdagent new file mode 100644 index 0000000000000000000000000000000000000000..448d94b08e5e6238626687b903b877eddce9975e --- /dev/null +++ b/fwupd-1.8.6/data/bash-completion/fwupdagent @@ -0,0 +1,40 @@ +_fwupdagent_cmd_list=( + 'get-devices' + 'get-updates' + 'get-upgrades' + 'security' +) + +_fwupdagent_opts=( + '--verbose' +) + +_show_modifiers() +{ + COMPREPLY+=( $(compgen -W '${_fwupdagent_opts[@]}' -- "$cur") ) +} + +_fwupdagent() +{ + local cur prev command arg args + COMPREPLY=() + _get_comp_words_by_ref cur prev + _get_first_arg + _count_args + + case $arg in + *) + #find first command + if [[ "$args" = "1" ]]; then + COMPREPLY=( $(compgen -W '${_fwupdagent_cmd_list[@]}' -- "$cur") ) + fi + ;; + esac + + #modifiers + _show_modifiers + + return 0 +} + +complete -F _fwupdagent fwupdagent diff --git a/fwupd-1.8.6/data/bash-completion/fwupdmgr b/fwupd-1.8.6/data/bash-completion/fwupdmgr new file mode 100644 index 0000000000000000000000000000000000000000..95646cf3787662cd02b4623cc1ea7f54ed81ab31 --- /dev/null +++ b/fwupd-1.8.6/data/bash-completion/fwupdmgr @@ -0,0 +1,280 @@ +_fwupdmgr_cmd_list=( + 'activate' + 'block-firmware' + 'clear-results' + 'disable-remote' + 'device-test' + 'downgrade' + 'download' + 'enable-remote' + 'get-approved-firmware' + 'get-bios-setting' + 'get-blocked-firmware' + 'get-details' + 'get-devices' + 'get-history' + 'get-releases' + 'get-remotes' + 'get-results' + 'get-topology' + 'get-updates' + 'get-upgrades' + 'get-plugins' + 'install' + 'local-install' + 'modify-config' + 'modify-remote' + 'reinstall' + 'refresh' + 'report-history' + 'security' + 'set-approved-firmware' + 'set-bios-setting' + 'switch-branch' + 'sync-bkc' + 'unlock' + 'unblock-firmware' + 'update' + 'upgrade' + 'verify' + 'verify-update' + '--version' +) + +_fwupdmgr_opts=( + '--verbose' + '--offline' + '--allow-reinstall' + '--allow-older' + '--allow-branch-switch' + '--force' + '--assume-yes' + '--no-history' + '--no-unreported-check' + '--no-metadata-check' + '--no-reboot-check' + '--no-safety-check' + '--no-remote-check' + '--show-all' + '--sign' + '--filter' + '--disable-ssl-strict' + '--ipfs' + '--json' +) + +bios_get_opts=( + '--no-authenticate' + '--json' + '--verbose' +) + +bios_set_opts=( + '--no-reboot-check' + '--json' + '--verbose' +) + +_show_file_in_dir() +{ + local files + files="$(ls 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${files}" -- "$cur") ) +} + +_show_bios_get_modifiers() +{ + COMPREPLY+=( $(compgen -W '${bios_get_opts[@]}' -- "$cur") ) +} + +_show_bios_set_modifiers() +{ + COMPREPLY+=( $(compgen -W '${bios_set_opts[@]}' -- "$cur") ) +} + +_show_filters() +{ + local flags + flags="$(command fwupdtool get-device-flags 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${flags}" -- "$cur") ) +} + +_show_modifiers() +{ + COMPREPLY+=( $(compgen -W '${_fwupdmgr_opts[@]}' -- "$cur") ) +} + +_show_bios_settings() +{ + if ! command -v jq &> /dev/null; then + return 0 + fi + local attr + attr="$(command fwupdmgr get-bios-setting --json --no-authenticate 2>/dev/null | jq '.BiosSettings | .[] | .Name')" + COMPREPLY+=( $(compgen -W "${attr}" -- "$cur") ) +} + +_show_bios_settings_possible() +{ + if ! command -v jq &> /dev/null; then + return 0 + fi + local attr + attr="$(command fwupdmgr get-bios-setting "$1" --json --no-authenticate 2>/dev/null | jq '.BiosSettings | .[] | .BiosSettingPossibleValues | .[]')" + COMPREPLY+=( $(compgen -W "${attr}" -- "$cur") ) +} + +_show_device_ids() +{ + if ! command -v jq &> /dev/null; then + return 0 + fi + local description + description="$(command fwupdmgr get-devices --json 2>/dev/null | jq '.Devices | .[] | .DeviceId')" + COMPREPLY+=( $(compgen -W "${description}" -- "$cur") ) +} + +_show_release_versions() +{ + if ! command -v jq &> /dev/null; then + return 0 + fi + local description + description="$(command fwupdmgr get-releases "$1" --json 2>/dev/null | jq '.Releases[].Version')" + COMPREPLY+=( $(compgen -W "${description}" -- "$cur") ) +} + +_show_remotes() +{ + local remotes + remotes="$(command fwupdmgr get-remotes | command awk '/Remote ID/ { print $4 }')" + COMPREPLY+=( $(compgen -W "${remotes}" -- "$cur") ) +} + +_fwupdmgr() +{ + local cur prev command arg args + COMPREPLY=() + _get_comp_words_by_ref cur prev + _get_first_arg + _count_args + + case $prev in + --filter) + _show_filters + return 0 + ;; + esac + + case $arg in + activate|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update|get-updates|switch-branch|update|upgrade) + #device ID + if [[ "$args" = "2" ]]; then + _show_device_ids + fi + ;; + get-bios-settings|get-bios-setting) + #bios settings (no limit) + _show_bios_settings + _show_bios_get_modifiers + return 0 + ;; + set-bios-setting) + if [[ "$prev" = "--json" ]]; then + _show_file_in_dir "$prev" + return 0 + fi + count=$(($((args)) % 2)) + #allow setting a single bios setting at a time + if [[ $count == 0 ]]; then + _show_bios_settings + fi + #possible values (only works for enumeration though) + if [[ $count == 1 ]]; then + _show_bios_settings_possible "$prev" + return 0 + fi + _show_bios_set_modifiers + return 0 + ;; + get-details) + #find files + if [[ "$args" = "2" ]]; then + _filedir + fi + ;; + device-test) + #find files + if [[ "$args" = "2" ]]; then + _filedir + fi + ;; + install) + #device ID + if [[ "$args" = "2" ]]; then + _show_device_ids + #version + elif [[ "$args" = "3" ]]; then + _show_release_versions "$prev" + fi + ;; + local-install) + #find files + if [[ "$args" = "2" ]]; then + _filedir + #device ID or modifiers + elif [[ "$args" = "3" ]]; then + _show_device_ids + _show_modifiers + fi + ;; + modify-remote) + #find remotes + if [[ "$args" = "2" ]]; then + _show_remotes + #add key + elif [[ "$args" = "3" ]]; then + local keys + keys="$(command fwupdmgr get-remotes | command awk -v pattern="Remote ID:.*${prev}$" '$0~pattern{show=1; next}/Remote/{show=0}{gsub(/:.*/,"")}show')" + COMPREPLY+=( $(compgen -W "${keys}" -- "$cur") ) + fi + ;; + enable-remote) + #find remotes + if [[ "$args" = "2" ]]; then + _show_remotes + fi + ;; + disable-remote) + #find remotes + if [[ "$args" = "2" ]]; then + _show_remotes + fi + ;; + refresh) + #find first file + if [[ "$args" = "2" ]]; then + _filedir + #find second file + elif [[ "$args" = "3" ]]; then + _filedir + #find remote ID + elif [[ "$args" = "4" ]]; then + _show_remotes + fi + ;; + *) + #find first command + if [[ "$args" = "1" ]]; then + COMPREPLY=( $(compgen -W '${_fwupdmgr_cmd_list[@]}' -- "$cur") ) + fi + ;; + esac + + #modifiers + _show_modifiers + + return 0 +} + +complete -F _fwupdmgr fwupdmgr diff --git a/fwupd-1.8.6/data/bash-completion/fwupdtool b/fwupd-1.8.6/data/bash-completion/fwupdtool new file mode 100644 index 0000000000000000000000000000000000000000..071d7384c2d1bb6d9247547569b170059e6ccc43 --- /dev/null +++ b/fwupd-1.8.6/data/bash-completion/fwupdtool @@ -0,0 +1,180 @@ +_fwupdtool_cmd_list=( + 'activate' + 'build-firmware' + 'clear-history' + 'esp-list' + 'esp-mount' + 'esp-unmount' + 'firmware-build' + 'firmware-convert' + 'firmware-export' + 'firmware-extract' + 'firmware-parse' + 'firmware-sign' + 'firmware-patch' + 'get-bios-setting' + 'get-updates' + 'get-upgrades' + 'get-details' + 'get-firmware-types' + 'get-device-flags' + 'get-devices' + 'get-history' + 'get-plugins' + 'get-remotes' + 'get-topology' + 'hwids' + 'update' + 'upgrade' + 'install' + 'install-blob' + 'monitor' + 'reinstall' + 'security' + 'set-bios-setting' + 'switch-branch' + 'self-sign' + 'smbios-dump' + 'attach' + 'detach' + 'firmware-dump' + 'firmware-read' + 'refresh' + 'verify-update' + 'watch' + 'unbind-driver' + 'bind-driver' + 'export-hwids' +) + +_fwupdtool_opts=( + '--verbose' + '--allow-reinstall' + '--allow-older' + '--force' + '--show-all' + '--plugins' + '--prepare' + '--cleanup' + '--filter' + '--method' + '--disable-ssl-strict' + '--no-safety-check' + '--ignore-checksum' + '--ignore-vid-pid' + '--save-backends' +) + +_show_filters() +{ + local flags + flags="$(command fwupdtool get-device-flags 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${flags}" -- "$cur") ) +} + +_show_firmware_types() +{ + local firmware_types + firmware_types="$(command fwupdtool get-firmware-types 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${firmware_types}" -- "$cur") ) +} + +_show_device_ids() +{ + if ! command -v jq &> /dev/null; then + return 0 + fi + local description + description="$(command jq '.Devices | .[] | .DeviceId' @localstatedir@/cache/fwupd/devices.json 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${description}" -- "$cur") ) +} + +_show_plugins() +{ + if ! command -v jq &> /dev/null; then + return 0 + fi + local plugins + plugins="$(command fwupdtool get-plugins --json 2>/dev/null | jq '.Plugins | .[] | .Name')" + COMPREPLY+=( $(compgen -W "${plugins}" -- "$cur") ) +} + +_show_modifiers() +{ + COMPREPLY+=( $(compgen -W '${_fwupdtool_opts[@]}' -- "$cur") ) +} + +_fwupdtool() +{ + local cur prev command arg args + COMPREPLY=() + _get_comp_words_by_ref cur prev + _get_first_arg + _count_args + + case $prev in + --plugins) + _show_plugins + return 0 + ;; + --filter) + _show_filters + return 0 + ;; + esac + + case $arg in + get-details|install|install-blob|firmware-dump|firmware-read) + #find files + if [[ "$args" = "2" ]]; then + _filedir + #device ID + elif [[ "$args" = "3" ]]; then + _show_device_ids + fi + ;; + attach|detach|activate|verify-update|reinstall|get-updates) + #device ID + if [[ "$args" = "2" ]]; then + _show_device_ids + fi + ;; + firmware-parse|firmware-patch) + #find files + if [[ "$args" = "2" ]]; then + _filedir + #firmware_type + elif [[ "$args" = "3" ]]; then + _show_firmware_types + fi + ;; + firmware-convert) + #file in + if [[ "$args" = "2" ]]; then + _filedir + #file out + elif [[ "$args" = "3" ]]; then + _filedir + #firmware_type in + elif [[ "$args" = "4" ]]; then + _show_firmware_types + #firmware_type out + elif [[ "$args" = "5" ]]; then + _show_firmware_types + fi + ;; + *) + #find first command + if [[ "$args" = "1" ]]; then + COMPREPLY=( $(compgen -W '${_fwupdtool_cmd_list[@]}' -- "$cur") ) + fi + ;; + esac + + #modifiers + _show_modifiers + + return 0 +} + +complete -F _fwupdtool fwupdtool diff --git a/fwupd-1.8.6/data/bash-completion/meson.build b/fwupd-1.8.6/data/bash-completion/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..815b7bf70b6b47a0dbc80e9c0c20aebc4f8c2d11 --- /dev/null +++ b/fwupd-1.8.6/data/bash-completion/meson.build @@ -0,0 +1,23 @@ +if bashcomp.found() + completions_dir = bashcomp.get_variable(pkgconfig: 'completionsdir', + pkgconfig_define: bashcomp.version().version_compare('>= 2.10') ? ['datadir', datadir] : ['prefix', prefix], + ) + + con = configuration_data() + con.set('localstatedir', localstatedir) + + configure_file( + input: 'fwupdtool', + output: 'fwupdtool', + configuration: con, + install: true, + install_dir: completions_dir, + ) + + if build_daemon + install_data(['fwupdagent', 'fwupdmgr'], + install_dir: completions_dir, + ) + endif # build_daemon + +endif # bashcomp.found() diff --git a/fwupd-1.8.6/data/bios-settings.d/README.md b/fwupd-1.8.6/data/bios-settings.d/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0d9fba88a79f8e833a1d4a2d2207463f432e0802 --- /dev/null +++ b/fwupd-1.8.6/data/bios-settings.d/README.md @@ -0,0 +1,25 @@ +# BIOS Settings + +On supported machines fwupd can enforce BIOS settings policy so that a user's desired settings are configured at bootup +and prevent fwupd clients from changing them. + +## JSON policies + +A policy file can be created using `fwupdmgr`. First determine what settings you want to enforce by running: + +```shell +# fwupdmgr get-bios-settings +``` + +After you have identified settings, create a JSON payload by listing them on the command line. Any number of attributes can +be listed. +For example for the BIOS setting `WindowsUEFIFirmwareUpdate` you would create a policy file like this: + +```shell +# fwupdmgr get-bios-settings --json WindowsUEFIFirmwareUpdate > ~/foo.json +``` + +Now examine `~/foo.json` and modify the `BiosSettingCurrentValue` key to your desired value. + +Lastly place this policy file into `/etc/fwupd/bios-settings.d`. Any number of policies is supported, and they will be examined +in alphabetical order. The next time that fwupd is started it will load this policy and ensure that no fwupd clients change it. diff --git a/fwupd-1.8.6/data/bios-settings.d/meson.build b/fwupd-1.8.6/data/bios-settings.d/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..b0ff5b10689ccbc39d0f357397d6a051c02bc6c6 --- /dev/null +++ b/fwupd-1.8.6/data/bios-settings.d/meson.build @@ -0,0 +1,5 @@ +if build_standalone and host_machine.system() == 'linux' +install_data('README.md', + install_dir: join_paths(sysconfdir, 'fwupd', 'bios-settings.d') +) +endif diff --git a/fwupd-1.8.6/data/cfi.quirk b/fwupd-1.8.6/data/cfi.quirk new file mode 100644 index 0000000000000000000000000000000000000000..8a397981ed16a31d197bb42bfaee939dbb9744a7 --- /dev/null +++ b/fwupd-1.8.6/data/cfi.quirk @@ -0,0 +1,194 @@ +# No Manufacturer +[CFI\FLASHID_0020] +Name = M25PxxA/xx +CfiDeviceCmdChipErase = 0xC7 +CfiDeviceCmdSectorErase = 0x00 +[CFI\FLASHID_00BF] +Name = PCT/SST25VFxxx/xxxA +CfiDeviceCmdReadId = 0x90 +CfiDeviceCmdChipErase = 0x60 +CfiDeviceCmdSectorErase = 0x20 +[CFI\FLASHID_009D] +Name = PM25LDxxx +CfiDeviceCmdReadId = 0x90 +CfiDeviceCmdChipErase = 0xC7 +CfiDeviceCmdSectorErase = 0xD7 +[CFI\FLASHID_009D] +Name = PM25LVxxx +CfiDeviceCmdReadId = 0xAB +CfiDeviceCmdChipErase = 0xC7 +CfiDeviceCmdSectorErase = 0xD7 +[CFI\FLASHID_00EF] +Name = W25XxxBV/W25XxxCL +CfiDeviceCmdChipErase = 0xC7 +CfiDeviceCmdSectorErase = 0x20 + +# Fujitsu +[CFI\FLASHID_04] +Vendor = Fujitsu + +# Atmel +[CFI\FLASHID_1F] +Vendor = Atmel +[CFI\FLASHID_1F65] +Name = AT25F512A/B +CfiDeviceCmdReadId = 0x15 +CfiDeviceCmdChipErase = 0x62 +CfiDeviceCmdSectorErase = 0x00 +FirmwareSizeMax = 0x10000 + +# EON +[CFI\FLASHID_1C] +Vendor = EON +[CFI\FLASHID_1C31] +Name = EN25Fxx +CfiDeviceCmdChipErase = 0x60 +CfiDeviceCmdSectorErase = 0x20 + +# ST +[CFI\FLASHID_20] +Vendor = ST + +# Catalyst +[CFI\FLASHID_31] +Vendor = Catalyst + +# AMIC +[CFI\FLASHID_37] +Vendor = AMIC +[CFI\FLASHID_3730] +Name = A25Lxxx +CfiDeviceCmdChipErase = 0xc7 +CfiDeviceCmdSectorErase = 0x20 + +# SyncMOS +[CFI\FLASHID_40] +Vendor = SyncMOS + +# ESI +[CFI\FLASHID_4A] +Vendor = ESI + +# Alliance +[CFI\FLASHID_52] +Vendor = Alliance + +# Tenx +[CFI\FLASHID_5E] +Vendor = Tenx + +# Sanyo +[CFI\FLASHID_62] +Vendor = Sanyo + +# AMIC +[CFI\FLASHID_7F] +Vendor = AMIC +[CFI\FLASHID_7F9D21] +Name = A49LF040A +FirmwareSizeMax = 0x20000 + +[CFI\FLASHID_89] +Vendor = Intel + +# Elite +[CFI\FLASHID_8C] +Vendor = Elite + +# Texas Instruments +[CFI\FLASHID_97] +Vendor = Texas Instruments + +# PMC +[CFI\FLASHID_9D] +Vendor = PMC + +# Fudan +[CFI\FLASHID_A1] +Vendor = Fudan +[CFI\FLASHID_A131] +Name = FM25xxx +CfiDeviceBlockSize = 0x10000 +CfiDeviceSectorSize = 0x1000 +CfiDevicePageSize = 0x100 +CfiDeviceCmdBlockErase = 0xd8 +[CFI\FLASHID_A13110] +Name = FM25W04 +FirmwareSizeMax = 0x80000 +[CFI\FLASHID_A13111] +Name = FM25F01 +FirmwareSizeMax = 0x20000 + +# Hyundai +[CFI\FLASHID_AD] +Vendor = Hyundai + +# Sharp +[CFI\FLASHID_B0] +Vendor = Sharp + +# SST +[CFI\FLASHID_BF] +Vendor = SST + +# Macronix +[CFI\FLASHID_C2] +Vendor = Macronix +[CFI\FLASHID_C220] +Name = MX25Lxxx/xxxC/xxxE +CfiDeviceCmdChipErase = 0x60 +CfiDeviceCmdSectorErase = 0x20 +CfiDeviceCmdBlockErase = 0xd8 +CfiDeviceBlockSize = 0x10000 +CfiDeviceSectorSize = 0x1000 +CfiDevicePageSize = 0x100 +[CFI\FLASHID_C22012] +Name = MX25V2033F +FirmwareSizeMax = 0x40000 +[CFI\FLASHID_C22016] +Name = MX25L3236F +FirmwareSizeMax = 0x400000 +[CFI\FLASHID_C222] +Name = MX25Lxxx1E +CfiDeviceCmdChipErase = 0x60 +CfiDeviceCmdSectorErase = 0x20 + +# GigaDevice +[CFI\FLASHID_C8] +Vendor = GigaDevice +[CFI\FLASHID_C840] +Name = GD25Qxxx +CfiDeviceCmdChipErase = 0xC7 +CfiDeviceCmdSectorErase = 0x20 +CfiDeviceCmdBlockErase = 0xd8 +CfiDeviceBlockSize = 0x10000 +CfiDeviceSectorSize = 0x1000 +CfiDevicePageSize = 0x100 +[CFI\FLASHID_C84012] +Name = GD25Q20C +FirmwareSizeMax = 0x40000 +[CFI\FLASHID_C84016] +Name = GD25Q32C +FirmwareSizeMax = 0x400000 + +# Nantronics +[CFI\FLASHID_D5] +Vendor = Nantronics + +# Winbond +[CFI\FLASHID_DA] +Vendor = Winbond + +# Winbond (ex Nexcom) +[CFI\FLASHID_EF] +Vendor = Winbond +[CFI\FLASHID_EF3010] +Name = W25X05CL +FirmwareSizeMax = 0x10000 +[CFI\FLASHID_EF4018] +Name = W25Q128 +FirmwareSizeMax = 0x1000000 + +# Fidelix +[CFI\FLASHID_F8] +Vendor = Fidelix diff --git a/fwupd-1.8.6/data/daemon.conf b/fwupd-1.8.6/data/daemon.conf new file mode 100644 index 0000000000000000000000000000000000000000..5066ce1c26c0bb405f8b0f87e1c82f6cf99a8b53 --- /dev/null +++ b/fwupd-1.8.6/data/daemon.conf @@ -0,0 +1,74 @@ +[fwupd] + +# Allow blocking specific devices by their GUID +# Uses semicolons as delimiter +DisabledDevices= + +# Allow blocking specific plugins +# Uses semicolons as delimiter +DisabledPlugins=test;test_ble + +# 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= + +# Update the message of the day (MOTD) on device and metadata changes +UpdateMotd=true + +# For some plugins, enumerate only devices supported by metadata +EnumerateAllDevices=false + +# A list of firmware checksums that has been approved by the site admin +# If unset, all firmware is approved +ApprovedFirmware= + +# Allow blocking specific devices by their checksum, either SHA1 or SHA256 +# Uses semicolons as delimiter +BlockedFirmware= + +# Allowed URI schemes in the preference order; failed downloads from the first +# scheme will be retried with the next in order until no choices remain. +# +# If unset or no schemes are listed, the default will be: file,https,http,ipfs +UriSchemes= + +# Ignore power levels of devices when running updates +IgnorePower=false + +# Only support installing firmware signed with a trusted key +OnlyTrusted=true + +# Show private data like device serial numbers and instance IDs to clients +ShowDevicePrivate=true + +# UIDs that should marked as trusted +TrustedUids= + +# A host best known configuration is used when using `fwupdmgr sync` which can +# downgrade firmware to factory versions or upgrade firmware to a supported +# config level. e.g. `vendor-factory-2021q1` +HostBkc= + +# The EFI system partition (ESP) path used if UDisks is not available +# or if this partition is not mounted at /boot/efi, /boot, or /efi +#EspLocation= + +# these are only required when the SMBIOS or Device Tree data is invalid or missing +#Manufacturer= +#ProductName= +#ProductSku= +#Family= +#EnclosureKind= +#BaseboardProduct= +#BaseboardManufacturer= diff --git a/fwupd-1.8.6/data/device-tests/8bitdo-nes30pro.json b/fwupd-1.8.6/data/device-tests/8bitdo-nes30pro.json new file mode 100644 index 0000000000000000000000000000000000000000..fe198100f2933139248fae687b0e29e78786f388 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/8bitdo-nes30pro.json @@ -0,0 +1,17 @@ +{ + "name": "8BitDo NES30Pro", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/1cb9a0277f536ecd81ca1cea6fd80d60cdbbdcd8-8Bitdo-SFC30PRO_NES30PRO-4.01.cab", + "components": [ + { + "version": "4.01", + "guids": [ + "c6566b1b-0c6e-5d2e-9376-78c23ab57bf2" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/8bitdo-sf30pro.json b/fwupd-1.8.6/data/device-tests/8bitdo-sf30pro.json new file mode 100644 index 0000000000000000000000000000000000000000..3969d55a4ada855422c778ebd4d2ad24f5c9be84 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/8bitdo-sf30pro.json @@ -0,0 +1,17 @@ +{ + "name": "8BitDo SF30Pro", + "interactive": true, + "steps": [ + { + "url": "https://fwupd.org/downloads/3d3a65ee2e8581647fb09d752fa7e21ee1566481-8Bitdo-SF30_Pro-SN30_Pro-1.26.cab", + "components": [ + { + "version": "1.26", + "guids": [ + "269b3121-097b-50d8-b9ba-d1f64f9cd241" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/8bitdo-sfc30.json b/fwupd-1.8.6/data/device-tests/8bitdo-sfc30.json new file mode 100644 index 0000000000000000000000000000000000000000..edff91d5cc791f830562907afe0f3195d041ccf6 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/8bitdo-sfc30.json @@ -0,0 +1,17 @@ +{ + "name": "8BitDo SFC30", + "interactive": true, + "steps": [ + { + "url": "https://fwupd.org/downloads/fe066b57c69265f4cce8a999a5f8ab90d1c13b24-8Bitdo-SFC30_NES30_SFC30_SNES30-4.01.cab", + "components": [ + { + "version": "4.01", + "guids": [ + "a7fcfbaf-e9e8-59f4-920d-7691dc6c8699" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/aiaiai-h05.json b/fwupd-1.8.6/data/device-tests/aiaiai-h05.json new file mode 100644 index 0000000000000000000000000000000000000000..a32272b4b8e615e45a3f51a224a036b84bc2aea3 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/aiaiai-h05.json @@ -0,0 +1,18 @@ +{ + "name": "AIAIAI H05", + "interactive": true, + "runtime": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/84279d6bab52262080531acac701523604f3e649-AIAIAI-H05-1.6.cab", + "components": [ + { + "version": "1.6", + "guids": [ + "7e8318e1-27ae-55e4-a7a7-a35eff60e9bf" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/bizlink-no-sku-vli.json b/fwupd-1.8.6/data/device-tests/bizlink-no-sku-vli.json new file mode 100644 index 0000000000000000000000000000000000000000..376926664930b7efa5d5bc9f2bbbe965f0b0d86a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/bizlink-no-sku-vli.json @@ -0,0 +1,44 @@ +{ + "name": "BizLink Cayenne Hub [VL822+VL103]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/e0d6f62140663744f114d59b1ec48dd3e4743006bb06d0643efe178b8bdb2a04-BizLink-Cayenne_New.cab", + "components": [ + { + "name": "tier1", + "version": "106.83", + "guids": [ + "a0eff862-6b0b-5571-91ec-a0c4bb25b539" + ] + }, + { + "name": "tier3", + "version": "138.2.5.18", + "guids": [ + "a7927038-e2df-5209-88db-2e28bcd3cf8e" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/baf2f1a78334b7722d913ffa468240f728a09d91d0d2f755ff5515d4fed111c1-BizLink-Cayenne_Old.cab", + "components": [ + { + "name": "tier1", + "version": "06.83", + "guids": [ + "a0eff862-6b0b-5571-91ec-a0c4bb25b539" + ] + }, + { + "name": "tier3", + "version": "122.2.5.18", + "guids": [ + "a7927038-e2df-5209-88db-2e28bcd3cf8e" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/corsair-katar-pro-xt.json b/fwupd-1.8.6/data/device-tests/corsair-katar-pro-xt.json new file mode 100644 index 0000000000000000000000000000000000000000..52a334f490ca4d070df52226b96ff5676e1bc097 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/corsair-katar-pro-xt.json @@ -0,0 +1,30 @@ +{ + "name": "Corsair KATAR PRO XT Gaming Mouse", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/3bfde46a20bffd123ea4efd8eb7b59fb50d473ec9176735538e139a4e6b6e12c-corsair-bora-1.6.25.cab", + "components": [ + { + "version": "1.6.25", + "protocol": "com.corsair.bp", + "guids": [ + "b7b0728e-94e9-5ad5-999f-c6a359a9ddd4" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/7598356dbb3c40f16bc4cd314497ad0b983303e52311f95afc1d9aa8661a154d-corsair-bora-1.6.26.cab", + "components": [ + { + "version": "1.6.26", + "protocol": "com.corsair.bp", + "guids": [ + "b7b0728e-94e9-5ad5-999f-c6a359a9ddd4" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/corsair-sabre-pro.json b/fwupd-1.8.6/data/device-tests/corsair-sabre-pro.json new file mode 100644 index 0000000000000000000000000000000000000000..67325e3995f298b51199052c78b6b4b9981d0106 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/corsair-sabre-pro.json @@ -0,0 +1,30 @@ +{ + "name": "Corsair SABRE PRO Gaming Mouse", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/c5d729ea474b0c380641a5a86f05796dad6e74888b3eb207237f18fab9da8157-corsair-tongs-1.15.24.cab", + "components": [ + { + "version": "1.15.24", + "protocol": "com.corsair.bp", + "guids": [ + "9896f0c3-6682-5392-80f6-0ba462918645" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/5adc54e39596b5b941339d247ac01f6a40377bc58e6b1b87c49d60c8a7509f4e-corsair-tongs-1.15.25.cab", + "components": [ + { + "version": "1.15.25", + "protocol": "com.corsair.bp", + "guids": [ + "9896f0c3-6682-5392-80f6-0ba462918645" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/corsair-sabre-rgb-pro.json b/fwupd-1.8.6/data/device-tests/corsair-sabre-rgb-pro.json new file mode 100644 index 0000000000000000000000000000000000000000..1a69c160d9968c71d0f9e5052284057173f58a4b --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/corsair-sabre-rgb-pro.json @@ -0,0 +1,30 @@ +{ + "name": "Corsair SABRE RGB PRO Gaming Mouse", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/6a765b6f9cdd2ac91c43b0d6e6e0d256e1c575dae6da2e287d603a1d8e9c5b49-corsair-vise-1.15.29.cab", + "components": [ + { + "version": "1.15.29", + "protocol": "com.corsair.bp", + "guids": [ + "7eb05392-a5e0-529a-991b-1910a4bce8cf" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/ad9aef845359ba6c8425329df68ff92e299588af334be2bc867e3b6e690c4279-corsair-vise-1.15.30.cab", + "components": [ + { + "version": "1.15.30", + "protocol": "com.corsair.bp", + "guids": [ + "7eb05392-a5e0-529a-991b-1910a4bce8cf" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/dell-kh08p.json b/fwupd-1.8.6/data/device-tests/dell-kh08p.json new file mode 100644 index 0000000000000000000000000000000000000000..310a9f5bc8f35665a795bcc08ba9782429dfb64a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/dell-kh08p.json @@ -0,0 +1,28 @@ +{ + "name": "Dell KH08P [BCM5719]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/6cf165037a381eb29c183319e031def6b87e3ce955781ecf73f28751a1365db2-kh08p-bcm5719-0.4.62.cab", + "components": [ + { + "version": "0.4.62", + "guids": [ + "ec5b8a9e-973b-58cc-935b-8322fabaebe9" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/c786be1c525ad062c5af8983474a9412f83f5251efb767fe9cb414a3a124b8ce-kh08p-bcm5719-0.4.64.cab", + "components": [ + { + "version": "0.4.64", + "guids": [ + "ec5b8a9e-973b-58cc-935b-8322fabaebe9" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/dell-wd19tb.json b/fwupd-1.8.6/data/device-tests/dell-wd19tb.json new file mode 100644 index 0000000000000000000000000000000000000000..1f5db67ac7bb3a30899ae508ae591df56ff7561a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/dell-wd19tb.json @@ -0,0 +1,53 @@ +{ + "name": "Dell WD19TB Dock", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/c05bacfd8f73f30812559f14245b92a069c680caf300e961c78e00c985efe3e0-WD19FirmwareUpdateLinux_01.00.14.cab", + "components": [ + { + "name": "ec", + "version": "01.00.00.04", + "guids": [ + "cd357cf1-40b2-5d87-b8df-bb2dd82774aa" + ] + }, + { + "name": "mst", + "version": "05.04.03", + "guids": [ + "89fec0b6-6b76-5008-b82c-5e5c6c164007" + ] + }, + { + "name": "pkg", + "version": "01.00.14.01", + "guids": [ + "8ceeeffd-51b6-580c-9b75-69143227aff8" + ] + }, + { + "name": "tbt", + "version": "43.00", + "guids": [ + "c94770ca-1773-592c-b20a-e87243bc7cd0" + ] + }, + { + "name": "usb1", + "version": "01.21", + "guids": [ + "ac5b774c-b49d-566b-9255-85f0f7f8a4ed" + ] + }, + { + "name": "usb2", + "version": "01.47", + "guids": [ + "568ffa1e-a0db-5287-9ea3-872b60f7730b" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/fwupd-a3bu-xplained.json b/fwupd-1.8.6/data/device-tests/fwupd-a3bu-xplained.json new file mode 100644 index 0000000000000000000000000000000000000000..9ffb0cae1d811eeb600951a9220fdc0cea7fa53a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/fwupd-a3bu-xplained.json @@ -0,0 +1,28 @@ +{ + "name": "LVFS A3BU XPLAINED", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/f5bbeaba1037dce31dd12f349e8148ae35f98b61-a3bu-xplained123.cab", + "components": [ + { + "version": "1.23", + "guids": [ + "80478b9a-3643-5e47-ab0f-ed28abe1019d" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/24d838541efe0340bf67e1cc5a9b95526e4d3702-a3bu-xplained124.cab", + "components": [ + { + "version": "1.24", + "guids": [ + "80478b9a-3643-5e47-ab0f-ed28abe1019d" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/fwupd-at90usbkey.json b/fwupd-1.8.6/data/device-tests/fwupd-at90usbkey.json new file mode 100644 index 0000000000000000000000000000000000000000..f0363b5d2a1dacdb13e5c4432f6edff46c4e492a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/fwupd-at90usbkey.json @@ -0,0 +1,28 @@ +{ + "name": "LVFS AT90USBKEY", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/b6bef375597e848971f230cf992c9740f7bf5b92-at90usbkey123.cab", + "components": [ + { + "version": "1.23", + "guids": [ + "c1874c52-5f6a-5864-926d-ea84bcdc82ea" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/47807fd4a94a4d5514ac6bf7a73038e00ed63225-at90usbkey124.cab", + "components": [ + { + "version": "1.24", + "guids": [ + "c1874c52-5f6a-5864-926d-ea84bcdc82ea" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/google-servo-micro.json b/fwupd-1.8.6/data/device-tests/google-servo-micro.json new file mode 100644 index 0000000000000000000000000000000000000000..adc77aafd00884063c126e027a67628f0a8cc61c --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/google-servo-micro.json @@ -0,0 +1,28 @@ +{ + "name": "Google Servo Micro", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/3c3123b6eaa89d5469553b301210a9e4cb06efa98a1cb7c6847a1d10bb0a0c4b-servo_micro_v2.4.0.cab", + "components": [ + { + "version": "2.4.0", + "guids": [ + "13564257-c649-586d-b4e4-4f048d480f36" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/1dc362734f138e71fa838ac5503491d297715d7e9bccc0424a62c4a5c68526cc-servo_micro_v2.4.17.cab", + "components": [ + { + "version": "2.4.17", + "guids": [ + "13564257-c649-586d-b4e4-4f048d480f36" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/hp-dock-g5.json b/fwupd-1.8.6/data/device-tests/hp-dock-g5.json new file mode 100644 index 0000000000000000000000000000000000000000..7b4acb8802858315060971b30b59533ac7e14eea --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/hp-dock-g5.json @@ -0,0 +1,28 @@ +{ + "name": "HP USB-C Dock G5", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/eb866447bb755c00e748cce14918dcbfaec0ec123237daefce5876c769c2bf92-HP-USBC_DOCK_G5-V1.0.11.0.cab", + "components": [ + { + "version": "1.0.11.0", + "guids": [ + "9434f89a-3351-536d-a281-f70203326833" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/c15a0df7386812781d1f376fe54729e64f69b2a8a6c4b580914d4f6740e4fcc3-HP-USBC_DOCK_G5-V1.0.13.0.cab", + "components": [ + { + "version": "1.0.13.0", + "guids": [ + "9434f89a-3351-536d-a281-f70203326833" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/hughski-colorhug-plus.json b/fwupd-1.8.6/data/device-tests/hughski-colorhug-plus.json new file mode 100644 index 0000000000000000000000000000000000000000..e5313a3983b76daa04768711ac15e76649b9ddd9 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/hughski-colorhug-plus.json @@ -0,0 +1,30 @@ +{ + "name": "Hughski ColorHug Plus", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/5cbff92158331aeb10008ca36fa918a9637dde7bfe31de3e0523d14090be8977-fakedevice01_dfu.cab", + "components": [ + { + "version": "0.1", + "guids": [ + "dfbaaded-754b-5214-a5f2-46aa3331e8ce", + "f5b42624-ff57-5073-ba09-b7c9c04241be" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/8bc3afd07a0af3baaab8b19893791dd3972e8305-fakedevice02_dfu.cab", + "components": [ + { + "version": "0.2", + "guids": [ + "dfbaaded-754b-5214-a5f2-46aa3331e8ce", + "f5b42624-ff57-5073-ba09-b7c9c04241be" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/hughski-colorhug.json b/fwupd-1.8.6/data/device-tests/hughski-colorhug.json new file mode 100644 index 0000000000000000000000000000000000000000..96fef5e0e9b26d9c3d3b5de6a405c2461772824b --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/hughski-colorhug.json @@ -0,0 +1,28 @@ +{ + "name": "Hughski ColorHug", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/9a4e77009da7d3b5f15a1388afeb9e5d41a5a8ae-hughski-colorhug2-1.2.5.cab", + "components": [ + { + "version": "1.2.5", + "guids": [ + "40338ceb-b966-4eae-adae-9c32edfcc484" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/2a066c8a1bfbd99f161c867b4dbe7e51ac36fc2b16ef37b11d18419874fbcb6c-hughski-colorhug-1.2.6.cab", + "components": [ + { + "version": "1.2.6", + "guids": [ + "40338ceb-b966-4eae-adae-9c32edfcc484" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/hughski-colorhug2.json b/fwupd-1.8.6/data/device-tests/hughski-colorhug2.json new file mode 100644 index 0000000000000000000000000000000000000000..7c0e1db06cd39f770c90aacd58d80c207fd08dd7 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/hughski-colorhug2.json @@ -0,0 +1,28 @@ +{ + "name": "Hughski ColorHug2", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "components": [ + { + "version": "2.0.6", + "guids": [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7-hughski-colorhug2-2.0.7.cab", + "components": [ + { + "version": "2.0.7", + "guids": [ + "2082b5e0-7a64-478a-b1b2-e3404fab6dad" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/hyper-no-sku-vli.json b/fwupd-1.8.6/data/device-tests/hyper-no-sku-vli.json new file mode 100644 index 0000000000000000000000000000000000000000..20675fb2be5f7fa6775b351a8def184bb23c9b14 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/hyper-no-sku-vli.json @@ -0,0 +1,44 @@ +{ + "name": "Hyper USB-C Hub [VL817+VL103]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/ecfebc47d63a5319e35da39bd6124b80e90138a208bc9134c9d1b256b232a704-Hyper-USB-C_Hub.cab", + "components": [ + { + "name": "tier1", + "version": "90.83", + "guids": [ + "a476b1bb-9f8e-5ad4-a0ed-afadb52334fc" + ] + }, + { + "name": "tier3", + "version": "154.36.9.55", + "guids": [ + "0a120f99-b0f4-52af-9af9-e13155634370" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/1694cceda16068b24d7627caace4bd373ecae73aca891f610dec0fe5a4d1207c-Hyper-USB-C_Hub_Old.cab", + "components": [ + { + "name": "tier1", + "version": "80.83", + "guids": [ + "a476b1bb-9f8e-5ad4-a0ed-afadb52334fc" + ] + }, + { + "name": "tier3", + "version": "138.36.9.55", + "guids": [ + "0a120f99-b0f4-52af-9af9-e13155634370" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/jabra-speak-410.json b/fwupd-1.8.6/data/device-tests/jabra-speak-410.json new file mode 100644 index 0000000000000000000000000000000000000000..522e8a103d40f49acc91473ced7a6d248c5333db --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/jabra-speak-410.json @@ -0,0 +1,28 @@ +{ + "name": "Jabra Speak 410", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/eab97d7e745e372e435dbd76404c3929730ac082-Jabra-SPEAK_410-1.8.cab", + "components": [ + { + "version": "1.8", + "guids": [ + "1764c519-4723-5514-baf9-3b42970de487" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/50a03efc5df333a948e159854ea40e1a3786c34c-Jabra-SPEAK_410-1.11.cab", + "components": [ + { + "version": "1.11", + "guids": [ + "1764c519-4723-5514-baf9-3b42970de487" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/jabra-speak-510.json b/fwupd-1.8.6/data/device-tests/jabra-speak-510.json new file mode 100644 index 0000000000000000000000000000000000000000..c3b7bdbf1849ca76feefb757757a26e73b978de8 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/jabra-speak-510.json @@ -0,0 +1,28 @@ +{ + "name": "Jabra Speak 510", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/45f88c50e79cfd30b6599df463463578d52f2fe9-Jabra-SPEAK_510-2.10.cab", + "components": [ + { + "version": "2.10", + "guids": [ + "443b9b32-7603-5c3a-bb30-291a7d8d6dbd" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/c0523a98ef72508b5c7ddd687418b915ad5f4eb9-Jabra-SPEAK_510-2.14.cab", + "components": [ + { + "version": "2.14", + "guids": [ + "443b9b32-7603-5c3a-bb30-291a7d8d6dbd" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/jabra-speak-710.json b/fwupd-1.8.6/data/device-tests/jabra-speak-710.json new file mode 100644 index 0000000000000000000000000000000000000000..a985179f15fabb0d42cac1897a3a6a8daf19a4c7 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/jabra-speak-710.json @@ -0,0 +1,28 @@ +{ + "name": "Jabra Speak 710", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/d2910cdbc45cf172767d05e60d9e39a07a10d242-Jabra-SPEAK_710-1.10.cab", + "components": [ + { + "version": "1.10", + "guids": [ + "0c503ad9-4969-5668-81e5-a3748682fc16" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/a5c627ae42de4e5c3ae3df28977f480624f96f66-Jabra-SPEAK_710-1.28.cab", + "components": [ + { + "version": "1.28", + "guids": [ + "0c503ad9-4969-5668-81e5-a3748682fc16" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/lenovo-03x7168.json b/fwupd-1.8.6/data/device-tests/lenovo-03x7168.json new file mode 100644 index 0000000000000000000000000000000000000000..ef95fa4f721d3e9262cf73e56822134470cd40e8 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/lenovo-03x7168.json @@ -0,0 +1,28 @@ +{ + "name": "Lenovo USB-C to HDMI (with power) [VL100]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/9ef0bb237baf8043f3ff16db5a8dd23bfd63fc24ea0eca2a6af3285792aa283b-Lenovo-USB-C_to_HDMI.cab", + "components": [ + { + "version": "130.4.22.1", + "guids": [ + "eca16353-163f-570b-9e0a-8329045fdcff" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/5b06a36aa0f2b99fc33f43a26a553d95c2d8ac46a7f5a2e45a840570456fe29b-Lenovo-USB-C_to_HDMI.cab", + "components": [ + { + "version": "130.4.23.1", + "guids": [ + "eca16353-163f-570b-9e0a-8329045fdcff" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/lenovo-03x7605.json b/fwupd-1.8.6/data/device-tests/lenovo-03x7605.json new file mode 100644 index 0000000000000000000000000000000000000000..f2488696e39a490d170cf741e073f5fa8b83e9c2 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/lenovo-03x7605.json @@ -0,0 +1,28 @@ +{ + "name": "Lenovo USB-C to HDMI (no power) [VL103]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/61b393d8e27503746bae9a16dccf54929b9f1a0d1b5106334933a7313596c00c-Lenovo-USB-C_to_HDMI.cab", + "components": [ + { + "version": "153.84.6.1", + "guids": [ + "d0909dd3-d140-5da8-85fd-4aa5b2dcee5d" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/8be7eea7db239766cf92adf2145138c9929fd953d6d36e39d60c9dd6425638de-Lenovo-USB-C_to_HDMI.cab", + "components": [ + { + "version": "153.84.7.1", + "guids": [ + "d0909dd3-d140-5da8-85fd-4aa5b2dcee5d" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/lenovo-03x7608-vli.json b/fwupd-1.8.6/data/device-tests/lenovo-03x7608-vli.json new file mode 100644 index 0000000000000000000000000000000000000000..660d7cd53342fc207d968257e9fb68740c476e2e --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/lenovo-03x7608-vli.json @@ -0,0 +1,25 @@ +{ + "name": "Lenovo Travel Hub Gen2 [VL817]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/ebfda3c96543d6d7ad97a972344f4d34a14549c535095bfbb1860115a88e6ff6-Lenovo-TravalHub-2020-12-11-153442.cab", + "components": [ + { + "name": "tier1", + "version": "4.74", + "guids": [ + "3fc55e47-f57a-55bd-9f41-ac60280bd689" + ] + }, + { + "name": "tier3", + "version": "138.04.72.18", + "guids": [ + "3ae6610b-5c33-5714-96e3-05735eb9b2a5" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/lenovo-03x7609-cxaudio.json b/fwupd-1.8.6/data/device-tests/lenovo-03x7609-cxaudio.json new file mode 100644 index 0000000000000000000000000000000000000000..6d6828fc0d6cd1c068ecc45be21f1914b7049e4b --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/lenovo-03x7609-cxaudio.json @@ -0,0 +1,19 @@ +{ + "name": "Lenovo USB-C Dock Gen2 (CXAUDIO)", + "interactive": false, + "repeat": 2, + "steps": [ + { + "url": "https://fwupd.org/downloads/2e0bf8aaf9c63ca11cfe3444d032277c21ec0d678e5963123a8b33e5dcd37d99-Lenovo-ThinkPad-USBCGen2Dock-Firmware-49-0E-14.cab", + "components": [ + { + "name": "cxaudio", + "version": "49-0E-14", + "guids": [ + "dbb8d54c-42e6-5215-b7ac-1df16872bb06" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/lenovo-40au0065-vli.json b/fwupd-1.8.6/data/device-tests/lenovo-40au0065-vli.json new file mode 100644 index 0000000000000000000000000000000000000000..5395729bf8864ff15db87ab4593100b0aed0fd80 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/lenovo-40au0065-vli.json @@ -0,0 +1,58 @@ +{ + "name": "Lenovo USB-C Mini Dock", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/3b183e21869e4fce8bc81b3357de2fd1ee47b35e272e26169ebaee88faa39e03-Lenovo-Mini_Dock_New.cab", + "components": [ + { + "name": "tier1", + "version": "4.154", + "guids": [ + "f281c1df-c3d5-5f8a-984d-e9548ffc95fe" + ] + }, + { + "name": "tier2", + "version": "4.43", + "guids": [ + "d636c717-44c4-5fcf-9d7f-b96f9c5f6608" + ] + }, + { + "name": "tier3", + "version": "138.4.24.38", + "guids": [ + "3ae6610b-5c33-5714-96e3-05735eb9b2a5" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/f55a307af1dc66d46bc12460ced828b31b2ee2a78c1d58d4f474958c2c6d134e-Lenovo-Mini_Dock_Old.cab", + "components": [ + { + "name": "tier1", + "version": "4.94", + "guids": [ + "f281c1df-c3d5-5f8a-984d-e9548ffc95fe" + ] + }, + { + "name": "tier2", + "version": "4.33", + "guids": [ + "d636c717-44c4-5fcf-9d7f-b96f9c5f6608" + ] + }, + { + "name": "tier3", + "version": "138.4.23.38", + "guids": [ + "3ae6610b-5c33-5714-96e3-05735eb9b2a5" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/lenovo-GX90T33021-vli.json b/fwupd-1.8.6/data/device-tests/lenovo-GX90T33021-vli.json new file mode 100644 index 0000000000000000000000000000000000000000..7e7f43600a61fd72804826b02c1ecc66b855ec65 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/lenovo-GX90T33021-vli.json @@ -0,0 +1,30 @@ +{ + "name": "Lenovo Travel Hub 1in3 [VL211]", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/2004603b40bd529d85c2fcfcf9d50d77b47229e6564ef0ea9c03633bdccff94d-Lenovo-Travel_Hub_1in3_New.cab", + "components": [ + { + "name": "tier1", + "version": "44.33", + "guids": [ + "7636b85e-d79f-5d30-a329-458957958b88" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/5fb4f4dce233626558806c2b7474d3b5ed4f500f6013aa3b376a54322642d449-Lenovo-Travel_Hub_1in3_Old.cab", + "components": [ + { + "name": "tier1", + "version": "04.33", + "guids": [ + "7636b85e-d79f-5d30-a329-458957958b88" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-bolt-receiver.json b/fwupd-1.8.6/data/device-tests/logitech-bolt-receiver.json new file mode 100644 index 0000000000000000000000000000000000000000..2bf161e14a235b2ecd4278558a01e07dd3d24684 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-bolt-receiver.json @@ -0,0 +1,42 @@ +{ + "name": "Logitech Bolt receiver", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/003dcc6c2c5437ab093e0c5c5fc1c4e5ab6274d3060505fe0ef843fd13e7200c-Logitech-MPR05-MPR05.00_B0008.cab", + "components": [ + { + "version": "MPR05.00_B0008", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "af1404c4-f038-5b3e-92b0-09bf4aa84f1c" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/c35ade1237da4e1ff90d031cc2b2218c32ee53c01b92b014aae2d2226aed2e35-Logitech-MPR05-MPR05.00_B0009.cab", + "components": [ + { + "version": "MPR05.00_B0009", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "af1404c4-f038-5b3e-92b0-09bf4aa84f1c" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/f1e3ba268ae4e1d3c029392d4f203a976970b162521296a2e9a9a31f314c0c46-Logitech-MPR05-MPR05.01_B0010.cab", + "components": [ + { + "version": "MPR05.01_B0010", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "af1404c4-f038-5b3e-92b0-09bf4aa84f1c" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-k780.json b/fwupd-1.8.6/data/device-tests/logitech-k780.json new file mode 100644 index 0000000000000000000000000000000000000000..a360f858fa70a41ac4a4158e04e93f47fa7727fa --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-k780.json @@ -0,0 +1,28 @@ +{ + "name": "Logitech K780", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/454f1034f5efeb531163d90852ef33afd3fafeeb-Logitech-K780-MPK01.02_B0021.cab", + "components": [ + { + "version": "MPK01.02_B0021", + "guids": [ + "3932ba15-2bbe-5bbb-817e-6c74e7088509" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/b0dffe84c6d3681e7ae5f27509781bc1cf924dd7-Logitech-K780-MPK01.03_B0024.cab", + "components": [ + { + "version": "MPK01.03_B0024", + "guids": [ + "3932ba15-2bbe-5bbb-817e-6c74e7088509" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-m650.json b/fwupd-1.8.6/data/device-tests/logitech-m650.json new file mode 100644 index 0000000000000000000000000000000000000000..9c19830ed301abe00f2642cf9f6122ff97db832a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-m650.json @@ -0,0 +1,30 @@ +{ + "name": "Logitech M650", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/6531c7a26d54f9dacbc24f81e8b26e37dd630fe22a417cd2ce6e13ea3b6fd105-Logitech-RBM16-RBM16.00_B0009.cab", + "components": [ + { + "version": "RBM16.00_B0009", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "bb3fe644-ed3c-55a4-a506-191e65974b04" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/422d8c719d859b4ef321d95aa9e21f8d0d899ac924b9ca3ed76b1d745224e8e4-Logitech-RBM16-RBM16.00_B0010.cab", + "components": [ + { + "version": "RBM16.00_B0010", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "bb3fe644-ed3c-55a4-a506-191e65974b04" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-m750.json b/fwupd-1.8.6/data/device-tests/logitech-m750.json new file mode 100644 index 0000000000000000000000000000000000000000..a5cd35373ccb4a0912e4aa56e07f8b9548693d0b --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-m750.json @@ -0,0 +1,17 @@ +{ + "name": "Logitech M750", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/597a12cca95b9dcf19e82e5add970a45bf658de8c82dc329cc271a6c50d68752-Logitech-RBM18-RBM18.00_B0010.cab", + "components": [ + { + "version": "RBM18.00_B0010", + "guids": [ + "b0904956-9081-5ce4-9490-bee057a2d577" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-mr0077.json b/fwupd-1.8.6/data/device-tests/logitech-mr0077.json new file mode 100644 index 0000000000000000000000000000000000000000..4518c08f3182fae6750dec291beddefb81f0f598 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-mr0077.json @@ -0,0 +1,30 @@ +{ + "name": "Logitech MR0077", + "interactive": true, + "steps": [ + { + "url": "https://fwupd.org/downloads/7b86a6cb5747607f38e78259734dcf0d1afc02d2116b7386ac00c15e70eece72-Logitech-RBM14-RBM14_00_B0007.cab", + "components": [ + { + "version": "RBM14.00_B0007", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "b2b50d12-c3df-5980-b5e9-6cc6c34b3037" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/1cec33151e0f206df9b353a56e318adebb9a9ba9f330b452336eef169f5b1f3c-Logitech-RBM14-RBM14_00_B0008.cab", + "components": [ + { + "version": "RBM14.00_B0008", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "b2b50d12-c3df-5980-b5e9-6cc6c34b3037" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-rqr12-signed.json b/fwupd-1.8.6/data/device-tests/logitech-rqr12-signed.json new file mode 100644 index 0000000000000000000000000000000000000000..d568697e8b99cc6b1ce0dc78abc9e10ce4047973 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-rqr12-signed.json @@ -0,0 +1,19 @@ +{ + "name": "Logitech Unifying Receiver (RQR12 SIGNED)", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/2443cef8b1dae48751d3c60dab22210733e57036-Logitech-Unifying-RQR12.11_B0032.cab", + "components": [ + { + "version": "RQR12.11_B0032", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "9d131a0c-a606-580f-8eda-80587250b8d6", + "d637baf7-3ab5-502a-8169-2545302e44e2" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-rqr12.json b/fwupd-1.8.6/data/device-tests/logitech-rqr12.json new file mode 100644 index 0000000000000000000000000000000000000000..b51b49b75c1c6cc1bd25064427ecd0060c71e523 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-rqr12.json @@ -0,0 +1,30 @@ +{ + "name": "Logitech Unifying Receiver (RQR12)", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/6e5ab5961ec4c577bff198ebb465106e979cf686-Logitech-Unifying-RQR12.05_B0028.cab", + "components": [ + { + "version": "RQR12.05_B0028", + "protocol": "com.logitech.unifying", + "guids": [ + "9d131a0c-a606-580f-8eda-80587250b8d6" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/938fec082652c603a1cdafde7cd25d76baadc70d-Logitech-Unifying-RQR12.07_B0029.cab", + "components": [ + { + "version": "RQR12.07_B0029", + "protocol": "com.logitech.unifying", + "guids": [ + "9d131a0c-a606-580f-8eda-80587250b8d6" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-rqr24-signed.json b/fwupd-1.8.6/data/device-tests/logitech-rqr24-signed.json new file mode 100644 index 0000000000000000000000000000000000000000..9f1b3794a328cb0f390b1be6bf12235060748a40 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-rqr24-signed.json @@ -0,0 +1,21 @@ +{ + "name": "Logitech Unifying Receiver (RQR24 SIGNED)", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/b30e729cc544c711a6ad947cef9ce6614b394a7b-Logitech-Unifying-RQR24.11_B0036.cab", + "components": [ + { + "version": "RQR24.11_B0036", + "protocol": "com.logitech.unifyingsigned", + "guids": [ + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + "87fd7145-3913-50c8-bfcb-86f85006d7d1", + "111c9951-f819-5c48-93ef-205a8f8b96c1", + "40410bd7-57eb-5c82-9eac-abf893861221" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/logitech-rqr24.json b/fwupd-1.8.6/data/device-tests/logitech-rqr24.json new file mode 100644 index 0000000000000000000000000000000000000000..94f9cc0d255f31cff67c6d39b844b32a228c8b40 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/logitech-rqr24.json @@ -0,0 +1,30 @@ +{ + "name": "Logitech Unifying Receiver (RQR24)", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/82b90b2614a9a4d0aced1ab8a4a99e228c95585c-Logitech-Unifying-RQ024.03_B0027.cab", + "components": [ + { + "version": "RQR24.03_B0027", + "protocol": "com.logitech.unifying", + "guids": [ + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/4511b9b0d123bdbe8a2007233318ab215a59dfe6-Logitech-Unifying-RQR24.05_B0029.cab", + "components": [ + { + "version": "RQR24.05_B0029", + "protocol": "com.logitech.unifying", + "guids": [ + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/meson.build b/fwupd-1.8.6/data/device-tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..beb51e84b39cf12f743e7c2c9bf402a00a1f7a06 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/meson.build @@ -0,0 +1,47 @@ +install_data([ + '8bitdo-nes30pro.json', + '8bitdo-sf30pro.json', + '8bitdo-sfc30.json', + 'aiaiai-h05.json', + 'bizlink-no-sku-vli.json', + 'corsair-katar-pro-xt.json', + 'corsair-sabre-pro.json', + 'corsair-sabre-rgb-pro.json', + 'dell-kh08p.json', + 'dell-wd19tb.json', + 'fwupd-a3bu-xplained.json', + 'fwupd-at90usbkey.json', + 'hp-dock-g5.json', + 'hughski-colorhug2.json', + 'hughski-colorhug.json', + 'hughski-colorhug-plus.json', + 'hyper-no-sku-vli.json', + 'jabra-speak-410.json', + 'jabra-speak-510.json', + 'jabra-speak-710.json', + 'lenovo-03x7168.json', + 'lenovo-03x7605.json', + 'lenovo-03x7608-vli.json', + 'lenovo-03x7609-cxaudio.json', + 'lenovo-40au0065-vli.json', + 'lenovo-GX90T33021-vli.json', + 'logitech-bolt-receiver.json', + 'logitech-k780.json', + 'logitech-m650.json', + 'logitech-m750.json', + 'logitech-mr0077.json', + 'logitech-rqr12.json', + 'logitech-rqr12-signed.json', + 'logitech-rqr24.json', + 'logitech-rqr24-signed.json', + 'nordic-hid-nrf52840-mcuboot.json', + 'realtek-rts5423.json', + 'realtek-rts5855.json', + 'synaptics-prometheus.json', + 'system76-thelio.json', + 'ugreen-cm260.json', + 'wacom-intuos-bt-m.json', + 'wacom-intuos-bt-s.json', + ], + install_dir: join_paths(datadir, 'fwupd', 'device-tests'), +) diff --git a/fwupd-1.8.6/data/device-tests/nordic-hid-nrf52840-mcuboot.json b/fwupd-1.8.6/data/device-tests/nordic-hid-nrf52840-mcuboot.json new file mode 100644 index 0000000000000000000000000000000000000000..4ca02e307084020314f10cca8a9b1bc0ea10d6be --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/nordic-hid-nrf52840-mcuboot.json @@ -0,0 +1,28 @@ +{ + "name": "nRF52840 DK (nRF52 Desktop) MCUBoot variant", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/904ff137256218bc617661bb4fc2bfddad19522c7e54773d1fd0290b54adfcee-nordic-nrf52840dk-mcuboot-0.0.0_2.cab", + "components": [ + { + "version": "0.0.0.2", + "guids": [ + "43b38427-fdf5-5400-a23c-f3eb7ea00e7c" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/2eb21f9439f0c4928c14548ff5c5c73ad6590d23fa704c58c4afa88168bcea90-nordic-nrf52840dk-mcuboot-0.0.0_3.cab", + "components": [ + { + "version": "0.0.0.3", + "guids": [ + "43b38427-fdf5-5400-a23c-f3eb7ea00e7c" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/realtek-rts5423.json b/fwupd-1.8.6/data/device-tests/realtek-rts5423.json new file mode 100644 index 0000000000000000000000000000000000000000..8a753d6dc74bc4dfebc604f5743cec4f5bbfa33f --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/realtek-rts5423.json @@ -0,0 +1,28 @@ +{ + "name": "Realtek 4-Port USB Hub", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/460a18d93f5d6118908d8437009ebbd6eceb3c6a0cdfbaf31f8a96df008564da-Realtek-RTS5423-1.56.cab", + "components": [ + { + "version": "1.56", + "guids": [ + "b2d2fae3-1546-5d16-a9af-ac117a255a91" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/659e721b0efbe2dfc003d3d30ea5b771c00113cc9a162333ee5a8918c4515f69-Realtek-RTS5423-1.57.cab", + "components": [ + { + "version": "1.57", + "guids": [ + "b2d2fae3-1546-5d16-a9af-ac117a255a91" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/realtek-rts5855.json b/fwupd-1.8.6/data/device-tests/realtek-rts5855.json new file mode 100644 index 0000000000000000000000000000000000000000..c19ff22b1bd0d66455832fb86aba13603a0cef40 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/realtek-rts5855.json @@ -0,0 +1,28 @@ +{ + "name": "Realtek RTS5855 Webcam", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/ff989c4b71c92a4a217dfb2f82c1c87691b8eb31-rts5855_v0.4.cab", + "components": [ + { + "version": "0.4", + "guids": [ + "9829f051-47f5-55e7-87dc-a49cf55602e2" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/ed5c411d6b74c363209f408f87618fa5c31b50ab-v0.3.cab", + "components": [ + { + "version": "0.3", + "guids": [ + "9829f051-47f5-55e7-87dc-a49cf55602e2" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/steelseries-aerox-3-wireless.json b/fwupd-1.8.6/data/device-tests/steelseries-aerox-3-wireless.json new file mode 100644 index 0000000000000000000000000000000000000000..6f713a4ec33a4e69f918e92168d1c845ba7b9a1a --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/steelseries-aerox-3-wireless.json @@ -0,0 +1,56 @@ +{ + "name": "Steelseries Aerox 3 Wireless", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/dee6986d0faf769bf57869ee2b61118c8142e4a0017dbfc3bb0da24fb6dcfb06-SteelSeries_Aerox_3_Wireless_Dongle_1.2.17.cab", + "components": [ + { + "version": "1.2.17", + "guids": [ + "291b0582-8d08-5714-88db-986911c744d9", + "df0cb7f0-be88-532f-87f9-65662da376b9" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/35883a1f60f9a171451ccd8ca9e3d89cfaedd99cc1325410465e30d180bcb47a-SteelSeries_Aerox_3_Wireless_Mouse_1.8.1.cab", + "components": [ + { + "version": "1.8.1", + "guids": [ + "541b2713-d367-5240-a443-bed07f09ffbf", + "4f3fac7c-981b-58cc-bb58-8a3b90f31c9c", + "ac7d7cbb-7ef5-5466-ab3d-e70ced97ee61" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/fb8e8253a6c4426233ff88a58ae9323f1a7b9002fe6f68541c2e886cded04d38-SteelSeries_Aerox_3_Wireless_Dongle_1.3.1.cab", + "components": [ + { + "version": "1.3.1", + "guids": [ + "291b0582-8d08-5714-88db-986911c744d9", + "df0cb7f0-be88-532f-87f9-65662da376b9" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/60627b15b7c1499fcd6a882ef02743f358812fcfc9ed3d1b200f55b91c554796-SteelSeries_Aerox_3_Wireless_Mouse_1.9.3.cab", + "components": [ + { + "version": "1.9.3", + "guids": [ + "541b2713-d367-5240-a443-bed07f09ffbf", + "4f3fac7c-981b-58cc-bb58-8a3b90f31c9c", + "ac7d7cbb-7ef5-5466-ab3d-e70ced97ee61" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/steelseries-rival-3-wireless.json b/fwupd-1.8.6/data/device-tests/steelseries-rival-3-wireless.json new file mode 100644 index 0000000000000000000000000000000000000000..f51bc71997328852dfb7b1e9ae22e7eb582c5966 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/steelseries-rival-3-wireless.json @@ -0,0 +1,28 @@ +{ + "name": "Steelseries Rival 3 Wireless", + "interactive": true, + "steps": [ + { + "url": "https://fwupd.org/downloads/fe3775d33b41898e69f06698ed03779a158d99b9716a9b1c0ee7e3051ab857c3-SteelSeries_Rival_3_Wireless_1.0.cab", + "components": [ + { + "version": "1.0", + "guids": [ + "73e6daa7-d5a1-567d-a0ff-01514f8498b1" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/30c6979b393fee0bbf27695fa1b36c1fa8ac276c7744a8e3845a859e79fb0c94-SteelSeries_Rival_3_Wireless_1.4.cab", + "components": [ + { + "version": "1.4", + "guids": [ + "73e6daa7-d5a1-567d-a0ff-01514f8498b1" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/synaptics-prometheus.json b/fwupd-1.8.6/data/device-tests/synaptics-prometheus.json new file mode 100644 index 0000000000000000000000000000000000000000..d0573d151eae2426a8a0947710d8b7c7c87d286d --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/synaptics-prometheus.json @@ -0,0 +1,17 @@ +{ + "name": "Prometheus Fingerprint Reader", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/7c260a13ea6df444f7a1fa8fa2bf431876a8c7203b6aeac371564aad30756ff1-Synaptics-Prometheus-10.01.3121519.cab", + "components": [ + { + "version": "10.01.3121519", + "guids": [ + "8088f861-6318-5b1e-9ce4-fbddbedb09ac" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/system76-thelio.json b/fwupd-1.8.6/data/device-tests/system76-thelio.json new file mode 100644 index 0000000000000000000000000000000000000000..63849317f529cee00e3df5b7e6d10a721faa456c --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/system76-thelio.json @@ -0,0 +1,42 @@ +{ + "name": "System76 Thelio Io", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/ed423e586cdd35d41f0dd708ea8e5b5b97c54d48eef7e23a02d9a088b231b3fd-thelio-io_0.0.0.cab", + "components": [ + { + "version": "0.0.0", + "protocol": "org.usb.dfu", + "guids": [ + "fdac0b40-51c6-591b-a049-e9cfc05e9271" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/1be9bcfa7b5f0db2bca927505f82d8829be5882ca295e33af492d0e1fdacc162-thelio-io_1.0.0.cab", + "components": [ + { + "version": "1.0.0", + "protocol": "org.usb.dfu", + "guids": [ + "fdac0b40-51c6-591b-a049-e9cfc05e9271" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/63d4a480162b729fc57ff7c92c1e2254540f43d6-thelio-io_1.0.2.cab", + "components": [ + { + "version": "1.0.2", + "protocol": "org.usb.dfu", + "guids": [ + "fdac0b40-51c6-591b-a049-e9cfc05e9271" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/ugreen-cm260.json b/fwupd-1.8.6/data/device-tests/ugreen-cm260.json new file mode 100644 index 0000000000000000000000000000000000000000..28e35ad9e1106ab9b5d1a793502545be62eea443 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/ugreen-cm260.json @@ -0,0 +1,28 @@ +{ + "name": "Ugreen CM260", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/53cd390673287a99e5d4cc4cf30b5fade0f5e708fe0b1bd424bf36467642d76c-Ugreen-CM260-7.2.1.0.cab", + "components": [ + { + "version": "7.2.1.0", + "guids": [ + "7afc5bff-be55-5e95-81ca-584f13207b1d" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/eec2c6216fa86077504587633fc8d018a366a1cfe3df8dd9c575c2e8ef0ef423-Ugreen-CM260-7.2.2.0.cab", + "components": [ + { + "version": "7.2.1.0", + "guids": [ + "7afc5bff-be55-5e95-81ca-584f13207b1d" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/wacom-intuos-bt-m.json b/fwupd-1.8.6/data/device-tests/wacom-intuos-bt-m.json new file mode 100644 index 0000000000000000000000000000000000000000..bcf88e65322a6827a98b5acb1877fc5efe1573e3 --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/wacom-intuos-bt-m.json @@ -0,0 +1,30 @@ +{ + "name": "Wacom Intuos BT-M", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/d16b682de56c42b134f1e5af7f9926c62dc246df851648e49aa1d1d0e5b38532-Wacom-Intuos_BT-M_MainFW-1.66.cab", + "components": [ + { + "name": "main", + "version": "1.66", + "guids": [ + "edf56833-dbe5-56ca-a651-734b01bb02ba" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/1ee4f3dc9fd08acd4c6bc833b25e7061f85ddd40122148d66940cd3ddd748920-Wacom-Intuos_BT-M_BluetoothFW-1.12.cab", + "components": [ + { + "name": "bluetooth", + "version": "1.12", + "guids": [ + "230fb992-35c7-56b1-8236-7f5674a04153" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/device-tests/wacom-intuos-bt-s.json b/fwupd-1.8.6/data/device-tests/wacom-intuos-bt-s.json new file mode 100644 index 0000000000000000000000000000000000000000..97ee8153b4b12d63d270ecabfd1b907722750b1f --- /dev/null +++ b/fwupd-1.8.6/data/device-tests/wacom-intuos-bt-s.json @@ -0,0 +1,44 @@ +{ + "name": "Wacom Intuos BT-S", + "interactive": false, + "steps": [ + { + "url": "https://fwupd.org/downloads/0d9314e86d28f78f5dc37d71c5cc5205b681334fe64b28f1ec95b5eedc6e1ea6-Wacom-CTL-4100WL-1.10.cab", + "components": [ + { + "name": "bluetooth", + "version": "1.12", + "guids": [ + "e317b626-4c1d-5892-8b4c-aafec894a4c9" + ] + }, + { + "name": "main", + "version": "1.10", + "guids": [ + "dc80ba55-c5f7-5195-b6f4-23c2940bcaec" + ] + } + ] + }, + { + "url": "https://fwupd.org/downloads/cfec6515263c0d358d40d7b8fb6472214015225210dfa2db8dd307e896f03dcf-Wacom-CTL-4100WL-1.11.cab", + "components": [ + { + "name": "bluetooth", + "version": "1.12", + "guids": [ + "e317b626-4c1d-5892-8b4c-aafec894a4c9" + ] + }, + { + "name": "main", + "version": "1.11", + "guids": [ + "dc80ba55-c5f7-5195-b6f4-23c2940bcaec" + ] + } + ] + } + ] +} diff --git a/fwupd-1.8.6/data/fish-completion/fwupdmgr.fish b/fwupd-1.8.6/data/fish-completion/fwupdmgr.fish new file mode 100644 index 0000000000000000000000000000000000000000..e2746ac15b05c97d39466e16bcdb3591fac0ae7c --- /dev/null +++ b/fwupd-1.8.6/data/fish-completion/fwupdmgr.fish @@ -0,0 +1,80 @@ +function __fish_fwupdmgr_devices --description 'Get device IDs used by fwupdmgr' + set -l ids (fwupdmgr get-devices | string replace -f -r '.*Device ID:\s*(.*)' '$1') + set -l names (fwupdmgr get-devices | string replace -f -r '.*─(.*):$' '$1') + for i in (seq (count $ids)) + echo -e "$ids[$i]\t$names[$i]" + end +end + +function __fish_fwupdmgr_remotes --description 'Get remote IDs used by fwupdmgr' + fwupdmgr get-remotes | string replace -f -r '.*Remote ID:\s*(.*)' '$1' +end + + +# complete options +complete -c fwupdmgr -s h -l help -d 'Show help options' +complete -c fwupdmgr -s v -l verbose -d 'Show extra debugging information' +complete -c fwupdmgr -l version -d 'Show client and daemon versions' +complete -c fwupdmgr -l offline -d 'Schedule installation for next reboot when possible' +complete -c fwupdmgr -l allow-reinstall -d 'Allow reinstalling existing firmware versions' +complete -c fwupdmgr -l allow-older -d 'Allow downgrading firmware versions' +complete -c fwupdmgr -l allow-branch-switch -d 'Allow switching firmware branch' +complete -c fwupdmgr -l force -d 'Force the action by relaxing some runtime checks' +complete -c fwupdmgr -s y -l assume-yes -d 'Answer yes to all questions' +complete -c fwupdmgr -l sign -d 'Sign the uploaded data with the client certificate' +complete -c fwupdmgr -l no-unreported-check -d 'Do not check for unreported history' +complete -c fwupdmgr -l no-metadata-check -d 'Do not check for old metadata' +complete -c fwupdmgr -l no-reboot-check -d 'Do not check or prompt for reboot after update' +complete -c fwupdmgr -l no-safety-check -d 'Do not perform device safety checks' +complete -c fwupdmgr -l no-history -d 'Do not write to the history database' +complete -c fwupdmgr -l show-all -d 'Show all results' +complete -c fwupdmgr -l disable-ssl-strict -d 'Ignore SSL strict checks when downloading' +complete -c fwupdmgr -l ipfs -d 'Use IPFS when downloading files' +complete -c fwupdmgr -l filter -d 'Filter with a set of device flags' + +# complete subcommands +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a activate -d 'Activate devices' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a block-firmware -d 'Blocks a specific firmware from being installed' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a clear-results -d 'Clears the results from the last update' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a disable-remote -d 'Disables a given remote' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a downgrade -d 'Downgrades the firmware on a device' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a enable-remote -d 'Enables a given remote' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-approved-firmware -d 'Gets the list of approved firmware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-blocked-firmware -d 'Gets the list of blocked firmware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-bios-setting -d 'Retrieve BIOS setting' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-details -d 'Gets details about a firmware file' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-devices -d 'Get all devices that support firmware updates' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-history -d 'Show history of firmware updates' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-releases -d 'Gets the releases for a device' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-remotes -d 'Gets the configured remotes' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-results -d 'Gets the results from the last update' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-updates -d 'Gets the list of updates for connected hardware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a install -d 'Install a firmware file on this hardware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-config -d 'Modifies a daemon configuration value' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-remote -d 'Modifies a given remote' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a refresh -d 'Refresh metadata from remote server' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a reinstall -d 'Reinstall current firmware on the device' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a report-history -d 'Share firmware history with the developers' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a set-bios-setting -d 'Set a BIOS setting' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a security -d 'Gets the host security attributes' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a set-approved-firmware -d 'Sets the list of approved firmware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a switch-branch -d 'Switch the firmware branch on the device' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a unblock-firmware -d 'Unblocks a specific firmware from being installed' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a unlock -d 'Unlocks the device for firmware access' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a update -d 'Updates all firmware to latest versions available' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a verify -d 'Checks cryptographic hash matches firmware' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a verify-update -d 'Update the stored cryptographic hash with current ROM contents' + +# commands exclusively consuming device IDs +set -l deviceid_consumers activate clear-results downgrade get-releases get-results get-updates reinstall switch-branch unlock update verify verify-update +# complete device IDs +complete -c fwupdmgr -n "__fish_seen_subcommand_from $deviceid_consumers" -x -a "(__fish_fwupdmgr_devices)" +# complete files and device IDs +complete -c fwupdmgr -n "__fish_seen_subcommand_from install" -r -a "(__fish_fwupdmgr_devices)" + +# commands exclusively consuming remote IDs +set -l remoteid_consumers disable-remote enable-remote modify-remote +# complete remote IDs +complete -c fwupdmgr -n "__fish_seen_subcommand_from $remoteid_consumers" -x -a "(__fish_fwupdmgr_remotes)" +# complete files and remote IDs +complete -c fwupdmgr -n "__fish_seen_subcommand_from refresh" -r -a "(__fish_fwupdmgr_remotes)" diff --git a/fwupd-1.8.6/data/fish-completion/meson.build b/fwupd-1.8.6/data/fish-completion/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..5cce15e4d51b58ee822903325ddabd6f9966d2ab --- /dev/null +++ b/fwupd-1.8.6/data/fish-completion/meson.build @@ -0,0 +1,3 @@ +install_data(['fwupdmgr.fish'], + install_dir: join_paths(datadir, 'fish', 'vendor_completions.d'), +) diff --git a/fwupd-1.8.6/data/fwupd-offline-update.service.in b/fwupd-1.8.6/data/fwupd-offline-update.service.in new file mode 100644 index 0000000000000000000000000000000000000000..ee44b6581f22f8d5e5b62236cb677059415fa500 --- /dev/null +++ b/fwupd-1.8.6/data/fwupd-offline-update.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=Updates device firmware whilst offline +Documentation=man:fwupdmgr +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] +Type=oneshot +ExecStart=@libexecdir@/fwupd/fwupdoffline +FailureAction=reboot diff --git a/fwupd-1.8.6/data/fwupd.ico b/fwupd-1.8.6/data/fwupd.ico new file mode 100644 index 0000000000000000000000000000000000000000..d92df08b6513a8699a72892c9d1d1f7f2c9dfda3 Binary files /dev/null and b/fwupd-1.8.6/data/fwupd.ico differ diff --git a/fwupd-1.8.6/data/fwupd.service.in b/fwupd-1.8.6/data/fwupd.service.in new file mode 100644 index 0000000000000000000000000000000000000000..0095c5b5aacb3cc92c1b4a472ed755fa0784ffce --- /dev/null +++ b/fwupd-1.8.6/data/fwupd.service.in @@ -0,0 +1,19 @@ +[Unit] +Description=Firmware update daemon +Documentation=https://fwupd.org/ +After=dbus.service +Before=display-manager.service +ConditionVirtualization=!container + +[Service] +Type=dbus +TimeoutSec=180 +RuntimeDirectory=@motd_dir@ +RuntimeDirectoryPreserve=yes +BusName=org.freedesktop.fwupd +ExecStart=@libexecdir@/fwupd/fwupd +PrivateTmp=yes +ProtectHome=yes +ProtectSystem=full +SystemCallFilter=~@mount +@dynamic_options@ diff --git a/fwupd-1.8.6/data/fwupd.shutdown.in b/fwupd-1.8.6/data/fwupd.shutdown.in new file mode 100755 index 0000000000000000000000000000000000000000..0a27b7680567fa8bc9c1599da5fff62c95c451c3 --- /dev/null +++ b/fwupd-1.8.6/data/fwupd.shutdown.in @@ -0,0 +1,11 @@ +#!/bin/sh + +# no history database exists +[ -f @localstatedir@/lib/fwupd/pending.db ] || exit 0 + +# activate firmware when we have a read-only filesysten +if ! @bindir@/fwupdtool activate; then + ret=$? + [ "$ret" -eq "2" ] && exit 0 + exit $ret +fi diff --git a/fwupd-1.8.6/data/installed-tests/README.md b/fwupd-1.8.6/data/installed-tests/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7fb4641501bc0da05d762cd370416a957b78531 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/README.md @@ -0,0 +1,83 @@ +# 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 `DisabledPlugins` + + ```shell + # sed "s,^Enabled=false,Enabled=true," -i /etc/fwupd/remotes.d/fwupd-tests.conf + ``` + +2. Enable the `fwupd-tests` remote for local CAB files. + + ```shell + # 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. + +```text +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. + +```shell +$ 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`. + +```shell +$ fwupdmgr downgrade +Choose a device: +0. Cancel +1. 08d460be0f1f9f128413f816022a6439e0078018 (Integrated Webcam™) +2. 8a21cacfb0a8d2b30c5ee9290eb71db021619f8b (XPS 13 9370 System Firmware) +3. d10c5f0ed12c6dc773f596b8ac51f8ace4355380 (XPS 13 9370 Thunderbolt Controller) +1 +Decompressing… [***************************************] +Authenticating… [***************************************] +Downgrading Integrated Webcam™… \ ] +Verifying… [***************************************] Less than one minute remaining… diff --git a/fwupd-1.8.6/data/installed-tests/fakedevice123.bin b/fwupd-1.8.6/data/installed-tests/fakedevice123.bin new file mode 100644 index 0000000000000000000000000000000000000000..885b0f234903bada1fe2c9e1ba5f21c18207c9f7 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fakedevice123.bin @@ -0,0 +1 @@ +0x1020003 diff --git a/fwupd-1.8.6/data/installed-tests/fakedevice123.bin.asc b/fwupd-1.8.6/data/installed-tests/fakedevice123.bin.asc new file mode 100644 index 0000000000000000000000000000000000000000..ed6d9b858de3fff8a2b2f286877d237090ceb5fd --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fakedevice123.bin.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (GNU/Linux) + +iQEcBAABAgAGBQJfey1HAAoJEEim2A5FOLrCn2MIAK6BnVojGYSwHVpZm58b05Xs +rNqozg5pvfDfB0Bde1S0T/4TlDEnJNUku0Gz5IFNbR3ENT5VnJgBkE5xa8Rmv6cy +Gm30CmX+UE1E8qK4BVhUdbNN8bEmeMtzUMK2KfpwMXlIqcpSjpln76PQIxMHj+3P +600bkcppkLEKhiOo+THNhiHxPYJ+wjSSPm3paeMmUuApIvP4YFH8uQ5qkKLdLDVI +V5QOx3O5P3avmHu936GILG9EwV3TkR1eNOe33OqtrGvpoMTcsxUF0Wc/qmUD066d +c9hkTe01paQoN0HW/RMgrIaMnLFwK2mBcwySOo6TU9MIyQfDmLGN3u12nCrmRH8= +=Rq70 +-----END PGP SIGNATURE----- diff --git a/fwupd-1.8.6/data/installed-tests/fakedevice123.metainfo.xml b/fwupd-1.8.6/data/installed-tests/fakedevice123.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..52c9c71f44777a004095b86ec2eb0a4229fd11d8 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fakedevice123.metainfo.xml @@ -0,0 +1,28 @@ + + + + org.fwupd.fakedevice.firmware + FakeDevice + Firmware for the ACME Corp Integrated Webcam + +

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

    +
    + + b585990a-003e-5270-89d5-3705a17f9a43 + + http://www.acme.com/ + CC0-1.0 + GPL-2.0+ + ACME Corp + + + + +

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

    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/data/installed-tests/fakedevice124.bin b/fwupd-1.8.6/data/installed-tests/fakedevice124.bin new file mode 100644 index 0000000000000000000000000000000000000000..f1b2c68db4d7df957abefa64862c4a09101e6ebc --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fakedevice124.bin @@ -0,0 +1 @@ +0x1020004 diff --git a/fwupd-1.8.6/data/installed-tests/fakedevice124.bin.asc b/fwupd-1.8.6/data/installed-tests/fakedevice124.bin.asc new file mode 100644 index 0000000000000000000000000000000000000000..2b2192170e3816c4a4b985b14098fdfa2491b1c8 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fakedevice124.bin.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (GNU/Linux) + +iQEcBAABAgAGBQJfey2FAAoJEEim2A5FOLrCl88IAKCggwAz3qBrBfrc91sYHCq5 +OthMyftOUTQ4JpfISY38k20pwFEhsSHKdKAYDKEVO2jopw+Cr9oTyFycWK20R2lz +tUn4e1EF8zQ29OLxGbvgGlP5/4vPJ2Cv5ujkub6LtNBrOMkNJ6+bB6G8nJZRTElU +e3wi9+E9oKPBgP40A/y79pzPiFMxXl1piYjU3JNeofd3nbtmyRqb6VAs9exQ94+p +CMWWZaJ9igxSAsQiE/NxZpO8qgG3KEmsW7yXRiaIe6xHxb49+JQdjxqS8Oc/C9sX +FSiVHDPzlUegZtcRWZy2zeSNTqmu8vzNSei0xEaLCaQ6PO+pQibxS2VZI/jDLdQ= +=Gha4 +-----END PGP SIGNATURE----- diff --git a/fwupd-1.8.6/data/installed-tests/fakedevice124.metainfo.xml b/fwupd-1.8.6/data/installed-tests/fakedevice124.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..6b67f36cd37d0861835fdf21bdd8c3abc9e04413 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fakedevice124.metainfo.xml @@ -0,0 +1,28 @@ + + + + org.fwupd.fakedevice.firmware + FakeDevice + Firmware for the ACME Corp Integrated Webcam + +

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

    +
    + + b585990a-003e-5270-89d5-3705a17f9a43 + + http://www.acme.com/ + CC0-1.0 + GPL-2.0+ + ACME Corp + + + + +

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

    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/data/installed-tests/fwupd-tests.xml b/fwupd-1.8.6/data/installed-tests/fwupd-tests.xml new file mode 100644 index 0000000000000000000000000000000000000000..c24bb31695c4532cfc80e889284b418059bad56b --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupd-tests.xml @@ -0,0 +1,77 @@ + + + + fakedevice.firmware + FakeDevice Firmware + Firmware for the ACME Corp Integrated Webcam + ACME Corp + GPL-2.0+ +

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

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

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

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

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

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

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

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

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

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

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

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

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

    +
    +
    + + 40338ceb-b966-4eae-adae-9c32edfcc484 + +
    +
    diff --git a/fwupd-1.8.6/data/installed-tests/fwupd.sh b/fwupd-1.8.6/data/installed-tests/fwupd.sh new file mode 100755 index 0000000000000000000000000000000000000000..320ded0d522ce55d5868c62ab47ee5b5b3ac4052 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupd.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +exec 2>&1 +dirname=`dirname $0` + +run_test() +{ + if [ -f $dirname/$1 ]; then + $dirname/$1 + rc=$?; if [ $rc != 0 ]; then exit $rc; fi + fi +} + +run_test acpi-dmar-self-test +run_test acpi-facp-self-test +run_test acpi-phat-self-test +run_test ata-self-test +run_test nitrokey-self-test +run_test linux-swap-self-test +run_test nvme-self-test +run_test wacom-usb-self-test +run_test redfish-self-test +run_test optionrom-self-test +run_test vli-self-test +run_test uefi-dbx-self-test +run_test synaptics-prometheus-self-test +run_test dfu-self-test +run_test mtd-self-test + +# success! +exit 0 diff --git a/fwupd-1.8.6/data/installed-tests/fwupd.test.in b/fwupd-1.8.6/data/installed-tests/fwupd.test.in new file mode 100644 index 0000000000000000000000000000000000000000..b33c85f3b55c5a348d4199eba728268c8f17bc2a --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupd.test.in @@ -0,0 +1,3 @@ +[Test] +Type=session +Exec=sh -c "G_TEST_SRCDIR=@installedtestsdatadir@ G_TEST_BUILDDIR=@installedtestsdatadir@ @installedtestsbindir@/fwupd.sh" diff --git a/fwupd-1.8.6/data/installed-tests/fwupdmgr-p2p.sh b/fwupd-1.8.6/data/installed-tests/fwupdmgr-p2p.sh new file mode 100755 index 0000000000000000000000000000000000000000..0fa734fd626c90934a7e03657de9e5b8bfb4e6ae --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupdmgr-p2p.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -e + +exec 2>&1 + +# only run as root, possibly only in CI +if [ "$(id -u)" -ne 0 ]; then exit 0; fi + +# --- +echo "Starting P2P daemon..." +export FWUPD_DBUS_SOCKET="/run/fwupd.sock" +rm -rf ${FWUPD_DBUS_SOCKET} +@libexecdir@/fwupd/fwupd --verbose --timed-exit --no-timestamp & +while [ ! -e ${FWUPD_DBUS_SOCKET} ]; do sleep 1; done + +# --- +echo "Starting P2P client..." +fwupdmgr get-devices --json +rc=$?; if [ $rc != 0 ]; then exit $rc; fi + +# --- +echo "Shutting down P2P daemon..." +gdbus call --system --dest org.freedesktop.fwupd --object-path / --method org.freedesktop.fwupd.Quit + +# success! +exit 0 diff --git a/fwupd-1.8.6/data/installed-tests/fwupdmgr-p2p.test.in b/fwupd-1.8.6/data/installed-tests/fwupdmgr-p2p.test.in new file mode 100644 index 0000000000000000000000000000000000000000..63fb215e59ca88af4fbcf906776f87a35c99c602 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupdmgr-p2p.test.in @@ -0,0 +1,3 @@ +[Test] +Type=session +Exec=sh -c "@installedtestsdir@/fwupdmgr-p2p.sh" diff --git a/fwupd-1.8.6/data/installed-tests/fwupdmgr.sh b/fwupd-1.8.6/data/installed-tests/fwupdmgr.sh new file mode 100755 index 0000000000000000000000000000000000000000..3d65395ec93fa343e6eccad80d838e07d3327cd8 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupdmgr.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +exec 2>&1 +device=08d460be0f1f9f128413f816022a6439e0078018 + +error() +{ + rc=$1 + journalctl -u fwupd -b || true + exit $rc +} + +# --- +echo "Getting the list of remotes..." +fwupdmgr get-remotes +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Enabling fwupd-tests remote..." +fwupdmgr enable-remote fwupd-tests +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Update the device hash database..." +fwupdmgr verify-update $device +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Getting devices (should be one)..." +fwupdmgr get-devices --no-unreported-check +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Testing the verification of firmware..." +fwupdmgr verify $device +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Getting updates (should be one)..." +fwupdmgr --no-unreported-check --no-metadata-check get-updates +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Installing test firmware..." +fwupdmgr update $device -y +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Getting updates (should be none)..." +fwupdmgr --no-unreported-check --no-metadata-check get-updates +rc=$?; if [ $rc != 2 ]; then error $rc; fi + +# --- +echo "Testing the verification of firmware (again)..." +fwupdmgr verify $device +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +if [ -z "$CI_NETWORK" ]; then + echo "Skipping remaining tests due to CI_NETWORK not being set" + exit 0 +fi + +# --- +echo "Downgrading to older release (requires network access)" +fwupdmgr downgrade $device -y +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Downgrading to older release (should be none)" +fwupdmgr downgrade $device +rc=$?; if [ $rc != 2 ]; then error $rc; fi + +# --- +echo "Updating all devices to latest release (requires network access)" +fwupdmgr --no-unreported-check --no-metadata-check --no-reboot-check update -y +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# --- +echo "Getting updates (should be none)..." +fwupdmgr --no-unreported-check --no-metadata-check get-updates +rc=$?; if [ $rc != 2 ]; then error $rc; fi + +# --- +echo "Refreshing from the LVFS (requires network access)..." +fwupdmgr refresh +rc=$?; if [ $rc != 0 ]; then error $rc; fi + +# success! +exit 0 diff --git a/fwupd-1.8.6/data/installed-tests/fwupdmgr.test.in b/fwupd-1.8.6/data/installed-tests/fwupdmgr.test.in new file mode 100644 index 0000000000000000000000000000000000000000..20c4347082a17dceeef50419343baa5fa9bf8f77 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/fwupdmgr.test.in @@ -0,0 +1,3 @@ +[Test] +Type=session +Exec=sh -c "@installedtestsdir@/fwupdmgr.sh" diff --git a/fwupd-1.8.6/data/installed-tests/meson.build b/fwupd-1.8.6/data/installed-tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..be3d5c6d9f9db68a16015dc94e944ee175f644aa --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/meson.build @@ -0,0 +1,87 @@ +con2 = configuration_data() +con2.set('installedtestsdir', installed_test_datadir) +con2.set('installedtestsbindir', installed_test_bindir) +con2.set('installedtestsdatadir', installed_test_datadir) +con2.set('bindir', bindir) +con2.set('libexecdir', libexecdir) + +configure_file( + input: 'fwupdmgr.test.in', + output: 'fwupdmgr.test', + configuration: con2, + install: true, + install_dir: installed_test_datadir, +) + +configure_file( + input: 'fwupdmgr-p2p.test.in', + output: 'fwupdmgr-p2p.test', + configuration: con2, + install: true, + install_dir: installed_test_datadir, +) + +configure_file( + input: 'fwupd.test.in', + output: 'fwupd.test', + configuration: con2, + install: true, + install_dir: installed_test_datadir, +) + +configure_file( + input: 'fwupdmgr-p2p.sh', + output: 'fwupdmgr-p2p.sh', + configuration: con2, + install: true, + install_dir: installed_test_datadir, +) + +install_data([ + 'fwupdmgr.sh', + 'fwupd-tests.xml', + ], + install_dir: installed_test_datadir, +) + +install_data([ + 'fwupd.sh', + ], + install_dir: installed_test_bindir, +) + +custom_target('installed-cab123', + input: [ + 'fakedevice123.bin', + 'fakedevice123.bin.asc', + 'fakedevice123.metainfo.xml', + ], + output: 'fakedevice123.cab', + command: [ + gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', + ], + install: true, + install_dir: installed_test_datadir, +) +custom_target('installed-cab124', + input: [ + 'fakedevice124.bin', + 'fakedevice124.bin.asc', + 'fakedevice124.metainfo.xml', + ], + output: 'fakedevice124.cab', + command: [ + gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', + ], + install: true, + install_dir: installed_test_datadir, +) + +# replace @installedtestsdir@ +configure_file( + input: 'remote.conf.in', + output: 'fwupd-tests.conf', + configuration: con2, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), +) diff --git a/fwupd-1.8.6/data/installed-tests/remote.conf.in b/fwupd-1.8.6/data/installed-tests/remote.conf.in new file mode 100644 index 0000000000000000000000000000000000000000..dca038b3712e200bc073dd641d20d200ddb75cd7 --- /dev/null +++ b/fwupd-1.8.6/data/installed-tests/remote.conf.in @@ -0,0 +1,9 @@ +[fwupd Remote] +# This is a local fwupd remote that is used only for installed tests +# either from continuous integration or for fake devices from fwupd +# frontends + +Enabled=false +Title=fwupd test suite +Keyring=none +MetadataURI=file://@installedtestsdir@/fwupd-tests.xml diff --git a/fwupd-1.8.6/data/meson.build b/fwupd-1.8.6/data/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..865b20e26113c8eae888cda5fa94c84513756e2d --- /dev/null +++ b/fwupd-1.8.6/data/meson.build @@ -0,0 +1,148 @@ +subdir('bios-settings.d') +subdir('pki') +subdir('remotes.d') + +if get_option('bash_completion') + subdir('bash-completion') +endif + +if get_option('fish_completion') + subdir('fish-completion') +endif + +if get_option('tests') +subdir('device-tests') +endif + +if build_daemon +subdir('motd') +endif + +if get_option('tests') + if build_daemon + subdir('installed-tests') + endif +endif + +if build_standalone + install_data(['daemon.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') + ) + plugin_quirks += files([ + 'power.quirk', + 'cfi.quirk', + ]) +endif + +if get_option('metainfo') + custom_target('metainfo', + input: 'org.freedesktop.fwupd.metainfo.xml', + output: 'org.freedesktop.fwupd.metainfo.xml', + command: [ + python3, + join_paths(meson.project_source_root(), 'contrib', 'generate-metainfo.py'), + '@INPUT@', + '@OUTPUT@', + ], + install: true, + install_dir: join_paths(datadir, 'metainfo'), + ) + install_data(['org.freedesktop.fwupd.svg'], + install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps') + ) +endif + +if build_daemon + install_data(['org.freedesktop.fwupd.conf'], + install_dir: join_paths(datadir, 'dbus-1', 'system.d') + ) + + if gudev.found() + install_data(['90-fwupd-devices.rules'], + install_dir: join_paths(udevdir, 'rules.d') + ) + endif + + if libsystemd.found() + con2 = configuration_data() + con2.set('libexecdir', libexecdir) + con2.set('bindir', bindir) + con2.set('datadir', datadir) + con2.set('localstatedir', localstatedir) + rw_directories = [] + if not get_option('plugin_uefi_capsule').disabled() + rw_directories += ['-/boot/efi', '-/efi/EFI', '-/boot/EFI', '-/boot/grub'] + 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 + #pull configuration/cache/state from /etc and /var only if prefix is /usr + if get_option('prefix') == '/usr' + dynamic_options += 'ConfigurationDirectory=fwupd' + dynamic_options += 'StateDirectory=fwupd' + dynamic_options += 'CacheDirectory=fwupd' + endif + if not get_option('plugin_redfish').disabled() + dynamic_options += 'RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET AF_INET6' + else + dynamic_options += 'RestrictAddressFamilies=AF_NETLINK AF_UNIX' + endif + con2.set('dynamic_options', '\n'.join(dynamic_options)) + con2.set('motd_dir', motd_dir) + + # replace @bindir@ + if offline.allowed() + configure_file( + input: 'fwupd-offline-update.service.in', + output: 'fwupd-offline-update.service', + configuration: con2, + install: true, + install_dir: systemdunitdir, + ) + endif + + # replace @dynamic_options@ + configure_file( + input: 'fwupd.service.in', + output: 'fwupd.service', + configuration: con2, + install: true, + install_dir: systemdunitdir, + ) + + # for activation + configure_file( + input: 'fwupd.shutdown.in', + output: 'fwupd.shutdown', + configuration: con2, + install: true, + install_dir: systemd_shutdown_dir, + ) + endif + + if libsystemd.found() or elogind.found() + con2 = configuration_data() + con2.set('libexecdir', libexecdir) + + # replace @libexecdir@ + configure_file( + input: 'org.freedesktop.fwupd.service.in', + output: 'org.freedesktop.fwupd.service', + configuration: con2, + install: true, + install_dir: join_paths(datadir, + 'dbus-1', + 'system-services'), + ) + endif +endif diff --git a/fwupd-1.8.6/data/motd/85-fwupd.motd.in b/fwupd-1.8.6/data/motd/85-fwupd.motd.in new file mode 100755 index 0000000000000000000000000000000000000000..450d5c7178afe72457bfa85f59e7b4618b2dbe1e --- /dev/null +++ b/fwupd-1.8.6/data/motd/85-fwupd.motd.in @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ -f @motd_fullpath@ ]; then + cat @motd_fullpath@ +fi diff --git a/fwupd-1.8.6/data/motd/README.md b/fwupd-1.8.6/data/motd/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5bf48d3fff2d6c2ee3cf6dc425058d0ebd0bc852 --- /dev/null +++ b/fwupd-1.8.6/data/motd/README.md @@ -0,0 +1,21 @@ +# Message of the day integration + +Message on the day integration is used to display the availability of updates when connecting to a remote console. + +It has two elements: + +* Automatic firmware metadata refresh +* Message of the day display + +## Automatic firmware metadata refresh + +This uses a systemd timer to run on a regular cadence. +To enable this, run + +```shell +# systemctl enable fwupd-refresh.timer +``` + +## Motd display + +Motd display is dependent upon the availability of the update-motd snippet consumption service such as pam_motd. diff --git a/fwupd-1.8.6/data/motd/fwupd-refresh.preset b/fwupd-1.8.6/data/motd/fwupd-refresh.preset new file mode 100644 index 0000000000000000000000000000000000000000..2aab2d26e44cfc2374903ed6b8b4c5936a0a9d5b --- /dev/null +++ b/fwupd-1.8.6/data/motd/fwupd-refresh.preset @@ -0,0 +1,2 @@ +disable fwupd-refresh.service +disable fwupd-refresh.timer diff --git a/fwupd-1.8.6/data/motd/fwupd-refresh.service.in b/fwupd-1.8.6/data/motd/fwupd-refresh.service.in new file mode 100644 index 0000000000000000000000000000000000000000..d217450e83398cc3d365f9ac0262595e20f1b9a5 --- /dev/null +++ b/fwupd-1.8.6/data/motd/fwupd-refresh.service.in @@ -0,0 +1,17 @@ +[Unit] +Description=Refresh fwupd metadata and update motd +Documentation=man:fwupdmgr(1) +After=network-online.target + +[Service] +Type=oneshot +CacheDirectory=fwupdmgr +StandardError=null +@user@ +RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET AF_INET6 +SystemCallFilter=~@mount +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictRealtime=yes +SuccessExitStatus=2 +ExecStart=@bindir@/fwupdmgr refresh diff --git a/fwupd-1.8.6/data/motd/fwupd-refresh.timer b/fwupd-1.8.6/data/motd/fwupd-refresh.timer new file mode 100644 index 0000000000000000000000000000000000000000..c72e05a7311bbb78a824ff4b519b82fd1c0d73d9 --- /dev/null +++ b/fwupd-1.8.6/data/motd/fwupd-refresh.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Refresh fwupd metadata regularly +ConditionVirtualization=!container + +[Timer] +OnCalendar=*-*-* 6,18:00 +RandomizedDelaySec=12h +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/fwupd-1.8.6/data/motd/meson.build b/fwupd-1.8.6/data/motd/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d817c010da3b65d6b7cf5c72602dc01b4a921429 --- /dev/null +++ b/fwupd-1.8.6/data/motd/meson.build @@ -0,0 +1,45 @@ + +if libsystemd.found() + install_data(['fwupd-refresh.timer'], + install_dir: systemdunitdir) + install_data(['fwupd-refresh.preset'], + install_dir: systemdsystempresetdir) + motd_fullpath = join_paths ('/run', motd_dir, motd_file) +else + motd_fullpath = join_paths (localstatedir, motd_dir, motd_file) +endif + +con2 = configuration_data() +con2.set('bindir', bindir) +con2.set('motd_fullpath', motd_fullpath) + +if libsystemd.found() + if get_option('systemd_unit_user') == '' + con2.set('user', 'DynamicUser=yes') + else + dynamic_options = [ + 'ProtectSystem=strict', + 'ProtectHome=read-only', + 'User=' + get_option('systemd_unit_user') + ] + con2.set('user','\n'.join(dynamic_options)) + endif + + configure_file( + input: 'fwupd-refresh.service.in', + output: 'fwupd-refresh.service', + configuration: con2, + install: true, + install_dir: systemdunitdir, + ) +endif + +# This file is only used in Ubuntu, which chooses to use update-motd instead +# of sourcing /run/motd.d/* +# See https://bugs.launchpad.net/ubuntu/+source/pam/+bug/399071 +configure_file( + input: '85-fwupd.motd.in', + output: motd_file, + configuration: con2, + install: false, +) diff --git a/fwupd-1.8.6/data/org.freedesktop.fwupd.conf b/fwupd-1.8.6/data/org.freedesktop.fwupd.conf new file mode 100644 index 0000000000000000000000000000000000000000..7267fa987d37e478b7b31cc4a6990fb353339268 --- /dev/null +++ b/fwupd-1.8.6/data/org.freedesktop.fwupd.conf @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/fwupd-1.8.6/data/org.freedesktop.fwupd.metainfo.xml b/fwupd-1.8.6/data/org.freedesktop.fwupd.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa573d90f17a78ef42e17a050a5d332c33788e72 --- /dev/null +++ b/fwupd-1.8.6/data/org.freedesktop.fwupd.metainfo.xml @@ -0,0 +1,2529 @@ + + + + org.freedesktop.fwupd + CC0-1.0 + LGPL-2.0+ + fwupd + Update device firmware on Linux + +

    + 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. +

    +

    + The fwupd process is a system 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. +

    +
    + https://github.com/fwupd/fwupd/issues + https://fwupd.org/ + https://www.transifex.com/freedesktop/fwupd/ + richard_at_hughsie.com + fwupd + + moderate + + + fwupdmgr + fwupdtool + + + + +

    + This release adds the following features: +

    +
      +
    • Reduce the installed package size by more than 30%
    • +
    • Translate more interactive messages
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allow disabling a DFU device when required
    • +
    • Fix a regression when getting the i2c bus number
    • +
    • Fix a small memory leak when reloading the parade-lspcon device
    • +
    • Fix installing the dbx update when using fwupdtool
    • +
    • Improve writing CoSWID and uSWID metadata
    • +
    • Only include the last 5 releases in the installed metainfo file
    • +
    • Only request the BOS descriptor for newer libgusb versions
    • +
    • Prevent high memory usage when loading corrupt SREC files
    • +
    • Try harder when trying to find the default ESP volume
    • +
    • Use a higher compression preset for the UEFI splash images
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Focaltech touchpads
    • +
    • FPC fingerprint readers
    • +
    • Supermicro machines using Redfish
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a new android-boot plugin to update specific block devices
    • +
    • Add new plugin to display SMU firmware version on AMD APU/CPU
    • +
    • Add support for platform capability descriptors so devices can set quirks
    • +
    • Move the generic Intel Goshen Ridge code out to a new plugin
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allow specifying the ESP when applying the dbx update
    • +
    • Always check the BDP partitions when getting all the possible ESPs
    • +
    • Correctly update Wacom AES devices
    • +
    • Disable changing sleep mode on Ryzen 6000 systems
    • +
    • Do not show the 'may not be usable while updating' message for DBX updates
    • +
    • Expose Pine64 PinePhone Pro MTD as Tow-Boot
    • +
    • Fix a critical warning when issuing Secure Boot modem AT commands
    • +
    • Fix a fuzzing crash when parsing malicious FDT data
    • +
    • Fix aligning up addresses greater than 4GB
    • +
    • Fix a possible crash when dumping VBE firmware
    • +
    • Fix a possible critical warning when parsing cabinet archives
    • +
    • Fix a regression when parsing pixart-rf firmware
    • +
    • Fix a small memory leak when parsing UF2 files
    • +
    • Fix checking for invalid depth requirements
    • +
    • Fix parsing the coSWID firmware ID when encoded as a UUID
    • +
    • Fix parsing uSWID uncompressed metadata
    • +
    • Fix uploading to DFU-CSR devices
    • +
    • Limit the archive size to 25% of the RAM, or 4G
    • +
    • Load coSWID metadata from a uSWID MTD block device
    • +
    • Never save the Redfish auto-generated password to a user-readable file
    • +
    • Only create users using IPMI when we know it's going to work
    • +
    • Write all the CCGX metadata block as intended
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Corsair SABRE RGB PRO Gaming mouse
    • +
    • More Sonix CAM devices
    • +
    • More Intel Goshen Ridge USB-4 docks
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a translated title and long description for HSI security attributes
    • +
    • Add support for loading a machine-default BIOS settings policy
    • +
    • Add support for reading and writing BIOS settings
    • +
    • Allow loading BIOS settings for host emulation
    • +
    • Prompt users to fix some BIOS configuration issues
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Actually show provided AppStream security issues
    • +
    • Add Quectel secure boot status AT commands
    • +
    • Correctly detect CET IBT
    • +
    • Do not assert when running with no plugins
    • +
    • Do not require UEFI capsule updates for checking TPM PCR0
    • +
    • Do not show HSI events where we changed the spec result value
    • +
    • Fix applying the latest DBX update
    • +
    • Include vfat in the list of possible BDP partition types
    • +
    • Install all devices with the same composite id in fwupdtool
    • +
    • Only fail the kernel HSI test for specific taint reasons
    • +
    • Only show changed events in fwupdmgr security
    • +
    • Update vulnerable CMSE versions from CSMEVDT data
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Elan non-HID touchpads
    • +
    • Google Prism
    • +
    • LabTop Mk III
    • +
    • ThinkPad Thunderbolt 4 Dock
    • +
    • ThinkPad Universal Smart Dock
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add resolution flags to each security attribute failures for the user
    • +
    • Allow loading in emulated host profiles for debugging
    • +
    • Check if Intel TME has been disabled by the firmware or platform
    • +
    • Wait for the system to acquiesce after doing each update
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Do not use CoD even when advertized on non-aarch64 platforms
    • +
    • Fix a crash when updating the Logitech Bolt radio device
    • +
    • Fix a critical warning when parsing an invalid PHAT record
    • +
    • Fix a critical warning when parsing invalid FDT firmware
    • +
    • Fix fwupdmgr security when plugins are added to the blocklist
    • +
    • Fix parsing SMBIOS data to correct the device hardware IDs
    • +
    • Fix uploading signed reports by sending the correct checksum
    • +
    • Use the correct protocol attribute name when exporting to JSON
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Additional Startech devices
    • +
    • Additional Elan fingerprint readers
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add startup profiling which allowed us to speed up daemon startup considerably
    • +
    • Add support for OptionROM, CPD and FPT firmware formats for future hardware
    • +
    • Add the HostVendor to the D-Bus interface
    • +
    • Break some internal ABI and add a conversion helper for out-of-tree plugins
    • +
    • Optionally build the quirk files into the daemon binary to reduce installed size
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allow front-end clients to read the percentage property
    • +
    • Allow more quirk entries to add multiple items
    • +
    • Allow to force install Genesys firmware even if the public-key does not match
    • +
    • Allow UFS disks to define the signed status in metadata
    • +
    • Autoconnect the Redfish network device when rebooting the BMC
    • +
    • Copy the instance ID strings when incorporating devices
    • +
    • Do not generate a capsule header for the FMP GUID
    • +
    • Ensure more firmware formats can round-trip to and from XML
    • +
    • Fix a regression for devices using the Atmel FLIP Bootloader
    • +
    • Fix running fwupdtool security with a user-specified plugin allowlist
    • +
    • Handle ENOTTY with the correct error code for ioctl calls
    • +
    • Increase the self tests coverage substantially
    • +
    • Modernize the AMT plugin and split out common MEI functionality
    • +
    • Only move the logitech-bulkcontroller progressbar forwards when writing
    • +
    • Set the device ID on the FwupdRequest to allow better UX
    • +
    • Show the get-details output when the device requirements fail
    • +
    • Simply quirk matching for i2c devices to speed up daemon startup
    • +
    • Support SHA256 fastboot hashes if specified
    • +
    • Use force-detach to bypass the DFU streaming check for camera devices
    • +
    • Use the SCSI target to correctly set the physical ID
    • +
    • Wait for the System76 launch device to re-enumerate if already unlocked
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Corsair HARPOON RGB Wireless mouse
    • +
    • U-Boot devices writing simple FIT images
    • +
    • Genesys M27fd AIM101
    • +
    • More PixArt wireless devices
    • +
    • More Steelseries HID, Sonic and Fizz devices
    • +
    • System76 launch_2
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add archive writing support for devices with composite firmware
    • +
    • Add a way to read device composite firmware in fwupdtool
    • +
    • Allow clients to opt-in to showing updates with user-solvable problems
    • +
    • Allow the device to pause polling when writing firmware
    • +
    • Export the system and device battery levels on the D-Bus interface
    • +
    • Log errors and warnings to the win32 eventlog when required
    • +
    • Add X-UsbReceiver as an update category with icon usb-receiver
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Accurately return the last-set status to client tools
    • +
    • Allow dumping flashrom firmware using fwupdtool
    • +
    • Allow specifying a non-file D-Bus transport
    • +
    • Allow to request post actions from fwupdtool
    • +
    • Always be arch-explicit when installing OS deps
    • +
    • Be more resilient when restarting the Redfish BMC
    • +
    • Do not mark all Redfish updates as UPDATABLE
    • +
    • Do not use 'dongle' to describe USB receiver hardware
    • +
    • Download in-process when using fwupdtool
    • +
    • Fix a critical warning on failed modem update
    • +
    • Fix regression when probing PS175 devices
    • +
    • Hardcode the Redfish filedata name to firmware.bin
    • +
    • Set the Bluetooth version if REV has been set
    • +
    • Switch the Windows installer from NSIS to MSI
    • +
    • Use StartServiceCtrlDispatcherA for the daemon on Windows
    • +
    • Use the native certificate store on Windows
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Corsair KATAR PRO XT, SABRE PRO and KATAR PRO Wireless
    • +
    • HP Thunderbolt Dock G4
    • +
    • Lenovo ThinkPad Universal USB-C Dock
    • +
    • More PixArt wireless devices
    • +
    • More SunplusIT USB cameras
    • +
    • Some UFS devices
    • +
    • Steelseries Aerox 3 Wireless and Rival 3 Wireless
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a new attribute for CPUs supported by HSI
    • +
    • Add coSWID and uSWID parsers to libfwupdplugin for initial SBoM support
    • +
    • Add new HSI attributes for the AMD PSP and various other system protections
    • +
    • Add the runtime fwupd-efi version as a firmware requirement
    • +
    • Allow 'fwupdmgr install' to install a specified firmware version
    • +
    • Allow overriding the detected machine type for debugging and development
    • +
    • Restart the BMC after installing BCM updates
    • +
    • Show the device serial number and instance IDs by default
    • +
    • Support dumping the MTD image to a firmware blob
    • +
    • Take a device inhibit when updating a device
    • +
    • Use the CFI manufacturer ID to set the vendor
    • +
    • Use the correct icon automatically for more hardware
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add signed-payload metadata for more devices
    • +
    • Allow Capsule-on-Disk to work in more cases
    • +
    • Allow quirking the detected flashrom flash size
    • +
    • Check for os-release on FWUPD_SYSCONFDIR
    • +
    • Check the alignment when parsing raw firmware
    • +
    • Check the update protocol exists when checking requirements
    • +
    • Convert the build system to use meson tristate features
    • +
    • Correctly probe USB-2 hubs with more than 7 ports
    • +
    • Do not add the Windows compatibility ID to capsule devices
    • +
    • Do not allow the DBX update for specific motherboards
    • +
    • Do not expect KernelCmdline on Windows
    • +
    • Do not export USB4 host controllers as updatable if they don't have unique GUIDs
    • +
    • Do not fallback to audio-card and use a more suitable icon for USB hubs
    • +
    • Do not hardcode the libexecdir to /usr/libexec
    • +
    • Do not leak child processes when canceling
    • +
    • Do not show unconnected or unreachable devices in the client tools
    • +
    • Do not throw away the TPM eventlog when uploading to the LVFS
    • +
    • Do not use /var/run for the socket
    • +
    • Export the version_lowest_raw value correctly
    • +
    • Fix build for MacOS and add to the CI matrix
    • +
    • Fix eventlog replay for Intel TXT machines
    • +
    • Fix several small memory leaks
    • +
    • Fix writing large mtd images than 10kb
    • +
    • Ignore MTD devices that report EPERM on open
    • +
    • Mark the ME region device locked if it is read only
    • +
    • Never send the DeviceChanged signal with old data
    • +
    • Only show the CLI time remaining for predictable status phases
    • +
    • Respect the NO_COLOR env variable
    • +
    • Return the correct error when there is no GPIO device to open
    • +
    • Support the new UPower PENDING device states
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • CH341A SPI programmer
    • +
    • Corsair Sabre RGB PRO and Slipstream USB receiver
    • +
    • Genesys GL3521 and GL3590 hubs
    • +
    • Google Servo Dock
    • +
    • Logitech M550, M650 and K650
    • +
    • More ELAN fingerprint readers
    • +
    • More integrated Wacom panels
    • +
    • More NovaCustom machines
    • +
    • More StaLabs StarLite machines
    • +
    • More Tuxedo laptops
    • +
    • Quectel EM05
    • +
    • FlatFrog devices
    • +
    • System76 launch_lite_1
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a flag for UEFI devices that never want a capsule header auto-added
    • +
    • Add a flag to indicate the device has a signed or unsigned payload
    • +
    • Add a plugin to set a GPIO pin for the duration of an update
    • +
    • Add a simple plugin to enumerate (but not update) SCSI hardware
    • +
    • Add two more instance IDs to the MTD devices
    • +
    • Add X-BaseboardManagementController as an update category
    • +
    • Allow assigning issues to devices for known high priority problems
    • +
    • Parse the MTD firmware version using the defined GType
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Check the IFD sections have non-zero data length to fix a critical warning
    • +
    • Modify the AT retry behavior to fix getting the firmware branch
    • +
    • Do not run fwupd-refresh automatically in containers
    • +
    • Do not show a warning if the TPM eventlog does not exist
    • +
    • Do not show TSS2 warning messages by default
    • +
    • Fix a critical warning when loading an empty TPM eventlog item
    • +
    • Fix a logic error when adding the community warning in fwupdmgr
    • +
    • Fix loading flashrom devices in coreboot mode
    • +
    • Fix the error handling when updating USB4 retimers
    • +
    • Show the user when devices are not updatable due to inhibits
    • +
    • Skip probing the Dell DA300 device to avoid a warning
    • +
    • Try harder to convert to a version into a correct semver
    • +
    • Use multiple checksums when there are no provided artifacts
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • HP M2xfd monitors
    • +
    • Star Lite Mk III
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a flag to indicate the firmware is not provided by the vendor
    • +
    • Add support for showing dependency versions in JSON format
    • +
    • Allow fwupd to operate in socket mode without a D-Bus daemon
    • +
    • Allow marking a device as End-of-Life by the OEM vendor
    • +
    • Allow specifying the machine Best Known Configuration locally
    • +
    • Fall back to the ARM Device Tree 'compatible' data when required
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Be more robust by retrying IPMI transactions on servers
    • +
    • Change the expired Redfish password when required
    • +
    • Fix a ModemManager segfault on startup for some MBIM-QDU devices
    • +
    • Fix a possible dell-dock segfault at startup
    • +
    • Fix compiling with new versions of efivar
    • +
    • Fix the Nordic bootloader type detection
    • +
    • Fix USB4 retimer enumeration
    • +
    • Get the SMBIOS table and host machine ID when running on Windows
    • +
    • Show results when calling get-details if failing requirements
    • +
    • Uninhibit the modem using ModemManager after upgrade
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Future Analogix devices
    • +
    • NovaCustom NV4x
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add firmware branch support for ModemManager devices
    • +
    • Allow firmware engineers to patch files at known offsets
    • +
    • Show why more devices are not marked as updatable
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allow fwupdtool to be run as the non-root user in more cases
    • +
    • Assign the Logitech bulkcontroller update interface correctly
    • +
    • Do not allow UEFI updates when the laptop lid is closed
    • +
    • Do not autoload ipmi-si to avoid warning on non-server hardware
    • +
    • Do not show a critical warning for a weird TPM event log
    • +
    • Fix waiting for USB devices when using Windows
    • +
    • Ignore non-PCI NVMe devices
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • HP USB-C G2 Dock
    • +
    • Many UF2 devices, experimentally
    • +
    • More PixArt devices
    • +
    • Nordic HID devices using MCUBoot
    • +
    • Quectel EG25-G LTE Modem
    • +
    • ThinkPad Thunderbolt 4 Dock
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a sync-bkc subcommand to ensure a known set of firmware versions
    • +
    • Add FuArchiveFirmware for plugins that use archives as firmware files
    • +
    • Add quirkable page and sector size properties to FuCfiDevice
    • +
    • Make Upower and powerd support optional
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add some sanity checks to the elanfp firmware parser
    • +
    • Add the CFI JEDEC instance ID if using the vendor-extended version
    • +
    • Check the value range when parsing the quirk keys
    • +
    • Do not wait for a USB runtime if will-disappear is set
    • +
    • Enable the MOTD integration when using pam_motd
    • +
    • Fix DFU regression when merging the FuProgress work
    • +
    • Fix running the tests when fwupd is not installed
    • +
    • Fix the GLib error message when inotify max_user_instances is too low
    • +
    • Fix VLI VL820Q7 detection to fix flashing of the Lenovo TBT3 dock
    • +
    • Ignore a USB error for STM32 attach when the device goes away
    • +
    • Make the HSI tests optional for embedded targets
    • +
    • Make the plugin startup order deterministic
    • +
    • Set Thunderbolt ports offline on host controller
    • +
    • Use endian-safe version functions when enumerating Logitech hardware
    • +
    • Use lowercase flag names in intel-spi to prevent a runtime warning
    • +
    • Wait for the System76 Launch device to come back from DFU mode
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Most Nordic Semiconductor nRF Secure devices
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a new HSI check that PCR registers 0-7 are not empty
    • +
    • Add several compile flags to reduce the install size by over 300Kb
    • +
    • Allow overriding HwId data from the daemon.conf config file
    • +
    • Allow overriding the firmware GType from a quirk file
    • +
    • Export the component release ID over DBus
    • +
    • Remove support for the SoloKey and ChaosKey devices
    • +
    • Show a daemon warning if quirk flags are malformed
    • +
    • Speed up the daemon startup by ~40% by doing less at startup
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Be case insensitive when fixing the device model
    • +
    • Fix a critial warning in ccgx found by the fuzzer
    • +
    • Fix a DFU crash if the attach failed due to a hardware fault
    • +
    • Fix a Redfish crash when specifying a URL without a port
    • +
    • Fix CLI downloads when using fwupdmgr --ipfs
    • +
    • Fix critical warning when /etc/machine-id does not exist
    • +
    • Inhibit thunderbolt devices to correctly use UPDATABLE_HIDDEN
    • +
    • Set SSL_VERIFYHOST=0 when using Redfish to fix OpenBMC auth
    • +
    • Skip UEFI devices that fail coldplug
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • All exported MTD block devices
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Allow specifying 'fwupdmgr device-test foo --json' for unattended testing
    • +
    • Allow using a filename when using set-approved-firmware
    • +
    • Inhibit ModemManager device in mbim-qdu
    • +
    • Share the Common Flash Memory Interface quirks between plugins
    • +
    • Show changes in HSI attributes when using 'fwupdmgr security'
    • +
    • Show the user a warning if updating may affect full-disk-encryption
    • +
    • Show translated firmware release notes when provided
    • +
    • Support loading remotes from /var/lib/fwupd/remotes.d
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Fix a CCGX regression when loading firmware
    • +
    • Fix a potential crash when dumping Parade devices
    • +
    • Fix build error when sys/io.h is not available
    • +
    • Fix building the Synaptics RMI self tests on s390x
    • +
    • Fix the CSME CVE detection for new generations
    • +
    • Handle EPERM when running the self tests on systems with IPMI
    • +
    • Mark as SUPPORTED even if on battery power
    • +
    • Only save the HSI attributes to the database if different
    • +
    • Raise the client timeout value from 25 seconds to fix Redfish startup
    • +
    • Redirect the old HSI links to the correct place
    • +
    • Relax the ITE SuperIO signature checks for new hardware support
    • +
    • Set device time and timezone for logitech bulkcontroller devices
    • +
    • Set the verfmt of the returned device when the daemon device is unset
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Dell Atomic Dock
    • +
    • HP Thunderbolt Dock G4
    • +
    • More PixArt devices
    • +
    • Steelseries Stratus
    • +
    • Wacom 3rd-gen Intuos BT
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add FuCfuPayload and FuCfuOffer for future usage
    • +
    • Add support for an 'unreachable' device flag
    • +
    • Add support for Logitech devices supporting the Unified Battery feature
    • +
    • Allow adding GUIDs to each HSI security attribute
    • +
    • Allow installing the LVFS remote, but with it disabled by default
    • +
    • Convert security attributes to JSON and write then to the database
    • +
    • Convert the device test script to a fwupdmgr subcommand
    • +
    • Create Redfish user accounts automatically using IPMI
    • +
    • Use an interactive request to restart some Logitech DFU devices
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Abort on invalid SREC files early to avoid a fuzzing timeout
    • +
    • Allow using interrupt transfers for HID devices
    • +
    • Allow waiting for multiple devices to replug
    • +
    • Fix a critical warning on a Unifying flash failure
    • +
    • Fix a regression in flashing the Dell dock
    • +
    • Fix Thunderbolt host controller probing
    • +
    • Forcefully set checksums found in cabinet files to lowercase
    • +
    • Force UX-capsule over full size BGRT
    • +
    • Make the SuperIO ports and timeouts specific to the DMI model
    • +
    • Only probe SynapticsMST devices that have opted-in
    • +
    • Remove support for --ignore-power as it did not work for UEFI firmware
    • +
    • Reset the CMOS as required when changing system firmware branch
    • +
    • Restart the daemon if any of the the plugin config files are modified
    • +
    • Show HSiLevel=0 attributes in JSON security output
    • +
    • Update the child composite ID if the parent changes
    • +
    • Use a per-device global percentage completion
    • +
    • Write the BMP image upside down to avoid using a negative bitmap height
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • A huge number of Synaptics CAPE devices
    • +
    • Elan fingerprint readers
    • +
    • Logitech Bolt peripherals, receivers and radio hardware
    • +
    • Logitech devices supporting the bulk controller protocol
    • +
    • More supported PixArt devices
    • +
    • More supported StarBook coreboot devices
    • +
    • Union Point SPI hardware
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add a plugin to check Lenovo firmware settings
    • +
    • Add initial support for the powerd daemon
    • +
    • Add support for CapsuleOnDisk
    • +
    • Add support for installing UEFI updates from GRUB
    • +
    • Add support for soft-requirements that can be ignored with --force
    • +
    • Allow devices to only accept version upgrades
    • +
    • Allow discovery of Redfish BMCs specified by VID-PID or MAC
    • +
    • Allow the daemon to request interactive action from the end user
    • +
    • Automatically connect the BMC network interface at startup
    • +
    • Show the build timestamp if set on the device
    • +
    • Show the user how to switch out of Wacom tablet Android-mode
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add the alternate vendor name into the 8BitDo allowlist
    • +
    • Allow multiple devices to set WAIT_FOR_REPLUG
    • +
    • Allow the client to watch for more property changes
    • +
    • Always ensure the SuperIO version string is NUL terminated
    • +
    • Automatically clear the update error as required
    • +
    • Disable all UX capsules for Lenovo hardware
    • +
    • Do not assume the metainfo file is NUL-terminated
    • +
    • Do not save invalid files on LVFS server error
    • +
    • Fix a VLI regression in enumerating the PD device
    • +
    • Fix a VLI regression when installing VL820Q7 firmware
    • +
    • Fix enumeration of the Synaptics Prometheus config child
    • +
    • Fix parsing Redfish USB/PCI network VID/PIDs
    • +
    • Fix the fwupdmgr progressbar spinner to actually work
    • +
    • Fix version number for legacy Wacom Bluetooth modules
    • +
    • Ignore virtual M.2 ATA devices
    • +
    • Preserve NEEDS_REBOOT on successful update
    • +
    • Prevent a corrupt PHAT table from allocating lots of memory
    • +
    • Read the Redfish SMBIOS table when required
    • +
    • Remove the vendor string from the device name where required
    • +
    • Save the update state to the database correctly all of the time
    • +
    • Switch from sysctl to ioctl for ESRT on FreeBSD
    • +
    • Try reading from /sys/class/dmi if SMBIOS direct access fails
    • +
    • Watch for children added or removed after setup has been completed
    • +
    • Work around a XCC-ism on Lenovo hardware
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • ModemManager devices supporting Firehose or MBIM QDU
    • +
    • More models of RTS54HUB
    • +
    • More Poly DFU devices
    • +
    • Parade LSPCON
    • +
    • PixArt receiver and wireless hardware
    • +
    • Realtek MST with RTD2142
    • +
    • SuperIO IT5570
    • +
    • USB4 Dell dock
    • +
    +
    +
    + + +

    + This release adds the following features: +

    +
      +
    • Add FreeBSD UEFI Capsule support
    • +
    • Add generic ModemManager support for PCI based modems
    • +
    • Add initial support for USB4 module in the Dell dock
    • +
    • Add support for sibling requirements
    • +
    • Add support for the ACPI PHAT table
    • +
    • Allow building the documentation with gi-docgen and gtk-doc
    • +
    • Support binary artifact resources in cabinet archives
    • +
    • Use GProxyResolver to get the system proxy setting for a given URL
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Ask the user to confirm all CLI actions
    • +
    • Check the versions of libfwupd and libfwupdplugin at startup
    • +
    • Do not prevent firmware updates on desktop hardware
    • +
    • Do not show an invalid DFU warning on attach
    • +
    • Fail parsing if wacom firmware sections are not in sorted order
    • +
    • Fall back to binary files when flashing STM32 hardware
    • +
    • Fix a critical warning when downloading files
    • +
    • Fix a possible critical warning due to a bug in type casting
    • +
    • Fix a regression in updating the WD19TB dock
    • +
    • Fix GUID generation on pixart hardware
    • +
    • Fix the VLI i2c device enumeration, e.g. MSP430
    • +
    • Follow HTTP 3XX redirects when downloading files
    • +
    • Force the device locker to close() an aborted open()
    • +
    • Handle bsdisks' UDisks2 implementation on FreeBSD
    • +
    • Only lock fwupdtool when loading the engine
    • +
    • Read current Wacom firmware index before finding image to write
    • +
    • Support all hash types when loading cabinet archives
    • +
    • Support mirroring the detach and update images
    • +
    • Switch lock directory from /var/run to /run/lock
    • +
    +

    This release adds support for the following hardware:

    +
      +
    • Minibons devices
    • +
    • More 8BitDo hardware
    • +
    • More Synaptics Prometheus hardware
    • +
    • RTD21xx devices in background mode
    • +
    • Some Kingston SSD and NVMe hardware
    • +
    +
    +
    + + +

    + This is the first release of the 1.6.x series, and since 1.5.x + some internal plugin API has been changed and removed. + Although we've tested this release on all the hardware we have + regression tests for, bugs may have crept in; please report failures + to the issue tracker as required. +

    +

    + There are several new plugins adding support for new hardware + and a lot of code has been migrated to the new plugin API. + The public libfwupd API also has some trivial additions, although no + action is required. +

    +

    + This release adds the following features: +

    +
      +
    • Add a composite ID that is used to identify dock device components
    • +
    • Add an Intel Flash Descriptor parser
    • +
    • Add API to allow the device to report its own battery level
    • +
    • Add API to recount why the the device is non-updatable
    • +
    • Add lspcon-i2c-spi programmer support
    • +
    • Add more hardware support to the pixart-rf plugin
    • +
    • Add some more new category types for firmware to use
    • +
    • Add support for downloading the SPI image from the Intel eSPI device
    • +
    • Add support for some Analogix hardware
    • +
    • Add support for writing SREC firmware
    • +
    • Add the firmware-sign command to fwupdtool to allow resigning archives
    • +
    • Split UEFI EFI binary into a subproject
    • +
    • Use an OFD or Unix lock to prevent more than one fwupdtool process
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Actually write the bcm57xx stage1 version into the file
    • +
    • Add option to disable the UEFI capsule splash screen generation
    • +
    • Avoid use-after-free when specifying the VID/PID in dfu-tool
    • +
    • Cancel the GDBusObjectManager operation to fix a potential crash
    • +
    • Check PixArt firmware compatibility with hardware before flashing
    • +
    • Do not check for native dependencies as target dependencies
    • +
    • Do not use help2man to build manual pages
    • +
    • Fix a crash when shutting down the daemon
    • +
    • Fix build on musl
    • +
    • Fix build when using BSD
    • +
    • Fix /etc/os-release ID_LIKE field parsing
    • +
    • Force the synaptics-rmi hardware into IEP mode as required
    • +
    • Never allow D-Bus replacement when a firmware update is in operation
    • +
    • Offer the user to refresh the remote after enabling
    • +
    • Remove unused, unsafe and deprecated functions from libfwupdplugin
    • +
    • Simplify asking the user about reviews
    • +
    • Write BMP data directly without using PIL
    • +
    • Write synaptics-rmi files with valid checksum data
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add initial support for Bluez bluetooth devices
    • +
    • Add more supported pixart devices
    • +
    • Add support for the RTD21xx HDMI converter
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Convert MBR types to GPT GUIDs to help find the ESP
    • +
    • Do not allow updating a synaptics-mst device with no customer ID
    • +
    • Drop unused heap pages after startup has completed
    • +
    • Ensure SBAT metadata is added correctly
    • +
    • Move the plugin build logic to the plugins themselves
    • +
    • Only allow verify-update for plugins that support CAN_VERIFY
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add SBAT metadata to the fwupd EFI binary
    • +
    • Add support for GD32VF103 as found in the Longan Nano
    • +
    • Add support for RMI PS2 devices
    • +
    • Add support for the System76 Keyboard
    • +
    • Allow downloading firmware from IPFS
    • +
    • Install the UX data into a single .tar.xz file
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add support for the Starlabs LabTop L4
    • +
    • Allow using an external ESP again
    • +
    • Ask the user to reboot when required if downgrading
    • +
    • Be more paranoid when parsing ASCII buffers and devices
    • +
    • Check if the fwupd BootXXXX entry exists on failure
    • +
    • Clear the pending flag if restarting the system
    • +
    • Do not allow flashing using flashrom if BLE is enabled
    • +
    • Do not allow Lenovo hardware to install multiple capsules
    • +
    • Do not parse the OptionROM image
    • +
    • Do not show Unknown [***] for every client connection
    • +
    • Fix dnload wBlockNum wraparound for ST devices
    • +
    • Fix OOM when using large ArchiveSizeMax values
    • +
    • Fix several crashes spotted by AddressSanitizer
    • +
    • Fix several places where the Goodix MOC plugin could crash
    • +
    • Include the PCR0 to the report metadata
    • +
    • Report the lockdown status from UEFI and SuperIO plugins
    • +
    • Show a console warning if the system clock is not set
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add a plugin to update PixArt RF devices
    • +
    • Add new hardware to use the elantp and rts54hid plugins
    • +
    • Allow specifying more than one VendorID for a device
    • +
    • Detect the AMD TSME encryption state for HSI-4
    • +
    • Detect the AMI PK test key is not installed for HSI-1
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Fix flashing a fingerprint reader that is in use
    • +
    • Fix several critical warnings when parsing invalid firmware
    • +
    • Fix updating DFU devices that use DNLOAD_BUSY
    • +
    • Ignore the legacy UEFI OVMF dummy GUID
    • +
    • Make libfwupd more thread safe to fix a crash in gnome-software
    • +
    • Never show unprintable chars from invalid firmware in the logs
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add Maple Ridge Thunderbolt firmware parsing support
    • +
    • Add --no-remote-check to ignore checking for download remotes
    • +
    • Allow creating FMAP and Synaptics firmware using builder.xml
    • +
    • Build a test harness that uses honggfuzz to fuzz firmware
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allow using fwupdtool as non-root for firmware commands
    • +
    • Do not trust the Block.HintSystem boolean for ESP filtering
    • +
    • Fix a memory leak when parsing Synaptics firmware
    • +
    • Fix a possible crash when reading the Goodix MOC USB request
    • +
    • Fix crashes when parsing invalid FMAP, DMC, Solokey and Synaptics images
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Allow setting the GMainContext when used for sync methods
    • +
    • Export the driver name from FuUdevDevice
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add a UEFI quirk for Star Labs Lite Mk III
    • +
    • Add the device firmware ID for serio class hardware
    • +
    • Allow the client to send legacy PKCS7 and GPG signatures
    • +
    • Do not use accidentally depend on new meson versions
    • +
    • Fix a possible critical warning due to missing retval
    • +
    • Fix the endianness for the CRC check in bcm57xx
    • +
    • Lower the CURL version required to fix RHEL
    • +
    • Make sure the correct interface number is used for QMI
    • +
    • Mark more user-visible strings as translatable
    • +
    • Restrict loading component types of firmware
    • +
    • Validate ModemManager firmware update method combinations
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add a flag to indicate if packages are supported
    • +
    • Add a plugin for the Pinebook Pro laptop
    • +
    • Allow components to set the icon from the metadata
    • +
    • Switch from libsoup to libcurl for downloading data
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Fall back to FAT32 internal partitions for detecting ESP
    • +
    • Fix detection of ColorHug version on older firmware versions
    • +
    • Fix reading BCM57XX vendor and device ids from firmware
    • +
    • Fix replugging the MSP430 device
    • +
    • Fix sync method when called from threads without a context
    • +
    • Ignore an invalid vendor-id when adding releases for display
    • +
    • Improve synaptics-mst reliability when writing data
    • +
    • Install modules-load configs in the correct directory
    • +
    • Notify the service manager when idle-quitting
    • +
    • Only download the remote metadata as required
    • +
    • Remove HSI update and attestation suffixes
    • +
    • Restore recognizing GPG and PKCS7 signature types in libfwupd
    • +
    • Set the SMBIOS chassis type to portable if a DT battery exists
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Include the amount of NVRAM size in use in the LVFS failure report
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Delete unused EFI variables when deploying firmware
    • +
    • Fix probe warning for the Logitech Unifying device
    • +
    • Make bcm57xx hotplug more reliable
    • +
    • Recognize authorized thunderbolt value of 2
    • +
    • Remove the duplicate parent-child data in FwupdDevice and FuDevice
    • +
    • Show a less scary fwupdate output for devices without info
    • +
    • Show a link to discover more information about a specific plugin failure
    • +
    • Use a different Device ID for the OptionROM devices
    • +
    • Use UDisks to find out if swap devices are encrypted
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add a compatible re-implementation of the rhboot dbxtool
    • +
    • Add async versions of the library for GUI tools
    • +
    • Add commands for interacting with the ESP to fwupdtool
    • +
    • Add firmware-extract subcommand to fwupdtool
    • +
    • Add FwupdPlugin so we can convey enumerated system errors to the end user
    • +
    • Add plugin for Goodix fingerprint sensors
    • +
    • Add plugin that can update the BCM5719 network adapter
    • +
    • Add plugin to update Elan Touchpads using HID
    • +
    • Add support for a delayed activation flow for Thunderbolt
    • +
    • Add support for ChromeOS Quiche and Gingerbread
    • +
    • Add support for Hyper hardware
    • +
    • Add support for the Host Security ID
    • +
    • Add support for ThunderBolt retimers
    • +
    • Add switch-branch command to fwupdtool and fwupdmgr
    • +
    • Allow blocking specific firmware releases by checksum
    • +
    • Allow constructing a firmware with multiple images
    • +
    • Allow firmware to require specific features from front-end clients
    • +
    • Allow updating the dbx using the LVFS, validating it is safe to apply
    • +
    • Include the HSI results and attributes in the uploaded report
    • +
    • Support loading DMI data from DT systems
    • +
    • Support LVFS::UpdateImage for GUI clients
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allow compiling the daemon without polkit support
    • +
    • Always look at all TPM eventlog supported algorithms
    • +
    • Change all instances of master/slave to initiator/target
    • +
    • Correctly order devices when using logical parents
    • +
    • Do not dedupe NVMe or VLI PD devices
    • +
    • Do not expose the VLI shared-SPI devices on the USB2 recovery device
    • +
    • Do not fix up the version on post-update mismatch
    • +
    • Download the metadata first when using 'fwupdtool refresh'
    • +
    • Drop efivar dependency
    • +
    • Drop support for ThunderBolt force power due to hardware issues
    • +
    • Fix setting BootNext correctly when multiple updates are scheduled
    • +
    • Fix the topology of the audio device on the Lenovo TR dock
    • +
    • Make return code different for get-updates with no updates
    • +
    • Make specific authorizations also imply others
    • +
    • Make TPM support more optional
    • +
    • Parse the HEX version before comparing for equality
    • +
    • Prevent dell-dock updates to occur via synaptics-mst plugin
    • +
    • Record the UEFI failure in more cases
    • +
    • Retry the HID SetReport to fix flashing the TB3 dock
    • +
    • Show an error when a plugin is missing dependencies
    • +
    • Use libxmlb bound parameters to speed up the device verification
    • +
    • Use pkttyagent to request user passwords if running without GUI
    • +
    • Use the JCat file to select the metadata file
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Allow adding a device 'proxy' device that can do actions on it
    • +
    • Allow specifying the device on the command line by GUID
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add a device quirk that forces an explicit device-id match
    • +
    • Allow a device to set the logical or physical ID during ->setup()
    • +
    • Correctly format firmware version of Dynabook X30 and X40
    • +
    • Do not show safe mode errors for USB4 host controllers
    • +
    • Do not show the USB 2 VLI recovery devices for USB 3 hubs
    • +
    • Fix the correct DeviceID set by GetDetails
    • +
    • Make the EP963X plugin actually work on real hardware
    • +
    • Make the tss2-esys dep conditional for RHEL 8
    • +
    • Only update the FW2 partition of the ThinkPad USB-C Dock Gen2
    • +
    • Prefer to update the child device first if the order is unspecified
    • +
    • Refresh device name and format before setting supported flag
    • +
    • Reset the progressbar time estimate if the percentage is invalid
    • +
    • Set the CCGX device name and summary from quirk files
    • +
    • Wait for the cxaudio device to reboot after writing firmware
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add 'firmware-convert' subcommand to fwupdtool
    • +
    • Add fu_device_retry() API
    • +
    • Add FuHidDevice abstraction
    • +
    • Add plugin for CPU microcode
    • +
    • Add plugin for Cypress CCGX hardware
    • +
    • Add plugin for EP963x hardware
    • +
    • Add 'reinstall' command to fu-tool
    • +
    • Allow server metadata to set the device name and version format
    • +
    • Export the device state as part of the D-Bus interface
    • +
    • Export the release creation time and urgency
    • +
    • Introduce a new VersionFormat of 'hex'
    • +
    • Use Jcat files in firmware archives and for metadata
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Actually reload the DFU device after upgrade has completed
    • +
    • Add a lot of missing metadata about wacom-usb devices
    • +
    • Add a way to set the device timeout from a quirk
    • +
    • Add STM32F745 DfuSe version quirk
    • +
    • Allow waiting for the parent device when replugging
    • +
    • Always check for 'PLAIN' when doing vercmp() operations
    • +
    • Apply version format to releases and devices at same time
    • +
    • Check the firmware requirements before adding 'SUPPORTED'
    • +
    • Correctly attach VL103 after a firmware update
    • +
    • Do not allow devices that have no vendor ID to be 'UPDATABLE'
    • +
    • Do not conditionalize attach() and detach() on 'IS_BOOTLOADER'
    • +
    • Do not use shim for non-secure boot configurations
    • +
    • Fix a crash when removing device parents
    • +
    • Fix a difficult-to-trigger daemon hang when replugging devices
    • +
    • Fix a runtime error when detaching MSP430
    • +
    • Fix CounterpartGuid when there is more than one supported device
    • +
    • Fix reporting Synaptics cxaudio version number
    • +
    • Load the signature to get the aliased CDN-safe version of the metadata
    • +
    • Never add USB hub devices that are not upgradable
    • +
    • Only auto-add counterpart GUIDs when required
    • +
    • Parse the CSR firmware as a DFU file
    • +
    • Set the protocol when updating logitech HID++ devices
    • +
    • When TPM PCR0 measurements fail, query if secure boot is available and enabled
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Added completion script for fish shell
    • +
    • Inihbit all power management actions using logind when updating
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Always check for PLAIN when doing vercmp() operations
    • +
    • Always return AppStream markup for remote agreements
    • +
    • Apply UEFI capsule update even with single valid capsule
    • +
    • Check the device protocol before de-duping devices
    • +
    • Copy the version and format from donor device in get-details
    • +
    • Correctly append the release to devices in `fwupdtool get-details`
    • +
    • Decrease minimum battery requirement to 10%
    • +
    • Discard the reason upgrades aren't available
    • +
    • Do not fail loading in /etc/machine-id is not available
    • +
    • Fix a critical warning when installing some firmware
    • +
    • For the `get-details` command make sure to always show devices
    • +
    • Set the MSP430 version format to pair
    • +
    • Switch off the ATA verbose logging by default
    • +
    • Use unknown for version format by default on get-details
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add an extra instance ID to disambiguate USB hubs
    • +
    • Add a plugin to update PD controllers by Fresco Logic
    • +
    • Replay the TPM event log to get the PCRx values
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Fix updating Synaptics MST devics with no PCI parent
    • +
    • Correctly reset VL100 PD devices
    • +
    • Do not rewrite BootOrder in the EFI helper
    • +
    • Do not use vercmp when the device version format is plain
    • +
    • Fix firmware regression in the EFI capsule helper
    • +
    • Ignore Unifying detach failures
    • +
    • Make the cxaudio version match that of the existing Windows tools
    • +
    • Set up more parent devices for various Lenovo USB hubs
    • +
    • Support the new gnuefi file locations
    • +
    • Use the correct command to get the VLI device firmware version
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add 'get-remotes' and 'refresh' to fwupdtool
    • +
    • Add support for standalone VIA PD devices
    • +
    • Allow applying all releases to get to a target version
    • +
    • Discourage command line metadata refreshes more than once per day
    • +
    • Generate a win32 setup binary
    • +
    • Get the list of updates in JSON format from fwupdagent
    • +
    • Move MOTD population into the daemon
    • +
    • Shut down automatically when there is system memory pressure
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Correctly delete UEFI variables
    • +
    • Correctly import PKCS-7 remote metadata
    • +
    • Disable the battery percentage checks if UPower is unavailable
    • +
    • Do not always get the vendor ID for udev devices using the parent
    • +
    • Fix display of UTF-8 characters on Windows
    • +
    • Show the device parent if there is an interesting child
    • +
    • Use a different protocol ID for VIA i2c devices
    • +
    • Use the correct timeout for Logitech IO channel writes
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add a new plugin that can parse the TPM event log
    • +
    • Add a new plugin that exposes the TPM device firmware version
    • +
    • Allow building on Windows with MinGW
    • +
    • Enforce that device protocol matches the metadata value
    • +
    • Export the device protocol and raw device version to the client --verbose output
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add a dell-bios version format to match what is shown on the vendor website
    • +
    • Allow incremental version major and minor number for Synaptics Prometheus devices
    • +
    • Clarify error messages when no upgrades are available
    • +
    • Correct the default prompt for reboot/shutdown
    • +
    • Do not expose bootloader version errors to users
    • +
    • Fix the quirk for the legacy VIA 813 usbhub chip
    • +
    • Hardcode the vendor ID for Dell dock hardware
    • +
    • Only check the vendor ID if the device has one set
    • +
    • Return exit status success if there is no firmware to be updated
    • +
    • Set the correct vendor eMMC ID prefix
    • +
    • Use the baseboard vendor as the superio vendor ID
    • +
    • Use the BIOS vendor as the coreboot and flashrom vendor ID
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Convert libfwupdprivate to a shared library libfwupdplugin
    • +
    • Create a REV_00 instance ID as this may be what the vendor needs to target
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Improve coreboot version detection
    • +
    • Invert default behavior to be safer for reboot and shutdown prompts
    • +
    • Reload the Synaptics prometheus device version after update
    • +
    • Use the correct unlocker when using GRWLock
    • +
    • Whitelist VIA USB hub PD and I²C devices
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add a new property Interactive to the daemon
    • +
    • Add a new script for installing a Dell BIOS from an EXE file
    • +
    • Add support for Foxconn T77W968 and DW5821e eSIM
    • +
    • Add support for matching firmware requirements on device parents
    • +
    • Add support for writing VIA PD and I2C devices
    • +
    • Add versions formats for the Microsoft Surface devices
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Allows confined snaps to activate fwupd via D-Bus
    • +
    • Correct Wacom panel HWID support
    • +
    • Don't assume all udev devices have device_file
    • +
    • Dynamically determine release version
    • +
    • Fall back to `ID_LIKE` when the path for `ID` doesn't exist
    • +
    • Fix a fastboot regression when updating modem firmware
    • +
    • Fix regression when coldplugging superio devices
    • +
    • Fix the linking of the UEFI update binary
    • +
    • Fix the vendor id of hidraw devices
    • +
    • Make loading USB device strings non-fatal
    • +
    • Reject invalid Synaptics MST chip IDs
    • +
    • Skip cleanup after device is done updating if required
    • +
    +
    +
    + + +

    This release adds the following features:

    +
      +
    • Add a plugin for systems running coreboot
    • +
    • Add a plugin to update eMMC devices
    • +
    • Add a plugin to update Synaptics RMI4 devices
    • +
    • Add a plugin to update VIA USB hub hardware
    • +
    • Add some success messages when CLI tasks have completed
    • +
    • Add support for automatically uploading reports
    • +
    • Add support for `fwupdmgr reinstall`
    • +
    • Allow fwupdtool to dump details of common firmware formats
    • +
    • Use XMLb to query quirks to reduce the RSS when running
    • +
    +

    This release fixes the following bugs:

    +
      +
    • Add several quirks for Realtek webcams
    • +
    • Add support for the 8bitdo SN30Pro+
    • +
    • Add support for the ThinkPad USB-C Dock Gen2 audio device
    • +
    • Always report the update-error correctly for multiple updates
    • +
    • Create a unique GUID for the Thunderbolt controller path
    • +
    • Fix a regression for Wacom EMR devices
    • +
    • Move the Jabra-specific detach out into its own plugin
    • +
    • Recognize new 'generation' Thunderbolt sysfs attribute for USB4
    • +
    • Reduce more boilerplate in plugins, modernizing where required
    • +
    • Remove unused DFU functionality
    • +
    • Rework ESP path detection and lifecycle to auto-unmount when required
    • +
    • Show a useful error for Logitech devices that cannot self-reset
    • +
    • Use correct method for stopping systemd units
    • +
    • Use device safety flags to show prompts before installing updates
    • +
    • Use `genpeimg` to mark ASLR and DP/NX on EFI binary
    • +
    • Use will-disappear flag for 8bitdo SF30/SN30 controllers
    • +
    +
    +
    + + +

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

    +
      +
    • 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 required 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:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following feature:

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

    This release fixes the following bugs:

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

    This release fixes the following bugs:

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

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

    +

    This release adds the following feature:

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

    This release fixes the following bugs:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release fixes the following bugs:

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

    This release adds the following feature:

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

    This release fixes the following bugs:

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

    This release adds the following feature:

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

    This release fixes the following bugs:

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

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

    +

    This release adds the following features:

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

    This release fixes the following bugs:

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

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

    +

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

    This release adds the following features:

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

    This release fixes the following bugs:

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

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

    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/data/org.freedesktop.fwupd.service.in b/fwupd-1.8.6/data/org.freedesktop.fwupd.service.in new file mode 100644 index 0000000000000000000000000000000000000000..53f5171093fd64ca038599fa1f2a0ac877e1fe57 --- /dev/null +++ b/fwupd-1.8.6/data/org.freedesktop.fwupd.service.in @@ -0,0 +1,7 @@ +[D-BUS Service] +Name=org.freedesktop.fwupd +Documentation=https://fwupd.org/ +Exec=@libexecdir@/fwupd/fwupd +User=root +SystemdService=fwupd.service +AssumedAppArmorLabel=unconfined diff --git a/fwupd-1.8.6/data/org.freedesktop.fwupd.svg b/fwupd-1.8.6/data/org.freedesktop.fwupd.svg new file mode 100644 index 0000000000000000000000000000000000000000..82a4b4b50721608d6f7fc70da609ca43bd14114d --- /dev/null +++ b/fwupd-1.8.6/data/org.freedesktop.fwupd.svg @@ -0,0 +1,209 @@ + + + + Adwaita Icon Template + + + + + + + + + + + image/svg+xml + + + + GNOME Design Team + + + + + Adwaita Icon Template + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fwupd + firmwareupdater + diff --git a/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Foundation-Firmware b/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Foundation-Firmware new file mode 100644 index 0000000000000000000000000000000000000000..e03bf682cd19c09f7655b7f12fbaf246b7738bbc --- /dev/null +++ b/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Foundation-Firmware @@ -0,0 +1,37 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQENBFsDBO0BCACjkrMuRgaWxP88Ubc1Xar5mxLMNXAJUzeYQVh/LnkEwytO3Ekh +GDH8Ch78269MiezkJmUGUUGyjKhqZECtZaKGp4LSl6gTPFDFHS/xKaq8L+8G/v4K +LEtZE03PKSnY2XYnf+3Kc6tmIZBB67yRg/79p3OpFd95wqyu+2c1cVkjCA1Q8XpO +bgCDfNacU3Yag6GXYlKpLmlVkYaAptjV0FrbLLBjaHvFeGAXgRUlv0PRyDjKD2XT +PEBtbg2+qTxPJIOlFgGNsJjkFL7R3mWwn00yF4jt9JMYGkpNAuFg1c/TZ1v64wlP +N6i2DsDwIMQ9S/ahJWdX/zP4JMTdpYNP91o9ABEBAAG0WkxWRlMgYSBTZXJpZXMg +b2YgTEYgUHJvamVjdHMsIExMQyAoTFZGUyBhIFNlcmllcyBvZiBMRiBQcm9qZWN0 +cywgTExDKSA8ZmlybXdhcmVAZnd1cGQub3JnPokBOAQTAQIAIgUCWwME7QIbAwYL +CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQxjYXh6CoSeE3pQf+MlPyQzkVhdRU +fqjzu3Ba9dZ+hmsol2ooFmytd058AIO1eKie2LxnkQw4P5prFOnVWbbFi79vUEzQ +KK5xpW46nEglU14xYgv+4cMlVNWBYIVsXIIKKs2z4gM8oCA20JpVunnVbaViWTna +YwiUbTniIvLQH4RLo66Qzd0b3Z8ycK0bVbCl4RazSbWAGHMAnqm0xSQqsWRQwaHk +vxXEbjb0hfO4P/PZui5vFGr82tZUdGVKift1JlOzDMjVcmFIuYITHQyGaZ5Xsh2h +Pu9gI9Vp7OQoA7dJ+Qc5LBk3rVxN5Zx3jUSWOd7Dtvrm+ArBy/y0MCVyv9fcSvAH +vEv9T4zhE7kBDQRbAwTtAQgAwjiRDT3qinYz7b1s9SM2Y7aZG9JhWi/Zp2qGzGVX +QpVV2EK3PnAfZpyt99I63N7d/QDPqLFmTyjlv5cMb3QxVyXGBCGGz3OiWYY0NaDd +s1sp3J+TT6bHNG/Lo+vgcTRvzHvq7HbbeNssIMLr6MDAj0fZSh5UlAfQdC3qz90A +qIPGcx5AwgCwXLDqzCusz17Erc3IK/TG4r0AbRFtGx0hl2w5EOn7funw8BhnJ59w +OMsq7sXDmFff4hQjgQoDezMkA1EgzFokRY7pToLG3X1KdDXKR0edQ3+1mlJTf9XN +Zaz+ortKsugmTmzsF4DlRhq0Ok0VWuq0rzWndpFyFvIewwARAQABiQI+BBgBAgAJ +BQJbAwTtAhsuASkJEMY2F4egqEnhwF0gBBkBAgAGBQJbAwTtAAoJENTAcNuxNA6+ +1T4H/jsWVrANLKElBwZJpPOVy4Haw7UG+zk7lfwck0H8J9ShDtwhNTg03BiOONP/ +JuR8XvOxjqdUexEmAJdQCtxJgLTlI40xSlcmSEIneCamOhA/I/T+nkXFAfV65FKF ++OR3Ee122sUMXvLCcNvcbM23GIWiN/YmYFlK1PGNe3oOn4MWJ/28dbCLuEOPP4Tu +VJM/RpZ65qCnojc1meMcPJxI5iNWZtG9SmmGWDI3f7mDK+dtD06VLmPd3uc8P23t +YN0o/Jkgz2oV5GKD9t2+Ne2C5H5xZ0aE7dDVM8ErEPd3wTS+bC8GhPxHjj4A6HyA +MNZAEZSAJ4TVpzbfyOMcpSRK4yVLTAgAmTVV79EH/14s60Ya0LCtpifpvpZimbbo +xBGFaymvX7doxZyITC66JNTzT4Eixp8FNRloKWkEo6gPwA6qshlhc0HpmiqNmC+k +QNYIVeanrflV2bVzYSdsIzrZLUTd6P835YYxD1nsQGwnCqeeD0gJlV+alo0LYTRt +lFwNYxHU7BM09wu7sUEvYW5wt4TXPUrZ9jV+BM9UQLatW+S5vO41wqfTmPKGEqct +doW2ZYUgCc4aFGTOj3fA5hoK6EjAQpVdkcA7fiRYLn5AIvM4FGxIqI5khjsZUEPa +9Gpui69Y4a+x4QEIDj/WAHOOMSIg/n96e+uRdGXN4c8nr7JSASxlow== +=RFA4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Foundation-Metadata b/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Foundation-Metadata new file mode 100644 index 0000000000000000000000000000000000000000..9ecdb0d08240ced9e96b4f507b2f09e43ee8c177 --- /dev/null +++ b/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Foundation-Metadata @@ -0,0 +1,37 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQENBFsDA8IBCACgSd0NAJFEUjqcyv38If9f/FCQi2C2MQbzNt05DblHAg6eBk/V +eYM/GI+Cr9sPwxs8ZWtN0IRoQp/d7MRxe43zFT4IH2N4RVaBTgWCoRerPn09k4K/ +2fk6GWIY8lgxlKV/LinM5XkFDXv6Zf/o8Nv/i9bVO9Dv1bVh1ThgA3xy8WIzUQge +cVviEjEYG10TX+NENGgdA+aD/fMk4Wzwz6L48D+ryTiXGFnwoizifr9DIn4yIp6i +b4vTQY96VoXHSgU6JRvYjzPPME+NmmcLgW0hGJlVvi8RL+7wJPVeS0ioqPzMtonS +evrwVv5E5k87i+LS/vdVu3SIUzR9JLIXtvNNABEBAAG0WkxWRlMgYSBTZXJpZXMg +b2YgTEYgUHJvamVjdHMsIExMQyAoTFZGUyBhIFNlcmllcyBvZiBMRiBQcm9qZWN0 +cywgTExDKSA8bWV0YWRhdGFAZnd1cGQub3JnPokBOAQTAQIAIgUCWwMDwgIbAwYL +CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQCm3O9rRvPb/wsQf7BGEkgT08Bx1v +657l//B/yniB7VGH+4plrX2OkWVzDxn+z6APcgqDMnzwatddxM7J6mm4yxA0nFKs +D5BueTItYv5zQCzX8e4TM1oaUWBr8nACK7/WSxNIC+GRUKl68v+dIbp1bhdmAYzj +wTn/uDW47Z7gtQYDJJit2MsKfu5Lwar7w+divH+6KWdaMctm2injYYIlpKCjffl8 +RZ4PgX7lN5C0s9kWDkH9iI2i5aqJaI8gZHK6EKwitmsHMJBczekymlXh4MheYCKm +IWJLh8tvK6LaVoddwfle4orhq4b7doA57H8BgJDDz+MxyjZn+GAireCMaJjtCSWv +q5bnsdnMH7kBDQRbAwPCAQgAq6hTejZZcIXnfA4Z/1c03USKnK8SeG/yqlggvpyZ +9C3hAvJITtQ7iZmz0VKOjwQtds52qnZYbOn/Fr+4Ef+cbFZRNxamZ4kf0XSAVXSv +EODMFj+BpdoDwAWhJcvyijoMV6N+gbCG+UedMNnpe25tlCRjouBSEF5KWJabejdh +iZ8ikN+HNJa5uQ68F76aDP1BOE0XiLrOZC0MZ7mvbyOi9LPXFyV2EuVj+gt7r+2x +OlSMmor7RTEAJcBK9tiew5LhNHeaqbe3xnOcpWrAaoVdIed7h5YbbetTFMWHCPGJ +raGGRSv3OrZDfQXZvOi+k6I6wWEQrsUCIiVKPefU+5xSpwARAQABiQI+BBgBAgAJ +BQJbAwPCAhsuASkJEAptzva0bz2/wF0gBBkBAgAGBQJbAwPCAAoJEL4e3StH4Ita +hvAIAIlZlrAJrbj7aQ3VkFcTJJC+68BaGNqte2S3Zv7ONLjT1kc0xSflf4c4MHef +q7WmLLsjUHocD0a8SUsR1V/Fp36qG1Yr7mfhf7dY3TwwUw9VXQoEdpEWZES/IJ4d +oPXSowk8eSbb72g4dvt9p+wlDKlsT7YHjmfn9Zct5FqcQ2kV9+900DtWlvPK8hRR +N0FibLR9GMorSFfHotFQ8AjdiXQUo+6GTb2HDJ1+aI4fpYo8NnqUs0wvCVtxrqn9 +JBzSgKkFAjHOiz/C6a0JOBtmH6tL41U7ZtUI2idQWsHiufyCTVOHRbyHoOxxFEpX +Xu3l2vckZaENNyNOGCqrIo8npFQczAf/REhK0Z8UumttRm2CQkJnkqRgLnIoJq3i +l7ljKjFi06XLJxOjLLpIFBH/yAJ5YbsYZt+XuT3WgORfHPDU3xepWGfQj4QFcsny +GWStnu6Ej/PogJyyvZ1hFpKu8g2cIp04vEebWeYHAMorvwty/p+MJnH6NeSQq5Pe +n22AuKKENtgYwulgJH0VQ0lJ5k1CcFuEuZqnXk5CUIC4tStdUS6hgn+vEkaJ5dX8 +nj/X9etEj2nnQGIL+7Dh2Z+UGaZqxSa1tjF5+7uC85K0NTq6Cpc+Bnd7Gqb+afnn +/iFHG21+hpjQmdSvpbGTabrM9gxd0iuDFXSFSttMtSC+gR5VcNQpUA== +=7Jrd +-----END PGP PUBLIC KEY BLOCK----- diff --git a/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Vendor-Firmware-Service b/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Vendor-Firmware-Service new file mode 100644 index 0000000000000000000000000000000000000000..dda1c3f4795f0aada2800741a8accd6574a13aeb --- /dev/null +++ b/fwupd-1.8.6/data/pki/GPG-KEY-Linux-Vendor-Firmware-Service @@ -0,0 +1,19 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2 + +mQENBFWt/98BCADZ4+lUHSp4OMlzVf4HlJNLJ7Ks5QxGwL/hy2wChoNLuA/j4GNM +9mBZutKynYmphD0Mi4XjXn7JNXyuJa8Qutz98/Iyhsjq4LeiL9ayaKMXT+3pKlTm +Gd/Fzo3QEOqTJ5s2RamrfwFIVuvwoj+rNmzj5fUCgoDOZeqVl6gxb7ZPzL8sWTOU +iLeGMSzZBGE0ioJ82PZzsHelrrObDP1mMre1jQ6zxLlnYUlLvtJpydAfeBxU+6yL +fgPeoFeuCE6JIszyWuyAgpBpYSGgj1bpt9Sxc2+MoZ0BjDzoijZqt4O48gYuEaLf +iqYzQybe1JF0McO4C0dmjdKQz2qm0XrQyNhVABEBAAG0LkxpbnV4IFZlbmRvciBG +aXJtd2FyZSBTZXJ2aWNlIDxzaWduQGZ3dXBkLm9yZz6JATcEEwEIACEFAlWt/98C +GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQSKbYDkU4usJjjQgAzmTcA8qH +s+1kieEZvsUzH4wun2Hlz7R5FRc/7BijgIQAA9TTrJnwbJmEBzEvHv7FKQLiBN3a +0lQIZgahmcUt1qm6VW94VAio+SDCdqTx73wUsgM3t9sAwKxkEdJQQoO8PqYHV3uK +rq0t2YjXglIBHRDiJlOTAR3if37OCDKCcHOOODqYrsN7wNleez+ulkDyP7C7ZTbm +/A7Xec73t2OQUnejU0uvRvc7VSnQDRFBHA9TPiBhbruMw+ZX+z/wfPd7x2RCqoOE +vHh+QofE41Ya2QOkT96fAKfcJ+gvIbmwp3w7h+Hus1h3xDrykCG9cCxuH0HxooVI +XL3IlFx/6OUpBA== +=6Dz2 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/fwupd-1.8.6/data/pki/LVFS-CA.pem b/fwupd-1.8.6/data/pki/LVFS-CA.pem new file mode 100644 index 0000000000000000000000000000000000000000..560c037b487cde0a2842203158894317d30f0033 --- /dev/null +++ b/fwupd-1.8.6/data/pki/LVFS-CA.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEqjCCAxKgAwIBAgIBATANBgkqhkiG9w0BAQsFADA6MRAwDgYDVQQDEwdMVkZT +IENBMSYwJAYDVQQKEx1MaW51eCBWZW5kb3IgRmlybXdhcmUgUHJvamVjdDAeFw0x +NzA4MDEwMDAwMDBaFw00NzA4MDEwMDAwMDBaMDoxEDAOBgNVBAMTB0xWRlMgQ0Ex +JjAkBgNVBAoTHUxpbnV4IFZlbmRvciBGaXJtd2FyZSBQcm9qZWN0MIIBojANBgkq +hkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAtfUXH3NwDJzWyhkPyPcFI899+tPZ/SMp +OkDtRr9dJjgQkSO9jKCue4DVq8Bd9RcL76F7XnEKG0LiuKnr+D7+x86TtDAPCbkP +WAS7fAaetLtiNFU96cokhjeALB3hyamkMQnCw+5Ov+sHJfGI9Bor9UaIIbIB4r8v +oU1WpE7N6Ix2qsS5b88+Z6EIV6CX8RbciOC/TfyYVnpF1cd4l7LH7TtL+ERpsPwv +rk0JgVoRzG3BT5yYfuxHIe4H4Axh95tW9i6urzyQkXRz14twwwcEDvl5ALrBLNJJ +8EDz9oR8HBPbxbd4i2dBfziY7TW4o/VgZKTGWA39JfwWNc5RxaYzBhBmg5nRcVFs +E7PlovhyFH/0RNm/3E6vZQCeM+FNps0ovVq8Yqg8whL/yZ0iNlavCGTWhaxisVHG +7mQopV4jZlafxvrcBFzK8RPe8Gi04FFn4ugZtJnOuMel+AiADhgtWZCENiyWV+V7 +WF1SFF4HaHuS8qqna/p9lrpVq6TBr0WRAgMBAAGjgbowgbcwEgYDVR0TAQH/BAgw +BgEB/wIBATAwBgNVHREEKTAnhhVodHRwOi8vd3d3LmZ3dXBkLm9yZy+BDnNpZ25A +Znd1cGQub3JnMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA8GA1UdDwEB/wQFAwMHBgAw +HQYDVR0OBBYEFLGN6uQjp34JjrXuMeBq3Z40N2WsMCoGA1UdHwQjMCEwH6AdoBuG +GWh0dHA6Ly93d3cuZnd1cGQub3JnL3BraS8wDQYJKoZIhvcNAQELBQADggGBABNK +mC4AcqsBCVRGpwJeUymh5G6uUpzkoEDw+y9TEoWzfldV0epU7ruqI2p8B8YshDK6 ++D4CFmCnW8cc+Jb6jrJ2ZcjUqWE/c+uwZhwsUHNdk6ummPPKfMhRSbduk1ngdQe5 +meIgWGkoCfJ48GUAVVD6MlrMTNFsot1GN9x3ALMqhSU49+X43yikcc9WY2F8JOY8 +xYpGpgUQV1hBSPOGK4XhgztpFLqw0GxJiLrOfKjtJwSTkxGCpPi2dLS0huk/mreT +NAQ5FnMLkoqfR1RGga3tiP5w13gqDBV7a6MYMdmMfAAZhfRtlDu6SiAmjEmlSkOK +PNhdoCNVDQLQpGaKZUI5hjMfR90U8Cm/6e0ondwjV4J6f4CS4wkQ5zzITGWptagE +01tpgTXf7TLaFGtzR8cl8XgV+UO3T4DQjEQkXUaS7n72ZCGv/s4LraLunhBrVHSq +glEXpU/V/JNptgArIiRFZOrto52cUnnlNEfgqIzAHv/LMFRIkMo8ZMGTgScFrA== +-----END CERTIFICATE----- diff --git a/fwupd-1.8.6/data/pki/meson.build b/fwupd-1.8.6/data/pki/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..3649fecea164d7cd235c76ad9f052bfcc07f7ef7 --- /dev/null +++ b/fwupd-1.8.6/data/pki/meson.build @@ -0,0 +1,36 @@ +# only install files that are going to be used +if libjcat.type_name() != 'internal' and libjcat.version().version_compare('>= 0.1.9') + supported_gpg = libjcat.get_variable(pkgconfig: 'supported_gpg') == '1' + supported_pkcs7 = libjcat.get_variable(pkgconfig: 'supported_pkcs7') == '1' +else + supported_gpg = host_machine.system() != 'windows' + supported_pkcs7 = true +endif + +if supported_gpg +install_data([ + 'GPG-KEY-Linux-Foundation-Firmware', + 'GPG-KEY-Linux-Vendor-Firmware-Service', + ], + install_dir: join_paths(sysconfdir, 'pki', 'fwupd') +) +install_data([ + 'GPG-KEY-Linux-Foundation-Metadata', + 'GPG-KEY-Linux-Vendor-Firmware-Service', + ], + install_dir: join_paths(sysconfdir, 'pki', 'fwupd-metadata') +) +endif + +if supported_pkcs7 +install_data([ + 'LVFS-CA.pem', + ], + install_dir: join_paths(sysconfdir, 'pki', 'fwupd') +) +install_data([ + 'LVFS-CA.pem', + ], + install_dir: join_paths(sysconfdir, 'pki', 'fwupd-metadata') +) +endif diff --git a/fwupd-1.8.6/data/power.quirk b/fwupd-1.8.6/data/power.quirk new file mode 100644 index 0000000000000000000000000000000000000000..bfddd35e63ede711696626af3de1329b3bb6ff2c --- /dev/null +++ b/fwupd-1.8.6/data/power.quirk @@ -0,0 +1,10 @@ +#This file provides manufacturer specified minimum battery thresholds + +[LENOVO] +BatteryThreshold = 25 + +[Star Labs] +BatteryThreshold = 30 + +[HP] +BatteryThreshold = 50 diff --git a/fwupd-1.8.6/data/remotes.d/README.md b/fwupd-1.8.6/data/remotes.d/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d289124c7c046eb7d6b7ceb2e8994cc4b2575861 --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/README.md @@ -0,0 +1,81 @@ +# Remotes + +## Vendor Firmware + +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.gz` + +The metadata should be of the form: + + + + + FIXME.firmware + FIXME + FIXME + FIXME + FIXME +

    FIXME

    + http://FIXME + + + 86406 + firmware/FIXME.cab + 96a92915c9ebaf3dd232cfc7dcc41c1c6f942877 +

    FIXME.

    +
    +
    + + FIXME + +
    +
    + +Ideally, the metadata and firmware should be signed by either GPG or a PKCS7 +certificate. If this is the case also change `Keyring=gpg` or `Keyring=pkcs7` +in `/etc/fwupd/remotes.d/vendor.conf` and ensure the correct public key or +signing certificate is installed in the `/etc/pki/fwupd` location. + +## Automatic metadata generation + +`fwupd` and `fwupdtool` support automatically generating metadata for a remote +by configuring it to be a *directory* type. This is very convenient if you want to dynamically add firmware from multiple packages while generating the image but there are a few deficiencies: + +* There will be a performance impact of starting the daemon or tool measured by O(# CAB files) +* It's not possible to verify metadata signature and any file validation should be part of the image validation. + +To enable this: + +* Change `/etc/fwupd/remotes.d/vendor-directory.conf` to have `Enabled=true` +* Change `/etc/fwupd/remotes.d/vendor-directory.conf` 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 upstream 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. + +For mirroring the LVFS content to a new CDN, you could use: + + [fwupd Remote] + Enabled=true + Type=download + Keyring=gpg + MetadataURI=https://my.new.cdn/mirror/firmware.xml.gz + FirmwareBaseURI=https://my.new.cdn/mirror + +New instances of the LVFS can actually output a relative URL for firmware files, +e.g. `bar.cab` and when downloading the `MetadataURI` name +and path prefix is used in this case. +This is not enabled for the "upstream" LVFS instance as versions of fwupd older +than 1.0.3 are unable to automatically use the `MetadataURI` value for firmware +downloads. diff --git a/fwupd-1.8.6/data/remotes.d/lvfs-testing.conf b/fwupd-1.8.6/data/remotes.d/lvfs-testing.conf new file mode 100644 index 0000000000000000000000000000000000000000..42575498f44c44d9240430868889d113de9697cb --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/lvfs-testing.conf @@ -0,0 +1,12 @@ +[fwupd Remote] + +# this remote provides metadata and firmware marked as 'testing' from the LVFS +Enabled=false +Title=Linux Vendor Firmware Service (testing) +MetadataURI=https://cdn.fwupd.org/downloads/firmware-testing.xml.gz +ReportURI=https://fwupd.org/lvfs/firmware/report +#Username= +#Password= +OrderBefore=lvfs,fwupd +AutomaticReports=false +ApprovalRequired=false diff --git a/fwupd-1.8.6/data/remotes.d/lvfs-testing.metainfo.xml b/fwupd-1.8.6/data/remotes.d/lvfs-testing.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e36aab775fe90023b985e23d1c5c108f736d00f --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/lvfs-testing.metainfo.xml @@ -0,0 +1,35 @@ + + + + + org.freedesktop.fwupd.remotes.lvfs-testing + Linux Vendor Firmware Service (testing firmware) + CC0-1.0 + + + + +

    + 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. +

    +

    + 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. +

    +

    + 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$. +

    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/data/remotes.d/lvfs.conf b/fwupd-1.8.6/data/remotes.d/lvfs.conf new file mode 100644 index 0000000000000000000000000000000000000000..f425ce55830ea07780b47fc2cefe56edd6520022 --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/lvfs.conf @@ -0,0 +1,12 @@ +[fwupd Remote] + +# this remote provides metadata and firmware marked as 'stable' from the LVFS +Enabled=@enabled@ +Title=Linux Vendor Firmware Service +MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.gz +ReportURI=https://fwupd.org/lvfs/firmware/report +SecurityReportURI=https://fwupd.org/lvfs/hsireports/upload +OrderBefore=fwupd +AutomaticReports=false +AutomaticSecurityReports=false +ApprovalRequired=false diff --git a/fwupd-1.8.6/data/remotes.d/lvfs.metainfo.xml b/fwupd-1.8.6/data/remotes.d/lvfs.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..d9565ddd11a78b5da9fbe1202c94a7375c5fa507 --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/lvfs.metainfo.xml @@ -0,0 +1,29 @@ + + + + + org.freedesktop.fwupd.remotes.lvfs + Linux Vendor Firmware Service (stable firmware) + CC0-1.0 + + + + +

    + 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. +

    +

    + 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$. +

    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/data/remotes.d/meson.build b/fwupd-1.8.6/data/remotes.d/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1d1698a7eb6e9ddd82fe8d1a3b1778b7bb677092 --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/meson.build @@ -0,0 +1,60 @@ +if build_standalone and get_option('lvfs') != 'false' + install_data([ + 'lvfs-testing.conf', + ], + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d') + ) + con3 = configuration_data() + if get_option('lvfs') == 'disabled' + con3.set('enabled', 'false') + else + con3.set('enabled', 'true') + endif + configure_file( + input: 'lvfs.conf', + output: 'lvfs.conf', + configuration: con3, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), + ) + i18n.merge_file( + input: 'lvfs.metainfo.xml', + output: 'org.freedesktop.fwupd.remotes.lvfs.metainfo.xml', + type: 'xml', + po_dir: join_paths(meson.project_source_root(), 'po'), + data_dirs: join_paths(meson.project_source_root(), 'po'), + install: true, + install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo') + ) + i18n.merge_file( + input: 'lvfs-testing.metainfo.xml', + output: 'org.freedesktop.fwupd.remotes.lvfs-testing.metainfo.xml', + type: 'xml', + po_dir: join_paths(meson.project_source_root(), 'po'), + data_dirs: join_paths(meson.project_source_root(), 'po'), + install: true, + install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo') + ) +endif + +install_data('README.md', + install_dir: join_paths(datadir, 'fwupd', 'remotes.d', 'vendor', 'firmware') +) + +# replace @datadir@ +con2 = configuration_data() +con2.set('datadir', datadir) +configure_file( + input: 'vendor.conf', + output: 'vendor.conf', + configuration: con2, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), +) +configure_file( + input: 'vendor-directory.conf', + output: 'vendor-directory.conf', + configuration: con2, + install: true, + install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), +) diff --git a/fwupd-1.8.6/data/remotes.d/vendor-directory.conf b/fwupd-1.8.6/data/remotes.d/vendor-directory.conf new file mode 100644 index 0000000000000000000000000000000000000000..3885159d8f517a8d84b90e23084595f121922579 --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/vendor-directory.conf @@ -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 --git a/fwupd-1.8.6/data/remotes.d/vendor.conf b/fwupd-1.8.6/data/remotes.d/vendor.conf new file mode 100644 index 0000000000000000000000000000000000000000..a69d1bfa00e3feb7f1466d67ac74b536f5ea4602 --- /dev/null +++ b/fwupd-1.8.6/data/remotes.d/vendor.conf @@ -0,0 +1,8 @@ +[fwupd Remote] +# this remote provides metadata shipped by the OS vendor and can be found in +# @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 --git a/fwupd-1.8.6/docs/architecture-plan.svg b/fwupd-1.8.6/docs/architecture-plan.svg new file mode 100644 index 0000000000000000000000000000000000000000..7dc2f7e0f18dd3d85403489cb50b953bc96ba5b5 --- /dev/null +++ b/fwupd-1.8.6/docs/architecture-plan.svg @@ -0,0 +1,1354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + fwupd + + Bluetooth (bluez) + + Keyboard + + customplugins + + udev, sysfs, ESRT + + systemd + + pending.db + + + + + + + internet + system + + + IPFS + + CDN + sqlite + gnome-softwarefwupdmgr + + + + Mouse + + + SAS / RAID + + + BMC + + + UpdateMetadata() + GetDevices() + + IPMI & Redfish + + only metadata + manual user opt-in + firmware + + + AppStream XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LVFS + + + + + session + embargoedmetadata + + + + + + + diff --git a/fwupd-1.8.6/docs/bios-settings.md b/fwupd-1.8.6/docs/bios-settings.md new file mode 100644 index 0000000000000000000000000000000000000000..03d161d14fe4fcfde302d7c9749856fbe730c80f --- /dev/null +++ b/fwupd-1.8.6/docs/bios-settings.md @@ -0,0 +1,183 @@ +--- +title: BIOS Settings API +--- + +fwupd 1.8.4 and later include the ability to modify BIOS settings on systems that support the Linux kernel's [firmware-attributes API](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-firmware-attributes). + +Drivers included with the Linux kernel on supported machines will advertise all BIOS settings that the OS can change. The fwupd daemon uses this API create an abstraction that fwupd clients can use to offer BIOS settings to change to end users. + +## Interactive command line usage + +Both `fwupdmgr` and `fwupdtool` have support for the fwupd BIOS settings API both for interactive use. +Using `fwupdgmr` will require PolicyKit authentication as only authorized users can view the current BIOS settings. +Using `fwupdtool` will require running the tool as root, as `fwupdtool` only works as root. + +### Getting available BIOS settings + +To fetch available BIOS settings for a machine: + +```shell +# fwupdmgr get-bios-setting +``` + +This will provide a listing of all available settings. If you would like to just see a subset of attributes, you can list them space delimited on the command line. For example: + +```shell +$ fwupdmgr get-bios-setting WindowsUEFIFirmwareUpdate MmioAbove4GLimit +Authenticating… [ - ] +WindowsUEFIFirmwareUpdate: + Setting type: Enumeration + Current Value: Enable + Description: BIOS updates delivered via LVFS or Windows Update + Read Only: False + Possible Values: + 0: Disable + 1: Enable + + +MmioAbove4GLimit: + Setting type: Enumeration + Current Value: Auto + Description: MmioAbove4GLimit + Read Only: False + Possible Values: + 0: Auto + 1: 40 + 2: 42 + 3: 44 + 4: 46 + 5: 48 +``` + +When using BASH as your shell, bash-completion can be used to discover BIOS settings. In the above example, those settings could be found from this series of tab actions: + +```shell +$ sudo fwupdmgr get-bios-setting W +WakeonLAN WakeUponAlarm WindowsUEFIFirmwareUpdate +130 $ sudo fwupdmgr get-bios-setting WindowsUEFIFirmwareUpdate +Display all 131 possibilities? (y or n) +AbsolutePersistenceModule CStateSupport M2Slot2Port PCIeSlot3Bifurcation PXEIPV6NetworkStack SetStrongPassword +AccessSecuritySettings DASHSupport MaxPasswordAttempts PCIeSlot3DLFSupport QuadM2PCIeCardFanControl SmartUSBProtection +AfterPowerLoss DataScrambling MCRUSBHeader PCIeSlot3LinkSpeed RealtimeDIAG SMT +AlarmDate(MM\DD\YYYY) DeviceGuard MediaCardReader PCIeSlot3Port RearAudioController SRIOVSupport +AlarmDayofWeek DevicePowerupDelay MmioAbove4GLimit PCIeSlot4Bifurcation RearUSBPorts StartupSequence +AlarmTime(HH:MM:SS) DeviceResetTimeout --no-authenticate PCIeSlot4DLFSupport RemoteSetSMP USBChargingPortInS4S5 +AllowJumperClearSVP DIAG7SegMode NUMA PCIeSlot4LinkSpeed RequireHDPonSystemBoot USBPortAccess +AMDMemoryGuard EnhancedPowerSavingMode NVMeRAIDMode PCIeSlot4Port RequireSVPwhenFlashing USBTransferTimeout +AMDSecureVirtualMachine ErrorBootSequence OnboardEthernetController PCIeSlot5Bifurcation SATAController UserDefinedAlarmFriday +ASPMSupport FanControlStepping OptionKeysDisplay PCIeSlot5DLFSupport SATADrive1 UserDefinedAlarmMonday +AutomaticBootSequence FrontUSBPorts PasswordChangeTime PCIeSlot5LinkSpeed SATADrive2 UserDefinedAlarmSaturday +BIOSPasswordAtBootDeviceList HardDiskPre-delay PasswordCountExceededError PCIeSlot5Port SATADrive3 UserDefinedAlarmSunday +BIOSPasswordAtReboot InternalUSB2Port PatroScrub PCIeSlot6Bifurcation SATADrive4 UserDefinedAlarmThursday +BIOSPasswordAtSystemBoot InternalUSB3Port PatroScrubInterval PCIeSlot6DLFSupport SATADrive5 UserDefinedAlarmTime +BootUpNumLockStatus IOMMU PCIeSlot1Bifurcation PCIeSlot6LinkSpeed SATADrive6 UserDefinedAlarmTuesday +CardLocation --json PCIeSlot1DLFSupport PCIeSlot6Port SATADrive6HotPlugSupport UserDefinedAlarmWednesday +ClearDIAGLog KeyboardLayout PCIeSlot1LinkSpeed pending_reboot SecureBoot --verbose +ConfigurationChangeDetection M2Slot1DLFSupport PCIeSlot1Port PhysicalPresenceforClear SecureRollBackPrevention WakeonLAN +ConfigureSATAas M2Slot1LinkSpeed PCIeSlot2Bifurcation POPChangeablebyUser SecurityChip WakeUponAlarm +CoverTamperDetected M2Slot1Port PCIeSlot2DLFSupport PostPackageRepair SelectActiveVideo WindowsUEFIFirmwareUpdate +CPBMode M2Slot2DLFSupport PCIeSlot2LinkSpeed PrimaryBootSequence SerialPort1Address XHCIHandoff +CPUC6Report M2Slot2LinkSpeed PCIeSlot2Port PXEIPV4NetworkStack SetMinimumLength +130 $ fwupdmgr get-bios-setting WindowsUEFIFirmwareUpdate MmioAbove4GLimit +``` + +### Setting BIOS settings + +To set one or more BIOS settings for a machine: + +```shell +# fwupdmgr set-bios-setting SETTING VALUE +``` + +For supported attributes, `fwupdmgr` will also use tab completion in BASH for filling out a value. +For example to enable an enumeration attribute: + +```shell +$ sudo fwupdmgr set-bios-setting W +WakeonLAN WakeUponAlarm WindowsUEFIFirmwareUpdate +130 $ sudo fwupdmgr set-bios-setting WindowsUEFIFirmwareUpdate +Disable Enable +130 $ sudo fwupdmgr set-bios-setting WindowsUEFIFirmwareUpdate Enable +``` + +After setting an attribute you may be prompted to reboot as most settings will require a reboot to take effect. + +```shell +$ fwupdmgr set-bios-setting WakeonLAN Primary +Authenticating… [ - ] +Set BIOS setting 'WakeonLAN' using 'Primary'. + +An update requires a reboot to complete. Restart now? [y|N]: +``` + +If you would like to program multiple attributes, list them in pairs of the name of the attribute followed by the desired value. + +## Programmatic command line usage + +`fwupdmgr` offers support for the fwupd BIOS settings API programmatically as well for use in scripts. To use the programmatic API you will add the `--json` argument to your calls. The output or input will then be expected to be JSON payloads. + +### Fetching BIOS settings programmatically + +Below is an example of fetching the `WakeonLAN` setting. + +```shell +# fwupdmgr get-bios-setting WakeonLAN --json +{ + "BiosSettings" : [ + { + "Name" : "WakeonLAN", + "Description" : "WakeonLAN", + "Filename" : "/sys/class/firmware-attributes/thinklmi/attributes/WakeonLAN", + "BiosSettingId" : "com.thinklmi.WakeonLAN", + "BiosSettingCurrentValue" : "Primary", + "BiosSettingReadOnly" : "false", + "BiosSettingType" : 1, + "BiosSettingPossibleValues" : [ + "Primary", + "Automatic", + "Disable" + ] + } + ] +} +``` + +Similar to the interactive usage, if no parameters are provided all settings are fetched, and if desired multiple settings can be listed on the command line. + +Error messages won't be emitted, you'll have to rely on the return code to tell if this was successful. +*NOTE:* To debug errors, use the `--verbose` argument to see messages related to the error. + +### Setting BIOS settings programmatically + +To set BIOS settings, a JSON payload will need to be crafted in advance. The path to this payload is used as an argument. + +```shell +# fwupdmgr set-bios-setting ~/foo.json --json +``` + +An important return code to know for programmatic usage is that *2* means nothing was done because all settings are already programmed. This message will also be emitted. +*NOTE:* To debug errors, use the `--verbose` argument to see messages related to the error. + +## Firmware setting policies + +`fwupd` has the ability to enforce the BIOS settings policy of a system administrator. To use this feature, create a json payload using `fwupdmgr get-bios-setting --json` that reflects the settings you would like to see enforced. + +Then copy this payload into `/etc/fwupd/bios-settings.d` with a filename ending in `.json`. The next time that the fwupd daemon is started (such as a system bootup) it will ensure that all BIOS settings are programed to your desired values. It will also mark those settings as read-only so no fwupd clients will be able to modify them. + +This *does not* stop the kernel firmware-attributes API from working. So a determined user with appropriate permissions would be able to modify settings from the kernel API directly, but they would be changed again on fwupd daemon startup. + +## Settings types + +The Linux kernel will offer the following types of BIOS settings: + +* Enumeration: The setting will only accept a limiited list of possible values +* Integer: The setting will will accept a limited range of integer values +* String: The setting will accept a limited length UTF-8 string + +All of those setting types are accepted by fwupd. It is expected that drivers or firmware will validate the input, but where possible fwupd will do validation of the input to give better error messages and avoid failures. + +fwupd will also do a mapping where it can accept multiple cases or synonyms for words that are obviously positive or negative. So for example if the setting expects `Enabled` but the user passes `tRUE` fwupd will map this into `Enabled` before sending it to the driver and the driver sending it to the firmware. + +## libfwupd + +`fwupdmgr` internally uses `FwupdClient` to manage BIOS settings. Any other clients that are interested in managing BIOS settings can use this library as well. A sample python application is included in the `contrib/` directory in the fwupd source tree. diff --git a/fwupd-1.8.6/docs/ds20.md b/fwupd-1.8.6/docs/ds20.md new file mode 100644 index 0000000000000000000000000000000000000000..a010c478cc27d4228d21c73d72177b82e3356497 --- /dev/null +++ b/fwupd-1.8.6/docs/ds20.md @@ -0,0 +1,135 @@ +# fwupd BOS DS20 Specification + +## Introduction + +When `fwupd` starts it enumerates all PCI and USB hardware and generates *Instance IDs* based on the reported vendor and product so that it can match the device to a plugin. +The plugin knows how to communicate with the device (for instance using vendor-specific USB control transfers) and also knows how to parse the firmware and deliver it to the device. + +Before a vendor can ship a firmware on the LVFS to a machine using Linux (or ChromeOS) the fwupd package often must be updated so that it knows what plugin to use for that specific device. +At this point other overridden [quirk data](https://fwupd.github.io/libfwupdplugin/class.Quirks.html) can also be set. For instance, the icon, long summary or even per-plugin flags that change device behavior. +For example `colorhug.quirk`: + + [USB\VID_273F&PID_1001] + Plugin = colorhug + Flags = self-recovery + Icon = colorimeter-colorhug + +Updating the fwupd binary package might take anywhere from a few weeks to several years due to various Linux distribution policies. +Clearly this isn't good when the majority of firmware updates are distributed to address security issues. + +One suggestion would be to put this quirk information into the existing update metadata provided by the configured remote, but this has several problems: + +* The daemon needs to *enumerate* hardware that does not have updates on the main LVFS remote. +* A *lot* of machines using fwupd have never connected to the internet and we still want to enumerate hardware for audit and verification purposes. +* Re-enumerating all physical hardware (to get the latest quirks) when updating the metadata is not straightforward. +* The VID/PID is not necessarily the information that the plugin matches to assign the firmware stream, and so this would have to be a separate facet of metadata -- which may have to include quirks too. + +Matching devices to plugins should be thought of as *orthogonal* to matching firmware to devices. +To simplify deployment it should be possible to allow the device itself to specify the plugin to use and optionally additional quirk data. + +## Specification + +Devices that implement a fwupd BOS DS20 descriptor can be updated without always needing to update the runtime version of fwupd for updated quirk entries, assuming the device is updatable by the existing vendor plugin. + +USB devices can create a new platform device capability BOS *“Binary Object Store”* descriptor with `bDevCapabilityType=0x05` and a fwupd-specific UUID, specifically `010aec63-f574-52cd-9dda-2852550d94f0`. +This UUID is generated type-5 SHA-1 hash of the word `fwupd` using a DNS namespace. +Using this custom UUID will ensure that Microsoft Windows does not try to parse the capability descriptor as a *Microsoft OS 2.0 descriptor set*. + +## Implementation + +Create a BOS DS20 descriptor such as: + + 1C 10 05 00 63 ec 0a 01 74 f5 cd 52 9d da 28 52 55 0d 94 f0 05 08 01 00 20 00 2a 00 + ├┘ ├┘ ├┘ ├┘ └─────────────┬───────────────────────────────┘ └─┬───────┘ └─┬─┘ ├┘ ├┘ + │ │ │ │ └─PlatformCapabilityUUID │ wLength─┘ │ │ + │ │ │ └─bReserved dwVersion─┘ bVendorCode─┘ │ + │ │ └────bDevCapability bAltEnumCmd────┘ + │ └───────bDescriptorType + └──────────bLength + +The `dwVersion` encoded here is fwupd `1.8.5`, which is also the first version that supports BOS DS20 descriptors. + +The BOS descriptors are sorted by the requester and can appear in any order. +The descriptor with the highest `dwVersion` that is not newer than the current running daemon version is used. +This means that `dwVersion` is effectively the minimum version of fwupd that should read the descriptor. +This allows devices to set different quirks depending on the fwupd version, although in practice this should not be required. +A suitable bVendorCode should be chosen that is not used for existing device operation. + +* The `dwVersion` parameter **must** be larger than `0x00010805`, i.e. 1.8.5. +* The `bAltEnumCmd` parameter **must** be zero. +* The `PlatformCapabilityUUID` **must** be `010aec63-f574-52cd-9dda-2852550d94f0`. + +Then allow the device to reply to a USB control request with the following parameters: + + transfer-direction: device-to-host + request-type: vendor + recipient: device + bRequest: {value of bVendorCode in the BOS descriptor} + wValue: 0x00 + wIndex: 0x07 + wLength: {value of wLength in the BOS descriptor} + +The device should return something like: + + 50 6c 75 67 69 6e 3d 64 66 75 0a 49 63 6f 6e 3d 63 6f 6d 70 75 74 65 72 0a 00 00 00 00 00 00 00 + +...which is the UTF-8 quirk data, e.g. + + Plugin=dfu + Icon=computer + +The UTF-8 quirk data must **not** contain Windows *CRLF-style* line endings. + +## Workflow + +To generate the fwupd DS20 descriptor save a file such as `fw-ds20.builder.xml`: + + + 42 + 32 + + +Then run `fwupdtool firmware-build fw-ds20.builder.xml fw-ds20.bin`. + +To generate the control transfer response, save a file such as `fw-ds20.quirk`: + + [USB\VID_273F&PID_1004] + Plugin = dfu + Icon = computer + +Then run `contrib/generate-ds20.py fw-ds20.quirk --bufsz 32`. +The maximum buffer size is typically hardcoded for the device and may be specified in a microcontroller datasheet. + +## Prior Art + +### Hardcoded Class & Subclass + +The fwupd project already support two plugins that use the USB class code, rather than the exact instance ID with the VID/PID. +For example, this DFU entry means *“match any USB device with class 0xFE (application specific) and subclass 0x01”*: + + [USB\CLASS_FE&SUBCLASS_01] + Plugin = dfu + +These kind of devices do not need any device to plugin mapping (although, they still might need a quirk if they are non-complaint in some way, for example needing `Flags = detach-for-attach`) – but in the most cases they just work. + +The same can be done for Fastboot devices, matching class `0xFF` (vendor specific), subclass `0x42` and protocol `0x03`, although there is the same caveat for non-compliant devices that need quirk entries like `FastbootOperationDelay = 250`: + + [USB\CLASS_FF&SUBCLASS_42&PROT_03] + Plugin = fastboot + +#### Microsoft OS Descriptors 1.0 + +The [Microsoft OS Descriptors 1.0 Specification](https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-1-0-descriptors-specification) defines a device-specific variable-length metadata block of `CompatibleID`:`SubCompatibleID` on a string index of `0xEE` where the `CompatibleID` and `SubCompatibleID` are also both hardcoded at 8 bytes. + +Using `FWUPDPLU` or `FWUPDFLA` as the `CompatibleID` would be acceptable, but we could not fit the plugin name (e.g. `logitech-bulkcontroller`) or the GUID (16 bytes) in an 8 byte `SubCompatibleID`. +Some non-compliant devices also hang and stop working when probing this specific string index. + +#### Microsoft OS Descriptors 2.0 + +The [Microsoft OS Descriptors 2.0 Specification](https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification) is more useful as it defines a new device capability that can return a variable length vendor-defined section of data, using a UUID as a key. +Using the `BOS` *“Binary Object Store”* descriptor is only available for devices using USB specification version 2.1 and newer. +The BOS descriptor is used for Wireless USB details, USB 2.0 extensions, SuperSpeed USB connection details and a Container ID. + +The BOS descriptor does give the OS the ability to parse the platform capability, which is `bDevCapabilityType=0x05`. For UUID `D8DD60DF-4589-4CC7-9CD2-659D9E648A9F` this is identified as a structured blob of data Microsoft Windows uses for the suspend mode of the device. + +Creating a new `bDevCapabilityType` would allow vendors to store a binary blob (e.g. `Plugin=foobarbaz\nFlags=QuirkValueHere\n`) but that would be out-of-specification and difficult to implement. diff --git a/fwupd-1.8.6/docs/env.md b/fwupd-1.8.6/docs/env.md new file mode 100644 index 0000000000000000000000000000000000000000..10d50b0fa42237ac11782dac6066d6eeb05c9ed2 --- /dev/null +++ b/fwupd-1.8.6/docs/env.md @@ -0,0 +1,96 @@ +--- +title: Environment Variables +--- + +When running fwupd reads some variables from your environment and changes some +behavior. This might be useful for debugging, or to make fwupd run somewhere +with a non-standard filesystem layout. + +## fwupdmgr and fwupdtool + +* `DISABLE_SSL_STRICT` disables strict SSL certificate checking, which may make + downloading files work when using some antisocial corporate firewalls. +* `FWUPD_CURL_VERBOSE` shows more information when downloading files +* `FWUPD_DEVICE_TESTS_BASE_URI` sets the base URI when downloading firmware for the device-tests +* `FWUPD_SUPPORTED` overrides the `-Dsupported_build` meson option at runtime +* `FWUPD_VERBOSE` is set when running `--verbose` +* `FWUPD_BACKEND_VERBOSE` can be used to show detailed plugin and backend debugging +* `FWUPD_XMLB_VERBOSE` can be set to show Xmlb silo regeneration and quirk matches +* `FWUPD_DBUS_SOCKET` is used to set the socket filename if running without a dbus-daemon +* `FWUPD_DOWNLOAD_VERBOSE` can be used to show wget or curl output +* `FWUPD_PROFILE` can be used to set the profile traceback threshold value in ms +* `FWUPD_FUZZER_RUNNING` if the firmware format is being fuzzed +* standard glibc variables like `LANG` are also honored for CLI tools that are translated +* libcurl respects the session proxy, e.g. `http_proxy`, `all_proxy`, `sftp_proxy` and `no_proxy` + +## daemon + +* `FWUPD_MACHINE_KIND` can be used to override the detected machine type, e.g. `physical`, `virtual`, or `container` +* `FWUPD_HOST_EMULATE` can be used to load test data from `/usr/share/fwupd/host-emulate.d`, e.g. `thinkpad-p1-no-iommu.json.gz` + +## Self Tests + +* `CI_NETWORK` if CI is running with network access +* `TPM_SERVER_RUNNING` if an emulated TPM is running + +## Shared libfwupdplugin + +* `FU_HID_DEVICE_VERBOSE` shows HID traffic +* `FU_SREC_FIRMWARE_VERBOSE` shows more information about parsing +* `FU_UDEV_DEVICE_DEBUG` shows more information about UDEV devices, including parents +* `FU_USB_DEVICE_DEBUG` shows more information about USB devices +* `FU_MEI_DEVICE_DEBUG` shows MEI reads and writes +* `FWUPD_DEVICE_LIST_VERBOSE` display devices being added and removed from the list +* `FWUPD_PROBE_VERBOSE` dump the detected devices to the console, even if not supported by fwupd +* `FWUPD_BIOS_SETTING_VERBOSE` be verbose while parsing BIOS settings + +## Plugins + +Most plugins read a plugin-specific runtime key to increase verbosity more than the usual `VERBOSE`. +This can be also used when using fwupdtool e.g. using `--plugin-verbose=dell` will set the +environment variable of `FWUPD_DELL_VERBOSE` automatically. + +Other variables, include: + +* `FWUPD_DELL_FAKE_SMBIOS` if set, use fake SMBIOS information for tests +* `FWUPD_FORCE_TPM2` ignores a TPM 1.2 device detected in the TPM self tests +* `FWUPD_PLUGIN_TEST` used by the test plugin to pass data out-of-band to the loader +* `FWUPD_REDFISH_SELF_TEST` if set, do destructive tests on the actual device BMC +* `FWUPD_REDFISH_SMBIOS_DATA` use this filename to emulate a specific SMBIOS blob +* `FWUPD_SOLOKEY_EMULATE` emulates a fake device for testing +* `FWUPD_SUPERIO_DISABLE_MIRROR` disables the e-flash fixup to get byte-accurate hardware dumps +* `FWUPD_SUPERIO_RECOVER` allow recovery of a corrupted SuperIO by hardcoding the device size +* `FWUPD_TEST_PLUGIN_XML` used by the test plugin to load XML state out-of-band before startup +* `FWUPD_UEFI_CAPSULE_RECREATE_COD_DATA` if set, write the files in the example COD tree in srcdir +* `FWUPD_UEFI_TEST` used by the UEFI plugins to disable specific sanity checks during self tests +* `FWUPD_WAC_EMULATE` emulates a fake device for testing + +## File system overrides + +These are not fully documented here, see +for details. + +* `CACHE_DIRECTORY` +* `CONFIGURATION_DIRECTORY` +* `FWUPD_ACPITABLESDIR` +* `FWUPD_DATADIR` +* `FWUPD_DATADIR_QUIRKS` +* `FWUPD_EFIAPPDIR` +* `FWUPD_FIRMWARESEARCH` +* `FWUPD_LIBDIR_PKG` +* `FWUPD_LOCALSTATEDIR` +* `FWUPD_LOCALSTATEDIR_QUIRKS` +* `FWUPD_OFFLINE_TRIGGER` +* `FWUPD_PROCFS` +* `FWUPD_SYSCONFDIR` +* `FWUPD_SYSFSDRIVERDIR` +* `FWUPD_SYSFSFWATTRIBDIR` +* `FWUPD_SYSFSFWDIR` +* `FWUPD_SYSFSSECURITYDIR` +* `FWUPD_SYSFSTPMDIR` +* `FWUPD_UEFI_ESP_PATH` +* `HOME` +* `RUNTIME_DIRECTORY` +* `SNAP` +* `SNAP_USER_DATA` +* `STATE_DIRECTORY` diff --git a/fwupd-1.8.6/docs/fwupd.toml.in b/fwupd-1.8.6/docs/fwupd.toml.in new file mode 100644 index 0000000000000000000000000000000000000000..fef8d43b6ca346048b939b0ead68225cdab31dd9 --- /dev/null +++ b/fwupd-1.8.6/docs/fwupd.toml.in @@ -0,0 +1,65 @@ +[library] +version = "@version@" +browse_url = "https://github.com/fwupd/fwupd" +repository_url = "https://github.com/fwupd/fwupd.git" +website_url = "https://www.fwupd.org" +authors = "fwupd Development Team" +logo_url = "org.freedesktop.fwupd.svg" +license = "LGPL-2.1-or-later" +description = "Functionality exported by libfwupd for client applications" +dependencies = [ + "GObject-2.0", + "Gio-2.0", + "Json-1.0" +] +devhelp = true +search_index = true + + [dependencies."GObject-2.0"] + name = "GObject" + description = "The base type system library" + docs_url = "https://docs.gtk.org/gobject/" + + [dependencies."Gio-2.0"] + name = "Gio" + description = "A modern, easy-to-use VFS API" + docs_url = "https://docs.gtk.org/gio/" + + [dependencies."Json-1.0"] + name = "Json" + description = "API for efficient parsing and writing of JSON (JavaScript Object Notation) streams" + docs_url = "https://gnome.pages.gitlab.gnome.org/json-glib/" + +[theme] +name = "basic" +show_index_summary = true +show_class_hierarchy = true + +[source-location] +base_url = "https://github.com/fwupd/fwupd/blob/@version@/" + +[extra] +content_images = [ + "../data/org.freedesktop.fwupd.svg", +] +urlmap_file = "urlmap_fwupd.js" + +[[object]] +name = "build_user_agent_system" +hidden = true + +[[object]] +name = "hash_kv_to_variant" +hidden = true + +[[object]] +name = "variant_to_hash_kv" +hidden = true + +[[object]] +name = "input_stream_read_bytes_async" +hidden = true + +[[object]] +name = "input_stream_read_bytes_finish" +hidden = true diff --git a/fwupd-1.8.6/docs/fwupdplugin.toml.in b/fwupd-1.8.6/docs/fwupdplugin.toml.in new file mode 100644 index 0000000000000000000000000000000000000000..32a5ef5c0ca6b4aa96b70dd71336cfb39c3bd079 --- /dev/null +++ b/fwupd-1.8.6/docs/fwupdplugin.toml.in @@ -0,0 +1,93 @@ +[library] +version = "@version@" +browse_url = "https://github.com/fwupd/fwupd" +repository_url = "https://github.com/fwupd/fwupd.git" +website_url = "https://www.fwupd.org" +authors = "fwupd Development Team" +logo_url = "org.freedesktop.fwupd.svg" +license = "LGPL-2.1-or-later" +description = "Functionality available to fwupd plugins" +dependencies = [ + "GObject-2.0", + "Gio-2.0", + "Fwupd-2.0", + "Xmlb-2.0", + "GUsb-1.0" +] +devhelp = true +search_index = true + + [dependencies."GObject-2.0"] + name = "GObject" + description = "The base type system library" + docs_url = "https://developer.gnome.org/gobject/stable/" + + [dependencies."Gio-2.0"] + name = "Gio" + description = "A modern, easy-to-use VFS API" + docs_url = "https://developer.gnome.org/gio/stable/" + + [dependencies."Fwupd-2.0"] + name = "Fwupd" + description = "Firmware update daemon client library" + docs_url = "../libfwupd/index.html" + +[theme] +name = "basic" +show_index_summary = true +show_class_hierarchy = true + +[source-location] +base_url = "https://github.com/fwupd/fwupd/blob/@version@/" + +[extra] +content_files = [ + "env.md", + "tutorial.md", + "hsi.md", + "ds20.md", + "bios-settings.md", +] +content_images = [ + "architecture-plan.svg", + "../data/org.freedesktop.fwupd.svg", +] +urlmap_file = "urlmap_fwupdplugin.js" + +[[object]] +name = "Device" + + [[object.method]] + name = "incorporate_from_component" + hidden = true + +[[object]] +name = "Cabinet" + + [[object.method]] + name = "set_jcat_context" + hidden = true + + [[object.method]] + name = "get_silo" + hidden = true + +[[object.function]] +name = "common_cab_build_silo" +hidden = true + +[[object]] +name = "Fmap" +hidden = true + +[[object]] +name = "FmapArea" +hidden = true + +[[object]] +name = "IhexFirmwareRecord" +hidden = true + +[[object]] +name = "SrecFirmwareRecord" +hidden = true diff --git a/fwupd-1.8.6/docs/hsi.html b/fwupd-1.8.6/docs/hsi.html new file mode 100644 index 0000000000000000000000000000000000000000..76e6306ac8a362d48b33079c236de27f8cba4669 --- /dev/null +++ b/fwupd-1.8.6/docs/hsi.html @@ -0,0 +1,13 @@ + + +Redirecting to https://fwupd.github.io/libfwupdplugin/hsi.html + + + + + diff --git a/fwupd-1.8.6/docs/hsi.md b/fwupd-1.8.6/docs/hsi.md new file mode 100644 index 0000000000000000000000000000000000000000..e097f5dd1eaa3ed0912a77238902c4c61a1c4c14 --- /dev/null +++ b/fwupd-1.8.6/docs/hsi.md @@ -0,0 +1,837 @@ +--- +title: Host Security ID Specification +--- + +**WARNING: +This specification is still in active development: it is incomplete, subject to change, and may have errors; use this at your own risk. +It is based on publicly available information.** + +Authors: + +- Richard Hughes +- Mario Limonciello +- Alex Bazhaniuk +- Alex Matrosov + +--- + +## Introduction + +Not all system vendors prioritize building a secure platform. +The truth is that **security costs money**. +Vendors have to choose between saving a few cents on a bill-of-materials by sharing a SPI chip, or correctly implementing BootGuard. +Discovering security vulnerabilities often takes an external researcher filing a disclosure. +These disclosures are often technical in nature and difficult for an average consumer to decipher. + +The Linux Vendor Firmware Service (LVFS) could provide some **easy-to-understand** information to people buying hardware. +The service already knows a huge amount of information about machines from signed reports uploaded to the LVFS and from analyzing firmware binaries. +However this information alone does not explain firmware security to the user in a way they can actually interpret. + +### Other Tools + +Traditionally, figuring out the true security of your hardware and firmware requires sifting through the marketing documentation provided by the OEM and in many cases just "trusting" they did it right. +Tools such as Chipsec can check the hardware configuration, but they do not work out of the box and use technical jargon that an average user cannot interpret. +Unfortunately, running a tool like Chipsec requires that you actively turn off some security layers such as UEFI Secure Boot, and allow 3rd party unsigned kernel modules to be loaded. + + + +## [Verifying Host Firmware Security](#verifying) + +To start out some core protections must be assigned a relative +importance. +Then an evaluation must be done to determine how each vendor is conforming to the model. +For instance, a user might say that for home use any hardware the bare minimum security level (`HSI:1`) is *good enough*. +For a work laptop the company IT department might restrict the choice of models to anything meeting the criteria of level `HSI:2` or above. +A journalist or a security researcher would only buy level `HSI:3` and above. +The reality is that `HSI:4` is going to be more expensive than some unbranded hardware that is rated `HSI:0`. + +To be trusted, this rating information should be distributed in a centralized agnostic database such as the LVFS. + +Of course, tools need to detect implementation errors, and to verify that the model that is measured does indeed match the HSI level advertised by the LVFS. +Some existing compliance solutions place the burden on the OEM to define what firmware security has been implemented, which is easy to get wrong and in some cases impossible to verify. + +For this reason HSI will only measure security protections that can be verified by the end user without requiring any extra hardware to be connected, additional software to be installed, or disabling any existing security layers to measure. + +The HSI specification is primarily designed for laptop and desktop hardware, although some tests *may* still make sense on server or embedded hardware. +It is not expected that non-consumer hardware will publish an HSI number. + + + +## [Runtime Behavior](#runtime-behaviour) + +Orthogonal to the security features provided by the firmware there are other security considerations related to the firmware which may require internet access to discover or that runtime OS changes directly affect the security of the firmware. +It would not make sense to have *have updates on the LVFS* as a requirement for a specific security level as this would mean offline the platform might be a higher level initially but as soon as it is brought online it is downgraded which would be really confusing to users. +The *core* security level will not change at Operating System runtime, but the suffix may. + +**More information** +Additional information about specific bugs and debugging steps are available on the [fwupd wiki](https://github.com/fwupd/fwupd/wiki/Host-security-ID-runtime-issues). + + + +### [HSI:0 (Insecure State)](#hsi-level0) + +Limited firmware protection. + +The lowest security level with little or no detected firmware protections. +This is the default security level if no tests can be run or some tests in the next security level have failed. + + + +### [HSI:1 (Critical State)](#hsi-level1) + +Basic protection but any failure would lead to a critical security impact. + +This security level corresponds to the most basic of security protections considered essential by security professionals. +Any failures at this level would have critical security impact and could likely be used to compromise the system firmware without physical access. + + + +### [HSI:2 (Risky State)](#hsi-level2) + +The failure is only happened by the theoretical exploit in the lab. + +This security level corresponds to firmware security issues that pose a theoretical concern or where any exploit would be difficult or impractical to use. +At this level various technologies may be employed to protect the boot process from modification by an attacker with local access to the machine. + + + +### [HSI:3 (Protected State)](#hsi-level3) + +The system firmware only has few minor issues which do not affect the security status. + +This security level corresponds to out-of-band protection of the system firmware perhaps including recovery. + + + +### [HSI:4 (Secure State)](#hsi-level4) + +The system is in a robust secure state. + +The system is corresponding several kind of encryption and execution protection for the system firmware. + + + +### [HSI:5 (Secure Proven State)](#hsi-level5) + +This security level corresponds to out-of-band attestation of the system firmware. +There are currently no tests implemented for HSI:5 and so this security level cannot yet be obtained. + + + +### [HSI Runtime Suffix `!`](#runtime-bang) + +A runtime security issue detected. + +- UEFI [Secure Boot](https://wiki.ubuntu.com/UEFI/SecureBoot) has been turned off. *[v1.5.0]* + +- The kernel is [tainted](https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html) due to a non-free module or critical firmware issue. *[v1.5.0]* + +- The kernel is not [locked down](https://mjg59.dreamwidth.org/50577.html). *[v1.5.0]* + +- Unencrypted [swap partition](https://wiki.archlinux.org/index.php/Dm-crypt/Swap_encryption). *[v1.5.0]* + +- The installed fwupd is running with [custom or modified plugins](https://github.com/fwupd/fwupd/tree/main/plugins). *[v1.5.0]* + + + +## [Low Security Level](#low-security-level) + +A safe baseline for security should be HSI-1. If your system isn't at least meeting this criteria, you should adjust firmware setup options, contact your manufacturer or replace the hardware. + +The command line tool `fwupdmgr security` included with fwupd 1.8.4 or later will make individual recommendations on what you can do for individual test failures. GUI tools built against `libfwupd` 1.8.4 or later may also make these recommendation as well. + + + +## [Not enough information](#not-enough-info) + +HSI calculations require that the SOC, firmware, and kernel provide enough data to the fwupd daemon about the state of the system. If any HSI test that runs on the system declares it's *missing data* then the client will show a message like this: + +**Not enough data was provided to make an HSI calculation.** + +The HSI level will also be set to `INVALID` indicating this. + + + +## [Tests included in fwupd](#tests) + +The set of tests is currently x86 UEFI-centric, but will be expanded in the future for various ARM or RISC-V firmware protections as required. +Where the requirement is architecture or processor specific it has been noted. + + + +### [UEFI SecureBoot](#org.fwupd.hsi.Uefi.SecureBoot) + +UEFI Secure boot is a verification mechanism for ensuring that code launched by firmware is trusted. + +Secure Boot requires that each binary loaded at boot is validated against trusted certificates. + +**Impact:** When Secure Boot is not enabled any EFI binary can be run at startup, which gives the attacker full access to your hardware. + +**Possible results:** + +- `not-found`: support has not been detected +- `not-enabled`: detected, but has been turned off +- `enabled`: supported and enabled + +To meet HSI-1 on UEFI systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**Resolution:** + +Turn off CSM boot and enable Secure Boot in the BIOS setup. + +**References:** + +- [Ubuntu SecureBoot Wiki Page](https://wiki.ubuntu.com/UEFI/SecureBoot) + + + +### [UEFI PK](#org.fwupd.hsi.Uefi.Pk) + +UEFI defines a platform key for the system. +This should not be a test key, e.g. `DO NOT TRUST - AMI Test PK` + +**Impact:** It is possible to sign an EFI binary with the test platform key, which invalidates the Secure Boot trust chain. +It effectively gives the local attacker full access to your hardware. + +**Possible results:** + +- `valid`: valid key +- `not-valid`: an invalid key has been enrolled + +To meet HSI-1 on UEFI systems that run this test, the result must be `valid`. *[v1.5.0]* + +**References:** + +- [Ubuntu SecureBoot Wiki Page](https://wiki.ubuntu.com/UEFI/SecureBoot/Testing) + + + +### [BIOS Write Enable (BWE)](#org.fwupd.hsi.Spi.Bioswe) + +Intel hardware provides this mechanism to protect the SPI ROM chip located on the motherboard from being overwritten by the operating +system. The `BIOSWE` bit must be unset otherwise userspace can write to the SPI chip. + +**Impact:** The system firmware can be written from userspace. +This gives any attacker with root access a method to write persistent executable code to the firmware, which survives even a full disk wipe and OS reinstall. + +**Possible results:** + +- `not-found`: the SPI device was not found +- `not-enabled`: write enable is disabled +- `enabled`: write enable is enabled + +To meet HSI-1 on systems that run this test, the result must be `not-enabled`. *[v1.5.0]* + +**References:** + +- [Intel C200 Datasheet](https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf) + + + +### [BIOS Lock Enable (BLE)](#org.fwupd.hsi.Spi.Ble) + +If the lock bit is set then System Management Interrupts (SMIs) are raised when setting BIOS Write Enable. +The `BLE` bit must be enabled in the PCH otherwise `BIOSWE` can easily be unset. + +**Impact:** The system firmware can be written from userspace. +This gives any attacker with root access a method to write persistent executable code to the firmware, which survives even a full disk wipe and OS reinstall. + +**Possible results:** + +- `enabled`: the register is locked +- `not-enabled`: the register is not locked + +To meet HSI-1 on systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**References:** + +- [Intel C200 Datasheet](https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf) + + + +### [SMM Bios Write Protect (SMM\_BWP)](#org.fwupd.hsi.Spi.SmmBwp) + +This bit set defines when the BIOS region can be written by the host. +The `SMM_BWP` bit must be set to make the BIOS region non-writable unless all processors are in system management mode. + +**Impact:** The system firmware can be written from userspace by exploiting a race condition in checking `BLE`. +This gives any attacker with root access a method to write persistent executable code to the firmware, which survives even a full disk wipe and OS reinstall. + +**Possible results:** + +- `locked`: the region is locked +- `not-locked`: the region is not locked + +To meet HSI-1 on systems that run this test, the result must be `locked`. *[v1.5.0]* + +**References:** + +- [Intel C200 Datasheet](https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf) + + + +### [Read-only SPI Descriptor](#org.fwupd.hsi.Spi.Descriptor) + +The SPI descriptor must always be read only from all other regions. +Additionally on Intel architectures the FLOCKDN register must be set to prevent configuration registers in the SPI BAR from being changed. + +**Impact:** The system firmware can be written from userspace by changing the protected region. +This gives any attacker with root access a method to write persistent executable code to the firmware, which survives even a full disk wipe and OS reinstall. + +**Possible results:** + +- `not-valid`: any region can write to the flash descriptor +- `locked`: the SPI BAR is locked and read only from all regions +- `not-locked`: the SPI BAR is not locked + +To meet HSI-1 on systems that run this test, the result must be `locked`. *[v1.6.0]* + + + +### [TPM 2.0 Present](#org.fwupd.hsi.Tpm.Version20) + +A TPM securely stores platform specific secrets that can only be divulged to trusted consumers in a secure environment. + +**Impact:** The PCR registers will not be available for use by the bootloader and kernel. +This means userspace cannot either encrypt disks to the specific machine, and also can't know if the system firmware was externally modified. + +**Possible results:** + +- `found`: device found in v2 mode +- `not-found`: no device found +- `not-enabled`: not in v2 mode + +To meet HSI-1 on systems that run this test, the result must be `found`. *[v1.5.0]* + +**References:** + +- [TPM Wikipedia Page](https://en.wikipedia.org/wiki/Trusted_Platform_Module) + + + +### [ME not in manufacturing mode](#org.fwupd.hsi.Mei.ManufacturingMode) + +There have been some unfortunate cases of the ME being distributed in manufacturing mode. +In manufacturing mode many features from the ME can be interacted with that decrease the platform's security. + +**Impact:** If the ME is in manufacturing mode then any user with root access can provision the ME engine with new keys. +This gives them full access to the system even when the system is powered off. + +**Possible results:** + +- `locked`: device has had manufacturing mode disabled +- `not-locked`: device is in manufacturing mode + +To meet HSI-1 on systems that run this test, the result must be `locked`. *[v1.5.0]* + +**References:** + +- [ME Manufacturing Mode: obscured dangers](https://malware.news/t/intel-me-manufacturing-mode-obscured-dangers-and-their-relationship-to-apple-macbook-vulnerability-cve-2018-4251/23214) +- [Intel security advisory SA-00086](https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00086.html) + + + +### [ME Flash Descriptor Override](#org.fwupd.hsi.Mei.OverrideStrap) + +The Flash Descriptor Security Override Strap is not accessible to end users on consumer boards and Intel stresses that this is for debugging +only. + +**Impact:** The system firmware can be written from userspace by changing the protected region. +This gives any attacker with root access a method to write persistent executable code to the firmware, which survives even a full disk wipe and OS reinstall. + +**Possible results:** + +- `locked`: device in in normal runtime mode +- `not-locked`: device is in debugging mode + +To meet HSI-1 on systems that run this test, the result must be `locked`. *[v1.5.0]* + +**References:** + +- [Chromium documentation for Intel ME](https://chromium.googlesource.com/chromiumos/third_party/flashrom/+/master/Documentation/mysteries_intel.txt) + + + +### [CSME Version](#org.fwupd.hsi.Mei.Version) + +Converged Security and Manageability Engine is a standalone management module that can manage and control some local devices without the host CPU involvement. +The CSME lives in the PCH and can only be updated by the OEM vendor. +The version of the CSME module can be checked to detect the most common and serious vulnerabilities: CVE-2017-5705, CVE-2017-5708, CVE-2017-5711, CVE-2017-5712, CVE-2017-5711, CVE-2017-5712, CVE-2017-5706, CVE-2017-5709, CVE-2017-5707 or CVE-2017-5710. + +**Impact:** Using any one of the critical vulnerabilities, a remote attacker can take full control of the system and all connected devices, even when the system is powered off. + +**Possible results:** + +- `valid`: is not affected by the most critical CVEs +- `not-valid`: affected by one of the below CVEs + +To meet HSI-1 on systems that run this test, the result must be `valid`. *[v1.5.0]* + +**References:** + +- [Intel CSME Security Review Cumulative Update](https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00086.html) + + + + +### [Intel DCI](#org.fwupd.hsi.PlatformDebugEnabled) + +Newer Intel CPUs support debugging over USB3 via a proprietary Direct Connection Interface (DCI) with the use of off-the-shelf hardware. + +**Impact:** Using DCI an attacker with physical access to the computer has full access to all registers and memory in the system, and is able to make changes. +This makes privilege escalation from user to root possible, and also modifying SMM makes it possible to write to system firmware for a persistent backdoor. + +**Possible results:** + +- `enabled`: debugging is currently enabled +- `not-enabled`: debugging is not currently enabled + +To meet HSI-1 on systems that run this test, the result must be `not-enabled`. *[v1.5.0]* + +Note: this attribute was previously known as org.fwupd.hsi.IntelDci.Enabled in 1.5.0, but was renamed in 1.8.0 to support other vendors. + +**References:** + +- [Intel Direct Connect Interface](https://www.intel.co.uk/content/www/uk/en/support/articles/000029393/processors.html) +- [Chipsec 4xxlp register definitions](https://github.com/chipsec/chipsec/blob/master/chipsec/cfg/8086/pch_4xxlp.xml#L270) +- [RISC-V EDK PCH register definitions](https://github.com/riscv/riscv-edk2-platforms/blob/85a50de1b459d1d6644a402081120770aa6dd8c7/Silicon/Intel/CoffeelakeSiliconPkg/Pch/Include/Register/PchRegsDci.h) + + + +### [Part is fused](#org.fwupd.hsi.PlatformFused) + +When fuses are blown in parts from some manufacturers the hardware will enforce protections against +tampering or accessing of certain registers. + +**Impact:** If using an unfused part, the platform's overall security will be decreased. + +**Possible results:** + +- `locked`: device is fused +- `not-locked`: device is not fused + +To meet HSI-1 on systems that run this test, the result must be `locked`. *[v1.8.0]* + + + + +### [Part is debug locked](#org.fwupd.hsi.PlatformDebugLocked) + +Some devices support a concept of whether a part has been unlocked for debugging using proprietary hardware. Such parts allow access to registers that are typically restricted when parts are fused. +On Intel systems access to this interface is done via a proprietary Direct Connection Interface (DCI). + +**Impact:** If using a debug unlocked part, the platform's overall security will be decreased as an attacker may have elevated access to registers and memory within the system and can potentially enable persistent backdoors. + +**Possible results:** + +- `locked`: device is locked +- `not-locked`: device is not not locked + +To meet HSI-2 on systems that run this test, the result must be `locked`. *[v1.8.0]* + +Note: this attribute was previously known as org.fwupd.hsi.IntelDci.Locked in 1.5.0, but was renamed in 1.8.0 to support other vendors. + +**References:** + +- [Intel Direct Connect Interface](https://www.intel.co.uk/content/www/uk/en/support/articles/000029393/processors.html) + + + +### [Empty PCR in TPM](#org.fwupd.hsi.Tpm.EmptyPcr) + +The system firmware is responsible for measuring values about its boot stage in PCRs 0 through 7. +Some firmwares have bugs that prevent them from measuring some of those values, breaking the fundamental assumption of the Measured Boot chain-of-trust. + +**Impact:** A local attacker could measure fake values into the empty PCR, corresponding to a firmware and OS that do not match the ones actually loaded. +This allows hiding a compromised boot chain or fooling a remote-attestation server into believing that a different kernel is running. + +**Possible results:** + +- `valid`: all correct +- `not-valid`: at least one empty checksum has been found +- `not-found`: no TPM hardware could be found + +To meet HSI-1 on systems that run this test, all PCRs from 0 to 7 in all banks must have non-empty measurements *[v1.7.2]* + +**References:** + +- [CVE-2021-42299: TPM Carte Blanche](https://github.com/google/security-research/blob/master/pocs/bios/tpm-carte-blanche/writeup.md) + + + +### [PCR0 TPM Event Log Reconstruction](#org.fwupd.hsi.Tpm.ReconstructionPcr0) + +The TPM event log records which events are registered for the PCR0 hash. +When reconstructed the event log values should always match the TPM PCR0. +If extra events are included in the event log, or some are missing, the reconstitution will fail. + +**Impact:** This is not a vulnerability per-se, but it shows that the system firmware checksum cannot be verified as the PCR result has been calculated incorrectly. + +**Possible results:** + +- `valid`: all correct +- `not-valid`: could not reconstitute the hash value +- `not-found`: no TPM hardware could be found + +To meet HSI-2 on systems that run this test, the result must be `valid`. *[v1.5.0]* + +**More information** +Additional information about specific bugs and debugging steps are available on the [fwupd wiki](https://github.com/fwupd/fwupd/wiki/TPM-PCR0-differs-from-reconstruction). + +**References:** + +- [Linux Kernel TPM Documentation](https://www.kernel.org/doc/html/latest/security/tpm/tpm_event_log.html) + + + + +### [Pre-boot DMA protection](#org.fwupd.hsi.PrebootDma) + +The IOMMU on modern systems is used to mitigate against DMA attacks. +All I/O for devices capable of DMA is mapped into a private virtual memory region. +On Intel systems the ACPI DMAR table indicated the system is configured with pre-boot DMA protection which eliminates some firmware attacks. +On AMD systems the ACPI IVRS table indicates the same. + +**Impact:** The IOMMU may is disabled at boot. +An attacker could connect a malicious peripheral using ThunderBolt and reboot the machine, which would allow the attacker to modify the system memory. +This would allow subverting the Secure Boot protection, and also invalidate any system attestation. + +**Possible results:** + +- `enabled`: detected correctly +- `not-valid`: could not determine state +- `not-enabled`: was not enabled + +To meet HSI-3 on systems that run this test, the result must be `enabled`. *[v1.8.0]* + +Note: a previous version of this attribute existed in 1.5.0 but was only for Intel systems. +It was renamed in 1.8.0 to support other vendors. + +**References:** + +- [IOMMU Wikipedia Page](https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit) +- [AMD IVRS Specification](https://www.amd.com/system/files/TechDocs/48882_IOMMU.pdf) + + + + +### [Intel BootGuard: Enabled](#org.fwupd.hsi.IntelBootguard.Enabled) + +BootGuard is a processor feature that prevents the machine from running firmware images not released by the system manufacturer. +It forms a root-of-trust by fusing in cryptographic keys into the processor itself that are used to verify the Authenticated Code Modules found in the SPI flash. + +**Impact:** When BootGuard is not set up correctly then the chain-of-trust between the CPU and the bootloader can not be verified. +This would allow subverting the Secure Boot protection which gives the attacker full access to your hardware. + +**Possible results:** + +- `enabled`: detected and enabled +- `not-enabled`: not detected, or detected but not enabled + +To meet HSI-2 on systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**References:** + +- [Coreboot documentation](https://github.com/coreboot/coreboot/blob/master/src/soc/intel/jasperlake/include/soc/me.h) + + + +### [Intel BootGuard: Verified](#org.fwupd.hsi.IntelBootguard.Verified) + +BootGuard is a processor feature that prevents the machine from running firmware images not released by the system manufacturer. +It forms a root-of-trust by fusing in cryptographic keys into the processor itself that are used to verify the Authenticated Code Modules found in the SPI flash. + +**Impact:** When BootGuard is not set up correctly then the chain-of-trust between the CPU and the bootloader can not be verified. +This would allow subverting the Secure Boot protection which gives the attacker full access to your hardware. + +**Possible results:** + +- `success`: verified boot chain +- `not-valid`: boot is not verified + +To meet HSI-2 on systems that run this test, the result must be `success`. *[v1.5.0]* + + + +### [Intel BootGuard: ACM](#org.fwupd.hsi.IntelBootguard.Acm) + +BootGuard is a processor feature that prevents the machine from running firmware images not released by the system manufacturer. +It forms a root-of-trust by fusing in cryptographic keys into the processor itself that are used to verify the Authenticated Code Modules found in the SPI flash. + +**Impact:** When BootGuard is not set up correctly then the chain-of-trust between the CPU and the bootloader can not be verified. +This would allow subverting the Secure Boot protection which gives the attacker full access to your hardware. + +**Possible results:** + +- `valid`: ACM protected +- `not-valid`: boot is not verified + +To meet HSI-2 on systems that run this test, the result must be `valid`. *[v1.5.0]* + + + +### [Intel BootGuard: Policy](#org.fwupd.hsi.IntelBootguard.Policy) + +BootGuard is a processor feature that prevents the machine from running firmware images not released by the system manufacturer. +It forms a root-of-trust by fusing in cryptographic keys into the processor itself that are used to verify the Authenticated Code Modules found in the SPI flash. + +**Impact:** The attacker can invalidate the chain of trust (subverting Secure Boot), and the user would get just a console warning and then continue to boot. + +**Possible results:** + +- `valid`: error enforce policy is set to shutdown +- `not-valid`: policy is invalid + +To meet HSI-3 on systems that run this test, the result must be `valid`. *[v1.5.0]* + + + +### [Intel BootGuard: OTP](#org.fwupd.hsi.IntelBootguard.Otp) + +BootGuard is a processor feature that prevents the machine from running firmware images not released by the system manufacturer. +It forms a root-of-trust by fusing in cryptographic keys into the processor itself that are used to verify the Authenticated Code Modules found in the SPI flash. + +**Impact:** When BootGuard is not set up correctly then the chain-of-trust between the CPU and the bootloader can not be verified. +This would allow subverting the Secure Boot protection which gives the attacker full access to your hardware. + +**Possible results:** + +- `valid`: SOC is locked +- `not-valid`: SOC is not locked + +To meet HSI-2 on systems that run this test, the result must be `valid`. *[v1.5.0]* + + + +### [Suspend to RAM disabled](#org.fwupd.hsi.SuspendToRam) + +Suspend to Ram (S3) keeps the raw contents of the DRAM refreshed when the system is asleep. +This means that the memory modules can be physically removed and the contents recovered, or a cold boot attack can be performed with a USB device. +The firmware should be configured to prefer using suspend to idle instead of suspend to ram or to not offer suspend to RAM. + +**Impact:** An attacker with physical access to a system can obtain the un-encrypted contents of the RAM by suspending the machine, removing the DIMM and inserting it into another machine with modified DRAM controller before the memory contents decay. + +**Possible results:** + +- `enabled`: sleep enabled +- `not-enabled`: suspend-to-ram being used +- `not-valid`: could not determine the default + +To meet HSI-3 on systems that run this test, the result must be `not-enabled`. *[v1.5.0]* + +**References:** + +- [Cold Boot Attack Wikipedia Page](https://en.wikipedia.org/wiki/Cold_boot_attack) + + + +### [Intel CET: Available](#org.fwupd.hsi.IntelCet.Enabled) + +Control enforcement technology is available on new Intel platforms and prevents exploits from hijacking the control-flow transfer instructions for both forward-edge (indirect call/jmp) and back-edge transfer (ret). + +**Impact:** A local or physical attacker with an existing unrelated vulnerability can use a reliable and well-known method to run arbitrary code. + +**Possible results:** + +- `enabled`: feature enabled by the platform +- `not-supported`: not supported + +To meet HSI-3 on systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**References:** + +- [Intel CET Technology Preview](https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf) + + + +### [Intel CET: Active](#org.fwupd.hsi.IntelCet.Active) + +Control enforcement technology is available on new Intel platforms and prevents exploits from hijacking the control-flow transfer instructions for both forward-edge (indirect call/jmp) and back-edge transfer (ret). + +**Impact:** A local or physical attacker with an existing unrelated vulnerability can use a ROP gadget to run arbitrary code. + +**Possible results:** + +- `supported`: being used +- `not-supported`: not being used by the host + +To meet HSI-3 on systems that run this test, the result must be `supported`. *[v1.5.0]* + +**References:** + +- [Intel CET Technology Preview](https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf) + + + +### [DRAM memory encryption](#org.fwupd.hsi.EncryptedRam) + +TME (Intel) or SME (AMD) is used by the hardware on supported SOCs to encrypt all data on external memory buses. +It mitigates against an attacker being able to capture memory data while the system is running or to capture memory by removing a DRAM chip. + +This encryption may be activated by either transparently via firmware configuration or by code running in the Linux kernel. + +**Impact:** A local attacker can either extract unencrypted content by attaching debug probes on the DIMM modules, or by removing them and inserting them into a computer with a modified DRAM controller. + +**Possible results:** + +- `encrypted`: detected and enabled +- `not-encrypted`: detected but disabled +- `not-supported`: not available + +To meet HSI-4 on systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**References:** + +- [Intel TME Press Release](https://software.intel.com/content/www/us/en/develop/blogs/intel-releases-new-technology-specification-for-memory-encryption.html) +- [WikiChip SME Overview](https://en.wikichip.org/wiki/x86/sme) + + + +### [AMD Rollback protection](#org.fwupd.hsi.Amd.RollbackProtection) + +AMD SOCs include the ability to prevent a rollback attack by a rollback protection feature on the firmware. This feature prevents an attacker from loading an older +firmware onto the part after a security vulnerability has been fixed. + +**Impact:** SOCs without this feature may be attacked by an attacker installing an older firmware that takes advantage of a well-known vulnerability. + +**Possible results:** + +- `enabled`: rollback protection enabled +- `not-enabled`: rollback protection disabled + +To meet HSI-1 on AMD systems that run this test, the result must be `enabled`. *[v1.8.0]* + +**References:** + +- [Rollback protection](https://www.psacertified.org/blog/anti-rollback-explained/) + + + +### [AMD SPI Write protections](#org.fwupd.hsi.Amd.SpiWriteProtection) + +SOCs may enforce control of the SPI bus to prevent writes other than by verified entities. + +**Impact:** SOCs without this feature may be attacked by an attacker modifying the SPI. + +**Possible results:** + +- `enabled`: spi protections enabled +- `not-enabled`: spi protections disabled + +To meet HSI-2 on systems that run this test, the result must be `enabled`. *[v1.8.0]* + + + +### [AMD SPI Replay protections](#org.fwupd.hsi.Amd.SpiReplayProtection) + +SOCs may include support for replay-protected monotonic counters to prevent replay attacks. + +**Impact:** SOCs without this feature may be attacked by an attacker modifying the SPI. + +**Possible results:** + +- `enabled`: spi protections enabled +- `not-enabled`: spi protections disabled + +To meet HSI-3 on systems that run this test, the result must be `enabled`. *[v1.8.0]* + + + +### [Supervisor Mode Access Prevention](#org.fwupd.hsi.IntelSmap) + +Without Supervisor Mode Access Prevention, the supervisor code usually has full read and write access to user-space memory mappings. +This can make exploits easier to write, as it allows the kernel to access user-space memory when it did not intend to. + +**Impact:** A local or remote attacker can use a simple exploit to modify the contents of kernel memory which can lead to privilege escalation. + +**Possible results:** + +- `enabled`: features are detected and enabled +- `not-supported`: not enabled + +To meet HSI-4 on systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**References:** + +- [SMAP Wikipedia Page](https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention) + + + +### [Kernel DMA protection](#org.fwupd.hsi.Iommu) + +The IOMMU on modern systems is used to mitigate against DMA attacks. +All I/O for devices capable of DMA is mapped into a private virtual memory region. +Common implementations are Intel VT-d and AMD-Vi. + +**Impact:** An attacker with inexpensive PCIe development hardware can write to system RAM from the ThunderBolt or Firewire ports which can lead to privilege escalation. + +**Possible results:** + +- `enabled`: hardware detected and enabled +- `not-found`: hardware was not detected + +To meet HSI-2 on systems that run this test, the result must be `enabled`. *[v1.5.0]* + +**Resolution:** If available, turn on IOMMU in the system BIOS. You may also have to use additional kernel boot parameters, for example `intel_iommu=on iommu=pt`. + +**References:** + +- [IOMMU Wikipedia Page](https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit) + + + +### [Suspend-to-Idle](#org.fwupd.hsi.SuspendToIdle) + +The platform should be set up with Suspend-to-Idle as the default S3 sleep state. + +**Impact:** A local attacker could overwrite the S3 resume script to modify system RAM which can lead to privilege escalation. + +**Possible results:** + +- `enabled`: deep sleep enabled +- `not-enabled`: suspend-to-idle being used +- `not-valid`: could not determine the default + +To meet HSI-3 on systems that run this test, the result must be `not-enabled`. *[v1.5.0]* + + + +## [Conclusion](#conclusions) + +Any system with a Host Security ID of `0` can easily be modified from userspace. +PCs with confidential documents should have a `HSI:3` or higher level of protection. +In a graphical tool that would show details about the computer (such as GNOME Control Center's details tab) the OS could display a field indicating Host Security ID. +The ID should be shown with an alert color if the security is not at least `HSI:1` or the suffix is `!`. + +On Linux `fwupd` is used to enumerate and update firmware. +It exports a property `HostSecurityId` and a `GetHostSecurityAttrs()` method. +The attributes are supposed to represent the *system as a whole* but individual (internal) devices are able to make a claim that they worsened the state of the security of the system. +Certain attributes can "obsolete" other attributes. +An example is BIOSGuard will set obsoletes to `org.intel.prx`. + +A plugin method gets called on each plugin which adds attributes directly from the hardware or kernel. +Several attributes may be dependent upon the kernel performing measurements and it will take time for these to be upstreamed. +In some cases security level measurements will only be possible on systems with a newer kernel. + +The long term goal is to increase the `HSI:x` level of systems being sold to consumers. +By making some of the `HSI:x` attributes part of the LVFS uploaded report we can allow users to compare vendors and models before purchasing hardware. + + + +## [Intentional Omissions](#ommissions) + +### Intel SGX + +This is not widely used as it has several high severity security issues. + +### Intel MPX + +MPX support was removed from GCC and the Linux kernel in 2019 and it is now considered obsolete. + +## Further Work + +More internal and external devices should be factored into the security equation. +For now the focus for further tests should be around internal device firmware as it is what can be most directly controlled by fwupd and the hardware manufacturer. + +Security conscious manufacturers are actively participating in the development of future initiatives in the Trusted Computing Group (TCG). +As those become ratified standards that are available in hardware, there are opportunities for synergy with this specification. diff --git a/fwupd-1.8.6/docs/index.html b/fwupd-1.8.6/docs/index.html new file mode 100644 index 0000000000000000000000000000000000000000..3e58e7a2ebe0ad3e76c1f83623d3c8c348ad0deb --- /dev/null +++ b/fwupd-1.8.6/docs/index.html @@ -0,0 +1,80 @@ + + + + fwupd + + + + + + + + + + + + + + + + +
    +
    +

    libfwupd

    +
    +

    Functionality exported by libfwupd for client applications.

    +
    +
    + +
    +

    libfwupdplugin

    +
    +

    Functionality available to fwupd plugins.

    +
    +
    + + + +
    +

    Host Security ID

    +
    +

    The fwupd HSI specification.

    +
    +
    + +
    +

    BIOS Settings

    +
    +

    The fwupd BIOS settings interface

    +
    +
    + +
    +

    BOS DS20 Specification

    +
    +

    The fwupd Binary Object Store descriptor specification

    +
    +
    + +
    + + diff --git a/fwupd-1.8.6/docs/meson.build b/fwupd-1.8.6/docs/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..75dd9a3d7b4b840677299298749f8457fa82fbf9 --- /dev/null +++ b/fwupd-1.8.6/docs/meson.build @@ -0,0 +1,91 @@ +if gidocgen_dep.found() and docs_python_deps.allowed() and gidocgen_app.found() and introspection.allowed() + toml_conf = configuration_data() + docgen_version = source_version + if git.found() and source_version != fwupd_version + docgen_version = run_command(git, 'branch', '--show-current').stdout().strip() + endif + toml_conf.set('version', docgen_version) + + fwupd_toml = configure_file( + input: 'fwupd.toml.in', + output: 'fwupd.toml', + configuration: toml_conf + ) + + fwupdplugin_toml = configure_file( + input: 'fwupdplugin.toml.in', + output: 'fwupdplugin.toml', + configuration: toml_conf + ) + + custom_target('doc-fwupd', + input: [ + fwupd_toml, + fwupd_gir[0], + ], + output: 'libfwupd', + command: [ + gidocgen_app, + 'generate', + '--quiet', + '--add-include-path=@0@'.format(meson.current_build_dir() / '../libfwupd'), + '--config=@INPUT0@', + '--output-dir=@OUTPUT@', + '--no-namespace-dir', + '--content-dir=@0@'.format(meson.current_source_dir()), + '@INPUT1@', + ], + depends: [ + fwupd_gir[0], + ], + build_by_default: true, + install: true, + install_dir: join_paths(datadir, 'doc'), + ) + + custom_target('doc-fwupdplugin', + input: [ + fwupdplugin_toml, + fwupdplugin_gir[0], + ], + output: 'libfwupdplugin', + command: [ + gidocgen_app, + 'generate', + '--quiet', + '--add-include-path=@0@'.format(meson.current_build_dir() / '../libfwupd'), + '--add-include-path=@0@'.format(meson.current_build_dir() / '../libfwupdplugin'), + '--config=@INPUT0@', + '--output-dir=@OUTPUT@', + '--no-namespace-dir', + '--content-dir=@0@'.format(meson.current_source_dir()), + '@INPUT1@', + ], + depends: [ + fwupdplugin_gir[0], + ], + build_by_default: true, + install: true, + install_dir: join_paths(datadir, 'doc'), + ) + if hsi + install_data(['index.html', 'hsi.html'], + install_dir : join_paths(datadir, 'doc', 'fwupd') + ) + endif + install_data(['urlmap_fwupd.js'], + install_dir: join_paths(datadir, 'doc', 'libfwupd') + ) + install_data(['urlmap_fwupdplugin.js'], + install_dir: join_paths(datadir, 'doc', 'libfwupdplugin') + ) + #make devhelp work + install_symlink('libfwupd', + install_dir: join_paths(datadir, 'doc', 'fwupd'), + pointing_to: join_paths('..', 'libfwupd'), + ) + install_symlink('libfwupdplugin', + install_dir: join_paths(datadir, 'doc', 'fwupd'), + pointing_to: join_paths('..', 'libfwupdplugin'), + ) +endif diff --git a/fwupd-1.8.6/docs/tutorial.md b/fwupd-1.8.6/docs/tutorial.md new file mode 100644 index 0000000000000000000000000000000000000000..aba4cd42eae7c08a2191ab3e2d61fc7b47e23381 --- /dev/null +++ b/fwupd-1.8.6/docs/tutorial.md @@ -0,0 +1,827 @@ +--- +title: Plugin Tutorial +--- + +## Introduction + +At the heart of fwupd are plugins that gets run at startup, when devices +get hotplugged and when updates are done. +The idea is we have lots of small plugins that each do one thing, and are +ordered by dependencies against each other at runtime. +Using plugins we can add support for new hardware or new policies without making +big changes all over the source tree. + +There are broadly 3 types of plugin methods: + +- **Mechanism**: Upload binary data into a specific hardware device. +- **Policy**: Control the system when updates are happening, e.g. preventing the + user from powering-off. +- **Helpers**: Providing more metadata about devices, for instance handling device quirks. + +A plugin only needs to define the vfuncs that are required, and the plugin name +is taken automatically from the GType. + + /* fu-foo-plugin.h + * + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + + #pragma once + + #include + + G_DECLARE_FINAL_TYPE(FuFooPlugin, fu_foo_plugin, FU, FOO_PLUGIN, FuPlugin) + + /* fu-foo-plugin.c + * + * Copyright (C) Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + + #include "config.h" + + #include "fu-foo-plugin.h" + + struct _FuFooPlugin { + FuPlugin parent_instance; + gpointer proxy; + }; + + G_DEFINE_TYPE(FuFooPlugin, fu_foo_plugin, FU_TYPE_PLUGIN) + + static gboolean + fu_foo_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) + { + FuPluginData *data = fu_plugin_get_data(plugin); + self->proxy = create_proxy(); + if(self->proxy == NULL) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, + "failed to create proxy"); + return FALSE; + } + return TRUE; + } + + static void + fu_foo_plugin_init(FuFooPlugin *self) + { + } + + static void + fu_foo_constructed(GObject *obj) + { + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_BEFORE, "dfu"); + } + + static void + fu_foo_finalize(GObject *obj) + { + FuFooPlugin *self = FU_FOO_PLUGIN(obj); + destroy_proxy(self->proxy); + G_OBJECT_CLASS(fu_foo_plugin_parent_class)->finalize(obj); + } + + static void + fu_foo_plugin_class_init(FuFooPluginClass *klass) + { + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_foo_constructed; + object_class->finalize = fu_foo_finalize; + plugin_class->startup = fu_foo_plugin_startup; + } + +We have to define when our plugin is run in reference to other plugins, in this +case, making sure we run before the `dfu` plugin. + +For most plugins it does not matter in what order they are run and this +information is not required. + +## 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. +The example here is all hardcoded, and a true plugin would have to +derive the details about the `FuDevice` from the hardware, for example reading +data from `sysfs` or `/dev`. + + static gboolean + fu_foo_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) + { + g_autoptr(FuDevice) dev = NULL; + fu_device_set_id(dev, "dummy-1:2:3"); + fu_device_add_guid(dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); + fu_device_set_version(dev, "1.2.3"); + fu_device_get_version_lowest(dev, "1.2.2"); + fu_device_get_version_bootloader(dev, "0.1.2"); + fu_device_add_icon(dev, "computer"); + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_plugin_device_add(plugin, dev); + return TRUE; + } + + static void + fu_foo_plugin_class_init(FuFooPluginClass *klass) + { + … + plugin_class->coldplug = fu_foo_plugin_coldplug; + … + } + +This shows a lot of the plugin architecture in action. +Some notable points: + +- The device ID (`dummy-1:2:3`) has to be unique on the system between all +plugins, so including the plugin name as a prefix is probably a good idea. + +- The GUID value can be generated automatically using +`fu_device_add_guid(dev,"some-identifier")` but is quoted here explicitly. The +GUID value has to match the `provides` value in the `.metainfo.xml` file for the +firmware update to succeed. + +- Setting a display name and an icon is a good idea in case the GUI software +needs to display the device to the user. Icons can be specified using a full +path, although icon theme names should be preferred for most devices. + +- The `FWUPD_DEVICE_FLAG_UPDATABLE` flag tells the client code that the device +is in a state where it can be updated. If the device needs to be in a special +mode (e.g. a bootloader) then the `FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER` flag can +also be used. If the update should only be allowed when there is AC power +available to the computer (i.e. not on battery) then +`FWUPD_DEVICE_FLAG_REQUIRE_AC` should be used as well. There are other flags and +the API documentation should be used when choosing what flags to use for each +kind of device. + +- Setting the lowest allows client software to refuse downgrading the device to +specific versions. +This is required in case the upgrade migrates some kind of data-store so as to +be incompatible with previous versions. +Similarly, setting the version of the bootloader (if known) allows the firmware +to depend on a specific bootloader version, for instance allowing signed +firmware to only be installable on hardware with a bootloader new enough to +deploy it. + +## Mechanism Plugins + +Although it would be a wonderful world if we could update all hardware using a +standard shared protocol this is not the universe we live in. +Using a mechanism like DFU or UpdateCapsule means that fwupd will just work +without requiring any special code, but for the real world we need +to support vendor-specific update protocols with layers of backwards compatibility. + +When a plugin has created a device that is `FWUPD_DEVICE_FLAG_UPDATABLE` we can +ask the daemon to update the device with a suitable `.cab` file. +When this is done the daemon checks the update for compatibility with the device, +and then calls the vfuncs to update the device. + + static gboolean + fu_foo_plugin_write_firmware(FuPlugin *plugin, + FuDevice *dev, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) + { + gsize sz = 0; + guint8 *buf = g_bytes_get_data(blob_fw, &sz); + /* write 'buf' of size 'sz' to the hardware */ + return TRUE; + } + + static void + fu_foo_plugin_class_init(FuFooPluginClass *klass) + { + … + plugin_class->write_firmware = fu_foo_plugin_write_firmware; + … + } + +It's important to note that the `blob_fw` is the binary firmware file +(e.g. `.dfu`) and **not** the `.cab` binary data. + +If `FWUPD_INSTALL_FLAG_FORCE` is used then the usual checks done by the flashing +process can be relaxed (e.g. checking for quirks), but please don't brick the +users hardware even if they ask you to. + +## Policy Helpers + +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 threshold, or it could be as complicated as ensuring a vendor-specific +GPIO is asserted when specific types of hardware are updated. + + static gboolean + fu_foo_plugin_prepare(FuPlugin *plugin, FuDevice *device, GError **error) + { + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC && !on_ac_power()) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AC_POWER_REQUIRED, + "Cannot install update " + "when not on AC power"); + return FALSE; + } + return TRUE; + } + + static gboolean + fu_foo_plugin_cleanup(FuPlugin *plugin, FuDevice *device, GError **error) + { + return g_file_set_contents("/var/lib/fwupd/something", + fu_device_get_id(device), -1, error); + } + + static void + fu_foo_plugin_class_init(FuFooPluginClass *klass) + { + … + plugin_class->prepare = fu_foo_plugin_prepare; + plugin_class->cleanup = fu_foo_plugin_cleanup; + … + } + +## Detaching to bootloader mode + +Some hardware can only be updated in a special bootloader mode, which 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. + +Before the device update is performed the fwupd daemon runs an optional +`update_detach()` vfunc which switches the device to bootloader mode. + +After the update (or if the update fails) an the daemon runs an optional +`update_attach()` vfunc which should switch the hardware back to runtime mode. +Finally an optional `update_reload()` vfunc is run to get the new firmware +version from the hardware. + +The optional vfuncs are **only** run on the plugin currently registered to +handle the device ID, although the registered plugin can change during the +attach and detach phases. + + static gboolean + fu_foo_plugin_detach(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) + { + if (hardware_in_bootloader) + return TRUE; + return _device_detach(device, progress, error); + } + + static gboolean + fu_foo_plugin_attach(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) + { + if (!hardware_in_bootloader) + return TRUE; + return _device_attach(device, progress, error); + } + + static gboolean + fu_foo_plugin_reload(FuPlugin *plugin, FuDevice *device, GError **error) + { + g_autofree gchar *version = _get_version(plugin, device, error); + if (version == NULL) + return FALSE; + fu_device_set_version(device, version); + return TRUE; + } + + static void + fu_foo_plugin_class_init(FuFooPluginClass *klass) + { + … + plugin_class->detach = fu_foo_plugin_detach; + plugin_class->attach = fu_foo_plugin_attach; + plugin_class->reload = fu_foo_plugin_reload; + … + } + +## The Plugin Object Cache + +The fwupd daemon provides a per-plugin cache which allows objects to be added, +removed and queried using a specified key. +Objects added to the cache must be `GObject`s to enable the cache objects to be +properly refcounted. + +## Debugging a Plugin + +If the fwupd daemon is started with `--plugin-verbose=$plugin` then the +environment variable `FWUPD_$PLUGIN_VERBOSE` is set process-wide. +This allows plugins to detect when they should output detailed debugging +information that would normally be too verbose to keep in the journal. +For example, using `--plugin-verbose=logitech_hidpp` would set +`FWUPD_LOGITECH_HID_VERBOSE=1`. + +## Using existing code to develop a plugin + +It is not usually possible to share a plugin codebase with firmware update +programs designed for other operating systems. + +Matching the same rationale as the Linux kernel, trying to use one code base +between projects with a compatibility shim layer in-between is real headache to +maintain. + +The general consensus is that trying to use a abstraction layer for hardware is +a very bad idea as you're not able to take advantage of the platform specific +helpers -- for instance quirk files and the custom GType device creation. + +The time the vendor saves by creating a shim layer and importing existing source +code into fwupd will be overtaken 100x by upstream maintenance costs longer term, +which isn't fair. + +In a similar way, using C++ rather than GObject C means expanding the test matrix +to include clang in C++ mode and GNU g++ too. +It's also doubled the runtime requirements to now include both the C standard library +as well as the C++ standard library and increases the dependency surface. + +Most rewritten fwupd plugins at up to x10 smaller than the standalone code as they +can take advantage of helpers provided by fwupd rather than re-implementing error +handling, device quirking and data chunking. + +## General guidelines for plugin developers + +### General considerations + +When adding support for a new device in fwupd some things need to be +evaluated beforehand: + +- how the hardware is discovered, identified and polled. +- how to communicate with the device (USB? file open/read/write?) +- does the device need to be switched to bootloader mode to make it + upgradable? +- about the format of the firmware files, do they follow any standard? + are they already supported in fwupd? +- about the update protocol, is it already supported in fwupd? +- Is the device composed of multiple different devices? Are those + devices enumerated and programmed independently or are they accessed + and flashed through a "root" device? + +In most cases, even if the features you need aren't implemented yet, +there's already a plugin that does something similar and can be used as +an example, so it's always a good idea to read the code of the existing +plugins to understand how they work and how to write a new one, as no +documentation will be as complete and updated as the code +itself. Besides, the mechanisms implemented in the plugin collection are +very diverse and the best way of knowing what can be done is to check +what is already been done. + +### Leveraging existing fwupd code + +Depending on how much of the key items for the device update (firmware +format, update protocol, transport layer) are already supported in +fwupd, the work needed to add support for a new device can range from +editing a quirk file to having to fully implement new device and +firmware types, although in most cases fwupd already implements helper +code that can be extended. + +#### If the firmware format, update protocol and device communication are already supported + +This is the simplest case, where an existing plugin fully implements the +update process for the new device and we only have to let fwupd know +that that plugin should be used for our device. In this case the only +thing to do is to edit the plugin quirk file and add the device +identifier in the format expected by the plugin together with any +required options for it (at least a "Plugin" key to declare that this is +the plugin to use for this device). Example: + + +#### If the device type is not supported + +Then we have to take a look at the existing device types and check if +there's any of them that have similarities and which can be partially +reused or extended for our device. If the device type is derivable and +it can support our new device by implementing the proper vfuncs, then we +can simply subclass it and add the required functionalities. If not, +we'll need to study what is the best way to reuse it for our needs. + +If a plugin already implements most of the things we need besides the +device type, we can add our new device type to that plugin. Otherwise we +should create a plugin that will hold the new device type. + +The core fwupd code contains some basic device types (such as +[FuUdevDevice](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-udev-device.c), [FuUsbDevice](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-usb-device.c), [FuBluezDevice](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-bluez-device.c)) that can be used as a base +type for most devices in case we have to implement our own device +access, identification and communication from scratch. + +If the device is natively visible by the OS, most of the time fwupd can +detect the device connection and disconnection by listening to udev +events, but a supported device may also be not directly accessible from +the OS -- for example, a composite device that contains an updatable chip +that's connected through I2C to a USB hub that acts as an interface. In +that case, the device discovery and enumeration must be programmed by +the developer, but the same device identification and management +mechanisms apply in all cases. See the "Creating a new device type" and +"Device identification" below for more details. + +#### If the firmware type is not supported + +Same as with the new device type, there could be an existing firmware +type that can be used as a base type for our new type, so first of all +we should look for firmware types that are similar to the one we're +using. Then, choosing where to define the new type depends on whether +there's already a plugin that implements most of the functionalities we +need or not. + +### Example: extending a firmware type + +Our firmware files are Intel HEX files that have optional +vendor-specific sections at fixed addresses, this is not supported by +any firmware type in fwupd out of the box but the [FuIhexFirmare](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-ihex-firmware.c) class +parses and models a standard Intel HEX file, so we can create a subclass +of it for our firmware type and override the parse method so that it +calls the method from the parent class, which would parse the file, and +then we can get the data with `fu_firmware_get_bytes()` and do the rest of +the custom parsing. Example: + + +### Example: extending a device type + +Communication with our new device is carried out by doing +read/write/ioctl operations on a device file, but using a custom +protocol that is not supported in fwupd. + +For this type of device we can create a new type derived from +`FuUdevDevice`, which takes care of discovering this type of devices, +possibly using a vendor-specific protocol, as well as of opening, +reading and writing device files, so we would only have to implement the +protocol on top of those primitives. (Example: +`fu_logitech_hidpp_runtime_bolt_poll_peripherals()` in +) +The process would be similar if our device was handled by a different +backend (USB or BlueZ). + +### Creating a new plugin + +The bare minimum a plugin should have is a `constructed` function that +defines the plugin characteristics such as the device type and firmware +type handled by it, the build hash and any plugin-specific quirk keys +that can be used for the plugin. + + static void + fu_foo_plugin_constructed(GObject *obj) + { + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_MOUSE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_GAMEPAD); + } + + static void + fu_foo_plugin_class_init(FuFooPluginClass *klass) + { + plugin_class->init = fu_foo_plugin_constructed; + } + +### Creating a new device type + +Besides defining its attributes as a data type, a device type should +implement at least the usual `init`, `finalize` and `class_init` functions, +and then, depending on its parent type, which methods it overrides and +what it does, it must implement a set of device methods. These are some +of them, the complete list is in [libfwupdplugin/fu-device.h](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.h). + +#### to_string + +Called whenever fwupd needs a human-readable representation of the +device. + +#### probe + +The `probe` method is called the first time a device is opened, before +actually opening it. The generic probe methods implemented in the base +device types (such as USB/udev) take care of basic device identification +and setting the non-specific parameters that don't need the device to be +opened or the interface claimed (vendor id, product id, guids, etc.). + +The device-specific probe method should start by calling the generic +method upwards in the class tree and then do any other specific setup +such as setting the appropriate device flags. + +#### open + +Depending on the type of device, opening it means different things. For +instance, opening a udev device means opening its device file. + +If there's no interface-specific `open` method, then opening a device +simply calls the `probe()` and `setup()` methods (the `open()` method would be +called in between if it exists). + +#### setup + +Sets parameters on the device object that require the device to be open +and have the interface claimed. USB/udev generic devices don't implement +this method, this is normally implemented for each different plugin +device type if needed. + +#### prepare + +If implemented, this takes care of decompressing or parsing the firmware +data. For example, to check if the firmware is valid, if it's suitable +for the device, etc. + +It takes a stream of bytes (`GBytes`) as a parameter, representing the +raw binary firmware data. + +It should create the firmware object and call the appropriate method to +load the firmware. Otherwise, if it's not implemented for the specific +device type, the generic implementation in +[libfwupdplugin/fu-device.c](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.c):`fu_device_prepare_firmware()` +creates a firmware object loaded with a provided image. + +#### detach + +Implemented if the device needs to be put in bootloader mode before +updating, this does all the necessary operations to put the device in +that mode. fwupd can handle the case where a device needs to be +disconnected to do the mode switch if the device has the +`FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG` flag. + +#### attach + +The inverse of `detach()`, to configure the device back to application mode. + +#### reload + +If implemented, this is called after the device update if it needs to +perform any kind of post-update operation. + +#### write_firmware + +Writes a firmware passed as a raw byte stream. The firmware parsing and +processing is done by the firmware object, so that when this method gets +the blob it simply has to write it to the device in the appropriate way +following the device update protocol. + +#### read_firmware + +Reads the firmware data from the device without any device-specific +configuration or serial numbers. This is meant to retrieve the current +firmware contents for verification purposes. The data read can then be +output to a binary blob using `fu_firmware_write()`. + +#### set_progress + +Informs the daemon of the expected duration percentages for the different +phases of update. The daemon runs the `->detach()`, `->write_firmware()`, +`->attach()` and `->reload()` phases as part of the engine during the firmware +update (rather than being done by plugin-specific code) and so this vfunc +informs the daemon how to scale the progress output accordingly. + +For instance, if your update takes 2 seconds to detach into bootloader mode, +10 seconds to write the firmware, 7 seconds to attach back into runtime mode +(which includes the time required for USB enumeration) and then 1 second to +read the new firmware version you would use: + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 45, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 40, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "reload"); + +If however your device does not require `->detach()` or `->attach()`, and +`->reload()` is instantaneous, you still however need to include 4 steps: + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); + +If the device has multiple phases that occur when actually in the write phase +then it is perfectly okay to split up the `FuProgress` steps in the +`->write_firmware()` vfunc further. For instance: + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "wait-for-idle"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "reset"); + +It should be noted that actions that are required to be done *before* the update +should be added as a `->prepare()` vfunc, and those to be done after in the `->cleanup()` +as the daemon will then recover the hardware if the update fails. For instance, +putting the device back into a *normal runtime power saving* state should always +be done during cleanup. + +### Creating a new firmware type + +The same way a device type implements some methods to complete its +functionality and override certain behaviors, there's a set of firmware +methods that a firmware class can (or must) implement: + +#### parse + +If implemented, it parses the firmware file passed as a byte +sequence. If the firmware to be used contains a custom header, a +specific structured format or multiple images embedded, this method +should take care of processing the format and appropriately populating +the `FuFirmware` object passed as a parameter. If not implemented, the +whole data blob is taken as is. + +#### write + +Returns a `FuFirmware` object as a byte sequence. This can be used to +output a firmware read with `fu_device_read_firmware()` as a binary +blob. + +#### export + +Converts a `FuFirmware` object to an xml representation. If not +implemented, the default implementation generates an xml representation +containing only generic attributes and, optionally, the firmware data as +well as the representation of children firmware nodes. + +When testing the implementation of a new firmware type, this is useful +to show if the parsing and processing of the firmware are correct and +can be checked with: + + fwupdtool firmware-parse --plugins + +#### tokenize + +If implemented it tokenizes a firmware, breaking it into records. + +#### build + +This is the reverse of `export()`, it builds a `FuFirmware` object from +an xml representation. + +#### get_checksum + +The default implementation returns a checksum of the payload data of a +`FuFirmware` object. Subclass it only if the checksum of your firmware +needs to be computed differently. + +### Device identification + +A device is identified in fwupd by its physical and logical ids. A +physical id represents the electrical connection of the device to the +system and many devices can have the same physical id. For example, +`PCI_SLOT_NAME=0000:3e:00:0` (see +[libfwupdplugin/fu-udev-device.c](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-udev-device.c):`fu_udev_device_set_physical_id()` for +examples) . The logical id is used to disambiguate devices with the same +physical id. Together they identify a device uniquely. There are many +examples of this in the existing plugins, such as +`fu_pxi_receiver_device_add_peripherals()` in + + +Besides that, each device type will have a unique instance id, which is +a string representing the device subsystem, vendor, model and revision +(specific details depend on the device type). This should identify a +device type in the system, that is, a particular device type, model and +revision by a specific vendor will have a defined instance id and two of +the same device will have the same instance id (see +[libfwupdplugin/fu-udev-device.c](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-udev-device.c):`fu_udev_device_probe()` +for examples). + +One or more GUIDs are generated for a device from its identifying +attributes, these GUIDs are then used to match a firmware metadata +against a specific device type. See the implementation of the many +`probe()` methods for examples. + +### Support for BLE devices + +BLE support in fwupd on Linux is provided by BlueZ. If the device +implements the standard HID-over-GATT BLE profile, then communication +with the device can be done through the [hidraw +interface](https://www.kernel.org/doc/html/latest/hid/hidraw.html). If +the device implements a custom BLE profile instead, then it will have to +be managed by the `FuBluezBackend`, which uses the BlueZ DBus interface +to communicate with the devices. The `FuBluezDevice` type implements +device enumeration as well as the basic primitives to read and write BLE +characteristics, and can be used as the base type for a more specific +BLE device. + +### Battery checks + +If the device can be updated wirelessly or if the update process doesn't +rely on an external power supply, the vendor might define a minimum +operative battery level to guarantee a correct update. fwupd provides a +simple API to define these requirements per-device. + +[fu_device_set_battery_threshold()](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.c) +can be used to define the minimum battery level required to allow a +firmware update on a device (10% by default). If the battery level is +below that threshold, fwupd will inhibit the device to prevent the user +from starting a firmware update. Then, the battery level of a device can +be queried and then set with +[fu_device_set_battery_level()](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.c). + +## Howtos + +### How to create a child device + +fwupd devices can be hierarchically ordered to model dependent and +composite devices such as docking stations composed of multiple +updatable chips. When writing support for a new composite device the +parent device should, at some point, poll the devices that "hang" from +it and register them in fwupd. The process of polling and identifying a +child device is totally vendor and device-specific, although the main +requirement for it is that the child device is properly identified +(having physical/logical and instance ids). Then, +[fu_device_add_child()](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.c) +can be used to add a new child device to an existing one. See +`fu_logitech_hidpp_runtime_bolt_poll_peripherals()` in + +for an example. + +Note that when deploying and installing a firmware set for a composite +device, there might be firmware dependencies between parent and child +devices that require a specific update ordering (for instance, child +devices first, then the parent). This can be modeled by setting an +appropriate firmware priority in the firmware metainfo or by setting the +`FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST` device flag. + +### How to add a delay + +In certain scenarios you may need to introduce small controlled delays +in the plugin code, for instance, to comply with a communications +protocol or to wait for the device to be ready after a particular +operation. In this case you can insert a delay in microseconds with +`g_usleep` or a delay in seconds that shows a progress bar with +`fu_device_sleep_with_progress`. Note that, in both cases, this will +stop the application main loop during the wait, so use it only when +necessary. + +### How to define private flags + +Besides the regular flags and internal flags that any device can have, a +device can define private flags for specific uses. These can be enabled +in the code as well as in quirk files, just as the rest of flags. To +define a private flag: + +1. Define the flag value. This is normally defined as a macro that + expands to a binary flag, for example: `#define MY_PRIVATE_FLAG (1 << + 2)`. Note that this will be part of the ABI, so it must be versioned +1. Call `fu_device_register_private_flag` in the device init function + and assign a string identifier to the flag: + `fu_device_register_private_flag (FU_DEVICE (self), MY_PRIVATE_FLAG, + "myflag");` + +You can then add it to the device programmatically with +`fu_device_add_private_flag`, remove it with `fu_device_remove_private_flag` +and query it with `fu_device_has_private_flag`. In a quirk file, you can +add the flag identifier to the Flags attribute of a device (eg. `Flags = +myflag,is-bootloader`) + +### How to make fwupd wait for a device replug + +Certain devices require a disconnection and reconnection to start the +update process. A common example are devices that have two booting +modes: application or runtime mode, and bootloader mode, where the +runtime mode is the normal operation mode and the bootloader mode is +exclusively used to update the device firmware. It's common for these +devices to require some operation from fwupd to switch the booting mode +and then to need a reset to enter bootloader mode. Often, the device is +enumerated differently in both modes, so fwupd needs to know that the +same device will be identified differently depending on the boot mode. + +The common way to do this is to add the +`FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG` flag in the device before its detach +method returns. This will make fwupd wait for a predetermined amount of +time for the device to be detected again. Then, to inform fwupd about +the two identities of the same device, the `CounterpartGuid` key can be +used in a device entry to match it with another defined device (example: +). + +### Inhibiting a device + +If a device becomes unsuitable for an update for whatever reason (see +"Battery checks" above for an example), a plugin can temporarily disable +firmware updates on it by calling [fu_device_inhibit()](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.c). The device will +still be listed as present by `fwupdmgr get-devices`, but fwupd won't +allow firmware updates on it. Device inhibition can be disabled with +[fu_device_uninhibit()](https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-device.c). + +Note that there might be multiple inhibits on a specific device, the +device will only be updatable when all of them are removed. + +## Debugging tips + +The most important rule when debugging is using the `--verbose` flag +when running fwupd or fwupdtool. Besides that, there are many +environment variables that allow some debug traces to be printed +conditionally, for example: `FWUPD_PROBE_VERBOSE`, +`FU_HID_DEVICE_VERBOSE`, `FWUPD_DEVICE_LIST_VERBOSE` and many other +plugin-specific envvars. + +### Adding debug messages + +The usual way to print a debug message is using the `g_debug` macro. Each +relevant module will define its own `G_LOG_DOMAIN` to tag the debug traces +accordingly. See + and + for more +information. + +### Inspecting raw binary data + +The `fu_dump_full` and `fu_dump_raw` functions implement the +printing of a binary buffer to the console as a stream of bytes in +hexadecimal. See `libfwupdplugin/fu-common.c` for their definitions, you +can find many examples of how to use them in the plugins code. diff --git a/fwupd-1.8.6/docs/urlmap_fwupd.js b/fwupd-1.8.6/docs/urlmap_fwupd.js new file mode 100644 index 0000000000000000000000000000000000000000..7e7f2cb321da36d3b75124a47dcdc17b6ec53bad --- /dev/null +++ b/fwupd-1.8.6/docs/urlmap_fwupd.js @@ -0,0 +1,4 @@ +baseURLs = [ + [ 'Gio', 'https://people.gnome.org/~ebassi/docs/_build/Gio/' ], + [ 'GObject', 'https://people.gnome.org/~ebassi/docs/_build/GObject/' ], +] diff --git a/fwupd-1.8.6/docs/urlmap_fwupdplugin.js b/fwupd-1.8.6/docs/urlmap_fwupdplugin.js new file mode 100644 index 0000000000000000000000000000000000000000..a41bcdb879366a01c1142d0ab5346eb698741f01 --- /dev/null +++ b/fwupd-1.8.6/docs/urlmap_fwupdplugin.js @@ -0,0 +1,5 @@ +baseURLs = [ + [ 'Gio', 'https://people.gnome.org/~ebassi/docs/_build/Gio/' ], + [ 'GObject', 'https://people.gnome.org/~ebassi/docs/_build/GObject/' ], + [ 'Fwupd', '../libfwupd/' ], +] diff --git a/fwupd-1.8.6/libfwupd/README.md b/fwupd-1.8.6/libfwupd/README.md new file mode 100644 index 0000000000000000000000000000000000000000..de31bfa4e4844caf28fdaf7ad9b7ac056c43c06a --- /dev/null +++ b/fwupd-1.8.6/libfwupd/README.md @@ -0,0 +1,31 @@ +# libfwupd + +## Planned API/ABI changes for next release + +* Typedef `FwupdFeatureFlags` to `guint64` so it's the same size on all platforms +* Remove the `soup-session` fallback property in `FwupdClient`. +* Remove fwupd_device_set_vendor_id() and fwupd_device_get_vendor_id() +* Remove the deprecated flags like `FWUPD_DEVICE_FLAG_MD_SET_ICON` +* Remove `fwupd_release_get_uri()` and `fwupd_release_set_uri()` +* Rename `fwupd_client_install_release2_async()` to `fwupd_client_install_release_async()` +* Remove fwupd_device_set_protocol() and fwupd_device_get_protocol() +* Remove deprecated install flag `FWUPD_INSTALL_FLAG_IGNORE_POWER` + +## Migration from Version 0.9.x + +* Rename FU_DEVICE_FLAG -> FWUPD_DEVICE_FLAG +* Rename FWUPD_DEVICE_FLAG_ALLOW_ONLINE -> FWUPD_DEVICE_FLAG_UPDATABLE +* Rename FWUPD_DEVICE_FLAG_ALLOW_OFFLINE -> FWUPD_DEVICE_FLAG_ONLY_OFFLINE +* Rename fwupd_client_get_devices_simple -> fwupd_client_get_devices +* Rename fwupd_client_get_details_local -> fwupd_client_get_details +* Rename fwupd_client_update_metadata_with_id -> fwupd_client_update_metadata +* Rename fwupd_remote_get_uri -> fwupd_remote_get_metadata_uri +* Rename fwupd_remote_get_uri_asc -> fwupd_remote_get_metadata_uri_sig +* Rename fwupd_remote_build_uri -> fwupd_remote_build_firmware_uri +* Switch FWUPD_RESULT_KEY_DEVICE_CHECKSUM_KIND to fwupd_checksum_guess_kind() +* Rename fwupd_result_update_*() to fwupd_release_*() +* Rename fwupd_result_*() to fwupd_device_*() +* Convert FwupdResult to FwupdDevice in all callbacks +* Rename fwupd_device_*_provider -> fwupd_device_*_plugin +* Convert hash types sa{sv} -> a{sv} +* Convert fwupd_client_get_updates() -> fwupd_client_get_upgrades() diff --git a/fwupd-1.8.6/libfwupd/fwupd-bios-setting-private.h b/fwupd-1.8.6/libfwupd/fwupd-bios-setting-private.h new file mode 100644 index 0000000000000000000000000000000000000000..741b0eae0eb439087c87dedd0fb23a0146824ce2 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-bios-setting-private.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +#include "fwupd-bios-setting.h" + +#pragma once + +GVariant * +fwupd_bios_setting_to_variant(FwupdBiosSetting *self, gboolean trusted); +void +fwupd_bios_setting_to_json(FwupdBiosSetting *self, JsonBuilder *builder); +gboolean +fwupd_bios_setting_from_json(FwupdBiosSetting *self, JsonNode *json_node, GError **error); diff --git a/fwupd-1.8.6/libfwupd/fwupd-bios-setting.c b/fwupd-1.8.6/libfwupd/fwupd-bios-setting.c new file mode 100644 index 0000000000000000000000000000000000000000..26716f20535de2bc4b42249bf4926de11692ffe8 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-bios-setting.c @@ -0,0 +1,1066 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-bios-setting-private.h" +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" + +/** + * FwupdBiosSetting: + * + * A BIOS setting that represents a setting in the firmware. + */ + +static void +fwupd_bios_setting_finalize(GObject *object); + +typedef struct { + FwupdBiosSettingKind kind; + gchar *id; + gchar *name; + gchar *description; + gchar *path; + gchar *current_value; + guint64 lower_bound; + guint64 upper_bound; + guint64 scalar_increment; + gboolean read_only; + GPtrArray *possible_values; +} FwupdBiosSettingPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdBiosSetting, fwupd_bios_setting, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_bios_setting_get_instance_private(o)) + +/** + * fwupd_bios_setting_get_id + * @self: a #FwupdBiosSetting + * + * Gets the unique attribute identifier for this attribute/driver + * + * Returns: attribute ID if set otherwise NULL + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_bios_setting_get_id(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + return priv->id; +} + +/** + * fwupd_bios_setting_set_id + * @self: a #FwupdBiosSetting + * + * Sets the unique attribute identifier for this attribute + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_id(FwupdBiosSetting *self, const gchar *id) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + g_free(priv->id); + priv->id = g_strdup(id); +} + +/** + * fwupd_bios_setting_get_read_only: + * @self: a #FwupdBiosSetting + * + * Determines if a BIOS setting is read only + * + * Returns: gboolean + * + * Since: 1.8.4 + **/ +gboolean +fwupd_bios_setting_get_read_only(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), FALSE); + return priv->read_only; +} + +/** + * fwupd_bios_setting_set_read_only: + * @self: a #FwupdBiosSetting + * + * Configures whether an attribute is read only + * maximum length for string attributes. + * + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_read_only(FwupdBiosSetting *self, gboolean val) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + priv->read_only = val; +} + +/** + * fwupd_bios_setting_get_lower_bound: + * @self: a #FwupdBiosSetting + * + * Gets the lower bound for integer attributes or + * minimum length for string attributes. + * + * Returns: guint64 + * + * Since: 1.8.4 + **/ +guint64 +fwupd_bios_setting_get_lower_bound(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), 0); + return priv->lower_bound; +} + +/** + * fwupd_bios_setting_get_upper_bound: + * @self: a #FwupdBiosSetting + * + * Gets the upper bound for integer attributes or + * maximum length for string attributes. + * + * Returns: guint64 + * + * Since: 1.8.4 + **/ +guint64 +fwupd_bios_setting_get_upper_bound(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), 0); + return priv->upper_bound; +} + +/** + * fwupd_bios_setting_get_scalar_increment: + * @self: a #FwupdBiosSetting + * + * Gets the scalar increment used for integer attributes. + * + * Returns: guint64 + * + * Since: 1.8.4 + **/ +guint64 +fwupd_bios_setting_get_scalar_increment(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), 0); + return priv->scalar_increment; +} + +/** + * fwupd_bios_setting_set_upper_bound: + * @self: a #FwupdBiosSetting + * @val: a guint64 value to set bound to + * + * Sets the upper bound used for BIOS integer attributes or max + * length for string attributes. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_upper_bound(FwupdBiosSetting *self, guint64 val) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + priv->upper_bound = val; +} + +/** + * fwupd_bios_setting_set_lower_bound: + * @self: a #FwupdBiosSetting + * @val: a guint64 value to set bound to + * + * Sets the lower bound used for BIOS integer attributes or max + * length for string attributes. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_lower_bound(FwupdBiosSetting *self, guint64 val) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + priv->lower_bound = val; +} + +/** + * fwupd_bios_setting_set_scalar_increment: + * @self: a #FwupdBiosSetting + * @val: a guint64 value to set increment to + * + * Sets the scalar increment used for BIOS integer attributes. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_scalar_increment(FwupdBiosSetting *self, guint64 val) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + priv->scalar_increment = val; +} + +/** + * fwupd_bios_setting_get_kind: + * @self: a #FwupdBiosSetting + * + * Gets the BIOS setting type used by the kernel interface. + * + * Returns: the bios setting type, or %FWUPD_BIOS_SETTING_KIND_UNKNOWN if unset. + * + * Since: 1.8.4 + **/ +FwupdBiosSettingKind +fwupd_bios_setting_get_kind(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), 0); + return priv->kind; +} + +/** + * fwupd_bios_setting_set_kind: + * @self: a #FwupdBiosSetting + * @type: a bios setting type, e.g. %FWUPD_BIOS_SETTING_KIND_ENUMERATION + * + * Sets the BIOS setting type used by the kernel interface. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_kind(FwupdBiosSetting *self, FwupdBiosSettingKind type) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + priv->kind = type; +} + +/** + * fwupd_bios_setting_set_name: + * @self: a #FwupdBiosSetting + * @name: (nullable): the attribute name + * + * Sets the attribute name provided by a kernel driver. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_name(FwupdBiosSetting *self, const gchar *name) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + + /* not changed */ + if (g_strcmp0(priv->name, name) == 0) + return; + + g_free(priv->name); + priv->name = g_strdup(name); +} + +/** + * fwupd_bios_setting_set_path: + * @self: a #FwupdBiosSetting + * @path: (nullable): the path the driver providing the attribute uses + * + * Sets path to the attribute. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_path(FwupdBiosSetting *self, const gchar *path) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + + /* not changed */ + if (g_strcmp0(priv->path, path) == 0) + return; + + g_free(priv->path); + priv->path = g_strdup(path); +} + +/** + * fwupd_bios_setting_set_description: + * @self: a #FwupdBiosSetting + * @description: (nullable): the attribute description + * + * Sets the attribute description. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_description(FwupdBiosSetting *self, const gchar *description) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + + /* not changed */ + if (g_strcmp0(priv->description, description) == 0) + return; + + g_free(priv->description); + priv->description = g_strdup(description); +} + +/* determine if key is supposed to be positive */ +static gboolean +fu_bios_setting_key_is_positive(const gchar *key) +{ + if (g_strrstr(key, "enable")) + return TRUE; + if (g_strcmp0(key, "true") == 0) + return TRUE; + if (g_strcmp0(key, "1") == 0) + return TRUE; + if (g_strcmp0(key, "on") == 0) + return TRUE; + return FALSE; +} + +/* determine if key is supposed to be negative */ +static gboolean +fu_bios_setting_key_is_negative(const gchar *key) +{ + if (g_strrstr(key, "disable")) + return TRUE; + if (g_strcmp0(key, "false") == 0) + return TRUE; + if (g_strcmp0(key, "0") == 0) + return TRUE; + if (g_strcmp0(key, "off") == 0) + return TRUE; + return FALSE; +} + +/** + * fwupd_bios_setting_map_possible_value: + * @self: a #FwupdBiosSetting + * @key: the string to try to map + * @error: (nullable): optional return location for an error + * + * Attempts to map a user provided string into strings that a #FwupdBiosSetting can + * support. The following heuristics are used: + * - Ignore case sensitivity + * - Map obviously "positive" phrases into a value that turns on the #FwupdBiosSetting + * - Map obviously "negative" phrases into a value that turns off the #FwupdBiosSetting + * + * Returns: (transfer none): the possible value that maps or NULL if none if found + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_bios_setting_map_possible_value(FwupdBiosSetting *self, const gchar *key, GError **error) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + gboolean positive_key = FALSE; + gboolean negative_key = FALSE; + g_autofree gchar *lower_key = NULL; + + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + g_return_val_if_fail(priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION, NULL); + + if (priv->possible_values->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s doesn't contain any possible values", + priv->name); + return NULL; + } + + lower_key = g_utf8_strdown(key, -1); + positive_key = fu_bios_setting_key_is_positive(lower_key); + negative_key = fu_bios_setting_key_is_negative(lower_key); + for (guint i = 0; i < priv->possible_values->len; i++) { + const gchar *possible = g_ptr_array_index(priv->possible_values, i); + g_autofree gchar *lower_possible = g_utf8_strdown(possible, -1); + gboolean positive_possible; + gboolean negative_possible; + + /* perfect match */ + if (g_strcmp0(lower_possible, lower_key) == 0) + return possible; + /* fuzzy match */ + positive_possible = fu_bios_setting_key_is_positive(lower_possible); + negative_possible = fu_bios_setting_key_is_negative(lower_possible); + if ((positive_possible && positive_key) || (negative_possible && negative_key)) + return possible; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s doesn't map to any possible values for %s", + key, + priv->name); + return NULL; +} + +/** + * fwupd_bios_setting_has_possible_value: + * @self: a #FwupdBiosSetting + * @val: the possible value string + * + * Finds out if a specific possible value was added to the attribute. + * + * Returns: %TRUE if the self matches. + * + * Since: 1.8.4 + **/ +gboolean +fwupd_bios_setting_has_possible_value(FwupdBiosSetting *self, const gchar *val) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), FALSE); + g_return_val_if_fail(val != NULL, FALSE); + + if (priv->possible_values->len == 0) + return TRUE; + + for (guint i = 0; i < priv->possible_values->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->possible_values, i); + if (g_strcmp0(tmp, val) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_bios_setting_add_possible_value: + * @self: a #FwupdBiosSetting + * @possible_value: the possible + * + * Adds a possible value to the attribute. This indicates one of the values the + * kernel driver will accept from userspace. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_add_possible_value(FwupdBiosSetting *self, const gchar *possible_value) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + if (priv->possible_values->len > 0 && + fwupd_bios_setting_has_possible_value(self, possible_value)) + return; + g_ptr_array_add(priv->possible_values, g_strdup(possible_value)); +} + +/** + * fwupd_bios_setting_get_possible_values: + * @self: a #FwupdBiosSetting + * + * Find all possible values for an enumeration attribute. + * + * Returns: (transfer container) (element-type gchar*): all possible values. + * + * Since: 1.8.4 + **/ +GPtrArray * +fwupd_bios_setting_get_possible_values(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + g_return_val_if_fail(priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION, NULL); + return priv->possible_values; +} + +/** + * fwupd_bios_setting_get_name: + * @self: a #FwupdBiosSetting + * + * Gets the attribute name. + * + * Returns: the attribute name, or %NULL if unset. + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_bios_setting_get_name(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + return priv->name; +} + +/** + * fwupd_bios_setting_get_path: + * @self: a #FwupdBiosSetting + * + * Gets the path for the driver providing the attribute. + * + * Returns: (nullable): the driver, or %NULL if unfound. + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_bios_setting_get_path(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + return priv->path; +} + +/** + * fwupd_bios_setting_get_description: + * @self: a #FwupdBiosSetting + * + * Gets the attribute description which is provided by some drivers to explain + * what they change. + * + * Returns: the attribute description, or %NULL if unset. + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_bios_setting_get_description(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + return priv->description; +} + +/** + * fwupd_bios_setting_get_current_value: + * @self: a #FwupdBiosSetting + * + * Gets the string representation of the current_value stored in an attribute + * from the kernel. This value is cached; so changing it outside of fwupd may + * may put it out of sync. + * + * Returns: the current value of the attribute. + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_bios_setting_get_current_value(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + return priv->current_value; +} + +/** + * fwupd_bios_setting_set_current_value: + * @self: a #FwupdBiosSetting + * @value: The string to set an attribute to + * + * Sets the string stored in an attribute. + * This doesn't change the representation in the kernel. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_set_current_value(FwupdBiosSetting *self, const gchar *value) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->current_value, value) == 0) + return; + + g_free(priv->current_value); + priv->current_value = g_strdup(value); +} + +static gboolean +fwupd_bios_setting_trusted(FwupdBiosSetting *self, gboolean trusted) +{ + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), FALSE); + + if (trusted) + return TRUE; + if (g_strcmp0(fwupd_bios_setting_get_name(self), "pending_reboot") == 0) + return TRUE; + return FALSE; +} + +/** + * fwupd_bios_setting_to_variant: + * @self: a #FwupdBiosSetting + * @trusted: whether the caller should receive trusted values + * + * Serialize the bios setting. + * + * Returns: the serialized data, or %NULL for error. + * + * Since: 1.8.4 + **/ +GVariant * +fwupd_bios_setting_to_variant(FwupdBiosSetting *self, gboolean trusted) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_TYPE, + g_variant_new_uint64(priv->kind)); + if (priv->id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_ID, + g_variant_new_string(priv->id)); + } + if (priv->name != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string(priv->name)); + } + if (priv->path != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_FILENAME, + g_variant_new_string(priv->path)); + } + if (priv->description != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DESCRIPTION, + g_variant_new_string(priv->description)); + } + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, + g_variant_new_boolean(priv->read_only)); + if (fwupd_bios_setting_trusted(self, trusted)) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + g_variant_new_string(priv->current_value)); + } + if (priv->kind == FWUPD_BIOS_SETTING_KIND_INTEGER || + priv->kind == FWUPD_BIOS_SETTING_KIND_STRING) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND, + g_variant_new_uint64(priv->lower_bound)); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND, + g_variant_new_uint64(priv->upper_bound)); + if (priv->kind == FWUPD_BIOS_SETTING_KIND_INTEGER) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT, + g_variant_new_uint64(priv->scalar_increment)); + } + } else if (priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + if (priv->possible_values->len > 0) { + g_autofree const gchar **strv = + g_new0(const gchar *, priv->possible_values->len + 1); + for (guint i = 0; i < priv->possible_values->len; i++) + strv[i] = + (const gchar *)g_ptr_array_index(priv->possible_values, i); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES, + g_variant_new_strv(strv, -1)); + } + } + return g_variant_new("a{sv}", &builder); +} + +static void +fwupd_bios_setting_from_key_value(FwupdBiosSetting *self, const gchar *key, GVariant *value) +{ + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_TYPE) == 0) { + fwupd_bios_setting_set_kind(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_ID) == 0) { + fwupd_bios_setting_set_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_bios_setting_set_name(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FILENAME) == 0) { + fwupd_bios_setting_set_path(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE) == 0) { + fwupd_bios_setting_set_current_value(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { + fwupd_bios_setting_set_description(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES) == 0) { + g_autofree const gchar **strv = g_variant_get_strv(value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_bios_setting_add_possible_value(self, strv[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND) == 0) { + fwupd_bios_setting_set_lower_bound(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND) == 0) { + fwupd_bios_setting_set_upper_bound(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT) == 0) { + fwupd_bios_setting_set_scalar_increment(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY) == 0) { + fwupd_bios_setting_set_read_only(self, g_variant_get_boolean(value)); + return; + } +} + +/** + * fwupd_bios_setting_from_json: + * @self: a #FwupdBiosSetting + * @json_node: a JSON node + * @error: (nullable): optional return location for an error + * + * Loads a fwupd bios setting from a JSON node. + * + * Returns: %TRUE for success + * + * Since: 1.8.4 + **/ +gboolean +fwupd_bios_setting_from_json(FwupdBiosSetting *self, JsonNode *json_node, GError **error) +{ +#if JSON_CHECK_VERSION(1, 6, 0) + JsonObject *obj; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + obj = json_node_get_object(json_node); + + fwupd_bios_setting_set_kind( + self, + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_BIOS_SETTING_TYPE, 0)); + fwupd_bios_setting_set_id( + self, + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_ID, + NULL)); + + fwupd_bios_setting_set_name( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_NAME, NULL)); + fwupd_bios_setting_set_description( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_DESCRIPTION, NULL)); + fwupd_bios_setting_set_path( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_FILENAME, NULL)); + fwupd_bios_setting_set_current_value( + self, + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + NULL)); + if (json_object_has_member(obj, FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES)) { + JsonArray *array = + json_object_get_array_member(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES); + for (guint i = 0; i < json_array_get_length(array); i++) { + const gchar *tmp = json_array_get_string_element(array, i); + fwupd_bios_setting_add_possible_value(self, tmp); + } + } + fwupd_bios_setting_set_lower_bound( + self, + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND, + 0)); + fwupd_bios_setting_set_upper_bound( + self, + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND, + 0)); + fwupd_bios_setting_set_scalar_increment( + self, + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT, + 0)); + fwupd_bios_setting_set_read_only( + self, + json_object_get_boolean_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, + FALSE)); + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "json-glib version too old"); + return FALSE; +#endif +} + +/** + * fwupd_bios_setting_to_json: + * @self: a #FwupdBiosSetting + * @builder: a JSON builder + * + * Adds a fwupd bios setting to a JSON builder. + * + * Since: 1.8.4 + **/ +void +fwupd_bios_setting_to_json(FwupdBiosSetting *self, JsonBuilder *builder) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + g_return_if_fail(builder != NULL); + + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_FILENAME, priv->path); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_BIOS_SETTING_ID, priv->id); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + priv->current_value); + fwupd_common_json_add_boolean(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, + priv->read_only); + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_BIOS_SETTING_TYPE, priv->kind); + if (priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + if (priv->possible_values->len > 0) { + json_builder_set_member_name(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->possible_values->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->possible_values, i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } + } + if (priv->kind == FWUPD_BIOS_SETTING_KIND_INTEGER || + priv->kind == FWUPD_BIOS_SETTING_KIND_STRING) { + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND, + priv->lower_bound); + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND, + priv->upper_bound); + if (priv->kind == FWUPD_BIOS_SETTING_KIND_INTEGER) { + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT, + priv->scalar_increment); + } + } +} + +/** + * fwupd_bios_setting_to_string: + * @self: a #FwupdBiosSetting + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 1.8.4 + **/ +gchar * +fwupd_bios_setting_to_string(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + GString *str; + + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + + str = g_string_new(NULL); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_BIOS_SETTING_ID, priv->id); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_FILENAME, priv->path); + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_BIOS_SETTING_TYPE, priv->kind); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, priv->current_value); + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, + priv->read_only ? "True" : "False"); + + if (priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + for (guint i = 0; i < priv->possible_values->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->possible_values, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES, tmp); + } + } + if (priv->kind == FWUPD_BIOS_SETTING_KIND_INTEGER || + priv->kind == FWUPD_BIOS_SETTING_KIND_STRING) { + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND, priv->lower_bound); + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND, priv->upper_bound); + if (priv->kind == FWUPD_BIOS_SETTING_KIND_INTEGER) { + fwupd_pad_kv_int(str, + FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT, + priv->scalar_increment); + } + } + + return g_string_free(str, FALSE); +} + +static void +fwupd_bios_setting_class_init(FwupdBiosSettingClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fwupd_bios_setting_finalize; +} + +static void +fwupd_bios_setting_init(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + priv->possible_values = g_ptr_array_new_with_free_func(g_free); +} + +static void +fwupd_bios_setting_finalize(GObject *object) +{ + FwupdBiosSetting *self = FWUPD_BIOS_SETTING(object); + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + + g_free(priv->current_value); + g_free(priv->id); + g_free(priv->name); + g_free(priv->description); + g_free(priv->path); + g_ptr_array_unref(priv->possible_values); + + G_OBJECT_CLASS(fwupd_bios_setting_parent_class)->finalize(object); +} + +static void +fwupd_bios_setting_set_from_variant_iter(FwupdBiosSetting *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + fwupd_bios_setting_from_key_value(self, key, value); + g_variant_unref(value); + } +} + +/** + * fwupd_bios_setting_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new bios setting using serialized data. + * + * Returns: (transfer full): a new #FwupdBiosSetting, or %NULL if @value was invalid. + * + * Since: 1.8.4 + **/ +FwupdBiosSetting * +fwupd_bios_setting_from_variant(GVariant *value) +{ + FwupdBiosSetting *rel = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + g_return_val_if_fail(value != NULL, NULL); + + type_string = g_variant_get_type_string(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + rel = g_object_new(FWUPD_TYPE_BIOS_SETTING, NULL); + g_variant_get(value, "(a{sv})", &iter); + fwupd_bios_setting_set_from_variant_iter(rel, iter); + } else if (g_strcmp0(type_string, "a{sv}") == 0) { + rel = g_object_new(FWUPD_TYPE_BIOS_SETTING, NULL); + g_variant_get(value, "a{sv}", &iter); + fwupd_bios_setting_set_from_variant_iter(rel, iter); + } else { + g_warning("type %s not known", type_string); + } + return rel; +} + +/** + * fwupd_bios_setting_array_from_variant: + * @value: (not nullable): the serialized data + * + * Creates an array of new bios settings using serialized data. + * + * Returns: (transfer container) (element-type FwupdBiosSetting): attributes, + * or %NULL if @value was invalid. + * + * Since: 1.8.4 + **/ +GPtrArray * +fwupd_bios_setting_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++) { + FwupdBiosSetting *rel; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value(untuple, i); + rel = fwupd_bios_setting_from_variant(data); + if (rel == NULL) + continue; + g_ptr_array_add(array, rel); + } + return array; +} + +/** + * fwupd_bios_setting_new: + * @name: (nullable): the attribute name + * @path: (nullable): the path the driver providing this attribute uses + * + * Creates a new bios setting. + * + * Returns: a new #FwupdBiosSetting. + * + * Since: 1.8.4 + **/ +FwupdBiosSetting * +fwupd_bios_setting_new(const gchar *name, const gchar *path) +{ + FwupdBiosSetting *self; + + self = g_object_new(FWUPD_TYPE_BIOS_SETTING, NULL); + if (name != NULL) + fwupd_bios_setting_set_name(self, name); + if (path != NULL) + fwupd_bios_setting_set_path(self, path); + + return FWUPD_BIOS_SETTING(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-bios-setting.h b/fwupd-1.8.6/libfwupd/fwupd-bios-setting.h new file mode 100644 index 0000000000000000000000000000000000000000..37c0483c90ccc582a852c05e35267053499c6881 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-bios-setting.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_BIOS_SETTING (fwupd_bios_setting_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdBiosSetting, fwupd_bios_setting, FWUPD, BIOS_SETTING, GObject) + +struct _FwupdBiosSettingClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +/* special attributes */ +#define FWUPD_BIOS_SETTING_PENDING_REBOOT "pending_reboot" +#define FWUPD_BIOS_SETTING_RESET_BIOS "reset_bios" +#define FWUPD_BIOS_SETTING_DEBUG_CMD "debug_cmd" + +/** + * FwupdBiosSettingKind: + * @FWUPD_BIOS_SETTING_KIND_UNKNOWN: BIOS setting type is unknown + * @FWUPD_BIOS_SETTING_KIND_ENUMERATION: BIOS setting that has enumerated possible + *values + * @FWUPD_BIOS_SETTING_KIND_INTEGER: BIOS setting that is an integer + * @FWUPD_BIOS_SETTING_KIND_STRING: BIOS setting that accepts a string + * + * The type of BIOS setting. + **/ +typedef enum { + FWUPD_BIOS_SETTING_KIND_UNKNOWN = 0, /* Since: 1.8.4 */ + FWUPD_BIOS_SETTING_KIND_ENUMERATION = 1, /* Since: 1.8.4 */ + FWUPD_BIOS_SETTING_KIND_INTEGER = 2, /* Since: 1.8.4 */ + FWUPD_BIOS_SETTING_KIND_STRING = 3, /* Since: 1.8.4 */ + /*< private >*/ + FWUPD_BIOS_SETTING_KIND_LAST = 4 /* perhaps increased in the future */ +} FwupdBiosSettingKind; + +FwupdBiosSetting * +fwupd_bios_setting_new(const gchar *name, const gchar *path); +gchar * +fwupd_bios_setting_to_string(FwupdBiosSetting *self); + +gboolean +fwupd_bios_setting_get_read_only(FwupdBiosSetting *self); +void +fwupd_bios_setting_set_read_only(FwupdBiosSetting *self, gboolean val); + +guint64 +fwupd_bios_setting_get_upper_bound(FwupdBiosSetting *self); +guint64 +fwupd_bios_setting_get_lower_bound(FwupdBiosSetting *self); +guint64 +fwupd_bios_setting_get_scalar_increment(FwupdBiosSetting *self); + +void +fwupd_bios_setting_set_upper_bound(FwupdBiosSetting *self, guint64 val); +void +fwupd_bios_setting_set_lower_bound(FwupdBiosSetting *self, guint64 val); +void +fwupd_bios_setting_set_scalar_increment(FwupdBiosSetting *self, guint64 val); + +void +fwupd_bios_setting_set_kind(FwupdBiosSetting *self, FwupdBiosSettingKind type); +void +fwupd_bios_setting_set_name(FwupdBiosSetting *self, const gchar *name); +void +fwupd_bios_setting_set_path(FwupdBiosSetting *self, const gchar *path); +void +fwupd_bios_setting_set_description(FwupdBiosSetting *self, const gchar *description); + +FwupdBiosSettingKind +fwupd_bios_setting_get_kind(FwupdBiosSetting *self); +const gchar * +fwupd_bios_setting_get_name(FwupdBiosSetting *self); +const gchar * +fwupd_bios_setting_get_path(FwupdBiosSetting *self); +const gchar * +fwupd_bios_setting_get_description(FwupdBiosSetting *self); +const gchar * +fwupd_bios_setting_map_possible_value(FwupdBiosSetting *self, const gchar *key, GError **error); +gboolean +fwupd_bios_setting_has_possible_value(FwupdBiosSetting *self, const gchar *val); +void +fwupd_bios_setting_add_possible_value(FwupdBiosSetting *self, const gchar *possible_value); +GPtrArray * +fwupd_bios_setting_get_possible_values(FwupdBiosSetting *self); + +FwupdBiosSetting * +fwupd_bios_setting_from_variant(GVariant *value); +GPtrArray * +fwupd_bios_setting_array_from_variant(GVariant *value); + +const gchar * +fwupd_bios_setting_get_current_value(FwupdBiosSetting *self); +void +fwupd_bios_setting_set_current_value(FwupdBiosSetting *self, const gchar *value); + +const gchar * +fwupd_bios_setting_get_id(FwupdBiosSetting *self); +void +fwupd_bios_setting_set_id(FwupdBiosSetting *self, const gchar *id); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-client-private.h b/fwupd-1.8.6/libfwupd/fwupd-client-private.h new file mode 100644 index 0000000000000000000000000000000000000000..2022faf850be7d86b289073f071e04a6fadb96fb --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-client-private.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-client.h" + +#ifdef HAVE_GIO_UNIX +#include +#endif + +void +fwupd_client_download_bytes2_async(FwupdClient *self, + GPtrArray *urls, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); + +#ifdef HAVE_GIO_UNIX +void +fwupd_client_get_details_stream_async(FwupdClient *self, + GUnixInputStream *istr, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +void +fwupd_client_install_stream_async(FwupdClient *self, + const gchar *device_id, + GUnixInputStream *istr, + const gchar *filename_hint, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +void +fwupd_client_update_metadata_stream_async(FwupdClient *self, + const gchar *remote_id, + GUnixInputStream *istr, + GUnixInputStream *istr_sig, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +#endif diff --git a/fwupd-1.8.6/libfwupd/fwupd-client-sync.c b/fwupd-1.8.6/libfwupd/fwupd-client-sync.c new file mode 100644 index 0000000000000000000000000000000000000000..a1505a47e73200342ed388a4d19793110fa14538 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-client-sync.c @@ -0,0 +1,2419 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_GIO_UNIX +#include +#endif + +#include "fwupd-client-private.h" +#include "fwupd-client-sync.h" +#include "fwupd-client.h" +#include "fwupd-common-private.h" +#include "fwupd-error.h" + +typedef struct { + gboolean ret; + gchar *str; + GError *error; + GPtrArray *array; + GMainContext *context; + GMainLoop *loop; + GVariant *val; + GHashTable *hash; + GBytes *bytes; + FwupdDevice *device; +} FwupdClientHelper; + +static void +fwupd_client_helper_free(FwupdClientHelper *helper) +{ + if (helper->val != NULL) + g_variant_unref(helper->val); + if (helper->error != NULL) + g_error_free(helper->error); + if (helper->array != NULL) + g_ptr_array_unref(helper->array); + if (helper->hash != NULL) + g_hash_table_unref(helper->hash); + if (helper->bytes != NULL) + g_bytes_unref(helper->bytes); + if (helper->device != NULL) + g_object_unref(helper->device); + g_free(helper->str); + g_main_loop_unref(helper->loop); + g_main_context_unref(helper->context); + g_main_context_pop_thread_default(helper->context); + g_free(helper); +} + +static FwupdClientHelper * +fwupd_client_helper_new(FwupdClient *self) +{ + FwupdClientHelper *helper; + helper = g_new0(FwupdClientHelper, 1); + helper->context = fwupd_client_get_main_context(self); + helper->loop = g_main_loop_new(helper->context, FALSE); + g_main_context_push_thread_default(helper->context); + return helper; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdClientHelper, fwupd_client_helper_free) +#pragma clang diagnostic pop + +static void +fwupd_client_connect_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_connect_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_connect: (skip) + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Sets up the client ready for use. Most other methods call this + * for you, and do you only need to call this if you are just watching + * the client. + * + * Returns: %TRUE for success + * + * Since: 0.7.1 + **/ +gboolean +fwupd_client_connect(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_connect_async(self, cancellable, fwupd_client_connect_cb, helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_devices_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_get_devices_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_devices: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the devices registered with the daemon. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 0.9.2 + **/ +GPtrArray * +fwupd_client_get_devices(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_devices_async(self, cancellable, fwupd_client_get_devices_cb, helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_plugins_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_get_plugins_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_plugins: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the plugins being used the daemon. + * + * Returns: (element-type FwupdPlugin) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_plugins(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_plugins_async(self, cancellable, fwupd_client_get_plugins_cb, helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_history_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_get_history_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_history: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the history. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.0.4 + **/ +GPtrArray * +fwupd_client_get_history(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_history_async(self, cancellable, fwupd_client_get_history_cb, helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_releases_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_get_releases_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_releases: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the releases for a specific device + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_client_get_releases(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_releases_async(self, + device_id, + cancellable, + fwupd_client_get_releases_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_downgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_downgrades_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_downgrades: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the downgrades for a specific device. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 0.9.8 + **/ +GPtrArray * +fwupd_client_get_downgrades(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_downgrades_async(self, + device_id, + cancellable, + fwupd_client_get_downgrades_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_upgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_get_upgrades_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_upgrades: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the upgrades for a specific device. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 0.9.8 + **/ +GPtrArray * +fwupd_client_get_upgrades(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_upgrades_async(self, + device_id, + cancellable, + fwupd_client_get_upgrades_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_details_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_details_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_details_bytes: + * @self: a #FwupdClient + * @bytes: the firmware archive + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets details about a specific firmware file. + * + * Returns: (transfer container) (element-type FwupdDevice): an array of results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_details_bytes(FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(bytes != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_details_bytes_async(self, + bytes, + cancellable, + fwupd_client_get_details_bytes_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +#ifdef HAVE_GIO_UNIX +static void +fwupd_client_get_details_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_details_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} +#endif + +/** + * fwupd_client_get_details: + * @self: a #FwupdClient + * @filename: the firmware filename, e.g. `firmware.cab` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets details about a specific firmware file. + * + * Returns: (transfer container) (element-type FwupdDevice): an array of results + * + * Since: 1.0.0 + **/ +GPtrArray * +fwupd_client_get_details(FwupdClient *self, + const gchar *filename, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + istr = fwupd_unix_input_stream_from_fn(filename, error); + if (istr == NULL) + return NULL; + helper = fwupd_client_helper_new(self); + fwupd_client_get_details_stream_async(self, + istr, + cancellable, + fwupd_client_get_details_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Get Details only supported on Linux"); + return NULL; +#endif +} + +static void +fwupd_client_verify_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_verify_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_verify: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Verify a specific device. + * + * Returns: %TRUE for verification success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_verify(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(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(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_verify_async(self, device_id, cancellable, fwupd_client_verify_cb, helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_verify_update_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_verify_update_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_verify_update: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Update the verification record for a specific device. + * + * Returns: %TRUE for verification success + * + * Since: 0.8.0 + **/ +gboolean +fwupd_client_verify_update(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(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(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_verify_update_async(self, + device_id, + cancellable, + fwupd_client_verify_update_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_unlock_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_unlock_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_unlock: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Unlocks a specific device so firmware can be read or wrote. + * + * Returns: %TRUE for success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_unlock(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(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(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_unlock_async(self, device_id, cancellable, fwupd_client_unlock_cb, helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} +static void +fwupd_client_modify_config_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_modify_config_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_modify_config + * @self: a #FwupdClient + * @key: config key, e.g. `DisabledPlugins` + * @value: config value, e.g. `*` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * 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 *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(value != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_modify_config_async(self, + key, + value, + cancellable, + fwupd_client_modify_config_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_activate_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_activate_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_activate: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @device_id: a device + * @error: (nullable): optional return location for an error + * + * 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 *self, + GCancellable *cancellable, + const gchar *device_id, /* yes, this is the wrong way around :/ */ + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), 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(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_activate_async(self, device_id, cancellable, fwupd_client_activate_cb, helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_clear_results_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_clear_results_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_clear_results: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Clears the results for a specific device. + * + * Returns: %TRUE for success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_clear_results(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(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(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_clear_results_async(self, + device_id, + cancellable, + fwupd_client_clear_results_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_results_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->device = fwupd_client_get_results_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_results: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets the results of a previous firmware update for a specific device. + * + * Returns: (transfer full): a device, or %NULL for failure + * + * Since: 0.7.0 + **/ +FwupdDevice * +fwupd_client_get_results(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_results_async(self, + device_id, + cancellable, + fwupd_client_get_results_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->device == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->device); +} + +static void +fwupd_client_modify_bios_setting_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_modify_bios_setting_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_modify_bios_setting + * @self: a #FwupdClient + * @settings: (transfer container): BIOS settings + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Modifies a BIOS setting using kernel API. + * The daemon will only respond to this request with proper permissions. + * + * Returns: %TRUE for success + * + * Since: 1.8.4 + **/ +gboolean +fwupd_client_modify_bios_setting(FwupdClient *self, + GHashTable *settings, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(settings != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_modify_bios_setting_async(self, + settings, + cancellable, + fwupd_client_modify_bios_setting_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_bios_settings_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_bios_settings_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_bios_settings: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the BIOS settings from the daemon. + * + * Returns: (element-type FwupdBiosSetting) (transfer container): attributes + * + * Since: 1.8.4 + **/ +GPtrArray * +fwupd_client_get_bios_settings(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_bios_settings_async(self, + cancellable, + fwupd_client_get_bios_settings_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_host_security_attrs_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_host_security_attrs_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_host_security_attrs: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the host security attributes from the daemon. + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): attributes + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_host_security_attrs(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_host_security_attrs_async(self, + cancellable, + fwupd_client_get_host_security_attrs_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_host_security_events_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_host_security_events_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_host_security_events: + * @self: a #FwupdClient + * @limit: maximum number of events, or 0 for no limit + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the host security events from the daemon. + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): attributes + * + * Since: 1.7.1 + **/ +GPtrArray * +fwupd_client_get_host_security_events(FwupdClient *self, + guint limit, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_host_security_events_async(self, + limit, + cancellable, + fwupd_client_get_host_security_events_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void +fwupd_client_get_device_by_id_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->device = + fwupd_client_get_device_by_id_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_device_by_id: + * @self: a #FwupdClient + * @device_id: the device ID, e.g. `usb:00:01:03:03` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets a device by its device ID. + * + * Returns: (transfer full): a device or %NULL + * + * Since: 0.9.3 + **/ +FwupdDevice * +fwupd_client_get_device_by_id(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_device_by_id_async(self, + device_id, + cancellable, + fwupd_client_get_device_by_id_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->device == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->device); +} + +static void +fwupd_client_get_devices_by_guid_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_devices_by_guid_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_devices_by_guid: + * @self: a #FwupdClient + * @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets any devices that provide a specific GUID. An error is returned if no + * devices contains this GUID. + * + * Returns: (element-type FwupdDevice) (transfer container): devices or %NULL + * + * Since: 1.4.1 + **/ +GPtrArray * +fwupd_client_get_devices_by_guid(FwupdClient *self, + const gchar *guid, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_devices_by_guid_async(self, + guid, + cancellable, + fwupd_client_get_devices_by_guid_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +#ifdef HAVE_GIO_UNIX +static void +fwupd_client_install_fd_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_install_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} +#endif + +/** + * fwupd_client_install: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @filename: the filename to install + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Install a file onto a specific device. + * + * Returns: %TRUE for success + * + * Since: 0.7.0 + **/ +gboolean +fwupd_client_install(FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(device_id != NULL, FALSE); + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_fn(filename, error); + if (istr == NULL) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_install_stream_async(self, + device_id, + istr, + filename, + install_flags, + cancellable, + fwupd_client_install_fd_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Install CAB only supported on Linux"); + return FALSE; +#endif +} + +static void +fwupd_client_install_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_install_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_install_bytes: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @bytes: cabinet archive + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Install firmware onto a specific device. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_install_bytes(FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(device_id != NULL, FALSE); + g_return_val_if_fail(bytes != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_install_bytes_async(self, + device_id, + bytes, + install_flags, + cancellable, + fwupd_client_install_bytes_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} +static void +fwupd_client_install_release_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_install_release_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_install_release2: + * @self: a #FwupdClient + * @device: a device + * @release: a release + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @download_flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Installs a new release on a device, downloading the firmware if required. + * + * Returns: %TRUE for success + * + * Since: 1.5.6 + **/ +gboolean +fwupd_client_install_release2(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + FwupdClientDownloadFlags download_flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(FWUPD_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FWUPD_IS_RELEASE(release), FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_install_release2_async(self, + device, + release, + install_flags, + download_flags, + cancellable, + fwupd_client_install_release_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +/** + * fwupd_client_install_release: + * @self: a #FwupdClient + * @device: a device + * @release: a release + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Installs a new release on a device, downloading the firmware if required. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + * Deprecated: 1.5.6 + **/ +gboolean +fwupd_client_install_release(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) +{ + return fwupd_client_install_release2(self, + device, + release, + install_flags, + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + error); +} + +#ifdef HAVE_GIO_UNIX +static void +fwupd_client_update_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_update_metadata_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} +#endif + +/** + * fwupd_client_update_metadata: + * @self: a #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @metadata_fn: the XML metadata filename + * @signature_fn: the GPG signature file + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Updates the metadata. This allows a session process to download the metadata + * and metadata signing file to be passed into the daemon to be checked and + * parsed. + * + * The @remote_id allows the firmware to be tagged so that the remote can be + * matched when the firmware is downloaded. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fwupd_client_update_metadata(FwupdClient *self, + const gchar *remote_id, + const gchar *metadata_fn, + const gchar *signature_fn, + GCancellable *cancellable, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(GUnixInputStream) istr_sig = NULL; + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(metadata_fn != NULL, FALSE); + g_return_val_if_fail(signature_fn != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + istr = fwupd_unix_input_stream_from_fn(metadata_fn, error); + if (istr == NULL) + return FALSE; + istr_sig = fwupd_unix_input_stream_from_fn(signature_fn, error); + if (istr_sig == NULL) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_update_metadata_stream_async(self, + remote_id, + istr, + istr_sig, + cancellable, + fwupd_client_update_metadata_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Update metadata only supported on Linux"); + return FALSE; +#endif +} + +static void +fwupd_client_update_metadata_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_update_metadata_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_update_metadata_bytes: + * @self: a #FwupdClient + * @remote_id: remote ID, e.g. `lvfs-testing` + * @metadata: XML metadata data + * @signature: signature data + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Updates the metadata. This allows a session process to download the metadata + * and metadata signing file to be passed into the daemon to be checked and + * parsed. + * + * The @remote_id allows the firmware to be tagged so that the remote can be + * matched when the firmware is downloaded. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_update_metadata_bytes(FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(metadata != NULL, FALSE); + g_return_val_if_fail(signature != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_update_metadata_bytes_async(self, + remote_id, + metadata, + signature, + cancellable, + fwupd_client_update_metadata_bytes_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_refresh_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_refresh_remote_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_refresh_remote: + * @self: a #FwupdClient + * @remote: a #FwupdRemote + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Refreshes a remote by downloading new metadata. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_refresh_remote(FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(FWUPD_IS_REMOTE(remote), FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_refresh_remote_async(self, + remote, + cancellable, + fwupd_client_refresh_remote_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_modify_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_modify_remote_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_modify_remote: + * @self: a #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @key: the key, e.g. `Enabled` + * @value: the key, e.g. `true` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Modifies a system remote in a specific way. + * + * NOTE: User authentication may be required to complete this action. + * + * Returns: %TRUE for success + * + * Since: 0.9.8 + **/ +gboolean +fwupd_client_modify_remote(FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(value != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_modify_remote_async(self, + remote_id, + key, + value, + cancellable, + fwupd_client_modify_remote_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_report_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->hash = + fwupd_client_get_report_metadata_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_report_metadata: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the report metadata from the daemon. + * + * Returns: (transfer container): attributes + * + * Since: 1.5.0 + **/ +GHashTable * +fwupd_client_get_report_metadata(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_report_metadata_async(self, + cancellable, + fwupd_client_get_report_metadata_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->hash == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->hash); +} + +static void +fwupd_client_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_modify_device_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_modify_device: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @key: (not nullable): the key, e.g. `Flags` + * @value: (not nullable): the key, e.g. `reported` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Modifies a device in a specific way. Not all properties on the #FwupdDevice + * are settable by the client, and some may have other restrictions on @value. + * + * NOTE: User authentication may be required to complete this action. + * + * Returns: %TRUE for success + * + * Since: 1.0.4 + **/ +gboolean +fwupd_client_modify_device(FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(device_id != NULL, FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(value != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_modify_device_async(self, + device_id, + key, + value, + cancellable, + fwupd_client_modify_device_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_remotes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_get_remotes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_remotes: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets the list of remotes that have been configured for the system. + * + * Returns: (element-type FwupdRemote) (transfer container): list of remotes, or %NULL + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_client_get_remotes(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_remotes_async(self, cancellable, fwupd_client_get_remotes_cb, helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static FwupdRemote * +fwupd_client_get_remote_by_id_noref(GPtrArray *remotes, const gchar *remote_id) +{ + 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; + } + return NULL; +} + +/** + * fwupd_client_get_remote_by_id: + * @self: a #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets a specific remote that has been configured for the system. + * + * Returns: (transfer full): a #FwupdRemote, or %NULL if not found + * + * Since: 0.9.3 + **/ +FwupdRemote * +fwupd_client_get_remote_by_id(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GError **error) +{ + FwupdRemote *remote; + g_autoptr(GPtrArray) remotes = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(remote_id != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find remote in list */ + remotes = fwupd_client_get_remotes(self, cancellable, error); + if (remotes == NULL) + return NULL; + remote = fwupd_client_get_remote_by_id_noref(remotes, remote_id); + if (remote == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No remote '%s' found in search paths", + remote_id); + return NULL; + } + + /* success */ + return g_object_ref(remote); +} + +static void +fwupd_client_get_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_approved_firmware_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_approved_firmware: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets the list of approved firmware. + * + * Returns: (transfer full): checksums, or %NULL for error + * + * Since: 1.2.6 + **/ +gchar ** +fwupd_client_get_approved_firmware(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + gchar **argv; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_approved_firmware_async(self, + cancellable, + fwupd_client_get_approved_firmware_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + argv = g_new0(gchar *, helper->array->len + 1); + for (guint i = 0; i < helper->array->len; i++) { + const gchar *tmp = g_ptr_array_index(helper->array, i); + argv[i] = g_strdup(tmp); + } + return argv; +} + +static void +fwupd_client_set_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_set_approved_firmware_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_set_approved_firmware: + * @self: a #FwupdClient + * @checksums: (not nullable): Array of checksums + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Sets the list of approved firmware. + * + * Returns: %TRUE for success + * + * Since: 1.2.6 + **/ +gboolean +fwupd_client_set_approved_firmware(FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(checksums != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* convert */ + for (guint i = 0; checksums[i] != NULL; i++) + g_ptr_array_add(array, g_strdup(checksums[i])); + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_set_approved_firmware_async(self, + array, + cancellable, + fwupd_client_set_approved_firmware_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_get_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = + fwupd_client_get_blocked_firmware_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_get_blocked_firmware: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets the list of blocked firmware. + * + * Returns: (transfer full): checksums, or %NULL for error + * + * Since: 1.4.6 + **/ +gchar ** +fwupd_client_get_blocked_firmware(FwupdClient *self, GCancellable *cancellable, GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + gchar **argv; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_get_blocked_firmware_async(self, + cancellable, + fwupd_client_get_blocked_firmware_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + argv = g_new0(gchar *, helper->array->len + 1); + for (guint i = 0; i < helper->array->len; i++) { + const gchar *tmp = g_ptr_array_index(helper->array, i); + argv[i] = g_strdup(tmp); + } + return argv; +} + +static void +fwupd_client_set_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_set_blocked_firmware_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_set_blocked_firmware: + * @self: a #FwupdClient + * @checksums: Array of checksums + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Sets the list of approved firmware. + * + * Returns: %TRUE for success + * + * Since: 1.4.6 + **/ +gboolean +fwupd_client_set_blocked_firmware(FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(checksums != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + for (guint i = 0; checksums[i] != NULL; i++) + g_ptr_array_add(array, g_strdup(checksums[i])); + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_set_blocked_firmware_async(self, + array, + cancellable, + fwupd_client_set_blocked_firmware_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_set_feature_flags_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = + fwupd_client_set_feature_flags_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_set_feature_flags: + * @self: a #FwupdClient + * @feature_flags: feature flags, e.g. %FWUPD_FEATURE_FLAG_UPDATE_TEXT + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Sets the features the client supports. This allows firmware to depend on + * specific front-end features, for instance showing the user an image on + * how to detach the hardware. + * + * Clients can call this none or multiple times. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_set_feature_flags(FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_set_feature_flags_async(self, + feature_flags, + cancellable, + fwupd_client_set_feature_flags_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_self_sign_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->str = fwupd_client_self_sign_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_self_sign: + * @self: a #FwupdClient + * @value: (not nullable): a string to sign, typically a JSON blob + * @flags: signing flags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Signs the data using the client self-signed certificate. + * + * Returns: a signature, or %NULL for failure + * + * Since: 1.2.6 + **/ +gchar * +fwupd_client_self_sign(FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(value != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_self_sign_async(self, + value, + flags, + cancellable, + fwupd_client_self_sign_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->str == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->str); +} + +static void +fwupd_client_download_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->bytes = + fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_download_bytes: + * @self: a #FwupdClient + * @url: (not nullable): the remote URL + * @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Downloads data from a remote server. The [method@Client.set_user_agent] function + * should be called before this method is used. + * + * Returns: (transfer full): downloaded data, or %NULL for error + * + * Since: 1.4.5 + **/ +GBytes * +fwupd_client_download_bytes(FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(url != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + g_return_val_if_fail(fwupd_client_get_user_agent(self) != NULL, NULL); + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_download_bytes_async(self, + url, + flags, + cancellable, + fwupd_client_download_bytes_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->bytes == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->bytes); +} + +/** + * fwupd_client_download_file: + * @self: a #FwupdClient + * @url: (not nullable): the remote URL + * @file: (not nullable): a file + * @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Downloads data from a remote server. The [method@Client.set_user_agent] function + * should be called before this method is used. + * + * Returns: %TRUE if the file was written + * + * Since: 1.5.2 + **/ +gboolean +fwupd_client_download_file(FwupdClient *self, + const gchar *url, + GFile *file, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GError **error) +{ + gssize size; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GOutputStream) ostream = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(url != NULL, FALSE); + g_return_val_if_fail(G_IS_FILE(file), FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(fwupd_client_get_user_agent(self) != NULL, FALSE); + + /* download then write */ + bytes = fwupd_client_download_bytes(self, url, flags, cancellable, error); + if (bytes == NULL) + return FALSE; + ostream = + G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + if (ostream == NULL) + return FALSE; + size = g_output_stream_write_bytes(ostream, bytes, NULL, error); + if (size < 0) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fwupd_client_upload_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->bytes = fwupd_client_upload_bytes_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_upload_bytes: + * @self: a #FwupdClient + * @url: (not nullable): the remote URL + * @payload: (not nullable): payload string + * @signature: (nullable): signature string + * @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Uploads data to a remote server. The [method@Client.set_user_agent] function + * should be called before this method is used. + * + * Returns: (transfer full): response data, or %NULL for error + * + * Since: 1.4.5 + **/ +GBytes * +fwupd_client_upload_bytes(FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(url != NULL, NULL); + g_return_val_if_fail(payload != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_upload_bytes_async(self, + url, + payload, + signature, + flags, + cancellable, + fwupd_client_upload_bytes_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->bytes == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->bytes); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-client-sync.h b/fwupd-1.8.6/libfwupd/fwupd-client-sync.h new file mode 100644 index 0000000000000000000000000000000000000000..bc38cf1e6b690a45962cbd0b8033361628d2ce35 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-client-sync.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-client.h" + +G_BEGIN_DECLS + +gboolean +fwupd_client_connect(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_devices(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_plugins(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_history(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_releases(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_downgrades(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_upgrades(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_details(FwupdClient *self, + const gchar *filename, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_details_bytes(FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_verify(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_verify_update(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_unlock(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_modify_config(FwupdClient *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_activate(FwupdClient *self, + GCancellable *cancellable, + const gchar *device_id, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_clear_results(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +FwupdDevice * +fwupd_client_get_results(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_modify_bios_setting(FwupdClient *self, + GHashTable *settings, + GCancellable *cancellable, + GError **error); +GPtrArray * +fwupd_client_get_bios_settings(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_host_security_attrs(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_host_security_events(FwupdClient *self, + guint limit, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +FwupdDevice * +fwupd_client_get_device_by_id(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_devices_by_guid(FwupdClient *self, + const gchar *guid, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_install(FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_install_bytes(FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_install_release(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GError **error) G_DEPRECATED_FOR(fwupd_client_install_release2); +gboolean +fwupd_client_install_release2(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + FwupdClientDownloadFlags download_flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_update_metadata(FwupdClient *self, + const gchar *remote_id, + const gchar *metadata_fn, + const gchar *signature_fn, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_update_metadata_bytes(FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_refresh_remote(FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_modify_remote(FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_modify_device(FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GHashTable * +fwupd_client_get_report_metadata(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fwupd_client_get_remotes(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +FwupdRemote * +fwupd_client_get_remote_by_id(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gchar ** +fwupd_client_get_approved_firmware(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_set_approved_firmware(FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gchar ** +fwupd_client_get_blocked_firmware(FwupdClient *self, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_set_blocked_firmware(FwupdClient *self, + gchar **checksums, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gchar * +fwupd_client_self_sign(FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_set_feature_flags(FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fwupd_client_download_bytes(FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_download_file(FwupdClient *self, + const gchar *url, + GFile *file, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fwupd_client_upload_bytes(FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-client.c b/fwupd-1.8.6/libfwupd/fwupd-client.c new file mode 100644 index 0000000000000000000000000000000000000000..82ffaabe4aa7ae87e6d92615bbd31d4869319ba5 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-client.c @@ -0,0 +1,5960 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#ifdef HAVE_LIBCURL +#include +#endif +#ifdef HAVE_GIO_UNIX +#include +#endif + +#include +#include +#include +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-client-private.h" +#include "fwupd-client-sync.h" +#include "fwupd-common-private.h" +#include "fwupd-deprecated.h" +#include "fwupd-device-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" +#include "fwupd-plugin-private.h" +#include "fwupd-release-private.h" +#include "fwupd-remote-private.h" +#include "fwupd-request-private.h" +#include "fwupd-security-attr-private.h" + +static void +fwupd_client_fixup_dbus_error(GError *error); + +typedef GObject *(*FwupdClientObjectNewFunc)(void); + +#define FWUPD_CLIENT_DBUS_PROXY_TIMEOUT 180000 /* ms */ + +/** + * FwupdClient: + * + * Allow client code to call the daemon methods. + * + * See also: [class@FwupdDevice] + */ + +static void +fwupd_client_finalize(GObject *object); + +typedef struct { + GMainContext *main_ctx; + FwupdStatus status; + gboolean tainted; + gboolean interactive; + guint percentage; + guint32 battery_level; + guint32 battery_threshold; + GMutex idle_mutex; /* for @idle_id and @idle_sources */ + guint idle_id; + GPtrArray *idle_sources; /* element-type FwupdClientContextHelper */ + gchar *daemon_version; + gchar *host_bkc; + gchar *host_product; + gchar *host_vendor; + gchar *host_machine_id; + gchar *host_security_id; + gboolean only_trusted; + GMutex proxy_mutex; /* for @proxy */ + GDBusProxy *proxy; + GProxyResolver *proxy_resolver; + gchar *user_agent; + GHashTable *hints; /* str:str */ +#ifdef SOUP_SESSION_COMPAT + GObject *soup_session; + GModule *soup_module; /* we leak this */ +#endif +} FwupdClientPrivate; + +#ifdef HAVE_LIBCURL +typedef struct { + GPtrArray *urls; + CURL *curl; + curl_mime *mime; + struct curl_slist *headers; +} FwupdCurlHelper; +#endif + +enum { + SIGNAL_CHANGED, + SIGNAL_STATUS_CHANGED, + SIGNAL_DEVICE_ADDED, + SIGNAL_DEVICE_REMOVED, + SIGNAL_DEVICE_CHANGED, + SIGNAL_DEVICE_REQUEST, + SIGNAL_LAST +}; + +enum { + PROP_0, + PROP_STATUS, + PROP_PERCENTAGE, + PROP_DAEMON_VERSION, + PROP_TAINTED, + PROP_SOUP_SESSION, /* compat ABI, do not use! */ + PROP_HOST_PRODUCT, + PROP_HOST_VENDOR, + PROP_HOST_MACHINE_ID, + PROP_HOST_SECURITY_ID, + PROP_HOST_BKC, + PROP_INTERACTIVE, + PROP_ONLY_TRUSTED, + PROP_BATTERY_LEVEL, + PROP_BATTERY_THRESHOLD, + PROP_LAST +}; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdClient, fwupd_client, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_client_get_instance_private(o)) + +#ifdef HAVE_LIBCURL_7_62_0 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup) +#endif + +#ifdef HAVE_LIBCURL +static void +fwupd_client_curl_helper_free(FwupdCurlHelper *helper) +{ + if (helper->curl != NULL) + curl_easy_cleanup(helper->curl); + if (helper->mime != NULL) + curl_mime_free(helper->mime); + if (helper->headers != NULL) + curl_slist_free_all(helper->headers); + if (helper->urls != NULL) + g_ptr_array_unref(helper->urls); + g_free(helper); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdCurlHelper, fwupd_client_curl_helper_free) +#endif + +typedef struct { + FwupdClient *self; + gchar *property_name; + guint signal_id; + GObject *payload; +} FwupdClientContextHelper; + +static void +fwupd_client_context_helper_free(FwupdClientContextHelper *helper) +{ + g_clear_object(&helper->payload); + g_object_unref(helper->self); + g_free(helper->property_name); + g_free(helper); +} + +/* always executed in the main context given by priv->main_ctx */ +static void +fwupd_client_context_object_notify(FwupdClient *self, const gchar *property_name) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(g_main_context_is_owner(priv->main_ctx)); + + /* property */ + g_object_notify(G_OBJECT(self), property_name); + + /* legacy signal name */ + if (g_strcmp0(property_name, "status") == 0) + g_signal_emit(self, signals[SIGNAL_STATUS_CHANGED], 0, priv->status); +} + +/* emits all pending context helpers in the correct GMainContext */ +static gboolean +fwupd_client_context_idle_cb(gpointer user_data) +{ + FwupdClient *self = FWUPD_CLIENT(user_data); + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->idle_mutex); + + g_assert(locker != NULL); + + for (guint i = 0; i < priv->idle_sources->len; i++) { + FwupdClientContextHelper *helper = g_ptr_array_index(priv->idle_sources, i); + + /* property */ + if (helper->property_name != NULL) + fwupd_client_context_object_notify(self, helper->property_name); + + /* payload signal */ + if (helper->signal_id != 0 && helper->payload != NULL) + g_signal_emit(self, signals[helper->signal_id], 0, helper->payload); + } + + /* all done */ + g_ptr_array_set_size(priv->idle_sources, 0); + priv->idle_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fwupd_client_context_helper(FwupdClient *self, FwupdClientContextHelper *helper) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->idle_mutex); + + g_assert(locker != NULL); + + /* no source already attached to the context */ + if (priv->idle_id == 0) { + g_autoptr(GSource) source = g_idle_source_new(); + g_source_set_callback(source, fwupd_client_context_idle_cb, self, NULL); + priv->idle_id = g_source_attach(g_steal_pointer(&source), priv->main_ctx); + } + + /* run in the correct GMainContext and thread */ + g_ptr_array_add(priv->idle_sources, helper); +} + +/* run callback in the correct thread */ +static void +fwupd_client_object_notify(FwupdClient *self, const gchar *property_name) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + FwupdClientContextHelper *helper = NULL; + + /* shortcut */ + if (g_main_context_is_owner(priv->main_ctx)) { + fwupd_client_context_object_notify(self, property_name); + return; + } + + /* run in the correct GMainContext and thread */ + helper = g_new0(FwupdClientContextHelper, 1); + helper->self = g_object_ref(self); + helper->property_name = g_strdup(property_name); + fwupd_client_context_helper(self, helper); +} + +/* run callback in the correct thread */ +static void +fwupd_client_signal_emit_object(FwupdClient *self, guint signal_id, GObject *payload) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + FwupdClientContextHelper *helper = NULL; + + /* shortcut */ + if (g_main_context_is_owner(priv->main_ctx)) { + g_signal_emit(self, signals[signal_id], 0, payload); + return; + } + + /* run in the correct GMainContext and thread */ + helper = g_new0(FwupdClientContextHelper, 1); + helper->self = g_object_ref(self); + helper->signal_id = signal_id; + helper->payload = g_object_ref(payload); + fwupd_client_context_helper(self, helper); +} + +static void +fwupd_client_set_host_vendor(FwupdClient *self, const gchar *host_vendor) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->host_vendor, host_vendor) == 0) + return; + + g_free(priv->host_vendor); + priv->host_vendor = g_strdup(host_vendor); + fwupd_client_object_notify(self, "host-vendor"); +} + +static void +fwupd_client_set_host_product(FwupdClient *self, const gchar *host_product) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->host_product, host_product) == 0) + return; + + g_free(priv->host_product); + priv->host_product = g_strdup(host_product); + fwupd_client_object_notify(self, "host-product"); +} + +static void +fwupd_client_set_host_machine_id(FwupdClient *self, const gchar *host_machine_id) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->host_machine_id, host_machine_id) == 0) + return; + + g_free(priv->host_machine_id); + priv->host_machine_id = g_strdup(host_machine_id); + fwupd_client_object_notify(self, "host-machine-id"); +} + +static void +fwupd_client_set_host_security_id(FwupdClient *self, const gchar *host_security_id) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->host_security_id, host_security_id) == 0) + return; + + g_free(priv->host_security_id); + priv->host_security_id = g_strdup(host_security_id); + fwupd_client_object_notify(self, "host-security-id"); +} + +static void +fwupd_client_set_daemon_version(FwupdClient *self, const gchar *daemon_version) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->daemon_version, daemon_version) == 0) + return; + + g_free(priv->daemon_version); + priv->daemon_version = g_strdup(daemon_version); + fwupd_client_object_notify(self, "daemon-version"); +} + +static void +fwupd_client_set_host_bkc(FwupdClient *self, const gchar *host_bkc) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + /* emulate a D-Bus maybe type */ + if (g_strcmp0(host_bkc, "") == 0) + host_bkc = NULL; + + /* not changed */ + if (g_strcmp0(priv->host_bkc, host_bkc) == 0) + return; + + g_free(priv->host_bkc); + priv->host_bkc = g_strdup(host_bkc); + fwupd_client_object_notify(self, "host-bkc"); +} + +static void +fwupd_client_set_status(FwupdClient *self, FwupdStatus status) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + if (priv->status == status) + return; + priv->status = status; + g_debug("Emitting ::status-changed() [%s]", fwupd_status_to_string(priv->status)); + fwupd_client_object_notify(self, "status"); +} + +static void +fwupd_client_set_percentage(FwupdClient *self, guint percentage) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + if (priv->percentage == percentage) + return; + priv->percentage = percentage; + fwupd_client_object_notify(self, "percentage"); +} + +static void +fwupd_client_set_battery_level(FwupdClient *self, guint32 battery_level) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + if (priv->battery_level == battery_level) + return; + priv->battery_level = battery_level; + g_object_notify(G_OBJECT(self), "battery-level"); +} + +static void +fwupd_client_set_battery_threshold(FwupdClient *self, guint32 battery_threshold) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + if (priv->battery_threshold == battery_threshold) + return; + priv->battery_threshold = battery_threshold; + g_object_notify(G_OBJECT(self), "battery-threshold"); +} + +static void +fwupd_client_properties_changed_cb(GDBusProxy *proxy, + GVariant *changed_properties, + const GStrv invalidated_properties, + FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GVariantDict) dict = NULL; + + /* print to the console */ + dict = g_variant_dict_new(changed_properties); + if (g_variant_dict_contains(dict, "Status")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "Status"); + if (val != NULL) + fwupd_client_set_status(self, g_variant_get_uint32(val)); + } + 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); + fwupd_client_object_notify(self, "tainted"); + } + } + if (g_variant_dict_contains(dict, "Interactive")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "Interactive"); + if (val != NULL) { + priv->interactive = g_variant_get_boolean(val); + fwupd_client_object_notify(self, "interactive"); + } + } + if (g_variant_dict_contains(dict, "Percentage")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "Percentage"); + if (val != NULL) + fwupd_client_set_percentage(self, g_variant_get_uint32(val)); + } + if (g_variant_dict_contains(dict, FWUPD_RESULT_KEY_BATTERY_LEVEL)) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, FWUPD_RESULT_KEY_BATTERY_LEVEL); + if (val != NULL) + fwupd_client_set_battery_level(self, g_variant_get_uint32(val)); + } + if (g_variant_dict_contains(dict, FWUPD_RESULT_KEY_BATTERY_THRESHOLD)) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, FWUPD_RESULT_KEY_BATTERY_THRESHOLD); + if (val != NULL) + fwupd_client_set_battery_threshold(self, g_variant_get_uint32(val)); + } + if (g_variant_dict_contains(dict, "DaemonVersion")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "DaemonVersion"); + if (val != NULL) + fwupd_client_set_daemon_version(self, g_variant_get_string(val, NULL)); + } + if (g_variant_dict_contains(dict, "HostBkc")) { + g_autoptr(GVariant) val = g_dbus_proxy_get_cached_property(proxy, "HostBkc"); + if (val != NULL) + fwupd_client_set_host_bkc(self, g_variant_get_string(val, NULL)); + } + if (g_variant_dict_contains(dict, "HostVendor")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "HostVendor"); + if (val != NULL) + fwupd_client_set_host_vendor(self, g_variant_get_string(val, NULL)); + } + if (g_variant_dict_contains(dict, "HostProduct")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "HostProduct"); + if (val != NULL) + fwupd_client_set_host_product(self, g_variant_get_string(val, NULL)); + } + if (g_variant_dict_contains(dict, "HostMachineId")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "HostMachineId"); + if (val != NULL) + fwupd_client_set_host_machine_id(self, g_variant_get_string(val, NULL)); + } + if (g_variant_dict_contains(dict, "HostSecurityId")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "HostSecurityId"); + if (val != NULL) + fwupd_client_set_host_security_id(self, g_variant_get_string(val, NULL)); + } + if (g_variant_dict_contains(dict, "OnlyTrusted")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy, "OnlyTrusted"); + if (val != NULL) { + priv->only_trusted = g_variant_get_boolean(val); + fwupd_client_object_notify(self, "only-trusted"); + } + } +} + +static void +fwupd_client_signal_cb(GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + FwupdClient *self) +{ + g_autoptr(FwupdDevice) dev = NULL; + if (g_strcmp0(signal_name, "Changed") == 0) { + g_debug("Emitting ::changed()"); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0); + return; + } + if (g_strcmp0(signal_name, "DeviceAdded") == 0) { + dev = fwupd_device_from_variant(parameters); + g_debug("Emitting ::device-added(%s)", fwupd_device_get_id(dev)); + fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_ADDED, G_OBJECT(dev)); + return; + } + if (g_strcmp0(signal_name, "DeviceRemoved") == 0) { + dev = fwupd_device_from_variant(parameters); + g_debug("Emitting ::device-removed(%s)", fwupd_device_get_id(dev)); + fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_REMOVED, G_OBJECT(dev)); + return; + } + if (g_strcmp0(signal_name, "DeviceChanged") == 0) { + dev = fwupd_device_from_variant(parameters); + g_debug("Emitting ::device-changed(%s)", fwupd_device_get_id(dev)); + fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_CHANGED, G_OBJECT(dev)); + return; + } + if (g_strcmp0(signal_name, "DeviceRequest") == 0) { + g_autoptr(FwupdRequest) req = fwupd_request_from_variant(parameters); + g_debug("Emitting ::device-request(%s)", fwupd_request_get_id(req)); + fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_REQUEST, G_OBJECT(req)); + return; + } + g_debug("Unknown signal name '%s' from %s", signal_name, sender_name); +} + +/** + * fwupd_client_get_main_context: + * @self: a #FwupdClient + * + * Gets the internal #GMainContext to use for synchronous methods. + * By default the value is set a new #GMainContext. + * + * Returns: (transfer full): the main context + * + * Since: 1.5.3 + **/ +GMainContext * +fwupd_client_get_main_context(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + if (priv->main_ctx != NULL) + return g_main_context_ref(priv->main_ctx); + return g_main_context_new(); +} + +/** + * fwupd_client_set_main_context: + * @self: a #FwupdClient + * @main_ctx: (nullable): the global default main context to use + * + * Sets the internal main context to use for returning progress signals. + * + * Since: 1.5.3 + **/ +void +fwupd_client_set_main_context(FwupdClient *self, GMainContext *main_ctx) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_CLIENT(self)); + if (main_ctx == priv->main_ctx) + return; + g_clear_pointer(&priv->main_ctx, g_main_context_unref); + if (main_ctx != NULL) + priv->main_ctx = g_main_context_ref(main_ctx); +} + +/** + * fwupd_client_ensure_networking: + * @self: a #FwupdClient + * @error: (nullable): optional return location for an error + * + * Sets up the client networking support ready for use. Most other download and + * upload methods call this automatically, and do you only need to call this if + * the session is being used outside the [class@FwupdClient]. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_client_ensure_networking(FwupdClient *self, GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* check the user agent is sane */ + if (priv->user_agent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "user agent unset"); + return FALSE; + } + if (g_strstr_len(priv->user_agent, -1, "fwupd/") == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "user agent unsuitable; fwupd version required"); + return FALSE; + } +#ifdef SOUP_SESSION_COMPAT + if (priv->soup_session != NULL) { + g_object_set(priv->soup_session, "user-agent", priv->user_agent, NULL); + } +#endif + return TRUE; +} + +#ifdef HAVE_LIBCURL +static int +fwupd_client_progress_callback_cb(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow) +{ + FwupdClient *self = FWUPD_CLIENT(clientp); + + /* calculate percentage */ + if (dltotal > 0 && dlnow >= 0 && dlnow <= dltotal) { + guint percentage = (guint)((100 * dlnow) / dltotal); + g_debug("download progress: %u%%", percentage); + fwupd_client_set_percentage(self, percentage); + } else if (ultotal > 0 && ulnow >= 0 && ulnow <= ultotal) { + guint percentage = (guint)((100 * ulnow) / ultotal); + g_debug("upload progress: %u%%", percentage); + fwupd_client_set_percentage(self, percentage); + } + + return 0; +} + +static void +fwupd_client_curl_helper_set_proxy(FwupdClient *self, FwupdCurlHelper *helper, const gchar *url) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_auto(GStrv) proxies = NULL; + g_autoptr(GError) error_local = NULL; + + proxies = g_proxy_resolver_lookup(priv->proxy_resolver, url, NULL, &error_local); + if (proxies == NULL) { + g_warning("failed to lookup proxy for %s: %s", url, error_local->message); + return; + } + if (g_strcmp0(proxies[0], "direct://") != 0) + (void)curl_easy_setopt(helper->curl, CURLOPT_PROXY, proxies[0]); +} + +static FwupdCurlHelper * +fwupd_client_curl_new(FwupdClient *self, GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(FwupdCurlHelper) helper = g_new0(FwupdCurlHelper, 1); + + /* check the user agent is sane */ + if (!fwupd_client_ensure_networking(self, error)) + return NULL; + + /* create the session */ + helper->curl = curl_easy_init(); + if (helper->curl == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to setup networking"); + return NULL; + } + if (g_getenv("FWUPD_CURL_VERBOSE") != NULL) + (void)curl_easy_setopt(helper->curl, CURLOPT_VERBOSE, 1L); + (void)curl_easy_setopt(helper->curl, + CURLOPT_XFERINFOFUNCTION, + fwupd_client_progress_callback_cb); + (void)curl_easy_setopt(helper->curl, CURLOPT_XFERINFODATA, self); + (void)curl_easy_setopt(helper->curl, CURLOPT_USERAGENT, priv->user_agent); + (void)curl_easy_setopt(helper->curl, CURLOPT_CONNECTTIMEOUT, 60L); + (void)curl_easy_setopt(helper->curl, CURLOPT_NOPROGRESS, 0L); + (void)curl_easy_setopt(helper->curl, CURLOPT_FOLLOWLOCATION, 1L); + (void)curl_easy_setopt(helper->curl, CURLOPT_MAXREDIRS, 5L); +#if CURL_AT_LEAST_VERSION(7, 71, 0) + (void)curl_easy_setopt(helper->curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); +#endif + + /* relax the SSL checks for broken corporate proxies */ + if (g_getenv("DISABLE_SSL_STRICT") != NULL) + (void)curl_easy_setopt(helper->curl, CURLOPT_SSL_VERIFYPEER, 0L); + + /* this disables the double-compression of the firmware.xml.gz file */ + (void)curl_easy_setopt(helper->curl, CURLOPT_HTTP_CONTENT_DECODING, 0L); + return g_steal_pointer(&helper); +} +#endif + +static void +fwupd_client_set_hints_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + /* new libfwupd and old daemon, just swallow the error */ + if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + g_debug("ignoring %s", error->message); + g_task_return_boolean(task, TRUE); + return; + } + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +static void +fwupd_client_connect_get_proxy_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + GVariantBuilder builder; + GHashTableIter iter; + gpointer key, value; + FwupdClient *self = g_task_get_source_object(task); + FwupdClientPrivate *priv = GET_PRIVATE(self); + GCancellable *cancellable = g_task_get_cancellable(task); + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + g_autoptr(GVariant) val2 = NULL; + g_autoptr(GVariant) val3 = NULL; + g_autoptr(GVariant) val4 = NULL; + g_autoptr(GVariant) val5 = NULL; + g_autoptr(GVariant) val6 = NULL; + g_autoptr(GVariant) val7 = NULL; + g_autoptr(GVariant) val8 = NULL; + g_autoptr(GVariant) val9 = NULL; + g_autoptr(GVariant) val10 = NULL; + g_autoptr(GMutexLocker) locker = NULL; + + proxy = g_dbus_proxy_new_finish(res, &error); + if (proxy == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* another thread did this for us */ + locker = g_mutex_locker_new(&priv->proxy_mutex); + if (locker == NULL || priv->proxy != NULL) { + g_task_return_boolean(task, TRUE); + return; + } + priv->proxy = g_steal_pointer(&proxy); + + /* connect signals, etc. */ + g_signal_connect(G_DBUS_PROXY(priv->proxy), + "g-properties-changed", + G_CALLBACK(fwupd_client_properties_changed_cb), + self); + g_signal_connect(G_DBUS_PROXY(priv->proxy), + "g-signal", + G_CALLBACK(fwupd_client_signal_cb), + self); + val = g_dbus_proxy_get_cached_property(priv->proxy, "DaemonVersion"); + if (val != NULL) + fwupd_client_set_daemon_version(self, 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); + val3 = g_dbus_proxy_get_cached_property(priv->proxy, "Status"); + if (val3 != NULL) + fwupd_client_set_status(self, g_variant_get_uint32(val3)); + val4 = g_dbus_proxy_get_cached_property(priv->proxy, "Interactive"); + if (val4 != NULL) + priv->interactive = g_variant_get_boolean(val4); + val5 = g_dbus_proxy_get_cached_property(priv->proxy, "HostProduct"); + if (val5 != NULL) + fwupd_client_set_host_product(self, g_variant_get_string(val5, NULL)); + val10 = g_dbus_proxy_get_cached_property(priv->proxy, "HostVendor"); + if (val10 != NULL) + fwupd_client_set_host_vendor(self, g_variant_get_string(val10, NULL)); + val6 = g_dbus_proxy_get_cached_property(priv->proxy, "HostMachineId"); + if (val6 != NULL) + fwupd_client_set_host_machine_id(self, g_variant_get_string(val6, NULL)); + val7 = g_dbus_proxy_get_cached_property(priv->proxy, "HostSecurityId"); + if (val7 != NULL) + fwupd_client_set_host_security_id(self, g_variant_get_string(val7, NULL)); + val8 = g_dbus_proxy_get_cached_property(priv->proxy, "HostBkc"); + if (val8 != NULL) + fwupd_client_set_host_bkc(self, g_variant_get_string(val8, NULL)); + val9 = g_dbus_proxy_get_cached_property(priv->proxy, "OnlyTrusted"); + if (val9 != NULL) + priv->only_trusted = g_variant_get_boolean(val9); + + /* build client hints */ + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_hash_table_iter_init(&iter, priv->hints); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (value == NULL) + continue; + g_variant_builder_add(&builder, "{ss}", (const gchar *)key, (const gchar *)value); + } + + /* only supported on fwupd >= 1.7.1 */ + g_dbus_proxy_call(priv->proxy, + "SetHints", + g_variant_new("(a{ss})", &builder), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_set_hints_cb, + g_steal_pointer(&task)); +} + +static void +fwupd_client_connect_get_connection_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + GCancellable *cancellable = g_task_get_cancellable(task); + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GError) error = NULL; + + connection = g_dbus_connection_new_for_address_finish(res, &error); + if (connection == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + g_dbus_proxy_new(connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + NULL, /* bus_name */ + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + cancellable, + fwupd_client_connect_get_proxy_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_connect_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Sets up the client ready for use. This is probably the first method you call + * when wanting to use libfwupd in an asynchronous manner. + * + * Other methods such as [method@FwupdClient.get_devices_async] should only be called + * after [method@FwupdClient.connect_finish] has been called without an error. + * + * Since: 1.5.0 + **/ +void +fwupd_client_connect_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + const gchar *socket_filename = g_getenv("FWUPD_DBUS_SOCKET"); + g_autofree gchar *socket_address = NULL; + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->proxy_mutex); + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + + g_assert(locker != NULL); + + /* nothing to do */ + if (priv->proxy != NULL) { + g_task_return_boolean(task, TRUE); + return; + } + + /* convert from filename to address, if required */ + if (socket_filename != NULL) { + if (g_strrstr(socket_filename, "=") == NULL) { + socket_address = g_strdup_printf("unix:path=%s", socket_filename); + } else { + socket_address = g_strdup(socket_filename); + } + } else { +#ifdef _WIN32 + socket_address = g_strdup(FWUPD_DBUS_P2P_SOCKET_ADDRESS); +#endif + } + + /* use peer-to-peer only if the env variable is set */ + if (socket_address != NULL) { + g_dbus_connection_new_for_address(socket_address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, + cancellable, + fwupd_client_connect_get_connection_cb, + g_steal_pointer(&task)); + return; + } + + /* typical case */ + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + FWUPD_DBUS_SERVICE, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + cancellable, + fwupd_client_connect_get_proxy_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_connect_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@Client.connect_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_connect_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +/** + * fwupd_client_disconnect: (skip) + * @self: a #FwupdClient + * @error: (nullable): optional return location for an error + * + * Tears down client after use. You only need to call this method if you are: + * + * - connecting to the daemon in one thread and finalizing the client in another one + * - to change the `FWUPD_DBUS_SOCKET` for a different peer-to-peer connection + * - to add or change connection hints as specified by [method@FwupdClient.add_hint]. + * + * Returns: %TRUE for success + * + * Since: 1.8.0 + **/ +gboolean +fwupd_client_disconnect(FwupdClient *self, GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->proxy_mutex); + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_assert(locker != NULL); + + /* sanity check */ + if (priv->proxy == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "not connected"); + return FALSE; + } + g_signal_handlers_disconnect_by_data(priv->proxy, self); + g_clear_object(&priv->proxy); + + /* success */ + return TRUE; +} + +static void +fwupd_client_fixup_dbus_error(GError *error) +{ + g_autofree gchar *name = NULL; + + g_return_if_fail(error != NULL); + + /* is a remote error? */ + if (!g_dbus_error_is_remote_error(error)) + return; + + /* parse the remote error */ + name = g_dbus_error_get_remote_error(error); + if (g_str_has_prefix(name, FWUPD_DBUS_INTERFACE)) { + error->domain = FWUPD_ERROR; + error->code = fwupd_error_from_string(name); + } else if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + error->domain = FWUPD_ERROR; + error->code = FWUPD_ERROR_NOT_SUPPORTED; + } else if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) { + error->domain = FWUPD_ERROR; + error->code = FWUPD_ERROR_NOT_SUPPORTED; + } else { + error->domain = FWUPD_ERROR; + error->code = FWUPD_ERROR_INTERNAL; + } + g_dbus_error_strip_remote_error(error); +} + +static void +fwupd_client_get_host_security_attrs_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_security_attr_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_host_security_attrs_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the host security attributes from the daemon. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_host_security_attrs_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetHostSecurityAttrs", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_host_security_attrs_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_host_security_attrs_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_host_security_attrs_async]. + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): attributes + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_host_security_attrs_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_modify_bios_setting_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_modify_bios_setting_async: + * @self: a #FwupdClient + * @settings: (transfer container): BIOS settings + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Modifies a BIOS setting using kernel API. + * The daemon will only respond to this request with proper permissions. + * + * Since: 1.8.4 + **/ +void +fwupd_client_modify_bios_setting_async(FwupdClient *self, + GHashTable *settings, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + GHashTableIter iter; + gpointer key, value; + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(settings != NULL); + g_return_if_fail(g_hash_table_size(settings) > 0); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_hash_table_iter_init(&iter, settings); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (value == NULL) + continue; + g_variant_builder_add(&builder, "{ss}", (const gchar *)key, (const gchar *)value); + } + g_dbus_proxy_call(priv->proxy, + "SetBiosSettings", + g_variant_new("(a{ss})", &builder), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_modify_bios_setting_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_modify_bios_setting_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.modify_bios_setting_async]. + * + * Returns: %TRUE for success + * + * Since: 1.8.4 + **/ +gboolean +fwupd_client_modify_bios_setting_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_get_bios_settings_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_bios_setting_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_bios_settings_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the host security attributes from the daemon. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.8.4 + **/ +void +fwupd_client_get_bios_settings_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetBiosSettings", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_bios_settings_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_bios_settings_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_bios_settings_async]. + * + * Returns: (element-type FwupdBiosSetting) (transfer container): attributes + * + * Since: 1.8.4 + **/ +GPtrArray * +fwupd_client_get_bios_settings_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_host_security_events_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_security_attr_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_host_security_events_async: + * @self: a #FwupdClient + * @limit: maximum number of events, or 0 for no limit + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the host security events from the daemon. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.7.1 + **/ +void +fwupd_client_get_host_security_events_async(FwupdClient *self, + guint limit, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetHostSecurityEvents", + g_variant_new("(u)", limit), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_host_security_events_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_host_security_events_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_host_security_events_async]. + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): attributes + * + * Since: 1.7.1 + **/ +GPtrArray * +fwupd_client_get_host_security_events_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static GHashTable * +fwupd_report_metadata_hash_from_variant(GVariant *value) +{ + GHashTable *hash; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + 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 = NULL; + const gchar *key = NULL; + const gchar *val = NULL; + data = g_variant_get_child_value(untuple, i); + g_variant_get(data, "{&s&s}", &key, &val); + g_hash_table_insert(hash, g_strdup(key), g_strdup(val)); + } + return hash; +} + +static void +fwupd_client_get_report_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_report_metadata_hash_from_variant(val), + (GDestroyNotify)g_hash_table_unref); +} + +/** + * fwupd_client_get_report_metadata_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the report metadata from the daemon. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_report_metadata_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetReportMetadata", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_report_metadata_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_report_metadata_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_report_metadata_async]. + * + * Returns: (transfer container): attributes + * + * Since: 1.5.0 + **/ +GHashTable * +fwupd_client_get_report_metadata_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_devices_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_device_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_devices_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the devices registered with the daemon. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_devices_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetDevices", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_devices_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_devices_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_devices_async]. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_devices_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_plugins_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_plugin_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_plugins_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the plugins being used by the daemon. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_plugins_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetPlugins", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_plugins_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_plugins_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_plugins_async]. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_plugins_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_history_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_device_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_history_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the history. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_history_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetHistory", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_history_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_history_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_history_async]. + * + * Returns: (element-type FwupdDevice) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_history_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_device_by_id_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdDevice *device_result = NULL; + gsize device_id_len; + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + const gchar *device_id = g_task_get_task_data(task); + + devices = fwupd_client_get_devices_finish(FWUPD_CLIENT(source), res, &error); + if (devices == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* support abbreviated hashes (client side) */ + device_id_len = strlen(device_id); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + if (strncmp(fwupd_device_get_id(dev), device_id, device_id_len) == 0) { + if (device_result != NULL) { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "more than one matching ID prefix '%s'", + device_id); + return; + } + device_result = dev; + } + } + + /* one result */ + if (device_result != NULL) { + g_task_return_pointer(task, + g_object_ref(device_result), + (GDestroyNotify)g_object_unref); + return; + } + + /* failed */ + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to find %s", + device_id); +} + +/** + * fwupd_client_get_device_by_id_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets a device by it's device ID. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_device_by_id_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_task_set_task_data(task, g_strdup(device_id), g_free); + fwupd_client_get_devices_async(self, + cancellable, + fwupd_client_get_device_by_id_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_device_by_id_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_device_by_id_async]. + * + * Returns: (transfer full): a device, or %NULL for failure + * + * Since: 1.5.0 + **/ +FwupdDevice * +fwupd_client_get_device_by_id_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_devices_by_guid_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_tmp = NULL; + const gchar *guid = g_task_get_task_data(task); + + /* get all the devices */ + devices_tmp = fwupd_client_get_devices_finish(FWUPD_CLIENT(source), res, &error); + if (devices_tmp == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* find the devices by GUID (client side) */ + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < devices_tmp->len; i++) { + FwupdDevice *dev_tmp = g_ptr_array_index(devices_tmp, i); + if (fwupd_device_has_guid(dev_tmp, guid)) + g_ptr_array_add(devices, g_object_ref(dev_tmp)); + } + + /* nothing */ + if (devices->len == 0) { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to find any device providing %s", + guid); + return; + } + + /* success */ + g_task_return_pointer(task, g_steal_pointer(&devices), (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_devices_by_guid_async: + * @self: a #FwupdClient + * @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63` + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets any devices that provide a specific GUID. An error is returned if no + * devices contains this GUID. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_devices_by_guid_async(FwupdClient *self, + const gchar *guid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(guid != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_task_set_task_data(task, g_strdup(guid), g_free); + fwupd_client_get_devices_async(self, + cancellable, + fwupd_client_get_devices_by_guid_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_devices_by_guid_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_devices_by_guid_async]. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_devices_by_guid_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_releases_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_release_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_releases_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the releases for a specific device + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_releases_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetReleases", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_releases_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_releases_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_releases_async]. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_releases_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_downgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_release_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_downgrades_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the downgrades for a specific device. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_downgrades_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetDowngrades", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_downgrades_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_downgrades_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_downgrades_async]. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_downgrades_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_upgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_release_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_upgrades_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the upgrades for a specific device. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_upgrades_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetUpgrades", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_upgrades_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_upgrades_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_upgrades_async]. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_upgrades_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_modify_config_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_modify_config_async: + * @self: a #FwupdClient + * @key: config key, e.g. `DisabledPlugins` + * @value: config value, e.g. `*` + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Modifies a daemon config option. + * The daemon will only respond to this request with proper permissions. + * + * Since: 1.5.0 + **/ +void +fwupd_client_modify_config_async(FwupdClient *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "ModifyConfig", + g_variant_new("(ss)", key, value), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_modify_config_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_modify_config_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.modify_config_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_modify_config_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_activate_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_activate_async: + * @self: a #FwupdClient + * @device_id: a device + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * 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. + * + * Since: 1.5.0 + **/ +void +fwupd_client_activate_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "Activate", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_activate_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_activate_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.activate_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_activate_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_verify_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_verify_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Verify a specific device. + * + * Since: 1.5.0 + **/ +void +fwupd_client_verify_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "Verify", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_verify_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_verify_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.verify_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_verify_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_verify_update_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_verify_update_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Update the verification record for a specific device. + * + * Since: 1.5.0 + **/ +void +fwupd_client_verify_update_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "VerifyUpdate", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_verify_update_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_verify_update_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.verify_update_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_verify_update_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_unlock_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_unlock_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Unlocks a specific device so firmware can be read or wrote. + * + * Since: 1.5.0 + **/ +void +fwupd_client_unlock_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "Unlock", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_unlock_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_unlock_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.unlock_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_unlock_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_clear_results_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_clear_results_async: + * @self: a #FwupdClient + * @device_id: a device + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Clears the results for a specific device. + * + * Since: 1.5.0 + **/ +void +fwupd_client_clear_results_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "ClearResults", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_clear_results_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_clear_results_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.clear_results_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_clear_results_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_get_results_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_device_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_results_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets the results of a previous firmware update for a specific device. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_results_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetResults", + g_variant_new("(s)", device_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_results_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_results_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_results_async]. + * + * Returns: (transfer full): a device, or %NULL for failure + * + * Since: 1.5.0 + **/ +FwupdDevice * +fwupd_client_get_results_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +#ifdef HAVE_GIO_UNIX + +static void +fwupd_client_install_stream_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GDBusMessage) msg = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + + msg = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source), + res, + &error); + if (msg == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + if (g_dbus_message_to_gerror(msg, &error)) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +void +fwupd_client_install_stream_async(FwupdClient *self, + const gchar *device_id, + GUnixInputStream *istr, + const gchar *filename_hint, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + g_autoptr(GDBusMessage) request = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + + /* set options */ + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, "{sv}", "reason", g_variant_new_string("user-action")); + if (filename_hint != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "filename", + g_variant_new_string(filename_hint)); + } + if (install_flags & FWUPD_INSTALL_FLAG_OFFLINE) { + g_variant_builder_add(&builder, "{sv}", "offline", g_variant_new_boolean(TRUE)); + } + if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + g_variant_builder_add(&builder, "{sv}", "allow-older", g_variant_new_boolean(TRUE)); + } + if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + g_variant_builder_add(&builder, + "{sv}", + "allow-reinstall", + g_variant_new_boolean(TRUE)); + } + if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) { + g_variant_builder_add(&builder, + "{sv}", + "allow-branch-switch", + g_variant_new_boolean(TRUE)); + } + if (install_flags & FWUPD_INSTALL_FLAG_FORCE) { + g_variant_builder_add(&builder, "{sv}", "force", g_variant_new_boolean(TRUE)); + } + if (install_flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) { + g_variant_builder_add(&builder, + "{sv}", + "ignore-power", + g_variant_new_boolean(TRUE)); + } + if (install_flags & FWUPD_INSTALL_FLAG_NO_HISTORY) { + g_variant_builder_add(&builder, "{sv}", "no-history", g_variant_new_boolean(TRUE)); + } + + /* set out of band file descriptor */ + fd_list = g_unix_fd_list_new(); + g_unix_fd_list_append(fd_list, g_unix_input_stream_get_fd(istr), NULL); + request = g_dbus_message_new_method_call(FWUPD_DBUS_SERVICE, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "Install"); + g_dbus_message_set_unix_fd_list(request, fd_list); + + /* call into daemon */ + g_dbus_message_set_body( + request, + g_variant_new("(sha{sv})", device_id, g_unix_input_stream_get_fd(istr), &builder)); + g_dbus_connection_send_message_with_reply(g_dbus_proxy_get_connection(priv->proxy), + request, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + fwupd_client_install_stream_cb, + g_steal_pointer(&task)); +} +#endif + +/** + * fwupd_client_install_bytes_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @bytes: cabinet archive + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Install firmware onto a specific device. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + **/ +void +fwupd_client_install_bytes_async(FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ +#ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error = NULL; + g_autoptr(GUnixInputStream) istr = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_bytes(bytes, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* call into daemon */ + fwupd_client_install_stream_async(self, + device_id, + istr, + NULL, + install_flags, + cancellable, + callback, + callback_data); +#else + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Install CAB only supported on Linux"); +#endif +} + +/** + * fwupd_client_install_bytes_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.install_bytes_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_install_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +/** + * fwupd_client_install_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @filename: the filename to install + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Install firmware onto a specific device. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + **/ +void +fwupd_client_install_async(FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ +#ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error = NULL; + g_autoptr(GUnixInputStream) istr = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(filename != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_fn(filename, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* call into daemon */ + fwupd_client_install_stream_async(self, + device_id, + istr, + NULL, + install_flags, + cancellable, + callback, + callback_data); +#else + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Install CAB async only supported on Linux"); +#endif +} + +/** + * fwupd_client_install_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.install_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_install_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +typedef struct { + FwupdDevice *device; + FwupdRelease *release; + FwupdInstallFlags install_flags; + FwupdClientDownloadFlags download_flags; +} FwupdClientInstallReleaseData; + +static void +fwupd_client_install_release_data_free(FwupdClientInstallReleaseData *data) +{ + g_object_unref(data->device); + g_object_unref(data->release); + g_free(data); +} + +static void +fwupd_client_install_release_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + + if (!fwupd_client_install_release_finish(FWUPD_CLIENT(source), res, &error)) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +static void +fwupd_client_install_release_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + + if (!fwupd_client_install_bytes_finish(FWUPD_CLIENT(source), res, &error)) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +static void +fwupd_client_install_release_download_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + FwupdClientInstallReleaseData *data = g_task_get_task_data(task); + GChecksumType checksum_type; + GCancellable *cancellable = g_task_get_cancellable(task); + const gchar *checksum_expected; + g_autofree gchar *checksum_actual = NULL; + + blob = fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &error); + if (blob == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* verify checksum */ + checksum_expected = fwupd_checksum_get_best(fwupd_release_get_checksums(data->release)); + checksum_type = fwupd_checksum_guess_kind(checksum_expected); + checksum_actual = g_compute_checksum_for_bytes(checksum_type, blob); + if (g_strcmp0(checksum_expected, checksum_actual) != 0) { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, expected %s got %s", + checksum_expected, + checksum_actual); + return; + } + + /* if the device specifies ONLY_OFFLINE automatically set this flag */ + if (fwupd_device_has_flag(data->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) + data->install_flags |= FWUPD_INSTALL_FLAG_OFFLINE; + fwupd_client_install_bytes_async(FWUPD_CLIENT(source), + fwupd_device_get_id(data->device), + blob, + data->install_flags, + cancellable, + fwupd_client_install_release_bytes_cb, + g_steal_pointer(&task)); +} + +static gboolean +fwupd_client_is_url_http(const gchar *perhaps_url) +{ +#ifdef HAVE_LIBCURL_7_62_0 + g_autoptr(CURLU) h = curl_url(); + return curl_url_set(h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK; +#else + return g_str_has_prefix(perhaps_url, "http://") || + g_str_has_prefix(perhaps_url, "https://"); +#endif +} + +static gboolean +fwupd_client_is_url_ipfs(const gchar *perhaps_url) +{ + return g_str_has_prefix(perhaps_url, "ipfs://") || g_str_has_prefix(perhaps_url, "ipns://"); +} + +static void +fwupd_client_install_release_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + GPtrArray *locations; + const gchar *uri_tmp; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GPtrArray) uris_built = g_ptr_array_new_with_free_func(g_free); + FwupdClientInstallReleaseData *data = g_task_get_task_data(task); + GCancellable *cancellable = g_task_get_cancellable(task); + + /* if a remote-id was specified, the remote has to exist */ + remote = fwupd_client_get_remote_by_id_finish(FWUPD_CLIENT(source), res, &error); + if (remote == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* get the default release only until other parts of fwupd can cope */ + locations = fwupd_release_get_locations(data->release); + if (locations->len == 0) { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "release missing URI"); + return; + } + uri_tmp = g_ptr_array_index(locations, 0); + + /* local and directory remotes may have the firmware already */ + if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_LOCAL && + !fwupd_client_is_url_http(uri_tmp)) { + const gchar *fn_cache = fwupd_remote_get_filename_cache(remote); + g_autofree gchar *path = g_path_get_dirname(fn_cache); + 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) { + fwupd_client_install_async(FWUPD_CLIENT(source), + fwupd_device_get_id(data->device), + fn, + data->install_flags, + cancellable, + fwupd_client_install_release_cb, + g_steal_pointer(&task)); + return; + } + + /* remote file */ + for (guint i = 0; i < locations->len; i++) { + uri_tmp = g_ptr_array_index(locations, i); + if (fwupd_client_is_url_ipfs(uri_tmp)) { + g_ptr_array_add(uris_built, g_strdup(uri_tmp)); + } else if (fwupd_client_is_url_http(uri_tmp)) { + g_autofree gchar *uri_str = NULL; + uri_str = fwupd_remote_build_firmware_uri(remote, uri_tmp, &error); + if (uri_str == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + g_ptr_array_add(uris_built, g_steal_pointer(&uri_str)); + } + } + + /* download file */ + fwupd_client_download_bytes2_async(FWUPD_CLIENT(source), + uris_built, + data->download_flags, + cancellable, + fwupd_client_install_release_download_cb, + g_steal_pointer(&task)); +} + +#ifdef HAVE_LIBCURL +static GPtrArray * +fwupd_client_filter_locations(GPtrArray *locations, + FwupdClientDownloadFlags download_flags, + GError **error) +{ + g_autoptr(GPtrArray) uris_filtered = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(locations != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + for (guint i = 0; i < locations->len; i++) { + const gchar *uri = g_ptr_array_index(locations, i); + if ((download_flags & FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS) > 0 && + !fwupd_client_is_url_ipfs(uri)) + continue; + g_ptr_array_add(uris_filtered, g_strdup(uri)); + } + if (uris_filtered->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no valid release URIs"); + return NULL; + } + return g_steal_pointer(&uris_filtered); +} +#endif + +/** + * fwupd_client_install_release2_async: + * @self: a #FwupdClient + * @device: a device + * @release: a release + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @download_flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_DISABLE_IPFS + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Installs a new release on a device, downloading the firmware if required. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.6 + **/ +void +fwupd_client_install_release2_async(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + FwupdClientDownloadFlags download_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + FwupdClientInstallReleaseData *data; + const gchar *remote_id; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(FWUPD_IS_DEVICE(device)); + g_return_if_fail(FWUPD_IS_RELEASE(release)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + data = g_new0(FwupdClientInstallReleaseData, 1); + data->device = g_object_ref(device); + data->release = g_object_ref(release); + data->download_flags = download_flags; + data->install_flags = install_flags; + g_task_set_task_data(task, data, (GDestroyNotify)fwupd_client_install_release_data_free); + + /* work out what remote-specific URI fields this should use */ + remote_id = fwupd_release_get_remote_id(release); + if (remote_id == NULL) { + fwupd_client_download_bytes2_async(self, + fwupd_release_get_locations(release), + download_flags, + cancellable, + fwupd_client_install_release_download_cb, + g_steal_pointer(&task)); + return; + } + + /* if a remote-id was specified, the remote has to exist */ + fwupd_client_get_remote_by_id_async(self, + remote_id, + cancellable, + fwupd_client_install_release_remote_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_install_release_async: + * @self: a #FwupdClient + * @device: a device + * @release: a release + * @install_flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Installs a new release on a device, downloading the firmware if required. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + * Deprecated: 1.5.6 + **/ +void +fwupd_client_install_release_async(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + return fwupd_client_install_release2_async(self, + device, + release, + install_flags, + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + callback, + callback_data); +} + +/** + * fwupd_client_install_release_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.install_release_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_install_release_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +#ifdef HAVE_GIO_UNIX + +static void +fwupd_client_get_details_stream_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GDBusMessage) msg = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + + msg = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source), + res, + &error); + if (msg == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + if (g_dbus_message_to_gerror(msg, &error)) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_device_array_from_variant(g_dbus_message_get_body(msg)), + (GDestroyNotify)g_ptr_array_unref); +} + +void +fwupd_client_get_details_stream_async(FwupdClient *self, + GUnixInputStream *istr, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + gint fd = g_unix_input_stream_get_fd(istr); + g_autoptr(GDBusMessage) request = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + + /* set out of band file descriptor */ + fd_list = g_unix_fd_list_new(); + g_unix_fd_list_append(fd_list, fd, NULL); + request = g_dbus_message_new_method_call(FWUPD_DBUS_SERVICE, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "GetDetails"); + g_dbus_message_set_unix_fd_list(request, fd_list); + + /* call into daemon */ + g_dbus_message_set_body(request, g_variant_new("(h)", fd)); + g_dbus_connection_send_message_with_reply(g_dbus_proxy_get_connection(priv->proxy), + request, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + fwupd_client_get_details_stream_cb, + g_steal_pointer(&task)); +} +#endif + +/** + * fwupd_client_get_details_bytes_async: + * @self: a #FwupdClient + * @bytes: firmware archive + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets details about a specific firmware file. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_details_bytes_async(FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ +#ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error = NULL; + g_autoptr(GUnixInputStream) istr = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_bytes(bytes, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* call into daemon */ + fwupd_client_get_details_stream_async(self, istr, cancellable, callback, callback_data); +#else + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Get Details only supported on Linux"); +#endif +} + +/** + * fwupd_client_get_details_bytes_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_details_bytes_async]. + * + * Returns: (transfer container) (element-type FwupdDevice): an array of results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_details_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +/** + * fwupd_client_get_percentage: + * @self: a #FwupdClient + * + * Gets the last returned percentage value. + * + * Returns: a percentage, or 0 for unknown. + * + * Since: 0.7.3 + **/ +guint +fwupd_client_get_percentage(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), 0); + return priv->percentage; +} + +/** + * fwupd_client_get_daemon_version: + * @self: a #FwupdClient + * + * Gets the daemon version number. + * + * Returns: a string, or %NULL for unknown. + * + * Since: 0.9.6 + **/ +const gchar * +fwupd_client_get_daemon_version(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->daemon_version; +} + +/** + * fwupd_client_get_host_bkc: + * @self: a #FwupdClient + * + * Gets the daemon version number. + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.7.3 + **/ +const gchar * +fwupd_client_get_host_bkc(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->host_bkc; +} + +/** + * fwupd_client_get_host_product: + * @self: a #FwupdClient + * + * Gets the string that represents the host running fwupd + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.3.1 + **/ +const gchar * +fwupd_client_get_host_product(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->host_product; +} + +/** + * fwupd_client_get_host_vendor: + * @self: a #FwupdClient + * + * Gets the string that represents the vendor of the host running fwupd + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.8.2 + **/ +const gchar * +fwupd_client_get_host_vendor(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->host_vendor; +} + +/** + * fwupd_client_get_host_machine_id: + * @self: a #FwupdClient + * + * Gets the string that represents the host machine ID + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.3.2 + **/ +const gchar * +fwupd_client_get_host_machine_id(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->host_machine_id; +} + +/** + * fwupd_client_get_host_security_id: + * @self: a #FwupdClient + * + * Gets the string that represents the host machine ID + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_client_get_host_security_id(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->host_security_id; +} + +/** + * fwupd_client_get_battery_level: + * @self: a #FwupdClient + * + * Returns the system battery level. + * + * Returns: value in percent + * + * Since: 1.8.1 + **/ +guint32 +fwupd_client_get_battery_level(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FWUPD_BATTERY_LEVEL_INVALID); + return priv->battery_level; +} + +/** + * fwupd_client_get_battery_threshold: + * @self: a #FwupdClient + * + * Returns the system battery threshold under which a firmware update cannot be + * performed. + * + * Returns: value in percent + * + * Since: 1.8.1 + **/ +guint32 +fwupd_client_get_battery_threshold(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FWUPD_BATTERY_LEVEL_INVALID); + return priv->battery_threshold; +} + +/** + * fwupd_client_get_status: + * @self: a #FwupdClient + * + * Gets the last returned status value. + * + * Returns: a #FwupdStatus, or %FWUPD_STATUS_UNKNOWN for unknown. + * + * Since: 0.7.3 + **/ +FwupdStatus +fwupd_client_get_status(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FWUPD_STATUS_UNKNOWN); + return priv->status; +} + +/** + * fwupd_client_get_tainted: + * @self: 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 *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + return priv->tainted; +} + +/** + * fwupd_client_get_only_trusted: + * @self: a #FwupdClient + * + * Gets if the daemon is verifying signatures from a trusted authority. + * + * Returns: %TRUE if the daemon is checking signatures + * + * Since: 1.8.0 + **/ +gboolean +fwupd_client_get_only_trusted(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + return priv->only_trusted; +} + +/** + * fwupd_client_get_daemon_interactive: + * @self: a #FwupdClient + * + * Gets if the daemon is running in an interactive terminal. + * + * Returns: %TRUE if the daemon is running in an interactive terminal + * + * Since: 1.3.4 + **/ +gboolean +fwupd_client_get_daemon_interactive(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + return priv->interactive; +} + +#ifdef HAVE_GIO_UNIX + +static void +fwupd_client_update_metadata_stream_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GDBusMessage) msg = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + + msg = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source), + res, + &error); + if (msg == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + if (g_dbus_message_to_gerror(msg, &error)) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +void +fwupd_client_update_metadata_stream_async(FwupdClient *self, + const gchar *remote_id, + GUnixInputStream *istr, + GUnixInputStream *istr_sig, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GDBusMessage) request = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + + /* set out of band file descriptor */ + fd_list = g_unix_fd_list_new(); + g_unix_fd_list_append(fd_list, g_unix_input_stream_get_fd(istr), NULL); + g_unix_fd_list_append(fd_list, g_unix_input_stream_get_fd(istr_sig), NULL); + request = g_dbus_message_new_method_call(FWUPD_DBUS_SERVICE, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "UpdateMetadata"); + g_dbus_message_set_unix_fd_list(request, fd_list); + + /* call into daemon */ + g_dbus_message_set_body(request, + g_variant_new("(shh)", + remote_id, + g_unix_input_stream_get_fd(istr), + g_unix_input_stream_get_fd(istr_sig))); + g_dbus_connection_send_message_with_reply(g_dbus_proxy_get_connection(priv->proxy), + request, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + fwupd_client_update_metadata_stream_cb, + g_steal_pointer(&task)); +} +#endif + +/** + * fwupd_client_update_metadata_bytes_async: + * @self: a #FwupdClient + * @remote_id: remote ID, e.g. `lvfs-testing` + * @metadata: XML metadata data + * @signature: signature data + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Updates the metadata. This allows a session process to download the metadata + * and metadata signing file to be passed into the daemon to be checked and + * parsed. + * + * The @remote_id allows the firmware to be tagged so that the remote can be + * matched when the firmware is downloaded. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + **/ +void +fwupd_client_update_metadata_bytes_async(FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ +#ifdef HAVE_GIO_UNIX + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error = NULL; + g_autoptr(GUnixInputStream) istr = NULL; + g_autoptr(GUnixInputStream) istr_sig = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(remote_id != NULL); + g_return_if_fail(metadata != NULL); + g_return_if_fail(signature != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* move to a thread if this ever takes more than a few ms */ + istr = fwupd_unix_input_stream_from_bytes(metadata, &error); + if (istr == NULL) { + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + istr_sig = fwupd_unix_input_stream_from_bytes(signature, &error); + if (istr_sig == NULL) { + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* call into daemon */ + fwupd_client_update_metadata_stream_async(self, + remote_id, + istr, + istr_sig, + cancellable, + callback, + callback_data); +#else + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Update metadata only supported on Linux"); +#endif +} + +/** + * fwupd_client_update_metadata_bytes_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.update_metadata_bytes_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_update_metadata_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +typedef struct { + FwupdRemote *remote; + GBytes *signature; + GBytes *metadata; +} FwupdClientRefreshRemoteData; + +static void +fwupd_client_refresh_remote_data_free(FwupdClientRefreshRemoteData *data) +{ + if (data->signature != NULL) + g_bytes_unref(data->signature); + if (data->metadata != NULL) + g_bytes_unref(data->metadata); + g_object_unref(data->remote); + g_free(data); +} + +static void +fwupd_client_refresh_remote_update_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + + /* save metadata */ + if (!fwupd_client_update_metadata_bytes_finish(FWUPD_CLIENT(source), res, &error)) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +static void +fwupd_client_refresh_remote_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + FwupdClientRefreshRemoteData *data = g_task_get_task_data(task); + FwupdClient *self = g_task_get_source_object(task); + GCancellable *cancellable = g_task_get_cancellable(task); + + /* save metadata */ + bytes = fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &error); + if (bytes == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + data->metadata = g_steal_pointer(&bytes); + + /* send all this to fwupd */ + fwupd_client_update_metadata_bytes_async(self, + fwupd_remote_get_id(data->remote), + data->metadata, + data->signature, + cancellable, + fwupd_client_refresh_remote_update_cb, + g_steal_pointer(&task)); +} + +static void +fwupd_client_refresh_remote_signature_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + FwupdClientRefreshRemoteData *data = g_task_get_task_data(task); + FwupdClient *self = g_task_get_source_object(task); + GCancellable *cancellable = g_task_get_cancellable(task); + GChecksumType checksum_kind; + g_autofree gchar *checksum = NULL; + + /* save signature */ + bytes = fwupd_client_download_bytes_finish(FWUPD_CLIENT(source), res, &error); + if (bytes == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + data->signature = g_steal_pointer(&bytes); + if (fwupd_remote_get_keyring_kind(data->remote) == FWUPD_KEYRING_KIND_JCAT) { + if (!fwupd_remote_load_signature_bytes(data->remote, data->signature, &error)) { + g_prefix_error(&error, "Failed to load signature: "); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + } + + /* is the signature checksum the same? */ + checksum_kind = fwupd_checksum_guess_kind(fwupd_remote_get_checksum(data->remote)); + checksum = + g_compute_checksum_for_data(checksum_kind, + (const guchar *)g_bytes_get_data(data->signature, NULL), + g_bytes_get_size(data->signature)); + if (g_strcmp0(checksum, fwupd_remote_get_checksum(data->remote)) == 0) { + g_debug("metadata signature of %s is unchanged, skipping", + fwupd_remote_get_id(data->remote)); + g_task_return_boolean(task, TRUE); + return; + } + + /* download metadata */ + fwupd_client_download_bytes_async(self, + fwupd_remote_get_metadata_uri(data->remote), + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + fwupd_client_refresh_remote_metadata_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_refresh_remote_async: + * @self: a #FwupdClient + * @remote: a #FwupdRemote + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Refreshes a remote by downloading new metadata. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + **/ +void +fwupd_client_refresh_remote_async(FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientRefreshRemoteData *data; + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(FWUPD_IS_REMOTE(remote)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + + task = g_task_new(self, cancellable, callback, callback_data); + data = g_new0(FwupdClientRefreshRemoteData, 1); + data->remote = g_object_ref(remote); + g_task_set_task_data(task, + g_steal_pointer(&data), + (GDestroyNotify)fwupd_client_refresh_remote_data_free); + + /* sanity check */ + if (fwupd_remote_get_metadata_uri_sig(remote) == NULL || + fwupd_remote_get_metadata_uri(remote) == NULL) { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no metadata URIs for %s", + fwupd_remote_get_id(remote)); + return; + } + + /* download signature */ + fwupd_client_download_bytes_async(self, + fwupd_remote_get_metadata_uri_sig(remote), + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + cancellable, + fwupd_client_refresh_remote_signature_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_refresh_remote_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.refresh_remote_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_refresh_remote_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_get_remotes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, + fwupd_remote_array_from_variant(val), + (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_remotes_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets the list of remotes that have been configured for the system. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_remotes_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetRemotes", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_remotes_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_remotes_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_remotes_async]. + * + * Returns: (element-type FwupdRemote) (transfer container): results + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_remotes_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_get_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_auto(GStrv) strv = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + g_variant_get(val, "(^as)", &strv); + for (guint i = 0; strv[i] != NULL; i++) + g_ptr_array_add(array, g_strdup(strv[i])); + + /* success */ + g_task_return_pointer(task, g_steal_pointer(&array), (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_approved_firmware_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets the list of approved firmware. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_approved_firmware_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetApprovedFirmware", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_approved_firmware_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_approved_firmware_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_approved_firmware_async]. + * + * Returns: (element-type utf8) (transfer container): checksums, or %NULL for error + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_approved_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_set_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_set_approved_firmware_async: + * @self: a #FwupdClient + * @checksums: (element-type utf8): firmware checksums + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Sets the list of approved firmware. + * + * Since: 1.5.0 + **/ +void +fwupd_client_set_approved_firmware_async(FwupdClient *self, + GPtrArray *checksums, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + g_auto(GStrv) strv = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + strv = g_new0(gchar *, checksums->len + 1); + for (guint i = 0; i < checksums->len; i++) { + const gchar *tmp = g_ptr_array_index(checksums, i); + strv[i] = g_strdup(tmp); + } + g_dbus_proxy_call(priv->proxy, + "SetApprovedFirmware", + g_variant_new("(^as)", strv), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_set_approved_firmware_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_set_approved_firmware_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.set_approved_firmware_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_set_approved_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_get_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_auto(GStrv) strv = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + g_variant_get(val, "(^as)", &strv); + for (guint i = 0; strv[i] != NULL; i++) + g_ptr_array_add(array, g_strdup(strv[i])); + + /* success */ + g_task_return_pointer(task, g_steal_pointer(&array), (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_get_blocked_firmware_async: + * @self: a #FwupdClient + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets the list of blocked firmware. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_blocked_firmware_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "GetBlockedFirmware", + NULL, + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_get_blocked_firmware_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_blocked_firmware_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_blocked_firmware_async]. + * + * Returns: (element-type utf8) (transfer container): checksums, or %NULL for error + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_client_get_blocked_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_set_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_set_blocked_firmware_async: + * @self: a #FwupdClient + * @checksums: (element-type utf8): firmware checksums + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Sets the list of blocked firmware. + * + * Since: 1.5.0 + **/ +void +fwupd_client_set_blocked_firmware_async(FwupdClient *self, + GPtrArray *checksums, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + g_auto(GStrv) strv = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + strv = g_new0(gchar *, checksums->len + 1); + for (guint i = 0; i < checksums->len; i++) { + const gchar *tmp = g_ptr_array_index(checksums, i); + strv[i] = g_strdup(tmp); + } + g_dbus_proxy_call(priv->proxy, + "SetBlockedFirmware", + g_variant_new("(^as)", strv), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_set_blocked_firmware_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_set_blocked_firmware_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.set_blocked_firmware_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_set_blocked_firmware_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_set_feature_flags_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_set_feature_flags_async: + * @self: a #FwupdClient + * @feature_flags: feature flags, e.g. %FWUPD_FEATURE_FLAG_UPDATE_TEXT + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Sets the features the client supports. This allows firmware to depend on + * specific front-end features, for instance showing the user an image on + * how to detach the hardware. + * + * Since: 1.5.0 + **/ +void +fwupd_client_set_feature_flags_async(FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "SetFeatureFlags", + g_variant_new("(t)", (guint64)feature_flags), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_set_feature_flags_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_set_feature_flags_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.set_feature_flags_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_set_feature_flags_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_self_sign_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + gchar *str = NULL; + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_variant_get(val, "(s)", &str); + g_task_return_pointer(task, g_steal_pointer(&str), (GDestroyNotify)g_free); +} + +/** + * fwupd_client_self_sign_async: + * @self: a #FwupdClient + * @value: a string to sign, typically a JSON blob + * @flags: signing flags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Signs the data using the client self-signed certificate. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 1.5.0 + **/ +void +fwupd_client_self_sign_async(FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(value != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != 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 */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "SelfSign", + g_variant_new("(sa{sv})", value, &builder), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_self_sign_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_self_sign_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.self_sign_async]. + * + * Returns: a signature, or %NULL for failure + * + * Since: 1.5.0 + **/ +gchar * +fwupd_client_self_sign_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void +fwupd_client_modify_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_modify_remote_async: + * @self: a #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @key: the key, e.g. `Enabled` + * @value: the key, e.g. `true` + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Modifies a system remote in a specific way. + * + * Since: 1.5.0 + **/ +void +fwupd_client_modify_remote_async(FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(remote_id != NULL); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "ModifyRemote", + g_variant_new("(sss)", remote_id, key, value), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_modify_remote_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_modify_remote_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.modify_remote_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_modify_remote_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void +fwupd_client_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_modify_device_async: + * @self: a #FwupdClient + * @device_id: (not nullable): the device ID + * @key: (not nullable): the key, e.g. `Flags` + * @value: (not nullable): the value, e.g. `reported` + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Modifies a device in a specific way. Not all properties on the #FwupdDevice + * are settable by the client, and some may have other restrictions on @value. + * + * Since: 1.5.0 + **/ +void +fwupd_client_modify_device_async(FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(device_id != NULL); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "ModifyDevice", + g_variant_new("(sss)", device_id, key, value), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_modify_device_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_modify_device_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.modify_device_async]. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fwupd_client_modify_device_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static FwupdRemote * +fwupd_client_get_remote_by_id_noref(GPtrArray *remotes, const gchar *remote_id) +{ + 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; + } + return NULL; +} + +static void +fwupd_client_get_remote_by_id_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdRemote *remote_tmp; + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) remotes = NULL; + const gchar *remote_id = g_task_get_task_data(task); + + remotes = fwupd_client_get_remotes_finish(FWUPD_CLIENT(source), res, &error); + if (remotes == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + remote_tmp = fwupd_client_get_remote_by_id_noref(remotes, remote_id); + if (remote_tmp == NULL) { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no remote '%s' found in search paths", + remote_id); + return; + } + + /* success */ + g_task_return_pointer(task, g_object_ref(remote_tmp), (GDestroyNotify)g_object_unref); +} + +/** + * fwupd_client_get_remote_by_id_async: + * @self: a #FwupdClient + * @remote_id: (not nullable): the remote ID, e.g. `lvfs-testing` + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets a specific remote that has been configured for the system. + * + * Since: 1.5.0 + **/ +void +fwupd_client_get_remote_by_id_async(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(remote_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_task_set_task_data(task, g_strdup(remote_id), g_free); + fwupd_client_get_remotes_async(self, + cancellable, + fwupd_client_get_remote_by_id_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_get_remote_by_id_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.get_remote_by_id_async]. + * + * Returns: (transfer full): a #FwupdRemote, or %NULL if not found + * + * Since: 1.5.0 + **/ +FwupdRemote * +fwupd_client_get_remote_by_id_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +/** + * fwupd_client_set_user_agent: + * @self: a #FwupdClient + * @user_agent: the user agent ID, e.g. `gnome-software/3.34.1` + * + * Manually sets the user agent that is used for downloading. The user agent + * should contain the runtime version of fwupd somewhere in the provided string. + * + * Since: 1.4.5 + **/ +void +fwupd_client_set_user_agent(FwupdClient *self, const gchar *user_agent) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(user_agent != NULL); + + /* not changed */ + if (g_strcmp0(priv->user_agent, user_agent) == 0) + return; + + g_free(priv->user_agent); + priv->user_agent = g_strdup(user_agent); +} + +/** + * fwupd_client_get_user_agent: + * @self: a #FwupdClient + * + * Gets the string that represents the user agent that is used for + * uploading and downloading. The user agent will contain the runtime + * version of fwupd somewhere in the provided string. + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.5.2 + **/ +const gchar * +fwupd_client_get_user_agent(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + return priv->user_agent; +} + +/** + * fwupd_client_set_user_agent_for_package: + * @self: a #FwupdClient + * @package_name: (not nullable): client program name, e.g. `gnome-software` + * @package_version: (not nullable): client program version, e.g. `3.28.1` + * + * Builds a user-agent to use for the download. + * + * Supplying harmless details to the server means it knows more about each + * client. This allows the web service to respond in a different way, for + * instance sending a different metadata file for old versions of fwupd, or + * returning an error for Solaris machines. + * + * Before freaking out about theoretical privacy implications, much more data + * than this is sent to each and every website you visit. + * + * Since: 1.4.5 + **/ +void +fwupd_client_set_user_agent_for_package(FwupdClient *self, + const gchar *package_name, + const gchar *package_version) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GString) str = g_string_new(NULL); + g_autofree gchar *system = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(package_name != NULL); + g_return_if_fail(package_version != NULL); + + /* application name and version */ + g_string_append_printf(str, "%s/%s", package_name, package_version); + + /* system information */ + system = fwupd_build_user_agent_system(); + if (system != NULL) + g_string_append_printf(str, " (%s)", system); + + /* platform, which in our case is just fwupd */ + if (g_strcmp0(package_name, "fwupd") != 0) + g_string_append_printf(str, " fwupd/%s", priv->daemon_version); + + /* success */ + g_free(priv->user_agent); + priv->user_agent = g_string_free(g_steal_pointer(&str), FALSE); +} + +#ifdef HAVE_LIBCURL +static size_t +fwupd_client_download_write_callback_cb(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + GByteArray *buf = (GByteArray *)userdata; + gsize realsize = size * nmemb; + g_byte_array_append(buf, (const guint8 *)ptr, realsize); + return realsize; +} + +static GBytes * +fwupd_client_download_ipfs(FwupdClient *self, + const gchar *url, + GCancellable *cancellable, + GError **error) +{ + GSubprocessFlags flags = G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE; + g_autofree gchar *path = NULL; + g_autoptr(GBytes) bstdout = NULL; + g_autoptr(GBytes) bstderr = NULL; + g_autoptr(GSubprocess) subprocess = NULL; + + /* we get no detailed progress details */ + fwupd_client_set_status(self, FWUPD_STATUS_DOWNLOADING); + fwupd_client_set_percentage(self, 0); + + /* convert from URI to path */ + if (g_str_has_prefix(url, "ipfs://")) { + path = g_strdup_printf("/ipfs/%s", url + 7); + } else if (g_str_has_prefix(url, "ipns://")) { + path = g_strdup_printf("/ipns/%s", url + 7); + } else { + path = g_strdup(url); + } + + /* run sync */ + subprocess = g_subprocess_new(flags, error, "ipfs", "cat", path, NULL); + if (subprocess == NULL) + return NULL; + if (!g_subprocess_communicate(subprocess, NULL, cancellable, &bstdout, &bstderr, error)) + return NULL; + fwupd_client_set_status(self, FWUPD_STATUS_IDLE); + if (g_subprocess_get_exit_status(subprocess) != 0) { + const gchar *msg = g_bytes_get_data(bstderr, NULL); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to download file: %s", + msg); + return NULL; + } + return g_steal_pointer(&bstdout); +} + +static GBytes * +fwupd_client_download_http(FwupdClient *self, CURL *curl, const gchar *url, GError **error) +{ + CURLcode res; + gchar errbuf[CURL_ERROR_SIZE] = {'\0'}; + glong status_code = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + fwupd_client_set_status(self, FWUPD_STATUS_DOWNLOADING); + (void)curl_easy_setopt(curl, CURLOPT_URL, url); + (void)curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + (void)curl_easy_setopt(curl, + CURLOPT_WRITEFUNCTION, + fwupd_client_download_write_callback_cb); + (void)curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf); + res = curl_easy_perform(curl); + fwupd_client_set_status(self, FWUPD_STATUS_IDLE); + if (res != CURLE_OK) { + if (errbuf[0] != '\0') { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to download file: %s", + errbuf); + return NULL; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to download file: %s", + curl_easy_strerror(res)); + return NULL; + } + + /* check for server limit */ + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); + g_debug("status-code was %ld", status_code); + if (status_code == 429) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to download due to server limit"); + return NULL; + } + if (status_code >= 400) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to download, server response was %u", + (guint)status_code); + return NULL; + } + + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fwupd_client_download_bytes_thread_cb(GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + FwupdClient *self = FWUPD_CLIENT(source_object); + FwupdCurlHelper *helper = g_task_get_task_data(task); + g_autoptr(GBytes) blob = NULL; + + for (guint i = 0; i < helper->urls->len; i++) { + const gchar *url = g_ptr_array_index(helper->urls, i); + g_autoptr(GError) error = NULL; + g_debug("downloading %s", url); + fwupd_client_curl_helper_set_proxy(self, helper, url); + if (fwupd_client_is_url_http(url)) { + blob = fwupd_client_download_http(self, helper->curl, url, &error); + if (blob != NULL) + break; + } else if (fwupd_client_is_url_ipfs(url)) { + blob = fwupd_client_download_ipfs(self, url, cancellable, &error); + if (blob != NULL) + break; + } else { + g_set_error(&error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "not sure how to handle: %s", + url); + } + if (i == helper->urls->len - 1) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + fwupd_client_set_percentage(self, 0); + fwupd_client_set_status(self, FWUPD_STATUS_IDLE); + g_debug("failed to download %s: %s, trying next URI…", url, error->message); + } + g_task_return_pointer(task, g_steal_pointer(&blob), (GDestroyNotify)g_bytes_unref); +} +#endif + +/* private */ +void +fwupd_client_download_bytes2_async(FwupdClient *self, + GPtrArray *urls, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + g_autoptr(GTask) task = NULL; +#ifdef HAVE_LIBCURL + g_autoptr(GError) error = NULL; + g_autoptr(FwupdCurlHelper) helper = NULL; +#endif + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(urls != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + + /* ensure networking set up */ + task = g_task_new(self, cancellable, callback, callback_data); +#ifdef HAVE_LIBCURL + helper = fwupd_client_curl_new(self, &error); + if (helper == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + helper->urls = fwupd_client_filter_locations(urls, flags, &error); + if (helper->urls == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + g_task_set_task_data(task, + g_steal_pointer(&helper), + (GDestroyNotify)fwupd_client_curl_helper_free); + + /* download data */ + g_task_run_in_thread(task, fwupd_client_download_bytes_thread_cb); +#else + g_task_return_new_error(task, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no libcurl support"); +#endif +} + +/** + * fwupd_client_download_bytes_async: + * @self: a #FwupdClient + * @url: (not nullable): the remote URL + * @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Downloads data from a remote server. The [method@Client.set_user_agent] function + * should be called before this method is used. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + **/ +void +fwupd_client_download_bytes_async(FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + g_autoptr(GPtrArray) urls = g_ptr_array_new_with_free_func(g_free); + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(url != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + + /* just proxy */ + g_ptr_array_add(urls, g_strdup(url)); + fwupd_client_download_bytes2_async(self, urls, flags, cancellable, callback, callback_data); +} + +/** + * fwupd_client_download_bytes_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.download_bytes_async]. + * + * Returns: (transfer full): downloaded data, or %NULL for error + * + * Since: 1.5.0 + **/ +GBytes * +fwupd_client_download_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +#ifdef HAVE_LIBCURL +static void +fwupd_client_upload_bytes_thread_cb(GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + FwupdClient *self = FWUPD_CLIENT(source_object); + FwupdCurlHelper *helper = g_task_get_task_data(task); + CURLcode res; + gchar errbuf[CURL_ERROR_SIZE] = {'\0'}; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + (void)curl_easy_setopt(helper->curl, CURLOPT_ERRORBUFFER, errbuf); + (void)curl_easy_setopt(helper->curl, + CURLOPT_WRITEFUNCTION, + fwupd_client_download_write_callback_cb); + (void)curl_easy_setopt(helper->curl, CURLOPT_WRITEDATA, buf); + res = curl_easy_perform(helper->curl); + fwupd_client_set_status(self, FWUPD_STATUS_IDLE); + if (res != CURLE_OK) { + glong status_code = 0; + curl_easy_getinfo(helper->curl, CURLINFO_RESPONSE_CODE, &status_code); + g_debug("status-code was %ld", status_code); + if (errbuf[0] != '\0') { + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to upload file: %s", + errbuf); + return; + } + g_task_return_new_error(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to upload file: %s", + curl_easy_strerror(res)); + + return; + } + g_task_return_pointer(task, + g_byte_array_free_to_bytes(g_steal_pointer(&buf)), + (GDestroyNotify)g_bytes_unref); +} +#endif + +/** + * fwupd_client_upload_bytes_async: + * @self: a #FwupdClient + * @url: (not nullable): the remote URL + * @payload: (not nullable): payload string + * @signature: (nullable): signature string + * @flags: download flags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE + * @cancellable: (nullable): optional #GCancellable + * @callback: the function to run on completion + * @callback_data: the data to pass to @callback + * + * Uploads data to a remote server. The [method@Client.set_user_agent] function + * should be called before this method is used. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * NOTE: This method is thread-safe, but progress signals will be + * emitted in the global default main context, if not explicitly set with + * [method@Client.set_main_context]. + * + * Since: 1.5.0 + **/ +void +fwupd_client_upload_bytes_async(FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; +#ifdef HAVE_LIBCURL + g_autoptr(FwupdCurlHelper) helper = NULL; + g_autoptr(GError) error = NULL; +#endif + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(url != NULL); + g_return_if_fail(payload != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* ensure networking set up */ + task = g_task_new(self, cancellable, callback, callback_data); +#ifdef HAVE_LIBCURL + helper = fwupd_client_curl_new(self, &error); + if (helper == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* build message */ + if ((flags & FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART) > 0 || signature != NULL) { + curl_mimepart *part; + helper->mime = curl_mime_init(helper->curl); + (void)curl_easy_setopt(helper->curl, CURLOPT_MIMEPOST, helper->mime); + part = curl_mime_addpart(helper->mime); + (void)curl_mime_data(part, payload, CURL_ZERO_TERMINATED); + curl_mime_name(part, "payload"); + if (signature != NULL) { + part = curl_mime_addpart(helper->mime); + (void)curl_mime_data(part, signature, CURL_ZERO_TERMINATED); + curl_mime_name(part, "signature"); + } + } else { + helper->headers = curl_slist_append(helper->headers, "Content-Type: text/plain"); + (void)curl_easy_setopt(helper->curl, CURLOPT_HTTPHEADER, helper->headers); + (void)curl_easy_setopt(helper->curl, CURLOPT_POST, 1L); + (void)curl_easy_setopt(helper->curl, CURLOPT_POSTFIELDSIZE, strlen(payload)); + (void)curl_easy_setopt(helper->curl, CURLOPT_COPYPOSTFIELDS, payload); + } + + fwupd_client_set_status(self, FWUPD_STATUS_IDLE); + g_debug("uploading to %s", url); + (void)curl_easy_setopt(helper->curl, CURLOPT_URL, url); + g_task_set_task_data(task, + g_steal_pointer(&helper), + (GDestroyNotify)fwupd_client_curl_helper_free); + g_task_run_in_thread(task, fwupd_client_upload_bytes_thread_cb); +#else + g_task_return_new_error(task, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no libcurl support"); +#endif +} + +/** + * fwupd_client_upload_bytes_finish: + * @self: a #FwupdClient + * @res: the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.upload_bytes_async]. + * + * Returns: (transfer full): response data, or %NULL for error + * + * Since: 1.5.0 + **/ +GBytes * +fwupd_client_upload_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +/** + * fwupd_client_add_hint: + * @self: a #FwupdClient + * @key: (not nullable): the key, e.g. `locale` + * @value: (nullable): the value @key should be set + * + * Sets optional hints from the client that may affect the list of devices. + * + * Since: 1.7.1 + **/ +void +fwupd_client_add_hint(FwupdClient *self, const gchar *key, const gchar *value) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(key != NULL); + + g_hash_table_insert(priv->hints, g_strdup(key), g_strdup(value)); +} + +#ifdef SOUP_SESSION_COMPAT +/* this is bad; we dlopen libsoup-2.4.so.1 and get the gtype manually + * to avoid deps on both libcurl and libsoup whilst preserving ABI */ +static void +fwupd_client_ensure_soup_session(FwupdClient *self) +{ + FwupdClientObjectNewFunc func = NULL; + FwupdClientPrivate *priv = GET_PRIVATE(self); + GType soup_gtype; + + /* already set up */ + if (priv->soup_session != NULL) + return; + + /* known GType, just create */ + soup_gtype = g_type_from_name("SoupSession"); + if (soup_gtype != 0) { + priv->soup_session = g_object_new(soup_gtype, NULL); + return; + } + + /* load the library at runtime, leaking the module */ + if (priv->soup_module == NULL) { + g_autofree gchar *fn = NULL; + fn = g_build_filename(FWUPD_LIBDIR, "libsoup-2.4.so.1", NULL); + priv->soup_module = g_module_open(fn, G_MODULE_BIND_LAZY); + if (priv->soup_module == NULL) { + g_warning("failed to find libsoup library"); + return; + } + } + if (!g_module_symbol(priv->soup_module, "soup_session_new", (gpointer *)&func)) { + g_warning("failed to find soup_session_get_type()"); + g_module_close(priv->soup_module); + priv->soup_module = NULL; + return; + } + priv->soup_session = func(); + g_object_set(priv->soup_session, "timeout", (guint)60, NULL); +} +#endif + +static void +fwupd_client_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FwupdClient *self = FWUPD_CLIENT(object); + FwupdClientPrivate *priv = GET_PRIVATE(self); + + switch (prop_id) { + case PROP_STATUS: + g_value_set_uint(value, priv->status); + break; + case PROP_TAINTED: + g_value_set_boolean(value, priv->tainted); + break; + case PROP_SOUP_SESSION: +#ifdef SOUP_SESSION_COMPAT + fwupd_client_ensure_soup_session(self); + g_value_set_object(value, priv->soup_session); +#else + g_value_set_object(value, NULL); +#endif + break; + case PROP_PERCENTAGE: + g_value_set_uint(value, priv->percentage); + break; + case PROP_DAEMON_VERSION: + g_value_set_string(value, priv->daemon_version); + break; + case PROP_HOST_BKC: + g_value_set_string(value, priv->host_bkc); + break; + case PROP_HOST_VENDOR: + g_value_set_string(value, priv->host_vendor); + break; + case PROP_HOST_PRODUCT: + g_value_set_string(value, priv->host_product); + break; + case PROP_HOST_MACHINE_ID: + g_value_set_string(value, priv->host_machine_id); + break; + case PROP_HOST_SECURITY_ID: + g_value_set_string(value, priv->host_security_id); + break; + case PROP_ONLY_TRUSTED: + g_value_set_boolean(value, priv->only_trusted); + break; + case PROP_INTERACTIVE: + g_value_set_boolean(value, priv->interactive); + break; + case PROP_BATTERY_LEVEL: + g_value_set_uint(value, priv->battery_level); + break; + case PROP_BATTERY_THRESHOLD: + g_value_set_uint(value, priv->battery_threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fwupd_client_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FwupdClient *self = FWUPD_CLIENT(object); + FwupdClientPrivate *priv = GET_PRIVATE(self); + + switch (prop_id) { + case PROP_STATUS: + priv->status = g_value_get_uint(value); + break; + case PROP_PERCENTAGE: + priv->percentage = g_value_get_uint(value); + break; + case PROP_BATTERY_LEVEL: + fwupd_client_set_battery_level(self, g_value_get_uint(value)); + break; + case PROP_BATTERY_THRESHOLD: + fwupd_client_set_battery_threshold(self, g_value_get_uint(value)); + break; + case PROP_HOST_BKC: + fwupd_client_set_host_bkc(self, g_value_get_string(value)); + break; + case PROP_HOST_VENDOR: + fwupd_client_set_host_vendor(self, g_value_get_string(value)); + break; + case PROP_HOST_PRODUCT: + fwupd_client_set_host_product(self, g_value_get_string(value)); + break; + case PROP_HOST_MACHINE_ID: + fwupd_client_set_host_machine_id(self, g_value_get_string(value)); + break; + case PROP_HOST_SECURITY_ID: + fwupd_client_set_host_security_id(self, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fwupd_client_class_init(FwupdClientClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fwupd_client_finalize; + object_class->get_property = fwupd_client_get_property; + object_class->set_property = fwupd_client_set_property; + + /** + * FwupdClient::changed: + * @self: the #FwupdClient instance that emitted the signal + * + * The ::changed signal is emitted when the daemon internal has + * changed, for instance when a device has been added or removed. + * + * Since: 0.7.0 + **/ + signals[SIGNAL_CHANGED] = g_signal_new("changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FwupdClientClass, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * FwupdClient::state-changed: + * @self: the #FwupdClient instance that emitted the signal + * @status: the #FwupdStatus + * + * The ::state-changed signal is emitted when the daemon status has + * changed, e.g. going from %FWUPD_STATUS_IDLE to %FWUPD_STATUS_DEVICE_WRITE. + * + * Since: 0.7.0 + **/ + signals[SIGNAL_STATUS_CHANGED] = + g_signal_new("status-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FwupdClientClass, status_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + + /** + * FwupdClient::device-added: + * @self: the #FwupdClient instance that emitted the signal + * @result: the #FwupdDevice + * + * The ::device-added signal is emitted when a device has been + * added. + * + * Since: 0.7.1 + **/ + signals[SIGNAL_DEVICE_ADDED] = g_signal_new("device-added", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FwupdClientClass, device_added), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + FWUPD_TYPE_DEVICE); + + /** + * FwupdClient::device-removed: + * @self: the #FwupdClient instance that emitted the signal + * @result: the #FwupdDevice + * + * The ::device-removed signal is emitted when a device has been + * removed. + * + * Since: 0.7.1 + **/ + signals[SIGNAL_DEVICE_REMOVED] = + g_signal_new("device-removed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FwupdClientClass, device_removed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + FWUPD_TYPE_DEVICE); + + /** + * FwupdClient::device-changed: + * @self: the #FwupdClient instance that emitted the signal + * @result: the #FwupdDevice + * + * The ::device-changed signal is emitted when a device has been + * changed in some way, e.g. the version number is updated. + * + * Since: 0.7.1 + **/ + signals[SIGNAL_DEVICE_CHANGED] = + g_signal_new("device-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FwupdClientClass, device_changed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + FWUPD_TYPE_DEVICE); + + /** + * FwupdClient::device-request: + * @self: the #FwupdClient instance that emitted the signal + * @msg: the #FwupdRequest + * + * The ::device-request signal is emitted when a device has been + * emitted some kind of event, e.g. a manual action is required. + * + * Since: 1.6.2 + **/ + signals[SIGNAL_DEVICE_REQUEST] = + g_signal_new("device-request", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FwupdClientClass, device_request), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + FWUPD_TYPE_REQUEST); + + /** + * FwupdClient:status: + * + * The last-reported status of the daemon. + * + * Since: 0.7.0 + */ + pspec = g_param_spec_uint("status", + NULL, + NULL, + 0, + FWUPD_STATUS_LAST, + FWUPD_STATUS_UNKNOWN, + 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:interactive: + * + * If the daemon is running in an interactive terminal + * + * Since: 1.3.4 + */ + pspec = g_param_spec_boolean("interactive", + NULL, + NULL, + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_INTERACTIVE, pspec); + + /** + * FwupdClient:percentage: + * + * The last-reported percentage of the daemon. + * + * Since: 0.7.3 + */ + pspec = g_param_spec_uint("percentage", + NULL, + NULL, + 0, + 100, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PERCENTAGE, pspec); + + /** + * FwupdClient:daemon-version: + * + * The daemon version number. + * + * Since: 0.9.6 + */ + pspec = g_param_spec_string("daemon-version", + NULL, + NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_DAEMON_VERSION, pspec); + + /** + * FwupdClient:host-bkc: + * + * The host best known configuration. + * + * Since: 1.7.3 + */ + pspec = g_param_spec_string("host-bkc", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_HOST_BKC, pspec); + + /** + * FwupdClient:soup-session: + * + * The libsoup session, now unused. + * + * Since: 1.4.5 + */ + pspec = g_param_spec_object("soup-session", + NULL, + NULL, + G_TYPE_OBJECT, + G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_SOUP_SESSION, pspec); + + /** + * FwupdClient:host-vendor: + * + * The host vendor string + * + * Since: 1.8.2 + */ + pspec = g_param_spec_string("host-vendor", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_HOST_VENDOR, pspec); + + /** + * FwupdClient:host-product: + * + * The host product string + * + * Since: 1.3.1 + */ + pspec = g_param_spec_string("host-product", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_HOST_PRODUCT, pspec); + + /** + * FwupdClient:host-machine-id: + * + * The host machine-id string + * + * Since: 1.3.2 + */ + pspec = g_param_spec_string("host-machine-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_HOST_MACHINE_ID, pspec); + + /** + * FwupdClient:host-security-id: + * + * The host machine-id string + * + * Since: 1.5.0 + */ + pspec = g_param_spec_string("host-security-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_HOST_SECURITY_ID, pspec); + + /** + * FwupdClient:only-trusted: + * + * If the daemon is verifying signatures from a trusted authority. + * + * Since: 1.8.0 + */ + pspec = g_param_spec_boolean("only-trusted", + NULL, + NULL, + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_ONLY_TRUSTED, pspec); + + /** + * FwupdClient:battery-level: + * + * The system battery level in percent. + * + * Since: 1.8.1 + */ + pspec = g_param_spec_uint("battery-level", + NULL, + NULL, + 0, + FWUPD_BATTERY_LEVEL_INVALID, + FWUPD_BATTERY_LEVEL_INVALID, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_LEVEL, pspec); + + /** + * FwupdClient:battery-threshold: + * + * The system battery threshold in percent. + * + * Since: 1.8.1 + */ + pspec = g_param_spec_uint("battery-threshold", + NULL, + NULL, + 0, + FWUPD_BATTERY_LEVEL_INVALID, + FWUPD_BATTERY_LEVEL_INVALID, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_THRESHOLD, pspec); +} + +static void +fwupd_client_init(FwupdClient *self) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_mutex_init(&priv->proxy_mutex); + g_mutex_init(&priv->idle_mutex); + priv->idle_sources = + g_ptr_array_new_with_free_func((GDestroyNotify)fwupd_client_context_helper_free); + priv->proxy_resolver = g_proxy_resolver_get_default(); + priv->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + priv->battery_level = FWUPD_BATTERY_LEVEL_INVALID; + priv->battery_threshold = FWUPD_BATTERY_LEVEL_INVALID; + + /* we get this one for free */ + fwupd_client_add_hint(self, "locale", g_getenv("LANG")); +} + +static void +fwupd_client_finalize(GObject *object) +{ + FwupdClient *self = FWUPD_CLIENT(object); + FwupdClientPrivate *priv = GET_PRIVATE(self); + + g_clear_pointer(&priv->main_ctx, g_main_context_unref); + g_free(priv->user_agent); + g_free(priv->daemon_version); + g_free(priv->host_bkc); + g_free(priv->host_vendor); + g_free(priv->host_product); + g_free(priv->host_machine_id); + g_free(priv->host_security_id); + g_hash_table_unref(priv->hints); + g_mutex_clear(&priv->idle_mutex); + if (priv->idle_id != 0) + g_source_remove(priv->idle_id); + g_ptr_array_unref(priv->idle_sources); + g_mutex_clear(&priv->proxy_mutex); + if (priv->proxy != NULL) + g_object_unref(priv->proxy); +#ifdef SOUP_SESSION_COMPAT + if (priv->soup_session != NULL) + g_object_unref(priv->soup_session); +#endif + + G_OBJECT_CLASS(fwupd_client_parent_class)->finalize(object); +} + +/** + * fwupd_client_new: + * + * Creates a new client. + * + * Returns: a new #FwupdClient + * + * Since: 0.7.0 + **/ +FwupdClient * +fwupd_client_new(void) +{ + FwupdClient *self; + self = g_object_new(FWUPD_TYPE_CLIENT, NULL); + return FWUPD_CLIENT(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-client.h b/fwupd-1.8.6/libfwupd/fwupd-client.h new file mode 100644 index 0000000000000000000000000000000000000000..d6d9d9e84d5f54950df6cb9de01cbd65eba44df0 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-client.h @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-device.h" +#include "fwupd-enums.h" +#include "fwupd-plugin.h" +#include "fwupd-remote.h" +#include "fwupd-request.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_CLIENT (fwupd_client_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdClient, fwupd_client, FWUPD, CLIENT, GObject) + +struct _FwupdClientClass { + GObjectClass parent_class; + void (*changed)(FwupdClient *client); + void (*status_changed)(FwupdClient *client, FwupdStatus status); + void (*device_added)(FwupdClient *client, FwupdDevice *result); + void (*device_removed)(FwupdClient *client, FwupdDevice *result); + void (*device_changed)(FwupdClient *client, FwupdDevice *result); + void (*device_request)(FwupdClient *client, FwupdRequest *request); + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); +}; + +/** + * FwupdClientDownloadFlags: + * @FWUPD_CLIENT_DOWNLOAD_FLAG_NONE: No flags set + * @FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS: Only use IPFS when downloading URIs + * + * The options to use for downloading. + **/ +typedef enum { + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE = 0, /* Since: 1.4.5 */ + FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS = 1 << 0, /* Since: 1.5.6 */ + /*< private >*/ + FWUPD_CLIENT_DOWNLOAD_FLAG_LAST +} FwupdClientDownloadFlags; + +/** + * FwupdClientUploadFlags: + * @FWUPD_CLIENT_UPLOAD_FLAG_NONE: No flags set + * @FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART: Always use multipart/form-data + * + * The options to use for uploading. + **/ +typedef enum { + FWUPD_CLIENT_UPLOAD_FLAG_NONE = 0, /* Since: 1.4.5 */ + FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART = 1 << 0, /* Since: 1.4.5 */ + /*< private >*/ + FWUPD_CLIENT_UPLOAD_FLAG_LAST +} FwupdClientUploadFlags; + +FwupdClient * +fwupd_client_new(void); +GMainContext * +fwupd_client_get_main_context(FwupdClient *self); +void +fwupd_client_set_main_context(FwupdClient *self, GMainContext *main_ctx); +void +fwupd_client_connect_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_connect_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_disconnect(FwupdClient *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_devices_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_devices_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_plugins_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_plugins_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_history_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_history_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_releases_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_releases_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_downgrades_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_downgrades_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_upgrades_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_upgrades_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_details_bytes_async(FwupdClient *self, + GBytes *bytes, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_details_bytes_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_verify_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_verify_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_verify_update_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_verify_update_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_unlock_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_unlock_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_modify_config_async(FwupdClient *self, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_modify_config_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_activate_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_activate_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_clear_results_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_clear_results_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_results_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +FwupdDevice * +fwupd_client_get_results_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_modify_bios_setting_async(FwupdClient *self, + GHashTable *settings, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_modify_bios_setting_finish(FwupdClient *self, GAsyncResult *res, GError **error); +void +fwupd_client_get_bios_settings_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_bios_settings_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_host_security_attrs_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_host_security_attrs_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_host_security_events_async(FwupdClient *self, + guint limit, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_host_security_events_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_device_by_id_async(FwupdClient *self, + const gchar *device_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +FwupdDevice * +fwupd_client_get_device_by_id_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_devices_by_guid_async(FwupdClient *self, + const gchar *guid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_devices_by_guid_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_install_async(FwupdClient *self, + const gchar *device_id, + const gchar *filename, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_install_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_install_bytes_async(FwupdClient *self, + const gchar *device_id, + GBytes *bytes, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_install_bytes_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_install_release_async(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) + G_DEPRECATED_FOR(fwupd_client_install_release2_async); +void +fwupd_client_install_release2_async(FwupdClient *self, + FwupdDevice *device, + FwupdRelease *release, + FwupdInstallFlags install_flags, + FwupdClientDownloadFlags download_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_install_release_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_update_metadata_bytes_async(FwupdClient *self, + const gchar *remote_id, + GBytes *metadata, + GBytes *signature, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_update_metadata_bytes_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_refresh_remote_async(FwupdClient *self, + FwupdRemote *remote, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_refresh_remote_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_modify_remote_async(FwupdClient *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_modify_remote_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_modify_device_async(FwupdClient *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_modify_device_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_report_metadata_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GHashTable * +fwupd_client_get_report_metadata_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +FwupdStatus +fwupd_client_get_status(FwupdClient *self); +gboolean +fwupd_client_get_tainted(FwupdClient *self); +gboolean +fwupd_client_get_only_trusted(FwupdClient *self); +gboolean +fwupd_client_get_daemon_interactive(FwupdClient *self); +guint +fwupd_client_get_percentage(FwupdClient *self); +const gchar * +fwupd_client_get_daemon_version(FwupdClient *self); +const gchar * +fwupd_client_get_host_bkc(FwupdClient *self); +const gchar * +fwupd_client_get_host_vendor(FwupdClient *self); +const gchar * +fwupd_client_get_host_product(FwupdClient *self); +const gchar * +fwupd_client_get_host_machine_id(FwupdClient *self); +const gchar * +fwupd_client_get_host_security_id(FwupdClient *self); +guint32 +fwupd_client_get_battery_level(FwupdClient *self); +guint32 +fwupd_client_get_battery_threshold(FwupdClient *self); + +void +fwupd_client_get_remotes_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_remotes_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_remote_by_id_async(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +FwupdRemote * +fwupd_client_get_remote_by_id_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_approved_firmware_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_approved_firmware_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_set_approved_firmware_async(FwupdClient *self, + GPtrArray *checksums, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_set_approved_firmware_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_get_blocked_firmware_async(FwupdClient *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GPtrArray * +fwupd_client_get_blocked_firmware_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_set_blocked_firmware_async(FwupdClient *self, + GPtrArray *checksums, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_set_blocked_firmware_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_self_sign_async(FwupdClient *self, + const gchar *value, + FwupdSelfSignFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gchar * +fwupd_client_self_sign_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_set_feature_flags_async(FwupdClient *self, + FwupdFeatureFlags feature_flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +gboolean +fwupd_client_set_feature_flags_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +const gchar * +fwupd_client_get_user_agent(FwupdClient *self); +void +fwupd_client_set_user_agent(FwupdClient *self, const gchar *user_agent); +void +fwupd_client_set_user_agent_for_package(FwupdClient *self, + const gchar *package_name, + const gchar *package_version); +void +fwupd_client_download_bytes_async(FwupdClient *self, + const gchar *url, + FwupdClientDownloadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GBytes * +fwupd_client_download_bytes_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_upload_bytes_async(FwupdClient *self, + const gchar *url, + const gchar *payload, + const gchar *signature, + FwupdClientUploadFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GBytes * +fwupd_client_upload_bytes_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_client_ensure_networking(FwupdClient *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fwupd_client_add_hint(FwupdClient *self, const gchar *key, const gchar *value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-common-private.h b/fwupd-1.8.6/libfwupd/fwupd-common-private.h new file mode 100644 index 0000000000000000000000000000000000000000..c58d79b0d78d46b15b23399b52b907bccbf5663b --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-common-private.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#ifdef HAVE_GIO_UNIX +#include +#endif + +#include + +#include "fwupd-common.h" + +G_BEGIN_DECLS + +GVariant * +fwupd_hash_kv_to_variant(GHashTable *hash); +GHashTable * +fwupd_variant_to_hash_kv(GVariant *dict); +gchar * +fwupd_build_user_agent_system(void); + +void +fwupd_input_stream_read_bytes_async(GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); +GBytes * +fwupd_input_stream_read_bytes_finish(GInputStream *stream, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +void +fwupd_common_json_add_string(JsonBuilder *builder, const gchar *key, const gchar *value); +void +fwupd_common_json_add_stringv(JsonBuilder *builder, const gchar *key, gchar **value); +void +fwupd_common_json_add_int(JsonBuilder *builder, const gchar *key, guint64 value); +void +fwupd_common_json_add_boolean(JsonBuilder *builder, const gchar *key, gboolean value); + +#ifdef HAVE_GIO_UNIX +GUnixInputStream * +fwupd_unix_input_stream_from_bytes(GBytes *bytes, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GUnixInputStream * +fwupd_unix_input_stream_from_fn(const gchar *fn, GError **error) G_GNUC_WARN_UNUSED_RESULT; +#endif + +void +fwupd_pad_kv_unx(GString *str, const gchar *key, guint64 value); +void +fwupd_pad_kv_str(GString *str, const gchar *key, const gchar *value); +void +fwupd_pad_kv_int(GString *str, const gchar *key, guint32 value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-common.c b/fwupd-1.8.6/libfwupd/fwupd-common.c new file mode 100644 index 0000000000000000000000000000000000000000..62906e946ce9b852a5912f191052dd6fe9cd66a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-common.c @@ -0,0 +1,1270 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-common-private.h" +#include "fwupd-device.h" +#include "fwupd-error.h" +#include "fwupd-release.h" + +#ifdef HAVE_GIO_UNIX +#include +#include +#include +#include +#endif + +#ifdef HAVE_MEMFD_CREATE +#include +#endif + +#include +#include +#ifdef HAVE_UTSNAME_H +#include +#endif +#include + +#if !GLIB_CHECK_VERSION(2, 54, 0) +#include +#endif + +/** + * fwupd_checksum_guess_kind: + * @checksum: (nullable): a checksum + * + * Guesses the checksum kind based on the length of the hash. + * + * Returns: a checksum type, e.g. %G_CHECKSUM_SHA1 + * + * Since: 0.9.3 + **/ +GChecksumType +fwupd_checksum_guess_kind(const gchar *checksum) +{ + guint len; + if (checksum == NULL) + return G_CHECKSUM_SHA1; + len = strlen(checksum); + if (len == 32) + return G_CHECKSUM_MD5; + if (len == 40) + return G_CHECKSUM_SHA1; + if (len == 64) + return G_CHECKSUM_SHA256; + if (len == 128) + return G_CHECKSUM_SHA512; + return G_CHECKSUM_SHA1; +} + +static const gchar * +fwupd_checksum_type_to_string_display(GChecksumType checksum_type) +{ + if (checksum_type == G_CHECKSUM_MD5) + return "MD5"; + 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 NULL; +} + +/** + * fwupd_checksum_format_for_display: + * @checksum: (nullable): a checksum + * + * Formats a checksum for display. + * + * Returns: text, or %NULL for invalid + * + * Since: 0.9.3 + **/ +gchar * +fwupd_checksum_format_for_display(const gchar *checksum) +{ + GChecksumType kind = fwupd_checksum_guess_kind(checksum); + return g_strdup_printf("%s(%s)", fwupd_checksum_type_to_string_display(kind), checksum); +} + +/** + * fwupd_checksum_get_by_kind: + * @checksums: (element-type utf8): checksums + * @kind: a checksum type, e.g. %G_CHECKSUM_SHA512 + * + * Gets a specific checksum kind. + * + * Returns: a checksum from the array, or %NULL if not found + * + * Since: 0.9.4 + **/ +const gchar * +fwupd_checksum_get_by_kind(GPtrArray *checksums, GChecksumType kind) +{ + g_return_val_if_fail(checksums != NULL, NULL); + for (guint i = 0; i < checksums->len; i++) { + const gchar *checksum = g_ptr_array_index(checksums, i); + if (fwupd_checksum_guess_kind(checksum) == kind) + return checksum; + } + return NULL; +} + +/** + * fwupd_checksum_get_best: + * @checksums: (element-type utf8): checksums + * + * Gets a the best possible checksum kind. + * + * Returns: a checksum from the array, or %NULL if nothing was suitable + * + * Since: 0.9.4 + **/ +const gchar * +fwupd_checksum_get_best(GPtrArray *checksums) +{ + GChecksumType checksum_types[] = {G_CHECKSUM_SHA512, G_CHECKSUM_SHA256, G_CHECKSUM_SHA1, 0}; + g_return_val_if_fail(checksums != NULL, NULL); + for (guint i = 0; checksum_types[i] != 0; i++) { + for (guint j = 0; j < checksums->len; j++) { + const gchar *checksum = g_ptr_array_index(checksums, j); + if (fwupd_checksum_guess_kind(checksum) == checksum_types[i]) + return checksum; + } + } + return NULL; +} + +static gchar * +fwupd_get_os_release_filename(void) +{ +#ifndef _WIN32 + const gchar *sysconfdir = g_getenv("FWUPD_SYSCONFDIR"); + g_autofree gchar *fn1 = NULL; + + /* override */ + if (sysconfdir != NULL) { + g_autofree gchar *fn2 = g_build_filename(sysconfdir, "os-release", NULL); + if (g_file_test(fn2, G_FILE_TEST_EXISTS)) + return g_steal_pointer(&fn2); + } + + /* host locations */ + if (g_strcmp0(sysconfdir, "/etc") != 0) { + g_autofree gchar *fn2 = g_strdup("/etc/os-release"); + if (g_file_test(fn2, G_FILE_TEST_EXISTS)) + return g_steal_pointer(&fn2); + } + fn1 = g_strdup("/usr/lib/os-release"); + if (g_file_test(fn1, G_FILE_TEST_EXISTS)) + return g_steal_pointer(&fn1); +#endif + return NULL; +} + +/** + * fwupd_get_os_release: + * @error: (nullable): optional return location for an error + * + * Loads information from the system os-release file. + * + * Returns: (transfer container) (element-type utf8 utf8): keys from os-release + * + * Since: 1.0.7 + **/ +GHashTable * +fwupd_get_os_release(GError **error) +{ + g_autofree gchar *buf = NULL; + g_autofree gchar *filename = NULL; + g_auto(GStrv) lines = NULL; + g_autoptr(GHashTable) hash = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + filename = fwupd_get_os_release_filename(); + if (filename == NULL) { +#if defined(_WIN32) + /* TODO: Read the Windows version */ + g_hash_table_insert(hash, g_strdup("OS"), g_strdup("Windows")); +#elif defined(__NetBSD__) + g_hash_table_insert(hash, g_strdup("OS"), g_strdup("NetBSD")); +#elif defined(__OpenBSD__) + g_hash_table_insert(hash, g_strdup("OS"), g_strdup("OpenBSD")); +#endif + if (g_hash_table_size(hash) > 0) + return g_steal_pointer(&hash); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "No os-release found"); + return NULL; + } + + /* load each line */ + if (!g_file_get_contents(filename, &buf, NULL, error)) + return NULL; + lines = g_strsplit(buf, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + gsize len, off = 0; + g_auto(GStrv) split = NULL; + + /* split up into sections */ + split = g_strsplit(lines[i], "=", 2); + if (g_strv_length(split) < 2) + continue; + + /* remove double quotes if set both ends */ + len = strlen(split[1]); + if (len == 0) + continue; + if (split[1][0] == '\"' && split[1][len - 1] == '\"') { + off++; + len -= 2; + } + g_hash_table_insert(hash, g_strdup(split[0]), g_strndup(split[1] + off, len)); + } + return g_steal_pointer(&hash); +} + +static gchar * +fwupd_build_user_agent_os_release(void) +{ + const gchar *keys[] = {"NAME", "VERSION_ID", "VARIANT", NULL}; + g_autoptr(GHashTable) hash = NULL; + g_autoptr(GPtrArray) ids_os = g_ptr_array_new(); + + /* get all keys */ + hash = fwupd_get_os_release(NULL); + if (hash == NULL) + return NULL; + + /* create an array of the keys that exist */ + for (guint i = 0; keys[i] != NULL; i++) { + const gchar *value = g_hash_table_lookup(hash, keys[i]); + if (value != NULL) + g_ptr_array_add(ids_os, (gpointer)value); + } + if (ids_os->len == 0) + return NULL; + g_ptr_array_add(ids_os, NULL); + return g_strjoinv(" ", (gchar **)ids_os->pdata); +} + +/** + * fwupd_build_user_agent_system: (skip): + **/ +gchar * +fwupd_build_user_agent_system(void) +{ +#ifdef HAVE_UTSNAME_H + struct utsname name_tmp; +#endif + g_autofree gchar *locale = NULL; + g_autofree gchar *os_release = NULL; + g_autoptr(GPtrArray) ids = g_ptr_array_new_with_free_func(g_free); + + /* system, architecture and kernel, e.g. "Linux i686 4.14.5" */ +#ifdef HAVE_UTSNAME_H + memset(&name_tmp, 0, sizeof(struct utsname)); + if (uname(&name_tmp) >= 0) { + g_ptr_array_add(ids, + g_strdup_printf("%s %s %s", + name_tmp.sysname, + name_tmp.machine, + name_tmp.release)); + } +#endif + + /* current locale, e.g. "en-gb" */ +#ifdef HAVE_LC_MESSAGES + locale = g_strdup(setlocale(LC_MESSAGES, NULL)); +#endif + if (locale != NULL) { + g_strdelimit(locale, ".", '\0'); + g_strdelimit(locale, "_", '-'); + g_ptr_array_add(ids, g_steal_pointer(&locale)); + } + + /* OS release, e.g. "Fedora 27 Workstation" */ + os_release = fwupd_build_user_agent_os_release(); + if (os_release != NULL) + g_ptr_array_add(ids, g_steal_pointer(&os_release)); + + /* convert to string */ + if (ids->len == 0) + return NULL; + g_ptr_array_add(ids, NULL); + return g_strjoinv("; ", (gchar **)ids->pdata); +} + +/** + * fwupd_build_user_agent: + * @package_name: (not nullable): client program name, e.g. `gnome-software` + * @package_version: (not nullable): client program version, e.g. `3.28.1` + * + * Builds a user-agent to use for the download. + * + * Supplying harmless details to the server means it knows more about each + * client. This allows the web service to respond in a different way, for + * instance sending a different metadata file for old versions of fwupd, or + * returning an error for Solaris machines. + * + * Before freaking out about theoretical privacy implications, much more data + * than this is sent to each and every website you visit. + * + * Rather that using this function you should use [method@Client.set_user_agent_for_package] + * which uses the *runtime* version of the daemon rather than the *build-time* + * version. + * + * Returns: a string, e.g. `foo/0.1 (Linux i386 4.14.5; en; Fedora 27) fwupd/1.0.3` + * + * Since: 1.0.3 + **/ +gchar * +fwupd_build_user_agent(const gchar *package_name, const gchar *package_version) +{ + g_autoptr(GString) str = g_string_new(NULL); + g_autofree gchar *system = NULL; + + g_return_val_if_fail(package_name != NULL, NULL); + g_return_val_if_fail(package_version != NULL, NULL); + + /* application name and version */ + g_string_append_printf(str, "%s/%s", package_name, package_version); + + /* system information */ + system = fwupd_build_user_agent_system(); + if (system != NULL) + g_string_append_printf(str, " (%s)", system); + + /* platform, which in our case is just fwupd */ + if (g_strcmp0(package_name, "fwupd") != 0) + g_string_append_printf(str, " fwupd/%s", PACKAGE_VERSION); + + /* success */ + return g_string_free(g_steal_pointer(&str), FALSE); +} + +/** + * fwupd_build_machine_id: + * @salt: (nullable): optional salt + * @error: (nullable): optional return location for an error + * + * Gets a salted hash of the /etc/machine-id contents. This can be used to + * identify a specific machine. It is not possible to recover the original + * machine-id from the machine-hash. + * + * Returns: the SHA256 machine hash, or %NULL if the ID is not present + * + * Since: 1.0.4 + **/ +gchar * +fwupd_build_machine_id(const gchar *salt, GError **error) +{ + const gchar *fn = NULL; + g_autofree gchar *buf = NULL; + g_auto(GStrv) fns = g_new0(gchar *, 6); + g_autoptr(GChecksum) csum = NULL; + gsize sz = 0; + + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* one of these has to exist */ + fns[0] = g_build_filename(FWUPD_SYSCONFDIR, "machine-id", NULL); + fns[1] = g_build_filename(FWUPD_LOCALSTATEDIR, "lib", "dbus", "machine-id", NULL); + fns[2] = g_strdup("/etc/machine-id"); + fns[3] = g_strdup("/var/lib/dbus/machine-id"); + fns[4] = g_strdup("/var/db/dbus/machine-id"); + for (guint i = 0; fns[i] != NULL; i++) { + if (g_file_test(fns[i], G_FILE_TEST_EXISTS)) { + fn = fns[i]; + break; + } + } + if (fn == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "The machine-id is not present"); + return NULL; + } + if (!g_file_get_contents(fn, &buf, &sz, error)) + return NULL; + if (sz == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "The machine-id is present but unset"); + return NULL; + } + csum = g_checksum_new(G_CHECKSUM_SHA256); + if (salt != NULL) + g_checksum_update(csum, (const guchar *)salt, (gssize)strlen(salt)); + g_checksum_update(csum, (const guchar *)buf, (gssize)sz); + return g_strdup(g_checksum_get_string(csum)); +} + +static void +fwupd_build_history_report_json_metadata_device(JsonBuilder *builder, FwupdDevice *dev) +{ + FwupdRelease *rel = fwupd_device_get_release_default(dev); + GHashTable *metadata = fwupd_release_get_metadata(rel); + g_autoptr(GList) keys = NULL; + + /* add each metadata value */ + keys = g_hash_table_get_keys(metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(metadata, key); + json_builder_set_member_name(builder, key); + json_builder_add_string_value(builder, value); + } +} + +static void +fwupd_build_history_report_json_device(JsonBuilder *builder, FwupdDevice *dev) +{ + FwupdRelease *rel = fwupd_device_get_release_default(dev); + GChecksumType checksum_types[] = {G_CHECKSUM_SHA256, G_CHECKSUM_SHA1, 0}; + GPtrArray *checksums; + GPtrArray *guids; + + /* identify the firmware used */ + checksums = fwupd_release_get_checksums(rel); + for (guint i = 0; checksum_types[i] != 0; i++) { + const gchar *checksum = fwupd_checksum_get_by_kind(checksums, checksum_types[i]); + if (checksum != NULL) { + json_builder_set_member_name(builder, "Checksum"); + json_builder_add_string_value(builder, checksum); + break; + } + } + + /* 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); + } + + /* allow matching the specific component */ + json_builder_set_member_name(builder, "ReleaseId"); + json_builder_add_string_value(builder, fwupd_release_get_id(rel)); + + /* 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)); + if (fwupd_device_get_update_error(dev) != NULL) { + 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 */ + guids = fwupd_device_get_guids(dev); + if (guids->len > 0) { + json_builder_set_member_name(builder, "Guid"); + json_builder_begin_array(builder); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + json_builder_add_string_value(builder, guid); + } + json_builder_end_array(builder); + } + + json_builder_set_member_name(builder, "Plugin"); + json_builder_add_string_value(builder, fwupd_device_get_plugin(dev)); + + /* report what we're trying to update *from* and *to* */ + json_builder_set_member_name(builder, "VersionOld"); + json_builder_add_string_value(builder, fwupd_device_get_version(dev)); + json_builder_set_member_name(builder, "VersionNew"); + json_builder_add_string_value(builder, fwupd_release_get_version(rel)); + + /* to know the state of the dev we're trying to update */ + json_builder_set_member_name(builder, "Flags"); + json_builder_add_int_value(builder, fwupd_device_get_flags(dev)); + + /* to know when the update tried to happen, and how soon after boot */ + json_builder_set_member_name(builder, "Created"); + json_builder_add_int_value(builder, fwupd_device_get_created(dev)); + json_builder_set_member_name(builder, "Modified"); + json_builder_add_int_value(builder, fwupd_device_get_modified(dev)); + + /* add saved metadata to the report */ + json_builder_set_member_name(builder, "Metadata"); + json_builder_begin_object(builder); + fwupd_build_history_report_json_metadata_device(builder, dev); + json_builder_end_object(builder); +} + +static gboolean +fwupd_build_history_report_json_metadata(JsonBuilder *builder, GError **error) +{ + g_autoptr(GHashTable) hash = NULL; + struct { + const gchar *key; + const gchar *val; + } distro_kv[] = {{"ID", "DistroId"}, + {"VERSION_ID", "DistroVersion"}, + {"VARIANT_ID", "DistroVariant"}, + {NULL, NULL}}; + + /* get all required os-release keys */ + hash = fwupd_get_os_release(error); + if (hash == NULL) + return FALSE; + for (guint i = 0; distro_kv[i].key != NULL; i++) { + const gchar *tmp = g_hash_table_lookup(hash, distro_kv[i].key); + if (tmp != NULL) { + json_builder_set_member_name(builder, distro_kv[i].val); + json_builder_add_string_value(builder, tmp); + } + } + return TRUE; +} + +/** + * fwupd_build_history_report_json: + * @devices: (element-type FwupdDevice): devices + * @error: (nullable): optional return location for an error + * + * Builds a JSON report for the list of devices. No filtering is done on the + * @devices array, and it is expected that the caller will filter to something + * sane, e.g. %FWUPD_DEVICE_FLAG_REPORTED at the bare minimum. + * + * Returns: a string, or %NULL if the ID is not present + * + * Since: 1.0.4 + **/ +gchar * +fwupd_build_history_report_json(GPtrArray *devices, GError **error) +{ + gchar *data; + g_autofree gchar *machine_id = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + g_return_val_if_fail(devices != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* get a hash that represents the machine */ + machine_id = fwupd_build_machine_id("fwupd", error); + if (machine_id == NULL) + return NULL; + + /* create header */ + builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "ReportVersion"); + json_builder_add_int_value(builder, 2); + json_builder_set_member_name(builder, "MachineId"); + json_builder_add_string_value(builder, machine_id); + + /* this is system metadata not stored in the database */ + json_builder_set_member_name(builder, "Metadata"); + json_builder_begin_object(builder); + if (!fwupd_build_history_report_json_metadata(builder, error)) + return NULL; + json_builder_end_object(builder); + + /* add each device */ + json_builder_set_member_name(builder, "Reports"); + 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_build_history_report_json_device(builder, dev); + 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); + 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 NULL; + } + 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: GUID flags, e.g. %FWUPD_GUID_FLAG_MIXED_ENDIAN + * + * Returns a text GUID of mixed or BE endian for a packed buffer. + * + * Returns: a new GUID string + * + * 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", + (guint)GUINT32_FROM_LE(gnat.a), + (guint)GUINT16_FROM_LE(gnat.b), + (guint)GUINT16_FROM_LE(gnat.c), + (guint)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", + (guint)GUINT32_FROM_BE(gnat.a), + (guint)GUINT16_FROM_BE(gnat.b), + (guint)GUINT16_FROM_BE(gnat.c), + (guint)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: (not nullable): a GUID, e.g. `00112233-4455-6677-8899-aabbccddeeff` + * @guid: (nullable): a #fwupd_guid_t, or NULL to just check the GUID + * @flags: GUID flags, e.g. %FWUPD_GUID_FLAG_MIXED_ENDIAN + * @error: (nullable): optional return location for an error + * + * 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); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* split into sections */ + if (strlen(guidstr) != 36) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "GUID 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, + "GUID 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, + "GUID 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: GUID flags, 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) +{ + gsize digestlen = 20; + guint8 hash[20]; + fwupd_guid_t uu_new; + g_autoptr(GChecksum) csum = NULL; + const fwupd_guid_t uu_default = {0x6b, + 0xa7, + 0xb8, + 0x10, + 0x9d, + 0xad, + 0x11, + 0xd1, + 0x80, + 0xb4, + 0x00, + 0xc0, + 0x4f, + 0xd4, + 0x30, + 0xc8}; + const fwupd_guid_t uu_microso = {0x70, 0xff, 0xd8, 0x12, 0x4c, 0x7f, 0x4c, 0x7d}; + const fwupd_guid_t *uu_namespace = &uu_default; + + 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) + uu_namespace = &uu_microso; + + /* 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_device_id_is_valid: + * @device_id: string to check, e.g. `d3fae86d95e5d56626129d00e332c4b8dac95442` + * + * Checks the string is a valid non-partial device ID. It is important to note + * that the wildcard ID of `*` is not considered a valid ID in this function and + * the client must check for this manually if this should be allowed. + * + * Returns: %TRUE if @guid was a fwupd device ID, %FALSE otherwise + * + * Since: 1.4.1 + **/ +gboolean +fwupd_device_id_is_valid(const gchar *device_id) +{ + if (device_id == NULL) + return FALSE; + if (strlen(device_id) != 40) + return FALSE; + for (guint i = 0; device_id[i] != '\0'; i++) { + gchar tmp = device_id[i]; + /* isalnum isn't case specific */ + if ((tmp < 'a' || tmp > 'f') && (tmp < '0' || tmp > '9')) + return FALSE; + } + return TRUE; +} + +/** + * 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) +{ + const gchar zeroguid[] = {"00000000-0000-0000-0000-000000000000"}; + + /* sanity check */ + if (guid == NULL) + return FALSE; + + /* check for dashes and hexdigits in the right place */ + for (guint i = 0; i < sizeof(zeroguid) - 1; i++) { + if (guid[i] == '\0') + return FALSE; + if (zeroguid[i] == '-') { + if (guid[i] != '-') + return FALSE; + continue; + } + if (!g_ascii_isxdigit(guid[i])) + return FALSE; + } + + /* longer than required */ + if (guid[sizeof(zeroguid) - 1] != '\0') + return FALSE; + + /* not valid */ + return g_strcmp0(guid, zeroguid) != 0; +} + +/** + * 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); +} + +/** + * fwupd_hash_kv_to_variant: (skip): + **/ +GVariant * +fwupd_hash_kv_to_variant(GHashTable *hash) +{ + GVariantBuilder builder; + g_autoptr(GList) keys = g_hash_table_get_keys(hash); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(hash, key); + g_variant_builder_add(&builder, "{ss}", key, value); + } + return g_variant_builder_end(&builder); +} + +/** + * fwupd_variant_to_hash_kv: (skip): + **/ +GHashTable * +fwupd_variant_to_hash_kv(GVariant *dict) +{ + GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + GVariantIter iter; + const gchar *key; + const gchar *value; + g_variant_iter_init(&iter, dict); + while (g_variant_iter_loop(&iter, "{&s&s}", &key, &value)) + g_hash_table_insert(hash, g_strdup(key), g_strdup(value)); + return hash; +} + +static void +fwupd_input_stream_read_bytes_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + GByteArray *bufarr; + GInputStream *stream = G_INPUT_STREAM(source); + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = G_TASK(user_data); +#if GLIB_CHECK_VERSION(2, 64, 0) + guint8 *buf; + gsize bufsz = 0; +#endif + + /* read buf */ + bytes = g_input_stream_read_bytes_finish(stream, res, &error); + if (bytes == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* add bytes to buffer */ + bufarr = g_task_get_task_data(task); + if (g_bytes_get_size(bytes) > 0) { + GCancellable *cancellable = g_task_get_cancellable(task); + g_debug("add %u", (guint)g_bytes_get_size(bytes)); + g_byte_array_append(bufarr, g_bytes_get_data(bytes, NULL), g_bytes_get_size(bytes)); + g_input_stream_read_bytes_async(g_steal_pointer(&stream), + 256 * 1024, /* bigger chunk */ + G_PRIORITY_DEFAULT, + cancellable, + fwupd_input_stream_read_bytes_cb, + g_steal_pointer(&task)); + return; + } + + /* success */ +#if GLIB_CHECK_VERSION(2, 64, 0) + buf = g_byte_array_steal(bufarr, &bufsz); + g_task_return_pointer(task, g_bytes_new_take(buf, bufsz), (GDestroyNotify)g_bytes_unref); +#else + g_task_return_pointer(task, + g_bytes_new(bufarr->data, bufarr->len), + (GDestroyNotify)g_bytes_unref); +#endif +} + +/** + * fwupd_input_stream_read_bytes_async: (skip): + **/ +void +fwupd_input_stream_read_bytes_async(GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail(G_IS_INPUT_STREAM(stream)); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + + task = g_task_new(stream, cancellable, callback, callback_data); + g_task_set_task_data(task, g_byte_array_new(), (GDestroyNotify)g_byte_array_unref); + g_input_stream_read_bytes_async(stream, + 64 * 1024, /* small */ + G_PRIORITY_DEFAULT, + cancellable, + fwupd_input_stream_read_bytes_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_input_stream_read_bytes_finish: (skip): + **/ +GBytes * +fwupd_input_stream_read_bytes_finish(GInputStream *stream, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); + g_return_val_if_fail(g_task_is_valid(res, stream), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +#ifdef HAVE_GIO_UNIX +/** + * fwupd_unix_input_stream_from_bytes: (skip): + **/ +GUnixInputStream * +fwupd_unix_input_stream_from_bytes(GBytes *bytes, GError **error) +{ + gint fd; + gssize rc; +#ifndef HAVE_MEMFD_CREATE + gchar tmp_file[] = "/tmp/fwupd.XXXXXX"; +#endif + +#ifdef HAVE_MEMFD_CREATE + fd = memfd_create("fwupd", MFD_CLOEXEC); +#else + /* emulate in-memory file by an unlinked temporary file */ + fd = g_mkstemp(tmp_file); + if (fd != -1) { + rc = g_unlink(tmp_file); + if (rc != 0) { + if (!g_close(fd, error)) { + g_prefix_error(error, "failed to close temporary file: "); + return NULL; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to unlink temporary file"); + return NULL; + } + } +#endif + + if (fd < 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to create memfd"); + return NULL; + } + rc = write(fd, g_bytes_get_data(bytes, NULL), g_bytes_get_size(bytes)); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to write %" G_GSSIZE_FORMAT, + rc); + return NULL; + } + if (lseek(fd, 0, SEEK_SET) < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to seek: %s", + g_strerror(errno)); + return NULL; + } + return G_UNIX_INPUT_STREAM(g_unix_input_stream_new(fd, TRUE)); +} + +/** + * fwupd_unix_input_stream_from_fn: (skip): + **/ +GUnixInputStream * +fwupd_unix_input_stream_from_fn(const gchar *fn, GError **error) +{ + gint fd = open(fn, O_RDONLY); + if (fd < 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "failed to open %s", fn); + return NULL; + } + return G_UNIX_INPUT_STREAM(g_unix_input_stream_new(fd, TRUE)); +} +#endif + +/** + * fwupd_common_json_add_string: (skip): + **/ +void +fwupd_common_json_add_string(JsonBuilder *builder, const gchar *key, const gchar *value) +{ + if (value == NULL) + return; + json_builder_set_member_name(builder, key); + json_builder_add_string_value(builder, value); +} + +/** + * fwupd_common_json_add_int: (skip): + **/ +void +fwupd_common_json_add_int(JsonBuilder *builder, const gchar *key, guint64 value) +{ + json_builder_set_member_name(builder, key); + json_builder_add_int_value(builder, value); +} + +/** + * fwupd_common_json_add_boolean: (skip): + **/ +void +fwupd_common_json_add_boolean(JsonBuilder *builder, const gchar *key, gboolean value) +{ + json_builder_set_member_name(builder, key); + json_builder_add_string_value(builder, value ? "true" : "false"); +} + +/** + * fwupd_common_json_add_stringv: (skip): + **/ +void +fwupd_common_json_add_stringv(JsonBuilder *builder, const gchar *key, gchar **value) +{ + if (value == NULL) + return; + json_builder_set_member_name(builder, key); + json_builder_begin_array(builder); + for (guint i = 0; value[i] != NULL; i++) + json_builder_add_string_value(builder, value[i]); + json_builder_end_array(builder); +} + +/** + * fwupd_pad_kv_str: (skip): + **/ +void +fwupd_pad_kv_str(GString *str, const gchar *key, const gchar *value) +{ + /* ignore */ + if (key == NULL || value == NULL) + return; + g_string_append_printf(str, " %s: ", key); + for (gsize i = strlen(key); i < 20; i++) + g_string_append(str, " "); + g_string_append_printf(str, "%s\n", value); +} + +/** + * fwupd_pad_kv_unx: (skip): + **/ +void +fwupd_pad_kv_unx(GString *str, const gchar *key, guint64 value) +{ + g_autoptr(GDateTime) date = NULL; + g_autofree gchar *tmp = NULL; + + /* ignore */ + if (value == 0) + return; + + date = g_date_time_new_from_unix_utc((gint64)value); + tmp = g_date_time_format(date, "%F"); + fwupd_pad_kv_str(str, key, tmp); +} + +/** + * fwupd_pad_kv_int: (skip): + **/ +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); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-common.h b/fwupd-1.8.6/libfwupd/fwupd-common.h new file mode 100644 index 0000000000000000000000000000000000000000..ede9ce69755d91a89777b5a25bf35a1241f565f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-common.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +/** + * FWUPD_DBUS_PATH: + * + * The dbus path + **/ +#define FWUPD_DBUS_PATH "/" +/** + * FWUPD_DBUS_SERVICE: + * + * The dbus service + **/ +#define FWUPD_DBUS_SERVICE "org.freedesktop.fwupd" +/** + * FWUPD_DBUS_INTERFACE: + * + * The dbus interface + **/ +#define FWUPD_DBUS_INTERFACE "org.freedesktop.fwupd" +/** + * FWUPD_DBUS_P2P_SOCKET_ADDRESS: + * + * The D-Bus socket address when using point-to-point connections. + **/ +#define FWUPD_DBUS_P2P_SOCKET_ADDRESS "tcp:host=localhost,port=1341" + +/** + * FWUPD_DEVICE_ID_ANY: + * + * Wildcard used for matching all device ids in 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); +GChecksumType +fwupd_checksum_guess_kind(const gchar *checksum); +gchar * +fwupd_checksum_format_for_display(const gchar *checksum); +gchar * +fwupd_build_user_agent(const gchar *package_name, const gchar *package_version) + G_DEPRECATED_FOR(fwupd_client_set_user_agent_for_package); +gchar * +fwupd_build_machine_id(const gchar *salt, GError **error); +GHashTable * +fwupd_get_os_release(GError **error); +gchar * +fwupd_build_history_report_json(GPtrArray *devices, GError **error); +gboolean +fwupd_device_id_is_valid(const gchar *device_id); +#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); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-context-test.c b/fwupd-1.8.6/libfwupd/fwupd-context-test.c new file mode 100644 index 0000000000000000000000000000000000000000..54126cf18bd6c47a7dc4e41276180651e6e8c77f --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-context-test.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 Philip Withnall + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +typedef struct { + GApplication *app; + FwupdClient *client; + GThread *main_thread; + GThread *worker_thread; +} FuThreadTestSelf; + +static gboolean +fwupd_thread_test_exit_idle_cb(gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_application_release(self->app); + return G_SOURCE_REMOVE; +} + +static gpointer +fwupd_thread_test_thread_cb(gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_autoptr(GError) error_local = NULL; + g_autoptr(GMainContext) context = g_main_context_new(); + g_autoptr(GMainContextPusher) pusher = g_main_context_pusher_new(context); + + g_assert_nonnull(pusher); + g_message("Calling fwupd_client_get_devices() in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + if (!fwupd_client_connect(self->client, NULL, &error_local)) + g_warning("%s", error_local->message); + g_idle_add(fwupd_thread_test_exit_idle_cb, self); + return NULL; +} + +static gboolean +fwupd_thread_test_idle_cb(gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_message("fwupd_thread_test_idle_cb() in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + self->worker_thread = g_thread_new("worker00", fwupd_thread_test_thread_cb, self); + return G_SOURCE_REMOVE; +} + +static void +fwupd_thread_test_activate_cb(GApplication *app, gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_application_hold(self->app); + g_idle_add(fwupd_thread_test_idle_cb, self); +} + +static void +fwupd_thread_test_notify_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_message("fwupd_thread_test_notify_cb() in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + g_assert_true(g_thread_self() == self->main_thread); + g_assert_null(g_main_context_get_thread_default()); +} + +static gboolean +fwupd_thread_test_has_system_bus(void) +{ + g_autoptr(GDBusConnection) conn = NULL; + conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); + return conn != NULL; +} + +int +main(void) +{ + gint retval; + g_autoptr(FwupdClient) client = fwupd_client_new(); + g_autoptr(GApplication) app = + g_application_new("org.fwupd.ContextTest", G_APPLICATION_NON_UNIQUE); + g_autoptr(GThread) worker_thread = NULL; + FuThreadTestSelf self = { + .app = app, + .client = client, + .worker_thread = worker_thread, + .main_thread = g_thread_self(), + }; + + /* only some of the CI targets have a DBus daemon */ + if (!fwupd_thread_test_has_system_bus()) { + g_message("D-Bus system bus unavailable, skipping tests."); + return 0; + } + g_message("Created FwupdClient in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + g_signal_connect(FWUPD_CLIENT(client), + "notify::status", + G_CALLBACK(fwupd_thread_test_notify_cb), + &self); + g_signal_connect(G_APPLICATION(app), + "activate", + G_CALLBACK(fwupd_thread_test_activate_cb), + &self); + retval = g_application_run(app, 0, NULL); + if (self.worker_thread != NULL) + g_thread_join(g_steal_pointer(&self.worker_thread)); + + return retval; +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-deprecated.h b/fwupd-1.8.6/libfwupd/fwupd-deprecated.h new file mode 100644 index 0000000000000000000000000000000000000000..a3671517a3f4125927ea8d6644c498e100b5067b --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-deprecated.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +G_BEGIN_DECLS + +/* indeed, nothing */ + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-device-private.h b/fwupd-1.8.6/libfwupd/fwupd-device-private.h new file mode 100644 index 0000000000000000000000000000000000000000..3fe7b2dff9cd8455cf6d18d3cd49e92055c497e4 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-device-private.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-device.h" + +G_BEGIN_DECLS + +GVariant * +fwupd_device_to_variant(FwupdDevice *self); +GVariant * +fwupd_device_to_variant_full(FwupdDevice *self, FwupdDeviceFlags flags); +void +fwupd_device_incorporate(FwupdDevice *self, FwupdDevice *donor); +void +fwupd_device_to_json(FwupdDevice *self, JsonBuilder *builder); +void +fwupd_device_to_json_full(FwupdDevice *self, JsonBuilder *builder, FwupdDeviceFlags flags); +gboolean +fwupd_device_from_json(FwupdDevice *self, JsonNode *json_node, GError **error); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-device.c b/fwupd-1.8.6/libfwupd/fwupd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..735a3492702df704e9ef6ee88c472777a66b2d34 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-device.c @@ -0,0 +1,4028 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fwupd-common-private.h" +#include "fwupd-device-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" +#include "fwupd-release-private.h" + +/** + * FwupdDevice: + * + * A physical device on the host with optionally updatable firmware. + * + * See also: [class@FwupdRelease] + */ + +static void +fwupd_device_finalize(GObject *object); + +typedef struct { + gchar *id; + gchar *parent_id; + gchar *composite_id; + guint64 created; + guint64 modified; + guint64 flags; + guint64 problems; + GPtrArray *guids; + GPtrArray *vendor_ids; + GPtrArray *protocols; + GPtrArray *instance_ids; + GPtrArray *icons; + GPtrArray *issues; /* of utf-8 */ + gchar *name; + gchar *serial; + gchar *summary; + gchar *branch; + gchar *description; + gchar *vendor; + gchar *vendor_id; /* for compat only */ + gchar *homepage; + gchar *plugin; + gchar *protocol; + gchar *version; + gchar *version_lowest; + gchar *version_bootloader; + FwupdVersionFormat version_format; + guint64 version_raw; + guint64 version_build_date; + guint64 version_lowest_raw; + guint64 version_bootloader_raw; + GPtrArray *checksums; + GPtrArray *children; + guint32 flashes_left; + guint32 battery_level; + guint32 battery_threshold; + guint32 install_duration; + FwupdUpdateState update_state; + gchar *update_error; + gchar *update_message; + gchar *update_image; + FwupdStatus status; + GPtrArray *releases; + FwupdDevice *parent; /* noref */ +} FwupdDevicePrivate; + +enum { + PROP_0, + PROP_VERSION_FORMAT, + PROP_FLAGS, + PROP_PROTOCOL, + PROP_STATUS, + PROP_PARENT, + PROP_UPDATE_STATE, + PROP_UPDATE_MESSAGE, + PROP_UPDATE_ERROR, + PROP_UPDATE_IMAGE, + PROP_BATTERY_LEVEL, + PROP_BATTERY_THRESHOLD, + PROP_PROBLEMS, + PROP_LAST +}; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdDevice, fwupd_device, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_device_get_instance_private(o)) + +#define FWUPD_BATTERY_THRESHOLD_DEFAULT 10 /* % */ + +/** + * fwupd_device_get_checksums: + * @self: a #FwupdDevice + * + * Gets the device checksums. + * + * Returns: (element-type utf8) (transfer none): the checksums, which may be empty + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_device_get_checksums(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->checksums; +} + +/** + * fwupd_device_add_checksum: + * @self: a #FwupdDevice + * @checksum: (not nullable): the device checksum + * + * Adds a device checksum. + * + * Since: 0.9.3 + **/ +void +fwupd_device_add_checksum(FwupdDevice *self, const gchar *checksum) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(checksum != NULL); + 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; + } + g_ptr_array_add(priv->checksums, g_strdup(checksum)); +} + +/** + * fwupd_device_get_issues: + * @self: a #FwupdDevice + * + * Gets the list of issues currently affecting this device. + * + * Returns: (element-type utf8) (transfer none): the issues, which may be empty + * + * Since: 1.7.6 + **/ +GPtrArray * +fwupd_device_get_issues(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->issues; +} + +/** + * fwupd_device_add_issue: + * @self: a #FwupdDevice + * @issue: (not nullable): the update issue, e.g. `CVE-2019-12345` + * + * Adds an current issue to this device. + * + * Since: 1.7.6 + **/ +void +fwupd_device_add_issue(FwupdDevice *self, const gchar *issue) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(issue != NULL); + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *issue_tmp = g_ptr_array_index(priv->issues, i); + if (g_strcmp0(issue_tmp, issue) == 0) + return; + } + g_ptr_array_add(priv->issues, g_strdup(issue)); +} + +/** + * fwupd_device_get_children: + * @self: a #FwupdDevice + * + * Gets the device children. These can only be assigned using fwupd_device_set_parent(). + * + * Returns: (element-type FwupdDevice) (transfer none): the children, which may be empty + * + * Since: 1.3.7 + **/ +GPtrArray * +fwupd_device_get_children(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->children; +} + +/** + * fwupd_device_get_summary: + * @self: a #FwupdDevice + * + * Gets the device summary. + * + * Returns: the device summary, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_summary(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->summary; +} + +/** + * fwupd_device_set_summary: + * @self: a #FwupdDevice + * @summary: (nullable): the device one line summary + * + * Sets the device summary. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_summary(FwupdDevice *self, const gchar *summary) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->summary, summary) == 0) + return; + + g_free(priv->summary); + priv->summary = g_strdup(summary); +} + +/** + * fwupd_device_get_branch: + * @self: a #FwupdDevice + * + * Gets the current device branch. + * + * Returns: the device branch, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_device_get_branch(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->branch; +} + +/** + * fwupd_device_set_branch: + * @self: a #FwupdDevice + * @branch: (nullable): the device one line branch + * + * Sets the current device branch. + * + * Since: 1.5.0 + **/ +void +fwupd_device_set_branch(FwupdDevice *self, const gchar *branch) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->branch, branch) == 0) + return; + + g_free(priv->branch); + priv->branch = g_strdup(branch); +} + +/** + * fwupd_device_get_serial: + * @self: 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 *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->serial; +} + +/** + * fwupd_device_set_serial: + * @self: a #FwupdDevice + * @serial: (nullable): the device serial number + * + * Sets the serial number for the device. + * + * Since: 1.1.2 + **/ +void +fwupd_device_set_serial(FwupdDevice *self, const gchar *serial) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->serial, serial) == 0) + return; + + g_free(priv->serial); + priv->serial = g_strdup(serial); +} + +/** + * fwupd_device_get_id: + * @self: a #FwupdDevice + * + * Gets the ID. + * + * Returns: the ID, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_id(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->id; +} + +/** + * fwupd_device_set_id: + * @self: a #FwupdDevice + * @id: (nullable): the device ID, e.g. `USB:foo` + * + * Sets the ID. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_id(FwupdDevice *self, const gchar *id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + g_free(priv->id); + priv->id = g_strdup(id); +} + +/** + * fwupd_device_get_parent_id: + * @self: a #FwupdDevice + * + * Gets the parent ID. + * + * Returns: the parent ID, or %NULL if unset + * + * Since: 1.0.8 + **/ +const gchar * +fwupd_device_get_parent_id(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->parent_id; +} + +/** + * fwupd_device_set_parent_id: + * @self: a #FwupdDevice + * @parent_id: (nullable): the device ID, e.g. `USB:foo` + * + * Sets the parent ID. + * + * Since: 1.0.8 + **/ +void +fwupd_device_set_parent_id(FwupdDevice *self, const gchar *parent_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->parent_id, parent_id) == 0) + return; + + g_free(priv->parent_id); + priv->parent_id = g_strdup(parent_id); +} + +/** + * fwupd_device_get_composite_id: + * @self: a #FwupdDevice + * + * Gets the composite ID, falling back to the device ID if unset. + * + * The composite ID will be the same value for all parent, child and sibling + * devices. + * + * Returns: (nullable): the composite ID + * + * Since: 1.6.0 + **/ +const gchar * +fwupd_device_get_composite_id(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + if (priv->composite_id != NULL) + return priv->composite_id; + return priv->id; +} + +/** + * fwupd_device_set_composite_id: + * @self: a #FwupdDevice + * @composite_id: (nullable): a device ID + * + * Sets the composite ID, which is usually a SHA1 hash of a grandparent or + * parent device. + * + * Since: 1.6.0 + **/ +void +fwupd_device_set_composite_id(FwupdDevice *self, const gchar *composite_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->composite_id, composite_id) == 0) + return; + + g_free(priv->composite_id); + priv->composite_id = g_strdup(composite_id); +} + +/** + * fwupd_device_get_parent: + * @self: a #FwupdDevice + * + * Gets the parent. + * + * Returns: (transfer none): the parent device, or %NULL if unset + * + * Since: 1.0.8 + **/ +FwupdDevice * +fwupd_device_get_parent(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->parent; +} + +/** + * fwupd_device_get_root: + * @self: a #FwupdDevice + * + * Gets the device root. + * + * Returns: (transfer none): the root device, or %NULL if unset + * + * Since: 1.7.4 + **/ +FwupdDevice * +fwupd_device_get_root(FwupdDevice *self) +{ + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + while (1) { + FwupdDevicePrivate *priv = GET_PRIVATE(self); + if (priv->parent == NULL) + break; + self = priv->parent; + } + return self; +} + +/** + * fwupd_device_set_parent: + * @self: a #FwupdDevice + * @parent: (nullable): another #FwupdDevice + * + * Sets the parent. Only used internally. + * + * Since: 1.0.8 + **/ +void +fwupd_device_set_parent(FwupdDevice *self, FwupdDevice *parent) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(self != parent); + + if (priv->parent != NULL) + g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + if (parent != NULL) + 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(self, parent != NULL ? fwupd_device_get_id(parent) : NULL); +} + +static void +fwupd_device_child_finalized_cb(gpointer data, GObject *where_the_object_was) +{ + FwupdDevice *self = FWUPD_DEVICE(data); + g_critical("FuDevice child %p was finalized while still having parent %s [%s]!", + where_the_object_was, + fwupd_device_get_name(self), + fwupd_device_get_id(self)); +} + +/** + * fwupd_device_add_child: + * @self: a #FwupdDevice + * @child: Another #FwupdDevice + * + * Adds a child device. An child device is logically linked to the primary + * device in some way. + * + * NOTE: You should never call this function from user code, it is for daemon + * use only. Only use fwupd_device_set_parent() to set up a logical tree. + * + * Since: 1.5.1 + **/ +void +fwupd_device_add_child(FwupdDevice *self, FwupdDevice *child) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(FWUPD_IS_DEVICE(child)); + g_return_if_fail(self != child); + + /* add if the child does not already exist */ + for (guint i = 0; i < priv->children->len; i++) { + FwupdDevice *devtmp = g_ptr_array_index(priv->children, i); + if (devtmp == child) + return; + } + g_object_weak_ref(G_OBJECT(child), fwupd_device_child_finalized_cb, self); + g_ptr_array_add(priv->children, g_object_ref(child)); +} + +/** + * fwupd_device_remove_child: + * @self: a #FwupdDevice + * @child: Another #FwupdDevice + * + * Removes a child device. + * + * NOTE: You should never call this function from user code, it is for daemon + * use only. + * + * Since: 1.6.2 + **/ +void +fwupd_device_remove_child(FwupdDevice *self, FwupdDevice *child) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + /* remove if the child exists */ + for (guint i = 0; i < priv->children->len; i++) { + FwupdDevice *child_tmp = g_ptr_array_index(priv->children, i); + if (child_tmp == child) { + g_object_weak_unref(G_OBJECT(child), fwupd_device_child_finalized_cb, self); + g_ptr_array_remove_index(priv->children, i); + return; + } + } +} + +/** + * fwupd_device_get_guids: + * @self: a #FwupdDevice + * + * Gets the GUIDs. + * + * Returns: (element-type utf8) (transfer none): the GUIDs + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_device_get_guids(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->guids; +} + +/** + * fwupd_device_has_guid: + * @self: a #FwupdDevice + * @guid: (not nullable): the GUID, e.g. `2082b5e0-7a64-478a-b1b2-e3404fab6dad` + * + * Finds out if the device has this specific GUID. + * + * Returns: %TRUE if the GUID is found + * + * Since: 0.9.3 + **/ +gboolean +fwupd_device_has_guid(FwupdDevice *self, const gchar *guid) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + g_return_val_if_fail(guid != NULL, FALSE); + + for (guint i = 0; i < priv->guids->len; i++) { + const gchar *guid_tmp = g_ptr_array_index(priv->guids, i); + if (g_strcmp0(guid, guid_tmp) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_device_add_guid: + * @self: a #FwupdDevice + * @guid: the GUID, e.g. `2082b5e0-7a64-478a-b1b2-e3404fab6dad` + * + * Adds the GUID if it does not already exist. + * + * Since: 0.9.3 + **/ +void +fwupd_device_add_guid(FwupdDevice *self, const gchar *guid) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(guid != NULL); + if (fwupd_device_has_guid(self, guid)) + return; + g_ptr_array_add(priv->guids, g_strdup(guid)); +} + +/** + * fwupd_device_get_guid_default: + * @self: a #FwupdDevice + * + * Gets the default GUID. + * + * Returns: the GUID, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_guid_default(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + if (priv->guids->len == 0) + return NULL; + return g_ptr_array_index(priv->guids, 0); +} + +/** + * fwupd_device_get_instance_ids: + * @self: a #FwupdDevice + * + * Gets the instance IDs. + * + * Returns: (element-type utf8) (transfer none): the instance IDs + * + * Since: 1.2.5 + **/ +GPtrArray * +fwupd_device_get_instance_ids(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->instance_ids; +} + +/** + * fwupd_device_has_instance_id: + * @self: a #FwupdDevice + * @instance_id: (not nullable): the instance ID, e.g. `PCI\VEN_10EC&DEV_525A` + * + * Finds out if the device has this specific instance ID. + * + * Returns: %TRUE if the instance ID is found + * + * Since: 1.2.5 + **/ +gboolean +fwupd_device_has_instance_id(FwupdDevice *self, const gchar *instance_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + g_return_val_if_fail(instance_id != NULL, 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: + * @self: a #FwupdDevice + * @instance_id: (not nullable): the instance ID, e.g. `PCI\VEN_10EC&DEV_525A` + * + * Adds the instance ID if it does not already exist. + * + * Since: 1.2.5 + **/ +void +fwupd_device_add_instance_id(FwupdDevice *self, const gchar *instance_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(instance_id != NULL); + if (fwupd_device_has_instance_id(self, instance_id)) + return; + g_ptr_array_add(priv->instance_ids, g_strdup(instance_id)); +} + +/** + * fwupd_device_get_icons: + * @self: a #FwupdDevice + * + * Gets the icon names to use for the device. + * + * NOTE: Icons specified without a full path are stock icons and should + * be loaded from the users icon theme. + * + * Returns: (element-type utf8) (transfer none): an array of icon names + * + * Since: 0.9.8 + **/ +GPtrArray * +fwupd_device_get_icons(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->icons; +} + +/** + * fwupd_device_has_icon: + * @self: a #FwupdDevice + * @icon: the icon name, e.g. `input-mouse` or `/usr/share/icons/foo.png` + * + * Finds out if the device has this specific icon. + * + * Returns: %TRUE if the icon name is found + * + * Since: 1.6.2 + **/ +gboolean +fwupd_device_has_icon(FwupdDevice *self, const gchar *icon) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + for (guint i = 0; i < priv->icons->len; i++) { + const gchar *icon_tmp = g_ptr_array_index(priv->icons, i); + if (g_strcmp0(icon, icon_tmp) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_device_add_icon: + * @self: a #FwupdDevice + * @icon: (not nullable): the icon name, e.g. `input-mouse` or `/usr/share/icons/foo.png` + * + * Adds the icon name if it does not already exist. + * + * Since: 0.9.8 + **/ +void +fwupd_device_add_icon(FwupdDevice *self, const gchar *icon) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(icon != NULL); + if (fwupd_device_has_icon(self, icon)) + return; + g_ptr_array_add(priv->icons, g_strdup(icon)); +} + +/** + * fwupd_device_get_name: + * @self: a #FwupdDevice + * + * Gets the device name. + * + * Returns: the device name, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_name(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->name; +} + +/** + * fwupd_device_set_name: + * @self: a #FwupdDevice + * @name: (nullable): the device name, e.g. `ColorHug2` + * + * Sets the device name. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_name(FwupdDevice *self, const gchar *name) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->name, name) == 0) + return; + + g_free(priv->name); + priv->name = g_strdup(name); +} + +/** + * fwupd_device_get_vendor: + * @self: a #FwupdDevice + * + * Gets the device vendor. + * + * Returns: the device vendor, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_vendor(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->vendor; +} + +/** + * fwupd_device_set_vendor: + * @self: a #FwupdDevice + * @vendor: (nullable): the vendor + * + * Sets the device vendor. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_vendor(FwupdDevice *self, const gchar *vendor) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->vendor, vendor) == 0) + return; + + g_free(priv->vendor); + priv->vendor = g_strdup(vendor); +} + +/** + * fwupd_device_get_vendor_id: + * @self: a #FwupdDevice + * + * Gets the combined device vendor ID. + * + * Returns: the device vendor, e.g. 'USB:0x1234|PCI:0x5678', or %NULL if unset + * + * Since: 0.9.4 + * + * Deprecated: 1.5.5: Use fwupd_device_get_vendor_ids() instead. + **/ +const gchar * +fwupd_device_get_vendor_id(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->vendor_id; +} + +/** + * fwupd_device_set_vendor_id: + * @self: a #FwupdDevice + * @vendor_id: (not nullable): the vendor ID, e.g. 'USB:0x1234' or 'USB:0x1234|PCI:0x5678' + * + * Sets the device vendor ID. + * + * Since: 0.9.4 + * + * Deprecated: 1.5.5: Use fwupd_device_add_vendor_id() instead. + **/ +void +fwupd_device_set_vendor_id(FwupdDevice *self, const gchar *vendor_id) +{ + g_auto(GStrv) vendor_ids = NULL; + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(vendor_id != NULL); + + /* add all */ + vendor_ids = g_strsplit(vendor_id, "|", -1); + for (guint i = 0; vendor_ids[i] != NULL; i++) + fwupd_device_add_vendor_id(self, vendor_ids[i]); +} + +/** + * fwupd_device_get_vendor_ids: + * @self: a #FwupdDevice + * + * Gets the device vendor ID. + * + * Returns: (element-type utf8) (transfer none): the device vendor ID + * + * Since: 1.5.5 + **/ +GPtrArray * +fwupd_device_get_vendor_ids(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->vendor_ids; +} + +/** + * fwupd_device_has_vendor_id: + * @self: a #FwupdDevice + * @vendor_id: (not nullable): the vendor ID, e.g. 'USB:0x1234' + * + * Finds out if the device has this specific vendor ID. + * + * Returns: %TRUE if the vendor ID is found + * + * Since: 1.5.5 + **/ +gboolean +fwupd_device_has_vendor_id(FwupdDevice *self, const gchar *vendor_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + g_return_val_if_fail(vendor_id != NULL, FALSE); + + for (guint i = 0; i < priv->vendor_ids->len; i++) { + const gchar *vendor_id_tmp = g_ptr_array_index(priv->vendor_ids, i); + if (g_strcmp0(vendor_id, vendor_id_tmp) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_device_add_vendor_id: + * @self: a #FwupdDevice + * @vendor_id: (not nullable): the ID, e.g. 'USB:0x1234' + * + * Adds a device vendor ID. + * + * Since: 1.5.5 + **/ +void +fwupd_device_add_vendor_id(FwupdDevice *self, const gchar *vendor_id) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_auto(GStrv) vendor_ids_tmp = NULL; + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(vendor_id != NULL); + + if (fwupd_device_has_vendor_id(self, vendor_id)) + return; + g_ptr_array_add(priv->vendor_ids, g_strdup(vendor_id)); + + /* build for compatibility */ + vendor_ids_tmp = g_new0(gchar *, priv->vendor_ids->len + 1); + for (guint i = 0; i < priv->vendor_ids->len; i++) { + const gchar *vendor_id_tmp = g_ptr_array_index(priv->vendor_ids, i); + vendor_ids_tmp[i] = g_strdup(vendor_id_tmp); + } + g_free(priv->vendor_id); + priv->vendor_id = g_strjoinv("|", vendor_ids_tmp); +} + +/** + * fwupd_device_get_description: + * @self: a #FwupdDevice + * + * Gets the device description in AppStream markup format. + * + * Returns: the device description, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_description(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->description; +} + +/** + * fwupd_device_set_description: + * @self: a #FwupdDevice + * @description: (nullable): the description in AppStream markup format + * + * Sets the device description. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_description(FwupdDevice *self, const gchar *description) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->description, description) == 0) + return; + + g_free(priv->description); + priv->description = g_strdup(description); +} + +/** + * fwupd_device_get_version: + * @self: a #FwupdDevice + * + * Gets the device version. + * + * Returns: the device version, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_version(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->version; +} + +/** + * fwupd_device_set_version: + * @self: a #FwupdDevice + * @version: (nullable): the device version, e.g. `1.2.3` + * + * Sets the device version. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_version(FwupdDevice *self, const gchar *version) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->version, version) == 0) + return; + + g_free(priv->version); + priv->version = g_strdup(version); +} + +/** + * fwupd_device_get_version_lowest: + * @self: a #FwupdDevice + * + * Gets the lowest version of firmware the device will accept. + * + * Returns: the device version_lowest, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_version_lowest(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->version_lowest; +} + +/** + * fwupd_device_set_version_lowest: + * @self: a #FwupdDevice + * @version_lowest: (nullable): the version + * + * Sets the lowest version of firmware the device will accept. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_version_lowest(FwupdDevice *self, const gchar *version_lowest) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->version_lowest, version_lowest) == 0) + return; + + g_free(priv->version_lowest); + priv->version_lowest = g_strdup(version_lowest); +} + +/** + * fwupd_device_get_version_lowest_raw: + * @self: a #FwupdDevice + * + * Gets the lowest version of firmware the device will accept in raw format. + * + * Returns: integer version number, or %0 if unset + * + * Since: 1.4.0 + **/ +guint64 +fwupd_device_get_version_lowest_raw(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->version_lowest_raw; +} + +/** + * fwupd_device_set_version_lowest_raw: + * @self: a #FwupdDevice + * @version_lowest_raw: the raw hardware version + * + * Sets the raw lowest version number from the hardware before converted to a string. + * + * Since: 1.4.0 + **/ +void +fwupd_device_set_version_lowest_raw(FwupdDevice *self, guint64 version_lowest_raw) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->version_lowest_raw = version_lowest_raw; +} + +/** + * fwupd_device_get_version_bootloader: + * @self: a #FwupdDevice + * + * Gets the version of the bootloader. + * + * Returns: the device version_bootloader, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_device_get_version_bootloader(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->version_bootloader; +} + +/** + * fwupd_device_set_version_bootloader: + * @self: a #FwupdDevice + * @version_bootloader: (nullable): the version + * + * Sets the bootloader version. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_version_bootloader(FwupdDevice *self, const gchar *version_bootloader) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->version_bootloader, version_bootloader) == 0) + return; + + g_free(priv->version_bootloader); + priv->version_bootloader = g_strdup(version_bootloader); +} + +/** + * fwupd_device_get_version_bootloader_raw: + * @self: a #FwupdDevice + * + * Gets the bootloader version of firmware the device will accept in raw format. + * + * Returns: integer version number, or %0 if unset + * + * Since: 1.4.0 + **/ +guint64 +fwupd_device_get_version_bootloader_raw(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->version_bootloader_raw; +} + +/** + * fwupd_device_set_version_bootloader_raw: + * @self: a #FwupdDevice + * @version_bootloader_raw: the raw hardware version + * + * Sets the raw bootloader version number from the hardware before converted to a string. + * + * Since: 1.4.0 + **/ +void +fwupd_device_set_version_bootloader_raw(FwupdDevice *self, guint64 version_bootloader_raw) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->version_bootloader_raw = version_bootloader_raw; +} + +/** + * fwupd_device_get_flashes_left: + * @self: a #FwupdDevice + * + * Gets the number of flash cycles left on the device + * + * Returns: the flash cycles left, or %NULL if unset + * + * Since: 0.9.3 + **/ +guint32 +fwupd_device_get_flashes_left(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->flashes_left; +} + +/** + * fwupd_device_set_flashes_left: + * @self: a #FwupdDevice + * @flashes_left: the description + * + * Sets the number of flash cycles left on the device + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_flashes_left(FwupdDevice *self, guint32 flashes_left) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->flashes_left = flashes_left; +} + +/** + * fwupd_device_get_battery_level: + * @self: a #FwupdDevice + * + * Returns the battery level. + * + * Returns: value in percent + * + * Since: 1.8.1 + **/ +guint32 +fwupd_device_get_battery_level(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), G_MAXUINT); + return priv->battery_level; +} + +/** + * fwupd_device_set_battery_level: + * @self: a #FwupdDevice + * @battery_level: the percentage value + * + * Sets the battery level, or %FWUPD_BATTERY_LEVEL_INVALID. + * + * Setting this allows fwupd to show a warning if the device change is too low + * to perform the update. + * + * Since: 1.8.1 + **/ +void +fwupd_device_set_battery_level(FwupdDevice *self, guint32 battery_level) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(battery_level <= FWUPD_BATTERY_LEVEL_INVALID); + + if (priv->battery_level == battery_level) + return; + priv->battery_level = battery_level; + g_object_notify(G_OBJECT(self), "battery-level"); +} + +/** + * fwupd_device_get_battery_threshold: + * @self: a #FwupdDevice + * + * Returns the battery threshold under which a firmware update cannot be + * performed. + * + * If fwupd_device_set_battery_threshold() has not been used, a default value is + * used instead. + * + * Returns: value in percent + * + * Since: 1.8.1 + **/ +guint32 +fwupd_device_get_battery_threshold(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FWUPD_BATTERY_LEVEL_INVALID); + + /* default value */ + if (priv->battery_threshold == FWUPD_BATTERY_LEVEL_INVALID) + return FWUPD_BATTERY_THRESHOLD_DEFAULT; + + return priv->battery_threshold; +} + +/** + * fwupd_device_set_battery_threshold: + * @self: a #FwupdDevice + * @battery_threshold: the percentage value + * + * Sets the battery level, or %FWUPD_BATTERY_LEVEL_INVALID for the default. + * + * Setting this allows fwupd to show a warning if the device change is too low + * to perform the update. + * + * Since: 1.8.1 + **/ +void +fwupd_device_set_battery_threshold(FwupdDevice *self, guint32 battery_threshold) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(battery_threshold <= FWUPD_BATTERY_LEVEL_INVALID); + + if (priv->battery_threshold == battery_threshold) + return; + priv->battery_threshold = battery_threshold; + g_object_notify(G_OBJECT(self), "battery-threshold"); +} + +/** + * fwupd_device_get_install_duration: + * @self: 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 *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->install_duration; +} + +/** + * fwupd_device_set_install_duration: + * @self: 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 *self, guint32 duration) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->install_duration = duration; +} + +/** + * fwupd_device_get_plugin: + * @self: a #FwupdDevice + * + * Gets the plugin that created the device. + * + * Returns: the plugin name, or %NULL if unset + * + * Since: 1.0.0 + **/ +const gchar * +fwupd_device_get_plugin(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->plugin; +} + +/** + * fwupd_device_set_plugin: + * @self: a #FwupdDevice + * @plugin: (nullable): the plugin name, e.g. `colorhug` + * + * Sets the plugin that created the device. + * + * Since: 1.0.0 + **/ +void +fwupd_device_set_plugin(FwupdDevice *self, const gchar *plugin) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->plugin, plugin) == 0) + return; + + g_free(priv->plugin); + priv->plugin = g_strdup(plugin); +} + +/** + * fwupd_device_get_protocol: + * @self: a #FwupdDevice + * + * Gets the protocol name that the device uses for updating. + * + * Returns: the protocol name, or %NULL if unset + * + * Since: 1.3.6 + * + * Deprecated: 1.5.8: Use fwupd_device_get_protocols() instead. + **/ +const gchar * +fwupd_device_get_protocol(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->protocol; +} + +/** + * fwupd_device_set_protocol: + * @self: a #FwupdDevice + * @protocol: (not nullable): the protocol name, e.g. `com.hughski.colorhug` + * + * Sets the protocol name that is used to update the device. + * + * Since: 1.3.6 + * + * Deprecated: 1.5.8: Use fwupd_device_add_protocol() instead. + **/ +void +fwupd_device_set_protocol(FwupdDevice *self, const gchar *protocol) +{ + g_auto(GStrv) protocols = NULL; + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(protocol != NULL); + + /* add all */ + protocols = g_strsplit(protocol, "|", -1); + for (guint i = 0; protocols[i] != NULL; i++) + fwupd_device_add_protocol(self, protocols[i]); +} + +/** + * fwupd_device_get_protocols: + * @self: a #FwupdDevice + * + * Gets the device protocol names. + * + * Returns: (element-type utf8) (transfer none): the device protocol names + * + * Since: 1.5.8 + **/ +GPtrArray * +fwupd_device_get_protocols(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->protocols; +} + +/** + * fwupd_device_has_protocol: + * @self: a #FwupdDevice + * @protocol: (not nullable): the protocol name, e.g. `com.hughski.colorhug` + * + * Finds out if the device has this specific protocol name. + * + * Returns: %TRUE if the protocol name is found + * + * Since: 1.5.8 + **/ +gboolean +fwupd_device_has_protocol(FwupdDevice *self, const gchar *protocol) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + g_return_val_if_fail(protocol != NULL, FALSE); + + for (guint i = 0; i < priv->protocols->len; i++) { + const gchar *protocol_tmp = g_ptr_array_index(priv->protocols, i); + if (g_strcmp0(protocol, protocol_tmp) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_device_add_protocol: + * @self: a #FwupdDevice + * @protocol: (not nullable): the protocol name, e.g. `com.hughski.colorhug` + * + * Adds a device protocol name. + * + * Since: 1.5.8 + **/ +void +fwupd_device_add_protocol(FwupdDevice *self, const gchar *protocol) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_auto(GStrv) protocols_tmp = NULL; + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(protocol != NULL); + + if (fwupd_device_has_protocol(self, protocol)) + return; + g_ptr_array_add(priv->protocols, g_strdup(protocol)); + + /* build for compatibility */ + protocols_tmp = g_new0(gchar *, priv->protocols->len + 1); + for (guint i = 0; i < priv->protocols->len; i++) { + const gchar *protocol_tmp = g_ptr_array_index(priv->protocols, i); + protocols_tmp[i] = g_strdup(protocol_tmp); + } + g_free(priv->protocol); + priv->protocol = g_strjoinv("|", protocols_tmp); +} + +/** + * fwupd_device_get_flags: + * @self: a #FwupdDevice + * + * Gets device flags. + * + * Returns: device flags, or 0 if unset + * + * Since: 0.9.3 + **/ +guint64 +fwupd_device_get_flags(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->flags; +} + +/** + * fwupd_device_set_flags: + * @self: a #FwupdDevice + * @flags: device flags, e.g. %FWUPD_DEVICE_FLAG_REQUIRE_AC + * + * Sets device flags. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_flags(FwupdDevice *self, guint64 flags) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (priv->flags == flags) + return; + priv->flags = flags; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_device_add_flag: + * @self: a #FwupdDevice + * @flag: the #FwupdDeviceFlags + * + * Adds a specific device flag to the device. + * + * Since: 0.9.3 + **/ +void +fwupd_device_add_flag(FwupdDevice *self, FwupdDeviceFlags flag) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (flag == 0) + return; + if ((priv->flags | flag) == priv->flags) + return; + priv->flags |= flag; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_device_remove_flag: + * @self: a #FwupdDevice + * @flag: the #FwupdDeviceFlags + * + * Removes a specific device flag from the device. + * + * Since: 0.9.3 + **/ +void +fwupd_device_remove_flag(FwupdDevice *self, FwupdDeviceFlags flag) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (flag == 0) + return; + if ((priv->flags & flag) == 0) + return; + priv->flags &= ~flag; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_device_has_flag: + * @self: a #FwupdDevice + * @flag: the #FwupdDeviceFlags + * + * Finds if the device has a specific device flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 0.9.3 + **/ +gboolean +fwupd_device_has_flag(FwupdDevice *self, FwupdDeviceFlags flag) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_device_get_problems: + * @self: a #FwupdDevice + * + * Gets device problems. + * + * Returns: device problems, or 0 if unset + * + * Since: 1.8.1 + **/ +guint64 +fwupd_device_get_problems(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->problems; +} + +/** + * fwupd_device_set_problems: + * @self: a #FwupdDevice + * @problems: device problems, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW + * + * Sets device problems. + * + * Since: 1.8.1 + **/ +void +fwupd_device_set_problems(FwupdDevice *self, guint64 problems) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (priv->problems == problems) + return; + priv->problems = problems; + g_object_notify(G_OBJECT(self), "problems"); +} + +/** + * fwupd_device_add_problem: + * @self: a #FwupdDevice + * @problem: the #FwupdDeviceProblem, e.g. #FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW + * + * Adds a specific device problem kind to the device. + * + * Since: 1.8.1 + **/ +void +fwupd_device_add_problem(FwupdDevice *self, FwupdDeviceProblem problem) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (problem == FWUPD_DEVICE_PROBLEM_NONE) + return; + if (fwupd_device_has_problem(self, problem)) + return; + priv->problems |= problem; + g_object_notify(G_OBJECT(self), "problems"); +} + +/** + * fwupd_device_remove_problem: + * @self: a #FwupdDevice + * @problem: the #FwupdDeviceProblem, e.g. #FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW + * + * Removes a specific device problem kind from the device. + * + * Since: 1.8.1 + **/ +void +fwupd_device_remove_problem(FwupdDevice *self, FwupdDeviceProblem problem) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (problem == FWUPD_DEVICE_PROBLEM_NONE) + return; + if (!fwupd_device_has_problem(self, problem)) + return; + priv->problems &= ~problem; + g_object_notify(G_OBJECT(self), "problems"); +} + +/** + * fwupd_device_has_problem: + * @self: a #FwupdDevice + * @problem: the #FwupdDeviceProblem + * + * Finds if the device has a specific device problem kind. + * + * Returns: %TRUE if the problem is set + * + * Since: 1.8.1 + **/ +gboolean +fwupd_device_has_problem(FwupdDevice *self, FwupdDeviceProblem problem) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + return (priv->problems & problem) > 0; +} + +/** + * fwupd_device_get_created: + * @self: a #FwupdDevice + * + * Gets when the device was created. + * + * Returns: the UNIX time, or 0 if unset + * + * Since: 0.9.3 + **/ +guint64 +fwupd_device_get_created(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->created; +} + +/** + * fwupd_device_set_created: + * @self: a #FwupdDevice + * @created: the UNIX time + * + * Sets when the device was created. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_created(FwupdDevice *self, guint64 created) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->created = created; +} + +/** + * fwupd_device_get_modified: + * @self: a #FwupdDevice + * + * Gets when the device was modified. + * + * Returns: the UNIX time, or 0 if unset + * + * Since: 0.9.3 + **/ +guint64 +fwupd_device_get_modified(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->modified; +} + +/** + * fwupd_device_set_modified: + * @self: a #FwupdDevice + * @modified: the UNIX time + * + * Sets when the device was modified. + * + * Since: 0.9.3 + **/ +void +fwupd_device_set_modified(FwupdDevice *self, guint64 modified) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->modified = modified; +} + +/** + * 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); + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(FWUPD_IS_DEVICE(donor)); + + fwupd_device_add_flag(self, priv_donor->flags); + fwupd_device_add_problem(self, priv_donor->problems); + 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->version_build_date == 0) + fwupd_device_set_version_build_date(self, priv_donor->version_build_date); + if (priv->flashes_left == 0) + fwupd_device_set_flashes_left(self, priv_donor->flashes_left); + if (priv->battery_level == FWUPD_BATTERY_LEVEL_INVALID) + fwupd_device_set_battery_level(self, priv_donor->battery_level); + if (priv->battery_threshold == FWUPD_BATTERY_LEVEL_INVALID) + fwupd_device_set_battery_threshold(self, priv_donor->battery_threshold); + if (priv->install_duration == 0) + fwupd_device_set_install_duration(self, priv_donor->install_duration); + if (priv->update_state == FWUPD_UPDATE_STATE_UNKNOWN) + 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->composite_id == NULL) + fwupd_device_set_composite_id(self, priv_donor->composite_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->branch == NULL) + fwupd_device_set_branch(self, priv_donor->branch); + if (priv->vendor == NULL) + fwupd_device_set_vendor(self, priv_donor->vendor); + for (guint i = 0; i < priv_donor->vendor_ids->len; i++) { + const gchar *tmp = g_ptr_array_index(priv_donor->vendor_ids, i); + fwupd_device_add_vendor_id(self, tmp); + } + if (priv->plugin == NULL) + fwupd_device_set_plugin(self, priv_donor->plugin); + for (guint i = 0; i < priv_donor->protocols->len; i++) { + const gchar *tmp = g_ptr_array_index(priv_donor->protocols, i); + fwupd_device_add_protocol(self, tmp); + } + 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->update_image == NULL) + fwupd_device_set_update_image(self, priv_donor->update_image); + 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); + if (priv->version_raw == 0) + fwupd_device_set_version_raw(self, priv_donor->version_raw); + if (priv->version_lowest_raw == 0) + fwupd_device_set_version_lowest_raw(self, priv_donor->version_lowest_raw); + if (priv->version_bootloader_raw == 0) + fwupd_device_set_version_bootloader_raw(self, priv_donor->version_bootloader_raw); + 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: + * @self: a #FwupdDevice + * @flags: device flags + * + * Serialize the device data. + * Optionally provides additional data based upon flags + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.1.2 + **/ +GVariant * +fwupd_device_to_variant_full(FwupdDevice *self, FwupdDeviceFlags flags) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + + /* create an array with all the metadata in */ + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + if (priv->id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DEVICE_ID, + g_variant_new_string(priv->id)); + } + if (priv->parent_id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_PARENT_DEVICE_ID, + g_variant_new_string(priv->parent_id)); + } + if (priv->composite_id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_COMPOSITE_ID, + g_variant_new_string(priv->composite_id)); + } + if (priv->guids->len > 0) { + const gchar *const *tmp = (const gchar *const *)priv->guids->pdata; + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_GUID, + g_variant_new_strv(tmp, priv->guids->len)); + } + if (priv->icons->len > 0) { + const gchar *const *tmp = (const gchar *const *)priv->icons->pdata; + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_ICON, + g_variant_new_strv(tmp, priv->icons->len)); + } + if (priv->name != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string(priv->name)); + } + if (priv->vendor != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VENDOR, + g_variant_new_string(priv->vendor)); + } + if (priv->vendor_ids->len > 0) { + g_autoptr(GString) str = g_string_new(NULL); + for (guint i = 0; i < priv->vendor_ids->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->vendor_ids, i); + g_string_append_printf(str, "%s|", tmp); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VENDOR_ID, + g_variant_new_string(str->str)); + } + if (priv->flags > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_FLAGS, + g_variant_new_uint64(priv->flags)); + } + if (priv->problems > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_PROBLEMS, + g_variant_new_uint64(priv->problems)); + } + if (priv->created > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CREATED, + g_variant_new_uint64(priv->created)); + } + if (priv->modified > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_MODIFIED, + g_variant_new_uint64(priv->modified)); + } + if (priv->version_build_date > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION_BUILD_DATE, + g_variant_new_uint64(priv->version_build_date)); + } + + if (priv->description != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DESCRIPTION, + g_variant_new_string(priv->description)); + } + if (priv->summary != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_SUMMARY, + g_variant_new_string(priv->summary)); + } + if (priv->branch != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BRANCH, + g_variant_new_string(priv->branch)); + } + if (priv->checksums->len > 0) { + g_autoptr(GString) str = g_string_new(""); + for (guint i = 0; i < priv->checksums->len; i++) { + const gchar *checksum = g_ptr_array_index(priv->checksums, i); + g_string_append_printf(str, "%s,", checksum); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CHECKSUM, + g_variant_new_string(str->str)); + } + if (priv->plugin != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_PLUGIN, + g_variant_new_string(priv->plugin)); + } + if (priv->protocols->len > 0) { + g_autoptr(GString) str = g_string_new(NULL); + for (guint i = 0; i < priv->protocols->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->protocols, i); + g_string_append_printf(str, "%s|", tmp); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_PROTOCOL, + g_variant_new_string(str->str)); + } + if (priv->issues->len > 0) { + g_autofree const gchar **strv = g_new0(const gchar *, priv->issues->len + 1); + for (guint i = 0; i < priv->issues->len; i++) + strv[i] = (const gchar *)g_ptr_array_index(priv->issues, i); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_ISSUES, + g_variant_new_strv(strv, -1)); + } + if (priv->version != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION, + g_variant_new_string(priv->version)); + } + if (priv->version_lowest != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION_LOWEST, + g_variant_new_string(priv->version_lowest)); + } + if (priv->version_bootloader != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION_BOOTLOADER, + g_variant_new_string(priv->version_bootloader)); + } + if (priv->version_raw > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION_RAW, + g_variant_new_uint64(priv->version_raw)); + } + if (priv->version_lowest_raw > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION_LOWEST_RAW, + g_variant_new_uint64(priv->version_lowest_raw)); + } + if (priv->version_bootloader_raw > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW, + g_variant_new_uint64(priv->version_raw)); + } + if (priv->flashes_left > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_FLASHES_LEFT, + g_variant_new_uint32(priv->flashes_left)); + } + if (priv->battery_level != FWUPD_BATTERY_LEVEL_INVALID) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BATTERY_LEVEL, + g_variant_new_uint32(priv->battery_level)); + } + if (priv->battery_threshold != FWUPD_BATTERY_LEVEL_INVALID) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BATTERY_THRESHOLD, + g_variant_new_uint32(priv->battery_threshold)); + } + 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_image != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_UPDATE_IMAGE, + g_variant_new_string(priv->update_image)); + } + 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->status != FWUPD_STATUS_UNKNOWN) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_STATUS, + g_variant_new_uint32(priv->status)); + } + 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) { + g_autofree GVariant **children = NULL; + children = g_new0(GVariant *, priv->releases->len); + for (guint i = 0; i < priv->releases->len; i++) { + FwupdRelease *release = g_ptr_array_index(priv->releases, i); + children[i] = fwupd_release_to_variant(release); + } + g_variant_builder_add( + &builder, + "{sv}", + FWUPD_RESULT_KEY_RELEASE, + g_variant_new_array(G_VARIANT_TYPE("a{sv}"), children, priv->releases->len)); + } + return g_variant_new("a{sv}", &builder); +} + +/** + * fwupd_device_to_variant: + * @self: a #FwupdDevice + * + * Serialize the device data omitting sensitive fields + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.0.0 + **/ +GVariant * +fwupd_device_to_variant(FwupdDevice *self) +{ + return fwupd_device_to_variant_full(self, FWUPD_DEVICE_FLAG_NONE); +} + +static void +fwupd_device_from_key_value(FwupdDevice *self, const gchar *key, GVariant *value) +{ + if (g_strcmp0(key, FWUPD_RESULT_KEY_RELEASE) == 0) { + GVariantIter iter; + GVariant *child; + g_variant_iter_init(&iter, value); + while ((child = g_variant_iter_next_value(&iter))) { + g_autoptr(FwupdRelease) release = fwupd_release_from_variant(child); + if (release != NULL) + fwupd_device_add_release(self, release); + g_variant_unref(child); + } + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DEVICE_ID) == 0) { + fwupd_device_set_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_PARENT_DEVICE_ID) == 0) { + fwupd_device_set_parent_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_COMPOSITE_ID) == 0) { + fwupd_device_set_composite_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FLAGS) == 0) { + fwupd_device_set_flags(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_PROBLEMS) == 0) { + fwupd_device_set_problems(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_CREATED) == 0) { + fwupd_device_set_created(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_MODIFIED) == 0) { + fwupd_device_set_modified(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_BUILD_DATE) == 0) { + fwupd_device_set_version_build_date(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_GUID) == 0) { + g_autofree const gchar **guids = g_variant_get_strv(value, NULL); + for (guint i = 0; guids != NULL && guids[i] != NULL; i++) + fwupd_device_add_guid(self, 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(self, 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++) + fwupd_device_add_icon(self, icons[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_device_set_name(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VENDOR) == 0) { + fwupd_device_set_vendor(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VENDOR_ID) == 0) { + g_auto(GStrv) vendor_ids = NULL; + vendor_ids = g_strsplit(g_variant_get_string(value, NULL), "|", -1); + for (guint i = 0; vendor_ids[i] != NULL; i++) + fwupd_device_add_vendor_id(self, vendor_ids[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_SERIAL) == 0) { + fwupd_device_set_serial(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_SUMMARY) == 0) { + fwupd_device_set_summary(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BRANCH) == 0) { + fwupd_device_set_branch(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { + fwupd_device_set_description(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_CHECKSUM) == 0) { + const gchar *checksums = g_variant_get_string(value, NULL); + if (checksums != NULL) { + g_auto(GStrv) split = g_strsplit(checksums, ",", -1); + for (guint i = 0; split[i] != NULL; i++) + fwupd_device_add_checksum(self, split[i]); + } + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_PLUGIN) == 0) { + fwupd_device_set_plugin(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_PROTOCOL) == 0) { + g_auto(GStrv) protocols = NULL; + protocols = g_strsplit(g_variant_get_string(value, NULL), "|", -1); + for (guint i = 0; protocols[i] != NULL; i++) + fwupd_device_add_protocol(self, protocols[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_ISSUES) == 0) { + g_autofree const gchar **strv = g_variant_get_strv(value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_device_add_issue(self, strv[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION) == 0) { + fwupd_device_set_version(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_LOWEST) == 0) { + fwupd_device_set_version_lowest(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_BOOTLOADER) == 0) { + fwupd_device_set_version_bootloader(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FLASHES_LEFT) == 0) { + fwupd_device_set_flashes_left(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BATTERY_LEVEL) == 0) { + fwupd_device_set_battery_level(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BATTERY_THRESHOLD) == 0) { + fwupd_device_set_battery_threshold(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_INSTALL_DURATION) == 0) { + fwupd_device_set_install_duration(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_ERROR) == 0) { + fwupd_device_set_update_error(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_MESSAGE) == 0) { + fwupd_device_set_update_message(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_IMAGE) == 0) { + fwupd_device_set_update_image(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_STATE) == 0) { + fwupd_device_set_update_state(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_STATUS) == 0) { + fwupd_device_set_status(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_FORMAT) == 0) { + fwupd_device_set_version_format(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_RAW) == 0) { + fwupd_device_set_version_raw(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_LOWEST_RAW) == 0) { + fwupd_device_set_version_lowest_raw(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW) == 0) { + fwupd_device_set_version_bootloader_raw(self, g_variant_get_uint64(value)); + return; + } +} + +static void +fwupd_pad_kv_dfl(GString *str, const gchar *key, guint64 device_flags) +{ + g_autoptr(GString) tmp = g_string_new(""); + for (guint i = 0; i < 64; i++) { + if ((device_flags & ((guint64)1 << i)) == 0) + continue; + g_string_append_printf(tmp, "%s|", fwupd_device_flag_to_string((guint64)1 << i)); + } + if (tmp->len == 0) { + g_string_append(tmp, fwupd_device_flag_to_string(0)); + } else { + g_string_truncate(tmp, tmp->len - 1); + } + fwupd_pad_kv_str(str, key, tmp->str); +} + +static void +fwupd_device_pad_kv_problems(GString *str, const gchar *key, guint64 device_problems) +{ + g_autoptr(GString) tmp = g_string_new(""); + for (guint i = 0; i < 64; i++) { + if ((device_problems & ((guint64)1 << i)) == 0) + continue; + g_string_append_printf(tmp, "%s|", fwupd_device_problem_to_string((guint64)1 << i)); + } + if (tmp->len == 0) { + g_string_append(tmp, fwupd_device_problem_to_string(0)); + } else { + g_string_truncate(tmp, tmp->len - 1); + } + fwupd_pad_kv_str(str, key, tmp->str); +} + +/** + * fwupd_device_get_update_state: + * @self: a #FwupdDevice + * + * Gets the update state. + * + * Returns: the update state, or %FWUPD_UPDATE_STATE_UNKNOWN if unset + * + * Since: 0.9.8 + **/ +FwupdUpdateState +fwupd_device_get_update_state(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FWUPD_UPDATE_STATE_UNKNOWN); + return priv->update_state; +} + +/** + * fwupd_device_set_update_state: + * @self: a #FwupdDevice + * @update_state: the state, e.g. %FWUPD_UPDATE_STATE_PENDING + * + * Sets the update state. + * + * Since: 0.9.8 + **/ +void +fwupd_device_set_update_state(FwupdDevice *self, FwupdUpdateState update_state) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (priv->update_state == update_state) + return; + priv->update_state = update_state; + g_object_notify(G_OBJECT(self), "update-state"); +} + +/** + * fwupd_device_get_version_format: + * @self: a #FwupdDevice + * + * Gets the version format. + * + * Returns: the version format, or %FWUPD_VERSION_FORMAT_UNKNOWN if unset + * + * Since: 1.2.9 + **/ +FwupdVersionFormat +fwupd_device_get_version_format(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FWUPD_VERSION_FORMAT_UNKNOWN); + return priv->version_format; +} + +/** + * fwupd_device_set_version_format: + * @self: a #FwupdDevice + * @version_format: the version format, e.g. %FWUPD_VERSION_FORMAT_NUMBER + * + * Sets the version format. + * + * Since: 1.2.9 + **/ +void +fwupd_device_set_version_format(FwupdDevice *self, FwupdVersionFormat version_format) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->version_format = version_format; +} + +/** + * fwupd_device_get_version_raw: + * @self: a #FwupdDevice + * + * Gets the raw version number from the hardware before converted to a string. + * + * Returns: the hardware version, or 0 if unset + * + * Since: 1.3.6 + **/ +guint64 +fwupd_device_get_version_raw(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->version_raw; +} + +/** + * fwupd_device_set_version_raw: + * @self: a #FwupdDevice + * @version_raw: the raw hardware version + * + * Sets the raw version number from the hardware before converted to a string. + * + * Since: 1.3.6 + **/ +void +fwupd_device_set_version_raw(FwupdDevice *self, guint64 version_raw) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->version_raw = version_raw; +} + +/** + * fwupd_device_get_version_build_date: + * @self: a #FwupdDevice + * + * Gets the date when the firmware was built. + * + * Returns: the UNIX time, or 0 if unset + * + * Since: 1.6.2 + **/ +guint64 +fwupd_device_get_version_build_date(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->version_build_date; +} + +/** + * fwupd_device_set_version_build_date: + * @self: a #FwupdDevice + * @version_build_date: the UNIX time + * + * Sets the date when the firmware was built. + * + * Since: 1.6.2 + **/ +void +fwupd_device_set_version_build_date(FwupdDevice *self, guint64 version_build_date) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + priv->version_build_date = version_build_date; +} + +/** + * fwupd_device_get_update_message: + * @self: a #FwupdDevice + * + * Gets the update message string. + * + * Returns: the update message string, or %NULL if unset + * + * Since: 1.2.4 + **/ +const gchar * +fwupd_device_get_update_message(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->update_message; +} + +/** + * fwupd_device_set_update_message: + * @self: a #FwupdDevice + * @update_message: (nullable): the update message string + * + * Sets the update message string. + * + * Since: 1.2.4 + **/ +void +fwupd_device_set_update_message(FwupdDevice *self, const gchar *update_message) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->update_message, update_message) == 0) + return; + + g_free(priv->update_message); + priv->update_message = g_strdup(update_message); + g_object_notify(G_OBJECT(self), "update-message"); +} + +/** + * fwupd_device_get_update_image: + * @self: a #FwupdDevice + * + * Gets the update image URL. + * + * Returns: the update image URL, or %NULL if unset + * + * Since: 1.4.5 + **/ +const gchar * +fwupd_device_get_update_image(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->update_image; +} + +/** + * fwupd_device_set_update_image: + * @self: a #FwupdDevice + * @update_image: (nullable): the update image URL + * + * Sets the update image URL. + * + * Since: 1.4.5 + **/ +void +fwupd_device_set_update_image(FwupdDevice *self, const gchar *update_image) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->update_image, update_image) == 0) + return; + + g_free(priv->update_image); + priv->update_image = g_strdup(update_image); + g_object_notify(G_OBJECT(self), "update-image"); +} + +/** + * fwupd_device_get_update_error: + * @self: a #FwupdDevice + * + * Gets the update error string. + * + * Returns: the update error string, or %NULL if unset + * + * Since: 0.9.8 + **/ +const gchar * +fwupd_device_get_update_error(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->update_error; +} + +/** + * fwupd_device_set_update_error: + * @self: a #FwupdDevice + * @update_error: (nullable): the update error string + * + * Sets the update error string. + * + * Since: 0.9.8 + **/ +void +fwupd_device_set_update_error(FwupdDevice *self, const gchar *update_error) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->update_error, update_error) == 0) + return; + + g_free(priv->update_error); + priv->update_error = g_strdup(update_error); + g_object_notify(G_OBJECT(self), "update-error"); +} + +/** + * fwupd_device_get_release_default: + * @self: a #FwupdDevice + * + * Gets the default release for this device. + * + * Returns: (transfer none): the #FwupdRelease, or %NULL if not set + * + * Since: 0.9.8 + **/ +FwupdRelease * +fwupd_device_get_release_default(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + if (priv->releases->len == 0) + return NULL; + return FWUPD_RELEASE(g_ptr_array_index(priv->releases, 0)); +} + +/** + * fwupd_device_get_releases: + * @self: a #FwupdDevice + * + * Gets all the releases for this device. + * + * Returns: (transfer none) (element-type FwupdRelease): array of releases + * + * Since: 0.9.8 + **/ +GPtrArray * +fwupd_device_get_releases(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + return priv->releases; +} + +/** + * fwupd_device_add_release: + * @self: a #FwupdDevice + * @release: (not nullable): a release + * + * Adds a release for this device. + * + * Since: 0.9.8 + **/ +void +fwupd_device_add_release(FwupdDevice *self, FwupdRelease *release) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(FWUPD_IS_RELEASE(release)); + g_ptr_array_add(priv->releases, g_object_ref(release)); +} + +/** + * fwupd_device_get_status: + * @self: a #FwupdDevice + * + * Returns what the device is currently doing. + * + * Returns: the status value, e.g. %FWUPD_STATUS_DEVICE_WRITE + * + * Since: 1.4.0 + **/ +FwupdStatus +fwupd_device_get_status(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), 0); + return priv->status; +} + +/** + * fwupd_device_set_status: + * @self: a #FwupdDevice + * @status: the status value, e.g. %FWUPD_STATUS_DEVICE_WRITE + * + * Sets what the device is currently doing. + * + * Since: 1.4.0 + **/ +void +fwupd_device_set_status(FwupdDevice *self, FwupdStatus status) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_DEVICE(self)); + if (priv->status == status) + return; + priv->status = status; + g_object_notify(G_OBJECT(self), "status"); +} + +static void +fwupd_pad_kv_ups(GString *str, const gchar *key, FwupdUpdateState value) +{ + if (value == FWUPD_UPDATE_STATE_UNKNOWN) + return; + fwupd_pad_kv_str(str, key, fwupd_update_state_to_string(value)); +} + +/** + * fwupd_device_to_json_full: + * @self: a #FwupdDevice + * @builder: (not nullable): a JSON builder + * @flags: device flags + * + * Adds a fwupd device to a JSON builder + * Optionally provides additional data based upon flags + * + * Since: 1.8.2 + **/ +void +fwupd_device_to_json_full(FwupdDevice *self, JsonBuilder *builder, FwupdDeviceFlags flags) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_DEVICE(self)); + g_return_if_fail(builder != NULL); + + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DEVICE_ID, priv->id); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_PARENT_DEVICE_ID, priv->parent_id); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_COMPOSITE_ID, priv->composite_id); + if (flags & FWUPD_DEVICE_FLAG_TRUSTED && priv->instance_ids->len > 0) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_INSTANCE_IDS); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(priv->instance_ids, i); + json_builder_add_string_value(builder, instance_id); + } + json_builder_end_array(builder); + } + 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); + } + if (flags & FWUPD_DEVICE_FLAG_TRUSTED) + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_SERIAL, priv->serial); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_SUMMARY, priv->summary); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_BRANCH, priv->branch); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_PROTOCOL, priv->protocol); + if (priv->protocols->len > 1) { /* --> 0 when bumping API */ + json_builder_set_member_name(builder, "Protocols"); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->protocols->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->protocols, i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } + if (priv->issues->len > 0) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_ISSUES); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->issues, i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } + if (priv->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->problems != FWUPD_DEVICE_PROBLEM_NONE) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_PROBLEMS); + json_builder_begin_array(builder); + for (guint i = 0; i < 64; i++) { + const gchar *tmp; + if ((priv->problems & ((guint64)1 << i)) == 0) + continue; + tmp = fwupd_device_problem_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_common_json_add_string(builder, FWUPD_RESULT_KEY_VENDOR, priv->vendor); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_VENDOR_ID, priv->vendor_id); + if (priv->vendor_ids->len > 1) { /* --> 0 when bumping API */ + json_builder_set_member_name(builder, "VendorIds"); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->vendor_ids->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->vendor_ids, i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_VERSION, priv->version); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_VERSION_LOWEST, + priv->version_lowest); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_VERSION_BOOTLOADER, + priv->version_bootloader); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_VERSION_FORMAT, + fwupd_version_format_to_string(priv->version_format)); + if (priv->flashes_left > 0) + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_FLASHES_LEFT, + priv->flashes_left); + if (priv->battery_level != FWUPD_BATTERY_LEVEL_INVALID) { + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_BATTERY_LEVEL, + priv->battery_level); + } + if (priv->battery_threshold != FWUPD_BATTERY_LEVEL_INVALID) { + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_BATTERY_THRESHOLD, + priv->battery_threshold); + } + if (priv->version_raw > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_VERSION_RAW, priv->version_raw); + if (priv->version_lowest_raw > 0) + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_VERSION_LOWEST_RAW, + priv->version_lowest_raw); + if (priv->version_bootloader_raw > 0) + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW, + priv->version_bootloader_raw); + if (priv->version_build_date > 0) + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_VERSION_BUILD_DATE, + priv->version_build_date); + 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); + } + if (priv->install_duration > 0) { + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_INSTALL_DURATION, + priv->install_duration); + } + if (priv->created > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_CREATED, priv->created); + if (priv->modified > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_MODIFIED, priv->modified); + if (priv->update_state > 0) + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_UPDATE_STATE, + priv->update_state); + if (priv->status > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_STATUS, priv->status); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_UPDATE_ERROR, priv->update_error); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_UPDATE_MESSAGE, + priv->update_message); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->update_image); + 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_from_json: + * @self: a #FwupdDevice + * @json_node: a JSON node + * @error: (nullable): optional return location for an error + * + * Loads a fwupd security attribute from a JSON node. + * + * Returns: %TRUE for success + * + * Since: 1.8.3 + **/ +gboolean +fwupd_device_from_json(FwupdDevice *self, JsonNode *json_node, GError **error) +{ +#if JSON_CHECK_VERSION(1, 6, 0) + JsonObject *obj; + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), FALSE); + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + obj = json_node_get_object(json_node); + + /* this has to exist */ + if (!json_object_has_member(obj, FWUPD_RESULT_KEY_DEVICE_ID)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no %s property in object", + FWUPD_RESULT_KEY_DEVICE_ID); + return FALSE; + } + fwupd_device_set_id(self, json_object_get_string_member(obj, FWUPD_RESULT_KEY_DEVICE_ID)); + + /* also optional */ + if (json_object_has_member(obj, FWUPD_RESULT_KEY_NAME)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_NAME, NULL); + fwupd_device_set_name(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_PARENT_DEVICE_ID)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_PARENT_DEVICE_ID, + NULL); + fwupd_device_set_parent_id(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_COMPOSITE_ID)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_COMPOSITE_ID, + NULL); + fwupd_device_set_composite_id(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_PROTOCOL)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_PROTOCOL, + NULL); + fwupd_device_add_protocol(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_SERIAL)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_SERIAL, NULL); + fwupd_device_set_serial(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_SUMMARY)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_SUMMARY, NULL); + fwupd_device_set_summary(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_DESCRIPTION)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_DESCRIPTION, + NULL); + fwupd_device_set_description(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_BRANCH)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_BRANCH, NULL); + fwupd_device_set_branch(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_PLUGIN)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_PLUGIN, NULL); + fwupd_device_set_plugin(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VENDOR)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_VENDOR, NULL); + fwupd_device_set_vendor(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VENDOR_ID)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_VENDOR_ID, + NULL); + if (tmp != NULL) { + g_auto(GStrv) split = g_strsplit(tmp, "|", -1); + for (guint i = 0; split[i] != NULL; i++) + fwupd_device_add_vendor_id(self, split[i]); + } + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_VERSION, NULL); + fwupd_device_set_version(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_LOWEST)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_VERSION_LOWEST, + NULL); + fwupd_device_set_version_lowest(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_BOOTLOADER)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_VERSION_BOOTLOADER, + NULL); + fwupd_device_set_version_bootloader(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_FORMAT)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_VERSION_FORMAT, + NULL); + fwupd_device_set_version_format(self, fwupd_version_format_from_string(tmp)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_FLASHES_LEFT)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_FLASHES_LEFT, 0); + fwupd_device_set_flashes_left(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_BATTERY_LEVEL)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_BATTERY_LEVEL, 0); + fwupd_device_set_battery_level(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_BATTERY_THRESHOLD)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_BATTERY_THRESHOLD, + 0); + fwupd_device_set_battery_threshold(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_RAW)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_VERSION_RAW, 0); + fwupd_device_set_version_raw(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_LOWEST_RAW)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_VERSION_LOWEST_RAW, + 0); + fwupd_device_set_version_lowest_raw(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW, + 0); + fwupd_device_set_version_bootloader_raw(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_VERSION_BUILD_DATE)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_VERSION_BUILD_DATE, + 0); + fwupd_device_set_version_build_date(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_INSTALL_DURATION)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, + FWUPD_RESULT_KEY_INSTALL_DURATION, + 0); + fwupd_device_set_install_duration(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_CREATED)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_CREATED, 0); + fwupd_device_set_created(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_MODIFIED)) { + gint64 tmp = + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_MODIFIED, 0); + fwupd_device_set_modified(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_UPDATE_STATE)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_UPDATE_STATE, + NULL); + fwupd_device_set_update_state(self, fwupd_update_state_from_string(tmp)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_STATUS)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_STATUS, NULL); + fwupd_device_set_status(self, fwupd_status_from_string(tmp)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_UPDATE_ERROR)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_UPDATE_ERROR, + NULL); + fwupd_device_set_update_error(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_UPDATE_MESSAGE)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_UPDATE_MESSAGE, + NULL); + fwupd_device_set_update_message(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_UPDATE_IMAGE)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_UPDATE_IMAGE, + NULL); + fwupd_device_set_update_image(self, tmp); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_INSTANCE_IDS)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_INSTANCE_IDS); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_instance_id(self, json_array_get_string_element(array, i)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_GUID)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_GUID); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_guid(self, json_array_get_string_element(array, i)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_ISSUES)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_ISSUES); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_issue(self, json_array_get_string_element(array, i)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_FLAGS)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_FLAGS); + for (guint i = 0; i < json_array_get_length(array); i++) { + const gchar *tmp = json_array_get_string_element(array, i); + fwupd_device_add_flag(self, fwupd_device_flag_from_string(tmp)); + } + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_PROBLEMS)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_PROBLEMS); + for (guint i = 0; i < json_array_get_length(array); i++) { + const gchar *tmp = json_array_get_string_element(array, i); + fwupd_device_add_problem(self, fwupd_device_flag_from_string(tmp)); + } + } + if (json_object_has_member(obj, "VendorIds")) { + JsonArray *array = json_object_get_array_member(obj, "VendorIds"); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_vendor_id(self, json_array_get_string_element(array, i)); + } + if (json_object_has_member(obj, "Protocols")) { + JsonArray *array = json_object_get_array_member(obj, "Protocols"); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_protocol(self, json_array_get_string_element(array, i)); + } + if (json_object_has_member(obj, "Icons")) { + JsonArray *array = json_object_get_array_member(obj, "Icons"); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_icon(self, json_array_get_string_element(array, i)); + } + if (json_object_has_member(obj, "Checksums")) { + JsonArray *array = json_object_get_array_member(obj, "Checksums"); + for (guint i = 0; i < json_array_get_length(array); i++) + fwupd_device_add_checksum(self, json_array_get_string_element(array, i)); + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "json-glib version too old"); + return FALSE; +#endif +} + +/** + * fwupd_device_to_json: + * @self: a #FwupdDevice + * @builder: (not nullable): a JSON builder + * + * Adds a fwupd device to a JSON builder + * + * Since: 1.2.6 + **/ +void +fwupd_device_to_json(FwupdDevice *self, JsonBuilder *builder) +{ + return fwupd_device_to_json_full(self, builder, FWUPD_DEVICE_FLAG_NONE); +} + +static gchar * +fwupd_device_verstr_raw(guint64 value_raw) +{ + if (value_raw > 0xffffffff) { + return g_strdup_printf("0x%08x%08x", + (guint)(value_raw >> 32), + (guint)(value_raw & 0xffffffff)); + } + return g_strdup_printf("0x%08x", (guint)value_raw); +} + +typedef struct { + gchar *guid; + gchar *instance_id; +} FwupdDeviceGuidHelper; + +static void +fwupd_device_guid_helper_new(FwupdDeviceGuidHelper *helper) +{ + g_free(helper->guid); + g_free(helper->instance_id); + g_free(helper); +} + +static FwupdDeviceGuidHelper * +fwupd_device_guid_helper_array_find(GPtrArray *array, const gchar *guid) +{ + for (guint i = 0; i < array->len; i++) { + FwupdDeviceGuidHelper *helper = g_ptr_array_index(array, i); + if (g_strcmp0(helper->guid, guid) == 0) + return helper; + } + return NULL; +} + +/** + * fwupd_device_to_string: + * @self: a #FwupdDevice + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 0.9.3 + **/ +gchar * +fwupd_device_to_string(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(GPtrArray) guid_helpers = NULL; + + g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL); + + g_string_append_printf(str, "%s:\n", G_OBJECT_TYPE_NAME(self)); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DEVICE_ID, priv->id); + if (g_strcmp0(priv->composite_id, priv->parent_id) != 0) + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_PARENT_DEVICE_ID, priv->parent_id); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_COMPOSITE_ID, priv->composite_id); + if (priv->name != NULL) + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_NAME, priv->name); + if (priv->status != FWUPD_STATUS_UNKNOWN) { + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_STATUS, + fwupd_status_to_string(priv->status)); + } + + /* show instance IDs optionally mapped to GUIDs, and also "standalone" GUIDs */ + guid_helpers = g_ptr_array_new_with_free_func((GDestroyNotify)fwupd_device_guid_helper_new); + for (guint i = 0; i < priv->instance_ids->len; i++) { + FwupdDeviceGuidHelper *helper = g_new0(FwupdDeviceGuidHelper, 1); + const gchar *instance_id = g_ptr_array_index(priv->instance_ids, i); + helper->guid = fwupd_guid_hash_string(instance_id); + helper->instance_id = g_strdup(instance_id); + g_ptr_array_add(guid_helpers, helper); + } + for (guint i = 0; i < priv->guids->len; i++) { + const gchar *guid = g_ptr_array_index(priv->guids, i); + if (fwupd_device_guid_helper_array_find(guid_helpers, guid) == NULL) { + FwupdDeviceGuidHelper *helper = g_new0(FwupdDeviceGuidHelper, 1); + helper->guid = g_strdup(guid); + g_ptr_array_add(guid_helpers, helper); + } + } + for (guint i = 0; i < guid_helpers->len; i++) { + FwupdDeviceGuidHelper *helper = g_ptr_array_index(guid_helpers, i); + g_autoptr(GString) tmp = g_string_new(helper->guid); + if (helper->instance_id != NULL) + g_string_append_printf(tmp, " ← %s", helper->instance_id); + if (!fwupd_device_has_guid(self, helper->guid)) + g_string_append(tmp, " ⚠"); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_GUID, tmp->str); + } + + 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_BRANCH, priv->branch); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + for (guint i = 0; i < priv->protocols->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->protocols, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_PROTOCOL, tmp); + } + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->issues, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_ISSUES, tmp); + } + fwupd_pad_kv_dfl(str, FWUPD_RESULT_KEY_FLAGS, priv->flags); + if (priv->problems != FWUPD_DEVICE_PROBLEM_NONE) { + fwupd_device_pad_kv_problems(str, FWUPD_RESULT_KEY_PROBLEMS, priv->problems); + } + 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); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_CHECKSUM, checksum_display); + } + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_VENDOR, priv->vendor); + for (guint i = 0; i < priv->vendor_ids->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->vendor_ids, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_VENDOR_ID, tmp); + } + 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->battery_level != FWUPD_BATTERY_LEVEL_INVALID) + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_BATTERY_LEVEL, priv->battery_level); + if (priv->battery_threshold != FWUPD_BATTERY_LEVEL_INVALID) + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_BATTERY_THRESHOLD, priv->battery_threshold); + if (priv->version_raw > 0) { + g_autofree gchar *tmp = fwupd_device_verstr_raw(priv->version_raw); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_VERSION_RAW, tmp); + } + if (priv->version_lowest_raw > 0) { + g_autofree gchar *tmp = fwupd_device_verstr_raw(priv->version_lowest_raw); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_VERSION_LOWEST_RAW, tmp); + } + if (priv->version_build_date > 0) { + fwupd_pad_kv_unx(str, + FWUPD_RESULT_KEY_VERSION_BUILD_DATE, + priv->version_build_date); + } + if (priv->version_bootloader_raw > 0) { + g_autofree gchar *tmp = fwupd_device_verstr_raw(priv->version_bootloader_raw); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW, tmp); + } + if (priv->icons->len > 0) { + g_autoptr(GString) tmp = g_string_new(NULL); + for (guint i = 0; i < priv->icons->len; i++) { + const gchar *icon = g_ptr_array_index(priv->icons, i); + g_string_append_printf(tmp, "%s,", icon); + } + if (tmp->len > 1) + 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); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->update_image); + 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); + g_string_append_printf(str, " \n [%s]\n%s", FWUPD_RESULT_KEY_RELEASE, tmp); + } + + return g_string_free(g_steal_pointer(&str), FALSE); +} + +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; + case PROP_FLAGS: + g_value_set_uint64(value, priv->flags); + break; + case PROP_PROBLEMS: + g_value_set_uint64(value, priv->problems); + break; + case PROP_PROTOCOL: + g_value_set_string(value, priv->protocol); + break; + case PROP_UPDATE_MESSAGE: + g_value_set_string(value, priv->update_message); + break; + case PROP_UPDATE_ERROR: + g_value_set_string(value, priv->update_error); + break; + case PROP_UPDATE_IMAGE: + g_value_set_string(value, priv->update_image); + break; + case PROP_STATUS: + g_value_set_uint(value, priv->status); + break; + case PROP_PARENT: + g_value_set_object(value, priv->parent); + break; + case PROP_UPDATE_STATE: + g_value_set_uint(value, priv->update_state); + break; + case PROP_BATTERY_LEVEL: + g_value_set_uint(value, priv->battery_level); + break; + case PROP_BATTERY_THRESHOLD: + g_value_set_uint(value, priv->battery_threshold); + 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; + case PROP_FLAGS: + fwupd_device_set_flags(self, g_value_get_uint64(value)); + break; + case PROP_PROBLEMS: + fwupd_device_set_problems(self, g_value_get_uint64(value)); + break; + case PROP_PROTOCOL: + fwupd_device_add_protocol(self, g_value_get_string(value)); + break; + case PROP_UPDATE_MESSAGE: + fwupd_device_set_update_message(self, g_value_get_string(value)); + break; + case PROP_UPDATE_ERROR: + fwupd_device_set_update_error(self, g_value_get_string(value)); + break; + case PROP_UPDATE_IMAGE: + fwupd_device_set_update_image(self, g_value_get_string(value)); + break; + case PROP_STATUS: + fwupd_device_set_status(self, g_value_get_uint(value)); + break; + case PROP_PARENT: + fwupd_device_set_parent(self, g_value_get_object(value)); + break; + case PROP_UPDATE_STATE: + fwupd_device_set_update_state(self, g_value_get_uint(value)); + break; + case PROP_BATTERY_LEVEL: + fwupd_device_set_battery_level(self, g_value_get_uint(value)); + break; + case PROP_BATTERY_THRESHOLD: + fwupd_device_set_battery_threshold(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; + + /** + * FwupdDevice:version-format: + * + * The version format of the device. + * + * Since: 1.2.9 + */ + 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); + + /** + * FwupdDevice:flags: + * + * The device flags. + * + * Since: 0.9.3 + */ + pspec = g_param_spec_uint64("flags", + NULL, + NULL, + FWUPD_DEVICE_FLAG_NONE, + FWUPD_DEVICE_FLAG_UNKNOWN, + FWUPD_DEVICE_FLAG_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FLAGS, pspec); + + /** + * FwupdDevice:problems: + * + * The problems with the device that the user could fix, e.g. "lid open". + * + * Since: 1.8.1 + */ + pspec = g_param_spec_uint64("problems", + NULL, + NULL, + FWUPD_DEVICE_PROBLEM_NONE, + FWUPD_DEVICE_PROBLEM_UNKNOWN, + FWUPD_DEVICE_PROBLEM_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PROBLEMS, pspec); + + /** + * FwupdDevice:protocol: + * + * The device protocol. + * + * Since: 1.3.6 + * Deprecated: 1.5.8 + */ + pspec = g_param_spec_string("protocol", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PROTOCOL, pspec); + + /** + * FwupdDevice:status: + * + * The current device status. + * + * Since: 1.4.0 + */ + pspec = g_param_spec_uint("status", + NULL, + NULL, + FWUPD_STATUS_UNKNOWN, + FWUPD_STATUS_LAST, + FWUPD_STATUS_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_STATUS, pspec); + + /** + * FwupdDevice:parent: + * + * The device parent. + * + * Since: 1.0.8 + */ + pspec = g_param_spec_object("parent", + NULL, + NULL, + FWUPD_TYPE_DEVICE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PARENT, pspec); + + /** + * FwupdDevice:update-state: + * + * The device update state. + * + * Since: 0.9.8 + */ + pspec = g_param_spec_uint("update-state", + NULL, + NULL, + FWUPD_UPDATE_STATE_UNKNOWN, + FWUPD_UPDATE_STATE_LAST, + FWUPD_UPDATE_STATE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_UPDATE_STATE, pspec); + + /** + * FwupdDevice:update-message: + * + * The device update message. + * + * Since: 1.2.4 + */ + pspec = g_param_spec_string("update-message", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_UPDATE_MESSAGE, pspec); + + /** + * FwupdDevice:update-error: + * + * The device update error. + * + * Since: 0.9.8 + */ + pspec = g_param_spec_string("update-error", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_UPDATE_ERROR, pspec); + + /** + * FwupdDevice:update-image: + * + * The update image for the device. + * + * Since: 1.4.5 + */ + pspec = g_param_spec_string("update-image", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_UPDATE_IMAGE, pspec); + + /** + * FwupdDevice:battery-level: + * + * The device battery level in percent. + * + * Since: 1.5.8 + */ + pspec = g_param_spec_uint("battery-level", + NULL, + NULL, + 0, + FWUPD_BATTERY_LEVEL_INVALID, + FWUPD_BATTERY_LEVEL_INVALID, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_LEVEL, pspec); + + /** + * FwupdDevice:battery-threshold: + * + * The device battery threshold in percent. + * + * Since: 1.5.8 + */ + pspec = g_param_spec_uint("battery-threshold", + NULL, + NULL, + 0, + FWUPD_BATTERY_LEVEL_INVALID, + FWUPD_BATTERY_LEVEL_INVALID, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_THRESHOLD, pspec); +} + +static void +fwupd_device_init(FwupdDevice *self) +{ + FwupdDevicePrivate *priv = GET_PRIVATE(self); + 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->vendor_ids = g_ptr_array_new_with_free_func(g_free); + priv->protocols = g_ptr_array_new_with_free_func(g_free); + priv->issues = g_ptr_array_new_with_free_func(g_free); + priv->children = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + priv->releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + priv->battery_level = FWUPD_BATTERY_LEVEL_INVALID; + priv->battery_threshold = FWUPD_BATTERY_LEVEL_INVALID; +} + +static void +fwupd_device_finalize(GObject *object) +{ + FwupdDevice *self = FWUPD_DEVICE(object); + FwupdDevicePrivate *priv = GET_PRIVATE(self); + + if (priv->parent != NULL) + g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + for (guint i = 0; i < priv->children->len; i++) { + FwupdDevice *child = g_ptr_array_index(priv->children, i); + g_object_weak_unref(G_OBJECT(child), fwupd_device_child_finalized_cb, self); + } + + g_free(priv->description); + g_free(priv->id); + g_free(priv->parent_id); + g_free(priv->composite_id); + g_free(priv->name); + g_free(priv->serial); + g_free(priv->summary); + g_free(priv->branch); + g_free(priv->vendor); + g_free(priv->vendor_id); + g_free(priv->plugin); + g_free(priv->protocol); + g_free(priv->update_error); + g_free(priv->update_message); + g_free(priv->update_image); + 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->vendor_ids); + g_ptr_array_unref(priv->protocols); + g_ptr_array_unref(priv->instance_ids); + g_ptr_array_unref(priv->icons); + g_ptr_array_unref(priv->checksums); + g_ptr_array_unref(priv->children); + g_ptr_array_unref(priv->releases); + g_ptr_array_unref(priv->issues); + + G_OBJECT_CLASS(fwupd_device_parent_class)->finalize(object); +} + +static void +fwupd_device_set_from_variant_iter(FwupdDevice *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + fwupd_device_from_key_value(self, key, value); + g_variant_unref(value); + } +} + +/** + * fwupd_device_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new device using serialized data. + * + * Returns: (transfer full): a new #FwupdDevice, or %NULL if @value was invalid + * + * Since: 1.0.0 + **/ +FwupdDevice * +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(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + dev = fwupd_device_new(); + 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(value, "a{sv}", &iter); + fwupd_device_set_from_variant_iter(dev, iter); + } else { + g_warning("type %s not known", type_string); + } + return dev; +} + +/** + * fwupd_device_array_ensure_parents: + * @devices: (element-type FwupdDevice): devices + * + * Sets the parent object on all devices in the array using the parent ID. + * + * Since: 1.3.7 + **/ +void +fwupd_device_array_ensure_parents(GPtrArray *devices) +{ + g_autoptr(GHashTable) devices_by_id = NULL; + + /* create hash of ID->FwupdDevice */ + devices_by_id = g_hash_table_new(g_str_hash, g_str_equal); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + if (fwupd_device_get_id(dev) == NULL) + continue; + 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 < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, 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); + if (dev_tmp != NULL) + fwupd_device_set_parent(dev, dev_tmp); + } + } +} + +/** + * fwupd_device_array_from_variant: + * @value: (not nullable): the serialized data + * + * Creates an array of new devices using serialized 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_return_val_if_fail(value != NULL, 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++) { + 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); + } + + /* set the parent on each child */ + fwupd_device_array_ensure_parents(array); + return array; +} + +/** + * fwupd_device_compare: + * @self1: a device + * @self2: a different device + * + * Comparison function for comparing two device objects. + * + * Returns: negative, 0 or positive + * + * Since: 1.1.1 + **/ +gint +fwupd_device_compare(FwupdDevice *self1, FwupdDevice *self2) +{ + FwupdDevicePrivate *priv1 = GET_PRIVATE(self1); + FwupdDevicePrivate *priv2 = GET_PRIVATE(self2); + g_return_val_if_fail(FWUPD_IS_DEVICE(self1), 0); + g_return_val_if_fail(FWUPD_IS_DEVICE(self2), 0); + return g_strcmp0(priv1->id, priv2->id); +} + +/** + * fwupd_device_new: + * + * Creates a new device. + * + * Returns: a new #FwupdDevice + * + * Since: 0.9.3 + **/ +FwupdDevice * +fwupd_device_new(void) +{ + FwupdDevice *self; + self = g_object_new(FWUPD_TYPE_DEVICE, NULL); + return FWUPD_DEVICE(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-device.h b/fwupd-1.8.6/libfwupd/fwupd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..42322acd343f4c82693ee3e74f07ea3e098fe296 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-device.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" +#include "fwupd-release.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_DEVICE (fwupd_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdDevice, fwupd_device, FWUPD, DEVICE, GObject) + +struct _FwupdDeviceClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +FwupdDevice * +fwupd_device_new(void); +gchar * +fwupd_device_to_string(FwupdDevice *self); + +const gchar * +fwupd_device_get_id(FwupdDevice *self); +void +fwupd_device_set_id(FwupdDevice *self, const gchar *id); +const gchar * +fwupd_device_get_parent_id(FwupdDevice *self); +void +fwupd_device_set_parent_id(FwupdDevice *self, const gchar *parent_id); +const gchar * +fwupd_device_get_composite_id(FwupdDevice *self); +void +fwupd_device_set_composite_id(FwupdDevice *self, const gchar *composite_id); +FwupdDevice * +fwupd_device_get_root(FwupdDevice *self); +FwupdDevice * +fwupd_device_get_parent(FwupdDevice *self); +void +fwupd_device_set_parent(FwupdDevice *self, FwupdDevice *parent); +void +fwupd_device_add_child(FwupdDevice *self, FwupdDevice *child); +void +fwupd_device_remove_child(FwupdDevice *self, FwupdDevice *child); +GPtrArray * +fwupd_device_get_children(FwupdDevice *self); +const gchar * +fwupd_device_get_name(FwupdDevice *self); +void +fwupd_device_set_name(FwupdDevice *self, const gchar *name); +const gchar * +fwupd_device_get_serial(FwupdDevice *self); +void +fwupd_device_set_serial(FwupdDevice *self, const gchar *serial); +const gchar * +fwupd_device_get_summary(FwupdDevice *self); +void +fwupd_device_set_summary(FwupdDevice *self, const gchar *summary); +const gchar * +fwupd_device_get_branch(FwupdDevice *self); +void +fwupd_device_set_branch(FwupdDevice *self, const gchar *branch); +const gchar * +fwupd_device_get_description(FwupdDevice *self); +void +fwupd_device_set_description(FwupdDevice *self, const gchar *description); +const gchar * +fwupd_device_get_version(FwupdDevice *self); +void +fwupd_device_set_version(FwupdDevice *self, const gchar *version); +const gchar * +fwupd_device_get_version_lowest(FwupdDevice *self); +void +fwupd_device_set_version_lowest(FwupdDevice *self, const gchar *version_lowest); +guint64 +fwupd_device_get_version_lowest_raw(FwupdDevice *self); +void +fwupd_device_set_version_lowest_raw(FwupdDevice *self, guint64 version_lowest_raw); +const gchar * +fwupd_device_get_version_bootloader(FwupdDevice *self); +void +fwupd_device_set_version_bootloader(FwupdDevice *self, const gchar *version_bootloader); +guint64 +fwupd_device_get_version_bootloader_raw(FwupdDevice *self); +void +fwupd_device_set_version_bootloader_raw(FwupdDevice *self, guint64 version_bootloader_raw); +guint64 +fwupd_device_get_version_raw(FwupdDevice *self); +void +fwupd_device_set_version_raw(FwupdDevice *self, guint64 version_raw); +guint64 +fwupd_device_get_version_build_date(FwupdDevice *self); +void +fwupd_device_set_version_build_date(FwupdDevice *self, guint64 version_build_date); +FwupdVersionFormat +fwupd_device_get_version_format(FwupdDevice *self); +void +fwupd_device_set_version_format(FwupdDevice *self, FwupdVersionFormat version_format); +guint32 +fwupd_device_get_flashes_left(FwupdDevice *self); +void +fwupd_device_set_flashes_left(FwupdDevice *self, guint32 flashes_left); +guint32 +fwupd_device_get_battery_level(FwupdDevice *self); +void +fwupd_device_set_battery_level(FwupdDevice *self, guint32 battery_level); +guint32 +fwupd_device_get_battery_threshold(FwupdDevice *self); +void +fwupd_device_set_battery_threshold(FwupdDevice *self, guint32 battery_threshold); +guint32 +fwupd_device_get_install_duration(FwupdDevice *self); +void +fwupd_device_set_install_duration(FwupdDevice *self, guint32 duration); +guint64 +fwupd_device_get_flags(FwupdDevice *self); +void +fwupd_device_set_flags(FwupdDevice *self, guint64 flags); +void +fwupd_device_add_flag(FwupdDevice *self, FwupdDeviceFlags flag); +void +fwupd_device_remove_flag(FwupdDevice *self, FwupdDeviceFlags flag); +gboolean +fwupd_device_has_flag(FwupdDevice *self, FwupdDeviceFlags flag); +guint64 +fwupd_device_get_problems(FwupdDevice *self); +void +fwupd_device_set_problems(FwupdDevice *self, guint64 problems); +void +fwupd_device_add_problem(FwupdDevice *self, FwupdDeviceProblem problem); +void +fwupd_device_remove_problem(FwupdDevice *self, FwupdDeviceProblem problem); +gboolean +fwupd_device_has_problem(FwupdDevice *self, FwupdDeviceProblem problem); +guint64 +fwupd_device_get_created(FwupdDevice *self); +void +fwupd_device_set_created(FwupdDevice *self, guint64 created); +guint64 +fwupd_device_get_modified(FwupdDevice *self); +void +fwupd_device_set_modified(FwupdDevice *self, guint64 modified); +GPtrArray * +fwupd_device_get_checksums(FwupdDevice *self); +void +fwupd_device_add_checksum(FwupdDevice *self, const gchar *checksum); +const gchar * +fwupd_device_get_plugin(FwupdDevice *self); +void +fwupd_device_set_plugin(FwupdDevice *self, const gchar *plugin); +G_DEPRECATED_FOR(fwupd_device_get_protocols) +const gchar * +fwupd_device_get_protocol(FwupdDevice *self); +G_DEPRECATED_FOR(fwupd_device_add_protocol) +void +fwupd_device_set_protocol(FwupdDevice *self, const gchar *protocol); +void +fwupd_device_add_protocol(FwupdDevice *self, const gchar *protocol); +gboolean +fwupd_device_has_protocol(FwupdDevice *self, const gchar *protocol); +GPtrArray * +fwupd_device_get_protocols(FwupdDevice *self); +const gchar * +fwupd_device_get_vendor(FwupdDevice *self); +void +fwupd_device_set_vendor(FwupdDevice *self, const gchar *vendor); +G_DEPRECATED_FOR(fwupd_device_get_vendor_ids) +const gchar * +fwupd_device_get_vendor_id(FwupdDevice *self); +G_DEPRECATED_FOR(fwupd_device_add_vendor_id) +void +fwupd_device_set_vendor_id(FwupdDevice *self, const gchar *vendor_id); +void +fwupd_device_add_vendor_id(FwupdDevice *self, const gchar *vendor_id); +gboolean +fwupd_device_has_vendor_id(FwupdDevice *self, const gchar *vendor_id); +GPtrArray * +fwupd_device_get_vendor_ids(FwupdDevice *self); +void +fwupd_device_add_guid(FwupdDevice *self, const gchar *guid); +gboolean +fwupd_device_has_guid(FwupdDevice *self, const gchar *guid); +GPtrArray * +fwupd_device_get_guids(FwupdDevice *self); +const gchar * +fwupd_device_get_guid_default(FwupdDevice *self); +void +fwupd_device_add_instance_id(FwupdDevice *self, const gchar *instance_id); +gboolean +fwupd_device_has_instance_id(FwupdDevice *self, const gchar *instance_id); +GPtrArray * +fwupd_device_get_instance_ids(FwupdDevice *self); +void +fwupd_device_add_icon(FwupdDevice *self, const gchar *icon); +gboolean +fwupd_device_has_icon(FwupdDevice *self, const gchar *icon); +GPtrArray * +fwupd_device_get_icons(FwupdDevice *self); +GPtrArray * +fwupd_device_get_issues(FwupdDevice *self); +void +fwupd_device_add_issue(FwupdDevice *self, const gchar *issue); + +FwupdUpdateState +fwupd_device_get_update_state(FwupdDevice *self); +void +fwupd_device_set_update_state(FwupdDevice *self, FwupdUpdateState update_state); +const gchar * +fwupd_device_get_update_error(FwupdDevice *self); +void +fwupd_device_set_update_error(FwupdDevice *self, const gchar *update_error); +const gchar * +fwupd_device_get_update_message(FwupdDevice *self); +void +fwupd_device_set_update_message(FwupdDevice *self, const gchar *update_message); +const gchar * +fwupd_device_get_update_image(FwupdDevice *self); +void +fwupd_device_set_update_image(FwupdDevice *self, const gchar *update_image); +FwupdStatus +fwupd_device_get_status(FwupdDevice *self); +void +fwupd_device_set_status(FwupdDevice *self, FwupdStatus status); +void +fwupd_device_add_release(FwupdDevice *self, FwupdRelease *release); +GPtrArray * +fwupd_device_get_releases(FwupdDevice *self); +FwupdRelease * +fwupd_device_get_release_default(FwupdDevice *self); +gint +fwupd_device_compare(FwupdDevice *self1, FwupdDevice *self2); + +FwupdDevice * +fwupd_device_from_variant(GVariant *value); +GPtrArray * +fwupd_device_array_from_variant(GVariant *value); +void +fwupd_device_array_ensure_parents(GPtrArray *devices); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-enums-private.h b/fwupd-1.8.6/libfwupd/fwupd-enums-private.h new file mode 100644 index 0000000000000000000000000000000000000000..b7926a4eb985d3d6c468131d28f63aca51a39fb1 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-enums-private.h @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +/** + * FWUPD_RESULT_KEY_APPSTREAM_ID: + * + * Result key to represent AppstreamId + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_APPSTREAM_ID "AppstreamId" +/** + * FWUPD_RESULT_KEY_RELEASE_ID: + * + * Result key to represent the release ID. + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_RELEASE_ID "ReleaseId" +/** + * FWUPD_RESULT_KEY_CHECKSUM: + * + * Result key to represent Checksum + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_CHECKSUM "Checksum" +/** + * FWUPD_RESULT_KEY_TAGS: + * + * Result key to represent release tags + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_TAGS "Tags" +/** + * FWUPD_RESULT_KEY_CREATED: + * + * Result key to represent Created + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_CREATED "Created" +/** + * FWUPD_RESULT_KEY_DESCRIPTION: + * + * Result key to represent Description + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_DESCRIPTION "Description" +/** + * FWUPD_RESULT_KEY_DETACH_CAPTION: + * + * Result key to represent DetachCaption + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_DETACH_CAPTION "DetachCaption" +/** + * FWUPD_RESULT_KEY_DETACH_IMAGE: + * + * Result key to represent DetachImage + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_DETACH_IMAGE "DetachImage" +/** + * FWUPD_RESULT_KEY_DEVICE_ID: + * + * Result key to represent DeviceId + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_DEVICE_ID "DeviceId" +/** + * FWUPD_RESULT_KEY_PARENT_DEVICE_ID: + * + * Result key to represent ParentDeviceId + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_PARENT_DEVICE_ID "ParentDeviceId" +/** + * FWUPD_RESULT_KEY_COMPOSITE_ID: + * + * Result key to represent CompositeId + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_COMPOSITE_ID "CompositeId" +/** + * FWUPD_RESULT_KEY_FILENAME: + * + * Result key to represent Filename + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_FILENAME "Filename" +/** + * FWUPD_RESULT_KEY_PROTOCOL: + * + * Result key to represent Protocol + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_PROTOCOL "Protocol" +/** + * FWUPD_RESULT_KEY_CATEGORIES: + * + * Result key to represent Categories + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_CATEGORIES "Categories" +/** + * FWUPD_RESULT_KEY_ISSUES: + * + * Result key to represent Issues + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_ISSUES "Issues" +/** + * FWUPD_RESULT_KEY_FLAGS: + * + * Result key to represent Flags + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_FLAGS "Flags" +/** + * FWUPD_RESULT_KEY_FLASHES_LEFT: + * + * Result key to represent FlashesLeft + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_FLASHES_LEFT "FlashesLeft" +/** + * FWUPD_RESULT_KEY_URGENCY: + * + * Result key to represent Urgency + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_URGENCY "Urgency" +/** + * FWUPD_RESULT_KEY_REQUEST_KIND: + * + * Result key to represent RequestKind + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_REQUEST_KIND "RequestKind" +/** + * FWUPD_RESULT_KEY_HSI_LEVEL: + * + * Result key to represent HsiLevel + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_HSI_LEVEL "HsiLevel" +/** + * FWUPD_RESULT_KEY_HSI_RESULT: + * + * Result key to represent HsiResult + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_HSI_RESULT "HsiResult" +/** + * FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK: + * + * Result key to represent the fallback HsiResult + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK "HsiResultFallback" +/** + * FWUPD_RESULT_KEY_INSTALL_DURATION: + * + * Result key to represent InstallDuration + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_INSTALL_DURATION "InstallDuration" +/** + * FWUPD_RESULT_KEY_GUID: + * + * Result key to represent Guid + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_GUID "Guid" +/** + * FWUPD_RESULT_KEY_INSTANCE_IDS: + * + * Result key to represent InstanceIds + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_INSTANCE_IDS "InstanceIds" +/** + * FWUPD_RESULT_KEY_HOMEPAGE: + * + * Result key to represent Homepage + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_HOMEPAGE "Homepage" +/** + * FWUPD_RESULT_KEY_DETAILS_URL: + * + * Result key to represent DetailsUrl + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_DETAILS_URL "DetailsUrl" +/** + * FWUPD_RESULT_KEY_SOURCE_URL: + * + * Result key to represent SourceUrl + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_SOURCE_URL "SourceUrl" +/** + * FWUPD_RESULT_KEY_ICON: + * + * Result key to represent Icon + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_ICON "Icon" +/** + * FWUPD_RESULT_KEY_LICENSE: + * + * Result key to represent License + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_LICENSE "License" +/** + * FWUPD_RESULT_KEY_MODIFIED: + * + * Result key to represent Modified + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_MODIFIED "Modified" +/** + * FWUPD_RESULT_KEY_VERSION_BUILD_DATE: + * + * Result key to represent VersionBuildDate + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_VERSION_BUILD_DATE "VersionBuildDate" +/** + * FWUPD_RESULT_KEY_METADATA: + * + * Result key to represent Metadata + * + * The D-Bus type signature string is 'a{ss}' i.e. a string dictionary. + **/ +#define FWUPD_RESULT_KEY_METADATA "Metadata" +/** + * FWUPD_RESULT_KEY_NAME: + * + * Result key to represent Name + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_NAME "Name" +/** + * FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX: + * + * Result key to represent NameVariantSuffix + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX "NameVariantSuffix" +/** + * FWUPD_RESULT_KEY_PLUGIN: + * + * Result key to represent Plugin + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_PLUGIN "Plugin" +/** + * FWUPD_RESULT_KEY_RELEASE: + * + * Result key to represent Release + * + * The D-Bus type signature string is 'a{sv}' i.e. a variant dictionary. + **/ +#define FWUPD_RESULT_KEY_RELEASE "Release" +/** + * FWUPD_RESULT_KEY_REMOTE_ID: + * + * Result key to represent RemoteId + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_REMOTE_ID "RemoteId" +/** + * FWUPD_RESULT_KEY_SERIAL: + * + * Result key to represent Serial + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_SERIAL "Serial" +/** + * FWUPD_RESULT_KEY_SIZE: + * + * Result key to represent Size + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_SIZE "Size" +/** + * FWUPD_RESULT_KEY_STATUS: + * + * Result key to represent Status + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_STATUS "Status" +/** + * FWUPD_RESULT_KEY_SUMMARY: + * + * Result key to represent Summary + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_SUMMARY "Summary" +/** + * FWUPD_RESULT_KEY_BRANCH: + * + * Result key to represent Branch + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_BRANCH "Branch" +/** + * FWUPD_RESULT_KEY_TRUST_FLAGS: + * + * Result key to represent TrustFlags + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_TRUST_FLAGS "TrustFlags" +/** + * FWUPD_RESULT_KEY_PROBLEMS: + * + * Result key to represent problems + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_PROBLEMS "Problems" +/** + * FWUPD_RESULT_KEY_UPDATE_MESSAGE: + * + * Result key to represent UpdateMessage + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_UPDATE_MESSAGE "UpdateMessage" +/** + * FWUPD_RESULT_KEY_UPDATE_IMAGE: + * + * Result key to represent UpdateImage + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_UPDATE_IMAGE "UpdateImage" +/** + * FWUPD_RESULT_KEY_UPDATE_ERROR: + * + * Result key to represent UpdateError + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_UPDATE_ERROR "UpdateError" +/** + * FWUPD_RESULT_KEY_UPDATE_STATE: + * + * Result key to represent UpdateState + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_UPDATE_STATE "UpdateState" +/** + * FWUPD_RESULT_KEY_URI: + * + * Result key to represent Uri + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_URI "Uri" +/** + * FWUPD_RESULT_KEY_LOCATIONS: + * + * Result key to represent Locations + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_LOCATIONS "Locations" +/** + * FWUPD_RESULT_KEY_VENDOR_ID: + * + * Result key to represent VendorId + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_VENDOR_ID "VendorId" +/** + * FWUPD_RESULT_KEY_VENDOR: + * + * Result key to represent Vendor + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_VENDOR "Vendor" +/** + * FWUPD_RESULT_KEY_VERSION_BOOTLOADER: + * + * Result key to represent VersionBootloader + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_VERSION_BOOTLOADER "VersionBootloader" +/** + * FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW: + * + * Result key to represent VersionBootloaderRaw + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_VERSION_BOOTLOADER_RAW "VersionBootloaderRaw" +/** + * FWUPD_RESULT_KEY_VERSION_FORMAT: + * + * Result key to represent VersionFormat + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_VERSION_FORMAT "VersionFormat" +/** + * FWUPD_RESULT_KEY_VERSION_RAW: + * + * Result key to represent VersionRaw + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_VERSION_RAW "VersionRaw" +/** + * FWUPD_RESULT_KEY_VERSION_LOWEST: + * + * Result key to represent VersionLowest + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_VERSION_LOWEST "VersionLowest" +/** + * FWUPD_RESULT_KEY_VERSION_LOWEST_RAW: + * + * Result key to represent VersionLowestRaw + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_VERSION_LOWEST_RAW "VersionLowestRaw" +/** + * FWUPD_RESULT_KEY_VERSION: + * + * Result key to represent Version + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_VERSION "Version" +/** + * FWUPD_RESULT_KEY_BATTERY_LEVEL: + * + * Result key to represent the current battery level in percent. + * Expressed from 0-100%, or 101 for invalid or unset. + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_BATTERY_LEVEL "BatteryLevel" +/** + * FWUPD_RESULT_KEY_BATTERY_THRESHOLD: + * + * Result key to represent the minimum battery level required to perform an update. + * Expressed from 0-100%, or 101 for invalid or unset. + * + * The D-Bus type signature string is 'u' i.e. a unsigned 32 bit integer. + **/ +#define FWUPD_RESULT_KEY_BATTERY_THRESHOLD "BatteryThreshold" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_ID: + * + * Result key to represent the unique identifier of the BIOS setting. + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_ID "BiosSettingId" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE: + * + * Result key to represent the value that would enable this attribute. + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE "BiosSettingTargetValue" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE: + * + * Result key to represent the current value of BIOS setting. + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE "BiosSettingCurrentValue" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_TYPE: + * + * Result key to represent the type of BIOS setting. + * 0 is invalid, 1+ represent an attribute type + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_TYPE "BiosSettingType" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES: + * + * Result key to represent possible values + * + * The D-Bus type signature string is 'as' i.e. an array of strings. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_POSSIBLE_VALUES "BiosSettingPossibleValues" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND: + * + * Result key to represent the upper bound for an integer BIOS setting. + * or minimum length for string BIOS setting. + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_LOWER_BOUND "BiosSettingLowerBound" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND: + * + * Result key to represent the lower bound for an integer BIOS setting + * or maximum length for string BIOS setting. + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_UPPER_BOUND "BiosSettingUpperBound" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT: + * + * Result key to represent the scalar increment for an integer BIOS setting. + * + * The D-Bus type signature string is 't' i.e. a unsigned 64 bit integer. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_SCALAR_INCREMENT "BiosSettingScalarIncrement" +/** + * FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY: + * + * Result key to represent whether BIOS setting is read only + * + * The D-Bus type signature string is 'b' i.e. a boolean. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY "BiosSettingReadOnly" + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-enums.c b/fwupd-1.8.6/libfwupd/fwupd-enums.c new file mode 100644 index 0000000000000000000000000000000000000000..4d457b5ed756b3d0bc43dfa7f76624448d416533 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-enums.c @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-enums.h" + +/** + * fwupd_status_to_string: + * @status: a status, e.g. %FWUPD_STATUS_DECOMPRESSING + * + * Converts an enumerated status to a string. + * + * Returns: identifier string + * + * Since: 0.1.1 + **/ +const gchar * +fwupd_status_to_string(FwupdStatus status) +{ + if (status == FWUPD_STATUS_UNKNOWN) + return "unknown"; + if (status == FWUPD_STATUS_IDLE) + return "idle"; + if (status == FWUPD_STATUS_DECOMPRESSING) + return "decompressing"; + if (status == FWUPD_STATUS_LOADING) + return "loading"; + if (status == FWUPD_STATUS_DEVICE_RESTART) + return "device-restart"; + if (status == FWUPD_STATUS_DEVICE_WRITE) + return "device-write"; + if (status == FWUPD_STATUS_DEVICE_READ) + return "device-read"; + if (status == FWUPD_STATUS_DEVICE_ERASE) + return "device-erase"; + if (status == FWUPD_STATUS_DEVICE_VERIFY) + return "device-verify"; + if (status == FWUPD_STATUS_DEVICE_BUSY) + return "device-busy"; + if (status == FWUPD_STATUS_SCHEDULING) + return "scheduling"; + if (status == FWUPD_STATUS_DOWNLOADING) + return "downloading"; + if (status == FWUPD_STATUS_WAITING_FOR_AUTH) + return "waiting-for-auth"; + if (status == FWUPD_STATUS_SHUTDOWN) + return "shutdown"; + return NULL; +} + +/** + * fwupd_status_from_string: + * @status: (nullable): a string, e.g. `decompressing` + * + * Converts a string to an enumerated status. + * + * Returns: enumerated value + * + * Since: 0.1.1 + **/ +FwupdStatus +fwupd_status_from_string(const gchar *status) +{ + if (g_strcmp0(status, "unknown") == 0) + return FWUPD_STATUS_UNKNOWN; + if (g_strcmp0(status, "idle") == 0) + return FWUPD_STATUS_IDLE; + if (g_strcmp0(status, "decompressing") == 0) + return FWUPD_STATUS_DECOMPRESSING; + if (g_strcmp0(status, "loading") == 0) + return FWUPD_STATUS_LOADING; + if (g_strcmp0(status, "device-restart") == 0) + return FWUPD_STATUS_DEVICE_RESTART; + if (g_strcmp0(status, "device-write") == 0) + return FWUPD_STATUS_DEVICE_WRITE; + if (g_strcmp0(status, "device-verify") == 0) + return FWUPD_STATUS_DEVICE_VERIFY; + if (g_strcmp0(status, "scheduling") == 0) + return FWUPD_STATUS_SCHEDULING; + if (g_strcmp0(status, "downloading") == 0) + return FWUPD_STATUS_DOWNLOADING; + if (g_strcmp0(status, "device-read") == 0) + return FWUPD_STATUS_DEVICE_READ; + if (g_strcmp0(status, "device-erase") == 0) + return FWUPD_STATUS_DEVICE_ERASE; + if (g_strcmp0(status, "device-busy") == 0) + 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; +} + +/** + * fwupd_device_flag_to_string: + * @device_flag: a device flag, e.g. %FWUPD_DEVICE_FLAG_REQUIRE_AC + * + * Converts a device flag to a string. + * + * Returns: identifier string + * + * Since: 0.7.0 + **/ +const gchar * +fwupd_device_flag_to_string(FwupdDeviceFlags device_flag) +{ + if (device_flag == FWUPD_DEVICE_FLAG_NONE) + return "none"; + if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) + return "internal"; + if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE) + return "updatable"; + if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) + return "only-offline"; + if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) + return "require-ac"; + if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) + return "locked"; + if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) + return "supported"; + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) + return "needs-bootloader"; + if (device_flag == FWUPD_DEVICE_FLAG_REGISTERED) + 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) + return "notified"; + if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) + return "use-runtime-version"; + if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) + 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_TRUSTED) + return "trusted"; + 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_HISTORICAL) + return "historical"; + if (device_flag == FWUPD_DEVICE_FLAG_ONLY_SUPPORTED) + return "only-supported"; + if (device_flag == FWUPD_DEVICE_FLAG_WILL_DISAPPEAR) + return "will-disappear"; + if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY) + return "can-verify"; + if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) + return "can-verify-image"; + if (device_flag == FWUPD_DEVICE_FLAG_DUAL_IMAGE) + return "dual-image"; + if (device_flag == FWUPD_DEVICE_FLAG_SELF_RECOVERY) + return "self-recovery"; + if (device_flag == FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) + return "usable-during-update"; + if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) + return "version-check-required"; + if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES) + return "install-all-releases"; + if (device_flag == FWUPD_DEVICE_FLAG_MD_SET_NAME) + return "md-set-name"; + if (device_flag == FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY) + return "md-set-name-category"; + if (device_flag == FWUPD_DEVICE_FLAG_MD_SET_VERFMT) + return "md-set-verfmt"; + if (device_flag == FWUPD_DEVICE_FLAG_MD_SET_ICON) + return "md-set-icon"; + if (device_flag == FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS) + return "add-counterpart-guids"; + if (device_flag == FWUPD_DEVICE_FLAG_NO_GUID_MATCHING) + return "no-guid-matching"; + if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN) + return "updatable-hidden"; + if (device_flag == FWUPD_DEVICE_FLAG_SKIPS_RESTART) + return "skips-restart"; + if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) + return "has-multiple-branches"; + if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) + return "backup-before-install"; + if (device_flag == FWUPD_DEVICE_FLAG_WILDCARD_INSTALL) + return "wildcard-install"; + if (device_flag == FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE) + return "only-version-upgrade"; + if (device_flag == FWUPD_DEVICE_FLAG_UNREACHABLE) + return "unreachable"; + if (device_flag == FWUPD_DEVICE_FLAG_AFFECTS_FDE) + return "affects-fde"; + if (device_flag == FWUPD_DEVICE_FLAG_END_OF_LIFE) + return "end-of-life"; + if (device_flag == FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) + return "signed-payload"; + if (device_flag == FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD) + return "unsigned-payload"; + if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) + return "unknown"; + return NULL; +} + +/** + * fwupd_device_flag_from_string: + * @device_flag: (nullable): a string, e.g. `require-ac` + * + * Converts a string to an enumerated device flag. + * + * Returns: enumerated value + * + * Since: 0.7.0 + **/ +FwupdDeviceFlags +fwupd_device_flag_from_string(const gchar *device_flag) +{ + if (g_strcmp0(device_flag, "none") == 0) + return FWUPD_DEVICE_FLAG_NONE; + if (g_strcmp0(device_flag, "internal") == 0) + return FWUPD_DEVICE_FLAG_INTERNAL; + if (g_strcmp0(device_flag, "updatable") == 0 || g_strcmp0(device_flag, "allow-online") == 0) + return FWUPD_DEVICE_FLAG_UPDATABLE; + if (g_strcmp0(device_flag, "only-offline") == 0 || + g_strcmp0(device_flag, "allow-offline") == 0) + return FWUPD_DEVICE_FLAG_ONLY_OFFLINE; + if (g_strcmp0(device_flag, "require-ac") == 0) + return FWUPD_DEVICE_FLAG_REQUIRE_AC; + if (g_strcmp0(device_flag, "locked") == 0) + return FWUPD_DEVICE_FLAG_LOCKED; + if (g_strcmp0(device_flag, "supported") == 0) + return FWUPD_DEVICE_FLAG_SUPPORTED; + if (g_strcmp0(device_flag, "needs-bootloader") == 0) + return FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER; + if (g_strcmp0(device_flag, "registered") == 0) + 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) + return FWUPD_DEVICE_FLAG_NOTIFIED; + if (g_strcmp0(device_flag, "use-runtime-version") == 0) + return FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION; + if (g_strcmp0(device_flag, "install-parent-first") == 0) + 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, "trusted") == 0) + return FWUPD_DEVICE_FLAG_TRUSTED; + 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; + if (g_strcmp0(device_flag, "historical") == 0) + return FWUPD_DEVICE_FLAG_HISTORICAL; + if (g_strcmp0(device_flag, "only-supported") == 0) + return FWUPD_DEVICE_FLAG_ONLY_SUPPORTED; + if (g_strcmp0(device_flag, "will-disappear") == 0) + return FWUPD_DEVICE_FLAG_WILL_DISAPPEAR; + if (g_strcmp0(device_flag, "can-verify") == 0) + return FWUPD_DEVICE_FLAG_CAN_VERIFY; + if (g_strcmp0(device_flag, "can-verify-image") == 0) + return FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; + if (g_strcmp0(device_flag, "dual-image") == 0) + return FWUPD_DEVICE_FLAG_DUAL_IMAGE; + if (g_strcmp0(device_flag, "self-recovery") == 0) + return FWUPD_DEVICE_FLAG_SELF_RECOVERY; + if (g_strcmp0(device_flag, "usable-during-update") == 0) + return FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE; + if (g_strcmp0(device_flag, "version-check-required") == 0) + return FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED; + if (g_strcmp0(device_flag, "install-all-releases") == 0) + return FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES; + if (g_strcmp0(device_flag, "md-set-name") == 0) + return FWUPD_DEVICE_FLAG_MD_SET_NAME; + if (g_strcmp0(device_flag, "md-set-name-category") == 0) + return FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY; + if (g_strcmp0(device_flag, "md-set-verfmt") == 0) + return FWUPD_DEVICE_FLAG_MD_SET_VERFMT; + if (g_strcmp0(device_flag, "md-set-icon") == 0) + return FWUPD_DEVICE_FLAG_MD_SET_ICON; + if (g_strcmp0(device_flag, "add-counterpart-guids") == 0) + return FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS; + if (g_strcmp0(device_flag, "no-guid-matching") == 0) + return FWUPD_DEVICE_FLAG_NO_GUID_MATCHING; + if (g_strcmp0(device_flag, "updatable-hidden") == 0) + return FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN; + if (g_strcmp0(device_flag, "skips-restart") == 0) + return FWUPD_DEVICE_FLAG_SKIPS_RESTART; + if (g_strcmp0(device_flag, "has-multiple-branches") == 0) + return FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + if (g_strcmp0(device_flag, "backup-before-install") == 0) + return FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL; + if (g_strcmp0(device_flag, "wildcard-install") == 0) + return FWUPD_DEVICE_FLAG_WILDCARD_INSTALL; + if (g_strcmp0(device_flag, "only-version-upgrade") == 0) + return FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE; + if (g_strcmp0(device_flag, "unreachable") == 0) + return FWUPD_DEVICE_FLAG_UNREACHABLE; + if (g_strcmp0(device_flag, "affects-fde") == 0) + return FWUPD_DEVICE_FLAG_AFFECTS_FDE; + if (g_strcmp0(device_flag, "end-of-life") == 0) + return FWUPD_DEVICE_FLAG_END_OF_LIFE; + if (g_strcmp0(device_flag, "signed-payload") == 0) + return FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD; + if (g_strcmp0(device_flag, "unsigned-payload") == 0) + return FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD; + return FWUPD_DEVICE_FLAG_UNKNOWN; +} + +/** + * fwupd_device_problem_to_string: + * @device_problem: a device inhibit kind, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW + * + * Converts a device inhibit kind to a string. + * + * Returns: identifier string + * + * Since: 1.8.1 + **/ +const gchar * +fwupd_device_problem_to_string(FwupdDeviceProblem device_problem) +{ + if (device_problem == FWUPD_DEVICE_PROBLEM_NONE) + return "none"; + if (device_problem == FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW) + return "system-power-too-low"; + if (device_problem == FWUPD_DEVICE_PROBLEM_UNREACHABLE) + return "unreachable"; + if (device_problem == FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW) + return "power-too-low"; + if (device_problem == FWUPD_DEVICE_PROBLEM_UPDATE_PENDING) + return "update-pending"; + if (device_problem == FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER) + return "require-ac-power"; + if (device_problem == FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED) + return "lid-is-closed"; + if (device_problem == FWUPD_DEVICE_PROBLEM_IS_EMULATED) + return "is-emulated"; + if (device_problem == FWUPD_DEVICE_PROBLEM_MISSING_LICENSE) + return "missing-license"; + if (device_problem == FWUPD_DEVICE_PROBLEM_UNKNOWN) + return "unknown"; + return NULL; +} + +/** + * fwupd_device_problem_from_string: + * @device_problem: (nullable): a string, e.g. `require-ac` + * + * Converts a string to a enumerated device inhibit kind. + * + * Returns: enumerated value + * + * Since: 1.8.1 + **/ +FwupdDeviceProblem +fwupd_device_problem_from_string(const gchar *device_problem) +{ + if (g_strcmp0(device_problem, "none") == 0) + return FWUPD_DEVICE_PROBLEM_NONE; + if (g_strcmp0(device_problem, "system-power-too-low") == 0) + return FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW; + if (g_strcmp0(device_problem, "unreachable") == 0) + return FWUPD_DEVICE_PROBLEM_UNREACHABLE; + if (g_strcmp0(device_problem, "power-too-low") == 0) + return FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW; + if (g_strcmp0(device_problem, "update-pending") == 0) + return FWUPD_DEVICE_PROBLEM_UPDATE_PENDING; + if (g_strcmp0(device_problem, "require-ac-power") == 0) + return FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER; + if (g_strcmp0(device_problem, "lid-is-closed") == 0) + return FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED; + if (g_strcmp0(device_problem, "is-emulated") == 0) + return FWUPD_DEVICE_PROBLEM_IS_EMULATED; + if (g_strcmp0(device_problem, "missing-license") == 0) + return FWUPD_DEVICE_PROBLEM_MISSING_LICENSE; + return FWUPD_DEVICE_PROBLEM_UNKNOWN; +} + +/** + * fwupd_plugin_flag_to_string: + * @plugin_flag: plugin flags, e.g. %FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE + * + * Converts an enumerated plugin flag to a string. + * + * Returns: identifier string + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_plugin_flag_to_string(FwupdPluginFlags plugin_flag) +{ + if (plugin_flag == FWUPD_DEVICE_FLAG_NONE) + return "none"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) + return "disabled"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING) + return "user-warning"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE) + return "clear-updatable"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) + return "no-hardware"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) + return "capsules-unsupported"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) + return "unlock-required"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) + return "efivar-not-mounted"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) + return "esp-not-found"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) + return "legacy-bios"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) + return "failed-open"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_REQUIRE_HWID) + return "require-hwid"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD) + return "kernel-too-old"; + if (plugin_flag == FWUPD_DEVICE_FLAG_UNKNOWN) + return "unknown"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_AUTH_REQUIRED) + return "auth-required"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_SECURE_CONFIG) + return "secure-config"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_MODULAR) + return "modular"; + return NULL; +} + +/** + * fwupd_plugin_flag_from_string: + * @plugin_flag: (nullable): a string, e.g. `require-ac` + * + * Converts a string to an enumerated plugin flag. + * + * Returns: enumerated value + * + * Since: 1.5.0 + **/ +FwupdPluginFlags +fwupd_plugin_flag_from_string(const gchar *plugin_flag) +{ + if (g_strcmp0(plugin_flag, "none") == 0) + return FWUPD_DEVICE_FLAG_NONE; + if (g_strcmp0(plugin_flag, "disabled") == 0) + return FWUPD_PLUGIN_FLAG_DISABLED; + if (g_strcmp0(plugin_flag, "user-warning") == 0) + return FWUPD_PLUGIN_FLAG_USER_WARNING; + if (g_strcmp0(plugin_flag, "clear-updatable") == 0) + return FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE; + if (g_strcmp0(plugin_flag, "no-hardware") == 0) + return FWUPD_PLUGIN_FLAG_NO_HARDWARE; + if (g_strcmp0(plugin_flag, "capsules-unsupported") == 0) + return FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED; + if (g_strcmp0(plugin_flag, "unlock-required") == 0) + return FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED; + if (g_strcmp0(plugin_flag, "efivar-not-mounted") == 0) + return FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED; + if (g_strcmp0(plugin_flag, "esp-not-found") == 0) + return FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND; + if (g_strcmp0(plugin_flag, "legacy-bios") == 0) + return FWUPD_PLUGIN_FLAG_LEGACY_BIOS; + if (g_strcmp0(plugin_flag, "failed-open") == 0) + return FWUPD_PLUGIN_FLAG_FAILED_OPEN; + if (g_strcmp0(plugin_flag, "require-hwid") == 0) + return FWUPD_PLUGIN_FLAG_REQUIRE_HWID; + if (g_strcmp0(plugin_flag, "kernel-too-old") == 0) + return FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD; + if (g_strcmp0(plugin_flag, "auth-required") == 0) + return FWUPD_PLUGIN_FLAG_AUTH_REQUIRED; + if (g_strcmp0(plugin_flag, "secure-config") == 0) + return FWUPD_PLUGIN_FLAG_SECURE_CONFIG; + if (g_strcmp0(plugin_flag, "modular") == 0) + return FWUPD_PLUGIN_FLAG_MODULAR; + return FWUPD_DEVICE_FLAG_UNKNOWN; +} + +/** + * fwupd_update_state_to_string: + * @update_state: the update state, e.g. %FWUPD_UPDATE_STATE_PENDING + * + * Converts an enumerated update state to a string. + * + * Returns: identifier string + * + * Since: 0.7.0 + **/ +const gchar * +fwupd_update_state_to_string(FwupdUpdateState update_state) +{ + if (update_state == FWUPD_UPDATE_STATE_UNKNOWN) + return "unknown"; + if (update_state == FWUPD_UPDATE_STATE_PENDING) + return "pending"; + if (update_state == FWUPD_UPDATE_STATE_SUCCESS) + 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; +} + +/** + * fwupd_update_state_from_string: + * @update_state: (nullable): a string, e.g. `pending` + * + * Converts a string to an enumerated update state. + * + * Returns: enumerated value + * + * Since: 0.7.0 + **/ +FwupdUpdateState +fwupd_update_state_from_string(const gchar *update_state) +{ + if (g_strcmp0(update_state, "unknown") == 0) + return FWUPD_UPDATE_STATE_UNKNOWN; + if (g_strcmp0(update_state, "pending") == 0) + return FWUPD_UPDATE_STATE_PENDING; + if (g_strcmp0(update_state, "success") == 0) + 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; +} + +/** + * fwupd_trust_flag_to_string: + * @trust_flag: the trust flags, e.g. %FWUPD_TRUST_FLAG_PAYLOAD + * + * Converts an enumerated trust flag to a string. + * + * Returns: identifier string + * + * Since: 0.7.0 + **/ +const gchar * +fwupd_trust_flag_to_string(FwupdTrustFlags trust_flag) +{ + if (trust_flag == FWUPD_TRUST_FLAG_NONE) + return "none"; + if (trust_flag == FWUPD_TRUST_FLAG_PAYLOAD) + return "payload"; + if (trust_flag == FWUPD_TRUST_FLAG_METADATA) + return "metadata"; + return NULL; +} + +/** + * fwupd_trust_flag_from_string: + * @trust_flag: (nullable): a string, e.g. `payload` + * + * Converts a string to an enumerated trust flag. + * + * Returns: enumerated value + * + * Since: 0.7.0 + **/ +FwupdTrustFlags +fwupd_trust_flag_from_string(const gchar *trust_flag) +{ + if (g_strcmp0(trust_flag, "none") == 0) + return FWUPD_TRUST_FLAG_NONE; + if (g_strcmp0(trust_flag, "payload") == 0) + return FWUPD_TRUST_FLAG_PAYLOAD; + if (g_strcmp0(trust_flag, "metadata") == 0) + return FWUPD_TRUST_FLAG_METADATA; + return FWUPD_TRUST_FLAG_LAST; +} + +/** + * fwupd_feature_flag_to_string: + * @feature_flag: a single feature flag, e.g. %FWUPD_FEATURE_FLAG_DETACH_ACTION + * + * Converts a feature flag to a string. + * + * Returns: identifier string + * + * Since: 1.4.5 + **/ +const gchar * +fwupd_feature_flag_to_string(FwupdFeatureFlags feature_flag) +{ + if (feature_flag == FWUPD_FEATURE_FLAG_NONE) + return "none"; + if (feature_flag == FWUPD_FEATURE_FLAG_CAN_REPORT) + return "can-report"; + if (feature_flag == FWUPD_FEATURE_FLAG_DETACH_ACTION) + return "detach-action"; + if (feature_flag == FWUPD_FEATURE_FLAG_UPDATE_ACTION) + return "update-action"; + if (feature_flag == FWUPD_FEATURE_FLAG_SWITCH_BRANCH) + return "switch-branch"; + if (feature_flag == FWUPD_FEATURE_FLAG_REQUESTS) + return "requests"; + if (feature_flag == FWUPD_FEATURE_FLAG_FDE_WARNING) + return "fde-warning"; + if (feature_flag == FWUPD_FEATURE_FLAG_COMMUNITY_TEXT) + return "community-text"; + if (feature_flag == FWUPD_FEATURE_FLAG_SHOW_PROBLEMS) + return "show-problems"; + if (feature_flag == FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION) + return "allow-authentication"; + return NULL; +} + +/** + * fwupd_feature_flag_from_string: + * @feature_flag: (nullable): a string, e.g. `detach-action` + * + * Converts a string to an enumerated feature flag. + * + * Returns: enumerated value + * + * Since: 1.4.5 + **/ +FwupdFeatureFlags +fwupd_feature_flag_from_string(const gchar *feature_flag) +{ + if (g_strcmp0(feature_flag, "none") == 0) + return FWUPD_FEATURE_FLAG_NONE; + if (g_strcmp0(feature_flag, "can-report") == 0) + return FWUPD_FEATURE_FLAG_CAN_REPORT; + if (g_strcmp0(feature_flag, "detach-action") == 0) + return FWUPD_FEATURE_FLAG_DETACH_ACTION; + if (g_strcmp0(feature_flag, "update-action") == 0) + return FWUPD_FEATURE_FLAG_UPDATE_ACTION; + if (g_strcmp0(feature_flag, "switch-branch") == 0) + return FWUPD_FEATURE_FLAG_SWITCH_BRANCH; + if (g_strcmp0(feature_flag, "requests") == 0) + return FWUPD_FEATURE_FLAG_REQUESTS; + if (g_strcmp0(feature_flag, "fde-warning") == 0) + return FWUPD_FEATURE_FLAG_FDE_WARNING; + if (g_strcmp0(feature_flag, "community-text") == 0) + return FWUPD_FEATURE_FLAG_COMMUNITY_TEXT; + if (g_strcmp0(feature_flag, "show-problems") == 0) + return FWUPD_FEATURE_FLAG_SHOW_PROBLEMS; + if (g_strcmp0(feature_flag, "allow-authentication") == 0) + return FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION; + return FWUPD_FEATURE_FLAG_LAST; +} + +/** + * fwupd_keyring_kind_from_string: + * @keyring_kind: (nullable): a string, e.g. `gpg` + * + * Converts an printable string to an enumerated keyring kind. + * + * Returns: keyring kind, e.g. %FWUPD_KEYRING_KIND_GPG + * + * Since: 0.9.7 + **/ +FwupdKeyringKind +fwupd_keyring_kind_from_string(const gchar *keyring_kind) +{ + if (g_strcmp0(keyring_kind, "none") == 0) + return FWUPD_KEYRING_KIND_NONE; + if (g_strcmp0(keyring_kind, "gpg") == 0) + return FWUPD_KEYRING_KIND_GPG; + if (g_strcmp0(keyring_kind, "pkcs7") == 0) + return FWUPD_KEYRING_KIND_PKCS7; + if (g_strcmp0(keyring_kind, "jcat") == 0) + return FWUPD_KEYRING_KIND_JCAT; + return FWUPD_KEYRING_KIND_UNKNOWN; +} + +/** + * fwupd_keyring_kind_to_string: + * @keyring_kind: a #FwupdKeyringKind, e.g. %FWUPD_KEYRING_KIND_GPG + * + * Converts an enumerated keyring kind to a printable string. + * + * Returns: a string, e.g. `gpg` + * + * Since: 0.9.7 + **/ +const gchar * +fwupd_keyring_kind_to_string(FwupdKeyringKind keyring_kind) +{ + if (keyring_kind == FWUPD_KEYRING_KIND_NONE) + return "none"; + if (keyring_kind == FWUPD_KEYRING_KIND_GPG) + return "gpg"; + if (keyring_kind == FWUPD_KEYRING_KIND_PKCS7) + return "pkcs7"; + if (keyring_kind == FWUPD_KEYRING_KIND_JCAT) + return "jcat"; + return NULL; +} + +/** + * fwupd_release_flag_to_string: + * @release_flag: a release flag, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD + * + * Converts an enumerated release flag to a string. + * + * Returns: 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"; + if (release_flag == FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH) + return "is-alternate-branch"; + if (release_flag == FWUPD_RELEASE_FLAG_IS_COMMUNITY) + return "is-community"; + return NULL; +} + +/** + * fwupd_release_flag_from_string: + * @release_flag: (nullable): a string, e.g. `trusted-payload` + * + * Converts a string to an enumerated release flag. + * + * Returns: 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; + if (g_strcmp0(release_flag, "is-alternate-branch") == 0) + return FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH; + if (g_strcmp0(release_flag, "is-community") == 0) + return FWUPD_RELEASE_FLAG_IS_COMMUNITY; + return FWUPD_RELEASE_FLAG_NONE; +} + +/** + * fwupd_release_urgency_to_string: + * @release_urgency: a release urgency, e.g. %FWUPD_RELEASE_URGENCY_HIGH + * + * Converts an enumerated release urgency to a string. + * + * Returns: identifier string + * + * Since: 1.4.0 + **/ +const gchar * +fwupd_release_urgency_to_string(FwupdReleaseUrgency release_urgency) +{ + if (release_urgency == FWUPD_RELEASE_URGENCY_LOW) + return "low"; + if (release_urgency == FWUPD_RELEASE_URGENCY_MEDIUM) + return "medium"; + if (release_urgency == FWUPD_RELEASE_URGENCY_HIGH) + return "high"; + if (release_urgency == FWUPD_RELEASE_URGENCY_CRITICAL) + return "critical"; + return NULL; +} + +/** + * fwupd_release_urgency_from_string: + * @release_urgency: (nullable): a string, e.g. `low` + * + * Converts a string to an enumerated release urgency value. + * + * Returns: enumerated value + * + * Since: 1.4.0 + **/ +FwupdReleaseUrgency +fwupd_release_urgency_from_string(const gchar *release_urgency) +{ + if (g_strcmp0(release_urgency, "low") == 0) + return FWUPD_RELEASE_URGENCY_LOW; + if (g_strcmp0(release_urgency, "medium") == 0) + return FWUPD_RELEASE_URGENCY_MEDIUM; + if (g_strcmp0(release_urgency, "high") == 0) + return FWUPD_RELEASE_URGENCY_HIGH; + if (g_strcmp0(release_urgency, "critical") == 0) + return FWUPD_RELEASE_URGENCY_CRITICAL; + return FWUPD_RELEASE_URGENCY_UNKNOWN; +} + +/** + * fwupd_version_format_from_string: + * @str: (nullable): a string, e.g. `quad` + * + * Converts text to a display version type. + * + * Returns: an enumerated version format, 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; + if (g_strcmp0(str, "surface-legacy") == 0) + return FWUPD_VERSION_FORMAT_SURFACE_LEGACY; + if (g_strcmp0(str, "surface") == 0) + return FWUPD_VERSION_FORMAT_SURFACE; + if (g_strcmp0(str, "dell-bios") == 0) + return FWUPD_VERSION_FORMAT_DELL_BIOS; + if (g_strcmp0(str, "hex") == 0) + return FWUPD_VERSION_FORMAT_HEX; + return FWUPD_VERSION_FORMAT_UNKNOWN; +} + +/** + * fwupd_version_format_to_string: + * @kind: a version format, e.g. %FWUPD_VERSION_FORMAT_TRIPLET + * + * Converts an enumerated version format 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"; + if (kind == FWUPD_VERSION_FORMAT_SURFACE_LEGACY) + return "surface-legacy"; + if (kind == FWUPD_VERSION_FORMAT_SURFACE) + return "surface"; + if (kind == FWUPD_VERSION_FORMAT_DELL_BIOS) + return "dell-bios"; + if (kind == FWUPD_VERSION_FORMAT_HEX) + return "hex"; + return NULL; +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-enums.h b/fwupd-1.8.6/libfwupd/fwupd-enums.h new file mode 100644 index 0000000000000000000000000000000000000000..6593455c9d266366f44b7c94972524f541a0d008 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-enums.h @@ -0,0 +1,1082 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +/** + * FwupdStatus: + * @FWUPD_STATUS_UNKNOWN: Unknown state + * @FWUPD_STATUS_IDLE: Idle + * @FWUPD_STATUS_LOADING: Loading a resource + * @FWUPD_STATUS_DECOMPRESSING: Decompressing firmware + * @FWUPD_STATUS_DEVICE_RESTART: Restarting the device + * @FWUPD_STATUS_DEVICE_WRITE: Writing to a device + * @FWUPD_STATUS_DEVICE_VERIFY: Verifying (reading) a device + * @FWUPD_STATUS_SCHEDULING: Scheduling an offline update + * @FWUPD_STATUS_DOWNLOADING: A file is downloading + * @FWUPD_STATUS_DEVICE_READ: Reading from a device + * @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. + **/ +typedef enum { + FWUPD_STATUS_UNKNOWN, /* Since: 0.1.1 */ + FWUPD_STATUS_IDLE, /* Since: 0.1.1 */ + FWUPD_STATUS_LOADING, /* Since: 0.1.1 */ + FWUPD_STATUS_DECOMPRESSING, /* Since: 0.1.1 */ + FWUPD_STATUS_DEVICE_RESTART, /* Since: 0.1.1 */ + FWUPD_STATUS_DEVICE_WRITE, /* Since: 0.1.1 */ + FWUPD_STATUS_DEVICE_VERIFY, /* Since: 0.1.1 */ + FWUPD_STATUS_SCHEDULING, /* Since: 0.1.1 */ + FWUPD_STATUS_DOWNLOADING, /* Since: 0.9.4 */ + FWUPD_STATUS_DEVICE_READ, /* Since: 1.0.0 */ + 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; + +/** + * FwupdTrustFlags: + * @FWUPD_TRUST_FLAG_NONE: No trust + * @FWUPD_TRUST_FLAG_PAYLOAD: The firmware is trusted + * @FWUPD_TRUST_FLAG_METADATA: The metadata is trusted + * + * The flags to show the level of trust. + **/ +typedef enum { + FWUPD_TRUST_FLAG_NONE = 0, /* Since: 0.1.2 */ + FWUPD_TRUST_FLAG_PAYLOAD = 1 << 0, /* Since: 0.1.2 */ + FWUPD_TRUST_FLAG_METADATA = 1 << 1, /* Since: 0.1.2 */ + /*< private >*/ + FWUPD_TRUST_FLAG_LAST +} FwupdTrustFlags; + +/** + * FwupdFeatureFlags: + * @FWUPD_FEATURE_FLAG_NONE: No trust + * @FWUPD_FEATURE_FLAG_CAN_REPORT: Can upload a report of the update back to the server + * @FWUPD_FEATURE_FLAG_DETACH_ACTION: Can perform detach action, typically showing text + * @FWUPD_FEATURE_FLAG_UPDATE_ACTION: Can perform update action, typically showing text + * @FWUPD_FEATURE_FLAG_SWITCH_BRANCH: Can switch the firmware branch + * @FWUPD_FEATURE_FLAG_REQUESTS: Can show interactive requests + * @FWUPD_FEATURE_FLAG_FDE_WARNING: Can warn about full disk encryption + * @FWUPD_FEATURE_FLAG_COMMUNITY_TEXT: Can show information about community supported + * @FWUPD_FEATURE_FLAG_SHOW_PROBLEMS: Can show problems when getting the update list + * @FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION: Can authenticate with PolicyKit for requests + * + * The flags to the feature capabilities of the front-end client. + **/ +typedef enum { + FWUPD_FEATURE_FLAG_NONE = 0, /* Since: 1.4.5 */ + FWUPD_FEATURE_FLAG_CAN_REPORT = 1 << 0, /* Since: 1.4.5 */ + FWUPD_FEATURE_FLAG_DETACH_ACTION = 1 << 1, /* Since: 1.4.5 */ + FWUPD_FEATURE_FLAG_UPDATE_ACTION = 1 << 2, /* Since: 1.4.5 */ + FWUPD_FEATURE_FLAG_SWITCH_BRANCH = 1 << 3, /* Since: 1.5.0 */ + FWUPD_FEATURE_FLAG_REQUESTS = 1 << 4, /* Since: 1.6.2 */ + FWUPD_FEATURE_FLAG_FDE_WARNING = 1 << 5, /* Since: 1.7.1 */ + FWUPD_FEATURE_FLAG_COMMUNITY_TEXT = 1 << 6, /* Since: 1.7.5 */ + FWUPD_FEATURE_FLAG_SHOW_PROBLEMS = 1 << 7, /* Since: 1.8.1 */ + FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION = 1 << 8, /* Since: 1.8.4 */ + /*< private >*/ + FWUPD_FEATURE_FLAG_LAST +} FwupdFeatureFlags; + +/** + * FWUPD_DEVICE_FLAG_NONE: + * + * No flags set + * + * Since 0.1.3 + */ +#define FWUPD_DEVICE_FLAG_NONE (0u) +/** + * FWUPD_DEVICE_FLAG_INTERNAL: + * + * Device is internal to the platform and cannot be removed easily. + * + * Since 0.1.3 + */ +#define FWUPD_DEVICE_FLAG_INTERNAL (1u << 0) +/** + * FWUPD_DEVICE_FLAG_UPDATABLE: + * + * Device has the ability to be updated in this or any other mode. + * + * Since 0.9.7 + */ +#define FWUPD_DEVICE_FLAG_UPDATABLE (1u << 1) +/** + * FWUPD_DEVICE_FLAG_ONLY_OFFLINE: + * + * Update can only be done from a limited functionality OS (offline mode). + * + * Since 0.9.7 + */ +#define FWUPD_DEVICE_FLAG_ONLY_OFFLINE (1u << 2) +/** + * FWUPD_DEVICE_FLAG_REQUIRE_AC: + * + * Device requires an external power source to be connected or the battery + * level at a minimum threshold to update. + * + * Since 0.6.3 + */ +#define FWUPD_DEVICE_FLAG_REQUIRE_AC (1u << 3) +/** + * FWUPD_DEVICE_FLAG_LOCKED: + * + * The device can not be updated without manual user interaction. + * + * Since 0.6.3 + */ +#define FWUPD_DEVICE_FLAG_LOCKED (1u << 4) +/** + * FWUPD_DEVICE_FLAG_SUPPORTED: + * + * The device is found in metadata loaded into the daemon. + * + * Since 0.7.1 + */ +#define FWUPD_DEVICE_FLAG_SUPPORTED (1u << 5) +/** + * FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER: + * + * The device requires entering a bootloader mode to be manually. + * + * Since 0.7.3 + */ +#define FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER (1u << 6) +/** + * FWUPD_DEVICE_FLAG_REGISTERED: + * + * The device has been registered with other plugins. + * + * Since 0.9.7 + */ +#define FWUPD_DEVICE_FLAG_REGISTERED (1u << 7) +/** + * FWUPD_DEVICE_FLAG_NEEDS_REBOOT: + * + * The device requires a system reboot to apply firmware or to reload hardware. + * + * Since 0.9.7 + */ +#define FWUPD_DEVICE_FLAG_NEEDS_REBOOT (1u << 8) +/** + * FWUPD_DEVICE_FLAG_REPORTED: + * + * The success or failure of a previous update has been reported to a metadata server. + * + * Since: 1.0.4 + */ +#define FWUPD_DEVICE_FLAG_REPORTED (1u << 9) +/** + * FWUPD_DEVICE_FLAG_NOTIFIED: + * + * The user has been notified about a change in the device state. + * + * Since: 1.0.5 + */ +#define FWUPD_DEVICE_FLAG_NOTIFIED (1u << 10) +/** + * FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION: + * + * The device will always display use the runtime version rather than the bootloader version. + * + * Since: 1.0.6 + */ +#define FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION (1u << 11) +/** + * FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST: + * + * The composite device requires installation of composite firmware on the parent before the child. + * Normally the child is installed before the parent. + * + * Since: 1.0.8 + */ +#define FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST (1u << 12) +/** + * FWUPD_DEVICE_FLAG_IS_BOOTLOADER: + * + * The device is currently in a read-only bootloader mode and not running application code. + * + * Since: 1.0.8 + */ +#define FWUPD_DEVICE_FLAG_IS_BOOTLOADER (1u << 13) +/** + * FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG: + * + * The device is in the middle of and update and the hardware is waiting to be probed/replugged. + * + * Since: 1.1.2 + */ +#define FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG (1u << 14) +/** + * FWUPD_DEVICE_FLAG_IGNORE_VALIDATION: + * + * When processing an update for the device, plugins should ignore all validation safety checks. + * + * Since: 1.1.2 + */ +#define FWUPD_DEVICE_FLAG_IGNORE_VALIDATION (1u << 15) +/** + * FWUPD_DEVICE_FLAG_TRUSTED: + * + * A trusted client is reading information about the device. + * Extra metadata such as serial number can be exposed about this device. + * + * Since: 1.1.2 + */ +#define FWUPD_DEVICE_FLAG_TRUSTED (1u << 16) +/** + * FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN: + * + * The device requires the system to be shutdown to finish application of new firmware. + * + * Since: 1.2.4 + */ +#define FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN (1u << 17) +/** + * FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED: + * + * The device requires the update to be retried, possibly with a different plugin. + * + * Since: 1.2.5 + */ +#define FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED (1u << 18) +/** + * FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS: + * + * Deprecated, no not use + * + * Since: 1.2.5 + * Deprecated 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS (1u << 19) +/** + * FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION: + * + * The device update needs to be separately activated. + * This process may occur automatically on shutdown in some operating systems + * or when the device is unplugged with some devices. + * + * Since: 1.2.6 + */ +#define FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION (1u << 20) +/** + * FWUPD_DEVICE_FLAG_ENSURE_SEMVER: + * + * Deprecated, no not use + * + * Since: 1.2.9 + * Deprecate: 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_ENSURE_SEMVER (1u << 21) +/** + * FWUPD_DEVICE_FLAG_HISTORICAL: + * + * The device is used for historical data only. + * + * Since: 1.3.2 + */ +#define FWUPD_DEVICE_FLAG_HISTORICAL (1u << 22) +/** + * FWUPD_DEVICE_FLAG_ONLY_SUPPORTED: + * + * Deprecated, no not use + * + * Since: 1.3.3 + * Deprecated 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_ONLY_SUPPORTED (1u << 23) +/** + * FWUPD_DEVICE_FLAG_WILL_DISAPPEAR: + * + * The device will disappear after the update is complete and success + * or failure can't be verified. + * + * Since: 1.3.3 + */ +#define FWUPD_DEVICE_FLAG_WILL_DISAPPEAR (1u << 24) +/** + * FWUPD_DEVICE_FLAG_CAN_VERIFY: + * + * The device checksums can be compared against metadata. + * + * Since: 1.3.3 + */ +#define FWUPD_DEVICE_FLAG_CAN_VERIFY (1u << 25) +/** + * FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE: + * + * The device application firmware image can be dumped from device for verification. + * + * Since: 1.3.3 + */ +#define FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE (1u << 26) +/** + * FWUPD_DEVICE_FLAG_DUAL_IMAGE: + * + * The device firmware update architecture uses a redundancy mechanism such + * as A/B partitions for updates. + * + * Since: 1.3.3 + */ +#define FWUPD_DEVICE_FLAG_DUAL_IMAGE (1u << 27) +/** + * FWUPD_DEVICE_FLAG_SELF_RECOVERY: + * + * In flashing mode, the device will only accept intended payloads and will + * revert back to a valid firmware image if an invalid or incomplete payload was sent. + * + * Since: 1.3.3 + */ +#define FWUPD_DEVICE_FLAG_SELF_RECOVERY (1u << 28) +/** + * FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE: + * + * The device remains usable while the update flashes or schedules the update. + * The update will implicitly be applied next time the device is power cycled or possibly activated. + * + * Since: 1.3.3 + */ +#define FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE (1u << 29) +/** + * FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED: + * + * All firmware updates for this device require a firmware version check. + * + * Since: 1.3.7 + */ +#define FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED (1u << 30) +/** + * FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES: + * + * Install each intermediate releases for the device rather than jumping directly to the newest. + * + * Since: 1.3.7 + */ +#define FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES (1u << 31) +/** + * FWUPD_DEVICE_FLAG_MD_SET_NAME: + * + * Deprecated, no not use + * + * Since: 1.4.0 + * Deprecated 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_MD_SET_NAME (1llu << 32) +/** + * FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY: + * + * Deprecated, no not use + * + * Since: 1.4.0 + * Deprecated 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY (1llu << 33) +/** + * FWUPD_DEVICE_FLAG_MD_SET_VERFMT: + * + * Deprecated, no not use + * + * Since: 1.4.0 + * Deprecated 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_MD_SET_VERFMT (1llu << 34) +/** + * FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS: + * + * The device will add counterpart GUIDs from an alternate mode like bootloader. + * This flag is typically specified in a quirk. + * + * Since: 1.4.0 + */ +#define FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS (1llu << 35) +/** + * FWUPD_DEVICE_FLAG_NO_GUID_MATCHING: + * + * Deprecated, no not use + * + * Since: 1.4.1 + * Deprecated 1.5.8 + */ +#define FWUPD_DEVICE_FLAG_NO_GUID_MATCHING (1llu << 36) +/** + * FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN: + * + * The device is updatable but is currently inhbitied from updates in the client. + * Reasons include but are not limited to low power or requiring reboot from a previous update. + * + * Since: 1.4.1 + */ +#define FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN (1llu << 37) +/** + * FWUPD_DEVICE_FLAG_SKIPS_RESTART: + * + * The device relies upon activation or power cycle to load firmware. + * + * Since: 1.5.0 + */ +#define FWUPD_DEVICE_FLAG_SKIPS_RESTART (1llu << 38) +/** + * FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES: + * + * The device supports switching to a different stream of firmware. + * + * Since: 1.5.0 + */ +#define FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES (1llu << 39) +/** + * FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL: + * + * The device firmware should be saved before installing firmware. + * + * Since: 1.5.0 + */ +#define FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL (1llu << 40) +/** + * FWUPD_DEVICE_FLAG_MD_SET_ICON: + * + * Deprecated, no not use + * + * Since: 1.5.2 + * Deprecated 1.5.5 + */ +#define FWUPD_DEVICE_FLAG_MD_SET_ICON (1llu << 41) +/** + * FWUPD_DEVICE_FLAG_WILDCARD_INSTALL: + * + * All devices with matching GUIDs will be updated at the same time. + * + * For some devices it is not possible to have different versions of firmware + * for hardware of the same type. Updating one device will force update of + * others with exactly the same instance IDs. + * + * Since: 1.6.2 + */ +#define FWUPD_DEVICE_FLAG_WILDCARD_INSTALL (1llu << 42) +/** + * FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE: + * + * The device firmware can only be updated to a newer version and never downgraded or reinstalled. + * + * Since 1.6.2 + */ +#define FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE (1llu << 43) +/** + * FWUPD_DEVICE_FLAG_UNREACHABLE: + * + * The device is currently unreachable, perhaps because it is in a lower power state or is out of + * wireless range. + * + * Since 1.7.0 + */ +#define FWUPD_DEVICE_FLAG_UNREACHABLE (1llu << 44) +/** + * FWUPD_DEVICE_FLAG_AFFECTS_FDE: + * + * The device is warning that a volume with full-disk-encryption was found on this machine, + * typically a Windows NTFS partition with BitLocker. + * Updating the firmware on this device may invalidate secrets used to decrypt the volume, and + * the recovery key may be required. + * + * Supported clients will display this information as a warning to the user. + * + * Since: 1.7.1 + */ +#define FWUPD_DEVICE_FLAG_AFFECTS_FDE (1llu << 45) +/** + * FWUPD_DEVICE_FLAG_END_OF_LIFE: + * + * The device is no longer supported by the original hardware vendor as it is considered + * end-of-life. It it unlikely to receive firmware updates, even for security issues. + * + * Since: 1.7.5 + */ +#define FWUPD_DEVICE_FLAG_END_OF_LIFE (1llu << 46) +/** + * FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD: + * + * The firmware payload is verified on-device the payload using strong cryptography such + * as RSA, AES or ECC. + * + * It is usually not possible to modify or flash custom firmware not provided by the vendor. + * + * Since: 1.7.6 + */ +#define FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD (1llu << 47) +/** + * FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD: + * + * The firmware payload is unsigned and it is possible to modify and flash custom firmware. + * + * Since: 1.7.6 + */ +#define FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD (1llu << 48) +/** + * FWUPD_DEVICE_FLAG_UNKNOWN: + * + * This flag is not defined, this typically will happen from mismatched + * fwupd library and clients. + * + * Since 0.7.3 + */ +#define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 +/** + * FwupdDeviceFlags: + * + * Flags used to represent device attributes + */ +typedef guint64 FwupdDeviceFlags; + +/** + * FWUPD_DEVICE_PROBLEM_NONE: + * + * No device problems detected. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_NONE (0u) +/** + * FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW: + * + * The system power is too low to perform the update. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW (1u << 0) +/** + * FWUPD_DEVICE_PROBLEM_UNREACHABLE: + * + * The device is unreachable, or out of wireless range. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_UNREACHABLE (1u << 1) +/** + * FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW: + * + * The device battery power is too low. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW (1u << 2) +/** + * FWUPD_DEVICE_PROBLEM_UPDATE_PENDING: + * + * The device is waiting for the update to be applied. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_UPDATE_PENDING (1u << 3) +/** + * FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER: + * + * The device requires AC power to be connected. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER (1u << 4) +/** + * FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED: + * + * The device cannot be used while the laptop lid is closed. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED (1u << 5) +/** + * FWUPD_DEVICE_PROBLEM_IS_EMULATED: + * + * The device is emulated from a different host. + * + * Since 1.8.3 + */ +#define FWUPD_DEVICE_PROBLEM_IS_EMULATED (1u << 6) +/** + * FWUPD_DEVICE_PROBLEM_MISSING_LICENSE: + * + * The device cannot be updated due to missing vendor's license. + * + * Since 1.8.6 + */ +#define FWUPD_DEVICE_PROBLEM_MISSING_LICENSE (1u << 7) +/** + * FWUPD_DEVICE_PROBLEM_UNKNOWN: + * + * This problem is not defined, this typically will happen from mismatched + * fwupd library and clients. + * + * Since 1.8.1 + */ +#define FWUPD_DEVICE_PROBLEM_UNKNOWN G_MAXUINT64 +/** + * FwupdDeviceProblem: + * + * Problems are reasons why the device is not updatable. + * + * All problems have to be fixable by the user, rather than the plugin author. + */ +typedef guint64 FwupdDeviceProblem; + +/** + * FWUPD_RELEASE_FLAG_NONE: + * + * No flags are set. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_NONE (0u) +/** + * FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD: + * + * The payload binary is trusted. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD (1u << 0) +/** + * FWUPD_RELEASE_FLAG_TRUSTED_METADATA: + * + * The payload metadata is trusted. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_TRUSTED_METADATA (1u << 1) +/** + * FWUPD_RELEASE_FLAG_IS_UPGRADE: + * + * The release is newer than the device version. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_IS_UPGRADE (1u << 2) +/** + * FWUPD_RELEASE_FLAG_IS_DOWNGRADE: + * + * The release is older than the device version. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_IS_DOWNGRADE (1u << 3) +/** + * FWUPD_RELEASE_FLAG_BLOCKED_VERSION: + * + * The installation of the release is blocked as below device version-lowest. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_BLOCKED_VERSION (1u << 4) +/** + * FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL: + * + * The installation of the release is blocked as release not approved by an administrator. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL (1u << 5) +/** + * FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH: + * + * The release is an alternate branch of firmware. + * + * Since: 1.5.0 + */ +#define FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH (1u << 6) +/** + * FWUPD_RELEASE_FLAG_IS_COMMUNITY: + * + * The release is supported by the community and not the hardware vendor. + * + * Since: 1.7.5 + */ +#define FWUPD_RELEASE_FLAG_IS_COMMUNITY (1u << 7) +/** + * FWUPD_RELEASE_FLAG_UNKNOWN: + * + * The release flag is unknown, typically caused by using mismatched client and daemon. + * + * Since: 1.2.6 + */ +#define FWUPD_RELEASE_FLAG_UNKNOWN G_MAXUINT64 +/** + * FwupdReleaseFlags: + * + * Flags used to represent release attributes + */ +typedef guint64 FwupdReleaseFlags; + +/** + * FwupdReleaseUrgency: + * @FWUPD_RELEASE_URGENCY_UNKNOWN: Unknown + * @FWUPD_RELEASE_URGENCY_LOW: Low + * @FWUPD_RELEASE_URGENCY_MEDIUM: Medium + * @FWUPD_RELEASE_URGENCY_HIGH: High + * @FWUPD_RELEASE_URGENCY_CRITICAL: Critical, e.g. a security fix + * + * The release urgency. + **/ +typedef enum { + FWUPD_RELEASE_URGENCY_UNKNOWN, /* Since: 1.4.0 */ + FWUPD_RELEASE_URGENCY_LOW, /* Since: 1.4.0 */ + FWUPD_RELEASE_URGENCY_MEDIUM, /* Since: 1.4.0 */ + FWUPD_RELEASE_URGENCY_HIGH, /* Since: 1.4.0 */ + FWUPD_RELEASE_URGENCY_CRITICAL, /* Since: 1.4.0 */ + /*< private >*/ + FWUPD_RELEASE_URGENCY_LAST +} FwupdReleaseUrgency; + +/** + * FWUPD_PLUGIN_FLAG_NONE: + * + * No plugin flags are set. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_NONE (0u) +/** + * FWUPD_PLUGIN_FLAG_DISABLED: + * + * The plugin has been disabled, either by daemon configuration or a problem. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_DISABLED (1u << 0) +/** + * FWUPD_PLUGIN_FLAG_USER_WARNING: + * + * The plugin has a problem and would like to show a user warning to a supported client. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_USER_WARNING (1u << 1) +/** + * FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE: + * + * When the plugin loads it should clear the UPDATABLE flag from any devices. + * This typically happens when the device requires a system restart. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE (1u << 2) +/** + * FWUPD_PLUGIN_FLAG_NO_HARDWARE: + * + * The plugin won't load because no supported hardware was found. + * This typically happens with plugins designed for a specific platform design + * (such as the dell plugin only works on Dell systems). + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_NO_HARDWARE (1u << 3) +/** + * FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: + * + * The plugin discovered that UEFI UpdateCapsule are unsupported. + * Supported clients will display this information to a user. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED (1u << 4) +/** + * FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: + * + * The plugin discovered that hardware unlock is required. + * Supported clients will display this information to a user. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED (1u << 5) +/** + * FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED: + * + * The plugin discovered the efivar filesystem is not found and is required for this plugin. + * Supported clients will display this information to a user. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED (1u << 6) +/** + * FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND: + * + * The plugins discovered that the EFI system partition was not found. + * Supported clients will display this information to a user. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND (1u << 7) +/** + * FWUPD_PLUGIN_FLAG_LEGACY_BIOS: + * + * The plugin discovered the system is running in legacy CSM mode. + * Supported clients will display this information to a user. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_LEGACY_BIOS (1u << 8) +/** + * FWUPD_PLUGIN_FLAG_FAILED_OPEN: + * + * Failed to open plugin (missing dependency). + * Supported clients will display this information to a user. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_FAILED_OPEN (1u << 9) +/** + * FWUPD_PLUGIN_FLAG_REQUIRE_HWID: + * + * A specific HWID is required to use this plugin. + * + * Since: 1.5.8 + */ +#define FWUPD_PLUGIN_FLAG_REQUIRE_HWID (1u << 10) +/** + * FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD: + * + * The feature is not supported as the kernel is too old. + * + * Since: 1.6.2 + */ +#define FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD (1u << 11) +/** + * FWUPD_PLUGIN_FLAG_AUTH_REQUIRED: + * + * The plugin requires the user to provide authentication details. + * Supported clients will display this information to a user. + * + * Since: 1.6.2 + */ +#define FWUPD_PLUGIN_FLAG_AUTH_REQUIRED (1u << 12) +/** + * FWUPD_PLUGIN_FLAG_SECURE_CONFIG: + * + * The plugin requires the config file to be saved with permissions that only allow the root user + * to read. + * + * Since: 1.8.5 + */ +#define FWUPD_PLUGIN_FLAG_SECURE_CONFIG (1u << 13) +/** + * FWUPD_PLUGIN_FLAG_MODULAR: + * + * The plugin is loaded from an external module. + * + * Since: 1.8.6 + */ +#define FWUPD_PLUGIN_FLAG_MODULAR (1u << 14) +/** + * FWUPD_PLUGIN_FLAG_UNKNOWN: + * + * The plugin flag is Unknown. + * This is usually caused by a mismatched libfwupdplugin and daemon. + * + * Since: 1.5.0 + */ +#define FWUPD_PLUGIN_FLAG_UNKNOWN G_MAXUINT64 +/** + * FwupdPluginFlags: + * + * Flags used to represent plugin attributes + */ +typedef guint64 FwupdPluginFlags; + +/** + * FwupdInstallFlags: + * @FWUPD_INSTALL_FLAG_NONE: No flags set + * @FWUPD_INSTALL_FLAG_OFFLINE: Schedule this for next boot + * @FWUPD_INSTALL_FLAG_ALLOW_REINSTALL: Allow reinstalling the same version + * @FWUPD_INSTALL_FLAG_ALLOW_OLDER: Allow downgrading firmware + * @FWUPD_INSTALL_FLAG_FORCE: Force the update even if not a good idea + * @FWUPD_INSTALL_FLAG_NO_HISTORY: Do not write to the history database + * @FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH: Allow firmware branch switching + * @FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM: Ignore firmware CRCs and checksums + * @FWUPD_INSTALL_FLAG_IGNORE_VID_PID: Ignore firmware vendor and project checks + * @FWUPD_INSTALL_FLAG_IGNORE_POWER: Ignore requirement of external power source + *(Deprecated since 1.7.0) + * @FWUPD_INSTALL_FLAG_NO_SEARCH: Do not use heuristics when parsing the image + * + * Flags to set when performing the firmware update or install. + **/ +typedef enum { + FWUPD_INSTALL_FLAG_NONE = 0, /* Since: 0.7.0 */ + FWUPD_INSTALL_FLAG_OFFLINE = 1 << 0, /* Since: 0.7.0 */ + FWUPD_INSTALL_FLAG_ALLOW_REINSTALL = 1 << 1, /* Since: 0.7.0 */ + FWUPD_INSTALL_FLAG_ALLOW_OLDER = 1 << 2, /* Since: 0.7.0 */ + FWUPD_INSTALL_FLAG_FORCE = 1 << 3, /* Since: 0.7.1 */ + FWUPD_INSTALL_FLAG_NO_HISTORY = 1 << 4, /* Since: 1.0.8 */ + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH = 1 << 5, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM = 1 << 6, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_IGNORE_VID_PID = 1 << 7, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_IGNORE_POWER = 1 << 8, /* Since: 1.5.0 */ + FWUPD_INSTALL_FLAG_NO_SEARCH = 1 << 9, /* Since: 1.5.0 */ + /*< private >*/ + FWUPD_INSTALL_FLAG_LAST +} 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 firmware 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 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. + **/ +typedef enum { + FWUPD_UPDATE_STATE_UNKNOWN, /* Since: 0.7.0 */ + FWUPD_UPDATE_STATE_PENDING, /* Since: 0.7.0 */ + 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; + +/** + * FwupdKeyringKind: + * @FWUPD_KEYRING_KIND_UNKNOWN: Unknown + * @FWUPD_KEYRING_KIND_NONE: No verification + * @FWUPD_KEYRING_KIND_GPG: Verification using GPG + * @FWUPD_KEYRING_KIND_PKCS7: Verification using PKCS7 + * @FWUPD_KEYRING_KIND_JCAT: Verification using Jcat + * + * Type of keyring used on a remote. + **/ +typedef enum { + FWUPD_KEYRING_KIND_UNKNOWN, /* Since: 0.9.7 */ + FWUPD_KEYRING_KIND_NONE, /* Since: 0.9.7 */ + FWUPD_KEYRING_KIND_GPG, /* Since: 0.9.7 */ + FWUPD_KEYRING_KIND_PKCS7, /* Since: 0.9.7 */ + FWUPD_KEYRING_KIND_JCAT, /* Since: 1.4.0 */ + /*< private >*/ + 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: UEFI-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 + * @FWUPD_VERSION_FORMAT_SURFACE_LEGACY: Legacy Microsoft Surface 10b.12b.10b + * @FWUPD_VERSION_FORMAT_SURFACE: Microsoft Surface 8b.16b.8b + * @FWUPD_VERSION_FORMAT_DELL_BIOS: Dell BIOS BB.CC.DD style + * @FWUPD_VERSION_FORMAT_HEX: Hexadecimal 0xAABCCDD style + * + * 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 */ + FWUPD_VERSION_FORMAT_SURFACE_LEGACY, /* Since: 1.3.4 */ + FWUPD_VERSION_FORMAT_SURFACE, /* Since: 1.3.4 */ + FWUPD_VERSION_FORMAT_DELL_BIOS, /* Since: 1.3.6 */ + FWUPD_VERSION_FORMAT_HEX, /* Since: 1.4.0 */ + /*< private >*/ + FWUPD_VERSION_FORMAT_LAST +} FwupdVersionFormat; + +/** + * FWUPD_BATTERY_LEVEL_INVALID: + * + * This value signifies the battery level is either unset, or the value cannot + * be discovered. + */ +#define FWUPD_BATTERY_LEVEL_INVALID 101 + +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_device_problem_to_string(FwupdDeviceProblem device_problem); +FwupdDeviceProblem +fwupd_device_problem_from_string(const gchar *device_problem); +const gchar * +fwupd_plugin_flag_to_string(FwupdPluginFlags plugin_flag); +FwupdPluginFlags +fwupd_plugin_flag_from_string(const gchar *plugin_flag); +const gchar * +fwupd_release_flag_to_string(FwupdReleaseFlags release_flag); +FwupdReleaseFlags +fwupd_release_flag_from_string(const gchar *release_flag); +const gchar * +fwupd_release_urgency_to_string(FwupdReleaseUrgency release_urgency); +FwupdReleaseUrgency +fwupd_release_urgency_from_string(const gchar *release_urgency); +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); +const gchar * +fwupd_feature_flag_to_string(FwupdFeatureFlags feature_flag); +FwupdFeatureFlags +fwupd_feature_flag_from_string(const gchar *feature_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); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-error.c b/fwupd-1.8.6/libfwupd/fwupd-error.c new file mode 100644 index 0000000000000000000000000000000000000000..05db35a5125d5756b147f3f9ee6392c836562f89 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-error.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015-2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fwupd-common.h" +#include "fwupd-enums.h" +#include "fwupd-error.h" + +/** + * fwupd_error_to_string: + * @error: an enumerated error, e.g. %FWUPD_ERROR_VERSION_NEWER + * + * Converts an enumerated error to a string. + * + * Returns: identifier string + * + * Since: 0.7.0 + **/ +const gchar * +fwupd_error_to_string(FwupdError error) +{ + if (error == FWUPD_ERROR_INTERNAL) + return FWUPD_DBUS_INTERFACE ".Internal"; + if (error == FWUPD_ERROR_VERSION_NEWER) + return FWUPD_DBUS_INTERFACE ".VersionNewer"; + if (error == FWUPD_ERROR_VERSION_SAME) + return FWUPD_DBUS_INTERFACE ".VersionSame"; + if (error == FWUPD_ERROR_ALREADY_PENDING) + return FWUPD_DBUS_INTERFACE ".AlreadyPending"; + if (error == FWUPD_ERROR_AUTH_FAILED) + return FWUPD_DBUS_INTERFACE ".AuthFailed"; + if (error == FWUPD_ERROR_READ) + return FWUPD_DBUS_INTERFACE ".Read"; + if (error == FWUPD_ERROR_WRITE) + return FWUPD_DBUS_INTERFACE ".Write"; + if (error == FWUPD_ERROR_INVALID_FILE) + return FWUPD_DBUS_INTERFACE ".InvalidFile"; + if (error == FWUPD_ERROR_NOT_FOUND) + return FWUPD_DBUS_INTERFACE ".NotFound"; + if (error == FWUPD_ERROR_NOTHING_TO_DO) + return FWUPD_DBUS_INTERFACE ".NothingToDo"; + if (error == FWUPD_ERROR_NOT_SUPPORTED) + return FWUPD_DBUS_INTERFACE ".NotSupported"; + if (error == FWUPD_ERROR_SIGNATURE_INVALID) + return FWUPD_DBUS_INTERFACE ".SignatureInvalid"; + if (error == FWUPD_ERROR_AC_POWER_REQUIRED) + 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"; + if (error == FWUPD_ERROR_NEEDS_USER_ACTION) + return FWUPD_DBUS_INTERFACE ".NeedsUserAction"; + if (error == FWUPD_ERROR_AUTH_EXPIRED) + return FWUPD_DBUS_INTERFACE ".AuthExpired"; + return NULL; +} + +/** + * fwupd_error_from_string: + * @error: (nullable): a string, e.g. `org.freedesktop.fwupd.VersionNewer` + * + * Converts a string to an enumerated error. + * + * Returns: enumerated value + * + * Since: 0.7.0 + **/ +FwupdError +fwupd_error_from_string(const gchar *error) +{ + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".Internal") == 0) + return FWUPD_ERROR_INTERNAL; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".VersionNewer") == 0) + return FWUPD_ERROR_VERSION_NEWER; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".VersionSame") == 0) + return FWUPD_ERROR_VERSION_SAME; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".AlreadyPending") == 0) + return FWUPD_ERROR_ALREADY_PENDING; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".AuthFailed") == 0) + return FWUPD_ERROR_AUTH_FAILED; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".Read") == 0) + return FWUPD_ERROR_READ; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".Write") == 0) + return FWUPD_ERROR_WRITE; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".InvalidFile") == 0) + return FWUPD_ERROR_INVALID_FILE; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".NotFound") == 0) + return FWUPD_ERROR_NOT_FOUND; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".NothingToDo") == 0) + return FWUPD_ERROR_NOTHING_TO_DO; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".NotSupported") == 0) + return FWUPD_ERROR_NOT_SUPPORTED; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".SignatureInvalid") == 0) + return FWUPD_ERROR_SIGNATURE_INVALID; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".AcPowerRequired") == 0) + 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; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".NeedsUserAction") == 0) + return FWUPD_ERROR_NEEDS_USER_ACTION; + if (g_strcmp0(error, FWUPD_DBUS_INTERFACE ".AuthExpired") == 0) + return FWUPD_ERROR_AUTH_EXPIRED; + return FWUPD_ERROR_LAST; +} + +/** + * fwupd_error_quark: + * + * The error quark for [enum@FwupdError]. + * + * Returns: an error quark + * + * Since: 0.1.1 + **/ +GQuark +fwupd_error_quark(void) +{ + static GQuark quark = 0; + if (!quark) { + quark = g_quark_from_static_string("FwupdError"); + for (gint i = 0; i < FWUPD_ERROR_LAST; i++) { + g_dbus_error_register_error(quark, i, fwupd_error_to_string(i)); + } + } + return quark; +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-error.h b/fwupd-1.8.6/libfwupd/fwupd-error.h new file mode 100644 index 0000000000000000000000000000000000000000..2740818c5d7e8b687c89deb216800520df868f8b --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-error.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015-2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define FWUPD_ERROR fwupd_error_quark() + +/** + * FwupdError: + * @FWUPD_ERROR_INTERNAL: Internal error + * @FWUPD_ERROR_VERSION_NEWER: Installed newer firmware version + * @FWUPD_ERROR_VERSION_SAME: Installed same firmware version + * @FWUPD_ERROR_ALREADY_PENDING: Already set to be installed offline + * @FWUPD_ERROR_AUTH_FAILED: Failed to get authentication + * @FWUPD_ERROR_READ: Failed to read from device + * @FWUPD_ERROR_WRITE: Failed to write to the device + * @FWUPD_ERROR_INVALID_FILE: Invalid file format + * @FWUPD_ERROR_NOT_FOUND: No matching device exists + * @FWUPD_ERROR_NOTHING_TO_DO: Nothing to do + * @FWUPD_ERROR_NOT_SUPPORTED: Action was not possible + * @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 + * @FWUPD_ERROR_NEEDS_USER_ACTION: User needs to do an action to complete the update + * @FWUPD_ERROR_AUTH_EXPIRED: Failed to get auth as credentials have expired + * + * The error code. + **/ +typedef enum { + FWUPD_ERROR_INTERNAL, /* Since: 0.1.1 */ + FWUPD_ERROR_VERSION_NEWER, /* Since: 0.1.1 */ + FWUPD_ERROR_VERSION_SAME, /* Since: 0.1.1 */ + FWUPD_ERROR_ALREADY_PENDING, /* Since: 0.1.1 */ + FWUPD_ERROR_AUTH_FAILED, /* Since: 0.1.1 */ + FWUPD_ERROR_READ, /* Since: 0.1.1 */ + FWUPD_ERROR_WRITE, /* Since: 0.1.1 */ + FWUPD_ERROR_INVALID_FILE, /* Since: 0.1.1 */ + FWUPD_ERROR_NOT_FOUND, /* Since: 0.1.1 */ + FWUPD_ERROR_NOTHING_TO_DO, /* Since: 0.1.1 */ + FWUPD_ERROR_NOT_SUPPORTED, /* Since: 0.1.1 */ + 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 */ + FWUPD_ERROR_NEEDS_USER_ACTION, /* Since: 1.3.3 */ + FWUPD_ERROR_AUTH_EXPIRED, /* Since: 1.7.5 */ + /*< private >*/ + FWUPD_ERROR_LAST +} FwupdError; + +GQuark +fwupd_error_quark(void); +const gchar * +fwupd_error_to_string(FwupdError error); +FwupdError +fwupd_error_from_string(const gchar *error); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-plugin-private.h b/fwupd-1.8.6/libfwupd/fwupd-plugin-private.h new file mode 100644 index 0000000000000000000000000000000000000000..8897dcec1e8ebd174ae360e25aea8bb8435aa2e2 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-plugin-private.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-plugin.h" + +G_BEGIN_DECLS + +GVariant * +fwupd_plugin_to_variant(FwupdPlugin *self); +void +fwupd_plugin_to_json(FwupdPlugin *self, JsonBuilder *builder); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-plugin.c b/fwupd-1.8.6/libfwupd/fwupd-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..4c045df67c4de6a2664b315593f75b0ee6331e3e --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-plugin.c @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-plugin-private.h" + +/** + * FwupdPlugin: + * + * A plugin which is used by fwupd to enumerate and update devices. + * + * See also: [class@FwupdRelease] + */ + +static void +fwupd_plugin_finalize(GObject *object); + +typedef struct { + gchar *name; + guint64 flags; +} FwupdPluginPrivate; + +enum { PROP_0, PROP_NAME, PROP_FLAGS, PROP_LAST }; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdPlugin, fwupd_plugin, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_plugin_get_instance_private(o)) + +/** + * fwupd_plugin_get_name: + * @self: a #FwupdPlugin + * + * Gets the plugin name. + * + * Returns: the plugin name, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_plugin_get_name(FwupdPlugin *self) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_PLUGIN(self), NULL); + return priv->name; +} + +/** + * fwupd_plugin_set_name: + * @self: a #FwupdPlugin + * @name: the plugin name, e.g. `bios` + * + * Sets the plugin name. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_set_name(FwupdPlugin *self, const gchar *name) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_PLUGIN(self)); + g_return_if_fail(name != NULL); + + /* not changed */ + if (g_strcmp0(priv->name, name) == 0) + return; + + g_free(priv->name); + priv->name = g_strdup(name); + g_object_notify(G_OBJECT(self), "name"); +} + +/** + * fwupd_plugin_get_flags: + * @self: a #FwupdPlugin + * + * Gets the plugin flags. + * + * Returns: plugin flags, or 0 if unset + * + * Since: 1.5.0 + **/ +guint64 +fwupd_plugin_get_flags(FwupdPlugin *self) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_PLUGIN(self), 0); + return priv->flags; +} + +/** + * fwupd_plugin_set_flags: + * @self: a #FwupdPlugin + * @flags: plugin flags, e.g. %FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED + * + * Sets the plugin flags. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_set_flags(FwupdPlugin *self, guint64 flags) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_PLUGIN(self)); + if (priv->flags == flags) + return; + priv->flags = flags; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_plugin_add_flag: + * @self: a #FwupdPlugin + * @flag: the #FwupdPluginFlags + * + * Adds a specific plugin flag to the plugin. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_add_flag(FwupdPlugin *self, FwupdPluginFlags flag) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_PLUGIN(self)); + if (flag == 0) + return; + if ((priv->flags & flag) > 0) + return; + priv->flags |= flag; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_plugin_remove_flag: + * @self: a #FwupdPlugin + * @flag: a plugin flag + * + * Removes a specific plugin flag from the plugin. + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_remove_flag(FwupdPlugin *self, FwupdPluginFlags flag) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_PLUGIN(self)); + if (flag == 0) + return; + if ((priv->flags & flag) == 0) + return; + priv->flags &= ~flag; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_plugin_has_flag: + * @self: a #FwupdPlugin + * @flag: a plugin flag + * + * Finds if the plugin has a specific plugin flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.5.0 + **/ +gboolean +fwupd_plugin_has_flag(FwupdPlugin *self, FwupdPluginFlags flag) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_PLUGIN(self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_plugin_to_variant: + * @self: a #FwupdPlugin + * + * Serialize the plugin data omitting sensitive fields + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.5.0 + **/ +GVariant * +fwupd_plugin_to_variant(FwupdPlugin *self) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + g_return_val_if_fail(FWUPD_IS_PLUGIN(self), NULL); + + /* create an array with all the metadata in */ + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + if (priv->name != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string(priv->name)); + } + if (priv->flags > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_FLAGS, + g_variant_new_uint64(priv->flags)); + } + return g_variant_new("a{sv}", &builder); +} + +static void +fwupd_plugin_from_key_value(FwupdPlugin *self, const gchar *key, GVariant *value) +{ + if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_plugin_set_name(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FLAGS) == 0) { + fwupd_plugin_set_flags(self, g_variant_get_uint64(value)); + return; + } +} + +static void +fwupd_pad_kv_dfl(GString *str, const gchar *key, guint64 plugin_flags) +{ + g_autoptr(GString) tmp = g_string_new(""); + for (guint i = 0; i < 64; i++) { + if ((plugin_flags & ((guint64)1 << i)) == 0) + continue; + g_string_append_printf(tmp, "%s|", fwupd_plugin_flag_to_string((guint64)1 << i)); + } + if (tmp->len == 0) { + g_string_append(tmp, fwupd_plugin_flag_to_string(0)); + } else { + g_string_truncate(tmp, tmp->len - 1); + } + fwupd_pad_kv_str(str, key, tmp->str); +} + +/** + * fwupd_plugin_to_json: + * @self: a #FwupdPlugin + * @builder: a JSON builder + * + * Adds a fwupd plugin to a JSON builder + * + * Since: 1.5.0 + **/ +void +fwupd_plugin_to_json(FwupdPlugin *self, JsonBuilder *builder) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_PLUGIN(self)); + g_return_if_fail(builder != NULL); + + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_NAME, priv->name); + if (priv->flags != FWUPD_PLUGIN_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_plugin_flag_to_string((guint64)1 << i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } +} + +/** + * fwupd_plugin_to_string: + * @self: a #FwupdPlugin + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 1.5.0 + **/ +gchar * +fwupd_plugin_to_string(FwupdPlugin *self) +{ + FwupdPluginPrivate *priv = GET_PRIVATE(self); + GString *str; + + g_return_val_if_fail(FWUPD_IS_PLUGIN(self), NULL); + + str = g_string_new(NULL); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_pad_kv_dfl(str, FWUPD_RESULT_KEY_FLAGS, priv->flags); + return g_string_free(str, FALSE); +} + +static void +fwupd_plugin_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FwupdPlugin *self = FWUPD_PLUGIN(object); + FwupdPluginPrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_NAME: + g_value_set_string(value, priv->name); + break; + case PROP_FLAGS: + g_value_set_uint64(value, priv->flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fwupd_plugin_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FwupdPlugin *self = FWUPD_PLUGIN(object); + switch (prop_id) { + case PROP_NAME: + fwupd_plugin_set_name(self, g_value_get_string(value)); + break; + case PROP_FLAGS: + fwupd_plugin_set_flags(self, g_value_get_uint64(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fwupd_plugin_class_init(FwupdPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->finalize = fwupd_plugin_finalize; + object_class->get_property = fwupd_plugin_get_property; + object_class->set_property = fwupd_plugin_set_property; + + /** + * FwupdPlugin:name: + * + * The plugin name. + * + * Since: 1.5.0 + */ + pspec = + g_param_spec_string("name", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_NAME, pspec); + + /** + * FwupdPlugin:flags: + * + * The plugin flags. + * + * Since: 1.5.0 + */ + pspec = g_param_spec_uint64("flags", + NULL, + NULL, + FWUPD_PLUGIN_FLAG_NONE, + FWUPD_PLUGIN_FLAG_UNKNOWN, + FWUPD_PLUGIN_FLAG_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FLAGS, pspec); +} + +static void +fwupd_plugin_init(FwupdPlugin *self) +{ +} + +static void +fwupd_plugin_finalize(GObject *object) +{ + FwupdPlugin *self = FWUPD_PLUGIN(object); + FwupdPluginPrivate *priv = GET_PRIVATE(self); + g_free(priv->name); + G_OBJECT_CLASS(fwupd_plugin_parent_class)->finalize(object); +} + +static void +fwupd_plugin_set_from_variant_iter(FwupdPlugin *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + fwupd_plugin_from_key_value(self, key, value); + g_variant_unref(value); + } +} + +/** + * fwupd_plugin_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new plugin using serialized data. + * + * Returns: (transfer full): a new #FwupdPlugin, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +FwupdPlugin * +fwupd_plugin_from_variant(GVariant *value) +{ + FwupdPlugin *self = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + /* format from GetDetails */ + type_string = g_variant_get_type_string(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + self = fwupd_plugin_new(); + g_variant_get(value, "(a{sv})", &iter); + fwupd_plugin_set_from_variant_iter(self, iter); + } else if (g_strcmp0(type_string, "a{sv}") == 0) { + self = fwupd_plugin_new(); + g_variant_get(value, "a{sv}", &iter); + fwupd_plugin_set_from_variant_iter(self, iter); + } else { + g_warning("type %s not known", type_string); + } + return self; +} + +/** + * fwupd_plugin_array_from_variant: + * @value: (not nullable): the serialized data + * + * Creates an array of new plugins using serialized data. + * + * Returns: (transfer container) (element-type FwupdPlugin): plugins, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_plugin_array_from_variant(GVariant *value) +{ + GPtrArray *array = NULL; + gsize sz; + g_autoptr(GVariant) untuple = NULL; + + g_return_val_if_fail(value != NULL, 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++) { + FwupdPlugin *self; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value(untuple, i); + self = fwupd_plugin_from_variant(data); + if (self == NULL) + continue; + g_ptr_array_add(array, self); + } + return array; +} + +/** + * fwupd_plugin_new: + * + * Creates a new plugin. + * + * Returns: a new #FwupdPlugin + * + * Since: 1.5.0 + **/ +FwupdPlugin * +fwupd_plugin_new(void) +{ + FwupdPlugin *self; + self = g_object_new(FWUPD_TYPE_PLUGIN, NULL); + return FWUPD_PLUGIN(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-plugin.h b/fwupd-1.8.6/libfwupd/fwupd-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..95072d05df3e08a4211694af273cc5329abaedbd --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-plugin.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_PLUGIN (fwupd_plugin_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdPlugin, fwupd_plugin, FWUPD, PLUGIN, GObject) + +struct _FwupdPluginClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +FwupdPlugin * +fwupd_plugin_new(void); +gchar * +fwupd_plugin_to_string(FwupdPlugin *self); + +const gchar * +fwupd_plugin_get_name(FwupdPlugin *self); +void +fwupd_plugin_set_name(FwupdPlugin *self, const gchar *name); +guint64 +fwupd_plugin_get_flags(FwupdPlugin *self); +void +fwupd_plugin_set_flags(FwupdPlugin *self, guint64 flags); +void +fwupd_plugin_add_flag(FwupdPlugin *self, FwupdPluginFlags flag); +void +fwupd_plugin_remove_flag(FwupdPlugin *self, FwupdPluginFlags flag); +gboolean +fwupd_plugin_has_flag(FwupdPlugin *self, FwupdPluginFlags flag); + +FwupdPlugin * +fwupd_plugin_from_variant(GVariant *value); +GPtrArray * +fwupd_plugin_array_from_variant(GVariant *value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-release-private.h b/fwupd-1.8.6/libfwupd/fwupd-release-private.h new file mode 100644 index 0000000000000000000000000000000000000000..cfc05854c9f34186c0d1e8bc68d6ce4d4b043b9d --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-release-private.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-release.h" + +G_BEGIN_DECLS + +GVariant * +fwupd_release_to_variant(FwupdRelease *self); +void +fwupd_release_to_json(FwupdRelease *self, JsonBuilder *builder); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-release.c b/fwupd-1.8.6/libfwupd/fwupd-release.c new file mode 100644 index 0000000000000000000000000000000000000000..838d153773ca8bfcd41e8511bd8f6945320c7356 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-release.c @@ -0,0 +1,2376 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" +#include "fwupd-release-private.h" + +/** + * FwupdRelease: + * + * A firmware release with a specific version. + * + * Devices can have more than one release, and the releases are typically + * ordered by their version. + * + * See also: [class@FwupdDevice] + */ + +static void +fwupd_release_finalize(GObject *object); + +typedef struct { + GPtrArray *checksums; + GPtrArray *tags; + GPtrArray *categories; + GPtrArray *issues; + GHashTable *metadata; + gchar *description; + gchar *filename; + gchar *protocol; + gchar *homepage; + gchar *details_url; + gchar *source_url; + gchar *appstream_id; + gchar *id; + gchar *detach_caption; + gchar *detach_image; + gchar *license; + gchar *name; + gchar *name_variant_suffix; + gchar *summary; + gchar *branch; + GPtrArray *locations; + gchar *vendor; + gchar *version; + gchar *remote_id; + guint64 size; + guint64 created; + guint32 install_duration; + FwupdReleaseFlags flags; + FwupdReleaseUrgency urgency; + gchar *update_message; + gchar *update_image; +} FwupdReleasePrivate; + +enum { PROP_0, PROP_REMOTE_ID, PROP_LAST }; + +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: + * @self: a #FwupdRelease + * + * Gets the remote ID that can be used for downloading. + * + * Returns: the ID, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_remote_id(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->remote_id; +} + +/** + * fwupd_release_set_remote_id: + * @self: a #FwupdRelease + * @remote_id: the release ID, e.g. `USB:foo` + * + * Sets the remote ID that can be used for downloading. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_remote_id(FwupdRelease *self, const gchar *remote_id) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->remote_id, remote_id) == 0) + return; + + g_free(priv->remote_id); + priv->remote_id = g_strdup(remote_id); + g_object_notify(G_OBJECT(self), "remote-id"); +} + +/** + * fwupd_release_get_version: + * @self: a #FwupdRelease + * + * Gets the update version. + * + * Returns: the update version, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_version(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->version; +} + +/** + * fwupd_release_set_version: + * @self: a #FwupdRelease + * @version: the update version, e.g. `1.2.4` + * + * Sets the update version. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_version(FwupdRelease *self, const gchar *version) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->version, version) == 0) + return; + + g_free(priv->version); + priv->version = g_strdup(version); +} + +/** + * fwupd_release_get_filename: + * @self: a #FwupdRelease + * + * Gets the update filename. + * + * Returns: the update filename, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_filename(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->filename; +} + +/** + * fwupd_release_set_filename: + * @self: a #FwupdRelease + * @filename: the update filename on disk + * + * Sets the update filename. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_filename(FwupdRelease *self, const gchar *filename) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->filename, filename) == 0) + return; + + g_free(priv->filename); + priv->filename = g_strdup(filename); +} + +/** + * fwupd_release_get_update_message: + * @self: 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 *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->update_message; +} + +/** + * fwupd_release_set_update_message: + * @self: a #FwupdRelease + * @update_message: (nullable): the update message string + * + * Sets the update message. + * + * Since: 1.2.4 + **/ +void +fwupd_release_set_update_message(FwupdRelease *self, const gchar *update_message) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->update_message, update_message) == 0) + return; + + g_free(priv->update_message); + priv->update_message = g_strdup(update_message); +} + +/** + * fwupd_release_get_update_image: + * @self: a #FwupdRelease + * + * Gets the update image. + * + * Returns: the update image URL, or %NULL if unset + * + * Since: 1.4.5 + **/ +const gchar * +fwupd_release_get_update_image(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->update_image; +} + +/** + * fwupd_release_set_update_image: + * @self: a #FwupdRelease + * @update_image: (nullable): the update image URL + * + * Sets the update image. + * + * Since: 1.4.5 + **/ +void +fwupd_release_set_update_image(FwupdRelease *self, const gchar *update_image) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->update_image, update_image) == 0) + return; + + g_free(priv->update_image); + priv->update_image = g_strdup(update_image); +} + +/** + * fwupd_release_get_protocol: + * @self: 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 *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->protocol; +} + +/** + * fwupd_release_set_protocol: + * @self: a #FwupdRelease + * @protocol: (nullable): the update protocol, e.g. `org.usb.dfu` + * + * Sets the update protocol. + * + * Since: 1.2.2 + **/ +void +fwupd_release_set_protocol(FwupdRelease *self, const gchar *protocol) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->protocol, protocol) == 0) + return; + + g_free(priv->protocol); + priv->protocol = g_strdup(protocol); +} + +/** + * fwupd_release_get_issues: + * @self: a #FwupdRelease + * + * Gets the list of issues fixed in this release. + * + * Returns: (element-type utf8) (transfer none): the issues, which may be empty + * + * Since: 1.3.2 + **/ +GPtrArray * +fwupd_release_get_issues(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->issues; +} + +/** + * fwupd_release_add_issue: + * @self: a #FwupdRelease + * @issue: (not nullable): the update issue, e.g. `CVE-2019-12345` + * + * Adds an resolved issue to this release. + * + * Since: 1.3.2 + **/ +void +fwupd_release_add_issue(FwupdRelease *self, const gchar *issue) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(issue != NULL); + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *issue_tmp = g_ptr_array_index(priv->issues, i); + if (g_strcmp0(issue_tmp, issue) == 0) + return; + } + g_ptr_array_add(priv->issues, g_strdup(issue)); +} + +/** + * fwupd_release_get_categories: + * @self: 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 *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->categories; +} + +/** + * fwupd_release_add_category: + * @self: a #FwupdRelease + * @category: (not nullable): the update category, e.g. `X-EmbeddedController` + * + * Adds the update category. + * + * Since: 1.2.7 + **/ +void +fwupd_release_add_category(FwupdRelease *self, const gchar *category) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + 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: + * @self: a #FwupdRelease + * @category: (not nullable): 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 *self, const gchar *category) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 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: + * @self: a #FwupdRelease + * + * Gets the release container checksums. + * + * Returns: (element-type utf8) (transfer none): the checksums, which may be empty + * + * Since: 0.9.3 + **/ +GPtrArray * +fwupd_release_get_checksums(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->checksums; +} + +/** + * fwupd_release_add_checksum: + * @self: a #FwupdRelease + * @checksum: (not nullable): the update container checksum + * + * Sets the update checksum. + * + * Since: 0.9.3 + **/ +void +fwupd_release_add_checksum(FwupdRelease *self, const gchar *checksum) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(checksum != NULL); + 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; + } + g_ptr_array_add(priv->checksums, g_strdup(checksum)); +} + +/** + * fwupd_release_has_checksum: + * @self: a #FwupdRelease + * @checksum: (not nullable): the update checksum + * + * Finds out if the release has the update container checksum. + * + * Returns: %TRUE if the release matches + * + * Since: 1.2.6 + **/ +gboolean +fwupd_release_has_checksum(FwupdRelease *self, const gchar *checksum) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 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_tags: + * @self: a #FwupdRelease + * + * Gets the release tags. + * + * Returns: (element-type utf8) (transfer none): the tags, which may be empty + * + * Since: 1.7.3 + **/ +GPtrArray * +fwupd_release_get_tags(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->tags; +} + +/** + * fwupd_release_add_tag: + * @self: a #FwupdRelease + * @tag: (not nullable): the update tag, e.g. `vendor-factory-2021q1` + * + * Adds a specific release tag. + * + * Since: 1.7.3 + **/ +void +fwupd_release_add_tag(FwupdRelease *self, const gchar *tag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(tag != NULL); + for (guint i = 0; i < priv->tags->len; i++) { + const gchar *tag_tmp = g_ptr_array_index(priv->tags, i); + if (g_strcmp0(tag_tmp, tag) == 0) + return; + } + g_ptr_array_add(priv->tags, g_strdup(tag)); +} + +/** + * fwupd_release_has_tag: + * @self: a #FwupdRelease + * @tag: (not nullable): the update tag, e.g. `vendor-factory-2021q1` + * + * Finds out if the release has a specific tag. + * + * Returns: %TRUE if the release matches + * + * Since: 1.7.3 + **/ +gboolean +fwupd_release_has_tag(FwupdRelease *self, const gchar *tag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), FALSE); + g_return_val_if_fail(tag != NULL, FALSE); + for (guint i = 0; i < priv->tags->len; i++) { + const gchar *tag_tmp = g_ptr_array_index(priv->tags, i); + if (g_strcmp0(tag_tmp, tag) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_release_get_metadata: + * @self: a #FwupdRelease + * + * Gets the release metadata. + * + * Returns: (transfer none): the metadata, which may be empty + * + * Since: 1.0.4 + **/ +GHashTable * +fwupd_release_get_metadata(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->metadata; +} + +/** + * fwupd_release_add_metadata_item: + * @self: a #FwupdRelease + * @key: (not nullable): the key + * @value: (not nullable): the value + * + * Sets a release metadata item. + * + * Since: 1.0.4 + **/ +void +fwupd_release_add_metadata_item(FwupdRelease *self, const gchar *key, const gchar *value) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + g_hash_table_insert(priv->metadata, g_strdup(key), g_strdup(value)); +} + +/** + * fwupd_release_add_metadata: + * @self: a #FwupdRelease + * @hash: (not nullable): the key-values + * + * Sets multiple release metadata items. + * + * Since: 1.0.4 + **/ +void +fwupd_release_add_metadata(FwupdRelease *self, GHashTable *hash) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_autoptr(GList) keys = NULL; + + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(hash != NULL); + + /* deep copy the whole map */ + keys = g_hash_table_get_keys(hash); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(hash, key); + g_hash_table_insert(priv->metadata, g_strdup(key), g_strdup(value)); + } +} + +/** + * fwupd_release_get_metadata_item: + * @self: a #FwupdRelease + * @key: (not nullable): the key + * + * Gets a release metadata item. + * + * Returns: the value, or %NULL if unset + * + * Since: 1.0.4 + **/ +const gchar * +fwupd_release_get_metadata_item(FwupdRelease *self, const gchar *key) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + g_return_val_if_fail(key != NULL, NULL); + return g_hash_table_lookup(priv->metadata, key); +} + +/** + * fwupd_release_get_uri: + * @self: a #FwupdRelease + * + * Gets the default update URI. + * + * Returns: the update URI, or %NULL if unset + * + * Since: 0.9.3 + * Deprecated: 1.5.6: Use fwupd_release_get_locations() instead. + **/ +const gchar * +fwupd_release_get_uri(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + if (priv->locations->len == 0) + return NULL; + return (const gchar *)g_ptr_array_index(priv->locations, 0); +} + +/** + * fwupd_release_set_uri: + * @self: a #FwupdRelease + * @uri: the update URI + * + * Sets the update URI, i.e. where you can download the firmware from. + * + * Since: 0.9.3 + * Deprecated: 1.5.6: Use fwupd_release_add_location() instead. + **/ +void +fwupd_release_set_uri(FwupdRelease *self, const gchar *uri) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_ptr_array_set_size(priv->locations, 0); + g_ptr_array_add(priv->locations, g_strdup(uri)); +} + +/** + * fwupd_release_get_locations: + * @self: a #FwupdRelease + * + * Gets the update URI, i.e. where you can download the firmware from. + * + * Typically the first URI will be the main HTTP mirror, but all URIs may not + * be valid HTTP URIs. For example, "ipns://QmSrPmba" is valid here. + * + * Returns: (element-type utf8) (transfer none): the URIs + * + * Since: 1.5.6 + **/ +GPtrArray * +fwupd_release_get_locations(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->locations; +} + +/** + * fwupd_release_add_location: + * @self: a #FwupdRelease + * @location: (not nullable): the update URI + * + * Adds an update URI, i.e. where you can download the firmware from. + * + * Since: 1.5.6 + **/ +void +fwupd_release_add_location(FwupdRelease *self, const gchar *location) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(location != NULL); + for (guint i = 0; i < priv->locations->len; i++) { + const gchar *location_tmp = g_ptr_array_index(priv->locations, i); + if (g_strcmp0(location_tmp, location) == 0) + return; + } + g_ptr_array_add(priv->locations, g_strdup(location)); +} + +/** + * fwupd_release_get_homepage: + * @self: a #FwupdRelease + * + * Gets the update homepage. + * + * Returns: the update homepage, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_homepage(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->homepage; +} + +/** + * fwupd_release_set_homepage: + * @self: a #FwupdRelease + * @homepage: (nullable): the URL + * + * Sets the update homepage URL. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_homepage(FwupdRelease *self, const gchar *homepage) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->homepage, homepage) == 0) + return; + + g_free(priv->homepage); + priv->homepage = g_strdup(homepage); +} + +/** + * fwupd_release_get_details_url: + * @self: 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 *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->details_url; +} + +/** + * fwupd_release_set_details_url: + * @self: a #FwupdRelease + * @details_url: (nullable): the URL + * + * Sets the URL for the online update notes. + * + * Since: 1.2.4 + **/ +void +fwupd_release_set_details_url(FwupdRelease *self, const gchar *details_url) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->details_url, details_url) == 0) + return; + + g_free(priv->details_url); + priv->details_url = g_strdup(details_url); +} + +/** + * fwupd_release_get_source_url: + * @self: 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 *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->source_url; +} + +/** + * fwupd_release_set_source_url: + * @self: a #FwupdRelease + * @source_url: (nullable): 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 *self, const gchar *source_url) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->source_url, source_url) == 0) + return; + + g_free(priv->source_url); + priv->source_url = g_strdup(source_url); +} + +/** + * fwupd_release_get_description: + * @self: a #FwupdRelease + * + * Gets the update description in AppStream markup format. + * + * Returns: the update description, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_description(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->description; +} + +/** + * fwupd_release_set_description: + * @self: a #FwupdRelease + * @description: (nullable): the update description in AppStream markup format + * + * Sets the update description. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_description(FwupdRelease *self, const gchar *description) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->description, description) == 0) + return; + + g_free(priv->description); + priv->description = g_strdup(description); +} + +/** + * fwupd_release_get_appstream_id: + * @self: a #FwupdRelease + * + * Gets the AppStream ID. + * + * Returns: the AppStream ID, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_appstream_id(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->appstream_id; +} + +/** + * fwupd_release_set_appstream_id: + * @self: a #FwupdRelease + * @appstream_id: (nullable): the AppStream component ID, e.g. `org.hughski.ColorHug2.firmware` + * + * Sets the AppStream ID. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_appstream_id(FwupdRelease *self, const gchar *appstream_id) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->appstream_id, appstream_id) == 0) + return; + + g_free(priv->appstream_id); + priv->appstream_id = g_strdup(appstream_id); +} + +/** + * fwupd_release_get_id: + * @self: a #FwupdRelease + * + * Gets the release ID, which allows identifying the specific uploaded component. + * + * Returns: the ID, or %NULL if unset + * + * Since: 1.7.2 + **/ +const gchar * +fwupd_release_get_id(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->id; +} + +/** + * fwupd_release_set_id: + * @self: a #FwupdRelease + * @id: (nullable): the AppStream component ID, e.g. `component:1234` + * + * Sets the ID, which allows identifying the specific uploaded component. + * + * Since: 1.7.2 + **/ +void +fwupd_release_set_id(FwupdRelease *self, const gchar *id) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + g_free(priv->id); + priv->id = g_strdup(id); +} + +/** + * fwupd_release_get_detach_caption: + * @self: a #FwupdRelease + * + * Gets the optional text caption used to manually detach the device. + * + * Returns: the string caption, or %NULL if unset + * + * Since: 1.3.3 + **/ +const gchar * +fwupd_release_get_detach_caption(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->detach_caption; +} + +/** + * fwupd_release_set_detach_caption: + * @self: a #FwupdRelease + * @detach_caption: (nullable): string caption + * + * Sets the optional text caption used to manually detach the device. + * + * Since: 1.3.3 + **/ +void +fwupd_release_set_detach_caption(FwupdRelease *self, const gchar *detach_caption) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->detach_caption, detach_caption) == 0) + return; + + g_free(priv->detach_caption); + priv->detach_caption = g_strdup(detach_caption); +} + +/** + * fwupd_release_get_detach_image: + * @self: a #FwupdRelease + * + * Gets the optional image used to manually detach the device. + * + * Returns: the URI, or %NULL if unset + * + * Since: 1.3.3 + **/ +const gchar * +fwupd_release_get_detach_image(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->detach_image; +} + +/** + * fwupd_release_set_detach_image: + * @self: a #FwupdRelease + * @detach_image: (nullable): a fully qualified URI + * + * Sets the optional image used to manually detach the device. + * + * Since: 1.3.3 + **/ +void +fwupd_release_set_detach_image(FwupdRelease *self, const gchar *detach_image) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->detach_image, detach_image) == 0) + return; + + g_free(priv->detach_image); + priv->detach_image = g_strdup(detach_image); +} + +/** + * fwupd_release_get_size: + * @self: a #FwupdRelease + * + * Gets the update size. + * + * Returns: the update size in bytes, or 0 if unset + * + * Since: 0.9.3 + **/ +guint64 +fwupd_release_get_size(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 0); + return priv->size; +} + +/** + * fwupd_release_set_size: + * @self: a #FwupdRelease + * @size: the update size in bytes + * + * Sets the update size. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_size(FwupdRelease *self, guint64 size) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->size = size; +} + +/** + * fwupd_release_get_created: + * @self: a #FwupdRelease + * + * Gets when the update was created. + * + * Returns: UTC timestamp in UNIX format, or 0 if unset + * + * Since: 1.4.0 + **/ +guint64 +fwupd_release_get_created(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 0); + return priv->created; +} + +/** + * fwupd_release_set_created: + * @self: a #FwupdRelease + * @created: UTC timestamp in UNIX format + * + * Sets when the update was created. + * + * Since: 1.4.0 + **/ +void +fwupd_release_set_created(FwupdRelease *self, guint64 created) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->created = created; +} + +/** + * fwupd_release_get_summary: + * @self: a #FwupdRelease + * + * Gets the update summary. + * + * Returns: the update summary, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_summary(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->summary; +} + +/** + * fwupd_release_set_summary: + * @self: a #FwupdRelease + * @summary: (nullable): the update one line summary + * + * Sets the update summary. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_summary(FwupdRelease *self, const gchar *summary) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->summary, summary) == 0) + return; + + g_free(priv->summary); + priv->summary = g_strdup(summary); +} + +/** + * fwupd_release_get_branch: + * @self: a #FwupdRelease + * + * Gets the update branch. + * + * Returns: the alternate branch, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_release_get_branch(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->branch; +} + +/** + * fwupd_release_set_branch: + * @self: a #FwupdRelease + * @branch: (nullable): the update one line branch + * + * Sets the alternate branch. + * + * Since: 1.5.0 + **/ +void +fwupd_release_set_branch(FwupdRelease *self, const gchar *branch) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->branch, branch) == 0) + return; + + g_free(priv->branch); + priv->branch = g_strdup(branch); +} + +/** + * fwupd_release_get_vendor: + * @self: a #FwupdRelease + * + * Gets the update vendor. + * + * Returns: the update vendor, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_vendor(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->vendor; +} + +/** + * fwupd_release_set_vendor: + * @self: a #FwupdRelease + * @vendor: (nullable): the vendor name, e.g. `Hughski Limited` + * + * Sets the update vendor. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_vendor(FwupdRelease *self, const gchar *vendor) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->vendor, vendor) == 0) + return; + + g_free(priv->vendor); + priv->vendor = g_strdup(vendor); +} + +/** + * fwupd_release_get_license: + * @self: a #FwupdRelease + * + * Gets the update license. + * + * Returns: the update license, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_license(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->license; +} + +/** + * fwupd_release_set_license: + * @self: a #FwupdRelease + * @license: (nullable): the update license. + * + * Sets the update license. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_license(FwupdRelease *self, const gchar *license) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->license, license) == 0) + return; + + g_free(priv->license); + priv->license = g_strdup(license); +} + +/** + * fwupd_release_get_name: + * @self: a #FwupdRelease + * + * Gets the update name. + * + * Returns: the update name, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_release_get_name(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->name; +} + +/** + * fwupd_release_set_name: + * @self: a #FwupdRelease + * @name: (nullable): the update name. + * + * Sets the update name. + * + * Since: 0.9.3 + **/ +void +fwupd_release_set_name(FwupdRelease *self, const gchar *name) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->name, name) == 0) + return; + + g_free(priv->name); + priv->name = g_strdup(name); +} + +/** + * fwupd_release_get_name_variant_suffix: + * @self: a #FwupdRelease + * + * Gets the update variant suffix. + * + * Returns: the update variant, or %NULL if unset + * + * Since: 1.3.2 + **/ +const gchar * +fwupd_release_get_name_variant_suffix(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + return priv->name_variant_suffix; +} + +/** + * fwupd_release_set_name_variant_suffix: + * @self: a #FwupdRelease + * @name_variant_suffix: (nullable): the description + * + * Sets the update variant suffix. + * + * Since: 1.3.2 + **/ +void +fwupd_release_set_name_variant_suffix(FwupdRelease *self, const gchar *name_variant_suffix) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(priv->name_variant_suffix, name_variant_suffix) == 0) + return; + + g_free(priv->name_variant_suffix); + priv->name_variant_suffix = g_strdup(name_variant_suffix); +} + +/** + * fwupd_release_get_trust_flags: + * @self: a #FwupdRelease + * + * Gets the trust level of the release. + * + * Returns: the trust bitfield, e.g. #FWUPD_TRUST_FLAG_PAYLOAD + * + * Since: 0.9.8 + **/ +FwupdTrustFlags +fwupd_release_get_trust_flags(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 0); + return priv->flags & FWUPD_RELEASE_TRUST_FLAGS_MASK; +} + +/** + * fwupd_release_set_trust_flags: + * @self: a #FwupdRelease + * @trust_flags: the bitfield, e.g. #FWUPD_TRUST_FLAG_PAYLOAD + * + * Sets the trust level of the release. + * + * Since: 0.9.8 + **/ +void +fwupd_release_set_trust_flags(FwupdRelease *self, FwupdTrustFlags trust_flags) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + + /* only overwrite the last two bits of the flags */ + priv->flags &= ~FWUPD_RELEASE_TRUST_FLAGS_MASK; + priv->flags |= trust_flags; +} + +/** + * fwupd_release_get_flags: + * @self: a #FwupdRelease + * + * Gets the release flags. + * + * Returns: release flags, or 0 if unset + * + * Since: 1.2.6 + **/ +FwupdReleaseFlags +fwupd_release_get_flags(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 0); + return priv->flags; +} + +/** + * fwupd_release_set_flags: + * @self: a #FwupdRelease + * @flags: release flags, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD + * + * Sets the release flags. + * + * Since: 1.2.6 + **/ +void +fwupd_release_set_flags(FwupdRelease *self, FwupdReleaseFlags flags) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->flags = flags; +} + +/** + * fwupd_release_add_flag: + * @self: a #FwupdRelease + * @flag: the #FwupdReleaseFlags + * + * Adds a specific release flag to the release. + * + * Since: 1.2.6 + **/ +void +fwupd_release_add_flag(FwupdRelease *self, FwupdReleaseFlags flag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->flags |= flag; +} + +/** + * fwupd_release_remove_flag: + * @self: a #FwupdRelease + * @flag: the #FwupdReleaseFlags + * + * Removes a specific release flag from the release. + * + * Since: 1.2.6 + **/ +void +fwupd_release_remove_flag(FwupdRelease *self, FwupdReleaseFlags flag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->flags &= ~flag; +} + +/** + * fwupd_release_has_flag: + * @self: 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 *self, FwupdReleaseFlags flag) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_release_get_urgency: + * @self: a #FwupdRelease + * + * Gets the release urgency. + * + * Returns: the release urgency, or 0 if unset + * + * Since: 1.4.0 + **/ +FwupdReleaseUrgency +fwupd_release_get_urgency(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 0); + return priv->urgency; +} + +/** + * fwupd_release_set_urgency: + * @self: a #FwupdRelease + * @urgency: the release urgency, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD + * + * Sets the release urgency. + * + * Since: 1.4.0 + **/ +void +fwupd_release_set_urgency(FwupdRelease *self, FwupdReleaseUrgency urgency) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->urgency = urgency; +} + +/** + * fwupd_release_get_install_duration: + * @self: 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 *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_RELEASE(self), 0); + return priv->install_duration; +} + +/** + * fwupd_release_set_install_duration: + * @self: a #FwupdRelease + * @duration: amount of time in seconds + * + * Sets the time estimate for firmware installation (in seconds) + * + * Since: 1.2.1 + **/ +void +fwupd_release_set_install_duration(FwupdRelease *self, guint32 duration) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_RELEASE(self)); + priv->install_duration = duration; +} + +/** + * fwupd_release_to_variant: + * @self: a #FwupdRelease + * + * Serialize the release data. + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.0.0 + **/ +GVariant * +fwupd_release_to_variant(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + + /* create an array with all the metadata in */ + 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, + g_variant_new_string(priv->remote_id)); + } + if (priv->appstream_id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_APPSTREAM_ID, + g_variant_new_string(priv->appstream_id)); + } + if (priv->id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_RELEASE_ID, + g_variant_new_string(priv->id)); + } + if (priv->detach_caption != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DETACH_CAPTION, + g_variant_new_string(priv->detach_caption)); + } + if (priv->detach_image != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DETACH_IMAGE, + g_variant_new_string(priv->detach_image)); + } + 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_image != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_UPDATE_IMAGE, + g_variant_new_string(priv->update_image)); + } + if (priv->filename != NULL) { + g_variant_builder_add(&builder, + "{sv}", + 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, + g_variant_new_string(priv->license)); + } + if (priv->name != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string(priv->name)); + } + if (priv->name_variant_suffix != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX, + g_variant_new_string(priv->name_variant_suffix)); + } + if (priv->size != 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_SIZE, + g_variant_new_uint64(priv->size)); + } + if (priv->created != 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CREATED, + g_variant_new_uint64(priv->created)); + } + if (priv->summary != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_SUMMARY, + g_variant_new_string(priv->summary)); + } + if (priv->branch != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BRANCH, + g_variant_new_string(priv->branch)); + } + if (priv->description != NULL) { + g_variant_builder_add(&builder, + "{sv}", + 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->issues->len > 0) { + g_autofree const gchar **strv = g_new0(const gchar *, priv->issues->len + 1); + for (guint i = 0; i < priv->issues->len; i++) + strv[i] = (const gchar *)g_ptr_array_index(priv->issues, i); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_ISSUES, + g_variant_new_strv(strv, -1)); + } + if (priv->checksums->len > 0) { + g_autoptr(GString) str = g_string_new(""); + for (guint i = 0; i < priv->checksums->len; i++) { + const gchar *checksum = g_ptr_array_index(priv->checksums, i); + g_string_append_printf(str, "%s,", checksum); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CHECKSUM, + g_variant_new_string(str->str)); + } + if (priv->locations->len > 0) { + g_variant_builder_add( + &builder, + "{sv}", + FWUPD_RESULT_KEY_LOCATIONS, + g_variant_new_strv((const gchar *const *)priv->locations->pdata, + priv->locations->len)); + /* for compatibility */ + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_URI, + g_variant_new_string(g_ptr_array_index(priv->locations, 0))); + } + if (priv->tags->len > 0) { + g_variant_builder_add( + &builder, + "{sv}", + FWUPD_RESULT_KEY_TAGS, + g_variant_new_strv((const gchar *const *)priv->tags->pdata, priv->tags->len)); + } + if (priv->homepage != NULL) { + g_variant_builder_add(&builder, + "{sv}", + 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, + g_variant_new_string(priv->version)); + } + if (priv->vendor != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_VENDOR, + g_variant_new_string(priv->vendor)); + } + if (priv->flags != 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_TRUST_FLAGS, + g_variant_new_uint64(priv->flags)); + } + if (priv->urgency != 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_URGENCY, + g_variant_new_uint32(priv->urgency)); + } + if (g_hash_table_size(priv->metadata) > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_METADATA, + fwupd_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); +} + +static void +fwupd_release_from_key_value(FwupdRelease *self, const gchar *key, GVariant *value) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + if (g_strcmp0(key, FWUPD_RESULT_KEY_REMOTE_ID) == 0) { + fwupd_release_set_remote_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_APPSTREAM_ID) == 0) { + fwupd_release_set_appstream_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_RELEASE_ID) == 0) { + fwupd_release_set_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DETACH_CAPTION) == 0) { + fwupd_release_set_detach_caption(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DETACH_IMAGE) == 0) { + fwupd_release_set_detach_image(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FILENAME) == 0) { + fwupd_release_set_filename(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_PROTOCOL) == 0) { + fwupd_release_set_protocol(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_LICENSE) == 0) { + fwupd_release_set_license(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_release_set_name(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX) == 0) { + fwupd_release_set_name_variant_suffix(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_SIZE) == 0) { + fwupd_release_set_size(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_CREATED) == 0) { + fwupd_release_set_created(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_SUMMARY) == 0) { + fwupd_release_set_summary(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BRANCH) == 0) { + fwupd_release_set_branch(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { + fwupd_release_set_description(self, 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(self, strv[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_ISSUES) == 0) { + g_autofree const gchar **strv = g_variant_get_strv(value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_release_add_issue(self, 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); + for (guint i = 0; split[i] != NULL; i++) + fwupd_release_add_checksum(self, split[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_LOCATIONS) == 0) { + g_autofree const gchar **strv = g_variant_get_strv(value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_release_add_location(self, strv[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_TAGS) == 0) { + g_autofree const gchar **strv = g_variant_get_strv(value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_release_add_tag(self, strv[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_URI) == 0) { + fwupd_release_add_location(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_HOMEPAGE) == 0) { + fwupd_release_set_homepage(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DETAILS_URL) == 0) { + fwupd_release_set_details_url(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_SOURCE_URL) == 0) { + fwupd_release_set_source_url(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION) == 0) { + fwupd_release_set_version(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_VENDOR) == 0) { + fwupd_release_set_vendor(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_TRUST_FLAGS) == 0) { + fwupd_release_set_flags(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_URGENCY) == 0) { + fwupd_release_set_urgency(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_INSTALL_DURATION) == 0) { + fwupd_release_set_install_duration(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_MESSAGE) == 0) { + fwupd_release_set_update_message(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_IMAGE) == 0) { + fwupd_release_set_update_image(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_METADATA) == 0) { + g_hash_table_unref(priv->metadata); + priv->metadata = fwupd_variant_to_hash_kv(value); + return; + } +} + +static void +fwupd_pad_kv_siz(GString *str, const gchar *key, guint64 value) +{ + g_autofree gchar *tmp = NULL; + + /* ignore */ + if (value == 0) + return; + tmp = g_format_size(value); + fwupd_pad_kv_str(str, key, tmp); +} + +static void +fwupd_pad_kv_tfl(GString *str, const gchar *key, FwupdReleaseFlags release_flags) +{ + g_autoptr(GString) tmp = g_string_new(""); + for (guint i = 0; i < 64; i++) { + if ((release_flags & ((guint64)1 << i)) == 0) + continue; + g_string_append_printf(tmp, "%s|", fwupd_release_flag_to_string((guint64)1 << i)); + } + if (tmp->len == 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); +} + +/** + * fwupd_release_to_json: + * @self: a #FwupdRelease + * @builder: a JSON builder + * + * Adds a fwupd release to a JSON builder + * + * Since: 1.2.6 + **/ +void +fwupd_release_to_json(FwupdRelease *self, JsonBuilder *builder) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + g_autoptr(GList) keys = NULL; + + g_return_if_fail(FWUPD_IS_RELEASE(self)); + g_return_if_fail(builder != NULL); + + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_RELEASE_ID, priv->id); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_REMOTE_ID, priv->remote_id); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX, + priv->name_variant_suffix); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_SUMMARY, priv->summary); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_BRANCH, priv->branch); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_VERSION, priv->version); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_FILENAME, priv->filename); + fwupd_common_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->issues->len > 0) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_ISSUES); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->issues, i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } + if (priv->checksums->len > 0) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_CHECKSUM); + json_builder_begin_array(builder); + 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); + } + if (priv->tags->len > 0) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_TAGS); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->tags->len; i++) { + const gchar *tag = g_ptr_array_index(priv->tags, i); + json_builder_add_string_value(builder, tag); + } + json_builder_end_array(builder); + } + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_LICENSE, priv->license); + if (priv->size > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_SIZE, priv->size); + if (priv->created > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_CREATED, priv->created); + if (priv->locations->len > 0) { + json_builder_set_member_name(builder, FWUPD_RESULT_KEY_LOCATIONS); + json_builder_begin_array(builder); + for (guint i = 0; i < priv->locations->len; i++) { + const gchar *location = g_ptr_array_index(priv->locations, i); + json_builder_add_string_value(builder, location); + } + json_builder_end_array(builder); + /* for compatibility */ + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_URI, + (const gchar *)g_ptr_array_index(priv->locations, 0)); + } + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_HOMEPAGE, priv->homepage); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DETAILS_URL, priv->details_url); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_SOURCE_URL, priv->source_url); + fwupd_common_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); + } + if (priv->install_duration > 0) { + fwupd_common_json_add_int(builder, + FWUPD_RESULT_KEY_INSTALL_DURATION, + priv->install_duration); + } + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_DETACH_CAPTION, + priv->detach_caption); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DETACH_IMAGE, priv->detach_image); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_UPDATE_MESSAGE, + priv->update_message); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->update_image); + + /* 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_common_json_add_string(builder, key, value); + } +} + +/** + * fwupd_release_to_string: + * @self: a #FwupdRelease + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 0.9.3 + **/ +gchar * +fwupd_release_to_string(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + GString *str; + g_autoptr(GList) keys = NULL; + + g_return_val_if_fail(FWUPD_IS_RELEASE(self), NULL); + + str = g_string_new(""); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_RELEASE_ID, priv->id); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_REMOTE_ID, priv->remote_id); + 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_BRANCH, priv->branch); + 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->issues->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->issues, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_ISSUES, tmp); + } + for (guint i = 0; i < priv->checksums->len; i++) { + const gchar *checksum = g_ptr_array_index(priv->checksums, i); + g_autofree gchar *checksum_display = fwupd_checksum_format_for_display(checksum); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_CHECKSUM, checksum_display); + } + for (guint i = 0; i < priv->tags->len; i++) { + const gchar *tag = g_ptr_array_index(priv->tags, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_TAGS, tag); + } + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_LICENSE, priv->license); + fwupd_pad_kv_siz(str, FWUPD_RESULT_KEY_SIZE, priv->size); + fwupd_pad_kv_unx(str, FWUPD_RESULT_KEY_CREATED, priv->created); + for (guint i = 0; i < priv->locations->len; i++) { + const gchar *location = g_ptr_array_index(priv->locations, i); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_URI, location); + } + 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); + if (priv->urgency != FWUPD_RELEASE_URGENCY_UNKNOWN) { + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_URGENCY, + fwupd_release_urgency_to_string(priv->urgency)); + } + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_VENDOR, priv->vendor); + fwupd_pad_kv_tfl(str, FWUPD_RESULT_KEY_FLAGS, priv->flags); + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_INSTALL_DURATION, priv->install_duration); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DETACH_CAPTION, priv->detach_caption); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DETACH_IMAGE, priv->detach_image); + if (priv->update_message != NULL) + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->update_message); + if (priv->update_image != NULL) + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->update_image); + /* 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_pad_kv_str(str, key, value); + } + + return g_string_free(str, FALSE); +} + +static void +fwupd_release_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FwupdRelease *self = FWUPD_RELEASE(obj); + FwupdReleasePrivate *priv = GET_PRIVATE(self); + + switch (prop_id) { + case PROP_REMOTE_ID: + g_value_set_string(value, priv->remote_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +fwupd_release_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FwupdRelease *self = FWUPD_RELEASE(obj); + + switch (prop_id) { + case PROP_REMOTE_ID: + fwupd_release_set_remote_id(self, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +fwupd_release_class_init(FwupdReleaseClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fwupd_release_finalize; + object_class->get_property = fwupd_release_get_property; + object_class->set_property = fwupd_release_set_property; + + /** + * FwupdRelease:remote-id: + * + * The remote ID. + * + * Since: 1.8.0 + */ + pspec = g_param_spec_string("remote-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_REMOTE_ID, pspec); +} + +static void +fwupd_release_init(FwupdRelease *self) +{ + FwupdReleasePrivate *priv = GET_PRIVATE(self); + priv->categories = g_ptr_array_new_with_free_func(g_free); + priv->issues = g_ptr_array_new_with_free_func(g_free); + priv->checksums = g_ptr_array_new_with_free_func(g_free); + priv->tags = g_ptr_array_new_with_free_func(g_free); + priv->locations = 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); +} + +static void +fwupd_release_finalize(GObject *object) +{ + FwupdRelease *self = FWUPD_RELEASE(object); + FwupdReleasePrivate *priv = GET_PRIVATE(self); + + g_free(priv->description); + g_free(priv->filename); + g_free(priv->protocol); + g_free(priv->appstream_id); + g_free(priv->id); + g_free(priv->detach_caption); + g_free(priv->detach_image); + g_free(priv->license); + g_free(priv->name); + g_free(priv->name_variant_suffix); + g_free(priv->summary); + g_free(priv->branch); + g_ptr_array_unref(priv->locations); + 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_free(priv->update_image); + g_ptr_array_unref(priv->categories); + g_ptr_array_unref(priv->issues); + g_ptr_array_unref(priv->checksums); + g_ptr_array_unref(priv->tags); + g_hash_table_unref(priv->metadata); + + G_OBJECT_CLASS(fwupd_release_parent_class)->finalize(object); +} + +static void +fwupd_release_set_from_variant_iter(FwupdRelease *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + fwupd_release_from_key_value(self, key, value); + g_variant_unref(value); + } +} + +/** + * fwupd_release_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new release using serialized data. + * + * Returns: (transfer full): a new #FwupdRelease, or %NULL if @value was invalid + * + * Since: 1.0.0 + **/ +FwupdRelease * +fwupd_release_from_variant(GVariant *value) +{ + FwupdRelease *self = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + /* format from GetDetails */ + type_string = g_variant_get_type_string(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + self = fwupd_release_new(); + g_variant_get(value, "(a{sv})", &iter); + fwupd_release_set_from_variant_iter(self, iter); + } else if (g_strcmp0(type_string, "a{sv}") == 0) { + self = fwupd_release_new(); + g_variant_get(value, "a{sv}", &iter); + fwupd_release_set_from_variant_iter(self, iter); + } else { + g_warning("type %s not known", type_string); + } + return self; +} + +/** + * fwupd_release_array_from_variant: + * @value: (not nullable): the serialized data + * + * Creates an array of new releases using serialized 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; + + g_return_val_if_fail(value != NULL, 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 *self; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value(untuple, i); + self = fwupd_release_from_variant(data); + if (self == NULL) + continue; + g_ptr_array_add(array, self); + } + return array; +} + +/** + * fwupd_release_new: + * + * Creates a new release. + * + * Returns: a new #FwupdRelease + * + * Since: 0.9.3 + **/ +FwupdRelease * +fwupd_release_new(void) +{ + FwupdRelease *self; + self = g_object_new(FWUPD_TYPE_RELEASE, NULL); + return FWUPD_RELEASE(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-release.h b/fwupd-1.8.6/libfwupd/fwupd-release.h new file mode 100644 index 0000000000000000000000000000000000000000..df60e8739edbfa8c3920783f18dde88e28404bc8 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-release.h @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_RELEASE (fwupd_release_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdRelease, fwupd_release, FWUPD, RELEASE, GObject) + +struct _FwupdReleaseClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +FwupdRelease * +fwupd_release_new(void); +gchar * +fwupd_release_to_string(FwupdRelease *self); + +const gchar * +fwupd_release_get_version(FwupdRelease *self); +void +fwupd_release_set_version(FwupdRelease *self, const gchar *version); +G_DEPRECATED_FOR(fwupd_release_get_locations) +const gchar * +fwupd_release_get_uri(FwupdRelease *self); +G_DEPRECATED_FOR(fwupd_release_add_location) +void +fwupd_release_set_uri(FwupdRelease *self, const gchar *uri); +GPtrArray * +fwupd_release_get_locations(FwupdRelease *self); +void +fwupd_release_add_location(FwupdRelease *self, const gchar *location); +GPtrArray * +fwupd_release_get_issues(FwupdRelease *self); +void +fwupd_release_add_issue(FwupdRelease *self, const gchar *issue); +GPtrArray * +fwupd_release_get_categories(FwupdRelease *self); +void +fwupd_release_add_category(FwupdRelease *self, const gchar *category); +gboolean +fwupd_release_has_category(FwupdRelease *self, const gchar *category); +GPtrArray * +fwupd_release_get_checksums(FwupdRelease *self); +void +fwupd_release_add_checksum(FwupdRelease *self, const gchar *checksum); +gboolean +fwupd_release_has_checksum(FwupdRelease *self, const gchar *checksum); +GPtrArray * +fwupd_release_get_tags(FwupdRelease *self); +void +fwupd_release_add_tag(FwupdRelease *self, const gchar *tag); +gboolean +fwupd_release_has_tag(FwupdRelease *self, const gchar *tag); + +GHashTable * +fwupd_release_get_metadata(FwupdRelease *self); +void +fwupd_release_add_metadata(FwupdRelease *self, GHashTable *hash); +void +fwupd_release_add_metadata_item(FwupdRelease *self, const gchar *key, const gchar *value); +const gchar * +fwupd_release_get_metadata_item(FwupdRelease *self, const gchar *key); + +const gchar * +fwupd_release_get_filename(FwupdRelease *self); +void +fwupd_release_set_filename(FwupdRelease *self, const gchar *filename); +const gchar * +fwupd_release_get_protocol(FwupdRelease *self); +void +fwupd_release_set_protocol(FwupdRelease *self, const gchar *protocol); +const gchar * +fwupd_release_get_id(FwupdRelease *self); +void +fwupd_release_set_id(FwupdRelease *self, const gchar *id); +const gchar * +fwupd_release_get_appstream_id(FwupdRelease *self); +void +fwupd_release_set_appstream_id(FwupdRelease *self, const gchar *appstream_id); +const gchar * +fwupd_release_get_detach_caption(FwupdRelease *self); +void +fwupd_release_set_detach_caption(FwupdRelease *self, const gchar *detach_caption); +const gchar * +fwupd_release_get_detach_image(FwupdRelease *self); +void +fwupd_release_set_detach_image(FwupdRelease *self, const gchar *detach_image); +const gchar * +fwupd_release_get_remote_id(FwupdRelease *self); +void +fwupd_release_set_remote_id(FwupdRelease *self, const gchar *remote_id); +const gchar * +fwupd_release_get_vendor(FwupdRelease *self); +void +fwupd_release_set_vendor(FwupdRelease *self, const gchar *vendor); +const gchar * +fwupd_release_get_name(FwupdRelease *self); +void +fwupd_release_set_name(FwupdRelease *self, const gchar *name); +const gchar * +fwupd_release_get_name_variant_suffix(FwupdRelease *self); +void +fwupd_release_set_name_variant_suffix(FwupdRelease *self, const gchar *name_variant_suffix); +const gchar * +fwupd_release_get_summary(FwupdRelease *self); +void +fwupd_release_set_summary(FwupdRelease *self, const gchar *summary); +const gchar * +fwupd_release_get_branch(FwupdRelease *self); +void +fwupd_release_set_branch(FwupdRelease *self, const gchar *branch); +const gchar * +fwupd_release_get_description(FwupdRelease *self); +void +fwupd_release_set_description(FwupdRelease *self, const gchar *description); +const gchar * +fwupd_release_get_homepage(FwupdRelease *self); +void +fwupd_release_set_homepage(FwupdRelease *self, const gchar *homepage); +const gchar * +fwupd_release_get_details_url(FwupdRelease *self); +void +fwupd_release_set_details_url(FwupdRelease *self, const gchar *details_url); +const gchar * +fwupd_release_get_source_url(FwupdRelease *self); +void +fwupd_release_set_source_url(FwupdRelease *self, const gchar *source_url); +guint64 +fwupd_release_get_size(FwupdRelease *self); +void +fwupd_release_set_size(FwupdRelease *self, guint64 size); +guint64 +fwupd_release_get_created(FwupdRelease *self); +void +fwupd_release_set_created(FwupdRelease *self, guint64 created); +const gchar * +fwupd_release_get_license(FwupdRelease *self); +void +fwupd_release_set_license(FwupdRelease *self, const gchar *license); +FwupdTrustFlags +fwupd_release_get_trust_flags(FwupdRelease *self) G_DEPRECATED_FOR(fwupd_release_get_flags); +void +fwupd_release_set_trust_flags(FwupdRelease *self, FwupdTrustFlags trust_flags) + G_DEPRECATED_FOR(fwupd_release_set_flags); +FwupdReleaseFlags +fwupd_release_get_flags(FwupdRelease *self); +void +fwupd_release_set_flags(FwupdRelease *self, FwupdReleaseFlags flags); +void +fwupd_release_add_flag(FwupdRelease *self, FwupdReleaseFlags flag); +void +fwupd_release_remove_flag(FwupdRelease *self, FwupdReleaseFlags flag); +gboolean +fwupd_release_has_flag(FwupdRelease *self, FwupdReleaseFlags flag); +FwupdReleaseUrgency +fwupd_release_get_urgency(FwupdRelease *self); +void +fwupd_release_set_urgency(FwupdRelease *self, FwupdReleaseUrgency urgency); +guint32 +fwupd_release_get_install_duration(FwupdRelease *self); +void +fwupd_release_set_install_duration(FwupdRelease *self, guint32 duration); +const gchar * +fwupd_release_get_update_message(FwupdRelease *self); +void +fwupd_release_set_update_message(FwupdRelease *self, const gchar *update_message); +const gchar * +fwupd_release_get_update_image(FwupdRelease *self); +void +fwupd_release_set_update_image(FwupdRelease *self, const gchar *update_image); + +FwupdRelease * +fwupd_release_from_variant(GVariant *value); +GPtrArray * +fwupd_release_array_from_variant(GVariant *value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-remote-private.h b/fwupd-1.8.6/libfwupd/fwupd-remote-private.h new file mode 100644 index 0000000000000000000000000000000000000000..07c1437c944c19e092a605d31ce49a7758fd350e --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-remote-private.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-remote.h" + +G_BEGIN_DECLS + +GVariant * +fwupd_remote_to_variant(FwupdRemote *self); +gboolean +fwupd_remote_load_from_filename(FwupdRemote *self, + const gchar *filename, + GCancellable *cancellable, + GError **error); +void +fwupd_remote_set_priority(FwupdRemote *self, gint priority); +void +fwupd_remote_set_agreement(FwupdRemote *self, const gchar *agreement); +void +fwupd_remote_set_checksum(FwupdRemote *self, const gchar *checksum); +void +fwupd_remote_set_filename_cache(FwupdRemote *self, const gchar *filename); +void +fwupd_remote_set_mtime(FwupdRemote *self, guint64 mtime); +gchar ** +fwupd_remote_get_order_after(FwupdRemote *self); +gchar ** +fwupd_remote_get_order_before(FwupdRemote *self); + +void +fwupd_remote_set_remotes_dir(FwupdRemote *self, const gchar *directory); +void +fwupd_remote_set_filename_source(FwupdRemote *self, const gchar *filename_source); +void +fwupd_remote_set_keyring_kind(FwupdRemote *self, FwupdKeyringKind keyring_kind); +gboolean +fwupd_remote_setup(FwupdRemote *self, GError **error); +void +fwupd_remote_to_json(FwupdRemote *self, JsonBuilder *builder); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-remote.c b/fwupd-1.8.6/libfwupd/fwupd-remote.c new file mode 100644 index 0000000000000000000000000000000000000000..72093735e887fc16321220af0edb97270594b2fa --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-remote.c @@ -0,0 +1,1816 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_LIBCURL +#include +#endif +#include + +#include "fwupd-common-private.h" +#include "fwupd-deprecated.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" +#include "fwupd-remote-private.h" + +/** + * FwupdRemote: + * + * A source of metadata that provides firmware. + * + * Remotes can be local (e.g. folders on a disk) or remote (e.g. downloaded + * over HTTP or IPFS). + * + * See also: [class@FwupdClient] + */ + +static void +fwupd_remote_finalize(GObject *obj); + +typedef struct { + FwupdRemoteKind kind; + FwupdKeyringKind keyring_kind; + gchar *id; + gchar *firmware_base_uri; + gchar *report_uri; + gchar *security_report_uri; + gchar *metadata_uri; + gchar *metadata_uri_sig; + gchar *username; + gchar *password; + gchar *title; + gchar *agreement; + gchar *checksum; + gchar *filename_cache; + gchar *filename_cache_sig; + gchar *filename_source; + gboolean enabled; + gboolean approval_required; + gint priority; + guint64 mtime; + gchar **order_after; + gchar **order_before; + gchar *remotes_dir; + gboolean automatic_reports; + gboolean automatic_security_reports; +} FwupdRemotePrivate; + +enum { + PROP_0, + PROP_ID, + PROP_ENABLED, + PROP_APPROVAL_REQUIRED, + PROP_AUTOMATIC_REPORTS, + PROP_AUTOMATIC_SECURITY_REPORTS, + PROP_LAST +}; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdRemote, fwupd_remote, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_remote_get_instance_private(o)) + +#ifdef HAVE_LIBCURL_7_62_0 +typedef gchar curlptr; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(curlptr, curl_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup) +#endif + +/** + * fwupd_remote_to_json: + * @self: a #FwupdRemote + * @builder: a JSON builder + * + * Adds a fwupd remote to a JSON builder + * + * Since: 1.6.2 + **/ +void +fwupd_remote_to_json(FwupdRemote *self, JsonBuilder *builder) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_REMOTE(self)); + g_return_if_fail(builder != NULL); + + fwupd_common_json_add_string(builder, "Id", priv->id); + if (priv->kind != FWUPD_REMOTE_KIND_UNKNOWN) { + fwupd_common_json_add_string(builder, + "Kind", + fwupd_remote_kind_to_string(priv->kind)); + } + if (priv->keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { + fwupd_common_json_add_string(builder, + "KeyringKind", + fwupd_keyring_kind_to_string(priv->keyring_kind)); + } + fwupd_common_json_add_string(builder, "FirmwareBaseUri", priv->firmware_base_uri); + fwupd_common_json_add_string(builder, "ReportUri", priv->report_uri); + fwupd_common_json_add_string(builder, "SecurityReportUri", priv->security_report_uri); + fwupd_common_json_add_string(builder, "MetadataUri", priv->metadata_uri); + fwupd_common_json_add_string(builder, "MetadataUriSig", priv->metadata_uri_sig); + fwupd_common_json_add_string(builder, "Username", priv->username); + fwupd_common_json_add_string(builder, "Password", priv->password); + fwupd_common_json_add_string(builder, "Title", priv->title); + fwupd_common_json_add_string(builder, "Agreement", priv->agreement); + fwupd_common_json_add_string(builder, "Checksum", priv->checksum); + fwupd_common_json_add_string(builder, "FilenameCache", priv->filename_cache); + fwupd_common_json_add_string(builder, "FilenameCacheSig", priv->filename_cache_sig); + fwupd_common_json_add_string(builder, "FilenameSource", priv->filename_source); + fwupd_common_json_add_boolean(builder, "Enabled", priv->enabled); + fwupd_common_json_add_boolean(builder, "ApprovalRequired", priv->approval_required); + fwupd_common_json_add_boolean(builder, "AutomaticReports", priv->automatic_reports); + fwupd_common_json_add_boolean(builder, + "AutomaticSecurityReports", + priv->automatic_security_reports); + fwupd_common_json_add_int(builder, "Priority", priv->priority); + fwupd_common_json_add_int(builder, "Mtime", priv->mtime); + fwupd_common_json_add_string(builder, "RemotesDir", priv->remotes_dir); + fwupd_common_json_add_stringv(builder, "OrderAfter", priv->order_after); + fwupd_common_json_add_stringv(builder, "OrderBefore", priv->order_before); +} + +static gchar * +fwupd_strdup_nonempty(const gchar *text) +{ + if (text == NULL || text[0] == '\0') + return NULL; + return g_strdup(text); +} + +static void +fwupd_remote_set_username(FwupdRemote *self, const gchar *username) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->username, username) == 0) + return; + + g_free(priv->username); + priv->username = g_strdup(username); +} + +static void +fwupd_remote_set_title(FwupdRemote *self, const gchar *title) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->title, title) == 0) + return; + + g_free(priv->title); + priv->title = g_strdup(title); +} + +/** + * fwupd_remote_set_agreement: + * @self: a #FwupdRemote + * @agreement: (nullable): agreement markup text + * + * Sets the remote agreement in AppStream markup format + * + * Since: 1.0.7 + **/ +void +fwupd_remote_set_agreement(FwupdRemote *self, const gchar *agreement) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->agreement, agreement) == 0) + return; + + g_free(priv->agreement); + priv->agreement = g_strdup(agreement); +} + +/** + * fwupd_remote_set_checksum: + * @self: a #FwupdRemote + * @checksum: (nullable): checksum string + * + * Sets the remote checksum, typically only useful in the self tests. + * + * Since: 1.8.2 + **/ +void +fwupd_remote_set_checksum(FwupdRemote *self, const gchar *checksum) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->checksum, checksum) == 0) + return; + + g_free(priv->checksum); + priv->checksum = g_strdup(checksum); +} + +static void +fwupd_remote_set_password(FwupdRemote *self, const gchar *password) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->password, password) == 0) + return; + + g_free(priv->password); + priv->password = g_strdup(password); +} + +static void +fwupd_remote_set_kind(FwupdRemote *self, FwupdRemoteKind kind) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + priv->kind = kind; +} + +/** + * fwupd_remote_set_keyring_kind: + * @self: a #FwupdRemote + * @keyring_kind: keyring kind e.g. #FWUPD_KEYRING_KIND_PKCS7 + * + * Sets the keyring kind + * + * Since: 1.5.3 + **/ +void +fwupd_remote_set_keyring_kind(FwupdRemote *self, FwupdKeyringKind keyring_kind) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + priv->keyring_kind = keyring_kind; +} + +/* note, this has to be set before url */ +static void +fwupd_remote_set_id(FwupdRemote *self, const gchar *id) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + g_free(priv->id); + priv->id = g_strdup(id); + g_strdelimit(priv->id, ".", '\0'); +} + +/** + * fwupd_remote_set_filename_source: + * @self: a #FwupdRemote + * @filename_source: (nullable): filename + * + * Sets the source filename. This is typically a file in `/etc/fwupd/remotes/`. + * + * Since: 1.6.1 + **/ +void +fwupd_remote_set_filename_source(FwupdRemote *self, const gchar *filename_source) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + if (priv->filename_source == filename_source) + return; + g_free(priv->filename_source); + priv->filename_source = g_strdup(filename_source); +} + +static const gchar * +fwupd_remote_get_suffix_for_keyring_kind(FwupdKeyringKind keyring_kind) +{ + if (keyring_kind == FWUPD_KEYRING_KIND_JCAT) + return ".jcat"; + if (keyring_kind == FWUPD_KEYRING_KIND_GPG) + return ".asc"; + if (keyring_kind == FWUPD_KEYRING_KIND_PKCS7) + return ".p7b"; + return NULL; +} + +static gchar * +fwupd_remote_build_uri(FwupdRemote *self, const gchar *url, GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); +#ifdef HAVE_LIBCURL_7_62_0 + g_autoptr(curlptr) tmp_uri = NULL; + g_autoptr(CURLU) uri = curl_url(); + + /* create URI, substituting if required */ + if (priv->firmware_base_uri != NULL) { + g_autofree gchar *basename = NULL; + g_autofree gchar *path_new = NULL; + g_autoptr(curlptr) path = NULL; + g_autoptr(CURLU) uri_tmp = curl_url(); + if (curl_url_set(uri_tmp, CURLUPART_URL, url, 0) != CURLUE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to parse url '%s'", + url); + return NULL; + } + (void)curl_url_get(uri_tmp, CURLUPART_PATH, &path, 0); + basename = g_path_get_basename(path); + path_new = g_build_filename(priv->firmware_base_uri, basename, NULL); + (void)curl_url_set(uri, CURLUPART_URL, path_new, 0); + + /* use the base URI of the metadata to build the full path */ + } else if (g_strstr_len(url, -1, "/") == NULL) { + g_autofree gchar *basename = NULL; + g_autofree gchar *path_new = NULL; + g_autoptr(curlptr) path = NULL; + if (curl_url_set(uri, CURLUPART_URL, priv->metadata_uri, 0) != CURLUE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to parse url '%s'", + priv->metadata_uri); + return NULL; + } + (void)curl_url_get(uri, CURLUPART_PATH, &path, 0); + basename = g_path_get_dirname(path); + path_new = g_build_filename(basename, url, NULL); + (void)curl_url_set(uri, CURLUPART_URL, path_new, 0); + + /* a normal URI */ + } else { + if (curl_url_set(uri, CURLUPART_URL, url, 0) != CURLUE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to parse URI '%s'", + url); + return NULL; + } + } + + /* set the username and password */ + if (priv->username != NULL) + (void)curl_url_set(uri, CURLUPART_USER, priv->username, 0); + if (priv->password != NULL) + (void)curl_url_set(uri, CURLUPART_PASSWORD, priv->password, 0); + (void)curl_url_get(uri, CURLUPART_URL, &tmp_uri, 0); + return g_strdup(tmp_uri); +#else + if (priv->firmware_base_uri != NULL) { + g_autofree gchar *basename = g_path_get_basename(url); + return g_build_filename(priv->firmware_base_uri, basename, NULL); + } + if (g_strstr_len(url, -1, "/") == NULL) { + g_autofree gchar *basename = g_path_get_dirname(priv->metadata_uri); + return g_build_filename(basename, url, NULL); + } + return g_strdup(url); +#endif +} + +/* note, this has to be set before username and password */ +static void +fwupd_remote_set_metadata_uri(FwupdRemote *self, const gchar *metadata_uri) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + const gchar *suffix; + + /* save this so we can export the object as a GVariant */ + priv->metadata_uri = g_strdup(metadata_uri); + + /* generate the signature URI too */ + suffix = fwupd_remote_get_suffix_for_keyring_kind(priv->keyring_kind); + if (suffix != NULL) + priv->metadata_uri_sig = g_strconcat(metadata_uri, suffix, NULL); +} + +/* note, this has to be set after MetadataURI */ +static void +fwupd_remote_set_firmware_base_uri(FwupdRemote *self, const gchar *firmware_base_uri) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->firmware_base_uri, firmware_base_uri) == 0) + return; + + g_free(priv->firmware_base_uri); + priv->firmware_base_uri = g_strdup(firmware_base_uri); +} + +static void +fwupd_remote_set_report_uri(FwupdRemote *self, const gchar *report_uri) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *report_uri_safe = fwupd_strdup_nonempty(report_uri); + + /* not changed */ + if (g_strcmp0(priv->report_uri, report_uri_safe) == 0) + return; + + g_free(priv->report_uri); + priv->report_uri = g_steal_pointer(&report_uri_safe); +} + +static void +fwupd_remote_set_security_report_uri(FwupdRemote *self, const gchar *security_report_uri) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *security_report_uri_safe = fwupd_strdup_nonempty(security_report_uri); + + /* not changed */ + if (g_strcmp0(priv->security_report_uri, security_report_uri_safe) == 0) + return; + + g_free(priv->security_report_uri); + priv->security_report_uri = g_steal_pointer(&security_report_uri_safe); +} + +/** + * fwupd_remote_kind_from_string: + * @kind: (nullable): a string, e.g. `download` + * + * Converts an printable string to an enumerated type. + * + * Returns: a #FwupdRemoteKind, e.g. %FWUPD_REMOTE_KIND_DOWNLOAD + * + * Since: 0.9.6 + **/ +FwupdRemoteKind +fwupd_remote_kind_from_string(const gchar *kind) +{ + if (g_strcmp0(kind, "download") == 0) + 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; +} + +/** + * fwupd_remote_kind_to_string: + * @kind: a #FwupdRemoteKind, e.g. %FWUPD_REMOTE_KIND_DOWNLOAD + * + * Converts an enumerated type to a printable string. + * + * Returns: a string, e.g. `download` + * + * Since: 0.9.6 + **/ +const gchar * +fwupd_remote_kind_to_string(FwupdRemoteKind kind) +{ + if (kind == FWUPD_REMOTE_KIND_DOWNLOAD) + return "download"; + if (kind == FWUPD_REMOTE_KIND_LOCAL) + return "local"; + if (kind == FWUPD_REMOTE_KIND_DIRECTORY) + return "directory"; + return NULL; +} + +/** + * fwupd_remote_set_filename_cache: + * @self: a #FwupdRemote + * @filename: (nullable): filename string + * + * Sets the remote filename cache filename, typically only useful in the self tests. + * + * Since: 1.8.2 + **/ +void +fwupd_remote_set_filename_cache(FwupdRemote *self, const gchar *filename) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + const gchar *suffix; + + g_return_if_fail(FWUPD_IS_REMOTE(self)); + + /* not changed */ + if (g_strcmp0(priv->filename_cache, filename) == 0) + return; + + g_free(priv->filename_cache); + priv->filename_cache = g_strdup(filename); + + /* create for all remote types */ + suffix = fwupd_remote_get_suffix_for_keyring_kind(priv->keyring_kind); + if (suffix != NULL) { + g_free(priv->filename_cache_sig); + priv->filename_cache_sig = g_strconcat(filename, suffix, NULL); + } +} + +static void +fwupd_remote_set_order_before(FwupdRemote *self, const gchar *order_before) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_clear_pointer(&priv->order_before, g_strfreev); + if (order_before != NULL) + priv->order_before = g_strsplit_set(order_before, ",:;", -1); +} + +static void +fwupd_remote_set_order_after(FwupdRemote *self, const gchar *order_after) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_clear_pointer(&priv->order_after, g_strfreev); + if (order_after != NULL) + priv->order_after = g_strsplit_set(order_after, ",:;", -1); +} + +/** + * fwupd_remote_setup: + * @self: a #FwupdRemote + * @error: (nullable): optional return location for an error + * + * Sets up the remote ready for use, checking that required parameters have + * been set. Calling this method multiple times has no effect. + * + * Returns: %TRUE for success + * + * Since: 1.6.1 + **/ +gboolean +fwupd_remote_setup(FwupdRemote *self, GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* we can override, hence the extra section */ + if (priv->kind == FWUPD_REMOTE_KIND_UNKNOWN) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "metadata kind invalid"); + return FALSE; + } + + /* some validation for DOWNLOAD types */ + if (priv->kind == FWUPD_REMOTE_KIND_DOWNLOAD) { + g_autofree gchar *filename_cache = NULL; + + if (priv->remotes_dir == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "remotes directory not set"); + return FALSE; + } + /* set cache to /var/lib... */ + filename_cache = + g_build_filename(priv->remotes_dir, priv->id, "metadata.xml.gz", NULL); + fwupd_remote_set_filename_cache(self, filename_cache); + } + + /* some validation for 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 (priv->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; + } + } + + /* load the checksum */ + if (priv->filename_cache_sig != NULL && + g_file_test(priv->filename_cache_sig, G_FILE_TEST_EXISTS)) { + gsize sz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(GChecksum) checksum = g_checksum_new(G_CHECKSUM_SHA256); + if (!g_file_get_contents(priv->filename_cache_sig, &buf, &sz, error)) { + g_prefix_error(error, "failed to get checksum: "); + return FALSE; + } + g_checksum_update(checksum, (guchar *)buf, (gssize)sz); + fwupd_remote_set_checksum(self, g_checksum_get_string(checksum)); + } else { + fwupd_remote_set_checksum(self, NULL); + } + + /* success */ + return TRUE; +} + +/** + * fwupd_remote_load_from_filename: + * @self: a #FwupdRemote + * @filename: (not nullable): a filename + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Loads metadata about the remote from a keyfile. + * This can be called zero or multiple times for each remote. + * + * Returns: %TRUE for success + * + * Since: 0.9.3 + **/ +gboolean +fwupd_remote_load_from_filename(FwupdRemote *self, + const gchar *filename, + GCancellable *cancellable, + GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + const gchar *group = "fwupd Remote"; + g_autofree gchar *id = NULL; + g_autoptr(GKeyFile) kf = NULL; + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* set ID */ + id = g_path_get_basename(filename); + fwupd_remote_set_id(self, id); + + /* load file */ + kf = g_key_file_new(); + if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, error)) + return FALSE; + + /* optional verification type */ + if (g_key_file_has_key(kf, group, "Keyring", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "Keyring", NULL); + priv->keyring_kind = fwupd_keyring_kind_from_string(tmp); + if (priv->keyring_kind == FWUPD_KEYRING_KIND_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "keyring kind '%s' unknown", + tmp); + return FALSE; + } + } + + /* the first remote sets the URI, even if it's file:// to the cache */ + if (g_key_file_has_key(kf, group, "MetadataURI", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "MetadataURI", NULL); + if (g_str_has_prefix(tmp, "file://")) { + const gchar *filename_cache = tmp; + 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(tmp, "http://") || g_str_has_prefix(tmp, "https://") || + g_str_has_prefix(tmp, "ipfs://") || g_str_has_prefix(tmp, "ipns://")) { + priv->kind = FWUPD_REMOTE_KIND_DOWNLOAD; + fwupd_remote_set_metadata_uri(self, tmp); + } + } + + /* all keys are optional */ + if (g_key_file_has_key(kf, group, "Enabled", NULL)) + priv->enabled = g_key_file_get_boolean(kf, group, "Enabled", NULL); + if (g_key_file_has_key(kf, group, "ApprovalRequired", NULL)) + priv->approval_required = + g_key_file_get_boolean(kf, group, "ApprovalRequired", NULL); + if (g_key_file_has_key(kf, group, "Title", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "Title", NULL); + fwupd_remote_set_title(self, tmp); + } + if (g_key_file_has_key(kf, group, "ReportURI", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "ReportURI", NULL); + fwupd_remote_set_report_uri(self, tmp); + } + if (g_key_file_has_key(kf, group, "SecurityReportURI", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "SecurityReportURI", NULL); + fwupd_remote_set_security_report_uri(self, tmp); + } + if (g_key_file_has_key(kf, group, "Username", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "Username", NULL); + fwupd_remote_set_username(self, tmp); + } + if (g_key_file_has_key(kf, group, "Password", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "Password", NULL); + fwupd_remote_set_password(self, tmp); + } + if (g_key_file_has_key(kf, group, "FirmwareBaseURI", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "FirmwareBaseURI", NULL); + fwupd_remote_set_firmware_base_uri(self, tmp); + } + if (g_key_file_has_key(kf, group, "OrderBefore", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "OrderBefore", NULL); + fwupd_remote_set_order_before(self, tmp); + } + if (g_key_file_has_key(kf, group, "OrderAfter", NULL)) { + g_autofree gchar *tmp = g_key_file_get_string(kf, group, "OrderAfter", NULL); + fwupd_remote_set_order_after(self, tmp); + } + if (g_key_file_has_key(kf, group, "AutomaticReports", NULL)) + priv->automatic_reports = + g_key_file_get_boolean(kf, group, "AutomaticReports", NULL); + if (g_key_file_has_key(kf, group, "AutomaticSecurityReports", NULL)) + priv->automatic_security_reports = + g_key_file_get_boolean(kf, group, "AutomaticSecurityReports", NULL); + + /* success */ + fwupd_remote_set_filename_source(self, filename); + return TRUE; +} + +/** + * fwupd_remote_get_order_after: + * @self: a #FwupdRemote + * + * Gets the list of remotes this plugin should be ordered after. + * + * Returns: (transfer none): an array + * + * Since: 0.9.5 + **/ +gchar ** +fwupd_remote_get_order_after(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->order_after; +} + +/** + * fwupd_remote_get_order_before: + * @self: a #FwupdRemote + * + * Gets the list of remotes this plugin should be ordered before. + * + * Returns: (transfer none): an array + * + * Since: 0.9.5 + **/ +gchar ** +fwupd_remote_get_order_before(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->order_before; +} + +/** + * fwupd_remote_get_filename_cache: + * @self: a #FwupdRemote + * + * Gets the path and filename that the remote is using for a cache. + * + * Returns: a string, or %NULL for unset + * + * Since: 0.9.6 + **/ +const gchar * +fwupd_remote_get_filename_cache(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->filename_cache; +} + +/** + * fwupd_remote_get_filename_cache_sig: + * @self: a #FwupdRemote + * + * Gets the path and filename that the remote is using for a signature cache. + * + * Returns: a string, or %NULL for unset + * + * Since: 0.9.7 + **/ +const gchar * +fwupd_remote_get_filename_cache_sig(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->filename_cache_sig; +} + +/** + * fwupd_remote_get_filename_source: + * @self: a #FwupdRemote + * + * Gets the path and filename of the remote itself, typically a `.conf` file. + * + * Returns: a string, or %NULL for unset + * + * Since: 0.9.8 + **/ +const gchar * +fwupd_remote_get_filename_source(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->filename_source; +} + +/** + * fwupd_remote_get_priority: + * @self: a #FwupdRemote + * + * Gets the priority of the remote, where bigger numbers are better. + * + * Returns: a priority, or 0 for the default value + * + * Since: 0.9.5 + **/ +gint +fwupd_remote_get_priority(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), 0); + return priv->priority; +} + +/** + * fwupd_remote_get_kind: + * @self: a #FwupdRemote + * + * Gets the kind of the remote. + * + * Returns: a #FwupdRemoteKind, e.g. #FWUPD_REMOTE_KIND_LOCAL + * + * Since: 0.9.6 + **/ +FwupdRemoteKind +fwupd_remote_get_kind(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), 0); + return priv->kind; +} + +/** + * fwupd_remote_get_keyring_kind: + * @self: a #FwupdRemote + * + * Gets the keyring kind of the remote. + * + * Returns: a #FwupdKeyringKind, e.g. #FWUPD_KEYRING_KIND_GPG + * + * Since: 0.9.7 + **/ +FwupdKeyringKind +fwupd_remote_get_keyring_kind(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), 0); + return priv->keyring_kind; +} + +/** + * fwupd_remote_get_age: + * @self: a #FwupdRemote + * + * Gets the age of the remote in seconds. + * + * Returns: a age, or %G_MAXUINT64 for unavailable + * + * Since: 0.9.5 + **/ +guint64 +fwupd_remote_get_age(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + guint64 now; + g_return_val_if_fail(FWUPD_IS_REMOTE(self), 0); + now = (guint64)g_get_real_time() / G_USEC_PER_SEC; + if (priv->mtime > now) + return G_MAXUINT64; + return now - priv->mtime; +} + +/** + * fwupd_remote_set_remotes_dir: + * @self: a #FwupdRemote + * @directory: (nullable): Remotes directory + * + * Sets the directory to store remote data + * + * Since: 1.3.1 + **/ +void +fwupd_remote_set_remotes_dir(FwupdRemote *self, const gchar *directory) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REMOTE(self)); + + /* not changed */ + if (g_strcmp0(priv->remotes_dir, directory) == 0) + return; + + g_free(priv->remotes_dir); + priv->remotes_dir = g_strdup(directory); +} + +/** + * fwupd_remote_set_priority: + * @self: a #FwupdRemote + * @priority: an integer, where 1 is better + * + * Sets the plugin priority. + * + * Since: 0.9.5 + **/ +void +fwupd_remote_set_priority(FwupdRemote *self, gint priority) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REMOTE(self)); + priv->priority = priority; +} + +/** + * fwupd_remote_set_mtime: + * @self: a #FwupdRemote + * @mtime: a UNIX timestamp + * + * Sets the plugin modification time. + * + * Since: 0.9.5 + **/ +void +fwupd_remote_set_mtime(FwupdRemote *self, guint64 mtime) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REMOTE(self)); + priv->mtime = mtime; +} + +/** + * fwupd_remote_get_username: + * @self: a #FwupdRemote + * + * Gets the username configured for the remote. + * + * Returns: a string, or %NULL for unset + * + * Since: 0.9.5 + **/ +const gchar * +fwupd_remote_get_username(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->username; +} + +/** + * fwupd_remote_get_password: + * @self: a #FwupdRemote + * + * Gets the password configured for the remote. + * + * Returns: a string, or %NULL for unset + * + * Since: 0.9.5 + **/ +const gchar * +fwupd_remote_get_password(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->password; +} + +/** + * fwupd_remote_get_title: + * @self: a #FwupdRemote + * + * Gets the remote title, e.g. `Linux Vendor Firmware Service`. + * + * Returns: a string, or %NULL if unset + * + * Since: 0.9.8 + **/ +const gchar * +fwupd_remote_get_title(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->title; +} + +/** + * fwupd_remote_get_agreement: + * @self: a #FwupdRemote + * + * Gets the remote agreement in AppStream markup format + * + * Returns: a string, or %NULL if unset + * + * Since: 1.0.7 + **/ +const gchar * +fwupd_remote_get_agreement(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->agreement; +} + +/** + * fwupd_remote_get_remotes_dir: + * @self: a #FwupdRemote + * + * Gets the base directory for storing remote metadata + * + * Returns: a string, or %NULL if unset + * + * Since: 1.3.1 + **/ +const gchar * +fwupd_remote_get_remotes_dir(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->remotes_dir; +} + +/** + * fwupd_remote_get_checksum: + * @self: a #FwupdRemote + * + * Gets the remote checksum. + * + * Returns: a string, or %NULL if unset + * + * Since: 1.0.0 + **/ +const gchar * +fwupd_remote_get_checksum(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->checksum; +} + +/** + * fwupd_remote_build_firmware_uri: + * @self: a #FwupdRemote + * @url: (not nullable): the URL to use + * @error: (nullable): optional return location for an error + * + * Builds a URI for the URL using the username and password set for the remote, + * including any basename URI substitution. + * + * Returns: (transfer full): a URI, or %NULL for error + * + * Since: 0.9.7 + **/ +gchar * +fwupd_remote_build_firmware_uri(FwupdRemote *self, const gchar *url, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + g_return_val_if_fail(url != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return fwupd_remote_build_uri(self, url, error); +} + +/** + * fwupd_remote_get_report_uri: + * @self: a #FwupdRemote + * + * Gets the URI for the remote reporting. + * + * Returns: (transfer none): a URI, or %NULL for invalid. + * + * Since: 1.0.4 + **/ +const gchar * +fwupd_remote_get_report_uri(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->report_uri; +} + +/** + * fwupd_remote_get_security_report_uri: + * @self: a #FwupdRemote + * + * Gets the URI for the security report. + * + * Returns: (transfer none): a URI, or %NULL for invalid. + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_remote_get_security_report_uri(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->security_report_uri; +} + +/** + * fwupd_remote_get_metadata_uri: + * @self: a #FwupdRemote + * + * Gets the URI for the remote metadata. + * + * Returns: (transfer none): a URI, or %NULL for invalid. + * + * Since: 0.9.7 + **/ +const gchar * +fwupd_remote_get_metadata_uri(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->metadata_uri; +} + +static gboolean +fwupd_remote_load_signature_jcat(FwupdRemote *self, JcatFile *jcat_file, GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + const gchar *id; + g_autofree gchar *basename = NULL; + g_autofree gchar *baseuri = NULL; + g_autofree gchar *metadata_uri = NULL; + g_autoptr(JcatItem) jcat_item = NULL; + + /* this seems pointless to get the item by ID then just read the ID, + * but _get_item_by_id() uses the AliasIds as a fallback */ + basename = g_path_get_basename(priv->metadata_uri); + jcat_item = jcat_file_get_item_by_id(jcat_file, basename, NULL); + if (jcat_item == NULL) { + /* if we're using libjcat 0.1.0 just get the default item */ + jcat_item = jcat_file_get_item_default(jcat_file, error); + if (jcat_item == NULL) + return FALSE; + } + id = jcat_item_get_id(jcat_item); + if (id == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No ID for JCat item"); + return FALSE; + } + + /* replace the URI if required */ + baseuri = g_path_get_dirname(priv->metadata_uri); + metadata_uri = g_build_path("/", baseuri, id, NULL); + if (g_strcmp0(metadata_uri, priv->metadata_uri) != 0) { + g_debug("changing metadata URI from %s to %s", priv->metadata_uri, metadata_uri); + g_free(priv->metadata_uri); + priv->metadata_uri = g_steal_pointer(&metadata_uri); + } + + /* success */ + return TRUE; +} + +/** + * fwupd_remote_load_signature_bytes: + * @self: a #FwupdRemote + * @bytes: (not nullable): data blob + * @error: (nullable): optional return location for an error + * + * Parses the signature, updating the metadata URI as appropriate. + * + * This can only be called for remotes with `Keyring=jcat` which is + * the default for most remotes. + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fwupd_remote_load_signature_bytes(FwupdRemote *self, GBytes *bytes, GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_autoptr(GInputStream) istr = NULL; + g_autoptr(JcatFile) jcat_file = jcat_file_new(); + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(bytes != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* sanity check */ + if (priv->keyring_kind != FWUPD_KEYRING_KIND_JCAT) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only supported for JCat remotes"); + return FALSE; + } + + istr = g_memory_input_stream_new_from_bytes(bytes); + if (!jcat_file_import_stream(jcat_file, istr, JCAT_IMPORT_FLAG_NONE, NULL, error)) + return FALSE; + return fwupd_remote_load_signature_jcat(self, jcat_file, error); +} + +/** + * fwupd_remote_load_signature: + * @self: a #FwupdRemote + * @filename: (not nullable): a filename + * @error: (nullable): optional return location for an error + * + * Parses the signature, updating the metadata URI as appropriate. + * + * Returns: %TRUE for success + * + * Since: 1.4.0 + **/ +gboolean +fwupd_remote_load_signature(FwupdRemote *self, const gchar *filename, GError **error) +{ + g_autoptr(GFile) gfile = NULL; + g_autoptr(JcatFile) jcat_file = jcat_file_new(); + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* load JCat file */ + gfile = g_file_new_for_path(filename); + if (!jcat_file_import_file(jcat_file, gfile, JCAT_IMPORT_FLAG_NONE, NULL, error)) + return FALSE; + return fwupd_remote_load_signature_jcat(self, jcat_file, error); +} + +/** + * fwupd_remote_get_metadata_uri_sig: + * @self: a #FwupdRemote + * + * Gets the URI for the remote metadata signature. + * + * Returns: (transfer none): a URI, or %NULL for invalid. + * + * Since: 0.9.7 + **/ +const gchar * +fwupd_remote_get_metadata_uri_sig(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->metadata_uri_sig; +} + +/** + * fwupd_remote_get_firmware_base_uri: + * @self: a #FwupdRemote + * + * Gets the base URI for firmware. + * + * Returns: (transfer none): a URI, or %NULL for unset. + * + * Since: 0.9.7 + **/ +const gchar * +fwupd_remote_get_firmware_base_uri(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->firmware_base_uri; +} + +/** + * fwupd_remote_get_enabled: + * @self: a #FwupdRemote + * + * Gets if the remote is enabled and should be used. + * + * Returns: a #TRUE if the remote is enabled + * + * Since: 0.9.3 + **/ +gboolean +fwupd_remote_get_enabled(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + return priv->enabled; +} + +/** + * fwupd_remote_get_automatic_reports: + * @self: a #FwupdRemote + * + * Gets if reports should be automatically uploaded to this remote + * + * Returns: a #TRUE if the remote should have reports uploaded automatically + * + * Since: 1.3.3 + **/ +gboolean +fwupd_remote_get_automatic_reports(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + return priv->automatic_reports; +} + +/** + * fwupd_remote_get_automatic_security_reports: + * @self: a #FwupdRemote + * + * Gets if security reports should be automatically uploaded to this remote + * + * Returns: a #TRUE if the remote should have reports uploaded automatically + * + * Since: 1.5.0 + **/ +gboolean +fwupd_remote_get_automatic_security_reports(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + return priv->automatic_security_reports; +} + +/** + * 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 + * + * Gets the remote ID, e.g. `lvfs-testing`. + * + * Returns: a string, or %NULL if unset + * + * Since: 0.9.3 + **/ +const gchar * +fwupd_remote_get_id(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), NULL); + return priv->id; +} + +static void +fwupd_remote_set_from_variant_iter(FwupdRemote *self, GVariantIter *iter) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + GVariant *value; + const gchar *key; + g_autoptr(GVariantIter) iter2 = g_variant_iter_copy(iter); + g_autoptr(GVariantIter) iter3 = g_variant_iter_copy(iter); + + /* three passes, as we have to construct Id -> Url -> * */ + while (g_variant_iter_loop(iter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, FWUPD_RESULT_KEY_REMOTE_ID) == 0) + fwupd_remote_set_id(self, g_variant_get_string(value, NULL)); + if (g_strcmp0(key, "Type") == 0) + fwupd_remote_set_kind(self, g_variant_get_uint32(value)); + if (g_strcmp0(key, "Keyring") == 0) + fwupd_remote_set_keyring_kind(self, g_variant_get_uint32(value)); + } + while (g_variant_iter_loop(iter2, "{&sv}", &key, &value)) { + if (g_strcmp0(key, FWUPD_RESULT_KEY_URI) == 0) + fwupd_remote_set_metadata_uri(self, g_variant_get_string(value, NULL)); + if (g_strcmp0(key, "FilenameCache") == 0) + fwupd_remote_set_filename_cache(self, g_variant_get_string(value, NULL)); + if (g_strcmp0(key, "FilenameSource") == 0) + fwupd_remote_set_filename_source(self, g_variant_get_string(value, NULL)); + if (g_strcmp0(key, "ReportUri") == 0) + fwupd_remote_set_report_uri(self, g_variant_get_string(value, NULL)); + if (g_strcmp0(key, "SecurityReportUri") == 0) + fwupd_remote_set_security_report_uri(self, + g_variant_get_string(value, NULL)); + } + while (g_variant_iter_loop(iter3, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "Username") == 0) { + fwupd_remote_set_username(self, g_variant_get_string(value, NULL)); + } else if (g_strcmp0(key, "Password") == 0) { + fwupd_remote_set_password(self, g_variant_get_string(value, NULL)); + } else if (g_strcmp0(key, "Title") == 0) { + fwupd_remote_set_title(self, g_variant_get_string(value, NULL)); + } else if (g_strcmp0(key, "Agreement") == 0) { + fwupd_remote_set_agreement(self, g_variant_get_string(value, NULL)); + } else if (g_strcmp0(key, FWUPD_RESULT_KEY_CHECKSUM) == 0) { + 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) { + priv->mtime = g_variant_get_uint64(value); + } else if (g_strcmp0(key, "FirmwareBaseUri") == 0) { + fwupd_remote_set_firmware_base_uri(self, g_variant_get_string(value, NULL)); + } else if (g_strcmp0(key, "AutomaticReports") == 0) { + priv->automatic_reports = g_variant_get_boolean(value); + } else if (g_strcmp0(key, "AutomaticSecurityReports") == 0) { + priv->automatic_security_reports = g_variant_get_boolean(value); + } + } +} + +/** + * fwupd_remote_to_variant: + * @self: a #FwupdRemote + * + * Serialize the remote data. + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.0.0 + **/ +GVariant * +fwupd_remote_to_variant(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + 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_VARDICT); + if (priv->id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_REMOTE_ID, + g_variant_new_string(priv->id)); + } + if (priv->username != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "Username", + g_variant_new_string(priv->username)); + } + if (priv->password != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "Password", + g_variant_new_string(priv->password)); + } + if (priv->title != NULL) { + g_variant_builder_add(&builder, "{sv}", "Title", g_variant_new_string(priv->title)); + } + if (priv->agreement != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "Agreement", + g_variant_new_string(priv->agreement)); + } + if (priv->checksum != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CHECKSUM, + g_variant_new_string(priv->checksum)); + } + if (priv->metadata_uri != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_URI, + g_variant_new_string(priv->metadata_uri)); + } + if (priv->report_uri != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "ReportUri", + g_variant_new_string(priv->report_uri)); + } + if (priv->security_report_uri != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "SecurityReportUri", + g_variant_new_string(priv->security_report_uri)); + } + if (priv->firmware_base_uri != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "FirmwareBaseUri", + g_variant_new_string(priv->firmware_base_uri)); + } + if (priv->priority != 0) { + g_variant_builder_add(&builder, + "{sv}", + "Priority", + g_variant_new_int32(priv->priority)); + } + if (priv->kind != FWUPD_REMOTE_KIND_UNKNOWN) { + g_variant_builder_add(&builder, "{sv}", "Type", g_variant_new_uint32(priv->kind)); + } + if (priv->keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { + g_variant_builder_add(&builder, + "{sv}", + "Keyring", + g_variant_new_uint32(priv->keyring_kind)); + } + if (priv->mtime != 0) { + g_variant_builder_add(&builder, + "{sv}", + "ModificationTime", + g_variant_new_uint64(priv->mtime)); + } + if (priv->filename_cache != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "FilenameCache", + g_variant_new_string(priv->filename_cache)); + } + if (priv->filename_source != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "FilenameSource", + g_variant_new_string(priv->filename_source)); + } + if (priv->remotes_dir != NULL) { + g_variant_builder_add(&builder, + "{sv}", + "RemotesDir", + g_variant_new_string(priv->remotes_dir)); + } + g_variant_builder_add(&builder, "{sv}", "Enabled", g_variant_new_boolean(priv->enabled)); + g_variant_builder_add(&builder, + "{sv}", + "ApprovalRequired", + g_variant_new_boolean(priv->approval_required)); + g_variant_builder_add(&builder, + "{sv}", + "AutomaticReports", + g_variant_new_boolean(priv->automatic_reports)); + g_variant_builder_add(&builder, + "{sv}", + "AutomaticSecurityReports", + g_variant_new_boolean(priv->automatic_security_reports)); + return g_variant_new("a{sv}", &builder); +} + +static void +fwupd_remote_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FwupdRemote *self = FWUPD_REMOTE(obj); + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + switch (prop_id) { + 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; + case PROP_AUTOMATIC_REPORTS: + g_value_set_boolean(value, priv->automatic_reports); + break; + case PROP_AUTOMATIC_SECURITY_REPORTS: + g_value_set_boolean(value, priv->automatic_security_reports); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +fwupd_remote_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FwupdRemote *self = FWUPD_REMOTE(obj); + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + switch (prop_id) { + 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; + case PROP_AUTOMATIC_REPORTS: + priv->automatic_reports = g_value_get_boolean(value); + break; + case PROP_AUTOMATIC_SECURITY_REPORTS: + priv->automatic_security_reports = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +fwupd_remote_class_init(FwupdRemoteClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fwupd_remote_finalize; + object_class->get_property = fwupd_remote_get_property; + object_class->set_property = fwupd_remote_set_property; + + /** + * FwupdRemote:id: + * + * The remote ID. + * + * Since: 0.9.3 + */ + pspec = + g_param_spec_string("id", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_ID, pspec); + + /** + * FwupdRemote:enabled: + * + * If the remote is enabled and should be used. + * + * Since: 0.9.3 + */ + pspec = g_param_spec_boolean("enabled", + NULL, + NULL, + 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); + + /** + * FwupdRemote:automatic-reports: + * + * The behavior for auto-uploading reports. + * + * Since: 1.3.3 + */ + pspec = g_param_spec_boolean("automatic-reports", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_AUTOMATIC_REPORTS, pspec); + + /** + * FwupdRemote:automatic-security-reports: + * + * The behavior for auto-uploading security reports. + * + * Since: 1.5.0 + */ + pspec = g_param_spec_boolean("automatic-security-reports", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_AUTOMATIC_SECURITY_REPORTS, pspec); +} + +static void +fwupd_remote_init(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + priv->keyring_kind = FWUPD_KEYRING_KIND_JCAT; +} + +static void +fwupd_remote_finalize(GObject *obj) +{ + FwupdRemote *self = FWUPD_REMOTE(obj); + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + g_free(priv->id); + g_free(priv->metadata_uri); + g_free(priv->metadata_uri_sig); + g_free(priv->firmware_base_uri); + g_free(priv->report_uri); + g_free(priv->security_report_uri); + g_free(priv->username); + g_free(priv->password); + g_free(priv->title); + g_free(priv->agreement); + g_free(priv->remotes_dir); + g_free(priv->checksum); + g_free(priv->filename_cache); + g_free(priv->filename_cache_sig); + g_free(priv->filename_source); + g_strfreev(priv->order_after); + g_strfreev(priv->order_before); + + G_OBJECT_CLASS(fwupd_remote_parent_class)->finalize(obj); +} + +/** + * fwupd_remote_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new remote using serialized data. + * + * Returns: (transfer full): a new #FwupdRemote, or %NULL if @value was invalid + * + * Since: 1.0.0 + **/ +FwupdRemote * +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(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + rel = fwupd_remote_new(); + 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(value, "a{sv}", &iter); + fwupd_remote_set_from_variant_iter(rel, iter); + } else { + g_warning("type %s not known", type_string); + } + return rel; +} + +/** + * fwupd_remote_array_from_variant: + * @value: (not nullable): the serialized data + * + * Creates an array of new devices using serialized 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; + + g_return_val_if_fail(value != NULL, 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. + * + * Returns: a new #FwupdRemote + * + * Since: 0.9.3 + **/ +FwupdRemote * +fwupd_remote_new(void) +{ + FwupdRemote *self; + self = g_object_new(FWUPD_TYPE_REMOTE, NULL); + return FWUPD_REMOTE(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-remote.h b/fwupd-1.8.6/libfwupd/fwupd-remote.h new file mode 100644 index 0000000000000000000000000000000000000000..c8551d5cdf2dbdbf09c28761d5b1bd68b467561d --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-remote.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_REMOTE (fwupd_remote_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdRemote, fwupd_remote, FWUPD, REMOTE, GObject) + +struct _FwupdRemoteClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +/** + * FwupdRemoteKind: + * @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. + **/ +typedef enum { + FWUPD_REMOTE_KIND_UNKNOWN, + FWUPD_REMOTE_KIND_DOWNLOAD, + FWUPD_REMOTE_KIND_LOCAL, + FWUPD_REMOTE_KIND_DIRECTORY, /* Since: 1.2.4 */ + /*< private >*/ + FWUPD_REMOTE_KIND_LAST +} FwupdRemoteKind; + +FwupdRemoteKind +fwupd_remote_kind_from_string(const gchar *kind); +const gchar * +fwupd_remote_kind_to_string(FwupdRemoteKind kind); + +FwupdRemote * +fwupd_remote_new(void); +const gchar * +fwupd_remote_get_id(FwupdRemote *self); +const gchar * +fwupd_remote_get_title(FwupdRemote *self); +const gchar * +fwupd_remote_get_agreement(FwupdRemote *self); +const gchar * +fwupd_remote_get_remotes_dir(FwupdRemote *self); +const gchar * +fwupd_remote_get_checksum(FwupdRemote *self); +const gchar * +fwupd_remote_get_username(FwupdRemote *self); +const gchar * +fwupd_remote_get_password(FwupdRemote *self); +const gchar * +fwupd_remote_get_filename_cache(FwupdRemote *self); +const gchar * +fwupd_remote_get_filename_cache_sig(FwupdRemote *self); +const gchar * +fwupd_remote_get_filename_source(FwupdRemote *self); +const gchar * +fwupd_remote_get_firmware_base_uri(FwupdRemote *self); +const gchar * +fwupd_remote_get_report_uri(FwupdRemote *self); +const gchar * +fwupd_remote_get_security_report_uri(FwupdRemote *self); +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); +gboolean +fwupd_remote_get_automatic_reports(FwupdRemote *self); +gboolean +fwupd_remote_get_automatic_security_reports(FwupdRemote *self); +gint +fwupd_remote_get_priority(FwupdRemote *self); +guint64 +fwupd_remote_get_age(FwupdRemote *self); +FwupdRemoteKind +fwupd_remote_get_kind(FwupdRemote *self); +FwupdKeyringKind +fwupd_remote_get_keyring_kind(FwupdRemote *self); +gchar * +fwupd_remote_build_firmware_uri(FwupdRemote *self, + const gchar *url, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_remote_load_signature(FwupdRemote *self, + const gchar *filename, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fwupd_remote_load_signature_bytes(FwupdRemote *self, + GBytes *bytes, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +FwupdRemote * +fwupd_remote_from_variant(GVariant *value); +GPtrArray * +fwupd_remote_array_from_variant(GVariant *value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-request-private.h b/fwupd-1.8.6/libfwupd/fwupd-request-private.h new file mode 100644 index 0000000000000000000000000000000000000000..cd112d37673e7c475933613e94f3dbc6cd413944 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-request-private.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-request.h" + +G_BEGIN_DECLS + +GVariant * +fwupd_request_to_variant(FwupdRequest *self); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-request.c b/fwupd-1.8.6/libfwupd/fwupd-request.c new file mode 100644 index 0000000000000000000000000000000000000000..a363f32b423e1994e731c1ca6b318a87e8189eba --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-request.c @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-request-private.h" + +/** + * FwupdRequest: + * + * A user request from the device. + * + * See also: [class@FwupdDevice] + */ + +typedef struct { + gchar *id; + FwupdRequestKind kind; + FwupdRequestFlags flags; + guint64 created; + gchar *device_id; + gchar *message; + gchar *image; +} FwupdRequestPrivate; + +enum { + PROP_0, + PROP_ID, + PROP_KIND, + PROP_FLAGS, + PROP_MESSAGE, + PROP_IMAGE, + PROP_DEVICE_ID, + PROP_LAST +}; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdRequest, fwupd_request, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_request_get_instance_private(o)) + +/** + * fwupd_request_kind_to_string: + * @kind: a update message kind, e.g. %FWUPD_REQUEST_KIND_IMMEDIATE + * + * Converts an enumerated update message kind to a string. + * + * Returns: identifier string + * + * Since: 1.6.2 + **/ +const gchar * +fwupd_request_kind_to_string(FwupdRequestKind kind) +{ + if (kind == FWUPD_REQUEST_KIND_UNKNOWN) + return "unknown"; + if (kind == FWUPD_REQUEST_KIND_POST) + return "post"; + if (kind == FWUPD_REQUEST_KIND_IMMEDIATE) + return "immediate"; + return NULL; +} + +/** + * fwupd_request_kind_from_string: + * @kind: (nullable): a string, e.g. `immediate` + * + * Converts a string to an enumerated update message kind. + * + * Returns: enumerated value + * + * Since: 1.6.2 + **/ +FwupdRequestKind +fwupd_request_kind_from_string(const gchar *kind) +{ + if (g_strcmp0(kind, "unknown") == 0) + return FWUPD_REQUEST_KIND_UNKNOWN; + if (g_strcmp0(kind, "post") == 0) + return FWUPD_REQUEST_KIND_POST; + if (g_strcmp0(kind, "immediate") == 0) + return FWUPD_REQUEST_KIND_IMMEDIATE; + return FWUPD_REQUEST_KIND_LAST; +} + +/** + * fwupd_request_flag_to_string: + * @flag: a request flag, e.g. %FWUPD_REQUEST_FLAG_NONE + * + * Converts an enumerated request flag to a string. + * + * Returns: identifier string + * + * Since: 1.8.6 + **/ +const gchar * +fwupd_request_flag_to_string(FwupdRequestFlags flag) +{ + if (flag == FWUPD_REQUEST_FLAG_NONE) + return "none"; + if (flag == FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE) + return "allow-generic-message"; + if (flag == FWUPD_REQUEST_FLAG_ALLOW_GENERIC_IMAGE) + return "allow-generic-image"; + return NULL; +} + +/** + * fwupd_request_flag_from_string: + * @flag: (nullable): a string, e.g. `none` + * + * Converts a string to an enumerated request flag. + * + * Returns: enumerated value + * + * Since: 1.8.6 + **/ +FwupdRequestFlags +fwupd_request_flag_from_string(const gchar *flag) +{ + if (g_strcmp0(flag, "allow-generic-message") == 0) + return FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE; + if (g_strcmp0(flag, "allow-generic-image") == 0) + return FWUPD_REQUEST_FLAG_ALLOW_GENERIC_IMAGE; + return FWUPD_REQUEST_FLAG_NONE; +} + +/** + * fwupd_request_get_id: + * @self: a #FwupdRequest + * + * Gets the ID. + * + * Returns: the ID, or %NULL if unset + * + * Since: 1.6.2 + **/ +const gchar * +fwupd_request_get_id(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), NULL); + return priv->id; +} + +/** + * fwupd_request_set_id: + * @self: a #FwupdRequest + * @id: (nullable): the request ID, e.g. `USB:foo` + * + * Sets the ID. + * + * Since: 1.6.2 + **/ +void +fwupd_request_set_id(FwupdRequest *self, const gchar *id) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + g_free(priv->id); + priv->id = g_strdup(id); +} + +/** + * fwupd_request_get_device_id: + * @self: a #FwupdRequest + * + * Gets the device_id that created the request. + * + * Returns: the device_id, or %NULL if unset + * + * Since: 1.6.2 + **/ +const gchar * +fwupd_request_get_device_id(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), NULL); + return priv->device_id; +} + +/** + * fwupd_request_set_device_id: + * @self: a #FwupdRequest + * @device_id: (nullable): the device_id, e.g. `colorhug` + * + * Sets the device_id that created the request. + * + * Since: 1.6.2 + **/ +void +fwupd_request_set_device_id(FwupdRequest *self, const gchar *device_id) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + + /* not changed */ + if (g_strcmp0(priv->device_id, device_id) == 0) + return; + + g_free(priv->device_id); + priv->device_id = g_strdup(device_id); +} + +/** + * fwupd_request_get_created: + * @self: a #FwupdRequest + * + * Gets when the request was created. + * + * Returns: the UNIX time, or 0 if unset + * + * Since: 1.6.2 + **/ +guint64 +fwupd_request_get_created(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), 0); + return priv->created; +} + +/** + * fwupd_request_set_created: + * @self: a #FwupdRequest + * @created: the UNIX time + * + * Sets when the request was created. + * + * Since: 1.6.2 + **/ +void +fwupd_request_set_created(FwupdRequest *self, guint64 created) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + priv->created = created; +} + +/** + * fwupd_request_to_variant: + * @self: a #FwupdRequest + * + * Serialize the request data. + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.6.2 + **/ +GVariant * +fwupd_request_to_variant(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + g_return_val_if_fail(FWUPD_IS_REQUEST(self), NULL); + + /* create an array with all the metadata in */ + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + if (priv->id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_APPSTREAM_ID, + g_variant_new_string(priv->id)); + } + if (priv->created > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CREATED, + g_variant_new_uint64(priv->created)); + } + if (priv->device_id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DEVICE_ID, + g_variant_new_string(priv->device_id)); + } + if (priv->message != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_UPDATE_MESSAGE, + g_variant_new_string(priv->message)); + } + if (priv->image != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_UPDATE_IMAGE, + g_variant_new_string(priv->image)); + } + if (priv->kind != FWUPD_REQUEST_KIND_UNKNOWN) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_REQUEST_KIND, + g_variant_new_uint32(priv->kind)); + } + if (priv->flags != FWUPD_REQUEST_FLAG_NONE) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_FLAGS, + g_variant_new_uint64(priv->flags)); + } + return g_variant_new("a{sv}", &builder); +} + +static void +fwupd_request_from_key_value(FwupdRequest *self, const gchar *key, GVariant *value) +{ + if (g_strcmp0(key, FWUPD_RESULT_KEY_APPSTREAM_ID) == 0) { + fwupd_request_set_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_CREATED) == 0) { + fwupd_request_set_created(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DEVICE_ID) == 0) { + fwupd_request_set_device_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_MESSAGE) == 0) { + fwupd_request_set_message(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_UPDATE_IMAGE) == 0) { + fwupd_request_set_image(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_REQUEST_KIND) == 0) { + fwupd_request_set_kind(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FLAGS) == 0) { + fwupd_request_set_flags(self, g_variant_get_uint64(value)); + return; + } +} + +/** + * fwupd_request_get_message: + * @self: a #FwupdRequest + * + * Gets the update message. + * + * Returns: the update message, or %NULL if unset + * + * Since: 1.6.2 + **/ +const gchar * +fwupd_request_get_message(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), NULL); + return priv->message; +} + +/** + * fwupd_request_set_message: + * @self: a #FwupdRequest + * @message: (nullable): the update message string + * + * Sets the update message. + * + * Since: 1.6.2 + **/ +void +fwupd_request_set_message(FwupdRequest *self, const gchar *message) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + + /* not changed */ + if (g_strcmp0(priv->message, message) == 0) + return; + + g_free(priv->message); + priv->message = g_strdup(message); + g_object_notify(G_OBJECT(self), "message"); +} + +/** + * fwupd_request_get_image: + * @self: a #FwupdRequest + * + * Gets the update image. + * + * Returns: the update image URL, or %NULL if unset + * + * Since: 1.6.2 + **/ +const gchar * +fwupd_request_get_image(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), NULL); + return priv->image; +} + +/** + * fwupd_request_set_image: + * @self: a #FwupdRequest + * @image: (nullable): the update image URL + * + * Sets the update image. + * + * Since: 1.6.2 + **/ +void +fwupd_request_set_image(FwupdRequest *self, const gchar *image) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + + /* not changed */ + if (g_strcmp0(priv->image, image) == 0) + return; + + g_free(priv->image); + priv->image = g_strdup(image); + g_object_notify(G_OBJECT(self), "image"); +} + +/** + * fwupd_request_get_kind: + * @self: a #FwupdRequest + * + * Returns what the request is currently doing. + * + * Returns: the kind value, e.g. %FWUPD_STATUS_REQUEST_WRITE + * + * Since: 1.6.2 + **/ +FwupdRequestKind +fwupd_request_get_kind(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), 0); + return priv->kind; +} + +/** + * fwupd_request_set_kind: + * @self: a #FwupdRequest + * @kind: the kind value, e.g. %FWUPD_STATUS_REQUEST_WRITE + * + * Sets what the request is currently doing. + * + * Since: 1.6.2 + **/ +void +fwupd_request_set_kind(FwupdRequest *self, FwupdRequestKind kind) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + if (priv->kind == kind) + return; + priv->kind = kind; + g_object_notify(G_OBJECT(self), "kind"); +} + +/** + * fwupd_request_get_flags: + * @self: a #FwupdRequest + * + * Gets the request flags. + * + * Returns: request flags, or 0 if unset + * + * Since: 1.8.6 + **/ +FwupdRequestFlags +fwupd_request_get_flags(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), 0); + return priv->flags; +} + +/** + * fwupd_request_set_flags: + * @self: a #FwupdRequest + * @flags: request flags, e.g. %FWUPD_REQUEST_FLAG_NONE + * + * Sets the request flags. + * + * Since: 1.8.6 + **/ +void +fwupd_request_set_flags(FwupdRequest *self, FwupdRequestFlags flags) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + + /* not changed */ + if (priv->flags == flags) + return; + + priv->flags = flags; + g_object_notify(G_OBJECT(self), "flags"); +} + +/** + * fwupd_request_add_flag: + * @self: a #FwupdRequest + * @flag: the #FwupdRequestFlags + * + * Adds a specific flag to the request. + * + * Since: 1.8.6 + **/ +void +fwupd_request_add_flag(FwupdRequest *self, FwupdRequestFlags flag) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + priv->flags |= flag; +} + +/** + * fwupd_request_remove_flag: + * @self: a #FwupdRequest + * @flag: the #FwupdRequestFlags + * + * Removes a specific flag from the request. + * + * Since: 1.8.6 + **/ +void +fwupd_request_remove_flag(FwupdRequest *self, FwupdRequestFlags flag) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_REQUEST(self)); + priv->flags &= ~flag; +} + +/** + * fwupd_request_has_flag: + * @self: a #FwupdRequest + * @flag: the #FwupdRequestFlags + * + * Finds if the request has a specific flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.8.6 + **/ +gboolean +fwupd_request_has_flag(FwupdRequest *self, FwupdRequestFlags flag) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REQUEST(self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_request_to_string: + * @self: a #FwupdRequest + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 1.6.2 + **/ +gchar * +fwupd_request_to_string(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + g_autoptr(GString) str = g_string_new(NULL); + + g_return_val_if_fail(FWUPD_IS_REQUEST(self), NULL); + + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->id); + if (priv->kind != FWUPD_REQUEST_KIND_UNKNOWN) { + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_REQUEST_KIND, + fwupd_request_kind_to_string(priv->kind)); + } + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_FLAGS, fwupd_request_flag_to_string(priv->flags)); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DEVICE_ID, priv->device_id); + fwupd_pad_kv_unx(str, FWUPD_RESULT_KEY_CREATED, priv->created); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->message); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->image); + return g_string_free(g_steal_pointer(&str), FALSE); +} + +static void +fwupd_request_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FwupdRequest *self = FWUPD_REQUEST(object); + FwupdRequestPrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_ID: + g_value_set_string(value, priv->id); + break; + case PROP_MESSAGE: + g_value_set_string(value, priv->message); + break; + case PROP_IMAGE: + g_value_set_string(value, priv->image); + break; + case PROP_DEVICE_ID: + g_value_set_string(value, priv->device_id); + break; + case PROP_KIND: + g_value_set_uint(value, priv->kind); + break; + case PROP_FLAGS: + g_value_set_uint64(value, priv->flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fwupd_request_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FwupdRequest *self = FWUPD_REQUEST(object); + switch (prop_id) { + case PROP_ID: + fwupd_request_set_id(self, g_value_get_string(value)); + break; + case PROP_MESSAGE: + fwupd_request_set_message(self, g_value_get_string(value)); + break; + case PROP_IMAGE: + fwupd_request_set_image(self, g_value_get_string(value)); + break; + case PROP_DEVICE_ID: + fwupd_request_set_device_id(self, g_value_get_string(value)); + break; + case PROP_KIND: + fwupd_request_set_kind(self, g_value_get_uint(value)); + break; + case PROP_FLAGS: + fwupd_request_set_flags(self, g_value_get_uint64(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fwupd_request_finalize(GObject *object) +{ + FwupdRequest *self = FWUPD_REQUEST(object); + FwupdRequestPrivate *priv = GET_PRIVATE(self); + + g_free(priv->id); + g_free(priv->device_id); + g_free(priv->message); + g_free(priv->image); + + G_OBJECT_CLASS(fwupd_request_parent_class)->finalize(object); +} + +static void +fwupd_request_init(FwupdRequest *self) +{ + FwupdRequestPrivate *priv = GET_PRIVATE(self); + priv->created = g_get_real_time() / G_USEC_PER_SEC; +} + +static void +fwupd_request_class_init(FwupdRequestClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->finalize = fwupd_request_finalize; + object_class->get_property = fwupd_request_get_property; + object_class->set_property = fwupd_request_set_property; + + /** + * FwupdRequest:id: + * + * The request identifier. + * + * Since: 1.6.2 + */ + pspec = + g_param_spec_string("id", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_ID, pspec); + + /** + * FwupdRequest:kind: + * + * The kind of the request. + * + * Since: 1.6.2 + */ + pspec = g_param_spec_uint("kind", + NULL, + NULL, + FWUPD_REQUEST_KIND_UNKNOWN, + FWUPD_REQUEST_KIND_LAST, + FWUPD_REQUEST_KIND_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_KIND, pspec); + + /** + * FwupdRequest:flags: + * + * The flags for the request. + * + * Since: 1.8.6 + */ + pspec = g_param_spec_uint64("flags", + NULL, + NULL, + FWUPD_REQUEST_FLAG_NONE, + FWUPD_REQUEST_FLAG_UNKNOWN, + FWUPD_REQUEST_FLAG_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FLAGS, pspec); + + /** + * FwupdRequest:message: + * + * The message text in the request. + * + * Since: 1.6.2 + */ + pspec = g_param_spec_string("message", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_MESSAGE, pspec); + + /** + * FwupdRequest:image: + * + * The image link for the request. + * + * Since: 1.6.2 + */ + pspec = + g_param_spec_string("image", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_IMAGE, pspec); + + /** + * FwupdRequest:device-id: + * + * The device ID for the request. + * + * Since: 1.8.2 + */ + pspec = g_param_spec_string("device-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_DEVICE_ID, pspec); +} + +static void +fwupd_request_set_from_variant_iter(FwupdRequest *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + fwupd_request_from_key_value(self, key, value); + g_variant_unref(value); + } +} + +/** + * fwupd_request_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new request using serialized data. + * + * Returns: (transfer full): a new #FwupdRequest, or %NULL if @value was invalid + * + * Since: 1.6.2 + **/ +FwupdRequest * +fwupd_request_from_variant(GVariant *value) +{ + FwupdRequest *self = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + g_return_val_if_fail(value != NULL, NULL); + + /* format from GetDetails */ + type_string = g_variant_get_type_string(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + self = fwupd_request_new(); + g_variant_get(value, "(a{sv})", &iter); + fwupd_request_set_from_variant_iter(self, iter); + } else if (g_strcmp0(type_string, "a{sv}") == 0) { + self = fwupd_request_new(); + g_variant_get(value, "a{sv}", &iter); + fwupd_request_set_from_variant_iter(self, iter); + } else { + g_warning("type %s not known", type_string); + } + return self; +} + +/** + * fwupd_request_new: + * + * Creates a new request. + * + * Returns: a new #FwupdRequest + * + * Since: 1.6.2 + **/ +FwupdRequest * +fwupd_request_new(void) +{ + FwupdRequest *self; + self = g_object_new(FWUPD_TYPE_REQUEST, NULL); + return FWUPD_REQUEST(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-request.h b/fwupd-1.8.6/libfwupd/fwupd-request.h new file mode 100644 index 0000000000000000000000000000000000000000..eb425dd0956513a78363c433a76492e550372240 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-request.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define FWUPD_TYPE_REQUEST (fwupd_request_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdRequest, fwupd_request, FWUPD, REQUEST, GObject) + +struct _FwupdRequestClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +/** + * FwupdRequestKind: + * @FWUPD_REQUEST_KIND_UNKNOWN: Unknown kind + * @FWUPD_REQUEST_KIND_POST: After the update + * @FWUPD_REQUEST_KIND_IMMEDIATE: Immediately + * + * The kind of request we are asking of the user. + **/ +typedef enum { + FWUPD_REQUEST_KIND_UNKNOWN, /* Since: 1.6.2 */ + FWUPD_REQUEST_KIND_POST, /* Since: 1.6.2 */ + FWUPD_REQUEST_KIND_IMMEDIATE, /* Since: 1.6.2 */ + /*< private >*/ + FWUPD_REQUEST_KIND_LAST +} FwupdRequestKind; + +/** + * FWUPD_REQUEST_ID_REMOVE_REPLUG: + * + * The user needs to remove and reinsert the device to complete the update, e.g. + * "The update will continue when the device USB cable has been unplugged and then re-inserted." + * + * Since 1.6.2 + */ +#define FWUPD_REQUEST_ID_REMOVE_REPLUG "org.freedesktop.fwupd.request.remove-replug" + +/** + * FWUPD_REQUEST_ID_PRESS_UNLOCK: + * + * The user needs to press unlock on the device to continue, e.g. + * "Press unlock on the device to continue the update process." + * + * Since 1.6.2 + */ +#define FWUPD_REQUEST_ID_PRESS_UNLOCK "org.freedesktop.fwupd.request.press-unlock" + +/** + * FWUPD_REQUEST_ID_REMOVE_USB_CABLE: + * + * The user needs to remove the device to complete the update, e.g. + * "The update will continue when the device USB cable has been unplugged." + * + * Since 1.8.6 + */ +#define FWUPD_REQUEST_ID_REMOVE_USB_CABLE "org.freedesktop.fwupd.request.remove-usb-cable" + +/** + * FWUPD_REQUEST_ID_DO_NOT_POWER_OFF: + * + * Show the user a message not to unplug the machine from the AC power, e.g. + * "Do not turn off your computer or remove the AC adaptor until you are sure the update has + * completed." + * + * Since 1.8.6 + */ +#define FWUPD_REQUEST_ID_DO_NOT_POWER_OFF "org.freedesktop.fwupd.request.do-not-power-off" + +/** + * FWUPD_REQUEST_FLAG_NONE: + * + * No flags are set. + * + * Since: 1.8.6 + */ +#define FWUPD_REQUEST_FLAG_NONE (0u) + +/** + * FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE: + * + * Use a generic (translated) request message. + * + * Since: 1.8.6 + */ +#define FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE (1u << 0) + +/** + * FWUPD_REQUEST_FLAG_ALLOW_GENERIC_IMAGE: + * + * Use a generic (translated) request image. + * + * Since: 1.8.6 + */ +#define FWUPD_REQUEST_FLAG_ALLOW_GENERIC_IMAGE (1u << 1) + +/** + * FWUPD_REQUEST_FLAG_UNKNOWN: + * + * The request flag is unknown, typically caused by using mismatched client and daemon. + * + * Since: 1.8.6 + */ +#define FWUPD_REQUEST_FLAG_UNKNOWN G_MAXUINT64 + +/** + * FwupdRequestFlags: + * + * Flags used to represent request attributes + */ +typedef guint64 FwupdRequestFlags; + +const gchar * +fwupd_request_kind_to_string(FwupdRequestKind kind); +FwupdRequestKind +fwupd_request_kind_from_string(const gchar *kind); + +const gchar * +fwupd_request_flag_to_string(FwupdRequestFlags flag); +FwupdRequestFlags +fwupd_request_flag_from_string(const gchar *flag); + +FwupdRequest * +fwupd_request_new(void); +gchar * +fwupd_request_to_string(FwupdRequest *self); + +const gchar * +fwupd_request_get_id(FwupdRequest *self); +void +fwupd_request_set_id(FwupdRequest *self, const gchar *id); +guint64 +fwupd_request_get_created(FwupdRequest *self); +void +fwupd_request_set_created(FwupdRequest *self, guint64 created); +const gchar * +fwupd_request_get_device_id(FwupdRequest *self); +void +fwupd_request_set_device_id(FwupdRequest *self, const gchar *device_id); +const gchar * +fwupd_request_get_message(FwupdRequest *self); +void +fwupd_request_set_message(FwupdRequest *self, const gchar *message); +const gchar * +fwupd_request_get_image(FwupdRequest *self); +void +fwupd_request_set_image(FwupdRequest *self, const gchar *image); +FwupdRequestKind +fwupd_request_get_kind(FwupdRequest *self); +void +fwupd_request_set_kind(FwupdRequest *self, FwupdRequestKind kind); + +FwupdRequestFlags +fwupd_request_get_flags(FwupdRequest *self); +void +fwupd_request_set_flags(FwupdRequest *self, FwupdRequestFlags flags); +void +fwupd_request_add_flag(FwupdRequest *self, FwupdRequestFlags flag); +void +fwupd_request_remove_flag(FwupdRequest *self, FwupdRequestFlags flag); +gboolean +fwupd_request_has_flag(FwupdRequest *self, FwupdRequestFlags flag); + +FwupdRequest * +fwupd_request_from_variant(GVariant *value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-security-attr-private.h b/fwupd-1.8.6/libfwupd/fwupd-security-attr-private.h new file mode 100644 index 0000000000000000000000000000000000000000..f6025beace088b23a8185249f6689c19ec3073a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-security-attr-private.h @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-security-attr.h" + +G_BEGIN_DECLS + +/** + * FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION: + * + * Host Security ID attribute for Pre-boot DMA protection + * + * This was previously known as org.fwupd.hsi.AcpiDmar for Intel from 1.5.0+. + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION "org.fwupd.hsi.PrebootDma" +/** + * FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM: + * + * Host Security ID attribute indicating encrypted RAM available + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM "org.fwupd.hsi.EncryptedRam" +/** + * FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION: + * + * Host Security ID attribute for attesation + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION "org.fwupd.hsi.Fwupd.Attestation" +/** + * FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS: + * + * Host Security ID attribute for plugins + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS "org.fwupd.hsi.Fwupd.Plugins" +/** + * FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES: + * + * Host Security ID attribute for updates + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES "org.fwupd.hsi.Fwupd.Updates" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED: + * + * Host Security ID attribute for Intel Bootguard enabled + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED "org.fwupd.hsi.IntelBootguard.Enabled" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED: + * + * Host Security ID attribute for Intel Bootguard verified + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED "org.fwupd.hsi.IntelBootguard.Verified" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM: + * + * Host Security ID attribute for Intel Bootguard ACM + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM "org.fwupd.hsi.IntelBootguard.Acm" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY: + * + * Host Security ID attribute for Intel Bootguard policy + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY "org.fwupd.hsi.IntelBootguard.Policy" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP: + * + * Host Security ID attribute for Intel Bootguard OTP fuse + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP "org.fwupd.hsi.IntelBootguard.Otp" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED: + * + * Host Security ID attribute for Intel CET enabled + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED "org.fwupd.hsi.IntelCet.Enabled" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE: + * + * Host Security ID attribute for Intel CET active + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE "org.fwupd.hsi.IntelCet.Active" +/** + * FWUPD_SECURITY_ATTR_ID_INTEL_SMAP: + * + * Host Security ID attribute for Intel SMAP + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_INTEL_SMAP "org.fwupd.hsi.IntelSmap" +/** + * FWUPD_SECURITY_ATTR_ID_IOMMU: + * + * Host Security ID attribute for IOMMU + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_IOMMU "org.fwupd.hsi.Iommu" +/** + * FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN: + * + * Host Security ID attribute for kernel lockdown + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN "org.fwupd.hsi.Kernel.Lockdown" +/** + * FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP: + * + * Host Security ID attribute for kernel swap + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP "org.fwupd.hsi.Kernel.Swap" +/** + * FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED: + * + * Host Security ID attribute for kernel taint + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED "org.fwupd.hsi.Kernel.Tainted" +/** + * FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE: + * + * Host Security ID attribute for Intel ME manufacturing mode + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE "org.fwupd.hsi.Mei.ManufacturingMode" +/** + * FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP: + * + * Host Security ID attribute for Intel ME override strap + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP "org.fwupd.hsi.Mei.OverrideStrap" +/** + * FWUPD_SECURITY_ATTR_ID_MEI_VERSION: + * + * Host Security ID attribute for Intel ME version + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_MEI_VERSION "org.fwupd.hsi.Mei.Version" +/** + * FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE: + * + * Host Security ID attribute for Intel SPI BIOSWE configuration + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE "org.fwupd.hsi.Spi.Bioswe" +/** + * FWUPD_SECURITY_ATTR_ID_SPI_BLE: + * + * Host Security ID attribute for Intel SPI BLE configuration + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SPI_BLE "org.fwupd.hsi.Spi.Ble" +/** + * FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP: + * + * Host Security ID attribute for Intel SPI SMM BWP + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP "org.fwupd.hsi.Spi.SmmBwp" +/** + * FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR: + * + * Host Security ID attribute for Intel SPI descriptor + * + * Since: 1.6.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR "org.fwupd.hsi.Spi.Descriptor" +/** + * FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE: + * + * Host Security ID attribute for Suspend to Idle + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE "org.fwupd.hsi.SuspendToIdle" +/** + * FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM: + * + * Host Security ID attribute for Suspend to RAM + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM "org.fwupd.hsi.SuspendToRam" +/** + * FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR: + * + * Host Security ID attribute for empty PCR + * + * Since: 1.7.2 + **/ +#define FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR "org.fwupd.hsi.Tpm.EmptyPcr" +/** + * FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0: + * + * Host Security ID attribute for TPM PCR0 reconstruction + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0 "org.fwupd.hsi.Tpm.ReconstructionPcr0" +/** + * FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20: + * + * Host Security ID attribute for TPM 2.0 + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20 "org.fwupd.hsi.Tpm.Version20" +/** + * FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT: + * + * Host Security ID attribute for UEFI secure boot + * + * Since: 1.5.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT "org.fwupd.hsi.Uefi.SecureBoot" +/** + * FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED: + * + * Host Security ID attribute for parts with debugging capabilities enabled + * + * This was previously known as org.fwupd.hsi.PlatformDebugEnabled for Intel 1.5.0+ + * It was renamed for all vendor support in 1.8.0. * + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED "org.fwupd.hsi.PlatformDebugEnabled" +/** + * FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED: + * + * Host Security ID attribute for fused parts + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED "org.fwupd.hsi.PlatformFused" +/** + * FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED: + * + * Host Security ID attribute for parts locked from debugging + * + * This was previously known as org.fwupd.hsi.IntelDci.Locked for Intel 1.5.0+ + * It was renamed for all vendor support in 1.8.0. + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED "org.fwupd.hsi.PlatformDebugLocked" +/** + * FWUPD_SECURITY_ATTR_ID_UEFI_PK: + * + * Host Security ID attribute for UEFI PK + * + * Since: 1.5.5 + **/ +#define FWUPD_SECURITY_ATTR_ID_UEFI_PK "org.fwupd.hsi.Uefi.Pk" +/** + * FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU + * + * Host Security ID attribute for Supported CPU + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU "org.fwupd.hsi.SupportedCpu" +/** + * FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION + * + * Host Security ID attribute for Rollback protection of AMD platform + * firmware + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION "org.fwupd.hsi.Amd.RollbackProtection" +/** + * FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION + * + * Host Security ID attribute for SPI Write protection + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION "org.fwupd.hsi.Amd.SpiWriteProtection" +/** + * FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION + * + * Host Security ID attribute for SPI replay protection + * + * Since: 1.8.0 + **/ +#define FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION "org.fwupd.hsi.Amd.SpiReplayProtection" +/** + * FWUPD_SECURITY_ATTR_ID_HOST_EMULATION + * + * Host Security ID attribute for host emulation + * + * Since: 1.8.3 + **/ +#define FWUPD_SECURITY_ATTR_ID_HOST_EMULATION "org.fwupd.hsi.HostEmulation" + +GVariant * +fwupd_security_attr_to_variant(FwupdSecurityAttr *self); +void +fwupd_security_attr_to_json(FwupdSecurityAttr *self, JsonBuilder *builder); +gboolean +fwupd_security_attr_from_json(FwupdSecurityAttr *self, JsonNode *json_node, GError **error); +FwupdSecurityAttr * +fwupd_security_attr_copy(FwupdSecurityAttr *self); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-security-attr.c b/fwupd-1.8.6/libfwupd/fwupd-security-attr.c new file mode 100644 index 0000000000000000000000000000000000000000..52c235223cc47c06293a43276b0657574036a1cb --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-security-attr.c @@ -0,0 +1,1721 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" +#include "fwupd-security-attr-private.h" + +/** + * FwupdSecurityAttr: + * + * A Host Security ID attribute that represents something that was measured. + */ + +static void +fwupd_security_attr_finalize(GObject *object); + +typedef struct { + gchar *appstream_id; + GPtrArray *obsoletes; + GPtrArray *guids; + GHashTable *metadata; /* (nullable) */ + gchar *name; + gchar *title; + gchar *description; + gchar *plugin; + gchar *url; + guint64 created; + FwupdSecurityAttrLevel level; + FwupdSecurityAttrResult result; + FwupdSecurityAttrResult result_fallback; + FwupdSecurityAttrFlags flags; + gchar *bios_setting_id; + gchar *bios_setting_target_value; + gchar *bios_setting_current_value; +} FwupdSecurityAttrPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FwupdSecurityAttr, fwupd_security_attr, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fwupd_security_attr_get_instance_private(o)) + +/** + * fwupd_security_attr_flag_to_string: + * @flag: security attribute flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_SUCCESS + * + * Returns the printable string for the flag. + * + * Returns: string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_flag_to_string(FwupdSecurityAttrFlags flag) +{ + if (flag == FWUPD_SECURITY_ATTR_FLAG_NONE) + return "none"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_SUCCESS) + return "success"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) + return "obsoleted"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA) + return "missing-data"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES) + return "runtime-updates"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION) + return "runtime-attestation"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) + return "runtime-issue"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM) + return "action-contact-oem"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW) + return "action-config-fw"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS) + return "action-config-os"; + return NULL; +} + +/** + * fwupd_security_attr_flag_from_string: + * @flag: (nullable): a string, e.g. `success` + * + * Converts a string to an enumerated flag. + * + * Returns: enumerated value + * + * Since: 1.7.1 + **/ +FwupdSecurityAttrFlags +fwupd_security_attr_flag_from_string(const gchar *flag) +{ + if (g_strcmp0(flag, "success") == 0) + return FWUPD_SECURITY_ATTR_FLAG_SUCCESS; + if (g_strcmp0(flag, "obsoleted") == 0) + return FWUPD_SECURITY_ATTR_FLAG_OBSOLETED; + if (g_strcmp0(flag, "missing-data") == 0) + return FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA; + if (g_strcmp0(flag, "runtime-updates") == 0) + return FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES; + if (g_strcmp0(flag, "runtime-attestation") == 0) + return FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION; + if (g_strcmp0(flag, "runtime-issue") == 0) + return FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE; + if (g_strcmp0(flag, "action-contact-oem") == 0) + return FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM; + if (g_strcmp0(flag, "action-config-fw") == 0) + return FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW; + if (g_strcmp0(flag, "action-config-os") == 0) + return FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS; + return FWUPD_SECURITY_ATTR_FLAG_NONE; +} + +/** + * fwupd_security_attr_result_to_string: + * @result: security attribute result, e.g. %FWUPD_SECURITY_ATTR_RESULT_ENABLED + * + * Returns the printable string for the result enum. + * + * Returns: string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_result_to_string(FwupdSecurityAttrResult result) +{ + if (result == FWUPD_SECURITY_ATTR_RESULT_VALID) + return "valid"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) + return "not-valid"; + if (result == FWUPD_SECURITY_ATTR_RESULT_ENABLED) + return "enabled"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED) + return "not-enabled"; + if (result == FWUPD_SECURITY_ATTR_RESULT_LOCKED) + return "locked"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED) + return "not-locked"; + if (result == FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED) + return "encrypted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED) + return "not-encrypted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_TAINTED) + return "tainted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED) + return "not-tainted"; + if (result == FWUPD_SECURITY_ATTR_RESULT_FOUND) + return "found"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND) + return "not-found"; + if (result == FWUPD_SECURITY_ATTR_RESULT_SUPPORTED) + return "supported"; + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED) + return "not-supported"; + return NULL; +} + +/** + * fwupd_security_attr_result_from_string: + * @result: (nullable): a string, e.g. `not-encrypted` + * + * Converts a string to an enumerated result. + * + * Returns: enumerated value + * + * Since: 1.7.1 + **/ +FwupdSecurityAttrResult +fwupd_security_attr_result_from_string(const gchar *result) +{ + if (g_strcmp0(result, "valid") == 0) + return FWUPD_SECURITY_ATTR_RESULT_VALID; + if (g_strcmp0(result, "not-valid") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_VALID; + if (g_strcmp0(result, "enabled") == 0) + return FWUPD_SECURITY_ATTR_RESULT_ENABLED; + if (g_strcmp0(result, "not-enabled") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED; + if (g_strcmp0(result, "locked") == 0) + return FWUPD_SECURITY_ATTR_RESULT_LOCKED; + if (g_strcmp0(result, "not-locked") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED; + if (g_strcmp0(result, "encrypted") == 0) + return FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED; + if (g_strcmp0(result, "not-encrypted") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED; + if (g_strcmp0(result, "tainted") == 0) + return FWUPD_SECURITY_ATTR_RESULT_TAINTED; + if (g_strcmp0(result, "not-tainted") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED; + if (g_strcmp0(result, "found") == 0) + return FWUPD_SECURITY_ATTR_RESULT_FOUND; + if (g_strcmp0(result, "not-found") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND; + if (g_strcmp0(result, "supported") == 0) + return FWUPD_SECURITY_ATTR_RESULT_SUPPORTED; + if (g_strcmp0(result, "not-supported") == 0) + return FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED; + return FWUPD_SECURITY_ATTR_RESULT_UNKNOWN; +} + +/** + * fwupd_security_attr_flag_to_suffix: + * @flag: security attribute flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES + * + * Returns the string suffix for the flag. + * + * Returns: string, or %NULL + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_flag_to_suffix(FwupdSecurityAttrFlags flag) +{ + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES) + return "U"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION) + return "A"; + if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) + return "!"; + return NULL; +} + +/** + * fwupd_security_attr_get_bios_setting_id: + * @self: a #FwupdSecurityAttr + * + * Gets the #FwupdBiosSetting that can be used to improve this + * #FwupdSecurityAttr. + * + * Returns: The unique ID used for #FwupdBiosSetting or NULL + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_security_attr_get_bios_setting_id(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->bios_setting_id; +} + +/** + * fwupd_security_attr_set_bios_setting_id: + * @self: a #FwupdSecurityAttr + * @id: Unique identifier used for #FwupdBiosSetting + * + * Sets the #FwupdBiosSetting that can be used to improve this + * #FwupdSecurityAttr. + * + * Since: 1.8.4 + **/ +void +fwupd_security_attr_set_bios_setting_id(FwupdSecurityAttr *self, const gchar *id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + if (priv->bios_setting_id == id) + return; + g_free(priv->bios_setting_id); + priv->bios_setting_id = g_strdup(id); +} + +/** + * fwupd_security_attr_get_obsoletes: + * @self: a #FwupdSecurityAttr + * + * Gets the list of attribute obsoletes. The obsoleted attributes will not + * contribute to the calculated HSI value or be visible in command line tools. + * + * Returns: (element-type utf8) (transfer none): the obsoletes, which may be empty + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_security_attr_get_obsoletes(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->obsoletes; +} + +/** + * fwupd_security_attr_add_obsolete: + * @self: a #FwupdSecurityAttr + * @appstream_id: the appstream_id or plugin name + * + * Adds an attribute appstream_id to obsolete. The obsoleted attribute will not + * contribute to the calculated HSI value or be visible in command line tools. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_add_obsolete(FwupdSecurityAttr *self, const gchar *appstream_id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + g_return_if_fail(appstream_id != NULL); + if (fwupd_security_attr_has_obsolete(self, appstream_id)) + return; + g_ptr_array_add(priv->obsoletes, g_strdup(appstream_id)); +} + +/** + * fwupd_security_attr_has_obsolete: + * @self: a #FwupdSecurityAttr + * @appstream_id: the attribute appstream_id + * + * Finds out if the attribute obsoletes a specific appstream_id. + * + * Returns: %TRUE if the self matches + * + * Since: 1.5.0 + **/ +gboolean +fwupd_security_attr_has_obsolete(FwupdSecurityAttr *self, const gchar *appstream_id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE); + g_return_val_if_fail(appstream_id != NULL, FALSE); + for (guint i = 0; i < priv->obsoletes->len; i++) { + const gchar *obsolete_tmp = g_ptr_array_index(priv->obsoletes, i); + if (g_strcmp0(obsolete_tmp, appstream_id) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_security_attr_get_guids: + * @self: a #FwupdSecurityAttr + * + * Gets the list of attribute GUIDs. The GUID values will not modify the calculated HSI value. + * + * Returns: (element-type utf8) (transfer none): the GUIDs, which may be empty + * + * Since: 1.7.0 + **/ +GPtrArray * +fwupd_security_attr_get_guids(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->guids; +} + +/** + * fwupd_security_attr_add_guid: + * @self: a #FwupdSecurityAttr + * @guid: the GUID + * + * Adds a device GUID to the attribute. This indicates the GUID in some way contributed to the + * result decided. + * + * Since: 1.7.0 + **/ +void +fwupd_security_attr_add_guid(FwupdSecurityAttr *self, const gchar *guid) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + g_return_if_fail(fwupd_guid_is_valid(guid)); + if (fwupd_security_attr_has_guid(self, guid)) + return; + g_ptr_array_add(priv->guids, g_strdup(guid)); +} + +/** + * fwupd_security_attr_add_guids: + * @self: a #FwupdSecurityAttr + * @guids: (element-type utf8): the GUIDs + * + * Adds device GUIDs to the attribute. This indicates the GUIDs in some way contributed to the + * result decided. + * + * Since: 1.7.0 + **/ +void +fwupd_security_attr_add_guids(FwupdSecurityAttr *self, GPtrArray *guids) +{ + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + g_return_if_fail(guids != NULL); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + fwupd_security_attr_add_guid(self, guid); + } +} + +/** + * fwupd_security_attr_has_guid: + * @self: a #FwupdSecurityAttr + * @guid: the attribute guid + * + * Finds out if a specific GUID was added to the attribute. + * + * Returns: %TRUE if the self matches + * + * Since: 1.7.0 + **/ +gboolean +fwupd_security_attr_has_guid(FwupdSecurityAttr *self, const gchar *guid) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE); + g_return_val_if_fail(guid != NULL, FALSE); + for (guint i = 0; i < priv->guids->len; i++) { + const gchar *guid_tmp = g_ptr_array_index(priv->guids, i); + if (g_strcmp0(guid_tmp, guid) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fwupd_security_attr_get_appstream_id: + * @self: a #FwupdSecurityAttr + * + * Gets the AppStream ID. + * + * Returns: the AppStream ID, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_appstream_id(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->appstream_id; +} + +/** + * fwupd_security_attr_set_appstream_id: + * @self: a #FwupdSecurityAttr + * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Sets the AppStream ID. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_appstream_id(FwupdSecurityAttr *self, const gchar *appstream_id) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* not changed */ + if (g_strcmp0(priv->appstream_id, appstream_id) == 0) + return; + + /* sanity check */ + if (!g_str_has_prefix(appstream_id, "org.fwupd.hsi.")) + g_critical("HSI attributes need to have a 'org.fwupd.hsi.' prefix"); + + g_free(priv->appstream_id); + priv->appstream_id = g_strdup(appstream_id); +} + +/** + * fwupd_security_attr_get_url: + * @self: a #FwupdSecurityAttr + * + * Gets the attribute URL. + * + * Returns: the attribute result, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_url(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->url; +} + +/** + * fwupd_security_attr_set_name: + * @self: a #FwupdSecurityAttr + * @name: (nullable): the attribute name + * + * Sets the attribute name. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_name(FwupdSecurityAttr *self, const gchar *name) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* not changed */ + if (g_strcmp0(priv->name, name) == 0) + return; + + g_free(priv->name); + priv->name = g_strdup(name); +} + +/** + * fwupd_security_attr_get_bios_setting_target_value: + * @self: a #FwupdSecurityAttr + * + * Gets the value that when written to an attribute would activate it or satisfy + * a security requirement. + * + * Returns: the target value of the attribute. + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_security_attr_get_bios_setting_target_value(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->bios_setting_target_value; +} + +/** + * fwupd_security_attr_set_bios_setting_target_value: + * @self: a #FwupdSecurityAttr + * @value: The string to set target value to + * + * Sets the string used for the target value of an attribute. + * + * Since: 1.8.4 + **/ +void +fwupd_security_attr_set_bios_setting_target_value(FwupdSecurityAttr *self, const gchar *value) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->bios_setting_target_value, value) == 0) + return; + + g_free(priv->bios_setting_target_value); + priv->bios_setting_target_value = g_strdup(value); +} + +/** + * fwupd_security_attr_get_bios_setting_current_value: + * @self: a #FwupdSecurityAttr + * + * Gets the current value of the BIOS setting that can be changed. + * + * Returns: the current value of the attribute. + * + * Since: 1.8.4 + **/ +const gchar * +fwupd_security_attr_get_bios_setting_current_value(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->bios_setting_current_value; +} + +/** + * fwupd_security_attr_set_bios_setting_current_value: + * @self: a #FwupdSecurityAttr + * @value: The string to set current value to + * + * Sets the current value of the BIOS setting that can be changed. + * + * Since: 1.8.4 + **/ +void +fwupd_security_attr_set_bios_setting_current_value(FwupdSecurityAttr *self, const gchar *value) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->bios_setting_current_value, value) == 0) + return; + + g_free(priv->bios_setting_current_value); + priv->bios_setting_current_value = g_strdup(value); +} + +/** + * fwupd_security_attr_set_title: + * @self: a #FwupdSecurityAttr + * @title: (nullable): the attribute title + * + * Sets the attribute title. + * + * Since: 1.8.2 + **/ +void +fwupd_security_attr_set_title(FwupdSecurityAttr *self, const gchar *title) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* not changed */ + if (g_strcmp0(priv->title, title) == 0) + return; + + g_free(priv->title); + priv->title = g_strdup(title); +} + +/** + * fwupd_security_attr_set_description: + * @self: a #FwupdSecurityAttr + * @description: (nullable): the attribute description + * + * Sets the attribute description. + * + * Since: 1.8.2 + **/ +void +fwupd_security_attr_set_description(FwupdSecurityAttr *self, const gchar *description) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* not changed */ + if (g_strcmp0(priv->description, description) == 0) + return; + + g_free(priv->description); + priv->description = g_strdup(description); +} + +/** + * fwupd_security_attr_set_plugin: + * @self: a #FwupdSecurityAttr + * @plugin: (nullable): the plugin name + * + * Sets the plugin that created the attribute. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_plugin(FwupdSecurityAttr *self, const gchar *plugin) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* not changed */ + if (g_strcmp0(priv->plugin, plugin) == 0) + return; + + g_free(priv->plugin); + priv->plugin = g_strdup(plugin); +} + +/** + * fwupd_security_attr_set_url: + * @self: a #FwupdSecurityAttr + * @url: (nullable): the attribute URL + * + * Sets the attribute result. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_url(FwupdSecurityAttr *self, const gchar *url) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* not changed */ + if (g_strcmp0(priv->url, url) == 0) + return; + + g_free(priv->url); + priv->url = g_strdup(url); +} +/** + * fwupd_security_attr_get_created: + * @self: a #FwupdSecurityAttr + * + * Gets when the attribute was created. + * + * Returns: the UNIX time, or 0 if unset + * + * Since: 1.7.1 + **/ +guint64 +fwupd_security_attr_get_created(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0); + return priv->created; +} + +/** + * fwupd_security_attr_set_created: + * @self: a #FwupdSecurityAttr + * @created: the UNIX time + * + * Sets when the attribute was created. + * + * Since: 1.7.1 + **/ +void +fwupd_security_attr_set_created(FwupdSecurityAttr *self, guint64 created) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + priv->created = created; +} + +/** + * fwupd_security_attr_get_name: + * @self: a #FwupdSecurityAttr + * + * Gets the attribute name. + * + * Returns: the attribute name, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_name(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->name; +} + +/** + * fwupd_security_attr_get_title: + * @self: a #FwupdSecurityAttr + * + * Gets the attribute title, which is typically a two word title. + * + * The fwupd client program may be able to get translations for this value using a method call + * like `dgettext("fwupd",str)`. + * + * Returns: the attribute title, or %NULL if unset + * + * Since: 1.8.2 + **/ +const gchar * +fwupd_security_attr_get_title(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->title; +} + +/** + * fwupd_security_attr_get_description: + * @self: a #FwupdSecurityAttr + * + * Gets the attribute description which is a few lines of prose that normal users will understand. + * + * The fwupd client program may be able to get translations for this value using a method call + * like `dgettext("fwupd",str)`. + * + * NOTE: The returned string may contain placeholders such as `$HostVendor$` or `$HostProduct$` + * and these should be replaced with the values from [method@FwupdClient.get_host_vendor] and + * [method@FwupdClient.get_host_product]. + * + * Returns: the attribute description, or %NULL if unset + * + * Since: 1.8.2 + **/ +const gchar * +fwupd_security_attr_get_description(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->description; +} + +/** + * fwupd_security_attr_get_plugin: + * @self: a #FwupdSecurityAttr + * + * Gets the plugin that created the attribute. + * + * Returns: the plugin name, or %NULL if unset + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_plugin(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + return priv->plugin; +} + +/** + * fwupd_security_attr_get_flags: + * @self: a #FwupdSecurityAttr + * + * Gets the self flags. + * + * Returns: security attribute flags, or 0 if unset + * + * Since: 1.5.0 + **/ +FwupdSecurityAttrFlags +fwupd_security_attr_get_flags(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0); + return priv->flags; +} + +/** + * fwupd_security_attr_set_flags: + * @self: a #FwupdSecurityAttr + * @flags: security attribute flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED + * + * Sets the attribute flags. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_flags(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flags) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + priv->flags = flags; +} + +/** + * fwupd_security_attr_add_flag: + * @self: a #FwupdSecurityAttr + * @flag: the #FwupdSecurityAttrFlags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED + * + * Adds a specific attribute flag to the attribute. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_add_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + priv->flags |= flag; +} + +/** + * fwupd_security_attr_remove_flag: + * @self: a #FwupdSecurityAttr + * @flag: the #FwupdSecurityAttrFlags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED + * + * Removes a specific attribute flag from the attribute. + * + * Since: 1.8.3 + **/ +void +fwupd_security_attr_remove_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + priv->flags &= ~flag; +} + +/** + * fwupd_security_attr_has_flag: + * @self: a #FwupdSecurityAttr + * @flag: the attribute flag, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED + * + * Finds if the attribute has a specific attribute flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.5.0 + **/ +gboolean +fwupd_security_attr_has_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fwupd_security_attr_get_level: + * @self: a #FwupdSecurityAttr + * + * Gets the HSI level. + * + * Returns: the security attribute level, or %FWUPD_SECURITY_ATTR_LEVEL_NONE if unset + * + * Since: 1.5.0 + **/ +FwupdSecurityAttrLevel +fwupd_security_attr_get_level(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0); + return priv->level; +} + +/** + * fwupd_security_attr_set_level: + * @self: a #FwupdSecurityAttr + * @level: a security attribute level, e.g. %FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT + * + * Sets the HSI level. A @level of %FWUPD_SECURITY_ATTR_LEVEL_NONE is not used + * for the HSI calculation. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_level(FwupdSecurityAttr *self, FwupdSecurityAttrLevel level) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + priv->level = level; +} + +/** + * fwupd_security_attr_set_result: + * @self: a #FwupdSecurityAttr + * @result: a security attribute result, e.g. %FWUPD_SECURITY_ATTR_LEVEL_LOCKED + * + * Sets the optional HSI result. This is required because some attributes may + * be a "success" when something is `locked` or may be "failed" if `found`. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_set_result(FwupdSecurityAttr *self, FwupdSecurityAttrResult result) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + + /* fixup any legacy attributes to the 'modern' value */ + if (g_strcmp0(priv->appstream_id, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM) == 0 && + result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED) { + result = FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED; + } + + priv->result = result; +} + +/** + * fwupd_security_attr_get_result: + * @self: a #FwupdSecurityAttr + * + * Gets the optional HSI result. + * + * Returns: the #FwupdSecurityAttrResult, e.g %FWUPD_SECURITY_ATTR_LEVEL_LOCKED + * + * Since: 1.5.0 + **/ +FwupdSecurityAttrResult +fwupd_security_attr_get_result(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0); + return priv->result; +} + +/** + * fwupd_security_attr_set_result_fallback: + * @self: a #FwupdSecurityAttr + * @result: a security attribute, e.g. %FWUPD_SECURITY_ATTR_LEVEL_LOCKED + * + * Sets the optional fallback HSI result. The fallback may represent the old state, or a state + * that may be considered equivalent. + * + * Since: 1.7.1 + **/ +void +fwupd_security_attr_set_result_fallback(FwupdSecurityAttr *self, FwupdSecurityAttrResult result) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + priv->result_fallback = result; +} + +/** + * fwupd_security_attr_get_result_fallback: + * @self: a #FwupdSecurityAttr + * + * Gets the optional fallback HSI result. + * + * Returns: the #FwupdSecurityAttrResult, e.g %FWUPD_SECURITY_ATTR_LEVEL_LOCKED + * + * Since: 1.7.1 + **/ +FwupdSecurityAttrResult +fwupd_security_attr_get_result_fallback(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0); + return priv->result_fallback; +} + +/** + * fwupd_security_attr_to_variant: + * @self: a #FwupdSecurityAttr + * + * Serialize the security attribute. + * + * Returns: the serialized data, or %NULL for error + * + * Since: 1.5.0 + **/ +GVariant * +fwupd_security_attr_to_variant(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + GVariantBuilder builder; + + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + if (priv->appstream_id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_APPSTREAM_ID, + g_variant_new_string(priv->appstream_id)); + } + if (priv->created > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CREATED, + g_variant_new_uint64(priv->created)); + } + if (priv->name != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_NAME, + g_variant_new_string(priv->name)); + } + if (priv->title != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_SUMMARY, + g_variant_new_string(priv->title)); + } + if (priv->description != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_DESCRIPTION, + g_variant_new_string(priv->description)); + } + if (priv->plugin != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_PLUGIN, + g_variant_new_string(priv->plugin)); + } + if (priv->url != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_URI, + g_variant_new_string(priv->url)); + } + if (priv->obsoletes->len > 0) { + g_autofree const gchar **strv = g_new0(const gchar *, priv->obsoletes->len + 1); + for (guint i = 0; i < priv->obsoletes->len; i++) + strv[i] = (const gchar *)g_ptr_array_index(priv->obsoletes, i); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_CATEGORIES, + g_variant_new_strv(strv, -1)); + } + if (priv->guids->len > 0) { + g_autofree const gchar **strv = g_new0(const gchar *, priv->guids->len + 1); + for (guint i = 0; i < priv->guids->len; i++) + strv[i] = (const gchar *)g_ptr_array_index(priv->guids, i); + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_GUID, + g_variant_new_strv(strv, -1)); + } + if (priv->flags != 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_FLAGS, + g_variant_new_uint64(priv->flags)); + } + if (priv->level > 0) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_HSI_LEVEL, + g_variant_new_uint32(priv->level)); + } + if (priv->result != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_HSI_RESULT, + g_variant_new_uint32(priv->result)); + } + if (priv->result_fallback != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK, + g_variant_new_uint32(priv->result_fallback)); + } + if (priv->metadata != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_METADATA, + fwupd_hash_kv_to_variant(priv->metadata)); + } + if (priv->bios_setting_id != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_ID, + g_variant_new_string(priv->bios_setting_id)); + } + if (priv->bios_setting_target_value != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE, + g_variant_new_string(priv->bios_setting_target_value)); + } + if (priv->bios_setting_current_value != NULL) { + g_variant_builder_add(&builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + g_variant_new_string(priv->bios_setting_current_value)); + } + return g_variant_new("a{sv}", &builder); +} + +/** + * fwupd_security_attr_get_metadata: + * @self: a #FwupdSecurityAttr + * @key: metadata key + * + * Gets private metadata from the attribute which may be used in the name. + * + * Returns: (nullable): the metadata value, or %NULL if unfound + * + * Since: 1.5.0 + **/ +const gchar * +fwupd_security_attr_get_metadata(FwupdSecurityAttr *self, const gchar *key) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + g_return_val_if_fail(key != NULL, NULL); + + if (priv->metadata == NULL) + return NULL; + return g_hash_table_lookup(priv->metadata, key); +} + +/** + * fwupd_security_attr_add_metadata: + * @self: a #FwupdSecurityAttr + * @key: metadata key + * @value: (nullable): metadata value + * + * Adds metadata to the attribute which may be used in the name. + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_add_metadata(FwupdSecurityAttr *self, const gchar *key, const gchar *value) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + g_return_if_fail(key != NULL); + + if (priv->metadata == NULL) { + priv->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + } + g_hash_table_insert(priv->metadata, g_strdup(key), g_strdup(value)); +} + +static void +fwupd_security_attr_from_key_value(FwupdSecurityAttr *self, const gchar *key, GVariant *value) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + if (g_strcmp0(key, FWUPD_RESULT_KEY_APPSTREAM_ID) == 0) { + fwupd_security_attr_set_appstream_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_CREATED) == 0) { + fwupd_security_attr_set_created(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME) == 0) { + fwupd_security_attr_set_name(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_SUMMARY) == 0) { + fwupd_security_attr_set_title(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { + fwupd_security_attr_set_description(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_PLUGIN) == 0) { + fwupd_security_attr_set_plugin(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_URI) == 0) { + fwupd_security_attr_set_url(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_FLAGS) == 0) { + fwupd_security_attr_set_flags(self, g_variant_get_uint64(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_LEVEL) == 0) { + fwupd_security_attr_set_level(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_RESULT) == 0) { + fwupd_security_attr_set_result(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK) == 0) { + fwupd_security_attr_set_result_fallback(self, g_variant_get_uint32(value)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_GUID) == 0) { + g_autofree const gchar **strv = g_variant_get_strv(value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_security_attr_add_guid(self, strv[i]); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_METADATA) == 0) { + if (priv->metadata != NULL) + g_hash_table_unref(priv->metadata); + priv->metadata = fwupd_variant_to_hash_kv(value); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_ID) == 0) { + fwupd_security_attr_set_bios_setting_id(self, g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE) == 0) { + fwupd_security_attr_set_bios_setting_target_value( + self, + g_variant_get_string(value, NULL)); + return; + } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE) == 0) { + fwupd_security_attr_set_bios_setting_current_value( + self, + g_variant_get_string(value, NULL)); + return; + } +} + +static void +fwupd_pad_kv_tfl(GString *str, const gchar *key, FwupdSecurityAttrFlags security_attr_flags) +{ + g_autoptr(GString) tmp = g_string_new(""); + for (guint i = 0; i < 64; i++) { + if ((security_attr_flags & ((guint64)1 << i)) == 0) + continue; + g_string_append_printf(tmp, + "%s|", + fwupd_security_attr_flag_to_string((guint64)1 << i)); + } + if (tmp->len == 0) { + g_string_append(tmp, fwupd_security_attr_flag_to_string(0)); + } else { + g_string_truncate(tmp, tmp->len - 1); + } + fwupd_pad_kv_str(str, key, tmp->str); +} + +/** + * fwupd_security_attr_from_json: + * @self: a #FwupdSecurityAttr + * @json_node: a JSON node + * @error: (nullable): optional return location for an error + * + * Loads a fwupd security attribute from a JSON node. + * + * Returns: %TRUE for success + * + * Since: 1.7.1 + **/ +gboolean +fwupd_security_attr_from_json(FwupdSecurityAttr *self, JsonNode *json_node, GError **error) +{ +#if JSON_CHECK_VERSION(1, 6, 0) + JsonObject *obj; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + obj = json_node_get_object(json_node); + + /* this has to exist */ + if (!json_object_has_member(obj, FWUPD_RESULT_KEY_APPSTREAM_ID)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no %s property in object", + FWUPD_RESULT_KEY_APPSTREAM_ID); + return FALSE; + } + + /* all optional */ + fwupd_security_attr_set_appstream_id( + self, + json_object_get_string_member(obj, FWUPD_RESULT_KEY_APPSTREAM_ID)); + fwupd_security_attr_set_name( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_NAME, NULL)); + fwupd_security_attr_set_title( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_SUMMARY, NULL)); + fwupd_security_attr_set_description( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_DESCRIPTION, NULL)); + fwupd_security_attr_set_plugin( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_PLUGIN, NULL)); + fwupd_security_attr_set_url( + self, + json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_URI, NULL)); + fwupd_security_attr_set_level( + self, + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_HSI_LEVEL, 0)); + fwupd_security_attr_set_created( + self, + json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_CREATED, 0)); + fwupd_security_attr_set_bios_setting_id( + self, + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_ID, + NULL)); + fwupd_security_attr_set_bios_setting_target_value( + self, + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE, + NULL)); + fwupd_security_attr_set_bios_setting_current_value( + self, + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + NULL)); + + /* also optional */ + if (json_object_has_member(obj, FWUPD_RESULT_KEY_HSI_RESULT)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_HSI_RESULT, + NULL); + fwupd_security_attr_set_result(self, fwupd_security_attr_result_from_string(tmp)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK)) { + const gchar *tmp = + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK, + NULL); + fwupd_security_attr_set_result_fallback( + self, + fwupd_security_attr_result_from_string(tmp)); + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_FLAGS)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_FLAGS); + for (guint i = 0; i < json_array_get_length(array); i++) { + const gchar *tmp = json_array_get_string_element(array, i); + FwupdSecurityAttrFlags flag = fwupd_security_attr_flag_from_string(tmp); + if (flag != FWUPD_SECURITY_ATTR_FLAG_NONE) + fwupd_security_attr_add_flag(self, flag); + } + } + if (json_object_has_member(obj, FWUPD_RESULT_KEY_GUID)) { + JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_GUID); + for (guint i = 0; i < json_array_get_length(array); i++) { + const gchar *tmp = json_array_get_string_element(array, i); + fwupd_security_attr_add_guid(self, tmp); + } + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "json-glib version too old"); + return FALSE; +#endif +} + +/** + * fwupd_security_attr_to_json: + * @self: a #FwupdSecurityAttr + * @builder: a JSON builder + * + * Adds a fwupd security attribute to a JSON builder + * + * Since: 1.5.0 + **/ +void +fwupd_security_attr_to_json(FwupdSecurityAttr *self, JsonBuilder *builder) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self)); + g_return_if_fail(builder != NULL); + + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + if (priv->created > 0) + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_CREATED, priv->created); + fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_HSI_RESULT, + fwupd_security_attr_result_to_string(priv->result)); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK, + fwupd_security_attr_result_to_string(priv->result_fallback)); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_SUMMARY, priv->title); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_URI, priv->url); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE, + priv->bios_setting_target_value); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + priv->bios_setting_current_value); + fwupd_common_json_add_string(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_ID, + priv->bios_setting_id); + + if (priv->flags != FWUPD_SECURITY_ATTR_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_security_attr_flag_to_string((guint64)1 << i); + json_builder_add_string_value(builder, tmp); + } + json_builder_end_array(builder); + } + 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); + } + if (priv->metadata != NULL) { + g_autoptr(GList) 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_common_json_add_string(builder, key, value); + } + } +} + +/** + * fwupd_security_attr_to_string: + * @self: a #FwupdSecurityAttr + * + * Builds a text representation of the object. + * + * Returns: text, or %NULL for invalid + * + * Since: 1.5.0 + **/ +gchar * +fwupd_security_attr_to_string(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + GString *str; + + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + + str = g_string_new(""); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); + if (priv->created > 0) + fwupd_pad_kv_unx(str, FWUPD_RESULT_KEY_CREATED, priv->created); + fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level); + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_HSI_RESULT, + fwupd_security_attr_result_to_string(priv->result)); + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK, + fwupd_security_attr_result_to_string(priv->result_fallback)); + if (priv->flags != FWUPD_SECURITY_ATTR_FLAG_NONE) + fwupd_pad_kv_tfl(str, FWUPD_RESULT_KEY_FLAGS, priv->flags); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_NAME, priv->name); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_SUMMARY, priv->title); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_PLUGIN, priv->plugin); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_URI, priv->url); + fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_BIOS_SETTING_ID, priv->bios_setting_id); + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE, + priv->bios_setting_target_value); + fwupd_pad_kv_str(str, + FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE, + priv->bios_setting_current_value); + + for (guint i = 0; i < priv->obsoletes->len; i++) { + const gchar *appstream_id = g_ptr_array_index(priv->obsoletes, i); + fwupd_pad_kv_str(str, "Obsolete", appstream_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); + } + if (priv->metadata != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys(priv->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(priv->metadata, key); + fwupd_pad_kv_str(str, key, value); + } + } + + return g_string_free(str, FALSE); +} + +static void +fwupd_security_attr_class_init(FwupdSecurityAttrClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fwupd_security_attr_finalize; +} + +static void +fwupd_security_attr_init(FwupdSecurityAttr *self) +{ + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + priv->level = FWUPD_SECURITY_ATTR_LEVEL_NONE; + priv->obsoletes = g_ptr_array_new_with_free_func(g_free); + priv->guids = g_ptr_array_new_with_free_func(g_free); + priv->created = (guint64)g_get_real_time() / G_USEC_PER_SEC; +} + +static void +fwupd_security_attr_finalize(GObject *object) +{ + FwupdSecurityAttr *self = FWUPD_SECURITY_ATTR(object); + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + if (priv->metadata != NULL) + g_hash_table_unref(priv->metadata); + g_free(priv->bios_setting_id); + g_free(priv->bios_setting_target_value); + g_free(priv->bios_setting_current_value); + g_free(priv->appstream_id); + g_free(priv->name); + g_free(priv->title); + g_free(priv->description); + g_free(priv->plugin); + g_free(priv->url); + g_ptr_array_unref(priv->obsoletes); + g_ptr_array_unref(priv->guids); + + G_OBJECT_CLASS(fwupd_security_attr_parent_class)->finalize(object); +} + +static void +fwupd_security_attr_set_from_variant_iter(FwupdSecurityAttr *self, GVariantIter *iter) +{ + GVariant *value; + const gchar *key; + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + fwupd_security_attr_from_key_value(self, key, value); + g_variant_unref(value); + } +} + +/** + * fwupd_security_attr_from_variant: + * @value: (not nullable): the serialized data + * + * Creates a new security attribute using serialized data. + * + * Returns: (transfer full): a new #FwupdSecurityAttr, or %NULL if @value was invalid + * + * Since: 1.5.0 + **/ +FwupdSecurityAttr * +fwupd_security_attr_from_variant(GVariant *value) +{ + FwupdSecurityAttr *rel = NULL; + const gchar *type_string; + g_autoptr(GVariantIter) iter = NULL; + + g_return_val_if_fail(value != NULL, NULL); + + type_string = g_variant_get_type_string(value); + if (g_strcmp0(type_string, "(a{sv})") == 0) { + rel = fwupd_security_attr_new(NULL); + g_variant_get(value, "(a{sv})", &iter); + fwupd_security_attr_set_from_variant_iter(rel, iter); + } else if (g_strcmp0(type_string, "a{sv}") == 0) { + rel = fwupd_security_attr_new(NULL); + g_variant_get(value, "a{sv}", &iter); + fwupd_security_attr_set_from_variant_iter(rel, iter); + } else { + g_warning("type %s not known", type_string); + } + return rel; +} + +/** + * fwupd_security_attr_array_from_variant: + * @value: (not nullable): the serialized data + * + * Creates an array of new security attributes using serialized data. + * + * Returns: (transfer container) (element-type FwupdSecurityAttr): attributes, or %NULL if @value + *was invalid + * + * Since: 1.5.0 + **/ +GPtrArray * +fwupd_security_attr_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++) { + FwupdSecurityAttr *rel; + g_autoptr(GVariant) data = NULL; + data = g_variant_get_child_value(untuple, i); + rel = fwupd_security_attr_from_variant(data); + if (rel == NULL) + continue; + g_ptr_array_add(array, rel); + } + return array; +} + +/** + * fwupd_security_attr_copy: + * @self: (nullable): a #FwupdSecurityAttr + * + * Makes a full (deep) copy of a security attribute. + * + * Returns: (transfer full): a new #FwupdSecurityAttr + * + * Since: 1.7.1 + **/ +FwupdSecurityAttr * +fwupd_security_attr_copy(FwupdSecurityAttr *self) +{ + g_autoptr(FwupdSecurityAttr) new = g_object_new(FWUPD_TYPE_SECURITY_ATTR, NULL); + FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL); + + fwupd_security_attr_set_appstream_id(new, priv->appstream_id); + fwupd_security_attr_set_name(new, priv->name); + fwupd_security_attr_set_title(new, priv->title); + fwupd_security_attr_set_description(new, priv->description); + fwupd_security_attr_set_plugin(new, priv->plugin); + fwupd_security_attr_set_url(new, priv->url); + fwupd_security_attr_set_level(new, priv->level); + fwupd_security_attr_set_flags(new, priv->flags); + fwupd_security_attr_set_result(new, priv->result); + fwupd_security_attr_set_created(new, priv->created); + fwupd_security_attr_set_bios_setting_id(new, priv->bios_setting_id); + + for (guint i = 0; i < priv->guids->len; i++) { + const gchar *guid = g_ptr_array_index(priv->guids, i); + fwupd_security_attr_add_guid(new, guid); + } + for (guint i = 0; i < priv->obsoletes->len; i++) { + const gchar *obsolete = g_ptr_array_index(priv->obsoletes, i); + fwupd_security_attr_add_obsolete(new, obsolete); + } + if (priv->metadata != NULL) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, priv->metadata); + while (g_hash_table_iter_next(&iter, &key, &value)) { + fwupd_security_attr_add_metadata(new, + (const gchar *)key, + (const gchar *)value); + } + } + return g_steal_pointer(&new); +} + +/** + * fwupd_security_attr_new: + * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Creates a new security attribute. + * + * Plugins should not use this method, and should instead use `fu_plugin_security_attr_new()` or + * `fu_security_attr_new()`. + * + * Returns: a new #FwupdSecurityAttr + * + * Since: 1.5.0 + **/ +FwupdSecurityAttr * +fwupd_security_attr_new(const gchar *appstream_id) +{ + FwupdSecurityAttr *self; + self = g_object_new(FWUPD_TYPE_SECURITY_ATTR, NULL); + if (appstream_id != NULL) + fwupd_security_attr_set_appstream_id(self, appstream_id); + return FWUPD_SECURITY_ATTR(self); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-security-attr.h b/fwupd-1.8.6/libfwupd/fwupd-security-attr.h new file mode 100644 index 0000000000000000000000000000000000000000..4ada93a41a0f8d521e5c5db7ef506cd40861ae56 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-security-attr.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-enums.h" + +G_BEGIN_DECLS + +#define FWUPD_TYPE_SECURITY_ATTR (fwupd_security_attr_get_type()) +G_DECLARE_DERIVABLE_TYPE(FwupdSecurityAttr, fwupd_security_attr, FWUPD, SECURITY_ATTR, GObject) + +struct _FwupdSecurityAttrClass { + GObjectClass parent_class; + /*< private >*/ + void (*_fwupd_reserved1)(void); + void (*_fwupd_reserved2)(void); + void (*_fwupd_reserved3)(void); + void (*_fwupd_reserved4)(void); + void (*_fwupd_reserved5)(void); + void (*_fwupd_reserved6)(void); + void (*_fwupd_reserved7)(void); +}; + +/** + * FwupdSecurityAttrFlags: + * @FWUPD_SECURITY_ATTR_FLAG_NONE: No flags set + * @FWUPD_SECURITY_ATTR_FLAG_SUCCESS: Success + * @FWUPD_SECURITY_ATTR_FLAG_OBSOLETED: Obsoleted by another attribute + * @FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA: Missing data + * @FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES: Suffix `U` + * @FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION: Suffix `A` + * @FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE: Suffix `!` + * @FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM: Contact the firmware vendor for a update + * @FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW: Failure may be fixed by changing FW config + * @FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS: Failure may be fixed by changing OS config + * + * The flags available for HSI attributes. + **/ +typedef enum { + FWUPD_SECURITY_ATTR_FLAG_NONE = 0, + FWUPD_SECURITY_ATTR_FLAG_SUCCESS = 1 << 0, + FWUPD_SECURITY_ATTR_FLAG_OBSOLETED = 1 << 1, + FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA = 1 << 2, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES = 1 << 8, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION = 1 << 9, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE = 1 << 10, + FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM = 1 << 11, + FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW = 1 << 12, + FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS = 1 << 13, +} FwupdSecurityAttrFlags; + +/** + * FwupdSecurityAttrLevel: + * @FWUPD_SECURITY_ATTR_LEVEL_NONE: Very few detected firmware protections + * @FWUPD_SECURITY_ATTR_LEVEL_CRITICAL: The most basic of security protections + * @FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT: Firmware security issues considered + *important + * @FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL: Firmware security issues that pose a + *theoretical concern + * @FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION: Out-of-band protection of the system + *firmware + * @FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_ATTESTATION: Out-of-band attestation of the system + *firmware + * + * The HSI level. + **/ +typedef enum { + FWUPD_SECURITY_ATTR_LEVEL_NONE = 0, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_CRITICAL = 1, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT = 2, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL = 3, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION = 4, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_ATTESTATION = 5, /* Since: 1.5.0 */ + /*< private >*/ + FWUPD_SECURITY_ATTR_LEVEL_LAST = 6 /* perhaps increased in the future */ +} FwupdSecurityAttrLevel; + +/** + * FwupdSecurityAttrResult: + * @FWUPD_SECURITY_ATTR_RESULT_UNKNOWN: Not known + * @FWUPD_SECURITY_ATTR_RESULT_ENABLED: Enabled + * @FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED: Not enabled + * @FWUPD_SECURITY_ATTR_RESULT_VALID: Valid + * @FWUPD_SECURITY_ATTR_RESULT_NOT_VALID: Not valid + * @FWUPD_SECURITY_ATTR_RESULT_LOCKED: Locked + * @FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED: Not locked + * @FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED: Encrypted + * @FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED: Not encrypted + * @FWUPD_SECURITY_ATTR_RESULT_TAINTED: Tainted + * @FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED: Not tainted + * @FWUPD_SECURITY_ATTR_RESULT_FOUND: Found + * @FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND: NOt found + * @FWUPD_SECURITY_ATTR_RESULT_SUPPORTED: Supported + * @FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED: Not supported + * + * The HSI result. + **/ +typedef enum { + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_ENABLED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_VALID, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_LOCKED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_TAINTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_FOUND, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_SUPPORTED, /* Since: 1.5.0 */ + FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED, /* Since: 1.5.0 */ + /*< private >*/ + FWUPD_SECURITY_ATTR_RESULT_LAST +} FwupdSecurityAttrResult; + +FwupdSecurityAttr * +fwupd_security_attr_new(const gchar *appstream_id); +gchar * +fwupd_security_attr_to_string(FwupdSecurityAttr *self); + +const gchar * +fwupd_security_attr_get_bios_setting_id(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_bios_setting_id(FwupdSecurityAttr *self, const gchar *id); +const gchar * +fwupd_security_attr_get_bios_setting_target_value(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_bios_setting_target_value(FwupdSecurityAttr *self, const gchar *value); +const gchar * +fwupd_security_attr_get_bios_setting_current_value(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_bios_setting_current_value(FwupdSecurityAttr *self, const gchar *value); + +const gchar * +fwupd_security_attr_get_appstream_id(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_appstream_id(FwupdSecurityAttr *self, const gchar *appstream_id); +FwupdSecurityAttrLevel +fwupd_security_attr_get_level(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_level(FwupdSecurityAttr *self, FwupdSecurityAttrLevel level); +FwupdSecurityAttrResult +fwupd_security_attr_get_result(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_result(FwupdSecurityAttr *self, FwupdSecurityAttrResult result); +FwupdSecurityAttrResult +fwupd_security_attr_get_result_fallback(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_result_fallback(FwupdSecurityAttr *self, FwupdSecurityAttrResult result); +const gchar * +fwupd_security_attr_get_name(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_name(FwupdSecurityAttr *self, const gchar *name); +const gchar * +fwupd_security_attr_get_title(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_title(FwupdSecurityAttr *self, const gchar *title); +const gchar * +fwupd_security_attr_get_description(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_description(FwupdSecurityAttr *self, const gchar *description); +const gchar * +fwupd_security_attr_get_plugin(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_plugin(FwupdSecurityAttr *self, const gchar *plugin); +const gchar * +fwupd_security_attr_get_url(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_url(FwupdSecurityAttr *self, const gchar *url); +guint64 +fwupd_security_attr_get_created(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_created(FwupdSecurityAttr *self, guint64 created); +GPtrArray * +fwupd_security_attr_get_obsoletes(FwupdSecurityAttr *self); +void +fwupd_security_attr_add_obsolete(FwupdSecurityAttr *self, const gchar *appstream_id); +gboolean +fwupd_security_attr_has_obsolete(FwupdSecurityAttr *self, const gchar *appstream_id); +GPtrArray * +fwupd_security_attr_get_guids(FwupdSecurityAttr *self); +void +fwupd_security_attr_add_guid(FwupdSecurityAttr *self, const gchar *guid); +void +fwupd_security_attr_add_guids(FwupdSecurityAttr *self, GPtrArray *guids); +gboolean +fwupd_security_attr_has_guid(FwupdSecurityAttr *self, const gchar *guid); +const gchar * +fwupd_security_attr_get_metadata(FwupdSecurityAttr *self, const gchar *key); +void +fwupd_security_attr_add_metadata(FwupdSecurityAttr *self, const gchar *key, const gchar *value); +FwupdSecurityAttrFlags +fwupd_security_attr_get_flags(FwupdSecurityAttr *self); +void +fwupd_security_attr_set_flags(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flags); +void +fwupd_security_attr_add_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag); +void +fwupd_security_attr_remove_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag); +gboolean +fwupd_security_attr_has_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag); +const gchar * +fwupd_security_attr_flag_to_string(FwupdSecurityAttrFlags flag); +FwupdSecurityAttrFlags +fwupd_security_attr_flag_from_string(const gchar *flag); +const gchar * +fwupd_security_attr_flag_to_suffix(FwupdSecurityAttrFlags flag); +const gchar * +fwupd_security_attr_result_to_string(FwupdSecurityAttrResult result); +FwupdSecurityAttrResult +fwupd_security_attr_result_from_string(const gchar *result); + +FwupdSecurityAttr * +fwupd_security_attr_from_variant(GVariant *value); +GPtrArray * +fwupd_security_attr_array_from_variant(GVariant *value); + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupd/fwupd-self-test.c b/fwupd-1.8.6/libfwupd/fwupd-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..a7d5fc3f2b95e5ebd2fc0390024571d4f0bc4410 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-self-test.c @@ -0,0 +1,1556 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#ifdef HAVE_FNMATCH_H +#include +#endif + +#include "fwupd-bios-setting-private.h" +#include "fwupd-client-sync.h" +#include "fwupd-client.h" +#include "fwupd-common.h" +#include "fwupd-device-private.h" +#include "fwupd-enums.h" +#include "fwupd-error.h" +#include "fwupd-plugin-private.h" +#include "fwupd-release-private.h" +#include "fwupd-remote-private.h" +#include "fwupd-request-private.h" +#include "fwupd-security-attr-private.h" + +static gboolean +fu_test_compare_lines(const gchar *txt1, const gchar *txt2, GError **error) +{ + g_autofree gchar *output = NULL; + + /* exactly the same */ + if (g_strcmp0(txt1, txt2) == 0) + return TRUE; + + /* matches a pattern */ +#ifdef HAVE_FNMATCH_H + if (fnmatch(txt2, txt1, FNM_NOESCAPE) == 0) + return TRUE; +#else + if (g_strcmp0(txt1, txt2) == 0) + return TRUE; +#endif + + /* save temp files and diff them */ + if (!g_file_set_contents("/tmp/a", txt1, -1, error)) + return FALSE; + if (!g_file_set_contents("/tmp/b", txt2, -1, error)) + return FALSE; + if (!g_spawn_command_line_sync("diff -urNp /tmp/b /tmp/a", &output, NULL, NULL, error)) + return FALSE; + + /* just output the diff */ + g_set_error_literal(error, 1, 0, output); + 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) +{ + /* enums */ + for (guint i = 0; i < FWUPD_ERROR_LAST; i++) { + const gchar *tmp = fwupd_error_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_error_from_string(tmp), ==, i); + } + for (guint i = 0; i < FWUPD_STATUS_LAST; i++) { + const gchar *tmp = fwupd_status_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_status_from_string(tmp), ==, i); + } + for (guint i = 0; i < FWUPD_UPDATE_STATE_LAST; i++) { + const gchar *tmp = fwupd_update_state_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_update_state_from_string(tmp), ==, i); + } + for (guint i = 0; i < FWUPD_TRUST_FLAG_LAST; i++) { + const gchar *tmp = fwupd_trust_flag_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_trust_flag_from_string(tmp), ==, i); + } + for (guint i = 0; i < FWUPD_REQUEST_KIND_LAST; i++) { + const gchar *tmp = fwupd_request_kind_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_request_kind_from_string(tmp), ==, i); + } + for (guint i = FWUPD_RELEASE_URGENCY_UNKNOWN + 1; i < FWUPD_RELEASE_URGENCY_LAST; i++) { + const gchar *tmp = fwupd_release_urgency_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_release_urgency_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); + } + for (guint i = 1; i < FWUPD_REMOTE_KIND_LAST; i++) { + const gchar *tmp = fwupd_remote_kind_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_remote_kind_from_string(tmp), ==, i); + } + + /* bitfield */ + for (guint64 i = 1; i <= FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD; i *= 2) { + const gchar *tmp = fwupd_device_flag_to_string(i); + if (tmp == NULL) + g_warning("missing device flag 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_device_flag_from_string(tmp), ==, i); + } + for (guint64 i = 1; i <= FWUPD_DEVICE_PROBLEM_IS_EMULATED; i *= 2) { + const gchar *tmp = fwupd_device_problem_to_string(i); + if (tmp == NULL) + g_warning("missing device problem 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_device_problem_from_string(tmp), ==, i); + } + for (guint64 i = 1; i <= FWUPD_PLUGIN_FLAG_MODULAR; i *= 2) { + const gchar *tmp = fwupd_plugin_flag_to_string(i); + if (tmp == NULL) + g_warning("missing plugin flag 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_plugin_flag_from_string(tmp), ==, i); + } + for (guint64 i = 1; i <= FWUPD_FEATURE_FLAG_SHOW_PROBLEMS; i *= 2) { + const gchar *tmp = fwupd_feature_flag_to_string(i); + if (tmp == NULL) + g_warning("missing feature flag 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_feature_flag_from_string(tmp), ==, i); + } + for (guint64 i = 1; i <= FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION; i *= 2) { + const gchar *tmp = fwupd_feature_flag_to_string(i); + if (tmp == NULL) + g_warning("missing feature flag 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_feature_flag_from_string(tmp), ==, i); + } + for (guint64 i = 1; i <= FWUPD_RELEASE_FLAG_IS_COMMUNITY; i *= 2) { + const gchar *tmp = fwupd_release_flag_to_string(i); + if (tmp == NULL) + g_warning("missing release flag 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_release_flag_from_string(tmp), ==, i); + } + for (guint64 i = 1; i <= FWUPD_REQUEST_FLAG_NONE; i *= 2) { + const gchar *tmp = fwupd_request_flag_to_string(i); + if (tmp == NULL) + g_warning("missing request flag 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_request_flag_from_string(tmp), ==, i); + } + for (guint i = 1; i < FWUPD_KEYRING_KIND_LAST; i++) { + const gchar *tmp = fwupd_keyring_kind_to_string(i); + if (tmp == NULL) + g_warning("missing keyring kind 0x%x", (guint)i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_keyring_kind_from_string(tmp), ==, i); + } +} + +static void +fwupd_remote_download_func(void) +{ + gboolean ret; + g_autofree gchar *fn = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *expected_metadata = NULL; + g_autofree gchar *expected_signature = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GError) error = NULL; + + remote = fwupd_remote_new(); + directory = g_build_filename(FWUPD_LOCALSTATEDIR, "lib", "fwupd", "remotes.d", NULL); + expected_metadata = g_build_filename(FWUPD_LOCALSTATEDIR, + "lib", + "fwupd", + "remotes.d", + "lvfs-testing", + "metadata.xml.gz", + NULL); + expected_signature = g_strdup_printf("%s.jcat", expected_metadata); + fwupd_remote_set_remotes_dir(remote, directory); + fn = g_test_build_filename(G_TEST_DIST, "tests", "remotes.d", "lvfs-testing.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fwupd_remote_setup(remote, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fwupd_remote_get_kind(remote), ==, FWUPD_REMOTE_KIND_DOWNLOAD); + g_assert_cmpint(fwupd_remote_get_keyring_kind(remote), ==, FWUPD_KEYRING_KIND_JCAT); + g_assert_cmpint(fwupd_remote_get_priority(remote), ==, 0); + g_assert_false(fwupd_remote_get_enabled(remote)); + g_assert_nonnull(fwupd_remote_get_metadata_uri(remote)); + g_assert_nonnull(fwupd_remote_get_metadata_uri_sig(remote)); + g_assert_cmpstr(fwupd_remote_get_title(remote), + ==, + "Linux Vendor Firmware Service (testing)"); + g_assert_cmpstr(fwupd_remote_get_report_uri(remote), + ==, + "https://fwupd.org/lvfs/firmware/report"); + g_assert_cmpstr(fwupd_remote_get_filename_cache(remote), ==, expected_metadata); + g_assert_cmpstr(fwupd_remote_get_filename_cache_sig(remote), ==, expected_signature); +} + +/* verify we used the FirmwareBaseURI just for firmware */ +static void +fwupd_remote_baseuri_func(void) +{ + gboolean ret; + g_autofree gchar *firmware_uri = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autofree gchar *directory = NULL; + g_autoptr(GError) error = NULL; + + remote = fwupd_remote_new(); + directory = g_build_filename(FWUPD_LOCALSTATEDIR, "lib", "fwupd", "remotes.d", NULL); + fwupd_remote_set_remotes_dir(remote, directory); + fn = g_test_build_filename(G_TEST_DIST, "tests", "firmware-base-uri.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fwupd_remote_get_kind(remote), ==, FWUPD_REMOTE_KIND_DOWNLOAD); + g_assert_cmpint(fwupd_remote_get_keyring_kind(remote), ==, FWUPD_KEYRING_KIND_JCAT); + g_assert_cmpint(fwupd_remote_get_priority(remote), ==, 0); + g_assert_true(fwupd_remote_get_enabled(remote)); + g_assert_cmpstr(fwupd_remote_get_firmware_base_uri(remote), ==, "https://my.fancy.cdn/"); + g_assert_cmpstr(fwupd_remote_get_agreement(remote), ==, NULL); + g_assert_cmpstr(fwupd_remote_get_checksum(remote), ==, NULL); + g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote), + ==, + "https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz"); + g_assert_cmpstr(fwupd_remote_get_metadata_uri_sig(remote), + ==, + "https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz.jcat"); + firmware_uri = + fwupd_remote_build_firmware_uri(remote, "http://bbc.co.uk/firmware.cab", &error); + g_assert_no_error(error); + g_assert_cmpstr(firmware_uri, ==, "https://my.fancy.cdn/firmware.cab"); +} + +static gchar * +fwupd_remote_to_json_string(FwupdRemote *remote, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(JsonNode) json_root = NULL; + json_builder_begin_object(builder); + fwupd_remote_to_json(remote, 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); + if (data == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert remote to json."); + return NULL; + } + return g_steal_pointer(&data); +} + +static void +fwupd_remote_auth_func(void) +{ + gboolean ret; + gchar **order; + g_autofree gchar *fn = NULL; + g_autofree gchar *remotes_dir = NULL; + g_autofree gchar *json = NULL; + g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autoptr(FwupdRemote) remote2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) data = NULL; + + remotes_dir = g_test_build_filename(G_TEST_BUILT, "tests", NULL); + fwupd_remote_set_remotes_dir(remote, remotes_dir); + + fn = g_test_build_filename(G_TEST_DIST, "tests", "auth.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpstr(fwupd_remote_get_username(remote), ==, "user"); + g_assert_cmpstr(fwupd_remote_get_password(remote), ==, "pass"); + g_assert_cmpstr(fwupd_remote_get_security_report_uri(remote), + ==, + "https://fwupd.org/lvfs/hsireports/upload"); + g_assert_false(fwupd_remote_get_approval_required(remote)); + g_assert_false(fwupd_remote_get_automatic_reports(remote)); + g_assert_true(fwupd_remote_get_automatic_security_reports(remote)); + + g_assert_true( + g_str_has_suffix(fwupd_remote_get_filename_source(remote), "tests/auth.conf")); + g_assert_true(g_str_has_suffix(fwupd_remote_get_remotes_dir(remote), "/libfwupd/tests")); + g_assert_cmpint(fwupd_remote_get_age(remote), >, 1000000); + + ret = fwupd_remote_setup(remote, &error); + g_assert_no_error(error); + g_assert_true(ret); + + order = fwupd_remote_get_order_before(remote); + g_assert_nonnull(order); + g_assert_cmpint(g_strv_length(order), ==, 1); + g_assert_cmpstr(order[0], ==, "before"); + order = fwupd_remote_get_order_after(remote); + g_assert_nonnull(order); + g_assert_cmpint(g_strv_length(order), ==, 1); + g_assert_cmpstr(order[0], ==, "after"); + + /* to/from GVariant */ + fwupd_remote_set_priority(remote, 999); + data = fwupd_remote_to_variant(remote); + remote2 = fwupd_remote_from_variant(data); + g_assert_cmpstr(fwupd_remote_get_username(remote2), ==, "user"); + g_assert_cmpint(fwupd_remote_get_priority(remote2), ==, 999); + + /* jcat-tool is not a hard dep, and the tests create an empty file if unfound */ + ret = fwupd_remote_load_signature(remote, + fwupd_remote_get_filename_cache_sig(remote), + &error); + if (!ret) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT)) { + g_test_skip("no jcat-tool, so skipping test"); + return; + } + } + g_assert_no_error(error); + g_assert_true(ret); + + /* to JSON */ + fwupd_remote_set_filename_source(remote2, NULL); + fwupd_remote_set_checksum( + remote2, + "dd1b4fd2a59bb0e4d9ea760c658ac3cf9336c7b6729357bab443485b5cf071b2"); + fwupd_remote_set_filename_cache(remote2, "./libfwupd/tests/auth/metadata.xml.gz"); + json = fwupd_remote_to_json_string(remote2, &error); + g_assert_no_error(error); + g_assert_nonnull(json); + ret = fu_test_compare_lines( + json, + "{\n" + " \"Id\" : \"auth\",\n" + " \"Kind\" : \"download\",\n" + " \"KeyringKind\" : \"jcat\",\n" + " \"FirmwareBaseUri\" : \"https://my.fancy.cdn/\",\n" + " \"ReportUri\" : \"https://fwupd.org/lvfs/firmware/report\",\n" + " \"SecurityReportUri\" : \"https://fwupd.org/lvfs/hsireports/upload\",\n" + " \"MetadataUri\" : \"https://cdn.fwupd.org/downloads/firmware.xml.gz\",\n" + " \"MetadataUriSig\" : \"https://cdn.fwupd.org/downloads/firmware.xml.gz.jcat\",\n" + " \"Username\" : \"user\",\n" + " \"Password\" : \"pass\",\n" + " \"Checksum\" : " + "\"dd1b4fd2a59bb0e4d9ea760c658ac3cf9336c7b6729357bab443485b5cf071b2\",\n" + " \"FilenameCache\" : \"./libfwupd/tests/auth/metadata.xml.gz\",\n" + " \"FilenameCacheSig\" : \"./libfwupd/tests/auth/metadata.xml.gz.jcat\",\n" + " \"Enabled\" : \"true\",\n" + " \"ApprovalRequired\" : \"false\",\n" + " \"AutomaticReports\" : \"false\",\n" + " \"AutomaticSecurityReports\" : \"true\",\n" + " \"Priority\" : 999,\n" + " \"Mtime\" : 0\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fwupd_remote_duplicate_func(void) +{ + gboolean ret; + g_autofree gchar *fn2 = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "stable.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + fn2 = g_test_build_filename(G_TEST_DIST, "tests", "disabled.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn2, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fwupd_remote_setup(remote, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fwupd_remote_setup(remote, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_false(fwupd_remote_get_enabled(remote)); + g_assert_cmpint(fwupd_remote_get_keyring_kind(remote), ==, FWUPD_KEYRING_KIND_NONE); + g_assert_cmpstr(fwupd_remote_get_username(remote), ==, NULL); + g_assert_cmpstr(fwupd_remote_get_password(remote), ==, ""); + g_assert_cmpstr(fwupd_remote_get_filename_cache(remote), + ==, + "/tmp/fwupd-self-test/stable.xml"); +} + +/* verify we used the metadata path for firmware */ +static void +fwupd_remote_nopath_func(void) +{ + gboolean ret; + g_autofree gchar *firmware_uri = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *directory = NULL; + + remote = fwupd_remote_new(); + directory = g_build_filename(FWUPD_LOCALSTATEDIR, "lib", "fwupd", "remotes.d", NULL); + fwupd_remote_set_remotes_dir(remote, directory); + fn = g_test_build_filename(G_TEST_DIST, "tests", "firmware-nopath.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fwupd_remote_get_kind(remote), ==, FWUPD_REMOTE_KIND_DOWNLOAD); + g_assert_cmpint(fwupd_remote_get_keyring_kind(remote), ==, FWUPD_KEYRING_KIND_JCAT); + g_assert_cmpint(fwupd_remote_get_priority(remote), ==, 0); + g_assert_true(fwupd_remote_get_enabled(remote)); + g_assert_cmpstr(fwupd_remote_get_checksum(remote), ==, NULL); + g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote), + ==, + "https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz"); + g_assert_cmpstr(fwupd_remote_get_metadata_uri_sig(remote), + ==, + "https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz.jcat"); + firmware_uri = fwupd_remote_build_firmware_uri(remote, "firmware.cab", &error); + g_assert_no_error(error); + g_assert_cmpstr(firmware_uri, + ==, + "https://s3.amazonaws.com/lvfsbucket/downloads/firmware.cab"); +} + +static void +fwupd_remote_local_func(void) +{ + gboolean ret; + g_autofree gchar *fn = NULL; + g_autofree gchar *json = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(FwupdRemote) remote2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) data = NULL; + + remote = fwupd_remote_new(); + fn = g_test_build_filename(G_TEST_DIST, "tests", "dell-esrt.conf", NULL); + ret = fwupd_remote_load_from_filename(remote, fn, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fwupd_remote_get_kind(remote), ==, FWUPD_REMOTE_KIND_LOCAL); + g_assert_cmpint(fwupd_remote_get_keyring_kind(remote), ==, FWUPD_KEYRING_KIND_NONE); + g_assert_true(fwupd_remote_get_enabled(remote)); + g_assert_null(fwupd_remote_get_metadata_uri(remote)); + g_assert_null(fwupd_remote_get_metadata_uri_sig(remote)); + g_assert_null(fwupd_remote_get_report_uri(remote)); + 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); + + /* to/from GVariant */ + data = fwupd_remote_to_variant(remote); + remote2 = fwupd_remote_from_variant(data); + g_assert_null(fwupd_remote_get_metadata_uri(remote)); + + /* to JSON */ + fwupd_remote_set_filename_source(remote2, NULL); + json = fwupd_remote_to_json_string(remote2, &error); + g_assert_no_error(error); + g_assert_nonnull(json); + ret = fu_test_compare_lines( + json, + "{\n" + " \"Id\" : \"dell-esrt\",\n" + " \"Kind\" : \"local\",\n" + " \"KeyringKind\" : \"none\",\n" + " \"Title\" : \"Enable UEFI capsule updates on Dell systems\",\n" + " \"FilenameCache\" : \"@datadir@/fwupd/remotes.d/dell-esrt/metadata.xml\",\n" + " \"Enabled\" : \"true\",\n" + " \"ApprovalRequired\" : \"false\",\n" + " \"AutomaticReports\" : \"false\",\n" + " \"AutomaticSecurityReports\" : \"false\",\n" + " \"Priority\" : 0,\n" + " \"Mtime\" : 0\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static gchar * +fwupd_release_to_json_string(FwupdRelease *release, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(JsonNode) json_root = NULL; + json_builder_begin_object(builder); + fwupd_release_to_json(release, 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); + if (data == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert release to json."); + return NULL; + } + return g_steal_pointer(&data); +} + +static void +fwupd_release_func(void) +{ + gboolean ret; + g_autofree gchar *json1 = NULL; + g_autofree gchar *json2 = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FwupdRelease) release1 = NULL; + g_autoptr(FwupdRelease) release2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) data = NULL; + + release1 = fwupd_release_new(); + fwupd_release_add_metadata_item(release1, "foo", "bar"); + fwupd_release_add_metadata_item(release1, "baz", "bam"); + fwupd_release_set_remote_id(release1, "remote-id"); + fwupd_release_set_appstream_id(release1, "appstream-id"); + fwupd_release_set_id(release1, "id"); + fwupd_release_set_detach_caption(release1, "detach_caption"); + fwupd_release_set_detach_image(release1, "detach_image"); + fwupd_release_set_update_message(release1, "update_message"); + fwupd_release_set_update_image(release1, "update_image"); + fwupd_release_set_filename(release1, "filename"); + fwupd_release_set_protocol(release1, "protocol"); + fwupd_release_set_license(release1, "license"); + fwupd_release_set_name(release1, "name"); + fwupd_release_set_name_variant_suffix(release1, "name_variant_suffix"); + fwupd_release_set_summary(release1, "summary"); + fwupd_release_set_branch(release1, "branch"); + fwupd_release_set_description(release1, "description"); + fwupd_release_set_homepage(release1, "homepage"); + fwupd_release_set_details_url(release1, "details_url"); + fwupd_release_set_source_url(release1, "source_url"); + fwupd_release_set_version(release1, "version"); + fwupd_release_set_vendor(release1, "vendor"); + fwupd_release_set_size(release1, 1234); + fwupd_release_set_created(release1, 5678); + fwupd_release_set_install_duration(release1, 2468); + fwupd_release_add_category(release1, "category"); + fwupd_release_add_category(release1, "category"); + fwupd_release_add_issue(release1, "issue"); + fwupd_release_add_issue(release1, "issue"); + fwupd_release_add_location(release1, "location"); + fwupd_release_add_location(release1, "location"); + fwupd_release_add_tag(release1, "tag"); + fwupd_release_add_tag(release1, "tag"); + fwupd_release_add_checksum(release1, "checksum"); + fwupd_release_add_checksum(release1, "checksum"); + fwupd_release_add_flag(release1, FWUPD_RELEASE_FLAG_IS_UPGRADE); + fwupd_release_add_flag(release1, FWUPD_RELEASE_FLAG_IS_UPGRADE); + fwupd_release_add_flag(release1, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + fwupd_release_remove_flag(release1, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + fwupd_release_set_urgency(release1, FWUPD_RELEASE_URGENCY_MEDIUM); + data = fwupd_release_to_variant(release1); + release2 = fwupd_release_from_variant(data); + g_assert_cmpstr(fwupd_release_get_metadata_item(release2, "foo"), ==, "bar"); + g_assert_cmpstr(fwupd_release_get_metadata_item(release2, "baz"), ==, "bam"); + g_assert_cmpstr(fwupd_release_get_remote_id(release2), ==, "remote-id"); + g_assert_cmpstr(fwupd_release_get_appstream_id(release2), ==, "appstream-id"); + g_assert_cmpstr(fwupd_release_get_id(release2), ==, "id"); + g_assert_cmpstr(fwupd_release_get_detach_caption(release2), ==, "detach_caption"); + g_assert_cmpstr(fwupd_release_get_detach_image(release2), ==, "detach_image"); + g_assert_cmpstr(fwupd_release_get_update_message(release2), ==, "update_message"); + g_assert_cmpstr(fwupd_release_get_update_image(release2), ==, "update_image"); + g_assert_cmpstr(fwupd_release_get_filename(release2), ==, "filename"); + g_assert_cmpstr(fwupd_release_get_protocol(release2), ==, "protocol"); + g_assert_cmpstr(fwupd_release_get_license(release2), ==, "license"); + g_assert_cmpstr(fwupd_release_get_name(release2), ==, "name"); + g_assert_cmpstr(fwupd_release_get_name_variant_suffix(release2), ==, "name_variant_suffix"); + g_assert_cmpstr(fwupd_release_get_summary(release2), ==, "summary"); + g_assert_cmpstr(fwupd_release_get_branch(release2), ==, "branch"); + g_assert_cmpstr(fwupd_release_get_description(release2), ==, "description"); + g_assert_cmpstr(fwupd_release_get_homepage(release2), ==, "homepage"); + g_assert_cmpstr(fwupd_release_get_details_url(release2), ==, "details_url"); + g_assert_cmpstr(fwupd_release_get_source_url(release2), ==, "source_url"); + g_assert_cmpstr(fwupd_release_get_version(release2), ==, "version"); + g_assert_cmpstr(fwupd_release_get_vendor(release2), ==, "vendor"); + g_assert_cmpint(fwupd_release_get_size(release2), ==, 1234); + g_assert_cmpint(fwupd_release_get_created(release2), ==, 5678); + g_assert_true(fwupd_release_has_category(release2, "category")); + g_assert_true(fwupd_release_has_tag(release2, "tag")); + g_assert_true(fwupd_release_has_checksum(release2, "checksum")); + g_assert_true(fwupd_release_has_flag(release2, FWUPD_RELEASE_FLAG_IS_UPGRADE)); + g_assert_false(fwupd_release_has_flag(release2, FWUPD_RELEASE_FLAG_IS_COMMUNITY)); + g_assert_cmpint(fwupd_release_get_issues(release2)->len, ==, 1); + g_assert_cmpint(fwupd_release_get_locations(release2)->len, ==, 1); + g_assert_cmpint(fwupd_release_get_categories(release2)->len, ==, 1); + g_assert_cmpint(fwupd_release_get_tags(release2)->len, ==, 1); + g_assert_cmpint(fwupd_release_get_checksums(release2)->len, ==, 1); + g_assert_cmpint(fwupd_release_get_urgency(release2), ==, FWUPD_RELEASE_URGENCY_MEDIUM); + g_assert_cmpint(fwupd_release_get_install_duration(release2), ==, 2468); + + /* to JSON */ + json1 = fwupd_release_to_json_string(release1, &error); + g_assert_no_error(error); + g_assert_nonnull(json1); + json2 = fwupd_release_to_json_string(release2, &error); + g_assert_no_error(error); + g_assert_nonnull(json2); + ret = fu_test_compare_lines(json1, json2, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* to string */ + str = fwupd_release_to_string(release2); + ret = fu_test_compare_lines(str, + " AppstreamId: appstream-id\n" + " ReleaseId: id\n" + " RemoteId: remote-id\n" + " Summary: summary\n" + " Description: description\n" + " Branch: branch\n" + " Version: version\n" + " Filename: filename\n" + " Protocol: protocol\n" + " Categories: category\n" + " Issues: issue\n" + " Checksum: SHA1(checksum)\n" + " Tags: tag\n" + " License: license\n" + " Size: 1.2 kB\n" + " Created: 1970-01-01\n" + " Uri: location\n" + " Homepage: homepage\n" + " DetailsUrl: details_url\n" + " SourceUrl: source_url\n" + " Urgency: medium\n" + " Vendor: vendor\n" + " Flags: is-upgrade\n" + " InstallDuration: 2468\n" + " DetachCaption: detach_caption\n" + " DetachImage: detach_image\n" + " UpdateMessage: update_message\n" + " UpdateImage: update_image\n" + " foo: bar\n" + " baz: bam\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fwupd_plugin_func(void) +{ + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FwupdPlugin) plugin1 = NULL; + g_autoptr(FwupdPlugin) plugin2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) data = NULL; + + plugin1 = fwupd_plugin_new(); + fwupd_plugin_set_name(plugin1, "foo"); + fwupd_plugin_set_flags(plugin1, FWUPD_PLUGIN_FLAG_USER_WARNING); + fwupd_plugin_add_flag(plugin1, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fwupd_plugin_add_flag(plugin1, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fwupd_plugin_add_flag(plugin1, FWUPD_PLUGIN_FLAG_NO_HARDWARE); + fwupd_plugin_remove_flag(plugin1, FWUPD_PLUGIN_FLAG_NO_HARDWARE); + fwupd_plugin_remove_flag(plugin1, FWUPD_PLUGIN_FLAG_NO_HARDWARE); + data = fwupd_plugin_to_variant(plugin1); + plugin2 = fwupd_plugin_from_variant(data); + g_assert_cmpstr(fwupd_plugin_get_name(plugin2), ==, "foo"); + g_assert_cmpint(fwupd_plugin_get_flags(plugin2), + ==, + FWUPD_PLUGIN_FLAG_USER_WARNING | FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + g_assert_true(fwupd_plugin_has_flag(plugin2, FWUPD_PLUGIN_FLAG_USER_WARNING)); + g_assert_true(fwupd_plugin_has_flag(plugin2, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE)); + g_assert_false(fwupd_plugin_has_flag(plugin2, FWUPD_PLUGIN_FLAG_NO_HARDWARE)); + + str = fwupd_plugin_to_string(plugin2); + ret = fu_test_compare_lines(str, + " Name: foo\n" + " Flags: user-warning|clear-updatable\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fwupd_request_func(void) +{ + g_autofree gchar *str = NULL; + g_autoptr(FwupdRequest) request = fwupd_request_new(); + g_autoptr(FwupdRequest) request2 = NULL; + g_autoptr(GVariant) data = NULL; + + /* create dummy */ + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_REMOVE_REPLUG); + fwupd_request_add_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + fwupd_request_set_message(request, "foo"); + fwupd_request_set_image(request, "bar"); + fwupd_request_set_device_id(request, "950da62d4c753a26e64f7f7d687104ce38e32ca5"); + str = fwupd_request_to_string(request); + g_debug("%s", str); + + g_assert_true(fwupd_request_has_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE)); + g_assert_cmpint(fwupd_request_get_flags(request), + ==, + FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + fwupd_request_remove_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + g_assert_false(fwupd_request_has_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE)); + g_assert_cmpint(fwupd_request_get_flags(request), ==, FWUPD_REQUEST_FLAG_NONE); + + /* set in init */ + g_assert_cmpint(fwupd_request_get_created(request), >, 0); + + /* to serialized and back again */ + data = fwupd_request_to_variant(request); + request2 = fwupd_request_from_variant(data); + g_assert_cmpint(fwupd_request_get_kind(request2), ==, FWUPD_REQUEST_KIND_IMMEDIATE); + g_assert_cmpint(fwupd_request_get_created(request2), >, 0); + g_assert_cmpstr(fwupd_request_get_id(request2), ==, FWUPD_REQUEST_ID_REMOVE_REPLUG); + g_assert_cmpstr(fwupd_request_get_message(request2), ==, "foo"); + g_assert_cmpstr(fwupd_request_get_image(request2), ==, "bar"); + g_assert_cmpstr(fwupd_request_get_device_id(request2), + ==, + "950da62d4c753a26e64f7f7d687104ce38e32ca5"); +} + +static void +fwupd_device_func(void) +{ + gboolean ret; + g_autofree gchar *data = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FwupdDevice) dev = NULL; + g_autoptr(FwupdDevice) dev2 = fwupd_device_new(); + g_autoptr(FwupdDevice) dev_new = fwupd_device_new(); + 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; + g_autoptr(JsonParser) parser = json_parser_new(); + + /* create dummy object */ + dev = fwupd_device_new(); + fwupd_device_add_checksum(dev, "beefdead"); + fwupd_device_set_created(dev, 1); + fwupd_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); + fwupd_device_set_id(dev, "USB:foo"); + fwupd_device_set_modified(dev, 60 * 60 * 24); + fwupd_device_set_name(dev, "ColorHug2"); + fwupd_device_add_guid(dev, "2082b5e0-7a64-478a-b1b2-e3404fab6dad"); + fwupd_device_add_guid(dev, "00000000-0000-0000-0000-000000000000"); + fwupd_device_add_instance_id(dev, "USB\\VID_1234&PID_0001"); + fwupd_device_add_icon(dev, "input-gaming"); + fwupd_device_add_icon(dev, "input-mouse"); + fwupd_device_add_vendor_id(dev, "USB:0x1234"); + fwupd_device_add_vendor_id(dev, "PCI:0x5678"); + fwupd_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE | FWUPD_DEVICE_FLAG_REQUIRE_AC); + g_assert_true(fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_REQUIRE_AC)); + g_assert_true(fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_HISTORICAL)); + rel = fwupd_release_new(); + 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"); + fwupd_release_set_appstream_id(rel, "org.dave.ColorHug.firmware"); + fwupd_release_set_size(rel, 1024); + fwupd_release_add_location(rel, "http://foo.com"); + fwupd_release_add_location(rel, "ftp://foo.com"); + fwupd_release_add_tag(rel, "vendor-2021q1"); + fwupd_release_add_tag(rel, "vendor-2021q2"); + fwupd_release_set_version(rel, "1.2.3"); + fwupd_device_add_release(dev, rel); + str = fwupd_device_to_string(dev); + g_print("\n%s", str); + + /* check GUIDs */ + g_assert_true(fwupd_device_has_guid(dev, "2082b5e0-7a64-478a-b1b2-e3404fab6dad")); + g_assert_true(fwupd_device_has_guid(dev, "00000000-0000-0000-0000-000000000000")); + g_assert_false(fwupd_device_has_guid(dev, "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")); + + /* 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, + "FwupdDevice:\n" + " DeviceId: USB:foo\n" + " Name: ColorHug2\n" + " Guid: 18f514d2-c12e-581f-a696-cc6d6c271699 " + "← USB\\VID_1234&PID_0001 ⚠\n" + " Guid: 2082b5e0-7a64-478a-b1b2-e3404fab6dad\n" + " Guid: 00000000-0000-0000-0000-000000000000\n" + " Flags: updatable|require-ac\n" + " Checksum: SHA1(beefdead)\n" + " VendorId: USB:0x1234\n" + " VendorId: PCI:0x5678\n" + " Icon: input-gaming,input-mouse\n" + " Created: 1970-01-01\n" + " Modified: 1970-01-02\n" + " \n" + " [Release]\n" + " AppstreamId: org.dave.ColorHug.firmware\n" + " Description:

    Hi there!

    \n" + " Version: 1.2.3\n" + " Filename: firmware.bin\n" + " Checksum: SHA1(deadbeef)\n" + " Tags: vendor-2021q1\n" + " Tags: vendor-2021q2\n" + " Size: 1.0 kB\n" + " Uri: http://foo.com\n" + " Uri: ftp://foo.com\n" + " Flags: trusted-payload\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* export to json */ + builder = json_builder_new(); + json_builder_begin_object(builder); + fwupd_device_to_json_full(dev, builder, FWUPD_DEVICE_FLAG_TRUSTED); + 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" + " \"InstanceIds\" : [\n" + " \"USB\\\\VID_1234&PID_0001\"\n" + " ],\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" + " \"VendorId\" : \"USB:0x1234|PCI:0x5678\",\n" + " \"VendorIds\" : [\n" + " \"USB:0x1234\",\n" + " \"PCI:0x5678\"\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" + " \"Tags\" : [\n" + " \"vendor-2021q1\",\n" + " \"vendor-2021q2\"\n" + " ],\n" + " \"Size\" : 1024,\n" + " \"Locations\" : [\n" + " \"http://foo.com\",\n" + " \"ftp://foo.com\"\n" + " ],\n" + " \"Uri\" : \"http://foo.com\",\n" + " \"Flags\" : [\n" + " \"trusted-payload\"\n" + " ]\n" + " }\n" + " ]\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* incorporate */ + fwupd_device_incorporate(dev_new, dev); + g_assert_true(fwupd_device_has_vendor_id(dev_new, "USB:0x1234")); + g_assert_true(fwupd_device_has_vendor_id(dev_new, "PCI:0x5678")); + g_assert_true(fwupd_device_has_instance_id(dev_new, "USB\\VID_1234&PID_0001")); + + /* from JSON */ + ret = json_parser_load_from_data(parser, data, -1, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fwupd_device_from_json(dev2, json_parser_get_root(parser), &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip(error->message); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fwupd_device_has_vendor_id(dev2, "USB:0x1234")); + g_assert_true(fwupd_device_has_instance_id(dev2, "USB\\VID_1234&PID_0001")); + g_assert_true(fwupd_device_has_flag(dev2, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fwupd_device_has_flag(dev2, FWUPD_DEVICE_FLAG_LOCKED)); +} + +static void +fwupd_client_devices_func(void) +{ + FwupdDevice *dev; + gboolean ret; + g_autoptr(FwupdClient) client = NULL; + g_autoptr(GPtrArray) array = NULL; + g_autoptr(GError) error = NULL; + + client = fwupd_client_new(); + + /* only run if running fwupd is new enough */ + ret = fwupd_client_connect(client, NULL, &error); + if (ret == FALSE && (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_TIMED_OUT) || + g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER) || + g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))) { + g_debug("%s", error->message); + g_test_skip("timeout connecting to daemon"); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + if (fwupd_client_get_daemon_version(client) == NULL) { + g_test_skip("no enabled fwupd daemon"); + return; + } + if (!g_str_has_prefix(fwupd_client_get_daemon_version(client), "1.")) { + g_test_skip("running fwupd is too old"); + return; + } + + array = fwupd_client_get_devices(client, NULL, &error); + if (array == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_test_skip("no available fwupd devices"); + return; + } + if (array == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("no available fwupd daemon"); + return; + } + g_assert_no_error(error); + g_assert_nonnull(array); + g_assert_cmpint(array->len, >, 0); + + /* check device */ + dev = g_ptr_array_index(array, 0); + g_assert_true(FWUPD_IS_DEVICE(dev)); + g_assert_cmpstr(fwupd_device_get_guid_default(dev), !=, NULL); + g_assert_cmpstr(fwupd_device_get_id(dev), !=, NULL); +} + +static void +fwupd_client_remotes_func(void) +{ + gboolean ret; + g_autofree gchar *remotesdir = NULL; + g_autoptr(FwupdClient) client = NULL; + g_autoptr(FwupdRemote) remote2 = NULL; + g_autoptr(FwupdRemote) remote3 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = NULL; + + remotesdir = g_test_build_filename(G_TEST_DIST, "tests", "remotes.d", NULL); + (void)g_setenv("FU_SELF_TEST_REMOTES_DIR", remotesdir, TRUE); + + client = fwupd_client_new(); + + /* only run if running fwupd is new enough */ + ret = fwupd_client_connect(client, NULL, &error); + if (ret == FALSE && (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_TIMED_OUT) || + g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER) || + g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))) { + g_debug("%s", error->message); + g_test_skip("timeout connecting to daemon"); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + if (fwupd_client_get_daemon_version(client) == NULL) { + g_test_skip("no enabled fwupd daemon"); + return; + } + if (!g_str_has_prefix(fwupd_client_get_daemon_version(client), "1.")) { + g_test_skip("running fwupd is too old"); + return; + } + + array = fwupd_client_get_remotes(client, NULL, &error); + if (array == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_test_skip("no available fwupd remotes"); + return; + } + if (array == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("no available fwupd daemon"); + return; + } + g_assert_no_error(error); + g_assert_nonnull(array); + g_assert_cmpint(array->len, >, 0); + + /* check we can find the right thing */ + remote2 = fwupd_client_get_remote_by_id(client, "lvfs", NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(remote2); + g_assert_cmpstr(fwupd_remote_get_id(remote2), ==, "lvfs"); + g_assert_nonnull(fwupd_remote_get_metadata_uri(remote2)); + + /* check we set an error when unfound */ + remote3 = fwupd_client_get_remote_by_id(client, "XXXX", NULL, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(remote3); +} + +static gboolean +fwupd_has_system_bus(void) +{ + g_autoptr(GDBusConnection) conn = NULL; + conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); + if (conn != NULL) + return TRUE; + g_debug("D-Bus system bus unavailable, skipping tests."); + return FALSE; +} + +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; + + if (!g_file_test("/etc/machine-id", G_FILE_TEST_EXISTS)) { + g_test_skip("Missing /etc/machine-id"); + return; + } + if (!g_file_get_contents("/etc/machine-id", &buf, &sz, &error)) { + g_test_skip("/etc/machine-id is unreadable"); + return; + } + + if (sz == 0) { + g_test_skip("Empty /etc/machine-id"); + return; + } + + mhash1 = fwupd_build_machine_id("salt1", &error); + g_assert_no_error(error); + g_assert_cmpstr(mhash1, !=, NULL); + mhash2 = fwupd_build_machine_id("salt2", &error); + g_assert_no_error(error); + g_assert_cmpstr(mhash2, !=, NULL); + g_assert_cmpstr(mhash2, !=, mhash1); +} + +static void +fwupd_common_device_id_func(void) +{ + g_assert_false(fwupd_device_id_is_valid(NULL)); + g_assert_false(fwupd_device_id_is_valid("")); + g_assert_false(fwupd_device_id_is_valid("1ff60ab2-3905-06a1-b476-0371f00c9e9b")); + g_assert_false(fwupd_device_id_is_valid("aaaaaad3fae86d95e5d56626129d00e332c4b8dac95442")); + g_assert_false(fwupd_device_id_is_valid("x3fae86d95e5d56626129d00e332c4b8dac95442")); + g_assert_false(fwupd_device_id_is_valid("D3FAE86D95E5D56626129D00E332C4B8DAC95442")); + g_assert_false(fwupd_device_id_is_valid(FWUPD_DEVICE_ID_ANY)); + g_assert_true(fwupd_device_id_is_valid("d3fae86d95e5d56626129d00e332c4b8dac95442")); +} + +static void +fwupd_common_guid_func(void) +{ + const guint8 msbuf[] = "hello world!"; + g_autofree gchar *guid1 = NULL; + g_autofree gchar *guid2 = NULL; + g_autofree gchar *guid3 = 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_false(fwupd_guid_is_valid(NULL)); + g_assert_false(fwupd_guid_is_valid("")); + g_assert_false(fwupd_guid_is_valid("1ff60ab2-3905-06a1-b476")); + g_assert_false(fwupd_guid_is_valid("1ff60ab2-XXXX-XXXX-XXXX-0371f00c9e9b")); + g_assert_false(fwupd_guid_is_valid("1ff60ab2-XXXX-XXXX-XXXX-0371f00c9e9bf")); + g_assert_false(fwupd_guid_is_valid(" 1ff60ab2-3905-06a1-b476-0371f00c9e9b")); + g_assert_false(fwupd_guid_is_valid("00000000-0000-0000-0000-000000000000")); + + /* valid */ + g_assert_true(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"); + + guid3 = fwupd_guid_hash_data(msbuf, sizeof(msbuf), FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT); + g_assert_cmpstr(guid3, ==, "6836cfac-f77a-527f-b375-4f92f01449c5"); + + /* round-trip BE */ + ret = fwupd_guid_from_string("00112233-4455-6677-8899-aabbccddeeff", + &buf, + FWUPD_GUID_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(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_no_error(error); + g_assert_true(ret); + g_assert_cmpint(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)); +} + +static gchar * +fwupd_attr_to_json_string(GObject *attr, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(JsonNode) json_root = NULL; + json_builder_begin_object(builder); + if (FWUPD_IS_SECURITY_ATTR(attr)) + fwupd_security_attr_to_json(FWUPD_SECURITY_ATTR(attr), builder); + else if (FWUPD_IS_BIOS_SETTING(attr)) + fwupd_bios_setting_to_json(FWUPD_BIOS_SETTING(attr), 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); + if (data == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert security attribute to json."); + return NULL; + } + return g_steal_pointer(&data); +} + +static void +fwupd_security_attr_func(void) +{ + gboolean ret; + g_autofree gchar *str1 = NULL; + g_autofree gchar *str2 = NULL; + g_autofree gchar *str3 = NULL; + g_autofree gchar *json = NULL; + g_autoptr(FwupdSecurityAttr) attr1 = fwupd_security_attr_new("org.fwupd.hsi.bar"); + g_autoptr(FwupdSecurityAttr) attr2 = fwupd_security_attr_new(NULL); + g_autoptr(FwupdSecurityAttr) attr3 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) data = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + + for (guint i = 1; i < FWUPD_SECURITY_ATTR_RESULT_LAST; i++) { + const gchar *tmp = fwupd_security_attr_result_to_string(i); + g_assert_cmpstr(tmp, !=, NULL); + g_assert_cmpint(fwupd_security_attr_result_from_string(tmp), ==, i); + } + + g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr1), ==, "org.fwupd.hsi.bar"); + fwupd_security_attr_set_appstream_id(attr1, "org.fwupd.hsi.baz"); + g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr1), ==, "org.fwupd.hsi.baz"); + + fwupd_security_attr_set_level(attr1, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + g_assert_cmpint(fwupd_security_attr_get_level(attr1), + ==, + FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + + fwupd_security_attr_set_result(attr1, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + g_assert_cmpint(fwupd_security_attr_get_result(attr1), + ==, + FWUPD_SECURITY_ATTR_RESULT_ENABLED); + + fwupd_security_attr_add_flag(attr1, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_flag(attr1, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + fwupd_security_attr_remove_flag(attr1, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + g_assert_true(fwupd_security_attr_has_flag(attr1, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)); + g_assert_false(fwupd_security_attr_has_flag(attr1, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA)); + g_assert_false(fwupd_security_attr_has_flag(attr1, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)); + + fwupd_security_attr_set_name(attr1, "DCI"); + g_assert_cmpstr(fwupd_security_attr_get_name(attr1), ==, "DCI"); + + fwupd_security_attr_set_plugin(attr1, "uefi-capsule"); + g_assert_cmpstr(fwupd_security_attr_get_plugin(attr1), ==, "uefi-capsule"); + + fwupd_security_attr_set_url(attr1, "https://foo.bar"); + g_assert_cmpstr(fwupd_security_attr_get_url(attr1), ==, "https://foo.bar"); + + fwupd_security_attr_add_guid(attr1, "af3fc12c-d090-5783-8a67-845b90d3cfec"); + g_assert_true(fwupd_security_attr_has_guid(attr1, "af3fc12c-d090-5783-8a67-845b90d3cfec")); + g_assert_false(fwupd_security_attr_has_guid(attr1, "NOT_GOING_TO_EXIST")); + + fwupd_security_attr_add_metadata(attr1, "KEY", "VALUE"); + g_assert_cmpstr(fwupd_security_attr_get_metadata(attr1, "KEY"), ==, "VALUE"); + + /* remove this from the output */ + fwupd_security_attr_set_created(attr1, 0); + + str1 = fwupd_security_attr_to_string(attr1); + ret = fu_test_compare_lines(str1, + " AppstreamId: org.fwupd.hsi.baz\n" + " HsiLevel: 2\n" + " HsiResult: enabled\n" + " Flags: success\n" + " Name: DCI\n" + " Plugin: uefi-capsule\n" + " Uri: https://foo.bar\n" + " Guid: af3fc12c-d090-5783-8a67-845b90d3cfec\n" + " KEY: VALUE\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* roundtrip GVariant */ + data = fwupd_security_attr_to_variant(attr1); + attr3 = fwupd_security_attr_from_variant(data); + fwupd_security_attr_set_created(attr3, 0); + str3 = fwupd_security_attr_to_string(attr3); + ret = fu_test_compare_lines(str3, + " AppstreamId: org.fwupd.hsi.baz\n" + " HsiLevel: 2\n" + " HsiResult: enabled\n" + " Flags: success\n" + " Name: DCI\n" + " Plugin: uefi-capsule\n" + " Uri: https://foo.bar\n" + " Guid: af3fc12c-d090-5783-8a67-845b90d3cfec\n" + " KEY: VALUE\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* to JSON */ + json = fwupd_attr_to_json_string(G_OBJECT(attr1), &error); + g_assert_no_error(error); + g_assert_nonnull(json); + ret = fu_test_compare_lines(json, + "{\n" + " \"AppstreamId\" : \"org.fwupd.hsi.baz\",\n" + " \"HsiLevel\" : 2,\n" + " \"HsiResult\" : \"enabled\",\n" + " \"Name\" : \"DCI\",\n" + " \"Plugin\" : \"uefi-capsule\",\n" + " \"Uri\" : \"https://foo.bar\",\n" + " \"Flags\" : [\n" + " \"success\"\n" + " ],\n" + " \"Guid\" : [\n" + " \"af3fc12c-d090-5783-8a67-845b90d3cfec\"\n" + " ],\n" + " \"KEY\" : \"VALUE\"\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* from JSON */ + ret = json_parser_load_from_data(parser, json, -1, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fwupd_security_attr_from_json(attr2, json_parser_get_root(parser), &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip(error->message); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + + /* we don't load unconditionally load metadata from the JSON */ + fwupd_security_attr_add_metadata(attr2, "KEY", "VALUE"); + + str2 = fwupd_security_attr_to_string(attr2); + ret = fu_test_compare_lines(str2, str1, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fwupd_bios_settings_func(void) +{ + gboolean ret; + g_autofree gchar *str1 = NULL; + g_autofree gchar *str2 = NULL; + g_autofree gchar *str3 = NULL; + g_autofree gchar *json1 = NULL; + g_autofree gchar *json2 = NULL; + g_autoptr(FwupdBiosSetting) attr1 = fwupd_bios_setting_new("foo", "/path/to/bar"); + g_autoptr(FwupdBiosSetting) attr2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) data1 = NULL; + g_autoptr(GVariant) data2 = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + + g_assert_cmpstr(fwupd_bios_setting_get_name(attr1), ==, "foo"); + fwupd_bios_setting_set_name(attr1, "UEFISecureBoot"); + g_assert_cmpstr(fwupd_bios_setting_get_name(attr1), ==, "UEFISecureBoot"); + + fwupd_bios_setting_set_kind(attr1, FWUPD_BIOS_SETTING_KIND_ENUMERATION); + g_assert_cmpint(fwupd_bios_setting_get_kind(attr1), + ==, + FWUPD_BIOS_SETTING_KIND_ENUMERATION); + + fwupd_bios_setting_set_description(attr1, "Controls Secure boot"); + g_assert_cmpstr(fwupd_bios_setting_get_description(attr1), ==, "Controls Secure boot"); + fwupd_bios_setting_set_current_value(attr1, "Disabled"); + g_assert_cmpstr(fwupd_bios_setting_get_current_value(attr1), ==, "Disabled"); + + fwupd_bios_setting_add_possible_value(attr1, "Disabled"); + fwupd_bios_setting_add_possible_value(attr1, "Enabled"); + g_assert_true(fwupd_bios_setting_has_possible_value(attr1, "Disabled")); + g_assert_false(fwupd_bios_setting_has_possible_value(attr1, "NOT_GOING_TO_EXIST")); + + str1 = fwupd_bios_setting_to_string(attr1); + ret = fu_test_compare_lines(str1, + " Name: UEFISecureBoot\n" + " Description: Controls Secure boot\n" + " Filename: /path/to/bar\n" + " BiosSettingType: 1\n" + " BiosSettingCurrentValue: Disabled\n" + " BiosSettingReadOnly: False\n" + " BiosSettingPossibleValues: Disabled\n" + " BiosSettingPossibleValues: Enabled\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* roundtrip GVariant */ + data1 = fwupd_bios_setting_to_variant(attr1, TRUE); + attr2 = fwupd_bios_setting_from_variant(data1); + str2 = fwupd_bios_setting_to_string(attr2); + ret = fu_test_compare_lines(str2, + " Name: UEFISecureBoot\n" + " Description: Controls Secure boot\n" + " Filename: /path/to/bar\n" + " BiosSettingType: 1\n" + " BiosSettingCurrentValue: Disabled\n" + " BiosSettingReadOnly: False\n" + " BiosSettingPossibleValues: Disabled\n" + " BiosSettingPossibleValues: Enabled\n", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* to JSON */ + json1 = fwupd_attr_to_json_string(G_OBJECT(attr1), &error); + g_assert_no_error(error); + g_assert_nonnull(json1); + ret = fu_test_compare_lines(json1, + "{\n" + " \"Name\" : \"UEFISecureBoot\",\n" + " \"Description\" : \"Controls Secure boot\",\n" + " \"Filename\" : \"/path/to/bar\",\n" + " \"BiosSettingCurrentValue\" : \"Disabled\",\n" + " \"BiosSettingReadOnly\" : \"false\",\n" + " \"BiosSettingType\" : 1,\n" + " \"BiosSettingPossibleValues\" : [\n" + " \"Disabled\",\n" + " \"Enabled\"\n" + " ]\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* from JSON */ + ret = json_parser_load_from_data(parser, json1, -1, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fwupd_bios_setting_from_json(attr2, json_parser_get_root(parser), &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip(error->message); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + + str3 = fwupd_bios_setting_to_string(attr2); + ret = fu_test_compare_lines(str3, str1, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* make sure we filter CurrentValue if not trusted */ + data2 = fwupd_bios_setting_to_variant(attr1, TRUE); + json2 = fwupd_attr_to_json_string(G_OBJECT(attr1), &error); + g_assert_no_error(error); + g_assert_nonnull(json2); + ret = fu_test_compare_lines(json2, + "{\n" + " \"Name\" : \"UEFISecureBoot\",\n" + " \"Description\" : \"Controls Secure boot\",\n" + " \"Filename\" : \"/path/to/bar\",\n" + " \"BiosSettingCurrentValue\" : \"Disabled\",\n" + " \"BiosSettingReadOnly\" : \"false\",\n" + " \"BiosSettingType\" : 1,\n" + " \"BiosSettingPossibleValues\" : [\n" + " \"Disabled\",\n" + " \"Enabled\"\n" + " ]\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +int +main(int argc, char **argv) +{ + setlocale(LC_ALL, ""); + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/fwupd/enums", fwupd_enums_func); + g_test_add_func("/fwupd/common{machine-hash}", fwupd_common_machine_hash_func); + g_test_add_func("/fwupd/common{device-id}", fwupd_common_device_id_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/plugin", fwupd_plugin_func); + g_test_add_func("/fwupd/request", fwupd_request_func); + g_test_add_func("/fwupd/device", fwupd_device_func); + g_test_add_func("/fwupd/security-attr", fwupd_security_attr_func); + g_test_add_func("/fwupd/remote{download}", fwupd_remote_download_func); + g_test_add_func("/fwupd/remote{base-uri}", fwupd_remote_baseuri_func); + g_test_add_func("/fwupd/remote{no-path}", fwupd_remote_nopath_func); + g_test_add_func("/fwupd/remote{local}", fwupd_remote_local_func); + g_test_add_func("/fwupd/remote{duplicate}", fwupd_remote_duplicate_func); + g_test_add_func("/fwupd/remote{auth}", fwupd_remote_auth_func); + g_test_add_func("/fwupd/bios-attrs", fwupd_bios_settings_func); + if (fwupd_has_system_bus()) { + g_test_add_func("/fwupd/client{remotes}", fwupd_client_remotes_func); + g_test_add_func("/fwupd/client{devices}", fwupd_client_devices_func); + } + return g_test_run(); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-thread-test.c b/fwupd-1.8.6/libfwupd/fwupd-thread-test.c new file mode 100644 index 0000000000000000000000000000000000000000..61a7e424f72c567dd91f4589b6b648d99d1d5ff9 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-thread-test.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 Philip Withnall + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +typedef struct { + GApplication *app; + FwupdClient *client; + GPtrArray *worker_threads; +} FuThreadTestSelf; + +static gboolean +fwupd_thread_test_exit_idle_cb(gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_application_release(self->app); + return G_SOURCE_REMOVE; +} + +static gpointer +fwupd_thread_test_thread_cb(gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GMainContext) context = g_main_context_new(); + g_autoptr(GMainContextPusher) pusher = g_main_context_pusher_new(context); + + g_assert_nonnull(pusher); + g_message("Calling fwupd_client_get_devices() in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + devices = fwupd_client_get_devices(self->client, NULL, &error_local); + if (devices == NULL) + g_warning("%s", error_local->message); + g_idle_add(fwupd_thread_test_exit_idle_cb, self); + return NULL; +} + +static gboolean +fwupd_thread_test_idle_cb(gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + + g_message("fwupd_thread_test_idle_cb() in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + + /* create 'n' threads with a small delay, and 'n-1' references on the app */ + for (guint i = 0; i < 30; i++) { + g_autofree gchar *thread_str = g_strdup_printf("worker%02u", i); + GThread *thread = g_thread_new(thread_str, fwupd_thread_test_thread_cb, self); + g_usleep(g_random_int_range(0, 1000)); + g_ptr_array_add(self->worker_threads, thread); + if (i > 0) + g_application_hold(self->app); + } + + return G_SOURCE_REMOVE; +} + +static void +fwupd_thread_test_activate_cb(GApplication *app, gpointer user_data) +{ + FuThreadTestSelf *self = user_data; + g_application_hold(self->app); + g_idle_add(fwupd_thread_test_idle_cb, self); +} + +static gboolean +fwupd_thread_test_has_system_bus(void) +{ + g_autoptr(GDBusConnection) conn = NULL; + conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); + return conn != NULL; +} + +int +main(void) +{ + gint retval; + g_autoptr(FwupdClient) client = fwupd_client_new(); + g_autoptr(GApplication) app = g_application_new("org.test.Test", G_APPLICATION_NON_UNIQUE); + g_autoptr(GPtrArray) worker_threads = g_ptr_array_new(); + FuThreadTestSelf self = {app, client, worker_threads}; + + /* only some of the CI targets have a DBus daemon */ + if (!fwupd_thread_test_has_system_bus()) { + g_message("D-Bus system bus unavailable, skipping tests."); + return 0; + } + + g_message("Created FwupdClient in thread %p with main context %p", + g_thread_self(), + g_main_context_get_thread_default()); + g_signal_connect(G_APPLICATION(app), + "activate", + G_CALLBACK(fwupd_thread_test_activate_cb), + &self); + retval = g_application_run(app, 0, NULL); + for (guint i = 0; i < self.worker_threads->len; i++) { + GThread *thread = g_ptr_array_index(self.worker_threads, i); + g_thread_join(thread); + } + + return retval; +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-version.c b/fwupd-1.8.6/libfwupd/fwupd-version.c new file mode 100644 index 0000000000000000000000000000000000000000..966bd2b010b0fbaaddfeafeb8b83b1f0badf4884 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-version.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-version.h" + +/** + * fwupd_version_string: + * + * Gets the libfwupd installed runtime version. + * + * This may be different to the *build-time* version if the daemon and library + * objects somehow get out of sync. + * + * Returns: version string + * + * Since: 1.6.1 + **/ +const gchar * +fwupd_version_string(void) +{ + return G_STRINGIFY(FWUPD_MAJOR_VERSION) "." G_STRINGIFY( + FWUPD_MINOR_VERSION) "." G_STRINGIFY(FWUPD_MICRO_VERSION); +} diff --git a/fwupd-1.8.6/libfwupd/fwupd-version.h.in b/fwupd-1.8.6/libfwupd/fwupd-version.h.in new file mode 100644 index 0000000000000000000000000000000000000000..77e4a1ccd3e14f53d09b6a61f2bef8313145dd4d --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd-version.h.in @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#if !defined(__FWUPD_H_INSIDE__) && !defined(FWUPD_COMPILATION) +#error "Only can be included directly." +#endif + +/* clang-format off */ +/** + * FWUPD_MAJOR_VERSION: + * + * The compile-time major version + */ +#define FWUPD_MAJOR_VERSION @MAJOR_VERSION@ + +/** + * FWUPD_MINOR_VERSION: + * + * The compile-time minor version + */ +#define FWUPD_MINOR_VERSION @MINOR_VERSION@ + +/** + * FWUPD_MICRO_VERSION: + * + * The compile-time micro version + */ +#define FWUPD_MICRO_VERSION @MICRO_VERSION@ +/* clang-format on */ + +/** + * FWUPD_CHECK_VERSION: + * @major: Major version number + * @minor: Minor version number + * @micro: Micro version number + * + * Check whether a fwupd version equal to or greater than + * major.minor.micro. + * + * These compile time macros allow the user to enable parts of client code + * depending on the version of libfwupd installed. + */ +#define FWUPD_CHECK_VERSION(major, minor, micro) \ + (FWUPD_MAJOR_VERSION > major || \ + (FWUPD_MAJOR_VERSION == major && FWUPD_MINOR_VERSION > minor) || \ + (FWUPD_MAJOR_VERSION == major && FWUPD_MINOR_VERSION == minor && \ + FWUPD_MICRO_VERSION >= micro)) + +const gchar * +fwupd_version_string(void); diff --git a/fwupd-1.8.6/libfwupd/fwupd.h b/fwupd-1.8.6/libfwupd/fwupd.h new file mode 100644 index 0000000000000000000000000000000000000000..74a56e806f37ef8e37bc500b00bde5e0fa15020d --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#define __FWUPD_H_INSIDE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FWUPD_DISABLE_DEPRECATED +#include +#endif + +#undef __FWUPD_H_INSIDE__ diff --git a/fwupd-1.8.6/libfwupd/fwupd.map b/fwupd-1.8.6/libfwupd/fwupd.map new file mode 100644 index 0000000000000000000000000000000000000000..ebc653df4fe56bf19977e89a92362c25f05a957d --- /dev/null +++ b/fwupd-1.8.6/libfwupd/fwupd.map @@ -0,0 +1,863 @@ +# generated automatically, do not edit! + +LIBFWUPD_0.1.1 { + global: + fwupd_error_quark; + fwupd_status_from_string; + fwupd_status_to_string; + local: *; +}; + +LIBFWUPD_0.7.0 { + global: + fwupd_client_clear_results; + fwupd_client_get_results; + fwupd_client_get_type; + fwupd_client_install; + fwupd_client_new; + fwupd_client_unlock; + fwupd_client_verify; + fwupd_device_flag_from_string; + fwupd_device_flag_to_string; + fwupd_error_from_string; + fwupd_error_to_string; + fwupd_trust_flag_from_string; + fwupd_trust_flag_to_string; + fwupd_update_state_from_string; + fwupd_update_state_to_string; + local: *; +} LIBFWUPD_0.1.1; + +LIBFWUPD_0.7.1 { + global: + fwupd_client_connect; + local: *; +} LIBFWUPD_0.7.0; + +LIBFWUPD_0.7.3 { + global: + fwupd_client_get_percentage; + fwupd_client_get_status; + local: *; +} LIBFWUPD_0.7.1; + +LIBFWUPD_0.8.0 { + global: + fwupd_client_verify_update; + local: *; +} LIBFWUPD_0.7.3; + +LIBFWUPD_0.9.2 { + global: + fwupd_client_get_devices; + local: *; +} LIBFWUPD_0.8.0; + +LIBFWUPD_0.9.3 { + global: + fwupd_checksum_format_for_display; + fwupd_checksum_guess_kind; + fwupd_client_get_device_by_id; + fwupd_client_get_releases; + fwupd_client_get_remote_by_id; + fwupd_client_get_remotes; + fwupd_device_add_checksum; + fwupd_device_add_flag; + fwupd_device_add_guid; + fwupd_device_get_checksums; + fwupd_device_get_created; + fwupd_device_get_description; + fwupd_device_get_flags; + fwupd_device_get_flashes_left; + fwupd_device_get_guid_default; + fwupd_device_get_guids; + fwupd_device_get_id; + fwupd_device_get_modified; + fwupd_device_get_name; + fwupd_device_get_summary; + fwupd_device_get_type; + fwupd_device_get_vendor; + fwupd_device_get_version; + fwupd_device_get_version_bootloader; + fwupd_device_get_version_lowest; + fwupd_device_has_flag; + fwupd_device_has_guid; + fwupd_device_new; + fwupd_device_remove_flag; + fwupd_device_set_created; + fwupd_device_set_description; + fwupd_device_set_flags; + fwupd_device_set_flashes_left; + fwupd_device_set_id; + fwupd_device_set_modified; + fwupd_device_set_name; + fwupd_device_set_summary; + fwupd_device_set_vendor; + fwupd_device_set_version; + fwupd_device_set_version_bootloader; + fwupd_device_set_version_lowest; + fwupd_device_to_string; + fwupd_release_add_checksum; + fwupd_release_get_appstream_id; + fwupd_release_get_checksums; + fwupd_release_get_description; + fwupd_release_get_filename; + fwupd_release_get_homepage; + fwupd_release_get_license; + fwupd_release_get_name; + fwupd_release_get_remote_id; + fwupd_release_get_size; + fwupd_release_get_summary; + fwupd_release_get_type; + fwupd_release_get_uri; + fwupd_release_get_vendor; + fwupd_release_get_version; + fwupd_release_new; + fwupd_release_set_appstream_id; + fwupd_release_set_description; + fwupd_release_set_filename; + fwupd_release_set_homepage; + fwupd_release_set_license; + fwupd_release_set_name; + fwupd_release_set_remote_id; + fwupd_release_set_size; + fwupd_release_set_summary; + fwupd_release_set_uri; + fwupd_release_set_vendor; + fwupd_release_set_version; + fwupd_release_to_string; + fwupd_remote_get_enabled; + fwupd_remote_get_id; + fwupd_remote_get_type; + fwupd_remote_load_from_filename; + fwupd_remote_new; + local: *; +} LIBFWUPD_0.9.2; + +LIBFWUPD_0.9.4 { + global: + fwupd_checksum_get_best; + fwupd_checksum_get_by_kind; + fwupd_device_get_vendor_id; + fwupd_device_set_vendor_id; + local: *; +} LIBFWUPD_0.9.3; + +LIBFWUPD_0.9.5 { + global: + fwupd_remote_get_age; + fwupd_remote_get_order_after; + fwupd_remote_get_order_before; + fwupd_remote_get_password; + fwupd_remote_get_priority; + fwupd_remote_get_username; + fwupd_remote_set_mtime; + fwupd_remote_set_priority; + local: *; +} LIBFWUPD_0.9.4; + +LIBFWUPD_0.9.6 { + global: + fwupd_client_get_daemon_version; + fwupd_remote_get_filename_cache; + fwupd_remote_get_kind; + fwupd_remote_kind_from_string; + fwupd_remote_kind_to_string; + local: *; +} LIBFWUPD_0.9.5; + +LIBFWUPD_0.9.7 { + global: + fwupd_keyring_kind_from_string; + fwupd_keyring_kind_to_string; + fwupd_remote_build_firmware_uri; + fwupd_remote_get_filename_cache_sig; + fwupd_remote_get_firmware_base_uri; + fwupd_remote_get_keyring_kind; + fwupd_remote_get_metadata_uri; + fwupd_remote_get_metadata_uri_sig; + local: *; +} LIBFWUPD_0.9.6; + +LIBFWUPD_0.9.8 { + global: + fwupd_client_get_downgrades; + fwupd_client_get_upgrades; + fwupd_client_modify_remote; + fwupd_device_add_icon; + fwupd_device_add_release; + fwupd_device_get_icons; + fwupd_device_get_release_default; + fwupd_device_get_releases; + fwupd_device_get_update_error; + fwupd_device_get_update_state; + fwupd_device_set_update_error; + fwupd_device_set_update_state; + fwupd_release_get_trust_flags; + fwupd_release_set_trust_flags; + fwupd_remote_get_filename_source; + fwupd_remote_get_title; + local: *; +} LIBFWUPD_0.9.7; + +LIBFWUPD_1.0.0 { + global: + fwupd_client_get_details; + fwupd_client_update_metadata; + fwupd_device_from_variant; + fwupd_device_get_plugin; + fwupd_device_set_plugin; + fwupd_device_to_variant; + fwupd_release_from_variant; + fwupd_release_to_variant; + fwupd_remote_from_variant; + fwupd_remote_get_checksum; + fwupd_remote_to_variant; + local: *; +} LIBFWUPD_0.9.8; + +LIBFWUPD_1.0.3 { + global: + fwupd_build_user_agent; + local: *; +} LIBFWUPD_1.0.0; + +LIBFWUPD_1.0.4 { + global: + fwupd_build_history_report_json; + fwupd_build_machine_id; + fwupd_client_get_history; + fwupd_client_modify_device; + fwupd_release_add_metadata; + fwupd_release_add_metadata_item; + fwupd_release_get_metadata; + fwupd_release_get_metadata_item; + fwupd_remote_get_report_uri; + local: *; +} LIBFWUPD_1.0.3; + +LIBFWUPD_1.0.7 { + global: + fwupd_get_os_release; + fwupd_remote_get_agreement; + fwupd_remote_set_agreement; + local: *; +} LIBFWUPD_1.0.4; + +LIBFWUPD_1.0.8 { + global: + fwupd_device_get_parent; + fwupd_device_get_parent_id; + fwupd_device_set_parent; + 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; + +LIBFWUPD_1.3.1 { + global: + fwupd_client_get_host_product; + fwupd_remote_get_remotes_dir; + fwupd_remote_set_remotes_dir; + local: *; +} LIBFWUPD_1.2.10; + +LIBFWUPD_1.3.2 { + global: + fwupd_client_get_host_machine_id; + fwupd_release_add_issue; + fwupd_release_get_issues; + fwupd_release_get_name_variant_suffix; + fwupd_release_set_name_variant_suffix; + local: *; +} LIBFWUPD_1.3.1; + +LIBFWUPD_1.3.3 { + global: + fwupd_release_get_detach_caption; + fwupd_release_get_detach_image; + fwupd_release_set_detach_caption; + fwupd_release_set_detach_image; + fwupd_remote_get_automatic_reports; + local: *; +} LIBFWUPD_1.3.2; + +LIBFWUPD_1.3.4 { + global: + fwupd_client_get_daemon_interactive; + local: *; +} LIBFWUPD_1.3.3; + +LIBFWUPD_1.3.6 { + global: + fwupd_device_get_protocol; + fwupd_device_get_version_raw; + fwupd_device_set_protocol; + fwupd_device_set_version_raw; + local: *; +} LIBFWUPD_1.3.4; + +LIBFWUPD_1.3.7 { + global: + fwupd_device_array_ensure_parents; + fwupd_device_get_children; + local: *; +} LIBFWUPD_1.3.6; + +LIBFWUPD_1.4.0 { + global: + fwupd_device_get_status; + fwupd_device_get_version_bootloader_raw; + fwupd_device_get_version_lowest_raw; + fwupd_device_set_status; + fwupd_device_set_version_bootloader_raw; + fwupd_device_set_version_lowest_raw; + fwupd_release_get_created; + fwupd_release_get_urgency; + fwupd_release_set_created; + fwupd_release_set_urgency; + fwupd_release_urgency_from_string; + fwupd_release_urgency_to_string; + fwupd_remote_load_signature; + local: *; +} LIBFWUPD_1.3.7; + +LIBFWUPD_1.4.1 { + global: + fwupd_client_get_devices_by_guid; + fwupd_device_id_is_valid; + local: *; +} LIBFWUPD_1.4.0; + +LIBFWUPD_1.4.5 { + global: + fwupd_client_download_bytes; + fwupd_client_ensure_networking; + fwupd_client_install_bytes; + fwupd_client_install_release; + fwupd_client_refresh_remote; + fwupd_client_set_feature_flags; + fwupd_client_set_user_agent; + fwupd_client_set_user_agent_for_package; + fwupd_client_update_metadata_bytes; + fwupd_client_upload_bytes; + fwupd_device_get_update_image; + fwupd_device_set_update_image; + fwupd_feature_flag_from_string; + fwupd_feature_flag_to_string; + fwupd_release_get_update_image; + fwupd_release_set_update_image; + fwupd_remote_load_signature_bytes; + local: *; +} LIBFWUPD_1.4.1; + +LIBFWUPD_1.4.6 { + global: + fwupd_client_get_blocked_firmware; + fwupd_client_set_blocked_firmware; + local: *; +} LIBFWUPD_1.4.5; + +LIBFWUPD_1.5.0 { + global: + fwupd_client_activate_async; + fwupd_client_activate_finish; + fwupd_client_clear_results_async; + fwupd_client_clear_results_finish; + fwupd_client_connect_async; + fwupd_client_connect_finish; + fwupd_client_download_bytes_async; + fwupd_client_download_bytes_finish; + fwupd_client_get_approved_firmware_async; + fwupd_client_get_approved_firmware_finish; + fwupd_client_get_blocked_firmware_async; + fwupd_client_get_blocked_firmware_finish; + fwupd_client_get_details_bytes; + fwupd_client_get_details_bytes_async; + fwupd_client_get_details_bytes_finish; + fwupd_client_get_device_by_id_async; + fwupd_client_get_device_by_id_finish; + fwupd_client_get_devices_async; + fwupd_client_get_devices_by_guid_async; + fwupd_client_get_devices_by_guid_finish; + fwupd_client_get_devices_finish; + fwupd_client_get_downgrades_async; + fwupd_client_get_downgrades_finish; + fwupd_client_get_history_async; + fwupd_client_get_history_finish; + fwupd_client_get_host_security_attrs; + fwupd_client_get_host_security_attrs_async; + fwupd_client_get_host_security_attrs_finish; + fwupd_client_get_host_security_id; + fwupd_client_get_plugins; + fwupd_client_get_plugins_async; + fwupd_client_get_plugins_finish; + fwupd_client_get_releases_async; + fwupd_client_get_releases_finish; + fwupd_client_get_remote_by_id_async; + fwupd_client_get_remote_by_id_finish; + fwupd_client_get_remotes_async; + fwupd_client_get_remotes_finish; + fwupd_client_get_report_metadata; + fwupd_client_get_report_metadata_async; + fwupd_client_get_report_metadata_finish; + fwupd_client_get_results_async; + fwupd_client_get_results_finish; + fwupd_client_get_upgrades_async; + fwupd_client_get_upgrades_finish; + fwupd_client_install_async; + fwupd_client_install_bytes_async; + fwupd_client_install_bytes_finish; + fwupd_client_install_finish; + fwupd_client_install_release_async; + fwupd_client_install_release_finish; + fwupd_client_modify_config_async; + fwupd_client_modify_config_finish; + fwupd_client_modify_device_async; + fwupd_client_modify_device_finish; + fwupd_client_modify_remote_async; + fwupd_client_modify_remote_finish; + fwupd_client_refresh_remote_async; + fwupd_client_refresh_remote_finish; + fwupd_client_self_sign_async; + fwupd_client_self_sign_finish; + fwupd_client_set_approved_firmware_async; + fwupd_client_set_approved_firmware_finish; + fwupd_client_set_blocked_firmware_async; + fwupd_client_set_blocked_firmware_finish; + fwupd_client_set_feature_flags_async; + fwupd_client_set_feature_flags_finish; + fwupd_client_unlock_async; + fwupd_client_unlock_finish; + fwupd_client_update_metadata_bytes_async; + fwupd_client_update_metadata_bytes_finish; + fwupd_client_upload_bytes_async; + fwupd_client_upload_bytes_finish; + fwupd_client_verify_async; + fwupd_client_verify_finish; + fwupd_client_verify_update_async; + fwupd_client_verify_update_finish; + fwupd_device_get_branch; + fwupd_device_set_branch; + fwupd_plugin_add_flag; + fwupd_plugin_array_from_variant; + fwupd_plugin_flag_from_string; + fwupd_plugin_flag_to_string; + fwupd_plugin_from_variant; + fwupd_plugin_get_flags; + fwupd_plugin_get_name; + fwupd_plugin_get_type; + fwupd_plugin_has_flag; + fwupd_plugin_new; + fwupd_plugin_remove_flag; + fwupd_plugin_set_flags; + fwupd_plugin_set_name; + fwupd_plugin_to_json; + fwupd_plugin_to_string; + fwupd_plugin_to_variant; + fwupd_release_get_branch; + fwupd_release_set_branch; + fwupd_remote_get_automatic_security_reports; + fwupd_remote_get_security_report_uri; + fwupd_security_attr_add_flag; + fwupd_security_attr_add_metadata; + fwupd_security_attr_add_obsolete; + fwupd_security_attr_array_from_variant; + fwupd_security_attr_flag_to_string; + fwupd_security_attr_flag_to_suffix; + fwupd_security_attr_from_variant; + fwupd_security_attr_get_appstream_id; + fwupd_security_attr_get_flags; + fwupd_security_attr_get_level; + fwupd_security_attr_get_metadata; + fwupd_security_attr_get_name; + fwupd_security_attr_get_obsoletes; + fwupd_security_attr_get_plugin; + fwupd_security_attr_get_result; + fwupd_security_attr_get_type; + fwupd_security_attr_get_url; + fwupd_security_attr_has_flag; + fwupd_security_attr_has_obsolete; + fwupd_security_attr_new; + fwupd_security_attr_result_to_string; + fwupd_security_attr_set_appstream_id; + fwupd_security_attr_set_flags; + fwupd_security_attr_set_level; + fwupd_security_attr_set_name; + fwupd_security_attr_set_plugin; + fwupd_security_attr_set_result; + fwupd_security_attr_set_url; + fwupd_security_attr_to_json; + fwupd_security_attr_to_string; + fwupd_security_attr_to_variant; + local: *; +} LIBFWUPD_1.4.6; + +LIBFWUPD_1.5.1 { + global: + fwupd_device_add_child; + local: *; +} LIBFWUPD_1.5.0; + +LIBFWUPD_1.5.2 { + global: + fwupd_client_download_file; + fwupd_client_get_user_agent; + local: *; +} LIBFWUPD_1.5.1; + +LIBFWUPD_1.5.3 { + global: + fwupd_client_get_main_context; + fwupd_client_set_main_context; + fwupd_remote_set_keyring_kind; + local: *; +} LIBFWUPD_1.5.2; + +LIBFWUPD_1.5.5 { + global: + fwupd_device_add_vendor_id; + fwupd_device_get_vendor_ids; + fwupd_device_has_vendor_id; + local: *; +} LIBFWUPD_1.5.3; + +LIBFWUPD_1.5.6 { + global: + fwupd_client_install_release2; + fwupd_client_install_release2_async; + fwupd_release_add_location; + fwupd_release_get_locations; + local: *; +} LIBFWUPD_1.5.5; + +LIBFWUPD_1.5.8 { + global: + fwupd_device_add_protocol; + fwupd_device_get_protocols; + fwupd_device_has_protocol; + local: *; +} LIBFWUPD_1.5.6; + +LIBFWUPD_1.6.0 { + global: + fwupd_device_get_composite_id; + fwupd_device_set_composite_id; + local: *; +} LIBFWUPD_1.5.8; + +LIBFWUPD_1.6.1 { + global: + fwupd_remote_set_filename_source; + fwupd_remote_setup; + fwupd_version_string; + local: *; +} LIBFWUPD_1.6.0; + +LIBFWUPD_1.6.2 { + global: + fwupd_device_get_version_build_date; + fwupd_device_has_icon; + fwupd_device_remove_child; + fwupd_device_set_version_build_date; + fwupd_remote_to_json; + fwupd_request_from_variant; + fwupd_request_get_created; + fwupd_request_get_device_id; + fwupd_request_get_id; + fwupd_request_get_image; + fwupd_request_get_kind; + fwupd_request_get_message; + fwupd_request_get_type; + fwupd_request_kind_from_string; + fwupd_request_kind_to_string; + fwupd_request_new; + fwupd_request_set_created; + fwupd_request_set_device_id; + fwupd_request_set_id; + fwupd_request_set_image; + fwupd_request_set_kind; + fwupd_request_set_message; + fwupd_request_to_string; + fwupd_request_to_variant; + local: *; +} LIBFWUPD_1.6.1; + +LIBFWUPD_1.7.0 { + global: + fwupd_security_attr_add_guid; + fwupd_security_attr_add_guids; + fwupd_security_attr_get_guids; + fwupd_security_attr_has_guid; + local: *; +} LIBFWUPD_1.6.2; + +LIBFWUPD_1.7.1 { + global: + fwupd_client_add_hint; + fwupd_client_get_host_security_events; + fwupd_client_get_host_security_events_async; + fwupd_client_get_host_security_events_finish; + fwupd_security_attr_copy; + fwupd_security_attr_flag_from_string; + fwupd_security_attr_from_json; + fwupd_security_attr_get_created; + fwupd_security_attr_get_result_fallback; + fwupd_security_attr_result_from_string; + fwupd_security_attr_set_created; + fwupd_security_attr_set_result_fallback; + local: *; +} LIBFWUPD_1.7.0; + +LIBFWUPD_1.7.2 { + global: + fwupd_release_get_id; + fwupd_release_set_id; + local: *; +} LIBFWUPD_1.7.1; + +LIBFWUPD_1.7.3 { + global: + fwupd_client_get_host_bkc; + fwupd_release_add_tag; + fwupd_release_get_tags; + fwupd_release_has_tag; + local: *; +} LIBFWUPD_1.7.2; + +LIBFWUPD_1.7.4 { + global: + fwupd_device_get_root; + local: *; +} LIBFWUPD_1.7.3; + +LIBFWUPD_1.7.6 { + global: + fwupd_device_add_issue; + fwupd_device_get_issues; + local: *; +} LIBFWUPD_1.7.4; + +LIBFWUPD_1.8.0 { + global: + fwupd_client_disconnect; + fwupd_client_get_only_trusted; + local: *; +} LIBFWUPD_1.7.6; + +LIBFWUPD_1.8.1 { + global: + fwupd_client_get_battery_level; + fwupd_client_get_battery_threshold; + fwupd_device_add_problem; + fwupd_device_get_battery_level; + fwupd_device_get_battery_threshold; + fwupd_device_get_problems; + fwupd_device_has_problem; + fwupd_device_problem_from_string; + fwupd_device_problem_to_string; + fwupd_device_remove_problem; + fwupd_device_set_battery_level; + fwupd_device_set_battery_threshold; + fwupd_device_set_problems; + local: *; +} LIBFWUPD_1.8.0; + +LIBFWUPD_1.8.2 { + global: + fwupd_client_get_host_vendor; + fwupd_device_to_json_full; + fwupd_remote_set_checksum; + fwupd_remote_set_filename_cache; + fwupd_security_attr_get_description; + fwupd_security_attr_get_title; + fwupd_security_attr_set_description; + fwupd_security_attr_set_title; + local: *; +} LIBFWUPD_1.8.1; + +LIBFWUPD_1.8.3 { + global: + fwupd_device_from_json; + fwupd_security_attr_remove_flag; + local: *; +} LIBFWUPD_1.8.2; + +LIBFWUPD_1.8.4 { + global: + fwupd_bios_setting_add_possible_value; + fwupd_bios_setting_array_from_variant; + fwupd_bios_setting_from_json; + fwupd_bios_setting_from_variant; + fwupd_bios_setting_get_current_value; + fwupd_bios_setting_get_description; + fwupd_bios_setting_get_id; + fwupd_bios_setting_get_kind; + fwupd_bios_setting_get_lower_bound; + fwupd_bios_setting_get_name; + fwupd_bios_setting_get_path; + fwupd_bios_setting_get_possible_values; + fwupd_bios_setting_get_read_only; + fwupd_bios_setting_get_scalar_increment; + fwupd_bios_setting_get_type; + fwupd_bios_setting_get_upper_bound; + fwupd_bios_setting_has_possible_value; + fwupd_bios_setting_map_possible_value; + fwupd_bios_setting_new; + fwupd_bios_setting_set_current_value; + fwupd_bios_setting_set_description; + fwupd_bios_setting_set_id; + fwupd_bios_setting_set_kind; + fwupd_bios_setting_set_lower_bound; + fwupd_bios_setting_set_name; + fwupd_bios_setting_set_path; + fwupd_bios_setting_set_read_only; + fwupd_bios_setting_set_scalar_increment; + fwupd_bios_setting_set_upper_bound; + fwupd_bios_setting_to_json; + fwupd_bios_setting_to_string; + fwupd_bios_setting_to_variant; + fwupd_client_get_bios_settings; + fwupd_client_get_bios_settings_async; + fwupd_client_get_bios_settings_finish; + fwupd_client_modify_bios_setting; + fwupd_client_modify_bios_setting_async; + fwupd_client_modify_bios_setting_finish; + fwupd_security_attr_get_bios_setting_current_value; + fwupd_security_attr_get_bios_setting_id; + fwupd_security_attr_get_bios_setting_target_value; + fwupd_security_attr_set_bios_setting_current_value; + fwupd_security_attr_set_bios_setting_id; + fwupd_security_attr_set_bios_setting_target_value; + local: *; +} LIBFWUPD_1.8.3; + +LIBFWUPD_1.8.6 { + global: + fwupd_request_add_flag; + fwupd_request_flag_from_string; + fwupd_request_flag_to_string; + fwupd_request_get_flags; + fwupd_request_has_flag; + fwupd_request_remove_flag; + fwupd_request_set_flags; + local: *; +} LIBFWUPD_1.8.4; diff --git a/fwupd-1.8.6/libfwupd/meson.build b/fwupd-1.8.6/libfwupd/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..07ce2eae7d0f9e069eaf51ca406e225fe4a4ff51 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/meson.build @@ -0,0 +1,247 @@ +if get_option('tests') +subdir('tests') +endif + +fwupd_version_h = configure_file( + input: 'fwupd-version.h.in', + output: 'fwupd-version.h', + configuration: conf +) + +install_headers( + 'fwupd.h', + subdir: 'fwupd-1', +) + +install_headers([ + 'fwupd-client.h', + 'fwupd-client-sync.h', + 'fwupd-common.h', + 'fwupd-deprecated.h', + 'fwupd-device.h', + 'fwupd-enums.h', + 'fwupd-error.h', + 'fwupd-remote.h', + 'fwupd-request.h', + 'fwupd-bios-setting.h', + 'fwupd-security-attr.h', + 'fwupd-release.h', + 'fwupd-plugin.h', + fwupd_version_h, + ], + subdir: 'fwupd-1/libfwupd', +) + +libfwupd_deps = [ + giounix, + gmodule, + libjcat, + libjsonglib, + libcurl, +] + +libfwupd_src = [ + 'fwupd-client.c', + 'fwupd-client-sync.c', + 'fwupd-common.c', # fuzzing + 'fwupd-device.c', # fuzzing + 'fwupd-enums.c', # fuzzing + 'fwupd-error.c', # fuzzing + 'fwupd-bios-setting.c', # fuzzing + 'fwupd-security-attr.c', # fuzzing + 'fwupd-release.c', # fuzzing + 'fwupd-plugin.c', + 'fwupd-remote.c', + 'fwupd-request.c', # fuzzing + 'fwupd-version.c', +] + +fwupd_mapfile = 'fwupd.map' +vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), fwupd_mapfile) +fwupd = library( + 'fwupd', + sources: libfwupd_src, + soversion: libfwupd_lt_current, + version: libfwupd_lt_version, + dependencies: libfwupd_deps, + c_args: [ + '-DG_LOG_DOMAIN="Fwupd"', + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + include_directories: root_incdir, + link_args: cc.get_supported_link_arguments([vflag]), + link_depends: fwupd_mapfile, + install: true +) + +libfwupd_dep = declare_dependency( + link_with: fwupd, + include_directories: [root_incdir, include_directories('.')], + dependencies: libfwupd_deps +) + +pkgg = import('pkgconfig') +pkgg.generate( + fwupd, + requires: [ 'gio-2.0' ], + subdirs: 'fwupd-1', + version: meson.project_version(), + name: 'fwupd', + filebase: 'fwupd', + description: 'fwupd is a system daemon for installing device firmware', +) + +if introspection.allowed() + fwupd_gir_deps = [ + giounix, + libcurl, + ] + fwupd_gir = gnome.generate_gir(fwupd, + sources: [ + 'fwupd-client.c', + 'fwupd-client.h', + 'fwupd-client-sync.c', + 'fwupd-client-sync.h', + 'fwupd-common.c', + 'fwupd-common.h', + 'fwupd-common-private.h', + 'fwupd-device.c', + 'fwupd-device.h', + 'fwupd-device-private.h', + 'fwupd-enums.c', + 'fwupd-enums.h', + 'fwupd-enums-private.h', + 'fwupd-error.c', + 'fwupd-error.h', + 'fwupd-bios-setting.c', + 'fwupd-bios-setting.h', + 'fwupd-bios-setting-private.h', + 'fwupd-security-attr.c', + 'fwupd-security-attr.h', + 'fwupd-security-attr-private.h', + 'fwupd-release.c', + 'fwupd-release.h', + 'fwupd-release-private.h', + 'fwupd-plugin.c', + 'fwupd-plugin.h', + 'fwupd-plugin-private.h', + 'fwupd-remote.c', + 'fwupd-remote.h', + 'fwupd-remote-private.h', + 'fwupd-request.c', + 'fwupd-request.h', + 'fwupd-request-private.h', + 'fwupd-version.c', + fwupd_version_h, + ], + nsversion: '2.0', + namespace: 'Fwupd', + symbol_prefix: 'fwupd', + identifier_prefix: 'Fwupd', + export_packages: 'fwupd', + header: 'fwupd.h', + dependencies: fwupd_gir_deps, + includes: [ + 'Gio-2.0', + 'GObject-2.0', + 'Json-1.0', + ], + install: true + ) + + gnome.generate_vapi('fwupd', + sources: fwupd_gir[0], + packages: ['gio-2.0', 'json-glib-1.0'], + install: true, + ) + + # Verify the map file is correct -- note we can't actually use the generated + # file for two reasons: + # + # 1. We don't hard depend on GObject Introspection + # 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 + # accidentally actually check in a version of the version script to git. + mapfile_target = custom_target('fwupd_mapfile', + input: fwupd_gir[0], + output: 'fwupd.map', + command: [ + python3, + join_paths(meson.project_source_root(), 'contrib', 'generate-version-script.py'), + 'LIBFWUPD', + '@INPUT@', + '@OUTPUT@', + ], + ) + test('fwupd-exported-api', diffcmd, + args: [ + '-urNp', + files('fwupd.map'), + mapfile_target, + ], + ) +endif + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'fwupd-self-test', + metadata_xml_gz_jcat, + sources: [ + 'fwupd-self-test.c' + ], + include_directories: [ + root_incdir, + ], + dependencies: [ + libfwupd_deps, + ], + link_with: fwupd, + c_args: [ + '-DG_LOG_DOMAIN="Fwupd"', + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + ) + test('fwupd-self-test', e, timeout: 60, env: env) + if run_sanitize_unsafe_tests and gio.version().version_compare ('>= 2.64.0') + e = executable( + 'fwupd-thread-test', + sources: [ + 'fwupd-thread-test.c' + ], + include_directories: [ + root_incdir, + ], + dependencies: [ + libfwupd_deps, + ], + link_with: fwupd, + c_args: [ + '-DG_LOG_DOMAIN="Fwupd"', + ], + ) + test('fwupd-thread-test', e, timeout: 60) + e = executable( + 'fwupd-context-test', + sources: [ + 'fwupd-context-test.c' + ], + include_directories: [ + root_incdir, + ], + dependencies: [ + libfwupd_deps, + ], + link_with: fwupd, + c_args: [ + '-DG_LOG_DOMAIN="Fwupd"', + ], + ) + test('fwupd-context-test', e, timeout: 60) + endif +endif + +fwupd_incdir = include_directories('.') diff --git a/fwupd-1.8.6/libfwupd/tests/auth.conf b/fwupd-1.8.6/libfwupd/tests/auth.conf new file mode 100644 index 0000000000000000000000000000000000000000..01167ebc64601500be7b02008be44fccd85edf49 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/auth.conf @@ -0,0 +1,11 @@ +[fwupd Remote] +Enabled=true +MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.gz +ReportURI=https://fwupd.org/lvfs/firmware/report +SecurityReportURI=https://fwupd.org/lvfs/hsireports/upload +AutomaticSecurityReports=true +Username=user +Password=pass +OrderBefore=before +OrderAfter=after +FirmwareBaseURI=https://my.fancy.cdn/ diff --git a/fwupd-1.8.6/libfwupd/tests/auth/meson.build b/fwupd-1.8.6/libfwupd/tests/auth/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..b635e92806a3a45153f1622c8e9d59ad6d35e423 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/auth/meson.build @@ -0,0 +1,23 @@ +jcat_tool = find_program('jcat-tool', required: false) + +if jcat_tool.found() +metadata_xml_gz_jcat = custom_target('metadata-xml-gz-jcat', + input: [ + 'metadata.xml.gz', + ], + output: 'metadata.xml.gz.jcat', + command: [ + jcat_tool, '--basename', '--appstream-id', 'localhost', 'self-sign', '@OUTPUT@', '@INPUT@', + ], +) +else +metadata_xml_gz_jcat = custom_target('metadata-xml-gz-jcat', + input: [ + 'metadata.xml.gz', + ], + output: 'metadata.xml.gz.jcat', + command: [ + 'touch', '@OUTPUT@', + ], +) +endif diff --git a/fwupd-1.8.6/libfwupd/tests/auth/metadata.xml.gz b/fwupd-1.8.6/libfwupd/tests/auth/metadata.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..3b18e512dba79e4c8300dd08aeb37f8e728b8dad --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/auth/metadata.xml.gz @@ -0,0 +1 @@ +hello world diff --git a/fwupd-1.8.6/libfwupd/tests/dell-esrt.conf b/fwupd-1.8.6/libfwupd/tests/dell-esrt.conf new file mode 120000 index 0000000000000000000000000000000000000000..074887625c3a93ab77b91b65b471363cc4dc47ec --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/dell-esrt.conf @@ -0,0 +1 @@ +../../plugins/dell-esrt/dell-esrt.conf \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupd/tests/disabled.conf b/fwupd-1.8.6/libfwupd/tests/disabled.conf new file mode 100644 index 0000000000000000000000000000000000000000..7fd7d46d0926c4b583c129ed2394cf491eed2b46 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/disabled.conf @@ -0,0 +1,4 @@ +[fwupd Remote] +Enabled=false +Keyring=none +Password= diff --git a/fwupd-1.8.6/libfwupd/tests/firmware-base-uri.conf b/fwupd-1.8.6/libfwupd/tests/firmware-base-uri.conf new file mode 100644 index 0000000000000000000000000000000000000000..6445335778e8e16b3c8ee30d2786cfe94f52399d --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/firmware-base-uri.conf @@ -0,0 +1,6 @@ +[fwupd Remote] +Enabled=true +Type=download +Keyring=jcat +MetadataURI=https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz +FirmwareBaseURI=https://my.fancy.cdn/ diff --git a/fwupd-1.8.6/libfwupd/tests/firmware-nopath.conf b/fwupd-1.8.6/libfwupd/tests/firmware-nopath.conf new file mode 100644 index 0000000000000000000000000000000000000000..cf0162e1be9e7bf1d870120719d3fb7aaaf19996 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/firmware-nopath.conf @@ -0,0 +1,5 @@ +[fwupd Remote] +Enabled=true +Type=download +Keyring=jcat +MetadataURI=https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz diff --git a/fwupd-1.8.6/libfwupd/tests/meson.build b/fwupd-1.8.6/libfwupd/tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..bb7024cc597ae6a0ac02f21950646cfc84227ec5 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/meson.build @@ -0,0 +1 @@ +subdir('auth') diff --git a/fwupd-1.8.6/libfwupd/tests/remotes.d b/fwupd-1.8.6/libfwupd/tests/remotes.d new file mode 120000 index 0000000000000000000000000000000000000000..958eb08404b21bf33d235dcd3ba0263e8e7d11c2 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/remotes.d @@ -0,0 +1 @@ +../../data/remotes.d/ \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupd/tests/stable.conf b/fwupd-1.8.6/libfwupd/tests/stable.conf new file mode 120000 index 0000000000000000000000000000000000000000..3619a13ec87faf39e3c94ddf57584e6db2da3ed4 --- /dev/null +++ b/fwupd-1.8.6/libfwupd/tests/stable.conf @@ -0,0 +1 @@ +../../src/tests/remotes.d/stable.conf \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/README.md b/fwupd-1.8.6/libfwupdplugin/README.md new file mode 100644 index 0000000000000000000000000000000000000000..857d17d22c5205c27ddca5d25e2c8543cb4c972e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/README.md @@ -0,0 +1,101 @@ +# libfwupdplugin + +This library is only partially API and ABI stable. Keeping unused, unsafe and +deprecated functions around forever is a maintenance burden and so symbols are +removed when branching for new minor versions. + +Use `./contrib/migrate.py` to migrate up out-of-tree plugins to the new API. + +Remember: Plugins should be upstream! + +## 1.5.5 + +* `fu_common_is_cpu_intel()`: Use `fu_common_get_cpu_vendor()` instead. +* `fu_firmware_strparse_uintXX()`: Use `fu_firmware_strparse_uintXX_safe()` instead. +* `fu_plugin_get_usb_context()`: Remove, as no longer required. +* `fu_plugin_set_usb_context()`: Remove, as no longer required. +* `fu_plugin_runner_usb_device_added()`: Use `fu_plugin_runner_backend_device_added()` instead. +* `fu_plugin_runner_udev_device_added()`: Use `fu_plugin_runner_backend_device_added()` instead. +* `fu_plugin_runner_udev_device_changed()`: Use `fu_plugin_runner_backend_device_added()` instead. +* `FuHidDevice->open()`: Use the `FuDevice` superclass instead. +* `FuHidDevice->close()`: Use the `FuDevice` superclass instead. +* `FuUsbDevice->probe()`: Use the `FuDevice` superclass instead. +* `FuUsbDevice->open()`: Use the `FuDevice` superclass instead. +* `FuUsbDevice->close()`: Use the `FuDevice` superclass instead. +* `FuUdevDevice->to_string()`: Use the `FuDevice` superclass instead. +* `FuUdevDevice->probe()`: Use the `FuDevice` superclass instead. +* `FuUdevDevice->open()`: Use the `FuDevice` superclass instead. +* `FuUdevDevice->close()`: Use the `FuDevice` superclass instead. + +## 1.5.6 + +* `fu_device_get_protocol()`: Use `fu_device_get_protocols()` instead. +* `fu_device_set_protocol()`: Use `fu_device_add_protocol()` instead. + +## 1.6.2 + +* `fu_device_has_custom_flag()`: Use `fu_device_has_private_flag()` instead. + +## 1.6.3 + +* `fu_device_sleep_with_progress()`: Use `fu_progress_sleep()` instead -- but be aware the unit of time has changed from *seconds* to *milliseconds*. +* `fu_device_get_status()`: Use `fu_progress_get_status()` instead. +* `fu_device_set_status()`: Use `fu_progress_set_status()` instead. +* `fu_device_get_progress()`: Use `fu_progress_get_percentage()` instead. +* `fu_device_set_progress_full()`: Use `fu_progress_set_percentage_full()` instead. +* `fu_device_set_progress()`: Use `fu_progress_set_steps()`, `fu_progress_add_step()` and `fu_progress_done()` -- see the `FuProgress` docs for more details! + +## 1.8.2 + +* `fu_udev_device_pread_full()`: Use `fu_udev_device_pread()` instead -- as the latter now specifies the buffer length. +* `fu_udev_device_pread_full()`: Use `fu_udev_device_pwrite()` instead -- as the latter now specifies the buffer length. +* `fu_udev_device_ioctl_full()`: Use `fu_udev_device_ioctl()` instead -- as the latter now always specifies the timeout. +* `fu_udev_device_new_full()`: Use `fu_udev_device_new()` instead -- as the latter always specifies the context. +* `fu_usb_device_new_full()`: Use `fu_usb_device_new()` instead -- as the latter always specifies the context. +* `fu_device_new_with_context()`: Use `fu_device_new()` instead -- as the latter always specifies the context. +* `fu_plugin_has_custom_flag()`: Use `fu_plugin_has_private_flag()` instead. +* `fu_efivar_secure_boot_enabled_full()`: Use `fu_efivar_secure_boot_enabled()` instead -- as the latter always specifies the error. +* `fu_progress_add_step()`: Add a 4th parameter to the function to specify the nice name for the step, or NULL. +* `fu_backend_setup()`: Now requires a `FuProgress`, although it can be ignored. +* `fu_backend_coldplug`: Now requires a `FuProgress`, although it can be ignored. +* `FuPluginVfuncs->setup`: Now requires a `FuProgress`, although it can be ignored. +* `FuPluginVfuncs->coldplug`: Now requires a `FuProgress`, although it can be ignored. +* `fu_common_crc*`: Use `fu_crc` prefix, i.e. remove the `_common` +* `fu_common_sum*`: Use `fu_sum` prefix, i.e. remove the `_common` +* `fu_byte_array_set_size_full()`: Use `fu_byte_array_set_size` instead -- as the latter now always specifies the fill char. +* `fu_common_string*`: Use `fu_string` prefix, i.e. remove the `_common` +* `fu_common_bytes*`: Use `fu_bytes` prefix, i.e. remove the `_common` +* `fu_common_set_contents_bytes()`: Use `fu_bytes_set_contents()` instead +* `fu_common_get_contents_bytes()`: Use `fu_bytes_get_contents()` instead +* `fu_common_read*`: Use `fu_memread` prefix, i.e. replace the `_common` with `_mem` +* `fu_common_write*`: Use `fu_memwrite` prefix, i.e. replace the `_common` with `_mem` +* `fu_common_bytes_compare_raw()`: Use `fu_memcmp_safe()` instead +* `fu_common_spawn_sync()`: Use `g_spawn_sync()` instead, or ideally not at all! +* `fu_common_extract_archive()`: Use `FuArchiveFirmware()` instead. +* `fu_common_instance_id_strsafe()`: Use `fu_device_add_instance_strsafe()` instead. +* `fu_common_kernel_locked_down()`: Use `fu_kernel_locked_down` instead. +* `fu_common_check_kernel_version()`: Use `fu_kernel_check_version` instead. +* `fu_common_get_firmware_search_path()`: Use `fu_kernel_get_firmware_search_path` instead. +* `fu_common_set_firmware_search_path()`: Use `fu_kernel_set_firmware_search_path` instead. +* `fu_common_reset_firmware_search_path()`: Use `fu_kernel_reset_firmware_search_path` instead. +* `fu_common_firmware_builder()`: You should not be using this. +* `fu_common_realpath()`: You should not be using this. +* `fu_common_uri_get_scheme()`: You should not be using this. +* `fu_common_dump*`: Use `fu_dump` prefix, i.e. remove the `_common` +* `fu_common_error_array_get_best()`: You should not be using this. +* `fu_common_cpuid()`: Use `fu_cpuid` instead. +* `fu_common_get_cpu_vendor()`: Use `fu_cpu_get_vendor` instead. +* `fu_common_vercmp_full()`: Use `fu_version_compare()` instead. +* `fu_common_version_ensure_semver()`: Use `fu_version_ensure_semver()` instead. +* `fu_common_version_from_uint*()`: Use `fu_version_from_uint*()` instead. +* `fu_common_strtoull()`: Use `fu_strtoull()` instead -- as the latter always specifies the error. +* `fu_smbios_to_string()`: Use `fu_firmware_to_string()` instead -- as `FuSmbios` is a `FuFirmware` superclass. +* `fu_common_cab_build_silo()`: You should not be using this. +* `fu_i2c_device_read_full()`: Use `fu_i2c_device_read` instead. +* `fu_i2c_device_write_full()`: Use `fu_i2c_device_write` instead. +* `fu_firmware_parse_full()`: Remove the `addr_end` parameter, and ensure that `offset` is a `gsize`. + +## 1.8.5 + +* `fu_volume_new_esp_default()`: Use `fu_context_get_esp_volumes()` instead. +* `fu_plugin_set_secure_config_value()`: Set `FWUPD_PLUGIN_FLAG_SECURE_CONFIG` and use `fu_plugin_set_config_value()` diff --git a/fwupd-1.8.6/libfwupdplugin/fu-archive-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-archive-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..8ddf3a6b0bf36362a9dd385ea186027e1210eb66 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-archive-firmware.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-archive-firmware.h" +#include "fu-archive.h" +#include "fu-common.h" + +/** + * FuArchiveFirmware: + * + * An archive firmware image, typically for nested firmware volumes. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + FuArchiveFormat format; + FuArchiveCompression compression; +} FuArchiveFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuArchiveFirmware, fu_archive_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_archive_firmware_get_instance_private(o)) + +static void +fu_archive_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuArchiveFirmware *self = FU_ARCHIVE_FIRMWARE(firmware); + FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kv(bn, "format", fu_archive_format_to_string(priv->format)); + fu_xmlb_builder_insert_kv(bn, + "compression", + fu_archive_compression_to_string(priv->compression)); +} + +static gboolean +fu_archive_firmware_parse_cb(FuArchive *self, + const gchar *filename, + GBytes *bytes, + gpointer user_data, + GError **error) +{ + FuFirmware *firmware = FU_FIRMWARE(user_data); + g_autoptr(FuFirmware) img = fu_firmware_new_from_bytes(bytes); + fu_firmware_set_id(img, filename); + fu_firmware_add_image(firmware, img); + return TRUE; +} + +static gboolean +fu_archive_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuArchive) archive = NULL; + + /* load archive */ + archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + /* decompress each image in the archive */ + return fu_archive_iterate(archive, fu_archive_firmware_parse_cb, firmware, error); +} + +/** + * fu_archive_firmware_get_format: + * @self: a #FuArchiveFirmware + * + * Gets the archive format. + * + * Returns: format + * + * Since: 1.8.1 + **/ +FuArchiveFormat +fu_archive_firmware_get_format(FuArchiveFirmware *self) +{ + FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_ARCHIVE_FIRMWARE(self), FU_ARCHIVE_FORMAT_UNKNOWN); + return priv->format; +} + +/** + * fu_archive_firmware_set_format: + * @self: a #FuArchiveFirmware + * @format: the archive format, e.g. %FU_ARCHIVE_FORMAT_ZIP + * + * Sets the archive format. + * + * Since: 1.8.1 + **/ +void +fu_archive_firmware_set_format(FuArchiveFirmware *self, FuArchiveFormat format) +{ + FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_ARCHIVE_FIRMWARE(self)); + priv->format = format; +} + +/** + * fu_archive_firmware_get_compression: + * @self: A #FuArchiveFirmware + * + * Returns the compression. + * + * Returns: compression + * + * Since: 1.8.1 + **/ +FuArchiveCompression +fu_archive_firmware_get_compression(FuArchiveFirmware *self) +{ + FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_ARCHIVE_FIRMWARE(self), FU_ARCHIVE_COMPRESSION_UNKNOWN); + return priv->compression; +} + +/** + * fu_archive_firmware_set_compression: + * @self: A #FuArchiveFirmware + * @compression: the compression, e.g. %FU_ARCHIVE_COMPRESSION_NONE + * + * Sets the compression. + * + * Since: 1.8.1 + **/ +void +fu_archive_firmware_set_compression(FuArchiveFirmware *self, FuArchiveCompression compression) +{ + FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_ARCHIVE_FIRMWARE(self)); + priv->compression = compression; +} + +static GBytes * +fu_archive_firmware_write(FuFirmware *firmware, GError **error) +{ + FuArchiveFirmware *self = FU_ARCHIVE_FIRMWARE(firmware); + FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* sanity check */ + if (priv->format == FU_ARCHIVE_FORMAT_UNKNOWN) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware archive format unspecified"); + return NULL; + } + if (priv->compression == FU_ARCHIVE_COMPRESSION_UNKNOWN) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware archive compression unspecified"); + return NULL; + } + + /* save archive and compress each image to the archive */ + archive = fu_archive_new(NULL, FU_ARCHIVE_FLAG_NONE, NULL); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = NULL; + + if (fu_firmware_get_id(img) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "image has no ID"); + return NULL; + } + blob = fu_firmware_get_bytes(img, error); + if (blob == NULL) + return NULL; + fu_archive_add_entry(archive, fu_firmware_get_id(img), blob); + } + return fu_archive_write(archive, priv->format, priv->compression, error); +} + +static gboolean +fu_archive_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuArchiveFirmware *self = FU_ARCHIVE_FIRMWARE(firmware); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "format", NULL); + if (tmp != NULL) { + FuArchiveFormat format = fu_archive_format_from_string(tmp); + if (format == FU_ARCHIVE_FORMAT_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "format %s not supported", + tmp); + return FALSE; + } + fu_archive_firmware_set_format(self, format); + } + tmp = xb_node_query_text(n, "compression", NULL); + if (tmp != NULL) { + FuArchiveCompression compression = fu_archive_compression_from_string(tmp); + if (compression == FU_ARCHIVE_COMPRESSION_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "compression %s not supported", + tmp); + return FALSE; + } + fu_archive_firmware_set_compression(self, compression); + } + + /* success */ + return TRUE; +} + +static void +fu_archive_firmware_init(FuArchiveFirmware *self) +{ +} + +static void +fu_archive_firmware_class_init(FuArchiveFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_archive_firmware_parse; + klass_firmware->write = fu_archive_firmware_write; + klass_firmware->build = fu_archive_firmware_build; + klass_firmware->export = fu_archive_firmware_export; +} + +/** + * fu_archive_firmware_new: + * + * Creates a new archive #FuFirmware + * + * Since: 1.7.3 + **/ +FuFirmware * +fu_archive_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ARCHIVE_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-archive-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-archive-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..83283e6eab11dbb2d8baa247f7e0d6d43cd67488 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-archive-firmware.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-archive.h" +#include "fu-firmware.h" + +#define FU_TYPE_ARCHIVE_FIRMWARE (fu_archive_firmware_get_type()) +#define FU_TYPE_ARCHIVE_FIRMWARE_RECORD (fu_archive_firmware_record_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuArchiveFirmware, fu_archive_firmware, FU, ARCHIVE_FIRMWARE, FuFirmware) + +struct _FuArchiveFirmwareClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_archive_firmware_new(void); +FuArchiveFormat +fu_archive_firmware_get_format(FuArchiveFirmware *self); +void +fu_archive_firmware_set_format(FuArchiveFirmware *self, FuArchiveFormat format); +FuArchiveCompression +fu_archive_firmware_get_compression(FuArchiveFirmware *self); +void +fu_archive_firmware_set_compression(FuArchiveFirmware *self, FuArchiveCompression compression); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-archive.c b/fwupd-1.8.6/libfwupdplugin/fu-archive.c new file mode 100644 index 0000000000000000000000000000000000000000..9f799de7623dc20d6e5af44d4bc12aec565b9dac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-archive.c @@ -0,0 +1,680 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuArchive" + +#include "config.h" + +#include + +#ifdef HAVE_LIBARCHIVE +#include +#include +#endif + +#include "fwupd-error.h" + +#include "fu-archive.h" + +/** + * FuArchive: + * + * An in-memory archive decompressor + */ + +struct _FuArchive { + GObject parent_instance; + GHashTable *entries; /* str:GBytes */ +}; + +G_DEFINE_TYPE(FuArchive, fu_archive, G_TYPE_OBJECT) + +/** + * fu_archive_format_to_string: + * @format: a format, e.g. %FU_ARCHIVE_FORMAT_ZIP + * + * Converts an enumerated format to a string. + * + * Returns: identifier string + * + * Since: 1.8.1 + **/ +const gchar * +fu_archive_format_to_string(FuArchiveFormat format) +{ + if (format == FU_ARCHIVE_FORMAT_UNKNOWN) + return "unknown"; + if (format == FU_ARCHIVE_FORMAT_CPIO) + return "cpio"; + if (format == FU_ARCHIVE_FORMAT_SHAR) + return "shar"; + if (format == FU_ARCHIVE_FORMAT_TAR) + return "tar"; + if (format == FU_ARCHIVE_FORMAT_USTAR) + return "ustar"; + if (format == FU_ARCHIVE_FORMAT_PAX) + return "pax"; + if (format == FU_ARCHIVE_FORMAT_GNUTAR) + return "gnutar"; + if (format == FU_ARCHIVE_FORMAT_ISO9660) + return "iso9660"; + if (format == FU_ARCHIVE_FORMAT_ZIP) + return "zip"; + if (format == FU_ARCHIVE_FORMAT_AR) + return "ar"; + if (format == FU_ARCHIVE_FORMAT_AR_SVR4) + return "ar-svr4"; + if (format == FU_ARCHIVE_FORMAT_MTREE) + return "mtree"; + if (format == FU_ARCHIVE_FORMAT_RAW) + return "raw"; + if (format == FU_ARCHIVE_FORMAT_XAR) + return "xar"; + if (format == FU_ARCHIVE_FORMAT_7ZIP) + return "7zip"; + if (format == FU_ARCHIVE_FORMAT_WARC) + return "warc"; + return NULL; +} + +/** + * fu_archive_format_from_string: + * @format: (nullable): a string, e.g. `zip` + * + * Converts a string to an enumerated format. + * + * Returns: enumerated value + * + * Since: 1.8.1 + **/ +FuArchiveFormat +fu_archive_format_from_string(const gchar *format) +{ + if (g_strcmp0(format, "unknown") == 0) + return FU_ARCHIVE_FORMAT_UNKNOWN; + if (g_strcmp0(format, "cpio") == 0) + return FU_ARCHIVE_FORMAT_CPIO; + if (g_strcmp0(format, "shar") == 0) + return FU_ARCHIVE_FORMAT_SHAR; + if (g_strcmp0(format, "tar") == 0) + return FU_ARCHIVE_FORMAT_TAR; + if (g_strcmp0(format, "ustar") == 0) + return FU_ARCHIVE_FORMAT_USTAR; + if (g_strcmp0(format, "pax") == 0) + return FU_ARCHIVE_FORMAT_PAX; + if (g_strcmp0(format, "gnutar") == 0) + return FU_ARCHIVE_FORMAT_GNUTAR; + if (g_strcmp0(format, "iso9660") == 0) + return FU_ARCHIVE_FORMAT_ISO9660; + if (g_strcmp0(format, "zip") == 0) + return FU_ARCHIVE_FORMAT_ZIP; + if (g_strcmp0(format, "ar") == 0) + return FU_ARCHIVE_FORMAT_AR; + if (g_strcmp0(format, "ar-svr4") == 0) + return FU_ARCHIVE_FORMAT_AR_SVR4; + if (g_strcmp0(format, "mtree") == 0) + return FU_ARCHIVE_FORMAT_MTREE; + if (g_strcmp0(format, "raw") == 0) + return FU_ARCHIVE_FORMAT_RAW; + if (g_strcmp0(format, "xar") == 0) + return FU_ARCHIVE_FORMAT_XAR; + if (g_strcmp0(format, "7zip") == 0) + return FU_ARCHIVE_FORMAT_7ZIP; + if (g_strcmp0(format, "warc") == 0) + return FU_ARCHIVE_FORMAT_WARC; + return FU_ARCHIVE_FORMAT_UNKNOWN; +} + +/** + * fu_archive_compression_to_string: + * @compression: a compression, e.g. %FU_ARCHIVE_COMPRESSION_ZIP + * + * Converts an enumerated compression to a string. + * + * Returns: identifier string + * + * Since: 1.8.1 + **/ +const gchar * +fu_archive_compression_to_string(FuArchiveCompression compression) +{ + if (compression == FU_ARCHIVE_COMPRESSION_UNKNOWN) + return "unknown"; + if (compression == FU_ARCHIVE_COMPRESSION_NONE) + return "none"; + if (compression == FU_ARCHIVE_COMPRESSION_GZIP) + return "gzip"; + if (compression == FU_ARCHIVE_COMPRESSION_BZIP2) + return "bzip2"; + if (compression == FU_ARCHIVE_COMPRESSION_COMPRESS) + return "compress"; + if (compression == FU_ARCHIVE_COMPRESSION_LZMA) + return "lzma"; + if (compression == FU_ARCHIVE_COMPRESSION_XZ) + return "xz"; + if (compression == FU_ARCHIVE_COMPRESSION_UU) + return "uuencode"; + if (compression == FU_ARCHIVE_COMPRESSION_LZIP) + return "lzip"; + if (compression == FU_ARCHIVE_COMPRESSION_LRZIP) + return "lrzip"; + if (compression == FU_ARCHIVE_COMPRESSION_LZOP) + return "lzop"; + if (compression == FU_ARCHIVE_COMPRESSION_GRZIP) + return "grzip"; + if (compression == FU_ARCHIVE_COMPRESSION_LZ4) + return "lz4"; + if (compression == FU_ARCHIVE_COMPRESSION_ZSTD) + return "zstd"; + return NULL; +} + +/** + * fu_archive_compression_from_string: + * @compression: (nullable): a string, e.g. `zip` + * + * Converts a string to an enumerated compression. + * + * Returns: enumerated value + * + * Since: 1.8.1 + **/ +FuArchiveCompression +fu_archive_compression_from_string(const gchar *compression) +{ + if (g_strcmp0(compression, "unknown") == 0) + return FU_ARCHIVE_COMPRESSION_UNKNOWN; + if (g_strcmp0(compression, "none") == 0) + return FU_ARCHIVE_COMPRESSION_NONE; + if (g_strcmp0(compression, "gzip") == 0) + return FU_ARCHIVE_COMPRESSION_GZIP; + if (g_strcmp0(compression, "bzip2") == 0) + return FU_ARCHIVE_COMPRESSION_BZIP2; + if (g_strcmp0(compression, "compress") == 0) + return FU_ARCHIVE_COMPRESSION_COMPRESS; + if (g_strcmp0(compression, "lzma") == 0) + return FU_ARCHIVE_COMPRESSION_LZMA; + if (g_strcmp0(compression, "xz") == 0) + return FU_ARCHIVE_COMPRESSION_XZ; + if (g_strcmp0(compression, "uuencode") == 0) + return FU_ARCHIVE_COMPRESSION_UU; + if (g_strcmp0(compression, "lzip") == 0) + return FU_ARCHIVE_COMPRESSION_LZIP; + if (g_strcmp0(compression, "lrzip") == 0) + return FU_ARCHIVE_COMPRESSION_LRZIP; + if (g_strcmp0(compression, "lzop") == 0) + return FU_ARCHIVE_COMPRESSION_LZOP; + if (g_strcmp0(compression, "grzip") == 0) + return FU_ARCHIVE_COMPRESSION_GRZIP; + if (g_strcmp0(compression, "lz4") == 0) + return FU_ARCHIVE_COMPRESSION_LZ4; + if (g_strcmp0(compression, "zstd") == 0) + return FU_ARCHIVE_COMPRESSION_ZSTD; + return FU_ARCHIVE_COMPRESSION_UNKNOWN; +} + +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_add_entry: + * @self: a #FuArchive + * @fn: (not nullable): a filename + * @blob: (not nullable): a #GBytes + * + * Adds, or replaces an entry to an archive. + * + * Since: 1.8.1 + **/ +void +fu_archive_add_entry(FuArchive *self, const gchar *fn, GBytes *blob) +{ + g_return_if_fail(FU_IS_ARCHIVE(self)); + g_return_if_fail(fn != NULL); + g_return_if_fail(blob != NULL); + g_hash_table_insert(self->entries, g_strdup(fn), g_bytes_ref(blob)); +} + +/** + * fu_archive_lookup_by_fn: + * @self: a #FuArchive + * @fn: a filename + * @error: (nullable): optional return location for an error + * + * Finds the blob referenced by filename + * + * Returns: (transfer none): a #GBytes, or %NULL if the filename was not found + * + * Since: 1.2.2 + **/ +GBytes * +fu_archive_lookup_by_fn(FuArchive *self, const gchar *fn, GError **error) +{ + GBytes *bytes; + + 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); + + bytes = g_hash_table_lookup(self->entries, fn); + if (bytes == NULL) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no blob for %s", fn); + } + return bytes; +} + +/** + * fu_archive_iterate: + * @self: a #FuArchive + * @callback: (scope call): a #FuArchiveIterateFunc. + * @user_data: user data + * @error: (nullable): optional return location for an error + * + * Iterates over the archive contents, calling the given function for each + * of the files found. If any @callback returns %FALSE scanning is aborted. + * + * Returns: True if no @callback returned FALSE + * + * Since: 1.3.4 + */ +gboolean +fu_archive_iterate(FuArchive *self, + FuArchiveIterateFunc callback, + gpointer user_data, + GError **error) +{ + GHashTableIter iter; + gpointer key, value; + + g_return_val_if_fail(FU_IS_ARCHIVE(self), FALSE); + g_return_val_if_fail(callback != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_hash_table_iter_init(&iter, self->entries); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (!callback(self, (const gchar *)key, (GBytes *)value, user_data, error)) + return FALSE; + } + return TRUE; +} + +#ifdef HAVE_LIBARCHIVE +/* 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) + +typedef struct archive _archive_write_ctx; + +static void +_archive_write_ctx_free(_archive_write_ctx *arch) +{ + archive_write_close(arch); + archive_write_free(arch); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_archive_write_ctx, _archive_write_ctx_free) + +typedef struct archive_entry _archive_entry_ctx; + +static void +_archive_entry_ctx_free(_archive_entry_ctx *entry) +{ + archive_entry_free(entry); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_archive_entry_ctx, _archive_entry_ctx_free) + +static void +fu_archive_set_format(_archive_write_ctx *arch, FuArchiveFormat format) +{ + if (format == FU_ARCHIVE_FORMAT_CPIO) + archive_write_set_format_cpio(arch); + if (format == FU_ARCHIVE_FORMAT_SHAR) + archive_write_set_format_shar(arch); + if (format == FU_ARCHIVE_FORMAT_TAR) + archive_write_set_format_pax_restricted(arch); + if (format == FU_ARCHIVE_FORMAT_USTAR) + archive_write_set_format_ustar(arch); + if (format == FU_ARCHIVE_FORMAT_PAX) + archive_write_set_format_pax(arch); + if (format == FU_ARCHIVE_FORMAT_GNUTAR) + archive_write_set_format_gnutar(arch); + if (format == FU_ARCHIVE_FORMAT_ISO9660) + archive_write_set_format_iso9660(arch); + if (format == FU_ARCHIVE_FORMAT_ZIP) + archive_write_set_format_zip(arch); + if (format == FU_ARCHIVE_FORMAT_AR) + archive_write_set_format_ar_bsd(arch); + if (format == FU_ARCHIVE_FORMAT_AR_SVR4) + archive_write_set_format_ar_svr4(arch); + if (format == FU_ARCHIVE_FORMAT_MTREE) + archive_write_set_format_mtree(arch); + if (format == FU_ARCHIVE_FORMAT_RAW) + archive_write_set_format_raw(arch); + if (format == FU_ARCHIVE_FORMAT_XAR) + archive_write_set_format_xar(arch); + if (format == FU_ARCHIVE_FORMAT_7ZIP) + archive_write_set_format_7zip(arch); + if (format == FU_ARCHIVE_FORMAT_WARC) + archive_write_set_format_warc(arch); +} + +static void +fu_archive_set_compression(_archive_write_ctx *arch, FuArchiveCompression compression) +{ + if (compression == FU_ARCHIVE_COMPRESSION_BZIP2) + archive_write_add_filter_bzip2(arch); + if (compression == FU_ARCHIVE_COMPRESSION_COMPRESS) + archive_write_add_filter_compress(arch); + if (compression == FU_ARCHIVE_COMPRESSION_GRZIP) + archive_write_add_filter_grzip(arch); + if (compression == FU_ARCHIVE_COMPRESSION_GZIP) + archive_write_add_filter_gzip(arch); + if (compression == FU_ARCHIVE_COMPRESSION_LRZIP) + archive_write_add_filter_lrzip(arch); + if (compression == FU_ARCHIVE_COMPRESSION_LZ4) + archive_write_add_filter_lz4(arch); + if (compression == FU_ARCHIVE_COMPRESSION_LZIP) + archive_write_add_filter_lzip(arch); + if (compression == FU_ARCHIVE_COMPRESSION_LZMA) + archive_write_add_filter_lzma(arch); + if (compression == FU_ARCHIVE_COMPRESSION_LZOP) + archive_write_add_filter_lzop(arch); + if (compression == FU_ARCHIVE_COMPRESSION_UU) + archive_write_add_filter_uuencode(arch); + if (compression == FU_ARCHIVE_COMPRESSION_XZ) + archive_write_add_filter_xz(arch); +#ifdef HAVE_LIBARCHIVE_WRITE_ADD_COMPRESSION_ZSTD + if (compression == FU_ARCHIVE_COMPRESSION_ZSTD) + archive_write_add_filter_zstd(arch); +#endif +} +#endif + +static gboolean +fu_archive_load(FuArchive *self, GBytes *blob, FuArchiveFlags flags, GError **error) +{ +#ifdef HAVE_LIBARCHIVE + 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; + g_autoptr(GBytes) bytes = 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); + bytes = g_bytes_new_take(g_steal_pointer(&buf), bufsz); + fu_archive_add_entry(self, fn_key, bytes); + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing libarchive support"); + return FALSE; +#endif +} + +/** + * fu_archive_new: + * @data: (nullable): archive contents + * @flags: archive flags, e.g. %FU_ARCHIVE_FLAG_NONE + * @error: (nullable): optional return location for an error + * + * Parses @data as an archive and decompresses all files to memory blobs. + * + * If @data is unspecified then a new empty archive is created. + * + * Returns: a #FuArchive, or %NULL if the archive was invalid in any way. + * + * Since: 1.2.2 + **/ +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(error == NULL || *error == NULL, NULL); + if (data != NULL) { + if (!fu_archive_load(self, data, flags, error)) + return NULL; + } + return g_steal_pointer(&self); +} + +#ifdef HAVE_LIBARCHIVE +static gssize +fu_archive_write_cb(struct archive *arch, void *user_data, const void *buf, gsize bufsz) +{ + GByteArray *blob = (GByteArray *)user_data; + g_byte_array_append(blob, buf, bufsz); + return (gssize)bufsz; +} +#endif + +/** + * fu_archive_write: + * @self: a #FuArchive + * @format: a compression, e.g. `FU_ARCHIVE_FORMAT_ZIP` + * @compression: a compression, e.g. `FU_ARCHIVE_COMPRESSION_NONE` + * @error: (nullable): optional return location for an error + * + * Writes an archive with specified @format and @compression. + * + * Returns: (transfer full): the archive blob + * + * Since: 1.8.1 + **/ +GBytes * +fu_archive_write(FuArchive *self, + FuArchiveFormat format, + FuArchiveCompression compression, + GError **error) +{ +#ifdef HAVE_LIBARCHIVE + int r; + g_autoptr(_archive_write_ctx) arch = NULL; + g_autoptr(GByteArray) blob = g_byte_array_new(); + g_autoptr(GList) keys = NULL; + + g_return_val_if_fail(FU_IS_ARCHIVE(self), NULL); + g_return_val_if_fail(format != FU_ARCHIVE_FORMAT_UNKNOWN, NULL); + g_return_val_if_fail(compression != FU_ARCHIVE_COMPRESSION_UNKNOWN, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* sanity check */ +#ifndef HAVE_LIBARCHIVE_WRITE_ADD_COMPRESSION_ZSTD + if (compression == FU_ARCHIVE_COMPRESSION_ZSTD) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "archive_write_add_filter_zstd() not supported"); + return NULL; + } +#endif + + /* compress anything matching either glob */ + arch = archive_write_new(); + if (arch == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "libarchive startup failed"); + return NULL; + } + fu_archive_set_compression(arch, compression); + fu_archive_set_format(arch, format); + r = archive_write_open(arch, blob, NULL, fu_archive_write_cb, NULL); + if (r != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot open: %s", + archive_error_string(arch)); + return NULL; + } + + keys = g_hash_table_get_keys(self->entries); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *fn = l->data; + GBytes *bytes = g_hash_table_lookup(self->entries, fn); + gssize rc; + g_autoptr(_archive_entry_ctx) entry = NULL; + + entry = archive_entry_new(); + archive_entry_set_pathname(entry, fn); + archive_entry_set_filetype(entry, AE_IFREG); + archive_entry_set_perm(entry, 0644); + archive_entry_set_size(entry, g_bytes_get_size(bytes)); + + r = archive_write_header(arch, entry); + if (r != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot write header: %s", + archive_error_string(arch)); + return NULL; + } + rc = archive_write_data(arch, + g_bytes_get_data(bytes, NULL), + g_bytes_get_size(bytes)); + if (rc < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot write data: %s", + archive_error_string(arch)); + return NULL; + } + } + + r = archive_write_close(arch); + if (r != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot close: %s", + archive_error_string(arch)); + return NULL; + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&blob)); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing libarchive support"); + return NULL; +#endif +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-archive.h b/fwupd-1.8.6/libfwupdplugin/fu-archive.h new file mode 100644 index 0000000000000000000000000000000000000000..23a210b61096338fc1100a308cf1305ff7ea98d4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-archive.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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; + +/** + * FuArchiveFormat: + * @FU_ARCHIVE_FORMAT_UNKNOWN: unknown + * @FU_ARCHIVE_FORMAT_CPIO: ASCII cpio + * @FU_ARCHIVE_FORMAT_SHAR: shar + * @FU_ARCHIVE_FORMAT_TAR: tar + * @FU_ARCHIVE_FORMAT_USTAR: POSIX ustar + * @FU_ARCHIVE_FORMAT_PAX: restricted POSIX pax interchange + * @FU_ARCHIVE_FORMAT_GNUTAR: GNU tar + * @FU_ARCHIVE_FORMAT_ISO9660: ISO9660 + * @FU_ARCHIVE_FORMAT_ZIP: ZIP + * @FU_ARCHIVE_FORMAT_AR: ar (BSD) + * @FU_ARCHIVE_FORMAT_AR_SVR4: ar (GNU/SVR4) + * @FU_ARCHIVE_FORMAT_MTREE: mtree + * @FU_ARCHIVE_FORMAT_RAW: raw + * @FU_ARCHIVE_FORMAT_XAR: xar + * @FU_ARCHIVE_FORMAT_7ZIP: 7-Zip + * @FU_ARCHIVE_FORMAT_WARC: WARC + * + * The archive format. + **/ +typedef enum { + FU_ARCHIVE_FORMAT_UNKNOWN, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_CPIO, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_SHAR, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_TAR, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_USTAR, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_PAX, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_GNUTAR, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_ISO9660, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_ZIP, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_AR, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_AR_SVR4, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_MTREE, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_RAW, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_XAR, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_7ZIP, /* Since: 1.8.1 */ + FU_ARCHIVE_FORMAT_WARC, /* Since: 1.8.1 */ +} FuArchiveFormat; + +/** + * FuArchiveCompression: + * @FU_ARCHIVE_COMPRESSION_UNKNOWN: unknown + * @FU_ARCHIVE_COMPRESSION_NONE: none + * @FU_ARCHIVE_COMPRESSION_GZIP: Gzip (GNU Zip) + * @FU_ARCHIVE_COMPRESSION_BZIP2: Bzip2 + * @FU_ARCHIVE_COMPRESSION_COMPRESS: compress (LZW) + * @FU_ARCHIVE_COMPRESSION_LZMA: LZMA + * @FU_ARCHIVE_COMPRESSION_XZ: XZ + * @FU_ARCHIVE_COMPRESSION_UU: Unix-to-Unix + * @FU_ARCHIVE_COMPRESSION_LZIP: LZip (LZMA) + * @FU_ARCHIVE_COMPRESSION_LRZIP: Long Range Zip (LZMA RZIP) + * @FU_ARCHIVE_COMPRESSION_LZOP: LZO + * @FU_ARCHIVE_COMPRESSION_GRZIP: GRZip + * @FU_ARCHIVE_COMPRESSION_LZ4: LZ4 + * @FU_ARCHIVE_COMPRESSION_ZSTD: Zstd + * + * The archive compression. + **/ +typedef enum { + FU_ARCHIVE_COMPRESSION_UNKNOWN, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_NONE, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_GZIP, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_BZIP2, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_COMPRESS, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_LZMA, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_XZ, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_UU, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_LZIP, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_LRZIP, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_LZOP, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_GRZIP, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_LZ4, /* Since: 1.8.1 */ + FU_ARCHIVE_COMPRESSION_ZSTD, /* Since: 1.8.1 */ +} FuArchiveCompression; + +/** + * FuArchiveIterateFunc: + * @self: a #FuArchive + * @filename: a filename + * @bytes: the blob referenced by @filename + * @user_data: user data + * @error: a #GError or NULL + * + * The archive iteration callback. + */ +typedef gboolean (*FuArchiveIterateFunc)(FuArchive *self, + const gchar *filename, + GBytes *bytes, + gpointer user_data, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +const gchar * +fu_archive_format_to_string(FuArchiveFormat format) G_GNUC_WARN_UNUSED_RESULT; +FuArchiveFormat +fu_archive_format_from_string(const gchar *format) G_GNUC_WARN_UNUSED_RESULT; + +FuArchiveCompression +fu_archive_compression_from_string(const gchar *compression) G_GNUC_WARN_UNUSED_RESULT; +const gchar * +fu_archive_compression_to_string(FuArchiveCompression compression) G_GNUC_WARN_UNUSED_RESULT; + +FuArchive * +fu_archive_new(GBytes *data, FuArchiveFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_archive_add_entry(FuArchive *self, const gchar *fn, GBytes *blob); +GBytes * +fu_archive_lookup_by_fn(FuArchive *self, const gchar *fn, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_archive_write(FuArchive *self, + FuArchiveFormat format, + FuArchiveCompression compression, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_archive_iterate(FuArchive *self, + FuArchiveIterateFunc callback, + gpointer user_data, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-backend-private.h b/fwupd-1.8.6/libfwupdplugin/fu-backend-private.h new file mode 100644 index 0000000000000000000000000000000000000000..497870fff14ce0541c96d678569be594dc2bac17 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-backend-private.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-backend.h" + +gboolean +fu_backend_load(FuBackend *self, + JsonObject *json_object, + const gchar *tag, + FuBackendLoadFlags flags, + GError **error); +gboolean +fu_backend_save(FuBackend *self, + JsonBuilder *json_builder, + const gchar *tag, + FuBackendSaveFlags flags, + GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-backend.c b/fwupd-1.8.6/libfwupdplugin/fu-backend.c new file mode 100644 index 0000000000000000000000000000000000000000..e8402228e74e9ee8b1b504ccfe4bea6adb1d3b7c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-backend.c @@ -0,0 +1,627 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuBackend" + +#include "config.h" + +#include "fu-backend-private.h" +#include "fu-string.h" + +/** + * FuBackend: + * + * An device discovery backend, for instance USB, BlueZ or UDev. + * + * See also: [class@FuDevice] + */ + +typedef struct { + FuContext *ctx; + gchar *name; + gboolean enabled; + gboolean done_setup; + gboolean can_invalidate; + GHashTable *devices; /* device_id : * FuDevice */ + GThread *thread_init; +} FuBackendPrivate; + +enum { SIGNAL_ADDED, SIGNAL_REMOVED, SIGNAL_CHANGED, SIGNAL_LAST }; + +enum { PROP_0, PROP_NAME, PROP_CAN_INVALIDATE, PROP_CONTEXT, PROP_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FuBackend, fu_backend, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fu_backend_get_instance_private(o)) + +/** + * fu_backend_device_added: + * @self: a #FuBackend + * @device: a device + * + * Emits a signal that indicates the device has been added. + * + * Since: 1.6.1 + **/ +void +fu_backend_device_added(FuBackend *self, FuDevice *device) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_BACKEND(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + g_return_if_fail(priv->thread_init == g_thread_self()); + + /* assign context if set */ + if (priv->ctx != NULL) + fu_device_set_context(device, priv->ctx); + + /* add */ + g_hash_table_insert(priv->devices, + g_strdup(fu_device_get_backend_id(device)), + g_object_ref(device)); + g_signal_emit(self, signals[SIGNAL_ADDED], 0, device); +} + +/** + * fu_backend_device_removed: + * @self: a #FuBackend + * @device: a device + * + * Emits a signal that indicates the device has been removed. + * + * Since: 1.6.1 + **/ +void +fu_backend_device_removed(FuBackend *self, FuDevice *device) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_BACKEND(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + g_return_if_fail(priv->thread_init == g_thread_self()); + g_signal_emit(self, signals[SIGNAL_REMOVED], 0, device); + g_hash_table_remove(priv->devices, fu_device_get_backend_id(device)); +} + +/** + * fu_backend_device_changed: + * @self: a #FuBackend + * @device: a device + * + * Emits a signal that indicates the device has been changed. + * + * Since: 1.6.1 + **/ +void +fu_backend_device_changed(FuBackend *self, FuDevice *device) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_BACKEND(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + g_return_if_fail(priv->thread_init == g_thread_self()); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0, device); +} + +/** + * fu_backend_registered: + * @self: a #FuBackend + * @device: a device + * + * Calls the ->registered() vfunc for the backend. This allows the backend to perform shared + * backend actions on superclassed devices. + * + * Since: 1.7.4 + **/ +void +fu_backend_registered(FuBackend *self, FuDevice *device) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + g_return_if_fail(FU_IS_BACKEND(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + if (klass->registered != NULL) + klass->registered(self, device); +} + +/** + * fu_backend_invalidate: + * @self: a #FuBackend + * + * Normally when calling [method@FuBackend.setup] multiple times it is only actually done once. + * Calling this method causes the next requests to [method@FuBackend.setup] to actually probe the + * hardware. + * + * Only subclassed backends setting `can-invalidate=TRUE` at construction time can use this + * method, as it is not always safe to call for backends shared between multiple plugins. + * + * This should be done in case the backing information source has changed, for instance if + * a platform subsystem has been updated. + * + * This also optionally calls the ->invalidate() vfunc for the backend. This allows the backend + * to perform shared backend actions on superclassed devices. + * + * Since: 1.8.0 + **/ +void +fu_backend_invalidate(FuBackend *self) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + FuBackendPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_BACKEND(self)); + g_return_if_fail(priv->can_invalidate); + + priv->done_setup = FALSE; + if (klass->invalidate != NULL) + klass->invalidate(self); +} + +/** + * fu_backend_add_string: + * @self: a #FuBackend + * @idt: indent level + * @str: a string to append to + * + * Add backend-specific device metadata to an existing string. + * + * Since: 1.8.4 + **/ +void +fu_backend_add_string(FuBackend *self, guint idt, GString *str) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + FuBackendPrivate *priv = GET_PRIVATE(self); + + fu_string_append(str, idt, G_OBJECT_TYPE_NAME(self), ""); + if (priv->name != NULL) + fu_string_append(str, idt + 1, "Name", priv->name); + fu_string_append_kb(str, idt + 1, "Enabled", priv->enabled); + fu_string_append_kb(str, idt + 1, "DoneSetup", priv->done_setup); + fu_string_append_kb(str, idt + 1, "CanInvalidate", priv->can_invalidate); + + /* subclassed */ + if (klass->to_string != NULL) + klass->to_string(self, idt, str); +} + +/** + * fu_backend_setup: + * @self: a #FuBackend + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Sets up the backend ready for use, which typically calls the subclassed setup + * function. No devices should be added or removed at this point. + * + * Returns: %TRUE for success + * + * Since: 1.6.1 + **/ +gboolean +fu_backend_setup(FuBackend *self, FuProgress *progress, GError **error) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + FuBackendPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_BACKEND(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (priv->done_setup) + return TRUE; + if (klass->setup != NULL) { + if (!klass->setup(self, progress, error)) { + priv->enabled = FALSE; + return FALSE; + } + } + priv->done_setup = TRUE; + return TRUE; +} + +/** + * fu_backend_load: + * @self: a #FuBackend + * @json_object: a #JsonObject + * @tag: a string backend tag, or %NULL + * @flags: %FuBackendLoadFlags, typically `FU_BACKEND_LOAD_FLAG_NONE` + * @error: (nullable): optional return location for an error + * + * Loads the backend from a JSON object. + * + * Returns: %TRUE for success + * + * Since: 1.8.5 + **/ +gboolean +fu_backend_load(FuBackend *self, + JsonObject *json_object, + const gchar *tag, + FuBackendLoadFlags flags, + GError **error) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_BACKEND(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optional */ + if (klass->load != NULL) { + if (!klass->load(self, json_object, tag, flags, error)) + return FALSE; + } + return TRUE; +} + +/** + * fu_backend_save: + * @self: a #FuBackend + * @json_builder: a #JsonBuilder + * @tag: a string backend tag, or %NULL + * @flags: %FuBackendSaveFlags, typically `FU_BACKEND_SAVE_FLAG_NONE` + * @error: (nullable): optional return location for an error + * + * Saves the backend to a JSON builder. + * + * Returns: %TRUE for success + * + * Since: 1.8.5 + **/ +gboolean +fu_backend_save(FuBackend *self, + JsonBuilder *json_builder, + const gchar *tag, + FuBackendSaveFlags flags, + GError **error) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_BACKEND(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optional */ + if (klass->save != NULL) { + if (!klass->save(self, json_builder, tag, flags, error)) + return FALSE; + } + return TRUE; +} + +/** + * fu_backend_coldplug: + * @self: a #FuBackend + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Adds devices using the subclassed backend. If [method@FuBackend.setup] has not + * already been called then it is run before this function automatically. + * + * Returns: %TRUE for success + * + * Since: 1.6.1 + **/ +gboolean +fu_backend_coldplug(FuBackend *self, FuProgress *progress, GError **error) +{ + FuBackendClass *klass = FU_BACKEND_GET_CLASS(self); + g_return_val_if_fail(FU_IS_BACKEND(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + if (!fu_backend_setup(self, progress, error)) + return FALSE; + if (klass->coldplug == NULL) + return TRUE; + return klass->coldplug(self, progress, error); +} + +/** + * fu_backend_get_name: + * @self: a #FuBackend + * + * Return the name of the backend, which is normally set by the subclass. + * + * Returns: backend name + * + * Since: 1.6.1 + **/ +const gchar * +fu_backend_get_name(FuBackend *self) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_BACKEND(self), NULL); + return priv->name; +} + +/** + * fu_backend_get_context: + * @self: a #FuBackend + * + * Gets the context for a backend. + * + * Returns: (transfer none): a #FuContext or %NULL if not set + * + * Since: 1.6.1 + **/ +FuContext * +fu_backend_get_context(FuBackend *self) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + return priv->ctx; +} + +/** + * fu_backend_get_enabled: + * @self: a #FuBackend + * + * Return the boolean value of a key if it's been configured + * + * Returns: %TRUE if the backend is enabled + * + * Since: 1.6.1 + **/ +gboolean +fu_backend_get_enabled(FuBackend *self) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_BACKEND(self), FALSE); + return priv->enabled; +} + +/** + * fu_backend_set_enabled: + * @self: a #FuBackend + * @enabled: enabled state + * + * Sets the backend enabled state. + * + * Since: 1.6.1 + **/ +void +fu_backend_set_enabled(FuBackend *self, gboolean enabled) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_BACKEND(self)); + priv->enabled = FALSE; +} + +/** + * fu_backend_lookup_by_id: + * @self: a #FuBackend + * @device_id: a DeviceID + * + * Gets a device previously added by the backend. + * + * Returns: (transfer none): device, or %NULL if not found + * + * Since: 1.6.1 + **/ +FuDevice * +fu_backend_lookup_by_id(FuBackend *self, const gchar *device_id) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_BACKEND(self), NULL); + return g_hash_table_lookup(priv->devices, device_id); +} + +static gint +fu_backend_get_devices_sort_cb(gconstpointer a, gconstpointer b) +{ + FuDevice *deva = *((FuDevice **)a); + FuDevice *devb = *((FuDevice **)b); + return g_strcmp0(fu_device_get_backend_id(deva), fu_device_get_backend_id(devb)); +} + +/** + * fu_backend_get_devices: + * @self: a #FuBackend + * + * Gets all the devices added by the backend. + * + * Returns: (transfer container) (element-type FuDevice): devices + * + * Since: 1.6.1 + **/ +GPtrArray * +fu_backend_get_devices(FuBackend *self) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + g_autoptr(GList) values = NULL; + g_autoptr(GPtrArray) devices = NULL; + + g_return_val_if_fail(FU_IS_BACKEND(self), NULL); + + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + values = g_hash_table_get_values(priv->devices); + for (GList *l = values; l != NULL; l = l->next) + g_ptr_array_add(devices, g_object_ref(l->data)); + g_ptr_array_sort(devices, fu_backend_get_devices_sort_cb); + return g_steal_pointer(&devices); +} + +static void +fu_backend_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuBackend *self = FU_BACKEND(object); + FuBackendPrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_NAME: + g_value_set_string(value, priv->name); + break; + case PROP_CAN_INVALIDATE: + g_value_set_boolean(value, priv->can_invalidate); + break; + case PROP_CONTEXT: + g_value_set_object(value, priv->ctx); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_backend_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuBackend *self = FU_BACKEND(object); + FuBackendPrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_NAME: + priv->name = g_value_dup_string(value); + break; + case PROP_CAN_INVALIDATE: + priv->can_invalidate = g_value_get_boolean(value); + break; + case PROP_CONTEXT: + g_set_object(&priv->ctx, g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_backend_init(FuBackend *self) +{ + FuBackendPrivate *priv = GET_PRIVATE(self); + priv->enabled = TRUE; + priv->thread_init = g_thread_self(); + priv->devices = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_object_unref); +} + +static void +fu_backend_dispose(GObject *object) +{ + FuBackend *self = FU_BACKEND(object); + FuBackendPrivate *priv = GET_PRIVATE(self); + g_hash_table_remove_all(priv->devices); + G_OBJECT_CLASS(fu_backend_parent_class)->dispose(object); +} + +static void +fu_backend_finalize(GObject *object) +{ + FuBackend *self = FU_BACKEND(object); + FuBackendPrivate *priv = GET_PRIVATE(self); + if (priv->ctx != NULL) + g_object_unref(priv->ctx); + g_free(priv->name); + g_hash_table_unref(priv->devices); + G_OBJECT_CLASS(fu_backend_parent_class)->finalize(object); +} + +static void +fu_backend_class_init(FuBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->get_property = fu_backend_get_property; + object_class->set_property = fu_backend_set_property; + object_class->finalize = fu_backend_finalize; + object_class->dispose = fu_backend_dispose; + + /** + * FuBackend:name: + * + * The backend name. + * + * Since: 1.6.1 + */ + pspec = + g_param_spec_string("name", + NULL, + NULL, + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_NAME, pspec); + + /** + * FuBackend:can-invalidate: + * + * If the backend can be invalidated. + * + * Since: 1.8.0 + */ + pspec = + g_param_spec_boolean("can-invalidate", + NULL, + NULL, + FALSE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_CAN_INVALIDATE, pspec); + + /** + * FuBackend:context: + * + * The #FuContent to use. + * + * Since: 1.6.1 + */ + pspec = + g_param_spec_object("context", + NULL, + NULL, + FU_TYPE_CONTEXT, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_CONTEXT, pspec); + + /** + * FuBackend::device-added: + * @self: the #FuBackend instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-added signal is emitted when a device has been added. + * + * Since: 1.6.1 + **/ + signals[SIGNAL_ADDED] = g_signal_new("device-added", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuBackend::device-removed: + * @self: the #FuBackend instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-removed signal is emitted when a device has been removed. + * + * Since: 1.6.1 + **/ + signals[SIGNAL_REMOVED] = g_signal_new("device-removed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuBackend::device-changed: + * @self: the #FuBackend instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-changed signal is emitted when a device has been changed. + * + * Since: 1.6.1 + **/ + signals[SIGNAL_CHANGED] = g_signal_new("device-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-backend.h b/fwupd-1.8.6/libfwupdplugin/fu-backend.h new file mode 100644 index 0000000000000000000000000000000000000000..4233496ec5437342a925cf65948090afe59e3f13 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-backend.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-context.h" +#include "fu-device.h" + +#define FU_TYPE_BACKEND (fu_backend_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuBackend, fu_backend, FU, BACKEND, GObject) + +typedef enum { + FU_BACKEND_LOAD_FLAG_NONE, +} FuBackendLoadFlags; + +typedef enum { + FU_BACKEND_SAVE_FLAG_NONE, +} FuBackendSaveFlags; + +struct _FuBackendClass { + GObjectClass parent_class; + /* signals */ + gboolean (*setup)(FuBackend *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*coldplug)(FuBackend *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + void (*registered)(FuBackend *self, FuDevice *device); + void (*invalidate)(FuBackend *self); + void (*to_string)(FuBackend *self, guint indent, GString *str); + gboolean (*load)(FuBackend *self, + JsonObject *json_object, + const gchar *tag, + FuBackendLoadFlags flags, + GError **error); + gboolean (*save)(FuBackend *self, + JsonBuilder *json_builder, + const gchar *tag, + FuBackendSaveFlags flags, + GError **error); +}; + +const gchar * +fu_backend_get_name(FuBackend *self); +FuContext * +fu_backend_get_context(FuBackend *self); +gboolean +fu_backend_get_enabled(FuBackend *self); +void +fu_backend_set_enabled(FuBackend *self, gboolean enabled); +GPtrArray * +fu_backend_get_devices(FuBackend *self); +FuDevice * +fu_backend_lookup_by_id(FuBackend *self, const gchar *device_id); +gboolean +fu_backend_setup(FuBackend *self, FuProgress *progress, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_backend_coldplug(FuBackend *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_backend_device_added(FuBackend *self, FuDevice *device); +void +fu_backend_device_removed(FuBackend *self, FuDevice *device); +void +fu_backend_device_changed(FuBackend *self, FuDevice *device); +void +fu_backend_registered(FuBackend *self, FuDevice *device); +void +fu_backend_invalidate(FuBackend *self); +void +fu_backend_add_string(FuBackend *self, guint idt, GString *str); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bios-settings-private.h b/fwupd-1.8.6/libfwupdplugin/fu-bios-settings-private.h new file mode 100644 index 0000000000000000000000000000000000000000..e0695b8ad68828332052652ed0b721d172cbb5f8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bios-settings-private.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-bios-settings.h" + +gboolean +fu_bios_settings_setup(FuBiosSettings *self, GError **error); + +GPtrArray * +fu_bios_settings_get_all(FuBiosSettings *self); + +GVariant * +fu_bios_settings_to_variant(FuBiosSettings *self, gboolean trusted); +gboolean +fu_bios_settings_from_json(FuBiosSettings *self, JsonNode *json_node, GError **error); +gboolean +fu_bios_settings_from_json_file(FuBiosSettings *self, const gchar *fn, GError **error); +GHashTable * +fu_bios_settings_to_hash_kv(FuBiosSettings *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bios-settings.c b/fwupd-1.8.6/libfwupdplugin/fu-bios-settings.c new file mode 100644 index 0000000000000000000000000000000000000000..b06bbe47ffb19e0b8ff555a17bab957d6addce98 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bios-settings.c @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuBiosSettings" + +#include "config.h" + +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-error.h" + +#include "fu-bios-settings-private.h" +#include "fu-path.h" +#include "fu-string.h" + +struct _FuBiosSettings { + GObject parent_instance; + GHashTable *descriptions; + GHashTable *read_only; + GPtrArray *attrs; +}; + +G_DEFINE_TYPE(FuBiosSettings, fu_bios_settings, G_TYPE_OBJECT) + +static void +fu_bios_settings_finalize(GObject *obj) +{ + FuBiosSettings *self = FU_BIOS_SETTINGS(obj); + g_ptr_array_unref(self->attrs); + g_hash_table_unref(self->descriptions); + g_hash_table_unref(self->read_only); + G_OBJECT_CLASS(fu_bios_settings_parent_class)->finalize(obj); +} + +static void +fu_bios_settings_class_init(FuBiosSettingsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_bios_settings_finalize; +} + +static gboolean +fu_bios_setting_get_key(FwupdBiosSetting *attr, const gchar *key, gchar **value_out, GError **error) +{ + g_autofree gchar *tmp = NULL; + + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(attr), FALSE); + g_return_val_if_fail(&value_out != NULL, FALSE); + + tmp = g_build_filename(fwupd_bios_setting_get_path(attr), key, NULL); + if (!g_file_get_contents(tmp, value_out, NULL, error)) { + g_prefix_error(error, "failed to load %s: ", key); + return FALSE; + } + g_strchomp(*value_out); + return TRUE; +} + +static gboolean +fu_bios_setting_set_description(FuBiosSettings *self, FwupdBiosSetting *attr, GError **error) +{ + g_autofree gchar *data = NULL; + const gchar *value; + + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(attr), FALSE); + + /* Try ID, then name, and then key */ + value = g_hash_table_lookup(self->descriptions, fwupd_bios_setting_get_id(attr)); + if (value != NULL) { + fwupd_bios_setting_set_description(attr, value); + return TRUE; + } + value = g_hash_table_lookup(self->descriptions, fwupd_bios_setting_get_name(attr)); + if (value != NULL) { + fwupd_bios_setting_set_description(attr, value); + return TRUE; + } + if (!fu_bios_setting_get_key(attr, "display_name", &data, error)) + return FALSE; + fwupd_bios_setting_set_description(attr, data); + + return TRUE; +} + +static guint64 +fu_bios_setting_get_key_as_integer(FwupdBiosSetting *attr, const gchar *key, GError **error) +{ + g_autofree gchar *str = NULL; + guint64 tmp; + + if (!fu_bios_setting_get_key(attr, key, &str, error)) + return G_MAXUINT64; + if (!fu_strtoull(str, &tmp, 0, G_MAXUINT64, error)) { + g_prefix_error(error, "failed to convert %s to integer: ", key); + return G_MAXUINT64; + } + return tmp; +} + +static gboolean +fu_bios_setting_set_enumeration_attrs(FwupdBiosSetting *attr, GError **error) +{ + const gchar *delimiters[] = {",", ";", NULL}; + g_autofree gchar *str = NULL; + + if (!fu_bios_setting_get_key(attr, "possible_values", &str, error)) + return FALSE; + for (guint j = 0; delimiters[j] != NULL; j++) { + g_auto(GStrv) vals = NULL; + if (g_strrstr(str, delimiters[j]) == NULL) + continue; + vals = fu_strsplit(str, strlen(str), delimiters[j], -1); + if (vals[0] != NULL) + fwupd_bios_setting_set_kind(attr, FWUPD_BIOS_SETTING_KIND_ENUMERATION); + for (guint i = 0; vals[i] != NULL && vals[i][0] != '\0'; i++) + fwupd_bios_setting_add_possible_value(attr, vals[i]); + } + return TRUE; +} + +static gboolean +fu_bios_setting_set_string_attrs(FwupdBiosSetting *attr, GError **error) +{ + guint64 tmp; + + tmp = fu_bios_setting_get_key_as_integer(attr, "min_length", error); + if (tmp == G_MAXUINT64) + return FALSE; + fwupd_bios_setting_set_lower_bound(attr, tmp); + tmp = fu_bios_setting_get_key_as_integer(attr, "max_length", error); + if (tmp == G_MAXUINT64) + return FALSE; + fwupd_bios_setting_set_upper_bound(attr, tmp); + fwupd_bios_setting_set_kind(attr, FWUPD_BIOS_SETTING_KIND_STRING); + return TRUE; +} + +static gboolean +fu_bios_setting_set_integer_attrs(FwupdBiosSetting *attr, GError **error) +{ + guint64 tmp; + + tmp = fu_bios_setting_get_key_as_integer(attr, "min_value", error); + if (tmp == G_MAXUINT64) + return FALSE; + fwupd_bios_setting_set_lower_bound(attr, tmp); + tmp = fu_bios_setting_get_key_as_integer(attr, "max_value", error); + if (tmp == G_MAXUINT64) + return FALSE; + fwupd_bios_setting_set_upper_bound(attr, tmp); + tmp = fu_bios_setting_get_key_as_integer(attr, "scalar_increment", error); + if (tmp == G_MAXUINT64) + return FALSE; + fwupd_bios_setting_set_scalar_increment(attr, tmp); + fwupd_bios_setting_set_kind(attr, FWUPD_BIOS_SETTING_KIND_INTEGER); + return TRUE; +} + +static gboolean +fu_bios_setting_set_current_value(FwupdBiosSetting *attr, GError **error) +{ + g_autofree gchar *str = NULL; + + if (!fu_bios_setting_get_key(attr, "current_value", &str, error)) + return FALSE; + fwupd_bios_setting_set_current_value(attr, str); + return TRUE; +} + +#define LENOVO_POSSIBLE_NEEDLE "[Optional:" +#define LENOVO_READ_ONLY_NEEDLE "[Status:ShowOnly]" +#define LENOVO_EXCLUDED "[Excluded from boot order:" + +static void +fu_bios_setting_set_read_only(FuBiosSettings *self, FwupdBiosSetting *attr) +{ + if (fwupd_bios_setting_get_kind(attr) == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + const gchar *value = + g_hash_table_lookup(self->read_only, fwupd_bios_setting_get_id(attr)); + if (g_strcmp0(value, fwupd_bios_setting_get_current_value(attr)) == 0) + fwupd_bios_setting_set_read_only(attr, TRUE); + } +} + +static gboolean +fu_bios_setting_fixup_lenovo_thinklmi_bug(FwupdBiosSetting *attr, GError **error) +{ + const gchar *current_value = fwupd_bios_setting_get_current_value(attr); + const gchar *tmp; + g_autoptr(GString) str = NULL; + g_autoptr(GString) right_str = NULL; + g_auto(GStrv) vals = NULL; + + if (g_getenv("FWUPD_BIOS_SETTING_VERBOSE") != NULL) { + g_debug("Processing %s: (%s)", + fwupd_bios_setting_get_name(attr), + fwupd_bios_setting_get_current_value(attr)); + } + + /* We have read only */ + tmp = g_strrstr(current_value, LENOVO_READ_ONLY_NEEDLE); + if (tmp != NULL) { + fwupd_bios_setting_set_read_only(attr, TRUE); + str = g_string_new_len(current_value, tmp - current_value); + } else { + str = g_string_new(current_value); + } + + /* empty string */ + if (str->len == 0) + return TRUE; + + /* split into left and right */ + vals = fu_strsplit(str->str, str->len, ";", 2); + + /* use left half for current value */ + fwupd_bios_setting_set_current_value(attr, vals[0]); + if (vals[1] == NULL) + return TRUE; + + /* use the right half to process further */ + right_str = g_string_new(vals[1]); + + /* Strip boot order exclusion info */ + tmp = g_strrstr(right_str->str, LENOVO_EXCLUDED); + if (tmp != NULL) + g_string_truncate(str, tmp - right_str->str); + + /* Look for possible values to populate */ + tmp = g_strrstr(right_str->str, LENOVO_POSSIBLE_NEEDLE); + if (tmp != NULL) { + g_auto(GStrv) possible_vals = NULL; + g_string_erase(right_str, 0, strlen(LENOVO_POSSIBLE_NEEDLE)); + possible_vals = fu_strsplit(right_str->str, right_str->len, ",", -1); + if (possible_vals[0] != NULL) + fwupd_bios_setting_set_kind(attr, FWUPD_BIOS_SETTING_KIND_ENUMERATION); + for (guint i = 0; possible_vals[i] != NULL && possible_vals[i][0] != '\0'; i++) { + /* last string */ + if (possible_vals[i + 1] == NULL && + g_strrstr(possible_vals[i], "]") != NULL) { + g_auto(GStrv) stripped_vals = fu_strsplit(possible_vals[i], + strlen(possible_vals[i]), + "]", + -1); + fwupd_bios_setting_add_possible_value(attr, stripped_vals[0]); + continue; + } + fwupd_bios_setting_add_possible_value(attr, possible_vals[i]); + } + } + return TRUE; +} + +static gboolean +fu_bios_settings_run_folder_fixups(FwupdBiosSetting *attr, GError **error) +{ + if (fwupd_bios_setting_get_kind(attr) == FWUPD_BIOS_SETTING_KIND_UNKNOWN) + return fu_bios_setting_fixup_lenovo_thinklmi_bug(attr, error); + return TRUE; +} + +static gboolean +fu_bios_setting_set_type(FuBiosSettings *self, FwupdBiosSetting *attr, GError **error) +{ + gboolean kernel_bug = FALSE; + g_autofree gchar *data = NULL; + g_autoptr(GError) error_key = NULL; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), FALSE); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(attr), FALSE); + + /* lenovo thinklmi seems to be missing it even though it's mandatory :/ */ + if (!fu_bios_setting_get_key(attr, "type", &data, &error_key)) { +#if GLIB_CHECK_VERSION(2, 64, 0) + g_warning_once("KERNEL BUG: 'type' attribute not exported: (%s)", + error_key->message); +#else + g_debug("KERNEL BUG: 'type' attribute not exported: (%s)", error_key->message); +#endif + kernel_bug = TRUE; + } + + if (g_strcmp0(data, "enumeration") == 0 || kernel_bug) { + if (!fu_bios_setting_set_enumeration_attrs(attr, &error_local)) { + if (g_getenv("FWUPD_BIOS_SETTING_VERBOSE") != NULL) + g_debug("failed to add enumeration attrs: %s", + error_local->message); + } + } else if (g_strcmp0(data, "integer") == 0) { + if (!fu_bios_setting_set_integer_attrs(attr, &error_local)) { + if (g_getenv("FWUPD_BIOS_SETTING_VERBOSE") != NULL) + g_debug("failed to add integer attrs: %s", error_local->message); + } + } else if (g_strcmp0(data, "string") == 0) { + if (!fu_bios_setting_set_string_attrs(attr, &error_local)) { + if (g_getenv("FWUPD_BIOS_SETTING_VERBOSE") != NULL) + g_debug("failed to add string attrs: %s", error_local->message); + } + } + return TRUE; +} + +/* Special case attribute that is a file not a folder + * https://github.com/torvalds/linux/blob/v5.18/Documentation/ABI/testing/sysfs-class-firmware-attributes#L300 + */ +static gboolean +fu_bios_setting_set_file_attributes(FuBiosSettings *self, FwupdBiosSetting *attr, GError **error) +{ + g_autofree gchar *value = NULL; + + if (g_strcmp0(fwupd_bios_setting_get_name(attr), FWUPD_BIOS_SETTING_PENDING_REBOOT) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s attribute is not supported", + fwupd_bios_setting_get_name(attr)); + return FALSE; + } + if (!fu_bios_setting_set_description(self, attr, error)) + return FALSE; + if (!fu_bios_setting_get_key(attr, NULL, &value, error)) + return FALSE; + fwupd_bios_setting_set_current_value(attr, value); + fwupd_bios_setting_set_read_only(attr, TRUE); + + return TRUE; +} + +static gboolean +fu_bios_settings_set_folder_attributes(FuBiosSettings *self, FwupdBiosSetting *attr, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + if (!fu_bios_setting_set_type(self, attr, error)) + return FALSE; + if (!fu_bios_setting_set_current_value(attr, error)) + return FALSE; + if (!fu_bios_setting_set_description(self, attr, &error_local)) + g_debug("%s", error_local->message); + if (!fu_bios_settings_run_folder_fixups(attr, error)) + return FALSE; + fu_bios_setting_set_read_only(self, attr); + return TRUE; +} + +static gboolean +fu_bios_settings_populate_attribute(FuBiosSettings *self, + const gchar *driver, + const gchar *path, + const gchar *name, + GError **error) +{ + g_autoptr(FwupdBiosSetting) attr = NULL; + g_autofree gchar *id = NULL; + + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), FALSE); + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(driver != NULL, FALSE); + + attr = fwupd_bios_setting_new(name, path); + + id = g_strdup_printf("com.%s.%s", driver, name); + fwupd_bios_setting_set_id(attr, id); + + if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + if (!fu_bios_settings_set_folder_attributes(self, attr, error)) + return FALSE; + } else { + if (!fu_bios_setting_set_file_attributes(self, attr, error)) + return FALSE; + } + + g_ptr_array_add(self->attrs, g_object_ref(attr)); + + return TRUE; +} + +static void +fu_bios_settings_populate_descriptions(FuBiosSettings *self) +{ + g_return_if_fail(FU_IS_BIOS_SETTINGS(self)); + + g_hash_table_insert(self->descriptions, + g_strdup("pending_reboot"), + /* TRANSLATORS: description of a BIOS setting */ + g_strdup(_("Settings will apply after system reboots"))); + g_hash_table_insert(self->descriptions, + g_strdup("com.thinklmi.WindowsUEFIFirmwareUpdate"), + /* TRANSLATORS: description of a BIOS setting */ + g_strdup(_("BIOS updates delivered via LVFS or Windows Update"))); +} + +static void +fu_bios_settings_populate_read_only(FuBiosSettings *self) +{ + g_return_if_fail(FU_IS_BIOS_SETTINGS(self)); + + g_hash_table_insert(self->read_only, + g_strdup("com.thinklmi.SecureBoot"), + g_strdup(_("Enable"))); + g_hash_table_insert(self->read_only, + g_strdup("com.dell-wmi-sysman.SecureBoot"), + g_strdup(_("Enabled"))); +} + +static void +fu_bios_settings_combination_fixups(FuBiosSettings *self) +{ + FwupdBiosSetting *thinklmi_sb = fu_bios_settings_get_attr(self, "com.thinklmi.SecureBoot"); + FwupdBiosSetting *thinklmi_3rd = + fu_bios_settings_get_attr(self, "com.thinklmi.Allow3rdPartyUEFICA"); + + if (thinklmi_sb != NULL && thinklmi_3rd != NULL) { + const gchar *val = fwupd_bios_setting_get_current_value(thinklmi_3rd); + if (g_strcmp0(val, "Disable") == 0) { + g_debug("Disabling changing %s since %s is %s", + fwupd_bios_setting_get_name(thinklmi_sb), + fwupd_bios_setting_get_name(thinklmi_3rd), + val); + fwupd_bios_setting_set_read_only(thinklmi_sb, TRUE); + } + } +} + +/** + * fu_bios_settings_setup: + * @self: a #FuBiosSettings + * + * Clears all attributes and re-initializes them. + * Mostly used for the test suite, but could potentially be connected to udev + * events for drivers being loaded or unloaded too. + * + * Since: 1.8.4 + **/ +gboolean +fu_bios_settings_setup(FuBiosSettings *self, GError **error) +{ + guint count = 0; + g_autofree gchar *sysfsfwdir = NULL; + g_autoptr(GDir) class_dir = NULL; + + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), FALSE); + + if (self->attrs->len > 0) { + g_debug("re-initializing attributes"); + g_ptr_array_set_size(self->attrs, 0); + } + if (g_hash_table_size(self->descriptions) == 0) + fu_bios_settings_populate_descriptions(self); + + if (g_hash_table_size(self->read_only) == 0) + fu_bios_settings_populate_read_only(self); + + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW_ATTRIB); + class_dir = g_dir_open(sysfsfwdir, 0, error); + if (class_dir == NULL) + return FALSE; + + do { + g_autofree gchar *path = NULL; + g_autoptr(GDir) driver_dir = NULL; + const gchar *driver = g_dir_read_name(class_dir); + if (driver == NULL) + break; + path = g_build_filename(sysfsfwdir, driver, "attributes", NULL); + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) { + g_debug("skipping non-directory %s", path); + continue; + } + driver_dir = g_dir_open(path, 0, error); + if (driver_dir == NULL) + return FALSE; + do { + const gchar *name = g_dir_read_name(driver_dir); + g_autofree gchar *full_path = NULL; + g_autoptr(GError) error_local = NULL; + if (name == NULL) + break; + full_path = g_build_filename(path, name, NULL); + if (!fu_bios_settings_populate_attribute(self, + driver, + full_path, + name, + &error_local)) { + if (g_error_matches(error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("%s is not supported", name); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } while (++count); + } while (TRUE); + g_debug("loaded %u BIOS settings", count); + + fu_bios_settings_combination_fixups(self); + + return TRUE; +} + +static void +fu_bios_settings_init(FuBiosSettings *self) +{ + self->attrs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + self->descriptions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + self->read_only = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +} + +/** + * fu_bios_settings_get_attr: + * @self: a #FuBiosSettings + * @val: the attribute ID or name to check for + * + * Returns: (transfer none): the attribute with the given ID or name or NULL if it doesn't exist. + * + * Since: 1.8.4 + **/ +FwupdBiosSetting * +fu_bios_settings_get_attr(FuBiosSettings *self, const gchar *val) +{ + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), NULL); + g_return_val_if_fail(val != NULL, NULL); + + for (guint i = 0; i < self->attrs->len; i++) { + FwupdBiosSetting *attr = g_ptr_array_index(self->attrs, i); + const gchar *tmp_id = fwupd_bios_setting_get_id(attr); + const gchar *tmp_name = fwupd_bios_setting_get_name(attr); + if (g_strcmp0(val, tmp_id) == 0 || g_strcmp0(val, tmp_name) == 0) + return attr; + } + return NULL; +} + +/** + * fu_bios_settings_get_all: + * @self: a #FuBiosSettings + * + * Gets all the attributes in the object. + * + * Returns: (transfer container) (element-type FwupdBiosSetting): attributes + * + * Since: 1.8.4 + **/ +GPtrArray * +fu_bios_settings_get_all(FuBiosSettings *self) +{ + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), NULL); + return g_ptr_array_ref(self->attrs); +} + +/** + * fu_bios_settings_get_pending_reboot: + * @self: a #FuBiosSettings + * @result: (out): Whether a reboot is pending + * @error: (nullable): optional return location for an error + * + * Determines if the system will apply changes to attributes upon reboot + * + * Since: 1.8.4 + **/ +gboolean +fu_bios_settings_get_pending_reboot(FuBiosSettings *self, gboolean *result, GError **error) +{ + FwupdBiosSetting *attr; + g_autofree gchar *data = NULL; + guint64 val = 0; + + g_return_val_if_fail(result != NULL, FALSE); + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), FALSE); + + for (guint i = 0; i < self->attrs->len; i++) { + const gchar *tmp; + + attr = g_ptr_array_index(self->attrs, i); + tmp = fwupd_bios_setting_get_name(attr); + if (g_strcmp0(tmp, FWUPD_BIOS_SETTING_PENDING_REBOOT) == 0) + break; + attr = NULL; + } + + if (attr == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find pending reboot attribute"); + return FALSE; + } + + /* refresh/re-read */ + if (!fu_bios_setting_get_key(attr, NULL, &data, error)) + return FALSE; + fwupd_bios_setting_set_current_value(attr, data); + if (!fu_strtoull(data, &val, 0, G_MAXUINT32, error)) + return FALSE; + + *result = (val == 1); + + return TRUE; +} + +/** + * fu_bios_settings_to_variant: + * @self: a #FuBiosSettings + * @trusted: whether the caller should receive trusted values + * + * Serializes the #FwupdBiosSetting objects. + * + * Returns: a #GVariant or %NULL + * + * Since: 1.8.4 + **/ +GVariant * +fu_bios_settings_to_variant(FuBiosSettings *self, gboolean trusted) +{ + GVariantBuilder builder; + + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + for (guint i = 0; i < self->attrs->len; i++) { + FwupdBiosSetting *bios_setting = g_ptr_array_index(self->attrs, i); + g_variant_builder_add_value(&builder, + fwupd_bios_setting_to_variant(bios_setting, trusted)); + } + return g_variant_new("(aa{sv})", &builder); +} + +/** + * fu_bios_settings_from_json: + * @self: a #FuBiosSettings + * @json_node: a Json node to parse from + * @error: (nullable): optional return location for an error + * + * Loads #FwupdBiosSetting objects from a JSON node. + * + * Returns: TRUE if the objects were imported + * + * Since: 1.8.4 + **/ +gboolean +fu_bios_settings_from_json(FuBiosSettings *self, JsonNode *json_node, GError **error) +{ + JsonArray *array; + JsonObject *obj; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + obj = json_node_get_object(json_node); + + /* this has to exist */ + if (!json_object_has_member(obj, "BiosSettings")) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no BiosSettings property in object"); + return FALSE; + } + array = json_object_get_array_member(obj, "BiosSettings"); + for (guint i = 0; i < json_array_get_length(array); i++) { + JsonNode *node_tmp = json_array_get_element(array, i); + g_autoptr(FwupdBiosSetting) attr = fwupd_bios_setting_new(NULL, NULL); + if (!fwupd_bios_setting_from_json(attr, node_tmp, error)) + return FALSE; + g_ptr_array_add(self->attrs, g_steal_pointer(&attr)); + } + + /* success */ + return TRUE; +} + +/** + * fu_bios_settings_from_json_file: + * @self: A #FuBiosSettings + * @fn: a path to a JSON file + * @error: (nullable): optional return location for an error + * + * Adds all BIOS attributes from a JSON filename + * + * Returns: TRUE for success, FALSE for failure + * + * Since: 1.8.4 + **/ +gboolean +fu_bios_settings_from_json_file(FuBiosSettings *self, const gchar *fn, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + + if (!g_file_get_contents(fn, &data, NULL, error)) + return FALSE; + if (!json_parser_load_from_data(parser, data, -1, error)) { + g_prefix_error(error, "%s doesn't look like JSON data: ", fn); + return FALSE; + } + return fu_bios_settings_from_json(self, json_parser_get_root(parser), error); +} + +/** + * fu_bios_settings_to_hash_kv: + * @self: A #FuBiosSettings + * + * Creates a #GHashTable with the name and current value of + * all BIOS settings. + * + * Returns: (transfer full): name/value pairs + * Since: 1.8.4 + **/ +GHashTable * +fu_bios_settings_to_hash_kv(FuBiosSettings *self) +{ + GHashTable *bios_settings = NULL; + + g_return_val_if_fail(self != NULL, NULL); + + bios_settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + for (guint i = 0; i < self->attrs->len; i++) { + FwupdBiosSetting *item_setting = g_ptr_array_index(self->attrs, i); + g_hash_table_insert(bios_settings, + g_strdup(fwupd_bios_setting_get_id(item_setting)), + g_strdup(fwupd_bios_setting_get_current_value(item_setting))); + } + return bios_settings; +} + +/** + * fu_bios_settings_new: + * + * Returns: #FuBiosSettings + * + * Since: 1.8.4 + **/ +FuBiosSettings * +fu_bios_settings_new(void) +{ + return g_object_new(FU_TYPE_FIRMWARE_ATTRS, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bios-settings.h b/fwupd-1.8.6/libfwupdplugin/fu-bios-settings.h new file mode 100644 index 0000000000000000000000000000000000000000..eb39502ff7d8fd3a37752f36a1c9550083d6df91 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bios-settings.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FIRMWARE_ATTRS (fu_bios_settings_get_type()) + +G_DECLARE_FINAL_TYPE(FuBiosSettings, fu_bios_settings, FU, BIOS_SETTINGS, GObject) + +FuBiosSettings * +fu_bios_settings_new(void); +gboolean +fu_bios_settings_get_pending_reboot(FuBiosSettings *self, gboolean *result, GError **error); +FwupdBiosSetting * +fu_bios_settings_get_attr(FuBiosSettings *self, const gchar *val); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bluez-device.c b/fwupd-1.8.6/libfwupdplugin/fu-bluez-device.c new file mode 100644 index 0000000000000000000000000000000000000000..31077e1285b581d112f47ba9a20cec991603cb9e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bluez-device.c @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuBluezDevice" + +#include "config.h" + +#include + +#include "fu-bluez-device.h" +#include "fu-common.h" +#include "fu-device-private.h" +#include "fu-firmware-common.h" +#include "fu-string.h" + +#define DEFAULT_PROXY_TIMEOUT 5000 + +/** + * FuBluezDevice: + * + * A BlueZ Bluetooth device. + * + * See also: [class@FuDevice] + */ + +typedef struct { + GDBusObjectManager *object_manager; + GDBusProxy *proxy; + GHashTable *uuids; /* utf8 : FuBluezDeviceUuidHelper */ +} FuBluezDevicePrivate; + +typedef struct { + FuBluezDevice *self; + gchar *uuid; + gchar *path; + gulong signal_id; + GDBusProxy *proxy; +} FuBluezDeviceUuidHelper; + +enum { PROP_0, PROP_OBJECT_MANAGER, PROP_PROXY, PROP_LAST }; + +enum { SIGNAL_CHANGED, SIGNAL_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FuBluezDevice, fu_bluez_device, FU_TYPE_DEVICE) + +#define GET_PRIVATE(o) (fu_bluez_device_get_instance_private(o)) + +static void +fu_bluez_uuid_free(FuBluezDeviceUuidHelper *uuid_helper) +{ + if (uuid_helper->path != NULL) + g_free(uuid_helper->path); + if (uuid_helper->proxy != NULL) + g_object_unref(uuid_helper->proxy); + g_free(uuid_helper->uuid); + g_object_unref(uuid_helper->self); + g_free(uuid_helper); +} + +/* + * Looks up a UUID in the FuBluezDevice uuids table. + */ +static FuBluezDeviceUuidHelper * +fu_bluez_device_get_uuid_helper(FuBluezDevice *self, const gchar *uuid, GError **error) +{ + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + FuBluezDeviceUuidHelper *uuid_helper; + + uuid_helper = g_hash_table_lookup(priv->uuids, uuid); + if (uuid_helper == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "UUID %s not supported", + uuid); + return NULL; + } + + return uuid_helper; +} + +static void +fu_bluez_device_signal_cb(GDBusProxy *proxy, + GVariant *changed_properties, + const GStrv invalidated_properties, + FuBluezDeviceUuidHelper *uuid_helper) +{ + g_signal_emit(uuid_helper->self, signals[SIGNAL_CHANGED], 0, uuid_helper->uuid); +} + +/* + * Builds the GDBusProxy of the BlueZ object identified by a UUID + * string. If the object doesn't have a dedicated proxy yet, this + * creates it and saves it in the FuBluezDeviceUuidHelper object. + * + * NOTE: Currently limited to GATT characteristics. + */ +static gboolean +fu_bluez_device_ensure_uuid_helper_proxy(FuBluezDeviceUuidHelper *uuid_helper, GError **error) +{ + if (uuid_helper->proxy != NULL) + return TRUE; + uuid_helper->proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.bluez", + uuid_helper->path, + "org.bluez.GattCharacteristic1", + NULL, + error); + if (uuid_helper->proxy == NULL) { + g_prefix_error(error, "Failed to create GDBusProxy for uuid_helper: "); + return FALSE; + } + g_dbus_proxy_set_default_timeout(uuid_helper->proxy, DEFAULT_PROXY_TIMEOUT); + uuid_helper->signal_id = g_signal_connect(G_DBUS_PROXY(uuid_helper->proxy), + "g-properties-changed", + G_CALLBACK(fu_bluez_device_signal_cb), + uuid_helper); + if (uuid_helper->signal_id <= 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot connect to signal of UUID %s", + uuid_helper->uuid); + return FALSE; + } + return TRUE; +} + +static void +fu_bluez_device_add_uuid_path(FuBluezDevice *self, const gchar *uuid, const gchar *path) +{ + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + FuBluezDeviceUuidHelper *uuid_helper; + g_return_if_fail(FU_IS_BLUEZ_DEVICE(self)); + g_return_if_fail(uuid != NULL); + g_return_if_fail(path != NULL); + + uuid_helper = g_new0(FuBluezDeviceUuidHelper, 1); + uuid_helper->self = g_object_ref(self); + uuid_helper->uuid = g_strdup(uuid); + uuid_helper->path = g_strdup(path); + g_hash_table_insert(priv->uuids, g_strdup(uuid), uuid_helper); +} + +static void +fu_bluez_device_set_modalias(FuBluezDevice *self, const gchar *modalias) +{ + gsize modaliaslen = strlen(modalias); + guint16 vid = 0x0; + guint16 pid = 0x0; + guint16 rev = 0x0; + + g_return_if_fail(modalias != NULL); + + /* usb:v0461p4EEFd0001 */ + if (g_str_has_prefix(modalias, "usb:")) { + fu_firmware_strparse_uint16_safe(modalias, modaliaslen, 5, &vid, NULL); + fu_firmware_strparse_uint16_safe(modalias, modaliaslen, 10, &pid, NULL); + fu_firmware_strparse_uint16_safe(modalias, modaliaslen, 15, &rev, NULL); + + /* bluetooth:v000ApFFFFdFFFF */ + } else if (g_str_has_prefix(modalias, "bluetooth:")) { + fu_firmware_strparse_uint16_safe(modalias, modaliaslen, 11, &vid, NULL); + fu_firmware_strparse_uint16_safe(modalias, modaliaslen, 16, &pid, NULL); + fu_firmware_strparse_uint16_safe(modalias, modaliaslen, 21, &rev, NULL); + } + + /* add generated IDs */ + if (vid != 0x0) + fu_device_add_instance_u16(FU_DEVICE(self), "VID", vid); + if (pid != 0x0) + fu_device_add_instance_u16(FU_DEVICE(self), "PID", pid); + fu_device_add_instance_u16(FU_DEVICE(self), "REV", rev); + fu_device_build_instance_id_quirk(FU_DEVICE(self), NULL, "BLUETOOTH", "VID", NULL); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "BLUETOOTH", "VID", "PID", NULL); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "BLUETOOTH", "VID", "PID", "REV", NULL); + + /* set vendor ID */ + if (vid != 0x0) { + g_autofree gchar *vendor_id = g_strdup_printf("BLUETOOTH:%04X", vid); + fu_device_add_vendor_id(FU_DEVICE(self), vendor_id); + } + + /* set version if the revision has been set */ + if (rev != 0x0 && + fu_device_get_version_format(FU_DEVICE(self)) == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_autofree gchar *version = NULL; + version = fu_version_from_uint16(rev, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version(FU_DEVICE(self), version); + } +} + +static void +fu_bluez_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuBluezDevice *self = FU_BLUEZ_DEVICE(device); + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + + if (priv->uuids != NULL) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, priv->uuids); + while (g_hash_table_iter_next(&iter, &key, &value)) { + FuBluezDeviceUuidHelper *uuid_helper = (FuBluezDeviceUuidHelper *)value; + fu_string_append(str, idt + 1, (const gchar *)key, uuid_helper->path); + } + } +} + +/* + * Returns the value of a property of an object specified by its path as + * a GVariant, or NULL if the property wasn't found. + */ +static GVariant * +fu_bluez_device_get_ble_property(const gchar *obj_path, + const gchar *iface, + const gchar *prop_name, + GError **error) +{ + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GVariant) val = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.bluez", + obj_path, + iface, + NULL, + error); + if (proxy == NULL) { + g_prefix_error(error, "failed to connect to %s: ", iface); + return NULL; + } + g_dbus_proxy_set_default_timeout(proxy, DEFAULT_PROXY_TIMEOUT); + val = g_dbus_proxy_get_cached_property(proxy, prop_name); + if (val == NULL) { + g_prefix_error(error, "property %s not found in %s: ", prop_name, obj_path); + return NULL; + } + + return g_steal_pointer(&val); +} + +/* + * Returns the value of the string property of an object specified by + * its path, or NULL if the property wasn't found. + * + * The returned string must be freed using g_free(). + */ +static gchar * +fu_bluez_device_get_ble_string_property(const gchar *obj_path, + const gchar *iface, + const gchar *prop_name, + GError **error) +{ + g_autoptr(GVariant) val = NULL; + val = fu_bluez_device_get_ble_property(obj_path, iface, prop_name, error); + if (val == NULL) + return NULL; + return g_variant_dup_string(val, NULL); +} + +/* + * Populates the {uuid_helper : object_path} entries of a device for all its + * characteristics. + * + * TODO: Extend to services and descriptors too? + */ +static void +fu_bluez_device_add_device_uuids(FuBluezDevice *self) +{ + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + g_autolist(GDBusObject) obj_list = NULL; + + obj_list = g_dbus_object_manager_get_objects(priv->object_manager); + for (GList *l = obj_list; l != NULL; l = l->next) { + GDBusObject *obj = G_DBUS_OBJECT(l->data); + g_autoptr(GDBusInterface) iface = NULL; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *obj_uuid = NULL; + const gchar *obj_path = g_dbus_object_get_object_path(obj); + + /* not us */ + if (!g_str_has_prefix(g_dbus_object_get_object_path(obj), + g_dbus_proxy_get_object_path(priv->proxy))) + continue; + + /* + * TODO: Currently restricted to getting UUIDs for + * characteristics only, as the only use case we're + * going to need for now is reading/writing + * characteristics. + */ + iface = g_dbus_object_get_interface(obj, "org.bluez.GattCharacteristic1"); + if (iface == NULL) + continue; + obj_uuid = fu_bluez_device_get_ble_string_property(obj_path, + "org.bluez.GattCharacteristic1", + "UUID", + &error_local); + if (obj_uuid == NULL) { + g_debug("failed to get property: %s", error_local->message); + continue; + } + fu_bluez_device_add_uuid_path(self, obj_uuid, obj_path); + } +} + +static gboolean +fu_bluez_device_setup(FuDevice *device, GError **error) +{ + FuBluezDevice *self = FU_BLUEZ_DEVICE(device); + + fu_bluez_device_add_device_uuids(self); + + return TRUE; +} + +static gboolean +fu_bluez_device_probe(FuDevice *device, GError **error) +{ + FuBluezDevice *self = FU_BLUEZ_DEVICE(device); + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GVariant) val_adapter = NULL; + g_autoptr(GVariant) val_address = NULL; + g_autoptr(GVariant) val_icon = NULL; + g_autoptr(GVariant) val_modalias = NULL; + g_autoptr(GVariant) val_name = NULL; + + val_address = g_dbus_proxy_get_cached_property(priv->proxy, "Address"); + if (val_address == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No required BLE address"); + return FALSE; + } + fu_device_set_logical_id(device, g_variant_get_string(val_address, NULL)); + val_adapter = g_dbus_proxy_get_cached_property(priv->proxy, "Adapter"); + if (val_adapter != NULL) + fu_device_set_physical_id(device, g_variant_get_string(val_adapter, NULL)); + val_name = g_dbus_proxy_get_cached_property(priv->proxy, "Name"); + if (val_name != NULL) + fu_device_set_name(device, g_variant_get_string(val_name, NULL)); + val_icon = g_dbus_proxy_get_cached_property(priv->proxy, "Icon"); + if (val_icon != NULL) + fu_device_add_icon(device, g_variant_get_string(val_name, NULL)); + val_modalias = g_dbus_proxy_get_cached_property(priv->proxy, "Modalias"); + if (val_modalias != NULL) + fu_bluez_device_set_modalias(self, g_variant_get_string(val_modalias, NULL)); + + /* success */ + return TRUE; +} + +/** + * fu_bluez_device_read: + * @self: a #FuBluezDevice + * @uuid: the UUID, e.g. `00cde35c-7062-11eb-9439-0242ac130002` + * @error: (nullable): optional return location for an error + * + * Reads from a UUID on the device. + * + * Returns: (transfer full): data, or %NULL for error + * + * Since: 1.5.7 + **/ +GByteArray * +fu_bluez_device_read(FuBluezDevice *self, const gchar *uuid, GError **error) +{ + FuBluezDeviceUuidHelper *uuid_helper; + guint8 byte; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GVariantBuilder) builder = NULL; + g_autoptr(GVariantIter) iter = NULL; + g_autoptr(GVariant) val = NULL; + + uuid_helper = fu_bluez_device_get_uuid_helper(self, uuid, error); + if (uuid_helper == NULL) + return NULL; + if (!fu_bluez_device_ensure_uuid_helper_proxy(uuid_helper, error)) + return NULL; + + /* + * Call the "ReadValue" method through the proxy synchronously. + * + * The method takes one argument: an array of dicts of + * {string:value} specifying the options (here the option is + * "offset":0. + * The result is a byte array. + */ + builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(builder, "{sv}", "offset", g_variant_new("q", 0)); + + val = g_dbus_proxy_call_sync(uuid_helper->proxy, + "ReadValue", + g_variant_new("(a{sv})", builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (val == NULL) { + g_prefix_error(error, "Failed to read GattCharacteristic1: "); + return NULL; + } + g_variant_get(val, "(ay)", &iter); + while (g_variant_iter_loop(iter, "y", &byte)) + g_byte_array_append(buf, &byte, 1); + + /* success */ + return g_steal_pointer(&buf); +} + +/** + * fu_bluez_device_read_string: + * @self: a #FuBluezDevice + * @uuid: the UUID, e.g. `00cde35c-7062-11eb-9439-0242ac130002` + * @error: (nullable): optional return location for an error + * + * Reads a string from a UUID on the device. + * + * Returns: (transfer full): data, or %NULL for error + * + * Since: 1.5.7 + **/ +gchar * +fu_bluez_device_read_string(FuBluezDevice *self, const gchar *uuid, GError **error) +{ + GByteArray *buf = fu_bluez_device_read(self, uuid, error); + if (buf == NULL) + return NULL; + return (gchar *)g_byte_array_free(buf, FALSE); +} + +/** + * fu_bluez_device_write: + * @self: a #FuBluezDevice + * @uuid: the UUID, e.g. `00cde35c-7062-11eb-9439-0242ac130002` + * @buf: data array + * @error: (nullable): optional return location for an error + * + * Writes to a UUID on the device. + * + * Returns: %TRUE if all the data was written + * + * Since: 1.5.7 + **/ +gboolean +fu_bluez_device_write(FuBluezDevice *self, const gchar *uuid, GByteArray *buf, GError **error) +{ + FuBluezDeviceUuidHelper *uuid_helper; + g_autoptr(GVariantBuilder) opt_builder = NULL; + g_autoptr(GVariantBuilder) val_builder = NULL; + g_autoptr(GVariant) ret = NULL; + GVariant *opt_variant = NULL; + GVariant *val_variant = NULL; + + uuid_helper = fu_bluez_device_get_uuid_helper(self, uuid, error); + if (uuid_helper == NULL) + return FALSE; + if (!fu_bluez_device_ensure_uuid_helper_proxy(uuid_helper, error)) + return FALSE; + + /* build the value variant */ + val_builder = g_variant_builder_new(G_VARIANT_TYPE("ay")); + for (gsize i = 0; i < buf->len; i++) + g_variant_builder_add(val_builder, "y", buf->data[i]); + val_variant = g_variant_new("ay", val_builder); + + /* build the options variant (offset = 0) */ + opt_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(opt_builder, "{sv}", "offset", g_variant_new_uint16(0)); + opt_variant = g_variant_new("a{sv}", opt_builder); + + ret = g_dbus_proxy_call_sync(uuid_helper->proxy, + "WriteValue", + g_variant_new("(@ay@a{sv})", val_variant, opt_variant), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (ret == NULL) { + g_prefix_error(error, "Failed to write GattCharacteristic1: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_bluez_device_notify_start: + * @self: a #FuBluezDevice + * @uuid: the UUID, e.g. `00cde35c-7062-11eb-9439-0242ac130002` + * @error: (nullable): optional return location for an error + * + * Enables notifications for property changes in a UUID (StartNotify + * method). + * + * Returns: %TRUE if the method call completed successfully. + * + * Since: 1.5.8 + **/ +gboolean +fu_bluez_device_notify_start(FuBluezDevice *self, const gchar *uuid, GError **error) +{ + FuBluezDeviceUuidHelper *uuid_helper; + g_autoptr(GVariant) retval = NULL; + + uuid_helper = fu_bluez_device_get_uuid_helper(self, uuid, error); + if (uuid_helper == NULL) + return FALSE; + if (!fu_bluez_device_ensure_uuid_helper_proxy(uuid_helper, error)) + return FALSE; + retval = g_dbus_proxy_call_sync(uuid_helper->proxy, + "StartNotify", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (retval == NULL) { + g_prefix_error(error, "Failed to enable notifications: "); + return FALSE; + } + + return TRUE; +} + +/** + * fu_bluez_device_notify_stop: + * @self: a #FuBluezDevice + * @uuid: the UUID, e.g. `00cde35c-7062-11eb-9439-0242ac130002` + * @error: (nullable): optional return location for an error + * + * Disables notifications for property changes in a UUID (StopNotify + * method). + * + * Returns: %TRUE if the method call completed successfully. + * + * Since: 1.5.8 + **/ +gboolean +fu_bluez_device_notify_stop(FuBluezDevice *self, const gchar *uuid, GError **error) +{ + FuBluezDeviceUuidHelper *uuid_helper; + g_autoptr(GVariant) retval = NULL; + + uuid_helper = fu_bluez_device_get_uuid_helper(self, uuid, error); + if (uuid_helper == NULL) + return FALSE; + if (!fu_bluez_device_ensure_uuid_helper_proxy(uuid_helper, error)) + return FALSE; + retval = g_dbus_proxy_call_sync(uuid_helper->proxy, + "StopNotify", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (retval == NULL) { + g_prefix_error(error, "Failed to enable notifications: "); + return FALSE; + } + + return TRUE; +} + +static void +fu_bluez_device_incorporate(FuDevice *self, FuDevice *donor) +{ + FuBluezDevice *uself = FU_BLUEZ_DEVICE(self); + FuBluezDevice *udonor = FU_BLUEZ_DEVICE(donor); + FuBluezDevicePrivate *priv = GET_PRIVATE(uself); + FuBluezDevicePrivate *privdonor = GET_PRIVATE(udonor); + + if (g_hash_table_size(priv->uuids) == 0) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, privdonor->uuids); + while (g_hash_table_iter_next(&iter, &key, &value)) { + FuBluezDeviceUuidHelper *uuid_helper = (FuBluezDeviceUuidHelper *)value; + fu_bluez_device_add_uuid_path(uself, (const gchar *)key, uuid_helper->path); + } + } + if (priv->object_manager == NULL) + priv->object_manager = g_object_ref(privdonor->object_manager); + if (priv->proxy == NULL) + priv->proxy = g_object_ref(privdonor->proxy); +} + +static void +fu_bluez_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuBluezDevice *self = FU_BLUEZ_DEVICE(object); + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_OBJECT_MANAGER: + g_value_set_object(value, priv->object_manager); + break; + case PROP_PROXY: + g_value_set_object(value, priv->proxy); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_bluez_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuBluezDevice *self = FU_BLUEZ_DEVICE(object); + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_OBJECT_MANAGER: + priv->object_manager = g_value_dup_object(value); + break; + case PROP_PROXY: + priv->proxy = g_value_dup_object(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_bluez_device_finalize(GObject *object) +{ + FuBluezDevice *self = FU_BLUEZ_DEVICE(object); + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + + g_hash_table_unref(priv->uuids); + g_object_unref(priv->proxy); + g_object_unref(priv->object_manager); + G_OBJECT_CLASS(fu_bluez_device_parent_class)->finalize(object); +} + +static void +fu_bluez_device_init(FuBluezDevice *self) +{ + FuBluezDevicePrivate *priv = GET_PRIVATE(self); + priv->uuids = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)fu_bluez_uuid_free); +} + +static void +fu_bluez_device_class_init(FuBluezDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->get_property = fu_bluez_device_get_property; + object_class->set_property = fu_bluez_device_set_property; + object_class->finalize = fu_bluez_device_finalize; + device_class->probe = fu_bluez_device_probe; + device_class->setup = fu_bluez_device_setup; + device_class->to_string = fu_bluez_device_to_string; + device_class->incorporate = fu_bluez_device_incorporate; + + /** + * FuBluezDevice::changed: + * @self: the #FuBluezDevice instance that emitted the signal + * @uuid: the UUID that changed + * + * The ::changed signal is emitted when a service with a specific UUID changed. + * + * Since: 1.5.8 + **/ + signals[SIGNAL_CHANGED] = g_signal_new("changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * FuBluezDevice:object-manager: + * + * The object manager instance for all devices. + * + * Since: 1.5.8 + */ + pspec = g_param_spec_object("object-manager", + NULL, + NULL, + G_TYPE_DBUS_OBJECT_MANAGER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_OBJECT_MANAGER, pspec); + + /** + * FuBluezDevice:proxy: + * + * The D-Bus proxy for the object. + * + * Since: 1.5.8 + */ + pspec = g_param_spec_object("proxy", + NULL, + NULL, + G_TYPE_DBUS_PROXY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PROXY, pspec); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bluez-device.h b/fwupd-1.8.6/libfwupdplugin/fu-bluez-device.h new file mode 100644 index 0000000000000000000000000000000000000000..b2084aa1e5e5c384942bb74035e03ca224b8ead5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bluez-device.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-device.h" + +#define FU_TYPE_BLUEZ_DEVICE (fu_bluez_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuBluezDevice, fu_bluez_device, FU, BLUEZ_DEVICE, FuDevice) + +struct _FuBluezDeviceClass { + FuDeviceClass parent_class; + gpointer __reserved[31]; +}; + +GByteArray * +fu_bluez_device_read(FuBluezDevice *self, const gchar *uuid, GError **error); +gchar * +fu_bluez_device_read_string(FuBluezDevice *self, const gchar *uuid, GError **error); +gboolean +fu_bluez_device_write(FuBluezDevice *self, const gchar *uuid, GByteArray *buf, GError **error); +gboolean +fu_bluez_device_notify_start(FuBluezDevice *self, const gchar *uuid, GError **error); +gboolean +fu_bluez_device_notify_stop(FuBluezDevice *self, const gchar *uuid, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-byte-array.c b/fwupd-1.8.6/libfwupdplugin/fu-byte-array.c new file mode 100644 index 0000000000000000000000000000000000000000..b581d50c06d990c9538e7c79e00eedf87dbbc032 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-byte-array.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-mem.h" + +/** + * fu_byte_array_append_uint8: + * @array: a #GByteArray + * @data: value + * + * Adds a 8 bit integer to a byte array. + * + * Since: 1.3.1 + **/ +void +fu_byte_array_append_uint8(GByteArray *array, guint8 data) +{ + g_byte_array_append(array, &data, sizeof(data)); +} + +/** + * fu_byte_array_append_uint16: + * @array: a #GByteArray + * @data: value + * @endian: endian type, e.g. #G_LITTLE_ENDIAN + * + * Adds a 16 bit integer to a byte array. + * + * Since: 1.3.1 + **/ +void +fu_byte_array_append_uint16(GByteArray *array, guint16 data, FuEndianType endian) +{ + guint8 buf[2]; + fu_memwrite_uint16(buf, data, endian); + g_byte_array_append(array, buf, sizeof(buf)); +} + +/** + * fu_byte_array_append_uint32: + * @array: a #GByteArray + * @data: value + * @endian: endian type, e.g. #G_LITTLE_ENDIAN + * + * Adds a 32 bit integer to a byte array. + * + * Since: 1.3.1 + **/ +void +fu_byte_array_append_uint32(GByteArray *array, guint32 data, FuEndianType endian) +{ + guint8 buf[4]; + fu_memwrite_uint32(buf, data, endian); + g_byte_array_append(array, buf, sizeof(buf)); +} + +/** + * fu_byte_array_append_uint64: + * @array: a #GByteArray + * @data: value + * @endian: endian type, e.g. #G_LITTLE_ENDIAN + * + * Adds a 64 bit integer to a byte array. + * + * Since: 1.5.8 + **/ +void +fu_byte_array_append_uint64(GByteArray *array, guint64 data, FuEndianType endian) +{ + guint8 buf[8]; + fu_memwrite_uint64(buf, data, endian); + g_byte_array_append(array, buf, sizeof(buf)); +} + +/** + * fu_byte_array_append_bytes: + * @array: a #GByteArray + * @bytes: data blob + * + * Adds the contents of a GBytes to a byte array. + * + * Since: 1.5.8 + **/ +void +fu_byte_array_append_bytes(GByteArray *array, GBytes *bytes) +{ + g_byte_array_append(array, g_bytes_get_data(bytes, NULL), g_bytes_get_size(bytes)); +} + +/** + * fu_byte_array_set_size: + * @array: a #GByteArray + * @length: the new size of the GByteArray + * @data: the byte used to pad the array + * + * Sets the size of the GByteArray, expanding with @data as required. + * + * Since: 1.8.2 + **/ +void +fu_byte_array_set_size(GByteArray *array, guint length, guint8 data) +{ + guint oldlength = array->len; + g_byte_array_set_size(array, length); + if (length > oldlength) + memset(array->data + oldlength, data, length - oldlength); +} + +/** + * fu_byte_array_align_up: + * @array: a #GByteArray + * @alignment: align to this power of 2 + * @data: the byte used to pad the array + * + * Align a byte array length to a power of 2 boundary, where @alignment is the + * bit position to align to. If @alignment is zero then @array is unchanged. + * + * Since: 1.6.0 + **/ +void +fu_byte_array_align_up(GByteArray *array, guint8 alignment, guint8 data) +{ + fu_byte_array_set_size(array, fu_common_align_up(array->len, alignment), data); +} + +/** + * fu_byte_array_compare: + * @buf1: a data blob + * @buf2: another #GByteArray + * @error: (nullable): optional return location for an error + * + * Compares two buffers for equality. + * + * Returns: %TRUE if @buf1 and @buf2 are identical + * + * Since: 1.8.0 + **/ +gboolean +fu_byte_array_compare(GByteArray *buf1, GByteArray *buf2, GError **error) +{ + g_return_val_if_fail(buf1 != NULL, FALSE); + g_return_val_if_fail(buf2 != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return fu_memcmp_safe(buf1->data, buf1->len, buf2->data, buf2->len, error); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-byte-array.h b/fwupd-1.8.6/libfwupdplugin/fu-byte-array.h new file mode 100644 index 0000000000000000000000000000000000000000..7733131cf180e69a91aa0f80f2c100867a6c9673 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-byte-array.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-common.h" + +void +fu_byte_array_set_size(GByteArray *array, guint length, guint8 data); +void +fu_byte_array_align_up(GByteArray *array, guint8 alignment, guint8 data); +void +fu_byte_array_append_uint8(GByteArray *array, guint8 data); +void +fu_byte_array_append_uint16(GByteArray *array, guint16 data, FuEndianType endian); +void +fu_byte_array_append_uint32(GByteArray *array, guint32 data, FuEndianType endian); +void +fu_byte_array_append_uint64(GByteArray *array, guint64 data, FuEndianType endian); +void +fu_byte_array_append_bytes(GByteArray *array, GBytes *bytes); +gboolean +fu_byte_array_compare(GByteArray *buf1, GByteArray *buf2, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bytes.c b/fwupd-1.8.6/libfwupdplugin/fu-bytes.c new file mode 100644 index 0000000000000000000000000000000000000000..416a5a58b1c915e435560b6ad616d3104d93b4e9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bytes.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#ifdef HAVE_GIO_UNIX +#include +#endif + +#include "fwupd-error.h" + +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-mem.h" + +/** + * fu_bytes_set_contents: + * @filename: a filename + * @bytes: data to write + * @error: (nullable): optional return location for an error + * + * Writes a blob of data to a filename, creating the parent directories as + * required. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_bytes_set_contents(const gchar *filename, GBytes *bytes, GError **error) +{ + const gchar *data; + gsize size; + g_autoptr(GFile) file = NULL; + g_autoptr(GFile) file_parent = NULL; + + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(bytes != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + file = g_file_new_for_path(filename); + file_parent = g_file_get_parent(file); + if (!g_file_query_exists(file_parent, NULL)) { + if (!g_file_make_directory_with_parents(file_parent, NULL, error)) + return FALSE; + } + data = g_bytes_get_data(bytes, &size); + g_debug("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size); + return g_file_set_contents(filename, data, size, error); +} + +/** + * fu_bytes_get_contents: + * @filename: a filename + * @error: (nullable): optional return location for an error + * + * Reads a blob of data from a file. + * + * Returns: a #GBytes, or %NULL for failure + * + * Since: 1.8.2 + **/ +GBytes * +fu_bytes_get_contents(const gchar *filename, GError **error) +{ + gchar *data = NULL; + gsize len = 0; + + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (!g_file_get_contents(filename, &data, &len, error)) + return NULL; + g_debug("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len); + return g_bytes_new_take(data, len); +} + +/** + * fu_bytes_get_contents_fd: + * @fd: a file descriptor + * @count: the maximum number of bytes to read + * @error: (nullable): optional return location for an error + * + * Reads a blob from a specific file descriptor. + * + * Note: this will close the fd when done + * + * Returns: (transfer full): a #GBytes, or %NULL + * + * Since: 1.8.2 + **/ +GBytes * +fu_bytes_get_contents_fd(gint fd, gsize count, GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GInputStream) stream = NULL; + + g_return_val_if_fail(fd > 0, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* read the entire fd to a data blob */ + stream = g_unix_input_stream_new(fd, TRUE); + return fu_bytes_get_contents_stream(stream, count, error); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return NULL; +#endif +} + +/** + * fu_bytes_get_contents_stream: + * @stream: input stream + * @count: the maximum number of bytes to read + * @error: (nullable): optional return location for an error + * + * Reads a blob from a specific input stream. + * + * Returns: (transfer full): a #GBytes, or %NULL + * + * Since: 1.8.2 + **/ +GBytes * +fu_bytes_get_contents_stream(GInputStream *stream, gsize count, GError **error) +{ + guint8 tmp[0x8000] = {0x0}; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* this is invalid */ + if (count == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "A maximum read size must be specified"); + return NULL; + } + + /* read from stream in 32kB chunks */ + while (TRUE) { + gssize sz; + sz = g_input_stream_read(stream, tmp, sizeof(tmp), NULL, &error_local); + if (sz == 0) + break; + if (sz < 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return NULL; + } + g_byte_array_append(buf, tmp, sz); + if (buf->len > count) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot read from fd: 0x%x > 0x%x", + buf->len, + (guint)count); + return NULL; + } + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +/** + * fu_bytes_align: + * @bytes: data blob + * @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.8.2 + **/ +GBytes * +fu_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_bytes_is_empty: + * @bytes: data blob + * + * Checks if a byte array are just empty (0xff) bytes. + * + * Returns: %TRUE if @bytes is empty + * + * Since: 1.8.2 + **/ +gboolean +fu_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_bytes_compare: + * @bytes1: a data blob + * @bytes2: another #GBytes + * @error: (nullable): optional return location for an error + * + * Compares the buffers for equality. + * + * Returns: %TRUE if @bytes1 and @bytes2 are identical + * + * Since: 1.8.2 + **/ +gboolean +fu_bytes_compare(GBytes *bytes1, GBytes *bytes2, GError **error) +{ + const guint8 *buf1; + const guint8 *buf2; + gsize bufsz1; + gsize bufsz2; + + g_return_val_if_fail(bytes1 != NULL, FALSE); + g_return_val_if_fail(bytes2 != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + buf1 = g_bytes_get_data(bytes1, &bufsz1); + buf2 = g_bytes_get_data(bytes2, &bufsz2); + return fu_memcmp_safe(buf1, bufsz1, buf2, bufsz2, error); +} + +/** + * fu_bytes_pad: + * @bytes: data blob + * @sz: the desired size in bytes + * + * Pads a GBytes to a minimum @sz with `0xff`. + * + * Returns: (transfer full): a data blob + * + * Since: 1.8.2 + **/ +GBytes * +fu_bytes_pad(GBytes *bytes, gsize sz) +{ + gsize bytes_sz; + + g_return_val_if_fail(bytes != NULL, NULL); + g_return_val_if_fail(sz != 0, NULL); + + /* pad */ + bytes_sz = g_bytes_get_size(bytes); + if (bytes_sz < sz) { + const guint8 *data = g_bytes_get_data(bytes, NULL); + guint8 *data_new = g_malloc(sz); + memcpy(data_new, data, bytes_sz); + memset(data_new + bytes_sz, 0xff, sz - bytes_sz); + return g_bytes_new_take(data_new, sz); + } + + /* not required */ + return g_bytes_ref(bytes); +} + +/** + * fu_bytes_new_offset: + * @bytes: data blob + * @offset: where subsection starts at + * @length: length of subsection + * @error: (nullable): optional return location for an error + * + * Creates a #GBytes which is a subsection of another #GBytes. + * + * Returns: (transfer full): a #GBytes, or #NULL if range is invalid + * + * Since: 1.8.2 + **/ +GBytes * +fu_bytes_new_offset(GBytes *bytes, gsize offset, gsize length, GError **error) +{ + g_return_val_if_fail(bytes != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* sanity check */ + if (offset + length < length || offset + length > g_bytes_get_size(bytes)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot create bytes @0x%02x for 0x%02x " + "as buffer only 0x%04x bytes in size", + (guint)offset, + (guint)length, + (guint)g_bytes_get_size(bytes)); + return NULL; + } + return g_bytes_new_from_bytes(bytes, offset, length); +} + +/** + * fu_bytes_get_data_safe: + * @bytes: data blob + * @bufsz: (out) (optional): location to return size of byte data + * @error: (nullable): optional return location for an error + * + * Get the byte data in the #GBytes. This data should not be modified. + * This function will always return the same pointer for a given #GBytes. + * + * If the size of @bytes is zero, then %NULL is returned and the @error is set, + * which differs in behavior to that of g_bytes_get_data(). + * + * This may be useful when calling g_mapped_file_new() on a zero-length file. + * + * Returns: a pointer to the byte data, or %NULL. + * + * Since: 1.6.0 + **/ +const guint8 * +fu_bytes_get_data_safe(GBytes *bytes, gsize *bufsz, GError **error) +{ + const guint8 *buf = g_bytes_get_data(bytes, bufsz); + if (buf == NULL) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "invalid data"); + return NULL; + } + return buf; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-bytes.h b/fwupd-1.8.6/libfwupdplugin/fu-bytes.h new file mode 100644 index 0000000000000000000000000000000000000000..687e4792cebe6eb9b2635c65b07f2be29f76fc15 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-bytes.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gboolean +fu_bytes_set_contents(const gchar *filename, + GBytes *bytes, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_bytes_get_contents(const gchar *filename, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_bytes_get_contents_stream(GInputStream *stream, + gsize count, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_bytes_get_contents_fd(gint fd, gsize count, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_bytes_align(GBytes *bytes, gsize blksz, gchar padval); +const guint8 * +fu_bytes_get_data_safe(GBytes *bytes, gsize *bufsz, GError **error); +gboolean +fu_bytes_is_empty(GBytes *bytes); +gboolean +fu_bytes_compare(GBytes *bytes1, GBytes *bytes2, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_bytes_pad(GBytes *bytes, gsize sz); +GBytes * +fu_bytes_new_offset(GBytes *bytes, gsize offset, gsize length, GError **error) + G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cabinet.c b/fwupd-1.8.6/libfwupdplugin/fu-cabinet.c new file mode 100644 index 0000000000000000000000000000000000000000..733cf418a016918e495d28f898ed10fdcc38585c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cabinet.c @@ -0,0 +1,1141 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCabinet" + +#include "config.h" + +#include +#include + +#include "fwupd-common.h" +#include "fwupd-enums.h" +#include "fwupd-error.h" + +#include "fu-cabinet.h" +#include "fu-common.h" +#include "fu-string.h" + +/** + * FuCabinet: + * + * Cabinet archive parser and writer. + * + * See also: [class@FuArchive] + */ + +struct _FuCabinet { + GObject parent_instance; + guint64 size_max; + GCabCabinet *gcab_cabinet; + gchar *container_checksum; + gchar *container_checksum_alt; + XbBuilder *builder; + XbSilo *silo; + JcatContext *jcat_context; + JcatFile *jcat_file; +}; + +G_DEFINE_TYPE(FuCabinet, fu_cabinet, G_TYPE_OBJECT) + +static void +fu_cabinet_finalize(GObject *obj) +{ + FuCabinet *self = FU_CABINET(obj); + if (self->silo != NULL) + g_object_unref(self->silo); + if (self->builder != NULL) + g_object_unref(self->builder); + g_free(self->container_checksum); + g_free(self->container_checksum_alt); + g_object_unref(self->gcab_cabinet); + g_object_unref(self->jcat_context); + g_object_unref(self->jcat_file); + G_OBJECT_CLASS(fu_cabinet_parent_class)->finalize(obj); +} + +static void +fu_cabinet_class_init(FuCabinetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_cabinet_finalize; +} + +static void +fu_cabinet_init(FuCabinet *self) +{ + self->size_max = 1024 * 1024 * 100; + self->gcab_cabinet = gcab_cabinet_new(); + self->builder = xb_builder_new(); + self->jcat_file = jcat_file_new(); + self->jcat_context = jcat_context_new(); +} + +/** + * fu_cabinet_set_size_max: + * @self: a #FuCabinet + * @size_max: size in bytes + * + * Sets the maximum size of the decompressed cabinet file. + * + * Since: 1.4.0 + **/ +void +fu_cabinet_set_size_max(FuCabinet *self, guint64 size_max) +{ + g_return_if_fail(FU_IS_CABINET(self)); + self->size_max = size_max; +} + +/** + * fu_cabinet_set_jcat_context: (skip): + * @self: a #FuCabinet + * @jcat_context: (nullable): a Jcat context + * + * Sets the Jcat context, which is used for setting the trust flags on the + * each release in the archive. + * + * Since: 1.4.0 + **/ +void +fu_cabinet_set_jcat_context(FuCabinet *self, JcatContext *jcat_context) +{ + g_return_if_fail(FU_IS_CABINET(self)); + g_return_if_fail(JCAT_IS_CONTEXT(jcat_context)); + g_set_object(&self->jcat_context, jcat_context); +} + +/** + * fu_cabinet_get_silo: (skip): + * @self: a #FuCabinet + * + * Gets the silo that represents the superset metadata of all the metainfo files + * found in the archive. + * + * Returns: (transfer full): a #XbSilo, or %NULL if the archive has not been parsed + * + * Since: 1.4.0 + **/ +XbSilo * +fu_cabinet_get_silo(FuCabinet *self) +{ + g_return_val_if_fail(FU_IS_CABINET(self), NULL); + if (self->silo == NULL) + return NULL; + return g_object_ref(self->silo); +} + +static GCabFile * +fu_cabinet_get_file_by_name(FuCabinet *self, const gchar *basename) +{ + GPtrArray *folders = gcab_cabinet_get_folders(self->gcab_cabinet); + for (guint i = 0; i < folders->len; i++) { + GCabFolder *cabfolder = GCAB_FOLDER(g_ptr_array_index(folders, i)); + GCabFile *cabfile = gcab_folder_get_file_by_name(cabfolder, basename); + if (cabfile != NULL) + return cabfile; + } + return NULL; +} + +/** + * fu_cabinet_add_file: + * @self: a #FuCabinet + * @basename: filename + * @data: file data + * + * Adds a file to the silo. + * + * Since: 1.6.0 + **/ +void +fu_cabinet_add_file(FuCabinet *self, const gchar *basename, GBytes *data) +{ + GPtrArray *folders; + GCabFile *gcab_file_old; + g_autoptr(GCabFolder) gcab_folder = NULL; + g_autoptr(GCabFile) gcab_file = NULL; + + g_return_if_fail(FU_IS_CABINET(self)); + g_return_if_fail(basename != NULL); + g_return_if_fail(data != NULL); + + /* existing file? */ + gcab_file_old = fu_cabinet_get_file_by_name(self, basename); + if (gcab_file_old != NULL) { +#ifdef HAVE_GCAB_FILE_SET_BYTES + gcab_file_set_bytes(gcab_file_old, data); +#else + g_object_set(gcab_file_old, "bytes", data, NULL); +#endif + return; + } + + /* new file, in a possibly new folder */ + folders = gcab_cabinet_get_folders(self->gcab_cabinet); + if (folders->len == 0) { + gcab_folder = gcab_folder_new(GCAB_COMPRESSION_NONE); + gcab_cabinet_add_folder(self->gcab_cabinet, gcab_folder, NULL); + } else { + gcab_folder = g_object_ref(GCAB_FOLDER(g_ptr_array_index(folders, 0))); + } + gcab_file = gcab_file_new_with_bytes(basename, data); + gcab_folder_add_file(gcab_folder, gcab_file, FALSE, NULL, NULL); +} + +/** + * fu_cabinet_get_file: + * @self: a #FuCabinet + * @basename: filename + * @error: (nullable): optional return location for an error + * + * Gets a file from the archive. + * + * Returns: (transfer full): a #GBytes, or %NULL if the file does not exist + * + * Since: 1.6.0 + **/ +GBytes * +fu_cabinet_get_file(FuCabinet *self, const gchar *basename, GError **error) +{ + GCabFile *cabfile; + GBytes *blob; + + g_return_val_if_fail(FU_IS_CABINET(self), NULL); + g_return_val_if_fail(basename != NULL, NULL); + + cabfile = fu_cabinet_get_file_by_name(self, basename); + if (cabfile == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot find %s in archive", + basename); + return NULL; + } + blob = gcab_file_get_bytes(cabfile); + if (blob == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile firmware"); + return NULL; + } + return g_bytes_ref(blob); +} + +/* sets the firmware and signature blobs on XbNode */ +static gboolean +fu_cabinet_parse_release(FuCabinet *self, XbNode *release, GError **error) +{ + GCabFile *cabfile; + GBytes *blob; + const gchar *csum_filename = NULL; + g_autofree gchar *basename = NULL; + g_autoptr(XbNode) artifact = NULL; + g_autoptr(XbNode) csum_tmp = NULL; + g_autoptr(XbNode) metadata_trust = NULL; + g_autoptr(XbNode) nsize = NULL; + g_autoptr(JcatItem) item = NULL; + g_autoptr(GBytes) release_flags_blob = NULL; + FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; + + /* we set this with XbBuilderSource before the silo was created */ + metadata_trust = xb_node_query_first(release, "../../info/metadata_trust", NULL); + if (metadata_trust != NULL) + release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_METADATA; + + /* look for source artifact first */ + artifact = xb_node_query_first(release, "artifacts/artifact[@type='source']", NULL); + if (artifact != NULL) { + csum_filename = xb_node_query_text(artifact, "filename", NULL); + csum_tmp = xb_node_query_first(artifact, "checksum[@type='sha256']", NULL); + if (csum_tmp == NULL) + csum_tmp = xb_node_query_first(artifact, "checksum", NULL); + } else { + 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(csum_filename); + cabfile = fu_cabinet_get_file_by_name(self, basename); + if (cabfile == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot find %s in archive", + basename); + return FALSE; + } + blob = gcab_file_get_bytes(cabfile); + if (blob == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile firmware"); + return FALSE; + } + + /* set the blob */ + xb_node_set_data(release, "fwupd::FirmwareBlob", blob); + + /* 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 = 0; + if (!fu_strtoull(xb_node_get_text(nsize), &size, 0, G_MAXSIZE, error)) + return FALSE; + 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 */ + if (csum_tmp != NULL && xb_node_get_text(csum_tmp) != NULL) { + const gchar *checksum_old = xb_node_get_text(csum_tmp); + GChecksumType checksum_type = fwupd_checksum_guess_kind(checksum_old); + g_autofree gchar *checksum = NULL; + checksum = g_compute_checksum_for_bytes(checksum_type, blob); + if (g_strcmp0(checksum, checksum_old) != 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; + } + } + + /* find out if the payload is signed, falling back to detached */ + item = jcat_file_get_item_by_id(self->jcat_file, basename, NULL); + if (item != NULL) { + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) results = NULL; + results = jcat_context_verify_item(self->jcat_context, + blob, + item, + JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | + JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + &error_local); + if (results == NULL) { + g_debug("failed to verify payload %s: %s", basename, error_local->message); + } else { + g_debug("verified payload %s: %u", basename, results->len); + release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD; + } + + /* legacy GPG detached signature */ + } else { + g_autofree gchar *basename_sig = NULL; + basename_sig = g_strdup_printf("%s.asc", basename); + cabfile = fu_cabinet_get_file_by_name(self, basename_sig); + if (cabfile != NULL) { + GBytes *data_sig; + g_autoptr(JcatResult) jcat_result = NULL; + g_autoptr(JcatBlob) jcat_blob = NULL; + g_autoptr(GError) error_local = NULL; + + data_sig = gcab_file_get_bytes(cabfile); + if (data_sig == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile %s", + basename_sig); + return FALSE; + } + jcat_blob = jcat_blob_new(JCAT_BLOB_KIND_GPG, data_sig); + jcat_result = jcat_context_verify_blob(self->jcat_context, + blob, + jcat_blob, + JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + &error_local); + if (jcat_result == NULL) { + g_debug("failed to verify payload %s using detached: %s", + basename, + error_local->message); + } else { + g_debug("verified payload %s using detached", basename); + release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD; + } + } + } + + /* this means we can get the data from fu_keyring_get_release_flags */ + release_flags_blob = g_bytes_new(&release_flags, sizeof(release_flags)); + xb_node_set_data(release, "fwupd::ReleaseFlags", release_flags_blob); + + /* success */ + return TRUE; +} + +static gint +fu_cabinet_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_cabinet_sort_priority_cb(XbBuilderFixup *self, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ + xb_builder_node_sort_children(bn, fu_cabinet_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, + const gchar *attr2_name, + const gchar *attr2_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, attr_name), attr_value) != 0) + continue; + if (g_strcmp0(xb_builder_node_get_attr(bc, attr2_name), attr2_value) == 0) + return g_object_ref(bc); + } + return NULL; +} + +static void +fu_cabinet_ensure_container_checksum(XbBuilderNode *bn, const gchar *type, const gchar *checksum) +{ + g_autoptr(XbBuilderNode) csum = NULL; + + /* verify it exists */ + csum = _xb_builder_node_get_child_by_element_attr(bn, + "checksum", + "type", + type, + "target", + "container"); + if (csum == NULL) { + csum = xb_builder_node_insert(bn, + "checksum", + "type", + type, + "target", + "container", + NULL); + } + + /* verify it is correct */ + if (g_strcmp0(xb_builder_node_get_text(csum), checksum) != 0) { + if (xb_builder_node_get_text(csum) != NULL) { + g_warning("invalid container checksum %s, fixing up to %s", + xb_builder_node_get_text(csum), + checksum); + } + xb_builder_node_set_text(csum, checksum, -1); + } +} + +static gboolean +fu_cabinet_ensure_container_checksum_cb(XbBuilderFixup *builder_fixup, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ + FuCabinet *self = FU_CABINET(user_data); + + /* not us */ + if (g_strcmp0(xb_builder_node_get_element(bn), "release") != 0) + return TRUE; + + fu_cabinet_ensure_container_checksum(bn, "sha1", self->container_checksum); + fu_cabinet_ensure_container_checksum(bn, "sha256", self->container_checksum_alt); + return TRUE; +} + +static void +fu_cabinet_fixup_checksum_children(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 (attr_value == NULL || + g_strcmp0(xb_builder_node_get_attr(bc, attr_name), attr_value) == 0) { + const gchar *tmp = xb_builder_node_get_text(bc); + if (tmp != NULL) { + g_autofree gchar *lowercase = g_ascii_strdown(tmp, -1); + xb_builder_node_set_text(bc, lowercase, -1); + } + } + } +} + +static gboolean +fu_cabinet_set_lowercase_checksum_cb(XbBuilderFixup *builder_fixup, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ + if (g_strcmp0(xb_builder_node_get_element(bn), "artifact") == 0) + /* don't care whether it's sha256, sha1 or something else so don't check for + * specific value */ + fu_cabinet_fixup_checksum_children(bn, "checksum", "type", NULL); + else if (g_strcmp0(xb_builder_node_get_element(bn), "release") == 0) + fu_cabinet_fixup_checksum_children(bn, "checksum", "target", "content"); + + return TRUE; +} + +static gboolean +fu_cabinet_fixup_strip_inner_text_cb(XbBuilderFixup *self, + XbBuilderNode *bn, + gpointer user_data, + GError **error) +{ +#if LIBXMLB_CHECK_VERSION(0, 3, 4) + if (xb_builder_node_get_first_child(bn) == NULL) + xb_builder_node_add_flag(bn, XB_BUILDER_NODE_FLAG_STRIP_TEXT); +#endif + return TRUE; +} + +/* adds each GCabFile to the silo */ +static gboolean +fu_cabinet_build_silo_file(FuCabinet *self, + GCabFile *cabfile, + FwupdReleaseFlags release_flags, + GError **error) +{ + GBytes *blob; + g_autoptr(GError) error_local = NULL; + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbBuilderNode) bn_info = xb_builder_node_new("info"); + + /* indicate the metainfo file was signed */ + if (release_flags & FWUPD_RELEASE_FLAG_TRUSTED_METADATA) + xb_builder_node_insert_text(bn_info, "metadata_trust", NULL, NULL); + xb_builder_node_insert_text(bn_info, "filename", gcab_file_get_name(cabfile), NULL); + xb_builder_source_set_info(source, bn_info); + + /* rewrite to be under a components root */ + xb_builder_source_set_prefix(source, "components"); + + /* parse file */ + blob = gcab_file_get_bytes(cabfile); + if (blob == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile"); + return FALSE; + } + if (!xb_builder_source_load_bytes(source, + blob, + XB_BUILDER_SOURCE_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; + } + xb_builder_import_source(self->builder, source); + + /* success */ + return TRUE; +} + +static gboolean +fu_cabinet_build_silo_metainfo(FuCabinet *self, GCabFile *cabfile, GError **error) +{ + FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; + const gchar *fn = gcab_file_get_extract_name(cabfile); + g_autoptr(JcatItem) item = NULL; + + /* validate against the Jcat file */ + item = jcat_file_get_item_by_id(self->jcat_file, fn, NULL); + if (item == NULL) { + g_debug("failed to verify %s: no JcatItem", fn); + } else { + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) results = NULL; + results = jcat_context_verify_item(self->jcat_context, + gcab_file_get_bytes(cabfile), + item, + JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | + JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + &error_local); + if (results == NULL) { + g_debug("failed to verify %s: %s", fn, error_local->message); + } else { + g_debug("verified metadata %s: %u", fn, results->len); + release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_METADATA; + } + } + + /* actually parse the XML now */ + g_debug("processing file: %s", fn); + if (!fu_cabinet_build_silo_file(self, cabfile, release_flags, error)) { + g_prefix_error(error, + "%s could not be loaded: ", + gcab_file_get_extract_name(cabfile)); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* load the firmware.jcat files if included */ +static gboolean +fu_cabinet_build_jcat_folder(FuCabinet *self, GCabFolder *cabfolder, GError **error) +{ + g_autoptr(GSList) cabfiles = gcab_folder_get_files(cabfolder); + for (GSList *l = cabfiles; l != NULL; l = l->next) { + GCabFile *cabfile = GCAB_FILE(l->data); + const gchar *fn = gcab_file_get_extract_name(cabfile); + if (g_str_has_suffix(fn, ".jcat")) { + GBytes *data_jcat = gcab_file_get_bytes(cabfile); + g_autoptr(GInputStream) istream = NULL; + istream = g_memory_input_stream_new_from_bytes(data_jcat); + if (!jcat_file_import_stream(self->jcat_file, + istream, + JCAT_IMPORT_FLAG_NONE, + NULL, + error)) + return FALSE; + } + } + return TRUE; +} + +/* adds each GCabFolder to the silo */ +static gboolean +fu_cabinet_build_silo_folder(FuCabinet *self, GCabFolder *cabfolder, GError **error) +{ + g_autoptr(GSList) cabfiles = gcab_folder_get_files(cabfolder); + for (GSList *l = cabfiles; l != NULL; l = l->next) { + GCabFile *cabfile = GCAB_FILE(l->data); + const gchar *fn = gcab_file_get_extract_name(cabfile); + if (!g_str_has_suffix(fn, ".metainfo.xml")) + continue; + if (!fu_cabinet_build_silo_metainfo(self, cabfile, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_cabinet_build_silo(FuCabinet *self, GBytes *data, GError **error) +{ + GPtrArray *folders; + g_autoptr(XbBuilderFixup) fixup1 = NULL; + g_autoptr(XbBuilderFixup) fixup2 = NULL; + g_autoptr(XbBuilderFixup) fixup3 = NULL; + g_autoptr(XbBuilderFixup) fixup4 = NULL; + + /* verbose profiling */ + if (g_getenv("FWUPD_XMLB_VERBOSE") != NULL) { + xb_builder_set_profile_flags(self->builder, + XB_SILO_PROFILE_FLAG_XPATH | + XB_SILO_PROFILE_FLAG_DEBUG); + } + + /* load Jcat */ + folders = gcab_cabinet_get_folders(self->gcab_cabinet); + if (self->jcat_context != NULL) { + for (guint i = 0; i < folders->len; i++) { + GCabFolder *cabfolder = GCAB_FOLDER(g_ptr_array_index(folders, i)); + if (!fu_cabinet_build_jcat_folder(self, cabfolder, error)) + return FALSE; + } + } + + /* adds each metainfo file to the silo */ + for (guint i = 0; i < folders->len; i++) { + GCabFolder *cabfolder = GCAB_FOLDER(g_ptr_array_index(folders, i)); + if (!fu_cabinet_build_silo_folder(self, cabfolder, error)) + return FALSE; + } + + /* sort the components by priority */ + fixup1 = xb_builder_fixup_new("OrderByPriority", fu_cabinet_sort_priority_cb, NULL, NULL); + xb_builder_fixup_set_max_depth(fixup1, 0); + xb_builder_add_fixup(self->builder, fixup1); + + /* ensure the container checksum is always set */ + fixup2 = xb_builder_fixup_new("EnsureContainerChecksum", + fu_cabinet_ensure_container_checksum_cb, + self, + NULL); + xb_builder_add_fixup(self->builder, fixup2); + + fixup3 = xb_builder_fixup_new("LowerCaseCheckSum", + fu_cabinet_set_lowercase_checksum_cb, + self, + NULL); + xb_builder_add_fixup(self->builder, fixup3); + + /* strip inner nodes without children */ + fixup4 = xb_builder_fixup_new("TextStripInner", + fu_cabinet_fixup_strip_inner_text_cb, + self, + NULL); + xb_builder_add_fixup(self->builder, fixup4); + + /* did we get any valid files */ + self->silo = xb_builder_compile(self->builder, +#if LIBXMLB_CHECK_VERSION(0, 3, 4) + XB_BUILDER_COMPILE_FLAG_SINGLE_ROOT, +#else + XB_BUILDER_COMPILE_FLAG_NONE, +#endif + NULL, + error); + if (self->silo == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +typedef struct { + FuCabinet *self; + guint64 size_total; + GError *error; +} FuCabinetDecompressHelper; + +static gboolean +fu_cabinet_decompress_file_cb(GCabFile *file, gpointer user_data) +{ + FuCabinetDecompressHelper *helper = (FuCabinetDecompressHelper *)user_data; + FuCabinet *self = FU_CABINET(helper->self); + g_autofree gchar *basename = NULL; + g_autofree gchar *name = NULL; + + /* already failed */ + if (helper->error != NULL) + return FALSE; + + /* check the size of the compressed file */ + if (gcab_file_get_size(file) > self->size_max) { + g_autofree gchar *sz_val = g_format_size(gcab_file_get_size(file)); + g_autofree gchar *sz_max = g_format_size(self->size_max); + g_set_error(&helper->error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "file %s was too large (%s, limit %s)", + gcab_file_get_name(file), + sz_val, + sz_max); + return FALSE; + } + + /* check the total size of all the compressed files */ + helper->size_total += gcab_file_get_size(file); + if (helper->size_total > self->size_max) { + g_autofree gchar *sz_val = g_format_size(helper->size_total); + g_autofree gchar *sz_max = g_format_size(self->size_max); + g_set_error(&helper->error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "uncompressed data too large (%s, limit %s)", + sz_val, + sz_max); + return FALSE; + } + + /* convert to UNIX paths */ + name = g_strdup(gcab_file_get_name(file)); + g_strdelimit(name, "\\", '/'); + + /* ignore the dirname completely */ + basename = g_path_get_basename(name); + gcab_file_set_extract_name(file, basename); + return TRUE; +} + +static gboolean +fu_cabinet_decompress(FuCabinet *self, GBytes *data, GError **error) +{ + FuCabinetDecompressHelper helper = { + .self = self, + .size_total = 0, + .error = NULL, + }; + g_autoptr(GError) error_local = NULL; + g_autoptr(GInputStream) istream = NULL; + + /* load from a seekable stream */ + istream = g_memory_input_stream_new_from_bytes(data); + if (!gcab_cabinet_load(self->gcab_cabinet, istream, NULL, error)) + return FALSE; + + /* check the size is sane */ + if (gcab_cabinet_get_size(self->gcab_cabinet) > self->size_max) { + g_autofree gchar *sz_val = g_format_size(gcab_cabinet_get_size(self->gcab_cabinet)); + g_autofree gchar *sz_max = g_format_size(self->size_max); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "archive too large (%s, limit %s)", + sz_val, + sz_max); + return FALSE; + } + + /* decompress the file to memory */ + if (!gcab_cabinet_extract_simple(self->gcab_cabinet, + NULL, + fu_cabinet_decompress_file_cb, + &helper, + NULL, + &error_local)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return FALSE; + } + + /* the file callback set an error */ + if (helper.error != NULL) { + g_propagate_error(error, helper.error); + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_cabinet_export: + * @self: a #FuCabinet + * @flags: export flags, e.g. %FU_CABINET_EXPORT_FLAG_NONE + * @error: (nullable): optional return location for an error + * + * Exports the cabinet archive. + * + * Returns: (transfer full): a data blob + * + * Since: 1.6.0 + **/ +GBytes * +fu_cabinet_export(FuCabinet *self, FuCabinetExportFlags flags, GError **error) +{ + g_autoptr(GOutputStream) op = NULL; + op = g_memory_output_stream_new_resizable(); + if (!gcab_cabinet_write_simple(self->gcab_cabinet, + op, + NULL, + NULL, /* progress */ + NULL, + error)) + return NULL; + if (!g_output_stream_close(op, NULL, error)) + return NULL; + return g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(op)); +} + +static gboolean +fu_cabinet_sign_filename(FuCabinet *self, + const gchar *filename, + JcatEngine *jcat_engine, + JcatFile *jcat_file, + GBytes *cert, + GBytes *privkey, + GError **error) +{ + g_autoptr(GBytes) source_blob = NULL; + g_autoptr(JcatBlob) jcat_blob = NULL; + g_autoptr(JcatItem) jcat_item = NULL; + + /* sign the file using the engine */ + source_blob = fu_cabinet_get_file(self, filename, error); + if (source_blob == NULL) + return FALSE; + jcat_item = jcat_file_get_item_by_id(jcat_file, filename, NULL); + if (jcat_item == NULL) { + jcat_item = jcat_item_new(filename); + jcat_file_add_item(jcat_file, jcat_item); + } + jcat_blob = jcat_engine_pubkey_sign(jcat_engine, + source_blob, + cert, + privkey, + JCAT_SIGN_FLAG_ADD_TIMESTAMP | JCAT_SIGN_FLAG_ADD_CERT, + error); + if (jcat_blob == NULL) + return FALSE; + jcat_item_add_blob(jcat_item, jcat_blob); + return TRUE; +} + +static gboolean +fu_cabinet_sign_enumerate_metainfo(FuCabinet *self, GPtrArray *files, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) nodes = NULL; + g_autoptr(XbSilo) silo = fu_cabinet_get_silo(self); + + /* get all the firmware referenced by the metainfo files */ + nodes = xb_silo_query(silo, + "components/component[@type='firmware']/info/filename", + 0, + &error_local); + if (nodes == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_debug("ignoring: %s", error_local->message); + g_ptr_array_add(files, g_strdup("firmware.metainfo.xml")); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } else { + for (guint i = 0; i < nodes->len; i++) { + XbNode *n = g_ptr_array_index(nodes, i); + g_debug("adding: %s", xb_node_get_text(n)); + g_ptr_array_add(files, g_strdup(xb_node_get_text(n))); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cabinet_sign_enumerate_firmware(FuCabinet *self, GPtrArray *files, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) nodes = NULL; + g_autoptr(XbSilo) silo = fu_cabinet_get_silo(self); + + nodes = xb_silo_query(silo, + "components/component[@type='firmware']/releases/" + "release/checksum[@target='content']", + 0, + &error_local); + if (nodes == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_debug("ignoring: %s", error_local->message); + g_ptr_array_add(files, g_strdup("firmware.bin")); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } else { + for (guint i = 0; i < nodes->len; i++) { + XbNode *n = g_ptr_array_index(nodes, i); + g_debug("adding: %s", xb_node_get_attr(n, "filename")); + g_ptr_array_add(files, g_strdup(xb_node_get_attr(n, "filename"))); + } + } + + /* success */ + return TRUE; +} + +/** + * fu_cabinet_sign: + * @self: a #FuCabinet + * @cert: a PCKS#7 certificate + * @privkey: a private key + * @flags: signing flags, e.g. %FU_CABINET_SIGN_FLAG_NONE + * @error: (nullable): optional return location for an error + * + * Sign the cabinet archive using JCat. + * + * Returns: %TRUE for success + * + * Since: 1.6.0 + **/ +gboolean +fu_cabinet_sign(FuCabinet *self, + GBytes *cert, + GBytes *privkey, + FuCabinetSignFlags flags, + GError **error) +{ + g_autoptr(GBytes) new_bytes = NULL; + g_autoptr(GBytes) old_bytes = NULL; + g_autoptr(GOutputStream) ostr = NULL; + g_autoptr(GPtrArray) filenames = g_ptr_array_new_with_free_func(g_free); + g_autoptr(JcatContext) jcat_context = jcat_context_new(); + g_autoptr(JcatEngine) jcat_engine = NULL; + g_autoptr(JcatFile) jcat_file = jcat_file_new(); + + /* load existing .jcat file if it exists */ + old_bytes = fu_cabinet_get_file(self, "firmware.jcat", NULL); + if (old_bytes != NULL) { + g_autoptr(GInputStream) istr = NULL; + istr = g_memory_input_stream_new_from_bytes(old_bytes); + if (!jcat_file_import_stream(jcat_file, istr, JCAT_IMPORT_FLAG_NONE, NULL, error)) + return FALSE; + } + + /* get all the metainfo.xml and firmware.bin files */ + if (!fu_cabinet_sign_enumerate_metainfo(self, filenames, error)) + return FALSE; + if (!fu_cabinet_sign_enumerate_firmware(self, filenames, error)) + return FALSE; + + /* sign all the files */ + jcat_engine = jcat_context_get_engine(jcat_context, JCAT_BLOB_KIND_PKCS7, error); + if (jcat_engine == NULL) + return FALSE; + for (guint i = 0; i < filenames->len; i++) { + const gchar *filename = g_ptr_array_index(filenames, i); + if (!fu_cabinet_sign_filename(self, + filename, + jcat_engine, + jcat_file, + cert, + privkey, + error)) + return FALSE; + } + + /* export new JCat file and add it to the archive */ + ostr = g_memory_output_stream_new_resizable(); + if (!jcat_file_export_stream(jcat_file, ostr, JCAT_EXPORT_FLAG_NONE, NULL, error)) + return FALSE; + new_bytes = g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(ostr)); + fu_cabinet_add_file(self, "firmware.jcat", new_bytes); + return TRUE; +} + +/** + * fu_cabinet_parse: + * @self: a #FuCabinet + * @data: cabinet archive + * @flags: parse flags, e.g. %FU_CABINET_PARSE_FLAG_NONE + * @error: (nullable): optional return location for an error + * + * Parses the cabinet archive. + * + * Returns: %TRUE for success + * + * Since: 1.4.0 + **/ +gboolean +fu_cabinet_parse(FuCabinet *self, GBytes *data, FuCabinetParseFlags flags, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(XbQuery) query = NULL; + + g_return_val_if_fail(FU_IS_CABINET(self), FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(self->silo == NULL, FALSE); + + /* decompress */ + if (!fu_cabinet_decompress(self, data, error)) + return FALSE; + + /* build xmlb silo */ + self->container_checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, data); + self->container_checksum_alt = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, data); + if (!fu_cabinet_build_silo(self, data, error)) + return FALSE; + + /* sanity check */ + components = + xb_silo_query(self->silo, "components/component[@type='firmware']", 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 FALSE; + } + + /* prepare query */ + query = xb_query_new_full(self->silo, + "releases/release", +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + XB_QUERY_FLAG_FORCE_NODE_CACHE, +#else + XB_QUERY_FLAG_NONE, +#endif + error); + if (query == NULL) + return FALSE; + + /* 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_full(component, query, &error_local); + if (releases == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no releases in metainfo file: %s", + error_local->message); + return FALSE; + } + 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_cabinet_parse_release(self, rel, error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +/** + * fu_cabinet_new: + * + * Returns: a #FuCabinet + * + * Since: 1.4.0 + **/ +FuCabinet * +fu_cabinet_new(void) +{ + return g_object_new(FU_TYPE_CABINET, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cabinet.h b/fwupd-1.8.6/libfwupdplugin/fu-cabinet.h new file mode 100644 index 0000000000000000000000000000000000000000..45fa1dff4da32434182bb0180ce903578caafc59 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cabinet.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#define FU_TYPE_CABINET (fu_cabinet_get_type()) + +G_DECLARE_FINAL_TYPE(FuCabinet, fu_cabinet, FU, CABINET, GObject) + +/** + * FuCabinetParseFlags: + * @FU_CABINET_PARSE_FLAG_NONE: No flags set + * + * The flags to use when loading the cabinet. + **/ +typedef enum { + FU_CABINET_PARSE_FLAG_NONE = 0, + /*< private >*/ + FU_CABINET_PARSE_FLAG_LAST +} FuCabinetParseFlags; + +/** + * FuCabinetExportFlags: + * @FU_CABINET_EXPORT_FLAG_NONE: No flags set + * + * The flags to use when exporting the archive. + **/ +typedef enum { + FU_CABINET_EXPORT_FLAG_NONE = 0, + /*< private >*/ + FU_CABINET_EXPORT_FLAG_LAST +} FuCabinetExportFlags; + +/** + * FuCabinetSignFlags: + * @FU_CABINET_SIGN_FLAG_NONE: No flags set + * + * The flags to use when signing the archive. + **/ +typedef enum { + FU_CABINET_SIGN_FLAG_NONE = 0, + /*< private >*/ + FU_CABINET_SIGN_FLAG_LAST +} FuCabinetSignFlags; + +FuCabinet * +fu_cabinet_new(void); +void +fu_cabinet_set_size_max(FuCabinet *self, guint64 size_max); +void +fu_cabinet_set_jcat_context(FuCabinet *self, JcatContext *jcat_context); +gboolean +fu_cabinet_parse(FuCabinet *self, GBytes *data, FuCabinetParseFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_cabinet_sign(FuCabinet *self, + GBytes *cert, + GBytes *privkey, + FuCabinetSignFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_cabinet_add_file(FuCabinet *self, const gchar *basename, GBytes *data); +GBytes * +fu_cabinet_get_file(FuCabinet *self, const gchar *basename, GError **error); +GBytes * +fu_cabinet_export(FuCabinet *self, + FuCabinetExportFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +XbSilo * +fu_cabinet_get_silo(FuCabinet *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfi-device.c b/fwupd-1.8.6/libfwupdplugin/fu-cfi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..5a7ce990c866762fe77405aa4c2934560948f05c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfi-device.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCfiDevice" + +#include "config.h" + +#include "fu-cfi-device.h" +#include "fu-quirks.h" +#include "fu-string.h" + +/** + * FuCfiDevice: + * + * A chip conforming to the Common Flash Memory Interface, typically a SPI flash chip. + * + * Where required, the quirks instance IDs will be added in ->setup(). + * + * The defaults are set as follows, and can be overridden in quirk files: + * + * * `PageSize`: 0x100 + * * `SectorSize`: 0x1000 + * * `BlockSize`: 0x10000 + * + * See also: [class@FuDevice] + */ + +typedef struct { + gchar *flash_id; + guint8 cmd_read_id_sz; + guint32 page_size; + guint32 sector_size; + guint32 block_size; + FuCfiDeviceCmd cmds[FU_CFI_DEVICE_CMD_LAST]; +} FuCfiDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuCfiDevice, fu_cfi_device, FU_TYPE_DEVICE) +enum { PROP_0, PROP_FLASH_ID, PROP_LAST }; + +#define GET_PRIVATE(o) (fu_cfi_device_get_instance_private(o)) + +#define FU_CFI_DEVICE_PAGE_SIZE_DEFAULT 0x100 +#define FU_CFI_DEVICE_SECTOR_SIZE_DEFAULT 0x1000 +#define FU_CFI_DEVICE_BLOCK_SIZE_DEFAULT 0x10000 + +static const gchar * +fu_cfi_device_cmd_to_string(FuCfiDeviceCmd cmd) +{ + if (cmd == FU_CFI_DEVICE_CMD_READ_ID) + return "ReadId"; + if (cmd == FU_CFI_DEVICE_CMD_PAGE_PROG) + return "PageProg"; + if (cmd == FU_CFI_DEVICE_CMD_CHIP_ERASE) + return "ChipErase"; + if (cmd == FU_CFI_DEVICE_CMD_READ_DATA) + return "ReadData"; + if (cmd == FU_CFI_DEVICE_CMD_READ_STATUS) + return "ReadStatus"; + if (cmd == FU_CFI_DEVICE_CMD_SECTOR_ERASE) + return "SectorErase"; + if (cmd == FU_CFI_DEVICE_CMD_WRITE_EN) + return "WriteEn"; + if (cmd == FU_CFI_DEVICE_CMD_WRITE_STATUS) + return "WriteStatus"; + if (cmd == FU_CFI_DEVICE_CMD_BLOCK_ERASE) + return "BlockErase"; + return NULL; +} + +/** + * fu_cfi_device_get_size: + * @self: a #FuCfiDevice + * + * Gets the chip maximum size. + * + * This is typically set with the `FirmwareSizeMax` quirk key. + * + * Returns: size in bytes, or 0 if unknown + * + * Since: 1.7.1 + **/ +guint64 +fu_cfi_device_get_size(FuCfiDevice *self) +{ + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), G_MAXUINT64); + return fu_device_get_firmware_size_max(FU_DEVICE(self)); +} + +/** + * fu_cfi_device_set_size: + * @self: a #FuCfiDevice + * @size: maximum size in bytes, or 0 if unknown + * + * Sets the chip maximum size. + * + * Since: 1.7.1 + **/ +void +fu_cfi_device_set_size(FuCfiDevice *self, guint64 size) +{ + g_return_if_fail(FU_IS_CFI_DEVICE(self)); + fu_device_set_firmware_size_max(FU_DEVICE(self), size); +} + +/** + * fu_cfi_device_get_flash_id: + * @self: a #FuCfiDevice + * + * Gets the chip ID used to identify the device. + * + * Returns: the ID, or %NULL + * + * Since: 1.7.1 + **/ +const gchar * +fu_cfi_device_get_flash_id(FuCfiDevice *self) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), NULL); + return priv->flash_id; +} + +/** + * fu_cfi_device_set_flash_id: + * @self: a #FuCfiDevice + * @flash_id: (nullable): The chip ID + * + * Sets the chip ID used to identify the device. + * + * Since: 1.7.1 + **/ +void +fu_cfi_device_set_flash_id(FuCfiDevice *self, const gchar *flash_id) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFI_DEVICE(self)); + if (g_strcmp0(flash_id, priv->flash_id) == 0) + return; + g_free(priv->flash_id); + priv->flash_id = g_strdup(flash_id); +} + +static void +fu_cfi_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuCfiDevice *self = FU_CFI_DEVICE(object); + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_FLASH_ID: + g_value_set_object(value, priv->flash_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_cfi_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuCfiDevice *self = FU_CFI_DEVICE(object); + switch (prop_id) { + case PROP_FLASH_ID: + fu_cfi_device_set_flash_id(self, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_cfi_device_finalize(GObject *object) +{ + FuCfiDevice *self = FU_CFI_DEVICE(object); + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_free(priv->flash_id); + G_OBJECT_CLASS(fu_cfi_device_parent_class)->finalize(object); +} + +static gboolean +fu_cfi_device_setup(FuDevice *device, GError **error) +{ + gsize flash_idsz = 0; + FuCfiDevice *self = FU_CFI_DEVICE(device); + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + + /* sanity check */ + if (priv->flash_id != NULL) + flash_idsz = strlen(priv->flash_id); + if (flash_idsz == 0 || flash_idsz % 2 != 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "not a valid flash-id"); + return FALSE; + } + + /* typically this will add quirk strings of 2, 4, then 6 bytes */ + for (guint i = 0; i < flash_idsz; i += 2) { + g_autofree gchar *flash_id = g_strndup(priv->flash_id, i + 2); + fu_device_add_instance_str(device, "FLASHID", flash_id); + if (!fu_device_build_instance_id_quirk(device, error, "CFI", "FLASHID", NULL)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_cfi_device_get_cmd: + * @self: a #FuCfiDevice + * @cmd: a #FuCfiDeviceCmd, e.g. %FU_CFI_DEVICE_CMD_CHIP_ERASE + * @value: the API command value to use + * @error: (nullable): optional return location for an error + * + * Gets the self vendor code. + * + * Returns: %TRUE on success + * + * Since: 1.7.1 + **/ +gboolean +fu_cfi_device_get_cmd(FuCfiDevice *self, FuCfiDeviceCmd cmd, guint8 *value, GError **error) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (cmd >= FU_CFI_DEVICE_CMD_LAST) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "CFI cmd invalid"); + return FALSE; + } + if (priv->cmds[cmd] == 0x0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No defined CFI cmd for %s", + fu_cfi_device_cmd_to_string(cmd)); + return FALSE; + } + if (value != NULL) + *value = priv->cmds[cmd]; + return TRUE; +} + +/** + * fu_cfi_device_get_page_size: + * @self: a #FuCfiDevice + * + * Gets the chip page size. This is typically the largest writable block size. + * + * This is typically set with the `CfiDevicePageSize` quirk key. + * + * Returns: page size in bytes + * + * Since: 1.7.3 + **/ +guint32 +fu_cfi_device_get_page_size(FuCfiDevice *self) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), G_MAXUINT32); + return priv->page_size; +} + +/** + * fu_cfi_device_set_page_size: + * @self: a #FuCfiDevice + * @page_size: page size in bytes, or 0 if unknown + * + * Sets the chip page size. This is typically the largest writable block size. + * + * Since: 1.7.3 + **/ +void +fu_cfi_device_set_page_size(FuCfiDevice *self, guint32 page_size) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFI_DEVICE(self)); + priv->page_size = page_size; +} + +/** + * fu_cfi_device_get_sector_size: + * @self: a #FuCfiDevice + * + * Gets the chip sector size. This is typically the smallest erasable page size. + * + * This is typically set with the `CfiDeviceSectorSize` quirk key. + * + * Returns: sector size in bytes + * + * Since: 1.7.3 + **/ +guint32 +fu_cfi_device_get_sector_size(FuCfiDevice *self) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), G_MAXUINT32); + return priv->sector_size; +} + +/** + * fu_cfi_device_set_block_size: + * @self: a #FuCfiDevice + * @block_size: block size in bytes, or 0 if unknown + * + * Sets the chip block size. This is typically the largest erasable chunk size. + * + * Since: 1.7.4 + **/ +void +fu_cfi_device_set_block_size(FuCfiDevice *self, guint32 block_size) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFI_DEVICE(self)); + priv->block_size = block_size; +} + +/** + * fu_cfi_device_get_block_size: + * @self: a #FuCfiDevice + * + * Gets the chip block size. This is typically the largest erasable block size. + * + * This is typically set with the `CfiDeviceBlockSize` quirk key. + * + * Returns: block size in bytes + * + * Since: 1.7.4 + **/ +guint32 +fu_cfi_device_get_block_size(FuCfiDevice *self) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), G_MAXUINT32); + return priv->block_size; +} + +/** + * fu_cfi_device_set_sector_size: + * @self: a #FuCfiDevice + * @sector_size: sector size in bytes, or 0 if unknown + * + * Sets the chip sector size. This is typically the smallest erasable page size. + * + * Since: 1.7.3 + **/ +void +fu_cfi_device_set_sector_size(FuCfiDevice *self, guint32 sector_size) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFI_DEVICE(self)); + priv->sector_size = sector_size; +} + +static gboolean +fu_cfi_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + FuCfiDevice *self = FU_CFI_DEVICE(device); + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_READ_ID) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_READ_ID] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_READ_ID_SZ) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmd_read_id_sz = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_CHIP_ERASE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_CHIP_ERASE] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_BLOCK_ERASE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_BLOCK_ERASE] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_SECTOR_ERASE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_SECTOR_ERASE] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_WRITE_STATUS) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_WRITE_STATUS] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_PAGE_PROG) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_PAGE_PROG] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_READ_DATA) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_READ_DATA] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_READ_STATUS) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_READ_STATUS] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_CMD_WRITE_EN) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->cmds[FU_CFI_DEVICE_CMD_WRITE_EN] = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_PAGE_SIZE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + priv->page_size = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_SECTOR_SIZE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + priv->sector_size = tmp; + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_CFI_DEVICE_BLOCK_SIZE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + priv->block_size = tmp; + return TRUE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_cfi_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCfiDevice *self = FU_CFI_DEVICE(device); + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append(str, idt, "FlashId", priv->flash_id); + for (guint i = 0; i < FU_CFI_DEVICE_CMD_LAST; i++) { + fu_string_append_kx(str, idt, fu_cfi_device_cmd_to_string(i), priv->cmds[i]); + } + if (priv->page_size > 0) + fu_string_append_kx(str, idt, "PageSize", priv->page_size); + if (priv->sector_size > 0) + fu_string_append_kx(str, idt, "SectorSize", priv->sector_size); + if (priv->block_size > 0) + fu_string_append_kx(str, idt, "BlockSize", priv->block_size); +} + +/** + * fu_cfi_device_chip_select: + * @self: a #FuCfiDevice + * @value: boolean + * @error: (nullable): optional return location for an error + * + * Sets the chip select value. + * + * Returns: %TRUE on success + * + * Since: 1.8.0 + **/ +gboolean +fu_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error) +{ + FuCfiDeviceClass *klass = FU_CFI_DEVICE_GET_CLASS(self); + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + if (klass->chip_select == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "chip select is not implemented on this device"); + return FALSE; + } + return klass->chip_select(self, value, error); +} + +static gboolean +fu_cfi_device_chip_select_assert(GObject *device, GError **error) +{ + return fu_cfi_device_chip_select(FU_CFI_DEVICE(device), TRUE, error); +} + +static gboolean +fu_cfi_device_chip_select_deassert(GObject *device, GError **error) +{ + return fu_cfi_device_chip_select(FU_CFI_DEVICE(device), FALSE, error); +} + +/** + * fu_cfi_device_chip_select_locker_new: + * @self: a #FuCfiDevice + * + * Creates a custom device locker that asserts and deasserts the chip select signal. + * + * Returns: (transfer full): (nullable): a #FuDeviceLocker + * + * Since: 1.8.0 + **/ +FuDeviceLocker * +fu_cfi_device_chip_select_locker_new(FuCfiDevice *self, GError **error) +{ + g_return_val_if_fail(FU_IS_CFI_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return fu_device_locker_new_full(self, + fu_cfi_device_chip_select_assert, + fu_cfi_device_chip_select_deassert, + error); +} + +static void +fu_cfi_device_init(FuCfiDevice *self) +{ + FuCfiDevicePrivate *priv = GET_PRIVATE(self); + priv->page_size = FU_CFI_DEVICE_PAGE_SIZE_DEFAULT; + priv->sector_size = FU_CFI_DEVICE_SECTOR_SIZE_DEFAULT; + priv->block_size = FU_CFI_DEVICE_BLOCK_SIZE_DEFAULT; + priv->cmds[FU_CFI_DEVICE_CMD_WRITE_STATUS] = 0x01; + priv->cmds[FU_CFI_DEVICE_CMD_PAGE_PROG] = 0x02; + priv->cmds[FU_CFI_DEVICE_CMD_READ_DATA] = 0x03; + priv->cmds[FU_CFI_DEVICE_CMD_READ_STATUS] = 0x05; + priv->cmds[FU_CFI_DEVICE_CMD_WRITE_EN] = 0x06; + priv->cmds[FU_CFI_DEVICE_CMD_SECTOR_ERASE] = 0x20; + priv->cmds[FU_CFI_DEVICE_CMD_CHIP_ERASE] = 0x60; + priv->cmds[FU_CFI_DEVICE_CMD_READ_ID] = 0x9f; + fu_device_set_summary(FU_DEVICE(self), "CFI flash chip"); +} + +static void +fu_cfi_device_class_init(FuCfiDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GParamSpec *pspec; + + object_class->finalize = fu_cfi_device_finalize; + object_class->get_property = fu_cfi_device_get_property; + object_class->set_property = fu_cfi_device_set_property; + klass_device->setup = fu_cfi_device_setup; + klass_device->to_string = fu_cfi_device_to_string; + klass_device->set_quirk_kv = fu_cfi_device_set_quirk_kv; + + /** + * FuCfiDevice:flash-id: + * + * The CCI JEDEC flash ID. + * + * Since: 1.7.1 + */ + pspec = g_param_spec_string("flash-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FLASH_ID, pspec); +} + +/** + * fu_cfi_device_new: + * @ctx: a #FuContext + * + * Creates a new #FuCfiDevice. + * + * Returns: (transfer full): a #FuCfiDevice + * + * Since: 1.7.1 + **/ +FuCfiDevice * +fu_cfi_device_new(FuContext *ctx, const gchar *flash_id) +{ + return g_object_new(FU_TYPE_CFI_DEVICE, "context", ctx, "flash-id", flash_id, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfi-device.h b/fwupd-1.8.6/libfwupdplugin/fu-cfi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..08dd4dfdb3b871546c5115b24dfaff0f1ad18d4e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfi-device.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-device.h" + +#define FU_TYPE_CFI_DEVICE (fu_cfi_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuCfiDevice, fu_cfi_device, FU, CFI_DEVICE, FuDevice) + +struct _FuCfiDeviceClass { + FuDeviceClass parent_class; + gboolean (*chip_select)(FuCfiDevice *self, gboolean value, GError **error); + gpointer __reserved[30]; +}; + +/** + * FuCfiDeviceCmd: + * @FU_CFI_DEVICE_CMD_READ_ID: Read the chip ID + * @FU_CFI_DEVICE_CMD_PAGE_PROG: Page program + * @FU_CFI_DEVICE_CMD_CHIP_ERASE: Whole chip erase + * @FU_CFI_DEVICE_CMD_READ_DATA: Read data + * @FU_CFI_DEVICE_CMD_READ_STATUS: Read status + * @FU_CFI_DEVICE_CMD_SECTOR_ERASE: Sector erase + * @FU_CFI_DEVICE_CMD_WRITE_EN: Write enable + * @FU_CFI_DEVICE_CMD_WRITE_STATUS: Write status + * @FU_CFI_DEVICE_CMD_BLOCK_ERASE: Block erase + * + * Commands used when calling fu_cfi_device_get_cmd(). + **/ +typedef enum { + FU_CFI_DEVICE_CMD_READ_ID, + FU_CFI_DEVICE_CMD_PAGE_PROG, + FU_CFI_DEVICE_CMD_CHIP_ERASE, + FU_CFI_DEVICE_CMD_READ_DATA, + FU_CFI_DEVICE_CMD_READ_STATUS, + FU_CFI_DEVICE_CMD_SECTOR_ERASE, + FU_CFI_DEVICE_CMD_WRITE_EN, + FU_CFI_DEVICE_CMD_WRITE_STATUS, + FU_CFI_DEVICE_CMD_BLOCK_ERASE, + /*< private >*/ + FU_CFI_DEVICE_CMD_LAST +} FuCfiDeviceCmd; + +FuCfiDevice * +fu_cfi_device_new(FuContext *ctx, const gchar *flash_id); +const gchar * +fu_cfi_device_get_flash_id(FuCfiDevice *self); +void +fu_cfi_device_set_flash_id(FuCfiDevice *self, const gchar *flash_id); +guint64 +fu_cfi_device_get_size(FuCfiDevice *self); +void +fu_cfi_device_set_size(FuCfiDevice *self, guint64 size); +guint32 +fu_cfi_device_get_page_size(FuCfiDevice *self); +void +fu_cfi_device_set_page_size(FuCfiDevice *self, guint32 page_size); +guint32 +fu_cfi_device_get_sector_size(FuCfiDevice *self); +void +fu_cfi_device_set_sector_size(FuCfiDevice *self, guint32 sector_size); +guint32 +fu_cfi_device_get_block_size(FuCfiDevice *self); +void +fu_cfi_device_set_block_size(FuCfiDevice *self, guint32 block_size); +gboolean +fu_cfi_device_get_cmd(FuCfiDevice *self, FuCfiDeviceCmd cmd, guint8 *value, GError **error); + +gboolean +fu_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error); +FuDeviceLocker * +fu_cfi_device_chip_select_locker_new(FuCfiDevice *self, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfu-common.c b/fwupd-1.8.6/libfwupdplugin/fu-cfu-common.c new file mode 100644 index 0000000000000000000000000000000000000000..1a9988be2304f4455a8bc3193d6277238b076588 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfu-common.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 Michael Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-cfu-common.h" + +/** + * fu_cfu_device_reject_to_string: + * @val: an enumerated value, e.g. %FU_CFU_DEVICE_REJECT_OLD_FIRMWARE + * + * Converts an enumerated reject type to a string. + * + * Returns: a string, or `unknown` for invalid + * + * Since: 1.7.0 + **/ +const gchar * +fu_cfu_device_reject_to_string(guint8 val) +{ + if (val == FU_CFU_DEVICE_REJECT_OLD_FIRMWARE) + return "old-firmware"; + if (val == FU_CFU_DEVICE_REJECT_INV_COMPONENT) + return "inv-component"; + if (val == FU_CFU_DEVICE_REJECT_SWAP_PENDING) + return "swap-pending"; + if (val == FU_CFU_DEVICE_REJECT_WRONG_BANK) + return "wrong-bank"; + if (val == FU_CFU_DEVICE_REJECT_SIGN_RULE) + return "sign-rule"; + if (val == FU_CFU_DEVICE_REJECT_VER_RELEASE_DEBUG) + return "ver-release-debug"; + if (val == FU_CFU_DEVICE_REJECT_DEBUG_SAME_VERSION) + return "debug-same-version"; + return "unknown"; +} + +/** + * fu_cfu_device_offer_to_string: + * @val: an enumerated value, e.g. %FU_CFU_DEVICE_OFFER_ACCEPT + * + * Converts an enumerated offer type to a string. + * + * Returns: a string, or `unknown` for invalid + * + * Since: 1.7.0 + **/ +const gchar * +fu_cfu_device_offer_to_string(guint8 val) +{ + if (val == FU_CFU_DEVICE_OFFER_SKIP) + return "skip"; + if (val == FU_CFU_DEVICE_OFFER_ACCEPT) + return "accept"; + if (val == FU_CFU_DEVICE_OFFER_REJECT) + return "reject"; + if (val == FU_CFU_DEVICE_OFFER_BUSY) + return "busy"; + if (val == FU_CFU_DEVICE_OFFER_COMMAND) + return "command"; + if (val == FU_CFU_DEVICE_OFFER_NOT_SUPPORTED) + return "not-supported"; + return "unknown"; +} + +/** + * fu_cfu_device_status_to_string: + * @val: an enumerated value, e.g. %FU_CFU_DEVICE_OFFER_ACCEPT + * + * Converts an enumerated status type to a string. + * + * Returns: a string, or `unknown` for invalid + * + * Since: 1.7.0 + **/ +const gchar * +fu_cfu_device_status_to_string(guint8 val) +{ + if (val == FU_CFU_DEVICE_STATUS_SUCCESS) + return "success"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_PREPARE) + return "error-prepare"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_WRITE) + return "error-write"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_COMPLETE) + return "error-complete"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_VERIFY) + return "error-verify"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_CRC) + return "error-crc"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_SIGNATURE) + return "error-signature"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_VERSION) + return "error-version"; + if (val == FU_CFU_DEVICE_STATUS_SWAP_PENDING) + return "swap-pending"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_INVALID_ADDR) + return "error-invalid-address"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_NO_OFFER) + return "error-no-offer"; + if (val == FU_CFU_DEVICE_STATUS_ERROR_INVALID) + return "error-invalid"; + return "unknown"; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfu-common.h b/fwupd-1.8.6/libfwupdplugin/fu-cfu-common.h new file mode 100644 index 0000000000000000000000000000000000000000..2c94524a5224d4b7b218feb8a14c720a16ed4f66 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfu-common.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 Michael Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_CFU_DEVICE_OFFER_SKIP 0x00 +#define FU_CFU_DEVICE_OFFER_ACCEPT 0x01 +#define FU_CFU_DEVICE_OFFER_REJECT 0x02 +#define FU_CFU_DEVICE_OFFER_BUSY 0x03 +#define FU_CFU_DEVICE_OFFER_COMMAND 0x04 +#define FU_CFU_DEVICE_OFFER_NOT_SUPPORTED 0xFF + +#define FU_CFU_DEVICE_FLAG_FIRST_BLOCK 0x80 +#define FU_CFU_DEVICE_FLAG_LAST_BLOCK 0x40 + +#define FU_CFU_DEVICE_STATUS_SUCCESS 0x00 +#define FU_CFU_DEVICE_STATUS_ERROR_PREPARE 0x01 +#define FU_CFU_DEVICE_STATUS_ERROR_WRITE 0x02 +#define FU_CFU_DEVICE_STATUS_ERROR_COMPLETE 0x03 +#define FU_CFU_DEVICE_STATUS_ERROR_VERIFY 0x04 +#define FU_CFU_DEVICE_STATUS_ERROR_CRC 0x05 +#define FU_CFU_DEVICE_STATUS_ERROR_SIGNATURE 0x06 +#define FU_CFU_DEVICE_STATUS_ERROR_VERSION 0x07 +#define FU_CFU_DEVICE_STATUS_SWAP_PENDING 0x08 +#define FU_CFU_DEVICE_STATUS_ERROR_INVALID_ADDR 0x09 +#define FU_CFU_DEVICE_STATUS_ERROR_NO_OFFER 0x0A +#define FU_CFU_DEVICE_STATUS_ERROR_INVALID 0x0B + +#define FU_CFU_DEVICE_REJECT_OLD_FIRMWARE 0x00 +#define FU_CFU_DEVICE_REJECT_INV_COMPONENT 0x01 +#define FU_CFU_DEVICE_REJECT_SWAP_PENDING 0x02 +#define FU_CFU_DEVICE_REJECT_WRONG_BANK 0x04 +#define FU_CFU_DEVICE_REJECT_SIGN_RULE 0xE0 +#define FU_CFU_DEVICE_REJECT_VER_RELEASE_DEBUG 0xE1 +#define FU_CFU_DEVICE_REJECT_DEBUG_SAME_VERSION 0xE2 + +const gchar * +fu_cfu_device_reject_to_string(guint8 val); +const gchar * +fu_cfu_device_status_to_string(guint8 val); +const gchar * +fu_cfu_device_offer_to_string(guint8 val); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfu-offer.c b/fwupd-1.8.6/libfwupdplugin/fu-cfu-offer.c new file mode 100644 index 0000000000000000000000000000000000000000..88780b336a2d596ce18379adf94e32e5ff9b33d1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfu-offer.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-cfu-offer.h" +#include "fu-common.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * FuCfuOffer: + * + * A CFU offer. This is a 16 byte blob which contains enough data for the device to either accept + * or refuse a firmware payload. The offer may be loaded from disk, network, or even constructed + * manually. There is much left to how the specific firmware implements CFU, and it's expected + * that multiple different plugins will use this offer in different ways. + * + * Documented: https://docs.microsoft.com/en-us/windows-hardware/drivers/cfu/cfu-specification + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint8 segment_number; + gboolean force_immediate_reset; + gboolean force_ignore_version; + guint8 component_id; + guint8 token; + guint32 hw_variant; + guint8 protocol_revision; + guint8 bank; + guint8 milestone; + guint16 product_id; +} FuCfuOfferPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuCfuOffer, fu_cfu_offer, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_cfu_offer_get_instance_private(o)) + +static void +fu_cfu_offer_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "segment_number", priv->segment_number); + fu_xmlb_builder_insert_kb(bn, "force_immediate_reset", priv->force_immediate_reset); + fu_xmlb_builder_insert_kb(bn, "force_ignore_version", priv->force_ignore_version); + fu_xmlb_builder_insert_kx(bn, "component_id", priv->component_id); + fu_xmlb_builder_insert_kx(bn, "token", priv->token); + fu_xmlb_builder_insert_kx(bn, "hw_variant", priv->hw_variant); + fu_xmlb_builder_insert_kx(bn, "protocol_revision", priv->protocol_revision); + fu_xmlb_builder_insert_kx(bn, "bank", priv->bank); + fu_xmlb_builder_insert_kx(bn, "milestone", priv->milestone); + fu_xmlb_builder_insert_kx(bn, "product_id", priv->product_id); +} + +/** + * fu_cfu_offer_get_segment_number: + * @self: a #FuCfuOffer + * + * Gets the part of the firmware that is being transferred. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_segment_number(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->segment_number; +} + +/** + * fu_cfu_offer_get_force_immediate_reset: + * @self: a #FuCfuOffer + * + * Gets if the in-situ firmware should reset into the new firmware immediately, rather than waiting + * for the next time the device is replugged. + * + * Returns: boolean + * + * Since: 1.7.0 + **/ +gboolean +fu_cfu_offer_get_force_immediate_reset(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), FALSE); + return priv->force_immediate_reset; +} + +/** + * fu_cfu_offer_get_force_ignore_version: + * @self: a #FuCfuOffer + * + * Gets if the in-situ firmware should ignore version mismatch (e.g. downgrade). + * + * Returns: boolean + * + * Since: 1.7.0 + **/ +gboolean +fu_cfu_offer_get_force_ignore_version(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), FALSE); + return priv->force_ignore_version; +} + +/** + * fu_cfu_offer_get_component_id: + * @self: a #FuCfuOffer + * + * Gets the component in the device to apply the firmware update. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_component_id(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->component_id; +} + +/** + * fu_cfu_offer_get_token: + * @self: a #FuCfuOffer + * + * Gets the token to identify the user specific software making the offer. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_token(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->token; +} + +/** + * fu_cfu_offer_get_hw_variant: + * @self: a #FuCfuOffer + * + * Gets the hardware variant bitmask corresponding with compatible firmware. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint32 +fu_cfu_offer_get_hw_variant(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->hw_variant; +} + +/** + * fu_cfu_offer_get_protocol_revision: + * @self: a #FuCfuOffer + * + * Gets the CFU protocol version. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_protocol_revision(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->protocol_revision; +} + +/** + * fu_cfu_offer_get_bank: + * @self: a #FuCfuOffer + * + * Gets the bank register, used if multiple banks are supported. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_bank(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->bank; +} + +/** + * fu_cfu_offer_get_milestone: + * @self: a #FuCfuOffer + * + * Gets the milestone, which can be used as a version for example EV1, EVT etc. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_milestone(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->milestone; +} + +/** + * fu_cfu_offer_get_product_id: + * @self: a #FuCfuOffer + * + * Gets the product ID for this CFU image. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint16 +fu_cfu_offer_get_product_id(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->product_id; +} + +/** + * fu_cfu_offer_set_segment_number: + * @self: a #FuCfuOffer + * @segment_number: integer + * + * Sets the part of the firmware that is being transferred. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_segment_number(FuCfuOffer *self, guint8 segment_number) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->segment_number = segment_number; +} + +/** + * fu_cfu_offer_set_force_immediate_reset: + * @self: a #FuCfuOffer + * @force_immediate_reset: boolean + * + * Sets if the in-situ firmware should reset into the new firmware immediately, rather than waiting + * for the next time the device is replugged. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_force_immediate_reset(FuCfuOffer *self, gboolean force_immediate_reset) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->force_immediate_reset = force_immediate_reset; +} + +/** + * fu_cfu_offer_set_force_ignore_version: + * @self: a #FuCfuOffer + * @force_ignore_version: boolean + * + * Sets if the in-situ firmware should ignore version mismatch (e.g. downgrade). + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_force_ignore_version(FuCfuOffer *self, gboolean force_ignore_version) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->force_ignore_version = force_ignore_version; +} + +/** + * fu_cfu_offer_set_component_id: + * @self: a #FuCfuOffer + * @component_id: integer + * + * Sets the component in the device to apply the firmware update. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_component_id(FuCfuOffer *self, guint8 component_id) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->component_id = component_id; +} + +/** + * fu_cfu_offer_set_token: + * @self: a #FuCfuOffer + * @token: integer + * + * Sets the token to identify the user specific software making the offer. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_token(FuCfuOffer *self, guint8 token) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->token = token; +} + +/** + * fu_cfu_offer_set_hw_variant: + * @self: a #FuCfuOffer + * @hw_variant: integer + * + * Sets the hardware variant bitmask corresponding with compatible firmware. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_hw_variant(FuCfuOffer *self, guint32 hw_variant) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->hw_variant = hw_variant; +} + +/** + * fu_cfu_offer_set_protocol_revision: + * @self: a #FuCfuOffer + * @protocol_revision: integer + * + * Sets the CFU protocol version. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_protocol_revision(FuCfuOffer *self, guint8 protocol_revision) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + g_return_if_fail(protocol_revision <= 0b1111); + priv->protocol_revision = protocol_revision; +} + +/** + * fu_cfu_offer_set_bank: + * @self: a #FuCfuOffer + * @bank: integer + * + * Sets bank register, used if multiple banks are supported. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_bank(FuCfuOffer *self, guint8 bank) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + g_return_if_fail(bank <= 0b11); + priv->bank = bank; +} + +/** + * fu_cfu_offer_set_milestone: + * @self: a #FuCfuOffer + * @milestone: integer + * + * Sets the milestone, which can be used as a version for example EV1, EVT etc. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_milestone(FuCfuOffer *self, guint8 milestone) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + g_return_if_fail(milestone <= 0b111); + priv->milestone = milestone; +} + +/** + * fu_cfu_offer_set_product_id: + * @self: a #FuCfuOffer + * @product_id: integer + * + * Sets the product ID for this CFU image. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_product_id(FuCfuOffer *self, guint16 product_id) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->product_id = product_id; +} + +static gboolean +fu_cfu_offer_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + gsize bufsz = 0; + guint8 tmp = 0; + guint32 tmp32 = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* component info */ + if (!fu_memread_uint8_safe(buf, bufsz, 0x0, &priv->segment_number, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, 0x1, &tmp, error)) + return FALSE; + priv->force_ignore_version = (tmp & 0b1) > 0; + priv->force_immediate_reset = (tmp & 0b10) > 0; + if (!fu_memread_uint8_safe(buf, bufsz, 0x2, &priv->component_id, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, 0x3, &priv->token, error)) + return FALSE; + + /* version */ + if (!fu_memread_uint32_safe(buf, bufsz, 0x4, &tmp32, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_firmware_set_version_raw(firmware, tmp32); + if (!fu_memread_uint32_safe(buf, bufsz, 0x8, &priv->hw_variant, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* product info */ + if (!fu_memread_uint8_safe(buf, bufsz, 0xC, &tmp, error)) + return FALSE; + priv->protocol_revision = (tmp >> 4) & 0b1111; + priv->bank = (tmp >> 2) & 0b11; + if (!fu_memread_uint8_safe(buf, bufsz, 0xD, &tmp, error)) + return FALSE; + priv->milestone = (tmp >> 5) & 0b111; + if (!fu_memread_uint16_safe(buf, bufsz, 0xE, &priv->product_id, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static GBytes * +fu_cfu_offer_write(FuFirmware *firmware, GError **error) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* component info */ + fu_byte_array_append_uint8(buf, priv->segment_number); + fu_byte_array_append_uint8(buf, + priv->force_ignore_version | (priv->force_immediate_reset << 1)); + fu_byte_array_append_uint8(buf, priv->component_id); + fu_byte_array_append_uint8(buf, priv->token); + + /* version */ + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, priv->hw_variant, G_LITTLE_ENDIAN); + + /* product info */ + fu_byte_array_append_uint8(buf, (priv->protocol_revision << 4) | (priv->bank << 2)); + fu_byte_array_append_uint8(buf, priv->milestone << 5); + fu_byte_array_append_uint16(buf, priv->product_id, G_LITTLE_ENDIAN); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_cfu_offer_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + guint64 tmp; + const gchar *tmpb; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "segment_number", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->segment_number = tmp; + tmpb = xb_node_query_text(n, "force_immediate_reset", NULL); + if (tmpb != NULL) { + if (!fu_strtobool(tmpb, &priv->force_immediate_reset, error)) + return FALSE; + } + tmpb = xb_node_query_text(n, "force_ignore_version", NULL); + if (tmpb != NULL) { + if (!fu_strtobool(tmpb, &priv->force_ignore_version, error)) + return FALSE; + } + tmp = xb_node_query_text_as_uint(n, "component_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->component_id = tmp; + tmp = xb_node_query_text_as_uint(n, "token", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->token = tmp; + tmp = xb_node_query_text_as_uint(n, "hw_variant", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->hw_variant = tmp; + tmp = xb_node_query_text_as_uint(n, "protocol_revision", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->protocol_revision = tmp; + tmp = xb_node_query_text_as_uint(n, "bank", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->bank = tmp; + tmp = xb_node_query_text_as_uint(n, "milestone", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->milestone = tmp; + tmp = xb_node_query_text_as_uint(n, "product_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + priv->product_id = tmp; + + /* success */ + return TRUE; +} + +static void +fu_cfu_offer_init(FuCfuOffer *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_cfu_offer_class_init(FuCfuOfferClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->export = fu_cfu_offer_export; + klass_firmware->parse = fu_cfu_offer_parse; + klass_firmware->write = fu_cfu_offer_write; + klass_firmware->build = fu_cfu_offer_build; +} + +/** + * fu_cfu_offer_new: + * + * Creates a new #FuFirmware for a CFU offer + * + * Since: 1.7.0 + **/ +FuFirmware * +fu_cfu_offer_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_CFU_OFFER, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfu-offer.h b/fwupd-1.8.6/libfwupdplugin/fu-cfu-offer.h new file mode 100644 index 0000000000000000000000000000000000000000..601d2635a1b73e30d06b71c0e1928535351dbd1b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfu-offer.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_CFU_OFFER (fu_cfu_offer_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuCfuOffer, fu_cfu_offer, FU, CFU_OFFER, FuFirmware) + +struct _FuCfuOfferClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_cfu_offer_new(void); +guint8 +fu_cfu_offer_get_segment_number(FuCfuOffer *self); +gboolean +fu_cfu_offer_get_force_immediate_reset(FuCfuOffer *self); +gboolean +fu_cfu_offer_get_force_ignore_version(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_component_id(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_token(FuCfuOffer *self); +guint32 +fu_cfu_offer_get_hw_variant(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_protocol_revision(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_bank(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_milestone(FuCfuOffer *self); +guint16 +fu_cfu_offer_get_product_id(FuCfuOffer *self); + +void +fu_cfu_offer_set_segment_number(FuCfuOffer *self, guint8 segment_number); +void +fu_cfu_offer_set_force_immediate_reset(FuCfuOffer *self, gboolean force_immediate_reset); +void +fu_cfu_offer_set_force_ignore_version(FuCfuOffer *self, gboolean force_ignore_version); +void +fu_cfu_offer_set_component_id(FuCfuOffer *self, guint8 component_id); +void +fu_cfu_offer_set_token(FuCfuOffer *self, guint8 token); +void +fu_cfu_offer_set_hw_variant(FuCfuOffer *self, guint32 hw_variant); +void +fu_cfu_offer_set_protocol_revision(FuCfuOffer *self, guint8 protocol_revision); +void +fu_cfu_offer_set_bank(FuCfuOffer *self, guint8 bank); +void +fu_cfu_offer_set_milestone(FuCfuOffer *self, guint8 milestone); +void +fu_cfu_offer_set_product_id(FuCfuOffer *self, guint16 product_id); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfu-payload.c b/fwupd-1.8.6/libfwupdplugin/fu-cfu-payload.c new file mode 100644 index 0000000000000000000000000000000000000000..eec5a5e5c54f7267f257710c482a1dd5a23a2ff7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfu-payload.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-cfu-payload.h" +#include "fu-common.h" +#include "fu-mem.h" + +/** + * FuCfuPayload: + * + * A CFU payload. This contains of a variable number of blocks, each containing the address, size + * and the chunk data. The chunks do not have to be the same size, and the address ranges do not + * have to be continuous. + * + * Documented: https://docs.microsoft.com/en-us/windows-hardware/drivers/cfu/cfu-specification + * + * See also: [class@FuFirmware] + */ + +G_DEFINE_TYPE(FuCfuPayload, fu_cfu_payload, FU_TYPE_FIRMWARE) + +static gboolean +fu_cfu_payload_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* process into chunks */ + while (offset < bufsz) { + guint32 chunk_addr = 0; + guint8 chunk_size = 0; + g_autoptr(FuChunk) chk = NULL; + g_autoptr(GBytes) blob = NULL; + + /* read chunk header */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset, + &chunk_addr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x4, &chunk_size, error)) + return FALSE; + offset += 0x5; + blob = fu_bytes_new_offset(fw, offset, chunk_size, error); + if (blob == NULL) + return FALSE; + chk = fu_chunk_bytes_new(blob); + fu_chunk_set_address(chk, chunk_addr); + fu_firmware_add_chunk(firmware, chk); + + /* next! */ + offset += chunk_size; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_cfu_payload_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_firmware_get_chunks(firmware, error); + if (chunks == NULL) + return NULL; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + fu_byte_array_append_uint32(buf, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, fu_chunk_get_data_sz(chk)); + g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_cfu_payload_init(FuCfuPayload *self) +{ +} + +static void +fu_cfu_payload_class_init(FuCfuPayloadClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_cfu_payload_parse; + klass_firmware->write = fu_cfu_payload_write; +} + +/** + * fu_cfu_payload_new: + * + * Creates a new #FuFirmware for a CFU payload + * + * Since: 1.7.0 + **/ +FuFirmware * +fu_cfu_payload_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_CFU_PAYLOAD, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-cfu-payload.h b/fwupd-1.8.6/libfwupdplugin/fu-cfu-payload.h new file mode 100644 index 0000000000000000000000000000000000000000..5dd6e2e6b20ee6653eb4d7077be1fe93cf07cd9c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-cfu-payload.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_CFU_PAYLOAD (fu_cfu_payload_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuCfuPayload, fu_cfu_payload, FU, CFU_PAYLOAD, FuFirmware) + +struct _FuCfuPayloadClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_cfu_payload_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-chunk-private.h b/fwupd-1.8.6/libfwupdplugin/fu-chunk-private.h new file mode 100644 index 0000000000000000000000000000000000000000..9e941afdd63c34d4faa8c3f99888c0fe0cd4ba84 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-chunk-private.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-chunk.h" +#include "fu-firmware.h" + +void +fu_chunk_export(FuChunk *self, FuFirmwareExportFlags flags, XbBuilderNode *bn); +gboolean +fu_chunk_build(FuChunk *self, XbNode *n, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-chunk.c b/fwupd-1.8.6/libfwupdplugin/fu-chunk.c new file mode 100644 index 0000000000000000000000000000000000000000..88be1624eedf5bd6e2d29923b055a3d70bf7b7f6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-chunk.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuChunk" + +#include "config.h" + +#include + +#include "fu-bytes.h" +#include "fu-chunk-private.h" +#include "fu-common.h" +#include "fu-string.h" + +/** + * FuChunk: + * + * A optionally mutable packet of chunked data with address, page and index. + */ + +struct _FuChunk { + GObject parent_instance; + guint32 idx; + guint32 page; + guint32 address; + const guint8 *data; + guint32 data_sz; + gboolean is_mutable; + GBytes *bytes; +}; + +G_DEFINE_TYPE(FuChunk, fu_chunk, G_TYPE_OBJECT) + +/** + * fu_chunk_set_idx: + * @self: a #FuChunk + * @idx: index, starting at 0 + * + * Sets the index of the chunk. + * + * Since: 1.5.6 + **/ +void +fu_chunk_set_idx(FuChunk *self, guint32 idx) +{ + g_return_if_fail(FU_IS_CHUNK(self)); + self->idx = idx; +} + +/** + * fu_chunk_get_idx: + * @self: a #FuChunk + * + * Gets the index of the chunk. + * + * Returns: index + * + * Since: 1.5.6 + **/ +guint32 +fu_chunk_get_idx(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + return self->idx; +} + +/** + * fu_chunk_set_page: + * @self: a #FuChunk + * @page: page number, starting at 0 + * + * Sets the page of the chunk. + * + * Since: 1.5.6 + **/ +void +fu_chunk_set_page(FuChunk *self, guint32 page) +{ + g_return_if_fail(FU_IS_CHUNK(self)); + self->page = page; +} + +/** + * fu_chunk_get_page: + * @self: a #FuChunk + * + * Gets the page of the chunk. + * + * Returns: page + * + * Since: 1.5.6 + **/ +guint32 +fu_chunk_get_page(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + return self->page; +} + +/** + * fu_chunk_set_address: + * @self: a #FuChunk + * @address: memory address + * + * Sets the address of the chunk. + * + * Since: 1.5.6 + **/ +void +fu_chunk_set_address(FuChunk *self, guint32 address) +{ + g_return_if_fail(FU_IS_CHUNK(self)); + self->address = address; +} + +/** + * fu_chunk_get_address: + * @self: a #FuChunk + * + * Gets the address of the chunk. + * + * Returns: address + * + * Since: 1.5.6 + **/ +guint32 +fu_chunk_get_address(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + return self->address; +} + +/** + * fu_chunk_get_data: + * @self: a #FuChunk + * + * Gets the data of the chunk. + * + * Returns: bytes + * + * Since: 1.5.6 + **/ +const guint8 * +fu_chunk_get_data(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), NULL); + return self->data; +} + +/** + * fu_chunk_get_data_out: + * @self: a #FuChunk + * + * Gets the mutable data of the chunk. + * + * WARNING: At the moment fu_chunk_get_data_out() returns the same data as + * fu_chunk_get_data() in all cases. The caller should verify the data passed to + * fu_chunk_array_new() is also writable (i.e. not `const` or `mmap`) before + * using this function. + * + * Returns: (transfer none): bytes + * + * Since: 1.5.6 + **/ +guint8 * +fu_chunk_get_data_out(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), NULL); + + /* warn, but allow to proceed */ + if (!self->is_mutable) { + g_critical("calling fu_chunk_get_data_out() from immutable chunk"); + self->is_mutable = TRUE; + } + return (guint8 *)self->data; +} + +/** + * fu_chunk_get_data_sz: + * @self: a #FuChunk + * + * Gets the data size of the chunk. + * + * Returns: size in bytes + * + * Since: 1.5.6 + **/ +guint32 +fu_chunk_get_data_sz(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + return self->data_sz; +} + +/** + * fu_chunk_set_bytes: + * @self: a #FuChunk + * @bytes: (nullable): data + * + * Sets the data to use for the chunk. + * + * Since: 1.5.6 + **/ +void +fu_chunk_set_bytes(FuChunk *self, GBytes *bytes) +{ + g_return_if_fail(FU_IS_CHUNK(self)); + + /* not changed */ + if (self->bytes == bytes) + return; + + if (self->bytes != NULL) { + g_bytes_unref(self->bytes); + self->bytes = NULL; + } + if (bytes != NULL) { + self->bytes = g_bytes_ref(bytes); + self->data = g_bytes_get_data(bytes, NULL); + self->data_sz = g_bytes_get_size(bytes); + } +} + +/** + * fu_chunk_get_bytes: + * @self: a #FuChunk + * + * Gets the data of the chunk. + * + * Returns: (transfer full): data + * + * Since: 1.5.6 + **/ +GBytes * +fu_chunk_get_bytes(FuChunk *self) +{ + g_return_val_if_fail(FU_IS_CHUNK(self), NULL); + if (self->bytes != NULL) + return g_bytes_ref(self->bytes); + return g_bytes_new_static(self->data, self->data_sz); +} + +/** + * 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. + * + * Returns: (transfer full): a #FuChunk + * + * Since: 1.1.2 + **/ +FuChunk * +fu_chunk_new(guint32 idx, guint32 page, guint32 address, const guint8 *data, guint32 data_sz) +{ + FuChunk *self = g_object_new(FU_TYPE_CHUNK, NULL); + self->idx = idx; + self->page = page; + self->address = address; + self->data = data; + self->data_sz = data_sz; + return self; +} + +/** + * fu_chunk_bytes_new: + * @bytes: (nullable): data + * + * Creates a new packet of data. + * + * Returns: (transfer full): a #FuChunk + * + * Since: 1.5.6 + **/ +FuChunk * +fu_chunk_bytes_new(GBytes *bytes) +{ + FuChunk *self = g_object_new(FU_TYPE_CHUNK, NULL); + fu_chunk_set_bytes(self, bytes); + return self; +} + +void +fu_chunk_export(FuChunk *self, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + fu_xmlb_builder_insert_kx(bn, "idx", self->idx); + fu_xmlb_builder_insert_kx(bn, "page", self->page); + fu_xmlb_builder_insert_kx(bn, "addr", self->address); + if (self->data != NULL) { + g_autofree gchar *datastr = NULL; + g_autofree gchar *dataszstr = g_strdup_printf("0x%x", (guint)self->data_sz); + if (flags & FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA) { + datastr = fu_strsafe((const gchar *)self->data, MIN(self->data_sz, 16)); + } else { + datastr = g_base64_encode(self->data, self->data_sz); + } + xb_builder_node_insert_text(bn, "data", datastr, "size", dataszstr, NULL); + } +} + +/** + * fu_chunk_to_string: + * @self: a #FuChunk + * + * Converts the chunked packet to a string representation. + * + * Returns: (transfer full): a string + * + * Since: 1.1.2 + **/ +gchar * +fu_chunk_to_string(FuChunk *self) +{ + g_autoptr(XbBuilderNode) bn = xb_builder_node_new("chunk"); + fu_chunk_export(self, FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA, bn); + return xb_builder_node_export(bn, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | +#if LIBXMLB_CHECK_VERSION(0, 2, 2) + XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY | +#endif + XB_NODE_EXPORT_FLAG_FORMAT_INDENT, + NULL); +} + +/** + * fu_chunk_array_to_string: + * @chunks: (element-type FuChunk): array of chunks + * + * Converts all the chunked packets in an array to a string representation. + * + * Returns: (transfer full): a string + * + * Since: 1.0.1 + **/ +gchar * +fu_chunk_array_to_string(GPtrArray *chunks) +{ + g_autoptr(XbBuilderNode) bn = xb_builder_node_new("chunks"); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "chunk", NULL); + fu_chunk_export(chk, FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA, bc); + } + return xb_builder_node_export(bn, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | +#if LIBXMLB_CHECK_VERSION(0, 2, 2) + XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY | +#endif + XB_NODE_EXPORT_FLAG_FORMAT_INDENT, + NULL); +} + +/** + * fu_chunk_array_mutable_new: + * @data: a mutable blob of memory + * @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 mutable blob of memory into packets, ensuring each packet does not + * cross a package boundary and is less that a specific transfer size. + * + * Returns: (transfer container) (element-type FuChunk): array of packets + * + * Since: 1.5.6 + **/ +GPtrArray * +fu_chunk_array_mutable_new(guint8 *data, + guint32 data_sz, + guint32 addr_start, + guint32 page_sz, + guint32 packet_sz) +{ + GPtrArray *chunks; + + g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(data_sz > 0, NULL); + + chunks = fu_chunk_array_new(data, data_sz, addr_start, page_sz, packet_sz); + if (chunks == NULL) + return NULL; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + chk->is_mutable = TRUE; + } + return chunks; +} + +/** + * fu_chunk_array_new: + * @data: (nullable): an optional linear blob of memory + * @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. + * + * Returns: (transfer container) (element-type FuChunk): array of packets + * + * Since: 1.1.2 + **/ +GPtrArray * +fu_chunk_array_new(const guint8 *data, + guint32 data_sz, + guint32 addr_start, + guint32 page_sz, + guint32 packet_sz) +{ + GPtrArray *chunks = NULL; + guint32 page_old = G_MAXUINT32; + guint32 idx; + guint32 last_flush = 0; + + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + if (data_sz == 0) + return chunks; + 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(chunks, + fu_chunk_new(chunks->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(chunks, + fu_chunk_new(chunks->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(chunks, + fu_chunk_new(chunks->len, + page, + address_offset, + data_offset, + data_sz - last_flush)); + } + return chunks; +} + +/** + * fu_chunk_array_new_from_bytes: + * @blob: data + * @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. + * + * Returns: (transfer container) (element-type FuChunk): array of packets + * + * Since: 1.1.2 + **/ +GPtrArray * +fu_chunk_array_new_from_bytes(GBytes *blob, guint32 addr_start, guint32 page_sz, guint32 packet_sz) +{ + GPtrArray *chunks; + gsize sz; + const guint8 *data = g_bytes_get_data(blob, &sz); + + chunks = fu_chunk_array_new(data, (guint32)sz, addr_start, page_sz, packet_sz); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + chk->bytes = fu_bytes_new_offset(blob, chk->data - data, chk->data_sz, NULL); + } + return chunks; +} + +/* private */ +gboolean +fu_chunk_build(FuChunk *self, XbNode *n, GError **error) +{ + guint64 tmp; + g_autoptr(XbNode) data = NULL; + + g_return_val_if_fail(FU_IS_CHUNK(self), FALSE); + g_return_val_if_fail(XB_IS_NODE(n), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "idx", NULL); + if (tmp != G_MAXUINT64) + self->idx = tmp; + tmp = xb_node_query_text_as_uint(n, "page", NULL); + if (tmp != G_MAXUINT64) + self->page = tmp; + tmp = xb_node_query_text_as_uint(n, "addr", NULL); + if (tmp != G_MAXUINT64) + self->address = tmp; + data = xb_node_query_first(n, "data", NULL); + if (data != NULL && xb_node_get_text(data) != NULL) { + gsize bufsz = 0; + g_autofree guchar *buf = NULL; + g_autoptr(GBytes) blob = NULL; + buf = g_base64_decode(xb_node_get_text(data), &bufsz); + blob = g_bytes_new(buf, bufsz); + fu_chunk_set_bytes(self, blob); + } else if (data != NULL) { + g_autoptr(GBytes) blob = NULL; + blob = g_bytes_new(NULL, 0); + fu_chunk_set_bytes(self, blob); + } + + /* success */ + return TRUE; +} + +static void +fu_chunk_finalize(GObject *object) +{ + FuChunk *self = FU_CHUNK(object); + if (self->bytes != NULL) + g_bytes_unref(self->bytes); + G_OBJECT_CLASS(fu_chunk_parent_class)->finalize(object); +} + +static void +fu_chunk_class_init(FuChunkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_chunk_finalize; +} + +static void +fu_chunk_init(FuChunk *self) +{ +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-chunk.h b/fwupd-1.8.6/libfwupdplugin/fu-chunk.h new file mode 100644 index 0000000000000000000000000000000000000000..f73322193717f94706824bb68a8513cf432b0399 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-chunk.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CHUNK (fu_chunk_get_type()) + +G_DECLARE_FINAL_TYPE(FuChunk, fu_chunk, FU, CHUNK, GObject) + +FuChunk * +fu_chunk_bytes_new(GBytes *bytes); +void +fu_chunk_set_idx(FuChunk *self, guint32 idx); +guint32 +fu_chunk_get_idx(FuChunk *self); +void +fu_chunk_set_page(FuChunk *self, guint32 page); +guint32 +fu_chunk_get_page(FuChunk *self); +void +fu_chunk_set_address(FuChunk *self, guint32 address); +guint32 +fu_chunk_get_address(FuChunk *self); +const guint8 * +fu_chunk_get_data(FuChunk *self); +guint8 * +fu_chunk_get_data_out(FuChunk *self); +guint32 +fu_chunk_get_data_sz(FuChunk *self); +void +fu_chunk_set_bytes(FuChunk *self, GBytes *bytes); +GBytes * +fu_chunk_get_bytes(FuChunk *self); + +FuChunk * +fu_chunk_new(guint32 idx, guint32 page, guint32 address, const guint8 *data, guint32 data_sz); +gchar * +fu_chunk_to_string(FuChunk *self); + +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_mutable_new(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); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common-darwin.c b/fwupd-1.8.6/libfwupdplugin/fu-common-darwin.c new file mode 100644 index 0000000000000000000000000000000000000000..f5faa5c02f83a17f251d311a1d1e17756e7a7831 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-darwin.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include +#include + +#include "fu-common-private.h" + +GPtrArray * +fu_common_get_block_devices(GError **error) +{ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "getting block devices is not supported on Darwin"); + return NULL; +} + +gboolean +fu_path_fnmatch_impl(const gchar *pattern, const gchar *str) +{ + return fnmatch(pattern, str, FNM_NOESCAPE) == 0; +} + +guint64 +fu_common_get_memory_size_impl(void) +{ + gint mib[] = {CTL_HW, HW_MEMSIZE}; + gint64 physical_memory = 0; + gsize length = sizeof(physical_memory); + sysctl(mib, 2, &physical_memory, &length, NULL, 0); + return (guint64)physical_memory; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common-freebsd.c b/fwupd-1.8.6/libfwupdplugin/fu-common-freebsd.c new file mode 100644 index 0000000000000000000000000000000000000000..7ec040ac33f7be94ee11c363bbbf64e46bfe83ac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-freebsd.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 Sergii Dmytruk + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_FNMATCH_H +#include +#endif + +#include "fu-common-private.h" +#include "fu-path-private.h" + +/* bsdisks doesn't provide Manager object */ +#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2" +#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager" +#define UDISKS_BLOCK_DEVICE_PATH "/org/freedesktop/UDisks2/block_devices/" + +GPtrArray * +fu_common_get_block_devices(GError **error) +{ + GVariant *ifaces; + const size_t device_path_len = strlen(UDISKS_BLOCK_DEVICE_PATH); + const gchar *obj; + g_autoptr(GVariant) output = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GVariantIter) obj_iter = NULL; + g_autoptr(GDBusConnection) connection = NULL; + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) { + g_prefix_error(error, "failed to get system bus: "); + return NULL; + } + proxy = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + UDISKS_DBUS_PATH, + UDISKS_DBUS_MANAGER_INTERFACE, + NULL, + error); + if (proxy == NULL) { + g_prefix_error(error, "failed to find %s: ", UDISKS_DBUS_SERVICE); + return NULL; + } + + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + output = g_dbus_proxy_call_sync(proxy, + "GetManagedObjects", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (output == NULL) { + if (error != NULL) + g_dbus_error_strip_remote_error(*error); + g_prefix_error(error, + "failed to call %s.%s(): ", + UDISKS_DBUS_MANAGER_INTERFACE, + "GetManagedObjects"); + return NULL; + } + + g_variant_get(output, "(a{oa{sa{sv}}})", &obj_iter); + while (g_variant_iter_next(obj_iter, "{&o@a{sa{sv}}}", &obj, &ifaces)) { + const gchar *iface; + GVariant *props; + GVariantIter iface_iter; + + if (strncmp(obj, UDISKS_BLOCK_DEVICE_PATH, device_path_len) != 0) + continue; + + g_variant_iter_init(&iface_iter, ifaces); + while (g_variant_iter_next(&iface_iter, "{&s@a{sv}}", &iface, &props)) { + g_autoptr(GDBusProxy) proxy_blk = NULL; + + g_variant_unref(props); + + if (strcmp(iface, UDISKS_DBUS_INTERFACE_BLOCK) != 0) + continue; + + proxy_blk = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + obj, + UDISKS_DBUS_INTERFACE_BLOCK, + NULL, + error); + if (proxy_blk == NULL) { + g_prefix_error(error, + "failed to initialize d-bus proxy for %s: ", + obj); + return NULL; + } + g_ptr_array_add(devices, g_steal_pointer(&proxy_blk)); + } + g_variant_unref(ifaces); + } + return g_steal_pointer(&devices); +} + +gboolean +fu_path_fnmatch_impl(const gchar *pattern, const gchar *str) +{ +#ifdef HAVE_FNMATCH_H + return fnmatch(pattern, str, FNM_NOESCAPE) == 0; +#else + return g_strcmp0(pattern, str) == 0; +#endif +} + +guint64 +fu_common_get_memory_size_impl(void) +{ + return (guint64)sysconf(_SC_PHYS_PAGES) * (guint64)sysconf(_SC_PAGE_SIZE); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common-guid.c b/fwupd-1.8.6/libfwupdplugin/fu-common-guid.c new file mode 100644 index 0000000000000000000000000000000000000000..953f4abb32244ef2084c8f7f38bf25622a788b1c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-guid.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#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 --git a/fwupd-1.8.6/libfwupdplugin/fu-common-guid.h b/fwupd-1.8.6/libfwupdplugin/fu-common-guid.h new file mode 100644 index 0000000000000000000000000000000000000000..b07c2da3553e32e600a1bcc70824c67fbd1edcd8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-guid.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gboolean +fu_common_guid_is_plausible(const guint8 *buf); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common-linux.c b/fwupd-1.8.6/libfwupdplugin/fu-common-linux.c new file mode 100644 index 0000000000000000000000000000000000000000..ed1ad34e27ce27aee0750930acf8167b4be69447 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-linux.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include +#include + +#ifdef HAVE_FNMATCH_H +#include +#endif + +#include "fu-common-private.h" +#include "fu-path-private.h" + +#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager" +#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager" + +GPtrArray * +fu_common_get_block_devices(GError **error) +{ + GVariantBuilder builder; + const gchar *obj; + g_autoptr(GVariant) output = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GVariantIter) obj_iter = NULL; + g_autoptr(GDBusConnection) connection = NULL; + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) { + g_prefix_error(error, "failed to get system bus: "); + return NULL; + } + proxy = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + UDISKS_DBUS_PATH, + UDISKS_DBUS_MANAGER_INTERFACE, + NULL, + error); + if (proxy == NULL) { + g_prefix_error(error, "failed to find %s: ", UDISKS_DBUS_SERVICE); + return NULL; + } + + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + output = g_dbus_proxy_call_sync(proxy, + "GetBlockDevices", + g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (output == NULL) { + if (error != NULL) + g_dbus_error_strip_remote_error(*error); + g_prefix_error(error, + "failed to call %s.%s(): ", + UDISKS_DBUS_MANAGER_INTERFACE, + "GetBlockDevices"); + return NULL; + } + + g_variant_get(output, "(ao)", &obj_iter); + while (g_variant_iter_next(obj_iter, "&o", &obj)) { + g_autoptr(GDBusProxy) proxy_blk = NULL; + proxy_blk = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + obj, + UDISKS_DBUS_INTERFACE_BLOCK, + NULL, + error); + if (proxy_blk == NULL) { + g_prefix_error(error, "failed to initialize d-bus proxy for %s: ", obj); + return NULL; + } + g_ptr_array_add(devices, g_steal_pointer(&proxy_blk)); + } + return g_steal_pointer(&devices); +} + +gboolean +fu_path_fnmatch_impl(const gchar *pattern, const gchar *str) +{ +#ifdef HAVE_FNMATCH_H + return fnmatch(pattern, str, FNM_NOESCAPE) == 0; +#else + return g_strcmp0(pattern, str) == 0; +#endif +} + +guint64 +fu_common_get_memory_size_impl(void) +{ + return (guint64)sysconf(_SC_PHYS_PAGES) * (guint64)sysconf(_SC_PAGE_SIZE); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common-private.h b/fwupd-1.8.6/libfwupdplugin/fu-common-private.h new file mode 100644 index 0000000000000000000000000000000000000000..a493668c87a9ec86e7a2332158780d2081ea6ddf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-private.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-common.h" + +#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2" +#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition" +#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem" +#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block" + +GPtrArray * +fu_common_get_block_devices(GError **error); +guint64 +fu_common_get_memory_size_impl(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common-windows.c b/fwupd-1.8.6/libfwupdplugin/fu-common-windows.c new file mode 100644 index 0000000000000000000000000000000000000000..fd3e72999c20cf47bbd29cceffa3e3f59036941c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common-windows.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include +#include +#include + +#include "fu-common-private.h" +#include "fu-path-private.h" + +GPtrArray * +fu_common_get_block_devices(GError **error) +{ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "getting block devices is not supported on Windows"); + return NULL; +} + +gboolean +fu_path_fnmatch_impl(const gchar *pattern, const gchar *str) +{ + g_return_val_if_fail(pattern != NULL, FALSE); + g_return_val_if_fail(str != NULL, FALSE); + return PathMatchSpecA(str, pattern); +} + +guint64 +fu_common_get_memory_size_impl(void) +{ + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + return (guint64)status.ullTotalPhys; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common.c b/fwupd-1.8.6/libfwupdplugin/fu-common.c new file mode 100644 index 0000000000000000000000000000000000000000..00b099fbd151dd0353e8b1da5786ac35ccf675c4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#ifdef HAVE_CPUID_H +#include +#endif + +#include "fu-common-private.h" +#include "fu-firmware.h" +#include "fu-string.h" + +/** + * fu_cpuid: + * @leaf: the CPUID level, now called the 'leaf' by Intel + * @eax: (out) (nullable): EAX register + * @ebx: (out) (nullable): EBX register + * @ecx: (out) (nullable): ECX register + * @edx: (out) (nullable): EDX register + * @error: (nullable): optional return location for an error + * + * Calls CPUID and returns the registers for the given leaf. + * + * Returns: %TRUE if the registers are set. + * + * Since: 1.8.2 + **/ +gboolean +fu_cpuid(guint32 leaf, guint32 *eax, guint32 *ebx, guint32 *ecx, guint32 *edx, GError **error) +{ +#ifdef HAVE_CPUID_H + guint eax_tmp = 0; + guint ebx_tmp = 0; + guint ecx_tmp = 0; + guint edx_tmp = 0; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* get vendor */ + __get_cpuid_count(leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp); + if (eax != NULL) + *eax = eax_tmp; + if (ebx != NULL) + *ebx = ebx_tmp; + if (ecx != NULL) + *ecx = ecx_tmp; + if (edx != NULL) + *edx = edx_tmp; + return TRUE; +#else + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no support"); + return FALSE; +#endif +} + +/** + * fu_cpu_get_vendor: + * + * Uses CPUID to discover the CPU vendor. + * + * Returns: a CPU vendor, e.g. %FU_CPU_VENDOR_AMD if the vendor was AMD. + * + * Since: 1.8.2 + **/ +FuCpuVendor +fu_cpu_get_vendor(void) +{ +#ifdef HAVE_CPUID_H + guint ebx = 0; + guint ecx = 0; + guint edx = 0; + + if (fu_cpuid(0x0, NULL, &ebx, &ecx, &edx, NULL)) { + if (ebx == signature_INTEL_ebx && edx == signature_INTEL_edx && + ecx == signature_INTEL_ecx) { + return FU_CPU_VENDOR_INTEL; + } + if (ebx == signature_AMD_ebx && edx == signature_AMD_edx && + ecx == signature_AMD_ecx) { + return FU_CPU_VENDOR_AMD; + } + } +#endif + + /* failed */ + return FU_CPU_VENDOR_UNKNOWN; +} + +/** + * fu_common_is_live_media: + * + * Checks if the user is running from a live media using various heuristics. + * + * Returns: %TRUE if live + * + * Since: 1.4.6 + **/ +gboolean +fu_common_is_live_media(void) +{ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_auto(GStrv) tokens = NULL; + const gchar *args[] = { + "rd.live.image", + "boot=live", + NULL, /* last entry */ + }; + if (g_file_test("/cdrom/.disk/info", G_FILE_TEST_EXISTS)) + return TRUE; + if (!g_file_get_contents("/proc/cmdline", &buf, &bufsz, NULL)) + return FALSE; + if (bufsz == 0) + return FALSE; + tokens = fu_strsplit(buf, bufsz - 1, " ", -1); + for (guint i = 0; args[i] != NULL; i++) { + if (g_strv_contains((const gchar *const *)tokens, args[i])) + return TRUE; + } + return FALSE; +} + +/** + * fu_common_get_memory_size: + * + * Returns the size of physical memory. + * + * Returns: bytes + * + * Since: 1.5.6 + **/ +guint64 +fu_common_get_memory_size(void) +{ + return fu_common_get_memory_size_impl(); +} + +/** + * fu_common_check_full_disk_encryption: + * @error: (nullable): optional return location for an error + * + * Checks that all FDE volumes are not going to be affected by a firmware update. If unsure, + * return with failure and let the user decide. + * + * Returns: %TRUE for success + * + * Since: 1.7.1 + **/ +gboolean +fu_common_check_full_disk_encryption(GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + devices = fu_common_get_block_devices(error); + if (devices == NULL) + return FALSE; + for (guint i = 0; i < devices->len; i++) { + GDBusProxy *proxy = g_ptr_array_index(devices, i); + g_autoptr(GVariant) id_type = g_dbus_proxy_get_cached_property(proxy, "IdType"); + g_autoptr(GVariant) device = g_dbus_proxy_get_cached_property(proxy, "Device"); + if (id_type == NULL || device == NULL) + continue; + if (g_strcmp0(g_variant_get_string(id_type, NULL), "BitLocker") == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK, + "%s device %s is encrypted", + g_variant_get_string(id_type, NULL), + g_variant_get_bytestring(device)); + return FALSE; + } + } + return TRUE; +} + +/** + * fu_common_align_up: + * @value: value to align + * @alignment: align to this power of 2, where 0x1F is the maximum value of 2GB + * + * Align a value to a power of 2 boundary, where @alignment is the bit position + * to align to. If @alignment is zero then @value is always returned unchanged. + * + * Returns: aligned value, which will be the same as @value if already aligned, + * or %G_MAXSIZE if the value would overflow + * + * Since: 1.6.0 + **/ +gsize +fu_common_align_up(gsize value, guint8 alignment) +{ + gsize value_new; + gsize mask = (gsize)1 << alignment; + + g_return_val_if_fail(alignment <= FU_FIRMWARE_ALIGNMENT_2G, G_MAXSIZE); + + /* no alignment required */ + if ((value & (mask - 1)) == 0) + return value; + + /* increment up to the next alignment value */ + value_new = value + mask; + value_new &= ~(mask - 1); + + /* overflow */ + if (value_new < value) + return G_MAXSIZE; + + /* success */ + return value_new; +} + +/** + * fu_battery_state_to_string: + * @battery_state: a battery state, e.g. %FU_BATTERY_STATE_FULLY_CHARGED + * + * Converts an enumerated type to a string. + * + * Returns: a string, or %NULL for invalid + * + * Since: 1.6.0 + **/ +const gchar * +fu_battery_state_to_string(FuBatteryState battery_state) +{ + if (battery_state == FU_BATTERY_STATE_UNKNOWN) + return "unknown"; + if (battery_state == FU_BATTERY_STATE_CHARGING) + return "charging"; + if (battery_state == FU_BATTERY_STATE_DISCHARGING) + return "discharging"; + if (battery_state == FU_BATTERY_STATE_EMPTY) + return "empty"; + if (battery_state == FU_BATTERY_STATE_FULLY_CHARGED) + return "fully-charged"; + return NULL; +} + +/** + * fu_lid_state_to_string: + * @lid_state: a battery state, e.g. %FU_LID_STATE_CLOSED + * + * Converts an enumerated type to a string. + * + * Returns: a string, or %NULL for invalid + * + * Since: 1.7.4 + **/ +const gchar * +fu_lid_state_to_string(FuLidState lid_state) +{ + if (lid_state == FU_LID_STATE_UNKNOWN) + return "unknown"; + if (lid_state == FU_LID_STATE_OPEN) + return "open"; + if (lid_state == FU_LID_STATE_CLOSED) + return "closed"; + return NULL; +} + +/** + * fu_xmlb_builder_insert_kv: + * @bn: #XbBuilderNode + * @key: string key + * @value: string value + * + * Convenience function to add an XML node with a string value. If @value is %NULL + * then no member is added. + * + * Since: 1.6.0 + **/ +void +fu_xmlb_builder_insert_kv(XbBuilderNode *bn, const gchar *key, const gchar *value) +{ + if (value == NULL) + return; + xb_builder_node_insert_text(bn, key, value, NULL); +} + +/** + * fu_xmlb_builder_insert_kx: + * @bn: #XbBuilderNode + * @key: string key + * @value: integer value + * + * Convenience function to add an XML node with an integer value. If @value is 0 + * then no member is added. + * + * Since: 1.6.0 + **/ +void +fu_xmlb_builder_insert_kx(XbBuilderNode *bn, const gchar *key, guint64 value) +{ + g_autofree gchar *value_hex = NULL; + if (value == 0) + return; + value_hex = g_strdup_printf("0x%x", (guint)value); + xb_builder_node_insert_text(bn, key, value_hex, NULL); +} + +/** + * fu_xmlb_builder_insert_kb: + * @bn: #XbBuilderNode + * @key: string key + * @value: boolean value + * + * Convenience function to add an XML node with a boolean value. + * + * Since: 1.6.0 + **/ +void +fu_xmlb_builder_insert_kb(XbBuilderNode *bn, const gchar *key, gboolean value) +{ + xb_builder_node_insert_text(bn, key, value ? "true" : "false", NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-common.h b/fwupd-1.8.6/libfwupdplugin/fu-common.h new file mode 100644 index 0000000000000000000000000000000000000000..4d4ef8fcbd158e8b44f99c362c7bc1ae935d092d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-common.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FuEndianType: + * + * The endian type, e.g. %G_LITTLE_ENDIAN + **/ +typedef guint FuEndianType; + +/** + * FuCpuVendor: + * @FU_CPU_VENDOR_UNKNOWN: Unknown + * @FU_CPU_VENDOR_INTEL: Intel + * @FU_CPU_VENDOR_AMD: AMD + * + * The CPU vendor. + **/ +typedef enum { + FU_CPU_VENDOR_UNKNOWN, + FU_CPU_VENDOR_INTEL, + FU_CPU_VENDOR_AMD, + /*< private >*/ + FU_CPU_VENDOR_LAST +} FuCpuVendor; + +/** + * FuBatteryState: + * @FU_BATTERY_STATE_UNKNOWN: Unknown + * @FU_BATTERY_STATE_CHARGING: Charging + * @FU_BATTERY_STATE_DISCHARGING: Discharging + * @FU_BATTERY_STATE_EMPTY: Empty + * @FU_BATTERY_STATE_FULLY_CHARGED: Fully charged + * + * The device battery state. + **/ +typedef enum { + FU_BATTERY_STATE_UNKNOWN, + FU_BATTERY_STATE_CHARGING, + FU_BATTERY_STATE_DISCHARGING, + FU_BATTERY_STATE_EMPTY, + FU_BATTERY_STATE_FULLY_CHARGED, + /*< private >*/ + FU_BATTERY_STATE_LAST +} FuBatteryState; + +/** + * FuLidState: + * @FU_LID_STATE_UNKNOWN: Unknown + * @FU_LID_STATE_OPEN: Charging + * @FU_LID_STATE_CLOSED: Discharging + * + * The device lid state. + **/ +typedef enum { + FU_LID_STATE_UNKNOWN, + FU_LID_STATE_OPEN, + FU_LID_STATE_CLOSED, + /*< private >*/ + FU_LID_STATE_LAST +} FuLidState; + +gboolean +fu_cpuid(guint32 leaf, guint32 *eax, guint32 *ebx, guint32 *ecx, guint32 *edx, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +FuCpuVendor +fu_cpu_get_vendor(void); +gboolean +fu_common_is_live_media(void); +guint64 +fu_common_get_memory_size(void); +gboolean +fu_common_check_full_disk_encryption(GError **error); + +gsize +fu_common_align_up(gsize value, guint8 alignment); +const gchar * +fu_battery_state_to_string(FuBatteryState battery_state); +const gchar * +fu_lid_state_to_string(FuLidState lid_state); + +void +fu_xmlb_builder_insert_kv(XbBuilderNode *bn, const gchar *key, const gchar *value); +void +fu_xmlb_builder_insert_kx(XbBuilderNode *bn, const gchar *key, guint64 value); +void +fu_xmlb_builder_insert_kb(XbBuilderNode *bn, const gchar *key, gboolean value); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-context-private.h b/fwupd-1.8.6/libfwupdplugin/fu-context-private.h new file mode 100644 index 0000000000000000000000000000000000000000..5e14bbd166ba7191d2e7a2a5e6bb95ff805b02dd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-context-private.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-context.h" +#include "fu-hwids.h" +#include "fu-quirks.h" +#include "fu-volume.h" + +FuContext * +fu_context_new(void); +gboolean +fu_context_reload_bios_settings(FuContext *self, GError **error); +gboolean +fu_context_load_hwinfo(FuContext *self, GError **error); +gboolean +fu_context_load_quirks(FuContext *self, FuQuirksLoadFlags flags, GError **error); +void +fu_context_set_runtime_versions(FuContext *self, GHashTable *runtime_versions); +void +fu_context_set_compile_versions(FuContext *self, GHashTable *compile_versions); +void +fu_context_add_firmware_gtype(FuContext *self, const gchar *id, GType gtype); +GPtrArray * +fu_context_get_firmware_gtype_ids(FuContext *self); +GType +fu_context_get_firmware_gtype_by_id(FuContext *self, const gchar *id); +void +fu_context_add_udev_subsystem(FuContext *self, const gchar *subsystem); +GPtrArray * +fu_context_get_udev_subsystems(FuContext *self); +void +fu_context_add_esp_volume(FuContext *self, FuVolume *volume); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-context.c b/fwupd-1.8.6/libfwupdplugin/fu-context.c new file mode 100644 index 0000000000000000000000000000000000000000..093a591ad3fadcbab156f34d44c55f5d1b2a903b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-context.c @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuContext" + +#include "config.h" + +#include "fu-bios-settings-private.h" +#include "fu-context-private.h" +#include "fu-hwids.h" +#include "fu-smbios-private.h" +#include "fu-volume-private.h" + +/** + * FuContext: + * + * A context that represents the shared system state. This object is shared + * between the engine, the plugins and the devices. + */ + +typedef struct { + FuContextFlags flags; + FuHwids *hwids; + FuSmbios *smbios; + FuQuirks *quirks; + GHashTable *runtime_versions; + GHashTable *compile_versions; + GPtrArray *udev_subsystems; + GPtrArray *esp_volumes; + GHashTable *firmware_gtypes; + GHashTable *hwid_flags; /* str: */ + FuBatteryState battery_state; + FuLidState lid_state; + guint battery_level; + guint battery_threshold; + FuBiosSettings *host_bios_settings; +} FuContextPrivate; + +enum { SIGNAL_SECURITY_CHANGED, SIGNAL_LAST }; + +enum { + PROP_0, + PROP_BATTERY_STATE, + PROP_LID_STATE, + PROP_BATTERY_LEVEL, + PROP_BATTERY_THRESHOLD, + PROP_LAST +}; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FuContext, fu_context, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) (fu_context_get_instance_private(o)) + +/** + * fu_context_get_smbios_string: + * @self: a #FuContext + * @structure_type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @offset: a SMBIOS offset + * + * Gets a hardware SMBIOS string. + * + * The @type and @offset can be referenced from the DMTF SMBIOS specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf + * + * Returns: a string, or %NULL + * + * Since: 1.6.0 + **/ +const gchar * +fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offset) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + return fu_smbios_get_string(priv->smbios, structure_type, offset, NULL); +} + +/** + * fu_context_get_smbios_data: + * @self: a #FuContext + * @structure_type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * + * Gets a hardware SMBIOS data. + * + * Returns: (transfer full): a #GBytes, or %NULL + * + * Since: 1.6.0 + **/ +GBytes * +fu_context_get_smbios_data(FuContext *self, guint8 structure_type) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + return fu_smbios_get_data(priv->smbios, structure_type, NULL); +} + +/** + * fu_context_get_smbios_integer: + * @self: a #FuContext + * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @offset: a structure offset + * + * Reads an integer value from the SMBIOS string table of a specific structure. + * + * The @type and @offset can be referenced from the DMTF SMBIOS specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf + * + * Returns: an integer, or %G_MAXUINT if invalid or not found + * + * Since: 1.6.0 + **/ +guint +fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT); + return fu_smbios_get_integer(priv->smbios, type, offset, NULL); +} + +/** + * fu_context_reload_bios_settings: + * @self: a #FuContext + * @error: (nullable): optional return location for an error + * + * Refreshes the list of firmware attributes on the system. + * + * Since: 1.8.4 + **/ +gboolean +fu_context_reload_bios_settings(FuContext *self, GError **error) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + return fu_bios_settings_setup(priv->host_bios_settings, error); +} + +/** + * fu_context_get_bios_settings: + * @self: a #FuContext + * + * Returns all the firmware attributes defined in the system. + * + * Returns: (transfer full): A #FuBiosSettings + * + * Since: 1.8.4 + **/ +FuBiosSettings * +fu_context_get_bios_settings(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + return g_object_ref(priv->host_bios_settings); +} + +/** + * fu_context_get_bios_setting: + * @self: a #FuContext + * @name: a BIOS setting title, e.g. `BootOrderLock` + * + * Finds out if a system supports a given BIOS setting. + * + * Returns: (transfer none): #FwupdBiosSetting if the attr exists. + * + * Since: 1.8.4 + **/ +FwupdBiosSetting * +fu_context_get_bios_setting(FuContext *self, const gchar *name) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + g_return_val_if_fail(name != NULL, NULL); + return fu_bios_settings_get_attr(priv->host_bios_settings, name); +} + +/** + * fu_context_get_bios_setting_pending_reboot: + * @self: a #FuContext + * + * Determine if updates to BIOS settings are pending until next boot. + * + * Returns: %TRUE if updates are pending. + * + * Since: 1.8.4 + **/ +gboolean +fu_context_get_bios_setting_pending_reboot(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + gboolean ret; + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + fu_bios_settings_get_pending_reboot(priv->host_bios_settings, &ret, NULL); + return ret; +} + +/** + * fu_context_has_hwid_guid: + * @self: a #FuContext + * @guid: a GUID, e.g. `059eb22d-6dc7-59af-abd3-94bbe017f67c` + * + * Finds out if a hardware GUID exists. + * + * Returns: %TRUE if the GUID exists + * + * Since: 1.6.0 + **/ +gboolean +fu_context_has_hwid_guid(FuContext *self, const gchar *guid) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + return fu_hwids_has_guid(priv->hwids, guid); +} + +/** + * fu_context_get_hwid_guids: + * @self: a #FuContext + * + * 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 utf8): an array of GUIDs + * + * Since: 1.6.0 + **/ +GPtrArray * +fu_context_get_hwid_guids(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + return fu_hwids_get_guids(priv->hwids); +} + +/** + * fu_context_get_hwid_value: + * @self: a #FuContext + * @key: a DMI ID, e.g. `BiosVersion` + * + * Gets the cached value for one specific key that is valid ASCII and suitable + * for display. + * + * Returns: the string, e.g. `1.2.3`, or %NULL if not found + * + * Since: 1.6.0 + **/ +const gchar * +fu_context_get_hwid_value(FuContext *self, const gchar *key) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + g_return_val_if_fail(key != NULL, NULL); + return fu_hwids_get_value(priv->hwids, key); +} + +/** + * fu_context_get_hwid_replace_value: + * @self: a #FuContext + * @keys: a key, e.g. `HardwareID-3` or %FU_HWIDS_KEY_PRODUCT_SKU + * @error: (nullable): optional return location for an error + * + * Gets the replacement value for a specific key. All hardware IDs on a + * specific system can be shown using the `fwupdmgr hwids` command. + * + * Returns: (transfer full): a string, or %NULL for error. + * + * Since: 1.6.0 + **/ +gchar * +fu_context_get_hwid_replace_value(FuContext *self, const gchar *keys, GError **error) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + g_return_val_if_fail(keys != NULL, NULL); + return fu_hwids_get_replace_values(priv->hwids, keys, error); +} + +/** + * fu_context_add_runtime_version: + * @self: a #FuContext + * @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 dependency. + * + * Since: 1.6.0 + **/ +void +fu_context_add_runtime_version(FuContext *self, const gchar *component_id, const gchar *version) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(component_id != NULL); + g_return_if_fail(version != NULL); + + if (priv->runtime_versions == NULL) + return; + g_hash_table_insert(priv->runtime_versions, g_strdup(component_id), g_strdup(version)); +} + +/** + * fu_context_set_runtime_versions: + * @self: a #FuContext + * @runtime_versions: (element-type utf8 utf8): dictionary of versions + * + * Sets the runtime versions for a plugin. + * + * Since: 1.6.0 + **/ +void +fu_context_set_runtime_versions(FuContext *self, GHashTable *runtime_versions) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(runtime_versions != NULL); + priv->runtime_versions = g_hash_table_ref(runtime_versions); +} + +/** + * fu_context_add_compile_version: + * @self: a #FuContext + * @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 dependency. + * + * Since: 1.6.0 + **/ +void +fu_context_add_compile_version(FuContext *self, const gchar *component_id, const gchar *version) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(component_id != NULL); + g_return_if_fail(version != NULL); + + if (priv->compile_versions == NULL) + return; + g_hash_table_insert(priv->compile_versions, g_strdup(component_id), g_strdup(version)); +} + +/** + * fu_context_set_compile_versions: + * @self: a #FuContext + * @compile_versions: (element-type utf8 utf8): dictionary of versions + * + * Sets the compile time versions for a plugin. + * + * Since: 1.6.0 + **/ +void +fu_context_set_compile_versions(FuContext *self, GHashTable *compile_versions) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(compile_versions != NULL); + priv->compile_versions = g_hash_table_ref(compile_versions); +} + +/** + * fu_context_add_udev_subsystem: + * @self: a #FuContext + * @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() + * + * Since: 1.6.0 + **/ +void +fu_context_add_udev_subsystem(FuContext *self, const gchar *subsystem) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(subsystem != NULL); + + 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)); +} + +/** + * fu_context_get_udev_subsystems: + * @self: a #FuContext + * + * Gets the udev subsystems required by all plugins. + * + * Returns: (transfer none) (element-type utf8): List of subsystems + * + * Since: 1.6.0 + **/ +GPtrArray * +fu_context_get_udev_subsystems(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + return priv->udev_subsystems; +} + +/** + * fu_context_add_firmware_gtype: + * @self: a #FuContext + * @id: (nullable): an optional string describing the type, e.g. `ihex` + * @gtype: a #GType e.g. `FU_TYPE_FOO_FIRMWARE` + * + * Adds a firmware #GType which is used when creating devices. If @id is not + * specified then it is guessed using the #GType name. + * + * Plugins can use this method only in fu_plugin_init() + * + * Since: 1.6.0 + **/ +void +fu_context_add_firmware_gtype(FuContext *self, const gchar *id, GType gtype) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(id != NULL); + g_return_if_fail(gtype != G_TYPE_INVALID); + g_type_ensure(gtype); + g_hash_table_insert(priv->firmware_gtypes, g_strdup(id), GSIZE_TO_POINTER(gtype)); +} + +/** + * fu_context_get_firmware_gtype_by_id: + * @self: a #FuContext + * @id: an string describing the type, e.g. `ihex` + * + * Returns the #GType using the firmware @id. + * + * Returns: a #GType, or %G_TYPE_INVALID + * + * Since: 1.6.0 + **/ +GType +fu_context_get_firmware_gtype_by_id(FuContext *self, const gchar *id) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), G_TYPE_INVALID); + g_return_val_if_fail(id != NULL, G_TYPE_INVALID); + return GPOINTER_TO_SIZE(g_hash_table_lookup(priv->firmware_gtypes, id)); +} + +static gint +fu_context_gtypes_sort_cb(gconstpointer a, gconstpointer b) +{ + const gchar *stra = *((const gchar **)a); + const gchar *strb = *((const gchar **)b); + return g_strcmp0(stra, strb); +} + +/** + * fu_context_get_firmware_gtype_ids: + * @self: a #FuContext + * + * Returns all the firmware #GType IDs. + * + * Returns: (transfer none) (element-type utf8): List of subsystems + * + * Since: 1.6.0 + **/ +GPtrArray * +fu_context_get_firmware_gtype_ids(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + GPtrArray *firmware_gtypes = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GList) keys = g_hash_table_get_keys(priv->firmware_gtypes); + + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *id = l->data; + g_ptr_array_add(firmware_gtypes, g_strdup(id)); + } + g_ptr_array_sort(firmware_gtypes, fu_context_gtypes_sort_cb); + return firmware_gtypes; +} + +/** + * fu_context_add_quirk_key: + * @self: a #FuContext + * @key: a quirk string, e.g. `DfuVersion` + * + * Adds a possible quirk key. If added by a plugin it should be namespaced + * using the plugin name, where possible. + * + * Plugins can use this method only in fu_plugin_init() + * + * Since: 1.6.0 + **/ +void +fu_context_add_quirk_key(FuContext *self, const gchar *key) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(key != NULL); + if (priv->quirks == NULL) + return; + fu_quirks_add_possible_key(priv->quirks, key); +} + +/** + * fu_context_lookup_quirk_by_id: + * @self: a #FuContext + * @guid: GUID to lookup + * @key: an ID to match the entry, e.g. `Summary` + * + * Looks up an entry in the hardware database using a string value. + * + * Returns: (transfer none): values from the database, or %NULL if not found + * + * Since: 1.6.0 + **/ +const gchar * +fu_context_lookup_quirk_by_id(FuContext *self, const gchar *guid, const gchar *key) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(key != NULL, NULL); + + /* exact ID */ + return fu_quirks_lookup_by_id(priv->quirks, guid, key); +} + +/** + * fu_context_lookup_quirk_by_id_iter: + * @self: a #FuContext + * @guid: GUID to lookup + * @iter_cb: (scope async): a function to call for each result + * @user_data: user data passed to @iter_cb + * + * Looks up all entries in the hardware database using a GUID value. + * + * Returns: %TRUE if the ID was found, and @iter was called + * + * Since: 1.6.0 + **/ +gboolean +fu_context_lookup_quirk_by_id_iter(FuContext *self, + const gchar *guid, + FuContextLookupIter iter_cb, + gpointer user_data) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(iter_cb != NULL, FALSE); + return fu_quirks_lookup_by_id_iter(priv->quirks, guid, (FuQuirksIter)iter_cb, user_data); +} + +/** + * fu_context_security_changed: + * @self: a #FuContext + * + * Informs the daemon that the HSI state may have changed. + * + * Since: 1.6.0 + **/ +void +fu_context_security_changed(FuContext *self) +{ + g_return_if_fail(FU_IS_CONTEXT(self)); + g_signal_emit(self, signals[SIGNAL_SECURITY_CHANGED], 0); +} + +/** + * fu_context_load_hwinfo: + * @self: a #FuContext + * @error: (nullable): optional return location for an error + * + * Loads all hardware information parts of the context. + * + * Returns: %TRUE for success + * + * Since: 1.6.0 + **/ +gboolean +fu_context_load_hwinfo(FuContext *self, GError **error) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + GPtrArray *guids; + g_autoptr(GError) error_smbios = NULL; + g_autoptr(GError) error_hwids = NULL; + g_autoptr(GError) error_bios_settings = NULL; + + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_smbios_setup(priv->smbios, &error_smbios)) + g_warning("Failed to load SMBIOS: %s", error_smbios->message); + if (!fu_hwids_setup(priv->hwids, priv->smbios, &error_hwids)) + g_warning("Failed to load HWIDs: %s", error_hwids->message); + + /* set the hwid flags */ + guids = fu_context_get_hwid_guids(self); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + const gchar *value; + + /* does prefixed quirk exist */ + value = fu_context_lookup_quirk_by_id(self, guid, FU_QUIRKS_FLAGS); + if (value != NULL) { + g_auto(GStrv) values = g_strsplit(value, ",", -1); + for (guint j = 0; values[j] != NULL; j++) + g_hash_table_add(priv->hwid_flags, g_strdup(values[j])); + } + } + + fu_context_add_udev_subsystem(self, "firmware-attributes"); + if (!fu_context_reload_bios_settings(self, &error_bios_settings)) + g_debug("%s", error_bios_settings->message); + + /* always */ + return TRUE; +} + +/** + * fu_context_has_hwid_flag: + * @self: a #FuContext + * @flag: flag, e.g. `use-legacy-bootmgr-desc` + * + * Returns if a HwId custom flag exists, typically added from a DMI quirk. + * + * Returns: %TRUE if the flag exists + * + * Since: 1.7.2 + **/ +gboolean +fu_context_has_hwid_flag(FuContext *self, const gchar *flag) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + g_return_val_if_fail(flag != NULL, FALSE); + return g_hash_table_lookup(priv->hwid_flags, flag) != NULL; +} + +/** + * fu_context_load_quirks: + * @self: a #FuContext + * @flags: quirks load flags, e.g. %FU_QUIRKS_LOAD_FLAG_READONLY_FS + * @error: (nullable): optional return location for an error + * + * Loads all quirks into the context. + * + * Returns: %TRUE for success + * + * Since: 1.6.0 + **/ +gboolean +fu_context_load_quirks(FuContext *self, FuQuirksLoadFlags flags, GError **error) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* rebuild silo if required */ + if (!fu_quirks_load(priv->quirks, flags, &error_local)) + g_warning("Failed to load quirks: %s", error_local->message); + + /* always */ + return TRUE; +} + +/** + * fu_context_get_battery_state: + * @self: a #FuContext + * + * Gets if the system is on battery power, e.g. UPS or laptop battery. + * + * Returns: a battery state, e.g. %FU_BATTERY_STATE_DISCHARGING + * + * Since: 1.6.0 + **/ +FuBatteryState +fu_context_get_battery_state(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + return priv->battery_state; +} + +/** + * fu_context_set_battery_state: + * @self: a #FuContext + * @battery_state: a battery state, e.g. %FU_BATTERY_STATE_DISCHARGING + * + * Sets if the system is on battery power, e.g. UPS or laptop battery. + * + * Since: 1.6.0 + **/ +void +fu_context_set_battery_state(FuContext *self, FuBatteryState battery_state) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + if (priv->battery_state == battery_state) + return; + priv->battery_state = battery_state; + g_debug("battery state now %s", fu_battery_state_to_string(battery_state)); + g_object_notify(G_OBJECT(self), "battery-state"); +} + +/** + * fu_context_get_lid_state: + * @self: a #FuContext + * + * Gets the laptop lid state, if applicable. + * + * Returns: a battery state, e.g. %FU_LID_STATE_CLOSED + * + * Since: 1.7.4 + **/ +FuLidState +fu_context_get_lid_state(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + return priv->lid_state; +} + +/** + * fu_context_set_lid_state: + * @self: a #FuContext + * @lid_state: a battery state, e.g. %FU_LID_STATE_CLOSED + * + * Sets the laptop lid state, if applicable. + * + * Since: 1.7.4 + **/ +void +fu_context_set_lid_state(FuContext *self, FuLidState lid_state) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + if (priv->lid_state == lid_state) + return; + priv->lid_state = lid_state; + g_debug("lid state now %s", fu_lid_state_to_string(lid_state)); + g_object_notify(G_OBJECT(self), "lid-state"); +} + +/** + * fu_context_get_battery_level: + * @self: a #FuContext + * + * Gets the system battery level in percent. + * + * Returns: percentage value, or %FWUPD_BATTERY_LEVEL_INVALID for unknown + * + * Since: 1.6.0 + **/ +guint +fu_context_get_battery_level(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT); + return priv->battery_level; +} + +/** + * fu_context_set_battery_level: + * @self: a #FuContext + * @battery_level: value + * + * Sets the system battery level in percent. + * + * Since: 1.6.0 + **/ +void +fu_context_set_battery_level(FuContext *self, guint battery_level) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(battery_level <= FWUPD_BATTERY_LEVEL_INVALID); + if (priv->battery_level == battery_level) + return; + priv->battery_level = battery_level; + g_debug("battery level now %u", battery_level); + g_object_notify(G_OBJECT(self), "battery-level"); +} + +/** + * fu_context_get_battery_threshold: + * @self: a #FuContext + * + * Gets the system battery threshold in percent. + * + * Returns: percentage value, or %FWUPD_BATTERY_LEVEL_INVALID for unknown + * + * Since: 1.6.0 + **/ +guint +fu_context_get_battery_threshold(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT); + return priv->battery_threshold; +} + +/** + * fu_context_set_battery_threshold: + * @self: a #FuContext + * @battery_threshold: value + * + * Sets the system battery threshold in percent. + * + * Since: 1.6.0 + **/ +void +fu_context_set_battery_threshold(FuContext *self, guint battery_threshold) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(battery_threshold <= FWUPD_BATTERY_LEVEL_INVALID); + if (priv->battery_threshold == battery_threshold) + return; + priv->battery_threshold = battery_threshold; + g_debug("battery threshold now %u", battery_threshold); + g_object_notify(G_OBJECT(self), "battery-threshold"); +} + +/** + * fu_context_add_flag: + * @context: a #FuContext + * @flag: the context flag + * + * Adds a specific flag to the context. + * + * Since: 1.8.5 + **/ +void +fu_context_add_flag(FuContext *context, FuContextFlags flag) +{ + FuContextPrivate *priv = GET_PRIVATE(context); + g_return_if_fail(FU_IS_CONTEXT(context)); + priv->flags |= flag; +} + +/** + * fu_context_has_flag: + * @context: a #FuContext + * @flag: the context flag + * + * Finds if the context has a specific flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.8.5 + **/ +gboolean +fu_context_has_flag(FuContext *context, FuContextFlags flag) +{ + FuContextPrivate *priv = GET_PRIVATE(context); + g_return_val_if_fail(FU_IS_CONTEXT(context), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fu_context_add_esp_volume: + * @self: a #FuContext + * @volume: a #FuVolume + * + * Adds an ESP volume location. + * + * Since: 1.8.5 + **/ +void +fu_context_add_esp_volume(FuContext *self, FuVolume *volume) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_CONTEXT(self)); + g_return_if_fail(FU_IS_VOLUME(volume)); + + /* check for dupes */ + for (guint i = 0; i < priv->esp_volumes->len; i++) { + FuVolume *volume_tmp = g_ptr_array_index(priv->esp_volumes, i); + if (g_strcmp0(fu_volume_get_id(volume_tmp), fu_volume_get_id(volume)) == 0) { + g_debug("not adding duplicate volume %s", fu_volume_get_id(volume)); + return; + } + } + + /* add */ + g_ptr_array_add(priv->esp_volumes, g_object_ref(volume)); +} + +/** + * fu_context_get_esp_volumes: + * @self: a #FuContext + * @error: (nullable): optional return location for an error + * + * Finds all volumes that could be an ESP. + * + * The volumes are cached and so subsequent calls to this function will be much faster. + * + * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if no ESP was found + * + * Since: 1.8.5 + **/ +GPtrArray * +fu_context_get_esp_volumes(FuContext *self, GError **error) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + const gchar *path_tmp; + g_autoptr(GError) error_bdp = NULL; + g_autoptr(GError) error_esp = NULL; + g_autoptr(GPtrArray) volumes_bdp = NULL; + g_autoptr(GPtrArray) volumes_esp = NULL; + + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* cached result */ + if (priv->esp_volumes->len > 0) + return g_ptr_array_ref(priv->esp_volumes); + + /* for the test suite use local directory for ESP */ + path_tmp = g_getenv("FWUPD_UEFI_ESP_PATH"); + if (path_tmp != NULL) { + g_autoptr(FuVolume) vol = fu_volume_new_from_mount_path(path_tmp); + fu_context_add_esp_volume(self, vol); + return g_ptr_array_ref(priv->esp_volumes); + } + + /* ESP */ + volumes_esp = fu_volume_new_by_kind(FU_VOLUME_KIND_ESP, &error_esp); + if (volumes_esp == NULL) { + g_debug("%s", error_esp->message); + } else { + for (guint i = 0; i < volumes_esp->len; i++) { + FuVolume *vol = g_ptr_array_index(volumes_esp, i); + fu_context_add_esp_volume(self, vol); + } + } + + /* BDP */ + volumes_bdp = fu_volume_new_by_kind(FU_VOLUME_KIND_BDP, &error_bdp); + if (volumes_bdp == NULL) { + g_debug("%s", error_bdp->message); + } else { + for (guint i = 0; i < volumes_bdp->len; i++) { + FuVolume *vol = g_ptr_array_index(volumes_bdp, i); + g_autofree gchar *type = fu_volume_get_id_type(vol); + if (g_strcmp0(type, "vfat") != 0) + continue; + if (!fu_volume_is_internal(vol)) + continue; + fu_context_add_esp_volume(self, vol); + } + } + + /* nothing found */ + if (priv->esp_volumes->len == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No ESP or BDP found"); + return NULL; + } + + /* success */ + return g_ptr_array_ref(priv->esp_volumes); +} + +static void +fu_context_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuContext *self = FU_CONTEXT(object); + FuContextPrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_BATTERY_STATE: + g_value_set_uint(value, priv->battery_state); + break; + case PROP_LID_STATE: + g_value_set_uint(value, priv->lid_state); + break; + case PROP_BATTERY_LEVEL: + g_value_set_uint(value, priv->battery_level); + break; + case PROP_BATTERY_THRESHOLD: + g_value_set_uint(value, priv->battery_threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_context_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuContext *self = FU_CONTEXT(object); + switch (prop_id) { + case PROP_BATTERY_STATE: + fu_context_set_battery_state(self, g_value_get_uint(value)); + break; + case PROP_LID_STATE: + fu_context_set_lid_state(self, g_value_get_uint(value)); + break; + case PROP_BATTERY_LEVEL: + fu_context_set_battery_level(self, g_value_get_uint(value)); + break; + case PROP_BATTERY_THRESHOLD: + fu_context_set_battery_threshold(self, g_value_get_uint(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_context_finalize(GObject *object) +{ + FuContext *self = FU_CONTEXT(object); + FuContextPrivate *priv = GET_PRIVATE(self); + + if (priv->runtime_versions != NULL) + g_hash_table_unref(priv->runtime_versions); + if (priv->compile_versions != NULL) + g_hash_table_unref(priv->compile_versions); + g_object_unref(priv->hwids); + g_hash_table_unref(priv->hwid_flags); + g_object_unref(priv->quirks); + g_object_unref(priv->smbios); + g_object_unref(priv->host_bios_settings); + g_hash_table_unref(priv->firmware_gtypes); + g_ptr_array_unref(priv->udev_subsystems); + g_ptr_array_unref(priv->esp_volumes); + + G_OBJECT_CLASS(fu_context_parent_class)->finalize(object); +} + +static void +fu_context_class_init(FuContextClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->get_property = fu_context_get_property; + object_class->set_property = fu_context_set_property; + + /** + * FuContext:battery-state: + * + * The system battery state. + * + * Since: 1.6.0 + */ + pspec = g_param_spec_uint("battery-state", + NULL, + NULL, + FU_BATTERY_STATE_UNKNOWN, + FU_BATTERY_STATE_LAST, + FU_BATTERY_STATE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_STATE, pspec); + + /** + * FuContext:lid-state: + * + * The system lid state. + * + * Since: 1.7.4 + */ + pspec = g_param_spec_uint("lid-state", + NULL, + NULL, + FU_LID_STATE_UNKNOWN, + FU_LID_STATE_LAST, + FU_LID_STATE_UNKNOWN, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_LID_STATE, pspec); + + /** + * FuContext:battery-level: + * + * The system battery level in percent. + * + * Since: 1.6.0 + */ + pspec = g_param_spec_uint("battery-level", + NULL, + NULL, + 0, + FWUPD_BATTERY_LEVEL_INVALID, + FWUPD_BATTERY_LEVEL_INVALID, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_LEVEL, pspec); + + /** + * FuContext:battery-threshold: + * + * The system battery threshold in percent. + * + * Since: 1.6.0 + */ + pspec = g_param_spec_uint("battery-threshold", + NULL, + NULL, + 0, + FWUPD_BATTERY_LEVEL_INVALID, + FWUPD_BATTERY_LEVEL_INVALID, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BATTERY_THRESHOLD, pspec); + + /** + * FuContext::security-changed: + * @self: the #FuContext instance that emitted the signal + * + * The ::security-changed signal is emitted when some system state has changed that could + * have affected the security level. + * + * Since: 1.6.0 + **/ + signals[SIGNAL_SECURITY_CHANGED] = + g_signal_new("security-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuContextClass, security_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + object_class->finalize = fu_context_finalize; +} + +static void +fu_context_init(FuContext *self) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + priv->battery_level = FWUPD_BATTERY_LEVEL_INVALID; + priv->battery_threshold = FWUPD_BATTERY_LEVEL_INVALID; + priv->smbios = fu_smbios_new(); + priv->hwids = fu_hwids_new(); + priv->hwid_flags = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + priv->udev_subsystems = g_ptr_array_new_with_free_func(g_free); + priv->firmware_gtypes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + priv->quirks = fu_quirks_new(); + priv->host_bios_settings = fu_bios_settings_new(); + priv->esp_volumes = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +/** + * fu_context_new: + * + * Creates a new #FuContext + * + * Returns: (transfer full): the object + * + * Since: 1.6.0 + **/ +FuContext * +fu_context_new(void) +{ + return FU_CONTEXT(g_object_new(FU_TYPE_CONTEXT, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-context.h b/fwupd-1.8.6/libfwupdplugin/fu-context.h new file mode 100644 index 0000000000000000000000000000000000000000..8f05fb9d0cc666445012353d73b8ca32ebe840d7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-context.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-bios-settings.h" +#include "fu-common.h" + +#define FU_TYPE_CONTEXT (fu_context_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuContext, fu_context, FU, CONTEXT, GObject) + +struct _FuContextClass { + GObjectClass parent_class; + /* signals */ + void (*security_changed)(FuContext *self); +}; + +/** + * FuContextLookupIter: + * @self: a #FuContext + * @key: a key + * @value: a value + * @user_data: user data + * + * The context lookup iteration callback. + */ +typedef void (*FuContextLookupIter)(FuContext *self, + const gchar *key, + const gchar *value, + gpointer user_data); + +/** + * FU_CONTEXT_FLAG_NONE: + * + * No flags set. + * + * Since: 1.8.5 + **/ +#define FU_CONTEXT_FLAG_NONE (0u) + +/** + * FU_CONTEXT_FLAG_SAVE_EVENTS: + * + * Save events so that they can be replayed to emulate devices. + * + * Since: 1.8.5 + **/ +#define FU_CONTEXT_FLAG_SAVE_EVENTS (1u << 0) + +/** + * FuContextFlags: + * + * The context flags. + **/ +typedef guint64 FuContextFlags; + +void +fu_context_add_flag(FuContext *context, FuContextFlags flag); +gboolean +fu_context_has_flag(FuContext *context, FuContextFlags flag); + +const gchar * +fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offset); +guint +fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset); +GBytes * +fu_context_get_smbios_data(FuContext *self, guint8 structure_type); +gboolean +fu_context_has_hwid_guid(FuContext *self, const gchar *guid); +GPtrArray * +fu_context_get_hwid_guids(FuContext *self); +gboolean +fu_context_has_hwid_flag(FuContext *self, const gchar *flag); +const gchar * +fu_context_get_hwid_value(FuContext *self, const gchar *key); +gchar * +fu_context_get_hwid_replace_value(FuContext *self, + const gchar *keys, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_context_add_runtime_version(FuContext *self, const gchar *component_id, const gchar *version); +void +fu_context_add_compile_version(FuContext *self, const gchar *component_id, const gchar *version); +const gchar * +fu_context_lookup_quirk_by_id(FuContext *self, const gchar *guid, const gchar *key); +gboolean +fu_context_lookup_quirk_by_id_iter(FuContext *self, + const gchar *guid, + FuContextLookupIter iter_cb, + gpointer user_data); +void +fu_context_add_quirk_key(FuContext *self, const gchar *key); +void +fu_context_security_changed(FuContext *self); + +FuBatteryState +fu_context_get_battery_state(FuContext *self); +void +fu_context_set_battery_state(FuContext *self, FuBatteryState battery_state); +FuLidState +fu_context_get_lid_state(FuContext *self); +void +fu_context_set_lid_state(FuContext *self, FuLidState lid_state); +guint +fu_context_get_battery_level(FuContext *self); +void +fu_context_set_battery_level(FuContext *self, guint battery_level); +guint +fu_context_get_battery_threshold(FuContext *self); +void +fu_context_set_battery_threshold(FuContext *self, guint battery_threshold); + +FuBiosSettings * +fu_context_get_bios_settings(FuContext *self); +gboolean +fu_context_get_bios_setting_pending_reboot(FuContext *self); +FwupdBiosSetting * +fu_context_get_bios_setting(FuContext *self, const gchar *name); + +GPtrArray * +fu_context_get_esp_volumes(FuContext *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-coswid-common.c b/fwupd-1.8.6/libfwupdplugin/fu-coswid-common.c new file mode 100644 index 0000000000000000000000000000000000000000..9ebc6d863a7970abe4323d177e292b473007479a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-coswid-common.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-coswid-common.h" + +FuCoswidEntityRole +fu_coswid_entity_role_from_string(const gchar *val) +{ + if (g_strcmp0(val, "tag-creator") == 0) + return FU_COSWID_ENTITY_ROLE_TAG_CREATOR; + if (g_strcmp0(val, "software-creator") == 0) + return FU_COSWID_ENTITY_ROLE_SOFTWARE_CREATOR; + if (g_strcmp0(val, "aggregator") == 0) + return FU_COSWID_ENTITY_ROLE_AGGREGATOR; + if (g_strcmp0(val, "distributor") == 0) + return FU_COSWID_ENTITY_ROLE_DISTRIBUTOR; + if (g_strcmp0(val, "licensor") == 0) + return FU_COSWID_ENTITY_ROLE_LICENSOR; + if (g_strcmp0(val, "maintainer") == 0) + return FU_COSWID_ENTITY_ROLE_MAINTAINER; + return FU_COSWID_ENTITY_ROLE_UNKNOWN; +} + +const gchar * +fu_coswid_entity_role_to_string(FuCoswidEntityRole val) +{ + if (val == FU_COSWID_ENTITY_ROLE_TAG_CREATOR) + return "tag-creator"; + if (val == FU_COSWID_ENTITY_ROLE_SOFTWARE_CREATOR) + return "software-creator"; + if (val == FU_COSWID_ENTITY_ROLE_AGGREGATOR) + return "aggregator"; + if (val == FU_COSWID_ENTITY_ROLE_DISTRIBUTOR) + return "distributor"; + if (val == FU_COSWID_ENTITY_ROLE_LICENSOR) + return "licensor"; + if (val == FU_COSWID_ENTITY_ROLE_MAINTAINER) + return "maintainer"; + return NULL; +} + +FuCoswidLinkRel +fu_coswid_link_rel_from_string(const gchar *val) +{ + if (g_strcmp0(val, "license") == 0) + return FU_COSWID_LINK_REL_LICENSE; + if (g_strcmp0(val, "compiler") == 0) + return FU_COSWID_LINK_REL_COMPILER; + if (g_strcmp0(val, "ancestor") == 0) + return FU_COSWID_LINK_REL_ANCESTOR; + if (g_strcmp0(val, "component") == 0) + return FU_COSWID_LINK_REL_COMPONENT; + if (g_strcmp0(val, "feature") == 0) + return FU_COSWID_LINK_REL_FEATURE; + if (g_strcmp0(val, "installationmedia") == 0) + return FU_COSWID_LINK_REL_INSTALLATIONMEDIA; + if (g_strcmp0(val, "packageinstaller") == 0) + return FU_COSWID_LINK_REL_PACKAGEINSTALLER; + if (g_strcmp0(val, "parent") == 0) + return FU_COSWID_LINK_REL_PARENT; + if (g_strcmp0(val, "patches") == 0) + return FU_COSWID_LINK_REL_PATCHES; + if (g_strcmp0(val, "requires") == 0) + return FU_COSWID_LINK_REL_REQUIRES; + if (g_strcmp0(val, "see-also") == 0) + return FU_COSWID_LINK_REL_SEE_ALSO; + if (g_strcmp0(val, "supersedes") == 0) + return FU_COSWID_LINK_REL_SUPERSEDES; + if (g_strcmp0(val, "supplemental") == 0) + return FU_COSWID_LINK_REL_SUPPLEMENTAL; + return FU_COSWID_LINK_REL_UNKNOWN; +} + +const gchar * +fu_coswid_link_rel_to_string(FuCoswidLinkRel val) +{ + if (val == FU_COSWID_LINK_REL_LICENSE) + return "license"; + if (val == FU_COSWID_LINK_REL_COMPILER) + return "compiler"; + if (val == FU_COSWID_LINK_REL_ANCESTOR) + return "ancestor"; + if (val == FU_COSWID_LINK_REL_COMPONENT) + return "component"; + if (val == FU_COSWID_LINK_REL_FEATURE) + return "feature"; + if (val == FU_COSWID_LINK_REL_INSTALLATIONMEDIA) + return "installationmedia"; + if (val == FU_COSWID_LINK_REL_PACKAGEINSTALLER) + return "packageinstaller"; + if (val == FU_COSWID_LINK_REL_PARENT) + return "parent"; + if (val == FU_COSWID_LINK_REL_PATCHES) + return "patches"; + if (val == FU_COSWID_LINK_REL_REQUIRES) + return "requires"; + if (val == FU_COSWID_LINK_REL_SEE_ALSO) + return "see-also"; + if (val == FU_COSWID_LINK_REL_SUPERSEDES) + return "supersedes"; + if (val == FU_COSWID_LINK_REL_SUPPLEMENTAL) + return "supplemental"; + return NULL; +} + +FuCoswidVersionScheme +fu_coswid_version_scheme_from_string(const gchar *val) +{ + if (g_strcmp0(val, "multipartnumeric") == 0) + return FU_COSWID_VERSION_SCHEME_MULTIPARTNUMERIC; + if (g_strcmp0(val, "multipartnumeric-suffix") == 0) + return FU_COSWID_VERSION_SCHEME_MULTIPARTNUMERIC_SUFFIX; + if (g_strcmp0(val, "alphanumeric") == 0) + return FU_COSWID_VERSION_SCHEME_ALPHANUMERIC; + if (g_strcmp0(val, "decimal") == 0) + return FU_COSWID_VERSION_SCHEME_DECIMAL; + if (g_strcmp0(val, "semver") == 0) + return FU_COSWID_VERSION_SCHEME_SEMVER; + return FU_COSWID_VERSION_SCHEME_UNKNOWN; +} + +const gchar * +fu_coswid_version_scheme_to_string(FuCoswidVersionScheme val) +{ + if (val == FU_COSWID_VERSION_SCHEME_MULTIPARTNUMERIC) + return "multipartnumeric"; + if (val == FU_COSWID_VERSION_SCHEME_MULTIPARTNUMERIC_SUFFIX) + return "multipartnumeric-suffix"; + if (val == FU_COSWID_VERSION_SCHEME_ALPHANUMERIC) + return "alphanumeric"; + if (val == FU_COSWID_VERSION_SCHEME_DECIMAL) + return "decimal"; + if (val == FU_COSWID_VERSION_SCHEME_SEMVER) + return "semver"; + return NULL; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-coswid-common.h b/fwupd-1.8.6/libfwupdplugin/fu-coswid-common.h new file mode 100644 index 0000000000000000000000000000000000000000..7906ce20ca31ab303ba49477e719f8d2843fa3ca --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-coswid-common.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +typedef enum { + FU_COSWID_TAG_TAG_ID, + FU_COSWID_TAG_SOFTWARE_NAME, + FU_COSWID_TAG_ENTITY, + FU_COSWID_TAG_EVIDENCE, + FU_COSWID_TAG_LINK, + FU_COSWID_TAG_SOFTWARE_META, + FU_COSWID_TAG_PAYLOAD, + FU_COSWID_TAG_HASH, + FU_COSWID_TAG_CORPUS, + FU_COSWID_TAG_PATCH, + FU_COSWID_TAG_MEDIA, + FU_COSWID_TAG_SUPPLEMENTAL, + FU_COSWID_TAG_TAG_VERSION, + FU_COSWID_TAG_SOFTWARE_VERSION, + FU_COSWID_TAG_VERSION_SCHEME, + FU_COSWID_TAG_LANG, + FU_COSWID_TAG_DIRECTORY, + FU_COSWID_TAG_FILE, + FU_COSWID_TAG_PROCESS, + FU_COSWID_TAG_RESOURCE, + FU_COSWID_TAG_SIZE, + FU_COSWID_TAG_FILE_VERSION, + FU_COSWID_TAG_KEY, + FU_COSWID_TAG_LOCATION, + FU_COSWID_TAG_FS_NAME, + FU_COSWID_TAG_ROOT, + FU_COSWID_TAG_PATH_ELEMENTS, + FU_COSWID_TAG_PROCESS_NAME, + FU_COSWID_TAG_PID, + FU_COSWID_TAG_TYPE, + FU_COSWID_TAG_MISSING30, /* not in the spec! */ + FU_COSWID_TAG_ENTITY_NAME, + FU_COSWID_TAG_REG_ID, + FU_COSWID_TAG_ROLE, + FU_COSWID_TAG_THUMBPRINT, + FU_COSWID_TAG_DATE, + FU_COSWID_TAG_DEVICE_ID, + FU_COSWID_TAG_ARTIFACT, + FU_COSWID_TAG_HREF, + FU_COSWID_TAG_OWNERSHIP, + FU_COSWID_TAG_REL, + FU_COSWID_TAG_MEDIA_TYPE, + FU_COSWID_TAG_USE, + FU_COSWID_TAG_ACTIVATION_STATUS, + FU_COSWID_TAG_CHANNEL_TYPE, + FU_COSWID_TAG_COLLOQUIAL_VERSION, + FU_COSWID_TAG_DESCRIPTION, + FU_COSWID_TAG_EDITION, + FU_COSWID_TAG_ENTITLEMENT_DATA_REQUIRED, + FU_COSWID_TAG_ENTITLEMENT_KEY, + FU_COSWID_TAG_GENERATOR, + FU_COSWID_TAG_PERSISTENT_ID, + FU_COSWID_TAG_PRODUCT, + FU_COSWID_TAG_PRODUCT_FAMILY, + FU_COSWID_TAG_REVISION, + FU_COSWID_TAG_SUMMARY, + FU_COSWID_TAG_UNSPSC_CODE, + FU_COSWID_TAG_UNSPSC_VERSION, +} FuCoswidTag; + +typedef enum { + FU_COSWID_VERSION_SCHEME_UNKNOWN, + FU_COSWID_VERSION_SCHEME_MULTIPARTNUMERIC, + FU_COSWID_VERSION_SCHEME_MULTIPARTNUMERIC_SUFFIX, + FU_COSWID_VERSION_SCHEME_ALPHANUMERIC, + FU_COSWID_VERSION_SCHEME_DECIMAL, + FU_COSWID_VERSION_SCHEME_SEMVER = 16384, +} FuCoswidVersionScheme; + +typedef enum { + FU_COSWID_LINK_REL_LICENSE = -2, + FU_COSWID_LINK_REL_COMPILER = -1, + FU_COSWID_LINK_REL_UNKNOWN = 0, + FU_COSWID_LINK_REL_ANCESTOR = 1, + FU_COSWID_LINK_REL_COMPONENT = 2, + FU_COSWID_LINK_REL_FEATURE = 3, + FU_COSWID_LINK_REL_INSTALLATIONMEDIA = 4, + FU_COSWID_LINK_REL_PACKAGEINSTALLER = 5, + FU_COSWID_LINK_REL_PARENT = 6, + FU_COSWID_LINK_REL_PATCHES = 7, + FU_COSWID_LINK_REL_REQUIRES = 8, + FU_COSWID_LINK_REL_SEE_ALSO = 9, + FU_COSWID_LINK_REL_SUPERSEDES = 10, + FU_COSWID_LINK_REL_SUPPLEMENTAL = 11, +} FuCoswidLinkRel; + +typedef enum { + FU_COSWID_ENTITY_ROLE_UNKNOWN, + FU_COSWID_ENTITY_ROLE_TAG_CREATOR, + FU_COSWID_ENTITY_ROLE_SOFTWARE_CREATOR, + FU_COSWID_ENTITY_ROLE_AGGREGATOR, + FU_COSWID_ENTITY_ROLE_DISTRIBUTOR, + FU_COSWID_ENTITY_ROLE_LICENSOR, + FU_COSWID_ENTITY_ROLE_MAINTAINER, +} FuCoswidEntityRole; + +FuCoswidEntityRole +fu_coswid_entity_role_from_string(const gchar *val); +const gchar * +fu_coswid_entity_role_to_string(FuCoswidEntityRole val); +FuCoswidLinkRel +fu_coswid_link_rel_from_string(const gchar *val); +const gchar * +fu_coswid_link_rel_to_string(FuCoswidLinkRel val); +FuCoswidVersionScheme +fu_coswid_version_scheme_from_string(const gchar *val); +const gchar * +fu_coswid_version_scheme_to_string(FuCoswidVersionScheme val); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-coswid-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-coswid-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..87fa75970ffd080e21e927956d298e7aaedac3c0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-coswid-firmware.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#ifdef HAVE_CBOR +#include +#endif + +#include "fu-common.h" +#include "fu-coswid-common.h" +#include "fu-coswid-firmware.h" + +/** + * FuCoswidFirmware: + * + * A coSWID SWID section. + * + * See also: [class@FuCoswidFirmware] + */ + +typedef struct { + gchar *product; + gchar *summary; + gchar *colloquial_version; + FuCoswidVersionScheme version_scheme; + GPtrArray *links; /* of FuCoswidFirmwareLink */ + GPtrArray *entities; /* of FuCoswidFirmwareEntity */ +} FuCoswidFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuCoswidFirmware, fu_coswid_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_coswid_firmware_get_instance_private(o)) + +typedef struct { + gchar *name; + gchar *regid; + FuCoswidEntityRole roles[6]; +} FuCoswidFirmwareEntity; + +typedef struct { + gchar *href; + FuCoswidLinkRel rel; +} FuCoswidFirmwareLink; + +static void +fu_coswid_firmware_entity_free(FuCoswidFirmwareEntity *entity) +{ + g_free(entity->name); + g_free(entity->regid); + g_free(entity); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCoswidFirmwareEntity, fu_coswid_firmware_entity_free) + +static void +fu_coswid_firmware_link_free(FuCoswidFirmwareLink *link) +{ + g_free(link->href); + g_free(link); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCoswidFirmwareLink, fu_coswid_firmware_link_free) + +#ifdef HAVE_CBOR +G_DEFINE_AUTOPTR_CLEANUP_FUNC(cbor_item_t, cbor_intermediate_decref) + +static gchar * +fu_coswid_firmware_strndup(cbor_item_t *item) +{ + if (!cbor_string_is_definite(item)) + return NULL; + return g_strndup((const gchar *)cbor_string_handle(item), cbor_string_length(item)); +} + +static gboolean +fu_coswid_firmware_parse_meta(FuCoswidFirmware *self, cbor_item_t *item, GError **error) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + struct cbor_pair *pairs = cbor_map_handle(item); + + for (gsize i = 0; i < cbor_map_size(item); i++) { + FuCoswidTag tag_id = cbor_get_uint8(pairs[i].key); + if (tag_id == FU_COSWID_TAG_SUMMARY) { + priv->summary = fu_coswid_firmware_strndup(pairs[i].value); + } else if (tag_id == FU_COSWID_TAG_COLLOQUIAL_VERSION) { + priv->colloquial_version = fu_coswid_firmware_strndup(pairs[i].value); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_coswid_firmware_parse_link(FuCoswidFirmware *self, cbor_item_t *item, GError **error) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + struct cbor_pair *pairs = cbor_map_handle(item); + g_autoptr(FuCoswidFirmwareLink) link = g_new0(FuCoswidFirmwareLink, 1); + + for (gsize i = 0; i < cbor_map_size(item); i++) { + FuCoswidTag tag_id = cbor_get_uint8(pairs[i].key); + if (tag_id == FU_COSWID_TAG_HREF) { + link->href = fu_coswid_firmware_strndup(pairs[i].value); + } else if (tag_id == FU_COSWID_TAG_REL) { + if (cbor_isa_negint(pairs[i].value)) + link->rel = (-1) - cbor_get_uint8(pairs[i].value); + else + link->rel = cbor_get_uint8(pairs[i].value); + } + } + + /* success */ + g_ptr_array_add(priv->links, g_steal_pointer(&link)); + return TRUE; +} + +static gboolean +fu_coswid_firmware_parse_entity(FuCoswidFirmware *self, cbor_item_t *item, GError **error) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + struct cbor_pair *pairs = cbor_map_handle(item); + guint entity_role_cnt = 0; + g_autoptr(FuCoswidFirmwareEntity) entity = g_new0(FuCoswidFirmwareEntity, 1); + + for (gsize i = 0; i < cbor_map_size(item); i++) { + FuCoswidTag tag_id = cbor_get_uint8(pairs[i].key); + if (tag_id == FU_COSWID_TAG_ENTITY_NAME) { + entity->name = fu_coswid_firmware_strndup(pairs[i].value); + } else if (tag_id == FU_COSWID_TAG_REG_ID) { + entity->regid = fu_coswid_firmware_strndup(pairs[i].value); + } else if (tag_id == FU_COSWID_TAG_ROLE) { + for (guint j = 0; j < cbor_array_size(pairs[i].value); j++) { + g_autoptr(cbor_item_t) value = cbor_array_get(pairs[i].value, j); + FuCoswidEntityRole role = cbor_get_uint8(value); + if (entity_role_cnt >= G_N_ELEMENTS(entity->roles)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "too many roles"); + return FALSE; + } + entity->roles[entity_role_cnt++] = role; + } + } + } + + /* success */ + g_ptr_array_add(priv->entities, g_steal_pointer(&entity)); + return TRUE; +} +#endif + +static gboolean +fu_coswid_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ +#ifdef HAVE_CBOR + FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + struct cbor_load_result result = {0x0}; + struct cbor_pair *pairs = NULL; + g_autoptr(cbor_item_t) item = NULL; + + item = cbor_load(g_bytes_get_data(fw, NULL), g_bytes_get_size(fw), &result); + if (item == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to parse CBOR at offset 0x%x: 0x%x", + (guint)result.error.position, + result.error.code); + return FALSE; + } + fu_firmware_set_size(firmware, result.read); + + /* pretty-print the result */ + if (g_getenv("FWUPD_CBOR_VERBOSE") != NULL) { + cbor_describe(item, stdout); + fflush(stdout); + } + + /* parse out anything interesting */ + pairs = cbor_map_handle(item); + for (gsize i = 0; i < cbor_map_size(item); i++) { + FuCoswidTag tag_id = cbor_get_uint8(pairs[i].key); + + /* identity can be specified as a string or in binary */ + if (tag_id == FU_COSWID_TAG_TAG_ID) { + g_autofree gchar *str = NULL; + if (cbor_isa_string(pairs[i].value)) { + str = fu_coswid_firmware_strndup(pairs[i].value); + } else if (cbor_isa_bytestring(pairs[i].value) && + cbor_bytestring_length(pairs[i].value) == 16) { + str = fwupd_guid_to_string( + (const fwupd_guid_t *)cbor_bytestring_handle(pairs[i].value), + FWUPD_GUID_FLAG_NONE); + } + if (str != NULL) + fu_firmware_set_id(firmware, str); + } else if (tag_id == FU_COSWID_TAG_SOFTWARE_NAME) { + priv->product = fu_coswid_firmware_strndup(pairs[i].value); + } else if (tag_id == FU_COSWID_TAG_SOFTWARE_VERSION) { + g_autofree gchar *str = fu_coswid_firmware_strndup(pairs[i].value); + fu_firmware_set_version(firmware, str); + } else if (tag_id == FU_COSWID_TAG_VERSION_SCHEME) { + priv->version_scheme = cbor_get_uint16(pairs[i].value); + } else if (tag_id == FU_COSWID_TAG_SOFTWARE_META) { + if (!fu_coswid_firmware_parse_meta(self, pairs[i].value, error)) + return FALSE; + } else if (tag_id == FU_COSWID_TAG_LINK) { + for (guint j = 0; j < cbor_array_size(pairs[i].value); j++) { + g_autoptr(cbor_item_t) value = cbor_array_get(pairs[i].value, j); + if (!fu_coswid_firmware_parse_link(self, value, error)) + return FALSE; + } + } else if (tag_id == FU_COSWID_TAG_ENTITY) { + for (guint j = 0; j < cbor_array_size(pairs[i].value); j++) { + g_autoptr(cbor_item_t) value = cbor_array_get(pairs[i].value, j); + if (!fu_coswid_firmware_parse_entity(self, value, error)) + return FALSE; + } + } + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "not compiled with CBOR support"); + return FALSE; +#endif +} + +#ifdef HAVE_CBOR +static void +fu_coswid_firmware_write_tag_string(cbor_item_t *root, FuCoswidTag tag, const gchar *item) +{ + g_autoptr(cbor_item_t) key = cbor_build_uint8(tag); + g_autoptr(cbor_item_t) val = cbor_build_string(item); + cbor_map_add(root, (struct cbor_pair){.key = key, .value = val}); +} + +static void +fu_coswid_firmware_write_tag_bool(cbor_item_t *root, FuCoswidTag tag, gboolean item) +{ + g_autoptr(cbor_item_t) key = cbor_build_uint8(tag); + g_autoptr(cbor_item_t) val = cbor_build_bool(item); + cbor_map_add(root, (struct cbor_pair){.key = key, .value = val}); +} + +static void +fu_coswid_firmware_write_tag_uint16(cbor_item_t *root, FuCoswidTag tag, guint16 item) +{ + g_autoptr(cbor_item_t) key = cbor_build_uint8(tag); + g_autoptr(cbor_item_t) val = cbor_build_uint16(item); + cbor_map_add(root, (struct cbor_pair){.key = key, .value = val}); +} + +static void +fu_coswid_firmware_write_tag_int8(cbor_item_t *root, FuCoswidTag tag, gint8 item) +{ + g_autoptr(cbor_item_t) key = cbor_build_uint8(tag); + g_autoptr(cbor_item_t) val = cbor_new_int8(); + if (item >= 0) { + cbor_set_uint8(val, item); + } else { + cbor_set_uint8(val, 0xFF - item); + cbor_mark_negint(val); + } + cbor_map_add(root, (struct cbor_pair){.key = key, .value = val}); +} + +static void +fu_coswid_firmware_write_tag_item(cbor_item_t *root, FuCoswidTag tag, cbor_item_t *item) +{ + g_autoptr(cbor_item_t) key = cbor_build_uint8(tag); + cbor_map_add(root, (struct cbor_pair){.key = key, .value = item}); +} +#endif + +static GBytes * +fu_coswid_firmware_write(FuFirmware *firmware, GError **error) +{ +#ifdef HAVE_CBOR + FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + gsize buflen; + gsize bufsz = 0; + g_autofree guchar *buf = NULL; + g_autoptr(cbor_item_t) root = cbor_new_indefinite_map(); + g_autoptr(cbor_item_t) item_meta = cbor_new_indefinite_map(); + + /* preallocate the map structure */ + fu_coswid_firmware_write_tag_string(root, FU_COSWID_TAG_LANG, "en-US"); + if (fu_firmware_get_id(firmware) != NULL) { + fu_coswid_firmware_write_tag_string(root, + FU_COSWID_TAG_TAG_ID, + fu_firmware_get_id(firmware)); + } + fu_coswid_firmware_write_tag_bool(root, FU_COSWID_TAG_CORPUS, TRUE); + if (priv->product != NULL) { + fu_coswid_firmware_write_tag_string(root, + FU_COSWID_TAG_SOFTWARE_NAME, + priv->product); + } + if (fu_firmware_get_version(firmware) != NULL) { + fu_coswid_firmware_write_tag_string(root, + FU_COSWID_TAG_SOFTWARE_VERSION, + fu_firmware_get_version(firmware)); + } + if (priv->version_scheme != FU_COSWID_VERSION_SCHEME_UNKNOWN) { + fu_coswid_firmware_write_tag_uint16(root, + FU_COSWID_TAG_VERSION_SCHEME, + priv->version_scheme); + } + fu_coswid_firmware_write_tag_item(root, FU_COSWID_TAG_SOFTWARE_META, item_meta); + fu_coswid_firmware_write_tag_string(item_meta, FU_COSWID_TAG_GENERATOR, PACKAGE_NAME); + if (priv->summary != NULL) { + fu_coswid_firmware_write_tag_string(item_meta, + FU_COSWID_TAG_SUMMARY, + priv->summary); + } + if (priv->colloquial_version != NULL) { + fu_coswid_firmware_write_tag_string(item_meta, + FU_COSWID_TAG_COLLOQUIAL_VERSION, + priv->colloquial_version); + } + + /* add entities */ + if (priv->entities->len > 0) { + g_autoptr(cbor_item_t) item_entities = cbor_new_indefinite_array(); + for (guint i = 0; i < priv->entities->len; i++) { + FuCoswidFirmwareEntity *entity = g_ptr_array_index(priv->entities, i); + g_autoptr(cbor_item_t) item_entity = cbor_new_indefinite_map(); + g_autoptr(cbor_item_t) item_roles = cbor_new_indefinite_array(); + if (entity->name != NULL) { + fu_coswid_firmware_write_tag_string(item_entity, + FU_COSWID_TAG_ENTITY_NAME, + entity->name); + } + if (entity->regid != NULL) { + fu_coswid_firmware_write_tag_string(item_entity, + FU_COSWID_TAG_REG_ID, + entity->regid); + } + for (guint j = 0; entity->roles[j] != FU_COSWID_ENTITY_ROLE_UNKNOWN; j++) { + g_autoptr(cbor_item_t) item_role = + cbor_build_uint8(entity->roles[j]); + cbor_array_push(item_roles, item_role); + } + fu_coswid_firmware_write_tag_item(item_entity, + FU_COSWID_TAG_ROLE, + item_roles); + cbor_array_push(item_entities, item_entity); + } + fu_coswid_firmware_write_tag_item(root, FU_COSWID_TAG_ENTITY, item_entities); + } + + /* add links */ + if (priv->links->len > 0) { + g_autoptr(cbor_item_t) item_links = cbor_new_indefinite_array(); + for (guint i = 0; i < priv->links->len; i++) { + FuCoswidFirmwareLink *link = g_ptr_array_index(priv->links, i); + g_autoptr(cbor_item_t) item_link = cbor_new_indefinite_map(); + if (link->href != NULL) { + fu_coswid_firmware_write_tag_string(item_link, + FU_COSWID_TAG_HREF, + link->href); + } + fu_coswid_firmware_write_tag_int8(item_link, FU_COSWID_TAG_REL, link->rel); + cbor_array_push(item_links, item_link); + } + fu_coswid_firmware_write_tag_item(root, FU_COSWID_TAG_LINK, item_links); + } + + /* serialize */ + buflen = cbor_serialize_alloc(root, &buf, &bufsz); + if (buflen > bufsz) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CBOR allocation failure"); + return NULL; + } + return g_bytes_new(buf, buflen); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "not compiled with CBOR support"); + return NULL; +#endif +} + +static gboolean +fu_coswid_firmware_build_entity(FuCoswidFirmware *self, XbNode *n, GError **error) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + guint entity_role_cnt = 0; + FuCoswidEntityRole role; + g_autoptr(GPtrArray) roles = NULL; + g_autoptr(FuCoswidFirmwareEntity) entity = g_new0(FuCoswidFirmwareEntity, 1); + + /* these are required */ + tmp = xb_node_query_text(n, "name", error); + if (tmp == NULL) + return FALSE; + entity->name = g_strdup(tmp); + tmp = xb_node_query_text(n, "regid", error); + if (tmp == NULL) + return FALSE; + entity->regid = g_strdup(tmp); + + /* optional */ + roles = xb_node_query(n, "role", 0, NULL); + if (roles != NULL) { + for (guint i = 0; i < roles->len; i++) { + XbNode *c = g_ptr_array_index(roles, i); + tmp = xb_node_get_text(c); + role = fu_coswid_entity_role_from_string(tmp); + if (role == FU_COSWID_ENTITY_ROLE_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to parse entity role %s", + tmp); + return FALSE; + } + if (entity_role_cnt >= G_N_ELEMENTS(entity->roles)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "too many roles"); + return FALSE; + } + entity->roles[entity_role_cnt++] = role; + } + } + + /* success */ + g_ptr_array_add(priv->entities, g_steal_pointer(&entity)); + return TRUE; +} + +static gboolean +fu_coswid_firmware_build_link(FuCoswidFirmware *self, XbNode *n, GError **error) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + g_autoptr(FuCoswidFirmwareLink) link = g_new0(FuCoswidFirmwareLink, 1); + + /* required */ + tmp = xb_node_query_text(n, "href", error); + if (tmp == NULL) + return FALSE; + link->href = g_strdup(tmp); + + /* optional */ + tmp = xb_node_query_text(n, "rel", NULL); + if (tmp != NULL) { + link->rel = fu_coswid_link_rel_from_string(tmp); + if (link->rel == FU_COSWID_LINK_REL_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to parse link rel %s", + tmp); + return FALSE; + } + } + + /* success */ + g_ptr_array_add(priv->links, g_steal_pointer(&link)); + return TRUE; +} + +static gboolean +fu_coswid_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + g_autoptr(GPtrArray) links = NULL; + g_autoptr(GPtrArray) entities = NULL; + + /* simple properties */ + tmp = xb_node_query_text(n, "product", NULL); + if (tmp != NULL) + priv->product = g_strdup(tmp); + tmp = xb_node_query_text(n, "summary", NULL); + if (tmp != NULL) + priv->summary = g_strdup(tmp); + tmp = xb_node_query_text(n, "colloquial_version", NULL); + if (tmp != NULL) + priv->colloquial_version = g_strdup(tmp); + + tmp = xb_node_query_text(n, "version_scheme", NULL); + if (tmp != NULL) { + priv->version_scheme = fu_coswid_version_scheme_from_string(tmp); + if (priv->version_scheme == FU_COSWID_VERSION_SCHEME_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to parse version_scheme %s", + tmp); + return FALSE; + } + } + + /* multiple links allowed */ + links = xb_node_query(n, "link", 0, NULL); + if (links != NULL) { + for (guint i = 0; i < links->len; i++) { + XbNode *c = g_ptr_array_index(links, i); + if (!fu_coswid_firmware_build_link(self, c, error)) + return FALSE; + } + } + + /* multiple entities allowed */ + entities = xb_node_query(n, "entity", 0, NULL); + if (entities != NULL) { + for (guint i = 0; i < entities->len; i++) { + XbNode *c = g_ptr_array_index(entities, i); + if (!fu_coswid_firmware_build_entity(self, c, error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_coswid_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuCoswidFirmware *self = FU_COSWID_FIRMWARE(firmware); + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + if (priv->version_scheme != FU_COSWID_VERSION_SCHEME_UNKNOWN) { + fu_xmlb_builder_insert_kv(bn, + "version_scheme", + fu_coswid_version_scheme_to_string(priv->version_scheme)); + } + fu_xmlb_builder_insert_kv(bn, "product", priv->product); + fu_xmlb_builder_insert_kv(bn, "summary", priv->summary); + fu_xmlb_builder_insert_kv(bn, "colloquial_version", priv->colloquial_version); + for (guint i = 0; i < priv->links->len; i++) { + FuCoswidFirmwareLink *link = g_ptr_array_index(priv->links, i); + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "link", NULL); + fu_xmlb_builder_insert_kv(bc, "href", link->href); + if (link->rel != FU_COSWID_LINK_REL_UNKNOWN) { + fu_xmlb_builder_insert_kv(bc, + "rel", + fu_coswid_link_rel_to_string(link->rel)); + } + } + for (guint i = 0; i < priv->entities->len; i++) { + FuCoswidFirmwareEntity *entity = g_ptr_array_index(priv->entities, i); + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "entity", NULL); + fu_xmlb_builder_insert_kv(bc, "name", entity->name); + fu_xmlb_builder_insert_kv(bc, "regid", entity->regid); + for (guint j = 0; entity->roles[j] != FU_COSWID_ENTITY_ROLE_UNKNOWN; j++) { + fu_xmlb_builder_insert_kv( + bc, + "role", + fu_coswid_entity_role_to_string(entity->roles[j])); + } + } +} + +static void +fu_coswid_firmware_init(FuCoswidFirmware *self) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + priv->version_scheme = FU_COSWID_VERSION_SCHEME_SEMVER; + priv->links = g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_link_free); + priv->entities = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_entity_free); +} + +static void +fu_coswid_firmware_finalize(GObject *object) +{ + FuCoswidFirmware *self = FU_COSWID_FIRMWARE(object); + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + + g_free(priv->product); + g_free(priv->summary); + g_free(priv->colloquial_version); + g_ptr_array_unref(priv->links); + g_ptr_array_unref(priv->entities); + + G_OBJECT_CLASS(fu_coswid_firmware_parent_class)->finalize(object); +} + +static void +fu_coswid_firmware_class_init(FuCoswidFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_coswid_firmware_finalize; + klass_firmware->parse = fu_coswid_firmware_parse; + klass_firmware->write = fu_coswid_firmware_write; + klass_firmware->build = fu_coswid_firmware_build; + klass_firmware->export = fu_coswid_firmware_export; +} + +/** + * fu_coswid_firmware_new: + * + * Creates a new #FuFirmware of sub type coSWID + * + * Since: 1.8.0 + **/ +FuFirmware * +fu_coswid_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_COSWID_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-coswid-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-coswid-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..fc64f0b0bcaf9b54915cc294cb6108aaf44fd085 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-coswid-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_COSWID_FIRMWARE (fu_coswid_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuCoswidFirmware, fu_coswid_firmware, FU, COSWID_FIRMWARE, FuFirmware) + +struct _FuCoswidFirmwareClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_coswid_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-crc.c b/fwupd-1.8.6/libfwupdplugin/fu-crc.c new file mode 100644 index 0000000000000000000000000000000000000000..9502b1d18012eaba4f50652fd39659d2a74c9961 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-crc.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include "fu-crc.h" + +/** + * fu_crc8_full: + * @buf: memory buffer + * @bufsz: size of @buf + * @crc_init: initial CRC value, typically 0x00 + * @polynomial: CRC polynomial, e.g. 0x07 for CCITT + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.8.2 + **/ +guint8 +fu_crc8_full(const guint8 *buf, gsize bufsz, guint8 crc_init, guint8 polynomial) +{ + guint32 crc = crc_init; + for (gsize j = bufsz; j > 0; j--) { + crc ^= (*(buf++) << 8); + for (guint32 i = 8; i; i--) { + if (crc & 0x8000) + crc ^= ((polynomial | 0x100) << 7); + crc <<= 1; + } + } + return ~((guint8)(crc >> 8)); +} + +/** + * fu_crc8: + * @buf: memory buffer + * @bufsz: size of @buf + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.8.2 + **/ +guint8 +fu_crc8(const guint8 *buf, gsize bufsz) +{ + return fu_crc8_full(buf, bufsz, 0x00, 0x07); +} + +/** + * fu_crc16_full: + * @buf: memory buffer + * @bufsz: size of @buf + * @crc: initial CRC value, typically 0xFFFF + * @polynomial: CRC polynomial, typically 0xA001 for IBM or 0x1021 for CCITT + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.8.2 + **/ +guint16 +fu_crc16_full(const guint8 *buf, gsize bufsz, guint16 crc, guint16 polynomial) +{ + for (gsize len = bufsz; len > 0; len--) { + crc = (guint16)(crc ^ (*buf++)); + for (guint8 i = 0; i < 8; i++) { + if (crc & 0x1) { + crc = (crc >> 1) ^ polynomial; + } else { + crc >>= 1; + } + } + } + return ~crc; +} + +/** + * fu_crc16: + * @buf: memory buffer + * @bufsz: size of @buf + * + * Returns the CRC-16-IBM cyclic redundancy value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.8.2 + **/ +guint16 +fu_crc16(const guint8 *buf, gsize bufsz) +{ + return fu_crc16_full(buf, bufsz, 0xFFFF, 0xA001); +} + +/** + * fu_crc32_full: + * @buf: memory buffer + * @bufsz: size of @buf + * @crc: initial CRC value, typically 0xFFFFFFFF + * @polynomial: CRC polynomial, typically 0xEDB88320 + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.8.2 + **/ +guint32 +fu_crc32_full(const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial) +{ + for (guint32 idx = 0; idx < bufsz; idx++) { + guint8 data = *buf++; + crc = crc ^ data; + for (guint32 bit = 0; bit < 8; bit++) { + guint32 mask = -(crc & 1); + crc = (crc >> 1) ^ (polynomial & mask); + } + } + return ~crc; +} + +/** + * fu_crc32: + * @buf: memory buffer + * @bufsz: size of @buf + * + * Returns the cyclic redundancy check value for the given memory buffer. + * + * Returns: CRC value + * + * Since: 1.8.2 + **/ +guint32 +fu_crc32(const guint8 *buf, gsize bufsz) +{ + return fu_crc32_full(buf, bufsz, 0xFFFFFFFF, 0xEDB88320); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-crc.h b/fwupd-1.8.6/libfwupdplugin/fu-crc.h new file mode 100644 index 0000000000000000000000000000000000000000..49cdb6e4044a71c91c7f4fd8adc627bf85de73d5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-crc.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-common.h" + +guint8 +fu_crc8(const guint8 *buf, gsize bufsz); +guint8 +fu_crc8_full(const guint8 *buf, gsize bufsz, guint8 crc_init, guint8 polynomial); +guint16 +fu_crc16(const guint8 *buf, gsize bufsz); +guint16 +fu_crc16_full(const guint8 *buf, gsize bufsz, guint16 crc, guint16 polynomial); +guint32 +fu_crc32(const guint8 *buf, gsize bufsz); +guint32 +fu_crc32_full(const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-deprecated.h b/fwupd-1.8.6/libfwupdplugin/fu-deprecated.h new file mode 100644 index 0000000000000000000000000000000000000000..a3671517a3f4125927ea8d6644c498e100b5067b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-deprecated.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +G_BEGIN_DECLS + +/* indeed, nothing */ + +G_END_DECLS diff --git a/fwupd-1.8.6/libfwupdplugin/fu-device-locker.c b/fwupd-1.8.6/libfwupdplugin/fu-device-locker.c new file mode 100644 index 0000000000000000000000000000000000000000..d53ed34ebca42a5f5eb8b28231c0781e9e86181d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-device-locker.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuDeviceLocker" + +#include "config.h" + +#include +#ifdef HAVE_GUSB +#include +#endif + +#include "fu-device-locker.h" +#include "fu-usb-device.h" + +/** + * FuDeviceLocker: + * + * Easily close a shared resource (such as a device) when an object goes out of + * scope. + * + * See also: [class@FuDevice] + */ + +struct _FuDeviceLocker { + GObject parent_instance; + GObject *device; + gboolean device_open; + FuDeviceLockerFunc open_func; + FuDeviceLockerFunc close_func; +}; + +G_DEFINE_TYPE(FuDeviceLocker, fu_device_locker, G_TYPE_OBJECT) + +static void +fu_device_locker_finalize(GObject *obj) +{ + FuDeviceLocker *self = FU_DEVICE_LOCKER(obj); + + /* close device */ + if (self->device_open) { + g_autoptr(GError) error = NULL; + if (!self->close_func(self->device, &error)) + g_warning("failed to close device: %s", error->message); + } + if (self->device != NULL) + g_object_unref(self->device); + G_OBJECT_CLASS(fu_device_locker_parent_class)->finalize(obj); +} + +static void +fu_device_locker_class_init(FuDeviceLockerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_device_locker_finalize; +} + +static void +fu_device_locker_init(FuDeviceLocker *self) +{ +} + +/** + * fu_device_locker_close: + * @self: a #FuDeviceLocker + * @error: (nullable): optional return location for an error + * + * Closes the locker before it gets cleaned up. + * + * This function can be used to manually close a device managed by a locker, + * and allows the caller to properly handle the error. + * + * Returns: %TRUE for success + * + * Since: 1.4.0 + **/ +gboolean +fu_device_locker_close(FuDeviceLocker *self, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_return_val_if_fail(FU_IS_DEVICE_LOCKER(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + if (!self->device_open) + return TRUE; + if (!self->close_func(self->device, &error_local)) { +#ifdef HAVE_GUSB + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE)) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } +#else + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; +#endif + } + self->device_open = FALSE; + return TRUE; +} + +/** + * fu_device_locker_new: + * @device: a #GObject + * @error: (nullable): optional return location for an error + * + * Opens the device for use. When the #FuDeviceLocker is deallocated the device + * will be closed and any error will just be directed to the console. + * This object is typically called using g_autoptr() but the device can also be + * 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 #FuDevice then + * this function will not work. + * + * For custom objects please use fu_device_locker_new_full(). + * + * NOTE: If the @open_func failed then the @close_func will not be called. + * + * Think of this object as the device ownership. + * + * Returns: a device locker, or %NULL if the @open_func failed. + * + * Since: 1.0.0 + **/ +FuDeviceLocker * +fu_device_locker_new(gpointer device, GError **error) +{ + g_return_val_if_fail(device != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + +#ifdef HAVE_GUSB + /* GUsbDevice */ + if (G_USB_IS_DEVICE(device)) { + return fu_device_locker_new_full(device, + (FuDeviceLockerFunc)g_usb_device_open, + (FuDeviceLockerFunc)g_usb_device_close, + error); + } +#endif + + /* FuDevice */ + if (FU_IS_DEVICE(device)) { + return fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_open, + (FuDeviceLockerFunc)fu_device_close, + error); + } + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "device object type not supported"); + return NULL; +} + +/** + * fu_device_locker_new_full: + * @device: a #GObject + * @open_func: (scope async): a function to open the device + * @close_func: (scope async): a function to close the device + * @error: (nullable): optional return location for an error + * + * Opens the device for use. When the #FuDeviceLocker is deallocated the device + * will be closed and any error will just be directed to the console. + * This object is typically called using g_autoptr() but the device can also be + * manually closed using g_clear_object(). + * + * NOTE: If the @open_func failed then the @close_func will not be called. + * + * Think of this object as the device ownership. + * + * Returns: a device locker, or %NULL if the @open_func failed. + * + * Since: 1.0.0 + **/ +FuDeviceLocker * +fu_device_locker_new_full(gpointer device, + FuDeviceLockerFunc open_func, + FuDeviceLockerFunc close_func, + GError **error) +{ + g_autoptr(FuDeviceLocker) self = NULL; + + g_return_val_if_fail(device != NULL, NULL); + g_return_val_if_fail(open_func != NULL, NULL); + g_return_val_if_fail(close_func != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* create object */ + self = g_object_new(FU_TYPE_DEVICE_LOCKER, NULL); + self->device = g_object_ref(device); + self->open_func = open_func; + self->close_func = close_func; + + /* open device */ + if (!self->open_func(device, error)) { + g_autoptr(GError) error_local = NULL; + if (!self->close_func(device, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("ignoring close error on aborted open: %s", + error_local->message); + } + } + return NULL; + } + + /* success */ + self->device_open = TRUE; + return g_steal_pointer(&self); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-device-locker.h b/fwupd-1.8.6/libfwupdplugin/fu-device-locker.h new file mode 100644 index 0000000000000000000000000000000000000000..fa92534a48aa0c4409424e1baf3a64efe39ed4b1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-device-locker.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_DEVICE_LOCKER (fu_device_locker_get_type()) + +G_DECLARE_FINAL_TYPE(FuDeviceLocker, fu_device_locker, FU, DEVICE_LOCKER, GObject) + +/** + * FuDeviceLockerFunc: + * + * Callback to use when opening and closing using [ctor@DeviceLocker.new_full]. + **/ +typedef gboolean (*FuDeviceLockerFunc)(GObject *device, GError **error); + +FuDeviceLocker * +fu_device_locker_new(gpointer device, GError **error) G_GNUC_WARN_UNUSED_RESULT; +FuDeviceLocker * +fu_device_locker_new_full(gpointer device, + FuDeviceLockerFunc open_func, + FuDeviceLockerFunc close_func, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_locker_close(FuDeviceLocker *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-device-metadata.h b/fwupd-1.8.6/libfwupdplugin/fu-device-metadata.h new file mode 100644 index 0000000000000000000000000000000000000000..edce705238c358a79d9eb08c479894baf1826dc6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-device-metadata.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FU_DEVICE_METADATA_TBT_IS_SAFE_MODE: + * + * If the Thunderbolt hardware is stuck in safe mode. + * Consumed by the thunderbolt plugin. + */ +#define FU_DEVICE_METADATA_TBT_IS_SAFE_MODE "Thunderbolt::IsSafeMode" + +/** + * 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" diff --git a/fwupd-1.8.6/libfwupdplugin/fu-device-private.h b/fwupd-1.8.6/libfwupdplugin/fu-device-private.h new file mode 100644 index 0000000000000000000000000000000000000000..9c6d56ab2145b55c08aa403ffb5842d79f094d3b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-device-private.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#define fu_device_set_plugin(d, v) fwupd_device_set_plugin(FWUPD_DEVICE(d), v) + +const gchar * +fu_device_internal_flag_to_string(FuDeviceInternalFlags flag); +FuDeviceInternalFlags +fu_device_internal_flag_from_string(const gchar *flag); + +GPtrArray * +fu_device_get_parent_guids(FuDevice *self); +gboolean +fu_device_has_parent_guid(FuDevice *self, const gchar *guid); +GPtrArray * +fu_device_get_parent_physical_ids(FuDevice *self); +gboolean +fu_device_has_parent_physical_id(FuDevice *self, const gchar *physical_id); +void +fu_device_set_parent(FuDevice *self, FuDevice *parent); +gint +fu_device_get_order(FuDevice *self); +void +fu_device_set_order(FuDevice *self, gint order); +const gchar * +fu_device_get_update_request_id(FuDevice *self); +void +fu_device_set_update_request_id(FuDevice *self, const gchar *update_request_id); +void +fu_device_set_alternate(FuDevice *self, FuDevice *alternate); +gboolean +fu_device_ensure_id(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_device_incorporate_from_component(FuDevice *device, XbNode *component); +void +fu_device_convert_instance_ids(FuDevice *self); +gchar * +fu_device_get_guids_as_str(FuDevice *self); +GPtrArray * +fu_device_get_possible_plugins(FuDevice *self); +void +fu_device_add_possible_plugin(FuDevice *self, const gchar *plugin); +guint +fu_device_get_request_cnt(FuDevice *self, FwupdRequestKind request_kind); +guint64 +fu_device_get_private_flags(FuDevice *self); +void +fu_device_set_private_flags(FuDevice *self, guint64 flag); +void +fu_device_set_progress(FuDevice *self, FuProgress *progress); +FuDeviceInternalFlags +fu_device_get_internal_flags(FuDevice *self); +void +fu_device_set_internal_flags(FuDevice *self, FuDeviceInternalFlags flags); +gboolean +fu_device_set_quirk_kv(FuDevice *self, const gchar *key, const gchar *value, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-device.c b/fwupd-1.8.6/libfwupdplugin/fu-device.c new file mode 100644 index 0000000000000000000000000000000000000000..1c62d588c213c8591d0c15c8c838321892fa6be5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-device.c @@ -0,0 +1,5800 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuDevice" + +#include "config.h" + +#include +#include + +#include "fwupd-common.h" +#include "fwupd-device-private.h" + +#include "fu-common.h" +#include "fu-device-private.h" +#include "fu-mutex.h" +#include "fu-quirks.h" +#include "fu-security-attr.h" +#include "fu-string.h" +#include "fu-version-common.h" + +#define FU_DEVICE_RETRY_OPEN_COUNT 5 +#define FU_DEVICE_RETRY_OPEN_DELAY 500 /* ms */ + +/** + * FuDevice: + * + * A physical or logical device that is exported to the daemon. + * + * See also: [class@FuDeviceLocker], [class@Fwupd.Device] + */ + +static void +fu_device_finalize(GObject *object); +static void +fu_device_inhibit_full(FuDevice *self, + FwupdDeviceProblem problem, + const gchar *inhibit_id, + const gchar *reason); + +typedef struct { + gchar *alternate_id; + gchar *equivalent_id; + gchar *physical_id; + gchar *logical_id; + gchar *backend_id; + gchar *update_request_id; + gchar *proxy_guid; + FuDevice *alternate; + FuDevice *proxy; /* noref */ + FuContext *ctx; + GHashTable *inhibits; /* (nullable) */ + GHashTable *metadata; /* (nullable) */ + GRWLock metadata_mutex; + GPtrArray *parent_guids; + GRWLock parent_guids_mutex; + GPtrArray *parent_physical_ids; /* (nullable) */ + guint remove_delay; /* ms */ + guint acquiesce_delay; /* ms */ + guint request_cnts[FWUPD_REQUEST_KIND_LAST]; + gint order; + guint priority; + guint poll_id; + gint poll_locker_cnt; + gboolean done_probe; + gboolean done_setup; + gboolean device_id_valid; + guint64 size_min; + guint64 size_max; + gint open_refcount; /* atomic */ + GType specialized_gtype; + GType firmware_gtype; + GPtrArray *possible_plugins; + GPtrArray *retry_recs; /* of FuDeviceRetryRecovery */ + guint retry_delay; + FuDeviceInternalFlags internal_flags; + guint64 private_flags; + GPtrArray *private_flag_items; /* (nullable) */ + gchar *custom_flags; + gulong notify_flags_handler_id; + GHashTable *instance_hash; + GPtrArray *backend_tags; /* of utf-8 */ +} FuDevicePrivate; + +typedef struct { + GQuark domain; + gint code; + FuDeviceRetryFunc recovery_func; +} FuDeviceRetryRecovery; + +typedef struct { + FwupdDeviceProblem problem; + gchar *inhibit_id; + gchar *reason; +} FuDeviceInhibit; + +enum { + PROP_0, + PROP_PHYSICAL_ID, + PROP_LOGICAL_ID, + PROP_BACKEND_ID, + PROP_CONTEXT, + PROP_PROXY, + PROP_PARENT, + PROP_BACKEND_TAGS, + PROP_LAST +}; + +enum { SIGNAL_CHILD_ADDED, SIGNAL_CHILD_REMOVED, SIGNAL_REQUEST, SIGNAL_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FuDevice, fu_device, FWUPD_TYPE_DEVICE) +#define GET_PRIVATE(o) (fu_device_get_instance_private(o)) + +static void +fu_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuDevice *self = FU_DEVICE(object); + FuDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_PHYSICAL_ID: + g_value_set_string(value, priv->physical_id); + break; + case PROP_LOGICAL_ID: + g_value_set_string(value, priv->logical_id); + break; + case PROP_BACKEND_ID: + g_value_set_string(value, priv->backend_id); + break; + case PROP_CONTEXT: + g_value_set_object(value, priv->ctx); + break; + case PROP_PROXY: + g_value_set_object(value, priv->proxy); + break; + case PROP_PARENT: + g_value_set_object(value, fu_device_get_parent(self)); + break; + case PROP_BACKEND_TAGS: + g_value_set_boxed(value, priv->backend_tags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuDevice *self = FU_DEVICE(object); + switch (prop_id) { + 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_BACKEND_ID: + fu_device_set_backend_id(self, g_value_get_string(value)); + break; + case PROP_CONTEXT: + fu_device_set_context(self, g_value_get_object(value)); + break; + case PROP_PROXY: + fu_device_set_proxy(self, g_value_get_object(value)); + break; + case PROP_PARENT: + fu_device_set_parent(self, g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/** + * fu_device_internal_flag_to_string: + * @flag: an internal device flag, e.g. %FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON + * + * Converts an internal device flag to a string. + * + * Returns: identifier string + * + * Since: 1.5.5 + **/ +const gchar * +fu_device_internal_flag_to_string(FuDeviceInternalFlags flag) +{ + if (flag == FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON) + return "md-set-icon"; + if (flag == FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME) + return "md-set-name"; + if (flag == FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY) + return "md-set-name-category"; + if (flag == FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT) + return "md-set-verfmt"; + if (flag == FU_DEVICE_INTERNAL_FLAG_ONLY_SUPPORTED) + return "only-supported"; + if (flag == FU_DEVICE_INTERNAL_FLAG_NO_AUTO_INSTANCE_IDS) + return "no-auto-instance-ids"; + if (flag == FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER) + return "ensure-semver"; + if (flag == FU_DEVICE_INTERNAL_FLAG_RETRY_OPEN) + return "retry-open"; + if (flag == FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID) + return "replug-match-guid"; + if (flag == FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION) + return "inherit-activation"; + if (flag == FU_DEVICE_INTERNAL_FLAG_IS_OPEN) + return "is-open"; + if (flag == FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER) + return "no-serial-number"; + if (flag == FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN) + return "auto-parent-children"; + if (flag == FU_DEVICE_INTERNAL_FLAG_ATTACH_EXTRA_RESET) + return "attach-extra-reset"; + if (flag == FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN) + return "inhibit-children"; + if (flag == FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN) + return "no-auto-remove-children"; + if (flag == FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN) + return "use-parent-for-open"; + if (flag == FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY) + return "use-parent-for-battery"; + if (flag == FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK) + return "use-proxy-fallback"; + if (flag == FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE) + return "no-auto-remove"; + if (flag == FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR) + return "md-set-vendor"; + if (flag == FU_DEVICE_INTERNAL_FLAG_NO_LID_CLOSED) + return "no-lid-closed"; + if (flag == FU_DEVICE_INTERNAL_FLAG_NO_PROBE) + return "no-probe"; + if (flag == FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED) + return "md-set-signed"; + if (flag == FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING) + return "auto-pause-polling"; + if (flag == FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG) + return "only-wait-for-replug"; + return NULL; +} + +/** + * fu_device_internal_flag_from_string: + * @flag: a string, e.g. `md-set-icon` + * + * Converts a string to an internal device flag. + * + * Returns: enumerated value + * + * Since: 1.5.5 + **/ +FuDeviceInternalFlags +fu_device_internal_flag_from_string(const gchar *flag) +{ + if (g_strcmp0(flag, "md-set-icon") == 0) + return FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON; + if (g_strcmp0(flag, "md-set-name") == 0) + return FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME; + if (g_strcmp0(flag, "md-set-name-category") == 0) + return FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY; + if (g_strcmp0(flag, "md-set-verfmt") == 0) + return FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT; + if (g_strcmp0(flag, "only-supported") == 0) + return FU_DEVICE_INTERNAL_FLAG_ONLY_SUPPORTED; + if (g_strcmp0(flag, "no-auto-instance-ids") == 0) + return FU_DEVICE_INTERNAL_FLAG_NO_AUTO_INSTANCE_IDS; + if (g_strcmp0(flag, "ensure-semver") == 0) + return FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER; + if (g_strcmp0(flag, "retry-open") == 0) + return FU_DEVICE_INTERNAL_FLAG_RETRY_OPEN; + if (g_strcmp0(flag, "replug-match-guid") == 0) + return FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID; + if (g_strcmp0(flag, "inherit-activation") == 0) + return FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION; + if (g_strcmp0(flag, "is-open") == 0) + return FU_DEVICE_INTERNAL_FLAG_IS_OPEN; + if (g_strcmp0(flag, "no-serial-number") == 0) + return FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER; + if (g_strcmp0(flag, "auto-parent-children") == 0) + return FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN; + if (g_strcmp0(flag, "attach-extra-reset") == 0) + return FU_DEVICE_INTERNAL_FLAG_ATTACH_EXTRA_RESET; + if (g_strcmp0(flag, "inhibit-children") == 0) + return FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN; + if (g_strcmp0(flag, "no-auto-remove-children") == 0) + return FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN; + if (g_strcmp0(flag, "use-parent-for-open") == 0) + return FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN; + if (g_strcmp0(flag, "use-parent-for-battery") == 0) + return FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY; + if (g_strcmp0(flag, "use-proxy-fallback") == 0) + return FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK; + if (g_strcmp0(flag, "no-auto-remove") == 0) + return FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE; + if (g_strcmp0(flag, "md-set-vendor") == 0) + return FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR; + if (g_strcmp0(flag, "no-lid-closed") == 0) + return FU_DEVICE_INTERNAL_FLAG_NO_LID_CLOSED; + if (g_strcmp0(flag, "no-probe") == 0) + return FU_DEVICE_INTERNAL_FLAG_NO_PROBE; + if (g_strcmp0(flag, "md-set-signed") == 0) + return FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED; + if (g_strcmp0(flag, "auto-pause-polling") == 0) + return FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING; + if (g_strcmp0(flag, "only-wait-for-replug") == 0) + return FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG; + return FU_DEVICE_INTERNAL_FLAG_UNKNOWN; +} + +/** + * fu_device_add_internal_flag: + * @self: a #FuDevice + * @flag: an internal device flag, e.g. %FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON + * + * Adds a private flag that stays internal to the engine and is not leaked to the client. + * + * Since: 1.5.5 + **/ +void +fu_device_add_internal_flag(FuDevice *self, FuDeviceInternalFlags flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->internal_flags |= flag; +} + +/** + * fu_device_remove_internal_flag: + * @self: a #FuDevice + * @flag: an internal device flag, e.g. %FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON + * + * Removes a private flag that stays internal to the engine and is not leaked to the client. + * + * Since: 1.5.5 + **/ +void +fu_device_remove_internal_flag(FuDevice *self, FuDeviceInternalFlags flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->internal_flags &= ~flag; +} + +/** + * fu_device_has_internal_flag: + * @self: a #FuDevice + * @flag: an internal device flag, e.g. %FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON + * + * Tests for a private flag that stays internal to the engine and is not leaked to the client. + * + * Since: 1.5.5 + **/ +gboolean +fu_device_has_internal_flag(FuDevice *self, FuDeviceInternalFlags flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + return (priv->internal_flags & flag) > 0; +} +/** + * fu_device_get_internal_flags: + * @self: a #FuDevice + * + * Gets all the internal flags. + * + * Returns: flags, e.g. %FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON + * + * Since: 1.7.1 + **/ +FuDeviceInternalFlags +fu_device_get_internal_flags(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_UNKNOWN); + return priv->internal_flags; +} + +/** + * fu_device_set_internal_flags: + * @self: a #FuDevice + * @flags: internal device flags, e.g. %FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON + * + * Sets the internal flags. + * + * Since: 1.7.1 + **/ +void +fu_device_set_internal_flags(FuDevice *self, FuDeviceInternalFlags flags) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->internal_flags = flags; +} + +/** + * fu_device_add_private_flag: + * @self: a #FuDevice + * @flag: a device flag + * + * Adds a private flag that can be used by the plugin for any purpose. + * + * Since: 1.6.2 + **/ +void +fu_device_add_private_flag(FuDevice *self, guint64 flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->private_flags |= flag; +} + +/** + * fu_device_remove_private_flag: + * @self: a #FuDevice + * @flag: a device flag + * + * Removes a private flag that can be used by the plugin for any purpose. + * + * Since: 1.6.2 + **/ +void +fu_device_remove_private_flag(FuDevice *self, guint64 flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->private_flags &= ~flag; +} + +/** + * fu_device_has_private_flag: + * @self: a #FuDevice + * @flag: a device flag + * + * Tests for a private flag that can be used by the plugin for any purpose. + * + * Since: 1.6.2 + **/ +gboolean +fu_device_has_private_flag(FuDevice *self, guint64 flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + return (priv->private_flags & flag) > 0; +} + +/** + * fu_device_get_private_flags: + * @self: a #FuDevice + * + * Returns all the private flags that can be used by the plugin for any purpose. + * + * Returns: flags + * + * Since: 1.6.2 + **/ +guint64 +fu_device_get_private_flags(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT64); + return priv->private_flags; +} + +/** + * fu_device_get_request_cnt: + * @self: a #FuDevice + * @request_kind: the type of request + * + * Returns the number of requests of a specific kind. This function is only + * useful to the daemon, which uses it to synthesize artificial events for + * plugins not yet ported to [class@Fwupd.Request]. + * + * Returns: integer, usually 0 + * + * Since: 1.6.2 + **/ +guint +fu_device_get_request_cnt(FuDevice *self, FwupdRequestKind request_kind) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT); + g_return_val_if_fail(request_kind < FWUPD_REQUEST_KIND_LAST, G_MAXUINT); + return priv->request_cnts[request_kind]; +} + +/** + * fu_device_set_private_flags: + * @self: a #FuDevice + * @flag: flags + * + * Sets private flags that can be used by the plugin for any purpose. + * + * Since: 1.6.2 + **/ +void +fu_device_set_private_flags(FuDevice *self, guint64 flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->private_flags = flag; +} + +/** + * fu_device_get_possible_plugins: + * @self: a #FuDevice + * + * Gets the list of possible plugin names, typically added from quirk files. + * + * Returns: (element-type utf8) (transfer container): plugin names + * + * Since: 1.3.3 + **/ +GPtrArray * +fu_device_get_possible_plugins(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + return g_ptr_array_ref(priv->possible_plugins); +} + +/** + * fu_device_add_possible_plugin: + * @self: a #FuDevice + * @plugin: a plugin name, e.g. `dfu` + * + * Adds a plugin name to the list of plugins that *might* be able to handle this + * device. This is tyically called from a quirk handler. + * + * Duplicate plugin names are ignored. + * + * Since: 1.5.1 + **/ +void +fu_device_add_possible_plugin(FuDevice *self, const gchar *plugin) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(plugin != NULL); + + /* add if it does not already exist */ +#if GLIB_CHECK_VERSION(2, 54, 3) + if (g_ptr_array_find_with_equal_func(priv->possible_plugins, plugin, g_str_equal, NULL)) + return; +#endif + g_ptr_array_add(priv->possible_plugins, g_strdup(plugin)); +} + +/** + * fu_device_retry_add_recovery: + * @self: a #FuDevice + * @domain: a #GQuark, or %0 for all domains + * @code: a #GError code + * @func: (scope async) (nullable): a function to recover the device + * + * Sets the optional function to be called when fu_device_retry() fails, which + * is possibly a device reset. + * + * If @func is %NULL then recovery is not possible and an error is returned + * straight away. + * + * Since: 1.4.0 + **/ +void +fu_device_retry_add_recovery(FuDevice *self, GQuark domain, gint code, FuDeviceRetryFunc func) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FuDeviceRetryRecovery *rec; + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(domain != 0); + + rec = g_new(FuDeviceRetryRecovery, 1); + rec->domain = domain; + rec->code = code; + rec->recovery_func = func; + g_ptr_array_add(priv->retry_recs, rec); +} + +/** + * fu_device_retry_set_delay: + * @self: a #FuDevice + * @delay: delay in ms + * + * Sets the recovery delay between failed retries. + * + * Since: 1.4.0 + **/ +void +fu_device_retry_set_delay(FuDevice *self, guint delay) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->retry_delay = delay; +} + +/** + * fu_device_retry_full: + * @self: a #FuDevice + * @func: (scope async): a function to execute + * @count: the number of tries to try the function + * @delay: the delay between each try in ms + * @user_data: (nullable): a helper to pass to @func + * @error: (nullable): optional return location for an error + * + * Calls a specific function a number of times, optionally handling the error + * with a reset action. + * + * If fu_device_retry_add_recovery() has not been used then all errors are + * considered non-fatal until the last try. + * + * If the reset function returns %FALSE, then the function returns straight away + * without processing any pending retries. + * + * Since: 1.5.5 + **/ +gboolean +fu_device_retry_full(FuDevice *self, + FuDeviceRetryFunc func, + guint count, + guint delay, + gpointer user_data, + GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(func != NULL, FALSE); + g_return_val_if_fail(count >= 1, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + for (guint i = 0;; i++) { + g_autoptr(GError) error_local = NULL; + + /* delay */ + if (i > 0 && delay > 0) + g_usleep(delay * 1000); + + /* run function, if success return success */ + if (func(self, user_data, &error_local)) + break; + + /* sanity check */ + if (error_local == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "exec failed but no error set!"); + return FALSE; + } + + /* too many retries */ + if (i >= count - 1) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed after %u retries: ", + count); + return FALSE; + } + + /* show recoverable error on the console */ + if (priv->retry_recs->len == 0) { + g_debug("failed on try %u of %u: %s", i + 1, count, error_local->message); + continue; + } + + /* find the condition that matches */ + for (guint j = 0; j < priv->retry_recs->len; j++) { + FuDeviceRetryRecovery *rec = g_ptr_array_index(priv->retry_recs, j); + if (g_error_matches(error_local, rec->domain, rec->code)) { + if (rec->recovery_func != NULL) { + if (!rec->recovery_func(self, user_data, error)) + return FALSE; + } else { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "device recovery not possible"); + return FALSE; + } + } + } + } + + /* success */ + return TRUE; +} + +/** + * fu_device_retry: + * @self: a #FuDevice + * @func: (scope async): a function to execute + * @count: the number of tries to try the function + * @user_data: (nullable): a helper to pass to @func + * @error: (nullable): optional return location for an error + * + * Calls a specific function a number of times, optionally handling the error + * with a reset action. + * + * If fu_device_retry_add_recovery() has not been used then all errors are + * considered non-fatal until the last try. + * + * If the reset function returns %FALSE, then the function returns straight away + * without processing any pending retries. + * + * Since: 1.4.0 + **/ +gboolean +fu_device_retry(FuDevice *self, + FuDeviceRetryFunc func, + guint count, + gpointer user_data, + GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + return fu_device_retry_full(self, func, count, priv->retry_delay, user_data, error); +} + +static gboolean +fu_device_poll_locker_open_cb(GObject *device, GError **error) +{ + FuDevice *self = FU_DEVICE(device); + FuDevicePrivate *priv = GET_PRIVATE(self); + g_atomic_int_inc(&priv->poll_locker_cnt); + return TRUE; +} + +static gboolean +fu_device_poll_locker_close_cb(GObject *device, GError **error) +{ + FuDevice *self = FU_DEVICE(device); + FuDevicePrivate *priv = GET_PRIVATE(self); + g_atomic_int_dec_and_test(&priv->poll_locker_cnt); + return TRUE; +} + +/** + * fu_device_poll_locker_new: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Returns a device locker that prevents polling on the device. If there are no open poll lockers + * then the poll callback will be called. + * + * Use %FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING to opt into this functionality. + * + * Returns: (transfer full): a #FuDeviceLocker + * + * Since: 1.8.1 + **/ +FuDeviceLocker * +fu_device_poll_locker_new(FuDevice *self, GError **error) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + return fu_device_locker_new_full(self, + fu_device_poll_locker_open_cb, + fu_device_poll_locker_close_cb, + error); +} + +/** + * fu_device_poll: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * 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; + + /* device is being detached, written, read, or attached */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING) && + priv->poll_locker_cnt > 0) { + g_debug("ignoring poll callback as an action is in progress"); + return G_SOURCE_CONTINUE; + } + + 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: + * @self: a #FuPlugin + * + * Gets the device order, where higher numbers are installed after lower + * numbers. + * + * Returns: the integer value + * + * Since: 1.0.8 + **/ +gint +fu_device_get_order(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), 0); + return priv->order; +} + +/** + * fu_device_set_order: + * @self: a #FuDevice + * @order: an integer value + * + * Sets the device order, where higher numbers are installed after lower + * numbers. + * + * Since: 1.0.8 + **/ +void +fu_device_set_order(FuDevice *self, gint order) +{ + 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: an 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; +} + +/** + * fu_device_get_equivalent_id: + * @self: a #FuDevice + * + * Gets any equivalent ID for a device + * + * Returns: (transfer none): a #gchar or NULL + * + * Since: 0.6.1 + **/ +const gchar * +fu_device_get_equivalent_id(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->equivalent_id; +} + +/** + * fu_device_set_equivalent_id: + * @self: a #FuDevice + * @equivalent_id: (nullable): a string + * + * Sets any equivalent ID for a device + * + * Since: 0.6.1 + **/ +void +fu_device_set_equivalent_id(FuDevice *self, const gchar *equivalent_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->equivalent_id, equivalent_id) == 0) + return; + + g_free(priv->equivalent_id); + priv->equivalent_id = g_strdup(equivalent_id); +} + +/** + * fu_device_get_alternate_id: + * @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 device 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_id: + * @self: a #FuDevice + * @alternate_id: (nullable): Another #FuDevice ID + * + * 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)); + + /* not changed */ + if (g_strcmp0(priv->alternate_id, alternate_id) == 0) + return; + + 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 device or %NULL + * + * Since: 0.7.2 + **/ +FuDevice * +fu_device_get_alternate(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->alternate; +} + +/** + * fu_device_set_alternate: + * @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 *self, FuDevice *alternate) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_set_object(&priv->alternate, alternate); +} + +/** + * fu_device_get_parent: + * @self: a #FuDevice + * + * Gets any parent device. An parent device is logically "above" the current + * device and this may be reflected in client tools. + * + * This information also allows the plugin to optionally verify the parent + * device, for instance checking the parent device firmware version. + * + * The parent object is not refcounted and if destroyed this function will then + * return %NULL. + * + * Returns: (transfer none): a device or %NULL + * + * Since: 1.0.8 + **/ +FuDevice * +fu_device_get_parent(FuDevice *self) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return FU_DEVICE(fwupd_device_get_parent(FWUPD_DEVICE(self))); +} + +/** + * fu_device_get_root: + * @self: a #FuDevice + * + * Gets the root parent device. A parent device is logically "above" the current + * device and this may be reflected in client tools. + * + * If there is no parent device defined, then @self is returned. + * + * Returns: (transfer full): a device + * + * Since: 1.4.0 + **/ +FuDevice * +fu_device_get_root(FuDevice *self) +{ + FuDevice *parent; + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + do { + parent = fu_device_get_parent(self); + if (parent != NULL) + self = parent; + } while (parent != NULL); + return g_object_ref(self); +} + +static void +fu_device_set_composite_id(FuDevice *self, const gchar *composite_id) +{ + GPtrArray *children; + + /* subclassed simple setter */ + fwupd_device_set_composite_id(FWUPD_DEVICE(self), composite_id); + + /* all children */ + children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child_tmp = g_ptr_array_index(children, i); + fu_device_set_composite_id(child_tmp, composite_id); + } +} + +/** + * fu_device_set_parent: + * @self: a #FuDevice + * @parent: (nullable): a device + * + * Sets any parent device. An parent device is logically "above" the current + * device and this may be reflected in client tools. + * + * This information also allows the plugin to optionally verify the parent + * device, for instance checking the parent device firmware version. + * + * Since: 1.0.8 + **/ +void +fu_device_set_parent(FuDevice *self, FuDevice *parent) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + + /* debug */ + if (parent != NULL) { + g_debug("setting parent of %s [%s] to be %s [%s]", + fu_device_get_name(self), + fu_device_get_id(self), + fu_device_get_name(parent), + fu_device_get_id(parent)); + } + + /* set the composite ID on the children and grandchildren */ + if (parent != NULL) + fu_device_set_composite_id(self, fu_device_get_composite_id(parent)); + + /* if the parent has a context, make the child inherit it */ + if (parent != NULL) { + if (fu_device_get_context(self) == NULL && fu_device_get_context(parent) != NULL) + fu_device_set_context(self, fu_device_get_context(parent)); + } + + fwupd_device_set_parent(FWUPD_DEVICE(self), FWUPD_DEVICE(parent)); + g_object_notify(G_OBJECT(self), "parent"); +} + +/** + * fu_device_set_proxy: + * @self: a #FuDevice + * @proxy: a device + * + * Sets any proxy device. A proxy device can be used to perform an action on + * behalf of another device, for instance attach()ing it after a successful + * update. + * + * Since: 1.4.1 + **/ +void +fu_device_set_proxy(FuDevice *self, FuDevice *proxy) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + + /* copy from proxy */ + if (proxy != NULL) { + if (fu_device_get_context(self) == NULL && fu_device_get_context(proxy) != NULL) + fu_device_set_context(self, fu_device_get_context(proxy)); + if (fu_device_get_physical_id(self) == NULL && + fu_device_get_physical_id(proxy) != NULL) + fu_device_set_physical_id(self, fu_device_get_physical_id(proxy)); + } + + if (priv->proxy != NULL) + g_object_remove_weak_pointer(G_OBJECT(priv->proxy), (gpointer *)&priv->proxy); + if (proxy != NULL) + g_object_add_weak_pointer(G_OBJECT(proxy), (gpointer *)&priv->proxy); + priv->proxy = proxy; + g_object_notify(G_OBJECT(self), "proxy"); +} + +/** + * fu_device_get_proxy: + * @self: a #FuDevice + * + * Gets any proxy device. A proxy device can be used to perform an action on + * behalf of another device, for instance attach()ing it after a successful + * update. + * + * The proxy object is not refcounted and if destroyed this function will then + * return %NULL. + * + * Returns: (transfer none): a device or %NULL + * + * Since: 1.4.1 + **/ +FuDevice * +fu_device_get_proxy(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->proxy; +} + +/** + * fu_device_get_proxy_with_fallback: + * @self: a #FuDevice + * + * Gets the proxy device if %FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK is set, falling back to the + * device itself. + * + * Returns: (transfer none): a device + * + * Since: 1.6.2 + **/ +FuDevice * +fu_device_get_proxy_with_fallback(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK) && + priv->proxy != NULL) + return priv->proxy; + return self; +} + +/** + * fu_device_get_children: + * @self: a #FuDevice + * + * Gets any child devices. A child device is logically "below" the current + * device and this may be reflected in client tools. + * + * Returns: (transfer none) (element-type FuDevice): child devices + * + * Since: 1.0.8 + **/ +GPtrArray * +fu_device_get_children(FuDevice *self) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return fwupd_device_get_children(FWUPD_DEVICE(self)); +} + +/** + * fu_device_get_backend_tags: + * @self: a #FuDevice + * + * Gets any backend tags. + * + * Returns: (transfer none) (element-type utf8): string tags + * + * Since: 1.8.5 + **/ +GPtrArray * +fu_device_get_backend_tags(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->backend_tags; +} + +/** + * fu_device_add_backend_tag: + * @self: a #FuDevice + * @backend_tag: a tag, for example `bootloader` or `runtime-reload` + * + * Adds a backend tag, which allows the backend to identify the specific device for a specific + * phase. For instance, there might be a pre-update runtime, a bootloader and a post-update runtime + * and allowing tags to be saved to the backend object allows us to identify each version of + * the same physical device. + * + * Since: 1.8.5 + **/ +void +fu_device_add_backend_tag(FuDevice *self, const gchar *backend_tag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(backend_tag != NULL); + if (fu_device_has_backend_tag(self, backend_tag)) + return; + g_ptr_array_add(priv->backend_tags, g_strdup(backend_tag)); + g_object_notify(G_OBJECT(self), "backend-tags"); +} + +/** + * fu_device_has_backend_tag: + * @self: a #FuDevice + * @backend_tag: a tag, for example `bootloader` or `runtime-reload` + * + * Finds if the backend tag already exists. + * + * Returns: %TRUE if the backend tag exists + * + * Since: 1.8.5 + **/ +gboolean +fu_device_has_backend_tag(FuDevice *self, const gchar *backend_tag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(backend_tag != NULL, FALSE); + for (guint i = 0; i < priv->backend_tags->len; i++) { + const gchar *backend_tag_tmp = g_ptr_array_index(priv->backend_tags, i); + if (g_strcmp0(backend_tag_tmp, backend_tag) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fu_device_add_child: + * @self: a #FuDevice + * @child: Another #FuDevice + * + * Sets any child device. An child device is logically linked to the primary + * device in some way. + * + * Since: 1.0.8 + **/ +void +fu_device_add_child(FuDevice *self, FuDevice *child) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FuDevicePrivate *priv_child = GET_PRIVATE(child); + GPtrArray *children; + g_autoptr(GError) error = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(FU_IS_DEVICE(child)); + + /* add if the child does not already exist */ + fwupd_device_add_child(FWUPD_DEVICE(self), FWUPD_DEVICE(child)); + + /* propagate inhibits to children */ + if (priv->inhibits != NULL && + fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN)) { + g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits); + for (GList *l = values; l != NULL; l = l->next) { + FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data; + fu_device_inhibit_full(child, + inhibit->problem, + inhibit->inhibit_id, + inhibit->reason); + } + } + + /* ensure the parent has the MAX() of the children's removal delay */ + children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child_tmp = g_ptr_array_index(children, i); + guint remove_delay = fu_device_get_remove_delay(child_tmp); + if (remove_delay > priv->remove_delay) { + g_debug("setting remove delay to %ums as child is greater than %ums", + remove_delay, + priv->remove_delay); + priv->remove_delay = remove_delay; + } + } + + /* ensure the parent has the MAX() of the children's acquiesce delay */ + children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child_tmp = g_ptr_array_index(children, i); + guint acquiesce_delay = fu_device_get_acquiesce_delay(child_tmp); + if (acquiesce_delay > priv->acquiesce_delay) { + g_debug("setting acquiesce delay to %ums as child is greater than %ums", + acquiesce_delay, + priv->acquiesce_delay); + priv->acquiesce_delay = acquiesce_delay; + } + } + + /* 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 (priv_child->backend_id == NULL && priv->backend_id != NULL) + fu_device_set_backend_id(child, priv->backend_id); + if (priv_child->ctx == NULL && priv->ctx != NULL) + fu_device_set_context(child, priv->ctx); + if (fu_device_get_vendor(child) == NULL) + fu_device_set_vendor(child, fu_device_get_vendor(self)); + if (priv_child->remove_delay == 0 && priv->remove_delay != 0) + fu_device_set_remove_delay(child, priv->remove_delay); + if (priv_child->acquiesce_delay == 0 && priv->acquiesce_delay != 0) + fu_device_set_acquiesce_delay(child, priv->acquiesce_delay); + if (fu_device_get_vendor_ids(child)->len == 0) { + GPtrArray *vendor_ids = fu_device_get_vendor_ids(self); + for (guint i = 0; i < vendor_ids->len; i++) { + const gchar *vendor_id = g_ptr_array_index(vendor_ids, i); + fu_device_add_vendor_id(child, vendor_id); + } + } + 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 ID is converted */ + if (!fu_device_ensure_id(child, &error)) + g_warning("failed to ensure child: %s", error->message); + + /* ensure the parent is also set on the child */ + fu_device_set_parent(child, self); + + /* signal to the plugin in case this is done after setup */ + g_signal_emit(self, signals[SIGNAL_CHILD_ADDED], 0, child); +} + +/** + * fu_device_remove_child: + * @self: a #FuDevice + * @child: Another #FuDevice + * + * Removes child device. + * + * Since: 1.6.2 + **/ +void +fu_device_remove_child(FuDevice *self, FuDevice *child) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(FU_IS_DEVICE(child)); + + /* proxy */ + fwupd_device_remove_child(FWUPD_DEVICE(self), FWUPD_DEVICE(child)); + + /* signal to the plugin */ + g_signal_emit(self, signals[SIGNAL_CHILD_REMOVED], 0, child); +} + +/** + * fu_device_get_parent_guids: + * @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 @self. + * + * Returns: (transfer none) (element-type utf8): a list of GUIDs + * + * Since: 1.0.8 + **/ +GPtrArray * +fu_device_get_parent_guids(FuDevice *self) +{ + 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: + * @self: a #FuDevice + * @guid: a GUID + * + * Searches the list of parent GUIDs for a string match. + * + * Returns: %TRUE if the parent GUID exists + * + * Since: 1.0.8 + **/ +gboolean +fu_device_has_parent_guid(FuDevice *self, const gchar *guid) +{ + 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(guid != NULL, 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) + return TRUE; + } + return FALSE; +} + +/** + * fu_device_add_parent_guid: + * @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 @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 *self, const gchar *guid) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GRWLockWriterLocker) locker = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(guid != NULL); + + /* make valid */ + 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)); + return; + } + + /* already valid */ + 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)); +} + +/** + * fu_device_get_parent_physical_ids: + * @self: a #FuDevice + * + * Gets any parent device IDs. If a device is added to the daemon that matches + * the physical ID added from fu_device_add_parent_physical_id() then this + * device is marked the parent of @self. + * + * Returns: (transfer none) (element-type utf8) (nullable): a list of IDs + * + * Since: 1.6.2 + **/ +GPtrArray * +fu_device_get_parent_physical_ids(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->parent_physical_ids; +} + +/** + * fu_device_has_parent_physical_id: + * @self: a #FuDevice + * @physical_id: a device physical ID + * + * Searches the list of parent IDs for a string match. + * + * Returns: %TRUE if the parent ID exists + * + * Since: 1.6.2 + **/ +gboolean +fu_device_has_parent_physical_id(FuDevice *self, const gchar *physical_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(physical_id != NULL, FALSE); + + if (priv->parent_physical_ids == NULL) + return FALSE; + for (guint i = 0; i < priv->parent_physical_ids->len; i++) { + const gchar *tmp = g_ptr_array_index(priv->parent_physical_ids, i); + if (g_strcmp0(tmp, physical_id) == 0) + return TRUE; + } + return FALSE; +} + +/** + * fu_device_add_parent_physical_id: + * @self: a #FuDevice + * @physical_id: a device physical ID + * + * Sets any parent device using the physical ID. An parent device is logically + * linked to the primary device in some way and can be added before or after @self. + * + * The IDs are searched in order, and so the order of adding IDs may be + * important if more than one parent device might match. + * + * Since: 1.6.2 + **/ +void +fu_device_add_parent_physical_id(FuDevice *self, const gchar *physical_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(physical_id != NULL); + + /* ensure exists */ + if (priv->parent_physical_ids == NULL) + priv->parent_physical_ids = g_ptr_array_new_with_free_func(g_free); + + /* already present */ + if (fu_device_has_parent_physical_id(self, physical_id)) + return; + g_ptr_array_add(priv->parent_physical_ids, g_strdup(physical_id)); +} + +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, "context", priv->ctx, "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_inhibit_section(FuDevice *self, const gchar *value, GError **error) +{ + g_auto(GStrv) sections = NULL; + + g_return_val_if_fail(value != NULL, FALSE); + + /* sanity check */ + sections = g_strsplit(value, ":", -1); + if (g_strv_length(sections) != 2) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported, expected k1:v1[,k2:v2][,k3:]"); + return FALSE; + } + + /* allow empty string to unset quirk */ + if (g_strcmp0(sections[1], "") != 0) + fu_device_inhibit(self, sections[0], sections[1]); + else + fu_device_uninhibit(self, sections[0]); + + /* success */ + return TRUE; +} + +/** + * fu_device_set_quirk_kv: + * @self: a #FuDevice + * @key: a string key + * @value: a string value + * @error: (nullable): optional return location for an error + * + * Sets a specific quirk on the device. + * + * Returns: %TRUE on success + * + * Since: 1.8.5 + **/ +gboolean +fu_device_set_quirk_kv(FuDevice *self, const gchar *key, const gchar *value, GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + guint64 tmp; + + g_return_val_if_fail(FU_IS_DEVICE(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); + + if (g_strcmp0(key, FU_QUIRKS_PLUGIN) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_possible_plugin(self, sections[i]); + 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_BRANCH) == 0) { + fu_device_set_branch(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) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_vendor_id(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_PROTOCOL) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_protocol(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_ISSUE) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_issue(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_VERSION) == 0) { + fu_device_set_version(self, value); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_UPDATE_MESSAGE) == 0) { + fu_device_set_update_message(self, value); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_UPDATE_IMAGE) == 0) { + fu_device_set_update_image(self, value); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_ICON) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_icon(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_GUID) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_guid(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_COUNTERPART_GUID) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_counterpart_guid(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_PARENT_GUID) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) + fu_device_add_parent_guid(self, sections[i]); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_PROXY_GUID) == 0) { + fu_device_set_proxy_guid(self, value); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_SIZE_MIN) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT64, error)) + return FALSE; + fu_device_set_firmware_size_min(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_SIZE_MAX) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT64, error)) + return FALSE; + fu_device_set_firmware_size_max(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_SIZE) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT64, error)) + return FALSE; + fu_device_set_firmware_size(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_INSTALL_DURATION) == 0) { + if (!fu_strtoull(value, &tmp, 0, 60 * 60 * 24, error)) + return FALSE; + fu_device_set_install_duration(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_PRIORITY) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + fu_device_set_priority(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_BATTERY_THRESHOLD) == 0) { + if (!fu_strtoull(value, &tmp, 0, 100, error)) + return FALSE; + fu_device_set_battery_threshold(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_REMOVE_DELAY) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, error)) + return FALSE; + fu_device_set_remove_delay(self, tmp); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_ACQUIESCE_DELAY) == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, error)) + return FALSE; + fu_device_set_acquiesce_delay(self, tmp); + 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_INHIBIT) == 0) { + g_auto(GStrv) sections = g_strsplit(value, ",", -1); + for (guint i = 0; sections[i] != NULL; i++) { + if (!fu_device_set_quirk_inhibit_section(self, sections[i], error)) + return FALSE; + } + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_GTYPE) == 0) { + if (priv->specialized_gtype != G_TYPE_INVALID) { + g_debug("already set GType to %s, ignoring %s", + g_type_name(priv->specialized_gtype), + value); + return TRUE; + } + priv->specialized_gtype = g_type_from_name(value); + return TRUE; + } + if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_GTYPE) == 0) { + if (priv->firmware_gtype != G_TYPE_INVALID) { + g_debug("already set firmware GType to %s, ignoring %s", + g_type_name(priv->firmware_gtype), + value); + return TRUE; + } + priv->firmware_gtype = g_type_from_name(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; +} + +/** + * fu_device_get_specialized_gtype: + * @self: a #FuDevice + * + * Gets the specialized type of the device + * + * Returns:#GType + * + * Since: 1.3.3 + **/ +GType +fu_device_get_specialized_gtype(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + return priv->specialized_gtype; +} + +/** + * fu_device_get_firmware_gtype: + * @self: a #FuDevice + * + * Gets the default firmware type for the device. + * + * Returns: #GType + * + * Since: 1.7.2 + **/ +GType +fu_device_get_firmware_gtype(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + return priv->firmware_gtype; +} + +/** + * fu_device_set_firmware_gtype: + * @self: a #FuDevice + * @firmware_gtype: a #GType + * + * Sets the default firmware type for the device. + * + * Since: 1.7.2 + **/ +void +fu_device_set_firmware_gtype(FuDevice *self, GType firmware_gtype) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + priv->firmware_gtype = firmware_gtype; +} + +static void +fu_device_quirks_iter_cb(FuContext *ctx, const gchar *key, const gchar *value, gpointer user_data) +{ + FuDevice *self = FU_DEVICE(user_data); + 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); + } + } +} + +static void +fu_device_add_guid_quirks(FuDevice *self, const gchar *guid) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + if (priv->ctx == NULL) { + g_autofree gchar *str = fu_device_to_string(self); + g_critical("no FuContext assigned for %s", str); + return; + } + fu_context_lookup_quirk_by_id_iter(priv->ctx, guid, fu_device_quirks_iter_cb, self); +} + +/** + * 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, FuDeviceInstanceFlags flags) +{ + /* add the device GUID before adding additional GUIDs from quirks + * to ensure the bootloader GUID is listed after the runtime GUID */ + if ((flags & FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS) == 0) + fwupd_device_add_guid(FWUPD_DEVICE(self), guid); + if ((flags & FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS) == 0) + 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); +} + +/** + * fu_device_add_instance_id_full: + * @self: a #FuDevice + * @instance_id: a instance ID, e.g. `WacomAES` + * @flags: instance ID flags + * + * Adds an instance ID with all parameters set + * + * Since: 1.2.9 + **/ +void +fu_device_add_instance_id_full(FuDevice *self, + const gchar *instance_id, + FuDeviceInstanceFlags flags) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *guid = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(instance_id != 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, flags); + 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); + if ((flags & FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS) == 0) + 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); + + /* already done by ->setup(), so this must be ->registered() */ + if (priv->done_setup) + fwupd_device_add_guid(FWUPD_DEVICE(self), guid); +} + +/** + * fu_device_add_instance_id: + * @self: a #FuDevice + * @instance_id: the instance ID, 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: + * @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(). + * + * Since: 0.7.2 + **/ +void +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_INSTANCE_FLAG_NONE); +} + +/** + * fu_device_add_guid_full: + * @self: a #FuDevice + * @guid: a GUID, e.g. `2082b5e0-7a64-478a-b1b2-e3404fab6dad` + * @flags: instance ID flags + * + * 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(). + * + * Since: 1.6.2 + **/ +void +fu_device_add_guid_full(FuDevice *self, const gchar *guid, FuDeviceInstanceFlags flags) +{ + 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_full(self, guid, flags); + return; + } + fu_device_add_guid_safe(self, guid, flags); +} + +/** + * 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 (!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(self), guid); +} + +/** + * fu_device_get_guids_as_str: + * @self: a #FuDevice + * + * Gets the device GUIDs as a joined string, which may be useful for error + * messages. + * + * Returns: a string, which may be empty length but not %NULL + * + * Since: 1.0.8 + **/ +gchar * +fu_device_get_guids_as_str(FuDevice *self) +{ + 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); +} + +/** + * fu_device_get_metadata: + * @self: a #FuDevice + * @key: the key + * + * Gets an item of metadata from the device. + * + * Returns: a string value, or %NULL for unfound. + * + * Since: 0.1.0 + **/ +const gchar * +fu_device_get_metadata(FuDevice *self, const gchar *key) +{ + 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); + if (priv->metadata == NULL) + return NULL; + return g_hash_table_lookup(priv->metadata, key); +} + +/** + * fu_device_get_metadata_boolean: + * @self: a #FuDevice + * @key: the key + * + * Gets an item of metadata from the device. + * + * Returns: a boolean value, or %FALSE for unfound or failure to parse. + * + * Since: 0.9.7 + **/ +gboolean +fu_device_get_metadata_boolean(FuDevice *self, const gchar *key) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + 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); + + if (priv->metadata == NULL) + return FALSE; + tmp = g_hash_table_lookup(priv->metadata, key); + if (tmp == NULL) + return FALSE; + return g_strcmp0(tmp, "true") == 0; +} + +/** + * fu_device_get_metadata_integer: + * @self: a #FuDevice + * @key: the key + * + * Gets an item of metadata from the device. + * + * Returns: an integer value, or %G_MAXUINT for unfound or failure to parse. + * + * Since: 0.9.7 + **/ +guint +fu_device_get_metadata_integer(FuDevice *self, const gchar *key) +{ + 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(self), G_MAXUINT); + g_return_val_if_fail(key != NULL, G_MAXUINT); + g_return_val_if_fail(locker != NULL, G_MAXUINT); + + if (priv->metadata == NULL) + return G_MAXUINT; + tmp = g_hash_table_lookup(priv->metadata, key); + if (tmp == NULL) + return G_MAXUINT; + val = g_ascii_strtoull(tmp, &endptr, 10); + if (endptr != NULL && endptr[0] != '\0') + return G_MAXUINT; + if (val > G_MAXUINT) + return G_MAXUINT; + return (guint)val; +} + +/** + * fu_device_remove_metadata: + * @self: a #FuDevice + * @key: the key + * + * Removes an item of metadata on the device. + * + * Since: 1.3.3 + **/ +void +fu_device_remove_metadata(FuDevice *self, const gchar *key) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GRWLockWriterLocker) 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(locker != NULL); + if (priv->metadata == NULL) + return; + g_hash_table_remove(priv->metadata, key); +} + +/** + * fu_device_set_metadata: + * @self: a #FuDevice + * @key: the key + * @value: the string value + * + * Sets an item of metadata on the device. + * + * Since: 0.1.0 + **/ +void +fu_device_set_metadata(FuDevice *self, const gchar *key, const gchar *value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GRWLockWriterLocker) 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); + if (priv->metadata == NULL) { + priv->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + } + g_hash_table_insert(priv->metadata, g_strdup(key), g_strdup(value)); +} + +/** + * fu_device_set_metadata_boolean: + * @self: a #FuDevice + * @key: the key + * @value: the boolean value + * + * Sets an item of metadata on the device. When @value is set to %TRUE + * the actual stored value is `true`. + * + * Since: 0.9.7 + **/ +void +fu_device_set_metadata_boolean(FuDevice *self, const gchar *key, gboolean value) +{ + 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: + * @self: a #FuDevice + * @key: the key + * @value: the unsigned integer value + * + * Sets an item of metadata on the device. The integer is stored as a + * base-10 string internally. + * + * Since: 0.9.7 + **/ +void +fu_device_set_metadata_integer(FuDevice *self, const gchar *key, guint value) +{ + g_autofree gchar *tmp = g_strdup_printf("%u", value); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + + fu_device_set_metadata(self, key, tmp); +} + +/* ensure the name does not have the vendor name as the prefix */ +static void +fu_device_fixup_vendor_name(FuDevice *self) +{ + const gchar *name = fu_device_get_name(self); + const gchar *vendor = fu_device_get_vendor(self); + if (name != NULL && vendor != NULL) { + g_autofree gchar *name_up = g_utf8_strup(name, -1); + g_autofree gchar *vendor_up = g_utf8_strup(vendor, -1); + if (g_str_has_prefix(name_up, vendor_up)) { + gsize vendor_len = strlen(vendor); + g_autofree gchar *name1 = g_strdup(name + vendor_len); + g_autofree gchar *name2 = fu_strstrip(name1); + g_debug("removing vendor prefix of '%s' from '%s'", vendor, name); + fwupd_device_set_name(FWUPD_DEVICE(self), name2); + } + } +} + +/** + * fu_device_set_vendor: + * @self: a #FuDevice + * @vendor: a device vendor + * + * Sets the vendor name on the device. + * + * Since: 1.6.2 + **/ +void +fu_device_set_vendor(FuDevice *self, const gchar *vendor) +{ + g_autofree gchar *vendor_safe = NULL; + + /* trim any leading and trailing spaces */ + if (vendor != NULL) + vendor_safe = fu_strstrip(vendor); + + /* proxy */ + fwupd_device_set_vendor(FWUPD_DEVICE(self), vendor_safe); + fu_device_fixup_vendor_name(self); +} + +static gchar * +fu_device_sanitize_name(const gchar *value) +{ + gboolean last_was_space = FALSE; + guint last_non_space = 0; + g_autoptr(GString) new = g_string_new(NULL); + + /* add each printable char with maximum of one whitespace char */ + for (guint i = 0; value[i] != '\0'; i++) { + const gchar tmp = value[i]; + if (!g_ascii_isprint(tmp)) + continue; + if (g_ascii_isspace(tmp) || tmp == '_') { + if (new->len == 0) + continue; + if (last_was_space) + continue; + last_was_space = TRUE; + g_string_append_c(new, ' '); + } else { + last_was_space = FALSE; + g_string_append_c(new, tmp); + last_non_space = new->len; + } + } + g_string_truncate(new, last_non_space); + fu_string_replace(new, "(TM)", "™"); + fu_string_replace(new, "(R)", ""); + if (new->len == 0) + return NULL; + return g_string_free(g_steal_pointer(&new), FALSE); +} + +/** + * fu_device_set_name: + * @self: a #FuDevice + * @value: a device name + * + * Sets the name on the device. Any invalid parts will be converted or removed. + * + * Since: 0.7.1 + **/ +void +fu_device_set_name(FuDevice *self, const gchar *value) +{ + g_autofree gchar *value_safe = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(value != NULL); + + /* overwriting? */ + value_safe = fu_device_sanitize_name(value); + if (g_strcmp0(value_safe, fu_device_get_name(self)) == 0) + return; + + /* changing */ + 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_safe); + } + + fwupd_device_set_name(FWUPD_DEVICE(self), value_safe); + fu_device_fixup_vendor_name(self); +} + +/** + * fu_device_set_id: + * @self: a #FuDevice + * @id: a string, e.g. `tbt-port1` + * + * Sets the ID on the device. The ID should represent the *connection* of the + * device, so that any similar device plugged into a different slot will + * have a different @id string. + * + * The @id will be converted to a SHA1 hash if required before the device is + * added to the daemon, and plugins should not assume that the ID that is set + * here is the same as what is returned by fu_device_get_id(). + * + * Since: 0.7.1 + **/ +void +fu_device_set_id(FuDevice *self, const gchar *id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + GPtrArray *children; + g_autofree gchar *id_hash = NULL; + g_autofree gchar *id_hash_old = g_strdup(fwupd_device_get_id(FWUPD_DEVICE(self))); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(id != NULL); + + /* allow sane device-id to be set directly */ + if (fwupd_device_id_is_valid(id)) { + id_hash = g_strdup(id); + } else { + 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(self), id_hash); + priv->device_id_valid = TRUE; + + /* ensure the parent ID is set */ + children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *devtmp = g_ptr_array_index(children, i); + fwupd_device_set_parent_id(FWUPD_DEVICE(devtmp), id_hash); + + /* update the composite ID of the child with the new ID if required; this will + * propagate to grandchildren and great-grandchildren as required */ + if (id_hash_old != NULL && + g_strcmp0(fu_device_get_composite_id(devtmp), id_hash_old) == 0) + fu_device_set_composite_id(devtmp, id_hash); + } +} + +/** + * fu_device_set_version_format: + * @self: a #FuDevice + * @fmt: the version format, e.g. %FWUPD_VERSION_FORMAT_PLAIN + * + * Sets the device version format. + * + * Since: 1.4.0 + **/ +void +fu_device_set_version_format(FuDevice *self, FwupdVersionFormat fmt) +{ + /* same */ + if (fu_device_get_version_format(self) == fmt) + return; + if (fu_device_get_version_format(self) != FWUPD_VERSION_FORMAT_UNKNOWN) { + g_debug("changing verfmt for %s: %s->%s", + fu_device_get_id(self), + fwupd_version_format_to_string(fu_device_get_version_format(self)), + fwupd_version_format_to_string(fmt)); + } + fwupd_device_set_version_format(FWUPD_DEVICE(self), fmt); +} + +/** + * fu_device_set_version: + * @self: a #FuDevice + * @version: (nullable): a string, e.g. `1.2.3` + * + * Sets the device version, sanitizing the string if required. + * + * Since: 1.2.9 + **/ +void +fu_device_set_version(FuDevice *self, const gchar *version) +{ + g_autofree gchar *version_safe = NULL; + g_autoptr(GError) error = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + + /* sanitize if required */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER)) { + version_safe = + fu_version_ensure_semver(version, fu_device_get_version_format(self)); + if (g_strcmp0(version, version_safe) != 0) + g_debug("converted '%s' to '%s'", version, version_safe); + } else { + version_safe = g_strdup(version); + } + + /* print a console warning for an invalid version, if semver */ + if (version_safe != NULL && + !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error)) + g_warning("%s", error->message); + + /* if different */ + if (g_strcmp0(fu_device_get_version(self), version_safe) != 0) { + if (fu_device_get_version(self) != NULL) { + g_debug("changing version for %s: %s->%s", + fu_device_get_id(self), + fu_device_get_version(self), + version_safe); + } + fwupd_device_set_version(FWUPD_DEVICE(self), version_safe); + } +} + +/** + * fu_device_set_version_lowest: + * @self: a #FuDevice + * @version: (nullable): a string, e.g. `1.2.3` + * + * Sets the device lowest version, sanitizing the string if required. + * + * Since: 1.4.0 + **/ +void +fu_device_set_version_lowest(FuDevice *self, const gchar *version) +{ + g_autofree gchar *version_safe = NULL; + g_autoptr(GError) error = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + + /* sanitize if required */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER)) { + version_safe = + fu_version_ensure_semver(version, fu_device_get_version_format(self)); + if (g_strcmp0(version, version_safe) != 0) + g_debug("converted '%s' to '%s'", version, version_safe); + } else { + version_safe = g_strdup(version); + } + + /* print a console warning for an invalid version, if semver */ + if (version_safe != NULL && + !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error)) + g_warning("%s", error->message); + + /* if different */ + if (g_strcmp0(fu_device_get_version_lowest(self), version_safe) != 0) { + if (fu_device_get_version_lowest(self) != NULL) { + g_debug("changing version lowest for %s: %s->%s", + fu_device_get_id(self), + fu_device_get_version_lowest(self), + version_safe); + } + fwupd_device_set_version_lowest(FWUPD_DEVICE(self), version_safe); + } +} + +/** + * fu_device_set_version_bootloader: + * @self: a #FuDevice + * @version: (nullable): a string, e.g. `1.2.3` + * + * Sets the device bootloader version, sanitizing the string if required. + * + * Since: 1.4.0 + **/ +void +fu_device_set_version_bootloader(FuDevice *self, const gchar *version) +{ + g_autofree gchar *version_safe = NULL; + g_autoptr(GError) error = NULL; + + g_return_if_fail(FU_IS_DEVICE(self)); + + /* sanitize if required */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER)) { + version_safe = + fu_version_ensure_semver(version, fu_device_get_version_format(self)); + if (g_strcmp0(version, version_safe) != 0) + g_debug("converted '%s' to '%s'", version, version_safe); + } else { + version_safe = g_strdup(version); + } + + /* print a console warning for an invalid version, if semver */ + if (version_safe != NULL && + !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error)) + g_warning("%s", error->message); + + /* if different */ + if (g_strcmp0(fu_device_get_version_bootloader(self), version_safe) != 0) { + if (fu_device_get_version_bootloader(self) != NULL) { + g_debug("changing version for %s: %s->%s", + fu_device_get_id(self), + fu_device_get_version_bootloader(self), + version_safe); + } + fwupd_device_set_version_bootloader(FWUPD_DEVICE(self), version_safe); + } +} + +static void +fu_device_inhibit_free(FuDeviceInhibit *inhibit) +{ + g_free(inhibit->inhibit_id); + g_free(inhibit->reason); + g_free(inhibit); +} + +static void +fu_device_ensure_inhibits(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FwupdDeviceProblem problems = FWUPD_DEVICE_PROBLEM_NONE; + guint nr_inhibits = g_hash_table_size(priv->inhibits); + + /* disable */ + if (priv->notify_flags_handler_id != 0) + g_signal_handler_block(self, priv->notify_flags_handler_id); + + /* was okay -> not okay */ + if (nr_inhibits > 0) { + g_autofree gchar *reasons_str = NULL; + g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits); + g_autoptr(GPtrArray) reasons = g_ptr_array_new(); + + /* updatable -> updatable-hidden -- which is required as devices might have + * inhibits and *not* be automatically updatable */ + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE)) { + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN); + } + + /* update update error */ + for (GList *l = values; l != NULL; l = l->next) { + FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data; + g_ptr_array_add(reasons, inhibit->reason); + problems |= inhibit->problem; + } + reasons_str = fu_strjoin(", ", reasons); + fu_device_set_update_error(self, reasons_str); + } else { + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) { + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN); + fu_device_add_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE); + } + fu_device_set_update_error(self, NULL); + } + + /* sync with baseclass */ + fwupd_device_set_problems(FWUPD_DEVICE(self), problems); + + /* enable */ + if (priv->notify_flags_handler_id != 0) + g_signal_handler_unblock(self, priv->notify_flags_handler_id); +} + +static gchar * +fu_device_problem_to_inhibit_reason(FuDevice *self, guint64 device_problem) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + if (device_problem == FWUPD_DEVICE_PROBLEM_UNREACHABLE) + return g_strdup("Device is unreachable, or out of wireless range"); + if (device_problem == FWUPD_DEVICE_PROBLEM_UPDATE_PENDING) + return g_strdup("Device is waiting for the update to be applied"); + if (device_problem == FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER) + return g_strdup("Device requires AC power to be connected"); + if (device_problem == FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED) + return g_strdup("Device cannot be used while the lid is closed"); + if (device_problem == FWUPD_DEVICE_PROBLEM_IS_EMULATED) + return g_strdup("Device is emulated"); + if (device_problem == FWUPD_DEVICE_PROBLEM_MISSING_LICENSE) + return g_strdup("Device does not have the necessary license installed"); + if (device_problem == FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW) { + if (priv->ctx == NULL) + return g_strdup("System power is too low to perform the update"); + return g_strdup_printf( + "System power is too low to perform the update (%u%%, requires %u%%)", + fu_context_get_battery_level(priv->ctx), + fu_context_get_battery_threshold(priv->ctx)); + } + if (device_problem == FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW) { + if (fu_device_get_battery_level(self) == FWUPD_BATTERY_LEVEL_INVALID || + fu_device_get_battery_threshold(self) == FWUPD_BATTERY_LEVEL_INVALID) { + return g_strdup_printf("Device battery power is too low"); + } + return g_strdup_printf("Device battery power is too low (%u%%, requires %u%%)", + fu_device_get_battery_level(self), + fu_device_get_battery_threshold(self)); + } + return NULL; +} + +static void +fu_device_inhibit_full(FuDevice *self, + FwupdDeviceProblem problem, + const gchar *inhibit_id, + const gchar *reason) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FuDeviceInhibit *inhibit; + + g_return_if_fail(FU_IS_DEVICE(self)); + + /* lazy create as most devices will not need this */ + if (priv->inhibits == NULL) { + priv->inhibits = g_hash_table_new_full(g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify)fu_device_inhibit_free); + } + + /* can fallback */ + if (inhibit_id == NULL) + inhibit_id = fwupd_device_problem_to_string(problem); + + /* already exists */ + inhibit = g_hash_table_lookup(priv->inhibits, inhibit_id); + if (inhibit != NULL) + return; + + /* create new */ + inhibit = g_new0(FuDeviceInhibit, 1); + inhibit->problem = problem; + inhibit->inhibit_id = g_strdup(inhibit_id); + if (reason != NULL) { + inhibit->reason = g_strdup(reason); + } else { + inhibit->reason = fu_device_problem_to_inhibit_reason(self, problem); + } + g_hash_table_insert(priv->inhibits, inhibit->inhibit_id, inhibit); + + /* refresh */ + fu_device_ensure_inhibits(self); + + /* propagate to children */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN)) { + GPtrArray *children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index(children, i); + fu_device_inhibit(child, inhibit_id, reason); + } + } +} + +/** + * fu_device_inhibit: + * @self: a #FuDevice + * @inhibit_id: an ID used for uninhibiting, e.g. `low-power` + * @reason: (nullable): a string, e.g. `Cannot update as foo [bar] needs reboot` + * + * Prevent the device from being updated, changing it from %FWUPD_DEVICE_FLAG_UPDATABLE + * to %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN if not already inhibited. + * + * If the device already has an inhibit with the same @inhibit_id then the request + * is ignored. + * + * Since: 1.6.0 + **/ +void +fu_device_inhibit(FuDevice *self, const gchar *inhibit_id, const gchar *reason) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(inhibit_id != NULL); + fu_device_inhibit_full(self, FWUPD_DEVICE_PROBLEM_NONE, inhibit_id, reason); +} + +/** + * fu_device_has_inhibit: + * @self: a #FuDevice + * @inhibit_id: an ID used for inhibiting, e.g. `low-power` + * + * Check if the device already has an inhibit with a specific ID. + * + * Returns: %TRUE if added + * + * Since: 1.8.0 + **/ +gboolean +fu_device_has_inhibit(FuDevice *self, const gchar *inhibit_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(inhibit_id != NULL, FALSE); + + if (priv->inhibits == NULL) + return FALSE; + return g_hash_table_contains(priv->inhibits, inhibit_id); +} + +/** + * fu_device_remove_problem: + * @self: a #FuDevice + * @problem: a #FwupdDeviceProblem, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW + * + * Allow the device from being updated if there are no other inhibitors, + * changing it from %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN to %FWUPD_DEVICE_FLAG_UPDATABLE. + * + * If the device already has no inhibit with the @inhibit_id then the request + * is ignored. + * + * Since: 1.8.1 + **/ +void +fu_device_remove_problem(FuDevice *self, FwupdDeviceProblem problem) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(problem != FWUPD_DEVICE_PROBLEM_UNKNOWN); + return fu_device_uninhibit(self, fwupd_device_problem_to_string(problem)); +} + +/** + * fu_device_add_problem: + * @self: a #FuDevice + * @problem: a #FwupdDeviceProblem, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW + * + * Prevent the device from being updated, changing it from %FWUPD_DEVICE_FLAG_UPDATABLE + * to %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN if not already inhibited. + * + * If the device already has an inhibit with the same @problem then the request + * is ignored. + * + * Since: 1.8.1 + **/ +void +fu_device_add_problem(FuDevice *self, FwupdDeviceProblem problem) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(problem != FWUPD_DEVICE_PROBLEM_UNKNOWN); + fu_device_inhibit_full(self, problem, NULL, NULL); +} + +/** + * fu_device_uninhibit: + * @self: a #FuDevice + * @inhibit_id: an ID used for uninhibiting, e.g. `low-power` + * + * Allow the device from being updated if there are no other inhibitors, + * changing it from %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN to %FWUPD_DEVICE_FLAG_UPDATABLE. + * + * If the device already has no inhibit with the @inhibit_id then the request + * is ignored. + * + * Since: 1.6.0 + **/ +void +fu_device_uninhibit(FuDevice *self, const gchar *inhibit_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(inhibit_id != NULL); + + if (priv->inhibits == NULL) + return; + if (g_hash_table_remove(priv->inhibits, inhibit_id)) + fu_device_ensure_inhibits(self); + + /* propagate to children */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN)) { + GPtrArray *children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index(children, i); + fu_device_uninhibit(child, inhibit_id); + } + } +} + +/** + * fu_device_ensure_id: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * 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) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + 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 (priv->device_id_valid) + return TRUE; + + /* nothing we can do! */ + if (priv->physical_id == 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_logical_id: + * @self: a #FuDevice + * + * 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.1.2 + **/ +const gchar * +fu_device_get_logical_id(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->logical_id; +} + +/** + * fu_device_set_logical_id: + * @self: a #FuDevice + * @logical_id: a string, e.g. `dev2` + * + * Sets the logical ID on the device. This is designed to disambiguate devices + * with the same physical ID. + * + * Since: 1.1.2 + **/ +void +fu_device_set_logical_id(FuDevice *self, const gchar *logical_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->logical_id, logical_id) == 0) + return; + + /* not allowed after ->probe() and ->setup() have completed */ + if (priv->done_setup) { + g_warning("cannot change %s logical ID from %s to %s as " + "FuDevice->setup() has already completed", + fu_device_get_id(self), + priv->logical_id, + logical_id); + return; + } + + g_free(priv->logical_id); + priv->logical_id = g_strdup(logical_id); + priv->device_id_valid = FALSE; + g_object_notify(G_OBJECT(self), "logical-id"); +} + +/** + * fu_device_get_backend_id: + * @self: a #FuDevice + * + * Gets the ID set for the device as recognized by the backend. This is typically + * a Linux sysfs path or USB platform ID. If unset, it also falls back to the + * physical ID as this may be the same value. + * + * Returns: a string value, or %NULL if never set. + * + * Since: 1.5.8 + **/ +const gchar * +fu_device_get_backend_id(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + if (priv->backend_id != NULL) + return priv->backend_id; + return priv->physical_id; +} + +/** + * fu_device_set_backend_id: + * @self: a #FuDevice + * @backend_id: a string, e.g. `dev2` + * + * Sets the backend ID on the device. This is designed to disambiguate devices + * with the same physical ID. This is typically a Linux sysfs path or USB + * platform ID. + * + * Since: 1.5.8 + **/ +void +fu_device_set_backend_id(FuDevice *self, const gchar *backend_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->backend_id, backend_id) == 0) + return; + + g_free(priv->backend_id); + priv->backend_id = g_strdup(backend_id); + priv->device_id_valid = FALSE; + g_object_notify(G_OBJECT(self), "backend-id"); +} + +/** + * fu_device_get_update_request_id: + * @self: a #FuDevice + * + * Gets the update request ID as specified from `LVFS::UpdateRequestId`. + * + * Returns: a string value, or %NULL if never set. + * + * Since: 1.8.6 + **/ +const gchar * +fu_device_get_update_request_id(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->update_request_id; +} + +/** + * fu_device_set_update_request_id: + * @self: a #FuDevice + * @update_request_id: a string, e.g. `org.freedesktop.fwupd.request.do-not-power-off` + * + * Sets the update request ID as specified in `LVFS::UpdateRequestId`. + * + * Since: 1.8.6 + **/ +void +fu_device_set_update_request_id(FuDevice *self, const gchar *update_request_id) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->update_request_id, update_request_id) == 0) + return; + + g_free(priv->update_request_id); + priv->update_request_id = g_strdup(update_request_id); +} + +/** + * fu_device_get_proxy_guid: + * @self: a #FuDevice + * + * Gets the proxy GUID device, which which is set to let the engine match up the + * proxy between plugins. + * + * Returns: a string value, or %NULL if never set. + * + * Since: 1.4.1 + **/ +const gchar * +fu_device_get_proxy_guid(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->proxy_guid; +} + +/** + * fu_device_set_proxy_guid: + * @self: a #FuDevice + * @proxy_guid: a string, e.g. `USB\VID_413C&PID_B06E&hub` + * + * Sets the GUID of the proxy device. The proxy device may update @self. + * + * Since: 1.4.1 + **/ +void +fu_device_set_proxy_guid(FuDevice *self, const gchar *proxy_guid) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + + /* not changed */ + if (g_strcmp0(priv->proxy_guid, proxy_guid) == 0) + return; + + g_free(priv->proxy_guid); + priv->proxy_guid = g_strdup(proxy_guid); +} + +/** + * 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) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(physical_id != NULL); + + /* not changed */ + if (g_strcmp0(priv->physical_id, physical_id) == 0) + return; + + /* not allowed after ->probe() and ->setup() have completed */ + if (priv->done_setup) { + g_warning("cannot change %s physical ID from %s to %s as " + "FuDevice->setup() has already completed", + fu_device_get_id(self), + priv->physical_id, + physical_id); + return; + } + + g_free(priv->physical_id); + priv->physical_id = g_strdup(physical_id); + priv->device_id_valid = FALSE; + g_object_notify(G_OBJECT(self), "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. + * + * Multiple #FuDevices can share a single physical ID. + * + * Returns: a string value, or %NULL if never set. + * + * Since: 1.1.2 + **/ +const gchar * +fu_device_get_physical_id(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->physical_id; +} + +/** + * fu_device_remove_flag: + * @self: a #FuDevice + * @flag: a device flag + * + * Removes a device flag from the device. + * + * Since: 1.6.0 + **/ +void +fu_device_remove_flag(FuDevice *self, FwupdDeviceFlags flag) +{ + /* proxy */ + fwupd_device_remove_flag(FWUPD_DEVICE(self), flag); + + /* allow it to be updatable again */ + if (flag & FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) + fu_device_uninhibit(self, "needs-activation"); + if (flag & FWUPD_DEVICE_FLAG_UNREACHABLE) + fu_device_uninhibit(self, "unreachable"); +} + +/** + * fu_device_add_flag: + * @self: a #FuDevice + * @flag: a device flag + * + * Adds a device flag to the device. + * + * Since: 0.1.0 + **/ +void +fu_device_add_flag(FuDevice *self, FwupdDeviceFlags flag) +{ + /* none is not used as an "exported" flag */ + if (flag == FWUPD_DEVICE_FLAG_NONE) + return; + + /* being both a bootloader and requiring a bootloader is invalid */ + if (flag & FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + if (flag & FWUPD_DEVICE_FLAG_IS_BOOTLOADER) + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + + /* being both a signed and unsigned is invalid */ + if (flag & FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + if (flag & FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD) + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + + /* one implies the other */ + if (flag & FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) + flag |= FWUPD_DEVICE_FLAG_CAN_VERIFY; + if (flag & FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES) + flag |= FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED; + fwupd_device_add_flag(FWUPD_DEVICE(self), flag); + + /* activatable devices shouldn't be allowed to update again until activated */ + /* don't let devices be updated until activated */ + if (flag & FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) + fu_device_inhibit(self, "needs-activation", "Pending activation"); + + /* do not let devices be updated until back in range */ + if (flag & FWUPD_DEVICE_FLAG_UNREACHABLE) + fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_UNREACHABLE); +} + +typedef struct { + guint64 value; + gchar *value_str; +} FuDevicePrivateFlagItem; + +static void +fu_device_private_flag_item_free(FuDevicePrivateFlagItem *item) +{ + g_free(item->value_str); + g_free(item); +} + +static FuDevicePrivateFlagItem * +fu_device_private_flag_item_find_by_str(FuDevice *self, const gchar *value_str) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + if (priv->private_flag_items == NULL) + return NULL; + for (guint i = 0; i < priv->private_flag_items->len; i++) { + FuDevicePrivateFlagItem *item = g_ptr_array_index(priv->private_flag_items, i); + if (g_strcmp0(item->value_str, value_str) == 0) + return item; + } + return NULL; +} + +static FuDevicePrivateFlagItem * +fu_device_private_flag_item_find_by_val(FuDevice *self, guint64 value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + if (priv->private_flag_items == NULL) + return NULL; + for (guint i = 0; i < priv->private_flag_items->len; i++) { + FuDevicePrivateFlagItem *item = g_ptr_array_index(priv->private_flag_items, i); + if (item->value == value) + return item; + } + return NULL; +} + +/** + * fu_device_register_private_flag: + * @self: a #FuDevice + * @value: an integer value + * @value_str: a string that represents @value + * + * Registers a private device flag so that it can be set from quirk files. + * + * Since: 1.6.2 + **/ +void +fu_device_register_private_flag(FuDevice *self, guint64 value, const gchar *value_str) +{ + FuDevicePrivateFlagItem *item; + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(value != 0); + g_return_if_fail(value_str != NULL); + + /* ensure exists */ + if (priv->private_flag_items == NULL) { + priv->private_flag_items = g_ptr_array_new_with_free_func( + (GDestroyNotify)fu_device_private_flag_item_free); + } + + /* sanity check */ + item = fu_device_private_flag_item_find_by_val(self, value); + if (item != NULL) { + g_critical("already registered private %s flag with value: %s:0x%x", + G_OBJECT_TYPE_NAME(self), + value_str, + (guint)value); + return; + } + item = fu_device_private_flag_item_find_by_str(self, value_str); + if (item != NULL) { + g_critical("already registered private %s flag with string: %s:0x%x", + G_OBJECT_TYPE_NAME(self), + value_str, + (guint)value); + return; + } + + /* add new */ + item = g_new0(FuDevicePrivateFlagItem, 1); + item->value = value; + item->value_str = g_strdup(value_str); + g_ptr_array_add(priv->private_flag_items, item); +} + +static void +fu_device_set_custom_flag(FuDevice *self, const gchar *hint) +{ + FwupdDeviceFlags flag; + FuDevicePrivateFlagItem *item; + FuDeviceInternalFlags internal_flag; + FuDevicePrivate *priv = GET_PRIVATE(self); + + /* is this a negated device flag */ + if (g_str_has_prefix(hint, "~")) { + flag = fwupd_device_flag_from_string(hint + 1); + if (flag != FWUPD_DEVICE_FLAG_UNKNOWN) { + fu_device_remove_flag(self, flag); + return; + } + internal_flag = fu_device_internal_flag_from_string(hint + 1); + if (internal_flag != FU_DEVICE_INTERNAL_FLAG_UNKNOWN) { + fu_device_remove_internal_flag(self, internal_flag); + return; + } + item = fu_device_private_flag_item_find_by_str(self, hint + 1); + if (item != NULL) { + priv->private_flags &= ~item->value; + return; + } + return; + } + + /* is this a known device flag */ + flag = fwupd_device_flag_from_string(hint); + if (flag != FWUPD_DEVICE_FLAG_UNKNOWN) { + fu_device_add_flag(self, flag); + return; + } + internal_flag = fu_device_internal_flag_from_string(hint); + if (internal_flag != FU_DEVICE_INTERNAL_FLAG_UNKNOWN) { + fu_device_add_internal_flag(self, internal_flag); + return; + } + item = fu_device_private_flag_item_find_by_str(self, hint); + if (item != NULL) { + priv->private_flags |= item->value; + return; + } +} + +/** + * fu_device_set_custom_flags: + * @self: a #FuDevice + * @custom_flags: a string + * + * 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.1.0 + **/ +void +fu_device_set_custom_flags(FuDevice *self, const gchar *custom_flags) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(custom_flags != NULL); + + /* save what was set so we can use it for incorporating a superclass */ + g_free(priv->custom_flags); + priv->custom_flags = g_strdup(custom_flags); + + /* 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_get_custom_flags: + * @self: a #FuDevice + * + * Gets the custom flags for the device from the quirk system. + * + * Returns: a string value, or %NULL if never set. + * + * Since: 1.1.0 + **/ +const gchar * +fu_device_get_custom_flags(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->custom_flags; +} + +/** + * fu_device_get_remove_delay: + * @self: a #FuDevice + * + * Returns the maximum delay expected when replugging the device going into + * bootloader mode. + * + * Returns: time in milliseconds + * + * Since: 1.0.2 + **/ +guint +fu_device_get_remove_delay(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), 0); + return priv->remove_delay; +} + +/** + * fu_device_set_remove_delay: + * @self: a #FuDevice + * @remove_delay: the value in milliseconds + * + * Sets the amount of time a device is allowed to return in bootloader mode. + * + * NOTE: this should be less than 3000ms for devices that just have to reset + * and automatically re-enumerate, but significantly longer if it involves a + * user removing a cable, pressing several buttons and removing a cable. + * A suggested value for this would be 10,000ms. + * + * Since: 1.0.2 + **/ +void +fu_device_set_remove_delay(FuDevice *self, guint remove_delay) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->remove_delay = remove_delay; +} + +/** + * fu_device_get_acquiesce_delay: + * @self: a #FuDevice + * + * Returns the time the daemon should wait for devices to finish hotplugging + * after the update has completed. + * + * Returns: time in milliseconds + * + * Since: 1.8.3 + **/ +guint +fu_device_get_acquiesce_delay(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), 0); + return priv->acquiesce_delay; +} + +/** + * fu_device_set_acquiesce_delay: + * @self: a #FuDevice + * @acquiesce_delay: the value in milliseconds + * + * Sets the time the daemon should wait for devices to finish hotplugging + * after the update has completed. + * + * Devices subclassing from [class@FuUsbDevice] and [class@FuUdevDevice] use + * a value of 2,500ms, and other devices use 50ms by default. This can be also + * be set using `AcquiesceDelay=` in a quirk file. + * + * Since: 1.8.3 + **/ +void +fu_device_set_acquiesce_delay(FuDevice *self, guint acquiesce_delay) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + priv->acquiesce_delay = acquiesce_delay; +} + +/** + * fu_device_set_update_state: + * @self: a #FuDevice + * @update_state: the state, e.g. %FWUPD_UPDATE_STATE_PENDING + * + * Sets the update state, clearing the update error as required. + * + * Since: 1.6.2 + **/ +void +fu_device_set_update_state(FuDevice *self, FwupdUpdateState update_state) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + if (update_state == FWUPD_UPDATE_STATE_SUCCESS || + update_state == FWUPD_UPDATE_STATE_PENDING || + update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) + fu_device_set_update_error(self, NULL); + fwupd_device_set_update_state(FWUPD_DEVICE(self), update_state); +} + +static void +fu_device_ensure_battery_inhibit(FuDevice *self) +{ + if (fu_device_get_battery_level(self) == FWUPD_BATTERY_LEVEL_INVALID || + fu_device_get_battery_level(self) >= fu_device_get_battery_threshold(self)) { + fu_device_remove_problem(self, FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW); + return; + } + fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW); +} + +/** + * fu_device_get_battery_level: + * @self: a #FuDevice + * + * Returns the battery level. + * + * Returns: value in percent + * + * Since: 1.5.8 + **/ +guint +fu_device_get_battery_level(FuDevice *self) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT); + + /* use the parent if the child is unset */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY) && + fwupd_device_get_battery_level(FWUPD_DEVICE(self)) == FWUPD_BATTERY_LEVEL_INVALID) { + FuDevice *parent = fu_device_get_parent(self); + if (parent != NULL) + return fu_device_get_battery_level(parent); + } + return fwupd_device_get_battery_level(FWUPD_DEVICE(self)); +} + +/** + * fu_device_set_battery_level: + * @self: a #FuDevice + * @battery_level: the percentage value + * + * Sets the battery level, or %FWUPD_BATTERY_LEVEL_INVALID. + * + * Setting this allows fwupd to show a warning if the device change is too low + * to perform the update. + * + * Since: 1.5.8 + **/ +void +fu_device_set_battery_level(FuDevice *self, guint battery_level) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(battery_level <= FWUPD_BATTERY_LEVEL_INVALID); + fwupd_device_set_battery_level(FWUPD_DEVICE(self), battery_level); + fu_device_ensure_battery_inhibit(self); +} + +/** + * fu_device_get_battery_threshold: + * @self: a #FuDevice + * + * Returns the battery threshold under which a firmware update cannot be + * performed. + * + * If fu_device_set_battery_threshold() has not been used, a default value is + * used instead. + * + * Returns: value in percent + * + * Since: 1.6.0 + **/ +guint +fu_device_get_battery_threshold(FuDevice *self) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), FWUPD_BATTERY_LEVEL_INVALID); + + /* use the parent if the child is unset */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY) && + fwupd_device_get_battery_threshold(FWUPD_DEVICE(self)) == FWUPD_BATTERY_LEVEL_INVALID) { + FuDevice *parent = fu_device_get_parent(self); + if (parent != NULL) + return fu_device_get_battery_threshold(parent); + } + return fwupd_device_get_battery_threshold(FWUPD_DEVICE(self)); +} + +/** + * fu_device_set_battery_threshold: + * @self: a #FuDevice + * @battery_threshold: the percentage value + * + * Sets the battery level, or %FWUPD_BATTERY_LEVEL_INVALID for the default. + * + * Setting this allows fwupd to show a warning if the device change is too low + * to perform the update. + * + * Since: 1.6.0 + **/ +void +fu_device_set_battery_threshold(FuDevice *self, guint battery_threshold) +{ + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(battery_threshold <= FWUPD_BATTERY_LEVEL_INVALID); + fwupd_device_set_battery_threshold(FWUPD_DEVICE(self), battery_threshold); + fu_device_ensure_battery_inhibit(self); +} + +/** + * fu_device_add_string: + * @self: a #FuDevice + * @idt: indent level + * @str: a string to append to + * + * Add daemon-specific device metadata to an existing string. + * + * Since: 1.7.1 + **/ +void +fu_device_add_string(FuDevice *self, guint idt, GString *str) +{ + GPtrArray *children; + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *tmp = NULL; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new(&priv->metadata_mutex); + + g_return_if_fail(locker != NULL); + + tmp = fwupd_device_to_string(FWUPD_DEVICE(self)); + if (tmp != NULL && tmp[0] != '\0') + g_string_append(str, tmp); + if (priv->alternate_id != NULL) + fu_string_append(str, idt + 1, "AlternateId", priv->alternate_id); + if (priv->equivalent_id != NULL) + fu_string_append(str, idt + 1, "EquivalentId", priv->equivalent_id); + if (priv->physical_id != NULL) + fu_string_append(str, idt + 1, "PhysicalId", priv->physical_id); + if (priv->logical_id != NULL) + fu_string_append(str, idt + 1, "LogicalId", priv->logical_id); + if (priv->backend_id != NULL) + fu_string_append(str, idt + 1, "BackendId", priv->backend_id); + if (priv->update_request_id != NULL) + fu_string_append(str, idt + 1, "UpdateRequestId", priv->update_request_id); + if (priv->proxy != NULL) + fu_string_append(str, idt + 1, "ProxyId", fu_device_get_id(priv->proxy)); + if (priv->proxy_guid != NULL) + fu_string_append(str, idt + 1, "ProxyGuid", priv->proxy_guid); + if (priv->remove_delay != 0) + fu_string_append_ku(str, idt + 1, "RemoveDelay", priv->remove_delay); + if (priv->acquiesce_delay != 0) + fu_string_append_ku(str, idt + 1, "AcquiesceDelay", priv->acquiesce_delay); + if (priv->custom_flags != NULL) + fu_string_append(str, idt + 1, "CustomFlags", priv->custom_flags); + if (priv->firmware_gtype != G_TYPE_INVALID) { + fu_string_append(str, idt + 1, "FirmwareGType", g_type_name(priv->firmware_gtype)); + } + if (priv->size_min > 0) { + g_autofree gchar *sz = g_strdup_printf("%" G_GUINT64_FORMAT, priv->size_min); + fu_string_append(str, idt + 1, "FirmwareSizeMin", sz); + } + if (priv->size_max > 0) { + g_autofree gchar *sz = g_strdup_printf("%" G_GUINT64_FORMAT, priv->size_max); + fu_string_append(str, idt + 1, "FirmwareSizeMax", sz); + } + if (priv->order != G_MAXINT) { + g_autofree gchar *order = g_strdup_printf("%i", priv->order); + fu_string_append(str, idt + 1, "Order", order); + } + if (priv->priority > 0) + fu_string_append_ku(str, idt + 1, "Priority", priv->priority); + if (priv->metadata != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys(priv->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(priv->metadata, key); + fu_string_append(str, idt + 1, key, value); + } + } + for (guint i = 0; i < priv->possible_plugins->len; i++) { + const gchar *name = g_ptr_array_index(priv->possible_plugins, i); + fu_string_append(str, idt + 1, "PossiblePlugin", name); + } + if (priv->parent_physical_ids != NULL && priv->parent_physical_ids->len > 0) { + g_autofree gchar *flags = fu_strjoin(",", priv->parent_physical_ids); + fu_string_append(str, idt + 1, "ParentPhysicalIds", flags); + } + if (priv->internal_flags != FU_DEVICE_INTERNAL_FLAG_NONE) { + g_autoptr(GString) tmp2 = g_string_new(""); + for (guint i = 0; i < 64; i++) { + if ((priv->internal_flags & ((guint64)1 << i)) == 0) + continue; + g_string_append_printf(tmp2, + "%s|", + fu_device_internal_flag_to_string((guint64)1 << i)); + } + if (tmp2->len > 0) + g_string_truncate(tmp2, tmp2->len - 1); + fu_string_append(str, idt + 1, "InternalFlags", tmp2->str); + } + if (priv->private_flags > 0) { + g_autoptr(GPtrArray) tmpv = g_ptr_array_new(); + g_autofree gchar *tmps = NULL; + for (guint64 i = 0; i < 64; i++) { + FuDevicePrivateFlagItem *item; + guint64 value = 1ull << i; + if ((priv->private_flags & value) == 0) + continue; + item = fu_device_private_flag_item_find_by_val(self, value); + if (item == NULL) + continue; + g_ptr_array_add(tmpv, item->value_str); + } + tmps = fu_strjoin(",", tmpv); + fu_string_append(str, idt + 1, "PrivateFlags", tmps); + } + if (priv->inhibits != NULL) { + g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits); + for (GList *l = values; l != NULL; l = l->next) { + FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data; + g_autofree gchar *val = + g_strdup_printf("[%s] %s", inhibit->inhibit_id, inhibit->reason); + fu_string_append(str, idt + 1, "Inhibit", val); + } + } + + /* subclassed */ + if (klass->to_string != NULL) + klass->to_string(self, idt + 1, str); + + /* print children also */ + children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index(children, i); + fu_device_add_string(child, idt + 1, str); + } +} + +/** + * fu_device_to_string: + * @self: a #FuDevice + * + * This allows us to easily print the device, the release and the + * daemon-specific metadata. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 0.9.8 + **/ +gchar * +fu_device_to_string(FuDevice *self) +{ + GString *str = g_string_new(NULL); + fu_device_add_string(self, 0, str); + return g_string_free(str, FALSE); +} + +/** + * fu_device_set_context: + * @self: a #FuDevice + * @ctx: (nullable): optional #FuContext + * + * Sets the optional context which may be useful to this device. + * This is typically set after the device has been created, but before + * the device has been opened or probed. + * + * Since: 1.6.0 + **/ +void +fu_device_set_context(FuDevice *self, FuContext *ctx) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(FU_IS_CONTEXT(ctx) || ctx == NULL); + +#ifndef SUPPORTED_BUILD + if (priv->ctx != NULL && ctx == NULL) { + g_critical("clearing device context for %s [%s]", + fu_device_get_name(self), + fu_device_get_id(self)); + return; + } +#endif + + if (g_set_object(&priv->ctx, ctx)) + g_object_notify(G_OBJECT(self), "context"); +} + +/** + * fu_device_get_context: + * @self: a #FuDevice + * + * Gets the context assigned for this device. + * + * Returns: (transfer none): the #FuContext object, or %NULL + * + * Since: 1.6.0 + **/ +FuContext * +fu_device_get_context(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->ctx; +} + +/** + * fu_device_get_release_default: + * @self: a #FuDevice + * + * Gets the default release for the device, creating one if not found. + * + * Returns: (transfer none): the #FwupdRelease object + * + * Since: 1.0.5 + **/ +FwupdRelease * +fu_device_get_release_default(FuDevice *self) +{ + g_autoptr(FwupdRelease) rel = NULL; + 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(self), rel); + return rel; +} + +/** + * fu_device_get_results: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Gets the results of the last update operation on the device by calling a vfunc. + * + * Returns: %TRUE on success + * + * Since: 1.6.2 + **/ +gboolean +fu_device_get_results(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); + + /* no plugin-specific method */ + if (klass->get_results == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "getting results not supported by device"); + return FALSE; + } + + /* call vfunc */ + return klass->get_results(self, error); +} + +/** + * fu_device_write_firmware: + * @self: a #FuDevice + * @fw: firmware blob + * @progress: a #FuProgress + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Writes firmware to the device by calling a plugin-specific vfunc. + * + * Returns: %TRUE on success + * + * Since: 1.0.8 + **/ +gboolean +fu_device_write_firmware(FuDevice *self, + GBytes *fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = NULL; + g_autofree gchar *str = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* no plugin-specific method */ + if (klass->write_firmware == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "writing firmware not supported by device"); + return FALSE; + } + + /* prepare (e.g. decompress) firmware */ + fu_progress_set_status(progress, FWUPD_STATUS_DECOMPRESSING); + firmware = fu_device_prepare_firmware(self, fw, flags, error); + if (firmware == NULL) + return FALSE; + str = fu_firmware_to_string(firmware); + g_debug("installing onto %s:\n%s", fu_device_get_id(self), str); + + /* call vfunc */ + if (!klass->write_firmware(self, firmware, progress, flags, error)) + return FALSE; + + /* the device set an UpdateMessage (possibly from a quirk, or XML file) + * but did not do an event; guess something */ + if (priv->request_cnts[FWUPD_REQUEST_KIND_POST] == 0 && + fu_device_get_update_message(self) != NULL) { + const gchar *update_request_id = fu_device_get_update_request_id(self); + g_autoptr(FwupdRequest) request = fwupd_request_new(); + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_POST); + if (update_request_id != NULL) { + fwupd_request_set_id(request, update_request_id); + fwupd_request_add_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + } + fwupd_request_set_message(request, fu_device_get_update_message(self)); + fwupd_request_set_image(request, fu_device_get_update_image(self)); + fu_device_emit_request(self, request); + } + + /* success */ + return TRUE; +} + +/** + * fu_device_prepare_firmware: + * @self: a #FuDevice + * @fw: firmware blob + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Prepares the firmware by calling an optional device-specific vfunc for the + * 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 + **/ +FuFirmware * +fu_device_prepare_firmware(FuDevice *self, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) fw_def = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(fw != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* optionally subclassed */ + if (klass->prepare_firmware != NULL) { + firmware = klass->prepare_firmware(self, fw, flags, error); + if (firmware == NULL) + return NULL; + } else if (priv->firmware_gtype != G_TYPE_INVALID) { + firmware = g_object_new(priv->firmware_gtype, NULL); + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + } else { + firmware = fu_firmware_new_from_bytes(fw); + } + + /* check size */ + fw_def = fu_firmware_get_bytes(firmware, NULL); + if (fw_def != NULL) { + guint64 fw_sz = (guint64)g_bytes_get_size(fw_def); + if (priv->size_max > 0 && fw_sz > priv->size_max) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is 0x%04x bytes larger than the allowed " + "maximum size of 0x%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; + } + } + + /* success */ + return g_steal_pointer(&firmware); +} + +/** + * fu_device_read_firmware: + * @self: a #FuDevice + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Reads firmware from the device by calling a plugin-specific vfunc. + * The device subclass should try to ensure the firmware does not contain any + * serial numbers or user-configuration values and can be used to calculate the + * device checksum. + * + * The return value can be converted to a blob of memory using fu_firmware_write(). + * + * Returns: (transfer full): a #FuFirmware, or %NULL for error + * + * Since: 1.0.8 + **/ +FuFirmware * +fu_device_read_firmware(FuDevice *self, FuProgress *progress, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + g_autoptr(GBytes) fw = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(FU_IS_PROGRESS(progress), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* device does not support reading for verification CRCs */ + if (!fu_device_has_flag(self, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "reading firmware is not supported by device"); + return NULL; + } + + /* call vfunc */ + if (klass->read_firmware != NULL) + return klass->read_firmware(self, progress, error); + + /* use the default FuFirmware when only ->dump_firmware is provided */ + fw = fu_device_dump_firmware(self, progress, error); + if (fw == NULL) + return NULL; + return fu_firmware_new_from_bytes(fw); +} + +/** + * fu_device_dump_firmware: + * @self: a #FuDevice + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Reads the raw firmware image from the device by calling a plugin-specific + * vfunc. This raw firmware image may contain serial numbers or device-specific + * configuration but should be a byte-for-byte match compared to using an + * external SPI programmer. + * + * Returns: (transfer full): a #GBytes, or %NULL for error + * + * Since: 1.5.0 + **/ +GBytes * +fu_device_dump_firmware(FuDevice *self, FuProgress *progress, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(FU_IS_PROGRESS(progress), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* use the default FuFirmware when only ->dump_firmware is provided */ + if (klass->dump_firmware == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "dumping firmware is not supported by device"); + return NULL; + } + + /* proxy */ + return klass->dump_firmware(self, progress, error); +} + +/** + * fu_device_detach: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Detaches a device from the application into bootloader mode. + * + * Returns: %TRUE on success + * + * Since: 1.0.8 + **/ +gboolean +fu_device_detach(FuDevice *self, GError **error) +{ + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + return fu_device_detach_full(self, progress, error); +} + +/** + * fu_device_detach_full: + * @self: a #FuDevice + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Detaches a device from the application into bootloader mode. + * + * Returns: %TRUE on success + * + * Since: 1.7.0 + **/ +gboolean +fu_device_detach_full(FuDevice *self, FuProgress *progress, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* no plugin-specific method */ + if (klass->detach == NULL) + return TRUE; + + /* call vfunc */ + return klass->detach(self, progress, error); +} + +/** + * fu_device_attach: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Attaches a device from the bootloader into application mode. + * + * Returns: %TRUE on success + * + * Since: 1.0.8 + **/ +gboolean +fu_device_attach(FuDevice *self, GError **error) +{ + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + return fu_device_attach_full(self, progress, error); +} + +/** + * fu_device_attach_full: + * @self: a #FuDevice + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Attaches a device from the bootloader into application mode. + * + * Returns: %TRUE on success + * + * Since: 1.7.0 + **/ +gboolean +fu_device_attach_full(FuDevice *self, FuProgress *progress, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* no plugin-specific method */ + if (klass->attach == NULL) + return TRUE; + + /* call vfunc */ + return klass->attach(self, progress, error); +} + +/** + * fu_device_reload: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Reloads a device that has just gone from bootloader into application mode. + * + * Returns: %TRUE on success + * + * Since: 1.3.3 + **/ +gboolean +fu_device_reload(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); + + /* no plugin-specific method */ + if (klass->reload == NULL) + return TRUE; + + /* call vfunc */ + return klass->reload(self, error); +} + +/** + * fu_device_prepare: + * @self: a #FuDevice + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Prepares a device for update. A different plugin can handle each of + * FuDevice->prepare(), FuDevice->detach() and FuDevice->write_firmware(). + * + * Returns: %TRUE on success + * + * Since: 1.3.3 + **/ +gboolean +fu_device_prepare(FuDevice *self, FuProgress *progress, FwupdInstallFlags flags, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* no plugin-specific method */ + if (klass->prepare == NULL) + return TRUE; + + /* call vfunc */ + return klass->prepare(self, progress, flags, error); +} + +/** + * fu_device_cleanup: + * @self: a #FuDevice + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Cleans up a device after an update. A different plugin can handle each of + * FuDevice->write_firmware(), FuDevice->attach() and FuDevice->cleanup(). + * + * Returns: %TRUE on success + * + * Since: 1.3.3 + **/ +gboolean +fu_device_cleanup(FuDevice *self, FuProgress *progress, FwupdInstallFlags flags, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* no plugin-specific method */ + if (klass->cleanup == NULL) + return TRUE; + + /* call vfunc */ + return klass->cleanup(self, progress, flags, error); +} + +static gboolean +fu_device_open_cb(FuDevice *self, gpointer user_data, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + return klass->open(self, error); +} + +static gboolean +fu_device_open_internal(FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + FuDevicePrivate *priv = GET_PRIVATE(self); + + /* 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 (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_RETRY_OPEN)) { + if (!fu_device_retry_full(self, + fu_device_open_cb, + FU_DEVICE_RETRY_OPEN_COUNT, + FU_DEVICE_RETRY_OPEN_DELAY, + NULL, + error)) + return FALSE; + } else { + if (!klass->open(self, error)) + return FALSE; + } + } + + /* setup */ + if (!fu_device_setup(self, error)) + return FALSE; + + /* ensure the device ID is still valid */ + if (!fu_device_ensure_id(self, error)) + return FALSE; + + /* success */ + fu_device_add_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_IS_OPEN); + return TRUE; +} + +/** + * fu_device_open: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * 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. + * + * If the `->probe()`, `->open()` and `->setup()` actions all complete + * successfully the internal device flag %FU_DEVICE_INTERNAL_FLAG_IS_OPEN will + * be set. + * + * NOTE: It is important to still call fu_device_close() even if this function + * fails as the device may still be partially initialized. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_open(FuDevice *self, GError **error) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* use parent */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN)) { + FuDevice *parent = fu_device_get_parent(self); + if (parent == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent device"); + return FALSE; + } + return fu_device_open_internal(parent, error); + } + return fu_device_open_internal(self, error); +} + +static gboolean +fu_device_close_internal(FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + FuDevicePrivate *priv = GET_PRIVATE(self); + + /* not yet open */ + if (priv->open_refcount == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "cannot close device, refcount already zero"); + return FALSE; + } + if (!g_atomic_int_dec_and_test(&priv->open_refcount)) + return TRUE; + + /* subclassed */ + if (klass->close != NULL) { + if (!klass->close(self, error)) + return FALSE; + } + + /* success */ + fu_device_remove_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_IS_OPEN); + return TRUE; +} + +/** + * fu_device_close: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * 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. + * + * If the close action completed successfully the internal device flag + * %FU_DEVICE_INTERNAL_FLAG_IS_OPEN will be cleared. + * + * Returns: %TRUE for success + * + * Since: 1.1.2 + **/ +gboolean +fu_device_close(FuDevice *self, GError **error) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* use parent */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN)) { + FuDevice *parent = fu_device_get_parent(self); + if (parent == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent device"); + return FALSE; + } + return fu_device_close_internal(parent, error); + } + return fu_device_close_internal(self, error); +} + +/** + * fu_device_probe: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * 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; + + /* device self-assigned */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_NO_PROBE)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing"); + return FALSE; + } + + /* subclassed */ + if (klass->probe != NULL) { + if (!klass->probe(self, error)) + return FALSE; + } + + /* vfunc skipped device */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_NO_PROBE)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing"); + return FALSE; + } + + /* success */ + priv->done_probe = TRUE; + return TRUE; +} + +/** + * fu_device_rescan: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Rescans a device, re-adding GUIDs or flags based on some hardware change. + * + * Returns: %TRUE for success + * + * Since: 1.3.1 + **/ +gboolean +fu_device_rescan(FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* remove all GUIDs */ + g_ptr_array_set_size(fu_device_get_instance_ids(self), 0); + g_ptr_array_set_size(fu_device_get_guids(self), 0); + + /* subclassed */ + if (klass->rescan != NULL) { + if (!klass->rescan(self, error)) { + fu_device_convert_instance_ids(self); + return FALSE; + } + } + + fu_device_convert_instance_ids(self); + return TRUE; +} + +/** + * fu_device_set_progress: + * @self: a #FuDevice + * @progress: a #FuProgress + * + * Sets steps on the progress object used to write firmware. + * + * Since: 1.7.0 + **/ +void +fu_device_set_progress(FuDevice *self, FuProgress *progress) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(FU_IS_PROGRESS(progress)); + + /* subclassed */ + if (klass->set_progress == NULL) + return; + klass->set_progress(self, progress); +} + +/** + * 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** %FU_DEVICE_INTERNAL_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) +{ + GPtrArray *instance_ids; + + /* OEM specific hardware */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_NO_AUTO_INSTANCE_IDS)) + return; + instance_ids = fwupd_device_get_instance_ids(FWUPD_DEVICE(self)); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string(instance_id); + fwupd_device_add_guid(FWUPD_DEVICE(self), guid); + } +} + +/** + * fu_device_setup: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * 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); + GPtrArray *children; + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* should have already been called */ + if (!fu_device_probe(self, error)) + return FALSE; + + /* already done */ + if (priv->done_setup) + return TRUE; + + /* subclassed */ + if (klass->setup != NULL) { + if (!klass->setup(self, error)) + return FALSE; + } + + /* vfunc skipped device */ + if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_NO_PROBE)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing"); + return FALSE; + } + + /* run setup on the children too (unless done already) */ + children = fu_device_get_children(self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child_tmp = g_ptr_array_index(children, i); + if (!fu_device_setup(child_tmp, error)) + return FALSE; + } + + /* convert the instance IDs to GUIDs */ + fu_device_convert_instance_ids(self); + + /* subclassed */ + if (klass->ready != NULL) { + if (!klass->ready(self, error)) + return FALSE; + } + + priv->done_setup = TRUE; + return TRUE; +} + +/** + * fu_device_activate: + * @self: a #FuDevice + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * 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, FuProgress *progress, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* subclassed */ + if (klass->activate != NULL) { + if (!klass->activate(self, progress, 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. + * + * 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_report_metadata_pre: + * @self: a #FuDevice + * + * Collects metadata that would be useful for debugging a failed update report. + * + * Returns: (transfer full) (nullable): a #GHashTable, or %NULL if there is no data + * + * Since: 1.5.0 + **/ +GHashTable * +fu_device_report_metadata_pre(FuDevice *self) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + g_autoptr(GHashTable) metadata = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + + /* not implemented */ + if (klass->report_metadata_pre == NULL) + return NULL; + + /* metadata for all devices */ + metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + klass->report_metadata_pre(self, metadata); + return g_steal_pointer(&metadata); +} + +/** + * fu_device_report_metadata_post: + * @self: a #FuDevice + * + * Collects metadata that would be useful for debugging a failed update report. + * + * Returns: (transfer full) (nullable): a #GHashTable, or %NULL if there is no data + * + * Since: 1.5.0 + **/ +GHashTable * +fu_device_report_metadata_post(FuDevice *self) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + g_autoptr(GHashTable) metadata = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + + /* not implemented */ + if (klass->report_metadata_post == NULL) + return NULL; + + /* metadata for all devices */ + metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + klass->report_metadata_post(self, metadata); + return g_steal_pointer(&metadata); +} + +/** + * fu_device_add_security_attrs: + * @self: a #FuDevice + * @attrs: a security attribute + * + * Adds HSI security attributes. + * + * Since: 1.6.0 + **/ +void +fu_device_add_security_attrs(FuDevice *self, FuSecurityAttrs *attrs) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + + /* optional */ + if (klass->add_security_attrs != NULL) + return klass->add_security_attrs(self, attrs); +} + +/** + * fu_device_bind_driver: + * @self: a #FuDevice + * @subsystem: a subsystem string, e.g. `pci` + * @driver: a kernel module name, e.g. `tg3` + * @error: (nullable): optional return location for an error + * + * Binds a driver to the device, which normally means the kernel driver takes + * control of the hardware. + * + * Returns: %TRUE if driver was bound. + * + * Since: 1.5.0 + **/ +gboolean +fu_device_bind_driver(FuDevice *self, const gchar *subsystem, const gchar *driver, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(subsystem != NULL, FALSE); + g_return_val_if_fail(driver != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not implemented */ + if (klass->bind_driver == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "binding drivers is not supported by device"); + return FALSE; + } + + /* subclass */ + return klass->bind_driver(self, subsystem, driver, error); +} + +/** + * fu_device_unbind_driver: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Unbinds the driver from the device, which normally means the kernel releases + * the hardware so it can be used from userspace. + * + * If there is no driver bound then this function will return with success + * without actually doing anything. + * + * Returns: %TRUE if driver was unbound. + * + * Since: 1.5.0 + **/ +gboolean +fu_device_unbind_driver(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); + + /* not implemented */ + if (klass->unbind_driver == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unbinding drivers is not supported by device"); + return FALSE; + } + + /* subclass */ + return klass->unbind_driver(self, error); +} + +static const gchar * +fu_device_instance_lookup(FuDevice *self, const gchar *key) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + return g_hash_table_lookup(priv->instance_hash, key); +} + +/** + * 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 *instance_ids = fu_device_get_instance_ids(donor); + GPtrArray *parent_guids = fu_device_get_parent_guids(donor); + GPtrArray *parent_physical_ids = fu_device_get_parent_physical_ids(donor); + GHashTableIter iter; + gpointer key, value; + + 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->physical_id == NULL && priv_donor->physical_id != NULL) + fu_device_set_physical_id(self, priv_donor->physical_id); + if (priv->logical_id == NULL && priv_donor->logical_id != NULL) + fu_device_set_logical_id(self, priv_donor->logical_id); + if (priv->backend_id == NULL && priv_donor->backend_id != NULL) + fu_device_set_backend_id(self, priv_donor->backend_id); + if (priv->update_request_id == NULL && priv_donor->update_request_id != NULL) + fu_device_set_update_request_id(self, priv_donor->update_request_id); + if (priv->proxy == NULL && priv_donor->proxy != NULL) + fu_device_set_proxy(self, priv_donor->proxy); + if (priv->proxy_guid == NULL && priv_donor->proxy_guid != NULL) + fu_device_set_proxy_guid(self, priv_donor->proxy_guid); + if (priv->custom_flags == NULL && priv_donor->custom_flags != NULL) + fu_device_set_custom_flags(self, priv_donor->custom_flags); + if (priv->ctx == NULL) + fu_device_set_context(self, fu_device_get_context(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); + if (parent_physical_ids != NULL) { + for (guint i = 0; i < parent_physical_ids->len; i++) { + const gchar *tmp = g_ptr_array_index(parent_physical_ids, i); + fu_device_add_parent_physical_id(self, tmp); + } + } + g_rw_lock_reader_lock(&priv_donor->metadata_mutex); + if (priv->metadata != NULL) { + g_hash_table_iter_init(&iter, priv_donor->metadata); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (fu_device_get_metadata(self, key) == NULL) + fu_device_set_metadata(self, key, value); + } + } + g_rw_lock_reader_unlock(&priv_donor->metadata_mutex); + + /* probably not required, but seems safer */ + for (guint i = 0; i < priv_donor->possible_plugins->len; i++) { + const gchar *possible_plugin = g_ptr_array_index(priv_donor->possible_plugins, i); + fu_device_add_possible_plugin(self, possible_plugin); + } + + /* copy all instance ID keys if not already set */ + g_hash_table_iter_init(&iter, priv_donor->instance_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (fu_device_instance_lookup(self, key) == NULL) + fu_device_add_instance_str(self, key, value); + } + + /* now the base class, where all the interesting bits are */ + fwupd_device_incorporate(FWUPD_DEVICE(self), FWUPD_DEVICE(donor)); + + /* set by the superclass */ + if (fu_device_get_id(self) != NULL) + priv->device_id_valid = TRUE; + + /* optional subclass */ + if (klass->incorporate != NULL) + klass->incorporate(self, donor); + + /* call the set_quirk_kv() vfunc for the superclassed object */ + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string(instance_id); + fu_device_add_guid_quirks(self, guid); + } +} + +/** + * fu_device_incorporate_flag: + * @self: a #FuDevice + * @donor: another device + * @flag: device flags + * + * Copy the value of a specific flag from the donor object. + * + * Since: 1.3.5 + **/ +void +fu_device_incorporate_flag(FuDevice *self, FuDevice *donor, FwupdDeviceFlags flag) +{ + if (fu_device_has_flag(donor, flag) && !fu_device_has_flag(self, flag)) { + g_debug("donor set %s", fwupd_device_flag_to_string(flag)); + fu_device_add_flag(self, flag); + } else if (!fu_device_has_flag(donor, flag) && fu_device_has_flag(self, flag)) { + g_debug("donor unset %s", fwupd_device_flag_to_string(flag)); + fu_device_remove_flag(self, flag); + } +} + +/** + * fu_device_incorporate_from_component: (skip): + * @device: a device + * @component: a Xmlb node + * + * 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); + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateImage']", NULL); + if (tmp != NULL) + fwupd_device_set_update_image(FWUPD_DEVICE(self), tmp); +} + +/** + * fu_device_emit_request: + * @self: a device + * @request: a request + * + * Emit a request from a plugin to the client. + * + * Since: 1.6.2 + **/ +void +fu_device_emit_request(FuDevice *self, FwupdRequest *request) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(FWUPD_IS_REQUEST(request)); + + /* sanity check */ + if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_UNKNOWN) { + g_critical("a request must have an assigned kind"); + return; + } + if (fwupd_request_get_id(request) == NULL) { + g_critical("a request must have an assigned ID"); + return; + } + if (fwupd_request_get_kind(request) >= FWUPD_REQUEST_KIND_LAST) { + g_critical("invalid request kind"); + return; + } + + /* ensure set */ + fwupd_request_set_device_id(request, fu_device_get_id(self)); + + /* for compatibility with older clients */ + if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_POST) { + fu_device_set_update_message(self, fwupd_request_get_message(request)); + fu_device_set_update_image(self, fwupd_request_get_image(request)); + } + + /* proxy to the engine */ + g_signal_emit(self, signals[SIGNAL_REQUEST], 0, request); + priv->request_cnts[fwupd_request_get_kind(request)]++; +} + +static void +fu_device_flags_notify_cb(FuDevice *self, GParamSpec *pspec, gpointer user_data) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + /* we only inhibit when the flags contains UPDATABLE, and that might be discovered by + * probing the hardware *after* the battery level has been set */ + if (priv->inhibits != NULL) + fu_device_ensure_inhibits(self); +} + +/** + * fu_device_add_instance_str: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: (nullable): value + * + * Assign a value for the @key. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_str(FuDevice *self, const gchar *key, const gchar *value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup(value)); +} + +static gboolean +fu_strsafe_instance_id_is_valid_char(gchar c) +{ + if (c == ' ') + return FALSE; + if (c == '_') + return FALSE; + if (c == '&') + return FALSE; + if (c == '/') + return FALSE; + if (c == '\\') + return FALSE; + return g_ascii_isprint(c); +} + +/* NOTE: we can't use fu_strsafe as this behavior is now effectively ABI */ +static gchar * +fu_common_instance_id_strsafe(const gchar *str) +{ + g_autoptr(GString) tmp = g_string_new(NULL); + gboolean has_content = FALSE; + + /* sanity check */ + if (str == NULL) + return NULL; + + /* use - to replace problematic chars -- but only once per section */ + for (guint i = 0; str[i] != '\0'; i++) { + gchar c = str[i]; + if (!fu_strsafe_instance_id_is_valid_char(c)) { + if (has_content) { + g_string_append_c(tmp, '-'); + has_content = FALSE; + } + } else { + g_string_append_c(tmp, c); + has_content = TRUE; + } + } + + /* remove any trailing replacements */ + if (tmp->len > 0 && tmp->str[tmp->len - 1] == '-') + g_string_truncate(tmp, tmp->len - 1); + + /* nothing left! */ + if (tmp->len == 0) + return NULL; + + /* success */ + return g_string_free(g_steal_pointer(&tmp), FALSE); +} + +/** + * fu_device_add_instance_strsafe: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: (nullable): value + * + * Assign a sanitized value for the @key. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_strsafe(FuDevice *self, const gchar *key, const gchar *value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, + g_strdup(key), + fu_common_instance_id_strsafe(value)); +} + +/** + * fu_device_add_instance_strup: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: (nullable): value + * + * Assign a uppercase value for the @key. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_strup(FuDevice *self, const gchar *key, const gchar *value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, + g_strdup(key), + value != NULL ? g_utf8_strup(value, -1) : NULL); +} + +/** + * fu_device_add_instance_u4: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: value + * + * Assign a value to the @key, which is padded as %1X. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_u4(FuDevice *self, const gchar *key, guint8 value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%01X", value)); +} + +/** + * fu_device_add_instance_u8: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: value + * + * Assign a value to the @key, which is padded as %2X. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_u8(FuDevice *self, const gchar *key, guint8 value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%02X", value)); +} + +/** + * fu_device_add_instance_u16: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: value + * + * Assign a value to the @key, which is padded as %4X. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_u16(FuDevice *self, const gchar *key, guint16 value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%04X", value)); +} + +/** + * fu_device_add_instance_u32: + * @self: a #FuDevice + * @key: (not nullable): string + * @value: value + * + * Assign a value to the @key, which is padded as %8X. + * + * Since: 1.7.7 + **/ +void +fu_device_add_instance_u32(FuDevice *self, const gchar *key, guint32 value) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%08X", value)); +} + +/** + * fu_device_build_instance_id: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * @subsystem: (not nullable): subsystem, e.g. `NVME` + * @...: pairs of string key values, ending with %NULL + * + * Creates an instance ID from a prefix and some key values. + * If the key value cannot be found, the parent and then proxy is also queried. + * + * If any of the key values remain unset then no instance ID is added. + * + * fu_device_add_instance_str(dev, "VID", "1234"); + * fu_device_add_instance_u16(dev, "PID", 5678); + * if (!fu_device_build_instance_id(dev, &error, "NVME", "VID", "PID", NULL)) + * g_warning("failed to add ID: %s", error->message); + * + * Returns: %TRUE if the instance ID was added. + * + * Since: 1.7.7 + **/ +gboolean +fu_device_build_instance_id(FuDevice *self, GError **error, const gchar *subsystem, ...) +{ + FuDevice *parent = fu_device_get_parent(self); + FuDevicePrivate *priv = GET_PRIVATE(self); + gboolean ret = TRUE; + va_list args; + g_autoptr(GString) str = g_string_new(subsystem); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(subsystem != NULL, FALSE); + + va_start(args, subsystem); + for (guint i = 0;; i++) { + const gchar *key = va_arg(args, const gchar *); + const gchar *value; + if (key == NULL) + break; + value = fu_device_instance_lookup(self, key); + if (value == NULL && parent != NULL) + value = fu_device_instance_lookup(parent, key); + if (value == NULL && priv->proxy != NULL) + value = fu_device_instance_lookup(priv->proxy, key); + if (value == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no value for %s", + key); + ret = FALSE; + break; + } + g_string_append(str, i == 0 ? "\\" : "&"); + g_string_append_printf(str, "%s_%s", key, value); + } + va_end(args); + + /* we set an error above */ + if (!ret) + return FALSE; + + /* success */ + fu_device_add_instance_id(self, str->str); + return TRUE; +} + +/** + * fu_device_build_instance_id_quirk: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * @subsystem: (not nullable): subsystem, e.g. `NVME` + * @...: pairs of string key values, ending with %NULL + * + * Creates an quirk-only instance ID from a prefix and some key values. If any of the key values + * are unset then no instance ID is added. + * + * Returns: %TRUE if the instance ID was added. + * + * Since: 1.7.7 + **/ +gboolean +fu_device_build_instance_id_quirk(FuDevice *self, GError **error, const gchar *subsystem, ...) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + gboolean ret = TRUE; + va_list args; + g_autoptr(GString) str = g_string_new(subsystem); + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(subsystem != NULL, FALSE); + + va_start(args, subsystem); + for (guint i = 0;; i++) { + const gchar *key = va_arg(args, const gchar *); + const gchar *value; + if (key == NULL) + break; + value = g_hash_table_lookup(priv->instance_hash, key); + if (value == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no value for %s", + key); + ret = FALSE; + break; + } + g_string_append(str, i == 0 ? "\\" : "&"); + g_string_append_printf(str, "%s_%s", key, value); + } + va_end(args); + + /* we set an error above */ + if (!ret) + return FALSE; + + /* success */ + fu_device_add_instance_id_full(self, str->str, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + return TRUE; +} + +/** + * fu_device_security_attr_new: + * @self: a #FuDevice + * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Creates a new #FwupdSecurityAttr for this specific device. + * + * Returns: (transfer full): a #FwupdSecurityAttr + * + * Since: 1.8.4 + **/ +FwupdSecurityAttr * +fu_device_security_attr_new(FuDevice *self, const gchar *appstream_id) +{ + FuDevicePrivate *priv = fu_device_get_instance_private(self); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(appstream_id != NULL, NULL); + + attr = fu_security_attr_new(priv->ctx, appstream_id); + fwupd_security_attr_set_plugin(attr, fu_device_get_plugin(FU_DEVICE(self))); + fwupd_security_attr_add_guids(attr, fu_device_get_guids(FU_DEVICE(self))); + return g_steal_pointer(&attr); +} + +static void +fu_device_class_init(FuDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + object_class->finalize = fu_device_finalize; + object_class->get_property = fu_device_get_property; + object_class->set_property = fu_device_set_property; + + /** + * FuDevice::child-added: + * @self: the #FuDevice instance that emitted the signal + * @device: the #FuDevice child + * + * The ::child-added signal is emitted when a device has been added as a child. + * + * Since: 1.0.8 + **/ + signals[SIGNAL_CHILD_ADDED] = g_signal_new("child-added", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuDeviceClass, child_added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuDevice::child-removed: + * @self: the #FuDevice instance that emitted the signal + * @device: the #FuDevice child + * + * The ::child-removed signal is emitted when a device has been removed as a child. + * + * Since: 1.0.8 + **/ + signals[SIGNAL_CHILD_REMOVED] = g_signal_new("child-removed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuDeviceClass, child_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuDevice::request: + * @self: the #FuDevice instance that emitted the signal + * @request: the #FwupdRequest + * + * The ::request signal is emitted when the device needs interactive action from the user. + * + * Since: 1.6.2 + **/ + signals[SIGNAL_REQUEST] = g_signal_new("request", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuDeviceClass, request), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FWUPD_TYPE_REQUEST); + + /** + * FuDevice:physical-id: + * + * The device physical ID. + * + * Since: 1.1.2 + */ + 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); + + /** + * FuDevice:logical-id: + * + * The device logical ID. + * + * Since: 1.1.2 + */ + 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); + + /** + * FuDevice:backend-id: + * + * The device backend ID. + * + * Since: 1.5.8 + */ + pspec = g_param_spec_string("backend-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BACKEND_ID, pspec); + + /** + * FuDevice:context: + * + * The #FuContext to use. + * + * Since: 1.6.0 + */ + pspec = g_param_spec_object("context", + NULL, + NULL, + FU_TYPE_CONTEXT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_CONTEXT, pspec); + + /** + * FuDevice:proxy: + * + * The device proxy to use. + * + * Since: 1.4.1 + */ + pspec = g_param_spec_object("proxy", + NULL, + NULL, + FU_TYPE_DEVICE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PROXY, pspec); + + /** + * FuDevice:parent: + * + * The device parent. + * + * Since: 1.0.8 + */ + pspec = g_param_spec_object("parent", + NULL, + NULL, + FU_TYPE_DEVICE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PARENT, pspec); + + /** + * FuDevice:backend-tags: + * + * The device tags used for backend identification. + * + * Since: 1.5.8 + */ + pspec = g_param_spec_boxed("backend-tags", + NULL, + NULL, + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BACKEND_TAGS, pspec); +} + +static void +fu_device_init(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + priv->order = G_MAXINT; + priv->parent_guids = g_ptr_array_new_with_free_func(g_free); + priv->possible_plugins = g_ptr_array_new_with_free_func(g_free); + priv->retry_recs = g_ptr_array_new_with_free_func(g_free); + priv->instance_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + priv->backend_tags = g_ptr_array_new_with_free_func(g_free); + priv->acquiesce_delay = 50; /* ms */ + g_rw_lock_init(&priv->parent_guids_mutex); + g_rw_lock_init(&priv->metadata_mutex); + priv->notify_flags_handler_id = g_signal_connect(FWUPD_DEVICE(self), + "notify::flags", + G_CALLBACK(fu_device_flags_notify_cb), + NULL); +} + +static void +fu_device_finalize(GObject *object) +{ + FuDevice *self = FU_DEVICE(object); + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_rw_lock_clear(&priv->metadata_mutex); + g_rw_lock_clear(&priv->parent_guids_mutex); + + if (priv->alternate != NULL) + g_object_unref(priv->alternate); + if (priv->proxy != NULL) + g_object_remove_weak_pointer(G_OBJECT(priv->proxy), (gpointer *)&priv->proxy); + if (priv->ctx != NULL) + g_object_unref(priv->ctx); + if (priv->poll_id != 0) + g_source_remove(priv->poll_id); + if (priv->metadata != NULL) + g_hash_table_unref(priv->metadata); + if (priv->inhibits != NULL) + g_hash_table_unref(priv->inhibits); + if (priv->parent_physical_ids != NULL) + g_ptr_array_unref(priv->parent_physical_ids); + if (priv->private_flag_items != NULL) + g_ptr_array_unref(priv->private_flag_items); + g_ptr_array_unref(priv->parent_guids); + g_ptr_array_unref(priv->possible_plugins); + g_ptr_array_unref(priv->retry_recs); + g_ptr_array_unref(priv->backend_tags); + g_free(priv->alternate_id); + g_free(priv->equivalent_id); + g_free(priv->physical_id); + g_free(priv->logical_id); + g_free(priv->backend_id); + g_free(priv->update_request_id); + g_free(priv->proxy_guid); + g_free(priv->custom_flags); + g_hash_table_unref(priv->instance_hash); + + G_OBJECT_CLASS(fu_device_parent_class)->finalize(object); +} + +/** + * fu_device_new: + * + * Creates a new #Fudevice + * + * Since: 1.8.2 + **/ +FuDevice * +fu_device_new(FuContext *ctx) +{ + FuDevice *self = g_object_new(FU_TYPE_DEVICE, "context", ctx, NULL); + return FU_DEVICE(self); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-device.h b/fwupd-1.8.6/libfwupdplugin/fu-device.h new file mode 100644 index 0000000000000000000000000000000000000000..845a055c833ab1d22b235c4c0d7c3732903ece69 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-device.h @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-context.h" +#include "fu-device-locker.h" +#include "fu-firmware.h" +#include "fu-progress.h" +#include "fu-security-attrs.h" +#include "fu-version-common.h" + +#define FU_TYPE_DEVICE (fu_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDevice, fu_device, FU, DEVICE, FwupdDevice) + +struct _FuDeviceClass { + FwupdDeviceClass parent_class; +#ifndef __GI_SCANNER__ + void (*to_string)(FuDevice *self, guint indent, GString *str); + gboolean (*write_firmware)(FuDevice *self, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + FuFirmware *(*read_firmware)(FuDevice *self, + FuProgress *progress, + GError **error)G_GNUC_WARN_UNUSED_RESULT; + gboolean (*detach)(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*attach)(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*open)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*close)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*probe)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*rescan)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + FuFirmware *(*prepare_firmware)(FuDevice *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error)G_GNUC_WARN_UNUSED_RESULT; + gboolean (*set_quirk_kv)(FuDevice *self, + const gchar *key, + const gchar *value, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*setup)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + void (*incorporate)(FuDevice *self, FuDevice *donor); + gboolean (*poll)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*activate)(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*reload)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*prepare)(FuDevice *self, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*cleanup)(FuDevice *self, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + void (*report_metadata_pre)(FuDevice *self, GHashTable *metadata); + void (*report_metadata_post)(FuDevice *self, GHashTable *metadata); + gboolean (*bind_driver)(FuDevice *self, + const gchar *subsystem, + const gchar *driver, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*unbind_driver)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + GBytes *(*dump_firmware)(FuDevice *self, + FuProgress *progress, + GError **error)G_GNUC_WARN_UNUSED_RESULT; + void (*add_security_attrs)(FuDevice *self, FuSecurityAttrs *attrs); + gboolean (*ready)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + void (*child_added)(FuDevice *self, /* signal */ + FuDevice *child); + void (*child_removed)(FuDevice *self, /* signal */ + FuDevice *child); + void (*request)(FuDevice *self, /* signal */ + FwupdRequest *request); + gboolean (*get_results)(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; + void (*set_progress)(FuDevice *self, FuProgress *progress); +#endif +}; + +/** + * FuDeviceInstanceFlags: + * @FU_DEVICE_INSTANCE_FLAG_NONE: No flags set + * @FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS: Only use instance ID for quirk matching + * @FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS: Do no 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, + FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS = 1 << 1, + /*< private >*/ + FU_DEVICE_INSTANCE_FLAG_LAST +} FuDeviceInstanceFlags; + +/** + * FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE: + * + * The default removal delay for device re-enumeration taking into account a + * chain of slow USB hubs. This should be used when the device is able to + * reset itself between bootloader->runtime->bootloader. + */ +#define FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE 10000 /* ms */ + +/** + * FU_DEVICE_REMOVE_DELAY_USER_REPLUG: + * + * The default removal delay for device re-plug taking into account humans + * 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 40000 /* ms */ + +/** + * FuDeviceRetryFunc: + * @self: a #FuDevice + * @user_data: user data + * @error: (nullable): optional return location for an error + * + * The device retry iteration callback. + * + * Returns: %TRUE on success + */ +typedef gboolean (*FuDeviceRetryFunc)(FuDevice *self, + gpointer user_data, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +FuDevice * +fu_device_new(FuContext *ctx); + +/* helpful casting macros */ +#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_has_vendor_id(d, v) fwupd_device_has_vendor_id(FWUPD_DEVICE(d), v) +#define fu_device_has_protocol(d, v) fwupd_device_has_protocol(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_has_icon(d, v) fwupd_device_has_icon(FWUPD_DEVICE(d), v) +#define fu_device_add_issue(d, v) fwupd_device_add_issue(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_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_branch(d, v) fwupd_device_set_branch(FWUPD_DEVICE(d), v) +#define fu_device_set_update_message(d, v) fwupd_device_set_update_message(FWUPD_DEVICE(d), v) +#define fu_device_set_update_image(d, v) fwupd_device_set_update_image(FWUPD_DEVICE(d), v) +#define fu_device_set_update_error(d, v) fwupd_device_set_update_error(FWUPD_DEVICE(d), v) +#define fu_device_add_vendor_id(d, v) fwupd_device_add_vendor_id(FWUPD_DEVICE(d), v) +#define fu_device_add_protocol(d, v) fwupd_device_add_protocol(FWUPD_DEVICE(d), v) +#define fu_device_set_version_raw(d, v) fwupd_device_set_version_raw(FWUPD_DEVICE(d), v) +#define fu_device_set_version_lowest_raw(d, v) \ + fwupd_device_set_version_lowest_raw(FWUPD_DEVICE(d), v) +#define fu_device_set_version_bootloader_raw(d, v) \ + fwupd_device_set_version_bootloader_raw(FWUPD_DEVICE(d), v) +#define fu_device_set_version_build_date(d, v) \ + fwupd_device_set_version_build_date(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)) +#define fu_device_get_modified(d) fwupd_device_get_modified(FWUPD_DEVICE(d)) +#define fu_device_get_guids(d) fwupd_device_get_guids(FWUPD_DEVICE(d)) +#define fu_device_get_guid_default(d) fwupd_device_get_guid_default(FWUPD_DEVICE(d)) +#define fu_device_get_instance_ids(d) fwupd_device_get_instance_ids(FWUPD_DEVICE(d)) +#define fu_device_get_icons(d) fwupd_device_get_icons(FWUPD_DEVICE(d)) +#define fu_device_get_issues(d) fwupd_device_get_issues(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_branch(d) fwupd_device_get_branch(FWUPD_DEVICE(d)) +#define fu_device_get_id(d) fwupd_device_get_id(FWUPD_DEVICE(d)) +#define fu_device_get_composite_id(d) fwupd_device_get_composite_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)) +#define fu_device_get_update_state(d) fwupd_device_get_update_state(FWUPD_DEVICE(d)) +#define fu_device_get_update_message(d) fwupd_device_get_update_message(FWUPD_DEVICE(d)) +#define fu_device_get_update_image(d) fwupd_device_get_update_image(FWUPD_DEVICE(d)) +#define fu_device_get_vendor(d) fwupd_device_get_vendor(FWUPD_DEVICE(d)) +#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_version_raw(d) fwupd_device_get_version_raw(FWUPD_DEVICE(d)) +#define fu_device_get_version_lowest_raw(d) fwupd_device_get_version_lowest_raw(FWUPD_DEVICE(d)) +#define fu_device_get_version_bootloader_raw(d) \ + fwupd_device_get_version_bootloader_raw(FWUPD_DEVICE(d)) +#define fu_device_get_version_build_date(d) fwupd_device_get_version_build_date(FWUPD_DEVICE(d)) +#define fu_device_get_vendor_ids(d) fwupd_device_get_vendor_ids(FWUPD_DEVICE(d)) +#define fu_device_get_protocols(d) fwupd_device_get_protocols(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)) + +/** + * FuDeviceInternalFlags: + * + * The device internal flags. + **/ +typedef guint64 FuDeviceInternalFlags; + +/** + * FU_DEVICE_INTERNAL_FLAG_NONE: + * + * No flags set. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_NONE (0) + +/** + * FU_DEVICE_INTERNAL_FLAG_UNKNOWN: + * + * Unknown flag value. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_UNKNOWN G_MAXUINT64 + +/** + * FU_DEVICE_INTERNAL_FLAG_NO_AUTO_INSTANCE_IDS: + * + * Do not add instance IDs from the device baseclass. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_NO_AUTO_INSTANCE_IDS (1ull << 0) + +/** + * FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER: + * + * Ensure the version is a valid semantic version, e.g. numbers separated with dots. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER (1ull << 1) + +/** + * FU_DEVICE_INTERNAL_FLAG_ONLY_SUPPORTED: + * + * Only devices supported in the metadata will be opened + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_ONLY_SUPPORTED (1ull << 2) + +/** + * FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME: + * + * Set the device name from the metadata `name` if available. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME (1ull << 3) + +/** + * FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY: + * + * Set the device name from the metadata `category` if available. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY (1ull << 4) + +/** + * FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT: + * + * Set the device version format from the metadata if available. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT (1ull << 5) + +/** + * FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON: + * + * Set the device icon from the metadata if available. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON (1ull << 6) + +/** + * FU_DEVICE_INTERNAL_FLAG_RETRY_OPEN: + * + * Retry the device open up to 5 times if it fails. + * + * Since: 1.5.5 + */ +#define FU_DEVICE_INTERNAL_FLAG_RETRY_OPEN (1ull << 7) + +/** + * FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID: + * + * Match GUIDs on device replug where the physical and logical IDs will be different. + * + * Since: 1.5.8 + */ +#define FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID (1ull << 8) + +/** + * FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION: + * + * Inherit activation status from the history database on startup. + * + * Since: 1.5.9 + */ +#define FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION (1ull << 9) + +/** + * FU_DEVICE_INTERNAL_FLAG_IS_OPEN: + * + * The device opened successfully and ready to use. + * + * Since: 1.6.1 + */ +#define FU_DEVICE_INTERNAL_FLAG_IS_OPEN (1ull << 10) + +/** + * FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER: + * + * Do not attempt to read the device serial number. + * + * Since: 1.6.2 + */ +#define FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER (1ull << 11) + +/** + * FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN: + * + * Automatically assign the parent for children of this device. + * + * Since: 1.6.2 + */ +#define FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN (1ull << 12) + +/** + * FU_DEVICE_INTERNAL_FLAG_ATTACH_EXTRA_RESET: + * + * Device needs resetting twice for attach after the firmware update. + * + * Since: 1.6.2 + */ +#define FU_DEVICE_INTERNAL_FLAG_ATTACH_EXTRA_RESET (1ull << 13) + +/** + * FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN: + * + * Children of the device are inhibited by the parent. + * + * Since: 1.6.2 + */ +#define FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN (1ull << 14) + +/** + * FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN: + * + * Do not auto-remove clildren in the device list. + * + * Since: 1.6.2 + */ +#define FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN (1ull << 15) + +/** + * FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN: + * + * Use parent to open and close the device. + * + * Since: 1.6.2 + */ +#define FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN (1ull << 16) + +/** + * FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY: + * + * Use parent for the battery level and threshold. + * + * Since: 1.6.3 + */ +#define FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY (1ull << 17) + +/** + * FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK: + * + * Use parent for the battery level and threshold. + * + * Since: 1.6.4 + */ +#define FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK (1ull << 18) + +/** + * FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE: + * + * The device is not auto removed. + * + * Since 1.7.3 + */ +#define FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE (1llu << 19) + +/** + * FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR: + * + * Set the device vendor from the metadata `developer_name` if available. + * + * Since: 1.7.4 + */ +#define FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR (1ull << 20) + +/** + * FU_DEVICE_INTERNAL_FLAG_NO_LID_CLOSED: + * + * Do not allow updating when the laptop lid is closed. + * + * Since: 1.7.4 + */ +#define FU_DEVICE_INTERNAL_FLAG_NO_LID_CLOSED (1ull << 21) + +/** + * FU_DEVICE_INTERNAL_FLAG_NO_PROBE: + * + * Do not probe this device. + * + * Since: 1.7.6 + */ +#define FU_DEVICE_INTERNAL_FLAG_NO_PROBE (1ull << 22) + +/** + * FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED: + * + * Set the signed/unsigned payload from the metadata if available. + * + * Since: 1.7.6 + */ +#define FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED (1ull << 23) + +/** + * FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING: + * + * Pause polling when reading or writing to the device + * + * Since: 1.8.1 + */ +#define FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING (1ull << 24) + +/** + * FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG: + * + * Only use the device removal delay when explicitly waiting for a replug, rather than every time + * the device is removed. + * + * Since: 1.8.1 + */ +#define FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG (1ull << 25) + +/* accessors */ +gchar * +fu_device_to_string(FuDevice *self); +void +fu_device_add_string(FuDevice *self, guint idt, GString *str); +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 *self, const gchar *guid); +void +fu_device_add_guid_full(FuDevice *self, const gchar *guid, FuDeviceInstanceFlags flags); +gboolean +fu_device_has_guid(FuDevice *self, const gchar *guid); +void +fu_device_add_instance_id(FuDevice *self, const gchar *instance_id); +void +fu_device_add_instance_id_full(FuDevice *self, + const gchar *instance_id, + FuDeviceInstanceFlags flags); +FuDevice * +fu_device_get_alternate(FuDevice *self); +FuDevice * +fu_device_get_root(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_remove_child(FuDevice *self, FuDevice *child); +void +fu_device_add_parent_guid(FuDevice *self, const gchar *guid); +void +fu_device_add_parent_physical_id(FuDevice *self, const gchar *physical_id); +void +fu_device_add_counterpart_guid(FuDevice *self, const gchar *guid); +FuDevice * +fu_device_get_proxy(FuDevice *self); +void +fu_device_set_proxy(FuDevice *self, FuDevice *proxy); +FuDevice * +fu_device_get_proxy_with_fallback(FuDevice *self); +const gchar * +fu_device_get_metadata(FuDevice *self, const gchar *key); +gboolean +fu_device_get_metadata_boolean(FuDevice *self, const gchar *key); +guint +fu_device_get_metadata_integer(FuDevice *self, const gchar *key); +void +fu_device_remove_metadata(FuDevice *self, const gchar *key); +void +fu_device_set_metadata(FuDevice *self, const gchar *key, const gchar *value); +void +fu_device_set_metadata_boolean(FuDevice *self, const gchar *key, gboolean value); +void +fu_device_set_metadata_integer(FuDevice *self, const gchar *key, guint value); +void +fu_device_set_id(FuDevice *self, const gchar *id); +void +fu_device_set_version_format(FuDevice *self, FwupdVersionFormat fmt); +void +fu_device_set_version(FuDevice *self, const gchar *version); +void +fu_device_set_version_lowest(FuDevice *self, const gchar *version); +void +fu_device_set_version_bootloader(FuDevice *self, const gchar *version); +void +fu_device_add_backend_tag(FuDevice *self, const gchar *backend_tag); +gboolean +fu_device_has_backend_tag(FuDevice *self, const gchar *backend_tag); +GPtrArray * +fu_device_get_backend_tags(FuDevice *self); +void +fu_device_inhibit(FuDevice *self, const gchar *inhibit_id, const gchar *reason); +void +fu_device_uninhibit(FuDevice *self, const gchar *inhibit_id); +void +fu_device_add_problem(FuDevice *self, FwupdDeviceProblem problem); +void +fu_device_remove_problem(FuDevice *self, FwupdDeviceProblem problem); +gboolean +fu_device_has_inhibit(FuDevice *self, const gchar *inhibit_id); +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_backend_id(FuDevice *self); +void +fu_device_set_backend_id(FuDevice *self, const gchar *backend_id); +const gchar * +fu_device_get_proxy_guid(FuDevice *self); +void +fu_device_set_proxy_guid(FuDevice *self, const gchar *proxy_guid); +guint +fu_device_get_priority(FuDevice *self); +void +fu_device_set_priority(FuDevice *self, guint priority); +void +fu_device_add_flag(FuDevice *self, FwupdDeviceFlags flag); +void +fu_device_remove_flag(FuDevice *self, FwupdDeviceFlags flag); +const gchar * +fu_device_get_custom_flags(FuDevice *self); +void +fu_device_set_custom_flags(FuDevice *self, const gchar *custom_flags); +void +fu_device_set_name(FuDevice *self, const gchar *value); +void +fu_device_set_vendor(FuDevice *self, const gchar *vendor); +guint +fu_device_get_remove_delay(FuDevice *self); +void +fu_device_set_remove_delay(FuDevice *self, guint remove_delay); +guint +fu_device_get_acquiesce_delay(FuDevice *self); +void +fu_device_set_acquiesce_delay(FuDevice *self, guint acquiesce_delay); +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_battery_level(FuDevice *self); +void +fu_device_set_battery_level(FuDevice *self, guint battery_level); +guint +fu_device_get_battery_threshold(FuDevice *self); +void +fu_device_set_battery_threshold(FuDevice *self, guint battery_threshold); +void +fu_device_set_update_state(FuDevice *self, FwupdUpdateState update_state); +void +fu_device_set_context(FuDevice *self, FuContext *ctx); +FuContext * +fu_device_get_context(FuDevice *self); +FwupdRelease * +fu_device_get_release_default(FuDevice *self); +GType +fu_device_get_specialized_gtype(FuDevice *self); +GType +fu_device_get_firmware_gtype(FuDevice *self); +void +fu_device_set_firmware_gtype(FuDevice *self, GType firmware_gtype); +void +fu_device_add_internal_flag(FuDevice *self, FuDeviceInternalFlags flag); +void +fu_device_remove_internal_flag(FuDevice *self, FuDeviceInternalFlags flag); +gboolean +fu_device_has_internal_flag(FuDevice *self, FuDeviceInternalFlags flag); +gboolean +fu_device_get_results(FuDevice *self, GError **error); +gboolean +fu_device_write_firmware(FuDevice *self, + GBytes *fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +FuFirmware * +fu_device_prepare_firmware(FuDevice *self, GBytes *fw, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +FuFirmware * +fu_device_read_firmware(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_device_dump_firmware(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_attach(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_detach(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_attach_full(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_detach_full(FuDevice *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_reload(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_prepare(FuDevice *self, FuProgress *progress, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_cleanup(FuDevice *self, FuProgress *progress, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +void +fu_device_incorporate(FuDevice *self, FuDevice *donor); +void +fu_device_incorporate_flag(FuDevice *self, FuDevice *donor, FwupdDeviceFlags flag); +gboolean +fu_device_open(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_close(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_probe(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_setup(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_rescan(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_activate(FuDevice *self, FuProgress *progress, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_device_probe_invalidate(FuDevice *self); +gboolean +fu_device_poll(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_device_set_poll_interval(FuDevice *self, guint interval); +void +fu_device_retry_set_delay(FuDevice *self, guint delay); +void +fu_device_retry_add_recovery(FuDevice *self, GQuark domain, gint code, FuDeviceRetryFunc func); +gboolean +fu_device_retry(FuDevice *self, + FuDeviceRetryFunc func, + guint count, + gpointer user_data, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_retry_full(FuDevice *self, + FuDeviceRetryFunc func, + guint count, + guint delay, + gpointer user_data, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_bind_driver(FuDevice *self, const gchar *subsystem, const gchar *driver, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_device_unbind_driver(FuDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GHashTable * +fu_device_report_metadata_pre(FuDevice *self); +GHashTable * +fu_device_report_metadata_post(FuDevice *self); +void +fu_device_add_security_attrs(FuDevice *self, FuSecurityAttrs *attrs); +void +fu_device_register_private_flag(FuDevice *self, guint64 value, const gchar *value_str); +void +fu_device_add_private_flag(FuDevice *self, guint64 flag); +void +fu_device_remove_private_flag(FuDevice *self, guint64 flag); +gboolean +fu_device_has_private_flag(FuDevice *self, guint64 flag); +void +fu_device_emit_request(FuDevice *self, FwupdRequest *request); +FwupdSecurityAttr * +fu_device_security_attr_new(FuDevice *self, const gchar *appstream_id); + +void +fu_device_add_instance_str(FuDevice *self, const gchar *key, const gchar *value); +void +fu_device_add_instance_strsafe(FuDevice *self, const gchar *key, const gchar *value); +void +fu_device_add_instance_strup(FuDevice *self, const gchar *key, const gchar *value); +void +fu_device_add_instance_u4(FuDevice *self, const gchar *key, guint8 value); +void +fu_device_add_instance_u8(FuDevice *self, const gchar *key, guint8 value); +void +fu_device_add_instance_u16(FuDevice *self, const gchar *key, guint16 value); +void +fu_device_add_instance_u32(FuDevice *self, const gchar *key, guint32 value); +gboolean +fu_device_build_instance_id(FuDevice *self, GError **error, const gchar *subsystem, ...); +gboolean +fu_device_build_instance_id_quirk(FuDevice *self, GError **error, const gchar *subsystem, ...); +FuDeviceLocker * +fu_device_poll_locker_new(FuDevice *self, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware-private.h b/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware-private.h new file mode 100644 index 0000000000000000000000000000000000000000..cf5595b9c02809d15892b6b5959d26b4270d749c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware-private.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-dfu-firmware.h" + +guint8 +fu_dfu_firmware_get_footer_len(FuDfuFirmware *self); +GBytes * +fu_dfu_firmware_append_footer(FuDfuFirmware *self, GBytes *contents, GError **error); +gboolean +fu_dfu_firmware_parse_footer(FuDfuFirmware *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..9634600150a584fd6035547b102db90d25b88ad0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware.c @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-crc.h" +#include "fu-dfu-firmware-private.h" +#include "fu-mem.h" + +/** + * FuDfuFirmware: + * + * A DFU firmware image. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint16 vid; + guint16 pid; + guint16 release; + guint16 dfu_version; + guint8 footer_len; +} FuDfuFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuDfuFirmware, fu_dfu_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_dfu_firmware_get_instance_private(o)) + +static void +fu_dfu_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuDfuFirmware *self = FU_DFU_FIRMWARE(firmware); + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "vendor", priv->vid); + fu_xmlb_builder_insert_kx(bn, "product", priv->pid); + fu_xmlb_builder_insert_kx(bn, "release", priv->release); + fu_xmlb_builder_insert_kx(bn, "dfu_version", priv->dfu_version); +} + +/* private */ +guint8 +fu_dfu_firmware_get_footer_len(FuDfuFirmware *self) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_FIRMWARE(self), 0x0); + return priv->footer_len; +} + +/** + * fu_dfu_firmware_get_vid: + * @self: a #FuDfuFirmware + * + * Gets the vendor ID, or 0xffff for no restriction. + * + * Returns: integer + * + * Since: 1.3.3 + **/ +guint16 +fu_dfu_firmware_get_vid(FuDfuFirmware *self) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_FIRMWARE(self), 0x0); + return priv->vid; +} + +/** + * fu_dfu_firmware_get_pid: + * @self: a #FuDfuFirmware + * + * Gets the product ID, or 0xffff for no restriction. + * + * Returns: integer + * + * Since: 1.3.3 + **/ +guint16 +fu_dfu_firmware_get_pid(FuDfuFirmware *self) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_FIRMWARE(self), 0x0); + return priv->pid; +} + +/** + * fu_dfu_firmware_get_release: + * @self: a #FuDfuFirmware + * + * Gets the device ID, or 0xffff for no restriction. + * + * Returns: integer + * + * Since: 1.3.3 + **/ +guint16 +fu_dfu_firmware_get_release(FuDfuFirmware *self) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_FIRMWARE(self), 0x0); + return priv->release; +} + +/** + * fu_dfu_firmware_get_version: + * @self: a #FuDfuFirmware + * + * Gets the file format version with is 0x0100 by default. + * + * Returns: integer + * + * Since: 1.3.3 + **/ +guint16 +fu_dfu_firmware_get_version(FuDfuFirmware *self) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_FIRMWARE(self), 0x0); + return priv->dfu_version; +} + +/** + * fu_dfu_firmware_set_vid: + * @self: a #FuDfuFirmware + * @vid: vendor ID, or 0xffff if the firmware should match any vendor + * + * Sets the vendor ID. + * + * Since: 1.3.3 + **/ +void +fu_dfu_firmware_set_vid(FuDfuFirmware *self, guint16 vid) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_FIRMWARE(self)); + priv->vid = vid; +} + +/** + * fu_dfu_firmware_set_pid: + * @self: a #FuDfuFirmware + * @pid: product ID, or 0xffff if the firmware should match any product + * + * Sets the product ID. + * + * Since: 1.3.3 + **/ +void +fu_dfu_firmware_set_pid(FuDfuFirmware *self, guint16 pid) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_FIRMWARE(self)); + priv->pid = pid; +} + +/** + * fu_dfu_firmware_set_release: + * @self: a #FuDfuFirmware + * @release: release, or 0xffff if the firmware should match any release + * + * Sets the release for the dfu firmware. + * + * Since: 1.3.3 + **/ +void +fu_dfu_firmware_set_release(FuDfuFirmware *self, guint16 release) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_FIRMWARE(self)); + priv->release = release; +} + +/** + * fu_dfu_firmware_set_version: + * @self: a #FuDfuFirmware + * @version: integer + * + * Sets the file format version. + * + * Since: 1.3.3 + **/ +void +fu_dfu_firmware_set_version(FuDfuFirmware *self, guint16 version) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_FIRMWARE(self)); + priv->dfu_version = version; +} + +typedef struct __attribute__((packed)) { + guint16 release; + guint16 pid; + guint16 vid; + guint16 ver; + guint8 sig[3]; + guint8 len; + guint32 crc; +} FuDfuFirmwareFooter; + +static gboolean +fu_dfu_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint8 magic[3] = {0x0}; + + /* is a footer */ + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0x0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + g_bytes_get_size(fw) - G_STRUCT_OFFSET(FuDfuFirmwareFooter, sig), + sizeof(magic), + error)) + return FALSE; + if (memcmp(magic, "UFD", sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no DFU signature"); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_dfu_firmware_parse_footer(FuDfuFirmware *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + FuDfuFirmwareFooter ftr; + gsize len; + guint32 crc; + guint32 crc_new; + const guint8 *data = g_bytes_get_data(fw, &len); + + /* verify the checksum */ + if (!fu_memcpy_safe((guint8 *)&ftr, + sizeof(FuDfuFirmwareFooter), + 0x0, /* dst */ + data, + len, + len - sizeof(FuDfuFirmwareFooter), /* src */ + sizeof(FuDfuFirmwareFooter), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + crc = GUINT32_FROM_LE(ftr.crc); + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + crc_new = ~fu_crc32(data, len - 4); + if (crc != crc_new) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "CRC failed, expected %04x, got %04x", + crc_new, + GUINT32_FROM_LE(ftr.crc)); + return FALSE; + } + } + + /* set from footer */ + priv->vid = GUINT16_FROM_LE(ftr.vid); + priv->pid = GUINT16_FROM_LE(ftr.pid); + priv->release = GUINT16_FROM_LE(ftr.release); + priv->dfu_version = GUINT16_FROM_LE(ftr.ver); + priv->footer_len = ftr.len; + + /* check reported length */ + if (priv->footer_len > len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "reported footer size %04x larger than file %04x", + (guint)priv->footer_len, + (guint)len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuDfuFirmware *self = FU_DFU_FIRMWARE(firmware); + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + gsize len = g_bytes_get_size(fw); + g_autoptr(GBytes) contents = NULL; + + /* parse footer */ + if (!fu_dfu_firmware_parse_footer(self, fw, flags, error)) + return FALSE; + + /* trim footer off */ + contents = fu_bytes_new_offset(fw, 0, len - priv->footer_len, error); + if (contents == NULL) + return FALSE; + fu_firmware_set_bytes(firmware, contents); + return TRUE; +} + +GBytes * +fu_dfu_firmware_append_footer(FuDfuFirmware *self, GBytes *contents, GError **error) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + GByteArray *buf = g_byte_array_new(); + const guint8 *blob; + gsize blobsz = 0; + + /* add the raw firmware data */ + blob = g_bytes_get_data(contents, &blobsz); + g_byte_array_append(buf, blob, blobsz); + + /* append footer */ + fu_byte_array_append_uint16(buf, priv->release, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, priv->pid, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, priv->vid, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, priv->dfu_version, G_LITTLE_ENDIAN); + g_byte_array_append(buf, (const guint8 *)"UFD", 3); + fu_byte_array_append_uint8(buf, sizeof(FuDfuFirmwareFooter)); + fu_byte_array_append_uint32(buf, ~fu_crc32(buf->data, buf->len), G_LITTLE_ENDIAN); + return g_byte_array_free_to_bytes(buf); +} + +static GBytes * +fu_dfu_firmware_write(FuFirmware *firmware, GError **error) +{ + FuDfuFirmware *self = FU_DFU_FIRMWARE(firmware); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + g_autoptr(GBytes) fw = NULL; + + /* can only contain one image */ + if (images->len > 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "DFU only supports writing one image"); + return NULL; + } + + /* add footer */ + fw = fu_firmware_get_bytes_with_patches(firmware, error); + if (fw == NULL) + return NULL; + return fu_dfu_firmware_append_footer(self, fw, error); +} + +static gboolean +fu_dfu_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuDfuFirmware *self = FU_DFU_FIRMWARE(firmware); + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "vendor", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + priv->vid = tmp; + tmp = xb_node_query_text_as_uint(n, "product", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + priv->pid = tmp; + tmp = xb_node_query_text_as_uint(n, "release", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + priv->release = tmp; + tmp = xb_node_query_text_as_uint(n, "dfu_version", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + priv->dfu_version = tmp; + + /* success */ + return TRUE; +} + +static void +fu_dfu_firmware_init(FuDfuFirmware *self) +{ + FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); + priv->vid = 0xffff; + priv->pid = 0xffff; + priv->release = 0xffff; + priv->dfu_version = FU_DFU_FIRMARE_VERSION_DFU_1_0; + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_dfu_firmware_class_init(FuDfuFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_dfu_firmware_check_magic; + klass_firmware->export = fu_dfu_firmware_export; + klass_firmware->parse = fu_dfu_firmware_parse; + klass_firmware->write = fu_dfu_firmware_write; + klass_firmware->build = fu_dfu_firmware_build; +} + +/** + * fu_dfu_firmware_new: + * + * Creates a new #FuFirmware of sub type Dfu + * + * Since: 1.3.3 + **/ +FuFirmware * +fu_dfu_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_DFU_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..97a9ee9a525463a8f9b6c3150a1fd5ffac045b5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dfu-firmware.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_DFU_FIRMWARE (fu_dfu_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuFirmware, fu_dfu_firmware, FU, DFU_FIRMWARE, FuFirmware) + +struct _FuDfuFirmwareClass { + FuFirmwareClass parent_class; +}; + +/** + * FU_DFU_FIRMARE_VERSION_UNKNOWN: + * + * Unknown version of the DFU standard in BCD format. + * + * Since: 1.6.1 + **/ +#define FU_DFU_FIRMARE_VERSION_UNKNOWN (0u) + +/** + * FU_DFU_FIRMARE_VERSION_DFU_1_0: + * + * The 1.0 version of the DFU standard in BCD format. + * + * Since: 1.6.1 + **/ +#define FU_DFU_FIRMARE_VERSION_DFU_1_0 (0x0100) + +/** + * FU_DFU_FIRMARE_VERSION_DFU_1_1: + * + * The 1.1 version of the DFU standard in BCD format. + * + * Since: 1.6.1 + **/ +#define FU_DFU_FIRMARE_VERSION_DFU_1_1 (0x0110) + +/** + * FU_DFU_FIRMARE_VERSION_DFUSE: + * + * The DfuSe version of the DFU standard in BCD format, defined by ST. + * + * Since: 1.6.1 + **/ +#define FU_DFU_FIRMARE_VERSION_DFUSE (0x011a) + +/** + * FU_DFU_FIRMARE_VERSION_ATMEL_AVR: + * + * The Atmel AVR version of the DFU standard in BCD format. + * + * Since: 1.6.1 + **/ +#define FU_DFU_FIRMARE_VERSION_ATMEL_AVR (0xff01) + +FuFirmware * +fu_dfu_firmware_new(void); +guint16 +fu_dfu_firmware_get_vid(FuDfuFirmware *self); +guint16 +fu_dfu_firmware_get_pid(FuDfuFirmware *self); +guint16 +fu_dfu_firmware_get_release(FuDfuFirmware *self); +guint16 +fu_dfu_firmware_get_version(FuDfuFirmware *self); +void +fu_dfu_firmware_set_vid(FuDfuFirmware *self, guint16 vid); +void +fu_dfu_firmware_set_pid(FuDfuFirmware *self, guint16 pid); +void +fu_dfu_firmware_set_release(FuDfuFirmware *self, guint16 release); +void +fu_dfu_firmware_set_version(FuDfuFirmware *self, guint16 version); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dfuse-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-dfuse-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..66bfea3597f60f4285155c8f81f0ab7c66c24fe2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dfuse-firmware.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-chunk-private.h" +#include "fu-common.h" +#include "fu-dfu-firmware-private.h" +#include "fu-dfuse-firmware.h" +#include "fu-mem.h" + +/** + * FuDfuseFirmware: + * + * A DfuSe firmware image. + * + * See also: [class@FuDfuFirmware] + */ + +G_DEFINE_TYPE(FuDfuseFirmware, fu_dfuse_firmware, FU_TYPE_DFU_FIRMWARE) + +/* firmware: LE */ +typedef struct __attribute__((packed)) { + guint8 sig[5]; + guint8 ver; + guint32 image_size; + guint8 targets; +} DfuSeHdr; + +/* image: LE */ +typedef struct __attribute__((packed)) { + guint8 sig[6]; + guint8 alt_setting; + guint32 target_named; + gchar target_name[255]; + guint32 target_size; + guint32 chunks; +} DfuSeImageHdr; + +/* element: LE */ +typedef struct __attribute__((packed)) { + guint32 address; + guint32 size; +} DfuSeElementHdr; + +G_STATIC_ASSERT(sizeof(DfuSeHdr) == 11); +G_STATIC_ASSERT(sizeof(DfuSeImageHdr) == 274); +G_STATIC_ASSERT(sizeof(DfuSeElementHdr) == 8); + +static FuChunk * +fu_firmware_image_chunk_parse(FuDfuseFirmware *self, GBytes *bytes, gsize *offset, GError **error) +{ + DfuSeElementHdr hdr = {0x0}; + gsize bufsz = 0; + gsize ftrlen = fu_dfu_firmware_get_footer_len(FU_DFU_FIRMWARE(self)); + const guint8 *buf = g_bytes_get_data(bytes, &bufsz); + g_autoptr(FuChunk) chk = NULL; + g_autoptr(GBytes) blob = NULL; + + /* check size */ + if (!fu_memcpy_safe((guint8 *)&hdr, + sizeof(hdr), + 0x0, /* dst */ + buf, + bufsz - ftrlen, + *offset, /* src */ + sizeof(hdr), + error)) + return NULL; + + /* create new chunk */ + *offset += sizeof(hdr); + blob = fu_bytes_new_offset(bytes, *offset, GUINT32_FROM_LE(hdr.size), error); + if (blob == NULL) + return NULL; + chk = fu_chunk_bytes_new(blob); + fu_chunk_set_address(chk, GUINT32_FROM_LE(hdr.address)); + *offset += fu_chunk_get_data_sz(chk); + + /* success */ + return g_steal_pointer(&chk); +} + +static FuFirmware * +fu_dfuse_firmware_image_parse(FuDfuseFirmware *self, GBytes *bytes, gsize *offset, GError **error) +{ + DfuSeImageHdr hdr = {0x0}; + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(bytes, &bufsz); + g_autoptr(FuFirmware) image = fu_firmware_new(); + + /* verify image signature */ + if (!fu_memcpy_safe((guint8 *)&hdr, + sizeof(hdr), + 0x0, /* dst */ + buf, + bufsz, + *offset, /* src */ + sizeof(hdr), + error)) + return NULL; + if (memcmp(hdr.sig, "Target", sizeof(hdr.sig)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid DfuSe target signature"); + return NULL; + } + + /* set properties */ + fu_firmware_set_idx(image, hdr.alt_setting); + if (GUINT32_FROM_LE(hdr.target_named) == 0x01) { + g_autofree gchar *img_id = NULL; + img_id = g_strndup(hdr.target_name, sizeof(hdr.target_name)); + fu_firmware_set_id(image, img_id); + } + + /* no chunks */ + if (hdr.chunks == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "DfuSe image has no chunks"); + return NULL; + } + + /* parse chunks */ + *offset += sizeof(hdr); + for (guint j = 0; j < GUINT32_FROM_LE(hdr.chunks); j++) { + g_autoptr(FuChunk) chk = NULL; + chk = fu_firmware_image_chunk_parse(self, bytes, offset, error); + if (chk == NULL) + return NULL; + fu_firmware_add_chunk(image, chk); + } + + /* success */ + return g_steal_pointer(&image); +} + +static gboolean +fu_dfuse_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint8 magic[5] = {0x0}; + + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0x0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, /* src */ + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, "DfuSe", sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid DfuSe prefix"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfuse_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuDfuFirmware *dfu_firmware = FU_DFU_FIRMWARE(firmware); + gsize bufsz = 0; + guint32 image_size = 0; + guint8 targets = 0; + guint8 ver = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* DFU footer first */ + if (!fu_dfu_firmware_parse_footer(dfu_firmware, fw, flags, error)) + return FALSE; + + /* check the version */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(DfuSeHdr, ver), + &ver, + error)) + return FALSE; + if (ver != 0x01) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid DfuSe version, got %02x", + ver); + return FALSE; + } + + /* check image size */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(DfuSeHdr, image_size), + &image_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (image_size != bufsz - fu_dfu_firmware_get_footer_len(dfu_firmware)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid DfuSe image size, " + "got %" G_GUINT32_FORMAT ", " + "expected %" G_GSIZE_FORMAT, + image_size, + bufsz - fu_dfu_firmware_get_footer_len(dfu_firmware)); + return FALSE; + } + + /* parse the image targets */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(DfuSeHdr, targets), + &targets, + error)) + return FALSE; + offset += sizeof(DfuSeHdr); + for (guint i = 0; i < targets; i++) { + g_autoptr(FuFirmware) image = NULL; + image = + fu_dfuse_firmware_image_parse(FU_DFUSE_FIRMWARE(firmware), fw, &offset, error); + if (image == NULL) + return FALSE; + fu_firmware_add_image(firmware, image); + } + return TRUE; +} + +static GBytes * +fu_firmware_chunk_write(FuChunk *chk) +{ + DfuSeElementHdr hdr = {0x0}; + const guint8 *data = fu_chunk_get_data(chk); + gsize length = fu_chunk_get_data_sz(chk); + g_autoptr(GByteArray) buf = NULL; + + buf = g_byte_array_sized_new(sizeof(DfuSeElementHdr) + length); + hdr.address = GUINT32_TO_LE(fu_chunk_get_address(chk)); + hdr.size = GUINT32_TO_LE(length); + g_byte_array_append(buf, (const guint8 *)&hdr, sizeof(hdr)); + g_byte_array_append(buf, data, length); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static GBytes * +fu_dfuse_firmware_write_image(FuFirmware *image, GError **error) +{ + DfuSeImageHdr hdr = {0x0}; + gsize totalsz = 0; + g_autoptr(GByteArray) buf = NULL; + g_autoptr(GPtrArray) blobs = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get total size */ + blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + chunks = fu_firmware_get_chunks(image, error); + if (chunks == NULL) + return NULL; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + GBytes *bytes = fu_firmware_chunk_write(chk); + g_ptr_array_add(blobs, bytes); + totalsz += g_bytes_get_size(bytes); + } + + /* mutable output buffer */ + buf = g_byte_array_sized_new(sizeof(DfuSeImageHdr) + totalsz); + + /* add prefix */ + memcpy(hdr.sig, "Target", 6); + hdr.alt_setting = fu_firmware_get_idx(image); + if (fu_firmware_get_id(image) != NULL) { + hdr.target_named = GUINT32_TO_LE(0x01); + (void)g_strlcpy((gchar *)&hdr.target_name, + fu_firmware_get_id(image), + sizeof(hdr.target_name)); + } + hdr.target_size = GUINT32_TO_LE(totalsz); + hdr.chunks = GUINT32_TO_LE(chunks->len); + g_byte_array_append(buf, (const guint8 *)&hdr, sizeof(hdr)); + + /* copy data */ + for (guint i = 0; i < blobs->len; i++) { + GBytes *blob = g_ptr_array_index(blobs, i); + fu_byte_array_append_bytes(buf, blob); + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static GBytes * +fu_dfuse_firmware_write(FuFirmware *firmware, GError **error) +{ + DfuSeHdr hdr = {0x0}; + gsize totalsz = 0; + g_autoptr(GByteArray) buf = NULL; + g_autoptr(GBytes) blob_noftr = NULL; + g_autoptr(GPtrArray) blobs = NULL; + g_autoptr(GPtrArray) images = NULL; + + /* create mutable output buffer */ + blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + images = fu_firmware_get_images(FU_FIRMWARE(firmware)); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = NULL; + blob = fu_dfuse_firmware_write_image(img, error); + if (blob == NULL) + return NULL; + totalsz += g_bytes_get_size(blob); + g_ptr_array_add(blobs, g_steal_pointer(&blob)); + } + buf = g_byte_array_sized_new(sizeof(DfuSeHdr) + totalsz); + + /* DfuSe header */ + memcpy(hdr.sig, "DfuSe", 5); + hdr.ver = 0x01; + hdr.image_size = GUINT32_TO_LE(sizeof(hdr) + totalsz); + if (images->len > G_MAXUINT8) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "too many (%u) images to write DfuSe file", + images->len); + return NULL; + } + hdr.targets = (guint8)images->len; + g_byte_array_append(buf, (const guint8 *)&hdr, sizeof(hdr)); + + /* copy images */ + for (guint i = 0; i < blobs->len; i++) { + GBytes *blob = g_ptr_array_index(blobs, i); + fu_byte_array_append_bytes(buf, blob); + } + + /* return blob */ + blob_noftr = g_byte_array_free_to_bytes(g_steal_pointer(&buf)); + return fu_dfu_firmware_append_footer(FU_DFU_FIRMWARE(firmware), blob_noftr, error); +} + +static void +fu_dfuse_firmware_init(FuDfuseFirmware *self) +{ + fu_dfu_firmware_set_version(FU_DFU_FIRMWARE(self), FU_DFU_FIRMARE_VERSION_DFUSE); +} + +static void +fu_dfuse_firmware_class_init(FuDfuseFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_dfuse_firmware_check_magic; + klass_firmware->parse = fu_dfuse_firmware_parse; + klass_firmware->write = fu_dfuse_firmware_write; +} + +/** + * fu_dfuse_firmware_new: + * + * Creates a new #FuFirmware of sub type DfuSe + * + * Since: 1.5.6 + **/ +FuFirmware * +fu_dfuse_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_DFUSE_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dfuse-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-dfuse-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..e98127daca979c3feb55135ce1e9bf92f8cf45ca --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dfuse-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-dfu-firmware.h" + +#define FU_TYPE_DFUSE_FIRMWARE (fu_dfuse_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuseFirmware, fu_dfuse_firmware, FU, DFUSE_FIRMWARE, FuDfuFirmware) + +struct _FuDfuseFirmwareClass { + FuDfuFirmwareClass parent_class; +}; + +FuFirmware * +fu_dfuse_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dump.c b/fwupd-1.8.6/libfwupdplugin/fu-dump.c new file mode 100644 index 0000000000000000000000000000000000000000..dd8373c29d80b9538b6e1dd0c7f5cf1e8cd79962 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dump.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include "fu-dump.h" + +/** + * fu_dump_full: + * @log_domain: (nullable): optional log domain, typically %G_LOG_DOMAIN + * @title: (nullable): optional prefix title + * @data: buffer to print + * @len: the size of @data + * @columns: break new lines after this many bytes + * @flags: dump flags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII + * + * Dumps a raw buffer to the screen. + * + * Since: 1.8.2 + **/ +void +fu_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); + if (flags & FU_DUMP_FLAGS_SHOW_ASCII) + g_string_append(str, " "); + } + g_string_append(str, "\n───────┼"); + for (gsize i = 0; i < columns; i++) { + g_string_append(str, "───"); + if (flags & FU_DUMP_FLAGS_SHOW_ASCII) + 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_dump_raw: + * @log_domain: (nullable): optional log domain, typically %G_LOG_DOMAIN + * @title: (nullable): optional prefix title + * @data: buffer to print + * @len: the size of @data + * + * Dumps a raw buffer to the screen. + * + * Since: 1.8.2 + **/ +void +fu_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_dump_full(log_domain, title, data, len, 32, flags); +} + +/** + * fu_dump_bytes: + * @log_domain: (nullable): optional log domain, typically %G_LOG_DOMAIN + * @title: (nullable): optional prefix title + * @bytes: data blob + * + * Dumps a byte buffer to the screen. + * + * Since: 1.8.2 + **/ +void +fu_dump_bytes(const gchar *log_domain, const gchar *title, GBytes *bytes) +{ + gsize len = 0; + const guint8 *data = g_bytes_get_data(bytes, &len); + fu_dump_raw(log_domain, title, data, len); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-dump.h b/fwupd-1.8.6/libfwupdplugin/fu-dump.h new file mode 100644 index 0000000000000000000000000000000000000000..3908dfe9f631acb9b2083086d9b913569d4e3769 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-dump.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * 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; + +void +fu_dump_raw(const gchar *log_domain, const gchar *title, const guint8 *data, gsize len); +void +fu_dump_full(const gchar *log_domain, + const gchar *title, + const guint8 *data, + gsize len, + guint columns, + FuDumpFlags flags); +void +fu_dump_bytes(const gchar *log_domain, const gchar *title, GBytes *bytes); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-common.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-common.c new file mode 100644 index 0000000000000000000000000000000000000000..b9397f67728294f8d7794c7da755a95eb217fda7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-common.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-efi-common.h" + +/** + * fu_efi_guid_to_name: + * @guid: A lowercase GUID string, e.g. `8c8ce578-8a3d-4f1c-9935-896185c32dd3` + * + * Converts a GUID to the known nice name. + * + * Returns: identifier string, or %NULL if unknown + * + * Since: 1.6.2 + **/ +const gchar * +fu_efi_guid_to_name(const gchar *guid) +{ + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_FFS1) == 0) + return "Volume:Ffs1"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_FFS2) == 0) + return "Volume:Ffs2"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_FFS3) == 0) + return "Volume:Ffs3"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_NVRAM_EVSA) == 0) + return "Volume:NvramEvsa"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_NVRAM_NVAR) == 0) + return "Volume:NvramNvar"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_NVRAM_EVSA2) == 0) + return "Volume:NvramEvsa2"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_APPLE_BOOT) == 0) + return "Volume:AppleBoot"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_PFH1) == 0) + return "Volume:Pfh1"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_VOLUME_GUID_PFH2) == 0) + return "Volume:Pfh2"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_FILE_FV_IMAGE) == 0) + return "File:FvImage"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_FILE_MICROCODE) == 0) + return "File:Microcode"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_FILE_BIOS_GUARD) == 0) + return "File:BiosGuard"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_SECTION_LZMA_COMPRESS) == 0) + return "Section:LzmaCompress"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_SECTION_TIANO_COMPRESS) == 0) + return "Section:TianoCompress"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_SECTION_SMBIOS_TABLE) == 0) + return "Section:SmbiosTable"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_SECTION_ESRT_TABLE) == 0) + return "Section:EsrtTable"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_SECTION_ACPI1_TABLE) == 0) + return "Section:Acpi1Table"; + if (g_strcmp0(guid, FU_EFI_FIRMWARE_SECTION_ACPI2_TABLE) == 0) + return "Section:Acpi2Table"; + return NULL; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-common.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-common.h new file mode 100644 index 0000000000000000000000000000000000000000..630d8f3d0bbefc2e6a13f9d56aeb0cddfc7114a2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-common.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_EFI_FIRMWARE_VOLUME_GUID_FFS1 "7a9354d9-0468-444a-81ce-0bf617d890df" +#define FU_EFI_FIRMWARE_VOLUME_GUID_FFS2 "8c8ce578-8a3d-4f1c-9935-896185c32dd3" +#define FU_EFI_FIRMWARE_VOLUME_GUID_FFS3 "5473c07a-3dcb-4dca-bd6f-1e9689e7349a" +#define FU_EFI_FIRMWARE_VOLUME_GUID_NVRAM_EVSA "fff12b8d-7696-4c8b-a985-2747075b4f50" +#define FU_EFI_FIRMWARE_VOLUME_GUID_NVRAM_NVAR "cef5b9a3-476d-497f-9fdc-e98143e0422c" +#define FU_EFI_FIRMWARE_VOLUME_GUID_NVRAM_EVSA2 "00504624-8a59-4eeb-bd0f-6b36e96128e0" +#define FU_EFI_FIRMWARE_VOLUME_GUID_APPLE_BOOT "04adeead-61ff-4d31-b6ba-64f8bf901f5a" +#define FU_EFI_FIRMWARE_VOLUME_GUID_PFH1 "16b45da2-7d70-4aea-a58d-760e9ecb841d" +#define FU_EFI_FIRMWARE_VOLUME_GUID_PFH2 "e360bdba-c3ce-46be-8f37-b231e5cb9f35" + +#define FU_EFI_FIRMWARE_FILE_FV_IMAGE "4e35fd93-9c72-4c15-8c4b-e77f1db2d792" +#define FU_EFI_FIRMWARE_FILE_MICROCODE "197db236-f856-4924-90f8-cdf12fb875f3" +#define FU_EFI_FIRMWARE_FILE_BIOS_GUARD "7934156d-cfce-460e-92f5-a07909a59eca" + +#define FU_EFI_FIRMWARE_SECTION_LZMA_COMPRESS "ee4e5898-3914-4259-9d6e-dc7bd79403cf" +#define FU_EFI_FIRMWARE_SECTION_TIANO_COMPRESS "a31280ad-481e-41b6-95e8-127f4c984779" +#define FU_EFI_FIRMWARE_SECTION_SMBIOS_TABLE "eb9d2d31-2d88-11d3-9a16-0090273fc14d" +#define FU_EFI_FIRMWARE_SECTION_ESRT_TABLE "b122a263-3661-4f68-9929-78f8b0d62180" +#define FU_EFI_FIRMWARE_SECTION_ACPI1_TABLE "eb9d2d30-2d88-11d3-9a16-0090273fc14d" +#define FU_EFI_FIRMWARE_SECTION_ACPI2_TABLE "8868e871-e4f1-11d3-bc22-0080c73c8881" + +const gchar * +fu_efi_guid_to_name(const gchar *guid); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-common.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-common.c new file mode 100644 index 0000000000000000000000000000000000000000..60c5fb8d6b3957ad00f5a993b29f27868d3661fd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-common.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_LZMA +#include +#endif + +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-efi-firmware-common.h" +#include "fu-efi-firmware-section.h" + +/** + * fu_efi_firmware_parse_sections: + * @firmware: #FuFirmware + * @fw: data + * @flags: flags + * @error: (nullable): optional return location for an error + * + * Parses a UEFI section. + * + * Returns: %TRUE for success + * + * Since: 1.6.2 + **/ +gboolean +fu_efi_firmware_parse_sections(FuFirmware *firmware, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + gsize offset = 0; + gsize bufsz = g_bytes_get_size(fw); + + while (offset < bufsz) { + g_autoptr(FuFirmware) img = fu_efi_firmware_section_new(); + g_autoptr(GBytes) blob = NULL; + + /* maximum payload */ + blob = fu_bytes_new_offset(fw, offset, bufsz - offset, error); + if (blob == NULL) + return FALSE; + + /* parse section */ + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + fu_firmware_set_offset(img, offset); + fu_firmware_add_image(firmware, img); + + /* next! */ + offset += fu_firmware_get_size(img); + } + if (offset != g_bytes_get_size(fw)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "EFI sections overflow 0x%x of 0x%x", + (guint)offset, + (guint)g_bytes_get_size(fw)); + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_efi_firmware_decompress_lzma: + * @blob: data + * @error: (nullable): optional return location for an error + * + * Decompresses a LZMA stream. + * + * Returns: decompressed data + * + * Since: 1.6.2 + **/ +GBytes * +fu_efi_firmware_decompress_lzma(GBytes *blob, GError **error) +{ +#ifdef HAVE_LZMA + const gsize tmpbufsz = 0x20000; + lzma_ret rc; + lzma_stream strm = LZMA_STREAM_INIT; + uint64_t memlimit = G_MAXUINT32; + g_autofree guint8 *tmpbuf = g_malloc0(tmpbufsz); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + strm.next_in = g_bytes_get_data(blob, NULL); + strm.avail_in = g_bytes_get_size(blob); + + rc = lzma_auto_decoder(&strm, memlimit, LZMA_TELL_UNSUPPORTED_CHECK); + if (rc != LZMA_OK) { + lzma_end(&strm); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to set up LZMA decoder rc=%u", + rc); + return NULL; + } + do { + strm.next_out = tmpbuf; + strm.avail_out = tmpbufsz; + rc = lzma_code(&strm, LZMA_RUN); + if (rc != LZMA_OK && rc != LZMA_STREAM_END) + break; + g_byte_array_append(buf, tmpbuf, tmpbufsz - strm.avail_out); + } while (rc == LZMA_OK); + lzma_end(&strm); + + /* success */ + if (rc != LZMA_OK && rc != LZMA_STREAM_END) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to decode LZMA data rc=%u", + rc); + return NULL; + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +#else + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "missing lzma support"); + return NULL; +#endif +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-common.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-common.h new file mode 100644 index 0000000000000000000000000000000000000000..26ae11963cd5e17c6807ff445bed1118761fd080 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-common.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +gboolean +fu_efi_firmware_parse_sections(FuFirmware *firmware, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); +GBytes * +fu_efi_firmware_decompress_lzma(GBytes *blob, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-file.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-file.c new file mode 100644 index 0000000000000000000000000000000000000000..8b7c455ac4bee0dd6df6ac9f5362419f443f9ab7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-file.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-efi-common.h" +#include "fu-efi-firmware-common.h" +#include "fu-efi-firmware-file.h" +#include "fu-efi-firmware-section.h" +#include "fu-mem.h" +#include "fu-sum.h" + +/** + * FuEfiFirmwareFile: + * + * A UEFI file. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint8 type; + guint8 attrib; +} FuEfiFirmwareFilePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuEfiFirmwareFile, fu_efi_firmware_file, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_efi_firmware_file_get_instance_private(o)) + +#define FU_EFI_FIRMWARE_FILE_ATTRIB_NONE 0x00 +#define FU_EFI_FIRMWARE_FILE_ATTRIB_LARGE_FILE 0x01 +#define FU_EFI_FIRMWARE_FILE_ATTRIB_DATA_ALIGNMENT_2 0x02 +#define FU_EFI_FIRMWARE_FILE_ATTRIB_FIXED 0x04 +#define FU_EFI_FIRMWARE_FILE_ATTRIB_DATA_ALIGNMENT 0x38 +#define FU_EFI_FIRMWARE_FILE_ATTRIB_CHECKSUM 0x40 + +#define FU_EFI_FIRMWARE_FILE_TYPE_ALL 0x00 +#define FU_EFI_FIRMWARE_FILE_TYPE_RAW 0x01 +#define FU_EFI_FIRMWARE_FILE_TYPE_FREEFORM 0x02 +#define FU_EFI_FIRMWARE_FILE_TYPE_SECURITY_CORE 0x03 +#define FU_EFI_FIRMWARE_FILE_TYPE_PEI_CORE 0x04 +#define FU_EFI_FIRMWARE_FILE_TYPE_DXE_CORE 0x05 +#define FU_EFI_FIRMWARE_FILE_TYPE_PEIM 0x06 +#define FU_EFI_FIRMWARE_FILE_TYPE_DRIVER 0x07 +#define FU_EFI_FIRMWARE_FILE_TYPE_COMBINED_PEIM_DRIVER 0x08 +#define FU_EFI_FIRMWARE_FILE_TYPE_APPLICATION 0x09 +#define FU_EFI_FIRMWARE_FILE_TYPE_MM 0x0A +#define FU_EFI_FIRMWARE_FILE_TYPE_FIRMWARE_VOLUME_IMAGE 0x0B +#define FU_EFI_FIRMWARE_FILE_TYPE_COMBINED_MM_DXE 0x0C +#define FU_EFI_FIRMWARE_FILE_TYPE_MM_CORE 0x0D +#define FU_EFI_FIRMWARE_FILE_TYPE_MM_STANDALONE 0x0E +#define FU_EFI_FIRMWARE_FILE_TYPE_MM_CORE_STANDALONE 0x0F +#define FU_EFI_FIRMWARE_FILE_TYPE_FFS_PAD 0xF0 + +#define FU_EFI_FIRMWARE_FILE_OFFSET_NAME 0x00 +#define FU_EFI_FIRMWARE_FILE_OFFSET_HDR_CHECKSUM 0x10 +#define FU_EFI_FIRMWARE_FILE_OFFSET_DATA_CHECKSUM 0x11 +#define FU_EFI_FIRMWARE_FILE_OFFSET_TYPE 0x12 +#define FU_EFI_FIRMWARE_FILE_OFFSET_ATTRS 0x13 +#define FU_EFI_FIRMWARE_FILE_OFFSET_SIZE 0x14 +#define FU_EFI_FIRMWARE_FILE_OFFSET_STATE 0x17 +#define FU_EFI_FIRMWARE_FILE_SIZE 0x18 + +static const gchar * +fu_efi_firmware_file_type_to_string(guint8 type) +{ + if (type == FU_EFI_FIRMWARE_FILE_TYPE_ALL) + return "all"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_RAW) + return "raw"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_FREEFORM) + return "freeform"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_SECURITY_CORE) + return "security-core"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_PEI_CORE) + return "pei-core"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_DXE_CORE) + return "dxe-core"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_PEIM) + return "peim"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_DRIVER) + return "driver"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_COMBINED_PEIM_DRIVER) + return "combined-peim-driver"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_APPLICATION) + return "application"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_MM) + return "mm"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_FIRMWARE_VOLUME_IMAGE) + return "firmware-volume-image"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_COMBINED_MM_DXE) + return "combined-mm-dxe"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_MM_CORE) + return "mm-core"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_MM_STANDALONE) + return "mm-standalone"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_MM_CORE_STANDALONE) + return "core-standalone"; + if (type == FU_EFI_FIRMWARE_FILE_TYPE_FFS_PAD) + return "ffs-pad"; + return NULL; +} + +static void +fu_efi_firmware_file_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuEfiFirmwareFile *self = FU_EFI_FIRMWARE_FILE(firmware); + FuEfiFirmwareFilePrivate *priv = GET_PRIVATE(self); + + fu_xmlb_builder_insert_kx(bn, "attrib", priv->attrib); + fu_xmlb_builder_insert_kx(bn, "type", priv->type); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kv(bn, + "name", + fu_efi_guid_to_name(fu_firmware_get_id(firmware))); + fu_xmlb_builder_insert_kv(bn, + "type_name", + fu_efi_firmware_file_type_to_string(priv->type)); + } +} + +static guint8 +fu_efi_firmware_file_hdr_checksum8(GBytes *blob) +{ + gsize bufsz = 0; + guint8 checksum = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + for (gsize i = 0; i < bufsz; i++) { + if (i == FU_EFI_FIRMWARE_FILE_OFFSET_HDR_CHECKSUM) + continue; + if (i == FU_EFI_FIRMWARE_FILE_OFFSET_DATA_CHECKSUM) + continue; + if (i == FU_EFI_FIRMWARE_FILE_OFFSET_STATE) + continue; + checksum += buf[i]; + } + return 0x100 - checksum; +} + +static gboolean +fu_efi_firmware_file_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuEfiFirmwareFile *self = FU_EFI_FIRMWARE_FILE(firmware); + FuEfiFirmwareFilePrivate *priv = GET_PRIVATE(self); + gsize bufsz = 0; + guint32 size = 0x0; + guint8 data_checksum = 0x0; + guint8 hdr_checksum = 0x0; + guint8 img_state = 0x0; + fwupd_guid_t guid = {0x0}; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *guid_str = NULL; + g_autoptr(GBytes) blob = NULL; + + /* header */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + FU_EFI_FIRMWARE_FILE_OFFSET_NAME, /* src */ + sizeof(guid), + error)) + return FALSE; + guid_str = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + fu_firmware_set_id(firmware, guid_str); + if (!fu_memread_uint8_safe(buf, + bufsz, + FU_EFI_FIRMWARE_FILE_OFFSET_STATE, + &img_state, + error)) + return FALSE; + if (img_state != 0xF8) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "state invalid, got 0x%x, expected 0x%x", + img_state, + (guint)0xF8); + return FALSE; + } + if (!fu_memread_uint8_safe(buf, + bufsz, + FU_EFI_FIRMWARE_FILE_OFFSET_HDR_CHECKSUM, + &hdr_checksum, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + FU_EFI_FIRMWARE_FILE_OFFSET_DATA_CHECKSUM, + &data_checksum, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + FU_EFI_FIRMWARE_FILE_OFFSET_TYPE, + &priv->type, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + FU_EFI_FIRMWARE_FILE_OFFSET_ATTRS, + &priv->attrib, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, /* uint24_t! */ + FU_EFI_FIRMWARE_FILE_OFFSET_SIZE, + &size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + size &= 0xFFFFFF; + if (size < FU_EFI_FIRMWARE_FILE_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid FFS length, got 0x%x", + (guint)size); + return FALSE; + } + + /* verify header checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 hdr_checksum_verify; + g_autoptr(GBytes) hdr_blob = NULL; + + hdr_blob = fu_bytes_new_offset(fw, 0x0, FU_EFI_FIRMWARE_FILE_SIZE, error); + if (hdr_blob == NULL) + return FALSE; + hdr_checksum_verify = fu_efi_firmware_file_hdr_checksum8(hdr_blob); + if (hdr_checksum_verify != hdr_checksum) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, got %02x, expected %02x", + hdr_checksum_verify, + hdr_checksum); + return FALSE; + } + } + + /* add simple blob */ + blob = fu_bytes_new_offset(fw, + FU_EFI_FIRMWARE_FILE_SIZE, + size - FU_EFI_FIRMWARE_FILE_SIZE, + error); + if (blob == NULL) + return FALSE; + + /* add fv-image */ + if (priv->type == FU_EFI_FIRMWARE_FILE_TYPE_FIRMWARE_VOLUME_IMAGE) { + if (!fu_efi_firmware_parse_sections(firmware, blob, flags, error)) + return FALSE; + } else { + fu_firmware_set_bytes(firmware, blob); + } + + /* verify data checksum */ + if ((priv->attrib & FU_EFI_FIRMWARE_FILE_ATTRIB_CHECKSUM) > 0 && + (flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 data_checksum_verify = 0x100 - fu_sum8_bytes(blob); + if (data_checksum_verify != data_checksum) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, got %02x, expected %02x", + data_checksum_verify, + data_checksum); + return FALSE; + } + } + + /* align size for volume */ + fu_firmware_set_size(firmware, + fu_common_align_up(size, fu_firmware_get_alignment(firmware))); + + /* success */ + return TRUE; +} + +static GBytes * +fu_efi_firmware_file_write_sections(FuFirmware *firmware, GError **error) +{ + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* sanity check */ + if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "alignment invalid, got 0x%02x", + fu_firmware_get_alignment(firmware)); + return NULL; + } + + /* no sections defined */ + if (images->len == 0) + return fu_firmware_get_bytes_with_patches(firmware, error); + + /* add each section */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = NULL; + fu_firmware_set_offset(img, buf->len); + blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + fu_byte_array_align_up(buf, fu_firmware_get_alignment(img), 0xFF); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static GBytes * +fu_efi_firmware_file_write(FuFirmware *firmware, GError **error) +{ + FuEfiFirmwareFile *self = FU_EFI_FIRMWARE_FILE(firmware); + FuEfiFirmwareFilePrivate *priv = GET_PRIVATE(self); + fwupd_guid_t guid = {0x0}; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) hdr_blob = NULL; + + /* simple blob for now */ + blob = fu_efi_firmware_file_write_sections(firmware, error); + if (blob == NULL) + return NULL; + + /* header */ + if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return NULL; + g_byte_array_append(buf, (guint8 *)&guid, sizeof(guid)); + fu_byte_array_append_uint8(buf, 0x0); /* hdr_checksum */ + fu_byte_array_append_uint8(buf, 0x100 - fu_sum8_bytes(blob)); + fu_byte_array_append_uint8(buf, priv->type); /* data_checksum */ + fu_byte_array_append_uint8(buf, priv->attrib); /* data_checksum */ + fu_byte_array_append_uint32(buf, + g_bytes_get_size(blob) + FU_EFI_FIRMWARE_FILE_SIZE, + G_LITTLE_ENDIAN); + buf->data[FU_EFI_FIRMWARE_FILE_OFFSET_STATE] = 0xF8; /* overwrite the LSB of size */ + + /* fix up header checksum */ + hdr_blob = g_bytes_new(buf->data, buf->len); + buf->data[FU_EFI_FIRMWARE_FILE_OFFSET_HDR_CHECKSUM] = + fu_efi_firmware_file_hdr_checksum8(hdr_blob); + + /* payload */ + fu_byte_array_append_bytes(buf, blob); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_efi_firmware_file_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuEfiFirmwareFile *self = FU_EFI_FIRMWARE_FILE(firmware); + FuEfiFirmwareFilePrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + /* simple properties */ + tmp = xb_node_query_text_as_uint(n, "type", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->type = tmp; + tmp = xb_node_query_text_as_uint(n, "attrib", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->attrib = tmp; + + /* success */ + return TRUE; +} + +static void +fu_efi_firmware_file_init(FuEfiFirmwareFile *self) +{ + FuEfiFirmwareFilePrivate *priv = GET_PRIVATE(self); + priv->attrib = FU_EFI_FIRMWARE_FILE_ATTRIB_NONE; + priv->type = FU_EFI_FIRMWARE_FILE_TYPE_RAW; + fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8); +} + +static void +fu_efi_firmware_file_class_init(FuEfiFirmwareFileClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_efi_firmware_file_parse; + klass_firmware->write = fu_efi_firmware_file_write; + klass_firmware->build = fu_efi_firmware_file_build; + klass_firmware->export = fu_efi_firmware_file_export; +} + +/** + * fu_efi_firmware_file_new: + * + * Creates a new #FuFirmware + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_efi_firmware_file_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FIRMWARE_FILE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-file.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-file.h new file mode 100644 index 0000000000000000000000000000000000000000..167f35cab218746bffa2784f881c9d6fdda3d56c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-file.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_FIRMWARE_FILE (fu_efi_firmware_file_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuEfiFirmwareFile, fu_efi_firmware_file, FU, EFI_FIRMWARE_FILE, FuFirmware) + +struct _FuEfiFirmwareFileClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_efi_firmware_file_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-filesystem.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-filesystem.c new file mode 100644 index 0000000000000000000000000000000000000000..d69a13c5a506798ef67e0ec3020f5d169919be13 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-filesystem.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-efi-firmware-file.h" +#include "fu-efi-firmware-filesystem.h" + +/** + * FuEfiFirmwareFilesystem: + * + * A UEFI filesystem. + * + * See also: [class@FuFirmware] + */ + +G_DEFINE_TYPE(FuEfiFirmwareFilesystem, fu_efi_firmware_filesystem, FU_TYPE_FIRMWARE) + +static gboolean +fu_efi_firmware_filesystem_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + while (offset + 0x18 < bufsz) { + g_autoptr(FuFirmware) img = fu_efi_firmware_file_new(); + g_autoptr(GBytes) fw_tmp = NULL; + gboolean is_freespace = TRUE; + + /* ignore free space */ + for (guint i = 0; i < 0x18; i++) { + if (buf[offset + i] != 0xff) { + is_freespace = FALSE; + break; + } + } + if (is_freespace) + break; + + fw_tmp = fu_bytes_new_offset(fw, offset, bufsz - offset, error); + if (fw_tmp == NULL) + return FALSE; + if (!fu_firmware_parse(img, fw_tmp, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) { + g_prefix_error(error, "failed to parse EFI file at 0x%x: ", (guint)offset); + return FALSE; + } + fu_firmware_set_offset(firmware, offset); + fu_firmware_add_image(firmware, img); + + /* next! */ + offset += fu_firmware_get_size(img); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_efi_firmware_filesystem_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* sanity check */ + if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "alignment invalid, got 0x%02x", + fu_firmware_get_alignment(firmware)); + return NULL; + } + + /* add each file */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = NULL; + fu_firmware_set_offset(img, buf->len); + blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + fu_byte_array_align_up(buf, fu_firmware_get_alignment(firmware), 0xFF); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_efi_firmware_filesystem_init(FuEfiFirmwareFilesystem *self) +{ + fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8); +} + +static void +fu_efi_firmware_filesystem_class_init(FuEfiFirmwareFilesystemClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_efi_firmware_filesystem_parse; + klass_firmware->write = fu_efi_firmware_filesystem_write; +} + +/** + * fu_efi_firmware_filesystem_new: + * + * Creates a new #FuFirmware + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_efi_firmware_filesystem_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FIRMWARE_FILESYSTEM, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-filesystem.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-filesystem.h new file mode 100644 index 0000000000000000000000000000000000000000..26d78dac642701da188f39c30706c25d8ec6d033 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-filesystem.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_FIRMWARE_FILESYSTEM (fu_efi_firmware_filesystem_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuEfiFirmwareFilesystem, + fu_efi_firmware_filesystem, + FU, + EFI_FIRMWARE_FILESYSTEM, + FuFirmware) + +struct _FuEfiFirmwareFilesystemClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_efi_firmware_filesystem_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-section.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-section.c new file mode 100644 index 0000000000000000000000000000000000000000..b797f0e6b5e3dc04a51d0865e103af9bfdd068e6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-section.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-efi-common.h" +#include "fu-efi-firmware-common.h" +#include "fu-efi-firmware-section.h" +#include "fu-efi-firmware-volume.h" +#include "fu-mem.h" + +/** + * FuEfiFirmwareSection: + * + * A UEFI firmware section. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint8 type; +} FuEfiFirmwareSectionPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuEfiFirmwareSection, fu_efi_firmware_section, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_efi_firmware_section_get_instance_private(o)) + +#define FU_EFI_FIRMWARE_SECTION_OFFSET_SIZE 0x00 +#define FU_EFI_FIRMWARE_SECTION_OFFSET_TYPE 0x03 +#define FU_EFI_FIRMWARE_SECTION_SIZE 0x04 + +/* only GUID defined */ +#define FU_EFI_FIRMWARE_SECTION_OFFSET_GUID_NAME 0x04 +#define FU_EFI_FIRMWARE_SECTION_OFFSET_GUID_DATA_OFFSET 0x14 +#define FU_EFI_FIRMWARE_SECTION_OFFSET_GUID_ATTR 0x16 + +#define FU_EFI_FIRMWARE_SECTION_TYPE_COMPRESSION 0x01 +#define FU_EFI_FIRMWARE_SECTION_TYPE_GUID_DEFINED 0x02 +#define FU_EFI_FIRMWARE_SECTION_TYPE_DISPOSABLE 0x03 +#define FU_EFI_FIRMWARE_SECTION_TYPE_PE32 0x10 +#define FU_EFI_FIRMWARE_SECTION_TYPE_PIC 0x11 +#define FU_EFI_FIRMWARE_SECTION_TYPE_TE 0x12 +#define FU_EFI_FIRMWARE_SECTION_TYPE_DXE_DEPEX 0x13 +#define FU_EFI_FIRMWARE_SECTION_TYPE_VERSION 0x14 +#define FU_EFI_FIRMWARE_SECTION_TYPE_USER_INTERFACE 0x15 +#define FU_EFI_FIRMWARE_SECTION_TYPE_COMPATIBILITY16 0x16 +#define FU_EFI_FIRMWARE_SECTION_TYPE_VOLUME_IMAGE 0x17 +#define FU_EFI_FIRMWARE_SECTION_TYPE_FREEFORM_SUBTYPE_GUID 0x18 +#define FU_EFI_FIRMWARE_SECTION_TYPE_RAW 0x19 +#define FU_EFI_FIRMWARE_SECTION_TYPE_PEI_DEPEX 0x1B +#define FU_EFI_FIRMWARE_SECTION_TYPE_MM_DEPEX 0x1C + +static const gchar * +fu_efi_firmware_section_type_to_string(guint8 type) +{ + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_COMPRESSION) + return "compression"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_GUID_DEFINED) + return "guid-defined"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_DISPOSABLE) + return "disposable"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_PE32) + return "pe32"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_PIC) + return "pic"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_TE) + return "te"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_DXE_DEPEX) + return "dxe-depex"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_VERSION) + return "version"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_USER_INTERFACE) + return "user-interface"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_COMPATIBILITY16) + return "compatibility16"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_VOLUME_IMAGE) + return "volume-image"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_FREEFORM_SUBTYPE_GUID) + return "freeform-subtype-guid"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_RAW) + return "raw"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_PEI_DEPEX) + return "pei-depex"; + if (type == FU_EFI_FIRMWARE_SECTION_TYPE_MM_DEPEX) + return "mm-depex"; + return NULL; +} + +static void +fu_efi_firmware_section_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuEfiFirmwareSection *self = FU_EFI_FIRMWARE_SECTION(firmware); + FuEfiFirmwareSectionPrivate *priv = GET_PRIVATE(self); + + fu_xmlb_builder_insert_kx(bn, "type", priv->type); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kv(bn, + "name", + fu_efi_guid_to_name(fu_firmware_get_id(firmware))); + fu_xmlb_builder_insert_kv(bn, + "type_name", + fu_efi_firmware_section_type_to_string(priv->type)); + } +} + +static gboolean +fu_efi_firmware_section_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset_ignored, + FwupdInstallFlags flags, + GError **error) +{ + FuEfiFirmwareSection *self = FU_EFI_FIRMWARE_SECTION(firmware); + FuEfiFirmwareSectionPrivate *priv = GET_PRIVATE(self); + gsize bufsz = 0; + guint16 attr = 0x0; + guint16 offset = FU_EFI_FIRMWARE_SECTION_SIZE; + guint32 size = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(GBytes) blob = NULL; + + if (!fu_memread_uint24_safe(buf, + bufsz, + FU_EFI_FIRMWARE_SECTION_OFFSET_SIZE, + &size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (size < FU_EFI_FIRMWARE_SECTION_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid section size, got 0x%x", + (guint)size); + return FALSE; + } + if (!fu_memread_uint8_safe(buf, + bufsz, + FU_EFI_FIRMWARE_SECTION_OFFSET_TYPE, + &priv->type, + error)) + return FALSE; + + /* name */ + if (priv->type == FU_EFI_FIRMWARE_SECTION_TYPE_GUID_DEFINED) { + fwupd_guid_t guid = {0x0}; + g_autofree gchar *guid_str = NULL; + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + FU_EFI_FIRMWARE_SECTION_OFFSET_GUID_NAME, /* src */ + sizeof(guid), + error)) + return FALSE; + guid_str = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + fu_firmware_set_id(firmware, guid_str); + if (!fu_memread_uint16_safe(buf, + bufsz, + FU_EFI_FIRMWARE_SECTION_OFFSET_GUID_DATA_OFFSET, + &offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (offset < FU_EFI_FIRMWARE_SECTION_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid section size, got 0x%x", + (guint)size); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + bufsz, + FU_EFI_FIRMWARE_SECTION_OFFSET_GUID_ATTR, + &attr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* create blob */ + blob = fu_bytes_new_offset(fw, offset, size - offset, error); + if (blob == NULL) + return FALSE; + fu_firmware_set_offset(firmware, offset); + fu_firmware_set_size(firmware, size); + fu_firmware_set_bytes(firmware, blob); + + /* nested volume */ + if (priv->type == FU_EFI_FIRMWARE_SECTION_TYPE_VOLUME_IMAGE) { + g_autoptr(FuFirmware) img = fu_efi_firmware_volume_new(); + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + fu_firmware_add_image(firmware, img); + + /* LZMA */ + } else if (priv->type == FU_EFI_FIRMWARE_SECTION_TYPE_GUID_DEFINED && + g_strcmp0(fu_firmware_get_id(firmware), FU_EFI_FIRMWARE_SECTION_LZMA_COMPRESS) == + 0) { + g_autoptr(GBytes) blob_uncomp = NULL; + + /* parse all sections */ + blob_uncomp = fu_efi_firmware_decompress_lzma(blob, error); + if (blob_uncomp == NULL) + return FALSE; + if (!fu_efi_firmware_parse_sections(firmware, blob_uncomp, flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_efi_firmware_section_write(FuFirmware *firmware, GError **error) +{ + FuEfiFirmwareSection *self = FU_EFI_FIRMWARE_SECTION(firmware); + FuEfiFirmwareSectionPrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + /* simple blob for now */ + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return NULL; + + /* header */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* will fixup */ + if (priv->type == FU_EFI_FIRMWARE_SECTION_TYPE_GUID_DEFINED) { + fwupd_guid_t guid = {0x0}; + if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return NULL; + g_byte_array_append(buf, (guint8 *)&guid, sizeof(guid)); + fu_byte_array_append_uint16(buf, buf->len + 0x4, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); + } + + /* correct size and type in common header */ + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, /* uint24_t! */ + FU_EFI_FIRMWARE_SECTION_OFFSET_SIZE, + buf->len + g_bytes_get_size(blob), + G_LITTLE_ENDIAN, + error)) + return NULL; + buf->data[FU_EFI_FIRMWARE_SECTION_OFFSET_TYPE] = priv->type; + + /* blob */ + fu_byte_array_append_bytes(buf, blob); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_efi_firmware_section_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuEfiFirmwareSection *self = FU_EFI_FIRMWARE_SECTION(firmware); + FuEfiFirmwareSectionPrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + /* simple properties */ + tmp = xb_node_query_text_as_uint(n, "type", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->type = tmp; + + /* success */ + return TRUE; +} + +static void +fu_efi_firmware_section_init(FuEfiFirmwareSection *self) +{ + FuEfiFirmwareSectionPrivate *priv = GET_PRIVATE(self); + priv->type = FU_EFI_FIRMWARE_SECTION_TYPE_RAW; + // fu_firmware_set_alignment (FU_FIRMWARE (self), FU_FIRMWARE_ALIGNMENT_8); +} + +static void +fu_efi_firmware_section_class_init(FuEfiFirmwareSectionClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_efi_firmware_section_parse; + klass_firmware->write = fu_efi_firmware_section_write; + klass_firmware->build = fu_efi_firmware_section_build; + klass_firmware->export = fu_efi_firmware_section_export; +} + +/** + * fu_efi_firmware_section_new: + * + * Creates a new #FuFirmware + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_efi_firmware_section_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FIRMWARE_SECTION, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-section.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-section.h new file mode 100644 index 0000000000000000000000000000000000000000..fffbff3bfa6141d18930c7ad23d8774efb83b180 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-section.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_FIRMWARE_SECTION (fu_efi_firmware_section_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuEfiFirmwareSection, + fu_efi_firmware_section, + FU, + EFI_FIRMWARE_SECTION, + FuFirmware) + +struct _FuEfiFirmwareSectionClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_efi_firmware_section_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-volume.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-volume.c new file mode 100644 index 0000000000000000000000000000000000000000..d88b65bdd0a3bb4dfb2f1492566ef893601390b2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-volume.c @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-efi-common.h" +#include "fu-efi-firmware-filesystem.h" +#include "fu-efi-firmware-volume.h" +#include "fu-mem.h" + +/** + * FuEfiFirmwareVolume: + * + * A UEFI file volume. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint16 attrs; +} FuEfiFirmwareVolumePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuEfiFirmwareVolume, fu_efi_firmware_volume, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_efi_firmware_volume_get_instance_private(o)) + +#define FU_EFI_FIRMWARE_VOLUME_SIGNATURE 0x4856465F +#define FU_EFI_FIRMWARE_VOLUME_REVISION 0x02 + +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_ZERO_VECTOR 0x00 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_GUID 0x10 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_LENGTH 0x20 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_SIGNATURE 0x28 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_ATTRS 0x2C +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_HDR_LEN 0x30 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_CHECKSUM 0x32 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_EXT_HDR 0x34 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_RESERVED 0x36 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_REVISION 0x37 +#define FU_EFI_FIRMWARE_VOLUME_OFFSET_BLOCK_MAP 0x38 +#define FU_EFI_FIRMWARE_VOLUME_SIZE 0x40 + +static void +fu_ifd_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuEfiFirmwareVolume *self = FU_EFI_FIRMWARE_VOLUME(firmware); + FuEfiFirmwareVolumePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "attrs", priv->attrs); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kv(bn, + "name", + fu_efi_guid_to_name(fu_firmware_get_id(firmware))); + } +} + +static gboolean +fu_efi_firmware_volume_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_SIGNATURE, + &magic, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != FU_EFI_FIRMWARE_VOLUME_SIGNATURE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "EFI FV signature invalid, got 0x%x, expected 0x%x", + magic, + (guint)FU_EFI_FIRMWARE_VOLUME_SIGNATURE); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_efi_firmware_volume_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuEfiFirmwareVolume *self = FU_EFI_FIRMWARE_VOLUME(firmware); + FuEfiFirmwareVolumePrivate *priv = GET_PRIVATE(self); + fwupd_guid_t guid = {0x0}; + gsize blockmap_sz = 0; + gsize bufsz = 0; + guint16 checksum = 0; + guint16 ext_hdr = 0; + guint16 hdr_length = 0; + guint32 attrs = 0; + guint64 fv_length = 0; + guint8 alignment; + guint8 revision = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *guid_str = NULL; + g_autoptr(GBytes) blob = NULL; + + /* guid */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_GUID, /* src */ + sizeof(guid), + error)) { + g_prefix_error(error, "failed to read GUID: "); + return FALSE; + } + guid_str = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + g_debug("volume GUID: %s [%s]", guid_str, fu_efi_guid_to_name(guid_str)); + + /* length */ + if (!fu_memread_uint64_safe(buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_LENGTH, + &fv_length, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read length: "); + return FALSE; + } + if (fv_length == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid volume length"); + return FALSE; + } + fu_firmware_set_size(firmware, fv_length); + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_ATTRS, + &attrs, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read attrs: "); + return FALSE; + } + alignment = (attrs & 0x00ff0000) >> 16; + if (alignment > FU_FIRMWARE_ALIGNMENT_2G) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "0x%x invalid, maximum is 0x%x", + (guint)alignment, + (guint)FU_FIRMWARE_ALIGNMENT_2G); + return FALSE; + } + fu_firmware_set_alignment(firmware, alignment); + priv->attrs = attrs & 0xffff; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_HDR_LEN, + &hdr_length, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read hdr_length: "); + return FALSE; + } + if (hdr_length < FU_EFI_FIRMWARE_VOLUME_SIZE || hdr_length > fv_length) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid volume header length"); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_CHECKSUM, + &checksum, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read checksum: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_EXT_HDR, + &ext_hdr, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read ext_hdr: "); + return FALSE; + } + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + FU_EFI_FIRMWARE_VOLUME_OFFSET_REVISION, + &revision, + error)) + return FALSE; + if (revision != FU_EFI_FIRMWARE_VOLUME_REVISION) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "revision invalid, got 0x%x, expected 0x%x", + revision, + (guint)FU_EFI_FIRMWARE_VOLUME_REVISION); + return FALSE; + } + + /* verify checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint16 checksum_verify = 0; + for (guint j = 0; j < hdr_length; j += sizeof(guint16)) { + guint16 checksum_tmp = 0; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + j, + &checksum_tmp, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to hdr checksum 0x%x: ", j); + return FALSE; + } + checksum_verify += checksum_tmp; + } + if (checksum_verify != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, got %02x, expected %02x", + checksum_verify, + checksum); + return FALSE; + } + } + + /* add image */ + blob = fu_bytes_new_offset(fw, offset + hdr_length, fv_length - hdr_length, error); + if (blob == NULL) + return FALSE; + fu_firmware_set_offset(firmware, offset); + fu_firmware_set_id(firmware, guid_str); + fu_firmware_set_size(firmware, fv_length); + + /* parse, which might cascade and do something like FFS2 */ + if (g_strcmp0(guid_str, FU_EFI_FIRMWARE_VOLUME_GUID_FFS2) == 0) { + g_autoptr(FuFirmware) img = fu_efi_firmware_filesystem_new(); + fu_firmware_set_alignment(img, fu_firmware_get_alignment(firmware)); + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + fu_firmware_add_image(firmware, img); + } else { + fu_firmware_set_bytes(firmware, blob); + } + + /* skip the blockmap */ + offset += FU_EFI_FIRMWARE_VOLUME_OFFSET_BLOCK_MAP; + while (offset < bufsz) { + guint32 num_blocks = 0; + guint32 length = 0; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset, + &num_blocks, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + sizeof(guint32), + &length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + offset += 2 * sizeof(guint32); + if (num_blocks == 0x0 && length == 0x0) + break; + blockmap_sz += (gsize)num_blocks * (gsize)length; + } + if (blockmap_sz < (gsize)fv_length) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "blocks allocated is less than volume length"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_efi_firmware_volume_write(FuFirmware *firmware, GError **error) +{ + FuEfiFirmwareVolume *self = FU_EFI_FIRMWARE_VOLUME(firmware); + FuEfiFirmwareVolumePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + fwupd_guid_t guid = {0x0}; + guint16 checksum = 0; + guint32 hdr_length = 0x48; + guint64 fv_length; + g_autoptr(GBytes) img_blob = NULL; + g_autoptr(FuFirmware) img = NULL; + + /* sanity check */ + if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "alignment invalid, got 0x%02x", + fu_firmware_get_alignment(firmware)); + return NULL; + } + + /* zero vector */ + for (guint i = 0; i < 0x10; i++) + fu_byte_array_append_uint8(buf, 0x0); + + /* GUID */ + if (fu_firmware_get_id(firmware) == NULL) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no GUID set for EFI FV"); + return NULL; + } + if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return NULL; + g_byte_array_append(buf, (const guint8 *)&guid, sizeof(guid)); + + /* length */ + img = fu_firmware_get_image_by_id(firmware, NULL, NULL); + if (img != NULL) { + img_blob = fu_firmware_write(img, error); + if (img_blob == NULL) { + g_prefix_error(error, "no EFI FV child payload: "); + return NULL; + } + } else { + img_blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (img_blob == NULL) { + g_prefix_error(error, "no EFI FV payload: "); + return NULL; + } + } + fv_length = fu_common_align_up(hdr_length + g_bytes_get_size(img_blob), + fu_firmware_get_alignment(firmware)); + fu_byte_array_append_uint64(buf, fv_length, G_LITTLE_ENDIAN); + + /* signature */ + fu_byte_array_append_uint32(buf, FU_EFI_FIRMWARE_VOLUME_SIGNATURE, G_LITTLE_ENDIAN); + + /* attributes */ + fu_byte_array_append_uint32(buf, + priv->attrs | + ((guint32)fu_firmware_get_alignment(firmware) << 16), + G_LITTLE_ENDIAN); + + /* header length */ + fu_byte_array_append_uint16(buf, hdr_length, G_LITTLE_ENDIAN); + + /* checksum (will fixup) */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); + + /* ext header offset */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); + + /* reserved */ + fu_byte_array_append_uint8(buf, 0x0); + + /* revision */ + fu_byte_array_append_uint8(buf, FU_EFI_FIRMWARE_VOLUME_REVISION); + + /* blockmap */ + fu_byte_array_append_uint32(buf, fv_length, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x1, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); + + /* fix up checksum */ + for (guint j = buf->len - hdr_length; j < buf->len; j += sizeof(guint16)) { + guint16 checksum_tmp = 0; + if (!fu_memread_uint16_safe(buf->data, + buf->len, + j, + &checksum_tmp, + G_LITTLE_ENDIAN, + error)) + return NULL; + checksum += checksum_tmp; + } + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + buf->len - 0x16, + 0x10000 - checksum, + G_LITTLE_ENDIAN, + error)) + return NULL; + + /* pad contents to alignment */ + fu_byte_array_append_bytes(buf, img_blob); + fu_byte_array_set_size(buf, fv_length, 0xFF); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_efi_firmware_volume_init(FuEfiFirmwareVolume *self) +{ + FuEfiFirmwareVolumePrivate *priv = GET_PRIVATE(self); + priv->attrs = 0xfeff; +} + +static void +fu_efi_firmware_volume_class_init(FuEfiFirmwareVolumeClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_efi_firmware_volume_check_magic; + klass_firmware->parse = fu_efi_firmware_volume_parse; + klass_firmware->write = fu_efi_firmware_volume_write; + klass_firmware->export = fu_ifd_firmware_export; +} + +/** + * fu_efi_firmware_volume_new: + * + * Creates a new #FuFirmware + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_efi_firmware_volume_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FIRMWARE_VOLUME, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-volume.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-volume.h new file mode 100644 index 0000000000000000000000000000000000000000..e6fe8eb7f4d6d0db0cf2e86b6e4909d45fc28ada --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-firmware-volume.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_FIRMWARE_VOLUME (fu_efi_firmware_volume_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuEfiFirmwareVolume, + fu_efi_firmware_volume, + FU, + EFI_FIRMWARE_VOLUME, + FuFirmware) + +struct _FuEfiFirmwareVolumeClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_efi_firmware_volume_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-list.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-list.c new file mode 100644 index 0000000000000000000000000000000000000000..c6073797ed4b582257e25eaad583c54934940bd0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-list.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-efi-signature-list.h" +#include "fu-efi-signature-private.h" +#include "fu-mem.h" + +/** + * FuEfiSignatureList: + * + * A UEFI signature list typically found in the `PK` and `KEK` keys. + * + * See also: [class@FuFirmware] + */ + +struct _FuEfiSignatureList { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuEfiSignatureList, fu_efi_signature_list, FU_TYPE_FIRMWARE) + +const guint8 FU_EFI_SIGLIST_HEADER_MAGIC[] = {0x26, 0x16, 0xC4, 0xC1, 0x4C}; + +static gboolean +fu_efi_signature_list_parse_item(FuEfiSignatureList *self, + FuEfiSignatureKind sig_kind, + const guint8 *buf, + gsize bufsz, + gsize offset, + guint32 sig_size, + GError **error) +{ + fwupd_guid_t guid; + gsize sig_datasz; + g_autofree gchar *sig_owner = NULL; + g_autofree guint8 *sig_data = NULL; + g_autoptr(FuEfiSignature) sig = NULL; + g_autoptr(GBytes) data = NULL; + + /* allocate data buf */ + if (sig_size <= sizeof(fwupd_guid_t)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "SignatureSize invalid: 0x%x", + (guint)sig_size); + return FALSE; + } + sig_datasz = sig_size - sizeof(fwupd_guid_t); + sig_data = g_malloc0(sig_datasz); + + /* read both blocks of data */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(guid), + error)) { + g_prefix_error(error, "failed to read signature GUID: "); + return FALSE; + } + if (!fu_memcpy_safe(sig_data, + sig_datasz, + 0x0, /* dst */ + buf, + bufsz, + offset + sizeof(fwupd_guid_t), /* src */ + sig_datasz, + error)) { + g_prefix_error(error, "failed to read signature data: "); + return FALSE; + } + + /* create item */ + sig_owner = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + data = g_bytes_new(sig_data, sig_datasz); + sig = fu_efi_signature_new(sig_kind, sig_owner); + fu_firmware_set_bytes(FU_FIRMWARE(sig), data); + fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(sig)); + return TRUE; +} + +static gboolean +fu_efi_signature_list_parse_list(FuEfiSignatureList *self, + const guint8 *buf, + gsize bufsz, + gsize *offset, + GError **error) +{ + FuEfiSignatureKind sig_kind = FU_EFI_SIGNATURE_KIND_UNKNOWN; + fwupd_guid_t guid; + gsize offset_tmp; + guint32 sig_header_size = 0; + guint32 sig_list_size = 0; + guint32 sig_size = 0; + g_autofree gchar *sig_type = NULL; + + /* read EFI_SIGNATURE_LIST */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + *offset, /* src */ + sizeof(guid), + error)) { + g_prefix_error(error, "failed to read GUID header: "); + return FALSE; + } + sig_type = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + if (g_strcmp0(sig_type, "c1c41626-504c-4092-aca9-41f936934328") == 0) { + sig_kind = FU_EFI_SIGNATURE_KIND_SHA256; + } else if (g_strcmp0(sig_type, "a5c059a1-94e4-4aa7-87b5-ab155c2bf072") == 0) { + sig_kind = FU_EFI_SIGNATURE_KIND_X509; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + *offset + 0x10, + &sig_list_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (sig_list_size < 0x1c || sig_list_size > 1024 * 1024) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "SignatureListSize invalid: 0x%x", + sig_list_size); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + *offset + 0x14, + &sig_header_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (sig_header_size > 1024 * 1024) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "SignatureHeaderSize invalid: 0x%x", + sig_size); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, *offset + 0x18, &sig_size, G_LITTLE_ENDIAN, error)) + return FALSE; + if (sig_size < sizeof(fwupd_guid_t) || sig_size > 1024 * 1024) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "SignatureSize invalid: 0x%x", + sig_size); + return FALSE; + } + + /* header is typically unused */ + offset_tmp = *offset + 0x1c + sig_header_size; + for (guint i = 0; i < (sig_list_size - 0x1c) / sig_size; i++) { + if (!fu_efi_signature_list_parse_item(self, + sig_kind, + buf, + bufsz, + offset_tmp, + sig_size, + error)) + return FALSE; + offset_tmp += sig_size; + } + *offset += sig_list_size; + return TRUE; +} + +static gchar * +fu_efi_signature_list_get_version(FuEfiSignatureList *self) +{ + guint csum_cnt = 0; + const gchar *ignored_guids[] = {FU_EFI_SIGNATURE_GUID_OVMF, + FU_EFI_SIGNATURE_GUID_OVMF_LEGACY, + NULL}; + g_autofree gchar *checksum_last = NULL; + g_autoptr(GPtrArray) sigs = NULL; + struct { + const gchar *checksum; + guint version; + } known_checksums[] = { + /* DBXUpdate-20100307.x64.bin */ + {"5391c3a2fb112102a6aa1edc25ae77e19f5d6f09cd09eeb2509922bfcd5992ea", 9}, + /* DBXUpdate-20140413.x64.bin */ + {"90fbe70e69d633408d3e170c6832dbb2d209e0272527dfb63d49d29572a6f44c", 13}, + /* DBXUpdate-20160809.x64.bin */ + {"45c7c8ae750acfbb48fc37527d6412dd644daed8913ccd8a24c94d856967df8e", 77}, + /* DBXUpdate-20200729.x64.bin */ + {"540801dd345dc1c33ef431b35bf4c0e68bd319b577b9abe1a9cff1cbc39f548f", 190}, + /* DBXUpdate-20200729.aa64.bin */ + {"8c8183ad9b96fe1f3c74dedb8087469227b642afe2e80f8fd22e0137c11c7d90", 19}, + /* DBXUpdate-20200729.ia32.bin */ + {"a7dfcc3a8d6ab30f93f31748dbc8ea38415cf52bb9ad8085672cd9ab8938d5de", 41}, + /* DBXUpdate-20210429.x64.bin */ + {"af79b14064601bc0987d4747af1e914a228c05d622ceda03b7a4f67014fee767", 211}, + /* DBXUpdate-20210429.aa64.bin */ + {"b133de42a37376f5d91439af3d61d38201f10377c36dacd9c2610f52aa124a91", 21}, + /* DBXUpdate-20210429.ia32.bin */ + {"a8a3300e33a0a2692839ccba84803c5e742d12501b6d58c46eb87f32017f2cff", 55}, + /* DBXUpdate-20220812.x64.bin */ + {"90aec5c4995674a849c1d1384463f3b02b5aa625a5c320fc4fe7d9bb58a62398", 217}, + /* DBXUpdate-20220812.aa64.bin - only X509 certificates removed */ + /* DBXUpdate-20220812.ia32.bin - only X509 certificates removed */ + {NULL, 0}}; + + sigs = fu_firmware_get_images(FU_FIRMWARE(self)); + for (guint i = 0; i < sigs->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i); + if (fu_efi_signature_get_kind(sig) != FU_EFI_SIGNATURE_KIND_SHA256) + continue; + if (g_strv_contains(ignored_guids, fu_efi_signature_get_owner(sig))) + continue; + + /* save the last hash in the list */ + if (i == sigs->len - 1) { + g_autoptr(GError) error_local = NULL; + checksum_last = fu_firmware_get_checksum(FU_FIRMWARE(sig), + G_CHECKSUM_SHA256, + &error_local); + if (checksum_last == NULL) { + g_warning("failed to get checksum for signature %u: %s", + i, + error_local->message); + } + } + + csum_cnt++; + } + + /* + * Microsoft seems to actually remove checksums in UEFI dbx updates, which I'm guessing is + * a result from OEM pressure about SPI usage -- but local dbx updates are append-only. + * + * That means that if you remove hashes then you can have a different set of dbx checksums + * on your machine depending on whether you went A->B->C->D or A->D... + * + * If we use the metric of "count the number of SHA256 checksums from MS" then we might + * overcount (due to the now-removed entries) -- in some cases enough to not actually apply + * the new update at all. + * + * In these cases look at the *last* dbx checksum and compare to the set we know to see if + * we need to artificially lower the reported version. This isn't helped by having *zero* + * visibility in the reason that entries were removed or added. + * + * This also fixes the DBX update 20200729 which added 4 duplicate entries, which should be + * rectified during the SetVariable(), so they're only really a problem for transactional + * size limits. But we all noticed that load-bearing *should* word there, didn't we. + */ + for (guint i = 0; checksum_last != NULL && known_checksums[i].checksum != NULL; i++) { + if (g_strcmp0(checksum_last, known_checksums[i].checksum) == 0) { + if (csum_cnt != known_checksums[i].version) { + g_debug("fixing signature list version from %u to %u as " + "last dbx checksum was %s", + csum_cnt, + known_checksums[i].version, + checksum_last); + csum_cnt = known_checksums[i].version; + } + break; + } + } + + return g_strdup_printf("%u", csum_cnt); +} + +static gboolean +fu_efi_signature_list_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + fwupd_guid_t guid = {0x0}; + g_autofree gchar *sig_type = NULL; + + /* read EFI_SIGNATURE_LIST */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, /* src */ + sizeof(guid), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + sig_type = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + if (g_strcmp0(sig_type, "c1c41626-504c-4092-aca9-41f936934328") != 0 && + g_strcmp0(sig_type, "a5c059a1-94e4-4aa7-87b5-ab155c2bf072") != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic for file"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_efi_signature_list_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuEfiSignatureList *self = FU_EFI_SIGNATURE_LIST(firmware); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *version_str = NULL; + + /* parse each EFI_SIGNATURE_LIST */ + while (offset < bufsz) { + if (!fu_efi_signature_list_parse_list(self, buf, bufsz, &offset, error)) + return FALSE; + } + + /* set version */ + version_str = fu_efi_signature_list_get_version(self); + if (version_str != NULL) + fu_firmware_set_version(firmware, version_str); + + /* success */ + return TRUE; +} + +static GBytes * +fu_efi_signature_list_write(FuFirmware *firmware, GError **error) +{ + GByteArray *buf = g_byte_array_new(); + + /* SignatureType */ + for (guint i = 0; i < 16; i++) + fu_byte_array_append_uint8(buf, 0x0); + + /* SignatureListSize */ + fu_byte_array_append_uint32(buf, 16 + 4 + 4 + 4 + 16 + 32, G_LITTLE_ENDIAN); + + /* SignatureHeaderSize */ + fu_byte_array_append_uint32(buf, 0, G_LITTLE_ENDIAN); + + /* SignatureSize */ + fu_byte_array_append_uint32(buf, 16 + 32, G_LITTLE_ENDIAN); + + /* SignatureOwner */ + for (guint i = 0; i < 16; i++) + fu_byte_array_append_uint8(buf, '1'); + + /* SignatureData */ + for (guint i = 0; i < 16; i++) + fu_byte_array_append_uint8(buf, '2'); + + return g_byte_array_free_to_bytes(buf); +} + +/** + * fu_efi_signature_list_new: + * + * Creates a new #FuFirmware that can parse an EFI_SIGNATURE_LIST + * + * Since: 1.5.5 + **/ +FuFirmware * +fu_efi_signature_list_new(void) +{ + return g_object_new(FU_TYPE_EFI_SIGNATURE_LIST, NULL); +} + +static void +fu_efi_signature_list_class_init(FuEfiSignatureListClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_efi_signature_list_check_magic; + klass_firmware->parse = fu_efi_signature_list_parse; + klass_firmware->write = fu_efi_signature_list_write; +} + +static void +fu_efi_signature_list_init(FuEfiSignatureList *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALWAYS_SEARCH); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-list.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-list.h new file mode 100644 index 0000000000000000000000000000000000000000..30b79b6ff64d31a09fe1099c3ce3360a39401d94 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-list.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_SIGNATURE_LIST (fu_efi_signature_list_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiSignatureList, fu_efi_signature_list, FU, EFI_SIGNATURE_LIST, FuFirmware) + +FuFirmware * +fu_efi_signature_list_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-private.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-private.h new file mode 100644 index 0000000000000000000000000000000000000000..df0fc6e123fe49bdbcb99021893e6e62f7324709 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature-private.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-efi-signature.h" + +FuEfiSignature * +fu_efi_signature_new(FuEfiSignatureKind kind, const gchar *owner); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-signature.c b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature.c new file mode 100644 index 0000000000000000000000000000000000000000..bcef802031cb546f1c547b629c30e57786df07cf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-efi-signature-private.h" + +/** + * FuEfiSignature: + * + * A UEFI Signature as found in an `EFI_SIGNATURE_LIST`. + * + * See also: [class@FuFirmware] + */ + +struct _FuEfiSignature { + FuFirmware parent_instance; + FuEfiSignatureKind kind; + gchar *owner; +}; + +G_DEFINE_TYPE(FuEfiSignature, fu_efi_signature, FU_TYPE_FIRMWARE) + +/** + * fu_efi_signature_kind_to_string: + * @kind: A #FuEfiSignatureKind, e.g. %FU_EFI_SIGNATURE_KIND_X509 + * + * Converts the signature kind to a text representation. + * + * Returns: text, e.g. `x509_cert` + * + * Since: 1.5.5 + **/ +const gchar * +fu_efi_signature_kind_to_string(FuEfiSignatureKind kind) +{ + if (kind == FU_EFI_SIGNATURE_KIND_SHA256) + return "sha256"; + if (kind == FU_EFI_SIGNATURE_KIND_X509) + return "x509_cert"; + return "unknown"; +} + +/** + * fu_efi_signature_new: (skip): + * @kind: A #FuEfiSignatureKind + * @owner: A GUID, e.g. %FU_EFI_SIGNATURE_GUID_MICROSOFT + * + * Creates a new EFI_SIGNATURE. + * + * Returns: (transfer full): signature + * + * Since: 1.5.5 + **/ +FuEfiSignature * +fu_efi_signature_new(FuEfiSignatureKind kind, const gchar *owner) +{ + g_autoptr(FuEfiSignature) self = g_object_new(FU_TYPE_EFI_SIGNATURE, NULL); + self->kind = kind; + self->owner = g_strdup(owner); + return g_steal_pointer(&self); +} + +/** + * fu_efi_signature_get_kind: + * @self: A #FuEfiSignature + * + * Returns the signature kind. + * + * Returns: #FuEfiSignatureKind, e.g. %FU_EFI_SIGNATURE_KIND_SHA256 + * + * Since: 1.5.5 + **/ +FuEfiSignatureKind +fu_efi_signature_get_kind(FuEfiSignature *self) +{ + g_return_val_if_fail(FU_IS_EFI_SIGNATURE(self), FU_EFI_SIGNATURE_KIND_UNKNOWN); + return self->kind; +} + +/** + * fu_efi_signature_get_owner: + * @self: A #FuEfiSignature + * + * Returns the GUID of the signature owner. + * + * Returns: GUID owner, perhaps %FU_EFI_SIGNATURE_GUID_MICROSOFT + * + * Since: 1.5.5 + **/ +const gchar * +fu_efi_signature_get_owner(FuEfiSignature *self) +{ + g_return_val_if_fail(FU_IS_EFI_SIGNATURE(self), NULL); + return self->owner; +} + +static gchar * +fu_efi_signature_get_checksum(FuFirmware *firmware, GChecksumType csum_kind, GError **error) +{ + FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); + g_autoptr(GBytes) data = fu_firmware_get_bytes_with_patches(firmware, error); + if (data == NULL) + return NULL; + + /* special case: this is *literally* a hash */ + if (self->kind == FU_EFI_SIGNATURE_KIND_SHA256 && csum_kind == G_CHECKSUM_SHA256) { + GString *str; + const guint8 *buf; + gsize bufsz = 0; + buf = g_bytes_get_data(data, &bufsz); + str = g_string_new(NULL); + for (gsize i = 0; i < bufsz; i++) + g_string_append_printf(str, "%02x", buf[i]); + return g_string_free(str, FALSE); + } + + /* fallback */ + return g_compute_checksum_for_bytes(csum_kind, data); +} + +static void +fu_efi_signature_finalize(GObject *obj) +{ + FuEfiSignature *self = FU_EFI_SIGNATURE(obj); + g_free(self->owner); + G_OBJECT_CLASS(fu_efi_signature_parent_class)->finalize(obj); +} + +static void +fu_efi_signature_class_init(FuEfiSignatureClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_efi_signature_finalize; + firmware_class->get_checksum = fu_efi_signature_get_checksum; +} + +static void +fu_efi_signature_init(FuEfiSignature *self) +{ +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efi-signature.h b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature.h new file mode 100644 index 0000000000000000000000000000000000000000..87a430e74b045296356f9ee24c435a52b7aef649 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efi-signature.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_SIGNATURE (fu_efi_signature_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiSignature, fu_efi_signature, FU, EFI_SIGNATURE, FuFirmware) + +/** + * FuEfiSignatureKind: + * @FU_EFI_SIGNATURE_KIND_UNKNOWN: No signature + * @FU_EFI_SIGNATURE_KIND_SHA256: SHA256 hash + * @FU_EFI_SIGNATURE_KIND_X509: X509 certificate + * + * The kind of EFI signature. + **/ +typedef enum { + FU_EFI_SIGNATURE_KIND_UNKNOWN, + FU_EFI_SIGNATURE_KIND_SHA256, + FU_EFI_SIGNATURE_KIND_X509, + /*< private >*/ + FU_EFI_SIGNATURE_KIND_LAST +} FuEfiSignatureKind; + +#define FU_EFI_SIGNATURE_GUID_ZERO "00000000-0000-0000-0000-000000000000" +#define FU_EFI_SIGNATURE_GUID_MICROSOFT "77fa9abd-0359-4d32-bd60-28f4e78f784b" +#define FU_EFI_SIGNATURE_GUID_OVMF "a0baa8a3-041d-48a8-bc87-c36d121b5e3d" +#define FU_EFI_SIGNATURE_GUID_OVMF_LEGACY "d5c1df0b-1bac-4edf-ba48-08834009ca5a" + +const gchar * +fu_efi_signature_kind_to_string(FuEfiSignatureKind kind); + +FuEfiSignatureKind +fu_efi_signature_get_kind(FuEfiSignature *self); +const gchar * +fu_efi_signature_get_owner(FuEfiSignature *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar-darwin.c b/fwupd-1.8.6/libfwupdplugin/fu-efivar-darwin.c new file mode 100644 index 0000000000000000000000000000000000000000..c015670c21352b775298d81234d8311bf5b05560 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar-darwin.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-error.h" + +#include "fu-efivar-impl.h" + +gboolean +fu_efivar_supported_impl(GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return FALSE; +} + +gboolean +fu_efivar_delete_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return FALSE; +} + +gboolean +fu_efivar_delete_with_glob_impl(const gchar *guid, const gchar *name_glob, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return FALSE; +} + +gboolean +fu_efivar_exists_impl(const gchar *guid, const gchar *name) +{ + return FALSE; +} + +gboolean +fu_efivar_get_data_impl(const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return FALSE; +} + +GPtrArray * +fu_efivar_get_names_impl(const gchar *guid, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return NULL; +} + +GFileMonitor * +fu_efivar_get_monitor_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return NULL; +} + +guint64 +fu_efivar_space_used_impl(GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return G_MAXUINT64; +} + +gboolean +fu_efivar_set_data_impl(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on darwin"); + return FALSE; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar-freebsd.c b/fwupd-1.8.6/libfwupdplugin/fu-efivar-freebsd.c new file mode 100644 index 0000000000000000000000000000000000000000..fe91fee0e5b78c5707228f6a35295165ec314046 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar-freebsd.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 Norbert Kamiński + * Copyright (C) 2021 Michał Kopeć + * Copyright (C) 2021 Sergii Dmytruk + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fwupd-error.h" + +#include "fu-common.h" +#include "fu-efivar-impl.h" +#include "fu-path.h" + +gboolean +fu_efivar_supported_impl(GError **error) +{ + if (efi_variables_supported() == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "kernel efivars support missing"); + return FALSE; + } + return TRUE; +} + +gboolean +fu_efivar_delete_impl(const gchar *guid, const gchar *name, GError **error) +{ + efi_guid_t guidt; + efi_str_to_guid(guid, &guidt); + + if (efi_del_variable(guidt, name) == 0) + return TRUE; + + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "failed to delete efivar %s", name); + return FALSE; +} + +gboolean +fu_efivar_delete_with_glob_impl(const gchar *guid, const gchar *name_glob, GError **error) +{ + efi_guid_t *guidt = NULL; + gchar *name = NULL; + gboolean rv = FALSE; + efi_guid_t guid_to_delete; + + efi_str_to_guid(guid, &guid_to_delete); + + while (efi_get_next_variable_name(&guidt, &name)) { + if (memcmp(&guid_to_delete, guidt, sizeof(guid_to_delete)) != 0) + continue; + if (!fu_path_fnmatch(name, name_glob)) + continue; + rv = fu_efivar_delete(guid, name, error); + if (!rv) + break; + } + return rv; +} + +static gboolean +fu_efivar_exists_guid(const gchar *guid) +{ + efi_guid_t *guidt = NULL; + gchar *name = NULL; + efi_guid_t test; + + efi_str_to_guid(guid, &test); + while (efi_get_next_variable_name(&guidt, &name)) { + if (memcmp(&test, guidt, sizeof(test)) == 0) { + return TRUE; + } + } + return FALSE; +} + +gboolean +fu_efivar_exists_impl(const gchar *guid, const gchar *name) +{ + /* any name */ + if (name == NULL) + return fu_efivar_exists_guid(guid); + + return fu_efivar_get_data(guid, name, NULL, NULL, NULL, NULL); +} + +gboolean +fu_efivar_get_data_impl(const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error) +{ + efi_guid_t guidt; + efi_str_to_guid(guid, &guidt); + return (efi_get_variable(guidt, name, data, data_sz, attr) != 0); +} + +GPtrArray * +fu_efivar_get_names_impl(const gchar *guid, GError **error) +{ + g_autoptr(GPtrArray) names = g_ptr_array_new_with_free_func(g_free); + efi_guid_t *guidt = NULL; + gchar *name = NULL; + efi_guid_t test; + efi_str_to_guid(guid, &test); + + /* find names with matching GUID */ + while (efi_get_next_variable_name(&guidt, &name)) { + if (memcmp(&test, guidt, sizeof(test)) == 0) { + g_ptr_array_add(names, g_strdup(name)); + } + } + + /* nothing found */ + if (names->len == 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no names for GUID %s", guid); + return NULL; + } + + /* success */ + return g_steal_pointer(&names); +} + +GFileMonitor * +fu_efivar_get_monitor_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs monitoring not supported on FreeBSD"); + return NULL; +} + +guint64 +fu_efivar_space_used_impl(GError **error) +{ + guint64 total = 0; + efi_guid_t *guidt = NULL; + char *name = NULL; + + while (efi_get_next_variable_name(&guidt, &name)) { + size_t size = 0; + if (efi_get_variable_size(*guidt, name, &size) < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to get efivar size"); + return G_MAXUINT64; + } + total += size; + } + + /* success */ + return total; +} + +gboolean +fu_efivar_set_data_impl(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error) +{ + efi_guid_t guidt; + efi_str_to_guid(guid, &guidt); + + if (efi_set_variable(guidt, name, (guint8 *)data, sz, attr) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to write data to efivar %s", + name); + return FALSE; + } + + /* success */ + return TRUE; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar-impl.h b/fwupd-1.8.6/libfwupdplugin/fu-efivar-impl.h new file mode 100644 index 0000000000000000000000000000000000000000..cb3221898fff1b3eb4a8542fc42e8becbb2aa5a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar-impl.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-efivar.h" + +gboolean +fu_efivar_supported_impl(GError **error); +guint64 +fu_efivar_space_used_impl(GError **error); +gboolean +fu_efivar_exists_impl(const gchar *guid, const gchar *name); +GFileMonitor * +fu_efivar_get_monitor_impl(const gchar *guid, const gchar *name, GError **error); +gboolean +fu_efivar_get_data_impl(const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error); +gboolean +fu_efivar_set_data_impl(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error); +gboolean +fu_efivar_delete_impl(const gchar *guid, const gchar *name, GError **error); +gboolean +fu_efivar_delete_with_glob_impl(const gchar *guid, const gchar *name_glob, GError **error); +GPtrArray * +fu_efivar_get_names_impl(const gchar *guid, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar-linux.c b/fwupd-1.8.6/libfwupdplugin/fu-efivar-linux.c new file mode 100644 index 0000000000000000000000000000000000000000..83bfaf1f21f9f9753f52ef7d189e5647194028b7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar-linux.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "fwupd-error.h" + +#include "fu-common.h" +#include "fu-efivar-impl.h" +#include "fu-path.h" + +static gchar * +fu_efivar_get_path(void) +{ + g_autofree gchar *sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + return g_build_filename(sysfsfwdir, "efi", "efivars", NULL); +} + +static gchar * +fu_efivar_get_filename(const gchar *guid, const gchar *name) +{ + g_autofree gchar *efivardir = fu_efivar_get_path(); + return g_strdup_printf("%s/%s-%s", efivardir, name, guid); +} + +gboolean +fu_efivar_supported_impl(GError **error) +{ + g_autofree gchar *efivardir = fu_efivar_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_efivar_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_efivar_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); + if (istr == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to create stream"); + return FALSE; + } + return fu_efivar_set_immutable_fd(fd, value, value_old, error); +} + +gboolean +fu_efivar_delete_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + + fn = fu_efivar_get_filename(guid, name); + file = g_file_new_for_path(fn); + if (!g_file_query_exists(file, NULL)) + return TRUE; + if (!fu_efivar_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_efivar_delete_with_glob_impl(const gchar *guid, const gchar *name_glob, GError **error) +{ + const gchar *fn; + g_autofree gchar *nameguid_glob = NULL; + g_autofree gchar *efivardir = fu_efivar_get_path(); + g_autoptr(GDir) dir = NULL; + + 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 (fu_path_fnmatch(nameguid_glob, fn)) { + g_autofree gchar *keyfn = g_build_filename(efivardir, fn, NULL); + g_autoptr(GFile) file = g_file_new_for_path(keyfn); + if (!fu_efivar_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; +} + +static gboolean +fu_efivar_exists_guid(const gchar *guid) +{ + const gchar *fn; + g_autofree gchar *efivardir = fu_efivar_get_path(); + g_autoptr(GDir) dir = NULL; + + dir = g_dir_open(efivardir, 0, NULL); + if (dir == NULL) + return FALSE; + while ((fn = g_dir_read_name(dir)) != NULL) { + if (g_str_has_suffix(fn, guid)) + return TRUE; + } + return TRUE; +} + +gboolean +fu_efivar_exists_impl(const gchar *guid, const gchar *name) +{ + g_autofree gchar *fn = NULL; + + /* any name */ + if (name == NULL) + return fu_efivar_exists_guid(guid); + + fn = fu_efivar_get_filename(guid, name); + return g_file_test(fn, G_FILE_TEST_EXISTS); +} + +gboolean +fu_efivar_get_data_impl(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 = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + g_autoptr(GInputStream) istr = NULL; + + /* open file as stream */ + fn = fu_efivar_get_filename(guid, name); + file = g_file_new_for_path(fn); + 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_tmp == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "no data to read"); + return FALSE; + } + 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; +} + +GPtrArray * +fu_efivar_get_names_impl(const gchar *guid, GError **error) +{ + const gchar *name_guid; + g_autofree gchar *path = fu_efivar_get_path(); + g_autoptr(GDir) dir = NULL; + g_autoptr(GPtrArray) names = g_ptr_array_new_with_free_func(g_free); + + /* find names with matching GUID */ + dir = g_dir_open(path, 0, error); + if (dir == NULL) + return NULL; + while ((name_guid = g_dir_read_name(dir)) != NULL) { + gsize name_guidsz = strlen(name_guid); + if (name_guidsz < 38) + continue; + if (g_strcmp0(name_guid + name_guidsz - 36, guid) == 0) { + g_ptr_array_add(names, g_strndup(name_guid, name_guidsz - 37)); + } + } + + /* nothing found */ + if (names->len == 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no names for GUID %s", guid); + return NULL; + } + + /* success */ + return g_steal_pointer(&names); +} + +GFileMonitor * +fu_efivar_get_monitor_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileMonitor) monitor = NULL; + + fn = fu_efivar_get_filename(guid, name); + file = g_file_new_for_path(fn); + monitor = g_file_monitor_file(file, G_FILE_MONITOR_NONE, NULL, error); + if (monitor == NULL) + return NULL; + g_file_monitor_set_rate_limit(monitor, 5000); + return g_steal_pointer(&monitor); +} + +guint64 +fu_efivar_space_used_impl(GError **error) +{ + const gchar *fn; + guint64 total = 0; + g_autoptr(GDir) dir = NULL; + g_autofree gchar *path = fu_efivar_get_path(); + + /* stat each file */ + dir = g_dir_open(path, 0, error); + if (dir == NULL) + return G_MAXUINT64; + while ((fn = g_dir_read_name(dir)) != NULL) { + guint64 sz; + g_autofree gchar *pathfn = g_build_filename(path, fn, NULL); + g_autoptr(GFile) file = g_file_new_for_path(pathfn); + g_autoptr(GFileInfo) info = NULL; + + info = g_file_query_info(file, + G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE + "," G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, + error); + if (info == NULL) + return G_MAXUINT64; + sz = g_file_info_get_attribute_uint64(info, + G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE); + if (sz == 0) + sz = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_STANDARD_SIZE); + total += sz; + } + + /* success */ + return total; +} + +gboolean +fu_efivar_set_data_impl(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error) +{ + int fd; + int open_wflags; + gboolean was_immutable; + g_autofree gchar *fn = fu_efivar_get_filename(guid, name); + g_autofree guint8 *buf = g_malloc0(sizeof(guint32) + 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)) { + g_prefix_error(error, "failed to touch efivarfs: "); + return FALSE; + } + } + if (!fu_efivar_set_immutable(fn, FALSE, &was_immutable, error)) { + g_prefix_error(error, "failed to set %s as mutable: ", fn); + return FALSE; + } + + /* open file for writing, optionally append */ + open_wflags = O_WRONLY; + if (attr & FU_EFIVAR_ATTR_APPEND_WRITE) + open_wflags |= O_APPEND; + fd = open(fn, open_wflags); + 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, sz); + if (g_output_stream_write(ostr, buf, sizeof(attr) + sz, NULL, error) < 0) { + g_prefix_error(error, "failed to write data to efivarfs: "); + return FALSE; + } + + /* set as immutable again */ + if (was_immutable && !fu_efivar_set_immutable(fn, TRUE, NULL, error)) { + g_prefix_error(error, "failed to set %s as immutable: ", fn); + return FALSE; + } + + /* success */ + return TRUE; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar-windows.c b/fwupd-1.8.6/libfwupdplugin/fu-efivar-windows.c new file mode 100644 index 0000000000000000000000000000000000000000..1d4558229d4f5db28b288435937bf42edc66b9e7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar-windows.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-error.h" + +#include "fu-efivar-impl.h" + +gboolean +fu_efivar_supported_impl(GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return FALSE; +} + +gboolean +fu_efivar_delete_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return FALSE; +} + +gboolean +fu_efivar_delete_with_glob_impl(const gchar *guid, const gchar *name_glob, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return FALSE; +} + +gboolean +fu_efivar_exists_impl(const gchar *guid, const gchar *name) +{ + return FALSE; +} + +gboolean +fu_efivar_get_data_impl(const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return FALSE; +} + +GPtrArray * +fu_efivar_get_names_impl(const gchar *guid, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return NULL; +} + +GFileMonitor * +fu_efivar_get_monitor_impl(const gchar *guid, const gchar *name, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return NULL; +} + +guint64 +fu_efivar_space_used_impl(GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return G_MAXUINT64; +} + +gboolean +fu_efivar_set_data_impl(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "efivarfs not currently supported on Windows"); + return FALSE; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar.c b/fwupd-1.8.6/libfwupdplugin/fu-efivar.c new file mode 100644 index 0000000000000000000000000000000000000000..e41e0809ba4d3175dc59095fb2f5ebd02edb79ee --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fwupd-error.h" + +#include "fu-efivar-impl.h" + +/** + * fu_efivar_supported: + * @error: #GError + * + * Determines if the kernel supports EFI variables + * + * Returns: %TRUE on success + * + * Since: 1.4.0 + **/ +gboolean +fu_efivar_supported(GError **error) +{ + return fu_efivar_supported_impl(error); +} + +/** + * fu_efivar_delete: + * @guid: Globally unique identifier + * @name: Variable name + * @error: #GError + * + * Removes a variable from NVRAM + * + * Returns: %TRUE on success + * + * Since: 1.4.0 + **/ +gboolean +fu_efivar_delete(const gchar *guid, const gchar *name, GError **error) +{ + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return fu_efivar_delete_impl(guid, name, error); +} + +/** + * fu_efivar_delete_with_glob: + * @guid: Globally unique identifier + * @name_glob: Variable name + * @error: #GError + * + * Removes a group of variables from NVRAM + * + * Returns: %TRUE on success + * + * Since: 1.4.0 + **/ +gboolean +fu_efivar_delete_with_glob(const gchar *guid, const gchar *name_glob, GError **error) +{ + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(name_glob != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return fu_efivar_delete_with_glob_impl(guid, name_glob, error); +} + +/** + * fu_efivar_exists: + * @guid: Globally unique identifier + * @name: (nullable): Variable name + * + * Test if a variable exists + * + * Returns: %TRUE on success + * + * Since: 1.4.0 + **/ +gboolean +fu_efivar_exists(const gchar *guid, const gchar *name) +{ + g_return_val_if_fail(guid != NULL, FALSE); + return fu_efivar_exists_impl(guid, name); +} + +/** + * fu_efivar_get_data: + * @guid: Globally unique identifier + * @name: Variable name + * @data: Data to set + * @data_sz: size of data + * @attr: Attributes + * @error: (nullable): optional return location for an error + * + * Gets the data from a UEFI variable in NVRAM + * + * Returns: %TRUE on success + * + * Since: 1.4.0 + **/ +gboolean +fu_efivar_get_data(const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error) +{ + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return fu_efivar_get_data_impl(guid, name, data, data_sz, attr, error); +} + +/** + * fu_efivar_get_data_bytes: + * @guid: Globally unique identifier + * @name: Variable name + * @attr: (nullable): Attributes + * @error: (nullable): optional return location for an error + * + * Gets the data from a UEFI variable in NVRAM + * + * Returns: (transfer full): a #GBytes, or %NULL + * + * Since: 1.5.0 + **/ +GBytes * +fu_efivar_get_data_bytes(const gchar *guid, const gchar *name, guint32 *attr, GError **error) +{ + guint8 *data = NULL; + gsize datasz = 0; + + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (!fu_efivar_get_data(guid, name, &data, &datasz, attr, error)) + return NULL; + return g_bytes_new_take(data, datasz); +} + +/** + * fu_efivar_get_names: + * @guid: Globally unique identifier + * @error: (nullable): optional return location for an error + * + * Gets the list of names where the GUID matches. An error is set if there are + * no names matching the GUID. + * + * Returns: (transfer container) (element-type utf8): array of names + * + * Since: 1.4.7 + **/ +GPtrArray * +fu_efivar_get_names(const gchar *guid, GError **error) +{ + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return fu_efivar_get_names_impl(guid, error); +} + +/** + * fu_efivar_get_monitor: + * @guid: Globally unique identifier + * @name: Variable name + * @error: (nullable): optional return location for an error + * + * Returns a file monitor for a specific key. + * + * Returns: (transfer full): a #GFileMonitor, or %NULL for an error + * + * Since: 1.5.5 + **/ +GFileMonitor * +fu_efivar_get_monitor(const gchar *guid, const gchar *name, GError **error) +{ + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + return fu_efivar_get_monitor_impl(guid, name, error); +} + +/** + * fu_efivar_space_used: + * @error: (nullable): optional return location for an error + * + * Gets the total size used by all EFI variables. This may be less than the size reported by the + * kernel as some (hopefully small) variables are hidden from userspace. + * + * Returns: total allocated size of all visible variables, or %G_MAXUINT64 on error + * + * Since: 1.5.1 + **/ +guint64 +fu_efivar_space_used(GError **error) +{ + g_return_val_if_fail(error == NULL || *error == NULL, G_MAXUINT64); + return fu_efivar_space_used_impl(error); +} +/** + * fu_efivar_set_data: + * @guid: Globally unique identifier + * @name: Variable name + * @data: Data to set + * @sz: size of @data + * @attr: Attributes + * @error: (nullable): optional return location for an error + * + * Sets the data to a UEFI variable in NVRAM + * + * Returns: %TRUE on success + * + * Since: 1.4.0 + **/ +gboolean +fu_efivar_set_data(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error) +{ + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return fu_efivar_set_data_impl(guid, name, data, sz, attr, error); +} + +/** + * fu_efivar_set_data_bytes: + * @guid: globally unique identifier + * @name: variable name + * @bytes: data blob + * @attr: attributes + * @error: (nullable): optional return location for an error + * + * Sets the data to a UEFI variable in NVRAM + * + * Returns: %TRUE on success + * + * Since: 1.5.0 + **/ +gboolean +fu_efivar_set_data_bytes(const gchar *guid, + const gchar *name, + GBytes *bytes, + guint32 attr, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf; + + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(name != NULL, FALSE); + g_return_val_if_fail(bytes != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + buf = g_bytes_get_data(bytes, &bufsz); + return fu_efivar_set_data(guid, name, buf, bufsz, attr, error); +} + +/** + * fu_efivar_secure_boot_enabled: + * @error: (nullable): optional return location for an error + * + * Determines if secure boot was enabled + * + * Returns: %TRUE on success + * + * Since: 1.8.2 + **/ +gboolean +fu_efivar_secure_boot_enabled(GError **error) +{ + gsize data_size = 0; + g_autofree guint8 *data = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "SecureBoot", + &data, + &data_size, + NULL, + NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SecureBoot is not available"); + return FALSE; + } + if (data_size >= 1 && data[0] & 1) + return TRUE; + + /* available, but not enabled */ + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "SecureBoot is not enabled"); + return FALSE; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-efivar.h b/fwupd-1.8.6/libfwupdplugin/fu-efivar.h new file mode 100644 index 0000000000000000000000000000000000000000..893a19a4f2aba2ac9f6fe983c627e2084546f7fe --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-efivar.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_EFIVAR_GUID_EFI_GLOBAL "8be4df61-93ca-11d2-aa0d-00e098032b8c" +#define FU_EFIVAR_GUID_FWUPDATE "0abba7dc-e516-4167-bbf5-4d9d1c739416" +#define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" +#define FU_EFIVAR_GUID_SECURITY_DATABASE "d719b2cb-3d3a-4596-a3bc-dad00e67656f" +#define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" +#define FU_EFIVAR_GUID_EFI_CAPSULE_REPORT "39b68c46-f7fb-441b-b6ec-16b0f69821f3" + +#define FU_EFIVAR_ATTR_NON_VOLATILE (1 << 0) +#define FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS (1 << 1) +#define FU_EFIVAR_ATTR_RUNTIME_ACCESS (1 << 2) +#define FU_EFIVAR_ATTR_HARDWARE_ERROR_RECORD (1 << 3) +#define FU_EFIVAR_ATTR_AUTHENTICATED_WRITE_ACCESS (1 << 4) +#define FU_EFIVAR_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS (1 << 5) +#define FU_EFIVAR_ATTR_APPEND_WRITE (1 << 6) + +gboolean +fu_efivar_supported(GError **error); +guint64 +fu_efivar_space_used(GError **error); +gboolean +fu_efivar_exists(const gchar *guid, const gchar *name); +GFileMonitor * +fu_efivar_get_monitor(const gchar *guid, const gchar *name, GError **error); +gboolean +fu_efivar_get_data(const gchar *guid, + const gchar *name, + guint8 **data, + gsize *data_sz, + guint32 *attr, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_efivar_get_data_bytes(const gchar *guid, const gchar *name, guint32 *attr, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_efivar_set_data(const gchar *guid, + const gchar *name, + const guint8 *data, + gsize sz, + guint32 attr, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_efivar_set_data_bytes(const gchar *guid, + const gchar *name, + GBytes *bytes, + guint32 attr, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_efivar_delete(const gchar *guid, const gchar *name, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_efivar_delete_with_glob(const gchar *guid, + const gchar *name_glob, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fu_efivar_get_names(const gchar *guid, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_efivar_secure_boot_enabled(GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fdt-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-fdt-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..3d72f9f897af16b0377a21ba4f6498435dd62ed0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fdt-firmware.c @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-crc.h" +#include "fu-dump.h" +#include "fu-fdt-firmware.h" +#include "fu-fdt-image.h" +#include "fu-mem.h" + +/** + * FuFdtFirmware: + * + * A Flattened DeviceTree firmware image. + * + * Documented: + * https://devicetree-specification.readthedocs.io/en/latest/chapter5-flattened-format.html + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint32 cpuid; +} FuFdtFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuFdtFirmware, fu_fdt_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_fdt_firmware_get_instance_private(o)) + +typedef struct __attribute__((packed)) { + guint32 magic; + guint32 totalsize; + guint32 off_dt_struct; + guint32 off_dt_strings; + guint32 off_mem_rsvmap; + guint32 version; + guint32 last_comp_version; + guint32 boot_cpuid_phys; + guint32 size_dt_strings; + guint32 size_dt_struct; +} FuFdtHeader; + +typedef struct __attribute__((packed)) { + guint64 address; + guint64 size; +} FuFdtReserveEntry; + +typedef struct __attribute__((packed)) { + guint32 len; + guint32 nameoff; +} FuFdtProp; + +#define FDT_MAGIC 0xD00DFEED +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +#define FDT_LAST_COMP_VERSION 2 +#define FDT_DEPTH_MAX 128 + +static GString * +fu_string_new_safe(const guint8 *buf, gsize bufsz, gsize offset, GError **error) +{ + g_autoptr(GString) str = g_string_new(NULL); + for (gsize i = offset; i < bufsz; i++) { + if (buf[i] == '\0') + return g_steal_pointer(&str); + g_string_append_c(str, (gchar)buf[i]); + } + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "buffer not NULL terminated"); + return NULL; +} + +static void +fu_fdt_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware); + FuFdtFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "cpuid", priv->cpuid); +} + +/** + * fu_fdt_firmware_get_cpuid: + * @self: a #FuFdtFirmware + * + * Gets the CPUID. + * + * Returns: integer + * + * Since: 1.8.2 + **/ +guint32 +fu_fdt_firmware_get_cpuid(FuFdtFirmware *self) +{ + FuFdtFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FDT_FIRMWARE(self), 0x0); + return priv->cpuid; +} + +/** + * fu_fdt_firmware_set_cpuid: + * @self: a #FuFdtFirmware + * @cpuid: integer value + * + * Sets the CPUID. + * + * Since: 1.8.2 + **/ +void +fu_fdt_firmware_set_cpuid(FuFdtFirmware *self, guint32 cpuid) +{ + FuFdtFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FDT_FIRMWARE(self)); + priv->cpuid = cpuid; +} + +/** + * fu_fdt_firmware_get_image_by_path: + * @self: a #FuFdtFirmware + * @path: ID path, e.g. `/images/firmware-1` + * @error: (nullable): optional return location for an error + * + * Gets the FDT image for a specific path. + * + * Returns: (transfer full): a #FuFirmware, or %NULL + * + * Since: 1.8.2 + **/ +FuFdtImage * +fu_fdt_firmware_get_image_by_path(FuFdtFirmware *self, const gchar *path, GError **error) +{ + g_auto(GStrv) paths = NULL; + g_autoptr(FuFirmware) img_current = g_object_ref(FU_FIRMWARE(self)); + + g_return_val_if_fail(FU_IS_FDT_FIRMWARE(self), NULL); + g_return_val_if_fail(path != NULL, NULL); + g_return_val_if_fail(path[0] != '\0', NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + paths = g_strsplit(path, "/", -1); + for (guint i = 0; paths[i] != NULL; i++) { + const gchar *id = paths[i]; + g_autoptr(FuFirmware) img_tmp = NULL; + + /* special case for empty */ + if (id[0] == '\0') + id = NULL; + img_tmp = fu_firmware_get_image_by_id(img_current, id, error); + if (img_tmp == NULL) + return NULL; + g_set_object(&img_current, img_tmp); + } + + /* success */ + return FU_FDT_IMAGE(g_steal_pointer(&img_current)); +} + +static gboolean +fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *strtab, GError **error) +{ + gsize bufsz = 0; + gsize offset = 0; + guint depth = 0; + gboolean has_end = FALSE; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) firmware_current = g_object_ref(FU_FIRMWARE(self)); + + /* debug */ + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "dt_struct", fw); + + /* parse */ + while (offset < bufsz) { + guint32 token = 0; + + /* read tag from aligned offset */ + offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4); + if (!fu_memread_uint32_safe(buf, bufsz, offset, &token, G_BIG_ENDIAN, error)) + return FALSE; + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + g_debug("token: 0x%x", token); + offset += sizeof(guint32); + + /* nothing to do */ + if (token == FDT_NOP) + continue; + + /* END */ + if (token == FDT_END) { + if (firmware_current != FU_FIRMWARE(self)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got END with unclosed node"); + return FALSE; + } + has_end = TRUE; + break; + } + + /* BEGIN NODE */ + if (token == FDT_BEGIN_NODE) { + g_autoptr(GString) str = NULL; + g_autoptr(FuFirmware) image = NULL; + + /* sanity check */ + if (depth++ > FDT_DEPTH_MAX) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "node depth exceeded maximum: 0x%x", + (guint)FDT_DEPTH_MAX); + return FALSE; + } + + str = fu_string_new_safe(buf, bufsz, offset, error); + if (str == NULL) + return FALSE; + offset += str->len + 1; + image = fu_fdt_image_new(); + if (str->len > 0) + fu_firmware_set_id(image, str->str); + fu_firmware_set_offset(image, offset); + fu_firmware_add_image(firmware_current, image); + g_set_object(&firmware_current, image); + continue; + } + + /* END NODE */ + if (token == FDT_END_NODE) { + if (firmware_current == FU_FIRMWARE(self)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got END NODE with no node to end"); + return FALSE; + } + g_set_object(&firmware_current, fu_firmware_get_parent(firmware_current)); + if (depth > 0) + depth--; + continue; + } + + /* PROP */ + if (token == FDT_PROP) { + guint32 prop_len = 0; + guint32 prop_nameoff = 0; + gpointer value = NULL; + g_autoptr(GBytes) blob = NULL; + + /* sanity check */ + if (firmware_current == FU_FIRMWARE(self)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got PROP with unopen node"); + return FALSE; + } + + /* parse */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtProp, len), + &prop_len, + G_BIG_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtProp, nameoff), + &prop_nameoff, + G_BIG_ENDIAN, + error)) + return FALSE; + offset += sizeof(FuFdtProp); + + /* add property */ + if (!g_hash_table_lookup_extended(strtab, + GINT_TO_POINTER(prop_nameoff), + NULL, + &value)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid strtab offset 0x%x", + prop_nameoff); + return FALSE; + } + blob = fu_bytes_new_offset(fw, offset, prop_len, error); + if (blob == NULL) + return FALSE; + fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current), + (const gchar *)value, + blob); + offset += prop_len; + continue; + } + + /* unknown token */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid token 0x%x @0%x", + token, + (guint)offset); + return FALSE; + } + + /* did not see FDT_END */ + if (!has_end) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not see FDT_END"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fdt_firmware_parse_dt_strings(FuFdtFirmware *self, + GBytes *fw, + GHashTable *strtab, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* debug */ + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "dt_strings", fw); + + /* parse */ + for (gsize offset = 0; offset < bufsz; offset++) { + g_autoptr(GString) str = NULL; + str = fu_string_new_safe(buf, bufsz, offset, error); + if (str == NULL) + return FALSE; + if (str->len == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "strtab invalid @0x%x", + (guint)offset); + return FALSE; + } + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + g_debug("strtab: %s", str->str); + g_hash_table_insert(strtab, GINT_TO_POINTER(offset), g_strdup(str->str)); + offset += str->len; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self, + const guint8 *buf, + gsize bufsz, + gsize offset, + GError **error) +{ + /* parse */ + for (; offset < bufsz; offset += sizeof(FuFdtReserveEntry)) { + guint64 address = 0; + guint64 size = 0; + if (!fu_memread_uint64_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtReserveEntry, address), + &address, + G_BIG_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint64_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtReserveEntry, size), + &size, + G_BIG_ENDIAN, + error)) + return FALSE; + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + g_debug("mem_rsvmap: 0x%x, 0x%x", (guint)address, (guint)size); + if (address == 0x0 && size == 0x0) + break; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fdt_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + G_STRUCT_OFFSET(FuFdtHeader, magic), + &magic, + G_BIG_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != FDT_MAGIC) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic 0x%x, expected 0x%x", + magic, + (guint)FDT_MAGIC); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fdt_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware); + FuFdtFirmwarePrivate *priv = GET_PRIVATE(self); + guint32 totalsize = 0; + gsize bufsz = 0; + guint32 off_dt_struct = 0; + guint32 off_dt_strings = 0; + guint32 off_mem_rsvmap = 0; + guint32 version = 0; + guint32 last_comp_version = 0; + guint32 size_dt_strings = 0; + guint32 size_dt_struct = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(GHashTable) strtab = NULL; /* uint:str */ + + /* sanity check */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, totalsize), + &totalsize, + G_BIG_ENDIAN, + error)) + return FALSE; + if (totalsize > bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "truncated image, got 0x%x, expected >= 0x%x", + (guint)bufsz, + (guint)totalsize); + return FALSE; + } + fu_firmware_set_size(firmware, totalsize); + + /* read string table */ + strtab = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_strings), + &off_dt_strings, + G_BIG_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, size_dt_strings), + &size_dt_strings, + G_BIG_ENDIAN, + error)) + return FALSE; + if (size_dt_strings != 0x0) { + g_autoptr(GBytes) dt_strings = NULL; + dt_strings = + fu_bytes_new_offset(fw, offset + off_dt_strings, size_dt_strings, error); + if (dt_strings == NULL) + return FALSE; + if (!fu_fdt_firmware_parse_dt_strings(self, dt_strings, strtab, error)) + return FALSE; + } + + /* read out DT struct */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_struct), + &off_dt_struct, + G_BIG_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, size_dt_struct), + &size_dt_struct, + G_BIG_ENDIAN, + error)) + return FALSE; + if (size_dt_struct != 0x0) { + g_autoptr(GBytes) dt_struct = NULL; + dt_struct = fu_bytes_new_offset(fw, offset + off_dt_struct, size_dt_struct, error); + if (dt_struct == NULL) + return FALSE; + if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct, strtab, error)) + return FALSE; + } + + /* read out reserved map */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, off_mem_rsvmap), + &off_mem_rsvmap, + G_BIG_ENDIAN, + error)) + return FALSE; + if (off_mem_rsvmap != 0x0) { + if (!fu_fdt_firmware_parse_mem_rsvmap(self, + buf, + bufsz, + offset + off_mem_rsvmap, + error)) + return FALSE; + } + + /* read in CPUID */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, boot_cpuid_phys), + &priv->cpuid, + G_BIG_ENDIAN, + error)) + return FALSE; + + /* header version */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, last_comp_version), + &last_comp_version, + G_BIG_ENDIAN, + error)) + return FALSE; + if (last_comp_version < FDT_LAST_COMP_VERSION) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid header version, got 0x%x, expected >= 0x%x", + (guint)last_comp_version, + (guint)FDT_LAST_COMP_VERSION); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuFdtHeader, version), + &version, + G_BIG_ENDIAN, + error)) + return FALSE; + fu_firmware_set_version_raw(firmware, version); + return TRUE; +} + +typedef struct { + GByteArray *dt_strings; + GByteArray *dt_struct; + GHashTable *strtab; +} FuFdtFirmwareBuildHelper; + +static guint32 +fu_fdt_firmware_append_to_strtab(FuFdtFirmwareBuildHelper *helper, const gchar *key) +{ + gpointer tmp = NULL; + guint32 offset; + + /* already exists */ + if (g_hash_table_lookup_extended(helper->strtab, key, NULL, &tmp)) + return GPOINTER_TO_UINT(tmp); + + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + g_debug("adding strtab: %s", key); + offset = helper->dt_strings->len; + g_byte_array_append(helper->dt_strings, (const guint8 *)key, strlen(key)); + fu_byte_array_append_uint8(helper->dt_strings, 0x0); + g_hash_table_insert(helper->strtab, g_strdup(key), GUINT_TO_POINTER(offset)); + return offset; +} + +static gboolean +fu_fdt_firmware_write_image(FuFdtImage *img, + FuFdtFirmwareBuildHelper *helper, + guint depth, + GError **error) +{ + const gchar *id = fu_firmware_get_id(FU_FIRMWARE(img)); + g_autoptr(GPtrArray) images = fu_firmware_get_images(FU_FIRMWARE(img)); + g_autoptr(GPtrArray) attrs = fu_fdt_image_get_attrs(img); + + /* sanity check */ + if (depth > 0 && id == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "child FuFdtImage requires ID"); + return FALSE; + } + + /* BEGIN_NODE, ID, NUL */ + fu_byte_array_append_uint32(helper->dt_struct, FDT_BEGIN_NODE, G_BIG_ENDIAN); + if (id != NULL) { + g_byte_array_append(helper->dt_struct, (const guint8 *)id, strlen(id) + 1); + } else { + fu_byte_array_append_uint8(helper->dt_struct, 0x0); + } + fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0); + + /* write properties */ + for (guint i = 0; i < attrs->len; i++) { + const gchar *key = g_ptr_array_index(attrs, i); + g_autoptr(GBytes) blob = NULL; + + blob = fu_fdt_image_get_attr(img, key, error); + if (blob == NULL) + return FALSE; + fu_byte_array_append_uint32(helper->dt_struct, FDT_PROP, G_BIG_ENDIAN); + fu_byte_array_append_uint32(helper->dt_struct, + g_bytes_get_size(blob), + G_BIG_ENDIAN); + fu_byte_array_append_uint32(helper->dt_struct, + fu_fdt_firmware_append_to_strtab(helper, key), + G_BIG_ENDIAN); + fu_byte_array_append_bytes(helper->dt_struct, blob); + fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0); + } + + /* write children, recursively */ + for (guint i = 0; i < images->len; i++) { + FuFdtImage *img_child = g_ptr_array_index(images, i); + if (!fu_fdt_firmware_write_image(img_child, helper, depth + 1, error)) + return FALSE; + } + + /* END_NODE */ + fu_byte_array_append_uint32(helper->dt_struct, FDT_END_NODE, G_BIG_ENDIAN); + return TRUE; +} + +static GBytes * +fu_fdt_firmware_write(FuFirmware *firmware, GError **error) +{ + FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware); + FuFdtFirmwarePrivate *priv = GET_PRIVATE(self); + guint32 off_dt_struct; + guint32 off_dt_strings; + guint32 off_mem_rsvmap; + guint32 totalsize; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GByteArray) dt_strings = g_byte_array_new(); + g_autoptr(GByteArray) dt_struct = g_byte_array_new(); + g_autoptr(GByteArray) mem_rsvmap = g_byte_array_new(); + g_autoptr(GHashTable) strtab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + FuFdtFirmwareBuildHelper helper = { + .dt_strings = dt_strings, + .dt_struct = dt_struct, + .strtab = strtab, + }; + + /* mem_rsvmap */ + off_mem_rsvmap = fu_common_align_up(sizeof(FuFdtHeader), FU_FIRMWARE_ALIGNMENT_4); + fu_byte_array_append_uint64(mem_rsvmap, 0x0, G_BIG_ENDIAN); + fu_byte_array_append_uint64(mem_rsvmap, 0x0, G_BIG_ENDIAN); + + /* dt_struct */ + off_dt_struct = + fu_common_align_up(off_mem_rsvmap + mem_rsvmap->len, FU_FIRMWARE_ALIGNMENT_4); + + /* only one root node supported */ + if (images->len != 1) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "no root node"); + return NULL; + } + if (!fu_fdt_firmware_write_image(FU_FDT_IMAGE(g_ptr_array_index(images, 0)), + &helper, + 0, + error)) + return NULL; + fu_byte_array_append_uint32(dt_struct, FDT_END, G_BIG_ENDIAN); + + /* dt_strings */ + off_dt_strings = + fu_common_align_up(off_dt_struct + dt_struct->len, FU_FIRMWARE_ALIGNMENT_4); + + /* write header */ + totalsize = off_dt_strings + dt_strings->len; + fu_byte_array_append_uint32(buf, FDT_MAGIC, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, totalsize, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, off_dt_struct, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, off_dt_strings, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, off_mem_rsvmap, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, FDT_LAST_COMP_VERSION, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, priv->cpuid, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, dt_strings->len, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, dt_struct->len, G_BIG_ENDIAN); + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + + /* write mem_rsvmap, dt_struct, dt_strings */ + g_byte_array_append(buf, mem_rsvmap->data, mem_rsvmap->len); + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + g_byte_array_append(buf, dt_struct->data, dt_struct->len); + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + g_byte_array_append(buf, dt_strings->data, dt_strings->len); + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_fdt_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware); + FuFdtFirmwarePrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "cpuid", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->cpuid = tmp; + + /* success */ + return TRUE; +} + +static void +fu_fdt_firmware_init(FuFdtFirmware *self) +{ + g_type_ensure(FU_TYPE_FDT_IMAGE); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_fdt_firmware_class_init(FuFdtFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_fdt_firmware_check_magic; + klass_firmware->export = fu_fdt_firmware_export; + klass_firmware->parse = fu_fdt_firmware_parse; + klass_firmware->write = fu_fdt_firmware_write; + klass_firmware->build = fu_fdt_firmware_build; +} + +/** + * fu_fdt_firmware_new: + * + * Creates a new #FuFirmware of sub type FDT + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_fdt_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_FDT_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fdt-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-fdt-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..aceadc6f02594870f5797e3a12025cb590fa2c37 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fdt-firmware.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-fdt-image.h" +#include "fu-firmware.h" + +#define FU_TYPE_FDT_FIRMWARE (fu_fdt_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuFdtFirmware, fu_fdt_firmware, FU, FDT_FIRMWARE, FuFirmware) + +struct _FuFdtFirmwareClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_fdt_firmware_new(void); +guint32 +fu_fdt_firmware_get_cpuid(FuFdtFirmware *self); +void +fu_fdt_firmware_set_cpuid(FuFdtFirmware *self, guint32 cpuid); +FuFdtImage * +fu_fdt_firmware_get_image_by_path(FuFdtFirmware *self, const gchar *path, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fdt-image.c b/fwupd-1.8.6/libfwupdplugin/fu-fdt-image.c new file mode 100644 index 0000000000000000000000000000000000000000..7c2326b8aec33265c34f225eded67e556d73ed98 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fdt-image.c @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-fdt-image.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * FuFdtImage: + * + * A Flattened DeviceTree firmware image. + * + * See also: [class@FuFdtFirmware] + */ + +typedef struct { + GHashTable *hash_attrs; + GHashTable *hash_attrs_format; +} FuFdtImagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuFdtImage, fu_fdt_image, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_fdt_image_get_instance_private(o)) + +#define FU_FDT_IMAGE_FORMAT_STR "str" +#define FU_FDT_IMAGE_FORMAT_STRLIST "strlist" +#define FU_FDT_IMAGE_FORMAT_UINT32 "uint32" +#define FU_FDT_IMAGE_FORMAT_UINT64 "uint64" +#define FU_FDT_IMAGE_FORMAT_DATA "data" + +static const gchar * +fu_fdt_image_guess_format_from_key(const gchar *key) +{ + struct { + const gchar *key; + const gchar *format; + } key_format_map[] = {{"#address-cells", FU_FDT_IMAGE_FORMAT_UINT32}, + {"algo", FU_FDT_IMAGE_FORMAT_STR}, + {"arch", FU_FDT_IMAGE_FORMAT_STR}, + {"compatible", FU_FDT_IMAGE_FORMAT_STRLIST}, + {"compression", FU_FDT_IMAGE_FORMAT_STR}, + {"creator", FU_FDT_IMAGE_FORMAT_STR}, + {"data-offset", FU_FDT_IMAGE_FORMAT_UINT32}, + {"data-size", FU_FDT_IMAGE_FORMAT_UINT32}, + {"default", FU_FDT_IMAGE_FORMAT_STR}, + {"description", FU_FDT_IMAGE_FORMAT_STR}, + {"entry", FU_FDT_IMAGE_FORMAT_STR}, + {"firmware", FU_FDT_IMAGE_FORMAT_STR}, + {"load", FU_FDT_IMAGE_FORMAT_UINT32}, + {"os", FU_FDT_IMAGE_FORMAT_STR}, + {"timestamp", FU_FDT_IMAGE_FORMAT_UINT32}, + {"type", FU_FDT_IMAGE_FORMAT_STR}, + {"version", FU_FDT_IMAGE_FORMAT_STR}, + {NULL, NULL}}; + for (guint i = 0; key_format_map[i].key != NULL; i++) { + if (g_strcmp0(key, key_format_map[i].key) == 0) + return key_format_map[i].format; + } + return NULL; +} + +static gchar ** +fu_fdt_image_strlist_from_blob(GBytes *blob) +{ + gchar **val; + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + g_autoptr(GPtrArray) strs = g_ptr_array_new(); + + /* delimit by NUL */ + for (gsize i = 0; i < bufsz; i++) { + const gchar *tmp = (const gchar *)buf + i; + g_ptr_array_add(strs, (gpointer)tmp); + i += strlen(tmp); + } + + /* copy to GStrv */ + val = g_new0(gchar *, strs->len + 1); + for (guint i = 0; i < strs->len; i++) + val[i] = g_strdup(g_ptr_array_index(strs, i)); + return val; +} + +static void +fu_fdt_image_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuFdtImage *self = FU_FDT_IMAGE(firmware); + FuFdtImagePrivate *priv = GET_PRIVATE(self); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, priv->hash_attrs); + while (g_hash_table_iter_next(&iter, &key, &value)) { + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(value, &bufsz); + const gchar *format = g_hash_table_lookup(priv->hash_attrs_format, key); + g_autofree gchar *str = NULL; + g_autoptr(XbBuilderNode) bc = NULL; + + /* guess format based on key name to improve debugging experience */ + if (format == NULL) + format = fu_fdt_image_guess_format_from_key(key); + if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_UINT32) == 0 && bufsz == 4) { + guint64 tmp = fu_memread_uint32(buf, G_BIG_ENDIAN); + str = g_strdup_printf("0x%x", (guint)tmp); + } else if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_UINT64) == 0 && bufsz == 8) { + guint64 tmp = fu_memread_uint64(buf, G_BIG_ENDIAN); + str = g_strdup_printf("0x%x", (guint)tmp); + } else if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_STR) == 0 && bufsz > 0) { + str = g_strndup((const gchar *)buf, bufsz); + } else if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_STRLIST) == 0 && bufsz > 0) { + g_auto(GStrv) tmp = fu_fdt_image_strlist_from_blob(value); + str = g_strjoinv(":", tmp); + } else { + str = g_base64_encode(buf, bufsz); + } + bc = xb_builder_node_insert(bn, "metadata", "key", key, NULL); + if (str != NULL) + xb_builder_node_set_text(bc, str, -1); + if (format != NULL) + xb_builder_node_set_attr(bc, "format", format); + } +} + +/** + * fu_fdt_image_get_attrs: + * @self: a #FuFdtImage + * + * Gets all the attributes stored on the image. + * + * Returns: (transfer container) (element-type utf8): keys + * + * Since: 1.8.2 + **/ +GPtrArray * +fu_fdt_image_get_attrs(FuFdtImage *self) +{ + FuFdtImagePrivate *priv = GET_PRIVATE(self); + GPtrArray *array = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GList) keys = NULL; + + g_return_val_if_fail(FU_IS_FDT_IMAGE(self), NULL); + + keys = g_hash_table_get_keys(priv->hash_attrs); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + g_ptr_array_add(array, g_strdup(key)); + } + return array; +} + +/** + * fu_fdt_image_get_attr: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @error: (nullable): optional return location for an error + * + * Gets a attribute from the image. + * + * Returns: (transfer full): blob + * + * Since: 1.8.2 + **/ +GBytes * +fu_fdt_image_get_attr(FuFdtImage *self, const gchar *key, GError **error) +{ + FuFdtImagePrivate *priv = GET_PRIVATE(self); + GBytes *blob; + + g_return_val_if_fail(FU_IS_FDT_IMAGE(self), NULL); + g_return_val_if_fail(key != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + blob = g_hash_table_lookup(priv->hash_attrs, key); + if (blob == NULL) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no data for %s", key); + return NULL; + } + + /* success */ + return g_bytes_ref(blob); +} + +/** + * fu_fdt_image_get_attr_u32: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @val: (out) (nullable): value + * @error: (nullable): optional return location for an error + * + * Gets a uint32 attribute from the image. + * + * Returns: %TRUE if @val was set. + * + * Since: 1.8.2 + **/ +gboolean +fu_fdt_image_get_attr_u32(FuFdtImage *self, const gchar *key, guint32 *val, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + + g_return_val_if_fail(FU_IS_FDT_IMAGE(self), FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + blob = fu_fdt_image_get_attr(self, key, error); + if (blob == NULL) + return FALSE; + if (g_bytes_get_size(blob) != sizeof(guint32)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid data size for %s, got 0x%x, expected 0x%x", + key, + (guint)g_bytes_get_size(blob), + (guint)sizeof(guint32)); + return FALSE; + } + if (val != NULL) + *val = fu_memread_uint32(g_bytes_get_data(blob, NULL), G_BIG_ENDIAN); + return TRUE; +} + +/** + * fu_fdt_image_get_attr_u64: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @val: (out) (nullable): value + * @error: (nullable): optional return location for an error + * + * Gets a uint64 attribute from the image. + * + * Returns: %TRUE if @val was set. + * + * Since: 1.8.2 + **/ +gboolean +fu_fdt_image_get_attr_u64(FuFdtImage *self, const gchar *key, guint64 *val, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + + g_return_val_if_fail(FU_IS_FDT_IMAGE(self), FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + blob = fu_fdt_image_get_attr(self, key, error); + if (blob == NULL) + return FALSE; + if (g_bytes_get_size(blob) != sizeof(guint64)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid data size for %s, got 0x%x, expected 0x%x", + key, + (guint)g_bytes_get_size(blob), + (guint)sizeof(guint64)); + return FALSE; + } + if (val != NULL) + *val = fu_memread_uint64(g_bytes_get_data(blob, NULL), G_BIG_ENDIAN); + return TRUE; +} + +/** + * fu_fdt_image_get_attr_strlist: + * @self: a #FuFdtImage + * @key: string, e.g. `compatible` + * @val: (out) (nullable) (transfer full): values + * @error: (nullable): optional return location for an error + * + * Gets a stringlist attribute from the image. @val is always `NUL` terminated. + * + * Returns: %TRUE if @val was set. + * + * Since: 1.8.2 + **/ +gboolean +fu_fdt_image_get_attr_strlist(FuFdtImage *self, const gchar *key, gchar ***val, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + const guint8 *buf; + gsize bufsz = 0; + + g_return_val_if_fail(FU_IS_FDT_IMAGE(self), FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + blob = fu_fdt_image_get_attr(self, key, error); + if (blob == NULL) + return FALSE; + if (g_bytes_get_size(blob) == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid data size for %s, got 0x%x", + key, + (guint)g_bytes_get_size(blob)); + return FALSE; + } + + /* sanity check */ + buf = g_bytes_get_data(blob, &bufsz); + for (gsize i = 0; i < bufsz; i++) { + if (buf[i] != 0x0 && !g_ascii_isprint((gchar)buf[i])) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "nonprintable character 0x%02x at offset 0x%x in %s", + buf[i], + (guint)i, + key); + return FALSE; + } + } + + /* success */ + if (val != NULL) + *val = fu_fdt_image_strlist_from_blob(blob); + return TRUE; +} + +/** + * fu_fdt_image_get_attr_str: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @val: (out) (nullable) (transfer full): value + * @error: (nullable): optional return location for an error + * + * Gets a string attribute from the image. @val is always `NUL` terminated. + * + * Returns: %TRUE if @val was set. + * + * Since: 1.8.2 + **/ +gboolean +fu_fdt_image_get_attr_str(FuFdtImage *self, const gchar *key, gchar **val, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + const guint8 *buf; + gsize bufsz = 0; + + g_return_val_if_fail(FU_IS_FDT_IMAGE(self), FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + blob = fu_fdt_image_get_attr(self, key, error); + if (blob == NULL) + return FALSE; + if (g_bytes_get_size(blob) == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid data size for %s, got 0x%x", + key, + (guint)g_bytes_get_size(blob)); + return FALSE; + } + + /* sanity check */ + buf = g_bytes_get_data(blob, &bufsz); + for (gsize i = 0; i < bufsz; i++) { + if (buf[i] != 0x0 && !g_ascii_isprint((gchar)buf[i])) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "nonprintable character 0x%02x at offset 0x%x in %s", + buf[i], + (guint)i, + key); + return FALSE; + } + } + + /* success */ + if (val != NULL) + *val = g_strndup(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); + return TRUE; +} + +/** + * fu_fdt_image_set_attr: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @blob: a #GBytes + * + * Sets a attribute for the image. + * + * Since: 1.8.2 + **/ +void +fu_fdt_image_set_attr(FuFdtImage *self, const gchar *key, GBytes *blob) +{ + FuFdtImagePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FDT_IMAGE(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(priv->hash_attrs, g_strdup(key), g_bytes_ref(blob)); +} + +static void +fu_fdt_image_set_attr_format(FuFdtImage *self, const gchar *key, const gchar *format) +{ + FuFdtImagePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FDT_IMAGE(self)); + g_return_if_fail(format != NULL); + g_hash_table_insert(priv->hash_attrs_format, g_strdup(key), strdup(format)); +} + +/** + * fu_fdt_image_set_attr_uint32: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @value: value to store + * + * Sets a uint32 attribute for the image. + * + * Since: 1.8.2 + **/ +void +fu_fdt_image_set_attr_uint32(FuFdtImage *self, const gchar *key, guint32 value) +{ + guint8 buf[4] = {0x0}; + g_autoptr(GBytes) blob = NULL; + + g_return_if_fail(FU_IS_FDT_IMAGE(self)); + g_return_if_fail(key != NULL); + + fu_memwrite_uint32(buf, value, G_BIG_ENDIAN); + blob = g_bytes_new(buf, sizeof(buf)); + fu_fdt_image_set_attr(self, key, blob); + fu_fdt_image_set_attr_format(self, key, FU_FDT_IMAGE_FORMAT_UINT32); +} + +/** + * fu_fdt_image_set_attr_uint64: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @value: value to store + * + * Sets a uint64 attribute for the image. + * + * Since: 1.8.2 + **/ +void +fu_fdt_image_set_attr_uint64(FuFdtImage *self, const gchar *key, guint64 value) +{ + guint8 buf[8] = {0x0}; + g_autoptr(GBytes) blob = NULL; + + g_return_if_fail(FU_IS_FDT_IMAGE(self)); + g_return_if_fail(key != NULL); + + fu_memwrite_uint64(buf, value, G_BIG_ENDIAN); + blob = g_bytes_new(buf, sizeof(buf)); + fu_fdt_image_set_attr(self, key, blob); + fu_fdt_image_set_attr_format(self, key, FU_FDT_IMAGE_FORMAT_UINT64); +} + +/** + * fu_fdt_image_set_attr_str: + * @self: a #FuFdtImage + * @key: string, e.g. `creator` + * @value: value to store + * + * Sets a string attribute for the image. + * + * Since: 1.8.2 + **/ +void +fu_fdt_image_set_attr_str(FuFdtImage *self, const gchar *key, const gchar *value) +{ + g_autoptr(GBytes) blob = NULL; + + g_return_if_fail(FU_IS_FDT_IMAGE(self)); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + + blob = g_bytes_new((const guint8 *)value, strlen(value) + 1); + fu_fdt_image_set_attr(self, key, blob); + fu_fdt_image_set_attr_format(self, key, FU_FDT_IMAGE_FORMAT_STR); +} + +/** + * fu_fdt_image_set_attr_strlist: + * @self: a #FuFdtImage + * @key: string, e.g. `compatible` + * @value: values to store + * + * Sets a stringlist attribute for the image. + * + * Since: 1.8.2 + **/ +void +fu_fdt_image_set_attr_strlist(FuFdtImage *self, const gchar *key, gchar **value) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + g_return_if_fail(FU_IS_FDT_IMAGE(self)); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + g_return_if_fail(value[0] != NULL); + + for (guint i = 0; value[i] != NULL; i++) { + g_byte_array_append(buf, (const guint8 *)value[i], strlen(value[i])); + fu_byte_array_append_uint8(buf, 0x0); + } + blob = g_bytes_new(buf->data, buf->len); + fu_fdt_image_set_attr(self, key, blob); + fu_fdt_image_set_attr_format(self, key, FU_FDT_IMAGE_FORMAT_STRLIST); +} + +static gboolean +fu_fdt_image_build_metadata_node(FuFdtImage *self, XbNode *n, GError **error) +{ + const gchar *key; + const gchar *format; + const gchar *value = xb_node_get_text(n); + + key = xb_node_get_attr(n, "key"); + if (key == NULL) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "key invalid"); + return FALSE; + } + format = xb_node_get_attr(n, "format"); + if (format == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "format unspecified for %s, expected uint64|uint32|str|strlist|data", + key); + return FALSE; + } + fu_fdt_image_set_attr_format(self, key, format); + + /* actually parse values */ + if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_UINT32) == 0) { + guint64 tmp = 0; + if (value != NULL) { + if (!fu_strtoull(value, &tmp, 0x0, G_MAXUINT32, error)) + return FALSE; + } + fu_fdt_image_set_attr_uint32(self, key, tmp); + return TRUE; + } + if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_UINT64) == 0) { + guint64 tmp = 0; + if (value != NULL) { + if (!fu_strtoull(value, &tmp, 0x0, G_MAXUINT64, error)) + return FALSE; + } + fu_fdt_image_set_attr_uint64(self, key, tmp); + return TRUE; + } + if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_STR) == 0) { + if (value != NULL) { + fu_fdt_image_set_attr_str(self, key, value); + } else { + g_autoptr(GBytes) blob = g_bytes_new(NULL, 0); + fu_fdt_image_set_attr(self, key, blob); + } + return TRUE; + } + if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_STRLIST) == 0) { + if (value != NULL) { + g_auto(GStrv) split = g_strsplit(value, ":", -1); + fu_fdt_image_set_attr_strlist(self, key, split); + } else { + g_autoptr(GBytes) blob = g_bytes_new(NULL, 0); + fu_fdt_image_set_attr(self, key, blob); + } + return TRUE; + } + if (g_strcmp0(format, FU_FDT_IMAGE_FORMAT_DATA) == 0) { + g_autoptr(GBytes) blob = NULL; + if (value != NULL) { + gsize bufsz = 0; + g_autofree guchar *buf = g_base64_decode(value, &bufsz); + blob = g_bytes_new(buf, bufsz); + } else { + blob = g_bytes_new(NULL, 0); + } + fu_fdt_image_set_attr(self, key, blob); + return TRUE; + } + + /* failed */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "format for %s invalid, expected uint64|uint32|str|strlist|data", + key); + return FALSE; +} + +static gboolean +fu_fdt_image_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuFdtImage *self = FU_FDT_IMAGE(firmware); + g_autoptr(GPtrArray) metadata = NULL; + + metadata = xb_node_query(n, "metadata", 0, NULL); + if (metadata != NULL) { + for (guint i = 0; i < metadata->len; i++) { + XbNode *c = g_ptr_array_index(metadata, i); + if (!fu_fdt_image_build_metadata_node(self, c, error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_fdt_image_init(FuFdtImage *self) +{ + FuFdtImagePrivate *priv = GET_PRIVATE(self); + priv->hash_attrs = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_bytes_unref); + priv->hash_attrs_format = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +} + +static void +fu_fdt_image_finalize(GObject *object) +{ + FuFdtImage *self = FU_FDT_IMAGE(object); + FuFdtImagePrivate *priv = GET_PRIVATE(self); + g_hash_table_unref(priv->hash_attrs); + g_hash_table_unref(priv->hash_attrs_format); + G_OBJECT_CLASS(fu_fdt_image_parent_class)->finalize(object); +} + +static void +fu_fdt_image_class_init(FuFdtImageClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_fdt_image_finalize; + klass_firmware->export = fu_fdt_image_export; + klass_firmware->build = fu_fdt_image_build; +} + +/** + * fu_fdt_image_new: + * + * Creates a new #FuFirmware of sub type FDT image + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_fdt_image_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_FDT_IMAGE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fdt-image.h b/fwupd-1.8.6/libfwupdplugin/fu-fdt-image.h new file mode 100644 index 0000000000000000000000000000000000000000..4ddb97017ecaf26f5e632455a94277e4544470b6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fdt-image.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_FDT_IMAGE (fu_fdt_image_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuFdtImage, fu_fdt_image, FU, FDT_IMAGE, FuFirmware) + +struct _FuFdtImageClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_fdt_image_new(void); + +GBytes * +fu_fdt_image_get_attr(FuFdtImage *self, const gchar *key, GError **error); +gboolean +fu_fdt_image_get_attr_u32(FuFdtImage *self, const gchar *key, guint32 *val, GError **error); +gboolean +fu_fdt_image_get_attr_u64(FuFdtImage *self, const gchar *key, guint64 *val, GError **error); +gboolean +fu_fdt_image_get_attr_str(FuFdtImage *self, const gchar *key, gchar **val, GError **error); +gboolean +fu_fdt_image_get_attr_strlist(FuFdtImage *self, const gchar *key, gchar ***val, GError **error); +void +fu_fdt_image_set_attr(FuFdtImage *self, const gchar *key, GBytes *blob); +void +fu_fdt_image_set_attr_uint32(FuFdtImage *self, const gchar *key, guint32 value); +void +fu_fdt_image_set_attr_uint64(FuFdtImage *self, const gchar *key, guint64 value); +void +fu_fdt_image_set_attr_str(FuFdtImage *self, const gchar *key, const gchar *value); +void +fu_fdt_image_set_attr_strlist(FuFdtImage *self, const gchar *key, gchar **value); +GPtrArray * +fu_fdt_image_get_attrs(FuFdtImage *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-firmware-common.c b/fwupd-1.8.6/libfwupdplugin/fu-firmware-common.c new file mode 100644 index 0000000000000000000000000000000000000000..1ed2e123c13e81be94429729579566028e43bedc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-firmware-common.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-firmware-common.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * fu_firmware_strparse_uint4_safe: + * @data: destination buffer + * @datasz: size of @data, typically the same as `strlen(data)` + * @offset: offset in chars into @data to read + * @value: (out) (nullable): parsed value + * @error: (nullable): optional return location for an error + * + * Parses a base 16 number from a string of 1 character in length. + * The returned @value will range from from 0 to 0xf. + * + * Returns: %TRUE if parsed, %FALSE otherwise + * + * Since: 1.5.6 + **/ +gboolean +fu_firmware_strparse_uint4_safe(const gchar *data, + gsize datasz, + gsize offset, + guint8 *value, + GError **error) +{ + gchar buffer[2] = {'\0'}; + gchar *endptr = NULL; + guint64 valuetmp; + if (!fu_memcpy_safe((guint8 *)buffer, + sizeof(buffer), + 0x0, /* dst */ + (const guint8 *)data, + datasz, + offset, /* src */ + sizeof(buffer) - 1, + error)) + return FALSE; + valuetmp = g_ascii_strtoull(buffer, &endptr, 16); + if (endptr - buffer != sizeof(buffer) - 1) { + g_autofree gchar *str = fu_strsafe(buffer, sizeof(buffer)); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as hex", + str); + return FALSE; + } + if (value != NULL) + *value = (guint8)valuetmp; + return TRUE; +} + +/** + * fu_firmware_strparse_uint8_safe: + * @data: destination buffer + * @datasz: size of @data, typically the same as `strlen(data)` + * @offset: offset in chars into @data to read + * @value: (out) (nullable): parsed value + * @error: (nullable): optional return location for an error + * + * Parses a base 16 number from a string of 2 characters in length. + * The returned @value will range from from 0 to 0xff. + * + * Returns: %TRUE if parsed, %FALSE otherwise + * + * Since: 1.5.6 + **/ +gboolean +fu_firmware_strparse_uint8_safe(const gchar *data, + gsize datasz, + gsize offset, + guint8 *value, + GError **error) +{ + gchar buffer[3] = {'\0'}; + gchar *endptr = NULL; + guint64 valuetmp; + if (!fu_memcpy_safe((guint8 *)buffer, + sizeof(buffer), + 0x0, /* dst */ + (const guint8 *)data, + datasz, + offset, /* src */ + sizeof(buffer) - 1, + error)) + return FALSE; + valuetmp = g_ascii_strtoull(buffer, &endptr, 16); + if (endptr - buffer != sizeof(buffer) - 1) { + g_autofree gchar *str = fu_strsafe(buffer, sizeof(buffer)); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as hex", + str); + return FALSE; + } + if (value != NULL) + *value = (guint8)valuetmp; + return TRUE; +} + +/** + * fu_firmware_strparse_uint16_safe: + * @data: destination buffer + * @datasz: size of @data, typically the same as `strlen(data)` + * @offset: offset in chars into @data to read + * @value: (out) (nullable): parsed value + * @error: (nullable): optional return location for an error + * + * Parses a base 16 number from a string of 4 characters in length. + * The returned @value will range from from 0 to 0xffff. + * + * Returns: %TRUE if parsed, %FALSE otherwise + * + * Since: 1.5.6 + **/ +gboolean +fu_firmware_strparse_uint16_safe(const gchar *data, + gsize datasz, + gsize offset, + guint16 *value, + GError **error) +{ + gchar buffer[5] = {'\0'}; + gchar *endptr = NULL; + guint64 valuetmp; + if (!fu_memcpy_safe((guint8 *)buffer, + sizeof(buffer), + 0x0, /* dst */ + (const guint8 *)data, + datasz, + offset, /* src */ + sizeof(buffer) - 1, + error)) + return FALSE; + valuetmp = g_ascii_strtoull(buffer, &endptr, 16); + if (endptr - buffer != sizeof(buffer) - 1) { + g_autofree gchar *str = fu_strsafe(buffer, sizeof(buffer)); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as hex", + str); + return FALSE; + } + if (value != NULL) + *value = (guint16)valuetmp; + return TRUE; +} + +/** + * fu_firmware_strparse_uint24_safe: + * @data: destination buffer + * @datasz: size of @data, typically the same as `strlen(data)` + * @offset: offset in chars into @data to read + * @value: (out) (nullable): parsed value + * @error: (nullable): optional return location for an error + * + * Parses a base 16 number from a string of 6 characters in length. + * The returned @value will range from from 0 to 0xffffff. + * + * Returns: %TRUE if parsed, %FALSE otherwise + * + * Since: 1.5.6 + **/ +gboolean +fu_firmware_strparse_uint24_safe(const gchar *data, + gsize datasz, + gsize offset, + guint32 *value, + GError **error) +{ + gchar buffer[7] = {'\0'}; + gchar *endptr = NULL; + guint64 valuetmp; + if (!fu_memcpy_safe((guint8 *)buffer, + sizeof(buffer), + 0x0, /* dst */ + (const guint8 *)data, + datasz, + offset, /* src */ + sizeof(buffer) - 1, + error)) + return FALSE; + valuetmp = g_ascii_strtoull(buffer, &endptr, 16); + if (endptr - buffer != sizeof(buffer) - 1) { + g_autofree gchar *str = fu_strsafe(buffer, sizeof(buffer)); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as hex", + str); + return FALSE; + } + if (value != NULL) + *value = (guint16)valuetmp; + return TRUE; +} + +/** + * fu_firmware_strparse_uint32_safe: + * @data: destination buffer + * @datasz: size of @data, typically the same as `strlen(data)` + * @offset: offset in chars into @data to read + * @value: (out) (nullable): parsed value + * @error: (nullable): optional return location for an error + * + * Parses a base 16 number from a string of 8 characters in length. + * The returned @value will range from from 0 to 0xffffffff. + * + * Returns: %TRUE if parsed, %FALSE otherwise + * + * Since: 1.5.6 + **/ +gboolean +fu_firmware_strparse_uint32_safe(const gchar *data, + gsize datasz, + gsize offset, + guint32 *value, + GError **error) +{ + gchar buffer[9] = {'\0'}; + gchar *endptr = NULL; + guint64 valuetmp; + if (!fu_memcpy_safe((guint8 *)buffer, + sizeof(buffer), + 0x0, /* dst */ + (const guint8 *)data, + datasz, + offset, /* src */ + sizeof(buffer) - 1, + error)) + return FALSE; + valuetmp = g_ascii_strtoull(buffer, &endptr, 16); + if (endptr - buffer != sizeof(buffer) - 1) { + g_autofree gchar *str = fu_strsafe(buffer, sizeof(buffer)); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as hex", + str); + return FALSE; + } + if (value != NULL) + *value = (guint32)valuetmp; + return TRUE; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-firmware-common.h b/fwupd-1.8.6/libfwupdplugin/fu-firmware-common.h new file mode 100644 index 0000000000000000000000000000000000000000..905cabbecdccb89b0ce3d962b5e71db0fcc5b5d2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-firmware-common.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gboolean +fu_firmware_strparse_uint4_safe(const gchar *data, + gsize datasz, + gsize offset, + guint8 *value, + GError **error); +gboolean +fu_firmware_strparse_uint8_safe(const gchar *data, + gsize datasz, + gsize offset, + guint8 *value, + GError **error); +gboolean +fu_firmware_strparse_uint16_safe(const gchar *data, + gsize datasz, + gsize offset, + guint16 *value, + GError **error); +gboolean +fu_firmware_strparse_uint24_safe(const gchar *data, + gsize datasz, + gsize offset, + guint32 *value, + GError **error); +gboolean +fu_firmware_strparse_uint32_safe(const gchar *data, + gsize datasz, + gsize offset, + guint32 *value, + GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..b66009d48b25d106bd685a0a662c473de8939bd3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-firmware.c @@ -0,0 +1,2045 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-chunk-private.h" +#include "fu-common.h" +#include "fu-firmware.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * FuFirmware: + * + * A firmware file which can have children which represent the images within. + * + * See also: [class@FuDfuFirmware], [class@FuIhexFirmware], [class@FuSrecFirmware] + */ + +typedef struct { + FuFirmwareFlags flags; + FuFirmware *parent; /* noref */ + GPtrArray *images; /* FuFirmware */ + gchar *version; + guint64 version_raw; + GBytes *bytes; + guint8 alignment; + gchar *id; + gchar *filename; + guint64 idx; + guint64 addr; + guint64 offset; + gsize size; + GPtrArray *chunks; /* nullable, element-type FuChunk */ + GPtrArray *patches; /* nullable, element-type FuFirmwarePatch */ +} FuFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuFirmware, fu_firmware, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fu_firmware_get_instance_private(o)) + +enum { PROP_0, PROP_PARENT, PROP_LAST }; + +#define FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX (32 * 1024 * 1024) + +/** + * fu_firmware_flag_to_string: + * @flag: a #FuFirmwareFlags, e.g. %FU_FIRMWARE_FLAG_DEDUPE_ID + * + * Converts a #FuFirmwareFlags to a string. + * + * Returns: identifier string + * + * Since: 1.5.0 + **/ +const gchar * +fu_firmware_flag_to_string(FuFirmwareFlags flag) +{ + if (flag == FU_FIRMWARE_FLAG_NONE) + return "none"; + if (flag == FU_FIRMWARE_FLAG_DEDUPE_ID) + return "dedupe-id"; + if (flag == FU_FIRMWARE_FLAG_DEDUPE_IDX) + return "dedupe-idx"; + if (flag == FU_FIRMWARE_FLAG_HAS_CHECKSUM) + return "has-checksum"; + if (flag == FU_FIRMWARE_FLAG_HAS_VID_PID) + return "has-vid-pid"; + if (flag == FU_FIRMWARE_FLAG_DONE_PARSE) + return "done-parse"; + if (flag == FU_FIRMWARE_FLAG_HAS_STORED_SIZE) + return "has-stored-size"; + if (flag == FU_FIRMWARE_FLAG_ALWAYS_SEARCH) + return "always-search"; + return NULL; +} + +/** + * fu_firmware_flag_from_string: + * @flag: a string, e.g. `dedupe-id` + * + * Converts a string to a #FuFirmwareFlags. + * + * Returns: enumerated value + * + * Since: 1.5.0 + **/ +FuFirmwareFlags +fu_firmware_flag_from_string(const gchar *flag) +{ + if (g_strcmp0(flag, "dedupe-id") == 0) + return FU_FIRMWARE_FLAG_DEDUPE_ID; + if (g_strcmp0(flag, "dedupe-idx") == 0) + return FU_FIRMWARE_FLAG_DEDUPE_IDX; + if (g_strcmp0(flag, "has-checksum") == 0) + return FU_FIRMWARE_FLAG_HAS_CHECKSUM; + if (g_strcmp0(flag, "has-vid-pid") == 0) + return FU_FIRMWARE_FLAG_HAS_VID_PID; + if (g_strcmp0(flag, "done-parse") == 0) + return FU_FIRMWARE_FLAG_DONE_PARSE; + if (g_strcmp0(flag, "has-stored-size") == 0) + return FU_FIRMWARE_FLAG_HAS_STORED_SIZE; + if (g_strcmp0(flag, "always-search") == 0) + return FU_FIRMWARE_FLAG_ALWAYS_SEARCH; + return FU_FIRMWARE_FLAG_NONE; +} + +typedef struct { + gsize offset; + GBytes *blob; +} FuFirmwarePatch; + +static void +fu_firmware_patch_free(FuFirmwarePatch *ptch) +{ + g_bytes_unref(ptch->blob); + g_free(ptch); +} + +/** + * fu_firmware_add_flag: + * @firmware: a #FuFirmware + * @flag: the firmware flag + * + * Adds a specific firmware flag to the firmware. + * + * Since: 1.5.0 + **/ +void +fu_firmware_add_flag(FuFirmware *firmware, FuFirmwareFlags flag) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(firmware); + g_return_if_fail(FU_IS_FIRMWARE(firmware)); + priv->flags |= flag; +} + +/** + * fu_firmware_has_flag: + * @firmware: a #FuFirmware + * @flag: the firmware flag + * + * Finds if the firmware has a specific firmware flag. + * + * Returns: %TRUE if the flag is set + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_has_flag(FuFirmware *firmware, FuFirmwareFlags flag) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(firmware); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fu_firmware_get_version: + * @self: a #FuFirmware + * + * Gets an optional version that represents the firmware. + * + * Returns: a string, or %NULL + * + * Since: 1.3.3 + **/ +const gchar * +fu_firmware_get_version(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + return priv->version; +} + +/** + * fu_firmware_set_version: + * @self: a #FuFirmware + * @version: (nullable): optional string version + * + * Sets an optional version that represents the firmware. + * + * Since: 1.3.3 + **/ +void +fu_firmware_set_version(FuFirmware *self, const gchar *version) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + + /* not changed */ + if (g_strcmp0(priv->version, version) == 0) + return; + + g_free(priv->version); + priv->version = g_strdup(version); +} + +/** + * fu_firmware_get_version_raw: + * @self: a #FuFirmware + * + * Gets an raw version that represents the firmware. This is most frequently + * used when building firmware with `0x123456` in a + * `firmware.builder.xml` file to avoid string splitting and sanity checks. + * + * Returns: an integer, or %G_MAXUINT64 for invalid + * + * Since: 1.5.7 + **/ +guint64 +fu_firmware_get_version_raw(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64); + return priv->version_raw; +} + +/** + * fu_firmware_set_version_raw: + * @self: a #FuFirmware + * @version_raw: a raw version, or %G_MAXUINT64 for invalid + * + * Sets an raw version that represents the firmware. + * + * This is optional, and is typically only used for debugging. + * + * Since: 1.5.7 + **/ +void +fu_firmware_set_version_raw(FuFirmware *self, guint64 version_raw) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + priv->version_raw = version_raw; +} + +/** + * fu_firmware_get_filename: + * @self: a #FuFirmware + * + * Gets an optional filename that represents the image source or destination. + * + * Returns: a string, or %NULL + * + * Since: 1.6.0 + **/ +const gchar * +fu_firmware_get_filename(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + return priv->filename; +} + +/** + * fu_firmware_set_filename: + * @self: a #FuFirmware + * @filename: (nullable): a string filename + * + * Sets an optional filename that represents the image source or destination. + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_filename(FuFirmware *self, const gchar *filename) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + + /* not changed */ + if (g_strcmp0(priv->filename, filename) == 0) + return; + + g_free(priv->filename); + priv->filename = g_strdup(filename); +} + +/** + * fu_firmware_set_id: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. `config` + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_id(FuFirmware *self, const gchar *id) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + g_free(priv->id); + priv->id = g_strdup(id); +} + +/** + * fu_firmware_get_id: + * @self: a #FuPlugin + * + * Gets the image ID, typically set at construction. + * + * Returns: image ID, e.g. `config` + * + * Since: 1.6.0 + **/ +const gchar * +fu_firmware_get_id(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + return priv->id; +} + +/** + * fu_firmware_set_addr: + * @self: a #FuPlugin + * @addr: integer + * + * Sets the base address of the image. + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_addr(FuFirmware *self, guint64 addr) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + priv->addr = addr; +} + +/** + * fu_firmware_get_addr: + * @self: a #FuPlugin + * + * Gets the base address of the image. + * + * Returns: integer + * + * Since: 1.6.0 + **/ +guint64 +fu_firmware_get_addr(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64); + return priv->addr; +} + +/** + * fu_firmware_set_offset: + * @self: a #FuPlugin + * @offset: integer + * + * Sets the base offset of the image. + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_offset(FuFirmware *self, guint64 offset) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + priv->offset = offset; +} + +/** + * fu_firmware_get_offset: + * @self: a #FuPlugin + * + * Gets the base offset of the image. + * + * Returns: integer + * + * Since: 1.6.0 + **/ +guint64 +fu_firmware_get_offset(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64); + return priv->offset; +} + +/** + * fu_firmware_get_parent: + * @self: a #FuFirmware + * + * Gets the parent. + * + * Returns: (transfer none): the parent firmware, or %NULL if unset + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_firmware_get_parent(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + return priv->parent; +} + +/** + * fu_firmware_set_parent: + * @self: a #FuFirmware + * @parent: (nullable): another #FuFirmware + * + * Sets the parent. Only used internally. + * + * Since: 1.8.2 + **/ +void +fu_firmware_set_parent(FuFirmware *self, FuFirmware *parent) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + + if (priv->parent != NULL) + g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + if (parent != NULL) + g_object_add_weak_pointer(G_OBJECT(parent), (gpointer *)&priv->parent); + priv->parent = parent; +} + +/** + * fu_firmware_set_size: + * @self: a #FuPlugin + * @size: integer + * + * Sets the total size of the image, which should be the same size as the + * data from fu_firmware_write(). + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_size(FuFirmware *self, gsize size) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + priv->size = size; +} + +/** + * fu_firmware_get_size: + * @self: a #FuPlugin + * + * Gets the total size of the image, which is typically the same size as the + * data from fu_firmware_write(). + * + * If the size has not been explicitly set, and fu_firmware_set_bytes() has been + * used then the size of this is used instead. + * + * Returns: integer + * + * Since: 1.6.0 + **/ +gsize +fu_firmware_get_size(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXSIZE); + if (priv->size != 0) + return priv->size; + if (priv->bytes != NULL) + return g_bytes_get_size(priv->bytes); + return 0; +} + +/** + * fu_firmware_set_idx: + * @self: a #FuPlugin + * @idx: integer + * + * Sets the index of the image which is used for ordering. + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_idx(FuFirmware *self, guint64 idx) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + priv->idx = idx; +} + +/** + * fu_firmware_get_idx: + * @self: a #FuPlugin + * + * Gets the index of the image which is used for ordering. + * + * Returns: integer + * + * Since: 1.6.0 + **/ +guint64 +fu_firmware_get_idx(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64); + return priv->idx; +} + +/** + * fu_firmware_set_bytes: + * @self: a #FuPlugin + * @bytes: data blob + * + * Sets the contents of the image if not created with fu_firmware_new_from_bytes(). + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_bytes(FuFirmware *self, GBytes *bytes) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + g_return_if_fail(bytes != NULL); + if (priv->bytes != NULL) + g_bytes_unref(priv->bytes); + priv->bytes = g_bytes_ref(bytes); +} + +/** + * fu_firmware_get_bytes: + * @self: a #FuPlugin + * @error: (nullable): optional return location for an error + * + * Gets the firmware payload, which does not have any header or footer included. + * + * If there is more than one potential payload or image section then fu_firmware_add_image() + * should be used instead. + * + * Returns: (transfer full): a #GBytes, or %NULL if the payload has never been set + * + * Since: 1.6.0 + **/ +GBytes * +fu_firmware_get_bytes(FuFirmware *self, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + if (priv->bytes == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no payload set"); + return NULL; + } + return g_bytes_ref(priv->bytes); +} + +/** + * fu_firmware_get_bytes_with_patches: + * @self: a #FuPlugin + * @error: (nullable): optional return location for an error + * + * Gets the firmware payload, with any defined patches applied. + * + * Returns: (transfer full): a #GBytes, or %NULL if the payload has never been set + * + * Since: 1.7.4 + **/ +GBytes * +fu_firmware_get_bytes_with_patches(FuFirmware *self, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + + if (priv->bytes == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no payload set"); + return NULL; + } + + /* usual case */ + if (priv->patches == NULL) + return g_bytes_ref(priv->bytes); + + /* convert to a mutable buffer, apply each patch, aborting if the offset isn't valid */ + fu_byte_array_append_bytes(buf, priv->bytes); + for (guint i = 0; i < priv->patches->len; i++) { + FuFirmwarePatch *ptch = g_ptr_array_index(priv->patches, i); + if (!fu_memcpy_safe(buf->data, + buf->len, + ptch->offset, /* dst */ + g_bytes_get_data(ptch->blob, NULL), + g_bytes_get_size(ptch->blob), + 0x0, /* src */ + g_bytes_get_size(ptch->blob), + error)) { + g_prefix_error(error, "failed to apply patch @0x%x: ", (guint)ptch->offset); + return NULL; + } + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +/** + * fu_firmware_set_alignment: + * @self: a #FuFirmware + * @alignment: integer, or 0 to disable + * + * Sets the alignment of the firmware. + * + * This allows a firmware to pad to a power of 2 boundary, where @alignment + * is the bit position to align to. + * + * Since: 1.6.0 + **/ +void +fu_firmware_set_alignment(FuFirmware *self, guint8 alignment) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + priv->alignment = alignment; +} + +/** + * fu_firmware_get_alignment: + * @self: a #FuFirmware + * + * Gets the alignment of the firmware. + * + * This allows a firmware to pad to a power of 2 boundary, where @alignment + * is the bit position to align to. + * + * Returns: integer + * + * Since: 1.6.0 + **/ +guint8 +fu_firmware_get_alignment(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT8); + return priv->alignment; +} + +/** + * fu_firmware_get_chunks: + * @self: a #FuFirmware + * @error: (nullable): optional return location for an error + * + * Gets the optional image chunks. + * + * Returns: (transfer container) (element-type FuChunk) (nullable): chunk data, or %NULL + * + * Since: 1.6.0 + **/ +GPtrArray * +fu_firmware_get_chunks(FuFirmware *self, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* set */ + if (priv->chunks != NULL) + return g_ptr_array_ref(priv->chunks); + + /* lets build something plausible */ + if (priv->bytes != NULL) { + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(FuChunk) chk = NULL; + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + chk = fu_chunk_bytes_new(priv->bytes); + fu_chunk_set_idx(chk, priv->idx); + fu_chunk_set_address(chk, priv->addr); + g_ptr_array_add(chunks, g_steal_pointer(&chk)); + return g_steal_pointer(&chunks); + } + + /* nothing to do */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no bytes or chunks found in firmware"); + return NULL; +} + +/** + * fu_firmware_add_chunk: + * @self: a #FuFirmware + * @chk: a #FuChunk + * + * Adds a chunk to the image. + * + * Since: 1.6.0 + **/ +void +fu_firmware_add_chunk(FuFirmware *self, FuChunk *chk) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + g_return_if_fail(FU_IS_CHUNK(chk)); + if (priv->chunks == NULL) + priv->chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_ptr_array_add(priv->chunks, g_object_ref(chk)); +} + +/** + * fu_firmware_get_checksum: + * @self: a #FuPlugin + * @csum_kind: a checksum type, e.g. %G_CHECKSUM_SHA256 + * @error: (nullable): optional return location for an error + * + * Returns a checksum of the payload data. + * + * Returns: (transfer full): a checksum string, or %NULL if the checksum is not available + * + * Since: 1.6.0 + **/ +gchar * +fu_firmware_get_checksum(FuFirmware *self, GChecksumType csum_kind, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + g_autoptr(GBytes) blob = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* subclassed */ + if (klass->get_checksum != NULL) + return klass->get_checksum(self, csum_kind, error); + + /* internal data */ + if (priv->bytes != NULL) + return g_compute_checksum_for_bytes(csum_kind, priv->bytes); + + /* write */ + blob = fu_firmware_write(self, error); + if (blob == NULL) + return NULL; + return g_compute_checksum_for_bytes(csum_kind, blob); +} + +/** + * fu_firmware_tokenize: + * @self: a #FuFirmware + * @fw: firmware blob + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Tokenizes a firmware, typically breaking the firmware into records. + * + * Records can be enumerated using subclass-specific functionality, for example + * using fu_srec_firmware_get_records(). + * + * Returns: %TRUE for success + * + * Since: 1.3.2 + **/ +gboolean +fu_firmware_tokenize(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optionally subclassed */ + if (klass->tokenize != NULL) + return klass->tokenize(self, fw, flags, error); + return TRUE; +} + +/** + * fu_firmware_check_compatible: + * @self: a #FuFirmware + * @other: a #FuFirmware + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Check a new firmware is compatible with the existing firmware. + * + * Returns: %TRUE for success + * + * Since: 1.8.4 + **/ +gboolean +fu_firmware_check_compatible(FuFirmware *self, + FuFirmware *other, + FwupdInstallFlags flags, + GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(other), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optionally subclassed */ + if (klass->check_compatible == NULL) + return TRUE; + return klass->check_compatible(self, other, flags, error); +} + +static gboolean +fu_firmware_check_magic_for_offset(FuFirmware *self, + GBytes *fw, + gsize *offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + + /* not implemented */ + if (klass->check_magic == NULL) + return TRUE; + + /* fuzzing */ + if (!fu_firmware_has_flag(self, FU_FIRMWARE_FLAG_ALWAYS_SEARCH) && + (flags & FWUPD_INSTALL_FLAG_NO_SEARCH) > 0) { + if (!klass->check_magic(self, fw, *offset, error)) { + g_prefix_error(error, "not searching magic due to install flags: "); + return FALSE; + } + return TRUE; + } + + /* limit the size of firmware we search */ + if (g_bytes_get_size(fw) > FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX) { + if (!klass->check_magic(self, fw, *offset, error)) { + g_prefix_error(error, + "failed to search for magic as firmware size was 0x%x and " + "limit was 0x%x: ", + (guint)g_bytes_get_size(fw), + (guint)FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX); + return FALSE; + } + return TRUE; + } + + /* increment the offset, looking for the magic */ + for (gsize offset_tmp = *offset; offset_tmp < g_bytes_get_size(fw); offset_tmp++) { + if (klass->check_magic(self, fw, offset_tmp, NULL)) { + fu_firmware_set_offset(self, offset_tmp); + *offset = offset_tmp; + return TRUE; + } + } + + /* did not find what we were looking for */ + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "did not find magic"); + return FALSE; +} + +/** + * fu_firmware_parse_full: + * @self: a #FuFirmware + * @fw: firmware blob + * @offset: start offset, useful for ignoring a bootloader + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Parses a firmware, typically breaking the firmware into images. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_firmware_parse_full(FuFirmware *self, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + FuFirmwarePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* sanity check */ + if (fu_firmware_has_flag(self, FU_FIRMWARE_FLAG_DONE_PARSE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware object cannot be reused"); + return FALSE; + } + if (g_bytes_get_size(fw) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid firmware as zero sized"); + return FALSE; + } + + /* any FuFirmware subclass that gets past this point might have allocated memory in + * ->tokenize() or ->parse() and needs to be destroyed before parsing again */ + fu_firmware_add_flag(self, FU_FIRMWARE_FLAG_DONE_PARSE); + + /* subclassed */ + if (klass->tokenize != NULL) { + if (!klass->tokenize(self, fw, flags, error)) + return FALSE; + } + if (!fu_firmware_check_magic_for_offset(self, fw, &offset, flags, error)) + return FALSE; + + /* always set by default */ + fu_firmware_set_bytes(self, fw); + + /* handled by the subclass */ + if (klass->parse != NULL) + return klass->parse(self, fw, offset, flags, error); + + /* verify alignment */ + if (g_bytes_get_size(fw) % (1ull << priv->alignment) != 0) { + g_autofree gchar *str = NULL; + str = g_format_size_full(1ull << priv->alignment, G_FORMAT_SIZE_IEC_UNITS); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "raw firmware is not aligned to 0x%x (%s)", + (guint)(1ull << priv->alignment), + str); + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_firmware_parse: + * @self: a #FuFirmware + * @fw: firmware blob + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Parses a firmware, typically breaking the firmware into images. + * + * Returns: %TRUE for success + * + * Since: 1.3.1 + **/ +gboolean +fu_firmware_parse(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + return fu_firmware_parse_full(self, fw, 0x0, flags, error); +} + +/** + * fu_firmware_build: + * @self: a #FuFirmware + * @n: a Xmlb node + * @error: (nullable): optional return location for an error + * + * Builds a firmware from an XML manifest. The manifest would typically have the + * following form: + * + * |[ + * + * + * 1.2.3 + * + * 7.8.9 + * stage1 + * 0x01 + * stage1.bin + * + * + * stage2 + * + * + * + * ape + * 0x7 + * aGVsbG8gd29ybGQ= + * + * + * ]| + * + * This would be used in a build-system to merge images from generated files: + * `fwupdtool firmware-build fw.builder.xml test.fw` + * + * Static binary content can be specified in the `/` section and + * is encoded as base64 text if not empty. + * + * Additionally, extra nodes can be included under nested `` objects + * which can be parsed by the subclassed objects. You should verify the + * subclassed object `FuFirmware->build` vfunc for the specific additional + * options supported. + * + * Plugins should manually g_type_ensure() subclassed image objects if not + * constructed as part of the plugin fu_plugin_init() or fu_plugin_setup() + * functions. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_build(FuFirmware *self, XbNode *n, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + const gchar *tmp; + guint64 tmpval; + guint64 version_raw; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GPtrArray) xb_images = NULL; + g_autoptr(XbNode) data = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(XB_IS_NODE(n), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* set attributes */ + tmp = xb_node_query_text(n, "version", NULL); + if (tmp != NULL) + fu_firmware_set_version(self, tmp); + version_raw = xb_node_query_text_as_uint(n, "version_raw", NULL); + if (version_raw != G_MAXUINT64) + fu_firmware_set_version_raw(self, version_raw); + tmp = xb_node_query_text(n, "id", NULL); + if (tmp != NULL) + fu_firmware_set_id(self, tmp); + tmpval = xb_node_query_text_as_uint(n, "idx", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_set_idx(self, tmpval); + tmpval = xb_node_query_text_as_uint(n, "addr", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_set_addr(self, tmpval); + tmpval = xb_node_query_text_as_uint(n, "offset", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_set_offset(self, tmpval); + tmpval = xb_node_query_text_as_uint(n, "size", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_set_size(self, tmpval); + tmpval = xb_node_query_text_as_uint(n, "alignment", NULL); + if (tmpval != G_MAXUINT64) { + if (tmpval > FU_FIRMWARE_ALIGNMENT_2G) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "0x%x invalid, maximum is 0x%x", + (guint)tmpval, + (guint)FU_FIRMWARE_ALIGNMENT_2G); + return FALSE; + } + fu_firmware_set_alignment(self, (guint8)tmpval); + } + tmp = xb_node_query_text(n, "filename", NULL); + if (tmp != NULL) { + g_autoptr(GBytes) blob = NULL; + blob = fu_bytes_get_contents(tmp, error); + if (blob == NULL) + return FALSE; + fu_firmware_set_bytes(self, blob); + fu_firmware_set_filename(self, tmp); + } + data = xb_node_query_first(n, "data", NULL); + if (data != NULL) { + guint64 sz = xb_node_get_attr_as_uint(data, "size"); + g_autoptr(GBytes) blob = NULL; + + /* base64 encoded data */ + if (xb_node_get_text(data) != NULL) { + gsize bufsz = 0; + g_autofree guchar *buf = NULL; + buf = g_base64_decode(xb_node_get_text(data), &bufsz); + blob = g_bytes_new(buf, bufsz); + } else { + blob = g_bytes_new(NULL, 0); + } + + /* padding is optional */ + if (sz == 0 || sz == G_MAXUINT64) { + fu_firmware_set_bytes(self, blob); + } else { + g_autoptr(GBytes) blob_padded = fu_bytes_pad(blob, (gsize)sz); + fu_firmware_set_bytes(self, blob_padded); + } + } + + /* optional chunks */ + chunks = xb_node_query(n, "chunks/chunk", 0, NULL); + if (chunks != NULL) { + for (guint i = 0; i < chunks->len; i++) { + XbNode *c = g_ptr_array_index(chunks, i); + g_autoptr(FuChunk) chk = fu_chunk_bytes_new(NULL); + fu_chunk_set_idx(chk, i); + if (!fu_chunk_build(chk, c, error)) + return FALSE; + fu_firmware_add_chunk(self, chk); + } + } + + /* parse images */ + xb_images = xb_node_query(n, "firmware", 0, NULL); + if (xb_images != NULL) { + for (guint i = 0; i < xb_images->len; i++) { + XbNode *xb_image = g_ptr_array_index(xb_images, i); + g_autoptr(FuFirmware) img = NULL; + tmp = xb_node_get_attr(xb_image, "gtype"); + if (tmp != NULL) { + GType gtype = g_type_from_name(tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not registered", + tmp); + return FALSE; + } + img = g_object_new(gtype, NULL); + } else { + img = fu_firmware_new(); + } + if (!fu_firmware_build(img, xb_image, error)) + return FALSE; + fu_firmware_add_image(self, img); + } + } + + /* subclassed */ + if (klass->build != NULL) { + if (!klass->build(self, n, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_firmware_build_from_xml: + * @self: a #FuFirmware + * @xml: XML text + * @error: (nullable): optional return location for an error + * + * Builds a firmware from an XML manifest. The manifest would typically have the + * following form: + * + * |[ + * + * + * 1.2.3 + * + * 7.8.9 + * stage1 + * 0x01 + * stage1.bin + * + * + * stage2 + * + * + * + * ape + * 0x7 + * aGVsbG8gd29ybGQ= + * + * + * ]| + * + * This would be used in a build-system to merge images from generated files: + * `fwupdtool firmware-build fw.builder.xml test.fw` + * + * Static binary content can be specified in the `/` section and + * is encoded as base64 text if not empty. + * + * Additionally, extra nodes can be included under nested `` objects + * which can be parsed by the subclassed objects. You should verify the + * subclassed object `FuFirmware->build` vfunc for the specific additional + * options supported. + * + * Plugins should manually g_type_ensure() subclassed image objects if not + * constructed as part of the plugin fu_plugin_init() or fu_plugin_setup() + * functions. + * + * Returns: %TRUE for success + * + * Since: 1.6.0 + **/ +gboolean +fu_firmware_build_from_xml(FuFirmware *self, const gchar *xml, GError **error) +{ + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* parse XML */ + if (!xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) { + g_prefix_error(error, "could not parse XML: "); + 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; + + /* create FuFirmware of specific GType */ + n = xb_silo_query_first(silo, "firmware", error); + if (n == NULL) + return FALSE; + return fu_firmware_build(self, n, error); +} + +/** + * fu_firmware_parse_file: + * @self: a #FuFirmware + * @file: a file + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Parses a firmware file, typically breaking the firmware into images. + * + * Returns: %TRUE for success + * + * Since: 1.3.3 + **/ +gboolean +fu_firmware_parse_file(FuFirmware *self, GFile *file, FwupdInstallFlags flags, GError **error) +{ + gchar *buf = NULL; + gsize bufsz = 0; + g_autoptr(GBytes) fw = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(G_IS_FILE(file), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!g_file_load_contents(file, NULL, &buf, &bufsz, NULL, error)) + return FALSE; + fw = g_bytes_new_take(buf, bufsz); + return fu_firmware_parse(self, fw, flags, error); +} + +/** + * fu_firmware_write: + * @self: a #FuFirmware + * @error: (nullable): optional return location for an error + * + * Writes a firmware, typically packing the images into a binary blob. + * + * Returns: (transfer full): a data blob + * + * Since: 1.3.1 + **/ +GBytes * +fu_firmware_write(FuFirmware *self, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* subclassed */ + if (klass->write != NULL) + return klass->write(self, error); + + /* just add default blob */ + return fu_firmware_get_bytes_with_patches(self, error); +} + +/** + * fu_firmware_add_patch: + * @self: a #FuFirmware + * @offset: an address smaller than fu_firmware_get_size() + * @blob: (not nullable): bytes to replace + * + * Adds a byte patch at a specific offset. If a patch already exists at the specified address then + * it is replaced. + * + * If the @address is larger than the size of the image then an error is returned. + * + * Since: 1.7.4 + **/ +void +fu_firmware_add_patch(FuFirmware *self, gsize offset, GBytes *blob) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + FuFirmwarePatch *ptch; + + g_return_if_fail(FU_IS_FIRMWARE(self)); + g_return_if_fail(blob != NULL); + + /* ensure exists */ + if (priv->patches == NULL) { + priv->patches = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_firmware_patch_free); + } + + /* find existing of exact same size */ + for (guint i = 0; i < priv->patches->len; i++) { + ptch = g_ptr_array_index(priv->patches, i); + if (ptch->offset == offset && + g_bytes_get_size(ptch->blob) == g_bytes_get_size(blob)) { + g_bytes_unref(ptch->blob); + ptch->blob = g_bytes_ref(blob); + return; + } + } + + /* add new */ + ptch = g_new0(FuFirmwarePatch, 1); + ptch->offset = offset; + ptch->blob = g_bytes_ref(blob); + g_ptr_array_add(priv->patches, ptch); +} + +/** + * fu_firmware_write_chunk: + * @self: a #FuFirmware + * @address: an address smaller than fu_firmware_get_addr() + * @chunk_sz_max: the size of the new chunk + * @error: (nullable): optional return location for an error + * + * Gets a block of data from the image. If the contents of the image is + * smaller than the requested chunk size then the #GBytes will be smaller + * than @chunk_sz_max. Use fu_bytes_pad() if padding is required. + * + * If the @address is larger than the size of the image then an error is returned. + * + * Returns: (transfer full): a #GBytes, or %NULL + * + * Since: 1.6.0 + **/ +GBytes * +fu_firmware_write_chunk(FuFirmware *self, guint64 address, guint64 chunk_sz_max, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + gsize chunk_left; + guint64 offset; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* check address requested is larger than base address */ + if (address < priv->addr) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "requested address 0x%x less than base address 0x%x", + (guint)address, + (guint)priv->addr); + return NULL; + } + + /* offset into data */ + offset = address - priv->addr; + if (offset > g_bytes_get_size(priv->bytes)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "offset 0x%x larger than data size 0x%x", + (guint)offset, + (guint)g_bytes_get_size(priv->bytes)); + return NULL; + } + + /* if we have less data than requested */ + chunk_left = g_bytes_get_size(priv->bytes) - offset; + if (chunk_sz_max > chunk_left) { + return fu_bytes_new_offset(priv->bytes, offset, chunk_left, error); + } + + /* check chunk */ + return fu_bytes_new_offset(priv->bytes, offset, chunk_sz_max, error); +} + +/** + * fu_firmware_write_file: + * @self: a #FuFirmware + * @file: a file + * @error: (nullable): optional return location for an error + * + * Writes a firmware, typically packing the images into a binary blob. + * + * Returns: %TRUE for success + * + * Since: 1.3.3 + **/ +gboolean +fu_firmware_write_file(FuFirmware *self, GFile *file, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(G_IS_FILE(file), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + blob = fu_firmware_write(self, error); + if (blob == NULL) + return FALSE; + return g_file_replace_contents(file, + g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + NULL, + FALSE, + G_FILE_CREATE_NONE, + NULL, + NULL, + error); +} + +/** + * fu_firmware_add_image: + * @self: a #FuPlugin + * @img: a child firmware image + * + * Adds an image to the firmware. + * + * If %FU_FIRMWARE_FLAG_DEDUPE_ID is set, an image with the same ID is already + * present it is replaced. + * + * Since: 1.3.1 + **/ +void +fu_firmware_add_image(FuFirmware *self, FuFirmware *img) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); + g_return_if_fail(FU_IS_FIRMWARE(img)); + + /* dedupe */ + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img_tmp = g_ptr_array_index(priv->images, i); + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) { + if (g_strcmp0(fu_firmware_get_id(img_tmp), fu_firmware_get_id(img)) == 0) { + g_ptr_array_remove_index(priv->images, i); + break; + } + } + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) { + if (fu_firmware_get_idx(img_tmp) == fu_firmware_get_idx(img)) { + g_ptr_array_remove_index(priv->images, i); + break; + } + } + } + + g_ptr_array_add(priv->images, g_object_ref(img)); + + /* set the other way around */ + fu_firmware_set_parent(img, self); +} + +/** + * fu_firmware_remove_image: + * @self: a #FuPlugin + * @img: a child firmware image + * @error: (nullable): optional return location for an error + * + * Remove an image from the firmware. + * + * Returns: %TRUE if the image was removed + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_remove_image(FuFirmware *self, FuFirmware *img, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(img), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (g_ptr_array_remove(priv->images, img)) + return TRUE; + + /* did not exist */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "image %s not found in firmware", + fu_firmware_get_id(img)); + return FALSE; +} + +/** + * fu_firmware_remove_image_by_idx: + * @self: a #FuPlugin + * @idx: index + * @error: (nullable): optional return location for an error + * + * Removes the first image from the firmware matching the index. + * + * Returns: %TRUE if an image was removed + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_remove_image_by_idx(FuFirmware *self, guint64 idx, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) img = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + img = fu_firmware_get_image_by_idx(self, idx, error); + if (img == NULL) + return FALSE; + g_ptr_array_remove(priv->images, img); + return TRUE; +} + +/** + * fu_firmware_remove_image_by_id: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. `config` + * @error: (nullable): optional return location for an error + * + * Removes the first image from the firmware matching the ID. + * + * Returns: %TRUE if an image was removed + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_remove_image_by_id(FuFirmware *self, const gchar *id, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) img = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + img = fu_firmware_get_image_by_id(self, id, error); + if (img == NULL) + return FALSE; + g_ptr_array_remove(priv->images, img); + return TRUE; +} + +/** + * fu_firmware_get_images: + * @self: a #FuFirmware + * + * Returns all the images in the firmware. + * + * Returns: (transfer container) (element-type FuFirmware): images + * + * Since: 1.3.1 + **/ +GPtrArray * +fu_firmware_get_images(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) imgs = NULL; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + + imgs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img = g_ptr_array_index(priv->images, i); + g_ptr_array_add(imgs, g_object_ref(img)); + } + return g_steal_pointer(&imgs); +} + +/** + * fu_firmware_get_image_by_id: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. `config` + * @error: (nullable): optional return location for an error + * + * Gets the firmware image using the image ID. + * + * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found + * + * Since: 1.3.1 + **/ +FuFirmware * +fu_firmware_get_image_by_id(FuFirmware *self, const gchar *id, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img = g_ptr_array_index(priv->images, i); + if (g_strcmp0(fu_firmware_get_id(img), id) == 0) + return g_object_ref(img); + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no image id %s found in firmware", + id); + return NULL; +} + +/** + * fu_firmware_get_image_by_id_bytes: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. `config` + * @error: (nullable): optional return location for an error + * + * Gets the firmware image bytes using the image ID. + * + * Returns: (transfer full): a #GBytes of a #FuFirmware, or %NULL if the image is not found + * + * Since: 1.3.1 + **/ +GBytes * +fu_firmware_get_image_by_id_bytes(FuFirmware *self, const gchar *id, GError **error) +{ + g_autoptr(FuFirmware) img = fu_firmware_get_image_by_id(self, id, error); + if (img == NULL) + return NULL; + return fu_firmware_write(img, error); +} + +/** + * fu_firmware_get_image_by_idx: + * @self: a #FuPlugin + * @idx: image index + * @error: (nullable): optional return location for an error + * + * Gets the firmware image using the image index. + * + * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found + * + * Since: 1.3.1 + **/ +FuFirmware * +fu_firmware_get_image_by_idx(FuFirmware *self, guint64 idx, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img = g_ptr_array_index(priv->images, i); + if (fu_firmware_get_idx(img) == idx) + return g_object_ref(img); + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no image idx %" G_GUINT64_FORMAT " found in firmware", + idx); + return NULL; +} + +/** + * fu_firmware_get_image_by_checksum: + * @self: a #FuPlugin + * @checksum: checksum string of any format + * @error: (nullable): optional return location for an error + * + * Gets the firmware image using the image checksum. The checksum type is guessed + * based on the length of the input string. + * + * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found + * + * Since: 1.5.5 + **/ +FuFirmware * +fu_firmware_get_image_by_checksum(FuFirmware *self, const gchar *checksum, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + GChecksumType csum_kind; + + g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); + g_return_val_if_fail(checksum != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + csum_kind = fwupd_checksum_guess_kind(checksum); + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img = g_ptr_array_index(priv->images, i); + g_autofree gchar *checksum_tmp = NULL; + + /* if this expensive then the subclassed FuFirmware can + * cache the result as required */ + checksum_tmp = fu_firmware_get_checksum(img, csum_kind, error); + if (checksum_tmp == NULL) + return NULL; + if (g_strcmp0(checksum_tmp, checksum) == 0) + return g_object_ref(img); + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no image with checksum %s found in firmware", + checksum); + return NULL; +} + +/** + * fu_firmware_get_image_by_idx_bytes: + * @self: a #FuPlugin + * @idx: image index + * @error: (nullable): optional return location for an error + * + * Gets the firmware image bytes using the image index. + * + * Returns: (transfer full): a #GBytes of a #FuFirmware, or %NULL if the image is not found + * + * Since: 1.3.1 + **/ +GBytes * +fu_firmware_get_image_by_idx_bytes(FuFirmware *self, guint64 idx, GError **error) +{ + g_autoptr(FuFirmware) img = fu_firmware_get_image_by_idx(self, idx, error); + if (img == NULL) + return NULL; + return fu_firmware_write(img, error); +} + +/** + * fu_firmware_export: + * @self: a #FuFirmware + * @flags: firmware export flags, e.g. %FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG + * @bn: a Xmlb builder node + * + * This allows us to build an XML object for the nested firmware. + * + * Since: 1.6.0 + **/ +void +fu_firmware_export(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + FuFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *gtypestr = G_OBJECT_TYPE_NAME(self); + + /* object */ + if (g_strcmp0(gtypestr, "FuFirmware") != 0) + xb_builder_node_set_attr(bn, "gtype", gtypestr); + + /* subclassed type */ + if (priv->flags != FU_FIRMWARE_FLAG_NONE) { + g_autoptr(GString) tmp = g_string_new(""); + for (guint i = 0; i < 64; i++) { + guint64 flag = (guint64)1 << i; + if (flag == FU_FIRMWARE_FLAG_DONE_PARSE) + continue; + if ((priv->flags & flag) == 0) + continue; + g_string_append_printf(tmp, "%s|", fu_firmware_flag_to_string(flag)); + } + if (tmp->len > 0) { + g_string_truncate(tmp, tmp->len - 1); + fu_xmlb_builder_insert_kv(bn, "flags", tmp->str); + } + } + fu_xmlb_builder_insert_kv(bn, "id", priv->id); + fu_xmlb_builder_insert_kx(bn, "idx", priv->idx); + fu_xmlb_builder_insert_kv(bn, "version", priv->version); + fu_xmlb_builder_insert_kx(bn, "version_raw", priv->version_raw); + fu_xmlb_builder_insert_kx(bn, "addr", priv->addr); + fu_xmlb_builder_insert_kx(bn, "offset", priv->offset); + fu_xmlb_builder_insert_kx(bn, "alignment", priv->alignment); + fu_xmlb_builder_insert_kx(bn, "size", priv->size); + fu_xmlb_builder_insert_kv(bn, "filename", priv->filename); + if (priv->bytes != NULL) { + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(priv->bytes, &bufsz); + g_autofree gchar *datastr = NULL; + g_autofree gchar *dataszstr = g_strdup_printf("0x%x", (guint)bufsz); + if (flags & FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA) { + datastr = fu_strsafe((const gchar *)buf, MIN(bufsz, 16)); + } else { +#if GLIB_CHECK_VERSION(2, 61, 0) + datastr = g_base64_encode(buf, bufsz); +#else + /* older GLib versions can't cope with buf=NULL */ + if (buf == NULL || bufsz == 0) { + datastr = g_strdup(""); + } else { + datastr = g_base64_encode(buf, bufsz); + } +#endif + } + xb_builder_node_insert_text(bn, "data", datastr, "size", dataszstr, NULL); + } + fu_xmlb_builder_insert_kx(bn, "alignment", priv->alignment); + + /* chunks */ + if (priv->chunks != NULL && priv->chunks->len > 0) { + g_autoptr(XbBuilderNode) bp = xb_builder_node_insert(bn, "chunks", NULL); + for (guint i = 0; i < priv->chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(priv->chunks, i); + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bp, "chunk", NULL); + fu_chunk_export(chk, flags, bc); + } + } + + /* vfunc */ + if (klass->export != NULL) + klass->export(self, flags, bn); + + /* children */ + if (priv->images->len > 0) { + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img = g_ptr_array_index(priv->images, i); + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "firmware", NULL); + fu_firmware_export(img, flags, bc); + } + } +} + +/** + * fu_firmware_export_to_xml: + * @self: a #FuFirmware + * @flags: firmware export flags, e.g. %FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG + * @error: (nullable): optional return location for an error + * + * This allows us to build an XML object for the nested firmware. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 1.6.0 + **/ +gchar * +fu_firmware_export_to_xml(FuFirmware *self, FuFirmwareExportFlags flags, GError **error) +{ + g_autoptr(XbBuilderNode) bn = xb_builder_node_new("firmware"); + fu_firmware_export(self, flags, bn); + return xb_builder_node_export(bn, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | +#if LIBXMLB_CHECK_VERSION(0, 2, 2) + XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY | +#endif + XB_NODE_EXPORT_FLAG_FORMAT_INDENT, + error); +} + +/** + * fu_firmware_to_string: + * @self: a #FuFirmware + * + * This allows us to easily print the object. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 1.3.1 + **/ +gchar * +fu_firmware_to_string(FuFirmware *self) +{ + g_autoptr(XbBuilderNode) bn = xb_builder_node_new("firmware"); + fu_firmware_export(self, + FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG | + FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA, + bn); + return xb_builder_node_export(bn, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | +#if LIBXMLB_CHECK_VERSION(0, 2, 2) + XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY | +#endif + XB_NODE_EXPORT_FLAG_FORMAT_INDENT, + NULL); +} + +static void +fu_firmware_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuFirmware *self = FU_FIRMWARE(object); + FuFirmwarePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, priv->parent); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_firmware_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuFirmware *self = FU_FIRMWARE(object); + switch (prop_id) { + case PROP_PARENT: + fu_firmware_set_parent(self, g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_firmware_init(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + priv->images = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_firmware_finalize(GObject *object) +{ + FuFirmware *self = FU_FIRMWARE(object); + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_free(priv->version); + g_free(priv->id); + g_free(priv->filename); + if (priv->bytes != NULL) + g_bytes_unref(priv->bytes); + if (priv->chunks != NULL) + g_ptr_array_unref(priv->chunks); + if (priv->patches != NULL) + g_ptr_array_unref(priv->patches); + if (priv->parent != NULL) + g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + g_ptr_array_unref(priv->images); + G_OBJECT_CLASS(fu_firmware_parent_class)->finalize(object); +} + +static void +fu_firmware_class_init(FuFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->finalize = fu_firmware_finalize; + object_class->get_property = fu_firmware_get_property; + object_class->set_property = fu_firmware_set_property; + + /** + * FuFirmware:parent: + * + * The firmware parent. + * + * Since: 1.8.2 + */ + pspec = g_param_spec_object("parent", + NULL, + NULL, + FU_TYPE_FIRMWARE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PARENT, pspec); +} + +/** + * fu_firmware_new: + * + * Creates an empty firmware object. + * + * Returns: a #FuFirmware + * + * Since: 1.3.1 + **/ +FuFirmware * +fu_firmware_new(void) +{ + FuFirmware *self = g_object_new(FU_TYPE_FIRMWARE, NULL); + return FU_FIRMWARE(self); +} + +/** + * fu_firmware_new_from_bytes: + * @fw: firmware blob image + * + * Creates a firmware object with the provided image set as default. + * + * Returns: a #FuFirmware + * + * Since: 1.3.1 + **/ +FuFirmware * +fu_firmware_new_from_bytes(GBytes *fw) +{ + FuFirmware *self = fu_firmware_new(); + fu_firmware_set_bytes(self, fw); + return self; +} + +/** + * fu_firmware_new_from_gtypes: + * @fw: firmware blob + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM + * @error: (nullable): optional return location for an error + * @...: an array of #GTypes, ending with %G_TYPE_INVALID + * + * Tries to parse the firmware with each #GType in order. + * + * Returns: (transfer full) (nullable): a #FuFirmware, or %NULL + * + * Since: 1.5.6 + **/ +FuFirmware * +fu_firmware_new_from_gtypes(GBytes *fw, FwupdInstallFlags flags, GError **error, ...) +{ + va_list args; + g_autoptr(GArray) gtypes = g_array_new(FALSE, FALSE, sizeof(GType)); + g_autoptr(GError) error_all = NULL; + + g_return_val_if_fail(fw != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* create array of GTypes */ + va_start(args, error); + while (TRUE) { + GType gtype = va_arg(args, GType); + if (gtype == G_TYPE_INVALID) + break; + g_array_append_val(gtypes, gtype); + } + va_end(args); + + /* invalid */ + if (gtypes->len == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "no GTypes specified"); + return NULL; + } + + /* try each GType in turn */ + for (guint i = 0; i < gtypes->len; i++) { + GType gtype = g_array_index(gtypes, GType, i); + g_autoptr(FuFirmware) firmware = g_object_new(gtype, NULL); + g_autoptr(GError) error_local = NULL; + if (!fu_firmware_parse(firmware, fw, flags, &error_local)) { + if (error_all == NULL) { + g_propagate_error(&error_all, g_steal_pointer(&error_local)); + } else { + g_prefix_error(&error_all, "%s: ", error_local->message); + } + continue; + } + return g_steal_pointer(&firmware); + } + + /* failed */ + g_propagate_error(error, g_steal_pointer(&error_all)); + return NULL; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..0d02229b09f719b30a3e51ef64040a58d54a354b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-firmware.h @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fu-chunk.h" +#include "fu-firmware.h" + +#define FU_TYPE_FIRMWARE (fu_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuFirmware, fu_firmware, FU, FIRMWARE, GObject) + +/** + * FU_FIRMWARE_EXPORT_FLAG_NONE: + * + * No flags set. + * + * Since: 1.6.0 + **/ +#define FU_FIRMWARE_EXPORT_FLAG_NONE (0u) +/** + * FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG: + * + * Include debug information when exporting. + * + * Since: 1.6.0 + **/ +#define FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG (1u << 0) +/** + * FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA: + * + * Write the data as UTF-8 strings. + * + * Since: 1.6.0 + **/ +#define FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA (1u << 1) + +/** + * FuFirmwareExportFlags: + * + * The firmware export flags. + **/ +typedef guint64 FuFirmwareExportFlags; + +struct _FuFirmwareClass { + GObjectClass parent_class; + gboolean (*parse)(FuFirmware *self, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + GBytes *(*write)(FuFirmware *self, GError **error)G_GNUC_WARN_UNUSED_RESULT; + void (*export)(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn); + gboolean (*tokenize)(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; + gboolean (*build)(FuFirmware *self, XbNode *n, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gchar *(*get_checksum)(FuFirmware *self, + GChecksumType csum_kind, + GError **error)G_GNUC_WARN_UNUSED_RESULT; + gboolean (*check_magic)(FuFirmware *self, GBytes *fw, gsize offset, GError **error); + gboolean (*check_compatible)(FuFirmware *self, + FuFirmware *other, + FwupdInstallFlags flags, + GError **error); +}; + +/** + * FU_FIRMWARE_FLAG_NONE: + * + * No flags set. + * + * Since: 1.5.0 + **/ +#define FU_FIRMWARE_FLAG_NONE (0u) +/** + * FU_FIRMWARE_FLAG_DEDUPE_ID: + * + * Dedupe imges by ID. + * + * Since: 1.5.0 + **/ +#define FU_FIRMWARE_FLAG_DEDUPE_ID (1u << 0) +/** + * FU_FIRMWARE_FLAG_DEDUPE_IDX: + * + * Dedupe imges by IDX. + * + * Since: 1.5.0 + **/ +#define FU_FIRMWARE_FLAG_DEDUPE_IDX (1u << 1) +/** + * FU_FIRMWARE_FLAG_HAS_CHECKSUM: + * + * Has a CRC or checksum to test internal consistency. + * + * Since: 1.5.6 + **/ +#define FU_FIRMWARE_FLAG_HAS_CHECKSUM (1u << 2) +/** + * FU_FIRMWARE_FLAG_HAS_VID_PID: + * + * Has a vendor or product ID in the firmware. + * + * Since: 1.5.6 + **/ +#define FU_FIRMWARE_FLAG_HAS_VID_PID (1u << 3) +/** + * FU_FIRMWARE_FLAG_DONE_PARSE: + * + * The firmware object has been used by fu_firmware_parse_full(). + * + * Since: 1.7.3 + **/ +#define FU_FIRMWARE_FLAG_DONE_PARSE (1u << 4) +/** + * FU_FIRMWARE_FLAG_HAS_STORED_SIZE: + * + * Encodes the image size in the firmware. + * + * Since: 1.8.2 + **/ +#define FU_FIRMWARE_FLAG_HAS_STORED_SIZE (1u << 5) +/** + * FU_FIRMWARE_FLAG_ALWAYS_SEARCH: + * + * Always searches for magic regardless of the install flags. + * This is useful for firmware that always has an *unparsed* variable-length + * header. + * + * Since: 1.8.6 + **/ +#define FU_FIRMWARE_FLAG_ALWAYS_SEARCH (1u << 6) + +/** + * FuFirmwareFlags: + * + * The firmware flags. + **/ +typedef guint64 FuFirmwareFlags; + +/** + * FU_FIRMWARE_ID_PAYLOAD: + * + * The usual firmware ID string for the payload. + * + * Since: 1.6.0 + **/ +#define FU_FIRMWARE_ID_PAYLOAD "payload" +/** + * FU_FIRMWARE_ID_SIGNATURE: + * + * The usual firmware ID string for the signature. + * + * Since: 1.6.0 + **/ +#define FU_FIRMWARE_ID_SIGNATURE "signature" +/** + * FU_FIRMWARE_ID_HEADER: + * + * The usual firmware ID string for the header. + * + * Since: 1.6.0 + **/ +#define FU_FIRMWARE_ID_HEADER "header" + +#define FU_FIRMWARE_ALIGNMENT_1 0x00 +#define FU_FIRMWARE_ALIGNMENT_2 0x01 +#define FU_FIRMWARE_ALIGNMENT_4 0x02 +#define FU_FIRMWARE_ALIGNMENT_8 0x03 +#define FU_FIRMWARE_ALIGNMENT_16 0x04 +#define FU_FIRMWARE_ALIGNMENT_32 0x05 +#define FU_FIRMWARE_ALIGNMENT_64 0x06 +#define FU_FIRMWARE_ALIGNMENT_128 0x07 +#define FU_FIRMWARE_ALIGNMENT_256 0x08 +#define FU_FIRMWARE_ALIGNMENT_512 0x09 +#define FU_FIRMWARE_ALIGNMENT_1K 0x0A +#define FU_FIRMWARE_ALIGNMENT_2K 0x0B +#define FU_FIRMWARE_ALIGNMENT_4K 0x0C +#define FU_FIRMWARE_ALIGNMENT_8K 0x0D +#define FU_FIRMWARE_ALIGNMENT_16K 0x0E +#define FU_FIRMWARE_ALIGNMENT_32K 0x0F +#define FU_FIRMWARE_ALIGNMENT_64K 0x10 +#define FU_FIRMWARE_ALIGNMENT_128K 0x11 +#define FU_FIRMWARE_ALIGNMENT_256K 0x12 +#define FU_FIRMWARE_ALIGNMENT_512K 0x13 +#define FU_FIRMWARE_ALIGNMENT_1M 0x14 +#define FU_FIRMWARE_ALIGNMENT_2M 0x15 +#define FU_FIRMWARE_ALIGNMENT_4M 0x16 +#define FU_FIRMWARE_ALIGNMENT_8M 0x17 +#define FU_FIRMWARE_ALIGNMENT_16M 0x18 +#define FU_FIRMWARE_ALIGNMENT_32M 0x19 +#define FU_FIRMWARE_ALIGNMENT_64M 0x1A +#define FU_FIRMWARE_ALIGNMENT_128M 0x1B +#define FU_FIRMWARE_ALIGNMENT_256M 0x1C +#define FU_FIRMWARE_ALIGNMENT_512M 0x1D +#define FU_FIRMWARE_ALIGNMENT_1G 0x1E +#define FU_FIRMWARE_ALIGNMENT_2G 0x1F +#define FU_FIRMWARE_ALIGNMENT_4G 0x20 + +const gchar * +fu_firmware_flag_to_string(FuFirmwareFlags flag); +FuFirmwareFlags +fu_firmware_flag_from_string(const gchar *flag); + +FuFirmware * +fu_firmware_new(void); +FuFirmware * +fu_firmware_new_from_bytes(GBytes *fw); +FuFirmware * +fu_firmware_new_from_gtypes(GBytes *fw, FwupdInstallFlags flags, GError **error, ...); +gchar * +fu_firmware_to_string(FuFirmware *self); +void +fu_firmware_export(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn); +gchar * +fu_firmware_export_to_xml(FuFirmware *self, FuFirmwareExportFlags flags, GError **error); +const gchar * +fu_firmware_get_version(FuFirmware *self); +void +fu_firmware_set_version(FuFirmware *self, const gchar *version); +guint64 +fu_firmware_get_version_raw(FuFirmware *self); +void +fu_firmware_set_version_raw(FuFirmware *self, guint64 version_raw); +void +fu_firmware_add_flag(FuFirmware *firmware, FuFirmwareFlags flag); +gboolean +fu_firmware_has_flag(FuFirmware *firmware, FuFirmwareFlags flag); +const gchar * +fu_firmware_get_filename(FuFirmware *self); +void +fu_firmware_set_filename(FuFirmware *self, const gchar *filename); +const gchar * +fu_firmware_get_id(FuFirmware *self); +void +fu_firmware_set_id(FuFirmware *self, const gchar *id); +guint64 +fu_firmware_get_addr(FuFirmware *self); +void +fu_firmware_set_addr(FuFirmware *self, guint64 addr); +guint64 +fu_firmware_get_offset(FuFirmware *self); +void +fu_firmware_set_offset(FuFirmware *self, guint64 offset); +gsize +fu_firmware_get_size(FuFirmware *self); +void +fu_firmware_set_size(FuFirmware *self, gsize size); +guint64 +fu_firmware_get_idx(FuFirmware *self); +void +fu_firmware_set_idx(FuFirmware *self, guint64 idx); +GBytes * +fu_firmware_get_bytes(FuFirmware *self, GError **error); +GBytes * +fu_firmware_get_bytes_with_patches(FuFirmware *self, GError **error); +void +fu_firmware_set_bytes(FuFirmware *self, GBytes *bytes); +guint8 +fu_firmware_get_alignment(FuFirmware *self); +void +fu_firmware_set_alignment(FuFirmware *self, guint8 alignment); +void +fu_firmware_add_chunk(FuFirmware *self, FuChunk *chk); +GPtrArray * +fu_firmware_get_chunks(FuFirmware *self, GError **error); +FuFirmware * +fu_firmware_get_parent(FuFirmware *self); +void +fu_firmware_set_parent(FuFirmware *self, FuFirmware *parent); + +gboolean +fu_firmware_tokenize(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_firmware_build(FuFirmware *self, XbNode *n, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_firmware_build_from_xml(FuFirmware *self, + const gchar *xml, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_firmware_parse(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_firmware_parse_file(FuFirmware *self, GFile *file, FwupdInstallFlags flags, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_firmware_parse_full(FuFirmware *self, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_firmware_write(FuFirmware *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_firmware_write_chunk(FuFirmware *self, guint64 address, guint64 chunk_sz_max, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_firmware_write_file(FuFirmware *self, GFile *file, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gchar * +fu_firmware_get_checksum(FuFirmware *self, GChecksumType csum_kind, GError **error); +gboolean +fu_firmware_check_compatible(FuFirmware *self, + FuFirmware *other, + FwupdInstallFlags flags, + GError **error); + +void +fu_firmware_add_image(FuFirmware *self, FuFirmware *img); +gboolean +fu_firmware_remove_image(FuFirmware *self, FuFirmware *img, GError **error); +gboolean +fu_firmware_remove_image_by_idx(FuFirmware *self, guint64 idx, GError **error); +gboolean +fu_firmware_remove_image_by_id(FuFirmware *self, const gchar *id, GError **error); +GPtrArray * +fu_firmware_get_images(FuFirmware *self); +FuFirmware * +fu_firmware_get_image_by_id(FuFirmware *self, const gchar *id, GError **error); +GBytes * +fu_firmware_get_image_by_id_bytes(FuFirmware *self, const gchar *id, GError **error); +FuFirmware * +fu_firmware_get_image_by_idx(FuFirmware *self, guint64 idx, GError **error); +GBytes * +fu_firmware_get_image_by_idx_bytes(FuFirmware *self, guint64 idx, GError **error); +FuFirmware * +fu_firmware_get_image_by_checksum(FuFirmware *self, const gchar *checksum, GError **error); +void +fu_firmware_add_patch(FuFirmware *self, gsize offset, GBytes *blob); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fit-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-fit-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..eea3ea2956cd1ecc61002d810ad7ecb320903ac8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fit-firmware.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-crc.h" +#include "fu-dump.h" +#include "fu-fdt-image.h" +#include "fu-fit-firmware.h" +#include "fu-mem.h" + +/** + * FuFitFirmware: + * + * A Flat Image Tree. + * + * Documented: + * https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt + * + * See also: [class@FuFdtFirmware] + */ + +G_DEFINE_TYPE(FuFitFirmware, fu_fit_firmware, FU_TYPE_FDT_FIRMWARE) + +static FuFdtImage * +fu_fit_firmware_get_image_root(FuFitFirmware *self) +{ + FuFirmware *img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), NULL, NULL); + if (img != NULL) + return FU_FDT_IMAGE(img); + img = fu_fdt_image_new(); + fu_fdt_image_set_attr_uint32(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_TIMESTAMP, 0x0); + fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "description", "Firmware image"); + fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "creator", "fwupd"); + fu_firmware_add_image(FU_FIRMWARE(self), img); + return FU_FDT_IMAGE(img); +} + +/** + * fu_fit_firmware_get_timestamp: + * @self: a #FuFitFirmware + * + * Gets the creation timestamp. + * + * Returns: integer + * + * Since: 1.8.2 + **/ +guint32 +fu_fit_firmware_get_timestamp(FuFitFirmware *self) +{ + guint32 tmp = 0; + g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self); + + g_return_val_if_fail(FU_IS_FIT_FIRMWARE(self), 0x0); + + /* this has to exist */ + (void)fu_fdt_image_get_attr_u32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, &tmp, NULL); + return tmp; +} + +/** + * fu_fit_firmware_set_timestamp: + * @self: a #FuFitFirmware + * @timestamp: integer value + * + * Sets the creation timestamp. + * + * Since: 1.8.2 + **/ +void +fu_fit_firmware_set_timestamp(FuFitFirmware *self, guint32 timestamp) +{ + g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self); + g_return_if_fail(FU_IS_FIT_FIRMWARE(self)); + fu_fdt_image_set_attr_uint32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, timestamp); +} + +static gboolean +fu_fit_firmware_verify_crc32(FuFirmware *firmware, + FuFirmware *img, + FuFirmware *img_hash, + GBytes *blob, + GError **error) +{ + guint32 value = 0; + guint32 value_calc; + + /* get value and verify */ + if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img_hash), + FU_FIT_FIRMWARE_ATTR_VALUE, + &value, + error)) + return FALSE; + value_calc = fu_crc32(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); + if (value_calc != value) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%s CRC did not match, got 0x%x, expected 0x%x", + fu_firmware_get_id(img), + value, + value_calc); + return FALSE; + } + + /* success */ + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM); + return TRUE; +} + +static gboolean +fu_fit_firmware_verify_checksum(FuFirmware *firmware, + FuFirmware *img, + FuFirmware *img_hash, + GChecksumType checksum_type, + GBytes *blob, + GError **error) +{ + gsize digest_len = g_checksum_type_get_length(checksum_type); + g_autofree guint8 *buf = g_malloc0(digest_len); + g_autoptr(GBytes) value = NULL; + g_autoptr(GBytes) value_calc = NULL; + g_autoptr(GChecksum) checksum = g_checksum_new(checksum_type); + + /* get value and verify */ + value = fu_fdt_image_get_attr(FU_FDT_IMAGE(img_hash), FU_FIT_FIRMWARE_ATTR_VALUE, error); + if (value == NULL) + return FALSE; + if (g_bytes_get_size(value) != digest_len) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%s invalid hash value size, got 0x%x, expected 0x%x", + fu_firmware_get_id(img), + (guint)g_bytes_get_size(value), + (guint)digest_len); + return FALSE; + } + g_checksum_update(checksum, + (const guchar *)g_bytes_get_data(value, NULL), + g_bytes_get_size(value)); + g_checksum_get_digest(checksum, buf, &digest_len); + value_calc = g_bytes_new(buf, digest_len); + if (!fu_bytes_compare(value, value_calc, error)) + return FALSE; + + /* success */ + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM); + return TRUE; +} + +static gboolean +fu_fit_firmware_verify_hash(FuFirmware *firmware, + FuFirmware *img, + FuFirmware *img_hash, + GBytes *blob, + GError **error) +{ + g_autofree gchar *algo = NULL; + + /* what is this */ + if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img_hash), + FU_FIT_FIRMWARE_ATTR_ALGO, + &algo, + error)) { + g_prefix_error(error, "cannot get algo for %s: ", fu_firmware_get_id(img)); + return FALSE; + } + if (g_strcmp0(algo, "crc32") == 0) + return fu_fit_firmware_verify_crc32(firmware, img, img_hash, blob, error); + if (g_strcmp0(algo, "md5") == 0) { + return fu_fit_firmware_verify_checksum(firmware, + img, + img_hash, + G_CHECKSUM_MD5, + blob, + error); + } + if (g_strcmp0(algo, "sha1") == 0) { + return fu_fit_firmware_verify_checksum(firmware, + img, + img_hash, + G_CHECKSUM_SHA1, + blob, + error); + } + if (g_strcmp0(algo, "sha256") == 0) { + return fu_fit_firmware_verify_checksum(firmware, + img, + img_hash, + G_CHECKSUM_SHA256, + blob, + error); + } + + /* ignore any hashes we do not support: success */ + return TRUE; +} + +static gboolean +fu_fit_firmware_verify_image(FuFirmware *firmware, + GBytes *fw, + FuFirmware *img, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GBytes) blob = NULL; + + /* sanity check */ + if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img), "type", NULL, error)) + return FALSE; + if (!fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img), "description", NULL, error)) + return FALSE; + + /* if has data data */ + blob = fu_fdt_image_get_attr(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_DATA, NULL); + if (blob == NULL) { + guint32 data_size = 0x0; + guint32 data_offset = 0x0; + + /* extra data outside of FIT image */ + if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img), + FU_FIT_FIRMWARE_ATTR_DATA_OFFSET, + &data_offset, + error)) + return FALSE; + if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img), + FU_FIT_FIRMWARE_ATTR_DATA_SIZE, + &data_size, + error)) + return FALSE; + blob = fu_bytes_new_offset(fw, data_offset, data_size, error); + if (blob == NULL) + return FALSE; + } + if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "data", blob); + + /* verify any hashes we recoginise */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + g_autoptr(GPtrArray) img_hashes = fu_firmware_get_images(img); + for (guint i = 0; i < img_hashes->len; i++) { + FuFirmware *img_hash = g_ptr_array_index(img_hashes, i); + if (fu_firmware_get_id(img_hash) == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no ID for image hash"); + return FALSE; + } + if (g_str_has_prefix(fu_firmware_get_id(img_hash), "hash")) { + if (!fu_fit_firmware_verify_hash(firmware, + img, + img_hash, + blob, + error)) + return FALSE; + } + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fit_firmware_verify_configuration(FuFirmware *firmware, + FuFirmware *img, + FwupdInstallFlags flags, + GError **error) +{ + /* sanity check */ + if (!fu_fdt_image_get_attr_strlist(FU_FDT_IMAGE(img), + FU_FIT_FIRMWARE_ATTR_COMPATIBLE, + NULL, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_fit_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) img_cfgs = NULL; + g_autoptr(FuFirmware) img_images = NULL; + g_autoptr(FuFirmware) img_root = NULL; + g_autoptr(GPtrArray) img_images_array = NULL; + g_autoptr(GPtrArray) img_cfgs_array = NULL; + + /* FuFdtFirmware->parse */ + if (!FU_FIRMWARE_CLASS(fu_fit_firmware_parent_class) + ->parse(firmware, fw, offset, flags, error)) + return FALSE; + + /* sanity check */ + img_root = fu_firmware_get_image_by_id(firmware, NULL, error); + if (img_root == NULL) + return FALSE; + if (!fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img_root), + FU_FIT_FIRMWARE_ATTR_TIMESTAMP, + NULL, + error)) + return FALSE; + + /* check the checksums of each image */ + img_images = fu_firmware_get_image_by_id(img_root, FU_FIT_FIRMWARE_ID_IMAGES, error); + if (img_images == NULL) + return FALSE; + img_images_array = fu_firmware_get_images(img_images); + for (guint i = 0; i < img_images_array->len; i++) { + FuFirmware *img = g_ptr_array_index(img_images_array, i); + if (!fu_fit_firmware_verify_image(firmware, fw, img, flags, error)) + return FALSE; + } + + /* check the setup of each configuration */ + img_cfgs = fu_firmware_get_image_by_id(img_root, FU_FIT_FIRMWARE_ID_CONFIGURATIONS, error); + if (img_cfgs == NULL) + return FALSE; + img_cfgs_array = fu_firmware_get_images(img_cfgs); + for (guint i = 0; i < img_cfgs_array->len; i++) { + FuFirmware *img = g_ptr_array_index(img_cfgs_array, i); + if (!fu_fit_firmware_verify_configuration(firmware, img, flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_fit_firmware_init(FuFitFirmware *self) +{ +} + +static void +fu_fit_firmware_class_init(FuFitFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_fit_firmware_parse; +} + +/** + * fu_fit_firmware_new: + * + * Creates a new #FuFirmware of sub type FIT + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_fit_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_FIT_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fit-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-fit-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..92a34804cbdf65f950de68431f56f9a835f0b5c9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fit-firmware.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-fdt-firmware.h" + +#define FU_TYPE_FIT_FIRMWARE (fu_fit_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuFitFirmware, fu_fit_firmware, FU, FIT_FIRMWARE, FuFdtFirmware) + +struct _FuFitFirmwareClass { + FuFdtFirmwareClass parent_class; +}; + +FuFirmware * +fu_fit_firmware_new(void); +guint32 +fu_fit_firmware_get_timestamp(FuFitFirmware *self); +void +fu_fit_firmware_set_timestamp(FuFitFirmware *self, guint32 timestamp); + +/** + * FU_FIT_FIRMWARE_ATTR_COMPATIBLE: + * + * The compatible metadata for the FIT image, typically a string list, e.g. + *`pine64,rockpro64-v2.1:pine64,rockpro64`. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_COMPATIBLE "compatible" + +/** + * FU_FIT_FIRMWARE_ATTR_DATA: + * + * The raw data for the FIT image, typically a blob. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_DATA "data" + +/** + * FU_FIT_FIRMWARE_ATTR_ALGO: + * + * The checksum algorithm for the FIT image, typically a string, e.g. `crc32`. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_ALGO "algo" + +/** + * FU_FIT_FIRMWARE_ATTR_DATA_OFFSET: + * + * The external data offset after the FIT image, typically a uint32. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_DATA_OFFSET "data-offset" + +/** + * FU_FIT_FIRMWARE_ATTR_DATA_SIZE: + * + * The data size of the external image, typically a uint32. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_DATA_SIZE "data-size" + +/** + * FU_FIT_FIRMWARE_ATTR_STORE_OFFSET: + * + * The store offset for the FIT image, typically a uint32. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_STORE_OFFSET "store-offset" + +/** + * FU_FIT_FIRMWARE_ATTR_VALUE: + * + * The value of the checksum, which is typically a blob. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_VALUE "value" + +/** + * FU_FIT_FIRMWARE_ATTR_SKIP_OFFSET: + * + * The offset to skip when writing the FIT image, typically a uint32. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_SKIP_OFFSET "skip-offset" + +/** + * FU_FIT_FIRMWARE_ATTR_VERSION: + * + * The version of the FIT image, typically a string, e.g. `1.2.3`. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_VERSION "version" + +/** + * FU_FIT_FIRMWARE_ATTR_TIMESTAMP: + * + * The creation timestamp of FIT image, typically a uint32. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ATTR_TIMESTAMP "timestamp" + +/** + * FU_FIT_FIRMWARE_ID_IMAGES: + * + * The usual firmware ID string for the images. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ID_IMAGES "images" + +/** + * FU_FIT_FIRMWARE_ID_CONFIGURATIONS: + * + * The usual firmware ID string for the configurations. + * + * Since: 1.8.2 + **/ +#define FU_FIT_FIRMWARE_ID_CONFIGURATIONS "configurations" diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fmap-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-fmap-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..49d23ac5bfc6848c1b2339d29be016605dde87c8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fmap-firmware.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-fmap-firmware.h" +#include "fu-mem.h" + +/** + * FuFmapFirmware: + * + * A FMAP firmware image. + * + * See also: [class@FuFirmware] + */ + +#define FMAP_SIGNATURE "__FMAP__" +#define FMAP_AREANAME "FMAP" + +G_DEFINE_TYPE(FuFmapFirmware, fu_fmap_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_fmap_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint8 magic[8] = {0x0}; + + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, FMAP_SIGNATURE, sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic for file"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fmap_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_GET_CLASS(firmware); + gsize bufsz; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + FuFmap fmap; + + /* corrupt */ + if (g_bytes_get_size(fw) < sizeof(FuFmap)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware too small for fmap"); + return FALSE; + } + + /* load header */ + if (!fu_memcpy_safe((guint8 *)&fmap, + sizeof(fmap), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(fmap), + error)) + return FALSE; + fu_firmware_set_addr(firmware, GUINT64_FROM_LE(fmap.base)); + + if (GUINT32_FROM_LE(fmap.size) != bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file size incorrect, expected 0x%04x got 0x%04x", + (guint)fmap.size, + (guint)bufsz); + return FALSE; + } + if (GUINT16_FROM_LE(fmap.nareas) < 1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "number of areas too small, got %" G_GUINT16_FORMAT, + GUINT16_FROM_LE(fmap.nareas)); + return FALSE; + } + offset += sizeof(fmap); + + for (gsize i = 0; i < GUINT16_FROM_LE(fmap.nareas); i++) { + FuFmapArea area; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autofree gchar *area_name = NULL; + + /* load area */ + if (!fu_memcpy_safe((guint8 *)&area, + sizeof(area), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(area), + error)) + return FALSE; + + /* skip */ + if (area.size == 0) + continue; + + bytes = fu_bytes_new_offset(fw, + (gsize)GUINT32_FROM_LE(area.offset), + (gsize)GUINT32_FROM_LE(area.size), + error); + if (bytes == NULL) + return FALSE; + area_name = g_strndup((const gchar *)area.name, FU_FMAP_FIRMWARE_STRLEN); + img = fu_firmware_new_from_bytes(bytes); + fu_firmware_set_id(img, area_name); + fu_firmware_set_idx(img, i + 1); + fu_firmware_set_addr(img, GUINT32_FROM_LE(area.offset)); + fu_firmware_add_image(firmware, img); + + if (g_strcmp0(area_name, FMAP_AREANAME) == 0) { + g_autofree gchar *version = NULL; + version = g_strdup_printf("%d.%d", fmap.ver_major, fmap.ver_minor); + fu_firmware_set_version(img, version); + } + offset += sizeof(area); + } + + /* subclassed */ + if (klass_firmware->parse != NULL) { + if (!klass_firmware->parse(firmware, fw, offset, flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_fmap_firmware_write(FuFirmware *firmware, GError **error) +{ + gsize total_sz; + gsize offset; + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + FuFmap hdr = { + .signature = {FMAP_SIGNATURE}, + .ver_major = 0x1, + .ver_minor = 0x1, + .base = GUINT64_TO_LE(fu_firmware_get_addr(firmware)), + .size = 0x0, + .name = "", + .nareas = GUINT16_TO_LE(images->len), + }; + + /* pad to offset */ + if (fu_firmware_get_offset(firmware) > 0) + fu_byte_array_set_size(buf, fu_firmware_get_offset(firmware), 0x00); + + /* add header */ + total_sz = offset = sizeof(hdr) + (sizeof(FuFmapArea) * images->len); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, error); + if (fw == NULL) + return NULL; + total_sz += g_bytes_get_size(fw); + } + hdr.size = GUINT32_TO_LE(fu_firmware_get_offset(firmware) + total_sz); + g_byte_array_append(buf, (const guint8 *)&hdr, sizeof(hdr)); + + /* add each area */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + const gchar *id = fu_firmware_get_id(img); + g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, NULL); + FuFmapArea area = { + .offset = GUINT32_TO_LE(fu_firmware_get_offset(firmware) + offset), + .size = GUINT32_TO_LE(g_bytes_get_size(fw)), + .name = {""}, + .flags = 0x0, + }; + if (id != NULL) + strncpy((gchar *)area.name, id, sizeof(area.name) - 1); + g_byte_array_append(buf, (const guint8 *)&area, sizeof(area)); + offset += g_bytes_get_size(fw); + } + + /* add the images */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, NULL); + fu_byte_array_append_bytes(buf, fw); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_fmap_firmware_init(FuFmapFirmware *self) +{ +} + +static void +fu_fmap_firmware_class_init(FuFmapFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_fmap_firmware_check_magic; + klass_firmware->parse = fu_fmap_firmware_parse; + klass_firmware->write = fu_fmap_firmware_write; +} + +/** + * fu_fmap_firmware_new + * + * Creates a new #FuFirmware of sub type fmap + * + * Since: 1.5.0 + **/ +FuFirmware * +fu_fmap_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_FMAP_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fmap-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-fmap-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..4a2998751deadfce55d79ff1fab1464e7bdd0891 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fmap-firmware.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_FMAP_FIRMWARE_STRLEN 32 /* maximum length for strings, */ + /* including null-terminator */ + +#define FU_TYPE_FMAP_FIRMWARE (fu_fmap_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuFmapFirmware, fu_fmap_firmware, FU, FMAP_FIRMWARE, FuFirmware) + +struct _FuFmapFirmwareClass { + FuFirmwareClass parent_class; + gboolean (*parse)(FuFirmware *self, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error); +}; + +/** + * FuFmapArea: + * + * Specific area of volatile and static regions in firmware binary. + **/ +typedef struct __attribute__((packed)) { + guint32 offset; /* offset relative to base */ + guint32 size; /* size in bytes */ + guint8 name[FU_FMAP_FIRMWARE_STRLEN]; /* descriptive name */ + guint16 flags; /* flags for this area */ +} FuFmapArea; + +/** + * FuFmap: + * + * Mapping of volatile and static regions in firmware binary. + **/ +typedef struct __attribute__((packed)) { + guint8 signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */ + guint8 ver_major; /* major version */ + guint8 ver_minor; /* minor version */ + guint64 base; /* address of the firmware binary */ + guint32 size; /* size of firmware binary in bytes */ + guint8 name[FU_FMAP_FIRMWARE_STRLEN]; /* name of this firmware binary */ + guint16 nareas; /* number of areas described by + areas[] below */ + FuFmapArea areas[]; +} FuFmap; + +FuFirmware * +fu_fmap_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fuzzer-firmware.c.in b/fwupd-1.8.6/libfwupdplugin/fu-fuzzer-firmware.c.in new file mode 100644 index 0000000000000000000000000000000000000000..25569735ac01f17d86ec3adbb7df503ec3abc54f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fuzzer-firmware.c.in @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "@INCLUDE@" + +int +LLVMFuzzerTestOneInput(const guint8 *data, gsize size) +{ + g_autoptr(FuFirmware) firmware = FU_FIRMWARE(@FIRMWARENEW@()); + g_autoptr(GBytes) fw = g_bytes_new(data, size); + gboolean ret; + + (void)g_setenv("G_DEBUG", "fatal-criticals", TRUE); + (void)g_setenv("FWUPD_FUZZER_RUNNING", "1", TRUE); + ret = fu_firmware_parse(firmware, fw, FWUPD_INSTALL_FLAG_NONE, NULL); + if (!ret && fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM)) { + g_clear_object(&firmware); + firmware = FU_FIRMWARE(@FIRMWARENEW@()); + ret = fu_firmware_parse(firmware, + fw, + FWUPD_INSTALL_FLAG_NO_SEARCH | + FWUPD_INSTALL_FLAG_IGNORE_VID_PID | + FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM, + NULL); + } + if (ret) { + g_autoptr(GBytes) fw2 = fu_firmware_write(firmware, NULL); + } + return 0; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-fuzzer-main.c b/fwupd-1.8.6/libfwupdplugin/fu-fuzzer-main.c new file mode 100644 index 0000000000000000000000000000000000000000..49bc81fd7341adbc77edcf039443ae5f8b23c907 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-fuzzer-main.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include + +__attribute__((weak)) extern int +LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); +__attribute__((weak)) extern int +LLVMFuzzerInitialize(int *argc, char ***argv); + +int +main(int argc, char **argv) +{ + g_assert_nonnull(LLVMFuzzerTestOneInput); + if (LLVMFuzzerInitialize != NULL) + LLVMFuzzerInitialize(&argc, &argv); + for (int i = 1; i < argc; i++) { + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(GError) error = NULL; + g_printerr("Running: %s\n", argv[i]); + if (!g_file_get_contents(argv[i], &buf, &bufsz, &error)) { + g_printerr("Failed to load: %s\n", error->message); + continue; + } + LLVMFuzzerTestOneInput((const guint8 *)buf, bufsz); + g_printerr("Done\n"); + } +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-hid-device.c b/fwupd-1.8.6/libfwupdplugin/fu-hid-device.c new file mode 100644 index 0000000000000000000000000000000000000000..fd34a83c3939b8c20b84db65825184c6570f0ddb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-hid-device.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuHidDevice" + +#include "config.h" + +#include "fu-dump.h" +#include "fu-hid-device.h" +#include "fu-string.h" + +#define FU_HID_REPORT_GET 0x01 +#define FU_HID_REPORT_SET 0x09 + +#define FU_HID_REPORT_TYPE_INPUT 0x01 +#define FU_HID_REPORT_TYPE_OUTPUT 0x02 +#define FU_HID_REPORT_TYPE_FEATURE 0x03 + +#define FU_HID_DEVICE_RETRIES 10 + +/** + * FuHidDevice: + * + * A Human Interface Device (HID) device. + * + * See also: [class@FuDevice], [class@FuUsbDevice] + */ + +typedef struct { + guint8 interface; + guint8 ep_addr_in; /* only for _USE_INTERRUPT_TRANSFER */ + guint8 ep_addr_out; /* only for _USE_INTERRUPT_TRANSFER */ + gboolean interface_autodetect; + FuHidDeviceFlags flags; +} FuHidDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuHidDevice, fu_hid_device, FU_TYPE_USB_DEVICE) + +enum { PROP_0, PROP_INTERFACE, PROP_LAST }; + +#define GET_PRIVATE(o) (fu_hid_device_get_instance_private(o)) + +static void +fu_hid_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuHidDevice *self = FU_HID_DEVICE(device); + FuHidDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append_kb(str, idt, "InterfaceAutodetect", priv->interface_autodetect); + fu_string_append_kx(str, idt, "Interface", priv->interface); + if (priv->ep_addr_in != 0) + fu_string_append_kx(str, idt, "EpAddrIn", priv->ep_addr_in); + if (priv->ep_addr_out != 0) + fu_string_append_kx(str, idt, "EpAddrOut", priv->ep_addr_out); +} + +static void +fu_hid_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuHidDevice *device = FU_HID_DEVICE(object); + FuHidDevicePrivate *priv = GET_PRIVATE(device); + switch (prop_id) { + case PROP_INTERFACE: + g_value_set_uint(value, priv->interface); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_hid_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuHidDevice *device = FU_HID_DEVICE(object); + switch (prop_id) { + case PROP_INTERFACE: + fu_hid_device_set_interface(device, g_value_get_uint(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +#ifdef HAVE_GUSB +static gboolean +fu_hid_device_autodetect_eps(FuHidDevice *self, GUsbInterface *iface, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + FuHidDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) eps = g_usb_interface_get_endpoints(iface); + for (guint i = 0; i < eps->len; i++) { + GUsbEndpoint *ep = g_ptr_array_index(eps, i); + if (g_usb_endpoint_get_direction(ep) == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST && + priv->ep_addr_in == 0) { + priv->ep_addr_in = g_usb_endpoint_get_address(ep); + continue; + } + if (g_usb_endpoint_get_direction(ep) == G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE && + priv->ep_addr_out == 0) { + priv->ep_addr_out = g_usb_endpoint_get_address(ep); + continue; + } + } + if (priv->ep_addr_in == 0x0 || priv->ep_addr_out == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not autodetect EP addresses"); + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} +#endif + +static gboolean +fu_hid_device_open(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuHidDevice *self = FU_HID_DEVICE(device); + FuHidDevicePrivate *priv = GET_PRIVATE(self); + GUsbDeviceClaimInterfaceFlags flags = 0; + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + + /* FuUsbDevice->open */ + if (!FU_DEVICE_CLASS(fu_hid_device_parent_class)->open(device, error)) + return FALSE; + + /* auto-detect */ + if (priv->interface_autodetect) { + g_autoptr(GPtrArray) ifaces = NULL; + ifaces = g_usb_device_get_interfaces(usb_device, error); + if (ifaces == NULL) + return FALSE; + for (guint i = 0; i < ifaces->len; i++) { + GUsbInterface *iface = g_ptr_array_index(ifaces, i); + if (g_usb_interface_get_class(iface) == G_USB_DEVICE_CLASS_HID) { + priv->interface = g_usb_interface_get_number(iface); + priv->interface_autodetect = FALSE; + if (priv->flags & FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER) { + if (!fu_hid_device_autodetect_eps(self, iface, error)) + return FALSE; + } + break; + } + } + if (priv->interface_autodetect) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not autodetect HID interface"); + return FALSE; + } + } + + /* claim */ + if ((priv->flags & FU_HID_DEVICE_FLAG_NO_KERNEL_UNBIND) == 0) + flags |= G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER; + if (!g_usb_device_claim_interface(usb_device, priv->interface, flags, error)) { + g_prefix_error(error, "failed to claim HID interface: "); + return FALSE; + } +#endif + + /* success */ + return TRUE; +} + +static gboolean +fu_hid_device_close(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuHidDevice *self = FU_HID_DEVICE(device); + FuHidDevicePrivate *priv = GET_PRIVATE(self); + GUsbDeviceClaimInterfaceFlags flags = 0; + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GError) error_local = NULL; +#endif + +#ifdef HAVE_GUSB + /* release */ + if ((priv->flags & FU_HID_DEVICE_FLAG_NO_KERNEL_REBIND) == 0) + flags |= G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER; + if (!g_usb_device_release_interface(usb_device, priv->interface, flags, &error_local)) { + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE) || + g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_INTERNAL)) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to release HID interface: "); + return FALSE; + } +#endif + + /* FuUsbDevice->close */ + return FU_DEVICE_CLASS(fu_hid_device_parent_class)->close(device, error); +} + +/** + * fu_hid_device_set_interface: + * @self: a #FuHidDevice + * @interface_number: an interface number, e.g. `0x03` + * + * Sets the HID USB interface number. + * + * In most cases the HID interface is auto-detected, but this function can be + * used where there are multiple HID interfaces or where the device USB + * interface descriptor is invalid. + * + * Since: 1.4.0 + **/ +void +fu_hid_device_set_interface(FuHidDevice *self, guint8 interface_number) +{ + FuHidDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_HID_DEVICE(self)); + priv->interface = interface_number; + priv->interface_autodetect = FALSE; +} + +/** + * fu_hid_device_get_interface: + * @self: a #FuHidDevice + * + * Gets the HID USB interface number. + * + * Returns: integer + * + * Since: 1.4.0 + **/ +guint8 +fu_hid_device_get_interface(FuHidDevice *self) +{ + FuHidDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_HID_DEVICE(self), 0xff); + return priv->interface; +} + +/** + * fu_hid_device_add_flag: + * @self: a #FuHidDevice + * @flag: HID device flags, e.g. %FU_HID_DEVICE_FLAG_RETRY_FAILURE + * + * Adds a flag to be used for all set and get report messages. + * + * Since: 1.5.2 + **/ +void +fu_hid_device_add_flag(FuHidDevice *self, FuHidDeviceFlags flag) +{ + FuHidDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_HID_DEVICE(self)); + priv->flags |= flag; +} + +typedef struct { + guint8 value; + guint8 *buf; + gsize bufsz; + guint timeout; + FuHidDeviceFlags flags; +} FuHidDeviceRetryHelper; + +static gboolean +fu_hid_device_set_report_internal(FuHidDevice *self, FuHidDeviceRetryHelper *helper, GError **error) +{ +#ifdef HAVE_GUSB + FuHidDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_len = 0; + + /* what method do we use? */ + if (priv->flags & FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER) { + if (g_getenv("FU_HID_DEVICE_VERBOSE") != NULL) { + g_autofree gchar *title = NULL; + title = g_strdup_printf("HID::SetReport [EP=0x%02x]", priv->ep_addr_out); + fu_dump_raw(G_LOG_DOMAIN, title, helper->buf, helper->bufsz); + } + if (!g_usb_device_interrupt_transfer(usb_device, + priv->ep_addr_out, + helper->buf, + helper->bufsz, + &actual_len, + helper->timeout, + NULL, /* cancellable */ + error)) { + return FALSE; + } + } else { + guint16 wvalue = (FU_HID_REPORT_TYPE_OUTPUT << 8) | helper->value; + + /* special case */ + if (helper->flags & FU_HID_DEVICE_FLAG_IS_FEATURE) + wvalue = (FU_HID_REPORT_TYPE_FEATURE << 8) | helper->value; + + if (g_getenv("FU_HID_DEVICE_VERBOSE") != NULL) { + g_autofree gchar *title = NULL; + title = g_strdup_printf("HID::SetReport [wValue=0x%04x ,wIndex=%u]", + wvalue, + priv->interface); + fu_dump_raw(G_LOG_DOMAIN, title, helper->buf, helper->bufsz); + } + if (!g_usb_device_control_transfer(usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + FU_HID_REPORT_SET, + wvalue, + priv->interface, + helper->buf, + helper->bufsz, + &actual_len, + helper->timeout, + NULL, + error)) { + g_prefix_error(error, "failed to SetReport: "); + return FALSE; + } + } + if ((helper->flags & FU_HID_DEVICE_FLAG_ALLOW_TRUNC) == 0 && actual_len != helper->bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wrote %" G_GSIZE_FORMAT ", requested %" G_GSIZE_FORMAT " bytes", + actual_len, + helper->bufsz); + return FALSE; + } +#endif + return TRUE; +} + +static gboolean +fu_hid_device_set_report_internal_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuHidDevice *self = FU_HID_DEVICE(device); + FuHidDeviceRetryHelper *helper = (FuHidDeviceRetryHelper *)user_data; + return fu_hid_device_set_report_internal(self, helper, error); +} + +/** + * fu_hid_device_set_report: + * @self: a #FuHidDevice + * @value: low byte of wValue, but unused when using %FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER + * @buf: (nullable): a mutable buffer of data to send + * @bufsz: size of @buf + * @timeout: timeout in ms + * @flags: HID device flags e.g. %FU_HID_DEVICE_FLAG_ALLOW_TRUNC + * @error: (nullable): optional return location for an error + * + * Calls SetReport on the hardware. + * + * Returns: %TRUE for success + * + * Since: 1.4.0 + **/ +gboolean +fu_hid_device_set_report(FuHidDevice *self, + guint8 value, + guint8 *buf, + gsize bufsz, + guint timeout, + FuHidDeviceFlags flags, + GError **error) +{ + FuHidDeviceRetryHelper helper; + FuHidDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_HID_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(bufsz != 0, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* create helper */ + helper.value = value; + helper.buf = buf; + helper.bufsz = bufsz; + helper.timeout = timeout; + helper.flags = priv->flags | flags; + + /* special case */ + if (flags & FU_HID_DEVICE_FLAG_RETRY_FAILURE) { + return fu_device_retry(FU_DEVICE(self), + fu_hid_device_set_report_internal_cb, + FU_HID_DEVICE_RETRIES, + &helper, + error); + } + + /* just one */ + return fu_hid_device_set_report_internal(self, &helper, error); +} + +static gboolean +fu_hid_device_get_report_internal(FuHidDevice *self, FuHidDeviceRetryHelper *helper, GError **error) +{ +#ifdef HAVE_GUSB + FuHidDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_len = 0; + + /* what method do we use? */ + if (priv->flags & FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER) { + if (!g_usb_device_interrupt_transfer(usb_device, + priv->ep_addr_in, + helper->buf, + helper->bufsz, + &actual_len, + helper->timeout, + NULL, /* cancellable */ + error)) { + return FALSE; + } + if (g_getenv("FU_HID_DEVICE_VERBOSE") != NULL) { + g_autofree gchar *title = NULL; + title = g_strdup_printf("HID::GetReport [EP=0x%02x]", priv->ep_addr_in); + fu_dump_raw(G_LOG_DOMAIN, title, helper->buf, helper->bufsz); + } + } else { + guint16 wvalue = (FU_HID_REPORT_TYPE_INPUT << 8) | helper->value; + + /* special case */ + if (helper->flags & FU_HID_DEVICE_FLAG_IS_FEATURE) + wvalue = (FU_HID_REPORT_TYPE_FEATURE << 8) | helper->value; + + if (g_getenv("FU_HID_DEVICE_VERBOSE") != NULL) { + g_autofree gchar *title = NULL; + title = g_strdup_printf("HID::GetReport [wValue=0x%04x, wIndex=%u]", + wvalue, + priv->interface); + fu_dump_raw(G_LOG_DOMAIN, title, helper->buf, actual_len); + } + 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, + FU_HID_REPORT_GET, + wvalue, + priv->interface, + helper->buf, + helper->bufsz, + &actual_len, /* actual length */ + helper->timeout, + NULL, + error)) { + g_prefix_error(error, "failed to GetReport: "); + return FALSE; + } + if (g_getenv("FU_HID_DEVICE_VERBOSE") != NULL) { + g_autofree gchar *title = NULL; + title = g_strdup_printf("HID::GetReport [wValue=0x%04x, wIndex=%u]", + wvalue, + priv->interface); + fu_dump_raw(G_LOG_DOMAIN, title, helper->buf, actual_len); + } + } + if ((helper->flags & FU_HID_DEVICE_FLAG_ALLOW_TRUNC) == 0 && actual_len != helper->bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "read %" G_GSIZE_FORMAT ", requested %" G_GSIZE_FORMAT " bytes", + actual_len, + helper->bufsz); + return FALSE; + } +#endif + return TRUE; +} + +static gboolean +fu_hid_device_get_report_internal_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuHidDevice *self = FU_HID_DEVICE(device); + FuHidDeviceRetryHelper *helper = (FuHidDeviceRetryHelper *)user_data; + return fu_hid_device_get_report_internal(self, helper, error); +} + +/** + * fu_hid_device_get_report: + * @self: a #FuHidDevice + * @value: low byte of wValue, but unused when using %FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER + * @buf: (nullable): a mutable buffer of data to send + * @bufsz: size of @buf + * @timeout: timeout in ms + * @flags: HID device flags e.g. %FU_HID_DEVICE_FLAG_ALLOW_TRUNC + * @error: (nullable): optional return location for an error + * + * Calls GetReport on the hardware. + * + * Returns: %TRUE for success + * + * Since: 1.4.0 + **/ +gboolean +fu_hid_device_get_report(FuHidDevice *self, + guint8 value, + guint8 *buf, + gsize bufsz, + guint timeout, + FuHidDeviceFlags flags, + GError **error) +{ + FuHidDeviceRetryHelper helper; + FuHidDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_HID_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(bufsz != 0, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* create helper */ + helper.value = value; + helper.buf = buf; + helper.bufsz = bufsz; + helper.timeout = timeout; + helper.flags = priv->flags | flags; + + /* special case */ + if (flags & FU_HID_DEVICE_FLAG_RETRY_FAILURE) { + return fu_device_retry(FU_DEVICE(self), + fu_hid_device_get_report_internal_cb, + FU_HID_DEVICE_RETRIES, + &helper, + error); + } + + /* just one */ + return fu_hid_device_get_report_internal(self, &helper, error); +} + +static void +fu_hid_device_init(FuHidDevice *self) +{ + FuHidDevicePrivate *priv = GET_PRIVATE(self); + priv->interface_autodetect = TRUE; +} + +/** + * fu_hid_device_new: + * @usb_device: a USB device + * + * Creates a new HID device. + * + * Returns: (transfer full): a #FuHidDevice + * + * Since: 1.4.0 + **/ +FuHidDevice * +fu_hid_device_new(GUsbDevice *usb_device) +{ + FuHidDevice *device = g_object_new(FU_TYPE_HID_DEVICE, NULL); + fu_usb_device_set_dev(FU_USB_DEVICE(device), usb_device); + return FU_HID_DEVICE(device); +} + +static void +fu_hid_device_class_init(FuHidDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->get_property = fu_hid_device_get_property; + object_class->set_property = fu_hid_device_set_property; + klass_device->open = fu_hid_device_open; + klass_device->close = fu_hid_device_close; + klass_device->to_string = fu_hid_device_to_string; + + /** + * FuHidDevice:interface: + * + * The HID interface to use. + * + * Since: 1.4.0 + */ + pspec = g_param_spec_uint("interface", + NULL, + NULL, + 0x00, + 0xff, + 0x00, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_INTERFACE, pspec); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-hid-device.h b/fwupd-1.8.6/libfwupdplugin/fu-hid-device.h new file mode 100644 index 0000000000000000000000000000000000000000..324b76db337f8dc6dadaed1508f6d12785ff168c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-hid-device.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-usb-device.h" + +#define FU_TYPE_HID_DEVICE (fu_hid_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuHidDevice, fu_hid_device, FU, HID_DEVICE, FuUsbDevice) + +struct _FuHidDeviceClass { + FuUsbDeviceClass parent_class; + gpointer __reserved[31]; +}; + +/** + * FuHidDeviceFlags: + * @FU_HID_DEVICE_FLAG_NONE: No flags set + * @FU_HID_DEVICE_FLAG_ALLOW_TRUNC: Allow truncated reads and writes + * @FU_HID_DEVICE_FLAG_IS_FEATURE: Use %FU_HID_REPORT_TYPE_FEATURE for wValue + * @FU_HID_DEVICE_FLAG_RETRY_FAILURE: Retry up to 10 times on failure + * @FU_HID_DEVICE_FLAG_NO_KERNEL_UNBIND: Do not unbind the kernel driver on open + * @FU_HID_DEVICE_FLAG_NO_KERNEL_REBIND: Do not rebind the kernel driver on close + * @FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER: Use interrupt transfers, not control transfers + * + * Flags used when calling fu_hid_device_get_report() and fu_hid_device_set_report(). + **/ +typedef enum { + FU_HID_DEVICE_FLAG_NONE = 0, + FU_HID_DEVICE_FLAG_ALLOW_TRUNC = 1 << 0, + FU_HID_DEVICE_FLAG_IS_FEATURE = 1 << 1, + FU_HID_DEVICE_FLAG_RETRY_FAILURE = 1 << 2, + FU_HID_DEVICE_FLAG_NO_KERNEL_UNBIND = 1 << 3, + FU_HID_DEVICE_FLAG_NO_KERNEL_REBIND = 1 << 4, + FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER = 1 << 5, + /*< private >*/ + FU_HID_DEVICE_FLAG_LAST +} FuHidDeviceFlags; + +FuHidDevice * +fu_hid_device_new(GUsbDevice *usb_device); +void +fu_hid_device_add_flag(FuHidDevice *self, FuHidDeviceFlags flag); +void +fu_hid_device_set_interface(FuHidDevice *self, guint8 interface_number); +guint8 +fu_hid_device_get_interface(FuHidDevice *self); +gboolean +fu_hid_device_set_report(FuHidDevice *self, + guint8 value, + guint8 *buf, + gsize bufsz, + guint timeout, + FuHidDeviceFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_hid_device_get_report(FuHidDevice *self, + guint8 value, + guint8 *buf, + gsize bufsz, + guint timeout, + FuHidDeviceFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-hwids.c b/fwupd-1.8.6/libfwupdplugin/fu-hwids.c new file mode 100644 index 0000000000000000000000000000000000000000..94dcfd97a06c0efaac02a347f9826db265b05040 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-hwids.c @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuHwids" + +#include "config.h" + +#include +#include + +#include "fwupd-common.h" +#include "fwupd-error.h" + +#include "fu-common.h" +#include "fu-hwids.h" +#include "fu-path.h" +#include "fu-string.h" + +/** + * FuHwids: + * + * A the hardware IDs on the system. + * + * Note, these are called "CHIDs" in Microsoft Windows and the results here + * will match that of `ComputerHardwareIds.exe`. + * + * See also: [class@FuSmbios] + */ + +struct _FuHwids { + GObject parent_instance; + GHashTable *hash_dmi_hw; /* BiosVersion->"1.2.3 " */ + GHashTable *hash_dmi_display; /* BiosVersion->"1.2.3" */ + GHashTable *hash_smbios_override; /* 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) + +/** + * fu_hwids_get_value: + * @self: a #FuHwids + * @key: a DMI ID, e.g. `BiosVersion` + * + * Gets the cached value for one specific key that is valid ASCII and suitable + * for display. + * + * Returns: the string, e.g. `1.2.3`, or %NULL if not found + * + * Since: 0.9.3 + **/ +const gchar * +fu_hwids_get_value(FuHwids *self, const gchar *key) +{ + return g_hash_table_lookup(self->hash_dmi_display, key); +} + +/** + * fu_hwids_has_guid: + * @self: a #FuHwids + * @guid: a GUID, e.g. `059eb22d-6dc7-59af-abd3-94bbe017f67c` + * + * Finds out if a hardware GUID exists. + * + * Returns: %TRUE if the GUID exists + * + * Since: 0.9.3 + **/ +gboolean +fu_hwids_has_guid(FuHwids *self, const gchar *guid) +{ + 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 utf8): an array of GUIDs + * + * Since: 0.9.3 + **/ +GPtrArray * +fu_hwids_get_guids(FuHwids *self) +{ + return self->array_guids; +} + +/** + * fu_hwids_get_keys: + * @self: a #FuHwids + * + * Returns all the defined HWID keys. + * + * Returns: (transfer container) (element-type utf8): All the known keys, + * e.g. %FU_HWIDS_KEY_FAMILY + * + * Since: 1.5.6 + **/ +GPtrArray * +fu_hwids_get_keys(FuHwids *self) +{ + GPtrArray *array = g_ptr_array_new(); + const gchar *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_FIRMWARE_MAJOR_RELEASE, + FU_HWIDS_KEY_FIRMWARE_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}; + for (guint i = 0; keys[i] != NULL; i++) + g_ptr_array_add(array, (gpointer)keys[i]); + return array; +} + +static gchar * +fu_hwids_get_guid_for_str(const gchar *str, GError **error) +{ + glong items_written = 0; + g_autofree gunichar2 *data = NULL; + + /* convert to UTF-16 and convert to GUID using custom namespace */ + data = g_utf8_to_utf16(str, -1, NULL, &items_written, error); + if (data == NULL) + return NULL; + + if (items_written == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GUIDs in data"); + return NULL; + } + + /* ensure the data is in little endian format */ + for (glong i = 0; i < items_written; i++) + data[i] = GUINT16_TO_LE(data[i]); + + /* convert to a GUID */ + return fwupd_guid_hash_data((guint8 *)data, + items_written * 2, + FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT); +} + +/** + * fu_hwids_get_replace_keys: + * @self: a #FuHwids + * @key: a HardwareID key, e.g. `HardwareID-3` + * + * Gets the replacement key for a well known value. + * + * Returns: the replacement value, e.g. `Manufacturer&ProductName`, or %NULL for error. + * + * Since: 0.9.3 + **/ +const gchar * +fu_hwids_get_replace_keys(FuHwids *self, const gchar *key) +{ + struct { + const gchar *search; + const gchar *replace; + } msdefined[] = { + {"HardwareID-0", + FU_HWIDS_KEY_MANUFACTURER + "&" FU_HWIDS_KEY_FAMILY "&" FU_HWIDS_KEY_PRODUCT_NAME "&" FU_HWIDS_KEY_PRODUCT_SKU + "&" FU_HWIDS_KEY_BIOS_VENDOR "&" FU_HWIDS_KEY_BIOS_VERSION + "&" FU_HWIDS_KEY_BIOS_MAJOR_RELEASE "&" FU_HWIDS_KEY_BIOS_MINOR_RELEASE}, + {"HardwareID-1", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_FAMILY "&" FU_HWIDS_KEY_PRODUCT_NAME + "&" FU_HWIDS_KEY_BIOS_VENDOR "&" FU_HWIDS_KEY_BIOS_VERSION + "&" FU_HWIDS_KEY_BIOS_MAJOR_RELEASE + "&" FU_HWIDS_KEY_BIOS_MINOR_RELEASE}, + {"HardwareID-2", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_PRODUCT_NAME "&" FU_HWIDS_KEY_BIOS_VENDOR + "&" FU_HWIDS_KEY_BIOS_VERSION + "&" FU_HWIDS_KEY_BIOS_MAJOR_RELEASE + "&" FU_HWIDS_KEY_BIOS_MINOR_RELEASE}, + {"HardwareID-3", + FU_HWIDS_KEY_MANUFACTURER + "&" FU_HWIDS_KEY_FAMILY "&" FU_HWIDS_KEY_PRODUCT_NAME "&" FU_HWIDS_KEY_PRODUCT_SKU + "&" FU_HWIDS_KEY_BASEBOARD_MANUFACTURER "&" FU_HWIDS_KEY_BASEBOARD_PRODUCT}, + {"HardwareID-4", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_FAMILY "&" FU_HWIDS_KEY_PRODUCT_NAME + "&" FU_HWIDS_KEY_PRODUCT_SKU}, + {"HardwareID-5", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_FAMILY "&" FU_HWIDS_KEY_PRODUCT_NAME}, + {"HardwareID-6", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_PRODUCT_SKU + "&" FU_HWIDS_KEY_BASEBOARD_MANUFACTURER + "&" FU_HWIDS_KEY_BASEBOARD_PRODUCT}, + {"HardwareID-7", FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_PRODUCT_SKU}, + {"HardwareID-8", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_PRODUCT_NAME + "&" FU_HWIDS_KEY_BASEBOARD_MANUFACTURER + "&" FU_HWIDS_KEY_BASEBOARD_PRODUCT}, + {"HardwareID-9", FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_PRODUCT_NAME}, + {"HardwareID-10", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_FAMILY + "&" FU_HWIDS_KEY_BASEBOARD_MANUFACTURER + "&" FU_HWIDS_KEY_BASEBOARD_PRODUCT}, + {"HardwareID-11", FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_FAMILY}, + {"HardwareID-12", FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_ENCLOSURE_KIND}, + {"HardwareID-13", + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_BASEBOARD_MANUFACTURER + "&" FU_HWIDS_KEY_BASEBOARD_PRODUCT}, + {"HardwareID-14", FU_HWIDS_KEY_MANUFACTURER}, + {NULL, NULL}}; + + /* defined for Windows 10 */ + for (guint i = 0; msdefined[i].search != NULL; i++) { + if (g_strcmp0(msdefined[i].search, key) == 0) { + key = msdefined[i].replace; + break; + } + } + + return key; +} + +/** + * fu_hwids_add_smbios_override: + * @self: a #FuHwids + * @key: a key, e.g. %FU_HWIDS_KEY_PRODUCT_SKU + * @value: (nullable): a new value, e.g. `ExampleModel` + * + * Sets SMBIOS override values so you can emulate another system. + * + * This function has no effect if called after fu_hwids_setup() + * + * Since: 1.5.6 + **/ +void +fu_hwids_add_smbios_override(FuHwids *self, const gchar *key, const gchar *value) +{ + g_return_if_fail(FU_IS_HWIDS(self)); + g_return_if_fail(key != NULL); + g_hash_table_insert(self->hash_smbios_override, g_strdup(key), g_strdup(value)); +} + +/** + * fu_hwids_get_replace_values: + * @self: a #FuHwids + * @keys: a key, e.g. `HardwareID-3` or %FU_HWIDS_KEY_PRODUCT_SKU + * @error: (nullable): optional return location for an error + * + * Gets the replacement values for a HardwareID key or plain key. + * + * Returns: a string, e.g. `LENOVO&ThinkPad T440s`, or %NULL for error. + * + * Since: 0.9.3 + **/ +gchar * +fu_hwids_get_replace_values(FuHwids *self, const gchar *keys, GError **error) +{ + g_auto(GStrv) split = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + g_return_val_if_fail(FU_IS_HWIDS(self), NULL); + g_return_val_if_fail(keys != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* do any replacements */ + keys = fu_hwids_get_replace_keys(self, keys); + + /* get each part of the HWID */ + split = g_strsplit(keys, "&", -1); + for (guint j = 0; split[j] != NULL; j++) { + const gchar *tmp = g_hash_table_lookup(self->hash_dmi_hw, split[j]); + if (tmp == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "not available as '%s' unknown", + split[j]); + return NULL; + } + g_string_append_printf(str, "%s&", tmp); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + return g_strdup(str->str); +} + +/** + * fu_hwids_get_guid: + * @self: a #FuHwids + * @keys: a key, e.g. `HardwareID-3` or %FU_HWIDS_KEY_PRODUCT_SKU + * @error: (nullable): optional return location for an error + * + * Gets the GUID for a specific key. + * + * Returns: a string, or %NULL for error. + * + * Since: 0.9.3 + **/ +gchar * +fu_hwids_get_guid(FuHwids *self, const gchar *keys, GError **error) +{ + g_autofree gchar *tmp = NULL; + + g_return_val_if_fail(FU_IS_HWIDS(self), NULL); + g_return_val_if_fail(keys != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + tmp = fu_hwids_get_replace_values(self, keys, error); + if (tmp == NULL) + return NULL; + return fu_hwids_get_guid_for_str(tmp, error); +} + +typedef gchar *(*FuHwidsConvertFunc)(FuSmbios *smbios, guint8 type, guint8 offset, GError **error); + +static gchar * +fu_hwids_convert_string_table_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) +{ + const gchar *tmp; + tmp = fu_smbios_get_string(smbios, type, offset, error); + if (tmp == NULL) + return NULL; + /* ComputerHardwareIds.exe seems to strip spaces */ + return fu_strstrip(tmp); +} + +static gchar * +fu_hwids_convert_padded_integer_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) +{ + guint tmp = fu_smbios_get_integer(smbios, type, offset, error); + if (tmp == G_MAXUINT) + return NULL; + return g_strdup_printf("%02x", tmp); +} + +static gchar * +fu_hwids_convert_integer_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) +{ + guint tmp = fu_smbios_get_integer(smbios, type, offset, error); + if (tmp == G_MAXUINT) + return NULL; + return g_strdup_printf("%x", tmp); +} + +static gboolean +fu_hwids_setup_overrides(FuHwids *self, GError **error) +{ + g_autofree gchar *localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + g_autofree gchar *sysconfigdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + g_autoptr(GKeyFile) kf = g_key_file_new(); + g_autoptr(GPtrArray) fns = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GPtrArray) keys = fu_hwids_get_keys(self); + + /* per-system configuration and optional overrides */ + g_ptr_array_add(fns, g_build_filename(sysconfigdir, "daemon.conf", NULL)); + g_ptr_array_add(fns, g_build_filename(localstatedir, "daemon.conf", NULL)); + for (guint i = 0; i < fns->len; i++) { + const gchar *fn = g_ptr_array_index(fns, i); + if (g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_debug("loading HwId overrides from %s", fn); + if (!g_key_file_load_from_file(kf, fn, G_KEY_FILE_NONE, error)) + return FALSE; + } else { + g_debug("not loading HwId overrides from %s", fn); + } + } + + /* all keys are optional */ + for (guint i = 0; i < keys->len; i++) { + const gchar *key = g_ptr_array_index(keys, i); + g_autofree gchar *value = g_key_file_get_string(kf, "fwupd", key, NULL); + if (value != NULL) + fu_hwids_add_smbios_override(self, key, value); + } + + /* success */ + return TRUE; +} + +/** + * fu_hwids_setup: + * @self: a #FuHwids + * @smbios: (nullable): a #FuSmbios + * @error: (nullable): optional return location for an error + * + * Reads all the SMBIOS values from the hardware. + * + * Returns: %TRUE for success + * + * Since: 0.9.3 + **/ +gboolean +fu_hwids_setup(FuHwids *self, FuSmbios *smbios, GError **error) +{ + struct { + const gchar *key; + guint8 type; + guint8 offset; + FuHwidsConvertFunc func; + } map[] = {{FU_HWIDS_KEY_MANUFACTURER, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x04, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_ENCLOSURE_KIND, + FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, + 0x05, + fu_hwids_convert_integer_cb}, + {FU_HWIDS_KEY_FAMILY, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x1a, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_PRODUCT_NAME, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x05, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_PRODUCT_SKU, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x19, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_BIOS_VENDOR, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x04, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_BIOS_VERSION, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x05, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_BIOS_MAJOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x14, + fu_hwids_convert_padded_integer_cb}, + {FU_HWIDS_KEY_BIOS_MINOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x15, + fu_hwids_convert_padded_integer_cb}, + {FU_HWIDS_KEY_FIRMWARE_MAJOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x16, + fu_hwids_convert_padded_integer_cb}, + {FU_HWIDS_KEY_FIRMWARE_MINOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x17, + fu_hwids_convert_padded_integer_cb}, + {FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x04, + fu_hwids_convert_string_table_cb}, + {FU_HWIDS_KEY_BASEBOARD_PRODUCT, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x05, + fu_hwids_convert_string_table_cb}, + {NULL, 0x00, 0x00, NULL}}; + + g_return_val_if_fail(FU_IS_HWIDS(self), FALSE); + g_return_val_if_fail(FU_IS_SMBIOS(smbios) || smbios == NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* override using a config file */ + if (!fu_hwids_setup_overrides(self, error)) + return FALSE; + + /* get all DMI data */ + for (guint i = 0; map[i].key != NULL; i++) { + const gchar *contents_hdr = NULL; + g_autofree gchar *contents = NULL; + g_autofree gchar *contents_safe = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the data from a SMBIOS table unless an override exists */ + if (g_hash_table_lookup_extended(self->hash_smbios_override, + map[i].key, + NULL, + (gpointer *)&contents_hdr)) { + if (contents_hdr == NULL) { + g_debug("ignoring %s", map[i].key); + continue; + } + } else if (smbios != NULL) { + contents = map[i].func(smbios, map[i].type, map[i].offset, &error_local); + if (contents == NULL) { + g_debug("ignoring %s: %s", map[i].key, error_local->message); + continue; + } + contents_hdr = contents; + } else { + g_debug("ignoring %s", map[i].key); + continue; + } + g_debug("smbios property %s=%s", map[i].key, contents_hdr); + + /* weirdly, remove leading zeros */ + while (contents_hdr[0] == '0' && map[i].func != fu_hwids_convert_padded_integer_cb) + contents_hdr++; + g_hash_table_insert(self->hash_dmi_hw, + g_strdup(map[i].key), + g_strdup(contents_hdr)); + + /* make suitable for display */ + contents_safe = g_str_to_ascii(contents_hdr, "C"); + g_strdelimit(contents_safe, "\n\r", '\0'); + g_strchomp(contents_safe); + g_hash_table_insert(self->hash_dmi_display, + g_strdup(map[i].key), + g_steal_pointer(&contents_safe)); + } + + /* add GUIDs */ + for (guint i = 0; i < 15; i++) { + g_autofree gchar *guid = NULL; + g_autofree gchar *key = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the GUID and add to hash */ + key = g_strdup_printf("HardwareID-%u", i); + guid = fu_hwids_get_guid(self, key, &error_local); + if (guid == NULL) { + g_debug("%s is not available, %s", key, error_local->message); + continue; + } + g_hash_table_insert(self->hash_guid, g_strdup(guid), GUINT_TO_POINTER(1)); + g_ptr_array_add(self->array_guids, g_steal_pointer(&guid)); + } + + return TRUE; +} + +static void +fu_hwids_finalize(GObject *object) +{ + FuHwids *self; + g_return_if_fail(FU_IS_HWIDS(object)); + self = FU_HWIDS(object); + + g_hash_table_unref(self->hash_dmi_hw); + g_hash_table_unref(self->hash_dmi_display); + g_hash_table_unref(self->hash_smbios_override); + g_hash_table_unref(self->hash_guid); + g_ptr_array_unref(self->array_guids); + + G_OBJECT_CLASS(fu_hwids_parent_class)->finalize(object); +} + +static void +fu_hwids_class_init(FuHwidsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_hwids_finalize; +} + +static void +fu_hwids_init(FuHwids *self) +{ + 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_smbios_override = 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); +} + +/** + * fu_hwids_new: + * + * Creates a new #FuHwids + * + * Since: 0.9.3 + **/ +FuHwids * +fu_hwids_new(void) +{ + FuHwids *self; + self = g_object_new(FU_TYPE_HWIDS, NULL); + return FU_HWIDS(self); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-hwids.h b/fwupd-1.8.6/libfwupdplugin/fu-hwids.h new file mode 100644 index 0000000000000000000000000000000000000000..3df9ca79866e18d8b448fff04b01083eace52db1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-hwids.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-smbios.h" + +#define FU_TYPE_HWIDS (fu_hwids_get_type()) + +G_DECLARE_FINAL_TYPE(FuHwids, fu_hwids, FU, HWIDS, GObject) + +/** + * FU_HWIDS_KEY_BASEBOARD_MANUFACTURER: + * + * The HwID key for the baseboard (motherboard) vendor. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_BASEBOARD_MANUFACTURER "BaseboardManufacturer" +/** + * FU_HWIDS_KEY_BASEBOARD_PRODUCT: + * + * The HwID key for baseboard (motherboard) product. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_BASEBOARD_PRODUCT "BaseboardProduct" +/** + * FU_HWIDS_KEY_BIOS_MAJOR_RELEASE: + * + * The HwID key for the BIOS major version. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_BIOS_MAJOR_RELEASE "BiosMajorRelease" +/** + * FU_HWIDS_KEY_BIOS_MINOR_RELEASE: + * + * The HwID key for the BIOS minor version. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_BIOS_MINOR_RELEASE "BiosMinorRelease" +/** + * FU_HWIDS_KEY_BIOS_VENDOR: + * + * The HwID key for the BIOS vendor. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_BIOS_VENDOR "BiosVendor" +/** + * FU_HWIDS_KEY_BIOS_VERSION: + * + * The HwID key for the BIOS version. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_BIOS_VERSION "BiosVersion" +/** + * FU_HWIDS_KEY_FIRMWARE_MAJOR_RELEASE: + * + * The HwID key for the firmware major version. + * + * Since: 1.6.1 + **/ +#define FU_HWIDS_KEY_FIRMWARE_MAJOR_RELEASE "FirmwareMajorRelease" +/** + * FU_HWIDS_KEY_FIRMWARE_MINOR_RELEASE: + * + * The HwID key for the firmware minor version. + * + * Since: 1.6.1 + **/ +#define FU_HWIDS_KEY_FIRMWARE_MINOR_RELEASE "FirmwareMinorRelease" +/** + * FU_HWIDS_KEY_ENCLOSURE_KIND: + * + * The HwID key for the enclosure kind. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_ENCLOSURE_KIND "EnclosureKind" +/** + * FU_HWIDS_KEY_FAMILY: + * + * The HwID key for the deice family. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_FAMILY "Family" +/** + * FU_HWIDS_KEY_MANUFACTURER: + * + * The HwID key for the top-level product vendor. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_MANUFACTURER "Manufacturer" +/** + * FU_HWIDS_KEY_PRODUCT_NAME: + * + * The HwID key for the top-level product product name. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_PRODUCT_NAME "ProductName" +/** + * FU_HWIDS_KEY_PRODUCT_SKU: + * + * The HwID key for the top-level product SKU. + * + * Since: 1.3.7 + **/ +#define FU_HWIDS_KEY_PRODUCT_SKU "ProductSku" + +FuHwids * +fu_hwids_new(void); +GPtrArray * +fu_hwids_get_keys(FuHwids *self); +const gchar * +fu_hwids_get_value(FuHwids *self, const gchar *key); +void +fu_hwids_add_smbios_override(FuHwids *self, const gchar *key, const gchar *value); +const gchar * +fu_hwids_get_replace_keys(FuHwids *self, const gchar *key); +gchar * +fu_hwids_get_replace_values(FuHwids *self, + const gchar *keys, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gchar * +fu_hwids_get_guid(FuHwids *self, const gchar *keys, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fu_hwids_get_guids(FuHwids *self); +gboolean +fu_hwids_has_guid(FuHwids *self, const gchar *guid); +gboolean +fu_hwids_setup(FuHwids *self, FuSmbios *smbios, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-i2c-device.c b/fwupd-1.8.6/libfwupdplugin/fu-i2c-device.c new file mode 100644 index 0000000000000000000000000000000000000000..09523f579aa3893b5d542bc621ee50e27759ab26 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-i2c-device.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuI2cDevice" + +#include "config.h" + +#include +#include +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "fu-i2c-device.h" +#include "fu-string.h" + +/** + * FuI2cDevice + * + * A I²C device with an assigned bus number. + * + * See also: #FuUdevDevice + */ + +typedef struct { + guint bus_number; +} FuI2cDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuI2cDevice, fu_i2c_device, FU_TYPE_UDEV_DEVICE) + +enum { PROP_0, PROP_BUS_NUMBER, PROP_LAST }; + +#define GET_PRIVATE(o) (fu_i2c_device_get_instance_private(o)) + +static void +fu_i2c_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuI2cDevice *self = FU_I2C_DEVICE(device); + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append_kx(str, idt, "BusNumber", priv->bus_number); +} + +static void +fu_i2c_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuI2cDevice *self = FU_I2C_DEVICE(object); + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_BUS_NUMBER: + g_value_set_uint(value, priv->bus_number); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_i2c_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuI2cDevice *self = FU_I2C_DEVICE(object); + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_BUS_NUMBER: + priv->bus_number = g_value_get_uint(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gboolean +fu_i2c_device_open(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUDEV + FuI2cDevice *self = FU_I2C_DEVICE(device); + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + gint bus_fd; + g_autofree gchar *bus_path = NULL; + + /* open the bus, not the device represented by self */ + bus_path = g_strdup_printf("/dev/i2c-%u", priv->bus_number); + if ((bus_fd = g_open(bus_path, O_RDWR, 0)) == -1) { + g_set_error(error, + G_IO_ERROR, +#ifdef HAVE_ERRNO_H + g_io_error_from_errno(errno), +#else + G_IO_ERROR_FAILED, +#endif + "failed to open %s read-write", + bus_path); + return FALSE; + } + fu_udev_device_set_fd(FU_UDEV_DEVICE(self), bus_fd); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), FU_UDEV_DEVICE_FLAG_NONE); +#endif + + /* FuUdevDevice->open */ + return FU_DEVICE_CLASS(fu_i2c_device_parent_class)->open(device, error); +} + +static gboolean +fu_i2c_device_probe(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUDEV + FuI2cDevice *self = FU_I2C_DEVICE(device); + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + const gchar *tmp; + g_autofree gchar *devname = NULL; + g_autoptr(GUdevDevice) udev_parent = NULL; +#endif + + /* set physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "i2c", error)) + return FALSE; + +#ifdef HAVE_GUDEV + /* i2c devices all expose a name */ + tmp = g_udev_device_get_sysfs_attr(udev_device, "name"); + fu_device_add_instance_strsafe(device, "NAME", tmp); + if (!fu_device_build_instance_id(device, error, "I2C", "NAME", NULL)) + return FALSE; + + /* get bus number out of sysfs path */ + udev_parent = g_udev_device_get_parent(udev_device); + devname = g_path_get_basename(g_udev_device_get_sysfs_path(udev_parent)); + if (g_str_has_prefix(devname, "i2c-")) { + guint64 tmp64 = 0; + g_autoptr(GError) error_local = NULL; + if (!fu_strtoull(devname + 4, &tmp64, 0, G_MAXUINT, &error_local)) { + g_debug("ignoring i2c devname bus number: %s", error_local->message); + } else { + priv->bus_number = tmp64; + } + } +#endif + + /* success */ + return TRUE; +} + +/** + * fu_i2c_device_get_bus_number: + * @self: a #FuI2cDevice + * + * Gets the I²C bus number. + * + * Returns: integer + * + * Since: 1.6.1 + **/ +guint +fu_i2c_device_get_bus_number(FuI2cDevice *self) +{ + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_I2C_DEVICE(self), G_MAXUINT); + return priv->bus_number; +} + +/** + * fu_i2c_device_set_bus_number: + * @self: a #FuI2cDevice + * @bus_number: integer, typically the output of g_udev_device_get_number() + * + * Sets the I²C bus number. + * + * Since: 1.6.2 + **/ +void +fu_i2c_device_set_bus_number(FuI2cDevice *self, guint bus_number) +{ + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_I2C_DEVICE(self)); + priv->bus_number = bus_number; +} + +/** + * fu_i2c_device_write: + * @self: a #FuI2cDevice + * @buf: (out): data + * @bufsz: size of @data + * @error: (nullable): optional return location for an error + * + * Write multiple bytes to the I²C device. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_i2c_device_write(FuI2cDevice *self, const guint8 *buf, gsize bufsz, GError **error) +{ + return fu_udev_device_pwrite(FU_UDEV_DEVICE(self), 0x0, buf, bufsz, error); +} + +/** + * fu_i2c_device_read: + * @self: a #FuI2cDevice + * @buf: (out): data + * @bufsz: size of @data + * @error: (nullable): optional return location for an error + * + * Read multiple bytes from the I²C device. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_i2c_device_read(FuI2cDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + return fu_udev_device_pread(FU_UDEV_DEVICE(self), 0x0, buf, bufsz, error); +} + +static void +fu_i2c_device_incorporate(FuDevice *device, FuDevice *donor) +{ + FuI2cDevice *self = FU_I2C_DEVICE(device); + FuI2cDevicePrivate *priv = GET_PRIVATE(self); + FuI2cDevicePrivate *priv_donor = GET_PRIVATE(FU_I2C_DEVICE(donor)); + + g_return_if_fail(FU_IS_I2C_DEVICE(self)); + g_return_if_fail(FU_IS_I2C_DEVICE(donor)); + + /* FuUdevDevice->incorporate */ + FU_DEVICE_CLASS(fu_i2c_device_parent_class)->incorporate(device, donor); + + /* copy private instance data */ + priv->bus_number = priv_donor->bus_number; +} + +static void +fu_i2c_device_init(FuI2cDevice *self) +{ +} + +static void +fu_i2c_device_class_init(FuI2cDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->get_property = fu_i2c_device_get_property; + object_class->set_property = fu_i2c_device_set_property; + klass_device->open = fu_i2c_device_open; + klass_device->probe = fu_i2c_device_probe; + klass_device->to_string = fu_i2c_device_to_string; + klass_device->incorporate = fu_i2c_device_incorporate; + + /** + * FuI2cDevice:bus-number: + * + * The I²C bus number. + * + * Since: 1.6.2 + */ + pspec = g_param_spec_uint("bus-number", + NULL, + NULL, + 0x0, + G_MAXUINT, + 0x0, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BUS_NUMBER, pspec); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-i2c-device.h b/fwupd-1.8.6/libfwupdplugin/fu-i2c-device.h new file mode 100644 index 0000000000000000000000000000000000000000..29562e93fbf6e574c38a6d61d206b761c1d4df39 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-i2c-device.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-udev-device.h" + +#define FU_TYPE_I2C_DEVICE (fu_i2c_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuI2cDevice, fu_i2c_device, FU, I2C_DEVICE, FuUdevDevice) + +struct _FuI2cDeviceClass { + FuUdevDeviceClass parent_class; + gpointer __reserved[31]; +}; + +guint +fu_i2c_device_get_bus_number(FuI2cDevice *self); +void +fu_i2c_device_set_bus_number(FuI2cDevice *self, guint bus_number); +gboolean +fu_i2c_device_read(FuI2cDevice *self, guint8 *buf, gsize bufsz, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_i2c_device_write(FuI2cDevice *self, const guint8 *buf, gsize bufsz, GError **error) + G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-bios.c b/fwupd-1.8.6/libfwupdplugin/fu-ifd-bios.c new file mode 100644 index 0000000000000000000000000000000000000000..2b52a522f00ecf4c498ae2d98c6680714fc70c40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-bios.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-efi-firmware-volume.h" +#include "fu-ifd-bios.h" +#include "fu-mem.h" + +/** + * FuIfdBios: + * + * An Intel BIOS section. + * + * See also: [class@FuFirmware] + */ + +G_DEFINE_TYPE(FuIfdBios, fu_ifd_bios, FU_TYPE_IFD_IMAGE) + +#define FU_IFD_BIOS_FIT_SIGNATURE 0x5449465F +#define FU_IFD_BIOS_FIT_SIZE 0x150000 + +static gboolean +fu_ifd_bios_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + guint32 sig; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* jump 16MiB as required */ + if (bufsz > 0x100000) + offset += 0x100000; + + /* read each volume in order */ + while (offset < bufsz) { + g_autoptr(FuFirmware) firmware_tmp = NULL; + g_autoptr(GBytes) fw_offset = NULL; + + /* ignore _FIT_ as EOF */ + if (!fu_memread_uint32_safe(buf, bufsz, offset, &sig, G_LITTLE_ENDIAN, error)) { + g_prefix_error(error, "failed to read start signature: "); + return FALSE; + } + if (sig == FU_IFD_BIOS_FIT_SIGNATURE) + break; + if (sig == 0xffffffff) + break; + + /* FV */ + fw_offset = fu_bytes_new_offset(fw, offset, bufsz - offset, error); + if (fw_offset == NULL) + return FALSE; + firmware_tmp = fu_firmware_new_from_gtypes(fw_offset, + flags, + error, + FU_TYPE_EFI_FIRMWARE_VOLUME, + G_TYPE_INVALID); + if (firmware_tmp == NULL) { + g_prefix_error(error, + "failed to read @0x%x of 0x%x: ", + (guint)offset, + (guint)bufsz); + return FALSE; + } + fu_firmware_set_offset(firmware_tmp, offset); + fu_firmware_add_image(firmware, firmware_tmp); + + /* next! */ + offset += fu_firmware_get_size(firmware_tmp); + } + + /* success */ + return TRUE; +} + +static void +fu_ifd_bios_init(FuIfdBios *self) +{ + fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_4K); +} + +static void +fu_ifd_bios_class_init(FuIfdBiosClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_ifd_bios_parse; +} + +/** + * fu_ifd_bios_new: + * + * Creates a new #FuFirmware + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_ifd_bios_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_IFD_BIOS, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-bios.h b/fwupd-1.8.6/libfwupdplugin/fu-ifd-bios.h new file mode 100644 index 0000000000000000000000000000000000000000..272e13afdf9d06d4e726069ae6e035cb228518ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-bios.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-ifd-image.h" + +#define FU_TYPE_IFD_BIOS (fu_ifd_bios_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIfdBios, fu_ifd_bios, FU, IFD_BIOS, FuIfdImage) + +struct _FuIfdBiosClass { + FuIfdImageClass parent_class; +}; + +FuFirmware * +fu_ifd_bios_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-common.c b/fwupd-1.8.6/libfwupdplugin/fu-ifd-common.c new file mode 100644 index 0000000000000000000000000000000000000000..f3666e819c74aa730400002fa441311fe1ad435d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-common.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ifd-common.h" + +/** + * fu_ifd_region_to_string: + * @region: A #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS + * + * Converts a #FuIfdRegion to a string. + * + * Returns: identifier string + * + * Since: 1.6.2 + **/ +const gchar * +fu_ifd_region_to_string(FuIfdRegion region) +{ + if (region == FU_IFD_REGION_DESC) + return "desc"; + if (region == FU_IFD_REGION_BIOS) + return "bios"; + if (region == FU_IFD_REGION_ME) + return "me"; + if (region == FU_IFD_REGION_GBE) + return "gbe"; + if (region == FU_IFD_REGION_PLATFORM) + return "platform"; + if (region == FU_IFD_REGION_DEVEXP) + return "devexp"; + if (region == FU_IFD_REGION_BIOS2) + return "bios2"; + if (region == FU_IFD_REGION_EC) + return "ec"; + if (region == FU_IFD_REGION_IE) + return "ie"; + if (region == FU_IFD_REGION_10GBE) + return "10gbe"; + return NULL; +} + +/** + * fu_ifd_region_to_name: + * @region: A #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS + * + * Converts a #FuIfdRegion to a name the user might recognize. + * + * Returns: identifier string + * + * Since: 1.6.2 + **/ +const gchar * +fu_ifd_region_to_name(FuIfdRegion region) +{ + if (region == FU_IFD_REGION_DESC) + return "IFD descriptor region"; + if (region == FU_IFD_REGION_BIOS) + return "BIOS"; + if (region == FU_IFD_REGION_ME) + return "Intel Management Engine"; + if (region == FU_IFD_REGION_GBE) + return "Gigabit Ethernet"; + if (region == FU_IFD_REGION_PLATFORM) + return "Platform firmware"; + if (region == FU_IFD_REGION_DEVEXP) + return "Device Firmware"; + if (region == FU_IFD_REGION_BIOS2) + return "BIOS Backup"; + if (region == FU_IFD_REGION_EC) + return "Embedded Controller"; + if (region == FU_IFD_REGION_IE) + return "Innovation Engine"; + if (region == FU_IFD_REGION_10GBE) + return "10 Gigabit Ethernet"; + return NULL; +} + +/** + * fu_ifd_access_to_string: + * @access: A #FuIfdAccess, e.g. %FU_IFD_ACCESS_READ + * + * Converts a #FuIfdAccess to a string. + * + * Returns: identifier string + * + * Since: 1.6.2 + **/ +const gchar * +fu_ifd_access_to_string(FuIfdAccess access) +{ + if (access == FU_IFD_ACCESS_NONE) + return "--"; + if (access == FU_IFD_ACCESS_READ) + return "ro"; + if (access == FU_IFD_ACCESS_WRITE) + return "wr"; + if (access == (FU_IFD_ACCESS_READ | FU_IFD_ACCESS_WRITE)) + return "rw"; + return NULL; +} + +/** + * fu_ifd_region_to_access: + * @region: A #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS + * @flash_master: flash master number + * @new_layout: if Skylake or newer + * + * Converts a #FuIfdRegion to an access level. + * + * Returns: access + * + * Since: 1.6.2 + **/ +FuIfdAccess +fu_ifd_region_to_access(FuIfdRegion region, guint32 flash_master, gboolean new_layout) +{ + guint8 bit_r = 0; + guint8 bit_w = 0; + + /* new layout */ + if (new_layout) { + bit_r = (flash_master >> (region + 8)) & 0b1; + bit_w = (flash_master >> (region + 20)) & 0b1; + return (bit_r ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) | + (bit_w ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE); + } + + /* old layout */ + if (region == FU_IFD_REGION_DESC) { + bit_r = 16; + bit_w = 24; + } else if (region == FU_IFD_REGION_BIOS) { + bit_r = 17; + bit_w = 25; + } else if (region == FU_IFD_REGION_ME) { + bit_r = 18; + bit_w = 26; + } else if (region == FU_IFD_REGION_GBE) { + bit_r = 19; + bit_w = 27; + } + return ((flash_master >> bit_r) & 0b1 ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) | + ((flash_master >> bit_w) & 0b1 ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-common.h b/fwupd-1.8.6/libfwupdplugin/fu-ifd-common.h new file mode 100644 index 0000000000000000000000000000000000000000..37b8bec5447ac2ac073dc0db00ca5f4870ed1135 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-common.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FuIfdRegion: + * @FU_IFD_REGION_DESC: IFD descriptor + * @FU_IFD_REGION_BIOS: BIOS + * @FU_IFD_REGION_ME: ME + * @FU_IFD_REGION_GBE: GBE + * @FU_IFD_REGION_PLATFORM: Platform + * @FU_IFD_REGION_DEVEXP: Developer + * @FU_IFD_REGION_BIOS2: BIOS Backup + * @FU_IFD_REGION_EC: Embedded Controller + * @FU_IFD_REGION_IE: IE + * @FU_IFD_REGION_10GBE: 10GBE + * @FU_IFD_REGION_MAX: Maximum value + * + * The IFD region. + **/ +typedef enum { + FU_IFD_REGION_DESC = 0x00, + FU_IFD_REGION_BIOS = 0x01, + FU_IFD_REGION_ME = 0x02, + FU_IFD_REGION_GBE = 0x03, + FU_IFD_REGION_PLATFORM = 0x04, + FU_IFD_REGION_DEVEXP = 0x05, + FU_IFD_REGION_BIOS2 = 0x06, + FU_IFD_REGION_EC = 0x08, + FU_IFD_REGION_IE = 0x0A, + FU_IFD_REGION_10GBE = 0x0B, + FU_IFD_REGION_MAX = 0x0F, +} FuIfdRegion; + +/** + * FuIfdAccess: + * @FU_IFD_ACCESS_NONE: None + * @FU_IFD_ACCESS_READ: Readable + * @FU_IFD_ACCESS_WRITE: Writable + * + * The flags to use for IFD access permissions. + **/ +typedef enum { + FU_IFD_ACCESS_NONE = 0, + FU_IFD_ACCESS_READ = 1 << 0, + FU_IFD_ACCESS_WRITE = 1 << 1, +} FuIfdAccess; + +#define FU_IFD_FREG_BASE(freg) (((freg) << 12) & 0x07FFF000) +#define FU_IFD_FREG_LIMIT(freg) ((((freg) >> 4) & 0x07FFF000) | 0x00000FFF) + +const gchar * +fu_ifd_region_to_string(FuIfdRegion region); +const gchar * +fu_ifd_region_to_name(FuIfdRegion region); +const gchar * +fu_ifd_access_to_string(FuIfdAccess access); + +FuIfdAccess +fu_ifd_region_to_access(FuIfdRegion region, guint32 flash_master, gboolean new_layout); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-ifd-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..6e645095c1e2491549ce10a081253687271b45f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-firmware.c @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-ifd-bios.h" +#include "fu-ifd-common.h" +#include "fu-ifd-firmware.h" +#include "fu-ifd-image.h" +#include "fu-mem.h" + +/** + * FuIfdFirmware: + * + * An Intel Flash Descriptor. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + gboolean new_layout; + guint32 descriptor_map0; + guint32 descriptor_map1; + guint32 descriptor_map2; + guint8 num_regions; + guint8 num_components; + guint32 flash_region_base_addr; + guint32 flash_component_base_addr; + guint32 flash_master_base_addr; + guint32 flash_master[4]; /* indexed from 1, ignore [0] */ + guint32 flash_ich_strap_base_addr; + guint32 flash_mch_strap_base_addr; + guint32 components_rcd; + guint32 illegal_jedec; + guint32 illegal_jedec1; + guint32 *flash_descriptor_regs; +} FuIfdFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuIfdFirmware, fu_ifd_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_ifd_firmware_get_instance_private(o)) + +#define FU_IFD_SIZE 0x1000 +#define FU_IFD_SIGNATURE 0x0FF0A55A + +#define FU_IFD_FDBAR_RESERVED 0x0000 +#define FU_IFD_FDBAR_SIGNATURE 0x0010 +#define FU_IFD_FDBAR_DESCRIPTOR_MAP0 0x0014 +#define FU_IFD_FDBAR_DESCRIPTOR_MAP1 0x0018 +#define FU_IFD_FDBAR_DESCRIPTOR_MAP2 0x001C +#define FU_IFD_FDBAR_FLASH_UPPER_MAP1 0x0EFC +#define FU_IFD_FDBAR_OEM_SECTION 0x0F00 + +#define FU_IFD_FCBA_FLCOMP 0x0000 +#define FU_IFD_FCBA_FLILL 0x0004 +#define FU_IFD_FCBA_FLILL1 0x0008 + +#define FU_IFD_FREG_BASE(freg) (((freg) << 12) & 0x07FFF000) +#define FU_IFD_FREG_LIMIT(freg) ((((freg) >> 4) & 0x07FFF000) | 0x00000FFF) + +static void +fu_ifd_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuIfdFirmware *self = FU_IFD_FIRMWARE(firmware); + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "descriptor_map0", priv->descriptor_map0); + fu_xmlb_builder_insert_kx(bn, "descriptor_map1", priv->descriptor_map1); + fu_xmlb_builder_insert_kx(bn, "descriptor_map2", priv->descriptor_map2); + fu_xmlb_builder_insert_kx(bn, "num_regions", priv->num_regions); + fu_xmlb_builder_insert_kx(bn, "num_components", priv->num_components + 1); + fu_xmlb_builder_insert_kx(bn, "flash_region_base_addr", priv->flash_region_base_addr); + fu_xmlb_builder_insert_kx(bn, "flash_component_base_addr", priv->flash_component_base_addr); + fu_xmlb_builder_insert_kx(bn, "flash_master_base_addr", priv->flash_master_base_addr); + fu_xmlb_builder_insert_kx(bn, "flash_ich_strap_base_addr", priv->flash_ich_strap_base_addr); + fu_xmlb_builder_insert_kx(bn, "flash_mch_strap_base_addr", priv->flash_mch_strap_base_addr); + fu_xmlb_builder_insert_kx(bn, "components_rcd", priv->components_rcd); + fu_xmlb_builder_insert_kx(bn, "illegal_jedec", priv->illegal_jedec); + fu_xmlb_builder_insert_kx(bn, "illegal_jedec1", priv->illegal_jedec1); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + for (guint i = 1; i < 3; i++) { + g_autofree gchar *title = g_strdup_printf("flash_master%x", i + 1); + fu_xmlb_builder_insert_kx(bn, title, priv->flash_master[i]); + } + if (priv->flash_descriptor_regs != NULL) { + for (guint i = 0; i < priv->num_regions; i++) { + g_autofree gchar *title = + g_strdup_printf("flash_descriptor_reg%x", i); + fu_xmlb_builder_insert_kx(bn, + title, + priv->flash_descriptor_regs[i]); + } + } + } +} + +static gboolean +fu_ifd_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + FU_IFD_FDBAR_SIGNATURE, + &magic, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != FU_IFD_SIGNATURE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "signature invalid, got 0x%x, expected 0x%x", + magic, + (guint)FU_IFD_SIGNATURE); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ifd_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuIfdFirmware *self = FU_IFD_FIRMWARE(firmware); + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* check size */ + if (bufsz < FU_IFD_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "file is too small, expected bufsz >= 0x%x", + (guint)FU_IFD_SIZE); + return FALSE; + } + + /* descriptor registers */ + priv->descriptor_map0 = + fu_memread_uint32(buf + FU_IFD_FDBAR_DESCRIPTOR_MAP0, G_LITTLE_ENDIAN); + priv->num_regions = (priv->descriptor_map0 >> 24) & 0b111; + if (priv->num_regions == 0) + priv->num_regions = 10; + priv->num_components = (priv->descriptor_map0 >> 8) & 0b11; + priv->flash_component_base_addr = (priv->descriptor_map0 << 4) & 0x00000FF0; + priv->flash_region_base_addr = (priv->descriptor_map0 >> 12) & 0x00000FF0; + priv->descriptor_map1 = + fu_memread_uint32(buf + FU_IFD_FDBAR_DESCRIPTOR_MAP1, G_LITTLE_ENDIAN); + priv->flash_master_base_addr = (priv->descriptor_map1 << 4) & 0x00000FF0; + priv->flash_ich_strap_base_addr = (priv->descriptor_map1 >> 12) & 0x00000FF0; + priv->descriptor_map2 = + fu_memread_uint32(buf + FU_IFD_FDBAR_DESCRIPTOR_MAP2, G_LITTLE_ENDIAN); + priv->flash_mch_strap_base_addr = (priv->descriptor_map2 << 4) & 0x00000FF0; + + /* FCBA */ + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_component_base_addr + FU_IFD_FCBA_FLCOMP, + &priv->components_rcd, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_component_base_addr + FU_IFD_FCBA_FLILL, + &priv->illegal_jedec, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_component_base_addr + FU_IFD_FCBA_FLILL1, + &priv->illegal_jedec1, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* FMBA */ + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_master_base_addr + 0x0, + &priv->flash_master[1], + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_master_base_addr + 0x4, + &priv->flash_master[2], + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_master_base_addr + 0x8, + &priv->flash_master[3], + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* FRBA */ + priv->flash_descriptor_regs = g_new0(guint32, priv->num_regions); + for (guint i = 0; i < priv->num_regions; i++) { + if (!fu_memread_uint32_safe(buf, + bufsz, + priv->flash_region_base_addr + (i * sizeof(guint32)), + &priv->flash_descriptor_regs[i], + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + for (guint i = 0; i < priv->num_regions; i++) { + const gchar *freg_str = fu_ifd_region_to_string(i); + guint32 freg_base = FU_IFD_FREG_BASE(priv->flash_descriptor_regs[i]); + guint32 freg_limt = FU_IFD_FREG_LIMIT(priv->flash_descriptor_regs[i]); + guint32 freg_size = (freg_limt - freg_base) + 1; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) contents = NULL; + + /* invalid */ + if (freg_base > freg_limt) + continue; + + /* create image */ + g_debug("freg %s 0x%04x -> 0x%04x", freg_str, freg_base, freg_limt); + contents = fu_bytes_new_offset(fw, freg_base, freg_size, error); + if (contents == NULL) + return FALSE; + if (i == FU_IFD_REGION_BIOS) { + img = fu_ifd_bios_new(); + } else { + img = fu_ifd_image_new(); + } + if (!fu_firmware_parse(img, contents, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + fu_firmware_set_addr(img, freg_base); + fu_firmware_set_idx(img, i); + if (freg_str != NULL) + fu_firmware_set_id(img, freg_str); + fu_firmware_add_image(firmware, img); + + /* is writable by anything other than the region itself */ + for (FuIfdRegion r = 1; r <= 3; r++) { + FuIfdAccess acc; + acc = fu_ifd_region_to_access(i, priv->flash_master[r], priv->new_layout); + fu_ifd_image_set_access(FU_IFD_IMAGE(img), r, acc); + } + } + + /* success */ + return TRUE; +} + +/** + * fu_ifd_firmware_check_jedec_cmd: + * @self: a #FuIfdFirmware + * @cmd: a JEDEC command, e.g. 0x42 for "whole chip erase" + * + * Checks a JEDEC command to see if it has been put on the "illegal_jedec" list. + * + * Returns: %TRUE if the command is allowed + * + * Since: 1.6.2 + **/ +gboolean +fu_ifd_firmware_check_jedec_cmd(FuIfdFirmware *self, guint8 cmd) +{ + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + for (guint j = 0; j < 32; j += 8) { + if (((priv->illegal_jedec >> j) & 0xff) == cmd) + return FALSE; + if (((priv->illegal_jedec1 >> j) & 0xff) == cmd) + return FALSE; + } + return TRUE; +} + +static GBytes * +fu_ifd_firmware_write(FuFirmware *firmware, GError **error) +{ + FuIfdFirmware *self = FU_IFD_FIRMWARE(firmware); + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + gsize bufsz_max = 0x0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GHashTable) blobs = NULL; + g_autoptr(FuFirmware) img_desc = NULL; + + /* if the descriptor does not exist, then add something plausible */ + img_desc = fu_firmware_get_image_by_idx(firmware, FU_IFD_REGION_DESC, NULL); + if (img_desc == NULL) { + g_autoptr(GByteArray) buf_desc = g_byte_array_new(); + g_autoptr(GBytes) blob_desc = NULL; + fu_byte_array_set_size(buf_desc, FU_IFD_SIZE, 0x00); + + /* success */ + blob_desc = g_byte_array_free_to_bytes(g_steal_pointer(&buf_desc)); + img_desc = fu_firmware_new_from_bytes(blob_desc); + fu_firmware_set_addr(img_desc, 0x0); + fu_firmware_set_idx(img_desc, FU_IFD_REGION_DESC); + fu_firmware_set_id(img_desc, "desc"); + fu_firmware_add_image(firmware, img_desc); + } + + /* generate ahead of time */ + blobs = g_hash_table_new_full(g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)g_bytes_unref); + for (guint i = 0; i < priv->num_regions; i++) { + g_autoptr(FuFirmware) img = fu_firmware_get_image_by_idx(firmware, i, NULL); + g_autoptr(GBytes) blob = NULL; + + if (img == NULL) + continue; + blob = fu_firmware_write(img, error); + if (blob == NULL) { + g_prefix_error(error, "failed to write %s: ", fu_firmware_get_id(img)); + return NULL; + } + if (g_bytes_get_data(blob, NULL) == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to write %s", + fu_firmware_get_id(img)); + return NULL; + } + g_hash_table_insert(blobs, GUINT_TO_POINTER(i), g_bytes_ref(blob)); + + /* check total size */ + bufsz_max = MAX(fu_firmware_get_addr(img) + g_bytes_get_size(blob), bufsz_max); + } + fu_byte_array_set_size(buf, bufsz_max, 0x00); + + /* reserved */ + for (guint i = 0; i < 0x10; i++) + buf->data[FU_IFD_FDBAR_RESERVED + i] = 0xff; + + /* signature */ + fu_memwrite_uint32(buf->data + FU_IFD_FDBAR_SIGNATURE, FU_IFD_SIGNATURE, G_LITTLE_ENDIAN); + + /* descriptor map */ + fu_memwrite_uint32(buf->data + FU_IFD_FDBAR_DESCRIPTOR_MAP0, + priv->descriptor_map0, + G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + FU_IFD_FDBAR_DESCRIPTOR_MAP1, + priv->descriptor_map1, + G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + FU_IFD_FDBAR_DESCRIPTOR_MAP2, + priv->descriptor_map2, + G_LITTLE_ENDIAN); + + /* FCBA */ + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + priv->flash_component_base_addr + FU_IFD_FCBA_FLCOMP, + priv->components_rcd, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + priv->flash_component_base_addr + FU_IFD_FCBA_FLILL, + priv->illegal_jedec, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + priv->flash_component_base_addr + FU_IFD_FCBA_FLILL1, + priv->illegal_jedec1, + G_LITTLE_ENDIAN, + error)) + return NULL; + + /* FRBA */ + for (guint i = 0; i < priv->num_regions; i++) { + guint32 freg_base = 0x7FFF000; + guint32 freg_limt = 0x0; + guint32 flreg; + g_autoptr(FuFirmware) img = fu_firmware_get_image_by_idx(firmware, i, NULL); + if (img != NULL) { + GBytes *blob = + g_hash_table_lookup(blobs, GUINT_TO_POINTER(fu_firmware_get_idx(img))); + freg_base = fu_firmware_get_addr(img); + freg_limt = (freg_base + g_bytes_get_size(blob)) - 1; + } + flreg = ((freg_limt << 4) & 0xFFFF0000) | (freg_base >> 12); + g_debug("freg 0x%04x -> 0x%04x = 0x%08x", freg_base, freg_limt, flreg); + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + priv->flash_region_base_addr + (i * sizeof(guint32)), + flreg, + G_LITTLE_ENDIAN, + error)) + return NULL; + } + + /* write images at correct offsets */ + for (guint i = 1; i < priv->num_regions; i++) { + GBytes *blob; + g_autoptr(FuFirmware) img = fu_firmware_get_image_by_idx(firmware, i, NULL); + if (img == NULL) + continue; + blob = g_hash_table_lookup(blobs, GUINT_TO_POINTER(fu_firmware_get_idx(img))); + if (!fu_memcpy_safe(buf->data, + buf->len, + fu_firmware_get_addr(img), + g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + 0x0, + g_bytes_get_size(blob), + error)) + return NULL; + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_ifd_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuIfdFirmware *self = FU_IFD_FIRMWARE(firmware); + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "descriptor_map0", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->descriptor_map0 = tmp; + tmp = xb_node_query_text_as_uint(n, "descriptor_map1", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->descriptor_map1 = tmp; + tmp = xb_node_query_text_as_uint(n, "descriptor_map2", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->descriptor_map2 = tmp; + tmp = xb_node_query_text_as_uint(n, "components_rcd", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->components_rcd = tmp; + tmp = xb_node_query_text_as_uint(n, "illegal_jedec", NULL); + if (tmp != G_MAXUINT64) { + priv->illegal_jedec = tmp & 0xFFFFFFFF; + priv->illegal_jedec1 = tmp >> 32; + } + + /* success */ + return TRUE; +} + +static void +fu_ifd_firmware_init(FuIfdFirmware *self) +{ + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + + /* some good defaults */ + priv->new_layout = TRUE; + priv->num_regions = 10; + priv->flash_region_base_addr = 0x40; + priv->flash_component_base_addr = 0x30; + priv->flash_master_base_addr = 0x80; + priv->flash_master[1] = 0x00A00F00; + priv->flash_master[2] = 0x00400D00; + priv->flash_master[3] = 0x00800900; + priv->flash_ich_strap_base_addr = 0x100; + priv->flash_mch_strap_base_addr = 0x300; +} + +static void +fu_ifd_firmware_finalize(GObject *object) +{ + FuIfdFirmware *self = FU_IFD_FIRMWARE(object); + FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); + g_free(priv->flash_descriptor_regs); + G_OBJECT_CLASS(fu_ifd_firmware_parent_class)->finalize(object); +} + +static void +fu_ifd_firmware_class_init(FuIfdFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_ifd_firmware_finalize; + klass_firmware->check_magic = fu_ifd_firmware_check_magic; + klass_firmware->export = fu_ifd_firmware_export; + klass_firmware->parse = fu_ifd_firmware_parse; + klass_firmware->write = fu_ifd_firmware_write; + klass_firmware->build = fu_ifd_firmware_build; +} + +/** + * fu_ifd_firmware_new: + * + * Creates a new #FuFirmware of sub type Ifd + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_ifd_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_IFD_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-ifd-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..79562564975ec7a3b5fe711ae3bea2a527a4196a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-firmware.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_IFD_FIRMWARE (fu_ifd_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIfdFirmware, fu_ifd_firmware, FU, IFD_FIRMWARE, FuFirmware) + +struct _FuIfdFirmwareClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_ifd_firmware_new(void); +gboolean +fu_ifd_firmware_check_jedec_cmd(FuIfdFirmware *self, guint8 cmd); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-image.c b/fwupd-1.8.6/libfwupdplugin/fu-ifd-image.c new file mode 100644 index 0000000000000000000000000000000000000000..299e9f049cd8ccbcd38089931aa4018c5adc6781 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-image.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-ifd-image.h" + +/** + * FuIfdImage: + * + * An Intel Flash Descriptor image, e.g. BIOS. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + FuIfdAccess access[FU_IFD_REGION_MAX]; +} FuIfdImagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuIfdImage, fu_ifd_image, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_ifd_image_get_instance_private(o)) + +static void +fu_ifd_image_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuIfdImage *self = FU_IFD_IMAGE(firmware); + FuIfdImagePrivate *priv = GET_PRIVATE(self); + for (guint i = 0; i < FU_IFD_REGION_MAX; i++) { + if (priv->access[i] == FU_IFD_ACCESS_NONE) + continue; + xb_builder_node_insert_text(bn, + "access", + fu_ifd_access_to_string(priv->access[i]), + "region", + fu_ifd_region_to_string(i), + NULL); + } +} + +/** + * fu_ifd_image_set_access: + * @self: a #FuIfdImage + * @region: a #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS + * @access: a #FuIfdAccess, e.g. %FU_IFD_ACCESS_NONE + * + * Sets the access control for a specific reason. + * + * Since: 1.6.2 + **/ +void +fu_ifd_image_set_access(FuIfdImage *self, FuIfdRegion region, FuIfdAccess access) +{ + FuIfdImagePrivate *priv = GET_PRIVATE(self); + priv->access[region] = access; +} + +/** + * fu_ifd_image_get_access: + * @self: a #FuIfdImage + * @region: a #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS + * + * Gets the access control for a specific reason. + * + * Returns: a #FuIfdAccess, e.g. %FU_IFD_ACCESS_NONE + * + * Since: 1.6.2 + **/ +FuIfdAccess +fu_ifd_image_get_access(FuIfdImage *self, FuIfdRegion region) +{ + FuIfdImagePrivate *priv = GET_PRIVATE(self); + return priv->access[region]; +} + +static GBytes * +fu_ifd_image_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* sanity check */ + if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "alignment invalid, got 0x%02x", + fu_firmware_get_alignment(firmware)); + return NULL; + } + + /* add each volume */ + if (images->len > 0) { + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) bytes = fu_firmware_write(img, error); + if (bytes == NULL) + return NULL; + fu_byte_array_append_bytes(buf, bytes); + } + } else { + g_autoptr(GBytes) bytes = NULL; + bytes = fu_firmware_get_bytes_with_patches(firmware, error); + if (bytes == NULL) + return NULL; + fu_byte_array_append_bytes(buf, bytes); + } + + /* align up */ + fu_byte_array_set_size(buf, + fu_common_align_up(buf->len, fu_firmware_get_alignment(firmware)), + 0x00); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_ifd_image_init(FuIfdImage *self) +{ + fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_4K); +} + +static void +fu_ifd_image_class_init(FuIfdImageClass *klass) +{ + FuFirmwareClass *klass_image = FU_FIRMWARE_CLASS(klass); + klass_image->export = fu_ifd_image_export; + klass_image->write = fu_ifd_image_write; +} + +/** + * fu_ifd_image_new: + * + * Creates a new #FuFirmware + * + * Since: 1.6.2 + **/ +FuFirmware * +fu_ifd_image_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_IFD_IMAGE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifd-image.h b/fwupd-1.8.6/libfwupdplugin/fu-ifd-image.h new file mode 100644 index 0000000000000000000000000000000000000000..2e3bed74ec4876e41ccdd983baca53596a97efdb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifd-image.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" +#include "fu-ifd-common.h" + +#define FU_TYPE_IFD_IMAGE (fu_ifd_image_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIfdImage, fu_ifd_image, FU, IFD_IMAGE, FuFirmware) + +struct _FuIfdImageClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_ifd_image_new(void); +void +fu_ifd_image_set_access(FuIfdImage *self, FuIfdRegion region, FuIfdAccess access); +FuIfdAccess +fu_ifd_image_get_access(FuIfdImage *self, FuIfdRegion region); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifwi-cpd-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-cpd-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..2da3e120f03a02eb8906900394db1d0ea018c4e0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-cpd-firmware.c @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Intel + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-ifwi-cpd-firmware.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * FuIfwiCpdFirmware: + * + * An Intel Code Partition Directory (aka CPD) can be found in IFWI (Integrated Firmware Image) + * firmware blobs which are used in various Intel products using an IPU (Infrastructure Processing + * Unit). + * + * This could include hardware like SmartNICs, GPUs, camera and audio devices. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint8 header_version; + guint8 entry_version; +} FuIfwiCpdFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuIfwiCpdFirmware, fu_ifwi_cpd_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_ifwi_cpd_firmware_get_instance_private(o)) + +#define FU_IFWI_CPD_FIRMWARE_HEADER_MARKER 0x44504324 +#define FU_IFWI_CPD_FIRMWARE_ENTRIES_MAX 1024 + +typedef struct __attribute__((packed)) { + guint32 header_marker; + guint32 num_of_entries; + guint8 header_version; + guint8 entry_version; + guint8 header_length; + guint8 checksum; + guint32 partition_name; + guint32 crc32; +} FuIfwiCpdHeader; + +typedef struct __attribute__((packed)) { + gchar name[12]; + guint32 offset; + guint32 length; + guint8 reserved1[4]; +} FuIfwiCpdEntry; + +typedef struct __attribute__((packed)) { + guint32 header_type; + guint32 header_length; /* dwords */ + guint32 header_version; + guint32 flags; + guint32 vendor; + guint32 date; + guint32 size; /* dwords */ + guint32 id; + guint32 rsvd; + guint64 version; + guint32 svn; +} FuIfwiCpdManifestHeader; + +typedef struct __attribute__((packed)) { + guint32 extension_type; + guint32 extension_length; +} FuIfwiCpdManifestExtHeader; + +static void +fu_ifwi_cpd_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuIfwiCpdFirmware *self = FU_IFWI_CPD_FIRMWARE(firmware); + FuIfwiCpdFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "header_version", priv->header_version); + fu_xmlb_builder_insert_kx(bn, "entry_version", priv->entry_version); +} + +static gboolean +fu_ifwi_cpd_firmware_parse_manifest(FuFirmware *firmware, GBytes *fw, GError **error) +{ + gsize bufsz = 0; + guint32 header_length = 0; + guint32 size = 0; + guint64 version = 0; + gsize offset = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* raw version */ + if (!fu_memread_uint64_safe(buf, + bufsz, + G_STRUCT_OFFSET(FuIfwiCpdManifestHeader, version), + &version, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_firmware_set_version_raw(firmware, version); + + /* verify the size */ + if (!fu_memread_uint32_safe(buf, + bufsz, + G_STRUCT_OFFSET(FuIfwiCpdManifestHeader, size), + &size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (size * 4 != bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid manifest invalid length, got 0x%x, expected 0x%x", + size * 4, + (guint)bufsz); + return FALSE; + } + + /* parse extensions */ + if (!fu_memread_uint32_safe(buf, + bufsz, + G_STRUCT_OFFSET(FuIfwiCpdManifestHeader, header_length), + &header_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + offset += header_length * 4; + while (offset < bufsz) { + guint32 extension_type = 0; + guint32 extension_length = 0; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) img = fu_firmware_new(); + + /* set the extension type as the index */ + if (!fu_memread_uint32_safe( + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdManifestExtHeader, extension_type), + &extension_type, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (extension_type == 0x0) + break; + fu_firmware_set_idx(img, extension_type); + + /* add data section */ + if (!fu_memread_uint32_safe( + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdManifestExtHeader, extension_length), + &extension_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (extension_length == 0x0) + break; + if (extension_length < sizeof(FuIfwiCpdManifestExtHeader)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid manifest extension header length 0x%x", + (guint)extension_length); + return FALSE; + } + blob = fu_bytes_new_offset(fw, + offset + sizeof(FuIfwiCpdManifestExtHeader), + extension_length - sizeof(FuIfwiCpdManifestExtHeader), + error); + if (blob == NULL) + return FALSE; + fu_firmware_set_bytes(img, blob); + + /* success */ + fu_firmware_set_offset(img, offset); + fu_firmware_add_image(firmware, img); + offset += extension_length; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ifwi_cpd_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + G_STRUCT_OFFSET(FuIfwiCpdHeader, header_marker), + &magic, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != FU_IFWI_CPD_FIRMWARE_HEADER_MARKER) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid CPD header marker 0x%x, expected 0x%x", + magic, + (guint)FU_IFWI_CPD_FIRMWARE_HEADER_MARKER); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ifwi_cpd_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuIfwiCpdFirmware *self = FU_IFWI_CPD_FIRMWARE(firmware); + FuIfwiCpdFirmwarePrivate *priv = GET_PRIVATE(self); + gsize bufsz = 0; + guint32 num_of_entries = 0; + guint32 partition_name = 0; + guint8 header_length = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* other header fields */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdHeader, header_version), + &priv->header_version, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdHeader, entry_version), + &priv->entry_version, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdHeader, header_length), + &header_length, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdHeader, partition_name), + &partition_name, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_firmware_set_idx(firmware, partition_name); + + /* read out entries */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdHeader, num_of_entries), + &num_of_entries, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (num_of_entries > FU_IFWI_CPD_FIRMWARE_ENTRIES_MAX) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "too many entries 0x%x, expected <= 0x%x", + num_of_entries, + (guint)FU_IFWI_CPD_FIRMWARE_ENTRIES_MAX); + return FALSE; + } + offset += header_length; + for (guint32 i = 0; i < num_of_entries; i++) { + gchar name[12] = {0x0}; + g_autoptr(FuFirmware) img = fu_firmware_new(); + g_autoptr(GBytes) img_blob = NULL; + g_autofree gchar *id = NULL; + guint32 img_offset = 0; + guint32 img_length = 0; + + /* the IDX is the position in the file */ + fu_firmware_set_idx(img, i); + + /* copy name as id */ + if (!fu_memcpy_safe((guint8 *)name, + sizeof(name), + 0x0, /* dst */ + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdEntry, name), /* src */ + sizeof(name), + error)) { + return FALSE; + } + id = fu_strsafe(name, sizeof(name)); + fu_firmware_set_id(img, id); + + /* copy offset, ignoring huffman and reserved bits */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdEntry, offset), + &img_offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + img_offset &= 0x1FFFFFF; + fu_firmware_set_offset(img, img_offset); + + /* copy data */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiCpdEntry, length), + &img_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + img_blob = fu_bytes_new_offset(fw, img_offset, img_length, error); + if (img_blob == NULL) + return FALSE; + fu_firmware_set_bytes(img, img_blob); + + /* read the manifest */ + if (i == FU_IFWI_CPD_FIRMWARE_IDX_MANIFEST && + g_bytes_get_size(img_blob) > sizeof(FuIfwiCpdManifestHeader)) { + if (!fu_ifwi_cpd_firmware_parse_manifest(img, img_blob, error)) + return FALSE; + } + + /* success */ + fu_firmware_add_image(firmware, img); + offset += sizeof(FuIfwiCpdEntry); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_ifwi_cpd_firmware_write(FuFirmware *firmware, GError **error) +{ + FuIfwiCpdFirmware *self = FU_IFWI_CPD_FIRMWARE(firmware); + FuIfwiCpdFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + gsize offset = 0; + + /* write the header */ + fu_byte_array_append_uint32(buf, FU_IFWI_CPD_FIRMWARE_HEADER_MARKER, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, imgs->len, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, priv->header_version); + fu_byte_array_append_uint8(buf, priv->entry_version); + fu_byte_array_append_uint8(buf, sizeof(FuIfwiCpdHeader)); + fu_byte_array_append_uint8(buf, 0x0); /* checksum */ + fu_byte_array_append_uint32(buf, fu_firmware_get_idx(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* crc32 */ + + /* fixup the image offsets */ + offset += sizeof(FuIfwiCpdHeader); + offset += sizeof(FuIfwiCpdEntry) * imgs->len; + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = NULL; + blob = fu_firmware_get_bytes(img, error); + if (blob == NULL) { + g_prefix_error(error, "image 0x%x: ", i); + return NULL; + } + fu_firmware_set_offset(img, offset); + offset += g_bytes_get_size(blob); + } + + /* add entry headers */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + const gchar *id = fu_firmware_get_id(img); + gchar name[12] = {0x0}; + + /* sanity check */ + if (id == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "image 0x%x must have an ID", + (guint)fu_firmware_get_idx(img)); + return NULL; + } + + /* copy id into name */ + if (!fu_memcpy_safe((guint8 *)name, + sizeof(name), + 0x0, /* dst */ + (const guint8 *)id, + strlen(id), + 0x0, /* src */ + strlen(id), + error)) { + return NULL; + } + + g_byte_array_append(buf, (const guint8 *)name, sizeof(name)); + fu_byte_array_append_uint32(buf, fu_firmware_get_offset(img), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, fu_firmware_get_size(img), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ + } + + /* add entry data */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = NULL; + blob = fu_firmware_get_bytes(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_ifwi_cpd_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuIfwiCpdFirmware *self = FU_IFWI_CPD_FIRMWARE(firmware); + FuIfwiCpdFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "header_version", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT8, error)) + return FALSE; + priv->header_version = val; + } + tmp = xb_node_query_text(n, "entry_version", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT8, error)) + return FALSE; + priv->entry_version = val; + } + + /* success */ + return TRUE; +} + +static void +fu_ifwi_cpd_firmware_init(FuIfwiCpdFirmware *self) +{ +} + +static void +fu_ifwi_cpd_firmware_class_init(FuIfwiCpdFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_ifwi_cpd_firmware_check_magic; + klass_firmware->export = fu_ifwi_cpd_firmware_export; + klass_firmware->parse = fu_ifwi_cpd_firmware_parse; + klass_firmware->write = fu_ifwi_cpd_firmware_write; + klass_firmware->build = fu_ifwi_cpd_firmware_build; +} + +/** + * fu_ifwi_cpd_firmware_new: + * + * Creates a new #FuFirmware of Intel Code Partition Directory format + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_ifwi_cpd_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_IFWI_CPD_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifwi-cpd-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-cpd-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..98211cccb06202131ec3702d2472f33389c344f6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-cpd-firmware.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Intel + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_IFWI_CPD_FIRMWARE (fu_ifwi_cpd_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIfwiCpdFirmware, fu_ifwi_cpd_firmware, FU, IFWI_CPD_FIRMWARE, FuFirmware) + +struct _FuIfwiCpdFirmwareClass { + FuFirmwareClass parent_class; +}; + +/** + * FU_IFWI_CPD_FIRMWARE_IDX_MANIFEST: + * + * The index for the IFWI manifest image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_CPD_FIRMWARE_IDX_MANIFEST 0x0 + +/** + * FU_IFWI_CPD_FIRMWARE_IDX_METADATA: + * + * The index for the IFWI metadata image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_CPD_FIRMWARE_IDX_METADATA 0x1 + +/** + * FU_IFWI_CPD_FIRMWARE_IDX_MODULEDATA_IDX: + * + * The index for the IFWI module data image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_CPD_FIRMWARE_IDX_MODULEDATA_IDX 0x2 + +FuFirmware * +fu_ifwi_cpd_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifwi-fpt-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-fpt-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..e6c742672ef5893104358db415045b4bcc57394e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-fpt-firmware.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Intel + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-ifwi-fpt-firmware.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * FuIfwiFptFirmware: + * + * An Intel Flash Program Tool (aka FPT) header can be found in IFWI (Integrated Firmware Image) + * firmware blobs which are used in various Intel products using an IPU (Infrastructure Processing + * Unit). + * + * This could include hardware like SmartNICs, GPUs, camera and audio devices. + * + * See also: [class@FuFirmware] + */ + +G_DEFINE_TYPE(FuIfwiFptFirmware, fu_ifwi_fpt_firmware, FU_TYPE_FIRMWARE) + +#define FU_IFWI_FPT_HEADER_MARKER 0x54504624 /* "$FPT" */ +#define FU_IFWI_FPT_HEADER_VERSION 0x20 +#define FU_IFWI_FPT_ENTRY_VERSION 0x10 +#define FU_IFWI_FPT_MAX_ENTRIES 56 + +typedef struct __attribute__((packed)) { + guint32 header_marker; + guint32 num_of_entries; + guint8 header_version; + guint8 entry_version; + guint8 header_length; /* bytes */ + guint8 flags; + guint16 ticks_to_add; + guint16 tokens_to_add; + guint32 uma_size; + guint32 crc32; + guint16 fitc_major; + guint16 fitc_minor; + guint16 fitc_hotfix; + guint16 fitc_build; +} FuIfwiFptHeader; + +typedef struct __attribute__((packed)) { + guint32 partition_name; + guint8 reserved1[4]; + guint32 offset; + guint32 length; /* bytes */ + guint8 reserved2[12]; + guint32 partition_type; /* 0 for code, 1 for data, 2 for GLUT */ +} FuIfwiFptEntry; + +static gboolean +fu_ifwi_fpt_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + G_STRUCT_OFFSET(FuIfwiFptHeader, header_marker), + &magic, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != FU_IFWI_FPT_HEADER_MARKER) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid FPT header marker 0x%x, expected 0x%x", + magic, + (guint)FU_IFWI_FPT_HEADER_MARKER); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ifwi_fpt_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + guint32 num_of_entries = 0; + guint8 header_length = 0; + guint8 header_version = 0; + guint8 entry_version = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* sanity check */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiFptHeader, num_of_entries), + &num_of_entries, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (num_of_entries > FU_IFWI_FPT_MAX_ENTRIES) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid FPT number of entries %u", + num_of_entries); + return FALSE; + } + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiFptHeader, header_version), + &header_version, + error)) + return FALSE; + if (header_version < FU_IFWI_FPT_HEADER_VERSION) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid FPT header version: 0x%x", + header_version); + return FALSE; + } + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiFptHeader, entry_version), + &entry_version, + error)) + return FALSE; + if (entry_version != FU_IFWI_FPT_ENTRY_VERSION) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid FPT entry version: 0x%x, expected 0x%x", + entry_version, + (guint)FU_IFWI_FPT_ENTRY_VERSION); + return FALSE; + } + + /* offset by header length */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiFptHeader, header_length), + &header_length, + error)) + return FALSE; + offset += header_length; + + /* read out entries */ + for (guint i = 0; i < num_of_entries; i++) { + guint32 data_length = 0; + guint32 data_offset = 0; + guint32 partition_name = 0; + g_autofree gchar *id = NULL; + g_autoptr(FuFirmware) img = fu_firmware_new(); + + /* read IDX */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + + G_STRUCT_OFFSET(FuIfwiFptEntry, partition_name), + &partition_name, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_firmware_set_idx(img, partition_name); + + /* convert to text form for conveneience */ + id = fu_strsafe((const gchar *)&partition_name, sizeof(partition_name)); + if (id != NULL) + fu_firmware_set_id(img, id); + + /* read offset and length */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiFptEntry, offset), + &data_offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuIfwiFptEntry, length), + &data_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* get data at offset using zero-copy */ + if (data_length != 0x0) { + g_autoptr(GBytes) blob = NULL; + blob = fu_bytes_new_offset(fw, data_offset, data_length, error); + if (blob == NULL) + return FALSE; + fu_firmware_set_bytes(img, blob); + fu_firmware_set_offset(img, data_offset); + } + fu_firmware_add_image(firmware, img); + + /* next */ + offset += sizeof(FuIfwiFptEntry); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_ifwi_fpt_firmware_write(FuFirmware *firmware, GError **error) +{ + gsize offset = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + /* fixup the image offsets */ + offset += sizeof(FuIfwiFptHeader); + offset += sizeof(FuIfwiFptEntry) * imgs->len; + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = NULL; + blob = fu_firmware_get_bytes(img, error); + if (blob == NULL) { + g_prefix_error(error, "image 0x%x: ", i); + return NULL; + } + fu_firmware_set_offset(img, offset); + offset += g_bytes_get_size(blob); + } + + /* write the header */ + fu_byte_array_append_uint32(buf, FU_IFWI_FPT_HEADER_MARKER, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, imgs->len, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, FU_IFWI_FPT_HEADER_VERSION); + fu_byte_array_append_uint8(buf, FU_IFWI_FPT_ENTRY_VERSION); + fu_byte_array_append_uint8(buf, sizeof(FuIfwiFptHeader)); + fu_byte_array_append_uint8(buf, 0x0); /* flags */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* ticks_to_add */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* tokens_to_add */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* uma_size */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* crc32 */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_major */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_minor */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_hotfix */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_build */ + + /* add entries */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved1 */ + fu_byte_array_append_uint32(buf, fu_firmware_get_offset(img), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, fu_firmware_get_size(img), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved2 */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved2 */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved2 */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* partition_type */ + } + + /* add entry data */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = NULL; + blob = fu_firmware_get_bytes(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_ifwi_fpt_firmware_init(FuIfwiFptFirmware *self) +{ +} + +static void +fu_ifwi_fpt_firmware_class_init(FuIfwiFptFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_ifwi_fpt_firmware_check_magic; + klass_firmware->parse = fu_ifwi_fpt_firmware_parse; + klass_firmware->write = fu_ifwi_fpt_firmware_write; +} + +/** + * fu_ifwi_fpt_firmware_new: + * + * Creates a new #FuFirmware of Intel Flash Program Tool format + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_ifwi_fpt_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_IFWI_FPT_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ifwi-fpt-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-fpt-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..933107d8dac048cc07591a0df6d76bda28058274 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ifwi-fpt-firmware.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Intel + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_IFWI_FPT_FIRMWARE (fu_ifwi_fpt_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIfwiFptFirmware, fu_ifwi_fpt_firmware, FU, IFWI_FPT_FIRMWARE, FuFirmware) + +struct _FuIfwiFptFirmwareClass { + FuFirmwareClass parent_class; +}; + +/** + * FU_IFWI_FPT_FIRMWARE_IDX_INFO: + * + * The index for the IFWI info image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_FPT_FIRMWARE_IDX_INFO 0x4f464e49 + +/** + * FU_IFWI_FPT_FIRMWARE_IDX_FWIM: + * + * The index for the IFWI firmware image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_FPT_FIRMWARE_IDX_FWIM 0x4d495746 + +/** + * FU_IFWI_FPT_FIRMWARE_IDX_IMGI: + * + * The index for the IFWI image instance. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_FPT_FIRMWARE_IDX_IMGI 0x49474d49 + +/** + * FU_IFWI_FPT_FIRMWARE_IDX_SDTA: + * + * The index for the IFWI firmware data image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_FPT_FIRMWARE_IDX_SDTA 0x41544447 + +/** + * FU_IFWI_FPT_FIRMWARE_IDX_CKSM: + * + * The index for the IFWI checksum image. + * + * Since: 1.8.2 + **/ +#define FU_IFWI_FPT_FIRMWARE_IDX_CKSM 0x4d534b43 + +FuFirmware * +fu_ifwi_fpt_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ihex-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-ihex-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..8a2df6df840810d6bdb2c8691de3aab4d5a14d59 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ihex-firmware.c @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-firmware-common.h" +#include "fu-ihex-firmware.h" +#include "fu-mem.h" +#include "fu-string.h" + +/** + * FuIhexFirmware: + * + * A Intel hex (ihex) firmware image. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + GPtrArray *records; + guint8 padding_value; +} FuIhexFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_ihex_firmware_get_instance_private(o)) + +#define FU_IHEX_FIRMWARE_TOKENS_MAX 100000 /* lines */ + +/** + * fu_ihex_firmware_get_records: + * @self: A #FuIhexFirmware + * + * Returns the raw lines from tokenization. + * + * This might be useful if the plugin is expecting the hex file to be a list + * of operations, rather than a simple linear image with filled holes. + * + * Returns: (transfer none) (element-type FuIhexFirmwareRecord): records + * + * Since: 1.3.4 + **/ +GPtrArray * +fu_ihex_firmware_get_records(FuIhexFirmware *self) +{ + FuIhexFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_IHEX_FIRMWARE(self), NULL); + return priv->records; +} + +/** + * fu_ihex_firmware_set_padding_value: + * @self: A #FuIhexFirmware + * @padding_value: the byte used to pad the image + * + * Set the padding value to fill incomplete address ranges. + * + * The default value of zero can be changed to `0xff` if functions like + * fu_bytes_is_empty() are going to be used on subsections of the data. + * + * Since: 1.6.0 + **/ +void +fu_ihex_firmware_set_padding_value(FuIhexFirmware *self, guint8 padding_value) +{ + FuIhexFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_IHEX_FIRMWARE(self)); + priv->padding_value = padding_value; +} + +static void +fu_ihex_firmware_record_free(FuIhexFirmwareRecord *rcd) +{ + g_string_free(rcd->buf, TRUE); + g_byte_array_unref(rcd->data); + g_free(rcd); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIhexFirmwareRecord, fu_ihex_firmware_record_free) + +static FuIhexFirmwareRecord * +fu_ihex_firmware_record_new(guint ln, const gchar *line, FwupdInstallFlags flags, GError **error) +{ + g_autoptr(FuIhexFirmwareRecord) rcd = NULL; + gsize linesz = strlen(line); + guint line_end; + guint16 addr16 = 0; + + /* check starting token */ + if (line[0] != ':') { + g_autofree gchar *strsafe = fu_strsafe(line, 5); + if (strsafe != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token: %s", + strsafe); + return NULL; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token"); + return NULL; + } + + /* length, 16-bit address, type */ + rcd = g_new0(FuIhexFirmwareRecord, 1); + rcd->ln = ln; + rcd->data = g_byte_array_new(); + rcd->buf = g_string_new(line); + if (!fu_firmware_strparse_uint8_safe(line, linesz, 1, &rcd->byte_cnt, error)) + return NULL; + if (!fu_firmware_strparse_uint16_safe(line, linesz, 3, &addr16, error)) + return NULL; + rcd->addr = addr16; + if (!fu_firmware_strparse_uint8_safe(line, linesz, 7, &rcd->record_type, error)) + return NULL; + + /* position of checksum */ + line_end = 9 + rcd->byte_cnt * 2; + if (line_end > (guint)rcd->buf->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "line malformed, length: %u", + line_end); + return NULL; + } + + /* verify checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 checksum = 0; + for (guint i = 1; i < line_end + 2; i += 2) { + guint8 data_tmp = 0; + if (!fu_firmware_strparse_uint8_safe(line, linesz, i, &data_tmp, error)) + return NULL; + checksum += data_tmp; + } + if (checksum != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid checksum (0x%02x)", + checksum); + return NULL; + } + } + + /* add data */ + for (guint i = 9; i < line_end; i += 2) { + guint8 tmp_c = 0; + if (!fu_firmware_strparse_uint8_safe(line, linesz, i, &tmp_c, error)) + return NULL; + fu_byte_array_append_uint8(rcd->data, tmp_c); + } + return g_steal_pointer(&rcd); +} + +static const gchar * +fu_ihex_firmware_record_type_to_string(guint8 record_type) +{ + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_DATA) + return "DATA"; + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EOF) + return "EOF"; + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_SEGMENT) + return "EXTENDED_SEGMENT"; + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_START_SEGMENT) + return "START_SEGMENT"; + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR) + return "EXTENDED_LINEAR"; + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_START_LINEAR) + return "ADDR32"; + if (record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE) + return "SIGNATURE"; + return NULL; +} + +typedef struct { + FuIhexFirmware *self; + FwupdInstallFlags flags; +} FuIhexFirmwareTokenHelper; + +static gboolean +fu_ihex_firmware_tokenize_cb(GString *token, guint token_idx, gpointer user_data, GError **error) +{ + FuIhexFirmwareTokenHelper *helper = (FuIhexFirmwareTokenHelper *)user_data; + FuIhexFirmwarePrivate *priv = GET_PRIVATE(helper->self); + g_autoptr(FuIhexFirmwareRecord) rcd = NULL; + + /* sanity check */ + if (token_idx > FU_IHEX_FIRMWARE_TOKENS_MAX) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file has too many lines"); + return FALSE; + } + + /* remove WIN32 line endings */ + g_strdelimit(token->str, "\r\x1a", '\0'); + token->len = strlen(token->str); + + /* ignore blank lines */ + if (token->len == 0) + return TRUE; + + /* ignore comments */ + if (g_str_has_prefix(token->str, ";")) + return TRUE; + + /* parse record */ + rcd = fu_ihex_firmware_record_new(token_idx + 1, token->str, helper->flags, error); + if (rcd == NULL) { + g_prefix_error(error, "invalid line %u: ", token_idx + 1); + return FALSE; + } + g_ptr_array_add(priv->records, g_steal_pointer(&rcd)); + return TRUE; +} + +static gboolean +fu_ihex_firmware_tokenize(FuFirmware *firmware, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + FuIhexFirmware *self = FU_IHEX_FIRMWARE(firmware); + FuIhexFirmwareTokenHelper helper = {.self = self, .flags = flags}; + return fu_strsplit_full(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + "\n", + fu_ihex_firmware_tokenize_cb, + &helper, + error); +} + +static gboolean +fu_ihex_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuIhexFirmware *self = FU_IHEX_FIRMWARE(firmware); + FuIhexFirmwarePrivate *priv = GET_PRIVATE(self); + gboolean got_eof = FALSE; + gboolean got_sig = FALSE; + guint32 abs_addr = 0x0; + guint32 addr_last = 0x0; + guint32 img_addr = G_MAXUINT32; + guint32 seg_addr = 0x0; + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* parse records */ + for (guint k = 0; k < priv->records->len; k++) { + FuIhexFirmwareRecord *rcd = g_ptr_array_index(priv->records, k); + guint16 addr16 = 0; + guint32 addr = rcd->addr + seg_addr + abs_addr; + guint32 len_hole; + + g_debug("%s:", fu_ihex_firmware_record_type_to_string(rcd->record_type)); + g_debug(" length:\t0x%02x", rcd->data->len); + g_debug(" addr:\t0x%08x", addr); + + /* sanity check */ + if (rcd->record_type != FU_IHEX_FIRMWARE_RECORD_TYPE_EOF && rcd->data->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "record 0x%x had zero size", + k); + return FALSE; + } + + /* process different record types */ + switch (rcd->record_type) { + case FU_IHEX_FIRMWARE_RECORD_TYPE_DATA: + + /* does not make sense */ + if (got_eof) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot process data after EOF"); + return FALSE; + } + if (rcd->data->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot parse invalid data"); + return FALSE; + } + + /* base address for element */ + if (img_addr == G_MAXUINT32) + img_addr = addr; + + /* does not make sense */ + if (addr < addr_last) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid address 0x%x, last was 0x%x on line %u", + (guint)addr, + (guint)addr_last, + rcd->ln); + return FALSE; + } + + /* any holes in the hex record */ + len_hole = addr - addr_last; + if (addr_last > 0 && len_hole > 0x100000) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill on line %u", + (guint)len_hole, + rcd->ln); + return FALSE; + } + if (addr_last > 0x0 && len_hole > 1) { + g_debug("filling address 0x%08x to 0x%08x on line %u", + addr_last + 1, + addr_last + len_hole - 1, + rcd->ln); + for (guint j = 1; j < len_hole; j++) + fu_byte_array_append_uint8(buf, priv->padding_value); + } + addr_last = addr + rcd->data->len - 1; + if (addr_last < addr) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "overflow of address 0x%x on line %u", + (guint)addr, + rcd->ln); + return FALSE; + } + + /* write into buf */ + g_byte_array_append(buf, rcd->data->data, rcd->data->len); + break; + case FU_IHEX_FIRMWARE_RECORD_TYPE_EOF: + if (got_eof) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate EOF, perhaps " + "corrupt file"); + return FALSE; + } + got_eof = TRUE; + break; + case FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR: + if (!fu_memread_uint16_safe(rcd->data->data, + rcd->data->len, + 0x0, + &addr16, + G_BIG_ENDIAN, + error)) + return FALSE; + abs_addr = (guint32)addr16 << 16; + g_debug(" abs_addr:\t0x%02x on line %u", abs_addr, rcd->ln); + break; + case FU_IHEX_FIRMWARE_RECORD_TYPE_START_LINEAR: + if (!fu_memread_uint32_safe(rcd->data->data, + rcd->data->len, + 0x0, + &abs_addr, + G_BIG_ENDIAN, + error)) + return FALSE; + g_debug(" abs_addr:\t0x%08x on line %u", abs_addr, rcd->ln); + break; + case FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_SEGMENT: + if (!fu_memread_uint16_safe(rcd->data->data, + rcd->data->len, + 0x0, + &addr16, + G_BIG_ENDIAN, + error)) + return FALSE; + /* segment base address, so ~1Mb addressable */ + seg_addr = (guint32)addr16 * 16; + g_debug(" seg_addr:\t0x%08x on line %u", seg_addr, rcd->ln); + break; + case FU_IHEX_FIRMWARE_RECORD_TYPE_START_SEGMENT: + /* initial content of the CS:IP registers */ + if (!fu_memread_uint32_safe(rcd->data->data, + rcd->data->len, + 0x0, + &seg_addr, + G_BIG_ENDIAN, + error)) + return FALSE; + g_debug(" seg_addr:\t0x%02x on line %u", seg_addr, rcd->ln); + break; + case FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE: + if (got_sig) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate signature, perhaps " + "corrupt file"); + return FALSE; + } + if (rcd->data->len > 0) { + g_autoptr(GBytes) data_sig = + g_bytes_new(rcd->data->data, rcd->data->len); + g_autoptr(FuFirmware) img_sig = + fu_firmware_new_from_bytes(data_sig); + fu_firmware_set_id(img_sig, FU_FIRMWARE_ID_SIGNATURE); + fu_firmware_add_image(firmware, img_sig); + } + got_sig = TRUE; + break; + default: + /* vendors sneak in nonstandard sections past the EOF */ + if (got_eof) + break; + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid ihex record type %i on line %u", + rcd->record_type, + rcd->ln); + return FALSE; + } + } + + /* no EOF */ + if (!got_eof) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no EOF, perhaps truncated file"); + return FALSE; + } + + /* add single image */ + img_bytes = g_bytes_new(buf->data, buf->len); + if (img_addr != G_MAXUINT32) + fu_firmware_set_addr(firmware, img_addr); + fu_firmware_set_bytes(firmware, img_bytes); + return TRUE; +} + +static void +fu_ihex_firmware_emit_chunk(GString *str, + guint16 address, + guint8 record_type, + const guint8 *data, + gsize sz) +{ + guint8 checksum = 0x00; + g_string_append_printf(str, ":%02X%04X%02X", (guint)sz, (guint)address, (guint)record_type); + for (gsize j = 0; j < sz; j++) + g_string_append_printf(str, "%02X", data[j]); + checksum = (guint8)sz; + checksum += (guint8)((address & 0xff00) >> 8); + checksum += (guint8)(address & 0xff); + checksum += record_type; + for (gsize j = 0; j < sz; j++) + checksum += data[j]; + g_string_append_printf(str, "%02X\n", (guint)(((~checksum) + 0x01) & 0xff)); +} + +static gboolean +fu_ihex_firmware_image_to_string(GBytes *bytes, + guint32 addr, + guint8 record_type, + GString *str, + GError **error) +{ + const guint8 *data; + const guint chunk_size = 16; + gsize len; + guint32 address_offset_last = 0x0; + + /* get number of chunks */ + data = g_bytes_get_data(bytes, &len); + for (gsize i = 0; i < len; i += chunk_size) { + guint32 address_tmp = addr + i; + guint32 address_offset = (address_tmp >> 16) & 0xffff; + gsize chunk_len = MIN(len - i, 16); + + /* need to offset */ + if (address_offset != address_offset_last) { + guint8 buf[2]; + fu_memwrite_uint16(buf, address_offset, G_BIG_ENDIAN); + fu_ihex_firmware_emit_chunk(str, + 0x0, + FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR, + buf, + 2); + address_offset_last = address_offset; + } + address_tmp &= 0xffff; + fu_ihex_firmware_emit_chunk(str, address_tmp, record_type, data + i, chunk_len); + } + return TRUE; +} + +static GBytes * +fu_ihex_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GBytes) fw = NULL; + g_autoptr(FuFirmware) img_sig = NULL; + g_autoptr(GString) str = g_string_new(""); + + /* payload */ + fw = fu_firmware_get_bytes_with_patches(firmware, error); + if (fw == NULL) + return NULL; + if (!fu_ihex_firmware_image_to_string(fw, + fu_firmware_get_addr(firmware), + FU_IHEX_FIRMWARE_RECORD_TYPE_DATA, + str, + error)) + return NULL; + + /* signature */ + img_sig = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_SIGNATURE, NULL); + if (img_sig != NULL) { + g_autoptr(GBytes) img_fw = fu_firmware_get_bytes(img_sig, error); + if (img_fw == NULL) + return NULL; + if (!fu_ihex_firmware_image_to_string(img_fw, + 0, + FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE, + str, + error)) + return NULL; + } + + /* add EOF */ + fu_ihex_firmware_emit_chunk(str, 0x0, FU_IHEX_FIRMWARE_RECORD_TYPE_EOF, NULL, 0); + return g_bytes_new(str->str, str->len); +} + +static void +fu_ihex_firmware_finalize(GObject *object) +{ + FuIhexFirmware *self = FU_IHEX_FIRMWARE(object); + FuIhexFirmwarePrivate *priv = GET_PRIVATE(self); + g_ptr_array_unref(priv->records); + G_OBJECT_CLASS(fu_ihex_firmware_parent_class)->finalize(object); +} + +static void +fu_ihex_firmware_init(FuIhexFirmware *self) +{ + FuIhexFirmwarePrivate *priv = GET_PRIVATE(self); + priv->padding_value = 0x00; /* chosen as we can't write 0xffff to PIC14 */ + priv->records = g_ptr_array_new_with_free_func((GFreeFunc)fu_ihex_firmware_record_free); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_ihex_firmware_class_init(FuIhexFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_ihex_firmware_finalize; + klass_firmware->parse = fu_ihex_firmware_parse; + klass_firmware->tokenize = fu_ihex_firmware_tokenize; + klass_firmware->write = fu_ihex_firmware_write; +} + +/** + * fu_ihex_firmware_new: + * + * Creates a new #FuFirmware of sub type Ihex + * + * Since: 1.3.1 + **/ +FuFirmware * +fu_ihex_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_IHEX_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-ihex-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-ihex-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..29dc3c2373e9b3a28fe1d580455519b59781f8e0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-ihex-firmware.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware) + +struct _FuIhexFirmwareClass { + FuFirmwareClass parent_class; +}; + +/** + * FuIhexFirmwareRecord: + * + * A single Intel HEX record. + **/ +typedef struct { + guint ln; + GString *buf; + guint8 byte_cnt; + guint32 addr; + guint8 record_type; + GByteArray *data; +} FuIhexFirmwareRecord; + +#define FU_IHEX_FIRMWARE_RECORD_TYPE_DATA 0x00 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_EOF 0x01 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_SEGMENT 0x02 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_START_SEGMENT 0x03 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_EXTENDED_LINEAR 0x04 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_START_LINEAR 0x05 +#define FU_IHEX_FIRMWARE_RECORD_TYPE_SIGNATURE 0xfd + +FuFirmware * +fu_ihex_firmware_new(void); +GPtrArray * +fu_ihex_firmware_get_records(FuIhexFirmware *self); +void +fu_ihex_firmware_set_padding_value(FuIhexFirmware *self, guint8 padding_value); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..a9d920f497d27d72e1fd5fe1119d9f88ac9bc2f9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-firmware.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 Dell Inc. + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * Copyright (C) 2017 Intel Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-intel-thunderbolt-firmware.h" +#include "fu-mem.h" + +/** + * FuIntelThunderboltFirmware: + * + * The Non-Volatile-Memory file-format specification. This is what you would find as the update + * payload. + * + * See also: [class@FuFirmware] + */ + +G_DEFINE_TYPE(FuIntelThunderboltFirmware, + fu_intel_thunderbolt_firmware, + FU_TYPE_INTEL_THUNDERBOLT_NVM) + +static gboolean +fu_intel_thunderbolt_nvm_valid_farb_pointer(guint32 pointer) +{ + return pointer != 0 && pointer != 0xFFFFFF; +} + +static gboolean +fu_intel_thunderbolt_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + const guint32 farb_offsets[] = {0x0, 0x1000}; + gboolean valid = FALSE; + guint32 farb_pointer = 0x0; + + /* get header offset */ + for (guint i = 0; i < G_N_ELEMENTS(farb_offsets); i++) { + if (!fu_memread_uint24_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + farb_offsets[i], + &farb_pointer, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (fu_intel_thunderbolt_nvm_valid_farb_pointer(farb_pointer)) { + valid = TRUE; + break; + } + } + if (!valid) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no valid farb pointer found"); + return FALSE; + } + g_debug("detected digital section begins at 0x%x", farb_pointer); + fu_firmware_set_offset(firmware, farb_pointer); + + /* FuIntelThunderboltNvm->parse */ + return FU_FIRMWARE_CLASS(fu_intel_thunderbolt_firmware_parent_class) + ->parse(firmware, fw, offset + farb_pointer, flags, error); +} + +static GBytes * +fu_intel_thunderbolt_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + /* sanity check */ + if (fu_firmware_get_offset(firmware) < 0x08) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "not valid offset"); + return NULL; + } + + /* offset */ + fu_byte_array_append_uint32(buf, fu_firmware_get_offset(firmware), G_LITTLE_ENDIAN); + fu_byte_array_set_size(buf, fu_firmware_get_offset(firmware), 0x00); + + /* FuIntelThunderboltNvm->write */ + blob = + FU_FIRMWARE_CLASS(fu_intel_thunderbolt_firmware_parent_class)->write(firmware, error); + fu_byte_array_append_bytes(buf, blob); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_intel_thunderbolt_firmware_init(FuIntelThunderboltFirmware *self) +{ +} + +static void +fu_intel_thunderbolt_firmware_class_init(FuIntelThunderboltFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_intel_thunderbolt_firmware_parse; + klass_firmware->write = fu_intel_thunderbolt_firmware_write; +} + +/** + * fu_intel_thunderbolt_firmware_new: + * + * Creates a new #FuFirmware of Intel NVM format + * + * Since: 1.8.5 + **/ +FuFirmware * +fu_intel_thunderbolt_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..f01d19efa28dd57bb08068a4eab0f4937681618a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-firmware.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 Dell Inc. + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * Copyright (C) 2017 Intel Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-intel-thunderbolt-nvm.h" + +#define FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE (fu_intel_thunderbolt_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIntelThunderboltFirmware, + fu_intel_thunderbolt_firmware, + FU, + INTEL_THUNDERBOLT_FIRMWARE, + FuIntelThunderboltNvm) + +struct _FuIntelThunderboltFirmwareClass { + FuIntelThunderboltNvmClass parent_class; +}; + +FuFirmware * +fu_intel_thunderbolt_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-nvm.c b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-nvm.c new file mode 100644 index 0000000000000000000000000000000000000000..8f7e3b7a9ff0352d166176ad7f96049c81b001b3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-nvm.c @@ -0,0 +1,979 @@ +/* + * Copyright (C) 2021 Dell Inc. + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * Copyright (C) 2017 Intel Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-intel-thunderbolt-nvm.h" +#include "fu-mem.h" +#include "fu-string.h" +#include "fu-version-common.h" + +/** + * FuIntelThunderboltNvm: + * + * The Non-Volatile-Memory device specification. This is what you would find on the device SPI chip. + * + * See also: [class@FuFirmware] + */ + +typedef enum { + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM, + FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DRAM_UCODE, + FU_INTEL_THUNDERBOLT_NVM_SECTION_LAST +} FuIntelThunderboltNvmSection; + +typedef enum { + FU_INTEL_THUNDERBOLT_NVM_FAMILY_UNKNOWN, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_GR, + FU_INTEL_THUNDERBOLT_NVM_FAMILY_LAST, +} FuIntelThunderboltNvmFamily; + +typedef struct { + guint32 sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_LAST]; + FuIntelThunderboltNvmFamily family; + gboolean is_host; + gboolean is_native; + gboolean has_pd; + guint16 vendor_id; + guint16 device_id; + guint16 model_id; + guint gen; + guint ports; + guint8 flash_size; +} FuIntelThunderboltNvmPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuIntelThunderboltNvm, fu_intel_thunderbolt_nvm, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_intel_thunderbolt_nvm_get_instance_private(o)) + +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS 0x0002 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_UCODE 0x0003 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DEVICE_ID 0x0005 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_VERSION 0x0009 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST 0x0010 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE 0x0045 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_ARC_PARAMS 0x0075 +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE 0x007B +#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DROM 0x010E + +#define FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_VENDOR_ID 0x0010 +#define FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_MODEL_ID 0x0012 + +#define FU_INTEL_THUNDERBOLT_NVM_ARC_PARAMS_OFFSET_PD_POINTER 0x010C + +static const gchar * +fu_intel_thunderbolt_nvm_family_to_string(FuIntelThunderboltNvmFamily family) +{ + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR) + return "falcon-ridge"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR) + return "win-ridge"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR) + return "alpine-ridge"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C) + return "alpine-ridge-c"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR) + return "titan-ridge"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB) + return "bb"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR) + return "maple-ridge"; + if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_GR) + return "goshen-ridge"; + return "unknown"; +} + +static FuIntelThunderboltNvmFamily +fu_intel_thunderbolt_nvm_family_from_string(const gchar *family) +{ + if (g_strcmp0(family, "falcon-ridge") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR; + if (g_strcmp0(family, "win-ridge") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR; + if (g_strcmp0(family, "alpine-ridge") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR; + if (g_strcmp0(family, "alpine-ridge-c") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C; + if (g_strcmp0(family, "titan-ridge") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR; + if (g_strcmp0(family, "bb") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB; + if (g_strcmp0(family, "maple-ridge") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR; + if (g_strcmp0(family, "goshen-ridge") == 0) + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_GR; + return FU_INTEL_THUNDERBOLT_NVM_FAMILY_UNKNOWN; +} + +static const gchar * +fu_intel_thunderbolt_nvm_section_to_string(FuIntelThunderboltNvmSection section) +{ + if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL) + return "digital"; + if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM) + return "drom"; + if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS) + return "arc-params"; + if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_DRAM_UCODE) + return "dram-ucode"; + return "unknown"; +} + +/** + * fu_intel_thunderbolt_nvm_get_vendor_id: + * @self: a #FuFirmware + * + * Gets the vendor ID. + * + * Returns: an integer, or 0x0 for unset + * + * Since: 1.8.5 + **/ +guint16 +fu_intel_thunderbolt_nvm_get_vendor_id(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), G_MAXUINT16); + return priv->vendor_id; +} + +/** + * fu_intel_thunderbolt_nvm_get_device_id: + * @self: a #FuFirmware + * + * Gets the device ID. + * + * Returns: an integer, or 0x0 for unset + * + * Since: 1.8.5 + **/ +guint16 +fu_intel_thunderbolt_nvm_get_device_id(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + return priv->device_id; +} + +/** + * fu_intel_thunderbolt_nvm_is_host: + * @self: a #FuFirmware + * + * Gets if the firmware is designed for a host controller rather than a device. + * + * Returns: %TRUE for controller, %FALSE for device + * + * Since: 1.8.5 + **/ +gboolean +fu_intel_thunderbolt_nvm_is_host(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), FALSE); + return priv->is_host; +} + +/** + * fu_intel_thunderbolt_nvm_is_native: + * @self: a #FuFirmware + * + * Gets if the device is native, i.e. not in recovery mode. + * + * Returns: %TRUE if set + * + * Since: 1.8.5 + **/ +gboolean +fu_intel_thunderbolt_nvm_is_native(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), FALSE); + return priv->is_native; +} + +/** + * fu_intel_thunderbolt_nvm_has_pd: + * @self: a #FuFirmware + * + * Gets if the device has power delivery capability. + * + * Returns: %TRUE if set + * + * Since: 1.8.5 + **/ +gboolean +fu_intel_thunderbolt_nvm_has_pd(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), FALSE); + return priv->has_pd; +} + +/** + * fu_intel_thunderbolt_nvm_get_model_id: + * @self: a #FuFirmware + * + * Gets the model ID. + * + * Returns: an integer, or 0x0 for unset + * + * Since: 1.8.5 + **/ +guint16 +fu_intel_thunderbolt_nvm_get_model_id(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), 0x0); + return priv->model_id; +} + +/** + * fu_intel_thunderbolt_nvm_get_flash_size: + * @self: a #FuFirmware + * + * Gets the flash size. + * + * NOTE: This does not correspond to a size in bytes, or a power of 2 and is only useful for + * comparison between firmware and device. + * + * Returns: an integer, or 0x0 for unset + * + * Since: 1.8.5 + **/ +guint8 +fu_intel_thunderbolt_nvm_get_flash_size(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), 0x0); + return priv->flash_size; +} + +static void +fu_intel_thunderbolt_nvm_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "vendor_id", priv->vendor_id); + fu_xmlb_builder_insert_kx(bn, "device_id", priv->device_id); + fu_xmlb_builder_insert_kx(bn, "model_id", priv->model_id); + fu_xmlb_builder_insert_kv(bn, + "family", + fu_intel_thunderbolt_nvm_family_to_string(priv->family)); + fu_xmlb_builder_insert_kb(bn, "is_host", priv->is_host); + fu_xmlb_builder_insert_kb(bn, "is_native", priv->is_native); + fu_xmlb_builder_insert_kx(bn, "flash_size", priv->flash_size); + fu_xmlb_builder_insert_kx(bn, "generation", priv->gen); + fu_xmlb_builder_insert_kx(bn, "ports", priv->ports); + fu_xmlb_builder_insert_kb(bn, "has_pd", priv->has_pd); + for (guint i = 0; i < FU_INTEL_THUNDERBOLT_NVM_SECTION_LAST; i++) { + if (priv->sections[i] != 0x0) { + g_autofree gchar *tmp = g_strdup_printf("0x%x", priv->sections[i]); + g_autoptr(XbBuilderNode) bc = + xb_builder_node_insert(bn, + "section", + "type", + fu_intel_thunderbolt_nvm_section_to_string(i), + "offset", + tmp, + NULL); + g_assert(bc != NULL); + } + } +} + +static inline gboolean +fu_intel_thunderbolt_nvm_valid_pd_pointer(guint32 pointer) +{ + return pointer != 0 && pointer != 0xFFFFFFFF; +} + +static gboolean +fu_intel_thunderbolt_nvm_read_uint8(FuIntelThunderboltNvm *self, + FuIntelThunderboltNvmSection section, + guint32 offset, + guint8 *value, + GError **error) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_autoptr(GBytes) fw = NULL; + + /* get blob and read */ + fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error); + if (fw == NULL) + return FALSE; + return fu_memread_uint8_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + priv->sections[section] + offset, + value, + error); +} + +static gboolean +fu_intel_thunderbolt_nvm_read_uint16(FuIntelThunderboltNvm *self, + FuIntelThunderboltNvmSection section, + guint32 offset, + guint16 *value, + GError **error) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_autoptr(GBytes) fw = NULL; + + /* get blob and read */ + fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error); + if (fw == NULL) + return FALSE; + return fu_memread_uint16_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + priv->sections[section] + offset, + value, + G_LITTLE_ENDIAN, + error); +} + +static gboolean +fu_intel_thunderbolt_nvm_read_uint32(FuIntelThunderboltNvm *self, + FuIntelThunderboltNvmSection section, + guint32 offset, + guint32 *value, + GError **error) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + g_autoptr(GBytes) fw = NULL; + + /* get blob and read */ + fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error); + if (fw == NULL) + return FALSE; + return fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + priv->sections[section] + offset, + value, + G_LITTLE_ENDIAN, + error); +} + +/* + * Size of ucode sections is uint16 value saved at the start of the section, + * it's in DWORDS (4-bytes) units and it doesn't include itself. We need the + * offset to the next section, so we translate it to bytes and add 2 for the + * size field itself. + * + * offset parameter must be relative to digital section + */ +static gboolean +fu_intel_thunderbolt_nvm_read_ucode_section_len(FuIntelThunderboltNvm *self, + guint32 offset, + guint16 *value, + GError **error) +{ + if (!fu_intel_thunderbolt_nvm_read_uint16(self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + offset, + value, + error)) { + g_prefix_error(error, "failed to read ucode section len: "); + return FALSE; + } + *value *= sizeof(guint32); + *value += sizeof(guint16); + return TRUE; +} + +/* assumes sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL].offset is already set */ +static gboolean +fu_intel_thunderbolt_nvm_read_sections(FuIntelThunderboltNvm *self, GError **error) +{ + guint32 offset; + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + + if (priv->gen >= 3 || priv->gen == 0) { + if (!fu_intel_thunderbolt_nvm_read_uint32( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DROM, + &offset, + error)) + return FALSE; + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM] = + offset + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL]; + + if (!fu_intel_thunderbolt_nvm_read_uint32( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_ARC_PARAMS, + &offset, + error)) + return FALSE; + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS] = + offset + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL]; + } + + if (priv->is_host && priv->gen > 2) { + /* + * To find the DRAM section, we have to jump from section to + * section in a chain of sections. + * available_sections location tells what sections exist at all + * (with a flag per section). + * ee_ucode_start_addr location tells the offset of the first + * section in the list relatively to the digital section start. + * After having the offset of the first section, we have a loop + * over the section list. If the section exists, we read its + * length (2 bytes at section start) and add it to current + * offset to find the start of the next section. Otherwise, we + * already have the next section offset... + */ + const guint8 DRAM_FLAG = 1 << 6; + guint16 ucode_offset; + guint8 available_sections = 0; + + if (!fu_intel_thunderbolt_nvm_read_uint8( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS, + &available_sections, + error)) { + g_prefix_error(error, "failed to read available sections: "); + return FALSE; + } + if (!fu_intel_thunderbolt_nvm_read_uint16( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_UCODE, + &ucode_offset, + error)) { + g_prefix_error(error, "failed to read ucode offset: "); + return FALSE; + } + offset = ucode_offset; + if ((available_sections & DRAM_FLAG) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot find needed FW sections in the FW image file"); + return FALSE; + } + + for (guint8 i = 1; i < DRAM_FLAG; i <<= 1) { + if (available_sections & i) { + if (!fu_intel_thunderbolt_nvm_read_ucode_section_len(self, + offset, + &ucode_offset, + error)) + return FALSE; + offset += ucode_offset; + } + } + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DRAM_UCODE] = + offset + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL]; + } + + return TRUE; +} + +static gboolean +fu_intel_thunderbolt_nvm_missing_needed_drom(FuIntelThunderboltNvm *self) +{ + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + if (priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM] != 0) + return FALSE; + if (priv->is_host && priv->gen < 3) + return FALSE; + return TRUE; +} + +static gboolean +fu_intel_thunderbolt_nvm_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + guint8 tmp = 0; + guint16 version_raw = 0; + struct { + guint16 device_id; + guint gen; + FuIntelThunderboltNvmFamily family; + guint ports; + } hw_info_arr[] = {{0x156D, 2, FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR, 2}, /* FR 4C */ + {0x156B, 2, FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR, 1}, /* FR 2C */ + {0x157E, 2, FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR, 1}, /* WR */ + {0x1578, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, 2}, /* AR 4C */ + {0x1576, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, 1}, /* AR 2C */ + {0x15C0, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, 1}, /* AR LP */ + {0x15D3, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C, 2}, /* AR-C 4C */ + {0x15DA, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C, 1}, /* AR-C 2C */ + {0x15E7, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, 1}, /* TR 2C */ + {0x15EA, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, 2}, /* TR 4C */ + {0x15EF, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, 2}, /* TR 4C device */ + {0x15EE, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB, 0}, /* BB device */ + {0x0B26, 4, FU_INTEL_THUNDERBOLT_NVM_FAMILY_GR, 2}, /* GR USB4 */ + /* Maple ridge devices + * NOTE: These are expected to be flashed via UEFI capsules *not* + * Thunderbolt plugin Flashing via fwupd will require matching kernel + * work. They're left here only for parsing the binaries + */ + {0x1136, 4, FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR, 2}, + {0x1137, 4, FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR, 2}, + {0}}; + g_autofree gchar *version = NULL; + g_autoptr(FuFirmware) img_payload = NULL; + g_autoptr(GBytes) fw_payload = NULL; + + /* add this straight away */ + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL] = offset; + + /* is native */ + if (!fu_intel_thunderbolt_nvm_read_uint8( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE, + &tmp, + error)) { + g_prefix_error(error, "failed to read native: "); + return FALSE; + } + priv->is_native = tmp & 0x20; + + /* we're only reading the first chunk */ + if (g_bytes_get_size(fw) == 0x80) + return TRUE; + + /* host or device */ + if (!fu_intel_thunderbolt_nvm_read_uint8(self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST, + &tmp, + error)) { + g_prefix_error(error, "failed to read is-host: "); + return FALSE; + } + priv->is_host = tmp & (1 << 1); + + /* device ID */ + if (!fu_intel_thunderbolt_nvm_read_uint16(self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DEVICE_ID, + &priv->device_id, + error)) { + g_prefix_error(error, "failed to read device-id: "); + return FALSE; + } + + /* this is best-effort */ + for (guint i = 0; hw_info_arr[i].device_id != 0; i++) { + if (hw_info_arr[i].device_id == priv->device_id) { + priv->family = hw_info_arr[i].family; + priv->gen = hw_info_arr[i].gen; + priv->ports = hw_info_arr[i].ports; + break; + } + } + if (priv->ports == 0 && priv->is_host) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unknown controller: %x", + priv->device_id); + return FALSE; + } + + /* read sections from file */ + if (!fu_intel_thunderbolt_nvm_read_sections(self, error)) + return FALSE; + if (fu_intel_thunderbolt_nvm_missing_needed_drom(self)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cannot find required drom section"); + return FALSE; + } + + /* vendor:model */ + if (priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM] != 0x0) { + if (!fu_intel_thunderbolt_nvm_read_uint16( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM, + FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_VENDOR_ID, + &priv->vendor_id, + error)) { + g_prefix_error(error, "failed to read vendor-id: "); + return FALSE; + } + if (!fu_intel_thunderbolt_nvm_read_uint16( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM, + FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_MODEL_ID, + &priv->model_id, + error)) { + g_prefix_error(error, "failed to read model-id: "); + return FALSE; + } + } + + /* versions */ + switch (priv->family) { + case FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR: + case FU_INTEL_THUNDERBOLT_NVM_FAMILY_GR: + if (!fu_intel_thunderbolt_nvm_read_uint16( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_VERSION, + &version_raw, + error)) { + g_prefix_error(error, "failed to read version: "); + return FALSE; + } + fu_firmware_set_version_raw(FU_FIRMWARE(self), version_raw); + version = fu_version_from_uint16(version_raw, FWUPD_VERSION_FORMAT_BCD); + fu_firmware_set_version(FU_FIRMWARE(self), version); + break; + default: + break; + } + + if (priv->is_host) { + switch (priv->family) { + case FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR: + case FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C: + case FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR: + /* used for comparison between old and new image, not a raw number */ + if (!fu_intel_thunderbolt_nvm_read_uint8( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE, + &tmp, + error)) { + g_prefix_error(error, "failed to read flash size: "); + return FALSE; + } + priv->flash_size = tmp & 0x07; + break; + default: + break; + } + } + + /* we're only reading enough to get the vendor-id and model-id */ + if (offset == 0x0 && + g_bytes_get_size(fw) < priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS]) + return TRUE; + + /* has PD */ + if (priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS] != 0x0) { + guint32 pd_pointer = 0x0; + if (!fu_intel_thunderbolt_nvm_read_uint32( + self, + FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS, + FU_INTEL_THUNDERBOLT_NVM_ARC_PARAMS_OFFSET_PD_POINTER, + &pd_pointer, + error)) { + g_prefix_error(error, "failed to read pd-pointer: "); + return FALSE; + } + priv->has_pd = fu_intel_thunderbolt_nvm_valid_pd_pointer(pd_pointer); + } + + /* as as easy-to-grab payload blob */ + if (offset > 0) { + fw_payload = fu_bytes_new_offset(fw, offset, g_bytes_get_size(fw) - offset, error); + if (fw_payload == NULL) + return FALSE; + } else { + fw_payload = g_bytes_ref(fw); + } + img_payload = fu_firmware_new_from_bytes(fw_payload); + fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD); + fu_firmware_add_image(firmware, img_payload); + + /* success */ + return TRUE; +} + +/* can only write version 3 NVM */ +static GBytes * +fu_intel_thunderbolt_nvm_write(FuFirmware *firmware, GError **error) +{ + FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + guint32 digital_size = 0x120; + guint32 drom_offset = 0 + digital_size; + guint32 drom_size = 0x20; + guint32 arc_param_offset = drom_offset + drom_size; + guint32 arc_param_size = 0x120; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* minimal size */ + fu_byte_array_set_size(buf, arc_param_offset + arc_param_size, 0x0); + + /* digital section */ + if (!fu_memwrite_uint8_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS, + 0x0, + error)) + return NULL; + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_UCODE, + 0x0, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint8_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE, + priv->is_native ? 0x20 : 0x0, + error)) + return NULL; + if (!fu_memwrite_uint8_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST, + priv->is_host ? 0x2 : 0x0, + error)) + return NULL; + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DEVICE_ID, + priv->device_id, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_VERSION, + fu_firmware_get_version_raw(firmware), + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint8_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE, + priv->flash_size, + error)) + return NULL; + + /* drom section */ + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DROM, + drom_offset, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + drom_offset + FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_VENDOR_ID, + priv->vendor_id, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + drom_offset + FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_MODEL_ID, + priv->model_id, + G_LITTLE_ENDIAN, + error)) + return NULL; + + /* ARC param section */ + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_ARC_PARAMS, + arc_param_offset, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + arc_param_offset + + FU_INTEL_THUNDERBOLT_NVM_ARC_PARAMS_OFFSET_PD_POINTER, + priv->has_pd ? 0x1 : 0x0, + G_LITTLE_ENDIAN, + error)) + return NULL; + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_intel_thunderbolt_nvm_check_compatible(FuFirmware *firmware, + FuFirmware *firmware_other, + FwupdInstallFlags flags, + GError **error) +{ + FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); + FuIntelThunderboltNvm *other = FU_INTEL_THUNDERBOLT_NVM(firmware_other); + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + FuIntelThunderboltNvmPrivate *priv_other = GET_PRIVATE(other); + + if (priv->is_host != priv_other->is_host) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect firmware mode, got %s, expected %s", + priv->is_host ? "host" : "device", + priv_other->is_host ? "host" : "device"); + return FALSE; + } + if (priv->vendor_id != priv_other->vendor_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect device vendor, got 0x%04x, expected 0x%04x", + priv->vendor_id, + priv_other->vendor_id); + return FALSE; + } + if (priv->device_id != priv_other->device_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect device type, got 0x%04x, expected 0x%04x", + priv->device_id, + priv_other->device_id); + return FALSE; + } + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if (priv->model_id != priv_other->model_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect device model, got 0x%04x, expected 0x%04x", + priv->model_id, + priv_other->model_id); + return FALSE; + } + /* old firmware has PD but new doesn't (we don't care about other way around) */ + if (priv->has_pd && !priv_other->has_pd) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect PD section"); + return FALSE; + } + if (priv->flash_size != priv_other->flash_size) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect flash size"); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_intel_thunderbolt_nvm_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); + FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "vendor_id", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->vendor_id = val; + } + tmp = xb_node_query_text(n, "device_id", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->device_id = val; + } + tmp = xb_node_query_text(n, "model_id", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->model_id = val; + } + tmp = xb_node_query_text(n, "family", NULL); + if (tmp != NULL) { + priv->family = fu_intel_thunderbolt_nvm_family_from_string(tmp); + if (priv->family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unknown family: %s", + tmp); + return FALSE; + } + } + tmp = xb_node_query_text(n, "flash_size", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, 0x07, error)) + return FALSE; + priv->flash_size = val; + } + tmp = xb_node_query_text(n, "is_host", NULL); + if (tmp != NULL) { + if (!fu_strtobool(tmp, &priv->is_host, error)) + return FALSE; + } + tmp = xb_node_query_text(n, "is_native", NULL); + if (tmp != NULL) { + if (!fu_strtobool(tmp, &priv->is_native, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_intel_thunderbolt_nvm_init(FuIntelThunderboltNvm *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_intel_thunderbolt_nvm_class_init(FuIntelThunderboltNvmClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->export = fu_intel_thunderbolt_nvm_export; + klass_firmware->parse = fu_intel_thunderbolt_nvm_parse; + klass_firmware->write = fu_intel_thunderbolt_nvm_write; + klass_firmware->build = fu_intel_thunderbolt_nvm_build; + klass_firmware->check_compatible = fu_intel_thunderbolt_nvm_check_compatible; +} + +/** + * fu_intel_thunderbolt_nvm_new: + * + * Creates a new #FuFirmware of Intel NVM format + * + * Since: 1.8.5 + **/ +FuFirmware * +fu_intel_thunderbolt_nvm_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_INTEL_THUNDERBOLT_NVM, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-nvm.h b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-nvm.h new file mode 100644 index 0000000000000000000000000000000000000000..fd5f23b1df2db7477e1a88a361eb9752d7c86e4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-intel-thunderbolt-nvm.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 Dell Inc. + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * Copyright (C) 2017 Intel Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_INTEL_THUNDERBOLT_NVM (fu_intel_thunderbolt_nvm_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIntelThunderboltNvm, + fu_intel_thunderbolt_nvm, + FU, + INTEL_THUNDERBOLT_NVM, + FuFirmware) + +struct _FuIntelThunderboltNvmClass { + FuFirmwareClass parent_class; +}; + +guint16 +fu_intel_thunderbolt_nvm_get_vendor_id(FuIntelThunderboltNvm *self); +guint16 +fu_intel_thunderbolt_nvm_get_device_id(FuIntelThunderboltNvm *self); +guint16 +fu_intel_thunderbolt_nvm_get_model_id(FuIntelThunderboltNvm *self); +gboolean +fu_intel_thunderbolt_nvm_is_host(FuIntelThunderboltNvm *self); +gboolean +fu_intel_thunderbolt_nvm_is_native(FuIntelThunderboltNvm *self); +gboolean +fu_intel_thunderbolt_nvm_has_pd(FuIntelThunderboltNvm *self); +guint8 +fu_intel_thunderbolt_nvm_get_flash_size(FuIntelThunderboltNvm *self); + +FuFirmware * +fu_intel_thunderbolt_nvm_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-io-channel.c b/fwupd-1.8.6/libfwupdplugin/fu-io-channel.c new file mode 100644 index 0000000000000000000000000000000000000000..a146fee410bde98021c517e3740897aebd59d2ab --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-io-channel.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuIOChannel" + +#include "config.h" + +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include + +#include "fwupd-error.h" + +#include "fu-common.h" +#include "fu-io-channel.h" + +/** + * FuIOChannel: + * + * A bidirectional IO channel which can be read from and written to. + */ + +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: (nullable): optional return location for an error + * + * 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); + g_return_val_if_fail(error == NULL || *error == NULL, 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: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * 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_byte_array: + * @self: a #FuIOChannel + * @buf: buffer to write + * @timeout_ms: timeout in ms + * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * Writes bytes to the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: %TRUE if all the bytes was written + * + * Since: 1.3.2 + **/ +gboolean +fu_io_channel_write_byte_array(FuIOChannel *self, + GByteArray *buf, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + return fu_io_channel_write_raw(self, buf->data, buf->len, timeout_ms, flags, error); +} + +/** + * fu_io_channel_write_raw: + * @self: a #FuIOChannel + * @data: buffer to write + * @datasz: size of @data + * @timeout_ms: timeout in ms + * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * 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); + g_return_val_if_fail(error == NULL || *error == NULL, 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) { + if (errno == EPROTO) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to write: %s", + strerror(errno)); + return FALSE; + } + 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; + } + if (errno == EPROTO) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to write: %s", + strerror(errno)); + return FALSE; + } + 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: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * 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) +{ + GByteArray *buf = fu_io_channel_read_byte_array(self, max_size, timeout_ms, flags, error); + if (buf == NULL) + return NULL; + return g_byte_array_free_to_bytes(buf); +} + +/** + * fu_io_channel_read_byte_array: + * @self: a #FuIOChannel + * @max_size: maximum size of the returned blob, or -1 for no limit + * @timeout_ms: timeout in ms + * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * Reads bytes from the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: (transfer full): a #GByteArray, or %NULL for error + * + * Since: 1.3.2 + **/ +GByteArray * +fu_io_channel_read_byte_array(FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + GPollFD fds = { + .fd = self->fd, + .events = G_IO_IN | G_IO_PRI | G_IO_ERR, + }; + g_autoptr(GByteArray) buf2 = g_byte_array_new(); + + 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_byte_array_append(buf2, buf, len); + return g_steal_pointer(&buf2); + } + + /* 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_byte_array_append(buf2, buf, len); + + /* check maximum size */ + if (max_size > 0 && buf2->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 (buf2->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_steal_pointer(&buf2); +} + +/** + * fu_io_channel_read_raw: + * @self: a #FuIOChannel + * @buf: (nullable): optional buffer + * @bufsz: size of @buf + * @bytes_read: (out) (nullable): data written to @buf + * @timeout_ms: timeout in ms + * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * 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 + * + * Since: 1.2.2 + **/ +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: (nullable): optional return location for an error + * + * Creates a new object to write and read from. + * + * Returns: a #FuIOChannel + * + * Since: 1.2.2 + **/ +FuIOChannel * +fu_io_channel_new_file(const gchar *filename, GError **error) +{ +#ifdef HAVE_POLL_H + gint fd; + + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + 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); +#else + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return NULL; +#endif +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-io-channel.h b/fwupd-1.8.6/libfwupdplugin/fu-io-channel.h new file mode 100644 index 0000000000000000000000000000000000000000..f93fff4b936887412c9a1c35729c60d52094f63a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-io-channel.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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) G_GNUC_WARN_UNUSED_RESULT; + +gint +fu_io_channel_unix_get_fd(FuIOChannel *self); +gboolean +fu_io_channel_shutdown(FuIOChannel *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_io_channel_write_raw(FuIOChannel *self, + const guint8 *data, + gsize datasz, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_io_channel_read_raw(FuIOChannel *self, + guint8 *buf, + gsize bufsz, + gsize *bytes_read, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_io_channel_write_bytes(FuIOChannel *self, + GBytes *bytes, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_io_channel_write_byte_array(FuIOChannel *self, + GByteArray *buf, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GBytes * +fu_io_channel_read_bytes(FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +GByteArray * +fu_io_channel_read_byte_array(FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-kenv.c b/fwupd-1.8.6/libfwupdplugin/fu-kenv.c new file mode 100644 index 0000000000000000000000000000000000000000..50431ab5e64157bf8c617fdd2348e04a23da4f47 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-kenv.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_KENV_H +#include +#endif + +#include "fwupd-error.h" + +#include "fu-kenv.h" + +/** + * fu_kenv_get_string: + * @key: a kenv key, e.g. `smbios.bios.version` + * @error: (nullable): optional return location for an error + * + * Gets a BSD kernel environment string. This will not work on Linux or + * Windows. + * + * Returns: (transfer full): a string, or %NULL if the @key was not found + * + * Since: 1.6.1 + **/ +gchar * +fu_kenv_get_string(const gchar *key, GError **error) +{ +#ifdef HAVE_KENV_H + gchar buf[128] = {'\0'}; + + g_return_val_if_fail(key != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (kenv(KENV_GET, key, buf, sizeof(buf)) == -1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cannot get kenv request for %s", + key); + return NULL; + } + return g_strndup(buf, sizeof(buf)); +#else + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "kenv not supported"); + return NULL; +#endif +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-kenv.h b/fwupd-1.8.6/libfwupdplugin/fu-kenv.h new file mode 100644 index 0000000000000000000000000000000000000000..d23acc5c5ad30beb92ff12016f618611345959f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-kenv.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gchar * +fu_kenv_get_string(const gchar *key, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-kernel.c b/fwupd-1.8.6/libfwupdplugin/fu-kernel.c new file mode 100644 index 0000000000000000000000000000000000000000..1eafede3bfc8270830f7f515dab1e5b1a3bf4d2f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-kernel.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include +#include +#ifdef HAVE_UTSNAME_H +#include +#endif + +#include "fu-common.h" +#include "fu-kernel.h" +#include "fu-path.h" +#include "fu-string.h" +#include "fu-version-common.h" + +/** + * fu_kernel_locked_down: + * + * Determines if kernel lockdown in effect + * + * Since: 1.8.2 + **/ +gboolean +fu_kernel_locked_down(void) +{ +#ifdef __linux__ + gsize len = 0; + g_autofree gchar *dir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_SECURITY); + g_autofree gchar *fname = g_build_filename(dir, "lockdown", NULL); + g_autofree gchar *data = NULL; + g_auto(GStrv) options = NULL; + + if (!g_file_test(fname, G_FILE_TEST_EXISTS)) + return FALSE; + if (!g_file_get_contents(fname, &data, &len, NULL)) + return FALSE; + if (len < 1) + return FALSE; + options = g_strsplit(data, " ", -1); + for (guint i = 0; options[i] != NULL; i++) { + if (g_strcmp0(options[i], "[none]") == 0) + return FALSE; + } + return TRUE; +#else + return FALSE; +#endif +} + +/** + * fu_kernel_check_version: + * @minimum_kernel: (not nullable): The minimum kernel version to check against + * @error: (nullable): optional return location for an error + * + * Determines if the system is running at least a certain required kernel version + * + * Since: 1.8.2 + **/ +gboolean +fu_kernel_check_version(const gchar *minimum_kernel, GError **error) +{ +#ifdef HAVE_UTSNAME_H + struct utsname name_tmp; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(minimum_kernel != NULL, FALSE); + + memset(&name_tmp, 0, sizeof(struct utsname)); + if (uname(&name_tmp) < 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to read kernel version"); + return FALSE; + } + if (fu_version_compare(name_tmp.release, minimum_kernel, FWUPD_VERSION_FORMAT_TRIPLET) < + 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "kernel %s doesn't meet minimum %s", + name_tmp.release, + minimum_kernel); + return FALSE; + } + + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "platform doesn't support checking for minimum Linux kernel"); + return FALSE; +#endif +} + +/** + * fu_kernel_get_firmware_search_path: + * @error: (nullable): optional return location for an error + * + * Reads the FU_PATH_KIND_FIRMWARE_SEARCH and + * returns its contents + * + * Returns: a pointer to a gchar array + * + * Since: 1.8.2 + **/ +gchar * +fu_kernel_get_firmware_search_path(GError **error) +{ + gsize sz = 0; + g_autofree gchar *sys_fw_search_path = NULL; + g_autofree gchar *contents = NULL; + + sys_fw_search_path = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH); + if (!g_file_get_contents(sys_fw_search_path, &contents, &sz, error)) + return NULL; + + /* sanity check */ + if (contents == NULL || sz == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get firmware search path from %s", + sys_fw_search_path); + return NULL; + } + + /* remove newline character */ + if (contents[sz - 1] == '\n') + contents[sz - 1] = 0; + + g_debug("read firmware search path (%" G_GSIZE_FORMAT "): %s", sz, contents); + + return g_steal_pointer(&contents); +} + +/** + * fu_kernel_set_firmware_search_path: + * @path: NUL-terminated string + * @error: (nullable): optional return location for an error + * + * Writes path to the FU_PATH_KIND_FIRMWARE_SEARCH + * + * Returns: %TRUE if successful + * + * Since: 1.8.2 + **/ +gboolean +fu_kernel_set_firmware_search_path(const gchar *path, GError **error) +{ +#if GLIB_CHECK_VERSION(2, 66, 0) + g_autofree gchar *sys_fw_search_path_prm = NULL; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(strlen(path) < PATH_MAX, FALSE); + + sys_fw_search_path_prm = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH); + + g_debug("writing firmware search path (%" G_GSIZE_FORMAT "): %s", strlen(path), path); + + return g_file_set_contents_full(sys_fw_search_path_prm, + path, + strlen(path), + G_FILE_SET_CONTENTS_NONE, + 0644, + error); +#else + FILE *fd; + gsize res; + g_autofree gchar *sys_fw_search_path_prm = NULL; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(strlen(path) < PATH_MAX, FALSE); + + sys_fw_search_path_prm = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH); + /* g_file_set_contents will try to create backup files in sysfs, so use fopen here */ + fd = fopen(sys_fw_search_path_prm, "w"); + if (fd == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "Failed to open %s: %s", + sys_fw_search_path_prm, + g_strerror(errno)); + return FALSE; + } + + g_debug("writing firmware search path (%" G_GSIZE_FORMAT "): %s", strlen(path), path); + + res = fwrite(path, sizeof(gchar), strlen(path), fd); + + fclose(fd); + + if (res != strlen(path)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "Failed to write firmware search path: %s", + g_strerror(errno)); + return FALSE; + } + + return TRUE; +#endif +} + +/** + * fu_kernel_reset_firmware_search_path: + * @error: (nullable): optional return location for an error + * + * Resets the FU_PATH_KIND_FIRMWARE_SEARCH to an empty string + * + * Returns: %TRUE if successful + * + * Since: 1.8.2 + **/ +gboolean +fu_kernel_reset_firmware_search_path(GError **error) +{ + const gchar *contents = " "; + + return fu_kernel_set_firmware_search_path(contents, error); +} + +/** + * fu_kernel_get_cmdline: + * @error: (nullable): optional return location for an error + * + * Loads all the kernel /proc/cmdline key/values into a hash table. + * + * Returns: (transfer container) (element-type utf8 utf8): keys from the kernel command line + * + * Since: 1.8.5 + **/ +GHashTable * +fu_kernel_get_cmdline(GError **error) +{ +#ifdef __linux__ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(GHashTable) hash = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (!g_file_get_contents("/proc/cmdline", &buf, &bufsz, error)) + return NULL; + hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + if (bufsz > 0) { + g_auto(GStrv) tokens = fu_strsplit(buf, bufsz - 1, " ", -1); + for (guint i = 0; tokens[i] != NULL; i++) { + g_auto(GStrv) kv = NULL; + if (strlen(tokens[i]) == 0) + continue; + kv = g_strsplit(tokens[i], "=", 2); + g_hash_table_insert(hash, g_strdup(kv[0]), g_strdup(kv[1])); + } + } + + /* success */ + return g_steal_pointer(&hash); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "platform does not support getting the kernel cmdline"); + return NULL; +#endif +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-kernel.h b/fwupd-1.8.6/libfwupdplugin/fu-kernel.h new file mode 100644 index 0000000000000000000000000000000000000000..8258f4473d7ed6eabfc8c7ce0f642a107056b819 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-kernel.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gboolean +fu_kernel_locked_down(void); +gboolean +fu_kernel_check_version(const gchar *minimum_kernel, GError **error); +gchar * +fu_kernel_get_firmware_search_path(GError **error); +gboolean +fu_kernel_set_firmware_search_path(const gchar *path, GError **error); +gboolean +fu_kernel_reset_firmware_search_path(GError **error); +GHashTable * +fu_kernel_get_cmdline(GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-linear-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-linear-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..a8c015e0efb2aecf721309070b5052bc8efc1834 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-linear-firmware.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-linear-firmware.h" + +/** + * FuLinearFirmware: + * + * A firmware made up of concatenated blobs of a different firmware type. + * + * NOTE: All the child images will be of the specified `GType`. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + GType image_gtype; +} FuLinearFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuLinearFirmware, fu_linear_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_linear_firmware_get_instance_private(o)) + +enum { PROP_0, PROP_IMAGE_GTYPE, PROP_LAST }; + +static void +fu_linear_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware); + FuLinearFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kv(bn, "image_gtype", g_type_name(priv->image_gtype)); +} + +/** + * fu_linear_firmware_get_image_gtype: + * @self: a #FuLinearFirmware + * + * Gets the image #GType to use when parsing a byte buffer. + * + * Returns: integer + * + * Since: 1.8.2 + **/ +GType +fu_linear_firmware_get_image_gtype(FuLinearFirmware *self) +{ + FuLinearFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_LINEAR_FIRMWARE(self), G_TYPE_INVALID); + return priv->image_gtype; +} + +static gboolean +fu_linear_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware); + FuLinearFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "image_gtype", NULL); + if (tmp != NULL) { + priv->image_gtype = g_type_from_name(tmp); + if (priv->image_gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not registered", + tmp); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_linear_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware); + FuLinearFirmwarePrivate *priv = GET_PRIVATE(self); + gsize bufsz = g_bytes_get_size(fw); + + while (offset < bufsz) { + g_autoptr(FuFirmware) img = g_object_new(priv->image_gtype, NULL); + g_autoptr(GBytes) fw_tmp = NULL; + + fw_tmp = fu_bytes_new_offset(fw, offset, bufsz - offset, error); + if (fw_tmp == NULL) + return FALSE; + if (!fu_firmware_parse(img, fw_tmp, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) { + g_prefix_error(error, "failed to parse at 0x%x: ", (guint)offset); + return FALSE; + } + fu_firmware_set_offset(firmware, offset); + fu_firmware_add_image(firmware, img); + + /* next! */ + offset += fu_firmware_get_size(img); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_linear_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* add each file */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = NULL; + fu_firmware_set_offset(img, buf->len); + blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_linear_firmware_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuLinearFirmware *self = FU_LINEAR_FIRMWARE(object); + FuLinearFirmwarePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_IMAGE_GTYPE: + g_value_set_gtype(value, priv->image_gtype); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_linear_firmware_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + FuLinearFirmware *self = FU_LINEAR_FIRMWARE(object); + FuLinearFirmwarePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_IMAGE_GTYPE: + priv->image_gtype = g_value_get_gtype(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_linear_firmware_init(FuLinearFirmware *self) +{ +} + +static void +fu_linear_firmware_class_init(FuLinearFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->get_property = fu_linear_firmware_get_property; + object_class->set_property = fu_linear_firmware_set_property; + klass_firmware->parse = fu_linear_firmware_parse; + klass_firmware->write = fu_linear_firmware_write; + klass_firmware->export = fu_linear_firmware_export; + klass_firmware->build = fu_linear_firmware_build; + + /** + * FuLinearFirmware:image-gtype: + * + * The image #GType + * + * Since: 1.8.2 + */ + pspec = + g_param_spec_gtype("image-gtype", + NULL, + NULL, + FU_TYPE_FIRMWARE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_IMAGE_GTYPE, pspec); +} + +/** + * fu_linear_firmware_new: + * @image_gtype: a #GType, e.g. %FU_TYPE_OPROM_FIRMWARE + * + * Creates a new #FuFirmware made up of concatenated images. + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_linear_firmware_new(GType image_gtype) +{ + return g_object_new(FU_TYPE_LINEAR_FIRMWARE, "image-gtype", image_gtype, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-linear-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-linear-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..a8b0020f136f2927718664c919eaa390b5b6330c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-linear-firmware.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_LINEAR_FIRMWARE (fu_linear_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuLinearFirmware, fu_linear_firmware, FU, LINEAR_FIRMWARE, FuFirmware) + +struct _FuLinearFirmwareClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_linear_firmware_new(GType image_gtype); +GType +fu_linear_firmware_get_image_gtype(FuLinearFirmware *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-mei-device.c b/fwupd-1.8.6/libfwupdplugin/fu-mei-device.c new file mode 100644 index 0000000000000000000000000000000000000000..892d3cc10351bbb15132a644fc5f50982a7f9990 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-mei-device.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMeiDevice" + +#include "config.h" + +#include + +#ifdef HAVE_MEI_H +#include +#endif +#ifdef HAVE_IOCTL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_SELECT_H +#include +#endif + +#include +#include +#include + +#include "fu-dump.h" +#include "fu-mei-device.h" +#include "fu-string.h" + +#define FU_MEI_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +/** + * FuMeiDevice + * + * The Intel proprietary Management Engine Interface. + * + * See also: #FuUdevDevice + */ + +typedef struct { + guint32 max_msg_length; + guint8 protocol_version; +} FuMeiDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuMeiDevice, fu_mei_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_mei_device_get_instance_private(o)) + +static void +fu_mei_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuMeiDevice *self = FU_MEI_DEVICE(device); + FuMeiDevicePrivate *priv = GET_PRIVATE(self); + FU_DEVICE_CLASS(fu_mei_device_parent_class)->to_string(device, idt, str); + if (priv->max_msg_length > 0x0) + fu_string_append_kx(str, idt, "MaxMsgLength", priv->max_msg_length); + if (priv->protocol_version > 0x0) + fu_string_append_kx(str, idt, "ProtocolVer", priv->protocol_version); +} + +static gboolean +fu_mei_device_probe(FuDevice *device, GError **error) +{ + /* only open the main device */ + if (fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)) == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no device"); + return FALSE; + } + + /* FuUdevDevice->probe */ + if (!FU_DEVICE_CLASS(fu_mei_device_parent_class)->probe(device, error)) + return FALSE; + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error); +} + +/** + * fu_mei_device_get_max_msg_length: + * @self: a #FuMeiDevice + * + * Gets the maximum message length. + * + * Returns: integer + * + * Since: 1.8.2 + **/ +guint32 +fu_mei_device_get_max_msg_length(FuMeiDevice *self) +{ + FuMeiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_MEI_DEVICE(self), G_MAXUINT32); + return priv->max_msg_length; +} + +/** + * fu_mei_device_get_protocol_version: + * @self: a #FuMeiDevice + * + * Gets the protocol version, or 0x for unset. + * + * Returns: integer + * + * Since: 1.8.2 + **/ +guint8 +fu_mei_device_get_protocol_version(FuMeiDevice *self) +{ + FuMeiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_MEI_DEVICE(self), G_MAXUINT8); + return priv->protocol_version; +} + +/** + * fu_mei_device_connect: + * @self: a #FuMeiDevice + * @guid: A GUID, e.g. "2800f812-b7b4-2d4b-aca8-46e0ff65814c" + * @req_protocol_version: required protocol version, or 0 + * @error: (nullable): optional return location for an error + * + * Connects to the MEI device for a specific GUID. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_mei_device_connect(FuMeiDevice *self, + const gchar *guid, + guchar req_protocol_version, + GError **error) +{ +#ifdef HAVE_MEI_H + FuMeiDevicePrivate *priv = GET_PRIVATE(self); + fwupd_guid_t guid_le = {0x0}; + struct mei_client *cl; + struct mei_connect_client_data data = {0x0}; + + g_return_val_if_fail(FU_IS_MEI_DEVICE(self), FALSE); + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fwupd_guid_from_string(guid, &guid_le, FWUPD_GUID_FLAG_NONE, error)) + return FALSE; + memcpy(&data.in_client_uuid, &guid_le, sizeof(guid_le)); + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + IOCTL_MEI_CONNECT_CLIENT, + (guint8 *)&data, + NULL, /* rc */ + FU_MEI_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + + cl = &data.out_client_properties; + if (req_protocol_version > 0 && cl->protocol_version != req_protocol_version) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Intel MEI protocol version not supported %i", + cl->protocol_version); + return FALSE; + } + + /* success */ + priv->max_msg_length = cl->max_msg_length; + priv->protocol_version = cl->protocol_version; + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "linux/mei.h not supported"); + return FALSE; +#endif +} + +/** + * fu_mei_device_read: + * @self: a #FuMeiDevice + * @buf: (out): data + * @bufsz: size of @data + * @bytes_read: (nullable): bytes read + * @timeout_ms: timeout + * @error: (nullable): optional return location for an error + * + * Read raw bytes from the device. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_mei_device_read(FuMeiDevice *self, + guint8 *buf, + gsize bufsz, + gsize *bytes_read, + guint timeout_ms, + GError **error) +{ + gssize rc; + + g_return_val_if_fail(FU_IS_MEI_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + rc = read(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), buf, bufsz); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "read failed %u: %s", + (guint)rc, + strerror(errno)); + return FALSE; + } + if (g_getenv("FU_MEI_DEVICE_DEBUG") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "read", buf, rc); + if (bytes_read != NULL) + *bytes_read = (gsize)rc; + return TRUE; +} + +/** + * fu_mei_device_write: + * @self: a #FuMeiDevice + * @buf: (out): data + * @bufsz: size of @data + * @timeout_ms: timeout + * @error: (nullable): optional return location for an error + * + * Write raw bytes to the device. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_mei_device_write(FuMeiDevice *self, + const guint8 *buf, + gsize bufsz, + guint timeout_ms, + GError **error) +{ +#ifdef HAVE_SELECT_H + struct timeval tv; + gssize written; + gssize rc; + fd_set set; + guint fd = fu_udev_device_get_fd(FU_UDEV_DEVICE(self)); + + g_return_val_if_fail(FU_IS_MEI_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000000; + + if (g_getenv("FU_MEI_DEVICE_DEBUG") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "write", buf, bufsz); + written = write(fd, buf, bufsz); + if (written < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "write failed with status %" G_GSSIZE_FORMAT " %s", + written, + strerror(errno)); + return FALSE; + } + if ((gsize)written != bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "only wrote %" G_GSSIZE_FORMAT " of %" G_GSIZE_FORMAT, + written, + bufsz); + return FALSE; + } + + FD_ZERO(&set); + FD_SET(fd, &set); + rc = select(fd + 1, &set, NULL, NULL, &tv); + if (rc > 0 && FD_ISSET(fd, &set)) + return TRUE; + + /* timed out */ + if (rc == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "write failed on timeout with status"); + return FALSE; + } + + /* rc < 0 */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "write failed on select with status %" G_GSSIZE_FORMAT, + rc); + return FALSE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "linux/select.h not supported"); + return FALSE; +#endif +} + +static void +fu_mei_device_incorporate(FuDevice *device, FuDevice *donor) +{ + FuMeiDevice *self = FU_MEI_DEVICE(device); + FuMeiDevicePrivate *priv = GET_PRIVATE(self); + FuMeiDevicePrivate *priv_donor = GET_PRIVATE(FU_MEI_DEVICE(donor)); + + g_return_if_fail(FU_IS_MEI_DEVICE(self)); + g_return_if_fail(FU_IS_MEI_DEVICE(donor)); + + /* FuUdevDevice->incorporate */ + FU_DEVICE_CLASS(fu_mei_device_parent_class)->incorporate(device, donor); + + /* copy private instance data */ + priv->max_msg_length = priv_donor->max_msg_length; + priv->protocol_version = priv_donor->protocol_version; +} + +static void +fu_mei_device_init(FuMeiDevice *self) +{ + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE | + FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT); +} + +static void +fu_mei_device_class_init(FuMeiDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_mei_device_probe; + klass_device->to_string = fu_mei_device_to_string; + klass_device->incorporate = fu_mei_device_incorporate; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-mei-device.h b/fwupd-1.8.6/libfwupdplugin/fu-mei-device.h new file mode 100644 index 0000000000000000000000000000000000000000..796446ed6c6aa3cd4415df095105e2abae3f1ec9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-mei-device.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-udev-device.h" + +#define FU_TYPE_MEI_DEVICE (fu_mei_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuMeiDevice, fu_mei_device, FU, MEI_DEVICE, FuUdevDevice) + +struct _FuMeiDeviceClass { + FuUdevDeviceClass parent_class; + gpointer __reserved[31]; +}; + +gboolean +fu_mei_device_connect(FuMeiDevice *self, + const gchar *guid, + guchar req_protocol_version, + GError **error); +gboolean +fu_mei_device_read(FuMeiDevice *self, + guint8 *buf, + gsize bufsz, + gsize *bytes_read, + guint timeout_ms, + GError **error); +gboolean +fu_mei_device_write(FuMeiDevice *self, + const guint8 *buf, + gsize bufsz, + guint timeout_ms, + GError **error); +guint +fu_mei_device_get_max_msg_length(FuMeiDevice *self); +guint8 +fu_mei_device_get_protocol_version(FuMeiDevice *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-mem.c b/fwupd-1.8.6/libfwupdplugin/fu-mem.c new file mode 100644 index 0000000000000000000000000000000000000000..6cb7dceba8dccfb95b58f3fd2c7e6adb26ca400f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-mem.c @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include "fwupd-error.h" + +#include "fu-mem.h" + +/** + * fu_memwrite_uint16: + * @buf: a writable buffer + * @val_native: a value in host byte-order + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Writes a value to a buffer using a specified endian. + * + * Since: 1.8.2 + **/ +void +fu_memwrite_uint16(guint8 *buf, guint16 val_native, FuEndianType endian) +{ + guint16 val_hw; + switch (endian) { + case G_BIG_ENDIAN: + val_hw = GUINT16_TO_BE(val_native); + break; + case G_LITTLE_ENDIAN: + val_hw = GUINT16_TO_LE(val_native); + break; + default: + g_assert_not_reached(); + } + memcpy(buf, &val_hw, sizeof(val_hw)); +} + +/** + * fu_memwrite_uint24: + * @buf: a writable buffer + * @val_native: a value in host byte-order + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Writes a value to a buffer using a specified endian. + * + * Since: 1.8.2 + **/ +void +fu_memwrite_uint24(guint8 *buf, guint32 val_native, FuEndianType endian) +{ + guint32 val_hw; + switch (endian) { + case G_BIG_ENDIAN: + val_hw = GUINT32_TO_BE(val_native); + memcpy(buf, ((const guint8 *)&val_hw) + 0x1, 0x3); + break; + case G_LITTLE_ENDIAN: + val_hw = GUINT32_TO_LE(val_native); + memcpy(buf, &val_hw, 0x3); + break; + default: + g_assert_not_reached(); + } +} + +/** + * fu_memwrite_uint32: + * @buf: a writable buffer + * @val_native: a value in host byte-order + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Writes a value to a buffer using a specified endian. + * + * Since: 1.8.2 + **/ +void +fu_memwrite_uint32(guint8 *buf, guint32 val_native, FuEndianType endian) +{ + guint32 val_hw; + switch (endian) { + case G_BIG_ENDIAN: + val_hw = GUINT32_TO_BE(val_native); + break; + case G_LITTLE_ENDIAN: + val_hw = GUINT32_TO_LE(val_native); + break; + default: + g_assert_not_reached(); + } + memcpy(buf, &val_hw, sizeof(val_hw)); +} + +/** + * fu_memwrite_uint64: + * @buf: a writable buffer + * @val_native: a value in host byte-order + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Writes a value to a buffer using a specified endian. + * + * Since: 1.8.2 + **/ +void +fu_memwrite_uint64(guint8 *buf, guint64 val_native, FuEndianType endian) +{ + guint64 val_hw; + switch (endian) { + case G_BIG_ENDIAN: + val_hw = GUINT64_TO_BE(val_native); + break; + case G_LITTLE_ENDIAN: + val_hw = GUINT64_TO_LE(val_native); + break; + default: + g_assert_not_reached(); + } + memcpy(buf, &val_hw, sizeof(val_hw)); +} + +/** + * fu_memread_uint16: + * @buf: a readable buffer + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Read a value from a buffer using a specified endian. + * + * Returns: a value in host byte-order + * + * Since: 1.8.2 + **/ +guint16 +fu_memread_uint16(const guint8 *buf, FuEndianType endian) +{ + guint16 val_hw, val_native; + memcpy(&val_hw, buf, sizeof(val_hw)); + switch (endian) { + case G_BIG_ENDIAN: + val_native = GUINT16_FROM_BE(val_hw); + break; + case G_LITTLE_ENDIAN: + val_native = GUINT16_FROM_LE(val_hw); + break; + default: + g_assert_not_reached(); + } + return val_native; +} + +/** + * fu_memread_uint24: + * @buf: a readable buffer + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Read a value from a buffer using a specified endian. + * + * Returns: a value in host byte-order + * + * Since: 1.8.2 + **/ +guint32 +fu_memread_uint24(const guint8 *buf, FuEndianType endian) +{ + guint32 val_hw = 0; + guint32 val_native; + switch (endian) { + case G_BIG_ENDIAN: + memcpy(((guint8 *)&val_hw) + 0x1, buf, 0x3); + val_native = GUINT32_FROM_BE(val_hw); + break; + case G_LITTLE_ENDIAN: + memcpy(&val_hw, buf, 0x3); + val_native = GUINT32_FROM_LE(val_hw); + break; + default: + g_assert_not_reached(); + } + return val_native; +} + +/** + * fu_memread_uint32: + * @buf: a readable buffer + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Read a value from a buffer using a specified endian. + * + * Returns: a value in host byte-order + * + * Since: 1.8.2 + **/ +guint32 +fu_memread_uint32(const guint8 *buf, FuEndianType endian) +{ + guint32 val_hw, val_native; + memcpy(&val_hw, buf, sizeof(val_hw)); + switch (endian) { + case G_BIG_ENDIAN: + val_native = GUINT32_FROM_BE(val_hw); + break; + case G_LITTLE_ENDIAN: + val_native = GUINT32_FROM_LE(val_hw); + break; + default: + g_assert_not_reached(); + } + return val_native; +} + +/** + * fu_memread_uint64: + * @buf: a readable buffer + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Read a value from a buffer using a specified endian. + * + * Returns: a value in host byte-order + * + * Since: 1.8.2 + **/ +guint64 +fu_memread_uint64(const guint8 *buf, FuEndianType endian) +{ + guint64 val_hw, val_native; + memcpy(&val_hw, buf, sizeof(val_hw)); + switch (endian) { + case G_BIG_ENDIAN: + val_native = GUINT64_FROM_BE(val_hw); + break; + case G_LITTLE_ENDIAN: + val_native = GUINT64_FROM_LE(val_hw); + break; + default: + g_assert_not_reached(); + } + return val_native; +} + +/** + * fu_memcmp_safe: + * @buf1: a buffer + * @bufsz1: sizeof @buf1 + * @buf2: another buffer + * @bufsz2: sizeof @buf2 + * @error: (nullable): optional return location for an error + * + * Compares the buffers for equality. + * + * Returns: %TRUE if @buf1 and @buf2 are identical + * + * Since: 1.8.2 + **/ +gboolean +fu_memcmp_safe(const guint8 *buf1, gsize bufsz1, const guint8 *buf2, gsize bufsz2, GError **error) +{ + g_return_val_if_fail(buf1 != NULL, FALSE); + g_return_val_if_fail(buf2 != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not the same length */ + 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_memcpy_safe: + * @dst: destination buffer + * @dst_sz: maximum size of @dst, typically `sizeof(dst)` + * @dst_offset: offset in bytes into @dst to copy to + * @src: source buffer + * @src_sz: maximum size of @dst, typically `sizeof(src)` + * @src_offset: offset in bytes into @src to copy from + * @n: number of bytes to copy from @src+@offset from + * @error: (nullable): optional return location for an error + * + * Copies some memory using memcpy in a safe way. Providing the buffer sizes + * of both the destination and the source allows us to check for buffer overflow. + * + * Providing the buffer offsets also allows us to check reading past the end of + * the source buffer. For this reason the caller should NEVER add an offset to + * @src or @dst. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if the bytes were copied, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memcpy_safe(guint8 *dst, + gsize dst_sz, + gsize dst_offset, + const guint8 *src, + gsize src_sz, + gsize src_offset, + gsize n, + GError **error) +{ + g_return_val_if_fail(dst != NULL, FALSE); + g_return_val_if_fail(src != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (n == 0) + return TRUE; + + if (n > src_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "attempted to read 0x%02x bytes from buffer of 0x%02x", + (guint)n, + (guint)src_sz); + return FALSE; + } + if (src_offset > src_sz || n + src_offset > src_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x", + (guint)n, + (guint)src_offset, + (guint)src_sz); + return FALSE; + } + if (n > dst_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "attempted to write 0x%02x bytes to buffer of 0x%02x", + (guint)n, + (guint)dst_sz); + return FALSE; + } + if (dst_offset > dst_sz || n + dst_offset > dst_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x", + (guint)n, + (guint)dst_offset, + (guint)dst_sz); + return FALSE; + } + + /* phew! */ + memcpy(dst + dst_offset, src + src_offset, n); + return TRUE; +} + +/** + * fu_memmem_safe: + * @haystack: destination buffer + * @haystack_sz: maximum size of @haystack, typically `sizeof(haystack)` + * @needle: source buffer + * @needle_sz: maximum size of @haystack, typically `sizeof(needle)` + * @offset: (out) (nullable): offset in bytes @needle has been found in @haystack + * @error: (nullable): optional return location for an error + * + * Finds a block of memory in another block of memory in a safe way. + * + * Returns: %TRUE if the needle was found in the haystack, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memmem_safe(const guint8 *haystack, + gsize haystack_sz, + const guint8 *needle, + gsize needle_sz, + gsize *offset, + GError **error) +{ +#ifdef HAVE_MEMMEM + const guint8 *tmp; +#endif + g_return_val_if_fail(haystack != NULL, FALSE); + g_return_val_if_fail(needle != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* nothing to find */ + if (needle_sz == 0) { + if (offset != NULL) + *offset = 0; + return TRUE; + } + + /* impossible */ + if (needle_sz > haystack_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "needle of 0x%02x bytes is larger than haystack of 0x%02x bytes", + (guint)needle_sz, + (guint)haystack_sz); + return FALSE; + } + +#ifdef HAVE_MEMMEM + /* trust glibc to do a binary or linear search as appropriate */ + tmp = memmem(haystack, haystack_sz, needle, needle_sz); + if (tmp != NULL) { + if (offset != NULL) + *offset = tmp - haystack; + return TRUE; + } +#else + for (gsize i = 0; i < haystack_sz - needle_sz; i++) { + if (memcmp(haystack + i, needle, needle_sz) == 0) { + if (offset != NULL) + *offset = i; + return TRUE; + } + } +#endif + + /* not found */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "needle of 0x%02x bytes was not found in haystack of 0x%02x bytes", + (guint)needle_sz, + (guint)haystack_sz); + return FALSE; +} + +/** + * fu_memdup_safe: + * @src: source buffer + * @n: number of bytes to copy from @src + * @error: (nullable): optional return location for an error + * + * Duplicates some memory using memdup in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * NOTE: This function intentionally limits allocation size to 1GB. + * + * Returns: (transfer full): block of allocated memory, or %NULL for an error. + * + * Since: 1.8.2 + **/ +guint8 * +fu_memdup_safe(const guint8 *src, gsize n, GError **error) +{ + /* sanity check */ + if (n > 0x40000000) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot allocate %uGB of memory", + (guint)(n / 0x40000000)); + return NULL; + } + +#if GLIB_CHECK_VERSION(2, 67, 3) + /* linear block of memory */ + return g_memdup2(src, n); +#else + return g_memdup(src, (guint)n); +#endif +} + +/** + * fu_memread_uint8_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to copy from + * @value: (out) (nullable): the parsed value + * @error: (nullable): optional return location for an error + * + * Read a value from a buffer in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was set, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memread_uint8_safe(const guint8 *buf, gsize bufsz, gsize offset, guint8 *value, GError **error) +{ + guint8 tmp; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_memcpy_safe(&tmp, + sizeof(tmp), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(tmp), + error)) + return FALSE; + if (value != NULL) + *value = tmp; + return TRUE; +} + +/** + * fu_memread_uint16_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to copy from + * @value: (out) (nullable): the parsed value + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Read a value from a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was set, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memread_uint16_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint16 *value, + FuEndianType endian, + GError **error) +{ + guint8 dst[2] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_memcpy_safe(dst, + sizeof(dst), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(dst), + error)) + return FALSE; + if (value != NULL) + *value = fu_memread_uint16(dst, endian); + return TRUE; +} + +/** + * fu_memread_uint24_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to copy from + * @value: (out) (nullable): the parsed value + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Read a value from a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was set, %FALSE otherwise + * + * Since: 1.8.3 + **/ +gboolean +fu_memread_uint24_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint32 *value, + FuEndianType endian, + GError **error) +{ + guint8 dst[3] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_memcpy_safe(dst, + sizeof(dst), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(dst), + error)) + return FALSE; + if (value != NULL) + *value = fu_memread_uint24(dst, endian); + return TRUE; +} + +/** + * fu_memread_uint32_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to copy from + * @value: (out) (nullable): the parsed value + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Read a value from a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was set, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memread_uint32_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint32 *value, + FuEndianType endian, + GError **error) +{ + guint8 dst[4] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_memcpy_safe(dst, + sizeof(dst), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(dst), + error)) + return FALSE; + if (value != NULL) + *value = fu_memread_uint32(dst, endian); + return TRUE; +} + +/** + * fu_memread_uint64_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to copy from + * @value: (out) (nullable): the parsed value + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Read a value from a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was set, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memread_uint64_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint64 *value, + FuEndianType endian, + GError **error) +{ + guint8 dst[8] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_memcpy_safe(dst, + sizeof(dst), + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + sizeof(dst), + error)) + return FALSE; + if (value != NULL) + *value = fu_memread_uint64(dst, endian); + return TRUE; +} + +/** + * fu_memwrite_uint8_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to write to + * @value: the value to write + * @error: (nullable): optional return location for an error + * + * Write a value to a buffer in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was written, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memwrite_uint8_safe(guint8 *buf, gsize bufsz, gsize offset, guint8 value, GError **error) +{ + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + return fu_memcpy_safe(buf, + bufsz, + offset, /* dst */ + &value, + sizeof(value), + 0x0, /* src */ + sizeof(value), + error); +} + +/** + * fu_memwrite_uint16_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to write to + * @value: the value to write + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Write a value to a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was written, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memwrite_uint16_safe(guint8 *buf, + gsize bufsz, + gsize offset, + guint16 value, + FuEndianType endian, + GError **error) +{ + guint8 tmp[2] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + fu_memwrite_uint16(tmp, value, endian); + return fu_memcpy_safe(buf, + bufsz, + offset, /* dst */ + tmp, + sizeof(tmp), + 0x0, /* src */ + sizeof(tmp), + error); +} + +/** + * fu_memwrite_uint32_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to write to + * @value: the value to write + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Write a value to a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was written, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memwrite_uint32_safe(guint8 *buf, + gsize bufsz, + gsize offset, + guint32 value, + FuEndianType endian, + GError **error) +{ + guint8 tmp[4] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + fu_memwrite_uint32(tmp, value, endian); + return fu_memcpy_safe(buf, + bufsz, + offset, /* dst */ + tmp, + sizeof(tmp), + 0x0, /* src */ + sizeof(tmp), + error); +} + +/** + * fu_memwrite_uint64_safe: + * @buf: source buffer + * @bufsz: maximum size of @buf, typically `sizeof(buf)` + * @offset: offset in bytes into @buf to write to + * @value: the value to write + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * @error: (nullable): optional return location for an error + * + * Write a value to a buffer using a specified endian in a safe way. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Returns: %TRUE if @value was written, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_memwrite_uint64_safe(guint8 *buf, + gsize bufsz, + gsize offset, + guint64 value, + FuEndianType endian, + GError **error) +{ + guint8 tmp[8] = {0x0}; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + fu_memwrite_uint64(tmp, value, endian); + return fu_memcpy_safe(buf, + bufsz, + offset, /* dst */ + tmp, + sizeof(tmp), + 0x0, /* src */ + sizeof(tmp), + error); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-mem.h b/fwupd-1.8.6/libfwupdplugin/fu-mem.h new file mode 100644 index 0000000000000000000000000000000000000000..d7a5fb2915f184f439d44b7cdd42d3a213ec4de3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-mem.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-common.h" + +gboolean +fu_memcmp_safe(const guint8 *buf1, gsize bufsz1, const guint8 *buf2, gsize bufsz2, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +guint8 * +fu_memdup_safe(const guint8 *src, gsize n, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memcpy_safe(guint8 *dst, + gsize dst_sz, + gsize dst_offset, + const guint8 *src, + gsize src_sz, + gsize src_offset, + gsize n, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memmem_safe(const guint8 *haystack, + gsize haystack_sz, + const guint8 *needle, + gsize needle_sz, + gsize *offset, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memread_uint8_safe(const guint8 *buf, gsize bufsz, gsize offset, guint8 *value, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memread_uint16_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint16 *value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memread_uint24_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint32 *value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memread_uint32_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint32 *value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memread_uint64_safe(const guint8 *buf, + gsize bufsz, + gsize offset, + guint64 *value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memwrite_uint8_safe(guint8 *buf, gsize bufsz, gsize offset, guint8 value, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memwrite_uint16_safe(guint8 *buf, + gsize bufsz, + gsize offset, + guint16 value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memwrite_uint32_safe(guint8 *buf, + gsize bufsz, + gsize offset, + guint32 value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_memwrite_uint64_safe(guint8 *buf, + gsize bufsz, + gsize offset, + guint64 value, + FuEndianType endian, + GError **error) G_GNUC_WARN_UNUSED_RESULT; + +void +fu_memwrite_uint16(guint8 *buf, guint16 val_native, FuEndianType endian); +void +fu_memwrite_uint24(guint8 *buf, guint32 val_native, FuEndianType endian); +void +fu_memwrite_uint32(guint8 *buf, guint32 val_native, FuEndianType endian); +void +fu_memwrite_uint64(guint8 *buf, guint64 val_native, FuEndianType endian); +guint16 +fu_memread_uint16(const guint8 *buf, FuEndianType endian); +guint32 +fu_memread_uint24(const guint8 *buf, FuEndianType endian); +guint32 +fu_memread_uint32(const guint8 *buf, FuEndianType endian); +guint64 +fu_memread_uint64(const guint8 *buf, FuEndianType endian); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-mutex.h b/fwupd-1.8.6/libfwupdplugin/fu-mutex.h new file mode 100644 index 0000000000000000000000000000000000000000..20a745561f85aa3a7adb4d77d258937bbe1c195c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-mutex.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2019 Kalev Lember + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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 diff --git a/fwupd-1.8.6/libfwupdplugin/fu-oprom-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-oprom-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..18fde8133b041f4da3a51c7e05fe9124b2a366cc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-oprom-firmware.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Intel + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-ifwi-cpd-firmware.h" +#include "fu-mem.h" +#include "fu-oprom-firmware.h" +#include "fu-string.h" + +/** + * FuOpromFirmware: + * + * An OptionROM can be found in nearly every PCI device. Multiple OptionROM images may be appended. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint16 machine_type; + guint16 subsystem; + guint16 compression_type; +} FuOpromFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuOpromFirmware, fu_oprom_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_oprom_firmware_get_instance_private(o)) + +#define FU_OPROM_HEADER_SIGNATURE 0xAA55 +#define FU_OPROM_FIRMWARE_PCI_DATA_SIGNATURE 0x52494350 /* "PCIR" */ +#define FU_OPROM_FIRMWARE_ALIGN_LEN 512u +#define FU_OPROM_FIRMWARE_LAST_IMAGE_INDICATOR_BIT (1u << 7) + +typedef struct __attribute__((packed)) { + guint16 signature; + guint16 image_size; /* of 512 bytes */ + guint32 init_func_entry_point; + guint16 subsystem; + guint16 machine_type; + guint16 compression_type; + guint8 reserved[8]; + guint16 efi_image_offset; + guint16 pci_header_offset; + guint16 expansion_header_offset; +} FuOpromFirmwareHeader2; + +typedef struct __attribute__((packed)) { + guint32 signature; + guint16 vendor_id; + guint16 device_id; + guint16 device_list_pointer; + guint16 structure_length; + guint8 structure_revision; + guint8 class_code[3]; + guint16 image_length; /* of 512 bytes */ + guint16 image_revision; + guint8 code_type; + guint8 indicator; + guint16 max_runtime_image_length; + guint16 conf_util_code_header_pointer; + guint16 dmtf_clp_entry_point_pointer; +} FuOpromFirmwarePciData; + +/** + * fu_oprom_firmware_get_machine_type: + * @self: a #FuFirmware + * + * Gets the machine type. + * + * Returns: an integer + * + * Since: 1.8.2 + **/ +guint16 +fu_oprom_firmware_get_machine_type(FuOpromFirmware *self) +{ + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_OPROM_FIRMWARE(self), G_MAXUINT16); + return priv->machine_type; +} + +/** + * fu_oprom_firmware_get_subsystem: + * @self: a #FuFirmware + * + * Gets the machine type. + * + * Returns: an integer + * + * Since: 1.8.2 + **/ +guint16 +fu_oprom_firmware_get_subsystem(FuOpromFirmware *self) +{ + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_OPROM_FIRMWARE(self), G_MAXUINT16); + return priv->subsystem; +} + +/** + * fu_oprom_firmware_get_compression_type: + * @self: a #FuFirmware + * + * Gets the machine type. + * + * Returns: an integer + * + * Since: 1.8.2 + **/ +guint16 +fu_oprom_firmware_get_compression_type(FuOpromFirmware *self) +{ + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_OPROM_FIRMWARE(self), G_MAXUINT16); + return priv->compression_type; +} + +static void +fu_oprom_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware); + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "machine_type", priv->machine_type); + fu_xmlb_builder_insert_kx(bn, "subsystem", priv->subsystem); + fu_xmlb_builder_insert_kx(bn, "compression_type", priv->compression_type); +} + +static gboolean +fu_oprom_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + gsize bufsz = 0; + guint16 signature = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuOpromFirmwareHeader2, signature), + &signature, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (signature != FU_OPROM_HEADER_SIGNATURE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid ROM signature, got 0x%x, expected 0x%x", + signature, + (guint)FU_OPROM_HEADER_SIGNATURE); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_oprom_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware); + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + guint16 expansion_header_offset = 0; + guint16 pci_header_offset = 0; + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + guint16 image_length = 0; + guint32 pci_signature = 0; + guint8 code_type = 0; + + /* parse header */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuOpromFirmwareHeader2, subsystem), + &priv->subsystem, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + + G_STRUCT_OFFSET(FuOpromFirmwareHeader2, compression_type), + &priv->compression_type, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuOpromFirmwareHeader2, machine_type), + &priv->machine_type, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* get PCI offset */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + + G_STRUCT_OFFSET(FuOpromFirmwareHeader2, pci_header_offset), + &pci_header_offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (pci_header_offset == 0x0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no PCI data structure offset provided"); + return FALSE; + } + + /* verify signature */ + offset += pci_header_offset; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuOpromFirmwarePciData, signature), + &pci_signature, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (pci_signature != FU_OPROM_FIRMWARE_PCI_DATA_SIGNATURE) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid PCI signature, got 0x%x, expected 0x%x", + pci_signature, + (guint)FU_OPROM_FIRMWARE_PCI_DATA_SIGNATURE); + return FALSE; + } + + /* get length */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuOpromFirmwarePciData, image_length), + &image_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (image_length == 0x0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "invalid image length"); + return FALSE; + } + fu_firmware_set_size(firmware, image_length * FU_OPROM_FIRMWARE_ALIGN_LEN); + + /* get code type */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuOpromFirmwarePciData, code_type), + &code_type, + error)) + return FALSE; + fu_firmware_set_idx(firmware, code_type); + + /* get CPD offset */ + if (!fu_memread_uint16_safe( + buf, + bufsz, + G_STRUCT_OFFSET(FuOpromFirmwareHeader2, expansion_header_offset), + &expansion_header_offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (expansion_header_offset != 0x0) { + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) blob = NULL; + + blob = fu_bytes_new_offset(fw, + expansion_header_offset, + bufsz - expansion_header_offset, + error); + if (blob == NULL) { + g_prefix_error(error, "failed to section CPD: "); + return FALSE; + } + + img = fu_firmware_new_from_gtypes(blob, + FWUPD_INSTALL_FLAG_NONE, + error, + FU_TYPE_IFWI_CPD_FIRMWARE, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); + if (img == NULL) { + g_prefix_error(error, "failed to build firmware: "); + return FALSE; + } + fu_firmware_set_id(img, "cpd"); + fu_firmware_set_offset(img, expansion_header_offset); + fu_firmware_add_image(firmware, img); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_oprom_firmware_write(FuFirmware *firmware, GError **error) +{ + FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware); + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + gsize image_size = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob_cpd = NULL; + + /* the smallest each image (and header) can be is 512 bytes */ + image_size += fu_common_align_up(sizeof(FuOpromFirmwareHeader2), FU_FIRMWARE_ALIGNMENT_512); + blob_cpd = fu_firmware_get_image_by_id_bytes(firmware, "cpd", NULL); + if (blob_cpd != NULL) { + image_size += + fu_common_align_up(g_bytes_get_size(blob_cpd), FU_FIRMWARE_ALIGNMENT_512); + } + + /* write the header */ + fu_byte_array_append_uint16(buf, FU_OPROM_HEADER_SIGNATURE, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, image_size / FU_OPROM_FIRMWARE_ALIGN_LEN, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* init_func_entry_point */ + fu_byte_array_append_uint16(buf, priv->subsystem, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, priv->machine_type, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, priv->compression_type, G_LITTLE_ENDIAN); + fu_byte_array_append_uint64(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* efi_image_offset */ + fu_byte_array_append_uint16(buf, + sizeof(FuOpromFirmwareHeader2), + G_LITTLE_ENDIAN); /* pci_header_offset */ + fu_byte_array_append_uint16(buf, + blob_cpd != NULL ? image_size - FU_OPROM_FIRMWARE_ALIGN_LEN + : 0x0, + G_LITTLE_ENDIAN); /* expansion_header_offset */ + g_assert(buf->len == sizeof(FuOpromFirmwareHeader2)); + + /* add PCI section */ + fu_byte_array_append_uint32(buf, FU_OPROM_FIRMWARE_PCI_DATA_SIGNATURE, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0x8086, G_LITTLE_ENDIAN); /* vendor_id */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* device_id */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* device_list_pointer */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* structure_length */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* structure_revision */ + fu_byte_array_append_uint16(buf, image_size / FU_OPROM_FIRMWARE_ALIGN_LEN, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* image_revision */ + fu_byte_array_append_uint8(buf, fu_firmware_get_idx(firmware)); + fu_byte_array_append_uint8(buf, FU_OPROM_FIRMWARE_LAST_IMAGE_INDICATOR_BIT); + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* max_runtime_image_length */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* conf_util_code_header_pointer */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* dmtf_clp_entry_point_pointer */ + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_512, 0xFF); + + /* add CPD */ + if (blob_cpd != NULL) { + fu_byte_array_append_bytes(buf, blob_cpd); + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_512, 0xFF); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_oprom_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware); + FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "machine_type", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->machine_type = val; + } + tmp = xb_node_query_text(n, "subsystem", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->subsystem = val; + } + tmp = xb_node_query_text(n, "compression_type", NULL); + if (tmp != NULL) { + guint64 val = 0; + if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->compression_type = val; + } + + /* success */ + return TRUE; +} + +static void +fu_oprom_firmware_init(FuOpromFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); +} + +static void +fu_oprom_firmware_class_init(FuOpromFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_oprom_firmware_check_magic; + klass_firmware->export = fu_oprom_firmware_export; + klass_firmware->parse = fu_oprom_firmware_parse; + klass_firmware->write = fu_oprom_firmware_write; + klass_firmware->build = fu_oprom_firmware_build; +} + +/** + * fu_oprom_firmware_new: + * + * Creates a new #FuFirmware of OptionROM format + * + * Since: 1.8.2 + **/ +FuFirmware * +fu_oprom_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_OPROM_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-oprom-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-oprom-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..bd0535203efc9efd58c75711c85434e96d179a7f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-oprom-firmware.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Intel + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_OPROM_FIRMWARE (fu_oprom_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuOpromFirmware, fu_oprom_firmware, FU, OPROM_FIRMWARE, FuFirmware) + +struct _FuOpromFirmwareClass { + FuFirmwareClass parent_class; +}; + +/** + * FU_OPROM_FIRMWARE_COMPRESSION_TYPE_NONE: + * + * No compression. + * + * Since: 1.8.2 + **/ +#define FU_OPROM_FIRMWARE_COMPRESSION_TYPE_NONE 0x00 + +/** + * FU_OPROM_FIRMWARE_SUBSYSTEM_EFI_BOOT_SRV_DRV: + * + * EFI boot. + * + * Since: 1.8.2 + **/ +#define FU_OPROM_FIRMWARE_SUBSYSTEM_EFI_BOOT_SRV_DRV 0x00 + +/** + * FU_OPROM_FIRMWARE_MACHINE_TYPE_X64: + * + * AMD64 machine type. + * + * Since: 1.8.2 + **/ +#define FU_OPROM_FIRMWARE_MACHINE_TYPE_X64 0x00 + +FuFirmware * +fu_oprom_firmware_new(void); + +guint16 +fu_oprom_firmware_get_machine_type(FuOpromFirmware *self); +guint16 +fu_oprom_firmware_get_subsystem(FuOpromFirmware *self); +guint16 +fu_oprom_firmware_get_compression_type(FuOpromFirmware *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-path-private.h b/fwupd-1.8.6/libfwupdplugin/fu-path-private.h new file mode 100644 index 0000000000000000000000000000000000000000..270cba96698588bd8abacdd16b168e4becff83da --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-path-private.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-path.h" + +gboolean +fu_path_fnmatch_impl(const gchar *pattern, const gchar *str); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-path.c b/fwupd-1.8.6/libfwupdplugin/fu-path.c new file mode 100644 index 0000000000000000000000000000000000000000..bd4e1662665985bb7bec4f7d15cafeadb840ca60 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-path.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include "fwupd-error.h" + +#include "fu-path-private.h" + +/** + * fu_path_rmtree: + * @directory: a directory name + * @error: (nullable): optional return location for an error + * + * Recursively removes a directory. + * + * Returns: %TRUE for success, %FALSE otherwise + * + * Since: 1.8.2 + **/ +gboolean +fu_path_rmtree(const gchar *directory, GError **error) +{ + const gchar *filename; + g_autoptr(GDir) dir = NULL; + + g_return_val_if_fail(directory != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* try to open */ + g_debug("removing %s", directory); + dir = g_dir_open(directory, 0, error); + if (dir == NULL) + return FALSE; + + /* find each */ + while ((filename = g_dir_read_name(dir))) { + g_autofree gchar *src = NULL; + src = g_build_filename(directory, filename, NULL); + if (g_file_test(src, G_FILE_TEST_IS_DIR)) { + if (!fu_path_rmtree(src, error)) + return FALSE; + } else { + if (g_unlink(src) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to delete: %s", + src); + return FALSE; + } + } + } + if (g_remove(directory) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to delete: %s", + directory); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_path_get_file_list_internal(GPtrArray *files, const gchar *directory, GError **error) +{ + const gchar *filename; + g_autoptr(GDir) dir = NULL; + + /* try to open */ + dir = g_dir_open(directory, 0, error); + if (dir == NULL) + return FALSE; + + /* find each */ + while ((filename = g_dir_read_name(dir))) { + g_autofree gchar *src = g_build_filename(directory, filename, NULL); + if (g_file_test(src, G_FILE_TEST_IS_DIR)) { + if (!fu_path_get_file_list_internal(files, src, error)) + return FALSE; + } else { + g_ptr_array_add(files, g_steal_pointer(&src)); + } + } + return TRUE; +} + +/** + * fu_path_get_files: + * @path: a directory name + * @error: (nullable): optional return location for an error + * + * Returns every file found under @directory, and any subdirectory. + * If any path under @directory cannot be accessed due to permissions an error + * will be returned. + * + * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error + * + * Since: 1.8.2 + **/ +GPtrArray * +fu_path_get_files(const gchar *path, GError **error) +{ + g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(path != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (!fu_path_get_file_list_internal(files, path, error)) + return NULL; + return g_steal_pointer(&files); +} + +/** + * fu_path_mkdir: + * @dirname: a directory name + * @error: (nullable): optional return location for an error + * + * Creates any required directories, including any parent directories. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_path_mkdir(const gchar *dirname, GError **error) +{ + g_return_val_if_fail(dirname != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) + g_debug("creating path %s", dirname); + if (g_mkdir_with_parents(dirname, 0755) == -1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to create '%s': %s", + dirname, + g_strerror(errno)); + return FALSE; + } + return TRUE; +} + +/** + * fu_path_mkdir_parent: + * @filename: a full pathname + * @error: (nullable): optional return location for an error + * + * Creates any required directories, including any parent directories. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_path_mkdir_parent(const gchar *filename, GError **error) +{ + g_autofree gchar *parent = NULL; + + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + parent = g_path_get_dirname(filename); + return fu_path_mkdir(parent, error); +} + +/** + * fu_path_find_program: + * @basename: the program to search + * @error: (nullable): optional return location for an error + * + * Looks for a program in the PATH variable + * + * Returns: a new #gchar, or %NULL for error + * + * Since: 1.8.2 + **/ +gchar * +fu_path_find_program(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; +} + +/** + * fu_path_get_win32_basedir: + * + * Gets the base directory that fwupd has been launched from on Windows. + * This is the directory containing all subdirectories (IE 'C:\Program Files (x86)\fwupd\') + * + * Returns: The system path, or %NULL if invalid + * + * Since: 1.8.2 + **/ +static gchar * +fu_path_get_win32_basedir(void) +{ +#ifdef _WIN32 + char drive_buf[_MAX_DRIVE]; + char dir_buf[_MAX_DIR]; + _splitpath(_pgmptr, drive_buf, dir_buf, NULL, NULL); + return g_build_filename(drive_buf, dir_buf, "..", NULL); +#endif + return NULL; +} + +/** + * fu_path_from_kind: + * @path_kind: a #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG + * + * Gets a fwupd-specific system path. These can be overridden with various + * environment variables, for instance %FWUPD_DATADIR. + * + * Returns: a system path, or %NULL if invalid + * + * Since: 1.8.2 + **/ +gchar * +fu_path_from_kind(FuPathKind path_kind) +{ + const gchar *tmp; + g_autofree gchar *basedir = NULL; + + switch (path_kind) { + /* /var */ + case FU_PATH_KIND_LOCALSTATEDIR: + tmp = g_getenv("FWUPD_LOCALSTATEDIR"); + if (tmp != NULL) + return g_strdup(tmp); +#ifdef _WIN32 + return g_build_filename(g_getenv("USERPROFILE"), + PACKAGE_NAME, + FWUPD_LOCALSTATEDIR, + NULL); +#else + tmp = g_getenv("SNAP_USER_DATA"); + if (tmp != NULL) + return g_build_filename(tmp, FWUPD_LOCALSTATEDIR, NULL); + return g_build_filename(FWUPD_LOCALSTATEDIR, NULL); +#endif + /* /proc */ + case FU_PATH_KIND_PROCFS: + tmp = g_getenv("FWUPD_PROCFS"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/proc"); + /* /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"); + /* /sys/kernel/security */ + case FU_PATH_KIND_SYSFSDIR_SECURITY: + tmp = g_getenv("FWUPD_SYSFSSECURITYDIR"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/sys/kernel/security"); + /* /sys/firmware/acpi/tables */ + case FU_PATH_KIND_ACPI_TABLES: + tmp = g_getenv("FWUPD_ACPITABLESDIR"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/sys/firmware/acpi/tables"); + /* /sys/module/firmware_class/parameters/path */ + case FU_PATH_KIND_FIRMWARE_SEARCH: + tmp = g_getenv("FWUPD_FIRMWARESEARCH"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/sys/module/firmware_class/parameters/path"); + /* /etc */ + case FU_PATH_KIND_SYSCONFDIR: + tmp = g_getenv("FWUPD_SYSCONFDIR"); + if (tmp != NULL) + return g_strdup(tmp); + tmp = g_getenv("SNAP_USER_DATA"); + if (tmp != NULL) + return g_build_filename(tmp, FWUPD_SYSCONFDIR, NULL); + basedir = fu_path_get_win32_basedir(); + if (basedir != NULL) + return g_build_filename(basedir, FWUPD_SYSCONFDIR, NULL); + return g_strdup(FWUPD_SYSCONFDIR); + + /* /usr/lib//fwupd-#VERSION# */ + case FU_PATH_KIND_LIBDIR_PKG: + tmp = g_getenv("FWUPD_LIBDIR_PKG"); + if (tmp != NULL) + return g_strdup(tmp); + tmp = g_getenv("SNAP"); + if (tmp != NULL) + return g_build_filename(tmp, FWUPD_LIBDIR_PKG, NULL); + basedir = fu_path_get_win32_basedir(); + if (basedir != NULL) + return g_build_filename(basedir, FWUPD_LIBDIR_PKG, NULL); + return g_build_filename(FWUPD_LIBDIR_PKG, NULL); + /* /usr/share/fwupd */ + case FU_PATH_KIND_DATADIR_PKG: + tmp = g_getenv("FWUPD_DATADIR"); + if (tmp != NULL) + return g_strdup(tmp); + tmp = g_getenv("SNAP"); + if (tmp != NULL) + return g_build_filename(tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL); + basedir = fu_path_get_win32_basedir(); + if (basedir != NULL) + return g_build_filename(basedir, FWUPD_DATADIR, PACKAGE_NAME, NULL); + return g_build_filename(FWUPD_DATADIR, PACKAGE_NAME, NULL); + /* /usr/share/fwupd/quirks.d */ + case FU_PATH_KIND_DATADIR_QUIRKS: + tmp = g_getenv("FWUPD_DATADIR_QUIRKS"); + if (tmp != NULL) + return g_strdup(tmp); + basedir = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); + return g_build_filename(basedir, "quirks.d", NULL); + /* /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: + tmp = g_getenv("CONFIGURATION_DIRECTORY"); + if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR); + return g_build_filename(basedir, PACKAGE_NAME, NULL); + /* /var/lib/fwupd */ + case FU_PATH_KIND_LOCALSTATEDIR_PKG: + tmp = g_getenv("STATE_DIRECTORY"); + if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); + return g_build_filename(basedir, "lib", PACKAGE_NAME, NULL); + /* /var/lib/fwupd/quirks.d */ + case FU_PATH_KIND_LOCALSTATEDIR_QUIRKS: + tmp = g_getenv("FWUPD_LOCALSTATEDIR_QUIRKS"); + if (tmp != NULL) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + return g_build_filename(basedir, "quirks.d", NULL); + /* /var/lib/fwupd/metadata */ + case FU_PATH_KIND_LOCALSTATEDIR_METADATA: + tmp = g_getenv("FWUPD_LOCALSTATEDIR_METADATA"); + if (tmp != NULL) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + return g_build_filename(basedir, "metadata", NULL); + /* /var/lib/fwupd/remotes.d */ + case FU_PATH_KIND_LOCALSTATEDIR_REMOTES: + tmp = g_getenv("FWUPD_LOCALSTATEDIR_REMOTES"); + if (tmp != NULL) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + return g_build_filename(basedir, "remotes.d", NULL); + /* /var/cache/fwupd */ + case FU_PATH_KIND_CACHEDIR_PKG: + tmp = g_getenv("CACHE_DIRECTORY"); + if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); + return g_build_filename(basedir, "cache", PACKAGE_NAME, NULL); + /* /var/etc/fwupd */ + case FU_PATH_KIND_LOCALCONFDIR_PKG: + tmp = g_getenv("LOCALCONF_DIRECTORY"); + if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) + return g_build_filename(tmp, NULL); + basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); + return g_build_filename(basedir, "etc", PACKAGE_NAME, NULL); + /* /run/lock */ + case FU_PATH_KIND_LOCKDIR: + return g_strdup("/run/lock"); + /* /sys/class/firmware-attributes */ + case FU_PATH_KIND_SYSFSDIR_FW_ATTRIB: + tmp = g_getenv("FWUPD_SYSFSFWATTRIBDIR"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/sys/class/firmware-attributes"); + case FU_PATH_KIND_OFFLINE_TRIGGER: + tmp = g_getenv("FWUPD_OFFLINE_TRIGGER"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/system-update"); + case FU_PATH_KIND_POLKIT_ACTIONS: +#ifdef POLKIT_ACTIONDIR + return g_strdup(POLKIT_ACTIONDIR); +#else + return NULL; +#endif + /* C:\Program Files (x86)\fwupd\ */ + case FU_PATH_KIND_WIN32_BASEDIR: + return fu_path_get_win32_basedir(); + /* this shouldn't happen */ + default: + g_warning("cannot build path for unknown kind %u", path_kind); + } + + return NULL; +} + +/** + * fu_path_fnmatch: + * @pattern: a glob pattern, e.g. `*foo*` + * @str: a string to match against the pattern, e.g. `bazfoobar` + * + * Matches a string against a glob pattern. + * + * Returns: %TRUE if the string matched + * + * Since: 1.8.2 + **/ +gboolean +fu_path_fnmatch(const gchar *pattern, const gchar *str) +{ + g_return_val_if_fail(pattern != NULL, FALSE); + g_return_val_if_fail(str != NULL, FALSE); + return fu_path_fnmatch_impl(pattern, str); +} + +static gint +fu_path_glob_sort_cb(gconstpointer a, gconstpointer b) +{ + return g_strcmp0(*(const gchar **)a, *(const gchar **)b); +} + +/** + * fu_path_glob: + * @directory: a directory path + * @pattern: a glob pattern, e.g. `*foo*` + * @error: (nullable): optional return location for an error + * + * Returns all the filenames that match a specific glob pattern. + * Any results are sorted. No matching files will set @error. + * + * Returns: (element-type utf8) (transfer container): matching files, or %NULL + * + * Since: 1.8.2 + **/ +GPtrArray * +fu_path_glob(const gchar *directory, const gchar *pattern, GError **error) +{ + const gchar *basename; + g_autoptr(GDir) dir = NULL; + g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(directory != NULL, NULL); + g_return_val_if_fail(pattern != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + dir = g_dir_open(directory, 0, error); + if (dir == NULL) + return NULL; + while ((basename = g_dir_read_name(dir)) != NULL) { + if (!fu_path_fnmatch(pattern, basename)) + continue; + g_ptr_array_add(files, g_build_filename(directory, basename, NULL)); + } + if (files->len == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no files matched pattern"); + return NULL; + } + g_ptr_array_sort(files, fu_path_glob_sort_cb); + return g_steal_pointer(&files); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-path.h b/fwupd-1.8.6/libfwupdplugin/fu-path.h new file mode 100644 index 0000000000000000000000000000000000000000..6329cc982b4d3ecbb9e7410b23185f3d39c730be --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-path.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * 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_LIBDIR_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_PROCFS: The procfs location (IE /proc) + * @FU_PATH_KIND_POLKIT_ACTIONS: The directory for policy kit actions (IE + * /usr/share/polkit-1/actions/) + * @FU_PATH_KIND_OFFLINE_TRIGGER: The file for the offline trigger (IE /system-update) + * @FU_PATH_KIND_SYSFSDIR_SECURITY: The sysfs security location (IE /sys/kernel/security) + * @FU_PATH_KIND_ACPI_TABLES: The location of the ACPI tables + * @FU_PATH_KIND_LOCKDIR: The lock directory (IE /run/lock) + * @FU_PATH_KIND_SYSFSDIR_FW_ATTRIB The firmware attributes directory (IE + * /sys/class/firmware-attributes) + * @FU_PATH_KIND_FIRMWARE_SEARCH: The path to configure the kernel policy for runtime loading + *other than /lib/firmware (IE /sys/module/firmware_class/parameters/path) + * @FU_PATH_KIND_DATADIR_QUIRKS: The quirks data store (IE /usr/share/fwupd/quirks.d) + * @FU_PATH_KIND_LOCALSTATEDIR_QUIRKS: The local state directory for quirks (IE + * /var/lib/fwupd/quirks.d) + * @FU_PATH_KIND_LOCALSTATEDIR_METADATA: The local state directory for metadata (IE + * /var/lib/fwupd/metadata) + * @FU_PATH_KIND_LOCALSTATEDIR_REMOTES: The local state directory for remotes (IE + * /var/lib/fwupd/remotes.d) + * @FU_PATH_KIND_WIN32_BASEDIR: The root of the install directory on Windows + * @FU_PATH_KIND_LOCALCONFDIR_PKG: The package configuration override (IE /var/etc/fwupd) + * + * 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_LIBDIR_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_PROCFS, + FU_PATH_KIND_POLKIT_ACTIONS, + FU_PATH_KIND_OFFLINE_TRIGGER, + FU_PATH_KIND_SYSFSDIR_SECURITY, + FU_PATH_KIND_ACPI_TABLES, + FU_PATH_KIND_LOCKDIR, + FU_PATH_KIND_SYSFSDIR_FW_ATTRIB, + FU_PATH_KIND_FIRMWARE_SEARCH, + FU_PATH_KIND_DATADIR_QUIRKS, + FU_PATH_KIND_LOCALSTATEDIR_QUIRKS, + FU_PATH_KIND_LOCALSTATEDIR_METADATA, + FU_PATH_KIND_LOCALSTATEDIR_REMOTES, + FU_PATH_KIND_WIN32_BASEDIR, + FU_PATH_KIND_LOCALCONFDIR_PKG, + /*< private >*/ + FU_PATH_KIND_LAST +} FuPathKind; + +gchar * +fu_path_from_kind(FuPathKind path_kind); +GPtrArray * +fu_path_glob(const gchar *directory, + const gchar *pattern, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_path_fnmatch(const gchar *pattern, const gchar *str); +gboolean +fu_path_rmtree(const gchar *directory, GError **error) G_GNUC_WARN_UNUSED_RESULT; +GPtrArray * +fu_path_get_files(const gchar *path, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_path_mkdir(const gchar *dirname, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_path_mkdir_parent(const gchar *filename, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gchar * +fu_path_find_program(const gchar *basename, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-plugin-private.h b/fwupd-1.8.6/libfwupdplugin/fu-plugin-private.h new file mode 100644 index 0000000000000000000000000000000000000000..2b8bb3f2d4be53f9cee7b93a3fa1bf88ec00f191 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-plugin-private.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-context.h" +#include "fu-plugin.h" +#include "fu-security-attrs.h" + +FuPlugin * +fu_plugin_new(FuContext *ctx); +FuPlugin * +fu_plugin_new_from_gtype(GType gtype, FuContext *ctx); +void +fu_plugin_set_context(FuPlugin *self, FuContext *ctx); +gboolean +fu_plugin_is_open(FuPlugin *self); +guint +fu_plugin_get_order(FuPlugin *self); +void +fu_plugin_set_order(FuPlugin *self, guint order); +guint +fu_plugin_get_priority(FuPlugin *self); +void +fu_plugin_set_priority(FuPlugin *self, guint priority); +gchar * +fu_plugin_to_string(FuPlugin *self); +void +fu_plugin_add_string(FuPlugin *self, guint idt, GString *str); +GPtrArray * +fu_plugin_get_rules(FuPlugin *self, FuPluginRule rule); +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) G_GNUC_WARN_UNUSED_RESULT; +void +fu_plugin_runner_init(FuPlugin *self); +gboolean +fu_plugin_runner_startup(FuPlugin *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_coldplug(FuPlugin *self, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_prepare(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_cleanup(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_composite_prepare(FuPlugin *self, + GPtrArray *devices, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_composite_cleanup(FuPlugin *self, + GPtrArray *devices, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_attach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_detach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_reload(FuPlugin *self, FuDevice *device, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_backend_device_added(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_backend_device_changed(FuPlugin *self, + FuDevice *device, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_device_created(FuPlugin *self, + FuDevice *device, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_plugin_runner_device_added(FuPlugin *self, FuDevice *device); +void +fu_plugin_runner_device_removed(FuPlugin *self, FuDevice *device); +void +fu_plugin_runner_device_register(FuPlugin *self, FuDevice *device); +gboolean +fu_plugin_runner_write_firmware(FuPlugin *self, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_verify(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FuPluginVerifyFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_activate(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error); +gboolean +fu_plugin_runner_unlock(FuPlugin *self, FuDevice *device, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_clear_results(FuPlugin *self, + FuDevice *device, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_plugin_runner_get_results(FuPlugin *self, + FuDevice *device, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_plugin_runner_add_security_attrs(FuPlugin *self, FuSecurityAttrs *attrs); +gint +fu_plugin_name_compare(FuPlugin *plugin1, FuPlugin *plugin2); +gint +fu_plugin_order_compare(FuPlugin *plugin1, FuPlugin *plugin2); + +/* utils */ +gchar * +fu_plugin_guess_name_from_fn(const gchar *filename); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-plugin-vfuncs.h b/fwupd-1.8.6/libfwupdplugin/fu-plugin-vfuncs.h new file mode 100644 index 0000000000000000000000000000000000000000..18ac801d0c324adc4ef135d9e7cb776701576d36 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-plugin-vfuncs.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +/** + * fu_plugin_init_vfuncs: + * @vfuncs: #FuPluginVfuncs + * + * Initializes the plugin vfuncs. + * + * Since: 1.7.2 + **/ +void +fu_plugin_init_vfuncs(FuPluginVfuncs *vfuncs); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-plugin.c b/fwupd-1.8.6/libfwupdplugin/fu-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..cf2f5bc9d5c64b0e7b6ca1e9c4f01dfa1a52f1ce --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-plugin.c @@ -0,0 +1,2891 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuPlugin" + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "fu-bytes.h" +#include "fu-context-private.h" +#include "fu-device-private.h" +#include "fu-kernel.h" +#include "fu-mutex.h" +#include "fu-path.h" +#include "fu-plugin-private.h" +#include "fu-security-attr.h" +#include "fu-string.h" + +/** + * FuPlugin: + * + * A plugin which is used by fwupd to enumerate and update devices. + * + * See also: [class@FuDevice], [class@Fwupd.Plugin] + */ + +static void +fu_plugin_finalize(GObject *object); + +typedef struct { + GModule *module; + guint order; + guint priority; + gboolean done_init; + GPtrArray *rules[FU_PLUGIN_RULE_LAST]; + GPtrArray *devices; /* (nullable) (element-type FuDevice) */ + GHashTable *runtime_versions; + GHashTable *compile_versions; + FuContext *ctx; + GArray *device_gtypes; /* (nullable): of #GType */ + GHashTable *cache; /* (nullable): platform_id:GObject */ + GRWLock cache_mutex; + GHashTable *report_metadata; /* (nullable): key:value */ + GFileMonitor *config_monitor; + FuPluginData *data; + FuPluginVfuncs vfuncs; +} FuPluginPrivate; + +enum { PROP_0, PROP_CONTEXT, PROP_LAST }; + +enum { + SIGNAL_DEVICE_ADDED, + SIGNAL_DEVICE_REMOVED, + SIGNAL_DEVICE_REGISTER, + SIGNAL_RULES_CHANGED, + SIGNAL_CONFIG_CHANGED, + SIGNAL_CHECK_SUPPORTED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FuPlugin, fu_plugin, FWUPD_TYPE_PLUGIN) +#define GET_PRIVATE(o) (fu_plugin_get_instance_private(o)) + +typedef void (*FuPluginInitVfuncsFunc)(FuPluginVfuncs *vfuncs); +typedef gboolean (*FuPluginDeviceFunc)(FuPlugin *self, FuDevice *device, GError **error); +typedef gboolean (*FuPluginDeviceProgressFunc)(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + GError **error); +typedef gboolean (*FuPluginFlaggedDeviceFunc)(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +typedef gboolean (*FuPluginDeviceArrayFunc)(FuPlugin *self, GPtrArray *devices, GError **error); + +#define FU_PLUGIN_FILE_MODE_NONSECURE 0644 +#define FU_PLUGIN_FILE_MODE_SECURE 0640 + +/** + * fu_plugin_is_open: + * @self: a #FuPlugin + * + * Determines if the plugin is opened + * + * Returns: TRUE for opened, FALSE for not + * + * Since: 1.3.5 + **/ +gboolean +fu_plugin_is_open(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + return priv->module != NULL; +} + +/** + * fu_plugin_get_name: + * @self: a #FuPlugin + * + * Gets the plugin name. + * + * Returns: a plugin name, or %NULL for unknown. + * + * Since: 0.8.0 + **/ +const gchar * +fu_plugin_get_name(FuPlugin *self) +{ + g_return_val_if_fail(FU_IS_PLUGIN(self), NULL); + return fwupd_plugin_get_name(FWUPD_PLUGIN(self)); +} + +/** + * fu_plugin_set_name: + * @self: a #FuPlugin + * @name: a string + * + * Sets the plugin name. + * + * Since: 0.8.0 + **/ +void +fu_plugin_set_name(FuPlugin *self, const gchar *name) +{ + g_return_if_fail(FU_IS_PLUGIN(self)); + fwupd_plugin_set_name(FWUPD_PLUGIN(self), name); +} + +static FuPluginVfuncs * +fu_plugin_get_vfuncs(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_MODULAR)) + return &priv->vfuncs; + return FU_PLUGIN_GET_CLASS(self); +} + +/** + * fu_plugin_cache_lookup: + * @self: a #FuPlugin + * @id: the key + * + * Finds an object in the per-plugin cache. + * + * Returns: (transfer none): a #GObject, or %NULL for unfound. + * + * Since: 0.8.0 + **/ +gpointer +fu_plugin_cache_lookup(FuPlugin *self, const gchar *id) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new(&priv->cache_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); + if (priv->cache == NULL) + return NULL; + return g_hash_table_lookup(priv->cache, id); +} + +/** + * fu_plugin_cache_add: + * @self: a #FuPlugin + * @id: the key + * @dev: a #GObject, typically a #FuDevice + * + * Adds an object to the per-plugin cache. + * + * Since: 0.8.0 + **/ +void +fu_plugin_cache_add(FuPlugin *self, const gchar *id, gpointer dev) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new(&priv->cache_mutex); + g_return_if_fail(FU_IS_PLUGIN(self)); + g_return_if_fail(id != NULL); + g_return_if_fail(G_IS_OBJECT(dev)); + g_return_if_fail(locker != NULL); + if (priv->cache == NULL) { + priv->cache = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)g_object_unref); + } + g_hash_table_insert(priv->cache, g_strdup(id), g_object_ref(dev)); +} + +/** + * fu_plugin_cache_remove: + * @self: a #FuPlugin + * @id: the key + * + * Removes an object from the per-plugin cache. + * + * Since: 0.8.0 + **/ +void +fu_plugin_cache_remove(FuPlugin *self, const gchar *id) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new(&priv->cache_mutex); + g_return_if_fail(FU_IS_PLUGIN(self)); + g_return_if_fail(id != NULL); + g_return_if_fail(locker != NULL); + if (priv->cache == NULL) + return; + g_hash_table_remove(priv->cache, id); +} + +/** + * fu_plugin_get_data: + * @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. + * + * Returns: (transfer none): a pointer to a structure, or %NULL for unset. + * + * Since: 0.8.0 + **/ +FuPluginData * +fu_plugin_get_data(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PLUGIN(self), NULL); + return priv->data; +} + +/** + * fu_plugin_alloc_data: (skip): + * @self: a #FuPlugin + * @data_sz: the size to allocate + * + * Allocates the per-plugin allocated private data. + * + * Returns: (transfer full): a pointer to a structure, or %NULL for unset. + * + * Since: 0.8.0 + **/ +FuPluginData * +fu_plugin_alloc_data(FuPlugin *self, gsize data_sz) +{ + 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; + } + priv->data = g_malloc0(data_sz); + return priv->data; +} + +/** + * fu_plugin_guess_name_from_fn: + * @filename: filename to guess + * + * Tries to guess the name of the plugin from a filename + * + * Returns: (transfer full): the guessed name of the plugin + * + * Since: 1.0.8 + **/ +gchar * +fu_plugin_guess_name_from_fn(const gchar *filename) +{ + const gchar *prefix = "libfu_plugin_"; + gchar *name; + gchar *str = g_strstr_len(filename, -1, prefix); + if (str == NULL) + return NULL; + name = g_strdup(str + strlen(prefix)); + g_strdelimit(name, ".", '\0'); + return name; +} + +static gchar * +fu_plugin_get_config_filename(FuPlugin *self) +{ + g_autofree gchar *conf_dir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + g_autofree gchar *conf_file = g_strdup_printf("%s.conf", fu_plugin_get_name(self)); + return g_build_filename(conf_dir, conf_file, NULL); +} + +/** + * fu_plugin_open: + * @self: a #FuPlugin + * @filename: the shared object filename to open + * @error: (nullable): optional return location for an error + * + * Opens the plugin module, and calls `->load()` on it. + * + * Returns: TRUE for success, FALSE for fail + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_open(FuPlugin *self, const gchar *filename, GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + FuPluginVfuncs *vfuncs; + FuPluginInitVfuncsFunc init_vfuncs = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + priv->module = g_module_open(filename, 0); + if (priv->module == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open plugin %s: %s", + filename, + g_module_error()); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_FAILED_OPEN); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_USER_WARNING); + return FALSE; + } + + /* call the vfunc setup */ + g_module_symbol(priv->module, "fu_plugin_init_vfuncs", (gpointer *)&init_vfuncs); + if (init_vfuncs == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to init_vfuncs() on plugin %s", + filename); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_FAILED_OPEN); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_USER_WARNING); + return FALSE; + } + + /* we can't "fallback" from modular to built-in so this is safe */ + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_MODULAR); + vfuncs = fu_plugin_get_vfuncs(self); + init_vfuncs(vfuncs); + + /* set automatically */ + if (fu_plugin_get_name(self) == NULL) { + g_autofree gchar *str = fu_plugin_guess_name_from_fn(filename); + fu_plugin_set_name(self, str); + } + + /* ensure the configure file is set to the correct permission */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_SECURE_CONFIG)) { + g_autofree gchar *conf_path = fu_plugin_get_config_filename(self); + if (g_file_test(conf_path, G_FILE_TEST_EXISTS)) { + gint rc = g_chmod(conf_path, FU_PLUGIN_FILE_MODE_SECURE); + if (rc != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to change permission of %s", + filename); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_FAILED_OPEN); + return FALSE; + } + } + } + + /* optional */ + if (vfuncs->load != NULL) { + FuContext *ctx = fu_plugin_get_context(self); + g_debug("load(%s)", filename); + vfuncs->load(ctx); + } + + return TRUE; +} + +static gchar * +fu_plugin_flags_to_string(FwupdPluginFlags flags) +{ + g_autoptr(GString) str = g_string_new(NULL); + for (guint i = 0; i < 64; i++) { + FwupdPluginFlags flag = (guint64)1 << i; + if ((flags & flag) == 0) + continue; + if (str->len > 0) + g_string_append_c(str, ','); + g_string_append(str, fwupd_plugin_flag_to_string(flag)); + } + if (str->len == 0) + return NULL; + return g_string_free(g_steal_pointer(&str), FALSE); +} + +/** + * fu_plugin_add_string: + * @self: a #FuPlugin + * @idt: indent level + * @str: a string to append to + * + * Add daemon-specific device metadata to an existing string. + * + * Since: 1.8.4 + **/ +void +fu_plugin_add_string(FuPlugin *self, guint idt, GString *str) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + const gchar *name = fwupd_plugin_get_name(FWUPD_PLUGIN(self)); + g_autofree gchar *flags = NULL; + + g_return_if_fail(FU_IS_PLUGIN(self)); + g_return_if_fail(str != NULL); + + /* attributes */ + fu_string_append(str, idt, G_OBJECT_TYPE_NAME(self), ""); + if (name != NULL) + fu_string_append(str, idt + 1, "Name", name); + flags = fu_plugin_flags_to_string(fwupd_plugin_get_flags(FWUPD_PLUGIN(self))); + if (flags != NULL) + fu_string_append(str, idt + 1, "Flags", flags); + if (priv->order != 0) + fu_string_append_ku(str, idt + 1, "Order", priv->order); + if (priv->priority != 0) + fu_string_append_ku(str, idt + 1, "Priority", priv->priority); + + /* optional */ + if (vfuncs->to_string != NULL) + vfuncs->to_string(self, idt + 1, str); +} + +/** + * fu_plugin_to_string: + * @self: a #FuPlugin + * + * This allows us to easily print the plugin metadata. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 1.8.4 + **/ +gchar * +fu_plugin_to_string(FuPlugin *self) +{ + g_autoptr(GString) str = g_string_new(NULL); + g_return_val_if_fail(FU_IS_PLUGIN(self), NULL); + fu_plugin_add_string(self, 0, str); + return g_string_free(g_steal_pointer(&str), FALSE); +} + +/* order of usefulness to the user */ +static const gchar * +fu_plugin_build_device_update_error(FuPlugin *self) +{ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_NO_HARDWARE)) + return "Not updatable as required hardware was not found"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_LEGACY_BIOS)) + return "Not updatable in legacy BIOS mode"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED)) + return "Not updatable as UEFI capsule updates not enabled in firmware setup"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED)) + return "Not updatable as requires unlock"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_AUTH_REQUIRED)) + return "Not updatable as requires authentication"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED)) + return "Not updatable as efivarfs was not found"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND)) + return "Not updatable as UEFI ESP partition not detected"; + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return "Not updatable as plugin was disabled"; + return NULL; +} + +static void +fu_plugin_ensure_devices(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + if (priv->devices != NULL) + return; + priv->devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_plugin_device_child_added_cb(FuDevice *device, FuDevice *child, FuPlugin *self) +{ + g_debug("child %s added to parent %s after setup, adding to daemon", + fu_device_get_id(child), + fu_device_get_id(device)); + fu_plugin_device_add(self, child); +} + +static void +fu_plugin_device_child_removed_cb(FuDevice *device, FuDevice *child, FuPlugin *self) +{ + g_debug("child %s removed from parent %s after setup, removing from daemon", + fu_device_get_id(child), + fu_device_get_id(device)); + fu_plugin_device_remove(self, child); +} + +static void +fu_plugin_config_monitor_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *self = FU_PLUGIN(user_data); + g_autofree gchar *fn = g_file_get_path(file); + g_debug("%s changed, sending signal", fn); + g_signal_emit(self, signals[SIGNAL_CONFIG_CHANGED], 0); +} + +/** + * fu_plugin_device_add: + * @self: a #FuPlugin + * @device: a device + * + * Asks the daemon to add a device to the exported list. If this device ID + * has already been added by a different plugin then this request will be + * ignored. + * + * Since: 0.8.0 + **/ +void +fu_plugin_device_add(FuPlugin *self, FuDevice *device) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + GPtrArray *children; + g_autoptr(GError) error = NULL; + + 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; + } + + /* add to array */ + fu_plugin_ensure_devices(self); + g_ptr_array_add(priv->devices, g_object_ref(device)); + + /* proxy to device where required */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE)) { + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_USER_WARNING)) { + fu_device_inhibit(device, + "clear-updatable", + fu_plugin_build_device_update_error(self)); + } else { + fu_device_inhibit(device, + "clear-updatable", + "Plugin disallowed updates with no user warning"); + } + } + + g_debug("emit added from %s: %s", 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(self)); + g_signal_emit(self, signals[SIGNAL_DEVICE_ADDED], 0, device); + + /* 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); + if (fu_device_get_created(child) == 0) + fu_plugin_device_add(self, child); + } + + /* watch to see if children are added or removed at runtime */ + g_signal_connect(FU_DEVICE(device), + "child-added", + G_CALLBACK(fu_plugin_device_child_added_cb), + self); + g_signal_connect(FU_DEVICE(device), + "child-removed", + G_CALLBACK(fu_plugin_device_child_removed_cb), + self); +} + +/** + * fu_plugin_get_devices: + * @self: a #FuPlugin + * + * Returns all devices added by the plugin using [method@FuPlugin.device_add] and + * not yet removed with [method@FuPlugin.device_remove]. + * + * Returns: (transfer none) (element-type FuDevice): devices + * + * Since: 1.5.6 + **/ +GPtrArray * +fu_plugin_get_devices(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PLUGIN(self), NULL); + fu_plugin_ensure_devices(self); + return priv->devices; +} + +/** + * fu_plugin_device_register: + * @self: a #FuPlugin + * @device: a device + * + * Registers the device with other plugins so they can set metadata. + * + * Plugins do not have to call this manually as this is done automatically + * when using [method@FuPlugin.device_add]. They may wish to use this manually + * if for instance the coldplug should be ignored based on the metadata + * set from other plugins. + * + * Since: 0.9.7 + **/ +void +fu_plugin_device_register(FuPlugin *self, FuDevice *device) +{ + g_autoptr(GError) error = NULL; + + 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 registration: %s", error->message); + return; + } + + 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: + * @self: a #FuPlugin + * @device: a device + * + * Asks the daemon to remove a device from the exported list. + * + * Since: 0.8.0 + **/ +void +fu_plugin_device_remove(FuPlugin *self, FuDevice *device) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_PLUGIN(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + + /* remove from array */ + if (priv->devices != NULL) + g_ptr_array_remove(priv->devices, device); + + g_debug("emit removed from %s: %s", fu_plugin_get_name(self), fu_device_get_id(device)); + g_signal_emit(self, signals[SIGNAL_DEVICE_REMOVED], 0, device); +} + +/** + * fu_plugin_check_supported: + * @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 + * AppStream metadata. + * + * Returns: %TRUE if the device is supported. + * + * Since: 1.0.0 + **/ +static gboolean +fu_plugin_check_supported(FuPlugin *self, const gchar *guid) +{ + gboolean retval = FALSE; + g_signal_emit(self, signals[SIGNAL_CHECK_SUPPORTED], 0, guid, &retval); + return retval; +} + +/** + * fu_plugin_get_context: + * @self: a #FuPlugin + * + * Gets the context for a plugin. + * + * Returns: (transfer none): a #FuContext or %NULL if not set + * + * Since: 1.6.0 + **/ +FuContext * +fu_plugin_get_context(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + return priv->ctx; +} + +/** + * fu_plugin_set_context: + * @self: a #FuPlugin + * @ctx: (nullable): optional #FuContext + * + * Sets the context for this plugin. + * + * Since: 1.8.6 + **/ +void +fu_plugin_set_context(FuPlugin *self, FuContext *ctx) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PLUGIN(self)); + g_return_if_fail(FU_IS_CONTEXT(ctx) || ctx == NULL); + + if (g_set_object(&priv->ctx, ctx)) + g_object_notify(G_OBJECT(self), "context"); +} + +static gboolean +fu_plugin_device_attach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + FuDeviceClass *proxy_klass = FU_DEVICE_GET_CLASS(proxy); + g_autoptr(FuDeviceLocker) locker = NULL; + if (proxy_klass->attach == NULL) + return TRUE; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + return fu_device_attach_full(device, progress, error); +} + +static gboolean +fu_plugin_device_detach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + FuDeviceClass *proxy_klass = FU_DEVICE_GET_CLASS(proxy); + g_autoptr(FuDeviceLocker) locker = NULL; + if (proxy_klass->detach == NULL) + return TRUE; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + return fu_device_detach_full(device, progress, error); +} + +static gboolean +fu_plugin_device_activate(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + FuDeviceClass *proxy_klass = FU_DEVICE_GET_CLASS(proxy); + g_autoptr(FuDeviceLocker) locker = NULL; + if (proxy_klass->activate == NULL) + return TRUE; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + return fu_device_activate(device, progress, error); +} + +static gboolean +fu_plugin_device_write_firmware(FuPlugin *self, + FuDevice *device, + GBytes *fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + + /* back the old firmware up to /var/lib/fwupd */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL)) { + g_autoptr(GBytes) fw_old = NULL; + g_autofree gchar *path = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 25, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 75, NULL); + + fw_old = fu_device_dump_firmware(device, fu_progress_get_child(progress), error); + if (fw_old == NULL) { + g_prefix_error(error, "failed to backup old firmware: "); + return FALSE; + } + localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf("%s.bin", fu_device_get_version(device)); + path = g_build_filename( + localstatedir, + "backup", + fu_device_get_id(device), + fu_device_get_serial(device) != NULL ? fu_device_get_serial(device) : "default", + fn, + NULL); + fu_progress_step_done(progress); + if (!fu_bytes_set_contents(path, fw_old, error)) + return FALSE; + if (!fu_device_write_firmware(device, + fw, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + return TRUE; + } + + return fu_device_write_firmware(device, fw, progress, flags, error); +} + +static gboolean +fu_plugin_device_get_results(FuPlugin *self, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_device_get_results(device, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) + return TRUE; + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_plugin_device_read_firmware(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) fw = NULL; + GChecksumType checksum_types[] = {G_CHECKSUM_SHA1, G_CHECKSUM_SHA256, 0}; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + if (!fu_device_detach_full(device, progress, error)) + return FALSE; + firmware = fu_device_read_firmware(device, progress, error); + if (firmware == NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_device_attach_full(device, progress, &error_local)) + g_debug("ignoring attach failure: %s", error_local->message); + g_prefix_error(error, "failed to read firmware: "); + return FALSE; + } + fw = fu_firmware_write(firmware, error); + if (fw == NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_device_attach_full(device, progress, &error_local)) + g_debug("ignoring attach failure: %s", error_local->message); + g_prefix_error(error, "failed to write firmware: "); + 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 fu_device_attach_full(device, progress, error); +} + +/** + * fu_plugin_runner_startup: + * @self: a #FuPlugin + * @error: (nullable): optional return location for an error + * + * Runs the startup routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_runner_startup(FuPlugin *self, FuProgress *progress, GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autofree gchar *config_filename = fu_plugin_get_config_filename(self); + g_autoptr(GError) error_local = NULL; + g_autoptr(GFile) file = g_file_new_for_path(config_filename); + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + + /* progress */ + fu_progress_set_name(progress, fu_plugin_get_name(self)); + + /* be helpful for unit tests */ + fu_plugin_runner_init(self); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->startup == NULL) + return TRUE; + g_debug("startup(%s)", fu_plugin_get_name(self)); + if (!vfuncs->startup(self, progress, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in startup(%s)", fu_plugin_get_name(self)); + 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: ", + fu_plugin_get_name(self)); + return FALSE; + } + + /* create a monitor on the config file */ + priv->config_monitor = g_file_monitor_file(file, G_FILE_MONITOR_NONE, NULL, error); + if (priv->config_monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(priv->config_monitor), + "changed", + G_CALLBACK(fu_plugin_config_monitor_changed_cb), + self); + + /* success */ + return TRUE; +} + +/** + * fu_plugin_runner_init: + * @self: a #FuPlugin + * + * Runs the constructed routine for the plugin, if enabled. + * + * Since: 1.8.1 + **/ +void +fu_plugin_runner_init(FuPlugin *self) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + FuPluginPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_PLUGIN(self)); + + /* already done */ + if (priv->done_init) + return; + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return; + + /* optional */ + if (vfuncs->constructed != NULL) { + g_debug("constructed(%s)", fu_plugin_get_name(self)); + vfuncs->constructed(G_OBJECT(self)); + priv->done_init = TRUE; + } +} + +static gboolean +fu_plugin_runner_device_generic(FuPlugin *self, + FuDevice *device, + const gchar *symbol_name, + FuPluginDeviceFunc device_func, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (device_func == NULL) + return TRUE; + g_debug("%s(%s)", symbol_name + 10, fu_plugin_get_name(self)); + if (!device_func(self, device, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in %s(%s)", + fu_plugin_get_name(self), + 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, + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_plugin_runner_device_generic_progress(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + const gchar *symbol_name, + FuPluginDeviceProgressFunc device_func, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (device_func == NULL) + return TRUE; + g_debug("%s(%s)", symbol_name + 10, fu_plugin_get_name(self)); + if (!device_func(self, device, progress, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in %s(%s)", + fu_plugin_get_name(self), + 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, + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_plugin_runner_flagged_device_generic(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + const gchar *symbol_name, + FuPluginFlaggedDeviceFunc func, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (func == NULL) + return TRUE; + g_debug("%s(%s)", symbol_name + 10, fu_plugin_get_name(self)); + if (!func(self, device, progress, flags, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in %s(%s)", + fu_plugin_get_name(self), + 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, + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_plugin_runner_device_array_generic(FuPlugin *self, + GPtrArray *devices, + const gchar *symbol_name, + FuPluginDeviceArrayFunc func, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (func == NULL) + return TRUE; + g_debug("%s(%s)", symbol_name + 10, fu_plugin_get_name(self)); + if (!func(self, devices, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in for %s(%s)", + fu_plugin_get_name(self), + 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, + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +/** + * fu_plugin_runner_coldplug: + * @self: a #FuPlugin + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Runs the coldplug routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_runner_coldplug(FuPlugin *self, FuProgress *progress, GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + + /* progress */ + fu_progress_set_name(progress, fu_plugin_get_name(self)); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* no HwId */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_REQUIRE_HWID)) + return TRUE; + + /* optional */ + if (vfuncs->coldplug == NULL) + return TRUE; + g_debug("coldplug(%s)", fu_plugin_get_name(self)); + if (!vfuncs->coldplug(self, progress, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in coldplug(%s)", fu_plugin_get_name(self)); + g_set_error_literal(&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + /* coldplug failed, but we might have already added devices to the daemon... */ + if (priv->devices != NULL) { + for (guint i = 0; i < priv->devices->len; i++) { + FuDevice *device = g_ptr_array_index(priv->devices, i); + g_warning("removing device %s due to failed coldplug", + fu_device_get_id(device)); + fu_plugin_device_remove(self, device); + } + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to coldplug using %s: ", + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +/** + * fu_plugin_runner_composite_prepare: + * @self: a #FuPlugin + * @devices: (element-type FuDevice): an array of devices + * @error: (nullable): optional return location for an error + * + * Runs the composite_prepare routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.0.9 + **/ +gboolean +fu_plugin_runner_composite_prepare(FuPlugin *self, GPtrArray *devices, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + return fu_plugin_runner_device_array_generic(self, + devices, + "fu_plugin_composite_prepare", + vfuncs->composite_prepare, + error); +} + +/** + * fu_plugin_runner_composite_cleanup: + * @self: a #FuPlugin + * @devices: (element-type FuDevice): an array of devices + * @error: (nullable): optional return location for an error + * + * Runs the composite_cleanup routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.0.9 + **/ +gboolean +fu_plugin_runner_composite_cleanup(FuPlugin *self, GPtrArray *devices, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + return fu_plugin_runner_device_array_generic(self, + devices, + "fu_plugin_composite_cleanup", + vfuncs->composite_cleanup, + error); +} + +/** + * fu_plugin_runner_prepare: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Runs the update_prepare routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_runner_prepare(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + fu_device_add_backend_tag(device, "prepare"); + return fu_plugin_runner_flagged_device_generic(self, + device, + progress, + flags, + "fu_plugin_prepare", + vfuncs->prepare, + error); +} + +/** + * fu_plugin_runner_cleanup: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Runs the update_cleanup routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_runner_cleanup(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + fu_device_add_backend_tag(device, "cleanup"); + return fu_plugin_runner_flagged_device_generic(self, + device, + progress, + flags, + "fu_plugin_cleanup", + vfuncs->cleanup, + error); +} + +/** + * fu_plugin_runner_attach: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Runs the update_attach routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_runner_attach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + fu_device_add_backend_tag(device, "attach"); + return fu_plugin_runner_device_generic_progress( + self, + device, + progress, + "fu_plugin_attach", + vfuncs->attach != NULL ? vfuncs->attach : fu_plugin_device_attach, + error); +} + +/** + * fu_plugin_runner_detach: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Runs the update_detach routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_runner_detach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + fu_device_add_backend_tag(device, "detach"); + return fu_plugin_runner_device_generic_progress( + self, + device, + progress, + "fu_plugin_detach", + vfuncs->detach != NULL ? vfuncs->detach : fu_plugin_device_detach, + error); +} + +/** + * fu_plugin_runner_reload: + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Runs reload routine for a device + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_runner_reload(FuPlugin *self, FuDevice *device, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* no object loaded */ + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + fu_device_add_backend_tag(device, "reload"); + return fu_device_reload(device, error); +} + +/** + * fu_plugin_runner_add_security_attrs: + * @self: a #FuPlugin + * @attrs: a security attribute + * + * Runs the `add_security_attrs()` routine for the plugin + * + * Since: 1.5.0 + **/ +void +fu_plugin_runner_add_security_attrs(FuPlugin *self, FuSecurityAttrs *attrs) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + + /* optional, but gets called even for disabled plugins */ + if (vfuncs->add_security_attrs == NULL) + return; + g_debug("add_security_attrs(%s)", fu_plugin_get_name(self)); + vfuncs->add_security_attrs(self, attrs); +} + +/** + * fu_plugin_add_device_gtype: + * @self: a #FuPlugin + * @device_gtype: a #GType, e.g. `FU_TYPE_DEVICE` + * + * Adds the device #GType which is used when creating devices. + * + * If this method is used then fu_plugin_backend_device_added() is not called, and + * instead the object is created in the daemon for the plugin. + * + * Plugins can use this method only in fu_plugin_init() + * + * Since: 1.6.0 + **/ +void +fu_plugin_add_device_gtype(FuPlugin *self, GType device_gtype) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + + /* create as required */ + if (priv->device_gtypes == NULL) + priv->device_gtypes = g_array_new(FALSE, FALSE, sizeof(GType)); + + /* ensure (to allow quirks to use it) then add */ + g_type_ensure(device_gtype); + g_array_append_val(priv->device_gtypes, device_gtype); +} + +static gchar * +fu_common_string_uncamelcase(const gchar *str) +{ + GString *tmp = g_string_new(NULL); + for (guint i = 0; str[i] != '\0'; i++) { + if (g_ascii_islower(str[i]) || g_ascii_isdigit(str[i])) { + g_string_append_c(tmp, str[i]); + continue; + } + if (i > 0) + g_string_append_c(tmp, '-'); + g_string_append_c(tmp, g_ascii_tolower(str[i])); + } + return g_string_free(tmp, FALSE); +} + +static gboolean +fu_plugin_check_amdgpu_dpaux(FuPlugin *self, GError **error) +{ +#ifdef __linux__ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_auto(GStrv) lines = NULL; + + /* no module support in the kernel, we can't test for amdgpu module */ + if (!g_file_test("/proc/modules", G_FILE_TEST_EXISTS)) + return TRUE; + 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 ")) { + /* released 2019! */ + return fu_kernel_check_version("5.2.0", error); + } + } +#endif + return TRUE; +} + +/** + * 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() + * + * Since: 1.6.2 + **/ +void +fu_plugin_add_udev_subsystem(FuPlugin *self, const gchar *subsystem) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + + /* see https://github.com/fwupd/fwupd/issues/1121 for more details */ + if (g_strcmp0(subsystem, "drm_dp_aux_dev") == 0) { + g_autoptr(GError) error = NULL; + if (!fu_plugin_check_amdgpu_dpaux(self, &error)) { + g_warning("failed to add subsystem: %s", error->message); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_DISABLED); + fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD); + return; + } + } + + /* proxy */ + fu_context_add_udev_subsystem(priv->ctx, subsystem); +} + +/** + * fu_plugin_add_firmware_gtype: + * @self: a #FuPlugin + * @id: (nullable): an optional string describing the type, e.g. `ihex` + * @gtype: a #GType e.g. `FU_TYPE_FOO_FIRMWARE` + * + * Adds a firmware #GType which is used when creating devices. If @id is not + * specified then it is guessed using the #GType name. + * + * Plugins can use this method only in fu_plugin_init() + * + * Since: 1.3.3 + **/ +void +fu_plugin_add_firmware_gtype(FuPlugin *self, const gchar *id, GType gtype) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_autofree gchar *id_safe = NULL; + if (id != NULL) { + id_safe = g_strdup(id); + } else { + g_autoptr(GString) str = g_string_new(g_type_name(gtype)); + if (g_str_has_prefix(str->str, "Fu")) + g_string_erase(str, 0, 2); + fu_string_replace(str, "Firmware", ""); + id_safe = fu_common_string_uncamelcase(str->str); + } + fu_context_add_firmware_gtype(priv->ctx, id_safe, gtype); +} + +static gboolean +fu_plugin_check_supported_device(FuPlugin *self, FuDevice *device) +{ + GPtrArray *instance_ids = fu_device_get_instance_ids(device); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string(instance_id); + if (fu_plugin_check_supported(self, guid)) + return TRUE; + } + return FALSE; +} + +static gboolean +fu_plugin_backend_device_added(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + GError **error) +{ + FuDevice *proxy; + FuPluginPrivate *priv = GET_PRIVATE(self); + GType device_gtype = fu_device_get_specialized_gtype(FU_DEVICE(device)); + g_autoptr(FuDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 4, "created"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 48, "open"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 48, "add"); + + /* fall back to plugin default */ + if (device_gtype == G_TYPE_INVALID) { + if (priv->device_gtypes->len > 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "too many GTypes to choose a default"); + return FALSE; + } + device_gtype = g_array_index(priv->device_gtypes, GType, 0); + } + + /* create new device and incorporate existing properties */ + dev = g_object_new(device_gtype, "context", priv->ctx, NULL); + fu_device_incorporate(dev, FU_DEVICE(device)); + if (!fu_plugin_runner_device_created(self, dev, error)) + return FALSE; + fu_progress_step_done(progress); + + /* there are a lot of different devices that match, but not all respond + * well to opening -- so limit some ones with issued updates */ + if (fu_device_has_internal_flag(dev, FU_DEVICE_INTERNAL_FLAG_ONLY_SUPPORTED)) { + if (!fu_device_probe(dev, error)) + return FALSE; + fu_device_convert_instance_ids(dev); + if (!fu_plugin_check_supported_device(self, dev)) { + g_autofree gchar *guids = fu_device_get_guids_as_str(dev); + g_debug("%s has no updates, so ignoring device", guids); + fu_progress_finished(progress); + return TRUE; + } + } + + /* open */ + proxy = fu_device_get_proxy(device); + if (proxy != NULL) { + g_autoptr(FuDeviceLocker) locker_proxy = NULL; + locker_proxy = fu_device_locker_new(proxy, error); + if (locker_proxy == NULL) + return FALSE; + } + locker = fu_device_locker_new(dev, error); + if (locker == NULL) + return FALSE; + fu_progress_step_done(progress); + + /* add */ + fu_plugin_device_add(self, dev); + fu_plugin_runner_device_added(self, dev); + fu_progress_step_done(progress); + return TRUE; +} + +/** + * fu_plugin_runner_backend_device_added: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Call the backend_device_added routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.5.6 + **/ +gboolean +fu_plugin_runner_backend_device_added(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->backend_device_added == NULL) { + if (priv->device_gtypes != NULL || + fu_device_get_specialized_gtype(device) != G_TYPE_INVALID) { + return fu_plugin_backend_device_added(self, device, progress, error); + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "No device GType set"); + return FALSE; + } + g_debug("backend_device_added(%s)", fu_plugin_get_name(self)); + if (!vfuncs->backend_device_added(self, device, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in backend_device_added(%s)", + fu_plugin_get_name(self)); + 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: ", + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +/** + * fu_plugin_runner_backend_device_changed: + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Call the backend_device_changed routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.5.6 + **/ +gboolean +fu_plugin_runner_backend_device_changed(FuPlugin *self, FuDevice *device, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->backend_device_changed == NULL) + return TRUE; + g_debug("udev_device_changed(%s)", fu_plugin_get_name(self)); + if (!vfuncs->backend_device_changed(self, device, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in udev_device_changed(%s)", + fu_plugin_get_name(self)); + g_set_error_literal(&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to change device on %s: ", + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +/** + * fu_plugin_runner_device_added: + * @self: a #FuPlugin + * @device: a device + * + * Call the device_added routine for the plugin + * + * Since: 1.5.0 + **/ +void +fu_plugin_runner_device_added(FuPlugin *self, FuDevice *device) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return; + + /* optional */ + if (vfuncs->device_added == NULL) + return; + g_debug("fu_plugin_device_added(%s)", fu_plugin_get_name(self)); + vfuncs->device_added(self, device); +} + +/** + * fu_plugin_runner_device_removed: + * @self: a #FuPlugin + * @device: a device + * + * Call the device_removed routine for the plugin + * + * Since: 1.1.2 + **/ +void +fu_plugin_runner_device_removed(FuPlugin *self, FuDevice *device) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + if (!fu_plugin_runner_device_generic(self, + device, + "fu_plugin_backend_device_removed", + vfuncs->backend_device_removed, + &error_local)) + g_warning("%s", error_local->message); +} + +/** + * fu_plugin_runner_device_register: + * @self: a #FuPlugin + * @device: a device + * + * Call the device_registered routine for the plugin + * + * Since: 0.9.7 + **/ +void +fu_plugin_runner_device_register(FuPlugin *self, FuDevice *device) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return; + + /* optional */ + if (vfuncs->device_registered != NULL) { + g_debug("fu_plugin_device_registered(%s)", fu_plugin_get_name(self)); + vfuncs->device_registered(self, device); + } +} + +/** + * fu_plugin_runner_device_created: + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Call the device_created routine for the plugin + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.4.0 + **/ +gboolean +fu_plugin_runner_device_created(FuPlugin *self, FuDevice *device, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->device_created == NULL) + return TRUE; + g_debug("fu_plugin_device_created(%s)", fu_plugin_get_name(self)); + return vfuncs->device_created(self, device, error); +} + +/** + * fu_plugin_runner_verify: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @flags: verify flags + * @error: (nullable): optional return location for an error + * + * Call into the plugin's verify routine + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_runner_verify(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FuPluginVerifyFlags flags, + GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + GPtrArray *checksums; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->verify == NULL) { + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device %s does not support verification", + fu_device_get_id(device)); + return FALSE; + } + return fu_plugin_device_read_firmware(self, device, progress, error); + } + + /* 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_progress( + self, + device, + progress, + "fu_plugin_detach", + vfuncs->detach != NULL ? vfuncs->detach : fu_plugin_device_detach, + error)) + return FALSE; + + /* run vfunc */ + g_debug("verify(%s)", fu_plugin_get_name(self)); + if (!vfuncs->verify(self, device, progress, flags, &error_local)) { + g_autoptr(GError) error_attach = NULL; + if (error_local == NULL) { + g_critical("unset plugin error in verify(%s)", fu_plugin_get_name(self)); + 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: ", + fu_plugin_get_name(self)); + /* make the device "work" again, but don't prefix the error */ + if (!fu_plugin_runner_device_generic_progress( + self, + device, + progress, + "fu_plugin_attach", + vfuncs->attach != NULL ? vfuncs->attach : fu_plugin_device_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_progress( + self, + device, + progress, + "fu_plugin_attach", + vfuncs->attach != NULL ? vfuncs->attach : fu_plugin_device_attach, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +/** + * fu_plugin_runner_activate: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Call into the plugin's activate routine + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.2.6 + **/ +gboolean +fu_plugin_runner_activate(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + guint64 flags; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* 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 */ + fu_device_add_backend_tag(device, "activate"); + if (!fu_plugin_runner_device_generic_progress( + self, + device, + progress, + "fu_plugin_activate", + vfuncs->activate != NULL ? vfuncs->activate : fu_plugin_device_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; +} + +/** + * fu_plugin_runner_unlock: + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Call into the plugin's unlock routine + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_runner_unlock(FuPlugin *self, FuDevice *device, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + guint64 flags; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* final check */ + flags = fu_device_get_flags(device); + if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s is not locked", + fu_device_get_id(device)); + return FALSE; + } + + /* run vfunc */ + fu_device_add_backend_tag(device, "unlock"); + if (!fu_plugin_runner_device_generic(self, + device, + "fu_plugin_unlock", + vfuncs->unlock, + error)) + return FALSE; + + /* update with correct flags */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_LOCKED); + fu_device_set_modified(device, (guint64)g_get_real_time() / G_USEC_PER_SEC); + return TRUE; +} + +/** + * fu_plugin_runner_write_firmware: + * @self: a #FuPlugin + * @device: a device + * @blob_fw: a data blob + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Call into the plugin's write firmware routine + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_runner_write_firmware(FuPlugin *self, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) { + g_debug("plugin not enabled, skipping"); + return TRUE; + } + fu_device_add_backend_tag(device, "write-firmware"); + + /* optional */ + if (vfuncs->write_firmware == NULL) { + g_debug("superclassed write_firmware(%s)", fu_plugin_get_name(self)); + return fu_plugin_device_write_firmware(self, + device, + blob_fw, + progress, + flags, + error); + } + + /* online */ + if (!vfuncs->write_firmware(self, device, blob_fw, progress, flags, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in update(%s)", fu_plugin_get_name(self)); + g_set_error_literal(&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + return FALSE; + } + fu_device_set_update_error(device, error_local->message); + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* no longer valid */ + 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); + } + + /* success */ + return TRUE; +} + +/** + * fu_plugin_runner_clear_results: + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Call into the plugin's clear results routine + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_runner_clear_results(FuPlugin *self, FuDevice *device, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->clear_results == NULL) + return TRUE; + g_debug("clear_result(%s)", fu_plugin_get_name(self)); + if (!vfuncs->clear_results(self, device, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in clear_result(%s)", + fu_plugin_get_name(self)); + 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: ", + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +/** + * fu_plugin_runner_get_results: + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Call into the plugin's get results routine + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 0.8.0 + **/ +gboolean +fu_plugin_runner_get_results(FuPlugin *self, FuDevice *device, GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) + return TRUE; + + /* optional */ + if (vfuncs->get_results == NULL) { + g_debug("superclassed get_results(%s)", fu_plugin_get_name(self)); + return fu_plugin_device_get_results(self, device, error); + } + g_debug("get_results(%s)", fu_plugin_get_name(self)); + if (!vfuncs->get_results(self, device, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in get_results(%s)", + fu_plugin_get_name(self)); + 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: ", + fu_plugin_get_name(self)); + return FALSE; + } + return TRUE; +} + +/** + * fu_plugin_get_order: + * @self: a #FuPlugin + * + * Gets the plugin order, where higher numbers are run after lower + * numbers. + * + * Returns: the integer value + * + * Since: 1.0.0 + **/ +guint +fu_plugin_get_order(FuPlugin *self) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + return priv->order; +} + +/** + * fu_plugin_set_order: + * @self: a #FuPlugin + * @order: an integer value + * + * Sets the plugin order, where higher numbers are run after lower + * numbers. + * + * Since: 1.0.0 + **/ +void +fu_plugin_set_order(FuPlugin *self, guint order) +{ + 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 + * + * Since: 1.1.1 + **/ +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: an integer value + * + * Sets the plugin priority, where higher numbers are better. + * + * Since: 1.0.0 + **/ +void +fu_plugin_set_priority(FuPlugin *self, guint priority) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + priv->priority = priority; +} + +/** + * fu_plugin_add_rule: + * @self: a #FuPlugin + * @rule: a plugin rule, e.g. %FU_PLUGIN_RULE_CONFLICTS + * @name: a plugin name, e.g. `upower` + * + * If the plugin name is found, the rule will be used to sort the plugin list, + * for example the plugin specified by @name will be ordered after this plugin + * when %FU_PLUGIN_RULE_RUN_AFTER is used. + * + * NOTE: The depsolver is iterative and may not solve overly-complicated rules; + * If depsolving fails then fwupd will not start. + * + * Since: 1.0.0 + **/ +void +fu_plugin_add_rule(FuPlugin *self, FuPluginRule rule, const gchar *name) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + if (priv->rules[rule] == NULL) + priv->rules[rule] = g_ptr_array_new_with_free_func(g_free); + g_ptr_array_add(priv->rules[rule], g_strdup(name)); + g_signal_emit(self, signals[SIGNAL_RULES_CHANGED], 0); +} + +/** + * fu_plugin_get_rules: + * @self: a #FuPlugin + * @rule: a plugin rule, e.g. %FU_PLUGIN_RULE_CONFLICTS + * + * Gets the plugin IDs that should be run after this plugin. + * + * Returns: (element-type utf8) (transfer none) (nullable): the list of plugin names, e.g. + *`['appstream']` + * + * Since: 1.0.0 + **/ +GPtrArray * +fu_plugin_get_rules(FuPlugin *self, FuPluginRule rule) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + g_return_val_if_fail(rule < FU_PLUGIN_RULE_LAST, NULL); + return priv->rules[rule]; +} + +/** + * fu_plugin_has_rule: + * @self: a #FuPlugin + * @rule: a plugin rule, 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 + * + * Since: 1.0.0 + **/ +gboolean +fu_plugin_has_rule(FuPlugin *self, FuPluginRule rule, const gchar *name) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + if (priv->rules[rule] == NULL) + return FALSE; + 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: + * @self: a #FuPlugin + * @key: a string, e.g. `FwupdateVersion` + * @value: a string, e.g. `10` + * + * Sets any additional metadata to be included in the firmware report to aid + * debugging problems. + * + * Any data included here will be sent to the metadata server after user + * confirmation. + * + * Since: 1.0.4 + **/ +void +fu_plugin_add_report_metadata(FuPlugin *self, const gchar *key, const gchar *value) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + if (priv->report_metadata == NULL) { + priv->report_metadata = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + } + g_hash_table_insert(priv->report_metadata, g_strdup(key), g_strdup(value)); +} + +/** + * fu_plugin_get_report_metadata: + * @self: a #FuPlugin + * + * Returns the list of additional metadata to be added when filing a report. + * + * Returns: (transfer none) (nullable): the map of report metadata + * + * Since: 1.0.4 + **/ +GHashTable * +fu_plugin_get_report_metadata(FuPlugin *self) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + return priv->report_metadata; +} + +/** + * fu_plugin_get_config_value: + * @self: a #FuPlugin + * @key: a settings key + * + * Return the value of a key if it's been configured + * + * Since: 1.0.6 + **/ +gchar * +fu_plugin_get_config_value(FuPlugin *self, const gchar *key) +{ + g_autofree gchar *conf_path = fu_plugin_get_config_filename(self); + g_autoptr(GKeyFile) keyfile = NULL; + if (!g_file_test(conf_path, G_FILE_TEST_IS_REGULAR)) + return NULL; + keyfile = g_key_file_new(); + if (!g_key_file_load_from_file(keyfile, conf_path, G_KEY_FILE_NONE, NULL)) + return NULL; + return g_key_file_get_string(keyfile, fu_plugin_get_name(self), key, NULL); +} + +/** + * fu_plugin_security_attr_new: + * @self: a #FuPlugin + * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Creates a new #FwupdSecurityAttr for this specific plugin. + * + * Returns: (transfer full): a #FwupdSecurityAttr + * + * Since: 1.8.4 + **/ +FwupdSecurityAttr * +fu_plugin_security_attr_new(FuPlugin *self, const gchar *appstream_id) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private(self); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), NULL); + g_return_val_if_fail(appstream_id != NULL, NULL); + + attr = fu_security_attr_new(priv->ctx, appstream_id); + fwupd_security_attr_set_plugin(attr, fu_plugin_get_name(self)); + return g_steal_pointer(&attr); +} + +#if !GLIB_CHECK_VERSION(2, 66, 0) + +#define G_FILE_SET_CONTENTS_CONSISTENT 0 +typedef guint GFileSetContentsFlags; +static gboolean +g_file_set_contents_full(const gchar *filename, + const gchar *contents, + gssize length, + GFileSetContentsFlags flags, + int mode, + GError **error) +{ + gint fd; + gssize wrote; + + if (length < 0) + length = strlen(contents); + fd = g_open(filename, O_CREAT, mode); + if (fd <= 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not open %s file", + filename); + return FALSE; + } + wrote = write(fd, contents, length); + if (wrote != length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "did not write %s file", + filename); + g_close(fd, NULL); + return FALSE; + } + return g_close(fd, error); +} +#endif + +static gboolean +fu_plugin_set_config_value_internal(FuPlugin *self, + const gchar *key, + const gchar *value, + guint32 mode, + GError **error) +{ + g_autofree gchar *conf_path = fu_plugin_get_config_filename(self); + g_autofree gchar *data = NULL; + g_autoptr(GKeyFile) keyfile = g_key_file_new(); + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!g_file_test(conf_path, G_FILE_TEST_EXISTS)) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "%s is missing", conf_path); + return FALSE; + } + if (!g_key_file_load_from_file(keyfile, conf_path, G_KEY_FILE_KEEP_COMMENTS, error)) + return FALSE; + g_key_file_set_string(keyfile, fu_plugin_get_name(self), key, value); + data = g_key_file_to_data(keyfile, NULL, error); + if (data == NULL) + return FALSE; + return g_file_set_contents_full(conf_path, + data, + -1, + G_FILE_SET_CONTENTS_CONSISTENT, + mode, + error); +} + +/** + * fu_plugin_set_config_value: + * @self: a #FuPlugin + * @key: a settings key + * @value: (nullable): a settings value + * @error: (nullable): optional return location for an error + * + * Sets a plugin config value. + * + * Returns: %TRUE for success + * + * Since: 1.7.0 + **/ +gboolean +fu_plugin_set_config_value(FuPlugin *self, const gchar *key, const gchar *value, GError **error) +{ + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(key != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* when removing fu_plugin_set_secure_config_value() just remove this instead */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_SECURE_CONFIG)) { + return fu_plugin_set_config_value_internal(self, + key, + value, + FU_PLUGIN_FILE_MODE_SECURE, + error); + } + return fu_plugin_set_config_value_internal(self, + key, + value, + FU_PLUGIN_FILE_MODE_NONSECURE, + error); +} + +/** + * fu_plugin_get_config_value_boolean: + * @self: a #FuPlugin + * @key: a settings key + * + * Return the boolean value of a key if it's been configured + * + * Returns: %TRUE if the value is `true` (case insensitive), %FALSE otherwise + * + * Since: 1.4.0 + **/ +gboolean +fu_plugin_get_config_value_boolean(FuPlugin *self, const gchar *key) +{ + g_autofree gchar *tmp = fu_plugin_get_config_value(self, key); + if (tmp == NULL) + return FALSE; + return g_ascii_strcasecmp(tmp, "true") == 0; +} + +/** + * fu_plugin_name_compare: + * @plugin1: first #FuPlugin to compare. + * @plugin2: second #FuPlugin to compare. + * + * Compares two plugins by their names. + * + * Returns: 1, 0 or -1 if @plugin1 is greater, equal, or less than @plugin2. + * + * Since: 1.0.8 + **/ +gint +fu_plugin_name_compare(FuPlugin *plugin1, FuPlugin *plugin2) +{ + return g_strcmp0(fu_plugin_get_name(plugin1), fu_plugin_get_name(plugin2)); +} + +/** + * fu_plugin_order_compare: + * @plugin1: first #FuPlugin to compare. + * @plugin2: second #FuPlugin to compare. + * + * Compares two plugins by their depsolved order, and then by name. + * + * Returns: 1, 0 or -1 if @plugin1 is greater, equal, or less than @plugin2. + * + * Since: 1.0.8 + **/ +gint +fu_plugin_order_compare(FuPlugin *plugin1, FuPlugin *plugin2) +{ + FuPluginPrivate *priv1 = fu_plugin_get_instance_private(plugin1); + FuPluginPrivate *priv2 = fu_plugin_get_instance_private(plugin2); + if (priv1->order < priv2->order) + return -1; + if (priv1->order > priv2->order) + return 1; + return fu_plugin_name_compare(plugin1, plugin2); +} + +static gchar * +fu_plugin_convert_gtype_to_name(GType gtype) +{ + const gchar *gtype_name = g_type_name(gtype); + gsize len = strlen(gtype_name); + g_autoptr(GString) str = g_string_new(NULL); + + g_return_val_if_fail(g_str_has_prefix(gtype_name, "Fu"), NULL); + g_return_val_if_fail(g_str_has_suffix(gtype_name, "Plugin"), NULL); + + /* self tests */ + if (g_strcmp0(gtype_name, "FuPlugin") == 0) + return g_strdup("plugin"); + + /* normal plugins */ + for (guint j = 2; j < len - 6; j++) { + gchar tmp = gtype_name[j]; + if (g_ascii_isupper(tmp)) { + if (str->len > 0) + g_string_append_c(str, '_'); + g_string_append_c(str, g_ascii_tolower(tmp)); + } else { + g_string_append_c(str, tmp); + } + } + if (str->len == 0) + return NULL; + return g_string_free(g_steal_pointer(&str), FALSE); +} + +static void +fu_plugin_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuPlugin *self = FU_PLUGIN(object); + FuPluginPrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_CONTEXT: + g_value_set_object(value, priv->ctx); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_plugin_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuPlugin *self = FU_PLUGIN(object); + switch (prop_id) { + case PROP_CONTEXT: + fu_plugin_set_context(self, g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_plugin_class_init(FuPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + object_class->finalize = fu_plugin_finalize; + object_class->get_property = fu_plugin_get_property; + object_class->set_property = fu_plugin_set_property; + + /** + * FuPlugin::device-added: + * @self: the #FuPlugin instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-added signal is emitted when a device has been added by the plugin. + * + * Since: 0.8.0 + **/ + signals[SIGNAL_DEVICE_ADDED] = g_signal_new("device-added", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuPluginClass, _device_added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuPlugin::device-removed: + * @self: the #FuPlugin instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-removed signal is emitted when a device has been removed by the plugin. + * + * Since: 0.8.0 + **/ + signals[SIGNAL_DEVICE_REMOVED] = + g_signal_new("device-removed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuPluginClass, _device_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuPlugin::device-register: + * @self: the #FuPlugin instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-register signal is emitted when another plugin has added the device. + * + * Since: 0.9.7 + **/ + signals[SIGNAL_DEVICE_REGISTER] = + g_signal_new("device-register", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuPluginClass, _device_register), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuPlugin::check-supported: + * @self: the #FuPlugin instance that emitted the signal + * @guid: a device GUID + * + * The ::check-supported signal is emitted when a plugin wants to ask the daemon if a + * specific device GUID is supported in the existing system metadata. + * + * Returns: %TRUE if the GUID is found + * + * Since: 1.0.0 + **/ + 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); + /** + * FuPlugin::config-changed: + * @self: the #FuPlugin instance that emitted the signal + * + * The ::config-changed signal is emitted when one or more config files have changed which + * may affect how the daemon should be run. + * + * Since: 1.7.0 + **/ + signals[SIGNAL_CONFIG_CHANGED] = + g_signal_new("config-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuPluginClass, _config_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * FuPlugin:context: + * + * The #FuContext to use. + * + * Since: 1.8.6 + */ + pspec = g_param_spec_object("context", + NULL, + NULL, + FU_TYPE_CONTEXT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_CONTEXT, pspec); +} + +static void +fu_plugin_init(FuPlugin *self) +{ + FuPluginPrivate *priv = GET_PRIVATE(self); + g_rw_lock_init(&priv->cache_mutex); +} + +static void +fu_plugin_finalize(GObject *object) +{ + FuPlugin *self = FU_PLUGIN(object); + FuPluginPrivate *priv = GET_PRIVATE(self); + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + + g_rw_lock_clear(&priv->cache_mutex); + + /* optional */ + if (priv->done_init && vfuncs->finalize != NULL) { + g_debug("finalize(%s)", fu_plugin_get_name(self)); + vfuncs->finalize(G_OBJECT(self)); + } + + for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) { + if (priv->rules[i] != NULL) + g_ptr_array_unref(priv->rules[i]); + } + if (priv->devices != NULL) + g_ptr_array_unref(priv->devices); + if (priv->ctx != NULL) + g_object_unref(priv->ctx); + if (priv->runtime_versions != NULL) + g_hash_table_unref(priv->runtime_versions); + if (priv->compile_versions != NULL) + g_hash_table_unref(priv->compile_versions); + if (priv->report_metadata != NULL) + g_hash_table_unref(priv->report_metadata); + if (priv->cache != NULL) + g_hash_table_unref(priv->cache); + if (priv->device_gtypes != NULL) + g_array_unref(priv->device_gtypes); + if (priv->config_monitor != NULL) + g_object_unref(priv->config_monitor); + g_free(priv->data); + + G_OBJECT_CLASS(fu_plugin_parent_class)->finalize(object); +} + +/** + * fu_plugin_new_from_gtype: + * @ctx: (nullable): a #FuContext + * @gtype: a #GType, possibly even `G_TYPE_PLUGIN` + * + * Creates a new #FuPlugin + * + * Since: 1.8.6 + **/ +FuPlugin * +fu_plugin_new_from_gtype(GType gtype, FuContext *ctx) +{ + FuPlugin *self; + + g_return_val_if_fail(gtype != G_TYPE_INVALID, NULL); + g_return_val_if_fail(ctx == NULL || FU_IS_CONTEXT(ctx), NULL); + + self = g_object_new(gtype, "context", ctx, NULL); + if (fu_plugin_get_name(self) == NULL) { + g_autofree gchar *name = fu_plugin_convert_gtype_to_name(gtype); + fu_plugin_set_name(self, name); + } + return self; +} + +/** + * fu_plugin_new: + * @ctx: (nullable): a #FuContext + * + * Creates a new #FuPlugin + * + * Since: 0.8.0 + **/ +FuPlugin * +fu_plugin_new(FuContext *ctx) +{ + FuPlugin *self = FU_PLUGIN(g_object_new(FU_TYPE_PLUGIN, NULL)); + if (ctx != NULL) + fu_plugin_set_context(self, ctx); + return self; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-plugin.h b/fwupd-1.8.6/libfwupdplugin/fu-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..03267391b61df78b446c61a83d72fdfa863e6685 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-plugin.h @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#ifdef HAVE_GUSB +#include +#endif + +#include "fu-bluez-device.h" +#include "fu-common-guid.h" +#include "fu-common.h" +#include "fu-context.h" +#include "fu-device-locker.h" +#include "fu-device.h" +#include "fu-hwids.h" +#include "fu-plugin.h" +#include "fu-quirks.h" +#include "fu-security-attrs.h" +#include "fu-usb-device.h" +#include "fu-version-common.h" +//#include "fu-hid-device.h" +#ifdef HAVE_GUDEV +#include "fu-udev-device.h" +#endif +#include +#include + +/* only until HSI is declared stable */ +#include "fwupd-security-attr-private.h" + +#define FU_TYPE_PLUGIN (fu_plugin_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuPlugin, fu_plugin, FU, PLUGIN, FwupdPlugin) + +#define fu_plugin_get_flags(p) fwupd_plugin_get_flags(FWUPD_PLUGIN(p)) +#define fu_plugin_has_flag(p, f) fwupd_plugin_has_flag(FWUPD_PLUGIN(p), f) +#define fu_plugin_add_flag(p, f) fwupd_plugin_add_flag(FWUPD_PLUGIN(p), f) +#define fu_plugin_remove_flag(p, f) fwupd_plugin_remove_flag(FWUPD_PLUGIN(p), f) + +/** + * FuPluginVerifyFlags: + * @FU_PLUGIN_VERIFY_FLAG_NONE: No flags set + * + * Flags used when verifying, currently unused. + **/ +typedef enum { + FU_PLUGIN_VERIFY_FLAG_NONE = 0, + /*< private >*/ + FU_PLUGIN_VERIFY_FLAG_LAST +} FuPluginVerifyFlags; + +struct _FuPluginClass { + FwupdPluginClass parent_class; + /* signals */ + void (*_device_added)(FuPlugin *self, FuDevice *device); + void (*_device_removed)(FuPlugin *self, FuDevice *device); + void (*_status_changed)(FuPlugin *self, FwupdStatus status); + void (*_percentage_changed)(FuPlugin *self, guint percentage); + void (*_device_register)(FuPlugin *self, FuDevice *device); + gboolean (*_check_supported)(FuPlugin *self, const gchar *guid); + void (*_rules_changed)(FuPlugin *self); + void (*_config_changed)(FuPlugin *self); + + /* vfuncs */ + /** + * init: + * @self: A #FuPlugin + * + * Initializes the modular plugin. + * Sets up any static data structures for the plugin. + * + * Since: 1.7.2 + **/ + void (*constructed)(GObject *obj); + /** + * finalize: + * @self: a plugin + * + * Destroys the modular plugin. + * Any allocated memory should be freed here. + * + * Since: 1.7.2 + **/ + void (*finalize)(GObject *obj); + /** + * startup: + * @self: a #FuPlugin + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Tries to start the plugin. + * Returns: TRUE for success or FALSE for failure. + * + * Any plugins not intended for the system or that have failure communicating + * with the device should return FALSE. + * Any allocated memory should be freed here. + * + * Since: 1.7.2 + **/ + gboolean (*startup)(FuPlugin *self, FuProgress *progress, GError **error); + /** + * coldplug: + * @self: a #FuPlugin + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Probes for devices. + * + * Since: 1.7.2 + **/ + gboolean (*coldplug)(FuPlugin *self, FuProgress *progress, GError **error); + /** + * device_created + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Function run when the subclassed device has been created. + * + * Since: 1.7.2 + **/ + gboolean (*device_created)(FuPlugin *self, FuDevice *device, GError **error); + /** + * device_registered + * @self: a #FuPlugin + * @dev: a device + * + * Function run when device registered from another plugin. + * + * Since: 1.7.2 + **/ + void (*device_registered)(FuPlugin *self, FuDevice *device); + /** + * device_added + * @self: a #FuPlugin + * @dev: a device + * + * Function run when the subclassed device has been added. + * + * Since: 1.7.2 + **/ + void (*device_added)(FuPlugin *self, FuDevice *device); + /** + * verify: + * @self: a #FuPlugin + * @dev: a device + * @progress: a #FuProgress + * @flags: verify flags + * @error: (nullable): optional return location for an error + * + * Verifies the firmware on the device matches the value stored in the database + * + * Since: 1.7.2 + **/ + gboolean (*verify)(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FuPluginVerifyFlags flags, + GError **error); + /** + * get_results: + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Obtains historical update results for the device. + * + * Since: 1.7.2 + **/ + gboolean (*get_results)(FuPlugin *self, FuDevice *device, GError **error); + /** + * clear_results: + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Clears stored update results for the device. + * + * Since: 1.7.2 + **/ + gboolean (*clear_results)(FuPlugin *self, FuDevice *device, GError **error); + /** + * backend_device_added + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Function to run after a device is added by a backend, e.g. by USB or Udev. + * + * Since: 1.7.2 + **/ + gboolean (*backend_device_added)(FuPlugin *self, FuDevice *device, GError **error); + /** + * backend_device_changed + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Function run when the device changed. + * + * Since: 1.7.2 + **/ + gboolean (*backend_device_changed)(FuPlugin *self, FuDevice *device, GError **error); + /** + * backend_device_removed + * @self: a #FuPlugin + * @device: a device + * @error: (nullable): optional return location for an error + * + * Function to run when device is physically removed. + * + * Since: 1.7.2 + **/ + gboolean (*backend_device_removed)(FuPlugin *self, FuDevice *device, GError **error); + /** + * add_security_attrs + * @self: a #FuPlugin + * @attrs: a security attribute + * + * Function that asks plugins to add Host Security Attributes. + * + * Since: 1.7.2 + **/ + void (*add_security_attrs)(FuPlugin *self, FuSecurityAttrs *attrs); + /** + * write_firmware: + * @self: a #FuPlugin + * @dev: a device + * @blob_fw: a data blob + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Updates the firmware on the device with blob_fw + * + * Since: 1.7.2 + **/ + gboolean (*write_firmware)(FuPlugin *self, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); + /** + * unlock: + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Unlocks the device for writes. + * + * Since: 1.7.2 + **/ + gboolean (*unlock)(FuPlugin *self, FuDevice *device, GError **error); + /** + * activate: + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Activates the new firmware on the device. + * + * This is intended for devices that it is not safe to immediately activate + * the firmware. It may be called at a more convenient time instead. + * + * Since: 1.7.2 + **/ + gboolean (*activate)(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + GError **error); + /** + * attach: + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Swaps the device from bootloader mode to runtime mode. + * + * Since: 1.7.2 + **/ + gboolean (*attach)(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error); + /** + * detach: + * @self: a #FuPlugin + * @dev: a device + * @error: (nullable): optional return location for an error + * + * Swaps the device from runtime mode to bootloader mode. + * + * Since: 1.7.2 + **/ + gboolean (*detach)(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error); + /** + * prepare: + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Prepares the device to receive an update. + * + * Since: 1.7.2 + **/ + gboolean (*prepare)(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); + /** + * cleanup + * @self: a #FuPlugin + * @device: a device + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Cleans up the device after receiving an update. + * + * Since: 1.7.2 + **/ + gboolean (*cleanup)(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); + /** + * composite_prepare + * @self: a #FuPlugin + * @devices: (element-type FuDevice): array of devices + * @error: (nullable): optional return location for an error + * + * Function run before updating group of composite devices. + * + * Since: 1.7.2 + **/ + gboolean (*composite_prepare)(FuPlugin *self, GPtrArray *devices, GError **error); + /** + * composite_cleanup + * @self: a #FuPlugin + * @devices: (element-type FuDevice): array of devices + * @error: (nullable): optional return location for an error + * + * Function run after updating group of composite devices. + * + * Since: 1.7.2 + **/ + gboolean (*composite_cleanup)(FuPlugin *self, GPtrArray *devices, GError **error); + /** + * load + * @ctx: a #FuContext + * + * Function to register context attributes, run during early startup even on plugins which + * will be later disabled. + * + * Since: 1.8.1 + **/ + void (*load)(FuContext *ctx); + /** + * to_string: + * @self: A #FuPlugin + * + * Prints plugin private data to the console. + * + * Since: 1.8.4 + **/ + void (*to_string)(FuPlugin *self, guint idt, GString *str); +}; + +/** + * FuPluginVfuncs: + * + * A subset of virtual functions that are implemented by modular plugins. + **/ +typedef struct _FuPluginClass FuPluginVfuncs; + +/** + * FuPluginRule: + * @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_BETTER_THAN: Is better than another plugin + * @FU_PLUGIN_RULE_INHIBITS_IDLE: The plugin inhibits the idle shutdown + * @FU_PLUGIN_RULE_METADATA_SOURCE: Uses another plugin as a source of report metadata + * + * The rules used for ordering plugins. + * Plugins are expected to add rules in fu_plugin_initialize(). + **/ +typedef enum { + FU_PLUGIN_RULE_CONFLICTS, + FU_PLUGIN_RULE_RUN_AFTER, + FU_PLUGIN_RULE_RUN_BEFORE, + FU_PLUGIN_RULE_BETTER_THAN, + FU_PLUGIN_RULE_INHIBITS_IDLE, + FU_PLUGIN_RULE_METADATA_SOURCE, /* Since: 1.3.6 */ + /*< private >*/ + FU_PLUGIN_RULE_LAST +} FuPluginRule; + +/** + * FuPluginData: + * + * The plugin-allocated private data. + **/ +typedef struct FuPluginData FuPluginData; + +/* for plugins to use */ +const gchar * +fu_plugin_get_name(FuPlugin *self); +void +fu_plugin_set_name(FuPlugin *self, const gchar *name); +FuPluginData * +fu_plugin_get_data(FuPlugin *self); +FuPluginData * +fu_plugin_alloc_data(FuPlugin *self, gsize data_sz); +FuContext * +fu_plugin_get_context(FuPlugin *self); +void +fu_plugin_device_add(FuPlugin *self, FuDevice *device); +void +fu_plugin_device_remove(FuPlugin *self, FuDevice *device); +void +fu_plugin_device_register(FuPlugin *self, FuDevice *device); +void +fu_plugin_add_device_gtype(FuPlugin *self, GType device_gtype); +void +fu_plugin_add_firmware_gtype(FuPlugin *self, const gchar *id, GType gtype); +void +fu_plugin_add_udev_subsystem(FuPlugin *self, const gchar *subsystem); +gpointer +fu_plugin_cache_lookup(FuPlugin *self, const gchar *id); +void +fu_plugin_cache_remove(FuPlugin *self, const gchar *id); +void +fu_plugin_cache_add(FuPlugin *self, const gchar *id, gpointer dev); +GPtrArray * +fu_plugin_get_devices(FuPlugin *self); +void +fu_plugin_add_rule(FuPlugin *self, FuPluginRule rule, const gchar *name); +void +fu_plugin_add_report_metadata(FuPlugin *self, const gchar *key, const gchar *value); +gchar * +fu_plugin_get_config_value(FuPlugin *self, const gchar *key); +gboolean +fu_plugin_get_config_value_boolean(FuPlugin *self, const gchar *key); +gboolean +fu_plugin_set_config_value(FuPlugin *self, const gchar *key, const gchar *value, GError **error); +FwupdSecurityAttr * +fu_plugin_security_attr_new(FuPlugin *self, const gchar *appstream_id); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-progress.c b/fwupd-1.8.6/libfwupdplugin/fu-progress.c new file mode 100644 index 0000000000000000000000000000000000000000..4e1a256772baed09c6f706ac7046f9aba879c324 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-progress.c @@ -0,0 +1,1111 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuProgress" + +#include "config.h" + +#include + +#include "fu-progress.h" +#include "fu-string.h" + +/** + * FuProgress: + * + * Objects can use fu_progress_set_percentage() if the absolute percentage + * is known. Percentages should always go up, not down. + * + * Modules usually set the number of steps that are expected using + * fu_progress_set_steps() and then after each section is completed, + * the fu_progress_step_done() function should be called. This will automatically + * call fu_progress_set_percentage() with the correct values. + * + * #FuProgress allows sub-modules to be "chained up" to the parent module + * so that as the sub-module progresses, so does the parent. + * The child can be reused for each section, and chains can be deep. + * + * To get a child object, you should use [method@FuProgress.get_child]. and then + * use the result in any sub-process. You should ensure that the child + * is not re-used without calling fu_progress_step_done(). + * + * There are a few nice touches in this module, so that if a module only has + * one progress step, the child progress is used for parent updates. + * + * static void + * _do_something(FuProgress *self) + * { + * // setup correct number of steps + * fu_progress_set_steps(self, 2); + * + * // run a sub function + * _do_something_else1(fu_progress_get_child(self)); + * + * // this section done + * fu_progress_step_done(self); + * + * // run another sub function + * _do_something_else2(fu_progress_get_child(self)); + * + * // this progress done (all complete) + * fu_progress_step_done(self); + * } + * + * See also: [class@FuDevice] + */ + +typedef struct { + gchar *id; + gchar *name; + FuProgressFlags flags; + guint percentage; + FwupdStatus status; + GPtrArray *children; /* of FuProgress */ + gboolean profile; + gdouble duration; /* seconds */ + guint step_weighting; + GTimer *timer; + GTimer *timer_child; + guint step_now; + FuProgress *parent; /* no-ref */ +} FuProgressPrivate; + +enum { SIGNAL_PERCENTAGE_CHANGED, SIGNAL_STATUS_CHANGED, SIGNAL_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE_WITH_PRIVATE(FuProgress, fu_progress, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fu_progress_get_instance_private(o)) + +/** + * fu_progress_get_id: + * @self: a #FuProgress + * + * Return the id of the progress, which is normally set by the caller. + * + * Returns: progress ID + * + * Since: 1.7.0 + **/ +const gchar * +fu_progress_get_id(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), NULL); + return priv->id; +} + +/** + * fu_progress_set_id: + * @self: a #FuProgress + * @id: progress ID, normally `G_STRLOC` + * + * Sets the id of the progress. + * + * Since: 1.7.0 + **/ +void +fu_progress_set_id(FuProgress *self, const gchar *id) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(id != NULL); + + /* not changed */ + if (g_strcmp0(priv->id, id) == 0) + return; + + /* set id */ + g_free(priv->id); + priv->id = g_strdup(id); +} + +/** + * fu_progress_get_name: + * @self: a #FuProgress + * + * Return the nice name of the progress, which is normally set by the caller. + * + * Returns: progress nice name, e.g. `add-devices` + * + * Since: 1.8.2 + **/ +const gchar * +fu_progress_get_name(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), NULL); + return priv->name; +} + +static const gchar * +fu_progress_get_name_fallback(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + if (priv->name != NULL) + return priv->name; + return fwupd_status_to_string(priv->status); +} + +/** + * fu_progress_set_name: + * @self: a #FuProgress + * @name: progress nice name, e.g. `add-devices`, or perhaps just `G_STRFUNC` + * + * Sets the nice name of the progress. + * + * Since: 1.8.2 + **/ +void +fu_progress_set_name(FuProgress *self, const gchar *name) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(name != NULL); + + /* not changed */ + if (g_strcmp0(priv->name, name) == 0) + return; + + /* set name */ + g_free(priv->name); + priv->name = g_strdup(name); +} + +/** + * fu_progress_get_status: + * @self: a #FuProgress + * + * Return the status of the progress, which is normally indirectly by fu_progress_add_step(). + * + * Returns: status + * + * Since: 1.7.0 + **/ +FwupdStatus +fu_progress_get_status(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), FWUPD_STATUS_UNKNOWN); + return priv->status; +} + +/** + * fu_progress_flag_to_string: + * @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED + * + * Converts an progress flag to a string. + * + * Returns: identifier string + * + * Since: 1.7.0 + **/ +const gchar * +fu_progress_flag_to_string(FuProgressFlags flag) +{ + if (flag == FU_PROGRESS_FLAG_GUESSED) + return "guessed"; + if (flag == FU_PROGRESS_FLAG_NO_PROFILE) + return "no-profile"; + if (flag == FU_PROGRESS_FLAG_NO_TRACEBACK) + return "no-traceback"; + return NULL; +} + +/** + * fu_progress_flag_from_string: + * @flag: a string, e.g. `guessed` + * + * Converts a string to an progress flag. + * + * Returns: enumerated value + * + * Since: 1.7.0 + **/ +FuProgressFlags +fu_progress_flag_from_string(const gchar *flag) +{ + if (g_strcmp0(flag, "guessed") == 0) + return FU_PROGRESS_FLAG_GUESSED; + if (g_strcmp0(flag, "no-profile") == 0) + return FU_PROGRESS_FLAG_NO_PROFILE; + if (g_strcmp0(flag, "no-traceback") == 0) + return FU_PROGRESS_FLAG_NO_TRACEBACK; + return FU_PROGRESS_FLAG_UNKNOWN; +} + +/** + * fu_progress_add_flag: + * @self: a #FuProgress + * @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED + * + * Adds a flag. + * + * Since: 1.7.0 + **/ +void +fu_progress_add_flag(FuProgress *self, FuProgressFlags flag) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + priv->flags |= flag; +} + +/** + * fu_progress_remove_flag: + * @self: a #FuProgress + * @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED + * + * Removes a flag. + * + * Since: 1.7.0 + **/ +void +fu_progress_remove_flag(FuProgress *self, FuProgressFlags flag) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + priv->flags &= ~flag; +} + +/** + * fu_progress_has_flag: + * @self: a #FuProgress + * @flag: an internal progress flag, e.g. %FU_PROGRESS_FLAG_GUESSED + * + * Tests for a flag. + * + * Since: 1.7.0 + **/ +gboolean +fu_progress_has_flag(FuProgress *self, FuProgressFlags flag) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), FALSE); + return (priv->flags & flag) > 0; +} + +/** + * fu_progress_set_status: + * @self: a #FuProgress + * @status: device status + * + * Sets the status of the progress. + * + * Since: 1.7.0 + **/ +void +fu_progress_set_status(FuProgress *self, FwupdStatus status) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + + /* not changed */ + if (priv->status == status) + return; + + /* save */ + priv->status = status; + g_signal_emit(self, signals[SIGNAL_STATUS_CHANGED], 0, status); +} + +/** + * fu_progress_get_percentage: + * @self: a #FuProgress + * + * Get the last set progress percentage. + * + * Return value: The percentage value, or %G_MAXUINT for error + * + * Since: 1.7.0 + **/ +guint +fu_progress_get_percentage(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), G_MAXUINT); + if (priv->percentage == G_MAXUINT) + return 0; + return priv->percentage; +} + +static void +fu_progress_set_parent(FuProgress *self, FuProgress *parent) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(FU_IS_PROGRESS(parent)); + priv->parent = parent; /* no ref! */ + priv->profile = fu_progress_get_profile(parent); +} + +static guint +fu_progress_get_step_weighting(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + return priv->step_weighting; +} + +static void +fu_progress_set_step_weighting(FuProgress *self, guint step_weighting) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + priv->step_weighting = step_weighting; +} + +/** + * fu_progress_get_duration: + * @self: a #FuProgress + * + * Get the duration of the step. + * + * Return value: The duration value in seconds + * + * Since: 1.8.2 + **/ +gdouble +fu_progress_get_duration(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + return priv->duration; +} + +static void +fu_progress_set_duration(FuProgress *self, gdouble duration) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + priv->duration = duration; +} + +static void +fu_progress_build_parent_chain(FuProgress *self, GString *str, guint level) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + if (priv->parent != NULL) + fu_progress_build_parent_chain(priv->parent, str, level + 1); + g_string_append_printf(str, + "%u) %s (%u/%u)\n", + level, + priv->id, + priv->step_now, + priv->children->len); +} + +/** + * fu_progress_set_percentage: + * @self: a #FuProgress + * @percentage: value between 0% and 100% + * + * Sets the progress percentage complete. + * + * NOTE: this must be above what was previously set, or it will be rejected. + * + * Since: 1.7.0 + **/ +void +fu_progress_set_percentage(FuProgress *self, guint percentage) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(percentage <= 100); + + /* is it the same */ + if (percentage == priv->percentage) + return; + + /* is it less */ + if (priv->percentage != G_MAXUINT && percentage < priv->percentage) { + if (priv->profile) { + g_autoptr(GString) str = g_string_new(NULL); + fu_progress_build_parent_chain(self, str, 0); + g_warning("percentage should not go down from %u to %u: %s", + priv->percentage, + percentage, + str->str); + } + return; + } + + /* done */ + if (percentage == 100) + fu_progress_set_duration(self, g_timer_elapsed(priv->timer, NULL)); + + /* save */ + priv->percentage = percentage; + g_signal_emit(self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage); +} + +/** + * fu_progress_set_percentage_full: + * @self: a #FuDevice + * @progress_done: the bytes already done + * @progress_total: the total number of bytes + * + * Sets the progress completion using the raw progress values. + * + * Since: 1.7.0 + **/ +void +fu_progress_set_percentage_full(FuProgress *self, gsize progress_done, gsize progress_total) +{ + gdouble percentage = 0.f; + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(progress_done <= progress_total); + if (progress_total > 0) + percentage = (100.f * (gdouble)progress_done) / (gdouble)progress_total; + fu_progress_set_percentage(self, (guint)percentage); +} + +/** + * fu_progress_set_profile: + * @self: A #FuProgress + * @profile: if profiling should be enabled + * + * This enables profiling of FuProgress. This may be useful in development, + * but be warned; enabling profiling makes #FuProgress very slow. + * + * Since: 1.7.0 + **/ +void +fu_progress_set_profile(FuProgress *self, gboolean profile) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + priv->profile = profile; +} + +/** + * fu_progress_get_profile: + * @self: A #FuProgress + * + * Returns if the profile is enabled for this progress. + * + * Return value: if profiling has been enabled + * + * Since: 1.8.2 + **/ +gboolean +fu_progress_get_profile(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), FALSE); + return priv->profile; +} + +/** + * fu_progress_reset: + * @self: A #FuProgress + * + * Resets the #FuProgress object to unset + * + * Since: 1.7.0 + **/ +void +fu_progress_reset(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + + /* reset values */ + priv->step_now = 0; + priv->percentage = G_MAXUINT; + + /* only use the timer if profiling; it's expensive */ + if (priv->profile) { + g_timer_start(priv->timer); + g_timer_start(priv->timer_child); + } + + /* no more step data */ + g_ptr_array_set_size(priv->children, 0); +} + +/** + * fu_progress_set_steps: + * @self: A #FuProgress + * @step_max: The number of sub-tasks in this progress, can be 0 + * + * Sets the number of sub-tasks, i.e. how many times the fu_progress_step_done() + * function will be called in the loop. + * + * The progress ID must be set fu_progress_set_id() before this method is used. + * + * Since: 1.7.0 + **/ +void +fu_progress_set_steps(FuProgress *self, guint step_max) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(priv->id != NULL); + + /* create fake steps */ + for (guint i = 0; i < step_max; i++) + fu_progress_add_step(self, priv->status, 0, NULL); + + /* show that the sub-progress has been created */ + fu_progress_set_percentage(self, 0); + fu_progress_add_flag(self, FU_PROGRESS_FLAG_NO_PROFILE); + + /* reset child timer */ + g_timer_start(priv->timer_child); +} + +/** + * fu_progress_get_steps: + * @self: A #FuProgress + * + * Gets the number of sub-tasks, i.e. how many times the fu_progress_step_done() + * function will be called in the loop. + * + * Return value: number of sub-tasks in this progress + * + * Since: 1.7.0 + **/ +guint +fu_progress_get_steps(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_PROGRESS(self), G_MAXUINT); + return priv->children->len; +} + +static gdouble +fu_progress_discrete_to_percent(guint discrete, guint step_max) +{ + /* check we are in range */ + if (discrete > step_max) + return 100; + if (step_max == 0) { + g_warning("step_max is 0!"); + return 0; + } + return ((gdouble)discrete * (100.0f / (gdouble)(step_max))); +} + +static gdouble +fu_progress_get_step_percentage(FuProgress *self, guint idx) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + guint current = 0; + guint total = 0; + + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + if (i <= idx) + current += fu_progress_get_step_weighting(child); + total += fu_progress_get_step_weighting(child); + } + if (total == 0) + return -1; + return ((gdouble)current * 100.f) / (gdouble)total; +} + +static void +fu_progress_child_status_changed_cb(FuProgress *child, FwupdStatus status, FuProgress *self) +{ + fu_progress_set_status(self, status); +} + +static void +fu_progress_child_percentage_changed_cb(FuProgress *child, guint percentage, FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + gdouble offset; + gdouble range; + gdouble extra; + guint parent_percentage = G_MAXUINT; + + /* propagate up the stack if FuProgress has only one priv */ + if (priv->children->len == 1) { + fu_progress_set_percentage(self, percentage); + return; + } + + /* did we call done on a step that did not have a size set? */ + if (priv->children->len == 0) + return; + + /* already at >= 100% */ + if (priv->step_now >= priv->children->len) { + g_warning("already at %u/%u step_max", priv->step_now, priv->children->len); + return; + } + + /* if the child finished, set the status back to the last parent status */ + if (percentage == 100) { + FuProgress *child_tmp = g_ptr_array_index(priv->children, priv->step_now); + if (fu_progress_get_status(child_tmp) != FWUPD_STATUS_UNKNOWN) + fu_progress_set_status(self, fu_progress_get_status(child_tmp)); + } + + /* we don't store zero */ + if (priv->step_now == 0) { + gdouble pc = fu_progress_get_step_percentage(self, 0); + if (pc > 0) + parent_percentage = percentage * pc / 100; + } else { + gdouble pc1 = fu_progress_get_step_percentage(self, priv->step_now - 1); + gdouble pc2 = fu_progress_get_step_percentage(self, priv->step_now); + /* bi-linearly interpolate */ + if (pc1 > 0 && pc2 > 0) + parent_percentage = (((100 - percentage) * pc1) + (percentage * pc2)) / 100; + } + if (parent_percentage != G_MAXUINT) { + fu_progress_set_percentage(self, parent_percentage); + return; + } + + /* get the range between the parent priv and the next parent priv */ + offset = fu_progress_discrete_to_percent(priv->step_now, priv->children->len); + range = fu_progress_discrete_to_percent(priv->step_now + 1, priv->children->len) - offset; + if (range < 0.01) + return; + + /* get the extra contributed by the child */ + extra = ((gdouble)percentage / 100.0f) * range; + + /* emit from the parent */ + parent_percentage = (guint)(offset + extra); + fu_progress_set_percentage(self, parent_percentage); +} + +/** + * fu_progress_add_step: + * @self: A #FuProgress + * @status: status value to use for this phase + * @value: A step weighting variable argument array + * @name: (nullable): Human readable name to identify the step + * + * This sets the step weighting, which you will want to do if one action + * will take a bigger chunk of time than another. + * + * The progress ID must be set fu_progress_set_id() before this method is used. + * + * Since: 1.8.2 + **/ +void +fu_progress_add_step(FuProgress *self, FwupdStatus status, guint value, const gchar *name) +{ + g_autoptr(FuProgress) child = fu_progress_new(NULL); + FuProgressPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(priv->id != NULL); + + /* save data */ + fu_progress_set_status(child, status); + fu_progress_set_step_weighting(child, value); + + /* connect signals */ + g_signal_connect(FU_PROGRESS(child), + "percentage-changed", + G_CALLBACK(fu_progress_child_percentage_changed_cb), + self); + g_signal_connect(FU_PROGRESS(child), + "status-changed", + G_CALLBACK(fu_progress_child_status_changed_cb), + self); + fu_progress_set_parent(child, self); + if (name != NULL) + fu_progress_set_name(child, name); + + /* use first child status */ + if (priv->children->len == 0) + fu_progress_set_status(self, status); + + /* add child */ + g_ptr_array_add(priv->children, g_steal_pointer(&child)); + + /* reset child timer */ + g_timer_start(priv->timer_child); +} + +/** + * fu_progress_finished: + * @self: A #FuProgress + * + * Called when the step_now sub-task wants to finish early and still complete. + * + * Since: 1.7.0 + **/ +void +fu_progress_finished(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_PROGRESS(self)); + + /* is already at 100%? */ + if (priv->step_now == priv->children->len) + return; + + /* all done */ + priv->step_now = priv->children->len; + fu_progress_set_percentage(self, 100); + + /* we finished early, so invalidate children */ + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + fu_progress_add_flag(child, FU_PROGRESS_FLAG_NO_TRACEBACK); + } +} + +/** + * fu_progress_get_child: + * @self: A #FuProgress + * + * Monitor a child and proxy back up to the parent with the correct percentage. + * + * Return value: (transfer none): A new %FuProgress or %NULL for failure + * + * Since: 1.7.0 + **/ +FuProgress * +fu_progress_get_child(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_PROGRESS(self), NULL); + g_return_val_if_fail(priv->id != NULL, NULL); + g_return_val_if_fail(priv->children->len > 0, NULL); + g_return_val_if_fail(priv->children->len > priv->step_now, NULL); + + /* all preallocated, nothing to do */ + return FU_PROGRESS(g_ptr_array_index(priv->children, priv->step_now)); +} + +static void +fu_progress_show_profile(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + gdouble division; + gdouble total_time = 0.0f; + gboolean close_enough = TRUE; + g_autoptr(GString) str = NULL; + + /* not accurate enough for a profile result */ + if (priv->flags & FU_PROGRESS_FLAG_NO_PROFILE) + return; + + /* get the total time so we can work out the divisor */ + str = g_string_new("raw timing data was { "); + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + g_string_append_printf(str, "%.3f, ", fu_progress_get_duration(child)); + } + if (priv->children->len > 0) + g_string_set_size(str, str->len - 2); + g_string_append(str, " } -- "); + + /* get the total time so we can work out the divisor */ + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + total_time += fu_progress_get_duration(child); + } + if (total_time < 0.001) + return; + division = total_time / 100.0f; + + /* what we set */ + g_string_append(str, "steps were set as [ "); + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + g_string_append_printf(str, "%u ", fu_progress_get_step_weighting(child)); + } + + /* what we _should_ have set */ + g_string_append_printf(str, "] but should have been [ "); + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + g_string_append_printf(str, "%.0f ", fu_progress_get_duration(child) / division); + + /* this is sufficiently different to what we guessed */ + if (fabs((fu_progress_get_duration(child) / division) - + (gdouble)fu_progress_get_step_weighting(child)) > 5) { + close_enough = FALSE; + } + } + g_string_append(str, "]"); + if (priv->flags & FU_PROGRESS_FLAG_GUESSED) { +#ifdef SUPPORTED_BUILD + g_debug("%s at %s [%s]", str->str, priv->id, fu_progress_get_name_fallback(self)); +#else + g_warning("%s at %s [%s]", str->str, priv->id, fu_progress_get_name_fallback(self)); + g_warning("Please see " + "https://github.com/fwupd/fwupd/wiki/Daemon-Warning:-FuProgress-steps"); +#endif + } else if (!close_enough) { + g_debug("%s at %s", str->str, priv->id); + } +} + +/** + * fu_progress_step_done: + * @self: A #FuProgress + * + * Called when the step_now sub-task has finished. + * + * Since: 1.7.0 + **/ +void +fu_progress_step_done(FuProgress *self) +{ + FuProgress *child = NULL; + FuProgressPrivate *priv = GET_PRIVATE(self); + gdouble percentage; + + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(priv->id != NULL); + + /* did we call done when no size set? */ + if (priv->children->len == 0) { + g_autoptr(GString) str = g_string_new(NULL); + fu_progress_build_parent_chain(self, str, 0); + g_warning("progress done when no size set! [%s]: %s", priv->id, str->str); + return; + } + + /* get the active child */ + if (priv->children->len > 0) + child = g_ptr_array_index(priv->children, priv->step_now); + + /* save the duration in the array */ + if (priv->profile) { + if (child != NULL) + fu_progress_set_duration(child, g_timer_elapsed(priv->timer_child, NULL)); + g_timer_start(priv->timer_child); + } + + /* is already at 100%? */ + if (priv->step_now >= priv->children->len) { + g_autoptr(GString) str = g_string_new(NULL); + fu_progress_build_parent_chain(self, str, 0); + g_warning("already at 100%% [%s]: %s", priv->id, str->str); + return; + } + + /* is child not at 100%? */ + if (!fu_progress_has_flag(self, FU_PROGRESS_FLAG_CHILD_FINISHED) && child != NULL) { + FuProgressPrivate *child_priv = GET_PRIVATE(child); + if (child_priv->step_now != child_priv->children->len) { + g_autoptr(GString) str = g_string_new(NULL); + fu_progress_build_parent_chain(child, str, 0); + g_warning("child is at %u/%u step_max and parent done [%s]\n%s", + child_priv->step_now, + child_priv->children->len, + priv->id, + str->str); + /* do not abort, as we want to clean this up */ + } + } + + /* another */ + priv->step_now++; + + /* update status */ + if (priv->step_now < priv->children->len) { + FuProgress *child_tmp = g_ptr_array_index(priv->children, priv->step_now); + if (fu_progress_get_status(child_tmp) != FWUPD_STATUS_UNKNOWN) + fu_progress_set_status(self, fu_progress_get_status(child_tmp)); + } else if (priv->parent != NULL) { + fu_progress_set_status(self, fu_progress_get_status(priv->parent)); + } else { + fu_progress_set_status(self, FWUPD_STATUS_UNKNOWN); + } + + /* find new percentage */ + percentage = fu_progress_get_step_percentage(self, priv->step_now - 1); + if (percentage < 0) + percentage = fu_progress_discrete_to_percent(priv->step_now, priv->children->len); + fu_progress_set_percentage(self, (guint)percentage); + + /* show any profiling stats */ + if (priv->profile && priv->step_now == priv->children->len) + fu_progress_show_profile(self); +} + +/** + * fu_progress_sleep: + * @self: a #FuProgress + * @delay_ms: the delay in milliseconds + * + * Sleeps, setting the device progress from 0..100% as time continues. + * + * Since: 1.7.0 + **/ +void +fu_progress_sleep(FuProgress *self, guint delay_ms) +{ + gulong delay_us_pc = (delay_ms * 1000) / 100; + + g_return_if_fail(FU_IS_PROGRESS(self)); + g_return_if_fail(delay_ms > 0); + + fu_progress_set_percentage(self, 0); + for (guint i = 0; i < 100; i++) { + g_usleep(delay_us_pc); + fu_progress_set_percentage(self, i + 1); + } +} + +static void +fu_progress_traceback_cb(FuProgress *self, + guint idt, + guint child_idx, + guint threshold_ms, + GString *str) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + if (priv->flags & FU_PROGRESS_FLAG_NO_TRACEBACK) + return; + if (priv->children->len == 0 && fu_progress_get_duration(self) < 0.0001) + return; + if (threshold_ms == 0 || fu_progress_get_duration(self) * 1000 > threshold_ms) { + for (guint i = 0; i < idt; i++) + g_string_append(str, " "); + if (priv->id != NULL) + g_string_append(str, priv->id); + if (priv->name != NULL) + g_string_append_printf(str, ":%s", priv->name); + if (priv->id == NULL && priv->name == NULL && child_idx != G_MAXUINT) + g_string_append_printf(str, "@%u", child_idx); + g_string_append_printf(str, " [%.2fms]", fu_progress_get_duration(self) * 1000.f); + g_string_append(str, priv->children->len > 0 ? ":\n" : "\n"); + } + for (guint i = 0; i < priv->children->len; i++) { + FuProgress *child = g_ptr_array_index(priv->children, i); + fu_progress_traceback_cb(child, idt + 4, i, threshold_ms, str); + } +} + +/** + * fu_progress_traceback: + * @self: A #FuProgress + * + * Create a traceback used for profiling startup. + * + * Return value: (transfer full): string + * + * Since: 1.8.2 + **/ +gchar * +fu_progress_traceback(FuProgress *self) +{ + const gchar *tmp = g_getenv("FWUPD_PROFILE"); + guint64 threshold_ms = 5000; + g_autoptr(GString) str = g_string_new(NULL); + + /* allow override */ + if (tmp != NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_strtoull(tmp, &threshold_ms, 0, G_MAXUINT, &error_local)) + g_warning("invalid threshold value: %s", tmp); + } + + fu_progress_traceback_cb(self, 0, G_MAXUINT, threshold_ms, str); + if (str->len == 0) + return NULL; + return g_string_free(g_steal_pointer(&str), FALSE); +} + +static void +fu_progress_init(FuProgress *self) +{ + FuProgressPrivate *priv = GET_PRIVATE(self); + priv->status = FWUPD_STATUS_UNKNOWN; + priv->percentage = G_MAXUINT; + priv->timer = g_timer_new(); + priv->timer_child = g_timer_new(); + priv->children = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + priv->duration = 0.f; +} + +static void +fu_progress_finalize(GObject *object) +{ + FuProgress *self = FU_PROGRESS(object); + FuProgressPrivate *priv = GET_PRIVATE(self); + + /* show any profiling stats */ + if (priv->profile) + fu_progress_show_profile(self); + + fu_progress_reset(self); + g_free(priv->id); + g_free(priv->name); + g_ptr_array_unref(priv->children); + g_timer_destroy(priv->timer); + g_timer_destroy(priv->timer_child); + + G_OBJECT_CLASS(fu_progress_parent_class)->finalize(object); +} + +static void +fu_progress_class_init(FuProgressClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_progress_finalize; + + /** + * FuProgress::percentage-changed: + * @self: the #FuProgress instance that emitted the signal + * @percentage: the new value + * + * The ::percentage-changed signal is emitted when the tasks completion has changed. + * + * Since: 1.7.0 + **/ + signals[SIGNAL_PERCENTAGE_CHANGED] = + g_signal_new("percentage-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuProgressClass, percentage_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); + /** + * FuProgress::status-changed: + * @self: the #FuProgress instance that emitted the signal + * @status: the new #FwupdStatus + * + * The ::status-changed signal is emitted when the task status has changed. + * + * Since: 1.7.0 + **/ + signals[SIGNAL_STATUS_CHANGED] = + g_signal_new("status-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(FuProgressClass, status_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); +} + +/** + * fu_progress_new: + * @id: (nullable): progress ID, normally `G_STRLOC` + * + * Return value: A new #FuProgress instance. + * + * Since: 1.7.0 + **/ +FuProgress * +fu_progress_new(const gchar *id) +{ + FuProgress *self; + self = g_object_new(FU_TYPE_PROGRESS, NULL); + if (id != NULL) + fu_progress_set_id(self, id); + return FU_PROGRESS(self); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-progress.h b/fwupd-1.8.6/libfwupdplugin/fu-progress.h new file mode 100644 index 0000000000000000000000000000000000000000..a893f6f161fc7ee64a4f88c9ef0ba8636eeed2d9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-progress.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#define FU_TYPE_PROGRESS (fu_progress_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuProgress, fu_progress, FU, PROGRESS, GObject) + +struct _FuProgressClass { + GObjectClass parent_class; + /* signals */ + void (*percentage_changed)(FuProgress *self, guint value); + void (*status_changed)(FuProgress *self, FwupdStatus status); +}; + +/** + * FuProgressFlags: + * + * The progress internal flags. + **/ +typedef guint64 FuProgressFlags; + +/** + * FU_PROGRESS_FLAG_NONE: + * + * No flags set. + * + * Since: 1.7.0 + */ +#define FU_PROGRESS_FLAG_NONE (0) + +/** + * FU_PROGRESS_FLAG_UNKNOWN: + * + * Unknown flag value. + * + * Since: 1.7.0 + */ +#define FU_PROGRESS_FLAG_UNKNOWN G_MAXUINT64 + +/** + * FU_PROGRESS_FLAG_GUESSED: + * + * The steps have not been measured on real hardware and have been guessed. + * + * Since: 1.7.0 + */ +#define FU_PROGRESS_FLAG_GUESSED (1ull << 0) + +/** + * FU_PROGRESS_FLAG_NO_PROFILE: + * + * The steps cannot be accurate enough for a profile result. + * + * Since: 1.7.0 + */ +#define FU_PROGRESS_FLAG_NO_PROFILE (1ull << 1) + +/** + * FU_PROGRESS_FLAG_CHILD_FINISHED: + * + * The child completed all the expected steps. + * + * Since: 1.8.2 + */ +#define FU_PROGRESS_FLAG_CHILD_FINISHED (1ull << 2) + +/** + * FU_PROGRESS_FLAG_NO_TRACEBACK: + * + * The steps should not be shown in the traceback. + * + * Since: 1.8.2 + */ +#define FU_PROGRESS_FLAG_NO_TRACEBACK (1ull << 3) + +FuProgress * +fu_progress_new(const gchar *id); +const gchar * +fu_progress_get_id(FuProgress *self); +void +fu_progress_set_id(FuProgress *self, const gchar *id); +const gchar * +fu_progress_get_name(FuProgress *self); +void +fu_progress_set_name(FuProgress *self, const gchar *name); +const gchar * +fu_progress_flag_to_string(FuProgressFlags flag); +FuProgressFlags +fu_progress_flag_from_string(const gchar *flag); +void +fu_progress_add_flag(FuProgress *self, FuProgressFlags flag); +void +fu_progress_remove_flag(FuProgress *self, FuProgressFlags flag); +gboolean +fu_progress_has_flag(FuProgress *self, FuProgressFlags flag); +FwupdStatus +fu_progress_get_status(FuProgress *self); +void +fu_progress_set_status(FuProgress *self, FwupdStatus status); +void +fu_progress_set_percentage(FuProgress *self, guint percentage); +void +fu_progress_set_percentage_full(FuProgress *self, gsize progress_done, gsize progress_total); +guint +fu_progress_get_percentage(FuProgress *self); +gdouble +fu_progress_get_duration(FuProgress *self); +void +fu_progress_set_profile(FuProgress *self, gboolean profile); +gboolean +fu_progress_get_profile(FuProgress *self); +void +fu_progress_reset(FuProgress *self); +void +fu_progress_set_steps(FuProgress *self, guint step_max); +guint +fu_progress_get_steps(FuProgress *self); +void +fu_progress_add_step(FuProgress *self, FwupdStatus status, guint value, const gchar *name); +void +fu_progress_finished(FuProgress *self); +void +fu_progress_step_done(FuProgress *self); +FuProgress * +fu_progress_get_child(FuProgress *self); +void +fu_progress_sleep(FuProgress *self, guint delay_ms); +gchar * +fu_progress_traceback(FuProgress *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-quirks.c b/fwupd-1.8.6/libfwupdplugin/fu-quirks.c new file mode 100644 index 0000000000000000000000000000000000000000..cfa887d1de99fbdbb4066c1fc99872957a4fcfa6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-quirks.c @@ -0,0 +1,650 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuQuirks" + +#include "config.h" + +#include +#include + +#include "fwupd-common.h" +#include "fwupd-error.h" +#include "fwupd-remote-private.h" + +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-mutex.h" +#include "fu-path.h" +#include "fu-quirks.h" +#include "fu-string.h" + +/** + * FuQuirks: + * + * Quirks can be used to modify device behavior. + * When fwupd is installed in long-term support distros it's very hard to + * backport new versions as new hardware is released. + * + * There are several reasons why we can't just include the mapping and quirk + * information in the AppStream metadata: + * + * * The extra data is hugely specific to the installed fwupd plugin versions + * * The device-id is per-device, and the mapping is usually per-plugin + * * Often the information is needed before the FuDevice is created + * * There are security implications in allowing plugins to handle new devices + * + * The idea with quirks is that the end user can drop an additional (or replace + * an existing) file in a .d director with a simple format and the hardware will + * magically start working. This assumes no new quirks are required, as this would + * obviously need code changes, but allows us to get most existing devices working + * in an easy way without the user compiling anything. + * + * See also: [class@FuDevice], [class@FuPlugin] + */ + +static void +fu_quirks_finalize(GObject *obj); + +struct _FuQuirks { + GObject parent_instance; + FuQuirksLoadFlags load_flags; + GHashTable *possible_keys; + GPtrArray *invalid_keys; + XbSilo *silo; + XbQuery *query_kv; + XbQuery *query_vs; + gboolean verbose; +}; + +G_DEFINE_TYPE(FuQuirks, fu_quirks, G_TYPE_OBJECT) + +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]); + g_warning("using %s for %s in quirk files is deprecated!", + guid_prefixes[i], + group); + if (fwupd_guid_is_valid(group + len)) + return g_strdup(group + len); + return fwupd_guid_hash_string(group + len); + } + } + + /* fallback */ + if (fwupd_guid_is_valid(group)) + return g_strdup(group); + return fwupd_guid_hash_string(group); +} + +static gboolean +fu_quirks_validate_flags(const gchar *value, GError **error) +{ + if (value == NULL) + return FALSE; + for (gsize i = 0; value[i] != '\0'; i++) { + gchar tmp = value[i]; + + /* allowed special chars */ + if (tmp == ',' || tmp == '~' || tmp == '-') + continue; + if (!g_ascii_isalnum(tmp)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%c is not alphanumeric", + tmp); + return FALSE; + } + if (g_ascii_isalpha(tmp) && !g_ascii_islower(tmp)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%c is not lowercase", + tmp); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_quirks_convert_keyfile_to_xml(FuQuirks *self, GBytes *bytes, GError **error) +{ + gsize xmlsz; + g_autofree gchar *xml = NULL; + g_auto(GStrv) groups = NULL; + g_autoptr(GKeyFile) kf = g_key_file_new(); + g_autoptr(XbBuilderNode) root = xb_builder_node_new("quirk"); + + /* parse keyfile */ + if (!g_key_file_load_from_data(kf, + g_bytes_get_data(bytes, NULL), + g_bytes_get_size(bytes), + G_KEY_FILE_NONE, + error)) + return NULL; + + /* add each set of groups and keys */ + groups = g_key_file_get_groups(kf, NULL); + for (guint i = 0; groups[i] != NULL; i++) { + g_auto(GStrv) keys = NULL; + g_autofree gchar *group_id = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(XbBuilderNode) bn = NULL; + + /* sanity check group */ + if (g_str_has_prefix(groups[i], "HwID") || + g_str_has_prefix(groups[i], "DeviceInstanceID") || + g_str_has_prefix(groups[i], "GUID")) { + g_warning("invalid group name '%s'", groups[i]); + continue; + } + + /* get all KVs for the entry */ + keys = g_key_file_get_keys(kf, groups[i], NULL, error); + if (keys == NULL) + return NULL; + group_id = fu_quirks_build_group_key(groups[i]); + bn = xb_builder_node_insert(root, "device", "id", group_id, NULL); + for (guint j = 0; keys[j] != NULL; j++) { + g_autofree gchar *value = NULL; + + /* sanity check key */ + if ((self->load_flags & FU_QUIRKS_LOAD_FLAG_NO_VERIFY) == 0 && + g_hash_table_lookup(self->possible_keys, keys[j]) == NULL) { + if (!g_ptr_array_find_with_equal_func(self->invalid_keys, + keys[j], + g_str_equal, + NULL)) { + g_ptr_array_add(self->invalid_keys, g_strdup(keys[j])); + } + } + value = g_key_file_get_value(kf, groups[i], keys[j], error); + if (value == NULL) + return NULL; + + /* sanity check flags */ + if (g_strcmp0(keys[j], FU_QUIRKS_FLAGS) == 0) { + if (!fu_quirks_validate_flags(value, &error_local)) { + g_warning("[%s] %s = %s is invalid: %s", + groups[i], + keys[j], + value, + error_local->message); + } + } + + xb_builder_node_insert_text(bn, "value", value, "key", keys[j], NULL); + } + } + + /* export as XML blob */ + xml = xb_builder_node_export(root, XB_NODE_EXPORT_FLAG_ADD_HEADER, error); + if (xml == NULL) + return NULL; + xmlsz = strlen(xml); + return g_bytes_new_take(g_steal_pointer(&xml), xmlsz); +} + +static GInputStream * +fu_quirks_convert_quirk_to_xml_cb(XbBuilderSource *source, + XbBuilderSourceCtx *ctx, + gpointer user_data, + GCancellable *cancellable, + GError **error) +{ + FuQuirks *self = FU_QUIRKS(user_data); + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GBytes) bytes_xml = NULL; + + bytes = xb_builder_source_ctx_get_bytes(ctx, cancellable, error); + if (bytes == NULL) + return NULL; + bytes_xml = fu_quirks_convert_keyfile_to_xml(self, bytes, error); + if (bytes_xml == NULL) + return NULL; + return g_memory_input_stream_new_from_bytes(bytes_xml); +} + +static gint +fu_quirks_filename_sort_cb(gconstpointer a, gconstpointer b) +{ + const gchar *stra = *((const gchar **)a); + const gchar *strb = *((const gchar **)b); + return g_strcmp0(stra, strb); +} + +static gboolean +fu_quirks_add_quirks_for_path(FuQuirks *self, XbBuilder *builder, const gchar *path, GError **error) +{ + const gchar *tmp; + g_autoptr(GDir) dir = NULL; + g_autoptr(GPtrArray) filenames = g_ptr_array_new_with_free_func(g_free); + + if (g_getenv("FWUPD_VERBOSE") != NULL) + g_debug("loading quirks from %s", path); + + /* add valid files to the array */ + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + return TRUE; + dir = g_dir_open(path, 0, error); + if (dir == NULL) + return FALSE; + while ((tmp = g_dir_read_name(dir)) != NULL) { + if (!g_str_has_suffix(tmp, ".quirk") && !g_str_has_suffix(tmp, ".quirk.gz")) { + g_debug("skipping invalid file %s", tmp); + continue; + } + g_ptr_array_add(filenames, g_build_filename(path, tmp, NULL)); + } + + /* sort */ + g_ptr_array_sort(filenames, fu_quirks_filename_sort_cb); + + /* process files */ + for (guint i = 0; i < filenames->len; i++) { + const gchar *filename = g_ptr_array_index(filenames, i); + g_autoptr(GFile) file = g_file_new_for_path(filename); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + + /* load from keyfile */ +#if LIBXMLB_CHECK_VERSION(0, 1, 15) + xb_builder_source_add_simple_adapter(source, + "text/plain,application/octet-stream,.quirk", + fu_quirks_convert_quirk_to_xml_cb, + self, + NULL); +#else + xb_builder_source_add_adapter(source, + "text/plain,application/octet-stream,.quirk", + fu_quirks_convert_quirk_to_xml_cb, + self, + NULL); +#endif + if (!xb_builder_source_load_file(source, + file, + XB_BUILDER_SOURCE_FLAG_WATCH_FILE | + XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT, + NULL, + error)) { + g_prefix_error(error, "failed to load %s: ", filename); + return FALSE; + } + + /* watch the file for changes */ + xb_builder_import_source(builder, source); + } + + /* success */ + return TRUE; +} + +static gint +fu_quirks_strcasecmp_cb(gconstpointer a, gconstpointer b) +{ + const gchar *entry1 = *((const gchar **)a); + const gchar *entry2 = *((const gchar **)b); + return g_ascii_strcasecmp(entry1, entry2); +} + +static gboolean +fu_quirks_check_silo(FuQuirks *self, GError **error) +{ + XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_WATCH_BLOB; + g_autofree gchar *datadir = NULL; + g_autofree gchar *localstatedir = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(XbBuilder) builder = NULL; + g_autoptr(XbNode) n_any = NULL; + + /* everything is okay */ + if (self->silo != NULL && xb_silo_is_valid(self->silo)) + return TRUE; + + /* system datadir */ + builder = xb_builder_new(); + datadir = fu_path_from_kind(FU_PATH_KIND_DATADIR_QUIRKS); + if (!fu_quirks_add_quirks_for_path(self, builder, datadir, error)) + return FALSE; + + /* something we can write when using Ostree */ + localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_QUIRKS); + if (!fu_quirks_add_quirks_for_path(self, builder, localstatedir, error)) + return FALSE; + + /* load silo */ + if (self->load_flags & FU_QUIRKS_LOAD_FLAG_NO_CACHE) { + g_autoptr(GFileIOStream) iostr = NULL; + file = g_file_new_tmp(NULL, &iostr, error); + if (file == NULL) + return FALSE; + } else { + g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); + g_autofree gchar *xmlbfn = g_build_filename(cachedirpkg, "quirks.xmlb", NULL); + file = g_file_new_for_path(xmlbfn); + } + if (g_getenv("FWUPD_XMLB_VERBOSE") != NULL) { + xb_builder_set_profile_flags(builder, + XB_SILO_PROFILE_FLAG_XPATH | + XB_SILO_PROFILE_FLAG_DEBUG); + } + if (self->load_flags & FU_QUIRKS_LOAD_FLAG_READONLY_FS) + compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; + self->silo = xb_builder_ensure(builder, file, compile_flags, NULL, error); + if (self->silo == NULL) + return FALSE; + + /* dump warnings to console, just once */ + if (self->invalid_keys->len > 0) { + g_autofree gchar *str = NULL; + g_ptr_array_sort(self->invalid_keys, fu_quirks_strcasecmp_cb); + str = fu_strjoin(",", self->invalid_keys); + g_debug("invalid key names: %s", str); + } + + /* check if there is any quirk data to load, as older libxmlb versions will not be able to + * create the prepared query with an unknown text ID */ + n_any = xb_silo_query_first(self->silo, "quirk", NULL); + if (n_any == NULL) { + g_debug("no quirk data, not creating prepared queries"); + return TRUE; + } + + /* create prepared queries to save time later */ + self->query_kv = xb_query_new_full(self->silo, + "quirk/device[@id=?]/value[@key=?]", + XB_QUERY_FLAG_OPTIMIZE, + error); + if (self->query_kv == NULL) { + g_prefix_error(error, "failed to prepare query: "); + return FALSE; + } + self->query_vs = xb_query_new_full(self->silo, + "quirk/device[@id=?]/value", + XB_QUERY_FLAG_OPTIMIZE, + error); + if (self->query_vs == NULL) { + g_prefix_error(error, "failed to prepare query: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_quirks_lookup_by_id: + * @self: a #FuQuirks + * @guid: GUID to lookup + * @key: an ID to match the entry, e.g. `Name` + * + * Looks up an entry in the hardware database using a string value. + * + * Returns: (transfer none): values from the database, or %NULL if not found + * + * Since: 1.0.1 + **/ +const gchar * +fu_quirks_lookup_by_id(FuQuirks *self, const gchar *guid, const gchar *key) +{ + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) n = NULL; +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); +#endif + + g_return_val_if_fail(FU_IS_QUIRKS(self), NULL); + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(key != NULL, NULL); + + /* ensure up to date */ + if (!fu_quirks_check_silo(self, &error)) { + g_warning("failed to build silo: %s", error->message); + return NULL; + } + + /* no quirk data */ + if (self->query_kv == NULL) + return NULL; + + /* query */ +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + xb_query_context_set_flags(&context, XB_QUERY_FLAG_USE_INDEXES); + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL); + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 1, key, NULL); + n = xb_silo_query_first_with_context(self->silo, self->query_kv, &context, &error); +#else + if (!xb_query_bind_str(self->query_kv, 0, guid, &error)) { + g_warning("failed to bind 0: %s", error->message); + return NULL; + } + if (!xb_query_bind_str(self->query_kv, 1, key, &error)) { + g_warning("failed to bind 1: %s", error->message); + return NULL; + } + n = xb_silo_query_first_full(self->silo, self->query_kv, &error); +#endif + + if (n == NULL) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + return NULL; + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + return NULL; + g_warning("failed to query: %s", error->message); + return NULL; + } + if (self->verbose) + g_debug("%s:%s → %s", guid, key, xb_node_get_text(n)); + return xb_node_get_text(n); +} + +/** + * fu_quirks_lookup_by_id_iter: + * @self: a #FuQuirks + * @guid: GUID to lookup + * @iter_cb: (scope async): a function to call for each result + * @user_data: user data passed to @iter_cb + * + * Looks up all entries in the hardware database using a GUID value. + * + * Returns: %TRUE if the ID was found, and @iter was called + * + * Since: 1.3.3 + **/ +gboolean +fu_quirks_lookup_by_id_iter(FuQuirks *self, + const gchar *guid, + FuQuirksIter iter_cb, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) results = NULL; +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); +#endif + + g_return_val_if_fail(FU_IS_QUIRKS(self), FALSE); + g_return_val_if_fail(guid != NULL, FALSE); + g_return_val_if_fail(iter_cb != NULL, FALSE); + + /* ensure up to date */ + if (!fu_quirks_check_silo(self, &error)) { + g_warning("failed to build silo: %s", error->message); + return FALSE; + } + + /* no quirk data */ + if (self->query_vs == NULL) + return FALSE; + + /* query */ +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + xb_query_context_set_flags(&context, XB_QUERY_FLAG_USE_INDEXES); + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL); + results = xb_silo_query_with_context(self->silo, self->query_vs, &context, &error); +#else + if (!xb_query_bind_str(self->query_vs, 0, guid, &error)) { + g_warning("failed to bind 0: %s", error->message); + return FALSE; + } + results = xb_silo_query_full(self->silo, self->query_vs, &error); +#endif + + if (results == NULL) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + return FALSE; + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + return FALSE; + g_warning("failed to query: %s", error->message); + return FALSE; + } + for (guint i = 0; i < results->len; i++) { + XbNode *n = g_ptr_array_index(results, i); + if (self->verbose) + g_debug("%s → %s", guid, xb_node_get_text(n)); + iter_cb(self, xb_node_get_attr(n, "key"), xb_node_get_text(n), user_data); + } + return TRUE; +} + +/** + * fu_quirks_load: (skip) + * @self: a #FuQuirks + * @load_flags: load flags + * @error: (nullable): optional return location for an error + * + * Loads the various files that define the hardware quirks used in plugins. + * + * Returns: %TRUE for success + * + * Since: 1.0.1 + **/ +gboolean +fu_quirks_load(FuQuirks *self, FuQuirksLoadFlags load_flags, GError **error) +{ + g_return_val_if_fail(FU_IS_QUIRKS(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + self->load_flags = load_flags; + self->verbose = g_getenv("FWUPD_XMLB_VERBOSE") != NULL; + return fu_quirks_check_silo(self, error); +} + +/** + * fu_quirks_add_possible_key: + * @self: a #FuQuirks + * @possible_key: a key name, e.g. `Flags` + * + * Adds a possible quirk key. If added by a plugin it should be namespaced + * using the plugin name, where possible. + * + * Since: 1.5.8 + **/ +void +fu_quirks_add_possible_key(FuQuirks *self, const gchar *possible_key) +{ + g_return_if_fail(FU_IS_QUIRKS(self)); + g_return_if_fail(possible_key != NULL); + g_hash_table_add(self->possible_keys, g_strdup(possible_key)); +} + +static void +fu_quirks_class_init(FuQuirksClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_quirks_finalize; +} + +static void +fu_quirks_init(FuQuirks *self) +{ + self->possible_keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + self->invalid_keys = g_ptr_array_new_with_free_func(g_free); + + /* built in */ + fu_quirks_add_possible_key(self, FU_QUIRKS_BRANCH); + fu_quirks_add_possible_key(self, FU_QUIRKS_CHILDREN); + fu_quirks_add_possible_key(self, FU_QUIRKS_COUNTERPART_GUID); + fu_quirks_add_possible_key(self, FU_QUIRKS_FIRMWARE_SIZE); + fu_quirks_add_possible_key(self, FU_QUIRKS_FIRMWARE_SIZE_MAX); + fu_quirks_add_possible_key(self, FU_QUIRKS_FIRMWARE_SIZE_MIN); + fu_quirks_add_possible_key(self, FU_QUIRKS_FLAGS); + fu_quirks_add_possible_key(self, FU_QUIRKS_GTYPE); + fu_quirks_add_possible_key(self, FU_QUIRKS_FIRMWARE_GTYPE); + fu_quirks_add_possible_key(self, FU_QUIRKS_GUID); + fu_quirks_add_possible_key(self, FU_QUIRKS_ICON); + fu_quirks_add_possible_key(self, FU_QUIRKS_INHIBIT); + fu_quirks_add_possible_key(self, FU_QUIRKS_INSTALL_DURATION); + fu_quirks_add_possible_key(self, FU_QUIRKS_ISSUE); + fu_quirks_add_possible_key(self, FU_QUIRKS_NAME); + fu_quirks_add_possible_key(self, FU_QUIRKS_PARENT_GUID); + fu_quirks_add_possible_key(self, FU_QUIRKS_PLUGIN); + fu_quirks_add_possible_key(self, FU_QUIRKS_PRIORITY); + fu_quirks_add_possible_key(self, FU_QUIRKS_PROTOCOL); + fu_quirks_add_possible_key(self, FU_QUIRKS_PROXY_GUID); + fu_quirks_add_possible_key(self, FU_QUIRKS_BATTERY_THRESHOLD); + fu_quirks_add_possible_key(self, FU_QUIRKS_REMOVE_DELAY); + fu_quirks_add_possible_key(self, FU_QUIRKS_SUMMARY); + fu_quirks_add_possible_key(self, FU_QUIRKS_UPDATE_IMAGE); + fu_quirks_add_possible_key(self, FU_QUIRKS_UPDATE_MESSAGE); + fu_quirks_add_possible_key(self, FU_QUIRKS_VENDOR); + fu_quirks_add_possible_key(self, FU_QUIRKS_VENDOR_ID); + fu_quirks_add_possible_key(self, FU_QUIRKS_VERSION); + fu_quirks_add_possible_key(self, FU_QUIRKS_VERSION_FORMAT); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_READ_ID); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_READ_ID_SZ); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_CHIP_ERASE); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_BLOCK_ERASE); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_SECTOR_ERASE); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_WRITE_STATUS); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_PAGE_PROG); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_READ_DATA); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_READ_STATUS); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_CMD_WRITE_EN); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_PAGE_SIZE); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_SECTOR_SIZE); + fu_quirks_add_possible_key(self, FU_QUIRKS_CFI_DEVICE_BLOCK_SIZE); +} + +static void +fu_quirks_finalize(GObject *obj) +{ + FuQuirks *self = FU_QUIRKS(obj); + if (self->query_kv != NULL) + g_object_unref(self->query_kv); + if (self->query_vs != NULL) + g_object_unref(self->query_vs); + if (self->silo != NULL) + g_object_unref(self->silo); + g_hash_table_unref(self->possible_keys); + g_ptr_array_unref(self->invalid_keys); + G_OBJECT_CLASS(fu_quirks_parent_class)->finalize(obj); +} + +/** + * fu_quirks_new: (skip) + * + * Creates a new quirks object. + * + * Returns: a new #FuQuirks + * + * Since: 1.0.1 + **/ +FuQuirks * +fu_quirks_new(void) +{ + FuQuirks *self; + self = g_object_new(FU_TYPE_QUIRKS, NULL); + return FU_QUIRKS(self); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-quirks.h b/fwupd-1.8.6/libfwupdplugin/fu-quirks.h new file mode 100644 index 0000000000000000000000000000000000000000..eb5f4a9aa71d9cdd683ba65fbbb052e457cef590 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-quirks.h @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_QUIRKS (fu_quirks_get_type()) +G_DECLARE_FINAL_TYPE(FuQuirks, fu_quirks, FU, QUIRKS, GObject) + +/** + * FuQuirksLoadFlags: + * @FU_QUIRKS_LOAD_FLAG_NONE: No flags set + * @FU_QUIRKS_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors + * @FU_QUIRKS_LOAD_FLAG_NO_CACHE: Do not save to a persistent cache + * @FU_QUIRKS_LOAD_FLAG_NO_VERIFY: Do not check the key files for errors + * + * The flags to use when loading quirks. + **/ +typedef enum { + FU_QUIRKS_LOAD_FLAG_NONE = 0, + FU_QUIRKS_LOAD_FLAG_READONLY_FS = 1 << 0, + FU_QUIRKS_LOAD_FLAG_NO_CACHE = 1 << 1, + FU_QUIRKS_LOAD_FLAG_NO_VERIFY = 1 << 2, + /*< private >*/ + FU_QUIRKS_LOAD_FLAG_LAST +} FuQuirksLoadFlags; + +/** + * FuQuirksIter: + * @self: a #FuQuirks + * @key: a key + * @value: a value + * @user_data: user data + * + * The quirks iteration callback. + */ +typedef void (*FuQuirksIter)(FuQuirks *self, + const gchar *key, + const gchar *value, + gpointer user_data); + +FuQuirks * +fu_quirks_new(void); +gboolean +fu_quirks_load(FuQuirks *self, + FuQuirksLoadFlags load_flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +const gchar * +fu_quirks_lookup_by_id(FuQuirks *self, const gchar *guid, const gchar *key); +gboolean +fu_quirks_lookup_by_id_iter(FuQuirks *self, + const gchar *guid, + FuQuirksIter iter_cb, + gpointer user_data); +void +fu_quirks_add_possible_key(FuQuirks *self, const gchar *possible_key); + +/** + * FU_QUIRKS_PLUGIN: + * + * The quirk key for the plugin name. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_PLUGIN "Plugin" +/** + * FU_QUIRKS_FLAGS: + * + * The quirk key for the public flags. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_FLAGS "Flags" +/** + * FU_QUIRKS_SUMMARY: + * + * The quirk key for the summary. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_SUMMARY "Summary" +/** + * FU_QUIRKS_ICON: + * + * The quirk key for the icon. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_ICON "Icon" +/** + * FU_QUIRKS_NAME: + * + * The quirk key for the name. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_NAME "Name" +/** + * FU_QUIRKS_BRANCH: + * + * The quirk key for the firmware branch. + * + * Since: 1.5.0 + **/ +#define FU_QUIRKS_BRANCH "Branch" +/** + * FU_QUIRKS_GUID: + * + * The quirk key for the GUID. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_GUID "Guid" +/** + * FU_QUIRKS_COUNTERPART_GUID: + * + * The quirk key for the counterpart GUID. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_COUNTERPART_GUID "CounterpartGuid" +/** + * FU_QUIRKS_PARENT_GUID: + * + * The quirk key for the parent GUID. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_PARENT_GUID "ParentGuid" +/** + * FU_QUIRKS_PROXY_GUID: + * + * The quirk key for the proxy GUID. + * + * Since: 1.4.1 + **/ +#define FU_QUIRKS_PROXY_GUID "ProxyGuid" +/** + * FU_QUIRKS_CHILDREN: + * + * The quirk key for the children. This should contain the custom GType. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_CHILDREN "Children" +/** + * FU_QUIRKS_VERSION: + * + * The quirk key for the version. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_VERSION "Version" +/** + * FU_QUIRKS_VENDOR: + * + * The quirk key for the vendor name. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_VENDOR "Vendor" +/** + * FU_QUIRKS_VENDOR_ID: + * + * The quirk key for the vendor ID. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_VENDOR_ID "VendorId" +/** + * FU_QUIRKS_FIRMWARE_SIZE_MIN: + * + * The quirk key for the minimum firmware size in bytes. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_FIRMWARE_SIZE_MIN "FirmwareSizeMin" +/** + * FU_QUIRKS_FIRMWARE_SIZE_MAX: + * + * The quirk key for the maximum firmware size in bytes. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_FIRMWARE_SIZE_MAX "FirmwareSizeMax" +/** + * FU_QUIRKS_FIRMWARE_SIZE: + * + * The quirk key for the exact required firmware size in bytes. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_FIRMWARE_SIZE "FirmwareSize" +/** + * FU_QUIRKS_INSTALL_DURATION: + * + * The quirk key for the install duration in seconds. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_INSTALL_DURATION "InstallDuration" +/** + * FU_QUIRKS_VERSION_FORMAT: + * + * The quirk key for the version format, e.g. `quad`. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_VERSION_FORMAT "VersionFormat" +/** + * FU_QUIRKS_GTYPE: + * + * The quirk key for the custom GType. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_GTYPE "GType" +/** + * FU_QUIRKS_FIRMWARE_GTYPE: + * + * The quirk key for the custom firmware GType. + * + * Since: 1.7.2 + **/ +#define FU_QUIRKS_FIRMWARE_GTYPE "FirmwareGType" +/** + * FU_QUIRKS_PROTOCOL: + * + * The quirk key for the protocol, e.g. `org.usb.dfu`. + * + * Since: 1.3.7 + **/ +#define FU_QUIRKS_PROTOCOL "Protocol" +/** + * FU_QUIRKS_UPDATE_MESSAGE: + * + * The quirk key for the update message shown after the transaction has completed. + * + * Since: 1.4.0 + **/ +#define FU_QUIRKS_UPDATE_MESSAGE "UpdateMessage" +/** + * FU_QUIRKS_UPDATE_IMAGE: + * + * The quirk key for the update image shown before the update is performed. + * + * Since: 1.5.0 + **/ +#define FU_QUIRKS_UPDATE_IMAGE "UpdateImage" +/** + * FU_QUIRKS_PRIORITY: + * + * The quirk key for the device priority. + * + * Since: 1.4.1 + **/ +#define FU_QUIRKS_PRIORITY "Priority" +/** + * FU_QUIRKS_BATTERY_THRESHOLD: + * + * The quirk key for the battery threshold in percent. + * + * Since: 1.6.0 + **/ +#define FU_QUIRKS_BATTERY_THRESHOLD "BatteryThreshold" +/** + * FU_QUIRKS_REMOVE_DELAY: + * + * The quirk key for the device removal delay in milliseconds. + * + * Since: 1.5.0 + **/ +#define FU_QUIRKS_REMOVE_DELAY "RemoveDelay" +/** + * FU_QUIRKS_ACQUIESCE_DELAY: + * + * The quirk key for the device removal delay in milliseconds. + * + * Since: 1.8.3 + **/ +#define FU_QUIRKS_ACQUIESCE_DELAY "AcquiesceDelay" +/** + * FU_QUIRKS_INHIBIT: + * + * The quirk key to inhibit the UPDATABLE flag and to set an update error. + * + * Since: 1.6.2 + **/ +#define FU_QUIRKS_INHIBIT "Inhibit" +/** + * FU_QUIRKS_ISSUE: + * + * The quirk key to add security issues affecting a specific device. + * + * Since: 1.7.6 + **/ +#define FU_QUIRKS_ISSUE "Issue" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_READ_ID + * + * The quirk key to set the CFI read ID command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_READ_ID "CfiDeviceCmdReadId" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_READ_ID_SZ + * + * The quirk key to set the CFI read ID size. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_READ_ID_SZ "CfiDeviceCmdReadIdSz" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_CHIP_ERASE + * + * The quirk key to set the CFI chip erase command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_CHIP_ERASE "CfiDeviceCmdChipErase" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_BLOCK_ERASE + * + * The quirk key to set the CFI block erase command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_BLOCK_ERASE "CfiDeviceCmdBlockErase" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_SECTOR_ERASE + * + * The quirk key to set the CFI sector erase command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_SECTOR_ERASE "CfiDeviceCmdSectorErase" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_WRITE_STATUS + * + * The quirk key to set the CFI write status command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_WRITE_STATUS "CfiDeviceCmdWriteStatus" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_PAGE_PROG + * + * The quirk key to set the CFI page program command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_PAGE_PROG "CfiDeviceCmdPageProg" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_READ_DATA + * + * The quirk key to set the CFI read data command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_READ_DATA "CfiDeviceCmdReadData" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_READ_STATUS + * + * The quirk key to set the CFI read status command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_READ_STATUS "CfiDeviceCmdReadStatus" +/** + * FU_QUIRKS_CFI_DEVICE_CMD_WRITE_EN + * + * The quirk key to set the CFI write en command. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_CMD_WRITE_EN "CfiDeviceCmdWriteEn" +/** + * FU_QUIRKS_CFI_DEVICE_PAGE_SIZE + * + * The quirk key to set the CFI page size. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_PAGE_SIZE "CfiDevicePageSize" +/** + * FU_QUIRKS_CFI_DEVICE_SECTOR_SIZE + * + * The quirk key to set the CFI sector size. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_SECTOR_SIZE "CfiDeviceSectorSize" +/** + * FU_QUIRKS_CFI_DEVICE_BLOCK_SIZE + * + * The quirk key to set the CFI block size. + * + * Since: 1.8.2 + **/ +#define FU_QUIRKS_CFI_DEVICE_BLOCK_SIZE "CfiDeviceBlockSize" diff --git a/fwupd-1.8.6/libfwupdplugin/fu-security-attr.c b/fwupd-1.8.6/libfwupdplugin/fu-security-attr.c new file mode 100644 index 0000000000000000000000000000000000000000..93f653ce745c6fc38f4201ed20f52a2f7f8f545b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-security-attr.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FwupdSecurityAttr" + +#include "config.h" + +#include "fu-security-attr.h" + +typedef struct { + FuContext *ctx; +} FuSecurityAttrPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuSecurityAttr, fu_security_attr, FWUPD_TYPE_SECURITY_ATTR) + +#define GET_PRIVATE(o) (fu_security_attr_get_instance_private(o)) + +/** + * fu_security_attr_add_bios_target_value: + * @attr: a #FwupdSecurityAttr + * @id: a #FwupdBiosSetting ID or name + * @needle: The substring of a target value + * + * Checks all configured possible values of an enumeration attribute and + * if any match @needle then set as the target value. + * + * Since: 1.8.4 + **/ +void +fu_security_attr_add_bios_target_value(FwupdSecurityAttr *attr, + const gchar *id, + const gchar *needle) +{ + FuSecurityAttr *self = FU_SECURITY_ATTR(attr); + FuSecurityAttrPrivate *priv = GET_PRIVATE(self); + FwupdBiosSetting *bios_setting; + GPtrArray *values; + + bios_setting = fu_context_get_bios_setting(priv->ctx, id); + if (bios_setting == NULL) + return; + fwupd_security_attr_set_bios_setting_id(attr, fwupd_bios_setting_get_id(bios_setting)); + fwupd_security_attr_set_bios_setting_current_value( + attr, + fwupd_bios_setting_get_current_value(bios_setting)); + if (fwupd_bios_setting_get_kind(bios_setting) != FWUPD_BIOS_SETTING_KIND_ENUMERATION) + return; + if (fwupd_bios_setting_get_read_only(bios_setting)) + return; + values = fwupd_bios_setting_get_possible_values(bios_setting); + for (guint i = 0; i < values->len; i++) { + const gchar *possible = g_ptr_array_index(values, i); + g_autofree gchar *lower = g_utf8_strdown(possible, -1); + if (g_strrstr(lower, needle)) { + fwupd_security_attr_set_bios_setting_target_value(attr, possible); + return; + } + } +} + +static void +fu_security_attr_init(FuSecurityAttr *self) +{ +} + +static void +fu_security_attr_finalize(GObject *object) +{ + FuSecurityAttr *self = FU_SECURITY_ATTR(object); + FuSecurityAttrPrivate *priv = GET_PRIVATE(self); + if (priv->ctx != NULL) + g_object_unref(priv->ctx); + G_OBJECT_CLASS(fu_security_attr_parent_class)->finalize(object); +} + +static void +fu_security_attr_class_init(FuSecurityAttrClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_security_attr_finalize; +} + +/** + * fu_security_attr_new: + * @ctx: a #FuContext + * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard` + * + * Creates a new #FwupdSecurityAttr with context set. + * + * Returns: (transfer full): a #FwupdSecurityAttr + * + * Since: 1.8.4 + **/ +FwupdSecurityAttr * +fu_security_attr_new(FuContext *ctx, const gchar *appstream_id) +{ + g_autoptr(FuSecurityAttr) self = g_object_new(FU_TYPE_SECURITY_ATTR, NULL); + FuSecurityAttrPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(ctx), NULL); + if (appstream_id != NULL) + fwupd_security_attr_set_appstream_id(FWUPD_SECURITY_ATTR(self), appstream_id); + priv->ctx = g_object_ref(ctx); + return FWUPD_SECURITY_ATTR(g_steal_pointer(&self)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-security-attr.h b/fwupd-1.8.6/libfwupdplugin/fu-security-attr.h new file mode 100644 index 0000000000000000000000000000000000000000..e59719a5f4de27adcf259ef072ea00b4359d2f15 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-security-attr.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-context.h" + +#define FU_TYPE_SECURITY_ATTR (fu_security_attr_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuSecurityAttr, fu_security_attr, FU, SECURITY_ATTR, FwupdSecurityAttr) + +struct _FuSecurityAttrClass { + FwupdSecurityAttrClass parent_class; +}; + +FwupdSecurityAttr * +fu_security_attr_new(FuContext *ctx, const gchar *appstream_id); +void +fu_security_attr_add_bios_target_value(FwupdSecurityAttr *attr, + const gchar *id, + const gchar *needle); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-security-attrs-private.h b/fwupd-1.8.6/libfwupdplugin/fu-security-attrs-private.h new file mode 100644 index 0000000000000000000000000000000000000000..3f6b471fc3fdf1d843edf688fdadff6ba97ccfe1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-security-attrs-private.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +/** + * FuSecurityAttrsFlags: + * @FU_SECURITY_ATTRS_FLAG_NONE: No flags set + * @FU_SECURITY_ATTRS_FLAG_ADD_VERSION: Add the daemon version to the HSI string + * + * The flags to use when calculating an HSI version. + **/ +typedef enum { + FU_SECURITY_ATTRS_FLAG_NONE = 0, + FU_SECURITY_ATTRS_FLAG_ADD_VERSION = 1 << 0, + /*< private >*/ + FU_SECURITY_ATTRS_FLAG_LAST +} FuSecurityAttrsFlags; + +#include "fu-security-attrs.h" + +FuSecurityAttrs * +fu_security_attrs_new(void); +gchar * +fu_security_attrs_calculate_hsi(FuSecurityAttrs *self, FuSecurityAttrsFlags flags); +void +fu_security_attrs_depsolve(FuSecurityAttrs *self); +GVariant * +fu_security_attrs_to_variant(FuSecurityAttrs *self); +GPtrArray * +fu_security_attrs_get_all(FuSecurityAttrs *self); +void +fu_security_attrs_append_internal(FuSecurityAttrs *self, FwupdSecurityAttr *attr); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-security-attrs.c b/fwupd-1.8.6/libfwupdplugin/fu-security-attrs.c new file mode 100644 index 0000000000000000000000000000000000000000..739aef07cd184856ba0ddcce8e4adb08f10bf31b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-security-attrs.c @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuSecurityAttrs" + +#include "config.h" + +#include + +#include "fwupd-security-attr-private.h" +#include "fwupd-version.h" + +#include "fu-security-attrs-private.h" +#include "fu-security-attrs.h" + +/** + * FuSecurityAttrs: + * + * A set of Host Security ID attributes that represents the system state. + */ + +struct _FuSecurityAttrs { + GObject parent_instance; + GPtrArray *attrs; +}; + +/* probably sane to *not* make this part of the ABI */ +#define FWUPD_SECURITY_ATTR_ID_DOC_URL "https://fwupd.github.io/libfwupdplugin/hsi.html" + +G_DEFINE_TYPE(FuSecurityAttrs, fu_security_attrs, G_TYPE_OBJECT) + +static void +fu_security_attrs_finalize(GObject *obj) +{ + FuSecurityAttrs *self = FU_SECURITY_ATTRS(obj); + g_ptr_array_unref(self->attrs); + G_OBJECT_CLASS(fu_security_attrs_parent_class)->finalize(obj); +} + +static void +fu_security_attrs_class_init(FuSecurityAttrsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_security_attrs_finalize; +} + +static void +fu_security_attrs_init(FuSecurityAttrs *self) +{ + self->attrs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +/** + * fu_security_attrs_append_internal: + * @self: a #FuSecurityAttrs + * @attr: a #FwupdSecurityAttr + * + * Adds a #FwupdSecurityAttr to the array with no sanity checks. + * + * Since: 1.7.1 + **/ +void +fu_security_attrs_append_internal(FuSecurityAttrs *self, FwupdSecurityAttr *attr) +{ + g_return_if_fail(FU_IS_SECURITY_ATTRS(self)); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(attr)); + g_ptr_array_add(self->attrs, g_object_ref(attr)); +} + +/** + * fu_security_attrs_append: + * @self: a #FuSecurityAttrs + * @attr: a #FwupdSecurityAttr + * + * Adds a #FwupdSecurityAttr to the array. + * + * Since: 1.5.0 + **/ +void +fu_security_attrs_append(FuSecurityAttrs *self, FwupdSecurityAttr *attr) +{ + g_return_if_fail(FU_IS_SECURITY_ATTRS(self)); + g_return_if_fail(FWUPD_IS_SECURITY_ATTR(attr)); + + /* sanity check */ + if (fwupd_security_attr_get_plugin(attr) == NULL) { + g_warning("%s has no plugin set", fwupd_security_attr_get_appstream_id(attr)); + } + + /* sanity check, and correctly prefix the URLs with the current mirror */ + if (fwupd_security_attr_get_url(attr) == NULL) { + g_autofree gchar *url = NULL; + url = g_strdup_printf("%s#%s", + FWUPD_SECURITY_ATTR_ID_DOC_URL, + fwupd_security_attr_get_appstream_id(attr)); + fwupd_security_attr_set_url(attr, url); + } else if (g_str_has_prefix(fwupd_security_attr_get_url(attr), "#")) { + g_autofree gchar *url = NULL; + url = g_strdup_printf("%s%s", + FWUPD_SECURITY_ATTR_ID_DOC_URL, + fwupd_security_attr_get_url(attr)); + fwupd_security_attr_set_url(attr, url); + } + fu_security_attrs_append_internal(self, attr); +} + +/** + * fu_security_attrs_get_by_appstream_id: + * @self: a #FuSecurityAttrs + * @appstream_id: an ID, e.g. %FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM + * + * Gets a specific #FwupdSecurityAttr from the array. + * + * Returns: (transfer full): a #FwupdSecurityAttr or %NULL + * + * Since: 1.7.2 + **/ +FwupdSecurityAttr * +fu_security_attrs_get_by_appstream_id(FuSecurityAttrs *self, const gchar *appstream_id) +{ + g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL); + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i); + if (g_strcmp0(fwupd_security_attr_get_appstream_id(attr), appstream_id) == 0) + return g_object_ref(attr); + } + return NULL; +} + +/** + * fu_security_attrs_to_variant: + * @self: a #FuSecurityAttrs + * + * Serializes the #FwupdSecurityAttr objects. + * + * Returns: a #GVariant or %NULL + * + * Since: 1.5.0 + **/ +GVariant * +fu_security_attrs_to_variant(FuSecurityAttrs *self) +{ + GVariantBuilder builder; + + g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *security_attr = g_ptr_array_index(self->attrs, i); + GVariant *tmp = fwupd_security_attr_to_variant(security_attr); + g_variant_builder_add_value(&builder, tmp); + } + return g_variant_new("(aa{sv})", &builder); +} + +/** + * fu_security_attrs_get_all: + * @self: a #FuSecurityAttrs + * + * Gets all the attributes in the object. + * + * Returns: (transfer container) (element-type FwupdSecurityAttr): attributes + * + * Since: 1.5.0 + **/ +GPtrArray * +fu_security_attrs_get_all(FuSecurityAttrs *self) +{ + g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL); + return g_ptr_array_ref(self->attrs); +} + +/** + * fu_security_attrs_remove_all: + * @self: a #FuSecurityAttrs + * + * Removes all the attributes in the object. + * + * Since: 1.5.0 + **/ +void +fu_security_attrs_remove_all(FuSecurityAttrs *self) +{ + g_return_if_fail(FU_IS_SECURITY_ATTRS(self)); + return g_ptr_array_set_size(self->attrs, 0); +} + +/** + * fu_security_attrs_calculate_hsi: + * @self: a #FuSecurityAttrs + * @flags: HSI attribute flags + * + * Calculates the HSI string from the appended attributes. + * + * Returns: (transfer full): a string or %NULL + * + * Since: 1.5.0 + **/ +gchar * +fu_security_attrs_calculate_hsi(FuSecurityAttrs *self, FuSecurityAttrsFlags flags) +{ + guint hsi_number = 0; + FwupdSecurityAttrFlags attr_flags = FWUPD_SECURITY_ATTR_FLAG_NONE; + g_autoptr(GString) str = g_string_new("HSI:"); + const FwupdSecurityAttrFlags hpi_suffixes[] = { + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE, + FWUPD_SECURITY_ATTR_FLAG_NONE, + }; + + g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL); + + /* find the highest HSI number where there are no failures and at least + * one success */ + for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) { + gboolean success_cnt = 0; + gboolean failure_cnt = 0; + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i); + if (fwupd_security_attr_get_level(attr) != j) + continue; + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + success_cnt++; + else if (!fwupd_security_attr_has_flag(attr, + FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) + failure_cnt++; + } + + /* abort */ + if (failure_cnt > 0) { + hsi_number = j - 1; + break; + } + + /* we matched at least one thing on this level */ + if (success_cnt > 0) + hsi_number = j; + } + + /* get a logical OR of the runtime flags */ + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i); + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) + continue; + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) && + fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + continue; + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA)) + return g_strdup("HSI:INVALID:missing-data"); + + attr_flags |= fwupd_security_attr_get_flags(attr); + } + + g_string_append_printf(str, "%u", hsi_number); + if (attr_flags & FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) { + for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) { + if (attr_flags & hpi_suffixes[j]) + g_string_append( + str, + fwupd_security_attr_flag_to_suffix(hpi_suffixes[j])); + } + } + + if (flags & FU_SECURITY_ATTRS_FLAG_ADD_VERSION) { + g_string_append_printf(str, + " (v%d.%d.%d)", + FWUPD_MAJOR_VERSION, + FWUPD_MINOR_VERSION, + FWUPD_MICRO_VERSION); + } + + return g_string_free(g_steal_pointer(&str), FALSE); +} + +static gchar * +fu_security_attrs_get_sort_key(FwupdSecurityAttr *attr) +{ + GString *str = g_string_new(NULL); + + /* level */ + g_string_append_printf(str, "%u", fwupd_security_attr_get_level(attr)); + + /* success -> fail -> obsoletes */ + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + g_string_append(str, "0"); + } else if (!fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS) && + !fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + g_string_append(str, "1"); + } else { + g_string_append(str, "9"); + } + + /* prefer name, but fallback to appstream-id for tests */ + if (fwupd_security_attr_get_name(attr) != NULL) { + g_string_append(str, fwupd_security_attr_get_name(attr)); + } else { + g_string_append(str, fwupd_security_attr_get_appstream_id(attr)); + } + return g_string_free(str, FALSE); +} + +static gint +fu_security_attrs_sort_cb(gconstpointer item1, gconstpointer item2) +{ + FwupdSecurityAttr *attr1 = *((FwupdSecurityAttr **)item1); + FwupdSecurityAttr *attr2 = *((FwupdSecurityAttr **)item2); + g_autofree gchar *sort1 = fu_security_attrs_get_sort_key(attr1); + g_autofree gchar *sort2 = fu_security_attrs_get_sort_key(attr2); + return g_strcmp0(sort1, sort2); +} + +static struct { + const gchar *appstream_id; + FwupdSecurityAttrLevel level; +} appstream_id_level_map[] = { + {FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION}, + {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_INTEL_SMAP, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION}, + {FWUPD_SECURITY_ATTR_ID_IOMMU, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_MEI_VERSION, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_SPI_BLE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL}, + {FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT}, + {FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_UEFI_PK, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL}, + {NULL, FWUPD_SECURITY_ATTR_LEVEL_NONE}}; + +static void +fu_security_attrs_ensure_level(FwupdSecurityAttr *attr) +{ + const gchar *appstream_id = fwupd_security_attr_get_appstream_id(attr); + + /* already set */ + if (fwupd_security_attr_get_level(attr) != FWUPD_SECURITY_ATTR_LEVEL_NONE) + return; + + /* not required */ + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)) + return; + + /* map ID to level in one place */ + for (guint i = 0; appstream_id_level_map[i].appstream_id != NULL; i++) { + if (g_strcmp0(appstream_id, appstream_id_level_map[i].appstream_id) == 0) { + fwupd_security_attr_set_level(attr, appstream_id_level_map[i].level); + return; + } + } + + /* somebody forgot to add to the level map... */ + g_warning("cannot map %s to a HSI level, assuming critical", appstream_id); + fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); +} + +/** + * fu_security_attrs_depsolve: + * @self: a #FuSecurityAttrs + * + * Marks any attributes with %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED that have been + * defined as obsoleted by other attributes. + * + * It is only required to call this function once, and should be done when all + * attributes have been added. This will also sort the attrs. + * + * Since: 1.5.0 + **/ +void +fu_security_attrs_depsolve(FuSecurityAttrs *self) +{ + g_return_if_fail(FU_IS_SECURITY_ATTRS(self)); + + /* assign HSI levels if not already done */ + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i); + fu_security_attrs_ensure_level(attr); + } + + /* set flat where required */ + for (guint i = 0; i < self->attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i); + const gchar *attr_id = fwupd_security_attr_get_appstream_id(attr); + const gchar *attr_plugin = fwupd_security_attr_get_plugin(attr); + GPtrArray *obsoletes = fwupd_security_attr_get_obsoletes(attr); + + for (guint j = 0; j < self->attrs->len; j++) { + FwupdSecurityAttr *attr_tmp = g_ptr_array_index(self->attrs, j); + const gchar *attr_tmp_id = fwupd_security_attr_get_appstream_id(attr_tmp); + const gchar *attr_tmp_plugin = fwupd_security_attr_get_plugin(attr_tmp); + + /* skip self */ + if (g_strcmp0(attr_plugin, attr_tmp_plugin) == 0 && + g_strcmp0(attr_id, attr_tmp_id) == 0) + continue; + + /* add duplicate (negative) attributes when obsolete not explicitly set + */ + if (obsoletes->len == 0) { + if (g_strcmp0(attr_id, attr_tmp_id) != 0) + continue; + if (fwupd_security_attr_has_flag(attr, + FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + continue; + if (fwupd_security_attr_has_flag(attr_tmp, + FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + continue; + + if (fwupd_security_attr_has_obsolete(attr, attr_id)) + continue; + if (fwupd_security_attr_has_obsolete(attr_tmp, attr_id)) + continue; + g_debug("duplicate security attr %s from plugin %s implicitly " + "obsoleted by plugin %s", + attr_id, + attr_plugin, + attr_tmp_plugin); + fwupd_security_attr_add_obsolete(attr, attr_id); + } + + /* walk all the obsoletes for matches appstream ID or plugin */ + for (guint k = 0; k < obsoletes->len; k++) { + const gchar *obsolete = g_ptr_array_index(obsoletes, k); + + if (g_strcmp0(attr_tmp_id, obsolete) == 0 || + g_strcmp0(attr_tmp_plugin, obsolete) == 0) { + g_debug("security attr %s:%s obsoleted by %s:%s", + attr_tmp_id, + attr_tmp_plugin, + attr_id, + attr_plugin); + fwupd_security_attr_add_flag( + attr_tmp, + FWUPD_SECURITY_ATTR_FLAG_OBSOLETED); + } + } + } + } + + /* sort */ + g_ptr_array_sort(self->attrs, fu_security_attrs_sort_cb); +} + +/** + * fu_security_attrs_new: + * + * Returns: a security attribute + * + * Since: 1.5.0 + **/ +FuSecurityAttrs * +fu_security_attrs_new(void) +{ + return g_object_new(FU_TYPE_SECURITY_ATTRS, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-security-attrs.h b/fwupd-1.8.6/libfwupdplugin/fu-security-attrs.h new file mode 100644 index 0000000000000000000000000000000000000000..c09a3da5468a085bdb9cb2af7083e1e898cb56a0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-security-attrs.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SECURITY_ATTRS (fu_security_attrs_get_type()) + +G_DECLARE_FINAL_TYPE(FuSecurityAttrs, fu_security_attrs, FU, SECURITY_ATTRS, GObject) + +void +fu_security_attrs_append(FuSecurityAttrs *self, FwupdSecurityAttr *attr); +void +fu_security_attrs_remove_all(FuSecurityAttrs *self); +FwupdSecurityAttr * +fu_security_attrs_get_by_appstream_id(FuSecurityAttrs *self, const gchar *appstream_id); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-self-test.c b/fwupd-1.8.6/libfwupdplugin/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..9b15f3da15413952b034beae0e583d51a1444c65 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-self-test.c @@ -0,0 +1,3738 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-security-attr-private.h" + +#include "fu-bios-settings-private.h" +#include "fu-cabinet.h" +#include "fu-common-private.h" +#include "fu-context-private.h" +#include "fu-coswid-firmware.h" +#include "fu-device-private.h" +#include "fu-plugin-private.h" +#include "fu-security-attrs-private.h" +#include "fu-smbios-private.h" + +static GMainLoop *_test_loop = NULL; +static guint _test_loop_timeout_id = 0; + +static gboolean +fu_test_hang_check_cb(gpointer user_data) +{ + g_main_loop_quit(_test_loop); + _test_loop_timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fu_test_loop_run_with_timeout(guint timeout_ms) +{ + g_assert_cmpint(_test_loop_timeout_id, ==, 0); + g_assert_null(_test_loop); + _test_loop = g_main_loop_new(NULL, FALSE); + _test_loop_timeout_id = g_timeout_add(timeout_ms, fu_test_hang_check_cb, NULL); + g_main_loop_run(_test_loop); +} + +static void +fu_test_loop_quit(void) +{ + if (_test_loop_timeout_id > 0) { + g_source_remove(_test_loop_timeout_id); + _test_loop_timeout_id = 0; + } + if (_test_loop != NULL) { + g_main_loop_quit(_test_loop); + g_main_loop_unref(_test_loop); + _test_loop = NULL; + } +} + +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; + +#ifndef HAVE_LIBARCHIVE + g_test_skip("no libarchive support"); + return; +#endif + + filename = g_test_build_filename(G_TEST_DIST, "tests", "metadata.xml", NULL); + data = fu_bytes_get_contents(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_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; + +#ifndef HAVE_LIBARCHIVE + g_test_skip("no libarchive support"); + return; +#endif + + filename = g_test_build_filename(G_TEST_BUILT, + "tests", + "colorhug", + "colorhug-als-3.0.2.cab", + NULL); + data = fu_bytes_get_contents(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_volume_gpt_type_func(void) +{ + g_assert_cmpstr(fu_volume_kind_convert_to_gpt("0xef"), + ==, + "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"); + g_assert_cmpstr(fu_volume_kind_convert_to_gpt("0x0b"), + ==, + "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"); + g_assert_cmpstr(fu_volume_kind_convert_to_gpt("fat32lba"), + ==, + "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"); + g_assert_cmpstr(fu_volume_kind_convert_to_gpt("0x00"), ==, "0x00"); +} + +static void +fu_common_align_up_func(void) +{ + g_assert_cmpint(fu_common_align_up(0, 0), ==, 0); + g_assert_cmpint(fu_common_align_up(5, 0), ==, 5); + g_assert_cmpint(fu_common_align_up(5, 3), ==, 8); + g_assert_cmpint(fu_common_align_up(1023, 10), ==, 1024); + g_assert_cmpint(fu_common_align_up(1024, 10), ==, 1024); + g_assert_cmpint(fu_common_align_up(G_MAXSIZE - 1, 10), ==, G_MAXSIZE); +} + +static void +fu_common_byte_array_func(void) +{ + g_autoptr(GByteArray) array = g_byte_array_new(); + + fu_byte_array_append_uint8(array, (guint8)'h'); + fu_byte_array_append_uint8(array, (guint8)'e'); + fu_byte_array_append_uint8(array, (guint8)'l'); + fu_byte_array_append_uint8(array, (guint8)'l'); + fu_byte_array_append_uint8(array, (guint8)'o'); + g_assert_cmpint(array->len, ==, 5); + g_assert_cmpint(memcmp(array->data, "hello", array->len), ==, 0); + + fu_byte_array_set_size(array, 10, 0x00); + g_assert_cmpint(array->len, ==, 10); + g_assert_cmpint(memcmp(array->data, "hello\0\0\0\0\0", array->len), ==, 0); +} + +static void +fu_common_crc_func(void) +{ + guint8 buf[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}; + g_assert_cmpint(fu_crc8(buf, sizeof(buf)), ==, 0x7A); + g_assert_cmpint(fu_crc16(buf, sizeof(buf)), ==, 0x4DF1); + g_assert_cmpint(fu_crc32(buf, sizeof(buf)), ==, 0x40EFAB9E); +} + +static void +fu_string_append_func(void) +{ + g_autoptr(GString) str = g_string_new(NULL); + fu_string_append(str, 0, "hdr", NULL); + fu_string_append(str, 0, "key", "value"); + fu_string_append(str, 0, "key1", "value1"); + fu_string_append(str, 1, "key2", "value2"); + fu_string_append(str, 1, "", "value2"); + fu_string_append(str, 2, "key3", "value3"); + g_assert_cmpstr(str->str, + ==, + "hdr:\n" + "key: value\n" + "key1: value1\n" + " key2: value2\n" + " value2\n" + " key3: value3\n"); +} + +static void +fu_version_guess_format_func(void) +{ + g_assert_cmpint(fu_version_guess_format(NULL), ==, FWUPD_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint(fu_version_guess_format(""), ==, FWUPD_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint(fu_version_guess_format("1234ac"), ==, FWUPD_VERSION_FORMAT_PLAIN); + g_assert_cmpint(fu_version_guess_format("1.2"), ==, FWUPD_VERSION_FORMAT_PAIR); + g_assert_cmpint(fu_version_guess_format("1.2.3"), ==, FWUPD_VERSION_FORMAT_TRIPLET); + g_assert_cmpint(fu_version_guess_format("1.2.3.4"), ==, FWUPD_VERSION_FORMAT_QUAD); + g_assert_cmpint(fu_version_guess_format("1.2.3.4.5"), ==, FWUPD_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint(fu_version_guess_format("1a.2b.3"), ==, FWUPD_VERSION_FORMAT_PLAIN); + g_assert_cmpint(fu_version_guess_format("1"), ==, FWUPD_VERSION_FORMAT_NUMBER); + g_assert_cmpint(fu_version_guess_format("0x10201"), ==, FWUPD_VERSION_FORMAT_NUMBER); +} + +static void +fu_device_version_format_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "Ver1.2.3 RELEASE"); + g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.3"); +} + +static void +fu_device_open_refcount_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(NULL); + 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_NOTHING_TO_DO); + g_assert_false(ret); +} + +static void +fu_device_name_func(void) +{ + g_autoptr(FuDevice) device1 = fu_device_new(NULL); + g_autoptr(FuDevice) device2 = fu_device_new(NULL); + + /* vendor then name */ + fu_device_set_vendor(device1, " Hughski "); + fu_device_set_name(device1, "HUGHSKI ColorHug(TM)__Pro "); + g_assert_cmpstr(fu_device_get_vendor(device1), ==, "Hughski"); + g_assert_cmpstr(fu_device_get_name(device1), ==, "ColorHug™ Pro"); + + /* name then vendor */ + fu_device_set_name(device2, "Hughski ColorHug(TM)_Pro"); + fu_device_set_vendor(device2, "Hughski"); + g_assert_cmpstr(fu_device_get_vendor(device2), ==, "Hughski"); + g_assert_cmpstr(fu_device_get_name(device2), ==, "ColorHug™ Pro"); + + /* a real example */ + fu_device_set_name(device2, "Intel(R) Core(TM) i7-10850H CPU @ 2.70GHz"); + fu_device_set_vendor(device2, "Intel"); + g_assert_cmpstr(fu_device_get_name(device2), ==, "Core™ i7-10850H CPU @ 2.70GHz"); +} + +static void +fu_device_cfi_device_func(void) +{ + gboolean ret; + guint8 cmd = 0; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuCfiDevice) cfi_device = NULL; + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + cfi_device = fu_cfi_device_new(ctx, "3730"); + ret = fu_device_setup(FU_DEVICE(cfi_device), &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* fallback */ + ret = fu_cfi_device_get_cmd(cfi_device, FU_CFI_DEVICE_CMD_READ_DATA, &cmd, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(cmd, ==, 0x03); + + /* from quirk */ + ret = fu_cfi_device_get_cmd(cfi_device, FU_CFI_DEVICE_CMD_CHIP_ERASE, &cmd, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(cmd, ==, 0xC7); + g_assert_cmpint(fu_cfi_device_get_size(cfi_device), ==, 0x10000); + g_assert_cmpint(fu_cfi_device_get_page_size(cfi_device), ==, 0x200); + g_assert_cmpint(fu_cfi_device_get_sector_size(cfi_device), ==, 0x2000); + g_assert_cmpint(fu_cfi_device_get_block_size(cfi_device), ==, 0x8000); +} + +static void +fu_device_metadata_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + + /* string */ + fu_device_set_metadata(device, "foo", "bar"); + g_assert_cmpstr(fu_device_get_metadata(device, "foo"), ==, "bar"); + fu_device_set_metadata(device, "foo", "baz"); + g_assert_cmpstr(fu_device_get_metadata(device, "foo"), ==, "baz"); + g_assert_null(fu_device_get_metadata(device, "unknown")); + + /* boolean */ + fu_device_set_metadata_boolean(device, "baz", TRUE); + g_assert_cmpstr(fu_device_get_metadata(device, "baz"), ==, "true"); + g_assert_true(fu_device_get_metadata_boolean(device, "baz")); + g_assert_false(fu_device_get_metadata_boolean(device, "unknown")); + + /* integer */ + fu_device_set_metadata_integer(device, "bam", 12345); + g_assert_cmpstr(fu_device_get_metadata(device, "bam"), ==, "12345"); + g_assert_cmpint(fu_device_get_metadata_integer(device, "bam"), ==, 12345); + g_assert_cmpint(fu_device_get_metadata_integer(device, "unknown"), ==, G_MAXUINT); + + /* broken integer */ + fu_device_set_metadata(device, "bam", "123junk"); + g_assert_cmpint(fu_device_get_metadata_integer(device, "bam"), ==, G_MAXUINT); + fu_device_set_metadata(device, "huge", "4294967296"); /* not 32 bit */ + g_assert_cmpint(fu_device_get_metadata_integer(device, "huge"), ==, G_MAXUINT); +} + +static void +fu_smbios_func(void) +{ + const gchar *str; + gboolean ret; + g_autofree gchar *dump = NULL; + g_autofree gchar *testdatadir = NULL; + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(GError) error = NULL; + +#ifdef _WIN32 + g_test_skip("Windows uses GetSystemFirmwareTable rather than parsing the fake test data"); + return; +#endif + + /* these tests will not write */ + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + + smbios = fu_smbios_new(); + ret = fu_smbios_setup(smbios, &error); + g_assert_no_error(error); + g_assert_true(ret); + dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); + if (g_getenv("FWUPD_VERBOSE") != NULL) + g_debug("%s", dump); + + /* test for missing table */ + str = fu_smbios_get_string(smbios, 0xff, 0, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(str); + g_clear_error(&error); + + /* check for invalid offset */ + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_BIOS, 0xff, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(str); + g_clear_error(&error); + + /* get vendor */ + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_BIOS, 0x04, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "LENOVO"); +} + +static void +fu_smbios3_func(void) +{ + const gchar *str; + gboolean ret; + g_autofree gchar *path = NULL; + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(GError) error = NULL; + + path = g_test_build_filename(G_TEST_DIST, "tests", "dmi", "tables64", NULL); + smbios = fu_smbios_new(); + ret = fu_smbios_setup_from_path(smbios, path, &error); + g_assert_no_error(error); + g_assert_true(ret); + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); + g_debug("%s", dump); + } + + /* get vendor */ + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_BIOS, 0x04, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "Dell Inc."); +} + +static void +fu_smbios_dt_func(void) +{ + const gchar *str; + gboolean ret; + g_autofree gchar *path = NULL; + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(GError) error = NULL; + + path = g_test_build_filename(G_TEST_DIST, "tests", "devicetree", "base", NULL); + smbios = fu_smbios_new(); + ret = fu_smbios_setup_from_path(smbios, path, &error); + g_assert_no_error(error); + g_assert_true(ret); + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); + g_debug("%s", dump); + } + + /* get vendor */ + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "Hughski Limited"); +} + +static void +fu_smbios_dt_fallback_func(void) +{ + const gchar *str; + gboolean ret; + g_autofree gchar *path = NULL; + g_autoptr(FuSmbios) smbios = fu_smbios_new(); + g_autoptr(GError) error = NULL; + + path = g_test_build_filename(G_TEST_DIST, "tests", "devicetree-fallback", "base", NULL); + ret = fu_smbios_setup_from_path(smbios, path, &error); + g_assert_no_error(error); + g_assert_true(ret); + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); + g_debug("%s", dump); + } + + /* get vendor */ + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "solidrun"); + + /* get model */ + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x05, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "honeycomb"); +} + +static void +fu_smbios_class_func(void) +{ + g_autofree gchar *path = g_test_build_filename(G_TEST_DIST, "tests", "dmi", "class", NULL); + g_autoptr(FuSmbios) smbios = fu_smbios_new(); + g_autoptr(GError) error = NULL; + gboolean ret; + const gchar *str; + guint8 byte; + + ret = fu_smbios_setup_from_kernel(smbios, path, &error); + g_assert_no_error(error); + g_assert_true(ret); + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); + g_debug("%s", dump); + } + + str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 4, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "FwupdTest"); + + byte = fu_smbios_get_integer(smbios, FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, 5, &error); + g_assert_no_error(error); + g_assert_cmpuint(byte, ==, 16); +} + +static gboolean +_strnsplit_add_cb(GString *token, guint token_idx, gpointer user_data, GError **error) +{ + GPtrArray *array = (GPtrArray *)user_data; + g_debug("TOKEN: [%s] (%u)", token->str, token_idx); + g_ptr_array_add(array, g_strdup(token->str)); + return TRUE; +} + +static gboolean +_strnsplit_nop_cb(GString *token, guint token_idx, gpointer user_data, GError **error) +{ + guint *cnt = (guint *)user_data; + (*cnt)++; + return TRUE; +} + +static void +fu_common_memmem_func(void) +{ + const guint8 haystack[] = {'H', 'A', 'Y', 'S'}; + const guint8 needle[] = {'A', 'Y'}; + gboolean ret; + gsize offset = 0; + g_autoptr(GError) error = NULL; + + ret = fu_memmem_safe(haystack, sizeof(haystack), needle, sizeof(needle), &offset, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(offset, ==, 0x1); + + ret = fu_memmem_safe(haystack + 2, + sizeof(haystack) - 2, + needle, + sizeof(needle), + &offset, + &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); +} + +static void +fu_strsplit_func(void) +{ + const gchar *str = "123foo123bar123"; + const guint bigsz = 1024 * 1024; + gboolean ret; + guint cnt = 0; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GString) bigstr = g_string_sized_new(bigsz * 2); + + /* works for me */ + ret = fu_strsplit_full(str, -1, "123", _strnsplit_add_cb, array, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(array->len, ==, 3); + g_assert_cmpstr(g_ptr_array_index(array, 0), ==, ""); + g_assert_cmpstr(g_ptr_array_index(array, 1), ==, "foo"); + g_assert_cmpstr(g_ptr_array_index(array, 2), ==, "bar"); + + /* lets try something insane */ + for (guint i = 0; i < bigsz; i++) + g_string_append(bigstr, "X\n"); + ret = fu_strsplit_full(bigstr->str, -1, "\n", _strnsplit_nop_cb, &cnt, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(cnt, ==, bigsz); +} + +static void +fu_strsafe_func(void) +{ + struct { + const gchar *in; + const gchar *op; + } strs[] = {{"dave123", "dave123"}, + {"dave123XXX", "dave123"}, + {"dave\x03XXX", "dave.XX"}, + {"dave\x03\x04XXX", "dave..X"}, + {"\x03\x03", NULL}, + {NULL, NULL}}; + GPtrArray *instance_ids; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) dev = fu_device_new(ctx); + g_autoptr(GError) error = NULL; + + /* check bespoke legacy instance ID behavior */ + fu_device_add_instance_strsafe(dev, "KEY", "_ _LEN&VO&\\&"); + ret = fu_device_build_instance_id(dev, &error, "SUB", "KEY", NULL); + g_assert_no_error(error); + g_assert_true(ret); + instance_ids = fu_device_get_instance_ids(dev); + g_assert_cmpint(instance_ids->len, ==, 1); + g_assert_cmpstr(g_ptr_array_index(instance_ids, 0), ==, "SUB\\KEY_LEN-VO"); + + for (guint i = 0; strs[i].in != NULL; i++) { + g_autofree gchar *tmp = fu_strsafe(strs[i].in, 7); + g_assert_cmpstr(tmp, ==, strs[i].op); + } +} + +static void +fu_hwids_func(void) +{ + g_autofree gchar *testdatadir = NULL; + g_autoptr(FuHwids) hwids = NULL; + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(GError) error = NULL; + gboolean ret; + + struct { + const gchar *key; + const gchar *value; + } guids[] = {{"Manufacturer", "6de5d951-d755-576b-bd09-c5cf66b27234"}, + {"HardwareID-14", "6de5d951-d755-576b-bd09-c5cf66b27234"}, + {"HardwareID-13", "f8e1de5f-b68c-5f52-9d1a-f1ba52f1f773"}, + {"HardwareID-12", "e093d715-70f7-51f4-b6c8-b4a7e31def85"}, + {"HardwareID-11", "db73af4c-4612-50f7-b8a7-787cf4871847"}, + {"HardwareID-10", "f4275c1f-6130-5191-845c-3426247eb6a1"}, + {"HardwareID-9", "0cf8618d-9eff-537c-9f35-46861406eb9c"}, + {"HardwareID-8", "059eb22d-6dc7-59af-abd3-94bbe017f67c"}, + {"HardwareID-7", "da1da9b6-62f5-5f22-8aaa-14db7eeda2a4"}, + {"HardwareID-6", "178cd22d-ad9f-562d-ae0a-34009822cdbe"}, + {"HardwareID-5", "8dc9b7c5-f5d5-5850-9ab3-bd6f0549d814"}, + {"HardwareID-4", "660ccba8-1b78-5a33-80e6-9fb8354ee873"}, + {"HardwareID-3", "3faec92a-3ae3-5744-be88-495e90a7d541"}, + {"HardwareID-2", "f5ff077f-3eeb-5bae-be1c-e98ffe8ce5f8"}, + {"HardwareID-1", "b7cceb67-774c-537e-bf8b-22c6107e9a74"}, + {"HardwareID-0", "147efce9-f201-5fc8-ab0c-c859751c3440"}, + {NULL, NULL}}; + +#ifdef _WIN32 + g_test_skip("Windows uses GetSystemFirmwareTable rather than parsing the fake test data"); + return; +#endif + + /* these tests will not write */ + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + + smbios = fu_smbios_new(); + ret = fu_smbios_setup(smbios, &error); + g_assert_no_error(error); + g_assert_true(ret); + + hwids = fu_hwids_new(); + ret = fu_hwids_setup(hwids, smbios, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_MANUFACTURER), ==, "LENOVO"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_ENCLOSURE_KIND), ==, "a"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_FAMILY), ==, "ThinkPad T440s"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_PRODUCT_NAME), ==, "20ARS19C0C"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_VENDOR), ==, "LENOVO"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_VERSION), + ==, + "GJET75WW (2.25 )"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE), ==, "02"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_MINOR_RELEASE), ==, "19"); + g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_PRODUCT_SKU), + ==, + "LENOVO_MT_20AR_BU_Think_FM_ThinkPad T440s"); + for (guint i = 0; guids[i].key != NULL; i++) { + g_autofree gchar *guid = fu_hwids_get_guid(hwids, guids[i].key, &error); + g_assert_no_error(error); + g_assert_cmpstr(guid, ==, guids[i].value); + } + for (guint i = 0; guids[i].key != NULL; i++) + g_assert_true(fu_hwids_has_guid(hwids, guids[i].value)); +} + +static void +_plugin_device_added_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + FuDevice **dev = (FuDevice **)user_data; + *dev = g_object_ref(device); + fu_test_loop_quit(); +} + +static void +fu_plugin_config_func(void) +{ + GStatBuf statbuf = {0}; + gboolean ret; + gint rc; + g_autofree gchar *conf_dir = NULL; + g_autofree gchar *conf_file = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *testdatadir = NULL; + g_autofree gchar *value = NULL; + g_autoptr(FuPlugin) plugin = fu_plugin_new(NULL); + g_autoptr(GError) error = NULL; + + /* this is a build file */ + testdatadir = g_test_build_filename(G_TEST_BUILT, "tests", NULL); + (void)g_setenv("FWUPD_SYSCONFDIR", testdatadir, TRUE); + conf_dir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + + /* remove existing file */ + fu_plugin_set_name(plugin, "test"); + conf_file = g_strdup_printf("%s.conf", fu_plugin_get_name(plugin)); + fn = g_build_filename(conf_dir, conf_file, NULL); + ret = fu_path_mkdir_parent(fn, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_remove(fn); + ret = g_file_set_contents(fn, "", -1, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* set a value */ + ret = fu_plugin_set_config_value(plugin, "Key", "True", &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(g_file_test(fn, G_FILE_TEST_EXISTS)); + + /* check it is world readable */ + rc = g_stat(fn, &statbuf); + g_assert_cmpint(rc, ==, 0); + g_assert_cmpint(statbuf.st_mode & 0777, ==, 0644); + + /* read back the value */ + value = fu_plugin_get_config_value(plugin, "Key"); + g_assert_cmpstr(value, ==, "True"); + g_assert_true(fu_plugin_get_config_value_boolean(plugin, "Key")); + + /* write it private, i.e. only readable by the user/group */ + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_SECURE_CONFIG); + ret = fu_plugin_set_config_value(plugin, "Key", "False", &error); + g_assert_no_error(error); + g_assert_true(ret); + rc = g_stat(fn, &statbuf); + g_assert_cmpint(rc, ==, 0); + g_assert_cmpint(statbuf.st_mode & 0777, ==, 0640); +} + +static void +fu_plugin_devices_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(FuDevice) child = fu_device_new(NULL); + g_autoptr(FuPlugin) plugin = fu_plugin_new(NULL); + GPtrArray *devices; + + devices = fu_plugin_get_devices(plugin); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 0); + + fu_device_set_id(device, "testdev"); + fu_device_set_name(device, "testdev"); + fu_plugin_device_add(plugin, device); + g_assert_cmpint(devices->len, ==, 1); + fu_plugin_device_remove(plugin, device); + g_assert_cmpint(devices->len, ==, 0); + + /* add a child after adding the parent to the plugin */ + fu_device_set_id(child, "child"); + fu_device_set_name(child, "child"); + fu_device_add_child(device, child); + g_assert_cmpint(devices->len, ==, 1); + + /* remove said child */ + fu_device_remove_child(device, child); + g_assert_cmpint(devices->len, ==, 0); +} + +static void +fu_plugin_device_inhibit_children_func(void) +{ + g_autoptr(FuDevice) parent = fu_device_new(NULL); + g_autoptr(FuDevice) child1 = fu_device_new(NULL); + g_autoptr(FuDevice) child2 = fu_device_new(NULL); + + fu_device_set_id(parent, "testdev"); + fu_device_set_name(parent, "testdev"); + fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_id(child1, "child1"); + fu_device_set_name(child1, "child1"); + fu_device_add_flag(child1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_child(parent, child1); + + /* inhibit the parent */ + fu_device_inhibit(parent, "test", "because"); + g_assert_false(fu_device_has_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_true(fu_device_has_flag(child1, FWUPD_DEVICE_FLAG_UPDATABLE)); + fu_device_uninhibit(parent, "test"); + + /* make the inhibit propagate to children */ + fu_device_add_internal_flag(parent, FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN); + fu_device_inhibit(parent, "test", "because"); + g_assert_false(fu_device_has_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(child1, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* add a child after the inhibit, which should also be inhibited too */ + fu_device_set_id(child2, "child2"); + fu_device_set_name(child2, "child2"); + fu_device_add_flag(child2, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_child(parent, child2); + g_assert_false(fu_device_has_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(child1, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(child2, FWUPD_DEVICE_FLAG_UPDATABLE)); +} + +static void +fu_plugin_delay_func(void) +{ + FuDevice *device_tmp; + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuDevice) device = NULL; + + plugin = fu_plugin_new(NULL); + g_signal_connect(FU_PLUGIN(plugin), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + &device_tmp); + g_signal_connect(FU_PLUGIN(plugin), + "device-removed", + G_CALLBACK(_plugin_device_added_cb), + &device_tmp); + + /* add device straight away */ + device = fu_device_new(NULL); + fu_device_set_id(device, "testdev"); + fu_plugin_device_add(plugin, device); + g_assert_nonnull(device_tmp); + g_assert_cmpstr(fu_device_get_id(device_tmp), + ==, + "b7eccd0059d6d7dc2ef76c35d6de0048cc8c029d"); + g_clear_object(&device_tmp); + + /* remove device */ + fu_plugin_device_remove(plugin, device); + g_assert_nonnull(device_tmp); + g_assert_cmpstr(fu_device_get_id(device_tmp), + ==, + "b7eccd0059d6d7dc2ef76c35d6de0048cc8c029d"); + g_clear_object(&device_tmp); +} + +static void +fu_plugin_quirks_func(void) +{ + const gchar *tmp; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* USB\\VID_0A5C&PID_6412 */ + tmp = fu_context_lookup_quirk_by_id(ctx, "7a1ba7b9-6bcd-54a4-8a36-d60cc5ee935c", "Flags"); + g_assert_cmpstr(tmp, ==, "ignore-runtime"); + + /* ACME Inc.=True */ + tmp = fu_context_lookup_quirk_by_id(ctx, "ec77e295-7c63-5935-9957-be0472d9593a", "Name"); + g_assert_cmpstr(tmp, ==, "awesome"); + + /* CORP* */ + tmp = fu_context_lookup_quirk_by_id(ctx, "3731cce4-484c-521f-a652-892c8e0a65c7", "Name"); + g_assert_cmpstr(tmp, ==, "town"); + + /* baz */ + tmp = fu_context_lookup_quirk_by_id(ctx, "579a3b1c-d1db-5bdc-b6b9-e2c1b28d5b8a", "Unfound"); + g_assert_cmpstr(tmp, ==, NULL); + + /* unfound */ + tmp = fu_context_lookup_quirk_by_id(ctx, "8ff2ed23-b37e-5f61-b409-b7fe9563be36", "tests"); + g_assert_cmpstr(tmp, ==, NULL); + + /* unfound */ + tmp = fu_context_lookup_quirk_by_id(ctx, "8ff2ed23-b37e-5f61-b409-b7fe9563be36", "unfound"); + g_assert_cmpstr(tmp, ==, NULL); + + /* GUID */ + tmp = fu_context_lookup_quirk_by_id(ctx, "bb9ec3e2-77b3-53bc-a1f1-b05916715627", "Flags"); + g_assert_cmpstr(tmp, ==, "clever"); +} + +static void +fu_plugin_quirks_performance_func(void) +{ + gboolean ret; + g_autoptr(FuQuirks) quirks = fu_quirks_new(); + g_autoptr(GTimer) timer = g_timer_new(); + g_autoptr(GError) error = NULL; + const gchar *keys[] = {"Name", "Children", "Flags", NULL}; + + ret = fu_quirks_load(quirks, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* lookup */ + g_timer_reset(timer); + for (guint j = 0; j < 1000; j++) { + const gchar *group = "bb9ec3e2-77b3-53bc-a1f1-b05916715627"; + for (guint i = 0; keys[i] != NULL; i++) { + const gchar *tmp = fu_quirks_lookup_by_id(quirks, group, keys[i]); + g_assert_cmpstr(tmp, !=, NULL); + } + } + 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(NULL); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* use quirk file to set device attributes */ + fu_device_set_physical_id(device, "usb:00:05"); + fu_device_set_context(device, ctx); + 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_true(fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_UPDATABLE)); +} + +static void +fu_common_kernel_lockdown_func(void) +{ + gboolean ret; + g_autofree gchar *locked_dir = NULL; + g_autofree gchar *none_dir = NULL; + g_autofree gchar *old_kernel_dir = NULL; + +#ifndef __linux__ + g_test_skip("only works on Linux"); + return; +#endif + + old_kernel_dir = g_test_build_filename(G_TEST_DIST, "tests", "lockdown", NULL); + (void)g_setenv("FWUPD_SYSFSSECURITYDIR", old_kernel_dir, TRUE); + ret = fu_kernel_locked_down(); + g_assert_false(ret); + + locked_dir = g_test_build_filename(G_TEST_DIST, "tests", "lockdown", "locked", NULL); + (void)g_setenv("FWUPD_SYSFSSECURITYDIR", locked_dir, TRUE); + ret = fu_kernel_locked_down(); + g_assert_true(ret); + + none_dir = g_test_build_filename(G_TEST_DIST, "tests", "lockdown", "none", NULL); + (void)g_setenv("FWUPD_SYSFSSECURITYDIR", none_dir, TRUE); + ret = fu_kernel_locked_down(); + g_assert_false(ret); +} + +static gboolean +_open_cb(GObject *device, GError **error) +{ + g_assert_cmpstr(g_object_get_data(device, "state"), ==, "closed"); + g_object_set_data(device, "state", (gpointer) "opened"); + return TRUE; +} + +static gboolean +_close_cb(GObject *device, GError **error) +{ + g_assert_cmpstr(g_object_get_data(device, "state"), ==, "opened"); + g_object_set_data(device, "state", (gpointer) "closed-on-unref"); + return TRUE; +} + +static void +fu_device_locker_func(void) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GObject) device = g_object_new(G_TYPE_OBJECT, NULL); + + g_object_set_data(device, "state", (gpointer) "closed"); + locker = fu_device_locker_new_full(device, _open_cb, _close_cb, &error); + g_assert_no_error(error); + g_assert_nonnull(locker); + g_clear_object(&locker); + g_assert_cmpstr(g_object_get_data(device, "state"), ==, "closed-on-unref"); +} + +static gboolean +_fail_open_cb(FuDevice *device, GError **error) +{ + fu_device_set_metadata_boolean(device, "Test::Open", TRUE); + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "fail"); + return FALSE; +} + +static gboolean +_fail_close_cb(FuDevice *device, GError **error) +{ + fu_device_set_metadata_boolean(device, "Test::Close", TRUE); + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_BUSY, "busy"); + return FALSE; +} + +static void +fu_device_locker_fail_func(void) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(FuDevice) device = fu_device_new(NULL); + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)_fail_open_cb, + (FuDeviceLockerFunc)_fail_close_cb, + &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_null(locker); + g_assert_true(fu_device_get_metadata_boolean(device, "Test::Open")); + g_assert_true(fu_device_get_metadata_boolean(device, "Test::Close")); + g_assert_false(fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_IS_OPEN)); +} + +static void +fu_common_endian_func(void) +{ + guint8 buf[3]; + + fu_memwrite_uint16(buf, 0x1234, G_LITTLE_ENDIAN); + g_assert_cmpint(buf[0], ==, 0x34); + g_assert_cmpint(buf[1], ==, 0x12); + g_assert_cmpint(fu_memread_uint16(buf, G_LITTLE_ENDIAN), ==, 0x1234); + + fu_memwrite_uint16(buf, 0x1234, G_BIG_ENDIAN); + g_assert_cmpint(buf[0], ==, 0x12); + g_assert_cmpint(buf[1], ==, 0x34); + g_assert_cmpint(fu_memread_uint16(buf, G_BIG_ENDIAN), ==, 0x1234); + + fu_memwrite_uint24(buf, 0x123456, G_LITTLE_ENDIAN); + g_assert_cmpint(buf[0], ==, 0x56); + g_assert_cmpint(buf[1], ==, 0x34); + g_assert_cmpint(buf[2], ==, 0x12); + g_assert_cmpint(fu_memread_uint24(buf, G_LITTLE_ENDIAN), ==, 0x123456); + + fu_memwrite_uint24(buf, 0x123456, G_BIG_ENDIAN); + g_assert_cmpint(buf[0], ==, 0x12); + g_assert_cmpint(buf[1], ==, 0x34); + g_assert_cmpint(buf[2], ==, 0x56); + g_assert_cmpint(fu_memread_uint24(buf, G_BIG_ENDIAN), ==, 0x123456); +} + +static void +fu_common_cabinet_func(void) +{ + g_autoptr(FuCabinet) cabinet = fu_cabinet_new(); + g_autoptr(GBytes) blob1 = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GBytes) jcat_blob1 = g_bytes_new_static("hello", 6); + g_autoptr(GBytes) jcat_blob2 = g_bytes_new_static("hellX", 6); + g_autoptr(GError) error = NULL; + + /* add */ + fu_cabinet_add_file(cabinet, "firmware.jcat", jcat_blob1); + + /* replace */ + fu_cabinet_add_file(cabinet, "firmware.jcat", jcat_blob2); + + /* get data */ + blob1 = fu_cabinet_get_file(cabinet, "firmware.jcat", &error); + g_assert_no_error(error); + g_assert_nonnull(blob1); + g_assert_cmpstr(g_bytes_get_data(blob1, NULL), ==, "hellX"); + + /* get data that does not exist */ + blob2 = fu_cabinet_get_file(cabinet, "foo.jcat", &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(blob2); +} + +static void +fu_common_bytes_get_data_func(void) +{ + const gchar *fn = "/tmp/fwupdzero"; + const guint8 *buf; + gboolean ret; + g_autoptr(GBytes) bytes1 = NULL; + g_autoptr(GBytes) bytes2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GMappedFile) mmap = NULL; + + /* create file with zero size */ + ret = g_file_set_contents(fn, NULL, 0, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check we got zero sized data */ + bytes1 = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(bytes1); + g_assert_cmpint(g_bytes_get_size(bytes1), ==, 0); + g_assert_nonnull(g_bytes_get_data(bytes1, NULL)); + + /* do the same with an mmap mapping, which returns NULL on empty file */ + mmap = g_mapped_file_new(fn, FALSE, &error); + g_assert_no_error(error); + g_assert_nonnull(mmap); + bytes2 = g_mapped_file_get_bytes(mmap); + g_assert_nonnull(bytes2); + g_assert_cmpint(g_bytes_get_size(bytes2), ==, 0); + g_assert_null(g_bytes_get_data(bytes2, NULL)); + + /* use the safe function */ + buf = fu_bytes_get_data_safe(bytes2, NULL, &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); + g_assert_null(buf); +} + +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(NULL); + 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_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(GPtrArray) possible_plugins = NULL; + + /* only add one plugin name of the same type */ + fu_device_add_possible_plugin(device, "test"); + fu_device_add_possible_plugin(device, "test"); + possible_plugins = fu_device_get_possible_plugins(device); + g_assert_cmpint(possible_plugins->len, ==, 1); + + g_assert_cmpint(fu_device_get_backend_tags(device)->len, ==, 0); + fu_device_add_backend_tag(device, "foo"); + fu_device_add_backend_tag(device, "bar"); + g_assert_cmpint(fu_device_get_backend_tags(device)->len, ==, 2); + g_assert_true(fu_device_has_backend_tag(device, "foo")); + g_assert_false(fu_device_has_backend_tag(device, "bazbazbazbazbaz")); +} + +static void +fu_device_instance_ids_func(void) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = fu_device_new(ctx); + g_autoptr(GError) error = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* sanity check */ + g_assert_false(fu_device_has_guid(device, "c0a26214-223b-572a-9477-cde897fe8619")); + + /* add a deferred instance ID that only gets converted on ->setup */ + fu_device_add_instance_id(device, "foobarbaz"); + g_assert_false(fu_device_has_guid(device, "c0a26214-223b-572a-9477-cde897fe8619")); + + ret = fu_device_setup(device, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_device_has_guid(device, "c0a26214-223b-572a-9477-cde897fe8619")); + + /* this gets added immediately */ + fu_device_add_instance_id(device, "bazbarfoo"); + g_assert_true(fu_device_has_guid(device, "77e49bb0-2cd6-5faf-bcee-5b7fbe6e944d")); +} + +static void +fu_device_composite_id_func(void) +{ + g_autoptr(FuDevice) dev1 = fu_device_new(NULL); + g_autoptr(FuDevice) dev2 = fu_device_new(NULL); + g_autoptr(FuDevice) dev3 = fu_device_new(NULL); + g_autoptr(FuDevice) dev4 = fu_device_new(NULL); + + /* single device */ + fu_device_set_id(dev1, "dev1"); + g_assert_cmpstr(fu_device_get_composite_id(dev1), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + fu_device_set_id(dev2, "dev2"); + + /* one child */ + fu_device_add_child(dev1, dev2); + g_assert_cmpstr(fu_device_get_composite_id(dev1), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + g_assert_cmpstr(fu_device_get_composite_id(dev2), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + + /* add a different "family" */ + fu_device_set_id(dev3, "dev3"); + fu_device_set_id(dev4, "dev4"); + fu_device_add_child(dev3, dev4); + fu_device_add_child(dev2, dev3); + g_assert_cmpstr(fu_device_get_composite_id(dev1), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + g_assert_cmpstr(fu_device_get_composite_id(dev2), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + g_assert_cmpstr(fu_device_get_composite_id(dev3), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + g_assert_cmpstr(fu_device_get_composite_id(dev4), + ==, + "3b42553c4e3241e8f3f8fbc19a69fa2f95708a9d"); + + /* change the parent ID */ + fu_device_set_id(dev1, "dev1-NEW"); + g_assert_cmpstr(fu_device_get_composite_id(dev1), + ==, + "a4c8efc6a0a58c2dc14c05fd33186703f7352997"); + g_assert_cmpstr(fu_device_get_composite_id(dev2), + ==, + "a4c8efc6a0a58c2dc14c05fd33186703f7352997"); +} + +static void +fu_device_inhibit_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_battery_threshold(device, 25); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + + /* does not exist -> fine */ + fu_device_uninhibit(device, "NOTGOINGTOEXIST"); + g_assert_false(fu_device_has_inhibit(device, "NOTGOINGTOEXIST")); + + /* first one */ + fu_device_inhibit(device, "needs-activation", "Device is pending activation"); + g_assert_true(fu_device_has_inhibit(device, "needs-activation")); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* another */ + fu_device_set_battery_level(device, 5); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* activated, power still too low */ + fu_device_uninhibit(device, "needs-activation"); + g_assert_false(fu_device_has_inhibit(device, "needs-activation")); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* we got some more power -> fine */ + fu_device_set_battery_level(device, 95); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); +} + +static void +fu_device_inhibit_updateable_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + g_assert_cmpstr(fu_device_get_update_error(device), ==, NULL); + + /* first one */ + fu_device_inhibit(device, "needs-activation", "Device is pending activation"); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_cmpstr(fu_device_get_update_error(device), ==, "Device is pending activation"); + + /* activated, but still not updatable */ + fu_device_uninhibit(device, "needs-activation"); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); + g_assert_cmpstr(fu_device_get_update_error(device), ==, NULL); +} + +#define TEST_FLAG_FOO (1 << 0) +#define TEST_FLAG_BAR (1 << 1) +#define TEST_FLAG_BAZ (1 << 2) + +static void +fu_device_private_flags_func(void) +{ + g_autofree gchar *tmp = NULL; + g_autoptr(FuDevice) device = fu_device_new(NULL); + + fu_device_register_private_flag(device, TEST_FLAG_FOO, "foo"); + fu_device_register_private_flag(device, TEST_FLAG_BAR, "bar"); + + fu_device_set_custom_flags(device, "foo"); + g_assert_cmpint(fu_device_get_private_flags(device), ==, TEST_FLAG_FOO); + fu_device_set_custom_flags(device, "bar"); + g_assert_cmpint(fu_device_get_private_flags(device), ==, TEST_FLAG_FOO | TEST_FLAG_BAR); + fu_device_set_custom_flags(device, "~bar"); + g_assert_cmpint(fu_device_get_private_flags(device), ==, TEST_FLAG_FOO); + fu_device_set_custom_flags(device, "baz"); + g_assert_cmpint(fu_device_get_private_flags(device), ==, TEST_FLAG_FOO); + fu_device_add_private_flag(device, TEST_FLAG_BAZ); + g_assert_cmpint(fu_device_get_private_flags(device), ==, TEST_FLAG_FOO | TEST_FLAG_BAZ); + + tmp = fu_device_to_string(device); + g_assert_cmpstr(tmp, + ==, + "FuDevice:\n" + " Flags: none\n" + " AcquiesceDelay: 50\n" + " CustomFlags: baz\n" + " PrivateFlags: foo\n"); +} + +static void +fu_device_flags_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + + /* bitfield */ + for (guint64 i = 1; i < FU_DEVICE_INTERNAL_FLAG_UNKNOWN; i *= 2) { + const gchar *tmp = fu_device_internal_flag_to_string(i); + if (tmp == NULL) + break; + g_assert_cmpint(fu_device_internal_flag_from_string(tmp), ==, i); + } + + g_assert_cmpint(fu_device_get_flags(device), ==, FWUPD_DEVICE_FLAG_NONE); + + /* remove IS_BOOTLOADER if is a BOOTLOADER */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + g_assert_cmpint(fu_device_get_flags(device), ==, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + + /* check implication */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + g_assert_cmpint(fu_device_get_flags(device), + ==, + FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE | FWUPD_DEVICE_FLAG_CAN_VERIFY); + fu_device_remove_flag(device, + FWUPD_DEVICE_FLAG_CAN_VERIFY | FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + + /* negation */ + fu_device_set_custom_flags(device, "is-bootloader,updatable"); + g_assert_cmpint(fu_device_get_flags(device), + ==, + FWUPD_DEVICE_FLAG_IS_BOOTLOADER | FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_custom_flags(device, "~is-bootloader"); + g_assert_cmpint(fu_device_get_flags(device), ==, FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_device_children_func(void) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) child = fu_device_new(NULL); + g_autoptr(FuDevice) parent = fu_device_new(ctx); + g_autoptr(GError) error = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + fu_device_set_physical_id(child, "dummy"); + fu_device_set_physical_id(parent, "dummy"); + + /* set up family */ + fu_device_add_child(parent, child); + + /* set an instance ID that will be converted to a GUID when the parent + * calls ->setup */ + fu_device_add_instance_id(child, "foo"); + g_assert_false(fu_device_has_guid(child, "b84ed8ed-a7b1-502f-83f6-90132e68adef")); + + /* setup parent, which also calls setup on child too (and thus also + * converts the instance ID to a GUID) */ + ret = fu_device_setup(parent, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_device_has_guid(child, "b84ed8ed-a7b1-502f-83f6-90132e68adef")); +} + +static void +fu_device_parent_func(void) +{ + g_autoptr(FuDevice) child = fu_device_new(NULL); + g_autoptr(FuDevice) child_root = NULL; + g_autoptr(FuDevice) grandparent = fu_device_new(NULL); + g_autoptr(FuDevice) grandparent_root = NULL; + g_autoptr(FuDevice) parent = fu_device_new(NULL); + g_autoptr(FuDevice) parent_root = NULL; + + fu_device_set_physical_id(child, "dummy"); + fu_device_set_physical_id(grandparent, "dummy"); + fu_device_set_physical_id(parent, "dummy"); + + /* set up three layer family */ + fu_device_add_child(grandparent, parent); + fu_device_add_child(parent, child); + + /* check parents */ + g_assert_true(fu_device_get_parent(child) == parent); + g_assert_true(fu_device_get_parent(parent) == grandparent); + g_assert_true(fu_device_get_parent(grandparent) == NULL); + + /* check root */ + child_root = fu_device_get_root(child); + g_assert_true(child_root == grandparent); + parent_root = fu_device_get_root(parent); + g_assert_true(parent_root == grandparent); + grandparent_root = fu_device_get_root(child); + g_assert_true(grandparent_root == grandparent); +} + +static void +fu_device_incorporate_func(void) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = fu_device_new(ctx); + g_autoptr(FuDevice) donor = fu_device_new(ctx); + g_autoptr(GError) error = NULL; + + /* 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"); + fu_device_add_instance_str(donor, "VID", "1234"); + + /* 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); + ret = fu_device_build_instance_id(device, &error, "SUBSYS", "VID", NULL); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_device_has_instance_id(device, "SUBSYS\\VID_1234")); +} + +static void +fu_backend_func(void) +{ + FuDevice *dev; + gboolean ret; + g_autoptr(FuBackend) backend = g_object_new(FU_TYPE_BACKEND, NULL); + g_autoptr(FuDevice) dev1 = fu_device_new(NULL); + g_autoptr(FuDevice) dev2 = fu_device_new(NULL); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + + /* defaults */ + g_assert_null(fu_backend_get_name(backend)); + g_assert_true(fu_backend_get_enabled(backend)); + + /* load */ + ret = fu_backend_setup(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_backend_coldplug(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add two devices, then remove one of them */ + fu_device_set_physical_id(dev1, "dev1"); + fu_backend_device_added(backend, dev1); + fu_device_set_physical_id(dev2, "dev2"); + fu_backend_device_added(backend, dev2); + fu_backend_device_changed(backend, dev2); + fu_backend_device_removed(backend, dev2); + + dev = fu_backend_lookup_by_id(backend, "dev1"); + g_assert_nonnull(dev); + g_assert_true(dev == dev1); + + /* should have been removed */ + dev = fu_backend_lookup_by_id(backend, "dev2"); + g_assert_null(dev); + + /* get linear array */ + devices = fu_backend_get_devices(backend); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + dev = g_ptr_array_index(devices, 0); + g_assert_nonnull(dev); + g_assert_true(dev == dev1); +} + +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_assert_cmpstr(chunked3_str, + ==, + "\n" + " \n" + " 123\n" + " \n" + " \n" + " 0x1\n" + " 0x1\n" + " 456\n" + " \n" + "\n"); + + chunked4 = fu_chunk_array_new((const guint8 *)"123456", 6, 0x4, 4, 4); + chunked4_str = fu_chunk_array_to_string(chunked4); + g_assert_cmpstr(chunked4_str, + ==, + "\n" + " \n" + " 0x1\n" + " 1234\n" + " \n" + " \n" + " 0x1\n" + " 0x2\n" + " 56\n" + " \n" + "\n"); + + chunked1 = fu_chunk_array_new((const guint8 *)"0123456789abcdef", 16, 0x0, 10, 4); + chunked1_str = fu_chunk_array_to_string(chunked1); + g_assert_cmpstr(chunked1_str, + ==, + "\n" + " \n" + " 0123\n" + " \n" + " \n" + " 0x1\n" + " 0x4\n" + " 4567\n" + " \n" + " \n" + " 0x2\n" + " 0x8\n" + " 89\n" + " \n" + " \n" + " 0x3\n" + " 0x1\n" + " abcd\n" + " \n" + " \n" + " 0x4\n" + " 0x1\n" + " 0x4\n" + " ef\n" + " \n" + "\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, + ==, + "\n" + " \n" + " XXXX\n" + " \n" + " \n" + " 0x1\n" + " 0x4\n" + " XX\n" + " \n" + " \n" + " 0x2\n" + " 0x1\n" + " YYYY\n" + " \n" + " \n" + " 0x3\n" + " 0x1\n" + " 0x4\n" + " YY\n" + " \n" + " \n" + " 0x4\n" + " 0x2\n" + " ZZZZ\n" + " \n" + " \n" + " 0x5\n" + " 0x2\n" + " 0x4\n" + " ZZ\n" + " \n" + "\n"); +} + +static void +fu_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_strstrip(map[i].old); + g_assert_cmpstr(tmp, ==, map[i].new); + } +} + +static void +fu_version_semver_func(void) +{ + struct { + const gchar *old; + const gchar *new; + FwupdVersionFormat fmt; + } map[] = {{"1.2.3", "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET}, + {"1.2.3.4", "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET}, + {"1.2", "0.1.2", FWUPD_VERSION_FORMAT_TRIPLET}, + {"1", "0.0.1", FWUPD_VERSION_FORMAT_TRIPLET}, + {"CBET1.2.3", "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET}, + {"4.11-1190-g12d8072e6b-dirty", "4.11.1190", FWUPD_VERSION_FORMAT_TRIPLET}, + {"4.11-1190-g12d8072e6b-dirty", "4.11", FWUPD_VERSION_FORMAT_PAIR}, + {NULL, NULL}}; + for (guint i = 0; map[i].old != NULL; i++) { + g_autofree gchar *tmp = fu_version_ensure_semver(map[i].old, map[i].fmt); + g_assert_cmpstr(tmp, ==, map[i].new); + } +} + +static void +fu_strtoull_func(void) +{ + gboolean ret; + guint64 val = 0; + g_autoptr(GError) error = NULL; + + ret = fu_strtoull("123", &val, 123, 200, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(val, ==, 123); + + ret = fu_strtoull("123\n", &val, 0, 200, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(val, ==, 123); + + ret = fu_strtoull("0x123", &val, 0, 0x123, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(val, ==, 0x123); + + ret = fu_strtoull(NULL, &val, 0, G_MAXUINT32, NULL); + g_assert_false(ret); + ret = fu_strtoull("", &val, 120, 123, NULL); + g_assert_false(ret); + ret = fu_strtoull("124", &val, 120, 123, NULL); + g_assert_false(ret); + ret = fu_strtoull("119", &val, 120, 123, NULL); + g_assert_false(ret); +} + +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}, + {0x226a4b00, "137.2706.768", FWUPD_VERSION_FORMAT_SURFACE_LEGACY}, + {0x6001988, "6.25.136", FWUPD_VERSION_FORMAT_SURFACE}, + {0x00ff0001, "255.0.1", FWUPD_VERSION_FORMAT_DELL_BIOS}, + {0xc8, "0x000000c8", FWUPD_VERSION_FORMAT_HEX}, + {0, NULL}}; + struct { + guint64 val; + const gchar *ver; + FwupdVersionFormat flags; + } version_from_uint64[] = { + {0x0, "0.0.0.0", FWUPD_VERSION_FORMAT_QUAD}, + {0xff, "0.0.0.255", FWUPD_VERSION_FORMAT_QUAD}, + {0xffffffffffffffff, "65535.65535.65535.65535", FWUPD_VERSION_FORMAT_QUAD}, + {0xff, "0.255", FWUPD_VERSION_FORMAT_PAIR}, + {0xffffffffffffffff, "4294967295.4294967295", FWUPD_VERSION_FORMAT_PAIR}, + {0x0, "0", FWUPD_VERSION_FORMAT_NUMBER}, + {0x11000000c8, "0x00000011000000c8", FWUPD_VERSION_FORMAT_HEX}, + {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_uint64[i].ver != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_version_from_uint64(version_from_uint64[i].val, + version_from_uint64[i].flags); + g_assert_cmpstr(ver, ==, version_from_uint64[i].ver); + } + for (i = 0; version_from_uint32[i].ver != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_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_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_version_parse_from_format(version_parse[i].old, + FWUPD_VERSION_FORMAT_TRIPLET); + g_assert_cmpstr(ver, ==, version_parse[i].new); + } +} + +static void +fu_common_vercmp_func(void) +{ + /* same */ + g_assert_cmpint(fu_version_compare("1.2.3", "1.2.3", FWUPD_VERSION_FORMAT_UNKNOWN), ==, 0); + g_assert_cmpint( + fu_version_compare("001.002.003", "001.002.003", FWUPD_VERSION_FORMAT_UNKNOWN), + ==, + 0); + g_assert_cmpint(fu_version_compare("0x00000002", "0x2", FWUPD_VERSION_FORMAT_HEX), ==, 0); + + /* upgrade and downgrade */ + g_assert_cmpint(fu_version_compare("1.2.3", "1.2.4", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + g_assert_cmpint( + fu_version_compare("001.002.000", "001.002.009", FWUPD_VERSION_FORMAT_UNKNOWN), + <, + 0); + g_assert_cmpint(fu_version_compare("1.2.3", "1.2.2", FWUPD_VERSION_FORMAT_UNKNOWN), >, 0); + g_assert_cmpint( + fu_version_compare("001.002.009", "001.002.000", FWUPD_VERSION_FORMAT_UNKNOWN), + >, + 0); + + /* unequal depth */ + g_assert_cmpint(fu_version_compare("1.2.3", "1.2.3.1", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + g_assert_cmpint(fu_version_compare("1.2.3.1", "1.2.4", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + + /* mixed-alpha-numeric */ + g_assert_cmpint(fu_version_compare("1.2.3a", "1.2.3a", FWUPD_VERSION_FORMAT_UNKNOWN), + ==, + 0); + g_assert_cmpint(fu_version_compare("1.2.3a", "1.2.3b", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + g_assert_cmpint(fu_version_compare("1.2.3b", "1.2.3a", FWUPD_VERSION_FORMAT_UNKNOWN), >, 0); + + /* alpha version append */ + g_assert_cmpint(fu_version_compare("1.2.3", "1.2.3a", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + g_assert_cmpint(fu_version_compare("1.2.3a", "1.2.3", FWUPD_VERSION_FORMAT_UNKNOWN), >, 0); + + /* alpha only */ + g_assert_cmpint(fu_version_compare("alpha", "alpha", FWUPD_VERSION_FORMAT_UNKNOWN), ==, 0); + g_assert_cmpint(fu_version_compare("alpha", "beta", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + g_assert_cmpint(fu_version_compare("beta", "alpha", FWUPD_VERSION_FORMAT_UNKNOWN), >, 0); + + /* alpha-compare */ + g_assert_cmpint(fu_version_compare("1.2a.3", "1.2a.3", FWUPD_VERSION_FORMAT_UNKNOWN), + ==, + 0); + g_assert_cmpint(fu_version_compare("1.2a.3", "1.2b.3", FWUPD_VERSION_FORMAT_UNKNOWN), <, 0); + g_assert_cmpint(fu_version_compare("1.2b.3", "1.2a.3", FWUPD_VERSION_FORMAT_UNKNOWN), >, 0); + + /* tilde is all-powerful */ + g_assert_cmpint(fu_version_compare("1.2.3~rc1", "1.2.3~rc1", FWUPD_VERSION_FORMAT_UNKNOWN), + ==, + 0); + g_assert_cmpint(fu_version_compare("1.2.3~rc1", "1.2.3", FWUPD_VERSION_FORMAT_UNKNOWN), + <, + 0); + g_assert_cmpint(fu_version_compare("1.2.3", "1.2.3~rc1", FWUPD_VERSION_FORMAT_UNKNOWN), + >, + 0); + g_assert_cmpint(fu_version_compare("1.2.3~rc2", "1.2.3~rc1", FWUPD_VERSION_FORMAT_UNKNOWN), + >, + 0); + + /* invalid */ + g_assert_cmpint(fu_version_compare("1", NULL, FWUPD_VERSION_FORMAT_UNKNOWN), ==, G_MAXINT); + g_assert_cmpint(fu_version_compare(NULL, "1", FWUPD_VERSION_FORMAT_UNKNOWN), ==, G_MAXINT); + g_assert_cmpint(fu_version_compare(NULL, NULL, FWUPD_VERSION_FORMAT_UNKNOWN), ==, G_MAXINT); +} + +static void +fu_firmware_raw_aligned_func(void) +{ + gboolean ret; + g_autoptr(FuFirmware) firmware1 = fu_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_firmware_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = g_bytes_new_static("hello", 5); + + /* no alignment */ + ret = fu_firmware_parse(firmware1, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* invalid alignment */ + fu_firmware_set_alignment(firmware2, FU_FIRMWARE_ALIGNMENT_4K); + ret = fu_firmware_parse(firmware2, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_false(ret); +} + +static void +fu_firmware_ihex_func(void) +{ + const guint8 *data; + gboolean ret; + gsize len; + g_autofree gchar *filename_hex = NULL; + g_autofree gchar *filename_ref = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new(); + g_autoptr(GBytes) data_file = NULL; + g_autoptr(GBytes) data_fw = NULL; + g_autoptr(GBytes) data_hex = NULL; + g_autoptr(GBytes) data_ref = NULL; + g_autoptr(GError) error = NULL; + + /* load a Intel hex32 file */ + filename_hex = g_test_build_filename(G_TEST_DIST, "tests", "firmware.hex", NULL); + data_file = fu_bytes_get_contents(filename_hex, &error); + g_assert_no_error(error); + g_assert_nonnull(data_file); + ret = fu_firmware_parse(firmware, data_file, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + data_fw = fu_firmware_get_bytes(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_fw); + g_assert_cmpint(g_bytes_get_size(data_fw), ==, 136); + + /* did we match the reference file? */ + filename_ref = g_test_build_filename(G_TEST_DIST, "tests", "firmware.bin", NULL); + data_ref = fu_bytes_get_contents(filename_ref, &error); + g_assert_no_error(error); + g_assert_nonnull(data_ref); + ret = fu_bytes_compare(data_fw, data_ref, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* export a ihex file (which will be slightly different due to + * non-continous regions being expanded */ + data_hex = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_hex); + data = g_bytes_get_data(data_hex, &len); + str = g_strndup((const gchar *)data, len); + g_assert_cmpstr(str, + ==, + ":104000003DEF20F000000000FACF01F0FBCF02F0FE\n" + ":10401000E9CF03F0EACF04F0E1CF05F0E2CF06F0FC\n" + ":10402000D9CF07F0DACF08F0F3CF09F0F4CF0AF0D8\n" + ":10403000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF078\n" + ":104040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF68\n" + ":104050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFA8\n" + ":1040600006C0E2FF05C0E1FF04C0EAFF03C0E9FFAC\n" + ":1040700002C0FBFF01C0FAFF11003FEF20F000017A\n" + ":0840800042EF20F03DEF20F0BB\n" + ":00000001FF\n"); +} + +static void +fu_firmware_ihex_signed_func(void) +{ + const guint8 *data; + gboolean ret; + gsize len; + g_autofree gchar *filename_shex = NULL; + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new(); + g_autoptr(GBytes) data_file = NULL; + g_autoptr(GBytes) data_fw = NULL; + g_autoptr(GBytes) data_sig = NULL; + g_autoptr(GError) error = NULL; + + /* load a signed Intel hex32 file */ + filename_shex = g_test_build_filename(G_TEST_DIST, "tests", "firmware.shex", NULL); + data_file = fu_bytes_get_contents(filename_shex, &error); + g_assert_no_error(error); + g_assert_nonnull(data_file); + ret = fu_firmware_parse(firmware, data_file, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + data_fw = fu_firmware_get_bytes(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_fw); + g_assert_cmpint(g_bytes_get_size(data_fw), ==, 136); + + /* get the signed image */ + data_sig = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_SIGNATURE, &error); + g_assert_no_error(error); + g_assert_nonnull(data_sig); + data = g_bytes_get_data(data_sig, &len); + g_assert_cmpint(len, ==, 8); + g_assert_nonnull(data); + g_assert_cmpint(memcmp(data, "deadbeef", 8), ==, 0); +} + +static void +fu_firmware_ihex_offset_func(void) +{ + const guint8 *data; + gboolean ret; + gsize len; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new(); + g_autoptr(FuFirmware) firmware_verify = fu_ihex_firmware_new(); + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GBytes) data_dummy = NULL; + g_autoptr(GBytes) data_verify = NULL; + g_autoptr(GError) error = NULL; + + /* add a 4 byte image in high memory */ + data_dummy = g_bytes_new_static("foo", 4); + fu_firmware_set_addr(firmware, 0x80000000); + fu_firmware_set_bytes(firmware, data_dummy); + data_bin = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_bin); + data = g_bytes_get_data(data_bin, &len); + str = g_strndup((const gchar *)data, len); + g_assert_cmpstr(str, + ==, + ":0200000480007A\n" + ":04000000666F6F00B8\n" + ":00000001FF\n"); + + /* check we can load it too */ + ret = fu_firmware_parse(firmware_verify, data_bin, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_firmware_get_addr(firmware_verify), ==, 0x80000000); + data_verify = fu_firmware_get_bytes(firmware_verify, &error); + g_assert_no_error(error); + g_assert_nonnull(data_verify); + g_assert_cmpint(g_bytes_get_size(data_verify), ==, 0x4); +} + +static void +fu_firmware_srec_func(void) +{ + gboolean ret; + g_autofree gchar *filename_srec = NULL; + g_autofree gchar *filename_ref = NULL; + g_autoptr(FuFirmware) firmware = fu_srec_firmware_new(); + g_autoptr(GBytes) data_ref = NULL; + g_autoptr(GBytes) data_srec = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GError) error = NULL; + + filename_srec = g_test_build_filename(G_TEST_DIST, "tests", "firmware.srec", NULL); + data_srec = fu_bytes_get_contents(filename_srec, &error); + g_assert_no_error(error); + g_assert_nonnull(data_srec); + ret = fu_firmware_parse(firmware, data_srec, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + data_bin = fu_firmware_get_bytes(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_bin); + g_assert_cmpint(g_bytes_get_size(data_bin), ==, 136); + + /* did we match the reference file? */ + filename_ref = g_test_build_filename(G_TEST_DIST, "tests", "firmware.bin", NULL); + data_ref = fu_bytes_get_contents(filename_ref, &error); + g_assert_no_error(error); + g_assert_nonnull(data_ref); + ret = fu_bytes_compare(data_bin, data_ref, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_firmware_fdt_func(void) +{ + gboolean ret; + guint32 val32 = 0; + guint64 val64 = 0; + g_autofree gchar *filename = NULL; + g_autofree gchar *val = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_fdt_firmware_new(); + g_autoptr(FuFirmware) img1 = NULL; + g_autoptr(FuFdtImage) img2 = NULL; + g_autoptr(GBytes) data = NULL; + g_autoptr(GError) error = NULL; + + filename = g_test_build_filename(G_TEST_DIST, "tests", "fdt.bin", NULL); + data = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(data); + ret = fu_firmware_parse(firmware, data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_fdt_firmware_get_cpuid(FU_FDT_FIRMWARE(firmware)), ==, 0x0); + str = fu_firmware_to_string(firmware); + g_debug("%s", str); + + img1 = fu_firmware_get_image_by_id(firmware, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(img1); + ret = fu_fdt_image_get_attr_str(FU_FDT_IMAGE(img1), "key", &val, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpstr(val, ==, "hello world"); + + /* get image, and get the uint32 attr */ + img2 = fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(firmware), + "/images/firmware-1", + &error); + g_assert_no_error(error); + g_assert_nonnull(img2); + ret = fu_fdt_image_get_attr_u32(FU_FDT_IMAGE(img2), "key", &val32, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(val32, ==, 0x123); + + /* wrong type */ + ret = fu_fdt_image_get_attr_u64(img2, "key", &val64, &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); + g_assert_false(ret); +} + +static void +fu_firmware_fit_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *str = NULL; + g_auto(GStrv) val = NULL; + g_autoptr(FuFdtImage) img1 = NULL; + g_autoptr(FuFirmware) firmware = fu_fit_firmware_new(); + g_autoptr(GBytes) data = NULL; + g_autoptr(GError) error = NULL; + + filename = g_test_build_filename(G_TEST_DIST, "tests", "fit.bin", NULL); + data = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(data); + ret = fu_firmware_parse(firmware, data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_fit_firmware_get_timestamp(FU_FIT_FIRMWARE(firmware)), ==, 0x629D4ABD); + str = fu_firmware_to_string(firmware); + g_debug("%s", str); + + img1 = fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(firmware), + "/configurations/conf-1", + &error); + g_assert_no_error(error); + g_assert_nonnull(img1); + ret = fu_fdt_image_get_attr_strlist(FU_FDT_IMAGE(img1), + FU_FIT_FIRMWARE_ATTR_COMPATIBLE, + &val, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_nonnull(val); + g_assert_cmpstr(val[0], ==, "alice"); + g_assert_cmpstr(val[1], ==, "bob"); + g_assert_cmpstr(val[2], ==, "clara"); + g_assert_cmpstr(val[3], ==, NULL); +} + +static void +fu_firmware_srec_tokenization_func(void) +{ + FuSrecFirmwareRecord *rcd; + GPtrArray *records; + gboolean ret; + g_autoptr(FuFirmware) firmware = fu_srec_firmware_new(); + g_autoptr(GBytes) data_srec = NULL; + g_autoptr(GError) error = NULL; + const gchar *buf = "S3060000001400E5\r\n" + "S31000000002281102000000007F0304002C\r\n" + "S306000000145095\r\n" + "S70500000000FA\r\n"; + data_srec = g_bytes_new_static(buf, strlen(buf)); + g_assert_no_error(error); + g_assert_nonnull(data_srec); + ret = fu_firmware_tokenize(firmware, data_srec, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + records = fu_srec_firmware_get_records(FU_SREC_FIRMWARE(firmware)); + g_assert_nonnull(records); + g_assert_cmpint(records->len, ==, 4); + rcd = g_ptr_array_index(records, 2); + g_assert_nonnull(rcd); + g_assert_cmpint(rcd->ln, ==, 0x3); + g_assert_cmpint(rcd->kind, ==, 3); + g_assert_cmpint(rcd->addr, ==, 0x14); + g_assert_cmpint(rcd->buf->len, ==, 0x1); + g_assert_cmpint(rcd->buf->data[0], ==, 0x50); +} + +static void +fu_firmware_build_func(void) +{ + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *buf = "\n" + "\n" + " 1.2.3\n" + " \n" + " 4.5.6\n" + " header\n" + " 456\n" + " 0x456\n" + " aGVsbG8=\n" + " \n" + " \n" + " 7.8.9\n" + " header\n" + " 789\n" + " 0x789\n" + " \n" + "\n"; + blob = g_bytes_new_static(buf, strlen(buf)); + g_assert_no_error(error); + g_assert_nonnull(blob); + + /* parse XML */ + ret = xb_builder_source_load_bytes(source, blob, XB_BUILDER_SOURCE_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + n = xb_silo_query_first(silo, "firmware", &error); + g_assert_no_error(error); + g_assert_nonnull(n); + + /* build object */ + ret = fu_firmware_build(firmware, n, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpstr(fu_firmware_get_version(firmware), ==, "1.2.3"); + + /* verify image */ + img = fu_firmware_get_image_by_id(firmware, "header", &error); + g_assert_no_error(error); + g_assert_nonnull(img); + g_assert_cmpstr(fu_firmware_get_version(img), ==, "4.5.6"); + g_assert_cmpint(fu_firmware_get_idx(img), ==, 456); + g_assert_cmpint(fu_firmware_get_addr(img), ==, 0x456); + blob2 = fu_firmware_write(img, &error); + g_assert_no_error(error); + g_assert_nonnull(blob2); + g_assert_cmpint(g_bytes_get_size(blob2), ==, 5); + str = g_strndup(g_bytes_get_data(blob2, NULL), g_bytes_get_size(blob2)); + g_assert_cmpstr(str, ==, "hello"); +} + +static gsize +fu_firmware_dfuse_image_get_size(FuFirmware *self) +{ + g_autoptr(GPtrArray) chunks = fu_firmware_get_chunks(self, NULL); + gsize length = 0; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + length += fu_chunk_get_data_sz(chk); + } + return length; +} + +static gsize +fu_firmware_dfuse_get_size(FuFirmware *firmware) +{ + gsize length = 0; + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + for (guint i = 0; i < images->len; i++) { + FuFirmware *image = g_ptr_array_index(images, i); + length += fu_firmware_dfuse_image_get_size(image); + } + return length; +} + +static void +fu_firmware_dfuse_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuFirmware) firmware = fu_dfuse_firmware_new(); + g_autoptr(GBytes) roundtrip_orig = NULL; + g_autoptr(GBytes) roundtrip = NULL; + g_autoptr(GError) error = NULL; + + /* load a DfuSe firmware */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "firmware.dfuse", NULL); + g_assert_nonnull(filename); + roundtrip_orig = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(roundtrip_orig); + ret = fu_firmware_parse(firmware, roundtrip_orig, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_dfu_firmware_get_vid(FU_DFU_FIRMWARE(firmware)), ==, 0x1234); + g_assert_cmpint(fu_dfu_firmware_get_pid(FU_DFU_FIRMWARE(firmware)), ==, 0x5678); + g_assert_cmpint(fu_dfu_firmware_get_release(FU_DFU_FIRMWARE(firmware)), ==, 0x8642); + g_assert_cmpint(fu_firmware_dfuse_get_size(firmware), ==, 0x21); + + /* can we roundtrip without losing data */ + roundtrip = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(roundtrip); + ret = fu_bytes_compare(roundtrip, roundtrip_orig, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_firmware_fmap_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *img_str = NULL; + g_autoptr(FuFirmware) firmware = fu_fmap_firmware_new(); + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) img_blob = NULL; + g_autoptr(GBytes) roundtrip = NULL; + g_autoptr(GBytes) roundtrip_orig = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) images = NULL; + +#ifndef HAVE_MEMMEM + g_test_skip("no memmem()"); + return; +#endif + + /* load firmware */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "fmap-offset.bin", NULL); + g_assert_nonnull(filename); + roundtrip_orig = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(roundtrip_orig); + ret = fu_firmware_parse(firmware, roundtrip_orig, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check image count */ + images = fu_firmware_get_images(firmware); + g_assert_cmpint(images->len, ==, 2); + + /* get a specific image */ + img = fu_firmware_get_image_by_id(firmware, "FMAP", &error); + g_assert_no_error(error); + g_assert_nonnull(img); + img_blob = fu_firmware_get_bytes(img, &error); + g_assert_no_error(error); + g_assert_nonnull(img_blob); + g_assert_cmpint(g_bytes_get_size(img_blob), ==, 0xb); + img_str = g_strndup(g_bytes_get_data(img_blob, NULL), g_bytes_get_size(img_blob)); + g_assert_cmpstr(img_str, ==, "hello world"); + + /* can we roundtrip without losing data */ + roundtrip = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(roundtrip); + ret = fu_bytes_compare(roundtrip, roundtrip_orig, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_firmware_new_from_gtypes_func(void) +{ + g_autofree gchar *fn = NULL; + g_autoptr(FuFirmware) firmware1 = NULL; + g_autoptr(FuFirmware) firmware2 = NULL; + g_autoptr(FuFirmware) firmware3 = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "firmware.dfu", NULL); + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + + /* dfu -> FuDfuFirmware */ + firmware1 = fu_firmware_new_from_gtypes(blob, + FWUPD_INSTALL_FLAG_NONE, + &error, + FU_TYPE_SREC_FIRMWARE, + FU_TYPE_DFUSE_FIRMWARE, + FU_TYPE_DFU_FIRMWARE, + G_TYPE_INVALID); + g_assert_no_error(error); + g_assert_nonnull(firmware1); + g_assert_cmpstr(G_OBJECT_TYPE_NAME(firmware1), ==, "FuDfuFirmware"); + + /* dfu -> FuFirmware */ + firmware2 = fu_firmware_new_from_gtypes(blob, + FWUPD_INSTALL_FLAG_NONE, + &error, + FU_TYPE_SREC_FIRMWARE, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); + g_assert_no_error(error); + g_assert_nonnull(firmware2); + g_assert_cmpstr(G_OBJECT_TYPE_NAME(firmware2), ==, "FuFirmware"); + + /* dfu -> error */ + firmware3 = fu_firmware_new_from_gtypes(blob, + FWUPD_INSTALL_FLAG_NONE, + &error, + FU_TYPE_SREC_FIRMWARE, + G_TYPE_INVALID); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(firmware3); +} + +static void +fu_firmware_linear_func(void) +{ + gboolean ret; + g_autoptr(FuFirmware) firmware1 = fu_linear_firmware_new(FU_TYPE_OPROM_FIRMWARE); + g_autoptr(FuFirmware) firmware2 = fu_linear_firmware_new(FU_TYPE_OPROM_FIRMWARE); + g_autoptr(GBytes) blob1 = g_bytes_new_static("XXXX", 4); + g_autoptr(GBytes) blob2 = g_bytes_new_static("HELO", 4); + g_autoptr(GBytes) blob3 = NULL; + g_autoptr(FuFirmware) img1 = fu_oprom_firmware_new(); + g_autoptr(FuFirmware) img2 = fu_oprom_firmware_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) imgs = NULL; + g_autofree gchar *str = NULL; + + /* add images then parse */ + fu_firmware_set_bytes(img1, blob1); + fu_firmware_add_image(firmware1, img1); + fu_firmware_set_bytes(img2, blob2); + fu_firmware_add_image(firmware1, img2); + blob3 = fu_firmware_write(firmware1, &error); + g_assert_no_error(error); + g_assert_nonnull(blob3); + g_assert_cmpint(g_bytes_get_size(blob3), ==, 1024); + + /* parse them back */ + ret = fu_firmware_parse(firmware2, blob3, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + str = fu_firmware_to_string(firmware2); + g_debug("\n%s", str); + + /* verify we got both images */ + imgs = fu_firmware_get_images(firmware2); + g_assert_cmpint(imgs->len, ==, 2); +} + +static void +fu_firmware_dfu_func(void) +{ + gboolean ret; + g_autofree gchar *filename_dfu = NULL; + g_autofree gchar *filename_ref = NULL; + g_autoptr(FuFirmware) firmware = fu_dfu_firmware_new(); + g_autoptr(GBytes) data_ref = NULL; + g_autoptr(GBytes) data_dfu = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GError) error = NULL; + + filename_dfu = g_test_build_filename(G_TEST_DIST, "tests", "firmware.dfu", NULL); + data_dfu = fu_bytes_get_contents(filename_dfu, &error); + g_assert_no_error(error); + g_assert_nonnull(data_dfu); + ret = fu_firmware_parse(firmware, data_dfu, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_dfu_firmware_get_vid(FU_DFU_FIRMWARE(firmware)), ==, 0x1234); + g_assert_cmpint(fu_dfu_firmware_get_pid(FU_DFU_FIRMWARE(firmware)), ==, 0x4321); + g_assert_cmpint(fu_dfu_firmware_get_release(FU_DFU_FIRMWARE(firmware)), ==, 0xdead); + data_bin = fu_firmware_get_bytes(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_bin); + g_assert_cmpint(g_bytes_get_size(data_bin), ==, 136); + + /* did we match the reference file? */ + filename_ref = g_test_build_filename(G_TEST_DIST, "tests", "firmware.bin", NULL); + data_ref = fu_bytes_get_contents(filename_ref, &error); + g_assert_no_error(error); + g_assert_nonnull(data_ref); + ret = fu_bytes_compare(data_bin, data_ref, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_firmware_ifwi_cpd_func(void) +{ + gboolean ret; + g_autofree gchar *filename_ifwi_cpd = NULL; + g_autoptr(FuFirmware) firmware = fu_ifwi_cpd_firmware_new(); + g_autoptr(FuFirmware) img1 = NULL; + g_autoptr(FuFirmware) img2 = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GBytes) data_ifwi_cpd = NULL; + g_autoptr(GError) error = NULL; + + filename_ifwi_cpd = g_test_build_filename(G_TEST_DIST, "tests", "ifwi-cpd.bin", NULL); + data_ifwi_cpd = fu_bytes_get_contents(filename_ifwi_cpd, &error); + g_assert_no_error(error); + g_assert_nonnull(data_ifwi_cpd); + ret = fu_firmware_parse(firmware, data_ifwi_cpd, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_firmware_get_idx(firmware), ==, 0x1234); + data_bin = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_bin); + g_assert_cmpint(g_bytes_get_size(data_bin), ==, 90); + + img1 = fu_firmware_get_image_by_id(firmware, "one", &error); + g_assert_no_error(error); + g_assert_nonnull(img1); + g_assert_cmpint(fu_firmware_get_offset(img1), ==, 68); + g_assert_cmpint(fu_firmware_get_size(img1), ==, 11); + + img2 = fu_firmware_get_image_by_id(firmware, "two", &error); + g_assert_no_error(error); + g_assert_nonnull(img2); + g_assert_cmpint(fu_firmware_get_offset(img2), ==, 79); + g_assert_cmpint(fu_firmware_get_size(img2), ==, 11); +} + +static void +fu_firmware_ifwi_fpt_func(void) +{ + gboolean ret; + g_autofree gchar *filename_ifwi_fpt = NULL; + g_autoptr(FuFirmware) firmware = fu_ifwi_fpt_firmware_new(); + g_autoptr(FuFirmware) img1 = NULL; + g_autoptr(FuFirmware) img2 = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GBytes) data_ifwi_fpt = NULL; + g_autoptr(GError) error = NULL; + + filename_ifwi_fpt = g_test_build_filename(G_TEST_DIST, "tests", "ifwi-fpt.bin", NULL); + data_ifwi_fpt = fu_bytes_get_contents(filename_ifwi_fpt, &error); + g_assert_no_error(error); + g_assert_nonnull(data_ifwi_fpt); + ret = fu_firmware_parse(firmware, data_ifwi_fpt, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + data_bin = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_bin); + g_assert_cmpint(g_bytes_get_size(data_bin), ==, 118); + + img1 = fu_firmware_get_image_by_idx(firmware, 0x4f464e49, &error); + g_assert_no_error(error); + g_assert_nonnull(img1); + g_assert_cmpint(fu_firmware_get_offset(img1), ==, 96); + g_assert_cmpint(fu_firmware_get_size(img1), ==, 11); + + img2 = fu_firmware_get_image_by_idx(firmware, 0x4d495746, &error); + g_assert_no_error(error); + g_assert_nonnull(img2); + g_assert_cmpint(fu_firmware_get_offset(img2), ==, 107); + g_assert_cmpint(fu_firmware_get_size(img2), ==, 11); +} + +static void +fu_firmware_oprom_func(void) +{ + gboolean ret; + g_autofree gchar *filename_oprom = NULL; + g_autoptr(FuFirmware) firmware = fu_oprom_firmware_new(); + g_autoptr(FuFirmware) img1 = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GBytes) data_oprom = NULL; + g_autoptr(GError) error = NULL; + + filename_oprom = g_test_build_filename(G_TEST_DIST, "tests", "oprom.bin", NULL); + data_oprom = fu_bytes_get_contents(filename_oprom, &error); + g_assert_no_error(error); + g_assert_nonnull(data_oprom); + ret = fu_firmware_parse(firmware, data_oprom, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_firmware_get_idx(firmware), ==, 0x1); + data_bin = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_bin); + g_assert_cmpint(g_bytes_get_size(data_bin), ==, 1024); + + img1 = fu_firmware_get_image_by_id(firmware, "cpd", &error); + g_assert_no_error(error); + g_assert_nonnull(img1); + g_assert_cmpint(fu_firmware_get_offset(img1), ==, 512); + g_assert_cmpint(fu_firmware_get_size(img1), ==, 512); +} + +static void +fu_firmware_dfu_patch_func(void) +{ + gboolean ret; + g_autofree gchar *csum = NULL; + g_autofree gchar *filename_dfu = NULL; + g_autoptr(FuFirmware) firmware = fu_dfu_firmware_new(); + g_autoptr(GBytes) data_dfu = NULL; + g_autoptr(GBytes) data_new = NULL; + g_autoptr(GBytes) data_patch0 = g_bytes_new_static("XXXX", 4); + g_autoptr(GBytes) data_patch1 = g_bytes_new_static("HELO", 4); + g_autoptr(GError) error = NULL; + + filename_dfu = g_test_build_filename(G_TEST_DIST, "tests", "firmware.dfu", NULL); + data_dfu = fu_bytes_get_contents(filename_dfu, &error); + g_assert_no_error(error); + g_assert_nonnull(data_dfu); + ret = fu_firmware_parse(firmware, data_dfu, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a couple of patches */ + fu_firmware_add_patch(firmware, 0x0, data_patch0); + fu_firmware_add_patch(firmware, 0x0, data_patch1); + fu_firmware_add_patch(firmware, 136 - 4, data_patch1); + + data_new = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(data_new); + fu_dump_full(G_LOG_DOMAIN, + "patch", + g_bytes_get_data(data_new, NULL), + g_bytes_get_size(data_new), + 20, + FU_DUMP_FLAGS_SHOW_ASCII | FU_DUMP_FLAGS_SHOW_ADDRESSES); + csum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, data_new); + g_assert_cmpstr(csum, ==, "0722727426092ac564861d1a11697182017be83f"); +} + +static void +fu_firmware_func(void) +{ + gboolean ret; + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(FuFirmware) img1 = fu_firmware_new(); + g_autoptr(FuFirmware) img2 = fu_firmware_new(); + g_autoptr(FuFirmware) img_id = NULL; + g_autoptr(FuFirmware) img_idx = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) images = NULL; + g_autofree gchar *str = NULL; + + fu_firmware_set_addr(img1, 0x200); + fu_firmware_set_idx(img1, 13); + fu_firmware_set_id(img1, "primary"); + fu_firmware_set_filename(img1, "BIOS.bin"); + fu_firmware_add_image(firmware, img1); + fu_firmware_set_addr(img2, 0x400); + fu_firmware_set_idx(img2, 23); + fu_firmware_set_id(img2, "secondary"); + fu_firmware_add_image(firmware, img2); + + img_id = fu_firmware_get_image_by_id(firmware, "NotGoingToExist", &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(img_id); + g_clear_error(&error); + img_id = fu_firmware_get_image_by_id(firmware, "primary", &error); + g_assert_no_error(error); + g_assert_nonnull(img_id); + g_assert_cmpint(fu_firmware_get_addr(img_id), ==, 0x200); + g_assert_cmpint(fu_firmware_get_idx(img_id), ==, 13); + g_assert_cmpstr(fu_firmware_get_id(img_id), ==, "primary"); + + img_idx = fu_firmware_get_image_by_idx(firmware, 123456, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(img_idx); + g_clear_error(&error); + img_idx = fu_firmware_get_image_by_idx(firmware, 23, &error); + g_assert_no_error(error); + g_assert_nonnull(img_idx); + g_assert_cmpint(fu_firmware_get_addr(img_idx), ==, 0x400); + g_assert_cmpint(fu_firmware_get_idx(img_idx), ==, 23); + g_assert_cmpstr(fu_firmware_get_id(img_idx), ==, "secondary"); + + str = fu_firmware_to_string(firmware); + g_assert_cmpstr(str, + ==, + "\n" + " \n" + " primary\n" + " 0xd\n" + " 0x200\n" + " BIOS.bin\n" + " \n" + " \n" + " secondary\n" + " 0x17\n" + " 0x400\n" + " \n" + "\n"); + + ret = fu_firmware_remove_image_by_idx(firmware, 0xd, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_remove_image_by_id(firmware, "secondary", &error); + g_assert_no_error(error); + g_assert_true(ret); + images = fu_firmware_get_images(firmware); + g_assert_nonnull(images); + g_assert_cmpint(images->len, ==, 0); + ret = fu_firmware_remove_image_by_id(firmware, "NOTGOINGTOEXIST", &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); +} + +static void +fu_firmware_common_func(void) +{ + gboolean ret; + guint8 value = 0; + g_autoptr(GError) error = NULL; + + ret = fu_firmware_strparse_uint8_safe("ff00XX", 6, 0, &value, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(value, ==, 0xFF); + + ret = fu_firmware_strparse_uint8_safe("ff00XX", 6, 2, &value, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(value, ==, 0x00); + + ret = fu_firmware_strparse_uint8_safe("ff00XX", 6, 4, &value, &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); + g_assert_false(ret); +} + +static void +fu_firmware_dedupe_func(void) +{ + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(FuFirmware) img1 = fu_firmware_new(); + g_autoptr(FuFirmware) img1_old = fu_firmware_new(); + g_autoptr(FuFirmware) img2 = fu_firmware_new(); + g_autoptr(FuFirmware) img2_old = fu_firmware_new(); + g_autoptr(FuFirmware) img_id = NULL; + g_autoptr(FuFirmware) img_idx = NULL; + g_autoptr(GError) error = NULL; + + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_DEDUPE_ID); + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_DEDUPE_IDX); + + fu_firmware_set_idx(img1_old, 13); + fu_firmware_set_id(img1_old, "DAVE"); + fu_firmware_add_image(firmware, img1_old); + g_assert_true(fu_firmware_get_parent(img1_old) == firmware); + + fu_firmware_set_idx(img1, 13); + fu_firmware_set_id(img1, "primary"); + fu_firmware_add_image(firmware, img1); + + fu_firmware_set_idx(img2_old, 123456); + fu_firmware_set_id(img2_old, "secondary"); + fu_firmware_add_image(firmware, img2_old); + + fu_firmware_set_idx(img2, 23); + fu_firmware_set_id(img2, "secondary"); + fu_firmware_add_image(firmware, img2); + + img_id = fu_firmware_get_image_by_id(firmware, "primary", &error); + g_assert_no_error(error); + g_assert_nonnull(img_id); + g_assert_cmpint(fu_firmware_get_idx(img_id), ==, 13); + g_assert_cmpstr(fu_firmware_get_id(img_id), ==, "primary"); + + img_idx = fu_firmware_get_image_by_idx(firmware, 23, &error); + g_assert_no_error(error); + g_assert_nonnull(img_idx); + g_assert_cmpint(fu_firmware_get_idx(img_idx), ==, 23); + g_assert_cmpstr(fu_firmware_get_id(img_idx), ==, "secondary"); +} + +static void +fu_efivar_func(void) +{ + gboolean ret; + gsize sz = 0; + guint32 attr = 0; + guint64 total; + g_autofree gchar *sysfsfwdir = NULL; + g_autofree guint8 *data = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) names = NULL; + +#ifndef __linux__ + g_test_skip("only works on Linux"); + return; +#endif + + /* these tests will write */ + sysfsfwdir = g_test_build_filename(G_TEST_BUILT, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWDIR", sysfsfwdir, TRUE); + + /* check supported */ + ret = fu_efivar_supported(&error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check we can get the space used */ + total = fu_efivar_space_used(&error); + g_assert_no_error(error); + g_assert_cmpint(total, >=, 0x2000); + + /* check existing keys */ + g_assert_false(fu_efivar_exists(FU_EFIVAR_GUID_EFI_GLOBAL, "NotGoingToExist")); + g_assert_true(fu_efivar_exists(FU_EFIVAR_GUID_EFI_GLOBAL, "SecureBoot")); + + /* list a few keys */ + names = fu_efivar_get_names(FU_EFIVAR_GUID_EFI_GLOBAL, &error); + g_assert_no_error(error); + g_assert_nonnull(names); + g_assert_cmpint(names->len, ==, 2); + + /* write and read a key */ + ret = fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "Test", + (guint8 *)"1", + 1, + FU_EFIVAR_ATTR_NON_VOLATILE | FU_EFIVAR_ATTR_RUNTIME_ACCESS, + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivar_get_data(FU_EFIVAR_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_EFIVAR_ATTR_NON_VOLATILE | FU_EFIVAR_ATTR_RUNTIME_ACCESS); + g_assert_cmpint(data[0], ==, '1'); + + /* delete single key */ + ret = fu_efivar_delete(FU_EFIVAR_GUID_EFI_GLOBAL, "Test", &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_false(fu_efivar_exists(FU_EFIVAR_GUID_EFI_GLOBAL, "Test")); + + /* delete multiple keys */ + ret = fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, "Test1", (guint8 *)"1", 1, 0, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, "Test2", (guint8 *)"1", 1, 0, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivar_delete_with_glob(FU_EFIVAR_GUID_EFI_GLOBAL, "Test*", &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_false(fu_efivar_exists(FU_EFIVAR_GUID_EFI_GLOBAL, "Test1")); + g_assert_false(fu_efivar_exists(FU_EFIVAR_GUID_EFI_GLOBAL, "Test2")); + + /* read a key that doesn't exist */ + ret = fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "NotGoingToExist", + NULL, + NULL, + NULL, + &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert_false(ret); +} + +typedef struct { + guint cnt_success; + guint cnt_failed; +} FuDeviceRetryHelper; + +static gboolean +fu_device_retry_success(FuDevice *device, gpointer user_data, GError **error) +{ + FuDeviceRetryHelper *helper = (FuDeviceRetryHelper *)user_data; + helper->cnt_success++; + return TRUE; +} + +static gboolean +fu_device_retry_failed(FuDevice *device, gpointer user_data, GError **error) +{ + FuDeviceRetryHelper *helper = (FuDeviceRetryHelper *)user_data; + helper->cnt_failed++; + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed"); + return FALSE; +} + +static gboolean +fu_device_retry_success_3rd_try(FuDevice *device, gpointer user_data, GError **error) +{ + FuDeviceRetryHelper *helper = (FuDeviceRetryHelper *)user_data; + if (helper->cnt_failed == 2) { + helper->cnt_success++; + return TRUE; + } + helper->cnt_failed++; + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed"); + return FALSE; +} + +static void +fu_device_retry_success_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(GError) error = NULL; + FuDeviceRetryHelper helper = { + .cnt_success = 0, + .cnt_failed = 0, + }; + fu_device_retry_add_recovery(device, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + fu_device_retry_failed); + ret = fu_device_retry(device, fu_device_retry_success, 3, &helper, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(helper.cnt_success, ==, 1); + g_assert_cmpint(helper.cnt_failed, ==, 0); +} + +static void +fu_device_retry_failed_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(GError) error = NULL; + FuDeviceRetryHelper helper = { + .cnt_success = 0, + .cnt_failed = 0, + }; + fu_device_retry_add_recovery(device, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + fu_device_retry_success); + ret = fu_device_retry(device, fu_device_retry_failed, 3, &helper, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); + g_assert_true(!ret); + g_assert_cmpint(helper.cnt_success, ==, 2); /* do not reset for the last failure */ + g_assert_cmpint(helper.cnt_failed, ==, 3); +} + +static void +fu_device_retry_hardware_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(GError) error = NULL; + FuDeviceRetryHelper helper = { + .cnt_success = 0, + .cnt_failed = 0, + }; + ret = fu_device_retry(device, fu_device_retry_success_3rd_try, 3, &helper, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(helper.cnt_success, ==, 1); + g_assert_cmpint(helper.cnt_failed, ==, 2); +} + +static void +fu_bios_settings_load_func(void) +{ + gboolean ret; + gint integer; + const gchar *tmp; + GPtrArray *values; + FwupdBiosSetting *setting; + FwupdBiosSettingKind kind; + g_autofree gchar *test_dir = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(GError) error = NULL; + g_autoptr(FuBiosSettings) p620_settings = NULL; + g_autoptr(FuBiosSettings) p14s_settings = NULL; + g_autoptr(FuBiosSettings) xp29310_settings = NULL; + g_autoptr(GPtrArray) p14s_items = NULL; + g_autoptr(GPtrArray) p620_items = NULL; + g_autoptr(GPtrArray) xps9310_items = NULL; + + /* load BIOS settings from a Lenovo P620 (with thinklmi driver problems) */ + test_dir = g_test_build_filename(G_TEST_DIST, "tests", "bios-attrs", "lenovo-p620", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + + g_test_expect_message("FuBiosSettings", G_LOG_LEVEL_WARNING, "*BUG*"); + ret = fu_context_reload_bios_settings(ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_test_assert_expected_messages(); + + p620_settings = fu_context_get_bios_settings(ctx); + p620_items = fu_bios_settings_get_all(p620_settings); + g_assert_cmpint(p620_items->len, ==, 128); + + /* make sure nothing pending */ + ret = fu_context_get_bios_setting_pending_reboot(ctx); + g_assert_false(ret); + + /* check a BIOS setting reads from kernel as expected by fwupd today */ + setting = fu_context_get_bios_setting(ctx, "com.thinklmi.AMDMemoryGuard"); + g_assert_nonnull(setting); + tmp = fwupd_bios_setting_get_name(setting); + g_assert_cmpstr(tmp, ==, "AMDMemoryGuard"); + tmp = fwupd_bios_setting_get_description(setting); + g_assert_cmpstr(tmp, ==, "AMDMemoryGuard"); + tmp = fwupd_bios_setting_get_current_value(setting); + g_assert_cmpstr(tmp, ==, "Disable"); + values = fwupd_bios_setting_get_possible_values(setting); + for (guint i = 0; i < values->len; i++) { + const gchar *possible = g_ptr_array_index(values, i); + if (i == 0) + g_assert_cmpstr(possible, ==, "Disable"); + if (i == 1) + g_assert_cmpstr(possible, ==, "Enable"); + } + + /* try to read an BIOS setting known to have ][Status] to make sure we worked + * around the thinklmi bug sufficiently + */ + setting = fu_context_get_bios_setting(ctx, "com.thinklmi.StartupSequence"); + g_assert_nonnull(setting); + tmp = fwupd_bios_setting_get_current_value(setting); + g_assert_cmpstr(tmp, ==, "Primary"); + values = fwupd_bios_setting_get_possible_values(setting); + for (guint i = 0; i < values->len; i++) { + const gchar *possible = g_ptr_array_index(values, i); + if (i == 0) + g_assert_cmpstr(possible, ==, "Primary"); + if (i == 1) + g_assert_cmpstr(possible, ==, "Automatic"); + } + + /* check no BIOS settings have [Status in them */ + for (guint i = 0; i < p620_items->len; i++) { + setting = g_ptr_array_index(p620_items, i); + tmp = fwupd_bios_setting_get_current_value(setting); + g_debug("%s", tmp); + g_assert_null(g_strrstr(tmp, "[Status")); + } + + g_free(test_dir); + + /* load BIOS settings from a Lenovo P14s Gen1 */ + test_dir = + g_test_build_filename(G_TEST_DIST, "tests", "bios-attrs", "lenovo-p14s-gen1", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + ret = fu_context_reload_bios_settings(ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + + p14s_settings = fu_context_get_bios_settings(ctx); + p14s_items = fu_bios_settings_get_all(p14s_settings); + g_assert_cmpint(p14s_items->len, ==, 75); + + /* reboot should be pending on this one */ + ret = fu_context_get_bios_setting_pending_reboot(ctx); + g_assert_true(ret); + + /* look for an enumeration BIOS setting with a space */ + setting = fu_context_get_bios_setting(ctx, "com.thinklmi.SleepState"); + g_assert_nonnull(setting); + tmp = fwupd_bios_setting_get_name(setting); + g_assert_cmpstr(tmp, ==, "SleepState"); + tmp = fwupd_bios_setting_get_description(setting); + g_assert_cmpstr(tmp, ==, "SleepState"); + values = fwupd_bios_setting_get_possible_values(setting); + for (guint i = 0; i < values->len; i++) { + const gchar *possible = g_ptr_array_index(values, i); + if (i == 0) + g_assert_cmpstr(possible, ==, "Linux"); + if (i == 1) + g_assert_cmpstr(possible, ==, "Windows 10"); + } + + /* make sure we defaulted UEFI Secure boot to read only if enabled */ + setting = fu_context_get_bios_setting(ctx, "com.thinklmi.SecureBoot"); + g_assert_nonnull(setting); + ret = fwupd_bios_setting_get_read_only(setting); + g_assert_true(ret); + + g_free(test_dir); + + /* load BIOS settings from a Dell XPS 9310 */ + test_dir = + g_test_build_filename(G_TEST_DIST, "tests", "bios-attrs", "dell-xps13-9310", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + ret = fu_context_reload_bios_settings(ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + + xp29310_settings = fu_context_get_bios_settings(ctx); + xps9310_items = fu_bios_settings_get_all(xp29310_settings); + g_assert_cmpint(xps9310_items->len, ==, 109); + + /* make sure that we DIDN'T parse reset_bios setting */ + setting = fu_context_get_bios_setting(ctx, FWUPD_BIOS_SETTING_RESET_BIOS); + g_assert_null(setting); + + /* look at a integer BIOS setting */ + setting = fu_context_get_bios_setting(ctx, "com.dell-wmi-sysman.CustomChargeStop"); + g_assert_nonnull(setting); + kind = fwupd_bios_setting_get_kind(setting); + g_assert_cmpint(kind, ==, FWUPD_BIOS_SETTING_KIND_INTEGER); + integer = fwupd_bios_setting_get_lower_bound(setting); + g_assert_cmpint(integer, ==, 55); + integer = fwupd_bios_setting_get_upper_bound(setting); + g_assert_cmpint(integer, ==, 100); + integer = fwupd_bios_setting_get_scalar_increment(setting); + g_assert_cmpint(integer, ==, 1); + + /* look at a string BIOS setting */ + setting = fu_context_get_bios_setting(ctx, "com.dell-wmi-sysman.Asset"); + g_assert_nonnull(setting); + integer = fwupd_bios_setting_get_lower_bound(setting); + g_assert_cmpint(integer, ==, 1); + integer = fwupd_bios_setting_get_upper_bound(setting); + g_assert_cmpint(integer, ==, 64); + tmp = fwupd_bios_setting_get_description(setting); + g_assert_cmpstr(tmp, ==, "Asset Tag"); + + /* look at a enumeration BIOS setting */ + setting = fu_context_get_bios_setting(ctx, "com.dell-wmi-sysman.BiosRcvrFrmHdd"); + g_assert_nonnull(setting); + kind = fwupd_bios_setting_get_kind(setting); + g_assert_cmpint(kind, ==, FWUPD_BIOS_SETTING_KIND_ENUMERATION); + values = fwupd_bios_setting_get_possible_values(setting); + for (guint i = 0; i < values->len; i++) { + const gchar *possible = g_ptr_array_index(values, i); + if (i == 0) + g_assert_cmpstr(possible, ==, "Disabled"); + if (i == 1) + g_assert_cmpstr(possible, ==, "Enabled"); + } + + /* make sure we defaulted UEFI Secure boot to read only if enabled */ + setting = fu_context_get_bios_setting(ctx, "com.dell-wmi-sysman.SecureBoot"); + g_assert_nonnull(setting); + ret = fwupd_bios_setting_get_read_only(setting); + g_assert_true(ret); +} + +static void +fu_security_attrs_hsi_func(void) +{ + g_autofree gchar *hsi1 = NULL; + g_autofree gchar *hsi2 = NULL; + g_autofree gchar *hsi3 = NULL; + g_autofree gchar *hsi4 = NULL; + g_autofree gchar *hsi5 = NULL; + g_autofree gchar *hsi6 = NULL; + g_autofree gchar *hsi7 = NULL; + g_autofree gchar *hsi8 = NULL; + g_autofree gchar *hsi9 = NULL; + g_autofree gchar *expected_hsi9 = NULL; + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* no attrs */ + attrs = fu_security_attrs_new(); + hsi1 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi1, ==, "HSI:0"); + + /* just success from HSI:1 */ + attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + hsi2 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi2, ==, "HSI:1"); + g_clear_object(&attr); + + /* add failed from HSI:2, so still HSI:1 */ + attr = fwupd_security_attr_new("org.fwupd.hsi.PRX"); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + hsi3 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi3, ==, "HSI:1"); + g_clear_object(&attr); + + /* add an implicit obsolete via duplication */ + attr = fwupd_security_attr_new("org.fwupd.hsi.PRX"); + fwupd_security_attr_set_plugin(attr, "other-plugin"); + fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT); + fwupd_security_attr_set_url(attr, "http://other-plugin"); + fu_security_attrs_append(attrs, attr); + fu_security_attrs_depsolve(attrs); + hsi4 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi4, ==, "HSI:1"); + g_assert_true(fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)); + g_clear_object(&attr); + + /* add attr from HSI:3, obsoleting the failure */ + attr = fwupd_security_attr_new("org.fwupd.hsi.BIOSGuard"); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_obsolete(attr, "org.fwupd.hsi.PRX"); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + fu_security_attrs_depsolve(attrs); + hsi5 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi5, ==, "HSI:3"); + g_clear_object(&attr); + + /* add taint that was fine */ + attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + hsi6 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi6, ==, "HSI:3"); + g_clear_object(&attr); + + /* add updates and attestation */ + attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + hsi7 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi7, ==, "HSI:3"); + g_clear_object(&attr); + + /* add issue that was uncool */ + attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + hsi8 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_NONE); + g_assert_cmpstr(hsi8, ==, "HSI:3!"); + g_clear_object(&attr); + + /* show version in the attribute */ + attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP); + fwupd_security_attr_set_plugin(attr, "test"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_url(attr, "http://test"); + fu_security_attrs_append(attrs, attr); + hsi9 = fu_security_attrs_calculate_hsi(attrs, FU_SECURITY_ATTRS_FLAG_ADD_VERSION); + expected_hsi9 = g_strdup_printf("HSI:3! (v%d.%d.%d)", + FWUPD_MAJOR_VERSION, + FWUPD_MINOR_VERSION, + FWUPD_MICRO_VERSION); + g_assert_cmpstr(hsi9, ==, expected_hsi9); + g_clear_object(&attr); +} +static void +fu_firmware_builder_round_trip_func(void) +{ + struct { + GType gtype; + const gchar *xml_fn; + const gchar *checksum; + } map[] = { + {FU_TYPE_DFUSE_FIRMWARE, + "dfuse.builder.xml", + "c1ff429f0e381c8fe8e1b2ee41a5a9a79e2f2ff7"}, + {FU_TYPE_FDT_FIRMWARE, "fdt.builder.xml", "40f7fbaff684a6bcf67c81b3079422c2529741e1"}, + {FU_TYPE_FIT_FIRMWARE, "fit.builder.xml", "293ce07351bb7d76631c4e2ba47243db1e150f3c"}, + {FU_TYPE_SREC_FIRMWARE, "srec.builder.xml", "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"}, + {FU_TYPE_IHEX_FIRMWARE, "ihex.builder.xml", "a8d74f767f3fc992b413e5ba801cedc80a4cf013"}, + {FU_TYPE_FMAP_FIRMWARE, "fmap.builder.xml", "a0b9ffc10a586d217edf9e9bae7c1fe7c564ea01"}, + {FU_TYPE_EFI_FIRMWARE_SECTION, + "efi-firmware-section.builder.xml", + "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"}, + {FU_TYPE_EFI_FIRMWARE_SECTION, + "efi-firmware-section.builder.xml", + "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"}, + {FU_TYPE_EFI_FIRMWARE_FILE, + "efi-firmware-file.builder.xml", + "1002c14b29a76069f3b7e35c50a55d2b0d197441"}, + {FU_TYPE_EFI_FIRMWARE_FILESYSTEM, + "efi-firmware-filesystem.builder.xml", + "d6fbadc1c303a3b4eede9db7fb0ddb353efffc86"}, + {FU_TYPE_EFI_FIRMWARE_VOLUME, + "efi-firmware-volume.builder.xml", + "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"}, + {FU_TYPE_IFD_FIRMWARE, "ifd.builder.xml", "0805c742e0deec12db2d8f9a86158a7cf610869b"}, + {FU_TYPE_CFU_OFFER, + "cfu-offer.builder.xml", + "acc572d03a129081921c36118b527dab34a077ad"}, + {FU_TYPE_CFU_PAYLOAD, + "cfu-payload.builder.xml", + "5da829f5fd15a28970aed98ebb26ebf2f88ed6f2"}, + {FU_TYPE_IFWI_CPD_FIRMWARE, + "ifwi-cpd.builder.xml", + "91e348d17cb91ef7a528e85beb39d15a0532dca5"}, + {FU_TYPE_IFWI_FPT_FIRMWARE, + "ifwi-fpt.builder.xml", + "d1f0fb2c2a7a99441bf4a825d060642315a94d91"}, + {FU_TYPE_OPROM_FIRMWARE, + "oprom.builder.xml", + "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"}, + {FU_TYPE_INTEL_THUNDERBOLT_NVM, + "intel-thunderbolt.builder.xml", + "e858000646fecb5223b41df57647c005b495749b"}, +#ifdef HAVE_CBOR + {FU_TYPE_USWID_FIRMWARE, + "uswid.builder.xml", + "b4631ebb64931da604500b9a7263225708195f54"}, +#endif + {G_TYPE_INVALID, NULL, NULL}}; + g_type_ensure(FU_TYPE_COSWID_FIRMWARE); + for (guint i = 0; map[i].gtype != G_TYPE_INVALID; i++) { + gboolean ret; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *filename = NULL; + g_autofree gchar *xml1 = NULL; + g_autofree gchar *xml2 = NULL; + g_autoptr(FuFirmware) firmware1 = g_object_new(map[i].gtype, NULL); + g_autoptr(FuFirmware) firmware2 = g_object_new(map[i].gtype, NULL); + g_autoptr(FuFirmware) firmware3 = g_object_new(map[i].gtype, NULL); + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", map[i].xml_fn, NULL); + ret = g_file_get_contents(filename, &xml1, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml1, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, map[i].checksum); + + /* ensure we can write and then parse what we just wrote */ + blob = fu_firmware_write(firmware1, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + ret = fu_firmware_parse(firmware3, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + if (!ret) + g_prefix_error(&error, "%s: ", map[i].xml_fn); + g_assert_no_error(error); + g_assert_true(ret); + + /* ensure we can round-trip */ + xml2 = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml2, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum2, ==, map[i].checksum); + } +} + +typedef struct { + guint last_percentage; + guint updates; +} FuProgressHelper; + +static void +fu_progress_percentage_changed_cb(FuProgress *progress, guint percentage, gpointer data) +{ + FuProgressHelper *helper = (FuProgressHelper *)data; + helper->last_percentage = percentage; + helper->updates++; +} + +static void +fu_progress_func(void) +{ + FuProgressHelper helper = {0}; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autofree gchar *str = NULL; + + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_progress_percentage_changed_cb), + &helper); + + g_assert_cmpfloat_with_epsilon(fu_progress_get_duration(progress), 0.f, 0.001); + + fu_progress_set_profile(progress, TRUE); + fu_progress_set_steps(progress, 5); + g_assert_cmpint(helper.last_percentage, ==, 0); + + g_usleep(20 * 1000); + fu_progress_step_done(progress); + g_assert_cmpint(helper.updates, ==, 2); + g_assert_cmpint(helper.last_percentage, ==, 20); + + for (guint i = 0; i < 4; i++) { + g_usleep(20 * 1000); + fu_progress_step_done(progress); + } + + g_assert_cmpint(helper.last_percentage, ==, 100); + g_assert_cmpint(helper.updates, ==, 6); + g_assert_cmpfloat_with_epsilon(fu_progress_get_duration(progress), 0.1f, 0.05); + str = fu_progress_traceback(progress); + g_debug("\n%s", str); +} + +static void +fu_progress_child_func(void) +{ + FuProgressHelper helper = {0}; + FuProgress *child; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* reset */ + fu_progress_set_profile(progress, TRUE); + fu_progress_set_steps(progress, 2); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_progress_percentage_changed_cb), + &helper); + + /* parent: |-----------------------|-----------------------| + * step1: |-----------------------| + * child: |-------------|---------| + */ + + /* PARENT UPDATE */ + g_debug("parent update #1"); + fu_progress_step_done(progress); + g_assert_cmpint(helper.updates, ==, 1); + g_assert_cmpint(helper.last_percentage, ==, 50); + + /* now test with a child */ + child = fu_progress_get_child(progress); + fu_progress_set_id(child, G_STRLOC); + fu_progress_set_steps(child, 2); + + g_debug("child update #1"); + fu_progress_step_done(child); + g_assert_cmpint(helper.updates, ==, 2); + g_assert_cmpint(helper.last_percentage, ==, 75); + + /* child update */ + g_debug("child update #2"); + fu_progress_step_done(child); + g_assert_cmpint(helper.updates, ==, 3); + g_assert_cmpint(helper.last_percentage, ==, 100); + + /* parent update */ + g_debug("parent update #2"); + fu_progress_step_done(progress); + + /* ensure we ignored the duplicate */ + g_assert_cmpint(helper.updates, ==, 3); + g_assert_cmpint(helper.last_percentage, ==, 100); +} + +static void +fu_progress_parent_one_step_proxy_func(void) +{ + FuProgressHelper helper = {0}; + FuProgress *child; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* one step */ + fu_progress_set_steps(progress, 1); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_progress_percentage_changed_cb), + &helper); + + /* now test with a child */ + child = fu_progress_get_child(progress); + fu_progress_set_id(child, G_STRLOC); + fu_progress_set_steps(child, 2); + + /* child set value */ + fu_progress_set_percentage(child, 33); + + /* ensure 1 updates for progress with one step and ensure using child value as parent */ + g_assert_cmpint(helper.updates, ==, 1); + g_assert_cmpint(helper.last_percentage, ==, 33); +} + +static void +fu_progress_non_equal_steps_func(void) +{ + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + FuProgress *child; + FuProgress *grandchild; + + /* test non-equal steps */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 60, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 20, NULL); + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 0); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_ERASE); + + /* child step should increment according to the custom steps */ + child = fu_progress_get_child(progress); + fu_progress_set_id(child, G_STRLOC); + fu_progress_set_steps(child, 2); + fu_progress_set_status(child, FWUPD_STATUS_DEVICE_BUSY); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_BUSY); + + /* start child */ + fu_progress_step_done(child); + + /* verify 10% */ + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 10); + + /* finish child */ + fu_progress_step_done(child); + + /* ensure the parent is switched back to the status before the child took over */ + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_ERASE); + + fu_progress_step_done(progress); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_WRITE); + + /* verify 20% */ + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 20); + + /* child step should increment according to the custom steps */ + child = fu_progress_get_child(progress); + fu_progress_set_id(child, G_STRLOC); + fu_progress_set_id(child, G_STRLOC); + fu_progress_add_step(child, FWUPD_STATUS_DEVICE_RESTART, 25, NULL); + fu_progress_add_step(child, FWUPD_STATUS_DEVICE_WRITE, 75, NULL); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_RESTART); + + /* start child */ + fu_progress_step_done(child); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_WRITE); + + /* verify bilinear interpolation is working */ + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 35); + + /* + * 0 20 80 100 + * |---------||----------------------------||---------| + * | 35 | + * |-------||-------------------| (25%) + * | 75.5 | + * |---------------||--| (90%) + */ + grandchild = fu_progress_get_child(child); + fu_progress_set_id(grandchild, G_STRLOC); + fu_progress_add_step(grandchild, FWUPD_STATUS_DEVICE_ERASE, 90, NULL); + fu_progress_add_step(grandchild, FWUPD_STATUS_DEVICE_WRITE, 10, NULL); + + fu_progress_step_done(grandchild); + + /* verify bilinear interpolation (twice) is working for subpercentage */ + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 75); + + fu_progress_step_done(grandchild); + + /* finish child */ + fu_progress_step_done(child); + + fu_progress_step_done(progress); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_DEVICE_READ); + + /* verify 80% */ + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 80); + + fu_progress_step_done(progress); + + /* verify 100% */ + g_assert_cmpint(fu_progress_get_percentage(progress), ==, 100); + g_assert_cmpint(fu_progress_get_status(progress), ==, FWUPD_STATUS_UNKNOWN); +} + +static void +fu_progress_finish_func(void) +{ + FuProgress *child; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* check straight finish */ + fu_progress_set_steps(progress, 3); + + child = fu_progress_get_child(progress); + fu_progress_set_id(child, G_STRLOC); + fu_progress_set_steps(child, 3); + fu_progress_finished(child); + + /* parent step done after child finish */ + fu_progress_step_done(progress); +} + +static void +fu_progress_child_finished(void) +{ + FuProgress *child; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* check straight finish */ + fu_progress_set_steps(progress, 3); + + child = fu_progress_get_child(progress); + fu_progress_set_id(child, G_STRLOC); + fu_progress_set_steps(child, 3); + /* some imaginary igorable error */ + + /* parent step done after child finish */ + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_CHILD_FINISHED); + fu_progress_step_done(progress); +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + + g_test_init(&argc, &argv, NULL); + g_type_ensure(FU_TYPE_IFD_BIOS); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_DATADIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_LIBDIR_PKG", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSCONFDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_OFFLINE_TRIGGER", "/tmp/fwupd-self-test/system-update", TRUE); + (void)g_setenv("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); + (void)g_setenv("FWUPD_PROFILE", "1", TRUE); + + g_test_add_func("/fwupd/common{strnsplit}", fu_strsplit_func); + g_test_add_func("/fwupd/common{memmem}", fu_common_memmem_func); + g_test_add_func("/fwupd/progress", fu_progress_func); + g_test_add_func("/fwupd/progress{child}", fu_progress_child_func); + g_test_add_func("/fwupd/progress{child-finished}", fu_progress_child_finished); + g_test_add_func("/fwupd/progress{parent-1-step}", fu_progress_parent_one_step_proxy_func); + g_test_add_func("/fwupd/progress{no-equal}", fu_progress_non_equal_steps_func); + g_test_add_func("/fwupd/progress{finish}", fu_progress_finish_func); + g_test_add_func("/fwupd/bios-attrs{load}", fu_bios_settings_load_func); + g_test_add_func("/fwupd/security-attrs{hsi}", fu_security_attrs_hsi_func); + g_test_add_func("/fwupd/plugin{config}", fu_plugin_config_func); + g_test_add_func("/fwupd/plugin{devices}", fu_plugin_devices_func); + g_test_add_func("/fwupd/plugin{device-inhibit-children}", + fu_plugin_device_inhibit_children_func); + g_test_add_func("/fwupd/plugin{delay}", fu_plugin_delay_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/backend", fu_backend_func); + g_test_add_func("/fwupd/chunk", fu_chunk_func); + g_test_add_func("/fwupd/common{align-up}", fu_common_align_up_func); + g_test_add_func("/fwupd/volume{gpt-type}", fu_volume_gpt_type_func); + g_test_add_func("/fwupd/common{byte-array}", fu_common_byte_array_func); + g_test_add_func("/fwupd/common{crc}", fu_common_crc_func); + g_test_add_func("/fwupd/common{string-append-kv}", fu_string_append_func); + g_test_add_func("/fwupd/common{version-guess-format}", fu_version_guess_format_func); + g_test_add_func("/fwupd/common{strtoull}", fu_strtoull_func); + g_test_add_func("/fwupd/common{version}", fu_common_version_func); + g_test_add_func("/fwupd/common{version-semver}", fu_version_semver_func); + g_test_add_func("/fwupd/common{vercmp}", fu_common_vercmp_func); + g_test_add_func("/fwupd/common{strstrip}", fu_strstrip_func); + g_test_add_func("/fwupd/common{endian}", fu_common_endian_func); + g_test_add_func("/fwupd/common{cabinet}", fu_common_cabinet_func); + g_test_add_func("/fwupd/common{bytes-get-data}", fu_common_bytes_get_data_func); + g_test_add_func("/fwupd/common{kernel-lockdown}", fu_common_kernel_lockdown_func); + g_test_add_func("/fwupd/common{strsafe}", fu_strsafe_func); + g_test_add_func("/fwupd/efivar", fu_efivar_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); + g_test_add_func("/fwupd/smbios{dt}", fu_smbios_dt_func); + g_test_add_func("/fwupd/smbios{dt-fallback}", fu_smbios_dt_fallback_func); + g_test_add_func("/fwupd/smbios{class}", fu_smbios_class_func); + g_test_add_func("/fwupd/firmware", fu_firmware_func); + g_test_add_func("/fwupd/firmware{common}", fu_firmware_common_func); + g_test_add_func("/fwupd/firmware{linear}", fu_firmware_linear_func); + g_test_add_func("/fwupd/firmware{dedupe}", fu_firmware_dedupe_func); + g_test_add_func("/fwupd/firmware{build}", fu_firmware_build_func); + g_test_add_func("/fwupd/firmware{raw-aligned}", fu_firmware_raw_aligned_func); + g_test_add_func("/fwupd/firmware{ihex}", fu_firmware_ihex_func); + g_test_add_func("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func); + g_test_add_func("/fwupd/firmware{ihex-signed}", fu_firmware_ihex_signed_func); + g_test_add_func("/fwupd/firmware{srec-tokenization}", fu_firmware_srec_tokenization_func); + g_test_add_func("/fwupd/firmware{srec}", fu_firmware_srec_func); + g_test_add_func("/fwupd/firmware{fdt}", fu_firmware_fdt_func); + g_test_add_func("/fwupd/firmware{fit}", fu_firmware_fit_func); + g_test_add_func("/fwupd/firmware{ifwi-cpd}", fu_firmware_ifwi_cpd_func); + g_test_add_func("/fwupd/firmware{ifwi-fpt}", fu_firmware_ifwi_fpt_func); + g_test_add_func("/fwupd/firmware{oprom}", fu_firmware_oprom_func); + g_test_add_func("/fwupd/firmware{dfu}", fu_firmware_dfu_func); + g_test_add_func("/fwupd/firmware{dfu-patch}", fu_firmware_dfu_patch_func); + g_test_add_func("/fwupd/firmware{dfuse}", fu_firmware_dfuse_func); + g_test_add_func("/fwupd/firmware{builder-round-trip}", fu_firmware_builder_round_trip_func); + g_test_add_func("/fwupd/firmware{fmap}", fu_firmware_fmap_func); + g_test_add_func("/fwupd/firmware{gtypes}", fu_firmware_new_from_gtypes_func); + g_test_add_func("/fwupd/archive{invalid}", fu_archive_invalid_func); + g_test_add_func("/fwupd/archive{cab}", fu_archive_cab_func); + g_test_add_func("/fwupd/device", fu_device_func); + g_test_add_func("/fwupd/device{instance-ids}", fu_device_instance_ids_func); + g_test_add_func("/fwupd/device{composite-id}", fu_device_composite_id_func); + g_test_add_func("/fwupd/device{flags}", fu_device_flags_func); + g_test_add_func("/fwupd/device{custom-flags}", fu_device_private_flags_func); + g_test_add_func("/fwupd/device{inhibit}", fu_device_inhibit_func); + g_test_add_func("/fwupd/device{inhibit-updateable}", fu_device_inhibit_updateable_func); + g_test_add_func("/fwupd/device{parent}", fu_device_parent_func); + g_test_add_func("/fwupd/device{children}", fu_device_children_func); + g_test_add_func("/fwupd/device{incorporate}", fu_device_incorporate_func); + if (g_test_slow()) + 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{name}", fu_device_name_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{retry-success}", fu_device_retry_success_func); + g_test_add_func("/fwupd/device{retry-failed}", fu_device_retry_failed_func); + g_test_add_func("/fwupd/device{retry-hardware}", fu_device_retry_hardware_func); + g_test_add_func("/fwupd/device{cfi-device}", fu_device_cfi_device_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-smbios-private.h b/fwupd-1.8.6/libfwupdplugin/fu-smbios-private.h new file mode 100644 index 0000000000000000000000000000000000000000..8e5cb2488308865fde018a67d81300440f8fa0c4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-smbios-private.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-smbios.h" + +gboolean +fu_smbios_setup(FuSmbios *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_smbios_setup_from_path(FuSmbios *self, + const gchar *path, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_smbios_setup_from_file(FuSmbios *self, + const gchar *filename, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_smbios_setup_from_kernel(FuSmbios *self, + const gchar *path, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-smbios.c b/fwupd-1.8.6/libfwupdplugin/fu-smbios.c new file mode 100644 index 0000000000000000000000000000000000000000..f387aaeb3ed9dc4a0e32921285d8850880da24df --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-smbios.c @@ -0,0 +1,1121 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuSmbios" + +#include "config.h" + +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include "fwupd-error.h" + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-kenv.h" +#include "fu-mem.h" +#include "fu-path.h" +#include "fu-smbios-private.h" +#include "fu-string.h" + +/** + * FuSmbios: + * + * Enumerate the SMBIOS data on the system, either using DMI or Device Tree. + * + * See also: [class@FuHwids] + */ + +struct _FuSmbios { + FuFirmware parent_instance; + guint32 structure_table_len; + GPtrArray *items; +}; + +/* little endian */ +typedef struct __attribute__((packed)) { + gchar anchor_str[4]; + guint8 entry_point_csum; + guint8 entry_point_len; + guint8 smbios_major_ver; + guint8 smbios_minor_ver; + guint16 max_structure_sz; + guint8 entry_point_rev; + guint8 formatted_area[5]; + gchar intermediate_anchor_str[5]; + guint8 intermediate_csum; + guint16 structure_table_len; + guint32 structure_table_addr; + guint16 number_smbios_structs; + guint8 smbios_bcd_rev; +} FuSmbiosStructureEntryPoint32; + +/* little endian */ +typedef struct __attribute__((packed)) { + gchar anchor_str[5]; + guint8 entry_point_csum; + guint8 entry_point_len; + guint8 smbios_major_ver; + guint8 smbios_minor_ver; + guint8 smbios_docrev; + guint8 entry_point_rev; + guint8 reserved0; + guint32 structure_table_len; + guint64 structure_table_addr; +} FuSmbiosStructureEntryPoint64; + +typedef struct { + guint8 type; + guint16 handle; + GByteArray *buf; + GPtrArray *strings; +} FuSmbiosItem; + +G_DEFINE_TYPE(FuSmbios, fu_smbios, FU_TYPE_FIRMWARE) + +static void +fu_smbios_set_integer(FuSmbios *self, guint8 type, guint8 offset, guint8 value) +{ + FuSmbiosItem *item = g_ptr_array_index(self->items, type); + for (guint i = item->buf->len; i < (guint)offset + 1; i++) + fu_byte_array_append_uint8(item->buf, 0x0); + item->buf->data[offset] = value; +} + +static void +fu_smbios_set_string(FuSmbios *self, guint8 type, guint8 offset, const gchar *buf, gssize bufsz) +{ + FuSmbiosItem *item = g_ptr_array_index(self->items, type); + + /* NUL terminated UTF-8 */ + if (bufsz < 0) + bufsz = strlen(buf); + + /* add value to string table */ + g_ptr_array_add(item->strings, g_strndup(buf, (gsize)bufsz)); + fu_smbios_set_integer(self, type, offset, item->strings->len); +} + +static gboolean +fu_smbios_convert_dt_string(FuSmbios *self, + guint8 type, + guint8 offset, + const gchar *path, + const gchar *subpath) +{ + gsize bufsz = 0; + g_autofree gchar *fn = g_build_filename(path, subpath, NULL); + g_autofree gchar *buf = NULL; + + /* not found */ + if (!g_file_get_contents(fn, &buf, &bufsz, NULL)) + return FALSE; + if (bufsz == 0) + return FALSE; + fu_smbios_set_string(self, type, offset, buf, (gssize)bufsz); + return TRUE; +} + +static gchar ** +fu_smbios_convert_dt_string_array(FuSmbios *self, const gchar *path, const gchar *subpath) +{ + gsize bufsz = 0; + g_autofree gchar *fn = g_build_filename(path, subpath, NULL); + g_autofree gchar *buf = NULL; + g_auto(GStrv) split = NULL; + + /* not found */ + if (!g_file_get_contents(fn, &buf, &bufsz, NULL)) + return NULL; + if (bufsz == 0) + return NULL; + + /* return only if valid */ + split = g_strsplit(buf, ",", -1); + if (g_strv_length(split) == 0) + return NULL; + + /* success */ + return g_steal_pointer(&split); +} + +#ifdef HAVE_KENV_H + +static gboolean +fu_smbios_convert_kenv_string(FuSmbios *self, + guint8 type, + guint8 offset, + const gchar *sminfo, + GError **error) +{ + g_autofree gchar *value = fu_kenv_get_string(sminfo, error); + if (value == NULL) + return FALSE; + fu_smbios_set_string(self, type, offset, value, -1); + return TRUE; +} + +static gboolean +fu_smbios_setup_from_kenv(FuSmbios *self, GError **error) +{ + gboolean is_valid = FALSE; + g_autoptr(GError) error_local = NULL; + + /* add all four faked structures */ + for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { + FuSmbiosItem *item = g_new0(FuSmbiosItem, 1); + item->type = i; + item->buf = g_byte_array_new(); + item->strings = g_ptr_array_new_with_free_func(g_free); + g_ptr_array_add(self->items, item); + } + + /* DMI:Manufacturer */ + if (!fu_smbios_convert_kenv_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x04, + "smbios.bios.vendor", + &error_local)) { + g_debug("ignoring: %s", error_local->message); + g_clear_error(&error_local); + } else { + is_valid = TRUE; + } + + /* DMI:BiosVersion */ + if (!fu_smbios_convert_kenv_string(self, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x05, + "smbios.bios.version", + &error_local)) { + g_debug("ignoring: %s", error_local->message); + g_clear_error(&error_local); + } else { + is_valid = TRUE; + } + + /* DMI:Family */ + if (!fu_smbios_convert_kenv_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x1a, + "smbios.system.family", + &error_local)) { + g_debug("ignoring: %s", error_local->message); + g_clear_error(&error_local); + } else { + is_valid = TRUE; + } + + /* DMI:ProductName */ + if (!fu_smbios_convert_kenv_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x05, + "smbios.planar.product", + &error_local)) { + g_debug("ignoring: %s", error_local->message); + g_clear_error(&error_local); + } else { + is_valid = TRUE; + } + + /* DMI:BaseboardManufacturer */ + if (!fu_smbios_convert_kenv_string(self, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x04, + "smbios.planar.maker", + &error_local)) { + g_debug("ignoring: %s", error_local->message); + g_clear_error(&error_local); + } else { + is_valid = TRUE; + } + + /* we got no data */ + if (!is_valid) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_READ, "no SMBIOS information provided"); + return FALSE; + } + + /* success */ + return TRUE; +} +#endif + +static gboolean +fu_smbios_setup_from_path_dt(FuSmbios *self, const gchar *path, GError **error) +{ + gboolean has_family; + gboolean has_model; + gboolean has_vendor; + g_autofree gchar *fn_battery = NULL; + + /* add all four faked structures */ + for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { + FuSmbiosItem *item = g_new0(FuSmbiosItem, 1); + item->type = i; + item->buf = g_byte_array_new(); + item->strings = g_ptr_array_new_with_free_func(g_free); + g_ptr_array_add(self->items, item); + } + + /* if it has a battery it is portable (probably a laptop) */ + fn_battery = g_build_filename(path, "battery", NULL); + if (g_file_test(fn_battery, G_FILE_TEST_EXISTS)) { + fu_smbios_set_integer(self, + FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, + 0x05, + FU_SMBIOS_CHASSIS_KIND_PORTABLE); + } + + /* DMI:Manufacturer */ + has_vendor = fu_smbios_convert_dt_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x04, + path, + "vendor"); + + /* DMI:Family */ + has_family = fu_smbios_convert_dt_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x1a, + path, + "model-name"); + + /* DMI:ProductName */ + has_model = + fu_smbios_convert_dt_string(self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x05, path, "model"); + + /* fall back to the first compatible string if required */ + if (!has_vendor || !has_model || !has_family) { + g_auto(GStrv) parts = NULL; + + /* NULL if invalid, otherwise we're sure this has size of exactly 3 */ + parts = fu_smbios_convert_dt_string_array(self, path, "compatible"); + if (parts != NULL) { + if (!has_vendor && g_strv_length(parts) > 0) { + fu_smbios_set_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x4, + parts[0], + -1); + } + if (!has_model && g_strv_length(parts) > 1) { + fu_smbios_set_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x05, + parts[1], + -1); + } + if (!has_family && g_strv_length(parts) > 2) { + fu_smbios_set_string(self, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x1a, + parts[2], + -1); + } + } + } + + /* DMI:BiosVersion */ + fu_smbios_convert_dt_string(self, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x05, + path, + "ibm,firmware-versions/version"); + + /* DMI:BaseboardManufacturer */ + fu_smbios_convert_dt_string(self, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x04, + path, + "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor"); + + /* DMI:BaseboardProduct */ + fu_smbios_convert_dt_string( + self, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x05, + path, + "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number"); + + return TRUE; +} + +static gboolean +fu_smbios_setup_from_data(FuSmbios *self, const guint8 *buf, gsize sz, GError **error) +{ + /* go through each structure */ + for (gsize i = 0; i < sz; i++) { + FuSmbiosItem *item; + guint16 str_handle = 0; + guint8 str_len = 0; + guint8 str_type = 0; + + /* le */ + if (!fu_memread_uint8_safe(buf, sz, i + 0x0, &str_type, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, sz, i + 0x1, &str_len, error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, sz, i + 0x2, &str_handle, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* invalid */ + if (str_len == 0x00) + break; + if (i + str_len >= sz) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "structure larger than available data"); + return FALSE; + } + + /* create a new result */ + item = g_new0(FuSmbiosItem, 1); + item->type = str_type; + item->handle = GUINT16_FROM_LE(str_handle); + item->buf = g_byte_array_sized_new(str_len); + item->strings = g_ptr_array_new_with_free_func(g_free); + g_byte_array_append(item->buf, buf + i, str_len); + g_ptr_array_add(self->items, item); + + /* jump to the end of the struct */ + i += str_len; + if (buf[i] == '\0' && buf[i + 1] == '\0') { + i++; + continue; + } + + /* add strings from table */ + for (gsize start_offset = i; i < sz; i++) { + if (buf[i] == '\0') { + if (start_offset == i) + break; + g_ptr_array_add(item->strings, + g_strdup((const gchar *)&buf[start_offset])); + start_offset = i + 1; + } + } + } + return TRUE; +} + +/** + * fu_smbios_setup_from_file: + * @self: a #FuSmbios + * @filename: a filename + * @error: (nullable): optional return location for an error + * + * Reads all the SMBIOS values from a DMI blob. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fu_smbios_setup_from_file(FuSmbios *self, const gchar *filename, GError **error) +{ + gsize sz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *basename = NULL; + + g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); + g_return_val_if_fail(filename != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* use a heuristic */ + basename = g_path_get_basename(filename); + if (g_strcmp0(basename, "base") == 0) + return fu_smbios_setup_from_path_dt(self, filename, error); + + /* DMI blob */ + if (!g_file_get_contents(filename, &buf, &sz, error)) + return FALSE; + return fu_smbios_setup_from_data(self, (guint8 *)buf, sz, error); +} + +static gboolean +fu_smbios_encode_string_from_kernel(FuSmbios *self, + const gchar *file_contents, + guint8 type, + guint8 offset, + GError **error) +{ + fu_smbios_set_string(self, type, offset, file_contents, -1); + return TRUE; +} + +static gboolean +fu_smbios_encode_byte_from_kernel(FuSmbios *self, + const gchar *file_contents, + guint8 type, + guint8 offset, + GError **error) +{ + gchar *endp; + gint64 value = g_ascii_strtoll(file_contents, &endp, 10); + + if (*endp != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "non-numeric values in numeric string: %s", + endp); + return FALSE; + } + if (value < 0 || value > G_MAXUINT8) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "value \"%s\" is not representable in a byte", + file_contents); + return FALSE; + } + + fu_smbios_set_integer(self, type, offset, value); + return TRUE; +} + +/* + * The mapping from SMBIOS field to sysfs name can be found by mapping + * the field to a kernel property name in dmi_decode() + * (drivers/firmware/dmi_scan.c), then the property name to sysfs entry + * in dmi_id_init_attr_table() (drivers/firmware/dmi-id.c). This table + * lists each attribute exposed in /sys/class/dmi when CONFIG_DMIID is + * enabled, mapping to the SMBIOS field and a function that can convert + * the textual version of the field back into the raw SMBIOS table + * representation. + */ +#define SYSFS_DMI_FIELD(_name, _type, offset_ignored, kind) \ + { \ + .name = _name, .type = _type, .offset = offset_ignored, \ + .encode = fu_smbios_encode_##kind##_from_kernel \ + } +const struct kernel_dmi_field { + const gchar *name; + gboolean (*encode)(FuSmbios *, const gchar *, guint8, guint8, GError **); + guint8 type; + guint8 offset; +} KERNEL_DMI_FIELDS[] = { + SYSFS_DMI_FIELD("bios_vendor", 0, 4, string), + SYSFS_DMI_FIELD("bios_version", 0, 5, string), + SYSFS_DMI_FIELD("bios_date", 0, 8, string), + SYSFS_DMI_FIELD("sys_vendor", 1, 4, string), + SYSFS_DMI_FIELD("product_name", 1, 5, string), + SYSFS_DMI_FIELD("product_version", 1, 6, string), + SYSFS_DMI_FIELD("product_serial", 1, 7, string), + /* SYSFS_DMI_FIELD("product_uuid", 1, 8, uuid) */ + SYSFS_DMI_FIELD("product_family", 1, 26, string), + SYSFS_DMI_FIELD("product_sku", 1, 25, string), + SYSFS_DMI_FIELD("board_vendor", 2, 4, string), + SYSFS_DMI_FIELD("board_name", 2, 5, string), + SYSFS_DMI_FIELD("board_version", 2, 6, string), + SYSFS_DMI_FIELD("board_serial", 2, 7, string), + SYSFS_DMI_FIELD("board_asset_tag", 2, 8, string), + SYSFS_DMI_FIELD("chassis_vendor", 3, 4, string), + SYSFS_DMI_FIELD("chassis_type", 3, 5, byte), + SYSFS_DMI_FIELD("chassis_version", 3, 6, string), + SYSFS_DMI_FIELD("chassis_serial", 3, 7, string), + SYSFS_DMI_FIELD("chassis_asset_tag", 3, 8, string), +}; + +/** + * fu_smbios_setup_from_kernel: + * @self: a #FuSmbios + * @path: a directory path + * @error: (nullable): optional return location for an error + * + * Reads SMBIOS value from DMI values provided by the kernel, such as in + * /sys/class/dmi on Linux. + * + * Returns: %TRUE for success + * + * Since: 1.6.2 + **/ +gboolean +fu_smbios_setup_from_kernel(FuSmbios *self, const gchar *path, GError **error) +{ + gboolean any_success = FALSE; + + /* add fake structures */ + for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { + FuSmbiosItem *item = g_new0(FuSmbiosItem, 1); + item->type = i; + item->buf = g_byte_array_new(); + item->strings = g_ptr_array_new_with_free_func(g_free); + g_ptr_array_add(self->items, item); + } + + /* parse every known field from the corresponding file */ + for (gsize i = 0; i < G_N_ELEMENTS(KERNEL_DMI_FIELDS); i++) { + const struct kernel_dmi_field *field = &KERNEL_DMI_FIELDS[i]; + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn = g_build_filename(path, field->name, NULL); + g_autoptr(GError) local_error = NULL; + + if (!g_file_get_contents(fn, &buf, &bufsz, &local_error)) { + g_debug("unable to read SMBIOS data from %s: %s", fn, local_error->message); + continue; + } + + /* trim trailing newline added by kernel */ + if (buf[bufsz - 1] == '\n') + buf[bufsz - 1] = 0; + + if (!field->encode(self, buf, field->type, field->offset, &local_error)) { + g_warning("failed to parse SMBIOS data from %s: %s", + fn, + local_error->message); + continue; + } + + any_success = TRUE; + } + if (!any_success) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read any SMBIOS values from %s", + path); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_smbios_parse_ep32(FuSmbios *self, const gchar *buf, gsize sz, GError **error) +{ + FuSmbiosStructureEntryPoint32 *ep; + guint8 csum = 0; + g_autofree gchar *version_str = NULL; + + /* verify size */ + if (sz != sizeof(FuSmbiosStructureEntryPoint32)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid smbios entry point got %" G_GSIZE_FORMAT + " bytes, expected %" G_GSIZE_FORMAT, + sz, + sizeof(FuSmbiosStructureEntryPoint32)); + return FALSE; + } + + /* verify checksum */ + for (guint i = 0; i < sz; i++) + csum += buf[i]; + if (csum != 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "entry point checksum invalid"); + return FALSE; + } + + /* verify intermediate section */ + ep = (FuSmbiosStructureEntryPoint32 *)buf; + if (memcmp(ep->intermediate_anchor_str, "_DMI_", 5) != 0) { + g_autofree gchar *tmp = g_strndup(ep->intermediate_anchor_str, 5); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "intermediate anchor signature invalid, got %s", + tmp); + return FALSE; + } + for (guint i = 10; i < sz; i++) + csum += buf[i]; + if (csum != 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "intermediate checksum invalid"); + return FALSE; + } + self->structure_table_len = GUINT16_FROM_LE(ep->structure_table_len); + version_str = g_strdup_printf("%u.%u", ep->smbios_major_ver, ep->smbios_minor_ver); + fu_firmware_set_version(FU_FIRMWARE(self), version_str); + fu_firmware_set_version_raw(FU_FIRMWARE(self), + (((guint16)ep->smbios_major_ver) << 8) + ep->smbios_minor_ver); + return TRUE; +} + +static gboolean +fu_smbios_parse_ep64(FuSmbios *self, const gchar *buf, gsize sz, GError **error) +{ + FuSmbiosStructureEntryPoint64 *ep; + guint8 csum = 0; + g_autofree gchar *version_str = NULL; + + /* verify size */ + if (sz != sizeof(FuSmbiosStructureEntryPoint64)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid smbios3 entry point got %" G_GSIZE_FORMAT + " bytes, expected %" G_GSIZE_FORMAT, + sz, + sizeof(FuSmbiosStructureEntryPoint32)); + return FALSE; + } + + /* verify checksum */ + for (guint i = 0; i < sz; i++) + csum += buf[i]; + if (csum != 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "entry point checksum invalid"); + return FALSE; + } + ep = (FuSmbiosStructureEntryPoint64 *)buf; + self->structure_table_len = GUINT32_FROM_LE(ep->structure_table_len); + version_str = g_strdup_printf("%u.%u", ep->smbios_major_ver, ep->smbios_minor_ver); + fu_firmware_set_version(FU_FIRMWARE(self), version_str); + return TRUE; +} + +static gboolean +fu_smbios_setup_from_path_dmi(FuSmbios *self, const gchar *path, GError **error) +{ + gsize sz = 0; + g_autofree gchar *dmi_fn = NULL; + g_autofree gchar *dmi_raw = NULL; + g_autofree gchar *ep_fn = NULL; + g_autofree gchar *ep_raw = NULL; + + g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); + + /* get the smbios entry point */ + ep_fn = g_build_filename(path, "smbios_entry_point", NULL); + if (!g_file_get_contents(ep_fn, &ep_raw, &sz, error)) + return FALSE; + + /* check we got enough data to read the signature */ + if (sz < 5) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid smbios entry point got %" G_GSIZE_FORMAT + " bytes, expected %" G_GSIZE_FORMAT " or %" G_GSIZE_FORMAT, + sz, + sizeof(FuSmbiosStructureEntryPoint32), + sizeof(FuSmbiosStructureEntryPoint64)); + return FALSE; + } + + /* parse 32 bit structure */ + if (memcmp(ep_raw, "_SM_", 4) == 0) { + if (!fu_smbios_parse_ep32(self, ep_raw, sz, error)) + return FALSE; + } else if (memcmp(ep_raw, "_SM3_", 5) == 0) { + if (!fu_smbios_parse_ep64(self, ep_raw, sz, error)) + return FALSE; + } else { + g_autofree gchar *tmp = g_strndup(ep_raw, 4); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "SMBIOS signature invalid, got %s", + tmp); + return FALSE; + } + + /* get the DMI data */ + dmi_fn = g_build_filename(path, "DMI", NULL); + if (!g_file_get_contents(dmi_fn, &dmi_raw, &sz, error)) + return FALSE; + if (sz != self->structure_table_len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid DMI data size, got %" G_GSIZE_FORMAT + " bytes, expected %" G_GUINT32_FORMAT, + sz, + self->structure_table_len); + return FALSE; + } + + /* parse blob */ + return fu_smbios_setup_from_data(self, (guint8 *)dmi_raw, sz, error); +} + +static gboolean +fu_smbios_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSmbios *self = FU_SMBIOS(firmware); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + return fu_smbios_setup_from_data(self, buf, bufsz, error); +} + +/** + * fu_smbios_setup_from_path: + * @self: a #FuSmbios + * @path: a path, e.g. `/sys/firmware/dmi/tables` + * @error: (nullable): optional return location for an error + * + * Reads all the SMBIOS values from a specific path. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fu_smbios_setup_from_path(FuSmbios *self, const gchar *path, GError **error) +{ + g_autofree gchar *basename = NULL; + + g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* use a heuristic */ + basename = g_path_get_basename(path); + if (g_strcmp0(basename, "base") == 0) + return fu_smbios_setup_from_path_dt(self, path, error); + return fu_smbios_setup_from_path_dmi(self, path, error); +} + +#ifdef _WIN32 +#define FU_SMBIOS_FT_SIG_ACPI 0x41435049 +#define FU_SMBIOS_FT_SIG_FIRM 0x4649524D +#define FU_SMBIOS_FT_SIG_RSMB 0x52534D42 +#define FU_SMBIOS_FT_RAW_OFFSET 0x08 +#endif + +/** + * fu_smbios_setup: + * @self: a #FuSmbios + * @error: (nullable): optional return location for an error + * + * Reads all the SMBIOS values from the hardware. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fu_smbios_setup(FuSmbios *self, GError **error) +{ +#ifdef _WIN32 + gsize bufsz; + guint rc; + g_autofree guint8 *buf = NULL; + + rc = GetSystemFirmwareTable(FU_SMBIOS_FT_SIG_RSMB, 0, 0, 0); + if (rc <= 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to access RSMB [%u]", + (guint)GetLastError()); + return FALSE; + } + if (rc < FU_SMBIOS_FT_RAW_OFFSET || rc > 0x1000000) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "RSMB impossible size"); + return FALSE; + } + bufsz = rc; + buf = g_malloc0(bufsz); + rc = GetSystemFirmwareTable(FU_SMBIOS_FT_SIG_RSMB, 0, buf, (DWORD)bufsz); + if (rc <= 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to read RSMB [%u]", + (guint)GetLastError()); + return FALSE; + } + return fu_smbios_setup_from_data(self, + buf + FU_SMBIOS_FT_RAW_OFFSET, + bufsz - FU_SMBIOS_FT_RAW_OFFSET, + error); +#else + g_autofree gchar *path = NULL; + g_autofree gchar *path_dt = NULL; + g_autofree gchar *sysfsfwdir = NULL; + const gchar *path_dmi_class = "/sys/class/dmi/id"; + + g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + + /* DMI */ + path = g_build_filename(sysfsfwdir, "dmi", "tables", NULL); + if (g_file_test(path, G_FILE_TEST_EXISTS)) { + g_autoptr(GError) error_local = NULL; + if (!fu_smbios_setup_from_path(self, path, &error_local)) { + if (!g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_ACCES)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + g_debug("ignoring %s", error_local->message); + } else + return TRUE; + } + + /* the values the kernel parsed; these are world-readable */ + if (g_file_test(path_dmi_class, G_FILE_TEST_IS_DIR)) { + g_debug("trying to read %s", path_dmi_class); + return fu_smbios_setup_from_kernel(self, path_dmi_class, error); + } + + /* DT */ + path_dt = g_build_filename(sysfsfwdir, "devicetree", "base", NULL); + if (g_file_test(path_dt, G_FILE_TEST_EXISTS)) + return fu_smbios_setup_from_path(self, path_dt, error); + +#ifdef HAVE_KENV_H + /* kenv */ + return fu_smbios_setup_from_kenv(self, error); +#endif + + /* neither found */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "neither SMBIOS or DT found"); + return FALSE; +#endif +} + +static void +fu_smbios_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuSmbios *self = FU_SMBIOS(firmware); + + for (guint i = 0; i < self->items->len; i++) { + FuSmbiosItem *item = g_ptr_array_index(self->items, i); + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "item", NULL); + fu_xmlb_builder_insert_kx(bc, "type", item->type); + fu_xmlb_builder_insert_kx(bc, "length", item->buf->len); + fu_xmlb_builder_insert_kx(bc, "handle", item->handle); + for (guint j = 0; j < item->strings->len; j++) { + const gchar *tmp = g_ptr_array_index(item->strings, j); + g_autofree gchar *title = g_strdup_printf("%02u", j); + g_autofree gchar *value = fu_strsafe(tmp, 20); + xb_builder_node_insert_text(bc, "string", value, "idx", title, NULL); + } + } +} + +static FuSmbiosItem * +fu_smbios_get_item_for_type(FuSmbios *self, guint8 type) +{ + for (guint i = 0; i < self->items->len; i++) { + FuSmbiosItem *item = g_ptr_array_index(self->items, i); + if (item->type == type) + return item; + } + return NULL; +} + +/** + * fu_smbios_get_data: + * @self: a #FuSmbios + * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @error: (nullable): optional return location for an error + * + * Reads a SMBIOS data blob, which includes the SMBIOS section header. + * + * Returns: (transfer full): a #GBytes, or %NULL if invalid or not found + * + * Since: 1.0.0 + **/ +GBytes * +fu_smbios_get_data(FuSmbios *self, guint8 type, GError **error) +{ + FuSmbiosItem *item; + + g_return_val_if_fail(FU_IS_SMBIOS(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + item = fu_smbios_get_item_for_type(self, type); + if (item == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no structure with type %02x", + type); + return NULL; + } + return g_bytes_new(item->buf->data, item->buf->len); +} + +/** + * fu_smbios_get_integer: + * @self: a #FuSmbios + * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @offset: a structure offset + * @error: (nullable): optional return location for an error + * + * Reads an integer value from the SMBIOS string table of a specific structure. + * + * The @type and @offset can be referenced from the DMTF SMBIOS specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf + * + * Returns: an integer, or %G_MAXUINT if invalid or not found + * + * Since: 1.5.0 + **/ +guint +fu_smbios_get_integer(FuSmbios *self, guint8 type, guint8 offset, GError **error) +{ + FuSmbiosItem *item; + + g_return_val_if_fail(FU_IS_SMBIOS(self), 0); + g_return_val_if_fail(error == NULL || *error == NULL, 0); + + /* get item */ + item = fu_smbios_get_item_for_type(self, type); + if (item == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no structure with type %02x", + type); + return G_MAXUINT; + } + + /* check offset valid */ + if (offset >= item->buf->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "offset bigger than size %u", + item->buf->len); + return G_MAXUINT; + } + + /* success */ + return item->buf->data[offset]; +} + +/** + * fu_smbios_get_string: + * @self: a #FuSmbios + * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @offset: a structure offset + * @error: (nullable): optional return location for an error + * + * Reads a string from the SMBIOS string table of a specific structure. + * + * The @type and @offset can be referenced from the DMTF SMBIOS specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf + * + * Returns: a string, or %NULL if invalid or not found + * + * Since: 1.0.0 + **/ +const gchar * +fu_smbios_get_string(FuSmbios *self, guint8 type, guint8 offset, GError **error) +{ + FuSmbiosItem *item; + + g_return_val_if_fail(FU_IS_SMBIOS(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* get item */ + item = fu_smbios_get_item_for_type(self, type); + if (item == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no structure with type %02x", + type); + return NULL; + } + + /* check offset valid */ + if (offset >= item->buf->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "offset bigger than size %u", + item->buf->len); + return NULL; + } + if (item->buf->data[offset] == 0x00) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no data available"); + return NULL; + } + + /* check string index valid */ + if (item->buf->data[offset] > item->strings->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "index larger than string table %u", + item->strings->len); + return NULL; + } + return g_ptr_array_index(item->strings, item->buf->data[offset] - 1); +} + +static void +fu_smbios_item_free(FuSmbiosItem *item) +{ + g_byte_array_unref(item->buf); + g_ptr_array_unref(item->strings); + g_free(item); +} + +static void +fu_smbios_finalize(GObject *object) +{ + FuSmbios *self = FU_SMBIOS(object); + g_ptr_array_unref(self->items); + G_OBJECT_CLASS(fu_smbios_parent_class)->finalize(object); +} + +static void +fu_smbios_class_init(FuSmbiosClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_smbios_finalize; + klass_firmware->parse = fu_smbios_parse; + klass_firmware->export = fu_smbios_export; +} + +static void +fu_smbios_init(FuSmbios *self) +{ + self->items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_smbios_item_free); +} + +/** + * fu_smbios_new: + * + * Creates a new object to parse SMBIOS data. + * + * Returns: a #FuSmbios + * + * Since: 1.0.0 + **/ +FuSmbios * +fu_smbios_new(void) +{ + FuSmbios *self; + self = g_object_new(FU_TYPE_SMBIOS, NULL); + return FU_SMBIOS(self); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-smbios.h b/fwupd-1.8.6/libfwupdplugin/fu-smbios.h new file mode 100644 index 0000000000000000000000000000000000000000..6f59a13bf95eec5d8d373427e0e4a6ae70cc9269 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-smbios.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_SMBIOS (fu_smbios_get_type()) + +G_DECLARE_FINAL_TYPE(FuSmbios, fu_smbios, FU, SMBIOS, FuFirmware) + +FuSmbios * +fu_smbios_new(void); + +/** + * FU_SMBIOS_STRUCTURE_TYPE_BIOS: + * + * The SMBIOS structure type for the BIOS. + * + * Since: 1.5.5 + **/ +#define FU_SMBIOS_STRUCTURE_TYPE_BIOS 0x00 +/** + * FU_SMBIOS_STRUCTURE_TYPE_SYSTEM: + * + * The SMBIOS structure type for the system as a whole. + * + * Since: 1.5.5 + **/ +#define FU_SMBIOS_STRUCTURE_TYPE_SYSTEM 0x01 +/** + * FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD: + * + * The SMBIOS structure type for the baseboard (motherboard). + * + * Since: 1.5.5 + **/ +#define FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD 0x02 +/** + * FU_SMBIOS_STRUCTURE_TYPE_CHASSIS: + * + * The SMBIOS structure type for the chassis. + * + * Since: 1.5.5 + **/ +#define FU_SMBIOS_STRUCTURE_TYPE_CHASSIS 0x03 +/** + * FU_SMBIOS_STRUCTURE_TYPE_LAST: + * + * The last possible SMBIOS structure type. + * + * Since: 1.5.5 + **/ +#define FU_SMBIOS_STRUCTURE_TYPE_LAST 0x04 + +/** + * FuSmbiosChassisKind: + * + * The system chassis kind. + **/ +typedef enum { + FU_SMBIOS_CHASSIS_KIND_OTHER = 0x01, + FU_SMBIOS_CHASSIS_KIND_UNKNOWN = 0x02, + FU_SMBIOS_CHASSIS_KIND_DESKTOP = 0x03, + FU_SMBIOS_CHASSIS_KIND_LOW_PROFILE_DESKTOP = 0x04, + FU_SMBIOS_CHASSIS_KIND_PIZZA_BOX = 0x05, + FU_SMBIOS_CHASSIS_KIND_MINI_TOWER = 0x06, + FU_SMBIOS_CHASSIS_KIND_TOWER = 0x07, + FU_SMBIOS_CHASSIS_KIND_PORTABLE = 0x08, + FU_SMBIOS_CHASSIS_KIND_LAPTOP = 0x09, + FU_SMBIOS_CHASSIS_KIND_NOTEBOOK = 0x0A, + FU_SMBIOS_CHASSIS_KIND_HAND_HELD = 0x0B, + FU_SMBIOS_CHASSIS_KIND_DOCKING_STATION = 0x0C, + FU_SMBIOS_CHASSIS_KIND_ALL_IN_ONE = 0x0D, + FU_SMBIOS_CHASSIS_KIND_SUB_NOTEBOOK = 0x0E, + FU_SMBIOS_CHASSIS_KIND_SPACE_SAVING = 0x0F, + FU_SMBIOS_CHASSIS_KIND_LUNCH_BOX = 0x10, + FU_SMBIOS_CHASSIS_KIND_MAIN_SERVER = 0x11, + FU_SMBIOS_CHASSIS_KIND_EXPANSION = 0x12, + FU_SMBIOS_CHASSIS_KIND_SUBCHASSIS = 0x13, + FU_SMBIOS_CHASSIS_KIND_BUS_EXPANSION = 0x14, + FU_SMBIOS_CHASSIS_KIND_PERIPHERAL = 0x15, + FU_SMBIOS_CHASSIS_KIND_RAID = 0x16, + FU_SMBIOS_CHASSIS_KIND_RACK_MOUNT = 0x17, + FU_SMBIOS_CHASSIS_KIND_SEALED_CASE_PC = 0x18, + FU_SMBIOS_CHASSIS_KIND_MULTI_SYSTEM = 0x19, + FU_SMBIOS_CHASSIS_KIND_COMPACT_PCI = 0x1A, + FU_SMBIOS_CHASSIS_KIND_ADVANCED_TCA = 0x1B, + FU_SMBIOS_CHASSIS_KIND_BLADE = 0x1C, + FU_SMBIOS_CHASSIS_KIND_TABLET = 0x1E, + FU_SMBIOS_CHASSIS_KIND_CONVERTIBLE = 0x1F, + FU_SMBIOS_CHASSIS_KIND_DETACHABLE = 0x20, + FU_SMBIOS_CHASSIS_KIND_IOT_GATEWAY = 0x21, + FU_SMBIOS_CHASSIS_KIND_EMBEDDED_PC = 0x22, + FU_SMBIOS_CHASSIS_KIND_MINI_PC = 0x23, + FU_SMBIOS_CHASSIS_KIND_STICK_PC = 0x24, + /*< private >*/ + FU_SMBIOS_CHASSIS_KIND_LAST, +} FuSmbiosChassisKind; + +const gchar * +fu_smbios_get_string(FuSmbios *self, guint8 type, guint8 offset, GError **error); +guint +fu_smbios_get_integer(FuSmbios *self, guint8 type, guint8 offset, GError **error); +GBytes * +fu_smbios_get_data(FuSmbios *self, guint8 type, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-srec-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-srec-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..52ec8e390b0251e7d13d861c3b69e2df3ebb5d7a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-srec-firmware.c @@ -0,0 +1,652 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-firmware-common.h" +#include "fu-srec-firmware.h" +#include "fu-string.h" + +/** + * FuSrecFirmware: + * + * A SREC firmware image. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + GPtrArray *records; +} FuSrecFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuSrecFirmware, fu_srec_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_srec_firmware_get_instance_private(o)) + +#define FU_SREC_FIRMWARE_TOKENS_MAX 100000 /* lines */ + +/** + * fu_srec_firmware_get_records: + * @self: A #FuSrecFirmware + * + * Returns the raw records from SREC tokenization. + * + * This might be useful if the plugin is expecting the SREC file to be a list + * of operations, rather than a simple linear image with filled holes. + * + * Returns: (transfer none) (element-type FuSrecFirmwareRecord): records + * + * Since: 1.3.2 + **/ +GPtrArray * +fu_srec_firmware_get_records(FuSrecFirmware *self) +{ + FuSrecFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_SREC_FIRMWARE(self), NULL); + return priv->records; +} + +static void +fu_srec_firmware_record_free(FuSrecFirmwareRecord *rcd) +{ + g_byte_array_unref(rcd->buf); + g_free(rcd); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuSrecFirmwareRecord, fu_srec_firmware_record_free); +#pragma clang diagnostic pop + +/** + * fu_srec_firmware_record_new: (skip): + * @ln: unsigned integer + * @kind: a record kind, e.g. #FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32 + * @addr: unsigned integer + * + * Returns a single firmware record + * + * Returns: (transfer full): record + * + * Since: 1.3.2 + **/ +FuSrecFirmwareRecord * +fu_srec_firmware_record_new(guint ln, FuFirmareSrecRecordKind kind, guint32 addr) +{ + FuSrecFirmwareRecord *rcd = g_new0(FuSrecFirmwareRecord, 1); + rcd->ln = ln; + rcd->kind = kind; + rcd->addr = addr; + rcd->buf = g_byte_array_new(); + return rcd; +} + +static FuSrecFirmwareRecord * +fu_srec_firmware_record_dup(const FuSrecFirmwareRecord *rcd) +{ + FuSrecFirmwareRecord *dest; + g_return_val_if_fail(rcd != NULL, NULL); + dest = fu_srec_firmware_record_new(rcd->ln, rcd->kind, rcd->addr); + dest->buf = g_byte_array_ref(rcd->buf); + return dest; +} + +/** + * fu_srec_firmware_record_get_type: + * + * Gets a specific type. + * + * Return value: a #GType + * + * Since: 1.6.1 + **/ +GType +fu_srec_firmware_record_get_type(void) +{ + static GType type_id = 0; + if (!type_id) { + type_id = + g_boxed_type_register_static("FuSrecFirmwareRecord", + (GBoxedCopyFunc)fu_srec_firmware_record_dup, + (GBoxedFreeFunc)fu_srec_firmware_record_free); + } + return type_id; +} + +typedef struct { + FuSrecFirmware *self; + FwupdInstallFlags flags; + gboolean got_eof; +} FuSrecFirmwareTokenHelper; + +static gboolean +fu_srec_firmware_tokenize_cb(GString *token, guint token_idx, gpointer user_data, GError **error) +{ + FuSrecFirmwareTokenHelper *helper = (FuSrecFirmwareTokenHelper *)user_data; + FuSrecFirmwarePrivate *priv = GET_PRIVATE(helper->self); + g_autoptr(FuSrecFirmwareRecord) rcd = NULL; + gboolean require_data = FALSE; + guint32 rec_addr32; + guint16 rec_addr16; + guint8 addrsz = 0; /* bytes */ + guint8 rec_count; /* words */ + guint8 rec_kind; + + /* sanity check */ + if (token_idx > FU_SREC_FIRMWARE_TOKENS_MAX) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file has too many lines"); + return FALSE; + } + + /* remove WIN32 line endings */ + g_strdelimit(token->str, "\r\x1a", '\0'); + token->len = strlen(token->str); + + /* ignore blank lines */ + if (token->len == 0) + return TRUE; + + /* check starting token */ + if (token->str[0] != 'S' || token->len < 3) { + g_autofree gchar *strsafe = fu_strsafe(token->str, 3); + if (strsafe != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token, got '%s' at line %u", + strsafe, + token_idx + 1); + return FALSE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token at line %u", + token_idx + 1); + return FALSE; + } + + /* kind, count, address, (data), checksum, linefeed */ + rec_kind = token->str[1] - '0'; + if (!fu_firmware_strparse_uint8_safe(token->str, token->len, 2, &rec_count, error)) + return FALSE; + if (rec_count * 2 != token->len - 4) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "count incomplete at line %u, " + "length %u, expected %u", + token_idx + 1, + (guint)token->len - 4, + (guint)rec_count * 2); + return FALSE; + } + + /* checksum check */ + if ((helper->flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 rec_csum = 0; + guint8 rec_csum_expected; + for (guint8 i = 0; i < rec_count; i++) { + guint8 csum_tmp = 0; + if (!fu_firmware_strparse_uint8_safe(token->str, + token->len, + (i * 2) + 2, + &csum_tmp, + error)) + return FALSE; + rec_csum += csum_tmp; + } + rec_csum ^= 0xff; + if (!fu_firmware_strparse_uint8_safe(token->str, + token->len, + (rec_count * 2) + 2, + &rec_csum_expected, + error)) + return FALSE; + if (rec_csum != rec_csum_expected) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum incorrect line %u, " + "expected %02x, got %02x", + token_idx + 1, + rec_csum_expected, + rec_csum); + return FALSE; + } + } + + /* set each command settings */ + switch (rec_kind) { + case FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER: + addrsz = 2; + require_data = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16: + addrsz = 2; + require_data = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24: + addrsz = 3; + require_data = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32: + addrsz = 4; + require_data = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16: + addrsz = 2; + helper->got_eof = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24: + addrsz = 3; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32: + addrsz = 4; + helper->got_eof = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24: + addrsz = 3; + helper->got_eof = TRUE; + break; + case FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16: + addrsz = 2; + helper->got_eof = TRUE; + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid srec record type S%c at line %u", + token->str[1], + token_idx + 1); + return FALSE; + } + + /* parse address */ + switch (addrsz) { + case 2: + if (!fu_firmware_strparse_uint16_safe(token->str, + token->len, + 4, + &rec_addr16, + error)) + return FALSE; + rec_addr32 = rec_addr16; + break; + case 3: + if (!fu_firmware_strparse_uint24_safe(token->str, + token->len, + 4, + &rec_addr32, + error)) + return FALSE; + break; + case 4: + if (!fu_firmware_strparse_uint32_safe(token->str, + token->len, + 4, + &rec_addr32, + error)) + return FALSE; + break; + default: + g_assert_not_reached(); + } + if (g_getenv("FU_SREC_FIRMWARE_VERBOSE") != NULL) { + g_debug("line %03u S%u addr:0x%04x datalen:0x%02x", + token_idx + 1, + rec_kind, + rec_addr32, + (guint)rec_count - addrsz - 1); + } + if (require_data && rec_count == addrsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "S%u required data but not provided", + rec_kind); + return FALSE; + } + + /* data */ + rcd = fu_srec_firmware_record_new(token_idx + 1, rec_kind, rec_addr32); + if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { + for (gsize i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { + guint8 tmp = 0; + if (!fu_firmware_strparse_uint8_safe(token->str, + token->len, + i, + &tmp, + error)) + return FALSE; + fu_byte_array_append_uint8(rcd->buf, tmp); + } + } + g_ptr_array_add(priv->records, g_steal_pointer(&rcd)); + return TRUE; +} + +static gboolean +fu_srec_firmware_tokenize(FuFirmware *firmware, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + FuSrecFirmware *self = FU_SREC_FIRMWARE(firmware); + FuSrecFirmwareTokenHelper helper = {.self = self, .flags = flags, .got_eof = FALSE}; + + /* parse records */ + if (!fu_strsplit_full(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + "\n", + fu_srec_firmware_tokenize_cb, + &helper, + error)) + return FALSE; + + /* no EOF */ + if (!helper.got_eof) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no EOF, perhaps truncated file"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_srec_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSrecFirmware *self = FU_SREC_FIRMWARE(firmware); + FuSrecFirmwarePrivate *priv = GET_PRIVATE(self); + gboolean got_hdr = FALSE; + guint16 data_cnt = 0; + guint32 addr32_last = 0; + guint32 img_address = 0; + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GByteArray) outbuf = g_byte_array_new(); + + /* parse records */ + for (guint j = 0; j < priv->records->len; j++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index(priv->records, j); + + /* header */ + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER) { + g_autoptr(GString) modname = g_string_new(NULL); + + /* check for duplicate */ + if (got_hdr) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate header record at line %u", + rcd->ln); + return FALSE; + } + + /* could be anything, lets assume text */ + for (guint i = 0; i < rcd->buf->len; i++) { + gchar tmp = rcd->buf->data[i]; + if (!g_ascii_isgraph(tmp)) + break; + g_string_append_c(modname, tmp); + } + if (modname->len != 0) + fu_firmware_set_id(firmware, modname->str); + got_hdr = TRUE; + continue; + } + + /* verify we got all records */ + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16) { + if (rcd->addr != data_cnt) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "count record was not valid, got 0x%02x expected " + "0x%02x at line %u", + (guint)rcd->addr, + (guint)data_cnt, + rcd->ln); + return FALSE; + } + continue; + } + + /* data */ + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16 || + rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24 || + rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) { + /* invalid */ + if (!got_hdr) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "missing header record at line %u", + rcd->ln); + return FALSE; + } + + /* does not make sense */ + if (rcd->addr < addr32_last) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid address 0x%x, last was 0x%x at line %u", + (guint)rcd->addr, + (guint)addr32_last, + rcd->ln); + return FALSE; + } + if (rcd->addr < offset) { + g_debug( + "ignoring data at 0x%x as before start address 0x%x at line %u", + (guint)rcd->addr, + (guint)offset, + rcd->ln); + } else { + guint32 len_hole = rcd->addr - addr32_last; + + /* fill any holes, but only up to 1Mb to avoid a DoS */ + if (addr32_last > 0 && len_hole > 0x100000) { + g_set_error( + error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill at line %u", + (guint)len_hole, + rcd->ln); + return FALSE; + } + if (addr32_last > 0x0 && len_hole > 1) { + g_debug("filling address 0x%08x to 0x%08x at line %u", + addr32_last + 1, + addr32_last + len_hole - 1, + rcd->ln); + for (guint i = 0; i < len_hole; i++) + fu_byte_array_append_uint8(outbuf, 0xff); + } + + /* add data */ + g_byte_array_append(outbuf, rcd->buf->data, rcd->buf->len); + if (img_address == 0x0) + img_address = rcd->addr; + addr32_last = rcd->addr + rcd->buf->len; + if (addr32_last < rcd->addr) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "overflow from address 0x%x at line %u", + (guint)rcd->addr, + rcd->ln); + return FALSE; + } + } + data_cnt++; + } + } + + /* add single image */ + img_bytes = g_bytes_new(outbuf->data, outbuf->len); + fu_firmware_set_bytes(firmware, img_bytes); + fu_firmware_set_addr(firmware, img_address); + return TRUE; +} + +static void +fu_srec_firmware_write_line(GString *str, + FuFirmareSrecRecordKind kind, + guint32 addr, + const guint8 *buf, + gsize bufsz) +{ + guint8 csum = 0; + g_autoptr(GByteArray) buf_addr = g_byte_array_new(); + + if (kind == FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER || + kind == FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16 || + kind == FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16 || + kind == FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16) { + fu_byte_array_append_uint16(buf_addr, addr, G_BIG_ENDIAN); + } else if (kind == FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24 || + kind == FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24 || + kind == FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24) { + fu_byte_array_append_uint32(buf_addr, addr, G_BIG_ENDIAN); + g_byte_array_remove_index(buf_addr, 0); + } else if (kind == FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32 || + kind == FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32) { + fu_byte_array_append_uint32(buf_addr, addr, G_BIG_ENDIAN); + } + + /* bytecount + address + data */ + csum = buf_addr->len + bufsz + 1; + for (guint i = 0; i < buf_addr->len; i++) + csum += buf_addr->data[i]; + for (guint i = 0; i < bufsz; i++) + csum += buf[i]; + csum ^= 0xff; + + /* output record */ + g_string_append_printf(str, "S%X", kind); + g_string_append_printf(str, "%02X", (guint)(buf_addr->len + bufsz + 1)); + for (guint i = 0; i < buf_addr->len; i++) + g_string_append_printf(str, "%02X", buf_addr->data[i]); + for (guint i = 0; i < bufsz; i++) + g_string_append_printf(str, "%02X", buf[i]); + g_string_append_printf(str, "%02X\n", csum); +} + +static GBytes * +fu_srec_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(GBytes) buf_blob = NULL; + const gchar *id = fu_firmware_get_id(firmware); + gsize id_strlen = id != NULL ? strlen(id) : 0; + FuFirmareSrecRecordKind kind_data = FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16; + FuFirmareSrecRecordKind kind_coun = FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16; + FuFirmareSrecRecordKind kind_term = FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16; + + /* upgrade to longer addresses? */ + if (fu_firmware_get_addr(firmware) >= (1ull << 24)) { + kind_data = FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32; + kind_term = FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32; /* intentional... */ + } else if (fu_firmware_get_addr(firmware) >= (1ull << 16)) { + kind_data = FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24; + kind_term = FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24; + } + + /* main blob */ + buf_blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (buf_blob == NULL) + return NULL; + + /* header */ + fu_srec_firmware_write_line(str, + FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER, + 0x0, + (const guint8 *)id, + id_strlen); + + /* payload */ + if (g_bytes_get_size(buf_blob) > 0) { + g_autoptr(GPtrArray) chunks = NULL; + chunks = fu_chunk_array_new_from_bytes(buf_blob, + fu_firmware_get_addr(firmware), + 0x0, + 64); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + fu_srec_firmware_write_line(str, + kind_data, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk)); + } + /* upgrade to longer format */ + if (chunks->len > G_MAXUINT16) + kind_coun = FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24; + fu_srec_firmware_write_line(str, kind_coun, chunks->len, NULL, 0); + } + + /* EOF */ + fu_srec_firmware_write_line(str, kind_term, 0x0, NULL, 0); + + /* success */ + return g_string_free_to_bytes(g_steal_pointer(&str)); +} + +static void +fu_srec_firmware_finalize(GObject *object) +{ + FuSrecFirmware *self = FU_SREC_FIRMWARE(object); + FuSrecFirmwarePrivate *priv = GET_PRIVATE(self); + g_ptr_array_unref(priv->records); + G_OBJECT_CLASS(fu_srec_firmware_parent_class)->finalize(object); +} + +static void +fu_srec_firmware_init(FuSrecFirmware *self) +{ + FuSrecFirmwarePrivate *priv = GET_PRIVATE(self); + priv->records = g_ptr_array_new_with_free_func((GFreeFunc)fu_srec_firmware_record_free); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_srec_firmware_class_init(FuSrecFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_srec_firmware_finalize; + klass_firmware->parse = fu_srec_firmware_parse; + klass_firmware->tokenize = fu_srec_firmware_tokenize; + klass_firmware->write = fu_srec_firmware_write; +} + +/** + * fu_srec_firmware_new: + * + * Creates a new #FuFirmware of type SREC + * + * Since: 1.3.2 + **/ +FuFirmware * +fu_srec_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_SREC_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-srec-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-srec-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..292f68adaaed96692ec3ef5b8f7472565af82e84 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-srec-firmware.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_SREC_FIRMWARE (fu_srec_firmware_get_type()) +#define FU_TYPE_SREC_FIRMWARE_RECORD (fu_srec_firmware_record_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuSrecFirmware, fu_srec_firmware, FU, SREC_FIRMWARE, FuFirmware) + +struct _FuSrecFirmwareClass { + FuFirmwareClass parent_class; +}; + +/** + * FuFirmareSrecRecordKind: + * @FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER: Header + * @FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16: 16 bit data + * @FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24: 24 bit data + * @FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32: 32 bit data + * @FU_FIRMWARE_SREC_RECORD_KIND_S4_RESERVED: Reserved value + * @FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16: 16 bit count + * @FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24: 24 bit count + * @FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32: 32 bit count + * @FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24: 24 bit termination + * @FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16: 16 bit termination + * + * The kind of SREC record kind. + **/ +typedef enum { + FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER, + FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16, + FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24, + FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32, + FU_FIRMWARE_SREC_RECORD_KIND_S4_RESERVED, + FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16, + FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24, + FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32, + FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24, + FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16, + /*< private >*/ + FU_FIRMWARE_SREC_RECORD_KIND_LAST +} FuFirmareSrecRecordKind; + +/** + * FuSrecFirmwareRecord: + * + * A single SREC record. + **/ +typedef struct { + guint ln; + FuFirmareSrecRecordKind kind; + guint32 addr; + GByteArray *buf; +} FuSrecFirmwareRecord; + +FuFirmware * +fu_srec_firmware_new(void); +GPtrArray * +fu_srec_firmware_get_records(FuSrecFirmware *self); +GType +fu_srec_firmware_record_get_type(void); +FuSrecFirmwareRecord * +fu_srec_firmware_record_new(guint ln, FuFirmareSrecRecordKind kind, guint32 addr); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-string.c b/fwupd-1.8.6/libfwupdplugin/fu-string.c new file mode 100644 index 0000000000000000000000000000000000000000..c0594de70565f26bb5d36785711d03b78cdd8945 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-string.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include "fu-string.h" + +/** + * fu_strtoull: + * @str: a string, e.g. `0x1234` + * @value: (out) (nullable): parsed value + * @min: minimum acceptable value, typically 0 + * @max: maximum acceptable value, typically G_MAXUINT64 + * @error: (nullable): optional return location for an error + * + * Converts a string value to an integer. Values are assumed base 10, unless + * prefixed with "0x" where they are parsed as base 16. + * + * Returns: %TRUE if the value was parsed correctly, or %FALSE for error + * + * Since: 1.8.2 + **/ +gboolean +fu_strtoull(const gchar *str, guint64 *value, guint64 min, guint64 max, GError **error) +{ + gchar *endptr = NULL; + guint64 value_tmp; + guint base = 10; + + /* sanity check */ + if (str == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse NULL"); + return FALSE; + } + + /* detect hex */ + if (g_str_has_prefix(str, "0x")) { + str += 2; + base = 16; + } + + /* convert */ + value_tmp = g_ascii_strtoull(str, &endptr, base); + if ((gsize)(endptr - str) != strlen(str) && *endptr != '\n') { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "cannot parse %s", str); + return FALSE; + } + + /* overflow check */ + if (value_tmp == G_MAXUINT64) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as caused overflow", + str); + return FALSE; + } + + /* range check */ + if (value_tmp < min) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "value %" G_GUINT64_FORMAT " was below minimum %" G_GUINT64_FORMAT, + value_tmp, + min); + return FALSE; + } + if (value_tmp > max) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "value %" G_GUINT64_FORMAT " was above maximum %" G_GUINT64_FORMAT, + value_tmp, + max); + return FALSE; + } + + /* success */ + if (value != NULL) + *value = value_tmp; + return TRUE; +} + +/** + * fu_strtobool: + * @str: a string, e.g. `true` + * @value: (out) (nullable): parsed value + * @error: (nullable): optional return location for an error + * + * Converts a string value to a boolean. Only `true` and `false` are accepted values. + * + * Returns: %TRUE if the value was parsed correctly, or %FALSE for error + * + * Since: 1.8.2 + **/ +gboolean +fu_strtobool(const gchar *str, gboolean *value, GError **error) +{ + /* sanity check */ + if (str == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse NULL"); + return FALSE; + } + + /* be super strict */ + if (g_strcmp0(str, "true") == 0) { + if (value != NULL) + *value = TRUE; + return TRUE; + } + if (g_strcmp0(str, "false") == 0) { + if (value != NULL) + *value = FALSE; + return TRUE; + } + + /* invalid */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse %s as boolean, expected true|false", + str); + return FALSE; +} + +/** + * fu_strstrip: + * @str: a string, e.g. ` test ` + * + * Removes leading and trailing whitespace from a constant string. + * + * Returns: newly allocated string + * + * Since: 1.8.2 + **/ +gchar * +fu_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 (!g_ascii_isspace(str[i])) + tail = i; + } + return g_strndup(str + head, tail - head + 1); +} + +/** + * fu_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.8.2 + **/ +guint +fu_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_strwidth: + * @text: the string to operate on + * + * Returns the width of the string in displayed characters on the console. + * + * Returns: width of text + * + * Since: 1.8.2 + **/ +gsize +fu_strwidth(const gchar *text) +{ + const gchar *p = text; + gsize width = 0; + + g_return_val_if_fail(text != NULL, 0); + + while (*p) { + gunichar c = g_utf8_get_char(p); + if (g_unichar_iswide(c)) + width += 2; + else if (!g_unichar_iszerowidth(c)) + width += 1; + p = g_utf8_next_char(p); + } + return width; +} + +/** + * fu_string_append: + * @str: a #GString + * @idt: the indent + * @key: a string to append + * @value: a string to append + * + * Appends a key and string value to a string + * + * Since: 1.8.2 + */ +void +fu_string_append(GString *str, guint idt, const gchar *key, const gchar *value) +{ + const guint align = 24; + gsize keysz; + + g_return_if_fail(idt * 2 < align); + + /* ignore */ + if (key == NULL) + return; + for (gsize i = 0; i < idt; i++) + g_string_append(str, " "); + if (key[0] != '\0') { + g_string_append_printf(str, "%s:", key); + keysz = (idt * 2) + fu_strwidth(key) + 1; + } else { + keysz = idt * 2; + } + if (value != NULL) { + g_auto(GStrv) split = NULL; + split = g_strsplit(value, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + if (i == 0) { + for (gsize j = keysz; j < align; j++) + g_string_append(str, " "); + } else { + g_string_append(str, "\n"); + for (gsize j = 0; j < idt; j++) + g_string_append(str, " "); + } + g_string_append(str, split[i]); + } + } + g_string_append(str, "\n"); +} + +/** + * fu_string_append_ku: + * @str: a #GString + * @idt: the indent + * @key: a string to append + * @value: guint64 + * + * Appends a key and unsigned integer to a string + * + * Since: 1.8.2 + */ +void +fu_string_append_ku(GString *str, guint idt, const gchar *key, guint64 value) +{ + g_autofree gchar *tmp = g_strdup_printf("%" G_GUINT64_FORMAT, value); + fu_string_append(str, idt, key, tmp); +} + +/** + * fu_string_append_kx: + * @str: a #GString + * @idt: the indent + * @key: a string to append + * @value: guint64 + * + * Appends a key and hex integer to a string + * + * Since: 1.8.2 + */ +void +fu_string_append_kx(GString *str, guint idt, const gchar *key, guint64 value) +{ + g_autofree gchar *tmp = g_strdup_printf("0x%x", (guint)value); + fu_string_append(str, idt, key, tmp); +} + +/** + * fu_string_append_kb: + * @str: a #GString + * @idt: the indent + * @key: a string to append + * @value: Boolean + * + * Appends a key and boolean value to a string + * + * Since: 1.8.2 + */ +void +fu_string_append_kb(GString *str, guint idt, const gchar *key, gboolean value) +{ + fu_string_append(str, idt, key, value ? "true" : "false"); +} + +/** + * fu_strsplit: + * @str: (not nullable): a string to split + * @sz: size of @str, which must be more than 0 + * @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. + * + * Returns: (transfer full): a newly-allocated NULL-terminated array of strings + * + * Since: 1.8.2 + **/ +gchar ** +fu_strsplit(const gchar *str, gsize sz, const gchar *delimiter, gint max_tokens) +{ + g_return_val_if_fail(str != NULL, NULL); + g_return_val_if_fail(sz > 0, NULL); + 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); +} + +/** + * fu_strsplit_full: + * @str: a string to split + * @sz: size of @str, or -1 for unknown + * @delimiter: a string which specifies the places at which to split the string + * @callback: (scope call): a #FuStrsplitFunc. + * @user_data: user data + * @error: (nullable): optional return location for an error + * + * Splits the string, calling the given function for each + * of the tokens found. If any @callback returns %FALSE scanning is aborted. + * + * Use this function in preference to fu_strsplit() when the input file is untrusted, + * and you don't want to allocate a GStrv with billions of one byte items. + * + * Returns: %TRUE if no @callback returned FALSE + * + * Since: 1.8.2 + */ +gboolean +fu_strsplit_full(const gchar *str, + gssize sz, + const gchar *delimiter, + FuStrsplitFunc callback, + gpointer user_data, + GError **error) +{ + gsize delimiter_sz; + gsize str_sz; + guint found_idx = 0; + guint token_idx = 0; + + g_return_val_if_fail(str != NULL, FALSE); + g_return_val_if_fail(delimiter != NULL && delimiter[0] != '\0', FALSE); + g_return_val_if_fail(callback != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* make known */ + str_sz = sz != -1 ? (gsize)sz : strlen(str); + delimiter_sz = strlen(delimiter); + + /* cannot split */ + if (delimiter_sz > str_sz) { + g_autoptr(GString) token = g_string_new(str); + return callback(token, token_idx, user_data, error); + } + + /* start splittin' */ + for (gsize i = 0; i < (str_sz - delimiter_sz) + 1;) { + if (strncmp(str + i, delimiter, delimiter_sz) == 0) { + g_autoptr(GString) token = g_string_new(NULL); + g_string_append_len(token, str + found_idx, i - found_idx); + if (!callback(token, token_idx++, user_data, error)) + return FALSE; + i += delimiter_sz; + found_idx = i; + } else { + i++; + } + } + + /* any bits left over? */ + if (found_idx != str_sz) { + g_autoptr(GString) token = g_string_new(NULL); + g_string_append_len(token, str + found_idx, str_sz - found_idx); + if (!callback(token, token_idx, user_data, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_strsafe: + * @str: (nullable): a string to make safe for printing + * @maxsz: maximum size of returned string + * + * Converts a string into something that can be safely printed. + * + * Returns: (transfer full): safe string, or %NULL if there was nothing valid + * + * Since: 1.8.2 + **/ +gchar * +fu_strsafe(const gchar *str, gsize maxsz) +{ + gboolean valid = FALSE; + g_autoptr(GString) tmp = NULL; + + /* sanity check */ + if (str == NULL || maxsz == 0) + return NULL; + + /* replace non-printable chars with '.' */ + tmp = g_string_sized_new(maxsz); + for (gsize i = 0; i < maxsz && str[i] != '\0'; i++) { + if (!g_ascii_isprint(str[i])) { + g_string_append_c(tmp, '.'); + continue; + } + g_string_append_c(tmp, str[i]); + if (!g_ascii_isspace(str[i])) + valid = TRUE; + } + + /* if just junk, don't return 'all dots' */ + if (tmp->len == 0 || !valid) + return NULL; + return g_string_free(g_steal_pointer(&tmp), FALSE); +} + +/** + * fu_strjoin: + * @separator: (nullable): string to insert between each of the strings + * @array: (element-type utf8): a #GPtrArray + * + * Joins an array of strings together to form one long string, with the optional + * separator inserted between each of them. + * + * If @array has no items, the return value will be an empty string. + * If @array contains a single item, separator will not appear in the resulting + * string. + * + * Returns: a string + * + * Since: 1.8.2 + **/ +gchar * +fu_strjoin(const gchar *separator, GPtrArray *array) +{ + g_autofree const gchar **strv = NULL; + + g_return_val_if_fail(array != NULL, NULL); + + strv = g_new0(const gchar *, array->len + 1); + for (guint i = 0; i < array->len; i++) + strv[i] = g_ptr_array_index(array, i); + return g_strjoinv(separator, (gchar **)strv); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-string.h b/fwupd-1.8.6/libfwupdplugin/fu-string.h new file mode 100644 index 0000000000000000000000000000000000000000..27eed8f345173178fedd9d16caa1d690a7842e34 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-string.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +guint +fu_string_replace(GString *string, const gchar *search, const gchar *replace); +void +fu_string_append(GString *str, guint idt, const gchar *key, const gchar *value); +void +fu_string_append_ku(GString *str, guint idt, const gchar *key, guint64 value); +void +fu_string_append_kx(GString *str, guint idt, const gchar *key, guint64 value); +void +fu_string_append_kb(GString *str, guint idt, const gchar *key, gboolean value); + +gchar * +fu_strsafe(const gchar *str, gsize maxsz); +gboolean +fu_strtoull(const gchar *str, guint64 *value, guint64 min, guint64 max, GError **error); +gboolean +fu_strtobool(const gchar *str, gboolean *value, GError **error); +gchar * +fu_strstrip(const gchar *str); +gsize +fu_strwidth(const gchar *text); +gchar ** +fu_strsplit(const gchar *str, gsize sz, const gchar *delimiter, gint max_tokens); +gchar * +fu_strjoin(const gchar *separator, GPtrArray *array); + +/** + * FuStrsplitFunc: + * @token: a #GString + * @token_idx: the token number + * @user_data: user data + * @error: a #GError or NULL + * + * The fu_strsplit_full() iteration callback. + */ +typedef gboolean (*FuStrsplitFunc)(GString *token, + guint token_idx, + gpointer user_data, + GError **error); +gboolean +fu_strsplit_full(const gchar *str, + gssize sz, + const gchar *delimiter, + FuStrsplitFunc callback, + gpointer user_data, + GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-sum.c b/fwupd-1.8.6/libfwupdplugin/fu-sum.c new file mode 100644 index 0000000000000000000000000000000000000000..e5064b45b781a856375ec3c59f16b6b554484019 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-sum.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include "fu-mem.h" +#include "fu-sum.h" + +/** + * fu_sum8: + * @buf: memory buffer + * @bufsz: size of @buf + * + * Returns the arithmetic sum of all bytes in @buf. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint8 +fu_sum8(const guint8 *buf, gsize bufsz) +{ + guint8 checksum = 0; + g_return_val_if_fail(buf != NULL, G_MAXUINT8); + for (gsize i = 0; i < bufsz; i++) + checksum += buf[i]; + return checksum; +} + +/** + * fu_sum8_bytes: + * @blob: a #GBytes + * + * Returns the arithmetic sum of all bytes in @blob. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint8 +fu_sum8_bytes(GBytes *blob) +{ + g_return_val_if_fail(blob != NULL, G_MAXUINT8); + if (g_bytes_get_size(blob) == 0) + return 0; + return fu_sum8(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); +} + +/** + * fu_sum16: + * @buf: memory buffer + * @bufsz: size of @buf + * + * Returns the arithmetic sum of all bytes in @buf, adding them one byte at a time. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint16 +fu_sum16(const guint8 *buf, gsize bufsz) +{ + guint16 checksum = 0; + g_return_val_if_fail(buf != NULL, G_MAXUINT16); + for (gsize i = 0; i < bufsz; i++) + checksum += buf[i]; + return checksum; +} + +/** + * fu_sum16_bytes: + * @blob: a #GBytes + * + * Returns the arithmetic sum of all bytes in @blob, adding them one byte at a time. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint16 +fu_sum16_bytes(GBytes *blob) +{ + g_return_val_if_fail(blob != NULL, G_MAXUINT16); + return fu_sum16(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); +} + +/** + * fu_sum16w: + * @buf: memory buffer + * @bufsz: size of @buf + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Returns the arithmetic sum of all bytes in @buf, adding them one word at a time. + * The caller must ensure that @bufsz is a multiple of 2. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint16 +fu_sum16w(const guint8 *buf, gsize bufsz, FuEndianType endian) +{ + guint16 checksum = 0; + g_return_val_if_fail(buf != NULL, G_MAXUINT16); + g_return_val_if_fail(bufsz % 2 == 0, G_MAXUINT16); + for (gsize i = 0; i < bufsz; i += 2) + checksum += fu_memread_uint16(&buf[i], endian); + return checksum; +} + +/** + * fu_sum16w_bytes: + * @blob: a #GBytes + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Returns the arithmetic sum of all bytes in @blob, adding them one word at a time. + * The caller must ensure that the size of @blob is a multiple of 2. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint16 +fu_sum16w_bytes(GBytes *blob, FuEndianType endian) +{ + g_return_val_if_fail(blob != NULL, G_MAXUINT16); + return fu_sum16w(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob), endian); +} + +/** + * fu_sum32: + * @buf: memory buffer + * @bufsz: size of @buf + * + * Returns the arithmetic sum of all bytes in @buf, adding them one byte at a time. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint32 +fu_sum32(const guint8 *buf, gsize bufsz) +{ + guint32 checksum = 0; + g_return_val_if_fail(buf != NULL, G_MAXUINT32); + for (gsize i = 0; i < bufsz; i++) + checksum += buf[i]; + return checksum; +} + +/** + * fu_sum32_bytes: + * @blob: a #GBytes + * + * Returns the arithmetic sum of all bytes in @blob, adding them one byte at a time. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint32 +fu_sum32_bytes(GBytes *blob) +{ + g_return_val_if_fail(blob != NULL, G_MAXUINT32); + return fu_sum32(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); +} + +/** + * fu_sum32w: + * @buf: memory buffer + * @bufsz: size of @buf + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Returns the arithmetic sum of all bytes in @buf, adding them one dword at a time. + * The caller must ensure that @bufsz is a multiple of 4. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint32 +fu_sum32w(const guint8 *buf, gsize bufsz, FuEndianType endian) +{ + guint32 checksum = 0; + g_return_val_if_fail(buf != NULL, G_MAXUINT32); + g_return_val_if_fail(bufsz % 4 == 0, G_MAXUINT32); + for (gsize i = 0; i < bufsz; i += 4) + checksum += fu_memread_uint32(&buf[i], endian); + return checksum; +} + +/** + * fu_sum32w_bytes: + * @blob: a #GBytes + * @endian: an endian type, e.g. %G_LITTLE_ENDIAN + * + * Returns the arithmetic sum of all bytes in @blob, adding them one dword at a time. + * The caller must ensure that the size of @blob is a multiple of 4. + * + * Returns: sum value + * + * Since: 1.8.2 + **/ +guint32 +fu_sum32w_bytes(GBytes *blob, FuEndianType endian) +{ + g_return_val_if_fail(blob != NULL, G_MAXUINT32); + return fu_sum32w(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob), endian); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-sum.h b/fwupd-1.8.6/libfwupdplugin/fu-sum.h new file mode 100644 index 0000000000000000000000000000000000000000..3d174af45d1feb9e5ecb48cd5a0a8aafcc1249d8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-sum.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-common.h" + +guint8 +fu_sum8(const guint8 *buf, gsize bufsz); +guint8 +fu_sum8_bytes(GBytes *blob); +guint16 +fu_sum16(const guint8 *buf, gsize bufsz); +guint16 +fu_sum16_bytes(GBytes *blob); +guint16 +fu_sum16w(const guint8 *buf, gsize bufsz, FuEndianType endian); +guint16 +fu_sum16w_bytes(GBytes *blob, FuEndianType endian); +guint32 +fu_sum32(const guint8 *buf, gsize bufsz); +guint32 +fu_sum32_bytes(GBytes *blob); +guint32 +fu_sum32w(const guint8 *buf, gsize bufsz, FuEndianType endian); +guint32 +fu_sum32w_bytes(GBytes *blob, FuEndianType endian); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-udev-device-private.h b/fwupd-1.8.6/libfwupdplugin/fu-udev-device-private.h new file mode 100644 index 0000000000000000000000000000000000000000..f94067962ebcc2e0ef47e4a0ec147c2c8fca0b28 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-udev-device-private.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-udev-device.h" + +void +fu_udev_device_emit_changed(FuUdevDevice *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-udev-device.c b/fwupd-1.8.6/libfwupdplugin/fu-udev-device.c new file mode 100644 index 0000000000000000000000000000000000000000..0521b1ff80f1cc3a0803b91d8cf319ce38229bd1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-udev-device.c @@ -0,0 +1,2277 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuUdevDevice" + +#include "config.h" + +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_IOCTL_H +#include +#endif +#include +#include +#include +#include + +#include "fu-device-private.h" +#include "fu-i2c-device.h" +#include "fu-string.h" +#include "fu-udev-device-private.h" + +/** + * FuUdevDevice: + * + * A UDev device, typically only available on Linux. + * + * See also: [class@FuDevice] + */ + +typedef struct { + GUdevDevice *udev_device; + guint16 vendor; + guint16 model; + guint16 subsystem_vendor; + guint16 subsystem_model; + guint8 revision; + gchar *subsystem; + gchar *bind_id; + gchar *driver; + gchar *device_file; + gint fd; + FuUdevDeviceFlags flags; +} FuUdevDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE) + +enum { + PROP_0, + PROP_UDEV_DEVICE, + PROP_SUBSYSTEM, + PROP_DRIVER, + PROP_DEVICE_FILE, + PROP_BIND_ID, + 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_autoptr(GError) error = NULL; + g_return_if_fail(FU_IS_UDEV_DEVICE(self)); + g_debug("FuUdevDevice emit changed"); + if (!fu_device_rescan(FU_DEVICE(self), &error)) + g_debug("%s", error->message); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0); +} + +#ifdef HAVE_GUDEV +static guint16 +fu_udev_device_get_sysfs_attr_as_uint16(GUdevDevice *udev_device, const gchar *name) +{ + const gchar *tmp; + guint64 tmp64 = 0; + g_autoptr(GError) error_local = NULL; + + tmp = g_udev_device_get_sysfs_attr(udev_device, name); + if (tmp == NULL) + return 0x0; + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT16 - 1, &error_local)) { + g_warning("reading %s for %s was invalid: %s", name, tmp, error_local->message); + return 0x0; + } + return tmp64; +} + +static guint8 +fu_udev_device_get_sysfs_attr_as_uint8(GUdevDevice *udev_device, const gchar *name) +{ + const gchar *tmp; + guint64 tmp64 = 0; + g_autoptr(GError) error_local = NULL; + + tmp = g_udev_device_get_sysfs_attr(udev_device, name); + if (tmp == NULL) + return 0x0; + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT8 - 1, &error_local)) { + g_warning("reading %s for %s was invalid: %s", + name, + g_udev_device_get_sysfs_path(udev_device), + error_local->message); + return 0x0; + } + return tmp64; +} + +static void +fu_udev_device_to_string_raw(GUdevDevice *udev_device, guint idt, GString *str) +{ + const gchar *const *keys; + if (udev_device == NULL) + return; + keys = g_udev_device_get_property_keys(udev_device); + for (guint i = 0; keys[i] != NULL; i++) { + fu_string_append(str, + idt, + 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++) { + fu_string_append(str, + idt, + keys[i], + g_udev_device_get_sysfs_attr(udev_device, keys[i])); + } +} +#endif + +static void +fu_udev_device_to_string(FuDevice *device, guint idt, GString *str) +{ +#ifdef HAVE_GUDEV + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + if (priv->vendor != 0x0) + fu_string_append_kx(str, idt, "Vendor", priv->vendor); + if (priv->model != 0x0) + fu_string_append_kx(str, idt, "Model", priv->model); + if (priv->subsystem_vendor != 0x0 || priv->subsystem_model != 0x0) { + fu_string_append_kx(str, idt, "SubsystemVendor", priv->subsystem_vendor); + fu_string_append_kx(str, idt, "SubsystemModel", priv->subsystem_model); + } + if (priv->revision != 0x0) + fu_string_append_kx(str, idt, "Revision", priv->revision); + if (priv->subsystem != NULL) + fu_string_append(str, idt, "Subsystem", priv->subsystem); + if (priv->driver != NULL) + fu_string_append(str, idt, "Driver", priv->driver); + if (priv->bind_id != NULL) + fu_string_append(str, idt, "BindId", priv->bind_id); + if (priv->device_file != NULL) + fu_string_append(str, idt, "DeviceFile", priv->device_file); + if (priv->udev_device != NULL) { + fu_string_append(str, + idt, + "SysfsPath", + g_udev_device_get_sysfs_path(priv->udev_device)); + } + if (g_getenv("FU_UDEV_DEVICE_DEBUG") != NULL) { + g_autoptr(GUdevDevice) udev_parent = NULL; + fu_udev_device_to_string_raw(priv->udev_device, idt, str); + udev_parent = g_udev_device_get_parent(priv->udev_device); + if (udev_parent != NULL) { + fu_string_append(str, idt, "Parent", NULL); + fu_udev_device_to_string_raw(udev_parent, idt + 1, str); + } + } +#endif +} + +static gboolean +fu_udev_device_ensure_bind_id(FuUdevDevice *self, GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* sanity check */ + if (priv->bind_id != NULL) + return TRUE; + +#ifdef HAVE_GUDEV + /* automatically set the bind ID from the subsystem */ + if (g_strcmp0(priv->subsystem, "pci") == 0) { + priv->bind_id = + g_strdup(g_udev_device_get_property(priv->udev_device, "PCI_SLOT_NAME")); + return TRUE; + } + if (g_strcmp0(priv->subsystem, "hid") == 0) { + priv->bind_id = g_strdup(g_udev_device_get_property(priv->udev_device, "HID_PHYS")); + return TRUE; + } + if (g_strcmp0(priv->subsystem, "usb") == 0) { + priv->bind_id = + g_path_get_basename(g_udev_device_get_sysfs_path(priv->udev_device)); + return TRUE; + } +#endif + + /* nothing found automatically */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot derive bind-id from subsystem %s", + priv->subsystem); + return FALSE; +} + +static void +fu_udev_device_set_subsystem(FuUdevDevice *self, const gchar *subsystem) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->subsystem, subsystem) == 0) + return; + + g_free(priv->subsystem); + priv->subsystem = g_strdup(subsystem); + g_object_notify(G_OBJECT(self), "subsystem"); +} + +/** + * fu_udev_device_set_bind_id: + * @self: a #FuUdevDevice + * @bind_id: a bind-id string, e.g. `pci:0:0:1` + * + * Sets the device ID used for binding the device, e.g. `pci:1:2:3` + * + * Since: 1.7.2 + **/ +void +fu_udev_device_set_bind_id(FuUdevDevice *self, const gchar *bind_id) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->bind_id, bind_id) == 0) + return; + + g_free(priv->bind_id); + priv->bind_id = g_strdup(bind_id); + g_object_notify(G_OBJECT(self), "bind-id"); +} + +static void +fu_udev_device_set_driver(FuUdevDevice *self, const gchar *driver) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->driver, driver) == 0) + return; + + g_free(priv->driver); + priv->driver = g_strdup(driver); + g_object_notify(G_OBJECT(self), "driver"); +} + +static void +fu_udev_device_set_device_file(FuUdevDevice *self, const gchar *device_file) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* not changed */ + if (g_strcmp0(priv->device_file, device_file) == 0) + return; + + g_free(priv->device_file); + priv->device_file = g_strdup(device_file); + g_object_notify(G_OBJECT(self), "device-file"); +} + +#ifdef HAVE_GUDEV +static const gchar * +fu_udev_device_get_vendor_fallback(GUdevDevice *udev_device) +{ + const gchar *tmp; + tmp = g_udev_device_get_property(udev_device, "ID_VENDOR_FROM_DATABASE"); + if (tmp != NULL) + return tmp; + tmp = g_udev_device_get_property(udev_device, "ID_VENDOR"); + if (tmp != NULL) + return tmp; + return NULL; +} +#endif + +#ifdef HAVE_GUDEV +static gboolean +fu_udev_device_probe_serio(FuUdevDevice *self, GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + /* firmware ID */ + tmp = g_udev_device_get_property(priv->udev_device, "SERIO_FIRMWARE_ID"); + if (tmp != NULL) { + /* this prefix is not useful */ + if (g_str_has_prefix(tmp, "PNP: ")) + tmp += 5; + fu_device_add_instance_strsafe(FU_DEVICE(self), "FWID", tmp); + if (!fu_device_build_instance_id(FU_DEVICE(self), error, "SERIO", "FWID", NULL)) + return FALSE; + } + return TRUE; +} + +static guint16 +fu_udev_device_get_property_as_uint16(GUdevDevice *udev_device, const gchar *key) +{ + const gchar *tmp = g_udev_device_get_property(udev_device, key); + guint64 value = 0; + g_autofree gchar *str = NULL; + + if (tmp == NULL) + return 0x0; + str = g_strdup_printf("0x%s", tmp); + if (!fu_strtoull(str, &value, 0x0, G_MAXUINT16, NULL)) + return 0x0; + return (guint16)value; +} + +static void +fu_udev_device_set_vendor_from_udev_device(FuUdevDevice *self, GUdevDevice *udev_device) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + priv->vendor = fu_udev_device_get_sysfs_attr_as_uint16(udev_device, "vendor"); + priv->model = fu_udev_device_get_sysfs_attr_as_uint16(udev_device, "device"); + priv->revision = fu_udev_device_get_sysfs_attr_as_uint8(udev_device, "revision"); + priv->subsystem_vendor = + fu_udev_device_get_sysfs_attr_as_uint16(udev_device, "subsystem_vendor"); + priv->subsystem_model = + fu_udev_device_get_sysfs_attr_as_uint16(udev_device, "subsystem_device"); + + /* fallback to properties as udev might be using a subsystem-specific prober */ + if (priv->vendor == 0x0) + priv->vendor = fu_udev_device_get_property_as_uint16(udev_device, "ID_VENDOR_ID"); + if (priv->model == 0x0) + priv->model = fu_udev_device_get_property_as_uint16(udev_device, "ID_MODEL_ID"); + if (priv->revision == 0x0) + priv->revision = fu_udev_device_get_property_as_uint16(udev_device, "ID_REVISION"); +} + +static void +fu_udev_device_set_vendor_from_parent(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GUdevDevice) udev_device = g_object_ref(priv->udev_device); + while (TRUE) { + g_autoptr(GUdevDevice) parent = g_udev_device_get_parent(udev_device); + if (parent == NULL) + break; + fu_udev_device_set_vendor_from_udev_device(self, parent); + if (priv->vendor != 0x0 || priv->model != 0x0 || priv->revision != 0x0) + break; + g_set_object(&udev_device, parent); + } +} +#endif + +static gboolean +fu_udev_device_probe(FuDevice *device, GError **error) +{ + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); +#ifdef HAVE_GUDEV + const gchar *tmp; + g_autofree gchar *subsystem = NULL; + g_autoptr(GUdevDevice) udev_parent = NULL; + g_autoptr(GUdevDevice) parent_i2c = NULL; +#endif + + /* nothing to do */ + if (priv->udev_device == NULL) + return TRUE; + +#ifdef HAVE_GUDEV + /* get IDs, but fallback to the parent, grandparent, great-grandparent, etc */ + fu_udev_device_set_vendor_from_udev_device(self, priv->udev_device); + udev_parent = g_udev_device_get_parent(priv->udev_device); + if (udev_parent != NULL && priv->flags & FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT) + fu_udev_device_set_vendor_from_parent(self); + + /* hidraw helpfully encodes the information in a different place */ + if (udev_parent != NULL && priv->vendor == 0x0 && priv->model == 0x0 && + priv->revision == 0x0 && g_strcmp0(priv->subsystem, "hidraw") == 0) { + tmp = g_udev_device_get_property(udev_parent, "HID_ID"); + if (tmp != NULL) { + g_auto(GStrv) split = g_strsplit(tmp, ":", -1); + if (g_strv_length(split) == 3) { + guint64 val = g_ascii_strtoull(split[1], NULL, 16); + if (val > G_MAXUINT16) { + g_warning("reading %s for %s overflowed", + split[1], + g_udev_device_get_sysfs_path(priv->udev_device)); + } else { + priv->vendor = val; + } + val = g_ascii_strtoull(split[2], NULL, 16); + if (val > G_MAXUINT32) { + g_warning("reading %s for %s overflowed", + split[2], + g_udev_device_get_sysfs_path(priv->udev_device)); + } else { + priv->model = val; + } + } + } + tmp = g_udev_device_get_property(udev_parent, "HID_NAME"); + if (tmp != NULL) { + if (fu_device_get_name(device) == NULL) + fu_device_set_name(device, tmp); + } + } + + /* set the version if the revision has been set */ + if (fu_device_get_version(device) == NULL && + fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN) { + if (priv->revision != 0x00 && priv->revision != 0xFF) { + g_autofree gchar *version = g_strdup_printf("%02x", priv->revision); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(device, version); + } + } + + /* set model */ + if (fu_device_get_name(device) == 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) + tmp = g_udev_device_get_property(priv->udev_device, + "ID_PCI_CLASS_FROM_DATABASE"); + if (tmp != NULL) + fu_device_set_name(device, tmp); + } + + /* set vendor */ + if (fu_device_get_vendor(device) == NULL) { + tmp = fu_udev_device_get_vendor_fallback(priv->udev_device); + if (tmp != NULL) + fu_device_set_vendor(device, tmp); + } + + /* try harder to find a vendor name the user will recognize */ + if (priv->flags & FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT && udev_parent != NULL && + fu_device_get_vendor(device) == NULL) { + g_autoptr(GUdevDevice) device_tmp = g_object_ref(udev_parent); + for (guint i = 0; i < 0xff; i++) { + g_autoptr(GUdevDevice) parent = NULL; + tmp = fu_udev_device_get_vendor_fallback(device_tmp); + if (tmp != NULL) { + fu_device_set_vendor(device, tmp); + break; + } + parent = g_udev_device_get_parent(device_tmp); + if (parent == NULL) + break; + g_set_object(&device_tmp, parent); + } + } + + /* set serial */ + if (!fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER) && + 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 && + fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN) { + tmp = g_udev_device_get_property(priv->udev_device, "ID_REVISION"); + if (tmp != NULL) + fu_device_set_version(device, tmp); + } + + /* set vendor ID */ + subsystem = g_ascii_strup(g_udev_device_get_subsystem(priv->udev_device), -1); + if (subsystem != NULL && priv->vendor != 0x0000) { + g_autofree gchar *vendor_id = NULL; + vendor_id = g_strdup_printf("%s:0x%04X", subsystem, (guint)priv->vendor); + fu_device_add_vendor_id(device, vendor_id); + } + + /* add GUIDs in order of priority */ + if (priv->vendor != 0x0000) + fu_device_add_instance_u16(device, "VEN", priv->vendor); + if (priv->model != 0x0000) + fu_device_add_instance_u16(device, "DEV", priv->model); + if (priv->subsystem_vendor != 0x0000 || priv->subsystem_model != 0x0000) { + g_autofree gchar *subsys = + g_strdup_printf("%04X%04X", priv->subsystem_vendor, priv->subsystem_model); + fu_device_add_instance_str(device, "SUBSYS", subsys); + } + if (priv->revision != 0xFF) + fu_device_add_instance_u8(device, "REV", priv->revision); + + fu_device_build_instance_id_quirk(device, NULL, subsystem, "VEN", NULL); + fu_device_build_instance_id(device, NULL, subsystem, "VEN", "DEV", NULL); + fu_device_build_instance_id(device, NULL, subsystem, "VEN", "DEV", "REV", NULL); + fu_device_build_instance_id(device, NULL, subsystem, "VEN", "DEV", "SUBSYS", NULL); + fu_device_build_instance_id(device, NULL, subsystem, "VEN", "DEV", "SUBSYS", "REV", NULL); + + /* add device class */ + tmp = g_udev_device_get_sysfs_attr(priv->udev_device, "class"); + if (tmp != NULL && g_str_has_prefix(tmp, "0x")) + tmp += 2; + fu_device_add_instance_strup(device, "CLASS", tmp); + fu_device_build_instance_id_quirk(device, NULL, subsystem, "VEN", "CLASS", NULL); + + /* add devtype */ + fu_device_add_instance_strup(device, "TYPE", g_udev_device_get_devtype(priv->udev_device)); + fu_device_build_instance_id_quirk(device, NULL, subsystem, "TYPE", NULL); + + /* add the driver */ + fu_device_add_instance_str(device, "DRIVER", priv->driver); + fu_device_build_instance_id_quirk(device, NULL, subsystem, "DRIVER", NULL); + + /* add the modalias */ + fu_device_add_instance_strsafe(device, + "MODALIAS", + g_udev_device_get_property(priv->udev_device, "MODALIAS")); + fu_device_build_instance_id_quirk(device, NULL, subsystem, "MODALIAS", NULL); + + /* add subsystem to match in plugins */ + if (subsystem != NULL) { + fu_device_add_instance_id_full(device, + subsystem, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + } + + /* add firmware_id */ + if (g_strcmp0(g_udev_device_get_subsystem(priv->udev_device), "serio") == 0) { + if (!fu_udev_device_probe_serio(self, error)) + return FALSE; + } + + /* determine if we're wired internally */ + parent_i2c = g_udev_device_get_parent_with_subsystem(priv->udev_device, "i2c", NULL); + if (parent_i2c != NULL) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); +#endif + + /* success */ + return TRUE; +} + +#ifdef HAVE_GUDEV +static gchar * +fu_udev_device_get_miscdev0(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + const gchar *fn; + g_autofree gchar *miscdir = NULL; + g_autoptr(GDir) dir = NULL; + + miscdir = g_build_filename(g_udev_device_get_sysfs_path(priv->udev_device), "misc", NULL); + dir = g_dir_open(miscdir, 0, NULL); + if (dir == NULL) + return NULL; + fn = g_dir_read_name(dir); + if (fn == NULL) + return NULL; + return g_strdup_printf("/dev/%s", fn); +} +#endif + +/** + * fu_udev_device_set_dev: + * @self: a #FuUdevDevice + * @udev_device: a #GUdevDevice + * + * Sets the #GUdevDevice. This may need to be used to replace the actual device + * used for reads and writes before the device is probed. + * + * Since: 1.6.2 + **/ +void +fu_udev_device_set_dev(FuUdevDevice *self, GUdevDevice *udev_device) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); +#ifdef HAVE_GUDEV + const gchar *summary; +#endif + + g_return_if_fail(FU_IS_UDEV_DEVICE(self)); + +#ifdef HAVE_GUDEV + /* the net subsystem is not a real hardware class */ + if (udev_device != NULL && + g_strcmp0(g_udev_device_get_subsystem(udev_device), "net") == 0) { + g_autoptr(GUdevDevice) udev_device_phys = NULL; + udev_device_phys = g_udev_device_get_parent(udev_device); + g_set_object(&priv->udev_device, udev_device_phys); + fu_device_set_metadata(FU_DEVICE(self), + "ParentSubsystem", + g_udev_device_get_subsystem(udev_device)); + } else { + g_set_object(&priv->udev_device, udev_device); + } +#else + g_set_object(&priv->udev_device, udev_device); +#endif + + /* set new device */ + if (priv->udev_device == NULL) + return; +#ifdef HAVE_GUDEV + fu_udev_device_set_subsystem(self, g_udev_device_get_subsystem(priv->udev_device)); + fu_udev_device_set_driver(self, g_udev_device_get_driver(priv->udev_device)); + fu_udev_device_set_device_file(self, g_udev_device_get_device_file(priv->udev_device)); + + /* so we can display something sensible for unclaimed devices */ + fu_device_set_backend_id(FU_DEVICE(self), g_udev_device_get_sysfs_path(priv->udev_device)); + + /* fall back to the first thing handled by misc drivers */ + if (priv->device_file == NULL) { + /* perhaps we should unconditionally fall back? or perhaps + * require FU_UDEV_DEVICE_FLAG_FALLBACK_MISC... */ + if (g_strcmp0(priv->subsystem, "serio") == 0) + priv->device_file = fu_udev_device_get_miscdev0(self); + if (priv->device_file != NULL) + g_debug("falling back to misc %s", priv->device_file); + } + + /* try to get one line summary */ + summary = g_udev_device_get_sysfs_attr(priv->udev_device, "description"); + if (summary == NULL) { + g_autoptr(GUdevDevice) parent = NULL; + parent = g_udev_device_get_parent(priv->udev_device); + if (parent != NULL) + summary = g_udev_device_get_sysfs_attr(parent, "description"); + } + if (summary != NULL) + fu_device_set_summary(FU_DEVICE(self), summary); +#endif +} + +/** + * fu_udev_device_get_slot_depth: + * @self: a #FuUdevDevice + * @subsystem: a subsystem + * + * Determine how far up a chain a given device is + * + * Returns: unsigned integer + * + * Since: 1.2.4 + **/ +guint +fu_udev_device_get_slot_depth(FuUdevDevice *self, const gchar *subsystem) +{ +#ifdef HAVE_GUDEV + 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); + } +#endif + return 0; +} + +static gboolean +fu_udev_device_unbind_driver(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUDEV + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GOutputStream) stream = NULL; + + /* is already unbound */ + fn = g_build_filename(g_udev_device_get_sysfs_path(priv->udev_device), + "driver", + "unbind", + NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) + return TRUE; + + /* write bus ID to file */ + if (!fu_udev_device_ensure_bind_id(self, error)) + return FALSE; + file = g_file_new_for_path(fn); + stream = + G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + if (stream == NULL) + return FALSE; + return g_output_stream_write_all(stream, + priv->bind_id, + strlen(priv->bind_id), + NULL, + NULL, + error); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "driver unbinding not supported"); + return FALSE; +#endif +} + +static gboolean +fu_udev_device_bind_driver(FuDevice *device, + const gchar *subsystem, + const gchar *driver, + GError **error) +{ +#ifdef HAVE_GUDEV + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *driver_safe = g_strdup(driver); + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GOutputStream) stream = NULL; + + /* copy the logic from modprobe */ + g_strdelimit(driver_safe, "-", '_'); + + /* driver exists */ + fn = g_strdup_printf("/sys/module/%s/drivers/%s:%s/bind", + driver_safe, + subsystem, + driver_safe); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot bind with %s:%s", + subsystem, + driver); + return FALSE; + } + + /* write bus ID to file */ + if (!fu_udev_device_ensure_bind_id(self, error)) + return FALSE; + if (priv->bind_id == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bind-id not set for subsystem %s", + priv->subsystem); + return FALSE; + } + file = g_file_new_for_path(fn); + stream = + G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + if (stream == NULL) + return FALSE; + return g_output_stream_write_all(stream, + priv->bind_id, + strlen(priv->bind_id), + NULL, + NULL, + error); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "driver binding not supported on Windows"); + return FALSE; +#endif +} + +static void +fu_udev_device_incorporate(FuDevice *self, FuDevice *donor) +{ + FuUdevDevice *uself = FU_UDEV_DEVICE(self); + FuUdevDevice *udonor = FU_UDEV_DEVICE(donor); + FuUdevDevicePrivate *priv = GET_PRIVATE(uself); + FuUdevDevicePrivate *priv_donor = GET_PRIVATE(udonor); + + g_return_if_fail(FU_IS_UDEV_DEVICE(self)); + g_return_if_fail(FU_IS_UDEV_DEVICE(donor)); + + fu_udev_device_set_dev(uself, fu_udev_device_get_dev(udonor)); + if (priv->device_file == NULL) { + fu_udev_device_set_subsystem(uself, fu_udev_device_get_subsystem(udonor)); + fu_udev_device_set_bind_id(uself, fu_udev_device_get_bind_id(udonor)); + fu_udev_device_set_device_file(uself, fu_udev_device_get_device_file(udonor)); + fu_udev_device_set_driver(uself, fu_udev_device_get_driver(udonor)); + } + if (priv->vendor == 0x0 && priv_donor->vendor != 0x0) + priv->vendor = priv_donor->vendor; + if (priv->model == 0x0 && priv_donor->model != 0x0) + priv->model = priv_donor->model; + if (priv->subsystem_vendor == 0x0 && priv_donor->subsystem_vendor != 0x0) + priv->subsystem_vendor = priv_donor->subsystem_vendor; + if (priv->subsystem_model == 0x0 && priv_donor->subsystem_model != 0x0) + priv->subsystem_model = priv_donor->subsystem_model; + if (priv->revision == 0x0 && priv_donor->revision != 0x0) + priv->revision = priv_donor->revision; +} + +/** + * 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 #FuUdevDevice + * + * 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 priv->subsystem; +} + +/** + * fu_udev_device_get_bind_id: + * @self: a #FuUdevDevice + * + * Gets the device ID used for binding the device, e.g. `pci:1:2:3` + * + * Returns: a bind_id, or NULL if unset or invalid + * + * Since: 1.7.2 + **/ +const gchar * +fu_udev_device_get_bind_id(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + fu_udev_device_ensure_bind_id(self, NULL); + return priv->bind_id; +} + +/** + * fu_udev_device_get_driver: + * @self: a #FuUdevDevice + * + * Gets the device driver, e.g. `psmouse`. + * + * Returns: a subsystem, or NULL if unset or invalid + * + * Since: 1.5.3 + **/ +const gchar * +fu_udev_device_get_driver(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + return priv->driver; +} + +/** + * fu_udev_device_get_device_file: + * @self: a #FuUdevDevice + * + * Gets the device node. + * + * Returns: a device file, or NULL if unset + * + * Since: 1.3.1 + **/ +const gchar * +fu_udev_device_get_device_file(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + return priv->device_file; +} + +/** + * fu_udev_device_get_sysfs_path: + * @self: a #FuUdevDevice + * + * 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) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + if (priv->udev_device != NULL) + return g_udev_device_get_sysfs_path(priv->udev_device); +#endif + return NULL; +} + +/** + * fu_udev_device_get_number: + * @self: a #FuUdevDevice + * + * Gets the device number, if any. + * + * Returns: integer, 0 if the data is unavailable, or %G_MAXUINT64 if the + * feature is not available + * + * Since: 1.5.0 + **/ +guint64 +fu_udev_device_get_number(FuUdevDevice *self) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), 0); + if (priv->udev_device != NULL) { + guint64 tmp = 0; + g_autoptr(GError) error_local = NULL; + if (!fu_strtoull(g_udev_device_get_number(priv->udev_device), + &tmp, + 0x0, + G_MAXUINT64, + &error_local)) { + g_warning("failed to convert udev number: %s", error_local->message); + return G_MAXUINT64; + } + return tmp; + } +#endif + return G_MAXUINT64; +} + +/** + * fu_udev_device_get_vendor: + * @self: a #FuUdevDevice + * + * 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 #FuUdevDevice + * + * 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_subsystem_vendor: + * @self: a #FuUdevDevice + * + * Gets the device subsystem vendor code. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.5.0 + **/ +guint16 +fu_udev_device_get_subsystem_vendor(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), 0x0000); + return priv->subsystem_vendor; +} + +/** + * fu_udev_device_get_subsystem_model: + * @self: a #FuUdevDevice + * + * Gets the device subsystem model code. + * + * Returns: a vendor code, or 0 if unset or invalid + * + * Since: 1.5.0 + **/ +guint16 +fu_udev_device_get_subsystem_model(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), 0x0000); + return priv->subsystem_model; +} + +/** + * fu_udev_device_get_revision: + * @self: a #FuUdevDevice + * + * 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; +} + +#ifdef HAVE_GUDEV +static gchar * +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 self and all parent devices */ + if (priv->subsystem != NULL) + g_string_append_printf(str, "%s,", priv->subsystem); + 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 g_string_free(str, FALSE); +} + +static gboolean +fu_udev_device_match_subsystem_devtype(GUdevDevice *udev_device, + const gchar *subsystem, + const gchar *devtype) +{ + if (subsystem != NULL) { + if (g_strcmp0(g_udev_device_get_subsystem(udev_device), subsystem) != 0) + return FALSE; + } + if (devtype != NULL) { + if (g_strcmp0(g_udev_device_get_devtype(udev_device), devtype) != 0) + return FALSE; + } + return TRUE; +} + +static GUdevDevice * +fu_udev_device_get_parent_with_subsystem_devtype(GUdevDevice *udev_device, + const gchar *subsystem, + const gchar *devtype) +{ + g_autoptr(GUdevDevice) udev_device_tmp = g_object_ref(udev_device); + while (udev_device_tmp != NULL) { + g_autoptr(GUdevDevice) parent = NULL; + if (fu_udev_device_match_subsystem_devtype(udev_device_tmp, subsystem, devtype)) + return g_object_ref(udev_device_tmp); + parent = g_udev_device_get_parent(udev_device_tmp); + g_set_object(&udev_device_tmp, parent); + } + return NULL; +} +#endif + +/** + * fu_udev_device_set_physical_id: + * @self: a #FuUdevDevice + * @subsystems: a subsystem string, e.g. `pci,usb,scsi:scsi_target` + * @error: (nullable): optional return location for an error + * + * 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. + * + * The devtype can also be specified for a specific device, which is useful when the + * subsystem alone is not enough to identify the physical device. e.g. ignoring the + * specific LUNs for a SCSI device. + * + * Returns: %TRUE if the physical device was set. + * + * Since: 1.1.2 + **/ +gboolean +fu_udev_device_set_physical_id(FuUdevDevice *self, const gchar *subsystems, GError **error) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + g_autofree gchar *physical_id = NULL; + g_autofree gchar *subsystem = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(subsystems != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* nothing to do */ + if (priv->udev_device == NULL) + return TRUE; + + /* look for each subsystem[:devtype] in turn */ + split = g_strsplit(subsystems, ",", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_auto(GStrv) subsys_devtype = g_strsplit(split[i], ":", 2); + + /* matching on devtype is optional */ + udev_device = fu_udev_device_get_parent_with_subsystem_devtype(priv->udev_device, + subsys_devtype[0], + subsys_devtype[1]); + if (udev_device != NULL) { + subsystem = g_strdup(subsys_devtype[0]); + break; + } + } + if (udev_device == NULL) { + g_autofree gchar *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 subsystems %s, only got %s", + subsystems, + 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, "mmc") == 0 || + g_strcmp0(subsystem, "i2c") == 0 || g_strcmp0(subsystem, "platform") == 0 || + g_strcmp0(subsystem, "scsi") == 0 || g_strcmp0(subsystem, "mtd") == 0 || + g_strcmp0(subsystem, "block") == 0 || g_strcmp0(subsystem, "gpio") == 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 if (g_strcmp0(subsystem, "tpm") == 0 || + g_strcmp0(subsystem, "drm_dp_aux_dev") == 0) { + tmp = g_udev_device_get_property(udev_device, "DEVNAME"); + if (tmp == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find DEVNAME"); + return FALSE; + } + physical_id = g_strdup_printf("DEVNAME=%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; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return FALSE; +#endif +} + +/** + * fu_udev_device_set_logical_id: + * @self: a #FuUdevDevice + * @subsystem: a subsystem string, e.g. `pci,usb` + * @error: (nullable): optional return location for an error + * + * Sets the logical ID from the device subsystem. Plugins should choose the + * subsystem that most relevant in the udev tree, for instance choosing `hid` + * over `usb` for a mouse device. + * + * Returns: %TRUE if the logical device was set. + * + * Since: 1.5.8 + **/ +gboolean +fu_udev_device_set_logical_id(FuUdevDevice *self, const gchar *subsystem, GError **error) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + g_autofree gchar *logical_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); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* nothing to do */ + if (priv->udev_device == NULL) + return TRUE; + + /* find correct device matching subsystem */ + if (g_strcmp0(priv->subsystem, subsystem) == 0) { + udev_device = g_object_ref(priv->udev_device); + } else { + udev_device = + g_udev_device_get_parent_with_subsystem(priv->udev_device, subsystem, NULL); + } + if (udev_device == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find device with subsystem %s", + subsystem); + return FALSE; + } + + /* query each subsystem */ + if (g_strcmp0(subsystem, "hid") == 0) { + tmp = g_udev_device_get_property(udev_device, "HID_UNIQ"); + if (tmp == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find HID_UNIQ"); + return FALSE; + } + logical_id = g_strdup_printf("HID_UNIQ=%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_logical_id(FU_DEVICE(self), logical_id); + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return FALSE; +#endif +} + +/** + * fu_udev_device_get_fd: + * @self: a #FuUdevDevice + * + * Gets the file descriptor if the device is open. + * + * Returns: positive integer, or -1 if the device is not open + * + * Since: 1.3.3 + **/ +gint +fu_udev_device_get_fd(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), -1); + return priv->fd; +} + +/** + * fu_udev_device_set_fd: + * @self: a #FuUdevDevice + * @fd: a valid file descriptor + * + * Replace the file descriptor to use when the device has already been opened. + * This object will automatically close() @fd when fu_device_close() is called. + * + * Since: 1.3.3 + **/ +void +fu_udev_device_set_fd(FuUdevDevice *self, gint fd) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_UDEV_DEVICE(self)); + if (priv->fd > 0) + close(priv->fd); + priv->fd = fd; +} + +/** + * fu_udev_device_set_flags: + * @self: a #FuUdevDevice + * @flags: udev device flags, e.g. %FU_UDEV_DEVICE_FLAG_OPEN_READ + * + * Sets the parameters to use when opening the device. + * + * For example %FU_UDEV_DEVICE_FLAG_OPEN_READ means that fu_device_open() + * would use `O_RDONLY` rather than `O_RDWR` which is the default. + * + * Since: 1.3.6 + **/ +void +fu_udev_device_set_flags(FuUdevDevice *self, FuUdevDeviceFlags flags) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_UDEV_DEVICE(self)); + priv->flags = flags; + +#ifdef HAVE_GUDEV + /* overwrite */ + if (flags & FU_UDEV_DEVICE_FLAG_USE_CONFIG) { + g_free(priv->device_file); + priv->device_file = + g_build_filename(g_udev_device_get_sysfs_path(priv->udev_device), + "config", + NULL); + } +#endif +} + +static gboolean +fu_udev_device_open(FuDevice *device, GError **error) +{ + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* open device */ + if (priv->device_file != NULL && priv->flags != FU_UDEV_DEVICE_FLAG_NONE) { + gint flags; + if (priv->flags & FU_UDEV_DEVICE_FLAG_OPEN_READ && + priv->flags & FU_UDEV_DEVICE_FLAG_OPEN_WRITE) { + flags = O_RDWR; + } else if (priv->flags & FU_UDEV_DEVICE_FLAG_OPEN_WRITE) { + flags = O_WRONLY; + } else { + flags = O_RDONLY; + } +#ifdef O_NONBLOCK + if (priv->flags & FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK) + flags |= O_NONBLOCK; +#endif +#ifdef O_SYNC + if (priv->flags & FU_UDEV_DEVICE_FLAG_OPEN_SYNC) + flags |= O_SYNC; +#endif + priv->fd = g_open(priv->device_file, flags, 0); + if (priv->fd < 0) { + g_set_error(error, + G_IO_ERROR, +#ifdef HAVE_ERRNO_H + g_io_error_from_errno(errno), +#else + G_IO_ERROR_FAILED, +#endif + "failed to open %s: %s", + priv->device_file, + strerror(errno)); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_udev_device_rescan(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUDEV + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + const gchar *sysfs_path; + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + g_autoptr(GUdevDevice) udev_device = NULL; + + /* never set */ + if (priv->udev_device == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "rescan with no previous device"); + return FALSE; + } + sysfs_path = g_udev_device_get_sysfs_path(priv->udev_device); + udev_device = g_udev_client_query_by_sysfs_path(udev_client, sysfs_path); + if (udev_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "rescan could not find device %s", + sysfs_path); + return FALSE; + } + fu_udev_device_set_dev(self, udev_device); + fu_device_probe_invalidate(device); +#endif + return fu_device_probe(device, error); +} + +static gboolean +fu_udev_device_close(FuDevice *device, GError **error) +{ + FuUdevDevice *self = FU_UDEV_DEVICE(device); + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + /* close device */ + if (priv->fd > 0) { + if (!g_close(priv->fd, error)) + return FALSE; + priv->fd = 0; + } + + /* success */ + return TRUE; +} + +/** + * fu_udev_device_ioctl: + * @self: a #FuUdevDevice + * @request: request number + * @buf: a buffer to use, which *must* be large enough for the request + * @rc: (out) (nullable): the raw return value from the ioctl + * @timeout: timeout in ms for the retry action, see %FU_UDEV_DEVICE_FLAG_IOCTL_RETRY + * @error: (nullable): optional return location for an error + * + * Control a device using a low-level request. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_udev_device_ioctl(FuUdevDevice *self, + gulong request, + guint8 *buf, + gint *rc, + guint timeout, + GError **error) +{ +#ifdef HAVE_IOCTL_H + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + gint rc_tmp; + g_autoptr(GTimer) timer = g_timer_new(); + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(request != 0x0, FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not open! */ + if (priv->fd == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s [%s] has not been opened", + fu_device_get_id(FU_DEVICE(self)), + fu_device_get_name(FU_DEVICE(self))); + return FALSE; + } + + /* poll if required up to the timeout */ + do { + rc_tmp = ioctl(priv->fd, request, buf); + if (rc_tmp >= 0) + break; + } while ((priv->flags & FU_UDEV_DEVICE_FLAG_IOCTL_RETRY) && + (errno == EINTR || errno == EAGAIN) && + g_timer_elapsed(timer, NULL) < timeout * 1000.f); + if (rc != NULL) + *rc = rc_tmp; + if (rc_tmp < 0) { +#ifdef HAVE_ERRNO_H + if (errno == EPERM) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "permission denied"); + return FALSE; + } + if (errno == ENOTTY) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "permission denied"); + return FALSE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "ioctl error: %s [%i]", + strerror(errno), + errno); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified ioctl error"); +#endif + return FALSE; + } + return TRUE; +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +/** + * fu_udev_device_pread: + * @self: a #FuUdevDevice + * @port: offset address + * @buf: (in): data + * @bufsz: size of @buf + * @error: (nullable): optional return location for an error + * + * Read a buffer from a file descriptor at a given offset. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_udev_device_pread(FuUdevDevice *self, goffset port, guint8 *buf, gsize bufsz, GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not open! */ + if (priv->fd == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s [%s] has not been opened", + fu_device_get_id(FU_DEVICE(self)), + fu_device_get_name(FU_DEVICE(self))); + return FALSE; + } + +#ifdef HAVE_PWRITE + if (pread(priv->fd, buf, bufsz, port) != (gssize)bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to read from port 0x%04x: %s", + (guint)port, + strerror(errno)); + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as pread() is unavailable"); + return FALSE; +#endif +} + +/** + * fu_udev_device_seek: + * @self: a #FuUdevDevice + * @offset: offset address + * @error: (nullable): optional return location for an error + * + * Seeks a file descriptor to a given offset. + * + * Returns: %TRUE for success + * + * Since: 1.7.2 + **/ +gboolean +fu_udev_device_seek(FuUdevDevice *self, goffset offset, GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not open! */ + if (priv->fd == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s [%s] has not been opened", + fu_device_get_id(FU_DEVICE(self)), + fu_device_get_name(FU_DEVICE(self))); + return FALSE; + } + +#ifdef HAVE_PWRITE + if (lseek(priv->fd, offset, SEEK_SET) < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to seek to 0x%04x: %s", + (guint)offset, + strerror(errno)); + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as lseek() is unavailable"); + return FALSE; +#endif +} + +/** + * fu_udev_device_pwrite: + * @self: a #FuUdevDevice + * @port: offset address + * @buf: (out): data + * @bufsz: size of @data + * @error: (nullable): optional return location for an error + * + * Write a buffer to a file descriptor at a given offset. + * + * Returns: %TRUE for success + * + * Since: 1.8.2 + **/ +gboolean +fu_udev_device_pwrite(FuUdevDevice *self, + goffset port, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not open! */ + if (priv->fd == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s [%s] has not been opened", + fu_device_get_id(FU_DEVICE(self)), + fu_device_get_name(FU_DEVICE(self))); + return FALSE; + } + +#ifdef HAVE_PWRITE + if (pwrite(priv->fd, buf, bufsz, port) != (gssize)bufsz) { + 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; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as pwrite() is unavailable"); + return FALSE; +#endif +} + +/** + * fu_udev_device_get_parent_name + * @self: a #FuUdevDevice + * + * Returns the name of the direct ancestor of this device + * + * Returns: string or NULL if unset or invalid + * + * Since: 1.4.5 + **/ +gchar * +fu_udev_device_get_parent_name(FuUdevDevice *self) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GUdevDevice) parent = NULL; + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + + parent = g_udev_device_get_parent(priv->udev_device); + return parent == NULL ? NULL : g_strdup(g_udev_device_get_name(parent)); +#else + return NULL; +#endif +} + +/** + * fu_udev_device_get_sysfs_attr: + * @self: a #FuUdevDevice + * @attr: name of attribute to get + * @error: (nullable): optional return location for an error + * + * Reads an arbitrary sysfs attribute 'attr' associated with UDEV device + * + * Returns: string or NULL + * + * Since: 1.4.5 + **/ +const gchar * +fu_udev_device_get_sysfs_attr(FuUdevDevice *self, const gchar *attr, GError **error) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + const gchar *result; + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + g_return_val_if_fail(attr != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* nothing to do */ + if (priv->udev_device == NULL) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "not yet initialized"); + return NULL; + } + result = g_udev_device_get_sysfs_attr(priv->udev_device, attr); + if (result == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "attribute %s returned no data", + attr); + return NULL; + } + + return result; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "getting attributes is not supported as no GUdev support"); + return NULL; +#endif +} + +/** + * fu_udev_device_get_sysfs_attr_uint64: + * @self: a #FuUdevDevice + * @attr: name of attribute to get + * @value: (out) (optional): value to return + * @error: (nullable): optional return location for an error + * + * Reads an arbitrary sysfs attribute 'attr' associated with UDEV device as a uint64. + * + * Returns: %TRUE for success + * + * Since: 1.7.2 + **/ +gboolean +fu_udev_device_get_sysfs_attr_uint64(FuUdevDevice *self, + const gchar *attr, + guint64 *value, + GError **error) +{ + const gchar *tmp; + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(attr != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + tmp = fu_udev_device_get_sysfs_attr(self, attr, error); + if (tmp == NULL) + return FALSE; + return fu_strtoull(tmp, value, 0, G_MAXUINT64, error); +} + +/** + * fu_udev_device_write_sysfs: + * @self: a #FuUdevDevice + * @attribute: sysfs attribute name + * @val: data to write into the attribute + * @error: (nullable): optional return location for an error + * + * Writes data into a sysfs attribute + * + * Returns: %TRUE for success + * + * Since: 1.4.5 + **/ +gboolean +fu_udev_device_write_sysfs(FuUdevDevice *self, + const gchar *attribute, + const gchar *val, + GError **error) +{ +#ifdef __linux__ + ssize_t n; + int r; + int fd; + g_autofree gchar *path = NULL; + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(attribute != NULL, FALSE); + g_return_val_if_fail(val != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + path = g_build_filename(fu_udev_device_get_sysfs_path(self), attribute, NULL); + fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) { + g_set_error(error, + G_IO_ERROR, + g_io_error_from_errno(errno), + "could not open %s: %s", + path, + g_strerror(errno)); + return FALSE; + } + + do { + n = write(fd, val, strlen(val)); + if (n < 1 && errno != EINTR) { + g_set_error(error, + G_IO_ERROR, + g_io_error_from_errno(errno), + "could not write to %s: %s", + path, + g_strerror(errno)); + (void)close(fd); + return FALSE; + } + } while (n < 1); + + r = close(fd); + if (r < 0 && errno != EINTR) { + g_set_error(error, + G_IO_ERROR, + g_io_error_from_errno(errno), + "could not close %s: %s", + path, + g_strerror(errno)); + return FALSE; + } + + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "sysfs attributes not supported on Windows"); + return FALSE; +#endif +} + +/** + * fu_udev_device_get_devtype + * @self: a #FuUdevDevice + * + * Returns the Udev device type + * + * Returns: device type specified in the uevent + * + * Since: 1.4.5 + **/ +const gchar * +fu_udev_device_get_devtype(FuUdevDevice *self) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + + return g_udev_device_get_devtype(priv->udev_device); +#else + return NULL; +#endif +} + +/** + * fu_udev_device_get_siblings_with_subsystem + * @self: a #FuUdevDevice + * @subsystem: the name of a udev subsystem + * + * Get a list of devices that are siblings of self and have the + * provided subsystem. + * + * Returns: (element-type FuUdevDevice) (transfer full): devices + * + * Since: 1.6.0 + */ +GPtrArray * +fu_udev_device_get_siblings_with_subsystem(FuUdevDevice *self, const gchar *const subsystem) +{ + g_autoptr(GPtrArray) out = g_ptr_array_new_with_free_func(g_object_unref); + +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GUdevDevice) udev_parent = g_udev_device_get_parent(priv->udev_device); + const gchar *udev_parent_path = g_udev_device_get_sysfs_path(udev_parent); + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + + g_autoptr(GList) enumerated = g_udev_client_query_by_subsystem(udev_client, subsystem); + for (GList *element = enumerated; element != NULL; element = element->next) { + g_autoptr(GUdevDevice) enumerated_device = element->data; + g_autoptr(GUdevDevice) enumerated_parent = NULL; + const gchar *enumerated_parent_path; + + /* get parent, if it exists */ + enumerated_parent = g_udev_device_get_parent(enumerated_device); + if (enumerated_parent == NULL) + break; + enumerated_parent_path = g_udev_device_get_sysfs_path(enumerated_parent); + + /* if the sysfs path of self's parent is the same as that of the + * located device's parent, they are siblings */ + if (g_strcmp0(udev_parent_path, enumerated_parent_path) == 0) { + FuUdevDevice *dev = + fu_udev_device_new(fu_device_get_context(FU_DEVICE(self)), + g_steal_pointer(&enumerated_device)); + g_ptr_array_add(out, dev); + } + } +#endif + + return g_steal_pointer(&out); +} + +/** + * fu_udev_device_get_parent_with_subsystem + * @self: a #FuUdevDevice + * @subsystem: the name of a udev subsystem + * + * Get the device that is a parent of self and has the provided subsystem. + * + * Returns: (transfer full): device, or %NULL + * + * Since: 1.7.6 + */ +FuUdevDevice * +fu_udev_device_get_parent_with_subsystem(FuUdevDevice *self, const gchar *subsystem) +{ +#ifdef HAVE_GUDEV + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GUdevDevice) device_tmp = NULL; + + device_tmp = g_udev_device_get_parent_with_subsystem(priv->udev_device, subsystem, NULL); + if (device_tmp == NULL) + return NULL; + return fu_udev_device_new(fu_device_get_context(FU_DEVICE(self)), device_tmp); +#else + return NULL; +#endif +} + +/** + * fu_udev_device_get_children_with_subsystem + * @self: a #FuUdevDevice + * @subsystem: the name of a udev subsystem + * + * Get a list of devices that are children of self and have the + * provided subsystem. + * + * Returns: (element-type FuUdevDevice) (transfer full): devices + * + * Since: 1.6.2 + */ +GPtrArray * +fu_udev_device_get_children_with_subsystem(FuUdevDevice *self, const gchar *const subsystem) +{ + g_autoptr(GPtrArray) out = g_ptr_array_new_with_free_func(g_object_unref); + +#ifdef HAVE_GUDEV + const gchar *self_path = fu_udev_device_get_sysfs_path(self); + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + + g_autoptr(GList) enumerated = g_udev_client_query_by_subsystem(udev_client, subsystem); + for (GList *element = enumerated; element != NULL; element = element->next) { + g_autoptr(GUdevDevice) enumerated_device = element->data; + g_autoptr(GUdevDevice) enumerated_parent = NULL; + const gchar *enumerated_parent_path; + + /* get parent, if it exists */ + enumerated_parent = g_udev_device_get_parent(enumerated_device); + if (enumerated_parent == NULL) + break; + enumerated_parent_path = g_udev_device_get_sysfs_path(enumerated_parent); + + /* enumerated device is a child of self if its parent is the + * same as self */ + if (g_strcmp0(self_path, enumerated_parent_path) == 0) { + FuUdevDevice *dev = + fu_udev_device_new(fu_device_get_context(FU_DEVICE(self)), + g_steal_pointer(&enumerated_device)); + g_ptr_array_add(out, dev); + } + } +#endif + + return g_steal_pointer(&out); +} + +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; + case PROP_SUBSYSTEM: + g_value_set_string(value, priv->subsystem); + break; + case PROP_BIND_ID: + g_value_set_string(value, priv->bind_id); + break; + case PROP_DRIVER: + g_value_set_string(value, priv->driver); + break; + case PROP_DEVICE_FILE: + g_value_set_string(value, priv->device_file); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +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; + case PROP_SUBSYSTEM: + fu_udev_device_set_subsystem(self, g_value_get_string(value)); + break; + case PROP_BIND_ID: + fu_udev_device_set_bind_id(self, g_value_get_string(value)); + break; + case PROP_DRIVER: + fu_udev_device_set_driver(self, g_value_get_string(value)); + break; + case PROP_DEVICE_FILE: + fu_udev_device_set_device_file(self, g_value_get_string(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); + + g_free(priv->subsystem); + g_free(priv->bind_id); + g_free(priv->driver); + g_free(priv->device_file); + if (priv->udev_device != NULL) + g_object_unref(priv->udev_device); + if (priv->fd > 0) + g_close(priv->fd, NULL); + + G_OBJECT_CLASS(fu_udev_device_parent_class)->finalize(object); +} + +static void +fu_udev_device_init(FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + priv->flags = FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE; + fu_device_set_acquiesce_delay(FU_DEVICE(self), 2500); +} + +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->rescan = fu_udev_device_rescan; + device_class->incorporate = fu_udev_device_incorporate; + device_class->open = fu_udev_device_open; + device_class->close = fu_udev_device_close; + device_class->to_string = fu_udev_device_to_string; + device_class->bind_driver = fu_udev_device_bind_driver; + device_class->unbind_driver = fu_udev_device_unbind_driver; + + /** + * FuUdevDevice::changed: + * @self: the #FuUdevDevice instance that emitted the signal + * + * The ::changed signal is emitted when the low-level GUdevDevice has changed. + * + * Since: 1.1.2 + **/ + 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); + + /** + * FuUdevDevice:udev-device: + * + * The low-level GUdevDevice. + * + * Since: 1.1.2 + */ + 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); + + /** + * FuUdevDevice:subsystem: + * + * The device subsystem. + * + * Since: 1.1.2 + */ + pspec = g_param_spec_string("subsystem", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_SUBSYSTEM, pspec); + + /** + * FuUdevDevice:bind-id: + * + * The bind ID to use when binding a new driver. + * + * Since: 1.7.2 + */ + pspec = g_param_spec_string("bind-id", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BIND_ID, pspec); + + /** + * FuUdevDevice:driver: + * + * The driver being used for the device. + * + * Since: 1.5.3 + */ + pspec = g_param_spec_string("driver", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_DRIVER, pspec); + + /** + * FuUdevDevice:device-file: + * + * The low level file to use for device access. + * + * Since: 1.3.1 + */ + pspec = g_param_spec_string("device-file", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_DEVICE_FILE, pspec); +} + +/** + * fu_udev_device_new: + * @ctx: (nullable): a #FuContext + * @udev_device: a #GUdevDevice + * + * Creates a new #FuUdevDevice. + * + * Returns: (transfer full): a #FuUdevDevice + * + * Since: 1.8.2 + **/ +FuUdevDevice * +fu_udev_device_new(FuContext *ctx, GUdevDevice *udev_device) +{ + return g_object_new(FU_TYPE_UDEV_DEVICE, "context", ctx, "udev-device", udev_device, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-udev-device.h b/fwupd-1.8.6/libfwupdplugin/fu-udev-device.h new file mode 100644 index 0000000000000000000000000000000000000000..4804f94994c8df33a682ea4f20c40f7f9e945581 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-udev-device.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#ifdef HAVE_GUDEV +#include +#else +#define G_UDEV_TYPE_DEVICE G_TYPE_OBJECT +#define GUdevDevice GObject +#endif + +#include "fu-plugin.h" + +#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; + gpointer __reserved[31]; +}; + +/** + * FuUdevDeviceFlags: + * @FU_UDEV_DEVICE_FLAG_NONE: No flags set + * @FU_UDEV_DEVICE_FLAG_OPEN_READ: Open the device read-only + * @FU_UDEV_DEVICE_FLAG_OPEN_WRITE: Open the device write-only + * @FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT: Get the vendor ID from a parent or grandparent + * @FU_UDEV_DEVICE_FLAG_USE_CONFIG: Read and write from the device config + * @FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK: Open nonblocking, e.g. O_NONBLOCK + * @FU_UDEV_DEVICE_FLAG_OPEN_SYNC: Open sync, e.g. O_SYNC + * @FU_UDEV_DEVICE_FLAG_IOCTL_RETRY: Retry the ioctl() call when required + * + * Flags used when opening the device using fu_device_open(). + **/ +typedef enum { + FU_UDEV_DEVICE_FLAG_NONE = 0, + FU_UDEV_DEVICE_FLAG_OPEN_READ = 1 << 0, + FU_UDEV_DEVICE_FLAG_OPEN_WRITE = 1 << 1, + FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT = 1 << 2, + FU_UDEV_DEVICE_FLAG_USE_CONFIG = 1 << 3, + FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK = 1 << 4, + FU_UDEV_DEVICE_FLAG_OPEN_SYNC = 1 << 5, + FU_UDEV_DEVICE_FLAG_IOCTL_RETRY = 1 << 6, + /*< private >*/ + FU_UDEV_DEVICE_FLAG_LAST +} FuUdevDeviceFlags; + +FuUdevDevice * +fu_udev_device_new(FuContext *ctx, GUdevDevice *udev_device); +GUdevDevice * +fu_udev_device_get_dev(FuUdevDevice *self); +void +fu_udev_device_set_dev(FuUdevDevice *self, GUdevDevice *udev_device); +const gchar * +fu_udev_device_get_device_file(FuUdevDevice *self); +const gchar * +fu_udev_device_get_sysfs_path(FuUdevDevice *self); +const gchar * +fu_udev_device_get_subsystem(FuUdevDevice *self); +const gchar * +fu_udev_device_get_bind_id(FuUdevDevice *self); +void +fu_udev_device_set_bind_id(FuUdevDevice *self, const gchar *bind_id); +const gchar * +fu_udev_device_get_driver(FuUdevDevice *self); +guint16 +fu_udev_device_get_vendor(FuUdevDevice *self); +guint16 +fu_udev_device_get_model(FuUdevDevice *self); +guint16 +fu_udev_device_get_subsystem_vendor(FuUdevDevice *self); +guint16 +fu_udev_device_get_subsystem_model(FuUdevDevice *self); +guint8 +fu_udev_device_get_revision(FuUdevDevice *self); +guint64 +fu_udev_device_get_number(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 *subsystems, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_udev_device_set_logical_id(FuUdevDevice *self, + const gchar *subsystem, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_udev_device_set_flags(FuUdevDevice *self, FuUdevDeviceFlags flags); + +gint +fu_udev_device_get_fd(FuUdevDevice *self); +void +fu_udev_device_set_fd(FuUdevDevice *self, gint fd); +gboolean +fu_udev_device_ioctl(FuUdevDevice *self, + gulong request, + guint8 *buf, + gint *rc, + guint timeout, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_udev_device_pwrite(FuUdevDevice *self, + goffset port, + const guint8 *buf, + gsize bufsz, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_udev_device_pread(FuUdevDevice *self, goffset port, guint8 *buf, gsize bufsz, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_udev_device_seek(FuUdevDevice *self, goffset offset, GError **error) G_GNUC_WARN_UNUSED_RESULT; +const gchar * +fu_udev_device_get_sysfs_attr(FuUdevDevice *self, const gchar *attr, GError **error); +gboolean +fu_udev_device_get_sysfs_attr_uint64(FuUdevDevice *self, + const gchar *attr, + guint64 *value, + GError **error); +gchar * +fu_udev_device_get_parent_name(FuUdevDevice *self); + +gboolean +fu_udev_device_write_sysfs(FuUdevDevice *self, + const gchar *attribute, + const gchar *val, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +const gchar * +fu_udev_device_get_devtype(FuUdevDevice *self); +GPtrArray * +fu_udev_device_get_siblings_with_subsystem(FuUdevDevice *self, const gchar *subsystem); +GPtrArray * +fu_udev_device_get_children_with_subsystem(FuUdevDevice *self, const gchar *subsystem); +FuUdevDevice * +fu_udev_device_get_parent_with_subsystem(FuUdevDevice *self, const gchar *subsystem); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ds20.c b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ds20.c new file mode 100644 index 0000000000000000000000000000000000000000..eafc7630b94b8ddb00e71f5a7f5fc8d10e8a0327 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ds20.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-dump.h" +#include "fu-mem.h" +#include "fu-usb-device-ds20.h" + +/** + * FuUsbDeviceDs20: + * + * A USB DS20 BOS descriptor. + * + * See also: [class@FuUsbDevice] + */ + +typedef struct { + guint32 version_lowest; +} FuUsbDeviceDs20Private; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUsbDeviceDs20, fu_usb_device_ds20, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_usb_device_ds20_get_instance_private(o)) + +typedef struct __attribute__((packed)) { + guint32 platform_ver; + guint16 total_length; + guint8 vendor_code; + guint8 alt_code; +} FuUsbDeviceDs20Item; + +/** + * fu_usb_device_ds20_set_version_lowest: + * @self: a #FuUsbDeviceDs20 + * @version_lowest: version number + * + * Sets the lowest possible `platform_ver` for a DS20 descriptor. + * + * Since: 1.8.5 + **/ +void +fu_usb_device_ds20_set_version_lowest(FuUsbDeviceDs20 *self, guint32 version_lowest) +{ + FuUsbDeviceDs20Private *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_USB_DEVICE_DS20(self)); + priv->version_lowest = version_lowest; +} + +/** + * fu_usb_device_ds20_apply_to_device: + * @self: a #FuUsbDeviceDs20 + * @device: a #FuUsbDevice + * @error: (nullable): optional return location for an error + * + * Sets the DS20 descriptor onto @device. + * + * Returns: %TRUE for success + * + * Since: 1.8.5 + **/ +gboolean +fu_usb_device_ds20_apply_to_device(FuUsbDeviceDs20 *self, FuUsbDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuUsbDeviceDs20Class *klass = FU_USB_DEVICE_DS20_GET_CLASS(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(device); + gsize actual_length = 0; + gsize total_length = fu_firmware_get_size(FU_FIRMWARE(self)); + guint8 vendor_code = fu_firmware_get_idx(FU_FIRMWARE(self)); + g_autofree guint8 *buf = g_malloc0(total_length); + g_autoptr(GBytes) blob = NULL; + + g_return_val_if_fail(FU_IS_USB_DEVICE_DS20(self), FALSE); + g_return_val_if_fail(FU_IS_USB_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + 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, + vendor_code, /* bRequest */ + 0x0, /* wValue */ + 0x07, /* wIndex */ + buf, + total_length, + &actual_length, + 500, + NULL, /* cancellable */ + error)) { + g_prefix_error(error, "requested vendor code 0x%02x: ", vendor_code); + return FALSE; + } + if (total_length != actual_length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected 0x%x bytes from vendor code 0x%02x, but got 0x%x", + (guint)total_length, + vendor_code, + (guint)actual_length); + return FALSE; + } + + /* debug */ + if (g_getenv("FWUPD_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "PlatformCapabilityOs20", buf, actual_length); + + /* FuUsbDeviceDs20->parse */ + blob = g_bytes_new_take(g_steal_pointer(&buf), actual_length); + return klass->parse(self, blob, device, error); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "GUsb support is unavailable"); + return FALSE; +#endif +} + +static gboolean +fu_usb_device_ds20_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + fwupd_guid_t guid = {0x0}; + g_autofree gchar *guid_str = NULL; + + /* parse GUID */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + 0x1, /* src */ + sizeof(guid), + error)) + return FALSE; + + /* matches the correct UUID */ + guid_str = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + if (g_strcmp0(guid_str, fu_firmware_get_id(firmware)) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid UUID for DS20, expected %s", + fu_firmware_get_id(firmware)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gint +fu_usb_device_ds20_sort_by_platform_ver_cb(gconstpointer a, gconstpointer b) +{ + FuUsbDeviceDs20Item *ds1 = *((FuUsbDeviceDs20Item **)a); + FuUsbDeviceDs20Item *ds2 = *((FuUsbDeviceDs20Item **)b); + if (ds1->platform_ver < ds2->platform_ver) + return -1; + if (ds1->platform_ver > ds2->platform_ver) + return 1; + return 0; +} + +static gboolean +fu_usb_device_ds20_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuUsbDeviceDs20 *self = FU_USB_DEVICE_DS20(firmware); + FuUsbDeviceDs20Private *priv = GET_PRIVATE(self); + const guint8 *buf; + gsize bufsz = 0; + guint version_lowest = fu_firmware_get_version_raw(firmware); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) dsinfos = g_ptr_array_new_with_free_func(g_free); + + /* cut out the data */ + blob = fu_bytes_new_offset(fw, + 1 + sizeof(fwupd_guid_t), + g_bytes_get_size(fw) - (1 + sizeof(fwupd_guid_t)), + error); + if (blob == NULL) + return FALSE; + buf = g_bytes_get_data(blob, &bufsz); + for (gsize off = 0; off < bufsz; off += sizeof(FuUsbDeviceDs20Item)) { + FuUsbDeviceDs20Item *dsinfo = g_new0(FuUsbDeviceDs20Item, 1); + guint16 total_length = 0; + guint32 platform_ver = 0; + + g_ptr_array_add(dsinfos, dsinfo); + if (!fu_memread_uint32_safe(buf, + bufsz, + off + + G_STRUCT_OFFSET(FuUsbDeviceDs20Item, platform_ver), + &platform_ver, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + off + + G_STRUCT_OFFSET(FuUsbDeviceDs20Item, total_length), + &total_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + off + G_STRUCT_OFFSET(FuUsbDeviceDs20Item, vendor_code), + &dsinfo->vendor_code, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + off + G_STRUCT_OFFSET(FuUsbDeviceDs20Item, alt_code), + &dsinfo->alt_code, + error)) + return FALSE; + dsinfo->platform_ver = platform_ver; + dsinfo->total_length = total_length; + g_debug("PlatformVersion=0x%08x, TotalLength=0x%04x, VendorCode=0x%02x, " + "AltCode=0x%02x", + dsinfo->platform_ver, + dsinfo->total_length, + dsinfo->vendor_code, + dsinfo->alt_code); + } + + /* sort by platform_ver, highest first */ + g_ptr_array_sort(dsinfos, fu_usb_device_ds20_sort_by_platform_ver_cb); + + /* find the newest info that's not newer than the lowest version */ + for (guint i = 0; i < dsinfos->len; i++) { + FuUsbDeviceDs20Item *dsinfo = g_ptr_array_index(dsinfos, i); + + /* not valid */ + if (dsinfo->platform_ver == 0x0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "invalid platform version 0x%08x", + dsinfo->platform_ver); + return FALSE; + } + if (dsinfo->platform_ver < priv->version_lowest) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "invalid platform version 0x%08x, expected >= 0x%08x", + dsinfo->platform_ver, + priv->version_lowest); + return FALSE; + } + + /* dwVersion is effectively the minimum version */ + if (dsinfo->platform_ver <= version_lowest) { + fu_firmware_set_size(firmware, dsinfo->total_length); + fu_firmware_set_idx(firmware, dsinfo->vendor_code); + return TRUE; + } + } + + /* failed */ + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported platform version"); + return FALSE; +} + +static GBytes * +fu_usb_device_ds20_write(FuFirmware *firmware, GError **error) +{ + fwupd_guid_t guid = {0x0}; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* bReserved */ + fu_byte_array_append_uint8(buf, 0x0); + + /* PlatformCapabilityUUID */ + if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return NULL; + g_byte_array_append(buf, (const guint8 *)&guid, sizeof(guid)); + + /* CapabilityData */ + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, fu_firmware_get_size(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, fu_firmware_get_idx(firmware)); + fu_byte_array_append_uint8(buf, 0x0); /* AltCode */ + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_usb_device_ds20_init(FuUsbDeviceDs20 *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); +} + +static void +fu_usb_device_ds20_class_init(FuUsbDeviceDs20Class *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_usb_device_ds20_check_magic; + klass_firmware->parse = fu_usb_device_ds20_parse; + klass_firmware->write = fu_usb_device_ds20_write; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ds20.h b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ds20.h new file mode 100644 index 0000000000000000000000000000000000000000..34fb1ee698ef0c1c99f4aa6b280d0393ec0d5189 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ds20.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" +#include "fu-usb-device.h" + +#define FU_TYPE_USB_DEVICE_DS20 (fu_usb_device_ds20_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuUsbDeviceDs20, fu_usb_device_ds20, FU, USB_DEVICE_DS20, FuFirmware) + +struct _FuUsbDeviceDs20Class { + FuFirmwareClass parent_class; + gboolean (*parse)(FuUsbDeviceDs20 *self, GBytes *blob, FuUsbDevice *device, GError **error) + G_GNUC_WARN_UNUSED_RESULT; +}; + +void +fu_usb_device_ds20_set_version_lowest(FuUsbDeviceDs20 *self, guint32 version_lowest); +gboolean +fu_usb_device_ds20_apply_to_device(FuUsbDeviceDs20 *self, FuUsbDevice *device, GError **error); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-fw-ds20.c b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-fw-ds20.c new file mode 100644 index 0000000000000000000000000000000000000000..583aacf3e9a954e01824054c6a7e997fae4d6058 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-fw-ds20.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-device-private.h" +#include "fu-string.h" +#include "fu-usb-device-fw-ds20.h" +#include "fu-version.h" + +struct _FuUsbDeviceFwDs20 { + FuUsbDeviceDs20 parent_instance; +}; + +G_DEFINE_TYPE(FuUsbDeviceFwDs20, fu_usb_device_fw_ds20, FU_TYPE_USB_DEVICE_DS20) + +#define DS20_VERSION_LOWEST ((1u << 16) | (8u << 8) | 5u) +#define DS20_VERSION_CURRENT \ + ((((guint32)FU_MAJOR_VERSION) << 16) | (((guint32)FU_MINOR_VERSION) << 8) | \ + ((guint)FU_MICRO_VERSION)) + +static gboolean +fu_usb_device_fw_ds20_parse(FuUsbDeviceDs20 *self, + GBytes *blob, + FuUsbDevice *device, + GError **error) +{ + gsize bufsz = 0; + gsize bufsz_safe = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + g_auto(GStrv) lines = NULL; + + /* only accept Linux line-endings */ + if (g_strstr_len((const gchar *)buf, bufsz, "\r") != NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Windows line endings are not supported"); + return FALSE; + } + + /* find the first NUL, if one exists */ + for (gsize i = 1; i < bufsz; i++) { + if (buf[i] == '\0') { + bufsz_safe = i - 1; + break; + } + } + + /* no NUL is unexpected, but fine */ + if (bufsz == 0) + bufsz_safe = bufsz; + + if (!g_utf8_validate((const gchar *)buf, (gssize)bufsz_safe, NULL)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "DS20 descriptor is not valid UTF-8"); + return FALSE; + } + + /* add payload for ->export() */ + fu_firmware_set_bytes(FU_FIRMWARE(self), blob); + + /* split into lines */ + lines = fu_strsplit((const gchar *)buf, bufsz_safe, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + g_auto(GStrv) kv = NULL; + if (lines[i][0] == '\0') + continue; + kv = g_strsplit(lines[i], "=", 2); + if (g_strv_length(kv) < 2) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected key=value for '%s'", + lines[i]); + return FALSE; + } + /* it's fine to be strict here, as we checked the fwupd version was new enough in + * FuUsbDeviceDs20Item */ + g_debug("setting ds20 device quirk '%s'='%s'", kv[0], kv[1]); + if (!fu_device_set_quirk_kv(FU_DEVICE(device), kv[0], kv[1], error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_usb_device_fw_ds20_class_init(FuUsbDeviceFwDs20Class *klass) +{ + FuUsbDeviceDs20Class *usb_device_ds20_klass = FU_USB_DEVICE_DS20_CLASS(klass); + usb_device_ds20_klass->parse = fu_usb_device_fw_ds20_parse; +} + +static void +fu_usb_device_fw_ds20_init(FuUsbDeviceFwDs20 *self) +{ + fu_firmware_set_version_raw(FU_FIRMWARE(self), DS20_VERSION_CURRENT); + fu_usb_device_ds20_set_version_lowest(FU_USB_DEVICE_DS20(self), DS20_VERSION_LOWEST); + fu_firmware_set_id(FU_FIRMWARE(self), "010aec63-f574-52cd-9dda-2852550d94f0"); +} + +/** + * fu_usb_device_fw_ds20_new: + * + * Creates a new #FuUsbDeviceFwDs20. + * + * Returns: (transfer full): a #FuFirmware + * + * Since: 1.8.5 + **/ +FuFirmware * +fu_usb_device_fw_ds20_new(void) +{ + return g_object_new(FU_TYPE_USB_DEVICE_FW_DS20, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-fw-ds20.h b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-fw-ds20.h new file mode 100644 index 0000000000000000000000000000000000000000..3cab06c7500bbfb4f2e51179017ea8987ae93199 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-fw-ds20.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-usb-device-ds20.h" + +#define FU_TYPE_USB_DEVICE_FW_DS20 (fu_usb_device_fw_ds20_get_type()) +G_DECLARE_FINAL_TYPE(FuUsbDeviceFwDs20, + fu_usb_device_fw_ds20, + FU, + USB_DEVICE_FW_DS20, + FuUsbDeviceDs20) + +FuFirmware * +fu_usb_device_fw_ds20_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ms-ds20.c b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ms-ds20.c new file mode 100644 index 0000000000000000000000000000000000000000..3ccae8f626b3e14bc638656c112d6a60f53d81b9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ms-ds20.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-mem.h" +#include "fu-usb-device-ms-ds20.h" + +struct _FuUsbDeviceMsDs20 { + FuUsbDeviceDs20 parent_instance; +}; + +G_DEFINE_TYPE(FuUsbDeviceMsDs20, fu_usb_device_ms_ds20, FU_TYPE_USB_DEVICE_DS20) + +#define USB_OS_20_SET_HEADER_DESCRIPTOR 0x00 +#define USB_OS_20_SUBSET_HEADER_CONFIGURATION 0x01 +#define USB_OS_20_SUBSET_HEADER_FUNCTION 0x02 +#define USB_OS_20_FEATURE_COMPATBLE_ID 0x03 +#define USB_OS_20_FEATURE_REG_PROPERTY 0x04 +#define USB_OS_20_FEATURE_MIN_RESUME_TIME 0x05 +#define USB_OS_20_FEATURE_MODEL_ID 0x06 +#define USB_OS_20_FEATURE_CCGP_DEVICE 0x07 +#define USB_OS_20_FEATURE_VENDOR_REVISION 0x08 + +static const gchar * +fu_usb_device_os20_type_to_string(guint16 type) +{ + if (type == USB_OS_20_SET_HEADER_DESCRIPTOR) + return "set-header-descriptor"; + if (type == USB_OS_20_SUBSET_HEADER_CONFIGURATION) + return "subset-header-configuration"; + if (type == USB_OS_20_SUBSET_HEADER_FUNCTION) + return "subset-header-function"; + if (type == USB_OS_20_FEATURE_COMPATBLE_ID) + return "feature-compatble-id"; + if (type == USB_OS_20_FEATURE_REG_PROPERTY) + return "feature-reg-property"; + if (type == USB_OS_20_FEATURE_MIN_RESUME_TIME) + return "feature-min-resume-time"; + if (type == USB_OS_20_FEATURE_MODEL_ID) + return "feature-model-id"; + if (type == USB_OS_20_FEATURE_CCGP_DEVICE) + return "feature-ccgp-device"; + if (type == USB_OS_20_FEATURE_VENDOR_REVISION) + return "feature-vendor-revision"; + return NULL; +} + +static gboolean +fu_usb_device_ms_ds20_parse(FuUsbDeviceDs20 *self, + GBytes *blob, + FuUsbDevice *device, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + + /* get length and type only */ + for (gsize offset = 0; offset < bufsz;) { + guint16 desc_sz = 0; + guint16 desc_type = 0; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + 0x0, + &desc_sz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (desc_sz == 0) + break; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + 0x2, + &desc_type, + G_LITTLE_ENDIAN, + error)) + return FALSE; + g_debug("USB OS descriptor type 0x%04x [%s], length 0x%04x", + desc_type, + fu_usb_device_os20_type_to_string(desc_type), + desc_sz); + offset += desc_sz; + } + + /* success */ + return TRUE; +} + +static void +fu_usb_device_ms_ds20_class_init(FuUsbDeviceMsDs20Class *klass) +{ + FuUsbDeviceDs20Class *usb_device_ds20_klass = FU_USB_DEVICE_DS20_CLASS(klass); + usb_device_ds20_klass->parse = fu_usb_device_ms_ds20_parse; +} + +static void +fu_usb_device_ms_ds20_init(FuUsbDeviceMsDs20 *self) +{ + fu_firmware_set_version_raw(FU_FIRMWARE(self), 0x06030000); /* Windows 8.1 */ + fu_firmware_set_id(FU_FIRMWARE(self), "d8dd60df-4589-4cc7-9cd2-659d9e648a9f"); +} + +/** + * fu_usb_device_ms_ds20_new: + * + * Creates a new #FuUsbDeviceMsDs20. + * + * Returns: (transfer full): a #FuFirmware + * + * Since: 1.8.5 + **/ +FuFirmware * +fu_usb_device_ms_ds20_new(void) +{ + return g_object_new(FU_TYPE_USB_DEVICE_MS_DS20, NULL); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ms-ds20.h b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ms-ds20.h new file mode 100644 index 0000000000000000000000000000000000000000..9fe06055dfac172fc5b81534c64a3462a3429422 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-ms-ds20.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-usb-device-ds20.h" + +#define FU_TYPE_USB_DEVICE_MS_DS20 (fu_usb_device_ms_ds20_get_type()) +G_DECLARE_FINAL_TYPE(FuUsbDeviceMsDs20, + fu_usb_device_ms_ds20, + FU, + USB_DEVICE_MS_DS20, + FuUsbDeviceDs20) + +FuFirmware * +fu_usb_device_ms_ds20_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device-private.h b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-private.h new file mode 100644 index 0000000000000000000000000000000000000000..0050c45c9a5590784f999e628bf044c161ec6108 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device-private.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-usb-device.h" + +const gchar * +fu_usb_device_get_platform_id(FuUsbDevice *self); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device.c b/fwupd-1.8.6/libfwupdplugin/fu-usb-device.c new file mode 100644 index 0000000000000000000000000000000000000000..136e6c3a987d52dfafe732c749ab9fe01e607b96 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device.c @@ -0,0 +1,946 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuUsbDevice" + +#include "config.h" + +#include "fu-device-private.h" +#include "fu-dump.h" +#include "fu-mem.h" +#include "fu-string.h" +#include "fu-usb-device-fw-ds20.h" +#include "fu-usb-device-ms-ds20.h" +#include "fu-usb-device-private.h" + +/** + * FuUsbDevice: + * + * A USB device. + * + * See also: [class@FuDevice], [class@FuHidDevice] + */ + +typedef struct { + GUsbDevice *usb_device; + gint configuration; + GPtrArray *interfaces; /* nullable, element-type FuUsbDeviceInterface */ + FuDeviceLocker *usb_device_locker; +} FuUsbDevicePrivate; + +typedef struct { + guint8 number; + gboolean claimed; +} FuUsbDeviceInterface; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUsbDevice, fu_usb_device, FU_TYPE_DEVICE) +enum { PROP_0, PROP_USB_DEVICE, PROP_LAST }; + +#define GET_PRIVATE(o) (fu_usb_device_get_instance_private(o)) + +static void +fu_usb_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuUsbDevice *device = FU_USB_DEVICE(object); + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + switch (prop_id) { + 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_usb_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuUsbDevice *device = FU_USB_DEVICE(object); + switch (prop_id) { + case PROP_USB_DEVICE: + fu_usb_device_set_dev(device, g_value_get_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_usb_device_finalize(GObject *object) +{ + FuUsbDevice *device = FU_USB_DEVICE(object); + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + + if (priv->usb_device_locker != NULL) + g_object_unref(priv->usb_device_locker); + if (priv->usb_device != NULL) + g_object_unref(priv->usb_device); + if (priv->interfaces != NULL) + g_ptr_array_unref(priv->interfaces); + + G_OBJECT_CLASS(fu_usb_device_parent_class)->finalize(object); +} + +#if G_USB_CHECK_VERSION(0, 4, 1) +static void +fu_usb_device_backend_tags_notify_cb(GObject *object, GParamSpec *pspec, FuUsbDevice *device) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + GPtrArray *backend_tags = fu_device_get_backend_tags(FU_DEVICE(device)); + for (guint i = 0; i < backend_tags->len; i++) { + const gchar *backend_tag = g_ptr_array_index(backend_tags, i); + g_usb_device_add_tag(priv->usb_device, backend_tag); + } +} +#endif + +static void +fu_usb_device_init(FuUsbDevice *device) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + priv->configuration = -1; + fu_device_set_acquiesce_delay(FU_DEVICE(device), 2500); +#ifdef HAVE_GUSB + fu_device_retry_add_recovery(FU_DEVICE(device), + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE, + NULL); + fu_device_retry_add_recovery(FU_DEVICE(device), + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_PERMISSION_DENIED, + NULL); +#endif + +#if G_USB_CHECK_VERSION(0, 4, 1) + /* copy this to the GUsbDevice */ + g_signal_connect(FU_DEVICE(device), + "notify::backend-tags", + G_CALLBACK(fu_usb_device_backend_tags_notify_cb), + device); +#endif +} + +/** + * fu_usb_device_is_open: + * @device: a #FuUsbDevice + * + * Finds out if a USB device is currently open. + * + * Returns: %TRUE if the device is open. + * + * Since: 1.0.3 + **/ +gboolean +fu_usb_device_is_open(FuUsbDevice *device) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + g_return_val_if_fail(FU_IS_USB_DEVICE(device), FALSE); + return priv->usb_device_locker != NULL; +} + +/** + * fu_usb_device_set_configuration: + * @device: a #FuUsbDevice + * @configuration: the configuration value to set + * + * Set the active bConfigurationValue for the device. + * + * Since: 1.7.4 + **/ +void +fu_usb_device_set_configuration(FuUsbDevice *device, gint configuration) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + g_return_if_fail(FU_IS_USB_DEVICE(device)); + priv->configuration = configuration; +} + +/** + * fu_usb_device_add_interface: + * @device: a #FuUsbDevice + * @number: bInterfaceNumber of the interface + * + * Adds an interface that will be claimed on `->open()` and released on `->close()`. + * + * Since: 1.7.4 + **/ +void +fu_usb_device_add_interface(FuUsbDevice *device, guint8 number) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + FuUsbDeviceInterface *iface; + + g_return_if_fail(FU_IS_USB_DEVICE(device)); + + if (priv->interfaces == NULL) + priv->interfaces = g_ptr_array_new_with_free_func(g_free); + + /* check for existing */ + for (guint i = 0; i < priv->interfaces->len; i++) { + iface = g_ptr_array_index(priv->interfaces, i); + if (iface->number == number) + return; + } + + /* add new */ + iface = g_new0(FuUsbDeviceInterface, 1); + iface->number = number; + g_ptr_array_add(priv->interfaces, iface); +} + +#ifdef HAVE_GUSB +static gboolean +fu_usb_device_query_hub(FuUsbDevice *self, GError **error) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + gsize sz = 0; + guint16 value = 0x29; + guint8 data[0x0c] = {0x0}; + g_autoptr(GString) hub = g_string_new(NULL); + + /* longer descriptor for SuperSpeed */ + if (fu_usb_device_get_spec(self) >= 0x0300) + value = 0x2a; + if (!g_usb_device_control_transfer(priv->usb_device, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0x06, /* LIBUSB_REQUEST_GET_DESCRIPTOR */ + value << 8, + 0x00, + data, + sizeof(data), + &sz, + 1000, + NULL, + error)) { + g_prefix_error(error, "failed to get USB descriptor: "); + return FALSE; + } + if (g_getenv("FU_USB_DEVICE_DEBUG") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "HUB_DT", data, sz); + + /* for USB 3: size is fixed as max ports is 15, + * for USB 2: size is variable as max ports is 255 */ + if (fu_usb_device_get_spec(self) >= 0x0300 && sz == 0x0C) { + g_string_append_printf(hub, "%02X", data[0x0B]); + g_string_append_printf(hub, "%02X", data[0x0A]); + } else if (sz >= 9) { + guint8 numbytes = fu_common_align_up(data[2] + 1, 0x03) / 8; + for (guint i = 0; i < numbytes; i++) { + guint8 tmp = 0x0; + if (!fu_memread_uint8_safe(data, sz, 7 + i, &tmp, error)) + return FALSE; + g_string_append_printf(hub, "%02X", tmp); + } + } + if (hub->len > 0) + fu_device_add_instance_str(FU_DEVICE(self), "HUB", hub->str); + return fu_device_build_instance_id(FU_DEVICE(self), error, "VID", "PID", "HUB", NULL); +} +#endif + +static gboolean +fu_usb_device_open(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuUsbDevice *self = FU_USB_DEVICE(device); + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuDeviceLocker) locker = NULL; + + 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; + + /* open */ + locker = fu_device_locker_new(priv->usb_device, error); + if (locker == NULL) + return FALSE; + + /* success */ + priv->usb_device_locker = g_steal_pointer(&locker); + + /* if set */ + if (priv->configuration >= 0) { + if (!g_usb_device_set_configuration(priv->usb_device, priv->configuration, error)) + return FALSE; + } + + /* claim interfaces */ + for (guint i = 0; priv->interfaces != NULL && i < priv->interfaces->len; i++) { + FuUsbDeviceInterface *iface = g_ptr_array_index(priv->interfaces, i); + if (!g_usb_device_claim_interface(priv->usb_device, + iface->number, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error(error, "failed to claim interface 0x%02x: ", iface->number); + return FALSE; + } + iface->claimed = TRUE; + } +#endif + return TRUE; +} + +static gboolean +fu_usb_device_setup(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuUsbDevice *self = FU_USB_DEVICE(device); + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + guint idx; +#if G_USB_CHECK_VERSION(0, 4, 0) + g_autoptr(GPtrArray) bos_descriptors = NULL; +#endif + + g_return_val_if_fail(FU_IS_USB_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* get vendor */ + 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; + g_autoptr(GError) error_local = NULL; + tmp = + g_usb_device_get_string_descriptor(priv->usb_device, idx, &error_local); + if (tmp != NULL) + fu_device_set_vendor(device, g_strchomp(tmp)); + else + g_debug( + "failed to load manufacturer string for usb device %u:%u: %s", + g_usb_device_get_bus(priv->usb_device), + g_usb_device_get_address(priv->usb_device), + error_local->message); + } + } + + /* get product */ + 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; + g_autoptr(GError) error_local = NULL; + tmp = + g_usb_device_get_string_descriptor(priv->usb_device, idx, &error_local); + if (tmp != NULL) + fu_device_set_name(device, g_strchomp(tmp)); + else + g_debug("failed to load product string for usb device %u:%u: %s", + g_usb_device_get_bus(priv->usb_device), + g_usb_device_get_address(priv->usb_device), + error_local->message); + } + } + + /* get serial number */ + if (!fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER) && + 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; + g_autoptr(GError) error_local = NULL; + tmp = + g_usb_device_get_string_descriptor(priv->usb_device, idx, &error_local); + if (tmp != NULL) + fu_device_set_serial(device, g_strchomp(tmp)); + else + g_debug( + "failed to load serial number string for usb device %u:%u: %s", + g_usb_device_get_bus(priv->usb_device), + g_usb_device_get_address(priv->usb_device), + error_local->message); + } + } + + /* get the hub descriptor if this is a hub */ + if (g_usb_device_get_device_class(priv->usb_device) == G_USB_DEVICE_CLASS_HUB) { + if (!fu_usb_device_query_hub(self, error)) + return FALSE; + } + +#if G_USB_CHECK_VERSION(0, 4, 2) + /* get the platform capability BOS descriptors */ + bos_descriptors = g_usb_device_get_bos_descriptors(priv->usb_device, NULL); + for (guint i = 0; bos_descriptors != NULL && i < bos_descriptors->len; i++) { + GUsbBosDescriptor *bos = g_ptr_array_index(bos_descriptors, i); + GBytes *extra = g_usb_bos_descriptor_get_extra(bos); + if (g_usb_bos_descriptor_get_capability(bos) == 0x5 && + g_bytes_get_size(extra) > 0) { + g_autoptr(FuFirmware) ds20 = NULL; + g_autoptr(GError) error_ds20 = NULL; + + ds20 = fu_firmware_new_from_gtypes(extra, + FWUPD_INSTALL_FLAG_NONE, + &error_ds20, + FU_TYPE_USB_DEVICE_FW_DS20, + FU_TYPE_USB_DEVICE_MS_DS20, + G_TYPE_INVALID); + if (ds20 == NULL) { + g_warning("failed to parse platform capability BOS descriptor: %s", + error_ds20->message); + continue; + } + if (!fu_usb_device_ds20_apply_to_device(FU_USB_DEVICE_DS20(ds20), + self, + &error_ds20)) { + g_warning("failed to get DS20 data: %s", error_ds20->message); + continue; + } + if (g_getenv("FU_USB_DEVICE_DEBUG") != NULL) { + g_autofree gchar *str = fu_firmware_to_string(ds20); + g_debug("DS20: %s", str); + } + } + } +#endif +#endif + + /* success */ + return TRUE; +} + +static gboolean +fu_usb_device_ready(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuUsbDevice *self = FU_USB_DEVICE(device); + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) intfs = NULL; + + /* get the interface GUIDs */ + intfs = g_usb_device_get_interfaces(priv->usb_device, error); + if (intfs == NULL) + return FALSE; + + /* add fallback icon if there is nothing added already */ + if (fu_device_get_icons(device)->len == 0) { + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + + /* Video: Video Control: i.e. a webcam */ + if (g_usb_interface_get_class(intf) == G_USB_DEVICE_CLASS_VIDEO && + g_usb_interface_get_subclass(intf) == 0x01) { + fu_device_add_icon(device, "camera-web"); + } + + /* Audio */ + if (g_usb_interface_get_class(intf) == G_USB_DEVICE_CLASS_AUDIO) + fu_device_add_icon(device, "audio-card"); + + /* Mass Storage */ + if (g_usb_interface_get_class(intf) == G_USB_DEVICE_CLASS_MASS_STORAGE) + fu_device_add_icon(device, "drive-harddisk"); + + /* Printer */ + if (g_usb_interface_get_class(intf) == G_USB_DEVICE_CLASS_PRINTER) + fu_device_add_icon(device, "printer"); + } + } +#endif + + /* success */ + return TRUE; +} + +static gboolean +fu_usb_device_close(FuDevice *device, GError **error) +{ + FuUsbDevice *self = FU_USB_DEVICE(device); + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_USB_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* already closed */ + if (priv->usb_device_locker == NULL) + return TRUE; + +#ifdef HAVE_GUSB + /* release interfaces, ignoring errors */ + for (guint i = 0; priv->interfaces != NULL && i < priv->interfaces->len; i++) { + FuUsbDeviceInterface *iface = g_ptr_array_index(priv->interfaces, i); + g_autoptr(GError) error_local = NULL; + if (!iface->claimed) + continue; + if (!g_usb_device_release_interface(priv->usb_device, + iface->number, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + &error_local)) { + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE) || + g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_INTERNAL)) { + g_debug("failed to release interface 0x%02x: %s", + iface->number, + error_local->message); + } else { + g_warning("failed to release interface 0x%02x: %s", + iface->number, + error_local->message); + } + } + iface->claimed = FALSE; + } +#endif + + g_clear_object(&priv->usb_device_locker); + return TRUE; +} + +static gboolean +fu_usb_device_probe(FuDevice *device, GError **error) +{ +#ifdef HAVE_GUSB + FuUsbDevice *self = FU_USB_DEVICE(device); + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + guint16 release; + g_autofree gchar *platform_id = NULL; + g_autofree gchar *vendor_id = NULL; + g_autoptr(GPtrArray) intfs = NULL; + + /* set vendor ID */ + vendor_id = g_strdup_printf("USB:0x%04X", g_usb_device_get_vid(priv->usb_device)); + fu_device_add_vendor_id(device, vendor_id); + + /* set the version if the release has been set */ + release = g_usb_device_get_release(priv->usb_device); + if (release != 0x0 && + fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_autofree gchar *version = NULL; + version = fu_version_from_uint16(release, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version(device, version); + } + + /* add GUIDs in order of priority */ + fu_device_add_instance_u16(device, "VID", g_usb_device_get_vid(priv->usb_device)); + fu_device_add_instance_u16(device, "PID", g_usb_device_get_pid(priv->usb_device)); + fu_device_add_instance_u16(device, "REV", release); + fu_device_build_instance_id_quirk(device, NULL, "USB", "VID", NULL); + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", NULL); + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "REV", NULL); + + /* 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); + fu_device_add_instance_u8(device, "CLASS", g_usb_interface_get_class(intf)); + fu_device_add_instance_u8(device, "SUBCLASS", g_usb_interface_get_subclass(intf)); + fu_device_add_instance_u8(device, "PROT", g_usb_interface_get_protocol(intf)); + fu_device_build_instance_id_quirk(device, NULL, "USB", "CLASS", NULL); + fu_device_build_instance_id_quirk(device, NULL, "USB", "CLASS", "SUBCLASS", NULL); + fu_device_build_instance_id_quirk(device, + NULL, + "USB", + "CLASS", + "SUBCLASS", + "PROT", + NULL); + } + + /* add 2 levels of parent IDs */ + platform_id = g_strdup(g_usb_device_get_platform_id(priv->usb_device)); + for (guint i = 0; i < 2; i++) { + gchar *tok = g_strrstr(platform_id, ":"); + if (tok == NULL) + break; + *tok = '\0'; + if (g_strcmp0(platform_id, "usb") == 0) + break; + fu_device_add_parent_physical_id(device, platform_id); + } +#endif + + /* 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) +{ +#ifdef HAVE_GUSB + 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); +#else + return 0x0; +#endif +} + +/** + * 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) +{ +#ifdef HAVE_GUSB + 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); +#else + return 0x0; +#endif +} + +/** + * 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) +{ +#ifdef HAVE_GUSB + 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); +#else + return NULL; +#endif +} + +/** + * fu_usb_device_get_spec: + * @self: a #FuUsbDevice + * + * Gets the string USB revision for the device. + * + * Returns: a specification revision in BCD format, or 0x0 if not supported + * + * Since: 1.3.4 + **/ +guint16 +fu_usb_device_get_spec(FuUsbDevice *self) +{ +#if G_USB_CHECK_VERSION(0, 3, 1) + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_USB_DEVICE(self), 0x0); + if (priv->usb_device == NULL) + return 0x0; + return g_usb_device_get_spec(priv->usb_device); +#else + return 0x0; +#endif +} + +/** + * fu_usb_device_set_dev: + * @device: a #FuUsbDevice + * @usb_device: (nullable): optional #GUsbDevice + * + * Sets the #GUsbDevice to use. + * + * Since: 1.0.2 + **/ +void +fu_usb_device_set_dev(FuUsbDevice *device, GUsbDevice *usb_device) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + + g_return_if_fail(FU_IS_USB_DEVICE(device)); + + /* need to re-probe hardware */ + fu_device_probe_invalidate(FU_DEVICE(device)); + + /* allow replacement */ + g_set_object(&priv->usb_device, usb_device); + if (usb_device == NULL) { + g_clear_object(&priv->usb_device_locker); + return; + } + +#ifdef HAVE_GUSB + /* set device ID automatically */ + fu_device_set_physical_id(FU_DEVICE(device), g_usb_device_get_platform_id(usb_device)); +#endif +} + +/** + * fu_usb_device_find_udev_device: + * @device: a #FuUsbDevice + * @error: (nullable): optional return location for an error + * + * Gets the matching #GUdevDevice for the #GUsbDevice. + * + * Returns: (transfer full): a #GUdevDevice, or NULL if unset or invalid + * + * Since: 1.3.2 + **/ +GUdevDevice * +fu_usb_device_find_udev_device(FuUsbDevice *device, GError **error) +{ +#if defined(HAVE_GUDEV) && defined(HAVE_GUSB) + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + g_autoptr(GList) devices = NULL; + g_autoptr(GUdevClient) gudev_client = g_udev_client_new(NULL); + + g_return_val_if_fail(FU_IS_USB_DEVICE(device), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find all tty devices */ + devices = g_udev_client_query_by_subsystem(gudev_client, "usb"); + for (GList *l = devices; l != NULL; l = l->next) { + GUdevDevice *dev = G_UDEV_DEVICE(l->data); + + /* check correct device */ + if (g_udev_device_get_sysfs_attr_as_int(dev, "busnum") != + g_usb_device_get_bus(priv->usb_device)) + continue; + if (g_udev_device_get_sysfs_attr_as_int(dev, "devnum") != + g_usb_device_get_address(priv->usb_device)) + continue; + + /* success */ + g_debug("USB device %u:%u is %s", + g_usb_device_get_bus(priv->usb_device), + g_usb_device_get_address(priv->usb_device), + g_udev_device_get_sysfs_path(dev)); + return g_object_ref(dev); + } + + /* failure */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not find sysfs device for %u:%u", + g_usb_device_get_bus(priv->usb_device), + g_usb_device_get_address(priv->usb_device)); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); +#endif + return NULL; +} + +/** + * fu_usb_device_get_dev: + * @device: a #FuUsbDevice + * + * Gets the #GUsbDevice. + * + * Returns: (transfer none): a USB device, or %NULL + * + * Since: 1.0.2 + **/ +GUsbDevice * +fu_usb_device_get_dev(FuUsbDevice *device) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE(device); + g_return_val_if_fail(FU_IS_USB_DEVICE(device), NULL); + 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))); +} + +static gboolean +fu_udev_device_bind_driver(FuDevice *device, + const gchar *subsystem, + const gchar *driver, + GError **error) +{ + FuUsbDevice *self = FU_USB_DEVICE(device); + g_autoptr(GUdevDevice) dev = NULL; + g_autoptr(FuUdevDevice) udev_device = NULL; + + /* use udev for this */ + dev = fu_usb_device_find_udev_device(self, error); + if (dev == NULL) + return FALSE; + udev_device = fu_udev_device_new(fu_device_get_context(device), dev); + return fu_device_bind_driver(FU_DEVICE(udev_device), subsystem, driver, error); +} + +static gboolean +fu_udev_device_unbind_driver(FuDevice *device, GError **error) +{ + FuUsbDevice *self = FU_USB_DEVICE(device); + g_autoptr(GUdevDevice) dev = NULL; + g_autoptr(FuUdevDevice) udev_device = NULL; + + /* use udev for this */ + dev = fu_usb_device_find_udev_device(self, error); + if (dev == NULL) + return FALSE; + udev_device = fu_udev_device_new(fu_device_get_context(device), dev); + return fu_device_unbind_driver(FU_DEVICE(udev_device), error); +} + +/** + * fu_usb_device_new: + * @ctx: (nullable): a #FuContext + * @usb_device: a USB device + * + * Creates a new #FuUsbDevice. + * + * Returns: (transfer full): a #FuUsbDevice + * + * Since: 1.8.2 + **/ +FuUsbDevice * +fu_usb_device_new(FuContext *ctx, GUsbDevice *usb_device) +{ + return g_object_new(FU_TYPE_USB_DEVICE, "context", ctx, "usb-device", usb_device, NULL); +} + +#ifdef HAVE_GUSB +static const gchar * +fu_usb_device_class_code_to_string(GUsbDeviceClassCode code) +{ + if (code == G_USB_DEVICE_CLASS_INTERFACE_DESC) + return "interface-desc"; + if (code == G_USB_DEVICE_CLASS_AUDIO) + return "audio"; + if (code == G_USB_DEVICE_CLASS_COMMUNICATIONS) + return "communications"; + if (code == G_USB_DEVICE_CLASS_HID) + return "hid"; + if (code == G_USB_DEVICE_CLASS_PHYSICAL) + return "physical"; + if (code == G_USB_DEVICE_CLASS_IMAGE) + return "image"; + if (code == G_USB_DEVICE_CLASS_PRINTER) + return "printer"; + if (code == G_USB_DEVICE_CLASS_MASS_STORAGE) + return "mass-storage"; + if (code == G_USB_DEVICE_CLASS_HUB) + return "hub"; + if (code == G_USB_DEVICE_CLASS_CDC_DATA) + return "cdc-data"; + if (code == G_USB_DEVICE_CLASS_SMART_CARD) + return "smart-card"; + if (code == G_USB_DEVICE_CLASS_CONTENT_SECURITY) + return "content-security"; + if (code == G_USB_DEVICE_CLASS_VIDEO) + return "video"; + if (code == G_USB_DEVICE_CLASS_PERSONAL_HEALTHCARE) + return "personal-healthcare"; + if (code == G_USB_DEVICE_CLASS_AUDIO_VIDEO) + return "audio-video"; + if (code == G_USB_DEVICE_CLASS_BILLBOARD) + return "billboard"; + if (code == G_USB_DEVICE_CLASS_DIAGNOSTIC) + return "diagnostic"; + if (code == G_USB_DEVICE_CLASS_WIRELESS_CONTROLLER) + return "wireless-controller"; + if (code == G_USB_DEVICE_CLASS_MISCELLANEOUS) + return "miscellaneous"; + if (code == G_USB_DEVICE_CLASS_APPLICATION_SPECIFIC) + return "application-specific"; + if (code == G_USB_DEVICE_CLASS_VENDOR_SPECIFIC) + return "vendor-specific"; + return NULL; +} +#endif + +static void +fu_usb_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuUsbDevice *self = FU_USB_DEVICE(device); + FuUsbDevicePrivate *priv = GET_PRIVATE(self); + + if (priv->configuration > 0) + fu_string_append_kx(str, idt, "Configuration", priv->configuration); + for (guint i = 0; priv->interfaces != NULL && i < priv->interfaces->len; i++) { + FuUsbDeviceInterface *iface = g_ptr_array_index(priv->interfaces, i); + g_autofree gchar *tmp = g_strdup_printf("InterfaceNumber#%02x", iface->number); + fu_string_append(str, idt, tmp, iface->claimed ? "claimed" : "released"); + } + +#ifdef HAVE_GUSB + if (priv->usb_device != NULL) { + GUsbDeviceClassCode code = g_usb_device_get_device_class(priv->usb_device); + fu_string_append(str, + idt, + "UsbDeviceClass", + fu_usb_device_class_code_to_string(code)); + } +#endif +} + +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->setup = fu_usb_device_setup; + device_class->ready = fu_usb_device_ready; + device_class->close = fu_usb_device_close; + device_class->probe = fu_usb_device_probe; + device_class->to_string = fu_usb_device_to_string; + device_class->incorporate = fu_usb_device_incorporate; + device_class->bind_driver = fu_udev_device_bind_driver; + device_class->unbind_driver = fu_udev_device_unbind_driver; + + /** + * FuUsbDevice:usb-device: + * + * The low-level #GUsbDevice. + * + * Since: 1.0.2 + */ + pspec = g_param_spec_object("usb-device", + NULL, + NULL, +#ifdef HAVE_GUSB + G_USB_TYPE_DEVICE, +#else + G_TYPE_OBJECT, +#endif + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_USB_DEVICE, pspec); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-usb-device.h b/fwupd-1.8.6/libfwupdplugin/fu-usb-device.h new file mode 100644 index 0000000000000000000000000000000000000000..e56489a2f912f39dcefb7047a1b4e44604ffecfd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-usb-device.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#ifdef HAVE_GUSB +#include +#else +#define GUsbContext GObject +#define GUsbDevice GObject +#ifndef __GI_SCANNER__ +#define G_USB_CHECK_VERSION(a, c, b) 0 +#endif +#endif + +#include "fu-plugin.h" +#include "fu-udev-device.h" + +#define FU_TYPE_USB_DEVICE (fu_usb_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuUsbDevice, fu_usb_device, FU, USB_DEVICE, FuDevice) + +struct _FuUsbDeviceClass { + FuDeviceClass parent_class; + gpointer __reserved[31]; +}; + +FuUsbDevice * +fu_usb_device_new(FuContext *ctx, GUsbDevice *usb_device); +guint16 +fu_usb_device_get_vid(FuUsbDevice *self); +guint16 +fu_usb_device_get_pid(FuUsbDevice *self); +guint16 +fu_usb_device_get_spec(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_is_open(FuUsbDevice *device); +GUdevDevice * +fu_usb_device_find_udev_device(FuUsbDevice *device, GError **error) G_GNUC_WARN_UNUSED_RESULT; +void +fu_usb_device_set_configuration(FuUsbDevice *device, gint configuration); +void +fu_usb_device_add_interface(FuUsbDevice *device, guint8 number); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-uswid-firmware.c b/fwupd-1.8.6/libfwupdplugin/fu-uswid-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..c0d2948af3658e7cb989e64e10e1f48cd8ecc916 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-uswid-firmware.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-bytes.h" +#include "fu-common.h" +#include "fu-coswid-firmware.h" +#include "fu-mem.h" +#include "fu-string.h" +#include "fu-uswid-firmware.h" + +/** + * FuUswidFirmware: + * + * A uSWID header with multiple optionally-compressed coSWID CBOR sections. + * + * See also: [class@FuCoswidFirmware] + */ + +typedef struct { + guint8 hdrver; + gboolean compressed; +} FuUswidFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUswidFirmware, fu_uswid_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_uswid_firmware_get_instance_private(o)) + +#define USWID_HEADER_VERSION_V1 1 +#define USWID_HEADER_SIZE_V1 23 +#define USWID_HEADER_SIZE_V2 24 + +#define USWID_HEADER_FLAG_COMPRESSED 0x01 + +typedef struct __attribute__((packed)) { + guint8 magic[16]; + guint8 hdrver; + guint16 hdrsz; + guint32 payloadsz; + guint8 flags; +} FuUswidFirmwareHdr; + +const guint8 USWID_HEADER_MAGIC[] = {0x53, + 0x42, + 0x4F, + 0x4D, + 0xD6, + 0xBA, + 0x2E, + 0xAC, + 0xA3, + 0xE6, + 0x7A, + 0x52, + 0xAA, + 0xEE, + 0x3B, + 0xAF}; + +static void +fu_uswid_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware); + FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "hdrver", priv->hdrver); + fu_xmlb_builder_insert_kb(bn, "compressed", priv->compressed); +} + +static gboolean +fu_uswid_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint8 magic[16] = {0x0}; + + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, USWID_HEADER_MAGIC, sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic for file"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uswid_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware); + FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); + guint16 hdrsz = 0; + guint32 payloadsz = 0; + guint8 uswid_flags = 0; + gsize bufsz; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(GBytes) payload = NULL; + + /* hdrver */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuUswidFirmwareHdr, hdrver), + &priv->hdrver, + error)) + return FALSE; + if (priv->hdrver < USWID_HEADER_VERSION_V1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "header version was unsupported"); + return FALSE; + } + + /* hdrsz */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuUswidFirmwareHdr, hdrsz), + &hdrsz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (hdrsz < USWID_HEADER_SIZE_V1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "header size is invalid"); + return FALSE; + } + + /* payloadsz */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuUswidFirmwareHdr, payloadsz), + &payloadsz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (payloadsz == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "payload size is invalid"); + return FALSE; + } + fu_firmware_set_size(firmware, hdrsz + payloadsz); + + /* flags */ + if (priv->hdrver >= 0x02) { + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuUswidFirmwareHdr, flags), + &uswid_flags, + error)) + return FALSE; + priv->compressed = (uswid_flags & USWID_HEADER_FLAG_COMPRESSED) > 0; + } + + /* zlib stream */ + if (priv->compressed) { + g_autoptr(GBytes) payload_tmp = NULL; + g_autoptr(GConverter) conv = NULL; + g_autoptr(GInputStream) istream1 = NULL; + g_autoptr(GInputStream) istream2 = NULL; + + payload_tmp = fu_bytes_new_offset(fw, offset + hdrsz, payloadsz, error); + if (payload_tmp == NULL) + return FALSE; + istream1 = g_memory_input_stream_new_from_bytes(payload_tmp); + conv = G_CONVERTER(g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB)); + istream2 = g_converter_input_stream_new(istream1, conv); + payload = fu_bytes_get_contents_stream(istream2, G_MAXSIZE, error); + if (payload == NULL) + return FALSE; + payloadsz = g_bytes_get_size(payload); + } else { + payload = fu_bytes_new_offset(fw, offset + hdrsz, payloadsz, error); + if (payload == NULL) + return FALSE; + } + + /* payload */ + for (gsize offset_tmp = 0; offset_tmp < payloadsz;) { + g_autoptr(FuFirmware) firmware_coswid = fu_coswid_firmware_new(); + g_autoptr(GBytes) fw2 = NULL; + + /* CBOR parse */ + fw2 = fu_bytes_new_offset(payload, offset_tmp, payloadsz - offset_tmp, error); + if (fw2 == NULL) + return FALSE; + if (!fu_firmware_parse(firmware_coswid, + fw2, + flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + error)) + return FALSE; + fu_firmware_add_image(firmware, firmware_coswid); + if (fu_firmware_get_size(firmware_coswid) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "coSWID read no bytes"); + return FALSE; + } + offset_tmp += fu_firmware_get_size(firmware_coswid); + } + + /* success */ + return TRUE; +} + +static guint8 +fu_uswid_firmware_calculate_hdrsz(FuUswidFirmware *self) +{ + FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); + if (priv->hdrver == 0x1) + return USWID_HEADER_SIZE_V1; + return USWID_HEADER_SIZE_V2; +} + +static GBytes * +fu_uswid_firmware_write(FuFirmware *firmware, GError **error) +{ + FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware); + FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GByteArray) payload = g_byte_array_new(); + g_autoptr(GBytes) payload_blob = NULL; + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* generate early so we know the size */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *firmware_coswid = g_ptr_array_index(images, i); + g_autoptr(GBytes) fw = fu_firmware_write(firmware_coswid, error); + if (fw == NULL) + return NULL; + fu_byte_array_append_bytes(payload, fw); + } + + /* zlibify */ + if (priv->compressed) { + g_autoptr(GConverter) conv = NULL; + g_autoptr(GInputStream) istream1 = NULL; + g_autoptr(GInputStream) istream2 = NULL; + + conv = G_CONVERTER(g_zlib_compressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB, -1)); + istream1 = g_memory_input_stream_new_from_data(payload->data, payload->len, NULL); + istream2 = g_converter_input_stream_new(istream1, conv); + payload_blob = fu_bytes_get_contents_stream(istream2, G_MAXSIZE, error); + if (payload_blob == NULL) + return NULL; + } else { + payload_blob = g_byte_array_free_to_bytes(g_steal_pointer(&payload)); + } + + /* header then CBOR blob */ + g_byte_array_append(buf, USWID_HEADER_MAGIC, sizeof(USWID_HEADER_MAGIC)); + fu_byte_array_append_uint8(buf, priv->hdrver); + fu_byte_array_append_uint16(buf, fu_uswid_firmware_calculate_hdrsz(self), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, g_bytes_get_size(payload_blob), G_LITTLE_ENDIAN); + if (priv->hdrver >= 2) { + guint8 flags = 0; + if (priv->compressed) + flags |= USWID_HEADER_FLAG_COMPRESSED; + fu_byte_array_append_uint8(buf, flags); + } + fu_byte_array_append_bytes(buf, payload_blob); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_uswid_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware); + FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *str; + guint64 tmp; + + /* simple properties */ + tmp = xb_node_query_text_as_uint(n, "hdrver", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->hdrver = tmp; + + /* simple properties */ + str = xb_node_query_text(n, "compressed", NULL); + if (str != NULL) { + if (!fu_strtobool(str, &priv->compressed, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_uswid_firmware_init(FuUswidFirmware *self) +{ + FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); + priv->hdrver = USWID_HEADER_VERSION_V1; + priv->compressed = FALSE; + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); +} + +static void +fu_uswid_firmware_class_init(FuUswidFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_uswid_firmware_check_magic; + klass_firmware->parse = fu_uswid_firmware_parse; + klass_firmware->write = fu_uswid_firmware_write; + klass_firmware->build = fu_uswid_firmware_build; + klass_firmware->export = fu_uswid_firmware_export; +} + +/** + * fu_uswid_firmware_new: + * + * Creates a new #FuFirmware of sub type uSWID + * + * Since: 1.8.0 + **/ +FuFirmware * +fu_uswid_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_USWID_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-uswid-firmware.h b/fwupd-1.8.6/libfwupdplugin/fu-uswid-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..a48beb76b54e51467172197e7d7c4f8444094919 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-uswid-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_USWID_FIRMWARE (fu_uswid_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuUswidFirmware, fu_uswid_firmware, FU, USWID_FIRMWARE, FuFirmware) + +struct _FuUswidFirmwareClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_uswid_firmware_new(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-version-common.c b/fwupd-1.8.6/libfwupdplugin/fu-version-common.c new file mode 100644 index 0000000000000000000000000000000000000000..48a96bcbb2dd2c82debf458b00cb00474c0857e2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-version-common.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include "config.h" + +#include + +#include "fwupd-enums.h" +#include "fwupd-error.h" + +#include "fu-version-common.h" + +#define FU_COMMON_VERSION_DECODE_BCD(val) ((((val) >> 4) & 0x0f) * 10 + ((val)&0x0f)) + +gchar * +fu_common_version_ensure_semver(const gchar *version); + +/** + * fu_version_from_uint64: + * @val: a raw version number + * @kind: version kind used for formatting, e.g. %FWUPD_VERSION_FORMAT_QUAD + * + * Returns a dotted decimal version string from a 64 bit number. + * + * Returns: a version number, e.g. `1.2.3.4`, or %NULL if not supported + * + * Since: 1.8.2 + **/ +gchar * +fu_version_from_uint64(guint64 val, FwupdVersionFormat kind) +{ + if (kind == FWUPD_VERSION_FORMAT_QUAD) { + /* AABB.CCDD.EEFF.GGHH */ + return g_strdup_printf("%" G_GUINT64_FORMAT "." + "%" G_GUINT64_FORMAT "." + "%" G_GUINT64_FORMAT "." + "%" G_GUINT64_FORMAT "", + (val >> 48) & 0xffff, + (val >> 32) & 0xffff, + (val >> 16) & 0xffff, + val & 0xffff); + } + if (kind == FWUPD_VERSION_FORMAT_PAIR) { + /* AABBCCDD.EEFFGGHH */ + return g_strdup_printf("%" G_GUINT64_FORMAT ".%" G_GUINT64_FORMAT "", + (val >> 32) & 0xffffffff, + val & 0xffffffff); + } + if (kind == FWUPD_VERSION_FORMAT_NUMBER || kind == FWUPD_VERSION_FORMAT_PLAIN) { + /* AABBCCDD */ + return g_strdup_printf("%" G_GUINT64_FORMAT, val); + } + if (kind == FWUPD_VERSION_FORMAT_HEX) { + /* 0xAABBCCDDEEFFGGHH */ + return g_strdup_printf("0x%08x%08x", + (guint32)(val >> 32), + (guint32)(val & 0xffffffff)); + } + g_critical("failed to convert version format %s: %" G_GUINT64_FORMAT "", + fwupd_version_format_to_string(kind), + val); + return NULL; +} + +/** + * fu_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.8.2 + **/ +gchar * +fu_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 || kind == FWUPD_VERSION_FORMAT_PLAIN) { + /* 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); + } + if (kind == FWUPD_VERSION_FORMAT_SURFACE_LEGACY) { + /* 10b.12b.10b */ + return g_strdup_printf("%u.%u.%u", + (val >> 22) & 0x3ff, + (val >> 10) & 0xfff, + val & 0x3ff); + } + if (kind == FWUPD_VERSION_FORMAT_SURFACE) { + /* 8b.16b.8b */ + return g_strdup_printf("%u.%u.%u", + (val >> 24) & 0xff, + (val >> 8) & 0xffff, + val & 0xff); + } + if (kind == FWUPD_VERSION_FORMAT_DELL_BIOS) { + /* BB.CC.DD */ + return g_strdup_printf("%u.%u.%u", + (val >> 16) & 0xff, + (val >> 8) & 0xff, + val & 0xff); + } + if (kind == FWUPD_VERSION_FORMAT_HEX) { + /* 0xAABBCCDD */ + return g_strdup_printf("0x%08x", val); + } + g_critical("failed to convert version format %s: %u", + fwupd_version_format_to_string(kind), + val); + return NULL; +} + +/** + * fu_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.8.2 + **/ +gchar * +fu_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 || kind == FWUPD_VERSION_FORMAT_PLAIN) { + return g_strdup_printf("%" G_GUINT16_FORMAT, val); + } + if (kind == FWUPD_VERSION_FORMAT_HEX) { + /* 0xAABB */ + return g_strdup_printf("0x%04x", val); + } + g_critical("failed to convert version format %s: %u", + fwupd_version_format_to_string(kind), + val); + return NULL; +} + +static gint +fu_version_compare_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_version_compare_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_version_compare_char(str1[i], str2[i]); + if (rc != 0) + return rc; + } + return fu_version_compare_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 guint +fu_version_format_number_sections(FwupdVersionFormat fmt) +{ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN || fmt == FWUPD_VERSION_FORMAT_NUMBER || + fmt == FWUPD_VERSION_FORMAT_HEX) + return 1; + if (fmt == FWUPD_VERSION_FORMAT_PAIR || fmt == FWUPD_VERSION_FORMAT_BCD) + return 2; + if (fmt == FWUPD_VERSION_FORMAT_TRIPLET || fmt == FWUPD_VERSION_FORMAT_SURFACE_LEGACY || + fmt == FWUPD_VERSION_FORMAT_SURFACE || fmt == FWUPD_VERSION_FORMAT_DELL_BIOS) + return 3; + if (fmt == FWUPD_VERSION_FORMAT_QUAD || fmt == FWUPD_VERSION_FORMAT_INTEL_ME || + fmt == FWUPD_VERSION_FORMAT_INTEL_ME2) + return 4; + return 0; +} + +/** + * fu_version_ensure_semver: + * @version: (nullable): a version number, e.g. ` V1.2.3 ` + * @fmt: a version format, e.g. %FWUPD_VERSION_FORMAT_TRIPLET + * + * Builds a semver from the possibly crazy version number. Depending on the @semver value + * the string will be split and a string in the correct format will be returned. + * + * Returns: a version number, e.g. `1.2.3`, or %NULL if the version was not valid + * + * Since: 1.8.2 + */ +gchar * +fu_version_ensure_semver(const gchar *version, FwupdVersionFormat fmt) +{ + guint sections_actual; + guint sections_expected = fu_version_format_number_sections(fmt); + g_autofree gchar *tmp = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* split into all sections */ + tmp = fu_common_version_ensure_semver(version); + if (tmp == NULL) + return NULL; + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) + return g_steal_pointer(&tmp); + split = g_strsplit(tmp, ".", -1); + sections_actual = g_strv_length(split); + + /* add zero sections as required */ + if (sections_actual < sections_expected) { + for (guint i = 0; i < sections_expected - sections_actual; i++) { + if (str->len > 0) + g_string_append(str, "."); + g_string_append(str, "0"); + } + } + + /* only add enough sections for the format */ + for (guint i = 0; i < sections_actual && i < sections_expected; i++) { + if (str->len > 0) + g_string_append(str, "."); + g_string_append(str, split[i]); + } + + /* success */ + return g_string_free(g_steal_pointer(&str), FALSE); +} + +/** + * fu_common_version_ensure_semver: + * @version: (nullable): 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`, or %NULL if the version was not valid + */ +gchar * +fu_common_version_ensure_semver(const gchar *version) +{ + gboolean dot_valid = FALSE; + guint digit_cnt = 0; + g_autoptr(GString) version_safe = g_string_new(NULL); + + /* invalid */ + if (version == NULL) + return NULL; + + /* hex prefix */ + if (g_str_has_prefix(version, "0x")) { + return fu_version_parse_from_format(version, FWUPD_VERSION_FORMAT_TRIPLET); + } + + /* make sane */ + for (guint i = 0; version[i] != '\0'; i++) { + if (g_ascii_isdigit(version[i])) { + g_string_append_c(version_safe, version[i]); + digit_cnt++; + dot_valid = TRUE; + continue; + } + if (version[i] == '-' || version[i] == '~') { + g_string_append_c(version_safe, '.'); + dot_valid = FALSE; + continue; + } + if (version[i] == '.' && dot_valid && version[i + 1] != '\0') { + g_string_append_c(version_safe, version[i]); + dot_valid = FALSE; + continue; + } + } + + /* remove any trailing dot */ + if (version_safe->len > 0 && version_safe->str[version_safe->len - 1] == '.') + g_string_truncate(version_safe, version_safe->len - 1); + + /* found no digits */ + if (digit_cnt == 0) + return NULL; + return g_string_free(g_steal_pointer(&version_safe), FALSE); +} + +/** + * fu_version_parse_from_format + * @version: (nullable): a version number + * @fmt: a version format, e.g. %FWUPD_VERSION_FORMAT_TRIPLET + * + * 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`, or %NULL on error + * + * Since: 1.8.2 + */ +gchar * +fu_version_parse_from_format(const gchar *version, FwupdVersionFormat fmt) +{ + const gchar *version_noprefix = version; + gchar *endptr = NULL; + guint64 tmp; + guint base; + + /* sanity check */ + if (version == NULL) + return NULL; + + /* 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_version_from_uint32((guint32)tmp, fmt); +} + +/** + * fu_version_guess_format: + * @version: (nullable): 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 version format, e.g. %FWUPD_VERSION_FORMAT_QUAD + * + * Since: 1.8.2 + */ +FwupdVersionFormat +fu_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") || _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_version_format_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_DELL_BIOS) + return FWUPD_VERSION_FORMAT_TRIPLET; + if (fmt == FWUPD_VERSION_FORMAT_BCD) + return FWUPD_VERSION_FORMAT_PAIR; + if (fmt == FWUPD_VERSION_FORMAT_HEX) + return FWUPD_VERSION_FORMAT_NUMBER; + return fmt; +} + +/** + * fu_version_verify_format: + * @version: (not nullable): a string, e.g. `0x1234` + * @fmt: a version format + * @error: (nullable): optional return location for an error + * + * Verifies if a version matches the input format. + * + * Returns: TRUE or FALSE + * + * Since: 1.8.2 + **/ +gboolean +fu_version_verify_format(const gchar *version, FwupdVersionFormat fmt, GError **error) +{ + FwupdVersionFormat fmt_base = fu_version_format_convert_base(fmt); + FwupdVersionFormat fmt_guess; + + g_return_val_if_fail(version != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* don't touch */ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN) + return TRUE; + + /* nothing we can check for */ + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) + return TRUE; + + /* check the base format */ + fmt_guess = fu_version_guess_format(version); + if (fmt_guess != fmt_base) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "%s is not a valid %s (guessed %s)", + version, + fwupd_version_format_to_string(fmt), + fwupd_version_format_to_string(fmt_guess)); + return FALSE; + } + return TRUE; +} + +static gint +fu_version_compare_safe(const gchar *version_a, const gchar *version_b) +{ + guint longest_split; + g_auto(GStrv) split_a = NULL; + g_auto(GStrv) split_b = NULL; + + /* sanity check */ + if (version_a == NULL || version_b == NULL) + return G_MAXINT; + + /* optimization */ + if (g_strcmp0(version_a, version_b) == 0) + return 0; + + /* split into sections, and try to parse */ + 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; + 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_version_compare_chunk(endptr_a, endptr_b); + if (rc < 0) + return -1; + if (rc > 0) + return 1; + } + } + + /* we really shouldn't get here */ + return 0; +} + +/** + * fu_version_compare: + * @version_a: (nullable): the semver release version, e.g. `1.2.3` + * @version_b: (nullable): the semver release version, e.g. `1.2.3.1` + * @fmt: a version format, e.g. %FWUPD_VERSION_FORMAT_PLAIN + * + * Compares version numbers for sorting taking into account the version format + * if required. + * + * Returns: -1 if a < b, +1 if a > b, 0 if they are equal, and %G_MAXINT on error + * + * Since: 1.8.2 + */ +gint +fu_version_compare(const gchar *version_a, const gchar *version_b, FwupdVersionFormat fmt) +{ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN) + return g_strcmp0(version_a, version_b); + if (fmt == FWUPD_VERSION_FORMAT_HEX) { + g_autofree gchar *hex_a = NULL; + g_autofree gchar *hex_b = NULL; + hex_a = fu_version_parse_from_format(version_a, fmt); + hex_b = fu_version_parse_from_format(version_b, fmt); + return fu_version_compare_safe(hex_a, hex_b); + } + return fu_version_compare_safe(version_a, version_b); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-version-common.h b/fwupd-1.8.6/libfwupdplugin/fu-version-common.h new file mode 100644 index 0000000000000000000000000000000000000000..4a36793c5f60d7c3923cc8c037ff250320308fa7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-version-common.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +gint +fu_version_compare(const gchar *version_a, const gchar *version_b, FwupdVersionFormat fmt); +gchar * +fu_version_from_uint64(guint64 val, FwupdVersionFormat kind); +gchar * +fu_version_from_uint32(guint32 val, FwupdVersionFormat kind); +gchar * +fu_version_from_uint16(guint16 val, FwupdVersionFormat kind); +gchar * +fu_version_parse_from_format(const gchar *version, FwupdVersionFormat fmt); +gchar * +fu_version_ensure_semver(const gchar *version, FwupdVersionFormat fmt); +FwupdVersionFormat +fu_version_guess_format(const gchar *version); +gboolean +fu_version_verify_format(const gchar *version, + FwupdVersionFormat fmt, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/libfwupdplugin/fu-version.c b/fwupd-1.8.6/libfwupdplugin/fu-version.c new file mode 100644 index 0000000000000000000000000000000000000000..900dbdb3a610975e43f39d24d044d1af04477085 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-version.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-version.h" + +/** + * fu_version_string: + * + * Gets the libfwupdplugin installed runtime version. + * + * This may be different to the *build-time* version if the daemon and library + * objects somehow get out of sync. + * + * Returns: version string + * + * Since: 1.6.1 + **/ +const gchar * +fu_version_string(void) +{ + return G_STRINGIFY(FU_MAJOR_VERSION) "." G_STRINGIFY(FU_MINOR_VERSION) "." G_STRINGIFY( + FU_MICRO_VERSION); +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-version.h.in b/fwupd-1.8.6/libfwupdplugin/fu-version.h.in new file mode 100644 index 0000000000000000000000000000000000000000..24e8721f284bbb5df49dce275d05b5174501b40a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-version.h.in @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* clang-format off */ +/** + * FU_MAJOR_VERSION: + * + * The compile-time major version + */ +#define FU_MAJOR_VERSION @MAJOR_VERSION@ + +/** + * FU_MINOR_VERSION: + * + * The compile-time minor version + */ +#define FU_MINOR_VERSION @MINOR_VERSION@ + +/** + * FU_MICRO_VERSION: + * + * The compile-time micro version + */ +#define FU_MICRO_VERSION @MICRO_VERSION@ +/* clang-format on */ + +/** + * FU_CHECK_VERSION: + * @major: Major version number + * @minor: Minor version number + * @micro: Micro version number + * + * Check whether a fwupd version equal to or greater than + * major.minor.micro. + * + * These compile time macros allow the user to enable parts of client code + * depending on the version of libfwupd installed. + */ +#define FU_CHECK_VERSION(major, minor, micro) \ + (FU_MAJOR_VERSION > major || \ + (FU_MAJOR_VERSION == major && FU_MINOR_VERSION > minor) || \ + (FU_MAJOR_VERSION == major && FU_MINOR_VERSION == minor && \ + FU_MICRO_VERSION >= micro)) + +const gchar * +fu_version_string(void); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-volume-private.h b/fwupd-1.8.6/libfwupdplugin/fu-volume-private.h new file mode 100644 index 0000000000000000000000000000000000000000..7bec07b1a2b31e4db006398bc71eb46526e41a00 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-volume-private.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-volume.h" + +FuVolume * +fu_volume_new_from_mount_path(const gchar *mount_path); diff --git a/fwupd-1.8.6/libfwupdplugin/fu-volume.c b/fwupd-1.8.6/libfwupdplugin/fu-volume.c new file mode 100644 index 0000000000000000000000000000000000000000..810f9aad8f08e4f34fc052b8ac65ea07173c2a5b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-volume.c @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuVolume" + +#include "config.h" + +#include + +#include "fwupd-error.h" + +#include "fu-common-private.h" +#include "fu-volume-private.h" + +/** + * FuVolume: + * + * Volume abstraction that uses UDisks + */ + +struct _FuVolume { + GObject parent_instance; + GDBusProxy *proxy_blk; + GDBusProxy *proxy_fs; + gchar *mount_path; /* only when mounted ourselves */ +}; + +enum { PROP_0, PROP_MOUNT_PATH, PROP_PROXY_BLOCK, PROP_PROXY_FILESYSTEM, PROP_LAST }; + +G_DEFINE_TYPE(FuVolume, fu_volume, G_TYPE_OBJECT) + +static void +fu_volume_finalize(GObject *obj) +{ + FuVolume *self = FU_VOLUME(obj); + g_free(self->mount_path); + if (self->proxy_blk != NULL) + g_object_unref(self->proxy_blk); + if (self->proxy_fs != NULL) + g_object_unref(self->proxy_fs); + G_OBJECT_CLASS(fu_volume_parent_class)->finalize(obj); +} + +static void +fu_volume_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuVolume *self = FU_VOLUME(object); + switch (prop_id) { + case PROP_MOUNT_PATH: + g_value_set_string(value, self->mount_path); + break; + case PROP_PROXY_BLOCK: + g_value_set_object(value, self->proxy_blk); + break; + case PROP_PROXY_FILESYSTEM: + g_value_set_object(value, self->proxy_fs); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_volume_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuVolume *self = FU_VOLUME(object); + switch (prop_id) { + case PROP_MOUNT_PATH: + self->mount_path = g_value_dup_string(value); + break; + case PROP_PROXY_BLOCK: + self->proxy_blk = g_value_dup_object(value); + break; + case PROP_PROXY_FILESYSTEM: + self->proxy_fs = g_value_dup_object(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_volume_class_init(FuVolumeClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_volume_finalize; + object_class->get_property = fu_volume_get_property; + object_class->set_property = fu_volume_set_property; + + /** + * FuVolume:proxy-block: + * + * The proxy for the block interface. + * + * Since: 1.4.6 + */ + pspec = + g_param_spec_object("proxy-block", + NULL, + NULL, + G_TYPE_DBUS_PROXY, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PROXY_BLOCK, pspec); + + /** + * FuVolume:proxy-filesystem: + * + * The proxy for the filesystem interface. + * + * Since: 1.4.6 + */ + pspec = + g_param_spec_object("proxy-filesystem", + NULL, + NULL, + G_TYPE_DBUS_PROXY, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_PROXY_FILESYSTEM, pspec); + + /** + * FuVolume:mount-path: + * + * The UNIX mount path. + * + * Since: 1.4.6 + */ + pspec = + g_param_spec_string("mount-path", + NULL, + NULL, + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_MOUNT_PATH, pspec); +} + +static void +fu_volume_init(FuVolume *self) +{ +} + +/** + * fu_volume_get_id: + * @self: a @FuVolume + * + * Gets the D-Bus path of the mount point. + * + * Returns: string ID, or %NULL + * + * Since: 1.4.6 + **/ +const gchar * +fu_volume_get_id(FuVolume *self) +{ + g_return_val_if_fail(FU_IS_VOLUME(self), NULL); + if (self->proxy_fs != NULL) + return g_dbus_proxy_get_object_path(self->proxy_fs); + if (self->proxy_blk != NULL) + return g_dbus_proxy_get_object_path(self->proxy_blk); + return NULL; +} + +/** + * fu_volume_get_mount_point: + * @self: a @FuVolume + * + * Gets the location of the volume mount point. + * + * Returns: UNIX path, or %NULL + * + * Since: 1.4.6 + **/ +gchar * +fu_volume_get_mount_point(FuVolume *self) +{ + g_autofree const gchar **mountpoints = NULL; + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail(FU_IS_VOLUME(self), NULL); + + /* we mounted it */ + if (self->mount_path != NULL) + return g_strdup(self->mount_path); + + /* something else mounted it */ + if (self->proxy_fs == NULL) + return NULL; + val = g_dbus_proxy_get_cached_property(self->proxy_fs, "MountPoints"); + if (val == NULL) + return NULL; + mountpoints = g_variant_get_bytestring_array(val, NULL); + return g_strdup(mountpoints[0]); +} + +/** + * fu_volume_check_free_space: + * @self: a @FuVolume + * @required: size in bytes + * @error: (nullable): optional return location for an error + * + * Checks the volume for required space. + * + * Returns: %TRUE for success + * + * Since: 1.4.6 + **/ +gboolean +fu_volume_check_free_space(FuVolume *self, guint64 required, GError **error) +{ + guint64 fs_free; + g_autofree gchar *path = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + + g_return_val_if_fail(FU_IS_VOLUME(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* skip the checks for unmounted disks */ + path = fu_volume_get_mount_point(self); + if (path == NULL) + return TRUE; + + 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; +} + +/** + * fu_volume_is_mounted: + * @self: a @FuVolume + * + * Checks if the VOLUME is already mounted. + * + * Returns: %TRUE for success + * + * Since: 1.4.6 + **/ +gboolean +fu_volume_is_mounted(FuVolume *self) +{ + g_autofree gchar *mount_point = NULL; + g_return_val_if_fail(FU_IS_VOLUME(self), FALSE); + mount_point = fu_volume_get_mount_point(self); + return mount_point != NULL; +} + +/** + * fu_volume_is_encrypted: + * @self: a @FuVolume + * + * Checks if the VOLUME is currently encrypted. + * + * Returns: %TRUE for success + * + * Since: 1.5.1 + **/ +gboolean +fu_volume_is_encrypted(FuVolume *self) +{ + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail(FU_IS_VOLUME(self), FALSE); + + if (self->proxy_blk == NULL) + return FALSE; + val = g_dbus_proxy_get_cached_property(self->proxy_blk, "CryptoBackingDevice"); + if (val == NULL) + return FALSE; + if (g_strcmp0(g_variant_get_string(val, NULL), "/") == 0) + return FALSE; + return TRUE; +} + +/** + * fu_volume_mount: + * @self: a @FuVolume + * @error: (nullable): optional return location for an error + * + * Mounts the VOLUME ready for use. + * + * Returns: %TRUE for success + * + * Since: 1.4.6 + **/ +gboolean +fu_volume_mount(FuVolume *self, GError **error) +{ + GVariantBuilder builder; + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail(FU_IS_VOLUME(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* device from the self tests */ + if (self->proxy_fs == NULL) + return TRUE; + + g_debug("mounting %s", fu_volume_get_id(self)); + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + val = g_dbus_proxy_call_sync(self->proxy_fs, + "Mount", + g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (val == NULL) + return FALSE; + g_variant_get(val, "(s)", &self->mount_path); + return TRUE; +} + +/** + * fu_volume_is_internal: + * @self: a @FuVolume + * + * Guesses if the drive is internal to the system + * + * Returns: %TRUE for success + * + * Since: 1.5.2 + **/ +gboolean +fu_volume_is_internal(FuVolume *self) +{ + g_autoptr(GVariant) val_system = NULL; + g_return_val_if_fail(FU_IS_VOLUME(self), FALSE); + + val_system = g_dbus_proxy_get_cached_property(self->proxy_blk, "HintSystem"); + if (val_system == NULL) + return FALSE; + + return g_variant_get_boolean(val_system); +} + +/** + * fu_volume_get_id_type: + * @self: a @FuVolume + * + * Return the IdType of the volume + * + * Returns: string for type or NULL + * + * Since: 1.5.2 + **/ +gchar * +fu_volume_get_id_type(FuVolume *self) +{ + g_autoptr(GVariant) val = NULL; + g_return_val_if_fail(FU_IS_VOLUME(self), NULL); + + val = g_dbus_proxy_get_cached_property(self->proxy_blk, "IdType"); + if (val == NULL) + return NULL; + + return g_strdup(g_variant_get_string(val, NULL)); +} + +/** + * fu_volume_unmount: + * @self: a @FuVolume + * @error: (nullable): optional return location for an error + * + * Unmounts the volume after use. + * + * Returns: %TRUE for success + * + * Since: 1.4.6 + **/ +gboolean +fu_volume_unmount(FuVolume *self, GError **error) +{ + GVariantBuilder builder; + g_autoptr(GVariant) val = NULL; + + g_return_val_if_fail(FU_IS_VOLUME(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* device from the self tests */ + if (self->proxy_fs == NULL) + return TRUE; + + g_debug("unmounting %s", fu_volume_get_id(self)); + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + val = g_dbus_proxy_call_sync(self->proxy_fs, + "Unmount", + g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (val == NULL) + return FALSE; + g_free(self->mount_path); + self->mount_path = NULL; + return TRUE; +} + +/** + * fu_volume_locker: + * @self: a @FuVolume + * @error: (nullable): optional return location for an error + * + * Locks the volume, mounting it and unmounting it as required. If the volume is + * already mounted then it is is _not_ unmounted when the locker is closed. + * + * Returns: (transfer full): a device locker for success, or %NULL + * + * Since: 1.4.6 + **/ +FuDeviceLocker * +fu_volume_locker(FuVolume *self, GError **error) +{ + g_return_val_if_fail(FU_IS_VOLUME(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* already open, so NOP */ + if (fu_volume_is_mounted(self)) + return g_object_new(FU_TYPE_DEVICE_LOCKER, NULL); + return fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_volume_mount, + (FuDeviceLockerFunc)fu_volume_unmount, + error); +} + +/* private */ +FuVolume * +fu_volume_new_from_mount_path(const gchar *mount_path) +{ + g_autoptr(FuVolume) self = g_object_new(FU_TYPE_VOLUME, NULL); + g_return_val_if_fail(mount_path != NULL, NULL); + self->mount_path = g_strdup(mount_path); + return g_steal_pointer(&self); +} + +/** + * fu_volume_kind_convert_to_gpt: + * @kind: UDisk reported type string, e.g. `efi` or `0xef` + * + * Converts a MBR type to a GPT type. + * + * Returns: the GPT type, usually a GUID. If not known @kind is returned. + * + * Since: 1.8.6 + **/ +const gchar * +fu_volume_kind_convert_to_gpt(const gchar *kind) +{ + struct { + const gchar *gpt; + const gchar *mbrs[6]; + } typeguids[] = {{"c12a7328-f81f-11d2-ba4b-00a0c93ec93b", /* esp */ + {"0xef", "efi", NULL}}, + {"ebd0a0a2-b9e5-4433-87c0-68b6b72699c7", /* fat32 */ + {"0x0b", "0x06", "vfat", "fat32", "fat32lba", NULL}}, + {NULL, {NULL}}}; + for (guint i = 0; typeguids[i].gpt != NULL; i++) { + for (guint j = 0; typeguids[i].mbrs[j] != NULL; j++) { + if (g_strcmp0(kind, typeguids[i].mbrs[j]) == 0) + return typeguids[i].gpt; + } + } + return kind; +} + +/** + * fu_volume_new_by_kind: + * @kind: a volume kind, typically a GUID + * @error: (nullable): optional return location for an error + * + * Finds all volumes of a specific partition type + * + * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not + *found + * + * Since: 1.8.2 + **/ +GPtrArray * +fu_volume_new_by_kind(const gchar *kind, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) volumes = NULL; + + g_return_val_if_fail(kind != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + devices = fu_common_get_block_devices(error); + if (devices == NULL) + return NULL; + volumes = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < devices->len; i++) { + GDBusProxy *proxy_blk = g_ptr_array_index(devices, i); + const gchar *type_str; + g_autoptr(FuVolume) vol = NULL; + g_autoptr(GDBusProxy) proxy_part = NULL; + g_autoptr(GDBusProxy) proxy_fs = NULL; + g_autoptr(GVariant) val = NULL; + + proxy_part = g_dbus_proxy_new_sync(g_dbus_proxy_get_connection(proxy_blk), + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + g_dbus_proxy_get_object_path(proxy_blk), + UDISKS_DBUS_INTERFACE_PARTITION, + NULL, + error); + if (proxy_part == NULL) { + g_prefix_error(error, + "failed to initialize d-bus proxy %s: ", + g_dbus_proxy_get_object_path(proxy_blk)); + return NULL; + } + val = g_dbus_proxy_get_cached_property(proxy_part, "Type"); + if (val == NULL) + continue; + + g_variant_get(val, "&s", &type_str); + proxy_fs = g_dbus_proxy_new_sync(g_dbus_proxy_get_connection(proxy_blk), + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + g_dbus_proxy_get_object_path(proxy_blk), + UDISKS_DBUS_INTERFACE_FILESYSTEM, + NULL, + error); + if (proxy_fs == NULL) { + g_prefix_error(error, + "failed to initialize d-bus proxy %s: ", + g_dbus_proxy_get_object_path(proxy_blk)); + return NULL; + } + vol = g_object_new(FU_TYPE_VOLUME, + "proxy-block", + proxy_blk, + "proxy-filesystem", + proxy_fs, + NULL); + + /* convert reported type to GPT type */ + type_str = fu_volume_kind_convert_to_gpt(type_str); + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *id_type = fu_volume_get_id_type(vol); + g_debug("device %s, type: %s, internal: %d, fs: %s", + g_dbus_proxy_get_object_path(proxy_blk), + type_str, + fu_volume_is_internal(vol), + id_type); + } + if (g_strcmp0(type_str, kind) != 0) + continue; + g_ptr_array_add(volumes, g_steal_pointer(&vol)); + } + if (volumes->len == 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no volumes of type %s", kind); + return NULL; + } + return g_steal_pointer(&volumes); +} + +/** + * fu_volume_new_by_device: + * @device: a device string, typically starting with `/dev/` + * @error: (nullable): optional return location for an error + * + * Finds the first volume from the specified device. + * + * Returns: (transfer full): a volume, or %NULL if the device was not found + * + * Since: 1.8.2 + **/ +FuVolume * +fu_volume_new_by_device(const gchar *device, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + g_return_val_if_fail(device != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find matching block device */ + devices = fu_common_get_block_devices(error); + if (devices == NULL) + return NULL; + for (guint i = 0; i < devices->len; i++) { + GDBusProxy *proxy_blk = g_ptr_array_index(devices, i); + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy_blk, "Device"); + if (val == NULL) + continue; + if (g_strcmp0(g_variant_get_bytestring(val), device) == 0) { + g_autoptr(GDBusProxy) proxy_fs = NULL; + g_autoptr(GError) error_local = NULL; + proxy_fs = g_dbus_proxy_new_sync(g_dbus_proxy_get_connection(proxy_blk), + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + g_dbus_proxy_get_object_path(proxy_blk), + UDISKS_DBUS_INTERFACE_FILESYSTEM, + NULL, + &error_local); + if (proxy_fs == NULL) + g_debug("ignoring: %s", error_local->message); + return g_object_new(FU_TYPE_VOLUME, + "proxy-block", + proxy_blk, + "proxy-filesystem", + proxy_fs, + NULL); + } + } + + /* failed */ + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no volumes for device %s", device); + return NULL; +} + +/** + * fu_volume_new_by_devnum: + * @devnum: a device number + * @error: (nullable): optional return location for an error + * + * Finds the first volume from the specified device. + * + * Returns: (transfer full): a volume, or %NULL if the device was not found + * + * Since: 1.8.2 + **/ +FuVolume * +fu_volume_new_by_devnum(guint32 devnum, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find matching block device */ + devices = fu_common_get_block_devices(error); + if (devices == NULL) + return NULL; + for (guint i = 0; i < devices->len; i++) { + GDBusProxy *proxy_blk = g_ptr_array_index(devices, i); + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property(proxy_blk, "DeviceNumber"); + if (val == NULL) + continue; + if (devnum == g_variant_get_uint64(val)) { + return g_object_new(FU_TYPE_VOLUME, "proxy-block", proxy_blk, NULL); + } + } + + /* failed */ + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no volumes for devnum %u", devnum); + return NULL; +} + +/** + * fu_volume_new_esp_for_path: + * @esp_path: a path to the ESP + * @error: (nullable): optional return location for an error + * + * Gets the platform ESP using a UNIX or UDisks path + * + * Returns: (transfer full): a #volume, or %NULL if the ESP was not found + * + * Since: 1.8.2 + **/ +FuVolume * +fu_volume_new_esp_for_path(const gchar *esp_path, GError **error) +{ + g_autofree gchar *basename = NULL; + g_autoptr(GPtrArray) volumes = NULL; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(esp_path != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + volumes = fu_volume_new_by_kind(FU_VOLUME_KIND_ESP, &error_local); + if (volumes == NULL) { + /* check if it's a valid directory already */ + if (g_file_test(esp_path, G_FILE_TEST_IS_DIR)) + return fu_volume_new_from_mount_path(esp_path); + g_propagate_error(error, g_steal_pointer(&error_local)); + return NULL; + } + basename = g_path_get_basename(esp_path); + for (guint i = 0; i < volumes->len; i++) { + FuVolume *vol = g_ptr_array_index(volumes, i); + const gchar *mount_point = fu_volume_get_mount_point(vol); + g_autofree gchar *vol_basename = NULL; + if (mount_point == NULL) + continue; + vol_basename = g_path_get_basename(mount_point); + if (g_strcmp0(basename, vol_basename) == 0) + return g_object_ref(vol); + } + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_FILENAME, + "No ESP with path %s", + esp_path); + return NULL; +} diff --git a/fwupd-1.8.6/libfwupdplugin/fu-volume.h b/fwupd-1.8.6/libfwupdplugin/fu-volume.h new file mode 100644 index 0000000000000000000000000000000000000000..c3e09f7ab833aaf88a691b1dfc576da18460f463 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fu-volume.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-device-locker.h" + +#define FU_TYPE_VOLUME (fu_volume_get_type()) + +G_DECLARE_FINAL_TYPE(FuVolume, fu_volume, FU, VOLUME, GObject) + +/** + * FU_VOLUME_KIND_ESP: + * + * The GUID for the ESP, see: https://en.wikipedia.org/wiki/EFI_system_partition + * + * Since: 1.5.0 + **/ +#define FU_VOLUME_KIND_ESP "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" +/** + * FU_VOLUME_KIND_BDP: + * + * The GUID for the BDP, see: https://en.wikipedia.org/wiki/Microsoft_basic_data_partition + * + * Since: 1.5.3 + **/ +#define FU_VOLUME_KIND_BDP "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" + +const gchar * +fu_volume_get_id(FuVolume *self); +gboolean +fu_volume_check_free_space(FuVolume *self, + guint64 required, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_volume_is_mounted(FuVolume *self); +gboolean +fu_volume_is_encrypted(FuVolume *self); +gchar * +fu_volume_get_mount_point(FuVolume *self); +gboolean +fu_volume_mount(FuVolume *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_volume_unmount(FuVolume *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +FuDeviceLocker * +fu_volume_locker(FuVolume *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_volume_is_internal(FuVolume *self); +gchar * +fu_volume_get_id_type(FuVolume *self); +GPtrArray * +fu_volume_new_by_kind(const gchar *kind, GError **error) G_GNUC_WARN_UNUSED_RESULT; +FuVolume * +fu_volume_new_by_device(const gchar *device, GError **error) G_GNUC_WARN_UNUSED_RESULT; +FuVolume * +fu_volume_new_by_devnum(guint32 devnum, GError **error) G_GNUC_WARN_UNUSED_RESULT; +FuVolume * +fu_volume_new_esp_for_path(const gchar *esp_path, GError **error) G_GNUC_WARN_UNUSED_RESULT; +const gchar * +fu_volume_kind_convert_to_gpt(const gchar *kind); diff --git a/fwupd-1.8.6/libfwupdplugin/fwupdplugin.h b/fwupd-1.8.6/libfwupdplugin/fwupdplugin.h new file mode 100644 index 0000000000000000000000000000000000000000..9a807485341ee6ef75cfa6305460aa7cb87a2930 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fwupdplugin.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#define __FWUPDPLUGIN_H_INSIDE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FWUPD_DISABLE_DEPRECATED +#include +#endif + +#undef __FWUPDPLUGIN_H_INSIDE__ diff --git a/fwupd-1.8.6/libfwupdplugin/fwupdplugin.map b/fwupd-1.8.6/libfwupdplugin/fwupdplugin.map new file mode 100644 index 0000000000000000000000000000000000000000..48155e197652c10e9f281ec61f0877d786b02674 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/fwupdplugin.map @@ -0,0 +1,1138 @@ +# generated automatically, do not edit! + +LIBFWUPDPLUGIN_0.1.0 { + global: + fu_device_add_flag; + fu_device_get_metadata; + fu_device_get_type; + fu_device_set_metadata; + local: *; +}; + +LIBFWUPDPLUGIN_0.6.1 { + global: + fu_device_get_equivalent_id; + fu_device_set_equivalent_id; + local: *; +} LIBFWUPDPLUGIN_0.1.0; + +LIBFWUPDPLUGIN_0.7.1 { + global: + fu_device_set_id; + fu_device_set_name; + local: *; +} LIBFWUPDPLUGIN_0.6.1; + +LIBFWUPDPLUGIN_0.7.2 { + global: + fu_device_add_guid; + fu_device_get_alternate; + fu_device_set_alternate; + local: *; +} LIBFWUPDPLUGIN_0.7.1; + +LIBFWUPDPLUGIN_0.8.0 { + global: + fu_plugin_alloc_data; + fu_plugin_cache_add; + fu_plugin_cache_lookup; + fu_plugin_cache_remove; + fu_plugin_device_add; + fu_plugin_device_remove; + fu_plugin_get_data; + fu_plugin_get_name; + fu_plugin_get_type; + fu_plugin_new; + fu_plugin_open; + fu_plugin_runner_clear_results; + fu_plugin_runner_coldplug; + fu_plugin_runner_get_results; + fu_plugin_runner_startup; + fu_plugin_runner_unlock; + fu_plugin_runner_verify; + fu_plugin_set_name; + local: *; +} LIBFWUPDPLUGIN_0.7.2; + +LIBFWUPDPLUGIN_0.9.3 { + global: + fu_hwids_get_guid; + fu_hwids_get_guids; + fu_hwids_get_replace_keys; + fu_hwids_get_replace_values; + fu_hwids_get_type; + fu_hwids_get_value; + fu_hwids_has_guid; + fu_hwids_new; + fu_hwids_setup; + local: *; +} LIBFWUPDPLUGIN_0.8.0; + +LIBFWUPDPLUGIN_0.9.7 { + global: + fu_device_get_metadata_boolean; + fu_device_get_metadata_integer; + fu_device_set_metadata_boolean; + fu_device_set_metadata_integer; + fu_plugin_device_register; + fu_plugin_runner_device_register; + local: *; +} LIBFWUPDPLUGIN_0.9.3; + +LIBFWUPDPLUGIN_0.9.8 { + global: + fu_device_to_string; + local: *; +} LIBFWUPDPLUGIN_0.9.7; + +LIBFWUPDPLUGIN_1.0.0 { + global: + fu_device_locker_get_type; + fu_device_locker_new; + fu_device_locker_new_full; + fu_plugin_add_rule; + fu_plugin_get_order; + fu_plugin_get_rules; + fu_plugin_has_rule; + fu_plugin_set_order; + fu_plugin_set_priority; + fu_smbios_get_data; + fu_smbios_get_string; + fu_smbios_get_type; + fu_smbios_new; + fu_smbios_setup; + fu_smbios_setup_from_file; + fu_smbios_setup_from_path; + local: *; +} LIBFWUPDPLUGIN_0.9.8; + +LIBFWUPDPLUGIN_1.0.1 { + global: + fu_chunk_array_to_string; + fu_quirks_get_type; + fu_quirks_load; + fu_quirks_lookup_by_id; + fu_quirks_new; + local: *; +} LIBFWUPDPLUGIN_1.0.0; + +LIBFWUPDPLUGIN_1.0.2 { + global: + fu_device_get_remove_delay; + fu_device_set_remove_delay; + fu_usb_device_get_dev; + fu_usb_device_get_type; + fu_usb_device_set_dev; + local: *; +} LIBFWUPDPLUGIN_1.0.1; + +LIBFWUPDPLUGIN_1.0.3 { + global: + fu_usb_device_is_open; + local: *; +} LIBFWUPDPLUGIN_1.0.2; + +LIBFWUPDPLUGIN_1.0.4 { + global: + fu_plugin_add_report_metadata; + fu_plugin_get_report_metadata; + local: *; +} LIBFWUPDPLUGIN_1.0.3; + +LIBFWUPDPLUGIN_1.0.5 { + global: + fu_device_get_release_default; + local: *; +} LIBFWUPDPLUGIN_1.0.4; + +LIBFWUPDPLUGIN_1.0.6 { + global: + fu_plugin_get_config_value; + local: *; +} LIBFWUPDPLUGIN_1.0.5; + +LIBFWUPDPLUGIN_1.0.8 { + global: + fu_device_add_child; + fu_device_add_parent_guid; + fu_device_attach; + fu_device_detach; + fu_device_get_children; + fu_device_get_guids_as_str; + fu_device_get_order; + fu_device_get_parent; + fu_device_get_parent_guids; + fu_device_has_parent_guid; + fu_device_read_firmware; + fu_device_set_order; + fu_device_set_parent; + fu_device_write_firmware; + fu_plugin_guess_name_from_fn; + fu_plugin_name_compare; + fu_plugin_order_compare; + local: *; +} LIBFWUPDPLUGIN_1.0.6; + +LIBFWUPDPLUGIN_1.0.9 { + global: + fu_plugin_runner_composite_cleanup; + fu_plugin_runner_composite_prepare; + local: *; +} LIBFWUPDPLUGIN_1.0.8; + +LIBFWUPDPLUGIN_1.1.0 { + global: + fu_device_get_alternate_id; + fu_device_get_custom_flags; + fu_device_incorporate; + fu_device_set_alternate_id; + fu_device_set_custom_flags; + local: *; +} LIBFWUPDPLUGIN_1.0.9; + +LIBFWUPDPLUGIN_1.1.1 { + global: + fu_device_get_priority; + fu_device_set_priority; + fu_plugin_get_priority; + local: *; +} LIBFWUPDPLUGIN_1.1.0; + +LIBFWUPDPLUGIN_1.1.2 { + global: + fu_chunk_array_new; + fu_chunk_array_new_from_bytes; + fu_chunk_new; + fu_chunk_to_string; + fu_device_add_counterpart_guid; + fu_device_close; + fu_device_ensure_id; + fu_device_get_logical_id; + fu_device_get_physical_id; + fu_device_open; + fu_device_poll; + fu_device_prepare_firmware; + fu_device_probe; + fu_device_probe_invalidate; + fu_device_set_firmware_size_max; + fu_device_set_firmware_size_min; + fu_device_set_logical_id; + fu_device_set_physical_id; + fu_device_set_poll_interval; + fu_device_setup; + fu_plugin_runner_device_removed; + fu_udev_device_emit_changed; + fu_udev_device_get_dev; + fu_udev_device_get_model; + fu_udev_device_get_revision; + fu_udev_device_get_subsystem; + fu_udev_device_get_sysfs_path; + fu_udev_device_get_type; + fu_udev_device_get_vendor; + fu_udev_device_set_physical_id; + fu_usb_device_get_pid; + fu_usb_device_get_platform_id; + fu_usb_device_get_vid; + local: *; +} LIBFWUPDPLUGIN_1.1.1; + +LIBFWUPDPLUGIN_1.2.2 { + global: + fu_archive_get_type; + fu_archive_lookup_by_fn; + fu_archive_new; + fu_device_has_guid; + fu_io_channel_get_type; + fu_io_channel_new_file; + fu_io_channel_read_bytes; + fu_io_channel_read_raw; + fu_io_channel_shutdown; + fu_io_channel_unix_get_fd; + fu_io_channel_unix_new; + fu_io_channel_write_bytes; + fu_io_channel_write_raw; + local: *; +} LIBFWUPDPLUGIN_1.1.2; + +LIBFWUPDPLUGIN_1.2.4 { + global: + fu_device_incorporate_from_component; + fu_udev_device_get_slot_depth; + local: *; +} LIBFWUPDPLUGIN_1.2.2; + +LIBFWUPDPLUGIN_1.2.5 { + global: + fu_common_guid_is_plausible; + fu_device_add_instance_id; + fu_device_convert_instance_ids; + local: *; +} LIBFWUPDPLUGIN_1.2.4; + +LIBFWUPDPLUGIN_1.2.6 { + global: + fu_device_activate; + fu_device_get_firmware_size_max; + fu_device_get_firmware_size_min; + fu_device_set_firmware_size; + fu_plugin_runner_activate; + local: *; +} LIBFWUPDPLUGIN_1.2.5; + +LIBFWUPDPLUGIN_1.2.9 { + global: + fu_device_add_instance_id_full; + fu_device_set_version; + local: *; +} LIBFWUPDPLUGIN_1.2.6; + +LIBFWUPDPLUGIN_1.3.1 { + global: + fu_byte_array_append_uint16; + fu_byte_array_append_uint32; + fu_byte_array_append_uint8; + fu_device_rescan; + fu_firmware_add_image; + fu_firmware_get_image_by_id; + fu_firmware_get_image_by_id_bytes; + fu_firmware_get_image_by_idx; + fu_firmware_get_image_by_idx_bytes; + fu_firmware_get_images; + fu_firmware_get_type; + fu_firmware_new; + fu_firmware_new_from_bytes; + fu_firmware_parse; + fu_firmware_to_string; + fu_firmware_write; + fu_ihex_firmware_get_type; + fu_ihex_firmware_new; + fu_udev_device_get_device_file; + local: *; +} LIBFWUPDPLUGIN_1.2.9; + +LIBFWUPDPLUGIN_1.3.2 { + global: + fu_firmware_tokenize; + fu_io_channel_read_byte_array; + fu_io_channel_write_byte_array; + fu_srec_firmware_get_records; + fu_srec_firmware_get_type; + fu_srec_firmware_new; + fu_srec_firmware_record_new; + fu_usb_device_find_udev_device; + local: *; +} LIBFWUPDPLUGIN_1.3.1; + +LIBFWUPDPLUGIN_1.3.3 { + global: + fu_device_cleanup; + fu_device_get_possible_plugins; + fu_device_get_specialized_gtype; + fu_device_prepare; + fu_device_reload; + fu_device_remove_metadata; + fu_dfu_firmware_get_pid; + fu_dfu_firmware_get_release; + fu_dfu_firmware_get_type; + fu_dfu_firmware_get_version; + fu_dfu_firmware_get_vid; + fu_dfu_firmware_new; + fu_dfu_firmware_set_pid; + fu_dfu_firmware_set_release; + fu_dfu_firmware_set_version; + fu_dfu_firmware_set_vid; + fu_firmware_get_version; + fu_firmware_parse_file; + fu_firmware_set_version; + fu_firmware_write_file; + fu_plugin_add_firmware_gtype; + fu_quirks_lookup_by_id_iter; + fu_udev_device_get_fd; + fu_udev_device_set_fd; + local: *; +} LIBFWUPDPLUGIN_1.3.2; + +LIBFWUPDPLUGIN_1.3.4 { + global: + fu_archive_iterate; + fu_ihex_firmware_get_records; + fu_usb_device_get_spec; + local: *; +} LIBFWUPDPLUGIN_1.3.3; + +LIBFWUPDPLUGIN_1.3.5 { + global: + fu_device_incorporate_flag; + fu_plugin_is_open; + local: *; +} LIBFWUPDPLUGIN_1.3.4; + +LIBFWUPDPLUGIN_1.3.6 { + global: + fu_udev_device_set_flags; + local: *; +} LIBFWUPDPLUGIN_1.3.5; + +LIBFWUPDPLUGIN_1.4.0 { + global: + fu_cabinet_get_silo; + fu_cabinet_get_type; + fu_cabinet_new; + fu_cabinet_parse; + fu_cabinet_set_jcat_context; + fu_cabinet_set_size_max; + fu_device_get_root; + fu_device_locker_close; + fu_device_retry; + fu_device_retry_add_recovery; + fu_device_retry_set_delay; + fu_device_set_version_bootloader; + fu_device_set_version_format; + fu_device_set_version_lowest; + fu_efivar_delete; + fu_efivar_delete_with_glob; + fu_efivar_exists; + fu_efivar_get_data; + fu_efivar_set_data; + fu_efivar_supported; + fu_hid_device_get_interface; + fu_hid_device_get_report; + fu_hid_device_get_type; + fu_hid_device_new; + fu_hid_device_set_interface; + fu_hid_device_set_report; + fu_plugin_get_config_value_boolean; + fu_plugin_runner_device_created; + local: *; +} LIBFWUPDPLUGIN_1.3.6; + +LIBFWUPDPLUGIN_1.4.1 { + global: + fu_device_get_proxy; + fu_device_get_proxy_guid; + fu_device_set_proxy; + fu_device_set_proxy_guid; + local: *; +} LIBFWUPDPLUGIN_1.4.0; + +LIBFWUPDPLUGIN_1.4.5 { + global: + fu_udev_device_get_devtype; + fu_udev_device_get_parent_name; + fu_udev_device_get_sysfs_attr; + fu_udev_device_write_sysfs; + local: *; +} LIBFWUPDPLUGIN_1.4.1; + +LIBFWUPDPLUGIN_1.4.6 { + global: + fu_common_is_live_media; + fu_volume_check_free_space; + fu_volume_get_id; + fu_volume_get_mount_point; + fu_volume_get_type; + fu_volume_is_mounted; + fu_volume_locker; + fu_volume_mount; + fu_volume_unmount; + local: *; +} LIBFWUPDPLUGIN_1.4.5; + +LIBFWUPDPLUGIN_1.4.7 { + global: + fu_efivar_get_names; + local: *; +} LIBFWUPDPLUGIN_1.4.6; + +LIBFWUPDPLUGIN_1.5.0 { + global: + fu_device_bind_driver; + fu_device_dump_firmware; + fu_device_report_metadata_post; + fu_device_report_metadata_pre; + fu_device_unbind_driver; + fu_efivar_get_data_bytes; + fu_efivar_set_data_bytes; + fu_firmware_add_flag; + fu_firmware_build; + fu_firmware_flag_from_string; + fu_firmware_flag_to_string; + fu_firmware_has_flag; + fu_firmware_remove_image; + fu_firmware_remove_image_by_id; + fu_firmware_remove_image_by_idx; + fu_fmap_firmware_get_type; + fu_fmap_firmware_new; + fu_plugin_runner_add_security_attrs; + fu_plugin_runner_device_added; + fu_security_attrs_append; + fu_security_attrs_calculate_hsi; + fu_security_attrs_depsolve; + fu_security_attrs_get_all; + fu_security_attrs_get_type; + fu_security_attrs_new; + fu_security_attrs_remove_all; + fu_security_attrs_to_variant; + fu_smbios_get_integer; + fu_udev_device_get_number; + fu_udev_device_get_subsystem_model; + fu_udev_device_get_subsystem_vendor; + local: *; +} LIBFWUPDPLUGIN_1.4.7; + +LIBFWUPDPLUGIN_1.5.1 { + global: + fu_device_add_possible_plugin; + fu_efivar_space_used; + fu_volume_is_encrypted; + local: *; +} LIBFWUPDPLUGIN_1.5.0; + +LIBFWUPDPLUGIN_1.5.2 { + global: + fu_hid_device_add_flag; + fu_volume_get_id_type; + fu_volume_is_internal; + local: *; +} LIBFWUPDPLUGIN_1.5.1; + +LIBFWUPDPLUGIN_1.5.3 { + global: + fu_udev_device_get_driver; + local: *; +} LIBFWUPDPLUGIN_1.5.2; + +LIBFWUPDPLUGIN_1.5.5 { + global: + fu_device_add_internal_flag; + fu_device_has_internal_flag; + fu_device_internal_flag_from_string; + fu_device_internal_flag_to_string; + fu_device_remove_internal_flag; + fu_device_retry_full; + fu_efi_signature_get_kind; + fu_efi_signature_get_owner; + fu_efi_signature_get_type; + fu_efi_signature_kind_to_string; + fu_efi_signature_list_get_type; + fu_efi_signature_list_new; + fu_efivar_get_monitor; + fu_firmware_get_image_by_checksum; + local: *; +} LIBFWUPDPLUGIN_1.5.3; + +LIBFWUPDPLUGIN_1.5.6 { + global: + fu_chunk_array_mutable_new; + fu_chunk_bytes_new; + fu_chunk_get_address; + fu_chunk_get_bytes; + fu_chunk_get_data; + fu_chunk_get_data_out; + fu_chunk_get_data_sz; + fu_chunk_get_idx; + fu_chunk_get_page; + fu_chunk_get_type; + fu_chunk_set_address; + fu_chunk_set_bytes; + fu_chunk_set_idx; + fu_chunk_set_page; + fu_common_get_memory_size; + fu_dfuse_firmware_get_type; + fu_dfuse_firmware_new; + fu_firmware_new_from_gtypes; + fu_firmware_strparse_uint16_safe; + fu_firmware_strparse_uint24_safe; + fu_firmware_strparse_uint32_safe; + fu_firmware_strparse_uint4_safe; + fu_firmware_strparse_uint8_safe; + fu_hwids_add_smbios_override; + fu_hwids_get_keys; + fu_plugin_get_devices; + fu_plugin_runner_backend_device_added; + fu_plugin_runner_backend_device_changed; + local: *; +} LIBFWUPDPLUGIN_1.5.5; + +LIBFWUPDPLUGIN_1.5.7 { + global: + fu_bluez_device_get_type; + fu_bluez_device_read; + fu_bluez_device_read_string; + fu_bluez_device_write; + fu_firmware_get_version_raw; + fu_firmware_set_version_raw; + local: *; +} LIBFWUPDPLUGIN_1.5.6; + +LIBFWUPDPLUGIN_1.5.8 { + global: + fu_bluez_device_notify_start; + fu_bluez_device_notify_stop; + fu_byte_array_append_bytes; + fu_byte_array_append_uint64; + fu_device_get_backend_id; + fu_device_get_battery_level; + fu_device_set_backend_id; + fu_device_set_battery_level; + fu_quirks_add_possible_key; + fu_udev_device_set_logical_id; + local: *; +} LIBFWUPDPLUGIN_1.5.7; + +LIBFWUPDPLUGIN_1.6.0 { + global: + fu_battery_state_to_string; + fu_byte_array_align_up; + fu_bytes_get_data_safe; + fu_cabinet_add_file; + fu_cabinet_export; + fu_cabinet_get_file; + fu_cabinet_sign; + fu_common_align_up; + fu_context_add_compile_version; + fu_context_add_firmware_gtype; + fu_context_add_quirk_key; + fu_context_add_runtime_version; + fu_context_add_udev_subsystem; + fu_context_get_battery_level; + fu_context_get_battery_state; + fu_context_get_battery_threshold; + fu_context_get_firmware_gtype_by_id; + fu_context_get_firmware_gtype_ids; + fu_context_get_hwid_guids; + fu_context_get_hwid_replace_value; + fu_context_get_hwid_value; + fu_context_get_smbios_data; + fu_context_get_smbios_integer; + fu_context_get_smbios_string; + fu_context_get_type; + fu_context_get_udev_subsystems; + fu_context_has_hwid_guid; + fu_context_load_hwinfo; + fu_context_load_quirks; + fu_context_lookup_quirk_by_id; + fu_context_lookup_quirk_by_id_iter; + fu_context_new; + fu_context_security_changed; + fu_context_set_battery_level; + fu_context_set_battery_state; + fu_context_set_battery_threshold; + fu_context_set_compile_versions; + fu_context_set_runtime_versions; + fu_device_add_security_attrs; + fu_device_get_battery_threshold; + fu_device_get_context; + fu_device_inhibit; + fu_device_remove_flag; + fu_device_set_battery_threshold; + fu_device_set_context; + fu_device_uninhibit; + fu_firmware_add_chunk; + fu_firmware_build_from_xml; + fu_firmware_export; + fu_firmware_export_to_xml; + fu_firmware_get_addr; + fu_firmware_get_alignment; + fu_firmware_get_bytes; + fu_firmware_get_checksum; + fu_firmware_get_chunks; + fu_firmware_get_filename; + fu_firmware_get_id; + fu_firmware_get_idx; + fu_firmware_get_offset; + fu_firmware_get_size; + fu_firmware_set_addr; + fu_firmware_set_alignment; + fu_firmware_set_bytes; + fu_firmware_set_filename; + fu_firmware_set_id; + fu_firmware_set_idx; + fu_firmware_set_offset; + fu_firmware_set_size; + fu_firmware_write_chunk; + fu_ihex_firmware_set_padding_value; + fu_plugin_add_device_gtype; + fu_plugin_get_context; + fu_udev_device_get_siblings_with_subsystem; + fu_xmlb_builder_insert_kb; + fu_xmlb_builder_insert_kv; + fu_xmlb_builder_insert_kx; + local: *; +} LIBFWUPDPLUGIN_1.5.8; + +LIBFWUPDPLUGIN_1.6.1 { + global: + fu_backend_coldplug; + fu_backend_device_added; + fu_backend_device_changed; + fu_backend_device_removed; + fu_backend_get_context; + fu_backend_get_devices; + fu_backend_get_enabled; + fu_backend_get_name; + fu_backend_get_type; + fu_backend_lookup_by_id; + fu_backend_set_enabled; + fu_backend_setup; + fu_i2c_device_get_bus_number; + fu_i2c_device_get_type; + fu_kenv_get_string; + fu_srec_firmware_record_get_type; + fu_version_string; + local: *; +} LIBFWUPDPLUGIN_1.6.0; + +LIBFWUPDPLUGIN_1.6.2 { + global: + fu_device_add_guid_full; + fu_device_add_parent_physical_id; + fu_device_add_private_flag; + fu_device_emit_request; + fu_device_get_parent_physical_ids; + fu_device_get_private_flags; + fu_device_get_proxy_with_fallback; + fu_device_get_request_cnt; + fu_device_get_results; + fu_device_has_parent_physical_id; + fu_device_has_private_flag; + fu_device_register_private_flag; + fu_device_remove_child; + fu_device_remove_private_flag; + fu_device_set_private_flags; + fu_device_set_update_state; + fu_device_set_vendor; + fu_efi_firmware_file_get_type; + fu_efi_firmware_file_new; + fu_efi_firmware_filesystem_get_type; + fu_efi_firmware_filesystem_new; + fu_efi_firmware_section_get_type; + fu_efi_firmware_section_new; + fu_efi_firmware_volume_get_type; + fu_efi_firmware_volume_new; + fu_efi_guid_to_name; + fu_i2c_device_set_bus_number; + fu_ifd_access_to_string; + fu_ifd_bios_get_type; + fu_ifd_bios_new; + fu_ifd_firmware_check_jedec_cmd; + fu_ifd_firmware_get_type; + fu_ifd_firmware_new; + fu_ifd_image_get_access; + fu_ifd_image_get_type; + fu_ifd_image_new; + fu_ifd_image_set_access; + fu_ifd_region_to_access; + fu_ifd_region_to_name; + fu_ifd_region_to_string; + fu_plugin_add_udev_subsystem; + fu_smbios_setup_from_kernel; + fu_udev_device_get_children_with_subsystem; + fu_udev_device_set_dev; + local: *; +} LIBFWUPDPLUGIN_1.6.1; + +LIBFWUPDPLUGIN_1.7.0 { + global: + fu_cfu_device_offer_to_string; + fu_cfu_device_reject_to_string; + fu_cfu_device_status_to_string; + fu_cfu_offer_get_bank; + fu_cfu_offer_get_component_id; + fu_cfu_offer_get_force_ignore_version; + fu_cfu_offer_get_force_immediate_reset; + fu_cfu_offer_get_hw_variant; + fu_cfu_offer_get_milestone; + fu_cfu_offer_get_product_id; + fu_cfu_offer_get_protocol_revision; + fu_cfu_offer_get_segment_number; + fu_cfu_offer_get_token; + fu_cfu_offer_get_type; + fu_cfu_offer_new; + fu_cfu_offer_set_bank; + fu_cfu_offer_set_component_id; + fu_cfu_offer_set_force_ignore_version; + fu_cfu_offer_set_force_immediate_reset; + fu_cfu_offer_set_hw_variant; + fu_cfu_offer_set_milestone; + fu_cfu_offer_set_product_id; + fu_cfu_offer_set_protocol_revision; + fu_cfu_offer_set_segment_number; + fu_cfu_offer_set_token; + fu_cfu_payload_get_type; + fu_cfu_payload_new; + fu_device_attach_full; + fu_device_detach_full; + fu_device_set_progress; + fu_plugin_runner_attach; + fu_plugin_runner_cleanup; + fu_plugin_runner_detach; + fu_plugin_runner_prepare; + fu_plugin_runner_reload; + fu_plugin_runner_write_firmware; + fu_plugin_set_config_value; + fu_progress_add_flag; + fu_progress_finished; + fu_progress_flag_from_string; + fu_progress_flag_to_string; + fu_progress_get_child; + fu_progress_get_id; + fu_progress_get_percentage; + fu_progress_get_status; + fu_progress_get_steps; + fu_progress_get_type; + fu_progress_has_flag; + fu_progress_new; + fu_progress_remove_flag; + fu_progress_reset; + fu_progress_set_id; + fu_progress_set_percentage; + fu_progress_set_percentage_full; + fu_progress_set_profile; + fu_progress_set_status; + fu_progress_set_steps; + fu_progress_sleep; + fu_progress_step_done; + local: *; +} LIBFWUPDPLUGIN_1.6.2; + +LIBFWUPDPLUGIN_1.7.1 { + global: + fu_cfi_device_get_cmd; + fu_cfi_device_get_flash_id; + fu_cfi_device_get_size; + fu_cfi_device_get_type; + fu_cfi_device_new; + fu_cfi_device_set_flash_id; + fu_cfi_device_set_size; + fu_common_check_full_disk_encryption; + fu_device_add_string; + fu_device_get_internal_flags; + fu_device_set_internal_flags; + fu_security_attrs_append_internal; + local: *; +} LIBFWUPDPLUGIN_1.7.0; + +LIBFWUPDPLUGIN_1.7.2 { + global: + fu_context_has_hwid_flag; + fu_device_get_firmware_gtype; + fu_device_set_firmware_gtype; + fu_security_attrs_get_by_appstream_id; + fu_udev_device_get_bind_id; + fu_udev_device_get_sysfs_attr_uint64; + fu_udev_device_seek; + fu_udev_device_set_bind_id; + local: *; +} LIBFWUPDPLUGIN_1.7.1; + +LIBFWUPDPLUGIN_1.7.3 { + global: + fu_archive_firmware_get_type; + fu_archive_firmware_new; + fu_cfi_device_get_page_size; + fu_cfi_device_get_sector_size; + fu_cfi_device_set_page_size; + fu_cfi_device_set_sector_size; + local: *; +} LIBFWUPDPLUGIN_1.7.2; + +LIBFWUPDPLUGIN_1.7.4 { + global: + fu_backend_registered; + fu_cfi_device_get_block_size; + fu_cfi_device_set_block_size; + fu_context_get_lid_state; + fu_context_set_lid_state; + fu_firmware_add_patch; + fu_firmware_get_bytes_with_patches; + fu_lid_state_to_string; + fu_usb_device_add_interface; + fu_usb_device_set_configuration; + local: *; +} LIBFWUPDPLUGIN_1.7.3; + +LIBFWUPDPLUGIN_1.7.6 { + global: + fu_udev_device_get_parent_with_subsystem; + local: *; +} LIBFWUPDPLUGIN_1.7.4; + +LIBFWUPDPLUGIN_1.7.7 { + global: + fu_device_add_instance_str; + fu_device_add_instance_strsafe; + fu_device_add_instance_strup; + fu_device_add_instance_u16; + fu_device_add_instance_u32; + fu_device_add_instance_u4; + fu_device_add_instance_u8; + fu_device_build_instance_id; + fu_device_build_instance_id_quirk; + local: *; +} LIBFWUPDPLUGIN_1.7.6; + +LIBFWUPDPLUGIN_1.8.0 { + global: + fu_backend_invalidate; + fu_byte_array_compare; + fu_cfi_device_chip_select; + fu_cfi_device_chip_select_locker_new; + fu_coswid_firmware_get_type; + fu_coswid_firmware_new; + fu_device_has_inhibit; + fu_uswid_firmware_get_type; + fu_uswid_firmware_new; + local: *; +} LIBFWUPDPLUGIN_1.7.7; + +LIBFWUPDPLUGIN_1.8.1 { + global: + fu_archive_add_entry; + fu_archive_compression_from_string; + fu_archive_compression_to_string; + fu_archive_firmware_get_compression; + fu_archive_firmware_get_format; + fu_archive_firmware_set_compression; + fu_archive_firmware_set_format; + fu_archive_format_from_string; + fu_archive_format_to_string; + fu_archive_write; + fu_device_add_problem; + fu_device_poll_locker_new; + fu_device_remove_problem; + fu_plugin_runner_init; + local: *; +} LIBFWUPDPLUGIN_1.8.0; + +LIBFWUPDPLUGIN_1.8.2 { + global: + fu_byte_array_set_size; + fu_bytes_align; + fu_bytes_compare; + fu_bytes_get_contents; + fu_bytes_get_contents_fd; + fu_bytes_get_contents_stream; + fu_bytes_is_empty; + fu_bytes_new_offset; + fu_bytes_pad; + fu_bytes_set_contents; + fu_cpu_get_vendor; + fu_cpuid; + fu_crc16; + fu_crc16_full; + fu_crc32; + fu_crc32_full; + fu_crc8; + fu_crc8_full; + fu_device_new; + fu_dump_bytes; + fu_dump_full; + fu_dump_raw; + fu_efivar_secure_boot_enabled; + fu_fdt_firmware_get_cpuid; + fu_fdt_firmware_get_image_by_path; + fu_fdt_firmware_get_type; + fu_fdt_firmware_new; + fu_fdt_firmware_set_cpuid; + fu_fdt_image_get_attr; + fu_fdt_image_get_attr_str; + fu_fdt_image_get_attr_strlist; + fu_fdt_image_get_attr_u32; + fu_fdt_image_get_attr_u64; + fu_fdt_image_get_attrs; + fu_fdt_image_get_type; + fu_fdt_image_new; + fu_fdt_image_set_attr; + fu_fdt_image_set_attr_str; + fu_fdt_image_set_attr_strlist; + fu_fdt_image_set_attr_uint32; + fu_fdt_image_set_attr_uint64; + fu_firmware_get_parent; + fu_firmware_parse_full; + fu_firmware_set_parent; + fu_fit_firmware_get_timestamp; + fu_fit_firmware_get_type; + fu_fit_firmware_new; + fu_fit_firmware_set_timestamp; + fu_i2c_device_read; + fu_i2c_device_write; + fu_ifwi_cpd_firmware_get_type; + fu_ifwi_cpd_firmware_new; + fu_ifwi_fpt_firmware_get_type; + fu_ifwi_fpt_firmware_new; + fu_kernel_check_version; + fu_kernel_get_firmware_search_path; + fu_kernel_locked_down; + fu_kernel_reset_firmware_search_path; + fu_kernel_set_firmware_search_path; + fu_linear_firmware_get_image_gtype; + fu_linear_firmware_get_type; + fu_linear_firmware_new; + fu_mei_device_connect; + fu_mei_device_get_max_msg_length; + fu_mei_device_get_protocol_version; + fu_mei_device_get_type; + fu_mei_device_read; + fu_mei_device_write; + fu_memcmp_safe; + fu_memcpy_safe; + fu_memdup_safe; + fu_memmem_safe; + fu_memread_uint16; + fu_memread_uint16_safe; + fu_memread_uint24; + fu_memread_uint32; + fu_memread_uint32_safe; + fu_memread_uint64; + fu_memread_uint64_safe; + fu_memread_uint8_safe; + fu_memwrite_uint16; + fu_memwrite_uint16_safe; + fu_memwrite_uint24; + fu_memwrite_uint32; + fu_memwrite_uint32_safe; + fu_memwrite_uint64; + fu_memwrite_uint64_safe; + fu_memwrite_uint8_safe; + fu_oprom_firmware_get_compression_type; + fu_oprom_firmware_get_machine_type; + fu_oprom_firmware_get_subsystem; + fu_oprom_firmware_get_type; + fu_oprom_firmware_new; + fu_path_find_program; + fu_path_fnmatch; + fu_path_from_kind; + fu_path_get_files; + fu_path_glob; + fu_path_mkdir; + fu_path_mkdir_parent; + fu_path_rmtree; + fu_progress_add_step; + fu_progress_get_duration; + fu_progress_get_name; + fu_progress_get_profile; + fu_progress_set_name; + fu_progress_traceback; + fu_string_append; + fu_string_append_kb; + fu_string_append_ku; + fu_string_append_kx; + fu_string_replace; + fu_strjoin; + fu_strsafe; + fu_strsplit; + fu_strsplit_full; + fu_strstrip; + fu_strtobool; + fu_strtoull; + fu_strwidth; + fu_sum16; + fu_sum16_bytes; + fu_sum16w; + fu_sum16w_bytes; + fu_sum32; + fu_sum32_bytes; + fu_sum32w; + fu_sum32w_bytes; + fu_sum8; + fu_sum8_bytes; + fu_udev_device_ioctl; + fu_udev_device_new; + fu_udev_device_pread; + fu_udev_device_pwrite; + fu_usb_device_new; + fu_version_compare; + fu_version_ensure_semver; + fu_version_from_uint16; + fu_version_from_uint32; + fu_version_from_uint64; + fu_version_guess_format; + fu_version_parse_from_format; + fu_version_verify_format; + fu_volume_new_by_device; + fu_volume_new_by_devnum; + fu_volume_new_by_kind; + fu_volume_new_esp_for_path; + local: *; +} LIBFWUPDPLUGIN_1.8.1; + +LIBFWUPDPLUGIN_1.8.3 { + global: + fu_device_get_acquiesce_delay; + fu_device_set_acquiesce_delay; + fu_memread_uint24_safe; + local: *; +} LIBFWUPDPLUGIN_1.8.2; + +LIBFWUPDPLUGIN_1.8.4 { + global: + fu_backend_add_string; + fu_bios_settings_from_json; + fu_bios_settings_from_json_file; + fu_bios_settings_get_all; + fu_bios_settings_get_attr; + fu_bios_settings_get_pending_reboot; + fu_bios_settings_get_type; + fu_bios_settings_new; + fu_bios_settings_setup; + fu_bios_settings_to_hash_kv; + fu_bios_settings_to_variant; + fu_context_get_bios_setting; + fu_context_get_bios_setting_pending_reboot; + fu_context_get_bios_settings; + fu_context_reload_bios_settings; + fu_device_security_attr_new; + fu_firmware_check_compatible; + fu_plugin_add_string; + fu_plugin_security_attr_new; + fu_plugin_to_string; + fu_security_attr_add_bios_target_value; + fu_security_attr_get_type; + fu_security_attr_new; + local: *; +} LIBFWUPDPLUGIN_1.8.3; + +LIBFWUPDPLUGIN_1.8.5 { + global: + fu_backend_load; + fu_backend_save; + fu_context_add_esp_volume; + fu_context_add_flag; + fu_context_get_esp_volumes; + fu_context_has_flag; + fu_device_add_backend_tag; + fu_device_get_backend_tags; + fu_device_has_backend_tag; + fu_device_set_quirk_kv; + fu_intel_thunderbolt_firmware_get_type; + fu_intel_thunderbolt_firmware_new; + fu_intel_thunderbolt_nvm_get_device_id; + fu_intel_thunderbolt_nvm_get_flash_size; + fu_intel_thunderbolt_nvm_get_model_id; + fu_intel_thunderbolt_nvm_get_type; + fu_intel_thunderbolt_nvm_get_vendor_id; + fu_intel_thunderbolt_nvm_has_pd; + fu_intel_thunderbolt_nvm_is_host; + fu_intel_thunderbolt_nvm_is_native; + fu_intel_thunderbolt_nvm_new; + fu_kernel_get_cmdline; + fu_usb_device_ds20_apply_to_device; + fu_usb_device_ds20_get_type; + fu_usb_device_ds20_set_version_lowest; + fu_usb_device_fw_ds20_get_type; + fu_usb_device_fw_ds20_new; + fu_usb_device_ms_ds20_get_type; + fu_usb_device_ms_ds20_new; + local: *; +} LIBFWUPDPLUGIN_1.8.4; + +LIBFWUPDPLUGIN_1.8.6 { + global: + fu_device_get_update_request_id; + fu_device_set_update_request_id; + fu_plugin_new_from_gtype; + fu_plugin_set_context; + fu_volume_kind_convert_to_gpt; + local: *; +} LIBFWUPDPLUGIN_1.8.5; diff --git a/fwupd-1.8.6/libfwupdplugin/meson.build b/fwupd-1.8.6/libfwupdplugin/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..71667b6bc3a67139f9aca5809b2a958da2a459a5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/meson.build @@ -0,0 +1,363 @@ +if get_option('tests') +subdir('tests') +endif + +fwupdplugin_version_h = configure_file( + input: 'fu-version.h.in', + output: 'fu-version.h', + configuration: conf +) + +fwupdplugin_src = [ + 'fu-archive.c', + 'fu-backend.c', + 'fu-bluez-device.c', + 'fu-cabinet.c', + 'fu-chunk.c', # fuzzing + 'fu-common.c', # fuzzing + 'fu-sum.c', # fuzzing + 'fu-crc.c', # fuzzing + 'fu-mem.c', # fuzzing + 'fu-byte-array.c', # fuzzing + 'fu-string.c', # fuzzing + 'fu-bytes.c', # fuzzing + 'fu-kernel.c', # fuzzing + 'fu-dump.c', # fuzzing + 'fu-path.c', # fuzzing + 'fu-common-guid.c', + 'fu-version-common.c', # fuzzing + 'fu-context.c', # fuzzing + 'fu-device-locker.c', # fuzzing + 'fu-device.c', # fuzzing + 'fu-dfu-firmware.c', # fuzzing + 'fu-fdt-firmware.c', # fuzzing + 'fu-fdt-image.c', # fuzzing + 'fu-fit-firmware.c', # fuzzing + 'fu-ifwi-cpd-firmware.c', # fuzzing + 'fu-ifwi-fpt-firmware.c', # fuzzing + 'fu-oprom-firmware.c', # fuzzing + 'fu-intel-thunderbolt-nvm.c', # fuzzing + 'fu-intel-thunderbolt-firmware.c', # fuzzing + 'fu-cfu-common.c', # fuzzing + 'fu-cfu-offer.c', # fuzzing + 'fu-cfu-payload.c', # fuzzing + 'fu-volume.c', # fuzzing + 'fu-firmware.c', # fuzzing + 'fu-bios-settings.c', # fuzzing + 'fu-firmware-common.c', # fuzzing + 'fu-dfuse-firmware.c', # fuzzing + 'fu-fmap-firmware.c', # fuzzing + 'fu-hwids.c', # fuzzing + 'fu-ihex-firmware.c', # fuzzing + 'fu-io-channel.c', # fuzzing + 'fu-plugin.c', + 'fu-quirks.c', # fuzzing + 'fu-progress.c', # fuzzing + 'fu-security-attr.c', # fuzzing + 'fu-security-attrs.c', + 'fu-smbios.c', # fuzzing + 'fu-srec-firmware.c', # fuzzing + 'fu-archive-firmware.c', + 'fu-kenv.c', # fuzzing + 'fu-efi-signature.c', + 'fu-efi-signature-list.c', + 'fu-efi-common.c', # fuzzing + 'fu-efi-firmware-common.c', # fuzzing + 'fu-efi-firmware-file.c', # fuzzing + 'fu-efi-firmware-filesystem.c', # fuzzing + 'fu-efi-firmware-section.c', # fuzzing + 'fu-efi-firmware-volume.c', # fuzzing + 'fu-ifd-bios.c', # fuzzing + 'fu-ifd-common.c', # fuzzing + 'fu-ifd-firmware.c', # fuzzing + 'fu-ifd-image.c', # fuzzing + 'fu-uswid-firmware.c', # fuzzing + 'fu-coswid-common.c', # fuzzing + 'fu-coswid-firmware.c', # fuzzing + 'fu-efivar.c', + 'fu-udev-device.c', + 'fu-i2c-device.c', + 'fu-mei-device.c', + 'fu-usb-device.c', + 'fu-usb-device-ds20.c', # fuzzing + 'fu-usb-device-fw-ds20.c', # fuzzing + 'fu-usb-device-ms-ds20.c', # fuzzing + 'fu-cfi-device.c', + 'fu-hid-device.c', + 'fu-linear-firmware.c', + 'fu-version.c', +] + +if host_machine.system() == 'linux' + fwupdplugin_src += 'fu-common-linux.c' # fuzzing + fwupdplugin_src += 'fu-efivar-linux.c' +elif host_machine.system() == 'freebsd' + fwupdplugin_src += 'fu-common-freebsd.c' + fwupdplugin_src += 'fu-efivar-freebsd.c' +elif host_machine.system() == 'windows' + fwupdplugin_src += 'fu-common-windows.c' + fwupdplugin_src += 'fu-efivar-windows.c' # fuzzing +elif host_machine.system() == 'darwin' + fwupdplugin_src += 'fu-common-darwin.c' + fwupdplugin_src += 'fu-efivar-darwin.c' +else + error('no OS support for @0@'.format(host_machine.system())) +endif + +fwupdplugin_headers = [ + 'fu-archive.h', + 'fu-backend.h', + 'fu-bluez-device.h', + 'fu-cabinet.h', + 'fu-chunk.h', + 'fu-common.h', + 'fu-byte-array.h', + 'fu-string.h', + 'fu-sum.h', + 'fu-crc.h', + 'fu-mem.h', + 'fu-dump.h', + 'fu-path.h', + 'fu-bytes.h', + 'fu-kernel.h', + 'fu-common-guid.h', + 'fu-version-common.h', + 'fu-context.h', + 'fu-deprecated.h', + 'fu-device.h', + 'fu-device-metadata.h', + 'fu-device-locker.h', + 'fu-dfu-firmware.h', + 'fu-fdt-firmware.h', + 'fu-fdt-image.h', + 'fu-fit-firmware.h', + 'fu-ifwi-cpd-firmware.h', + 'fu-ifwi-fpt-firmware.h', + 'fu-oprom-firmware.h', + 'fu-intel-thunderbolt-firmware.h', + 'fu-intel-thunderbolt-nvm.h', + 'fu-cfu-common.h', + 'fu-cfu-offer.h', + 'fu-cfu-payload.h', + 'fu-efi-common.h', + 'fu-efi-firmware-file.h', + 'fu-efi-firmware-filesystem.h', + 'fu-efi-firmware-section.h', + 'fu-efi-firmware-volume.h', + 'fu-ifd-bios.h', + 'fu-ifd-common.h', + 'fu-ifd-firmware.h', + 'fu-uswid-firmware.h', + 'fu-coswid-firmware.h', + 'fu-ifd-image.h', + 'fu-linear-firmware.h', + 'fu-volume.h', + 'fu-firmware.h', + 'fu-bios-settings.h', + 'fu-firmware-common.h', + 'fu-fmap-firmware.h', + 'fu-dfuse-firmware.h', + 'fu-hwids.h', + 'fu-ihex-firmware.h', + 'fu-io-channel.h', + 'fu-plugin.h', + 'fu-quirks.h', + 'fu-security-attr.h', + 'fu-security-attrs.h', + 'fu-progress.h', + 'fu-smbios.h', + 'fu-cfi-device.h', + 'fu-srec-firmware.h', + 'fu-archive-firmware.h', + 'fu-efi-signature.h', + 'fu-efi-signature-list.h', + 'fu-efivar.h', + 'fu-udev-device.h', + 'fu-i2c-device.h', + 'fu-mei-device.h', + 'fu-usb-device.h', + 'fu-hid-device.h', +] + +fwupdplugin_headers_private = [ + 'fu-backend-private.h', + 'fu-context-private.h', + 'fu-device-private.h', + 'fu-kenv.h', + 'fu-plugin-private.h', + 'fu-bios-settings-private.h', + 'fu-security-attrs-private.h', + 'fu-smbios-private.h', + 'fu-udev-device-private.h', + 'fu-usb-device-ds20.h', + 'fu-usb-device-fw-ds20.h', + 'fu-usb-device-ms-ds20.h', + 'fu-usb-device-private.h', + fwupdplugin_version_h, +] + +introspection_deps = [ + libxmlb, + libjcat, + giounix, + gusb, + gudev, +] + +pkgg_requires = [ 'gio-2.0', + 'gmodule-2.0', + 'gobject-2.0', + 'fwupd', + 'json-glib-1.0', + 'libarchive', + 'libgcab-1.0', + 'xmlb', + 'jcat', + 'gusb' +] + +library_deps = [ + introspection_deps, + gmodule, + libjsonglib, + libgcab, + valgrind, + libjcat, + lzma, + libarchive, + cbor, + platform_deps, +] + +fwupdplugin_mapfile = 'fwupdplugin.map' +vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), fwupdplugin_mapfile) +fwupdplugin = library( + 'fwupdplugin', + sources: [ + fwupdplugin_src, + fwupdplugin_headers, + fwupdplugin_headers_private, + ], + include_directories: [ + root_incdir, + fwupd_incdir, + ], + dependencies: [ + library_deps + ], + link_with: [ + fwupd, + ], + link_args: cc.get_supported_link_arguments([vflag]), + link_depends: fwupdplugin_mapfile, + install_dir: libdir_pkg, + install: true +) + +if introspection.allowed() + gir_dep = declare_dependency(sources: fwupd_gir) + girtargets = [] + extra_args = [] + if gusb.found() + if gusb.type_name() == 'internal' + girtargets += subproject('gusb').get_variable('libgusb_girtarget')[0] + else + girtargets += 'GUsb-1.0' + endif + extra_args += '-DHAVE_GUSB' + endif + if libxmlb.type_name() == 'internal' + girtargets += subproject('libxmlb').get_variable('gir')[0] + elif libxmlb.version().version_compare ('>= 0.3.2') + girtargets += 'Xmlb-2.0' + endif + fwupdplugin_gir = gnome.generate_gir(fwupd, + sources: [ + fwupdplugin_src, + fwupdplugin_headers, + fwupdplugin_headers_private, + ], + nsversion: '1.0', + namespace: 'FwupdPlugin', + symbol_prefix: 'fu', + identifier_prefix: 'Fu', + export_packages: 'fwupdplugin', + extra_args: extra_args, + include_directories: [ + fwupd_incdir, + ], + header: 'fwupdplugin.h', + dependencies: [ + gir_dep, + introspection_deps + ], + link_with: [ + fwupdplugin, + ], + includes: [ + 'Gio-2.0', + 'GObject-2.0', + girtargets, + fwupd_gir[0], + ], + install: false + ) + + # Verify the map file is correct -- note we can't actually use the generated + # file for two reasons: + # + # 1. We don't hard depend on GObject Introspection + # 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 + # accidentally actually check in a version of the version script to git. + fwupdplugin_mapfile_target = custom_target('fwupdplugin_mapfile', + input: fwupdplugin_gir[0], + output: 'fwupdplugin.map', + command: [ + python3, + join_paths(meson.project_source_root(), 'contrib', 'generate-version-script.py'), + 'LIBFWUPDPLUGIN', + '@INPUT@', + '@OUTPUT@', + '--override', 'fu_chunk_get_type', '1.5.6', + '--override', 'fu_srec_firmware_record_get_type', '1.6.1', + ], + ) + + test('fwupdplugin-exported-api', diffcmd, + args: [ + '-urNp', + files('fwupdplugin.map'), + fwupdplugin_mapfile_target, + ], + ) +endif + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'fwupdplugin-self-test', + sources: [ + 'fu-self-test.c' + ], + include_directories: [ + root_incdir, + fwupd_incdir, + ], + dependencies: [ + library_deps + ], + link_with: [ + fwupd, + fwupdplugin + ], + c_args: [ + ], + ) + test('fwupdplugin-self-test', e, is_parallel: false, timeout: 180, env: env) +endif + +fwupdplugin_incdir = include_directories('.') diff --git a/fwupd-1.8.6/libfwupdplugin/tests/archive.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/archive.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..28271429ba52c31e7919e3befe8ece4be262c20f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/archive.builder.xml @@ -0,0 +1,12 @@ + + ustar + gzip + + one.txt + aGVsbG8gd29ybGQ= + + + two.txt + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d056b8d91fe3c0236fab652171d965f610593c66 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/display_name @@ -0,0 +1 @@ +Absolute diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..f3652ad6d51b63366cb20a5572b10d2811bbfca5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/possible_values @@ -0,0 +1 @@ +Enabled;Disabled;PermanentlyDisabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Absolute/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/display_name new file mode 100644 index 0000000000000000000000000000000000000000..204123a5235e52805b50d84864b4140fcabf5c64 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/display_name @@ -0,0 +1 @@ +Enable Admin Setup Lockout diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdminSetupLockout/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/display_name new file mode 100644 index 0000000000000000000000000000000000000000..93d32383f9d7a19ec67205b35ee88b1d95a3818b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/display_name @@ -0,0 +1 @@ +Enable Advanced Battery Charge Configuration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvBatteryChargeCfg/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bb82a737e5c6f9c602841f280a2afaec09f02c17 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/display_name @@ -0,0 +1 @@ +BIOS Setup Advanced Mode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AdvancedMode/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4039977899370f128f84cd9ff4a767d0c805fd21 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/display_name @@ -0,0 +1 @@ +Allow BIOS Downgrade diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AllowBiosDowngrade/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/current_value @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/default_value new file mode 100644 index 0000000000000000000000000000000000000000..33a43d62efbe522b731232ee0e2307c1ff2b2c93 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/default_value @@ -0,0 +1 @@ +Asset Tag diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/display_name new file mode 100644 index 0000000000000000000000000000000000000000..33a43d62efbe522b731232ee0e2307c1ff2b2c93 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/display_name @@ -0,0 +1 @@ +Asset Tag diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/max_length b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/max_length new file mode 100644 index 0000000000000000000000000000000000000000..900731ffd51ffc82db488b6554f719de735f12bd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/max_length @@ -0,0 +1 @@ +64 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/min_length b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/min_length new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/min_length @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/type new file mode 100644 index 0000000000000000000000000000000000000000..ee8a39c38d2d35ace182524fcc6329b900ef8ce1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/type @@ -0,0 +1 @@ +string diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/current_value new file mode 100644 index 0000000000000000000000000000000000000000..0cfbf08886fca9a91cb753ec8734c84fcbe52c9f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/current_value @@ -0,0 +1 @@ +2 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/default_value new file mode 100644 index 0000000000000000000000000000000000000000..0cfbf08886fca9a91cb753ec8734c84fcbe52c9f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/default_value @@ -0,0 +1 @@ +2 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e15124b6132c35bb3e8d14accbafa11940f83870 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/display_name @@ -0,0 +1 @@ +Dell Auto OS Recovery Threshold diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..62b4ec97f86ceb22c9b1d13731d491d06d719418 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/possible_values @@ -0,0 +1 @@ +OFF;1;2;3; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOSRecoveryThreshold/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a0a567faaea2a79c1a24da82aa6d2da25b18bb56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/display_name @@ -0,0 +1 @@ +Auto On Time diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..13d015878e74f1f9f6950e46316d074e693777bd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/possible_values @@ -0,0 +1 @@ +Disabled;Everyday;Weekdays;SelectDays; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOn/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b0e838a86b3c7009ffb048c775b2094bf995ce0c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/display_name @@ -0,0 +1 @@ +Friday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnFri/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/current_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/current_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/default_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/default_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7319c1e7edeabf0bafbfd155203bf4707727ea19 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/display_name @@ -0,0 +1 @@ +Hours (HH) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/max_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/max_value new file mode 100644 index 0000000000000000000000000000000000000000..409940768f2a684935a7d15a29f96e82c487f439 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/max_value @@ -0,0 +1 @@ +23 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/min_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/min_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/min_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/scalar_increment b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnHr/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/current_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/current_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/default_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/default_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bc3bab5339449dcbd4ea662a3ac65381a2f81bb4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/display_name @@ -0,0 +1 @@ +Minutes (MM) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/max_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/max_value new file mode 100644 index 0000000000000000000000000000000000000000..04f9fe46068b397a6fc24d647b7e3ec4315c15e7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/max_value @@ -0,0 +1 @@ +59 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/min_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/min_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/min_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/scalar_increment b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMn/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e9e3a4ae779117f283b8a23ac11d128b5d1832a3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/display_name @@ -0,0 +1 @@ +Monday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnMon/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/display_name new file mode 100644 index 0000000000000000000000000000000000000000..48fa79a20ea9045f5059c18750924645c385412a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/display_name @@ -0,0 +1 @@ +Saturday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSat/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e8319a10d3cc55a8cf1d8a762938818a5954b01f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/display_name @@ -0,0 +1 @@ +Sunday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnSun/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c6c496b8d6ac318040333aed3459b1a832205b8e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/display_name @@ -0,0 +1 @@ +Thursday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnThur/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ca5687197e338f398e3c0b8d49ff4951023a6474 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/display_name @@ -0,0 +1 @@ +Tuesday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnTue/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8183d5f068de2c6e67dc83d5a35032d7aa2ce4f1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/dell_modifier @@ -0,0 +1 @@ +[SuppressIfNot:AutoOn=SelectDays] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4bb6d8486d8b53aee80d9a6e3e60a61b243b6ab9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/display_name @@ -0,0 +1 @@ +Wednesday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/AutoOnWed/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6954fd4f4f1fa088fb6414df5fac1faaf52a1d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/display_name @@ -0,0 +1 @@ +BIOSConnect diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BIOSConnect/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fed66b3e92e4c06aebaa1b49a02da45680272d5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/current_value @@ -0,0 +1 @@ +Keep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/default_value new file mode 100644 index 0000000000000000000000000000000000000000..fed66b3e92e4c06aebaa1b49a02da45680272d5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/default_value @@ -0,0 +1 @@ +Keep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e06e2d5b437ace1bd412daf490d886af95cca5a3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/display_name @@ -0,0 +1 @@ +Clear Bios Event Log diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..1fba09ef6dac9f775ef625b50d22ec7ee4028c21 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/possible_values @@ -0,0 +1 @@ +Keep;Clear; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosLogClear/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0041a803cd7d8aae9e8e67dc3b8706a0dfa1b2c5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/display_name @@ -0,0 +1 @@ +BIOS Recovery from Hard Drive diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/display_name new file mode 100644 index 0000000000000000000000000000000000000000..972acb58e712621f5dab899867756587204d91b3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/display_name @@ -0,0 +1 @@ +Block Sleep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BlockSleep/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/display_name new file mode 100644 index 0000000000000000000000000000000000000000..df78f73ad9a88807026869b7ee76b423221fce5c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/display_name @@ -0,0 +1 @@ +Bluetooth diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BluetoothDevice/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/display_name new file mode 100644 index 0000000000000000000000000000000000000000..32cb739eefdfeaa30ab6a076d909f0df60bcd8f9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/display_name @@ -0,0 +1 @@ +Enable C-State Control diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CStatesCtrl/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0ef02396c347b8a5cbaed297dd0855d5600b63a8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/display_name @@ -0,0 +1 @@ +Enable Camera diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Camera/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f936c2eca6554edcbda40c0f6df2d7cd779aaa40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/display_name @@ -0,0 +1 @@ +Enable UEFI Capsule Firmware Updates diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CapsuleFirmwareUpdate/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/current_value new file mode 100644 index 0000000000000000000000000000000000000000..ae905029df4a6aad275f3a9d7168841f3156502d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/current_value @@ -0,0 +1 @@ +CoresAll diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/default_value new file mode 100644 index 0000000000000000000000000000000000000000..ae905029df4a6aad275f3a9d7168841f3156502d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/default_value @@ -0,0 +1 @@ +CoresAll diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b99facf9320f4b7f9a4f70ad69432e31b632b7a4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/display_name @@ -0,0 +1 @@ +Active Cores diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..7a18ddfb56ca1ac0f1e1e71154db74948386ea8e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/possible_values @@ -0,0 +1 @@ +CoresAll;1;2;3; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CpuCore/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/current_value new file mode 100644 index 0000000000000000000000000000000000000000..e373ee695f6e76d7d3f8f8c4e92d1d60995352e5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/current_value @@ -0,0 +1 @@ +50 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/default_value new file mode 100644 index 0000000000000000000000000000000000000000..e373ee695f6e76d7d3f8f8c4e92d1d60995352e5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/default_value @@ -0,0 +1 @@ +50 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d47bf72f491a1f99f5d064c1989b85dc2bbce4d8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/display_name @@ -0,0 +1 @@ +Custom Charge Start diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/max_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/max_value new file mode 100644 index 0000000000000000000000000000000000000000..5595fa46c063eb10e18b06d2b0849e17f756dc7f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/max_value @@ -0,0 +1 @@ +95 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/min_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/min_value new file mode 100644 index 0000000000000000000000000000000000000000..e373ee695f6e76d7d3f8f8c4e92d1d60995352e5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/min_value @@ -0,0 +1 @@ +50 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/scalar_increment b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStart/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/current_value new file mode 100644 index 0000000000000000000000000000000000000000..d61f00d8cad3920809f4d992ac3031b3f32e7f10 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/current_value @@ -0,0 +1 @@ +90 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/default_value new file mode 100644 index 0000000000000000000000000000000000000000..d61f00d8cad3920809f4d992ac3031b3f32e7f10 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/default_value @@ -0,0 +1 @@ +90 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d18eaaba1c177a8de38acecbc7944604c4469a40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/display_name @@ -0,0 +1 @@ +Custom Charge Stop diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/max_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/max_value new file mode 100644 index 0000000000000000000000000000000000000000..29d6383b52c1352e92a45875b5bb206f89139643 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/max_value @@ -0,0 +1 @@ +100 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/min_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/min_value new file mode 100644 index 0000000000000000000000000000000000000000..c3f407c0955bb5738e40a82664c79b63f04a9adb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/min_value @@ -0,0 +1 @@ +55 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/scalar_increment b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/display_name new file mode 100644 index 0000000000000000000000000000000000000000..dfea42d3a1d066d21c2f810cdb45fadb0e56869e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/display_name @@ -0,0 +1 @@ +DellCoreService diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DellCoreService/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c22ea90ddb28e5a5a96cb4483e0ca4bb8524470b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/display_name @@ -0,0 +1 @@ +Enable Dock Warning Messages diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DockWarningsEnMsg/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3b07d73d45e59663cf0df1d5b1226016db6343a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/display_name @@ -0,0 +1 @@ +Enable Dynamic Tuning:Machine Learning diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/DynTunML/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/current_value new file mode 100644 index 0000000000000000000000000000000000000000..22b0dc15c1046058f2783575163e193963544b78 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/current_value @@ -0,0 +1 @@ +Raid diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/default_value new file mode 100644 index 0000000000000000000000000000000000000000..22b0dc15c1046058f2783575163e193963544b78 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/default_value @@ -0,0 +1 @@ +Raid diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/display_name new file mode 100644 index 0000000000000000000000000000000000000000..504a30007f82236f2c0e6bb3f1fdbab1c98bb601 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/display_name @@ -0,0 +1 @@ +SATA/NVMe Opeartion diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..1512b731b4a7d7dcad2cd86f6dab591c6fe7ac9d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/possible_values @@ -0,0 +1 @@ +Disabled;Ahci;Raid; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/EmbSataRaid/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9e293ba3578a3fc9bcf1b5cd090d374b2008d250 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/current_value @@ -0,0 +1 @@ +0s diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/default_value new file mode 100644 index 0000000000000000000000000000000000000000..9e293ba3578a3fc9bcf1b5cd090d374b2008d250 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/default_value @@ -0,0 +1 @@ +0s diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d73b2822b5c401e854f18771125299b412d1cf2c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/display_name @@ -0,0 +1 @@ +Extend BIOS POST Time diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..9b628542470200bca23086ef96e2d08727706e78 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/possible_values @@ -0,0 +1 @@ +0s;5s;10s; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ExtPostTime/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/display_name new file mode 100644 index 0000000000000000000000000000000000000000..017a9c6fd238986469fe3743e0712fd4c060acfe --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/display_name @@ -0,0 +1 @@ +FOTA diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FOTA/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..ca35ca53d62971740e17fc199383badd86be2291 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/current_value @@ -0,0 +1 @@ +Minimal diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/default_value new file mode 100644 index 0000000000000000000000000000000000000000..d7c0bcfce65930d828e4d4f078a57c835c684f1d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/default_value @@ -0,0 +1 @@ +Thorough diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..33346f0c91c7411eb360d2d0c34d3f5a329fd3d8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/display_name @@ -0,0 +1 @@ +Fastboot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..67ef08db85dc0bd2d5f9aa93c8c8a8add24a45c3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/possible_values @@ -0,0 +1 @@ +Minimal;Thorough;Auto; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Fastboot/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5b93d8b27efb6c9393fe63627b97f0b6dbd0cde3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/display_name @@ -0,0 +1 @@ +Enable Fingerprint Reader Device diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReader/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d016ddb8d8b4911407fecdd89ace40e9a0e674a4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/display_name @@ -0,0 +1 @@ +Enable Fingerprint Reader Single Sign On diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FingerprintReaderSingleSignOn/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/display_name new file mode 100644 index 0000000000000000000000000000000000000000..33fe788a94d26a765d5621273e97d05d50824f7d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/display_name @@ -0,0 +1 @@ +Fn Lock Options diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLock/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..d8b32482513a8cdbc4316171721a3f196df0e22e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/current_value @@ -0,0 +1 @@ +DisableStandard diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/default_value new file mode 100644 index 0000000000000000000000000000000000000000..58e47e1ddbd4f0817d09cb9703ca1ceccca13a8b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/default_value @@ -0,0 +1 @@ +EnableSecondary diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0ed78805c54dcf7477de3aea92f0124ba8f95027 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/display_name @@ -0,0 +1 @@ +Lock Mode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..5c07fa90af6222d75bbf4c2f0a0f2bad66245ada --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/possible_values @@ -0,0 +1 @@ +DisableStandard;EnableSecondary; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FnLockMode/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/display_name new file mode 100644 index 0000000000000000000000000000000000000000..96ab7a7719401100996ba9e11c2932a28788ed99 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/display_name @@ -0,0 +1 @@ +Full Screen Logo diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/FullScreenLogo/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4fe26af2c696926fa27713317368a78f965dced2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/display_name @@ -0,0 +1 @@ +Enable Audio diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntegratedAudio/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ebf04356ea431b8e10a5d5fd67f873a185a728ce --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/display_name @@ -0,0 +1 @@ +Intel@ GNA Accelerator diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/IntelGna/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/display_name new file mode 100644 index 0000000000000000000000000000000000000000..923c3ce61ebf31bac620a0bf10bd01372ee56c36 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/display_name @@ -0,0 +1 @@ +Enable Internal Speaker diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/InternalSpeaker/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/current_value new file mode 100644 index 0000000000000000000000000000000000000000..39e1887456fade5b6749ff2933fdc49073bc8c64 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/current_value @@ -0,0 +1 @@ +10s diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/default_value new file mode 100644 index 0000000000000000000000000000000000000000..39e1887456fade5b6749ff2933fdc49073bc8c64 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/default_value @@ -0,0 +1 @@ +10s diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/display_name new file mode 100644 index 0000000000000000000000000000000000000000..169b8433f70393d4fe7d83ec1819bbf764512908 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/display_name @@ -0,0 +1 @@ +Keyboard Backlight Timeout on AC diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..234874c1690f03ac0706e10462111aa378dafa0f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/possible_values @@ -0,0 +1 @@ +5s;10s;15s;30s;1m;5m;15m;Never; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutAc/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/current_value new file mode 100644 index 0000000000000000000000000000000000000000..39e1887456fade5b6749ff2933fdc49073bc8c64 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/current_value @@ -0,0 +1 @@ +10s diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/default_value new file mode 100644 index 0000000000000000000000000000000000000000..39e1887456fade5b6749ff2933fdc49073bc8c64 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/default_value @@ -0,0 +1 @@ +10s diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ce8c0e4822d37a0c9bba0877cb6b1ecd23a105b1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/display_name @@ -0,0 +1 @@ +Keyboard Backlight Timeout on Battery diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..234874c1690f03ac0706e10462111aa378dafa0f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/possible_values @@ -0,0 +1 @@ +5s;10s;15s;30s;1m;5m;15m;Never; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KbdBacklightTimeoutBatt/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/default_value new file mode 100644 index 0000000000000000000000000000000000000000..ffe5772920658b4101fd9c9f32bc09b4c188d8e4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/default_value @@ -0,0 +1 @@ +Bright diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c20008c70bde1c1795ab91d399ddaa01f9a28168 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/display_name @@ -0,0 +1 @@ +Keyboard Illumination diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..5e42e023d6522b13fa4b627cdf657dc7151d85fb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/possible_values @@ -0,0 +1 @@ +Disabled;Dim;Bright; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/KeyboardIllumination/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2b9f48156768b9feaf73aa150c1d3e4390853da1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/display_name @@ -0,0 +1 @@ +Enable Lid Switch diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LidSwitch/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/display_name new file mode 100644 index 0000000000000000000000000000000000000000..190c4bbc5e4c768a7b3a172d280da274d116c105 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/display_name @@ -0,0 +1 @@ +Enable Intel Hyper-Threading Technology diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/LogicProc/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1c9e0e5c440e9b158db6d039574741d96a6faeb4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/display_name @@ -0,0 +1 @@ +M.2 PCIe SSD diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/M2PcieSsd0/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/current_value new file mode 100644 index 0000000000000000000000000000000000000000..a79443963bbcae1203d29aab9a5c9d496a3f9218 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/current_value @@ -0,0 +1 @@ +SystemUnique diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/default_value new file mode 100644 index 0000000000000000000000000000000000000000..a79443963bbcae1203d29aab9a5c9d496a3f9218 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/default_value @@ -0,0 +1 @@ +SystemUnique diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9f442fc70e70a6bf65e497ec71176af7934a5a37 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/display_name @@ -0,0 +1 @@ +MAC Address Pass-Through diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e2cf7590eb304ec2b7cd274521472d1b6cbd3f3c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/possible_values @@ -0,0 +1 @@ +SystemUnique;Disabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MacAddrPassThru/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e2c9e5f2f9dfc85c696c0e19d57cec26b078f942 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/display_name @@ -0,0 +1 @@ +Master Password Lockout diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/MasterPasswordLockout/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7e0410effc23729e10c5529273fdcf49001a1ce6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/display_name @@ -0,0 +1 @@ +Enable Microphone diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Microphone/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d7a97259356b9ec0b4e9de08aa462921ec98085d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/display_name @@ -0,0 +1 @@ +Enable Numlock diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/NumLock/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c378981c039cb28b9002c00ee09d27ab34067202 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/display_name @@ -0,0 +1 @@ +Password Bypass diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..f5a9e8d54987ed4569b2a0e2e38d51f491437a76 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/possible_values @@ -0,0 +1 @@ +Disabled;RebootBypass; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordBypass/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0120341d2e40086d101291717c0907b978a7e28e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/display_name @@ -0,0 +1 @@ +Enable Non-Admin Password Changes diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PasswordLock/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/current_value new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/current_value @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/default_value new file mode 100644 index 0000000000000000000000000000000000000000..60d3b2f4a4cd5f1637eba020358bfe5ecb5edcf2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/default_value @@ -0,0 +1 @@ +15 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..c958530ec78fd0ac22663c519e1b2601ab9d517b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/dell_modifier @@ -0,0 +1 @@ +[SuppressIf:PeakShiftCfg=Disabled] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/display_name new file mode 100644 index 0000000000000000000000000000000000000000..cdd713ff3b9956176f4f24b4885ce6d0115c820d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/display_name @@ -0,0 +1 @@ +Battery Threshold [15% to 100%] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/max_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/max_value new file mode 100644 index 0000000000000000000000000000000000000000..29d6383b52c1352e92a45875b5bb206f89139643 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/max_value @@ -0,0 +1 @@ +100 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/min_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/min_value new file mode 100644 index 0000000000000000000000000000000000000000..60d3b2f4a4cd5f1637eba020358bfe5ecb5edcf2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/min_value @@ -0,0 +1 @@ +15 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/scalar_increment b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftBatteryThreshold/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/display_name new file mode 100644 index 0000000000000000000000000000000000000000..eccda2aa8e3dfc19560fc5ee3846186dccd69562 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/display_name @@ -0,0 +1 @@ +Enable Peak Shift diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PeakShiftCfg/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/current_value new file mode 100644 index 0000000000000000000000000000000000000000..7f43d084f36de180613c5c6326af3745f2658356 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/current_value @@ -0,0 +1 @@ +Touchpad diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/default_value new file mode 100644 index 0000000000000000000000000000000000000000..7f43d084f36de180613c5c6326af3745f2658356 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/default_value @@ -0,0 +1 @@ +Touchpad diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c4d3a8bb09adb23959d065cacad7cd03f1a773d4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/display_name @@ -0,0 +1 @@ +Mouse/Touchpad diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..38e852adbc95458124ddf620dc276fc6f3dce7e1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/possible_values @@ -0,0 +1 @@ +SerialMouse;Ps2Mouse;Touchpad; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PntDevice/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fed66b3e92e4c06aebaa1b49a02da45680272d5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/current_value @@ -0,0 +1 @@ +Keep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/default_value new file mode 100644 index 0000000000000000000000000000000000000000..fed66b3e92e4c06aebaa1b49a02da45680272d5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/default_value @@ -0,0 +1 @@ +Keep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/display_name new file mode 100644 index 0000000000000000000000000000000000000000..8cca165f20ccd220b6844c2a8bce907a0b231a72 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/display_name @@ -0,0 +1 @@ +Clear POWER Event Log diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..1fba09ef6dac9f775ef625b50d22ec7ee4028c21 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/possible_values @@ -0,0 +1 @@ +Keep;Clear; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerLogClear/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5d5f623133b3ed6bccb306cc7bb9a0af38c5ec0c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/display_name @@ -0,0 +1 @@ +Power On Lid Open diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerOnLidOpen/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2ff4f3459358a3107c2013270c243e6c728e70d6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/display_name @@ -0,0 +1 @@ +Enable Adapter Warnings diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PowerWarn/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/current_value new file mode 100644 index 0000000000000000000000000000000000000000..4e033b98d30ffbdb0f36eb32aafe38bbb27c7f08 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/current_value @@ -0,0 +1 @@ +Adaptive diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/default_value new file mode 100644 index 0000000000000000000000000000000000000000..4e033b98d30ffbdb0f36eb32aafe38bbb27c7f08 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/default_value @@ -0,0 +1 @@ +Adaptive diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5f77030a374a54dd8e2cb2cde0ab3c6f143810cf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/display_name @@ -0,0 +1 @@ +Battery Configuration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..5fb2bc6cc5e45d2eba99653f376e7b41b0d795f8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/possible_values @@ -0,0 +1 @@ +Adaptive;Standard;Express;PrimAcUse;Custom; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PrimaryBattChargeCfg/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/display_name new file mode 100644 index 0000000000000000000000000000000000000000..cda123c1bb4228fefdebb84835b816ee0bcfa632 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/display_name @@ -0,0 +1 @@ +Digit diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdDigitRqd/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ddbea0be60e8177b830b3a983db6f18cded591cd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/display_name @@ -0,0 +1 @@ +Lower Case Letter diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdLowerCaseRqd/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/current_value new file mode 100644 index 0000000000000000000000000000000000000000..b8626c4cff2849624fb67f87cd0ad72b163671ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/current_value @@ -0,0 +1 @@ +4 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/default_value new file mode 100644 index 0000000000000000000000000000000000000000..b8626c4cff2849624fb67f87cd0ad72b163671ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/default_value @@ -0,0 +1 @@ +4 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2bc73287b6809da3233824a73b60a08d46251d5a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/display_name @@ -0,0 +1 @@ +Minimum Characters diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/max_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/max_value new file mode 100644 index 0000000000000000000000000000000000000000..f5c89552bd3e62bfce023a230e90d141f7a46b2f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/max_value @@ -0,0 +1 @@ +32 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/min_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/min_value new file mode 100644 index 0000000000000000000000000000000000000000..b8626c4cff2849624fb67f87cd0ad72b163671ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/min_value @@ -0,0 +1 @@ +4 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/scalar_increment b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdMinLen/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/display_name new file mode 100644 index 0000000000000000000000000000000000000000..579266590261129575aedcd5140c4c753ad73a17 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/display_name @@ -0,0 +1 @@ +Special Character diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdSpecialCharRqd/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f6ae44b875ba43381e8299eb150c3142ff1d4b84 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/display_name @@ -0,0 +1 @@ +Upper Case Letter diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/PwdUpperCaseRqd/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/current_value new file mode 100644 index 0000000000000000000000000000000000000000..5e83da53f8f3495354ab1034e5472a15db4d65c0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/current_value @@ -0,0 +1 @@ +Unarmed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/default_value new file mode 100644 index 0000000000000000000000000000000000000000..5e83da53f8f3495354ab1034e5472a15db4d65c0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/default_value @@ -0,0 +1 @@ +Unarmed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..7ff8883b9abc340d8f6312f7b401b94e2916be7f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/dell_modifier @@ -0,0 +1 @@ +[ProgHideLocal:TRUE] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/display_name new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/display_name @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..cb3f0090ce58d442e29e59e31fa9cec6389cbb88 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/possible_values @@ -0,0 +1 @@ +Unarmed;Armed;Complete;Error; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/RemoteWipeInternalDrives/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/display_name new file mode 100644 index 0000000000000000000000000000000000000000..461b233f3adcbdd08e0c1a8d0404d83ade24f530 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/display_name @@ -0,0 +1 @@ +SHA-256 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SHA256/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/display_name new file mode 100644 index 0000000000000000000000000000000000000000..55cf8ca96dcf4bf23b8d23f90ee7b25cbdd8b027 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/display_name @@ -0,0 +1 @@ +Secure Digital (SD) Card diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCard/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6529ef20879689ee416a44b9e3a07a0f4b7a9f5a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/display_name @@ -0,0 +1 @@ +Enable Secure Digital (SD) Card Boot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardBoot/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/display_name new file mode 100644 index 0000000000000000000000000000000000000000..981994d9982d60145a43be8277b7b14f090ca4f3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/display_name @@ -0,0 +1 @@ +Secure Digital (SD) Card Read-Only Mode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SdCardReadOnly/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..42d9aeb336d90b9683d437f611ee6d1a5d5bf65b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/display_name @@ -0,0 +1 @@ +Enable Secure Boot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..a6d00461d873fc21657ae488151260b0c87449eb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/current_value @@ -0,0 +1 @@ +DeployedMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/default_value new file mode 100644 index 0000000000000000000000000000000000000000..a6d00461d873fc21657ae488151260b0c87449eb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/default_value @@ -0,0 +1 @@ +DeployedMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..defab8e605099abd7bc1d9cb17a26a37e6fd32ce --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/display_name @@ -0,0 +1 @@ +Secure Boot Mode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..013a869a7c88cb589edd8e50832b7997f6fe58ac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/possible_values @@ -0,0 +1 @@ +DeployedMode;AuditMode; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBootMode/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fc8421812e18c31f9af68d1dd66c72b6d0c5bd9a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/display_name @@ -0,0 +1 @@ +Early Logo Display diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByDisplay/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f9a429f89da49ce46c3b41fe9fb0fa4307e242ee --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/display_name @@ -0,0 +1 @@ +Early Keyboard Backlight diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SignOfLifeByKbdBacklight/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/display_name new file mode 100644 index 0000000000000000000000000000000000000000..55640d91e85104eb4189e8284522884057f6c7d5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/display_name @@ -0,0 +1 @@ +Enable SMART Reporting diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmartErrors/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f68bc25137a8304c66e03c9f5440c0d6cf0b7a7c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/display_name @@ -0,0 +1 @@ +SMM Security Mitigation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SmmSecurityMitigation/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/display_name new file mode 100644 index 0000000000000000000000000000000000000000..df0489b6dc948ec08d6cb64b1294b3d725f1e7bf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/display_name @@ -0,0 +1 @@ +Intel Speed Shift Technology diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SpeedShift/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/display_name new file mode 100644 index 0000000000000000000000000000000000000000..90a703c187a4cc031f2b9645253cf7fd2c0e3d93 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/display_name @@ -0,0 +1 @@ +Enable Intel SpeedStep Technology diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Speedstep/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2abf51adb6745d39feda741463ec4eb7d98f9b77 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/display_name @@ -0,0 +1 @@ +Enable Strong Passwords diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/StrongPassword/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d245fad76ee058f421a7f4a06deb1d0dce3c23c2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/display_name @@ -0,0 +1 @@ +SupportAssist OS Recovery diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SupportAssistOSRecovery/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/current_value new file mode 100644 index 0000000000000000000000000000000000000000..db4033344e2a66379351c1b30537de5907302da0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/current_value @@ -0,0 +1 @@ +8RQ19C3 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/default_value new file mode 100644 index 0000000000000000000000000000000000000000..7dd242f9a8b71293ec48aa245155784bf30303f0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/default_value @@ -0,0 +1 @@ +Service Tag diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7dd242f9a8b71293ec48aa245155784bf30303f0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/display_name @@ -0,0 +1 @@ +Service Tag diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/max_length b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/max_length new file mode 100644 index 0000000000000000000000000000000000000000..7f8f011eb73d6043d2e6db9d2c101195ae2801f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/max_length @@ -0,0 +1 @@ +7 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/min_length b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/min_length new file mode 100644 index 0000000000000000000000000000000000000000..7f8f011eb73d6043d2e6db9d2c101195ae2801f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/min_length @@ -0,0 +1 @@ +7 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/type new file mode 100644 index 0000000000000000000000000000000000000000..ee8a39c38d2d35ace182524fcc6329b900ef8ce1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SvcTag/type @@ -0,0 +1 @@ +string diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f1a797a6b196603c55b2093874bb7354ad5fc402 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/current_value @@ -0,0 +1 @@ +Full diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/default_value new file mode 100644 index 0000000000000000000000000000000000000000..f1a797a6b196603c55b2093874bb7354ad5fc402 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/default_value @@ -0,0 +1 @@ +Full diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a83bc586bf3240cdb92b60635b4f28761ba14f4b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/display_name @@ -0,0 +1 @@ +Telemetry Access Level diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..53727bb87e48b77896e74541bc901f630d717f10 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/possible_values @@ -0,0 +1 @@ +Disabled;Basic;Enhanced;Full; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TelemetryAccessLvl/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fed66b3e92e4c06aebaa1b49a02da45680272d5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/current_value @@ -0,0 +1 @@ +Keep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/default_value new file mode 100644 index 0000000000000000000000000000000000000000..fed66b3e92e4c06aebaa1b49a02da45680272d5f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/default_value @@ -0,0 +1 @@ +Keep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/display_name new file mode 100644 index 0000000000000000000000000000000000000000..09949b1e2994642fecb3d7c8e99f81246d2e74aa --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/display_name @@ -0,0 +1 @@ +Clear Thermal Event Log diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..1fba09ef6dac9f775ef625b50d22ec7ee4028c21 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/possible_values @@ -0,0 +1 @@ +Keep;Clear; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalLogClear/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/current_value new file mode 100644 index 0000000000000000000000000000000000000000..08332999632f5f63b24dc753492354b58995bdf4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/current_value @@ -0,0 +1 @@ +Optimized diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/default_value new file mode 100644 index 0000000000000000000000000000000000000000..08332999632f5f63b24dc753492354b58995bdf4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/default_value @@ -0,0 +1 @@ +Optimized diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e4dac29d37030f7e9bbb51903ffb7e90b16126a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/display_name @@ -0,0 +1 @@ +Thermal Management diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..94c248633d88e55eaa9a8c78d71f4326186e323e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/possible_values @@ -0,0 +1 @@ +Optimized;Cool;Quiet;UltraPerformance; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThermalManagement/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3fa003b8f2248579cd9883c65874bd93d7aa9a92 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/display_name @@ -0,0 +1 @@ +Enable Thunderbolt" Boot Support diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltBoot/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..54ba8e6da7dcd401ea11521f4f2e9beef24a8d18 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/display_name @@ -0,0 +1 @@ +Enable Thunderbolt" (and PCIe behind TBT) pre-boot modules diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/ThunderboltPreboot/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/display_name new file mode 100644 index 0000000000000000000000000000000000000000..8bd4946638ae4eabab79f7210bab935aa7c913b9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/display_name @@ -0,0 +1 @@ +Touchscreen diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Touchscreen/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..a9f1077b3636e8b9584bf0cffda296fa108ed670 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/dell_modifier @@ -0,0 +1 @@ +[SuppressIf:TpmSecurity=Disabled] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0d0a7cea79770c0df5d1e386ece5e20c6e6eab2d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/display_name @@ -0,0 +1 @@ +TPM State diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmActivation/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..a9f1077b3636e8b9584bf0cffda296fa108ed670 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/dell_modifier @@ -0,0 +1 @@ +[SuppressIf:TpmSecurity=Disabled] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f1b7eb449d8da0be90135ab6548bd1fe6c90d373 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/display_name @@ -0,0 +1 @@ +PPI Bypass for Clear Commands diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiClearOverride/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..a9f1077b3636e8b9584bf0cffda296fa108ed670 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/dell_modifier @@ -0,0 +1 @@ +[SuppressIf:TpmSecurity=Disabled] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3b04786e081042669708035919bf927a9ed90026 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/display_name @@ -0,0 +1 @@ +PPI Bypass for Disable Commands diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiDpo/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..a9f1077b3636e8b9584bf0cffda296fa108ed670 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/dell_modifier @@ -0,0 +1 @@ +[SuppressIf:TpmSecurity=Disabled] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/display_name new file mode 100644 index 0000000000000000000000000000000000000000..69a58af63911f19fdbe54929cc1b81979ae36bbc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/display_name @@ -0,0 +1 @@ +PPI Bypass for Enable Commands diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmPpiPo/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a4363852f9f60ec691a2b29ad72d11db405ee2ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/display_name @@ -0,0 +1 @@ +TPM 2.0 Security On diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TpmSecurity/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..1342ed2409b898ac386a092d7caaf946ca72dbeb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/dell_value_modifier @@ -0,0 +1 @@ +Disabled[ForceIf:TpmSecurity=Disabled][ForceIf:Virtualization=Disabled][ForceIf:VtForDirectIo=Disabled][ForceIfNot:CpuCore=CoresAll]; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2c43f67e0a8b793a2cd936f90afec92c99c17363 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/display_name @@ -0,0 +1 @@ +Enable Intel Trusted Execution Technology (TXT) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TrustExecution/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..12b9b95ae93f0bfdb66202759d30d951dce034a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/display_name @@ -0,0 +1 @@ +Enable Intel Turbo Boost Technology diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/TurboMode/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/current_value new file mode 100644 index 0000000000000000000000000000000000000000..ac0724b9b5d4b953594d7291aa062c1ff6f7247d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/current_value @@ -0,0 +1 @@ +AlwaysExceptInternalHdd diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/default_value new file mode 100644 index 0000000000000000000000000000000000000000..ac0724b9b5d4b953594d7291aa062c1ff6f7247d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/default_value @@ -0,0 +1 @@ +AlwaysExceptInternalHdd diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/display_name new file mode 100644 index 0000000000000000000000000000000000000000..19169c60e5a52e3a42629e0c922818aeb8a129ac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/display_name @@ -0,0 +1 @@ +UEFI Boot Path Security diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..94c1f3485c9dacefbad08963c699a4d3dd44db6e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/possible_values @@ -0,0 +1 @@ +Never;Always;AlwaysExceptInternalHdd;AlwaysExceptInternalHddPxe; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiBootPathSecurity/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/display_name new file mode 100644 index 0000000000000000000000000000000000000000..66f4da62d1c3d66c7bd69b2c47b692abec5bc926 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/display_name @@ -0,0 +1 @@ +Enable UEFI Network Stack diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UefiNwStack/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/display_name new file mode 100644 index 0000000000000000000000000000000000000000..48a11db48f68eae7c765a68278308109af1d4061 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/display_name @@ -0,0 +1 @@ +Enable USB Boot Support diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbEmu/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1fb9f374b13b83a99363fbf7074d19b49e6f50aa --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/display_name @@ -0,0 +1 @@ +Enable External USB Ports diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/UsbPortsExternal/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/display_name new file mode 100644 index 0000000000000000000000000000000000000000..127fcc7ed443b7f9228b97e5e9551940fe073378 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/display_name @@ -0,0 +1 @@ +Enable Intel Virtualization Technology (VT) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Virtualization/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ecb67e7f29127252ba5e886db04e446820114a37 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/display_name @@ -0,0 +1 @@ +Enable Intel VT for Direct I/O diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/VtForDirectIo/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/display_name new file mode 100644 index 0000000000000000000000000000000000000000..596d4e1d087503d2f5698046b4c0c5a6113d7a73 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/display_name @@ -0,0 +1 @@ +Wake on AC diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnAc/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/display_name new file mode 100644 index 0000000000000000000000000000000000000000..030709bc1359c14687aed24e56febdf78c79bb98 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/display_name @@ -0,0 +1 @@ +Wake on Dell USB-C Dock diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnDock/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2b9fe646fd41e52e139ec3f95855e4d6e33ceeaa --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/display_name @@ -0,0 +1 @@ +Wake on LAN diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..fd13d0ad2a51f08f702a0d21ad938406ed94cf8b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/possible_values @@ -0,0 +1 @@ +Disabled;LanOnly;LanWithPxeBoot; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WakeOnLan/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1f08bff44fd2d5f4a08a92bed1d6c4aec9dbacea --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/current_value @@ -0,0 +1 @@ +PromptWrnErr diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/default_value new file mode 100644 index 0000000000000000000000000000000000000000..1f08bff44fd2d5f4a08a92bed1d6c4aec9dbacea --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/default_value @@ -0,0 +1 @@ +PromptWrnErr diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f616bf743ddaadc77b62d8c48788977803591b2d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/display_name @@ -0,0 +1 @@ +Warnings and Errors diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..91d36cafc5ed6c0fbcf913fda0cfc7d14914caa5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/possible_values @@ -0,0 +1 @@ +PromptWrnErr;ContWrn;ContWrnErr; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WarningsAndErr/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/current_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/current_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a50e4bbb097f1cc22ff74c2d86010d84f4c01f03 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/display_name @@ -0,0 +1 @@ +WLAN diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WirelessLan/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/current_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/current_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/default_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/default_value new file mode 100644 index 0000000000000000000000000000000000000000..44f03f1ab0958d4cd099df84f5be4ccb17771240 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/default_value @@ -0,0 +1 @@ +Disabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_value_modifier b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4a8a15347062af2b3522c6b541062264994b5f4a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/display_name @@ -0,0 +1 @@ +Control WLAN radio diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/display_name_language_code b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a5a958dd60dff37c1e62042df4df3ce1d3f80007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/possible_values @@ -0,0 +1 @@ +Disabled;Enabled; diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/type b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/pending_reboot b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/pending_reboot @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/reset_bios b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/reset_bios new file mode 100644 index 0000000000000000000000000000000000000000..47ba5039cddf74cc8f6c744d566fc3634c6a69e9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/reset_bios @@ -0,0 +1 @@ +builtinsafe lastknowngood factory custom diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/strings.txt b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/strings.txt new file mode 100644 index 0000000000000000000000000000000000000000..631e1fbd3b5f721c4722c1c60639217a1e706028 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/dell-xps13-9310/strings.txt @@ -0,0 +1,321 @@ +#TRANSLATORS: Description of BIOS setting +Service Tag + +#TRANSLATORS: Description of BIOS setting +Keyboard Illumination + +#TRANSLATORS: Description of BIOS setting +Intel Speed Shift Technology + +#TRANSLATORS: Description of BIOS setting +BIOS Recovery from Hard Drive + +#TRANSLATORS: Description of BIOS setting +Enable Thunderbolt (and PCIe behind TBT) pre-boot modules + +#TRANSLATORS: Description of BIOS setting +BIOSConnect + +#TRANSLATORS: Description of BIOS setting +Control WLAN radio + +#TRANSLATORS: Description of BIOS setting +FOTA + +#TRANSLATORS: Description of BIOS setting +TPM State + +#TRANSLATORS: Description of BIOS setting +Battery Threshold [15% to 100%] + +#TRANSLATORS: Description of BIOS setting +Special Character + +#TRANSLATORS: Description of BIOS setting +Clear Thermal Event Log + +#TRANSLATORS: Description of BIOS setting +Enable Numlock + +#TRANSLATORS: Description of BIOS setting +Sunday + +#TRANSLATORS: Description of BIOS setting +Enable Advanced Battery Charge Configuration + +#TRANSLATORS: Description of BIOS setting +Secure Boot Mode + +#TRANSLATORS: Description of BIOS setting +Enable External USB Ports + +#TRANSLATORS: Description of BIOS setting +Master Password Lockout + +#TRANSLATORS: Description of BIOS setting +Enable Non-Admin Password Changes + +#TRANSLATORS: Description of BIOS setting +M.2 PCIe SSD + +#TRANSLATORS: Description of BIOS setting +Battery Configuration + +#TRANSLATORS: Description of BIOS setting +Enable Intel Hyper-Threading Technology + +#TRANSLATORS: Description of BIOS setting +Lower Case Letter + +#TRANSLATORS: Description of BIOS setting +Enable Intel Virtualization Technology (VT) + +#TRANSLATORS: Description of BIOS setting +Thermal Management + +#TRANSLATORS: Description of BIOS setting +DellCoreService + +#TRANSLATORS: Description of BIOS setting +Keyboard Backlight Timeout on AC + +#TRANSLATORS: Description of BIOS setting +Mouse/Touchpad + +#TRANSLATORS: Description of BIOS setting +Enable Microphone + +#TRANSLATORS: Description of BIOS setting +Enable UEFI Network Stack + +#TRANSLATORS: Description of BIOS setting +SMM Security Mitigation + +#TRANSLATORS: Description of BIOS setting +Enable USB Boot Support + +#TRANSLATORS: Description of BIOS setting +Allow BIOS Downgrade + +#TRANSLATORS: Description of BIOS setting +Lock Mode + +#TRANSLATORS: Description of BIOS setting +Clear Bios Event Log + +#TRANSLATORS: Description of BIOS setting +Wake on Dell USB-C Dock + +#TRANSLATORS: Description of BIOS setting +Enable Secure Digital (SD) Card Boot + +#TRANSLATORS: Description of BIOS setting +Enable Adapter Warnings + +#TRANSLATORS: Description of BIOS setting +Saturday + +#TRANSLATORS: Description of BIOS setting +Custom Charge Start + +#TRANSLATORS: Description of BIOS setting +Enable Admin Setup Lockout + +#TRANSLATORS: Description of BIOS setting +Wake on LAN + +#TRANSLATORS: Description of BIOS setting +Fastboot + +#TRANSLATORS: Description of BIOS setting +Enable SMART Reporting + +#TRANSLATORS: Description of BIOS setting +Early Keyboard Backlight + +#TRANSLATORS: Description of BIOS setting +Intel@ GNA Accelerator + +#TRANSLATORS: Description of BIOS setting +Power On Lid Open + +#TRANSLATORS: Description of BIOS setting +Touchscreen + +#TRANSLATORS: Description of BIOS setting +Auto On Time + +#TRANSLATORS: Description of BIOS setting +Asset Tag + +#TRANSLATORS: Description of BIOS setting +Block Sleep + +#TRANSLATORS: Description of BIOS setting +Thursday + +#TRANSLATORS: Description of BIOS setting +MAC Address Pass-Through + +#TRANSLATORS: Description of BIOS setting +Enable Intel SpeedStep Technology + +#TRANSLATORS: Description of BIOS setting +Enable Intel Turbo Boost Technology + +#TRANSLATORS: Description of BIOS setting +Enable Peak Shift + +#TRANSLATORS: Description of BIOS setting +Wake on AC + +#TRANSLATORS: Description of BIOS setting +Bluetooth + +#TRANSLATORS: Description of BIOS setting +PPI Bypass for Clear Commands + +#TRANSLATORS: Description of BIOS setting +Absolute + +#TRANSLATORS: Description of BIOS setting +Clear POWER Event Log + +#TRANSLATORS: Description of BIOS setting +SATA/NVMe Opeartion + +#TRANSLATORS: Description of BIOS setting +Active Cores + +#TRANSLATORS: Description of BIOS setting +UEFI Boot Path Security + +#TRANSLATORS: Description of BIOS setting +Dell Auto OS Recovery Threshold + +#TRANSLATORS: Description of BIOS setting +SHA-256 + +#TRANSLATORS: Description of BIOS setting +PPI Bypass for Enable Commands + +#TRANSLATORS: Description of BIOS setting +Friday + +#TRANSLATORS: Description of BIOS setting +Enable Internal Speaker + +#TRANSLATORS: Description of BIOS setting +Minimum Characters + +#TRANSLATORS: Description of BIOS setting +Enable Intel VT for Direct I/O + +#TRANSLATORS: Description of BIOS setting +Enable C-State Control + +#TRANSLATORS: Description of BIOS setting +TPM 2.0 Security On + +#TRANSLATORS: Description of BIOS setting +Enable Lid Switch + +#TRANSLATORS: Description of BIOS setting +Enable Audio + +#TRANSLATORS: Description of BIOS setting +Enable Thunderbolt Boot Support + +#TRANSLATORS: Description of BIOS setting +Enable Fingerprint Reader Single Sign On + +#TRANSLATORS: Description of BIOS setting +PPI Bypass for Disable Commands + +#TRANSLATORS: Description of BIOS setting +Keyboard Backlight Timeout on Battery + +#TRANSLATORS: Description of BIOS setting +Telemetry Access Level + +#TRANSLATORS: Description of BIOS setting +Tuesday + +#TRANSLATORS: Description of BIOS setting +Warnings and Errors + +#TRANSLATORS: Description of BIOS setting +BIOS Setup Advanced Mode + +#TRANSLATORS: Description of BIOS setting +Digit + +#TRANSLATORS: Description of BIOS setting +Full Screen Logo + +#TRANSLATORS: Description of BIOS setting +Upper Case Letter + +#TRANSLATORS: Description of BIOS setting +Fn Lock Options + +#TRANSLATORS: Description of BIOS setting +Enable Fingerprint Reader Device + +#TRANSLATORS: Description of BIOS setting +SupportAssist OS Recovery + +#TRANSLATORS: Description of BIOS setting +Enable UEFI Capsule Firmware Updates + +#TRANSLATORS: Description of BIOS setting +Minutes (MM) + +#TRANSLATORS: Description of BIOS setting +Hours (HH) + +#TRANSLATORS: Description of BIOS setting +Enable Dock Warning Messages + +#TRANSLATORS: Description of BIOS setting +Enable Strong Passwords + +#TRANSLATORS: Description of BIOS setting +Enable Secure Boot + +#TRANSLATORS: Description of BIOS setting +Extend BIOS POST Time + +#TRANSLATORS: Description of BIOS setting +Password Bypass + +#TRANSLATORS: Description of BIOS setting +Wednesday + +#TRANSLATORS: Description of BIOS setting +WLAN + +#TRANSLATORS: Description of BIOS setting +Secure Digital (SD) Card Read-Only Mode + +#TRANSLATORS: Description of BIOS setting +Monday + +#TRANSLATORS: Description of BIOS setting +Enable Camera + +#TRANSLATORS: Description of BIOS setting +Secure Digital (SD) Card + +#TRANSLATORS: Description of BIOS setting +Enable Dynamic Tuning:Machine Learning + +#TRANSLATORS: Description of BIOS setting +Early Logo Display + +#TRANSLATORS: Description of BIOS setting +Enable Intel Trusted Execution Technology (TXT) + +#TRANSLATORS: Description of BIOS setting +Custom Charge Stop + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/current_value new file mode 100644 index 0000000000000000000000000000000000000000..b7c1dc7edfff56622a06869cc332a79a80e116dc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/current_value @@ -0,0 +1 @@ +01/01/2020 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/display_name new file mode 100644 index 0000000000000000000000000000000000000000..da964976cc7ac1b69adc797bb0ad01757cf83899 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/display_name @@ -0,0 +1 @@ +AlarmDate diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..db8c87e83e91cb8049e3e37af852f7d232865970 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDate/possible_values @@ -0,0 +1 @@ +MM/DD/YYYY diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/current_value new file mode 100644 index 0000000000000000000000000000000000000000..e8319a10d3cc55a8cf1d8a762938818a5954b01f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/current_value @@ -0,0 +1 @@ +Sunday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a26d00451049bcfd4643a52f39da54d1db2b24e1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/display_name @@ -0,0 +1 @@ +AlarmDayofWeek diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e348160f2cded137559183756016cf2133c03282 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmDayofWeek/possible_values @@ -0,0 +1 @@ +Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/current_value new file mode 100644 index 0000000000000000000000000000000000000000..feda35981cb19138da74f38dcf64ef42ee8dc74d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/current_value @@ -0,0 +1 @@ +00:00:00 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d2decb7015a7bf15c52834ae9f8c413c8726d760 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/display_name @@ -0,0 +1 @@ +AlarmTime diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..c5c7f17bcedafeb20ab24e95481e926e5f46edad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlarmTime/possible_values @@ -0,0 +1 @@ +HH/MM/SS diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a6520cd832b08906446d72d2c268cdf1342d4478 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/display_name @@ -0,0 +1 @@ +AlwaysOnUSB diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AlwaysOnUSB/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/display_name new file mode 100644 index 0000000000000000000000000000000000000000..50d2d7fa05921f0e3674d47d5e511cbd0d11883c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/display_name @@ -0,0 +1 @@ +AmdVt diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..c66bdaf31df495e9f6627bfd97d124440607a2de --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/AmdVt/possible_values @@ -0,0 +1 @@ +Enable,Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e8762007decfcd596a1376675555e8d869948294 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/display_name @@ -0,0 +1 @@ +BIOSPasswordAtBootDeviceList diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtBootDeviceList/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..246a470e230a91ed0b812f376b588e6d8643008c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/display_name @@ -0,0 +1 @@ +BIOSPasswordAtReboot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtReboot/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a38fd36cf825043acefc8c31f2175856df24f7ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/display_name @@ -0,0 +1 @@ +BIOSPasswordAtUnattendedBoot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSPasswordAtUnattendedBoot/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5ceea629985961a7d32beab59675c6189ead95ec --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/display_name @@ -0,0 +1 @@ +BIOSUpdateByEndUsers diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BIOSUpdateByEndUsers/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d5df9db0c0d83aa51ea435925338de60e0466ee0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/display_name @@ -0,0 +1 @@ +BluetoothAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BluetoothAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9265126e0ccbb662f45edfe4db3cc0c334d27c85 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/display_name @@ -0,0 +1 @@ +BootDeviceListF12Option diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDeviceListF12Option/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/current_value new file mode 100644 index 0000000000000000000000000000000000000000..2825c64de97f1b9ee799e2a5a5666a03ba828c1b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/current_value @@ -0,0 +1 @@ +LCD diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/display_name new file mode 100644 index 0000000000000000000000000000000000000000..56199f5c88ffa56f7a1543968ca234ed7921da01 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/display_name @@ -0,0 +1 @@ +BootDisplayDevice diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..c2c7cab42ad05211d7651164ae6c953eb2390016 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootDisplayDevice/possible_values @@ -0,0 +1 @@ +LCD,HDMI,USBTypeCA,USBTypeCB diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..51b09a6c48e0aa1f9827d79d168befc39aab9252 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/current_value @@ -0,0 +1 @@ +Quick diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ac3d31f804dda100b71da60a25f702c68bcdf8c1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/display_name @@ -0,0 +1 @@ +BootMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..368952c141592858286428b4c74623e3d2107feb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootMode/possible_values @@ -0,0 +1 @@ +Quick,Diagnostics diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/current_value new file mode 100644 index 0000000000000000000000000000000000000000..72f47b29d20635419e86c4abb21dd8fff64205df --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/current_value @@ -0,0 +1 @@ +USBCD:USBFDD:NVMe0:HDD0:USBHDD:PCILAN diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0dce700e01fc4f51aeb0c4d43064013ec478ddfa --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/display_name @@ -0,0 +1 @@ +BootOrder diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a800e99fa3dac3020d40ff7d651f5102db2fb5d7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrder/possible_values @@ -0,0 +1 @@ +HDD0,HDD1,HDD2,HDD3,HDD4,PCILAN,ATAPICD0,ATAPICD1,ATAPICD2,USBFDD,USBCD,USBHDD,OtherHDD,OtherCD,NVMe0,NVMe1,NODEV diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/display_name new file mode 100644 index 0000000000000000000000000000000000000000..92f4cad3a32472564d02135d7b31318edcd7d6f0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/display_name @@ -0,0 +1 @@ +BootOrderLock diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootOrderLock/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/display_name new file mode 100644 index 0000000000000000000000000000000000000000..10ca144eddae7b2bb7f1ed9bff9ab4eabb647823 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/display_name @@ -0,0 +1 @@ +BootTimeExtension diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..342486ad7bcf802914a9f1319ee02adad9263196 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BootTimeExtension/possible_values @@ -0,0 +1 @@ +Disable,1,2,3,5,10 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5e6a17b6c89cf31e2bef272cbf0d72ed24b3f460 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/display_name @@ -0,0 +1 @@ +BottomCoverTamperDetected diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/BottomCoverTamperDetected/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/display_name new file mode 100644 index 0000000000000000000000000000000000000000..99e882bdaf681ce9f5091ec1b56709df556b7916 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/display_name @@ -0,0 +1 @@ +CPUPowerManagement diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/CPUPowerManagement/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c6e50f1dff5954906a2a949bca77c4549ba7664b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/display_name @@ -0,0 +1 @@ +ComputraceModuleActivation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..2894886dcc67ee3c4b865b00f5242021ce71aa35 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ComputraceModuleActivation/possible_values @@ -0,0 +1 @@ +Disable,Enable,Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4e965c2eb5773766be4d9c62e409ca2e9a519f5b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/display_name @@ -0,0 +1 @@ +DashEnabled diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DashEnabled/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/display_name new file mode 100644 index 0000000000000000000000000000000000000000..01b6228674af08fad0b2a42b9fab056b46a55575 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/display_name @@ -0,0 +1 @@ +DataExecutionPrevention diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DataExecutionPrevention/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e428d37e77c40b08a1a2d25cd560d380d8253017 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/display_name @@ -0,0 +1 @@ +DeviceGuard diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/DeviceGuard/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d20137163b1eb9e80294c15c649ee7385ca7f0e5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/display_name @@ -0,0 +1 @@ +EthernetLANAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/EthernetLANAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1330673cad4106950ef16198b691ca3fc4397892 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/display_name @@ -0,0 +1 @@ +FingerprintPredesktopAuthentication diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintPredesktopAuthentication/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c117e76176d1a90cf2ba7c7a95885a5f145b92a5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/display_name @@ -0,0 +1 @@ +FingerprintReaderAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintReaderAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..b09e2da4ae42d046da39148c24a21a256fb984a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/current_value @@ -0,0 +1 @@ +Normal diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9303882523a2eec61a1187f9a2452baba6e98ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/display_name @@ -0,0 +1 @@ +FingerprintSecurityMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..9bb2240f21703de95b6ed51bb1682a92cd53a31b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FingerprintSecurityMode/possible_values @@ -0,0 +1 @@ +Normal,High diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b2d0ab6a6ecaa06df6d0cb1fe3c64e829e056fab --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/display_name @@ -0,0 +1 @@ +FnCtrlKeySwap diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnCtrlKeySwap/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1d797cf3f3e4843913b5d019534b249ca17dd438 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/display_name @@ -0,0 +1 @@ +FnKeyAsPrimary diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnKeyAsPrimary/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b4a9667321ba900e000b6ea6284f850d319f8349 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/display_name @@ -0,0 +1 @@ +FnSticky diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/FnSticky/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b98431f015936930a4b71539ce1eacad0d48c9f6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/display_name @@ -0,0 +1 @@ +IPv4NetworkStack diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv4NetworkStack/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b657bc69ad341a27573c40ba04500b07bb5aad2e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/display_name @@ -0,0 +1 @@ +IPv6NetworkStack diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IPv6NetworkStack/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7624586138f262e887abaa044f4bc9dd93758470 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/display_name @@ -0,0 +1 @@ +IntegratedAudioAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedAudioAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..04195e3e8f061fec44c1232b9d41972f7719e7e7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/display_name @@ -0,0 +1 @@ +IntegratedCameraAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/IntegratedCameraAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/display_name new file mode 100644 index 0000000000000000000000000000000000000000..474fa13fb7f7c2aabcc01775e36fc73d7d7feb70 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/display_name @@ -0,0 +1 @@ +InternalStorageTamper diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/InternalStorageTamper/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9555081bbd80ccda8b17e9bd577d978d4575dc76 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/display_name @@ -0,0 +1 @@ +KeyboardBeep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/KeyboardBeep/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2dee649e5a90e10c9ddd48613139ffdda99cd490 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/display_name @@ -0,0 +1 @@ +LidSensor diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..c66bdaf31df495e9f6627bfd97d124440607a2de --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LidSensor/possible_values @@ -0,0 +1 @@ +Enable,Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a6ea7c025ca5a4fe8d9f04b64c47d90b193f0468 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/display_name @@ -0,0 +1 @@ +LockBIOSSetting diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/LockBIOSSetting/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e31458794388d1a025376afb734806bc353e9fde --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/display_name @@ -0,0 +1 @@ +MACAddressPassThrough diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..f26c99cbb5c64686572c99169d0964544105ebbb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MACAddressPassThrough/possible_values @@ -0,0 +1 @@ +Disable,Enable,Second diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..96d9ff693b2d892eaa94d1471d2b1dd5a93c0e9b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/display_name @@ -0,0 +1 @@ +MemoryCardSlotAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MemoryCardSlotAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c84fc603dc026543c37822fbe8d2b97c35df72a2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/display_name @@ -0,0 +1 @@ +MicrophoneAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MicrophoneAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/display_name new file mode 100644 index 0000000000000000000000000000000000000000..dc4ef12dc65a98f939d5ea64611f3de5845655db --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/display_name @@ -0,0 +1 @@ +MinimumPasswordLength diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..68f2d26319dd47122ce0436239dacb3360392784 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/MinimumPasswordLength/possible_values @@ -0,0 +1 @@ +Disable,4,5,6,7,8,9,10,11,12 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..b17deef16d6d5e22d3c4e781c6f6877c4d08a058 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/current_value @@ -0,0 +1 @@ +PCILAN diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b2b8bc47b34f7f865728febf60723f66ecd6e007 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/display_name @@ -0,0 +1 @@ +NetworkBoot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a800e99fa3dac3020d40ff7d651f5102db2fb5d7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/NetworkBoot/possible_values @@ -0,0 +1 @@ +HDD0,HDD1,HDD2,HDD3,HDD4,PCILAN,ATAPICD0,ATAPICD1,ATAPICD2,USBFDD,USBCD,USBHDD,OtherHDD,OtherCD,NVMe0,NVMe1,NODEV diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4a448c963fad0bb463fb6d5bd55364850accc008 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/display_name @@ -0,0 +1 @@ +PasswordBeep diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordBeep/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/display_name new file mode 100644 index 0000000000000000000000000000000000000000..63d5fe3a068739906ed271dde306c30377ea4d96 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/display_name @@ -0,0 +1 @@ +PasswordCountExceededError diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PasswordCountExceededError/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9e2792e1363f3e11cbcfb4a420715a835962ff45 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/display_name @@ -0,0 +1 @@ +PhysicalPresenceForTpmClear diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PhysicalPresenceForTpmClear/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7d0d6aea43d0d7af37d6e11c08992ff522c9c127 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/display_name @@ -0,0 +1 @@ +PowerOnByAcAttach diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/PowerOnByAcAttach/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b0fb51a7107943ce95c8433faa74ccdbc14f09c9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/display_name @@ -0,0 +1 @@ +SecureBoot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureBoot/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6557a6c625b17abc642b778856a766c43eb31a30 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/display_name @@ -0,0 +1 @@ +SecureRollBackPrevention diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecureRollBackPrevention/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e5d78329c3874e6021aeaa58cc0f85262153d9cf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/display_name @@ -0,0 +1 @@ +SecurityChip diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..9c151c6932c1ccecf01aa792fa93d7bd6651e5b5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SecurityChip/possible_values @@ -0,0 +1 @@ +Active,Inactive,Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/current_value new file mode 100644 index 0000000000000000000000000000000000000000..5cfddc6b32556322f82447c7bb4b2e671ef37ad9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/current_value @@ -0,0 +1 @@ +Graphical diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/display_name new file mode 100644 index 0000000000000000000000000000000000000000..898882e461c61e9ba20a2bebd78c7c5ebdad3132 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/display_name @@ -0,0 +1 @@ +SetupUI diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..2ec092149a35b1a7ff5b18fb74d1aba846faa572 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SetupUI/possible_values @@ -0,0 +1 @@ +SimpleText,Graphical diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/current_value new file mode 100644 index 0000000000000000000000000000000000000000..d0866c6690e9a75bda98d818237f1205c1edd4b3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/current_value @@ -0,0 +1 @@ +Windows 10 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2c4c07b911d2cc8fb82efafaa9fc67e9194bdf56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/display_name @@ -0,0 +1 @@ +SleepState diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..4e02709df4e23b42f8e0673005d0e988ce723282 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SleepState/possible_values @@ -0,0 +1 @@ +Linux,Windows 10 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bb09412809223ebcee7469bb86afcb1cdb113eb4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/display_name @@ -0,0 +1 @@ +SmartCardSlotAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/SmartCardSlotAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b624e786b9414c8a99268d94ecc3c3bdb0a0f7e0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/display_name @@ -0,0 +1 @@ +StartupOptionKeys diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/StartupOptionKeys/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/display_name new file mode 100644 index 0000000000000000000000000000000000000000..23f672b8746efcdf9393785322ca9529e5ee3d0d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/display_name @@ -0,0 +1 @@ +ThinkShieldsecurewipe diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/ThinkShieldsecurewipe/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/display_name new file mode 100644 index 0000000000000000000000000000000000000000..342a18e0d40ae216c994ab18805032e6e61169cd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/display_name @@ -0,0 +1 @@ +TouchPad diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TouchPad/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7fd9bfa3d9b8909146a7f4809c0261f5cc3fcd36 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/display_name @@ -0,0 +1 @@ +TrackPoint diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/TrackPoint/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fd9885b95f58f91e4958f1b6c6f6f447166709d1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/current_value @@ -0,0 +1 @@ +Auto diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/display_name new file mode 100644 index 0000000000000000000000000000000000000000..148cbd189673da000d0700ae4971871ca893b424 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/display_name @@ -0,0 +1 @@ +UMAFramebufferSize diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..a4d7736e73a2cff9773f5ac487de06c5d05eeedc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UMAFramebufferSize/possible_values @@ -0,0 +1 @@ +Auto,512MB,1GB,2GB diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7d03035734024238ae0ee75f830fdc1555a79309 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/display_name @@ -0,0 +1 @@ +USBPortAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/USBPortAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/current_value new file mode 100644 index 0000000000000000000000000000000000000000..b40a26988186d86f28905a2a82a778f9e3b82cae --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/current_value @@ -0,0 +1 @@ +IPv4First diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ac4fd098f6e00bec07b4ee44bad5d0b1aec25bf0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/display_name @@ -0,0 +1 @@ +UefiPxeBootPriority diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..4afd42c0068d85bb57de82cd128063a9f0ed32b1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UefiPxeBootPriority/possible_values @@ -0,0 +1 @@ +IPv6First,IPv4First diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0239cfb2a0ce1dcec52ed70c1f7cabbe398c3e70 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmFriday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmFriday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b1917c13b4caeb2792a21ae5e2ea4d0ede59da55 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmMonday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmMonday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9ee0098be559bbca9c451551077aaddd3e92efcf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmSaturday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSaturday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bf8e412ffe808391171c90d97ed220ba3bb99839 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmSunday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmSunday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..089fdd88ca73753f3ebeb236adbee13db8e008bc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmThursday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmThursday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/current_value new file mode 100644 index 0000000000000000000000000000000000000000..feda35981cb19138da74f38dcf64ef42ee8dc74d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/current_value @@ -0,0 +1 @@ +00:00:00 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fc5e66172d6b5cc9eb1a142339034b468e44add0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/display_name @@ -0,0 +1 @@ +UserDefinedAlarmTime diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..c5c7f17bcedafeb20ab24e95481e926e5f46edad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTime/possible_values @@ -0,0 +1 @@ +HH/MM/SS diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f521e5cb0c7c466bfaccd2190478df6adf307cb4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmTuesday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmTuesday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3da79accdb71e827bd68ef88f4b895a6118369b5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmWednesday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/UserDefinedAlarmWednesday/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fa256ebf0f004000d9246c956ce4905ae99253ea --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/display_name @@ -0,0 +1 @@ +WakeOnLAN diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..694f5504721fa78af4f77f84e44ba322ab02bddf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLAN/possible_values @@ -0,0 +1 @@ +Disable,ACOnly,ACandBattery diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/display_name new file mode 100644 index 0000000000000000000000000000000000000000..128557d9a0cf0218097a32047e5fa4ab93f2c541 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/display_name @@ -0,0 +1 @@ +WakeOnLANDock diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeOnLANDock/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7b25a4612f9b1b98582d34e028af64148af70aa0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/display_name @@ -0,0 +1 @@ +WakeUponAlarm diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..2eff50e112c7847e070da254d35bb13fe979b24f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WakeUponAlarm/possible_values @@ -0,0 +1 @@ +Disable,UserDefined,WeeklyEvent,DailyEvent,SingleEvent diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/display_name new file mode 100644 index 0000000000000000000000000000000000000000..09b95008a50eca42f205d12913f0f613761ba9fe --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/display_name @@ -0,0 +1 @@ +WindowsUEFIFirmwareUpdate diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WindowsUEFIFirmwareUpdate/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1405f7370f420788d78b1ab7b5101cc71d04c6e0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/display_name @@ -0,0 +1 @@ +WirelessAutoDisconnection diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessAutoDisconnection/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..91fccc71c791ad43c98ae08f41be3b517799507a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/display_name @@ -0,0 +1 @@ +WirelessLANAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessLANAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..84f88e893b8d5d0cc5bbfcc74c59a2e8689186cd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/display_name @@ -0,0 +1 @@ +WirelessWANAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..d61d6de4928bf20ea2d04b7ba90cfbe1c7277842 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/WirelessWANAccess/possible_values @@ -0,0 +1 @@ +Disable,Enable diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/pending_reboot b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p14s-gen1/thinklmi/attributes/pending_reboot @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5911de5b950e6621531df3e15d09c5d98bc69f0f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/display_name @@ -0,0 +1 @@ +AMDMemoryGuard diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDMemoryGuard/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/display_name new file mode 100644 index 0000000000000000000000000000000000000000..349b2700f761ed033356f1fe9fab2c163763cdad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/display_name @@ -0,0 +1 @@ +AMDSecureVirtualMachine diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AMDSecureVirtualMachine/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..25fc72cae7fc5213f4409e47ea8a8285ee90f145 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Auto] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..98d96fe66fecec1b1758539ee31f140c2231d167 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/display_name @@ -0,0 +1 @@ +ASPMSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ASPMSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/current_value new file mode 100644 index 0000000000000000000000000000000000000000..0f394ce3000c4000a473e1373506a395f2aabeb8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/current_value @@ -0,0 +1 @@ +Permanently Disable;[Optional:Disable,Enable,Permanently Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d2aaa8e2637ddeacd3637b79a8389dcb57ef43d4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/display_name @@ -0,0 +1 @@ +AbsolutePersistenceModule diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AbsolutePersistenceModule/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e10ec98561ad5e78ba72a00e27de9cdfd6876f3d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/display_name @@ -0,0 +1 @@ +AccessSecuritySettings diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AccessSecuritySettings/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/current_value new file mode 100644 index 0000000000000000000000000000000000000000..7358a54a6d9cc00489aa2f5f0b3a46657c338c8b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/current_value @@ -0,0 +1 @@ +Last State;[Optional:Power Off,Power On,Last State,Power On to Automatic Boot Sequence] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/display_name new file mode 100644 index 0000000000000000000000000000000000000000..76659b78bf8849f2371e5954629c39b2036b63c0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/display_name @@ -0,0 +1 @@ +AfterPowerLoss diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AfterPowerLoss/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/current_value" "b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/current_value" new file mode 100644 index 0000000000000000000000000000000000000000..29d89f300df790e904e89d1b4c450c5abf7b7a32 --- /dev/null +++ "b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/current_value" @@ -0,0 +1 @@ +[01/01/2019][Status:ShowOnly] diff --git "a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/display_name" "b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/display_name" new file mode 100644 index 0000000000000000000000000000000000000000..ccd07b64ba4e1957a8a7a5c21f536caf890c53a6 --- /dev/null +++ "b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/display_name" @@ -0,0 +1 @@ +AlarmDate(MM\DD\YYYY) diff --git "a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/possible_values" "b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDate(MM\\DD\\YYYY)/possible_values" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/current_value new file mode 100644 index 0000000000000000000000000000000000000000..22f729e0e523f1fcf4202d43fc0551a0e108dfb5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/current_value @@ -0,0 +1 @@ +Sunday;[Optional:Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a26d00451049bcfd4643a52f39da54d1db2b24e1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/display_name @@ -0,0 +1 @@ +AlarmDayofWeek diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmDayofWeek/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8340985ed806c6f661f392ff774a1d6478ea59f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/current_value @@ -0,0 +1 @@ +[00:00:00][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7581487b583f17d8f0879c528997b94dfe2599e9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/display_name @@ -0,0 +1 @@ +AlarmTime(HH:MM:SS) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AlarmTime(HH:MM:SS)/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9fba22921bdebbfb8fc947033c7fa7378961e95a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/current_value @@ -0,0 +1 @@ +Yes;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/display_name new file mode 100644 index 0000000000000000000000000000000000000000..43a0639f5510a8888b703dde2d593a06157384fd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/display_name @@ -0,0 +1 @@ +AllowJumperClearSVP diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AllowJumperClearSVP/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9123f618ecdeef526916a802eac174b0d15201d7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/current_value @@ -0,0 +1 @@ +Network 1:M.2 Drive 1:M.2 Drive 2:PCIe Drive 1:PCIe Drive 2:PCIe Drive 3:PCIe Drive 4:PCIe Drive 5:PCIe Drive 6:SATA 1:SATA 2:SATA 3:SATA 4:SATA 5:SATA 6 / eSATA:Other Device;[Excluded from boot order:Network 2:Network 3:Network 4:Network 5:Network 6:Network 7:Network 8:Network 9:Network 10:USB HDD:USB CDROM] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/display_name new file mode 100644 index 0000000000000000000000000000000000000000..02f82d2d822962cc6886b8f1cdae459d98b715a5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/display_name @@ -0,0 +1 @@ +AutomaticBootSequence diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/AutomaticBootSequence/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/current_value new file mode 100644 index 0000000000000000000000000000000000000000..6a5977704dbf1743ce74d3181d61b57772583103 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/current_value @@ -0,0 +1 @@ +No;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e8762007decfcd596a1376675555e8d869948294 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/display_name @@ -0,0 +1 @@ +BIOSPasswordAtBootDeviceList diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtBootDeviceList/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..6a5977704dbf1743ce74d3181d61b57772583103 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/current_value @@ -0,0 +1 @@ +No;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..246a470e230a91ed0b812f376b588e6d8643008c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/display_name @@ -0,0 +1 @@ +BIOSPasswordAtReboot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtReboot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9fba22921bdebbfb8fc947033c7fa7378961e95a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/current_value @@ -0,0 +1 @@ +Yes;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a6f697bee4f9a93c61385ffb3fd4e8e9a5b96fc1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/display_name @@ -0,0 +1 @@ +BIOSPasswordAtSystemBoot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BIOSPasswordAtSystemBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f54db51a158d6f79fcdeb8ae83653c371ef27a9c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/current_value @@ -0,0 +1 @@ +On;[Optional:Off,On] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/display_name new file mode 100644 index 0000000000000000000000000000000000000000..dadab2993e6e7c4dfc09d7f00a019688ea1e3e07 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/display_name @@ -0,0 +1 @@ +BootUpNumLockStatus diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/BootUpNumLockStatus/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3e92974c15533d88c0e87714d4062082b224312d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/display_name @@ -0,0 +1 @@ +CPBMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPBMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/display_name new file mode 100644 index 0000000000000000000000000000000000000000..49a27688c260ce1d7d7706543f52b5969dc61204 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/display_name @@ -0,0 +1 @@ +CPUC6Report diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CPUC6Report/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..96a9508e2457b1d27b1677cc5b9bc8ff60be25a8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/display_name @@ -0,0 +1 @@ +CStateSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CStateSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1d83123d723bb3a2287390184314845b1d06c553 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Slot1(CPU),Slot3(CPU),Slot4(CPU),Slot5(CPU),Auto] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..459ff5a8a61846203757636314832b7e997f75b2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/display_name @@ -0,0 +1 @@ +CardLocation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CardLocation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/current_value new file mode 100644 index 0000000000000000000000000000000000000000..239d12b5b81e01d64266116ea2dd7bdeca446e6b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/current_value @@ -0,0 +1 @@ +No;[Optional:Yes,No] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/display_name new file mode 100644 index 0000000000000000000000000000000000000000..424bf1c24d8fcaedb1f75a24ae0e612fb122d448 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/display_name @@ -0,0 +1 @@ +ClearDIAGLog diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ClearDIAGLog/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b75bfbad84725dc30ca583dc18e6ae8f925574f3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/display_name @@ -0,0 +1 @@ +ConfigurationChangeDetection diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigurationChangeDetection/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/current_value new file mode 100644 index 0000000000000000000000000000000000000000..174a1e1a3b8a5eef29432443d2f23368e0bafa7d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/current_value @@ -0,0 +1 @@ +AHCI;[Optional:AHCI,RAID] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1b671ccd74a3e57af4e39b0409490a1088a646ca --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/display_name @@ -0,0 +1 @@ +ConfigureSATAas diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ConfigureSATAas/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6a8b66fcccffbeaef10b74adb2f3042e4b189310 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/display_name @@ -0,0 +1 @@ +CoverTamperDetected diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/CoverTamperDetected/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..be81766fef2da85a7049d31a51cbf9144604f456 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/display_name @@ -0,0 +1 @@ +DASHSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DASHSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..4dbbc4b7cb20dd8685235eca2d67988dbe04e4e7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/current_value @@ -0,0 +1 @@ +Only DIAG Error Code;[Optional:Disable,Only DIAG Error Code,Both BIOS POST & DIAG Error Code] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..29fbbd6fcdddb548d798d964aa3955e22dbcd585 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/display_name @@ -0,0 +1 @@ +DIAG7SegMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DIAG7SegMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/display_name new file mode 100644 index 0000000000000000000000000000000000000000..789d426b8b1ad0086e57e59f278ec8542b0810c9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/display_name @@ -0,0 +1 @@ +DataScrambling diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DataScrambling/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e428d37e77c40b08a1a2d25cd560d380d8253017 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/display_name @@ -0,0 +1 @@ +DeviceGuard diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceGuard/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/current_value new file mode 100644 index 0000000000000000000000000000000000000000..790986b341fcc9ab4fde037521dc5d34b99e3992 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Manual] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ccfbd813eba6b552ba76765fab9f3cbd84d7643b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/display_name @@ -0,0 +1 @@ +DevicePowerupDelay diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DevicePowerupDelay/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/current_value new file mode 100644 index 0000000000000000000000000000000000000000..13154f71ddbf4d22a10f6b8a75602148d4e1c9fa --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/current_value @@ -0,0 +1 @@ +20 sec;[Optional:10 sec,20 sec,30 sec,40 sec] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fa0e2cd227d1e84aa344d856303cc9bab5553486 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/display_name @@ -0,0 +1 @@ +DeviceResetTimeout diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/DeviceResetTimeout/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7d5e99c1fc67b971bbb27d76fd496767adaeb3c5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/display_name @@ -0,0 +1 @@ +EnhancedPowerSavingMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/EnhancedPowerSavingMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9123f618ecdeef526916a802eac174b0d15201d7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/current_value @@ -0,0 +1 @@ +Network 1:M.2 Drive 1:M.2 Drive 2:PCIe Drive 1:PCIe Drive 2:PCIe Drive 3:PCIe Drive 4:PCIe Drive 5:PCIe Drive 6:SATA 1:SATA 2:SATA 3:SATA 4:SATA 5:SATA 6 / eSATA:Other Device;[Excluded from boot order:Network 2:Network 3:Network 4:Network 5:Network 6:Network 7:Network 8:Network 9:Network 10:USB HDD:USB CDROM] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/display_name new file mode 100644 index 0000000000000000000000000000000000000000..1187e55e9a41531352709aae0790793628fd80a2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/display_name @@ -0,0 +1 @@ +ErrorBootSequence diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/ErrorBootSequence/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/current_value new file mode 100644 index 0000000000000000000000000000000000000000..828231f04ab9bb670892ef1a6f4d06dc8a6e6391 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/current_value @@ -0,0 +1 @@ +1 - Lower Fan Speed;[Optional:1 - Lower Fan Speed,2,3,4,5,6,7 - Higher Fan Speed] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fc6a246e22771a8eb17c9181048bd2579ef0e2b1 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/display_name @@ -0,0 +1 @@ +FanControlStepping diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FanControlStepping/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3bb4edc6b76fca6bc2f041af8a8760a4321fe948 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/display_name @@ -0,0 +1 @@ +FrontUSBPorts diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/FrontUSBPorts/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/current_value new file mode 100644 index 0000000000000000000000000000000000000000..d4c5b77f26c59825fad119425090fd2c77a32b7a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,3 Seconds,6 Seconds,9 Seconds,12 Seconds,15 Seconds,21 Seconds,30 Seconds] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3adfe7a4115b7d42a532ac1f4dcf523d8338c955 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/display_name @@ -0,0 +1 @@ +HardDiskPre-delay diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/HardDiskPre-delay/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/display_name new file mode 100644 index 0000000000000000000000000000000000000000..30789163a405c522f62a1523364adb81c915274c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/display_name @@ -0,0 +1 @@ +IOMMU diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/IOMMU/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2eb08b01ef61d99cf4ecb2b3f3148f7ef3ebd473 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/display_name @@ -0,0 +1 @@ +InternalUSB2Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB2Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d8736f4940893596513c03a75a22ce00b6eb73dc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/display_name @@ -0,0 +1 @@ +InternalUSB3Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/InternalUSB3Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/current_value new file mode 100644 index 0000000000000000000000000000000000000000..422d00279d7ca6eca4e11204e54ee170b05dd74c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/current_value @@ -0,0 +1 @@ +English;[Optional:English,French,German,Chinese] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3e68e2835785fae32e163288f0153846f83f8157 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/display_name @@ -0,0 +1 @@ +KeyboardLayout diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/KeyboardLayout/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ba1138149bda9421629a24294c1345edd50ddf41 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/display_name @@ -0,0 +1 @@ +M2Slot1DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..22bd599f806d1e353f1c8a3c33d7fbe20241f265 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/display_name @@ -0,0 +1 @@ +M2Slot1LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b6ccda93c0f8877d73318f3456fe784f04f9c4b5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/display_name @@ -0,0 +1 @@ +M2Slot1Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot1Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..02b6d34e3f29ccbf92aafb8748b90abf03a47837 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/display_name @@ -0,0 +1 @@ +M2Slot2DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3ff493ef8fe1c29cc9f23a635d40d3da5f9b37de --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/display_name @@ -0,0 +1 @@ +M2Slot2LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9a3df1126e89ab567f3d4867fcec328678c416b0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/display_name @@ -0,0 +1 @@ +M2Slot2Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/M2Slot2Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f6d95e2510141a2c58b1308bf01a8798e25574f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/current_value @@ -0,0 +1 @@ +Internal;[Optional:External,Internal] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/display_name new file mode 100644 index 0000000000000000000000000000000000000000..4227ad14954ff1da1ae459049d675a9d479b72ad --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/display_name @@ -0,0 +1 @@ +MCRUSBHeader diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MCRUSBHeader/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9f07b16ec8be5757f5d8925bf5602621397b357c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/current_value @@ -0,0 +1 @@ +UnLimited;[Optional:100,1,UnLimited,3] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/display_name new file mode 100644 index 0000000000000000000000000000000000000000..089ad465545cafcf35efa30bf6e55035ede3e6a2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/display_name @@ -0,0 +1 @@ +MaxPasswordAttempts diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MaxPasswordAttempts/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/display_name new file mode 100644 index 0000000000000000000000000000000000000000..546cfc123a592072c889dafd513f79b177ffd63b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/display_name @@ -0,0 +1 @@ +MediaCardReader diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MediaCardReader/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/current_value new file mode 100644 index 0000000000000000000000000000000000000000..37f94da22f61a4938ec1b5808a5bcf2c98baf566 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,40,42,44,46,48] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b13d27e5c5a3aebb479d35b7883215c8b1d264c5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/display_name @@ -0,0 +1 @@ +MmioAbove4GLimit diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/MmioAbove4GLimit/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/current_value new file mode 100644 index 0000000000000000000000000000000000000000..dedf29dc836107ca07325f3fa89c595a23400a76 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/current_value @@ -0,0 +1 @@ +Auto;[Optional:NPS1,NPS2,NPS4,Auto] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f4da38338bd56052aceedcc9872aa93ee17d0d6c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/display_name @@ -0,0 +1 @@ +NUMA diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NUMA/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/display_name new file mode 100644 index 0000000000000000000000000000000000000000..91d84ec8ad69218750eeaeda0e16428db28fbb90 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/display_name @@ -0,0 +1 @@ +NVMeRAIDMode diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/NVMeRAIDMode/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6cca038f2664469d758c43f6ab9126cf2a7692ec --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/display_name @@ -0,0 +1 @@ +OnboardEthernetController diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OnboardEthernetController/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/display_name new file mode 100644 index 0000000000000000000000000000000000000000..948cf032a66b40fb73e6011d90855856dd9f8e16 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/display_name @@ -0,0 +1 @@ +OptionKeysDisplay diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/OptionKeysDisplay/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1363a641d967114c758f4b6e153278030538bda5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,x4x4x4x4,x8x8,x16] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2e32ef0180c89c11b8b7eb45b7e0c15aeddf742e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/display_name @@ -0,0 +1 @@ +PCIeSlot1Bifurcation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Bifurcation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..37618ac588126ce8953464591d6f7e0734d6890b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/display_name @@ -0,0 +1 @@ +PCIeSlot1DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..959b5d5fd6d803f9ae938fb7a1ca6f8cd480dfd4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/display_name @@ -0,0 +1 @@ +PCIeSlot1LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..89b291546250e4dfe9004060bd654be619d730c5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/display_name @@ -0,0 +1 @@ +PCIeSlot1Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot1Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1363a641d967114c758f4b6e153278030538bda5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,x4x4x4x4,x8x8,x16] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b133d04412de626aba9c0be981c66361c75adda7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/display_name @@ -0,0 +1 @@ +PCIeSlot2Bifurcation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Bifurcation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d8c17903b8abe94f01d1e8b2cee95b1ed12cd12c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/display_name @@ -0,0 +1 @@ +PCIeSlot2DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bc70234408dff22a7f7ca2a00097a82f0f3dbd54 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/display_name @@ -0,0 +1 @@ +PCIeSlot2LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d784284c2a9c19a68b42fc1df9a019f6af90efac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/display_name @@ -0,0 +1 @@ +PCIeSlot2Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot2Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1363a641d967114c758f4b6e153278030538bda5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,x4x4x4x4,x8x8,x16] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5771c4c31427efcae05a8c222419dcdf9e92306f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/display_name @@ -0,0 +1 @@ +PCIeSlot3Bifurcation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Bifurcation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..19c255b5a1e5fabc45d1bf9d7d22f36ff872ab44 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/display_name @@ -0,0 +1 @@ +PCIeSlot3DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f47bff8b35ef7e96cfc7b212aaca0a22320918bd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/display_name @@ -0,0 +1 @@ +PCIeSlot3LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..43ba894f635cf34c9a496b651edfe190c9882530 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/display_name @@ -0,0 +1 @@ +PCIeSlot3Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot3Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1363a641d967114c758f4b6e153278030538bda5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,x4x4x4x4,x8x8,x16] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..31a76f43db2bad252f5b2ec3738000e2787f8f20 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/display_name @@ -0,0 +1 @@ +PCIeSlot4Bifurcation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Bifurcation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0d4ed00f83d7961ba77981e97f0103b9c53d0843 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/display_name @@ -0,0 +1 @@ +PCIeSlot4DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..76e41c0172347f7a3d6613b54a5e9d3cb624fc04 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/display_name @@ -0,0 +1 @@ +PCIeSlot4LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..607db0f44c04d734f574bb5d2f8185e49f54a990 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/display_name @@ -0,0 +1 @@ +PCIeSlot4Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot4Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1363a641d967114c758f4b6e153278030538bda5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,x4x4x4x4,x8x8,x16] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0ed7ea64a4a3d704baa1f9bb5ea19f32a4686c94 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/display_name @@ -0,0 +1 @@ +PCIeSlot5Bifurcation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Bifurcation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3effe831546b35a425fe65978939483b63a3d044 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/display_name @@ -0,0 +1 @@ +PCIeSlot5DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a5109cf818ff372e568c31ae69d06c34c1fce62d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/display_name @@ -0,0 +1 @@ +PCIeSlot5LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ffc57cc4decec0eaf8dff25de27f404f8f6b7d9f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/display_name @@ -0,0 +1 @@ +PCIeSlot5Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot5Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1363a641d967114c758f4b6e153278030538bda5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,x4x4x4x4,x8x8,x16] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fea7b5a014030a6525e6a1076038ead2aa5fb4e7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/display_name @@ -0,0 +1 @@ +PCIeSlot6Bifurcation diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Bifurcation/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..fcb78453771b3ed0d0a15f058ef43f38ea247e40 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/current_value @@ -0,0 +1 @@ +Enable;[Optional:Enable,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bab7c96f73647616e02ea90c5fe73d9078dc4be3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/display_name @@ -0,0 +1 @@ +PCIeSlot6DLFSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6DLFSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/current_value new file mode 100644 index 0000000000000000000000000000000000000000..950da615c8e987ba2c927325fe6063ff85c4b13a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Gen 1 (2.5 GT/s),Gen 2 (5 GT/s),Gen 3 (8 GT/s),Gen 4 (16 GT/s)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f3a9e9ffb7bb631a26c708664821a85257f29751 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/display_name @@ -0,0 +1 @@ +PCIeSlot6LinkSpeed diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6LinkSpeed/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c3953853fdf539c4a4f82dc2597731db7e8f464f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/display_name @@ -0,0 +1 @@ +PCIeSlot6Port diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PCIeSlot6Port/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9fba22921bdebbfb8fc947033c7fa7378961e95a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/current_value @@ -0,0 +1 @@ +Yes;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/display_name new file mode 100644 index 0000000000000000000000000000000000000000..cdc55780a8ef64c91c95fd4b50646c99f3417f12 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/display_name @@ -0,0 +1 @@ +POPChangeablebyUser diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/POPChangeablebyUser/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/display_name new file mode 100644 index 0000000000000000000000000000000000000000..906d6ce4543a6dabed6a7b6cc3b0afb190245782 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/display_name @@ -0,0 +1 @@ +PXEIPV4NetworkStack diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV4NetworkStack/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/display_name new file mode 100644 index 0000000000000000000000000000000000000000..51ce81c23ceaece0508e3c4b0cb8a49fc365d073 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/display_name @@ -0,0 +1 @@ +PXEIPV6NetworkStack diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PXEIPV6NetworkStack/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/current_value new file mode 100644 index 0000000000000000000000000000000000000000..1725bd0bd96309df6d8cd909b0094bb84d970ce9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/current_value @@ -0,0 +1 @@ +Immediately;[Optional:After Reboot,Immediately] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/display_name new file mode 100644 index 0000000000000000000000000000000000000000..38c5b3a9cfcda6fbe6f250131ac51b71558bfa0a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/display_name @@ -0,0 +1 @@ +PasswordChangeTime diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordChangeTime/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/display_name new file mode 100644 index 0000000000000000000000000000000000000000..63d5fe3a068739906ed271dde306c30377ea4d96 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/display_name @@ -0,0 +1 @@ +PasswordCountExceededError diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PasswordCountExceededError/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/display_name new file mode 100644 index 0000000000000000000000000000000000000000..ecf203e6d0c9a687028dc2120c821c9cf6f86816 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/display_name @@ -0,0 +1 @@ +PatroScrub diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrub/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/current_value new file mode 100644 index 0000000000000000000000000000000000000000..ceaa347e59ad0bd69748106716d9576b36ab6019 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/current_value @@ -0,0 +1 @@ +24;[Optional:Auto,1,4,8,16,24,48] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fc89995218091801bbaffdcf7c58585320c5576e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/display_name @@ -0,0 +1 @@ +PatroScrubInterval diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PatroScrubInterval/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/display_name new file mode 100644 index 0000000000000000000000000000000000000000..37a0f450115330ca4cdb7e1f4104906edc8fb568 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/display_name @@ -0,0 +1 @@ +PhysicalPresenceforClear diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PhysicalPresenceforClear/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/display_name new file mode 100644 index 0000000000000000000000000000000000000000..8dfedd53d4224f13877d1f95f983b25012eabe00 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/display_name @@ -0,0 +1 @@ +PostPackageRepair diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PostPackageRepair/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/current_value new file mode 100644 index 0000000000000000000000000000000000000000..d1ff831a6f5a2d61e85c7169c7a3fba5cbe4d998 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/current_value @@ -0,0 +1 @@ +M.2 Drive 1:M.2 Drive 2:PCIe Drive 1:PCIe Drive 2:PCIe Drive 3:PCIe Drive 4:PCIe Drive 5:PCIe Drive 6:SATA 1:SATA 2:SATA 3:SATA 4:SATA 5:SATA 6 / eSATA:Network 1:USB HDD:USB CDROM:Other Device;[Excluded from boot order:Network 2:Network 3:Network 4:Network 5:Network 6:Network 7:Network 8:Network 9:Network 10] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fac8874a1015fc06f943f9d3877afdaad2e6ad30 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/display_name @@ -0,0 +1 @@ +PrimaryBootSequence diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/PrimaryBootSequence/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/current_value new file mode 100644 index 0000000000000000000000000000000000000000..015006eb0765243a25fd46203a26acc2cb718abd --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/current_value @@ -0,0 +1 @@ +Low Speed;[Optional:Low Speed,Medium Speed,High Speed] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/display_name new file mode 100644 index 0000000000000000000000000000000000000000..cabc4f962b9a2f6b2250e896fcbc9fcd65e08b57 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/display_name @@ -0,0 +1 @@ +QuadM2PCIeCardFanControl diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/QuadM2PCIeCardFanControl/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e878228b1793620051bfd762f18c09ce46d29e5d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/display_name @@ -0,0 +1 @@ +RealtimeDIAG diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RealtimeDIAG/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/display_name new file mode 100644 index 0000000000000000000000000000000000000000..2e72d8549a880c9768667c76ebca65565cda8dc7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/display_name @@ -0,0 +1 @@ +RearAudioController diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearAudioController/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/display_name new file mode 100644 index 0000000000000000000000000000000000000000..530c7ace96a2410979ad56bba63a0f053872ffc7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/display_name @@ -0,0 +1 @@ +RearUSBPorts diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RearUSBPorts/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5093fb7ee951cd29b05201e9991c587cc6a997b4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/display_name @@ -0,0 +1 @@ +RemoteSetSMP diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RemoteSetSMP/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..4d023d8c441ae147715281746de41ffd933e67a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/current_value @@ -0,0 +1 @@ +Auto;[Optional:No,Auto] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..adc211cbba4384c7b3ce13a0f70b9a799680030a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/display_name @@ -0,0 +1 @@ +RequireHDPonSystemBoot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireHDPonSystemBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/current_value new file mode 100644 index 0000000000000000000000000000000000000000..6a5977704dbf1743ce74d3181d61b57772583103 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/current_value @@ -0,0 +1 @@ +No;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e7b8b39173348e9d8d9fefc082bc30bbe67651c3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/display_name @@ -0,0 +1 @@ +RequireSVPwhenFlashing diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/RequireSVPwhenFlashing/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/display_name new file mode 100644 index 0000000000000000000000000000000000000000..425cef7cf2e44a82fc58ffb473c594fb0f8e00ac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/display_name @@ -0,0 +1 @@ +SATAController diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATAController/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e379e800dd606c84aea45f371a8c18257087e670 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/display_name @@ -0,0 +1 @@ +SATADrive1 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive1/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/display_name new file mode 100644 index 0000000000000000000000000000000000000000..5e4b850d0ca4cf7e9e477325def6954aa62e6267 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/display_name @@ -0,0 +1 @@ +SATADrive2 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive2/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/display_name new file mode 100644 index 0000000000000000000000000000000000000000..77781fb3d510cfb1b342040367550dcf1d8c11bc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/display_name @@ -0,0 +1 @@ +SATADrive3 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive3/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/display_name new file mode 100644 index 0000000000000000000000000000000000000000..51a44adce46dfc0d5929fabd6172f5d7c7ce0b46 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/display_name @@ -0,0 +1 @@ +SATADrive4 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive4/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/display_name new file mode 100644 index 0000000000000000000000000000000000000000..23841ff8b6e118badf70a0e8ec1697f675420aba --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/display_name @@ -0,0 +1 @@ +SATADrive5 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive5/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/display_name new file mode 100644 index 0000000000000000000000000000000000000000..a3b1e06d252228998c35dbde080cefd14f78df45 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/display_name @@ -0,0 +1 @@ +SATADrive6 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..79a896f356f2b49a3c5afb5f5d380ac700e54cac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/display_name @@ -0,0 +1 @@ +SATADrive6HotPlugSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SATADrive6HotPlugSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0e9140a3dc42a182c601292b1d538ca71e6453d5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/display_name @@ -0,0 +1 @@ +SMT diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SMT/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/display_name new file mode 100644 index 0000000000000000000000000000000000000000..cc8cfad15681d79287733d8655b690533538b3b4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/display_name @@ -0,0 +1 @@ +SRIOVSupport diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SRIOVSupport/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b0fb51a7107943ce95c8433faa74ccdbc14f09c9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/display_name @@ -0,0 +1 @@ +SecureBoot diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureBoot/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9fba22921bdebbfb8fc947033c7fa7378961e95a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/current_value @@ -0,0 +1 @@ +Yes;[Optional:No,Yes] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6557a6c625b17abc642b778856a766c43eb31a30 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/display_name @@ -0,0 +1 @@ +SecureRollBackPrevention diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecureRollBackPrevention/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e5d78329c3874e6021aeaa58cc0f85262153d9cf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/display_name @@ -0,0 +1 @@ +SecurityChip diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SecurityChip/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/current_value new file mode 100644 index 0000000000000000000000000000000000000000..d8d3670566468b50f573ec75a723ae724f356c6a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/current_value @@ -0,0 +1 @@ +Auto;[Optional:Auto,Slot1(PEG),Slot2(PEG),Slot3(PEG),Slot4(PEG),Slot5(PEG),Slot6(PEG)] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/display_name new file mode 100644 index 0000000000000000000000000000000000000000..19547d1ed17ff1ac4a6c21ece922f90dd75b68b3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/display_name @@ -0,0 +1 @@ +SelectActiveVideo diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SelectActiveVideo/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/current_value new file mode 100644 index 0000000000000000000000000000000000000000..0ef4b9ef765e7a391717ad93be431be98451ae15 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,3F8/IRQ4,2F8/IRQ3,3E8/IRQ4,2E8/IRQ3] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/display_name new file mode 100644 index 0000000000000000000000000000000000000000..6f1aec298ed4fdf0b96ce475e382524de6143d74 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/display_name @@ -0,0 +1 @@ +SerialPort1Address diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SerialPort1Address/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/current_value new file mode 100644 index 0000000000000000000000000000000000000000..dea37224e6bf5645ccf96b67ab76aaa889def906 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,4 Characters,5 Characters,6 Characters,7 Characters,8 Characters,9 Characters,10 Characters,11 Characters,12 Characters] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/display_name new file mode 100644 index 0000000000000000000000000000000000000000..88f78fec1fd278751e487fe3c18fc7be37df7d0e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/display_name @@ -0,0 +1 @@ +SetMinimumLength diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetMinimumLength/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a0547d276943bbd4f3821f1d46ed367f78eb7cb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0e7bef9150f1e8608a017e0bae11b6be8c23ec74 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/display_name @@ -0,0 +1 @@ +SetStrongPassword diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SetStrongPassword/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/current_value new file mode 100644 index 0000000000000000000000000000000000000000..94b37fb17aeff87e1c264a41e87c2a07ced2eba2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Read Only,No Access] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/display_name new file mode 100644 index 0000000000000000000000000000000000000000..59772497c0a67ea031350bb1986db4b366144922 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/display_name @@ -0,0 +1 @@ +SmartUSBProtection diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/SmartUSBProtection/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/current_value new file mode 100644 index 0000000000000000000000000000000000000000..ed32edc7fc12303503e06e314a88b00d72aada2b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/current_value @@ -0,0 +1 @@ +Primary;[Optional:Primary,Automatic][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3422afaaf599c778ede535e3689edc43c19ea821 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/display_name @@ -0,0 +1 @@ +StartupSequence diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/StartupSequence/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/display_name new file mode 100644 index 0000000000000000000000000000000000000000..c4bc2f897657970d47868d15473b2ed2ca2381f7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/display_name @@ -0,0 +1 @@ +USBChargingPortInS4S5 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBChargingPortInS4S5/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7d03035734024238ae0ee75f830fdc1555a79309 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/display_name @@ -0,0 +1 @@ +USBPortAccess diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBPortAccess/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8a1483b1396fb79e26e54217e6caa036963ea009 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/current_value @@ -0,0 +1 @@ +20 sec;[Optional:1 sec,5 sec,10 sec,20 sec] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/display_name new file mode 100644 index 0000000000000000000000000000000000000000..e4949e2a5e7a7863dc2cadf0fe8b12f1b357a4e4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/display_name @@ -0,0 +1 @@ +USBTransferTimeout diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/USBTransferTimeout/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..0239cfb2a0ce1dcec52ed70c1f7cabbe398c3e70 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmFriday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmFriday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..b1917c13b4caeb2792a21ae5e2ea4d0ede59da55 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmMonday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmMonday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..9ee0098be559bbca9c451551077aaddd3e92efcf --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmSaturday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSaturday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..bf8e412ffe808391171c90d97ed220ba3bb99839 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmSunday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmSunday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..089fdd88ca73753f3ebeb236adbee13db8e008bc --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmThursday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmThursday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/current_value new file mode 100644 index 0000000000000000000000000000000000000000..8340985ed806c6f661f392ff774a1d6478ea59f2 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/current_value @@ -0,0 +1 @@ +[00:00:00][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/display_name new file mode 100644 index 0000000000000000000000000000000000000000..fc5e66172d6b5cc9eb1a142339034b468e44add0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/display_name @@ -0,0 +1 @@ +UserDefinedAlarmTime diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTime/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f521e5cb0c7c466bfaccd2190478df6adf307cb4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmTuesday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmTuesday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/current_value new file mode 100644 index 0000000000000000000000000000000000000000..f0ea4a77dc2bd21784bf0931823b6315662d3ec3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/current_value @@ -0,0 +1 @@ +Disable;[Optional:Disable,Enable][Status:ShowOnly] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/display_name new file mode 100644 index 0000000000000000000000000000000000000000..3da79accdb71e827bd68ef88f4b895a6118369b5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/display_name @@ -0,0 +1 @@ +UserDefinedAlarmWednesday diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/UserDefinedAlarmWednesday/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/current_value new file mode 100644 index 0000000000000000000000000000000000000000..9d4a122f1aa68216db935bdab7584727b599c9c4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/current_value @@ -0,0 +1 @@ +Disable;[Optional:Single Event,Daily Event,Weekly Event,Disable,User Defined] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/display_name new file mode 100644 index 0000000000000000000000000000000000000000..7b25a4612f9b1b98582d34e028af64148af70aa0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/display_name @@ -0,0 +1 @@ +WakeUponAlarm diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeUponAlarm/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/current_value new file mode 100644 index 0000000000000000000000000000000000000000..80d7fa371e37edfcb484f983b72342d68aeb55a9 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/current_value @@ -0,0 +1 @@ +Automatic;[Optional:Primary,Automatic,Disable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/display_name new file mode 100644 index 0000000000000000000000000000000000000000..66661f6daee75b42ea271986c3a42c9e8c28a0a3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/display_name @@ -0,0 +1 @@ +WakeonLAN diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WakeonLAN/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/display_name new file mode 100644 index 0000000000000000000000000000000000000000..09b95008a50eca42f205d12913f0f613761ba9fe --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/display_name @@ -0,0 +1 @@ +WindowsUEFIFirmwareUpdate diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/WindowsUEFIFirmwareUpdate/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/current_value b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/current_value new file mode 100644 index 0000000000000000000000000000000000000000..86e12a3074c36f56a313b1887e2264a985762f29 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/current_value @@ -0,0 +1 @@ +Enable;[Optional:Disable,Enable] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/display_name b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/display_name new file mode 100644 index 0000000000000000000000000000000000000000..f0bf00f9dd568269c6854b266ecc18f404377c72 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/display_name @@ -0,0 +1 @@ +XHCIHandoff diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/possible_values b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/XHCIHandoff/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/pending_reboot b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/bios-attrs/lenovo-p620/thinklmi/attributes/pending_reboot @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/cfu-offer.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/cfu-offer.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..61e1d9a084eb71dc70555e6fbd7e41439fff0f30 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/cfu-offer.builder.xml @@ -0,0 +1,13 @@ + + 0x42 + true + true + 0xAB + 0xCD + 0x4567 + 0xFACE + 0xF + 0x1 + 0x2 + 0xDEAD + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/cfu-payload.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/cfu-payload.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2da25cd3dbc8a548d4e7aabb97d76541aa2363a --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/cfu-payload.builder.xml @@ -0,0 +1,12 @@ + + + + aGVsbG8gd29ybGQ= + 0x8001234 + + + aGVsbG8gd29ybGQ= + 0x8005678 + + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/colorhug/README.md b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a9371a10da15323a38f5b7fefefe72ac2a595436 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/README.md @@ -0,0 +1,7 @@ +# Generating the p7b file manually + + certtool --p7-detached-sign --p7-time \ + --load-privkey LVFS/pkcs7/secure-lvfs.rhcloud.com.key \ + --load-certificate LVFS/pkcs7/secure-lvfs.rhcloud.com_signed.pem \ + --infile firmware.bin \ + --outfile firmware.bin.p7b diff --git a/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin new file mode 100644 index 0000000000000000000000000000000000000000..c3f129f25766c71fb82a420de7945d78d4460add Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin.asc b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin.asc new file mode 100644 index 0000000000000000000000000000000000000000..30bdf8b143a3c24e734294a578eeb08d5774d5d3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1 + +iQEcBAABAgAGBQJVLPMHAAoJEK2KUo/sRIgeAxAH/jPY7c2qrG4UEsZXgUFxMUQe +QEufh3cK9cv8kA7SAzpSHy6M0rNanC2vCqcc/fTJI/yBRfBjPPZYEsQgwpB/8m9y +wiTPRuQySwCKsH+ZXNh3j6x8Oaf3DTiO7bJI/M3sOb4fdvb0Csp910g67Nt+HtMw +I5EUM0uvMquZTUygp9B6BBJv8xRKtCNgqvPhyoDZKxKrPzaFwvb7BY50Q03LymU6 +hQUIkjHIvMcTljNocOZNvTBHvEGB2BiBb60QhAXYyNfDrS58pm2JHfw/pgOuQTzT +3Lw9qmedRXbWR95u/piUmyUsY5ey75lD08U/2aE9RLBZ9xR17u1mAgyLGoIMYEk= +=ZdoH +-----END PGP SIGNATURE----- diff --git a/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin.p7b b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin.p7b new file mode 100644 index 0000000000000000000000000000000000000000..963f0d2945bf5d705718e6fa11bc83681ffb92f5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.bin.p7b @@ -0,0 +1,37 @@ +-----BEGIN PKCS7----- +MIIGYAYJKoZIhvcNAQcCoIIGUTCCBk0CAQExDTALBglghkgBZQMEAgEwCwYJKoZI +hvcNAQcBoIIESDCCBEQwggKsoAMCAQICDFmdjlgcgXiV33RlVTANBgkqhkiG9w0B +AQsFADA6MRAwDgYDVQQDEwdMVkZTIENBMSYwJAYDVQQKEx1MaW51eCBWZW5kb3Ig +RmlybXdhcmUgUHJvamVjdDAeFw0xNzA4MDEwMDAwMDBaFw0xOTA4MDEwMDAwMDBa +MBkxFzAVBgNVBAMTDlJpY2hhcmQgSHVnaGVzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA5XlsYGdD5isOAEim4tRR9usJa8C4Gs3TUPfe5EfXcIT44dJr +plVcXpH2Wau/Pbcvc/2cY/bZmgcRMgw8O/4HoJyCCCKfjCfT6yN9BlLnxAgZVLSw +QT2d2JW0m5bY/VgZNwdNZWb+fMnPDx7JMCjtdpUpwQ0R6hwrryRt+6zFyhDayCCL +GOsxpmo7Fc9ix/nP5DEcPjU6Bofz0jFFMesod8babaQSWm2b/QN7aTgkrPjslC+p +BkTLq7IrndgQzLKI9bXn++LFKE2Srm0nHZ6DapKCgsSE3UOqDGtKTUf86aT2IGnV +5JzTZ/HZk/sGqAyS2wb5m13rJfbzkKnf9c14qwIDAQABo4HqMIHnMAwGA1UdEwEB +/wQCMAAwRQYDVR0RBD4wPIYlaHR0cHM6Ly9zZWN1cmUtbHZmcy5yaGNsb3VkLmNv +bS9sdmZzL4ETcmljaGFyZEBodWdoc2llLmNvbTATBgNVHSUEDDAKBggrBgEFBQcD +AzAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBSZpooSP4z6IVWsXkoUjpByAh5D +fzAfBgNVHSMEGDAWgBSxjerkI6d+CY617jHgat2eNDdlrDAqBgNVHR8EIzAhMB+g +HaAbhhlodHRwOi8vd3d3LmZ3dXBkLm9yZy9wa2kvMA0GCSqGSIb3DQEBCwUAA4IB +gQBSXRGZB6YR8wTyuOdEelRcJj45Mz5tiuuCfei8ZOTyFzkGjnRno0Dl57tnmUmX +ufN2Rb9yzXBGHmSTXT6j2uVg+U1xevPAVCWlIslhwxJcqncfpALxL7TwVL5PpJls +/Ao7y/KkS5Bxd8u45A2/wIFkawxn/X0nRmwNh6jF9m3+NSwCv3QxYdgGcfhzD96p +6hG+DuXT97h0lJ3gJJDPbVkWTvuhoNo+iEz8fAfSmlk12HDQ+oQIGRgpFZYHREFr +2/A2HoBfAPFVdmRfYWNrxODrVg3tQEHmtxG7HIHocyRSVzqd31yJKgkwh4I9meUY +rCOf0hhMjWmxiviPKJx4SEcNg7Ye8Ib2OtXxcQbZ71ax57dUyVZZXEcfR3KjBuFp +vY6QnVF5D3NsyV5q3M1VV8XRh9ELRafruX+Ygx8NLkDPKqFGZh0xKDzr55gJF9q8 +rfuHjQ/cd5tokRMI1qlGymbQ/bWgsLBO2MOWeZezITBO1ZVbz6QMJ4YnvHug8nsZ +/SkxggHeMIIB2gIBATBKMDoxEDAOBgNVBAMTB0xWRlMgQ0ExJjAkBgNVBAoTHUxp +bnV4IFZlbmRvciBGaXJtd2FyZSBQcm9qZWN0AgxZnY5YHIF4ld90ZVUwCwYJYIZI +AWUDBAIBoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx +DxcNMTcwODIzMTQzNTQ1WjAvBgkqhkiG9w0BCQQxIgQgoZZQTQmHHaT32DuHS1AP +jubgYZq3mfB0gUsxbYj5b38wDQYJKoZIhvcNAQEBBQAEggEAWc7kxSri1v+c+N8h +S8cerVmAPBm150DjB58F3gxSl91gs/z8d1uWOx88eX0DjOU4C7sQj7E9WiZSPcvb +z2KvXqg7MJy+ev9wXPwDqqPtsVZdLKd665JqF7kfSXxpMFzutu/NxW7UUUrKot4v +d93NlAEXmjjuQ8V6STtYapxzyuWGXThI/K89kXaMvzmqTYQ4S9+98sXG1PMX69zm +z00PT+rL2QGMsZCSUcnE/u38s0q7uCEfBB9uoq5QIECYch65ezX3H2GqVcKPG4M3 +6Ttko+W01+2IIPN02ZHPqXqEw8diTiMYS5HVRD7nVs5TTxNNB+rAIBR+mJJBkxin +7MLHjQ== +-----END PKCS7----- diff --git a/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.metainfo.xml b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..9f5bf8263c6832d212e6ba326d78c291d0740a46 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/firmware.metainfo.xml @@ -0,0 +1,33 @@ + + + + com.hughski.ColorHugALS.firmware + ColorHugALS Firmware + Firmware for the ColorHugALS Ambient Light Sensor + +

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

    +
    + + 84f40464-9272-4ef7-9399-cd95f12da696 + 12345678-1234-1234-1234-123456789012 + + http://www.hughski.com/ + CC0-1.0 + GPL-2.0+ + richard_at_hughsie.com + Hughski Limited + + + +

    This stable release fixes the following bugs:

    +
      +
    • Fix the return code from GetHardwareVersion
    • +
    • Scale the output of TakeReadingRaw by the datasheet values
    • +
    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/libfwupdplugin/tests/colorhug/meson.build b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..08fff74380b9004f667da74f69095a89480f6a7e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/colorhug/meson.build @@ -0,0 +1,11 @@ +colorhug_test_firmware = custom_target('colorhug-test-firmware', + input: [ + 'firmware.bin', + 'firmware.bin.asc', + 'firmware.metainfo.xml', + ], + output: 'colorhug-als-3.0.2.cab', + command: [ + gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', + ], +) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/coswid.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/coswid.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6f479fa2a46b24445419dfb79103603a205c610 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/coswid.builder.xml @@ -0,0 +1,18 @@ + + fwupd-efi:fwupdx64 + 1.4 + semver + fwupdx64 + EFI helpers to install system firmware + 1.3-3-g1d0c69f + + license + https://spdx.org/licenses/LGPL-2.0.html + + + Richard Hughes + hughsie.com + maintainer + tag-creator + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree-fallback/base/compatible b/fwupd-1.8.6/libfwupdplugin/tests/devicetree-fallback/base/compatible new file mode 120000 index 0000000000000000000000000000000000000000..abcb73dbbde7adb2fd257de35b221bc6638ee390 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree-fallback/base/compatible @@ -0,0 +1 @@ +../../devicetree/base/compatible \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/compatible b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/compatible new file mode 100644 index 0000000000000000000000000000000000000000..33529460d183c891311abf8eef628e9ff68cc449 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/compatible differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/ibm,firmware-versions/version b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/ibm,firmware-versions/version new file mode 100644 index 0000000000000000000000000000000000000000..94b2bd66de1319e611b5fccec50c5068b9f4d036 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/ibm,firmware-versions/version @@ -0,0 +1 @@ +1.2.3-4 \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/model b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/model new file mode 100644 index 0000000000000000000000000000000000000000..86c8a128b43972b86133b5c07206682273991463 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/model @@ -0,0 +1 @@ +ColorHug \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/model-name b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/model-name new file mode 100644 index 0000000000000000000000000000000000000000..9d37fc1aca312daab7e33dd588840a10d418cf68 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/model-name @@ -0,0 +1 @@ +To Be Filled By O.E.M. \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/name b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/name new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/name differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vendor b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vendor new file mode 100644 index 0000000000000000000000000000000000000000..1555d1a5cb5df75cc59e16045b35cba64c88a075 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vendor @@ -0,0 +1 @@ +Hughski Limited \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/name b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/name new file mode 100644 index 0000000000000000000000000000000000000000..b8acbc9ee850c5c1285c4a0f54a03d28165f653b Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/name differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name new file mode 100644 index 0000000000000000000000000000000000000000..1f22cb8d670d0d08120031be7c8461f5027f4644 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number new file mode 100644 index 0000000000000000000000000000000000000000..e00c9e144b96eb7f3c36d99893d8b080f4d79719 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number @@ -0,0 +1 @@ +PCB-CH001 \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor new file mode 100644 index 0000000000000000000000000000000000000000..ffbb9e015ef06e998b8912ef49636dabaf1a8a2b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor @@ -0,0 +1 @@ +Richard Hughes \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name new file mode 100644 index 0000000000000000000000000000000000000000..88600030bc3acfe10f5bce653908c3a00e821e6e Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/name b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/name new file mode 100644 index 0000000000000000000000000000000000000000..c6f7bd989f98cda61046b5f287ea2d6462a197ec Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/name differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dfuse.bin b/fwupd-1.8.6/libfwupdplugin/tests/dfuse.bin new file mode 100644 index 0000000000000000000000000000000000000000..3725f8a2bc59a205eadf9a27fd00a7837db5b0c5 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/dfuse.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dfuse.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/dfuse.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..378628fab6898469761e5db75f02ebe8f82a2009 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/dfuse.builder.xml @@ -0,0 +1,29 @@ + + 0x1234 + 0x5678 + 0x8642 + + 0x1 + one + + + aGVsbG8gd29ybGQ= + 0x8001234 + + + aGVsbG8gd29ybGQ= + 0x8005678 + + + + + two + 0x7 + + + aGVsbG8gd29ybGQ= + 0x8000000 + + + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dmi/class/chassis_type b/fwupd-1.8.6/libfwupdplugin/tests/dmi/class/chassis_type new file mode 100644 index 0000000000000000000000000000000000000000..b6a7d89c68e0ca66e96a9a51892cc33db66fb8a3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/dmi/class/chassis_type @@ -0,0 +1 @@ +16 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dmi/class/sys_vendor b/fwupd-1.8.6/libfwupdplugin/tests/dmi/class/sys_vendor new file mode 100644 index 0000000000000000000000000000000000000000..d1beb7188dbf9cb4dc4174e5178f33065e0f2cf7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/dmi/class/sys_vendor @@ -0,0 +1 @@ +FwupdTest diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables/DMI b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables/DMI new file mode 100644 index 0000000000000000000000000000000000000000..2d84367d5140c3b0f9fbe0762a837f2283018429 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables/DMI differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables/smbios_entry_point b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables/smbios_entry_point new file mode 100644 index 0000000000000000000000000000000000000000..189ffc5639f6064cdb788ba5cab50e4946cef067 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables/smbios_entry_point differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables64/DMI b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables64/DMI new file mode 100644 index 0000000000000000000000000000000000000000..45f7b1054dbbbfe8c075b3bd001f776f5fef2e0e Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables64/DMI differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables64/smbios_entry_point b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables64/smbios_entry_point new file mode 100644 index 0000000000000000000000000000000000000000..a0c2b76666ae1449c599f1ecde8fe44253dca403 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/dmi/tables64/smbios_entry_point differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-file.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-file.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..962ee68ed6a88594ac428a0aff6dabf27b121ba3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-file.builder.xml @@ -0,0 +1,7 @@ + + ced4eac6-49f3-4c12-a597-fc8c33447691 + 0x0B + + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-filesystem.bin b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-filesystem.bin new file mode 100644 index 0000000000000000000000000000000000000000..e32cf708275faa90af16532223d0a4e520589dc9 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-filesystem.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-filesystem.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-filesystem.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..75425cf7afb0711bf6cad8595d15658b1b32bdd0 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-filesystem.builder.xml @@ -0,0 +1,15 @@ + + 8c8ce578-8a3d-4f1c-9935-896185c32dd3 + + + 0x3 + ced4eac6-49f3-4c12-a597-fc8c33447691 + aGVsbG8gd29ybGQ= + + + 0x3 + ced4eac6-49f3-4c12-a597-fc8c33447691 + aGVsbG8gd29ybGQ= + + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-section.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-section.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..e179e18a70f234db5d759d18bc3e62c58a5c61e3 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-section.builder.xml @@ -0,0 +1,5 @@ + + 0x02 + ced4eac6-49f3-4c12-a597-fc8c33447691 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-volume.bin b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-volume.bin new file mode 100644 index 0000000000000000000000000000000000000000..7bb01af745565b654a483a65f881ba7a1d46c88d Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-volume.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-volume.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-volume.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..3d0372d64d7af9bddd7cd88b9f3dad4175f7deba --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi-firmware-volume.builder.xml @@ -0,0 +1,5 @@ + + 0x3 + fff12b8d-7696-4c8b-a985-2747075b4f50 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c new file mode 100644 index 0000000000000000000000000000000000000000..56a6051ca2b02b04ef92d5150c9ef600403cb1de --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 new file mode 100644 index 0000000000000000000000000000000000000000..8fbb1772616b13ae00a5aa56bacd27217d63013c Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/meson.build b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..be4253d873310f81b8ce2db9e47354d558305a56 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi/efivars/meson.build @@ -0,0 +1,9 @@ +configure_file(input: 'BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c', + output: 'BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c', + copy: true) +configure_file(input: 'fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416', + output: 'fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416', + copy: true) +configure_file(input: 'SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c', + output: 'SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c', + copy: true) diff --git a/fwupd-1.8.6/libfwupdplugin/tests/efi/meson.build b/fwupd-1.8.6/libfwupdplugin/tests/efi/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c9788153c89b87f3863f3976d995f65d5cc16e79 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/efi/meson.build @@ -0,0 +1 @@ +subdir('efivars') diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fdt.bin b/fwupd-1.8.6/libfwupdplugin/tests/fdt.bin new file mode 100644 index 0000000000000000000000000000000000000000..878488274d38f5d61b68e4d46eca6bacb83945db Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/fdt.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fdt.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/fdt.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..0768d938416e35ad2188a0306886a0698e80443d --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/fdt.builder.xml @@ -0,0 +1,26 @@ + + 0x11 + + 0x5 + hello world + + images + + firmware-1 + 0x123 + + hash-1 + aGVsbG8gd29ybGQ + + + + + configurations + conf-1 + + conf-1 + aGVsbG8gd29ybGQ + + + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/firmware.bin b/fwupd-1.8.6/libfwupdplugin/tests/firmware.bin new file mode 100644 index 0000000000000000000000000000000000000000..d7f1c9f527c918797eb0696cd1cc30e3ec62b7d7 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/firmware.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/firmware.dfu b/fwupd-1.8.6/libfwupdplugin/tests/firmware.dfu new file mode 100644 index 0000000000000000000000000000000000000000..e30f48c5c99a324e678aa6c0aa49f1ad6f72d4b1 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/firmware.dfu differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/firmware.dfuse b/fwupd-1.8.6/libfwupdplugin/tests/firmware.dfuse new file mode 100644 index 0000000000000000000000000000000000000000..19547284ba315bd98ba3e93c724d58217816ebd1 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/firmware.dfuse differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/firmware.hex b/fwupd-1.8.6/libfwupdplugin/tests/firmware.hex new file mode 100644 index 0000000000000000000000000000000000000000..509025a1ffca441e1aae0e6e49c91ee3f3843572 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/firmware.hex @@ -0,0 +1,10 @@ +:044000003DEF20F080 +:10400800FACF01F0FBCF02F0E9CF03F0EACF04F0DA +:10401800E1CF05F0E2CF06F0D9CF07F0DACF08F00C +:10402800F3CF09F0F4CF0AF0F6CF0BF0F7CF0CF08E +:10403800F8CF0DF0F5CF0EF00EC0F5FF0DC0F8FF6C +:104048000CC0F7FF0BC0F6FF0AC0F4FF09C0F3FF6E +:1040580008C0DAFF07C0D9FF06C0E2FF05C0E1FFCC +:1040680004C0EAFF03C0E9FF02C0FBFF01C0FAFF7A +:1040780011003FEF20F0000142EF20F03DEF20F06B +:00000001FF diff --git a/fwupd-1.8.6/libfwupdplugin/tests/firmware.shex b/fwupd-1.8.6/libfwupdplugin/tests/firmware.shex new file mode 100644 index 0000000000000000000000000000000000000000..04128646331d46200ce25182e9b829286af091a7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/firmware.shex @@ -0,0 +1,11 @@ +:100000003DEF20F000000000FACF01F0FBCF02F03E +:10001000E9CF03F0EACF04F0E1CF05F0E2CF06F03C +:10002000D9CF07F0DACF08F0F3CF09F0F4CF0AF018 +:10003000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF0B8 +:100040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FFA8 +:100050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFE8 +:1000600006C0E2FF05C0E1FF04C0EAFF03C0E9FFEC +:1000700002C0FBFF01C0FAFF11003FEF20F00001BA +:0800800042EF20F03DEF20F0FB +:080000FD6465616462656566DB +:00000001FF diff --git a/fwupd-1.8.6/libfwupdplugin/tests/firmware.srec b/fwupd-1.8.6/libfwupdplugin/tests/firmware.srec new file mode 100644 index 0000000000000000000000000000000000000000..890739a7ad605e573c03a4e9789baaf42c70664e --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/firmware.srec @@ -0,0 +1,7 @@ +S0220000687474703A2F2F737265636F72642E736F75726365666F7267652E6E65742F1D +S12300003DEF20F000000000FACF01F0FBCF02F0E9CF03F0EACF04F0E1CF05F0E2CF06F086 +S1230020D9CF07F0DACF08F0F3CF09F0F4CF0AF0F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF0FC +S12300400EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF0AC0F4FF09C0F3FF08C0DAFF07C0D9FFDC +S123006006C0E2FF05C0E1FF04C0EAFF03C0E9FF02C0FBFF01C0FAFF11003FEF20F0000112 +S10B008042EF20F03DEF20F0F7 +S5030005F7 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fit.bin b/fwupd-1.8.6/libfwupdplugin/tests/fit.bin new file mode 100644 index 0000000000000000000000000000000000000000..c794c8c62748465523f159c35db2bbeabed90145 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/fit.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fit.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/fit.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..c635069476cd03d4961c4f96e59c20a0204ef66b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/fit.builder.xml @@ -0,0 +1,36 @@ + + 0x11 + + FIT test + FIT description + 0x629d4abd + + images + + firmware-1 + + u-boot + YWJj + AAABAA== + arm64 + firmware + none + v1.2.4 + + hash-1 + crc32 + 0x352441C2 + + + + + configurations + conf-1 + + conf-1 + alice:bob:clara + firmware-1 + + + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fmap-offset.bin b/fwupd-1.8.6/libfwupdplugin/tests/fmap-offset.bin new file mode 100644 index 0000000000000000000000000000000000000000..53be6d215e2b489596e8da98e8693557b0d326cd Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/fmap-offset.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fmap-offset.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/fmap-offset.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..fcf47b644e87e4a3a2f2d58fe94e09059be06aa7 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/fmap-offset.builder.xml @@ -0,0 +1,11 @@ + + 0x10 + + FMAP + aGVsbG8gd29ybGQ= + + + TEST + V29ybGQh + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fmap.bin b/fwupd-1.8.6/libfwupdplugin/tests/fmap.bin new file mode 100644 index 0000000000000000000000000000000000000000..59850925de3f3c5cc6e0991754cc4f54dbd46fb4 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/fmap.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/fmap.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/fmap.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..dab712cee1f398178662fd239fbab6e9024ed414 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/fmap.builder.xml @@ -0,0 +1,10 @@ + + + FMAP + aGVsbG8gd29ybGQ= + + + TEST + V29ybGQh + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifd-bios.bin b/fwupd-1.8.6/libfwupdplugin/tests/ifd-bios.bin new file mode 100644 index 0000000000000000000000000000000000000000..67b34ae2b84380b985f1b7edfd3835133d771dca Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/ifd-bios.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifd-bios.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/ifd-bios.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..ad5503871b5ab3c1f3d1d718cab68a447ba64dac --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ifd-bios.builder.xml @@ -0,0 +1,15 @@ + + bios + 0x1 + 0x1000 + + fff12b8d-7696-4c8b-a985-2747075b4f50 + 0xB + aGVsbG8gd29ybGQ= + + + fff12b8d-7696-4c8b-a985-2747075b4f50 + 0xB + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifd-no-bios.bin b/fwupd-1.8.6/libfwupdplugin/tests/ifd-no-bios.bin new file mode 100644 index 0000000000000000000000000000000000000000..21ccf436373b87ab92aab37668ee952c32085d34 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/ifd-no-bios.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifd-no-bios.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/ifd-no-bios.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..701be6eb4be813d0b464a372799d9a599eee494b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ifd-no-bios.builder.xml @@ -0,0 +1,19 @@ + + 0x40003 + 0x58100208 + 0x310330 + 0x325c00f5 + 0x42 + + gbe + 0x3 + 0x3000 + V29ybGQh + + + me + 0x2 + 0x2000 + V29ybGQh + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifd.bin b/fwupd-1.8.6/libfwupdplugin/tests/ifd.bin new file mode 100644 index 0000000000000000000000000000000000000000..e617ca4055fcf88984d2696b67c745a197289590 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/ifd.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifd.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/ifd.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..ab36acb2e64afdb5699272aaf0af4fe19b12d0c8 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ifd.builder.xml @@ -0,0 +1,46 @@ + + 0x40003 + 0x58100208 + 0x310330 + 0x325c00f5 + 0x42 + + bios + 0x1 + 0x1000 + + 8c8ce578-8a3d-4f1c-9935-896185c32dd3 + 0xB + + 0x3 + + ced4eac6-49f3-4c12-a597-fc8c33447691 + 0x0B + + 0x02 + ced4eac6-49f3-4c12-a597-fc8c33447691 + aGVsbG8gd29ybGQ= + + + aGVsbG8gd29ybGQ= + + + + ced4eac6-49f3-4c12-a597-fc8c33447691 + aGVsbG8gd29ybGQ= + + + + + fff12b8d-7696-4c8b-a985-2747075b4f50 + 0xB + aGVsbG8gd29ybGQ= + + + + me + 0x2 + 0x2000 + V29ybGQh + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifwi-cpd.bin b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-cpd.bin new file mode 100644 index 0000000000000000000000000000000000000000..9ce3f97c9e2f8b7cb6103c367a27d48ead797ca2 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-cpd.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifwi-cpd.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-cpd.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..b47cedce5586313b396c2ae3c436601b6e5a9ea4 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-cpd.builder.xml @@ -0,0 +1,13 @@ + + 0x1234 + 0x1 + 0x2 + + one + aGVsbG8gd29ybGQ= + + + two + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifwi-fpt.bin b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-fpt.bin new file mode 100644 index 0000000000000000000000000000000000000000..21f920a8e19b8a68983d35352f8e7cfebd68f803 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-fpt.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ifwi-fpt.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-fpt.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..4557b16180497a42d0fc78370c233be9301eef5b --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ifwi-fpt.builder.xml @@ -0,0 +1,10 @@ + + + 0x4f464e49 + aGVsbG8gd29ybGQ= + + + 0x4d495746 + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ihex.bin b/fwupd-1.8.6/libfwupdplugin/tests/ihex.bin new file mode 100644 index 0000000000000000000000000000000000000000..92a00f40daa05c833fc98f873010e1f83208f33c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ihex.bin @@ -0,0 +1,8 @@ +:100000004E6571756520706F72726F2071756973BE +:100010007175616D206573742071756920646F6CF2 +:100020006F72656D20697073756D207175696120DF +:10003000646F6C6F722073697420616D65742C201D +:10004000636F6E73656374657475722C2061646987 +:0C00500070697363692076656C69740A3E +:040000FD646176655F +:00000001FF diff --git a/fwupd-1.8.6/libfwupdplugin/tests/ihex.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/ihex.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..1827424da6df1797b7db93b996323b2490fa0eaa --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/ihex.builder.xml @@ -0,0 +1,7 @@ + + TmVxdWUgcG9ycm8gcXVpc3F1YW0gZXN0IHF1aSBkb2xvcmVtIGlwc3VtIHF1aWEgZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyLCBhZGlwaXNjaSB2ZWxpdAo= + + signature + ZGF2ZQ== + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/intel-thunderbolt.bin b/fwupd-1.8.6/libfwupdplugin/tests/intel-thunderbolt.bin new file mode 100644 index 0000000000000000000000000000000000000000..356e40c9785687f8b33f1e16742f3a0074c06a8a Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/intel-thunderbolt.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/intel-thunderbolt.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/intel-thunderbolt.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac8bddb571e124397683eebe0a6a9a2257cd516f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/intel-thunderbolt.builder.xml @@ -0,0 +1,11 @@ + + 0x10 + 0xd4 + 0x15ef + 0xb070 + titan-ridge + 0x3 + false + false + 0x6000 + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/lockdown/locked/lockdown b/fwupd-1.8.6/libfwupdplugin/tests/lockdown/locked/lockdown new file mode 100644 index 0000000000000000000000000000000000000000..65d9bb9cd99c3173daad8cebe974443142e0f1ce --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/lockdown/locked/lockdown @@ -0,0 +1 @@ +none integrity [confidentiality] diff --git a/fwupd-1.8.6/libfwupdplugin/tests/lockdown/none/lockdown b/fwupd-1.8.6/libfwupdplugin/tests/lockdown/none/lockdown new file mode 100644 index 0000000000000000000000000000000000000000..c33b21074ad52947607d12f0ac1ae18086ea1d9c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/lockdown/none/lockdown @@ -0,0 +1 @@ +[none] integrity confidentiality diff --git a/fwupd-1.8.6/libfwupdplugin/tests/meson.build b/fwupd-1.8.6/libfwupdplugin/tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..74a08ca331696113c140d41fb72bddb18da5eff5 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/meson.build @@ -0,0 +1,2 @@ +subdir('colorhug') +subdir('efi') diff --git a/fwupd-1.8.6/libfwupdplugin/tests/metadata.xml b/fwupd-1.8.6/libfwupdplugin/tests/metadata.xml new file mode 120000 index 0000000000000000000000000000000000000000..18b74f250285678198c4c513ee93bc851359a06c --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/metadata.xml @@ -0,0 +1 @@ +../../src/tests/metadata.xml \ No newline at end of file diff --git a/fwupd-1.8.6/libfwupdplugin/tests/oprom.bin b/fwupd-1.8.6/libfwupdplugin/tests/oprom.bin new file mode 100644 index 0000000000000000000000000000000000000000..fc484f7f406c3df01dd4a1c9e8d2493cabf80cd1 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/oprom.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/oprom.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/oprom.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..e79c6cbab8f8940f968c23382e91a3308fc38b8f --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/oprom.builder.xml @@ -0,0 +1,10 @@ + + 0x1 + 0x0 + 0x1 + aGVsbG8gd29ybGQ= + + cpd + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/quirks.d/tests.quirk b/fwupd-1.8.6/libfwupdplugin/tests/quirks.d/tests.quirk new file mode 100644 index 0000000000000000000000000000000000000000..f7288ed53b0f244b857b8d5270942206bbb4dd30 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/quirks.d/tests.quirk @@ -0,0 +1,26 @@ +[USB\VID_0A5C&PID_6412] +Flags = ignore-runtime + +[ACME Inc.=True] +Name = awesome + +[CORP*] +Name = town + +[USB\VID_0BDA&PID_1100] +Flags = clever +Name = Hub +Children = FuDevice|USB\VID_0763&PID_2806&I2C_01 + +[USB\VID_0763&PID_2806&I2C_01] +Name = HDMI +Flags = updatable,internal + +[CFI\FLASHID_3730] +Name = A25Lxxx +CfiDeviceCmdChipErase = 0xc7 +CfiDeviceCmdSectorErase = 0x20 +CfiDevicePageSize = 0x200 +CfiDeviceSectorSize = 0x2000 +CfiDeviceBlockSize = 0x8000 +FirmwareSizeMax = 0x10000 diff --git a/fwupd-1.8.6/libfwupdplugin/tests/srec-addr32.bin b/fwupd-1.8.6/libfwupdplugin/tests/srec-addr32.bin new file mode 100644 index 0000000000000000000000000000000000000000..04fedfe567c64f76417b71bb6478ea47cde01e71 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/srec-addr32.bin @@ -0,0 +1,4 @@ +S00600004844521B +S3100100000068656C6C6F20776F726C6492 +S5030001FB +S70500000000FA diff --git a/fwupd-1.8.6/libfwupdplugin/tests/srec-addr32.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/srec-addr32.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..087e0407a35de43b6c071fcb289011d93d896d73 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/srec-addr32.builder.xml @@ -0,0 +1,5 @@ + + 0x1000000 + HDR + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/srec.bin b/fwupd-1.8.6/libfwupdplugin/tests/srec.bin new file mode 100644 index 0000000000000000000000000000000000000000..306b69142bf183fbf545358b06a47fa5897460d6 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/srec.bin @@ -0,0 +1,4 @@ +S00600004844521B +S10E000068656C6C6F20776F726C6495 +S5030001FB +S9030000FC diff --git a/fwupd-1.8.6/libfwupdplugin/tests/srec.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/srec.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..9a1f1b3f16e199576c128ae8617293e03fdf31fb --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/srec.builder.xml @@ -0,0 +1,4 @@ + + HDR + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/libfwupdplugin/tests/uswid.bin b/fwupd-1.8.6/libfwupdplugin/tests/uswid.bin new file mode 100644 index 0000000000000000000000000000000000000000..90b27da90dfde5b95d4e641e49cf2dec17914fc4 Binary files /dev/null and b/fwupd-1.8.6/libfwupdplugin/tests/uswid.bin differ diff --git a/fwupd-1.8.6/libfwupdplugin/tests/uswid.builder.xml b/fwupd-1.8.6/libfwupdplugin/tests/uswid.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..895658068e018fad36d6068442d2c5b1fbb8a262 --- /dev/null +++ b/fwupd-1.8.6/libfwupdplugin/tests/uswid.builder.xml @@ -0,0 +1,11 @@ + + foo + 1.2.3 + 0x1 + + 123 + + + 456 + + diff --git a/fwupd-1.8.6/meson.build b/fwupd-1.8.6/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..99faef6c43bac1bf0bb2e870c929d3e8c5ee399a --- /dev/null +++ b/fwupd-1.8.6/meson.build @@ -0,0 +1,609 @@ +project('fwupd', 'c', + version: '1.8.6', + license: 'LGPL-2.1+', + meson_version: '>=0.59.0', + default_options: ['warning_level=2', 'c_std=c99'], +) + +fwupd_version = meson.project_version() +varr = fwupd_version.split('.') +fwupd_major_version = varr[0] +fwupd_minor_version = varr[1] +fwupd_micro_version = varr[2] + +conf = configuration_data() +conf.set('MAJOR_VERSION', fwupd_major_version) +conf.set('MINOR_VERSION', fwupd_minor_version) +conf.set('MICRO_VERSION', fwupd_micro_version) +conf.set_quoted('PACKAGE_VERSION', fwupd_version) + +# get source version, falling back to package version +git = find_program('git', required: false) +tag = false +if git.found() + source_version = run_command(git, 'describe').stdout().strip() + if source_version == '' + source_version = fwupd_version + endif + tag = run_command([git, 'describe', '--exact-match']).returncode() == 0 +else + source_version = fwupd_version +endif +conf.set_quoted('SOURCE_VERSION', source_version) + +# libtool versioning - this applies to libfwupd +# +# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details +# +# - If interfaces have been changed or added, but binary compatibility +# has been preserved, change: +# CURRENT += 1 +# REVISION = 0 +# AGE += 1 +# - If binary compatibility has been broken (eg removed or changed +# interfaces), change: +# CURRENT += 1 +# REVISION = 0 +# AGE = 0 +# - If the interface is the same as the previous version, but bugs are +# fixed, change: +# REVISION += 1 +libfwupd_lt_current = '2' +libfwupd_lt_revision = '0' +libfwupd_lt_age = '0' +libfwupd_lt_version = '@0@.@1@.@2@'.format(libfwupd_lt_current, libfwupd_lt_age, libfwupd_lt_revision) + +# get supported warning flags +warning_flags = [ + '-Waggregate-return', + '-Wunused', + '-Warray-bounds', + '-Wcast-align', + '-Wclobbered', + '-Wdeclaration-after-statement', + '-Wdiscarded-qualifiers', + '-Wduplicated-branches', + '-Wduplicated-cond', + '-Wempty-body', + '-Wformat=2', + '-Wformat-nonliteral', + '-Wformat-security', + '-Wformat-signedness', + '-Wignored-qualifiers', + '-Wimplicit-function-declaration', + '-Winit-self', + '-Wlogical-op', + '-Wmaybe-uninitialized', + '-Wmissing-declarations', + '-Wmissing-format-attribute', + '-Wmissing-include-dirs', + '-Wmissing-noreturn', + '-Wmissing-parameter-type', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wno-cast-function-type', + '-Wno-address-of-packed-member', # incompatible with g_autoptr() + '-Wno-unknown-pragmas', + '-Wno-missing-field-initializers', + '-Wno-strict-aliasing', + '-Wno-suggest-attribute=format', + '-Wno-unknown-warning-option', + '-Wno-unused-parameter', + '-Wold-style-definition', + '-Woverride-init', + '-Wpointer-arith', + '-Wredundant-decls', + '-Wreturn-type', + '-Wshadow', + '-Wsign-compare', + '-Wstrict-aliasing', + '-Wstrict-prototypes', + '-Wswitch-default', + '-Wtype-limits', + '-Wundef', + '-Wuninitialized', + '-Wunused-but-set-variable', + '-Wunused-variable', + '-Wvla', + '-Wwrite-strings' +] +if get_option('static_analysis') and host_machine.system() != 'windows' + warning_flags += ['-fanalyzer', '-Wno-analyzer-null-dereference'] +endif +cc = meson.get_compiler('c') +add_project_arguments(cc.get_supported_arguments(warning_flags), language: 'c') + +if not meson.is_cross_build() + add_project_arguments('-fstack-protector-strong', language: 'c') +endif + +# enable full RELRO where possible +# FIXME: until https://github.com/mesonbuild/meson/issues/1140 is fixed +global_link_args = [] +test_link_args = [ + '-Wl,-z,relro', + '-Wl,-z,defs', + '-Wl,-z,now', + '-Wl,-z,ibt,-z,shstk', +] +foreach arg: test_link_args + if cc.has_link_argument(arg) + global_link_args += arg + endif +endforeach +add_project_link_arguments( + global_link_args, + language: 'c' +) + +add_project_arguments('-DFWUPD_COMPILATION', language: 'c') + +# Needed for realpath(), syscall(), cfmakeraw(), etc. +add_project_arguments('-D_DEFAULT_SOURCE', language: 'c') + +# do not use deprecated symbols or defines internally +add_project_arguments('-DFWUPD_DISABLE_DEPRECATED', language: 'c') + +# needed for symlink() and BYTE_ORDER +add_project_arguments('-D_BSD_SOURCE', language: 'c') +add_project_arguments('-D__BSD_VISIBLE', language: 'c') +add_project_arguments('-D_XOPEN_SOURCE=700', language: 'c') + +# needed for memfd_create() +add_project_arguments('-D_GNU_SOURCE', language: 'c') + +# needed for memmem() +add_project_arguments('-D_DARWIN_C_SOURCE=900000', language: 'c') + +# sanity check +if get_option('build') == 'all' + build_standalone = true + build_daemon = true +elif get_option('build') == 'standalone' + build_standalone = true + build_daemon = false +elif get_option('build') == 'library' + build_standalone = false + build_daemon = false +endif + +prefix = get_option('prefix') + +bindir = join_paths(prefix, get_option('bindir')) +libdir = join_paths(prefix, get_option('libdir')) +libexecdir = join_paths(prefix, get_option('libexecdir')) +#this ends up in compiled code, ignore prefix +if host_machine.system() == 'windows' + sysconfdir = get_option('sysconfdir') + localstatedir = get_option('localstatedir') + datadir = get_option('datadir') + installed_test_bindir = get_option('libexecdir') + installed_test_datadir = get_option('datadir') + daemon_dir = get_option('libexecdir') +else + datadir = join_paths(prefix, get_option('datadir')) + sysconfdir = join_paths(prefix, get_option('sysconfdir')) + localstatedir = join_paths(prefix, get_option('localstatedir')) + installed_test_bindir = join_paths(libexecdir, 'installed-tests', meson.project_name()) + installed_test_datadir = join_paths(datadir, 'installed-tests', meson.project_name()) + daemon_dir = join_paths(libexecdir, 'fwupd') +endif +mandir = join_paths(prefix, get_option('mandir')) +localedir = join_paths(prefix, get_option('localedir')) + +diffcmd = find_program('diff') +gio = dependency('gio-2.0', version: '>= 2.45.8') +giounix = dependency('gio-unix-2.0', version: '>= 2.45.8', required: false) +if giounix.found() + conf.set('HAVE_GIO_UNIX', '1') +endif +if gio.version().version_compare ('>= 2.55.0') + conf.set('HAVE_GIO_2_55_0', '1') +endif +gmodule = dependency('gmodule-2.0') +if build_standalone +gudev = dependency('gudev-1.0', version: '>= 232', + required: get_option('gudev').disable_auto_if(host_machine.system() != 'linux')) +if gudev.found() + conf.set('HAVE_GUDEV', '1') +endif +bluez = get_option('bluez').disable_auto_if(host_machine.system() != 'linux') +if bluez.allowed() + conf.set('HAVE_BLUEZ', '1') +endif +host_cpu = host_machine.cpu_family() +hsi = get_option('hsi').disable_auto_if(host_machine.system() != 'linux').disable_auto_if(host_cpu != 'x86' and host_cpu != 'x86_64').allowed() +if hsi + conf.set('HAVE_HSI', '1') +endif +libxmlb = dependency('xmlb', version: '>= 0.1.13', fallback: ['libxmlb', 'libxmlb_dep']) +gusb = dependency('gusb', version: '>= 0.3.0', fallback: ['gusb', 'gusb_dep'], required: get_option('gusb')) +if gusb.found() + conf.set('HAVE_GUSB', '1') +endif +sqlite = dependency('sqlite3', required: get_option('sqlite')) +if sqlite.found() + conf.set('HAVE_SQLITE', '1') +endif +libarchive = dependency('libarchive', required: get_option('libarchive')) +if libarchive.found() + conf.set('HAVE_LIBARCHIVE', '1') + if cc.has_header_symbol('archive.h', 'archive_write_add_filter_zstd') + conf.set('HAVE_LIBARCHIVE_WRITE_ADD_FILTER_ZSTD', '1') + endif +endif +endif +libjcat = dependency('jcat', version: '>= 0.1.4', fallback: ['libjcat', 'libjcat_dep']) +libjsonglib = dependency('json-glib-1.0', version: '>= 1.1.1') +valgrind = dependency('valgrind', required: false) +libcurl = dependency('libcurl', version: '>= 7.56.0', required: get_option('curl')) +if libcurl.found() + conf.set('HAVE_LIBCURL', '1') + if libcurl.version().version_compare('>= 7.62.0') + conf.set('HAVE_LIBCURL_7_62_0', '1') + endif +endif +polkit = dependency('polkit-gobject-1', version: '>= 0.103', + required: get_option('polkit').disable_auto_if(host_machine.system() != 'linux')) +if polkit.found() + conf.set('HAVE_POLKIT', '1') + if polkit.version().version_compare('>= 0.114') + conf.set('HAVE_POLKIT_0_114', '1') + endif + conf.set_quoted ('POLKIT_ACTIONDIR', polkit.get_variable(pkgconfig: 'actiondir')) +endif +if build_daemon + if not polkit.found() + warning('Polkit is disabled, the daemon will allow ALL client actions') + endif + udevdir = get_option('udevdir') + if udevdir == '' and host_machine.system() == 'linux' + udev = dependency('udev') + udevdir = udev.get_variable(pkgconfig: 'udevdir') + endif +endif +libm = cc.find_library('m', required: false) +libgcab = dependency('libgcab-1.0', version: '>= 1.0', fallback: ['gcab', 'gcab_dep']) +if libgcab.type_name() == 'pkgconfig' and cc.has_function('gcab_file_set_bytes', dependencies: libgcab) + conf.set('HAVE_GCAB_FILE_SET_BYTES', '1') +endif + +bashcomp = dependency('bash-completion', required: false) +python3 = import('python').find_installation('python3') + +gnutls = dependency('gnutls', version: '>= 3.6.0', required: get_option('gnutls')) +if gnutls.found() + conf.set('HAVE_GNUTLS', '1') +endif + +lzma = dependency('liblzma', required: get_option('lzma')) +if lzma.found() + conf.set('HAVE_LZMA', '1') +endif + +cbor = dependency('libcbor', version: '>= 0.7.0', required: get_option('cbor')) +if cbor.found() + conf.set('HAVE_CBOR', '1') +endif + +platform_deps = [] +if get_option('default_library') != 'static' + if host_machine.system() == 'windows' + platform_deps += cc.find_library('shlwapi') + endif + if host_machine.system() == 'freebsd' + platform_deps += dependency('efivar') + endif +endif + +if valgrind.found() + conf.set('HAVE_VALGRIND', '1') +endif + +libsystemd = dependency('libsystemd', + required: get_option('systemd').disable_auto_if(host_machine.system() != 'linux')) +offline = get_option('offline').require(libsystemd.found(), + error_message: '-Doffline=enabled requires systemd') +if offline.allowed() + conf.set('HAVE_FWUPDOFFLINE', '1') +endif + +if cc.has_header('sys/utsname.h') + conf.set('HAVE_UTSNAME_H', '1') +endif +if cc.has_header('sys/inotify.h') + conf.set('HAVE_INOTIFY_H', '1') +endif +if cc.has_header('sys/ioctl.h') + conf.set('HAVE_IOCTL_H', '1') +endif +if cc.has_header('errno.h') + conf.set('HAVE_ERRNO_H', '1') +endif +if cc.has_header('sys/socket.h') + conf.set('HAVE_SOCKET_H', '1') +endif +if cc.has_header('sys/select.h') + conf.set('HAVE_SELECT_H', '1') +endif +if cc.has_header('sys/io.h') and cc.has_function('outb', prefix: '#include ') + conf.set('HAVE_IO_H', '1') +endif +if cc.has_header('linux/ethtool.h') + conf.set('HAVE_ETHTOOL_H', '1') +endif +if cc.has_header('linux/mei.h') + conf.set('HAVE_MEI_H', '1') +endif +if cc.has_header('mtd/mtd-user.h') + conf.set('HAVE_MTD_USER_H', '1') +endif +if cc.has_header('linux/hidraw.h') + conf.set('HAVE_HIDRAW_H', '1') +endif +if cc.has_header('sys/mman.h') + conf.set('HAVE_MMAN_H', '1') +endif +if cc.has_header('poll.h') + conf.set('HAVE_POLL_H', '1') +endif +if cc.has_header('fnmatch.h') + conf.set('HAVE_FNMATCH_H', '1') +endif +if cc.has_header('kenv.h') + conf.set('HAVE_KENV_H', '1') +endif +if cc.has_header('malloc.h') + conf.set('HAVE_MALLOC_H', '1') + if cc.has_function('malloc_trim', prefix: '#include ') + conf.set('HAVE_MALLOC_TRIM', '1') + endif +endif +has_cpuid = cc.has_header_symbol('cpuid.h', '__get_cpuid_count', required: get_option('plugin_msr')) +if has_cpuid + conf.set('HAVE_CPUID_H', '1') +endif +if cc.has_function('getuid') + conf.set('HAVE_GETUID', '1') +endif +if cc.has_function('realpath') + conf.set('HAVE_REALPATH', '1') +endif +if cc.has_function('memmem') + conf.set('HAVE_MEMMEM', '1') +endif +if cc.has_function('sigaction') + conf.set('HAVE_SIGACTION', '1') +endif +if cc.has_function('memfd_create') + conf.set('HAVE_MEMFD_CREATE', '1') +endif +if cc.has_header_symbol('locale.h', 'LC_MESSAGES') + conf.set('HAVE_LC_MESSAGES', '1') +endif +if cc.has_header('linux/ipmi.h') + have_linux_ipmi = true + conf.set('HAVE_LINUX_IPMI_H', '1') +else + have_linux_ipmi = false +endif +if cc.has_header_symbol('fcntl.h', 'F_WRLCK') + conf.set('HAVE_WRLCK', '1') +endif +if cc.has_header_symbol('fcntl.h', 'F_OFD_SETLK') + conf.set('HAVE_OFD', '1') +endif +if cc.has_function('pwrite', args: '-D_XOPEN_SOURCE') + conf.set('HAVE_PWRITE', '1') +endif + +if host_machine.system() == 'freebsd' + if cc.has_type('struct efi_esrt_entry_v1', prefix: '#include \n#include ') + conf.set('HAVE_FREEBSD_ESRT', '1') + endif +endif + +efiboot = dependency('efiboot', required: get_option('plugin_uefi_capsule')) +efivar = dependency('efivar', required: get_option('plugin_uefi_capsule')) +if build_standalone and efiboot.found() and efivar.found() + + if cc.has_header_symbol('efivar/efivar-types.h', 'efi_time_t', dependencies: efivar) + conf.set('HAVE_EFI_TIME_T', '1') + endif + + efi_app_location = join_paths(libexecdir, 'fwupd', 'efi') + conf.set_quoted('EFI_APP_LOCATION', efi_app_location) + + if host_cpu == 'x86' + EFI_MACHINE_TYPE_NAME = 'ia32' + elif host_cpu == 'x86_64' + EFI_MACHINE_TYPE_NAME = 'x64' + elif host_cpu == 'arm' + EFI_MACHINE_TYPE_NAME = 'arm' + elif host_cpu == 'aarch64' + EFI_MACHINE_TYPE_NAME = 'aa64' + else + EFI_MACHINE_TYPE_NAME = '' + endif + conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME) + + if get_option('efi_binary') + efi_binary = dependency ('fwupd-efi', fallback: ['fwupd-efi', 'fwupd_efi_dep']) + endif + + if get_option('plugin_uefi_capsule_splash') + r = run_command([python3, 'po/test-deps']) + if r.returncode() != 0 + error(r.stdout()) + endif + endif +endif + +if get_option('soup_session_compat') + conf.set('SOUP_SESSION_COMPAT', '1') +endif + +umockdev = dependency('umockdev-1.0', required: false) + +if build_standalone + libflashrom = dependency('flashrom', + fallback: ['flashrom', 'flashrom_dep'], + required: get_option('plugin_flashrom').disable_auto_if(host_machine.system() != 'linux')) +endif + +if libsystemd.found() + systemd = dependency('systemd', version: '>= 211', required: get_option('systemd')) + conf.set('HAVE_SYSTEMD' , '1') + conf.set('HAVE_LOGIND' , '1') + systemd_root_prefix = get_option('systemd_root_prefix') + if systemd_root_prefix == '' + systemdunitdir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir') + systemdsystempresetdir = systemd.get_variable(pkgconfig: 'systemdsystempresetdir') + systemd_shutdown_dir = systemd.get_variable(pkgconfig: 'systemdshutdowndir') + systemd_modules_load_dir = systemd.get_variable(pkgconfig: 'modulesloaddir') + else + systemdunitdir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir', pkgconfig_define: ['rootprefix', systemd_root_prefix]) + systemdsystempresetdir = systemd.get_variable(pkgconfig: 'systemdsystempresetdir', pkgconfig_define: ['rootprefix', systemd_root_prefix]) + systemd_root_prefix_varname = 'root_prefix' + if systemd.version().version_compare('< 246') + systemd_root_prefix_varname = 'rootprefix' + endif + systemd_shutdown_dir = systemd.get_variable(pkgconfig: 'systemdshutdowndir', pkgconfig_define: [systemd_root_prefix_varname, systemd_root_prefix]) + systemd_modules_load_dir = systemd.get_variable(pkgconfig: 'modulesloaddir', pkgconfig_define: [systemd_root_prefix_varname, systemd_root_prefix]) + endif +endif + +elogind = dependency('systemd', 'libelogind', required: get_option('elogind')) +if elogind.found() + conf.set('HAVE_LOGIND' , '1') +endif + +if get_option('consolekit').disable_auto_if(host_machine.system() != 'linux').allowed() + conf.set('HAVE_CONSOLEKIT' , '1') +endif + +if get_option('supported_build').disable_auto_if(not tag).allowed() + conf.set('SUPPORTED_BUILD', '1') +endif + +gnome = import('gnome') +i18n = import('i18n') + +conf.set_quoted('FWUPD_PREFIX', prefix) +conf.set_quoted('FWUPD_BINDIR', bindir) +conf.set_quoted('FWUPD_LIBDIR', libdir) +conf.set_quoted('FWUPD_LIBEXECDIR', libexecdir) +conf.set_quoted('FWUPD_DATADIR', datadir) +conf.set_quoted('FWUPD_LOCALSTATEDIR', localstatedir) +conf.set_quoted('FWUPD_SYSCONFDIR', sysconfdir) +conf.set_quoted('FWUPD_LOCALEDIR', localedir) + +if build_standalone +if host_machine.system() == 'windows' + libdir_pkg = 'fwupd-@0@'.format(fwupd_version) +else + libdir_pkg = join_paths(libdir, 'fwupd-@0@'.format(fwupd_version)) +endif +conf.set_quoted('FWUPD_LIBDIR_PKG', libdir_pkg) +endif + +# sanity check, otherwise there is not point building +if build_standalone and host_machine.system() == 'windows' and not gusb.found() + error('gusb feature is required for Windows build') +endif + +conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) +conf.set_quoted('PACKAGE_NAME', meson.project_name()) +conf.set_quoted('VERSION', meson.project_version()) + +motd_file = '85-fwupd' +motd_dir = 'motd.d' +conf.set_quoted('MOTD_FILE', motd_file) +conf.set_quoted('MOTD_DIR', motd_dir) + +configure_file( + output: 'config.h', + configuration: conf +) + +protobufc = dependency('libprotobuf-c', required: get_option('plugin_logitech_bulkcontroller')) +protoc = find_program('protoc', 'protoc-c', required: get_option('plugin_logitech_bulkcontroller')) + +root_incdir = include_directories('.') + +fwupd_gir = [] +gir_dep = dependency('gobject-introspection-1.0', required: get_option('introspection')) +introspection = get_option('introspection').disable_auto_if(host_machine.system() != 'linux').disable_auto_if(not gir_dep.found()) + +markdown_version = run_command( + python3, '-c', 'import markdown; print(markdown.__version__)' +).stdout().strip() +docs_python_deps = get_option('docs').require(markdown_version.version_compare('>=3.2'), + error_message: 'docs=enabled requires at least markdown >= 3.2') +gidocgen_dep = dependency('gi-docgen', + version: '>= 2021.1', + native: true, + fallback: ['gi-docgen', 'dummy_dep'], + required: docs_python_deps, +) +gidocgen_app = find_program('gi-docgen', required: docs_python_deps) + +# using "meson configure -Db_sanitize=address,undefined" is super useful in finding corruption, +# but it does not work with our GMainContext-abuse tests... +if get_option('b_sanitize') in ['address,undefined', 'address', 'undefined', 'leak'] + run_sanitize_unsafe_tests = false +else + run_sanitize_unsafe_tests = true +endif + +subdir('libfwupd') +if polkit.found() + subdir('policy') +endif +if build_standalone + plugin_quirks = [] + gcab = find_program('gcab', required: get_option('tests')) + subdir('data') + subdir('po') + subdir('libfwupdplugin') + subdir('contrib') + + # common to all plugins + plugin_builtins = [] + plugin_incdirs = [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ] + plugin_libs = [ + fwupd, + fwupdplugin, + ] + subdir('plugins') + subdir('src') +endif +subdir('docs') + +# append all the quirks into one big file and gzip it -- lzma is smaller, but needs libxmlb 0.3.3 +builtin_quirk = custom_target('builtin-quirk', + input: plugin_quirks, + output: 'builtin.quirk', + capture: true, + command: ['cat', '@INPUT@'], +) +gzip = find_program('gzip') +custom_target('builtin-quirk-gz', + input: builtin_quirk, + output: 'builtin.quirk.gz', + capture: true, + command: [gzip, '-k', '--stdout', '@INPUT@'], + install: true, + install_dir: join_paths(datadir, 'fwupd', 'quirks.d'), +) + +if build_daemon and libsystemd.found() + install_symlink('fwupd-offline-update.service', + install_dir: join_paths(systemdunitdir, 'system-update.target.wants'), + pointing_to: join_paths('..', 'fwupd-offline-update.service') + ) +endif diff --git a/fwupd-1.8.6/meson_options.txt b/fwupd-1.8.6/meson_options.txt new file mode 100644 index 0000000000000000000000000000000000000000..71292cdec1974681fb7ebb5da4d1b562520bdfa2 --- /dev/null +++ b/fwupd-1.8.6/meson_options.txt @@ -0,0 +1,69 @@ +option('build', type : 'combo', choices : ['all', 'standalone', 'library'], value : 'all', description : 'build type') +option('consolekit', type : 'feature', description : 'ConsoleKit support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('static_analysis', type : 'boolean', value : false, description : 'enable GCC static analysis support') +option('firmware-packager', type : 'boolean', value : true, description : 'enable firmware-packager installation') +option('docs', type : 'feature', description : 'Build developer documentation', deprecated: {'docgen': 'enabled', 'none': 'disabled'}) +option('introspection', type : 'feature', description : 'generate GObject Introspection data', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('lvfs', type : 'combo', choices : ['true', 'false', 'disabled'], value : 'true', description : 'install LVFS remotes') +option('man', type : 'boolean', value : true, description : 'enable man pages') +option('libarchive', type : 'feature', description : 'libarchive support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('gudev', type : 'feature', description : 'GUdev support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('gusb', type : 'feature', description : 'GUsb support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('bluez', type : 'feature', description : 'BlueZ support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('polkit', type: 'feature', description : 'PolKit support in daemon', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('gnutls', type: 'feature', description : 'GnuTLS support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('sqlite', type: 'feature', description : 'sqlite support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('lzma', type: 'feature', description : 'LZMA support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('cbor', type: 'feature', description : 'CBOR support for coSWID and uSWID') +option('plugin_amt', type : 'feature', description : 'Intel AMT support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_acpi_phat', type : 'feature', description : 'ACPI PHAT support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_android_boot', type : 'feature', description : 'Android Boot support') +option('plugin_bcm57xx', type : 'feature', description : 'BCM57xx support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_cfu', type : 'feature', description : 'CFU support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_cpu', type : 'feature', description : 'CPU support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_dell', type : 'feature', description : 'Dell-specific support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_dummy', type : 'boolean', value : false, description : 'enable the dummy device') +option('plugin_emmc', type : 'feature', description : 'eMMC support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_ep963x', type : 'feature', description : 'EP963x support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_fastboot', type : 'feature', description : 'Fastboot support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_gpio', type : 'feature', description : 'Linux GPIO support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_logitech_bulkcontroller', type : 'feature', description : 'Logitech bulk controller support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_parade_lspcon', type : 'feature', description : 'Parade LSPCON support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_pixart_rf', type : 'feature', description : 'PixartRF support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_realtek_mst', type : 'feature', description : 'Realtek MST hub support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_synaptics_mst', type: 'feature', description : 'Synaptics MST hub support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_synaptics_rmi', type: 'feature', description : 'Synaptics RMI support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_scsi', type: 'feature', description : 'SCSI support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_tpm', type : 'feature', description : 'TPM support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_redfish', type : 'feature' , description : 'Redfish support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_uefi_capsule', type : 'feature', description : 'UEFI capsule support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_uefi_capsule_splash', type : 'boolean', value : true, description : 'enable UEFI capsule splash support') +option('plugin_uefi_pk', type : 'feature', description : 'UEFI PK support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_nitrokey', type : 'feature', description : 'Nitrokey support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_nvme', type : 'feature', description : 'NVMe support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_modem_manager', type : 'feature', description : 'ModemManager support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_msr', type : 'feature', description : 'MSR support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_mtd', type : 'feature', description : 'MTD support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_flashrom', type : 'feature', description : 'flashrom support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_intel_spi', type : 'boolean', value : false, description : 'enable Intel SPI support') +option('plugin_uf2', type : 'feature', description : 'support for UF2', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_upower', type : 'feature', description : 'support for UPower', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('plugin_powerd', type : 'feature', description : 'support for powerd', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('qubes', type : 'boolean', value : false, description : 'build packages for Qubes OS') +option('supported_build', type : 'feature', description: 'distribution package with upstream support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('systemd', type : 'feature', description : 'systemd support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('systemd_unit_user', type : 'string', description : 'User account to use for fwupd-refresh.service (empty for DynamicUser)') +option('systemd_root_prefix', type: 'string', value: '', description: 'Directory to base systemd’s installation directories on') +option('elogind', type : 'feature', description : 'elogind support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('tests', type : 'boolean', value : true, description : 'enable tests') +option('soup_session_compat', type : 'boolean', value : true, description : 'enable SoupSession runtime compatibility support') +option('curl', type : 'feature', description : 'libcurl support', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('udevdir', type: 'string', value: '', description: 'Directory for udev rules') +option('efi_os_dir', type: 'string', description : 'the hardcoded name of OS directory in ESP, e.g. fedora') +option('efi_binary', type: 'boolean', value : true, description : 'generate uefi binary if missing') +option('metainfo', type: 'boolean', value : true, description : 'install the project metainfo.xml information') +option('bash_completion', type: 'boolean', value : true, description : 'enable bash completion') +option('fish_completion', type: 'boolean', value : true, description : 'enable fish completion') +option('offline', type: 'feature', description : 'Allow installing firmware using a pre-boot systemd target', deprecated: {'true': 'enabled', 'false': 'disabled'}) +option('compat_cli', type: 'boolean', value : true, description : 'enable legacy commands: fwupdagent,dfu-tool,fwupdate') +option('hsi', type: 'feature', description : ' Host Security Information', deprecated: {'true': 'enabled', 'false': 'disabled'}) diff --git a/fwupd-1.8.6/plugins/README.md b/fwupd-1.8.6/plugins/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3279a01a1fa9d34f5438d9f06dba0366fea523bf --- /dev/null +++ b/fwupd-1.8.6/plugins/README.md @@ -0,0 +1,28 @@ +# Adding a new plugin + +An extensible architecture allows for providing new plugin types (for reading +and writing different firmware) as well as ways quirk their behavior. + +You can find more information about the architecture in the developers section +of the [fwupd website](https://fwupd.org). + +You can use the [fwupd developer documentation](https://fwupd.github.io) to assist +with APIs available to write the plugin. + +If you have a firmware specification and would like to see support +in this project, please file an issue and share the spec. Patches are also +welcome. + +We will not accept plugins that upgrade hardware using a proprietary Linux +executable, proprietary UEFI executable, proprietary library, or DBus interface. + +## Plugin interaction + +Some plugins may be able to influence the behavior of other plugins. +This includes things like one plugin turning on a device, or providing missing +metadata to another plugin. + +The ABI for these interactions is defined in: + + +All interactions between plugins should have the interface defined in that file. diff --git a/fwupd-1.8.6/plugins/acpi-dmar/README.md b/fwupd-1.8.6/plugins/acpi-dmar/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f4c606c89e461db0ffe84eb7821f6797ce52be24 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/README.md @@ -0,0 +1,10 @@ +# DMA Protection + +## Introduction + +This plugin checks if DMA remapping for Thunderbolt devices is available. The +result will be stored in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/firmware/acpi/tables`. diff --git a/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar-plugin.c b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..5ef60aec9fa9539fb0bf979d0351f63604e8e953 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar-plugin.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-acpi-dmar-plugin.h" +#include "fu-acpi-dmar.h" + +struct _FuAcpiDmarPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAcpiDmarPlugin, fu_acpi_dmar_plugin, FU_TYPE_PLUGIN) + +static void +fu_acpi_dmar_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuAcpiDmar) dmar = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + + /* only Intel */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_INTEL) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION); + fu_security_attrs_append(attrs, attr); + + /* load DMAR table */ + path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); + fn = g_build_filename(path, "DMAR", NULL); + blob = fu_bytes_get_contents(fn, &error_local); + if (blob == NULL) { + g_debug("failed to load %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + dmar = fu_acpi_dmar_new(blob, &error_local); + if (dmar == NULL) { + g_warning("failed to parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (!fu_acpi_dmar_get_opt_in(dmar)) { + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_acpi_dmar_plugin_init(FuAcpiDmarPlugin *self) +{ +} + +static void +fu_acpi_dmar_plugin_class_init(FuAcpiDmarPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->add_security_attrs = fu_acpi_dmar_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar-plugin.h b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..99e4bda4a30f5d5aa5b1074380df7fcdef74d53b --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAcpiDmarPlugin, fu_acpi_dmar_plugin, FU, ACPI_DMAR_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar.c b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar.c new file mode 100644 index 0000000000000000000000000000000000000000..bf1aecb1d827fae6bb14bb6cc75189146fa7df40 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-dmar.h" + +struct _FuAcpiDmar { + GObject parent_instance; + gboolean opt_in; +}; + +G_DEFINE_TYPE(FuAcpiDmar, fu_acpi_dmar, G_TYPE_OBJECT) + +#define DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG 0x4 + +FuAcpiDmar * +fu_acpi_dmar_new(GBytes *blob, GError **error) +{ + FuAcpiDmar *self = g_object_new(FU_TYPE_ACPI_DMAR, NULL); + gchar creator_id[5] = {'\0'}; + gchar oem_table_id[9] = {'\0'}; + gchar signature[5] = {'\0'}; + gsize bufsz = 0; + guint8 flags = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + + /* parse table */ + if (!fu_memcpy_safe((guint8 *)signature, + sizeof(signature), + 0x0, /* dst */ + buf, + bufsz, + 0x00, /* src */ + sizeof(signature) - 1, + error)) + return NULL; + if (strcmp(signature, "DMAR") != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Not a DMAR table, got %s", + signature); + return NULL; + } + if (!fu_memcpy_safe((guint8 *)oem_table_id, + sizeof(oem_table_id), + 0x0, /* dst */ + buf, + bufsz, + 0x10, /* src */ + sizeof(oem_table_id) - 1, + error)) + return NULL; + g_debug("OemTableId: %s", oem_table_id); + if (!fu_memcpy_safe((guint8 *)creator_id, + sizeof(creator_id), + 0x0, /* dst */ + buf, + bufsz, + 0x1c, /* src */ + sizeof(creator_id) - 1, + error)) + return NULL; + g_debug("CreatorId: %s", creator_id); + if (!fu_memcpy_safe(&flags, + sizeof(flags), + 0x0, /* dst */ + buf, + bufsz, + 0x25, /* src */ + sizeof(flags), + error)) + return NULL; + g_debug("Flags: 0x%02x", flags); + self->opt_in = (flags & DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG) > 0; + return self; +} + +gboolean +fu_acpi_dmar_get_opt_in(FuAcpiDmar *self) +{ + g_return_val_if_fail(FU_IS_ACPI_DMAR(self), FALSE); + return self->opt_in; +} + +static void +fu_acpi_dmar_class_init(FuAcpiDmarClass *klass) +{ +} + +static void +fu_acpi_dmar_init(FuAcpiDmar *self) +{ +} diff --git a/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar.h b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar.h new file mode 100644 index 0000000000000000000000000000000000000000..46ff5dff31252bdbc963056384ced22de2d05c49 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/fu-acpi-dmar.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_DMAR (fu_acpi_dmar_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiDmar, fu_acpi_dmar, FU, ACPI_DMAR, GObject) + +FuAcpiDmar * +fu_acpi_dmar_new(GBytes *blob, GError **error); +gboolean +fu_acpi_dmar_get_opt_in(FuAcpiDmar *self); diff --git a/fwupd-1.8.6/plugins/acpi-dmar/fu-self-test.c b/fwupd-1.8.6/plugins/acpi-dmar/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..17a48378a36a3da0562105d171fdc59319b8e951 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/fu-self-test.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-acpi-dmar.h" + +static void +fu_acpi_dmar_opt_in_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + g_autoptr(FuAcpiDmar) dmar = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "DMAR", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing DMAR"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + dmar = fu_acpi_dmar_new(blob, &error); + g_assert_no_error(error); + g_assert_nonnull(dmar); + g_assert_true(fu_acpi_dmar_get_opt_in(dmar)); +} + +static void +fu_acpi_dmar_opt_out_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + g_autoptr(FuAcpiDmar) dmar = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "DMAR-OPTOUT", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing DMAR-OPTOUT"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + dmar = fu_acpi_dmar_new(blob, &error); + g_assert_no_error(error); + g_assert_nonnull(dmar); + g_assert_false(fu_acpi_dmar_get_opt_in(dmar)); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/acpi-dmar/opt-in", fu_acpi_dmar_opt_in_func); + g_test_add_func("/acpi-dmar/opt-out", fu_acpi_dmar_opt_out_func); + + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/acpi-dmar/meson.build b/fwupd-1.8.6/plugins/acpi-dmar/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d733e4e5892f58cfaefb39cce22accfbe385d306 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-dmar/meson.build @@ -0,0 +1,37 @@ +if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiDmar"'] + +plugin_builtin_acpi_dmar = static_library('fu_plugin_acpi_dmar', + sources: [ + 'fu-acpi-dmar-plugin.c', + 'fu-acpi-dmar.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_acpi_dmar + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'acpi-dmar-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_acpi_dmar, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('acpi-dmar-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/acpi-facp/README.md b/fwupd-1.8.6/plugins/acpi-facp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..59a56ea0d95199a912bfad959aa7cbd1a057fcac --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/README.md @@ -0,0 +1,10 @@ +# ACPI FACP + +## Introduction + +This plugin checks if S2I sleep is available. The result will be stored in an +security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/firmware/acpi/tables`. diff --git a/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp-plugin.c b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..8d317fc650a9a8dde8e580e8f789cb75715d3355 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp-plugin.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-acpi-facp-plugin.h" +#include "fu-acpi-facp.h" + +struct _FuAcpiFacpPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAcpiFacpPlugin, fu_acpi_facp_plugin, FU_TYPE_PLUGIN) + +static void +fu_acpi_facp_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuAcpiFacp) facp = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE); + fu_security_attrs_append(attrs, attr); + + /* load FACP table */ + path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); + fn = g_build_filename(path, "FACP", NULL); + blob = fu_bytes_get_contents(fn, &error_local); + if (blob == NULL) { + g_debug("failed to load %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + facp = fu_acpi_facp_new(blob, &error_local); + if (facp == NULL) { + g_warning("failed to parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* options are usually "Linux" (S3) or "Windows" (s2idle) */ + fu_security_attr_add_bios_target_value(attr, "com.thinklmi.SleepState", "windows"); + + if (!fu_acpi_facp_get_s2i(facp)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_acpi_facp_plugin_init(FuAcpiFacpPlugin *self) +{ +} + +static void +fu_acpi_facp_plugin_class_init(FuAcpiFacpPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->add_security_attrs = fu_acpi_facp_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp-plugin.h b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6f29ae37331c5ba86643ef65af80070decd4952a --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAcpiFacpPlugin, fu_acpi_facp_plugin, FU, ACPI_FACP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp.c b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp.c new file mode 100644 index 0000000000000000000000000000000000000000..acb9198287ab7e1d7f23200541f711829f6369c6 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-facp.h" + +struct _FuAcpiFacp { + GObject parent_instance; + gboolean get_s2i; +}; + +G_DEFINE_TYPE(FuAcpiFacp, fu_acpi_facp, G_TYPE_OBJECT) + +#define LOW_POWER_S0_IDLE_CAPABLE (1 << 21) + +FuAcpiFacp * +fu_acpi_facp_new(GBytes *blob, GError **error) +{ + FuAcpiFacp *self = g_object_new(FU_TYPE_ACPI_FACP, NULL); + gsize bufsz = 0; + guint32 flags = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + + /* parse table */ + if (!fu_memread_uint32_safe(buf, bufsz, 0x70, &flags, G_LITTLE_ENDIAN, error)) + return NULL; + g_debug("Flags: 0x%04x", flags); + self->get_s2i = (flags & LOW_POWER_S0_IDLE_CAPABLE) > 0; + return self; +} + +gboolean +fu_acpi_facp_get_s2i(FuAcpiFacp *self) +{ + g_return_val_if_fail(FU_IS_ACPI_FACP(self), FALSE); + return self->get_s2i; +} + +static void +fu_acpi_facp_class_init(FuAcpiFacpClass *klass) +{ +} + +static void +fu_acpi_facp_init(FuAcpiFacp *self) +{ +} diff --git a/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp.h b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp.h new file mode 100644 index 0000000000000000000000000000000000000000..08b76f94b4856c9b6d5ac4c60e05a4777ef56599 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/fu-acpi-facp.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_FACP (fu_acpi_facp_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiFacp, fu_acpi_facp, FU, ACPI_FACP, GObject) + +FuAcpiFacp * +fu_acpi_facp_new(GBytes *blob, GError **error); +gboolean +fu_acpi_facp_get_s2i(FuAcpiFacp *self); diff --git a/fwupd-1.8.6/plugins/acpi-facp/fu-self-test.c b/fwupd-1.8.6/plugins/acpi-facp/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..209e779a31fd19852ef862b8cb207788cbe4df62 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/fu-self-test.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-acpi-facp.h" + +static void +fu_acpi_facp_s2i_disabled_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + g_autofree gchar *fn = NULL; + g_autoptr(FuAcpiFacp) facp = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "FACP", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing FACP"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + facp = fu_acpi_facp_new(blob, &error); + g_assert_no_error(error); + g_assert_nonnull(facp); + g_assert_false(fu_acpi_facp_get_s2i(facp)); +} + +static void +fu_acpi_facp_s2i_enabled_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + g_autofree gchar *fn = NULL; + g_autoptr(FuAcpiFacp) facp = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "FACP-S2I", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing FACP-S2I"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + facp = fu_acpi_facp_new(blob, &error); + g_assert_no_error(error); + g_assert_nonnull(facp); + g_assert_true(fu_acpi_facp_get_s2i(facp)); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/acpi-facp/s2i{disabled}", fu_acpi_facp_s2i_disabled_func); + g_test_add_func("/acpi-facp/s2i{enabled}", fu_acpi_facp_s2i_enabled_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/acpi-facp/meson.build b/fwupd-1.8.6/plugins/acpi-facp/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..fb8ce36f88f69fc515e8c8d888424996df574e5d --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-facp/meson.build @@ -0,0 +1,37 @@ +if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiFacp"'] + +plugin_builtin_acpi_facp = static_library('fu_plugin_acpi_facp', + sources: [ + 'fu-acpi-facp-plugin.c', + 'fu-acpi-facp.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_acpi_facp + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'acpi-facp-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_acpi_facp, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('acpi-facp-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/README.md b/fwupd-1.8.6/plugins/acpi-ivrs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d0fba374a094d8714c65d3375f8f1690227f6b97 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/README.md @@ -0,0 +1,10 @@ +# DMA Protection + +## Introduction + +This plugin checks if Pre-boot DMA remapping is available and enabled from the [ACPI IVRS](http://support.amd.com/TechDocs/48882_IOMMU.pdf) table. +The result will be stored in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/firmware/acpi/tables`. diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..604386e79e14268511fc40b3887ea0663f4bc97c --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-acpi-ivrs-plugin.h" +#include "fu-acpi-ivrs.h" + +struct _FuAcpiIvrsPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAcpiIvrsPlugin, fu_acpi_ivrs_plugin, FU_TYPE_PLUGIN) + +static void +fu_acpi_ivrs_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuAcpiIvrs) ivrs = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + + /* only AMD */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_AMD) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION); + fu_security_attrs_append(attrs, attr); + + /* load IVRS table */ + path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); + fn = g_build_filename(path, "IVRS", NULL); + blob = fu_bytes_get_contents(fn, &error_local); + if (blob == NULL) { + g_debug("failed to load %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + ivrs = fu_acpi_ivrs_new(blob, &error_local); + if (ivrs == NULL) { + g_warning("failed to parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (!fu_acpi_ivrs_get_dma_remap(ivrs)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_acpi_ivrs_plugin_init(FuAcpiIvrsPlugin *self) +{ +} + +static void +fu_acpi_ivrs_plugin_class_init(FuAcpiIvrsPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->add_security_attrs = fu_acpi_ivrs_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.h b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..d6f5547483ce46e2d2696ab9d70e99abc888c8e0 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAcpiIvrsPlugin, fu_acpi_ivrs_plugin, FU, ACPI_IVRS_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs.c b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs.c new file mode 100644 index 0000000000000000000000000000000000000000..c24741dd05d3e67c8f7d207d0a0beffa943e4d16 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-ivrs.h" + +struct _FuAcpiIvrs { + GObject parent_instance; + gboolean remap_support; +}; + +G_DEFINE_TYPE(FuAcpiIvrs, fu_acpi_ivrs, G_TYPE_OBJECT) + +/* IVINfo field */ +#define IVRS_DMA_REMAP_SUPPORT_FLAG 0x2 + +FuAcpiIvrs * +fu_acpi_ivrs_new(GBytes *blob, GError **error) +{ + FuAcpiIvrs *self = g_object_new(FU_TYPE_ACPI_IVRS, NULL); + gchar creator_id[5] = {'\0'}; + gchar oem_table_id[9] = {'\0'}; + gchar signature[5] = {'\0'}; + gsize bufsz = 0; + guint8 ivinfo = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + + /* parse table */ + if (!fu_memcpy_safe((guint8 *)signature, + sizeof(signature), + 0x0, /* dst */ + buf, + bufsz, + 0x00, /* src */ + sizeof(signature) - 1, + error)) + return NULL; + if (strcmp(signature, "IVRS") != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Not a IVRS table, got %s", + signature); + return NULL; + } + if (!fu_memcpy_safe((guint8 *)oem_table_id, + sizeof(oem_table_id), + 0x0, /* dst */ + buf, + bufsz, + 0x10, /* src */ + sizeof(oem_table_id) - 1, + error)) + return NULL; + g_debug("OemTableId: %s", oem_table_id); + if (!fu_memcpy_safe((guint8 *)creator_id, + sizeof(creator_id), + 0x0, /* dst */ + buf, + bufsz, + 0x1c, /* src */ + sizeof(creator_id) - 1, + error)) + return NULL; + g_debug("CreatorId: %s", creator_id); + if (!fu_memcpy_safe(&ivinfo, + sizeof(ivinfo), + 0x0, /* dst */ + buf, + bufsz, + 0x24, /* src */ + sizeof(ivinfo), + error)) + return NULL; + g_debug("Flags: 0x%02x", ivinfo); + self->remap_support = ivinfo & IVRS_DMA_REMAP_SUPPORT_FLAG; + return self; +} + +gboolean +fu_acpi_ivrs_get_dma_remap(FuAcpiIvrs *self) +{ + g_return_val_if_fail(FU_IS_ACPI_IVRS(self), FALSE); + return self->remap_support; +} + +static void +fu_acpi_ivrs_class_init(FuAcpiIvrsClass *klass) +{ +} + +static void +fu_acpi_ivrs_init(FuAcpiIvrs *self) +{ +} diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs.h b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs.h new file mode 100644 index 0000000000000000000000000000000000000000..6ebfd3397c3b70bd459667c3eab12c4b49c1d2ec --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/fu-acpi-ivrs.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_IVRS (fu_acpi_ivrs_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiIvrs, fu_acpi_ivrs, FU, ACPI_IVRS, GObject) + +FuAcpiIvrs * +fu_acpi_ivrs_new(GBytes *blob, GError **error); +gboolean +fu_acpi_ivrs_get_dma_remap(FuAcpiIvrs *self); diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/fu-self-test.c b/fwupd-1.8.6/plugins/acpi-ivrs/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..36bc02654c2da91bdbf80ffcf362ce92f5c92e1c --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/fu-self-test.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-acpi-ivrs.h" + +static void +fu_acpi_ivrs_dma_remap_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + g_autoptr(FuAcpiIvrs) ivrs = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "IVRS-REMAP", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing IVRS-REMAP"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + ivrs = fu_acpi_ivrs_new(blob, &error); + g_assert_no_error(error); + g_assert_nonnull(ivrs); + g_assert_true(fu_acpi_ivrs_get_dma_remap(ivrs)); +} + +static void +fu_acpi_ivrs_no_dma_remap_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + g_autoptr(FuAcpiIvrs) ivrs = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "IVRS-NOREMAP", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing IVRS-NOREMAP"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + ivrs = fu_acpi_ivrs_new(blob, &error); + g_assert_no_error(error); + g_assert_nonnull(ivrs); + g_assert_false(fu_acpi_ivrs_get_dma_remap(ivrs)); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/acpi-ivrs/dma-remap-support", fu_acpi_ivrs_dma_remap_func); + g_test_add_func("/acpi-ivrs/no-dma-remap-support", fu_acpi_ivrs_no_dma_remap_func); + + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/acpi-ivrs/meson.build b/fwupd-1.8.6/plugins/acpi-ivrs/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..084bf386d10243ac05a746de344b7d4e366beab1 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-ivrs/meson.build @@ -0,0 +1,37 @@ +if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiIvrs"'] + +plugin_builtin_acpi_ivrs = static_library('fu_plugin_acpi_ivrs', + sources: [ + 'fu-acpi-ivrs-plugin.c', + 'fu-acpi-ivrs.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_acpi_ivrs + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'acpi-ivrs-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_acpi_ivrs, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('acpi-ivrs-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/acpi-phat/README.md b/fwupd-1.8.6/plugins/acpi-phat/README.md new file mode 100644 index 0000000000000000000000000000000000000000..75c9f2aaea8e3ed97d1241e29bff341870efd88e --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/README.md @@ -0,0 +1,21 @@ +# Platform Health Assessment Table + +## Introduction + +The PHAT is an ACPI table where a platform can expose health related telemetry +that may be useful for software running within the constraints of an OS. + +These elements are typically going to encompass things that are likely otherwise +not enumerable during the OS runtime phase of operations, such as version of +pre-OS components. + +The daemon includes some of the PHAT data in the report data sent to the LVFS +so that we can debug failures with the help of the IHV. This allows us to find +the root cause of the problem, and so we know what other OEMs may be affected. + +See +for more information. + +## External Interface Access + +This plugin requires read access to `/sys/firmware/acpi/tables`. diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-health-record.c b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-health-record.c new file mode 100644 index 0000000000000000000000000000000000000000..21ccabc92d41ef350a4238f2e84545f2102245e7 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-health-record.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-phat-health-record.h" +#include "fu-acpi-phat.h" + +struct _FuAcpiPhatHealthRecord { + FuFirmware parent_instance; + guint8 am_healthy; + gchar *guid; + gchar *device_path; +}; + +G_DEFINE_TYPE(FuAcpiPhatHealthRecord, fu_acpi_phat_health_record, FU_TYPE_FIRMWARE) + +static void +fu_acpi_phat_health_record_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); + if (self->guid != NULL) + fu_xmlb_builder_insert_kv(bn, "guid", self->guid); + if (self->device_path != NULL) + fu_xmlb_builder_insert_kv(bn, "device_path", self->device_path); + if (self->am_healthy != 0) + fu_xmlb_builder_insert_kx(bn, "am_healthy", self->am_healthy); +} + +static gboolean +fu_acpi_phat_health_record_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); + gsize bufsz = 0; + guint16 rcdlen = 0; + guint32 dataoff = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + fwupd_guid_t guid = {0x0}; + + /* record length */ + if (!fu_memread_uint16_safe(buf, bufsz, 2, &rcdlen, G_LITTLE_ENDIAN, error)) + return FALSE; + if (rcdlen != bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "record length not valid: %" G_GUINT16_FORMAT, + rcdlen); + return FALSE; + } + + /* am healthy */ + if (!fu_memread_uint8_safe(buf, bufsz, 7, &self->am_healthy, error)) + return FALSE; + + /* device signature */ + if (!fu_memcpy_safe((guint8 *)&guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + 8, /* src */ + sizeof(guid), + error)) + return FALSE; + self->guid = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + + /* read the data offset to work out the size of the middle part */ + if (!fu_memread_uint32_safe(buf, bufsz, 24, &dataoff, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* device path */ + if (bufsz > 28) { + gsize ubufsz; /* bytes */ + g_autofree gunichar2 *ubuf = NULL; + + /* header -> devicepath -> data */ + if (dataoff == 0x0) { + ubufsz = bufsz - 28; + } else { + ubufsz = dataoff - 28; + } + if (ubufsz > bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "device path too large: 0x%x", + (guint)ubufsz); + return FALSE; + } + + /* check this is an even number of bytes */ + if (ubufsz == 0 || ubufsz % 2 != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "device path not valid: %" G_GSIZE_FORMAT, + ubufsz); + return FALSE; + } + + /* align and convert */ + ubuf = g_new0(gunichar2, ubufsz / 2); + if (!fu_memcpy_safe((guint8 *)ubuf, + ubufsz, + 0x0, /* dst */ + buf, + bufsz, + 28, /* src */ + ubufsz, + error)) + return FALSE; + self->device_path = g_utf16_to_utf8(ubuf, ubufsz / 2, NULL, NULL, error); + if (self->device_path == NULL) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_acpi_phat_health_record_write(FuFirmware *firmware, GError **error) +{ + FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); + fwupd_guid_t guid = {0x0}; + glong device_path_utf16sz = 0; + g_autofree gunichar2 *device_path_utf16 = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* convert device path ahead of time to get total record length */ + if (self->device_path != NULL) { + device_path_utf16 = + g_utf8_to_utf16(self->device_path, -1, NULL, &device_path_utf16sz, error); + if (device_path_utf16 == NULL) + return NULL; + device_path_utf16sz *= 2; + } + + /* data record */ + fu_byte_array_append_uint16(buf, FU_ACPI_PHAT_RECORD_TYPE_HEALTH, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 28 + device_path_utf16sz, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, fu_firmware_get_version_raw(firmware)); + fu_byte_array_append_uint8(buf, 0x00); + fu_byte_array_append_uint8(buf, 0x00); + fu_byte_array_append_uint8(buf, self->am_healthy); + + /* device signature */ + if (self->guid != NULL) { + if (!fwupd_guid_from_string(self->guid, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) + return NULL; + } + g_byte_array_append(buf, guid, sizeof(guid)); + + /* device-specific data unsupported */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); + + /* device path */ + if (self->device_path != NULL) { + g_byte_array_append(buf, (const guint8 *)device_path_utf16, device_path_utf16sz); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_acpi_phat_health_record_set_guid(FuAcpiPhatHealthRecord *self, const gchar *guid) +{ + g_free(self->guid); + self->guid = g_strdup(guid); +} + +static void +fu_acpi_phat_health_record_set_device_path(FuAcpiPhatHealthRecord *self, const gchar *device_path) +{ + g_free(self->device_path); + self->device_path = g_strdup(device_path); +} + +static gboolean +fu_acpi_phat_health_record_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); + const gchar *tmp; + guint64 tmp64; + + /* optional properties */ + tmp = xb_node_query_text(n, "device_path", NULL); + if (tmp != NULL) + fu_acpi_phat_health_record_set_device_path(self, tmp); + tmp = xb_node_query_text(n, "guid", NULL); + if (tmp != NULL) + fu_acpi_phat_health_record_set_guid(self, tmp); + tmp64 = xb_node_query_text_as_uint(n, "am_healthy", NULL); + if (tmp64 > G_MAXUINT8) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "am_healthy value invalid, got 0x%x", + (guint)tmp64); + return FALSE; + } + self->am_healthy = (guint8)tmp64; + + /* success */ + return TRUE; +} + +static void +fu_acpi_phat_health_record_init(FuAcpiPhatHealthRecord *self) +{ +} + +static void +fu_acpi_phat_health_record_finalize(GObject *object) +{ + FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(object); + g_free(self->guid); + g_free(self->device_path); + G_OBJECT_CLASS(fu_acpi_phat_health_record_parent_class)->finalize(object); +} + +static void +fu_acpi_phat_health_record_class_init(FuAcpiPhatHealthRecordClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_acpi_phat_health_record_finalize; + klass_firmware->parse = fu_acpi_phat_health_record_parse; + klass_firmware->write = fu_acpi_phat_health_record_write; + klass_firmware->export = fu_acpi_phat_health_record_export; + klass_firmware->build = fu_acpi_phat_health_record_build; +} + +FuFirmware * +fu_acpi_phat_health_record_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_PHAT_HEALTH_RECORD, NULL)); +} diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-health-record.h b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-health-record.h new file mode 100644 index 0000000000000000000000000000000000000000..3ab3d27cf5c14a193f260a52cc43222f90bcf5ad --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-health-record.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_PHAT_HEALTH_RECORD (fu_acpi_phat_health_record_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiPhatHealthRecord, + fu_acpi_phat_health_record, + FU, + ACPI_PHAT_HEALTH_RECORD, + FuFirmware) + +FuFirmware * +fu_acpi_phat_health_record_new(void); diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-plugin.c b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..fa359db201b31f6fb65011f985c1be1266b92609 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-plugin.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-acpi-phat-health-record.h" +#include "fu-acpi-phat-plugin.h" +#include "fu-acpi-phat-version-element.h" +#include "fu-acpi-phat-version-record.h" +#include "fu-acpi-phat.h" + +struct _FuAcpiPhatPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAcpiPhatPlugin, fu_acpi_phat_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_acpi_phat_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + g_autofree gchar *path = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) phat = fu_acpi_phat_new(); + g_autoptr(GBytes) blob = NULL; + + path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); + fn = g_build_filename(path, "PHAT", NULL); + blob = fu_bytes_get_contents(fn, error); + if (blob == NULL) + return FALSE; + if (!fu_firmware_parse(phat, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + str = fu_acpi_phat_to_report_string(FU_ACPI_PHAT(phat)); + fu_plugin_add_report_metadata(plugin, "PHAT", str); + return TRUE; +} + +static void +fu_acpi_phat_plugin_init(FuAcpiPhatPlugin *self) +{ +} + +static void +fu_acpi_phat_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ACPI_PHAT); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ACPI_PHAT_HEALTH_RECORD); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ACPI_PHAT_VERSION_ELEMENT); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ACPI_PHAT_VERSION_RECORD); +} + +static void +fu_acpi_phat_plugin_class_init(FuAcpiPhatPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_acpi_phat_plugin_constructed; + plugin_class->coldplug = fu_acpi_phat_plugin_coldplug; +} diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-plugin.h b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..7bce94a825af79b52489e0fa9f533e1a4957544c --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAcpiPhatPlugin, fu_acpi_phat_plugin, FU, ACPI_PHAT_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-element.c b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-element.c new file mode 100644 index 0000000000000000000000000000000000000000..772fc586c3ff7d471d79955ee925a6ad89ab1a72 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-element.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-phat-version-element.h" + +struct _FuAcpiPhatVersionElement { + FuFirmware parent_instance; + gchar *guid; + gchar *producer_id; +}; + +G_DEFINE_TYPE(FuAcpiPhatVersionElement, fu_acpi_phat_version_element, FU_TYPE_FIRMWARE) + +static void +fu_acpi_phat_version_element_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(firmware); + if (self->guid != NULL) + fu_xmlb_builder_insert_kv(bn, "guid", self->guid); + if (self->producer_id != NULL) + fu_xmlb_builder_insert_kv(bn, "producer_id", self->producer_id); +} + +static gboolean +fu_acpi_phat_version_element_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(firmware); + fwupd_guid_t component_id = {0x0}; + gchar producer_id[4] = {'\0'}; + gsize bufsz = 0; + guint64 version_value = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* hardcoded */ + fu_firmware_set_size(firmware, 28); + + if (!fu_memcpy_safe((guint8 *)&component_id, + sizeof(component_id), + 0x0, /* dst */ + buf, + bufsz, + 0, /* src */ + sizeof(component_id), + error)) + return FALSE; + self->guid = fwupd_guid_to_string(&component_id, FWUPD_GUID_FLAG_MIXED_ENDIAN); + + if (!fu_memread_uint64_safe(buf, bufsz, 16, &version_value, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_firmware_set_version_raw(firmware, version_value); + if (!fu_memcpy_safe((guint8 *)producer_id, + sizeof(producer_id), + 0x0, /* dst */ + buf, + bufsz, + 24, /* src */ + sizeof(producer_id), + error)) + return FALSE; + if (memcmp(producer_id, "\0\0\0\0", 4) == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "PHAT version element invalid"); + return FALSE; + } + self->producer_id = fu_strsafe((const gchar *)producer_id, sizeof(producer_id)); + return TRUE; +} + +static GBytes * +fu_acpi_phat_version_element_write(FuFirmware *firmware, GError **error) +{ + FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(firmware); + fwupd_guid_t guid = {0x0}; + guint8 producer_id[4] = {'\0'}; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* component ID */ + if (self->guid != NULL) { + if (!fwupd_guid_from_string(self->guid, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) + return NULL; + } + g_byte_array_append(buf, guid, sizeof(guid)); + + /* version value */ + fu_byte_array_append_uint64(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + + /* producer ID */ + if (self->producer_id != NULL) { + gsize producer_idsz = strlen(self->producer_id); + if (!fu_memcpy_safe(producer_id, + sizeof(producer_id), + 0x0, /* dst */ + (const guint8 *)self->producer_id, + producer_idsz, + 0x0, /* src */ + producer_idsz, + error)) + return NULL; + } + g_byte_array_append(buf, producer_id, sizeof(producer_id)); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_acpi_phat_version_element_set_guid(FuAcpiPhatVersionElement *self, const gchar *guid) +{ + g_free(self->guid); + self->guid = g_strdup(guid); +} + +static void +fu_acpi_phat_version_element_set_producer_id(FuAcpiPhatVersionElement *self, + const gchar *producer_id) +{ + g_free(self->producer_id); + self->producer_id = g_strdup(producer_id); +} + +static gboolean +fu_acpi_phat_version_element_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(firmware); + const gchar *tmp; + + /* optional properties */ + tmp = xb_node_query_text(n, "producer_id", NULL); + if (tmp != NULL) + fu_acpi_phat_version_element_set_producer_id(self, tmp); + tmp = xb_node_query_text(n, "guid", NULL); + if (tmp != NULL) + fu_acpi_phat_version_element_set_guid(self, tmp); + + /* success */ + return TRUE; +} + +static void +fu_acpi_phat_version_element_init(FuAcpiPhatVersionElement *self) +{ +} + +static void +fu_acpi_phat_version_element_finalize(GObject *object) +{ + FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(object); + g_free(self->guid); + g_free(self->producer_id); + G_OBJECT_CLASS(fu_acpi_phat_version_element_parent_class)->finalize(object); +} + +static void +fu_acpi_phat_version_element_class_init(FuAcpiPhatVersionElementClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_acpi_phat_version_element_finalize; + klass_firmware->parse = fu_acpi_phat_version_element_parse; + klass_firmware->write = fu_acpi_phat_version_element_write; + klass_firmware->export = fu_acpi_phat_version_element_export; + klass_firmware->build = fu_acpi_phat_version_element_build; +} + +FuFirmware * +fu_acpi_phat_version_element_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_PHAT_VERSION_ELEMENT, NULL)); +} diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-element.h b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-element.h new file mode 100644 index 0000000000000000000000000000000000000000..981452a200d8921de2cfaecb5dd25cf9f8082567 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-element.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_PHAT_VERSION_ELEMENT (fu_acpi_phat_version_element_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiPhatVersionElement, + fu_acpi_phat_version_element, + FU, + ACPI_PHAT_VERSION_ELEMENT, + FuFirmware) + +FuFirmware * +fu_acpi_phat_version_element_new(void); diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-record.c b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-record.c new file mode 100644 index 0000000000000000000000000000000000000000..10accf29c251d60644c6931e1036f72da67d3cde --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-record.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-phat-version-element.h" +#include "fu-acpi-phat-version-record.h" +#include "fu-acpi-phat.h" + +struct _FuAcpiPhatVersionRecord { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuAcpiPhatVersionRecord, fu_acpi_phat_version_record, FU_TYPE_FIRMWARE) + +static gboolean +fu_acpi_phat_version_record_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + guint32 record_count = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + if (!fu_memread_uint32_safe(buf, bufsz, offset + 8, &record_count, G_LITTLE_ENDIAN, error)) + return FALSE; + for (guint32 i = 0; i < record_count; i++) { + g_autoptr(FuFirmware) firmware_tmp = fu_acpi_phat_version_element_new(); + g_autoptr(GBytes) fw_tmp = NULL; + fw_tmp = fu_bytes_new_offset(fw, offset + 12, 28, error); + if (fw_tmp == NULL) + return FALSE; + fu_firmware_set_offset(firmware_tmp, offset + 12); + if (!fu_firmware_parse(firmware_tmp, + fw_tmp, + flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + error)) + return FALSE; + fu_firmware_add_image(firmware, firmware_tmp); + offset += fu_firmware_get_size(firmware_tmp); + } + return TRUE; +} + +static GBytes * +fu_acpi_phat_version_record_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GByteArray) buf2 = g_byte_array_new(); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* write each element so we get the image size */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf2, blob); + } + + /* data record */ + fu_byte_array_append_uint16(buf, FU_ACPI_PHAT_RECORD_TYPE_VERSION, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 12 + buf2->len, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, fu_firmware_get_version_raw(firmware)); + fu_byte_array_append_uint8(buf, 0x00); + fu_byte_array_append_uint8(buf, 0x00); + fu_byte_array_append_uint8(buf, 0x00); + fu_byte_array_append_uint32(buf, images->len, G_LITTLE_ENDIAN); + + /* element data */ + g_byte_array_append(buf, buf2->data, buf2->len); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_acpi_phat_version_record_init(FuAcpiPhatVersionRecord *self) +{ +} + +static void +fu_acpi_phat_version_record_class_init(FuAcpiPhatVersionRecordClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_acpi_phat_version_record_parse; + klass_firmware->write = fu_acpi_phat_version_record_write; +} + +FuFirmware * +fu_acpi_phat_version_record_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_PHAT_VERSION_RECORD, NULL)); +} diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-record.h b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-record.h new file mode 100644 index 0000000000000000000000000000000000000000..3ca9831ac294299259ed833cdd67151b1743526d --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat-version-record.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_PHAT_VERSION_RECORD (fu_acpi_phat_version_record_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiPhatVersionRecord, + fu_acpi_phat_version_record, + FU, + ACPI_PHAT_VERSION_RECORD, + FuFirmware) + +FuFirmware * +fu_acpi_phat_version_record_new(void); diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat.c b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat.c new file mode 100644 index 0000000000000000000000000000000000000000..07fad6ae7c7a6e88f2eb9b961248c933c087ac1f --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-acpi-phat-health-record.h" +#include "fu-acpi-phat-version-record.h" +#include "fu-acpi-phat.h" + +struct _FuAcpiPhat { + FuFirmware parent_instance; + gchar *oem_id; +}; + +G_DEFINE_TYPE(FuAcpiPhat, fu_acpi_phat, FU_TYPE_FIRMWARE) + +static void +fu_acpi_phat_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuAcpiPhat *self = FU_ACPI_PHAT(firmware); + if (self->oem_id != NULL) + fu_xmlb_builder_insert_kv(bn, "oem_id", self->oem_id); +} + +static gboolean +fu_acpi_phat_record_parse(FuFirmware *firmware, + GBytes *fw, + gsize *offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + guint16 record_length = 0; + guint16 record_type = 0; + guint8 revision; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) firmware_rcd = NULL; + + /* common header */ + if (!fu_memread_uint16_safe(buf, bufsz, *offset, &record_type, G_LITTLE_ENDIAN, error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + *offset + 2, + &record_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (record_length < 5) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "PHAT record length invalid, got 0x%x", + record_length); + return FALSE; + } + if (!fu_memread_uint8_safe(buf, bufsz, *offset + 4, &revision, error)) + return FALSE; + + /* firmware version data record */ + if (record_type == FU_ACPI_PHAT_RECORD_TYPE_VERSION) { + firmware_rcd = fu_acpi_phat_version_record_new(); + } else if (record_type == FU_ACPI_PHAT_RECORD_TYPE_HEALTH) { + firmware_rcd = fu_acpi_phat_health_record_new(); + } + + /* supported record type */ + if (firmware_rcd != NULL) { + g_autoptr(GBytes) fw_tmp = NULL; + fw_tmp = fu_bytes_new_offset(fw, *offset, record_length, error); + if (fw_tmp == NULL) + return FALSE; + fu_firmware_set_size(firmware_rcd, record_length); + fu_firmware_set_offset(firmware_rcd, *offset); + fu_firmware_set_version_raw(firmware_rcd, revision); + if (!fu_firmware_parse(firmware_rcd, fw_tmp, flags, error)) + return FALSE; + fu_firmware_add_image(firmware, firmware_rcd); + } + + *offset += record_length; + return TRUE; +} + +static void +fu_acpi_phat_set_oem_id(FuAcpiPhat *self, const gchar *oem_id) +{ + g_free(self->oem_id); + self->oem_id = g_strdup(oem_id); +} + +static gboolean +fu_acpi_phat_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + gchar magic[4] = {'\0'}; + + if (!fu_memcpy_safe((guint8 *)magic, + sizeof(magic), + 0x0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, /* src */ + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, "PHAT", sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic signature, expected PHAT"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_acpi_phat_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuAcpiPhat *self = FU_ACPI_PHAT(firmware); + gchar oem_id[6] = {'\0'}; + gchar oem_table_id[8] = {'\0'}; + gsize bufsz = 0; + guint32 length = 0; + guint32 oem_revision = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *oem_id_safe = NULL; + g_autofree gchar *oem_table_id_safe = NULL; + + /* parse table */ + if (!fu_memread_uint32_safe(buf, bufsz, offset + 4, &length, G_LITTLE_ENDIAN, error)) + return FALSE; + if (bufsz < length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "PHAT table invalid size, got 0x%x, expected 0x%x", + (guint)bufsz, + length); + return FALSE; + } + + /* spec revision */ + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + guint8 revision = 0; + if (!fu_memread_uint8_safe(buf, bufsz, offset + 8, &revision, error)) + return FALSE; + if (revision != FU_ACPI_PHAT_REVISION) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "PHAT table revision invalid, got 0x%x, expected 0x%x", + revision, + (guint)FU_ACPI_PHAT_REVISION); + return FALSE; + } + } + + /* verify checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 checksum = fu_sum8(buf, length); + if (checksum != 0x00) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "PHAT table checksum invalid, got 0x%x", + checksum); + return FALSE; + } + } + + /* OEMID */ + if (!fu_memcpy_safe((guint8 *)oem_id, + sizeof(oem_id), + 0x0, /* dst */ + buf, + bufsz, + offset + 10, /* src */ + sizeof(oem_id), + error)) + return FALSE; + oem_id_safe = fu_strsafe((const gchar *)oem_id, sizeof(oem_id)); + fu_acpi_phat_set_oem_id(self, oem_id_safe); + + /* OEM Table ID */ + if (!fu_memcpy_safe((guint8 *)oem_table_id, + sizeof(oem_table_id), + 0x0, /* dst */ + buf, + bufsz, + offset + 16, /* src */ + sizeof(oem_table_id), + error)) + return FALSE; + oem_table_id_safe = fu_strsafe((const gchar *)oem_table_id, sizeof(oem_table_id)); + fu_firmware_set_id(firmware, oem_table_id_safe); + if (!fu_memread_uint32_safe(buf, bufsz, offset + 24, &oem_revision, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_firmware_set_version_raw(firmware, oem_revision); + + /* platform telemetry records */ + for (gsize offset_tmp = offset + 36; offset_tmp < length;) { + if (!fu_acpi_phat_record_parse(firmware, fw, &offset_tmp, flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_acpi_phat_write(FuFirmware *firmware, GError **error) +{ + FuAcpiPhat *self = FU_ACPI_PHAT(firmware); + const gchar *oem_table_id_str = fu_firmware_get_id(firmware); + guint8 creator_id[] = {'F', 'W', 'U', 'P'}; + guint8 creator_rev[] = {'0', '0', '0', '0'}; + guint8 oem_id[6] = {'\0'}; + guint8 oem_table_id[8] = {'\0'}; + guint8 signature[] = {'P', 'H', 'A', 'T'}; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GByteArray) buf2 = g_byte_array_new(); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* write each image so we get the total size */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf2, blob); + } + + /* header */ + g_byte_array_append(buf, signature, sizeof(signature)); + fu_byte_array_append_uint32(buf, buf2->len + 36, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, fu_firmware_get_version_raw(firmware)); + fu_byte_array_append_uint8(buf, 0xFF); /* will fixup */ + if (self->oem_id != NULL) { + gsize oem_id_strlen = strlen(self->oem_id); + if (!fu_memcpy_safe(oem_id, + sizeof(oem_id), + 0x0, /* dst */ + (const guint8 *)self->oem_id, + oem_id_strlen, + 0x0, /* src */ + oem_id_strlen, + error)) + return NULL; + } + g_byte_array_append(buf, oem_id, sizeof(oem_id)); + if (oem_table_id_str != NULL) { + gsize oem_table_id_strlen = strlen(oem_table_id_str); + if (!fu_memcpy_safe(oem_table_id, + sizeof(oem_table_id), + 0x0, /* dst */ + (const guint8 *)oem_table_id_str, + oem_table_id_strlen, + 0x0, /* src */ + oem_table_id_strlen, + error)) + return NULL; + } + g_byte_array_append(buf, oem_table_id, sizeof(oem_table_id)); + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + g_byte_array_append(buf, creator_id, sizeof(creator_id)); + g_byte_array_append(buf, creator_rev, sizeof(creator_rev)); + g_byte_array_append(buf, buf2->data, buf2->len); + + /* fixup checksum */ + buf->data[9] = 0xFF - fu_sum8(buf->data, buf->len); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_acpi_phat_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuAcpiPhat *self = FU_ACPI_PHAT(firmware); + const gchar *tmp; + + /* optional properties */ + tmp = xb_node_query_text(n, "oem_id", NULL); + if (tmp != NULL) + fu_acpi_phat_set_oem_id(self, tmp); + + /* success */ + return TRUE; +} + +static gboolean +fu_acpi_phat_to_report_string_cb(XbBuilderNode *bn, gpointer user_data) +{ + if (g_strcmp0(xb_builder_node_get_element(bn), "offset") == 0 || + g_strcmp0(xb_builder_node_get_element(bn), "flags") == 0 || + g_strcmp0(xb_builder_node_get_element(bn), "size") == 0) + xb_builder_node_add_flag(bn, XB_BUILDER_NODE_FLAG_IGNORE); + return FALSE; +} + +gchar * +fu_acpi_phat_to_report_string(FuAcpiPhat *self) +{ + g_autoptr(XbBuilderNode) bn = xb_builder_node_new("firmware"); + fu_firmware_export(FU_FIRMWARE(self), FU_FIRMWARE_EXPORT_FLAG_NONE, bn); + xb_builder_node_traverse(bn, + G_PRE_ORDER, + G_TRAVERSE_ALL, + 3, + fu_acpi_phat_to_report_string_cb, + NULL); + return xb_builder_node_export(bn, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | + XB_NODE_EXPORT_FLAG_FORMAT_INDENT, + NULL); +} + +static void +fu_acpi_phat_init(FuAcpiPhat *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_acpi_phat_finalize(GObject *object) +{ + FuAcpiPhat *self = FU_ACPI_PHAT(object); + g_free(self->oem_id); + G_OBJECT_CLASS(fu_acpi_phat_parent_class)->finalize(object); +} + +static void +fu_acpi_phat_class_init(FuAcpiPhatClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_acpi_phat_finalize; + klass_firmware->check_magic = fu_acpi_phat_check_magic; + klass_firmware->parse = fu_acpi_phat_parse; + klass_firmware->write = fu_acpi_phat_write; + klass_firmware->export = fu_acpi_phat_export; + klass_firmware->build = fu_acpi_phat_build; +} + +FuFirmware * +fu_acpi_phat_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_PHAT, NULL)); +} diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat.h b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat.h new file mode 100644 index 0000000000000000000000000000000000000000..d085dcd860e8753d90d823161fbc05a492efa717 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-acpi-phat.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ACPI_PHAT (fu_acpi_phat_get_type()) +G_DECLARE_FINAL_TYPE(FuAcpiPhat, fu_acpi_phat, FU, ACPI_PHAT, FuFirmware) + +#define FU_ACPI_PHAT_RECORD_TYPE_VERSION 0x0000 +#define FU_ACPI_PHAT_RECORD_TYPE_HEALTH 0x0001 +#define FU_ACPI_PHAT_REVISION 0x01 + +FuFirmware * +fu_acpi_phat_new(void); +gchar * +fu_acpi_phat_to_report_string(FuAcpiPhat *self); diff --git a/fwupd-1.8.6/plugins/acpi-phat/fu-self-test.c b/fwupd-1.8.6/plugins/acpi-phat/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..4acb707228a713eaff48baa8b27a346a19e79f10 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/fu-self-test.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-acpi-phat.h" + +static void +fu_acpi_phat_parse_func(void) +{ + gboolean ret; + g_autoptr(FuFirmware) phat = fu_acpi_phat_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree gchar *str = NULL; + g_autofree gchar *fn = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "PHAT", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_test_skip("missing PHAT"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + ret = fu_firmware_parse(phat, + blob, + FWUPD_INSTALL_FLAG_FORCE | FWUPD_INSTALL_FLAG_NO_SEARCH, + &error); + g_assert_no_error(error); + g_assert_true(ret); + str = fu_acpi_phat_to_report_string(FU_ACPI_PHAT(phat)); + g_print("%s\n", str); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/acpi-phat/parse", fu_acpi_phat_parse_func); + + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/acpi-phat/meson.build b/fwupd-1.8.6/plugins/acpi-phat/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..94a68a0fb53d712ac84211bb7d85d17df6800d9e --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/meson.build @@ -0,0 +1,40 @@ +if get_option('plugin_acpi_phat').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiPhat"'] + +plugin_builtin_acpi_phat = static_library('fu_plugin_acpi_phat', + sources: [ + 'fu-acpi-phat-plugin.c', + 'fu-acpi-phat.c', # fuzzing + 'fu-acpi-phat-health-record.c', # fuzzing + 'fu-acpi-phat-version-element.c', # fuzzing + 'fu-acpi-phat-version-record.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_acpi_phat + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'acpi-phat-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_acpi_phat, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('acpi-phat-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/acpi-phat/tests/acpi-phat.bin b/fwupd-1.8.6/plugins/acpi-phat/tests/acpi-phat.bin new file mode 100644 index 0000000000000000000000000000000000000000..1aa26cc9b27ebb8f87b981aeae9803c18a865283 Binary files /dev/null and b/fwupd-1.8.6/plugins/acpi-phat/tests/acpi-phat.bin differ diff --git a/fwupd-1.8.6/plugins/acpi-phat/tests/acpi-phat.builder.xml b/fwupd-1.8.6/plugins/acpi-phat/tests/acpi-phat.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..945ccccfcc7b5651ce3071e18b5c303169b08030 --- /dev/null +++ b/fwupd-1.8.6/plugins/acpi-phat/tests/acpi-phat.builder.xml @@ -0,0 +1,35 @@ + + SUDODAVE + 0x1 + HUGHES + + 0x1 + + 0x123 + 40338ceb-b966-4eae-adae-9c32edfcc484 + HUGH + + + 0x124 + 2082b5e0-7a64-478a-b1b2-e3404fab6dad + LENO + + + + 0x1 + + 0x125 + dfbaaded-754b-5214-a5f2-46aa3331e8ce + DELL + + + + 0x1 + + + 0x1 + 0x1 + dfbaaded-754b-5214-a5f2-46aa3331e8ce + /dev/foo + + diff --git a/fwupd-1.8.6/plugins/amd-pmc/README.md b/fwupd-1.8.6/plugins/amd-pmc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7ed53d3cb8ce41e1a88fa1eb5dda72c66ee6fff8 --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/README.md @@ -0,0 +1,10 @@ +# AMD PMC + +## Introduction + +This plugin reports the firmware version of a microcontroller contained in AMD Zen CPU/APUs +called the System Management Unit on kernels that support exporting this information. + +## External Interface Access + +This plugin requires read only access to attributes located within `/sys/bus/platform/drivers/amd_pmc`. diff --git a/fwupd-1.8.6/plugins/amd-pmc/amd-pmc.quirk b/fwupd-1.8.6/plugins/amd-pmc/amd-pmc.quirk new file mode 100644 index 0000000000000000000000000000000000000000..98107e9d93cee78219c23c5b090a74de13ecfc12 --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/amd-pmc.quirk @@ -0,0 +1,2 @@ +[PLATFORM\DRIVER_amd_pmc] +Plugin = amd_pmc diff --git a/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-device.c b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..a34b67a0e0efb55c574a3c1c8307af345e851513 --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-device.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 Advanced Micro Devices Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-amd-pmc-device.h" +struct _FuAmdPmcDevice { + FuUdevDevice parent_instance; +}; + +G_DEFINE_TYPE(FuAmdPmcDevice, fu_amd_pmc_device, FU_TYPE_UDEV_DEVICE) + +static gboolean +fu_amd_pmc_device_probe(FuDevice *device, GError **error) +{ + guint64 program; + const gchar *version; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *summary = NULL; + + version = + fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), "smu_fw_version", &error_local); + if (version == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported kernel version"); + return FALSE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + if (!fu_udev_device_get_sysfs_attr_uint64(FU_UDEV_DEVICE(device), + "smu_program", + &program, + error)) + return FALSE; + + fu_device_set_version(device, version); + summary = g_strdup_printf("Microcontroller used within CPU/APU program %" G_GUINT64_FORMAT, + program); + fu_device_set_summary(device, summary); + fu_device_add_instance_id(device, fu_device_get_backend_id(device)); + + return TRUE; +} + +static void +fu_amd_pmc_device_init(FuAmdPmcDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "System Management Unit (SMU)"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_parent_guid(FU_DEVICE(self), "cpu"); + fu_device_set_vendor(FU_DEVICE(self), "AMD"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_physical_id(FU_DEVICE(self), "amd-pmc"); +} + +static void +fu_amd_pmc_device_class_init(FuAmdPmcDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_amd_pmc_device_probe; +} diff --git a/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-device.h b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..8cfd1ec7dbe27df78242daf7f71d47a04b9ad07e --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Advanced Micro Devices Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_AMD_PMC_DEVICE (fu_amd_pmc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuAmdPmcDevice, fu_amd_pmc_device, FU, AMD_PMC_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-plugin.c b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..506c5a5bcc5071d67fbea64b01e566344c83d774 --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Advanced Micro Devices Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-amd-pmc-device.h" +#include "fu-amd-pmc-plugin.h" + +struct _FuAmdPmcPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAmdPmcPlugin, fu_amd_pmc_plugin, FU_TYPE_PLUGIN) + +static void +fu_amd_pmc_plugin_init(FuAmdPmcPlugin *self) +{ +} + +static void +fu_amd_pmc_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "platform"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_AMD_PMC_DEVICE); +} + +static void +fu_amd_pmc_plugin_class_init(FuAmdPmcPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_amd_pmc_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-plugin.h b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..0ffd345d26a081c29ac26316efda678c42ca3cf3 --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/fu-amd-pmc-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAmdPmcPlugin, fu_amd_pmc_plugin, FU, AMD_PMC_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/amd-pmc/meson.build b/fwupd-1.8.6/plugins/amd-pmc/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..2a6a30b913aa82a68510e700ff958760f3e936c2 --- /dev/null +++ b/fwupd-1.8.6/plugins/amd-pmc/meson.build @@ -0,0 +1,15 @@ +if gudev.found() and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginAmdPmc"'] + +plugin_quirks += files('amd-pmc.quirk') +plugin_builtins += static_library('fu_plugin_amd_pmc', + sources: [ + 'fu-amd-pmc-plugin.c', + 'fu-amd-pmc-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/amt/README.md b/fwupd-1.8.6/plugins/amt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ec73789ea5721a7e62928a247b65f281ea121f3c --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/README.md @@ -0,0 +1,27 @@ +# Intel Management Engine + +## Introduction + +This plugin is used to get the version number on the Intel Management Engine. + +If AMT is enabled and provisioned and the AMT version is between 6.0 and 11.2, +and you have not upgraded your firmware, you are vulnerable to CVE-2017-5689 and +you should disable AMT in your system firmware. + +This code is inspired by 'AMT status checker for Linux' by Matthew Garrett +which can be found here: + +That tool in turn is heavily based on mei-amt-version from samples/mei in the +Linux source tree and copyright Intel Corporation. + +## GUID Generation + +These devices use the existing GUID provided by the AMT host interface. + +## Vendor ID Security + +The device is not upgradable and thus requires no vendor ID set. + +## External Interface Access + +This plugin requires read only access to `/dev/mei0`. diff --git a/fwupd-1.8.6/plugins/amt/amt.quirk b/fwupd-1.8.6/plugins/amt/amt.quirk new file mode 100644 index 0000000000000000000000000000000000000000..8f935d8e60ce3e0dd28d20d069adbf6f2071c52d --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/amt.quirk @@ -0,0 +1,2 @@ +[MEI] +Plugin = amt diff --git a/fwupd-1.8.6/plugins/amt/fu-amt-device.c b/fwupd-1.8.6/plugins/amt/fu-amt-device.c new file mode 100644 index 0000000000000000000000000000000000000000..1b2df815e8ea6066f9823ad39b87d6daf9a82070 --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/fu-amt-device.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2012 Intel Corporation. + * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-amt-device.h" + +struct _FuAmtDevice { + FuMeiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuAmtDevice, fu_amt_device, FU_TYPE_MEI_DEVICE) + +#define FU_AMT_DEVICE_MEI_IAMTHIF "2800f812-b7b4-2d4b-aca8-46e0ff65814c" + +#define AMT_MAJOR_VERSION 1 +#define AMT_MINOR_VERSION 1 + +#define AMT_STATUS_SUCCESS 0x0 +#define AMT_STATUS_INTERNAL_ERROR 0x1 +#define AMT_STATUS_NOT_READY 0x2 +#define AMT_STATUS_INVALID_AMT_MODE 0x3 +#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4 + +#define AMT_STATUS_HOST_IF_EMPTY_RESPONSE 0x4000 +#define AMT_STATUS_SDK_RESOURCES 0x1004 + +#define AMT_BIOS_VERSION_LEN 65 +#define AMT_VERSIONS_NUMBER 50 +#define AMT_UNICODE_STRING_LEN 20 + +struct amt_unicode_string { + guint16 length; + char string[AMT_UNICODE_STRING_LEN]; +} __attribute__((packed)); + +struct amt_version_type { + struct amt_unicode_string description; + struct amt_unicode_string version; +} __attribute__((packed)); + +struct amt_version { + guint8 major; + guint8 minor; +} __attribute__((packed)); + +struct amt_code_versions { + guint8 bios[AMT_BIOS_VERSION_LEN]; + guint32 count; + struct amt_version_type versions[AMT_VERSIONS_NUMBER]; +} __attribute__((packed)); + +struct amt_provisioning_state { + guint8 bios[AMT_BIOS_VERSION_LEN]; + guint32 count; + guint8 state; +} __attribute__((packed)); + +/*************************************************************************** + * Intel Advanced Management Technology Host Interface + ***************************************************************************/ + +struct amt_host_if_msg_header { + struct amt_version version; + guint16 _reserved; + guint32 command; + guint32 length; +} __attribute__((packed)); + +struct amt_host_if_resp_header { + struct amt_host_if_msg_header header; + guint32 status; + guchar data[0]; +} __attribute__((packed)); + +#define AMT_HOST_IF_CODE_VERSIONS_REQUEST 0x0400001A +#define AMT_HOST_IF_CODE_VERSIONS_RESPONSE 0x0480001A + +const struct amt_host_if_msg_header CODE_VERSION_REQ = { + .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, + ._reserved = 0, + .command = AMT_HOST_IF_CODE_VERSIONS_REQUEST, + .length = 0}; + +#define AMT_HOST_IF_PROVISIONING_MODE_REQUEST 0x04000008 +#define AMT_HOST_IF_PROVISIONING_MODE_RESPONSE 0x04800008 + +const struct amt_host_if_msg_header PROVISIONING_MODE_REQUEST = { + .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, + ._reserved = 0, + .command = AMT_HOST_IF_PROVISIONING_MODE_REQUEST, + .length = 0}; + +#define AMT_HOST_IF_PROVISIONING_STATE_REQUEST 0x04000011 +#define AMT_HOST_IF_PROVISIONING_STATE_RESPONSE 0x04800011 + +const struct amt_host_if_msg_header PROVISIONING_STATE_REQUEST = { + .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, + ._reserved = 0, + .command = AMT_HOST_IF_PROVISIONING_STATE_REQUEST, + .length = 0}; + +struct amt_host_if { + FuAmtDevice self; +}; + +static gboolean +fu_amt_device_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)) { + 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) { + 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)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "string was invalid size"); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_amt_device_status_set_error(guint32 status, GError **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; + } + 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 gboolean +fu_amt_device_host_if_call(FuAmtDevice *self, + const guchar *command, + gssize command_sz, + guint8 **read_buf, + guint32 rcmd, + guint expected_sz, + unsigned long send_timeout, + GError **error) +{ + gsize in_buf_sz = fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)); + gsize out_buf_sz; + struct amt_host_if_resp_header *msg_hdr; + + *read_buf = (guint8 *)g_malloc0(in_buf_sz); + msg_hdr = (struct amt_host_if_resp_header *)*read_buf; + + if (!fu_mei_device_write(FU_MEI_DEVICE(self), command, command_sz, send_timeout, error)) + return FALSE; + if (!fu_mei_device_read(FU_MEI_DEVICE(self), + *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 %u", + (guint)expected_sz, + (guint)out_buf_sz); + return FALSE; + } + if (!fu_amt_device_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 gboolean +fu_amt_device_get_provisioning_state(FuAmtDevice *self, guint8 *state, GError **error) +{ + g_autofree struct amt_host_if_resp_header *response = NULL; + if (!fu_amt_device_host_if_call(self, + (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; +} + +static gboolean +fu_amt_device_open(FuDevice *device, GError **error) +{ + /* open then create context */ + if (!FU_DEVICE_CLASS(fu_amt_device_parent_class)->open(device, error)) + return FALSE; + return fu_mei_device_connect(FU_MEI_DEVICE(device), FU_AMT_DEVICE_MEI_IAMTHIF, 0, error); +} + +static gboolean +fu_amt_device_setup(FuDevice *device, GError **error) +{ + FuAmtDevice *self = FU_AMT_DEVICE(device); + guint8 state; + struct amt_code_versions ver; + g_autofree struct amt_host_if_resp_header *response = NULL; + g_autoptr(GString) version_bl = g_string_new(NULL); + g_autoptr(GString) version_fw = g_string_new(NULL); + + /* check version */ + if (!fu_amt_device_host_if_call(self, + (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 FALSE; + } + if (!fu_amt_device_verify_code_versions(response, error)) { + g_prefix_error(error, "failed to verify code versions: "); + return FALSE; + } + memcpy(&ver, response->data, sizeof(struct amt_code_versions)); + + if (!fu_amt_device_get_provisioning_state(self, &state, error)) + return FALSE; + switch (state) { + case 0: + fu_device_set_name(device, "AMT [unprovisioned]"); + break; + case 1: + fu_device_set_name(device, "AMT [being provisioned]"); + break; + case 2: + fu_device_set_name(device, "AMT [provisioned]"); + break; + default: + fu_device_set_name(device, "AMT [unknown]"); + break; + } + + /* add guid */ + fu_device_add_guid(device, FU_AMT_DEVICE_MEI_IAMTHIF); + fu_device_add_parent_guid(device, "main-system-firmware"); + + /* get version numbers */ + for (guint i = 0; i < ver.count; i++) { + if (g_strcmp0(ver.versions[i].description.string, "AMT") == 0) { + 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 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(device, version_fw->str); + if (version_bl->len > 0) + fu_device_set_version_bootloader(device, version_bl->str); + + /* success */ + return TRUE; +} + +static void +fu_amt_device_init(FuAmtDevice *self) +{ + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_INTEL_ME); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_set_summary(FU_DEVICE(self), + "Hardware and firmware technology for remote " + "out-of-band management"); +} + +static void +fu_amt_device_class_init(FuAmtDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->open = fu_amt_device_open; + klass_device->setup = fu_amt_device_setup; +} diff --git a/fwupd-1.8.6/plugins/amt/fu-amt-device.h b/fwupd-1.8.6/plugins/amt/fu-amt-device.h new file mode 100644 index 0000000000000000000000000000000000000000..d6948256c694df450d737a2e952f1353f479016a --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/fu-amt-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_AMT_DEVICE (fu_amt_device_get_type()) +G_DECLARE_FINAL_TYPE(FuAmtDevice, fu_amt_device, FU, AMT_DEVICE, FuMeiDevice) diff --git a/fwupd-1.8.6/plugins/amt/fu-amt-plugin.c b/fwupd-1.8.6/plugins/amt/fu-amt-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..e9de8ef67a3a75cbb72ad726ebd65a2513e824b1 --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/fu-amt-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-amt-device.h" +#include "fu-amt-plugin.h" + +struct _FuAmtPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAmtPlugin, fu_amt_plugin, FU_TYPE_PLUGIN) + +static void +fu_amt_plugin_init(FuAmtPlugin *self) +{ +} + +static void +fu_amt_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "mei"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_AMT_DEVICE); +} + +static void +fu_amt_plugin_class_init(FuAmtPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_amt_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/amt/fu-amt-plugin.h b/fwupd-1.8.6/plugins/amt/fu-amt-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..cb82b077b6d74a03cc408c62fc5c2a8cacadba42 --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/fu-amt-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAmtPlugin, fu_amt_plugin, FU, AMT_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/amt/meson.build b/fwupd-1.8.6/plugins/amt/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c5b36fbcb6826ac739f2752a9ac0fc0dc625f7c9 --- /dev/null +++ b/fwupd-1.8.6/plugins/amt/meson.build @@ -0,0 +1,15 @@ +if get_option('plugin_amt').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginAmt"'] + +plugin_quirks += files('amt.quirk') +plugin_builtins += static_library('fu_plugin_amt', + sources: [ + 'fu-amt-plugin.c', + 'fu-amt-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/analogix/README.md b/fwupd-1.8.6/plugins/analogix/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9b5cff9886df9f92b5095eba947c31f4e038cc12 --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/README.md @@ -0,0 +1,47 @@ +# Analogix + +## Introduction + +This plugin can flash the firmware of some Analogix billboard devices. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a Intel Hex file format. The resulting binary image is either: + +* `OCM` section only +* `CUSTOM` section only +* Multiple sections excluded `CUSTOM` -- at fixed offsets and sizes + +This plugin supports the following protocol ID: + +* com.analogix.bb + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1F29&PID_7518` +* `USB\VID_050D&PID_008B` +* `USB\VID_047D&PID_80C8` +* `USB\VID_0502&PID_04C4` +* `USB\VID_14B0&PID_01D0` +* `USB\VID_14B0&PID_01D1` + +## Update Behavior + +The device is updated at runtime using USB control transfers. + +## Vendor ID Security + +The vendor ID is set from the USB vendor. The list of USB VIDs used is: + +* `USB:0x1F29` +* `USB:0x050D` +* `USB:0x047D` +* `USB:0x0502` +* `USB:0x14B0` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/analogix/analogix.quirk b/fwupd-1.8.6/plugins/analogix/analogix.quirk new file mode 100644 index 0000000000000000000000000000000000000000..dbf2114ac226886243f777df03919b63940dea1e --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/analogix.quirk @@ -0,0 +1,22 @@ +# Phoenix-Lite +[USB\VID_1F29&PID_7518] +Plugin = analogix + +# Belkin +[USB\VID_050D&PID_008B] +Plugin = analogix + +# Kensington +[USB\VID_047D&PID_80C8] +Plugin = analogix + +# ACER +[USB\VID_0502&PID_04C4] +Plugin = analogix + +# Startech +[USB\VID_14B0&PID_01D0] +Plugin = analogix + +[USB\VID_14B0&PID_01D1] +Plugin = analogix diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-common.c b/fwupd-1.8.6/plugins/analogix/fu-analogix-common.c new file mode 100644 index 0000000000000000000000000000000000000000..a7feb125668d415e20f6c0494440e6be02a52f02 --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-common.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-analogix-common.h" + +const gchar * +fu_analogix_update_status_to_string(AnxUpdateStatus status) +{ + if (status == UPDATE_STATUS_INVALID) + return "invalid"; + if (status == UPDATE_STATUS_START) + return "start"; + if (status == UPDATE_STATUS_FINISH) + return "finish"; + if (status == UPDATE_STATUS_ERROR) + return "error"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-common.h b/fwupd-1.8.6/plugins/analogix/fu-analogix-common.h new file mode 100644 index 0000000000000000000000000000000000000000..2f3985b00e10ceffd4c347d923b9706a8b275835 --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-common.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define ANX_BB_TRANSACTION_TIMEOUT 5000 /* ms */ +#define BILLBOARD_CLASS 0x11 +#define BILLBOARD_SUBCLASS 0x00 +#define BILLBOARD_PROTOCOL 0x00 +#define BILLBOARD_MAX_PACKET_SIZE 64 +#define OCM_FLASH_SIZE 0x18000 +#define SECURE_OCM_TX_SIZE 0x3000 +#define SECURE_OCM_RX_SIZE 0x3000 +#define CUSTOM_FLASH_SIZE 0x1000 + +#define FLASH_OCM_ADDR 0x1000 +#define FLASH_TXFW_ADDR 0x31000 +#define FLASH_RXFW_ADDR 0x34000 +#define FLASH_CUSTOM_ADDR 0x38000 +#define OCM_FW_VERSION_ADDR 0x14FF0 + +/* bRequest for Phoenix-Lite Billboard */ +typedef enum { + ANX_BB_RQT_SEND_UPDATE_DATA = 0x01, + ANX_BB_RQT_READ_UPDATE_DATA = 0x02, + ANX_BB_RQT_GET_UPDATE_STATUS = 0x10, + ANX_BB_RQT_READ_FW_VER = 0x12, + ANX_BB_RQT_READ_CUS_VER = 0x13, + ANX_BB_RQT_READ_FW_RVER = 0x19, + ANX_BB_RQT_READ_CUS_RVER = 0x1c, +} AnxBbRqtCode; + +/* wValue low byte */ +typedef enum { + ANX_BB_WVAL_UPDATE_OCM = 0x06, + ANX_BB_WVAL_UPDATE_CUSTOM_DEF = 0x07, + ANX_BB_WVAL_UPDATE_SECURE_TX = 0x08, + ANX_BB_WVAL_UPDATE_SECURE_RX = 0x09, +} AnxwValCode; + +typedef enum { + UPDATE_STATUS_INVALID, + UPDATE_STATUS_START, + UPDATE_STATUS_FINISH, + UPDATE_STATUS_ERROR = 0xFF, +} AnxUpdateStatus; + +const gchar * +fu_analogix_update_status_to_string(AnxUpdateStatus status); diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-device.c b/fwupd-1.8.6/plugins/analogix/fu-analogix-device.c new file mode 100644 index 0000000000000000000000000000000000000000..c620b9055343d845a6a362e0e99c184a530a3424 --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-device.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#include "config.h" + +#include + +#include "fu-analogix-common.h" +#include "fu-analogix-device.h" +#include "fu-analogix-firmware.h" + +struct _FuAnalogixDevice { + FuUsbDevice parent_instance; + guint16 ocm_version; + guint16 custom_version; +}; + +G_DEFINE_TYPE(FuAnalogixDevice, fu_analogix_device, FU_TYPE_USB_DEVICE) + +static void +fu_analogix_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuAnalogixDevice *self = FU_ANALOGIX_DEVICE(device); + fu_string_append_kx(str, idt, "OcmVersion", self->ocm_version); + fu_string_append_kx(str, idt, "CustomVersion", self->custom_version); +} + +static gboolean +fu_analogix_device_send(FuAnalogixDevice *self, + AnxBbRqtCode reqcode, + guint16 val0code, + guint16 index, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + gsize actual_len = 0; + g_autofree guint8 *buf_tmp = NULL; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(bufsz <= 64, FALSE); + + /* make mutable */ + buf_tmp = fu_memdup_safe(buf, bufsz, error); + if (buf_tmp == NULL) + return FALSE; + + /* send data to device */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + reqcode, /* request */ + val0code, /* value */ + index, /* index */ + buf_tmp, /* data */ + bufsz, /* length */ + &actual_len, /* actual length */ + (guint)ANX_BB_TRANSACTION_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send data error: "); + return FALSE; + } + if (actual_len != bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "send data length is incorrect"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_analogix_device_receive(FuAnalogixDevice *self, + AnxBbRqtCode reqcode, + guint16 val0code, + guint16 index, + guint8 *buf, + gsize bufsz, + GError **error) +{ + gsize actual_len = 0; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(bufsz <= 64, FALSE); + + /* get data from device */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + reqcode, /* request */ + val0code, /* value */ + index, + buf, /* data */ + bufsz, /* length */ + &actual_len, /* actual length */ + (guint)ANX_BB_TRANSACTION_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "receive data error: "); + return FALSE; + } + if (actual_len != bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "receive data length is incorrect"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_analogix_device_get_update_status(FuAnalogixDevice *self, + AnxUpdateStatus *status, + GError **error) +{ + for (guint i = 0; i < 3000; i++) { + guint8 status_tmp = UPDATE_STATUS_INVALID; + if (!fu_analogix_device_receive(self, + ANX_BB_RQT_GET_UPDATE_STATUS, + 0, + 0, + &status_tmp, + sizeof(status_tmp), + error)) + return FALSE; + if ((status_tmp != UPDATE_STATUS_ERROR) && (status_tmp != UPDATE_STATUS_INVALID)) { + if (status != NULL) + *status = status_tmp; + return TRUE; + } + /* wait 1ms */ + g_usleep(1000); + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "timed out: status was invalid"); + return FALSE; +} + +static gboolean +fu_analogix_device_setup(FuDevice *device, GError **error) +{ + FuAnalogixDevice *self = FU_ANALOGIX_DEVICE(device); + guint8 buf_fw[2] = {0x0}; + guint8 buf_custom[2] = {0x0}; + g_autofree gchar *version = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_analogix_device_parent_class)->setup(device, error)) + return FALSE; + + /* get OCM version */ + if (!fu_analogix_device_receive(self, ANX_BB_RQT_READ_FW_VER, 0, 0, &buf_fw[1], 1, error)) + return FALSE; + if (!fu_analogix_device_receive(self, ANX_BB_RQT_READ_FW_RVER, 0, 0, &buf_fw[0], 1, error)) + return FALSE; + self->ocm_version = fu_memread_uint16(buf_fw, G_LITTLE_ENDIAN); + + /* get custom version */ + if (!fu_analogix_device_receive(self, + ANX_BB_RQT_READ_CUS_VER, + 0, + 0, + &buf_custom[1], + 1, + error)) + return FALSE; + if (!fu_analogix_device_receive(self, + ANX_BB_RQT_READ_CUS_RVER, + 0, + 0, + &buf_custom[0], + 1, + error)) + return FALSE; + self->custom_version = fu_memread_uint16(buf_custom, G_LITTLE_ENDIAN); + + /* device version is both versions as a pair */ + version = g_strdup_printf("%04x.%04x", self->custom_version, self->ocm_version); + fu_device_set_version(FU_DEVICE(device), version); + return TRUE; +} + +static gboolean +fu_analogix_device_find_interface(FuUsbDevice *device, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + GUsbDevice *usb_device = fu_usb_device_get_dev(device); + FuAnalogixDevice *self = FU_ANALOGIX_DEVICE(device); + g_autoptr(GPtrArray) intfs = NULL; + + intfs = g_usb_device_get_interfaces(usb_device, error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == BILLBOARD_CLASS && + g_usb_interface_get_subclass(intf) == BILLBOARD_SUBCLASS && + g_usb_interface_get_protocol(intf) == BILLBOARD_PROTOCOL) { + g_autoptr(GPtrArray) endpoints = NULL; + + endpoints = g_usb_interface_get_endpoints(intf); + if (endpoints == NULL) + continue; + fu_usb_device_add_interface(FU_USB_DEVICE(self), + g_usb_interface_get_number(intf)); + return TRUE; + } + } + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no update interface found"); + return FALSE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} + +static gboolean +fu_analogix_device_probe(FuDevice *device, GError **error) +{ + if (!fu_analogix_device_find_interface(FU_USB_DEVICE(device), error)) { + g_prefix_error(error, "failed to find update interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_analogix_device_write_image(FuAnalogixDevice *self, + FuFirmware *image, + guint16 req_val, + FuProgress *progress, + GError **error) +{ + AnxUpdateStatus status = UPDATE_STATUS_INVALID; + guint8 buf_init[4] = {0x0}; + g_autoptr(GBytes) block_bytes = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "initialization"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + + /* offset into firmware */ + block_bytes = fu_firmware_get_bytes(image, error); + if (block_bytes == NULL) + return FALSE; + + /* initialization */ + fu_memwrite_uint32(buf_init, g_bytes_get_size(block_bytes), G_LITTLE_ENDIAN); + if (!fu_analogix_device_send(self, + ANX_BB_RQT_SEND_UPDATE_DATA, + req_val, + 0, + buf_init, + 3, + error)) { + g_prefix_error(error, "program initialized failed: "); + return FALSE; + } + if (!fu_analogix_device_get_update_status(self, &status, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write data */ + chunks = fu_chunk_array_new_from_bytes(block_bytes, 0x00, 0x00, BILLBOARD_MAX_PACKET_SIZE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_analogix_device_send(self, + ANX_BB_RQT_SEND_UPDATE_DATA, + req_val, + i + 1, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "failed send on chk %u: ", i); + return FALSE; + } + if (!fu_analogix_device_get_update_status(self, &status, error)) { + g_prefix_error(error, "failed status on chk %u: ", i); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + chunks->len); + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_analogix_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuAnalogixDevice *self = FU_ANALOGIX_DEVICE(device); + g_autoptr(FuFirmware) fw_cus = NULL; + g_autoptr(FuFirmware) fw_ocm = NULL; + g_autoptr(FuFirmware) fw_srx = NULL; + g_autoptr(FuFirmware) fw_stx = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 25, "cus"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 25, "stx"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 25, "srx"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 25, "ocm"); + + /* CUSTOM_DEF */ + fw_cus = fu_firmware_get_image_by_id(firmware, "custom", NULL); + if (fw_cus != NULL) { + if (!fu_analogix_device_write_image(self, + fw_cus, + ANX_BB_WVAL_UPDATE_CUSTOM_DEF, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "program custom define failed: "); + return FALSE; + } + } + fu_progress_step_done(progress); + + /* SECURE_TX */ + fw_stx = fu_firmware_get_image_by_id(firmware, "stx", NULL); + if (fw_stx != NULL) { + if (!fu_analogix_device_write_image(self, + fw_stx, + ANX_BB_WVAL_UPDATE_SECURE_TX, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "program secure TX failed: "); + return FALSE; + } + } + fu_progress_step_done(progress); + + /* SECURE_RX */ + fw_srx = fu_firmware_get_image_by_id(firmware, "srx", NULL); + if (fw_srx != NULL) { + if (!fu_analogix_device_write_image(self, + fw_srx, + ANX_BB_WVAL_UPDATE_SECURE_RX, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "program secure RX failed: "); + return FALSE; + } + } + fu_progress_step_done(progress); + + /* OCM */ + fw_ocm = fu_firmware_get_image_by_id(firmware, "ocm", NULL); + if (fw_ocm != NULL) { + if (!fu_analogix_device_write_image(self, + fw_ocm, + ANX_BB_WVAL_UPDATE_OCM, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "program OCM failed: "); + return FALSE; + } + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_analogix_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_analogix_device_init(FuAnalogixDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.analogix.bb"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ANALOGIX_FIRMWARE); +} + +static void +fu_analogix_device_class_init(FuAnalogixDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_analogix_device_to_string; + klass_device->write_firmware = fu_analogix_device_write_firmware; + klass_device->setup = fu_analogix_device_setup; + klass_device->probe = fu_analogix_device_probe; + klass_device->set_progress = fu_analogix_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-device.h b/fwupd-1.8.6/plugins/analogix/fu-analogix-device.h new file mode 100644 index 0000000000000000000000000000000000000000..58486249745e44d200f909305ba6dcd66e102f3b --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-device.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#pragma once + +#include + +#define FU_TYPE_ANALOGIX_DEVICE (fu_analogix_device_get_type()) +G_DECLARE_FINAL_TYPE(FuAnalogixDevice, fu_analogix_device, FU, ANALOGIX_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-firmware.c b/fwupd-1.8.6/plugins/analogix/fu-analogix-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..5a8fd2d17b87ab727a5ef5020ffb9bbef6eb4ccf --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-firmware.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-analogix-common.h" +#include "fu-analogix-firmware.h" + +struct _FuAnalogixFirmware { + FuIhexFirmwareClass parent_instance; +}; + +G_DEFINE_TYPE(FuAnalogixFirmware, fu_analogix_firmware, FU_TYPE_IHEX_FIRMWARE) + +static gboolean +fu_analogix_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_CLASS(fu_analogix_firmware_parent_class); + const guint8 *buf = NULL; + gsize bufsz = 0; + guint16 ocm_version; + guint8 version_hi = 0; + guint8 version_lo = 0; + g_autofree gchar *version = NULL; + g_autoptr(FuFirmware) fw_ocm = NULL; + g_autoptr(GBytes) blob_cus = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob_ocm = NULL; + g_autoptr(GBytes) blob_srx = NULL; + g_autoptr(GBytes) blob_stx = NULL; + + /* convert to binary with FuIhexFirmware->parse */ + if (!klass->parse(firmware, fw, offset, flags, error)) + return FALSE; + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return FALSE; + + /* OCM section only, CUSTOM section only, or multiple sections excluded CUSTOM */ + if (g_bytes_get_size(blob) == OCM_FLASH_SIZE) { + blob_ocm = g_bytes_ref(blob); + } else if (g_bytes_get_size(blob) == CUSTOM_FLASH_SIZE) { + /* custom */ + blob_cus = fu_bytes_new_offset(blob, 0, CUSTOM_FLASH_SIZE, error); + } else { + blob_ocm = fu_bytes_new_offset(blob, 0, OCM_FLASH_SIZE, error); + if (blob_ocm == NULL) + return FALSE; + } + if (blob_ocm != NULL) { + fw_ocm = fu_firmware_new_from_bytes(blob_ocm); + fu_firmware_set_id(fw_ocm, "ocm"); + fu_firmware_set_addr(fw_ocm, FLASH_OCM_ADDR); + fu_firmware_add_image(firmware, fw_ocm); + + /* get OCM version */ + buf = g_bytes_get_data(blob_ocm, &bufsz); + if (!fu_memread_uint8_safe(buf, + bufsz, + OCM_FW_VERSION_ADDR - FLASH_OCM_ADDR + 8, + &version_hi, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + OCM_FW_VERSION_ADDR - FLASH_OCM_ADDR + 12, + &version_lo, + error)) + return FALSE; + ocm_version = ((guint16)version_hi) << 8 | version_lo; + fu_firmware_set_version_raw(fw_ocm, ocm_version); + version = g_strdup_printf("%02x.%02x", version_hi, version_lo); + fu_firmware_set_version(fw_ocm, version); + } + + /* TXFW is optional */ + blob_stx = + fu_bytes_new_offset(blob, FLASH_TXFW_ADDR - FLASH_OCM_ADDR, SECURE_OCM_TX_SIZE, NULL); + if (blob_stx != NULL && !fu_bytes_is_empty(blob_stx)) { + g_autoptr(FuFirmware) fw2 = fu_firmware_new_from_bytes(blob_stx); + fu_firmware_set_id(fw2, "stx"); + fu_firmware_set_addr(fw2, FLASH_TXFW_ADDR); + fu_firmware_add_image(firmware, fw2); + } + + /* RXFW is optional */ + blob_srx = + fu_bytes_new_offset(blob, FLASH_RXFW_ADDR - FLASH_OCM_ADDR, SECURE_OCM_RX_SIZE, NULL); + if (blob_srx != NULL && !fu_bytes_is_empty(blob_srx)) { + g_autoptr(FuFirmware) fw2 = fu_firmware_new_from_bytes(blob_srx); + fu_firmware_set_id(fw2, "srx"); + fu_firmware_set_addr(fw2, FLASH_RXFW_ADDR); + fu_firmware_add_image(firmware, fw2); + } + if (blob_cus != NULL && !fu_bytes_is_empty(blob_cus)) { + g_autoptr(FuFirmware) fw2 = fu_firmware_new_from_bytes(blob_cus); + fu_firmware_set_id(fw2, "custom"); + fu_firmware_set_addr(fw2, FLASH_CUSTOM_ADDR); + fu_firmware_add_image(firmware, fw2); + } + + /* success */ + return TRUE; +} + +static void +fu_analogix_firmware_init(FuAnalogixFirmware *self) +{ + fu_ihex_firmware_set_padding_value(FU_IHEX_FIRMWARE(self), 0xFF); +} + +static void +fu_analogix_firmware_class_init(FuAnalogixFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_analogix_firmware_parse; +} + +FuFirmware * +fu_analogix_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ANALOGIX_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-firmware.h b/fwupd-1.8.6/plugins/analogix/fu-analogix-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..241aa114b2da6c48d33b1c446f9568b4713f5107 --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-firmware.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#pragma once + +#include + +#define FU_TYPE_ANALOGIX_FIRMWARE (fu_analogix_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuAnalogixFirmware, + fu_analogix_firmware, + FU, + ANALOGIX_FIRMWARE, + FuIhexFirmware) + +FuFirmware * +fu_analogix_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-plugin.c b/fwupd-1.8.6/plugins/analogix/fu-analogix-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..aa8ad797afaa179bfdae3cdce7c9e989dd1db9fe --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Xiaotian Cui + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-analogix-device.h" +#include "fu-analogix-firmware.h" +#include "fu-analogix-plugin.h" + +struct _FuAnalogixPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAnalogixPlugin, fu_analogix_plugin, FU_TYPE_PLUGIN) + +static void +fu_analogix_plugin_init(FuAnalogixPlugin *self) +{ +} + +static void +fu_analogix_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ANALOGIX_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ANALOGIX_FIRMWARE); +} + +static void +fu_analogix_plugin_class_init(FuAnalogixPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_analogix_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/analogix/fu-analogix-plugin.h b/fwupd-1.8.6/plugins/analogix/fu-analogix-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..0c1b9837c9dff0128c52ddefddab53caed7c4bc0 --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/fu-analogix-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAnalogixPlugin, fu_analogix_plugin, FU, ANALOGIX_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/analogix/meson.build b/fwupd-1.8.6/plugins/analogix/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..279c0a73bd24249654e5b3a5560baa44fda90b6a --- /dev/null +++ b/fwupd-1.8.6/plugins/analogix/meson.build @@ -0,0 +1,17 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginAnalogix"'] + +plugin_quirks += files('analogix.quirk') +plugin_builtins += static_library('fu_plugin_analogix', + sources: [ + 'fu-analogix-plugin.c', + 'fu-analogix-device.c', + 'fu-analogix-common.c', + 'fu-analogix-firmware.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/android-boot/README.md b/fwupd-1.8.6/plugins/android-boot/README.md new file mode 100644 index 0000000000000000000000000000000000000000..12c8262d24dde2d7ebcc106a30750abf6643ac86 --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/README.md @@ -0,0 +1,53 @@ +# Android Bootloaders + +## Introduction + +This plugin is used to update hardware that use partitions to store their firmware on. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob as a Raw Disk File +in the IMG format. The firmware blob will be flashed to the partition. Fastboot devices are similar +but are flashed in fastboot mode using an external device. This plugin is similar but can be used +to flash from the device itself rather than external device. + +This plugin supports the following protocol ID: + +* com.google.android_boot + +## GUID Generation + +The GUID is generated by combining the partition UUID of the block device, its label and optionally boot slot +when using an Android A/B partitioning scheme, e.g. + +* `DRIVE\UUID_c49183ed-aaec-9bf5-760a-66330fbcffc1&LABEL_label&SLOT_a` +* `DRIVE\UUID_c49183ed-aaec-9bf5-760a-66330fbcffc1&LABEL_label` +* `DRIVE\UUID_c49183ed-aaec-9bf5-760a-66330fbcffc1` + +## Update Behavior + +The block device is erased in chunks, written and then read back to verify. + +## Quirk Use + +This plugin uses the following plugin-specific quirk: + +### AndroidBootVersionProperty + +Property to parse from `/proc/cmdline` to retrieve the bootloader version. + +Since: 1.8.5 + +### AndroidBootPartitionMaxSize + +Maximum size the firmware may use of a partition. + +Since: 1.8.5 + +## Vendor ID Security + +The vendor ID is set through the `android-boot.quirk` file. + +## External Interface Access + +This plugin requires read/write access to `/dev/block`. diff --git a/fwupd-1.8.6/plugins/android-boot/android-boot.quirk b/fwupd-1.8.6/plugins/android-boot/android-boot.quirk new file mode 100644 index 0000000000000000000000000000000000000000..09d78e7b60997e8d8c98d0138bff3e7dddbd9adf --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/android-boot.quirk @@ -0,0 +1,16 @@ +[BLOCK] +Plugin = android_boot + +# SHIFT6mq ABL A +[DRIVE\UUID_c49183ed-aaec-9bf5-760a-66330fbcffc1&LABEL_abl-a] +Flags = updatable,signed-payload +Vendor = SHIFT GmbH +VendorId = eco.shift +AndroidBootVersionProperty = androidboot.abl.revision + +# SHIFT6mq ABL B +[DRIVE\UUID_3d7b21e8-048b-db0b-0c18-d07a9bb32f2d&LABEL_abl-b] +Flags = updatable,signed-payload +Vendor = SHIFT GmbH +VendorId = eco.shift +AndroidBootVersionProperty = androidboot.abl.revision diff --git a/fwupd-1.8.6/plugins/android-boot/fu-android-boot-device.c b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-device.c new file mode 100644 index 0000000000000000000000000000000000000000..c4651bc786aec5f02b79b7ebad6e11ef2f78b93e --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-device.c @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2022 Dylan Van Assche + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-android-boot-device.h" + +#define ANDROID_BOOT_UNKNOWN_VERSION "0.0.0" +#define ANDROID_BOOT_SECTOR_SIZE 512 + +struct _FuAndroidBootDevice { + FuUdevDevice parent_instance; + gchar *label; + gchar *uuid; + gchar *boot_slot; + guint64 max_size; +}; + +G_DEFINE_TYPE(FuAndroidBootDevice, fu_android_boot_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_android_boot_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuAndroidBootDevice *self = FU_ANDROID_BOOT_DEVICE(device); + + fu_string_append(str, idt, "BootSlot", self->boot_slot); + fu_string_append(str, idt, "Label", self->label); + fu_string_append(str, idt, "UUID", self->uuid); + fu_string_append_kx(str, idt, "MaxSize", self->max_size); +} + +static gboolean +fu_android_boot_device_probe(FuDevice *device, GError **error) +{ + FuAndroidBootDevice *self = FU_ANDROID_BOOT_DEVICE(device); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + guint64 sectors = 0; + guint64 size = 0; + g_autoptr(GHashTable) cmdline = NULL; + + /* FuUdevDevice->probe */ + if (!FU_DEVICE_CLASS(fu_android_boot_device_parent_class)->probe(device, error)) + return FALSE; + + /* get kernel cmdline */ + cmdline = fu_kernel_get_cmdline(error); + if (cmdline == NULL) + return FALSE; + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "block", error)) + return FALSE; + + /* extract boot slot if available */ + self->boot_slot = g_strdup(g_hash_table_lookup(cmdline, "androidboot.slot_suffix")); + + /* extract label and check if it matches boot slot*/ + if (g_udev_device_has_property(udev_device, "ID_PART_ENTRY_NAME")) { + self->label = + g_strdup(g_udev_device_get_property(udev_device, "ID_PART_ENTRY_NAME")); + + /* Use label as device name */ + fu_device_set_name(device, self->label); + + /* If the device has A/B partitioning, compare boot slot to only expose partitions + * in-use */ + if (self->boot_slot != NULL && !g_str_has_suffix(self->label, self->boot_slot)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device is on a different bootslot"); + return FALSE; + } + } + + /* set max firmware size, required to avoid writing firmware bigger than partition */ + if (!g_udev_device_has_property(udev_device, "ID_PART_ENTRY_SIZE")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device does not expose its size"); + return FALSE; + } + + sectors = g_udev_device_get_property_as_uint64(udev_device, "ID_PART_ENTRY_SIZE"); + size = sectors * ANDROID_BOOT_SECTOR_SIZE; + self->max_size = size; + + /* extract partition UUID and require it for supporting a device */ + if (!g_udev_device_has_property(udev_device, "ID_PART_ENTRY_UUID")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device does not have a UUID"); + return FALSE; + } + self->uuid = g_strdup(g_udev_device_get_property(udev_device, "ID_PART_ENTRY_UUID")); + + /* extract serial number and set it */ + fu_device_set_serial(device, g_hash_table_lookup(cmdline, "androidboot.serialno")); + + /* + * Some devices don't have unique TYPE UUIDs, add the partition label to make them truly + * unique Devices have a fixed partition scheme anyway because they originally have Android + * which has such requirements. + */ + fu_device_add_instance_strsafe(device, "UUID", self->uuid); + fu_device_add_instance_strsafe(device, "LABEL", self->label); + fu_device_add_instance_strsafe(device, "SLOT", self->boot_slot); + + /* GUID based on UUID / UUID, label / UUID, label, slot */ + fu_device_build_instance_id(device, NULL, "DRIVE", "UUID", NULL); + fu_device_build_instance_id(device, NULL, "DRIVE", "UUID", "LABEL", NULL); + fu_device_build_instance_id(device, NULL, "DRIVE", "UUID", "LABEL", "SLOT", NULL); + + /* quirks will have matched now */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device is not updatable"); + return FALSE; + } + + /* set the firmware maximum size based on partition size or from quirk */ + fu_device_set_firmware_size_max(device, self->max_size); + + return TRUE; +} + +static gboolean +fu_android_boot_device_open(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* FuUdevDevice->open */ + if (!FU_DEVICE_CLASS(fu_android_boot_device_parent_class)->open(device, &error_local)) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + error_local->message); + return FALSE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_android_boot_device_write(FuAndroidBootDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + /* rewind */ + if (!fu_udev_device_seek(FU_UDEV_DEVICE(self), 0x0, error)) { + g_prefix_error(error, "failed to rewind: "); + return FALSE; + } + + /* write each chunk */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(self), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + return TRUE; +} + +static gboolean +fu_android_boot_device_erase(FuAndroidBootDevice *self, FuProgress *progress, GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + gsize bufsz = fu_device_get_firmware_size_max(FU_DEVICE(self)); + g_autofree guint8 *buf = g_malloc0(bufsz); + + chunks = fu_chunk_array_new(buf, bufsz, 0x0, 0x0, 10 * 1024); + + if (g_getenv("FWUPD_ANDROID_BOOT_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "erase", buf, bufsz); + + if (!fu_android_boot_device_write(self, chunks, progress, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_android_boot_device_verify(FuAndroidBootDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + /* verify each chunk */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autofree guint8 *buf = g_malloc0(fu_chunk_get_data_sz(chk)); + g_autoptr(GBytes) blob1 = fu_chunk_get_bytes(chk); + g_autoptr(GBytes) blob2 = NULL; + + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), + fu_chunk_get_address(chk), + buf, + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to read @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + blob2 = g_bytes_new_static(buf, fu_chunk_get_data_sz(chk)); + if (!fu_bytes_compare(blob1, blob2, error)) { + g_prefix_error(error, + "failed to verify @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + return TRUE; +} + +static gboolean +fu_android_boot_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuAndroidBootDevice *self = FU_ANDROID_BOOT_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get data to write */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + if (g_getenv("FWUPD_ANDROID_BOOT_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "write", fw); + + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, 10 * 1024); + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 72, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 7, NULL); + + /* erase, write, verify */ + if (!fu_android_boot_device_erase(self, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_android_boot_device_write(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_android_boot_device_verify(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + return TRUE; +} + +static gboolean +fu_android_boot_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuAndroidBootDevice *self = FU_ANDROID_BOOT_DEVICE(device); + + /* load from quirks */ + if (g_strcmp0(key, "AndroidBootVersionProperty") == 0) { + g_autoptr(GHashTable) cmdline = NULL; + const gchar *version = NULL; + + cmdline = fu_kernel_get_cmdline(error); + if (cmdline == NULL) + return FALSE; + + version = g_hash_table_lookup(cmdline, value); + if (version != NULL) + fu_device_set_version(device, version); + return TRUE; + } + + if (g_strcmp0(key, "AndroidBootPartitionMaxSize") == 0) { + guint64 size = 0; + + if (!fu_strtoull(value, &size, 0, G_MAXUINT32, error)) + return FALSE; + self->max_size = size; + 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_android_boot_device_finalize(GObject *obj) +{ + FuAndroidBootDevice *self = FU_ANDROID_BOOT_DEVICE(obj); + G_OBJECT_CLASS(fu_android_boot_device_parent_class)->finalize(obj); + g_free(self->boot_slot); + g_free(self->label); + g_free(self->uuid); +} + +static void +fu_android_boot_device_init(FuAndroidBootDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "Android Bootloader"); + fu_device_add_protocol(FU_DEVICE(self), "com.google.android_boot"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE | + FU_UDEV_DEVICE_FLAG_OPEN_SYNC); + fu_device_add_icon(FU_DEVICE(self), "computer"); + + /* + * Fallback for ABL without version reporting, fwupd will always provide an upgrade in this + * case. Once upgraded, the version reporting will be available and the update notification + * will disappear. If version reporting is available, the reported version is set. + */ + fu_device_set_version(FU_DEVICE(self), ANDROID_BOOT_UNKNOWN_VERSION); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); +} + +static void +fu_android_boot_device_class_init(FuAndroidBootDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *klass_object = G_OBJECT_CLASS(klass); + + klass_object->finalize = fu_android_boot_device_finalize; + klass_device->probe = fu_android_boot_device_probe; + klass_device->open = fu_android_boot_device_open; + klass_device->write_firmware = fu_android_boot_device_write_firmware; + klass_device->to_string = fu_android_boot_device_to_string; + klass_device->set_quirk_kv = fu_android_boot_device_set_quirk_kv; +} diff --git a/fwupd-1.8.6/plugins/android-boot/fu-android-boot-device.h b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ca59e00756a0144bdab6f94e64a374e13d5496f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2022 Dylan Van Assche + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ANDROID_BOOT_DEVICE (fu_android_boot_device_get_type()) + +G_DECLARE_FINAL_TYPE(FuAndroidBootDevice, + fu_android_boot_device, + FU, + ANDROID_BOOT_DEVICE, + FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/android-boot/fu-android-boot-plugin.c b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..2eeda5c5d944b8e3a5848fb7673f89616b932474 --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Dylan Van Assche + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-android-boot-device.h" +#include "fu-android-boot-plugin.h" + +struct _FuAndroidBootPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAndroidBootPlugin, fu_android_boot_plugin, FU_TYPE_PLUGIN) + +static void +fu_android_boot_plugin_init(FuAndroidBootPlugin *self) +{ +} + +static void +fu_android_boot_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ANDROID_BOOT_DEVICE); + fu_plugin_add_udev_subsystem(plugin, "block"); +} + +static void +fu_android_boot_plugin_class_init(FuAndroidBootPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_android_boot_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/android-boot/fu-android-boot-plugin.h b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..f7b5805b9e90aa522e3923b7e70e969b04b8a849 --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/fu-android-boot-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAndroidBootPlugin, fu_android_boot_plugin, FU, ANDROID_BOOT_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/android-boot/meson.build b/fwupd-1.8.6/plugins/android-boot/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c38ad2911873bd9eeff649b1b3793a0dc65f22f0 --- /dev/null +++ b/fwupd-1.8.6/plugins/android-boot/meson.build @@ -0,0 +1,16 @@ +if get_option('plugin_android_boot').require(gudev.found(), + error_message: 'gudev is needed for plugin_android_boot').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginAndroidBoot"'] + +plugin_quirks += files('android-boot.quirk') +plugin_builtins += static_library('fu_plugin_android_boot', + sources: [ + 'fu-android-boot-plugin.c', + 'fu-android-boot-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/ata/README.md b/fwupd-1.8.6/plugins/ata/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a459dfccc594978c0382c8f0ee2ac20a68265739 --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/README.md @@ -0,0 +1,62 @@ +# 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 +for more details. + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, but it is +only activated when the system is in the final shutdown stages. This is done to +minimize the chance of data loss if the switch to the new firmware is not done +correctly. + +## Vendor ID Security + +No vendor ID is set as there is no vendor field in the IDENTIFY response. + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### AtaTransferBlocks + +Blocks to transfer, or `0xffff` for max + +Since: 1.2.4 + +### AtaTransferMode + +The transfer mode, `0x3`, `0x7` or `0xe` + +Since: 1.2.4 + +## External Interface Access + +This plugin requires the `SG_IO` ioctl interface. diff --git a/fwupd-1.8.6/plugins/ata/ata.quirk b/fwupd-1.8.6/plugins/ata/ata.quirk new file mode 100644 index 0000000000000000000000000000000000000000..8e8ff6750fc5c09d669c5343be7f4c70fd6504ac --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/ata.quirk @@ -0,0 +1,99 @@ +# match all devices with this udev subsystem +[BLOCK] +Plugin = ata + +[ThinkSystem M.2 VD] +Flags = ~updatable + +[OUI\000039] +Vendor = Toshiba +VendorId = ATA:0x1179 + +[OUI\0000f0] +Vendor = Samsung +VendorId = ATA:0x144D + +[OUI\000120] +Vendor = Corsair +VendorId = ATA:0x1987 + +[OUI\0004cf] +Vendor = Seagate +VendorId = ATA:0x1BB1 + +[OUI\00080d] +Vendor = Toshiba +VendorId = ATA:0x1179 + +[OUI\000c50] +Vendor = Seagate +VendorId = ATA:0x1BB1 + +[OUI\000cca] +Vendor = Western Digital +VendorId = ATA:0x101C + +[OUI\0014ee] +Vendor = Western Digital +VendorId = ATA:0x101C + +[OUI\001517] +Vendor = Intel +VendorId = ATA:0x8086 + +[OUI\001b44] +Vendor = Western Digital +VendorId = ATA:0x101C + +[OUI\002303] +Vendor = LITE-ON +VendorId = ATA:0x14A4 + +[OUI\0024e9] +Vendor = Samsung +VendorId = ATA:0x144D + +[OUI\002538] +Vendor = Samsung +VendorId = ATA:0x144D + +[OUI\0026b7] +Vendor = Kingston +VendorId = ATA:0x2646 +Flags = needs-shutdown + +[OUI\00a075] +Vendor = Micron +VendorId = ATA:0x1344 + +[OUI\030302] +Vendor = SK hynix +VendorId = ATA:0x1C5C + +[OUI\5cd2e4] +Vendor = Intel +VendorId = ATA:0x8086 + +[OUI\707c18] +Vendor = ADATA +VendorId = ATA:0x1CC1 + +[OUI\7c3548] +Vendor = Transcend +VendorId = ATA:0x8564 + +[OUI\001b44] +Vendor = SanDisk +VendorId = ATA:0x15B7 + +[OUI\96a060] +Vendor = Western Digital +VendorId = ATA:0x101C + +[OUI\f8db4c] +Vendor = PNY +VendorId = ATA:0x196E + +[OUI\e83a97] +Vendor = Toshiba +VendorId = ATA:0x1179 diff --git a/fwupd-1.8.6/plugins/ata/fu-ata-device.c b/fwupd-1.8.6/plugins/ata/fu-ata-device.c new file mode 100644 index 0000000000000000000000000000000000000000..1e1c0aa7fe823789c51261e506599275d082e9a6 --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/fu-ata-device.c @@ -0,0 +1,931 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-ata-device.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) + +#define FU_ATA_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +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; + guint16 transfer_blocks; + guint8 transfer_mode; + guint32 oui; +}; + +G_DEFINE_TYPE(FuAtaDevice, fu_ata_device, FU_TYPE_UDEV_DEVICE) + +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, guint idt, GString *str) +{ + FuAtaDevice *self = FU_ATA_DEVICE(device); + FU_DEVICE_CLASS(fu_ata_device_parent_class)->to_string(device, idt, str); + fu_string_append_kx(str, idt, "TransferMode", self->transfer_mode); + fu_string_append_kx(str, idt, "TransferBlocks", self->transfer_blocks); + if (self->oui != 0x0) + fu_string_append_kx(str, idt, "OUI", self->oui); + fu_string_append_ku(str, idt, "PciDepth", self->pci_depth); + fu_string_append_ku(str, idt, "UsbDepth", 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_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 *)(((guint8 *)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_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_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); + + /* owned by Dell */ + fu_device_set_vendor(FU_DEVICE(self), "Dell"); + fu_device_add_vendor_id(FU_DEVICE(self), "ATA:0x1028"); +} + +static void +fu_ata_device_parse_vendor_name(FuAtaDevice *self, const gchar *name) +{ + struct { + const gchar *prefix; /* in CAPS */ + guint16 vid; + const gchar *name; + } map_name[] = {/* vendor matches */ + {"ADATA*", 0x1cc1, "ADATA"}, + {"APACER*", 0x0000, "Apacer"}, /* not in pci.ids */ + {"APPLE*", 0x106b, "Apple"}, + {"CORSAIR*", 0x1987, "Corsair"}, /* identifies as Phison */ + {"CRUCIAL*", 0xc0a9, "Crucial"}, + {"FUJITSU*", 0x10cf, "Fujitsu"}, + {"GIGABYTE*", 0x1458, "Gigabyte"}, + {"HGST*", 0x101c, "Western Digital"}, + {"HITACHI*", 0x101c, "Western Digital"}, /* was acquired by WD */ + {"HITACHI*", 0x1054, "Hitachi"}, + {"HP SSD*", 0x103c, "HP"}, + {"INTEL*", 0x8086, "Intel"}, + {"KINGSPEC*", 0x0000, "KingSpec"}, /* not in pci.ids */ + {"KINGSTON*", 0x2646, "Kingston"}, + {"LITEON*", 0x14a4, "LITE-ON"}, + {"MAXTOR*", 0x115f, "Maxtor"}, + {"MICRON*", 0x1344, "Micron"}, + {"OCZ*", 0x1179, "Toshiba"}, + {"PNY*", 0x196e, "PNY"}, + {"QEMU*", 0x1b36, "QEMU"}, /* identifies as Red Hat! */ + {"SAMSUNG*", 0x144d, "Samsung"}, + {"SANDISK*", 0x15b7, "SanDisk"}, + {"SEAGATE*", 0x1bb1, "Seagate"}, + { + "SK HYNIX*", + 0x1c5c, + "SK hynix", + }, + {"SUPERMICRO*", 0x15d9, "SuperMicro"}, + {"TOSHIBA*", 0x1179, "Toshiba"}, + {"WDC*", 0x101c, "Western Digital"}, + {NULL, 0x0000, NULL}}; + struct { + const gchar *prefix; /* in CAPS */ + guint16 vid; + const gchar *name; + } map_fuzzy[] = {/* fuzzy name matches -- also see legacy list at: + * https://github.com/linuxhw/hw-probe/blob/master/hw-probe.pl#L647 */ + {"001-*", 0x1bb1, "Seagate"}, + {"726060*", 0x101c, "Western Digital"}, + {"CT*", 0xc0a9, "Crucial"}, + {"DT0*", 0x1179, "Toshiba"}, + {"EK0*", 0x1590, "HPE"}, + {"EZEX*", 0x101c, "Western Digital"}, + {"GB0*", 0x1590, "HPE"}, + {"GOODRAM*", 0x1987, "Phison"}, + {"H??54*", 0x101c, "Western Digital"}, + {"H??72?0*", 0x101c, "Western Digital"}, + {"HDWG*", 0x1179, "Toshiba"}, + {"M?0??CA*", 0x1179, "Toshiba"}, /* enterprise */ + {"M4-CT*", 0xc0a9, "Crucial"}, + { + "MA*", + 0x10cf, + "Fujitsu", + }, + { + "MB*", + 0x10cf, + "Fujitsu", + }, + {"MK0*", 0x1590, "HPE"}, + {"MTFDDAK*", 0x1344, "Micron"}, + { + "NIM*", + 0x0000, + "Nimbus", + }, /* no PCI ID */ + { + "SATADOM*", + 0x0000, + "Innodisk", + }, /* no PCI ID */ + {"SSD 860*", 0x144d, "Samsung"}, + {"SSDPR*", 0x1987, "Phison"}, + {"SSDSC?K*", 0x8086, "Intel"}, + { + "ST*", + 0x1bb1, + "Seagate", + }, + {"TEAM*", 0x0000, "Team Group"}, /* not in pci.ids */ + {"TS*", 0x8564, "Transcend"}, + {"VK0*", 0x1590, "HPE"}, + {"WD*", 0x101c, "Western Digital"}, + {NULL, 0x0000, NULL}}; + struct { + const gchar *prefix; /* in CAPS */ + guint16 vid; + const gchar *name; + } map_version[] = {/* fuzzy version matches */ + {"CS2111*", 0x196e, "PNY"}, + {"S?FM*", 0x1987, "Phison"}, + {NULL, 0x0000, NULL}}; + g_autofree gchar *name_up = g_ascii_strup(name, -1); + g_autofree gchar *vendor_id = NULL; + + /* find match */ + for (guint i = 0; map_name[i].prefix != NULL; i++) { + if (fu_path_fnmatch(map_name[i].prefix, name_up)) { + name += strlen(map_name[i].prefix) - 1; + fu_device_set_vendor(FU_DEVICE(self), map_name[i].name); + vendor_id = g_strdup_printf("ATA:0x%X", map_name[i].vid); + break; + } + } + + /* fall back to fuzzy match */ + if (vendor_id == NULL) { + for (guint i = 0; map_fuzzy[i].prefix != NULL; i++) { + if (fu_path_fnmatch(map_fuzzy[i].prefix, name_up)) { + fu_device_set_vendor(FU_DEVICE(self), map_fuzzy[i].name); + vendor_id = g_strdup_printf("ATA:0x%X", map_fuzzy[i].vid); + break; + } + } + } + + /* fall back to version */ + if (vendor_id == NULL) { + g_autofree gchar *version_up = + g_ascii_strup(fu_device_get_version(FU_DEVICE(self)), -1); + for (guint i = 0; map_version[i].prefix != NULL; i++) { + if (fu_path_fnmatch(map_version[i].prefix, version_up)) { + fu_device_set_vendor(FU_DEVICE(self), map_version[i].name); + vendor_id = g_strdup_printf("ATA:0x%X", map_version[i].vid); + break; + } + } + } + + /* devices without a vendor ID will not be UPGRADABLE */ + if (vendor_id != NULL) + fu_device_add_vendor_id(FU_DEVICE(self), vendor_id); + + /* remove leading junk */ + while (name[0] == ' ' || name[0] == '_' || name[0] == '-') + name += 1; + + /* if changed */ + if (g_strcmp0(fu_device_get_name(FU_DEVICE(self)), name) != 0) + fu_device_set_name(FU_DEVICE(self), name); +} + +static gboolean +fu_ata_device_parse_id(FuAtaDevice *self, const guint8 *buf, gsize sz, GError **error) +{ + FuDevice *device = FU_DEVICE(self); + gboolean has_oui_quirk = FALSE; + guint16 xfer_min = 1; + guint16 xfer_max = 0xffff; + guint16 id[FU_ATA_IDENTIFY_SIZE / 2]; + g_autofree gchar *name = 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_memread_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_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); + } + + /* get OUI if set */ + self->oui = ((guint32)(id[108] & 0x0fff)) << 12 | ((guint32)(id[109] & 0xfff0)) >> 4; + if (self->oui > 0x0) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf("OUI\\%06x", self->oui); + fu_device_add_instance_id_full(device, tmp, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + has_oui_quirk = fu_device_get_vendor(FU_DEVICE(self)) != NULL; + } + if (self->oui > 0x0) { + g_autofree gchar *vendor_id = NULL; + vendor_id = g_strdup_printf("OUI:%06x", self->oui); + fu_device_add_vendor_id(device, vendor_id); + } + + /* if not already set using the vendor block or a OUI quirk */ + name = fu_ata_device_get_string(id, 27, 46); + if (name != NULL) { + /* use the name as-is */ + if (has_oui_quirk) { + fu_device_set_name(FU_DEVICE(self), name); + } else { + fu_ata_device_parse_vendor_name(self, name); + } + } + + /* 8 byte additional product identifier == SKU? */ + sku = fu_ata_device_get_string(id, 170, 173); + if (sku != NULL) + g_debug("SKU=%s", sku); + + /* add extra GUIDs if none detected from identify block */ + if (name != NULL && fu_device_get_guids(device)->len == 0) { + g_autofree gchar *name_pad = fu_ata_device_pad_string_for_id(name); + 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, name); + } + + /* for Phison this is per-chipset -- which is specified in the version prefix */ + if (g_strcmp0(fu_device_get_vendor(device), "Phison") == 0 && + fu_device_get_version(device) != NULL) { + if (g_str_has_prefix(fu_device_get_version(device), "SB")) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + } else if (g_str_has_prefix(fu_device_get_version(device), "SC") || + g_str_has_prefix(fu_device_get_version(device), "SH")) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } + } + + return TRUE; +} + +static gboolean +fu_ata_device_probe(FuDevice *device, GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE(device); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + + /* check is valid */ + if (g_strcmp0(g_udev_device_get_devtype(udev_device), "disk") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct devtype=%s, expected disk", + g_udev_device_get_devtype(udev_device)); + return FALSE; + } + if (!g_udev_device_get_property_as_boolean(udev_device, "ID_ATA_SATA") || + !g_udev_device_get_property_as_boolean(udev_device, "ID_ATA_DOWNLOAD_MICROCODE")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "has no ID_ATA_DOWNLOAD_MICROCODE"); + return FALSE; + } + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(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(FU_UDEV_DEVICE(device), "pci"); + self->usb_depth = fu_udev_device_get_slot_depth(FU_UDEV_DEVICE(device), "usb"); + if (self->pci_depth <= 2 && self->usb_depth <= 2) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + } + + 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; + if (g_getenv("FWUPD_ATA_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "CDB", cdb, sizeof(cdb)); + if (dxfer_direction == SG_DXFER_TO_DEV && dxferp != NULL) { + fu_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 (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + SG_IO, + (guint8 *)&io_hdr, + NULL, + FU_ATA_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + if (g_getenv("FWUPD_ATA_VERBOSE") != NULL) { + 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_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]; + if (g_getenv("FWUPD_ATA_VERBOSE") != NULL) { + 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 (g_getenv("FWUPD_ATA_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "IDENTIFY", id, sizeof(id)); + if (!fu_ata_device_parse_id(self, id, sizeof(id), error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_ata_device_activate(FuDevice *device, FuProgress *progress, 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_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, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuAtaDevice *self = FU_ATA_DEVICE(device); + guint32 chunksz = (guint32)self->transfer_blocks * FU_ATA_BLOCK_SIZE; + guint max_size = 0xffff * FU_ATA_BLOCK_SIZE; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* 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_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + chunks = fu_chunk_array_new_from_bytes(fw, 0x00, 0x00, chunksz); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_ata_device_fw_download(self, + fu_chunk_get_idx(chk), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "failed to write chunk %u: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success! */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + 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); + guint64 tmp = 0; + + if (g_strcmp0(key, "AtaTransferMode") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + 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) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + 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_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +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_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_set_summary(FU_DEVICE(self), "ATA drive"); + fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_add_protocol(FU_DEVICE(self), "org.t13.ata"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), FU_UDEV_DEVICE_FLAG_OPEN_READ); +} + +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); + 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->setup = fu_ata_device_setup; + klass_device->activate = fu_ata_device_activate; + klass_device->write_firmware = fu_ata_device_write_firmware; + klass_device->probe = fu_ata_device_probe; + klass_device->set_progress = fu_ata_device_set_progress; +} + +FuAtaDevice * +fu_ata_device_new_from_blob(FuContext *ctx, const guint8 *buf, gsize sz, GError **error) +{ + g_autoptr(FuAtaDevice) self = NULL; + + self = g_object_new(FU_TYPE_ATA_DEVICE, "context", ctx, NULL); + if (!fu_ata_device_parse_id(self, buf, sz, error)) + return NULL; + return g_steal_pointer(&self); +} diff --git a/fwupd-1.8.6/plugins/ata/fu-ata-device.h b/fwupd-1.8.6/plugins/ata/fu-ata-device.h new file mode 100644 index 0000000000000000000000000000000000000000..25bd65b3df25bee219bb3b1be59102f9e3b94be3 --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/fu-ata-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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_from_blob(FuContext *ctx, 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); diff --git a/fwupd-1.8.6/plugins/ata/fu-ata-plugin.c b/fwupd-1.8.6/plugins/ata/fu-ata-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..439457f35523612ca16462150aa23443f07955b0 --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/fu-ata-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ata-device.h" +#include "fu-ata-plugin.h" + +struct _FuAtaPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuAtaPlugin, fu_ata_plugin, FU_TYPE_PLUGIN) + +static void +fu_ata_plugin_init(FuAtaPlugin *self) +{ +} + +static void +fu_ata_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "block"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ATA_DEVICE); +} + +static void +fu_ata_plugin_class_init(FuAtaPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_ata_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/ata/fu-ata-plugin.h b/fwupd-1.8.6/plugins/ata/fu-ata-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..327245220bd684d34cf0f90f24e746558e51cb9e --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/fu-ata-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuAtaPlugin, fu_ata_plugin, FU, ATA_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/ata/fu-self-test.c b/fwupd-1.8.6/plugins/ata/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..7ce60e73f6c4cb5f495e6f40e0f7dea465a220d3 --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/fu-self-test.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ata-device.h" +#include "fu-context-private.h" +#include "fu-device-private.h" + +static void +fu_ata_id_func(void) +{ + gboolean ret; + gsize sz; + const gchar *ci = g_getenv("CI_NETWORK"); + g_autofree gchar *data = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuAtaDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + path = g_test_build_filename(G_TEST_DIST, "tests", "StarDrive-SBFM61.2.bin", NULL); + if (!g_file_test(path, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing StarDrive-SBFM61.2.bin"); + return; + } + ret = g_file_get_contents(path, &data, &sz, &error); + g_assert_no_error(error); + g_assert_true(ret); + dev = fu_ata_device_new_from_blob(ctx, (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"); +} + +static void +fu_ata_oui_func(void) +{ + gboolean ret; + gsize sz; + const gchar *ci = g_getenv("CI_NETWORK"); + g_autofree gchar *data = NULL; + g_autofree gchar *path = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuAtaDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + path = g_test_build_filename(G_TEST_DIST, "tests", "Samsung SSD 860 EVO 500GB.bin", NULL); + if (!g_file_test(path, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing Samsung SSD 860 EVO 500GB.bin"); + return; + } + ret = g_file_get_contents(path, &data, &sz, &error); + g_assert_no_error(error); + g_assert_true(ret); + dev = fu_ata_device_new_from_blob(ctx, (guint8 *)data, sz, &error); + g_assert_no_error(error); + g_assert_nonnull(dev); + fu_device_convert_instance_ids(FU_DEVICE(dev)); + str = fu_device_to_string(FU_DEVICE(dev)); + g_debug("%s", str); + 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)), ==, "S3Z1NB0K862928X"); + g_assert_cmpstr(fu_device_get_name(FU_DEVICE(dev)), ==, "SSD 860 EVO 500GB"); + g_assert_cmpstr(fu_device_get_version(FU_DEVICE(dev)), ==, "RVT01B6Q"); +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + /* tests go here */ + g_test_add_func("/fwupd/ata/id", fu_ata_id_func); + g_test_add_func("/fwupd/ata/oui", fu_ata_oui_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/ata/meson.build b/fwupd-1.8.6/plugins/ata/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..a655ccddd7113ded8675a58f567afa6c28c6895b --- /dev/null +++ b/fwupd-1.8.6/plugins/ata/meson.build @@ -0,0 +1,42 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginAta"'] + +plugin_quirks += files('ata.quirk') +plugin_builtin_ata = static_library('fu_plugin_ata', + sources: [ + 'fu-ata-plugin.c', + 'fu-ata-device.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_ata + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_DATADIR_QUIRKS', meson.current_source_dir()) + e = executable( + 'ata-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_ata, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('ata-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/bcm57xx/README.md b/fwupd-1.8.6/plugins/bcm57xx/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e5639fb243ac2cfc914d00657dbd6394318f8861 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/README.md @@ -0,0 +1,38 @@ +# BCM57xx + +## Introduction + +This plugin updates BCM57xx wired network adaptors from Broadcom using a +reverse-engineered flashing protocol. It is designed to be used with the +clean-room reimplementation of the BCM5719 firmware found here: + + +## Protocol + +BCM57xx devices support a custom `com.broadcom.bcm57xx` protocol which is +implemented as ioctls like ethtool does. + +## GUID Generation + +These devices use the standard PCI instance IDs, for example: + +* `PCI\VEN_14E4&DEV_1657` +* `PCI\VEN_14E4&DEV_1657&SUBSYS_17AA222E` + +## Update Behavior + +The device usually presents in runtime mode, and the firmware is written to the +device without disconnecting the working kernel driver. Once complete the APE +is reset which may cause a brief link reconnection. + +On flash failure the device is nonfunctional, but is recoverable using direct +BAR writes, which is typically much slower than updating the device using the +kernel driver and the ethtool API. + +## Vendor ID Security + +The vendor ID is set from the PCI vendor, in this instance set to `PCI:0x14E4` + +## External Interface Access + +This plugin requires the `SIOCETHTOOL` ioctl interface. diff --git a/fwupd-1.8.6/plugins/bcm57xx/bcm57xx.quirk b/fwupd-1.8.6/plugins/bcm57xx/bcm57xx.quirk new file mode 100644 index 0000000000000000000000000000000000000000..7be2eda2bda127b30b34ceeaf6c9f57bd15dde9d --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/bcm57xx.quirk @@ -0,0 +1,9 @@ +# Broadcom BCM5719 +[PCI\VEN_14E4&DEV_1657] +Plugin = bcm57xx + +# Dell PCI card +[PCI\VEN_14E4&DEV_1657&SUBSYS_14E41904] +FirmwareSize = 0x80000 +[PCI\VEN_14E4&DEV_1657&SUBSYS_94E41904] +FirmwareSize = 0x80000 diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-common.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-common.c new file mode 100644 index 0000000000000000000000000000000000000000..34891621f3d129950e2d8d880d88c12d83b0a456 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-common.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-bcm57xx-common.h" + +guint32 +fu_bcm57xx_nvram_crc(const guint8 *buf, gsize bufsz) +{ + return fu_crc32(buf, bufsz); +} + +gboolean +fu_bcm57xx_verify_crc(GBytes *fw, GError **error) +{ + guint32 crc_actual; + guint32 crc_file = 0; + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* expected */ + if (!fu_memread_uint32_safe(buf, + bufsz, + bufsz - sizeof(guint32), + &crc_file, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* reality */ + crc_actual = fu_bcm57xx_nvram_crc(buf, bufsz - sizeof(guint32)); + if (crc_actual != crc_file) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid CRC, expected 0x%08x got: 0x%08x", + (guint)crc_file, + (guint)crc_actual); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_bcm57xx_verify_magic(GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* hardcoded value */ + if (!fu_memread_uint32_safe(buf, bufsz, offset, &magic, G_BIG_ENDIAN, error)) + return FALSE; + if (magic != BCM_NVRAM_MAGIC) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid magic, got: 0x%x", + (guint)magic); + return FALSE; + } + + /* success */ + return TRUE; +} + +void +fu_bcm57xx_veritem_free(Bcm57xxVeritem *veritem) +{ + g_free(veritem->branch); + g_free(veritem->version); + g_free(veritem); +} + +Bcm57xxVeritem * +fu_bcm57xx_veritem_new(const guint8 *buf, gsize bufsz) +{ + g_autofree gchar *tmp = NULL; + g_autoptr(Bcm57xxVeritem) veritem = g_new0(Bcm57xxVeritem, 1); + struct { + const gchar *prefix; + const gchar *branch; + FwupdVersionFormat verfmt; + } data[] = {{"5719-v", BCM_FW_BRANCH_UNKNOWN, FWUPD_VERSION_FORMAT_PAIR}, + {"stage1-", BCM_FW_BRANCH_OSS_FIRMWARE, FWUPD_VERSION_FORMAT_TRIPLET}, + {NULL, NULL, 0}}; + + /* do not assume this is NUL terminated */ + tmp = g_strndup((const gchar *)buf, bufsz); + if (tmp == NULL || tmp[0] == '\0') + return NULL; + + /* use prefix to define object */ + for (guint i = 0; data[i].prefix != NULL; i++) { + if (g_str_has_prefix(tmp, data[i].prefix)) { + veritem->version = g_strdup(tmp + strlen(data[i].prefix)); + veritem->branch = g_strdup(data[i].branch); + veritem->verfmt = data[i].verfmt; + return g_steal_pointer(&veritem); + } + } + veritem->verfmt = FWUPD_VERSION_FORMAT_UNKNOWN; + veritem->version = g_strdup(tmp); + return g_steal_pointer(&veritem); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-common.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-common.h new file mode 100644 index 0000000000000000000000000000000000000000..9c5c0fddfdfbc73aafe74b425fa812fb3da6061b --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-common.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define BCM_VENDOR_BROADCOM 0x14E4 + +#define BCM_FW_BRANCH_UNKNOWN NULL +#define BCM_FW_BRANCH_OSS_FIRMWARE "oss-firmware" + +#define BCM_FIRMWARE_SIZE 0x40000 /* x2 for Dell */ +#define BCM_PHYS_ADDR_DEFAULT 0x08003800 + +#define BCM_NVRAM_MAGIC 0x669955AA + +/* offsets into NVMRAM */ +#define BCM_NVRAM_HEADER_BASE 0x00 +#define BCM_NVRAM_DIRECTORY_BASE 0x14 +#define BCM_NVRAM_INFO_BASE 0x74 +#define BCM_NVRAM_VPD_BASE 0x100 +#define BCM_NVRAM_INFO2_BASE 0x200 +#define BCM_NVRAM_STAGE1_BASE 0x28c + +#define BCM_NVRAM_HEADER_MAGIC 0x00 +#define BCM_NVRAM_HEADER_PHYS_ADDR 0x04 +#define BCM_NVRAM_HEADER_SIZE_WRDS 0x08 +#define BCM_NVRAM_HEADER_OFFSET 0x0C +#define BCM_NVRAM_HEADER_CRC 0x10 +#define BCM_NVRAM_HEADER_SZ 0x14 + +#define BCM_NVRAM_INFO_MAC_ADDR0 0x00 +#define BCM_NVRAM_INFO_VENDOR 0x2E +#define BCM_NVRAM_INFO_DEVICE 0x2C +#define BCM_NVRAM_INFO_SZ 0x8C + +#define BCM_NVRAM_DIRECTORY_ADDR 0x00 +#define BCM_NVRAM_DIRECTORY_SIZE_WRDS 0x04 +#define BCM_NVRAM_DIRECTORY_OFFSET 0x08 +#define BCM_NVRAM_DIRECTORY_SZ 0x0c + +#define BCM_NVRAM_VPD_SZ 0x100 + +#define BCM_NVRAM_INFO2_SZ 0x8c + +#define BCM_NVRAM_STAGE1_VERADDR 0x08 +#define BCM_NVRAM_STAGE1_VERSION 0x0C + +typedef struct { + gchar *branch; + gchar *version; + FwupdVersionFormat verfmt; +} Bcm57xxVeritem; + +guint32 +fu_bcm57xx_nvram_crc(const guint8 *buf, gsize bufsz); +gboolean +fu_bcm57xx_verify_crc(GBytes *fw, GError **error); +gboolean +fu_bcm57xx_verify_magic(GBytes *fw, gsize offset, GError **error); + +/* parses stage1 version */ +void +fu_bcm57xx_veritem_free(Bcm57xxVeritem *veritem); +Bcm57xxVeritem * +fu_bcm57xx_veritem_new(const guint8 *buf, gsize bufsz); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Bcm57xxVeritem, fu_bcm57xx_veritem_free) diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-device.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-device.c new file mode 100644 index 0000000000000000000000000000000000000000..c745d1122764c38509fc350fc2578ae396e671c5 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-device.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2018 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#include +#ifdef HAVE_ETHTOOL_H +#include +#include +#include +#endif +#ifdef HAVE_IOCTL_H +#include +#endif +#ifdef HAVE_SOCKET_H +#include +#endif + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-device.h" +#include "fu-bcm57xx-dict-image.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-recovery-device.h" + +#define FU_BCM57XX_BLOCK_SZ 0x4000 /* 16kb */ + +struct _FuBcm57xxDevice { + FuUdevDevice parent_instance; + FuBcm57xxRecoveryDevice *recovery; + gchar *ethtool_iface; + int ethtool_fd; +}; + +G_DEFINE_TYPE(FuBcm57xxDevice, fu_bcm57xx_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_bcm57xx_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + FU_DEVICE_CLASS(fu_bcm57xx_device_parent_class)->to_string(device, idt, str); + fu_string_append(str, idt, "EthtoolIface", self->ethtool_iface); +} + +static gboolean +fu_bcm57xx_device_probe(FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + g_autofree gchar *fn = NULL; + g_autoptr(GPtrArray) ifaces = NULL; + + /* only enumerate number 0 */ + if (fu_udev_device_get_number(FU_UDEV_DEVICE(device)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only device 0 supported on multi-device card"); + return FALSE; + } + + /* we need this even for non-recovery to reset APE */ + fu_device_set_context(FU_DEVICE(self->recovery), fu_device_get_context(FU_DEVICE(self))); + fu_device_incorporate(FU_DEVICE(self->recovery), FU_DEVICE(self)); + if (!fu_device_probe(FU_DEVICE(self->recovery), error)) + return FALSE; + + /* only if has an interface */ + fn = g_build_filename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)), "net", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_debug("waiting for net devices to appear"); + g_usleep(50 * 1000); + } + ifaces = fu_path_glob(fn, "en*", NULL); + if (ifaces == NULL || ifaces->len == 0) { + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(self->recovery)); + } else { + self->ethtool_iface = g_path_get_basename(g_ptr_array_index(ifaces, 0)); + } + + /* success */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error); +} + +static gboolean +fu_bcm57xx_device_nvram_write(FuBcm57xxDevice *self, + guint32 address, + const guint8 *buf, + gsize bufsz, + GError **error) +{ +#ifdef HAVE_ETHTOOL_H + gsize eepromsz; + gint rc = -1; + struct ifreq ifr = {0}; + g_autofree struct ethtool_eeprom *eeprom = NULL; + + /* failed to load tg3 */ + if (self->ethtool_iface == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as ethtool interface disabled"); + return FALSE; + } + + /* sanity check */ + if (address + bufsz > fu_device_get_firmware_size_max(FU_DEVICE(self))) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "tried to read outside of EEPROM size [0x%x]", + (guint)fu_device_get_firmware_size_max(FU_DEVICE(self))); + return FALSE; + } + + /* write EEPROM (NVRAM) data */ + eepromsz = sizeof(struct ethtool_eeprom) + bufsz; + eeprom = (struct ethtool_eeprom *)g_malloc0(eepromsz); + eeprom->cmd = ETHTOOL_SEEPROM; + eeprom->magic = BCM_NVRAM_MAGIC; + eeprom->len = bufsz; + eeprom->offset = address; + memcpy(eeprom->data, buf, eeprom->len); + strncpy(ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1); + ifr.ifr_data = (char *)eeprom; +#ifdef HAVE_IOCTL_H + rc = ioctl(self->ethtool_fd, SIOCETHTOOL, &ifr); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif + if (rc < 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot write eeprom [%i]", rc); + return FALSE; + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_nvram_read(FuBcm57xxDevice *self, + guint32 address, + guint8 *buf, + gsize bufsz, + GError **error) +{ +#ifdef HAVE_ETHTOOL_H + gsize eepromsz; + gint rc = -1; + struct ifreq ifr = {0}; + g_autofree struct ethtool_eeprom *eeprom = NULL; + + /* failed to load tg3 */ + if (self->ethtool_iface == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as ethtool interface disabled"); + return FALSE; + } + + /* sanity check */ + if (address + bufsz > fu_device_get_firmware_size_max(FU_DEVICE(self))) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "tried to read outside of EEPROM size [0x%x]", + (guint)fu_device_get_firmware_size_max(FU_DEVICE(self))); + return FALSE; + } + + /* read EEPROM (NVRAM) data */ + eepromsz = sizeof(struct ethtool_eeprom) + bufsz; + eeprom = (struct ethtool_eeprom *)g_malloc0(eepromsz); + eeprom->cmd = ETHTOOL_GEEPROM; + eeprom->len = bufsz; + eeprom->offset = address; + strncpy(ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1); + ifr.ifr_data = (char *)eeprom; +#ifdef HAVE_IOCTL_H + rc = ioctl(self->ethtool_fd, SIOCETHTOOL, &ifr); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif + if (rc < 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot read eeprom [%i]", rc); + return FALSE; + } + + /* copy back data */ + if (!fu_memcpy_safe(buf, + bufsz, + 0x0, /* dst */ + (guint8 *)eeprom, + eepromsz, /* src */ + G_STRUCT_OFFSET(struct ethtool_eeprom, data), + bufsz, + error)) + return FALSE; + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_nvram_check(FuBcm57xxDevice *self, GError **error) +{ +#ifdef HAVE_ETHTOOL_H + gint rc = -1; + struct ethtool_drvinfo drvinfo = {0}; + struct ifreq ifr = {0}; + + /* failed to load tg3 */ + if (self->ethtool_iface == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as ethtool interface disabled"); + return FALSE; + } + + /* get driver info */ + drvinfo.cmd = ETHTOOL_GDRVINFO; + strncpy(ifr.ifr_name, self->ethtool_iface, IFNAMSIZ - 1); + ifr.ifr_data = (char *)&drvinfo; +#ifdef HAVE_IOCTL_H + rc = ioctl(self->ethtool_fd, SIOCETHTOOL, &ifr); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif + if (rc < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot get driver information [%i]", + rc); + return FALSE; + } + g_debug("FW version %s", drvinfo.fw_version); + + /* sanity check */ + if (drvinfo.eedump_len != fu_device_get_firmware_size_max(FU_DEVICE(self))) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "EEPROM size invalid, got 0x%x, expected 0x%x", + drvinfo.eedump_len, + (guint)fu_device_get_firmware_size_max(FU_DEVICE(self))); + return FALSE; + } + + /* success */ + return TRUE; +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_activate(FuDevice *device, FuProgress *progress, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + g_autoptr(FuDeviceLocker) locker1 = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + + /* the only way to do this is using the mmap method */ + locker2 = fu_device_locker_new_full(FU_DEVICE(self->recovery), + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker2 == NULL) + return FALSE; + + /* open */ + locker1 = fu_device_locker_new(FU_DEVICE(self->recovery), error); + if (locker1 == NULL) + return FALSE; + + /* activate, causing APE reset, then close, then attach */ + if (!fu_device_activate(FU_DEVICE(self->recovery), progress, error)) + return FALSE; + + /* ensure we attach before we close */ + if (!fu_device_locker_close(locker2, error)) + return FALSE; + + /* wait for the device to restart before calling reload() */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); + fu_progress_sleep(progress, 5000); + return TRUE; +} + +static GBytes * +fu_bcm57xx_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + const gsize bufsz = fu_device_get_firmware_size_max(FU_DEVICE(self)); + g_autofree guint8 *buf = g_malloc0(bufsz); + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_mutable_new(buf, bufsz, 0x0, 0x0, FU_BCM57XX_BLOCK_SZ); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_bcm57xx_device_nvram_read(self, + fu_chunk_get_address(chk), + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) + return NULL; + fu_progress_step_done(progress); + } + + /* read from hardware */ + return g_bytes_new_take(g_steal_pointer(&buf), bufsz); +} + +static FuFirmware * +fu_bcm57xx_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new(); + g_autoptr(GBytes) fw = NULL; + + /* read from hardware */ + fw = fu_bcm57xx_device_dump_firmware(device, progress, error); + if (fw == NULL) + return NULL; + if (!fu_firmware_parse(firmware, fw, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return NULL; + + /* remove images that will contain user-data */ + if (!fu_firmware_remove_image_by_id(firmware, "info", error)) + return NULL; + if (!fu_firmware_remove_image_by_id(firmware, "info2", error)) + return NULL; + if (!fu_firmware_remove_image_by_id(firmware, "vpd", error)) + return NULL; + return g_steal_pointer(&firmware); +} + +static FuFirmware * +fu_bcm57xx_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + guint dict_cnt = 0; + g_autoptr(GBytes) fw_old = NULL; + g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new(); + g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new(); + g_autoptr(FuFirmware) img_ape = NULL; + g_autoptr(FuFirmware) img_stage1 = NULL; + g_autoptr(FuFirmware) img_stage2 = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GPtrArray) images = NULL; + + /* try to parse NVRAM, stage1 or APE */ + if (!fu_firmware_parse(firmware_tmp, fw, flags, error)) { + g_prefix_error(error, "failed to parse new firmware: "); + return NULL; + } + + /* for full NVRAM image, verify if correct device */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + guint16 vid = fu_bcm57xx_firmware_get_vendor(FU_BCM57XX_FIRMWARE(firmware_tmp)); + guint16 did = fu_bcm57xx_firmware_get_model(FU_BCM57XX_FIRMWARE(firmware_tmp)); + if (vid != 0x0 && did != 0x0 && + (fu_udev_device_get_vendor(FU_UDEV_DEVICE(device)) != vid || + fu_udev_device_get_model(FU_UDEV_DEVICE(device)) != did)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "PCI vendor or model incorrect, " + "got: %04X:%04X expected %04X:%04X", + vid, + did, + fu_udev_device_get_vendor(FU_UDEV_DEVICE(device)), + fu_udev_device_get_model(FU_UDEV_DEVICE(device))); + return NULL; + } + } + + /* get the existing firmware from the device */ + fw_old = fu_bcm57xx_device_dump_firmware(device, progress, error); + if (fw_old == NULL) + return NULL; + if (!fu_firmware_parse(firmware, fw_old, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) { + g_prefix_error(error, "failed to parse existing firmware: "); + return NULL; + } + if (g_getenv("FWUPD_BCM57XX_VERBOSE") != NULL) { + g_autofree gchar *str = fu_firmware_to_string(firmware); + g_debug("existing device firmware: %s", str); + } + + /* merge in all the provided images into the existing firmware */ + img_stage1 = fu_firmware_get_image_by_id(firmware_tmp, "stage1", NULL); + if (img_stage1 != NULL) + fu_firmware_add_image(firmware, img_stage1); + img_stage2 = fu_firmware_get_image_by_id(firmware_tmp, "stage2", NULL); + if (img_stage2 != NULL) + fu_firmware_add_image(firmware, img_stage2); + img_ape = fu_firmware_get_image_by_id(firmware_tmp, "ape", NULL); + if (img_ape != NULL) + fu_firmware_add_image(firmware, img_ape); + + /* the src and dst dictionaries may be in different order */ + images = fu_firmware_get_images(firmware); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + if (FU_IS_BCM57XX_DICT_IMAGE(img)) { + fu_firmware_set_idx(img, 0x80 + dict_cnt); + dict_cnt++; + } + } + if (g_getenv("FWUPD_BCM57XX_VERBOSE") != NULL) { + g_autofree gchar *str = fu_firmware_to_string(firmware); + g_debug("proposed device firmware: %s", str); + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_bcm57xx_device_write_chunks(FuBcm57xxDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_bcm57xx_device_nvram_write(self, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_step_done(progress); + } + return TRUE; +} + +static gboolean +fu_bcm57xx_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob_verify = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "build-img"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, "write-chunks"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 19, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, NULL); + + /* build the images into one linear blob of the correct size */ + blob = fu_firmware_write(firmware, error); + if (blob == NULL) + return FALSE; + fu_progress_step_done(progress); + + /* hit hardware */ + chunks = fu_chunk_array_new_from_bytes(blob, 0x0, 0x0, FU_BCM57XX_BLOCK_SZ); + if (!fu_bcm57xx_device_write_chunks(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + blob_verify = + fu_bcm57xx_device_dump_firmware(device, fu_progress_get_child(progress), error); + if (blob_verify == NULL) + return FALSE; + if (!fu_bytes_compare(blob, blob_verify, error)) + return FALSE; + fu_progress_step_done(progress); + + /* reset APE */ + if (!fu_device_activate(device, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_device_setup(FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + guint32 fwversion = 0; + + /* device is in recovery mode */ + if (self->ethtool_iface == NULL) { + g_autoptr(FuDeviceLocker) locker = NULL; + g_debug("device in recovery mode, use alternate device"); + locker = fu_device_locker_new(FU_DEVICE(self->recovery), error); + if (locker == NULL) + return FALSE; + return fu_device_setup(FU_DEVICE(self->recovery), error); + } + + /* check the EEPROM size */ + if (!fu_bcm57xx_device_nvram_check(self, error)) + return FALSE; + + /* get NVRAM version */ + if (!fu_bcm57xx_device_nvram_read(self, + BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION, + (guint8 *)&fwversion, + sizeof(guint32), + error)) + return FALSE; + if (fwversion != 0x0) { + g_autofree gchar *fwversion_str = NULL; + + /* this is only set on the OSS firmware */ + fwversion_str = fu_version_from_uint32(GUINT32_FROM_BE(fwversion), + FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, fwversion_str); + fu_device_set_version_raw(device, fwversion); + fu_device_set_branch(device, BCM_FW_BRANCH_OSS_FIRMWARE); + } else { + guint8 bufver[16] = {0x0}; + guint32 veraddr = 0; + g_autoptr(Bcm57xxVeritem) veritem = NULL; + + /* fall back to the string, e.g. '5719-v1.43' */ + if (!fu_bcm57xx_device_nvram_read(self, + BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERADDR, + (guint8 *)&veraddr, + sizeof(guint32), + error)) + return FALSE; + veraddr = GUINT32_FROM_BE(veraddr); + if (veraddr > BCM_PHYS_ADDR_DEFAULT) + veraddr -= BCM_PHYS_ADDR_DEFAULT; + if (!fu_bcm57xx_device_nvram_read(self, + BCM_NVRAM_STAGE1_BASE + veraddr, + bufver, + sizeof(bufver), + error)) + return FALSE; + veritem = fu_bcm57xx_veritem_new(bufver, sizeof(bufver)); + if (veritem != NULL) { + fu_device_set_version_format(device, veritem->verfmt); + fu_device_set_version(device, veritem->version); + fu_device_set_branch(device, veritem->branch); + } + } + + /* success */ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); + return TRUE; +} + +static gboolean +fu_bcm57xx_device_open(FuDevice *device, GError **error) +{ +#ifdef HAVE_SOCKET_H + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + self->ethtool_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (self->ethtool_fd < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to open socket: %s", +#ifdef HAVE_ERRNO_H + strerror(errno)); +#else + "unspecified error"); +#endif + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "socket() not supported as sys/socket.h not available"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_device_close(FuDevice *device, GError **error) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(device); + return g_close(self->ethtool_fd, error); +} + +static void +fu_bcm57xx_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_bcm57xx_device_init(FuBcm57xxDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(FU_DEVICE(self), "com.broadcom.bcm57xx"); + fu_device_add_icon(FU_DEVICE(self), "network-wired"); + + /* other values are set from a quirk */ + fu_device_set_firmware_size(FU_DEVICE(self), BCM_FIRMWARE_SIZE); + + /* used for recovery in case of ethtool failure and for APE reset */ + self->recovery = fu_bcm57xx_recovery_device_new(); +} + +static void +fu_bcm57xx_device_finalize(GObject *object) +{ + FuBcm57xxDevice *self = FU_BCM57XX_DEVICE(object); + g_free(self->ethtool_iface); + G_OBJECT_CLASS(fu_bcm57xx_device_parent_class)->finalize(object); +} + +static void +fu_bcm57xx_device_class_init(FuBcm57xxDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_bcm57xx_device_finalize; + klass_device->prepare_firmware = fu_bcm57xx_device_prepare_firmware; + klass_device->setup = fu_bcm57xx_device_setup; + klass_device->reload = fu_bcm57xx_device_setup; + klass_device->open = fu_bcm57xx_device_open; + klass_device->close = fu_bcm57xx_device_close; + klass_device->activate = fu_bcm57xx_device_activate; + klass_device->write_firmware = fu_bcm57xx_device_write_firmware; + klass_device->read_firmware = fu_bcm57xx_device_read_firmware; + klass_device->dump_firmware = fu_bcm57xx_device_dump_firmware; + klass_device->probe = fu_bcm57xx_device_probe; + klass_device->to_string = fu_bcm57xx_device_to_string; + klass_device->set_progress = fu_bcm57xx_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-device.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-device.h new file mode 100644 index 0000000000000000000000000000000000000000..03beb9c0d8a6d56ab49afa2f278ec38c8dc8763d --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_BCM57XX_DEVICE (fu_bcm57xx_device_get_type()) +G_DECLARE_FINAL_TYPE(FuBcm57xxDevice, fu_bcm57xx_device, FU, BCM57XX_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-dict-image.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-dict-image.c new file mode 100644 index 0000000000000000000000000000000000000000..0a5c9c3718e8f782239164f9881821c055406654 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-dict-image.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-dict-image.h" + +struct _FuBcm57xxDictImage { + FuFirmware parent_instance; + guint8 target; + guint8 kind; +}; + +G_DEFINE_TYPE(FuBcm57xxDictImage, fu_bcm57xx_dict_image, FU_TYPE_FIRMWARE) + +static void +fu_bcm57xx_dict_image_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuBcm57xxDictImage *self = FU_BCM57XX_DICT_IMAGE(firmware); + if (self->target != 0xff) + fu_xmlb_builder_insert_kx(bn, "target", self->target); + if (self->kind != 0xff) + fu_xmlb_builder_insert_kx(bn, "kind", self->kind); +} + +static gboolean +fu_bcm57xx_dict_image_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GBytes) fw_nocrc = NULL; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (!fu_bcm57xx_verify_crc(fw, error)) + return FALSE; + } + fw_nocrc = fu_bytes_new_offset(fw, 0x0, g_bytes_get_size(fw) - sizeof(guint32), error); + if (fw_nocrc == NULL) + return FALSE; + fu_firmware_set_bytes(firmware, fw_nocrc); + return TRUE; +} + +static GBytes * +fu_bcm57xx_dict_image_write(FuFirmware *firmware, GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + guint32 crc; + g_autoptr(GByteArray) blob = NULL; + g_autoptr(GBytes) fw_nocrc = NULL; + + /* get the CRC-less data */ + fw_nocrc = fu_firmware_get_bytes(firmware, error); + if (fw_nocrc == NULL) + return NULL; + + /* add to a mutable buffer */ + buf = g_bytes_get_data(fw_nocrc, &bufsz); + blob = g_byte_array_sized_new(bufsz + sizeof(guint32)); + fu_byte_array_append_bytes(blob, fw_nocrc); + + /* add CRC */ + crc = fu_bcm57xx_nvram_crc(buf, bufsz); + fu_byte_array_append_uint32(blob, crc, G_LITTLE_ENDIAN); + return g_byte_array_free_to_bytes(g_steal_pointer(&blob)); +} + +static gboolean +fu_bcm57xx_dict_image_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuBcm57xxDictImage *self = FU_BCM57XX_DICT_IMAGE(firmware); + guint64 tmp; + + /* two simple properties */ + tmp = xb_node_query_text_as_uint(n, "kind", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + fu_bcm57xx_dict_image_set_kind(self, tmp); + tmp = xb_node_query_text_as_uint(n, "target", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + fu_bcm57xx_dict_image_set_target(self, tmp); + + /* success */ + return TRUE; +} + +static void +fu_bcm57xx_dict_image_ensure_id(FuBcm57xxDictImage *self) +{ + g_autofree gchar *id = NULL; + struct { + guint8 target; + guint8 kind; + const gchar *id; + } ids[] = {{0x00, 0x00, "pxe"}, + {0x0D, 0x00, "ape"}, + {0x09, 0x00, "iscsi1"}, + {0x05, 0x00, "iscsi2"}, + {0x0b, 0x00, "iscsi3"}, + {0x00, 0x01, "cfg1000"}, + {0x04, 0x01, "vpd2"}, + {0xff, 0xff, NULL}}; + if (self->target == 0xff || self->kind == 0xff) + return; + for (guint i = 0; ids[i].id != NULL; i++) { + if (self->target == ids[i].target && self->kind == ids[i].kind) { + g_debug("using %s for %02x:%02x", ids[i].id, self->target, self->kind); + fu_firmware_set_id(FU_FIRMWARE(self), ids[i].id); + return; + } + } + id = g_strdup_printf("dict-%02x-%02x", self->target, self->kind); + if (g_getenv("FWUPD_FUZZER_RUNNING") == NULL) + g_warning("falling back to %s, please report", id); + fu_firmware_set_id(FU_FIRMWARE(self), id); +} + +void +fu_bcm57xx_dict_image_set_target(FuBcm57xxDictImage *self, guint8 target) +{ + self->target = target; + fu_bcm57xx_dict_image_ensure_id(self); +} + +guint8 +fu_bcm57xx_dict_image_get_target(FuBcm57xxDictImage *self) +{ + return self->target; +} + +void +fu_bcm57xx_dict_image_set_kind(FuBcm57xxDictImage *self, guint8 kind) +{ + self->kind = kind; + fu_bcm57xx_dict_image_ensure_id(self); +} + +guint8 +fu_bcm57xx_dict_image_get_kind(FuBcm57xxDictImage *self) +{ + return self->kind; +} + +static void +fu_bcm57xx_dict_image_init(FuBcm57xxDictImage *self) +{ + self->target = 0xff; + self->kind = 0xff; +} + +static void +fu_bcm57xx_dict_image_class_init(FuBcm57xxDictImageClass *klass) +{ + FuFirmwareClass *klass_image = FU_FIRMWARE_CLASS(klass); + klass_image->parse = fu_bcm57xx_dict_image_parse; + klass_image->write = fu_bcm57xx_dict_image_write; + klass_image->build = fu_bcm57xx_dict_image_build; + klass_image->export = fu_bcm57xx_dict_image_export; +} + +FuFirmware * +fu_bcm57xx_dict_image_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_BCM57XX_DICT_IMAGE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-dict-image.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-dict-image.h new file mode 100644 index 0000000000000000000000000000000000000000..23d8db169c6a7ec14ed6076fcb2ff09396a945ff --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-dict-image.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_BCM57XX_DICT_IMAGE (fu_bcm57xx_dict_image_get_type()) +G_DECLARE_FINAL_TYPE(FuBcm57xxDictImage, fu_bcm57xx_dict_image, FU, BCM57XX_DICT_IMAGE, FuFirmware) + +FuFirmware * +fu_bcm57xx_dict_image_new(void); +void +fu_bcm57xx_dict_image_set_kind(FuBcm57xxDictImage *self, guint8 kind); +guint8 +fu_bcm57xx_dict_image_get_kind(FuBcm57xxDictImage *self); +void +fu_bcm57xx_dict_image_set_target(FuBcm57xxDictImage *self, guint8 target); +guint8 +fu_bcm57xx_dict_image_get_target(FuBcm57xxDictImage *self); diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-firmware.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..95cac172bea7cc80e2253b8518cd9c8dce168659 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-firmware.c @@ -0,0 +1,667 @@ +/* + * Copyright (C) 2018 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-dict-image.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-stage1-image.h" +#include "fu-bcm57xx-stage2-image.h" + +struct _FuBcm57xxFirmware { + FuFirmware parent_instance; + guint16 vendor; + guint16 model; + gboolean is_backup; + guint32 phys_addr; + gsize source_size; + guint8 source_padchar; +}; + +G_DEFINE_TYPE(FuBcm57xxFirmware, fu_bcm57xx_firmware, FU_TYPE_FIRMWARE) + +#define BCM_STAGE1_HEADER_MAGIC_BROADCOM 0x0E000E03 +#define BCM_STAGE1_HEADER_MAGIC_MEKLORT 0x3C1D0800 + +#define BCM_APE_HEADER_MAGIC 0x1A4D4342 + +#define BCM_CODE_DIRECTORY_ADDR_APE 0x07 + +static void +fu_bcm57xx_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "vendor", self->vendor); + fu_xmlb_builder_insert_kx(bn, "model", self->model); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kb(bn, "is_backup", self->is_backup); + fu_xmlb_builder_insert_kx(bn, "phys_addr", self->phys_addr); + } +} + +static gboolean +fu_bcm57xx_firmware_parse_header(FuBcm57xxFirmware *self, GBytes *fw, GError **error) +{ + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* verify magic and CRC */ + if (!fu_bcm57xx_verify_magic(fw, 0x0, error)) + return FALSE; + if (!fu_bcm57xx_verify_crc(fw, error)) + return FALSE; + + /* get address */ + return fu_memread_uint32_safe(buf, + bufsz, + BCM_NVRAM_HEADER_PHYS_ADDR, + &self->phys_addr, + G_BIG_ENDIAN, + error); +} + +static FuFirmware * +fu_bcm57xx_firmware_parse_info(FuBcm57xxFirmware *self, GBytes *fw, GError **error) +{ + gsize bufsz = 0x0; + guint32 mac_addr0 = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) img = fu_firmware_new_from_bytes(fw); + + /* if the MAC is set non-zero this is an actual backup rather than a container */ + if (!fu_memread_uint32_safe(buf, + bufsz, + BCM_NVRAM_INFO_MAC_ADDR0, + &mac_addr0, + G_BIG_ENDIAN, + error)) + return NULL; + self->is_backup = mac_addr0 != 0x0 && mac_addr0 != 0xffffffff; + + /* read vendor + model */ + if (!fu_memread_uint16_safe(buf, + bufsz, + BCM_NVRAM_INFO_VENDOR, + &self->vendor, + G_BIG_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint16_safe(buf, + bufsz, + BCM_NVRAM_INFO_DEVICE, + &self->model, + G_BIG_ENDIAN, + error)) + return NULL; + + /* success */ + fu_firmware_set_id(img, "info"); + return g_steal_pointer(&img); +} + +static FuFirmware * +fu_bcm57xx_firmware_parse_stage1(FuBcm57xxFirmware *self, + GBytes *fw, + guint32 *out_stage1_sz, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + guint32 stage1_wrds = 0; + guint32 stage1_sz; + guint32 stage1_off = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) img = fu_bcm57xx_stage1_image_new(); + g_autoptr(GBytes) blob = NULL; + + if (!fu_memread_uint32_safe(buf, + bufsz, + BCM_NVRAM_HEADER_BASE + BCM_NVRAM_HEADER_SIZE_WRDS, + &stage1_wrds, + G_BIG_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + BCM_NVRAM_HEADER_BASE + BCM_NVRAM_HEADER_OFFSET, + &stage1_off, + G_BIG_ENDIAN, + error)) + return NULL; + stage1_sz = (stage1_wrds * sizeof(guint32)); + if (stage1_off != BCM_NVRAM_STAGE1_BASE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "stage1 offset invalid, got: 0x%x, expected 0x%x", + (guint)stage1_sz, + (guint)BCM_NVRAM_STAGE1_BASE); + return NULL; + } + if (stage1_off + stage1_sz > bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bigger than firmware, got: 0x%x @ 0x%x", + (guint)stage1_sz, + (guint)stage1_off); + return NULL; + } + + /* verify CRC */ + blob = fu_bytes_new_offset(fw, stage1_off, stage1_sz, error); + if (blob == NULL) + return NULL; + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return NULL; + + /* needed for stage2 */ + if (out_stage1_sz != NULL) + *out_stage1_sz = stage1_sz; + + /* success */ + fu_firmware_set_id(img, "stage1"); + fu_firmware_set_offset(img, stage1_off); + return g_steal_pointer(&img); +} + +static FuFirmware * +fu_bcm57xx_firmware_parse_stage2(FuBcm57xxFirmware *self, + GBytes *fw, + guint32 stage1_sz, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + guint32 stage2_off = 0; + guint32 stage2_sz = 0; + g_autoptr(FuFirmware) img = fu_bcm57xx_stage2_image_new(); + g_autoptr(GBytes) blob = NULL; + + stage2_off = BCM_NVRAM_STAGE1_BASE + stage1_sz; + if (!fu_bcm57xx_verify_magic(fw, stage2_off, error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + stage2_off + sizeof(guint32), + &stage2_sz, + G_BIG_ENDIAN, + error)) + return NULL; + if (stage2_off + stage2_sz > bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bigger than firmware, got: 0x%x @ 0x%x", + (guint)stage2_sz, + (guint)stage2_off); + return NULL; + } + + /* verify CRC */ + blob = fu_bytes_new_offset(fw, stage2_off + 0x8, stage2_sz, error); + if (blob == NULL) + return NULL; + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return NULL; + + /* success */ + fu_firmware_set_id(img, "stage2"); + fu_firmware_set_offset(img, stage2_off); + return g_steal_pointer(&img); +} + +static gboolean +fu_bcm57xx_firmware_parse_dict(FuBcm57xxFirmware *self, + GBytes *fw, + guint idx, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + guint32 dict_addr = 0x0; + guint32 dict_info = 0x0; + guint32 dict_off = 0x0; + guint32 dict_sz; + guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * BCM_NVRAM_DIRECTORY_SZ); + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new(); + g_autoptr(GBytes) blob = NULL; + + /* header */ + if (!fu_memread_uint32_safe(buf, + bufsz, + base + BCM_NVRAM_DIRECTORY_ADDR, + &dict_addr, + G_BIG_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + base + BCM_NVRAM_DIRECTORY_SIZE_WRDS, + &dict_info, + G_BIG_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + base + BCM_NVRAM_DIRECTORY_OFFSET, + &dict_off, + G_BIG_ENDIAN, + error)) + return FALSE; + + /* no dict stored */ + if (dict_addr == 0 && dict_info == 0 && dict_off == 0) + return TRUE; + + dict_sz = + (dict_info & 0x00FFFFFF) * sizeof(guint32); /* implies that maximum size is 16 MB */ + fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img), + (dict_info & 0x0F000000) >> 24); + fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), (dict_info & 0xF0000000) >> 28); + fu_firmware_set_addr(img, dict_addr); + fu_firmware_set_offset(img, dict_off); + fu_firmware_set_idx(img, 0x80 + idx); + + /* empty */ + if (dict_sz == 0) { + blob = g_bytes_new(NULL, 0); + fu_firmware_set_bytes(img, blob); + fu_firmware_add_image(FU_FIRMWARE(self), img); + return TRUE; + } + + /* check against image size */ + if (dict_off + dict_sz > bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "bigger than firmware, got: 0x%x @ 0x%x", + (guint)dict_sz, + (guint)dict_off); + return FALSE; + } + blob = fu_bytes_new_offset(fw, dict_off, dict_sz, error); + if (blob == NULL) + return FALSE; + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + + /* success */ + fu_firmware_add_image(FU_FIRMWARE(self), img); + return TRUE; +} + +static gboolean +fu_bcm57xx_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + 0x0, + &magic, + G_BIG_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != BCM_APE_HEADER_MAGIC && magic != BCM_STAGE1_HEADER_MAGIC_BROADCOM && + magic != BCM_STAGE1_HEADER_MAGIC_MEKLORT && magic != BCM_NVRAM_MAGIC) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "file not supported, got: 0x%08X", + magic); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware); + gsize bufsz = 0x0; + guint32 magic = 0; + guint32 stage1_sz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) img_info2 = NULL; + g_autoptr(FuFirmware) img_info = NULL; + g_autoptr(FuFirmware) img_stage1 = NULL; + g_autoptr(FuFirmware) img_stage2 = NULL; + g_autoptr(FuFirmware) img_vpd = NULL; + g_autoptr(GBytes) blob_header = NULL; + g_autoptr(GBytes) blob_info2 = NULL; + g_autoptr(GBytes) blob_info = NULL; + g_autoptr(GBytes) blob_vpd = NULL; + + /* try to autodetect the file type */ + if (!fu_memread_uint32_safe(buf, bufsz, 0x0, &magic, G_BIG_ENDIAN, error)) + return FALSE; + + /* standalone APE */ + if (magic == BCM_APE_HEADER_MAGIC) { + g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new(); + fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img), 0xD); + fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), 0x0); + fu_firmware_set_addr(img, BCM_CODE_DIRECTORY_ADDR_APE); + fu_firmware_set_id(img, "ape"); + fu_firmware_add_image(firmware, img); + return TRUE; + } + + /* standalone stage1 */ + if (magic == BCM_STAGE1_HEADER_MAGIC_BROADCOM || magic == BCM_STAGE1_HEADER_MAGIC_MEKLORT) { + img_stage1 = fu_firmware_new_from_bytes(fw); + fu_firmware_set_id(img_stage1, "stage1"); + fu_firmware_add_image(firmware, img_stage1); + return TRUE; + } + + /* not full NVRAM image */ + if (magic != BCM_NVRAM_MAGIC) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "file not supported, got: 0x%08X", + magic); + return FALSE; + } + + /* save the size so we can export the padding for a perfect roundtrip */ + self->source_size = bufsz; + self->source_padchar = buf[bufsz - 1]; + + /* NVRAM header */ + blob_header = fu_bytes_new_offset(fw, BCM_NVRAM_HEADER_BASE, BCM_NVRAM_HEADER_SZ, error); + if (blob_header == NULL) + return FALSE; + if (!fu_bcm57xx_firmware_parse_header(self, blob_header, error)) { + g_prefix_error(error, "failed to parse header: "); + return FALSE; + } + + /* info */ + blob_info = fu_bytes_new_offset(fw, BCM_NVRAM_INFO_BASE, BCM_NVRAM_INFO_SZ, error); + if (blob_info == NULL) + return FALSE; + img_info = fu_bcm57xx_firmware_parse_info(self, blob_info, error); + if (img_info == NULL) { + g_prefix_error(error, "failed to parse info: "); + return FALSE; + } + fu_firmware_set_offset(img_info, BCM_NVRAM_INFO_BASE); + fu_firmware_add_image(firmware, img_info); + + /* VPD */ + blob_vpd = fu_bytes_new_offset(fw, BCM_NVRAM_VPD_BASE, BCM_NVRAM_VPD_SZ, error); + if (blob_vpd == NULL) + return FALSE; + img_vpd = fu_firmware_new_from_bytes(blob_vpd); + fu_firmware_set_id(img_vpd, "vpd"); + fu_firmware_set_offset(img_vpd, BCM_NVRAM_VPD_BASE); + fu_firmware_add_image(firmware, img_vpd); + + /* info2 */ + blob_info2 = fu_bytes_new_offset(fw, BCM_NVRAM_INFO2_BASE, BCM_NVRAM_INFO2_SZ, error); + if (blob_info2 == NULL) + return FALSE; + img_info2 = fu_firmware_new_from_bytes(blob_info2); + fu_firmware_set_id(img_info2, "info2"); + fu_firmware_set_offset(img_info2, BCM_NVRAM_INFO2_BASE); + fu_firmware_add_image(firmware, img_info2); + + /* stage1 */ + img_stage1 = fu_bcm57xx_firmware_parse_stage1(self, fw, &stage1_sz, flags, error); + if (img_stage1 == NULL) { + g_prefix_error(error, "failed to parse stage1: "); + return FALSE; + } + fu_firmware_add_image(firmware, img_stage1); + + /* stage2 */ + img_stage2 = fu_bcm57xx_firmware_parse_stage2(self, fw, stage1_sz, flags, error); + if (img_stage2 == NULL) { + g_prefix_error(error, "failed to parse stage2: "); + return FALSE; + } + fu_firmware_add_image(firmware, img_stage2); + + /* dictionaries, e.g. APE */ + for (guint i = 0; i < 8; i++) { + if (!fu_bcm57xx_firmware_parse_dict(self, fw, i, flags, error)) { + g_prefix_error(error, "failed to parse dict 0x%x: ", i); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static GBytes * +_g_bytes_new_sized(gsize sz) +{ + GByteArray *tmp = g_byte_array_sized_new(sz); + for (gsize i = 0; i < sz; i++) + fu_byte_array_append_uint8(tmp, 0x0); + return g_byte_array_free_to_bytes(tmp); +} + +static gboolean +fu_bcm57xx_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware); + guint64 tmp; + + /* two simple properties */ + tmp = xb_node_query_text_as_uint(n, "vendor", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->vendor = tmp; + tmp = xb_node_query_text_as_uint(n, "model", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->model = tmp; + + /* success */ + return TRUE; +} + +static GBytes * +fu_bcm57xx_firmware_write(FuFirmware *firmware, GError **error) +{ + gsize off = BCM_NVRAM_STAGE1_BASE; + FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware); + g_autoptr(GByteArray) buf = g_byte_array_sized_new(self->source_size); + g_autoptr(FuFirmware) img_info2 = NULL; + g_autoptr(FuFirmware) img_info = NULL; + g_autoptr(FuFirmware) img_stage1 = NULL; + g_autoptr(FuFirmware) img_stage2 = NULL; + g_autoptr(FuFirmware) img_vpd = NULL; + g_autoptr(GBytes) blob_info2 = NULL; + g_autoptr(GBytes) blob_info = NULL; + g_autoptr(GBytes) blob_stage1 = NULL; + g_autoptr(GBytes) blob_stage2 = NULL; + g_autoptr(GBytes) blob_vpd = NULL; + g_autoptr(GPtrArray) blob_dicts = NULL; + + /* write out the things we need to pre-compute */ + img_stage1 = fu_firmware_get_image_by_id(firmware, "stage1", error); + if (img_stage1 == NULL) + return NULL; + blob_stage1 = fu_firmware_write(img_stage1, error); + if (blob_stage1 == NULL) + return NULL; + off += g_bytes_get_size(blob_stage1); + img_stage2 = fu_firmware_get_image_by_id(firmware, "stage2", error); + if (img_stage2 == NULL) + return NULL; + blob_stage2 = fu_firmware_write(img_stage2, error); + if (blob_stage2 == NULL) + return NULL; + off += g_bytes_get_size(blob_stage2); + + /* add header */ + fu_byte_array_append_uint32(buf, BCM_NVRAM_MAGIC, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, self->phys_addr, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, + g_bytes_get_size(blob_stage1) / sizeof(guint32), + G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, BCM_NVRAM_STAGE1_BASE, G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf, + fu_bcm57xx_nvram_crc(buf->data, buf->len), + G_LITTLE_ENDIAN); + + /* add directory entries */ + blob_dicts = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + for (guint i = 0; i < 8; i++) { + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) blob = NULL; + + img = fu_firmware_get_image_by_idx(firmware, 0x80 + i, NULL); + if (img != NULL) { + blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + } + if (blob != NULL) { + fu_byte_array_append_uint32(buf, fu_firmware_get_addr(img), G_BIG_ENDIAN); + fu_byte_array_append_uint32( + buf, + (g_bytes_get_size(blob) / sizeof(guint32)) | + (guint32)fu_bcm57xx_dict_image_get_target( + FU_BCM57XX_DICT_IMAGE(img)) + << 24 | + (guint32)fu_bcm57xx_dict_image_get_kind(FU_BCM57XX_DICT_IMAGE(img)) + << 28, + G_BIG_ENDIAN); + if (g_bytes_get_size(blob) > 0) { + fu_byte_array_append_uint32(buf, off, G_BIG_ENDIAN); + off += g_bytes_get_size(blob); + } else { + fu_byte_array_append_uint32(buf, 0x0, G_BIG_ENDIAN); + } + } else { + blob = g_bytes_new(NULL, 0); + for (guint32 j = 0; j < sizeof(guint32) * 3; j++) + fu_byte_array_append_uint8(buf, 0x0); + } + g_ptr_array_add(blob_dicts, g_steal_pointer(&blob)); + } + + /* add info */ + img_info = fu_firmware_get_image_by_id(firmware, "info", NULL); + if (img_info != NULL) { + blob_info = fu_firmware_write(img_info, error); + if (blob_info == NULL) + return NULL; + } else { + GByteArray *tmp = g_byte_array_sized_new(BCM_NVRAM_INFO_SZ); + for (gsize i = 0; i < BCM_NVRAM_INFO_SZ; i++) + fu_byte_array_append_uint8(tmp, 0x0); + fu_memwrite_uint16(tmp->data + BCM_NVRAM_INFO_VENDOR, self->vendor, G_BIG_ENDIAN); + fu_memwrite_uint16(tmp->data + BCM_NVRAM_INFO_DEVICE, self->model, G_BIG_ENDIAN); + blob_info = g_byte_array_free_to_bytes(tmp); + } + fu_byte_array_append_bytes(buf, blob_info); + + /* add vpd */ + img_vpd = fu_firmware_get_image_by_id(firmware, "vpd", NULL); + if (img_vpd != NULL) { + blob_vpd = fu_firmware_write(img_vpd, error); + if (blob_vpd == NULL) + return NULL; + } else { + blob_vpd = _g_bytes_new_sized(BCM_NVRAM_VPD_SZ); + } + fu_byte_array_append_bytes(buf, blob_vpd); + + /* add info2 */ + img_info2 = fu_firmware_get_image_by_id(firmware, "info2", NULL); + if (img_info2 != NULL) { + blob_info2 = fu_firmware_write(img_info2, error); + if (blob_info2 == NULL) + return NULL; + } else { + blob_info2 = _g_bytes_new_sized(BCM_NVRAM_INFO2_SZ); + } + fu_byte_array_append_bytes(buf, blob_info2); + + /* add stage1+2 */ + fu_byte_array_append_bytes(buf, blob_stage1); + fu_byte_array_append_bytes(buf, blob_stage2); + + /* add dictionaries, e.g. APE */ + for (guint i = 0; i < blob_dicts->len; i++) { + GBytes *blob = g_ptr_array_index(blob_dicts, i); + fu_byte_array_append_bytes(buf, blob); + } + + /* pad until full */ + for (guint32 i = buf->len; i < self->source_size; i++) + fu_byte_array_append_uint8(buf, self->source_padchar); + + /* add EOF */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +guint16 +fu_bcm57xx_firmware_get_vendor(FuBcm57xxFirmware *self) +{ + return self->vendor; +} + +guint16 +fu_bcm57xx_firmware_get_model(FuBcm57xxFirmware *self) +{ + return self->model; +} + +gboolean +fu_bcm57xx_firmware_is_backup(FuBcm57xxFirmware *self) +{ + return self->is_backup; +} + +static void +fu_bcm57xx_firmware_init(FuBcm57xxFirmware *self) +{ + self->phys_addr = BCM_PHYS_ADDR_DEFAULT; + self->source_size = BCM_FIRMWARE_SIZE; + self->source_padchar = 0xff; + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_bcm57xx_firmware_class_init(FuBcm57xxFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_bcm57xx_firmware_check_magic; + klass_firmware->parse = fu_bcm57xx_firmware_parse; + klass_firmware->export = fu_bcm57xx_firmware_export; + klass_firmware->write = fu_bcm57xx_firmware_write; + klass_firmware->build = fu_bcm57xx_firmware_build; +} + +FuFirmware * +fu_bcm57xx_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_BCM57XX_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-firmware.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..f7397afb2a0716bbf5d846286a8d8ea8b0b3fb5f --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-firmware.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_BCM57XX_FIRMWARE (fu_bcm57xx_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuBcm57xxFirmware, fu_bcm57xx_firmware, FU, BCM57XX_FIRMWARE, FuFirmware) + +FuFirmware * +fu_bcm57xx_firmware_new(void); +guint16 +fu_bcm57xx_firmware_get_vendor(FuBcm57xxFirmware *self); +guint16 +fu_bcm57xx_firmware_get_model(FuBcm57xxFirmware *self); +gboolean +fu_bcm57xx_firmware_is_backup(FuBcm57xxFirmware *self); diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-plugin.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..403bcf0b6009dbecf43ba213e1a8f3548326b1b6 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-plugin.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-bcm57xx-device.h" +#include "fu-bcm57xx-dict-image.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-plugin.h" +#include "fu-bcm57xx-stage1-image.h" +#include "fu-bcm57xx-stage2-image.h" + +struct _FuBcm57XxPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuBcm57XxPlugin, fu_bcm57xx_plugin, FU_TYPE_PLUGIN) + +static void +fu_bcm57xx_plugin_init(FuBcm57XxPlugin *self) +{ +} + +static void +fu_bcm57xx_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "bcm57xx"); + fu_plugin_add_udev_subsystem(plugin, "pci"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_BCM57XX_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_FIRMWARE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_DICT_IMAGE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_STAGE1_IMAGE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_STAGE2_IMAGE); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_BETTER_THAN, "optionrom"); +} + +static void +fu_bcm57xx_plugin_class_init(FuBcm57XxPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_bcm57xx_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-plugin.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..bc1379e16792f3d417c2e1f97962aad2da674b47 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuBcm57XxPlugin, fu_bcm57xx_plugin, FU, BCM57XX_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-recovery-device.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-recovery-device.c new file mode 100644 index 0000000000000000000000000000000000000000..ef150cfed6df598d7dda39ae649957d8a3d3f3f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-recovery-device.c @@ -0,0 +1,909 @@ +/* + * Copyright (C) 2018 Evan Lojewski + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_MMAN_H +#include +#endif + +#ifdef HAVE_VALGRIND +#include +#endif /* HAVE_VALGRIND */ + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-recovery-device.h" + +/* offsets into BAR[0] */ +#define REG_DEVICE_PCI_VENDOR_DEVICE_ID 0x6434 +#define REG_NVM_SOFTWARE_ARBITRATION 0x7020 +#define REG_NVM_ACCESS 0x7024 +#define REG_NVM_COMMAND 0x7000 +#define REG_NVM_ADDR 0x700c +#define REG_NVM_READ 0x7010 +#define REG_NVM_WRITE 0x7008 + +/* offsets into BAR[2] */ +#define REG_APE_MODE 0x0 + +typedef struct { + guint8 *buf; + gsize bufsz; +} FuBcm57xxMmap; + +#define FU_BCM57XX_BAR_DEVICE 0 +#define FU_BCM57XX_BAR_APE 1 +#define FU_BCM57XX_BAR_MAX 3 + +struct _FuBcm57xxRecoveryDevice { + FuUdevDevice parent_instance; + FuBcm57xxMmap bar[FU_BCM57XX_BAR_MAX]; +}; + +typedef union { + guint32 r32; + struct { + guint32 reserved_0_0 : 1; + guint32 Reset : 1; + guint32 reserved_2_2 : 1; + guint32 Done : 1; + guint32 Doit : 1; + guint32 Wr : 1; + guint32 Erase : 1; + guint32 First : 1; + guint32 Last : 1; + guint32 reserved_15_9 : 7; + guint32 WriteEnableCommand : 1; + guint32 WriteDisableCommand : 1; + guint32 reserved_31_18 : 14; + } __attribute__((packed)) bits; +} BcmRegNVMCommand; + +typedef union { + guint32 r32; + struct { + guint32 ReqSet0 : 1; + guint32 ReqSet1 : 1; + guint32 ReqSet2 : 1; + guint32 ReqSet3 : 1; + guint32 ReqClr0 : 1; + guint32 ReqClr1 : 1; + guint32 ReqClr2 : 1; + guint32 ReqClr3 : 1; + guint32 ArbWon0 : 1; + guint32 ArbWon1 : 1; + guint32 ArbWon2 : 1; + guint32 ArbWon3 : 1; + guint32 Req0 : 1; + guint32 Req1 : 1; + guint32 Req2 : 1; + guint32 Req3 : 1; + guint32 reserved_31_16 : 16; + } __attribute__((packed)) bits; +} BcmRegNVMSoftwareArbitration; + +typedef union { + guint32 r32; + struct { + guint32 Enable : 1; + guint32 WriteEnable : 1; + guint32 reserved_31_2 : 30; + } __attribute__((packed)) bits; +} BcmRegNVMAccess; + +typedef union { + guint32 r32; + struct { + guint32 Reset : 1; + guint32 Halt : 1; + guint32 FastBoot : 1; + guint32 HostDiag : 1; + guint32 reserved_4_4 : 1; + guint32 Event1 : 1; + guint32 Event2 : 1; + guint32 GRCint : 1; + guint32 reserved_8_8 : 1; + guint32 SwapATBdword : 1; + guint32 reserved_10_10 : 1; + guint32 SwapARBdword : 1; + guint32 reserved_13_12 : 2; + guint32 Channel0Enable : 1; + guint32 Channel2Enable : 1; + guint32 reserved_17_16 : 2; + guint32 MemoryECC : 1; + guint32 ICodePIPRdDisable : 1; + guint32 reserved_29_20 : 10; + guint32 Channel1Enable : 1; + guint32 Channel3Enable : 1; + } __attribute__((packed)) bits; +} BcmRegAPEMode; + +G_DEFINE_TYPE(FuBcm57xxRecoveryDevice, fu_bcm57xx_recovery_device, FU_TYPE_UDEV_DEVICE) + +#ifdef __ppc64__ +#define BARRIER() __asm__ volatile("sync 0\neieio\n" : : : "memory") +#else +#define BARRIER() __asm__ volatile("" : : : "memory"); +#endif + +static gboolean +fu_bcm57xx_recovery_device_bar_read(FuBcm57xxRecoveryDevice *self, + guint bar, + gsize offset, + guint32 *val, + GError **error) +{ + /* this should never happen */ + if (self->bar[bar].buf == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "BAR[%u] is not mapped!", + bar); + return FALSE; + } + + BARRIER(); + return fu_memcpy_safe((guint8 *)val, + sizeof(*val), + 0x0, /* dst */ + self->bar[bar].buf, + self->bar[bar].bufsz, + offset, + sizeof(*val), + error); +} + +static gboolean +fu_bcm57xx_recovery_device_bar_write(FuBcm57xxRecoveryDevice *self, + guint bar, + gsize offset, + guint32 val, + GError **error) +{ + /* this should never happen */ + if (self->bar[bar].buf == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "BAR[%u] is not mapped!", + bar); + return FALSE; + } + + BARRIER(); + if (!fu_memcpy_safe(self->bar[bar].buf, + self->bar[bar].bufsz, + offset, /* dst */ + (const guint8 *)&val, + sizeof(val), + 0x0, /* src */ + sizeof(val), + error)) + return FALSE; + BARRIER(); + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_disable(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMAccess tmp; + if (!fu_bcm57xx_recovery_device_bar_read(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, + &tmp.r32, + error)) + return FALSE; + tmp.bits.Enable = FALSE; + tmp.bits.WriteEnable = FALSE; + return fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, + tmp.r32, + error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_enable(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMAccess tmp; + if (!fu_bcm57xx_recovery_device_bar_read(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, + &tmp.r32, + error)) + return FALSE; + tmp.bits.Enable = TRUE; + tmp.bits.WriteEnable = FALSE; + return fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, + tmp.r32, + error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_enable_write(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMAccess tmp; + if (!fu_bcm57xx_recovery_device_bar_read(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, + &tmp.r32, + error)) + return FALSE; + tmp.bits.Enable = TRUE; + tmp.bits.WriteEnable = TRUE; + return fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ACCESS, + tmp.r32, + error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_acquire_lock(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMSoftwareArbitration tmp = {0}; + g_autoptr(GTimer) timer = g_timer_new(); + tmp.bits.ReqSet1 = 1; + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_SOFTWARE_ARBITRATION, + tmp.r32, + error)) + return FALSE; + do { + if (!fu_bcm57xx_recovery_device_bar_read(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_SOFTWARE_ARBITRATION, + &tmp.r32, + error)) + return FALSE; + if (tmp.bits.ArbWon1) + return TRUE; + if (g_timer_elapsed(timer, NULL) > 0.2) + break; + } while (TRUE); + + /* timed out */ + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "timed out trying to acquire lock #1"); + return FALSE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_release_lock(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMSoftwareArbitration tmp = {0}; + tmp.r32 = 0; + tmp.bits.ReqClr1 = 1; + return fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_SOFTWARE_ARBITRATION, + tmp.r32, + error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_wait_done(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMCommand tmp = {0}; + g_autoptr(GTimer) timer = g_timer_new(); + do { + if (!fu_bcm57xx_recovery_device_bar_read(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, + &tmp.r32, + error)) + return FALSE; + if (tmp.bits.Done) + return TRUE; + if (g_timer_elapsed(timer, NULL) > 0.2) + break; + } while (TRUE); + + /* timed out */ + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "timed out"); + return FALSE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_clear_done(FuBcm57xxRecoveryDevice *self, GError **error) +{ + BcmRegNVMCommand tmp = {0}; + tmp.bits.Done = 1; + return fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, + tmp.r32, + error); +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_read(FuBcm57xxRecoveryDevice *self, + guint32 address, + guint32 *buf, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, bufsz); + for (guint i = 0; i < bufsz; i++) { + BcmRegNVMCommand tmp = {0}; + guint32 val32 = 0; + if (!fu_bcm57xx_recovery_device_nvram_clear_done(self, error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ADDR, + address, + error)) + return FALSE; + tmp.bits.Doit = 1; + tmp.bits.First = i == 0; + tmp.bits.Last = i == bufsz - 1; + + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, + tmp.r32, + error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_nvram_wait_done(self, error)) { + g_prefix_error(error, "failed to read @0x%x: ", address); + return FALSE; + } + if (!fu_bcm57xx_recovery_device_bar_read(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_READ, + &val32, + error)) + return FALSE; + buf[i] = GUINT32_FROM_BE(val32); + address += sizeof(guint32); + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_nvram_write(FuBcm57xxRecoveryDevice *self, + guint32 address, + const guint32 *buf, + gsize bufsz_dwrds, + FuProgress *progress, + GError **error) +{ + const guint32 page_size_dwrds = 64; + + /* can only write in pages of 64 dwords */ + if (bufsz_dwrds % page_size_dwrds != 0 || + (address * sizeof(guint32)) % page_size_dwrds != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "can only write aligned with page size 0x%x", + page_size_dwrds); + return FALSE; + } + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, bufsz_dwrds); + for (guint i = 0; i < bufsz_dwrds; i++) { + BcmRegNVMCommand tmp = {0}; + if (!fu_bcm57xx_recovery_device_nvram_clear_done(self, error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_WRITE, + GUINT32_TO_BE(buf[i]), + error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_ADDR, + address, + error)) + return FALSE; + tmp.bits.Wr = TRUE; + tmp.bits.Doit = TRUE; + tmp.bits.First = i % page_size_dwrds == 0; + tmp.bits.Last = (i + 1) % page_size_dwrds == 0; + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_DEVICE, + REG_NVM_COMMAND, + tmp.r32, + error)) + return FALSE; + if (!fu_bcm57xx_recovery_device_nvram_wait_done(self, error)) { + g_prefix_error(error, "failed to write @0x%x: ", address); + return FALSE; + } + address += sizeof(guint32); + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + /* unbind tg3 */ + return fu_device_unbind_driver(device, error); +} + +static gboolean +fu_bcm57xx_recovery_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* bind tg3, which might fail if the module is not compiled */ + if (!fu_device_bind_driver(device, "pci", "tg3", &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning("failed to bind tg3: %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to bind tg3: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_activate(FuDevice *device, FuProgress *progress, GError **error) +{ + BcmRegAPEMode mode = {0}; + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); + + /* halt */ + mode.bits.Halt = 1; + mode.bits.FastBoot = 0; + if (!fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_APE, + REG_APE_MODE, + mode.r32, + error)) + return FALSE; + + /* boot */ + mode.bits.Halt = 0; + mode.bits.FastBoot = 0; + mode.bits.Reset = 1; + return fu_bcm57xx_recovery_device_bar_write(self, + FU_BCM57XX_BAR_APE, + REG_APE_MODE, + mode.r32, + error); +} + +static GBytes * +fu_bcm57xx_recovery_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); + gsize bufsz_dwrds = fu_device_get_firmware_size_max(FU_DEVICE(self)) / sizeof(guint32); + g_autofree guint32 *buf_dwrds = g_new0(guint32, bufsz_dwrds); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + + /* read from hardware */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + locker = fu_device_locker_new_full( + self, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_acquire_lock, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_release_lock, + error); + if (locker == NULL) + return NULL; + locker2 = + fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_enable, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_disable, + error); + if (locker2 == NULL) + return NULL; + if (!fu_bcm57xx_recovery_device_nvram_read(self, + 0x0, + buf_dwrds, + bufsz_dwrds, + progress, + error)) + return NULL; + if (!fu_device_locker_close(locker2, error)) + return NULL; + return g_bytes_new(buf_dwrds, bufsz_dwrds * sizeof(guint32)); +} + +static FuFirmware * +fu_bcm57xx_recovery_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware_bin = fu_firmware_new(); + g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new(); + + /* check is a NVRAM backup */ + if (!fu_firmware_parse(firmware_tmp, fw, flags, error)) { + g_prefix_error(error, "failed to parse new firmware: "); + return NULL; + } + if (!fu_bcm57xx_firmware_is_backup(FU_BCM57XX_FIRMWARE(firmware_tmp))) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "can only recover with backup firmware"); + return NULL; + } + if (!fu_firmware_parse(firmware_bin, fw, flags, error)) + return NULL; + return g_steal_pointer(&firmware_bin); +} + +static gboolean +fu_bcm57xx_recovery_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); + const guint8 *buf; + gsize bufsz = 0; + gsize bufsz_dwrds; + g_autofree guint32 *buf_dwrds = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + g_autoptr(GBytes) blob = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, NULL); + + /* build the images into one linear blob of the correct size */ + blob = fu_firmware_write(firmware, error); + if (blob == NULL) + return FALSE; + fu_progress_step_done(progress); + + /* align into uint32_t buffer */ + buf = g_bytes_get_data(blob, &bufsz); + bufsz_dwrds = bufsz / sizeof(guint32); + buf_dwrds = g_new0(guint32, bufsz_dwrds); + if (!fu_memcpy_safe((guint8 *)buf_dwrds, + bufsz_dwrds * sizeof(guint32), + 0x0, /* dst */ + buf, + bufsz, + 0x0, /* src */ + bufsz, + error)) + return FALSE; + + /* hit hardware */ + locker = fu_device_locker_new_full( + self, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_acquire_lock, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_release_lock, + error); + if (locker == NULL) + return FALSE; + locker2 = fu_device_locker_new_full( + self, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_enable_write, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_disable, + error); + if (locker2 == NULL) + return FALSE; + if (!fu_bcm57xx_recovery_device_nvram_write(self, + 0x0, + buf_dwrds, + bufsz_dwrds, + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_device_locker_close(locker2, error)) + return FALSE; + if (!fu_device_locker_close(locker, error)) + return FALSE; + fu_progress_step_done(progress); + + /* reset APE */ + if (!fu_device_activate(device, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_setup(FuDevice *device, GError **error) +{ + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); + guint32 fwversion = 0; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) locker2 = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "enable"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 80, "nvram"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "veraddr"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "version"); + + locker = fu_device_locker_new_full( + self, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_acquire_lock, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_release_lock, + error); + if (locker == NULL) + return FALSE; + locker2 = + fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_enable, + (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_disable, + error); + if (locker2 == NULL) + return FALSE; + fu_progress_step_done(progress); + + /* get NVRAM version */ + if (!fu_bcm57xx_recovery_device_nvram_read(self, + BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION, + &fwversion, + 1, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + if (fwversion != 0x0) { + g_autofree gchar *fwversion_str = NULL; + + /* this is only set on the OSS firmware */ + fwversion_str = fu_version_from_uint32(GUINT32_FROM_BE(fwversion), + FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, fwversion_str); + fu_device_set_version_raw(device, fwversion); + fu_device_set_branch(device, BCM_FW_BRANCH_OSS_FIRMWARE); + fu_progress_step_done(progress); + fu_progress_step_done(progress); + } else { + guint32 bufver[4] = {0x0}; + guint32 veraddr = 0; + g_autoptr(Bcm57xxVeritem) veritem = NULL; + + /* fall back to the string, e.g. '5719-v1.43' */ + if (!fu_bcm57xx_recovery_device_nvram_read(self, + BCM_NVRAM_STAGE1_BASE + + BCM_NVRAM_STAGE1_VERADDR, + &veraddr, + 1, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + veraddr = GUINT32_FROM_BE(veraddr); + if (veraddr > BCM_PHYS_ADDR_DEFAULT) + veraddr -= BCM_PHYS_ADDR_DEFAULT; + if (!fu_bcm57xx_recovery_device_nvram_read(self, + BCM_NVRAM_STAGE1_BASE + veraddr, + bufver, + 4, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + veritem = fu_bcm57xx_veritem_new((guint8 *)bufver, sizeof(bufver)); + if (veritem != NULL) { + fu_device_set_version(device, veritem->version); + fu_device_set_branch(device, veritem->branch); + fu_device_set_version_format(device, veritem->verfmt); + } + } + + return TRUE; +} + +static gboolean +fu_bcm57xx_recovery_device_open(FuDevice *device, GError **error) +{ +#ifdef HAVE_MMAN_H + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); + FuUdevDevice *udev_device = FU_UDEV_DEVICE(device); + const gchar *sysfs_path = fu_udev_device_get_sysfs_path(udev_device); +#endif + +#ifdef RUNNING_ON_VALGRIND + /* this can't work */ + if (RUNNING_ON_VALGRIND) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot mmap'ing BARs when using valgrind"); + return FALSE; + } +#endif + +#ifdef HAVE_MMAN_H + /* map BARs */ + for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { + int memfd; + struct stat st; + g_autofree gchar *fn = NULL; + g_autofree gchar *resfn = NULL; + + /* open 64 bit resource */ + resfn = g_strdup_printf("resource%u", i * 2); + fn = g_build_filename(sysfs_path, resfn, NULL); + memfd = open(fn, O_RDWR | O_SYNC); + if (memfd < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "error opening %s", + fn); + return FALSE; + } + if (fstat(memfd, &st) < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "could not stat %s", + fn); + close(memfd); + return FALSE; + } + + /* mmap */ + if (g_getenv("FWUPD_BCM57XX_VERBOSE") != NULL) + g_debug("mapping BAR[%u] %s for 0x%x bytes", i, fn, (guint)st.st_size); + self->bar[i].buf = + (guint8 *)mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); + self->bar[i].bufsz = st.st_size; + close(memfd); + if (self->bar[i].buf == MAP_FAILED) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "could not mmap %s: %s", + fn, + strerror(errno)); + return FALSE; + } + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "mmap() not supported as sys/mman.h not available"); + return FALSE; +#endif +} + +static gboolean +fu_bcm57xx_recovery_device_close(FuDevice *device, GError **error) +{ +#ifdef HAVE_MMAN_H + FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); + + /* unmap BARs */ + for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { + if (self->bar[i].buf == NULL) + continue; + if (g_getenv("FWUPD_BCM57XX_VERBOSE") != NULL) + g_debug("unmapping BAR[%u]", i); + munmap(self->bar[i].buf, self->bar[i].bufsz); + self->bar[i].buf = NULL; + self->bar[i].bufsz = 0; + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "munmap() not supported as sys/mman.h not available"); + return FALSE; +#endif +} + +static void +fu_bcm57xx_recovery_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_bcm57xx_recovery_device_init(FuBcm57xxRecoveryDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IGNORE_VALIDATION); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(FU_DEVICE(self), "com.broadcom.bcm57xx"); + fu_device_add_icon(FU_DEVICE(self), "network-wired"); + fu_device_set_logical_id(FU_DEVICE(self), "recovery"); + + /* other values are set from a quirk */ + fu_device_set_firmware_size(FU_DEVICE(self), BCM_FIRMWARE_SIZE); + + /* no BARs mapped */ + for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { + self->bar[i].buf = NULL; + self->bar[i].bufsz = 0; + } +} + +static gboolean +fu_bcm57xx_recovery_device_probe(FuDevice *device, GError **error) +{ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error); +} + +static void +fu_bcm57xx_recovery_device_class_init(FuBcm57xxRecoveryDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->activate = fu_bcm57xx_recovery_device_activate; + klass_device->prepare_firmware = fu_bcm57xx_recovery_device_prepare_firmware; + klass_device->setup = fu_bcm57xx_recovery_device_setup; + klass_device->reload = fu_bcm57xx_recovery_device_setup; + klass_device->open = fu_bcm57xx_recovery_device_open; + klass_device->close = fu_bcm57xx_recovery_device_close; + klass_device->write_firmware = fu_bcm57xx_recovery_device_write_firmware; + klass_device->dump_firmware = fu_bcm57xx_recovery_device_dump_firmware; + klass_device->attach = fu_bcm57xx_recovery_device_attach; + klass_device->detach = fu_bcm57xx_recovery_device_detach; + klass_device->probe = fu_bcm57xx_recovery_device_probe; + klass_device->set_progress = fu_bcm57xx_recovery_device_set_progress; +} + +FuBcm57xxRecoveryDevice * +fu_bcm57xx_recovery_device_new(void) +{ + FuUdevDevice *self = g_object_new(FU_TYPE_BCM57XX_RECOVERY_DEVICE, NULL); + return FU_BCM57XX_RECOVERY_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-recovery-device.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-recovery-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5feb3c6657d17eab5610a54033e7922b5589c4ce --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-recovery-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_BCM57XX_RECOVERY_DEVICE (fu_bcm57xx_recovery_device_get_type()) +G_DECLARE_FINAL_TYPE(FuBcm57xxRecoveryDevice, + fu_bcm57xx_recovery_device, + FU, + BCM57XX_RECOVERY_DEVICE, + FuUdevDevice) + +FuBcm57xxRecoveryDevice * +fu_bcm57xx_recovery_device_new(void); diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage1-image.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage1-image.c new file mode 100644 index 0000000000000000000000000000000000000000..9eb0a0cc6a414bdbca46cd8444fb24f866497cf5 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage1-image.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-stage1-image.h" + +struct _FuBcm57xxStage1Image { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuBcm57xxStage1Image, fu_bcm57xx_stage1_image, FU_TYPE_FIRMWARE) + +static gboolean +fu_bcm57xx_stage1_image_parse(FuFirmware *image, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0x0; + guint32 fwversion = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(GBytes) fw_nocrc = NULL; + + /* verify CRC */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (!fu_bcm57xx_verify_crc(fw, error)) + return FALSE; + } + + /* get version number */ + if (!fu_memread_uint32_safe(buf, + bufsz, + BCM_NVRAM_STAGE1_VERSION, + &fwversion, + G_BIG_ENDIAN, + error)) + return FALSE; + if (fwversion != 0x0) { + g_autofree gchar *tmp = NULL; + tmp = fu_version_from_uint32(fwversion, FWUPD_VERSION_FORMAT_TRIPLET); + fu_firmware_set_version(image, tmp); + fu_firmware_set_version_raw(image, fwversion); + } else { + guint32 veraddr = 0x0; + + /* fall back to the optional string, e.g. '5719-v1.43' */ + if (!fu_memread_uint32_safe(buf, + bufsz, + BCM_NVRAM_STAGE1_VERADDR, + &veraddr, + G_BIG_ENDIAN, + error)) + return FALSE; + if (veraddr != 0x0) { + guint32 bufver[4] = {'\0'}; + g_autoptr(Bcm57xxVeritem) veritem = NULL; + if (veraddr < BCM_PHYS_ADDR_DEFAULT + sizeof(bufver)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version address 0x%x less than physical 0x%x", + veraddr, + (guint)BCM_PHYS_ADDR_DEFAULT); + return FALSE; + } + if (!fu_memcpy_safe((guint8 *)bufver, + sizeof(bufver), + 0x0, /* dst */ + buf, + bufsz, + veraddr - BCM_PHYS_ADDR_DEFAULT, /* src */ + sizeof(bufver), + error)) + return FALSE; + veritem = fu_bcm57xx_veritem_new((guint8 *)bufver, sizeof(bufver)); + if (veritem != NULL) + fu_firmware_set_version(image, veritem->version); + } + } + + fw_nocrc = fu_bytes_new_offset(fw, 0x0, g_bytes_get_size(fw) - sizeof(guint32), error); + if (fw_nocrc == NULL) + return FALSE; + fu_firmware_set_bytes(image, fw_nocrc); + return TRUE; +} + +static GBytes * +fu_bcm57xx_stage1_image_write(FuFirmware *firmware, GError **error) +{ + guint32 crc; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) fw_nocrc = NULL; + + /* sanity check */ + if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "alignment invalid, got 0x%02x", + fu_firmware_get_alignment(firmware)); + return NULL; + } + + /* the CRC-less payload */ + fw_nocrc = fu_firmware_get_bytes(firmware, error); + if (fw_nocrc == NULL) + return NULL; + + /* fuzzing, so write a header with the version */ + if (g_bytes_get_size(fw_nocrc) < BCM_NVRAM_STAGE1_VERSION) + fu_byte_array_set_size(buf, BCM_NVRAM_STAGE1_VERSION + sizeof(guint32), 0x00); + + /* payload */ + fu_byte_array_append_bytes(buf, fw_nocrc); + + /* update version */ + if (!fu_memwrite_uint32_safe(buf->data, + buf->len, + BCM_NVRAM_STAGE1_VERSION, + fu_firmware_get_version_raw(firmware), + G_BIG_ENDIAN, + error)) + return NULL; + + /* align */ + fu_byte_array_set_size( + buf, + fu_common_align_up(g_bytes_get_size(fw_nocrc), fu_firmware_get_alignment(firmware)), + 0x00); + + /* add CRC */ + crc = fu_bcm57xx_nvram_crc(buf->data, buf->len); + fu_byte_array_append_uint32(buf, crc, G_LITTLE_ENDIAN); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_bcm57xx_stage1_image_init(FuBcm57xxStage1Image *self) +{ + fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_4); +} + +static void +fu_bcm57xx_stage1_image_class_init(FuBcm57xxStage1ImageClass *klass) +{ + FuFirmwareClass *klass_image = FU_FIRMWARE_CLASS(klass); + klass_image->parse = fu_bcm57xx_stage1_image_parse; + klass_image->write = fu_bcm57xx_stage1_image_write; +} + +FuFirmware * +fu_bcm57xx_stage1_image_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_BCM57XX_STAGE1_IMAGE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage1-image.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage1-image.h new file mode 100644 index 0000000000000000000000000000000000000000..59db91679d32f5b8db955cac151683b1c5991a47 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage1-image.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_BCM57XX_STAGE1_IMAGE (fu_bcm57xx_stage1_image_get_type()) +G_DECLARE_FINAL_TYPE(FuBcm57xxStage1Image, + fu_bcm57xx_stage1_image, + FU, + BCM57XX_STAGE1_IMAGE, + FuFirmware) + +FuFirmware * +fu_bcm57xx_stage1_image_new(void); diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage2-image.c b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage2-image.c new file mode 100644 index 0000000000000000000000000000000000000000..bd4c8075c11a8e7a641c5b7d43c19328ad71bee5 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage2-image.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-stage2-image.h" + +struct _FuBcm57xxStage2Image { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuBcm57xxStage2Image, fu_bcm57xx_stage2_image, FU_TYPE_FIRMWARE) + +static gboolean +fu_bcm57xx_stage2_image_parse(FuFirmware *image, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GBytes) fw_nocrc = NULL; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (!fu_bcm57xx_verify_crc(fw, error)) + return FALSE; + } + fw_nocrc = fu_bytes_new_offset(fw, 0x0, g_bytes_get_size(fw) - sizeof(guint32), error); + if (fw_nocrc == NULL) + return FALSE; + fu_firmware_set_bytes(image, fw_nocrc); + return TRUE; +} + +static GBytes * +fu_bcm57xx_stage2_image_write(FuFirmware *image, GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + g_autoptr(GByteArray) blob = NULL; + g_autoptr(GBytes) fw_nocrc = NULL; + + /* get the CRC-less data */ + fw_nocrc = fu_firmware_get_bytes(image, error); + if (fw_nocrc == NULL) + return NULL; + + /* add to a mutable buffer */ + buf = g_bytes_get_data(fw_nocrc, &bufsz); + blob = g_byte_array_sized_new(bufsz + (sizeof(guint32) * 3)); + fu_byte_array_append_uint32(blob, BCM_NVRAM_MAGIC, G_BIG_ENDIAN); + fu_byte_array_append_uint32(blob, + g_bytes_get_size(fw_nocrc) + sizeof(guint32), + G_BIG_ENDIAN); + fu_byte_array_append_bytes(blob, fw_nocrc); + + /* add CRC */ + fu_byte_array_append_uint32(blob, fu_bcm57xx_nvram_crc(buf, bufsz), G_LITTLE_ENDIAN); + return g_byte_array_free_to_bytes(g_steal_pointer(&blob)); +} + +static void +fu_bcm57xx_stage2_image_init(FuBcm57xxStage2Image *self) +{ +} + +static void +fu_bcm57xx_stage2_image_class_init(FuBcm57xxStage2ImageClass *klass) +{ + FuFirmwareClass *klass_image = FU_FIRMWARE_CLASS(klass); + klass_image->parse = fu_bcm57xx_stage2_image_parse; + klass_image->write = fu_bcm57xx_stage2_image_write; +} + +FuFirmware * +fu_bcm57xx_stage2_image_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_BCM57XX_STAGE2_IMAGE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage2-image.h b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage2-image.h new file mode 100644 index 0000000000000000000000000000000000000000..711dd271dc16c1cdf3cc2d0254dd0b38ada7fb77 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-bcm57xx-stage2-image.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_BCM57XX_STAGE2_IMAGE (fu_bcm57xx_stage2_image_get_type()) +G_DECLARE_FINAL_TYPE(FuBcm57xxStage2Image, + fu_bcm57xx_stage2_image, + FU, + BCM57XX_STAGE2_IMAGE, + FuFirmware) + +FuFirmware * +fu_bcm57xx_stage2_image_new(void); diff --git a/fwupd-1.8.6/plugins/bcm57xx/fu-self-test.c b/fwupd-1.8.6/plugins/bcm57xx/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..c3bf451cc6d99d7bada00fb54d66b6b2b8937d36 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/fu-self-test.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-bcm57xx-common.h" +#include "fu-bcm57xx-dict-image.h" +#include "fu-bcm57xx-firmware.h" +#include "fu-bcm57xx-stage1-image.h" +#include "fu-bcm57xx-stage2-image.h" + +static void +fu_bcm57xx_create_verbuf(guint8 *bufver, const gchar *version) +{ + memcpy(bufver, version, strlen(version) + 1); +} + +static void +fu_bcm57xx_common_veritem_func(void) +{ + g_autoptr(Bcm57xxVeritem) veritem1 = NULL; + g_autoptr(Bcm57xxVeritem) veritem2 = NULL; + g_autoptr(Bcm57xxVeritem) veritem3 = NULL; + guint8 bufver[16] = {0x0}; + + fu_bcm57xx_create_verbuf(bufver, "5719-v1.43"); + veritem1 = fu_bcm57xx_veritem_new(bufver, sizeof(bufver)); + g_assert_nonnull(veritem1); + g_assert_cmpstr(veritem1->version, ==, "1.43"); + g_assert_cmpstr(veritem1->branch, ==, BCM_FW_BRANCH_UNKNOWN); + g_assert_cmpint(veritem1->verfmt, ==, FWUPD_VERSION_FORMAT_PAIR); + + fu_bcm57xx_create_verbuf(bufver, "stage1-0.4.391"); + veritem2 = fu_bcm57xx_veritem_new(bufver, sizeof(bufver)); + g_assert_nonnull(veritem2); + g_assert_cmpstr(veritem2->version, ==, "0.4.391"); + g_assert_cmpstr(veritem2->branch, ==, BCM_FW_BRANCH_OSS_FIRMWARE); + g_assert_cmpint(veritem2->verfmt, ==, FWUPD_VERSION_FORMAT_TRIPLET); + + fu_bcm57xx_create_verbuf(bufver, "RANDOM-7"); + veritem3 = fu_bcm57xx_veritem_new(bufver, sizeof(bufver)); + g_assert_nonnull(veritem3); + g_assert_cmpstr(veritem3->version, ==, "RANDOM-7"); + g_assert_cmpstr(veritem3->branch, ==, BCM_FW_BRANCH_UNKNOWN); + g_assert_cmpint(veritem3->verfmt, ==, FWUPD_VERSION_FORMAT_UNKNOWN); +} + +static void +fu_bcm57xx_firmware_talos_func(void) +{ + gboolean ret; + g_autofree gchar *fn = NULL; + g_autofree gchar *fn_out = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob_out = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) images = NULL; + g_autoptr(FuFirmware) firmware = fu_bcm57xx_firmware_new(); + + /* load file */ + fn = g_test_build_filename(G_TEST_DIST, "tests", "Bcm5719_talos.bin", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_test_skip("missing file"); + return; + } + blob = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + ret = fu_firmware_parse(firmware, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + images = fu_firmware_get_images(firmware); + g_assert_cmpint(images->len, ==, 6); + + blob_out = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_out); + fn_out = g_test_build_filename(G_TEST_BUILT, "tests", "Bcm5719_talos.bin", NULL); + ret = fu_bytes_set_contents(fn_out, blob_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_bytes_compare(blob, blob_out, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_bcm57xx_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_bcm57xx_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_bcm57xx_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "bcm57xx.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "a3ac108905c37857cf48612b707c1c72c582f914"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_type_ensure(FU_TYPE_BCM57XX_STAGE1_IMAGE); + g_type_ensure(FU_TYPE_BCM57XX_STAGE2_IMAGE); + g_type_ensure(FU_TYPE_BCM57XX_DICT_IMAGE); + + /* 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/bcm57xx/firmware{xml}", fu_bcm57xx_firmware_xml_func); + g_test_add_func("/fwupd/bcm57xx/firmware{talos}", fu_bcm57xx_firmware_talos_func); + g_test_add_func("/fwupd/bcm57xx/common{veritem}", fu_bcm57xx_common_veritem_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/bcm57xx/meson.build b/fwupd-1.8.6/plugins/bcm57xx/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..3d69f299670c2152525b9e04c41e3029c48827c9 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/meson.build @@ -0,0 +1,50 @@ +if get_option('plugin_bcm57xx').require(gudev.found(), + error_message: 'gudev is needed for plugin_bcm57xx').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginBcm57xx"'] + +plugin_quirks += files('bcm57xx.quirk') +plugin_builtin_bcm57xx = static_library('fu_plugin_bcm57xx', + sources: [ + 'fu-bcm57xx-plugin.c', + 'fu-bcm57xx-common.c', # fuzzing + 'fu-bcm57xx-device.c', + 'fu-bcm57xx-dict-image.c', # fuzzing + 'fu-bcm57xx-firmware.c', # fuzzing + 'fu-bcm57xx-recovery-device.c', + 'fu-bcm57xx-stage1-image.c', # fuzzing + 'fu-bcm57xx-stage2-image.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + valgrind, + ], +) +plugin_builtins += plugin_builtin_bcm57xx + +if get_option('tests') + install_data(['tests/bcm57xx.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'bcm57xx-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_bcm57xx, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('bcm57xx-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/bcm57xx/tests/bcm57xx.bin b/fwupd-1.8.6/plugins/bcm57xx/tests/bcm57xx.bin new file mode 100644 index 0000000000000000000000000000000000000000..9850e321a0bea8e010c7285082e1ae0203d51abb Binary files /dev/null and b/fwupd-1.8.6/plugins/bcm57xx/tests/bcm57xx.bin differ diff --git a/fwupd-1.8.6/plugins/bcm57xx/tests/bcm57xx.builder.xml b/fwupd-1.8.6/plugins/bcm57xx/tests/bcm57xx.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..5c328a9b7277a106e3cfae6ea985f2de17374616 --- /dev/null +++ b/fwupd-1.8.6/plugins/bcm57xx/tests/bcm57xx.builder.xml @@ -0,0 +1,18 @@ + + 1.2.3 + + 0x123456 + stage1 + 0x01 + aGVsbG8gd29ybGQ= + + + stage2 + + + + ape + 0x7 + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/plugins/bios/fu-bios-plugin.c b/fwupd-1.8.6/plugins/bios/fu-bios-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..474a3bec48a4e5741b08a36fabd8b4a700d50c94 --- /dev/null +++ b/fwupd-1.8.6/plugins/bios/fu-bios-plugin.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-bios-plugin.h" + +struct _FuBiosPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuBiosPlugin, fu_bios_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_bios_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *vendor; + + vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VENDOR); + if (g_strcmp0(vendor, "coreboot") == 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "system uses coreboot"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_bios_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + g_autofree gchar *sysfsfwdir = NULL; + g_autofree gchar *esrt_path = NULL; + + /* are the EFI dirs set up so we can update each device */ +#if defined(__x86_64__) || defined(__i386__) + g_autoptr(GError) error_local = NULL; + if (!fu_efivar_supported(&error_local)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_LEGACY_BIOS); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + return TRUE; + } +#endif + + /* get the directory of ESRT entries */ + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + esrt_path = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + if (!g_file_test(esrt_path, G_FILE_TEST_IS_DIR)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + return TRUE; + } + + /* we appear to have UEFI capsule updates */ + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED); + return TRUE; +} + +static void +fu_bios_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + + if (!fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_LEGACY_BIOS)) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT); + fu_security_attrs_append(attrs, attr); + + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_bios_plugin_init(FuBiosPlugin *self) +{ +} + +static void +fu_bios_plugin_class_init(FuBiosPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->startup = fu_bios_plugin_startup; + plugin_class->coldplug = fu_bios_plugin_coldplug; + plugin_class->add_security_attrs = fu_bios_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/bios/fu-bios-plugin.h b/fwupd-1.8.6/plugins/bios/fu-bios-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..562b4f9effaf64cb901286a4c618788e94c55490 --- /dev/null +++ b/fwupd-1.8.6/plugins/bios/fu-bios-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuBiosPlugin, fu_bios_plugin, FU, BIOS_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/bios/meson.build b/fwupd-1.8.6/plugins/bios/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..6d8c93343e1beb3f5e8c5453cec34a2214c86151 --- /dev/null +++ b/fwupd-1.8.6/plugins/bios/meson.build @@ -0,0 +1,13 @@ +if get_option('plugin_uefi_capsule').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginBios"'] + +plugin_builtins += static_library('fu_plugin_bios', + sources: [ + 'fu-bios-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/ccgx/README.md b/fwupd-1.8.6/plugins/ccgx/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3c6070d413bf3133f1668318037d57a3b0fed8cc --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/README.md @@ -0,0 +1,113 @@ +# Infineon Technologies + +## Introduction + +This plugin can flash firmware on Infineon (previously Cypress) CCGx USB-C +controller family of devices used in docks. + +## Supported Protocols + +This plugin supports the following protocol IDs: + +* com.cypress.ccgx +* com.cypress.ccgx.dmc +* com.infineon.ccgx +* com.infineon.ccgx.dmc + +## Device Flash + +There are four kinds of flash layout. Single image firmware is not currently +supported in this plugin. + +### Symmetric Firmware + +In symmetric firmware topology, FW1 and FW2 are both primary (main) firmware +with identical sizes and functionality. We can only update FW1 from FW2 or FW2 +from FW1. This does mean we need to update just one time as booting from either +firmware slot gives a fully functional device. + +After updating the "other" firmware we can just use `CY_PD_DEVICE_RESET_CMD_SIG` +to reboot into the new firmware, and no further action is required. + +### Asymmetric Firmware + +In asymmetric firmware topology, FW1 is backup and FW2 is primary (main) +firmware with different firmware sizes. The backup firmware may not support all +dock functionality. + +To update primary, we thus need to update twice: + +Case 1: FW2 is running + +* Update FW1 -> Jump to backup FW `CY_PD_JUMP_TO_ALT_FW_CMD_SIG` -> reboot +* Update FW2 -> Reset device `CY_PD_DEVICE_RESET_CMD_SIG` -> reboot -> FW2 + +Case 2: FW1 is running (recovery case) + +* Update FW2 -> Reset device `CY_PD_DEVICE_RESET_CMD_SIG` -> reboot -> FW2 + +The `CY_PD_JUMP_TO_ALT_FW_CMD_SIG` command is allowed only in asymmetric FW, but +`CY_PD_DEVICE_RESET_CMD_SIG` is allowed in both asymmetric FW and symmetric FW. + +### DMC(Dock Management Controller) Composite Firmware + +In composite firmware topology, a single firmware image contains metadata and +firmware images of multiple devices including DMC itself in a dock system. + +## Firmware Format + +There are two kinds of firmware format. + +### Cyacd firmware format + +The daemon will decompress the cabinet archive and extract several firmware +blobs in cyacd file format. See +for more details. + +### DMC composite firmware format + +The daemon will decompress the cabinet archive and extract several firmware +blobs in a combined image file format. See 4.4.1 Single Composite +(Combined) Dock Image at +for more details. + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1234&PID_5678` + +Devices also have additional instance IDs which corresponds to the silicon ID, +application ID and device mode, e.g. + +* `USB\VID_1234&PID_5678&SID_9ABC` +* `USB\VID_1234&PID_5678&SID_9ABC&APP_DEF1` +* `USB\VID_1234&PID_5678&SID_9ABC&APP_DEF1&MODE_FW2` + +## Update Behavior + +The device usually presents in runtime HID mode, but on detach re-enumerates +with with a DMC or HPI interface. On attach the device again re-enumerates +back to the runtime HID mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the DMC/HPI and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, for example set to `USB:0x04B4` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### Trigger Code + +DMC devices need a specified trigger code to request the device to update +the firmware and the trigger code depends on the devices. + +Since: 1.8.0 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/ccgx/ccgx-ids.quirk b/fwupd-1.8.6/plugins/ccgx/ccgx-ids.quirk new file mode 100644 index 0000000000000000000000000000000000000000..31fca2f183485add2b804b129e6f3893d5461c16 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/ccgx-ids.quirk @@ -0,0 +1,319 @@ +# CCG2 - CYPD2103-20FNXI +[CCGX\SID_1400] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2104-20FNXI +[CCGX\SID_1401] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2105-20FNXI +[CCGX\SID_1402] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2103-14LHXI +[CCGX\SID_1403] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2122-24LQXI +[CCGX\SID_1404] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2134-24LQXI +[CCGX\SID_1405] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2122-20FNXI +[CCGX\SID_1406] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2123-24LQXI +[CCGX\SID_1407] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2124-24LQXI +[CCGX\SID_1408] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2119-24LQXI +[CCGX\SID_1409] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2121-24LQXI +[CCGX\SID_1410] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2125-24LQXI +[CCGX\SID_1411] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG2 - CYPD2120-24LQXI +[CCGX\SID_1412] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x8000 + +# CCG3 - CYPD3120-40LQXI +[CCGX\SID_1D00] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3105-42FNXI +[CCGX\SID_1D01] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3121-40LQXI +[CCGX\SID_1D02] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3122-40LQXI +[CCGX\SID_1D03] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3125-40LQXI +[CCGX\SID_1D04] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3135-40LQXI +[CCGX\SID_1D05] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3135-16SXQ' +[CCGX\SID_1D06] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3126-42FNXI +[CCGX\SID_1D07] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3 - CYPD3123-40LQXI +[CCGX\SID_1D09] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4225-40LQXI +[CCGX\SID_1800] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4125-40LQXI +[CCGX\SID_1801] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4235-40LQXI +[CCGX\SID_1802] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4135-40LQXI +[CCGX\SID_1803] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4225A0-33FNXIT +[CCGX\SID_1810] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4226-40LQXI +[CCGX\SID_1F00] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4126-40LQXI +[CCGX\SID_1F01] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4126-24LQXI +[CCGX\SID_1F04] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4236-40LQXI +[CCGX\SID_1F02] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4136-40LQXI +[CCGX\SID_1F03] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG4 - CYPD4136-24LQXI +[CCGX\SID_1F05] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG3PA - CYPD3174-24LQXQ +[CCGX\SID_2000] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA - CYPD3174-16SXQ +[CCGX\SID_2001] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA - CYPD3175-24LQXQ +[CCGX\SID_2002] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA - CYPD3171-24LQXQ +[CCGX\SID_2003] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA - CYPD3195-24LDXS +[CCGX\SID_2005] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA - CYPD3196-24LDXS +[CCGX\SID_2006] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA - CYPD3197-24LDXS +[CCGX\SID_2007] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG3PA2 - CYPDC1185-32LQXQ +[CCGX\SID_2400] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3PA2 - CYPDC1186-30FNXI +[CCGX\SID_2401] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG3PA2 - CYPDC1186B2-30FNXI +[CCGX\SID_2402] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5225-96BZXI +[CCGX\SID_2100] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5125-40LQXI +[CCGX\SID_2101] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5235-96BZXI +[CCGX\SID_2102] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5236-96BZXI +[CCGX\SID_2103] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5237-96BZXI +[CCGX\SID_2104] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5227-96BZXI +[CCGX\SID_2105] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG5 - CYPD5135-40LQXI +[CCGX\SID_2106] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG6 - CYPD6125-40LQXI +[CCGX\SID_2A00] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG6 - CYPD6126-96BZXI +[CCGX\SID_2A10] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG6 - CYPD5126-40LQXI +[CCGX\SID_2A01] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG6 - CYPD5137-40LQXI +[CCGX\SID_2A02] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# CCG6 - CYPD6137-40LQXI +[CCGX\SID_2A03] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# PAG1S - CYPAS111-24LQXQ +[CCGX\SID_2B01] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# PAG1S - CYPD3184-24LQXQ +[CCGX\SID_2B00] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# HX3PD - CYUSB4347-BZXC_PD +[CCGX\SID_1F82] +CcgxFlashRowSize = 0x100 +CcgxFlashSize = 0x20000 + +# ACG1F - CYAC1126-24LQXI +[CCGX\SID_2F00] +CcgxFlashRowSize = 0x40 +CcgxFlashSize = 0x4000 + +# ACG1F - CYAC1126-40LQXI +[CCGX\SID_2F01] +CcgxFlashRowSize = 0x40 +CcgxFlashSize = 0x4000 + +# CCG6DF - CYPD6227-96BZXI +[CCGX\SID_3000] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG6DF - CYPD6127-96BZXI +[CCGX\SID_3001] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG6SF - CYPD6128-96BZXI +[CCGX\SID_3300] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 + +# CCG6SF - CYPD6127-48LQXI +[CCGX\SID_3301] +CcgxFlashRowSize = 0x80 +CcgxFlashSize = 0x10000 diff --git a/fwupd-1.8.6/plugins/ccgx/ccgx-noinst.quirk b/fwupd-1.8.6/plugins/ccgx/ccgx-noinst.quirk new file mode 100644 index 0000000000000000000000000000000000000000..7b31ef75931218aa007f10a63090340c8d78d4a7 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/ccgx-noinst.quirk @@ -0,0 +1,15 @@ +# Any EVB board that uses CY7C65219 +[USB\VID_04B4&PID_5220] +Plugin = ccgx +GType = FuCcgxDmcDevice +CcgxDmcTriggerCode = 1 +CcgxImageKind = dmc-composite +RemoveDelay = 732000 + +# Any EVB board that uses CYUSB4357 +[USB\VID_04B4&PID_521B] +Plugin = ccgx +GType = FuCcgxDmcDevice +CcgxDmcTriggerCode = 1 +CcgxImageKind = dmc-composite +RemoveDelay = 732000 diff --git a/fwupd-1.8.6/plugins/ccgx/ccgx.quirk b/fwupd-1.8.6/plugins/ccgx/ccgx.quirk new file mode 100644 index 0000000000000000000000000000000000000000..5a29a34bf12356080f0e6d46d0f139b52405b84e --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/ccgx.quirk @@ -0,0 +1,125 @@ +# Lenovo ThinkPad USB-C Dock Gen2 +[USB\VID_17EF&PID_A38F] +Plugin = ccgx +GType = FuCcgxHidDevice +ParentGuid = USB\VID_17EF&PID_A391 + +[USB\VID_04B4&PID_521A] +Plugin = ccgx +GType = FuCcgxHpiDevice + +[USB\VID_04B4&PID_521A&SID_1F00&APP_6D64] +CcgxImageKind = dual-asymmetric +Name = ThinkPad USB-C Dock Gen2 PD Controller +ParentGuid = USB\VID_17EF&PID_A391 +InstallDuration = 120 +RemoveDelay = 60000 + +[USB\VID_04B4&PID_521A&SID_1F00&APP_6D64&MODE_FW1] +Summary = CCGx Power Delivery Device (Bootloader) +Flags = is-bootloader + +[USB\VID_04B4&PID_521A&SID_1F00&APP_6D64&MODE_FW2] +Summary = CCGx Power Delivery Device +CounterpartGuid = USB\VID_04B4&PID_521A&SID_1F00&APP_6D64&MODE_FW1 + +# Lenovo ThinkPad USB-C Dock Hybrid +[USB\VID_17EF&PID_A354] +Plugin = ccgx +GType = FuCcgxHidDevice +ParentGuid = USB\VID_17EF&PID_1028 + +[USB\VID_17EF&PID_A35F] +Plugin = ccgx +GType = FuCcgxHidDevice +ParentGuid = USB\VID_17EF&PID_1028 + +[USB\VID_04B4&PID_5218] +Plugin = ccgx +GType = FuCcgxHpiDevice + +[USB\VID_04B4&PID_5218&SID_1F00&APP_6432] +CcgxImageKind = dual-symmetric +Name = ThinkPad USB-C Dock Hybrid PD Controller +ParentGuid = USB\VID_17EF&PID_1028 +InstallDuration = 120 +RemoveDelay = 60000 + +[USB\VID_04B4&PID_5218&SID_1F00&APP_6432&MODE_FW1] +Summary = CCGx Power Delivery Device (Symmetric FW1) + +[USB\VID_04B4&PID_5218&SID_1F00&APP_6432&MODE_FW2] +Summary = CCGx Power Delivery Device (Symmetric FW2) + +# Lenovo ThinkPad Universal USB-C Dock +[USB\VID_17EF&PID_30A9] +Plugin = ccgx +GType = FuCcgxDmcDevice +Summary = Dock Management Controller Device +ParentGuid = USB\VID_17EF&PID_30AF +Name = ThinkPad Universal USB-C Dock +Flags = has-manual-replug +CcgxDmcTriggerCode = 0x02 +CcgxImageKind = dmc-composite +InstallDuration = 60 + +[USB\VID_17EF&PID_3105] +Plugin = ccgx +GType = FuCcgxDmcDevice +Summary = Dock Management Controller Device +ParentGuid = USB\VID_17EF&PID_30AF +Name = ThinkPad Universal USB-C Dock +Flags = has-manual-replug +CcgxDmcTriggerCode = 0x02 +CcgxImageKind = dmc-composite +InstallDuration = 60 + +# HP USB-C Dock G5 +[USB\VID_03F0&PID_046B] +Plugin = ccgx +GType = FuCcgxDmcDevice +Summary = Dock Management Controller Device +ParentGuid = USB\VID_03F0&PID_0363 +Vendor = HP +Name = USB-C Dock G5 +CcgxDmcTriggerCode = 0x01 +CcgxImageKind = dmc-composite +InstallDuration = 233 +RemoveDelay = 203000 + +# HP USB-C/A Universal Dock G2 +[USB\VID_03F0&PID_0A6B] +Plugin = ccgx +GType = FuCcgxDmcDevice +Summary = Dock Management Controller Device +ParentGuid = USB\VID_03F0&PID_096B +Vendor = HP +Name = USB-C/A Universal Dock G2 +CcgxDmcTriggerCode = 0x01 +CcgxImageKind = dmc-composite +InstallDuration = 180 +RemoveDelay = 162000 + +# HP Thunderbolt Dock G4 Root Hub +[USB\VID_1D5C&PID_5801] +Summary = USB Hub +Name = Thunderbolt Dock G4 top most USB Hub + +# HP Thunderbolt Dock G4 Hub +[USB\VID_03F0&PID_2488] +Summary = USB Hub +ParentGuid = USB\VID_1D5C&PID_5801 +Name = Thunderbolt Dock G4 USB Hub + +# HP Thunderbolt Dock G4 +[USB\VID_03F0&PID_0488] +Plugin = ccgx +GType = FuCcgxDmcDevice +Summary = Dock Management Controller Device +ParentGuid = USB\VID_03F0&PID_2488 +Vendor = HP +Name = Thunderbolt Dock G4 +CcgxDmcTriggerCode = 0x01 +CcgxImageKind = dmc-composite +InstallDuration = 898 +RemoveDelay = 732000 diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-common.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-common.c new file mode 100644 index 0000000000000000000000000000000000000000..956904a23cd36b9c266cd11ddb9b78bea50e367d --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-common.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ccgx-common.h" + +gchar * +fu_ccgx_version_to_string(guint32 val) +{ + /* 16 bits: application type [LSB] + * 8 bits: build number + * 4 bits: minor version + * 4 bits: major version [MSB] */ + return g_strdup_printf("%u.%u.%u", + (val >> 28) & 0x0f, + (val >> 24) & 0x0f, + (val >> 16) & 0xff); +} + +const gchar * +fu_ccgx_fw_mode_to_string(FWMode val) +{ + if (val == FW_MODE_BOOT) + return "BOOT"; + if (val == FW_MODE_FW1) + return "FW1"; + if (val == FW_MODE_FW2) + return "FW2"; + return NULL; +} + +const gchar * +fu_ccgx_fw_image_type_to_string(FWImageType val) +{ + if (val == FW_IMAGE_TYPE_SINGLE) + return "single"; + if (val == FW_IMAGE_TYPE_DUAL_SYMMETRIC) + return "dual-symmetric"; + if (val == FW_IMAGE_TYPE_DUAL_ASYMMETRIC) + return "dual-asymmetric"; + if (val == FW_IMAGE_TYPE_DUAL_ASYMMETRIC_VARIABLE) + return "dual-asymmetric-variable"; + if (val == FW_IMAGE_TYPE_DMC_COMPOSITE) + return "dmc-composite"; + return NULL; +} + +FWImageType +fu_ccgx_fw_image_type_from_string(const gchar *val) +{ + if (g_strcmp0(val, "single") == 0) + return FW_IMAGE_TYPE_SINGLE; + if (g_strcmp0(val, "dual-symmetric") == 0) + return FW_IMAGE_TYPE_DUAL_SYMMETRIC; + if (g_strcmp0(val, "dual-asymmetric") == 0) + return FW_IMAGE_TYPE_DUAL_ASYMMETRIC; + if (g_strcmp0(val, "dual-asymmetric-variable") == 0) + return FW_IMAGE_TYPE_DUAL_ASYMMETRIC_VARIABLE; + if (g_strcmp0(val, "dmc-composite") == 0) + return FW_IMAGE_TYPE_DMC_COMPOSITE; + return FW_IMAGE_TYPE_UNKNOWN; +} + +FWMode +fu_ccgx_fw_mode_get_alternate(FWMode fw_mode) +{ + if (fw_mode == FW_MODE_FW1) + return FW_MODE_FW2; + if (fw_mode == FW_MODE_FW2) + return FW_MODE_FW1; + return FW_MODE_BOOT; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-common.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-common.h new file mode 100644 index 0000000000000000000000000000000000000000..7d7ee9c340c2c976c742fd6a203721dfb0d04701 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-common.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* metadata valid signature "CY" */ +#define CCGX_METADATA_VALID_SIG 0x4359 + +typedef struct __attribute__((packed)) { + guint8 fw_checksum; /* firmware checksum */ + guint32 fw_entry; /* firmware entry address */ + guint16 last_boot_row; /* last flash row of bootloader or previous firmware */ + guint8 reserved1[2]; /* reserved */ + guint32 fw_size; /* firmware size */ + guint8 reserved2[9]; /* reserved */ + guint16 metadata_valid; /* metadata valid "CY" */ + guint8 reserved3[4]; /* reserved */ + guint32 boot_seq; /* boot sequence number */ +} CCGxMetaData; + +/* firmware mode in device */ +typedef enum { FW_MODE_BOOT = 0, FW_MODE_FW1, FW_MODE_FW2, FW_MODE_LAST } FWMode; + +/* firmware image type */ +typedef enum { + FW_IMAGE_TYPE_UNKNOWN = 0, + FW_IMAGE_TYPE_SINGLE, + FW_IMAGE_TYPE_DUAL_SYMMETRIC, /* A/B runtime */ + FW_IMAGE_TYPE_DUAL_ASYMMETRIC, /* A=bootloader (fixed), B=runtime */ + FW_IMAGE_TYPE_DUAL_ASYMMETRIC_VARIABLE, /* A=bootloader (variable), B=runtime */ + FW_IMAGE_TYPE_DMC_COMPOSITE, /* composite firmware image for dmc */ +} FWImageType; + +gchar * +fu_ccgx_version_to_string(guint32 val); +const gchar * +fu_ccgx_fw_mode_to_string(FWMode val); +FWMode +fu_ccgx_fw_mode_get_alternate(FWMode val); +const gchar * +fu_ccgx_fw_image_type_to_string(FWImageType val); +FWImageType +fu_ccgx_fw_image_type_from_string(const gchar *val); diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-common.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-common.c new file mode 100644 index 0000000000000000000000000000000000000000..6a963e051150062bcad3ab59d5a56491a50ae1c6 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-common.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ccgx-dmc-common.h" + +const gchar * +fu_ccgx_dmc_update_model_type_to_string(DmcUpdateModel val) +{ + if (val == DMC_UPDATE_MODEL_NONE) + return "None"; + if (val == DMC_UPDATE_MODEL_DOWNLOAD_TRIGGER) + return "Download Trigger"; + if (val == DMC_UPDATE_MODEL_PENDING_RESET) + return "Pending Reset"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-common.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-common.h new file mode 100644 index 0000000000000000000000000000000000000000..be4c572dedabc43c2642295f29c58d7f023068c7 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-common.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* maximum number of programmable devices expected to be connected in dock. + * this is design limitation. This shall not be edited, unless stated by C Y*/ +#define DMC_DOCK_MAX_DEV_COUNT 16 + +/* size of FW version structure in bytes */ +#define DMC_DOCK_FW_VERSION_SIZE 8 + +/* indicates length of string in dock identity*/ +#define DMC_IDENTITY_STRING_LEN 32 + +/* interrupt end point for DMC Dock */ +#define DMC_INTERRUPT_PIPE_ID 0x82 + +/* USB bulk end point for DMC Dock */ +#define DMC_BULK_PIPE_ID 1 + +/* indicates length of interrupt structure's data array filed */ +#define DMC_INTERRUPT_DATA_LEN 8 + +/* status of the dmc will have different length. so first few bytes of status + * is read, which will contain actual length of status. this value indicates + * how much byte should read at first stage */ +#define DMC_GET_STATUS_MIN_LEN 32 + +#define DMC_HASH_SIZE 32 + +/* time out to be set in control in/out pipe policy in ms */ +#define DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT 5000 + +/* time out to be set in bulk out pipe policy in ms */ +#define DMC_BULK_OUT_PIPE_TIMEOUT 2000 + +/* time out to be set in bulk out pipe policy in ms */ +#define DMC_GET_REQUEST_TIMEOUT 20000 + +#define DMC_FWCT_SIGN 0x54435746 /* 'F' 'W' 'C' 'T' */ + +/* first we have to read few bytes to know the actual length of FWCT + * this constant defines, number bytest to be read for getting length */ +#define DMC_FWCT_MIN_LENGTH 6 +#define DMC_FWCT_LENGTH_OFFSET 4 +#define DMC_FWCT_MAX_SIZE 2048 + +#define DMC_CUSTOM_META_LENGTH_FIELD_SIZE 2 +#define DMC_CUSTOM_META_LENGTH_OFFSET 0 +#define DMC_CUSTOM_META_MAX_SIZE 256 + +/* this data type enumerates the image types */ +typedef enum { DMC_IMG_TYPE_INVALID = 0, DMC_IMG_TYPE_IMAGE_0, DMC_IMG_TYPE_IMAGE_1 } DmcImgType; + +/* this data type enumerates the image status */ +typedef enum { + DMC_IMG_STATUS_UNKNOWN = 0, + DMC_IMG_STATUS_VALID, + DMC_IMG_STATUS_INVALID, + DMC_IMG_STATUS_RECOVERY, + DMC_IMG_STATUS_RECOVERED_FROM_SECONDARY, + DMC_IMG_STATUS_NOT_SUPPORTED = 0x0F +} DmcImgStatus; + +/* this data type enumerates the image modes or flash architecture */ +typedef enum { + /* indicates that the device has a single image */ + DMC_IMG_MODE_SINGLE_IMG = 0, + /* the device supports symmetric boot. In symmetric mode the bootloader + * boots the image with higher version, when they are valid */ + DMC_IMG_MODE_DUAL_IMG_SYM, + /* the device supports Asymmetric boot. Image-1 & 2 can be different or + * same. in this method Bootloader is hard coded to boot the primary + * image. Secondary acts as recovery */ + DMC_IMG_MODE_DUAL_IMG_ASYM, + DMC_IMG_MODE_SINGLE_IMG_WITH_RAM_IMG, +} DmcImgMode; + +/* this data type enumerates the dock status */ +typedef enum { + /* status code indicating DOCK IDLE state. SUCCESS: no malfunctioning + * no outstanding request or event */ + DMC_DEVICE_STATUS_IDLE = 0, + /* status code indicating dock FW update in progress */ + DMC_DEVICE_STATUS_UPDATE_IN_PROGRESS, + /* status code indicating dock FW update is partially complete */ + DMC_DEVICE_STATUS_UPDATE_PARTIAL, + /* status code indicating dock FW update SUCCESS - all m_images of all + * devices are valid */ + DMC_DEVICE_STATUS_UPDATE_COMPLETE_FULL, + /* status code indicating dock FW update SUCCESS - not all m_images of all + * devices are valid */ + DMC_DEVICE_STATUS_UPDATE_COMPLETE_PARTIAL, + /* fw download status */ + DMC_DEVICE_STATUS_UPDATE_PHASE_1_COMPLETE, + DMC_DEVICE_STATUS_FW_DOWNLOADED_UPDATE_PEND, + DMC_DEVICE_STATUS_FW_DOWNLOADED_PARTIAL_UPDATE_PEND, + DMC_DEVICE_STATUS_PHASE2_UPDATE_IN_PROGRESS = 0x81, + DMC_DEVICE_STATUS_PHASE2_UPDATE_PARTIAL, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FACTORY_BACKUP, + DMC_DEVICE_STATUS_PHASE2_UPDATE_COMPLETE_PARTIAL, + DMC_DEVICE_STATUS_PHASE2_UPDATE_COMPLETE_FULL, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_FWCT, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_DOCK_IDENTITY, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_COMPOSITE_VER, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_AUTHENTICATION_FAILED, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_ALGORITHM, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_SPI_READ_FAILED, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_NO_VALID_KEY, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_NO_VALID_SPI_PACKAGE, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_RAM_INIT_FAILED, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_FACTORY_BACKUP_FAILED, + DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_NO_VALID_FACTORY_PACKAGE, + /* status code indicating dock FW update FAILED */ + DMC_DEVICE_STATUS_UPDATE_FAIL = 0xFF +} DmcDeviceStatus; + +/* this data type enumerates the request codes for vendor interface */ +typedef enum { + DMC_RQT_CODE_UPGRADE_START = 0xD0, + DMC_RQT_CODE_RESERV_0, + DMC_RQT_CODE_FWCT_WRITE, + DMC_RQT_CODE_IMG_WRITE, + DMC_RQT_CODE_RESERV_1, + DMC_RQT_CODE_RESERV_2, + DMC_RQT_CODE_DOCK_STATUS, + DMC_RQT_CODE_DOCK_IDENTITY, + /* command to reset dmc state machine of DMC */ + DMC_RQT_CODE_RESET_STATE_MACHINE, + /* command to reset for online enhanced mode (no reset during update) */ + DMC_RQT_CODE_SOFT_RESET = 0xDC, + /* Update Trigger command for offline mode */ + DMC_RQT_CODE_TRIGGER = 0xDA +} DmcRqtCode; + +/* this data type enumerates the opcode of interrupt read */ +typedef enum { + DMC_INT_OPCODE_FW_UPGRADE_RQT = 1, + DMC_INT_OPCODE_FW_UPGRADE_STATUS = 0x80, + DMC_INT_OPCODE_IMG_WRITE_STATUS, + DMC_INT_OPCODE_REENUM, + DMC_INT_OPCODE_FWCT_ANALYSIS_STATUS +} DmcIntOpcode; + +/* this data type enumerates the fwct analysis status */ +typedef enum { + DMC_FWCT_ANALYSIS_STATUS_INVALID_FWCT = 0, + DMC_FWCT_ANALYSIS_STATUS_INVALID_DOCK_IDENTITY, + DMC_FWCT_ANALYSIS_STATUS_INVALID_COMPOSITE_VERSION, + DMC_FWCT_ANALYSIS_STATUS_AUTHENTICATION_FAILED, + DMC_FWCT_ANALYSIS_STATUS_INVALID_ALGORITHM +} DmcFwctAnalysisStatus; + +typedef enum { + DMC_UPDATE_MODEL_NONE = 0, + /* need to trigger after updating FW */ + DMC_UPDATE_MODEL_DOWNLOAD_TRIGGER, + /* need to set soft reset after updating FW */ + DMC_UPDATE_MODEL_PENDING_RESET, +} DmcUpdateModel; + +/* this structure defines the fields of data returned when reading dock_identity + * for new firmware */ +typedef struct __attribute__((packed)) { + /* this field indicates both validity and structure version + * 0 : invalid + * 1 : old structure + * 2 : new structure */ + guint8 structure_version; + guint8 cdtt_version; + guint16 vid; + guint16 pid; + guint16 device_id; + gchar vendor_string[DMC_IDENTITY_STRING_LEN]; + gchar product_string[DMC_IDENTITY_STRING_LEN]; + guint8 custom_meta_data_flag; + /* model field indicates the type of the firmware upgrade status + * 0 - online/offline + * 1 - Online model + * 2 - ADICORA/Offline model + * 3 - No reset + * 4 - 0xFF - Reserved + */ + guint8 model; +} DmcDockIdentity; + +/* this structure defines the fields of status of a specific device */ +typedef struct __attribute__((packed)) { + /* device ID of the device */ + guint8 device_type; + /* component ID of the device */ + guint8 component_id; + /* image mode of the device - single image/ dual symmetric/ dual + * asymmetric image > */ + guint8 image_mode; + /* current running image */ + guint8 current_image; + /* image status + * b7:b4 => Image 2 status + * b3:b0 => Image 1 status + * 0 = Unknown + * 1 = Valid + * 2 = Invalid + * 3-0xF = Reserved + */ + guint8 img_status; + /* padding */ + guint8 reserved_0[3]; + /* complete fw version 8 bytes for bootload, image1 and image2. 8 byte + * for fw version and application version */ + guint8 fw_version[24]; +} DmcDevxStatus; + +/* this structure defines the fields of data returned when reading dock_status */ +typedef struct __attribute__((packed)) { + /* overall status of dock. see DmcDeviceStatus */ + guint8 device_status; + /* eevice count */ + guint8 device_count; + /* length of status bytes including dock_status, + devx_status for each device */ + guint16 status_length; + /* dock composite version m_fwct_info */ + guint32 composite_version; + /* fw status of device of interest */ + DmcDevxStatus devx_status[DMC_DOCK_MAX_DEV_COUNT]; +} DmcDockStatus; + +/* This structure defines the fields of data returned when reading an interrupt + * from DMC */ +typedef struct __attribute__((packed)) { + guint8 opcode; + guint8 length; + guint8 data[DMC_INTERRUPT_DATA_LEN]; +} DmcIntRqt; + +/* this structure defines header structure of FWCT */ +typedef struct __attribute__((packed)) { + guint32 signature; + guint16 size; + guint8 checksum; + guint8 version; + guint8 custom_meta_type; + guint8 cdtt_version; + guint16 vid; + guint16 pid; + guint16 device_id; + guint8 reserv0[16]; + guint32 composite_version; + guint8 image_count; + guint8 reserv1[3]; +} FwctInfo; + +typedef struct __attribute__((packed)) { + guint8 device_type; + guint8 img_type; + guint8 comp_id; + guint8 row_size; + guint8 reserv0[4]; + guint32 fw_version; + guint32 app_version; + guint32 img_offset; + guint32 img_size; + guint8 img_digest[32]; + guint8 num_img_segments; + guint8 reserv1[3]; +} FwctImageInfo; + +typedef struct __attribute__((packed)) { + guint8 img_id; + guint8 type; + guint16 start_row; + guint16 num_rows; /* size */ + guint8 reserv0[2]; +} FwctSegmentationInfo; + +const gchar * +fu_ccgx_dmc_update_model_type_to_string(DmcUpdateModel val); diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-device.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..0439d1c1b1e247ab7dc8b87463f1560c9ecab0ba --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-device.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ccgx-common.h" +#include "fu-ccgx-dmc-common.h" +#include "fu-ccgx-dmc-device.h" +#include "fu-ccgx-dmc-firmware.h" + +#define DMC_FW_WRITE_STATUS_RETRY_COUNT 3 +#define DMC_FW_WRITE_STATUS_RETRY_DELAY_MS 30 + +struct _FuCcgxDmcDevice { + FuUsbDevice parent_instance; + FWImageType fw_image_type; + DmcDockIdentity dock_id; + guint8 ep_intr_in; + guint8 ep_bulk_out; + DmcUpdateModel update_model; + guint16 trigger_code; /* trigger code for update */ +}; + +/** + * FU_CCGX_DMC_DEVICE_FLAG_HAS_MANUAL_REPLUG: + * + * Needs a manual replug from the end-user. + */ +#define FU_CCGX_DMC_DEVICE_FLAG_HAS_MANUAL_REPLUG (1 << 0) + +G_DEFINE_TYPE(FuCcgxDmcDevice, fu_ccgx_dmc_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_ccgx_dmc_device_get_dock_id(FuCcgxDmcDevice *self, DmcDockIdentity *dock_id, GError **error) +{ + g_return_val_if_fail(dock_id != NULL, FALSE); + + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_DOCK_IDENTITY, /* request */ + 0, /* value */ + 0, /* index */ + (guint8 *)dock_id, /* data */ + sizeof(DmcDockIdentity), /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "get_dock_id error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_get_dock_status(FuCcgxDmcDevice *self, + DmcDockStatus *dock_status, + GError **error) +{ + g_return_val_if_fail(dock_status != NULL, FALSE); + + /* read minimum status length */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_DOCK_STATUS, /* request */ + 0, /* value */ + 0, /* index */ + (guint8 *)dock_status, /* data */ + DMC_GET_STATUS_MIN_LEN, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "get_dock_status min size error: "); + return FALSE; + } + if (dock_status->status_length <= sizeof(DmcDockStatus)) { + /* read full status length */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_DOCK_STATUS, /* request */ + 0, /* value */ + 0, /* index */ + (guint8 *)dock_status, /* data */ + sizeof(DmcDockStatus), /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "get_dock_status actual size error: "); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_reset_state_machine(FuCcgxDmcDevice *self, GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_RESET_STATE_MACHINE, /* request */ + 0, /* value */ + 0, /* index */ + 0, /* data */ + 0, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send reset state machine error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_sort_reset(FuCcgxDmcDevice *self, gboolean reset_later, GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_SOFT_RESET, /* request */ + reset_later, /* value */ + 0, /* index */ + 0, /* data */ + 0, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send reset error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_start_upgrade(FuCcgxDmcDevice *self, + const guint8 *custom_meta_data, + guint16 custom_meta_bufsz, + GError **error) +{ + guint16 value = 0; + if (custom_meta_bufsz > 0) + value = 1; + + if (custom_meta_bufsz > 0 && custom_meta_data == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid metadata, buffer is NULL but size = %d", + custom_meta_bufsz); + return FALSE; + } + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_UPGRADE_START, /* request */ + value, /* value */ + 1, /* index, forced update */ + (guint8 *)custom_meta_data, /* data */ + custom_meta_bufsz, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send reset error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_download_trigger(FuCcgxDmcDevice *self, guint16 trigger, GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_TRIGGER, /* request */ + trigger, /* value */ + 0, /* index */ + 0, /* data */ + 0, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send download trigger error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_fwct(FuCcgxDmcDevice *self, + const guint8 *fwct_buf, + guint16 fwct_sz, + GError **error) +{ + g_return_val_if_fail(fwct_buf != NULL, FALSE); + + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_FWCT_WRITE, /* request */ + 0, /* value */ + 0, /* index */ + (guint8 *)fwct_buf, /* data */ + fwct_sz, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send fwct error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_read_intr_req(FuCcgxDmcDevice *self, DmcIntRqt *intr_rqt, GError **error) +{ + g_return_val_if_fail(intr_rqt != NULL, FALSE); + + if (!g_usb_device_interrupt_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->ep_intr_in, + (guint8 *)intr_rqt, + sizeof(DmcIntRqt), + NULL, + DMC_GET_REQUEST_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "read intr rqt error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_write_command(FuCcgxDmcDevice *self, + guint16 start_row, + guint16 num_of_row, + GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + DMC_RQT_CODE_IMG_WRITE, /* request */ + start_row, /* value */ + num_of_row, /* index */ + 0, /* data */ + 0, /* length */ + NULL, /* actual length */ + DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "send fwct error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_send_row_data(FuCcgxDmcDevice *self, + const guint8 *row_buffer, + guint16 row_size, + GError **error) +{ + g_return_val_if_fail(row_buffer != NULL, FALSE); + + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->ep_bulk_out, + (guint8 *)row_buffer, + row_size, + NULL, + DMC_BULK_OUT_PIPE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "write row data error: "); + return FALSE; + } + return TRUE; +} + +static void +fu_ccgx_dmc_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + fu_string_append(str, + idt, + "UpdateModel", + fu_ccgx_dmc_update_model_type_to_string(self->update_model)); + if (self->fw_image_type != FW_IMAGE_TYPE_UNKNOWN) { + fu_string_append(str, + idt, + "FwImageType", + fu_ccgx_fw_image_type_to_string(self->fw_image_type)); + } + fu_string_append_kx(str, idt, "EpBulkOut", self->ep_bulk_out); + fu_string_append_kx(str, idt, "EpIntrIn", self->ep_intr_in); + fu_string_append_kx(str, idt, "TriggerCode", self->trigger_code); +} + +static gboolean +fu_ccgx_dmc_get_image_write_status_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + DmcIntRqt dmc_int_req = {0}; + + /* get interrupt request */ + if (!fu_ccgx_dmc_device_read_intr_req(self, &dmc_int_req, error)) { + g_prefix_error(error, "read intr req error in image write status: "); + return FALSE; + } + /* check opcode for fw write */ + if (dmc_int_req.opcode != DMC_INT_OPCODE_IMG_WRITE_STATUS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid dmc intr req opcode in image write status = %d", + dmc_int_req.opcode); + return FALSE; + } + + /* retry if data[0] is 1 otherwise error */ + if (dmc_int_req.data[0] != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid dmc intr req data in image write status = %d", + dmc_int_req.data[0]); + g_usleep(DMC_FW_WRITE_STATUS_RETRY_DELAY_MS * 1000); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_write_firmware_record(FuCcgxDmcDevice *self, + FuCcgxDmcFirmwareSegmentRecord *seg_rcd, + gsize *fw_data_written, + FuProgress *progress, + GError **error) +{ + GPtrArray *data_records = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + + /* write start row and number of rows to a device */ + if (!fu_ccgx_dmc_device_send_write_command(self, + seg_rcd->start_row, + seg_rcd->num_rows, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* send data records */ + data_records = seg_rcd->data_records; + for (guint32 data_index = 0; data_index < data_records->len; data_index++) { + GBytes *data_rcd = g_ptr_array_index(data_records, data_index); + const guint8 *row_buffer = NULL; + gsize row_size = 0; + + /* write row data */ + row_buffer = g_bytes_get_data(data_rcd, &row_size); + if (!fu_ccgx_dmc_device_send_row_data(self, row_buffer, (guint16)row_size, error)) + return FALSE; + + /* increase fw written size */ + *fw_data_written += row_size; + + /* get status */ + if (!fu_device_retry(FU_DEVICE(self), + fu_ccgx_dmc_get_image_write_status_cb, + DMC_FW_WRITE_STATUS_RETRY_COUNT, + NULL, + error)) + return FALSE; + + /* done */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + data_index + 1, + data_records->len); + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_ccgx_dmc_write_firmware_image(FuDevice *device, + FuCcgxDmcFirmwareRecord *img_rcd, + gsize *fw_data_written, + const gsize fw_data_size, + FuProgress *progress, + GError **error) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + GPtrArray *seg_records; + + g_return_val_if_fail(img_rcd != NULL, FALSE); + g_return_val_if_fail(fw_data_written != NULL, FALSE); + + /* get segment records */ + seg_records = img_rcd->seg_records; + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, seg_records->len); + for (guint32 seg_index = 0; seg_index < seg_records->len; seg_index++) { + FuCcgxDmcFirmwareSegmentRecord *seg_rcd = g_ptr_array_index(seg_records, seg_index); + if (!fu_ccgx_dmc_write_firmware_record(self, + seg_rcd, + fw_data_written, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + return TRUE; +} + +static gboolean +fu_ccgx_dmc_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + FuCcgxDmcFirmwareRecord *img_rcd = NULL; + DmcIntRqt dmc_int_rqt = {0}; + GBytes *custom_meta_blob; + GBytes *fwct_blob; + GPtrArray *image_records; + const guint8 *custom_meta_data = NULL; + const guint8 *fwct_buf = NULL; + gsize custom_meta_bufsz = 0; + gsize fwct_sz = 0; + gsize fw_data_size = 0; + gsize fw_data_written = 0; + guint8 img_index = 0; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "fwct"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "img"); + + /* get fwct record */ + fwct_blob = fu_ccgx_dmc_firmware_get_fwct_record(FU_CCGX_DMC_FIRMWARE(firmware)); + fwct_buf = g_bytes_get_data(fwct_blob, &fwct_sz); + if (fwct_buf == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid fwct data"); + return FALSE; + } + + /* get custom meta record */ + custom_meta_blob = + fu_ccgx_dmc_firmware_get_custom_meta_record(FU_CCGX_DMC_FIRMWARE(firmware)); + if (custom_meta_blob != NULL) + custom_meta_data = g_bytes_get_data(custom_meta_blob, &custom_meta_bufsz); + + /* reset */ + if (!fu_ccgx_dmc_device_send_reset_state_machine(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* start fw upgrade with custom metadata */ + if (!fu_ccgx_dmc_device_send_start_upgrade(self, + custom_meta_data, + custom_meta_bufsz, + error)) + return FALSE; + + /* send fwct data */ + if (!fu_ccgx_dmc_device_send_fwct(self, fwct_buf, fwct_sz, error)) + return FALSE; + fu_progress_step_done(progress); + + /* get total fw size */ + image_records = fu_ccgx_dmc_firmware_get_image_records(FU_CCGX_DMC_FIRMWARE(firmware)); + fw_data_size = fu_ccgx_dmc_firmware_get_fw_data_size(FU_CCGX_DMC_FIRMWARE(firmware)); + while (1) { + /* get interrupt request */ + if (!fu_ccgx_dmc_device_read_intr_req(self, &dmc_int_rqt, error)) + return FALSE; + + /* fw upgrade request */ + if (dmc_int_rqt.opcode != DMC_INT_OPCODE_FW_UPGRADE_RQT) + break; + + img_index = dmc_int_rqt.data[0]; + if (img_index >= image_records->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid image index %d, expected less than %u", + img_index, + image_records->len); + return FALSE; + } + + /* write image */ + img_rcd = g_ptr_array_index(image_records, img_index); + if (!fu_ccgx_dmc_write_firmware_image(device, + img_rcd, + &fw_data_written, + fw_data_size, + fu_progress_get_child(progress), + error)) + return FALSE; + } + if (dmc_int_rqt.opcode != DMC_INT_OPCODE_FW_UPGRADE_STATUS) { + if (dmc_int_rqt.opcode == DMC_INT_OPCODE_FWCT_ANALYSIS_STATUS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "fwct analysis failed with status = %d", + dmc_int_rqt.data[0]); + return FALSE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid dmc intr req opcode = %d with status = %d", + dmc_int_rqt.opcode, + dmc_int_rqt.data[0]); + return FALSE; + } + + self->update_model = DMC_UPDATE_MODEL_NONE; + if (dmc_int_rqt.data[0] == DMC_DEVICE_STATUS_UPDATE_PHASE_1_COMPLETE) { + self->update_model = DMC_UPDATE_MODEL_DOWNLOAD_TRIGGER; + } else if (dmc_int_rqt.data[0] == DMC_DEVICE_STATUS_FW_DOWNLOADED_UPDATE_PEND) { + self->update_model = DMC_UPDATE_MODEL_PENDING_RESET; + } else if (dmc_int_rqt.data[0] >= DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_FWCT) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid status code = %u", + dmc_int_rqt.data[0]); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_ccgx_dmc_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_ccgx_dmc_firmware_new(); + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + GBytes *custom_meta_blob = NULL; + gboolean custom_meta_exist = FALSE; + + /* parse all images */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* get custom meta record */ + custom_meta_blob = + fu_ccgx_dmc_firmware_get_custom_meta_record(FU_CCGX_DMC_FIRMWARE(firmware)); + if (custom_meta_blob) + if (g_bytes_get_size(custom_meta_blob) > 0) + custom_meta_exist = TRUE; + + /* check custom meta flag */ + if (self->dock_id.custom_meta_data_flag != custom_meta_exist) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "custom metadata mismatch"); + return NULL; + } + return g_steal_pointer(&firmware); +} + +static gboolean +fu_ccgx_dmc_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + gboolean manual_replug; + + manual_replug = + fu_device_has_private_flag(device, FU_CCGX_DMC_DEVICE_FLAG_HAS_MANUAL_REPLUG); + + if (fu_device_get_update_state(self) != FWUPD_UPDATE_STATE_SUCCESS) + return TRUE; + + if (manual_replug) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + } else { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + + if (self->update_model == DMC_UPDATE_MODEL_DOWNLOAD_TRIGGER) { + if (self->trigger_code > 0) { + if (!fu_ccgx_dmc_device_send_download_trigger(self, + self->trigger_code, + error)) { + if (manual_replug == FALSE) { + fu_device_remove_flag(device, + FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + g_prefix_error(error, "download trigger error: "); + return FALSE; + } + } + } else if (self->update_model == DMC_UPDATE_MODEL_PENDING_RESET) { + if (!fu_ccgx_dmc_device_send_sort_reset(self, manual_replug, error)) { + if (manual_replug == FALSE) { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + g_prefix_error(error, "soft reset error: "); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_setup(FuDevice *device, GError **error) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + DmcDockStatus dock_status = {0}; + DmcDockIdentity dock_id = {0}; + guint32 version_raw = 0; + g_autofree gchar *version = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_ccgx_dmc_device_parent_class)->setup(device, error)) + return FALSE; + + /* get dock identity */ + if (!fu_ccgx_dmc_device_get_dock_id(self, &dock_id, error)) + return FALSE; + + /* store dock identity */ + if (!fu_memcpy_safe((guint8 *)&self->dock_id, + sizeof(DmcDockIdentity), + 0x0, /* dst */ + (guint8 *)&dock_id, + sizeof(DmcDockIdentity), + 0, /* src */ + sizeof(DmcDockIdentity), + error)) + return FALSE; + + /* get dock status */ + if (!fu_ccgx_dmc_device_get_dock_status(self, &dock_status, error)) + return FALSE; + + /* set composite version */ + version_raw = dock_status.composite_version; + version = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(FU_DEVICE(self), version); + fu_device_set_version_raw(FU_DEVICE(self), version_raw); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + + if (dock_id.custom_meta_data_flag > 0) + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + else + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + + return TRUE; +} + +static gboolean +fu_ccgx_dmc_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); + guint64 tmp; + + if (g_strcmp0(key, "CcgxDmcTriggerCode") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->trigger_code = tmp; + return TRUE; + } + if (g_strcmp0(key, "CcgxImageKind") == 0) { + self->fw_image_type = fu_ccgx_fw_image_type_from_string(value); + if (self->fw_image_type != FW_IMAGE_TYPE_UNKNOWN) + return TRUE; + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid CcgxImageKind"); + return FALSE; + } + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported"); + return FALSE; +} + +static void +fu_ccgx_dmc_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); /* actually 0, 20, 0, 80! */ + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 75, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 25, "reload"); +} + +static void +fu_ccgx_dmc_device_init(FuCcgxDmcDevice *self) +{ + self->ep_intr_in = DMC_INTERRUPT_PIPE_ID; + self->ep_bulk_out = DMC_BULK_PIPE_ID; + fu_device_add_protocol(FU_DEVICE(self), "com.cypress.ccgx.dmc"); + fu_device_add_protocol(FU_DEVICE(self), "com.infineon.ccgx.dmc"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG); + fu_device_register_private_flag(FU_DEVICE(self), + FU_CCGX_DMC_DEVICE_FLAG_HAS_MANUAL_REPLUG, + "has-manual-replug"); +} + +static void +fu_ccgx_dmc_device_class_init(FuCcgxDmcDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_ccgx_dmc_device_to_string; + klass_device->write_firmware = fu_ccgx_dmc_write_firmware; + klass_device->prepare_firmware = fu_ccgx_dmc_device_prepare_firmware; + klass_device->attach = fu_ccgx_dmc_device_attach; + klass_device->setup = fu_ccgx_dmc_device_setup; + klass_device->set_quirk_kv = fu_ccgx_dmc_device_set_quirk_kv; + klass_device->set_progress = fu_ccgx_dmc_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-device.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..196e4209cf2b0c8991883df7457f49498abd8d13 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CCGX_DMC_DEVICE (fu_ccgx_dmc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCcgxDmcDevice, fu_ccgx_dmc_device, FU, CCGX_DMC_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-firmware.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..26e08f291492e8c805f97c008fba924f44dd6193 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-firmware.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-ccgx-common.h" +#include "fu-ccgx-dmc-common.h" +#include "fu-ccgx-dmc-firmware.h" + +struct _FuCcgxDmcFirmware { + FuFirmwareClass parent_instance; + GPtrArray *image_records; + GBytes *fwct_blob; + GBytes *custom_meta_blob; + guint32 row_data_offset_start; + guint32 fw_data_size; +}; + +G_DEFINE_TYPE(FuCcgxDmcFirmware, fu_ccgx_dmc_firmware, FU_TYPE_FIRMWARE) + +static void +fu_ccgx_dmc_firmware_record_free(FuCcgxDmcFirmwareRecord *rcd) +{ + if (rcd->seg_records != NULL) + g_ptr_array_unref(rcd->seg_records); + g_free(rcd); +} + +static void +fu_ccgx_dmc_firmware_segment_record_free(FuCcgxDmcFirmwareSegmentRecord *rcd) +{ + if (rcd->data_records != NULL) + g_ptr_array_unref(rcd->data_records); + g_free(rcd); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCcgxDmcFirmwareRecord, fu_ccgx_dmc_firmware_record_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCcgxDmcFirmwareSegmentRecord, + fu_ccgx_dmc_firmware_segment_record_free) + +GPtrArray * +fu_ccgx_dmc_firmware_get_image_records(FuCcgxDmcFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), NULL); + return self->image_records; +} + +GBytes * +fu_ccgx_dmc_firmware_get_fwct_record(FuCcgxDmcFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), NULL); + return self->fwct_blob; +} + +GBytes * +fu_ccgx_dmc_firmware_get_custom_meta_record(FuCcgxDmcFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), NULL); + return self->custom_meta_blob; +} + +guint32 +fu_ccgx_dmc_firmware_get_fw_data_size(FuCcgxDmcFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), 0); + return self->fw_data_size; +} + +static void +fu_ccgx_dmc_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kx(bn, "fw_data_size", self->fw_data_size); + fu_xmlb_builder_insert_kx(bn, "image_records", self->image_records->len); + } +} + +static gboolean +fu_ccgx_dmc_firmware_parse_segment(FuFirmware *firmware, + const guint8 *buf, + gsize bufsz, + FuCcgxDmcFirmwareRecord *img_rcd, + gsize *seg_off, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); + gsize row_off; + g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA256); + + /* set row data offset in current image */ + row_off = self->row_data_offset_start + img_rcd->img_offset; + + /* parse segment in image */ + img_rcd->seg_records = + g_ptr_array_new_with_free_func((GFreeFunc)fu_ccgx_dmc_firmware_segment_record_free); + for (guint32 i = 0; i < img_rcd->num_img_segments; i++) { + guint16 row_size_bytes = 0; + g_autofree guint8 *row_buf = NULL; + g_autoptr(FuCcgxDmcFirmwareSegmentRecord) seg_rcd = NULL; + + /* read segment info */ + seg_rcd = g_new0(FuCcgxDmcFirmwareSegmentRecord, 1); + if (!fu_memread_uint16_safe(buf, + bufsz, + *seg_off + + G_STRUCT_OFFSET(FwctSegmentationInfo, start_row), + &seg_rcd->start_row, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + *seg_off + + G_STRUCT_OFFSET(FwctSegmentationInfo, num_rows), + &seg_rcd->num_rows, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* calculate actual row size */ + row_size_bytes = img_rcd->row_size * 64; + + /* create data record array in segment record */ + seg_rcd->data_records = + g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + + /* read row data in segment */ + row_buf = g_malloc0(row_size_bytes); + for (int row = 0; row < seg_rcd->num_rows; row++) { + g_autoptr(GBytes) data_rcd = NULL; + + /* read row data */ + if (!fu_memcpy_safe(row_buf, + row_size_bytes, + 0x0, /* dst */ + buf, + bufsz, + row_off, /* src */ + row_size_bytes, + error)) { + g_prefix_error(error, "failed to read row data: "); + return FALSE; + } + + /* update hash */ + g_checksum_update(csum, (guchar *)row_buf, row_size_bytes); + + /* add row data to data record */ + data_rcd = g_bytes_new(row_buf, row_size_bytes); + g_ptr_array_add(seg_rcd->data_records, g_steal_pointer(&data_rcd)); + + /* increment row data offset */ + row_off += row_size_bytes; + } + + /* add segment record to segment array */ + g_ptr_array_add(img_rcd->seg_records, g_steal_pointer(&seg_rcd)); + + /* increment segment info offset */ + *seg_off += sizeof(FwctSegmentationInfo); + } + + /* check checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 csumbuf[DMC_HASH_SIZE] = {0x0}; + gsize csumbufsz = sizeof(csumbuf); + g_checksum_get_digest(csum, csumbuf, &csumbufsz); + if (memcmp(csumbuf, img_rcd->img_digest, DMC_HASH_SIZE) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid hash"); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ccgx_dmc_firmware_parse_image(FuFirmware *firmware, + guint8 image_count, + const guint8 *buf, + gsize bufsz, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); + gsize img_off = sizeof(FwctInfo); + gsize seg_off = sizeof(FwctInfo) + image_count * sizeof(FwctImageInfo); + + /* set initial segment info offset */ + for (guint32 i = 0; i < image_count; i++) { + g_autoptr(FuCcgxDmcFirmwareRecord) img_rcd = NULL; + + /* read image info */ + img_rcd = g_new0(FuCcgxDmcFirmwareRecord, 1); + if (!fu_memread_uint8_safe(buf, + bufsz, + img_off + G_STRUCT_OFFSET(FwctImageInfo, row_size), + &img_rcd->row_size, + error)) + return FALSE; + if (img_rcd->row_size == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid row size 0x%x", + img_rcd->row_size); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + img_off + G_STRUCT_OFFSET(FwctImageInfo, img_offset), + &img_rcd->img_offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, + bufsz, + img_off + + G_STRUCT_OFFSET(FwctImageInfo, num_img_segments), + &img_rcd->num_img_segments, + error)) + return FALSE; + if (img_rcd->num_img_segments == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid segment number = %d", + img_rcd->num_img_segments); + return FALSE; + } + if (!fu_memcpy_safe((guint8 *)&img_rcd->img_digest, + sizeof(img_rcd->img_digest), + 0x0, /* dst */ + buf, + bufsz, + img_off + G_STRUCT_OFFSET(FwctImageInfo, img_digest), /* src */ + sizeof(img_rcd->img_digest), + error)) + return FALSE; + + /* parse segment */ + if (!fu_ccgx_dmc_firmware_parse_segment(firmware, + buf, + bufsz, + img_rcd, + &seg_off, + flags, + error)) + return FALSE; + + /* add image record to image record array */ + g_ptr_array_add(self->image_records, g_steal_pointer(&img_rcd)); + + /* increment image offset */ + img_off += sizeof(FwctImageInfo); + } + + return TRUE; +} + +static gboolean +fu_ccgx_dmc_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, + &magic, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != DMC_FWCT_SIGN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid signature, expected 0x%04X got 0x%04X", + (guint32)DMC_FWCT_SIGN, + (guint32)magic); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ccgx_dmc_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); + gsize bufsz = 0; + guint16 hdr_size = 0; + guint16 mdbufsz = 0; + guint32 hdr_composite_version = 0; + guint8 hdr_image_count = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) img = fu_firmware_new_from_bytes(fw); + + /* check fwct size */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FwctInfo, size), + &hdr_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (hdr_size > DMC_FWCT_MAX_SIZE || hdr_size == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid dmc fwct size, expected <= 0x%x, got 0x%x", + (guint)DMC_FWCT_MAX_SIZE, + (guint)hdr_size); + return FALSE; + } + + /* set version */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FwctInfo, composite_version), + &hdr_composite_version, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (hdr_composite_version != 0) { + g_autofree gchar *ver = NULL; + ver = fu_version_from_uint32(hdr_composite_version, FWUPD_VERSION_FORMAT_QUAD); + fu_firmware_set_version(firmware, ver); + fu_firmware_set_version_raw(firmware, hdr_composite_version); + } + + /* read fwct data */ + self->fwct_blob = fu_bytes_new_offset(fw, offset, hdr_size, error); + if (self->fwct_blob == NULL) + return FALSE; + + /* create custom meta binary */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + hdr_size, + &mdbufsz, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read metadata size: "); + return FALSE; + } + if (mdbufsz > 0) { + self->custom_meta_blob = + fu_bytes_new_offset(fw, offset + hdr_size + 2, mdbufsz, error); + if (self->custom_meta_blob == NULL) + return FALSE; + } + + /* set row data start offset */ + self->row_data_offset_start = hdr_size + DMC_CUSTOM_META_LENGTH_FIELD_SIZE + mdbufsz; + self->fw_data_size = bufsz - self->row_data_offset_start; + + /* parse image */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FwctInfo, image_count), + &hdr_image_count, + error)) + return FALSE; + if (!fu_ccgx_dmc_firmware_parse_image(firmware, hdr_image_count, buf, bufsz, flags, error)) + return FALSE; + + /* add something, although we'll use the records for the update */ + fu_firmware_set_addr(img, 0x0); + fu_firmware_add_image(firmware, img); + return TRUE; +} + +static GBytes * +fu_ccgx_dmc_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + /* add header */ + fu_byte_array_append_uint32(buf, DMC_FWCT_SIGN, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16( + buf, + sizeof(FwctInfo) + + (images->len * (sizeof(FwctImageInfo) + sizeof(FwctSegmentationInfo))), /* size */ + G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, 0x0); /* checksum, unused */ + fu_byte_array_append_uint8(buf, 0x2); /* version */ + fu_byte_array_append_uint8(buf, 0x3); /* custom_meta_type */ + fu_byte_array_append_uint8(buf, 0x1); /* cdtt_version */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* vid, unused */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* pid, unused */ + fu_byte_array_append_uint16(buf, 0x1, G_LITTLE_ENDIAN); /* device_id */ + for (guint j = 0; j < 16; j++) + fu_byte_array_append_uint8(buf, 0x0); /* reserv0 */ + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, images->len); + for (guint j = 0; j < 3; j++) + fu_byte_array_append_uint8(buf, 0x0); /* reserv1 */ + + /* add image headers */ + for (guint i = 0; i < images->len; i++) { + fu_byte_array_append_uint8(buf, 0x2); /* device_type, unknown */ + fu_byte_array_append_uint8(buf, 0x1); /* img_type, unknown */ + fu_byte_array_append_uint8(buf, 0x0); /* comp_id, unknown */ + fu_byte_array_append_uint8(buf, 0x1); /* row_size, multiplier for num_rows */ + for (guint j = 0; j < 4; j++) + fu_byte_array_append_uint8(buf, 0x0); /* reserv0 */ + fu_byte_array_append_uint32(buf, + 0x330006d2, + G_LITTLE_ENDIAN); /* fw_version, hardcoded */ + fu_byte_array_append_uint32(buf, + 0x14136161, + G_LITTLE_ENDIAN); /* app_version, hardcoded */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* start of element data */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* img_size */ + for (guint j = 0; j < 32; j++) + fu_byte_array_append_uint8(buf, 0x0); /* img_digest */ + fu_byte_array_append_uint8(buf, 0x1); /* num_img_segments */ + for (guint j = 0; j < 3; j++) + fu_byte_array_append_uint8(buf, 0x0); /* reserv1 */ + } + + /* add segments */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GBytes) img_bytes = fu_firmware_get_bytes(img, error); + if (img_bytes == NULL) + return NULL; + chunks = fu_chunk_array_new_from_bytes(img_bytes, 0x0, 0x0, 64); + fu_byte_array_append_uint8(buf, 0x0); /* img_id */ + fu_byte_array_append_uint8(buf, 0x0); /* type */ + fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* start_row, unknown */ + fu_byte_array_append_uint16(buf, + MAX(chunks->len, 1), + G_LITTLE_ENDIAN); /* num_rows */ + for (guint j = 0; j < 2; j++) + fu_byte_array_append_uint8(buf, 0x0); /* reserv0 */ + } + + /* metadata */ + fu_byte_array_append_uint16(buf, 0x1, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, 0xff); + + /* add image headers */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + gsize csumbufsz = DMC_HASH_SIZE; + gsize img_offset = sizeof(FwctInfo) + (i * sizeof(FwctImageInfo)); + guint8 csumbuf[DMC_HASH_SIZE] = {0x0}; + g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA256); + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GBytes) img_padded = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + img_bytes = fu_firmware_get_bytes(img, error); + if (img_bytes == NULL) + return NULL; + chunks = fu_chunk_array_new_from_bytes(img_bytes, 0x0, 0x0, 64); + img_padded = fu_bytes_pad(img_bytes, MAX(chunks->len, 1) * 64); + fu_byte_array_append_bytes(buf, img_padded); + g_checksum_update(csum, + (const guchar *)g_bytes_get_data(img_padded, NULL), + g_bytes_get_size(img_padded)); + g_checksum_get_digest(csum, csumbuf, &csumbufsz); + + /* update checksum */ + if (!fu_memcpy_safe(buf->data, + buf->len, /* dst */ + img_offset + G_STRUCT_OFFSET(FwctImageInfo, img_digest), + csumbuf, + sizeof(csumbuf), + 0x0, /* src */ + sizeof(csumbuf), + error)) + return NULL; + } + + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_ccgx_dmc_firmware_init(FuCcgxDmcFirmware *self) +{ + self->image_records = + g_ptr_array_new_with_free_func((GFreeFunc)fu_ccgx_dmc_firmware_record_free); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_ccgx_dmc_firmware_finalize(GObject *object) +{ + FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(object); + + if (self->fwct_blob != NULL) + g_bytes_unref(self->fwct_blob); + if (self->custom_meta_blob != NULL) + g_bytes_unref(self->custom_meta_blob); + if (self->image_records != NULL) + g_ptr_array_unref(self->image_records); + + G_OBJECT_CLASS(fu_ccgx_dmc_firmware_parent_class)->finalize(object); +} + +static void +fu_ccgx_dmc_firmware_class_init(FuCcgxDmcFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_ccgx_dmc_firmware_finalize; + klass_firmware->check_magic = fu_ccgx_dmc_firmware_check_magic; + klass_firmware->parse = fu_ccgx_dmc_firmware_parse; + klass_firmware->write = fu_ccgx_dmc_firmware_write; + klass_firmware->export = fu_ccgx_dmc_firmware_export; +} + +FuFirmware * +fu_ccgx_dmc_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_CCGX_DMC_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-firmware.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..1453c0df7ab46950b0ab58275237d97d46e851c9 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-dmc-firmware.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-ccgx-dmc-common.h" + +#define FU_TYPE_CCGX_DMC_FIRMWARE (fu_ccgx_dmc_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuCcgxDmcFirmware, fu_ccgx_dmc_firmware, FU, CCGX_DMC_FIRMWARE, FuFirmware) + +typedef struct { + guint16 start_row; + guint16 num_rows; + GPtrArray *data_records; +} FuCcgxDmcFirmwareSegmentRecord; + +typedef struct { + guint8 row_size; + guint32 img_offset; + guint8 img_digest[32]; + guint8 num_img_segments; + GPtrArray *seg_records; +} FuCcgxDmcFirmwareRecord; + +FuFirmware * +fu_ccgx_dmc_firmware_new(void); +GPtrArray * +fu_ccgx_dmc_firmware_get_image_records(FuCcgxDmcFirmware *self); +GBytes * +fu_ccgx_dmc_firmware_get_fwct_record(FuCcgxDmcFirmware *self); +GBytes * +fu_ccgx_dmc_firmware_get_custom_meta_record(FuCcgxDmcFirmware *self); +guint32 +fu_ccgx_dmc_firmware_get_fw_data_size(FuCcgxDmcFirmware *self); diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-firmware.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..d96a667341e05da7a893b53de138e32b68d9138a --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-firmware.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-ccgx-common.h" +#include "fu-ccgx-firmware.h" + +struct _FuCcgxFirmware { + FuFirmwareClass parent_instance; + GPtrArray *records; + guint16 app_type; + guint16 silicon_id; + FWMode fw_mode; +}; + +G_DEFINE_TYPE(FuCcgxFirmware, fu_ccgx_firmware, FU_TYPE_FIRMWARE) + +/* offset stored application version for CCGx */ +#define CCGX_APP_VERSION_OFFSET 228 /* 128+64+32+4 */ + +#define FU_CCGX_FIRMWARE_TOKENS_MAX 100000 /* lines */ + +GPtrArray * +fu_ccgx_firmware_get_records(FuCcgxFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_FIRMWARE(self), NULL); + return self->records; +} + +guint16 +fu_ccgx_firmware_get_app_type(FuCcgxFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_FIRMWARE(self), 0); + return self->app_type; +} + +guint16 +fu_ccgx_firmware_get_silicon_id(FuCcgxFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_FIRMWARE(self), 0); + return self->silicon_id; +} + +FWMode +fu_ccgx_firmware_get_fw_mode(FuCcgxFirmware *self) +{ + g_return_val_if_fail(FU_IS_CCGX_FIRMWARE(self), 0); + return self->fw_mode; +} + +static void +fu_ccgx_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuCcgxFirmware *self = FU_CCGX_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "silicon_id", self->silicon_id); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kx(bn, "app_type", self->app_type); + fu_xmlb_builder_insert_kx(bn, "records", self->records->len); + fu_xmlb_builder_insert_kv(bn, "fw_mode", fu_ccgx_fw_mode_to_string(self->fw_mode)); + } +} + +static void +fu_ccgx_firmware_record_free(FuCcgxFirmwareRecord *rcd) +{ + if (rcd->data != NULL) + g_bytes_unref(rcd->data); + g_free(rcd); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCcgxFirmwareRecord, fu_ccgx_firmware_record_free) + +static gboolean +fu_ccgx_firmware_add_record(FuCcgxFirmware *self, + GString *token, + FwupdInstallFlags flags, + GError **error) +{ + guint16 buflen; + guint8 checksum_calc = 0; + g_autoptr(FuCcgxFirmwareRecord) rcd = NULL; + g_autoptr(GByteArray) data = g_byte_array_new(); + + /* this is not in the specification, but exists in reality */ + if (token->str[0] == ':') + g_string_erase(token, 0, 1); + + /* parse according to https://community.cypress.com/docs/DOC-10562 */ + rcd = g_new0(FuCcgxFirmwareRecord, 1); + if (!fu_firmware_strparse_uint8_safe(token->str, token->len, 0, &rcd->array_id, error)) + return FALSE; + if (!fu_firmware_strparse_uint16_safe(token->str, token->len, 2, &rcd->row_number, error)) + return FALSE; + if (!fu_firmware_strparse_uint16_safe(token->str, token->len, 6, &buflen, error)) + return FALSE; + if (token->len != ((gsize)buflen * 2) + 12) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid record, expected %u chars, got %u", + (guint)(buflen * 2) + 12, + (guint)token->len); + return FALSE; + } + + /* parse payload, adding checksum */ + for (guint i = 0; i < buflen; i++) { + guint8 tmp = 0; + if (!fu_firmware_strparse_uint8_safe(token->str, + token->len, + 10 + (i * 2), + &tmp, + error)) + return FALSE; + fu_byte_array_append_uint8(data, tmp); + checksum_calc += tmp; + } + rcd->data = g_byte_array_free_to_bytes(g_steal_pointer(&data)); + + /* verify 2s complement checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 checksum_file; + if (!fu_firmware_strparse_uint8_safe(token->str, + token->len, + (buflen * 2) + 10, + &checksum_file, + error)) + return FALSE; + for (guint i = 0; i < 5; i++) { + guint8 tmp = 0; + if (!fu_firmware_strparse_uint8_safe(token->str, + token->len, + i * 2, + &tmp, + error)) + return FALSE; + checksum_calc += tmp; + } + checksum_calc = 1 + ~checksum_calc; + if (checksum_file != checksum_calc) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, got %02x, expected %02x", + checksum_calc, + checksum_file); + return FALSE; + } + } + + /* success */ + g_ptr_array_add(self->records, g_steal_pointer(&rcd)); + return TRUE; +} + +static gboolean +fu_ccgx_firmware_parse_md_block(FuCcgxFirmware *self, FwupdInstallFlags flags, GError **error) +{ + FuCcgxFirmwareRecord *rcd; + CCGxMetaData metadata; + const guint8 *buf; + gsize bufsz = 0; + gsize md_offset = 0; + guint32 fw_size = 0; + guint32 rcd_version_idx = 0; + guint32 version = 0; + guint8 checksum_calc = 0; + + /* sanity check */ + if (self->records->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no records added to image"); + return FALSE; + } + + /* read metadata from correct ofsset */ + rcd = g_ptr_array_index(self->records, self->records->len - 1); + buf = g_bytes_get_data(rcd->data, &bufsz); + if (bufsz == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid buffer size"); + return FALSE; + } + switch (bufsz) { + case 0x80: + md_offset = 0x40; + break; + case 0x100: + md_offset = 0xC0; + break; + default: + break; + } + if (!fu_memcpy_safe((guint8 *)&metadata, + sizeof(metadata), + 0x0, /* dst */ + buf, + bufsz, + md_offset, + sizeof(metadata), + error)) /* src */ + return FALSE; + + /* sanity check */ + if (metadata.metadata_valid != CCGX_METADATA_VALID_SIG) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid metadata 0x@%x, expected 0x%04x, got 0x%04x", + (guint)md_offset, + (guint)CCGX_METADATA_VALID_SIG, + (guint)metadata.metadata_valid); + return FALSE; + } + for (guint i = 0; i < self->records->len - 1; i++) { + rcd = g_ptr_array_index(self->records, i); + checksum_calc += fu_sum8_bytes(rcd->data); + fw_size += g_bytes_get_size(rcd->data); + } + if (fw_size != metadata.fw_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware size invalid, got %02x, expected %02x", + fw_size, + metadata.fw_size); + return FALSE; + } + checksum_calc = 1 + ~checksum_calc; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (metadata.fw_checksum != checksum_calc) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, got %02x, expected %02x", + checksum_calc, + metadata.fw_checksum); + return FALSE; + } + } + + /* get version if enough data */ + rcd_version_idx = CCGX_APP_VERSION_OFFSET / bufsz; + if (rcd_version_idx < self->records->len) { + g_autofree gchar *version_str = NULL; + rcd = g_ptr_array_index(self->records, rcd_version_idx); + buf = g_bytes_get_data(rcd->data, &bufsz); + if (bufsz == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "metadata record had zero size"); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + CCGX_APP_VERSION_OFFSET % bufsz, + &version, + G_LITTLE_ENDIAN, + error)) + return FALSE; + self->app_type = version & 0xffff; + version_str = fu_ccgx_version_to_string(version); + fu_firmware_set_version(FU_FIRMWARE(self), version_str); + fu_firmware_set_version_raw(FU_FIRMWARE(self), version); + } + + /* work out the FWMode */ + if (self->records->len > 0) { + rcd = g_ptr_array_index(self->records, self->records->len - 1); + if ((rcd->row_number & 0xFF) == 0xFF) /* last row */ + self->fw_mode = FW_MODE_FW1; + if ((rcd->row_number & 0xFF) == 0xFE) /* penultimate row */ + self->fw_mode = FW_MODE_FW2; + } + return TRUE; +} + +typedef struct { + FuCcgxFirmware *self; + FwupdInstallFlags flags; +} FuCcgxFirmwareTokenHelper; + +static gboolean +fu_ccgx_firmware_tokenize_cb(GString *token, guint token_idx, gpointer user_data, GError **error) +{ + FuCcgxFirmwareTokenHelper *helper = (FuCcgxFirmwareTokenHelper *)user_data; + FuCcgxFirmware *self = FU_CCGX_FIRMWARE(helper->self); + + /* sanity check */ + if (token_idx > FU_CCGX_FIRMWARE_TOKENS_MAX) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file has too many lines"); + return FALSE; + } + + /* remove WIN32 line endings */ + g_strdelimit(token->str, "\r\x1a", '\0'); + token->len = strlen(token->str); + + /* header */ + if (token_idx == 0) { + guint32 device_id = 0; + if (token->len != 12) { + g_autofree gchar *strsafe = fu_strsafe(token->str, 12); + if (strsafe != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid header, expected == 12 chars -- got %s", + strsafe); + return FALSE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid header, expected == 12 chars"); + return FALSE; + } + if (!fu_firmware_strparse_uint32_safe(token->str, token->len, 0, &device_id, error)) + return FALSE; + self->silicon_id = device_id >> 16; + return TRUE; + } + + /* ignore blank lines */ + if (token->len == 0) + return TRUE; + + /* parse record */ + if (!fu_ccgx_firmware_add_record(self, token, helper->flags, error)) { + g_prefix_error(error, "error on line %u: ", token_idx + 1); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ccgx_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxFirmware *self = FU_CCGX_FIRMWARE(firmware); + FuCcgxFirmwareTokenHelper helper = {.self = self, .flags = flags}; + + /* tokenize */ + if (!fu_strsplit_full(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + "\n", + fu_ccgx_firmware_tokenize_cb, + &helper, + error)) + return FALSE; + + /* address is first data entry */ + if (self->records->len > 0) { + FuCcgxFirmwareRecord *rcd = g_ptr_array_index(self->records, 0); + fu_firmware_set_addr(firmware, rcd->row_number); + } + + /* parse metadata block */ + if (!fu_ccgx_firmware_parse_md_block(self, flags, error)) { + g_prefix_error(error, "failed to parse metadata: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_ccgx_firmware_write_record(GString *str, + guint8 array_id, + guint8 row_number, + const guint8 *buf, + guint16 bufsz) +{ + guint8 checksum_calc = 0xff; + g_autoptr(GString) datastr = g_string_new(NULL); + + /* offset for bootloader perhaps? */ + row_number += 0xE; + + checksum_calc += array_id; + checksum_calc += row_number; + checksum_calc += bufsz & 0xff; + checksum_calc += (bufsz >> 8) & 0xff; + for (guint j = 0; j < bufsz; j++) { + g_string_append_printf(datastr, "%02X", buf[j]); + checksum_calc += buf[j]; + } + g_string_append_printf(str, + ":%02X%04X%04X%s%02X\n", + array_id, + row_number, + bufsz, + datastr->str, + (guint)((guint8)~checksum_calc)); +} + +static GBytes * +fu_ccgx_firmware_write(FuFirmware *firmware, GError **error) +{ + FuCcgxFirmware *self = FU_CCGX_FIRMWARE(firmware); + CCGxMetaData metadata = {0x0}; + gsize fwbufsz = 0; + guint8 checksum_img = 0xff; + const guint8 *fwbuf; + g_autoptr(GByteArray) mdbuf = g_byte_array_new(); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* header record */ + g_string_append_printf(str, + "%04X%04X%02X%02X\n", + self->silicon_id, + (guint)0x11AF, /* SiliconID */ + (guint)0x0, /* SiliconRev */ + (guint)0x0); /* Checksum, or 0x0 */ + + /* add image in chunks */ + fw = fu_firmware_get_bytes_with_patches(firmware, error); + if (fw == NULL) + return NULL; + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, 0x100); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + fu_ccgx_firmware_write_record(str, + 0x0, + i, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk)); + } + + /* add metadata */ + fwbuf = g_bytes_get_data(fw, &fwbufsz); + for (guint j = 0; j < fwbufsz; j++) + checksum_img += fwbuf[j]; + metadata.fw_checksum = ~checksum_img; + metadata.fw_entry = 0x0; /* unknown */ + metadata.last_boot_row = 0x13; + metadata.fw_size = fwbufsz; + metadata.metadata_valid = CCGX_METADATA_VALID_SIG; + metadata.boot_seq = 0x0; /* unknown */ + + /* copy into place */ + fu_byte_array_set_size(mdbuf, 0x80, 0x00); + if (!fu_memcpy_safe(mdbuf->data, + mdbuf->len, + 0x40, /* dst */ + (const guint8 *)&metadata, + sizeof(metadata), + 0x0, /* src */ + sizeof(metadata), + error)) + return NULL; + fu_ccgx_firmware_write_record(str, + 0x0, + 0xFE, /* FW2: penultimate row */ + mdbuf->data, + mdbuf->len); + + return g_string_free_to_bytes(g_steal_pointer(&str)); +} + +static gboolean +fu_ccgx_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuCcgxFirmware *self = FU_CCGX_FIRMWARE(firmware); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "silicon_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->silicon_id = tmp; + + /* success */ + return TRUE; +} + +static void +fu_ccgx_firmware_init(FuCcgxFirmware *self) +{ + self->records = g_ptr_array_new_with_free_func((GFreeFunc)fu_ccgx_firmware_record_free); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_ccgx_firmware_finalize(GObject *object) +{ + FuCcgxFirmware *self = FU_CCGX_FIRMWARE(object); + g_ptr_array_unref(self->records); + G_OBJECT_CLASS(fu_ccgx_firmware_parent_class)->finalize(object); +} + +static void +fu_ccgx_firmware_class_init(FuCcgxFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_ccgx_firmware_finalize; + klass_firmware->parse = fu_ccgx_firmware_parse; + klass_firmware->write = fu_ccgx_firmware_write; + klass_firmware->build = fu_ccgx_firmware_build; + klass_firmware->export = fu_ccgx_firmware_export; +} + +FuFirmware * +fu_ccgx_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_CCGX_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-firmware.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..1d225e4b7f55e2169c9796f71aace052d7d1468e --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-firmware.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-ccgx-common.h" + +#define FU_TYPE_CCGX_FIRMWARE (fu_ccgx_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuCcgxFirmware, fu_ccgx_firmware, FU, CCGX_FIRMWARE, FuFirmware) + +typedef struct { + guint8 array_id; + guint16 row_number; + GBytes *data; +} FuCcgxFirmwareRecord; + +FuFirmware * +fu_ccgx_firmware_new(void); +GPtrArray * +fu_ccgx_firmware_get_records(FuCcgxFirmware *self); +guint16 +fu_ccgx_firmware_get_app_type(FuCcgxFirmware *self); +guint16 +fu_ccgx_firmware_get_silicon_id(FuCcgxFirmware *self); +FWMode +fu_ccgx_firmware_get_fw_mode(FuCcgxFirmware *self); diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hid-device.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hid-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b15eb6e5924c72aacf6d3b37122993c6ab1a54ce --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hid-device.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ccgx-hid-device.h" + +struct _FuCcgxHidDevice { + FuHidDevice parent_instance; +}; + +G_DEFINE_TYPE(FuCcgxHidDevice, fu_ccgx_hid_device, FU_TYPE_HID_DEVICE) + +#define FU_CCGX_HID_DEVICE_TIMEOUT 5000 /* ms */ +#define FU_CCGX_HID_DEVICE_RETRY_DELAY 30 /* ms */ +#define FU_CCGX_HID_DEVICE_RETRY_CNT 5 + +static gboolean +fu_ccgx_hid_device_enable_hpi_mode_cb(FuDevice *device, gpointer user_data, GError **error) +{ + guint8 buf[5] = {0xEE, 0xBC, 0xA6, 0xB9, 0xA8}; + + if (!fu_hid_device_set_report(FU_HID_DEVICE(device), + buf[0], + buf, + sizeof(buf), + FU_CCGX_HID_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "switch to HPI mode error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + if (!fu_device_retry(device, + fu_ccgx_hid_device_enable_hpi_mode_cb, + FU_CCGX_HID_DEVICE_RETRY_CNT, + NULL, + error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_ccgx_hid_device_setup(FuDevice *device, GError **error) +{ + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_ccgx_hid_device_parent_class)->setup(device, error)) + return FALSE; + + /* This seems insane... but we need to switch the device from HID + * mode to HPI mode at startup. The device continues to function + * exactly as before and no user-visible effects are noted */ + if (!fu_device_retry(device, + fu_ccgx_hid_device_enable_hpi_mode_cb, + FU_CCGX_HID_DEVICE_RETRY_CNT, + NULL, + error)) + return FALSE; + + /* never add this device, the daemon does not expect the device to + * disconnect before it is added */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device is replugging into HPI mode"); + return FALSE; +} + +static void +fu_ccgx_hid_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_ccgx_hid_device_init(FuCcgxHidDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.cypress.ccgx"); + fu_device_add_protocol(FU_DEVICE(self), "com.infineon.ccgx"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WILL_DISAPPEAR); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_retry_set_delay(FU_DEVICE(self), FU_CCGX_HID_DEVICE_RETRY_DELAY); +} + +static void +fu_ccgx_hid_device_class_init(FuCcgxHidDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->detach = fu_ccgx_hid_device_detach; + klass_device->setup = fu_ccgx_hid_device_setup; + klass_device->set_progress = fu_ccgx_hid_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hid-device.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hid-device.h new file mode 100644 index 0000000000000000000000000000000000000000..64a43054cba685cc99386e0f0da6659ba9a21bf1 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hid-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CCGX_HID_DEVICE (fu_ccgx_hid_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCcgxHidDevice, fu_ccgx_hid_device, FU, CCGX_HID_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-common.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-common.c new file mode 100644 index 0000000000000000000000000000000000000000..2b5666b57cf22b3debf0e6cde03f547d22030600 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-common.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ccgx-hpi-common.h" + +const gchar * +fu_ccgx_pd_resp_to_string(CyPDResp val) +{ + if (val == CY_PD_RESP_NO_RESPONSE) + return "resp-no-response"; + if (val == CY_PD_RESP_SUCCESS) + return "resp-success"; + if (val == CY_PD_RESP_FLASH_DATA_AVAILABLE) + return "resp-flash-data-available"; + if (val == CY_PD_RESP_INVALID_COMMAND) + return "resp-invalid-command"; + if (val == CY_PD_RESP_COLLISION_DETECTED) + return "resp-collision-detected"; + if (val == CY_PD_RESP_FLASH_UPDATE_FAILED) + return "resp-flash-update-failed"; + if (val == CY_PD_RESP_INVALID_FW) + return "resp-invalid-fw"; + if (val == CY_PD_RESP_INVALID_ARGUMENTS) + return "resp-invalid-arguments"; + if (val == CY_PD_RESP_NOT_SUPPORTED) + return "resp-not-supported"; + if (val == CY_PD_RESP_TRANSACTION_FAILED) + return "resp-transaction-failed"; + if (val == CY_PD_RESP_PD_COMMAND_FAILED) + return "resp-pd-command-failed"; + if (val == CY_PD_RESP_UNDEFINED) + return "resp-undefined"; + if (val == CY_PD_RESP_RA_DETECT) + return "resp-ra-detect"; + if (val == CY_PD_RESP_RA_REMOVED) + return "resp-ra-removed"; + if (val == CY_PD_RESP_RESET_COMPLETE) + return "resp-reset-complete"; + if (val == CY_PD_RESP_MESSAGE_QUEUE_OVERFLOW) + return "resp-message-queue-overflow"; + if (val == CY_PD_RESP_OVER_CURRENT_DETECTED) + return "resp-over-current-detected"; + if (val == CY_PD_RESP_OVER_VOLTAGE_DETECTED) + return "resp-over-voltage-detected"; + if (val == CY_PD_RESP_TYPC_C_CONNECTED) + return "resp-typc-c-connected"; + if (val == CY_PD_RESP_TYPE_C_DISCONNECTED) + return "resp-type-c-disconnected"; + if (val == CY_PD_RESP_PD_CONTRACT_ESTABLISHED) + return "resp-pd-contract-established"; + if (val == CY_PD_RESP_DR_SWAP) + return "resp-dr-swap"; + if (val == CY_PD_RESP_PR_SWAP) + return "resp-pr-swap"; + if (val == CY_PD_RESP_VCON_SWAP) + return "resp-vcon-swap"; + if (val == CY_PD_RESP_PS_RDY) + return "resp-ps-rdy"; + if (val == CY_PD_RESP_GOTOMIN) + return "resp-gotomin"; + if (val == CY_PD_RESP_ACCEPT_MESSAGE) + return "resp-accept-message"; + if (val == CY_PD_RESP_REJECT_MESSAGE) + return "resp-reject-message"; + if (val == CY_PD_RESP_WAIT_MESSAGE) + return "resp-wait-message"; + if (val == CY_PD_RESP_HARD_RESET) + return "resp-hard-reset"; + if (val == CY_PD_RESP_VDM_RECEIVED) + return "resp-vdm-received"; + if (val == CY_PD_RESP_SRC_CAP_RCVD) + return "resp-src-cap-rcvd"; + if (val == CY_PD_RESP_SINK_CAP_RCVD) + return "resp-sink-cap-rcvd"; + if (val == CY_PD_RESP_DP_ALTERNATE_MODE) + return "resp-dp-alternate-mode"; + if (val == CY_PD_RESP_DP_DEVICE_CONNECTED) + return "resp-dp-device-connected"; + if (val == CY_PD_RESP_DP_DEVICE_NOT_CONNECTED) + return "resp-dp-device-not-connected"; + if (val == CY_PD_RESP_DP_SID_NOT_FOUND) + return "resp-dp-sid-not-found"; + if (val == CY_PD_RESP_MULTIPLE_SVID_DISCOVERED) + return "resp-multiple-svid-discovered"; + if (val == CY_PD_RESP_DP_FUNCTION_NOT_SUPPORTED) + return "resp-dp-function-not-supported"; + if (val == CY_PD_RESP_DP_PORT_CONFIG_NOT_SUPPORTED) + return "resp-dp-port-config-not-supported"; + if (val == CY_PD_HARD_RESET_SENT) + return "hard-reset-sent"; + if (val == CY_PD_SOFT_RESET_SENT) + return "soft-reset-sent"; + if (val == CY_PD_CABLE_RESET_SENT) + return "cable-reset-sent"; + if (val == CY_PD_SOURCE_DISBALED_STATE_ENTERED) + return "source-disbaled-state-entered"; + if (val == CY_PD_SENDER_RESPONSE_TIMER_TIMEOUT) + return "sender-response-timer-timeout"; + if (val == CY_PD_NO_VDM_RESPONSE_RECEIVED) + return "no-vdm-response-received"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-common.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-common.h new file mode 100644 index 0000000000000000000000000000000000000000..91ad150ba28275be45c3b4b8e3ba0f8d27f6893c --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-common.h @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define I2C_READ_WRITE_DELAY_US 10000 /* 10 msec */ + +#define CY_SCB_INDEX_POS 15 +#define CY_I2C_WRITE_COMMAND_POS 3 +#define CY_I2C_WRITE_COMMAND_LEN_POS 4 +#define CY_I2C_GET_STATUS_LEN 3 +#define CY_I2C_MODE_WRITE 1 +#define CY_I2C_MODE_READ 0 +#define CY_I2C_ERROR_BIT 1 +#define CY_I2C_ARBITRATION_ERROR_BIT (1 << 1) +#define CY_I2C_NAK_ERROR_BIT (1 << 2) +#define CY_I2C_BUS_ERROR_BIT (1 << 3) +#define CY_I2C_STOP_BIT_ERROR (1 << 4) +#define CY_I2C_BUS_BUSY_ERROR (1 << 5) +#define CY_I2C_ENABLE_PRECISE_TIMING 1 +#define CY_I2C_EVENT_NOTIFICATION_LEN 3 + +#define PD_I2C_TARGET_ADDRESS 0x08 + +/* timeout (ms) for USB I2C communication */ +#define FU_CCGX_HPI_WAIT_TIMEOUT 5000 + +/* max i2c frequency */ +#define FU_CCGX_HPI_FREQ 400000 + +typedef enum { + CY_GET_VERSION_CMD = 0xB0, /* get the version of the boot-loader + * value = 0, index = 0, length = 4; + * data_in = 32 bit version */ + CY_GET_SIGNATURE_CMD = 0xBD, /* get the signature of the firmware + * It is suppose to be 'CYUS' for normal firmware + * and 'CYBL' for Bootloader */ + CY_UART_GET_CONFIG_CMD = 0xC0, /* retrieve the 16 byte UART configuration information + * MS bit of value indicates the SCB index + * length = 16, data_in = 16 byte configuration */ + CY_UART_SET_CONFIG_CMD, /* update the 16 byte UART configuration information + * MS bit of value indicates the SCB index. + * length = 16, data_out = 16 byte configuration information */ + CY_SPI_GET_CONFIG_CMD, /* retrieve the 16 byte SPI configuration information + * MS bit of value indicates the SCB index + * length = 16, data_in = 16 byte configuration */ + CY_SPI_SET_CONFIG_CMD, /* update the 16 byte SPI configuration information + * MS bit of value indicates the SCB index + * length = 16, data_out = 16 byte configuration information */ + CY_I2C_GET_CONFIG_CMD, /* retrieve the 16 byte I2C configuration information + * MS bit of value indicates the SCB index + * length = 16, data_in = 16 byte configuration */ + CY_I2C_SET_CONFIG_CMD = + 0xC5, /* update the 16 byte I2C configuration information + * MS bit of value indicates the SCB index + * length = 16, data_out = 16 byte configuration information */ + CY_I2C_WRITE_CMD, /* perform I2C write operation + * value = bit0 - start, bit1 - stop, bit3 - start on idle, + * bits[14:8] - target address, bit15 - scbIndex. length = 0 the + * data is provided over the bulk endpoints */ + CY_I2C_READ_CMD, /* rerform I2C read operation. + * value = bit0 - start, bit1 - stop, bit2 - Nak last byte, + * bit3 - start on idle, bits[14:8] - target address, bit15 - scbIndex, + * length = 0. The data is provided over the bulk endpoints */ + CY_I2C_GET_STATUS_CMD, /* retrieve the I2C bus status. + * value = bit0 - 0: TX 1: RX, bit15 - scbIndex, length = 3, + * data_in = byte0: bit0 - flag, bit1 - bus_state, bit2 - SDA state, + * bit3 - TX underflow, bit4 - arbitration error, bit5 - NAK + * bit6 - bus error, + * byte[2:1] Data count remaining */ + CY_I2C_RESET_CMD, /* the command cleans up the I2C state machine and frees the bus + * value = bit0 - 0: TX path, 1: RX path; bit15 - scbIndex, + * length = 0 */ + CY_SPI_READ_WRITE_CMD = 0xCA, /* the command starts a read / write operation at SPI + * value = bit 0 - RX enable, bit 1 - TX enable, bit 15 - + * scbIndex; index = length of transfer */ + CY_SPI_RESET_CMD, /* the command resets the SPI pipes and allows it to receive new + * request + * value = bit 15 - scbIndex */ + CY_SPI_GET_STATUS_CMD, /* the command returns the current transfer status + * the count will match the TX pipe status at SPI end + * for completion of read, read all data + * at the USB end signifies the end of transfer + * value = bit 15 - scbIndex */ + CY_JTAG_ENABLE_CMD = 0xD0, /* enable JTAG module */ + CY_JTAG_DISABLE_CMD, /* disable JTAG module */ + CY_JTAG_READ_CMD, /* jtag read vendor command */ + CY_JTAG_WRITE_CMD, /* jtag write vendor command */ + CY_GPIO_GET_CONFIG_CMD = 0xD8, /* get the GPIO configuration */ + CY_GPIO_SET_CONFIG_CMD, /* set the GPIO configuration */ + CY_GPIO_GET_VALUE_CMD, /* get GPIO value */ + CY_GPIO_SET_VALUE_CMD, /* set the GPIO value */ + CY_PROG_USER_FLASH_CMD = 0xE0, /* program user flash area. The total space available is 512 + * bytes this can be accessed by the user from USB. The flash + * area address offset is from 0x0000 to 0x00200 and can be + * written to page wise (128 byte) */ + CY_READ_USER_FLASH_CMD, /* read user flash area. The total space available is 512 bytes + * this can be accessed by the user from USB. The flash area + * address offset is from 0x0000 to 0x00200 and can be written to + * page wise (128 byte) */ + CY_DEVICE_RESET_CMD = 0xE3, /* performs a device reset from firmware */ +} CyVendorCommand; + +typedef struct __attribute__((packed)) { + guint32 frequency; /* frequency of operation. Only valid values are 100KHz and 400KHz */ + guint8 target_address; /* target address to be used when in target mode */ + guint8 is_msb_first; /* whether to transmit most significant bit first */ + guint8 is_initiator; /* whether to block is to be configured as a initiator */ + guint8 s_ignore; /* ignore general call in target mode */ + guint8 is_clock_stretch; /* whether to stretch clock in case of no FIFO availability */ + guint8 is_loop_back; /* whether to loop back TX data to RX. Valid only for debug purposes + */ + guint8 reserved[6]; +} CyI2CConfig; + +typedef enum { + CY_I2C_DATA_CONFIG_NONE = 0, + CY_I2C_DATA_CONFIG_STOP = 1 << 0, + CY_I2C_DATA_CONFIG_NAK = 1 << 1, /* only for read */ +} CyI2CDataConfigBits; + +typedef enum { + HPI_DEV_REG_DEVICE_MODE = 0, + HPI_DEV_REG_BOOT_MODE_REASON, + HPI_DEV_REG_SI_ID, + HPI_DEV_REG_SI_ID_LSB, + HPI_DEV_REG_BL_LAST_ROW, + HPI_DEV_REG_BL_LAST_ROW_LSB, + HPI_DEV_REG_INTR_ADDR, + HPI_DEV_REG_JUMP_TO_BOOT, + HPI_DEV_REG_RESET_ADDR, + HPI_DEV_REG_RESET_CMD, + HPI_DEV_REG_ENTER_FLASH_MODE, + HPI_DEV_REG_VALIDATE_FW_ADDR, + HPI_DEV_REG_FLASH_READ_WRITE, + HPI_DEV_REG_FLASH_READ_WRITE_CMD, + HPI_DEV_REG_FLASH_ROW, + HPI_DEV_REG_FLASH_ROW_LSB, + HPI_DEV_REG_ALL_VERSION, + HPI_DEV_REG_ALL_VERSION_BYTE_1, + HPI_DEV_REG_ALL_VERSION_BYTE_2, + HPI_DEV_REG_ALL_VERSION_BYTE_3, + HPI_DEV_REG_ALL_VERSION_BYTE_4, + HPI_DEV_REG_ALL_VERSION_BYTE_5, + HPI_DEV_REG_ALL_VERSION_BYTE_6, + HPI_DEV_REG_ALL_VERSION_BYTE_7, + HPI_DEV_REG_ALL_VERSION_BYTE_8, + HPI_DEV_REG_ALL_VERSION_BYTE_9, + HPI_DEV_REG_ALL_VERSION_BYTE_10, + HPI_DEV_REG_ALL_VERSION_BYTE_11, + HPI_DEV_REG_ALL_VERSION_BYTE_12, + HPI_DEV_REG_ALL_VERSION_BYTE_13, + HPI_DEV_REG_ALL_VERSION_BYTE_14, + HPI_DEV_REG_ALL_VERSION_BYTE_15, + HPI_DEV_REG_FW_2_VERSION, + HPI_DEV_REG_FW_2_VERSION_BYTE_1, + HPI_DEV_REG_FW_2_VERSION_BYTE_2, + HPI_DEV_REG_FW_2_VERSION_BYTE_3, + HPI_DEV_REG_FW_2_VERSION_BYTE_4, + HPI_DEV_REG_FW_2_VERSION_BYTE_5, + HPI_DEV_REG_FW_2_VERSION_BYTE_6, + HPI_DEV_REG_FW_2_VERSION_BYTE_7, + HPI_DEV_REG_FW_BIN_LOC, + HPI_DEV_REG_FW_1_BIN_LOC_LSB, + HPI_DEV_REG_FW_2_BIN_LOC_MSB, + HPI_DEV_REG_FW_2_BIN_LOC_LSB, + HPI_DEV_REG_PORT_ENABLE, + HPI_DEV_SPACE_REG_LEN, + HPI_DEV_REG_RESPONSE = 0x007E, + HPI_DEV_REG_FLASH_MEM = 0x0200 +} HPIDevReg; + +typedef enum { + HPI_REG_SECTION_DEV = 0, /* device information */ + HPI_REG_SECTION_PORT_0, /* USB-PD Port 0 related */ + HPI_REG_SECTION_PORT_1, /* USB-PD Port 1 related */ + HPI_REG_SECTION_ALL /* select all registers */ +} HPIRegSection; + +typedef struct __attribute__((packed)) { + guint16 event_code; + guint16 event_length; + guint8 event_data[128]; +} HPIEvent; + +typedef enum { + HPI_REG_PART_REG = 0, /* register region */ + HPI_REG_PART_DATA = 1, /* data memory */ + HPI_REG_PART_FLASH = 2, /* flash memory */ + HPI_REG_PART_PDDATA_READ = 4, /* read data memory */ + HPI_REG_PART_PDDATA_WRITE = 8, /* write data memory */ +} HPIRegPart; + +typedef enum { + CY_PD_REG_DEVICE_MODE_ADDR, + CY_PD_BOOT_MODE_REASON, + CY_PD_SILICON_ID, + CY_PD_BL_LAST_ROW = 0x04, + CY_PD_REG_INTR_REG_ADDR = 0x06, + CY_PD_JUMP_TO_BOOT_REG_ADDR, + CY_PD_REG_RESET_ADDR, + CY_PD_REG_ENTER_FLASH_MODE_ADDR = 0x0A, + CY_PD_REG_VALIDATE_FW_ADDR, + CY_PD_REG_FLASH_READ_WRITE_ADDR, + CY_PD_GET_VERSION = 0x10, + CY_PD_REG_DBG_PD_INIT = 0x12, + CY_PD_REG_U_VDM_CTRL_ADDR = 0x20, + CY_PD_REG_READ_PD_PROFILE = 0x22, + CY_PD_REG_EFFECTIVE_SOURCE_PDO_MASK = 0x24, + CY_PD_REG_EFFECTIVE_SINK_PDO_MASK, + CY_PD_REG_SELECT_SOURCE_PDO, + CY_PD_REG_SELECT_SINK_PDO, + CY_PD_REG_PD_CONTROL, + CY_PD_REG_PD_STATUS = 0x2C, + CY_PD_REG_TYPE_C_STATUS = 0x30, + CY_PD_REG_CURRENT_PDO = 0x34, + CY_PD_REG_CURRENT_RDO = 0x38, + CY_PD_REG_CURRENT_CABLE_VDO = 0x3C, + CY_PD_REG_DISPLAY_PORT_STATUS = 0x40, + CY_PD_REG_DISPLAY_PORT_CONFIG = 0x44, + CY_PD_REG_ALTERNATE_MODE_MUX_SELECTION = 0X45, + CY_PD_REG_EVENT_MASK = 0x48, + CY_PD_REG_RESPONSE_ADDR = 0x7E, + CY_PD_REG_BOOTDATA_MEMORY_ADDR = 0x80, + CY_PD_REG_FWDATA_MEMEORY_ADDR = 0xC0, +} CyPDReg; + +#define CY_PD_GET_SILICON_ID_CMD_SIG 0x53 +#define CY_PD_REG_INTR_REG_CLEAR_RQT 0x01 +#define CY_PD_JUMP_TO_BOOT_CMD_SIG 0x4A +#define CY_PD_JUMP_TO_ALT_FW_CMD_SIG 0x41 +#define CY_PD_DEVICE_RESET_CMD_SIG 0x52 +#define CY_PD_REG_RESET_DEVICE_CMD 0x01 +#define CY_PD_ENTER_FLASHING_MODE_CMD_SIG 0x50 +#define CY_PD_FLASH_READ_WRITE_CMD_SIG 0x46 +#define CY_PD_REG_FLASH_ROW_READ_CMD 0x00 +#define CY_PD_REG_FLASH_ROW_WRITE_CMD 0x01 +#define CY_PD_REG_FLASH_READ_WRITE_ROW_LSB 0x02 +#define CY_PD_REG_FLASH_READ_WRITE_ROW_MSB 0x03 +#define CY_PD_U_VDM_TYPE 0x00 +#define HPI_GET_SILICON_ID_CMD_SIG 0x53 +#define HPI_REG_INTR_REG_CLEAR_RQT 0x01 +#define HPI_JUMP_TO_BOOT_CMD_SIG 0x4A +#define HPI_DEVICE_RESET_CMD_SIG 0x52 +#define HPI_REG_RESET_DEVICE_CMD 0x01 +#define HPI_ENTER_FLASHING_MODE_CMD_SIG 0x50 +#define HPI_FLASH_READ_WRITE_CMD_SIG 0x46 +#define HPI_REG_FLASH_ROW_READ_CMD 0x00 +#define HPI_REG_FLASH_ROW_WRITE_CMD 0x01 +#define HPI_REG_FLASH_READ_WRITE_ROW_LSB 0x02 +#define HPI_REG_FLASH_READ_WRITE_ROW_MSB 0x03 +#define HPI_PORT_DISABLE_CMD 0x11 + +#define HPI_DEVICE_VERSION_SIZE_HPIV1 16 +#define HPI_DEVICE_VERSION_SIZE_HPIV2 24 +#define HPI_META_DATA_OFFSET_ROW_128 64 +#define HPI_META_DATA_OFFSET_ROW_256 (64 + 128) + +#define PD_I2C_USB_EP_BULK_OUT 0x01 +#define PD_I2C_USB_EP_BULK_IN 0x82 +#define PD_I2C_USB_EP_INTR_IN 0x83 +#define PD_I2CM_USB_EP_BULK_OUT 0x02 +#define PD_I2CM_USB_EP_BULK_IN 0x83 +#define PD_I2CM_USB_EP_INTR_IN 0x84 + +typedef enum { + /* responses */ + CY_PD_RESP_NO_RESPONSE, + CY_PD_RESP_SUCCESS = 0x02, + CY_PD_RESP_FLASH_DATA_AVAILABLE, + CY_PD_RESP_INVALID_COMMAND = 0x05, + CY_PD_RESP_COLLISION_DETECTED, + CY_PD_RESP_FLASH_UPDATE_FAILED, + CY_PD_RESP_INVALID_FW, + CY_PD_RESP_INVALID_ARGUMENTS, + CY_PD_RESP_NOT_SUPPORTED, + CY_PD_RESP_TRANSACTION_FAILED = 0x0C, + CY_PD_RESP_PD_COMMAND_FAILED, + CY_PD_RESP_UNDEFINED, + CY_PD_RESP_RA_DETECT = 0x10, + CY_PD_RESP_RA_REMOVED, + + /* device specific events */ + CY_PD_RESP_RESET_COMPLETE = 0x80, + CY_PD_RESP_MESSAGE_QUEUE_OVERFLOW, + + /* type-c specific events */ + CY_PD_RESP_OVER_CURRENT_DETECTED, + CY_PD_RESP_OVER_VOLTAGE_DETECTED, + CY_PD_RESP_TYPC_C_CONNECTED, + CY_PD_RESP_TYPE_C_DISCONNECTED, + + /* pd specific events and asynchronous messages */ + CY_PD_RESP_PD_CONTRACT_ESTABLISHED, + CY_PD_RESP_DR_SWAP, + CY_PD_RESP_PR_SWAP, + CY_PD_RESP_VCON_SWAP, + CY_PD_RESP_PS_RDY, + CY_PD_RESP_GOTOMIN, + CY_PD_RESP_ACCEPT_MESSAGE, + CY_PD_RESP_REJECT_MESSAGE, + CY_PD_RESP_WAIT_MESSAGE, + CY_PD_RESP_HARD_RESET, + CY_PD_RESP_VDM_RECEIVED, + CY_PD_RESP_SRC_CAP_RCVD, + CY_PD_RESP_SINK_CAP_RCVD, + CY_PD_RESP_DP_ALTERNATE_MODE, + CY_PD_RESP_DP_DEVICE_CONNECTED, + CY_PD_RESP_DP_DEVICE_NOT_CONNECTED, + CY_PD_RESP_DP_SID_NOT_FOUND, + CY_PD_RESP_MULTIPLE_SVID_DISCOVERED, + CY_PD_RESP_DP_FUNCTION_NOT_SUPPORTED, + CY_PD_RESP_DP_PORT_CONFIG_NOT_SUPPORTED, + CY_PD_HARD_RESET_SENT, + CY_PD_SOFT_RESET_SENT, + CY_PD_CABLE_RESET_SENT, + CY_PD_SOURCE_DISBALED_STATE_ENTERED, + CY_PD_SENDER_RESPONSE_TIMER_TIMEOUT, + CY_PD_NO_VDM_RESPONSE_RECEIVED +} CyPDResp; + +typedef enum { + HPI_RESPONSE_NO_RESPONSE, + HPI_RESPONSE_SUCCESS = 0x02, + HPI_RESPONSE_FLASH_DATA_AVAILABLE, + HPI_RESPONSE_INVALID_COMMAND = 0x05, + HPI_RESPONSE_FLASH_UPDATE_FAILED = 0x07, + HPI_RESPONSE_INVALID_FW, + HPI_RESPONSE_INVALID_ARGUMENT, + HPI_RESPONSE_NOT_SUPPORTED, + HPI_RESPONSE_PD_TRANSACTION_FAILED = 0x0C, + HPI_RESPONSE_PD_COMMAND_FAILED, + HPI_RESPONSE_UNDEFINED_ERROR = 0x0F, + HPI_EVENT_RESET_COMPLETE = 0x80, + HPI_EVENT_MSG_OVERFLOW, + HPI_EVENT_OC_DETECT, + HPI_EVENT_OV_DETECT, + HPI_EVENT_CONNECT_DETECT, + HPI_EVENT_DISCONNECT_DETECT, + HPI_EVENT_NEGOTIATION_COMPLETE, + HPI_EVENT_SWAP_COMPLETE, + HPI_EVENT_PS_RDY_RECEIVED = 0x8A, + HPI_EVENT_GOTO_MIN_RECEIVED, + HPI_EVENT_ACCEPT_RECEIVED, + HPI_EVENT_REJECT_RECEIVED, + HPI_EVENT_WAIT_RECEIVED, + HPI_EVENT_HARD_RESET_RECEIVED, + HPI_EVENT_VDM_RECEIVED = 0x90, + HPI_EVENT_SOURCE_CAP_RECEIVED, + HPI_EVENT_SINK_CAP_RECEIVED, + HPI_EVENT_DP_MODE_ENTERED, + HPI_EVENT_DP_STATUS_UPDATE, + HPI_EVENT_DP_SID_NOT_FOUND = 0x96, + HPI_EVENT_DP_MANY_SID_FOUND, + HPI_EVENT_DP_NO_CABLE_SUPPORT, + HPI_EVENT_DP_NO_UFP_SUPPORT, + HPI_EVENT_HARD_RESET_SENT, + HPI_EVENT_SOFT_RESET_SENT, + HPI_EVENT_CABLE_RESET_SENT, + HPI_EVENT_SOURCE_DISABLED, + HPI_EVENT_SENDER_TIMEOUT, + HPI_EVENT_VDM_NO_RESPONSE, + HPI_EVENT_UNEXPECTED_VOLTAGE, + HPI_EVENT_ERROR_RECOVERY, + HPI_EVENT_EMCA_DETECT = 0xA6, + HPI_EVENT_RP_CHANGE_DETECT = 0xAA, + HPI_EVENT_TB_ENTERED = 0xB0, + HPI_EVENT_TB_EXITED +} HPIResp; + +const gchar * +fu_ccgx_pd_resp_to_string(CyPDResp val); diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-device.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..7ba491f2ee191209b6baca7cdc08fc3ad25fef58 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-device.c @@ -0,0 +1,1606 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-ccgx-common.h" +#include "fu-ccgx-firmware.h" +#include "fu-ccgx-hpi-common.h" +#include "fu-ccgx-hpi-device.h" + +struct _FuCcgxHpiDevice { + FuUsbDevice parent_instance; + guint8 inf_num; /* USB interface number */ + guint8 scb_index; + guint16 silicon_id; + guint16 fw_app_type; + guint8 hpi_addrsz; /* hpiv1: 1 byte, hpiv2: 2 byte */ + guint8 num_ports; /* max number of ports */ + FWMode fw_mode; + FWImageType fw_image_type; + guint8 target_address; + guint8 ep_bulk_in; + guint8 ep_bulk_out; + guint8 ep_intr_in; + guint32 flash_row_size; + guint32 flash_size; +}; + +/** + * FU_CCGX_HPI_DEVICE_IS_IN_RESTART: + * + * Device is in restart and should not be closed manually. + * + * Since: 1.7.0 + */ +#define FU_CCGX_HPI_DEVICE_IS_IN_RESTART (1 << 0) + +G_DEFINE_TYPE(FuCcgxHpiDevice, fu_ccgx_hpi_device, FU_TYPE_USB_DEVICE) + +#define HPI_CMD_REG_READ_WRITE_DELAY_US 10000 +#define HPI_CMD_ENTER_FLASH_MODE_DELAY_US 20000 +#define HPI_CMD_SETUP_EVENT_WAIT_TIME_MS 200 +#define HPI_CMD_SETUP_EVENT_CLEAR_TIME_MS 150 +#define HPI_CMD_COMMAND_RESPONSE_TIME_MS 500 +#define HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS 30 +#define HPI_CMD_RESET_COMPLETE_DELAY_US 150000 +#define HPI_CMD_RETRY_DELAY 30 /* ms */ +#define HPI_CMD_RESET_RETRY_CNT 3 +#define HPI_CMD_ENTER_LEAVE_FLASH_MODE_RETRY_CNT 3 +#define HPI_CMD_FLASH_WRITE_RETRY_CNT 3 +#define HPI_CMD_FLASH_READ_RETRY_CNT 3 +#define HPI_CMD_VALIDATE_FW_RETRY_CNT 3 + +static void +fu_ccgx_hpi_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + fu_string_append_kx(str, idt, "ScbIndex", self->scb_index); + fu_string_append_kx(str, idt, "SiliconId", self->silicon_id); + fu_string_append_kx(str, idt, "FwAppType", self->fw_app_type); + fu_string_append_kx(str, idt, "HpiAddrsz", self->hpi_addrsz); + fu_string_append_kx(str, idt, "NumPorts", self->num_ports); + fu_string_append(str, idt, "FWMode", fu_ccgx_fw_mode_to_string(self->fw_mode)); + fu_string_append(str, + idt, + "FwImageType", + fu_ccgx_fw_image_type_to_string(self->fw_image_type)); + fu_string_append_kx(str, idt, "EpBulkIn", self->ep_bulk_in); + fu_string_append_kx(str, idt, "EpBulkOut", self->ep_bulk_out); + fu_string_append_kx(str, idt, "EpIntrIn", self->ep_intr_in); + if (self->flash_row_size > 0) + fu_string_append_kx(str, idt, "CcgxFlashRowSize", self->flash_row_size); + if (self->flash_size > 0) + fu_string_append_kx(str, idt, "CcgxFlashSize", self->flash_size); +} + +typedef struct { + guint8 mode; + guint16 addr; + guint8 *buf; + gsize bufsz; +} FuCcgxHpiDeviceRetryHelper; + +typedef struct { + guint16 addr; + const guint8 *buf; + gsize bufsz; +} FuCcgxHpiFlashWriteRetryHelper; + +typedef struct { + guint16 addr; + guint8 *buf; + gsize bufsz; +} FuCcgxHpiFlashReadRetryHelper; + +static gboolean +fu_ccgx_hpi_device_i2c_reset_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + FuCcgxHpiDeviceRetryHelper *helper = (FuCcgxHpiDeviceRetryHelper *)user_data; + g_autoptr(GError) error_local = NULL; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_RESET_CMD, + (self->scb_index << CY_SCB_INDEX_POS) | helper->mode, + 0x0, + NULL, + 0x0, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to reset i2c: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_check_i2c_status(FuCcgxHpiDevice *self, guint8 mode, GError **error) +{ + guint8 buf[CY_I2C_GET_STATUS_LEN] = {0x0}; + g_autoptr(GError) error_local = NULL; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_GET_STATUS_CMD, + (((guint16)self->scb_index) << CY_SCB_INDEX_POS) | mode, + 0x0, + buf, + sizeof(buf), + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get i2c status: %s", + error_local->message); + return FALSE; + } + if (buf[0] & CY_I2C_ERROR_BIT) { + if (buf[0] & 0x80) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "i2c status write error: 0x%x", + buf[0]); + return FALSE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "i2c status read error: 0x%x", + buf[0]); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_get_i2c_config(FuCcgxHpiDevice *self, CyI2CConfig *i2c_config, GError **error) +{ + g_autoptr(GError) error_local = NULL; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_GET_CONFIG_CMD, + ((guint16)self->scb_index) << CY_SCB_INDEX_POS, + 0x0, + (guint8 *)i2c_config, + sizeof(*i2c_config), + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "i2c get config error: control xfer: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_set_i2c_config(FuCcgxHpiDevice *self, CyI2CConfig *i2c_config, GError **error) +{ + g_autoptr(GError) error_local = NULL; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_SET_CONFIG_CMD, + ((guint16)self->scb_index) << CY_SCB_INDEX_POS, + 0x0, + (guint8 *)i2c_config, + sizeof(*i2c_config), + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "i2c set config error: control xfer: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_wait_for_notify(FuCcgxHpiDevice *self, guint16 *bytes_pending, GError **error) +{ + guint8 buf[CY_I2C_EVENT_NOTIFICATION_LEN] = {0x0}; + g_autoptr(GError) error_local = NULL; + + if (!g_usb_device_interrupt_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->ep_intr_in, + buf, + sizeof(buf), + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get i2c event: %s", + error_local->message); + return FALSE; + } + + /* @bytes_pending available on failure */ + if (buf[0] & CY_I2C_ERROR_BIT) { + if (bytes_pending != NULL) { + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x01, + bytes_pending, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + if (buf[0] & 0x80) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "i2c status write error: 0x%x", + buf[0]); + return FALSE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "i2c status read error: 0x%x", + buf[0]); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_i2c_read(FuCcgxHpiDevice *self, + guint8 *buf, + gsize bufsz, + CyI2CDataConfigBits cfg_bits, + GError **error) +{ + guint8 target_address = 0; + + if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_READ, error)) { + g_prefix_error(error, "i2c read error: "); + return FALSE; + } + target_address = (self->target_address & 0x7F) | (self->scb_index << 7); + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_READ_CMD, + (((guint16)target_address) << 8) | cfg_bits, + bufsz, + NULL, + 0x0, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "i2c read error: control xfer: "); + return FALSE; + } + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->ep_bulk_in, + buf, + bufsz, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "i2c read error: bulk xfer: "); + return FALSE; + } + + /* 10 msec delay */ + g_usleep(I2C_READ_WRITE_DELAY_US); + if (!fu_ccgx_hpi_device_wait_for_notify(self, NULL, error)) { + g_prefix_error(error, "i2c read error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_i2c_write(FuCcgxHpiDevice *self, + guint8 *buf, + gsize bufsz, + CyI2CDataConfigBits cfg_bits, + GError **error) +{ + guint8 target_address; + + if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_WRITE, error)) { + g_prefix_error(error, "i2c get status error: "); + return FALSE; + } + target_address = (self->target_address & 0x7F) | (self->scb_index << 7); + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_WRITE_CMD, + ((guint16)target_address << 8) | + (cfg_bits & CY_I2C_DATA_CONFIG_STOP), + bufsz, /* idx */ + NULL, + 0x0, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "i2c write error: control xfer: "); + return FALSE; + } + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->ep_bulk_out, + buf, + bufsz, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "i2c write error: bulk xfer: "); + return FALSE; + } + + /* 10 msec delay */ + g_usleep(I2C_READ_WRITE_DELAY_US); + if (!fu_ccgx_hpi_device_wait_for_notify(self, NULL, error)) { + g_prefix_error(error, "i2c wait for notification error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_i2c_write_no_resp(FuCcgxHpiDevice *self, + guint8 *buf, + gsize bufsz, + CyI2CDataConfigBits cfg_bits, + GError **error) +{ + guint8 target_address = 0; + g_autoptr(GError) error_local = NULL; + + if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_WRITE, error)) { + g_prefix_error(error, "i2c write error: "); + return FALSE; + } + target_address = (self->target_address & 0x7F) | (self->scb_index << 7); + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + CY_I2C_WRITE_CMD, + ((guint16)target_address << 8) | + (cfg_bits & CY_I2C_DATA_CONFIG_STOP), + bufsz, + NULL, + 0x0, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "i2c write error: control xfer: "); + return FALSE; + } + + /* device will reboot after this, so txfer will fail */ + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->ep_bulk_out, + buf, + bufsz, + NULL, + FU_CCGX_HPI_WAIT_TIMEOUT, + NULL, + &error_local)) { + g_debug("ignoring i2c write error: bulk xfer: %s", error_local->message); + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_reg_read_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDeviceRetryHelper *helper = (FuCcgxHpiDeviceRetryHelper *)user_data; + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + g_autofree guint8 *bufhw = g_malloc0(self->hpi_addrsz); + + for (guint32 i = 0; i < self->hpi_addrsz; i++) + bufhw[i] = (guint8)(helper->addr >> (8 * i)); + if (!fu_ccgx_hpi_device_i2c_write(self, + bufhw, + self->hpi_addrsz, + CY_I2C_DATA_CONFIG_NAK, + error)) { + g_prefix_error(error, "write error: "); + return FALSE; + } + if (!fu_ccgx_hpi_device_i2c_read(self, + helper->buf, + helper->bufsz, + CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK, + error)) { + g_prefix_error(error, "read error: "); + return FALSE; + } + g_usleep(HPI_CMD_REG_READ_WRITE_DELAY_US); + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_reg_read(FuCcgxHpiDevice *self, + guint16 addr, + guint8 *buf, + gsize bufsz, + GError **error) +{ + FuCcgxHpiDeviceRetryHelper helper = { + .addr = addr, + .mode = CY_I2C_MODE_READ, + .buf = buf, + .bufsz = bufsz, + }; + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_device_reg_read_cb, + HPI_CMD_RESET_RETRY_CNT, + &helper, + error); +} + +static gboolean +fu_ccgx_hpi_device_reg_write_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDeviceRetryHelper *helper = (FuCcgxHpiDeviceRetryHelper *)user_data; + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + g_autofree guint8 *bufhw = g_malloc0(helper->bufsz + self->hpi_addrsz); + + for (guint32 i = 0; i < self->hpi_addrsz; i++) + bufhw[i] = (guint8)(helper->addr >> (8 * i)); + memcpy(&bufhw[self->hpi_addrsz], helper->buf, helper->bufsz); + if (!fu_ccgx_hpi_device_i2c_write(self, + bufhw, + helper->bufsz + self->hpi_addrsz, + CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK, + error)) { + g_prefix_error(error, "reg write error: "); + return FALSE; + } + g_usleep(HPI_CMD_REG_READ_WRITE_DELAY_US); + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_reg_write(FuCcgxHpiDevice *self, + guint16 addr, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + FuCcgxHpiDeviceRetryHelper helper = { + .addr = addr, + .mode = CY_I2C_MODE_WRITE, + .buf = (guint8 *)buf, + .bufsz = bufsz, + }; + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_device_reg_write_cb, + HPI_CMD_RESET_RETRY_CNT, + &helper, + error); +} + +static gboolean +fu_ccgx_hpi_device_reg_write_no_resp(FuCcgxHpiDevice *self, + guint16 addr, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + g_autofree guint8 *bufhw = g_malloc0(bufsz + self->hpi_addrsz); + for (guint32 i = 0; i < self->hpi_addrsz; i++) + bufhw[i] = (guint8)(addr >> (8 * i)); + memcpy(&bufhw[self->hpi_addrsz], buf, bufsz); + if (!fu_ccgx_hpi_device_i2c_write_no_resp(self, + bufhw, + bufsz + self->hpi_addrsz, + CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK, + error)) { + g_prefix_error(error, "reg write no-resp error: "); + return FALSE; + } + g_usleep(HPI_CMD_REG_READ_WRITE_DELAY_US); + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_clear_intr(FuCcgxHpiDevice *self, HPIRegSection section, GError **error) +{ + guint8 intr = 0; + for (guint8 i = 0; i <= self->num_ports; i++) { + if (i == section || section == HPI_REG_SECTION_ALL) + intr |= 1 << i; + } + if (!fu_ccgx_hpi_device_reg_write(self, + HPI_DEV_REG_INTR_ADDR, + &intr, + sizeof(intr), + error)) { + g_prefix_error(error, "failed to clear interrupt: "); + return FALSE; + } + return TRUE; +} + +static guint16 +fu_ccgx_hpi_device_reg_addr_gen(guint8 section, guint8 part, guint8 addr) +{ + return (((guint16)section) << 12) | (((guint16)part) << 8) | addr; +} + +static gboolean +fu_ccgx_hpi_device_read_event_reg(FuCcgxHpiDevice *self, + HPIRegSection section, + HPIEvent *event, + GError **error) +{ + if (section != HPI_REG_SECTION_DEV) { + guint16 reg_addr; + guint8 buf[4] = {0x0}; + + /* first read the response register */ + reg_addr = fu_ccgx_hpi_device_reg_addr_gen(section, HPI_REG_PART_PDDATA_READ, 0); + if (!fu_ccgx_hpi_device_reg_read(self, reg_addr, buf, sizeof(buf), error)) { + g_prefix_error(error, "read response reg error: "); + return FALSE; + } + + /* byte 1 is reserved and should read as zero */ + buf[1] = 0; + memcpy((guint8 *)event, buf, sizeof(buf)); + if (event->event_length != 0) { + reg_addr = fu_ccgx_hpi_device_reg_addr_gen(section, + HPI_REG_PART_PDDATA_READ, + sizeof(buf)); + if (!fu_ccgx_hpi_device_reg_read(self, + reg_addr, + event->event_data, + event->event_length, + error)) { + g_prefix_error(error, "read event data error: "); + return FALSE; + } + } + } else { + guint8 buf[2] = {0x0}; + if (!fu_ccgx_hpi_device_reg_read(self, + CY_PD_REG_RESPONSE_ADDR, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "read response reg error: "); + return FALSE; + } + event->event_code = buf[0]; + event->event_length = buf[1]; + if (event->event_length != 0) { + /* read the data memory */ + if (!fu_ccgx_hpi_device_reg_read(self, + CY_PD_REG_BOOTDATA_MEMORY_ADDR, + event->event_data, + event->event_length, + error)) { + g_prefix_error(error, "read event data error: "); + return FALSE; + } + } + } + + /* success */ + return fu_ccgx_hpi_device_clear_intr(self, section, error); +} + +static gboolean +fu_ccgx_hpi_device_app_read_intr_reg(FuCcgxHpiDevice *self, + HPIRegSection section, + HPIEvent *event_array, + guint8 *event_count, + GError **error) +{ + guint16 reg_addr; + guint8 event_count_tmp = 0; + guint8 intr_reg = 0; + + reg_addr = fu_ccgx_hpi_device_reg_addr_gen(HPI_REG_SECTION_DEV, + HPI_REG_PART_REG, + HPI_DEV_REG_INTR_ADDR); + if (!fu_ccgx_hpi_device_reg_read(self, reg_addr, &intr_reg, sizeof(intr_reg), error)) { + g_prefix_error(error, "read intr reg error: "); + return FALSE; + } + + /* device section will not come here */ + for (guint8 i = 0; i <= self->num_ports; i++) { + /* check if this section is needed */ + if (section == i || section == HPI_REG_SECTION_ALL) { + /* check whether this section has any event/response */ + if ((1 << i) & intr_reg) { + if (!fu_ccgx_hpi_device_read_event_reg(self, + section, + &event_array[i], + error)) { + g_prefix_error(error, "read event error: "); + return FALSE; + } + event_count_tmp++; + } + } + } + if (event_count != NULL) + *event_count = event_count_tmp; + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_wait_for_event(FuCcgxHpiDevice *self, + HPIRegSection section, + HPIEvent *event_array, + guint32 timeout_ms, + GError **error) +{ + guint8 event_count = 0; + g_autoptr(GTimer) start_time = g_timer_new(); + do { + if (!fu_ccgx_hpi_device_app_read_intr_reg(self, + section, + event_array, + &event_count, + error)) + return FALSE; + if (event_count > 0) + return TRUE; + } while (g_timer_elapsed(start_time, NULL) * 1000.f <= timeout_ms); + + /* timed out */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "failed to wait for event in %ums", + timeout_ms); + return FALSE; +} + +static gboolean +fu_ccgx_hpi_device_get_event(FuCcgxHpiDevice *self, + HPIRegSection reg_section, + CyPDResp *event, + guint32 io_timeout, + GError **error) +{ + HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = {0x0}; + if (!fu_ccgx_hpi_device_wait_for_event(self, reg_section, event_array, io_timeout, error)) { + g_prefix_error(error, "failed to get event: "); + return FALSE; + } + *event = event_array[reg_section].event_code; + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_clear_all_events(FuCcgxHpiDevice *self, guint32 io_timeout, GError **error) +{ + HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = {0x0}; + if (io_timeout == 0) { + return fu_ccgx_hpi_device_app_read_intr_reg(self, + HPI_REG_SECTION_ALL, + event_array, + NULL, + error); + } + for (guint8 i = 0; i < self->num_ports; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_ccgx_hpi_device_wait_for_event(self, + i, + event_array, + io_timeout, + &error_local)) { + if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to clear events: "); + return FALSE; + } + } + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_validate_fw_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + guint8 *fw_index = (guint8 *)user_data; + CyPDResp hpi_event = 0; + + g_return_val_if_fail(fw_index != NULL, FALSE); + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + + if (!fu_ccgx_hpi_device_reg_write(self, CY_PD_REG_VALIDATE_FW_ADDR, fw_index, 1, error)) { + g_prefix_error(error, "validate fw error: "); + return FALSE; + } + if (!fu_ccgx_hpi_device_get_event(self, + HPI_REG_SECTION_DEV, + &hpi_event, + HPI_CMD_COMMAND_RESPONSE_TIME_MS, + error)) { + g_prefix_error(error, "validate fw resp error: "); + return FALSE; + } + if (hpi_event != CY_PD_RESP_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "validate failed: %s [0x%x]", + fu_ccgx_pd_resp_to_string(hpi_event), + hpi_event); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_validate_fw(FuCcgxHpiDevice *self, guint8 fw_index, GError **error) +{ + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_validate_fw_cb, + HPI_CMD_VALIDATE_FW_RETRY_CNT, + &fw_index, + error); +} + +static gboolean +fu_ccgx_hpi_enter_flash_mode_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + CyPDResp hpi_event = 0; + guint8 buf[] = {CY_PD_ENTER_FLASHING_MODE_CMD_SIG}; + + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + if (!fu_ccgx_hpi_device_reg_write(self, + CY_PD_REG_ENTER_FLASH_MODE_ADDR, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "enter flash mode error: "); + return FALSE; + } + if (!fu_ccgx_hpi_device_get_event(self, + HPI_REG_SECTION_DEV, + &hpi_event, + HPI_CMD_COMMAND_RESPONSE_TIME_MS, + error)) { + g_prefix_error(error, "enter flash mode resp error: "); + return FALSE; + } + if (hpi_event != CY_PD_RESP_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "enter flash failed: %s [0x%x]", + fu_ccgx_pd_resp_to_string(hpi_event), + hpi_event); + return FALSE; + } + + /* wait 10 msec */ + g_usleep(HPI_CMD_ENTER_FLASH_MODE_DELAY_US); + return TRUE; +} + +static gboolean +fu_ccgx_hpi_enter_flash_mode(FuCcgxHpiDevice *self, GError **error) +{ + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_enter_flash_mode_cb, + HPI_CMD_ENTER_LEAVE_FLASH_MODE_RETRY_CNT, + NULL, + error); +} + +static gboolean +fu_ccgx_hpi_leave_flash_mode_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + CyPDResp hpi_event = 0; + guint8 buf = {0x0}; + + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + + if (!fu_ccgx_hpi_device_reg_write(self, + CY_PD_REG_ENTER_FLASH_MODE_ADDR, + &buf, + sizeof(buf), + error)) { + g_prefix_error(error, "leave flash mode error: "); + return FALSE; + } + if (!fu_ccgx_hpi_device_get_event(self, + HPI_REG_SECTION_DEV, + &hpi_event, + HPI_CMD_COMMAND_RESPONSE_TIME_MS, + error)) { + g_prefix_error(error, "leave flash mode resp error: "); + return FALSE; + } + if (hpi_event != CY_PD_RESP_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "leave flash mode failed: %s [0x%x]", + fu_ccgx_pd_resp_to_string(hpi_event), + hpi_event); + return FALSE; + } + + /* wait 10 msec */ + g_usleep(HPI_CMD_ENTER_FLASH_MODE_DELAY_US); + return TRUE; +} + +static gboolean +fu_ccgx_hpi_leave_flash_mode(FuCcgxHpiDevice *self, GError **error) +{ + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_leave_flash_mode_cb, + HPI_CMD_ENTER_LEAVE_FLASH_MODE_RETRY_CNT, + NULL, + error); +} + +static gboolean +fu_ccgx_hpi_write_flash_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + FuCcgxHpiFlashWriteRetryHelper *helper = (FuCcgxHpiFlashWriteRetryHelper *)user_data; + CyPDResp hpi_event = 0; + guint16 addr_tmp = 0; + guint8 bufhw[] = { + CY_PD_FLASH_READ_WRITE_CMD_SIG, + CY_PD_REG_FLASH_ROW_WRITE_CMD, + helper->addr & 0xFF, + helper->addr >> 8, + }; + + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + + /* write data to memory */ + addr_tmp = self->hpi_addrsz > 1 ? HPI_DEV_REG_FLASH_MEM : CY_PD_REG_BOOTDATA_MEMORY_ADDR; + if (!fu_ccgx_hpi_device_reg_write(self, addr_tmp, helper->buf, helper->bufsz, error)) { + g_prefix_error(error, "write buf to memory error: "); + return FALSE; + } + if (!fu_ccgx_hpi_device_reg_write(self, + CY_PD_REG_FLASH_READ_WRITE_ADDR, + bufhw, + sizeof(bufhw), + error)) { + g_prefix_error(error, "write flash error: "); + return FALSE; + } + + /* wait until flash is written */ + if (!fu_ccgx_hpi_device_get_event(self, + HPI_REG_SECTION_DEV, + &hpi_event, + HPI_CMD_COMMAND_RESPONSE_TIME_MS, + error)) { + g_prefix_error(error, "write flash resp error: "); + return FALSE; + } + if (hpi_event != CY_PD_RESP_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "write flash failed: %s [0x%x]", + fu_ccgx_pd_resp_to_string(hpi_event), + hpi_event); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_write_flash(FuCcgxHpiDevice *self, + guint16 addr, + const guint8 *buf, + guint16 bufsz, + GError **error) +{ + FuCcgxHpiFlashWriteRetryHelper helper = { + .addr = addr, + .buf = buf, + .bufsz = bufsz, + }; + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_write_flash_cb, + HPI_CMD_FLASH_WRITE_RETRY_CNT, + &helper, + error); +} + +static gboolean +fu_ccgx_hpi_read_flash_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + FuCcgxHpiFlashReadRetryHelper *helper = (FuCcgxHpiFlashReadRetryHelper *)user_data; + CyPDResp hpi_event = 0; + guint16 addr_tmp; + guint8 bufhw[] = { + CY_PD_FLASH_READ_WRITE_CMD_SIG, + CY_PD_REG_FLASH_ROW_READ_CMD, + helper->addr & 0xFF, + helper->addr >> 8, + }; + + /* set address */ + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + if (!fu_ccgx_hpi_device_reg_write(self, + CY_PD_REG_FLASH_READ_WRITE_ADDR, + bufhw, + sizeof(bufhw), + error)) { + g_prefix_error(error, "read flash error: "); + return FALSE; + } + + /* wait until flash is read */ + if (!fu_ccgx_hpi_device_get_event(self, + HPI_REG_SECTION_DEV, + &hpi_event, + HPI_CMD_COMMAND_RESPONSE_TIME_MS, + error)) { + g_prefix_error(error, "read flash resp error: "); + return FALSE; + } + if (hpi_event != CY_PD_RESP_FLASH_DATA_AVAILABLE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "read flash failed: %s [0x%x]", + fu_ccgx_pd_resp_to_string(hpi_event), + hpi_event); + return FALSE; + } + addr_tmp = self->hpi_addrsz > 1 ? HPI_DEV_REG_FLASH_MEM : CY_PD_REG_BOOTDATA_MEMORY_ADDR; + if (!fu_ccgx_hpi_device_reg_read(self, addr_tmp, helper->buf, helper->bufsz, error)) { + g_prefix_error(error, "read data from memory error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_read_flash(FuCcgxHpiDevice *self, + guint16 addr, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + FuCcgxHpiFlashReadRetryHelper helper = { + .addr = addr, + .buf = buf, + .bufsz = bufsz, + }; + return fu_device_retry(FU_DEVICE(self), + fu_ccgx_hpi_read_flash_cb, + HPI_CMD_FLASH_READ_RETRY_CNT, + &helper, + error); +} + +static gboolean +fu_ccgx_hpi_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + guint8 buf[] = { + CY_PD_JUMP_TO_ALT_FW_CMD_SIG, + }; + + /* not required */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER) || + self->fw_image_type == FW_IMAGE_TYPE_DUAL_SYMMETRIC) + return TRUE; + + /* jump to Alt FW */ + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + if (!fu_ccgx_hpi_device_reg_write(self, + CY_PD_JUMP_TO_BOOT_REG_ADDR, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "jump to alt mode error: "); + return FALSE; + } + + /* sym not required */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_add_private_flag(device, FU_CCGX_HPI_DEVICE_IS_IN_RESTART); + + /* success */ + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + guint8 buf[] = { + CY_PD_DEVICE_RESET_CMD_SIG, + CY_PD_REG_RESET_DEVICE_CMD, + }; + if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) + return FALSE; + if (!fu_ccgx_hpi_device_reg_write_no_resp(self, + CY_PD_REG_RESET_ADDR, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "reset device error: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_add_private_flag(device, FU_CCGX_HPI_DEVICE_IS_IN_RESTART); + return TRUE; +} + +static FuFirmware * +fu_ccgx_hpi_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + FWMode fw_mode; + guint16 fw_app_type; + guint16 fw_silicon_id; + g_autoptr(FuFirmware) firmware = fu_ccgx_firmware_new(); + + /* parse all images */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check the silicon ID */ + fw_silicon_id = fu_ccgx_firmware_get_silicon_id(FU_CCGX_FIRMWARE(firmware)); + if (fw_silicon_id != self->silicon_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "silicon id mismatch, expected 0x%x, got 0x%x", + self->silicon_id, + fw_silicon_id); + return NULL; + } + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + fw_app_type = fu_ccgx_firmware_get_app_type(FU_CCGX_FIRMWARE(firmware)); + if (fw_app_type != self->fw_app_type) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "app type mismatch, expected 0x%x, got 0x%x", + self->fw_app_type, + fw_app_type); + return NULL; + } + } + fw_mode = fu_ccgx_firmware_get_fw_mode(FU_CCGX_FIRMWARE(firmware)); + if (fw_mode != fu_ccgx_fw_mode_get_alternate(self->fw_mode)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "FWMode mismatch, expected %s, got %s", + fu_ccgx_fw_mode_to_string(fu_ccgx_fw_mode_get_alternate(self->fw_mode)), + fu_ccgx_fw_mode_to_string(fw_mode)); + return NULL; + } + return g_steal_pointer(&firmware); +} + +static gboolean +fu_ccgx_hpi_get_metadata_offset(FuCcgxHpiDevice *self, + FWMode fw_mode, + guint32 *addr, + guint32 *offset, + GError **error) +{ + guint32 addr_max; + + /* sanity check */ + if (self->flash_row_size == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unset support row size"); + return FALSE; + } + + /* get the row offset for the flash size */ + addr_max = self->flash_size / self->flash_row_size; + if (self->flash_row_size == 128) { + *offset = HPI_META_DATA_OFFSET_ROW_128; + } else if (self->flash_row_size == 256) { + *offset = HPI_META_DATA_OFFSET_ROW_256; + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported support row size: 0x%x", + self->flash_row_size); + return FALSE; + } + + /* get the row offset in the flash */ + switch (fw_mode) { + case FW_MODE_FW1: + *addr = addr_max - 1; + break; + case FW_MODE_FW2: + *addr = addr_max - 2; + break; + default: + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "boot recovery not supported"); + return FALSE; + } + return TRUE; +} + +/* this will only work after fu_ccgx_hpi_enter_flash_mode() has been used */ +static gboolean +fu_ccgx_hpi_load_metadata(FuCcgxHpiDevice *self, + FWMode fw_mode, + CCGxMetaData *metadata, + GError **error) +{ + guint32 addr = 0x0; + guint32 md_offset = 0x0; + g_autofree guint8 *buf = NULL; + + /* read flash at correct address */ + if (!fu_ccgx_hpi_get_metadata_offset(self, fw_mode, &addr, &md_offset, error)) + return FALSE; + buf = g_malloc0(self->flash_row_size); + if (!fu_ccgx_hpi_read_flash(self, addr, buf, self->flash_row_size, error)) { + g_prefix_error(error, "fw metadata read error: "); + return FALSE; + } + return fu_memcpy_safe((guint8 *)metadata, + sizeof(*metadata), + 0x0, + buf, + self->flash_row_size, + md_offset, + sizeof(*metadata), + error); +} + +/* this will only work after fu_ccgx_hpi_enter_flash_mode() has been used */ +static gboolean +fu_ccgx_hpi_save_metadata(FuCcgxHpiDevice *self, + FWMode fw_mode, + CCGxMetaData *metadata, + GError **error) +{ + guint32 addr = 0x0; + guint32 md_offset = 0x0; + g_autofree guint8 *buf = NULL; + + /* read entire row of flash at correct address */ + if (!fu_ccgx_hpi_get_metadata_offset(self, fw_mode, &addr, &md_offset, error)) + return FALSE; + buf = g_malloc0(self->flash_row_size); + if (!fu_ccgx_hpi_read_flash(self, addr, buf, self->flash_row_size, error)) { + g_prefix_error(error, "fw metadata read existing error: "); + return FALSE; + } + if (!fu_memcpy_safe(buf, + self->flash_row_size, + md_offset, + (guint8 *)metadata, + sizeof(*metadata), + 0x0, + sizeof(*metadata), + error)) + return FALSE; + if (!fu_ccgx_hpi_write_flash(self, addr, buf, self->flash_row_size, error)) { + g_prefix_error(error, "fw metadata write error: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ccgx_hpi_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + CCGxMetaData metadata = {0x0}; + GPtrArray *records = fu_ccgx_firmware_get_records(FU_CCGX_FIRMWARE(firmware)); + FWMode fw_mode_alt = fu_ccgx_fw_mode_get_alternate(self->fw_mode); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "invalidate-metadata"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "leave-flash"); + + /* enter flash mode */ + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_ccgx_hpi_enter_flash_mode, + (FuDeviceLockerFunc)fu_ccgx_hpi_leave_flash_mode, + error); + if (locker == NULL) + return FALSE; + + /* invalidate metadata for alternate image */ + if (!fu_ccgx_hpi_load_metadata(self, fw_mode_alt, &metadata, error)) + return FALSE; + metadata.metadata_valid = 0x00; + if (!fu_ccgx_hpi_save_metadata(self, fw_mode_alt, &metadata, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write new image */ + for (guint i = 0; i < records->len; i++) { + FuCcgxFirmwareRecord *rcd = g_ptr_array_index(records, i); + + /* write chunk */ + if (!fu_ccgx_hpi_write_flash(self, + rcd->row_number, + g_bytes_get_data(rcd->data, NULL), + g_bytes_get_size(rcd->data), + error)) { + g_prefix_error(error, "fw write error @0x%x: ", rcd->row_number); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)records->len); + } + fu_progress_step_done(progress); + + /* validate fw */ + if (!fu_ccgx_hpi_validate_fw(self, fw_mode_alt, error)) { + g_prefix_error(error, "fw validate error: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* this is a good time to leave the flash mode *before* rebooting */ + if (!fu_device_locker_close(locker, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_ccgx_hpi_device_ensure_silicon_id(FuCcgxHpiDevice *self, GError **error) +{ + guint8 buf[2] = {0x0}; + + if (!fu_ccgx_hpi_device_reg_read(self, CY_PD_SILICON_ID, buf, sizeof(buf), error)) { + g_prefix_error(error, "get silicon id error: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x0, + &self->silicon_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* add quirks */ + if (self->silicon_id != 0x0) + fu_device_add_instance_u16(FU_DEVICE(self), "SID", self->silicon_id); + fu_device_build_instance_id_quirk(FU_DEVICE(self), NULL, "CCGX", "SID", NULL); + + /* sanity check */ + if (self->flash_row_size == 0x0 || self->flash_size == 0x0 || + self->flash_size % self->flash_row_size != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid row size for: 0x%x/0x%x", + self->flash_row_size, + self->flash_size); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_ccgx_hpi_device_set_version_raw(FuCcgxHpiDevice *self, guint32 version_raw) +{ + g_autofree gchar *version = fu_ccgx_version_to_string(version_raw); + fu_device_set_version(FU_DEVICE(self), version); + fu_device_set_version_raw(FU_DEVICE(self), version_raw); +} + +static gboolean +fu_ccgx_hpi_device_setup(FuDevice *device, GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + CyI2CConfig i2c_config = {0x0}; + guint32 hpi_event = 0; + guint8 mode = 0; + g_autoptr(GError) error_local = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_ccgx_hpi_device_parent_class)->setup(device, error)) + return FALSE; + + /* set the new config */ + if (!fu_ccgx_hpi_device_get_i2c_config(self, &i2c_config, error)) { + g_prefix_error(error, "get config error: "); + return FALSE; + } + i2c_config.frequency = FU_CCGX_HPI_FREQ; + i2c_config.is_initiator = TRUE; + i2c_config.is_msb_first = TRUE; + if (!fu_ccgx_hpi_device_set_i2c_config(self, &i2c_config, error)) { + g_prefix_error(error, "set config error: "); + return FALSE; + } + if (!fu_ccgx_hpi_device_reg_read(self, CY_PD_REG_DEVICE_MODE_ADDR, &mode, 1, error)) { + g_prefix_error(error, "get device mode error: "); + return FALSE; + } + self->hpi_addrsz = mode & 0x80 ? 2 : 1; + self->num_ports = (mode >> 2) & 0x03 ? 2 : 1; + self->fw_mode = (FWMode)(mode & 0x03); + fu_device_set_logical_id(device, fu_ccgx_fw_mode_to_string(self->fw_mode)); + fu_device_add_instance_str(device, "MODE", fu_device_get_logical_id(device)); + + /* get silicon ID */ + if (!fu_ccgx_hpi_device_ensure_silicon_id(self, error)) + return FALSE; + + /* get correct version if not in boot mode */ + if (self->fw_mode != FW_MODE_BOOT) { + guint16 bufsz; + guint32 versions[FW_MODE_LAST] = {0x0}; + guint8 bufver[HPI_DEVICE_VERSION_SIZE_HPIV2] = {0x0}; + + bufsz = self->hpi_addrsz == 1 ? HPI_DEVICE_VERSION_SIZE_HPIV1 + : HPI_DEVICE_VERSION_SIZE_HPIV2; + if (!fu_ccgx_hpi_device_reg_read(self, CY_PD_GET_VERSION, bufver, bufsz, error)) { + g_prefix_error(error, "get version error: "); + return FALSE; + } + + /* fw1 */ + if (!fu_memread_uint32_safe(bufver, + sizeof(bufver), + 0x0c, + &versions[FW_MODE_FW1], + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* fw2 */ + if (!fu_memread_uint32_safe(bufver, + sizeof(bufver), + 0x14, + &versions[FW_MODE_FW2], + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* add GUIDs that are specific to the firmware app type */ + self->fw_app_type = versions[self->fw_mode] & 0xffff; + if (self->fw_app_type != 0x0) + fu_device_add_instance_u16(device, "APP", self->fw_app_type); + + /* if running in bootloader force an upgrade to any version */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_ccgx_hpi_device_set_version_raw(self, 0x0); + } else { + fu_ccgx_hpi_device_set_version_raw(self, versions[self->fw_mode]); + } + } + + /* not supported in boot mode */ + if (self->fw_mode == FW_MODE_BOOT) { + fu_device_inhibit(device, "device-in-boot-mode", "Not supported in BOOT mode"); + } else { + fu_device_uninhibit(device, "device-in-boot-mode"); + } + + /* add extra instance IDs */ + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "SID", "APP", NULL); + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "SID", "APP", "MODE", NULL); + + /* if we are coming back from reset, wait for hardware to settle */ + if (!fu_ccgx_hpi_device_get_event(self, + HPI_REG_SECTION_DEV, + &hpi_event, + HPI_CMD_SETUP_EVENT_WAIT_TIME_MS, + &error_local)) { + if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } else { + if (hpi_event == CY_PD_RESP_RESET_COMPLETE) + g_usleep(HPI_CMD_RESET_COMPLETE_DELAY_US); + } + + /* start with no events in the queue */ + return fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_SETUP_EVENT_CLEAR_TIME_MS, error); +} + +static gboolean +fu_ccgx_hpi_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "SiliconId") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->silicon_id = tmp; + return TRUE; + } + if (g_strcmp0(key, "CcgxFlashRowSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->flash_row_size = tmp; + return TRUE; + } + if (g_strcmp0(key, "CcgxFlashSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->flash_size = tmp; + return TRUE; + } + if (g_strcmp0(key, "CcgxImageKind") == 0) { + self->fw_image_type = fu_ccgx_fw_image_type_from_string(value); + if (self->fw_image_type != FW_IMAGE_TYPE_UNKNOWN) + return TRUE; + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid CcgxImageKind"); + return FALSE; + } + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported"); + return FALSE; +} + +static gboolean +fu_ccgx_hpi_device_close(FuDevice *device, GError **error) +{ + /* do not close handle when device restarts */ + if (fu_device_has_private_flag(device, FU_CCGX_HPI_DEVICE_IS_IN_RESTART)) + return TRUE; + + /* FuUsbDevice->close */ + return FU_DEVICE_CLASS(fu_ccgx_hpi_device_parent_class)->close(device, error); +} + +static void +fu_ccgx_hpi_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_ccgx_hpi_device_init(FuCcgxHpiDevice *self) +{ + self->inf_num = 0x0; + self->hpi_addrsz = 1; + self->num_ports = 1; + self->target_address = PD_I2C_TARGET_ADDRESS; + self->ep_bulk_out = PD_I2C_USB_EP_BULK_OUT; + self->ep_bulk_in = PD_I2C_USB_EP_BULK_IN; + self->ep_intr_in = PD_I2C_USB_EP_INTR_IN; + fu_device_add_protocol(FU_DEVICE(self), "com.cypress.ccgx"); + fu_device_add_protocol(FU_DEVICE(self), "com.infineon.ccgx"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_retry_set_delay(FU_DEVICE(self), HPI_CMD_RETRY_DELAY); + + /* we can recover the I²C link using reset */ + fu_device_retry_add_recovery(FU_DEVICE(self), + FWUPD_ERROR, + FWUPD_ERROR_READ, + fu_ccgx_hpi_device_i2c_reset_cb); + fu_device_retry_add_recovery(FU_DEVICE(self), + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + fu_ccgx_hpi_device_i2c_reset_cb); + + /* this might not be true for future hardware */ + if (self->inf_num > 0) + self->scb_index = 1; + fu_usb_device_add_interface(FU_USB_DEVICE(self), self->inf_num); +} + +static void +fu_ccgx_hpi_device_class_init(FuCcgxHpiDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_ccgx_hpi_device_to_string; + klass_device->write_firmware = fu_ccgx_hpi_write_firmware; + klass_device->prepare_firmware = fu_ccgx_hpi_device_prepare_firmware; + klass_device->detach = fu_ccgx_hpi_device_detach; + klass_device->attach = fu_ccgx_hpi_device_attach; + klass_device->setup = fu_ccgx_hpi_device_setup; + klass_device->set_quirk_kv = fu_ccgx_hpi_device_set_quirk_kv; + klass_device->close = fu_ccgx_hpi_device_close; + klass_device->set_progress = fu_ccgx_hpi_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-device.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..253c3346cabf4da248907ecd08937eb729b96b12 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-hpi-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CCGX_HPI_DEVICE (fu_ccgx_hpi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCcgxHpiDevice, fu_ccgx_hpi_device, FU, CCGX_HPI_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-plugin.c b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..076454dc877193f1c12148544b30adabae9b855c --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-plugin.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ccgx-dmc-device.h" +#include "fu-ccgx-dmc-firmware.h" +#include "fu-ccgx-firmware.h" +#include "fu-ccgx-hid-device.h" +#include "fu-ccgx-hpi-device.h" +#include "fu-ccgx-plugin.h" + +struct _FuCcgxPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuCcgxPlugin, fu_ccgx_plugin, FU_TYPE_PLUGIN) + +static void +fu_ccgx_plugin_init(FuCcgxPlugin *self) +{ +} + +static void +fu_ccgx_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "CcgxFlashRowSize"); + fu_context_add_quirk_key(ctx, "CcgxFlashSize"); + fu_context_add_quirk_key(ctx, "CcgxImageKind"); + fu_context_add_quirk_key(ctx, "CcgxDmcTriggerCode"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_CCGX_FIRMWARE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_CCGX_DMC_FIRMWARE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CCGX_HID_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CCGX_HPI_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CCGX_DMC_DEVICE); +} + +static void +fu_ccgx_plugin_class_init(FuCcgxPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_ccgx_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/ccgx/fu-ccgx-plugin.h b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..18fc112f21a1586b27e0b6a721a237b6401366e6 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-ccgx-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuCcgxPlugin, fu_ccgx_plugin, FU, CCGX_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/ccgx/fu-self-test.c b/fwupd-1.8.6/plugins/ccgx/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..8c33b3533f1e556be6ae9d7b9b3dfc3ffeb35bc2 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/fu-self-test.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ccgx-dmc-firmware.h" +#include "fu-ccgx-firmware.h" + +static void +fu_ccgx_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_ccgx_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_ccgx_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "ccgx.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +static void +fu_ccgx_dmc_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_ccgx_dmc_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_ccgx_dmc_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "ccgx-dmc.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + 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("/ccgx/firmware{xml}", fu_ccgx_firmware_xml_func); + g_test_add_func("/ccgx-dmc/firmware{xml}", fu_ccgx_dmc_firmware_xml_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/ccgx/meson.build b/fwupd-1.8.6/plugins/ccgx/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..949591fac430f468851bb2f34b4bd0a89f4fdfdf --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/meson.build @@ -0,0 +1,53 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginCcgx"'] + +plugin_quirks += files([ + 'ccgx-ids.quirk', + 'ccgx.quirk', + ]) +plugin_builtin_ccgx = static_library('fu_plugin_ccgx', + sources: [ + 'fu-ccgx-plugin.c', + 'fu-ccgx-common.c', # fuzzing + 'fu-ccgx-firmware.c', # fuzzing + 'fu-ccgx-hid-device.c', + 'fu-ccgx-hpi-common.c', + 'fu-ccgx-hpi-device.c', + 'fu-ccgx-dmc-device.c', + 'fu-ccgx-dmc-firmware.c', # fuzzing + 'fu-ccgx-dmc-common.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + gudev, + ], +) +plugin_builtins += plugin_builtin_ccgx + +if get_option('tests') + install_data(['tests/ccgx.builder.xml', 'tests/ccgx-dmc.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'ccgx-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_ccgx, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('ccgx-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/ccgx/tests/ccgx-dmc.bin b/fwupd-1.8.6/plugins/ccgx/tests/ccgx-dmc.bin new file mode 100644 index 0000000000000000000000000000000000000000..d378c83685584c1542c0c5434a0da55b82e70445 Binary files /dev/null and b/fwupd-1.8.6/plugins/ccgx/tests/ccgx-dmc.bin differ diff --git a/fwupd-1.8.6/plugins/ccgx/tests/ccgx-dmc.builder.xml b/fwupd-1.8.6/plugins/ccgx/tests/ccgx-dmc.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..b7038c15966971bf1bee069dd757b9fe6fdc1a78 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/tests/ccgx-dmc.builder.xml @@ -0,0 +1,4 @@ + + 0x1000800 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/ccgx/tests/ccgx.bin b/fwupd-1.8.6/plugins/ccgx/tests/ccgx.bin new file mode 100644 index 0000000000000000000000000000000000000000..c81bc5fee36fa436ed511261bbe11cfe2dc83077 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/tests/ccgx.bin @@ -0,0 +1,3 @@ +1F0011AF0000 +:00000E000B68656C6C6F20776F726C648B +:00000C008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A400000000130000000B00000000000000000000000059430000000000000000000000000000000000000000000000000000000000000000000000000000000016 diff --git a/fwupd-1.8.6/plugins/ccgx/tests/ccgx.builder.xml b/fwupd-1.8.6/plugins/ccgx/tests/ccgx.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..8a7f181fe43d9ca9fe8afb8a9f630993de51e270 --- /dev/null +++ b/fwupd-1.8.6/plugins/ccgx/tests/ccgx.builder.xml @@ -0,0 +1,4 @@ + + 0x1F00 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/cfu/README.md b/fwupd-1.8.6/plugins/cfu/README.md new file mode 100644 index 0000000000000000000000000000000000000000..53446267ba14341d29d6fc598a5ca73f5a7b95d4 --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/README.md @@ -0,0 +1,38 @@ +# Component Firmware update + +## Introduction + +CFU is a protocol from Microsoft to make it easy to install firmware on HID devices. + +This protocol is unique in that it requires has a pre-download phase before sending the firmware to +the microcontroller. This is so the device can check if the firmware is required and compatible. +CFU also requires devices to be able to transfer the entire new transfer mode in runtime mode. + +See for more +details. + +This plugin supports the following protocol ID: + +* com.microsoft.cfu + +## GUID Generation + +These devices use standard USB DeviceInstanceId values, as well as two extra for the component ID +and the bank, e.g. + +* `HIDRAW\VEN_17EF&DEV_7226&CID_01&BANK_1` +* `HIDRAW\VEN_17EF&DEV_7226&CID_01` +* `HIDRAW\VEN_17EF&DEV_7226` + +## Update Behavior + +The device has to support runtime updates and does not have a detach-into-bootloader mode -- but +after the install has completed the device still has to reboot into the new firmware. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `HIDRAW:0x17EF` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/cfu/cfu.quirk b/fwupd-1.8.6/plugins/cfu/cfu.quirk new file mode 100644 index 0000000000000000000000000000000000000000..445eac333f80a6b8d09a7ace0ebb2b884bdec58c --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/cfu.quirk @@ -0,0 +1,2 @@ +[USB\VID_273F&PID_100A] +Plugin = cfu diff --git a/fwupd-1.8.6/plugins/cfu/fu-cfu-device.c b/fwupd-1.8.6/plugins/cfu/fu-cfu-device.c new file mode 100644 index 0000000000000000000000000000000000000000..1e86476634a5042d3da5c8d46b3c7e55749d24ec --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/fu-cfu-device.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-cfu-device.h" +#include "fu-cfu-module.h" + +struct _FuCfuDevice { + FuHidDevice parent_instance; + guint8 protocol_version; +}; + +G_DEFINE_TYPE(FuCfuDevice, fu_cfu_device, FU_TYPE_HID_DEVICE) + +#define FU_CFU_DEVICE_TIMEOUT 5000 /* ms */ +#define FU_CFU_FEATURE_SIZE 60 /* bytes */ + +#define FU_CFU_CMD_GET_FIRMWARE_VERSION 0x00 +#define FU_CFU_CMD_SEND_OFFER 0x00 // TODO + +static void +fu_cfu_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCfuDevice *self = FU_CFU_DEVICE(device); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_cfu_device_parent_class)->to_string(device, idt, str); + + fu_string_append_kx(str, idt, "ProtocolVersion", self->protocol_version); +} + +static gboolean +fu_cfu_device_write_offer(FuCfuDevice *self, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + guint8 buf2[FU_CFU_FEATURE_SIZE] = {0}; + g_autofree guint8 *buf_tmp = NULL; + g_autoptr(GBytes) blob = NULL; + + /* generate a offer blob */ + if (flags & FWUPD_INSTALL_FLAG_FORCE) + fu_cfu_offer_set_force_ignore_version(FU_CFU_OFFER(firmware), TRUE); + blob = fu_firmware_write(firmware, error); + if (blob == NULL) + return FALSE; + + /* send it to the hardware */ + buf = g_bytes_get_data(blob, &bufsz); + buf_tmp = fu_memdup_safe(buf, bufsz, error); + if (buf_tmp == NULL) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + FU_CFU_CMD_SEND_OFFER, + buf_tmp, + bufsz, + FU_CFU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to send offer: "); + return FALSE; + } + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + FU_CFU_CMD_SEND_OFFER, + buf2, + sizeof(buf2), + FU_CFU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + return FALSE; + } + g_debug("status:%s reject:%s", + fu_cfu_device_offer_to_string(buf2[13]), + fu_cfu_device_reject_to_string(buf2[9])); + if (buf2[13] != FU_CFU_DEVICE_OFFER_ACCEPT) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "not supported: %s", + fu_cfu_device_offer_to_string(buf2[13])); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cfu_device_write_payload(FuCfuDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + /* write each chunk */ + chunks = fu_firmware_get_chunks(firmware, error); + if (chunks == NULL) + return FALSE; + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 databuf[60] = {0}; + guint8 buf2[60] = {0}; + + /* flags */ + if (i == 0) + databuf[0] = FU_CFU_DEVICE_FLAG_FIRST_BLOCK; + else if (i == chunks->len - 1) + databuf[0] = FU_CFU_DEVICE_FLAG_LAST_BLOCK; + + /* length */ + databuf[1] = fu_chunk_get_data_sz(chk); + + /* sequence number */ + if (!fu_memwrite_uint16_safe(databuf, + sizeof(databuf), + 0x2, + i + 1, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* address */ + if (!fu_memwrite_uint32_safe(databuf, + sizeof(databuf), + 0x4, + fu_chunk_get_address(chk), + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* data */ + if (!fu_memcpy_safe(databuf, + sizeof(databuf), + 0x8, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "memory copy for payload fail: "); + return FALSE; + } + // send + // revc + if (buf2[5] != FU_CFU_DEVICE_STATUS_SUCCESS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to send chunk %u: %s", + i + 1, + fu_cfu_device_status_to_string(buf2[5])); + return FALSE; + } + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cfu_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuCfuDevice *self = FU_CFU_DEVICE(device); + g_autoptr(FuFirmware) fw_offer = NULL; + g_autoptr(FuFirmware) fw_payload = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "offer"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "payload"); + + /* send offer */ + fw_offer = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_HEADER, error); + if (fw_offer == NULL) + return FALSE; + if (!fu_cfu_device_write_offer(self, + fw_offer, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* send payload */ + fw_payload = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_PAYLOAD, error); + if (fw_payload == NULL) + return FALSE; + if (!fu_cfu_device_write_payload(self, fw_payload, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_cfu_device_setup(FuDevice *device, GError **error) +{ + FuCfuDevice *self = FU_CFU_DEVICE(device); + guint8 buf[FU_CFU_FEATURE_SIZE] = {0}; + guint8 component_cnt = 0; + guint8 tmp = 0; + gsize offset = 0; + g_autoptr(GHashTable) modules_by_cid = NULL; + + /* FuHidDevice->setup */ + if (!FU_DEVICE_CLASS(fu_cfu_device_parent_class)->setup(device, error)) + return FALSE; + + /* get version */ + if (!fu_hid_device_get_report(FU_HID_DEVICE(device), + FU_CFU_CMD_GET_FIRMWARE_VERSION, + buf, + sizeof(buf), + FU_CFU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + return FALSE; + } + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x0, &component_cnt, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x3, &tmp, error)) + return FALSE; + self->protocol_version = tmp & 0b1111; + + /* keep track of all modules so we can work out which are dual bank */ + modules_by_cid = g_hash_table_new(g_int_hash, g_int_equal); + + /* read each component module version */ + offset += 4; + for (guint i = 0; i < component_cnt; i++) { + g_autoptr(FuCfuModule) module = fu_cfu_module_new(device); + FuCfuModule *module_tmp; + + if (!fu_cfu_module_setup(module, buf, sizeof(buf), offset, error)) + return FALSE; + fu_device_add_child(device, FU_DEVICE(module)); + + /* same module already exists, so mark both as being dual bank */ + module_tmp = + g_hash_table_lookup(modules_by_cid, + GINT_TO_POINTER(fu_cfu_module_get_component_id(module))); + if (module_tmp != NULL) { + fu_device_add_flag(FU_DEVICE(module), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(module_tmp), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + } else { + g_hash_table_insert(modules_by_cid, + GINT_TO_POINTER(fu_cfu_module_get_component_id(module)), + module); + } + + /* done */ + offset += 0x8; + } + + /* success */ + return TRUE; +} + +static void +fu_cfu_device_init(FuCfuDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); +} + +static void +fu_cfu_device_class_init(FuCfuDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_cfu_device_setup; + klass_device->to_string = fu_cfu_device_to_string; + klass_device->write_firmware = fu_cfu_device_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/cfu/fu-cfu-device.h b/fwupd-1.8.6/plugins/cfu/fu-cfu-device.h new file mode 100644 index 0000000000000000000000000000000000000000..08907aa422d382a5d9fa04676d81e61a18c8b85c --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/fu-cfu-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CFU_DEVICE (fu_cfu_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCfuDevice, fu_cfu_device, FU, CFU_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/cfu/fu-cfu-module.c b/fwupd-1.8.6/plugins/cfu/fu-cfu-module.c new file mode 100644 index 0000000000000000000000000000000000000000..c5fae84087c6a733843d08d49ccac8a827ae8f3a --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/fu-cfu-module.c @@ -0,0 +1,198 @@ +/*# + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-cfu-module.h" + +struct _FuCfuModule { + FuDevice parent_instance; + guint8 component_id; + guint8 bank; +}; + +G_DEFINE_TYPE(FuCfuModule, fu_cfu_module, FU_TYPE_DEVICE) + +static void +fu_cfu_module_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCfuModule *self = FU_CFU_MODULE(device); + fu_string_append_kx(str, idt, "ComponentId", self->component_id); + fu_string_append_kx(str, idt, "Bank", self->bank); +} + +guint8 +fu_cfu_module_get_component_id(FuCfuModule *self) +{ + return self->component_id; +} + +guint8 +fu_cfu_module_get_bank(FuCfuModule *self) +{ + return self->bank; +} + +gboolean +fu_cfu_module_setup(FuCfuModule *self, const guint8 *buf, gsize bufsz, gsize offset, GError **error) +{ + FuDevice *device = FU_DEVICE(self); + FuDevice *parent = fu_device_get_proxy(device); + guint32 version_raw = 0; + guint8 tmp = 0; + g_autofree gchar *logical_id = NULL; + g_autofree gchar *version = NULL; + + /* component ID */ + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x5, &self->component_id, error)) + return FALSE; + + /* these GUIDs may cause the name or version-format to be overwritten */ + fu_device_add_instance_u8(device, "CID", self->component_id); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "CID", NULL)) + return FALSE; + + /* bank */ + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x4, &tmp, error)) + return FALSE; + self->bank = tmp & 0b11; + fu_device_add_instance_u4(device, "BANK", self->bank); + if (!fu_device_build_instance_id(device, + error, + "HIDRAW", + "VEN", + "DEV", + "CID", + "BANK", + NULL)) + return FALSE; + + /* set name, if not already set using a quirk */ + if (fu_device_get_name(device) == NULL) { + g_autofree gchar *name = NULL; + name = g_strdup_printf("%s (0x%02X:0x%02x)", + fu_device_get_name(parent), + self->component_id, + self->bank); + fu_device_set_name(device, name); + } + + /* version */ + if (!fu_memread_uint32_safe(buf, bufsz, offset, &version_raw, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_device_set_version_raw(device, version_raw); + version = fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); + fu_device_set_version(device, version); + + /* logical ID */ + logical_id = g_strdup_printf("CID:0x%02x,BANK:0x%02x", self->component_id, self->bank); + fu_device_set_logical_id(device, logical_id); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_cfu_module_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(FuFirmware) offer = fu_cfu_offer_new(); + g_autoptr(FuFirmware) payload = fu_cfu_payload_new(); + g_autoptr(GBytes) fw_offset = NULL; + + /* offer */ + if (!fu_firmware_parse(offer, fw, flags, error)) + return NULL; + fu_firmware_set_id(offer, FU_FIRMWARE_ID_HEADER); + fu_firmware_add_image(firmware, offer); + + /* payload */ + fw_offset = fu_bytes_new_offset(fw, 0x10, g_bytes_get_size(fw) - 0x10, error); + if (fw_offset == NULL) + return NULL; + if (!fu_firmware_parse(payload, fw_offset, flags, error)) + return NULL; + fu_firmware_set_id(payload, FU_FIRMWARE_ID_PAYLOAD); + fu_firmware_add_image(firmware, payload); + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_cfu_module_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *proxy; + g_autoptr(GBytes) fw = NULL; + + /* get the whole image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* process by the parent */ + proxy = fu_device_get_proxy(device); + if (proxy == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no proxy device assigned"); + return FALSE; + } + return fu_device_write_firmware(proxy, fw, progress, flags, error); +} + +static void +fu_cfu_module_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_cfu_module_init(FuCfuModule *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.microsoft.cfu"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); +} + +static void +fu_cfu_module_class_init(FuCfuModuleClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_cfu_module_to_string; + klass_device->prepare_firmware = fu_cfu_module_prepare_firmware; + klass_device->write_firmware = fu_cfu_module_write_firmware; + klass_device->set_progress = fu_cfu_module_set_progress; +} + +FuCfuModule * +fu_cfu_module_new(FuDevice *parent) +{ + FuCfuModule *self; + self = g_object_new(FU_TYPE_CFU_MODULE, + "ctx", + fu_device_get_context(parent), + "proxy", + parent, + NULL); + return self; +} diff --git a/fwupd-1.8.6/plugins/cfu/fu-cfu-module.h b/fwupd-1.8.6/plugins/cfu/fu-cfu-module.h new file mode 100644 index 0000000000000000000000000000000000000000..b6f553985248ead8af7b285830ffb5a855911487 --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/fu-cfu-module.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CFU_MODULE (fu_cfu_module_get_type()) +G_DECLARE_FINAL_TYPE(FuCfuModule, fu_cfu_module, FU, CFU_MODULE, FuDevice) + +guint8 +fu_cfu_module_get_component_id(FuCfuModule *self); +guint8 +fu_cfu_module_get_bank(FuCfuModule *self); +gboolean +fu_cfu_module_setup(FuCfuModule *self, + const guint8 *buf, + gsize bufsz, + gsize offset, + GError **error); +FuCfuModule * +fu_cfu_module_new(FuDevice *parent); diff --git a/fwupd-1.8.6/plugins/cfu/fu-cfu-plugin.c b/fwupd-1.8.6/plugins/cfu/fu-cfu-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..847471b0cd0d042496be359fa67146f482e74f79 --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/fu-cfu-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-cfu-device.h" +#include "fu-cfu-plugin.h" + +struct _FuCfuPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuCfuPlugin, fu_cfu_plugin, FU_TYPE_PLUGIN) + +static void +fu_cfu_plugin_init(FuCfuPlugin *self) +{ +} + +static void +fu_cfu_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CFU_DEVICE); +} + +static void +fu_cfu_plugin_class_init(FuCfuPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_cfu_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/cfu/fu-cfu-plugin.h b/fwupd-1.8.6/plugins/cfu/fu-cfu-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..acbe48f3791ba233847cddb2e92a9ef6f57d2093 --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/fu-cfu-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuCfuPlugin, fu_cfu_plugin, FU, CFU_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/cfu/meson.build b/fwupd-1.8.6/plugins/cfu/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..02aa05e402664fb6c3b481f5882b66ae697d128d --- /dev/null +++ b/fwupd-1.8.6/plugins/cfu/meson.build @@ -0,0 +1,17 @@ +if get_option('plugin_cfu').require(gudev.found(), + error_message: 'gudev is needed for plugin_cfu').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginCfu"'] + +plugin_quirks += files('cfu.quirk') +plugin_builtins += static_library('fu_plugin_cfu', + sources: [ + 'fu-cfu-device.c', + 'fu-cfu-module.c', + 'fu-cfu-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/ch341a/README.md b/fwupd-1.8.6/plugins/ch341a/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9b873feeeeae3aaf66cc2e014566ccc7ef99f734 --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/README.md @@ -0,0 +1,47 @@ +# CH341A + +## Introduction + +The CH341A is an affordable SPI programmer. + +The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip: + + UIO CH341A SPI CH341A + 0 D0/15 CS/1 CS0 + 1 D1/16 unused CS1 + 2 D2/17 unused CS2 + 3 D3/18 SCK/6 DCK + 4 D4/19 unused DOUT2 + 5 D5/20 SI/5 DOUBT + 6 D6/21 unused DIN2 + 7 D7/22 SO/2 DIN + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob of unspecified format. + +This plugin supports the following protocol ID: + +- com.winchiphead.ch341a + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +- `USB\VID_1A86&PID_5512&REV_0304` +- `USB\VID_1A86&PID_5512` + +## Update Behavior + +The device programs devices in raw mode, and can best be used with `fwupdtool`. + +To write an image, use `sudo fwupdtool --plugins ch341a install-blob firmware.bin` and to backup +the contents of a SPI device use `sudo fwupdtool --plugins ch341a firmware-dump backup.bin` + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1A86` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/ch341a/ch341a.quirk b/fwupd-1.8.6/plugins/ch341a/ch341a.quirk new file mode 100644 index 0000000000000000000000000000000000000000..f1744ae194900b4b5912586d1739ceaae541c8b6 --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/ch341a.quirk @@ -0,0 +1,2 @@ +[USB\VID_1A86&PID_5512] +Plugin = ch341a diff --git a/fwupd-1.8.6/plugins/ch341a/fu-ch341a-cfi-device.c b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-cfi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..d22653374bd6f58a71ba90c85806fba88ecfd8eb --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-cfi-device.c @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ch341a-cfi-device.h" +#include "fu-ch341a-device.h" + +struct _FuCh341aCfiDevice { + FuCfiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuCh341aCfiDevice, fu_ch341a_cfi_device, FU_TYPE_CFI_DEVICE) + +#define CH341A_PAYLOAD_SIZE 0x1A + +static gboolean +fu_ch341a_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error) +{ + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + return fu_ch341a_device_chip_select(proxy, value, error); +} + +typedef struct { + guint8 mask; + guint8 value; +} FuCh341aCfiDeviceHelper; + +static gboolean +fu_ch341a_cfi_device_wait_for_status_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuCh341aCfiDeviceHelper *helper = (FuCh341aCfiDeviceHelper *)user_data; + FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(device)); + guint8 buf[2] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_READ_STATUS, + &buf[0], + error)) + return FALSE; + if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to want to status: "); + return FALSE; + } + if ((buf[0x1] & helper->mask) != helper->value) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wanted 0x%x, got 0x%x", + helper->value, + buf[0x1] & helper->mask); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ch341a_cfi_device_wait_for_status(FuCh341aCfiDevice *self, + guint8 mask, + guint8 value, + guint count, + guint delay, + GError **error) +{ + FuCh341aCfiDeviceHelper helper = {.mask = mask, .value = value}; + return fu_device_retry_full(FU_DEVICE(self), + fu_ch341a_cfi_device_wait_for_status_cb, + count, + delay, + &helper, + error); +} + +static gboolean +fu_ch341a_cfi_device_read_jedec(FuCh341aCfiDevice *self, GError **error) +{ + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint8 buf[CH341A_PAYLOAD_SIZE] = {0x9F}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + g_autoptr(GString) flash_id = g_string_new(NULL); + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + + /* read JEDEC ID */ + if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to request JEDEC ID: "); + return FALSE; + } + if (buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash ID non-valid"); + return FALSE; + } + if (buf[1] == 0xFF && buf[2] == 0xFF && buf[3] == 0xFF) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device not detected"); + return FALSE; + } + g_string_append_printf(flash_id, "%02X", buf[1]); + g_string_append_printf(flash_id, "%02X", buf[2]); + g_string_append_printf(flash_id, "%02X", buf[3]); + fu_cfi_device_set_flash_id(FU_CFI_DEVICE(self), flash_id->str); + + /* success */ + return TRUE; +} + +static gboolean +fu_ch341a_cfi_device_setup(FuDevice *device, GError **error) +{ + FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); + + /* setup SPI chip */ + if (!fu_ch341a_cfi_device_read_jedec(self, error)) + return FALSE; + + /* this is a generic SPI chip */ + fu_device_add_instance_id(device, "SPI"); + fu_device_add_vendor_id(device, "SPI:*"); + + /* FuCfiDevice->setup */ + return FU_DEVICE_CLASS(fu_ch341a_cfi_device_parent_class)->setup(device, error); +} + +static gboolean +fu_ch341a_cfi_device_write_enable(FuCh341aCfiDevice *self, GError **error) +{ + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint8 buf[1] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + /* write enable */ + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), FU_CFI_DEVICE_CMD_WRITE_EN, &buf[0], error)) + return FALSE; + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return FALSE; + if (!fu_device_locker_close(cslocker, error)) + return FALSE; + + /* check that WEL is now set */ + return fu_ch341a_cfi_device_wait_for_status(self, 0b10, 0b10, 10, 5, error); +} + +static gboolean +fu_ch341a_cfi_device_chip_erase(FuCh341aCfiDevice *self, GError **error) +{ + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint8 buf[] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + + /* erase */ + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_CHIP_ERASE, + &buf[0], + error)) + return FALSE; + if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return FALSE; + if (!fu_device_locker_close(cslocker, error)) + return FALSE; + + /* poll Read Status register BUSY */ + return fu_ch341a_cfi_device_wait_for_status(self, 0b1, 0b0, 100, 500, error); +} + +static gboolean +fu_ch341a_cfi_device_write_page(FuCh341aCfiDevice *self, FuChunk *page, GError **error) +{ + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint8 buf[4] = {0x0}; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + if (!fu_ch341a_cfi_device_write_enable(self, error)) + return FALSE; + + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + + /* cmd, then 24 bit starting address */ + fu_memwrite_uint32(buf, fu_chunk_get_address(page), G_BIG_ENDIAN); + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_PAGE_PROG, + &buf[0], + error)) + return FALSE; + if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return FALSE; + + /* send data */ + chunks = fu_chunk_array_new(fu_chunk_get_data(page), + fu_chunk_get_data_sz(page), + 0x0, + 0x0, + CH341A_PAYLOAD_SIZE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 buf2[CH341A_PAYLOAD_SIZE] = {0x0}; + if (!fu_memcpy_safe(buf2, + sizeof(buf2), + 0x0, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + if (!fu_ch341a_device_spi_transfer(proxy, buf2, fu_chunk_get_data_sz(chk), error)) + return FALSE; + } + if (!fu_device_locker_close(cslocker, error)) + return FALSE; + + /* poll Read Status register BUSY */ + return fu_ch341a_cfi_device_wait_for_status(self, 0b1, 0b0, 100, 50, error); +} + +static gboolean +fu_ch341a_cfi_device_write_pages(FuCh341aCfiDevice *self, + GPtrArray *pages, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, pages->len); + for (guint i = 0; i < pages->len; i++) { + FuChunk *page = g_ptr_array_index(pages, i); + if (!fu_ch341a_cfi_device_write_page(self, page, error)) + return FALSE; + fu_progress_step_done(progress); + } + /* success */ + return TRUE; +} + +static GBytes * +fu_ch341a_cfi_device_read_firmware(FuCh341aCfiDevice *self, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint8 buf[CH341A_PAYLOAD_SIZE] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + g_autoptr(GByteArray) blob = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return NULL; + + /* read each block */ + chunks = fu_chunk_array_new(NULL, bufsz + 0x4, 0x0, 0x0, CH341A_PAYLOAD_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + + /* cmd, then 24 bit starting address */ + fu_memwrite_uint32(buf, 0x0, G_BIG_ENDIAN); + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_READ_DATA, + &buf[0], + error)) + return NULL; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + /* the first package has cmd and address info */ + if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return NULL; + if (i == 0) { + g_byte_array_append(blob, buf + 0x4, fu_chunk_get_data_sz(chk) - 0x4); + } else { + g_byte_array_append(blob, buf + 0x0, fu_chunk_get_data_sz(chk)); + } + + /* done */ + fu_progress_step_done(progress); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&blob)); +} + +static gboolean +fu_ch341a_cfi_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_verify = NULL; + g_autoptr(GPtrArray) pages = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open programmer */ + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 33, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 44, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 35, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* erase */ + if (!fu_ch341a_cfi_device_write_enable(self, error)) { + g_prefix_error(error, "failed to enable writes: "); + return FALSE; + } + if (!fu_ch341a_cfi_device_chip_erase(self, error)) { + g_prefix_error(error, "failed to erase: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* write each block */ + pages = fu_chunk_array_new_from_bytes(fw, + 0x0, + 0x0, + fu_cfi_device_get_page_size(FU_CFI_DEVICE(self))); + if (!fu_ch341a_cfi_device_write_pages(self, + pages, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write pages: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* verify each block */ + fw_verify = fu_ch341a_cfi_device_read_firmware(self, + g_bytes_get_size(fw), + fu_progress_get_child(progress), + error); + if (fw_verify == NULL) { + g_prefix_error(error, "failed to verify blocks: "); + return FALSE; + } + if (!fu_bytes_compare(fw, fw_verify, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static GBytes * +fu_ch341a_cfi_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); + FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + gsize bufsz = fu_device_get_firmware_size_max(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open programmer */ + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return NULL; + + /* sanity check */ + if (bufsz == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "device firmware size not set"); + return NULL; + } + return fu_ch341a_cfi_device_read_firmware(self, bufsz, progress, error); +} + +static void +fu_ch341a_cfi_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_ch341a_cfi_device_init(FuCh341aCfiDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "org.jedec.cfi"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); +} + +static void +fu_ch341a_cfi_device_class_init(FuCh341aCfiDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuCfiDeviceClass *klass_cfi = FU_CFI_DEVICE_CLASS(klass); + + klass_cfi->chip_select = fu_ch341a_cfi_device_chip_select; + + klass_device->setup = fu_ch341a_cfi_device_setup; + klass_device->write_firmware = fu_ch341a_cfi_device_write_firmware; + klass_device->dump_firmware = fu_ch341a_cfi_device_dump_firmware; + klass_device->set_progress = fu_ch341a_cfi_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/ch341a/fu-ch341a-cfi-device.h b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-cfi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..91814b2bad5044d85daa54cb3bd0b2e1c12c49a6 --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-cfi-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CH341A_CFI_DEVICE (fu_ch341a_cfi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCh341aCfiDevice, fu_ch341a_cfi_device, FU, CH341A_CFI_DEVICE, FuCfiDevice) diff --git a/fwupd-1.8.6/plugins/ch341a/fu-ch341a-device.c b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-device.c new file mode 100644 index 0000000000000000000000000000000000000000..eb38cd3ea81c4e0bea96ab0364c182bc0184a42f --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-device.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ch341a-cfi-device.h" +#include "fu-ch341a-device.h" + +struct _FuCh341aDevice { + FuUsbDevice parent_instance; + guint8 speed; +}; + +G_DEFINE_TYPE(FuCh341aDevice, fu_ch341a_device, FU_TYPE_USB_DEVICE) + +#define CH341A_USB_TIMEOUT 1000 +#define CH341A_EP_OUT 0x02 /* host to device (write) */ +#define CH341A_EP_IN 0x82 /* device to host (read) */ +#define CH341A_EP_SIZE 0x20 + +#define CH341A_CMD_SET_OUTPUT 0xA1 +#define CH341A_CMD_IO_ADDR 0xA2 +#define CH341A_CMD_PRINT_OUT 0xA3 +#define CH341A_CMD_SPI_STREAM 0xA8 +#define CH341A_CMD_SIO_STREAM 0xA9 +#define CH341A_CMD_I2C_STREAM 0xAA +#define CH341A_CMD_UIO_STREAM 0xAB + +#define CH341A_CMD_I2C_STM_START 0x74 +#define CH341A_CMD_I2C_STM_STOP 0x75 +#define CH341A_CMD_I2C_STM_OUT 0x80 +#define CH341A_CMD_I2C_STM_IN 0xC0 +#define CH341A_CMD_I2C_STM_SET 0x60 +#define CH341A_CMD_I2C_STM_US 0x40 +#define CH341A_CMD_I2C_STM_MS 0x50 +#define CH341A_CMD_I2C_STM_DLY 0x0F +#define CH341A_CMD_I2C_STM_END 0x00 + +#define CH341A_CMD_UIO_STM_IN 0x00 +#define CH341A_CMD_UIO_STM_DIR 0x40 +#define CH341A_CMD_UIO_STM_OUT 0x80 +#define CH341A_CMD_UIO_STM_US 0xC0 +#define CH341A_CMD_UIO_STM_END 0x20 + +#define CH341A_STM_I2C_SPEED_LOW 0x00 +#define CH341A_STM_I2C_SPEED_STANDARD 0x01 +#define CH341A_STM_I2C_SPEED_FAST 0x02 +#define CH341A_STM_I2C_SPEED_HIGH 0x03 + +#define CH341A_STM_SPI_MODUS_STANDARD 0x00 +#define CH341A_STM_SPI_MODUS_DOUBLE 0x04 + +#define CH341A_STM_SPI_ENDIAN_BIG 0x0 +#define CH341A_STM_SPI_ENDIAN_LITTLE 0x80 + +static const gchar * +fu_ch341a_device_speed_to_string(guint8 speed) +{ + if (speed == CH341A_STM_I2C_SPEED_LOW) + return "20kHz"; + if (speed == CH341A_STM_I2C_SPEED_STANDARD) + return "100kHz"; + if (speed == CH341A_STM_I2C_SPEED_FAST) + return "400kHz"; + if (speed == CH341A_STM_I2C_SPEED_HIGH) + return "750kHz"; + if (speed == (CH341A_STM_I2C_SPEED_LOW | CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*20kHz"; + if (speed == (CH341A_STM_I2C_SPEED_STANDARD | CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*100kHz"; + if (speed == (CH341A_STM_I2C_SPEED_FAST | CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*400kHz"; + if (speed == (CH341A_STM_I2C_SPEED_HIGH | CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*750kHz"; + return NULL; +} + +static void +fu_ch341a_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCh341aDevice *self = FU_CH341A_DEVICE(device); + + /* FuUsbDevice->to_string */ + FU_DEVICE_CLASS(fu_ch341a_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "Speed", fu_ch341a_device_speed_to_string(self->speed)); +} + +static gboolean +fu_ch341a_device_write(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_length = 0; + + /* debug */ + if (g_getenv("FWUPD_CH341A_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "write", buf, bufsz); + + if (!g_usb_device_bulk_transfer(usb_device, + CH341A_EP_OUT, + buf, + bufsz, + &actual_length, + CH341A_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write 0x%x bytes:", (guint)bufsz); + return FALSE; + } + if (bufsz != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "only wrote 0x%x of 0x%x", + (guint)actual_length, + (guint)bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ch341a_device_read(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_length = 0; + + if (!g_usb_device_bulk_transfer(usb_device, + CH341A_EP_IN, + buf, + bufsz, + &actual_length, + CH341A_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read 0x%x bytes: ", (guint)bufsz); + return FALSE; + } + if (bufsz != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "only read 0x%x of 0x%x", + (guint)actual_length, + (guint)bufsz); + return FALSE; + } + + /* debug */ + if (g_getenv("FWUPD_CH341A_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "read", buf, bufsz); + + /* success */ + return TRUE; +} + +/** + * fu_ch341a_reverse_uint8: + * @value: integer + * + * Calculates the reverse bit order for a single byte. + * + * Returns: the @value, reversed + **/ +static guint8 +fu_ch341a_reverse_uint8(guint8 value) +{ + guint8 tmp = 0; + if (value & 0x01) + tmp = 0x80; + if (value & 0x02) + tmp |= 0x40; + if (value & 0x04) + tmp |= 0x20; + if (value & 0x08) + tmp |= 0x10; + if (value & 0x10) + tmp |= 0x08; + if (value & 0x20) + tmp |= 0x04; + if (value & 0x40) + tmp |= 0x02; + if (value & 0x80) + tmp |= 0x01; + return tmp; +} + +gboolean +fu_ch341a_device_spi_transfer(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + gsize buf2sz = bufsz + 1; + g_autofree guint8 *buf2 = g_malloc0(buf2sz); + + /* requires LSB first */ + buf2[0] = CH341A_CMD_SPI_STREAM; + for (gsize i = 0; i < bufsz; i++) + buf2[i + 1] = fu_ch341a_reverse_uint8(buf[i]); + + /* debug */ + if (g_getenv("FWUPD_CH341A_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SPIwrite", buf, bufsz); + if (!fu_ch341a_device_write(self, buf2, buf2sz, error)) + return FALSE; + + if (!fu_ch341a_device_read(self, buf, bufsz, error)) + return FALSE; + + /* requires LSB first */ + for (gsize i = 0; i < bufsz; i++) + buf[i] = fu_ch341a_reverse_uint8(buf[i]); + + /* debug */ + if (g_getenv("FWUPD_CH341A_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SPIread", buf, bufsz); + + /* success */ + return TRUE; +} + +static gboolean +fu_ch341a_device_configure_stream(FuCh341aDevice *self, GError **error) +{ + guint8 buf[] = {CH341A_CMD_I2C_STREAM, + CH341A_CMD_I2C_STM_SET | self->speed, + CH341A_CMD_I2C_STM_END}; + if (!fu_ch341a_device_write(self, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to configure stream: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_ch341a_device_chip_select(FuCh341aDevice *self, gboolean val, GError **error) +{ + guint8 buf[] = { + CH341A_CMD_UIO_STREAM, + CH341A_CMD_UIO_STM_OUT | (val ? 0x36 : 0x37), /* CS* high, SCK=0, DOUBT*=1 */ + CH341A_CMD_UIO_STM_DIR | (val ? 0x3F : 0x00), /* pin direction */ + CH341A_CMD_UIO_STM_END, + }; + return fu_ch341a_device_write(self, buf, sizeof(buf), error); +} + +static gboolean +fu_ch341a_device_setup(FuDevice *device, GError **error) +{ + FuCh341aDevice *self = FU_CH341A_DEVICE(device); + g_autoptr(FuCh341aCfiDevice) cfi_device = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_ch341a_device_parent_class)->setup(device, error)) + return FALSE; + + /* set speed */ + if (!fu_ch341a_device_configure_stream(self, error)) + return FALSE; + + /* setup SPI chip */ + cfi_device = g_object_new(FU_TYPE_CH341A_CFI_DEVICE, + "context", + fu_device_get_context(FU_DEVICE(self)), + "proxy", + FU_DEVICE(self), + "logical-id", + "SPI", + NULL); + if (!fu_device_setup(FU_DEVICE(cfi_device), error)) + return FALSE; + fu_device_add_child(device, FU_DEVICE(cfi_device)); + + /* success */ + return TRUE; +} + +static void +fu_ch341a_device_init(FuCh341aDevice *self) +{ + self->speed = CH341A_STM_I2C_SPEED_STANDARD; + fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x0); + fu_device_set_name(FU_DEVICE(self), "CH341A"); + fu_device_set_vendor(FU_DEVICE(self), "WinChipHead"); +} + +static void +fu_ch341a_device_class_init(FuCh341aDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_ch341a_device_setup; + klass_device->to_string = fu_ch341a_device_to_string; +} diff --git a/fwupd-1.8.6/plugins/ch341a/fu-ch341a-device.h b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ef3d1cb7ddd98319cf6cf8ed1c93520f9fdde831 --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CH341A_DEVICE (fu_ch341a_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCh341aDevice, fu_ch341a_device, FU, CH341A_DEVICE, FuUsbDevice) + +gboolean +fu_ch341a_device_chip_select(FuCh341aDevice *self, gboolean val, GError **error); +gboolean +fu_ch341a_device_spi_transfer(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error); diff --git a/fwupd-1.8.6/plugins/ch341a/fu-ch341a-plugin.c b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..30ceaba2f69cd938ef1293d367db2b5ebe42866c --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ch341a-device.h" +#include "fu-ch341a-plugin.h" + +struct _FuCh341APlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuCh341APlugin, fu_ch341a_plugin, FU_TYPE_PLUGIN) + +static void +fu_ch341a_plugin_init(FuCh341APlugin *self) +{ +} + +static void +fu_ch341a_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "ch341a"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CH341A_DEVICE); +} + +static void +fu_ch341a_plugin_class_init(FuCh341APluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_ch341a_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/ch341a/fu-ch341a-plugin.h b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..5ed9a3936dfec2e6d1e81cfd405a802ccb9609b2 --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/fu-ch341a-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuCh341APlugin, fu_ch341a_plugin, FU, CH341A_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/ch341a/lsusb.txt b/fwupd-1.8.6/plugins/ch341a/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..93cd47de29f1a7f8d4b4c1fcd3666ee919e6c38b --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/lsusb.txt @@ -0,0 +1,68 @@ +Bus 001 Device 124: ID 1a86:5512 QinHeng Electronics CH341 in EPP/MEM/I2C mode, EPP/I2C adapter +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 0 + bDeviceProtocol 2 + bMaxPacketSize0 8 + idVendor 0x1a86 QinHeng Electronics + idProduct 0x5512 CH341 in EPP/MEM/I2C mode, EPP/I2C adapter + bcdDevice 3.04 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0027 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 96mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 1 + bInterfaceProtocol 2 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/ch341a/meson.build b/fwupd-1.8.6/plugins/ch341a/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..8060c44abab6986bf57343855a3370cb96f2cd2f --- /dev/null +++ b/fwupd-1.8.6/plugins/ch341a/meson.build @@ -0,0 +1,16 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginCh341a"'] + +plugin_quirks += files('ch341a.quirk') +plugin_builtins += static_library('fu_plugin_ch341a', + sources: [ + 'fu-ch341a-cfi-device.c', + 'fu-ch341a-device.c', + 'fu-ch341a-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/colorhug/README.md b/fwupd-1.8.6/plugins/colorhug/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5431abca7de9fca335074f04e91c7cb90857eefd --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/README.md @@ -0,0 +1,44 @@ +# ColorHug + +## Introduction + +The ColorHug is an affordable open source display colorimeter built by +Hughski Limited. The USB device allows you to calibrate your screen for +accurate color matching. + +ColorHug versions 1 and 2 support a custom HID-based flashing protocol, but +version 3 (ColorHug+) has now switched to DFU. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* com.hughski.colorhug + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_273F&PID_1001&REV_0001` +* `USB\VID_273F&PID_1001` +* `USB\VID_273F` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different USB PID in a bootloader mode. On attach the device again re-enumerates +back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x273F` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/colorhug/colorhug.quirk b/fwupd-1.8.6/plugins/colorhug/colorhug.quirk new file mode 100644 index 0000000000000000000000000000000000000000..dd91137267b80aa6be91db44db7d9808ed19dfbe --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/colorhug.quirk @@ -0,0 +1,59 @@ +# ColorHug1 +[USB\VID_273F&PID_1000] +Plugin = colorhug +Flags = is-bootloader,self-recovery +Guid = 40338ceb-b966-4eae-adae-9c32edfcc484 +FirmwareSizeMin = 0x2000 +FirmwareSizeMax = 0x8000 +CounterpartGuid = USB\VID_273F&PID_1001 +InstallDuration = 8 + +# ColorHug1: first batch! +[USB\VID_04D8&PID_F8DA] +Guid = USB\VID_273F&PID_1000 + +[USB\VID_273F&PID_1001] +Plugin = colorhug +Flags = self-recovery +Summary = An open source display colorimeter +Icon = colorimeter-colorhug +Guid = 40338ceb-b966-4eae-adae-9c32edfcc484 +CounterpartGuid = USB\VID_273F&PID_1000 +InstallDuration = 8 + +# ColorHug2 +[USB\VID_273F&PID_1004] +Plugin = colorhug +Flags = self-recovery +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 + +[USB\VID_273F&PID_1005] +Plugin = colorhug +Flags = is-bootloader,self-recovery +Guid = 2082b5e0-7a64-478a-b1b2-e3404fab6dad +CounterpartGuid = USB\VID_273F&PID_1004 +InstallDuration = 8 + +# ColorHugALS +[USB\VID_273F&PID_1007] +Plugin = colorhug +Flags = halfsize,self-recovery +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 + +[USB\VID_273F&PID_1006] +Plugin = colorhug +Flags = halfsize,is-bootloader,self-recovery +Guid = 84f40464-9272-4ef7-9399-cd95f12da696 +CounterpartGuid = USB\VID_273F&PID_1007 +InstallDuration = 5 diff --git a/fwupd-1.8.6/plugins/colorhug/fu-colorhug-common.c b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-common.c new file mode 100644 index 0000000000000000000000000000000000000000..0057eab42daf0e7d6b5dd0baac892b364deade51 --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-common.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-colorhug-common.h" + +const gchar * +ch_strerror(ChError error_enum) +{ + if (error_enum == CH_ERROR_NONE) + return "Success"; + if (error_enum == CH_ERROR_UNKNOWN_CMD) + return "Unknown command"; + if (error_enum == CH_ERROR_WRONG_UNLOCK_CODE) + return "Wrong unlock code"; + if (error_enum == CH_ERROR_NOT_IMPLEMENTED) + return "Not implemented"; + if (error_enum == CH_ERROR_UNDERFLOW_SENSOR) + return "Underflow of sensor"; + if (error_enum == CH_ERROR_NO_SERIAL) + return "No serial"; + if (error_enum == CH_ERROR_WATCHDOG) + return "Watchdog"; + if (error_enum == CH_ERROR_INVALID_ADDRESS) + return "Invalid address"; + if (error_enum == CH_ERROR_INVALID_LENGTH) + return "Invalid length"; + if (error_enum == CH_ERROR_INVALID_CHECKSUM) + return "Invalid checksum"; + if (error_enum == CH_ERROR_INVALID_VALUE) + return "Invalid value"; + if (error_enum == CH_ERROR_UNKNOWN_CMD_FOR_BOOTLOADER) + return "Unknown command for bootloader"; + if (error_enum == CH_ERROR_OVERFLOW_MULTIPLY) + return "Overflow of multiply"; + if (error_enum == CH_ERROR_OVERFLOW_ADDITION) + return "Overflow of addition"; + if (error_enum == CH_ERROR_OVERFLOW_SENSOR) + return "Overflow of sensor"; + if (error_enum == CH_ERROR_OVERFLOW_STACK) + return "Overflow of stack"; + if (error_enum == CH_ERROR_NO_CALIBRATION) + return "No calibration"; + if (error_enum == CH_ERROR_DEVICE_DEACTIVATED) + return "Device deactivated"; + if (error_enum == CH_ERROR_INCOMPLETE_REQUEST) + return "Incomplete previous request"; + if (error_enum == CH_ERROR_SELF_TEST_SENSOR) + return "Self test failed: Sensor"; + if (error_enum == CH_ERROR_SELF_TEST_RED) + return "Self test failed: Red"; + if (error_enum == CH_ERROR_SELF_TEST_GREEN) + return "Self test failed: Green"; + if (error_enum == CH_ERROR_SELF_TEST_BLUE) + return "Self test failed: Blue"; + if (error_enum == CH_ERROR_SELF_TEST_MULTIPLIER) + return "Self test failed: Multiplier"; + if (error_enum == CH_ERROR_SELF_TEST_COLOR_SELECT) + return "Self test failed: Color Select"; + if (error_enum == CH_ERROR_SELF_TEST_TEMPERATURE) + return "Self test failed: Temperature"; + if (error_enum == CH_ERROR_INVALID_CALIBRATION) + return "Invalid calibration"; + if (error_enum == CH_ERROR_SRAM_FAILED) + return "SRAM failed"; + if (error_enum == CH_ERROR_OUT_OF_MEMORY) + return "Out of memory"; + if (error_enum == CH_ERROR_SELF_TEST_I2C) + return "Self test failed: I2C"; + if (error_enum == CH_ERROR_SELF_TEST_ADC_VDD) + return "Self test failed: ADC Vdd"; + if (error_enum == CH_ERROR_SELF_TEST_ADC_VSS) + return "Self test failed: ADC Vss"; + if (error_enum == CH_ERROR_SELF_TEST_ADC_VREF) + return "Self test failed: ADC Vref"; + if (error_enum == CH_ERROR_I2C_TARGET_ADDRESS) + return "I2C set target address failed"; + if (error_enum == CH_ERROR_I2C_TARGET_CONFIG) + return "I2C set target config failed"; + if (error_enum == CH_ERROR_SELF_TEST_EEPROM) + return "Self test failed: EEPROM"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/colorhug/fu-colorhug-common.h b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-common.h new file mode 100644 index 0000000000000000000000000000000000000000..358e110cfbfc7582756c9c39ccb01e42091b550e --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-common.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +typedef enum { + CH_ERROR_NONE, + CH_ERROR_UNKNOWN_CMD, + CH_ERROR_WRONG_UNLOCK_CODE, + CH_ERROR_NOT_IMPLEMENTED, + CH_ERROR_UNDERFLOW_SENSOR, + CH_ERROR_NO_SERIAL, + CH_ERROR_WATCHDOG, + CH_ERROR_INVALID_ADDRESS, + CH_ERROR_INVALID_LENGTH, + CH_ERROR_INVALID_CHECKSUM, + CH_ERROR_INVALID_VALUE, + CH_ERROR_UNKNOWN_CMD_FOR_BOOTLOADER, + CH_ERROR_NO_CALIBRATION, + CH_ERROR_OVERFLOW_MULTIPLY, + CH_ERROR_OVERFLOW_ADDITION, + CH_ERROR_OVERFLOW_SENSOR, + CH_ERROR_OVERFLOW_STACK, + CH_ERROR_DEVICE_DEACTIVATED, + CH_ERROR_INCOMPLETE_REQUEST, + CH_ERROR_SELF_TEST_SENSOR, + CH_ERROR_SELF_TEST_RED, + CH_ERROR_SELF_TEST_GREEN, + CH_ERROR_SELF_TEST_BLUE, + CH_ERROR_SELF_TEST_COLOR_SELECT, + CH_ERROR_SELF_TEST_MULTIPLIER, + CH_ERROR_INVALID_CALIBRATION, + CH_ERROR_SRAM_FAILED, + CH_ERROR_OUT_OF_MEMORY, + CH_ERROR_SELF_TEST_TEMPERATURE, + CH_ERROR_SELF_TEST_I2C, + CH_ERROR_SELF_TEST_ADC_VDD, + CH_ERROR_SELF_TEST_ADC_VSS, + CH_ERROR_SELF_TEST_ADC_VREF, + CH_ERROR_I2C_TARGET_ADDRESS, + CH_ERROR_I2C_TARGET_CONFIG, + CH_ERROR_SELF_TEST_EEPROM, + CH_ERROR_LAST +} ChError; + +const gchar * +ch_strerror(ChError error_enum); diff --git a/fwupd-1.8.6/plugins/colorhug/fu-colorhug-device.c b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-device.c new file mode 100644 index 0000000000000000000000000000000000000000..4908258f42862aeede488ae9e78587b99cdb0d6a --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-device.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#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 (1 << 0) + +struct _FuColorhugDevice { + FuUsbDevice parent_instance; + guint16 start_addr; +}; + +G_DEFINE_TYPE(FuColorhugDevice, fu_colorhug_device, FU_TYPE_USB_DEVICE) + +#define CH_CMD_GET_FIRMWARE_VERSION 0x07 +#define CH_CMD_RESET 0x24 +#define CH_CMD_READ_FLASH 0x25 +#define CH_CMD_WRITE_FLASH 0x26 +#define CH_CMD_BOOT_FLASH 0x27 +#define CH_CMD_SET_FLASH_SUCCESS 0x28 +#define CH_CMD_ERASE_FLASH 0x29 + +#define CH_USB_HID_EP 0x0001 +#define CH_USB_HID_EP_IN (CH_USB_HID_EP | 0x80) +#define CH_USB_HID_EP_OUT (CH_USB_HID_EP | 0x00) +#define CH_USB_HID_EP_SIZE 64 +#define CH_USB_CONFIG 0x0001 +#define CH_USB_INTERFACE 0x0000 +#define CH_EEPROM_ADDR_RUNCODE 0x4000 +#define CH_EEPROM_ADDR_RUNCODE_ALS 0x2000 + +#define CH_DEVICE_USB_TIMEOUT 5000 /* ms */ +#define CH_FLASH_TRANSFER_BLOCK_SIZE 0x020 /* 32 */ + +static gboolean +fu_colorhug_device_msg(FuColorhugDevice *self, + guint8 cmd, + guint8 *ibuf, + gsize ibufsz, + guint8 *obuf, + gsize obufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 buf[] = {[0] = cmd, [1 ... CH_USB_HID_EP_SIZE - 1] = 0x00}; + gsize actual_length = 0; + g_autoptr(GError) error_local = NULL; + + /* check size */ + if (ibufsz > sizeof(buf) - 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot process chunk of size %" G_GSIZE_FORMAT, + ibufsz); + return FALSE; + } + if (obufsz > sizeof(buf) - 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot process chunk of size %" G_GSIZE_FORMAT, + ibufsz); + return FALSE; + } + + /* optionally copy in data */ + if (ibuf != NULL) { + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x1, /* dst */ + ibuf, + ibufsz, + 0x0, /* src */ + ibufsz, + error)) + return FALSE; + } + + /* request */ + if (g_getenv("FWUPD_COLORHUG_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "REQ", buf, ibufsz + 1); + if (!g_usb_device_interrupt_transfer(usb_device, + CH_USB_HID_EP_OUT, + buf, + sizeof(buf), + &actual_length, + CH_DEVICE_USB_TIMEOUT, + NULL, /* cancellable */ + &error_local)) { + if (cmd == CH_CMD_RESET && g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE)) { + g_debug("ignoring '%s' on reset", error_local->message); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to send request: "); + return FALSE; + } + if (actual_length != CH_USB_HID_EP_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "request not all sent, got %" G_GSIZE_FORMAT, + actual_length); + return FALSE; + } + + /* read reply */ + if (!g_usb_device_interrupt_transfer(usb_device, + CH_USB_HID_EP_IN, + buf, + sizeof(buf), + &actual_length, + CH_DEVICE_USB_TIMEOUT, + NULL, /* cancellable */ + &error_local)) { + if (cmd == CH_CMD_RESET && g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE)) { + g_debug("ignoring '%s' on reset", error_local->message); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to get reply: "); + return FALSE; + } + if (g_getenv("FWUPD_COLORHUG_VERBOSE") != NULL) + fu_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 && actual_length != 2 && + actual_length != obufsz + 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "request not all received, got %" G_GSIZE_FORMAT, + actual_length); + return FALSE; + } + + /* check error code */ + if (buf[0] != CH_ERROR_NONE) { + const gchar *msg = ch_strerror(buf[0]); + if (msg == NULL) + msg = "unknown error"; + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, msg); + return FALSE; + } + + /* check cmd matches */ + if (buf[1] != cmd) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cmd incorrect, expected %u, got %u", + cmd, + buf[1]); + return FALSE; + } + + /* copy back optional buf */ + if (obuf != NULL) { + if (!fu_memcpy_safe(obuf, + obufsz, + 0x0, /* dst */ + buf, + sizeof(buf), + 0x2, /* src */ + obufsz, + error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_colorhug_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + if (!fu_colorhug_device_msg(self, + CH_CMD_RESET, + NULL, + 0, /* in */ + NULL, + 0, /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to reset device: %s", + error_local->message); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_colorhug_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + if (!fu_colorhug_device_msg(self, + CH_CMD_BOOT_FLASH, + NULL, + 0, /* in */ + NULL, + 0, /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to boot to runtime: %s", + error_local->message); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_colorhug_device_set_flash_success(FuColorhugDevice *self, gboolean val, GError **error) +{ + guint8 buf[] = {[0] = val ? 0x01 : 0x00}; + g_autoptr(GError) error_local = NULL; + + g_debug("setting flash success"); + if (!fu_colorhug_device_msg(self, + CH_CMD_SET_FLASH_SUCCESS, + buf, + sizeof(buf), /* in */ + NULL, + 0, /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to set flash success: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_colorhug_device_reload(FuDevice *device, GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE(device); + return fu_colorhug_device_set_flash_success(self, TRUE, error); +} + +static gboolean +fu_colorhug_device_erase(FuColorhugDevice *self, guint16 addr, gsize sz, GError **error) +{ + guint8 buf[4]; + g_autoptr(GError) error_local = NULL; + + fu_memwrite_uint16(buf + 0, addr, G_LITTLE_ENDIAN); + fu_memwrite_uint16(buf + 2, sz, G_LITTLE_ENDIAN); + if (!fu_colorhug_device_msg(self, + CH_CMD_ERASE_FLASH, + buf, + sizeof(buf), /* in */ + NULL, + 0, /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to erase device: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +static gchar * +fu_colorhug_device_get_version(FuColorhugDevice *self, GError **error) +{ + guint8 buf[6]; + if (!fu_colorhug_device_msg(self, + CH_CMD_GET_FIRMWARE_VERSION, + NULL, + 0, /* in */ + buf, + sizeof(buf), /* out */ + error)) { + return NULL; + } + return g_strdup_printf("%i.%i.%i", + fu_memread_uint16(buf + 0, G_LITTLE_ENDIAN), + fu_memread_uint16(buf + 2, G_LITTLE_ENDIAN), + fu_memread_uint16(buf + 4, G_LITTLE_ENDIAN)); +} + +static gboolean +fu_colorhug_device_probe(FuDevice *device, GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE(device); + + /* compact memory layout */ + if (fu_device_has_private_flag(device, FU_COLORHUG_DEVICE_FLAG_HALFSIZE)) + self->start_addr = CH_EEPROM_ADDR_RUNCODE_ALS; + + /* add hardcoded bits */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + + /* success */ + return TRUE; +} + +static gboolean +fu_colorhug_device_setup(FuDevice *device, GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + guint idx; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_colorhug_device_parent_class)->setup(device, error)) + return FALSE; + + /* get version number, falling back to the USB device release */ + idx = g_usb_device_get_custom_index(usb_device, + G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, + 'F', + 'W', + NULL); + if (idx != 0x00) { + g_autofree gchar *tmp = NULL; + tmp = g_usb_device_get_string_descriptor(usb_device, idx, NULL); + /* 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 */ + if (tmp != NULL) { + fu_device_set_version_format(device, fu_version_guess_format(tmp)); + fu_device_set_version(device, tmp); + } + } + + /* get GUID from the descriptor if set */ + idx = g_usb_device_get_custom_index(usb_device, + G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, + 'G', + 'U', + NULL); + if (idx != 0x00) { + g_autofree gchar *tmp = NULL; + tmp = g_usb_device_get_string_descriptor(usb_device, idx, NULL); + fu_device_add_guid(device, tmp); + } + + /* using the USB descriptor and old firmware */ + if (fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_BCD) { + 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(device, version); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + } else { + g_warning("failed to get firmware version: %s", error_local->message); + } + } + + /* success */ + return TRUE; +} + +static guint8 +ch_colorhug_device_calculate_checksum(const guint8 *data, guint32 len) +{ + guint8 checksum = 0xff; + for (guint32 i = 0; i < len; i++) + checksum ^= data[i]; + return checksum; +} + +static gboolean +fu_colorhug_device_write_blocks(FuColorhugDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 buf[CH_FLASH_TRANSFER_BLOCK_SIZE + 4]; + g_autoptr(GError) error_local = NULL; + + /* set address, length, checksum, data */ + fu_memwrite_uint16(buf + 0, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + buf[2] = fu_chunk_get_data_sz(chk); + buf[3] = ch_colorhug_device_calculate_checksum(fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk)); + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x4, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + if (!fu_colorhug_device_msg(self, + CH_CMD_WRITE_FLASH, + buf, + sizeof(buf), /* in */ + NULL, + 0, /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_local->message); + return FALSE; + } + + /* update progress */ + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_colorhug_device_verify_blocks(FuColorhugDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 buf[3]; + guint8 buf_out[CH_FLASH_TRANSFER_BLOCK_SIZE + 1]; + g_autoptr(GError) error_local = NULL; + + /* set address */ + fu_memwrite_uint16(buf + 0, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + buf[2] = fu_chunk_get_data_sz(chk); + if (!fu_colorhug_device_msg(self, + CH_CMD_READ_FLASH, + buf, + sizeof(buf), /* in */ + buf_out, + sizeof(buf_out), /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read: %s", + error_local->message); + return FALSE; + } + + /* verify */ + if (memcmp(buf_out + 1, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)) != 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)fu_chunk_get_address(chk), + fu_chunk_get_data_sz(chk)); + return FALSE; + } + + /* update progress */ + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_colorhug_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuColorhugDevice *self = FU_COLORHUG_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 19, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 44, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 35, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* don't auto-boot firmware */ + if (!fu_colorhug_device_set_flash_success(self, FALSE, error)) + return FALSE; + fu_progress_step_done(progress); + + /* erase flash */ + if (!fu_colorhug_device_erase(self, self->start_addr, g_bytes_get_size(fw), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write each block */ + chunks = fu_chunk_array_new_from_bytes(fw, + self->start_addr, + 0x00, /* page_sz */ + CH_FLASH_TRANSFER_BLOCK_SIZE); + if (!fu_colorhug_device_write_blocks(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify each block */ + if (!fu_colorhug_device_verify_blocks(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_colorhug_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 57, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 43, "reload"); +} + +static void +fu_colorhug_device_init(FuColorhugDevice *self) +{ + /* this is the application code */ + self->start_addr = CH_EEPROM_ADDR_RUNCODE; + fu_device_add_protocol(FU_DEVICE(self), "com.hughski.colorhug"); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG); + fu_device_register_private_flag(FU_DEVICE(self), + FU_COLORHUG_DEVICE_FLAG_HALFSIZE, + "halfsize"); + fu_usb_device_set_configuration(FU_USB_DEVICE(self), CH_USB_CONFIG); + fu_usb_device_add_interface(FU_USB_DEVICE(self), CH_USB_INTERFACE); +} + +static void +fu_colorhug_device_class_init(FuColorhugDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + 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->reload = fu_colorhug_device_reload; + klass_device->setup = fu_colorhug_device_setup; + klass_device->probe = fu_colorhug_device_probe; + klass_device->set_progress = fu_colorhug_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/colorhug/fu-colorhug-device.h b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-device.h new file mode 100644 index 0000000000000000000000000000000000000000..113d3fbd81da1c88d5f89f949cb31aa1d5f40ad4 --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_COLORHUG_DEVICE (fu_colorhug_device_get_type()) +G_DECLARE_FINAL_TYPE(FuColorhugDevice, fu_colorhug_device, FU, COLORHUG_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/colorhug/fu-colorhug-plugin.c b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..84c88b76d84a935a4873385be4be7f8567067c10 --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-colorhug-device.h" +#include "fu-colorhug-plugin.h" + +struct _FuColorhugPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuColorhugPlugin, fu_colorhug_plugin, FU_TYPE_PLUGIN) + +static void +fu_colorhug_plugin_init(FuColorhugPlugin *self) +{ +} + +static void +fu_colorhug_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_COLORHUG_DEVICE); +} + +static void +fu_colorhug_plugin_class_init(FuColorhugPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_colorhug_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/colorhug/fu-colorhug-plugin.h b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6b65087abaa06af67e700c9be7bd1ff738ec334c --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/fu-colorhug-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuColorhugPlugin, fu_colorhug_plugin, FU, COLORHUG_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/colorhug/meson.build b/fwupd-1.8.6/plugins/colorhug/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..df6111d653e53e65ec7cb4e2b35ae09e4c14b87e --- /dev/null +++ b/fwupd-1.8.6/plugins/colorhug/meson.build @@ -0,0 +1,16 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginColorHug"'] + +plugin_quirks += files('colorhug.quirk') +plugin_builtins += static_library('fu_plugin_colorhug', + sources: [ + 'fu-colorhug-common.c', + 'fu-colorhug-device.c', + 'fu-colorhug-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/corsair/README.md b/fwupd-1.8.6/plugins/corsair/README.md new file mode 100644 index 0000000000000000000000000000000000000000..933b209cf145892ef41d283beae96105da6c909e --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/README.md @@ -0,0 +1,60 @@ +# Corsair + +## Introduction + +This plugin allows to update firmware on Corsair mice and receivers: + +* SABRE RGB PRO WIRELESS +* SLIPSTREAM WIRELESS USB Receiver +* KATAR PRO WIRELESS +* KATAR PRO XT Gaming Mouse +* SABRE PRO Gaming Mouse + +## Code structure + +All devices handled by one object (FuCorsairDevice). Receivers with wireless-only +devices will be shown as two entities: parent device as a receiver and wireless +device as a child. Difference in behavior is handled by private flags. + +FuCorsairBp contains low-level protocol related routines. Device objects should +call correct versions of these routines in order to update firmware. Correct +routines chosen by device quirsks and private flags. + +## Wired mice update behavior + +Mice and/or it's wireless adapter must be connected to host via USB cable +to apply an update. The device is switched to bootloader mode to flash +updates, and is reset automatically to new firmware after flashing. + +## Wireless mice update behavior + +The receiver should be connected to host and the mouse should be turned on +and not sleeping. + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### CorsairVendorInterfaceId + +Some devices have non-standard USB interface for protocol communication. +This quirk should be set if protocol interface is not 1. + +Since: 1.8.0 + +### CorsairSubdeviceId + +Specifies ID of any wireless child device which can be updated. Polling will +be turned on if a subdevice is not connected when parent is being probed. + +### Flags:legacy-attach + +This flag is used if legacy attach command should be used + +### Flags:no-version-in-bl + +This flag handles cases if device reports incorrect firmware version in bootloader mode. + +### Flags:is-subdevice + +This flag tells device that it is a child device. All subdevice behavior tweaks will be applied. diff --git a/fwupd-1.8.6/plugins/corsair/corsair.quirk b/fwupd-1.8.6/plugins/corsair/corsair.quirk new file mode 100644 index 0000000000000000000000000000000000000000..33f84e0768e5dbd6226b025735eef17d7818a402 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/corsair.quirk @@ -0,0 +1,20 @@ +[USB\VID_1B1C&PID_1BAC] +Plugin = corsair +GType = FuCorsairDevice +Name = KATAR PRO XT Gaming Mouse +CorsairDeviceKind = mouse +Flags = legacy-attach,no-version-in-bl + +[USB\VID_1B1C&PID_1B7A] +Plugin = corsair +GType = FuCorsairDevice +Name = SABRE PRO Gaming Mouse +CorsairDeviceKind = mouse +Flags = legacy-attach,no-version-in-bl + +[USB\VID_1B1C&PID_1B79] +Plugin = corsair +GType = FuCorsairDevice +Name = SABRE RGB PRO Gaming Mouse +CorsairDeviceKind = mouse +Flags = legacy-attach,no-version-in-bl diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-bp.c b/fwupd-1.8.6/plugins/corsair/fu-corsair-bp.c new file mode 100644 index 0000000000000000000000000000000000000000..cab7b5e9a6bfbe7a7350ccb5d2cf149dd46408f5 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-bp.c @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2022 Andrii Dushko + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-corsair-bp.h" +#include "fu-corsair-common.h" + +#define CORSAIR_DEFAULT_VENDOR_INTERFACE_ID 1 +#define CORSAIR_ACTIVATION_TIMEOUT 30000 +#define CORSAIR_MODE_BOOTLOADER 3 +#define CORSAIR_FIRST_CHUNK_HEADER_SIZE 7 +#define CORSAIR_NEXT_CHUNKS_HEADER_SIZE 3 +#define CORSAIR_TRANSACTION_TIMEOUT 10000 +#define CORSAIR_DEFAULT_CMD_SIZE 64 + +#define CORSAIR_OFFSET_CMD_PROPERTY_ID 0x02 +#define CORSAIR_OFFSET_CMD_PROPERTY_VALUE 0x03 +#define CORSAIR_OFFSET_CMD_VERSION 0x03 +#define CORSAIR_OFFSET_CMD_CRC 0x08 +#define CORSAIR_OFFSET_CMD_MODE 0x03 +#define CORSAIR_OFFSET_CMD_STATUS 0x02 +#define CORSAIR_OFFSET_CMD_FIRMWARE_SIZE 0x03 +#define CORSAIR_OFFSET_CMD_SET_MODE 0x04 +#define CORSAIR_OFFSET_CMD_DESTINATION 0x00 + +#define CORSAIR_INPUT_FLUSH_TIMEOUT 10 +#define CORSAIR_INPUT_FLUSH_ITERATIONS 3 + +typedef enum { + FU_CORSAIR_BP_DESTINATION_SELF = 0x08, + FU_CORSAIR_BP_DESTINATION_SUBDEVICE = 0x09 +} FuCorsairBpDestination; + +struct _FuCorsairBp { + FuUsbDevice parent_instance; + guint8 destination; + guint8 epin; + guint8 epout; + guint16 cmd_write_size; + guint16 cmd_read_size; + gboolean is_legacy_attach; +}; +G_DEFINE_TYPE(FuCorsairBp, fu_corsair_bp, FU_TYPE_USB_DEVICE) + +static gboolean +fu_corsair_bp_command(FuCorsairBp *self, + guint8 *data, + guint timeout, + gboolean need_reply, + GError **error) +{ + gsize actual_len = 0; + gboolean ret; + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + + data[CORSAIR_OFFSET_CMD_DESTINATION] = self->destination; + + fu_dump_raw("FuPluginCorsair", "command", data, self->cmd_write_size); + + ret = g_usb_device_interrupt_transfer(usb_device, + self->epout, + data, + self->cmd_write_size, + &actual_len, + timeout, + NULL, + error); + if (!ret) { + g_prefix_error(error, "failed to write command: "); + return FALSE; + } + if (actual_len != self->cmd_write_size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wrong size written: %" G_GSIZE_FORMAT, + actual_len); + return FALSE; + } + + if (!need_reply) + return TRUE; + + memset(data, 0, FU_CORSAIR_MAX_CMD_SIZE); + + ret = g_usb_device_interrupt_transfer(usb_device, + self->epin, + data, + self->cmd_read_size, + &actual_len, + timeout, + NULL, + error); + if (!ret) { + g_prefix_error(error, "failed to get command response: "); + return FALSE; + } + if (actual_len != self->cmd_read_size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wrong size read: %" G_GSIZE_FORMAT, + actual_len); + return FALSE; + } + + fu_dump_raw("FuPluginCorsair", "response", data, self->cmd_write_size); + + if (data[CORSAIR_OFFSET_CMD_STATUS] != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "device replied with error: 0x%02x", + data[CORSAIR_OFFSET_CMD_STATUS]); + return FALSE; + } + + return TRUE; +} + +/** + * @brief Flush all input reports if there are any. + * @self: a #FuCorsairBp + * + * This function clears any dangling IN reports that + * the device may have sent after the enumeration. + */ +void +fu_corsair_bp_flush_input_reports(FuCorsairBp *self) +{ + gsize actual_len; + g_autofree guint8 *buf = g_malloc0(self->cmd_read_size); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + + for (guint i = 0; i < CORSAIR_INPUT_FLUSH_ITERATIONS; i++) { + g_autoptr(GError) error_local = NULL; + if (!g_usb_device_interrupt_transfer(usb_device, + self->epin, + buf, + self->cmd_read_size, + &actual_len, + CORSAIR_INPUT_FLUSH_TIMEOUT, + NULL, + &error_local)) + g_debug("flushing status: %s", error_local->message); + } +} + +static gboolean +fu_corsair_bp_write_first_chunk(FuCorsairBp *self, + FuChunk *chunk, + guint32 firmware_size, + GError **error) +{ + guint8 init_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x0d, 0x00, 0x03}; + guint8 write_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x06, 0x00}; + if (!fu_corsair_bp_command(self, init_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { + g_prefix_error(error, "firmware init fail: "); + return FALSE; + } + + if (!fu_memwrite_uint32_safe(write_cmd, + sizeof(write_cmd), + CORSAIR_OFFSET_CMD_FIRMWARE_SIZE, + firmware_size, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "cannot serialize firmware size: "); + return FALSE; + } + if (!fu_memcpy_safe(write_cmd, + sizeof(write_cmd), + CORSAIR_FIRST_CHUNK_HEADER_SIZE, + fu_chunk_get_data(chunk), + fu_chunk_get_data_sz(chunk), + 0, + fu_chunk_get_data_sz(chunk), + error)) { + g_prefix_error(error, "cannot set data: "); + return FALSE; + } + if (!fu_corsair_bp_command(self, write_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { + g_prefix_error(error, "write command fail: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_corsair_bp_write_chunk(FuCorsairBp *self, FuChunk *chunk, GError **error) +{ + guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x07}; + if (!fu_memcpy_safe(cmd, + sizeof(cmd), + CORSAIR_NEXT_CHUNKS_HEADER_SIZE, + fu_chunk_get_data(chunk), + fu_chunk_get_data_sz(chunk), + 0, + fu_chunk_get_data_sz(chunk), + error)) { + g_prefix_error(error, "cannot set data: "); + return FALSE; + } + if (!fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { + g_prefix_error(error, "write command fail: "); + return FALSE; + } + return TRUE; +} + +static void +fu_corsair_bp_incorporate(FuDevice *self, FuDevice *donor) +{ + FuCorsairBp *bp_self = FU_CORSAIR_BP(self); + FuCorsairBp *bp_donor = FU_CORSAIR_BP(donor); + + bp_self->epin = bp_donor->epin; + bp_self->epout = bp_donor->epout; + bp_self->cmd_write_size = bp_donor->cmd_write_size; + bp_self->cmd_read_size = bp_donor->cmd_read_size; +} + +static void +fu_corsair_bp_init(FuCorsairBp *self) +{ + self->cmd_read_size = CORSAIR_DEFAULT_CMD_SIZE; + self->cmd_write_size = CORSAIR_DEFAULT_CMD_SIZE; + self->destination = FU_CORSAIR_BP_DESTINATION_SELF; +} + +gboolean +fu_corsair_bp_get_property(FuCorsairBp *self, + FuCorsairBpProperty property, + guint32 *value, + GError **error) +{ + guint8 data[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x02}; + + fu_memwrite_uint16(&data[CORSAIR_OFFSET_CMD_PROPERTY_ID], + (guint16)property, + G_LITTLE_ENDIAN); + + if (!fu_corsair_bp_command(self, data, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) + return FALSE; + + *value = fu_memread_uint32(&data[CORSAIR_OFFSET_CMD_PROPERTY_VALUE], G_LITTLE_ENDIAN); + + return TRUE; +} + +static gboolean +fu_corsair_bp_set_mode(FuCorsairBp *self, FuCorsairDeviceMode mode, GError **error) +{ + guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x01, 0x03}; + + cmd[CORSAIR_OFFSET_CMD_SET_MODE] = mode; + + if (!fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { + g_prefix_error(error, "set mode command fail: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_corsair_bp_write_firmware_chunks(FuCorsairBp *self, + FuChunk *first_chunk, + GPtrArray *chunks, + FuProgress *progress, + guint32 firmware_size, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len + 1); + + if (!fu_corsair_bp_write_first_chunk(self, first_chunk, firmware_size, error)) { + g_prefix_error(error, "cannot write first chunk: "); + return FALSE; + } + fu_progress_step_done(progress); + + for (guint id = 0; id < chunks->len; id++) { + FuChunk *chunk = g_ptr_array_index(chunks, id); + if (!fu_corsair_bp_write_chunk(self, chunk, error)) { + g_prefix_error(error, "cannot write chunk %u", id); + return FALSE; + } + fu_progress_step_done(progress); + } + + return TRUE; +} + +static gboolean +fu_corsair_bp_commit_firmware(FuCorsairBp *self, GError **error) +{ + guint8 commit_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x05, 0x01, 0x00}; + if (!fu_corsair_bp_command(self, commit_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { + g_prefix_error(error, "firmware commit fail: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_corsair_bp_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + const guint8 *firmware_raw; + gsize firmware_size; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(FuChunk) firstChunk = NULL; + g_autoptr(GBytes) rest_of_firmware = NULL; + FuCorsairBp *self = FU_CORSAIR_BP(device); + guint32 first_chunk_size = self->cmd_write_size - CORSAIR_FIRST_CHUNK_HEADER_SIZE; + + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) { + g_prefix_error(error, "cannot get firmware data"); + return FALSE; + } + firmware_raw = fu_bytes_get_data_safe(blob, &firmware_size, error); + if (firmware_raw == NULL) { + g_prefix_error(error, "cannot get firmware data: "); + return FALSE; + } + + /* the firmware size should be greater than 1 chunk */ + if (firmware_size <= first_chunk_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "update file should be bigger"); + return FALSE; + } + + firstChunk = fu_chunk_new(0, 0, 0, g_bytes_get_data(blob, NULL), first_chunk_size); + rest_of_firmware = + fu_bytes_new_offset(blob, first_chunk_size, firmware_size - first_chunk_size, error); + if (rest_of_firmware == NULL) { + g_prefix_error(error, "cannot get firmware past first chunk: "); + return FALSE; + } + chunks = + fu_chunk_array_new_from_bytes(rest_of_firmware, + first_chunk_size, + 0, + self->cmd_write_size - CORSAIR_NEXT_CHUNKS_HEADER_SIZE); + + if (!fu_corsair_bp_write_firmware_chunks(self, + firstChunk, + chunks, + progress, + g_bytes_get_size(blob), + error)) + return FALSE; + + if (!fu_corsair_bp_commit_firmware(self, error)) + return FALSE; + + return TRUE; +} + +gboolean +fu_corsair_bp_activate_firmware(FuCorsairBp *self, FuFirmware *firmware, GError **error) +{ + guint32 crc; + gsize firmware_size; + const guint8 *firmware_raw; + g_autoptr(GBytes) blob = NULL; + guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x16, 0x00, 0x01, 0x03, 0x00, 0x01, 0x01}; + + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) { + g_prefix_error(error, "cannot get firmware bytes"); + return FALSE; + } + + firmware_raw = fu_bytes_get_data_safe(blob, &firmware_size, error); + if (firmware_raw == NULL) { + g_prefix_error(error, "cannot get firmware data: "); + return FALSE; + } + + crc = fu_corsair_calculate_crc(firmware_raw, firmware_size); + fu_memwrite_uint32(&cmd[CORSAIR_OFFSET_CMD_CRC], crc, G_LITTLE_ENDIAN); + + return fu_corsair_bp_command(self, cmd, CORSAIR_ACTIVATION_TIMEOUT, TRUE, error); +} + +static gboolean +fu_corsair_bp_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCorsairBp *self = FU_CORSAIR_BP(device); + if (self->is_legacy_attach) { + guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x10, 0x01, 0x00, 0x03, 0x00, 0x01}; + return fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, FALSE, error); + } else { + return fu_corsair_bp_set_mode(self, FU_CORSAIR_DEVICE_MODE_APPLICATION, error); + } +} + +static gboolean +fu_corsair_bp_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCorsairBp *self = FU_CORSAIR_BP(device); + return fu_corsair_bp_set_mode(self, FU_CORSAIR_DEVICE_MODE_BOOTLOADER, error); +} + +static void +fu_corsair_bp_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCorsairBp *self = FU_CORSAIR_BP(device); + + FU_DEVICE_CLASS(fu_corsair_bp_parent_class)->to_string(device, idt, str); + + fu_string_append_kx(str, idt, "InEndpoint", self->epin); + fu_string_append_kx(str, idt, "OutEndpoint", self->epout); +} + +static void +fu_corsair_bp_class_init(FuCorsairBpClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->incorporate = fu_corsair_bp_incorporate; + klass_device->write_firmware = fu_corsair_bp_write_firmware; + klass_device->attach = fu_corsair_bp_attach; + klass_device->detach = fu_corsair_bp_detach; + klass_device->to_string = fu_corsair_bp_to_string; +} + +FuCorsairBp * +fu_corsair_bp_new(GUsbDevice *usb_device, gboolean is_subdevice) +{ + FuCorsairBp *self = g_object_new(FU_TYPE_CORSAIR_BP, "usb_device", usb_device, NULL); + + if (is_subdevice) { + self->destination = FU_CORSAIR_BP_DESTINATION_SUBDEVICE; + } else { + self->destination = FU_CORSAIR_BP_DESTINATION_SELF; + } + + return self; +} + +void +fu_corsair_bp_set_cmd_size(FuCorsairBp *self, guint16 write_size, guint16 read_size) +{ + self->cmd_write_size = write_size; + self->cmd_read_size = read_size; +} + +void +fu_corsair_bp_set_endpoints(FuCorsairBp *self, guint8 epin, guint8 epout) +{ + self->epin = epin; + self->epout = epout; +} + +void +fu_corsair_bp_set_legacy_attach(FuCorsairBp *self, gboolean is_legacy_attach) +{ + self->is_legacy_attach = is_legacy_attach; +} diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-bp.h b/fwupd-1.8.6/plugins/corsair/fu-corsair-bp.h new file mode 100644 index 0000000000000000000000000000000000000000..c35f261caa07ecddf3bb9544fa7e1b886cc99348 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-bp.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Andrii Dushko + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-corsair-common.h" + +#define FU_TYPE_CORSAIR_BP (fu_corsair_bp_get_type()) +G_DECLARE_FINAL_TYPE(FuCorsairBp, fu_corsair_bp, FU, CORSAIR_BP, FuUsbDevice) + +struct _FuCorsairBpClass { + FuUsbDeviceClass parent_class; +}; + +void +fu_corsair_bp_flush_input_reports(FuCorsairBp *self); + +gboolean +fu_corsair_bp_get_property(FuCorsairBp *self, + FuCorsairBpProperty property, + guint32 *value, + GError **error); + +gboolean +fu_corsair_bp_activate_firmware(FuCorsairBp *self, FuFirmware *firmware, GError **error); + +void +fu_corsair_bp_set_cmd_size(FuCorsairBp *self, guint16 write_size, guint16 read_size); +void +fu_corsair_bp_set_endpoints(FuCorsairBp *self, guint8 epin, guint8 epout); +void +fu_corsair_bp_set_legacy_attach(FuCorsairBp *self, gboolean is_legacy_attach); + +FuCorsairBp * +fu_corsair_bp_new(GUsbDevice *usb_device, gboolean is_subdevice); diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-common.c b/fwupd-1.8.6/plugins/corsair/fu-corsair-common.c new file mode 100644 index 0000000000000000000000000000000000000000..fa4f7683d5c209ddcf3071c4463a47778cc507d0 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-common.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 Andrii Dushko + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-corsair-common.h" + +FuCorsairDeviceKind +fu_corsair_device_type_from_string(const gchar *kind) +{ + if (g_strcmp0(kind, "mouse") == 0) + return FU_CORSAIR_DEVICE_MOUSE; + if (g_strcmp0(kind, "receiver") == 0) + return FU_CORSAIR_DEVICE_RECEIVER; + return FU_CORSAIR_DEVICE_UNKNOWN; +} + +const gchar * +fu_corsair_device_type_to_string(FuCorsairDeviceKind type) +{ + if (type == FU_CORSAIR_DEVICE_MOUSE) + return "mouse"; + if (type == FU_CORSAIR_DEVICE_RECEIVER) + return "receiver"; + + return "unknown"; +} + +guint32 +fu_corsair_calculate_crc(const guint8 *data, guint32 data_len) +{ + gboolean bit; + guint8 c; + guint32 crc = 0xffffffff; + + while (data_len--) { + c = *data++; + for (guint i = 0x80; i > 0; i >>= 1) { + bit = crc & 0x80000000; + if (c & i) { + bit = !bit; + } + crc <<= 1; + if (bit) { + crc ^= 0x04c11db7; + } + } + } + return crc; +} + +/** + * fu_corsair_version_from_uint32: + * @val: version in corsair device format + * + * fu_version_from_uint32(... %FWUPD_VERSION_FORMAT_TRIPLET) + * cannot be used because bytes in the version are in non-standard + * order: 0xCCDD.BB.AA. + * + * Returns: a version number, e.g. `1.0.3`. + **/ +gchar * +fu_corsair_version_from_uint32(guint32 value) +{ + return g_strdup_printf("%u.%u.%u", + value & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xffff); +} diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-common.h b/fwupd-1.8.6/plugins/corsair/fu-corsair-common.h new file mode 100644 index 0000000000000000000000000000000000000000..167c4d3f17a702a6e2bdef641173a951fd365f4f --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-common.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Andrii Dushko + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH (1 << 0) +#define FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE (1 << 1) +#define FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER (1 << 2) + +#define FU_CORSAIR_MAX_CMD_SIZE 1024 + +typedef enum { + FU_CORSAIR_DEVICE_UNKNOWN = 0, + FU_CORSAIR_DEVICE_MOUSE, + FU_CORSAIR_DEVICE_RECEIVER +} FuCorsairDeviceKind; + +typedef enum { + FU_CORSAIR_BP_PROPERTY_MODE = 0x03, + FU_CORSAIR_BP_PROPERTY_BATTERY_LEVEL = 0x0F, + FU_CORSAIR_BP_PROPERTY_VERSION = 0x13, + FU_CORSAIR_BP_PROPERTY_BOOTLOADER_VERSION = 0x14, + FU_CORSAIR_BP_PROPERTY_SUBDEVICES = 0x36, +} FuCorsairBpProperty; + +typedef enum { + FU_CORSAIR_DEVICE_MODE_APPLICATION = 0x01, + FU_CORSAIR_DEVICE_MODE_BOOTLOADER = 0x03 +} FuCorsairDeviceMode; + +FuCorsairDeviceKind +fu_corsair_device_type_from_string(const gchar *kind); + +const gchar * +fu_corsair_device_type_to_string(FuCorsairDeviceKind type); + +guint32 +fu_corsair_calculate_crc(const guint8 *data, guint32 data_len); + +gchar * +fu_corsair_version_from_uint32(guint32 val); diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-device.c b/fwupd-1.8.6/plugins/corsair/fu-corsair-device.c new file mode 100644 index 0000000000000000000000000000000000000000..2d4927fa0cebc4ebda179751f3042deada19e4b6 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-device.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2022 Andrii Dushko + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-corsair-bp.h" +#include "fu-corsair-common.h" +#include "fu-corsair-device.h" + +#define CORSAIR_DEFAULT_VENDOR_INTERFACE_ID 1 + +#define CORSAIR_TRANSACTION_TIMEOUT 4000 +#define CORSAIR_SUBDEVICE_POLL_PERIOD 30000 +#define CORSAIR_SUBDEVICE_REBOOT_DELAY (4 * G_USEC_PER_SEC) +#define CORSAIR_SUBDEVICE_RECONNECT_RETRIES 30 +#define CORSAIR_SUBDEVICE_RECONNECT_PERIOD 1000 +#define CORSAIR_SUBDEVICE_FIRST_POLL_DELAY (2 * G_USEC_PER_SEC) + +struct _FuCorsairDevice { + FuUsbDevice parent_instance; + FuCorsairDeviceKind device_kind; + guint8 vendor_interface; + gchar *subdevice_id; + FuCorsairBp *bp; +}; +G_DEFINE_TYPE(FuCorsairDevice, fu_corsair_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_corsair_device_probe(FuDevice *device, GError **error) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + GUsbInterface *iface = NULL; + GUsbEndpoint *ep1 = NULL; + GUsbEndpoint *ep2 = NULL; + g_autoptr(GPtrArray) ifaces = NULL; + g_autoptr(GPtrArray) endpoints = NULL; + guint16 cmd_write_size; + guint16 cmd_read_size; + guint8 epin; + guint8 epout; + + /* probing are skipped for subdevices */ + if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) + return TRUE; + + if (!FU_DEVICE_CLASS(fu_corsair_device_parent_class)->probe(device, error)) + return FALSE; + + ifaces = g_usb_device_get_interfaces(usb_device, error); + if (ifaces == NULL || (ifaces->len < (self->vendor_interface + 1u))) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "update interface not found"); + return FALSE; + } + + iface = g_ptr_array_index(ifaces, self->vendor_interface); + endpoints = g_usb_interface_get_endpoints(iface); + /* expecting to have two endpoints for communication */ + if (endpoints == NULL || endpoints->len != 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "update interface endpoints not found"); + return FALSE; + } + + ep1 = g_ptr_array_index(endpoints, 0); + ep2 = g_ptr_array_index(endpoints, 1); + if (g_usb_endpoint_get_direction(ep1) == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST) { + epin = g_usb_endpoint_get_address(ep1); + epout = g_usb_endpoint_get_address(ep2); + cmd_read_size = g_usb_endpoint_get_maximum_packet_size(ep1); + cmd_write_size = g_usb_endpoint_get_maximum_packet_size(ep2); + } else { + epin = g_usb_endpoint_get_address(ep2); + epout = g_usb_endpoint_get_address(ep1); + cmd_read_size = g_usb_endpoint_get_maximum_packet_size(ep2); + cmd_write_size = g_usb_endpoint_get_maximum_packet_size(ep1); + } + + if (cmd_write_size > FU_CORSAIR_MAX_CMD_SIZE || cmd_read_size > FU_CORSAIR_MAX_CMD_SIZE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "endpoint size is bigger than allowed command size"); + return FALSE; + } + + fu_usb_device_add_interface(FU_USB_DEVICE(self), self->vendor_interface); + + self->bp = fu_corsair_bp_new(usb_device, FALSE); + fu_corsair_bp_set_cmd_size(self->bp, cmd_write_size, cmd_read_size); + fu_corsair_bp_set_endpoints(self->bp, epin, epout); + + return TRUE; +} + +static gboolean +fu_corsair_poll_subdevice(FuDevice *device, gboolean *subdevice_added, GError **error) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + guint32 subdevices; + g_autoptr(FuCorsairDevice) child = NULL; + g_autoptr(FuCorsairBp) child_bp = NULL; + + if (!fu_corsair_bp_get_property(self->bp, + FU_CORSAIR_BP_PROPERTY_SUBDEVICES, + &subdevices, + error)) { + g_prefix_error(error, "cannot get subdevices: "); + return FALSE; + } + + if (subdevices == 0) { + *subdevice_added = FALSE; + return TRUE; + } + + child_bp = fu_corsair_bp_new(usb_device, TRUE); + fu_device_incorporate(FU_DEVICE(child_bp), FU_DEVICE(self->bp)); + + child = fu_corsair_device_new(self, child_bp); + fu_device_add_instance_id(FU_DEVICE(child), self->subdevice_id); + fu_device_set_logical_id(FU_DEVICE(child), "subdevice"); + fu_device_add_internal_flag(FU_DEVICE(child), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + + if (!fu_device_probe(FU_DEVICE(child), error)) + return FALSE; + if (!fu_device_setup(FU_DEVICE(child), error)) + return FALSE; + + fu_device_add_child(device, FU_DEVICE(child)); + *subdevice_added = TRUE; + + return TRUE; +} + +static gchar * +fu_corsair_device_get_version(FuDevice *device, GError **error) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + guint32 version_raw; + + if (!fu_corsair_bp_get_property(self->bp, + FU_CORSAIR_BP_PROPERTY_VERSION, + &version_raw, + error)) + return NULL; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + gboolean broken_by_flag = + fu_device_has_private_flag(device, + FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER); + + /* Version 0xffffffff means that previous update was interrupted. + Set version to 0.0.0 in both broken and interrupted cases to make sure that new + firmware will not be rejected because of older version. It is safe to always + pass firmware because setup in bootloader mode can only happen during + emergency update */ + if (broken_by_flag || version_raw == G_MAXUINT32) { + version_raw = 0; + } + } + + return fu_corsair_version_from_uint32(version_raw); +} + +static gchar * +fu_corsair_device_get_bootloader_version(FuCorsairBp *self, GError **error) +{ + guint32 version_raw; + + if (!fu_corsair_bp_get_property(self, + FU_CORSAIR_BP_PROPERTY_BOOTLOADER_VERSION, + &version_raw, + error)) + return NULL; + + return fu_corsair_version_from_uint32(version_raw); +} + +static gboolean +fu_corsair_device_setup(FuDevice *device, GError **error) +{ + guint32 mode; + guint32 battery_level; + g_autofree gchar *bootloader_version = NULL; + g_autofree gchar *version = NULL; + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + + fu_corsair_bp_flush_input_reports(self->bp); + + if (!fu_corsair_bp_get_property(self->bp, FU_CORSAIR_BP_PROPERTY_MODE, &mode, error)) + return FALSE; + if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + + version = fu_corsair_device_get_version(device, error); + if (version == NULL) { + g_prefix_error(error, "cannot get version: "); + return FALSE; + } + fu_device_set_version(device, version); + + bootloader_version = fu_corsair_device_get_bootloader_version(self->bp, error); + if (bootloader_version == NULL) { + g_prefix_error(error, "cannot get bootloader version: "); + return FALSE; + } + fu_device_set_version_bootloader(device, bootloader_version); + + if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_corsair_bp_get_property(self->bp, + FU_CORSAIR_BP_PROPERTY_BATTERY_LEVEL, + &battery_level, + error)) { + g_prefix_error(error, "cannot get battery level: "); + return FALSE; + } + fu_device_set_battery_level(device, battery_level / 10); + } + fu_corsair_bp_set_legacy_attach( + self->bp, + fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH)); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + + /* check for a subdevice */ + if (self->subdevice_id != NULL && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + gboolean subdevice_added = FALSE; + g_autoptr(GError) local_error = NULL; + + /* Give some time to a subdevice to get connected to the receiver. + * Without this delay a subdevice may be not present even if it is + * turned on. */ + g_usleep(CORSAIR_SUBDEVICE_FIRST_POLL_DELAY); + if (!fu_corsair_poll_subdevice(device, &subdevice_added, &local_error)) { + g_warning("error polling subdevice: %s", local_error->message); + } else { + /* start polling if a subdevice was not added */ + if (!subdevice_added) + fu_device_set_poll_interval(device, CORSAIR_SUBDEVICE_POLL_PERIOD); + } + } + + return TRUE; +} + +static gboolean +fu_corsair_device_reload(FuDevice *device, GError **error) +{ + if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) { + return fu_corsair_device_setup(device, error); + } + + /* USB devices will be reloaded by FWUPD after reenumeration */ + return TRUE; +} + +static gboolean +fu_corsair_is_subdevice_connected_cb(FuDevice *device, gpointer user_data, GError **error) +{ + guint32 subdevices = 0; + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + + if (!fu_corsair_bp_get_property(self->bp, + FU_CORSAIR_BP_PROPERTY_SUBDEVICES, + &subdevices, + error)) { + g_prefix_error(error, "cannot get subdevices: "); + return FALSE; + } + + if (subdevices == 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "subdevice is not connected"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_corsair_reconnect_subdevice(FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + + if (parent == NULL) { + g_prefix_error(error, "cannot get parent: "); + return FALSE; + } + + /* Wait some time to make sure that a subdevice was disconnected. */ + g_usleep(CORSAIR_SUBDEVICE_REBOOT_DELAY); + + if (!fu_device_retry_full(parent, + fu_corsair_is_subdevice_connected_cb, + CORSAIR_SUBDEVICE_RECONNECT_RETRIES, + CORSAIR_SUBDEVICE_RECONNECT_PERIOD, + NULL, + error)) { + g_prefix_error(error, "a subdevice did not reconnect after attach: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_corsair_ensure_mode(FuDevice *device, FuCorsairDeviceMode mode, GError **error) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + FuCorsairDeviceMode current_mode; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + current_mode = FU_CORSAIR_DEVICE_MODE_BOOTLOADER; + } else { + current_mode = FU_CORSAIR_DEVICE_MODE_APPLICATION; + } + + if (mode == current_mode) + return TRUE; + + if (mode == FU_CORSAIR_DEVICE_MODE_APPLICATION) { + if (!fu_device_attach(FU_DEVICE(self->bp), error)) { + g_prefix_error(error, "attach failed: "); + return FALSE; + } + } else { + if (!fu_device_detach(FU_DEVICE(self->bp), error)) { + g_prefix_error(error, "detach failed: "); + return FALSE; + } + } + + if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) { + if (!fu_corsair_reconnect_subdevice(device, error)) { + g_prefix_error(error, "subdevice did not reconnect: "); + return FALSE; + } + if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + } else { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + + return TRUE; +} + +static gboolean +fu_corsair_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + return fu_corsair_ensure_mode(device, FU_CORSAIR_DEVICE_MODE_APPLICATION, error); +} + +static gboolean +fu_corsair_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + return fu_corsair_ensure_mode(device, FU_CORSAIR_DEVICE_MODE_BOOTLOADER, error); +} + +static gboolean +fu_corsair_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + g_autoptr(GBytes) firmware_bytes = fu_firmware_get_bytes(firmware, error); + + if (firmware_bytes == NULL) { + g_prefix_error(error, "cannot get firmware data: "); + return FALSE; + } + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, NULL); + + if (!fu_device_write_firmware(FU_DEVICE(self->bp), + firmware_bytes, + fu_progress_get_child(progress), + flags, + error)) { + g_prefix_error(error, "cannot write firmware: "); + return FALSE; + } + + fu_progress_step_done(progress); + + if (!fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH)) { + if (!fu_corsair_bp_activate_firmware(self->bp, firmware, error)) { + g_prefix_error(error, "firmware activation fail: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + + fu_progress_step_done(progress); + + return TRUE; +} + +static void +fu_corsair_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + + FU_DEVICE_CLASS(fu_corsair_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, + idt, + "DeviceKind", + fu_corsair_device_type_to_string(self->device_kind)); + + fu_device_add_string(FU_DEVICE(self->bp), idt, str); +} + +static void +fu_corsair_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 92, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static gboolean +fu_corsair_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + guint64 vendor_interface; + + if (g_strcmp0(key, "CorsairDeviceKind") == 0) { + self->device_kind = fu_corsair_device_type_from_string(value); + if (self->device_kind != FU_CORSAIR_DEVICE_UNKNOWN) + return TRUE; + + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unsupported device in quirk"); + return FALSE; + } else if (g_strcmp0(key, "CorsairVendorInterfaceId") == 0) { + /* clapped to uint8 because bNumInterfaces is 8 bits long */ + if (!fu_strtoull(value, &vendor_interface, 0, 255, error)) { + g_prefix_error(error, "cannot parse CorsairVendorInterface: "); + return FALSE; + } + self->vendor_interface = vendor_interface; + return TRUE; + } else if (g_strcmp0(key, "CorsairSubdeviceId") == 0) { + self->subdevice_id = g_strdup(value); + return TRUE; + } + + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "quirk key not supported"); + return FALSE; +} + +static gboolean +fu_corsair_device_poll(FuDevice *device, GError **error) +{ + gboolean subdevice_added = FALSE; + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error(error, "cannot open device: "); + return FALSE; + } + + if (!fu_corsair_poll_subdevice(device, &subdevice_added, error)) { + return FALSE; + } + + /* stop polling if a subdevice was added */ + if (subdevice_added) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "subdevice added successfully"); + return FALSE; + } + + return TRUE; +} + +static void +fu_corsair_device_finalize(GObject *object) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(object); + + g_free(self->subdevice_id); + g_object_unref(self->bp); + + G_OBJECT_CLASS(fu_corsair_device_parent_class)->finalize(object); +} + +static void +fu_corsair_device_class_init(FuCorsairDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + klass_device->poll = fu_corsair_device_poll; + klass_device->probe = fu_corsair_device_probe; + klass_device->set_quirk_kv = fu_corsair_set_quirk_kv; + klass_device->setup = fu_corsair_device_setup; + klass_device->reload = fu_corsair_device_reload; + klass_device->attach = fu_corsair_device_attach; + klass_device->detach = fu_corsair_device_detach; + klass_device->write_firmware = fu_corsair_device_write_firmware; + klass_device->to_string = fu_corsair_device_to_string; + klass_device->set_progress = fu_corsair_device_set_progress; + + object_class->finalize = fu_corsair_device_finalize; +} + +static void +fu_corsair_device_init(FuCorsairDevice *device) +{ + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + + self->device_kind = FU_CORSAIR_DEVICE_MOUSE; + self->vendor_interface = CORSAIR_DEFAULT_VENDOR_INTERFACE_ID; + + fu_device_register_private_flag(FU_DEVICE(device), + FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE, + "is-subdevice"); + fu_device_register_private_flag(FU_DEVICE(device), + FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH, + "legacy-attach"); + fu_device_register_private_flag(FU_DEVICE(device), + FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER, + "no-version-in-bl"); + + fu_device_set_remove_delay(FU_DEVICE(device), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_version_format(FU_DEVICE(device), FWUPD_VERSION_FORMAT_TRIPLET); + + fu_device_add_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + + fu_device_add_internal_flag(FU_DEVICE(device), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_protocol(FU_DEVICE(device), "com.corsair.bp"); +} + +FuCorsairDevice * +fu_corsair_device_new(FuCorsairDevice *parent, FuCorsairBp *bp) +{ + FuCorsairDevice *self = NULL; + FuDevice *device = FU_DEVICE(parent); + + self = g_object_new(FU_TYPE_CORSAIR_DEVICE, + "context", + fu_device_get_context(device), + "usb_device", + fu_usb_device_get_dev(FU_USB_DEVICE(device)), + NULL); + self->bp = g_object_ref(bp); + return self; +} diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-device.h b/fwupd-1.8.6/plugins/corsair/fu-corsair-device.h new file mode 100644 index 0000000000000000000000000000000000000000..6bde0e5955a458c908e448f477611c770d6eab86 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-device.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Andrii Dushko + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-corsair-bp.h" +#include "fu-corsair-common.h" + +#define FU_TYPE_CORSAIR_DEVICE (fu_corsair_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCorsairDevice, fu_corsair_device, FU, CORSAIR_DEVICE, FuUsbDevice) + +struct _FuCorsairDeviceClass { + FuUsbDeviceClass parent_class; +}; + +FuCorsairDevice * +fu_corsair_device_new(FuCorsairDevice *parent, FuCorsairBp *bp); diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-plugin.c b/fwupd-1.8.6/plugins/corsair/fu-corsair-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..36bc93dc44ac6033f44a5ed80d0ae05811a98300 --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-plugin.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Andrii Dushko + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-corsair-device.h" +#include "fu-corsair-plugin.h" + +struct _FuCorsairPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuCorsairPlugin, fu_corsair_plugin, FU_TYPE_PLUGIN) + +static void +fu_corsair_plugin_init(FuCorsairPlugin *self) +{ +} + +static void +fu_corsair_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "CorsairDeviceKind"); + fu_context_add_quirk_key(ctx, "CorsairVendorInterfaceId"); + fu_context_add_quirk_key(ctx, "CorsairSubdeviceId"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CORSAIR_DEVICE); +} + +static void +fu_corsair_plugin_class_init(FuCorsairPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_corsair_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/corsair/fu-corsair-plugin.h b/fwupd-1.8.6/plugins/corsair/fu-corsair-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..93e0075ce2357e8eb3d50b5095bcaa728e09944c --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/fu-corsair-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuCorsairPlugin, fu_corsair_plugin, FU, CORSAIR_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/corsair/meson.build b/fwupd-1.8.6/plugins/corsair/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..349abb4d7194ce5032c883850b325f0d103926ef --- /dev/null +++ b/fwupd-1.8.6/plugins/corsair/meson.build @@ -0,0 +1,17 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginCorsair"'] + +plugin_quirks += join_paths(meson.current_source_dir(), 'corsair.quirk') +plugin_builtins += static_library('fu_plugin_corsair', + sources: [ + 'fu-corsair-plugin.c', + 'fu-corsair-common.c', + 'fu-corsair-device.c', + 'fu-corsair-bp.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/cpu/README.md b/fwupd-1.8.6/plugins/cpu/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e5e1e5cbfc1f5667d503aa5c9da9f9aec5817d1a --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/README.md @@ -0,0 +1,19 @@ +# CPU Microcode + +## Introduction + +This plugin reads the sysfs attributes associated with CPU microcode. +It displays a read-only value of the CPU microcode version loaded onto +the physical CPU at fwupd startup. + +## GUID Generation + +These devices add extra instance IDs from the CPUID values, e.g. + +* `CPUID\PRO_0&FAM_06` +* `CPUID\PRO_0&FAM_06&MOD_0E` +* `CPUID\PRO_0&FAM_06&MOD_0E&STP_3` + +## External Interface Access + +This plugin requires no extra access. diff --git a/fwupd-1.8.6/plugins/cpu/cpu.quirk b/fwupd-1.8.6/plugins/cpu/cpu.quirk new file mode 100644 index 0000000000000000000000000000000000000000..7bbdbb41117236a29cc4bd58cfbb329f850ccbd2 --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/cpu.quirk @@ -0,0 +1,3 @@ +# Intel Atom Bay Trail [Silvermont] +[CPUID\PRO_0&FAM_06&MOD_37] +PciBcrAddr = 0x0 diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-device.c b/fwupd-1.8.6/plugins/cpu/fu-cpu-device.c new file mode 100644 index 0000000000000000000000000000000000000000..78bb35894fb98759c422c1dd10f7b8acfea7bb2f --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-device.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include + +#include "fu-cpu-device.h" + +struct _FuCpuDevice { + FuDevice parent_instance; + FuCpuDeviceFlag flags; +}; + +G_DEFINE_TYPE(FuCpuDevice, fu_cpu_device, FU_TYPE_DEVICE) + +gboolean +fu_cpu_device_has_flag(FuCpuDevice *self, FuCpuDeviceFlag flag) +{ + return (self->flags & flag) > 0; +} + +static void +fu_cpu_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCpuDevice *self = FU_CPU_DEVICE(device); + fu_string_append_kb(str, + idt, + "HasSHSTK", + fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SHSTK)); + fu_string_append_kb(str, + idt, + "HasIBT", + fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_IBT)); + fu_string_append_kb(str, + idt, + "HasTME", + fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_TME)); + fu_string_append_kb(str, + idt, + "HasSMAP", + fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SMAP)); +} + +static const gchar * +fu_cpu_device_convert_vendor(const gchar *vendor) +{ + if (g_strcmp0(vendor, "GenuineIntel") == 0) + return "Intel"; + if (g_strcmp0(vendor, "AuthenticAMD") == 0 || g_strcmp0(vendor, "AMDisbetter!") == 0) + return "AMD"; + if (g_strcmp0(vendor, "CentaurHauls") == 0) + return "IDT"; + if (g_strcmp0(vendor, "CyrixInstead") == 0) + return "Cyrix"; + if (g_strcmp0(vendor, "TransmetaCPU") == 0 || g_strcmp0(vendor, "GenuineTMx86") == 0) + return "Transmeta"; + if (g_strcmp0(vendor, "Geode by NSC") == 0) + return "National Semiconductor"; + if (g_strcmp0(vendor, "NexGenDriven") == 0) + return "NexGen"; + if (g_strcmp0(vendor, "RiseRiseRise") == 0) + return "Rise"; + if (g_strcmp0(vendor, "SiS SiS SiS ") == 0) + return "SiS"; + if (g_strcmp0(vendor, "UMC UMC UMC ") == 0) + return "UMC"; + if (g_strcmp0(vendor, "VIA VIA VIA ") == 0) + return "VIA"; + if (g_strcmp0(vendor, "Vortex86 SoC") == 0) + return "Vortex"; + if (g_strcmp0(vendor, " Shanghai ") == 0) + return "Zhaoxin"; + if (g_strcmp0(vendor, "HygonGenuine") == 0) + return "Hygon"; + if (g_strcmp0(vendor, "E2K MACHINE") == 0) + return "MCST"; + if (g_strcmp0(vendor, "bhyve bhyve ") == 0) + return "bhyve"; + if (g_strcmp0(vendor, " KVMKVMKVM ") == 0) + return "KVM"; + if (g_strcmp0(vendor, "TCGTCGTCGTCG") == 0) + return "QEMU"; + if (g_strcmp0(vendor, "Microsoft Hv") == 0) + return "Microsoft"; + if (g_strcmp0(vendor, " lrpepyh vr") == 0) + return "Parallels"; + if (g_strcmp0(vendor, "VMwareVMware") == 0) + return "VMware"; + if (g_strcmp0(vendor, "XenVMMXenVMM") == 0) + return "Xen"; + if (g_strcmp0(vendor, "ACRNACRNACRN") == 0) + return "ACRN"; + if (g_strcmp0(vendor, " QNXQVMBSQG ") == 0) + return "QNX"; + if (g_strcmp0(vendor, "VirtualApple") == 0) + return "Apple"; + return vendor; +} + +static void +fu_cpu_device_init(FuCpuDevice *self) +{ + fu_device_add_guid_full(FU_DEVICE(self), "cpu", FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); + fu_device_set_physical_id(FU_DEVICE(self), "cpu:0"); +} + +static gboolean +fu_cpu_device_add_instance_ids(FuDevice *device, GError **error) +{ + guint32 eax = 0; + guint32 family_id; + guint32 family_id_ext; + guint32 model_id; + guint32 model_id_ext; + guint32 processor_id; + guint32 stepping_id; + + /* decode according to https://en.wikipedia.org/wiki/CPUID */ + if (!fu_cpuid(0x1, &eax, NULL, NULL, NULL, error)) + return FALSE; + processor_id = (eax >> 12) & 0x3; + model_id = (eax >> 4) & 0xf; + family_id = (eax >> 8) & 0xf; + model_id_ext = (eax >> 16) & 0xf; + family_id_ext = (eax >> 20) & 0xff; + stepping_id = eax & 0xf; + + /* use extended IDs where required */ + if (family_id == 6 || family_id == 15) + model_id |= model_id_ext << 4; + if (family_id == 15) + family_id += family_id_ext; + + /* add GUIDs */ + fu_device_add_instance_u4(device, "PRO", processor_id); + fu_device_add_instance_u8(device, "FAM", family_id); + fu_device_add_instance_u8(device, "MOD", model_id); + fu_device_add_instance_u4(device, "STP", stepping_id); + fu_device_build_instance_id(device, NULL, "CPUID", "PRO", "FAM", NULL); + fu_device_build_instance_id(device, NULL, "CPUID", "PRO", "FAM", "MOD", NULL); + fu_device_build_instance_id(device, NULL, "CPUID", "PRO", "FAM", "MOD", "STP", NULL); + + /* success */ + return TRUE; +} + +static gboolean +fu_cpu_device_probe_manufacturer_id(FuDevice *device, GError **error) +{ + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + gchar str[13] = {'\0'}; + if (!fu_cpuid(0x0, NULL, &ebx, &ecx, &edx, error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + 0x0, /* dst */ + (const guint8 *)&ebx, + sizeof(ebx), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + 0x4, /* dst */ + (const guint8 *)&edx, + sizeof(edx), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + 0x8, /* dst */ + (const guint8 *)&ecx, + sizeof(ecx), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + fu_device_set_vendor(device, fu_cpu_device_convert_vendor(str)); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_model(FuDevice *device, GError **error) +{ + guint32 eax = 0; + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + gchar str[49] = {'\0'}; + + for (guint32 i = 0; i < 3; i++) { + if (!fu_cpuid(0x80000002 + i, &eax, &ebx, &ecx, &edx, error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + (16 * i) + 0x0, /* dst */ + (const guint8 *)&eax, + sizeof(eax), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + (16 * i) + 0x4, /* dst */ + (const guint8 *)&ebx, + sizeof(ebx), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + (16 * i) + 0x8, /* dst */ + (const guint8 *)&ecx, + sizeof(ecx), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)str, + sizeof(str), + (16 * i) + 0xc, /* dst */ + (const guint8 *)&edx, + sizeof(edx), + 0x0, /* src */ + sizeof(guint32), + error)) + return FALSE; + } + fu_device_set_name(device, str); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_extended_features(FuDevice *device, GError **error) +{ + FuCpuDevice *self = FU_CPU_DEVICE(device); + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + + if (!fu_cpuid(0x7, NULL, &ebx, &ecx, &edx, error)) + return FALSE; + if ((ebx >> 20) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_SMAP; + if ((ecx >> 7) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_SHSTK; + if ((ecx >> 13) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_TME; + if ((edx >> 20) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_IBT; + return TRUE; +} + +static gboolean +fu_cpu_device_probe(FuDevice *device, GError **error) +{ + if (!fu_cpu_device_probe_manufacturer_id(device, error)) + return FALSE; + if (!fu_cpu_device_probe_model(device, error)) + return FALSE; + if (!fu_cpu_device_probe_extended_features(device, error)) + return FALSE; + if (!fu_cpu_device_add_instance_ids(device, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_cpu_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + if (g_strcmp0(key, "PciBcrAddr") == 0) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + fu_device_set_metadata_integer(device, "PciBcrAddr", tmp); + return TRUE; + } + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported"); + return FALSE; +} + +static void +fu_cpu_device_add_security_attrs_intel_cet_enabled(FuCpuDevice *self, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = + fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED); + fu_security_attrs_append(attrs, attr); + + /* check for CET */ + if (!fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SHSTK) || + !fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_IBT)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_cpu_device_add_security_attrs_intel_cet_active(FuCpuDevice *self, FuSecurityAttrs *attrs) +{ + gint exit_status = 0xff; + g_autofree gchar *toolfn = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* check for CET */ + if (!fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SHSTK) || + !fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_IBT)) + return; + + /* create attr */ + attr = + fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append(attrs, attr); + + /* check that userspace has been compiled for CET support */ + toolfn = g_build_filename(FWUPD_LIBEXECDIR, "fwupd", "fwupd-detect-cet", NULL); + if (!g_spawn_command_line_sync(toolfn, NULL, NULL, &exit_status, &error_local)) { + g_warning("failed to test CET: %s", error_local->message); + return; + } +#if GLIB_CHECK_VERSION(2, 69, 2) + if (!g_spawn_check_wait_status(exit_status, &error_local)) { +#else + if (!g_spawn_check_exit_status(exit_status, &error_local)) { +#endif + g_debug("CET does not function, not supported: %s", error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_SUPPORTED); +} + +static void +fu_cpu_device_add_security_attrs_intel_tme(FuCpuDevice *self, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + fu_security_attrs_append(attrs, attr); + + /* check for TME */ + if (!fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_TME)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_cpu_device_add_security_attrs_intel_smap(FuCpuDevice *self, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_INTEL_SMAP); + fu_security_attrs_append(attrs, attr); + + /* check for SMEP and SMAP */ + if (!fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SMAP)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_cpu_device_add_supported_cpu_attribute(FuCpuDevice *self, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + + attr = fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU); + switch (fu_cpu_get_vendor()) { + case FU_CPU_VENDOR_INTEL: + case FU_CPU_VENDOR_AMD: + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); + break; + default: + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + } + fu_security_attrs_append(attrs, attr); +} + +static void +fu_cpu_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) +{ + FuCpuDevice *self = FU_CPU_DEVICE(device); + + /* only Intel */ + if (fu_cpu_get_vendor() == FU_CPU_VENDOR_INTEL) { + fu_cpu_device_add_security_attrs_intel_cet_enabled(self, attrs); + fu_cpu_device_add_security_attrs_intel_cet_active(self, attrs); + fu_cpu_device_add_security_attrs_intel_tme(self, attrs); + fu_cpu_device_add_security_attrs_intel_smap(self, attrs); + } + + fu_cpu_device_add_supported_cpu_attribute(self, attrs); +} + +static void +fu_cpu_device_class_init(FuCpuDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_cpu_device_to_string; + klass_device->probe = fu_cpu_device_probe; + klass_device->set_quirk_kv = fu_cpu_device_set_quirk_kv; + klass_device->add_security_attrs = fu_cpu_device_add_security_attrs; +} + +FuCpuDevice * +fu_cpu_device_new(FuContext *ctx) +{ + FuCpuDevice *device = NULL; + device = g_object_new(FU_TYPE_CPU_DEVICE, "context", ctx, NULL); + return device; +} diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-device.h b/fwupd-1.8.6/plugins/cpu/fu-cpu-device.h new file mode 100644 index 0000000000000000000000000000000000000000..3c3fbabc5d1176826e12423022538b2ff7bb717c --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-device.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CPU_DEVICE (fu_cpu_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCpuDevice, fu_cpu_device, FU, CPU_DEVICE, FuDevice) + +typedef enum { + FU_CPU_DEVICE_FLAG_NONE = 0, + FU_CPU_DEVICE_FLAG_SHSTK = 1 << 0, + FU_CPU_DEVICE_FLAG_IBT = 1 << 1, + FU_CPU_DEVICE_FLAG_TME = 1 << 2, + FU_CPU_DEVICE_FLAG_SMAP = 1 << 3, +} FuCpuDeviceFlag; + +FuCpuDevice * +fu_cpu_device_new(FuContext *ctx); +gboolean +fu_cpu_device_has_flag(FuCpuDevice *self, FuCpuDeviceFlag flag); diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet-common.c b/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet-common.c new file mode 100644 index 0000000000000000000000000000000000000000..f1bc49446b260ad1166c1c2f40fd495d63e9523d --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet-common.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 H.J. Lu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-cpu-helper-cet-common.h" + +static void +fu_cpu_helper_cet_testfn_fptr(void) +{ +} + +static void __attribute__((noinline, noclone)) +fu_cpu_helper_cet_testfn_call_fptr(void (*func)(void)) +{ + func(); +} + +void __attribute__((noinline, noclone)) fu_cpu_helper_cet_testfn1(void) +{ + fu_cpu_helper_cet_testfn_call_fptr(fu_cpu_helper_cet_testfn_fptr); +} diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet-common.h b/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet-common.h new file mode 100644 index 0000000000000000000000000000000000000000..9f1bd26bb599c2ce92e1f11d193aeaefc1f00f26 --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet-common.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 H.J. Lu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +void +fu_cpu_helper_cet_testfn1(void); diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet.c b/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet.c new file mode 100644 index 0000000000000000000000000000000000000000..1e9d930842c20aaf57cc04f3b26c2d160b50f6b8 --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-helper-cet.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (C) 2020 H.J. Lu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-cpu-helper-cet-common.h" + +#ifdef HAVE_SIGACTION +static __attribute__((noreturn)) void +segfault_sigaction(int signal, siginfo_t *si, void *arg) +{ + /* CET did exactly as it should to protect the system */ + exit(0); +} +#endif + +int +main(int argc, char *argv[]) +{ +#ifdef HAVE_SIGACTION + struct sigaction sa = {0}; + + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = segfault_sigaction; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); +#endif + + fu_cpu_helper_cet_testfn1(); + + /* this means CET did not work */ + return 1; +} diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-plugin.c b/fwupd-1.8.6/plugins/cpu/fu-cpu-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..c73ae67c9564e5acbaa8905a5f303a503e85667b --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-plugin.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-cpu-device.h" +#include "fu-cpu-plugin.h" + +struct _FuCpuPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuCpuPlugin, fu_cpu_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_cpu_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(FuCpuDevice) dev = fu_cpu_device_new(ctx); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "probe"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "setup"); + + if (!fu_device_probe(FU_DEVICE(dev), error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_device_setup(FU_DEVICE(dev), error)) + return FALSE; + fu_progress_step_done(progress); + + fu_plugin_cache_add(plugin, "cpu", dev); + fu_plugin_device_add(plugin, FU_DEVICE(dev)); + return TRUE; +} + +static void +fu_cpu_plugin_init(FuCpuPlugin *self) +{ +} + +static void +fu_cpu_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_BEFORE, "msr"); +} + +static void +fu_cpu_plugin_class_init(FuCpuPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_cpu_plugin_constructed; + plugin_class->coldplug = fu_cpu_plugin_coldplug; +} diff --git a/fwupd-1.8.6/plugins/cpu/fu-cpu-plugin.h b/fwupd-1.8.6/plugins/cpu/fu-cpu-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..9bacde01a6f72a224b053a8b1d1ae2f425c68592 --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/fu-cpu-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuCpuPlugin, fu_cpu_plugin, FU, CPU_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/cpu/meson.build b/fwupd-1.8.6/plugins/cpu/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..537e16a8ac8228a5f64472d0ff42dd6cb375a24e --- /dev/null +++ b/fwupd-1.8.6/plugins/cpu/meson.build @@ -0,0 +1,61 @@ +if get_option('plugin_cpu').disable_auto_if(host_machine.system() != 'linux').require(hsi, + error_message: 'plugin_cpu needs hsi to be set').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginCpu"'] + +plugin_quirks += files('cpu.quirk') +plugin_builtins += static_library('fu_plugin_cpu', + sources: [ + 'fu-cpu-plugin.c', + 'fu-cpu-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +code = ''' +#if !__has_attribute (__noclone__) +#error symver attribute not supported +#endif +static void __attribute__((noinline,noclone)) f(void) {} +''' + +# verify the compiler knows what to do +if cc.has_argument('-fcf-protection') + build_fwupdcethelper = cc.compiles(code, + name: '__attribute__((noinline,noclone))', + ) +else + build_fwupdcethelper = false +endif + +if build_fwupdcethelper + libfwupdcethelper = static_library('fwupdcethelper', + sources: [ + 'fu-cpu-helper-cet-common.c', + ], + include_directories: [ + root_incdir, + ], + c_args: ['-fcf-protection=none'], + install: false, + ) + + executable( + 'fwupd-detect-cet', + sources: [ + 'fu-cpu-helper-cet.c', + ], + include_directories: [ + root_incdir, + ], + link_with: [ + libfwupdcethelper, + ], + c_args: ['-fcf-protection=full'], + install: true, + install_dir: join_paths(libexecdir, 'fwupd') + ) +endif +endif diff --git a/fwupd-1.8.6/plugins/cros-ec/README.md b/fwupd-1.8.6/plugins/cros-ec/README.md new file mode 100644 index 0000000000000000000000000000000000000000..00f768127b269a01eb50b3486881d3ef3aa97e9b --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/README.md @@ -0,0 +1,48 @@ +# Chrome OS EC + +## Introduction + +This plugin provides support for the firmware updates for Chrome OS EC +project based devices. + +Initially, it supports the USB endpoint updater, but lays the groundwork for +future updaters which use other update methods other than the USB endpoint. + +This is based on the chromeos ec project's [usb_updater2 application](https://chromium.googlesource.com/chromiumos/platform/ec/+/master/extra/usb_updater/usb_updater2.c). + +Information about the USB update protocol is [available here](https://chromium.googlesource.com/chromiumos/platform/ec/+/master/docs/usb_updater.md). + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +the Google [fmap file format](https://www.chromium.org/chromium-os/firmware-porting-guide/fmap). + +This plugin supports the following protocol ID: + +* com.google.usb.crosec + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_18D1&PID_501A` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with +the same USB PID in an unlocked mode. On attach the device again re-enumerates +back to the runtime locked mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, which is set to various different +values depending on the model and device mode. The list of USB VIDs used is: + +* `USB:0x18D1` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/cros-ec/cros-ec.quirk b/fwupd-1.8.6/plugins/cros-ec/cros-ec.quirk new file mode 100644 index 0000000000000000000000000000000000000000..8273c5ea0eccad2e10b60a5d44f133661061d678 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/cros-ec.quirk @@ -0,0 +1,29 @@ +# Servo Micro +[USB\VID_18D1&PID_501A] +Plugin = cros_ec +Summary = Servo Micro (aka "uServo") Debug Board + +# Quiche +[USB\VID_18D1&PID_5048] +Plugin = cros_ec +Summary = Quiche Reference Board + +# Baklava (D501) +[USB\VID_0502&PID_1195] +Plugin = cros_ec +Summary = D501 Device (Google Quiche derivative) + +# Gingerbread +[USB\VID_18D1&PID_5049] +Plugin = cros_ec +Summary = Gingerbread Reference Board + +# Belkin +[USB\VID_050D&PID_003F] +Plugin = cros_ec +Summary = Belkin Reference Board + +# Prism +[USB\VID_18D1&PID_5022] +Plugin = cros_ec +Summary = Prism Board diff --git a/fwupd-1.8.6/plugins/cros-ec/data/lsusb-servo-micro.txt b/fwupd-1.8.6/plugins/cros-ec/data/lsusb-servo-micro.txt new file mode 100644 index 0000000000000000000000000000000000000000..358ad17248c222154789002493b79d5aa6140ac4 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/data/lsusb-servo-micro.txt @@ -0,0 +1,239 @@ + +Bus 003 Device 006: ID 18d1:501a Google Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x18d1 Google Inc. + idProduct 0x501a + bcdDevice 1.00 + iManufacturer 1 Google Inc. + iProduct 2 Servo Micro + iSerial 3 CMO653-00166-040491U00771 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 170 + bNumInterfaces 7 + bConfigurationValue 1 + iConfiguration 4 servo_micro_v2.4.17-df61092c3 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 6 UART3 + 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 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 83 + bInterfaceProtocol 255 + iInterface 10 Firmware update + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 81 + bInterfaceProtocol 1 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 3 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 7 Servo Shell + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x84 EP 4 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x04 EP 4 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 4 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 82 + bInterfaceProtocol 1 + iInterface 5 I2C + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x85 EP 5 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x05 EP 5 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 5 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 8 CPU + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x86 EP 6 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x06 EP 6 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 6 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 80 + bInterfaceProtocol 1 + iInterface 9 EC + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x87 EP 7 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 10 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x07 EP 7 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-common.c b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-common.c new file mode 100644 index 0000000000000000000000000000000000000000..793ad3aad62c171fcab6934f49854e0f7c65e390 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-common.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-cros-ec-common.h" + +gboolean +fu_cros_ec_parse_version(const gchar *version_raw, struct cros_ec_version *version, GError **error) +{ + g_auto(GStrv) v_split = NULL; + g_auto(GStrv) marker_split = NULL; + g_auto(GStrv) triplet_split = NULL; + + if (NULL == version_raw || 0 == strlen(version_raw)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no version string to parse"); + return FALSE; + } + + /* sample version string: cheese_v1.1.1755-4da9520 */ + v_split = g_strsplit(version_raw, "_v", 2); + if (g_strv_length(v_split) < 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "version marker not found"); + return FALSE; + } + marker_split = g_strsplit_set(v_split[1], "-+", 2); + if (g_strv_length(marker_split) < 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "hash marker not found: %s", + v_split[1]); + return FALSE; + } + triplet_split = g_strsplit_set(marker_split[0], ".", 3); + if (g_strv_length(triplet_split) < 3) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "improper version triplet: %s", + marker_split[0]); + return FALSE; + } + (void)g_strlcpy(version->triplet, marker_split[0], 32); + if (g_strlcpy(version->boardname, v_split[0], 32) == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "empty board name"); + return FALSE; + } + if (g_strlcpy(version->sha1, marker_split[1], 32) == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "empty SHA"); + return FALSE; + } + version->dirty = (g_strrstr(v_split[1], "+") != NULL); + + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-common.h b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-common.h new file mode 100644 index 0000000000000000000000000000000000000000..b1707a2ce1c4a7cbc942f4c848cc0aeef9cdbaa0 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-common.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define UPDATE_PROTOCOL_VERSION 6 +#define FU_CROS_EC_STRLEN 32 + +/* + * This is the format of the update PDU header. + * + * block digest: the first four bytes of the sha1 digest of the rest of the + * structure (can be 0 on boards where digest is ignored). + * block_base: offset of this PDU into the flash SPI. + */ +typedef struct __attribute__((packed)) { + guint32 block_digest; + guint32 block_base; + /* The actual payload goes here. */ +} update_command; + +/* + * This is the frame format the host uses when sending update PDUs over USB. + * + * The PDUs are up to 1K bytes in size, they are fragmented into USB chunks of + * 64 bytes each and reassembled on the receive side before being passed to + * the flash update function. + * + * The flash update function receives the unframed PDU body (starting at the + * cmd field below), and puts its reply into the same buffer the PDU was in. + */ +struct update_frame_header { + guint32 block_size; /* Total frame size, including this field. */ + update_command cmd; +}; + +/* + * A convenience structure which allows to group together various revision + * fields of the header created by the signer (cr50-specific). + * + * These fields are compared when deciding if versions of two images are the + * same or when deciding which one of the available images to run. + */ +struct signed_header_version { + guint32 minor; + guint32 major; + guint32 epoch; +}; + +/* + * Response to the connection establishment request. + * + * When responding to the very first packet of the update sequence, the + * original USB update implementation was responding with a four byte value, + * just as to any other block of the transfer sequence. + * + * It became clear that there is a need to be able to enhance the update + * protocol, while staying backwards compatible. + * + * All newer protocol versions (starting with version 2) respond to the very + * first packet with an 8 byte or larger response, where the first 4 bytes are + * a version specific data, and the second 4 bytes - the protocol version + * number. + * + * This way the host receiving of a four byte value in response to the first + * packet is considered an indication of the target running the 'legacy' + * protocol, version 1. Receiving of an 8 byte or longer response would + * communicates the protocol version in the second 4 bytes. + */ +struct first_response_pdu { + guint32 return_value; + + /* The below fields are present in versions 2 and up. */ + + /* Type of header following (one of first_response_pdu_header_type) */ + guint16 header_type; + + /* Must be UPDATE_PROTOCOL_VERSION */ + guint16 protocol_version; + + /* In version 6 and up, a board-specific header follows. */ + union { + /* cr50 (header_type = UPDATE_HEADER_TYPE_CR50) */ + struct { + /* The below fields are present in versions 3 and up. */ + guint32 backup_ro_offset; + guint32 backup_rw_offset; + + /* The below fields are present in versions 4 and up. */ + /* + * Versions of the currently active RO and RW sections. + */ + struct signed_header_version shv[2]; + + /* The below fields are present in versions 5 and up */ + /* keyids of the currently active RO and RW sections. */ + guint32 keyid[2]; + } cr50; + /* Common code (header_type = UPDATE_HEADER_TYPE_COMMON) */ + struct { + /* Maximum PDU size */ + guint32 maximum_pdu_size; + + /* Flash protection status */ + guint32 flash_protection; + + /* Offset of the other region */ + guint32 offset; + + /* Version string of the other region */ + gchar version[FU_CROS_EC_STRLEN]; + + /* Minimum rollback version that RO will accept */ + gint32 min_rollback; + + /* RO public key version */ + guint32 key_version; + } common; + }; +}; + +enum first_response_pdu_header_type { + UPDATE_HEADER_TYPE_CR50 = 0, /* Must be 0 for backwards compatibility */ + UPDATE_HEADER_TYPE_COMMON = 1, +}; + +struct cros_ec_version { + gchar boardname[FU_CROS_EC_STRLEN]; + gchar triplet[FU_CROS_EC_STRLEN]; + gchar sha1[FU_CROS_EC_STRLEN]; + gboolean dirty; +}; + +gboolean +fu_cros_ec_parse_version(const gchar *version_raw, struct cros_ec_version *version, GError **error); diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-firmware.c b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..bf0c6fcb147cdea2e1b2015e7f26340cf688df2f --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-firmware.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-cros-ec-common.h" +#include "fu-cros-ec-firmware.h" + +#define MAXSECTIONS 2 + +struct _FuCrosEcFirmware { + FuFmapFirmware parent_instance; + struct cros_ec_version version; + GPtrArray *sections; +}; + +G_DEFINE_TYPE(FuCrosEcFirmware, fu_cros_ec_firmware, FU_TYPE_FMAP_FIRMWARE) + +gboolean +fu_cros_ec_firmware_pick_sections(FuCrosEcFirmware *self, guint32 writeable_offset, GError **error) +{ + gboolean found = FALSE; + + for (gsize i = 0; i < self->sections->len; i++) { + FuCrosEcFirmwareSection *section = g_ptr_array_index(self->sections, i); + guint32 offset = section->offset; + + if (offset != writeable_offset) + continue; + + section->ustatus = FU_CROS_EC_FW_NEEDED; + found = TRUE; + } + + if (!found) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no writeable section found with offset: 0x%x", + writeable_offset); + return FALSE; + } + + /* success */ + return TRUE; +} + +GPtrArray * +fu_cros_ec_firmware_get_needed_sections(FuCrosEcFirmware *self, GError **error) +{ + g_autoptr(GPtrArray) needed_sections = g_ptr_array_new(); + + for (guint i = 0; i < self->sections->len; i++) { + FuCrosEcFirmwareSection *section = g_ptr_array_index(self->sections, i); + if (section->ustatus != FU_CROS_EC_FW_NEEDED) + continue; + g_ptr_array_add(needed_sections, section); + } + if (needed_sections->len == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no needed sections"); + return NULL; + } + + /* success */ + return g_steal_pointer(&needed_sections); +} + +static gboolean +fu_cros_ec_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcFirmware *self = FU_CROS_EC_FIRMWARE(firmware); + FuFirmware *fmap_firmware = FU_FIRMWARE(firmware); + + for (gsize i = 0; i < self->sections->len; i++) { + gboolean rw = FALSE; + FuCrosEcFirmwareSection *section = g_ptr_array_index(self->sections, i); + const gchar *fmap_name; + const gchar *fmap_fwid_name; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(FuFirmware) fwid_img = NULL; + g_autoptr(GBytes) payload_bytes = NULL; + g_autoptr(GBytes) fwid_bytes = NULL; + + if (g_strcmp0(section->name, "RO") == 0) { + fmap_name = "EC_RO"; + fmap_fwid_name = "RO_FRID"; + } else if (g_strcmp0(section->name, "RW") == 0) { + rw = TRUE; + fmap_name = "EC_RW"; + fmap_fwid_name = "RW_FWID"; + } else { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "incorrect section name"); + return FALSE; + } + + img = fu_firmware_get_image_by_id(fmap_firmware, fmap_name, error); + if (img == NULL) { + g_prefix_error(error, "%s image not found: ", fmap_name); + return FALSE; + } + + fwid_img = fu_firmware_get_image_by_id(fmap_firmware, fmap_fwid_name, error); + if (fwid_img == NULL) { + g_prefix_error(error, "%s image not found: ", fmap_fwid_name); + return FALSE; + } + fwid_bytes = fu_firmware_write(fwid_img, error); + if (fwid_bytes == NULL) { + g_prefix_error(error, "unable to get bytes from %s: ", fmap_fwid_name); + return FALSE; + } + if (!fu_memcpy_safe((guint8 *)section->raw_version, + FU_FMAP_FIRMWARE_STRLEN, + 0x0, + g_bytes_get_data(fwid_bytes, NULL), + g_bytes_get_size(fwid_bytes), + 0x0, + g_bytes_get_size(fwid_bytes), + error)) + return FALSE; + + payload_bytes = fu_firmware_write(img, error); + if (payload_bytes == NULL) { + g_prefix_error(error, "unable to get bytes from %s: ", fmap_name); + return FALSE; + } + section->offset = fu_firmware_get_addr(img); + section->size = g_bytes_get_size(payload_bytes); + fu_firmware_set_version(img, section->raw_version); + section->image_idx = fu_firmware_get_idx(img); + + if (!fu_cros_ec_parse_version(section->raw_version, §ion->version, error)) { + g_prefix_error(error, + "failed parsing firmware's version: %32s: ", + section->raw_version); + return FALSE; + } + + if (rw) { + if (!fu_cros_ec_parse_version(section->raw_version, + &self->version, + error)) { + g_prefix_error(error, + "failed parsing firmware's version: %32s: ", + section->raw_version); + return FALSE; + } + fu_firmware_set_version(firmware, self->version.triplet); + } + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_firmware_init(FuCrosEcFirmware *self) +{ + FuCrosEcFirmwareSection *section; + + self->sections = g_ptr_array_new_with_free_func(g_free); + section = g_new0(FuCrosEcFirmwareSection, 1); + section->name = "RO"; + g_ptr_array_add(self->sections, section); + section = g_new0(FuCrosEcFirmwareSection, 1); + section->name = "RW"; + g_ptr_array_add(self->sections, section); +} + +static void +fu_cros_ec_firmware_finalize(GObject *object) +{ + FuCrosEcFirmware *self = FU_CROS_EC_FIRMWARE(object); + g_ptr_array_free(self->sections, TRUE); + G_OBJECT_CLASS(fu_cros_ec_firmware_parent_class)->finalize(object); +} + +static void +fu_cros_ec_firmware_class_init(FuCrosEcFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_cros_ec_firmware_parse; + object_class->finalize = fu_cros_ec_firmware_finalize; +} + +FuFirmware * +fu_cros_ec_firmware_new(void) +{ + return g_object_new(FU_TYPE_CROS_EC_FIRMWARE, NULL); +} diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-firmware.h b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..ad75721dfff7fc14d5d7c5a5e49f6af90ce98807 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-firmware.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-cros-ec-common.h" + +#define FU_TYPE_CROS_EC_FIRMWARE (fu_cros_ec_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuCrosEcFirmware, fu_cros_ec_firmware, FU, CROS_EC_FIRMWARE, FuFmapFirmware) + +/* + * Each RO or RW section of the new image can be in one of the following + * states. + */ +typedef enum { + FU_CROS_EC_FW_NOT_NEEDED = 0, /* Version below or equal that on the target. */ + FU_CROS_EC_FW_NOT_POSSIBLE, /* + * RO is newer, but can't be transferred due to + * target RW shortcomings. + */ + FU_CROS_EC_FW_NEEDED /* + * This section needs to be transferred to the + * target. + */ +} FuCrosEcFirmwareUpgradeStatus; + +typedef struct { + const gchar *name; + guint32 offset; + gsize size; + FuCrosEcFirmwareUpgradeStatus ustatus; + gchar raw_version[FU_FMAP_FIRMWARE_STRLEN]; + struct cros_ec_version version; + gint32 rollback; + guint32 key_version; + guint64 image_idx; +} FuCrosEcFirmwareSection; + +gboolean +fu_cros_ec_firmware_pick_sections(FuCrosEcFirmware *self, guint32 writeable_offset, GError **error); +GPtrArray * +fu_cros_ec_firmware_get_needed_sections(FuCrosEcFirmware *self, GError **error); +FuFirmware * +fu_cros_ec_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-plugin.c b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..ee0b06e18e652973a0c167a0916cadb4277a2422 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-cros-ec-firmware.h" +#include "fu-cros-ec-plugin.h" +#include "fu-cros-ec-usb-device.h" + +struct _FuCrosEcPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuCrosEcPlugin, fu_cros_ec_plugin, FU_TYPE_PLUGIN) + +static void +fu_cros_ec_plugin_init(FuCrosEcPlugin *self) +{ +} + +static void +fu_cros_ec_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_CROS_EC_USB_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_CROS_EC_FIRMWARE); +} + +static void +fu_cros_ec_plugin_class_init(FuCrosEcPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_cros_ec_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-plugin.h b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..4e48222e471a31f19a6ff3271739947bacf47042 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuCrosEcPlugin, fu_cros_ec_plugin, FU, CROS_EC_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-usb-device.c b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-usb-device.c new file mode 100644 index 0000000000000000000000000000000000000000..36d3d8030c3c669731b2f7ee3cb8eb5a7baf419b --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-usb-device.c @@ -0,0 +1,1057 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-cros-ec-common.h" +#include "fu-cros-ec-firmware.h" +#include "fu-cros-ec-usb-device.h" + +#define USB_SUBCLASS_GOOGLE_UPDATE 0x53 +#define USB_PROTOCOL_GOOGLE_UPDATE 0xff + +#define SETUP_RETRY_CNT 5 +#define MAX_BLOCK_XFER_RETRIES 10 +#define FLUSH_TIMEOUT_MS 10 +#define BULK_SEND_TIMEOUT_MS 2000 +#define BULK_RECV_TIMEOUT_MS 5000 +#define CROS_EC_REMOVE_DELAY_RE_ENUMERATE 20000 + +#define UPDATE_DONE 0xB007AB1E +#define UPDATE_EXTRA_CMD 0xB007AB1F + +enum update_extra_command { + UPDATE_EXTRA_CMD_IMMEDIATE_RESET = 0, + UPDATE_EXTRA_CMD_JUMP_TO_RW = 1, + UPDATE_EXTRA_CMD_STAY_IN_RO = 2, + UPDATE_EXTRA_CMD_UNLOCK_RW = 3, + UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK = 4, + UPDATE_EXTRA_CMD_INJECT_ENTROPY = 5, + UPDATE_EXTRA_CMD_PAIR_CHALLENGE = 6, + UPDATE_EXTRA_CMD_TOUCHPAD_INFO = 7, + UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG = 8, + UPDATE_EXTRA_CMD_CONSOLE_READ_INIT = 9, + UPDATE_EXTRA_CMD_CONSOLE_READ_NEXT = 10, +}; + +struct _FuCrosEcUsbDevice { + FuUsbDevice parent_instance; + guint8 iface_idx; /* bInterfaceNumber */ + guint8 ep_num; /* bEndpointAddress */ + guint16 chunk_len; /* wMaxPacketSize */ + + struct first_response_pdu targ; + guint32 writeable_offset; + guint16 protocol_version; + guint16 header_type; + struct cros_ec_version version; /* version of other region */ + struct cros_ec_version active_version; /* version of active region */ + gchar configuration[FU_CROS_EC_STRLEN]; + gboolean in_bootloader; +}; + +G_DEFINE_TYPE(FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU_TYPE_USB_DEVICE) + +typedef union _START_RESP { + struct first_response_pdu rpdu; + guint32 legacy_resp; +} START_RESP; + +typedef struct { + FuChunk *block; + FuProgress *progress; +} FuCrosEcUsbBlockHelper; + +#define FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN (1 << 0) +#define FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN (1 << 1) +#define FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO (1 << 2) +#define FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL (1 << 3) + +static gboolean +fu_cros_ec_usb_device_get_configuration(FuCrosEcUsbDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 index; + g_autofree gchar *configuration = NULL; + +#if G_USB_CHECK_VERSION(0, 3, 5) + index = g_usb_device_get_configuration_index(usb_device); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif + configuration = g_usb_device_get_string_descriptor(usb_device, index, error); + if (configuration == NULL) + return FALSE; + + if (g_strlcpy(self->configuration, configuration, FU_CROS_EC_STRLEN) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "empty iConfiguration"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_find_interface(FuUsbDevice *device, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + GUsbDevice *usb_device = fu_usb_device_get_dev(device); + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + g_autoptr(GPtrArray) intfs = NULL; + + /* based on usb_updater2's find_interfacei() and find_endpoint() */ + + intfs = g_usb_device_get_interfaces(usb_device, error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == 255 && + g_usb_interface_get_subclass(intf) == USB_SUBCLASS_GOOGLE_UPDATE && + g_usb_interface_get_protocol(intf) == USB_PROTOCOL_GOOGLE_UPDATE) { + GUsbEndpoint *ep; + g_autoptr(GPtrArray) endpoints = NULL; + + endpoints = g_usb_interface_get_endpoints(intf); + if (NULL == endpoints || 0 == endpoints->len) + continue; + ep = g_ptr_array_index(endpoints, 0); + self->iface_idx = g_usb_interface_get_number(intf); + self->ep_num = g_usb_endpoint_get_address(ep) & 0x7f; + self->chunk_len = g_usb_endpoint_get_maximum_packet_size(ep); + + return TRUE; + } + } + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no update interface found"); + return FALSE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} + +static gboolean +fu_cros_ec_usb_device_probe(FuDevice *device, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + + /* very much like usb_updater2's usb_findit() */ + + if (!fu_cros_ec_usb_device_find_interface(FU_USB_DEVICE(device), error)) { + g_prefix_error(error, "failed to find update interface: "); + return FALSE; + } + fu_usb_device_add_interface(FU_USB_DEVICE(self), self->iface_idx); + + if (self->chunk_len == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wMaxPacketSize isn't valid: %" G_GUINT16_FORMAT, + self->chunk_len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_do_xfer(FuCrosEcUsbDevice *self, + const guint8 *outbuf, + gsize outlen, + guint8 *inbuf, + gsize inlen, + gboolean allow_less, + gsize *rxed_count, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual = 0; + + /* send data out */ + if (outbuf != NULL && outlen > 0) { + g_autofree guint8 *outbuf_tmp = NULL; + + /* make mutable */ + outbuf_tmp = fu_memdup_safe(outbuf, outlen, error); + if (outbuf_tmp == NULL) + return FALSE; + + if (!g_usb_device_bulk_transfer(usb_device, + self->ep_num, + outbuf_tmp, + outlen, + &actual, + BULK_SEND_TIMEOUT_MS, + NULL, + error)) { + return FALSE; + } + if (actual != outlen) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "only sent %" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT " bytes", + actual, + outlen); + return FALSE; + } + } + + /* read reply back */ + if (inbuf != NULL && inlen > 0) { + actual = 0; + if (!g_usb_device_bulk_transfer(usb_device, + self->ep_num | 0x80, + inbuf, + inlen, + &actual, + BULK_RECV_TIMEOUT_MS, + NULL, + error)) { + return FALSE; + } + if (actual != inlen && !allow_less) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "only received %" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT " bytes", + actual, + inlen); + return FALSE; + } + } + + if (rxed_count != NULL) + *rxed_count = actual; + + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_flush(FuDevice *device, gpointer user_data, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + gsize actual = 0; + g_autofree guint8 *inbuf = g_malloc0(self->chunk_len); + + /* bulk transfer expected to fail normally (ie, no stale data) + * but if bulk transfer succeeds, indicates stale bytes on the device + * so this will retry until they're emptied */ + if (g_usb_device_bulk_transfer(usb_device, + self->ep_num | 0x80, + inbuf, + self->chunk_len, + &actual, + FLUSH_TIMEOUT_MS, + NULL, + NULL)) { + g_debug("flushing %" G_GSIZE_FORMAT " bytes", actual); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "flushing %" G_GSIZE_FORMAT " bytes", + actual); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_recovery(FuDevice *device, GError **error) +{ + /* flush all data from endpoint to recover in case of error */ + if (!fu_device_retry(device, fu_cros_ec_usb_device_flush, SETUP_RETRY_CNT, NULL, error)) { + g_prefix_error(error, "failed to flush device to idle state: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* + * Channel TPM extension/vendor command over USB. The payload of the USB frame + * in this case consists of the 2 byte subcommand code concatenated with the + * command body. The caller needs to indicate if a response is expected, and + * if it is - of what maximum size. + */ +static gboolean +fu_cros_ec_usb_ext_cmd(FuDevice *device, + guint16 subcommand, + gpointer cmd_body, + gsize body_size, + gpointer resp, + gsize *resp_size, + gboolean allow_less, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + guint16 *frame_ptr; + gsize usb_msg_size = sizeof(struct update_frame_header) + sizeof(subcommand) + body_size; + g_autofree struct update_frame_header *ufh = g_malloc0(usb_msg_size); + + ufh->block_size = GUINT32_TO_BE(usb_msg_size); + ufh->cmd.block_digest = 0; + ufh->cmd.block_base = GUINT32_TO_BE(UPDATE_EXTRA_CMD); + frame_ptr = (guint16 *)(ufh + 1); + *frame_ptr = GUINT16_TO_BE(subcommand); + + if (body_size != 0) { + gsize offset = sizeof(struct update_frame_header) + sizeof(subcommand); + if (!fu_memcpy_safe((guint8 *)ufh, + usb_msg_size, + offset, + (const guint8 *)cmd_body, + body_size, + 0x0, + body_size, + error)) + return FALSE; + } + + return fu_cros_ec_usb_device_do_xfer(self, + (const guint8 *)ufh, + usb_msg_size, + (guint8 *)resp, + resp_size != NULL ? *resp_size : 0, + TRUE, + NULL, + error); +} + +static gboolean +fu_cros_ec_usb_device_start_request(FuDevice *device, gpointer user_data, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + guint8 *start_resp = (guint8 *)user_data; + struct update_frame_header ufh; + gsize rxed_size = 0; + + memset(&ufh, 0, sizeof(ufh)); + ufh.block_size = GUINT32_TO_BE(sizeof(ufh)); + if (!fu_cros_ec_usb_device_do_xfer(self, + (const guint8 *)&ufh, + sizeof(ufh), + start_resp, + sizeof(START_RESP), + TRUE, + &rxed_size, + error)) + return FALSE; + + /* we got something, so check for errors in response */ + if (rxed_size < 8) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "unexpected response size %" G_GSIZE_FORMAT, + rxed_size); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_setup(FuDevice *device, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + guint32 error_code; + START_RESP start_resp; + g_auto(GStrv) config_split = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_cros_ec_usb_device_parent_class)->setup(device, error)) + return FALSE; + + if (!fu_cros_ec_usb_device_recovery(device, error)) + return FALSE; + + /* send start request */ + if (!fu_device_retry(device, + fu_cros_ec_usb_device_start_request, + SETUP_RETRY_CNT, + &start_resp, + error)) { + g_prefix_error(error, "failed to send start request: "); + return FALSE; + } + + self->protocol_version = GUINT16_FROM_BE(start_resp.rpdu.protocol_version); + + if (self->protocol_version < 5 || self->protocol_version > 6) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "unsupported protocol version %d", + self->protocol_version); + return FALSE; + } + self->header_type = GUINT16_FROM_BE(start_resp.rpdu.header_type); + + error_code = GUINT32_FROM_BE(start_resp.rpdu.return_value); + if (error_code != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "target reporting error %u", + error_code); + return FALSE; + } + + self->writeable_offset = GUINT32_FROM_BE(start_resp.rpdu.common.offset); + if (!fu_memcpy_safe((guint8 *)self->targ.common.version, + FU_CROS_EC_STRLEN, + 0x0, + (const guint8 *)start_resp.rpdu.common.version, + sizeof(start_resp.rpdu.common.version), + 0x0, + sizeof(start_resp.rpdu.common.version), + error)) + return FALSE; + self->targ.common.maximum_pdu_size = + GUINT32_FROM_BE(start_resp.rpdu.common.maximum_pdu_size); + self->targ.common.flash_protection = + GUINT32_FROM_BE(start_resp.rpdu.common.flash_protection); + self->targ.common.min_rollback = GINT32_FROM_BE(start_resp.rpdu.common.min_rollback); + self->targ.common.key_version = GUINT32_FROM_BE(start_resp.rpdu.common.key_version); + + /* get active version string and running region from iConfiguration */ + if (!fu_cros_ec_usb_device_get_configuration(self, error)) + return FALSE; + config_split = g_strsplit(self->configuration, ":", 2); + if (g_strv_length(config_split) < 2) { + /* no prefix found so fall back to offset */ + self->in_bootloader = self->writeable_offset != 0x0; + if (!fu_cros_ec_parse_version(self->configuration, &self->active_version, error)) { + g_prefix_error(error, + "failed parsing device's version: %32s: ", + self->configuration); + return FALSE; + } + } else { + self->in_bootloader = g_strcmp0("RO", config_split[0]) == 0; + if (!fu_cros_ec_parse_version(config_split[1], &self->active_version, error)) { + g_prefix_error(error, + "failed parsing device's version: %32s: ", + config_split[1]); + return FALSE; + } + } + + /* get the other region's version string from targ */ + if (!fu_cros_ec_parse_version(self->targ.common.version, &self->version, error)) { + g_prefix_error(error, + "failed parsing device's version: %32s: ", + self->targ.common.version); + return FALSE; + } + + if (self->in_bootloader) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_set_version(FU_DEVICE(device), self->version.triplet); + fu_device_set_version_bootloader(FU_DEVICE(device), self->active_version.triplet); + } else { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_set_version(FU_DEVICE(device), self->active_version.triplet); + fu_device_set_version_bootloader(FU_DEVICE(device), self->version.triplet); + } + fu_device_add_instance_id(FU_DEVICE(device), self->version.boardname); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_transfer_block(FuDevice *device, gpointer user_data, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbBlockHelper *helper = (FuCrosEcUsbBlockHelper *)user_data; + gsize transfer_size = 0; + guint32 reply = 0; + g_autoptr(GPtrArray) chunks = NULL; + struct update_frame_header ufh = { + .block_size = GUINT32_TO_BE(fu_chunk_get_data_sz(helper->block) + + sizeof(struct update_frame_header)), + .cmd.block_base = GUINT32_TO_BE(fu_chunk_get_address(helper->block)), + .cmd.block_digest = 0, + }; + + /* first send the header */ + if (!fu_cros_ec_usb_device_do_xfer(self, + (const guint8 *)&ufh, + sizeof(struct update_frame_header), + NULL, + 0, + FALSE, + NULL, + error)) { + g_autoptr(GError) error_flush = NULL; + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery(device, &error_flush)) { + g_debug("failed to flush to idle: %s", error_flush->message); + } + g_prefix_error(error, "failed at sending header: "); + return FALSE; + } + + /* send the block, chunk by chunk */ + chunks = fu_chunk_array_new(fu_chunk_get_data(helper->block), + fu_chunk_get_data_sz(helper->block), + 0x00, + 0x00, + self->chunk_len); + fu_progress_set_id(helper->progress, G_STRLOC); + fu_progress_set_steps(helper->progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!fu_cros_ec_usb_device_do_xfer(self, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + NULL, + 0, + FALSE, + NULL, + error)) { + g_autoptr(GError) error_flush = NULL; + g_prefix_error(error, "failed sending chunk 0x%x: ", i); + + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery(device, &error_flush)) { + g_debug("failed to flush to idle: %s", error_flush->message); + } + return FALSE; + } + fu_progress_step_done(helper->progress); + } + + /* get the reply */ + if (!fu_cros_ec_usb_device_do_xfer(self, + NULL, + 0, + (guint8 *)&reply, + sizeof(reply), + TRUE, + &transfer_size, + error)) { + g_autoptr(GError) error_flush = NULL; + g_prefix_error(error, "failed at reply: "); + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery(device, &error_flush)) { + g_debug("failed to flush to idle: %s", error_flush->message); + } + return FALSE; + } + if (transfer_size == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "zero bytes received for block reply"); + return FALSE; + } + if (reply != 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "error: status 0x%#x", reply); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_transfer_section(FuDevice *device, + FuFirmware *firmware, + FuCrosEcFirmwareSection *section, + FuProgress *progress, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + const guint8 *data_ptr = NULL; + gsize data_len = 0; + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GPtrArray) blocks = NULL; + + g_return_val_if_fail(section != NULL, FALSE); + + img_bytes = fu_firmware_get_image_by_idx_bytes(firmware, section->image_idx, error); + if (img_bytes == NULL) { + g_prefix_error(error, "failed to find section image: "); + return FALSE; + } + + data_ptr = (const guint8 *)g_bytes_get_data(img_bytes, &data_len); + if (data_ptr == NULL || data_len != section->size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "image and section sizes do not match: image = %" G_GSIZE_FORMAT + " bytes vs section size = %" G_GSIZE_FORMAT " bytes", + data_len, + section->size); + return FALSE; + } + + /* smart update: trim trailing bytes */ + while (data_len != 0 && (data_ptr[data_len - 1] == 0xff)) + data_len--; + g_debug("trimmed %" G_GSIZE_FORMAT " trailing bytes", section->size - data_len); + g_debug("sending 0x%x bytes to 0x%x", (guint)data_len, section->offset); + + /* send in chunks of PDU size */ + blocks = fu_chunk_array_new(data_ptr, + data_len, + section->offset, + 0x0, + self->targ.common.maximum_pdu_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, blocks->len); + for (guint i = 0; i < blocks->len; i++) { + FuCrosEcUsbBlockHelper helper = { + .block = g_ptr_array_index(blocks, i), + .progress = fu_progress_get_child(progress), + }; + if (!fu_device_retry(device, + fu_cros_ec_usb_device_transfer_block, + MAX_BLOCK_XFER_RETRIES, + &helper, + error)) { + g_prefix_error(error, "failed to transfer block 0x%x: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_usb_device_send_done(FuDevice *device) +{ + guint32 out = GUINT32_TO_BE(UPDATE_DONE); + g_autoptr(GError) error_local = NULL; + + /* send stop request, ignoring reply */ + if (!fu_cros_ec_usb_device_do_xfer(FU_CROS_EC_USB_DEVICE(device), + (const guint8 *)&out, + sizeof(out), + (guint8 *)&out, + 1, + FALSE, + NULL, + &error_local)) { + g_debug("error on transfer of done: %s", error_local->message); + } +} + +static gboolean +fu_cros_ec_usb_device_send_subcommand(FuDevice *device, + guint16 subcommand, + gpointer cmd_body, + gsize body_size, + gpointer resp, + gsize *resp_size, + gboolean allow_less, + GError **error) +{ + fu_cros_ec_usb_device_send_done(device); + + if (!fu_cros_ec_usb_ext_cmd(device, + subcommand, + cmd_body, + body_size, + resp, + resp_size, + FALSE, + error)) { + g_prefix_error(error, + "failed to send subcommand %" G_GUINT16_FORMAT ": ", + subcommand); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_reset_to_ro(FuDevice *device, GError **error) +{ + guint8 response; + guint16 subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; + guint8 command_body[2]; /* Max command body size. */ + gsize command_body_size = 0; + gsize response_size = 1; + g_autoptr(GError) error_local = NULL; + + fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); + if (!fu_cros_ec_usb_device_send_subcommand(device, + subcommand, + command_body, + command_body_size, + &response, + &response_size, + FALSE, + &error_local)) { + /* failure here is ok */ + g_debug("ignoring failure: %s", error_local->message); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_jump_to_rw(FuDevice *device) +{ + guint8 response; + guint16 subcommand = UPDATE_EXTRA_CMD_JUMP_TO_RW; + guint8 command_body[2]; /* Max command body size. */ + gsize command_body_size = 0; + gsize response_size = 1; + + if (!fu_cros_ec_usb_device_send_subcommand(device, + subcommand, + command_body, + command_body_size, + &response, + &response_size, + FALSE, + NULL)) { + /* bail out early here if subcommand failed, which is normal */ + return TRUE; + } + + /* Jump to rw may not work, so if we've reached here, initiate a + * full reset using immediate reset */ + subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; + fu_cros_ec_usb_device_send_subcommand(device, + subcommand, + command_body, + command_body_size, + &response, + &response_size, + FALSE, + NULL); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_stay_in_ro(FuDevice *device, GError **error) +{ + gsize response_size = 1; + guint8 response; + guint16 subcommand = UPDATE_EXTRA_CMD_STAY_IN_RO; + guint8 command_body[2]; /* Max command body size. */ + gsize command_body_size = 0; + + if (!fu_cros_ec_usb_device_send_subcommand(device, + subcommand, + command_body, + command_body_size, + &response, + &response_size, + FALSE, + error)) { + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + g_autoptr(GPtrArray) sections = NULL; + FuCrosEcFirmware *cros_ec_firmware = FU_CROS_EC_FIRMWARE(firmware); + + fu_device_remove_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL); + + if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO)) { + START_RESP start_resp; + + fu_device_remove_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); + if (!fu_cros_ec_usb_device_stay_in_ro(device, error)) { + g_prefix_error(error, "failed to send stay-in-ro subcommand: "); + return FALSE; + } + + /* flush all data from endpoint to recover in case of error */ + if (!fu_cros_ec_usb_device_recovery(device, error)) { + g_prefix_error(error, "failed to flush device to idle state: "); + return FALSE; + } + + /* send start request */ + if (!fu_device_retry(device, + fu_cros_ec_usb_device_start_request, + SETUP_RETRY_CNT, + &start_resp, + error)) { + g_prefix_error(error, "failed to send start request: "); + return FALSE; + } + } + + if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN) && + self->in_bootloader) { + /* + * We had previously written to the rw region (while we were + * booted from ro region), but somehow landed in ro again after + * a reboot. Since we wrote rw already, we wanted to jump + * to the new rw so we could evaluate ro. + * + * This is a transitory state due to the fact that we have to + * boot through RO to get to RW. Set another write required to + * allow the RO region to auto-jump to RW. + * + * Special flow: write phase skips actual write -> attach skips + * send of reset command, just sets wait for replug, and + * device restart status. + */ + fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + return TRUE; + } + + sections = fu_cros_ec_firmware_get_needed_sections(cros_ec_firmware, error); + if (sections == NULL) + return FALSE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, sections->len); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < sections->len; i++) { + FuCrosEcFirmwareSection *section = g_ptr_array_index(sections, i); + g_autoptr(GError) error_local = NULL; + + if (!fu_cros_ec_usb_device_transfer_section(device, + firmware, + section, + fu_progress_get_child(progress), + &error_local)) { + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NOT_SUPPORTED)) { + g_debug("failed to transfer section, trying another write, " + "ignoring error: %s", + error_local->message); + fu_device_add_flag(device, + FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + fu_progress_finished(progress); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + if (self->in_bootloader) { + fu_device_set_version(FU_DEVICE(device), section->version.triplet); + } else { + fu_device_set_version_bootloader(FU_DEVICE(device), + section->version.triplet); + } + + fu_progress_step_done(progress); + } + /* send done */ + fu_cros_ec_usb_device_send_done(device); + + if (self->in_bootloader) + fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN); + else + fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN); + + /* logical XOR */ + if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN) != + fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN)) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_cros_ec_usb_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcFirmware *cros_ec_firmware = NULL; + g_autoptr(FuFirmware) firmware = fu_cros_ec_firmware_new(); + + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + cros_ec_firmware = FU_CROS_EC_FIRMWARE(firmware); + + /* pick sections */ + if (!fu_cros_ec_firmware_pick_sections(cros_ec_firmware, self->writeable_offset, error)) { + g_prefix_error(error, "failed to pick sections: "); + return NULL; + } + return g_steal_pointer(&firmware); +} + +static gboolean +fu_cros_ec_usb_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + + fu_device_set_remove_delay(device, CROS_EC_REMOVE_DELAY_RE_ENUMERATE); + if (self->in_bootloader && + fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL)) { + /* + * attach after the SPECIAL flag was set. The EC will auto-jump + * from ro -> rw, so we do not need to send an explicit + * reset_to_ro. We just need to set for another wait for replug + * as a detach + reenumeration is expected as we jump from + * ro -> rw. + */ + fu_device_remove_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN) && + !fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN)) { + if (!fu_cros_ec_usb_device_reset_to_ro(device, error)) { + return FALSE; + } + } else { + fu_cros_ec_usb_device_jump_to_rw(device); + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_cros_ec_usb_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + + if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN) && + !fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN)) + return TRUE; + + if (self->in_bootloader) { + /* If EC just rebooted - prevent jumping to RW during the update */ + fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); + g_debug("skipping immediate reboot in case of already in bootloader"); + /* in RO so skip reboot */ + return TRUE; + } else if (self->targ.common.flash_protection != 0x0) { + /* in RW, and RO region is write protected, so jump to RO */ + fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN); + fu_device_set_remove_delay(device, CROS_EC_REMOVE_DELAY_RE_ENUMERATE); + if (!fu_cros_ec_usb_device_reset_to_ro(device, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_usb_device_init(FuCrosEcUsbDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.google.usb.crosec"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_register_private_flag(FU_DEVICE(self), + FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN, + "ro-written"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN, + "rw-written"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO, + "rebooting-to-ro"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL, + "special"); +} + +static void +fu_cros_ec_usb_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + g_autofree gchar *min_rollback = NULL; + + fu_string_append(str, idt, "GitHash", self->version.sha1); + fu_string_append_kb(str, idt, "Dirty", self->version.dirty); + fu_string_append_ku(str, idt, "ProtocolVersion", self->protocol_version); + fu_string_append_ku(str, idt, "HeaderType", self->header_type); + fu_string_append_ku(str, idt, "MaxPDUSize", self->targ.common.maximum_pdu_size); + fu_string_append_kx(str, idt, "FlashProtectionStatus", self->targ.common.flash_protection); + fu_string_append(str, idt, "RawVersion", self->targ.common.version); + fu_string_append_ku(str, idt, "KeyVersion", self->targ.common.key_version); + min_rollback = g_strdup_printf("%" G_GINT32_FORMAT, self->targ.common.min_rollback); + fu_string_append(str, idt, "MinRollback", min_rollback); + fu_string_append_kx(str, idt, "WriteableOffset", self->writeable_offset); +} + +static void +fu_cros_ec_usb_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_cros_ec_usb_device_class_init(FuCrosEcUsbDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->attach = fu_cros_ec_usb_device_attach; + klass_device->detach = fu_cros_ec_usb_device_detach; + klass_device->prepare_firmware = fu_cros_ec_usb_device_prepare_firmware; + klass_device->setup = fu_cros_ec_usb_device_setup; + klass_device->to_string = fu_cros_ec_usb_device_to_string; + klass_device->write_firmware = fu_cros_ec_usb_device_write_firmware; + klass_device->probe = fu_cros_ec_usb_device_probe; + klass_device->set_progress = fu_cros_ec_usb_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-usb-device.h b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-usb-device.h new file mode 100644 index 0000000000000000000000000000000000000000..881748bfda04bafda00a9845d252ea1014c3e007 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/fu-cros-ec-usb-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_CROS_EC_USB_DEVICE (fu_cros_ec_usb_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU, CROS_EC_USB_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/cros-ec/meson.build b/fwupd-1.8.6/plugins/cros-ec/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..a8aa53f6e1afdfcecba7f071bfe72ea2a90cce52 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/meson.build @@ -0,0 +1,17 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginCrosEc"'] + +plugin_quirks += files('cros-ec.quirk') +plugin_builtins += static_library('fu_plugin_cros_ec', + sources: [ + 'fu-cros-ec-plugin.c', + 'fu-cros-ec-usb-device.c', + 'fu-cros-ec-common.c', # fuzzing + 'fu-cros-ec-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/cros-ec/tests/cros-ec.bin b/fwupd-1.8.6/plugins/cros-ec/tests/cros-ec.bin new file mode 100644 index 0000000000000000000000000000000000000000..004cad7ee0e4573c355a12955e8e19b61db01a85 Binary files /dev/null and b/fwupd-1.8.6/plugins/cros-ec/tests/cros-ec.bin differ diff --git a/fwupd-1.8.6/plugins/cros-ec/tests/cros-ec.builder.xml b/fwupd-1.8.6/plugins/cros-ec/tests/cros-ec.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..4a24bf841abf3853f66bc0e61d5c80e15fae8d55 --- /dev/null +++ b/fwupd-1.8.6/plugins/cros-ec/tests/cros-ec.builder.xml @@ -0,0 +1,19 @@ + + 0x3000 + + RO_FRID + Y2hlZXNlX3YxLjEuMTc1NS00ZGE5NTIwICAgICAgICA= + + + RW_FWID + Y2hlZXNlX3YxLjEuMTc1NS00ZGE5NTIwICAgICAgICA= + + + EC_RO + Y2hlZXNlX3YxLjEuMTc1NS00ZGE5NTIwICAgICAgICA= + + + EC_RW + Y2hlZXNlX3YxLjEuMTc1NS00ZGE5NTIwICAgICAgICA= + + diff --git a/fwupd-1.8.6/plugins/dell-dock/README.md b/fwupd-1.8.6/plugins/dell-dock/README.md new file mode 100644 index 0000000000000000000000000000000000000000..07a89067fd2d460dd37869f48585f5431d9172cf --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/README.md @@ -0,0 +1,144 @@ +# 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` +* USB4 Controller: `TBT-00d4b071` + +## Update Behavior + +All devices will be updated the next time the usb-c plug from the dock is unplugged from the host. + +### USB4 Controller + +This device will be probed by `dell-dock` plugin over the USB interface, additionally will be probed by `thunderbolt` plugin if thunderbolt hardware is enabled at the host. The primary plugin has been chosen to `dell-dock` for broader supoprt, the device introduced by `thunderbolt` plugin will be default inhibited, before fwupd version 1.8.2 (include), `Update Error` will be seen which is expected. + +```shell +USB4 controller in Dell dock: + Device ID: ce501f4b2e03e819c525bb9354aa88c03db4f11e + Summary: USB4 controller + Current version: 36.00 + Vendor: Dell Inc. (THUNDERBOLT:0x00D4, TBT:0x00D4) + Install Duration: 46 seconds + Update Error: firmware update inhibited by [dell_dock] plugin + GUIDs: 4fb9d92e-2b96-51a7-9ed5-3db156dfcf12 ← THUNDERBOLT\VEN_00D4&DEV_B071 + bd79ce60-525b-5f39-a3f6-c98c495039ff ← TBT-00d4b071 + 03f008d5-d06a-5d2e-89ca-61f12a8dbf73 ← TBT-00d4b071-controller0-3 + Device Flags: • System requires external power source + • Device stages updates + • Updatable + • Signed Payload +``` + +After version 1.8.2, the device introduced by `thunderbolt` plugin will be default hidden, so only the primary plugin will be seen on the device view. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x413C` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### DellDockUnlockTarget + +The EC argument needed for unlocking certain device usage. + +Since: 1.1.3 + +### DellDockBlobMajorOffset + +The offset of the major version number in a payload. + +Since: 1.1.3 + +### DellDockBlobMinorOffset + +The offset of the minor version number in a payload + +Since: 1.1.3 + +### DellDockBlobBuildOffset + +The offset of the build version number in a payload + +Since: 1.1.3 + +### DellDockBlobVersionOffset + +The offset of the ASCII representation of a version string in a payload. + +Since: 1.1.3 + +### DellDockBoardMin + +The minimum board revision required to safely operate the plugin. + +Since: 1.1.3 + +### DellDockVersionLowest + +The minimum component version required to safely operate the plugin. + +Since: 1.1.3 + +### DellDockBoard* + +The board description of a board revision. + +Since: 1.1.3 + +### DellDockInstallDurationI2C + +The duration of time required to install a payload via I2C. + +Since: 1.1.3 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/dell-dock/dell-dock.quirk b/fwupd-1.8.6/plugins/dell-dock/dell-dock.quirk new file mode 100644 index 0000000000000000000000000000000000000000..6c3a121d9ce7c1263fa5049859f56d7cee7f2265 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/dell-dock.quirk @@ -0,0 +1,215 @@ +# +# 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 +# + +# Used to make plugin probe the devices +[USB\VID_413C&PID_B06F] +Name = Unprobed Dell accessory endpoint +Plugin = dell_dock +[USB\VID_413C&PID_B06E] +Name = Unprobed Dell accessory endpoint +Plugin = dell_dock +Flags = has-bridge + +# USB hub1 +[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 = updatable,dual-image,usable-during-update +DellDockUnlockTarget = 8 +DellDockBlobMajorOffset = 0x7F6E +DellDockBlobMinorOffset = 0x7F6F +InstallDuration = 14 + +# USB hub2 +[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 = updatable,has-bridge,dual-image,usable-during-update +DellDockUnlockTarget = 7 +DellDockBlobMajorOffset = 0x7F52 +DellDockBlobMinorOffset = 0x7F53 +InstallDuration = 3 + +# Atomic USB hub1 +[USB\VID_413C&PID_B06F&atomic_hub] +Name = RTS5413 in Dell dock +Summary = USB 3.1 Generation 1 Hub +ParentGuid = USB\VID_413C&PID_B06E&hub&atomic_embedded +Plugin = dell_dock +Vendor = Dell Inc. +Icon = dock-usb +FirmwareSize = 0x10000 +Flags = updatable,dual-image,usable-during-update +DellDockUnlockTarget = 8 +DellDockBlobMajorOffset = 0x7F6E +DellDockBlobMinorOffset = 0x7F6F +InstallDuration = 14 + +# Atomic USB hub2 +[USB\VID_413C&PID_B06E&atomic_hub] +Name = RTS5487 in Dell dock +Summary = USB 3.1 Generation 2 Hub +ParentGuid = USB\VID_413C&PID_B06E&hub&atomic_embedded +Vendor = Dell Inc. +Plugin = dell_dock +Icon = dock-usb +FirmwareSize = 0x10000 +Flags = updatable,has-bridge,dual-image,usable-during-update +DellDockUnlockTarget = 7 +DellDockBlobMajorOffset = 0x7F52 +DellDockBlobMinorOffset = 0x7F53 +InstallDuration = 3 + +# Embedded Controller +# Name is intentionally not set (it's queried by dock) +[USB\VID_413C&PID_B06E&hub&embedded] +Name = Dell dock +Summary = High performance dock +Plugin = dell_dock +Vendor = Dell Inc. +VendorId = USB:0x413C +Icon = dock-usb +FirmwareSizeMin = 0x1FFC0 +FirmwareSizeMax = 0x20000 +Flags = dual-image,self-recovery,usable-during-update +DellDockUnlockTarget = 1 +DellDockBoardMin = 6 +DellDockVersionLowest = 01.01.00.01 +DellDockBlobVersionOffset = 0x1AFC0 +InstallDuration = 60 + +#Atomic Embedded Controller +# Name is intentionally not set (it's queried by dock) +[USB\VID_413C&PID_B06E&hub&atomic_embedded] +Name = Dell dock +Summary = High performance dock +Plugin = dell_dock +Vendor = Dell Inc. +VendorId = USB:0x413C +Icon = dock-usb +FirmwareSizeMin = 0x1FFC0 +FirmwareSizeMax = 0x20000 +Flags = dual-image,self-recovery,usable-during-update +DellDockUnlockTarget = 1 +DellDockBlobVersionOffset = 0x1AFC0 +InstallDuration = 60 + +# Representation of overall dock update +[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. +Flags = self-recovery,usable-during-update +FirmwareSize = 24 +InstallDuration = 5 +DellDockBlobVersionOffset = 0x14 + +# Representation of overall dock update +[USB\VID_413C&PID_B06E&hub&salomon_mlk_status] +Name = Package level of Dell dock +Summary = A representation of dock update status +Plugin = dell_dock +Vendor = Dell Inc. +Flags = self-recovery,usable-during-update +FirmwareSize = 24 +InstallDuration = 5 +DellDockBlobVersionOffset = 0x14 + +# Representation of overall dock update +[USB\VID_413C&PID_B06E&hub&atomic_status] +Name = Package level of Dell dock +Summary = A representation of dock update status +Plugin = dell_dock +Vendor = Dell Inc. +Flags = self-recovery,usable-during-update +FirmwareSize = 24 +InstallDuration = 5 +DellDockBlobVersionOffset = 0x14 + +# MST Hub +[MST-panamera-vmm5331-259] +Name = VMM5331 in Dell dock +Summary = Multi Stream Transport controller +Vendor = Dell Inc. +Plugin = dell_dock +ParentGuid = USB\VID_413C&PID_B06E&hub&embedded +Flags = skips-restart,dual-image,usable-during-update +FirmwareSize = 524288 +DellDockUnlockTarget = 9 +InstallDuration = 95 +DellDockInstallDurationI2C = 360 +DellDockBlobMajorOffset = 0x18400 +DellDockBlobMinorOffset = 0x18401 +DellDockBlobBuildOffset = 0x18402 +Icon = video-display + +#Atomic MST Hub +[MST-cayenne-vmm6210-257] +Name = VMM6210 in Dell dock +Summary = Multi Stream Transport controller +Vendor = Dell Inc. +Plugin = dell_dock +ParentGuid = USB\VID_413C&PID_B06E&hub&atomic_embedded +Flags = skips-restart,dual-image,usable-during-update +FirmwareSize = 1048576 +DellDockUnlockTarget = 9 +InstallDuration = 95 +DellDockInstallDurationI2C = 360 +DellDockBlobMajorOffset = 0x4000 +DellDockBlobMinorOffset = 0x4001 +DellDockBlobBuildOffset = 0x4002 +Icon = video-display + +# Thunderbolt controller +[TBT-00d4b070] +Name = Thunderbolt controller in Dell dock +Summary = Thunderbolt controller +Vendor = Dell Inc. +VendorId = TBT:0x00D4 +ParentGuid = USB\VID_413C&PID_B06E&hub&embedded +FirmwareSizeMin = 0x40000 +FirmwareSizeMax = 0x80000 +Flags = dual-image +Icon = thunderbolt +InstallDuration = 22 +DellDockInstallDurationI2C = 181 +DellDockUnlockTarget = 10 +DellDockHubVersionLowest = 1.31 +DellDockBlobMajorOffset = 0x400a +DellDockBlobMinorOffset = 0x4009 + +# USB4 controller +[TBT-00d4b071] +Name = USB4 controller in Dell dock +Summary = USB4 controller +Vendor = Dell Inc. +VendorId = TBT:0x00D4 +ParentGuid = USB\VID_413C&PID_B06E&hub&embedded +FirmwareSizeMin = 0x40000 +FirmwareSizeMax = 0x80000 +Flags = skips-restart,dual-image +Icon = thunderbolt +InstallDuration = 46 diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-common.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-common.c new file mode 100644 index 0000000000000000000000000000000000000000..6b41b3e26670d254ce16e92f204716cd9c0f6e37 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-common.c @@ -0,0 +1,61 @@ +/* + * 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" +#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); +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-common.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-common.h new file mode 100644 index 0000000000000000000000000000000000000000..3e5084cf7b6156942d4a8c952ce579b9dca4cd1d --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-common.h @@ -0,0 +1,51 @@ +/* + * 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-dell-dock-hid.h" +#include "fu-dell-dock-hub.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-status.h" + +#define DELL_DOCK_DOCK1_INSTANCE_ID "USB\\VID_413C&PID_B06E&hub&status" +#define DELL_DOCK_DOCK2_INSTANCE_ID "USB\\VID_413C&PID_B06E&hub&salomon_mlk_status" +#define DELL_DOCK_EC_INSTANCE_ID "USB\\VID_413C&PID_B06E&hub&embedded" +#define DELL_DOCK_TBT_INSTANCE_ID "TBT-00d4b070" +#define DELL_DOCK_USB4_INSTANCE_ID "TBT-00d4b071" +#define DELL_DOCK_VM5331_INSTANCE_ID "MST-panamera-vmm5331-259" +#define GR_USB_VID 0x8087 +#define GR_USB_PID 0x0B40 +#define DELL_DOCK_ATOMIC_STATUS_INSTANCE_ID "USB\\VID_413C&PID_B06E&hub&atomic_status" +#define DELL_DOCK_ATOMIC_EC_INSTANCE_ID "USB\\VID_413C&PID_B06E&hub&atomic_embedded" +#define DELL_DOCK_VMM6210_INSTANCE_ID "MST-cayenne-vmm6210-257" +#define ATOMIC_HUB2_PID 0x548A +#define ATOMIC_HUB1_PID 0x541A +#define DELL_VID 0x413C + +#define DOCK_BASE_TYPE_UNKNOWN 0x0 +#define DOCK_BASE_TYPE_SALOMON 0x04 +#define DOCK_BASE_TYPE_ATOMIC 0x05 + +gboolean +fu_dell_dock_set_power(FuDevice *device, guint8 target, gboolean enabled, GError **error); +void +fu_dell_dock_will_replug(FuDevice *device); diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hid.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hid.c new file mode 100644 index 0000000000000000000000000000000000000000..c82e3ed4ba5ca1f5a4f7ffd7f409e6626327e229 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hid.c @@ -0,0 +1,489 @@ +/* + * 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 + +#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 i2ctargetaddr; + 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_cb(FuDevice *self, gpointer user_data, GError **error) +{ + guint8 *outbuffer = (guint8 *)user_data; + return fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + outbuffer, + 192, + HIDI2C_TRANSACTION_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error); +} + +static gboolean +fu_dell_dock_hid_set_report(FuDevice *self, guint8 *outbuffer, GError **error) +{ + return fu_device_retry(self, + fu_dell_dock_hid_set_report_cb, + HID_MAX_RETRIES, + outbuffer, + error); +} + +static gboolean +fu_dell_dock_hid_get_report_cb(FuDevice *self, gpointer user_data, GError **error) +{ + guint8 *inbuffer = (guint8 *)user_data; + return fu_hid_device_get_report(FU_HID_DEVICE(self), + 0x0, + inbuffer, + 192, + HIDI2C_TRANSACTION_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error); +} + +static gboolean +fu_dell_dock_hid_get_report(FuDevice *self, guint8 *inbuffer, GError **error) +{ + return fu_device_retry(self, + fu_dell_dock_hid_get_report_cb, + HID_MAX_RETRIES, + inbuffer, + error); +} + +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 = {.i2ctargetaddr = 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_format(self, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(self, version); + 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 = {.i2ctargetaddr = 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 = {.i2ctargetaddr = 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 = {.i2ctargetaddr = 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 = {.i2ctargetaddr = 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 = {.i2ctargetaddr = 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 = {.i2ctargetaddr = parameters->i2ctargetaddr, + .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 = {.i2ctargetaddr = parameters->i2ctargetaddr, + .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, + .i2ctargetaddr = parameters->i2ctargetaddr, + .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, + .i2ctargetaddr = parameters->i2ctargetaddr, + .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, + .i2ctargetaddr = parameters->i2ctargetaddr, + .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ + .tbt_command = GUINT32_TO_LE(TBT_COMMAND_AUTHENTICATE), + .bufferlen = 0, + .extended_cmdarea[0 ... 53] = 0, + }; + guint8 result; + + if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { + g_prefix_error(error, "failed to send authentication: "); + return FALSE; + } + + cmd_buffer.tbt_command = GUINT32_TO_LE(TBT_COMMAND_AUTHENTICATE_STATUS); + /* needs at least 2 seconds */ + g_usleep(2000000); + for (gint i = 1; i <= TBT_MAX_RETRIES; i++) { + if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { + g_prefix_error(error, "failed to set check authentication: "); + return FALSE; + } + if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) { + g_prefix_error(error, "failed to get check authentication: "); + return FALSE; + } + result = cmd_buffer.data[1] & 0xf; + if (result == 0) + break; + g_debug("attempt %d/%d: Thunderbolt authenticate failed: %x", + i, + TBT_MAX_RETRIES, + result); + g_usleep(500000); + } + if (result != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Thunderbolt authentication failed: %s", + fu_dell_dock_hid_tbt_map_error(result)); + return FALSE; + } + + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hid.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hid.h new file mode 100644 index 0000000000000000000000000000000000000000..757989a77b7807ddf81dc166131240b2cd23ca5a --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hid.h @@ -0,0 +1,90 @@ +/* + * 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 + +typedef struct __attribute__((packed)) { + guint8 i2ctargetaddr; + 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 --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hub.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hub.c new file mode 100644 index 0000000000000000000000000000000000000000..86a24662e2c41c7c0f9cee1e2abb9557f746cd0c --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hub.c @@ -0,0 +1,227 @@ +/* + * 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 _FuDellDockHub { + FuHidDevice parent_instance; + guint8 unlock_target; + guint64 blob_major_offset; + guint64 blob_minor_offset; +}; + +G_DEFINE_TYPE(FuDellDockHub, fu_dell_dock_hub, FU_TYPE_HID_DEVICE) + +void +fu_dell_dock_hub_add_instance(FuDevice *device, guint8 dock_type) +{ + g_autofree gchar *devid = NULL; + + if (dock_type == DOCK_BASE_TYPE_ATOMIC) { + devid = g_strdup_printf("USB\\VID_%04X&PID_%04X&atomic_hub", + (guint)fu_usb_device_get_vid(FU_USB_DEVICE(device)), + (guint)fu_usb_device_get_pid(FU_USB_DEVICE(device))); + } else { + 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_add_instance_id(device, devid); +} + +static gboolean +fu_dell_dock_hub_probe(FuDevice *device, GError **error) +{ + fu_device_set_logical_id(device, "hub"); + fu_device_add_protocol(device, "com.dell.dock"); + + return TRUE; +} + +static gboolean +fu_dell_dock_hub_write_fw(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockHub *self = FU_DELL_DOCK_HUB(device); + gsize fw_size = 0; + const guint8 *data; + gsize write_size; + gsize nwritten = 0; + guint32 address = 0; + gboolean result = FALSE; + g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 49, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 50, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data(fw, &fw_size); + write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; + + dynamic_version = g_strdup_printf("%02x.%02x", + data[self->blob_major_offset], + 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; + + /* erase */ + if (!fu_dell_dock_hid_erase_bank(device, 1, error)) + return FALSE; + fu_progress_step_done(progress); + + /* 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_progress_set_percentage_full(fu_progress_get_child(progress), nwritten, fw_size); + } while (nwritten < fw_size); + fu_progress_step_done(progress); + + /* verify */ + 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; + } + fu_progress_step_done(progress); + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device, dynamic_version); + return TRUE; +} + +static gboolean +fu_dell_dock_hub_setup(FuDevice *device, GError **error) +{ + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_dell_dock_hub_parent_class)->setup(device, error)) + return FALSE; + return fu_dell_dock_hid_get_hub_version(device, error); +} + +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); + guint64 tmp = 0; + + if (g_strcmp0(key, "DellDockUnlockTarget") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->unlock_target = tmp; + return TRUE; + } + if (g_strcmp0(key, "DellDockBlobMajorOffset") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_major_offset = tmp; + return TRUE; + } + if (g_strcmp0(key, "DellDockBlobMinorOffset") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_minor_offset = tmp; + 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_hub_finalize(GObject *object) +{ + G_OBJECT_CLASS(fu_dell_dock_hub_parent_class)->finalize(object); +} + +static void +fu_dell_dock_hub_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_dell_dock_hub_init(FuDellDockHub *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_retry_set_delay(FU_DEVICE(self), 1000); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DELL_DOCK_HUB_FLAG_HAS_BRIDGE, + "has-bridge"); +} + +static void +fu_dell_dock_hub_class_init(FuDellDockHubClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_dell_dock_hub_finalize; + klass_device->setup = fu_dell_dock_hub_setup; + 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; + klass_device->set_progress = fu_dell_dock_hub_set_progress; +} + +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 --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hub.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hub.h new file mode 100644 index 0000000000000000000000000000000000000000..ce73b2cf3b0eedd8ac63899858a1dcd8760dff97 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-hub.h @@ -0,0 +1,35 @@ +/* + * 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 + +#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, FuHidDevice) + +/** + * FU_DELL_DOCK_HUB_FLAG_HAS_BRIDGE: + * + * A bridge is present, possibly with extended devices. + */ +#define FU_DELL_DOCK_HUB_FLAG_HAS_BRIDGE (1 << 0) + +FuDellDockHub * +fu_dell_dock_hub_new(FuUsbDevice *device); +void +fu_dell_dock_hub_add_instance(FuDevice *device, guint8 dock_type); diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-ec.c new file mode 100644 index 0000000000000000000000000000000000000000..f1c271396352b2a666853148ea18c67bf9822607 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -0,0 +1,1038 @@ +/* + * 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-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_PASSIVE 0x0d +#define EC_GET_FW_UPDATE_STATUS 0x0f + +#define EXPECTED_DOCK_INFO_SIZE 0xb7 + +#define TBT_MODE_MASK 0x01 + +#define BIT_SET(x, y) (x |= (1 << y)) +#define BIT_CLEAR(x, y) (x &= (~(1 << y))) + +#define PASSIVE_RESET_MASK 0x01 +#define PASSIVE_REBOOT_MASK 0x02 +#define PASSIVE_TBT_MASK 0x04 + +typedef enum { + FW_UPDATE_IN_PROGRESS, + FW_UPDATE_COMPLETE, + FW_UPDATE_AUTHENTICATION_FAILED, +} FuDellDockECFWUpdateStatus; + +const FuHIDI2CParameters ec_base_settings = { + .i2ctargetaddr = I2C_EC_ADDRESS, + .regaddrlen = 1, + .i2cspeed = I2C_SPEED_250K, +}; + +typedef enum { + LOCATION_BASE, + LOCATION_MODULE, +} FuDellDockLocationEnum; + +typedef enum { + FU_DELL_DOCK_DEVICETYPE_MAIN_EC = 0, + FU_DELL_DOCK_DEVICETYPE_PD = 1, + FU_DELL_DOCK_DEVICETYPE_HUB = 3, + FU_DELL_DOCK_DEVICETYPE_MST = 4, + FU_DELL_DOCK_DEVICETYPE_TBT = 5, +} FuDellDockDeviceTypeEnum; + +typedef enum { + SUBTYPE_GEN2, + SUBTYPE_GEN1, +} FuDellDockHubSubTypeEnum; + +typedef struct __attribute__((packed)) { + guint8 total_devices; + guint8 first_index; + guint8 last_index; +} FuDellDockDockInfoHeader; + +typedef struct __attribute__((packed)) { + guint8 location; + guint8 device_type; + guint8 sub_type; + guint8 arg; + guint8 instance; +} FuDellDockEcAddrMap; + +typedef struct __attribute__((packed)) { + FuDellDockEcAddrMap ec_addr_map; + union { + guint32 version_32; + guint8 version_8[4]; + } version; +} FuDellDockEcQueryEntry; + +typedef enum { + MODULE_TYPE_45_TBT = 1, + MODULE_TYPE_45, + MODULE_TYPE_130_TBT, + MODULE_TYPE_130_DP, + MODULE_TYPE_130_UNIVERSAL, + MODULE_TYPE_240_TRIN, + MODULE_TYPE_210_DUAL, + MODULE_TYPE_130_USB4, +} FuDellDockDockModule; + +typedef struct __attribute__((packed)) { + guint8 dock_configuration; + guint8 dock_type; + guint16 power_supply_wattage; + guint16 module_type; + guint16 board_id; + guint16 port0_dock_status; + guint16 port1_dock_status; + guint32 dock_firmware_pkg_ver; + guint64 module_serial; + guint64 original_module_serial; + gchar service_tag[7]; + gchar marketing_name[64]; +} FuDellDockDockDataStructure; + +typedef struct __attribute__((packed)) { + guint32 ec_version; + guint32 mst_version; + guint32 hub1_version; + guint32 hub2_version; + guint32 tbt_version; + guint32 pkg_version; +} FuDellDockDockPackageFWVersion; + +struct _FuDellDockEc { + FuDevice parent_instance; + FuDellDockDockDataStructure *data; + FuDellDockDockPackageFWVersion *raw_versions; + guint8 base_type; + gchar *ec_version; + gchar *mst_version; + gchar *tbt_version; + guint8 unlock_target; + guint8 board_min; + gchar *ec_minimum_version; + guint64 blob_version_offset; + guint8 passive_flow; + guint32 dock_unlock_status; +}; + +static gboolean +fu_dell_dock_get_ec_status(FuDevice *device, + FuDellDockECFWUpdateStatus *status_out, + GError **error); + +G_DEFINE_TYPE(FuDellDockEc, fu_dell_dock_ec, FU_TYPE_DEVICE) + +/* Used to root out I2C communication problems */ +static gboolean +fu_dell_dock_test_valid_byte(const guint8 *str, gint index) +{ + if (str[index] == 0x00 || str[index] == 0xff) + return FALSE; + return TRUE; +} + +static void +fu_dell_dock_ec_set_board(FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + const gchar *summary = NULL; + g_autofree gchar *board_type_str = NULL; + + board_type_str = g_strdup_printf("DellDockBoard%u", self->data->board_id); + summary = fu_device_get_metadata(device, board_type_str); + if (summary != NULL) + fu_device_set_summary(device, summary); +} + +gboolean +fu_dell_dock_module_is_usb4(FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + return self->data->module_type == MODULE_TYPE_130_USB4; +} + +guint8 +fu_dell_dock_get_dock_type(FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + return self->base_type; +} + +const gchar * +fu_dell_dock_ec_get_module_type(FuDevice *device) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + switch (self->data->module_type) { + case MODULE_TYPE_45_TBT: + return "45 (TBT)"; + case MODULE_TYPE_45: + return "45"; + case MODULE_TYPE_130_TBT: + return "130 (TBT)"; + case MODULE_TYPE_130_DP: + return "130 (DP)"; + case MODULE_TYPE_130_UNIVERSAL: + return "130 (Universal)"; + case MODULE_TYPE_240_TRIN: + return "240 (Trinity)"; + case MODULE_TYPE_210_DUAL: + return "210 (Dual)"; + case MODULE_TYPE_130_USB4: + return "130 (TBT4)"; + default: + return "unknown"; + } +} + +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_130_TBT && + self->data->module_type != MODULE_TYPE_45_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; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(fu_device_get_proxy(device) != NULL, FALSE); + g_return_val_if_fail(bytes != NULL, FALSE); + + if (!fu_dell_dock_hid_i2c_read(fu_device_get_proxy(device), + 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) +{ + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(fu_device_get_proxy(device) != NULL, FALSE); + g_return_val_if_fail(length > 1, FALSE); + + if (!fu_dell_dock_hid_i2c_write(fu_device_get_proxy(device), + data, + length, + &ec_base_settings, + error)) { + g_prefix_error(error, "write over HID-I2C failed: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_dell_dock_is_valid_dock(FuDevice *device, GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + const guint8 *result = NULL; + gsize sz = 0; + g_autoptr(GBytes) data = NULL; + + g_return_val_if_fail(device != NULL, FALSE); + + 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, &sz); + if (sz != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No valid dock was found"); + return FALSE; + } + self->base_type = result[0]; + + /* this will trigger setting up all the quirks */ + if (self->base_type == DOCK_BASE_TYPE_SALOMON) { + fu_device_add_instance_id(device, DELL_DOCK_EC_INSTANCE_ID); + return TRUE; + } else if (self->base_type == DOCK_BASE_TYPE_ATOMIC) { + fu_device_add_instance_id(device, DELL_DOCK_ATOMIC_EC_INSTANCE_ID); + return TRUE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid dock type: %x", + self->base_type); + return FALSE; +} + +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_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(FU_DEVICE(self), self->ec_version); + + } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_MST) { + self->raw_versions->mst_version = device_entry[i].version.version_32; + /* 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_130_TBT || + self->data->module_type == MODULE_TYPE_45_TBT || + self->data->module_type == MODULE_TYPE_130_USB4)) { + /* 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_130_TBT || + self->data->module_type == MODULE_TYPE_45_TBT || + self->data->module_type == MODULE_TYPE_130_USB4) { + guint64 tmp = fu_device_get_install_duration(device); + fu_device_set_install_duration(device, tmp + 20); + } + + /* Determine if the passive flow should be used when flashing */ + hub_version = fu_device_get_version(fu_device_get_proxy(device)); + if (fu_version_compare(hub_version, "1.42", FWUPD_VERSION_FORMAT_PAIR) < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "dock containing hub2 version %s is not supported", + hub_version); + return FALSE; + } + self->passive_flow = PASSIVE_REBOOT_MASK; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); + 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_uninhibit(device, "update-pending"); + } else { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + } + } else { + fu_device_inhibit(device, "not-supported", "Utility does not support this board"); + } + + return TRUE; +} + +static void +fu_dell_dock_ec_to_string(FuDevice *device, guint idt, GString *str) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + gchar service_tag[8] = {0x00}; + + fu_string_append_ku(str, idt, "BaseType", self->base_type); + fu_string_append_ku(str, idt, "BoardId", self->data->board_id); + fu_string_append_ku(str, idt, "PowerSupply", self->data->power_supply_wattage); + fu_string_append_kx(str, idt, "StatusPort0", self->data->port0_dock_status); + fu_string_append_kx(str, idt, "StatusPort1", self->data->port1_dock_status); + memcpy(service_tag, self->data->service_tag, 7); + fu_string_append(str, idt, "ServiceTag", service_tag); + fu_string_append_ku(str, idt, "Configuration", self->data->dock_configuration); + fu_string_append_kx(str, idt, "PackageFirmwareVersion", self->data->dock_firmware_pkg_ver); + fu_string_append_ku(str, idt, "ModuleSerial", self->data->module_serial); + fu_string_append_ku(str, idt, "OriginalModuleSerial", self->data->original_module_serial); + fu_string_append_ku(str, idt, "Type", self->data->dock_type); + fu_string_append_kx(str, idt, "ModuleType", self->data->module_type); + fu_string_append(str, idt, "MinimumEc", self->ec_minimum_version); + fu_string_append_ku(str, idt, "PassiveFlow", 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, FuProgress *progress, 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); + guint32 cmd = EC_CMD_PASSIVE | /* cmd */ + 1 << 8 | /* length of data arguments */ + self->passive_flow << 16; + + g_return_val_if_fail(device != NULL, FALSE); + + g_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); +} + +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, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(device); + gsize fw_size = 0; + const guint8 *data; + gsize write_size = 0; + gsize nwritten = 0; + guint32 address = 0 | 0xff << 24; + g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 15, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 85, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data(fw, &fw_size); + write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; + dynamic_version = g_strndup((gchar *)data + self->blob_version_offset, 11); + g_debug("writing EC firmware version %s", dynamic_version); + + /* meet the minimum EC version */ + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + (fu_version_compare(dynamic_version, + self->ec_minimum_version, + FWUPD_VERSION_FORMAT_QUAD) < 0)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "New EC version %s is less than minimum required %s", + dynamic_version, + self->ec_minimum_version); + return FALSE; + } + + 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(fu_device_get_proxy(device), TRUE, error)) + return FALSE; + + /* erase */ + if (!fu_dell_dock_hid_erase_bank(fu_device_get_proxy(device), 0xff, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + do { + /* last packet */ + if (fw_size - nwritten < write_size) + write_size = fw_size - nwritten; + + if (!fu_dell_dock_hid_write_flash(fu_device_get_proxy(device), + address, + data, + write_size, + error)) { + g_prefix_error(error, "write over HID failed: "); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), nwritten, fw_size); + nwritten += write_size; + data += write_size; + address += write_size; + } while (nwritten < fw_size); + fu_progress_step_done(progress); + + if (!fu_dell_dock_hid_raise_mcu_clock(fu_device_get_proxy(device), FALSE, error)) + return FALSE; + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(device, dynamic_version); + + /* activate passive behavior */ + self->passive_flow |= PASSIVE_RESET_MASK; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + 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); + guint64 tmp = 0; + + if (g_strcmp0(key, "DellDockUnlockTarget") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->unlock_target = tmp; + return TRUE; + } + if (g_strcmp0(key, "DellDockBoardMin") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->board_min = tmp; + return TRUE; + } + 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) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_version_offset = 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_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(fu_device_get_proxy(device), error)) + return FALSE; + if (!self->data->dock_type) + return fu_dell_dock_is_valid_dock(device, error); + return TRUE; +} + +static gboolean +fu_dell_dock_ec_close(FuDevice *device, GError **error) +{ + return fu_device_close(fu_device_get_proxy(device), error); +} + +static void +fu_dell_dock_ec_finalize(GObject *object) +{ + FuDellDockEc *self = FU_DELL_DOCK_EC(object); + 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_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_dell_dock_ec_init(FuDellDockEc *self) +{ + self->data = g_new0(FuDellDockDockDataStructure, 1); + self->raw_versions = g_new0(FuDellDockDockPackageFWVersion, 1); + fu_device_add_protocol(FU_DEVICE(self), "com.dell.dock"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN); +} + +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->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; + klass_device->set_progress = fu_dell_dock_ec_set_progress; +} + +FuDellDockEc * +fu_dell_dock_ec_new(FuDevice *proxy) +{ + FuDellDockEc *self = NULL; + FuContext *ctx = fu_device_get_context(proxy); + + self = g_object_new(FU_TYPE_DELL_DOCK_EC, "context", ctx, NULL); + fu_device_set_proxy(FU_DEVICE(self), proxy); + fu_device_set_physical_id(FU_DEVICE(self), fu_device_get_physical_id(proxy)); + fu_device_set_logical_id(FU_DEVICE(self), "ec"); + + return self; +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-ec.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-ec.h new file mode 100644 index 0000000000000000000000000000000000000000..c6c3267bcee873a50b20c506c80bf65097393f22 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-ec.h @@ -0,0 +1,51 @@ +/* + * 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 + +#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 *proxy); + +const gchar * +fu_dell_dock_ec_get_module_type(FuDevice *device); +gboolean +fu_dell_dock_ec_needs_tbt(FuDevice *device); +gboolean +fu_dell_dock_ec_tbt_passive(FuDevice *device); +gboolean +fu_dell_dock_ec_modify_lock(FuDevice *self, guint8 target, gboolean unlocked, 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); +gboolean +fu_dell_dock_module_is_usb4(FuDevice *device); +guint8 +fu_dell_dock_get_dock_type(FuDevice *device); diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-mst.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-mst.c new file mode 100644 index 0000000000000000000000000000000000000000..7345ca4c03adbf065fc9561bb885074471b62d80 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-mst.c @@ -0,0 +1,1260 @@ +/* + * 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 + +#include "fu-dell-dock-common.h" + +#define I2C_MST_ADDRESS 0x72 + +/* Panamera MST registers */ +#define PANAMERA_MST_RC_TRIGGER_ADDR 0x2000fc +#define PANAMERA_MST_CORE_MCU_BOOTLOADER_STS 0x20010c +#define PANAMERA_MST_RC_COMMAND_ADDR 0x200110 +#define PANAMERA_MST_RC_OFFSET_ADDR 0x200114 +#define PANAMERA_MST_RC_LENGTH_ADDR 0x200118 +#define PANAMERA_MST_RC_DATA_ADDR 0x200120 +#define PANAMERA_MST_CORE_MCU_FW_VERSION 0x200160 +#define PANAMERA_MST_REG_QUAD_DISABLE 0x200fc0 +#define PANAMERA_MST_REG_HDCP22_DISABLE 0x200f90 + +/* Cayenne MST registers */ +#define CAYENNE_MST_RC_TRIGGER_ADDR 0x2020021C +#define CAYENNE_MST_CORE_MCU_BOOTLOADER_STS 0x2020022C +#define CAYENNE_MST_RC_COMMAND_ADDR 0x20200280 +#define CAYENNE_MST_RC_OFFSET_ADDR 0x20200284 +#define CAYENNE_MST_RC_LENGTH_ADDR 0x20200288 +#define CAYENNE_MST_RC_DATA_ADDR 0x20200290 + +/* MST remote control commands */ +#define MST_CMD_ENABLE_REMOTE_CONTROL 0x1 +#define MST_CMD_DISABLE_REMOTE_CONTROL 0x2 +#define MST_CMD_CHECKSUM 0x11 +#define MST_CMD_ERASE_FLASH 0x14 +#define MST_CMD_WRITE_FLASH 0x20 +#define MST_CMD_READ_FLASH 0x30 +#define MST_CMD_WRITE_MEMORY 0x21 +#define MST_CMD_READ_MEMORY 0x31 + +/* Cayenne specific remote control commands */ +#define MST_CMD_CRC16_CHECKSUM 0x17 +#define MST_CMD_ACTIVATE_FW 0x18 + +/* Arguments related to flashing */ +#define FLASH_SECTOR_ERASE_4K 0x1000 +#define FLASH_SECTOR_ERASE_32K 0x2000 +#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 { + Panamera_mst, + Cayenne_mst, + Unknown, +} MSTType; + +typedef enum { + Bank0, + Bank1, + ESM, + Cayenne, +} MSTBank; + +typedef struct { + guint start; + guint length; + guint checksum_cmd; +} MSTBankAttributes; + +const MSTBankAttributes bank0_attributes = { + .start = 0, + .length = EEPROM_BANK_OFFSET, + .checksum_cmd = MST_CMD_CHECKSUM, +}; + +const MSTBankAttributes bank1_attributes = { + .start = EEPROM_BANK_OFFSET, + .length = EEPROM_BANK_OFFSET, + .checksum_cmd = MST_CMD_CHECKSUM, +}; + +const MSTBankAttributes esm_attributes = { + .start = EEPROM_ESM_OFFSET, + .length = 0x3ffff, + .checksum_cmd = MST_CMD_CHECKSUM, +}; + +const MSTBankAttributes cayenne_attributes = { + .start = 0, + .length = 0x50000, + .checksum_cmd = MST_CMD_CRC16_CHECKSUM, +}; + +FuHIDI2CParameters mst_base_settings = { + .i2ctargetaddr = I2C_MST_ADDRESS, + .regaddrlen = 0, + .i2cspeed = I2C_SPEED_400K, +}; + +struct _FuDellDockMst { + FuDevice parent_instance; + guint8 unlock_target; + guint64 blob_major_offset; + guint64 blob_minor_offset; + guint64 blob_build_offset; + guint32 mst_rc_trigger_addr; + guint32 mst_rc_command_addr; + guint32 mst_rc_data_addr; + guint32 mst_core_mcu_bootloader_addr; +}; + +G_DEFINE_TYPE(FuDellDockMst, fu_dell_dock_mst, FU_TYPE_DEVICE) + +/** + * fu_dell_dock_mst_get_bank_attribs: + * @bank: the MSTBank + * @out (out): the MSTBankAttributes attribute that matches + * @error: (nullable): optional return location for an error + * + * 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; + case Cayenne: + *out = &cayenne_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 *device, + guint8 cmd, + guint32 length, + guint32 offset, + const guint8 *data, + GError **error); + +static gboolean +fu_dell_dock_mst_read_register(FuDevice *proxy, + guint32 address, + gsize length, + GBytes **bytes, + GError **error) +{ + g_return_val_if_fail(proxy != 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(proxy, (guint8 *)&address, 4, &mst_base_settings, error)) + return FALSE; + + /* read data for the result */ + if (!fu_dell_dock_hid_i2c_read(proxy, 0, length, bytes, &mst_base_settings, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_write_register(FuDevice *proxy, + guint32 address, + guint8 *data, + gsize length, + GError **error) +{ + g_autofree guint8 *buffer = g_malloc0(length + 4); + + g_return_val_if_fail(proxy != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + + memcpy(buffer, &address, 4); + memcpy(buffer + 4, data, length); + + /* write the offset we're querying */ + return fu_dell_dock_hid_i2c_write(proxy, buffer, length + 4, &mst_base_settings, error); +} + +static gboolean +fu_dell_dock_mst_query_active_bank(FuDevice *proxy, MSTBank *active, GError **error) +{ + g_autoptr(GBytes) bytes = NULL; + const guint32 *data = NULL; + gsize length = 4; + + if (!fu_dell_dock_mst_read_register(proxy, + PANAMERA_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 *device, GError **error) +{ + g_debug("MST: Disabling remote control"); + return fu_dell_dock_mst_rc_command(device, + MST_CMD_DISABLE_REMOTE_CONTROL, + 0, + 0, + NULL, + error); +} + +static gboolean +fu_dell_dock_mst_enable_remote_control(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + const gchar *data = "PRIUS"; + + g_debug("MST: Enabling remote control"); + if (!fu_dell_dock_mst_rc_command(device, + MST_CMD_ENABLE_REMOTE_CONTROL, + 5, + 0, + (guint8 *)data, + &error_local)) { + g_debug("Failed to enable remote control: %s", error_local->message); + /* try to disable / re-enable */ + if (!fu_dell_dock_mst_disable_remote_control(device, error)) + return FALSE; + return fu_dell_dock_mst_enable_remote_control(device, error); + } + return TRUE; +} + +static gboolean +fu_dell_dock_trigger_rc_command(FuDevice *device, GError **error) +{ + const guint8 *result = NULL; + FuDevice *proxy = fu_device_get_proxy(device); + FuDellDockMst *self = FU_DELL_DOCK_MST(device); + guint32 tmp; + + /* Trigger the write */ + tmp = MST_TRIGGER_WRITE; + if (!fu_dell_dock_mst_write_register(proxy, + self->mst_rc_trigger_addr, + (guint8 *)&tmp, + sizeof(guint32), + error)) { + g_prefix_error(error, "Failed to write MST_RC_TRIGGER_ADDR: "); + 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(proxy, + self->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(device, 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 *device, + guint8 cmd, + guint32 length, + guint32 offset, + const guint8 *data, + GError **error) +{ + /* 4 for cmd, 4 for offset, 4 for length, 4 for garbage */ + FuDellDockMst *self = FU_DELL_DOCK_MST(device); + FuDevice *proxy = fu_device_get_proxy(device); + gint buffer_len = (data == NULL) ? 12 : length + 16; + g_autofree guint8 *buffer = g_malloc0(buffer_len); + guint32 tmp; + + g_return_val_if_fail(proxy != NULL, FALSE); + + /* command */ + tmp = (cmd | 0x80) << 16; + memcpy(buffer, &tmp, 4); + /* 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(proxy, + self->mst_rc_command_addr, + buffer, + buffer_len, + error)) + return FALSE; + + return fu_dell_dock_trigger_rc_command(device, error); +} + +static MSTType +fu_dell_dock_mst_check_type(FuDevice *device) +{ + GPtrArray *instance_ids; + const gchar *tmp = NULL; + + instance_ids = fu_device_get_instance_ids(device); + for (guint i = 0; i < instance_ids->len; i++) { + tmp = g_ptr_array_index(instance_ids, i); + if (g_strcmp0(tmp, DELL_DOCK_VMM6210_INSTANCE_ID) == 0) + return Cayenne_mst; + else if (g_strcmp0(tmp, DELL_DOCK_VM5331_INSTANCE_ID) == 0) + return Panamera_mst; + } + return Unknown; +} + +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 *device, GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST(device); + g_autoptr(GBytes) bytes = NULL; + const guint8 *data; + gsize length = 4; + + if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + self->mst_core_mcu_bootloader_addr, + 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 guint16 +fu_dell_dock_mst_get_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, 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, + 0x8077, 0x0072, 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, 0x80c3, + 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, 0x00f0, 0x80f5, 0x80ff, 0x00fa, + 0x80eb, 0x00ee, 0x00e4, 0x80e1, 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, + 0x80b1, 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, 0x8183, 0x0186, + 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, + 0x01ae, 0x01a4, 0x81a1, 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, 0x0140, 0x8145, 0x814f, + 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, + 0x8167, 0x0162, 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, 0x0110, + 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, 0x8303, 0x0306, 0x030c, 0x8309, + 0x0318, 0x831d, 0x8317, 0x0312, 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, + 0x8321, 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, 0x8353, 0x0356, + 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, + 0x03de, 0x03d4, 0x83d1, 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, 0x0390, 0x8395, 0x839f, + 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, + 0x0294, 0x8291, 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, 0x82e3, + 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, 0x02d0, 0x82d5, 0x82df, 0x02da, + 0x82cb, 0x02ce, 0x02c4, 0x82c1, 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, + 0x0252, 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, 0x0220, 0x8225, + 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, + 0x820d, 0x8207, 0x0202}; + static const guint16 CRC8_table[] = { + 0x00, 0xd5, 0x7f, 0xaa, 0xfe, 0x2b, 0x81, 0x54, 0x29, 0xfc, 0x56, 0x83, 0xd7, 0x02, + 0xa8, 0x7d, 0x52, 0x87, 0x2d, 0xf8, 0xac, 0x79, 0xd3, 0x06, 0x7b, 0xae, 0x04, 0xd1, + 0x85, 0x50, 0xfa, 0x2f, 0xa4, 0x71, 0xdb, 0x0e, 0x5a, 0x8f, 0x25, 0xf0, 0x8d, 0x58, + 0xf2, 0x27, 0x73, 0xa6, 0x0c, 0xd9, 0xf6, 0x23, 0x89, 0x5c, 0x08, 0xdd, 0x77, 0xa2, + 0xdf, 0x0a, 0xa0, 0x75, 0x21, 0xf4, 0x5e, 0x8b, 0x9d, 0x48, 0xe2, 0x37, 0x63, 0xb6, + 0x1c, 0xc9, 0xb4, 0x61, 0xcb, 0x1e, 0x4a, 0x9f, 0x35, 0xe0, 0xcf, 0x1a, 0xb0, 0x65, + 0x31, 0xe4, 0x4e, 0x9b, 0xe6, 0x33, 0x99, 0x4c, 0x18, 0xcd, 0x67, 0xb2, 0x39, 0xec, + 0x46, 0x93, 0xc7, 0x12, 0xb8, 0x6d, 0x10, 0xc5, 0x6f, 0xba, 0xee, 0x3b, 0x91, 0x44, + 0x6b, 0xbe, 0x14, 0xc1, 0x95, 0x40, 0xea, 0x3f, 0x42, 0x97, 0x3d, 0xe8, 0xbc, 0x69, + 0xc3, 0x16, 0xef, 0x3a, 0x90, 0x45, 0x11, 0xc4, 0x6e, 0xbb, 0xc6, 0x13, 0xb9, 0x6c, + 0x38, 0xed, 0x47, 0x92, 0xbd, 0x68, 0xc2, 0x17, 0x43, 0x96, 0x3c, 0xe9, 0x94, 0x41, + 0xeb, 0x3e, 0x6a, 0xbf, 0x15, 0xc0, 0x4b, 0x9e, 0x34, 0xe1, 0xb5, 0x60, 0xca, 0x1f, + 0x62, 0xb7, 0x1d, 0xc8, 0x9c, 0x49, 0xe3, 0x36, 0x19, 0xcc, 0x66, 0xb3, 0xe7, 0x32, + 0x98, 0x4d, 0x30, 0xe5, 0x4f, 0x9a, 0xce, 0x1b, 0xb1, 0x64, 0x72, 0xa7, 0x0d, 0xd8, + 0x8c, 0x59, 0xf3, 0x26, 0x5b, 0x8e, 0x24, 0xf1, 0xa5, 0x70, 0xda, 0x0f, 0x20, 0xf5, + 0x5f, 0x8a, 0xde, 0x0b, 0xa1, 0x74, 0x09, 0xdc, 0x76, 0xa3, 0xf7, 0x22, 0x88, 0x5d, + 0xd6, 0x03, 0xa9, 0x7c, 0x28, 0xfd, 0x57, 0x82, 0xff, 0x2a, 0x80, 0x55, 0x01, 0xd4, + 0x7e, 0xab, 0x84, 0x51, 0xfb, 0x2e, 0x7a, 0xaf, 0x05, 0xd0, 0xad, 0x78, 0xd2, 0x07, + 0x53, 0x86, 0x2c, 0xf9}; + guint8 val; + guint16 crc = 0; + const guint8 *message = payload_data; + + if (type == 8) { + for (guint32 byte = 0; byte < length; ++byte) { + val = (guint8)(message[byte] ^ crc); + crc = CRC8_table[val]; + } + } else { + for (guint32 byte = 0; byte < length; ++byte) { + val = (guint8)(message[byte] ^ (crc >> 8)); + crc = CRC16_table[val] ^ (crc << 8); + } + } + + return crc; +} + +static gboolean +fu_dell_dock_mst_checksum_bank(FuDevice *device, + GBytes *blob_fw, + MSTBank bank, + gboolean *checksum, + GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST(device); + 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 */ + if (attribs->checksum_cmd == MST_CMD_CRC16_CHECKSUM) + payload_sum = + fu_dell_dock_mst_get_crc(16, (attribs->length + attribs->start), data); + else { + 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(device, + attribs->checksum_cmd, + 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(fu_device_get_proxy(device), + self->mst_rc_data_addr, + 4, + &csum_bytes, + error)) + return FALSE; + data = g_bytes_get_data(csum_bytes, NULL); + bank_sum = GUINT32_FROM_LE(data[0] | data[1] << 8 | data[2] << 16 | 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_panamera_bank(FuDevice *device, 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(device, + 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_mst_erase_cayenne(FuDevice *device, GError **error) +{ + guint8 data[4] = {0, 0x30, 0, 0}; + + for (guint8 i = 0; i < 5; i++) { + data[0] = i; + if (!fu_dell_dock_mst_rc_command(device, + MST_CMD_ERASE_FLASH, + 4, + 0, + (guint8 *)&data, + error)) { + g_prefix_error(error, "Failed to erase sector: %d", i); + 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, + FuProgress *progress, + GError **error) +{ + 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(device, + 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_progress_set_percentage_full(progress, i - attribs->start, end - attribs->start); + } + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_stop_esm(FuDevice *device, 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(device, + MST_CMD_WRITE_MEMORY, + length, + PANAMERA_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(device, + MST_CMD_READ_MEMORY, + length, + PANAMERA_MST_REG_QUAD_DISABLE, + NULL, + error)) + return FALSE; + + if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + PANAMERA_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(device, + MST_CMD_WRITE_MEMORY, + length, + PANAMERA_MST_REG_QUAD_DISABLE, + data_out, + error)) + return FALSE; + + /* disable HDCP2.2 */ + if (!fu_dell_dock_mst_rc_command(device, + MST_CMD_READ_MEMORY, + length, + PANAMERA_MST_REG_HDCP22_DISABLE, + NULL, + error)) + return FALSE; + + if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + PANAMERA_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(device, + MST_CMD_WRITE_MEMORY, + length, + PANAMERA_MST_REG_HDCP22_DISABLE, + data_out, + error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_invalidate_bank(FuDevice *device, 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(device, 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(fu_device_get_proxy(device), + PANAMERA_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(device, + 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(device, + 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(device, + 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(fu_device_get_proxy(device), + PANAMERA_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_bank(FuDevice *device, + GBytes *fw, + guint8 bank, + FuProgress *progress, + GError **error) +{ + const guint retries = 2; + for (guint i = 0; i < retries; i++) { + gboolean checksum = FALSE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 15, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 84, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + + if (!fu_dell_dock_mst_erase_panamera_bank(device, bank, error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_dell_dock_write_flash_bank(device, + fw, + bank, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_dell_dock_mst_checksum_bank(device, fw, bank, &checksum, error)) + return FALSE; + if (!checksum) { + g_debug("MST: Failed to verify checksum on bank %u", bank); + fu_progress_reset(progress); + continue; + } + fu_progress_step_done(progress); + + g_debug("MST: Bank %u successfully flashed", bank); + return TRUE; + } + + /* failed after all our retries */ + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to write to bank %u", bank); + return FALSE; +} + +static FuProgress * +fu_dell_dock_mst_set_local_progress(FuProgress *progress, guint steps) +{ + FuProgress *progress_local; + progress_local = fu_progress_get_child(progress); + fu_progress_set_id(progress_local, G_STRLOC); + fu_progress_set_steps(progress_local, steps); + + return progress_local; +} + +static gboolean +fu_dell_dock_mst_write_panamera(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + FuProgress *progress, + GError **error) +{ + gboolean checksum = FALSE; + MSTBank bank_in_use = 0; + guint8 order[2] = {ESM, Bank0}; + FuProgress *progress_local; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "stop-esm"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + /* determine the flash order */ + if (!fu_dell_dock_mst_query_active_bank(fu_device_get_proxy(device), &bank_in_use, error)) + return FALSE; + + if (bank_in_use == Bank0) + order[1] = Bank1; + /* ESM needs special handling during flash process*/ + if (!fu_dell_dock_mst_stop_esm(device, error)) + return FALSE; + fu_progress_step_done(progress); + + progress_local = fu_dell_dock_mst_set_local_progress(progress, 2); + /* Write each bank in order */ + for (guint phase = 0; phase < 2; phase++) { + g_debug("MST: Checking bank %u", order[phase]); + if (!fu_dell_dock_mst_checksum_bank(device, fw, order[phase], &checksum, error)) + return FALSE; + if (checksum) { + g_debug("MST: bank %u is already up to date", order[phase]); + fu_progress_step_done(progress_local); + continue; + } + g_debug("MST: bank %u needs to be updated", order[phase]); + if (!fu_dell_dock_mst_write_bank(device, + fw, + order[phase], + fu_progress_get_child(progress_local), + error)) + return FALSE; + fu_progress_step_done(progress_local); + } + /* invalidate the previous bank */ + if (!fu_dell_dock_mst_invalidate_bank(device, bank_in_use, error)) { + g_prefix_error(error, "failed to invalidate bank %u: ", bank_in_use); + return FALSE; + } + fu_progress_step_done(progress); + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_write_cayenne(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + FuProgress *progress, + GError **error) +{ + gboolean checksum = FALSE; + guint retries = 2; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 3, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, NULL); + + for (guint i = 0; i < retries; i++) { + if (!fu_dell_dock_mst_erase_cayenne(device, error)) + return FALSE; + fu_progress_step_done(progress); + if (!fu_dell_dock_write_flash_bank(device, + fw, + Cayenne, + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_dell_dock_mst_checksum_bank(device, fw, Cayenne, &checksum, error)) + return FALSE; + fu_progress_step_done(progress); + if (!checksum) { + g_debug("MST: Failed to verify checksum"); + fu_progress_reset(progress); + continue; + } + break; + } + /* failed after all our retries */ + if (!checksum) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to write to bank"); + return FALSE; + } + /* activate the FW */ + if (!fu_dell_dock_mst_rc_command(device, + MST_CMD_ACTIVATE_FW, + g_bytes_get_size(fw), + 0x0, + NULL, + error)) { + g_prefix_error(error, "Failed to activate FW: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_dell_dock_mst_write_fw(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockMst *self = FU_DELL_DOCK_MST(device); + const guint8 *data; + g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; + MSTType type; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + g_return_val_if_fail(fu_device_get_proxy(device) != NULL, FALSE); + + /* open the hub*/ + if (!fu_device_open(fu_device_get_proxy(device), error)) + return FALSE; + + /* open up access to controller bus */ + if (!fu_dell_dock_set_power(device, self->unlock_target, TRUE, error)) + return FALSE; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data(fw, NULL); + + dynamic_version = g_strdup_printf("%02x.%02x.%02x", + data[self->blob_major_offset], + data[self->blob_minor_offset], + data[self->blob_build_offset]); + g_debug("writing MST firmware version %s", dynamic_version); + + /* enable remote control */ + if (!fu_dell_dock_mst_enable_remote_control(device, error)) + return FALSE; + + type = fu_dell_dock_mst_check_type(device); + if (type == Panamera_mst) { + if (!fu_dell_dock_mst_write_panamera(device, fw, flags, progress, error)) + return FALSE; + } else if (type == Cayenne_mst) { + if (!fu_dell_dock_mst_write_cayenne(device, fw, flags, progress, error)) + return FALSE; + } else { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Unknown mst found"); + return FALSE; + } + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, dynamic_version); + + /* disable remote control now */ + return fu_dell_dock_mst_disable_remote_control(device, 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); + guint64 tmp = 0; + + if (g_strcmp0(key, "DellDockUnlockTarget") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->unlock_target = tmp; + return TRUE; + } + if (g_strcmp0(key, "DellDockBlobMajorOffset") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_major_offset = tmp; + return TRUE; + } + if (g_strcmp0(key, "DellDockBlobMinorOffset") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_minor_offset = tmp; + return TRUE; + } + if (g_strcmp0(key, "DellDockBlobBuildOffset") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_build_offset = tmp; + return TRUE; + } else if (g_strcmp0(key, "DellDockInstallDurationI2C") == 0) { + if (!fu_strtoull(value, &tmp, 0, 60 * 60 * 24, error)) + return FALSE; + 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) +{ + FuDevice *parent; + const gchar *version; + + /* sanity check that we can talk to MST */ + if (!fu_d19_mst_check_fw(device, 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_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, version); + } + + return TRUE; +} + +static gboolean +fu_dell_dock_mst_probe(FuDevice *device, GError **error) +{ + MSTType type; + FuDellDockMst *self = FU_DELL_DOCK_MST(device); + + fu_device_set_logical_id(FU_DEVICE(device), "mst"); + + /* confige mst register via instance id*/ + type = fu_dell_dock_mst_check_type(device); + switch (type) { + case Cayenne_mst: + self->mst_rc_trigger_addr = CAYENNE_MST_RC_TRIGGER_ADDR; + self->mst_rc_command_addr = CAYENNE_MST_RC_COMMAND_ADDR; + self->mst_rc_data_addr = CAYENNE_MST_RC_DATA_ADDR; + self->mst_core_mcu_bootloader_addr = CAYENNE_MST_CORE_MCU_BOOTLOADER_STS; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + return TRUE; + case Panamera_mst: + self->mst_rc_trigger_addr = PANAMERA_MST_RC_TRIGGER_ADDR; + self->mst_rc_command_addr = PANAMERA_MST_RC_COMMAND_ADDR; + self->mst_rc_data_addr = PANAMERA_MST_RC_DATA_ADDR; + self->mst_core_mcu_bootloader_addr = PANAMERA_MST_CORE_MCU_BOOTLOADER_STS; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + return TRUE; + case Unknown: + default: + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Unknown mst found"); + return FALSE; + } + 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 (fu_device_get_proxy(device) == NULL) + fu_device_set_proxy(device, fu_device_get_proxy(parent)); + + if (!fu_device_open(fu_device_get_proxy(device), 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(fu_device_get_proxy(device), error); +} + +static void +fu_dell_dock_mst_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_dell_dock_mst_init(FuDellDockMst *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.mst"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_dell_dock_mst_class_init(FuDellDockMstClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + 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->write_firmware = fu_dell_dock_mst_write_fw; + klass_device->set_quirk_kv = fu_dell_dock_mst_set_quirk_kv; + klass_device->set_progress = fu_dell_dock_mst_set_progress; +} + +FuDellDockMst * +fu_dell_dock_mst_new(FuContext *ctx) +{ + FuDellDockMst *device = NULL; + device = g_object_new(FU_TYPE_DELL_DOCK_MST, "context", ctx, NULL); + return device; +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-mst.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-mst.h new file mode 100644 index 0000000000000000000000000000000000000000..90b1e7517a9ff91b1fd9466616c775525a783058 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-mst.h @@ -0,0 +1,26 @@ +/* + * 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 + +#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(FuContext *ctx); diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-tbt.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-tbt.c new file mode 100644 index 0000000000000000000000000000000000000000..9001dd12b6aeb5c0a6e8321eb4aff3ac1ff8a5bb --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-tbt.c @@ -0,0 +1,300 @@ +/* + * 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 + +#include "fu-dell-dock-common.h" + +#define I2C_TBT_ADDRESS 0xa2 + +const FuHIDI2CParameters tbt_base_settings = { + .i2ctargetaddr = 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; + 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, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + guint32 start_offset = 0; + gsize image_size = 0; + const guint8 *buffer; + guint16 target_system = 0; + g_autoptr(GTimer) timer = g_timer_new(); + g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + buffer = g_bytes_get_data(fw, &image_size); + + dynamic_version = g_strdup_printf("%02x.%02x", + buffer[self->blob_major_offset], + 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(fu_device_get_proxy(device), &tbt_base_settings, error)) + return FALSE; + g_usleep(2000000); + + fu_progress_set_status(progress, 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(fu_device_get_proxy(device), + i, + buffer, + write_size, + &tbt_base_settings, + error)) + return FALSE; + + fu_progress_set_percentage_full(progress, i, image_size); + } + g_debug("writing took %f seconds", g_timer_elapsed(timer, NULL)); + + fu_progress_set_status(progress, 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(fu_device_get_proxy(device), + &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_format(device, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device, dynamic_version); + + return TRUE; +} + +static gboolean +fu_dell_dock_tbt_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "DellDockUnlockTarget") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->unlock_target = tmp; + return TRUE; + } else if (g_strcmp0(key, "DellDockInstallDurationI2C") == 0) { + if (!fu_strtoull(value, &tmp, 0, 60 * 60 * 24, error)) + return FALSE; + 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) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_major_offset = tmp; + return TRUE; + } else if (g_strcmp0(key, "DellDockBlobMinorOffset") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_minor_offset = 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_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_format(device, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device, version); + } + + /* minimum version of NVM that supports this feature */ + if (version == NULL || + fu_version_compare(version, MIN_NVM, FWUPD_VERSION_FORMAT_PAIR) < 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(fu_device_get_proxy(device)); + if (fu_version_compare(hub_version, self->hub_minimum_version, FWUPD_VERSION_FORMAT_PAIR) < + 0) { + fu_device_set_update_error( + device, + "Updates over I2C are disabled due to insufficient USB 3.1 G2 hub version"); + return TRUE; + } + + 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); + /* this is true only when connected to non-thunderbolt port */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + + return TRUE; +} + +static gboolean +fu_dell_dock_tbt_open(FuDevice *device, GError **error) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + + g_return_val_if_fail(self->unlock_target != 0, FALSE); + + if (!fu_device_open(fu_device_get_proxy(device), 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(fu_device_get_proxy(device), error); +} + +static void +fu_dell_dock_tbt_finalize(GObject *object) +{ + FuDellDockTbt *self = FU_DELL_DOCK_TBT(object); + 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 *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.intel.thunderbolt"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); +} + +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(FuDevice *proxy) +{ + FuContext *ctx = fu_device_get_context(proxy); + FuDellDockTbt *self = g_object_new(FU_TYPE_DELL_DOCK_TBT, "context", ctx, NULL); + fu_device_set_proxy(FU_DEVICE(self), proxy); + return self; +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-tbt.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-tbt.h new file mode 100644 index 0000000000000000000000000000000000000000..953871ac469ac591b270dc8af0da4d7b40f1382c --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-i2c-tbt.h @@ -0,0 +1,27 @@ +/* + * 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 + +#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(FuDevice *proxy); diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-plugin.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..5166d5032f0330e0c52d4488ce7c0a69257c8ca4 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-plugin.c @@ -0,0 +1,353 @@ +/* + * 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-dell-dock-plugin.h" + +struct _FuDellDockPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuDellDockPlugin, fu_dell_dock_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_dell_dock_plugin_create_node(FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + fu_plugin_device_add(plugin, device); + + return TRUE; +} + +static gboolean +fu_dell_dock_plugin_probe(FuPlugin *plugin, FuDevice *proxy, GError **error) +{ + const gchar *instance_id_mst; + const gchar *instance_id_status; + g_autofree const gchar *instance_guid_mst = NULL; + g_autofree const gchar *instance_guid_status = NULL; + g_autoptr(FuDellDockEc) ec_device = NULL; + g_autoptr(FuDellDockMst) mst_device = NULL; + g_autoptr(FuDellDockStatus) status_device = NULL; + FuContext *ctx = fu_plugin_get_context(plugin); + + /* create ec endpoint */ + ec_device = fu_dell_dock_ec_new(proxy); + if (!fu_dell_dock_plugin_create_node(plugin, FU_DEVICE(ec_device), error)) + return FALSE; + + /* create mst endpoint */ + mst_device = fu_dell_dock_mst_new(ctx); + if (fu_dell_dock_get_dock_type(FU_DEVICE(ec_device)) == DOCK_BASE_TYPE_ATOMIC) + instance_id_mst = DELL_DOCK_VMM6210_INSTANCE_ID; + else + instance_id_mst = DELL_DOCK_VM5331_INSTANCE_ID; + fu_device_add_instance_id(FU_DEVICE(mst_device), instance_id_mst); + instance_guid_mst = fwupd_guid_hash_string(instance_id_mst); + fu_device_add_guid(FU_DEVICE(mst_device), instance_guid_mst); + if (!fu_device_probe(FU_DEVICE(mst_device), error)) + return FALSE; + fu_device_add_child(FU_DEVICE(ec_device), FU_DEVICE(mst_device)); + if (!fu_dell_dock_plugin_create_node(plugin, FU_DEVICE(mst_device), error)) + return FALSE; + + /* create package version endpoint */ + status_device = fu_dell_dock_status_new(ctx); + if (fu_dell_dock_get_dock_type(FU_DEVICE(ec_device)) == DOCK_BASE_TYPE_ATOMIC) + instance_id_status = DELL_DOCK_ATOMIC_STATUS_INSTANCE_ID; + else if (fu_dell_dock_module_is_usb4(FU_DEVICE(ec_device))) + instance_id_status = DELL_DOCK_DOCK2_INSTANCE_ID; + else + instance_id_status = DELL_DOCK_DOCK1_INSTANCE_ID; + instance_guid_status = fwupd_guid_hash_string(instance_id_status); + fu_device_add_guid(FU_DEVICE(status_device), fwupd_guid_hash_string(instance_guid_status)); + fu_device_add_child(FU_DEVICE(ec_device), FU_DEVICE(status_device)); + fu_device_add_instance_id(FU_DEVICE(status_device), instance_id_status); + if (!fu_dell_dock_plugin_create_node(plugin, FU_DEVICE(status_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(proxy); + g_autofree const gchar *instance_guid_tbt = + fwupd_guid_hash_string(DELL_DOCK_TBT_INSTANCE_ID); + fu_device_add_guid(FU_DEVICE(tbt_device), instance_guid_tbt); + fu_device_add_child(FU_DEVICE(ec_device), FU_DEVICE(tbt_device)); + if (!fu_dell_dock_plugin_create_node(plugin, FU_DEVICE(tbt_device), error)) + return FALSE; + } + + return TRUE; +} + +/* prefer to use EC if in the transaction and parent if it is not */ +static FuDevice * +fu_dell_dock_plugin_get_ec(GPtrArray *devices) +{ + FuDevice *ec_parent = NULL; + for (gint i = devices->len - 1; i >= 0; i--) { + FuDevice *dev = g_ptr_array_index(devices, i); + FuDevice *parent; + if (FU_IS_DELL_DOCK_EC(dev)) + return dev; + parent = fu_device_get_parent(dev); + if (parent != NULL && FU_IS_DELL_DOCK_EC(parent)) + ec_parent = parent; + } + + return ec_parent; +} + +static gboolean +fu_dell_dock_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDellDockHub) hub = NULL; + const gchar *hub_cache_key = "hub-usb3-gen1"; + GPtrArray *devices; + FuDevice *ec_device; + FuDevice *hub_dev; + guint8 dock_type; + + /* not interesting */ + if (!FU_IS_USB_DEVICE(device)) + return TRUE; + + hub = fu_dell_dock_hub_new(FU_USB_DEVICE(device)); + locker = fu_device_locker_new(FU_DEVICE(hub), error); + if (locker == NULL) + return FALSE; + + /* probe extend devices under Usb3.1 Gen 2 Hub */ + if (fu_device_has_private_flag(FU_DEVICE(hub), FU_DELL_DOCK_HUB_FLAG_HAS_BRIDGE)) { + if (!fu_dell_dock_plugin_probe(plugin, FU_DEVICE(hub), error)) + return FALSE; + } + + /* process hub devices if ec device is added */ + devices = fu_plugin_get_devices(plugin); + ec_device = fu_dell_dock_plugin_get_ec(devices); + if (ec_device == NULL) { + fu_plugin_cache_add(plugin, hub_cache_key, FU_DEVICE(hub)); + return TRUE; + } + + /* determine dock type by ec */ + dock_type = fu_dell_dock_get_dock_type(ec_device); + if (dock_type == DOCK_BASE_TYPE_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "can't read base dock type from EC"); + return FALSE; + } + fu_dell_dock_hub_add_instance(FU_DEVICE(hub), dock_type); + fu_plugin_device_add(plugin, FU_DEVICE(hub)); + + /* add hub instance id for the cached device */ + hub_dev = fu_plugin_cache_lookup(plugin, hub_cache_key); + if (hub_dev != NULL) { + fu_dell_dock_hub_add_instance(FU_DEVICE(hub_dev), dock_type); + fu_plugin_device_add(plugin, FU_DEVICE(hub_dev)); + fu_plugin_cache_remove(plugin, hub_cache_key); + } + return TRUE; +} + +static void +fu_dell_dock_plugin_separate_activation(FuPlugin *plugin) +{ + FuDevice *device_ec = fu_plugin_cache_lookup(plugin, "ec"); + FuDevice *device_usb4 = fu_plugin_cache_lookup(plugin, "usb4"); + + /* both usb4 and ec device are found */ + if (device_usb4 && device_ec) { + if (fu_device_has_flag(device_usb4, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) && + fu_device_has_flag(device_ec, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_device_remove_flag(FU_DEVICE(device_ec), + FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + g_debug("activate for %s is inhibited by %s", + fu_device_get_name(device_ec), + fu_device_get_name(device_usb4)); + } + } +} + +static void +fu_dell_dock_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + /* dell dock delays the activation so skips device restart */ + if (fu_device_has_guid(device, DELL_DOCK_TBT_INSTANCE_ID)) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); + fu_plugin_cache_add(plugin, "tbt", device); + } + if (fu_device_has_guid(device, DELL_DOCK_USB4_INSTANCE_ID)) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); + fu_plugin_cache_add(plugin, "usb4", device); + } + if (FU_IS_DELL_DOCK_EC(device)) + fu_plugin_cache_add(plugin, "ec", device); + + /* usb4 device from thunderbolt plugin */ + if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") == 0 && + fu_device_has_guid(device, DELL_DOCK_USB4_INSTANCE_ID)) { + g_autofree gchar *msg = NULL; + msg = g_strdup_printf("firmware update inhibited by [%s] plugin", + fu_plugin_get_name(plugin)); + fu_device_inhibit(device, "hidden", msg); + return; + } + + /* online activation is mutually exclusive between usb4 and ec */ + fu_dell_dock_plugin_separate_activation(plugin); +} + +static gboolean +fu_dell_dock_plugin_backend_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; +} + +static gboolean +fu_dell_dock_plugin_composite_prepare(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + FuDevice *parent = fu_dell_dock_plugin_get_ec(devices); + const gchar *sku; + if (parent == NULL) + return TRUE; + sku = fu_dell_dock_ec_get_module_type(parent); + if (sku != NULL) + fu_plugin_add_report_metadata(plugin, "DellDockSKU", sku); + + return TRUE; +} + +static gboolean +fu_dell_dock_plugin_composite_cleanup(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + FuDevice *parent = fu_dell_dock_plugin_get_ec(devices); + FuDevice *dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + gboolean needs_activation = FALSE; + + if (parent == NULL) + return TRUE; + + /* if thunderbolt is in the transaction it needs to be activated separately */ + for (guint i = 0; i < devices->len; i++) { + dev = g_ptr_array_index(devices, i); + if ((g_strcmp0(fu_device_get_plugin(dev), "thunderbolt") == 0 || + g_strcmp0(fu_device_get_plugin(dev), "dell_dock") == 0) && + fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + /* the kernel and/or thunderbolt plugin have been configured to let HW + * finish the update */ + if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { + fu_dell_dock_ec_tbt_passive(parent); + /* run the update immediately - no kernel support */ + } else { + needs_activation = TRUE; + break; + } + } + } + /* separate activation flag between usb4 and ec device */ + fu_dell_dock_plugin_separate_activation(plugin); + + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + if (!fu_dell_dock_ec_reboot_dock(parent, error)) + return FALSE; + + /* close this first so we don't have an error from the thunderbolt activation */ + if (!fu_device_locker_close(locker, error)) + return FALSE; + + if (needs_activation && dev != NULL) { + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + if (!fu_device_activate(dev, progress, error)) + return FALSE; + } + + return TRUE; +} + +static void +fu_dell_dock_plugin_init(FuDellDockPlugin *self) +{ +} + +static void +fu_dell_dock_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "DellDockBlobBuildOffset"); + fu_context_add_quirk_key(ctx, "DellDockBlobMajorOffset"); + fu_context_add_quirk_key(ctx, "DellDockBlobMinorOffset"); + fu_context_add_quirk_key(ctx, "DellDockBlobVersionOffset"); + fu_context_add_quirk_key(ctx, "DellDockBoardMin"); + fu_context_add_quirk_key(ctx, "DellDockHubVersionLowest"); + fu_context_add_quirk_key(ctx, "DellDockInstallDurationI2C"); + fu_context_add_quirk_key(ctx, "DellDockUnlockTarget"); + fu_context_add_quirk_key(ctx, "DellDockVersionLowest"); + + /* allow these to be built by quirks */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_DELL_DOCK_STATUS); + fu_plugin_add_device_gtype(plugin, FU_TYPE_DELL_DOCK_MST); + +#ifndef _WIN32 + /* currently slower performance, but more reliable in corner cases */ + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_BETTER_THAN, "synaptics_mst"); +#endif +} + +static void +fu_dell_dock_plugin_class_init(FuDellDockPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_dell_dock_plugin_constructed; + plugin_class->device_registered = fu_dell_dock_plugin_device_registered; + plugin_class->backend_device_added = fu_dell_dock_plugin_backend_device_added; + plugin_class->backend_device_removed = fu_dell_dock_plugin_backend_device_removed; + plugin_class->composite_cleanup = fu_dell_dock_plugin_composite_cleanup; + plugin_class->composite_prepare = fu_dell_dock_plugin_composite_prepare; +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-plugin.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..0c7eb7785dc89774197ce8b070941b90a99f3f4f --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuDellDockPlugin, fu_dell_dock_plugin, FU, DELL_DOCK_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-status.c b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-status.c new file mode 100644 index 0000000000000000000000000000000000000000..73b6979ebc4acf14dd12e4037c68408d1ff359e9 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-status.c @@ -0,0 +1,184 @@ +/* + * 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_format(device, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(device, dynamic_version); + fu_device_set_logical_id(FU_DEVICE(device), "status"); + return TRUE; +} + +static gboolean +fu_dell_dock_status_write(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDellDockStatus *self = FU_DELL_DOCK_STATUS(device); + FuDevice *parent; + gsize length = 0; + guint32 status_version = 0; + const guint8 *data; + g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data(fw, &length); + if (!fu_memcpy_safe((guint8 *)&status_version, + sizeof(status_version), + 0x0, /* dst */ + data, + length, + self->blob_version_offset, /* src */ + sizeof(status_version), + error)) + return FALSE; + dynamic_version = fu_dell_dock_status_ver_string(status_version); + g_debug("writing status firmware version %s", dynamic_version); + + parent = fu_device_get_parent(device); + if (!fu_dell_dock_ec_commit_package(parent, fw, error)) + return FALSE; + + /* dock will reboot to re-read; this is to appease the daemon */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(device, dynamic_version); + 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) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->blob_version_offset = tmp; + 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_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 13, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 72, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 9, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 7, "reload"); +} + +static void +fu_dell_dock_status_init(FuDellDockStatus *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.dell.dock"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); +} + +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; + klass_device->set_progress = fu_dell_dock_status_set_progress; +} + +FuDellDockStatus * +fu_dell_dock_status_new(FuContext *ctx) +{ + FuDellDockStatus *self = NULL; + self = g_object_new(FU_TYPE_DELL_DOCK_STATUS, "context", ctx, NULL); + return self; +} diff --git a/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-status.h b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-status.h new file mode 100644 index 0000000000000000000000000000000000000000..a716a802e7b3900cd99bbab3271ed6cb7b56310e --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/fu-dell-dock-status.h @@ -0,0 +1,26 @@ +/* + * 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 + +#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(FuContext *ctx); diff --git a/fwupd-1.8.6/plugins/dell-dock/meson.build b/fwupd-1.8.6/plugins/dell-dock/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c2b08965690d5a5ea112c5781d6abd960dff0199 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-dock/meson.build @@ -0,0 +1,24 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginDellDock"'] + +plugin_quirks += files('dell-dock.quirk') +plugin_builtins += static_library('fu_plugin_dell_dock', + sources: [ + 'fu-dell-dock-plugin.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: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + gudev, + ], +) +endif diff --git a/fwupd-1.8.6/plugins/dell-esrt/README.md b/fwupd-1.8.6/plugins/dell-esrt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e95220b3b7f3bfc433cf70fd187649b0f34388f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-esrt/README.md @@ -0,0 +1,51 @@ +# 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`. + +## Vendor ID Security + +The vendor ID is hardcoded to `PCI:0x1028`. + +## 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=disabled` 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: + +```text +UEFI dummy device + Guid: 2d47f29b-83a2-4f31-a2e8-63474f4d4c2e + Plugin: dell-esrt + Flags: internal|updatable|locked + Version: 0 + Created: 2018-06-25 +``` + +## External Interface Access + +This plugin requires: + +* read/write access to `/dev/wmi/dell-smbios` and `/sys/bus/platform/devices/dcdbas`. +* read access to `/sys/firmware/efi/esrt`. diff --git a/fwupd-1.8.6/plugins/dell-esrt/dell-esrt.conf b/fwupd-1.8.6/plugins/dell-esrt/dell-esrt.conf new file mode 100644 index 0000000000000000000000000000000000000000..dbdb97424ddf7e03b3c6f6ea1bada85efed38566 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-esrt/dell-esrt.conf @@ -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 --git a/fwupd-1.8.6/plugins/dell-esrt/fu-dell-esrt-plugin.c b/fwupd-1.8.6/plugins/dell-esrt/fu-dell-esrt-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..0e8bfff86a6fa1311802441996ef491bc53e26be --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-esrt/fu-dell-esrt-plugin.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2017 Dell, Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "fu-dell-esrt-plugin.h" + +struct _FuDellEsrtPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuDellEsrtPlugin, fu_dell_esrt_plugin, FU_TYPE_PLUGIN) + +/* allowed smbios class/select commands */ +#define CLASS_ADMIN_PROP 10 +#define SELECT_ADMIN_PROP 3 + +/* allowed 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_dell_esrt_plugin_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_dell_esrt_plugin_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_dell_esrt_plugin_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; +} + +static gboolean +fu_dell_esrt_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + gboolean capsule_disable = FALSE; + g_autofree gchar *sysfsfwdir = NULL; + g_autofree gchar *esrtdir = NULL; + + /* already exists */ + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + esrtdir = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + 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_dell_esrt_plugin_query_token(CAPSULE_DIS_TOKEN, &capsule_disable, error)) + return FALSE; + if (!capsule_disable) { + gboolean capsule_enable = FALSE; + if (!fu_dell_esrt_plugin_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; +} + +static gboolean +fu_dell_esrt_plugin_unlock(FuPlugin *plugin, FuDevice *device, GError **error) +{ + gboolean password_present = FALSE; + /* check the admin password isn't set */ + if (!fu_dell_esrt_plugin_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_dell_esrt_plugin_query_token(CAPSULE_EN_TOKEN, NULL, error)) + return FALSE; + if (!fu_dell_esrt_plugin_activate_token(CAPSULE_EN_TOKEN, error)) + return FALSE; + fu_device_set_update_error(device, NULL); + + return TRUE; +} + +static gboolean +fu_dell_esrt_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + g_autoptr(FuDevice) dev = fu_device_new(NULL); + + /* create a dummy device so we can unlock the feature */ + fu_device_set_id(dev, "UEFI-dummy"); + fu_device_set_name(dev, "Dell UEFI updates"); + fu_device_set_summary(dev, "UEFI update functionality"); + fu_device_add_vendor_id(dev, "PCI:0x1028"); + fu_device_add_instance_id(dev, "main-system-firmware"); + fu_device_add_guid(dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); + fu_device_set_version_format(dev, FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_version(dev, "0"); + 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"); + if (!fu_device_setup(dev, error)) + return FALSE; + fu_plugin_device_add(plugin, dev); + return TRUE; +} + +static void +fu_dell_esrt_plugin_init(FuDellEsrtPlugin *self) +{ + fu_plugin_add_rule(FU_PLUGIN(self), FU_PLUGIN_RULE_BETTER_THAN, "bios"); +} + +static void +fu_dell_esrt_plugin_class_init(FuDellEsrtPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->startup = fu_dell_esrt_plugin_startup; + plugin_class->coldplug = fu_dell_esrt_plugin_coldplug; + plugin_class->unlock = fu_dell_esrt_plugin_unlock; +} diff --git a/fwupd-1.8.6/plugins/dell-esrt/fu-dell-esrt-plugin.h b/fwupd-1.8.6/plugins/dell-esrt/fu-dell-esrt-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..4ed82d10f4c31fec87698f68edeaba8957133056 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-esrt/fu-dell-esrt-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuDellEsrtPlugin, fu_dell_esrt_plugin, FU, DELL_ESRT_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/dell-esrt/meson.build b/fwupd-1.8.6/plugins/dell-esrt/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..88c039f854571f6dd3a6cd4fdd2e20a37e73fbb3 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-esrt/meson.build @@ -0,0 +1,32 @@ +if libsmbios_c.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginDellEsrt"'] + +install_data(['metadata.xml'], + install_dir: join_paths(datadir, 'fwupd', 'remotes.d', 'dell-esrt') +) +plugin_builtins += static_library('fu_plugin_dell_esrt', + sources: [ + 'fu-dell-esrt-plugin.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + ], + link_with: plugin_libs, + 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'), +) +endif diff --git a/fwupd-1.8.6/plugins/dell-esrt/metadata.xml b/fwupd-1.8.6/plugins/dell-esrt/metadata.xml new file mode 100644 index 0000000000000000000000000000000000000000..fce443c4da5a1aba5646f8c83195ef28b2682f4b --- /dev/null +++ b/fwupd-1.8.6/plugins/dell-esrt/metadata.xml @@ -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 --git a/fwupd-1.8.6/plugins/dell/README.md b/fwupd-1.8.6/plugins/dell/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4d1402af50c449ee5aed569ba3a5ec94a688ae9f --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/README.md @@ -0,0 +1,191 @@ +# Dell Support + +## Introduction + +This allows installing Dell capsules that are not part of the ESRT table. + +## GUID Generation + +These devices uses custom GUIDs for Dell-specific hardware. + +* Thunderbolt devices: `TBT-0x00d4u$(system-id)` +* TPM devices `$(system-id)-$(mode)`, where `mode` is either `2.0` or `1.2` + +In both cases the `system-id` is derived from the SMBIOS Product SKU property. + +TPM GUIDs are also built using the TSS properties +`TPM2_PT_FAMILY_INDICATOR`, `TPM2_PT_MANUFACTURER`, and `TPM2_PT_VENDOR_STRING_*` +These are built hierarchically with more parts for each GUID: + +* `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1` +* `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1$VENDOR_STRING_2` +* `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1$VENDOR_STRING_2$VENDOR_STRING_3` +* `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1$VENDOR_STRING_2$VENDOR_STRING_3$VENDOR_STRING_4` + +If there are non-ASCII values in any vendor string or any vendor is missing that octet will be skipped. + +Example resultant GUIDs from a real system containing a TPM from Nuvoton: + +```text + Guid: 7d65b10b-bb24-552d-ade5-590b3b278188 <- DELL-TPM-2.0-NTC-NPCT + Guid: 6f5ddd3a-8339-5b2a-b9a6-cf3b92f6c86d <- DELL-TPM-2.0-NTC-NPCT75x + Guid: fe462d4a-e48f-5069-9172-47330fc5e838 <- DELL-TPM-2.0-NTC-NPCT75xrls +``` + +## Vendor ID Security + +The vendor ID is hardcoded to `TPM:DELL`. + +## 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=disabled` 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 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 +on the OS side to determine if the devices should be offered to flash. + +## Switchable TPM Devices + +Machines with switchable TPMs can operate in both TPM 1.2 and TPM 2.0 modes. +Switching modes will require flashing an alternative firmware and clearing the +contents of the TPM. + +Machines that offer this functionality will display two devices in +`fwupdmgr get-devices` output. + +Example (from a *Precision 5510*): + +```text +Precision 5510 TPM 1.2 + Guid: b2088ba1-51ae-514e-8f0a-64756c6e4ffc + DeviceID: DELL-b2088ba1-51ae-514e-8f0a-64756c6e4ffclu + Plugin: dell + Flags: internal|allow-offline|require-ac + Version: 5.81.0.0 + Created: 2016-07-19 + +Precision 5510 TPM 2.0 + Guid: 475d9bbd-1b7a-554e-8ca7-54985174a962 + DeviceID: DELL-475d9bbd-1b7a-554e-8ca7-54985174a962lu + Plugin: dell + Flags: internal|require-ac|locked + Created: 2016-07-19 +``` + +In this example, the TPM is currently operating in **TPM 1.2 mode**. Any +firmware updates posted to *LVFS* for TPM 1.2 mode will be applied. + +### Switching TPM Modes + +In order to be offered to switch the TPM to **TPM 2.0 mode**, the virtual device +representing the *TPM 2.0 mode* will need to be unlocked. + +```# fwupdmgr unlock DELL-475d9bbd-1b7a-554e-8ca7-54985174a962lu``` + +If the TPM is currently *owned*, an error will be displayed such as this one: + + ERROR: Precision 5510 TPM 1.2 is currently OWNED. Ownership must be removed to switch modes. + +TPM Ownership can be cleared from within the BIOS setup menus. + +If the unlock process was successful, then the devices will be modified: + +```text +Precision 5510 TPM 1.2 + Guid: b2088ba1-51ae-514e-8f0a-64756c6e4ffc + DeviceID: DELL-b2088ba1-51ae-514e-8f0a-64756c6e4ffclu + Plugin: dell + Flags: internal|require-ac + Version: 5.81.0.0 + Created: 2016-07-19 + +Precision 5510 TPM 2.0 + Guid: 475d9bbd-1b7a-554e-8ca7-54985174a962 + DeviceID: DELL-475d9bbd-1b7a-554e-8ca7-54985174a962lu + Plugin: dell + Flags: internal|allow-offline|require-ac + Version: 0.0.0.0 + Created: 2016-07-19 + Modified: 2016-07-19 +``` + +Now the firmware for TPM 2.0 mode can be pulled down from LVFS and flashed: + +```shell +# fwupdmgr update +``` + +Upon the next reboot, the new TPM firmware will be flashed. If the firmware is +*not offered from LVFS*, then switching modes may not work on this machine. + +After updating the output from ```# fwupdmgr get-devices``` will reflect the +new mode. + +```text +Precision 5510 TPM 2.0 + Guid: 475d9bbd-1b7a-554e-8ca7-54985174a962 + DeviceID: DELL-475d9bbd-1b7a-554e-8ca7-54985174a962lu + Plugin: dell + Flags: internal|allow-offline|require-ac + Version: 1.3.0.1 + Created: 2016-07-20 + +Precision 5510 TPM 1.2 + Guid: b2088ba1-51ae-514e-8f0a-64756c6e4ffc + DeviceID: DELL-b2088ba1-51ae-514e-8f0a-64756c6e4ffclu + Plugin: dell + Flags: internal|require-ac|locked + Created: 2016-07-20 +``` + +Keep in mind that **TPM 1.2** and **TPM 2.0** will require different userspace +tools. + +## Dock Devices + +The *TB16* and *WD15* have a variety of updatable components. Each component +will create a virtual device in ```# fwupdmgr get-devices``` + +For example the WD15 will display these components: + +```text +Dell WD15 Port Controller 1 + Guid: 8ba2b709-6f97-47fc-b7e7-6a87b578fe25 + DeviceID: DELL-8ba2b709-6f97-47fc-b7e7-6a87b578fe25lu + Plugin: dell + Flags: allow-offline|require-ac + Version: 0.1.1.8 + Created: 2016-07-19 + +Dell WD15 + Guid: e7ca1f36-bf73-4574-afe6-a4ccacabf479 + DeviceID: DELL-e7ca1f36-bf73-4574-afe6-a4ccacabf479lu + Plugin: dell + Flags: allow-offline|require-ac + Version: 0.0.0.67 + Created: 2016-07-19 +``` + +Components that can be updated via UEFI capsule will have the `allow-offline` moniker applied. + +These updates can be performed the standard method of using `fwupdmgr update`. + +Some components are updatable via other plugins in fwupd such as multi stream +transport hub (MST) and thunderbolt NVM. + +## External Interface Access + +This plugin requires read/write access to `/dev/wmi/dell-smbios` and `/sys/bus/platform/devices/dcdbas`. diff --git a/fwupd-1.8.6/plugins/dell/dell.quirk b/fwupd-1.8.6/plugins/dell/dell.quirk new file mode 100644 index 0000000000000000000000000000000000000000..d2f2590db025751bd8257f2f80fded65c81570d1 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/dell.quirk @@ -0,0 +1,34 @@ +# Realtek NIC in Dell docks +[USB\VID_0BDA&PID_8153] +Plugin = dell + +# Dell TB16/TB18 cable +[TBT-00d4b051] +Plugin = thunderbolt +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +# Dell TB16/TB18 dock +[TBT-00d4b054] +Plugin = thunderbolt +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +# Dell WD15 dock +[MST-wd15-vmm3332-274] +Plugin = synaptics_mst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +# Dell TB16 dock +[MST-tb16-vmm3320-274] +Plugin = synaptics_mst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 +[MST-tb16-vmm3330-274] +Plugin = synaptics_mst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 + +#Dell TB18 dock +[MST-tb18-vmm3320-274] +Plugin = synaptics_mst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 +[MST-tb18-vmm3330-274] +Plugin = synaptics_mst +ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 diff --git a/fwupd-1.8.6/plugins/dell/fu-dell-plugin.c b/fwupd-1.8.6/plugins/dell/fu-dell-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..9f88ee1b9e750d9fc0a6a8cc59a24d6612e0f7ba --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/fu-dell-plugin.c @@ -0,0 +1,995 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2016 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fu-dell-plugin.h" +#include "fu-plugin-dell.h" + +struct _FuDellPlugin { + FuPlugin parent_instance; + FuDellSmiObj *smi_obj; + guint16 fake_vid; + guint16 fake_pid; + gboolean can_switch_modes; + gboolean capsule_supported; +}; + +G_DEFINE_TYPE(FuDellPlugin, fu_dell_plugin, FU_TYPE_PLUGIN) + +/* These are used to indicate the status of a previous DELL flash */ +#define DELL_SUCCESS 0x0000 +#define DELL_CONSISTENCY_FAIL 0x0001 +#define DELL_FLASH_MEMORY_FAIL 0x0002 +#define DELL_FLASH_NOT_READY 0x0003 +#define DELL_FLASH_DISABLED 0x0004 +#define DELL_BATTERY_MISSING 0x0005 +#define DELL_BATTERY_DEAD 0x0006 +#define DELL_AC_MISSING 0x0007 +#define DELL_CANT_SET_12V 0x0008 +#define DELL_CANT_UNSET_12V 0x0009 +#define DELL_FAILURE_BLOCK_ERASE 0x000A +#define DELL_GENERAL_FAILURE 0x000B +#define DELL_DATA_MISCOMPARE 0x000C +#define DELL_IMAGE_MISSING 0x000D +#define DELL_DID_NOTHING 0xFFFF + +/* Delay for settling */ +#define DELL_FLASH_MODE_DELAY 2 + +typedef struct _DOCK_DESCRIPTION { + const gchar *guid; + const gchar *query; + 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" +#define TB16_PC2_STR "2 1 0 1 1" +#define TB16_PC1_STR "2 1 0 1 0" +#define WD15_PC1_STR "2 1 0 2 0" +#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" +#define WD15_EC_GUID "e8445370-0211-449d-9faa-107906ab189f" +#define TB16_EC_GUID "33cc8870-b1fc-4ec7-948a-c07496874faf" +#define TB16_PC2_GUID "1b52c630-86f6-4aee-9f0c-474dc6be49b6" +#define TB16_PC1_GUID "8fe183da-c94e-4804-b319-0f1ba5457a69" +#define WD15_PC1_GUID "8ba2b709-6f97-47fc-b7e7-6a87b578fe25" +#define LEGACY_CBL_GUID "fece1537-d683-4ea8-b968-154530bb6f73" +#define UNIV_CBL_GUID "e2bf3aad-61a3-44bf-91ef-349b39515d29" +#define TBT_CBL_GUID "6dc832fc-5bb0-4e63-a2ff-02aaba5bc1dc" + +#define EC_DESC "EC" +#define PC1_DESC "Port Controller 1" +#define PC2_DESC "Port Controller 2" +#define LEGACY_CBL_DESC "Passive Cable" +#define UNIV_CBL_DESC "Universal Cable" +#define TBT_CBL_DESC "Thunderbolt Cable" + +/** + * Devices that should allow modeswitching + */ +static guint16 tpm_switch_allowlist[] = { + 0x06F2, 0x06F3, 0x06DD, 0x06DE, 0x06DF, 0x06DB, 0x06DC, 0x06BB, 0x06C6, 0x06BA, 0x06B9, 0x05CA, + 0x06C7, 0x06B7, 0x06E0, 0x06E5, 0x06D9, 0x06DA, 0x06E4, 0x0704, 0x0720, 0x0730, 0x0758, 0x0759, + 0x075B, 0x07A0, 0x079F, 0x07A4, 0x07A5, 0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07B0, + 0x07B1, 0x07B2, 0x07B4, 0x07B7, 0x07B8, 0x07B9, 0x07BE, 0x07BF, 0x077A, 0x07CF}; +/** + * Dell device types to run + */ +static guint8 enclosure_allowlist[] = {0x03, /* desktop */ + 0x04, /* low profile desktop */ + 0x06, /* mini tower */ + 0x07, /* tower */ + 0x08, /* portable */ + 0x09, /* laptop */ + 0x0A, /* notebook */ + 0x0D, /* AIO */ + 0x1E, /* tablet */ + 0x1F, /* convertible */ + 0x21, /* IoT gateway */ + 0x22, + /* embedded PC */}; + +static guint16 +fu_dell_get_system_id(FuPlugin *plugin) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *system_id_str = NULL; + guint16 system_id = 0; + gchar *endptr = NULL; + + /* don't care for test suite */ + if (self->smi_obj->fake_smbios) + return 0; + + system_id_str = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_PRODUCT_SKU); + if (system_id_str != NULL) + system_id = g_ascii_strtoull(system_id_str, &endptr, 16); + if (system_id == 0 || endptr == system_id_str) + system_id = (guint16)sysinfo_get_dell_system_id(); + + return system_id; +} + +static gboolean +fu_dell_supported(FuPlugin *plugin) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(GBytes) de_table = NULL; + g_autoptr(GBytes) da_table = NULL; + g_autoptr(GBytes) enclosure = NULL; + const guint8 *value; + const struct da_structure *da_values; + gsize len; + + /* make sure that Dell SMBIOS methods are available */ + de_table = fu_context_get_smbios_data(ctx, 0xDE); + if (de_table == NULL) + return FALSE; + value = g_bytes_get_data(de_table, &len); + if (len == 0) + return FALSE; + if (*value != 0xDE) + return FALSE; + da_table = fu_context_get_smbios_data(ctx, 0xDA); + if (da_table == NULL) + return FALSE; + da_values = (struct da_structure *)g_bytes_get_data(da_table, &len); + if (len == 0) + return FALSE; + if (!(da_values->supported_cmds & (1 << DACI_FLASH_INTERFACE_CLASS))) { + g_debug("unable to access flash interface. supported commands: 0x%x", + da_values->supported_cmds); + return FALSE; + } + + /* only run on intended Dell hw types */ + enclosure = fu_context_get_smbios_data(ctx, FU_SMBIOS_STRUCTURE_TYPE_CHASSIS); + if (enclosure == NULL) + return FALSE; + value = g_bytes_get_data(enclosure, &len); + if (len == 0) + return FALSE; + for (guint i = 0; i < G_N_ELEMENTS(enclosure_allowlist); i++) { + if (enclosure_allowlist[i] == value[0]) + return TRUE; + } + + return FALSE; +} + +static gboolean +fu_dell_plugin_match_dock_component(const gchar *query_str, + const gchar **guid_out, + const gchar **name_out) +{ + const DOCK_DESCRIPTION list[] = { + {WD15_EC_GUID, WD15_EC_STR, EC_DESC}, + {TB16_EC_GUID, TB16_EC_STR, EC_DESC}, + {WD15_PC1_GUID, WD15_PC1_STR, PC1_DESC}, + {TB16_PC1_GUID, TB16_PC1_STR, PC1_DESC}, + {TB16_PC2_GUID, TB16_PC2_STR, PC2_DESC}, + {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++) { + if (g_strcmp0(query_str, list[i].query) == 0) { + *guid_out = list[i].guid; + *name_out = list[i].desc; + return TRUE; + } + } + return FALSE; +} + +void +fu_dell_plugin_inject_fake_data(FuPlugin *plugin, + guint32 *output, + guint16 vid, + guint16 pid, + guint8 *buf, + gboolean can_switch_modes) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + if (!self->smi_obj->fake_smbios) + return; + for (guint i = 0; i < 4; i++) + self->smi_obj->output[i] = output[i]; + self->fake_vid = vid; + self->fake_pid = pid; + self->smi_obj->fake_buffer = buf; + self->can_switch_modes = TRUE; +} + +static gboolean +fu_dell_plugin_capsule_supported(FuPlugin *plugin) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + return self->smi_obj->fake_smbios || self->capsule_supported; +} + +static gboolean +fu_plugin_dock_node(FuPlugin *plugin, + const gchar *platform, + guint8 type, + const gchar *component_guid, + const gchar *component_desc, + const gchar *version, + FwupdVersionFormat version_format) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *dock_type; + g_autofree gchar *dock_name = NULL; + g_autoptr(FuDevice) dev = NULL; + + dock_type = fu_dell_get_dock_type(type); + if (dock_type == NULL) { + g_debug("Unknown dock type %d", type); + return FALSE; + } + + dev = fu_device_new(ctx); + 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); + fu_device_add_parent_guid(dev, DOCK_FLASH_GUID); + } else { + dock_name = g_strdup_printf("Dell %s", dock_type); + } + fu_device_set_vendor(dev, "Dell Inc."); + fu_device_add_vendor_id(dev, "PCI:0x1028"); + 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, "Thunderbolt™ 3 docking station"); + } else if (type == DOCK_TYPE_WD15) { + fu_device_set_summary(dev, "USB type-C docking station"); + } + fu_device_add_icon(dev, "computer"); + fu_device_add_guid(dev, component_guid); + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_set_version_format(dev, version_format); + if (version != NULL) { + fu_device_set_version(dev, version); + if (fu_dell_plugin_capsule_supported(plugin)) { + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } + } + + fu_plugin_device_register(plugin, dev); + return TRUE; +} + +gboolean +fu_dell_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + FwupdVersionFormat version_format = FWUPD_VERSION_FORMAT_DELL_BIOS; + 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; + g_autofree gchar *flash_ver_str = NULL; + + /* not interesting */ + if (!FU_IS_USB_DEVICE(device)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not a USB device"); + return FALSE; + } + + /* 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 (!self->smi_obj->fake_smbios) { + vid = fu_usb_device_get_vid(FU_USB_DEVICE(device)); + pid = fu_usb_device_get_pid(FU_USB_DEVICE(device)); + platform = fu_device_get_physical_id(FU_DEVICE(device)); + } else { + vid = self->fake_vid; + pid = self->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) { + 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(self->smi_obj, &buf)) { + g_debug("no dock detected"); + return TRUE; + } + + if (buf.record->dock_info_header.dir_version != 1) { + 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; + + if (g_getenv("FWUPD_DELL_VERBOSE") != NULL) { + g_debug("Dock description: %s", dock_info->dock_description); + /* Note: fw package version is deprecated, look at components instead */ + g_debug("Dock flash pkg ver: 0x%x", dock_info->flash_pkg_version); + 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); + } + if (dock_info->flash_pkg_version == 0x00ffffff) + g_debug("WARNING: dock flash package version invalid"); + + for (guint i = 0; i < dock_info->component_count; i++) { + g_autofree gchar *fw_str = NULL; + if (i >= MAX_COMPONENTS) { + g_debug("Too many components. Invalid: #%u", i); + break; + } + g_debug("Dock component %u: %s (version 0x%x)", + i, + dock_info->components[i].description, + dock_info->components[i].fw_version); + query_str = g_strrstr(dock_info->components[i].description, "Query "); + if (query_str == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid dock component request"); + return FALSE; + } + if (!fu_dell_plugin_match_dock_component(query_str + 6, + &component_guid, + &component_name)) { + 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 */ + if (dock_info->flash_pkg_version == 0x00ffffff) { + old_ec = TRUE; + dock_info->flash_pkg_version = 0; + continue; + } + /* if invalid version, don't mark device for updates */ + else if (dock_info->components[i].fw_version == 0 || + dock_info->components[i].fw_version == 0xffffffff) { + old_ec = TRUE; + continue; + } + + fw_str = + fu_version_from_uint32(dock_info->components[i].fw_version, version_format); + if (!fu_plugin_dock_node(plugin, + platform, + buf.record->dock_info_header.dock_type, + 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 = + fu_version_from_uint32(dock_info->flash_pkg_version, version_format); + if (!fu_plugin_dock_node(plugin, + platform, + buf.record->dock_info_header.dock_type, + DOCK_FLASH_GUID, + NULL, + flash_ver_str, + version_format)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to create top dock node"); + + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_dell_plugin_get_results(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(GBytes) de_table = NULL; + const gchar *tmp = NULL; + const guint16 *completion_code; + gsize len; + + de_table = fu_context_get_smbios_data(ctx, 0xDE); + completion_code = g_bytes_get_data(de_table, &len); + if (len < 8) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "ERROR: Unable to read results of %s: %" G_GSIZE_FORMAT " < 8", + fu_device_get_name(device), + len); + return FALSE; + } + + /* look at byte offset 0x06 for identifier meaning completion code */ + if (completion_code[3] == DELL_SUCCESS) { + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_SUCCESS); + } else { + FwupdUpdateState update_state = FWUPD_UPDATE_STATE_FAILED; + switch (completion_code[3]) { + case DELL_CONSISTENCY_FAIL: + tmp = "The image failed one or more consistency checks."; + break; + case DELL_FLASH_MEMORY_FAIL: + tmp = "The BIOS could not access the flash-memory device."; + break; + case DELL_FLASH_NOT_READY: + tmp = "The flash-memory device was not ready when an erase was attempted."; + break; + case DELL_FLASH_DISABLED: + tmp = "Flash programming is currently disabled on the system, or the " + "voltage is low."; + 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."; + break; + case DELL_CANT_UNSET_12V: + tmp = "The 12V required to program the flash-memory could not be removed."; + break; + case DELL_FAILURE_BLOCK_ERASE: + tmp = "A flash-memory failure occurred during a block-erase operation."; + break; + case DELL_GENERAL_FAILURE: + tmp = "A general failure occurred during the flash programming."; + break; + case DELL_DATA_MISCOMPARE: + tmp = "A data miscompare error occurred during the flash programming."; + break; + case DELL_IMAGE_MISSING: + tmp = "The image could not be found in memory, i.e. the header could not " + "be located."; + break; + case DELL_DID_NOTHING: + tmp = "No update operation has been performed on the system."; + break; + default: + break; + } + fu_device_set_update_state(device, update_state); + if (tmp != NULL) + fu_device_set_update_error(device, tmp); + } + + return TRUE; +} + +static void +Esys_Finalize_autoptr_cleanup(ESYS_CONTEXT *esys_context) +{ + Esys_Finalize(&esys_context); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup) + +static gchar * +fu_dell_plugin_get_tpm_capability(ESYS_CONTEXT *ctx, guint32 query) +{ + TSS2_RC rc; + guint32 val; + gchar result[5] = {'\0'}; + g_autofree TPMS_CAPABILITY_DATA *capability = NULL; + rc = Esys_GetCapability(ctx, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + TPM2_CAP_TPM_PROPERTIES, + query, + 1, + NULL, + &capability); + if (rc != TSS2_RC_SUCCESS) { + g_debug("capability request failed for query %x", query); + return NULL; + } + if (capability->data.tpmProperties.count == 0) { + g_debug("no properties returned for query %x", query); + return NULL; + } + if (capability->data.tpmProperties.tpmProperty[0].property != query) { + g_debug("wrong query returned (got %x expected %x)", + capability->data.tpmProperties.tpmProperty[0].property, + query); + return NULL; + } + + val = GUINT32_FROM_BE(capability->data.tpmProperties.tpmProperty[0].value); + memcpy(result, (gchar *)&val, 4); + + /* convert non-ASCII into spaces */ + for (guint i = 0; i < 4; i++) { + if (!g_ascii_isgraph(result[i]) && result[i] != '\0') + result[i] = 0x20; + } + + return fu_strstrip(result); +} + +static gboolean +fu_dell_plugin_add_tpm_model(FuDevice *dev, GError **error) +{ + TSS2_RC rc; + const gchar *base = "DELL-TPM"; + g_autoptr(ESYS_CONTEXT) ctx = NULL; + g_autofree gchar *family = NULL; + g_autofree gchar *manufacturer = NULL; + g_autofree gchar *vendor1 = NULL; + g_autofree gchar *vendor2 = NULL; + g_autofree gchar *vendor3 = NULL; + g_autofree gchar *vendor4 = NULL; + g_autofree gchar *v1 = NULL; + g_autofree gchar *v1_v2 = NULL; + g_autofree gchar *v1_v2_v3 = NULL; + g_autofree gchar *v1_v2_v3_v4 = NULL; + + rc = Esys_Initialize(&ctx, NULL, NULL); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to initialize TPM library"); + return FALSE; + } + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to initialize TPM"); + return FALSE; + } + + /* lookup guaranteed details from TPM */ + family = fu_dell_plugin_get_tpm_capability(ctx, TPM2_PT_FAMILY_INDICATOR); + if (family == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to read TPM family"); + return FALSE; + } + manufacturer = fu_dell_plugin_get_tpm_capability(ctx, TPM2_PT_MANUFACTURER); + if (manufacturer == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to read TPM manufacturer"); + return FALSE; + } + vendor1 = fu_dell_plugin_get_tpm_capability(ctx, TPM2_PT_VENDOR_STRING_1); + if (vendor1 == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to read TPM vendor string"); + return FALSE; + } + fu_device_set_metadata(dev, "TpmFamily", family); + + /* these are not guaranteed by spec and may be NULL */ + vendor2 = fu_dell_plugin_get_tpm_capability(ctx, TPM2_PT_VENDOR_STRING_2); + vendor3 = fu_dell_plugin_get_tpm_capability(ctx, TPM2_PT_VENDOR_STRING_3); + vendor4 = fu_dell_plugin_get_tpm_capability(ctx, TPM2_PT_VENDOR_STRING_4); + + /* add GUIDs to daemon */ + v1 = g_strjoin("-", base, family, manufacturer, vendor1, NULL); + v1_v2 = g_strconcat(v1, vendor2, NULL); + v1_v2_v3 = g_strconcat(v1_v2, vendor3, NULL); + v1_v2_v3_v4 = g_strconcat(v1_v2_v3, vendor4, NULL); + fu_device_add_instance_id(dev, v1); + fu_device_add_instance_id(dev, v1_v2); + fu_device_add_instance_id(dev, v1_v2_v3); + fu_device_add_instance_id(dev, v1_v2_v3_v4); + + return TRUE; +} + +gboolean +fu_dell_plugin_detect_tpm(FuPlugin *plugin, GError **error) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *tpm_mode; + const gchar *tpm_mode_alt; + guint16 system_id = 0; + gboolean can_switch_modes = FALSE; + g_autofree gchar *pretty_tpm_name_alt = NULL; + g_autofree gchar *pretty_tpm_name = NULL; + g_autofree gchar *tpm_guid_raw_alt = NULL; + g_autofree gchar *tpm_guid_alt = NULL; + g_autofree gchar *tpm_guid = NULL; + g_autofree gchar *tpm_guid_raw = NULL; + g_autofree gchar *tpm_id_alt = NULL; + g_autofree gchar *version_str = NULL; + struct tpm_status *out = NULL; + g_autoptr(FuDevice) dev_alt = NULL; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error_tss = NULL; + + fu_dell_clear_smi(self->smi_obj); + out = (struct tpm_status *)self->smi_obj->output; + + /* execute TPM Status Query */ + self->smi_obj->input[0] = DACI_FLASH_ARG_TPM; + if (!fu_dell_execute_simple_smi(self->smi_obj, + DACI_FLASH_INTERFACE_CLASS, + DACI_FLASH_INTERFACE_SELECT)) + return FALSE; + + if (out->ret != 0) { + g_debug("Failed to query system for TPM information: " + "(%" G_GUINT32_FORMAT ")", + out->ret); + return FALSE; + } + /* HW version is output in second /input/ arg + * it may be relevant as next gen TPM is enabled + */ + g_debug("TPM HW version: 0x%x", self->smi_obj->input[1]); + g_debug("TPM Status: 0x%x", out->status); + + /* test TPM enabled (Bit 0) */ + if (!(out->status & TPM_EN_MASK)) { + g_debug("TPM not enabled (%x)", out->status); + return FALSE; + } + + /* test TPM mode to determine current mode */ + if (((out->status & TPM_TYPE_MASK) >> 8) == TPM_1_2_MODE) { + tpm_mode = "1.2"; + tpm_mode_alt = "2.0"; + } else if (((out->status & TPM_TYPE_MASK) >> 8) == TPM_2_0_MODE) { + tpm_mode = "2.0"; + tpm_mode_alt = "1.2"; + } else { + g_debug("Unable to determine TPM mode"); + return FALSE; + } + + system_id = fu_dell_get_system_id(plugin); + if (self->smi_obj->fake_smbios) + can_switch_modes = self->can_switch_modes; + else if (system_id == 0) + return FALSE; + + for (guint i = 0; i < G_N_ELEMENTS(tpm_switch_allowlist); i++) { + if (tpm_switch_allowlist[i] == system_id) { + can_switch_modes = TRUE; + } + } + + tpm_guid_raw = g_strdup_printf("%04x-%s", system_id, tpm_mode); + tpm_guid = fwupd_guid_hash_string(tpm_guid_raw); + + tpm_guid_raw_alt = g_strdup_printf("%04x-%s", system_id, tpm_mode_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 = fu_version_from_uint32(out->fw_version, FWUPD_VERSION_FORMAT_QUAD); + + /* make it clear that the TPM is a discrete device of the product */ + pretty_tpm_name = g_strdup_printf("TPM %s", tpm_mode); + pretty_tpm_name_alt = g_strdup_printf("TPM %s", tpm_mode_alt); + + /* build Standard device nodes */ + dev = fu_device_new(ctx); + fu_device_set_physical_id(dev, "DEVNAME=/dev/tpm0"); + fu_device_set_logical_id(dev, "UEFI"); + fu_device_add_instance_id(dev, tpm_guid_raw); + fu_device_add_instance_id(dev, "system-tpm"); + fu_device_set_vendor(dev, "Dell Inc."); + fu_device_add_vendor_id(dev, "PCI:0x1028"); + fu_device_set_name(dev, pretty_tpm_name); + fu_device_set_summary(dev, "Platform TPM device"); + fu_device_set_version_format(dev, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(dev, version_str); + 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_dell_plugin_capsule_supported(plugin)) { + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } + fu_device_set_flashes_left(dev, out->flashes_left); + } else { + fu_device_set_update_error(dev, "Updating disabled due to TPM ownership"); + } + /* build GUIDs from TSS strings */ + if (!fu_dell_plugin_add_tpm_model(dev, &error_tss)) + g_debug("could not build instances: %s", error_tss->message); + + if (!fu_device_setup(dev, error)) + return FALSE; + fu_plugin_device_register(plugin, dev); + fu_plugin_add_report_metadata(plugin, + "TpmFamily", + fu_device_get_metadata(dev, "TpmFamily")); + + /* build alternate device node */ + if (can_switch_modes) { + dev_alt = fu_device_new(ctx); + fu_device_set_id(dev_alt, tpm_id_alt); + fu_device_add_instance_id(dev_alt, tpm_guid_raw_alt); + fu_device_set_vendor(dev, "Dell Inc."); + fu_device_add_vendor_id(dev, "PCI:0x1028"); + fu_device_set_name(dev_alt, pretty_tpm_name_alt); + fu_device_set_summary(dev_alt, "Alternate mode for platform TPM device"); + fu_device_add_flag(dev_alt, FWUPD_DEVICE_FLAG_INTERNAL); + 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_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 + * + * Mode switching is turned on by setting flashes left on alternate + * device. + */ + if ((out->status & TPM_OWN_MASK) == 0 && out->flashes_left > 0) { + fu_device_set_flashes_left(dev_alt, out->flashes_left); + } else { + fu_device_set_update_error(dev_alt, + "mode switch disabled due to TPM ownership"); + } + if (!fu_device_setup(dev_alt, error)) + return FALSE; + fu_plugin_device_register(plugin, dev_alt); + } else + g_debug("System %04x does not offer TPM modeswitching", system_id); + + return TRUE; +} + +static void +fu_dell_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + /* thunderbolt plugin */ + if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") == 0 && + fu_device_has_flag(device, FWUPD_DEVICE_FLAG_INTERNAL)) { + /* fix VID/DID of safe mode devices */ + if (fu_device_get_metadata_boolean(device, FU_DEVICE_METADATA_TBT_IS_SAFE_MODE)) { + g_autofree gchar *vendor_id = NULL; + g_autofree gchar *device_id = NULL; + guint16 system_id = 0; + + vendor_id = g_strdup("TBT:0x00D4"); + system_id = fu_dell_get_system_id(plugin); + if (system_id == 0) + return; + /* the kernel returns lowercase in sysfs, need to match it */ + device_id = g_strdup_printf("TBT-%04x%04x", 0x00d4u, (unsigned)system_id); + fu_device_add_vendor_id(device, vendor_id); + fu_device_add_instance_id(device, device_id); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + } + } +} + +static void +fu_dell_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + fu_string_append_kb(str, idt, "CanSwitchModes", self->can_switch_modes); + fu_string_append_kb(str, idt, "CapsuleSupported", self->capsule_supported); +} + +static gboolean +fu_dell_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + g_autofree gchar *sysfsfwdir = NULL; + g_autofree gchar *esrtdir = NULL; + + if (self->smi_obj->fake_smbios) { + g_debug("Called with fake SMBIOS implementation. " + "We're ignoring test for SBMIOS table and ESRT. " + "Individual calls will need to be properly staged."); + return TRUE; + } + + if (!fu_dell_supported(plugin)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Firmware updating not supported"); + return FALSE; + } + + if (self->smi_obj->smi == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to initialize libsmbios library"); + return FALSE; + } + + /* If ESRT is not turned on, fwupd will have already created an + * unlock device. + * + * Once unlocked, that will enable flashing capsules here too. + */ + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + esrtdir = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + if (g_file_test(esrtdir, G_FILE_TEST_EXISTS)) + self->capsule_supported = TRUE; + + /* capsules not supported */ + if (!fu_dell_plugin_capsule_supported(plugin)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED); + } + + return TRUE; +} + +static gboolean +fu_dell_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + /* look for switchable TPM */ + if (!fu_dell_plugin_detect_tpm(plugin, error)) + g_debug("No switchable TPM detected"); + return TRUE; +} + +static void +fu_dell_plugin_init(FuDellPlugin *self) +{ + self->smi_obj = g_malloc0(sizeof(FuDellSmiObj)); + if (g_getenv("FWUPD_DELL_VERBOSE") != NULL) + (void)g_setenv("LIBSMBIOS_C_DEBUG_OUTPUT_ALL", "1", TRUE); + else + (void)g_setenv("TSS2_LOG", "esys+none,tcti+none", FALSE); +} + +static void +fu_dell_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + FuDellPlugin *self = FU_DELL_PLUGIN(plugin); + g_autofree gchar *tmp = NULL; + + tmp = g_strdup_printf("%d.%d", + smbios_get_library_version_major(), + smbios_get_library_version_minor()); + fu_context_add_runtime_version(ctx, "com.dell.libsmbios", tmp); + g_debug("Using libsmbios %s", tmp); + + if (fu_dell_supported(plugin)) + self->smi_obj->smi = dell_smi_factory(DELL_SMI_DEFAULTS); + self->smi_obj->fake_smbios = FALSE; + if (g_getenv("FWUPD_DELL_FAKE_SMBIOS") != NULL) + self->smi_obj->fake_smbios = TRUE; + + /* make sure that UEFI plugin is ready to receive devices */ + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_AFTER, "uefi_capsule"); + + /* our TPM device is upgradable! */ + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_BETTER_THAN, "tpm"); +} + +static void +fu_dell_finalize(GObject *obj) +{ + FuDellPlugin *self = FU_DELL_PLUGIN(obj); + if (self->smi_obj->smi) + dell_smi_obj_free(self->smi_obj->smi); + g_free(self->smi_obj); + G_OBJECT_CLASS(fu_dell_plugin_parent_class)->finalize(obj); +} + +static void +fu_dell_plugin_class_init(FuDellPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_dell_plugin_constructed; + object_class->finalize = fu_dell_finalize; + plugin_class->to_string = fu_dell_plugin_to_string; + plugin_class->startup = fu_dell_plugin_startup; + plugin_class->coldplug = fu_dell_plugin_coldplug; + plugin_class->backend_device_added = fu_dell_plugin_backend_device_added; + plugin_class->device_registered = fu_dell_plugin_device_registered; + plugin_class->get_results = fu_dell_plugin_get_results; +} diff --git a/fwupd-1.8.6/plugins/dell/fu-dell-plugin.h b/fwupd-1.8.6/plugins/dell/fu-dell-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..605b0eb8fab48f58b821a30c9a55eb54a0ef1cc7 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/fu-dell-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuDellPlugin, fu_dell_plugin, FU, DELL_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/dell/fu-dell-smi.c b/fwupd-1.8.6/plugins/dell/fu-dell-smi.c new file mode 100644 index 0000000000000000000000000000000000000000..5f38b0f8e6ec7af220e9a8eed284a651d1fe22e0 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/fu-dell-smi.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2017 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-dell-smi.h" + +/* These are for dock query capabilities */ +struct dock_count_in { + guint32 argument; + guint32 reserved1; + guint32 reserved2; + guint32 reserved3; +}; + +struct dock_count_out { + guint32 ret; + guint32 count; + guint32 location; + guint32 reserved; +}; + +static void +_dell_smi_obj_free(FuDellSmiObj *obj) +{ + dell_smi_obj_free(obj->smi); + g_free(obj); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuDellSmiObj, _dell_smi_obj_free); +#pragma clang diagnostic pop + +/* don't actually clear if we're testing */ +gboolean +fu_dell_clear_smi(FuDellSmiObj *obj) +{ + if (obj->fake_smbios) + return TRUE; + + for (gint i = 0; i < 4; i++) { + obj->input[i] = 0; + obj->output[i] = 0; + } + return TRUE; +} + +gboolean +fu_dell_execute_smi(FuDellSmiObj *obj) +{ + gint ret; + + if (obj->fake_smbios) + return TRUE; + + ret = dell_smi_obj_execute(obj->smi); + if (ret != 0) { + g_debug("SMI execution failed: %i", ret); + return FALSE; + } + return TRUE; +} + +guint32 +fu_dell_get_res(FuDellSmiObj *smi_obj, guint8 arg) +{ + if (smi_obj->fake_smbios) + return smi_obj->output[arg]; + + return dell_smi_obj_get_res(smi_obj->smi, arg); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overread" +gboolean +fu_dell_execute_simple_smi(FuDellSmiObj *obj, guint16 class, guint16 select) +{ + /* test suite will mean don't actually call */ + if (obj->fake_smbios) + return TRUE; + + if (dell_simple_ci_smi(class, select, obj->input, obj->output)) { + g_debug("failed to run query %u/%u", class, select); + return FALSE; + } + return TRUE; +} +#pragma GCC diagnostic pop + +gboolean +fu_dell_detect_dock(FuDellSmiObj *smi_obj, guint32 *location) +{ + struct dock_count_in *count_args; + struct dock_count_out *count_out; + + /* look up dock count */ + count_args = (struct dock_count_in *)smi_obj->input; + count_out = (struct dock_count_out *)smi_obj->output; + if (!fu_dell_clear_smi(smi_obj)) { + g_debug("failed to clear SMI buffers"); + return FALSE; + } + count_args->argument = DACI_DOCK_ARG_COUNT; + + if (!fu_dell_execute_simple_smi(smi_obj, DACI_DOCK_CLASS, DACI_DOCK_SELECT)) + return FALSE; + if (count_out->ret != 0) { + g_debug("Failed to query system for dock count: " + "(%" G_GUINT32_FORMAT ")", + count_out->ret); + return FALSE; + } + if (count_out->count < 1) { + g_debug("no dock plugged in"); + return FALSE; + } + if (location != NULL) + *location = count_out->location; + return TRUE; +} + +gboolean +fu_dell_query_dock(FuDellSmiObj *smi_obj, DOCK_UNION *buf) +{ + gint result; + guint32 location; + guint buf_size; + + if (!fu_dell_detect_dock(smi_obj, &location)) + return FALSE; + + fu_dell_clear_smi(smi_obj); + + /* look up more information on dock */ + if (smi_obj->fake_smbios) + buf->buf = smi_obj->fake_buffer; + else { + dell_smi_obj_set_class(smi_obj->smi, DACI_DOCK_CLASS); + dell_smi_obj_set_select(smi_obj->smi, DACI_DOCK_SELECT); + dell_smi_obj_set_arg(smi_obj->smi, cbARG1, DACI_DOCK_ARG_INFO); + dell_smi_obj_set_arg(smi_obj->smi, cbARG2, location); + buf_size = sizeof(DOCK_INFO_RECORD); + buf->buf = dell_smi_obj_make_buffer_frombios_auto(smi_obj->smi, cbARG3, buf_size); + if (!buf->buf) { + g_debug("Failed to initialize buffer"); + return FALSE; + } + } + if (!fu_dell_execute_smi(smi_obj)) + return FALSE; + result = fu_dell_get_res(smi_obj, cbARG1); + if (result != SMI_SUCCESS) { + if (result == SMI_INVALID_BUFFER) { + g_debug("Invalid buffer size, needed %" G_GUINT32_FORMAT, + fu_dell_get_res(smi_obj, cbARG2)); + } else { + g_debug("SMI execution returned error: %d", result); + } + return FALSE; + } + return TRUE; +} + +const gchar * +fu_dell_get_dock_type(guint8 type) +{ + g_autoptr(FuDellSmiObj) smi_obj = NULL; + DOCK_UNION buf; + + /* not yet initialized, look it up */ + if (type == DOCK_TYPE_NONE) { + smi_obj = g_malloc0(sizeof(FuDellSmiObj)); + smi_obj->smi = dell_smi_factory(DELL_SMI_DEFAULTS); + if (!fu_dell_query_dock(smi_obj, &buf)) + return NULL; + type = buf.record->dock_info_header.dock_type; + } + + switch (type) { + case DOCK_TYPE_TB16: + return "TB16"; + case DOCK_TYPE_WD15: + return "WD15"; + default: + g_debug("Dock type %d unknown", type); + } + + return NULL; +} + +gboolean +fu_dell_toggle_dock_mode(FuDellSmiObj *smi_obj, + guint32 new_mode, + guint32 dock_location, + GError **error) +{ + /* Put into mode to accept AR/MST */ + fu_dell_clear_smi(smi_obj); + smi_obj->input[0] = DACI_DOCK_ARG_MODE; + smi_obj->input[1] = dock_location; + smi_obj->input[2] = new_mode; + + if (!fu_dell_execute_simple_smi(smi_obj, DACI_DOCK_CLASS, DACI_DOCK_SELECT)) + return FALSE; + if (smi_obj->output[1] != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Failed to set dock flash mode: %u", + smi_obj->output[1]); + return FALSE; + } + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/dell/fu-dell-smi.h b/fwupd-1.8.6/plugins/dell/fu-dell-smi.h new file mode 100644 index 0000000000000000000000000000000000000000..773e240946646fd5d49bbdba543b352cab623335 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/fu-dell-smi.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include +#include + +typedef struct { + struct dell_smi_obj *smi; + guint32 input[4]; + guint32 output[4]; + gboolean fake_smbios; + guint8 *fake_buffer; +} FuDellSmiObj; + +/* Dock Info version 1 */ +#pragma pack(1) +#define MAX_COMPONENTS 5 + +typedef struct _COMPONENTS { + gchar description[80]; + guint32 fw_version; /* BCD format: 0x00XXYYZZ */ +} COMPONENTS; + +typedef struct _DOCK_INFO { + gchar dock_description[80]; + guint32 flash_pkg_version; /* BCD format: 0x00XXYYZZ */ + guint32 cable_type; /* bit0-7 cable type, bit7-31 set to 0 */ + guint8 location; /* Location of the dock */ + guint8 reserved; + guint8 component_count; + COMPONENTS components[MAX_COMPONENTS]; /* number of component_count */ +} DOCK_INFO; + +typedef struct _DOCK_INFO_HEADER { + guint8 dir_version; /* version 1, 2 … */ + guint8 dock_type; + guint16 reserved; +} DOCK_INFO_HEADER; + +typedef struct _DOCK_INFO_RECORD { + DOCK_INFO_HEADER dock_info_header; /* dock version specific definition */ + DOCK_INFO dock_info; +} DOCK_INFO_RECORD; + +typedef union _DOCK_UNION { + guint8 *buf; + DOCK_INFO_RECORD *record; +} DOCK_UNION; +#pragma pack() + +typedef enum _DOCK_TYPE { DOCK_TYPE_NONE, DOCK_TYPE_TB16, DOCK_TYPE_WD15 } DOCK_TYPE; + +typedef enum _CABLE_TYPE { + CABLE_TYPE_NONE, + CABLE_TYPE_LEGACY, + CABLE_TYPE_UNIV, + CABLE_TYPE_TBT +} CABLE_TYPE; + +gboolean +fu_dell_clear_smi(FuDellSmiObj *obj); + +guint32 +fu_dell_get_res(FuDellSmiObj *smi_obj, guint8 arg); + +gboolean +fu_dell_execute_smi(FuDellSmiObj *obj); + +gboolean +fu_dell_execute_simple_smi(FuDellSmiObj *obj, guint16 class, guint16 select); + +gboolean +fu_dell_detect_dock(FuDellSmiObj *obj, guint32 *location); + +gboolean +fu_dell_query_dock(FuDellSmiObj *smi_obj, DOCK_UNION *buf); + +const gchar * +fu_dell_get_dock_type(guint8 type); + +gboolean +fu_dell_toggle_dock_mode(FuDellSmiObj *smi_obj, + guint32 new_mode, + guint32 dock_location, + GError **error); + +/* SMI return values used */ +#define SMI_SUCCESS 0 +#define SMI_INVALID_BUFFER -6 + +/* These are DACI class/select needed for + * flash capability queries + */ +#define DACI_FLASH_INTERFACE_CLASS 7 +#define DACI_FLASH_INTERFACE_SELECT 3 +#define DACI_FLASH_ARG_TPM 2 +#define DACI_FLASH_ARG_FLASH_MODE 3 +#define DACI_FLASH_MODE_USER 0 +#define DACI_FLASH_MODE_FLASH 1 + +/* DACI class/select for dock capabilities */ +#define DACI_DOCK_CLASS 17 +#define DACI_DOCK_SELECT 22 +#define DACI_DOCK_ARG_COUNT 0 +#define DACI_DOCK_ARG_INFO 1 +#define DACI_DOCK_ARG_MODE 2 +#define DACI_DOCK_ARG_MODE_USER 0 +#define DACI_DOCK_ARG_MODE_FLASH 1 + +/* VID/PID of ethernet controller on dock */ +#define DOCK_NIC_VID 0x0bda +#define DOCK_NIC_PID 0x8153 diff --git a/fwupd-1.8.6/plugins/dell/fu-plugin-dell.h b/fwupd-1.8.6/plugins/dell/fu-plugin-dell.h new file mode 100644 index 0000000000000000000000000000000000000000..a3f839ba723efaa46d0d5f17ae3f7923394913e5 --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/fu-plugin-dell.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-dell-smi.h" + +void +fu_dell_plugin_inject_fake_data(FuPlugin *plugin, + guint32 *output, + guint16 vid, + guint16 pid, + guint8 *buf, + gboolean can_switch_modes); + +gboolean +fu_dell_plugin_detect_tpm(FuPlugin *plugin, GError **error); +gboolean +fu_dell_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error); + +/* These are nodes that will indicate information about + * the TPM status + */ +struct tpm_status { + guint32 ret; + guint32 fw_version; + guint32 status; + guint32 flashes_left; +}; +#define TPM_EN_MASK 0x0001 +#define TPM_OWN_MASK 0x0004 +#define TPM_TYPE_MASK 0x0F00 +#define TPM_1_2_MODE 0x0001 +#define TPM_2_0_MODE 0x0002 diff --git a/fwupd-1.8.6/plugins/dell/fu-self-test.c b/fwupd-1.8.6/plugins/dell/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..3306abb01df1331b10a670501fa07b4cc8c0963c --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/fu-self-test.c @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "../uefi-capsule/fu-uefi-capsule-plugin.h" +#include "fu-context-private.h" +#include "fu-dell-plugin.h" +#include "fu-device-private.h" +#include "fu-plugin-dell.h" +#include "fu-plugin-private.h" + +typedef struct { + FuPlugin *plugin_uefi_capsule; + FuPlugin *plugin_dell; +} FuTest; + +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) +{ + 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_capsule = 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_capsule, device); +} + +static void +fu_dell_plugin_tpm_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + FuDevice *device_v12; + FuDevice *device_v20; + const guint8 fw[30] = {'F', 'W', 0x00}; + gboolean ret; + gulong added_id; + gulong register_id; + struct tpm_status tpm_out; + const gchar *tpm_server_running = g_getenv("TPM_SERVER_RUNNING"); + g_autoptr(GBytes) blob_fw = g_bytes_new_static(fw, sizeof(fw)); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + +#ifdef HAVE_GETUID + if (tpm_server_running == NULL && (getuid() != 0 || geteuid() != 0)) { + g_test_skip("TPM tests require simulated TPM2.0 running or need root access with " + "physical TPM"); + return; + } +#endif + + memset(&tpm_out, 0x0, sizeof(tpm_out)); + + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + added_id = g_signal_connect(FU_PLUGIN(self->plugin_uefi_capsule), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + devices); + + register_id = g_signal_connect(FU_PLUGIN(self->plugin_dell), + "device-register", + G_CALLBACK(fu_engine_plugin_device_register_cb), + self->plugin_uefi_capsule); + ret = fu_plugin_runner_coldplug(self->plugin_dell, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* inject fake data (no TPM) */ + tpm_out.ret = -2; + fu_dell_plugin_inject_fake_data(self->plugin_dell, (guint32 *)&tpm_out, 0, 0, NULL, FALSE); + ret = fu_dell_plugin_detect_tpm(self->plugin_dell, &error); + g_assert_no_error(error); + g_assert_false(ret); + g_assert_cmpint(devices->len, ==, 0); + + /* inject fake data: + * - that is out of flashes + * - no ownership + * - TPM 1.2 + * dev will be the locked 2.0, alt will be the orig 1.2 + */ + tpm_out.ret = 0; + tpm_out.fw_version = 0; + tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8); + tpm_out.flashes_left = 0; + fu_dell_plugin_inject_fake_data(self->plugin_dell, (guint32 *)&tpm_out, 0, 0, NULL, TRUE); + ret = fu_dell_plugin_detect_tpm(self->plugin_dell, &error); + g_assert_true(ret); + g_assert_cmpint(devices->len, ==, 2); + + /* make sure 2.0 is locked */ + device_v20 = _find_device_by_name(devices, "TPM 2.0"); + g_assert_nonnull(device_v20); + g_assert_true(fu_device_has_flag(device_v20, FWUPD_DEVICE_FLAG_LOCKED)); + + /* make sure not allowed to flash 1.2 */ + device_v12 = _find_device_by_name(devices, "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(self->plugin_uefi_capsule, device_v20, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + g_clear_error(&error); + + /* cleanup */ + g_ptr_array_set_size(devices, 0); + + /* inject fake data: + * - 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_dell_plugin_inject_fake_data(self->plugin_dell, (guint32 *)&tpm_out, 0, 0, NULL, TRUE); + ret = fu_dell_plugin_detect_tpm(self->plugin_dell, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* make sure not allowed to flash 1.2 */ + device_v12 = _find_device_by_name(devices, "TPM 1.2"); + g_assert_nonnull(device_v12); + g_assert_false(fu_device_has_flag(device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* try to unlock 2.0 */ + device_v20 = _find_device_by_name(devices, "TPM 2.0"); + g_assert_nonnull(device_v20); + ret = fu_plugin_runner_unlock(self->plugin_uefi_capsule, device_v20, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + g_clear_error(&error); + + /* cleanup */ + g_ptr_array_set_size(devices, 0); + + /* inject fake data: + * - that has flashes + * - not 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_1_2_MODE << 8); + tpm_out.flashes_left = 125; + fu_dell_plugin_inject_fake_data(self->plugin_dell, (guint32 *)&tpm_out, 0, 0, NULL, TRUE); + ret = fu_dell_plugin_detect_tpm(self->plugin_dell, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* make sure allowed to flash 1.2 but not 2.0 */ + device_v12 = _find_device_by_name(devices, "TPM 1.2"); + g_assert_nonnull(device_v12); + g_assert_true(fu_device_has_flag(device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v20 = _find_device_by_name(devices, "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(self->plugin_uefi_capsule, device_v20, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* make sure no longer allowed to flash 1.2 but can flash 2.0 */ + 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 */ + g_ptr_array_set_size(devices, 0); + + /* inject fake data: + * - that has 1 flash left + * - not owned + * - TPM 2.0 + * dev will be the locked 1.2, alt will be the orig 2.0 + */ + tpm_out.status = TPM_EN_MASK | (TPM_2_0_MODE << 8); + tpm_out.flashes_left = 1; + fu_dell_plugin_inject_fake_data(self->plugin_dell, (guint32 *)&tpm_out, 0, 0, NULL, TRUE); + ret = fu_dell_plugin_detect_tpm(self->plugin_dell, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* make sure allowed to flash 2.0 but not 1.2 */ + device_v20 = _find_device_by_name(devices, "TPM 2.0"); + g_assert_nonnull(device_v20); + g_assert_true(fu_device_has_flag(device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); + device_v12 = _find_device_by_name(devices, "TPM 1.2"); + g_assert_nonnull(device_v12); + g_assert_false(fu_device_has_flag(device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* ensure flags set */ + ret = fu_device_probe(device_v20, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* With one flash left we need an override */ + ret = fu_plugin_runner_write_firmware(self->plugin_uefi_capsule, + device_v20, + blob_fw, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &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_write_firmware(self->plugin_uefi_capsule, + device_v20, + blob_fw, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH | FWUPD_INSTALL_FLAG_FORCE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* all */ + g_signal_handler_disconnect(self->plugin_uefi_capsule, added_id); + g_signal_handler_disconnect(self->plugin_dell, register_id); +} + +static void +fu_dell_plugin_dock_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + guint32 out[4] = {0x0, 0x0, 0x0, 0x0}; + DOCK_UNION buf; + DOCK_INFO *dock_info; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(FuUsbDevice) fake_usb_device = NULL; + gulong added_id; + gulong register_id; + + fake_usb_device = fu_usb_device_new(fu_plugin_get_context(self->plugin_dell), NULL); + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + added_id = g_signal_connect(FU_PLUGIN(self->plugin_uefi_capsule), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + devices); + register_id = g_signal_connect(FU_PLUGIN(self->plugin_dell), + "device-register", + G_CALLBACK(fu_engine_plugin_device_register_cb), + self->plugin_uefi_capsule); + + /* make sure bad device doesn't trigger this */ + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + 0x1234, + 0x4321, + NULL, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + &error); + g_assert_false(ret); + g_clear_error(&error); + g_assert_cmpint(devices->len, ==, 0); + + /* inject a USB receiver matching correct VID/PID */ + out[0] = 0; + out[1] = 0; + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + DOCK_NIC_VID, + DOCK_NIC_PID, + NULL, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + &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)); + dock_info = &buf.record->dock_info; + buf.record->dock_info_header.dir_version = 1; + buf.record->dock_info_header.dock_type = DOCK_TYPE_TB16; + memcpy(dock_info->dock_description, "BME_Dock", 8); + dock_info->flash_pkg_version = 0x00ffffff; + dock_info->cable_type = CABLE_TYPE_TBT; + dock_info->location = 2; + dock_info->component_count = 4; + dock_info->components[0].fw_version = 0x00ffffff; + memcpy(dock_info->components[0].description, + "Dock1,EC,MIPS32,BME_Dock,0 :Query 2 0 2 1 0", + 43); + dock_info->components[1].fw_version = 0x10201; + memcpy(dock_info->components[1].description, "Dock1,PC,TI,BME_Dock,0 :Query 2 1 0 1 0", 39); + dock_info->components[2].fw_version = 0x10201; + memcpy(dock_info->components[2].description, "Dock1,PC,TI,BME_Dock,1 :Query 2 1 0 1 1", 39); + dock_info->components[3].fw_version = 0x00ffffff; + memcpy(dock_info->components[3].description, + "Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", + 44); + out[0] = 0; + out[1] = 1; + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + DOCK_NIC_VID, + DOCK_NIC_PID, + buf.buf, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + NULL); + g_assert_true(ret); + g_assert_cmpint(devices->len, ==, 4); + g_ptr_array_set_size(devices, 0); + g_free(buf.record); + + /* inject valid TB16 dock w/ older system EC */ + buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD)); + dock_info = &buf.record->dock_info; + buf.record->dock_info_header.dir_version = 1; + buf.record->dock_info_header.dock_type = DOCK_TYPE_TB16; + memcpy(dock_info->dock_description, "BME_Dock", 8); + dock_info->flash_pkg_version = 0x43; + dock_info->cable_type = CABLE_TYPE_TBT; + dock_info->location = 2; + dock_info->component_count = 4; + dock_info->components[0].fw_version = 0xffffffff; + memcpy(dock_info->components[0].description, + "Dock1,EC,MIPS32,BME_Dock,0 :Query 2 0 2 1 0", + 43); + dock_info->components[1].fw_version = 0x10211; + memcpy(dock_info->components[1].description, "Dock1,PC,TI,BME_Dock,0 :Query 2 1 0 1 0", 39); + dock_info->components[2].fw_version = 0x10212; + memcpy(dock_info->components[2].description, "Dock1,PC,TI,BME_Dock,1 :Query 2 1 0 1 1", 39); + dock_info->components[3].fw_version = 0xffffffff; + memcpy(dock_info->components[3].description, + "Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", + 44); + out[0] = 0; + out[1] = 1; + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + DOCK_NIC_VID, + DOCK_NIC_PID, + buf.buf, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + NULL); + g_assert_true(ret); + g_assert_cmpint(devices->len, ==, 3); + g_ptr_array_set_size(devices, 0); + g_free(buf.record); + + /* inject valid WD15 dock w/ invalid flash pkg version */ + buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD)); + dock_info = &buf.record->dock_info; + buf.record->dock_info_header.dir_version = 1; + buf.record->dock_info_header.dock_type = DOCK_TYPE_WD15; + memcpy(dock_info->dock_description, "IE_Dock", 7); + dock_info->flash_pkg_version = 0x00ffffff; + dock_info->cable_type = CABLE_TYPE_LEGACY; + dock_info->location = 2; + dock_info->component_count = 3; + dock_info->components[0].fw_version = 0x00ffffff; + memcpy(dock_info->components[0].description, + "Dock1,EC,MIPS32,IE_Dock,0 :Query 2 0 2 2 0", + 42); + dock_info->components[1].fw_version = 0x00ffffff; + memcpy(dock_info->components[1].description, "Dock1,PC,TI,IE_Dock,0 :Query 2 1 0 2 0", 38); + dock_info->components[2].fw_version = 0x00ffffff; + memcpy(dock_info->components[2].description, + "Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", + 43); + out[0] = 0; + out[1] = 1; + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + DOCK_NIC_VID, + DOCK_NIC_PID, + buf.buf, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(devices->len, ==, 3); + g_ptr_array_set_size(devices, 0); + g_free(buf.record); + + /* inject valid WD15 dock w/ older system EC */ + buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD)); + dock_info = &buf.record->dock_info; + buf.record->dock_info_header.dir_version = 1; + buf.record->dock_info_header.dock_type = DOCK_TYPE_WD15; + memcpy(dock_info->dock_description, "IE_Dock", 7); + dock_info->flash_pkg_version = 0x43; + dock_info->cable_type = CABLE_TYPE_LEGACY; + dock_info->location = 2; + dock_info->component_count = 3; + dock_info->components[0].fw_version = 0xffffffff; + memcpy(dock_info->components[0].description, + "Dock1,EC,MIPS32,IE_Dock,0 :Query 2 0 2 2 0", + 42); + dock_info->components[1].fw_version = 0x10108; + memcpy(dock_info->components[1].description, "Dock1,PC,TI,IE_Dock,0 :Query 2 1 0 2 0", 38); + dock_info->components[2].fw_version = 0xffffffff; + memcpy(dock_info->components[2].description, + "Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", + 43); + out[0] = 0; + out[1] = 1; + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + DOCK_NIC_VID, + DOCK_NIC_PID, + buf.buf, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(devices->len, ==, 2); + g_ptr_array_set_size(devices, 0); + g_free(buf.record); + + /* inject an invalid future dock */ + buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD)); + dock_info = &buf.record->dock_info; + buf.record->dock_info_header.dir_version = 1; + buf.record->dock_info_header.dock_type = 50; + memcpy(dock_info->dock_description, "Future!", 8); + dock_info->flash_pkg_version = 0x00ffffff; + dock_info->cable_type = CABLE_TYPE_UNIV; + dock_info->location = 2; + dock_info->component_count = 1; + dock_info->components[0].fw_version = 0x00ffffff; + memcpy(dock_info->components[0].description, + "Dock1,EC,MIPS32,FUT_Dock,0 :Query 2 0 2 2 0", + 43); + out[0] = 0; + out[1] = 1; + fu_dell_plugin_inject_fake_data(self->plugin_dell, + (guint32 *)&out, + DOCK_NIC_VID, + DOCK_NIC_PID, + buf.buf, + FALSE); + ret = fu_dell_plugin_backend_device_added(self->plugin_dell, + FU_DEVICE(fake_usb_device), + &error); + g_assert_false(ret); + g_assert_cmpint(devices->len, ==, 0); + g_free(buf.record); + + /* all */ + g_signal_handler_disconnect(self->plugin_uefi_capsule, added_id); + g_signal_handler_disconnect(self->plugin_dell, register_id); +} + +static void +fu_test_self_init(FuTest *self) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + self->plugin_uefi_capsule = + fu_plugin_new_from_gtype(fu_uefi_capsule_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->plugin_uefi_capsule, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + self->plugin_dell = fu_plugin_new_from_gtype(fu_dell_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->plugin_dell, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_test_self_free(FuTest *self) +{ + if (self->plugin_uefi_capsule != NULL) + g_object_unref(self->plugin_uefi_capsule); + if (self->plugin_dell != NULL) + g_object_unref(self->plugin_dell); + g_free(self); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_self_free) +#pragma clang diagnostic pop + +int +main(int argc, char **argv) +{ + g_autofree gchar *sysfsdir = NULL; + g_autofree gchar *testdatadir = NULL; + g_autoptr(FuTest) self = g_new0(FuTest, 1); + + g_test_init(&argc, &argv, NULL); + + /* change path */ + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + /* change behavior */ + sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + (void)g_setenv("FWUPD_UEFI_ESP_PATH", sysfsdir, TRUE); + (void)g_setenv("FWUPD_UEFI_TEST", "1", TRUE); + (void)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); + + g_assert_cmpint(g_mkdir_with_parents("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); + + /* tests go here */ + fu_test_self_init(self); + g_test_add_data_func("/fwupd/plugin{dell:tpm}", self, fu_dell_plugin_tpm_func); + g_test_add_data_func("/fwupd/plugin{dell:dock}", self, fu_dell_plugin_dock_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/dell/meson.build b/fwupd-1.8.6/plugins/dell/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..0bf27c6b174fbbaae25d98824078cb7de8c84ffa --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/meson.build @@ -0,0 +1,58 @@ +tpm2tss_dell = dependency('tss2-esys', version: '>= 2.0', required: get_option('plugin_dell')) +libsmbios_c = dependency('libsmbios_c', version: '>= 2.4.0', required: get_option('plugin_dell')) + +if tpm2tss_dell.found() and libsmbios_c.found() and \ + get_option('plugin_dell').require(get_option('plugin_uefi_capsule').allowed(), + error_message: 'plugin_uefi_capsule is needed for plugin_dell').allowed() + +cargs = ['-DG_LOG_DOMAIN="FuPluginDell"'] + +plugin_quirks += files('dell.quirk') + +plugin_builtin_dell = static_library('fu_plugin_dell', + sources: [ + 'fu-dell-plugin.c', + 'fu-dell-smi.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: [ + cargs, + ], + dependencies: [ + plugin_deps, + libsmbios_c, + tpm2tss_dell, + ], +) +plugin_builtins += plugin_builtin_dell + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + e = executable( + 'dell-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + libsmbios_c, + valgrind, + tpm2tss_dell, + ], + link_with: [ + plugin_libs, + plugin_builtin_uefi_capsule, + plugin_builtin_dell, + ], + c_args: [ + cargs, + ], + ) + test('dell-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/dell/tests b/fwupd-1.8.6/plugins/dell/tests new file mode 120000 index 0000000000000000000000000000000000000000..df988d0b1f414b9c57e944ce63ef2d9f3dd7ae6c --- /dev/null +++ b/fwupd-1.8.6/plugins/dell/tests @@ -0,0 +1 @@ +../uefi-capsule/tests/ \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/dfu-csr/README.md b/fwupd-1.8.6/plugins/dfu-csr/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5335719f0679467e6352456e5a6dd8a007222969 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/README.md @@ -0,0 +1,50 @@ +# DFU CSR + +## 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` + +## Update Behavior + +A DFU device usually presents in runtime mode (or with no interfaces defined), +but if the user puts the device into bootloader mode using a physical button +it then enumerates with a HID descriptor. On attach the device returns to +runtime mode which *may* mean the device "goes away". + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x0A12` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/dfu-csr/data/lsusb.txt b/fwupd-1.8.6/plugins/dfu-csr/data/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..fd1bebca665575d2f23b8331617bec28b8ea00d5 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/data/lsusb.txt @@ -0,0 +1,58 @@ +Bus 001 Device 040: ID 0a12:1337 Cambridge Silicon Radio, Ltd +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0a12 Cambridge Silicon Radio, Ltd + idProduct 0x1337 + bcdDevice 25.20 + iManufacturer 0 + iProduct 2 AIAIAI H05 in + iSerial 3 ABCDEF0123456789 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + 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.00 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 40 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/dfu-csr/data/upgrade.tdc.gz b/fwupd-1.8.6/plugins/dfu-csr/data/upgrade.tdc.gz new file mode 100644 index 0000000000000000000000000000000000000000..380523f3d378057f15f6229ca2e8be270322165b Binary files /dev/null and b/fwupd-1.8.6/plugins/dfu-csr/data/upgrade.tdc.gz differ diff --git a/fwupd-1.8.6/plugins/dfu-csr/dfu-csr.quirk b/fwupd-1.8.6/plugins/dfu-csr/dfu-csr.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b2eec83504923c691d68020a9f93653a1842e077 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/dfu-csr.quirk @@ -0,0 +1,15 @@ +[USB\VID_0A12&PID_1337] +Plugin = dfu_csr +Name = H05 +Summary = Bluetooth Headphones +Icon = audio-headphones +Vendor = AIAIAI +[USB\VID_0A12&PID_1337&REV_2520] +Version = 1.2 + +[USB\VID_0A12&PID_4004] +Plugin = dfu_csr +Name = H60 +Summary = Bluetooth Headphones +Icon = audio-headphones +Vendor = AIAIAI diff --git a/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-device.c b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-device.c new file mode 100644 index 0000000000000000000000000000000000000000..9d2098346a4888440b64c1ab1380f5cdb974076c --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-device.c @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-csr-device.h" + +/** + * FU_DFU_CSR_DEVICE_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_DFU_CSR_DEVICE_FLAG_REQUIRE_DELAY (1 << 0) + +struct _FuDfuCsrDevice { + FuHidDevice parent_instance; + FuDfuState dfu_state; + guint32 dnload_timeout; +}; + +G_DEFINE_TYPE(FuDfuCsrDevice, fu_dfu_csr_device, FU_TYPE_HID_DEVICE) + +#define FU_DFU_CSR_REPORT_ID_COMMAND 0x01 +#define FU_DFU_CSR_REPORT_ID_STATUS 0x02 +#define FU_DFU_CSR_REPORT_ID_CONTROL 0x03 + +#define FU_DFU_CSR_COMMAND_HEADER_SIZE 6 /* bytes */ +#define FU_DFU_CSR_COMMAND_UPGRADE 0x01 + +#define FU_DFU_CSR_STATUS_HEADER_SIZE 7 + +#define FU_DFU_CSR_CONTROL_HEADER_SIZE 2 /* bytes */ +#define FU_DFU_CSR_CONTROL_CLEAR_STATUS 0x04 +#define FU_DFU_CSR_CONTROL_RESET 0xff + +/* maximum firmware packet, including the command header */ +#define FU_DFU_CSR_PACKET_DATA_SIZE 1023 /* bytes */ + +#define FU_DFU_CSR_DEVICE_TIMEOUT 5000 /* ms */ + +static void +fu_dfu_csr_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuDfuCsrDevice *self = FU_DFU_CSR_DEVICE(device); + fu_string_append(str, idt, "State", fu_dfu_state_to_string(self->dfu_state)); + fu_string_append_ku(str, idt, "DownloadTimeout", self->dnload_timeout); +} + +static gboolean +fu_dfu_csr_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 buf[] = {FU_DFU_CSR_REPORT_ID_CONTROL, FU_DFU_CSR_CONTROL_RESET}; + if (!fu_hid_device_set_report(FU_HID_DEVICE(device), + FU_DFU_CSR_REPORT_ID_CONTROL, + buf, + sizeof(buf), + FU_DFU_CSR_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to attach: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_dfu_csr_device_get_status(FuDfuCsrDevice *self, GError **error) +{ + guint8 buf[64] = {0}; + + /* hit hardware */ + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + FU_DFU_CSR_REPORT_ID_STATUS, + buf, + sizeof(buf), + FU_DFU_CSR_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_ALLOW_TRUNC | + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to GetStatus: "); + return FALSE; + } + + /* check packet */ + if (buf[0] != FU_DFU_CSR_REPORT_ID_STATUS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "GetStatus packet-id was %i expected %i", + buf[0], + FU_DFU_CSR_REPORT_ID_STATUS); + return FALSE; + } + + self->dfu_state = buf[5]; + self->dnload_timeout = fu_memread_uint24(&buf[2], G_LITTLE_ENDIAN); + g_debug("timeout=%" G_GUINT32_FORMAT, self->dnload_timeout); + g_debug("state=%s", fu_dfu_state_to_string(self->dfu_state)); + g_debug("status=%s", fu_dfu_status_to_string(buf[6])); + return TRUE; +} + +static gboolean +fu_dfu_csr_device_clear_status(FuDfuCsrDevice *self, GError **error) +{ + guint8 buf[] = {FU_DFU_CSR_REPORT_ID_CONTROL, FU_DFU_CSR_CONTROL_CLEAR_STATUS}; + + /* only clear the status if the state is error */ + if (!fu_dfu_csr_device_get_status(self, error)) + return FALSE; + if (self->dfu_state != FU_DFU_STATE_DFU_ERROR) + return TRUE; + + /* hit hardware */ + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + FU_DFU_CSR_REPORT_ID_CONTROL, + buf, + sizeof(buf), + FU_DFU_CSR_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to ClearStatus: "); + return FALSE; + } + + /* check the hardware again */ + return fu_dfu_csr_device_get_status(self, error); +} + +static GBytes * +fu_dfu_csr_device_upload_chunk(FuDfuCsrDevice *self, GError **error) +{ + guint16 data_sz; + guint8 buf[64] = {0}; + + /* hit hardware */ + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + FU_DFU_CSR_REPORT_ID_COMMAND, + buf, + sizeof(buf), + FU_DFU_CSR_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_ALLOW_TRUNC | + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to ReadFirmware: "); + return NULL; + } + + /* check command byte */ + if (buf[0] != FU_DFU_CSR_REPORT_ID_COMMAND) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "wrong report ID %u", buf[0]); + return NULL; + } + + /* check the length */ + data_sz = fu_memread_uint16(&buf[1], G_LITTLE_ENDIAN); + if (data_sz + FU_DFU_CSR_COMMAND_HEADER_SIZE != (guint16)sizeof(buf)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong data length %" G_GUINT16_FORMAT, + data_sz); + return NULL; + } + + /* return as bytes */ + return g_bytes_new(buf + FU_DFU_CSR_COMMAND_HEADER_SIZE, + sizeof(buf) - FU_DFU_CSR_COMMAND_HEADER_SIZE); +} + +static GBytes * +fu_dfu_csr_device_upload(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDfuCsrDevice *self = FU_DFU_CSR_DEVICE(device); + g_autoptr(GPtrArray) chunks = NULL; + guint32 total_sz = 0; + gsize done_sz = 0; + + /* notify UI */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + for (guint32 i = 0; i < 0x3ffffff; i++) { + g_autoptr(GBytes) chunk = NULL; + gsize chunk_sz; + + /* hit hardware */ + chunk = fu_dfu_csr_device_upload_chunk(self, error); + if (chunk == NULL) + return NULL; + chunk_sz = g_bytes_get_size(chunk); + + /* get the total size using the CSR header */ + if (i == 0 && chunk_sz >= 10) { + const guint8 *buf = g_bytes_get_data(chunk, NULL); + if (memcmp(buf, "CSR-dfu", 7) == 0) { + guint16 hdr_ver; + guint16 hdr_len; + if (!fu_memread_uint16_safe(buf, + chunk_sz, + 8, + &hdr_ver, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (hdr_ver != 0x03) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "DFU_CSR header version is " + "invalid %" G_GUINT16_FORMAT, + hdr_ver); + return NULL; + } + if (!fu_memread_uint32_safe(buf, + chunk_sz, + 10, + &total_sz, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (total_sz == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "DFU_CSR header data length " + "invalid %" G_GUINT32_FORMAT, + total_sz); + return NULL; + } + if (!fu_memread_uint16_safe(buf, + chunk_sz, + 14, + &hdr_len, + G_LITTLE_ENDIAN, + error)) + return NULL; + g_debug("DFU_CSR header length: %" G_GUINT16_FORMAT, hdr_len); + } + } + + /* add to chunk array */ + done_sz += chunk_sz; + g_ptr_array_add(chunks, g_steal_pointer(&chunk)); + fu_progress_set_percentage_full(progress, done_sz, (gsize)total_sz); + + /* we're done */ + if (chunk_sz < 64 - FU_DFU_CSR_COMMAND_HEADER_SIZE) + break; + } + + /* notify UI */ + return fu_dfu_utils_bytes_join_array(chunks); +} + +static gboolean +fu_dfu_csr_device_download_chunk(FuDfuCsrDevice *self, guint16 idx, GBytes *chunk, GError **error) +{ + const guint8 *chunk_data; + gsize chunk_sz = 0; + guint8 buf[FU_DFU_CSR_PACKET_DATA_SIZE] = {0}; + + /* too large? */ + chunk_data = g_bytes_get_data(chunk, &chunk_sz); + if (chunk_sz + FU_DFU_CSR_COMMAND_HEADER_SIZE > FU_DFU_CSR_PACKET_DATA_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "packet was too large: %" G_GSIZE_FORMAT, + chunk_sz); + return FALSE; + } + g_debug("writing %" G_GSIZE_FORMAT " bytes of data", chunk_sz); + + /* create packet */ + buf[0] = FU_DFU_CSR_REPORT_ID_COMMAND; + buf[1] = FU_DFU_CSR_COMMAND_UPGRADE; + fu_memwrite_uint16(&buf[2], idx, G_LITTLE_ENDIAN); + fu_memwrite_uint16(&buf[4], chunk_sz, G_LITTLE_ENDIAN); + if (!fu_memcpy_safe(buf, + sizeof(buf), + FU_DFU_CSR_COMMAND_HEADER_SIZE, /* dst */ + chunk_data, + chunk_sz, + 0x0, /* src */ + chunk_sz, + error)) + return FALSE; + + /* hit hardware */ + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + FU_DFU_CSR_REPORT_ID_COMMAND, + buf, + sizeof(buf), + FU_DFU_CSR_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to Upgrade: "); + return FALSE; + } + + /* wait for hardware */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_CSR_DEVICE_FLAG_REQUIRE_DELAY)) { + g_debug("sleeping for %ums", self->dnload_timeout); + g_usleep(self->dnload_timeout * 1000); + } + + /* get status */ + if (!fu_dfu_csr_device_get_status(self, error)) + return FALSE; + + /* is still busy */ + if (self->dfu_state == FU_DFU_STATE_DFU_DNBUSY) { + g_debug("busy, so sleeping a bit longer"); + g_usleep(G_USEC_PER_SEC); + if (!fu_dfu_csr_device_get_status(self, error)) + return FALSE; + } + + /* not correct */ + if (self->dfu_state != FU_DFU_STATE_DFU_DNLOAD_IDLE && + self->dfu_state != FU_DFU_STATE_DFU_IDLE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "device did not return to IDLE"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_csr_device_download(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDfuCsrDevice *self = FU_DFU_CSR_DEVICE(device); + guint idx; + g_autoptr(GBytes) blob_empty = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get default image */ + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return FALSE; + + /* notify UI */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + + /* create chunks */ + chunks = fu_chunk_array_new_from_bytes(blob, + 0x0, + 0x0, + FU_DFU_CSR_PACKET_DATA_SIZE - + FU_DFU_CSR_COMMAND_HEADER_SIZE); + if (chunks->len > G_MAXUINT16) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "too many chunks for hardware: 0x%x", + chunks->len); + return FALSE; + } + + /* send to hardware */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (idx = 0; idx < chunks->len; idx++) { + FuChunk *chk = g_ptr_array_index(chunks, idx); + g_autoptr(GBytes) blob_tmp = fu_chunk_get_bytes(chk); + + /* send packet */ + if (!fu_dfu_csr_device_download_chunk(self, idx, blob_tmp, error)) + return FALSE; + + /* update progress */ + fu_progress_step_done(progress); + } + + /* all done */ + blob_empty = g_bytes_new(NULL, 0); + return fu_dfu_csr_device_download_chunk(self, idx, blob_empty, error); +} + +static gboolean +fu_dfu_csr_device_setup(FuDevice *device, GError **error) +{ + FuDfuCsrDevice *self = FU_DFU_CSR_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_dfu_csr_device_parent_class)->setup(device, error)) + return FALSE; + + if (!fu_dfu_csr_device_clear_status(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_dfu_csr_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_dfu_csr_device_init(FuDfuCsrDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.qualcomm.dfu"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_DFU_FIRMWARE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_CSR_DEVICE_FLAG_REQUIRE_DELAY, + "require-delay"); +} + +static void +fu_dfu_csr_device_class_init(FuDfuCsrDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_dfu_csr_device_to_string; + klass_device->write_firmware = fu_dfu_csr_device_download; + klass_device->dump_firmware = fu_dfu_csr_device_upload; + klass_device->attach = fu_dfu_csr_device_attach; + klass_device->setup = fu_dfu_csr_device_setup; + klass_device->set_progress = fu_dfu_csr_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-device.h b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-device.h new file mode 100644 index 0000000000000000000000000000000000000000..a6a312308c6635048fcfe99b9242b622691b2a59 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_DFU_CSR_DEVICE (fu_dfu_csr_device_get_type()) +G_DECLARE_FINAL_TYPE(FuDfuCsrDevice, fu_dfu_csr_device, FU, DFU_CSR_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-plugin.c b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..dc990c280308c9d2c89c3245bf52da938dbb5d8f --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-dfu-csr-device.h" +#include "fu-dfu-csr-plugin.h" + +struct _FuDfuCsrPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuDfuCsrPlugin, fu_dfu_csr_plugin, FU_TYPE_PLUGIN) + +static void +fu_dfu_csr_plugin_init(FuDfuCsrPlugin *self) +{ +} + +static void +fu_dfu_csr_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_DFU_CSR_DEVICE); +} + +static void +fu_dfu_csr_plugin_class_init(FuDfuCsrPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_dfu_csr_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-plugin.h b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..0fcee899d768991eee6cb3fb51368a07d1e6db24 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/fu-dfu-csr-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuDfuCsrPlugin, fu_dfu_csr_plugin, FU, DFU_CSR_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/dfu-csr/meson.build b/fwupd-1.8.6/plugins/dfu-csr/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..5c65b3364c2aa29b636b47f3100db9846165f222 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu-csr/meson.build @@ -0,0 +1,23 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginDfuCsr"'] + +plugin_quirks += files('dfu-csr.quirk') +plugin_builtins += static_library('fu_plugin_dfu_csr', + sources: [ + 'fu-dfu-csr-device.c', + 'fu-dfu-csr-plugin.c', + ], + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + plugindfu_incdir, + ], + c_args: cargs, + dependencies: plugin_deps, + link_with: [ + fwupdplugin, + plugin_builtin_dfu, + ], +) +endif diff --git a/fwupd-1.8.6/plugins/dfu/README.md b/fwupd-1.8.6/plugins/dfu/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d020230d2fe368b8909115ec548612aed1edab3f --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/README.md @@ -0,0 +1,70 @@ +# DFU + +## Introduction + +Device Firmware Update is a standard that allows USB devices to be easily and +safely updated by any operating system. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +DFU or DfuSe file format. + +This plugin supports the following protocol IDs: + +* org.usb.dfu +* com.st.dfuse + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_273F&PID_1003&REV_0001` +* `USB\VID_273F&PID_1003` +* `USB\VID_273F` + +## Update Behavior + +A DFU device usually presents in runtime mode (with optional DFU runtime), but +on detach re-enumerates with an additional required DFU descriptor. On attach +the device again re-enumerates back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, for example `USB:0x0A12` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### DfuFlags + +Optional quirks for a DFU device which doesn't follow the DFU 1.0 or 1.1 specification. + +Since: 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. +If set to 0000 then the DFU functionality is disabled. + +Since: 1.0.1 + +### DfuForceTimeout + +Forces a specific device timeout, in ms. + +Since: 1.4.0 + +### DfuForceTransferSize + +Forces a target transfer size, in bytes. + +Since: 1.5.6 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/dfu/contrib/parse-avrdude-conf.py b/fwupd-1.8.6/plugins/dfu/contrib/parse-avrdude-conf.py new file mode 100755 index 0000000000000000000000000000000000000000..f4c7eb1705d996b9c025cb7a28b5073c331c70df --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/contrib/parse-avrdude-conf.py @@ -0,0 +1,169 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ + +""" This parses avrdude.conf and generates quirks for fwupd """ + +# pylint: disable=wrong-import-position,pointless-string-statement + +import sys +from difflib import SequenceMatcher + +# finds a part using the ID +def _find_part_by_id(parts, part_id): + for part in parts: + if "id" not in part: + continue + if part["id"] == part_id: + return part + return None + + +# finds a memory layout for a part, climbing up the tree to the parent if reqd. +def _find_mem_layout(parts, part): + if "memory-application" in part: + memory_flash = part["memory-application"] + if memory_flash: + return memory_flash + + # look at the parent + if "parent" in part: + parent = _find_part_by_id(parts, part["parent"]) + if parent: + return _find_mem_layout(parts, parent) + print("no parent ", part["parent"], "found for", part["id"]) + return None + + +# parses the weird syntax of avrdude.conf and makes lots of nested dictionaries +def _parse_parts(fn_source): + print("reading", fn_source) + + part = None + memory_id = None + parts = [] + + for line in open(fn_source).readlines(): + + # try to clean up crazy syntax + line = line.replace("\n", "") + if line.endswith(";"): + line = line[:-1] + + # ignore blank lines + line = line.rstrip() + if not line: + continue + + # count how many spaces deep this is + lvl = 0 + for char in line: + if char != " ": + break + lvl = lvl + 1 + + # ignore comments + line = line.strip() + if line[0] == "#": + continue + + # level 0 of hell + if lvl == 0: + if line.startswith("part"): + memory_id = None + part = {} + parts.append(part) + if line.startswith("part parent "): + part["parent"] = line[13:].replace('"', "") + continue + + # level 4 of hell + if lvl == 4: + if line.startswith("memory"): + memory_id = "memory-" + line[7:].replace('"', "") + part[memory_id] = {} + continue + split = line.split("=") + if len(split) != 2: + print("ignoring", line) + continue + part[split[0].strip()] = split[1].strip().replace('"', "") + continue + + # level 8 of hell + if lvl == 8: + if memory_id: + split = line.split("=") + if len(split) != 2: + continue + memory = part[memory_id] + memory[split[0].strip()] = split[1].strip() + continue + return parts + + +def _get_longest_substring(s1, s2): + match = SequenceMatcher(None, s1, s2).find_longest_match(0, len(s1), 0, len(s2)) + return s2[match.b : match.b + match.size] + + +# writes important data to the quirks file +def _write_quirks(parts, fn_destination): + outp = [] + + results = {} + + for part in parts: + + # ignore meta parts with deprecated names + if "desc" not in part: + continue + if "signature" not in part: + continue + + # find the layout + mem_part = _find_mem_layout(parts, part) + if not mem_part: + print("no memory layout for", part["desc"]) + continue + if not "size" in mem_part: + print("no memory size for", part["desc"]) + continue + if mem_part["size"].startswith("0x"): + size = int(mem_part["size"], 16) + else: + size = int(mem_part["size"], 10) + + # output the line for the quirk + chip_id = "0x" + part["signature"].replace("0x", "").replace(" ", "") + mem_layout = "@Flash/0x0/1*%.0iKg" % int(size / 1024) + + # merge duplicate quirks + if chip_id in results: + result = results[chip_id] + result["desc"] = _get_longest_substring(result["desc"], part["desc"]) + else: + result = {} + result["desc"] = part["desc"] + result["size"] = size + result["mem_layout"] = mem_layout + results[chip_id] = result + + for chip_id in results: + result = results[chip_id] + outp.append( + "# " + result["desc"] + " [USER] USER=0x%x" % result["size"] + "\n" + ) + outp.append(chip_id + "=" + result["mem_layout"] + "\n\n") + + # write file + print("writing", fn_destination) + open(fn_destination, "w").writelines(outp) + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("USAGE: %s avrdude.conf tmp.quirk" % sys.argv[0]) + sys.exit(1) + + all_parts = _parse_parts(sys.argv[1]) + _write_quirks(all_parts, sys.argv[2]) diff --git a/fwupd-1.8.6/plugins/dfu/dfu-tool.1 b/fwupd-1.8.6/plugins/dfu/dfu-tool.1 new file mode 100644 index 0000000000000000000000000000000000000000..831d2e1468c32a30ae5f3127f321c8776ec3fa7d --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/dfu-tool.1 @@ -0,0 +1,31 @@ +.\" Report problems in https://github.com/fwupd/fwupd +.TH man 1 "11 April 2021" @PACKAGE_VERSION@ "dfu-tool man page" +.SH NAME +dfu-tool \- write firmware to DFU devices +.SH SYNOPSIS +dfu-tool [CMD] +.SH DESCRIPTION +.PP +This manual page documents briefly the \fBdfu-tool\fR command. +.PP +\fBdfu-tool\fR allows a user to write various kinds of +firmware onto devices supporting the USB Device Firmware Upgrade protocol. +This tool can be used to switch the device from the normal runtime mode +to `DFU mode' which allows the user to read and write firmware. +Either the whole device can be written in one operation, or individual +`targets' can be specified with the alternative name or number. +.PP +All synchronous actions can be safely cancelled and on failure will return +errors with both a type and a full textual description. +libdfu supports DFU 1.0, DFU 1.1 and the ST DfuSe vendor extension, and +handles many device `quirks' necessary for the real-world implementations +of DFU\&. +.SH OPTIONS +The dfu-tool command takes various options depending on the action. +Run \fBdfu-tool --help\fR for the full list. +.SH SEE ALSO +fwupdtool(1), fwupdmgr(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Richard Hughes (richard@hughsie.com) diff --git a/fwupd-1.8.6/plugins/dfu/dfu.quirk b/fwupd-1.8.6/plugins/dfu/dfu.quirk new file mode 100644 index 0000000000000000000000000000000000000000..2402faa8b20055f8b7af0bfa470ae085bbc54665 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/dfu.quirk @@ -0,0 +1,489 @@ +# All DFU devices +[USB\CLASS_FE&SUBCLASS_01] +Plugin = dfu + +# GD32VF103 Rev1 +[USB\VID_28E9&PID_0189] +Flags = gd32,force-dfu-mode,will-disappear +Name = GD32VF103 +Vendor = GDMicroelectronics + +# Realtek USB camera +[USB\VID_0BDA&PID_5850] +CounterpartGuid = USB\VID_0BDA&PID_5800 +[USB\VID_0BDA&PID_5855] +CounterpartGuid = USB\VID_0BDA&PID_5800 +[USB\VID_0BDA&PID_58FE] +CounterpartGuid = USB\VID_0BDA&PID_5800 +[USB\VID_0BDA&PID_5800] +Flags = detach-for-attach + +# Openmoko Freerunner / GTA02 +[USB\VID_1D50&PID_5119] +Plugin = dfu +Flags = ignore-polltimeout,no-pid-change,no-dfu-runtime,needs-bootloader,no-get-status-upload + +# OpenPCD Reader +[USB\VID_16C0&PID_076B] +Plugin = dfu +Flags = ignore-polltimeout + +# SIMtrace +[USB\VID_16C0&PID_0762] +Plugin = dfu +Flags = ignore-polltimeout + +# OpenPICC +[USB\VID_16C0&PID_076C] +Plugin = dfu +Flags = ignore-polltimeout + +# Siemens AG, PXM 40 & PXM 50 +[USB\VID_0908&PID_02C4] +Plugin = dfu +[USB\VID_0908&PID_02C5] +Plugin = dfu +[USB\VID_0908&PID_02C4&REV_0000] +Flags = ignore-polltimeout +[USB\VID_0908&PID_02C5&REV_0000] +Flags = ignore-polltimeout + +# Midiman M-Audio Transit +[USB\VID_0763&PID_2806] +Plugin = dfu +Flags = ignore-polltimeout + +# LPC DFU bootloader +[USB\VID_1FC9&PID_000C] +Plugin = dfu +Flags = force-dfu-mode + +# m-stack DFU +[USB\VID_273F&PID_1003] +Flags = attach-upload-download +[USB\VID_273F&PID_100A] +Flags = attach-upload-download +[USB\VID_273F&PID_1008] +Flags = attach-upload-download + +# HydraBus +[USB\VID_1D50&PID_60A7] +Plugin = dfu +Flags = no-dfu-runtime,needs-bootloader + +# Hughski AT90USBKEY Mouse+DFU Demo +[USB\VID_273F&PID_2000] +Flags = unsigned-payload + +# Jabra 410 [appIDLE & dfuIDLE] +[USB\VID_0B0E&PID_0411] +Plugin = dfu +Flags = no-pid-change,ignore-upload,attach-extra-reset + +# Jabra 510 [appIDLE & dfuIDLE] +[USB\VID_0B0E&PID_0421] +Plugin = dfu +Flags = no-pid-change,ignore-upload,attach-extra-reset + +# Jabra 710 [appIDLE & dfuIDLE] +[USB\VID_0B0E&PID_0982] +Plugin = dfu +Flags = no-pid-change,ignore-upload,attach-extra-reset + +# Jabra 810 [appIDLE & dfuIDLE] +[USB\VID_0B0E&PID_0971] +Plugin = dfu +Flags = no-pid-change,ignore-upload,attach-extra-reset + +# Atmel AT90USB Bootloader +[USB\VID_03EB&PID_2FF7] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +DfuForceVersion = 0xff01 +[USB\VID_03EB&PID_2FF9] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +DfuForceVersion = 0xff01 +[USB\VID_03EB&PID_2FFA] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +DfuForceVersion = 0xff01 +[USB\VID_03EB&PID_2FFB] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +DfuForceVersion = 0xff01 + +# Atmel ATMEGA Bootloader +[USB\VID_03EB&PID_2FEE] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +[USB\VID_03EB&PID_2FEF] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +[USB\VID_03EB&PID_2FF0] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +[USB\VID_03EB&PID_2FF2] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +[USB\VID_03EB&PID_2FF3] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode +[USB\VID_03EB&PID_2FF4] +Plugin = dfu +Flags = use-any-interface,legacy-protocol,force-dfu-mode + +# Atmel XMEGA Bootloader +[USB\VID_03EB&PID_2FE2] +Plugin = dfu +Flags = use-any-interface,force-dfu-mode +DfuForceVersion = 0xff01 + +# Leaflabs Maple3 +[USB\VID_1EAF&PID_0003&REV_0200] +Plugin = dfu +DfuForceVersion = 0x0110 + +# AT32UC3B1256 [BLDR][USER] USER@0x2000, BLDR+USER=0x40000 +[DFU_AVR\CID_0x58200203] +DfuAltName = @Flash/0x2000/1*248Kg + +# AT32UC3A3256 [BLDR][USER] USER@0x2000, BLDR+USER=0x40000 +[DFU_AVR\CID_0x58200204] +DfuAltName = @Flash/0x2000/1*248Kg + +# AT90USB1287 [USER][BLDR] BLDR@0x1e000, BLDR+USER=0x20000 +[DFU_AVR\CID_0x581e9782] +DfuAltName = @Flash/0x0/1*120Kg + +# AT90USB647 [USER][BLDR] BLDR@0x0e000, BLDR+USER=0x10000 +# AT90USB646 [USER][BLDR] BLDR@0x0e000, BLDR+USER=0x10000 +[DFU_AVR\CID_0x581e9682] +DfuAltName = @Flash/0x0/1*56Kg + +# ATmega32U4 [USER][BLDR] BLDR@0x07000, BLDR+USER=0x08000 +[DFU_AVR\CID_0x581e9587] +DfuAltName = @Flash/0x0/1*28Kg + +# ATmega16U4 [USER][BLDR] BLDR@0x03000, BLDR+USER=0x04000 +[DFU_AVR\CID_0x581e9488] +DfuAltName = @Flash/0x0/1*12Kg + +# ATmega32U2 [USER][BLDR] BLDR@0x07000, BLDR+USER=0x08000 +[DFU_AVR\CID_0x581e958a] +DfuAltName = @Flash/0x0/1*28Kg + +# ATmega16U2 [USER][BLDR] BLDR@0x03000, BLDR+USER=0x04000 +[DFU_AVR\CID_0x581e9489] +DfuAltName = @Flash/0x0/1*12Kg + +# AT90USB162 [USER][BLDR] BLDR@0x03000, BLDR+USER=0x04000 +[DFU_AVR\CID_0x581e9482] +DfuAltName = @Flash/0x0/1*12Kg + +# ATmega8U2 [USER][BLDR] BLDR@0x01000, BLDR+USER=0x02000 +[DFU_AVR\CID_0x581e9389] +DfuAltName = @Flash/0x0/1*4Kg + +# AT90USB82 [USER][BLDR] BLDR@0x01000, BLDR+USER=0x02000 +[DFU_AVR\CID_0x581e9382] +DfuAltName = @Flash/0x0/1*4Kg + +# ATxmega16A4 [USER] USER=0x4000 +[DFU_AVR\CID_0x1e9441] +DfuAltName = @Flash/0x0/1*16Kg + +# ATxmega16C4 [USER] USER=0x4000 +[DFU_AVR\CID_0x1e9544] +DfuAltName = @Flash/0x0/1*16Kg + +# ATxmega16D4 [USER] USER=0x4000 +[DFU_AVR\CID_0x1e9442] +DfuAltName = @Flash/0x0/1*16Kg + +# ATxmega32A4 [USER] USER=0x8000 +[DFU_AVR\CID_0x1e9541] +DfuAltName = @Flash/0x0/1*32Kg + +# ATxmega32C4 [USER] USER=0x8000 +[DFU_AVR\CID_0x1e9443] +DfuAltName = @Flash/0x0/1*32Kg + +# ATxmega32D4 [USER] USER=0x8000 +[DFU_AVR\CID_0x1e9542] +DfuAltName = @Flash/0x0/1*32Kg + +# ATxmega64A4 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e9646] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64C3 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e9649] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64D3 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e964a] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64D4 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e9647] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64A1 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e964e] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64A3 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e9642] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64B1 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e9652] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega64B3 [USER] USER=0x10000 +[DFU_AVR\CID_0x1e9651] +DfuAltName = @Flash/0x0/1*64Kg + +# ATxmega128C3 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e9752] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128D3 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e9748] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128D4 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e9747] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128A1 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e974c] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128A1D [USER] USER=0x20000 +[DFU_AVR\CID_0x1e9741] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128A3 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e9742] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128A4 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e9746] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128B1 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e974d] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega128B3 [USER] USER=0x20000 +[DFU_AVR\CID_0x1e974b] +DfuAltName = @Flash/0x0/1*128Kg + +# ATxmega192C3 [USER] USER=0x30000 +[DFU_AVR\CID_0x1e9751] +DfuAltName = @Flash/0x0/1*192Kg + +# ATxmega192D3 [USER] USER=0x30000 +[DFU_AVR\CID_0x1e9749] +DfuAltName = @Flash/0x0/1*192Kg + +# ATxmega192A1 [USER] USER=0x30000 +[DFU_AVR\CID_0x1e974e] +DfuAltName = @Flash/0x0/1*192Kg + +# ATxmega192A3 [USER] USER=0x30000 +[DFU_AVR\CID_0x1e9744] +DfuAltName = @Flash/0x0/1*192Kg + +# ATxmega256 [USER] USER=0x40000 +[DFU_AVR\CID_0x1e9846] +DfuAltName = @Flash/0x0/1*256Kg + +# ATxmega256D3 [USER] USER=0x40000 +[DFU_AVR\CID_0x1e9844] +DfuAltName = @Flash/0x0/1*256Kg + +# ATxmega256A3 [USER] USER=0x40000 +[DFU_AVR\CID_0x1e9842] +DfuAltName = @Flash/0x0/1*256Kg + +# ATxmega256A3B [USER] USER=0x40000 +[DFU_AVR\CID_0x1e9843] +DfuAltName = @Flash/0x0/1*256Kg + +# ATxmega384C3 [USER] USER=0x60000 +[DFU_AVR\CID_0x1e9845] +DfuAltName = @Flash/0x0/1*384Kg + +# ATxmega384D3 [USER] USER=0x60000 +[DFU_AVR\CID_0x1e9847] +DfuAltName = @Flash/0x0/1*384Kg + +# ATxmega8E5 [USER] USER=0x2000 +[DFU_AVR\CID_0x1e9341] +DfuAltName = @Flash/0x0/1*8Kg + +# ATxmega16E5 [USER] USER=0x4000 +[DFU_AVR\CID_0x1e9445] +DfuAltName = @Flash/0x0/1*16Kg + +# ATxmega32E5 [USER] USER=0x8000 +[DFU_AVR\CID_0x1e954c] +DfuAltName = @Flash/0x0/1*32Kg + +# STM32F745 dfuse bootloader +[USB\VID_0483&PID_DF11] +Flags = absent-sector-size,will-disappear +Plugin = dfu +DfuForceVersion = 0x011a +DfuForceTimeout = 5000 + +# Poly Studio USB +[USB\VID_095D&PID_9217] +Plugin = dfu +Flags = manifest-poll,no-bus-reset-attach,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 60000 +[USB\VID_095D&PID_9218] +Plugin = dfu +Flags = manifest-poll,no-bus-reset-attach,allow-zero-polltimeout,signed-payload +RemoveDelay = 60000 + +# Poly Eagle Eye Cube +[USB\VID_095D&PID_9212] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 30000 +[USB\VID_095D&PID_9213] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 30000 + +# Poly Studio P15 +[USB\VID_095D&PID_9290] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 60000 +[USB\VID_095D&PID_9291] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload +RemoveDelay = 60000 + +# Poly ULCC +[USB\VID_095D&PID_9160] +Plugin = dfu +Flags = manifest-poll,no-bus-reset-attach,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 60000 +[USB\VID_095D&PID_927B] +Plugin = dfu +Flags = manifest-poll,no-bus-reset-attach,allow-zero-polltimeout,signed-payload +RemoveDelay = 60000 + +# Poly Eagle Eye Mini +[USB\VID_095D&PID_3001] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 9000 +[USB\VID_095D&PID_3002] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 9000 + +# Poly Studio R30 +[USB\VID_095D&PID_92B2] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 60000 +[USB\VID_095D&PID_92B3] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload +RemoveDelay = 60000 +[USB\VID_095D&PID_92B4] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 60000 + +# Poly Studio P5 +[USB\VID_095D&PID_9296] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 9000 +[USB\VID_095D&PID_9297] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 9000 + +# Poly Studio E70 +[USB\VID_095D&PID_92A1] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 90000 +[USB\VID_095D&PID_92A2] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload +RemoveDelay = 90000 + +# Poly Studio P21 +[USB\VID_095D&PID_9298] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 9000 +[USB\VID_095D&PID_9299] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,unsigned-payload +RemoveDelay = 9000 + +# AVer ATLAS CAM +[USB\VID_34AD&PID_0006] +Plugin = dfu +Flags = detach-for-attach +RemoveDelay = 60000 + +# AVer CAM520 Pro2 +[USB\VID_2574&PID_0A30] +Plugin = dfu +Flags = detach-for-attach +RemoveDelay = 180000 + +# FlatFrog DFU +[USB\VID_25B5&PID_0004] +Plugin = dfu +Flags = manifest-poll,detach-for-attach,ignore-upload + +# SunplusIT USB cameras +[USB\VID_1BCF&PID_0B1D] +Plugin = dfu +Flags = detach-for-attach,ignore-upload,ignore-polltimeout + +[USB\VID_1BCF&PID_0B1E] +Plugin = dfu +Flags = detach-for-attach,ignore-upload + +# Sonix USB cameras +[USB\VID_0C45&PID_636D] +Plugin = dfu +Flags = detach-for-attach,ignore-upload +[USB\VID_0C45&PID_636C] +Plugin = dfu +Flags = detach-for-attach,ignore-upload +[USB\VID_0C45&PID_636E] +Plugin = dfu +Flags = detach-for-attach,ignore-upload +[USB\VID_0C45&PID_636F] +Plugin = dfu +Flags = detach-for-attach,ignore-upload +[USB\VID_0C45&PID_6373] +Plugin = dfu +Flags = detach-for-attach,ignore-upload +[USB\VID_0C45&PID_6374] +Plugin = dfu +Flags = detach-for-attach,ignore-upload + +# FPC Fingerprint Reader +[USB\VID_10A5&PID_FFE0] +DfuForceVersion = 0x0 +[USB\VID_10A5&PID_FFE1] +DfuForceVersion = 0x0 +[USB\VID_10A5&PID_9800] +DfuForceVersion = 0x0 diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-common.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-common.c new file mode 100644 index 0000000000000000000000000000000000000000..aac683ee29f39a1369a341397dcdbc7a86e21d9a --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-common.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-dfu-common.h" + +/** + * fu_dfu_state_to_string: + * @state: a #FuDfuState, e.g. %FU_DFU_STATE_DFU_MANIFEST + * + * Converts an enumerated value to a string. + * + * Returns: a string + **/ +const gchar * +fu_dfu_state_to_string(FuDfuState state) +{ + if (state == FU_DFU_STATE_APP_IDLE) + return "appIDLE"; + if (state == FU_DFU_STATE_APP_DETACH) + return "appDETACH"; + if (state == FU_DFU_STATE_DFU_IDLE) + return "dfuIDLE"; + if (state == FU_DFU_STATE_DFU_DNLOAD_SYNC) + return "dfuDNLOAD-SYNC"; + if (state == FU_DFU_STATE_DFU_DNBUSY) + return "dfuDNBUSY"; + if (state == FU_DFU_STATE_DFU_DNLOAD_IDLE) + return "dfuDNLOAD-IDLE"; + if (state == FU_DFU_STATE_DFU_MANIFEST_SYNC) + return "dfuMANIFEST-SYNC"; + if (state == FU_DFU_STATE_DFU_MANIFEST) + return "dfuMANIFEST"; + if (state == FU_DFU_STATE_DFU_MANIFEST_WAIT_RESET) + return "dfuMANIFEST-WAIT-RESET"; + if (state == FU_DFU_STATE_DFU_UPLOAD_IDLE) + return "dfuUPLOAD-IDLE"; + if (state == FU_DFU_STATE_DFU_ERROR) + return "dfuERROR"; + return NULL; +} + +/** + * fu_dfu_status_to_string: + * @status: a #FuDfuStatus, e.g. %FU_DFU_STATUS_ERR_ERASE + * + * Converts an enumerated value to a string. + * + * Returns: a string + **/ +const gchar * +fu_dfu_status_to_string(FuDfuStatus status) +{ + if (status == FU_DFU_STATUS_OK) + return "OK"; + if (status == FU_DFU_STATUS_ERR_TARGET) + return "errTARGET"; + if (status == FU_DFU_STATUS_ERR_FILE) + return "errFILE"; + if (status == FU_DFU_STATUS_ERR_WRITE) + return "errwrite"; + if (status == FU_DFU_STATUS_ERR_ERASE) + return "errERASE"; + if (status == FU_DFU_STATUS_ERR_CHECK_ERASED) + return "errCHECK_ERASED"; + if (status == FU_DFU_STATUS_ERR_PROG) + return "errPROG"; + if (status == FU_DFU_STATUS_ERR_VERIFY) + return "errVERIFY"; + if (status == FU_DFU_STATUS_ERR_ADDRESS) + return "errADDRESS"; + if (status == FU_DFU_STATUS_ERR_NOTDONE) + return "errNOTDONE"; + if (status == FU_DFU_STATUS_ERR_FIRMWARE) + return "errFIRMWARE"; + if (status == FU_DFU_STATUS_ERR_VENDOR) + return "errVENDOR"; + if (status == FU_DFU_STATUS_ERR_USBR) + return "errUSBR"; + if (status == FU_DFU_STATUS_ERR_POR) + return "errPOR"; + if (status == FU_DFU_STATUS_ERR_UNKNOWN) + return "errUNKNOWN"; + if (status == FU_DFU_STATUS_ERR_STALLDPKT) + return "errSTALLDPKT"; + return NULL; +} + +/** + * fu_dfu_utils_bytes_join_array: + * @chunks: (element-type GBytes): bytes + * + * Creates a monolithic block of memory from an array of #GBytes. + * + * Returns: (transfer full): a new GBytes + **/ +GBytes * +fu_dfu_utils_bytes_join_array(GPtrArray *chunks) +{ + gsize total_size = 0; + guint32 offset = 0; + guint8 *buffer; + + /* get the size of all the chunks */ + for (guint i = 0; i < chunks->len; i++) { + GBytes *chunk_tmp = g_ptr_array_index(chunks, i); + total_size += g_bytes_get_size(chunk_tmp); + } + + /* copy them into a buffer */ + buffer = g_malloc0(total_size); + for (guint i = 0; i < chunks->len; i++) { + const guint8 *chunk_data; + gsize chunk_size = 0; + GBytes *chunk_tmp = g_ptr_array_index(chunks, i); + chunk_data = g_bytes_get_data(chunk_tmp, &chunk_size); + if (chunk_size == 0) + continue; + memcpy(buffer + offset, chunk_data, chunk_size); + offset += chunk_size; + } + return g_bytes_new_take(buffer, total_size); +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-common.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-common.h new file mode 100644 index 0000000000000000000000000000000000000000..ee07843d17fda8236dacc1d3808d227bc3e3f498 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-common.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FuDfuRequest: + * @FU_DFU_REQUEST_DETACH: Detach + * @FU_DFU_REQUEST_DNLOAD: Download host-to-device + * @FU_DFU_REQUEST_UPLOAD: Upload device-to-host + * @FU_DFU_REQUEST_GETSTATUS: Get the device status + * @FU_DFU_REQUEST_CLRSTATUS: Clear the device status + * @FU_DFU_REQUEST_GETSTATE: Get the last set state + * @FU_DFU_REQUEST_ABORT: Abort the current transfer + * + * The DFU request kinds. + **/ +typedef enum { + FU_DFU_REQUEST_DETACH = 0x00, + FU_DFU_REQUEST_DNLOAD = 0x01, + FU_DFU_REQUEST_UPLOAD = 0x02, + FU_DFU_REQUEST_GETSTATUS = 0x03, + FU_DFU_REQUEST_CLRSTATUS = 0x04, + FU_DFU_REQUEST_GETSTATE = 0x05, + FU_DFU_REQUEST_ABORT = 0x06, + /*< private >*/ + FU_DFU_REQUEST_LAST +} FuDfuRequest; + +/** + * FuDfuStatus: + * @FU_DFU_STATUS_OK: No error condition is present + * @FU_DFU_STATUS_ERR_TARGET: File is not targeted for use by this device + * @FU_DFU_STATUS_ERR_FILE: File is for this device but fails a verification + *test + * @FU_DFU_STATUS_ERR_WRITE: Device is unable to write memory + * @FU_DFU_STATUS_ERR_ERASE: Memory erase function failed + * @FU_DFU_STATUS_ERR_CHECK_ERASED: Memory erase check failed + * @FU_DFU_STATUS_ERR_PROG: Program memory function failed + * @FU_DFU_STATUS_ERR_VERIFY: Programmed memory failed verification + * @FU_DFU_STATUS_ERR_ADDRESS: Cannot program memory due to received address that + *isout of range + * @FU_DFU_STATUS_ERR_NOTDONE: Received DFU_DNLOAD with wLength = 0 but data is + *incomplete + * @FU_DFU_STATUS_ERR_FIRMWARE: Device firmware is corrupt + * @FU_DFU_STATUS_ERR_VENDOR: iString indicates a vendor-specific error + * @FU_DFU_STATUS_ERR_USBR: Device detected unexpected USB reset signaling + * @FU_DFU_STATUS_ERR_POR: Device detected unexpected power on reset + * @FU_DFU_STATUS_ERR_UNKNOWN: Something unexpected went wrong + * @FU_DFU_STATUS_ERR_STALLDPKT: Device stalled an unexpected request + * + * The status enumerated kind. + **/ +typedef enum { + FU_DFU_STATUS_OK = 0x00, + FU_DFU_STATUS_ERR_TARGET = 0x01, + FU_DFU_STATUS_ERR_FILE = 0x02, + FU_DFU_STATUS_ERR_WRITE = 0x03, + FU_DFU_STATUS_ERR_ERASE = 0x04, + FU_DFU_STATUS_ERR_CHECK_ERASED = 0x05, + FU_DFU_STATUS_ERR_PROG = 0x06, + FU_DFU_STATUS_ERR_VERIFY = 0x07, + FU_DFU_STATUS_ERR_ADDRESS = 0x08, + FU_DFU_STATUS_ERR_NOTDONE = 0x09, + FU_DFU_STATUS_ERR_FIRMWARE = 0x0a, + FU_DFU_STATUS_ERR_VENDOR = 0x0b, + FU_DFU_STATUS_ERR_USBR = 0x0c, + FU_DFU_STATUS_ERR_POR = 0x0d, + FU_DFU_STATUS_ERR_UNKNOWN = 0x0e, + FU_DFU_STATUS_ERR_STALLDPKT = 0x0f, + /*< private >*/ + FU_DFU_STATUS_LAST +} FuDfuStatus; + +/** + * FuDfuState: + * @FU_DFU_STATE_APP_IDLE: State 0 + * @FU_DFU_STATE_APP_DETACH: State 1 + * @FU_DFU_STATE_DFU_IDLE: State 2 + * @FU_DFU_STATE_DFU_DNLOAD_SYNC: State 3 + * @FU_DFU_STATE_DFU_DNBUSY: State 4 + * @FU_DFU_STATE_DFU_DNLOAD_IDLE: State 5 + * @FU_DFU_STATE_DFU_MANIFEST_SYNC: State 6 + * @FU_DFU_STATE_DFU_MANIFEST: State 7 + * @FU_DFU_STATE_DFU_MANIFEST_WAIT_RESET: State 8 + * @FU_DFU_STATE_DFU_UPLOAD_IDLE: State 9 + * @FU_DFU_STATE_DFU_ERROR: State 10 + * + * The state enumerated kind. + **/ +typedef enum { + FU_DFU_STATE_APP_IDLE = 0x00, + FU_DFU_STATE_APP_DETACH = 0x01, + FU_DFU_STATE_DFU_IDLE = 0x02, + FU_DFU_STATE_DFU_DNLOAD_SYNC = 0x03, + FU_DFU_STATE_DFU_DNBUSY = 0x04, + FU_DFU_STATE_DFU_DNLOAD_IDLE = 0x05, + FU_DFU_STATE_DFU_MANIFEST_SYNC = 0x06, + FU_DFU_STATE_DFU_MANIFEST = 0x07, + FU_DFU_STATE_DFU_MANIFEST_WAIT_RESET = 0x08, + FU_DFU_STATE_DFU_UPLOAD_IDLE = 0x09, + FU_DFU_STATE_DFU_ERROR = 0x0a, + /*< private >*/ + FU_DFU_STATE_LAST +} FuDfuState; + +/** + * FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD: + * + * Can download from host->device. + */ +#define FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD (1ull << 0) +/** + * FU_DFU_DEVICE_FLAG_CAN_UPLOAD: + * + * Can upload from device->host. + */ +#define FU_DFU_DEVICE_FLAG_CAN_UPLOAD (1ull << 1) +/** + * FU_DFU_DEVICE_FLAG_MANIFEST_TOL: + * + * Can answer GetStatus in manifest. + */ +#define FU_DFU_DEVICE_FLAG_MANIFEST_TOL (1ull << 2) +/** + * FU_DFU_DEVICE_FLAG_WILL_DETACH: + * + * Will self-detach. + */ +#define FU_DFU_DEVICE_FLAG_WILL_DETACH (1ull << 3) +/** + * FU_DFU_DEVICE_FLAG_CAN_ACCELERATE: + * + * Use a larger transfer size for speed. + */ +#define FU_DFU_DEVICE_FLAG_CAN_ACCELERATE (1ull << 7) + +/** + * FU_DFU_DEVICE_FLAG_ATTACH_EXTRA_RESET: + * + * Device needs resetting twice for attach. + */ +#define FU_DFU_DEVICE_FLAG_ATTACH_EXTRA_RESET (1ull << (8 + 0)) +/** + * FU_DFU_DEVICE_FLAG_ATTACH_UPLOAD_DOWNLOAD: + * + * An upload or download is required for attach. + */ +#define FU_DFU_DEVICE_FLAG_ATTACH_UPLOAD_DOWNLOAD (1ull << (8 + 1)) +/** + * FU_DFU_DEVICE_FLAG_FORCE_DFU_MODE: + * + * Force DFU mode. + */ +#define FU_DFU_DEVICE_FLAG_FORCE_DFU_MODE (1ull << (8 + 2)) +/** + * FU_DFU_DEVICE_FLAG_IGNORE_POLLTIMEOUT: + * + * Ignore the device download timeout. + */ +#define FU_DFU_DEVICE_FLAG_IGNORE_POLLTIMEOUT (1ull << (8 + 3)) +/** + * FU_DFU_DEVICE_FLAG_IGNORE_RUNTIME: + * + * Device has broken DFU runtime support. + */ +#define FU_DFU_DEVICE_FLAG_IGNORE_RUNTIME (1ull << (8 + 4)) +/** + * FU_DFU_DEVICE_FLAG_IGNORE_UPLOAD: + * + * Uploading from the device is broken. + */ +#define FU_DFU_DEVICE_FLAG_IGNORE_UPLOAD (1ull << (8 + 5)) +/** + * FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME: + * + * No DFU runtime interface is provided. + */ +#define FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME (1ull << (8 + 6)) +/** + * FU_DFU_DEVICE_FLAG_NO_GET_STATUS_UPLOAD: + * + * Do not do GetStatus when uploading. + */ +#define FU_DFU_DEVICE_FLAG_NO_GET_STATUS_UPLOAD (1ull << (8 + 7)) +/** + * FU_DFU_DEVICE_FLAG_NO_PID_CHANGE: + * + * Accept the same VID:PID when changing modes. + */ +#define FU_DFU_DEVICE_FLAG_NO_PID_CHANGE (1ull << (8 + 8)) +/** + * FU_DFU_DEVICE_FLAG_USE_ANY_INTERFACE: + * + * Use any interface for DFU. + */ +#define FU_DFU_DEVICE_FLAG_USE_ANY_INTERFACE (1ull << (8 + 9)) +/** + * FU_DFU_DEVICE_FLAG_USE_ATMEL_AVR: + * + * Device uses the ATMEL bootloader. + */ +#define FU_DFU_DEVICE_FLAG_USE_ATMEL_AVR (1ull << (8 + 10)) +/** + * FU_DFU_DEVICE_FLAG_USE_PROTOCOL_ZERO: + * + * Fix up the protocol number. + */ +#define FU_DFU_DEVICE_FLAG_USE_PROTOCOL_ZERO (1ull << (8 + 11)) +/** + * FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL: + * + * Use a legacy protocol version. + */ +#define FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL (1ull << (8 + 12)) +/** + * FU_DFU_DEVICE_FLAG_DETACH_FOR_ATTACH: + * + * Requires a FU_DFU_REQUEST_DETACH to attach. + */ +#define FU_DFU_DEVICE_FLAG_DETACH_FOR_ATTACH (1ull << (8 + 13)) +/** + * FU_DFU_DEVICE_FLAG_ABSENT_SECTOR_SIZE: + * + * In absence of sector size, assume byte. + */ +#define FU_DFU_DEVICE_FLAG_ABSENT_SECTOR_SIZE (1ull << (8 + 14)) +/** + * FU_DFU_DEVICE_FLAG_MANIFEST_POLL: + * + * Requires polling via GetStatus in dfuManifest state. + */ +#define FU_DFU_DEVICE_FLAG_MANIFEST_POLL (1ull << (8 + 15)) +/** + * FU_DFU_DEVICE_FLAG_NO_BUS_RESET_ATTACH: + * + * Do not require a bus reset to attach to normal. + */ +#define FU_DFU_DEVICE_FLAG_NO_BUS_RESET_ATTACH (1ull << (8 + 16)) +/** + * FU_DFU_DEVICE_FLAG_GD32: + * + * Uses the slightly weird GD32 variant of DFU. + */ +#define FU_DFU_DEVICE_FLAG_GD32 (1ull << (8 + 17)) +/** + * FU_DFU_DEVICE_FLAG_ALLOW_ZERO_POLLTIMEOUT: + * + * Allows the zero bwPollTimeout from GetStatus in dfuDNLOAD-SYNC state. + */ +#define FU_DFU_DEVICE_FLAG_ALLOW_ZERO_POLLTIMEOUT (1ull << (8 + 18)) +/** + * FU_DFU_DEVICE_FLAG_INDEX_FORCE_DETACH: + * + * Requires Force Detach in wIndex to bypass status checking. + */ +#define FU_DFU_DEVICE_FLAG_INDEX_FORCE_DETACH (1ull << (8 + 19)) + +const gchar * +fu_dfu_state_to_string(FuDfuState state); +const gchar * +fu_dfu_status_to_string(FuDfuStatus status); + +/* helpers */ +GBytes * +fu_dfu_utils_bytes_join_array(GPtrArray *chunks); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-device.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-device.c new file mode 100644 index 0000000000000000000000000000000000000000..66e4f201401aaa1ec31ba9c85796e269a78374e3 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-device.c @@ -0,0 +1,1769 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/** + * FuDfuDevice: + * + * This object allows two things: + * + * - Downloading from the host to the device, optionally with + * verification using a DFU or DfuSe firmware file. + * + * - Uploading from the device to the host to a DFU or DfuSe firmware + * file. The file format is chosen automatically, with DfuSe being + * chosen if the device contains more than one target. + * + * See also: [class@FuDfuTarget], [class@FuDfuseFirmware] + */ + +/** + * 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. If zero, then DFU functionality is disabled. + * + * Since: 1.0.1 + */ +#define FU_QUIRKS_DFU_FORCE_VERSION "DfuForceVersion" + +#define DFU_DEVICE_DNLOAD_TIMEOUT_DEFAULT 5 /* ms */ + +#include "config.h" + +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-device.h" +#include "fu-dfu-target-avr.h" +#include "fu-dfu-target-private.h" /* waive-pre-commit */ +#include "fu-dfu-target-stm.h" + +static void +fu_dfu_device_finalize(GObject *object); + +typedef struct { + FuDfuState state; + FuDfuStatus status; + GPtrArray *targets; + gboolean done_upload_or_download; + gboolean claimed_interface; + gchar *chip_id; + guint16 version; + guint16 force_version; + guint16 force_transfer_size; + guint16 runtime_pid; + guint16 runtime_vid; + guint16 runtime_release; + guint16 transfer_size; + guint8 iface_number; + guint dnload_timeout; + guint timeout_ms; +} FuDfuDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuDfuDevice, fu_dfu_device, FU_TYPE_USB_DEVICE) +#define GET_PRIVATE(o) (fu_dfu_device_get_instance_private(o)) + +static void +fu_dfu_device_set_state(FuDfuDevice *self, FuDfuState state); + +static void +fu_dfu_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append(str, idt, "State", fu_dfu_state_to_string(priv->state)); + fu_string_append(str, idt, "Status", fu_dfu_status_to_string(priv->status)); + fu_string_append_kb(str, idt, "DoneUploadOrDownload", priv->done_upload_or_download); + fu_string_append_kb(str, idt, "ClaimedInterface", priv->claimed_interface); + if (priv->chip_id != NULL) + fu_string_append(str, idt, "ChipId", priv->chip_id); + fu_string_append_kx(str, idt, "Version", priv->version); + if (priv->force_version != G_MAXUINT16) + fu_string_append_kx(str, idt, "ForceVersion", priv->force_version); + if (priv->force_transfer_size != 0x0) { + fu_string_append_kx(str, idt, "ForceTransferSize", priv->force_transfer_size); + } + fu_string_append_kx(str, idt, "RuntimePid", priv->runtime_pid); + fu_string_append_kx(str, idt, "RuntimeVid", priv->runtime_vid); + fu_string_append_kx(str, idt, "RuntimeRelease", priv->runtime_release); + fu_string_append_kx(str, idt, "TransferSize", priv->transfer_size); + fu_string_append_kx(str, idt, "IfaceNumber", priv->iface_number); + fu_string_append_kx(str, idt, "DnloadTimeout", priv->dnload_timeout); + fu_string_append_kx(str, idt, "TimeoutMs", priv->timeout_ms); + + for (guint i = 0; i < priv->targets->len; i++) { + FuDfuTarget *target = g_ptr_array_index(priv->targets, i); + fu_device_add_string(FU_DEVICE(target), idt + 1, str); + } +} + +/** + * fu_dfu_device_get_transfer_size: + * @device: a USB device + * + * Gets the transfer size in bytes. + * + * Returns: packet size, or 0 for unknown + **/ +guint16 +fu_dfu_device_get_transfer_size(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + return priv->transfer_size; +} + +/** + * fu_dfu_device_get_version: + * @self: a #FuDfuDevice + * + * Gets the DFU specification version supported by the device. + * + * Returns: integer, or 0 for unknown, e.g. %FU_DFU_FIRMARE_VERSION_DFU_1_1 + **/ +guint16 +fu_dfu_device_get_version(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + return priv->version; +} + +/** + * fu_dfu_device_get_download_timeout: + * @self: a #FuDfuDevice + * + * Gets the download timeout in ms. + * + * Returns: delay, or 0 for unknown + **/ +guint +fu_dfu_device_get_download_timeout(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0); + return priv->dnload_timeout; +} + +/** + * fu_dfu_device_set_transfer_size: + * @self: a #FuDfuDevice + * @transfer_size: maximum packet size + * + * Sets the transfer size in bytes. + **/ +void +fu_dfu_device_set_transfer_size(FuDfuDevice *self, guint16 transfer_size) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_DEVICE(self)); + priv->transfer_size = transfer_size; +} + +typedef struct __attribute__((packed)) { + guint8 bLength; + guint8 bDescriptorType; + guint8 bmAttributes; + guint16 wDetachTimeOut; + guint16 wTransferSize; + guint16 bcdDFUVersion; +} DfuFuncDescriptor; + +static gboolean +fu_dfu_device_parse_iface_data(FuDfuDevice *self, GBytes *iface_data, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + DfuFuncDescriptor desc = {0x0}; + const guint8 *buf; + gsize sz; + + /* parse the functional descriptor */ + 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); + desc.bcdDFUVersion = FU_DFU_FIRMARE_VERSION_DFU_1_1; + } else { + g_autoptr(GString) bufstr = g_string_new(NULL); + for (gsize i = 0; i < sz; i++) + g_string_append_printf(bufstr, "%02x ", buf[i]); + if (bufstr->len > 0) + g_string_truncate(bufstr, bufstr->len - 1); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "interface found, but not the correct length for " + "functional data: %" G_GSIZE_FORMAT " bytes: %s", + sz, + bufstr->str); + return FALSE; + } + + /* get transfer size and version */ + priv->transfer_size = GUINT16_FROM_LE(desc.wTransferSize); + priv->version = GUINT16_FROM_LE(desc.bcdDFUVersion); + + /* ST-specific */ + if (priv->version == FU_DFU_FIRMARE_VERSION_DFUSE && + desc.bmAttributes & FU_DFU_DEVICE_FLAG_CAN_ACCELERATE) + priv->transfer_size = 0x1000; + + /* get attributes about the DFU operation */ + fu_device_add_private_flag(FU_DEVICE(self), desc.bmAttributes); + return TRUE; +} + +static void +fu_dfu_device_guess_state_from_iface(FuDfuDevice *self, GUsbInterface *iface) +{ + /* some devices use the wrong interface */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_FORCE_DFU_MODE)) { + g_debug("quirking device into DFU mode"); + fu_dfu_device_set_state(self, FU_DFU_STATE_DFU_IDLE); + return; + } + + /* runtime */ + if (g_usb_interface_get_protocol(iface) == 0x01) { + fu_dfu_device_set_state(self, FU_DFU_STATE_APP_IDLE); + return; + } + + /* DFU */ + if (g_usb_interface_get_protocol(iface) == 0x02) { + fu_dfu_device_set_state(self, FU_DFU_STATE_DFU_IDLE); + return; + } + g_warning("unable to guess initial device state from interface %u", + g_usb_interface_get_protocol(iface)); +} + +static gboolean +fu_dfu_device_add_targets(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autoptr(GPtrArray) ifaces = NULL; + + /* disabled using quirk */ + if (priv->force_version == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ignoring device as DFU version set to 0x0"); + return FALSE; + } + + /* add all DFU-capable targets */ + ifaces = g_usb_device_get_interfaces(usb_device, error); + if (ifaces == NULL) + return FALSE; + g_ptr_array_set_size(priv->targets, 0); + for (guint i = 0; i < ifaces->len; i++) { + GBytes *iface_data = NULL; + FuDfuTarget *target; + g_autoptr(GError) error_local = NULL; + + GUsbInterface *iface = g_ptr_array_index(ifaces, i); + + /* some devices don't use the right class and subclass */ + if (!fu_device_has_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_USE_ANY_INTERFACE)) { + if (g_usb_interface_get_class(iface) != + G_USB_DEVICE_CLASS_APPLICATION_SPECIFIC) + continue; + if (g_usb_interface_get_subclass(iface) != 0x01) + continue; + } + /* parse any interface data */ + iface_data = g_usb_interface_get_extra(iface); + if (g_bytes_get_size(iface_data) > 0) { + if (!fu_dfu_device_parse_iface_data(self, iface_data, &error_local)) { + g_warning("failed to parse interface data for %04x:%04x: %s", + g_usb_device_get_vid(usb_device), + g_usb_device_get_pid(usb_device), + error_local->message); + continue; + } + } else { + fu_device_add_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD | + FU_DFU_DEVICE_FLAG_CAN_UPLOAD); + } + + /* fix up the version */ + if (priv->force_version != G_MAXUINT16) + priv->version = priv->force_version; + if (priv->version == FU_DFU_FIRMARE_VERSION_DFU_1_0 || + priv->version == FU_DFU_FIRMARE_VERSION_DFU_1_1) { + g_debug("DFU v1.1"); + } else if (priv->version == FU_DFU_FIRMARE_VERSION_ATMEL_AVR) { + g_debug("AVR-DFU support"); + priv->version = FU_DFU_FIRMARE_VERSION_ATMEL_AVR; + } else if (priv->version == FU_DFU_FIRMARE_VERSION_DFUSE) { + g_debug("STM-DFU support"); + } else if (priv->version == 0x0101) { + g_debug("DFU v1.1 assumed"); + priv->version = FU_DFU_FIRMARE_VERSION_DFU_1_1; + } else { + g_warning("DFU version 0x%04x invalid, v1.1 assumed", priv->version); + priv->version = FU_DFU_FIRMARE_VERSION_DFU_1_1; + } + + /* set expected protocol */ + if (priv->version == FU_DFU_FIRMARE_VERSION_DFUSE) { + fu_device_add_protocol(FU_DEVICE(self), "com.st.dfuse"); + } else { + fu_device_add_protocol(FU_DEVICE(self), "org.usb.dfu"); + } + + /* fix up the transfer size */ + if (priv->force_transfer_size != 0x0) { + priv->transfer_size = priv->force_transfer_size; + g_debug("forcing DFU transfer size 0x%04x bytes", priv->transfer_size); + } else if (priv->transfer_size == 0xffff) { + priv->transfer_size = 0x0400; + g_debug("DFU transfer size unspecified, guessing"); + } else if (priv->transfer_size == 0x0) { + g_warning("DFU transfer size invalid, using default"); + priv->transfer_size = 64; + } else { + g_debug("using DFU transfer size 0x%04x bytes", priv->transfer_size); + } + + /* create a target of the required type */ + switch (priv->version) { + case FU_DFU_FIRMARE_VERSION_DFUSE: + target = fu_dfu_target_stm_new(); + break; + case FU_DFU_FIRMARE_VERSION_ATMEL_AVR: + target = fu_dfu_target_avr_new(); + break; + default: + target = fu_dfu_target_new(); + break; + } + fu_device_set_proxy(FU_DEVICE(target), FU_DEVICE(self)); + fu_dfu_target_set_alt_idx(target, g_usb_interface_get_index(iface)); + fu_dfu_target_set_alt_setting(target, g_usb_interface_get_alternate(iface)); + + /* add target */ + priv->iface_number = g_usb_interface_get_number(iface); + g_ptr_array_add(priv->targets, target); + fu_dfu_device_guess_state_from_iface(self, iface); + } + + /* save for reset */ + if (priv->state == FU_DFU_STATE_APP_IDLE || + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_PID_CHANGE)) { + 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); + } + + /* the device has no DFU runtime, so cheat */ + if (priv->targets->len == 0 && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) { + g_debug("no DFU runtime, so faking device"); + fu_dfu_device_set_state(self, FU_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); + fu_device_add_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD | + FU_DFU_DEVICE_FLAG_CAN_UPLOAD); + return TRUE; + } + + /* no targets */ + if (priv->targets->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no DFU interfaces"); + return FALSE; + } + + /* the device upload is broken */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_IGNORE_UPLOAD)) + fu_device_remove_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_CAN_UPLOAD); + + return TRUE; +} + +/** + * fu_dfu_device_set_timeout: + * @self: a #FuDfuDevice + * @timeout_ms: the timeout in ms + * + * Sets the USB timeout to use when contacting the USB device. + **/ +void +fu_dfu_device_set_timeout(FuDfuDevice *self, guint timeout_ms) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_DEVICE(self)); + priv->timeout_ms = timeout_ms; +} + +/** + * fu_dfu_device_get_timeout: + * @device: a #FuDfuDevice + * + * Gets the device timeout. + * + * Returns: enumerated timeout in ms + **/ +guint +fu_dfu_device_get_timeout(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0); + return priv->timeout_ms; +} + +/** + * fu_dfu_device_get_state: + * @device: a #FuDfuDevice + * + * Gets the device state. + * + * Returns: enumerated state, e.g. %FU_DFU_STATE_DFU_UPLOAD_IDLE + **/ +FuDfuState +fu_dfu_device_get_state(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0); + return priv->state; +} + +/** + * fu_dfu_device_get_status: + * @device: a USB device + * + * Gets the device status. + * + * Returns: enumerated status, e.g. %FU_DFU_STATUS_ERR_ADDRESS + **/ +FuDfuStatus +fu_dfu_device_get_status(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0); + return priv->status; +} + +/** + * fu_dfu_device_new: + * + * Creates a new DFU device object. + * + * Returns: a new #FuDfuDevice + **/ +FuDfuDevice * +fu_dfu_device_new(FuContext *ctx, GUsbDevice *usb_device) +{ + FuDfuDevice *self; + self = g_object_new(FU_TYPE_DFU_DEVICE, "usb-device", usb_device, "context", ctx, NULL); + return self; +} + +/** + * fu_dfu_device_get_target_by_alt_setting: + * @self: a #FuDfuDevice + * @alt_setting: the setting used to find + * @error: (nullable): optional return location for an error + * + * Gets a target with a specific alternative setting. + * + * Returns: (transfer full): a #FuDfuTarget, or %NULL + **/ +FuDfuTarget * +fu_dfu_device_get_target_by_alt_setting(FuDfuDevice *self, guint8 alt_setting, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find by ID */ + for (guint i = 0; i < priv->targets->len; i++) { + FuDfuTarget *target = g_ptr_array_index(priv->targets, i); + if (fu_dfu_target_get_alt_setting(target) == alt_setting) + return g_object_ref(target); + } + + /* failed */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No target with alt-setting %i", + alt_setting); + return NULL; +} + +/** + * fu_dfu_device_get_target_by_alt_name: + * @self: a #FuDfuDevice + * @alt_name: the name used to find + * @error: (nullable): optional return location for an error + * + * Gets a target with a specific alternative name. + * + * Returns: (transfer full): a #FuDfuTarget, or %NULL + **/ +FuDfuTarget * +fu_dfu_device_get_target_by_alt_name(FuDfuDevice *self, const gchar *alt_name, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find by ID */ + for (guint i = 0; i < priv->targets->len; i++) { + FuDfuTarget *target = g_ptr_array_index(priv->targets, i); + if (g_strcmp0(fu_device_get_logical_id(FU_DEVICE(target)), alt_name) == 0) + return g_object_ref(target); + } + + /* failed */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No target with alt-name %s", + alt_name); + return NULL; +} + +/** + * fu_dfu_device_get_runtime_vid: + * @self: a #FuDfuDevice + * + * Gets the runtime vendor ID. + * + * Returns: vendor ID, or 0xffff for unknown + **/ +guint16 +fu_dfu_device_get_runtime_vid(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + return priv->runtime_vid; +} + +/** + * fu_dfu_device_get_runtime_pid: + * @self: a #FuDfuDevice + * + * Gets the runtime product ID. + * + * Returns: product ID, or 0xffff for unknown + **/ +guint16 +fu_dfu_device_get_runtime_pid(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + return priv->runtime_pid; +} + +/** + * fu_dfu_device_get_runtime_release: + * @self: a #FuDfuDevice + * + * Gets the runtime release number in BCD format. + * + * Returns: release number, or 0xffff for unknown + **/ +guint16 +fu_dfu_device_get_runtime_release(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + return priv->runtime_release; +} + +const gchar * +fu_dfu_device_get_chip_id(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), NULL); + return priv->chip_id; +} + +void +fu_dfu_device_set_chip_id(FuDfuDevice *self, const gchar *chip_id) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DFU_DEVICE(self)); + g_debug("chip ID set to: %s", chip_id); + priv->chip_id = g_strdup(chip_id); +} + +static void +fu_dfu_device_set_state(FuDfuDevice *self, FuDfuState state) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + if (priv->state == state) + return; + priv->state = state; + + /* set bootloader status */ + if (state == FU_DFU_STATE_APP_IDLE || state == FU_DFU_STATE_APP_DETACH) { + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } +} + +static void +fu_dfu_device_set_status(FuDfuDevice *self, FuDfuStatus status) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + if (priv->status == status) + return; + priv->status = status; +} + +gboolean +fu_dfu_device_ensure_interface(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autoptr(GError) error_local = NULL; + + /* already done */ + if (priv->claimed_interface) + return TRUE; + + /* nothing set */ + if (priv->iface_number == 0xff) + return TRUE; + + /* claim, without detaching kernel driver */ + if (!g_usb_device_claim_interface(usb_device, + (gint)priv->iface_number, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot claim interface %i: %s", + priv->iface_number, + error_local->message); + return FALSE; + } + + /* success */ + priv->claimed_interface = TRUE; + return TRUE; +} + +/** + * fu_dfu_device_refresh_and_clear: + * @self: a #FuDfuDevice + * @error: (nullable): optional return location for an error + * + * Refreshes the cached properties on the DFU device. If there are any transers + * in progress they are cancelled, and if there are any pending errors they are + * cancelled. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_device_refresh_and_clear(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + if (!fu_dfu_device_refresh(self, error)) + return FALSE; + switch (priv->state) { + case FU_DFU_STATE_DFU_UPLOAD_IDLE: + case FU_DFU_STATE_DFU_DNLOAD_IDLE: + case FU_DFU_STATE_DFU_DNLOAD_SYNC: + g_debug("aborting transfer %s", fu_dfu_status_to_string(priv->status)); + if (!fu_dfu_device_abort(self, error)) + return FALSE; + break; + case FU_DFU_STATE_DFU_ERROR: + g_debug("clearing error %s", fu_dfu_status_to_string(priv->status)); + if (!fu_dfu_device_clear_status(self, error)) + return FALSE; + break; + default: + break; + } + return TRUE; +} + +/** + * fu_dfu_device_refresh: + * @self: a #FuDfuDevice + * @error: (nullable): optional return location for an error + * + * Refreshes the cached properties on the DFU device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_device_refresh(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_length = 0; + guint8 buf[6]; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* the device has no DFU runtime, so cheat */ + if (priv->state == FU_DFU_STATE_APP_IDLE && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) + return TRUE; + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(self, error)) + return FALSE; + + /* Device that cannot communicate via the USB after the + * Manifestation phase indicated this limitation to the + * host by clearing bmAttributes bit bitManifestationTolerant. + * so we assume the operation was successful */ + if (priv->state == FU_DFU_STATE_DFU_MANIFEST && + !fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_MANIFEST_TOL)) + return TRUE; + + 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, + FU_DFU_REQUEST_GETSTATUS, + 0, + priv->iface_number, + buf, + sizeof(buf), + &actual_length, + priv->timeout_ms, + NULL, /* cancellable */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot get device state: %s", + error_local->message); + return FALSE; + } + if (actual_length != 6) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot get device status, invalid size: %04x", + (guint)actual_length); + return FALSE; + } + + /* some devices use the wrong state value */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_FORCE_DFU_MODE) && + fu_dfu_device_get_state(self) != FU_DFU_STATE_DFU_IDLE) { + g_debug("quirking device into DFU mode"); + fu_dfu_device_set_state(self, FU_DFU_STATE_DFU_IDLE); + } else { + fu_dfu_device_set_state(self, buf[4]); + } + + /* status or state changed */ + fu_dfu_device_set_status(self, buf[0]); + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_IGNORE_POLLTIMEOUT)) { + priv->dnload_timeout = DFU_DEVICE_DNLOAD_TIMEOUT_DEFAULT; + } else { + priv->dnload_timeout = fu_memread_uint24(&buf[1], G_LITTLE_ENDIAN); + if (priv->dnload_timeout == 0 && + !fu_device_has_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_ALLOW_ZERO_POLLTIMEOUT)) { + priv->dnload_timeout = DFU_DEVICE_DNLOAD_TIMEOUT_DEFAULT; + g_debug("no dnload-timeout, using default of %ums", priv->dnload_timeout); + } + } + g_debug("refreshed status=%s and state=%s (dnload=%u)", + fu_dfu_status_to_string(priv->status), + fu_dfu_state_to_string(priv->state), + priv->dnload_timeout); + return TRUE; +} + +static gboolean +fu_dfu_device_request_detach(FuDfuDevice *self, FuProgress *progress, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + const guint16 timeout_reset_ms = 1000; + guint16 ctrl_setup_index = priv->iface_number; + g_autoptr(GError) error_local = NULL; + + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_INDEX_FORCE_DETACH)) + ctrl_setup_index |= 0x01u << 8; + + 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, + FU_DFU_REQUEST_DETACH, + timeout_reset_ms, + ctrl_setup_index, + NULL, + 0, + NULL, + priv->timeout_ms, + NULL, /* cancellable */ + &error_local)) { + /* some devices just reboot and stall the endpoint :/ */ + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NOT_SUPPORTED) || + g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_FAILED)) { + g_debug("ignoring while detaching: %s", error_local->message); + } else { + /* refresh the error code */ + fu_dfu_device_error_fixup(self, &error_local); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot detach device: %s", + error_local->message); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_dfu_device_reload(FuDevice *device, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + return fu_dfu_device_refresh_and_clear(self, error); +} + +static gboolean +fu_dfu_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* already in DFU mode */ + if (!fu_dfu_device_refresh_and_clear(self, error)) + return FALSE; + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* the device has no DFU runtime, so cheat */ + if (priv->state == FU_DFU_STATE_APP_IDLE && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) + return TRUE; + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(self, error)) + return FALSE; + + /* inform UI there's going to be a detach:attach */ + if (!fu_dfu_device_request_detach(self, progress, error)) + return FALSE; + + /* do a host reset */ + if (!fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_WILL_DETACH)) { + g_debug("doing device reset as host will not self-reset"); + if (!fu_dfu_device_reset(self, progress, error)) + return FALSE; + } + + /* success */ + priv->force_version = G_MAXUINT16; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +/** + * fu_dfu_device_abort: + * @self: a #FuDfuDevice + * @error: (nullable): optional return location for an error + * + * Aborts any upload or download in progress. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_device_abort(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), FALSE); + g_return_val_if_fail(G_USB_IS_DEVICE(usb_device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* the device has no DFU runtime, so cheat */ + if (priv->state == FU_DFU_STATE_APP_IDLE && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported as no DFU runtime"); + return FALSE; + } + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(self, error)) + return FALSE; + + 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, + FU_DFU_REQUEST_ABORT, + 0, + priv->iface_number, + NULL, + 0, + NULL, + priv->timeout_ms, + NULL, /* cancellable */ + &error_local)) { + /* refresh the error code */ + fu_dfu_device_error_fixup(self, &error_local); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot abort device: %s", + error_local->message); + return FALSE; + } + + return TRUE; +} + +/** + * fu_dfu_device_clear_status: + * @self: a #FuDfuDevice + * @error: (nullable): optional return location for an error + * + * Clears any error status on the DFU device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_device_clear_status(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* the device has no DFU runtime, so cheat */ + if (priv->state == FU_DFU_STATE_APP_IDLE && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported as no DFU runtime"); + return FALSE; + } + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(self, error)) + return FALSE; + + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + FU_DFU_REQUEST_CLRSTATUS, + 0, + priv->iface_number, + NULL, + 0, + NULL, + priv->timeout_ms, + NULL, /* cancellable */ + &error_local)) { + /* refresh the error code */ + fu_dfu_device_error_fixup(self, &error_local); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot clear status on the device: %s", + error_local->message); + return FALSE; + } + return TRUE; +} + +/** + * fu_dfu_device_get_interface: + * @self: a #FuDfuDevice + * + * Gets the interface number. + **/ +guint8 +fu_dfu_device_get_interface(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xff); + return priv->iface_number; +} + +/** + * fu_dfu_device_open: + * @self: a #FuDfuDevice + * @error: (nullable): optional return location for an error + * + * Opens a DFU-capable device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_device_open(FuDevice *device, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FU_IS_DFU_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* FuUsbDevice->open */ + if (!FU_DEVICE_CLASS(fu_dfu_device_parent_class)->open(device, error)) + return FALSE; + + /* the device has no DFU runtime, so cheat */ + if (priv->state == FU_DFU_STATE_APP_IDLE && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) { + fu_dfu_device_set_state(self, FU_DFU_STATE_APP_IDLE); + priv->status = FU_DFU_STATUS_OK; + } + + /* GD32VF103 encodes the serial number in UTF-8 (rather than UTF-16) + * and also uses the first two bytes as the model identifier */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_GD32)) { +#if G_USB_CHECK_VERSION(0, 3, 6) + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + const guint8 *buf; + gsize bufsz = 0; + guint16 langid = G_USB_DEVICE_LANGID_ENGLISH_UNITED_STATES; + guint8 idx = g_usb_device_get_serial_number_index(usb_device); + g_autofree gchar *chip_id = NULL; + g_autofree gchar *serial_str = NULL; + g_autoptr(GBytes) serial_blob = NULL; + serial_blob = + g_usb_device_get_string_descriptor_bytes(usb_device, idx, langid, error); + if (serial_blob == NULL) + return FALSE; + if (g_getenv("FWUPD_DFU_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "GD32 serial", serial_blob); + buf = g_bytes_get_data(serial_blob, &bufsz); + if (bufsz < 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "GD32 serial number invalid"); + return FALSE; + } + + /* ID is first two bytes */ + chip_id = g_strdup_printf("%02x%02x", buf[0], buf[1]); + fu_dfu_device_set_chip_id(self, chip_id); + + /* serial number follows */ + serial_str = g_strndup((const gchar *)buf + 2, bufsz - 2); + fu_device_set_serial(FU_DEVICE(device), serial_str); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "GUsb version too old to support GD32, " + "fwupd needs to be rebuilt against 0.3.6 or later"); + return FALSE; +#endif + } + + /* set up target ready for use */ + for (guint j = 0; j < priv->targets->len; j++) { + FuDfuTarget *target = g_ptr_array_index(priv->targets, j); + if (!fu_dfu_target_setup(target, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_dfu_device_close: + * @self: a #FuDfuDevice + * @error: (nullable): optional return location for an error + * + * Closes a DFU device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_device_close(FuDevice *device, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + + /* release interface */ + if (priv->claimed_interface) { + g_autoptr(GError) error_local = NULL; + if (!g_usb_device_release_interface(usb_device, + (gint)priv->iface_number, + 0, + &error_local)) { + if (!g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE)) { + g_warning("failed to release interface: %s", error_local->message); + } + } + priv->claimed_interface = FALSE; + } + + /* FuUsbDevice->close */ + return FU_DEVICE_CLASS(fu_dfu_device_parent_class)->close(device, error); +} + +static gboolean +fu_dfu_device_probe(FuDevice *device, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + + /* add all the targets */ + if (!fu_dfu_device_add_targets(self, error)) { + g_prefix_error(error, + "%04x:%04x is not supported: ", + g_usb_device_get_vid(usb_device), + g_usb_device_get_pid(usb_device)); + return FALSE; + } + + /* check capabilities */ + if (!fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD)) { + g_debug("%04x:%04x is missing download capability", + g_usb_device_get_vid(usb_device), + g_usb_device_get_pid(usb_device)); + } + + /* hardware from Jabra literally reboots if you try to retry a failed + * write -- there's no way to avoid blocking the daemon like this... */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_ATTACH_EXTRA_RESET)) { + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + fu_progress_sleep(progress, 10000); + } + + /* success */ + return TRUE; +} + +gboolean +fu_dfu_device_reset(FuDfuDevice *self, FuProgress *progress, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(GTimer) timer = g_timer_new(); + + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!g_usb_device_reset(fu_usb_device_get_dev(FU_USB_DEVICE(self)), &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot reset USB device: %s [%i]", + error_local->message, + error_local->code); + return FALSE; + } + g_debug("reset took %.2lfms", g_timer_elapsed(timer, NULL) * 1000); + return TRUE; +} + +static gboolean +fu_dfu_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuDfuTarget) target = NULL; + + g_return_val_if_fail(FU_IS_DFU_DEVICE(device), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* already in runtime mode */ + if (!fu_dfu_device_refresh_and_clear(self, error)) + return FALSE; + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* handle weirdness */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_DETACH_FOR_ATTACH)) { + if (!fu_dfu_device_request_detach(self, progress, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + /* handle m-stack DFU bootloaders */ + if (!priv->done_upload_or_download && + fu_device_has_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_ATTACH_UPLOAD_DOWNLOAD)) { + g_autoptr(GBytes) chunk = NULL; + g_autoptr(FuDfuTarget) target_zero = NULL; + g_debug("doing dummy upload to work around m-stack quirk"); + target_zero = fu_dfu_device_get_target_by_alt_setting(self, 0, error); + if (target_zero == NULL) + return FALSE; + chunk = fu_dfu_target_upload_chunk(target_zero, 0, 0, progress, error); + if (chunk == NULL) + return FALSE; + } + + /* get default target */ + target = fu_dfu_device_get_target_by_alt_setting(self, 0, error); + if (target == NULL) + return FALSE; + + /* normal DFU mode just needs a bus reset */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_BUS_RESET_ATTACH) && + fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_WILL_DETACH)) { + g_debug("Bus reset is not required. Device will reboot to normal"); + } else if (!fu_dfu_target_attach(target, progress, error)) { + g_prefix_error(error, "failed to attach target: "); + return FALSE; + } + + /* there is no USB runtime whatsoever */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) + return TRUE; + + /* success */ + priv->force_version = 0x0; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +/** + * fu_dfu_device_upload: + * @self: a #FuDfuDevice + * @flags: DFU target flags, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY + * @error: (nullable): optional return location for an error + * + * Uploads firmware from the target to the host. + * + * Returns: (transfer full): the uploaded firmware, or %NULL for error + **/ +FuFirmware * +fu_dfu_device_upload(FuDfuDevice *self, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + gboolean use_dfuse = FALSE; + g_autoptr(FuFirmware) firmware = NULL; + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(self, error)) + return NULL; + + /* choose the most appropriate type */ + for (guint i = 0; i < priv->targets->len; i++) { + FuDfuTarget *target = g_ptr_array_index(priv->targets, i); + if (fu_device_get_logical_id(FU_DEVICE(target)) != NULL || i > 0) { + use_dfuse = TRUE; + break; + } + } + if (use_dfuse) { + firmware = fu_dfuse_firmware_new(); + g_debug("switching to DefuSe automatically"); + } else { + firmware = fu_dfu_firmware_new(); + } + fu_dfu_firmware_set_vid(FU_DFU_FIRMWARE(firmware), priv->runtime_vid); + fu_dfu_firmware_set_pid(FU_DFU_FIRMWARE(firmware), priv->runtime_pid); + fu_dfu_firmware_set_release(FU_DFU_FIRMWARE(firmware), 0xffff); + + /* upload from each target */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, priv->targets->len); + for (guint i = 0; i < priv->targets->len; i++) { + FuDfuTarget *target; + + /* upload to target and proxy signals */ + target = g_ptr_array_index(priv->targets, i); + + /* ignore some target types */ + if (g_strcmp0(fu_device_get_name(FU_DEVICE(target)), "Option Bytes") == 0) { + g_debug("ignoring target %s", fu_device_get_name(target)); + continue; + } + if (!fu_dfu_target_upload(target, + firmware, + fu_progress_get_child(progress), + DFU_TARGET_TRANSFER_FLAG_NONE, + error)) + return NULL; + fu_progress_step_done(progress); + } + + /* do not do the dummy upload for quirked devices */ + priv->done_upload_or_download = TRUE; + + /* success */ + return g_object_ref(firmware); +} + +static gboolean +fu_dfu_device_id_compatible(guint16 id_file, guint16 id_runtime, guint16 id_dev) +{ + /* file doesn't specify */ + if (id_file == 0xffff) + return TRUE; + + /* runtime matches */ + if (id_runtime != 0xffff && id_file == id_runtime) + return TRUE; + + /* bootloader matches */ + if (id_dev != 0xffff && id_file == id_dev) + return TRUE; + + /* nothing */ + return FALSE; +} + +static gsize +fu_dfu_device_calculate_chunks_size(GPtrArray *chunks) +{ + gsize total = 0; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + total += fu_chunk_get_data_sz(chk); + } + return total; +} + +static gboolean +fu_dfu_device_download(FuDfuDevice *self, + FuFirmware *firmware, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + gboolean ret; + g_autoptr(GPtrArray) images = NULL; + guint16 firmware_pid = 0xffff; + guint16 firmware_vid = 0xffff; + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(self, error)) + return FALSE; + + /* firmware supports footer? */ + if (FU_IS_DFU_FIRMWARE(firmware)) { + firmware_vid = fu_dfu_firmware_get_vid(FU_DFU_FIRMWARE(firmware)); + firmware_pid = fu_dfu_firmware_get_pid(FU_DFU_FIRMWARE(firmware)); + } else { + flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; + flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; + } + + /* do we allow wildcard VID:PID matches */ + if ((flags & DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID) == 0) { + if (firmware_vid == 0xffff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware vendor ID not specified"); + return FALSE; + } + } + if ((flags & DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID) == 0) { + if (firmware_pid == 0xffff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware product ID not specified"); + return FALSE; + } + } + + /* check vendor matches */ + if (priv->runtime_vid != 0xffff) { + if (!fu_dfu_device_id_compatible(firmware_vid, + priv->runtime_vid, + fu_usb_device_get_vid(FU_USB_DEVICE(self)))) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "vendor ID incorrect, expected 0x%04x " + "got 0x%04x and 0x%04x\n", + firmware_vid, + priv->runtime_vid, + fu_usb_device_get_vid(FU_USB_DEVICE(self))); + return FALSE; + } + } + + /* check product matches */ + if (priv->runtime_pid != 0xffff) { + if (!fu_dfu_device_id_compatible(firmware_pid, + priv->runtime_pid, + fu_usb_device_get_pid(FU_USB_DEVICE(self)))) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "product ID incorrect, expected 0x%04x " + "got 0x%04x and 0x%04x", + firmware_pid, + priv->runtime_pid, + fu_usb_device_get_pid(FU_USB_DEVICE(self))); + return FALSE; + } + } + + /* download each target */ + images = fu_firmware_get_images(firmware); + if (images->len == 0) + g_ptr_array_add(images, g_object_ref(firmware)); + fu_progress_set_id(progress, G_STRLOC); + for (guint i = 0; i < images->len; i++) { + FuFirmware *image = g_ptr_array_index(images, i); + g_autoptr(GPtrArray) chunks = fu_firmware_get_chunks(image, error); + if (chunks == NULL) + return FALSE; + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_WRITE, + fu_dfu_device_calculate_chunks_size(chunks), + NULL); + } + for (guint i = 0; i < images->len; i++) { + FuFirmware *image = g_ptr_array_index(images, i); + FuDfuTargetTransferFlags flags_local = DFU_TARGET_TRANSFER_FLAG_NONE; + guint8 alt; + g_autoptr(FuDfuTarget) target_tmp = NULL; + + alt = fu_firmware_get_idx(image); + target_tmp = fu_dfu_device_get_target_by_alt_setting(self, alt, error); + if (target_tmp == NULL) + return FALSE; + if (!fu_dfu_target_setup(target_tmp, error)) + return FALSE; + g_debug("downloading to target: %s", + fu_device_get_logical_id(FU_DEVICE(target_tmp))); + + /* download onto target */ + if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) + flags_local = DFU_TARGET_TRANSFER_FLAG_VERIFY; + if (!FU_IS_DFU_FIRMWARE(firmware) || + fu_dfu_firmware_get_version(FU_DFU_FIRMWARE(firmware)) == 0x0) + flags_local |= DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC; + ret = fu_dfu_target_download(target_tmp, + image, + fu_progress_get_child(progress), + flags_local, + error); + if (!ret) + return FALSE; + fu_progress_step_done(progress); + } + + /* do not do the dummy upload for quirked devices */ + priv->done_upload_or_download = TRUE; + + /* success */ + return TRUE; +} + +void +fu_dfu_device_error_fixup(FuDfuDevice *self, GError **error) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + + /* sad panda */ + if (error == NULL) + return; + + /* not the right error to query */ + if (!g_error_matches(*error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_NOT_SUPPORTED)) + return; + + /* get the status */ + if (!fu_dfu_device_refresh(self, NULL)) + return; + + /* not in an error state */ + if (priv->state != FU_DFU_STATE_DFU_ERROR) + return; + + /* prefix the error */ + switch (priv->status) { + case FU_DFU_STATUS_OK: + /* ignore */ + break; + case FU_DFU_STATUS_ERR_VENDOR: + g_prefix_error(error, "read protection is active: "); + break; + default: + g_prefix_error(error, + "[%s,%s]: ", + fu_dfu_state_to_string(priv->state), + fu_dfu_status_to_string(priv->status)); + break; + } +} + +static GBytes * +fu_dfu_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + g_autoptr(FuFirmware) firmware = NULL; + + /* get data from hardware */ + g_debug("uploading from device->host"); + if (!fu_dfu_device_refresh_and_clear(self, error)) + return NULL; + firmware = fu_dfu_device_upload(self, progress, DFU_TARGET_TRANSFER_FLAG_NONE, error); + if (firmware == NULL) + return NULL; + + /* get the checksum */ + return fu_firmware_write(firmware, error); +} + +static FuFirmware * +fu_dfu_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + return fu_firmware_new_from_gtypes(fw, + flags, + error, + FU_TYPE_IHEX_FIRMWARE, + FU_TYPE_DFUSE_FIRMWARE, + FU_TYPE_DFU_FIRMWARE, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); +} + +static gboolean +fu_dfu_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuTargetTransferFlags transfer_flags = DFU_TARGET_TRANSFER_FLAG_VERIFY; + + /* open it */ + if (!fu_dfu_device_refresh_and_clear(self, error)) + return FALSE; + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { + transfer_flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; + transfer_flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; + } + + /* hit hardware */ + return fu_dfu_device_download(self, firmware, progress, transfer_flags, error); +} + +static gboolean +fu_dfu_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + FuDfuDevice *self = FU_DFU_DEVICE(device); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp = 0; + + if (g_strcmp0(key, FU_QUIRKS_DFU_FORCE_VERSION) == 0) { + if (!fu_strtoull(value, &tmp, 0x0, G_MAXUINT16, error)) + return FALSE; + priv->force_version = tmp; + return TRUE; + } + if (g_strcmp0(key, "DfuForceTimeout") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, error)) + return FALSE; + priv->timeout_ms = tmp; + return TRUE; + } + if (g_strcmp0(key, "DfuForceTransferSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + priv->force_transfer_size = tmp; + return TRUE; + } + if (g_strcmp0(key, "DfuAltName") == 0) { + fu_dfu_device_set_chip_id(self, 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_dfu_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 88, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "reload"); +} + +static void +fu_dfu_device_finalize(GObject *object) +{ + FuDfuDevice *self = FU_DFU_DEVICE(object); + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + + g_free(priv->chip_id); + g_ptr_array_unref(priv->targets); + + G_OBJECT_CLASS(fu_dfu_device_parent_class)->finalize(object); +} + +static void +fu_dfu_device_class_init(FuDfuDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->set_quirk_kv = fu_dfu_device_set_quirk_kv; + klass_device->to_string = fu_dfu_device_to_string; + klass_device->dump_firmware = fu_dfu_device_dump_firmware; + klass_device->write_firmware = fu_dfu_device_write_firmware; + klass_device->prepare_firmware = fu_dfu_device_prepare_firmware; + klass_device->attach = fu_dfu_device_attach; + klass_device->detach = fu_dfu_device_detach; + klass_device->reload = fu_dfu_device_reload; + klass_device->open = fu_dfu_device_open; + klass_device->close = fu_dfu_device_close; + klass_device->probe = fu_dfu_device_probe; + klass_device->set_progress = fu_dfu_device_set_progress; + object_class->finalize = fu_dfu_device_finalize; +} + +static void +fu_dfu_device_init(FuDfuDevice *self) +{ + FuDfuDevicePrivate *priv = GET_PRIVATE(self); + priv->iface_number = 0xff; + priv->runtime_pid = 0xffff; + priv->runtime_vid = 0xffff; + priv->runtime_release = 0xffff; + priv->state = FU_DFU_STATE_APP_IDLE; + priv->status = FU_DFU_STATUS_OK; + priv->targets = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + priv->timeout_ms = 1500; + priv->transfer_size = 64; + priv->force_version = G_MAXUINT16; + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD, + "can-download"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_CAN_UPLOAD, + "can-upload"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_MANIFEST_TOL, + "manifest-tol"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_WILL_DETACH, + "will-detach"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_CAN_ACCELERATE, + "can-accelerate"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_ATTACH_EXTRA_RESET, + "attach-extra-reset"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_ATTACH_UPLOAD_DOWNLOAD, + "attach-upload-download"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_FORCE_DFU_MODE, + "force-dfu-mode"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_IGNORE_POLLTIMEOUT, + "ignore-polltimeout"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_IGNORE_RUNTIME, + "ignore-runtime"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_IGNORE_UPLOAD, + "ignore-upload"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME, + "no-dfu-runtime"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_NO_GET_STATUS_UPLOAD, + "no-get-status-upload"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_NO_PID_CHANGE, + "no-pid-change"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_USE_ANY_INTERFACE, + "use-any-interface"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_USE_ATMEL_AVR, + "use-atmel-avr"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_USE_PROTOCOL_ZERO, + "use-protocol-zero"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL, + "legacy-protocol"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_DETACH_FOR_ATTACH, + "detach-for-attach"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_ABSENT_SECTOR_SIZE, + "absent-sector-size"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_MANIFEST_POLL, + "manifest-poll"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_NO_BUS_RESET_ATTACH, + "no-bus-reset-attach"); + fu_device_register_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_GD32, "gd32"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_ALLOW_ZERO_POLLTIMEOUT, + "allow-zero-polltimeout"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DFU_DEVICE_FLAG_INDEX_FORCE_DETACH, + "index-force-detach"); +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-device.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-device.h new file mode 100644 index 0000000000000000000000000000000000000000..252c449204054b98c565835b4b50f3a7b040cdee --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-device.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-target.h" + +#define FU_TYPE_DFU_DEVICE (fu_dfu_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuDevice, fu_dfu_device, FU, DFU_DEVICE, FuUsbDevice) + +struct _FuDfuDeviceClass { + FuUsbDeviceClass parent_class; +}; + +FuDfuDevice * +fu_dfu_device_new(FuContext *ctx, GUsbDevice *usb_device); +FuDfuTarget * +fu_dfu_device_get_target_by_alt_setting(FuDfuDevice *self, guint8 alt_setting, GError **error); +FuDfuTarget * +fu_dfu_device_get_target_by_alt_name(FuDfuDevice *self, const gchar *alt_name, GError **error); +const gchar * +fu_dfu_device_get_chip_id(FuDfuDevice *self); +void +fu_dfu_device_set_chip_id(FuDfuDevice *self, const gchar *chip_id); +guint16 +fu_dfu_device_get_runtime_vid(FuDfuDevice *self); +guint16 +fu_dfu_device_get_runtime_pid(FuDfuDevice *self); +guint16 +fu_dfu_device_get_runtime_release(FuDfuDevice *self); +gboolean +fu_dfu_device_reset(FuDfuDevice *self, FuProgress *progress, GError **error); +FuFirmware * +fu_dfu_device_upload(FuDfuDevice *self, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error); +gboolean +fu_dfu_device_refresh(FuDfuDevice *self, GError **error); +gboolean +fu_dfu_device_refresh_and_clear(FuDfuDevice *self, GError **error); +gboolean +fu_dfu_device_abort(FuDfuDevice *self, GError **error); +gboolean +fu_dfu_device_clear_status(FuDfuDevice *self, GError **error); + +guint8 +fu_dfu_device_get_interface(FuDfuDevice *self); +FuDfuState +fu_dfu_device_get_state(FuDfuDevice *self); +FuDfuStatus +fu_dfu_device_get_status(FuDfuDevice *self); +guint16 +fu_dfu_device_get_transfer_size(FuDfuDevice *self); +guint16 +fu_dfu_device_get_version(FuDfuDevice *self); +guint +fu_dfu_device_get_timeout(FuDfuDevice *self); + +void +fu_dfu_device_set_transfer_size(FuDfuDevice *self, guint16 transfer_size); +void +fu_dfu_device_set_timeout(FuDfuDevice *self, guint timeout_ms); +void +fu_dfu_device_error_fixup(FuDfuDevice *self, GError **error); +guint +fu_dfu_device_get_download_timeout(FuDfuDevice *self); +gboolean +fu_dfu_device_ensure_interface(FuDfuDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-plugin.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..1b199f96fd9809185a01f0da234de3399f389244 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-plugin.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-dfu-device.h" +#include "fu-dfu-plugin.h" + +struct _FuDfuPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuDfuPlugin, fu_dfu_plugin, FU_TYPE_PLUGIN) + +static void +fu_dfu_plugin_init(FuDfuPlugin *self) +{ +} + +static void +fu_dfu_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "DfuAltName"); + fu_context_add_quirk_key(ctx, "DfuForceTimeout"); + fu_context_add_quirk_key(ctx, "DfuForceVersion"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_DFU_DEVICE); +} + +static void +fu_dfu_plugin_class_init(FuDfuPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_dfu_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-plugin.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..92b3399eecb0312c5c7c0db8c928cb8ce53bfc0a --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuDfuPlugin, fu_dfu_plugin, FU, DFU_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-sector.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-sector.c new file mode 100644 index 0000000000000000000000000000000000000000..1b20f5fd6d175a5561d70ec7b376288f9e222b3a --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-sector.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/** + * FuDfuSector: + * + * This object represents an sector of memory at a specific address on the + * device itself. + * + * This allows relocatable data segments to be stored in different + * locations on the device itself. + * + * You can think of these objects as flash segments on devices, where a + * complete block can be erased and then written to. + * + * See also: [class@FuChunk] + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-sector.h" + +typedef struct { + guint32 address; + guint32 size; + guint32 size_left; + guint16 zone; + guint16 number; + FuDfuSectorCap cap; +} FuDfuSectorPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuDfuSector, fu_dfu_sector, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fu_dfu_sector_get_instance_private(o)) + +static void +fu_dfu_sector_class_init(FuDfuSectorClass *klass) +{ +} + +static void +fu_dfu_sector_init(FuDfuSector *self) +{ +} + +/** + * fu_dfu_sector_new: (skip) + * address: the address for the sector + * size: the size of this sector + * size_left: the size of the rest of the sector + * zone: the zone of memory the setor belongs + * number: the sector number in the zone + * cap: the #FuDfuSectorCap + * + * Creates a new DFU sector object. + * + * Returns: a new #FuDfuSector + **/ +FuDfuSector * +fu_dfu_sector_new(guint32 address, + guint32 size, + guint32 size_left, + guint16 zone, + guint16 number, + FuDfuSectorCap cap) +{ + FuDfuSectorPrivate *priv; + FuDfuSector *self; + self = g_object_new(FU_TYPE_DFU_SECTOR, NULL); + priv = GET_PRIVATE(self); + priv->address = address; + priv->size = size; + priv->size_left = size_left; + priv->zone = zone; + priv->number = number; + priv->cap = cap; + return self; +} + +/** + * fu_dfu_sector_get_address: + * @self: a #FuDfuSector + * + * Gets the alternate setting. + * + * Returns: integer, or 0x00 for unset + **/ +guint32 +fu_dfu_sector_get_address(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), 0x00); + return priv->address; +} + +/** + * fu_dfu_sector_get_size: + * @self: a #FuDfuSector + * + * Gets the sector size. + * + * Returns: integer, or 0x00 for unset + **/ +guint32 +fu_dfu_sector_get_size(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), 0x00); + return priv->size; +} + +/** + * fu_dfu_sector_get_size_left: + * @self: a #FuDfuSector + * + * Gets the size of the rest of the sector. + * + * Returns: integer, or 0x00 for unset + **/ +guint32 +fu_dfu_sector_get_size_left(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), 0x00); + return priv->size_left; +} + +/** + * fu_dfu_sector_get_zone: + * @self: a #FuDfuSector + * + * Gets the sector zone number. + * + * Returns: integer, or 0x00 for unset + **/ +guint16 +fu_dfu_sector_get_zone(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), 0x00); + return priv->zone; +} + +/** + * fu_dfu_sector_get_number: + * @self: a #FuDfuSector + * + * Gets the sector index number. + * + * Returns: integer, or 0x00 for unset + **/ +guint16 +fu_dfu_sector_get_number(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), 0x00); + return priv->number; +} + +/** + * fu_dfu_sector_get_id: + * @self: a #FuDfuSector + * + * Gets the sector ID which is a combination of the zone and sector number. + * You can use this number to check if the segment is the 'same' as the last + * written or read sector. + * + * Returns: integer ID, or 0x00 for unset + **/ +guint32 +fu_dfu_sector_get_id(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), 0x00); + return (((guint32)priv->zone) << 16) | priv->number; +} + +/** + * fu_dfu_sector_has_cap: + * @self: a #FuDfuSector + * @cap: a #FuDfuSectorCap, e.g. %DFU_SECTOR_CAP_ERASEABLE + * + * Finds out if the sector has the required capability. + * + * Returns: %TRUE if the sector has the capabilily + **/ +gboolean +fu_dfu_sector_has_cap(FuDfuSector *self, FuDfuSectorCap cap) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), FALSE); + return (priv->cap & cap) > 0; +} + +static gchar * +fu_dfu_sector_cap_to_string(FuDfuSectorCap cap) +{ + GString *str = g_string_new(NULL); + if (cap & DFU_SECTOR_CAP_READABLE) + g_string_append(str, "R"); + if (cap & DFU_SECTOR_CAP_ERASEABLE) + g_string_append(str, "E"); + if (cap & DFU_SECTOR_CAP_WRITEABLE) + g_string_append(str, "W"); + return g_string_free(str, FALSE); +} + +/** + * fu_dfu_sector_to_string: + * @self: a #FuDfuSector + * + * Returns a string representation of the object. + * + * Returns: NULL terminated string, or %NULL for invalid + **/ +gchar * +fu_dfu_sector_to_string(FuDfuSector *self) +{ + FuDfuSectorPrivate *priv = GET_PRIVATE(self); + GString *str; + g_autofree gchar *caps_str = NULL; + + g_return_val_if_fail(FU_IS_DFU_SECTOR(self), NULL); + + str = g_string_new(""); + caps_str = fu_dfu_sector_cap_to_string(priv->cap); + g_string_append_printf(str, + "Zone:%i, Sec#:%i, Addr:0x%08x, " + "Size:0x%04x, Caps:0x%01x [%s]", + priv->zone, + priv->number, + priv->address, + priv->size, + priv->cap, + caps_str); + return g_string_free(str, FALSE); +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-sector.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-sector.h new file mode 100644 index 0000000000000000000000000000000000000000..4abfd3ac266130eba39a8ff663426cd9148227ec --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-sector.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_DFU_SECTOR (fu_dfu_sector_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuSector, fu_dfu_sector, FU, DFU_SECTOR, GObject) + +struct _FuDfuSectorClass { + GObjectClass parent_class; +}; + +/** + * FuDfuSectorCap: + * @DFU_SECTOR_CAP_NONE: No operations possible + * @DFU_SECTOR_CAP_READABLE: Sector can be read + * @DFU_SECTOR_CAP_WRITEABLE: Sector can be written + * @DFU_SECTOR_CAP_ERASEABLE: Sector can be erased + * + * The flags indicating what the sector can do. + **/ +typedef enum { + DFU_SECTOR_CAP_NONE = 0, + DFU_SECTOR_CAP_READABLE = 1 << 0, + DFU_SECTOR_CAP_WRITEABLE = 1 << 1, + DFU_SECTOR_CAP_ERASEABLE = 1 << 2, + /*< private >*/ + DFU_SECTOR_CAP_LAST +} FuDfuSectorCap; + +FuDfuSector * +fu_dfu_sector_new(guint32 address, + guint32 size, + guint32 size_left, + guint16 zone, + guint16 number, + FuDfuSectorCap cap); +guint32 +fu_dfu_sector_get_id(FuDfuSector *self); +guint32 +fu_dfu_sector_get_address(FuDfuSector *self); +guint32 +fu_dfu_sector_get_size(FuDfuSector *self); +guint32 +fu_dfu_sector_get_size_left(FuDfuSector *self); +guint16 +fu_dfu_sector_get_zone(FuDfuSector *self); +guint16 +fu_dfu_sector_get_number(FuDfuSector *self); +gboolean +fu_dfu_sector_has_cap(FuDfuSector *self, FuDfuSectorCap cap); +gchar * +fu_dfu_sector_to_string(FuDfuSector *self); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-self-test.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..8e6bdfa41dcb9031f9cbcaea1a5fb3cfbdd0742c --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-self-test.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-context-private.h" +#include "fu-dfu-device.h" +#include "fu-dfu-sector.h" +#include "fu-dfu-target-private.h" + +static void +fu_dfu_enums_func(void) +{ + for (guint i = 0; i < FU_DFU_STATE_LAST; i++) + g_assert_cmpstr(fu_dfu_state_to_string(i), !=, NULL); + for (guint i = 0; i < FU_DFU_STATUS_LAST; i++) + g_assert_cmpstr(fu_dfu_status_to_string(i), !=, NULL); +} + +static gboolean +fu_test_compare_lines(const gchar *txt1, const gchar *txt2, GError **error) +{ + g_autofree gchar *output = NULL; + if (g_strcmp0(txt1, txt2) == 0) + return TRUE; + if (fu_path_fnmatch(txt2, txt1)) + return TRUE; + if (!g_file_set_contents("/tmp/a", txt1, -1, error)) + return FALSE; + if (!g_file_set_contents("/tmp/b", txt2, -1, error)) + return FALSE; + if (!g_spawn_command_line_sync("diff -urNp /tmp/b /tmp/a", &output, NULL, NULL, error)) + return FALSE; + g_set_error_literal(error, 1, 0, output); + return FALSE; +} + +static gchar * +fu_dfu_target_sectors_to_string(FuDfuTarget *target) +{ + GPtrArray *sectors; + GString *str; + + str = g_string_new(""); + sectors = fu_dfu_target_get_sectors(target); + for (guint i = 0; i < sectors->len; i++) { + FuDfuSector *sector = g_ptr_array_index(sectors, i); + g_autofree gchar *tmp = fu_dfu_sector_to_string(sector); + g_string_append_printf(str, "%s\n", tmp); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + return g_string_free(str, FALSE); +} + +static void +fu_dfu_target_dfuse_func(void) +{ + gboolean ret; + gchar *tmp; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDfuDevice) device = fu_dfu_device_new(ctx, NULL); + g_autoptr(FuDfuTarget) target = NULL; + g_autoptr(GError) error = NULL; + + /* NULL */ + target = g_object_new(FU_TYPE_DFU_TARGET, NULL); + fu_device_set_proxy(FU_DEVICE(target), FU_DEVICE(device)); + ret = fu_dfu_target_parse_sectors(target, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + tmp = fu_dfu_target_sectors_to_string(target); + g_assert_cmpstr(tmp, ==, ""); + g_free(tmp); + + /* no addresses */ + ret = fu_dfu_target_parse_sectors(target, "@Flash3", &error); + g_assert_no_error(error); + g_assert_true(ret); + tmp = fu_dfu_target_sectors_to_string(target); + g_assert_cmpstr(tmp, ==, ""); + g_free(tmp); + + /* one sector, no space */ + ret = fu_dfu_target_parse_sectors(target, "@Internal Flash /0x08000000/2*001Ka", &error); + g_assert_no_error(error); + g_assert_true(ret); + tmp = fu_dfu_target_sectors_to_string(target); + ret = fu_test_compare_lines(tmp, + "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1 [R]\n" + "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1 [R]", + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_free(tmp); + + /* multiple sectors */ + ret = fu_dfu_target_parse_sectors(target, "@Flash1 /0x08000000/2*001Ka,4*001Kg", &error); + g_assert_no_error(error); + g_assert_true(ret); + tmp = fu_dfu_target_sectors_to_string(target); + ret = fu_test_compare_lines(tmp, + "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1 [R]\n" + "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1 [R]\n" + "Zone:0, Sec#:1, Addr:0x08000800, Size:0x0400, Caps:0x7 [REW]\n" + "Zone:0, Sec#:1, Addr:0x08000c00, Size:0x0400, Caps:0x7 [REW]\n" + "Zone:0, Sec#:1, Addr:0x08001000, Size:0x0400, Caps:0x7 [REW]\n" + "Zone:0, Sec#:1, Addr:0x08001400, Size:0x0400, Caps:0x7 [REW]", + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_free(tmp); + + /* non-contiguous */ + ret = fu_dfu_target_parse_sectors(target, + "@Flash2 /0xF000/4*100Ba/0xE000/3*8Kg/0x80000/2*24Kg", + &error); + g_assert_no_error(error); + g_assert_true(ret); + tmp = fu_dfu_target_sectors_to_string(target); + ret = fu_test_compare_lines(tmp, + "Zone:0, Sec#:0, Addr:0x0000f000, Size:0x0064, Caps:0x1 [R]\n" + "Zone:0, Sec#:0, Addr:0x0000f064, Size:0x0064, Caps:0x1 [R]\n" + "Zone:0, Sec#:0, Addr:0x0000f0c8, Size:0x0064, Caps:0x1 [R]\n" + "Zone:0, Sec#:0, Addr:0x0000f12c, Size:0x0064, Caps:0x1 [R]\n" + "Zone:1, Sec#:0, Addr:0x0000e000, Size:0x2000, Caps:0x7 [REW]\n" + "Zone:1, Sec#:0, Addr:0x00010000, Size:0x2000, Caps:0x7 [REW]\n" + "Zone:1, Sec#:0, Addr:0x00012000, Size:0x2000, Caps:0x7 [REW]\n" + "Zone:2, Sec#:0, Addr:0x00080000, Size:0x6000, Caps:0x7 [REW]\n" + "Zone:2, Sec#:0, Addr:0x00086000, Size:0x6000, Caps:0x7 [REW]", + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_free(tmp); + + /* invalid */ + ret = fu_dfu_target_parse_sectors(target, "Flash", NULL); + g_assert_true(ret); + ret = fu_dfu_target_parse_sectors(target, "@Internal Flash /0x08000000", NULL); + g_assert_false(ret); + ret = fu_dfu_target_parse_sectors(target, "@Internal Flash /0x08000000/12*001a", NULL); + g_assert_false(ret); +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); + + /* environment */ + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + /* tests go here */ + g_test_add_func("/dfu/enums", fu_dfu_enums_func); + g_test_add_func("/dfu/target(DfuSe}", fu_dfu_target_dfuse_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target-avr.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-avr.c new file mode 100644 index 0000000000000000000000000000000000000000..3d6d6e5b48a5789a46fae1c1751cb0faf1845b5a --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-avr.c @@ -0,0 +1,959 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-device.h" +#include "fu-dfu-sector.h" +#include "fu-dfu-target-avr.h" +#include "fu-dfu-target-private.h" /* waive-pre-commit */ + +/** + * 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 + * chunk 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; +} FuDfuTargetAvrPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuDfuTargetAvr, fu_dfu_target_avr, FU_TYPE_DFU_TARGET) +#define GET_PRIVATE(o) (fu_dfu_target_avr_get_instance_private(o)) + +/* ATMEL AVR version of DFU: + * http://www.atmel.com/Images/doc7618.pdf */ +#define DFU_AVR_CMD_PROG_START 0x01 +#define DFU_AVR_CMD_DISPLAY_DATA 0x03 +#define DFU_AVR_CMD_WRITE_COMMAND 0x04 +#define DFU_AVR_CMD_READ_COMMAND 0x05 +#define DFU_AVR_CMD_CHANGE_BASE_ADDR 0x06 + +/* Atmel AVR32 version of DFU: + * http://www.atmel.com/images/doc32131.pdf */ +#define DFU_AVR32_GROUP_SELECT 0x06 /** SELECT */ +#define DFU_AVR32_CMD_SELECT_MEMORY 0x03 +#define DFU_AVR32_MEMORY_UNIT 0x00 +#define DFU_AVR32_MEMORY_PAGE 0x01 +#define DFU_AVR32_MEMORY_UNIT_FLASH 0x00 +#define DFU_AVR32_MEMORY_UNIT_EEPROM 0x01 +#define DFU_AVR32_MEMORY_UNIT_SECURITY 0x02 +#define DFU_AVR32_MEMORY_UNIT_CONFIGURATION 0x03 +#define DFU_AVR32_MEMORY_UNIT_BOOTLOADER 0x04 +#define DFU_AVR32_MEMORY_UNIT_SIGNATURE 0x05 +#define DFU_AVR32_MEMORY_UNIT_USER 0x06 +#define DFU_AVR32_GROUP_DOWNLOAD 0x01 /** DOWNLOAD */ +#define DFU_AVR32_CMD_PROGRAM_START 0x00 +#define DFU_AVR32_GROUP_UPLOAD 0x03 /** UPLOAD */ +#define DFU_AVR32_CMD_READ_MEMORY 0x00 +#define DFU_AVR32_CMD_BLANK_CHECK 0x01 +#define DFU_AVR32_GROUP_EXEC 0x04 /** EXEC */ +#define DFU_AVR32_CMD_ERASE 0x00 +#define DFU_AVR32_ERASE_EVERYTHING 0xff +#define DFU_AVR32_CMD_START_APPLI 0x03 +#define DFU_AVR32_START_APPLI_RESET 0x00 +#define DFU_AVR32_START_APPLI_NO_RESET 0x01 + +#define ATMEL_64KB_PAGE 0x10000 +#define ATMEL_MAX_TRANSFER_SIZE 0x0400 +#define ATMEL_AVR_CONTROL_BLOCK_SIZE 32 +#define ATMEL_AVR32_CONTROL_BLOCK_SIZE 64 + +#define ATMEL_MANUFACTURER_CODE1 0x58 +#define ATMEL_MANUFACTURER_CODE2 0x1e + +static gboolean +fu_dfu_target_avr_mass_erase(FuDfuTarget *target, FuProgress *progress, GError **error) +{ + g_autoptr(GBytes) data_in = NULL; + guint8 buf[3]; + + /* this takes a long time on some devices */ + fu_dfu_device_set_timeout(FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))), 5000); + + /* format buffer */ + buf[0] = DFU_AVR32_GROUP_EXEC; + buf[1] = DFU_AVR32_CMD_ERASE; + buf[2] = 0xff; + data_in = g_bytes_new_static(buf, sizeof(buf)); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot mass-erase: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_dfu_target_avr_attach(FuDfuTarget *target, FuProgress *progress, GError **error) +{ + guint8 buf[3]; + g_autoptr(GBytes) data_empty = NULL; + g_autoptr(GBytes) data_in = NULL; + g_autoptr(GError) error_local = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 50, "download-chunk"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 50, "download-zero"); + + /* format buffer */ + buf[0] = DFU_AVR32_GROUP_EXEC; + buf[1] = DFU_AVR32_CMD_START_APPLI; + buf[2] = DFU_AVR32_START_APPLI_RESET; + data_in = g_bytes_new_static(buf, sizeof(buf)); + if (!fu_dfu_target_download_chunk(target, + 0, + data_in, + fu_progress_get_child(progress), + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("ignoring as device rebooting: %s", error_local->message); + fu_progress_finished(progress); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "cannot start application reset attach: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* do zero-sized download to initiate the reset */ + data_empty = g_bytes_new(NULL, 0); + if (!fu_dfu_target_download_chunk(target, + 0, + data_empty, + fu_progress_get_child(progress), + &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "cannot initiate reset for attach: "); + return FALSE; + } + g_debug("ignoring as device rebooting: %s", error_local->message); + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +/** + * fu_dfu_target_avr_select_memory_unit: + * @target: a #FuDfuTarget + * @memory_unit: a unit, e.g. %DFU_AVR32_MEMORY_UNIT_FLASH + * @error: (nullable): optional return location for an error + * + * Selects the memory unit for the device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_avr_select_memory_unit(FuDfuTarget *target, + guint8 memory_unit, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) data_in = NULL; + guint8 buf[4]; + + /* check legacy protocol quirk */ + if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + g_debug("ignoring select memory unit as legacy protocol"); + return TRUE; + } + + /* format buffer */ + buf[0] = DFU_AVR32_GROUP_SELECT; + buf[1] = DFU_AVR32_CMD_SELECT_MEMORY; + buf[2] = DFU_AVR32_MEMORY_UNIT; + buf[3] = memory_unit; + data_in = g_bytes_new_static(buf, sizeof(buf)); + g_debug("selecting memory unit 0x%02x", (guint)memory_unit); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot select memory unit: "); + return FALSE; + } + return TRUE; +} + +/** + * fu_dfu_target_avr_select_memory_page: + * @target: a #FuDfuTarget + * @memory_page: an address + * @error: (nullable): optional return location for an error + * + * Selects the memory page for the AVR device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_avr_select_memory_page(FuDfuTarget *target, + guint16 memory_page, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) data_in = NULL; + guint8 buf[4]; + + /* check page not too large for protocol */ + if (memory_page > 0xff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot select memory page:0x%02x " + "with FLIP protocol version 1", + memory_page); + return FALSE; + } + + /* format buffer */ + buf[0] = DFU_AVR_CMD_CHANGE_BASE_ADDR; + buf[1] = 0x03; + buf[2] = 0x00; + buf[3] = memory_page & 0xff; + data_in = g_bytes_new_static(buf, sizeof(buf)); + g_debug("selecting memory page 0x%01x", (guint)memory_page); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot select memory page: "); + return FALSE; + } + return TRUE; +} + +/** + * fu_dfu_target_avr32_select_memory_page: + * @target: a #FuDfuTarget + * @memory_page: an address + * @error: (nullable): optional return location for an error + * + * Selects the memory page for the AVR32 device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_avr32_select_memory_page(FuDfuTarget *target, + guint16 memory_page, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) data_in = NULL; + guint8 buf[5]; + + /* format buffer */ + buf[0] = DFU_AVR32_GROUP_SELECT; + buf[1] = DFU_AVR32_CMD_SELECT_MEMORY; + buf[2] = DFU_AVR32_MEMORY_PAGE; + fu_memwrite_uint16(&buf[3], memory_page, G_BIG_ENDIAN); + data_in = g_bytes_new_static(buf, sizeof(buf)); + g_debug("selecting memory page 0x%02x", (guint)memory_page); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot select memory page: "); + return FALSE; + } + return TRUE; +} + +/** + * fu_dfu_target_avr_read_memory + * @target: a #FuDfuTarget + * @addr_start: an address + * @addr_end: an address + * @error: (nullable): optional return location for an error + * + * Reads flash data from the device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_avr_read_memory(FuDfuTarget *target, + guint16 addr_start, + guint16 addr_end, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) data_in = NULL; + guint8 buf[6]; + + /* format buffer */ + buf[0] = DFU_AVR32_GROUP_UPLOAD; + buf[1] = DFU_AVR32_CMD_READ_MEMORY; + fu_memwrite_uint16(&buf[2], addr_start, G_BIG_ENDIAN); + fu_memwrite_uint16(&buf[4], addr_end, G_BIG_ENDIAN); + data_in = g_bytes_new_static(buf, sizeof(buf)); + g_debug("reading memory from 0x%04x to 0x%04x", (guint)addr_start, (guint)addr_end); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, + "cannot read memory 0x%04x to 0x%04x: ", + (guint)addr_start, + (guint)addr_end); + return FALSE; + } + return TRUE; +} + +/** + * fu_dfu_target_avr_read_command: + * @target: a #FuDfuTarget + * @memory_unit: a unit, e.g. %DFU_AVR32_MEMORY_UNIT_FLASH + * @error: (nullable): optional return location for an error + * + * Performs a read operation on the device. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_avr_read_command(FuDfuTarget *target, + guint8 page, + guint8 addr, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) data_in = NULL; + guint8 buf[3]; + + /* format buffer */ + buf[0] = DFU_AVR_CMD_READ_COMMAND; + buf[1] = page; + buf[2] = addr; + data_in = g_bytes_new_static(buf, sizeof(buf)); + g_debug("read command page:0x%02x addr:0x%02x", (guint)page, (guint)addr); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot read command page: "); + return FALSE; + } + return TRUE; +} + +/** + * fu_dfu_target_avr32_get_chip_signature: + * @target: a #FuDfuTarget + * @error: (nullable): optional return location for an error + * + * Gets the chip signature for the AVR32 device. + * + * Returns: a 4-byte %GBytes object for success, else %NULL + **/ +static GBytes * +fu_dfu_target_avr32_get_chip_signature(FuDfuTarget *target, FuProgress *progress, GError **error) +{ + g_autoptr(GBytes) buf = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 25, "mem-unit"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 25, "mem-page"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 25, "read-memory"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 25, "upload"); + + /* select unit, and request 4 bytes */ + if (!fu_dfu_target_avr_select_memory_unit(target, + DFU_AVR32_MEMORY_UNIT_SIGNATURE, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + if (!fu_dfu_target_avr32_select_memory_page(target, + 0x00, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + if (!fu_dfu_target_avr_read_memory(target, + 0x00, + 0x03, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + + /* get data back */ + buf = fu_dfu_target_upload_chunk(target, 0x00, 0, fu_progress_get_child(progress), error); + if (buf == NULL) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_steal_pointer(&buf); +} + +static GBytes * +fu_dfu_target_avr_get_chip_signature_for_addr(FuDfuTarget *target, + guint8 page, + guint addr, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) buf = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 10, "req"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 90, "res"); + + /* request a single byte */ + if (!fu_dfu_target_avr_read_command(target, + page, + addr, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + + /* get data back */ + buf = fu_dfu_target_upload_chunk(target, 0x00, 0x01, progress, error); + if (buf == NULL) + return NULL; + if (g_bytes_get_size(buf) != 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot read signature memory page:0x%02x " + "addr:0x%02x, got 0x%02x bytes", + (guint)page, + (guint)addr, + (guint)g_bytes_get_size(buf)); + return NULL; + } + fu_progress_step_done(progress); + + /* success */ + return g_steal_pointer(&buf); +} + +/** + * fu_dfu_target_avr_get_chip_signature: + * @target: a #FuDfuTarget + * @error: (nullable): optional return location for an error + * + * Gets the chip signature for the AVR device. + * + * Returns: a 4-byte %GBytes object for success, else %NULL + **/ +static GBytes * +fu_dfu_target_avr_get_chip_signature(FuDfuTarget *target, FuProgress *progress, GError **error) +{ + struct { + guint8 page; + guint addr; + } signature_locations[] = {{0x01, 0x30}, + {0x01, 0x31}, + {0x01, 0x60}, + {0x01, 0x61}, + {0xff, 0xff}}; + g_autoptr(GPtrArray) chunks = NULL; + + /* we have to request this one byte at a time */ + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, G_N_ELEMENTS(signature_locations)); + for (guint i = 0; signature_locations[i].page != 0xff; i++) { + g_autoptr(GBytes) buf = NULL; + buf = fu_dfu_target_avr_get_chip_signature_for_addr(target, + signature_locations[i].page, + signature_locations[i].addr, + fu_progress_get_child(progress), + error); + if (buf == NULL) + return NULL; + g_ptr_array_add(chunks, g_steal_pointer(&buf)); + fu_progress_step_done(progress); + } + return fu_dfu_utils_bytes_join_array(chunks); +} + +static gboolean +fu_dfu_target_avr_setup(FuDfuTarget *target, GError **error) +{ + FuDfuDevice *device; + FuDfuTargetAvr *self = FU_DFU_TARGET_AVR(target); + FuDfuTargetAvrPrivate *priv = GET_PRIVATE(self); + const gchar *chip_id; + const guint8 *buf; + gsize sz; + guint32 device_id_be; + g_autofree gchar *chip_id_guid = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) chunk_sig = NULL; + + /* already done */ + if (priv->device_id > 0x0) + return TRUE; + + /* different methods for AVR vs. AVR32 */ + if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + chunk_sig = fu_dfu_target_avr_get_chip_signature(target, progress, error); + if (chunk_sig == NULL) + return FALSE; + } else { + chunk_sig = fu_dfu_target_avr32_get_chip_signature(target, progress, error); + if (chunk_sig == NULL) { + g_prefix_error(error, "failed to get chip signature: "); + return FALSE; + } + } + + /* get data back */ + buf = g_bytes_get_data(chunk_sig, &sz); + if (g_getenv("FWUPD_DFU_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "AVR:CID", chunk_sig); + if (sz != 4) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot read config memory, got 0x%02x bytes", + (guint)sz); + return FALSE; + } + memcpy(&device_id_be, buf, 4); + priv->device_id = GINT32_FROM_BE(device_id_be); + + if (buf[0] == ATMEL_MANUFACTURER_CODE1) { + chip_id_guid = g_strdup_printf("0x%08x", (guint)priv->device_id); + } else if (buf[0] == ATMEL_MANUFACTURER_CODE2) { + chip_id_guid = g_strdup_printf("0x%06x", (guint)priv->device_id >> 8); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot read config vendor, got 0x%08x, " + "expected 0x%02x or 0x%02x", + (guint)priv->device_id, + (guint)ATMEL_MANUFACTURER_CODE1, + (guint)ATMEL_MANUFACTURER_CODE2); + return FALSE; + } + + /* set the alt-name using the chip ID via a quirk */ + device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))); + fu_device_add_instance_str(FU_DEVICE(device), "CID", chip_id_guid); + if (!fu_device_build_instance_id(FU_DEVICE(device), error, "DFU_AVR", "CID", NULL)) + return FALSE; + chip_id = fu_dfu_device_get_chip_id(device); + if (chip_id == NULL) { + fu_device_remove_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD); + fu_device_remove_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_CAN_UPLOAD); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ChipID GUID %s is not supported", + chip_id_guid); + return FALSE; + } + fu_device_set_logical_id(FU_DEVICE(target), chip_id); + + return TRUE; +} + +static gboolean +fu_dfu_target_avr_download_element_chunks(FuDfuTarget *target, + GPtrArray *chunks, + guint16 *page_last, + gsize header_sz, + FuProgress *progress, + GError **error) +{ + const guint8 footer[] = {0x00, + 0x00, + 0x00, + 0x00, /* CRC */ + 16, /* len */ + 'D', + 'F', + 'U', /* signature */ + 0x01, + 0x10, /* version */ + 0xff, + 0xff, /* vendor ID */ + 0xff, + 0xff, /* product ID */ + 0xff, + 0xff}; /* release */ + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autofree guint8 *buf = NULL; + g_autoptr(GBytes) chunk_tmp = NULL; + + /* select page if required */ + if (fu_chunk_get_page(chk) != *page_last) { + g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); + if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + if (!fu_dfu_target_avr_select_memory_page(target, + fu_chunk_get_page(chk), + progress_tmp, + error)) + return FALSE; + } else { + if (!fu_dfu_target_avr32_select_memory_page(target, + fu_chunk_get_page(chk), + progress_tmp, + error)) + return FALSE; + } + *page_last = fu_chunk_get_page(chk); + } + + /* create chunk with header and footer */ + buf = g_malloc0(fu_chunk_get_data_sz(chk) + header_sz + sizeof(footer)); + buf[0] = DFU_AVR32_GROUP_DOWNLOAD; + buf[1] = DFU_AVR32_CMD_PROGRAM_START; + fu_memwrite_uint16(&buf[2], fu_chunk_get_address(chk), G_BIG_ENDIAN); + fu_memwrite_uint16(&buf[4], + fu_chunk_get_address(chk) + fu_chunk_get_data_sz(chk) - 1, + G_BIG_ENDIAN); + memcpy(&buf[header_sz], fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + memcpy(&buf[header_sz + fu_chunk_get_data_sz(chk)], footer, sizeof(footer)); + + /* download data */ + chunk_tmp = + g_bytes_new_static(buf, fu_chunk_get_data_sz(chk) + header_sz + sizeof(footer)); + g_debug("sending %" G_GSIZE_FORMAT " bytes to the hardware", + g_bytes_get_size(chunk_tmp)); + if (!fu_dfu_target_download_chunk(target, + i, + chunk_tmp, + fu_progress_get_child(progress), + error)) + return FALSE; + + /* update UI */ + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_target_avr_download_element(FuDfuTarget *target, + FuChunk *chk, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDfuSector *sector; + const guint8 *data; + gsize header_sz = ATMEL_AVR32_CONTROL_BLOCK_SIZE; + guint16 page_last = G_MAXUINT16; + guint32 address; + guint32 address_offset = 0x0; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + + /* select a memory and erase everything */ + if (!fu_dfu_target_avr_select_memory_unit(target, + fu_dfu_target_get_alt_setting(target), + progress, + error)) + return FALSE; + if (!fu_dfu_target_avr_mass_erase(target, progress, error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify the element isn't larger than the target size */ + blob = fu_chunk_get_bytes(chk); + sector = fu_dfu_target_get_sector_default(target); + if (sector == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no sector defined for target"); + return FALSE; + } + address = fu_chunk_get_address(chk) & ~0x80000000; + if (address < fu_dfu_sector_get_address(sector)) { + address_offset = fu_dfu_sector_get_address(sector) - address; + g_warning("firmware element starts at 0x%x but sector " + "starts at 0x%x, so offsetting by 0x%x (bootloader?)", + (guint)address, + (guint)fu_dfu_sector_get_address(sector), + (guint)address_offset); + } + if (g_bytes_get_size(blob) + address_offset > fu_dfu_sector_get_size(sector)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "element was larger than sector size: 0x%x", + (guint)fu_dfu_sector_get_size(sector)); + return FALSE; + } + + /* the original AVR protocol uses a half-size control block */ + if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + header_sz = ATMEL_AVR_CONTROL_BLOCK_SIZE; + } + + /* chunk up the memory space into pages */ + data = g_bytes_get_data(blob, NULL); + chunks = fu_chunk_array_new(data + address_offset, + g_bytes_get_size(blob) - address_offset, + fu_dfu_sector_get_address(sector), + ATMEL_64KB_PAGE, + ATMEL_MAX_TRANSFER_SIZE); + if (!fu_dfu_target_avr_download_element_chunks(target, + chunks, + &page_last, + header_sz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* done */ + return TRUE; +} + +static GBytes * +fu_dfu_target_avr_upload_element_chunk(FuDfuTarget *target, + FuChunk *chk, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) blob = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 70, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 30, NULL); + + /* prepare to read */ + if (!fu_dfu_target_avr_read_memory(target, + fu_chunk_get_address(chk), + fu_chunk_get_address(chk) + fu_chunk_get_data_sz(chk) - + 1, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + + /* upload data */ + g_debug("requesting %i bytes from the hardware for chunk 0x%x", + ATMEL_MAX_TRANSFER_SIZE, + fu_chunk_get_idx(chk)); + blob = fu_dfu_target_upload_chunk(target, + fu_chunk_get_idx(chk), + ATMEL_MAX_TRANSFER_SIZE, + fu_progress_get_child(progress), + error); + if (blob == NULL) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_steal_pointer(&blob); +} + +static FuChunk * +fu_dfu_target_avr_upload_element_chunks(FuDfuTarget *target, + guint32 address, + gsize expected_size, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + guint16 page_last = G_MAXUINT16; + guint chunk_valid = G_MAXUINT; + g_autoptr(FuChunk) chk2 = NULL; + g_autoptr(GBytes) contents = NULL; + g_autoptr(GBytes) contents_truncated = NULL; + g_autoptr(GPtrArray) blobs = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + /* process each chunk */ + blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + for (guint i = 0; i < chunks->len; i++) { + GBytes *blob_tmp = NULL; + FuChunk *chk = g_ptr_array_index(chunks, i); + + /* select page if required */ + if (fu_chunk_get_page(chk) != page_last) { + g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); + if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), + FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + if (!fu_dfu_target_avr_select_memory_page(target, + fu_chunk_get_page(chk), + progress_tmp, + error)) + return NULL; + } else { + if (!fu_dfu_target_avr32_select_memory_page(target, + fu_chunk_get_page(chk), + progress_tmp, + error)) + return NULL; + } + page_last = fu_chunk_get_page(chk); + } + + blob_tmp = fu_dfu_target_avr_upload_element_chunk(target, + chk, + fu_progress_get_child(progress), + error); + if (blob_tmp == NULL) + return NULL; + g_ptr_array_add(blobs, blob_tmp); + + /* this page has valid data */ + if (!fu_bytes_is_empty(blob_tmp)) { + g_debug("chunk %u has data (page %" G_GUINT32_FORMAT ")", + i, + fu_chunk_get_page(chk)); + chunk_valid = i; + } else { + g_debug("chunk %u is empty", i); + } + + /* update UI */ + fu_progress_step_done(progress); + } + + /* truncate the image if any sectors are empty, i.e. all 0xff */ + if (chunk_valid == G_MAXUINT) { + g_debug("all %u chunks are empty", blobs->len); + g_ptr_array_set_size(chunks, 0); + } else if (blobs->len != chunk_valid + 1) { + g_debug("truncating chunks from %u to %u", blobs->len, chunk_valid + 1); + g_ptr_array_set_size(blobs, chunk_valid + 1); + } + + /* create element of required size */ + contents = fu_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 { + contents_truncated = g_bytes_ref(contents); + } + + chk2 = fu_chunk_bytes_new(contents_truncated); + fu_chunk_set_address(chk2, address | 0x80000000); /* flash */ + return g_steal_pointer(&chk2); +} + +static FuChunk * +fu_dfu_target_avr_upload_element(FuDfuTarget *target, + guint32 address, + gsize expected_size, + gsize maximum_size, + FuProgress *progress, + GError **error) +{ + FuDfuSector *sector; + g_autoptr(FuChunk) chk2 = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 95, NULL); + + /* select unit */ + if (!fu_dfu_target_avr_select_memory_unit(target, + fu_dfu_target_get_alt_setting(target), + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + + /* verify the element isn't lower than the flash area */ + sector = fu_dfu_target_get_sector_default(target); + if (sector == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no sector defined for target"); + return NULL; + } + if (address < fu_dfu_sector_get_address(sector)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot read from below sector start"); + return NULL; + } + + /* the flash starts at 0x80000000, but is indexed from zero */ + address &= ~0x80000000; + + /* chunk up the memory space into pages */ + chunks = fu_chunk_array_new(NULL, + maximum_size, + address, + ATMEL_64KB_PAGE, + ATMEL_MAX_TRANSFER_SIZE); + chk2 = fu_dfu_target_avr_upload_element_chunks(target, + address, + expected_size, + chunks, + fu_progress_get_child(progress), + error); + if (chk2 == NULL) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_steal_pointer(&chk2); +} + +static void +fu_dfu_target_avr_init(FuDfuTargetAvr *self) +{ +} + +static void +fu_dfu_target_avr_class_init(FuDfuTargetAvrClass *klass) +{ + FuDfuTargetClass *klass_target = FU_DFU_TARGET_CLASS(klass); + klass_target->setup = fu_dfu_target_avr_setup; + klass_target->attach = fu_dfu_target_avr_attach; + klass_target->mass_erase = fu_dfu_target_avr_mass_erase; + klass_target->upload_element = fu_dfu_target_avr_upload_element; + klass_target->download_element = fu_dfu_target_avr_download_element; +} + +FuDfuTarget * +fu_dfu_target_avr_new(void) +{ + FuDfuTarget *target; + target = g_object_new(FU_TYPE_DFU_TARGET_AVR, NULL); + return target; +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target-avr.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-avr.h new file mode 100644 index 0000000000000000000000000000000000000000..ac91c80aaa5b558b87b544254b01455b338140a9 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-avr.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-dfu-target.h" + +#define FU_TYPE_DFU_TARGET_AVR (fu_dfu_target_avr_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuTargetAvr, fu_dfu_target_avr, FU, DFU_TARGET_AVR, FuDfuTarget) + +struct _FuDfuTargetAvrClass { + FuDfuTargetClass parent_class; +}; + +FuDfuTarget * +fu_dfu_target_avr_new(void); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target-private.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-private.h new file mode 100644 index 0000000000000000000000000000000000000000..4aa60fef07cda331be824d259b9c278b805811f1 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-private.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-dfu-device.h" +#include "fu-dfu-sector.h" +#include "fu-dfu-target.h" + +FuDfuTarget * +fu_dfu_target_new(void); + +GBytes * +fu_dfu_target_upload_chunk(FuDfuTarget *self, + guint16 index, + gsize buf_sz, + FuProgress *progress, + GError **error); +gboolean +fu_dfu_target_download_chunk(FuDfuTarget *self, + guint16 index, + GBytes *bytes, + FuProgress *progress, + GError **error); +gboolean +fu_dfu_target_attach(FuDfuTarget *self, FuProgress *progress, GError **error); +void +fu_dfu_target_set_alt_idx(FuDfuTarget *self, guint8 alt_idx); +void +fu_dfu_target_set_alt_setting(FuDfuTarget *self, guint8 alt_setting); + +/* for the other implementations */ +void +fu_dfu_target_set_alt_name(FuDfuTarget *self, const gchar *alt_name); +gboolean +fu_dfu_target_check_status(FuDfuTarget *self, GError **error); +FuDfuSector * +fu_dfu_target_get_sector_for_addr(FuDfuTarget *self, guint32 addr); + +/* export this just for the self tests */ +gboolean +fu_dfu_target_parse_sectors(FuDfuTarget *self, const gchar *alt_name, GError **error); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target-stm.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-stm.c new file mode 100644 index 0000000000000000000000000000000000000000..d10a581cfae3995606bfabb6f6ab0f4b93cde4a8 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-stm.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-sector.h" +#include "fu-dfu-target-private.h" /* waive-pre-commit */ +#include "fu-dfu-target-stm.h" + +G_DEFINE_TYPE(FuDfuTargetStm, fu_dfu_target_stm, FU_TYPE_DFU_TARGET) + +/* STMicroelectronics STM32 version of DFU: + * www.st.com/resource/en/application_note/cd00264379.pdf */ +#define DFU_STM_CMD_GET_COMMAND 0x00 +#define DFU_STM_CMD_SET_ADDRESS_POINTER 0x21 +#define DFU_STM_CMD_ERASE 0x41 +#define DFU_STM_CMD_READ_UNPROTECT 0x92 + +static gboolean +fu_dfu_target_stm_attach(FuDfuTarget *target, FuProgress *progress, GError **error) +{ + /* downloading empty payload will cause a dfu to leave, + * the returned status will be dfuMANIFEST and expect the device to disconnect */ + g_autoptr(GBytes) bytes_tmp = g_bytes_new(NULL, 0); + g_autoptr(GError) error_local = NULL; + if (!fu_dfu_target_download_chunk(target, 2, bytes_tmp, progress, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_dfu_target_stm_mass_erase(FuDfuTarget *target, FuProgress *progress, GError **error) +{ + GBytes *data_in; + guint8 buf[1]; + + /* format buffer */ + buf[0] = DFU_STM_CMD_ERASE; + data_in = g_bytes_new_static(buf, sizeof(buf)); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot mass-erase: "); + return FALSE; + } + + /* 2nd check required to get error code */ + return fu_dfu_target_check_status(target, error); +} + +/* sets the address used for the next download or upload request */ +static gboolean +fu_dfu_target_stm_set_address(FuDfuTarget *target, + guint32 address, + FuProgress *progress, + GError **error) +{ + GBytes *data_in; + guint8 buf[5]; + + /* format buffer */ + buf[0] = DFU_STM_CMD_SET_ADDRESS_POINTER; + memcpy(buf + 1, &address, 4); + data_in = g_bytes_new_static(buf, sizeof(buf)); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot set address 0x%x: ", address); + return FALSE; + } + + /* 2nd check required to get error code */ + g_debug("doing actual check status"); + return fu_dfu_target_check_status(target, error); +} + +static FuChunk * +fu_dfu_target_stm_upload_element(FuDfuTarget *target, + guint32 address, + gsize expected_size, + gsize maximum_size, + FuProgress *progress, + GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))); + FuDfuSector *sector; + FuChunk *chk = NULL; + GBytes *chunk_tmp; + guint32 offset = address; + guint percentage_size = expected_size > 0 ? expected_size : maximum_size; + gsize total_size = 0; + guint16 transfer_size = fu_dfu_device_get_transfer_size(device); + g_autoptr(GBytes) contents = NULL; + g_autoptr(GBytes) contents_truncated = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 40, "set-addr"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "abort"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 58, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + + /* for DfuSe devices we need to handle the address manually */ + sector = fu_dfu_target_get_sector_for_addr(target, offset); + if (sector == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no memory sector at 0x%04x", + (guint)offset); + return NULL; + } + g_debug("using sector %u for read of %x", fu_dfu_sector_get_id(sector), offset); + if (!fu_dfu_sector_has_cap(sector, DFU_SECTOR_CAP_READABLE)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "memory sector at 0x%04x is not readable", + (guint)offset); + return NULL; + } + + /* manually set the sector address */ + g_debug("setting DfuSe address to 0x%04x", (guint)offset); + if (!fu_dfu_target_stm_set_address(target, offset, fu_progress_get_child(progress), error)) + return NULL; + fu_progress_step_done(progress); + + /* abort back to IDLE */ + if (!fu_dfu_device_abort(device, error)) + return NULL; + fu_progress_step_done(progress); + + /* get all the chunks from the hardware */ + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + for (guint16 idx = 0; idx < G_MAXUINT16; idx++) { + guint32 chunk_size; + g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); + + /* read chunk of data -- ST uses wBlockNum=0 for DfuSe commands + * and wBlockNum=1 is reserved */ + chunk_tmp = fu_dfu_target_upload_chunk(target, + idx + 2, + 0, /* device transfer size */ + progress_tmp, /* we don't know! */ + error); + if (chunk_tmp == NULL) + return NULL; + + /* add to array */ + chunk_size = (guint32)g_bytes_get_size(chunk_tmp); + g_debug("got #%04x chunk @0x%x of size %" G_GUINT32_FORMAT, + idx, + offset, + chunk_size); + g_ptr_array_add(chunks, chunk_tmp); + total_size += chunk_size; + offset += chunk_size; + + /* update UI */ + if (chunk_size > 0) { + fu_progress_set_percentage_full(fu_progress_get_child(progress), + MIN(total_size, percentage_size), + percentage_size); + } + + /* detect short write as EOF */ + if (chunk_size < transfer_size) + break; + + /* more data than we needed */ + if (maximum_size > 0 && total_size > maximum_size) + break; + } + fu_progress_step_done(progress); + + /* abort back to IDLE */ + if (!fu_dfu_device_abort(device, error)) + return NULL; + fu_progress_step_done(progress); + + /* check final size */ + if (expected_size > 0) { + if (total_size < expected_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid size, got %" G_GSIZE_FORMAT ", " + "expected %" G_GSIZE_FORMAT, + total_size, + expected_size); + return NULL; + } + } + + /* create new image */ + contents = fu_dfu_utils_bytes_join_array(chunks); + if (expected_size > 0) { + contents_truncated = fu_bytes_new_offset(contents, 0, expected_size, error); + if (contents_truncated == NULL) + return NULL; + } else { + contents_truncated = g_bytes_ref(contents); + } + chk = fu_chunk_bytes_new(contents_truncated); + fu_chunk_set_address(chk, address); + return chk; +} + +/** + * fu_dfu_target_stm_erase_address: + * @target: a #FuDfuTarget + * @address: memory address + * @error: (nullable): optional return location for an error + * + * Erases a memory sector at a given address. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_stm_erase_address(FuDfuTarget *target, + guint32 address, + FuProgress *progress, + GError **error) +{ + GBytes *data_in; + guint8 buf[5]; + + /* format buffer */ + buf[0] = DFU_STM_CMD_ERASE; + memcpy(buf + 1, &address, 4); + data_in = g_bytes_new_static(buf, sizeof(buf)); + if (!fu_dfu_target_download_chunk(target, 0, data_in, progress, error)) { + g_prefix_error(error, "cannot erase address 0x%x: ", address); + return FALSE; + } + + /* 2nd check required to get error code */ + g_debug("doing actual check status"); + return fu_dfu_target_check_status(target, error); +} + +static gboolean +fu_dfu_target_stm_download_element1(FuDfuTarget *target, + GPtrArray *chunks, + GPtrArray *sectors_array, + FuProgress *progress, + GError **error) +{ + g_autoptr(GHashTable) sectors_hash = g_hash_table_new(g_direct_hash, g_direct_equal); + guint32 address = 0; + guint32 transfer_size = 0; + + /* start offset */ + if (chunks->len > 0) { + FuChunk *chk = g_ptr_array_index(chunks, 0); + address = fu_chunk_get_address(chk); + transfer_size = fu_chunk_get_data_sz(chk); + } + + /* no progress */ + for (guint i = 0; i < chunks->len; i++) { + guint32 offset_dev = i * transfer_size; + + /* for DfuSe devices we need to handle the erase and setting + * the sectory address manually */ + while (offset_dev < (i + 1) * transfer_size) { + FuDfuSector *sector = + fu_dfu_target_get_sector_for_addr(target, address + offset_dev); + if (sector == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no memory sector at 0x%04x", + (guint)address + offset_dev); + return FALSE; + } + if (!fu_dfu_sector_has_cap(sector, DFU_SECTOR_CAP_WRITEABLE)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "memory sector at 0x%04x is not writable", + (guint)address + offset_dev); + return FALSE; + } + + /* if it's erasable and not yet blanked */ + if (fu_dfu_sector_has_cap(sector, DFU_SECTOR_CAP_ERASEABLE) && + g_hash_table_lookup(sectors_hash, sector) == NULL) { + g_hash_table_insert(sectors_hash, sector, GINT_TO_POINTER(1)); + g_ptr_array_add(sectors_array, sector); + g_debug("marking sector 0x%04x-%04x to be erased", + fu_dfu_sector_get_address(sector), + fu_dfu_sector_get_address(sector) + + fu_dfu_sector_get_size(sector)); + } + offset_dev += fu_dfu_sector_get_size(sector); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_target_stm_download_element2(FuDfuTarget *target, + GPtrArray *sectors_array, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, sectors_array->len); + + for (guint i = 0; i < sectors_array->len; i++) { + FuDfuSector *sector = g_ptr_array_index(sectors_array, i); + g_debug("erasing sector at 0x%04x", fu_dfu_sector_get_address(sector)); + if (!fu_dfu_target_stm_erase_address(target, + fu_dfu_sector_get_address(sector), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_target_stm_download_element3(FuDfuTarget *target, + GPtrArray *chunks, + GPtrArray *sectors_array, + FuProgress *progress, + GError **error) +{ + guint zone_last = G_MAXUINT; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk_tmp = g_ptr_array_index(chunks, i); + FuDfuSector *sector; + guint32 offset_dev = fu_chunk_get_address(chk_tmp); + g_autoptr(GBytes) bytes_tmp = NULL; + + /* for DfuSe devices we need to set the address manually */ + sector = fu_dfu_target_get_sector_for_addr(target, offset_dev); + if (sector == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no valid sector for %x", + offset_dev); + return FALSE; + } + + /* manually set the sector address */ + if (fu_dfu_sector_get_zone(sector) != zone_last) { + g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); + g_debug("setting address to 0x%04x", (guint)offset_dev); + if (!fu_dfu_target_stm_set_address(target, + (guint32)offset_dev, + progress_tmp, + error)) + return FALSE; + zone_last = fu_dfu_sector_get_zone(sector); + } + + /* we have to write one final zero-sized chunk for EOF */ + bytes_tmp = fu_chunk_get_bytes(chk_tmp); + g_debug("writing sector at 0x%04x (0x%" G_GSIZE_FORMAT ")", + offset_dev, + g_bytes_get_size(bytes_tmp)); + /* ST uses wBlockNum=0 for DfuSe commands and wBlockNum=1 is reserved */ + if (!fu_dfu_target_download_chunk(target, + (i + 2), + bytes_tmp, + fu_progress_get_child(progress), + error)) + return FALSE; + + /* getting the status moves the state machine to DNLOAD-IDLE */ + if (!fu_dfu_target_check_status(target, error)) + return FALSE; + + /* update UI */ + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_target_stm_download_element(FuDfuTarget *target, + FuChunk *chk, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))); + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GPtrArray) sectors_array = g_ptr_array_new(); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 49, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + + /* 1st pass: work out which sectors need erasing */ + bytes = fu_chunk_get_bytes(chk); + chunks = fu_chunk_array_new_from_bytes(bytes, + fu_chunk_get_address(chk), + 0x0, + fu_dfu_device_get_transfer_size(device)); + if (!fu_dfu_target_stm_download_element1(target, + chunks, + sectors_array, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* 2nd pass: actually erase sectors */ + if (!fu_dfu_target_stm_download_element2(target, + sectors_array, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* 3rd pass: write data */ + if (!fu_dfu_target_stm_download_element3(target, + chunks, + sectors_array, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_dfu_target_stm_init(FuDfuTargetStm *self) +{ +} + +static void +fu_dfu_target_stm_class_init(FuDfuTargetStmClass *klass) +{ + FuDfuTargetClass *klass_target = FU_DFU_TARGET_CLASS(klass); + klass_target->attach = fu_dfu_target_stm_attach; + klass_target->mass_erase = fu_dfu_target_stm_mass_erase; + klass_target->upload_element = fu_dfu_target_stm_upload_element; + klass_target->download_element = fu_dfu_target_stm_download_element; +} + +FuDfuTarget * +fu_dfu_target_stm_new(void) +{ + FuDfuTarget *target; + target = g_object_new(FU_TYPE_DFU_TARGET_STM, NULL); + return target; +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target-stm.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-stm.h new file mode 100644 index 0000000000000000000000000000000000000000..8907b04d25941780a863090a56e09052124afbcb --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target-stm.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-dfu-target.h" + +#define FU_TYPE_DFU_TARGET_STM (fu_dfu_target_stm_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuTargetStm, fu_dfu_target_stm, FU, DFU_TARGET_STM, FuDfuTarget) + +struct _FuDfuTargetStmClass { + FuDfuTargetClass parent_class; +}; + +FuDfuTarget * +fu_dfu_target_stm_new(void); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-target.c new file mode 100644 index 0000000000000000000000000000000000000000..9f1ab860f09795020c4c3a6703139c09fd99b883 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target.c @@ -0,0 +1,1297 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/** + * FuDfuTarget: + * + * This object allows uploading and downloading an image onto a + * specific DFU-capable target. + * + * You only need to use this in preference to #FuDfuDevice if you only + * want to update one target on the device. Most users will want to + * update all the targets on the device at the same time. + * + * See also: [class@FuDfuDevice], [class@FuFirmware] + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-device.h" +#include "fu-dfu-sector.h" +#include "fu-dfu-target-private.h" /* waive-pre-commit */ + +#define DFU_TARGET_MANIFEST_MAX_POLLING_TRIES 200 + +typedef struct { + gboolean done_setup; + guint8 alt_setting; + guint8 alt_idx; + GPtrArray *sectors; /* of FuDfuSector */ +} FuDfuTargetPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuDfuTarget, fu_dfu_target, FU_TYPE_DEVICE) +#define GET_PRIVATE(o) (fu_dfu_target_get_instance_private(o)) + +static void +fu_dfu_target_init(FuDfuTarget *self) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + priv->sectors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_dfu_target_finalize(GObject *object) +{ + FuDfuTarget *self = FU_DFU_TARGET(object); + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + + g_ptr_array_unref(priv->sectors); + + G_OBJECT_CLASS(fu_dfu_target_parent_class)->finalize(object); +} + +static void +fu_dfu_target_to_string(FuDevice *device, guint idt, GString *str) +{ + FuDfuTarget *self = FU_DFU_TARGET(device); + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + fu_string_append_kx(str, idt, "AltSetting", priv->alt_setting); + fu_string_append_kx(str, idt, "AltIdx", priv->alt_idx); + for (guint i = 0; i < priv->sectors->len; i++) { + FuDfuSector *sector = g_ptr_array_index(priv->sectors, i); + g_autofree gchar *tmp1 = g_strdup_printf("Idx%02x", i); + g_autofree gchar *tmp2 = fu_dfu_sector_to_string(sector); + fu_string_append(str, idt + 1, tmp1, tmp2); + } +} + +FuDfuSector * +fu_dfu_target_get_sector_for_addr(FuDfuTarget *self, guint32 addr) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + + for (guint i = 0; i < priv->sectors->len; i++) { + FuDfuSector *sector = g_ptr_array_index(priv->sectors, i); + if (addr < fu_dfu_sector_get_address(sector)) + continue; + if (addr > fu_dfu_sector_get_address(sector) + fu_dfu_sector_get_size(sector)) + continue; + return sector; + } + return NULL; +} + +static gboolean +fu_dfu_target_parse_sector(FuDfuTarget *self, + const gchar *dfuse_sector_id, + guint32 *addr, + guint16 zone, + guint16 number, + GError **error) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + FuDfuSectorCap cap = DFU_SECTOR_CAP_NONE; + gchar *tmp; + guint32 addr_offset = 0; + guint64 nr_sectors; + guint64 sector_size; + + /* parse # of sectors */ + nr_sectors = g_ascii_strtoull(dfuse_sector_id, &tmp, 10); + if (nr_sectors > 999) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid number of sectors: %s", + dfuse_sector_id); + return FALSE; + } + + /* check this is the delimiter */ + if (tmp[0] != '*') { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid sector ID: %s", + dfuse_sector_id); + return FALSE; + } + + /* parse sector size */ + sector_size = g_ascii_strtoull(tmp + 1, &tmp, 10); + if (sector_size > 999) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid sector size: %s", + dfuse_sector_id); + return FALSE; + } + + /* handle weirdness */ + if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(self)), + FU_DFU_DEVICE_FLAG_ABSENT_SECTOR_SIZE)) { + if (tmp[1] == '\0') { + tmp[1] = tmp[0]; + tmp[0] = 'B'; + } + } + + /* get multiplier */ + switch (tmp[0]) { + case 'B': /* byte */ + case ' ': /* byte, ST reference bootloader :/ */ + break; + case 'K': /* Kilo */ + sector_size *= 0x400; + break; + case 'M': /* Mega */ + sector_size *= 0x100000; + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid sector multiplier: %s", + tmp); + return FALSE; + } + + /* get sector type */ + switch (tmp[1]) { + case 'a': + cap = DFU_SECTOR_CAP_READABLE; + break; + case 'b': + cap = DFU_SECTOR_CAP_ERASEABLE; + break; + case 'c': + cap = DFU_SECTOR_CAP_READABLE | DFU_SECTOR_CAP_ERASEABLE; + break; + case 'd': + cap = DFU_SECTOR_CAP_WRITEABLE; + break; + case 'e': + cap = DFU_SECTOR_CAP_READABLE | DFU_SECTOR_CAP_WRITEABLE; + break; + case 'f': + cap = DFU_SECTOR_CAP_ERASEABLE | DFU_SECTOR_CAP_WRITEABLE; + break; + case 'g': + cap = DFU_SECTOR_CAP_READABLE | DFU_SECTOR_CAP_ERASEABLE | DFU_SECTOR_CAP_WRITEABLE; + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid sector type: %s", + tmp); + return FALSE; + } + + /* add all the sectors */ + for (guint i = 0; i < nr_sectors; i++) { + FuDfuSector *sector; + sector = fu_dfu_sector_new(*addr + addr_offset, + (guint32)sector_size, + (guint32)((nr_sectors * sector_size) - addr_offset), + zone, + number, + cap); + g_ptr_array_add(priv->sectors, sector); + addr_offset += fu_dfu_sector_get_size(sector); + } + + /* update for next sector */ + *addr += addr_offset; + return TRUE; +} + +gboolean +fu_dfu_target_parse_sectors(FuDfuTarget *self, const gchar *alt_name, GError **error) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + g_auto(GStrv) zones = NULL; + + /* not set */ + if (alt_name == NULL) + return TRUE; + + /* From the Neo Freerunner */ + if (g_str_has_prefix(alt_name, "RAM 0x")) { + FuDfuSector *sector; + guint64 addr_tmp; + addr_tmp = g_ascii_strtoull(alt_name + 6, NULL, 16); + if (addr_tmp == 0 || addr_tmp > G_MAXUINT32) + return FALSE; + g_debug("RAM description, so parsing"); + sector = fu_dfu_sector_new((guint32)addr_tmp, + 0x0, /* size */ + 0x0, /* size_left */ + 0x0, /* zone */ + 0x0, /* number */ + DFU_SECTOR_CAP_ERASEABLE | DFU_SECTOR_CAP_READABLE | + DFU_SECTOR_CAP_WRITEABLE); + g_ptr_array_add(priv->sectors, sector); + } + + /* not a DfuSe alternative name */ + if (alt_name[0] != '@') + return TRUE; + + /* clear any existing zones */ + g_ptr_array_set_size(priv->sectors, 0); + + /* parse zones */ + zones = g_strsplit(alt_name, "/", -1); + fu_device_set_name(FU_DEVICE(self), g_strchomp(zones[0] + 1)); + for (guint i = 1; zones[i] != NULL; i += 2) { + guint32 addr; + guint64 addr_tmp; + g_auto(GStrv) sectors = NULL; + + /* parse address */ + if (!g_str_has_prefix(zones[i], "0x")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No sector address"); + return FALSE; + } + addr_tmp = g_ascii_strtoull(zones[i] + 2, NULL, 16); + if (addr_tmp > G_MAXUINT32) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Sector address too large"); + return FALSE; + } + addr = (guint32)addr_tmp; + + /* no sectors?! */ + if (zones[i + 1] == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No sector section"); + return FALSE; + } + + /* parse sectors */ + sectors = g_strsplit(zones[i + 1], ",", -1); + for (guint16 j = 0; sectors[j] != NULL; j++) { + if (!fu_dfu_target_parse_sector(self, + sectors[j], + &addr, + (i - 1) / 2, + j, + error)) { + g_prefix_error(error, "Failed to parse: '%s': ", sectors[j]); + return FALSE; + } + } + } + + /* success */ + return TRUE; +} + +/** + * fu_dfu_target_new: (skip) + * + * Creates a new DFU target, which represents an alt-setting on a + * DFU-capable device. + * + * Returns: a #FuDfuTarget + **/ +FuDfuTarget * +fu_dfu_target_new(void) +{ + FuDfuTarget *self; + self = g_object_new(FU_TYPE_DFU_TARGET, NULL); + return self; +} + +/** + * fu_dfu_target_get_sectors: + * @self: a #FuDfuTarget + * + * Gets the sectors exported by the target. + * + * Returns: (transfer none) (element-type FuDfuSector): sectors + **/ +GPtrArray * +fu_dfu_target_get_sectors(FuDfuTarget *self) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_TARGET(self), NULL); + return priv->sectors; +} + +/** + * fu_dfu_target_get_sector_default: + * @self: a #FuDfuTarget + * + * Gets the default (first) sector exported by the target. + * + * Returns: (transfer none): a #FuDfuSector, or %NULL + **/ +FuDfuSector * +fu_dfu_target_get_sector_default(FuDfuTarget *self) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_TARGET(self), NULL); + if (priv->sectors->len == 0) + return NULL; + return FU_DFU_SECTOR(g_ptr_array_index(priv->sectors, 0)); +} + +/** + * fu_dfu_target_status_to_error_msg: + * @status: a #FuDfuStatus, e.g. %FU_DFU_STATUS_ERR_ERASE + * + * Converts an enumerated value to an error description. + * + * Returns: a string + **/ +static const gchar * +fu_dfu_target_status_to_error_msg(FuDfuStatus status) +{ + if (status == FU_DFU_STATUS_OK) + return "No error condition is present"; + if (status == FU_DFU_STATUS_ERR_TARGET) + return "Firmware is not for designed this device"; + if (status == FU_DFU_STATUS_ERR_FILE) + return "Firmware is for this device but fails verification"; + if (status == FU_DFU_STATUS_ERR_WRITE) + return "Device is unable to write memory"; + if (status == FU_DFU_STATUS_ERR_ERASE) + return "Memory erase function failed"; + if (status == FU_DFU_STATUS_ERR_CHECK_ERASED) + return "Memory erase check failed"; + if (status == FU_DFU_STATUS_ERR_PROG) + return "Program memory function failed"; + if (status == FU_DFU_STATUS_ERR_VERIFY) + return "Programmed memory failed verification"; + if (status == FU_DFU_STATUS_ERR_ADDRESS) + return "Cannot program memory due to address out of range"; + if (status == FU_DFU_STATUS_ERR_NOTDONE) + return "Received zero-length download but data is incomplete"; + if (status == FU_DFU_STATUS_ERR_FIRMWARE) + return "Device firmware is corrupt"; + if (status == FU_DFU_STATUS_ERR_VENDOR) + return "Vendor-specific error"; + if (status == FU_DFU_STATUS_ERR_USBR) + return "Device detected unexpected USB reset signaling"; + if (status == FU_DFU_STATUS_ERR_POR) + return "Device detected unexpected power on reset"; + if (status == FU_DFU_STATUS_ERR_UNKNOWN) + return "Something unexpected went wrong"; + if (status == FU_DFU_STATUS_ERR_STALLDPKT) + return "Device stalled an unexpected request"; + return NULL; +} + +static gboolean +fu_dfu_target_manifest_wait(FuDfuTarget *self, GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint polling_count = 0; + + /* get the status */ + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + + /* wait for FU_DFU_STATE_DFU_MANIFEST to not be set */ + while (fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_MANIFEST_SYNC || + fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_MANIFEST) { + g_debug("waiting for FU_DFU_STATE_DFU_MANIFEST to clear"); + + if (polling_count++ > DFU_TARGET_MANIFEST_MAX_POLLING_TRIES) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "reach to max polling tries"); + return FALSE; + } + + g_usleep((fu_dfu_device_get_download_timeout(device) + 1000) * 1000); + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + } + + /* in an error state */ + if (fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_ERROR) { + g_set_error_literal( + error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + fu_dfu_target_status_to_error_msg(fu_dfu_device_get_status(device))); + return FALSE; + } + + return TRUE; +} + +gboolean +fu_dfu_target_check_status(FuDfuTarget *self, GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuStatus status; + g_autoptr(GTimer) timer = g_timer_new(); + + /* get the status */ + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + + /* wait for dfuDNBUSY to not be set */ + while (fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_DNBUSY) { + g_debug("waiting for FU_DFU_STATE_DFU_DNBUSY to clear"); + g_usleep(fu_dfu_device_get_download_timeout(device) * 1000); + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + /* this is a really long time to save fwupd in case + * the device has got wedged */ + if (g_timer_elapsed(timer, NULL) > 120.f) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Stuck in DFU_DNBUSY"); + return FALSE; + } + } + + /* not in an error state */ + if (fu_dfu_device_get_state(device) != FU_DFU_STATE_DFU_ERROR) + return TRUE; + + /* STM32-specific long errors */ + status = fu_dfu_device_get_status(device); + if (fu_dfu_device_get_version(device) == FU_DFU_FIRMARE_VERSION_DFUSE) { + if (status == FU_DFU_STATUS_ERR_VENDOR) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Read protection is active"); + return FALSE; + } + if (status == FU_DFU_STATUS_ERR_TARGET) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Address is wrong or unsupported"); + return FALSE; + } + } + + /* use a proper error description */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + fu_dfu_target_status_to_error_msg(status)); + return FALSE; +} + +/** + * fu_dfu_target_use_alt_setting: + * @self: a #FuDfuTarget + * @error: (nullable): optional return location for an error + * + * Opens a DFU-capable target. + * + * Returns: %TRUE for success + **/ +static gboolean +fu_dfu_target_use_alt_setting(FuDfuTarget *self, GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_DFU_TARGET(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* ensure interface is claimed */ + if (!fu_dfu_device_ensure_interface(device, error)) + return FALSE; + + /* use the correct setting */ + if (fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!g_usb_device_set_interface_alt(usb_device, + (gint)fu_dfu_device_get_interface(device), + (gint)priv->alt_setting, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot set alternate setting 0x%02x on interface %i: %s", + priv->alt_setting, + fu_dfu_device_get_interface(device), + error_local->message); + return FALSE; + } + } + + return TRUE; +} + +/** + * fu_dfu_target_setup: + * @self: a #FuDfuTarget + * @error: (nullable): optional return location for an error + * + * Opens a DFU-capable target. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_target_setup(FuDfuTarget *self, GError **error) +{ + FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + FuDevice *device = fu_device_get_proxy(FU_DEVICE(self)); + + g_return_val_if_fail(FU_IS_DFU_TARGET(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* already done */ + if (priv->done_setup) + return TRUE; + + /* superclassed */ + if (klass->setup != NULL) { + if (!klass->setup(self, error)) + return FALSE; + } + + /* GD32VF103 devices features and peripheral list */ + if (priv->alt_setting == 0x0 && + fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_GD32)) { + /* RB R8 R6 R4 VB V8 + * Flash (KB) 128 64 32 16 128 64 + * TB T8 T6 T4 CB C8 C6 C4 + * Flash (KB) 128 64 32 16 128 64 32 16 + */ + const gchar *serial = fu_device_get_serial(device); + if (serial == NULL || strlen(serial) < 4 || serial[3] != 'J') { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "GD32 serial number %s invalid", + serial); + return FALSE; + } + if (serial[2] == '2') { + fu_device_set_logical_id(FU_DEVICE(self), + "@Internal Flash /0x8000000/8*1Kg"); + } else if (serial[2] == '4') { + fu_device_set_logical_id(FU_DEVICE(self), + "@Internal Flash /0x8000000/16*1Kg"); + } else if (serial[2] == '6') { + fu_device_set_logical_id(FU_DEVICE(self), + "@Internal Flash /0x8000000/32*1Kg"); + } else if (serial[2] == '8') { + fu_device_set_logical_id(FU_DEVICE(self), + "@Internal Flash /0x8000000/64*1Kg"); + } else if (serial[2] == 'B') { + fu_device_set_logical_id(FU_DEVICE(self), + "@Internal Flash /0x8000000/128*1Kg"); + } else if (serial[2] == 'D') { + fu_device_set_logical_id(FU_DEVICE(self), + "@Internal Flash /0x8000000/256*1Kg"); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown GD32 sector size: %c", + serial[2]); + return FALSE; + } + } + + /* get string */ + if (priv->alt_idx != 0x00 && fu_device_get_logical_id(FU_DEVICE(self)) == NULL) { + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autofree gchar *alt_name = NULL; + alt_name = g_usb_device_get_string_descriptor(usb_device, priv->alt_idx, NULL); + fu_device_set_logical_id(FU_DEVICE(self), alt_name); + } + + /* parse the DfuSe format according to UM0424 */ + if (priv->sectors->len == 0) { + if (!fu_dfu_target_parse_sectors(self, + fu_device_get_logical_id(FU_DEVICE(self)), + error)) + return FALSE; + } + + /* add a dummy entry */ + if (priv->sectors->len == 0) { + FuDfuSector *sector; + sector = fu_dfu_sector_new(0x0, /* addr */ + 0x0, /* size */ + 0x0, /* size_left */ + 0x0, /* zone */ + 0x0, /* number */ + DFU_SECTOR_CAP_READABLE | DFU_SECTOR_CAP_WRITEABLE); + g_debug("no UM0424 sector description in %s", + fu_device_get_logical_id(FU_DEVICE(self))); + g_ptr_array_add(priv->sectors, sector); + } + + priv->done_setup = TRUE; + return TRUE; +} + +/** + * fu_dfu_target_mass_erase: + * @self: a #FuDfuTarget + * @error: (nullable): optional return location for an error + * + * Mass erases the device clearing all SRAM and EEPROM memory. + * + * IMPORTANT: This only works on STM32 devices from ST and AVR32 devices from Atmel. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_target_mass_erase(FuDfuTarget *self, FuProgress *progress, GError **error) +{ + FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); + if (!fu_dfu_target_setup(self, error)) + return FALSE; + if (klass->mass_erase == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "mass erase not supported"); + return FALSE; + } + return klass->mass_erase(self, progress, error); +} + +gboolean +fu_dfu_target_download_chunk(FuDfuTarget *self, + guint16 index, + GBytes *bytes, + FuProgress *progress, + GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GError) error_local = NULL; + gsize actual_length; + + /* low level packet debugging */ + if (g_getenv("FWUPD_DFU_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "Message", bytes); + + 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, + FU_DFU_REQUEST_DNLOAD, + index, + fu_dfu_device_get_interface(device), + (guint8 *)g_bytes_get_data(bytes, NULL), + g_bytes_get_size(bytes), + &actual_length, + fu_dfu_device_get_timeout(device), + NULL, + &error_local)) { + /* refresh the error code */ + fu_dfu_device_error_fixup(device, &error_local); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot download data: %s", + error_local->message); + return FALSE; + } + + /* for STM32 devices, the action only occurs when we do GetStatus */ + if (fu_dfu_device_get_version(device) == FU_DFU_FIRMARE_VERSION_DFUSE) { + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + } + + /* wait for the device to write contents to the EEPROM */ + if (g_bytes_get_size(bytes) == 0 && fu_dfu_device_get_download_timeout(device) > 0) + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); + if (fu_dfu_device_get_download_timeout(device) > 0) { + g_debug("sleeping for %ums…", fu_dfu_device_get_download_timeout(device)); + g_usleep(fu_dfu_device_get_download_timeout(device) * 1000); + } + + /* find out if the write was successful, waiting for BUSY to clear */ + if (!fu_dfu_target_check_status(self, error)) { + g_prefix_error(error, "cannot wait for busy: "); + return FALSE; + } + + g_assert_cmpint(actual_length, ==, g_bytes_get_size(bytes)); + return TRUE; +} + +GBytes * +fu_dfu_target_upload_chunk(FuDfuTarget *self, + guint16 index, + gsize buf_sz, + FuProgress *progress, + GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GError) error_local = NULL; + guint8 *buf; + gsize actual_length; + + /* unset */ + if (buf_sz == 0) + buf_sz = (gsize)fu_dfu_device_get_transfer_size(device); + + buf = g_new0(guint8, buf_sz); + 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, + FU_DFU_REQUEST_UPLOAD, + index, + fu_dfu_device_get_interface(device), + buf, + buf_sz, + &actual_length, + fu_dfu_device_get_timeout(device), + NULL, + &error_local)) { + /* refresh the error code */ + fu_dfu_device_error_fixup(device, &error_local); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot upload data: %s", + error_local->message); + return NULL; + } + + /* low level packet debugging */ + if (g_getenv("FWUPD_DFU_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Message", buf, actual_length); + + return g_bytes_new_take(buf, actual_length); +} + +void +fu_dfu_target_set_alt_idx(FuDfuTarget *self, guint8 alt_idx) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + priv->alt_idx = alt_idx; +} + +void +fu_dfu_target_set_alt_setting(FuDfuTarget *self, guint8 alt_setting) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + priv->alt_setting = alt_setting; +} + +gboolean +fu_dfu_target_attach(FuDfuTarget *self, FuProgress *progress, GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); + + /* ensure populated */ + if (!fu_dfu_target_setup(self, error)) + return FALSE; + + /* implemented as part of a superclass */ + if (klass->attach != NULL) + return klass->attach(self, progress, error); + + /* normal DFU mode just needs a bus reset */ + return fu_dfu_device_reset(device, progress, error); +} + +static FuChunk * +fu_dfu_target_upload_element_dfu(FuDfuTarget *self, + guint32 address, + gsize expected_size, + gsize maximum_size, + FuProgress *progress, + GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + GBytes *chunk_tmp; + guint percentage_size = expected_size > 0 ? expected_size : maximum_size; + gsize total_size = 0; + guint16 transfer_size = fu_dfu_device_get_transfer_size(device); + g_autoptr(GBytes) contents = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* update UI */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + + /* get all the chunks from the hardware */ + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); + for (guint16 idx = 0; idx < G_MAXUINT16; idx++) { + guint32 chunk_size; + + /* read chunk of data */ + chunk_tmp = fu_dfu_target_upload_chunk(self, + idx, + 0, /* device transfer size */ + progress, + error); + if (chunk_tmp == NULL) + return NULL; + + /* keep a sum of all the chunks */ + chunk_size = (guint32)g_bytes_get_size(chunk_tmp); + total_size += chunk_size; + + /* add to array */ + g_debug("got #%04x chunk of size %" G_GUINT32_FORMAT, idx, chunk_size); + g_ptr_array_add(chunks, chunk_tmp); + + /* update UI */ + if (chunk_size > 0) + fu_progress_set_percentage_full(progress, total_size, percentage_size); + + /* detect short write as EOF */ + if (chunk_size < transfer_size) + break; + } + + /* check final size */ + if (expected_size > 0) { + if (total_size != expected_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid size, got %" G_GSIZE_FORMAT ", " + "expected %" G_GSIZE_FORMAT, + total_size, + expected_size); + return NULL; + } + } + + /* done */ + fu_progress_set_percentage(progress, 100); + + /* create new image */ + contents = fu_dfu_utils_bytes_join_array(chunks); + return fu_chunk_bytes_new(contents); +} + +static FuChunk * +fu_dfu_target_upload_element(FuDfuTarget *self, + guint32 address, + gsize expected_size, + gsize maximum_size, + FuProgress *progress, + GError **error) +{ + FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); + + /* implemented as part of a superclass */ + if (klass->upload_element != NULL) { + return klass + ->upload_element(self, address, expected_size, maximum_size, progress, error); + } + return fu_dfu_target_upload_element_dfu(self, + address, + expected_size, + maximum_size, + progress, + error); +} + +static guint32 +fu_dfu_target_get_size_of_zone(FuDfuTarget *self, guint16 zone) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + guint32 len = 0; + for (guint i = 0; i < priv->sectors->len; i++) { + FuDfuSector *sector = g_ptr_array_index(priv->sectors, i); + if (fu_dfu_sector_get_zone(sector) != zone) + continue; + len += fu_dfu_sector_get_size(sector); + } + return len; +} + +/* private */ +gboolean +fu_dfu_target_upload(FuDfuTarget *self, + FuFirmware *firmware, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + FuDfuSector *sector; + guint16 zone_cur; + guint32 zone_size = 0; + guint32 zone_last = G_MAXUINT; + g_autoptr(FuFirmware) image = NULL; + + g_return_val_if_fail(FU_IS_DFU_TARGET(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* ensure populated */ + if (!fu_dfu_target_setup(self, error)) + return FALSE; + + /* can the target do this? */ + if (!fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(self)), + FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "target cannot do uploading"); + return FALSE; + } + + /* use correct alt */ + if (!fu_dfu_target_use_alt_setting(self, error)) + return FALSE; + + /* no open?! */ + if (priv->sectors->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no sectors defined for target"); + return FALSE; + } + + /* create a new image */ + image = fu_firmware_new(); + fu_firmware_set_id(image, fu_device_get_logical_id(FU_DEVICE(self))); + fu_firmware_set_idx(image, priv->alt_setting); + + /* get all the sectors for the device */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, priv->sectors->len); + for (guint i = 0; i < priv->sectors->len; i++) { + g_autoptr(FuChunk) chk = NULL; + + /* only upload to the start of any zone:sector */ + sector = g_ptr_array_index(priv->sectors, i); + zone_cur = fu_dfu_sector_get_zone(sector); + if (zone_cur == zone_last) + continue; + + /* get the size of the entire continuous zone */ + zone_size = fu_dfu_target_get_size_of_zone(self, zone_cur); + zone_last = zone_cur; + + /* get the first element from the hardware */ + g_debug("starting upload from 0x%08x (0x%04x)", + fu_dfu_sector_get_address(sector), + zone_size); + chk = fu_dfu_target_upload_element(self, + fu_dfu_sector_get_address(sector), + 0, /* expected */ + zone_size, /* maximum */ + fu_progress_get_child(progress), + error); + if (chk == NULL) + return FALSE; + + /* this chunk was uploaded okay */ + fu_firmware_add_chunk(image, chk); + fu_progress_step_done(progress); + } + + /* success */ + fu_firmware_add_image(firmware, image); + return TRUE; +} + +static gchar * +_g_bytes_compare_verbose(GBytes *bytes1, GBytes *bytes2) +{ + const guint8 *data1; + const guint8 *data2; + gsize length1; + gsize length2; + + data1 = g_bytes_get_data(bytes1, &length1); + data2 = g_bytes_get_data(bytes2, &length2); + + /* not the same length */ + if (length1 != length2) { + return g_strdup_printf("got %" G_GSIZE_FORMAT " bytes, " + "expected %" G_GSIZE_FORMAT, + length1, + length2); + } + + /* return 00 01 02 03 */ + for (guint i = 0; i < length1; i++) { + if (data1[i] != data2[i]) { + return g_strdup_printf("got 0x%02x, expected 0x%02x @ 0x%04x", + data1[i], + data2[i], + i); + } + } + return NULL; +} + +static gboolean +fu_dfu_target_download_element_dfu(FuDfuTarget *self, + FuChunk *chk, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + guint32 nr_chunks; + guint16 transfer_size = fu_dfu_device_get_transfer_size(device); + g_autoptr(GBytes) bytes = NULL; + + /* round up as we have to transfer incomplete blocks */ + bytes = fu_chunk_get_bytes(chk); + nr_chunks = (guint)ceil((gdouble)g_bytes_get_size(bytes) / (gdouble)transfer_size); + if (nr_chunks == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "zero-length firmware"); + return FALSE; + } + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + for (guint32 i = 0; i < nr_chunks + 1; i++) { + gsize length; + guint32 offset; + g_autoptr(GBytes) bytes_tmp = NULL; + + /* caclulate the offset into the chunk data */ + offset = i * transfer_size; + + /* we have to write one final zero-sized chunk for EOF */ + if (i < nr_chunks) { + length = g_bytes_get_size(bytes) - offset; + if (length > transfer_size) + length = transfer_size; + bytes_tmp = fu_bytes_new_offset(bytes, offset, length, error); + if (bytes_tmp == NULL) + return FALSE; + } else { + bytes_tmp = g_bytes_new(NULL, 0); + } + g_debug("writing #%04x chunk of size %" G_GSIZE_FORMAT, + i, + g_bytes_get_size(bytes_tmp)); + if (!fu_dfu_target_download_chunk(self, i, bytes_tmp, progress, error)) + return FALSE; + + /* update UI */ + fu_progress_set_percentage_full(progress, i + 1, nr_chunks + 1); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_target_download_element(FuDfuTarget *self, + FuChunk *chk, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDevice *device = fu_device_get_proxy(FU_DEVICE(self)); + FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); + + /* progress */ + if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY && + fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 4, NULL); + } else { + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, 1); + } + + /* implemented as part of a superclass */ + if (klass->download_element != NULL) { + if (!klass->download_element(self, + chk, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + } else { + if (!fu_dfu_target_download_element_dfu(self, + chk, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + } + fu_progress_step_done(progress); + + /* verify */ + if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY && + fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GBytes) bytes_tmp = NULL; + g_autoptr(FuChunk) chunk_tmp = NULL; + bytes = fu_chunk_get_bytes(chk); + chunk_tmp = fu_dfu_target_upload_element(self, + fu_chunk_get_address(chk), + g_bytes_get_size(bytes), + g_bytes_get_size(bytes), + fu_progress_get_child(progress), + error); + if (chunk_tmp == NULL) + return FALSE; + bytes_tmp = fu_chunk_get_bytes(chunk_tmp); + if (g_bytes_compare(bytes_tmp, bytes) != 0) { + g_autofree gchar *bytes_cmp_str = NULL; + bytes_cmp_str = _g_bytes_compare_verbose(bytes_tmp, bytes); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "verify failed: %s", + bytes_cmp_str); + return FALSE; + } + fu_progress_step_done(progress); + } + + return TRUE; +} + +/** + * fu_dfu_target_download: + * @self: a #FuDfuTarget + * @image: a #FuFirmware + * @flags: DFU target flags, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY + * @error: (nullable): optional return location for an error + * + * Downloads firmware from the host to the target, optionally verifying + * the transfer. + * + * Returns: %TRUE for success + **/ +gboolean +fu_dfu_target_download(FuDfuTarget *self, + FuFirmware *image, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error) +{ + FuDevice *device = fu_device_get_proxy(FU_DEVICE(self)); + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) chunks = NULL; + + g_return_val_if_fail(FU_IS_DFU_TARGET(self), FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(image), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* ensure populated */ + if (!fu_dfu_target_setup(self, error)) + return FALSE; + + /* can the target do this? */ + if (!fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "target cannot do downloading"); + return FALSE; + } + + /* use correct alt */ + if (!fu_dfu_target_use_alt_setting(self, error)) + return FALSE; + + /* download all chunks in the image to the device */ + chunks = fu_firmware_get_chunks(image, error); + if (chunks == NULL) + return FALSE; + if (chunks->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no image chunks"); + return FALSE; + } + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_debug("downloading chunk at 0x%04x", fu_chunk_get_address(chk)); + + /* auto-detect missing firmware address -- this assumes + * that the first target is the main program memory and that + * there is only one element in the firmware file */ + if (flags & DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC && + fu_chunk_get_address(chk) == 0x0 && chunks->len == 1 && + priv->sectors->len > 0) { + FuDfuSector *sector = g_ptr_array_index(priv->sectors, 0); + g_debug("fixing up firmware address from 0x0 to 0x%x", + fu_dfu_sector_get_address(sector)); + fu_chunk_set_address(chk, fu_dfu_sector_get_address(sector)); + } + + /* download to device */ + if (!fu_dfu_target_download_element(self, + chk, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + } + + if (fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_MANIFEST_POLL) && + fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_MANIFEST_TOL)) + if (!fu_dfu_target_manifest_wait(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +/** + * fu_dfu_target_get_alt_setting: + * @self: a #FuDfuTarget + * + * Gets the alternate setting to use for this interface. + * + * Returns: the alternative setting, typically zero + **/ +guint8 +fu_dfu_target_get_alt_setting(FuDfuTarget *self) +{ + FuDfuTargetPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DFU_TARGET(self), 0xff); + return priv->alt_setting; +} + +static void +fu_dfu_target_class_init(FuDfuTargetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_dfu_target_to_string; + object_class->finalize = fu_dfu_target_finalize; +} diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-target.h b/fwupd-1.8.6/plugins/dfu/fu-dfu-target.h new file mode 100644 index 0000000000000000000000000000000000000000..9a992d2218c264d265d55f9a38ffe4a477810c23 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-target.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-dfu-common.h" +#include "fu-dfu-sector.h" + +#define FU_TYPE_DFU_TARGET (fu_dfu_target_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuDfuTarget, fu_dfu_target, FU, DFU_TARGET, FuDevice) + +/** + * FuDfuTargetTransferFlags: + * @DFU_TARGET_TRANSFER_FLAG_NONE: No flags set + * @DFU_TARGET_TRANSFER_FLAG_VERIFY: Verify the download once complete + * @DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID: Allow downloading images with wildcard VIDs + * @DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID: Allow downloading images with wildcard PIDs + * @DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC: Automatically detect the address to use + * + * The optional flags used for transferring firmware. + **/ +typedef enum { + DFU_TARGET_TRANSFER_FLAG_NONE = 0, + DFU_TARGET_TRANSFER_FLAG_VERIFY = (1 << 0), + DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID = (1 << 4), + DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID = (1 << 5), + DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC = (1 << 7), + /*< private >*/ + DFU_TARGET_TRANSFER_FLAG_LAST +} FuDfuTargetTransferFlags; + +struct _FuDfuTargetClass { + FuDeviceClass parent_class; + gboolean (*setup)(FuDfuTarget *self, GError **error); + gboolean (*attach)(FuDfuTarget *self, FuProgress *progress, GError **error); + gboolean (*detach)(FuDfuTarget *self, FuProgress *progress, GError **error); + gboolean (*mass_erase)(FuDfuTarget *self, FuProgress *progress, GError **error); + FuChunk *(*upload_element)(FuDfuTarget *self, + guint32 address, + gsize expected_size, + gsize maximum_size, + FuProgress *progress, + GError **error); + gboolean (*download_element)(FuDfuTarget *self, + FuChunk *chk, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error); +}; + +GPtrArray * +fu_dfu_target_get_sectors(FuDfuTarget *self); +FuDfuSector * +fu_dfu_target_get_sector_default(FuDfuTarget *self); +guint8 +fu_dfu_target_get_alt_setting(FuDfuTarget *self); +gboolean +fu_dfu_target_upload(FuDfuTarget *self, + FuFirmware *firmware, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error); +gboolean +fu_dfu_target_setup(FuDfuTarget *self, GError **error); +gboolean +fu_dfu_target_download(FuDfuTarget *self, + FuFirmware *image, + FuProgress *progress, + FuDfuTargetTransferFlags flags, + GError **error); +gboolean +fu_dfu_target_mass_erase(FuDfuTarget *self, FuProgress *progress, GError **error); diff --git a/fwupd-1.8.6/plugins/dfu/fu-dfu-tool.c b/fwupd-1.8.6/plugins/dfu/fu-dfu-tool.c new file mode 100644 index 0000000000000000000000000000000000000000..d828ffb2717fa5b4378b1d9dbcc5c18976c7945a --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/fu-dfu-tool.c @@ -0,0 +1,858 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#ifdef HAVE_GIO_UNIX +#include +#endif + +#include "fu-context-private.h" +#include "fu-dfu-device.h" +#include "fu-dfu-sector.h" + +typedef struct { + GCancellable *cancellable; + GPtrArray *cmd_array; + gboolean force; + gchar *device_vid_pid; + guint16 transfer_size; + FuContext *ctx; + GUsbContext *usb_context; +} FuDfuTool; + +static void +fu_dfu_tool_free(FuDfuTool *self) +{ + if (self == NULL) + return; + g_free(self->device_vid_pid); + if (self->cancellable != NULL) + g_object_unref(self->cancellable); + if (self->usb_context != NULL) + g_object_unref(self->usb_context); + g_object_unref(self->ctx); + if (self->cmd_array != NULL) + g_ptr_array_unref(self->cmd_array); + g_free(self); +} +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuDfuTool, fu_dfu_tool_free) +#pragma clang diagnostic pop + +typedef gboolean (*FuUtilPrivateCb)(FuDfuTool *util, gchar **values, GError **error); + +typedef struct { + gchar *name; + gchar *arguments; + gchar *description; + FuUtilPrivateCb callback; +} FuUtilItem; + +static void +fu_dfu_tool_item_free(FuUtilItem *item) +{ + g_free(item->name); + g_free(item->arguments); + g_free(item->description); + g_free(item); +} + +static gint +fu_dfu_tool_sort_command_name_cb(FuUtilItem **item1, FuUtilItem **item2) +{ + return g_strcmp0((*item1)->name, (*item2)->name); +} + +static void +fu_dfu_tool_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_dfu_tool_get_descriptions(GPtrArray *array) +{ + gsize len; + const gsize max_len = 31; + FuUtilItem *item; + GString *string; + + /* print each command */ + string = g_string_new(""); + for (guint i = 0; i < array->len; i++) { + 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 (guint 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 (guint 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_dfu_tool_run(FuDfuTool *self, const gchar *command, gchar **values, GError **error) +{ + /* find command */ + for (guint i = 0; i < self->cmd_array->len; i++) { + FuUtilItem *item = g_ptr_array_index(self->cmd_array, i); + if (g_strcmp0(item->name, command) == 0) + return item->callback(self, values, error); + } + + /* not found */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + /* TRANSLATORS: error message */ + _("Command not found")); + return FALSE; +} + +static FuDfuDevice * +fu_dfu_tool_get_default_device(FuDfuTool *self, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* get all the DFU devices */ + self->usb_context = g_usb_context_new(error); + if (self->usb_context == NULL) + return NULL; + g_usb_context_enumerate(self->usb_context); + + /* we specified it manually */ + if (self->device_vid_pid != NULL) { + gchar *tmp; + guint64 pid; + guint64 vid; + g_autoptr(GUsbDevice) usb_device = NULL; + + /* parse */ + vid = g_ascii_strtoull(self->device_vid_pid, &tmp, 16); + if (vid == 0 || vid > G_MAXUINT16) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid format of VID:PID"); + return NULL; + } + if (tmp[0] != ':') { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid format of VID:PID"); + return NULL; + } + pid = g_ascii_strtoull(tmp + 1, NULL, 16); + if (pid == 0 || pid > G_MAXUINT16) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid format of VID:PID"); + return NULL; + } + + /* find device */ + usb_device = g_usb_context_find_by_vid_pid(self->usb_context, + (guint16)vid, + (guint16)pid, + error); + if (usb_device == NULL) { + g_prefix_error(error, + "no device matches for %04x:%04x: ", + (guint)vid, + (guint)pid); + return NULL; + } + return fu_dfu_device_new(self->ctx, usb_device); + } + + /* auto-detect first device */ + devices = g_usb_context_get_devices(self->usb_context); + for (guint i = 0; i < devices->len; i++) { + GUsbDevice *usb_device = g_ptr_array_index(devices, i); + g_autoptr(FuDfuDevice) device = fu_dfu_device_new(self->ctx, usb_device); + if (fu_device_probe(FU_DEVICE(device), NULL)) + return g_steal_pointer(&device); + } + + /* failed */ + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no DFU devices found"); + return NULL; +} + +static gboolean +fu_dfu_device_wait_for_replug(FuDfuTool *self, FuDfuDevice *device, guint timeout, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GUsbDevice) usb_device2 = NULL; + g_autoptr(GError) error_local = NULL; + + /* close */ + if (!fu_device_close(FU_DEVICE(device), &error_local)) + g_debug("failed to close: %s", error_local->message); + + /* watch the device disappear and re-appear */ + usb_device2 = g_usb_context_wait_for_replug(self->usb_context, usb_device, timeout, error); + if (usb_device2 == NULL) + return FALSE; + + /* re-open with new device set */ + fu_usb_device_set_dev(FU_USB_DEVICE(device), usb_device2); + if (!fu_device_open(FU_DEVICE(device), error)) + return FALSE; + if (!fu_dfu_device_refresh_and_clear(device, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_dfu_tool_parse_firmware_from_file(FuFirmware *firmware, + GFile *file, + FwupdInstallFlags flags, + GError **error) +{ + gchar *contents = NULL; + gsize length = 0; + g_autoptr(GBytes) bytes = NULL; + if (!g_file_load_contents(file, NULL, &contents, &length, NULL, error)) + return FALSE; + bytes = g_bytes_new_take(contents, length); + return fu_firmware_parse(firmware, bytes, flags, error); +} + +static gboolean +fu_dfu_tool_write_firmware_to_file(FuFirmware *firmware, GFile *file, GError **error) +{ + const guint8 *data; + gsize length = 0; + g_autoptr(GBytes) bytes = fu_firmware_write(firmware, error); + if (bytes == NULL) + return FALSE; + data = g_bytes_get_data(bytes, &length); + return g_file_replace_contents(file, + (const gchar *)data, + length, + NULL, + FALSE, + G_FILE_CREATE_NONE, + NULL, + NULL, /* cancellable */ + error); +} + +static gboolean +fu_dfu_tool_replace_data(FuDfuTool *self, gchar **values, GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Available as `fwupdtool firmware-patch`"); + return FALSE; +} + +static void +fu_tool_action_changed_cb(FuProgress *progress, gpointer dummy, FuDfuTool *self) +{ + g_print("%s:\t%u%%\n", + fwupd_status_to_string(fu_progress_get_status(progress)), + fu_progress_get_percentage(progress)); +} + +static gboolean +fu_dfu_tool_read_alt(FuDfuTool *self, gchar **values, GError **error) +{ + FuDfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_NONE; + g_autofree gchar *str_debug = NULL; + g_autoptr(FuDfuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuDfuTarget) target = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* check args */ + if (g_strv_length(values) < 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid arguments, expected " + "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID"); + return FALSE; + } + + /* open correct device */ + device = fu_dfu_tool_get_default_device(self, error); + if (device == NULL) + return FALSE; + if (self->transfer_size > 0) + fu_dfu_device_set_transfer_size(device, self->transfer_size); + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + + /* set up progress */ + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + + /* APP -> DFU */ + if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("detaching"); + if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, + device, + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, + error)) + return FALSE; + } + + /* transfer */ + target = fu_dfu_device_get_target_by_alt_name(device, values[1], NULL); + if (target == NULL) { + gchar *endptr; + guint64 tmp = g_ascii_strtoull(values[1], &endptr, 10); + if (tmp > 0xff || endptr[0] != '\0') { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to parse alt-setting '%s'", + values[1]); + return FALSE; + } + target = fu_dfu_device_get_target_by_alt_setting(device, (guint8)tmp, error); + if (target == NULL) + return FALSE; + } + + /* do transfer */ + firmware = fu_dfuse_firmware_new(); + fu_dfu_firmware_set_vid(FU_DFU_FIRMWARE(firmware), fu_dfu_device_get_runtime_vid(device)); + fu_dfu_firmware_set_pid(FU_DFU_FIRMWARE(firmware), fu_dfu_device_get_runtime_pid(device)); + if (!fu_dfu_target_upload(target, firmware, progress, flags, error)) + return FALSE; + + /* do host reset */ + if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, + device, + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, + error)) + return FALSE; + + /* save file */ + file = g_file_new_for_path(values[0]); + if (!fu_dfu_tool_write_firmware_to_file(firmware, file, error)) + return FALSE; + + /* print the new object */ + str_debug = fu_firmware_to_string(firmware); + g_debug("DFU: %s", str_debug); + + /* success */ + g_print("Successfully uploaded from device\n"); + return TRUE; +} + +static gboolean +fu_dfu_tool_read(FuDfuTool *self, gchar **values, GError **error) +{ + FuDfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_NONE; + g_autofree gchar *str_debug = NULL; + g_autoptr(FuDfuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* check args */ + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid arguments, expected FILENAME"); + return FALSE; + } + + /* open correct device */ + device = fu_dfu_tool_get_default_device(self, error); + if (device == NULL) + return FALSE; + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + + /* APP -> DFU */ + if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, + device, + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, + error)) { + return FALSE; + } + } + + /* transfer */ + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + firmware = fu_dfu_device_upload(device, progress, flags, error); + if (firmware == NULL) + return FALSE; + + /* do host reset */ + if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, + device, + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, + error)) + return FALSE; + + /* save file */ + file = g_file_new_for_path(values[0]); + if (!fu_dfu_tool_write_firmware_to_file(firmware, file, error)) + return FALSE; + + /* print the new object */ + str_debug = fu_firmware_to_string(firmware); + g_debug("DFU: %s", str_debug); + + /* success */ + g_print("successfully uploaded from device\n"); + return TRUE; +} + +static gboolean +fu_dfu_tool_write_alt(FuDfuTool *self, gchar **values, GError **error) +{ + FuDfuTargetTransferFlags flags = DFU_TARGET_TRANSFER_FLAG_VERIFY; + g_autofree gchar *str_debug = NULL; + g_autoptr(FuDfuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuFirmware) image = NULL; + g_autoptr(FuDfuTarget) target = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* check args */ + if (g_strv_length(values) < 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid arguments, expected " + "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID " + "[IMAGE-ALT-NAME|IMAGE-ALT-ID]"); + return FALSE; + } + + /* open file */ + firmware = fu_dfuse_firmware_new(); + file = g_file_new_for_path(values[0]); + if (!fu_dfu_tool_parse_firmware_from_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, error)) + return FALSE; + + /* open correct device */ + device = fu_dfu_tool_get_default_device(self, error); + if (device == NULL) + return FALSE; + if (self->transfer_size > 0) + fu_dfu_device_set_transfer_size(device, self->transfer_size); + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + + /* set up progress */ + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + + /* APP -> DFU */ + if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("detaching"); + if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, device, 5000, error)) + return FALSE; + } + + /* print the new object */ + str_debug = fu_firmware_to_string(firmware); + g_debug("DFU: %s", str_debug); + + /* get correct target on device */ + target = fu_dfu_device_get_target_by_alt_name(device, values[1], NULL); + if (target == NULL) { + gchar *endptr; + guint64 tmp = g_ascii_strtoull(values[1], &endptr, 10); + if (tmp > 0xff || endptr[0] != '\0') { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to parse alt-setting '%s'", + values[1]); + return FALSE; + } + target = fu_dfu_device_get_target_by_alt_setting(device, (guint8)tmp, error); + if (target == NULL) + return FALSE; + } + + /* allow overriding the firmware alt-setting */ + if (g_strv_length(values) > 2) { + image = fu_firmware_get_image_by_id(firmware, values[2], NULL); + if (image == NULL) { + gchar *endptr; + guint64 tmp = g_ascii_strtoull(values[2], &endptr, 10); + if (tmp > 0xff || endptr[0] != '\0') { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to parse image alt-setting '%s'", + values[2]); + return FALSE; + } + image = fu_firmware_get_image_by_idx(firmware, tmp, error); + if (image == NULL) + return FALSE; + } + } else { + g_print("WARNING: Using default firmware image\n"); + image = fu_firmware_get_image_by_id(firmware, NULL, error); + if (image == NULL) + return FALSE; + } + + /* transfer */ + if (!fu_dfu_target_download(target, image, progress, flags, error)) + return FALSE; + + /* do host reset */ + if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, + device, + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, + error)) + return FALSE; + + /* success */ + g_print("Successfully downloaded to device\n"); + return TRUE; +} + +static gboolean +fu_dfu_tool_write(FuDfuTool *self, gchar **values, GError **error) +{ + FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NO_SEARCH; + g_autoptr(FuDfuDevice) device = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* check args */ + if (g_strv_length(values) < 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid arguments, expected FILENAME"); + return FALSE; + } + + /* open file */ + fw = fu_bytes_get_contents(values[0], error); + if (fw == NULL) + return FALSE; + + /* open correct device */ + device = fu_dfu_tool_get_default_device(self, error); + if (device == NULL) + return FALSE; + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_dfu_device_refresh(device, error)) + return FALSE; + + /* APP -> DFU */ + if (!fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_device_detach_full(FU_DEVICE(device), progress, error)) + return FALSE; + if (!fu_dfu_device_wait_for_replug(self, + device, + fu_device_get_remove_delay(FU_DEVICE(device)), + error)) + return FALSE; + } + + /* allow wildcards */ + if (self->force) { + flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID; + flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; + } + + /* transfer */ + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_tool_action_changed_cb), + self); + if (!fu_device_write_firmware(FU_DEVICE(device), fw, progress, flags, error)) + return FALSE; + + /* do host reset */ + if (!fu_device_attach_full(FU_DEVICE(device), progress, error)) + return FALSE; + + if (fu_device_has_private_flag(FU_DEVICE(device), FU_DFU_DEVICE_FLAG_MANIFEST_TOL)) { + if (!fu_dfu_device_wait_for_replug(self, + device, + fu_device_get_remove_delay(FU_DEVICE(device)), + error)) + return FALSE; + } + + /* success */ + g_print("%u bytes successfully downloaded to device\n", (guint)g_bytes_get_size(fw)); + return TRUE; +} + +#ifdef HAVE_GIO_UNIX +static gboolean +fu_dfu_tool_sigint_cb(gpointer user_data) +{ + FuDfuTool *self = (FuDfuTool *)user_data; + g_debug("Handling SIGINT"); + g_cancellable_cancel(self->cancellable); + return FALSE; +} +#endif + +int +main(int argc, char *argv[]) +{ + gboolean ret; + gboolean verbose = FALSE; + gboolean version = FALSE; + g_autofree gchar *cmd_descriptions = NULL; + g_autoptr(FuDfuTool) self = g_new0(FuDfuTool, 1); + g_autoptr(GError) error = NULL; + g_autoptr(GOptionContext) context = NULL; + const GOptionEntry options[] = { + {"version", '\0', 0, G_OPTION_ARG_NONE, &version, _("Print the version number"), NULL}, + {"verbose", + 'v', + 0, + G_OPTION_ARG_NONE, + &verbose, + N_("Print verbose debug statements"), + NULL}, + {"device", + 'd', + 0, + G_OPTION_ARG_STRING, + &self->device_vid_pid, + N_("Specify Vendor/Product ID(s) of DFU device"), + N_("VID:PID")}, + {"transfer-size", + 't', + 0, + G_OPTION_ARG_STRING, + &self->transfer_size, + N_("Specify the number of bytes per USB transfer"), + N_("BYTES")}, + {"force", + '\0', + 0, + G_OPTION_ARG_NONE, + &self->force, + N_("Force the action ignoring all warnings"), + NULL}, + {NULL}}; + + setlocale(LC_ALL, ""); + + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* add commands */ + self->cmd_array = g_ptr_array_new_with_free_func((GDestroyNotify)fu_dfu_tool_item_free); + fu_dfu_tool_add(self->cmd_array, + "read", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME"), + /* TRANSLATORS: command description */ + _("Read firmware from device into a file"), + fu_dfu_tool_read); + fu_dfu_tool_add(self->cmd_array, + "read-alt", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID"), + /* TRANSLATORS: command description */ + _("Read firmware from one partition into a file"), + fu_dfu_tool_read_alt); + fu_dfu_tool_add(self->cmd_array, + "write", + NULL, + /* TRANSLATORS: command description */ + _("Write firmware from file into device"), + fu_dfu_tool_write); + fu_dfu_tool_add(self->cmd_array, + "write-alt", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]"), + /* TRANSLATORS: command description */ + _("Write firmware from file into one partition"), + fu_dfu_tool_write_alt); + fu_dfu_tool_add(self->cmd_array, + "replace-data", + NULL, + /* TRANSLATORS: command description */ + _("Replace data in an existing firmware file"), + fu_dfu_tool_replace_data); + + /* use quirks */ + self->ctx = fu_context_new(); + if (!fu_context_load_quirks(self->ctx, FU_QUIRKS_LOAD_FLAG_NONE, &error)) { + /* TRANSLATORS: quirks are device-specific workarounds */ + g_print("%s: %s\n", _("Failed to load quirks"), error->message); + return EXIT_FAILURE; + } + + /* do stuff on ctrl+c */ + self->cancellable = g_cancellable_new(); +#ifdef HAVE_GIO_UNIX + g_unix_signal_add_full(G_PRIORITY_DEFAULT, SIGINT, fu_dfu_tool_sigint_cb, self, NULL); +#endif + + /* sort by command name */ + g_ptr_array_sort(self->cmd_array, (GCompareFunc)fu_dfu_tool_sort_command_name_cb); + + /* get a list of the commands */ + context = g_option_context_new(NULL); + cmd_descriptions = fu_dfu_tool_get_descriptions(self->cmd_array); + g_option_context_set_summary(context, cmd_descriptions); + + /* TRANSLATORS: DFU stands for device firmware update */ + g_set_application_name(_("DFU Utility")); + g_option_context_add_main_entries(context, options, NULL); + ret = g_option_context_parse(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) + (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); + + /* version */ + if (version) { + g_print("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + return EXIT_SUCCESS; + } + + /* run the specified command */ + ret = fu_dfu_tool_run(self, argv[1], (gchar **)&argv[2], &error); + if (!ret) { + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) { + g_autofree gchar *tmp = NULL; + tmp = g_option_context_get_help(context, TRUE, NULL); + g_print("%s\n\n%s", error->message, tmp); + } else { + g_print("%s\n", error->message); + } + return EXIT_FAILURE; + } + + /* success/ */ + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/plugins/dfu/meson.build b/fwupd-1.8.6/plugins/dfu/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1d51df061e366ddbeb600fa81c518670f797e7f9 --- /dev/null +++ b/fwupd-1.8.6/plugins/dfu/meson.build @@ -0,0 +1,81 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginDfu"'] + +plugin_quirks += files('dfu.quirk') + +plugin_builtin_dfu = static_library('fu_plugin_dfu', + sources: [ + 'fu-dfu-plugin.c', + 'fu-dfu-common.c', + 'fu-dfu-device.c', + 'fu-dfu-sector.c', + 'fu-dfu-target.c', + 'fu-dfu-target-stm.c', + 'fu-dfu-target-avr.c', + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: [ + plugin_deps, + libm, + ], + link_with: plugin_libs, +) +plugin_builtins += plugin_builtin_dfu + +if get_option('compat_cli') +fu_dfu_tool = executable( + 'dfu-tool', + sources: [ + 'fu-dfu-tool.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_dfu, + ], + c_args: cargs, + install: true, + install_rpath: libdir_pkg, + install_dir: bindir +) +endif + +if get_option('compat_cli') and get_option('man') + configure_file( + input: 'dfu-tool.1', + output: 'dfu-tool.1', + configuration: conf, + install: true, + install_dir: join_paths(mandir, 'man1'), + ) +endif + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'fu-dfu-self-test', + sources: [ + 'fu-dfu-self-test.c' + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + libm, + ], + link_with: [ + plugin_libs, + plugin_builtin_dfu, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('fu-dfu-self-test', e, env: env) # added to installed-tests +endif + +plugindfu_incdir = include_directories('.') +endif diff --git a/fwupd-1.8.6/plugins/ebitdo/README.md b/fwupd-1.8.6/plugins/ebitdo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..63473bf90228251add834ccf1555a5e0c6cee691 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/README.md @@ -0,0 +1,54 @@ +# 8BitDo + +## Introduction + +This plugin can flash the firmware on the 8BitDo game pads. + +Ebitdo support is supported directly by this project with the embedded libebitdo +library and is possible thanks to the vendor open sourcing the flashing tool. + +The 8BitDo devices share legacy USB VID/PIDs with other projects and so we have +to be a bit careful to not claim other devices as our own. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format. The binary file has a vendor-specific header +that is used when flashing the image. + +This plugin supports the following protocol ID: + +* com.8bitdo + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_2DC8&PID_AB11&REV_0001` +* `USB\VID_2DC8&PID_AB11` +* `USB\VID_2DC8` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different USB VID and PID in a bootloader mode. On attach the device again +re-enumerates back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, which is set to various different +values depending on the model and device mode. The list of USB VIDs used is: + +* `USB:0x2DC8` +* `USB:0x0483` +* `USB:0x1002` +* `USB:0x1235` +* `USB:0x2002` +* `USB:0x8000` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/ebitdo/data/m30.txt b/fwupd-1.8.6/plugins/ebitdo/data/m30.txt new file mode 100644 index 0000000000000000000000000000000000000000..34c620e5e296e0a16f01155c327a3cf25205109f --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/data/m30.txt @@ -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 --git a/fwupd-1.8.6/plugins/ebitdo/data/nes30pro.txt b/fwupd-1.8.6/plugins/ebitdo/data/nes30pro.txt new file mode 100644 index 0000000000000000000000000000000000000000..28adf7ca7fe619c6acb71db95a71a6b59c01ef75 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/data/nes30pro.txt @@ -0,0 +1,137 @@ +Bus 003 Device 017: ID 2002:9000 DAP Technologies +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x2002 DAP Technologies + idProduct 0x9000 + bcdDevice 0.01 + iManufacturer 1 8Bitdo NES30 Pro + iProduct 2 8Bitdo NES30 Pro + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + 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 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 32 + 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) + +Bus 003 Device 019: ID 0483:5750 STMicroelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 STMicroelectronics + idProduct 0x5750 + bcdDevice 2.00 + iManufacturer 1 8BitdoJoy + iProduct 2 8Bitdo + iSerial 3 BootMod + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + 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.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 33 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 32 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 16 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/ebitdo/data/scf30.txt b/fwupd-1.8.6/plugins/ebitdo/data/scf30.txt new file mode 100644 index 0000000000000000000000000000000000000000..db6f23d50bfa7da0abcb9e1cd7829c09af04aa65 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/data/scf30.txt @@ -0,0 +1,134 @@ + +Bus 002 Device 053: ID 1235:ab21 Focusrite-Novation +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x1235 Focusrite-Novation + idProduct 0xab21 + bcdDevice 0.01 + iManufacturer 1 + iProduct 2 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + 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.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 99 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 32 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 16 + +Bus 002 Device 055: ID 0483:5750 STMicroelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 STMicroelectronics + idProduct 0x5750 + bcdDevice 2.00 + iManufacturer 1 + iProduct 2 + iSerial 3 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + 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.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 33 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 32 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 16 diff --git a/fwupd-1.8.6/plugins/ebitdo/data/sf30pro.txt b/fwupd-1.8.6/plugins/ebitdo/data/sf30pro.txt new file mode 100644 index 0000000000000000000000000000000000000000..e1cb4c4a77e4dcead9b1c65f34cd14c5856d9bf0 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/data/sf30pro.txt @@ -0,0 +1,362 @@ +Start + Y +--------- +Bus 001 Device 008: ID 057e:2009 Nintendo Co., Ltd +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x057e Nintendo Co., Ltd + idProduct 0x2009 + bcdDevice 2.00 + iManufacturer 1 Nintendo Co., Ltd. + iProduct 2 Pro Controller + iSerial 3 000000000001 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 No Subclass + bInterfaceProtocol 0 None + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 203 + 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 8 + 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 8 +Device Status: 0x0000 + (Bus Powered) + + +Start + B +------------ +Bus 001 Device 009: ID 2dc8:6000 +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x2dc8 + idProduct 0x6000 + bcdDevice 0.01 + iManufacturer 1 8Bitdo SF30 Pro + iProduct 2 8Bitdo SF30 Pro + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + 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 No Subclass + bInterfaceProtocol 0 None + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 123 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 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) + + +Start + A +--------- +Bus 001 Device 012: ID 054c:05c4 Sony Corp. DualShock 4 +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x054c Sony Corp. + idProduct 0x05c4 DualShock 4 + bcdDevice 1.00 + iManufacturer 1 Sony Computer Entertainment + iProduct 2 Wireless Controller + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 41 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 No Subclass + bInterfaceProtocol 0 None + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 499 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x84 EP 4 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 0x03 EP 3 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 +Device Status: 0x0000 + (Bus Powered) + +Start + X +--------- +Bus 001 Device 013: ID 045e:028e Microsoft Corp. Xbox360 Controller +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 255 Vendor Specific Subclass + bDeviceProtocol 255 Vendor Specific Protocol + bMaxPacketSize0 8 + idVendor 0x045e Microsoft Corp. + idProduct 0x028e Xbox360 Controller + bcdDevice 1.14 + iManufacturer 1 8Bitdo SF30 Pro + iProduct 2 Controller + iSerial 3 (error) + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 153 + bNumInterfaces 4 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 93 + bInterfaceProtocol 1 + iInterface 0 + ** UNRECOGNIZED: 11 21 00 01 01 25 81 14 00 00 00 00 13 01 08 00 00 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 4 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 8 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 93 + bInterfaceProtocol 3 + iInterface 0 + ** UNRECOGNIZED: 1b 21 00 01 01 01 82 40 01 02 20 16 83 00 00 00 00 00 00 16 03 00 00 00 00 00 00 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 2 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 4 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 64 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 16 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 93 + bInterfaceProtocol 2 + iInterface 0 + ** UNRECOGNIZED: 09 21 00 01 01 22 84 07 00 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x84 EP 4 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 16 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 3 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 253 + bInterfaceProtocol 19 + iInterface 4 µ∡H釰俸샴`翰⣸贠øĀ贤Ǹɀ败˸赐ϸ桀F㏰៸贠ø贀Ǹ赀˸赐ϸ桀F⟰ + ** UNRECOGNIZED: 06 41 00 01 01 03 +Device Status: 0x0000 + (Bus Powered) + diff --git a/fwupd-1.8.6/plugins/ebitdo/data/update.csv b/fwupd-1.8.6/plugins/ebitdo/data/update.csv new file mode 100644 index 0000000000000000000000000000000000000000..eb608a453b4832cfa8b7455041402f2f9b5c238c --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/data/update.csv @@ -0,0 +1,8962 @@ +# Total Phase Data Center(tm) v6.63 +# (c) 2005-2015 Total Phase, Inc. +# www.totalphase.com +# +# Fri Jun 03 12:02:30 2016 +# +# Level,Sp,Index,m:s.ms.us,Dur,Len,Err,Dev,Ep,Record,Summary +0,,0,0:00.000.000,,,,,,Capture started (Aggregate),[Fri 03 Jun 2016 12:00:01 BST] +0,,1,0:00.000.024,,,,,, / , +0,,2,0:09.124.398,,,,,, / , +0,,3,0:09.565.223,,,,,, / , +0,,4,0:09.643.335,,,,,, / , +0,,5,0:10.095.673,,,,,, / , +0,,6,0:10.126.773,,,,,, / , +0,,7,0:10.127.444,31.007.125 ms,,,,,[32 SOF],[Frames: 1881 - 1912] +0,,8,0:10.158.452,13.000 us,8 B,,00,00,SETUP txn,80 06 00 01 00 00 40 00 +0,,12,0:10.159.448,2.812 us,,,,,[1 SOF],[Frame: 1913] +0,,13,0:10.159.451,20.229 us,18 B,,00,00,IN txn,12 01 00 02 00 00 00 40 83 04 50 57 00 02 01 02 03 01 +0,,17,0:10.160.448,1.002.958 ms,,,,,[2 SOF],[Frames: 1914 - 1915] +0,,18,0:10.161.452,7.666 us,0 B,,00,00,OUT txn, +0,,22,0:10.162.449,2.833 us,,,,,[1 SOF],[Frame: 1916] +0,,23,0:10.162.542,,,,,, / , +0,,24,0:10.189.147,,,,,, / , +0,,25,0:10.189.452,31.007.125 ms,,,,,[32 SOF],[Frames: 1943 - 1974] +0,,26,0:10.220.460,13.000 us,8 B,,00,00,SETUP txn,00 05 01 00 00 00 00 00 +0,,30,0:10.221.457,2.812 us,,,,,[1 SOF],[Frame: 1975] +0,,31,0:10.221.460,8.229 us,0 B,,00,00,IN txn, +0,,35,0:10.222.457,30.007.000 ms,,,,,[31 SOF],[Frames: 1976 - 2006] +0,,36,0:10.252.465,13.000 us,8 B,,01,00,SETUP txn,80 06 00 01 00 00 12 00 +0,,40,0:10.253.461,2.895 us,,,,,[1 SOF],[Frame: 2007] +0,,41,0:10.253.465,20.229 us,18 B,,01,00,IN txn,12 01 00 02 00 00 00 40 83 04 50 57 00 02 01 02 03 01 +0,,45,0:10.254.461,2.812 us,,,,,[1 SOF],[Frame: 2008] +0,,46,0:10.254.465,7.645 us,0 B,,01,00,OUT txn, +0,,50,0:10.255.462,1.003.041 ms,,,,,[2 SOF],[Frames: 2009 - 2010] +0,,51,0:10.256.465,13.083 us,8 B,,01,00,SETUP txn,80 06 00 02 00 00 FF 00 +0,,55,0:10.257.462,2.812 us,,,,,[1 SOF],[Frame: 2011] +0,,56,0:10.257.465,35.729 us,41 B,,01,00,IN txn,09 02 29 00 01 01 00 C0 F0 09 04 00 00 02 03 00 00 00 09 21 10 01 00 01… +0,,60,0:10.258.462,1.002.958 ms,,,,,[2 SOF],[Frames: 2012 - 2013] +0,,61,0:10.259.465,7.666 us,0 B,,01,00,OUT txn, +0,,65,0:10.260.462,1.003.125 ms,,,,,[2 SOF],[Frames: 2014 - 2015] +0,,66,0:10.261.466,13.083 us,8 B,,01,00,SETUP txn,80 06 03 03 09 04 FF 00 +0,,70,0:10.262.463,2.895 us,,,,,[1 SOF],[Frame: 2016] +0,,71,0:10.262.466,25.562 us,26 B,,01,00,IN txn,1A 03 38 00 42 00 69 00 74 00 64 00 6F 00 20 00 00 00 00 00 00 00 00 00… +0,,75,0:10.263.463,1.003.041 ms,,,,,[2 SOF],[Frames: 2017 - 2018] +0,,76,0:10.264.466,7.666 us,0 B,,01,00,OUT txn, +0,,80,0:10.265.463,1.003.041 ms,,,,,[2 SOF],[Frames: 2019 - 2020] +0,,81,0:10.266.466,13.062 us,8 B,,01,00,SETUP txn,80 06 00 03 00 00 FF 00 +0,,85,0:10.267.463,2.916 us,,,,,[1 SOF],[Frame: 2021] +0,,86,0:10.267.467,10.916 us,4 B,,01,00,IN txn,04 03 09 04 +0,,90,0:10.268.463,1.003.041 ms,,,,,[2 SOF],[Frames: 2022 - 2023] +0,,91,0:10.269.467,7.645 us,0 B,,01,00,OUT txn, +0,,95,0:10.270.464,1.003.041 ms,,,,,[2 SOF],[Frames: 2024 - 2025] +0,,96,0:10.271.467,13.062 us,8 B,,01,00,SETUP txn,80 06 02 03 09 04 FF 00 +0,,100,0:10.272.464,2.895 us,,,,,[1 SOF],[Frame: 2026] +0,,101,0:10.272.467,20.229 us,18 B,,01,00,IN txn,12 03 38 00 42 00 69 00 74 00 64 00 6F 00 20 00 20 00 +0,,105,0:10.273.464,1.003.041 ms,,,,,[2 SOF],[Frames: 2027 - 2028] +0,,106,0:10.274.468,7.645 us,0 B,,01,00,OUT txn, +0,,110,0:10.275.464,1.003.041 ms,,,,,[2 SOF],[Frames: 2029 - 2030] +0,,111,0:10.276.468,13.000 us,8 B,,01,00,SETUP txn,80 06 00 06 00 00 0A 00 +0,,115,0:10.277.465,2.895 us,,,,,[1 SOF],[Frame: 2031] +0,,116,0:10.277.468,4.583 us,,,01,00,IN txn (STALL), +0,,119,0:10.278.465,4.003.458 ms,,,,,[5 SOF],[Frames: 2032 - 2036] +0,,120,0:10.282.469,13.020 us,8 B,,01,00,SETUP txn,80 06 00 01 00 00 12 00 +0,,124,0:10.283.465,2.895 us,,,,,[1 SOF],[Frame: 2037] +0,,125,0:10.283.469,20.229 us,18 B,,01,00,IN txn,12 01 00 02 00 00 00 40 83 04 50 57 00 02 01 02 03 01 +0,,129,0:10.284.466,2.895 us,,,,,[1 SOF],[Frame: 2038] +0,,130,0:10.284.469,7.666 us,0 B,,01,00,OUT txn, +0,,134,0:10.285.466,1.003.041 ms,,,,,[2 SOF],[Frames: 2039 - 2040] +0,,135,0:10.286.470,13.000 us,8 B,,01,00,SETUP txn,80 06 00 02 00 00 09 00 +0,,139,0:10.287.466,2.895 us,,,,,[1 SOF],[Frame: 2041] +0,,140,0:10.287.469,14.229 us,9 B,,01,00,IN txn,09 02 29 00 01 01 00 C0 F0 +0,,144,0:10.288.466,2.916 us,,,,,[1 SOF],[Frame: 2042] +0,,145,0:10.288.469,7.666 us,0 B,,01,00,OUT txn, +0,,149,0:10.289.466,1.003.041 ms,,,,,[2 SOF],[Frames: 2043 - 2044] +0,,150,0:10.290.470,13.000 us,8 B,,01,00,SETUP txn,80 06 00 02 00 00 29 00 +0,,154,0:10.291.467,3.000 us,,,,,[1 SOF],[Frame: 2045] +0,,155,0:10.291.470,35.750 us,41 B,,01,00,IN txn,09 02 29 00 01 01 00 C0 F0 09 04 00 00 02 03 00 00 00 09 21 10 01 00 01… +0,,159,0:10.292.467,2.979 us,,,,,[1 SOF],[Frame: 2046] +0,,160,0:10.292.470,7.666 us,0 B,,01,00,OUT txn, +0,,164,0:10.293.467,1.002.958 ms,,,,,[2 SOF],[Frames: 2047 - 0] +0,,165,0:10.294.472,13.000 us,8 B,,01,00,SETUP txn,00 09 01 00 00 00 00 00 +0,,169,0:10.295.467,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,170,0:10.295.470,8.229 us,0 B,,01,00,IN txn, +0,,174,0:10.296.467,2.812 us,,,,,[1 SOF],[Frame: 2] +0,,175,0:10.296.471,13.000 us,8 B,,01,00,SETUP txn,21 0A 00 00 00 00 00 00 +0,,179,0:10.297.467,2.833 us,,,,,[1 SOF],[Frame: 3] +0,,180,0:10.297.471,4.604 us,,,01,00,IN txn (STALL), +0,,183,0:10.298.468,1.002.958 ms,,,,,[2 SOF],[Frames: 4 - 5] +0,,184,0:10.299.473,13.083 us,8 B,,01,00,SETUP txn,81 06 00 22 00 00 61 00 +0,,188,0:10.300.468,2.833 us,,,,,[1 SOF],[Frame: 6] +0,,189,0:10.300.471,30.416 us,33 B,,01,00,IN txn,05 8C 09 01 A1 01 09 03 15 00 26 00 FF 75 08 95 40 81 02 09 04 15 00 26… +0,,193,0:10.301.468,1.002.958 ms,,,,,[2 SOF],[Frames: 7 - 8] +0,,194,0:10.302.471,7.666 us,0 B,,01,00,OUT txn, +0,,198,0:10.303.468,88.015.041 ms,,,,,[89 SOF],[Frames: 9 - 97] +0,,199,0:10.391.484,50.312 us,64 B,,01,01,OUT txn,05 00 16 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,203,0:10.392.481,207.031.562 ms,,,,,[208 SOF],[Frames: 98 - 305] +0,,204,0:10.599.513,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,208,0:10.600.509,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,209,0:10.326.475,288.090.895 ms,64 B,,01,02,IN txn [9 POLL],0C 00 16 07 00 04 04 00 0B 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,214,0:10.615.512,192.029.479 ms,,,,,[193 SOF],[Frames: 321 - 513] +0,,215,0:10.807.541,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,219,0:10.808.538,30.006.979 ms,,,,,[31 SOF],[Frames: 514 - 544] +0,,220,0:10.646.519,192.077.562 ms,64 B,,01,02,IN txn [6 POLL],0C 00 16 07 00 04 04 00 0B 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,225,0:10.839.543,1.999.280.375 s,,,,,[2000 SOF],[Frames: 545 - 496] [Periodic Timeout] +0,,226,0:10.870.550,1.984.280.041 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,227,0:12.839.820,1.999.280.375 s,,,,,[2000 SOF],[Frames: 497 - 448] [Periodic Timeout] +0,,228,0:12.886.830,1.984.280.062 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,229,0:14.840.098,1.999.280.375 s,,,,,[2000 SOF],[Frames: 449 - 400] [Periodic Timeout] +0,,230,0:14.903.110,1.984.280.041 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,231,0:16.840.376,1.999.280.375 s,,,,,[2000 SOF],[Frames: 401 - 352] [Periodic Timeout] +0,,232,0:16.919.390,1.984.280.041 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,233,0:18.840.653,288.042.812 ms,,,,,[289 SOF],[Frames: 353 - 641] +0,,234,0:19.128.697,50.333 us,64 B,,01,01,OUT txn,05 00 1A 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,238,0:19.129.694,30.006.979 ms,,,,,[31 SOF],[Frames: 642 - 672] +0,,239,0:18.935.670,224.082.354 ms,64 B,,01,02,IN txn [7 POLL],2D 00 19 24 00 7F 1E 50 33 39 31 32 02 4E 39 31 38 2C BD CB 0F 9A 89 12… +0,,244,0:19.160.698,1.999.280.354 s,,,,,[2000 SOF],[Frames: 673 - 624] [Periodic Timeout] +0,,245,0:19.191.705,1.984.280.041 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,246,0:21.160.975,1.999.280.375 s,,,,,[2000 SOF],[Frames: 625 - 576] [Periodic Timeout] +0,,247,0:21.207.985,1.984.280.020 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,248,0:23.161.253,1.999.280.354 s,,,,,[2000 SOF],[Frames: 577 - 528] [Periodic Timeout] +0,,249,0:23.224.265,1.984.280.041 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,250,0:25.161.531,1.999.280.375 s,,,,,[2000 SOF],[Frames: 529 - 480] [Periodic Timeout] +0,,251,0:25.240.545,1.984.280.041 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,252,0:27.161.809,1.552.218.375 s,,,,,[1553 SOF],[Frames: 481 - 2033] +0,,253,0:28.714.027,50.333 us,64 B,,01,01,OUT txn,23 00 16 1F 00 01 1C 00 0B 01 00 00 00 A0 00 08 00 B8 00 00 80 68 CF 01… +0,,257,0:28.715.024,15.004.916 ms,,,,,[16 SOF],[Frames: 2034 - 1] +0,,258,0:28.730.029,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 1C 00 0B 01 00 00 00 A0 00 08 00 B8 00 00 80 68 CF 01… +0,,262,0:27.256.825,1.984.280.020 s,,,01,02,[63 IN-NAK],[Periodic Timeout] +0,,263,0:28.731.026,1.839.258.145 s,,,,,[1840 SOF],[Frames: 2 - 1841] +0,,264,0:28.746.032,1.824.303.562 s,64 B,,01,01,OUT txn [114 POLL],05 00 1D 01 00 00 1C 00 0B 01 00 00 00 A0 00 08 00 B8 00 00 80 68 CF 01… +0,,725,0:30.571.282,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,726,0:29.273.105,1.312.233.062 s,64 B,,01,02,IN txn [41 POLL],06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,731,0:30.586.284,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,732,0:30.586.287,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 1C 00 0B 01 00 00 00 A0 00 08 00 B8 00 00 80 68 CF 01… +0,,736,0:30.587.284,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,737,0:30.602.289,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DB 7D 28 D4 3A 00 4A F1 30 AC 25 4C FD 08 67 17… +0,,741,0:30.603.286,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,742,0:30.617.291,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,746,0:30.618.288,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,747,0:30.618.292,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DB 7D 28 D4 3A 00 4A F1 30 AC 25 4C FD 08 67 17… +0,,751,0:30.619.288,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,752,0:30.634.294,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D0 E3 13 0F 27 61 F6 24 5D 4B B2 25 D4 AF 56 6E… +0,,756,0:30.635.291,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,757,0:30.649.296,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,761,0:30.650.293,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,762,0:30.650.296,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D0 E3 13 0F 27 61 F6 24 5D 4B B2 25 D4 AF 56 6E… +0,,766,0:30.651.293,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,767,0:30.666.298,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C DD 4E 02 98 67 3D 24 80 29 06 7C CB A8 FD 44… +0,,771,0:30.667.295,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,772,0:30.681.300,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,776,0:30.682.297,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,777,0:30.682.300,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C DD 4E 02 98 67 3D 24 80 29 06 7C CB A8 FD 44… +0,,781,0:30.683.297,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,782,0:30.698.303,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 6A 81 1E F9 A0 8D 24 E8 A3 35 4C E8 67 F3 F9… +0,,786,0:30.699.300,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,787,0:30.713.305,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,791,0:30.714.302,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,792,0:30.714.305,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 6A 81 1E F9 A0 8D 24 E8 A3 35 4C E8 67 F3 F9… +0,,796,0:30.715.302,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,797,0:30.730.307,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 32 52 7B 9D C1 93 B1 88 79 49 FB 02 32 9F BA… +0,,801,0:30.731.304,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,802,0:30.745.309,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,806,0:30.746.306,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,807,0:30.746.309,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 22 32 52 7B 9D C1 93 B1 88 79 49 FB 02 32 9F BA… +0,,811,0:30.747.306,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,812,0:30.762.312,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 08 B8 FC B9 85 51 F6 32 2E E3 BD EF 38 77 72… +0,,816,0:30.763.308,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,817,0:30.777.314,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,821,0:30.778.311,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,822,0:30.778.314,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 08 B8 FC B9 85 51 F6 32 2E E3 BD EF 38 77 72… +0,,826,0:30.779.311,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,827,0:30.794.316,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D1 EC AF 7B 87 DB 1A 24 35 70 6F 77 0B 07 AE 30… +0,,831,0:30.795.313,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,832,0:30.809.318,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,836,0:30.810.315,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,837,0:30.810.318,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D1 EC AF 7B 87 DB 1A 24 35 70 6F 77 0B 07 AE 30… +0,,841,0:30.811.315,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,842,0:30.826.320,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 91 59 84 CB 48 C0 82 4C D9 EB C5 7E 78 F1 0B… +0,,846,0:30.827.317,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,847,0:30.841.323,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,851,0:30.842.319,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,852,0:30.842.323,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 91 59 84 CB 48 C0 82 4C D9 EB C5 7E 78 F1 0B… +0,,856,0:30.843.320,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,857,0:30.858.325,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE BA 5B 62 0B 67 2F A6 8F 71 D4 C3 30 08 C9 B7… +0,,861,0:30.859.322,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,862,0:30.873.327,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,866,0:30.874.324,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,867,0:30.874.327,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DE BA 5B 62 0B 67 2F A6 8F 71 D4 C3 30 08 C9 B7… +0,,871,0:30.875.324,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,872,0:30.890.329,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 BF D6 CF DE B4 36 32 1D 5C 56 AE 14 03 70 42… +0,,876,0:30.891.326,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,877,0:30.905.331,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,881,0:30.906.328,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,882,0:30.906.332,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 BF D6 CF DE B4 36 32 1D 5C 56 AE 14 03 70 42… +0,,886,0:30.907.328,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,887,0:30.922.334,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AF 24 EF 66 05 78 4C 13 3E AA C1 D2 E9 C7 6C 41… +0,,891,0:30.923.331,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,892,0:30.937.336,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,896,0:30.938.333,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,897,0:30.938.336,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AF 24 EF 66 05 78 4C 13 3E AA C1 D2 E9 C7 6C 41… +0,,901,0:30.939.333,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,902,0:30.954.338,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8B B7 26 DC 03 B1 E2 C0 F6 AB 95 C8 C6 8A B8 77… +0,,906,0:30.955.335,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,907,0:30.969.340,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,911,0:30.970.337,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,912,0:30.970.340,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8B B7 26 DC 03 B1 E2 C0 F6 AB 95 C8 C6 8A B8 77… +0,,916,0:30.971.337,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,917,0:30.986.343,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D 68 58 E9 BE C1 35 C5 D8 04 E9 3B E4 CB 8C 0E… +0,,921,0:30.987.340,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,922,0:31.001.345,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,926,0:31.002.342,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,927,0:31.002.345,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D 68 58 E9 BE C1 35 C5 D8 04 E9 3B E4 CB 8C 0E… +0,,931,0:31.003.342,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,932,0:31.018.347,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 21 BB 73 CE 2E 26 27 1C DF E7 01 8A 78 1C BC… +0,,936,0:31.019.344,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,937,0:31.033.349,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,941,0:31.034.346,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,942,0:31.034.349,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 21 BB 73 CE 2E 26 27 1C DF E7 01 8A 78 1C BC… +0,,946,0:31.035.346,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,947,0:31.050.352,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FF 98 9B 98 3F D8 A7 7B FF FD A5 2B 32 CF 42 E3… +0,,951,0:31.051.348,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,952,0:31.065.354,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,956,0:31.066.351,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,957,0:31.066.354,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FF 98 9B 98 3F D8 A7 7B FF FD A5 2B 32 CF 42 E3… +0,,961,0:31.067.351,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,962,0:31.082.356,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AC C5 DE 5D 0C A9 27 3D 37 88 70 55 0A AF 1B 33… +0,,966,0:31.083.353,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,967,0:31.097.358,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,971,0:31.098.355,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,972,0:31.098.358,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AC C5 DE 5D 0C A9 27 3D 37 88 70 55 0A AF 1B 33… +0,,976,0:31.099.355,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,977,0:31.114.360,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 77 F3 6D 38 6A 16 2E F4 76 80 DD 73 3A D4 88… +0,,981,0:31.115.357,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,982,0:31.129.363,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,986,0:31.130.359,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,987,0:31.130.363,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 77 F3 6D 38 6A 16 2E F4 76 80 DD 73 3A D4 88… +0,,991,0:31.131.360,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,992,0:31.146.365,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 B9 74 C4 E1 68 3B 17 91 E7 22 F0 F4 91 77 E2… +0,,996,0:31.147.362,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,997,0:31.161.367,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1001,0:31.162.364,16.005.041 ms,,,,,[17 SOF],[Frames: 385 - 401] +0,,1002,0:31.178.369,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E5 A6 A0 56 CA 7E 26 FF AB EE 6A 14 00 AB 77 D6… +0,,1006,0:31.179.366,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,1007,0:31.193.371,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1011,0:31.194.368,16.005.041 ms,,,,,[17 SOF],[Frames: 417 - 433] +0,,1012,0:31.210.374,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 A6 A0 56 CA 7E 26 FF AB EE 6A 14 00 AB 77 D6… +0,,1016,0:31.211.371,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,1017,0:31.225.376,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1021,0:31.226.373,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,1022,0:31.226.376,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 A6 A0 56 CA 7E 26 FF AB EE 6A 14 00 AB 77 D6… +0,,1026,0:31.227.373,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,1027,0:31.242.378,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 90 84 4C FF 0A E7 3B 9A AC 33 6A A2 52 E2 A4… +0,,1031,0:31.243.375,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,1032,0:31.257.380,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1036,0:31.258.377,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,1037,0:31.258.380,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 90 84 4C FF 0A E7 3B 9A AC 33 6A A2 52 E2 A4… +0,,1041,0:31.259.377,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,1042,0:31.274.383,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 07 97 FC 22 EC 53 D1 BB 1F C2 4E D3 05 56 1E A9… +0,,1046,0:31.275.380,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,1047,0:31.289.385,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1051,0:31.290.382,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,1052,0:31.290.385,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 07 97 FC 22 EC 53 D1 BB 1F C2 4E D3 05 56 1E A9… +0,,1056,0:31.291.382,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,1057,0:31.306.387,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FF 19 3C 8C 2F C8 7B 3A 5E 28 74 86 64 6E 97 28… +0,,1061,0:31.307.384,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,1062,0:31.321.389,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1066,0:31.322.386,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,1067,0:31.322.389,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FF 19 3C 8C 2F C8 7B 3A 5E 28 74 86 64 6E 97 28… +0,,1071,0:31.323.386,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,1072,0:31.338.392,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 68 E4 2B F6 74 64 05 5E C3 F7 B4 46 E6 8C BC… +0,,1076,0:31.339.388,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,1077,0:31.353.394,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1081,0:31.354.391,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,1082,0:31.354.394,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 68 E4 2B F6 74 64 05 5E C3 F7 B4 46 E6 8C BC… +0,,1086,0:31.355.391,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,1087,0:31.370.396,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 97 F8 20 A8 A5 6A 61 16 FD 82 60 00 4B 05 DB 2D… +0,,1091,0:31.371.393,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,1092,0:31.385.398,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1096,0:31.386.395,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,1097,0:31.386.398,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 97 F8 20 A8 A5 6A 61 16 FD 82 60 00 4B 05 DB 2D… +0,,1101,0:31.387.395,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,1102,0:31.402.400,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A2 7E 0A FF 96 2C E8 4C A1 D0 BE E3 3A 36 18 28… +0,,1106,0:31.403.397,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,1107,0:31.417.403,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1111,0:31.418.399,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,1112,0:31.418.403,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A2 7E 0A FF 96 2C E8 4C A1 D0 BE E3 3A 36 18 28… +0,,1116,0:31.419.400,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,1117,0:31.434.405,50.854 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FF D8 5F 12 48 C6 CD 87 70 93 E0 CE C2 C8 92 FF… +0,,1121,0:31.435.402,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,1122,0:31.449.407,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1126,0:31.450.404,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,1127,0:31.450.407,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FF D8 5F 12 48 C6 CD 87 70 93 E0 CE C2 C8 92 FF… +0,,1131,0:31.451.404,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,1132,0:31.466.409,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 00 27 3E 1F C9 6F 01 DD A9 D7 33 14 B9 DE 56 08… +0,,1136,0:31.467.406,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,1137,0:31.481.411,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1141,0:31.482.408,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,1142,0:31.482.412,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 00 27 3E 1F C9 6F 01 DD A9 D7 33 14 B9 DE 56 08… +0,,1146,0:31.483.408,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,1147,0:31.498.414,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C3 26 6B 5E 85 30 FB 6A 78 D9 C4 D1 71 0D 01 AA… +0,,1151,0:31.499.411,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,1152,0:31.513.416,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1156,0:31.514.413,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,1157,0:31.514.416,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C3 26 6B 5E 85 30 FB 6A 78 D9 C4 D1 71 0D 01 AA… +0,,1161,0:31.515.413,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,1162,0:31.530.418,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 49 B1 42 9A AD 07 51 4A 3B 9F 83 F5 3A 52 98… +0,,1166,0:31.531.415,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,1167,0:31.545.420,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1171,0:31.546.417,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,1172,0:31.546.420,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 49 B1 42 9A AD 07 51 4A 3B 9F 83 F5 3A 52 98… +0,,1176,0:31.547.417,15.004.916 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,1177,0:31.562.423,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC 91 B9 91 C2 AD A6 00 99 92 1B 63 B8 60 D0 26… +0,,1181,0:31.563.420,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,1182,0:31.577.425,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1186,0:31.578.422,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,1187,0:31.578.425,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC 91 B9 91 C2 AD A6 00 99 92 1B 63 B8 60 D0 26… +0,,1191,0:31.579.422,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,1192,0:31.594.427,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 3A 70 87 32 41 29 84 34 5E 03 54 85 C0 C4 9D… +0,,1196,0:31.595.424,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,1197,0:31.609.429,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1201,0:31.610.426,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,1202,0:31.610.429,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 3A 70 87 32 41 29 84 34 5E 03 54 85 C0 C4 9D… +0,,1206,0:31.611.426,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,1207,0:31.626.432,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 9F 96 DB 76 CC A0 2D 87 D2 F0 74 2E C9 B8 F9… +0,,1211,0:31.627.428,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,1212,0:31.641.434,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1216,0:31.642.431,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,1217,0:31.642.434,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 9F 96 DB 76 CC A0 2D 87 D2 F0 74 2E C9 B8 F9… +0,,1221,0:31.643.431,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,1222,0:31.658.436,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 D2 EA CB FE 7B 7B E3 FE 98 D9 6D F3 D4 9F 4C… +0,,1226,0:31.659.433,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,1227,0:31.673.438,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1231,0:31.674.435,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,1232,0:31.674.438,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 D2 EA CB FE 7B 7B E3 FE 98 D9 6D F3 D4 9F 4C… +0,,1236,0:31.675.435,15.004.916 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,1237,0:31.690.440,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 71 2C B9 B8 67 65 0E 62 6A 12 00 B9 51 93 AB 76… +0,,1241,0:31.691.437,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,1242,0:31.705.443,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1246,0:31.706.439,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,1247,0:31.706.443,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 71 2C B9 B8 67 65 0E 62 6A 12 00 B9 51 93 AB 76… +0,,1251,0:31.707.440,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,1252,0:31.722.445,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 54 91 CD 15 CF 37 70 1E 40 83 70 52 33 C6 A1… +0,,1256,0:31.723.442,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,1257,0:31.737.447,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1261,0:31.738.444,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,1262,0:31.738.447,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 93 54 91 CD 15 CF 37 70 1E 40 83 70 52 33 C6 A1… +0,,1266,0:31.739.444,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,1267,0:31.754.449,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 0A F6 77 15 90 B5 B0 6A AC EC B9 56 F8 51 C8… +0,,1271,0:31.755.446,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,1272,0:31.769.451,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1276,0:31.770.448,16.005.125 ms,,,,,[17 SOF],[Frames: 993 - 1009] +0,,1277,0:31.786.454,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 20 64 E9 0E 99 B4 72 5F DC 48 F6 3A F2 B3 C6… +0,,1281,0:31.787.451,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,1282,0:31.801.456,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1286,0:31.802.453,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,1287,0:31.802.456,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 20 64 E9 0E 99 B4 72 5F DC 48 F6 3A F2 B3 C6… +0,,1291,0:31.803.453,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,1292,0:31.818.458,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B B1 5A 26 3A B6 1A BC CC 28 BA 62 86 70 89 1E… +0,,1296,0:31.819.455,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,1297,0:31.833.460,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1301,0:31.834.457,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,1302,0:31.834.460,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B B1 5A 26 3A B6 1A BC CC 28 BA 62 86 70 89 1E… +0,,1306,0:31.835.457,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,1307,0:31.850.463,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C B0 C5 8E D3 6B F4 2A E7 D1 E0 EB 2B 22 B9 2C… +0,,1311,0:31.851.460,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,1312,0:31.865.465,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1316,0:31.866.462,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,1317,0:31.866.465,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C B0 C5 8E D3 6B F4 2A E7 D1 E0 EB 2B 22 B9 2C… +0,,1321,0:31.867.462,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,1322,0:31.882.467,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E5 F1 7E 3F BA 67 BA 7C DE DD 8B 8C 98 9F C6 F5… +0,,1326,0:31.883.464,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,1327,0:31.897.469,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1331,0:31.898.466,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,1332,0:31.898.469,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 F1 7E 3F BA 67 BA 7C DE DD 8B 8C 98 9F C6 F5… +0,,1336,0:31.899.466,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,1337,0:31.914.472,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 35 C8 8B 4D CE A3 EC FC EE 20 B9 53 00 2E 2F 67… +0,,1341,0:31.915.468,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,1342,0:31.929.474,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1346,0:31.930.471,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,1347,0:31.930.474,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 35 C8 8B 4D CE A3 EC FC EE 20 B9 53 00 2E 2F 67… +0,,1351,0:31.931.471,15.004.916 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,1352,0:31.946.476,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 0C 95 20 4C 59 B9 B1 E0 43 20 E8 00 29 C1 FA… +0,,1356,0:31.947.473,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,1357,0:31.961.478,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1361,0:31.962.475,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,1362,0:31.962.478,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A7 0C 95 20 4C 59 B9 B1 E0 43 20 E8 00 29 C1 FA… +0,,1366,0:31.963.475,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,1367,0:31.978.480,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A B6 DC FD A0 C5 1B 37 C6 6F 8C C3 63 23 2C E8… +0,,1371,0:31.979.477,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,1372,0:31.993.483,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1376,0:31.994.479,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,1377,0:31.994.483,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A B6 DC FD A0 C5 1B 37 C6 6F 8C C3 63 23 2C E8… +0,,1381,0:31.995.480,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,1382,0:32.010.485,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 EB 47 C8 52 F3 28 09 82 7E F6 91 B3 24 8A 6B… +0,,1386,0:32.011.482,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,1387,0:32.025.487,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1391,0:32.026.484,2.833 us,,,,,[1 SOF],[Frame: 1249] +0,,1392,0:32.026.487,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 EB 47 C8 52 F3 28 09 82 7E F6 91 B3 24 8A 6B… +0,,1396,0:32.027.484,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,1397,0:32.042.489,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DB B1 35 6D B3 AD E0 D5 08 64 94 45 F1 00 14 7A… +0,,1401,0:32.043.486,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,1402,0:32.057.491,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1406,0:32.058.488,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,1407,0:32.058.492,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DB B1 35 6D B3 AD E0 D5 08 64 94 45 F1 00 14 7A… +0,,1411,0:32.059.488,15.004.916 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,1412,0:32.074.494,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 98 F7 F9 DC 55 01 23 D1 90 80 05 69 01 80 90 5E… +0,,1416,0:32.075.491,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,1417,0:32.089.496,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1421,0:32.090.493,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,1422,0:32.090.496,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 98 F7 F9 DC 55 01 23 D1 90 80 05 69 01 80 90 5E… +0,,1426,0:32.091.493,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,1427,0:32.106.498,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C 0B 1E 13 8D B2 6E EF 95 57 69 90 3E CB 86 D9… +0,,1431,0:32.107.495,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,1432,0:32.121.500,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1436,0:32.122.497,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,1437,0:32.122.500,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C 0B 1E 13 8D B2 6E EF 95 57 69 90 3E CB 86 D9… +0,,1441,0:32.123.497,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,1442,0:32.138.503,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 45 AD E8 A2 46 6F E0 2F 78 FB AD B6 1E 13 9B… +0,,1446,0:32.139.500,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,1447,0:32.153.505,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1451,0:32.154.502,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,1452,0:32.154.505,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 45 AD E8 A2 46 6F E0 2F 78 FB AD B6 1E 13 9B… +0,,1456,0:32.155.502,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,1457,0:32.170.507,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 F7 0A F2 B5 7E 4D 07 06 B9 A5 AA 13 56 98 A0… +0,,1461,0:32.171.504,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,1462,0:32.185.509,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1466,0:32.186.506,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,1467,0:32.186.509,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 F7 0A F2 B5 7E 4D 07 06 B9 A5 AA 13 56 98 A0… +0,,1471,0:32.187.506,15.004.916 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,1472,0:32.202.512,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C4 07 28 27 AD 79 8B EA FF 90 30 CD ED 1E 7E 65… +0,,1476,0:32.203.508,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,1477,0:32.217.514,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1481,0:32.218.510,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,1482,0:32.218.514,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C4 07 28 27 AD 79 8B EA FF 90 30 CD ED 1E 7E 65… +0,,1486,0:32.219.511,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,1487,0:32.234.516,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D EF A8 CB C9 99 CE D2 69 EB 90 72 17 08 01 14… +0,,1491,0:32.235.513,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,1492,0:32.249.518,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1496,0:32.250.515,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,1497,0:32.250.518,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D EF A8 CB C9 99 CE D2 69 EB 90 72 17 08 01 14… +0,,1501,0:32.251.515,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,1502,0:32.266.520,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A D5 7B F6 67 10 92 64 46 45 A4 48 D0 AC 08 87… +0,,1506,0:32.267.517,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,1507,0:32.281.523,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1511,0:32.282.519,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,1512,0:32.282.523,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A D5 7B F6 67 10 92 64 46 45 A4 48 D0 AC 08 87… +0,,1516,0:32.283.520,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,1517,0:32.298.525,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D1 D8 84 39 8E 3B 94 24 A1 4B 89 B9 3A 9D EB 33… +0,,1521,0:32.299.522,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,1522,0:32.313.527,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1526,0:32.314.524,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,1527,0:32.314.527,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D1 D8 84 39 8E 3B 94 24 A1 4B 89 B9 3A 9D EB 33… +0,,1531,0:32.315.524,15.005.000 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,1532,0:32.330.529,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BE 1A FF 43 FA 58 94 D0 35 DC 9D 55 BB 7A ED 41… +0,,1536,0:32.331.526,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,1537,0:32.345.531,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1541,0:32.346.528,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,1542,0:32.346.532,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BE 1A FF 43 FA 58 94 D0 35 DC 9D 55 BB 7A ED 41… +0,,1546,0:32.347.528,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,1547,0:32.362.534,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A1 7A 47 E5 00 E0 0D 6D 1D 9A DC A1 0B 2F 24 D1… +0,,1551,0:32.363.531,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,1552,0:32.377.536,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1556,0:32.378.533,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,1557,0:32.378.536,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A1 7A 47 E5 00 E0 0D 6D 1D 9A DC A1 0B 2F 24 D1… +0,,1561,0:32.379.533,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,1562,0:32.394.538,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C0 F9 EC 91 41 76 FC 09 42 B6 ED C2 71 3B AE 63… +0,,1566,0:32.395.535,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,1567,0:32.409.540,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1571,0:32.410.537,16.005.041 ms,,,,,[17 SOF],[Frames: 1633 - 1649] +0,,1572,0:32.426.543,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A0 3A 51 81 A7 26 D5 0E 6C ED DB 99 02 B0 6A 9C… +0,,1576,0:32.427.540,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,1577,0:32.441.545,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1581,0:32.442.542,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,1582,0:32.442.545,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A0 3A 51 81 A7 26 D5 0E 6C ED DB 99 02 B0 6A 9C… +0,,1586,0:32.443.542,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,1587,0:32.458.547,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 6A 03 2E BD 3D FD B8 DA 07 A2 22 86 BB 9D FC… +0,,1591,0:32.459.544,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,1592,0:32.473.549,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1596,0:32.474.546,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,1597,0:32.474.549,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 6A 03 2E BD 3D FD B8 DA 07 A2 22 86 BB 9D FC… +0,,1601,0:32.475.546,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,1602,0:32.490.552,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 A3 FA 54 E3 F3 CD 3F 20 0B 43 41 FE 0D 52 3C… +0,,1606,0:32.491.548,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,1607,0:32.505.554,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1611,0:32.506.550,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,1612,0:32.506.554,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B5 A3 FA 54 E3 F3 CD 3F 20 0B 43 41 FE 0D 52 3C… +0,,1616,0:32.507.551,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,1617,0:32.522.556,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD BE 6D 16 2E BF 61 E2 2F 8E E4 88 5C 9D 8E 27… +0,,1621,0:32.523.553,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,1622,0:32.537.558,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1626,0:32.538.555,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,1627,0:32.538.558,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD BE 6D 16 2E BF 61 E2 2F 8E E4 88 5C 9D 8E 27… +0,,1631,0:32.539.555,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,1632,0:32.554.560,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 54 5D F2 53 23 65 43 57 D4 C2 C7 F9 A1 F0 B6 20… +0,,1636,0:32.555.557,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,1637,0:32.569.562,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1641,0:32.570.559,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,1642,0:32.570.563,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 54 5D F2 53 23 65 43 57 D4 C2 C7 F9 A1 F0 B6 20… +0,,1646,0:32.571.559,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,1647,0:32.586.565,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1F 92 B1 0A 6D 3B DF A0 C7 CE B8 DD DD 6B 8E 20… +0,,1651,0:32.587.562,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,1652,0:32.601.567,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1656,0:32.602.564,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,1657,0:32.602.567,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1F 92 B1 0A 6D 3B DF A0 C7 CE B8 DD DD 6B 8E 20… +0,,1661,0:32.603.564,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,1662,0:32.618.569,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EC EE A7 CC 67 01 8F 3D 78 9C 22 C2 ED 73 54 31… +0,,1666,0:32.619.566,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,1667,0:32.633.571,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1671,0:32.634.568,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,1672,0:32.634.571,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EC EE A7 CC 67 01 8F 3D 78 9C 22 C2 ED 73 54 31… +0,,1676,0:32.635.568,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,1677,0:32.650.574,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9A 2A 96 8B 8B 07 9E E6 FE F9 3B 43 22 45 06 F4… +0,,1681,0:32.651.571,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,1682,0:32.665.576,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1686,0:32.666.573,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,1687,0:32.666.576,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9A 2A 96 8B 8B 07 9E E6 FE F9 3B 43 22 45 06 F4… +0,,1691,0:32.667.573,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,1692,0:32.682.578,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EE 69 75 CB 1F 8C 63 DF 7F A7 EB A7 EA C2 3F 54… +0,,1696,0:32.683.575,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,1697,0:32.697.580,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1701,0:32.698.577,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,1702,0:32.698.580,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EE 69 75 CB 1F 8C 63 DF 7F A7 EB A7 EA C2 3F 54… +0,,1706,0:32.699.577,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,1707,0:32.714.583,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 20 BA 64 DD 01 08 6E 7D 57 CB F1 6D A7 ED 9D FE… +0,,1711,0:32.715.579,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,1712,0:32.729.585,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1716,0:32.730.582,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,1717,0:32.730.585,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 20 BA 64 DD 01 08 6E 7D 57 CB F1 6D A7 ED 9D FE… +0,,1721,0:32.731.582,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,1722,0:32.746.587,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CB 5A 01 0F 9C 98 A8 9E 74 7E 5E 74 26 20 D5 72… +0,,1726,0:32.747.584,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,1727,0:32.761.589,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1731,0:32.762.586,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,1732,0:32.762.589,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CB 5A 01 0F 9C 98 A8 9E 74 7E 5E 74 26 20 D5 72… +0,,1736,0:32.763.586,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,1737,0:32.778.592,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D4 E6 7A 96 C0 4E BA 80 CA C4 3C DC AD B7 B0 3C… +0,,1741,0:32.779.588,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,1742,0:32.793.594,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1746,0:32.794.590,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,1747,0:32.794.594,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D4 E6 7A 96 C0 4E BA 80 CA C4 3C DC AD B7 B0 3C… +0,,1751,0:32.795.591,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,1752,0:32.810.596,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D3 A0 BC CF 14 D7 7A BB 25 F7 1E 45 8C F1 76 BA… +0,,1756,0:32.811.593,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,1757,0:32.825.598,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1761,0:32.826.595,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,1762,0:32.826.598,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D3 A0 BC CF 14 D7 7A BB 25 F7 1E 45 8C F1 76 BA… +0,,1766,0:32.827.595,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,1767,0:32.842.600,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 A2 66 12 39 B0 83 CE E5 84 74 C6 AC 50 C3 BC… +0,,1771,0:32.843.597,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,1772,0:32.857.602,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1776,0:32.858.599,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,1777,0:32.858.603,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 A2 66 12 39 B0 83 CE E5 84 74 C6 AC 50 C3 BC… +0,,1781,0:32.859.599,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,1782,0:32.874.605,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 74 FD 25 FF 65 86 B5 1E 20 E0 31 7E E2 8C A5 FE… +0,,1786,0:32.875.602,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,1787,0:32.889.607,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1791,0:32.890.604,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,1792,0:32.890.607,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 74 FD 25 FF 65 86 B5 1E 20 E0 31 7E E2 8C A5 FE… +0,,1796,0:32.891.604,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,1797,0:32.906.609,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C5 DA B6 D7 2D B7 2C 8D 07 D3 92 80 F1 2B 91 4A… +0,,1801,0:32.907.606,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,1802,0:32.921.611,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1806,0:32.922.608,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,1807,0:32.922.611,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C5 DA B6 D7 2D B7 2C 8D 07 D3 92 80 F1 2B 91 4A… +0,,1811,0:32.923.608,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,1812,0:32.938.614,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 1F A4 8C 58 9D 4F E9 05 B8 B7 6D 62 3F 48 D2… +0,,1816,0:32.939.611,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,1817,0:32.953.616,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1821,0:32.954.613,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,1822,0:32.954.616,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 1F A4 8C 58 9D 4F E9 05 B8 B7 6D 62 3F 48 D2… +0,,1826,0:32.955.613,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,1827,0:32.970.618,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 ED 54 23 7D 48 1E 3C 0D 0B E2 2C 2C FD E5 93 E4… +0,,1831,0:32.971.615,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,1832,0:32.985.620,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1836,0:32.986.617,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,1837,0:32.986.620,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 ED 54 23 7D 48 1E 3C 0D 0B E2 2C 2C FD E5 93 E4… +0,,1841,0:32.987.617,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,1842,0:33.002.623,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C6 3D 45 74 34 A6 63 3F 89 6E 3F D6 8A 04 FD 74… +0,,1846,0:33.003.619,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,1847,0:33.017.625,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1851,0:33.018.622,16.005.041 ms,,,,,[17 SOF],[Frames: 193 - 209] +0,,1852,0:33.034.627,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 FC 9B 03 2D 29 7D FD 47 6F A9 C9 6D C9 5C 86… +0,,1856,0:33.035.624,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,1857,0:33.049.629,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1861,0:33.050.626,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,1862,0:33.050.629,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 FC 9B 03 2D 29 7D FD 47 6F A9 C9 6D C9 5C 86… +0,,1866,0:33.051.626,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,1867,0:33.066.631,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 99 0E 95 5F BF 6F D0 99 D9 10 FC CE BC 31 2F FE… +0,,1871,0:33.067.628,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,1872,0:33.081.634,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1876,0:33.082.630,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,1877,0:33.082.634,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 99 0E 95 5F BF 6F D0 99 D9 10 FC CE BC 31 2F FE… +0,,1881,0:33.083.631,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,1882,0:33.098.636,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B 50 A2 7D 4F 26 27 68 4A 9D D9 8D 19 40 29 0B… +0,,1886,0:33.099.633,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,1887,0:33.113.638,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1891,0:33.114.635,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,1892,0:33.114.638,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B 50 A2 7D 4F 26 27 68 4A 9D D9 8D 19 40 29 0B… +0,,1896,0:33.115.635,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,1897,0:33.130.640,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FA FF 5B 7A B8 4A C3 7C 58 C0 52 71 05 3D BA BB… +0,,1901,0:33.131.637,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,1902,0:33.145.642,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1906,0:33.146.639,2.833 us,,,,,[1 SOF],[Frame: 321] +0,,1907,0:33.146.643,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FA FF 5B 7A B8 4A C3 7C 58 C0 52 71 05 3D BA BB… +0,,1911,0:33.147.639,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,1912,0:33.162.645,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 1A 17 C8 5C 7A 47 6E 59 E1 A3 B6 17 C6 53 51… +0,,1916,0:33.163.642,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,1917,0:33.177.647,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1921,0:33.178.644,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,1922,0:33.178.647,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 1A 17 C8 5C 7A 47 6E 59 E1 A3 B6 17 C6 53 51… +0,,1926,0:33.179.644,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,1927,0:33.194.649,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9D 97 DF 34 83 7B 9C 9C 0E CF A0 A4 26 87 EC 9A… +0,,1931,0:33.195.646,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,1932,0:33.209.651,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1936,0:33.210.648,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,1937,0:33.210.651,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9D 97 DF 34 83 7B 9C 9C 0E CF A0 A4 26 87 EC 9A… +0,,1941,0:33.211.648,15.004.916 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,1942,0:33.226.654,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 81 94 4A 76 2C 4D AA 19 85 E9 B1 EC 35 94 4A… +0,,1946,0:33.227.651,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,1947,0:33.241.656,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1951,0:33.242.653,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,1952,0:33.242.656,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 81 94 4A 76 2C 4D AA 19 85 E9 B1 EC 35 94 4A… +0,,1956,0:33.243.653,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,1957,0:33.258.658,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 C5 74 96 6B B0 76 AC 97 DE F8 02 50 59 D6 5D… +0,,1961,0:33.259.655,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,1962,0:33.273.660,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1966,0:33.274.657,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,1967,0:33.274.660,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 C5 74 96 6B B0 76 AC 97 DE F8 02 50 59 D6 5D… +0,,1971,0:33.275.657,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,1972,0:33.290.663,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 21 8B 60 AF B1 AB C8 56 7D 5B FE B7 38 7F 6D 2D… +0,,1976,0:33.291.659,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,1977,0:33.305.665,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1981,0:33.306.662,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,1982,0:33.306.665,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 21 8B 60 AF B1 AB C8 56 7D 5B FE B7 38 7F 6D 2D… +0,,1986,0:33.307.662,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,1987,0:33.322.667,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9F D2 4D 4D EB 1B AC 51 5B A3 E5 58 79 7D D7 D0… +0,,1991,0:33.323.664,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,1992,0:33.337.669,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,1996,0:33.338.666,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,1997,0:33.338.669,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9F D2 4D 4D EB 1B AC 51 5B A3 E5 58 79 7D D7 D0… +0,,2001,0:33.339.666,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,2002,0:33.354.671,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D 59 BE 1B C4 AA EE 1C EE BB EF 36 92 02 F1 88… +0,,2006,0:33.355.668,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,2007,0:33.369.674,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2011,0:33.370.670,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,2012,0:33.370.674,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D 59 BE 1B C4 AA EE 1C EE BB EF 36 92 02 F1 88… +0,,2016,0:33.371.671,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,2017,0:33.386.676,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EE 8B C5 D3 A6 A7 9B E0 0E FF 60 E9 0B DC F0 C5… +0,,2021,0:33.387.673,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,2022,0:33.401.678,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2026,0:33.402.675,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,2027,0:33.402.678,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EE 8B C5 D3 A6 A7 9B E0 0E FF 60 E9 0B DC F0 C5… +0,,2031,0:33.403.675,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,2032,0:33.418.680,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F B6 0A BF D1 4F E4 7B 84 DE 01 00 0F 8A 96 E8… +0,,2036,0:33.419.677,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,2037,0:33.433.682,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2041,0:33.434.679,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,2042,0:33.434.683,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0F B6 0A BF D1 4F E4 7B 84 DE 01 00 0F 8A 96 E8… +0,,2046,0:33.435.679,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,2047,0:33.450.685,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 BB 6B D9 28 2D F5 CF 18 89 00 66 D9 55 81 92… +0,,2051,0:33.451.682,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,2052,0:33.465.687,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2056,0:33.466.684,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,2057,0:33.466.687,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A7 BB 6B D9 28 2D F5 CF 18 89 00 66 D9 55 81 92… +0,,2061,0:33.467.684,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,2062,0:33.482.689,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 07 AC 24 58 34 5E D0 3D CB 55 E8 EA 37 3E AC… +0,,2066,0:33.483.686,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,2067,0:33.497.691,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2071,0:33.498.688,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,2072,0:33.498.691,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 07 AC 24 58 34 5E D0 3D CB 55 E8 EA 37 3E AC… +0,,2076,0:33.499.688,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,2077,0:33.514.694,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D CA 5F 42 85 E3 1F 9E 46 A7 84 D2 CE 41 99 A1… +0,,2081,0:33.515.691,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,2082,0:33.529.696,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2086,0:33.530.693,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,2087,0:33.530.696,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D CA 5F 42 85 E3 1F 9E 46 A7 84 D2 CE 41 99 A1… +0,,2091,0:33.531.693,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,2092,0:33.546.698,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C1 FC 6B 5C 0F 4F D6 BE 83 BC 22 FE 85 5B 8B EA… +0,,2096,0:33.547.695,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,2097,0:33.561.700,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2101,0:33.562.697,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,2102,0:33.562.700,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C1 FC 6B 5C 0F 4F D6 BE 83 BC 22 FE 85 5B 8B EA… +0,,2106,0:33.563.697,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,2107,0:33.578.703,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 E1 C6 AD C7 1F 46 95 51 69 F1 0F E8 90 C1 69… +0,,2111,0:33.579.699,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,2112,0:33.593.705,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2116,0:33.594.702,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,2117,0:33.594.705,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 E1 C6 AD C7 1F 46 95 51 69 F1 0F E8 90 C1 69… +0,,2121,0:33.595.702,15.004.916 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,2122,0:33.610.707,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 32 AC F1 35 B2 BD 1A 21 EB B5 76 4A E3 59 A9 FE… +0,,2126,0:33.611.704,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,2127,0:33.625.709,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2131,0:33.626.706,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,2132,0:33.626.709,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 32 AC F1 35 B2 BD 1A 21 EB B5 76 4A E3 59 A9 FE… +0,,2136,0:33.627.706,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,2137,0:33.642.711,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E3 21 4E FD AB 65 F4 65 CD 3F E0 BB 40 52 FD 69… +0,,2141,0:33.643.708,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,2142,0:33.657.714,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2146,0:33.658.710,16.005.041 ms,,,,,[17 SOF],[Frames: 833 - 849] +0,,2147,0:33.674.716,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 E3 1D 93 7E FE AA B1 7B F3 38 72 C2 50 9F 6D… +0,,2151,0:33.675.713,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,2152,0:33.689.718,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2156,0:33.690.715,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,2157,0:33.690.718,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 E3 1D 93 7E FE AA B1 7B F3 38 72 C2 50 9F 6D… +0,,2161,0:33.691.715,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,2162,0:33.706.720,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 7A 07 EA AC B7 AD 05 58 6F C9 51 60 C3 C3 02… +0,,2166,0:33.707.717,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,2167,0:33.721.722,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2171,0:33.722.719,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,2172,0:33.722.723,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 7A 07 EA AC B7 AD 05 58 6F C9 51 60 C3 C3 02… +0,,2176,0:33.723.719,15.004.916 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,2177,0:33.738.725,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 81 1E 1D A4 D7 2C A7 F5 15 81 F7 F9 A6 A2 24… +0,,2181,0:33.739.722,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,2182,0:33.753.727,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2186,0:33.754.724,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,2187,0:33.754.727,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 81 1E 1D A4 D7 2C A7 F5 15 81 F7 F9 A6 A2 24… +0,,2191,0:33.755.724,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,2192,0:33.770.729,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 24 2A 89 38 3F F1 66 68 E5 A6 54 7E 5C EB E8… +0,,2196,0:33.771.726,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,2197,0:33.785.731,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2201,0:33.786.728,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,2202,0:33.786.731,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 24 2A 89 38 3F F1 66 68 E5 A6 54 7E 5C EB E8… +0,,2206,0:33.787.728,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,2207,0:33.802.734,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F FD 56 69 9A 60 BB A0 CD 63 F3 D2 EA C4 97 68… +0,,2211,0:33.803.731,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,2212,0:33.817.736,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2216,0:33.818.733,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,2217,0:33.818.736,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F FD 56 69 9A 60 BB A0 CD 63 F3 D2 EA C4 97 68… +0,,2221,0:33.819.733,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,2222,0:33.834.738,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 53 57 1F 5D BB 31 C4 B1 0E 0C 5F 4B A8 85 FF… +0,,2226,0:33.835.735,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,2227,0:33.849.740,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2231,0:33.850.737,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,2232,0:33.850.740,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 53 57 1F 5D BB 31 C4 B1 0E 0C 5F 4B A8 85 FF… +0,,2236,0:33.851.737,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,2237,0:33.866.743,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B 04 53 56 D8 2B C9 B0 8E 94 7C BF 12 B7 D0 2D… +0,,2241,0:33.867.739,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,2242,0:33.881.745,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2246,0:33.882.742,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,2247,0:33.882.745,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B 04 53 56 D8 2B C9 B0 8E 94 7C BF 12 B7 D0 2D… +0,,2251,0:33.883.742,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,2252,0:33.898.747,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D4 F7 67 CC D4 AD E1 45 75 5F 8E 45 15 1B 2F 93… +0,,2256,0:33.899.744,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,2257,0:33.913.749,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2261,0:33.914.746,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,2262,0:33.914.749,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D4 F7 67 CC D4 AD E1 45 75 5F 8E 45 15 1B 2F 93… +0,,2266,0:33.915.746,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,2267,0:33.930.751,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5C 5B E7 63 93 87 E2 C0 EC D5 E6 E3 9E 6F CF 01… +0,,2271,0:33.931.748,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,2272,0:33.945.754,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2276,0:33.946.750,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,2277,0:33.946.754,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5C 5B E7 63 93 87 E2 C0 EC D5 E6 E3 9E 6F CF 01… +0,,2281,0:33.947.751,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,2282,0:33.962.756,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 B3 5E B8 2B 1C 2E C2 EF 69 11 DC 69 70 4D 32… +0,,2286,0:33.963.753,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,2287,0:33.977.758,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2291,0:33.978.755,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,2292,0:33.978.758,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 22 B3 5E B8 2B 1C 2E C2 EF 69 11 DC 69 70 4D 32… +0,,2296,0:33.979.755,15.004.916 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,2297,0:33.994.760,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B 50 F6 5B 45 B2 86 CC B4 EB B2 80 A5 B4 7F C2… +0,,2301,0:33.995.757,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,2302,0:34.009.762,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2306,0:34.010.759,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,2307,0:34.010.763,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B 50 F6 5B 45 B2 86 CC B4 EB B2 80 A5 B4 7F C2… +0,,2311,0:34.011.759,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,2312,0:34.026.765,50.916 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1D 54 0B BF 20 6C 1A 08 89 77 2A 9D 69 58 FC BF… +0,,2316,0:34.027.762,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,2317,0:34.041.767,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2321,0:34.042.764,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,2322,0:34.042.767,50.916 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1D 54 0B BF 20 6C 1A 08 89 77 2A 9D 69 58 FC BF… +0,,2326,0:34.043.764,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,2327,0:34.058.769,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D5 69 DC 56 86 00 38 21 4F 22 E6 58 6B 2C 0A 4F… +0,,2331,0:34.059.766,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,2332,0:34.073.771,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2336,0:34.074.768,2.833 us,,,,,[1 SOF],[Frame: 1249] +0,,2337,0:34.074.771,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D5 69 DC 56 86 00 38 21 4F 22 E6 58 6B 2C 0A 4F… +0,,2341,0:34.075.768,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,2342,0:34.090.774,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 4B 7D A1 64 EC 87 D4 22 09 2D 0B FF 7F 64 F4… +0,,2346,0:34.091.771,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,2347,0:34.105.776,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2351,0:34.106.773,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,2352,0:34.106.776,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 4B 7D A1 64 EC 87 D4 22 09 2D 0B FF 7F 64 F4… +0,,2356,0:34.107.773,15.004.916 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,2357,0:34.122.778,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 60 8F D0 CB 17 5B F1 C3 06 71 B3 56 0B F7 C8… +0,,2361,0:34.123.775,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,2362,0:34.137.780,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2366,0:34.138.777,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,2367,0:34.138.780,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 93 60 8F D0 CB 17 5B F1 C3 06 71 B3 56 0B F7 C8… +0,,2371,0:34.139.777,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,2372,0:34.154.783,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FB DB A2 1B D6 7E 8A 16 95 2A 83 E0 6C 2A BF 85… +0,,2376,0:34.155.779,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,2377,0:34.169.785,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2381,0:34.170.781,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,2382,0:34.170.785,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FB DB A2 1B D6 7E 8A 16 95 2A 83 E0 6C 2A BF 85… +0,,2386,0:34.171.782,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,2387,0:34.186.787,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D1 FF F7 9E 82 94 07 C4 0D 8B 6C 79 C2 92 A9 84… +0,,2391,0:34.187.784,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,2392,0:34.201.789,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2396,0:34.202.786,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,2397,0:34.202.789,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D1 FF F7 9E 82 94 07 C4 0D 8B 6C 79 C2 92 A9 84… +0,,2401,0:34.203.786,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,2402,0:34.218.791,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 98 1C EF 46 A1 E7 3B 90 F3 74 27 42 7C 4E 1E C2… +0,,2406,0:34.219.788,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,2407,0:34.233.793,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2411,0:34.234.790,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,2412,0:34.234.794,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 98 1C EF 46 A1 E7 3B 90 F3 74 27 42 7C 4E 1E C2… +0,,2416,0:34.235.791,15.004.916 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,2417,0:34.250.796,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 ED 2A C7 6F C4 06 A0 BF 78 89 0E A5 2B DC CA 00… +0,,2421,0:34.251.793,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,2422,0:34.265.798,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2426,0:34.266.795,16.005.041 ms,,,,,[17 SOF],[Frames: 1441 - 1457] +0,,2427,0:34.282.800,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 95 70 03 63 0D C3 B5 A3 6B F6 B3 18 0E 97 F9 44… +0,,2431,0:34.283.797,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,2432,0:34.297.802,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2436,0:34.298.799,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,2437,0:34.298.803,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 95 70 03 63 0D C3 B5 A3 6B F6 B3 18 0E 97 F9 44… +0,,2441,0:34.299.799,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,2442,0:34.314.805,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 69 95 89 1B D6 D0 B7 C2 83 95 10 75 06 44 FE… +0,,2446,0:34.315.802,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,2447,0:34.329.807,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2451,0:34.330.804,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,2452,0:34.330.807,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F6 69 95 89 1B D6 D0 B7 C2 83 95 10 75 06 44 FE… +0,,2456,0:34.331.804,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,2457,0:34.346.809,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 82 BE 6A 45 AF 82 B0 CA 63 42 29 72 D3 6C 85… +0,,2461,0:34.347.806,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,2462,0:34.361.811,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2466,0:34.362.808,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,2467,0:34.362.811,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 82 BE 6A 45 AF 82 B0 CA 63 42 29 72 D3 6C 85… +0,,2471,0:34.363.808,15.005.000 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,2472,0:34.378.814,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F2 6C F1 3D 3B 97 CB 0F 86 AB 1B EC 7D 03 4E 4E… +0,,2476,0:34.379.811,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,2477,0:34.393.816,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2481,0:34.394.813,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,2482,0:34.394.816,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F2 6C F1 3D 3B 97 CB 0F 86 AB 1B EC 7D 03 4E 4E… +0,,2486,0:34.395.813,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,2487,0:34.410.818,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9A 2D ED CF 5B E8 BF 21 44 84 25 9D 0B 1E 51 48… +0,,2491,0:34.411.815,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,2492,0:34.425.820,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2496,0:34.426.817,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,2497,0:34.426.820,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9A 2D ED CF 5B E8 BF 21 44 84 25 9D 0B 1E 51 48… +0,,2501,0:34.427.817,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,2502,0:34.442.823,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 24 59 DF 7D D9 D5 51 C7 34 62 A7 94 65 9D 09… +0,,2506,0:34.443.819,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,2507,0:34.457.825,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2511,0:34.458.821,2.833 us,,,,,[1 SOF],[Frame: 1633] +0,,2512,0:34.458.825,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 24 59 DF 7D D9 D5 51 C7 34 62 A7 94 65 9D 09… +0,,2516,0:34.459.822,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,2517,0:34.474.827,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 15 CB 54 3E 5D 9B 5C 29 A4 9C 64 3F 24 2A CC 2E… +0,,2521,0:34.475.824,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,2522,0:34.489.829,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2526,0:34.490.826,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,2527,0:34.490.829,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 15 CB 54 3E 5D 9B 5C 29 A4 9C 64 3F 24 2A CC 2E… +0,,2531,0:34.491.826,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,2532,0:34.506.831,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 13 92 83 E9 70 31 E0 3D 04 E9 6C 6B C3 63 6B… +0,,2536,0:34.507.828,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,2537,0:34.521.833,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2541,0:34.522.830,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,2542,0:34.522.834,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F6 13 92 83 E9 70 31 E0 3D 04 E9 6C 6B C3 63 6B… +0,,2546,0:34.523.831,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,2547,0:34.538.836,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 35 6F 7D 86 79 6F EE 64 48 75 4B 36 EA 98 A7 4E… +0,,2551,0:34.539.833,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,2552,0:34.553.838,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2556,0:34.554.835,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,2557,0:34.554.838,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 35 6F 7D 86 79 6F EE 64 48 75 4B 36 EA 98 A7 4E… +0,,2561,0:34.555.835,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,2562,0:34.570.840,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 2B 3C 15 F4 4F 30 5B 03 4E 04 31 31 33 11 CE… +0,,2566,0:34.571.837,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,2567,0:34.585.842,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2571,0:34.586.839,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,2572,0:34.586.843,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 22 2B 3C 15 F4 4F 30 5B 03 4E 04 31 31 33 11 CE… +0,,2576,0:34.587.839,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,2577,0:34.602.845,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 21 45 2C 94 DB D3 A3 39 E9 5A 44 A6 BC 6C D9 4E… +0,,2581,0:34.603.842,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,2582,0:34.617.847,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2586,0:34.618.844,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,2587,0:34.618.847,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 21 45 2C 94 DB D3 A3 39 E9 5A 44 A6 BC 6C D9 4E… +0,,2591,0:34.619.844,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,2592,0:34.634.849,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 64 46 0B FF 18 F4 46 44 73 61 55 E3 02 CE 9B 46… +0,,2596,0:34.635.846,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,2597,0:34.649.851,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2601,0:34.650.848,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,2602,0:34.650.851,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 64 46 0B FF 18 F4 46 44 73 61 55 E3 02 CE 9B 46… +0,,2606,0:34.651.848,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,2607,0:34.666.854,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 2B DE 82 16 9E D3 C1 05 DB D7 C3 28 C4 E4 D8… +0,,2611,0:34.667.850,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,2612,0:34.681.856,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2616,0:34.682.853,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,2617,0:34.682.856,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 2B DE 82 16 9E D3 C1 05 DB D7 C3 28 C4 E4 D8… +0,,2621,0:34.683.853,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,2622,0:34.698.858,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 8C 22 16 FC 1D 79 E6 FF EC DC B5 03 F2 95 FC… +0,,2626,0:34.699.855,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,2627,0:34.713.860,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2631,0:34.714.857,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,2632,0:34.714.860,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 8C 22 16 FC 1D 79 E6 FF EC DC B5 03 F2 95 FC… +0,,2636,0:34.715.857,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,2637,0:34.730.862,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C6 BE CB C5 2A 35 AC 6F FA 8B 3E 5A 2E C2 EA 41… +0,,2641,0:34.731.859,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,2642,0:34.745.865,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2646,0:34.746.861,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,2647,0:34.746.865,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C6 BE CB C5 2A 35 AC 6F FA 8B 3E 5A 2E C2 EA 41… +0,,2651,0:34.747.862,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,2652,0:34.762.867,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C6 29 8C 92 43 A3 87 DB 17 D8 91 0B DC 31 29 F8… +0,,2656,0:34.763.864,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,2657,0:34.777.869,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2661,0:34.778.866,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,2662,0:34.778.869,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C6 29 8C 92 43 A3 87 DB 17 D8 91 0B DC 31 29 F8… +0,,2666,0:34.779.866,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,2667,0:34.794.871,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E E9 09 57 76 6C 4F D6 62 61 A7 E6 4C 67 E4 DB… +0,,2671,0:34.795.868,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,2672,0:34.809.873,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2676,0:34.810.870,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,2677,0:34.810.874,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E E9 09 57 76 6C 4F D6 62 61 A7 E6 4C 67 E4 DB… +0,,2681,0:34.811.870,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,2682,0:34.826.876,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC 48 25 24 DE 7E 6F B8 76 B4 9F 06 AB 4C 9C D7… +0,,2686,0:34.827.873,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,2687,0:34.841.878,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2691,0:34.842.875,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,2692,0:34.842.878,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC 48 25 24 DE 7E 6F B8 76 B4 9F 06 AB 4C 9C D7… +0,,2696,0:34.843.875,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,2697,0:34.858.880,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E 29 63 B6 D9 AA EF 34 76 DF 79 98 3F 8C B5 D2… +0,,2701,0:34.859.877,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,2702,0:34.873.882,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2706,0:34.874.879,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,2707,0:34.874.882,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E 29 63 B6 D9 AA EF 34 76 DF 79 98 3F 8C B5 D2… +0,,2711,0:34.875.879,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,2712,0:34.890.885,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3A 08 E5 D1 BA 9F 4C 1A 5B A7 55 11 23 89 CB 80… +0,,2716,0:34.891.882,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,2717,0:34.905.887,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2721,0:34.906.884,16.005.041 ms,,,,,[17 SOF],[Frames: 33 - 49] +0,,2722,0:34.922.889,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D8 66 C2 6C 83 00 2F 89 96 CB 16 EB 52 43 68 CF… +0,,2726,0:34.923.886,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,2727,0:34.937.891,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2731,0:34.938.888,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,2732,0:34.938.891,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D8 66 C2 6C 83 00 2F 89 96 CB 16 EB 52 43 68 CF… +0,,2736,0:34.939.888,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,2737,0:34.954.894,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 AC AB 74 98 6E 61 27 BE F7 69 08 76 B4 73 6B… +0,,2741,0:34.955.890,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,2742,0:34.969.896,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2746,0:34.970.893,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,2747,0:34.970.896,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 AC AB 74 98 6E 61 27 BE F7 69 08 76 B4 73 6B… +0,,2751,0:34.971.893,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,2752,0:34.986.898,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B 92 6F 5B 63 96 83 E3 45 98 DE 59 D9 11 EB 87… +0,,2756,0:34.987.895,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,2757,0:35.001.900,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2761,0:35.002.897,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,2762,0:35.002.900,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B 92 6F 5B 63 96 83 E3 45 98 DE 59 D9 11 EB 87… +0,,2766,0:35.003.897,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,2767,0:35.018.902,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 74 1E 9E 7E 0B 7D A0 ED 5C F2 84 6C 52 28 7A 7A… +0,,2771,0:35.019.899,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,2772,0:35.033.905,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2776,0:35.034.901,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,2777,0:35.034.905,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 74 1E 9E 7E 0B 7D A0 ED 5C F2 84 6C 52 28 7A 7A… +0,,2781,0:35.035.902,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,2782,0:35.050.907,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C5 64 13 73 C3 A0 E8 1B AF 0A 4B 39 D4 54 32 26… +0,,2786,0:35.051.904,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,2787,0:35.065.909,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2791,0:35.066.906,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,2792,0:35.066.909,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C5 64 13 73 C3 A0 E8 1B AF 0A 4B 39 D4 54 32 26… +0,,2796,0:35.067.906,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,2797,0:35.082.911,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 F1 3B A7 73 C2 7D A6 12 13 70 57 52 BB 7C 9B… +0,,2801,0:35.083.908,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,2802,0:35.097.913,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2806,0:35.098.910,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,2807,0:35.098.914,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 F1 3B A7 73 C2 7D A6 12 13 70 57 52 BB 7C 9B… +0,,2811,0:35.099.910,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,2812,0:35.114.916,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 4F 46 B4 93 4E 56 DA 7C 82 E3 BC 6E 86 72 6C… +0,,2816,0:35.115.913,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,2817,0:35.129.918,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2821,0:35.130.915,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,2822,0:35.130.918,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F 4F 46 B4 93 4E 56 DA 7C 82 E3 BC 6E 86 72 6C… +0,,2826,0:35.131.915,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,2827,0:35.146.920,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 D7 0D 2B F8 1E 70 05 F4 8C 86 C0 6F C4 4B 03… +0,,2831,0:35.147.917,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,2832,0:35.161.922,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2836,0:35.162.919,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,2837,0:35.162.922,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 D7 0D 2B F8 1E 70 05 F4 8C 86 C0 6F C4 4B 03… +0,,2841,0:35.163.919,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,2842,0:35.178.925,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 B9 90 89 DB 3C 2C FF D7 89 CB 42 AA 78 29 6C… +0,,2846,0:35.179.922,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,2847,0:35.193.927,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2851,0:35.194.924,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,2852,0:35.194.927,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 B9 90 89 DB 3C 2C FF D7 89 CB 42 AA 78 29 6C… +0,,2856,0:35.195.924,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,2857,0:35.210.929,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 91 DC FC B6 7E 01 62 41 D1 98 72 5B 65 D7 18… +0,,2861,0:35.211.926,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,2862,0:35.225.931,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2866,0:35.226.928,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,2867,0:35.226.931,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 91 DC FC B6 7E 01 62 41 D1 98 72 5B 65 D7 18… +0,,2871,0:35.227.928,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,2872,0:35.242.934,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E9 B2 7A 96 3B 6C 66 D7 1C 82 B0 CA 57 1A 69 48… +0,,2876,0:35.243.930,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,2877,0:35.257.936,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2881,0:35.258.933,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,2882,0:35.258.936,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E9 B2 7A 96 3B 6C 66 D7 1C 82 B0 CA 57 1A 69 48… +0,,2886,0:35.259.933,15.004.916 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,2887,0:35.274.938,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B 26 D9 C6 7C B0 78 5B CE 0D 7C CC 51 3C EB 3C… +0,,2891,0:35.275.935,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,2892,0:35.289.940,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2896,0:35.290.937,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,2897,0:35.290.940,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B 26 D9 C6 7C B0 78 5B CE 0D 7C CC 51 3C EB 3C… +0,,2901,0:35.291.937,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,2902,0:35.306.942,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0C 7F 8A 6C 7C AA 59 EA 84 1A 60 F0 5A 3F 61 91… +0,,2906,0:35.307.939,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,2907,0:35.321.945,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2911,0:35.322.941,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,2912,0:35.322.945,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0C 7F 8A 6C 7C AA 59 EA 84 1A 60 F0 5A 3F 61 91… +0,,2916,0:35.323.942,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,2917,0:35.338.947,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 17 51 85 99 58 4A 89 37 2B 0C 91 E1 3D 2C 2C E9… +0,,2921,0:35.339.944,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,2922,0:35.353.949,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2926,0:35.354.946,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,2927,0:35.354.949,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 17 51 85 99 58 4A 89 37 2B 0C 91 E1 3D 2C 2C E9… +0,,2931,0:35.355.946,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,2932,0:35.370.951,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AB B0 BD 54 D0 BE 6F 9B C4 02 42 5C 17 5A 74 6A… +0,,2936,0:35.371.948,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,2937,0:35.385.953,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2941,0:35.386.950,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,2942,0:35.386.954,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AB B0 BD 54 D0 BE 6F 9B C4 02 42 5C 17 5A 74 6A… +0,,2946,0:35.387.950,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,2947,0:35.402.956,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF 06 30 30 A2 1C 73 A9 B8 3D 37 37 8D BC B5 F7… +0,,2951,0:35.403.953,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,2952,0:35.417.958,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2956,0:35.418.955,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,2957,0:35.418.958,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF 06 30 30 A2 1C 73 A9 B8 3D 37 37 8D BC B5 F7… +0,,2961,0:35.419.955,15.004.916 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,2962,0:35.434.960,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AD 61 D7 99 B1 FD DC D1 C8 62 7A BC 83 F3 B3 7B… +0,,2966,0:35.435.957,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,2967,0:35.449.962,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2971,0:35.450.959,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,2972,0:35.450.962,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AD 61 D7 99 B1 FD DC D1 C8 62 7A BC 83 F3 B3 7B… +0,,2976,0:35.451.959,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,2977,0:35.466.965,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 BE 91 BB 19 68 85 83 8F 30 FB 66 9D 19 4F C7… +0,,2981,0:35.467.962,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,2982,0:35.481.967,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,2986,0:35.482.964,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,2987,0:35.482.967,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 BE 91 BB 19 68 85 83 8F 30 FB 66 9D 19 4F C7… +0,,2991,0:35.483.964,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,2992,0:35.498.969,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 0E EF FA 4C 20 07 83 F3 B9 FE 72 01 57 6D 5C… +0,,2996,0:35.499.966,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,2997,0:35.513.971,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3001,0:35.514.968,16.005.041 ms,,,,,[17 SOF],[Frames: 641 - 657] +0,,3002,0:35.530.974,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 55 BA 2E 4E 75 8F 38 E8 DA BC C9 70 43 76 0B… +0,,3006,0:35.531.970,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,3007,0:35.545.976,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3011,0:35.546.973,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,3012,0:35.546.976,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 55 BA 2E 4E 75 8F 38 E8 DA BC C9 70 43 76 0B… +0,,3016,0:35.547.973,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,3017,0:35.562.978,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 CD 5B FF DA 80 1A 2D E0 BB 2A 99 46 6F E3 36… +0,,3021,0:35.563.975,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,3022,0:35.577.980,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3026,0:35.578.977,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,3027,0:35.578.980,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 CD 5B FF DA 80 1A 2D E0 BB 2A 99 46 6F E3 36… +0,,3031,0:35.579.977,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,3032,0:35.594.982,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CA E1 AF FE FE FA FB 85 8F 9A 5B 8A 9C AC F8 3D… +0,,3036,0:35.595.979,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,3037,0:35.609.985,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3041,0:35.610.981,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,3042,0:35.610.985,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CA E1 AF FE FE FA FB 85 8F 9A 5B 8A 9C AC F8 3D… +0,,3046,0:35.611.982,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,3047,0:35.626.987,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E 34 58 B7 16 BE 8D 33 D1 1D 6F C7 00 1A 1E 26… +0,,3051,0:35.627.984,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,3052,0:35.641.989,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3056,0:35.642.986,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,3057,0:35.642.989,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E 34 58 B7 16 BE 8D 33 D1 1D 6F C7 00 1A 1E 26… +0,,3061,0:35.643.986,15.004.916 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,3062,0:35.658.991,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 89 D4 B6 A8 6F 14 CD 3F 43 E3 6B 12 68 9B F9 3C… +0,,3066,0:35.659.988,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,3067,0:35.673.993,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3071,0:35.674.990,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,3072,0:35.674.994,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 89 D4 B6 A8 6F 14 CD 3F 43 E3 6B 12 68 9B F9 3C… +0,,3076,0:35.675.990,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,3077,0:35.690.996,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E8 11 24 19 1A BA 1C 72 BB CB B9 93 20 3E 7B C8… +0,,3081,0:35.691.993,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,3082,0:35.705.998,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3086,0:35.706.995,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,3087,0:35.706.998,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E8 11 24 19 1A BA 1C 72 BB CB B9 93 20 3E 7B C8… +0,,3091,0:35.707.995,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,3092,0:35.723.000,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 F8 34 A3 4E E9 33 11 08 18 03 4D 0C 38 B1 6A… +0,,3096,0:35.723.997,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,3097,0:35.738.002,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3101,0:35.738.999,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,3102,0:35.739.002,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 F8 34 A3 4E E9 33 11 08 18 03 4D 0C 38 B1 6A… +0,,3106,0:35.739.999,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,3107,0:35.755.005,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4A 8E D4 41 0D E0 23 44 05 AD 4E 5D 5B AB 37 63… +0,,3111,0:35.756.002,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,3112,0:35.770.007,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3116,0:35.771.004,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,3117,0:35.771.007,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4A 8E D4 41 0D E0 23 44 05 AD 4E 5D 5B AB 37 63… +0,,3121,0:35.772.004,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,3122,0:35.787.009,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 CA 20 38 AA 23 3B 38 91 D2 0E FA 33 8C 7B 5A… +0,,3126,0:35.788.006,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,3127,0:35.802.011,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3131,0:35.803.008,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,3132,0:35.803.011,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 CA 20 38 AA 23 3B 38 91 D2 0E FA 33 8C 7B 5A… +0,,3136,0:35.804.008,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,3137,0:35.819.014,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 ED 62 CE D8 AC EE 00 B2 58 E8 35 89 71 36 41… +0,,3141,0:35.820.010,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,3142,0:35.834.016,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3146,0:35.835.013,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,3147,0:35.835.016,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B5 ED 62 CE D8 AC EE 00 B2 58 E8 35 89 71 36 41… +0,,3151,0:35.836.013,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,3152,0:35.851.018,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B3 E4 C5 74 8F 0F 96 56 70 27 A5 11 6A CA 6B 7B… +0,,3156,0:35.852.015,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,3157,0:35.866.020,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3161,0:35.867.017,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,3162,0:35.867.020,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B3 E4 C5 74 8F 0F 96 56 70 27 A5 11 6A CA 6B 7B… +0,,3166,0:35.868.017,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,3167,0:35.883.023,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D5 C9 F0 81 2C 79 0B A9 E6 AF 0A 88 78 AB 01 38… +0,,3171,0:35.884.019,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,3172,0:35.898.025,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3176,0:35.899.021,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,3177,0:35.899.025,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D5 C9 F0 81 2C 79 0B A9 E6 AF 0A 88 78 AB 01 38… +0,,3181,0:35.900.022,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,3182,0:35.915.027,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E C6 0C 60 B2 EB 94 DA 94 3B E9 4B 6C BC 63 0F… +0,,3186,0:35.916.024,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,3187,0:35.930.029,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3191,0:35.931.026,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,3192,0:35.931.029,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E C6 0C 60 B2 EB 94 DA 94 3B E9 4B 6C BC 63 0F… +0,,3196,0:35.932.026,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,3197,0:35.947.031,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 84 F8 D7 DF B8 C9 30 80 FF 1D B4 99 66 5F 0D 23… +0,,3201,0:35.948.028,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,3202,0:35.962.033,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3206,0:35.963.030,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,3207,0:35.963.034,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 84 F8 D7 DF B8 C9 30 80 FF 1D B4 99 66 5F 0D 23… +0,,3211,0:35.964.030,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,3212,0:35.979.036,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C B4 58 4B 1B 7D F8 DC 80 B9 F8 E8 5C 80 6A 97… +0,,3216,0:35.980.033,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,3217,0:35.994.038,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3221,0:35.995.035,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,3222,0:35.995.038,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9C B4 58 4B 1B 7D F8 DC 80 B9 F8 E8 5C 80 6A 97… +0,,3226,0:35.996.035,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,3227,0:36.011.040,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 28 26 F2 51 9D 76 72 1D 9E 1F 9A BE 8B 9F E1 EA… +0,,3231,0:36.012.037,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,3232,0:36.026.042,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3236,0:36.027.039,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,3237,0:36.027.043,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 28 26 F2 51 9D 76 72 1D 9E 1F 9A BE 8B 9F E1 EA… +0,,3241,0:36.028.039,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,3242,0:36.043.045,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 19 98 6A D7 85 35 EE 8D 03 16 A0 DF 47 90 0F… +0,,3246,0:36.044.042,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,3247,0:36.058.047,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3251,0:36.059.044,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,3252,0:36.059.047,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 19 98 6A D7 85 35 EE 8D 03 16 A0 DF 47 90 0F… +0,,3256,0:36.060.044,15.004.916 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,3257,0:36.075.049,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D3 81 3C 6B 32 27 E5 94 39 98 10 5A 6F 28 EB 09… +0,,3261,0:36.076.046,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,3262,0:36.090.051,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3266,0:36.091.048,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,3267,0:36.091.051,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D3 81 3C 6B 32 27 E5 94 39 98 10 5A 6F 28 EB 09… +0,,3271,0:36.092.048,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,3272,0:36.107.054,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF 6A 22 A7 6F 0B 68 F7 8A 3E B3 4F DA C6 80 24… +0,,3276,0:36.108.050,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,3277,0:36.122.056,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3281,0:36.123.052,2.833 us,,,,,[1 SOF],[Frame: 1249] +0,,3282,0:36.123.056,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF 6A 22 A7 6F 0B 68 F7 8A 3E B3 4F DA C6 80 24… +0,,3286,0:36.124.053,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,3287,0:36.139.058,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0B 81 A1 77 E9 C3 A0 41 32 44 2E DC 3A 32 DA 9A… +0,,3291,0:36.140.055,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,3292,0:36.154.060,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3296,0:36.155.057,16.005.041 ms,,,,,[17 SOF],[Frames: 1281 - 1297] +0,,3297,0:36.171.062,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 81 2D 84 83 A1 75 B6 B6 8F 0A D8 E8 3E F4 B4… +0,,3301,0:36.172.059,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,3302,0:36.186.064,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3306,0:36.187.061,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,3307,0:36.187.065,50.895 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 81 2D 84 83 A1 75 B6 B6 8F 0A D8 E8 3E F4 B4… +0,,3311,0:36.188.062,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,3312,0:36.203.067,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 C4 34 6F DB 27 0B C0 61 48 90 8E 27 2A 1A E7… +0,,3316,0:36.204.064,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,3317,0:36.218.069,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3321,0:36.219.066,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,3322,0:36.219.069,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 C4 34 6F DB 27 0B C0 61 48 90 8E 27 2A 1A E7… +0,,3326,0:36.220.066,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,3327,0:36.235.071,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3D 5E 13 85 CD 7A E7 51 2D A0 0E 17 EB 9C 83 00… +0,,3331,0:36.236.068,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,3332,0:36.250.073,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3336,0:36.251.070,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,3337,0:36.251.074,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3D 5E 13 85 CD 7A E7 51 2D A0 0E 17 EB 9C 83 00… +0,,3341,0:36.252.070,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,3342,0:36.267.076,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 79 4B E2 7F 7B F6 64 4F D4 8C 78 33 1B 63 E5… +0,,3346,0:36.268.073,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,3347,0:36.282.078,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3351,0:36.283.075,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,3352,0:36.283.078,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 79 4B E2 7F 7B F6 64 4F D4 8C 78 33 1B 63 E5… +0,,3356,0:36.284.075,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,3357,0:36.299.080,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE FB AB 46 B9 12 68 FD B6 7E 69 F2 58 7F 7D 5F… +0,,3361,0:36.300.077,14.004.750 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,3362,0:36.314.082,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3366,0:36.315.079,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,3367,0:36.315.082,50.916 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DE FB AB 46 B9 12 68 FD B6 7E 69 F2 58 7F 7D 5F… +0,,3371,0:36.316.079,15.004.916 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,3372,0:36.331.085,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 63 8B 7A 94 B9 55 B8 B2 6D 92 69 2C 8E DD 12… +0,,3376,0:36.332.082,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,3377,0:36.346.087,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3381,0:36.347.084,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,3382,0:36.347.087,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 63 8B 7A 94 B9 55 B8 B2 6D 92 69 2C 8E DD 12… +0,,3386,0:36.348.084,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,3387,0:36.363.089,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A 1E 56 F6 A7 EE C2 1E 95 E1 6B 70 9C 6E A8 15… +0,,3391,0:36.364.086,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,3392,0:36.378.091,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3396,0:36.379.088,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,3397,0:36.379.091,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A 1E 56 F6 A7 EE C2 1E 95 E1 6B 70 9C 6E A8 15… +0,,3401,0:36.380.088,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,3402,0:36.395.094,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 77 F1 F7 FD BB 77 D3 1F 6D 0B 71 99 6F 44 5F… +0,,3406,0:36.396.090,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,3407,0:36.410.096,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3411,0:36.411.092,2.833 us,,,,,[1 SOF],[Frame: 1537] +0,,3412,0:36.411.096,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 77 F1 F7 FD BB 77 D3 1F 6D 0B 71 99 6F 44 5F… +0,,3416,0:36.412.093,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,3417,0:36.427.098,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E 0A 27 0F 6A 8D 3D F8 6E 3A 44 A2 C2 73 2E 0D… +0,,3421,0:36.428.095,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,3422,0:36.442.100,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3426,0:36.443.097,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,3427,0:36.443.100,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E 0A 27 0F 6A 8D 3D F8 6E 3A 44 A2 C2 73 2E 0D… +0,,3431,0:36.444.097,15.004.916 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,3432,0:36.459.102,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DB 08 57 77 E9 17 D4 0C 5E BB 02 11 F6 40 D4 E5… +0,,3436,0:36.460.099,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,3437,0:36.474.104,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3441,0:36.475.101,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,3442,0:36.475.105,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DB 08 57 77 E9 17 D4 0C 5E BB 02 11 F6 40 D4 E5… +0,,3446,0:36.476.102,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,3447,0:36.491.107,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 40 E5 53 C2 00 5E 17 B3 41 D8 CE 58 07 30 47 86… +0,,3451,0:36.492.104,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,3452,0:36.506.109,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3456,0:36.507.106,2.833 us,,,,,[1 SOF],[Frame: 1633] +0,,3457,0:36.507.109,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 40 E5 53 C2 00 5E 17 B3 41 D8 CE 58 07 30 47 86… +0,,3461,0:36.508.106,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,3462,0:36.523.111,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 40 9D 5A 99 57 14 6F 32 BA F6 7E 37 2D D5 5A… +0,,3466,0:36.524.108,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,3467,0:36.538.113,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3471,0:36.539.110,2.833 us,,,,,[1 SOF],[Frame: 1665] +0,,3472,0:36.539.113,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 40 9D 5A 99 57 14 6F 32 BA F6 7E 37 2D D5 5A… +0,,3476,0:36.540.110,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,3477,0:36.555.116,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 A4 3D 01 D2 DF 2C 78 F8 84 A6 D9 90 C0 34 F1… +0,,3481,0:36.556.113,14.004.750 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,3482,0:36.570.118,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3486,0:36.571.115,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,3487,0:36.571.118,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 A4 3D 01 D2 DF 2C 78 F8 84 A6 D9 90 C0 34 F1… +0,,3491,0:36.572.115,15.004.916 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,3492,0:36.587.120,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 49 D2 9A 26 2D B2 FD 86 E0 38 E2 44 2E 06 78… +0,,3496,0:36.588.117,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,3497,0:36.602.122,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3501,0:36.603.119,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,3502,0:36.603.122,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 49 D2 9A 26 2D B2 FD 86 E0 38 E2 44 2E 06 78… +0,,3506,0:36.604.119,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,3507,0:36.619.125,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 35 DB 35 DA 85 44 34 2F 71 62 3C D9 01 E4 5C 2B… +0,,3511,0:36.620.121,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,3512,0:36.634.127,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3516,0:36.635.124,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,3517,0:36.635.127,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 35 DB 35 DA 85 44 34 2F 71 62 3C D9 01 E4 5C 2B… +0,,3521,0:36.636.124,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,3522,0:36.651.129,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 46 CE 1D 4D 73 C4 AA D8 95 44 95 E0 18 28 E4… +0,,3526,0:36.652.126,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,3527,0:36.666.131,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3531,0:36.667.128,2.833 us,,,,,[1 SOF],[Frame: 1793] +0,,3532,0:36.667.131,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 46 CE 1D 4D 73 C4 AA D8 95 44 95 E0 18 28 E4… +0,,3536,0:36.668.128,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,3537,0:36.683.133,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 88 32 FC 0F D6 6C 46 CE 19 6F 78 B5 ED 21 A9 83… +0,,3541,0:36.684.130,14.004.750 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,3542,0:36.698.136,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3546,0:36.699.132,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,3547,0:36.699.136,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 88 32 FC 0F D6 6C 46 CE 19 6F 78 B5 ED 21 A9 83… +0,,3551,0:36.700.133,15.004.916 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,3552,0:36.715.138,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0A 5F DF F8 FD 99 DC C4 78 66 0F 33 89 4F AA F6… +0,,3556,0:36.716.135,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,3557,0:36.730.140,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3561,0:36.731.137,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,3562,0:36.731.140,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0A 5F DF F8 FD 99 DC C4 78 66 0F 33 89 4F AA F6… +0,,3566,0:36.732.137,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,3567,0:36.747.142,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 25 CB 14 EF 50 12 A8 0C 6A BB 2F 30 8B 9C ED 90… +0,,3571,0:36.748.139,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,3572,0:36.762.144,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3576,0:36.763.141,16.005.041 ms,,,,,[17 SOF],[Frames: 1889 - 1905] +0,,3577,0:36.779.147,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C6 E6 F6 76 CF 6E D3 80 8C 43 6F 85 20 26 AF B7… +0,,3581,0:36.780.144,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,3582,0:36.794.149,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3586,0:36.795.146,2.833 us,,,,,[1 SOF],[Frame: 1921] +0,,3587,0:36.795.149,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C6 E6 F6 76 CF 6E D3 80 8C 43 6F 85 20 26 AF B7… +0,,3591,0:36.796.146,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,3592,0:36.811.151,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4D 91 58 F8 5A F2 F0 8A 30 34 85 00 CD 59 01 E2… +0,,3596,0:36.812.148,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,3597,0:36.826.153,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3601,0:36.827.150,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,3602,0:36.827.153,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D 91 58 F8 5A F2 F0 8A 30 34 85 00 CD 59 01 E2… +0,,3606,0:36.828.150,15.004.916 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,3607,0:36.843.156,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 26 8E D6 A5 95 63 EC 64 86 44 DF 7B 11 11 C5 DD… +0,,3611,0:36.844.153,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,3612,0:36.858.158,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3616,0:36.859.155,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,3617,0:36.859.158,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 26 8E D6 A5 95 63 EC 64 86 44 DF 7B 11 11 C5 DD… +0,,3621,0:36.860.155,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,3622,0:36.875.160,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DA 2A 3B 2F 93 C8 04 09 58 C2 C3 B2 EF 7E 93 3B… +0,,3626,0:36.876.157,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,3627,0:36.890.162,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3631,0:36.891.159,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,3632,0:36.891.162,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DA 2A 3B 2F 93 C8 04 09 58 C2 C3 B2 EF 7E 93 3B… +0,,3636,0:36.892.159,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,3637,0:36.907.165,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 E7 61 D7 09 D8 11 93 E3 53 FC F4 52 4B AB C0… +0,,3641,0:36.908.161,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,3642,0:36.922.167,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3646,0:36.923.164,2.833 us,,,,,[1 SOF],[Frame: 1] +0,,3647,0:36.923.167,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 E7 61 D7 09 D8 11 93 E3 53 FC F4 52 4B AB C0… +0,,3651,0:36.924.164,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,3652,0:36.939.169,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D 59 45 3A 78 41 FE 3D F0 6B 61 C8 B3 77 1D 64… +0,,3656,0:36.940.166,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,3657,0:36.954.171,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3661,0:36.955.168,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,3662,0:36.955.171,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D 59 45 3A 78 41 FE 3D F0 6B 61 C8 B3 77 1D 64… +0,,3666,0:36.956.168,15.004.916 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,3667,0:36.971.173,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8B 62 B4 06 AF 8D 71 0F 03 00 C6 16 83 B2 FB 1D… +0,,3671,0:36.972.170,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,3672,0:36.986.176,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3676,0:36.987.172,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,3677,0:36.987.176,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8B 62 B4 06 AF 8D 71 0F 03 00 C6 16 83 B2 FB 1D… +0,,3681,0:36.988.173,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,3682,0:37.003.178,50.916 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 50 6E 9D FF 30 A3 C0 20 17 76 AB FF CF D2 46 E5… +0,,3686,0:37.004.175,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,3687,0:37.018.180,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3691,0:37.019.177,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,3692,0:37.019.180,50.916 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 50 6E 9D FF 30 A3 C0 20 17 76 AB FF CF D2 46 E5… +0,,3696,0:37.020.177,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,3697,0:37.035.182,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A2 A1 57 8C 42 65 B1 B5 15 5F 52 D1 41 FA 5C 19… +0,,3701,0:37.036.179,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,3702,0:37.050.184,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3706,0:37.051.181,2.833 us,,,,,[1 SOF],[Frame: 129] +0,,3707,0:37.051.185,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A2 A1 57 8C 42 65 B1 B5 15 5F 52 D1 41 FA 5C 19… +0,,3711,0:37.052.181,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,3712,0:37.067.187,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BD D2 F9 31 20 27 F8 41 D5 D0 3F 7A F7 8E 22 06… +0,,3716,0:37.068.184,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,3717,0:37.082.189,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3721,0:37.083.186,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,3722,0:37.083.189,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BD D2 F9 31 20 27 F8 41 D5 D0 3F 7A F7 8E 22 06… +0,,3726,0:37.084.186,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,3727,0:37.099.191,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 7B 53 48 41 4B 90 45 62 14 0C 6C 81 A7 BC 0B… +0,,3731,0:37.100.188,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,3732,0:37.114.193,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3736,0:37.115.190,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,3737,0:37.115.193,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 7B 53 48 41 4B 90 45 62 14 0C 6C 81 A7 BC 0B… +0,,3741,0:37.116.190,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,3742,0:37.131.196,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D D1 9C 75 D0 DE 85 C0 4B AD 9A 85 24 62 A9 0F… +0,,3746,0:37.132.193,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,3747,0:37.146.198,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3751,0:37.147.195,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,3752,0:37.147.198,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D D1 9C 75 D0 DE 85 C0 4B AD 9A 85 24 62 A9 0F… +0,,3756,0:37.148.195,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,3757,0:37.163.200,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 91 C7 A0 56 0D C4 D7 4F 42 7E 64 A9 0A E0 EA… +0,,3761,0:37.164.197,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,3762,0:37.178.202,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3766,0:37.179.199,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,3767,0:37.179.202,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 91 C7 A0 56 0D C4 D7 4F 42 7E 64 A9 0A E0 EA… +0,,3771,0:37.180.199,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,3772,0:37.195.205,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 43 08 37 D6 B8 AC EF 46 86 24 C0 8A D2 09 19 CC… +0,,3776,0:37.196.201,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,3777,0:37.210.207,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3781,0:37.211.204,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,3782,0:37.211.207,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 43 08 37 D6 B8 AC EF 46 86 24 C0 8A D2 09 19 CC… +0,,3786,0:37.212.204,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,3787,0:37.227.209,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF FB CA B4 0C CC 03 B1 B4 3F A5 98 B7 14 58 3A… +0,,3791,0:37.228.206,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,3792,0:37.242.211,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3796,0:37.243.208,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,3797,0:37.243.211,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF FB CA B4 0C CC 03 B1 B4 3F A5 98 B7 14 58 3A… +0,,3801,0:37.244.208,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,3802,0:37.259.213,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CA C4 32 36 7F 78 CB 75 C9 E2 55 25 F5 79 18 4E… +0,,3806,0:37.260.210,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,3807,0:37.274.216,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3811,0:37.275.212,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,3812,0:37.275.216,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CA C4 32 36 7F 78 CB 75 C9 E2 55 25 F5 79 18 4E… +0,,3816,0:37.276.213,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,3817,0:37.291.218,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EA DF 2E 6A 5B E4 DA 2D 8B 36 6D 19 98 CD 62 A9… +0,,3821,0:37.292.215,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,3822,0:37.306.220,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3826,0:37.307.217,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,3827,0:37.307.220,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EA DF 2E 6A 5B E4 DA 2D 8B 36 6D 19 98 CD 62 A9… +0,,3831,0:37.308.217,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,3832,0:37.323.222,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 22 1B 4D E7 E4 EA E1 29 1B C5 95 7D 21 C0 87… +0,,3836,0:37.324.219,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,3837,0:37.338.224,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3841,0:37.339.221,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,3842,0:37.339.225,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 22 1B 4D E7 E4 EA E1 29 1B C5 95 7D 21 C0 87… +0,,3846,0:37.340.221,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,3847,0:37.355.227,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 52 27 23 31 18 1F 24 1D B7 8C 59 3C 22 71 45 71… +0,,3851,0:37.356.224,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,3852,0:37.370.229,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3856,0:37.371.226,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,3857,0:37.371.229,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 52 27 23 31 18 1F 24 1D B7 8C 59 3C 22 71 45 71… +0,,3861,0:37.372.226,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,3862,0:37.387.231,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C9 79 F8 84 AE 61 CF 9E 6A 83 A2 2E 59 1E B8 2E… +0,,3866,0:37.388.228,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,3867,0:37.402.233,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3871,0:37.403.230,16.005.041 ms,,,,,[17 SOF],[Frames: 481 - 497] +0,,3872,0:37.419.236,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E3 13 31 27 BB 58 66 50 8E 1C A6 E4 98 D1 13 19… +0,,3876,0:37.420.233,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,3877,0:37.434.238,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3881,0:37.435.235,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,3882,0:37.435.238,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E3 13 31 27 BB 58 66 50 8E 1C A6 E4 98 D1 13 19… +0,,3886,0:37.436.235,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,3887,0:37.451.240,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 19 E9 4C 86 43 85 A3 9D 0C C7 F9 48 93 BA 48 92… +0,,3891,0:37.452.237,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,3892,0:37.466.242,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3896,0:37.467.239,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,3897,0:37.467.242,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 19 E9 4C 86 43 85 A3 9D 0C C7 F9 48 93 BA 48 92… +0,,3901,0:37.468.239,15.004.916 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,3902,0:37.483.245,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7F B5 61 C7 48 8F FA 39 50 45 02 57 03 19 D2 F4… +0,,3906,0:37.484.241,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,3907,0:37.498.247,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3911,0:37.499.244,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,3912,0:37.499.247,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7F B5 61 C7 48 8F FA 39 50 45 02 57 03 19 D2 F4… +0,,3916,0:37.500.244,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,3917,0:37.515.249,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 7E 09 BD 88 74 34 C1 F0 2C 51 39 F7 49 F2 FB… +0,,3921,0:37.516.246,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,3922,0:37.530.251,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3926,0:37.531.248,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,3927,0:37.531.251,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 7E 09 BD 88 74 34 C1 F0 2C 51 39 F7 49 F2 FB… +0,,3931,0:37.532.248,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,3932,0:37.547.253,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6B 2D 4E 2D 8A 39 A0 2D 33 53 3D 0A AD A8 AF 3A… +0,,3936,0:37.548.250,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,3937,0:37.562.256,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3941,0:37.563.252,2.833 us,,,,,[1 SOF],[Frame: 641] +0,,3942,0:37.563.256,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6B 2D 4E 2D 8A 39 A0 2D 33 53 3D 0A AD A8 AF 3A… +0,,3946,0:37.564.253,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,3947,0:37.579.258,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D3 42 06 04 5B 76 9C 5E F4 C0 5B EE 2F 79 CD 3E… +0,,3951,0:37.580.255,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,3952,0:37.594.260,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3956,0:37.595.257,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,3957,0:37.595.260,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D3 42 06 04 5B 76 9C 5E F4 C0 5B EE 2F 79 CD 3E… +0,,3961,0:37.596.257,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,3962,0:37.611.262,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 1F B1 9F 3C 9E 1A 0B 2E A3 D1 A9 F3 7B 1B 61… +0,,3966,0:37.612.259,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,3967,0:37.626.264,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3971,0:37.627.261,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,3972,0:37.627.265,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 1F B1 9F 3C 9E 1A 0B 2E A3 D1 A9 F3 7B 1B 61… +0,,3976,0:37.628.261,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,3977,0:37.643.267,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 D6 D9 9C CA AC E2 5C 6A 23 3F EC 08 15 81 30… +0,,3981,0:37.644.264,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,3982,0:37.658.269,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,3986,0:37.659.266,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,3987,0:37.659.269,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 D6 D9 9C CA AC E2 5C 6A 23 3F EC 08 15 81 30… +0,,3991,0:37.660.266,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,3992,0:37.675.271,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 23 15 A3 1A 9A 5A 42 08 86 27 3F 50 9A 31 62… +0,,3996,0:37.676.268,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,3997,0:37.690.273,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4001,0:37.691.270,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,4002,0:37.691.273,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 23 15 A3 1A 9A 5A 42 08 86 27 3F 50 9A 31 62… +0,,4006,0:37.692.270,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,4007,0:37.707.276,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 7C FD 72 2A C1 77 78 98 77 83 16 09 EE 6B EA… +0,,4011,0:37.708.273,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,4012,0:37.722.278,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4016,0:37.723.275,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,4017,0:37.723.278,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 7C FD 72 2A C1 77 78 98 77 83 16 09 EE 6B EA… +0,,4021,0:37.724.275,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,4022,0:37.739.280,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC FB 15 7D FB 41 70 E8 DF 4F 87 49 CB E3 51 40… +0,,4026,0:37.740.277,14.004.750 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,4027,0:37.754.282,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4031,0:37.755.279,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,4032,0:37.755.282,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC FB 15 7D FB 41 70 E8 DF 4F 87 49 CB E3 51 40… +0,,4036,0:37.756.279,15.004.916 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,4037,0:37.771.285,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8E 1F EF AD DA C5 52 9C AC 25 B1 F5 EA FC 99 8A… +0,,4041,0:37.772.281,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,4042,0:37.786.287,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4046,0:37.787.284,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,4047,0:37.787.287,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8E 1F EF AD DA C5 52 9C AC 25 B1 F5 EA FC 99 8A… +0,,4051,0:37.788.284,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,4052,0:37.803.289,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0A 58 DF 3F 73 9A 11 8B 84 34 EF C7 60 9C DD 59… +0,,4056,0:37.804.286,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,4057,0:37.818.291,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4061,0:37.819.288,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,4062,0:37.819.291,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0A 58 DF 3F 73 9A 11 8B 84 34 EF C7 60 9C DD 59… +0,,4066,0:37.820.288,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,4067,0:37.835.293,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 29 3B 81 F5 AA E2 AA 0C 46 4F BE 3E AD 0F A0… +0,,4071,0:37.836.290,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,4072,0:37.850.296,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4076,0:37.851.292,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,4077,0:37.851.296,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7A 29 3B 81 F5 AA E2 AA 0C 46 4F BE 3E AD 0F A0… +0,,4081,0:37.852.293,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,4082,0:37.867.298,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B7 CA BF A4 CA E9 E6 1F 42 8C D2 1B D9 1F 36 52… +0,,4086,0:37.868.295,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,4087,0:37.882.300,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4091,0:37.883.297,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,4092,0:37.883.300,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B7 CA BF A4 CA E9 E6 1F 42 8C D2 1B D9 1F 36 52… +0,,4096,0:37.884.297,15.004.916 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,4097,0:37.899.302,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 28 C3 04 39 5F 69 1F 30 59 FD 7E 11 BC 82 5B F6… +0,,4101,0:37.900.299,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,4102,0:37.914.304,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4106,0:37.915.301,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,4107,0:37.915.305,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 28 C3 04 39 5F 69 1F 30 59 FD 7E 11 BC 82 5B F6… +0,,4111,0:37.916.301,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,4112,0:37.931.307,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 70 09 E5 11 05 D0 B0 43 D2 FA D4 2D E1 32 3C 1F… +0,,4116,0:37.932.304,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,4117,0:37.946.309,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4121,0:37.947.306,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,4122,0:37.947.309,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 70 09 E5 11 05 D0 B0 43 D2 FA D4 2D E1 32 3C 1F… +0,,4126,0:37.948.306,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,4127,0:37.963.311,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1F C2 73 3B 1A 28 C6 17 89 57 10 FC D4 21 61 95… +0,,4131,0:37.964.308,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,4132,0:37.978.313,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4136,0:37.979.310,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,4137,0:37.979.313,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1F C2 73 3B 1A 28 C6 17 89 57 10 FC D4 21 61 95… +0,,4141,0:37.980.310,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,4142,0:37.995.316,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9A 63 0E C2 91 0F 43 E5 C8 C8 EF 79 4A E4 07 32… +0,,4146,0:37.996.313,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,4147,0:38.010.318,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4151,0:38.011.315,16.005.041 ms,,,,,[17 SOF],[Frames: 1089 - 1105] +0,,4152,0:38.027.320,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 2B 05 C1 9F 75 4B EA D2 9F 1B 7D 6E 0C E2 0B… +0,,4156,0:38.028.317,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,4157,0:38.042.322,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4161,0:38.043.319,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,4162,0:38.043.322,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 2B 05 C1 9F 75 4B EA D2 9F 1B 7D 6E 0C E2 0B… +0,,4166,0:38.044.319,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,4167,0:38.059.325,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 EF 7A 5D 6C A8 94 BB 17 F0 D7 51 71 C0 87 23… +0,,4171,0:38.060.321,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,4172,0:38.074.327,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4176,0:38.075.323,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,4177,0:38.075.327,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 EF 7A 5D 6C A8 94 BB 17 F0 D7 51 71 C0 87 23… +0,,4181,0:38.076.324,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,4182,0:38.091.329,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 54 9A 58 82 72 82 3E 83 DE 45 6A ED 75 ED FE… +0,,4186,0:38.092.326,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,4187,0:38.106.331,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4191,0:38.107.328,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,4192,0:38.107.331,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 54 9A 58 82 72 82 3E 83 DE 45 6A ED 75 ED FE… +0,,4196,0:38.108.328,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,4197,0:38.123.333,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 FE 81 18 9B 8D AD 7B 0A 83 66 76 EA 4C E2 C8… +0,,4201,0:38.124.330,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,4202,0:38.138.335,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4206,0:38.139.332,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,4207,0:38.139.336,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 FE 81 18 9B 8D AD 7B 0A 83 66 76 EA 4C E2 C8… +0,,4211,0:38.140.333,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,4212,0:38.155.338,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 26 52 ED 4C E2 AA 2C 53 60 95 4B DF 29 7B 42… +0,,4216,0:38.156.335,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,4217,0:38.170.340,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4221,0:38.171.337,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,4222,0:38.171.340,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 26 52 ED 4C E2 AA 2C 53 60 95 4B DF 29 7B 42… +0,,4226,0:38.172.337,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,4227,0:38.187.342,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 A5 61 4E 54 64 E2 9E D2 E0 16 8E 3E 25 39 E5… +0,,4231,0:38.188.339,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,4232,0:38.202.344,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4236,0:38.203.341,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,4237,0:38.203.345,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F6 A5 61 4E 54 64 E2 9E D2 E0 16 8E 3E 25 39 E5… +0,,4241,0:38.204.341,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,4242,0:38.219.347,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB BB B2 64 AF 9A 11 E1 77 39 FB A6 DB 2A 92 2D… +0,,4246,0:38.220.344,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,4247,0:38.234.349,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4251,0:38.235.346,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,4252,0:38.235.349,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB BB B2 64 AF 9A 11 E1 77 39 FB A6 DB 2A 92 2D… +0,,4256,0:38.236.346,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,4257,0:38.251.351,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0A 85 2D B4 E2 7C 56 39 78 8B 61 01 BD A9 38 13… +0,,4261,0:38.252.348,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,4262,0:38.266.353,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4266,0:38.267.350,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,4267,0:38.267.353,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0A 85 2D B4 E2 7C 56 39 78 8B 61 01 BD A9 38 13… +0,,4271,0:38.268.350,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,4272,0:38.283.356,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EC E6 53 2B 86 71 1B 58 83 23 27 99 26 FE F0 7E… +0,,4276,0:38.284.353,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,4277,0:38.298.358,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4281,0:38.299.355,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,4282,0:38.299.358,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EC E6 53 2B 86 71 1B 58 83 23 27 99 26 FE F0 7E… +0,,4286,0:38.300.355,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,4287,0:38.315.360,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 51 DE 11 BF BC 77 07 C9 4D D1 BD 9B 70 C2 A7 41… +0,,4291,0:38.316.357,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,4292,0:38.330.362,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4296,0:38.331.359,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,4297,0:38.331.362,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 51 DE 11 BF BC 77 07 C9 4D D1 BD 9B 70 C2 A7 41… +0,,4301,0:38.332.359,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,4302,0:38.347.365,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7B 59 0A 6F DF B1 7D AD 3E 63 B9 1E D7 82 9D F5… +0,,4306,0:38.348.361,14.004.750 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,4307,0:38.362.367,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4311,0:38.363.363,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,4312,0:38.363.367,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7B 59 0A 6F DF B1 7D AD 3E 63 B9 1E D7 82 9D F5… +0,,4316,0:38.364.364,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,4317,0:38.379.369,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 11 E5 84 90 6C 33 1C D5 5D D3 BB F4 8A 34 98 BA… +0,,4321,0:38.380.366,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,4322,0:38.394.371,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4326,0:38.395.368,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,4327,0:38.395.371,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 11 E5 84 90 6C 33 1C D5 5D D3 BB F4 8A 34 98 BA… +0,,4331,0:38.396.368,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,4332,0:38.411.373,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AC A7 20 79 3F B0 45 CB AB FF 4E 5E E9 99 19 56… +0,,4336,0:38.412.370,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,4337,0:38.426.376,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4341,0:38.427.372,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,4342,0:38.427.376,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AC A7 20 79 3F B0 45 CB AB FF 4E 5E E9 99 19 56… +0,,4346,0:38.428.373,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,4347,0:38.443.378,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B ED 01 E9 AB 5A B3 55 AC FE 5A 16 A5 57 44 3A… +0,,4351,0:38.444.375,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,4352,0:38.458.380,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4356,0:38.459.377,2.833 us,,,,,[1 SOF],[Frame: 1537] +0,,4357,0:38.459.380,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B ED 01 E9 AB 5A B3 55 AC FE 5A 16 A5 57 44 3A… +0,,4361,0:38.460.377,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,4362,0:38.475.382,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC 65 FA FC 9A C0 A5 26 C6 13 37 D0 28 3D 65 FB… +0,,4366,0:38.476.379,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,4367,0:38.490.384,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4371,0:38.491.381,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,4372,0:38.491.384,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC 65 FA FC 9A C0 A5 26 C6 13 37 D0 28 3D 65 FB… +0,,4376,0:38.492.381,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,4377,0:38.507.387,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B D6 95 CE 0D 12 9D 55 69 1C CA 7B 1C A1 47 34… +0,,4381,0:38.508.384,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,4382,0:38.522.389,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4386,0:38.523.386,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,4387,0:38.523.389,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B D6 95 CE 0D 12 9D 55 69 1C CA 7B 1C A1 47 34… +0,,4391,0:38.524.386,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,4392,0:38.539.391,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F9 6A 6E DD 9E D7 C2 8B 9E 94 53 45 49 C9 4C 81… +0,,4396,0:38.540.388,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,4397,0:38.554.393,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4401,0:38.555.390,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,4402,0:38.555.393,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F9 6A 6E DD 9E D7 C2 8B 9E 94 53 45 49 C9 4C 81… +0,,4406,0:38.556.390,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,4407,0:38.571.396,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 84 D9 8F 4D C6 90 CD 03 03 52 C5 8B B9 D4 D6 37… +0,,4411,0:38.572.392,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,4412,0:38.586.398,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4416,0:38.587.395,2.833 us,,,,,[1 SOF],[Frame: 1665] +0,,4417,0:38.587.398,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 84 D9 8F 4D C6 90 CD 03 03 52 C5 8B B9 D4 D6 37… +0,,4421,0:38.588.395,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,4422,0:38.603.400,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 73 1E 4B 36 AF 33 29 D0 F3 D3 88 AE 45 E9 A2 C8… +0,,4426,0:38.604.397,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,4427,0:38.618.402,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4431,0:38.619.399,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,4432,0:38.619.402,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 73 1E 4B 36 AF 33 29 D0 F3 D3 88 AE 45 E9 A2 C8… +0,,4436,0:38.620.399,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,4437,0:38.635.404,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 DD AD 76 8C 17 66 EE 4B E0 88 41 4A EF 17 2C… +0,,4441,0:38.636.401,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,4442,0:38.650.407,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4446,0:38.651.403,16.005.041 ms,,,,,[17 SOF],[Frames: 1729 - 1745] +0,,4447,0:38.667.409,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 6C 8F 69 D2 A1 62 D6 DE 67 8F B0 61 D6 9F 50… +0,,4451,0:38.668.406,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,4452,0:38.682.411,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4456,0:38.683.408,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,4457,0:38.683.411,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 6C 8F 69 D2 A1 62 D6 DE 67 8F B0 61 D6 9F 50… +0,,4461,0:38.684.408,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,4462,0:38.699.413,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 5B DB 08 D8 F3 FD F3 22 08 A6 46 F5 B6 25 30… +0,,4466,0:38.700.410,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,4467,0:38.714.415,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4471,0:38.715.412,2.833 us,,,,,[1 SOF],[Frame: 1793] +0,,4472,0:38.715.416,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 5B DB 08 D8 F3 FD F3 22 08 A6 46 F5 B6 25 30… +0,,4476,0:38.716.412,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,4477,0:38.731.418,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 30 4D B9 A4 0E F8 C4 D9 07 45 97 08 7F 25 A1… +0,,4481,0:38.732.415,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,4482,0:38.746.420,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4486,0:38.747.417,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,4487,0:38.747.420,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 30 4D B9 A4 0E F8 C4 D9 07 45 97 08 7F 25 A1… +0,,4491,0:38.748.417,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,4492,0:38.763.422,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF 9B F6 F8 7C 2A C8 7B F5 56 A7 C4 85 5C CA 28… +0,,4496,0:38.764.419,14.004.750 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,4497,0:38.778.424,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4501,0:38.779.421,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,4502,0:38.779.424,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF 9B F6 F8 7C 2A C8 7B F5 56 A7 C4 85 5C CA 28… +0,,4506,0:38.780.421,15.004.916 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,4507,0:38.795.427,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 8B 2D 1A 6D 8A 68 11 AA EF 4C 04 25 1D D4 63… +0,,4511,0:38.796.424,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,4512,0:38.810.429,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4516,0:38.811.426,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,4517,0:38.811.429,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 8B 2D 1A 6D 8A 68 11 AA EF 4C 04 25 1D D4 63… +0,,4521,0:38.812.426,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,4522,0:38.827.431,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4F F8 DE D5 42 90 D5 9F 93 57 3D DE 44 9A 6C A2… +0,,4526,0:38.828.428,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,4527,0:38.842.433,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4531,0:38.843.430,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,4532,0:38.843.433,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4F F8 DE D5 42 90 D5 9F 93 57 3D DE 44 9A 6C A2… +0,,4536,0:38.844.430,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,4537,0:38.859.436,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C8 A8 76 AA 52 BC F5 A9 11 7A 36 C3 91 4B E3 60… +0,,4541,0:38.860.432,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,4542,0:38.874.438,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4546,0:38.875.435,2.833 us,,,,,[1 SOF],[Frame: 1953] +0,,4547,0:38.875.438,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 A8 76 AA 52 BC F5 A9 11 7A 36 C3 91 4B E3 60… +0,,4551,0:38.876.435,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,4552,0:38.891.440,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C0 D8 22 FB 12 46 EF E8 ED D3 09 A8 6A C1 DB 14… +0,,4556,0:38.892.437,14.004.750 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,4557,0:38.906.442,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4561,0:38.907.439,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,4562,0:38.907.442,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C0 D8 22 FB 12 46 EF E8 ED D3 09 A8 6A C1 DB 14… +0,,4566,0:38.908.439,15.005.000 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,4567,0:38.923.445,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB 47 70 20 FA 9C 0E CC 34 2A 7F A9 10 95 8C 4A… +0,,4571,0:38.924.441,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,4572,0:38.938.447,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4576,0:38.939.443,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,4577,0:38.939.447,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB 47 70 20 FA 9C 0E CC 34 2A 7F A9 10 95 8C 4A… +0,,4581,0:38.940.444,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,4582,0:38.955.449,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 33 2E 9B A9 5E 95 A4 E0 02 35 FA 0F 1F B1 DB 44… +0,,4586,0:38.956.446,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,4587,0:38.970.451,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4591,0:38.971.448,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,4592,0:38.971.451,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 33 2E 9B A9 5E 95 A4 E0 02 35 FA 0F 1F B1 DB 44… +0,,4596,0:38.972.448,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,4597,0:38.987.453,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 78 D0 82 EB 98 B3 E5 AA 4D 46 B8 27 A0 12 9C 1A… +0,,4601,0:38.988.450,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,4602,0:39.002.455,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4606,0:39.003.452,2.833 us,,,,,[1 SOF],[Frame: 33] +0,,4607,0:39.003.456,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 78 D0 82 EB 98 B3 E5 AA 4D 46 B8 27 A0 12 9C 1A… +0,,4611,0:39.004.452,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,4612,0:39.019.458,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 95 CE EA 7F 9F A2 53 F7 3C EE 03 E0 94 07 50 E4… +0,,4616,0:39.020.455,14.004.750 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,4617,0:39.034.460,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4621,0:39.035.457,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,4622,0:39.035.460,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 95 CE EA 7F 9F A2 53 F7 3C EE 03 E0 94 07 50 E4… +0,,4626,0:39.036.457,15.004.916 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,4627,0:39.051.462,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 1E A4 CA 4F 39 CB D9 53 C6 C3 D6 5A 89 FB 1E… +0,,4631,0:39.052.459,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,4632,0:39.066.464,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4636,0:39.067.461,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,4637,0:39.067.464,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 1E A4 CA 4F 39 CB D9 53 C6 C3 D6 5A 89 FB 1E… +0,,4641,0:39.068.461,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,4642,0:39.083.467,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 78 15 CC 2D 94 6A CF 88 A8 18 D4 A9 8F 64 31… +0,,4646,0:39.084.464,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,4647,0:39.098.469,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4651,0:39.099.466,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,4652,0:39.099.469,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 78 15 CC 2D 94 6A CF 88 A8 18 D4 A9 8F 64 31… +0,,4656,0:39.100.466,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,4657,0:39.115.471,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 78 DB 39 3E 15 D2 31 50 A3 77 B6 19 45 78 49… +0,,4661,0:39.116.468,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,4662,0:39.130.473,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4666,0:39.131.470,2.833 us,,,,,[1 SOF],[Frame: 161] +0,,4667,0:39.131.473,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 78 DB 39 3E 15 D2 31 50 A3 77 B6 19 45 78 49… +0,,4671,0:39.132.470,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,4672,0:39.147.476,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BB 16 0F 5D 2F D3 55 7F 2C F2 A9 A5 D7 08 54 5B… +0,,4676,0:39.148.472,14.004.750 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,4677,0:39.162.478,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4681,0:39.163.475,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,4682,0:39.163.478,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BB 16 0F 5D 2F D3 55 7F 2C F2 A9 A5 D7 08 54 5B… +0,,4686,0:39.164.475,15.004.916 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,4687,0:39.179.480,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 1D 76 30 65 4F 27 24 54 E6 DE 06 1A 96 11 31… +0,,4691,0:39.180.477,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,4692,0:39.194.482,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4696,0:39.195.479,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,4697,0:39.195.482,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 1D 76 30 65 4F 27 24 54 E6 DE 06 1A 96 11 31… +0,,4701,0:39.196.479,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,4702,0:39.211.484,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 94 0C A6 3E 6C 23 C2 C6 25 9D B2 4A A5 C6 1B… +0,,4706,0:39.212.481,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,4707,0:39.226.487,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4711,0:39.227.483,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,4712,0:39.227.487,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 94 0C A6 3E 6C 23 C2 C6 25 9D B2 4A A5 C6 1B… +0,,4716,0:39.228.484,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,4717,0:39.243.489,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 83 AF 13 B8 51 9E FC 9A 11 DC 96 A6 50 35 96 C4… +0,,4721,0:39.244.486,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,4722,0:39.258.491,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4726,0:39.259.488,16.005.041 ms,,,,,[17 SOF],[Frames: 289 - 305] +0,,4727,0:39.275.493,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 30 FE CF 3D 6E 4D 60 CE F0 88 9B 15 71 39 A9 57… +0,,4731,0:39.276.490,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,4732,0:39.290.495,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4736,0:39.291.492,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,4737,0:39.291.496,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 30 FE CF 3D 6E 4D 60 CE F0 88 9B 15 71 39 A9 57… +0,,4741,0:39.292.492,15.004.916 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,4742,0:39.307.498,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E 28 7A 45 1B E6 F2 9E 28 C0 A9 74 77 16 ED D0… +0,,4746,0:39.308.495,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,4747,0:39.322.500,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4751,0:39.323.497,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,4752,0:39.323.500,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E 28 7A 45 1B E6 F2 9E 28 C0 A9 74 77 16 ED D0… +0,,4756,0:39.324.497,15.004.916 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,4757,0:39.339.502,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 C8 4B E4 EB 5E DF 2C D0 71 C8 19 A2 C5 BF B2… +0,,4761,0:39.340.499,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,4762,0:39.354.504,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4766,0:39.355.501,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,4767,0:39.355.504,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 C8 4B E4 EB 5E DF 2C D0 71 C8 19 A2 C5 BF B2… +0,,4771,0:39.356.501,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,4772,0:39.371.507,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 59 4D 99 33 AF A2 8C BF 01 A1 7B EA 72 C9 12… +0,,4776,0:39.372.504,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,4777,0:39.386.509,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4781,0:39.387.506,2.833 us,,,,,[1 SOF],[Frame: 417] +0,,4782,0:39.387.509,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 59 4D 99 33 AF A2 8C BF 01 A1 7B EA 72 C9 12… +0,,4786,0:39.388.506,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,4787,0:39.403.511,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A E8 D7 1A 5E 9B B7 7C 35 AD 47 AE 40 EB 9A 6A… +0,,4791,0:39.404.508,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,4792,0:39.418.513,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4796,0:39.419.510,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,4797,0:39.419.513,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A E8 D7 1A 5E 9B B7 7C 35 AD 47 AE 40 EB 9A 6A… +0,,4801,0:39.420.510,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,4802,0:39.435.516,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 53 B0 3F D7 7B B9 AA FC A3 89 7B 53 6E 93 DD 50… +0,,4806,0:39.436.512,14.004.750 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,4807,0:39.450.518,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4811,0:39.451.515,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,4812,0:39.451.518,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 53 B0 3F D7 7B B9 AA FC A3 89 7B 53 6E 93 DD 50… +0,,4816,0:39.452.515,15.004.916 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,4817,0:39.467.520,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE 43 5D D2 96 F1 25 AF D3 2F F8 AC A9 66 50 55… +0,,4821,0:39.468.517,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,4822,0:39.482.522,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4826,0:39.483.519,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,4827,0:39.483.522,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DE 43 5D D2 96 F1 25 AF D3 2F F8 AC A9 66 50 55… +0,,4831,0:39.484.519,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,4832,0:39.499.524,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FF E3 03 24 8A E8 1B 45 42 4D B7 C5 DA 83 26 7F… +0,,4836,0:39.500.521,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,4837,0:39.514.527,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4841,0:39.515.523,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,4842,0:39.515.527,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FF E3 03 24 8A E8 1B 45 42 4D B7 C5 DA 83 26 7F… +0,,4846,0:39.516.524,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,4847,0:39.531.529,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 70 36 4D 35 6F 4A 5C 52 1D 4B B3 1B E5 03 AF 80… +0,,4851,0:39.532.526,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,4852,0:39.546.531,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4856,0:39.547.528,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,4857,0:39.547.531,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 70 36 4D 35 6F 4A 5C 52 1D 4B B3 1B E5 03 AF 80… +0,,4861,0:39.548.528,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,4862,0:39.563.533,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B0 70 03 C7 22 B8 F6 63 AD D3 68 46 09 49 7B 29… +0,,4866,0:39.564.530,14.004.750 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,4867,0:39.578.535,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4871,0:39.579.532,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,4872,0:39.579.536,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B0 70 03 C7 22 B8 F6 63 AD D3 68 46 09 49 7B 29… +0,,4876,0:39.580.532,15.004.916 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,4877,0:39.595.538,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 39 D5 36 6F 66 F6 68 57 FC 9C 98 50 79 B9 54 FF… +0,,4881,0:39.596.535,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,4882,0:39.610.540,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4886,0:39.611.537,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,4887,0:39.611.540,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 39 D5 36 6F 66 F6 68 57 FC 9C 98 50 79 B9 54 FF… +0,,4891,0:39.612.537,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,4892,0:39.627.542,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AF 8A 9B 49 C1 67 D3 E4 B1 28 3B 6B 8A 76 44 97… +0,,4896,0:39.628.539,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,4897,0:39.642.544,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4901,0:39.643.541,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,4902,0:39.643.544,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AF 8A 9B 49 C1 67 D3 E4 B1 28 3B 6B 8A 76 44 97… +0,,4906,0:39.644.541,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,4907,0:39.659.547,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F8 68 8D F9 35 C0 13 3B 96 3A AD 0B F4 0E 1B A5… +0,,4911,0:39.660.544,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,4912,0:39.674.549,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4916,0:39.675.546,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,4917,0:39.675.549,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F8 68 8D F9 35 C0 13 3B 96 3A AD 0B F4 0E 1B A5… +0,,4921,0:39.676.546,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,4922,0:39.691.551,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F 8E 8C 8E 3A 4E E4 25 AF E7 D2 11 6E 3F C0 87… +0,,4926,0:39.692.548,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,4927,0:39.706.553,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4931,0:39.707.550,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,4932,0:39.707.553,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0F 8E 8C 8E 3A 4E E4 25 AF E7 D2 11 6E 3F C0 87… +0,,4936,0:39.708.550,15.004.916 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,4937,0:39.723.556,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6E 98 1F 27 B8 FA 7C 6F 69 83 74 0F 2E E2 37 78… +0,,4941,0:39.724.552,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,4942,0:39.738.558,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4946,0:39.739.555,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,4947,0:39.739.558,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6E 98 1F 27 B8 FA 7C 6F 69 83 74 0F 2E E2 37 78… +0,,4951,0:39.740.555,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,4952,0:39.755.560,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 70 8D 35 90 2E AE C1 B9 45 63 C8 AF D1 28 8D A0… +0,,4956,0:39.756.557,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,4957,0:39.770.562,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4961,0:39.771.559,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,4962,0:39.771.562,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 70 8D 35 90 2E AE C1 B9 45 63 C8 AF D1 28 8D A0… +0,,4966,0:39.772.559,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,4967,0:39.787.564,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1E 7E 19 16 D6 66 E7 43 0F 35 EB D4 A7 0D D7 07… +0,,4971,0:39.788.561,14.004.750 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,4972,0:39.802.567,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4976,0:39.803.563,2.833 us,,,,,[1 SOF],[Frame: 833] +0,,4977,0:39.803.567,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1E 7E 19 16 D6 66 E7 43 0F 35 EB D4 A7 0D D7 07… +0,,4981,0:39.804.564,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,4982,0:39.819.569,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D 40 9A 7D BD 1E DB 21 F1 58 A9 0D 36 9E 5E 9A… +0,,4986,0:39.820.566,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,4987,0:39.834.571,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,4991,0:39.835.568,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,4992,0:39.835.571,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D 40 9A 7D BD 1E DB 21 F1 58 A9 0D 36 9E 5E 9A… +0,,4996,0:39.836.568,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,4997,0:39.851.573,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 E4 8B 27 38 43 7C 9E C8 E5 E5 4E 3A DF 98 C6… +0,,5001,0:39.852.570,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,5002,0:39.866.575,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5006,0:39.867.572,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,5007,0:39.867.576,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 E4 8B 27 38 43 7C 9E C8 E5 E5 4E 3A DF 98 C6… +0,,5011,0:39.868.572,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,5012,0:39.883.578,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 77 B6 E8 24 4F BE 68 58 C9 86 FA 40 70 30 01… +0,,5016,0:39.884.575,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,5017,0:39.898.580,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5021,0:39.899.577,16.005.041 ms,,,,,[17 SOF],[Frames: 929 - 945] +0,,5022,0:39.915.582,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 98 13 F5 E7 6A FC C1 88 24 94 49 44 E2 96 40 01… +0,,5026,0:39.916.579,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,5027,0:39.930.584,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5031,0:39.931.581,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,5032,0:39.931.584,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 98 13 F5 E7 6A FC C1 88 24 94 49 44 E2 96 40 01… +0,,5036,0:39.932.581,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,5037,0:39.947.587,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC BD 70 AB 5F 56 8B 70 DC AF AD CC 43 8D 6A 10… +0,,5041,0:39.948.584,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,5042,0:39.962.589,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5046,0:39.963.586,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,5047,0:39.963.589,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC BD 70 AB 5F 56 8B 70 DC AF AD CC 43 8D 6A 10… +0,,5051,0:39.964.586,15.005.000 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,5052,0:39.979.591,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 29 E1 3C 14 9D 2F 6D AE 60 B7 AA 26 75 7F 84… +0,,5056,0:39.980.588,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,5057,0:39.994.593,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5061,0:39.995.590,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,5062,0:39.995.593,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 29 E1 3C 14 9D 2F 6D AE 60 B7 AA 26 75 7F 84… +0,,5066,0:39.996.590,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,5067,0:40.011.596,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 7F E7 E6 E8 9D F9 05 09 66 C7 6C 11 E8 9A 54… +0,,5071,0:40.012.592,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,5072,0:40.026.598,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5076,0:40.027.594,2.833 us,,,,,[1 SOF],[Frame: 1057] +0,,5077,0:40.027.598,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 7F E7 E6 E8 9D F9 05 09 66 C7 6C 11 E8 9A 54… +0,,5081,0:40.028.595,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,5082,0:40.043.600,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 09 26 42 2F 9C C9 0F E8 D7 03 CD EC 8B 8B 6E E5… +0,,5086,0:40.044.597,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,5087,0:40.058.602,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5091,0:40.059.599,2.833 us,,,,,[1 SOF],[Frame: 1089] +0,,5092,0:40.059.602,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 09 26 42 2F 9C C9 0F E8 D7 03 CD EC 8B 8B 6E E5… +0,,5096,0:40.060.599,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,5097,0:40.075.604,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC 0B 6E C2 D2 01 EE 88 E7 21 B3 2A CB AB 1E 41… +0,,5101,0:40.076.601,14.004.750 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,5102,0:40.090.606,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5106,0:40.091.603,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,5107,0:40.091.607,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC 0B 6E C2 D2 01 EE 88 E7 21 B3 2A CB AB 1E 41… +0,,5111,0:40.092.604,15.004.916 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,5112,0:40.107.609,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0A 2D 5A 07 96 00 75 EA DA 3A 7A 8E 88 74 FB CD… +0,,5116,0:40.108.606,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,5117,0:40.122.611,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5121,0:40.123.608,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,5122,0:40.123.611,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0A 2D 5A 07 96 00 75 EA DA 3A 7A 8E 88 74 FB CD… +0,,5126,0:40.124.608,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,5127,0:40.139.613,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 EC 1C 1E CA 9C D9 E7 AC 82 AA FF 44 1F ED 32… +0,,5131,0:40.140.610,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,5132,0:40.154.615,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5136,0:40.155.612,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,5137,0:40.155.616,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 EC 1C 1E CA 9C D9 E7 AC 82 AA FF 44 1F ED 32… +0,,5141,0:40.156.612,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,5142,0:40.171.618,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD 61 08 79 AB 53 20 52 90 53 30 3B 64 A2 AB 87… +0,,5146,0:40.172.615,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,5147,0:40.186.620,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5151,0:40.187.617,2.833 us,,,,,[1 SOF],[Frame: 1217] +0,,5152,0:40.187.620,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD 61 08 79 AB 53 20 52 90 53 30 3B 64 A2 AB 87… +0,,5156,0:40.188.617,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,5157,0:40.203.622,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AF FF 04 2C 9E C5 09 A0 A4 D6 DA 2C E8 00 B8 7F… +0,,5161,0:40.204.619,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,5162,0:40.218.624,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5166,0:40.219.621,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,5167,0:40.219.624,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AF FF 04 2C 9E C5 09 A0 A4 D6 DA 2C E8 00 B8 7F… +0,,5171,0:40.220.621,15.004.916 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,5172,0:40.235.627,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 76 1E 23 A0 8C 27 6F 92 EF 46 85 24 B3 5D D9… +0,,5176,0:40.236.624,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,5177,0:40.250.629,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5181,0:40.251.626,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,5182,0:40.251.629,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 76 1E 23 A0 8C 27 6F 92 EF 46 85 24 B3 5D D9… +0,,5186,0:40.252.626,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,5187,0:40.267.631,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 46 B2 22 0E 4E 74 E7 34 DE EF A2 3C 9D F4 21… +0,,5191,0:40.268.628,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,5192,0:40.282.633,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5196,0:40.283.630,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,5197,0:40.283.633,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 46 B2 22 0E 4E 74 E7 34 DE EF A2 3C 9D F4 21… +0,,5201,0:40.284.630,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,5202,0:40.299.636,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D4 73 34 48 8C 7A FB 3F 77 3B 35 94 4D 62 6F 4F… +0,,5206,0:40.300.632,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,5207,0:40.314.638,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5211,0:40.315.634,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,5212,0:40.315.638,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D4 73 34 48 8C 7A FB 3F 77 3B 35 94 4D 62 6F 4F… +0,,5216,0:40.316.635,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,5217,0:40.331.640,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C C7 C9 93 A2 06 8C 07 22 13 44 C0 DE 59 16 F5… +0,,5221,0:40.332.637,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,5222,0:40.346.642,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5226,0:40.347.639,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,5227,0:40.347.642,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C C7 C9 93 A2 06 8C 07 22 13 44 C0 DE 59 16 F5… +0,,5231,0:40.348.639,15.004.916 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,5232,0:40.363.644,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 5B BC 0E 87 29 19 7A 52 03 B3 BF 85 30 91 B1… +0,,5236,0:40.364.641,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,5237,0:40.378.646,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5241,0:40.379.643,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,5242,0:40.379.647,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 5B BC 0E 87 29 19 7A 52 03 B3 BF 85 30 91 B1… +0,,5246,0:40.380.644,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,5247,0:40.395.649,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E9 FD 3A 89 B6 92 25 E0 FC 06 F6 50 DE D5 46 CE… +0,,5251,0:40.396.646,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,5252,0:40.410.651,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5256,0:40.411.648,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,5257,0:40.411.651,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E9 FD 3A 89 B6 92 25 E0 FC 06 F6 50 DE D5 46 CE… +0,,5261,0:40.412.648,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,5262,0:40.427.653,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E F9 0E 83 46 03 1A 86 E5 D4 74 2B D8 19 B0 F2… +0,,5266,0:40.428.650,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,5267,0:40.442.655,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5271,0:40.443.652,2.833 us,,,,,[1 SOF],[Frame: 1473] +0,,5272,0:40.443.655,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E F9 0E 83 46 03 1A 86 E5 D4 74 2B D8 19 B0 F2… +0,,5276,0:40.444.652,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,5277,0:40.459.658,50.916 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F A1 D4 D6 A4 93 9F 7A 4B F2 FE 57 86 0B B6 19… +0,,5281,0:40.460.655,14.004.833 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,5282,0:40.474.660,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5286,0:40.475.657,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,5287,0:40.475.660,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F A1 D4 D6 A4 93 9F 7A 4B F2 FE 57 86 0B B6 19… +0,,5291,0:40.476.657,15.004.916 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,5292,0:40.491.662,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 E7 CD 40 70 16 D3 97 8D B6 9E F8 F7 32 26 78… +0,,5296,0:40.492.659,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,5297,0:40.506.664,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5301,0:40.507.661,16.005.125 ms,,,,,[17 SOF],[Frames: 1537 - 1553] +0,,5302,0:40.523.667,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 D4 31 1A E6 5A 78 D3 E1 EE 69 92 53 2A 58 93… +0,,5306,0:40.524.663,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,5307,0:40.538.669,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5311,0:40.539.666,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,5312,0:40.539.669,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 D4 31 1A E6 5A 78 D3 E1 EE 69 92 53 2A 58 93… +0,,5316,0:40.540.666,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,5317,0:40.555.671,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D1 3F 29 CF FE 48 4B B5 BE E0 BA 9D B3 DA 72 54… +0,,5321,0:40.556.668,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,5322,0:40.570.673,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5326,0:40.571.670,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,5327,0:40.571.673,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D1 3F 29 CF FE 48 4B B5 BE E0 BA 9D B3 DA 72 54… +0,,5331,0:40.572.670,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,5332,0:40.587.675,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1A F1 7F 68 52 EB 7C 44 57 47 36 36 05 1A D8 29… +0,,5336,0:40.588.672,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,5337,0:40.602.678,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5341,0:40.603.674,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,5342,0:40.603.678,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1A F1 7F 68 52 EB 7C 44 57 47 36 36 05 1A D8 29… +0,,5346,0:40.604.675,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,5347,0:40.619.680,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 49 98 44 70 0C 4F BD FB 20 50 29 BD A0 6C BA C6… +0,,5351,0:40.620.677,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,5352,0:40.634.682,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5356,0:40.635.679,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,5357,0:40.635.682,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 49 98 44 70 0C 4F BD FB 20 50 29 BD A0 6C BA C6… +0,,5361,0:40.636.679,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,5362,0:40.651.684,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 DC BF E8 8A B9 39 AA 6D 7F 48 56 8B F1 5E AF… +0,,5366,0:40.652.681,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,5367,0:40.666.686,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5371,0:40.667.683,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,5372,0:40.667.687,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 76 DC BF E8 8A B9 39 AA 6D 7F 48 56 8B F1 5E AF… +0,,5376,0:40.668.683,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,5377,0:40.683.689,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3D A8 43 96 82 18 70 4D 5A AA 2B 21 0D 56 D9 01… +0,,5381,0:40.684.686,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,5382,0:40.698.691,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5386,0:40.699.688,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,5387,0:40.699.691,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3D A8 43 96 82 18 70 4D 5A AA 2B 21 0D 56 D9 01… +0,,5391,0:40.700.688,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,5392,0:40.715.693,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E5 C7 E3 B6 B1 DF 6B E4 0A 21 08 F4 27 57 B5 7C… +0,,5396,0:40.716.690,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,5397,0:40.730.695,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5401,0:40.731.692,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,5402,0:40.731.696,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 C7 E3 B6 B1 DF 6B E4 0A 21 08 F4 27 57 B5 7C… +0,,5406,0:40.732.692,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,5407,0:40.747.698,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A 74 AC 2D C6 A2 FA 4B D9 19 A5 00 DE 64 37 27… +0,,5411,0:40.748.695,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,5412,0:40.762.700,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5416,0:40.763.697,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,5417,0:40.763.700,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A 74 AC 2D C6 A2 FA 4B D9 19 A5 00 DE 64 37 27… +0,,5421,0:40.764.697,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,5422,0:40.779.702,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 15 16 7D 4A 28 61 09 8C 39 EF 94 34 E8 0E A7 94… +0,,5426,0:40.780.699,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,5427,0:40.794.704,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5431,0:40.795.701,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,5432,0:40.795.704,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 15 16 7D 4A 28 61 09 8C 39 EF 94 34 E8 0E A7 94… +0,,5436,0:40.796.701,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,5437,0:40.811.707,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3C 03 3E CD E3 2C AD 47 B0 1D D3 53 B2 E4 16 A1… +0,,5441,0:40.812.703,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,5442,0:40.826.709,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5446,0:40.827.706,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,5447,0:40.827.709,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3C 03 3E CD E3 2C AD 47 B0 1D D3 53 B2 E4 16 A1… +0,,5451,0:40.828.706,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,5452,0:40.843.711,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 17 38 82 03 0D 8C 06 FC AF 3E 10 39 0E 4D 01… +0,,5456,0:40.844.708,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,5457,0:40.858.713,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5461,0:40.859.710,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,5462,0:40.859.713,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 17 38 82 03 0D 8C 06 FC AF 3E 10 39 0E 4D 01… +0,,5466,0:40.860.710,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,5467,0:40.875.715,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 69 0F 13 54 CC 46 D7 6E C9 32 AA 25 A5 56 2C 3C… +0,,5471,0:40.876.712,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,5472,0:40.890.718,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5476,0:40.891.714,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,5477,0:40.891.718,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 69 0F 13 54 CC 46 D7 6E C9 32 AA 25 A5 56 2C 3C… +0,,5481,0:40.892.715,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,5482,0:40.907.720,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC 9B 99 FF E5 FA E8 FC E1 CA C8 42 EA 69 2E 9E… +0,,5486,0:40.908.717,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,5487,0:40.922.722,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5491,0:40.923.719,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,5492,0:40.923.722,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC 9B 99 FF E5 FA E8 FC E1 CA C8 42 EA 69 2E 9E… +0,,5496,0:40.924.719,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,5497,0:40.939.724,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4A 12 C7 B7 09 B9 1E 6D 37 E8 5F 87 18 56 54 61… +0,,5501,0:40.940.721,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,5502,0:40.954.726,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5506,0:40.955.723,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,5507,0:40.955.727,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4A 12 C7 B7 09 B9 1E 6D 37 E8 5F 87 18 56 54 61… +0,,5511,0:40.956.723,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,5512,0:40.971.729,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C2 12 2E 5A 0F 5F EA 8D 2D 90 EC 65 12 2B 7C B6… +0,,5516,0:40.972.726,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,5517,0:40.986.731,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5521,0:40.987.728,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,5522,0:40.987.731,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C2 12 2E 5A 0F 5F EA 8D 2D 90 EC 65 12 2B 7C B6… +0,,5526,0:40.988.728,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,5527,0:41.003.733,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F4 4E 98 A2 2C 4C 43 34 0B 4E EF E8 C2 85 8A 06… +0,,5531,0:41.004.730,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,5532,0:41.018.735,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5536,0:41.019.732,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,5537,0:41.019.735,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F4 4E 98 A2 2C 4C 43 34 0B 4E EF E8 C2 85 8A 06… +0,,5541,0:41.020.732,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,5542,0:41.035.738,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D8 9F 33 EE 23 FA BD 25 A7 21 0F EB 47 CC 29 8C… +0,,5546,0:41.036.735,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,5547,0:41.050.740,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5551,0:41.051.737,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,5552,0:41.051.740,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D8 9F 33 EE 23 FA BD 25 A7 21 0F EB 47 CC 29 8C… +0,,5556,0:41.052.737,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,5557,0:41.067.742,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C2 F8 A8 C7 98 F5 01 4C EC 5A 11 F0 FB 0D 3D 6A… +0,,5561,0:41.068.739,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,5562,0:41.082.744,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5566,0:41.083.741,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,5567,0:41.083.744,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C2 F8 A8 C7 98 F5 01 4C EC 5A 11 F0 FB 0D 3D 6A… +0,,5571,0:41.084.741,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,5572,0:41.099.747,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4D 7F B2 25 D3 57 0F 97 76 B0 B4 86 CC 73 04 7F… +0,,5576,0:41.100.743,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,5577,0:41.114.749,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5581,0:41.115.746,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,5582,0:41.115.749,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D 7F B2 25 D3 57 0F 97 76 B0 B4 86 CC 73 04 7F… +0,,5586,0:41.116.746,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,5587,0:41.131.751,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4B 2B FE 5F C7 06 79 EC F6 DB DD 6B 3D 86 B3 52… +0,,5591,0:41.132.748,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,5592,0:41.146.753,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5596,0:41.147.750,16.005.041 ms,,,,,[17 SOF],[Frames: 129 - 145] +0,,5597,0:41.163.755,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4B 2B FE 5F C7 06 79 EC F6 DB DD 6B 3D 86 B3 52… +0,,5601,0:41.164.752,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,5602,0:41.178.758,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5606,0:41.179.754,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,5607,0:41.179.758,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4B 2B FE 5F C7 06 79 EC F6 DB DD 6B 3D 86 B3 52… +0,,5611,0:41.180.755,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,5612,0:41.195.760,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC 5A 18 7B 79 D3 2C CA 33 B5 A7 40 85 1A A0 43… +0,,5616,0:41.196.757,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,5617,0:41.210.762,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5621,0:41.211.759,2.833 us,,,,,[1 SOF],[Frame: 193] +0,,5622,0:41.211.762,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC 5A 18 7B 79 D3 2C CA 33 B5 A7 40 85 1A A0 43… +0,,5626,0:41.212.759,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,5627,0:41.227.764,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 03 F9 9D 33 1E 22 70 D6 CD FB 29 92 92 20 96 6A… +0,,5631,0:41.228.761,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,5632,0:41.242.766,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5636,0:41.243.763,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,5637,0:41.243.767,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 03 F9 9D 33 1E 22 70 D6 CD FB 29 92 92 20 96 6A… +0,,5641,0:41.244.763,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,5642,0:41.259.769,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 53 EA 8F AE B0 1D 88 C5 8A 6C 7E 8B 02 A7 4B A3… +0,,5646,0:41.260.766,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,5647,0:41.274.771,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5651,0:41.275.768,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,5652,0:41.275.771,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 53 EA 8F AE B0 1D 88 C5 8A 6C 7E 8B 02 A7 4B A3… +0,,5656,0:41.276.768,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,5657,0:41.291.773,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D 07 EC 7B C5 C9 EC 70 56 E7 3C 61 C6 F1 FD 75… +0,,5661,0:41.292.770,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,5662,0:41.306.775,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5666,0:41.307.772,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,5667,0:41.307.775,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D 07 EC 7B C5 C9 EC 70 56 E7 3C 61 C6 F1 FD 75… +0,,5671,0:41.308.772,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,5672,0:41.323.778,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 4F E9 7D BA 92 E7 7D C8 AE F9 32 2E A9 E1 44… +0,,5676,0:41.324.775,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,5677,0:41.338.780,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5681,0:41.339.777,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,5682,0:41.339.780,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 4F E9 7D BA 92 E7 7D C8 AE F9 32 2E A9 E1 44… +0,,5686,0:41.340.777,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,5687,0:41.355.782,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CA DB 79 79 07 7F 71 45 5E F4 63 4C D8 E7 C9 A7… +0,,5691,0:41.356.779,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,5692,0:41.370.784,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5696,0:41.371.781,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,5697,0:41.371.784,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CA DB 79 79 07 7F 71 45 5E F4 63 4C D8 E7 C9 A7… +0,,5701,0:41.372.781,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,5702,0:41.387.787,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 E3 B9 C7 DB 0F 59 AE A3 87 86 5F 27 BC EE 04… +0,,5706,0:41.388.783,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,5707,0:41.402.789,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5711,0:41.403.786,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,5712,0:41.403.789,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 E3 B9 C7 DB 0F 59 AE A3 87 86 5F 27 BC EE 04… +0,,5716,0:41.404.786,15.004.916 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,5717,0:41.419.791,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7E C6 C5 55 80 30 5F C0 5E 76 93 BF 7B 00 C3 31… +0,,5721,0:41.420.788,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,5722,0:41.434.793,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5726,0:41.435.790,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,5727,0:41.435.793,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7E C6 C5 55 80 30 5F C0 5E 76 93 BF 7B 00 C3 31… +0,,5731,0:41.436.790,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,5732,0:41.451.795,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 2A 38 E2 E0 09 9E 16 0F B5 B8 44 88 AB EC 52… +0,,5736,0:41.452.792,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,5737,0:41.466.798,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5741,0:41.467.794,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,5742,0:41.467.798,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F 2A 38 E2 E0 09 9E 16 0F B5 B8 44 88 AB EC 52… +0,,5746,0:41.468.795,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,5747,0:41.483.800,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BE 37 1B 35 3A 46 7A 1F 62 B9 FD AE 37 BD 2E EF… +0,,5751,0:41.484.797,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,5752,0:41.498.802,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5756,0:41.499.799,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,5757,0:41.499.802,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BE 37 1B 35 3A 46 7A 1F 62 B9 FD AE 37 BD 2E EF… +0,,5761,0:41.500.799,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,5762,0:41.515.804,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 E6 6D 0D 3A AA 20 7A 11 03 A8 8C 25 86 A3 36… +0,,5766,0:41.516.801,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,5767,0:41.530.806,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5771,0:41.531.803,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,5772,0:41.531.807,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 E6 6D 0D 3A AA 20 7A 11 03 A8 8C 25 86 A3 36… +0,,5776,0:41.532.803,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,5777,0:41.547.809,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C4 A5 42 56 93 25 13 8B F1 AC 8B 59 62 66 17 56… +0,,5781,0:41.548.806,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,5782,0:41.562.811,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5786,0:41.563.808,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,5787,0:41.563.811,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C4 A5 42 56 93 25 13 8B F1 AC 8B 59 62 66 17 56… +0,,5791,0:41.564.808,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,5792,0:41.579.813,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 89 BE F3 C6 B9 08 41 B7 A9 23 88 EF 6C 07 5E 6F… +0,,5796,0:41.580.810,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,5797,0:41.594.815,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5801,0:41.595.812,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,5802,0:41.595.815,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 89 BE F3 C6 B9 08 41 B7 A9 23 88 EF 6C 07 5E 6F… +0,,5806,0:41.596.812,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,5807,0:41.611.818,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 A4 83 2A 0E 46 17 B9 9D 22 48 B1 0B 8D F8 9C… +0,,5811,0:41.612.815,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,5812,0:41.626.820,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5816,0:41.627.817,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,5817,0:41.627.820,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 22 A4 83 2A 0E 46 17 B9 9D 22 48 B1 0B 8D F8 9C… +0,,5821,0:41.628.817,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,5822,0:41.643.822,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 08 F8 B0 FD AB 0C 7A 68 D2 BA B1 44 57 12 2A… +0,,5826,0:41.644.819,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,5827,0:41.658.824,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5831,0:41.659.821,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,5832,0:41.659.824,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 08 F8 B0 FD AB 0C 7A 68 D2 BA B1 44 57 12 2A… +0,,5836,0:41.660.821,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,5837,0:41.675.827,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CA BF 26 7E F4 B5 F0 1F EE 8B A8 A7 CD EA FB C4… +0,,5841,0:41.676.823,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,5842,0:41.690.829,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5846,0:41.691.826,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,5847,0:41.691.829,50.729 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CA BF 26 7E F4 B5 F0 1F EE 8B A8 A7 CD EA FB C4… +0,,5851,0:41.692.826,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,5852,0:41.707.831,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CC 96 8C 90 E9 C7 F5 38 35 3E 51 95 5E F6 1B 94… +0,,5856,0:41.708.828,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,5857,0:41.722.833,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5861,0:41.723.830,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,5862,0:41.723.833,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CC 96 8C 90 E9 C7 F5 38 35 3E 51 95 5E F6 1B 94… +0,,5866,0:41.724.830,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,5867,0:41.739.835,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 AF 88 95 89 2F 03 63 D2 E7 24 1E 7A 15 44 8E… +0,,5871,0:41.740.832,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,5872,0:41.754.838,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5876,0:41.755.834,16.005.041 ms,,,,,[17 SOF],[Frames: 737 - 753] +0,,5877,0:41.771.840,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 DA 25 45 05 F6 CA A2 15 B9 88 80 A3 2F DF B1… +0,,5881,0:41.772.837,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,5882,0:41.786.842,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5886,0:41.787.839,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,5887,0:41.787.842,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 DA 25 45 05 F6 CA A2 15 B9 88 80 A3 2F DF B1… +0,,5891,0:41.788.839,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,5892,0:41.803.844,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3D 1A 95 52 65 03 7D A7 31 43 35 32 6E 25 24 7D… +0,,5896,0:41.804.841,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,5897,0:41.818.846,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5901,0:41.819.843,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,5902,0:41.819.847,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3D 1A 95 52 65 03 7D A7 31 43 35 32 6E 25 24 7D… +0,,5906,0:41.820.843,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,5907,0:41.835.849,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 15 17 5E AE 13 27 28 F8 96 20 4E 60 6B FB B2 70… +0,,5911,0:41.836.846,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,5912,0:41.850.851,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5916,0:41.851.848,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,5917,0:41.851.851,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 15 17 5E AE 13 27 28 F8 96 20 4E 60 6B FB B2 70… +0,,5921,0:41.852.848,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,5922,0:41.867.853,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A1 C7 ED 1F EF 52 61 4D 34 30 D3 63 63 F6 E4 8C… +0,,5926,0:41.868.850,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,5927,0:41.882.855,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5931,0:41.883.852,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,5932,0:41.883.855,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A1 C7 ED 1F EF 52 61 4D 34 30 D3 63 63 F6 E4 8C… +0,,5936,0:41.884.852,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,5937,0:41.899.858,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 72 3C 08 53 62 43 5B 51 2A C4 AF E0 7A 6B 43… +0,,5941,0:41.900.855,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,5942,0:41.914.860,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5946,0:41.915.857,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,5947,0:41.915.860,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 72 3C 08 53 62 43 5B 51 2A C4 AF E0 7A 6B 43… +0,,5951,0:41.916.857,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,5952,0:41.931.862,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1E 09 5F 89 1E C0 05 F4 B4 CD D2 EF B4 3A E4 65… +0,,5956,0:41.932.859,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,5957,0:41.946.864,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5961,0:41.947.861,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,5962,0:41.947.864,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1E 09 5F 89 1E C0 05 F4 B4 CD D2 EF B4 3A E4 65… +0,,5966,0:41.948.861,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,5967,0:41.963.867,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 BD CA E2 7C 9C ED 2B 52 AF A7 C1 D8 01 18 82… +0,,5971,0:41.964.863,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,5972,0:41.978.869,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5976,0:41.979.865,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,5977,0:41.979.869,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F6 BD CA E2 7C 9C ED 2B 52 AF A7 C1 D8 01 18 82… +0,,5981,0:41.980.866,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,5982,0:41.995.871,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 18 2A 32 F7 28 94 19 29 C3 E7 F2 DB FB E7 F5… +0,,5986,0:41.996.868,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,5987,0:42.010.873,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,5991,0:42.011.870,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,5992,0:42.011.873,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7A 18 2A 32 F7 28 94 19 29 C3 E7 F2 DB FB E7 F5… +0,,5996,0:42.012.870,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,5997,0:42.027.875,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E 39 8F 44 27 97 B0 DB 78 EF 6B 90 78 E4 6A BB… +0,,6001,0:42.028.872,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,6002,0:42.042.877,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6006,0:42.043.874,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,6007,0:42.043.878,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E 39 8F 44 27 97 B0 DB 78 EF 6B 90 78 E4 6A BB… +0,,6011,0:42.044.875,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,6012,0:42.059.880,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 DA CF 22 79 58 FB D5 09 FC 92 CC EE 86 9C B1… +0,,6016,0:42.060.877,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,6017,0:42.074.882,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6021,0:42.075.879,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,6022,0:42.075.882,50.729 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 DA CF 22 79 58 FB D5 09 FC 92 CC EE 86 9C B1… +0,,6026,0:42.076.879,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,6027,0:42.091.884,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 73 8A A0 FB E8 5A EE F3 3E F8 E9 AE BE 48 90 A8… +0,,6031,0:42.092.881,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,6032,0:42.106.886,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6036,0:42.107.883,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,6037,0:42.107.887,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 73 8A A0 FB E8 5A EE F3 3E F8 E9 AE BE 48 90 A8… +0,,6041,0:42.108.883,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,6042,0:42.123.889,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 8B 55 1C B2 14 21 46 C1 E4 74 B8 73 6F B1 4F… +0,,6046,0:42.124.886,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,6047,0:42.138.891,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6051,0:42.139.888,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,6052,0:42.139.891,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 8B 55 1C B2 14 21 46 C1 E4 74 B8 73 6F B1 4F… +0,,6056,0:42.140.888,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,6057,0:42.155.893,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 52 1A FF 71 8D 9B E9 6B A9 41 37 DB DD E4 BC DC… +0,,6061,0:42.156.890,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,6062,0:42.170.895,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6066,0:42.171.892,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,6067,0:42.171.895,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 52 1A FF 71 8D 9B E9 6B A9 41 37 DB DD E4 BC DC… +0,,6071,0:42.172.892,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,6072,0:42.187.898,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C7 A6 C6 E6 15 40 FF C1 A4 9E 09 2B 5F 20 66 73… +0,,6076,0:42.188.895,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,6077,0:42.202.900,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6081,0:42.203.897,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,6082,0:42.203.900,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C7 A6 C6 E6 15 40 FF C1 A4 9E 09 2B 5F 20 66 73… +0,,6086,0:42.204.897,15.004.916 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,6087,0:42.219.902,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A 2C 73 C1 6C 5F C5 81 D2 AB 05 2F EC 40 E1 4D… +0,,6091,0:42.220.899,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,6092,0:42.234.904,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6096,0:42.235.901,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,6097,0:42.235.904,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A 2C 73 C1 6C 5F C5 81 D2 AB 05 2F EC 40 E1 4D… +0,,6101,0:42.236.901,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,6102,0:42.251.907,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3A E8 BB 93 4A 78 0A EA 9E 28 E9 20 18 C0 B7 07… +0,,6106,0:42.252.903,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,6107,0:42.266.909,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6111,0:42.267.905,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,6112,0:42.267.909,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3A E8 BB 93 4A 78 0A EA 9E 28 E9 20 18 C0 B7 07… +0,,6116,0:42.268.906,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,6117,0:42.283.911,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 C2 EE 6E 63 AD 46 81 BE 41 58 B1 F3 83 E8 C0… +0,,6121,0:42.284.908,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,6122,0:42.298.913,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6126,0:42.299.910,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,6127,0:42.299.913,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 C2 EE 6E 63 AD 46 81 BE 41 58 B1 F3 83 E8 C0… +0,,6131,0:42.300.910,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,6132,0:42.315.915,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 56 06 50 AF CE 53 D0 53 0F C2 AC 7B 8D EC 95 CE… +0,,6136,0:42.316.912,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,6137,0:42.330.917,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6141,0:42.331.914,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,6142,0:42.331.918,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 56 06 50 AF CE 53 D0 53 0F C2 AC 7B 8D EC 95 CE… +0,,6146,0:42.332.914,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,6147,0:42.347.920,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 DD C8 EB 92 7F 8F BB 43 BD 96 80 D9 4A 18 13… +0,,6151,0:42.348.917,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,6152,0:42.362.922,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6156,0:42.363.919,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,6157,0:42.363.922,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B5 DD C8 EB 92 7F 8F BB 43 BD 96 80 D9 4A 18 13… +0,,6161,0:42.364.919,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,6162,0:42.379.924,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 60 02 1E 0C 61 93 28 BB B0 BA 83 B8 02 1A DB E1… +0,,6166,0:42.380.921,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,6167,0:42.394.926,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6171,0:42.395.923,16.005.041 ms,,,,,[17 SOF],[Frames: 1377 - 1393] +0,,6172,0:42.411.929,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 60 02 1E 0C 61 93 28 BB B0 BA 83 B8 02 1A DB E1… +0,,6176,0:42.412.926,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,6177,0:42.426.931,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6181,0:42.427.928,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,6182,0:42.427.931,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 60 02 1E 0C 61 93 28 BB B0 BA 83 B8 02 1A DB E1… +0,,6186,0:42.428.928,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,6187,0:42.443.933,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F 42 29 42 9F F5 3C 02 31 14 5C 96 11 A6 AE B1… +0,,6191,0:42.444.930,14.004.750 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,6192,0:42.458.935,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6196,0:42.459.932,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,6197,0:42.459.935,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0F 42 29 42 9F F5 3C 02 31 14 5C 96 11 A6 AE B1… +0,,6201,0:42.460.932,15.004.916 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,6202,0:42.475.938,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 B3 4B 3E 4D 30 3E 79 30 26 5F F0 24 18 BC 00… +0,,6206,0:42.476.934,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,6207,0:42.490.940,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6211,0:42.491.937,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,6212,0:42.491.940,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 B3 4B 3E 4D 30 3E 79 30 26 5F F0 24 18 BC 00… +0,,6216,0:42.492.937,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,6217,0:42.507.942,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4D 03 40 E6 3C 7D F6 3F 11 83 F6 78 AD 3B 70 58… +0,,6221,0:42.508.939,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,6222,0:42.522.944,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6226,0:42.523.941,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,6227,0:42.523.944,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D 03 40 E6 3C 7D F6 3F 11 83 F6 78 AD 3B 70 58… +0,,6231,0:42.524.941,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,6232,0:42.539.946,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DB E8 59 D2 89 3A E5 0E 6A 2F 91 1C F4 F7 B2 DE… +0,,6236,0:42.540.943,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,6237,0:42.554.949,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6241,0:42.555.945,2.833 us,,,,,[1 SOF],[Frame: 1537] +0,,6242,0:42.555.949,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DB E8 59 D2 89 3A E5 0E 6A 2F 91 1C F4 F7 B2 DE… +0,,6246,0:42.556.946,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,6247,0:42.571.951,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C5 CB BA FA 44 65 02 BC A7 72 77 DE 3F 44 EF AE… +0,,6251,0:42.572.948,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,6252,0:42.586.953,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6256,0:42.587.950,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,6257,0:42.587.953,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C5 CB BA FA 44 65 02 BC A7 72 77 DE 3F 44 EF AE… +0,,6261,0:42.588.950,15.004.916 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,6262,0:42.603.955,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 54 AC C6 1E E1 19 FC 70 45 32 52 21 9E E6 E8 65… +0,,6266,0:42.604.952,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,6267,0:42.618.957,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6271,0:42.619.954,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,6272,0:42.619.958,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 54 AC C6 1E E1 19 FC 70 45 32 52 21 9E E6 E8 65… +0,,6276,0:42.620.954,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,6277,0:42.635.960,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 5E DB C5 5C B7 58 3B DE EB 7D D1 10 B6 59 DB… +0,,6281,0:42.636.957,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,6282,0:42.650.962,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6286,0:42.651.959,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,6287,0:42.651.962,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 5E DB C5 5C B7 58 3B DE EB 7D D1 10 B6 59 DB… +0,,6291,0:42.652.959,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,6292,0:42.667.964,50.812 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C7 8A 79 B7 74 4E 5C F0 8D 0E 91 90 F8 AB 0D 06… +0,,6296,0:42.668.961,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,6297,0:42.682.966,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6301,0:42.683.963,2.833 us,,,,,[1 SOF],[Frame: 1665] +0,,6302,0:42.683.966,50.854 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C7 8A 79 B7 74 4E 5C F0 8D 0E 91 90 F8 AB 0D 06… +0,,6306,0:42.684.963,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,6307,0:42.699.969,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 EA 5D C7 54 B9 7A D7 60 17 2B 2E 1F F6 D8 2B… +0,,6311,0:42.700.966,14.004.750 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,6312,0:42.714.971,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6316,0:42.715.968,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,6317,0:42.715.971,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 EA 5D C7 54 B9 7A D7 60 17 2B 2E 1F F6 D8 2B… +0,,6321,0:42.716.968,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,6322,0:42.731.973,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 2B 22 5E 38 AA 43 97 B4 C7 78 25 EA 95 05 91… +0,,6326,0:42.732.970,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,6327,0:42.746.975,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6331,0:42.747.972,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,6332,0:42.747.975,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 2B 22 5E 38 AA 43 97 B4 C7 78 25 EA 95 05 91… +0,,6336,0:42.748.972,15.004.916 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,6337,0:42.763.978,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CF E2 31 C6 B0 D7 B9 CE 64 C2 EC A2 6C AC D1 80… +0,,6341,0:42.764.974,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,6342,0:42.778.980,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6346,0:42.779.977,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,6347,0:42.779.980,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CF E2 31 C6 B0 D7 B9 CE 64 C2 EC A2 6C AC D1 80… +0,,6351,0:42.780.977,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,6352,0:42.795.982,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 B2 A2 AB 90 51 01 AF A9 56 4D 4E 87 47 CD 67… +0,,6356,0:42.796.979,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,6357,0:42.810.984,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6361,0:42.811.981,2.833 us,,,,,[1 SOF],[Frame: 1793] +0,,6362,0:42.811.984,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 B2 A2 AB 90 51 01 AF A9 56 4D 4E 87 47 CD 67… +0,,6366,0:42.812.981,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,6367,0:42.827.986,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8F 9F CF 3A 43 79 A1 93 12 9E 02 A9 89 17 D6 96… +0,,6371,0:42.828.983,14.004.750 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,6372,0:42.842.989,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6376,0:42.843.985,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,6377,0:42.843.989,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8F 9F CF 3A 43 79 A1 93 12 9E 02 A9 89 17 D6 96… +0,,6381,0:42.844.986,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,6382,0:42.859.991,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F8 04 1E 26 45 C6 11 A9 33 73 19 5B B3 D3 9D 76… +0,,6386,0:42.860.988,14.004.750 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,6387,0:42.874.993,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6391,0:42.875.990,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,6392,0:42.875.993,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F8 04 1E 26 45 C6 11 A9 33 73 19 5B B3 D3 9D 76… +0,,6396,0:42.876.990,15.004.916 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,6397,0:42.891.995,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 05 12 92 01 BA 7C A8 12 F7 6B D1 AC D3 78 F3… +0,,6401,0:42.892.992,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,6402,0:42.906.997,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6406,0:42.907.994,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,6407,0:42.907.998,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 76 05 12 92 01 BA 7C A8 12 F7 6B D1 AC D3 78 F3… +0,,6411,0:42.908.994,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,6412,0:42.924.000,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 58 40 81 D2 33 40 4F 78 79 48 AD 5F 4B C8 7F… +0,,6416,0:42.924.997,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,6417,0:42.939.002,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6421,0:42.939.999,2.833 us,,,,,[1 SOF],[Frame: 1921] +0,,6422,0:42.940.002,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 58 40 81 D2 33 40 4F 78 79 48 AD 5F 4B C8 7F… +0,,6426,0:42.940.999,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,6427,0:42.956.004,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CC 43 D5 36 0C 12 1E 2F BE 7A 73 8B 34 BC 86 95… +0,,6431,0:42.957.001,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,6432,0:42.971.006,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6436,0:42.972.003,2.833 us,,,,,[1 SOF],[Frame: 1953] +0,,6437,0:42.972.006,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CC 43 D5 36 0C 12 1E 2F BE 7A 73 8B 34 BC 86 95… +0,,6441,0:42.973.003,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,6442,0:42.988.009,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0B A8 54 AE B1 26 F4 EE 27 C6 75 6D 22 19 C3 58… +0,,6446,0:42.989.006,14.004.750 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,6447,0:43.003.011,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6451,0:43.004.008,16.005.125 ms,,,,,[17 SOF],[Frames: 1985 - 2001] +0,,6452,0:43.020.013,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D 65 45 6C 53 46 FC 1D AF 34 52 63 C9 0F 06 9C… +0,,6456,0:43.021.010,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,6457,0:43.035.015,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6461,0:43.036.012,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,6462,0:43.036.015,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D 65 45 6C 53 46 FC 1D AF 34 52 63 C9 0F 06 9C… +0,,6466,0:43.037.012,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,6467,0:43.052.018,50.729 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 3A 07 9C 6B 1B 17 FF A5 A6 4E BD 1F 4A D6 FF… +0,,6471,0:43.053.014,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,6472,0:43.067.020,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6476,0:43.068.017,2.833 us,,,,,[1 SOF],[Frame: 1] +0,,6477,0:43.068.020,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 3A 07 9C 6B 1B 17 FF A5 A6 4E BD 1F 4A D6 FF… +0,,6481,0:43.069.017,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,6482,0:43.084.022,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 74 AB 21 A1 70 27 6B E9 81 BE 3A 4B C8 66 D0 05… +0,,6486,0:43.085.019,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,6487,0:43.099.024,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6491,0:43.100.021,2.833 us,,,,,[1 SOF],[Frame: 33] +0,,6492,0:43.100.024,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 74 AB 21 A1 70 27 6B E9 81 BE 3A 4B C8 66 D0 05… +0,,6496,0:43.101.021,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,6497,0:43.116.026,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D9 81 62 76 F1 BA 80 10 82 15 F6 9E 83 56 B5 65… +0,,6501,0:43.117.023,14.004.750 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,6502,0:43.131.029,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6506,0:43.132.025,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,6507,0:43.132.029,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D9 81 62 76 F1 BA 80 10 82 15 F6 9E 83 56 B5 65… +0,,6511,0:43.133.026,15.004.916 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,6512,0:43.148.031,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E D6 B2 14 EB 88 ED 29 FA 88 3E 21 2E E7 64 0B… +0,,6516,0:43.149.028,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,6517,0:43.163.033,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6521,0:43.164.030,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,6522,0:43.164.033,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E D6 B2 14 EB 88 ED 29 FA 88 3E 21 2E E7 64 0B… +0,,6526,0:43.165.030,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,6527,0:43.180.035,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FA 49 85 96 48 67 6A 09 75 D0 66 20 9A C4 4D 81… +0,,6531,0:43.181.032,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,6532,0:43.195.037,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6536,0:43.196.034,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,6537,0:43.196.038,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FA 49 85 96 48 67 6A 09 75 D0 66 20 9A C4 4D 81… +0,,6541,0:43.197.034,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,6542,0:43.212.040,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 B8 52 2D 42 6F FE 35 05 0E B2 54 6B 0C 7F 7B… +0,,6546,0:43.213.037,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,6547,0:43.227.042,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6551,0:43.228.039,2.833 us,,,,,[1 SOF],[Frame: 161] +0,,6552,0:43.228.042,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 B8 52 2D 42 6F FE 35 05 0E B2 54 6B 0C 7F 7B… +0,,6556,0:43.229.039,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,6557,0:43.244.044,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F5 E6 35 AF D5 F1 F1 A5 CA 77 78 EE EF A6 BE D6… +0,,6561,0:43.245.041,14.004.750 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,6562,0:43.259.046,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6566,0:43.260.043,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,6567,0:43.260.046,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F5 E6 35 AF D5 F1 F1 A5 CA 77 78 EE EF A6 BE D6… +0,,6571,0:43.261.043,15.004.916 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,6572,0:43.276.049,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 DE 2E C1 38 25 07 DE 1A AA EB 7E 47 E0 BA CD… +0,,6576,0:43.277.046,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,6577,0:43.291.051,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6581,0:43.292.048,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,6582,0:43.292.051,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 DE 2E C1 38 25 07 DE 1A AA EB 7E 47 E0 BA CD… +0,,6586,0:43.293.048,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,6587,0:43.308.053,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 C1 6D 87 81 F6 9C 7E 8F D8 AD 49 CA 36 2C C4… +0,,6591,0:43.309.050,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,6592,0:43.323.055,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6596,0:43.324.052,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,6597,0:43.324.055,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 C1 6D 87 81 F6 9C 7E 8F D8 AD 49 CA 36 2C C4… +0,,6601,0:43.325.052,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,6602,0:43.340.058,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 3F D3 97 FB 77 41 39 10 FD 95 E0 3B AF 20 9D… +0,,6606,0:43.341.054,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,6607,0:43.355.060,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6611,0:43.356.057,2.833 us,,,,,[1 SOF],[Frame: 289] +0,,6612,0:43.356.060,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 3F D3 97 FB 77 41 39 10 FD 95 E0 3B AF 20 9D… +0,,6616,0:43.357.057,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,6617,0:43.372.062,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 B4 C9 0C A4 F8 6E 31 83 28 C4 FF 95 89 EC 7B… +0,,6621,0:43.373.059,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,6622,0:43.387.064,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6626,0:43.388.061,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,6627,0:43.388.064,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 B4 C9 0C A4 F8 6E 31 83 28 C4 FF 95 89 EC 7B… +0,,6631,0:43.389.061,15.004.916 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,6632,0:43.404.066,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C CC 8E F8 B3 B0 0E 8E 93 D6 93 AA D8 91 53 5B… +0,,6636,0:43.405.063,14.004.750 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,6637,0:43.419.069,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6641,0:43.420.065,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,6642,0:43.420.069,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C CC 8E F8 B3 B0 0E 8E 93 D6 93 AA D8 91 53 5B… +0,,6646,0:43.421.066,15.004.916 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,6647,0:43.436.071,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 17 09 E3 98 81 95 D1 67 B7 00 9D E7 14 45 56 AF… +0,,6651,0:43.437.068,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,6652,0:43.451.073,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6656,0:43.452.070,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,6657,0:43.452.073,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 17 09 E3 98 81 95 D1 67 B7 00 9D E7 14 45 56 AF… +0,,6661,0:43.453.070,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,6662,0:43.468.075,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E 0E E8 C6 19 00 5F AE E5 21 18 7C E6 DB 9D 14… +0,,6666,0:43.469.072,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,6667,0:43.483.077,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6671,0:43.484.074,2.833 us,,,,,[1 SOF],[Frame: 417] +0,,6672,0:43.484.078,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E 0E E8 C6 19 00 5F AE E5 21 18 7C E6 DB 9D 14… +0,,6676,0:43.485.074,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,6677,0:43.500.080,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CA 71 9E 84 62 C4 D0 08 67 8A 05 D7 6A 03 3A 5F… +0,,6681,0:43.501.077,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,6682,0:43.515.082,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6686,0:43.516.079,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,6687,0:43.516.082,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CA 71 9E 84 62 C4 D0 08 67 8A 05 D7 6A 03 3A 5F… +0,,6691,0:43.517.079,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,6692,0:43.532.084,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 95 6D F8 0C 22 A3 1A 3E 16 B0 A0 15 C9 F8 82 F3… +0,,6696,0:43.533.081,14.004.750 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,6697,0:43.547.086,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6701,0:43.548.083,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,6702,0:43.548.086,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 95 6D F8 0C 22 A3 1A 3E 16 B0 A0 15 C9 F8 82 F3… +0,,6706,0:43.549.083,15.004.916 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,6707,0:43.564.089,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D 7A 6F 26 8A 82 3C FB 9B AB 7A C4 E6 BB C7 14… +0,,6711,0:43.565.086,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,6712,0:43.579.091,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6716,0:43.580.088,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,6717,0:43.580.091,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D 7A 6F 26 8A 82 3C FB 9B AB 7A C4 E6 BB C7 14… +0,,6721,0:43.581.088,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,6722,0:43.596.093,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C3 42 61 37 31 88 FF 0F 56 2D BE AD 01 E9 27 97… +0,,6726,0:43.597.090,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,6727,0:43.611.095,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6731,0:43.612.092,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,6732,0:43.612.095,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C3 42 61 37 31 88 FF 0F 56 2D BE AD 01 E9 27 97… +0,,6736,0:43.613.092,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,6737,0:43.628.098,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7E E7 5B 3F 6C B0 94 A2 53 ED B2 3A C9 0D CA B5… +0,,6741,0:43.629.094,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,6742,0:43.643.100,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6746,0:43.644.097,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,6747,0:43.644.100,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7E E7 5B 3F 6C B0 94 A2 53 ED B2 3A C9 0D CA B5… +0,,6751,0:43.645.097,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,6752,0:43.660.102,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF 29 6A 50 89 9E A5 C5 21 B5 7B FF B9 E7 8D 34… +0,,6756,0:43.661.099,14.004.750 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,6757,0:43.675.104,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6761,0:43.676.101,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,6762,0:43.676.104,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF 29 6A 50 89 9E A5 C5 21 B5 7B FF B9 E7 8D 34… +0,,6766,0:43.677.101,15.004.916 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,6767,0:43.692.106,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5C A0 90 A6 2D 72 28 64 FA 40 3E 42 CA AD 3F BD… +0,,6771,0:43.693.103,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,6772,0:43.707.108,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6776,0:43.708.105,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,6777,0:43.708.109,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5C A0 90 A6 2D 72 28 64 FA 40 3E 42 CA AD 3F BD… +0,,6781,0:43.709.106,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,6782,0:43.724.111,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4D BF 16 19 DB 60 05 87 4B 79 B0 C1 C5 08 B0 19… +0,,6786,0:43.725.108,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,6787,0:43.739.113,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6791,0:43.740.110,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,6792,0:43.740.113,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D BF 16 19 DB 60 05 87 4B 79 B0 C1 C5 08 B0 19… +0,,6796,0:43.741.110,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,6797,0:43.756.115,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5C AF 93 EC EC CC FF BD AF ED 45 4C 18 6E 77 22… +0,,6801,0:43.757.112,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,6802,0:43.771.117,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6806,0:43.772.114,2.833 us,,,,,[1 SOF],[Frame: 705] +0,,6807,0:43.772.118,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5C AF 93 EC EC CC FF BD AF ED 45 4C 18 6E 77 22… +0,,6811,0:43.773.114,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,6812,0:43.788.120,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 59 15 EB 46 28 AD F2 5D F8 0E A7 10 7B F8 27 A8… +0,,6816,0:43.789.117,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,6817,0:43.803.122,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6821,0:43.804.119,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,6822,0:43.804.122,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 59 15 EB 46 28 AD F2 5D F8 0E A7 10 7B F8 27 A8… +0,,6826,0:43.805.119,15.004.916 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,6827,0:43.820.124,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7B 0D CA 40 F5 6F 75 EB 7C E4 2E F0 FA C3 6B D0… +0,,6831,0:43.821.121,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,6832,0:43.835.126,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6836,0:43.836.123,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,6837,0:43.836.126,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7B 0D CA 40 F5 6F 75 EB 7C E4 2E F0 FA C3 6B D0… +0,,6841,0:43.837.123,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,6842,0:43.852.129,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 61 0E 63 B4 26 16 C6 49 1D 4F 59 1F C8 AE A1 96… +0,,6846,0:43.853.126,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,6847,0:43.867.131,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6851,0:43.868.128,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,6852,0:43.868.131,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 61 0E 63 B4 26 16 C6 49 1D 4F 59 1F C8 AE A1 96… +0,,6856,0:43.869.128,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,6857,0:43.884.133,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C 68 1B 79 0A A2 A9 9C 9B 4D 42 A3 90 51 F3 20… +0,,6861,0:43.885.130,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,6862,0:43.899.135,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6866,0:43.900.132,2.833 us,,,,,[1 SOF],[Frame: 833] +0,,6867,0:43.900.135,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C 68 1B 79 0A A2 A9 9C 9B 4D 42 A3 90 51 F3 20… +0,,6871,0:43.901.132,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,6872,0:43.916.138,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 8B DF 36 2B 4F 0E 8E 95 A1 32 00 BC 38 91 DE… +0,,6876,0:43.917.134,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,6877,0:43.931.140,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6881,0:43.932.136,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,6882,0:43.932.140,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 8B DF 36 2B 4F 0E 8E 95 A1 32 00 BC 38 91 DE… +0,,6886,0:43.933.137,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,6887,0:43.948.142,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 65 37 9B A3 26 80 43 7D C7 8B F2 95 BC CB 6A B0… +0,,6891,0:43.949.139,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,6892,0:43.963.144,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6896,0:43.964.141,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,6897,0:43.964.144,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 65 37 9B A3 26 80 43 7D C7 8B F2 95 BC CB 6A B0… +0,,6901,0:43.965.141,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,6902,0:43.980.146,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6E 97 08 BE CE 8F 22 B4 19 32 81 BD 2E D0 E1 5C… +0,,6906,0:43.981.143,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,6907,0:43.995.148,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6911,0:43.996.145,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,6912,0:43.996.149,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6E 97 08 BE CE 8F 22 B4 19 32 81 BD 2E D0 E1 5C… +0,,6916,0:43.997.146,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,6917,0:44.012.151,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EA B5 72 5F 51 1D B8 9D CF 94 03 A7 2A 46 1B E1… +0,,6921,0:44.013.148,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,6922,0:44.027.153,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6926,0:44.028.150,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,6927,0:44.028.153,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EA B5 72 5F 51 1D B8 9D CF 94 03 A7 2A 46 1B E1… +0,,6931,0:44.029.150,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,6932,0:44.044.155,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C3 17 00 B2 04 65 2C DA 39 83 80 6F CD 2B 7E 1E… +0,,6936,0:44.045.152,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,6937,0:44.059.157,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6941,0:44.060.154,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,6942,0:44.060.158,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C3 17 00 B2 04 65 2C DA 39 83 80 6F CD 2B 7E 1E… +0,,6946,0:44.061.154,15.005.000 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,6947,0:44.076.160,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 38 67 41 12 3D C7 B8 24 0B 27 CB 6C 86 43 D9… +0,,6951,0:44.077.157,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,6952,0:44.091.162,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6956,0:44.092.159,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,6957,0:44.092.162,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 38 67 41 12 3D C7 B8 24 0B 27 CB 6C 86 43 D9… +0,,6961,0:44.093.159,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,6962,0:44.108.164,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 7E E1 5B AF 5A B4 9A B6 A7 95 CB 29 14 68 70… +0,,6966,0:44.109.161,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,6967,0:44.123.166,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6971,0:44.124.163,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,6972,0:44.124.166,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7A 7E E1 5B AF 5A B4 9A B6 A7 95 CB 29 14 68 70… +0,,6976,0:44.125.163,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,6977,0:44.140.169,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 7F AA 49 AF C5 15 AA 86 AC 8F 50 7C E8 AA 20… +0,,6981,0:44.141.165,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,6982,0:44.155.171,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,6986,0:44.156.168,2.833 us,,,,,[1 SOF],[Frame: 1089] +0,,6987,0:44.156.171,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 7F AA 49 AF C5 15 AA 86 AC 8F 50 7C E8 AA 20… +0,,6991,0:44.157.168,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,6992,0:44.172.173,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 6F 0A 18 7E C0 02 17 D4 5D 3A F6 D6 AB 7B 1D… +0,,6996,0:44.173.170,14.004.750 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,6997,0:44.187.175,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7001,0:44.188.172,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,7002,0:44.188.175,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 6F 0A 18 7E C0 02 17 D4 5D 3A F6 D6 AB 7B 1D… +0,,7006,0:44.189.172,15.004.916 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,7007,0:44.204.177,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 69 57 98 ED 6E C3 05 36 95 E4 F6 EC E2 C5 0B… +0,,7011,0:44.205.174,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,7012,0:44.219.180,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7016,0:44.220.176,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,7017,0:44.220.180,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 69 57 98 ED 6E C3 05 36 95 E4 F6 EC E2 C5 0B… +0,,7021,0:44.221.177,15.004.916 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,7022,0:44.236.182,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3D 2A D0 CB F6 61 FF B7 06 92 2B C6 BA 5F F8 42… +0,,7026,0:44.237.179,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,7027,0:44.251.184,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7031,0:44.252.181,16.005.041 ms,,,,,[17 SOF],[Frames: 1185 - 1201] +0,,7032,0:44.268.186,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 54 BC 18 C2 97 1B CC 80 D5 6E CB 85 F0 51 6C 7B… +0,,7036,0:44.269.183,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,7037,0:44.283.188,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7041,0:44.284.185,2.833 us,,,,,[1 SOF],[Frame: 1217] +0,,7042,0:44.284.189,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 54 BC 18 C2 97 1B CC 80 D5 6E CB 85 F0 51 6C 7B… +0,,7046,0:44.285.185,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,7047,0:44.300.191,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4A 1F ED 17 49 33 06 4A FB C8 28 2C 17 C7 BC 8E… +0,,7051,0:44.301.188,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,7052,0:44.315.193,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7056,0:44.316.190,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,7057,0:44.316.193,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4A 1F ED 17 49 33 06 4A FB C8 28 2C 17 C7 BC 8E… +0,,7061,0:44.317.190,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,7062,0:44.332.195,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C 80 D5 FD 88 DB C8 9A 7E 4E 73 E4 C6 EF 78 75… +0,,7066,0:44.333.192,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,7067,0:44.347.197,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7071,0:44.348.194,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,7072,0:44.348.197,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C 80 D5 FD 88 DB C8 9A 7E 4E 73 E4 C6 EF 78 75… +0,,7076,0:44.349.194,15.004.916 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,7077,0:44.364.200,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 47 60 BD 72 54 03 D4 F6 71 47 9B 8B F3 77 33… +0,,7081,0:44.365.197,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,7082,0:44.379.202,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7086,0:44.380.199,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,7087,0:44.380.202,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 47 60 BD 72 54 03 D4 F6 71 47 9B 8B F3 77 33… +0,,7091,0:44.381.199,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,7092,0:44.396.204,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C 8B D9 B6 FD 2D 3F 63 59 BE 92 34 A2 5E E8 EE… +0,,7096,0:44.397.201,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,7097,0:44.411.206,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7101,0:44.412.203,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,7102,0:44.412.206,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C 8B D9 B6 FD 2D 3F 63 59 BE 92 34 A2 5E E8 EE… +0,,7106,0:44.413.203,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,7107,0:44.428.209,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2E E7 D3 76 A7 17 8D FB E3 BF 84 2F 16 C2 90 E8… +0,,7111,0:44.429.205,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,7112,0:44.443.211,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7116,0:44.444.208,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,7117,0:44.444.211,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2E E7 D3 76 A7 17 8D FB E3 BF 84 2F 16 C2 90 E8… +0,,7121,0:44.445.208,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,7122,0:44.460.213,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 3D 06 12 DE 8B 56 7D C3 9C 5A 84 51 42 15 83… +0,,7126,0:44.461.210,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,7127,0:44.475.215,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7131,0:44.476.212,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,7132,0:44.476.215,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 3D 06 12 DE 8B 56 7D C3 9C 5A 84 51 42 15 83… +0,,7136,0:44.477.212,15.004.916 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,7137,0:44.492.217,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 47 9E 1E 7B DE FD 39 7A 05 47 C0 6D 57 85 11 A5… +0,,7141,0:44.493.214,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,7142,0:44.507.220,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7146,0:44.508.216,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,7147,0:44.508.220,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 47 9E 1E 7B DE FD 39 7A 05 47 C0 6D 57 85 11 A5… +0,,7151,0:44.509.217,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,7152,0:44.524.222,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 C2 77 FA F9 10 49 83 A9 D2 D1 3A 8B F2 A6 A1… +0,,7156,0:44.525.219,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,7157,0:44.539.224,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7161,0:44.540.221,2.833 us,,,,,[1 SOF],[Frame: 1473] +0,,7162,0:44.540.224,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 C2 77 FA F9 10 49 83 A9 D2 D1 3A 8B F2 A6 A1… +0,,7166,0:44.541.221,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,7167,0:44.556.226,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB 93 78 D7 69 B2 50 C3 56 29 3A 06 26 69 1C 3C… +0,,7171,0:44.557.223,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,7172,0:44.571.229,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7176,0:44.572.225,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,7177,0:44.572.229,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB 93 78 D7 69 B2 50 C3 56 29 3A 06 26 69 1C 3C… +0,,7181,0:44.573.225,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,7182,0:44.588.231,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AD D1 CD 02 16 52 F2 D0 0F 51 0D 95 7E 47 22 00… +0,,7186,0:44.589.228,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,7187,0:44.603.233,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7191,0:44.604.230,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,7192,0:44.604.233,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AD D1 CD 02 16 52 F2 D0 0F 51 0D 95 7E 47 22 00… +0,,7196,0:44.605.230,15.005.000 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,7197,0:44.620.235,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 41 1B 9A 14 AB 81 3F 39 6D A7 A0 13 11 19 F0… +0,,7201,0:44.621.232,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,7202,0:44.635.237,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7206,0:44.636.234,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,7207,0:44.636.237,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 41 1B 9A 14 AB 81 3F 39 6D A7 A0 13 11 19 F0… +0,,7211,0:44.637.234,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,7212,0:44.652.240,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D 4F C1 84 6E 73 8D A1 88 2C CF 81 D1 70 C0 9D… +0,,7216,0:44.653.237,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,7217,0:44.667.242,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7221,0:44.668.239,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,7222,0:44.668.242,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D 4F C1 84 6E 73 8D A1 88 2C CF 81 D1 70 C0 9D… +0,,7226,0:44.669.239,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,7227,0:44.684.244,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D6 6B CF 0B 9F 92 3B 6B DC C5 BA 3C 72 8C 65 C9… +0,,7231,0:44.685.241,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,7232,0:44.699.246,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7236,0:44.700.243,2.833 us,,,,,[1 SOF],[Frame: 1633] +0,,7237,0:44.700.246,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D6 6B CF 0B 9F 92 3B 6B DC C5 BA 3C 72 8C 65 C9… +0,,7241,0:44.701.243,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,7242,0:44.716.249,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 69 20 57 E7 9F D7 44 E8 5A 99 57 E3 38 C1 F7 35… +0,,7246,0:44.717.245,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,7247,0:44.731.251,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7251,0:44.732.248,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,7252,0:44.732.251,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 69 20 57 E7 9F D7 44 E8 5A 99 57 E3 38 C1 F7 35… +0,,7256,0:44.733.248,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,7257,0:44.748.253,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EE FF 7D CA 09 C4 51 96 57 40 FA 88 0C 12 67 3C… +0,,7261,0:44.749.250,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,7262,0:44.763.255,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7266,0:44.764.252,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,7267,0:44.764.255,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EE FF 7D CA 09 C4 51 96 57 40 FA 88 0C 12 67 3C… +0,,7271,0:44.765.252,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,7272,0:44.780.257,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B7 94 A3 61 B9 7A EF 7E 62 AB 57 5E 5C 3B 6B 5E… +0,,7276,0:44.781.254,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,7277,0:44.795.260,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7281,0:44.796.256,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,7282,0:44.796.260,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B7 94 A3 61 B9 7A EF 7E 62 AB 57 5E 5C 3B 6B 5E… +0,,7286,0:44.797.257,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,7287,0:44.812.262,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC 1B E5 1A A2 1E 16 35 1B 63 85 18 EF 67 B8 9C… +0,,7291,0:44.813.259,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,7292,0:44.827.264,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7296,0:44.828.261,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,7297,0:44.828.264,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC 1B E5 1A A2 1E 16 35 1B 63 85 18 EF 67 B8 9C… +0,,7301,0:44.829.261,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,7302,0:44.844.266,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6F 73 23 65 C2 26 F8 49 4D 54 04 1C D2 71 A3 63… +0,,7306,0:44.845.263,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,7307,0:44.859.268,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7311,0:44.860.265,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,7312,0:44.860.269,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6F 73 23 65 C2 26 F8 49 4D 54 04 1C D2 71 A3 63… +0,,7316,0:44.861.265,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,7317,0:44.876.271,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 80 2F 45 F9 44 08 D9 61 2B 73 BD 18 F9 E8 85 B3… +0,,7321,0:44.877.268,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,7322,0:44.891.273,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7326,0:44.892.270,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,7327,0:44.892.273,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 80 2F 45 F9 44 08 D9 61 2B 73 BD 18 F9 E8 85 B3… +0,,7331,0:44.893.270,15.004.916 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,7332,0:44.908.275,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 20 B1 B2 F5 CE 94 1A 01 CD 21 8B 83 FB 28 BE A8… +0,,7336,0:44.909.272,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,7337,0:44.923.277,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7341,0:44.924.274,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,7342,0:44.924.277,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 20 B1 B2 F5 CE 94 1A 01 CD 21 8B 83 FB 28 BE A8… +0,,7346,0:44.925.274,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,7347,0:44.940.280,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 96 2F 77 B2 16 25 2F DD 57 2D 79 B7 30 C1 E2… +0,,7351,0:44.941.277,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,7352,0:44.955.282,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7356,0:44.956.279,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,7357,0:44.956.282,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA 96 2F 77 B2 16 25 2F DD 57 2D 79 B7 30 C1 E2… +0,,7361,0:44.957.279,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,7362,0:44.972.284,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 93 6A E4 32 32 A6 01 6F EA 74 55 36 54 71 77… +0,,7366,0:44.973.281,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,7367,0:44.987.286,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7371,0:44.988.283,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,7372,0:44.988.286,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 93 6A E4 32 32 A6 01 6F EA 74 55 36 54 71 77… +0,,7376,0:44.989.283,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,7377,0:45.004.289,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 D8 B3 3B F8 57 01 DF A3 45 F6 1B 58 AE DA CC… +0,,7381,0:45.005.285,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,7382,0:45.019.291,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7386,0:45.020.288,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,7387,0:45.020.291,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 D8 B3 3B F8 57 01 DF A3 45 F6 1B 58 AE DA CC… +0,,7391,0:45.021.288,15.004.916 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,7392,0:45.036.293,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 70 80 2D AE 7E 15 1F 43 F0 0B D1 A3 E0 34 4D… +0,,7396,0:45.037.290,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,7397,0:45.051.295,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7401,0:45.052.292,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,7402,0:45.052.295,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 70 80 2D AE 7E 15 1F 43 F0 0B D1 A3 E0 34 4D… +0,,7406,0:45.053.292,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,7407,0:45.068.298,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 38 53 88 2D F7 F2 0A 21 B0 10 6C 8E 3C 8D 16… +0,,7411,0:45.069.294,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,7412,0:45.083.300,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7416,0:45.084.296,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,7417,0:45.084.300,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 38 53 88 2D F7 F2 0A 21 B0 10 6C 8E 3C 8D 16… +0,,7421,0:45.085.297,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,7422,0:45.100.302,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF 37 C9 86 52 E0 1B B4 0F 3A 3C 9F EF 18 93 46… +0,,7426,0:45.101.299,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,7427,0:45.115.304,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7431,0:45.116.301,2.833 us,,,,,[1 SOF],[Frame: 1] +0,,7432,0:45.116.304,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF 37 C9 86 52 E0 1B B4 0F 3A 3C 9F EF 18 93 46… +0,,7436,0:45.117.301,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,7437,0:45.132.306,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 91 95 04 EC 45 07 95 46 C0 62 85 89 33 03 7D A1… +0,,7441,0:45.133.303,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,7442,0:45.147.308,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7446,0:45.148.305,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,7447,0:45.148.309,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 91 95 04 EC 45 07 95 46 C0 62 85 89 33 03 7D A1… +0,,7451,0:45.149.305,15.004.916 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,7452,0:45.164.311,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 72 AF 78 A2 E0 54 F8 FB 37 E2 35 E3 08 63 99 A7… +0,,7456,0:45.165.308,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,7457,0:45.179.313,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7461,0:45.180.310,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,7462,0:45.180.313,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 72 AF 78 A2 E0 54 F8 FB 37 E2 35 E3 08 63 99 A7… +0,,7466,0:45.181.310,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,7467,0:45.196.315,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C A9 12 30 3F 62 A5 DB 73 6B F8 5F 58 1B D4 03… +0,,7471,0:45.197.312,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,7472,0:45.211.317,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7476,0:45.212.314,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,7477,0:45.212.317,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C A9 12 30 3F 62 A5 DB 73 6B F8 5F 58 1B D4 03… +0,,7481,0:45.213.314,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,7482,0:45.228.320,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E5 83 C1 63 3B 10 5D 90 8C 6C B9 E8 7E 33 E3 16… +0,,7486,0:45.229.317,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,7487,0:45.243.322,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7491,0:45.244.319,2.833 us,,,,,[1 SOF],[Frame: 129] +0,,7492,0:45.244.322,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 83 C1 63 3B 10 5D 90 8C 6C B9 E8 7E 33 E3 16… +0,,7496,0:45.245.319,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,7497,0:45.260.324,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 1A C1 27 D7 03 2C 3B D7 5C 04 7D 4D 72 C6 EE… +0,,7501,0:45.261.321,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,7502,0:45.275.326,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7506,0:45.276.323,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,7507,0:45.276.326,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 1A C1 27 D7 03 2C 3B D7 5C 04 7D 4D 72 C6 EE… +0,,7511,0:45.277.323,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,7512,0:45.292.329,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 81 5A B3 B4 DE C8 1D F8 24 3D 7B 14 24 4A 88… +0,,7516,0:45.293.325,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,7517,0:45.307.331,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7521,0:45.308.328,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,7522,0:45.308.331,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 81 5A B3 B4 DE C8 1D F8 24 3D 7B 14 24 4A 88… +0,,7526,0:45.309.328,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,7527,0:45.324.333,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FF 17 78 49 58 CB F2 39 5D 45 EA C1 20 4E D3 88… +0,,7531,0:45.325.330,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,7532,0:45.339.335,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7536,0:45.340.332,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,7537,0:45.340.335,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FF 17 78 49 58 CB F2 39 5D 45 EA C1 20 4E D3 88… +0,,7541,0:45.341.332,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,7542,0:45.356.337,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F B0 3A A2 60 45 95 AC E7 E6 DD D7 F3 22 A6 21… +0,,7546,0:45.357.334,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,7547,0:45.371.340,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7551,0:45.372.336,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,7552,0:45.372.340,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0F B0 3A A2 60 45 95 AC E7 E6 DD D7 F3 22 A6 21… +0,,7556,0:45.373.337,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,7557,0:45.388.342,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F9 A4 91 D1 15 54 64 87 D6 60 A9 05 E0 1F D8 D1… +0,,7561,0:45.389.339,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,7562,0:45.403.344,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7566,0:45.404.341,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,7567,0:45.404.344,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F9 A4 91 D1 15 54 64 87 D6 60 A9 05 E0 1F D8 D1… +0,,7571,0:45.405.341,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,7572,0:45.420.346,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 60 3E 87 84 A8 A3 5F 47 AE A5 25 14 57 F1 7A… +0,,7576,0:45.421.343,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,7577,0:45.435.348,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7581,0:45.436.345,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,7582,0:45.436.349,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 60 3E 87 84 A8 A3 5F 47 AE A5 25 14 57 F1 7A… +0,,7586,0:45.437.345,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,7587,0:45.452.351,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB 21 73 CC DA E8 45 5E ED 2C 08 F9 5E CE 3B 11… +0,,7591,0:45.453.348,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,7592,0:45.467.353,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7596,0:45.468.350,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,7597,0:45.468.353,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB 21 73 CC DA E8 45 5E ED 2C 08 F9 5E CE 3B 11… +0,,7601,0:45.469.350,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,7602,0:45.484.355,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C 97 5A B5 C5 37 5D 3E 58 64 26 29 E5 D2 EC 66… +0,,7606,0:45.485.352,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,7607,0:45.499.357,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7611,0:45.500.354,16.005.041 ms,,,,,[17 SOF],[Frames: 385 - 401] +0,,7612,0:45.516.360,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F C0 8D 89 92 E0 4D A9 76 90 15 7B A4 9B 5F 7F… +0,,7616,0:45.517.357,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,7617,0:45.531.362,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7621,0:45.532.359,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,7622,0:45.532.362,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0F C0 8D 89 92 E0 4D A9 76 90 15 7B A4 9B 5F 7F… +0,,7626,0:45.533.359,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,7627,0:45.548.364,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC AD A7 26 19 A9 87 48 39 F8 42 60 02 5D F0 1B… +0,,7631,0:45.549.361,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,7632,0:45.563.366,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7636,0:45.564.363,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,7637,0:45.564.366,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC AD A7 26 19 A9 87 48 39 F8 42 60 02 5D F0 1B… +0,,7641,0:45.565.363,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,7642,0:45.580.369,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 15 51 35 55 24 D0 F6 8B 4B 6D D2 59 97 4A 62 56… +0,,7646,0:45.581.365,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,7647,0:45.595.371,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7651,0:45.596.367,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,7652,0:45.596.371,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 15 51 35 55 24 D0 F6 8B 4B 6D D2 59 97 4A 62 56… +0,,7656,0:45.597.368,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,7657,0:45.612.373,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 C5 B5 9B B1 7D EA CC A4 E2 B8 E1 91 33 F1 2A… +0,,7661,0:45.613.370,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,7662,0:45.627.375,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7666,0:45.628.372,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,7667,0:45.628.375,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 C5 B5 9B B1 7D EA CC A4 E2 B8 E1 91 33 F1 2A… +0,,7671,0:45.629.372,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,7672,0:45.644.377,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D 11 CA 4B 1C BF A5 8D 88 14 E7 B5 8A 02 63 8C… +0,,7676,0:45.645.374,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,7677,0:45.659.379,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7681,0:45.660.376,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,7682,0:45.660.380,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D 11 CA 4B 1C BF A5 8D 88 14 E7 B5 8A 02 63 8C… +0,,7686,0:45.661.377,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,7687,0:45.676.382,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 0D 4C 97 53 5D EF B1 A5 D2 AB 44 3D 5C 11 FC… +0,,7691,0:45.677.379,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,7692,0:45.691.384,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7696,0:45.692.381,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,7697,0:45.692.384,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 0D 4C 97 53 5D EF B1 A5 D2 AB 44 3D 5C 11 FC… +0,,7701,0:45.693.381,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,7702,0:45.708.386,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 67 25 14 73 19 CA E9 E1 64 E1 02 8E C7 37 07… +0,,7706,0:45.709.383,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,7707,0:45.723.388,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7711,0:45.724.385,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,7712,0:45.724.389,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 67 25 14 73 19 CA E9 E1 64 E1 02 8E C7 37 07… +0,,7716,0:45.725.385,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,7717,0:45.740.391,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC C0 5F 3E D8 B3 66 96 21 98 22 40 95 B2 79 B3… +0,,7721,0:45.741.388,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,7722,0:45.755.393,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7726,0:45.756.390,2.833 us,,,,,[1 SOF],[Frame: 641] +0,,7727,0:45.756.393,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC C0 5F 3E D8 B3 66 96 21 98 22 40 95 B2 79 B3… +0,,7731,0:45.757.390,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,7732,0:45.772.395,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 19 4D 6A F5 0F 6B 6C 8C 57 5C 44 00 6C 16 6D 19… +0,,7736,0:45.773.392,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,7737,0:45.787.397,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7741,0:45.788.394,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,7742,0:45.788.397,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 19 4D 6A F5 0F 6B 6C 8C 57 5C 44 00 6C 16 6D 19… +0,,7746,0:45.789.394,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,7747,0:45.804.400,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5C 69 EE 6A DC 5A 83 E1 69 48 F3 77 21 9C 1F C3… +0,,7751,0:45.805.397,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,7752,0:45.819.402,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7756,0:45.820.399,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,7757,0:45.820.402,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5C 69 EE 6A DC 5A 83 E1 69 48 F3 77 21 9C 1F C3… +0,,7761,0:45.821.399,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,7762,0:45.836.404,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 66 8F 96 7E 04 EB 7A B1 EB E5 9D 32 F7 60 A3 A4… +0,,7766,0:45.837.401,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,7767,0:45.851.406,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7771,0:45.852.403,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,7772,0:45.852.406,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 66 8F 96 7E 04 EB 7A B1 EB E5 9D 32 F7 60 A3 A4… +0,,7776,0:45.853.403,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,7777,0:45.868.409,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 31 78 71 F6 28 AA 72 8D 8B A1 7A 34 FD 37 AE… +0,,7781,0:45.869.405,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,7782,0:45.883.411,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7786,0:45.884.407,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,7787,0:45.884.411,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 31 78 71 F6 28 AA 72 8D 8B A1 7A 34 FD 37 AE… +0,,7791,0:45.885.408,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,7792,0:45.900.413,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 90 B5 F9 C0 5B 1F C1 F4 04 CB E6 E0 32 71 03 3C… +0,,7796,0:45.901.410,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,7797,0:45.915.415,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7801,0:45.916.412,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,7802,0:45.916.415,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 90 B5 F9 C0 5B 1F C1 F4 04 CB E6 E0 32 71 03 3C… +0,,7806,0:45.917.412,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,7807,0:45.932.417,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 F9 BD 4F 43 B0 27 3C 73 34 7C D6 2F EB 7D 98… +0,,7811,0:45.933.414,14.004.750 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,7812,0:45.947.419,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7816,0:45.948.416,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,7817,0:45.948.420,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 F9 BD 4F 43 B0 27 3C 73 34 7C D6 2F EB 7D 98… +0,,7821,0:45.949.416,15.004.916 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,7822,0:45.964.422,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5D 7C B3 74 CA 7B D5 AF 56 FE 1E F4 B0 52 2E 28… +0,,7826,0:45.965.419,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,7827,0:45.979.424,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7831,0:45.980.421,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,7832,0:45.980.424,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5D 7C B3 74 CA 7B D5 AF 56 FE 1E F4 B0 52 2E 28… +0,,7836,0:45.981.421,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,7837,0:45.996.426,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 51 0B 86 24 1D C3 98 A5 F1 0A B0 3F 3F 24 F6 42… +0,,7841,0:45.997.423,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,7842,0:46.011.428,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7846,0:46.012.425,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,7847,0:46.012.428,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 51 0B 86 24 1D C3 98 A5 F1 0A B0 3F 3F 24 F6 42… +0,,7851,0:46.013.425,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,7852,0:46.028.431,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 70 5F 2E FB 4F 5D F0 59 16 D7 2B 74 A0 8E 18 C7… +0,,7856,0:46.029.428,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,7857,0:46.043.433,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7861,0:46.044.430,2.833 us,,,,,[1 SOF],[Frame: 929] +0,,7862,0:46.044.433,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 70 5F 2E FB 4F 5D F0 59 16 D7 2B 74 A0 8E 18 C7… +0,,7866,0:46.045.430,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,7867,0:46.060.435,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 55 65 B4 53 20 6A 4B 58 43 C7 3D B5 BD 67 A4 C2… +0,,7871,0:46.061.432,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,7872,0:46.075.437,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7876,0:46.076.434,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,7877,0:46.076.437,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 55 65 B4 53 20 6A 4B 58 43 C7 3D B5 BD 67 A4 C2… +0,,7881,0:46.077.434,15.004.916 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,7882,0:46.092.440,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BF 5B 3F 84 E5 71 F4 3F 09 5D 73 A8 4D 05 B8 A9… +0,,7886,0:46.093.436,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,7887,0:46.107.442,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7891,0:46.108.439,16.005.125 ms,,,,,[17 SOF],[Frames: 993 - 1009] +0,,7892,0:46.124.444,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 26 B3 72 1E BB 53 71 7E 6F D4 71 59 78 26 B6 0B… +0,,7896,0:46.125.441,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,7897,0:46.139.446,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7901,0:46.140.443,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,7902,0:46.140.446,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 26 B3 72 1E BB 53 71 7E 6F D4 71 59 78 26 B6 0B… +0,,7906,0:46.141.443,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,7907,0:46.156.448,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FA 72 B8 05 01 19 0D 06 8A C7 19 AC 71 96 4B 73… +0,,7911,0:46.157.445,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,7912,0:46.171.451,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7916,0:46.172.447,2.833 us,,,,,[1 SOF],[Frame: 1057] +0,,7917,0:46.172.451,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FA 72 B8 05 01 19 0D 06 8A C7 19 AC 71 96 4B 73… +0,,7921,0:46.173.448,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,7922,0:46.188.453,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 11 64 D6 22 CC C9 A5 C3 D1 BC 4B E6 5C 98 5C… +0,,7926,0:46.189.450,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,7927,0:46.203.455,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7931,0:46.204.452,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,7932,0:46.204.455,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 11 64 D6 22 CC C9 A5 C3 D1 BC 4B E6 5C 98 5C… +0,,7936,0:46.205.452,15.004.916 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,7937,0:46.220.457,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 71 68 F5 49 23 40 53 70 06 AC A8 94 D6 B2 C0 F1… +0,,7941,0:46.221.454,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,7942,0:46.235.459,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7946,0:46.236.456,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,7947,0:46.236.460,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 71 68 F5 49 23 40 53 70 06 AC A8 94 D6 B2 C0 F1… +0,,7951,0:46.237.456,15.004.916 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,7952,0:46.252.462,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 99 A6 DC D8 41 49 E9 9D C1 CC 7B D6 71 26 0A… +0,,7956,0:46.253.459,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,7957,0:46.267.464,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7961,0:46.268.461,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,7962,0:46.268.464,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 99 A6 DC D8 41 49 E9 9D C1 CC 7B D6 71 26 0A… +0,,7966,0:46.269.461,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,7967,0:46.284.466,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 38 5F 46 0B C9 6D 42 83 55 4B 1F 73 DD 77 10… +0,,7971,0:46.285.463,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,7972,0:46.299.468,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7976,0:46.300.465,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,7977,0:46.300.468,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 31 38 5F 46 0B C9 6D 42 83 55 4B 1F 73 DD 77 10… +0,,7981,0:46.301.465,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,7982,0:46.316.471,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 90 F2 31 E0 98 84 F0 60 64 1F 01 46 69 8A 02 7B… +0,,7986,0:46.317.468,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,7987,0:46.331.473,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,7991,0:46.332.470,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,7992,0:46.332.473,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 90 F2 31 E0 98 84 F0 60 64 1F 01 46 69 8A 02 7B… +0,,7996,0:46.333.470,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,7997,0:46.348.475,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 12 B0 16 76 6A 8D B2 84 7C A9 AA 7B 0D 1F 16… +0,,8001,0:46.349.472,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,8002,0:46.363.477,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8006,0:46.364.474,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,8007,0:46.364.477,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 12 B0 16 76 6A 8D B2 84 7C A9 AA 7B 0D 1F 16… +0,,8011,0:46.365.474,15.004.916 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,8012,0:46.380.480,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 35 DA B9 EE A7 35 33 89 C2 2E 05 79 ED C1 68 71… +0,,8016,0:46.381.476,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,8017,0:46.395.482,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8021,0:46.396.479,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,8022,0:46.396.482,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 35 DA B9 EE A7 35 33 89 C2 2E 05 79 ED C1 68 71… +0,,8026,0:46.397.479,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,8027,0:46.412.484,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 43 F1 85 CE 65 12 12 DB 24 DF 87 6D 96 5A 85 0D… +0,,8031,0:46.413.481,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,8032,0:46.427.486,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8036,0:46.428.483,2.833 us,,,,,[1 SOF],[Frame: 1313] +0,,8037,0:46.428.486,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 43 F1 85 CE 65 12 12 DB 24 DF 87 6D 96 5A 85 0D… +0,,8041,0:46.429.483,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,8042,0:46.444.488,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 E8 71 6E 0A E9 73 3C A7 2A CB A1 91 4B 85 D0… +0,,8046,0:46.445.485,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,8047,0:46.459.491,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8051,0:46.460.487,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,8052,0:46.460.491,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 E8 71 6E 0A E9 73 3C A7 2A CB A1 91 4B 85 D0… +0,,8056,0:46.461.488,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,8057,0:46.476.493,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 4C 2E 19 7A 24 68 64 85 6E F2 BD FB EA 01 88… +0,,8061,0:46.477.490,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,8062,0:46.491.495,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8066,0:46.492.492,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,8067,0:46.492.495,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 4C 2E 19 7A 24 68 64 85 6E F2 BD FB EA 01 88… +0,,8071,0:46.493.492,15.004.916 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,8072,0:46.508.497,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 24 0B A0 98 46 88 ED 7A BE 06 64 F4 D2 33 AF 8D… +0,,8076,0:46.509.494,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,8077,0:46.523.499,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8081,0:46.524.496,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,8082,0:46.524.500,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 24 0B A0 98 46 88 ED 7A BE 06 64 F4 D2 33 AF 8D… +0,,8086,0:46.525.496,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,8087,0:46.540.502,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E8 89 78 69 EA 7A DF 0B 35 55 93 AD E4 57 99 22… +0,,8091,0:46.541.499,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,8092,0:46.555.504,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8096,0:46.556.501,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,8097,0:46.556.504,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E8 89 78 69 EA 7A DF 0B 35 55 93 AD E4 57 99 22… +0,,8101,0:46.557.501,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,8102,0:46.572.506,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 26 E1 E7 BD 55 7C 91 9A 6B A3 4F 3A E9 26 CD DA… +0,,8106,0:46.573.503,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,8107,0:46.587.508,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8111,0:46.588.505,2.833 us,,,,,[1 SOF],[Frame: 1473] +0,,8112,0:46.588.508,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 26 E1 E7 BD 55 7C 91 9A 6B A3 4F 3A E9 26 CD DA… +0,,8116,0:46.589.505,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,8117,0:46.604.511,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C 93 CE 2E 1E DE 6B 33 9B E4 1E AA 37 33 66 CB… +0,,8121,0:46.605.508,14.004.833 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,8122,0:46.619.513,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8126,0:46.620.510,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,8127,0:46.620.513,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9C 93 CE 2E 1E DE 6B 33 9B E4 1E AA 37 33 66 CB… +0,,8131,0:46.621.510,15.004.916 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,8132,0:46.636.515,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BF D6 B0 97 45 2B EC 68 97 9F FF 14 5A 30 8E FA… +0,,8136,0:46.637.512,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,8137,0:46.651.517,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8141,0:46.652.514,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,8142,0:46.652.517,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BF D6 B0 97 45 2B EC 68 97 9F FF 14 5A 30 8E FA… +0,,8146,0:46.653.514,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,8147,0:46.668.520,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E5 FC E1 28 8A B1 14 0F F8 9C D0 B5 9B 66 0A DB… +0,,8151,0:46.669.516,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,8152,0:46.683.522,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8156,0:46.684.519,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,8157,0:46.684.522,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 FC E1 28 8A B1 14 0F F8 9C D0 B5 9B 66 0A DB… +0,,8161,0:46.685.519,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,8162,0:46.700.524,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 AA CE 3A 61 9D C4 77 7F F9 97 18 66 54 82 E5… +0,,8166,0:46.701.521,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,8167,0:46.715.526,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8171,0:46.716.523,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,8172,0:46.716.526,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 AA CE 3A 61 9D C4 77 7F F9 97 18 66 54 82 E5… +0,,8176,0:46.717.523,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,8177,0:46.732.528,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 63 0F 3D 61 9A 3F DA 21 FB F3 34 3D 7D 91 56… +0,,8181,0:46.733.525,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,8182,0:46.747.531,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8186,0:46.748.527,16.005.041 ms,,,,,[17 SOF],[Frames: 1633 - 1649] +0,,8187,0:46.764.533,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0E 9F C4 ED B3 DA B9 A8 69 A1 D7 27 C5 D9 94 5B… +0,,8191,0:46.765.530,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,8192,0:46.779.535,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8196,0:46.780.532,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,8197,0:46.780.535,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0E 9F C4 ED B3 DA B9 A8 69 A1 D7 27 C5 D9 94 5B… +0,,8201,0:46.781.532,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,8202,0:46.796.537,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 C1 0E 01 EE 30 F1 13 66 7E 3C 68 60 27 86 B4… +0,,8206,0:46.797.534,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,8207,0:46.811.539,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8211,0:46.812.536,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,8212,0:46.812.540,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 C1 0E 01 EE 30 F1 13 66 7E 3C 68 60 27 86 B4… +0,,8216,0:46.813.536,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,8217,0:46.828.542,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4A E7 BB E2 0B A2 D3 B0 23 67 68 F5 1D 9D 00 7D… +0,,8221,0:46.829.539,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,8222,0:46.843.544,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8226,0:46.844.541,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,8227,0:46.844.544,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4A E7 BB E2 0B A2 D3 B0 23 67 68 F5 1D 9D 00 7D… +0,,8231,0:46.845.541,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,8232,0:46.860.546,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD F8 CC C1 4C 1F D5 A2 C4 E5 CA 09 85 79 28 F5… +0,,8236,0:46.861.543,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,8237,0:46.875.548,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8241,0:46.876.545,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,8242,0:46.876.549,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD F8 CC C1 4C 1F D5 A2 C4 E5 CA 09 85 79 28 F5… +0,,8246,0:46.877.545,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,8247,0:46.892.551,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A 94 18 48 BE A4 7D 88 B4 D7 08 E4 F7 96 B0 1B… +0,,8251,0:46.893.548,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,8252,0:46.907.553,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8256,0:46.908.550,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,8257,0:46.908.553,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A 94 18 48 BE A4 7D 88 B4 D7 08 E4 F7 96 B0 1B… +0,,8261,0:46.909.550,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,8262,0:46.924.555,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C B2 D2 74 49 87 14 24 FD E3 90 C3 03 E1 1C 2A… +0,,8266,0:46.925.552,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,8267,0:46.939.557,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8271,0:46.940.554,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,8272,0:46.940.557,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C B2 D2 74 49 87 14 24 FD E3 90 C3 03 E1 1C 2A… +0,,8276,0:46.941.554,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,8277,0:46.956.560,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 47 97 9D 5C F6 3D 52 7C 15 94 D2 B7 D2 0F 3F 3A… +0,,8281,0:46.957.556,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,8282,0:46.971.562,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8286,0:46.972.559,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,8287,0:46.972.562,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 47 97 9D 5C F6 3D 52 7C 15 94 D2 B7 D2 0F 3F 3A… +0,,8291,0:46.973.559,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,8292,0:46.988.564,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 2A 2A FC 14 DB A2 71 C6 94 EC 58 B9 B9 45 89… +0,,8296,0:46.989.561,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,8297,0:47.003.566,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8301,0:47.004.563,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,8302,0:47.004.566,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 2A 2A FC 14 DB A2 71 C6 94 EC 58 B9 B9 45 89… +0,,8306,0:47.005.563,15.004.916 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,8307,0:47.020.568,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E9 DD E0 93 40 94 A5 5E 7C 20 03 21 4F D8 E2 B7… +0,,8311,0:47.021.565,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,8312,0:47.035.571,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8316,0:47.036.567,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,8317,0:47.036.571,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E9 DD E0 93 40 94 A5 5E 7C 20 03 21 4F D8 E2 B7… +0,,8321,0:47.037.568,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,8322,0:47.052.573,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 49 9A CC 62 68 79 81 19 93 D0 E2 A7 7A 24 59… +0,,8326,0:47.053.570,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,8327,0:47.067.575,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8331,0:47.068.572,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,8332,0:47.068.575,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 49 9A CC 62 68 79 81 19 93 D0 E2 A7 7A 24 59… +0,,8336,0:47.069.572,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,8337,0:47.084.577,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 8D 5A FA 40 A2 B7 6B 07 62 5A FD 20 F9 3F 5B… +0,,8341,0:47.085.574,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,8342,0:47.099.579,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8346,0:47.100.576,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,8347,0:47.100.580,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA 8D 5A FA 40 A2 B7 6B 07 62 5A FD 20 F9 3F 5B… +0,,8351,0:47.101.576,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,8352,0:47.116.582,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F7 51 8E 04 26 EC 1B 99 35 68 3D 00 A0 08 5A 06… +0,,8356,0:47.117.579,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,8357,0:47.131.584,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8361,0:47.132.581,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,8362,0:47.132.584,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F7 51 8E 04 26 EC 1B 99 35 68 3D 00 A0 08 5A 06… +0,,8366,0:47.133.581,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,8367,0:47.148.586,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AF 5A D0 95 29 B0 25 11 FA 18 8A B8 60 83 BE CE… +0,,8371,0:47.149.583,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,8372,0:47.163.588,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8376,0:47.164.585,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,8377,0:47.164.588,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AF 5A D0 95 29 B0 25 11 FA 18 8A B8 60 83 BE CE… +0,,8381,0:47.165.585,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,8382,0:47.180.591,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 90 7F 12 E3 AC DA C6 B6 DC E0 BC 1C 0C 0A 24 C6… +0,,8386,0:47.181.588,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,8387,0:47.195.593,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8391,0:47.196.590,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,8392,0:47.196.593,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 90 7F 12 E3 AC DA C6 B6 DC E0 BC 1C 0C 0A 24 C6… +0,,8396,0:47.197.590,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,8397,0:47.212.595,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 95 88 3D 74 FD 72 B4 18 FC 9A 9A DC 43 93 14 3D… +0,,8401,0:47.213.592,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,8402,0:47.227.597,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8406,0:47.228.594,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,8407,0:47.228.597,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 95 88 3D 74 FD 72 B4 18 FC 9A 9A DC 43 93 14 3D… +0,,8411,0:47.229.594,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,8412,0:47.244.600,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 77 09 C3 AD 4C FF E5 3F 58 F1 18 C3 61 B7 B2 89… +0,,8416,0:47.245.596,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,8417,0:47.259.602,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8421,0:47.260.598,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,8422,0:47.260.602,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 77 09 C3 AD 4C FF E5 3F 58 F1 18 C3 61 B7 B2 89… +0,,8426,0:47.261.599,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,8427,0:47.276.604,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E 35 83 90 7D B3 CB 1E 04 AC CE 0B 1B F5 4D EC… +0,,8431,0:47.277.601,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,8432,0:47.291.606,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8436,0:47.292.603,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,8437,0:47.292.606,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E 35 83 90 7D B3 CB 1E 04 AC CE 0B 1B F5 4D EC… +0,,8441,0:47.293.603,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,8442,0:47.308.608,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 8F 29 77 FC 78 16 E3 DA 36 B0 8E CE 35 96 BE… +0,,8446,0:47.309.605,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,8447,0:47.323.610,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8451,0:47.324.607,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,8452,0:47.324.611,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F 8F 29 77 FC 78 16 E3 DA 36 B0 8E CE 35 96 BE… +0,,8456,0:47.325.608,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,8457,0:47.340.613,50.729 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 9D F2 B1 F2 BE EE 6F 6A 08 6E 8F FF A0 BD 97… +0,,8461,0:47.341.610,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,8462,0:47.355.615,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8466,0:47.356.612,16.005.041 ms,,,,,[17 SOF],[Frames: 193 - 209] +0,,8467,0:47.372.617,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BE 31 B4 72 9C 8A 35 A9 B1 75 CB D6 41 39 A9 FF… +0,,8471,0:47.373.614,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,8472,0:47.387.619,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8476,0:47.388.616,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,8477,0:47.388.620,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BE 31 B4 72 9C 8A 35 A9 B1 75 CB D6 41 39 A9 FF… +0,,8481,0:47.389.616,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,8482,0:47.404.622,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 00 52 33 9F F5 CE 4F 38 49 D6 1D 37 9D C9 C7 78… +0,,8486,0:47.405.619,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,8487,0:47.419.624,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8491,0:47.420.621,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,8492,0:47.420.624,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 00 52 33 9F F5 CE 4F 38 49 D6 1D 37 9D C9 C7 78… +0,,8496,0:47.421.621,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,8497,0:47.436.626,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF FF 17 80 BE CB EE 5A D2 F9 5D 2C 69 9B 62 8D… +0,,8501,0:47.437.623,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,8502,0:47.451.628,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8506,0:47.452.625,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,8507,0:47.452.628,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF FF 17 80 BE CB EE 5A D2 F9 5D 2C 69 9B 62 8D… +0,,8511,0:47.453.625,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,8512,0:47.468.631,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 69 21 7D 17 0B DB 64 6D 9F 8F 15 64 B0 DB DF F8… +0,,8516,0:47.469.628,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,8517,0:47.483.633,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8521,0:47.484.630,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,8522,0:47.484.633,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 69 21 7D 17 0B DB 64 6D 9F 8F 15 64 B0 DB DF F8… +0,,8526,0:47.485.630,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,8527,0:47.500.635,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 01 CA AD B2 2B 69 F7 DB 6F 8C 5D 37 1E 91 03… +0,,8531,0:47.501.632,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,8532,0:47.515.637,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8536,0:47.516.634,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,8537,0:47.516.637,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 01 CA AD B2 2B 69 F7 DB 6F 8C 5D 37 1E 91 03… +0,,8541,0:47.517.634,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,8542,0:47.532.640,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 92 61 EE 17 92 3B 75 3F A9 2B 24 D3 57 EC 2A 81… +0,,8546,0:47.533.636,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,8547,0:47.547.642,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8551,0:47.548.638,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,8552,0:47.548.642,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 92 61 EE 17 92 3B 75 3F A9 2B 24 D3 57 EC 2A 81… +0,,8556,0:47.549.639,15.004.916 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,8557,0:47.564.644,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 00 3F 23 A5 E4 F9 63 23 8B D7 04 74 DB 89 57 D2… +0,,8561,0:47.565.641,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,8562,0:47.579.646,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8566,0:47.580.643,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,8567,0:47.580.646,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 00 3F 23 A5 E4 F9 63 23 8B D7 04 74 DB 89 57 D2… +0,,8571,0:47.581.643,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,8572,0:47.596.648,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A2 AA 08 EB 97 3F 8C 0A 19 F7 C2 42 41 1D 8A CC… +0,,8576,0:47.597.645,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,8577,0:47.611.650,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8581,0:47.612.647,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,8582,0:47.612.651,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A2 AA 08 EB 97 3F 8C 0A 19 F7 C2 42 41 1D 8A CC… +0,,8586,0:47.613.648,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,8587,0:47.628.653,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 80 09 8F AA 95 0A D1 53 7F 1E 60 15 CB 08 77 1E… +0,,8591,0:47.629.650,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,8592,0:47.643.655,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8596,0:47.644.652,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,8597,0:47.644.655,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 80 09 8F AA 95 0A D1 53 7F 1E 60 15 CB 08 77 1E… +0,,8601,0:47.645.652,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,8602,0:47.660.657,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3B 87 99 60 31 AA C5 A2 09 9C 61 36 04 62 71 0C… +0,,8606,0:47.661.654,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,8607,0:47.675.659,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8611,0:47.676.656,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,8612,0:47.676.660,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3B 87 99 60 31 AA C5 A2 09 9C 61 36 04 62 71 0C… +0,,8616,0:47.677.656,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,8617,0:47.692.662,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 92 DF 22 64 35 AB 09 45 8B 0D 84 1F E8 FF E6 15… +0,,8621,0:47.693.659,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,8622,0:47.707.664,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8626,0:47.708.661,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,8627,0:47.708.664,50.916 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 92 DF 22 64 35 AB 09 45 8B 0D 84 1F E8 FF E6 15… +0,,8631,0:47.709.661,15.004.916 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,8632,0:47.724.666,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 2C B3 55 23 D8 48 C3 05 E5 8C 59 69 E3 0F 47… +0,,8636,0:47.725.663,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,8637,0:47.739.668,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8641,0:47.740.665,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,8642,0:47.740.668,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 2C B3 55 23 D8 48 C3 05 E5 8C 59 69 E3 0F 47… +0,,8646,0:47.741.665,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,8647,0:47.756.671,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E F9 DA D2 70 DB 75 FD 63 B6 41 33 B1 68 CD AD… +0,,8651,0:47.757.667,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,8652,0:47.771.673,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8656,0:47.772.670,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,8657,0:47.772.673,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E F9 DA D2 70 DB 75 FD 63 B6 41 33 B1 68 CD AD… +0,,8661,0:47.773.670,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,8662,0:47.788.675,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 E8 2D 36 CA 38 AF 7A 8F 2C 03 6D C0 0C 10 7A… +0,,8666,0:47.789.672,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,8667,0:47.803.677,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8671,0:47.804.674,2.833 us,,,,,[1 SOF],[Frame: 641] +0,,8672,0:47.804.677,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 E8 2D 36 CA 38 AF 7A 8F 2C 03 6D C0 0C 10 7A… +0,,8676,0:47.805.674,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,8677,0:47.820.679,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 58 93 0C D0 37 BA 15 57 13 DD BA 43 1F 05 35… +0,,8681,0:47.821.676,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,8682,0:47.835.682,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8686,0:47.836.678,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,8687,0:47.836.682,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 58 93 0C D0 37 BA 15 57 13 DD BA 43 1F 05 35… +0,,8691,0:47.837.679,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,8692,0:47.852.684,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 55 48 12 E3 14 49 CD 83 C0 74 30 39 8E 23 87… +0,,8696,0:47.853.681,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,8697,0:47.867.686,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8701,0:47.868.683,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,8702,0:47.868.686,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 55 48 12 E3 14 49 CD 83 C0 74 30 39 8E 23 87… +0,,8706,0:47.869.683,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,8707,0:47.884.688,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 7D BF 04 A6 FC C2 A8 1B 50 D7 CA D9 31 EB 17… +0,,8711,0:47.885.685,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,8712,0:47.899.690,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8716,0:47.900.687,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,8717,0:47.900.691,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 7D BF 04 A6 FC C2 A8 1B 50 D7 CA D9 31 EB 17… +0,,8721,0:47.901.687,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,8722,0:47.916.693,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 E3 DC DA D7 4F 8F 29 AE 25 53 6F 36 48 0E 11… +0,,8726,0:47.917.690,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,8727,0:47.931.695,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8731,0:47.932.692,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,8732,0:47.932.695,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 E3 DC DA D7 4F 8F 29 AE 25 53 6F 36 48 0E 11… +0,,8736,0:47.933.692,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,8737,0:47.948.697,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 20 58 1F 4F 72 71 2C 9E D3 A1 81 96 C3 94 5C 63… +0,,8741,0:47.949.694,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,8742,0:47.963.699,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8746,0:47.964.696,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,8747,0:47.964.699,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 20 58 1F 4F 72 71 2C 9E D3 A1 81 96 C3 94 5C 63… +0,,8751,0:47.965.696,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,8752,0:47.980.702,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 1E 5F F8 0C BF 01 71 9E 15 3F DC D7 BF D4 A5… +0,,8756,0:47.981.699,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,8757,0:47.995.704,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8761,0:47.996.701,16.005.041 ms,,,,,[17 SOF],[Frames: 833 - 849] +0,,8762,0:48.012.706,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3A 7B 83 81 70 B4 73 4D C9 30 6C 06 50 B2 89 68… +0,,8766,0:48.013.703,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,8767,0:48.027.708,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8771,0:48.028.705,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,8772,0:48.028.708,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3A 7B 83 81 70 B4 73 4D C9 30 6C 06 50 B2 89 68… +0,,8776,0:48.029.705,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,8777,0:48.044.711,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B B5 75 EE 4B 2B 7B 00 77 46 34 E0 8F 1A CB 9D… +0,,8781,0:48.045.707,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,8782,0:48.059.713,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8786,0:48.060.710,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,8787,0:48.060.713,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B B5 75 EE 4B 2B 7B 00 77 46 34 E0 8F 1A CB 9D… +0,,8791,0:48.061.710,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,8792,0:48.076.715,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 F2 C1 D6 65 4C 6C EA 60 37 48 5C 04 E8 56 7C… +0,,8796,0:48.077.712,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,8797,0:48.091.717,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8801,0:48.092.714,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,8802,0:48.092.717,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 F2 C1 D6 65 4C 6C EA 60 37 48 5C 04 E8 56 7C… +0,,8806,0:48.093.714,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,8807,0:48.108.719,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BD CB 7E 16 B5 5B 61 D3 97 A7 5E 34 5B F7 ED 5B… +0,,8811,0:48.109.716,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,8812,0:48.123.722,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8816,0:48.124.718,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,8817,0:48.124.722,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BD CB 7E 16 B5 5B 61 D3 97 A7 5E 34 5B F7 ED 5B… +0,,8821,0:48.125.719,15.004.916 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,8822,0:48.140.724,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 05 BF 21 49 13 48 CE 3A BF 85 5A BC 4F AA 73… +0,,8826,0:48.141.721,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,8827,0:48.155.726,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8831,0:48.156.723,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,8832,0:48.156.726,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 05 BF 21 49 13 48 CE 3A BF 85 5A BC 4F AA 73… +0,,8836,0:48.157.723,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,8837,0:48.172.728,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 E6 30 C4 8B BD C1 38 0D E2 AA 8B D8 35 BF A8… +0,,8841,0:48.173.725,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,8842,0:48.187.730,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8846,0:48.188.727,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,8847,0:48.188.731,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F6 E6 30 C4 8B BD C1 38 0D E2 AA 8B D8 35 BF A8… +0,,8851,0:48.189.727,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,8852,0:48.204.733,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 C5 BB 19 2C 22 7F 75 35 AD 95 A0 F6 B3 6D FB… +0,,8856,0:48.205.730,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,8857,0:48.219.735,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8861,0:48.220.732,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,8862,0:48.220.735,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 C5 BB 19 2C 22 7F 75 35 AD 95 A0 F6 B3 6D FB… +0,,8866,0:48.221.732,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,8867,0:48.236.737,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 4A 8B 35 AA 97 10 02 44 1D DD A1 82 13 2A 1B… +0,,8871,0:48.237.734,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,8872,0:48.251.739,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8876,0:48.252.736,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,8877,0:48.252.739,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 4A 8B 35 AA 97 10 02 44 1D DD A1 82 13 2A 1B… +0,,8881,0:48.253.736,15.004.916 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,8882,0:48.268.742,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C0 8D 3D 65 86 7E F3 B5 80 C9 CE 5B 30 E5 9B F9… +0,,8886,0:48.269.739,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,8887,0:48.283.744,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8891,0:48.284.741,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,8892,0:48.284.744,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C0 8D 3D 65 86 7E F3 B5 80 C9 CE 5B 30 E5 9B F9… +0,,8896,0:48.285.741,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,8897,0:48.300.746,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 03 AD DC 3C F2 60 8F FE DB 83 19 70 2A 9B A5 53… +0,,8901,0:48.301.743,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,8902,0:48.315.748,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8906,0:48.316.745,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,8907,0:48.316.748,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 03 AD DC 3C F2 60 8F FE DB 83 19 70 2A 9B A5 53… +0,,8911,0:48.317.745,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,8912,0:48.332.751,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3B 4E 92 82 49 A6 8F 1A 69 CE 82 07 5A 99 B1 9D… +0,,8916,0:48.333.747,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,8917,0:48.347.753,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8921,0:48.348.750,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,8922,0:48.348.753,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3B 4E 92 82 49 A6 8F 1A 69 CE 82 07 5A 99 B1 9D… +0,,8926,0:48.349.750,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,8927,0:48.364.755,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 C1 A1 61 56 11 59 7B 49 E4 20 63 E1 F9 1E B7… +0,,8931,0:48.365.752,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,8932,0:48.379.757,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8936,0:48.380.754,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,8937,0:48.380.757,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 C1 A1 61 56 11 59 7B 49 E4 20 63 E1 F9 1E B7… +0,,8941,0:48.381.754,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,8942,0:48.396.759,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 5E 4A 29 B7 68 21 49 02 62 B6 06 F0 71 49 7F… +0,,8946,0:48.397.756,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,8947,0:48.411.762,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8951,0:48.412.758,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,8952,0:48.412.762,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 5E 4A 29 B7 68 21 49 02 62 B6 06 F0 71 49 7F… +0,,8956,0:48.413.759,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,8957,0:48.428.764,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 55 20 CC F9 26 06 2B 8F 67 80 1B 3B A9 10 2F 79… +0,,8961,0:48.429.761,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,8962,0:48.443.766,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8966,0:48.444.763,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,8967,0:48.444.766,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 55 20 CC F9 26 06 2B 8F 67 80 1B 3B A9 10 2F 79… +0,,8971,0:48.445.763,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,8972,0:48.460.768,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A0 92 7F 03 52 A8 DA A1 5A AA 2D 30 E3 6E D7 18… +0,,8976,0:48.461.765,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,8977,0:48.475.770,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8981,0:48.476.767,2.833 us,,,,,[1 SOF],[Frame: 1313] +0,,8982,0:48.476.771,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A0 92 7F 03 52 A8 DA A1 5A AA 2D 30 E3 6E D7 18… +0,,8986,0:48.477.767,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,8987,0:48.492.773,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 DD 6D 63 98 60 58 54 B8 73 1D 3A A8 B6 FA A3… +0,,8991,0:48.493.770,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,8992,0:48.507.775,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,8996,0:48.508.772,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,8997,0:48.508.775,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 DD 6D 63 98 60 58 54 B8 73 1D 3A A8 B6 FA A3… +0,,9001,0:48.509.772,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,9002,0:48.524.777,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 D8 34 FF F3 85 D9 54 91 0F CE 86 F2 44 F0 63… +0,,9006,0:48.525.774,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,9007,0:48.539.779,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9011,0:48.540.776,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,9012,0:48.540.779,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 D8 34 FF F3 85 D9 54 91 0F CE 86 F2 44 F0 63… +0,,9016,0:48.541.776,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,9017,0:48.556.782,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 92 CB 27 8E 68 62 7B 06 41 BD CB 8C B8 31 73 6D… +0,,9021,0:48.557.779,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,9022,0:48.571.784,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9026,0:48.572.781,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,9027,0:48.572.784,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 92 CB 27 8E 68 62 7B 06 41 BD CB 8C B8 31 73 6D… +0,,9031,0:48.573.781,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,9032,0:48.588.786,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE C4 1F 84 D5 15 1F 44 76 9D 56 5E 5D DA 77 A4… +0,,9036,0:48.589.783,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,9037,0:48.603.788,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9041,0:48.604.785,16.005.041 ms,,,,,[17 SOF],[Frames: 1441 - 1457] +0,,9042,0:48.620.791,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5D B1 E8 4F A2 1A 69 89 B8 3D 1F 90 E9 59 80 3E… +0,,9046,0:48.621.787,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,9047,0:48.635.793,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9051,0:48.636.790,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,9052,0:48.636.793,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5D B1 E8 4F A2 1A 69 89 B8 3D 1F 90 E9 59 80 3E… +0,,9056,0:48.637.790,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,9057,0:48.652.795,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C3 32 DE 5F E4 40 1B CA 6B 6A 93 02 D6 7B 9F B9… +0,,9061,0:48.653.792,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,9062,0:48.667.797,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9066,0:48.668.794,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,9067,0:48.668.797,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C3 32 DE 5F E4 40 1B CA 6B 6A 93 02 D6 7B 9F B9… +0,,9071,0:48.669.794,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,9072,0:48.684.799,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4F FD 6F 0F 24 49 63 DB 08 C5 AC 77 EB 5E 19 C9… +0,,9076,0:48.685.796,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,9077,0:48.699.802,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9081,0:48.700.798,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,9082,0:48.700.802,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4F FD 6F 0F 24 49 63 DB 08 C5 AC 77 EB 5E 19 C9… +0,,9086,0:48.701.799,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,9087,0:48.716.804,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F5 17 5F D7 CF A7 46 D1 79 48 37 67 E4 42 A0 FF… +0,,9091,0:48.717.801,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,9092,0:48.731.806,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9096,0:48.732.803,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,9097,0:48.732.806,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F5 17 5F D7 CF A7 46 D1 79 48 37 67 E4 42 A0 FF… +0,,9101,0:48.733.803,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,9102,0:48.748.808,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3B DD B7 F0 CD A9 E9 C9 B6 10 85 F1 84 42 56 F7… +0,,9106,0:48.749.805,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,9107,0:48.763.810,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9111,0:48.764.807,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,9112,0:48.764.811,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3B DD B7 F0 CD A9 E9 C9 B6 10 85 F1 84 42 56 F7… +0,,9116,0:48.765.807,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,9117,0:48.780.813,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 1F 36 E2 77 C8 8B EF B6 BA 9E CC A8 FB D5 37… +0,,9121,0:48.781.810,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,9122,0:48.795.815,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9126,0:48.796.812,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,9127,0:48.796.815,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 1F 36 E2 77 C8 8B EF B6 BA 9E CC A8 FB D5 37… +0,,9131,0:48.797.812,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,9132,0:48.812.817,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 65 DB 99 9E 17 4E 46 06 3E E5 E6 76 CD DB F3 DB… +0,,9136,0:48.813.814,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,9137,0:48.827.819,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9141,0:48.828.816,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,9142,0:48.828.819,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 65 DB 99 9E 17 4E 46 06 3E E5 E6 76 CD DB F3 DB… +0,,9146,0:48.829.816,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,9147,0:48.844.822,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7B EB 7B 76 20 69 8A D7 7E 51 F8 CA 84 7B 2E 91… +0,,9151,0:48.845.819,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,9152,0:48.859.824,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9156,0:48.860.821,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,9157,0:48.860.824,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7B EB 7B 76 20 69 8A D7 7E 51 F8 CA 84 7B 2E 91… +0,,9161,0:48.861.821,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,9162,0:48.876.826,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D ED 8A B9 02 A8 DF 0C 44 B3 3E BD B2 90 15 04… +0,,9166,0:48.877.823,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,9167,0:48.891.828,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9171,0:48.892.825,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,9172,0:48.892.828,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D ED 8A B9 02 A8 DF 0C 44 B3 3E BD B2 90 15 04… +0,,9176,0:48.893.825,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,9177,0:48.908.831,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA E7 C6 DA 19 22 9A 03 BA 51 C8 05 3E 8B 1E E3… +0,,9181,0:48.909.827,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,9182,0:48.923.833,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9186,0:48.924.830,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,9187,0:48.924.833,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA E7 C6 DA 19 22 9A 03 BA 51 C8 05 3E 8B 1E E3… +0,,9191,0:48.925.830,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,9192,0:48.940.835,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D E4 0D C5 CE 4A C2 99 7F 0C A2 B8 5D 81 27 21… +0,,9196,0:48.941.832,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,9197,0:48.955.837,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9201,0:48.956.834,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,9202,0:48.956.837,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D E4 0D C5 CE 4A C2 99 7F 0C A2 B8 5D 81 27 21… +0,,9206,0:48.957.834,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,9207,0:48.972.839,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 13 6F 02 EB D6 6E B1 46 26 6A 76 E1 A7 46 3B… +0,,9211,0:48.973.836,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,9212,0:48.987.842,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9216,0:48.988.838,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,9217,0:48.988.842,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 13 6F 02 EB D6 6E B1 46 26 6A 76 E1 A7 46 3B… +0,,9221,0:48.989.839,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,9222,0:49.004.844,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 49 5A 42 60 C5 BC 1E A0 7F F5 CC F2 69 FF 85… +0,,9226,0:49.005.841,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,9227,0:49.019.846,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9231,0:49.020.843,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,9232,0:49.020.846,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 31 49 5A 42 60 C5 BC 1E A0 7F F5 CC F2 69 FF 85… +0,,9236,0:49.021.843,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,9237,0:49.036.848,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 5E 16 AE 21 C9 67 9C 3D 02 DE B4 39 EF 8A 7B… +0,,9241,0:49.037.845,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,9242,0:49.051.850,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9246,0:49.052.847,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,9247,0:49.052.851,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 5E 16 AE 21 C9 67 9C 3D 02 DE B4 39 EF 8A 7B… +0,,9251,0:49.053.847,15.004.916 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,9252,0:49.068.853,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F5 31 D6 F5 4A 05 44 5D C4 26 8A 50 45 B5 E7 46… +0,,9256,0:49.069.850,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,9257,0:49.083.855,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9261,0:49.084.852,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,9262,0:49.084.855,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F5 31 D6 F5 4A 05 44 5D C4 26 8A 50 45 B5 E7 46… +0,,9266,0:49.085.852,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,9267,0:49.100.857,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 52 8F 32 AB 23 2A 8A 3D 33 40 E3 03 2A 18 D0 CA… +0,,9271,0:49.101.854,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,9272,0:49.115.859,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9276,0:49.116.856,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,9277,0:49.116.859,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 52 8F 32 AB 23 2A 8A 3D 33 40 E3 03 2A 18 D0 CA… +0,,9281,0:49.117.856,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,9282,0:49.132.862,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 25 0B 52 4A 64 E0 B2 0F 3D CC 5C D0 9A 14 A6 03… +0,,9286,0:49.133.859,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,9287,0:49.147.864,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9291,0:49.148.861,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,9292,0:49.148.864,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 25 0B 52 4A 64 E0 B2 0F 3D CC 5C D0 9A 14 A6 03… +0,,9296,0:49.149.861,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,9297,0:49.164.866,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F9 DE A1 26 72 4E 14 69 C1 F3 C0 CE D6 6F D1 E9… +0,,9301,0:49.165.863,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,9302,0:49.179.868,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9306,0:49.180.865,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,9307,0:49.180.868,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F9 DE A1 26 72 4E 14 69 C1 F3 C0 CE D6 6F D1 E9… +0,,9311,0:49.181.865,15.005.000 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,9312,0:49.196.871,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 91 86 DF 44 17 5D 5A 37 1C 94 85 29 9F F8 01 B9… +0,,9316,0:49.197.867,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,9317,0:49.211.873,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9321,0:49.212.869,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,9322,0:49.212.873,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 91 86 DF 44 17 5D 5A 37 1C 94 85 29 9F F8 01 B9… +0,,9326,0:49.213.870,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,9327,0:49.228.875,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 26 B5 B9 44 A1 00 F9 B1 61 A7 C3 AD 02 3B 27 6F… +0,,9331,0:49.229.872,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,9332,0:49.243.877,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9336,0:49.244.874,16.005.041 ms,,,,,[17 SOF],[Frames: 33 - 49] +0,,9337,0:49.260.879,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3F 26 7C 8A F4 97 6F 4E FF 60 10 C7 32 83 59 4A… +0,,9341,0:49.261.876,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,9342,0:49.275.881,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9346,0:49.276.878,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,9347,0:49.276.882,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3F 26 7C 8A F4 97 6F 4E FF 60 10 C7 32 83 59 4A… +0,,9351,0:49.277.879,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,9352,0:49.292.884,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FA 4B DE E4 9D 23 40 79 E3 A9 26 B8 52 94 35 4A… +0,,9356,0:49.293.881,14.004.750 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,9357,0:49.307.886,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9361,0:49.308.883,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,9362,0:49.308.886,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FA 4B DE E4 9D 23 40 79 E3 A9 26 B8 52 94 35 4A… +0,,9366,0:49.309.883,15.004.916 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,9367,0:49.324.888,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 C7 F8 32 88 53 F6 7B FE C7 25 6A 03 B1 0A A7… +0,,9371,0:49.325.885,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,9372,0:49.339.890,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9376,0:49.340.887,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,9377,0:49.340.891,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 C7 F8 32 88 53 F6 7B FE C7 25 6A 03 B1 0A A7… +0,,9381,0:49.341.887,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,9382,0:49.356.893,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D 9F BE 0D AE 40 64 05 20 CD 2A 20 26 E1 54 F2… +0,,9386,0:49.357.890,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,9387,0:49.371.895,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9391,0:49.372.892,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,9392,0:49.372.895,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D 9F BE 0D AE 40 64 05 20 CD 2A 20 26 E1 54 F2… +0,,9396,0:49.373.892,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,9397,0:49.388.897,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 77 2F 26 47 C7 EE 79 C7 E6 67 26 03 88 FF F4 AC… +0,,9401,0:49.389.894,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,9402,0:49.403.899,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9406,0:49.404.896,2.833 us,,,,,[1 SOF],[Frame: 193] +0,,9407,0:49.404.899,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 77 2F 26 47 C7 EE 79 C7 E6 67 26 03 88 FF F4 AC… +0,,9411,0:49.405.896,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,9412,0:49.420.902,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0B A4 87 D0 BD 0C 92 F9 A8 0D 48 14 64 08 BE 68… +0,,9416,0:49.421.899,14.004.750 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,9417,0:49.435.904,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9421,0:49.436.901,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,9422,0:49.436.904,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0B A4 87 D0 BD 0C 92 F9 A8 0D 48 14 64 08 BE 68… +0,,9426,0:49.437.901,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,9427,0:49.452.906,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D F2 D5 85 8B 73 E4 AB 34 C7 00 13 5B 62 7B AB… +0,,9431,0:49.453.903,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,9432,0:49.467.908,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9436,0:49.468.905,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,9437,0:49.468.908,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D F2 D5 85 8B 73 E4 AB 34 C7 00 13 5B 62 7B AB… +0,,9441,0:49.469.905,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,9442,0:49.484.910,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 ED 20 C7 6B 9B 96 AB C1 2C 1F 0E CF 99 E8 A0 8E… +0,,9446,0:49.485.907,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,9447,0:49.499.913,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9451,0:49.500.909,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,9452,0:49.500.913,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 ED 20 C7 6B 9B 96 AB C1 2C 1F 0E CF 99 E8 A0 8E… +0,,9456,0:49.501.910,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,9457,0:49.516.915,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 21 97 76 0F 80 04 25 15 91 80 8F A1 C7 C8 AE 8B… +0,,9461,0:49.517.912,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,9462,0:49.531.917,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9466,0:49.532.914,2.833 us,,,,,[1 SOF],[Frame: 321] +0,,9467,0:49.532.917,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 21 97 76 0F 80 04 25 15 91 80 8F A1 C7 C8 AE 8B… +0,,9471,0:49.533.914,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,9472,0:49.548.919,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 7F E1 6A 03 E8 49 9E E2 90 FB B3 F2 98 49 37… +0,,9476,0:49.549.916,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,9477,0:49.563.921,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9481,0:49.564.918,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,9482,0:49.564.922,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 7F E1 6A 03 E8 49 9E E2 90 FB B3 F2 98 49 37… +0,,9486,0:49.565.918,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,9487,0:49.580.924,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 FE B0 71 3B 46 86 ED 26 2C D7 07 49 5A 8B 6F… +0,,9491,0:49.581.921,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,9492,0:49.595.926,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9496,0:49.596.923,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,9497,0:49.596.926,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 FE B0 71 3B 46 86 ED 26 2C D7 07 49 5A 8B 6F… +0,,9501,0:49.597.923,15.004.916 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,9502,0:49.612.928,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A B4 A2 BC 34 AA 17 F5 41 F1 77 64 92 75 D2 A7… +0,,9506,0:49.613.925,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,9507,0:49.627.930,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9511,0:49.628.927,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,9512,0:49.628.930,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A B4 A2 BC 34 AA 17 F5 41 F1 77 64 92 75 D2 A7… +0,,9516,0:49.629.927,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,9517,0:49.644.933,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A F2 7F 10 65 7B 31 E1 74 00 A2 AE B5 8B 4F F9… +0,,9521,0:49.645.930,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,9522,0:49.659.935,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9526,0:49.660.932,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,9527,0:49.660.935,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7A F2 7F 10 65 7B 31 E1 74 00 A2 AE B5 8B 4F F9… +0,,9531,0:49.661.932,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,9532,0:49.676.937,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6B DC 37 4D 74 4C 3E 40 F0 7E 70 5B 63 EB 70 A0… +0,,9536,0:49.677.934,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,9537,0:49.691.939,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9541,0:49.692.936,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,9542,0:49.692.939,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6B DC 37 4D 74 4C 3E 40 F0 7E 70 5B 63 EB 70 A0… +0,,9546,0:49.693.936,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,9547,0:49.708.942,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC 34 F2 B2 47 0A 9A 67 A5 4B 4C 95 ED 4E 8D 0B… +0,,9551,0:49.709.938,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,9552,0:49.723.944,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9556,0:49.724.941,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,9557,0:49.724.944,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC 34 F2 B2 47 0A 9A 67 A5 4B 4C 95 ED 4E 8D 0B… +0,,9561,0:49.725.941,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,9562,0:49.740.946,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B2 28 E2 E5 68 86 B7 7E 2D 16 3F E6 34 EB 01 2A… +0,,9566,0:49.741.943,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,9567,0:49.755.948,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9571,0:49.756.945,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,9572,0:49.756.948,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B2 28 E2 E5 68 86 B7 7E 2D 16 3F E6 34 EB 01 2A… +0,,9576,0:49.757.945,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,9577,0:49.772.950,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B DB 19 1E 00 5F 77 47 CC 68 3B 3B 2C 9F C2 70… +0,,9581,0:49.773.947,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,9582,0:49.787.953,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9586,0:49.788.949,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,9587,0:49.788.953,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B DB 19 1E 00 5F 77 47 CC 68 3B 3B 2C 9F C2 70… +0,,9591,0:49.789.950,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,9592,0:49.804.955,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2E 88 54 DF A5 C7 35 01 8F CE 64 E2 1D 28 36 6E… +0,,9596,0:49.805.952,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,9597,0:49.819.957,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9601,0:49.820.954,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,9602,0:49.820.957,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2E 88 54 DF A5 C7 35 01 8F CE 64 E2 1D 28 36 6E… +0,,9606,0:49.821.954,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,9607,0:49.836.959,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0E 77 25 8E 74 95 1A F7 92 21 89 93 56 F6 6A 99… +0,,9611,0:49.837.956,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,9612,0:49.851.961,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9616,0:49.852.958,16.005.041 ms,,,,,[17 SOF],[Frames: 641 - 657] +0,,9617,0:49.868.964,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 46 4C 51 C6 B9 A1 89 91 68 68 E7 0E C5 5F 7C 9D… +0,,9621,0:49.869.961,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,9622,0:49.883.966,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9626,0:49.884.963,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,9627,0:49.884.966,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 46 4C 51 C6 B9 A1 89 91 68 68 E7 0E C5 5F 7C 9D… +0,,9631,0:49.885.963,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,9632,0:49.900.968,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C 17 88 0C DE 3D 36 A9 24 35 0B 23 64 9F EF 54… +0,,9636,0:49.901.965,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,9637,0:49.915.970,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9641,0:49.916.967,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,9642,0:49.916.970,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C 17 88 0C DE 3D 36 A9 24 35 0B 23 64 9F EF 54… +0,,9646,0:49.917.967,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,9647,0:49.932.973,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AF F3 1D F8 2A 49 CF EE DA ED 83 AD 82 6F 28 45… +0,,9651,0:49.933.970,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,9652,0:49.947.975,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9656,0:49.948.972,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,9657,0:49.948.975,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AF F3 1D F8 2A 49 CF EE DA ED 83 AD 82 6F 28 45… +0,,9661,0:49.949.972,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,9662,0:49.964.977,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0B 89 28 D5 73 5E 8E 40 7E 8B 0D 50 44 C0 F7 0A… +0,,9666,0:49.965.974,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,9667,0:49.979.979,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9671,0:49.980.976,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,9672,0:49.980.979,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0B 89 28 D5 73 5E 8E 40 7E 8B 0D 50 44 C0 F7 0A… +0,,9676,0:49.981.976,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,9677,0:49.996.982,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 07 9A 9E 17 74 84 6F E0 CD F8 F0 E5 4B F9 D8… +0,,9681,0:49.997.978,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,9682,0:50.011.984,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9686,0:50.012.981,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,9687,0:50.012.984,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 07 9A 9E 17 74 84 6F E0 CD F8 F0 E5 4B F9 D8… +0,,9691,0:50.013.981,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,9692,0:50.028.986,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 CA F5 B2 1D DF 0B 74 0A A2 0B 43 58 3B 98 4D… +0,,9696,0:50.029.983,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,9697,0:50.043.988,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9701,0:50.044.985,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,9702,0:50.044.988,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 CA F5 B2 1D DF 0B 74 0A A2 0B 43 58 3B 98 4D… +0,,9706,0:50.045.985,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,9707,0:50.060.990,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 9A D6 6A CC C5 E6 3F 26 25 0B 11 5C 04 15 19… +0,,9711,0:50.061.987,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,9712,0:50.075.993,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9716,0:50.076.989,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,9717,0:50.076.993,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 9A D6 6A CC C5 E6 3F 26 25 0B 11 5C 04 15 19… +0,,9721,0:50.077.990,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,9722,0:50.092.995,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3C D9 86 A2 C5 DD 30 6B 75 9F 61 22 62 89 2A DE… +0,,9726,0:50.093.992,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,9727,0:50.107.997,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9731,0:50.108.994,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,9732,0:50.108.997,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3C D9 86 A2 C5 DD 30 6B 75 9F 61 22 62 89 2A DE… +0,,9736,0:50.109.994,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,9737,0:50.124.999,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 12 0D 98 BA C4 8D F5 49 EF 49 97 BE 3A 97 25… +0,,9741,0:50.125.996,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,9742,0:50.140.001,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9746,0:50.140.998,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,9747,0:50.141.002,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 12 0D 98 BA C4 8D F5 49 EF 49 97 BE 3A 97 25… +0,,9751,0:50.141.998,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,9752,0:50.157.004,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9F 71 49 FB 29 02 65 C4 7B E6 F7 2E 40 6C 4D 35… +0,,9756,0:50.158.001,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,9757,0:50.172.006,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9761,0:50.173.003,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,9762,0:50.173.006,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9F 71 49 FB 29 02 65 C4 7B E6 F7 2E 40 6C 4D 35… +0,,9766,0:50.174.003,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,9767,0:50.189.008,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 C0 1A 13 50 3E B1 BC 13 55 18 09 54 7A 9B B5… +0,,9771,0:50.190.005,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,9772,0:50.204.010,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9776,0:50.205.007,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,9777,0:50.205.010,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 C0 1A 13 50 3E B1 BC 13 55 18 09 54 7A 9B B5… +0,,9781,0:50.206.007,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,9782,0:50.221.013,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 3B 85 9C 3C 02 CC 6E 9B 85 37 F0 FE CD 65 D1… +0,,9786,0:50.222.010,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,9787,0:50.236.015,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9791,0:50.237.012,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,9792,0:50.237.015,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 3B 85 9C 3C 02 CC 6E 9B 85 37 F0 FE CD 65 D1… +0,,9796,0:50.238.012,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,9797,0:50.253.017,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7B 4F AB 80 6E E0 CA EC B2 A7 74 6C 64 64 5F 75… +0,,9801,0:50.254.014,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,9802,0:50.268.019,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9806,0:50.269.016,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,9807,0:50.269.019,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7B 4F AB 80 6E E0 CA EC B2 A7 74 6C 64 64 5F 75… +0,,9811,0:50.270.016,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,9812,0:50.285.022,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 87 FD 73 89 5D 62 F2 EE 28 71 C4 07 9A 68 1A B9… +0,,9816,0:50.286.018,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,9817,0:50.300.024,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9821,0:50.301.021,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,9822,0:50.301.024,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 87 FD 73 89 5D 62 F2 EE 28 71 C4 07 9A 68 1A B9… +0,,9826,0:50.302.021,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,9827,0:50.317.026,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 3D 2D 70 AE BA 60 A8 5E ED 42 4E F8 35 5C FB… +0,,9831,0:50.318.023,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,9832,0:50.332.028,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9836,0:50.333.025,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,9837,0:50.333.028,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 3D 2D 70 AE BA 60 A8 5E ED 42 4E F8 35 5C FB… +0,,9841,0:50.334.025,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,9842,0:50.349.030,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C 7B C4 EB E8 CA 34 27 40 1C 62 92 06 F6 7C C0… +0,,9846,0:50.350.027,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,9847,0:50.364.033,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9851,0:50.365.029,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,9852,0:50.365.033,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7C 7B C4 EB E8 CA 34 27 40 1C 62 92 06 F6 7C C0… +0,,9856,0:50.366.030,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,9857,0:50.381.035,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 90 E1 74 6F 65 8F 45 5A 1B 9A D7 C6 77 45 DC 2C… +0,,9861,0:50.382.032,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,9862,0:50.396.037,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9866,0:50.397.034,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,9867,0:50.397.037,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 90 E1 74 6F 65 8F 45 5A 1B 9A D7 C6 77 45 DC 2C… +0,,9871,0:50.398.034,15.004.916 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,9872,0:50.413.039,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE 63 DA E1 21 78 0C 18 A8 32 A7 77 7B 1A 38 22… +0,,9876,0:50.414.036,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,9877,0:50.428.041,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9881,0:50.429.038,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,9882,0:50.429.042,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DE 63 DA E1 21 78 0C 18 A8 32 A7 77 7B 1A 38 22… +0,,9886,0:50.430.038,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,9887,0:50.445.044,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AD 21 B1 E2 9D E8 60 54 B6 67 08 5D 4D BD 0F F4… +0,,9891,0:50.446.041,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,9892,0:50.460.046,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9896,0:50.461.043,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,9897,0:50.461.046,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AD 21 B1 E2 9D E8 60 54 B6 67 08 5D 4D BD 0F F4… +0,,9901,0:50.462.043,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,9902,0:50.477.048,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 71 CD C1 F6 97 86 C2 A0 DE CB A3 3A 72 3D 25… +0,,9906,0:50.478.045,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,9907,0:50.492.050,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9911,0:50.493.047,16.005.041 ms,,,,,[17 SOF],[Frames: 1281 - 1297] +0,,9912,0:50.509.053,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 09 62 4C 5C 39 4D 2D 7F 67 44 00 2B 99 91 92 5D… +0,,9916,0:50.510.050,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,9917,0:50.524.055,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9921,0:50.525.052,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,9922,0:50.525.055,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 09 62 4C 5C 39 4D 2D 7F 67 44 00 2B 99 91 92 5D… +0,,9926,0:50.526.052,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,9927,0:50.541.057,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3C 7E F2 A6 BD A4 E2 68 28 EB 30 2B CE 4F D7 0E… +0,,9931,0:50.542.054,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,9932,0:50.556.059,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9936,0:50.557.056,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,9937,0:50.557.059,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3C 7E F2 A6 BD A4 E2 68 28 EB 30 2B CE 4F D7 0E… +0,,9941,0:50.558.056,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,9942,0:50.573.062,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C6 B0 26 1C D3 0F A8 4E 44 2C 8A 9F 69 4D 7F B1… +0,,9946,0:50.574.058,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,9947,0:50.588.064,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9951,0:50.589.061,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,9952,0:50.589.064,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C6 B0 26 1C D3 0F A8 4E 44 2C 8A 9F 69 4D 7F B1… +0,,9956,0:50.590.061,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,9957,0:50.605.066,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 A7 87 18 D8 2F 87 E0 54 97 D1 C8 FF 89 37 34… +0,,9961,0:50.606.063,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,9962,0:50.620.068,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9966,0:50.621.065,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,9967,0:50.621.068,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 A7 87 18 D8 2F 87 E0 54 97 D1 C8 FF 89 37 34… +0,,9971,0:50.622.065,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,9972,0:50.637.070,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C0 6B 57 40 27 E0 F4 A9 5A 21 93 B3 87 C5 39 66… +0,,9976,0:50.638.067,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,9977,0:50.652.073,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9981,0:50.653.069,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,9982,0:50.653.073,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C0 6B 57 40 27 E0 F4 A9 5A 21 93 B3 87 C5 39 66… +0,,9986,0:50.654.070,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,9987,0:50.669.075,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 E6 31 50 F3 9D 5D 67 0B 9C B5 D2 9A EE 5E EF… +0,,9991,0:50.670.072,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,9992,0:50.684.077,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,9996,0:50.685.074,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,9997,0:50.685.077,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 E6 31 50 F3 9D 5D 67 0B 9C B5 D2 9A EE 5E EF… +0,,10001,0:50.686.074,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,10002,0:50.701.079,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E A3 4D 38 80 DB E3 F1 04 6F 0E 4F 4B A7 20 11… +0,,10006,0:50.702.076,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,10007,0:50.716.081,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10011,0:50.717.078,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,10012,0:50.717.082,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E A3 4D 38 80 DB E3 F1 04 6F 0E 4F 4B A7 20 11… +0,,10016,0:50.718.078,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,10017,0:50.733.084,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DA 6B A3 45 A9 D9 CC A3 40 FC 50 4C 42 7B 0F D9… +0,,10021,0:50.734.081,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,10022,0:50.748.086,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10026,0:50.749.083,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,10027,0:50.749.086,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DA 6B A3 45 A9 D9 CC A3 40 FC 50 4C 42 7B 0F D9… +0,,10031,0:50.750.083,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,10032,0:50.765.088,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 A7 36 B0 2D AA 58 84 C4 98 43 67 6F A1 C3 57… +0,,10036,0:50.766.085,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,10037,0:50.780.090,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10041,0:50.781.087,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,10042,0:50.781.090,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 A7 36 B0 2D AA 58 84 C4 98 43 67 6F A1 C3 57… +0,,10046,0:50.782.087,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,10047,0:50.797.093,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 0B F6 B5 4A 36 C2 DD 75 D7 81 01 39 E7 38 5E… +0,,10051,0:50.798.090,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,10052,0:50.812.095,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10056,0:50.813.092,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,10057,0:50.813.095,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 0B F6 B5 4A 36 C2 DD 75 D7 81 01 39 E7 38 5E… +0,,10061,0:50.814.092,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,10062,0:50.829.097,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B7 29 D0 EF 80 7E 3F 41 74 82 E8 EC 90 12 86 4B… +0,,10066,0:50.830.094,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,10067,0:50.844.099,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10071,0:50.845.096,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,10072,0:50.845.099,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B7 29 D0 EF 80 7E 3F 41 74 82 E8 EC 90 12 86 4B… +0,,10076,0:50.846.096,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,10077,0:50.861.102,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BF 5A FE 00 5C 4E 78 A4 7C 8E 22 98 6D CD ED F6… +0,,10081,0:50.862.098,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,10082,0:50.876.104,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10086,0:50.877.100,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,10087,0:50.877.104,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BF 5A FE 00 5C 4E 78 A4 7C 8E 22 98 6D CD ED F6… +0,,10091,0:50.878.101,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,10092,0:50.893.106,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CA 0F 19 C3 0B DF 60 C6 15 48 7A 96 35 3C 56 FA… +0,,10096,0:50.894.103,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,10097,0:50.908.108,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10101,0:50.909.105,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,10102,0:50.909.108,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CA 0F 19 C3 0B DF 60 C6 15 48 7A 96 35 3C 56 FA… +0,,10106,0:50.910.105,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,10107,0:50.925.110,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 46 3F 28 5B 51 A0 F4 A9 1E 3B BA 98 A2 BA 30 50… +0,,10111,0:50.926.107,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,10112,0:50.940.112,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10116,0:50.941.109,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,10117,0:50.941.113,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 46 3F 28 5B 51 A0 F4 A9 1E 3B BA 98 A2 BA 30 50… +0,,10121,0:50.942.110,15.004.916 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,10122,0:50.957.115,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 73 F9 DE 35 47 23 8E C7 16 69 02 1F B4 40 F0 28… +0,,10126,0:50.958.112,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,10127,0:50.972.117,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10131,0:50.973.114,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,10132,0:50.973.117,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 73 F9 DE 35 47 23 8E C7 16 69 02 1F B4 40 F0 28… +0,,10136,0:50.974.114,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,10137,0:50.989.119,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6E 2D 06 A9 F1 EA 3B 8C EA 05 19 89 75 33 8B 5F… +0,,10141,0:50.990.116,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,10142,0:51.004.121,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10146,0:51.005.118,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,10147,0:51.005.122,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6E 2D 06 A9 F1 EA 3B 8C EA 05 19 89 75 33 8B 5F… +0,,10151,0:51.006.118,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,10152,0:51.021.124,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4D 1A 66 27 88 9A 4E B2 49 A9 F9 E1 F3 CA 2D 0F… +0,,10156,0:51.022.121,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,10157,0:51.036.126,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10161,0:51.037.123,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,10162,0:51.037.126,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D 1A 66 27 88 9A 4E B2 49 A9 F9 E1 F3 CA 2D 0F… +0,,10166,0:51.038.123,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,10167,0:51.053.128,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F0 B6 73 CF 23 69 40 26 21 DE C4 B0 78 A3 43 B2… +0,,10171,0:51.054.125,14.004.750 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,10172,0:51.068.130,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10176,0:51.069.127,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,10177,0:51.069.130,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F0 B6 73 CF 23 69 40 26 21 DE C4 B0 78 A3 43 B2… +0,,10181,0:51.070.127,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,10182,0:51.085.133,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F 0C DA BC 78 60 33 30 60 EF F4 20 B8 71 E1 1C… +0,,10186,0:51.086.130,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,10187,0:51.100.135,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10191,0:51.101.132,16.005.041 ms,,,,,[17 SOF],[Frames: 1889 - 1905] +0,,10192,0:51.117.137,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 47 0F A2 15 57 A7 E7 B7 55 27 7B B9 62 6E E5 45… +0,,10196,0:51.118.134,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,10197,0:51.132.139,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10201,0:51.133.136,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,10202,0:51.133.139,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 47 0F A2 15 57 A7 E7 B7 55 27 7B B9 62 6E E5 45… +0,,10206,0:51.134.136,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,10207,0:51.149.142,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 C2 6F 6E 27 07 2C 43 A4 0A 60 C8 52 6A 68 B3… +0,,10211,0:51.150.138,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,10212,0:51.164.144,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10216,0:51.165.140,2.833 us,,,,,[1 SOF],[Frame: 1953] +0,,10217,0:51.165.144,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 C2 6F 6E 27 07 2C 43 A4 0A 60 C8 52 6A 68 B3… +0,,10221,0:51.166.141,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,10222,0:51.181.146,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1E 8B FB 80 53 D9 E2 29 38 FA 13 5A 9C 4B 71 E9… +0,,10226,0:51.182.143,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,10227,0:51.196.148,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10231,0:51.197.145,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,10232,0:51.197.148,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1E 8B FB 80 53 D9 E2 29 38 FA 13 5A 9C 4B 71 E9… +0,,10236,0:51.198.145,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,10237,0:51.213.150,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 76 E5 4D B5 7F D0 67 86 1D 5E B1 E6 3D A2 2E… +0,,10241,0:51.214.147,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,10242,0:51.228.153,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10246,0:51.229.149,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,10247,0:51.229.153,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 76 E5 4D B5 7F D0 67 86 1D 5E B1 E6 3D A2 2E… +0,,10251,0:51.230.149,15.005.000 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,10252,0:51.245.155,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0C B4 E2 F4 AF 86 66 88 23 9E BA E9 0D F1 69 8B… +0,,10256,0:51.246.152,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,10257,0:51.260.157,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10261,0:51.261.154,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,10262,0:51.261.157,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0C B4 E2 F4 AF 86 66 88 23 9E BA E9 0D F1 69 8B… +0,,10266,0:51.262.154,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,10267,0:51.277.159,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B7 0A 64 76 AB 99 AA 20 34 7B 7D A8 C9 C9 8B BC… +0,,10271,0:51.278.156,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,10272,0:51.292.161,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10276,0:51.293.158,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,10277,0:51.293.161,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B7 0A 64 76 AB 99 AA 20 34 7B 7D A8 C9 C9 8B BC… +0,,10281,0:51.294.158,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,10282,0:51.309.164,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D 08 82 C6 B9 BF 6A C4 59 99 D4 DD 9D 39 90 2F… +0,,10286,0:51.310.161,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,10287,0:51.324.166,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10291,0:51.325.163,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,10292,0:51.325.166,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D 08 82 C6 B9 BF 6A C4 59 99 D4 DD 9D 39 90 2F… +0,,10296,0:51.326.163,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,10297,0:51.341.168,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E8 3D 97 BA 83 4D 56 DC BB 89 4E 55 98 27 6E 41… +0,,10301,0:51.342.165,14.004.750 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,10302,0:51.356.170,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10306,0:51.357.167,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,10307,0:51.357.170,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E8 3D 97 BA 83 4D 56 DC BB 89 4E 55 98 27 6E 41… +0,,10311,0:51.358.167,15.004.916 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,10312,0:51.373.173,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7B FC C3 92 42 49 25 51 E2 06 9A 3B AD A8 F5 16… +0,,10316,0:51.374.169,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,10317,0:51.388.175,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10321,0:51.389.172,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,10322,0:51.389.175,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7B FC C3 92 42 49 25 51 E2 06 9A 3B AD A8 F5 16… +0,,10326,0:51.390.172,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,10327,0:51.405.177,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 1E F7 41 7A 92 97 58 DE F9 9F 7E D2 D9 25 E8… +0,,10331,0:51.406.174,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,10332,0:51.420.179,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10336,0:51.421.176,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,10337,0:51.421.179,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 1E F7 41 7A 92 97 58 DE F9 9F 7E D2 D9 25 E8… +0,,10341,0:51.422.176,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,10342,0:51.437.181,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D BD 8C BB D9 20 75 D0 A8 9B 1A 4E C3 3A B5 3E… +0,,10346,0:51.438.178,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,10347,0:51.452.184,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10351,0:51.453.180,2.833 us,,,,,[1 SOF],[Frame: 193] +0,,10352,0:51.453.184,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D BD 8C BB D9 20 75 D0 A8 9B 1A 4E C3 3A B5 3E… +0,,10356,0:51.454.181,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,10357,0:51.469.186,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D 38 63 A7 F7 4C 65 D2 2C 68 A2 BF 73 01 8E B8… +0,,10361,0:51.470.183,14.004.750 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,10362,0:51.484.188,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10366,0:51.485.185,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,10367,0:51.485.188,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D 38 63 A7 F7 4C 65 D2 2C 68 A2 BF 73 01 8E B8… +0,,10371,0:51.486.185,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,10372,0:51.501.190,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6B DC A4 16 EF 7D BC F5 B3 C3 A6 73 30 BA 00 DE… +0,,10376,0:51.502.187,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,10377,0:51.516.192,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10381,0:51.517.189,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,10382,0:51.517.193,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6B DC A4 16 EF 7D BC F5 B3 C3 A6 73 30 BA 00 DE… +0,,10386,0:51.518.189,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,10387,0:51.533.195,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD 35 94 D2 EB 22 8F EE A8 4C C4 A5 B0 2F 51 7C… +0,,10391,0:51.534.192,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,10392,0:51.548.197,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10396,0:51.549.194,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,10397,0:51.549.197,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD 35 94 D2 EB 22 8F EE A8 4C C4 A5 B0 2F 51 7C… +0,,10401,0:51.550.194,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,10402,0:51.565.199,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 99 8A 40 E4 EC 87 46 3D 8B 64 2E B5 47 89 21 24… +0,,10406,0:51.566.196,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,10407,0:51.580.201,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10411,0:51.581.198,2.833 us,,,,,[1 SOF],[Frame: 321] +0,,10412,0:51.581.201,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 99 8A 40 E4 EC 87 46 3D 8B 64 2E B5 47 89 21 24… +0,,10416,0:51.582.198,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,10417,0:51.597.204,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D 60 EF 07 D1 5A 48 6A 2A 23 5E 86 13 92 C5 52… +0,,10421,0:51.598.201,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,10422,0:51.612.206,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10426,0:51.613.203,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,10427,0:51.613.206,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D 60 EF 07 D1 5A 48 6A 2A 23 5E 86 13 92 C5 52… +0,,10431,0:51.614.203,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,10432,0:51.629.208,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8E 70 4E C3 93 AB 2F 92 BC 3A 4F 48 12 9F B4 89… +0,,10436,0:51.630.205,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,10437,0:51.644.210,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10441,0:51.645.207,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,10442,0:51.645.210,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8E 70 4E C3 93 AB 2F 92 BC 3A 4F 48 12 9F B4 89… +0,,10446,0:51.646.207,15.004.916 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,10447,0:51.661.213,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 30 BD DC 25 FE 45 37 C7 39 A5 0D 17 7E E6 4F… +0,,10451,0:51.662.209,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,10452,0:51.676.215,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10456,0:51.677.212,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,10457,0:51.677.215,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 30 BD DC 25 FE 45 37 C7 39 A5 0D 17 7E E6 4F… +0,,10461,0:51.678.212,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,10462,0:51.693.217,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 29 9C 3F FB 18 1B 7F 93 3F F1 2F 15 52 03 F9… +0,,10466,0:51.694.214,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,10467,0:51.708.219,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10471,0:51.709.216,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,10472,0:51.709.219,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 29 9C 3F FB 18 1B 7F 93 3F F1 2F 15 52 03 F9… +0,,10476,0:51.710.216,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,10477,0:51.725.221,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 58 1C 43 A4 A7 F5 C4 98 53 CC 5C 3A D8 36 03… +0,,10481,0:51.726.218,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,10482,0:51.740.224,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10486,0:51.741.220,16.005.041 ms,,,,,[17 SOF],[Frames: 481 - 497] +0,,10487,0:51.757.226,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3B 71 17 4E 0D 01 D3 9C 34 49 2E 10 D1 9A 12 B4… +0,,10491,0:51.758.223,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,10492,0:51.772.228,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10496,0:51.773.225,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,10497,0:51.773.228,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3B 71 17 4E 0D 01 D3 9C 34 49 2E 10 D1 9A 12 B4… +0,,10501,0:51.774.225,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,10502,0:51.789.230,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 5B 72 BC B4 0E 2D 92 84 D7 D4 0D 33 80 20 42… +0,,10506,0:51.790.227,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,10507,0:51.804.232,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10511,0:51.805.229,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,10512,0:51.805.233,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 5B 72 BC B4 0E 2D 92 84 D7 D4 0D 33 80 20 42… +0,,10516,0:51.806.229,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,10517,0:51.821.235,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 32 B6 EA C9 BC B5 A5 19 A9 F1 D6 F2 64 26 08 E2… +0,,10521,0:51.822.232,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,10522,0:51.836.237,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10526,0:51.837.234,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,10527,0:51.837.237,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 32 B6 EA C9 BC B5 A5 19 A9 F1 D6 F2 64 26 08 E2… +0,,10531,0:51.838.234,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,10532,0:51.853.239,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB ED 73 FA BA FD 77 D8 3B 9D B0 90 EB BE 21 CC… +0,,10536,0:51.854.236,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,10537,0:51.868.241,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10541,0:51.869.238,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,10542,0:51.869.241,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB ED 73 FA BA FD 77 D8 3B 9D B0 90 EB BE 21 CC… +0,,10546,0:51.870.238,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,10547,0:51.885.244,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 39 E5 B0 5E 0B 75 1F 32 3C B7 3F 96 4C 11 9F… +0,,10551,0:51.886.241,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,10552,0:51.900.246,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10556,0:51.901.243,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,10557,0:51.901.246,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 39 E5 B0 5E 0B 75 1F 32 3C B7 3F 96 4C 11 9F… +0,,10561,0:51.902.243,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,10562,0:51.917.248,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 59 4B F4 29 69 EE C8 29 E1 B4 0B B3 0F 17 3A 24… +0,,10566,0:51.918.245,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,10567,0:51.932.250,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10571,0:51.933.247,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,10572,0:51.933.250,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 59 4B F4 29 69 EE C8 29 E1 B4 0B B3 0F 17 3A 24… +0,,10576,0:51.934.247,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,10577,0:51.949.253,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 A2 31 B6 00 88 D4 71 6A 9C A9 A5 59 DB C8 D7… +0,,10581,0:51.950.249,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,10582,0:51.964.255,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10586,0:51.965.252,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,10587,0:51.965.255,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 A2 31 B6 00 88 D4 71 6A 9C A9 A5 59 DB C8 D7… +0,,10591,0:51.966.252,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,10592,0:51.981.257,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 1E C5 D5 2A D7 B1 E8 D9 17 93 96 C8 C3 3F 1B… +0,,10596,0:51.982.254,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,10597,0:51.996.259,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10601,0:51.997.256,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,10602,0:51.997.259,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 1E C5 D5 2A D7 B1 E8 D9 17 93 96 C8 C3 3F 1B… +0,,10606,0:51.998.256,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,10607,0:52.013.261,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 61 E3 CD 8C D0 A2 A4 94 C1 0B F4 F0 22 01 48 84… +0,,10611,0:52.014.258,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,10612,0:52.028.264,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10616,0:52.029.260,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,10617,0:52.029.264,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 61 E3 CD 8C D0 A2 A4 94 C1 0B F4 F0 22 01 48 84… +0,,10621,0:52.030.261,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,10622,0:52.045.266,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 09 32 F4 63 DB 39 29 2C EA 84 90 CC 87 F8 27… +0,,10626,0:52.046.263,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,10627,0:52.060.268,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10631,0:52.061.265,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,10632,0:52.061.268,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 09 32 F4 63 DB 39 29 2C EA 84 90 CC 87 F8 27… +0,,10636,0:52.062.265,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,10637,0:52.077.270,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E AB 9D 40 E0 E8 0B 31 26 F2 DF 7E 6D 18 03 40… +0,,10641,0:52.078.267,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,10642,0:52.092.272,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10646,0:52.093.269,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,10647,0:52.093.273,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E AB 9D 40 E0 E8 0B 31 26 F2 DF 7E 6D 18 03 40… +0,,10651,0:52.094.269,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,10652,0:52.109.275,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A E2 9D 68 F0 BB 46 98 2A 72 B4 56 28 21 A4 8C… +0,,10656,0:52.110.272,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,10657,0:52.124.277,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10661,0:52.125.274,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,10662,0:52.125.277,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A E2 9D 68 F0 BB 46 98 2A 72 B4 56 28 21 A4 8C… +0,,10666,0:52.126.274,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,10667,0:52.141.279,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 02 65 2F 78 7F FA 05 B5 14 C3 7A 22 43 5B 71… +0,,10671,0:52.142.276,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,10672,0:52.156.281,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10676,0:52.157.278,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,10677,0:52.157.281,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 02 65 2F 78 7F FA 05 B5 14 C3 7A 22 43 5B 71… +0,,10681,0:52.158.278,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,10682,0:52.173.284,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9F C5 70 63 BA 60 CD B7 A9 F1 9C 19 02 68 60 48… +0,,10686,0:52.174.281,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,10687,0:52.188.286,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10691,0:52.189.283,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,10692,0:52.189.286,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9F C5 70 63 BA 60 CD B7 A9 F1 9C 19 02 68 60 48… +0,,10696,0:52.190.283,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,10697,0:52.205.288,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BF 60 8A 92 B8 A7 11 3D A4 BB F2 4F 77 2C E1 AF… +0,,10701,0:52.206.285,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,10702,0:52.220.290,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10706,0:52.221.287,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,10707,0:52.221.290,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BF 60 8A 92 B8 A7 11 3D A4 BB F2 4F 77 2C E1 AF… +0,,10711,0:52.222.287,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,10712,0:52.237.293,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D 90 85 D3 F5 0D 28 C4 96 73 87 5F 55 6D 3C B0… +0,,10716,0:52.238.289,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,10717,0:52.252.295,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10721,0:52.253.292,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,10722,0:52.253.295,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D 90 85 D3 F5 0D 28 C4 96 73 87 5F 55 6D 3C B0… +0,,10726,0:52.254.292,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,10727,0:52.269.297,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C2 8E 74 90 6A 8D 04 95 B2 E9 5D 7B 19 32 CD 34… +0,,10731,0:52.270.294,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,10732,0:52.284.299,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10736,0:52.285.296,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,10737,0:52.285.299,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C2 8E 74 90 6A 8D 04 95 B2 E9 5D 7B 19 32 CD 34… +0,,10741,0:52.286.296,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,10742,0:52.301.301,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F5 E0 16 6D A0 17 F6 11 A4 64 AA B8 FA A9 B9 11… +0,,10746,0:52.302.298,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,10747,0:52.316.304,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10751,0:52.317.300,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,10752,0:52.317.304,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F5 E0 16 6D A0 17 F6 11 A4 64 AA B8 FA A9 B9 11… +0,,10756,0:52.318.301,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,10757,0:52.333.306,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 12 33 CF BB A2 B8 0E FC 1C BB 68 B0 74 78 43 65… +0,,10761,0:52.334.303,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,10762,0:52.348.308,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10766,0:52.349.305,16.005.041 ms,,,,,[17 SOF],[Frames: 1089 - 1105] +0,,10767,0:52.365.310,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 78 65 C7 8D 0D D6 9F 05 DC 73 B9 45 11 BD 96 BD… +0,,10771,0:52.366.307,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,10772,0:52.380.312,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10776,0:52.381.309,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,10777,0:52.381.313,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 78 65 C7 8D 0D D6 9F 05 DC 73 B9 45 11 BD 96 BD… +0,,10781,0:52.382.309,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,10782,0:52.397.315,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 1C E9 B4 3C 50 79 2C 6E 04 19 B5 88 E0 36 88… +0,,10786,0:52.398.312,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,10787,0:52.412.317,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10791,0:52.413.314,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,10792,0:52.413.317,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F 1C E9 B4 3C 50 79 2C 6E 04 19 B5 88 E0 36 88… +0,,10796,0:52.414.314,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,10797,0:52.429.319,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 0C 3E BF BA 61 AC 52 CF 0A C5 B0 5B 31 D2 16… +0,,10801,0:52.430.316,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,10802,0:52.444.321,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10806,0:52.445.318,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,10807,0:52.445.321,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 0C 3E BF BA 61 AC 52 CF 0A C5 B0 5B 31 D2 16… +0,,10811,0:52.446.318,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,10812,0:52.461.324,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D6 6A 60 72 E8 35 CD 0B D5 99 79 FE 20 72 2A E1… +0,,10816,0:52.462.321,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,10817,0:52.476.326,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10821,0:52.477.323,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,10822,0:52.477.326,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D6 6A 60 72 E8 35 CD 0B D5 99 79 FE 20 72 2A E1… +0,,10826,0:52.478.323,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,10827,0:52.493.328,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 FF 82 9C 38 5A 9C 36 E6 3B 74 C3 31 F4 23 B8… +0,,10831,0:52.494.325,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,10832,0:52.508.330,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10836,0:52.509.327,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,10837,0:52.509.330,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 FF 82 9C 38 5A 9C 36 E6 3B 74 C3 31 F4 23 B8… +0,,10841,0:52.510.327,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,10842,0:52.525.333,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5C 1B F0 EC 10 46 82 A5 E1 87 A3 51 AB 6D 77 F9… +0,,10846,0:52.526.329,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,10847,0:52.540.335,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10851,0:52.541.331,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,10852,0:52.541.335,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5C 1B F0 EC 10 46 82 A5 E1 87 A3 51 AB 6D 77 F9… +0,,10856,0:52.542.332,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,10857,0:52.557.337,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 CF 1B 38 3E D0 24 B3 1E 8E 8B FD BD 79 30 68… +0,,10861,0:52.558.334,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,10862,0:52.572.339,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10866,0:52.573.336,2.833 us,,,,,[1 SOF],[Frame: 1313] +0,,10867,0:52.573.339,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 CF 1B 38 3E D0 24 B3 1E 8E 8B FD BD 79 30 68… +0,,10871,0:52.574.336,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,10872,0:52.589.341,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AE 1A 15 2B CD 84 6A A9 9B 68 CB A5 BE 33 D0 0B… +0,,10876,0:52.590.338,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,10877,0:52.604.343,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10881,0:52.605.340,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,10882,0:52.605.344,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AE 1A 15 2B CD 84 6A A9 9B 68 CB A5 BE 33 D0 0B… +0,,10886,0:52.606.341,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,10887,0:52.621.346,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 00 11 87 7B 5E 61 34 E4 DF B9 1F DC 40 44 05… +0,,10891,0:52.622.343,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,10892,0:52.636.348,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10896,0:52.637.345,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,10897,0:52.637.348,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B5 00 11 87 7B 5E 61 34 E4 DF B9 1F DC 40 44 05… +0,,10901,0:52.638.345,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,10902,0:52.653.350,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 25 AE 18 2E F6 34 F2 93 66 F3 D6 9E ED 1A 2B C2… +0,,10906,0:52.654.347,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,10907,0:52.668.352,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10911,0:52.669.349,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,10912,0:52.669.353,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 25 AE 18 2E F6 34 F2 93 66 F3 D6 9E ED 1A 2B C2… +0,,10916,0:52.670.349,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,10917,0:52.685.355,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CF F5 E2 D2 0D 49 E3 FD 97 8E 79 9D 55 13 C4 B4… +0,,10921,0:52.686.352,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,10922,0:52.700.357,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10926,0:52.701.354,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,10927,0:52.701.357,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CF F5 E2 D2 0D 49 E3 FD 97 8E 79 9D 55 13 C4 B4… +0,,10931,0:52.702.354,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,10932,0:52.717.359,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AA B6 C4 F9 9E 2A C1 E3 E2 A5 EB FF AA 14 91 C0… +0,,10936,0:52.718.356,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,10937,0:52.732.361,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10941,0:52.733.358,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,10942,0:52.733.361,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AA B6 C4 F9 9E 2A C1 E3 E2 A5 EB FF AA 14 91 C0… +0,,10946,0:52.734.358,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,10947,0:52.749.364,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 13 3A BC 18 A1 D9 25 E0 3C 01 44 A8 4D 8D 7F BD… +0,,10951,0:52.750.361,14.004.833 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,10952,0:52.764.366,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10956,0:52.765.363,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,10957,0:52.765.366,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 13 3A BC 18 A1 D9 25 E0 3C 01 44 A8 4D 8D 7F BD… +0,,10961,0:52.766.363,15.004.916 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,10962,0:52.781.368,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 4E D6 F2 6F 47 E0 1C C9 70 4B 1D 14 E7 70 84… +0,,10966,0:52.782.365,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,10967,0:52.796.370,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10971,0:52.797.367,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,10972,0:52.797.370,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 4E D6 F2 6F 47 E0 1C C9 70 4B 1D 14 E7 70 84… +0,,10976,0:52.798.367,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,10977,0:52.813.373,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 77 79 59 8B 3A 05 C5 E1 9E 36 BA 91 61 27 6C… +0,,10981,0:52.814.369,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,10982,0:52.828.375,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,10986,0:52.829.371,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,10987,0:52.829.375,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 77 79 59 8B 3A 05 C5 E1 9E 36 BA 91 61 27 6C… +0,,10991,0:52.830.372,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,10992,0:52.845.377,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A6 02 F8 4B 7D CE 75 B1 E3 01 F4 CC 00 9A EC 50… +0,,10996,0:52.846.374,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,10997,0:52.860.379,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11001,0:52.861.376,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,11002,0:52.861.379,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A6 02 F8 4B 7D CE 75 B1 E3 01 F4 CC 00 9A EC 50… +0,,11006,0:52.862.376,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,11007,0:52.877.381,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E 0F C0 F5 01 8D 6B B4 96 00 A3 C7 CF 39 0D 3B… +0,,11011,0:52.878.378,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,11012,0:52.892.383,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11016,0:52.893.380,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,11017,0:52.893.384,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E 0F C0 F5 01 8D 6B B4 96 00 A3 C7 CF 39 0D 3B… +0,,11021,0:52.894.380,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,11022,0:52.909.386,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 67 A7 7A 84 5F F2 2C 6D 25 2D 5F 65 A7 4E 41… +0,,11026,0:52.910.383,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,11027,0:52.924.388,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11031,0:52.925.385,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,11032,0:52.925.388,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 67 A7 7A 84 5F F2 2C 6D 25 2D 5F 65 A7 4E 41… +0,,11036,0:52.926.385,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,11037,0:52.941.390,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1A 2D 21 ED E9 E7 68 51 4C 13 A8 E5 2D 5C E2 EE… +0,,11041,0:52.942.387,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,11042,0:52.956.392,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11046,0:52.957.389,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,11047,0:52.957.392,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1A 2D 21 ED E9 E7 68 51 4C 13 A8 E5 2D 5C E2 EE… +0,,11051,0:52.958.389,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,11052,0:52.973.395,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D4 9A 9B 2C 9B A0 7D 48 9F 15 76 79 A2 74 3A 33… +0,,11056,0:52.974.392,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,11057,0:52.988.397,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11061,0:52.989.394,16.005.041 ms,,,,,[17 SOF],[Frames: 1729 - 1745] +0,,11062,0:53.005.399,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 89 AE 78 DA 78 17 3B 67 C3 2A D8 BE BE FB 40 E7… +0,,11066,0:53.006.396,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,11067,0:53.020.401,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11071,0:53.021.398,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,11072,0:53.021.401,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 89 AE 78 DA 78 17 3B 67 C3 2A D8 BE BE FB 40 E7… +0,,11076,0:53.022.398,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,11077,0:53.037.404,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 29 4A 18 01 A2 F8 4D 82 D2 30 DD 96 48 FC 62… +0,,11081,0:53.038.400,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,11082,0:53.052.406,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11086,0:53.053.403,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,11087,0:53.053.406,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 29 4A 18 01 A2 F8 4D 82 D2 30 DD 96 48 FC 62… +0,,11091,0:53.054.403,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,11092,0:53.069.408,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 19 32 DF 8F CE 0E A0 41 1B 86 B7 6D 47 96 84 01… +0,,11096,0:53.070.405,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,11097,0:53.084.410,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11101,0:53.085.407,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,11102,0:53.085.410,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 19 32 DF 8F CE 0E A0 41 1B 86 B7 6D 47 96 84 01… +0,,11106,0:53.086.407,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,11107,0:53.101.412,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0A 69 E3 46 29 32 21 48 DB 26 8C 03 D9 25 1F B1… +0,,11111,0:53.102.409,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,11112,0:53.116.415,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11116,0:53.117.411,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,11117,0:53.117.415,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0A 69 E3 46 29 32 21 48 DB 26 8C 03 D9 25 1F B1… +0,,11121,0:53.118.412,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,11122,0:53.133.417,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 12 04 06 56 C9 2D ED C7 AF 6C AC B0 10 1E 23 88… +0,,11126,0:53.134.414,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,11127,0:53.148.419,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11131,0:53.149.416,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,11132,0:53.149.419,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 12 04 06 56 C9 2D ED C7 AF 6C AC B0 10 1E 23 88… +0,,11136,0:53.150.416,15.004.916 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,11137,0:53.165.421,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 A8 A8 BA 6B 8B 67 01 11 51 4D 2E E4 76 79 71… +0,,11141,0:53.166.418,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,11142,0:53.180.424,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11146,0:53.181.420,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,11147,0:53.181.424,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 A8 A8 BA 6B 8B 67 01 11 51 4D 2E E4 76 79 71… +0,,11151,0:53.182.420,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,11152,0:53.197.426,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 91 2A C4 A6 69 12 BF 90 36 BB 3A F0 41 66 38 CD… +0,,11156,0:53.198.423,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,11157,0:53.212.428,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11161,0:53.213.425,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,11162,0:53.213.428,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 91 2A C4 A6 69 12 BF 90 36 BB 3A F0 41 66 38 CD… +0,,11166,0:53.214.425,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,11167,0:53.229.430,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 8E 15 B0 F8 4D E9 A9 C2 B2 89 12 B7 7C 04 57… +0,,11171,0:53.230.427,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,11172,0:53.244.432,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11176,0:53.245.429,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,11177,0:53.245.433,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A7 8E 15 B0 F8 4D E9 A9 C2 B2 89 12 B7 7C 04 57… +0,,11181,0:53.246.429,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,11182,0:53.261.435,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0C 3A 6F 98 AD 5E DC 07 73 EA A6 ED F2 A6 C7 F5… +0,,11186,0:53.262.432,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,11187,0:53.276.437,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11191,0:53.277.434,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,11192,0:53.277.437,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0C 3A 6F 98 AD 5E DC 07 73 EA A6 ED F2 A6 C7 F5… +0,,11196,0:53.278.434,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,11197,0:53.293.439,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB AF 23 FC 31 7F 38 B0 9D DA BF 54 C2 51 56 AB… +0,,11201,0:53.294.436,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,11202,0:53.308.441,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11206,0:53.309.438,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,11207,0:53.309.441,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB AF 23 FC 31 7F 38 B0 9D DA BF 54 C2 51 56 AB… +0,,11211,0:53.310.438,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,11212,0:53.325.444,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 28 3C 63 D0 93 EA 5A 3C 7E 81 D8 ED C4 0C 5C 97… +0,,11216,0:53.326.440,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,11217,0:53.340.446,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11221,0:53.341.443,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,11222,0:53.341.446,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 28 3C 63 D0 93 EA 5A 3C 7E 81 D8 ED C4 0C 5C 97… +0,,11226,0:53.342.443,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,11227,0:53.357.448,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF BE 71 BF 68 64 CE 22 77 29 60 8B 7E CD 27 CE… +0,,11231,0:53.358.445,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,11232,0:53.372.450,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11236,0:53.373.447,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,11237,0:53.373.450,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF BE 71 BF 68 64 CE 22 77 29 60 8B 7E CD 27 CE… +0,,11241,0:53.374.447,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,11242,0:53.389.452,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E D8 D2 75 F2 91 32 0C B9 C0 11 F0 FD B6 DE 51… +0,,11246,0:53.390.449,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,11247,0:53.404.455,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11251,0:53.405.451,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,11252,0:53.405.455,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E D8 D2 75 F2 91 32 0C B9 C0 11 F0 FD B6 DE 51… +0,,11256,0:53.406.452,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,11257,0:53.421.457,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1E DC 3D F9 90 98 B3 EA 29 64 62 DE E5 00 33 EF… +0,,11261,0:53.422.454,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,11262,0:53.436.459,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11266,0:53.437.456,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,11267,0:53.437.459,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1E DC 3D F9 90 98 B3 EA 29 64 62 DE E5 00 33 EF… +0,,11271,0:53.438.456,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,11272,0:53.453.461,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B0 E3 C1 66 89 28 19 4D C7 3C 5B B6 23 EB 1A 2E… +0,,11276,0:53.454.458,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,11277,0:53.468.463,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11281,0:53.469.460,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,11282,0:53.469.464,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B0 E3 C1 66 89 28 19 4D C7 3C 5B B6 23 EB 1A 2E… +0,,11286,0:53.470.460,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,11287,0:53.485.466,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 55 36 78 41 70 85 E5 87 B3 04 DE 23 61 5B 1C 3F… +0,,11291,0:53.486.463,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,11292,0:53.500.468,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11296,0:53.501.465,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,11297,0:53.501.468,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 55 36 78 41 70 85 E5 87 B3 04 DE 23 61 5B 1C 3F… +0,,11301,0:53.502.465,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,11302,0:53.517.470,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 E7 88 64 A4 17 97 71 71 ED 42 DC CC EA 8E 81… +0,,11306,0:53.518.467,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,11307,0:53.532.472,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11311,0:53.533.469,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,11312,0:53.533.472,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 E7 88 64 A4 17 97 71 71 ED 42 DC CC EA 8E 81… +0,,11316,0:53.534.469,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,11317,0:53.549.475,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 60 A4 09 5D B6 75 6D 36 93 33 A8 77 21 EC 6A… +0,,11321,0:53.550.472,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,11322,0:53.564.477,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11326,0:53.565.474,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,11327,0:53.565.477,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 60 A4 09 5D B6 75 6D 36 93 33 A8 77 21 EC 6A… +0,,11331,0:53.566.474,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,11332,0:53.581.479,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 6D 05 D3 30 67 5D 14 3F 84 3A 3D 14 59 9C 4E… +0,,11336,0:53.582.476,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,11337,0:53.596.481,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11341,0:53.597.478,16.005.041 ms,,,,,[17 SOF],[Frames: 289 - 305] +0,,11342,0:53.613.484,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E9 93 AD C3 87 63 A5 B9 38 9B 3A 26 C7 26 68 59… +0,,11346,0:53.614.480,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,11347,0:53.628.486,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11351,0:53.629.483,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,11352,0:53.629.486,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E9 93 AD C3 87 63 A5 B9 38 9B 3A 26 C7 26 68 59… +0,,11356,0:53.630.483,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,11357,0:53.645.488,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B 6F 6D D3 D8 88 CC 69 DE FD 5E 01 D1 71 8B 03… +0,,11361,0:53.646.485,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,11362,0:53.660.490,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11366,0:53.661.487,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,11367,0:53.661.490,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B 6F 6D D3 D8 88 CC 69 DE FD 5E 01 D1 71 8B 03… +0,,11371,0:53.662.487,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,11372,0:53.677.492,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C 05 38 4F F1 C0 89 78 21 B5 95 64 72 94 15 0E… +0,,11376,0:53.678.489,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,11377,0:53.692.495,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11381,0:53.693.491,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,11382,0:53.693.495,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C 05 38 4F F1 C0 89 78 21 B5 95 64 72 94 15 0E… +0,,11386,0:53.694.492,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,11387,0:53.709.497,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 67 81 C1 35 D3 1C 85 2D 2F 41 5A 28 AD 6D 90… +0,,11391,0:53.710.494,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,11392,0:53.724.499,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11396,0:53.725.496,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,11397,0:53.725.499,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 67 81 C1 35 D3 1C 85 2D 2F 41 5A 28 AD 6D 90… +0,,11401,0:53.726.496,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,11402,0:53.741.501,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B DF C0 79 CD 18 82 85 C6 2B 0F 3E 86 0D 4B 76… +0,,11406,0:53.742.498,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,11407,0:53.756.503,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11411,0:53.757.500,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,11412,0:53.757.504,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B DF C0 79 CD 18 82 85 C6 2B 0F 3E 86 0D 4B 76… +0,,11416,0:53.758.500,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,11417,0:53.773.506,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AC 2E 88 4B 57 3F 90 F6 8B FC B2 81 47 FE 15 14… +0,,11421,0:53.774.503,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,11422,0:53.788.508,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11426,0:53.789.505,2.833 us,,,,,[1 SOF],[Frame: 481] +0,,11427,0:53.789.508,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AC 2E 88 4B 57 3F 90 F6 8B FC B2 81 47 FE 15 14… +0,,11431,0:53.790.505,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,11432,0:53.805.510,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 87 D6 EA FE 0C 00 A6 AA 28 FF 96 B2 E7 33 07… +0,,11436,0:53.806.507,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,11437,0:53.820.512,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11441,0:53.821.509,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,11442,0:53.821.512,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 87 D6 EA FE 0C 00 A6 AA 28 FF 96 B2 E7 33 07… +0,,11446,0:53.822.509,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,11447,0:53.837.515,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C1 DD 29 31 A0 AE 97 0B 8A C5 E4 1C 68 12 AE 16… +0,,11451,0:53.838.512,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,11452,0:53.852.517,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11456,0:53.853.514,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,11457,0:53.853.517,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C1 DD 29 31 A0 AE 97 0B 8A C5 E4 1C 68 12 AE 16… +0,,11461,0:53.854.514,15.004.916 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,11462,0:53.869.519,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 DB 39 E3 F9 92 52 74 33 2D 5F 37 6C 45 67 02… +0,,11466,0:53.870.516,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,11467,0:53.884.521,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11471,0:53.885.518,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,11472,0:53.885.521,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 DB 39 E3 F9 92 52 74 33 2D 5F 37 6C 45 67 02… +0,,11476,0:53.886.518,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,11477,0:53.901.524,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 73 80 F2 90 F2 40 55 E4 B3 D3 55 3C 5D EA 22… +0,,11481,0:53.902.520,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,11482,0:53.916.526,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11486,0:53.917.523,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,11487,0:53.917.526,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 73 80 F2 90 F2 40 55 E4 B3 D3 55 3C 5D EA 22… +0,,11491,0:53.918.523,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,11492,0:53.933.528,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 74 39 46 60 59 8A BC 08 B0 57 43 1F 18 AA 11 8A… +0,,11496,0:53.934.525,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,11497,0:53.948.530,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11501,0:53.949.527,2.833 us,,,,,[1 SOF],[Frame: 641] +0,,11502,0:53.949.530,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 74 39 46 60 59 8A BC 08 B0 57 43 1F 18 AA 11 8A… +0,,11506,0:53.950.527,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,11507,0:53.965.532,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E3 6A 63 4F 0F DB DA 23 7F CC E9 A4 12 19 F2 3A… +0,,11511,0:53.966.529,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,11512,0:53.980.535,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11516,0:53.981.531,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,11517,0:53.981.535,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E3 6A 63 4F 0F DB DA 23 7F CC E9 A4 12 19 F2 3A… +0,,11521,0:53.982.532,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,11522,0:53.997.537,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 55 7D 5F 8B 35 8C D8 A3 EA AC B6 12 0E CD B7… +0,,11526,0:53.998.534,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,11527,0:54.012.539,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11531,0:54.013.536,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,11532,0:54.013.539,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 55 7D 5F 8B 35 8C D8 A3 EA AC B6 12 0E CD B7… +0,,11536,0:54.014.536,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,11537,0:54.029.541,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 80 5D 7A 31 8B 71 D6 80 4A E8 03 15 F7 77 18 46… +0,,11541,0:54.030.538,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,11542,0:54.044.543,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11546,0:54.045.540,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,11547,0:54.045.544,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 80 5D 7A 31 8B 71 D6 80 4A E8 03 15 F7 77 18 46… +0,,11551,0:54.046.540,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,11552,0:54.061.546,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 46 91 D5 43 8D 64 BB C9 03 E0 09 42 2F 77 68 60… +0,,11556,0:54.062.543,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,11557,0:54.076.548,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11561,0:54.077.545,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,11562,0:54.077.548,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 46 91 D5 43 8D 64 BB C9 03 E0 09 42 2F 77 68 60… +0,,11566,0:54.078.545,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,11567,0:54.093.550,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C BD F2 9F 20 14 CE BA 74 74 61 30 AD 5E C0 1D… +0,,11571,0:54.094.547,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,11572,0:54.108.552,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11576,0:54.109.549,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,11577,0:54.109.552,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C BD F2 9F 20 14 CE BA 74 74 61 30 AD 5E C0 1D… +0,,11581,0:54.110.549,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,11582,0:54.125.555,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 92 80 01 05 56 BA 19 3F B8 F2 5A E8 11 65 CE 8B… +0,,11586,0:54.126.552,14.004.750 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,11587,0:54.140.557,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11591,0:54.141.554,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,11592,0:54.141.557,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 92 80 01 05 56 BA 19 3F B8 F2 5A E8 11 65 CE 8B… +0,,11596,0:54.142.554,15.004.916 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,11597,0:54.157.559,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 AC 5E ED 11 D6 CC 7C E2 2D C2 E0 0B D8 27 56… +0,,11601,0:54.158.556,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,11602,0:54.172.561,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11606,0:54.173.558,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,11607,0:54.173.561,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 AC 5E ED 11 D6 CC 7C E2 2D C2 E0 0B D8 27 56… +0,,11611,0:54.174.558,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,11612,0:54.189.564,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8B A1 02 05 F8 1B CD 5C 05 BE B3 F0 99 0D 47 EF… +0,,11616,0:54.190.560,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,11617,0:54.204.566,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11621,0:54.205.562,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,11622,0:54.205.566,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8B A1 02 05 F8 1B CD 5C 05 BE B3 F0 99 0D 47 EF… +0,,11626,0:54.206.563,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,11627,0:54.221.568,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C8 97 56 CC 23 95 31 5E 08 3D 95 71 BC AF 90 5A… +0,,11631,0:54.222.565,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,11632,0:54.236.570,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11636,0:54.237.567,16.005.041 ms,,,,,[17 SOF],[Frames: 929 - 945] +0,,11637,0:54.253.572,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 97 56 CC 23 95 31 5E 08 3D 95 71 BC AF 90 5A… +0,,11641,0:54.254.569,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,11642,0:54.268.574,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11646,0:54.269.571,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,11647,0:54.269.575,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 97 56 CC 23 95 31 5E 08 3D 95 71 BC AF 90 5A… +0,,11651,0:54.270.572,15.004.916 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,11652,0:54.285.577,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1F E2 D1 02 E0 D5 B3 D9 12 7D 3E 93 E8 D5 A0 D9… +0,,11656,0:54.286.574,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,11657,0:54.300.579,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11661,0:54.301.576,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,11662,0:54.301.579,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1F E2 D1 02 E0 D5 B3 D9 12 7D 3E 93 E8 D5 A0 D9… +0,,11666,0:54.302.576,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,11667,0:54.317.581,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D6 2D 06 3D F9 A1 DA D6 D9 36 DF A4 FD F5 75 EE… +0,,11671,0:54.318.578,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,11672,0:54.332.583,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11676,0:54.333.580,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,11677,0:54.333.584,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D6 2D 06 3D F9 A1 DA D6 D9 36 DF A4 FD F5 75 EE… +0,,11681,0:54.334.580,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,11682,0:54.349.586,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B3 D7 3E 80 A7 48 54 AF 58 1C 6F F1 21 BA FF C0… +0,,11686,0:54.350.583,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,11687,0:54.364.588,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11691,0:54.365.585,2.833 us,,,,,[1 SOF],[Frame: 1057] +0,,11692,0:54.365.588,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B3 D7 3E 80 A7 48 54 AF 58 1C 6F F1 21 BA FF C0… +0,,11696,0:54.366.585,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,11697,0:54.381.590,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CC A4 9E 28 64 3A D9 38 0B 56 E7 8F 42 4B E2 6D… +0,,11701,0:54.382.587,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,11702,0:54.396.592,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11706,0:54.397.589,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,11707,0:54.397.592,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CC A4 9E 28 64 3A D9 38 0B 56 E7 8F 42 4B E2 6D… +0,,11711,0:54.398.589,15.004.916 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,11712,0:54.413.595,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3C F5 85 5B 45 D4 30 C7 D9 57 C8 32 F1 93 5F 54… +0,,11716,0:54.414.592,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,11717,0:54.428.597,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11721,0:54.429.594,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,11722,0:54.429.597,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3C F5 85 5B 45 D4 30 C7 D9 57 C8 32 F1 93 5F 54… +0,,11726,0:54.430.594,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,11727,0:54.445.599,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FF E3 A6 07 EA 72 A4 7B 41 67 95 74 2E C9 F0 C0… +0,,11731,0:54.446.596,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,11732,0:54.460.601,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11736,0:54.461.598,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,11737,0:54.461.601,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FF E3 A6 07 EA 72 A4 7B 41 67 95 74 2E C9 F0 C0… +0,,11741,0:54.462.598,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,11742,0:54.477.604,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5B DA ED FB D8 54 23 73 77 07 25 AA E4 F1 17 B6… +0,,11746,0:54.478.600,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,11747,0:54.492.606,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11751,0:54.493.602,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,11752,0:54.493.606,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5B DA ED FB D8 54 23 73 77 07 25 AA E4 F1 17 B6… +0,,11756,0:54.494.603,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,11757,0:54.509.608,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A A7 CC 56 BC 72 B7 DF D9 A0 8B 65 DD 75 B8 0C… +0,,11761,0:54.510.605,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,11762,0:54.524.610,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11766,0:54.525.607,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,11767,0:54.525.610,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A A7 CC 56 BC 72 B7 DF D9 A0 8B 65 DD 75 B8 0C… +0,,11771,0:54.526.607,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,11772,0:54.541.612,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 49 92 E1 4C 02 2B B6 67 E5 EF 35 12 36 BE 68 07… +0,,11776,0:54.542.609,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,11777,0:54.556.614,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11781,0:54.557.611,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,11782,0:54.557.615,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 49 92 E1 4C 02 2B B6 67 E5 EF 35 12 36 BE 68 07… +0,,11786,0:54.558.611,15.004.916 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,11787,0:54.573.617,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7F D8 35 A5 EE 86 8D C2 B2 99 1A 20 E0 F5 7C DF… +0,,11791,0:54.574.614,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,11792,0:54.588.619,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11796,0:54.589.616,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,11797,0:54.589.619,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7F D8 35 A5 EE 86 8D C2 B2 99 1A 20 E0 F5 7C DF… +0,,11801,0:54.590.616,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,11802,0:54.605.621,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A 4A E0 9F 93 A2 59 06 DF D0 34 A8 F0 B2 6B FA… +0,,11806,0:54.606.618,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,11807,0:54.620.623,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11811,0:54.621.620,2.833 us,,,,,[1 SOF],[Frame: 1313] +0,,11812,0:54.621.623,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A 4A E0 9F 93 A2 59 06 DF D0 34 A8 F0 B2 6B FA… +0,,11816,0:54.622.620,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,11817,0:54.637.626,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C2 B6 EA 66 B2 63 66 3F BA 15 AF F7 B7 93 F3 39… +0,,11821,0:54.638.623,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,11822,0:54.652.628,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11826,0:54.653.625,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,11827,0:54.653.628,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C2 B6 EA 66 B2 63 66 3F BA 15 AF F7 B7 93 F3 39… +0,,11831,0:54.654.625,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,11832,0:54.669.630,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C 8C C0 E3 EC AD 69 2F F1 EF 05 30 94 B2 83 E8… +0,,11836,0:54.670.627,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,11837,0:54.684.632,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11841,0:54.685.629,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,11842,0:54.685.632,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7C 8C C0 E3 EC AD 69 2F F1 EF 05 30 94 B2 83 E8… +0,,11846,0:54.686.629,15.004.916 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,11847,0:54.701.635,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7E CF E0 4F 06 F7 C1 28 A6 C2 C4 48 B7 B6 90 CF… +0,,11851,0:54.702.631,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,11852,0:54.716.637,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11856,0:54.717.634,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,11857,0:54.717.637,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7E CF E0 4F 06 F7 C1 28 A6 C2 C4 48 B7 B6 90 CF… +0,,11861,0:54.718.634,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,11862,0:54.733.639,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EC A8 0C 95 76 CB 2B 5B 55 37 0C 72 14 EF 4E AB… +0,,11866,0:54.734.636,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,11867,0:54.748.641,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11871,0:54.749.638,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,11872,0:54.749.641,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EC A8 0C 95 76 CB 2B 5B 55 37 0C 72 14 EF 4E AB… +0,,11876,0:54.750.638,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,11877,0:54.765.643,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 F7 CE 2A DD FE C1 79 5D 79 53 E9 6E 6A 37 8F… +0,,11881,0:54.766.640,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,11882,0:54.780.646,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11886,0:54.781.642,2.833 us,,,,,[1 SOF],[Frame: 1473] +0,,11887,0:54.781.646,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B5 F7 CE 2A DD FE C1 79 5D 79 53 E9 6E 6A 37 8F… +0,,11891,0:54.782.643,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,11892,0:54.797.648,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 52 6B 32 91 E2 C8 C1 A1 75 28 41 72 F6 D8 6B B3… +0,,11896,0:54.798.645,14.004.833 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,11897,0:54.812.650,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11901,0:54.813.647,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,11902,0:54.813.650,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 52 6B 32 91 E2 C8 C1 A1 75 28 41 72 F6 D8 6B B3… +0,,11906,0:54.814.647,15.004.916 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,11907,0:54.829.652,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 FA 2E E2 A9 97 68 37 04 F4 3E 60 E0 73 FA 15… +0,,11911,0:54.830.649,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,11912,0:54.844.654,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11916,0:54.845.651,16.005.125 ms,,,,,[17 SOF],[Frames: 1537 - 1553] +0,,11917,0:54.861.657,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 B7 10 D5 6B 7F FA 36 63 7F 68 80 F4 BB 76 01… +0,,11921,0:54.862.654,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,11922,0:54.876.659,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11926,0:54.877.656,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,11927,0:54.877.659,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 B7 10 D5 6B 7F FA 36 63 7F 68 80 F4 BB 76 01… +0,,11931,0:54.878.656,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,11932,0:54.893.661,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 02 11 47 A7 D4 3B 76 1A E1 A8 E1 3B AC BE 66 C3… +0,,11936,0:54.894.658,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,11937,0:54.908.663,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11941,0:54.909.660,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,11942,0:54.909.663,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 02 11 47 A7 D4 3B 76 1A E1 A8 E1 3B AC BE 66 C3… +0,,11946,0:54.910.660,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,11947,0:54.925.666,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D5 52 5F FA 13 80 94 91 01 58 30 53 AE 11 93 6F… +0,,11951,0:54.926.663,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,11952,0:54.940.668,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11956,0:54.941.665,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,11957,0:54.941.668,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D5 52 5F FA 13 80 94 91 01 58 30 53 AE 11 93 6F… +0,,11961,0:54.942.665,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,11962,0:54.957.670,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9A 12 47 71 77 FB 26 2E 25 F0 EC B6 67 DD 12 10… +0,,11966,0:54.958.667,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,11967,0:54.972.672,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11971,0:54.973.669,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,11972,0:54.973.672,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9A 12 47 71 77 FB 26 2E 25 F0 EC B6 67 DD 12 10… +0,,11976,0:54.974.669,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,11977,0:54.989.675,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E7 16 5F 4E 67 DF D9 AA 91 3B C2 89 2A E9 52 10… +0,,11981,0:54.990.671,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,11982,0:55.004.677,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,11986,0:55.005.674,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,11987,0:55.005.677,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E7 16 5F 4E 67 DF D9 AA 91 3B C2 89 2A E9 52 10… +0,,11991,0:55.006.674,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,11992,0:55.021.679,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9D 53 49 DC EE 71 63 CC 24 5D 33 4D 7D 1A 9E 8D… +0,,11996,0:55.022.676,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,11997,0:55.036.681,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12001,0:55.037.678,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,12002,0:55.037.681,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9D 53 49 DC EE 71 63 CC 24 5D 33 4D 7D 1A 9E 8D… +0,,12006,0:55.038.678,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,12007,0:55.053.683,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 11 6D A7 48 2D E2 F9 43 B4 6A 7A D9 D0 F6 51… +0,,12011,0:55.054.680,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,12012,0:55.068.686,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12016,0:55.069.682,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,12017,0:55.069.686,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 11 6D A7 48 2D E2 F9 43 B4 6A 7A D9 D0 F6 51… +0,,12021,0:55.070.683,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,12022,0:55.085.688,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 98 EF EC F0 E8 82 5C A7 A5 75 04 26 9E 6C DA… +0,,12026,0:55.086.685,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,12027,0:55.100.690,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12031,0:55.101.687,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,12032,0:55.101.690,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 98 EF EC F0 E8 82 5C A7 A5 75 04 26 9E 6C DA… +0,,12036,0:55.102.687,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,12037,0:55.117.692,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 54 2E 30 DC E1 E4 C9 63 97 9A 2B 21 E2 F9 4B 6D… +0,,12041,0:55.118.689,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,12042,0:55.132.694,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12046,0:55.133.691,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,12047,0:55.133.695,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 54 2E 30 DC E1 E4 C9 63 97 9A 2B 21 E2 F9 4B 6D… +0,,12051,0:55.134.691,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,12052,0:55.149.697,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1A 8D 2E A3 E6 A6 7D 50 C0 14 88 06 80 BF 76 D7… +0,,12056,0:55.150.694,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,12057,0:55.164.699,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12061,0:55.165.696,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,12062,0:55.165.699,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1A 8D 2E A3 E6 A6 7D 50 C0 14 88 06 80 BF 76 D7… +0,,12066,0:55.166.696,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,12067,0:55.181.701,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 03 A1 28 5F B4 36 A5 50 DD 8A 4F F3 81 D2 BE… +0,,12071,0:55.182.698,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,12072,0:55.196.703,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12076,0:55.197.700,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,12077,0:55.197.703,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 03 A1 28 5F B4 36 A5 50 DD 8A 4F F3 81 D2 BE… +0,,12081,0:55.198.700,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,12082,0:55.213.706,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C 73 D1 F9 0E 53 16 69 FA 43 3A 13 EF 2D 56 9B… +0,,12086,0:55.214.703,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,12087,0:55.228.708,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12091,0:55.229.705,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,12092,0:55.229.708,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9C 73 D1 F9 0E 53 16 69 FA 43 3A 13 EF 2D 56 9B… +0,,12096,0:55.230.705,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,12097,0:55.245.710,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5D B8 2C 45 27 5B 5C 22 AE 55 B3 09 9D C5 09 63… +0,,12101,0:55.246.707,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,12102,0:55.260.712,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12106,0:55.261.709,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,12107,0:55.261.712,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5D B8 2C 45 27 5B 5C 22 AE 55 B3 09 9D C5 09 63… +0,,12111,0:55.262.709,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,12112,0:55.277.715,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2E 1D CB 55 B0 1C 71 77 AF 92 01 47 83 67 A3 C2… +0,,12116,0:55.278.711,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,12117,0:55.292.717,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12121,0:55.293.714,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,12122,0:55.293.717,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2E 1D CB 55 B0 1C 71 77 AF 92 01 47 83 67 A3 C2… +0,,12126,0:55.294.714,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,12127,0:55.309.719,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 77 A7 CA 82 80 7F 10 A1 02 7E 1F 9E 23 BA 6D 6C… +0,,12131,0:55.310.716,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,12132,0:55.324.721,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12136,0:55.325.718,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,12137,0:55.325.721,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 77 A7 CA 82 80 7F 10 A1 02 7E 1F 9E 23 BA 6D 6C… +0,,12141,0:55.326.718,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,12142,0:55.341.724,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5D 11 72 EB F7 73 8A 60 70 5A E9 B1 99 57 CE 90… +0,,12146,0:55.342.720,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,12147,0:55.356.726,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12151,0:55.357.722,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,12152,0:55.357.726,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5D 11 72 EB F7 73 8A 60 70 5A E9 B1 99 57 CE 90… +0,,12156,0:55.358.723,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,12157,0:55.373.728,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA C8 C1 72 5A 59 96 CB 80 54 D2 DB 8C 0A 5E 73… +0,,12161,0:55.374.725,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,12162,0:55.388.730,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12166,0:55.389.727,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,12167,0:55.389.730,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA C8 C1 72 5A 59 96 CB 80 54 D2 DB 8C 0A 5E 73… +0,,12171,0:55.390.727,15.004.916 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,12172,0:55.405.732,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1D 9C 2F 59 73 4D 78 55 01 D0 AE D7 FB 09 1E 4E… +0,,12176,0:55.406.729,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,12177,0:55.420.734,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12181,0:55.421.731,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,12182,0:55.421.735,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1D 9C 2F 59 73 4D 78 55 01 D0 AE D7 FB 09 1E 4E… +0,,12186,0:55.422.731,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,12187,0:55.437.737,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 88 A3 90 F4 8F 48 80 E2 26 73 75 61 DD 06 C8… +0,,12191,0:55.438.734,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,12192,0:55.452.739,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12196,0:55.453.736,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,12197,0:55.453.739,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 88 A3 90 F4 8F 48 80 E2 26 73 75 61 DD 06 C8… +0,,12201,0:55.454.736,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,12202,0:55.469.741,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC CA E3 65 0B 9F 7D 05 A6 16 A1 A6 CF 40 B2 65… +0,,12206,0:55.470.738,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,12207,0:55.484.743,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12211,0:55.485.740,16.005.041 ms,,,,,[17 SOF],[Frames: 129 - 145] +0,,12212,0:55.501.746,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC CA E3 65 0B 9F 7D 05 A6 16 A1 A6 CF 40 B2 65… +0,,12216,0:55.502.743,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,12217,0:55.516.748,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12221,0:55.517.745,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,12222,0:55.517.748,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E 6C 19 26 EB 30 0B 73 7C 7A DD 90 99 3E 42 85… +0,,12226,0:55.518.745,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,12227,0:55.533.750,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B C3 1C E2 4C B4 CB 63 47 12 E2 E4 1B FC EC D6… +0,,12231,0:55.534.747,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,12232,0:55.548.752,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12236,0:55.549.749,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,12237,0:55.549.752,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B C3 1C E2 4C B4 CB 63 47 12 E2 E4 1B FC EC D6… +0,,12241,0:55.550.749,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,12242,0:55.565.755,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A 4A B4 79 2D DE 85 D6 27 C5 06 DB DC EF 77 74… +0,,12246,0:55.566.751,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,12247,0:55.580.757,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12251,0:55.581.754,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,12252,0:55.581.757,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A 4A B4 79 2D DE 85 D6 27 C5 06 DB DC EF 77 74… +0,,12256,0:55.582.754,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,12257,0:55.597.759,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E5 0B 51 41 20 D6 5B 39 32 C8 B6 80 D9 8A E9 41… +0,,12261,0:55.598.756,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,12262,0:55.612.761,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12266,0:55.613.758,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,12267,0:55.613.761,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E5 0B 51 41 20 D6 5B 39 32 C8 B6 80 D9 8A E9 41… +0,,12271,0:55.614.758,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,12272,0:55.629.763,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B BC 67 12 5B 2B 99 59 44 D2 15 95 2E 0A BA 5D… +0,,12276,0:55.630.760,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,12277,0:55.644.766,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12281,0:55.645.762,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,12282,0:55.645.766,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B BC 67 12 5B 2B 99 59 44 D2 15 95 2E 0A BA 5D… +0,,12286,0:55.646.763,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,12287,0:55.661.768,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E7 C2 87 EF 15 B6 D6 9D E6 66 0D 29 A4 28 9A 3F… +0,,12291,0:55.662.765,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,12292,0:55.676.770,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12296,0:55.677.767,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,12297,0:55.677.770,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E7 C2 87 EF 15 B6 D6 9D E6 66 0D 29 A4 28 9A 3F… +0,,12301,0:55.678.767,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,12302,0:55.693.772,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E 63 EB 94 62 6A 75 DA 03 0B C1 D2 A4 0B CD 44… +0,,12306,0:55.694.769,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,12307,0:55.708.774,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12311,0:55.709.771,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,12312,0:55.709.775,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E 63 EB 94 62 6A 75 DA 03 0B C1 D2 A4 0B CD 44… +0,,12316,0:55.710.771,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,12317,0:55.725.777,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5D 95 CD 2E A7 7B 61 DF BE 7E 92 E3 48 A5 82 6B… +0,,12321,0:55.726.774,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,12322,0:55.740.779,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12326,0:55.741.776,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,12327,0:55.741.779,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5D 95 CD 2E A7 7B 61 DF BE 7E 92 E3 48 A5 82 6B… +0,,12331,0:55.742.776,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,12332,0:55.757.781,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B 0F 05 71 5F 64 57 4C 80 0A 63 C0 A6 91 90 89… +0,,12336,0:55.758.778,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,12337,0:55.772.783,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12341,0:55.773.780,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,12342,0:55.773.783,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B 0F 05 71 5F 64 57 4C 80 0A 63 C0 A6 91 90 89… +0,,12346,0:55.774.780,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,12347,0:55.789.786,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 70 FF 73 DE E3 40 F3 B0 81 F5 D3 34 3E 36 3F 8B… +0,,12351,0:55.790.783,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,12352,0:55.804.788,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12356,0:55.805.785,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,12357,0:55.805.788,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 70 FF 73 DE E3 40 F3 B0 81 F5 D3 34 3E 36 3F 8B… +0,,12361,0:55.806.785,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,12362,0:55.821.790,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DB 6E 8F B6 68 13 F8 51 13 76 31 33 E8 F0 85 33… +0,,12366,0:55.822.787,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,12367,0:55.836.792,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12371,0:55.837.789,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,12372,0:55.837.792,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DB 6E 8F B6 68 13 F8 51 13 76 31 33 E8 F0 85 33… +0,,12376,0:55.838.789,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,12377,0:55.853.795,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 21 E2 93 6D 49 A5 53 F7 59 74 01 2E CE FF AF… +0,,12381,0:55.854.791,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,12382,0:55.868.797,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12386,0:55.869.793,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,12387,0:55.869.797,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 22 21 E2 93 6D 49 A5 53 F7 59 74 01 2E CE FF AF… +0,,12391,0:55.870.794,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,12392,0:55.885.799,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 31 1C FF 17 D1 33 38 66 A0 1A 04 C3 FF F4 6E… +0,,12396,0:55.886.796,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,12397,0:55.900.801,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12401,0:55.901.798,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,12402,0:55.901.801,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 31 1C FF 17 D1 33 38 66 A0 1A 04 C3 FF F4 6E… +0,,12406,0:55.902.798,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,12407,0:55.917.803,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 D5 09 95 42 C1 CC 20 CD 2E 84 67 74 38 48 46… +0,,12411,0:55.918.800,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,12412,0:55.932.805,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12416,0:55.933.802,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,12417,0:55.933.806,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 D5 09 95 42 C1 CC 20 CD 2E 84 67 74 38 48 46… +0,,12421,0:55.934.803,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,12422,0:55.949.808,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 37 88 C4 94 5F EE 82 A0 AC A8 A6 2D 03 50 A8 DD… +0,,12426,0:55.950.805,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,12427,0:55.964.810,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12431,0:55.965.807,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,12432,0:55.965.810,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 37 88 C4 94 5F EE 82 A0 AC A8 A6 2D 03 50 A8 DD… +0,,12436,0:55.966.807,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,12437,0:55.981.812,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 55 D4 03 84 BE 79 51 D5 C1 C5 9D 50 EA BA CC… +0,,12441,0:55.982.809,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,12442,0:55.996.814,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12446,0:55.997.811,2.833 us,,,,,[1 SOF],[Frame: 641] +0,,12447,0:55.997.815,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 55 D4 03 84 BE 79 51 D5 C1 C5 9D 50 EA BA CC… +0,,12451,0:55.998.811,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,12452,0:56.013.817,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 BA 9E 66 08 C8 53 3B B5 43 4F 10 DD D6 78 19… +0,,12456,0:56.014.814,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,12457,0:56.028.819,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12461,0:56.029.816,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,12462,0:56.029.819,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A7 BA 9E 66 08 C8 53 3B B5 43 4F 10 DD D6 78 19… +0,,12466,0:56.030.816,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,12467,0:56.045.821,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 28 B9 79 BE A2 B7 79 1F B8 42 E6 CA 96 44 E5… +0,,12471,0:56.046.818,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,12472,0:56.060.823,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12476,0:56.061.820,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,12477,0:56.061.823,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 28 B9 79 BE A2 B7 79 1F B8 42 E6 CA 96 44 E5… +0,,12481,0:56.062.820,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,12482,0:56.077.826,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 77 92 BC 20 4F 0E 17 4B C9 69 CC 85 50 89 74 BB… +0,,12486,0:56.078.823,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,12487,0:56.092.828,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12491,0:56.093.825,16.005.041 ms,,,,,[17 SOF],[Frames: 737 - 753] +0,,12492,0:56.109.830,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 7E 22 A0 2C 04 70 74 56 0D 4D FA 0B E8 E3 25… +0,,12496,0:56.110.827,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,12497,0:56.124.832,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12501,0:56.125.829,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,12502,0:56.125.832,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 7E 22 A0 2C 04 70 74 56 0D 4D FA 0B E8 E3 25… +0,,12506,0:56.126.829,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,12507,0:56.141.835,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 68 43 7B 3C 5B EC 32 A7 62 C4 6C 5F 2E 3F 7F… +0,,12511,0:56.142.831,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,12512,0:56.156.837,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12516,0:56.157.833,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,12517,0:56.157.837,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 68 43 7B 3C 5B EC 32 A7 62 C4 6C 5F 2E 3F 7F… +0,,12521,0:56.158.834,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,12522,0:56.173.839,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 94 D0 4B 76 10 94 64 50 07 DA 53 6A F9 18 CF… +0,,12526,0:56.174.836,14.004.750 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,12527,0:56.188.841,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12531,0:56.189.838,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,12532,0:56.189.841,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 93 94 D0 4B 76 10 94 64 50 07 DA 53 6A F9 18 CF… +0,,12536,0:56.190.838,15.004.916 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,12537,0:56.205.843,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 29 6D 0A 6C EC 3F 96 B8 31 B7 6D 17 94 34 6C 19… +0,,12541,0:56.206.840,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,12542,0:56.220.845,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12546,0:56.221.842,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,12547,0:56.221.846,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 29 6D 0A 6C EC 3F 96 B8 31 B7 6D 17 94 34 6C 19… +0,,12551,0:56.222.842,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,12552,0:56.237.848,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F0 48 7B 6E FA AF AA 11 82 DD E5 3A 5A 3B A0 DB… +0,,12556,0:56.238.845,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,12557,0:56.252.850,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12561,0:56.253.847,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,12562,0:56.253.850,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F0 48 7B 6E FA AF AA 11 82 DD E5 3A 5A 3B A0 DB… +0,,12566,0:56.254.847,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,12567,0:56.269.852,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CF 47 FD 2F F0 DD 50 5A 86 3B B9 81 43 96 96 65… +0,,12571,0:56.270.849,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,12572,0:56.284.854,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12576,0:56.285.851,2.833 us,,,,,[1 SOF],[Frame: 929] +0,,12577,0:56.285.854,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CF 47 FD 2F F0 DD 50 5A 86 3B B9 81 43 96 96 65… +0,,12581,0:56.286.851,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,12582,0:56.301.857,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 92 2F 0E 5B F2 A8 F0 CB 06 B2 9D 8A 00 6B 32 70… +0,,12586,0:56.302.854,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,12587,0:56.316.859,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12591,0:56.317.856,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,12592,0:56.317.859,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 92 2F 0E 5B F2 A8 F0 CB 06 B2 9D 8A 00 6B 32 70… +0,,12596,0:56.318.856,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,12597,0:56.333.861,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 34 44 F1 2F D4 F0 79 2D 00 65 3E 30 C7 72 3B… +0,,12601,0:56.334.858,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,12602,0:56.348.863,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12606,0:56.349.860,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,12607,0:56.349.863,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 34 44 F1 2F D4 F0 79 2D 00 65 3E 30 C7 72 3B… +0,,12611,0:56.350.860,15.005.000 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,12612,0:56.365.866,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 FB 05 89 92 CD B2 FC AB 7B 3A 19 35 54 32 9D… +0,,12616,0:56.366.862,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,12617,0:56.380.868,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12621,0:56.381.865,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,12622,0:56.381.868,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 FB 05 89 92 CD B2 FC AB 7B 3A 19 35 54 32 9D… +0,,12626,0:56.382.865,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,12627,0:56.397.870,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 50 F5 84 C3 A9 60 F7 A8 08 BC E2 86 55 74 F2… +0,,12631,0:56.398.867,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,12632,0:56.412.872,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12636,0:56.413.869,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,12637,0:56.413.872,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B5 50 F5 84 C3 A9 60 F7 A8 08 BC E2 86 55 74 F2… +0,,12641,0:56.414.869,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,12642,0:56.429.874,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C4 92 71 EE 62 BF F1 66 36 B6 FD 72 B2 32 BC BC… +0,,12646,0:56.430.871,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,12647,0:56.444.877,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12651,0:56.445.873,2.833 us,,,,,[1 SOF],[Frame: 1089] +0,,12652,0:56.445.877,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C4 92 71 EE 62 BF F1 66 36 B6 FD 72 B2 32 BC BC… +0,,12656,0:56.446.874,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,12657,0:56.461.879,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 76 7A 51 7D 3B A6 81 37 56 18 36 81 3F FE 19… +0,,12661,0:56.462.876,14.004.750 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,12662,0:56.476.881,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12666,0:56.477.878,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,12667,0:56.477.881,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 76 7A 51 7D 3B A6 81 37 56 18 36 81 3F FE 19… +0,,12671,0:56.478.878,15.004.916 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,12672,0:56.493.883,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 1F 05 E9 46 8F E7 9C FC 88 CA FB 51 1B 61 C3… +0,,12676,0:56.494.880,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,12677,0:56.508.885,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12681,0:56.509.882,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,12682,0:56.509.886,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A7 1F 05 E9 46 8F E7 9C FC 88 CA FB 51 1B 61 C3… +0,,12686,0:56.510.882,15.004.916 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,12687,0:56.525.888,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 40 47 63 B6 F9 EC 7A 39 C3 D8 80 44 FF 35 47… +0,,12691,0:56.526.885,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,12692,0:56.540.890,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12696,0:56.541.887,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,12697,0:56.541.890,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 40 47 63 B6 F9 EC 7A 39 C3 D8 80 44 FF 35 47… +0,,12701,0:56.542.887,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,12702,0:56.557.892,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 25 B7 32 A3 BB D4 0A 72 5D 15 99 6F 12 C2 78 F2… +0,,12706,0:56.558.889,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,12707,0:56.572.894,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12711,0:56.573.891,2.833 us,,,,,[1 SOF],[Frame: 1217] +0,,12712,0:56.573.894,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 25 B7 32 A3 BB D4 0A 72 5D 15 99 6F 12 C2 78 F2… +0,,12716,0:56.574.891,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,12717,0:56.589.897,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 E6 94 C1 A1 C3 B1 D0 FA 0B 63 69 99 E4 BC 05… +0,,12721,0:56.590.894,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,12722,0:56.604.899,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12726,0:56.605.896,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,12727,0:56.605.899,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 E6 94 C1 A1 C3 B1 D0 FA 0B 63 69 99 E4 BC 05… +0,,12731,0:56.606.896,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,12732,0:56.621.901,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 07 BA 49 3C 19 43 23 86 FB 41 FA 31 B1 93 7F 2C… +0,,12736,0:56.622.898,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,12737,0:56.636.903,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12741,0:56.637.900,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,12742,0:56.637.903,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 07 BA 49 3C 19 43 23 86 FB 41 FA 31 B1 93 7F 2C… +0,,12746,0:56.638.900,15.004.916 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,12747,0:56.653.906,50.937 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 88 62 6C 7F 28 14 AE 95 F9 63 FE C7 6B 09 CA F7… +0,,12751,0:56.654.902,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,12752,0:56.668.908,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12756,0:56.669.905,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,12757,0:56.669.908,50.916 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 88 62 6C 7F 28 14 AE 95 F9 63 FE C7 6B 09 CA F7… +0,,12761,0:56.670.905,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,12762,0:56.685.910,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 61 8B 9C 31 FE 85 0F 96 9D E8 30 5B 76 B6 31 39… +0,,12766,0:56.686.907,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,12767,0:56.700.912,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12771,0:56.701.909,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,12772,0:56.701.912,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 61 8B 9C 31 FE 85 0F 96 9D E8 30 5B 76 B6 31 39… +0,,12776,0:56.702.909,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,12777,0:56.717.914,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 35 8F C3 1A 29 A7 43 D7 35 97 4F 61 E2 65 12… +0,,12781,0:56.718.911,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,12782,0:56.732.917,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12786,0:56.733.913,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,12787,0:56.733.917,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 35 8F C3 1A 29 A7 43 D7 35 97 4F 61 E2 65 12… +0,,12791,0:56.734.914,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,12792,0:56.749.919,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D0 01 04 43 62 19 97 AE F6 46 BB 11 5B E6 C3 AC… +0,,12796,0:56.750.916,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,12797,0:56.764.921,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12801,0:56.765.918,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,12802,0:56.765.921,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D0 01 04 43 62 19 97 AE F6 46 BB 11 5B E6 C3 AC… +0,,12806,0:56.766.918,15.004.916 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,12807,0:56.781.923,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 32 69 9F 51 D3 EA E8 BD EC 2B 2A CC C5 B7 E6 83… +0,,12811,0:56.782.920,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,12812,0:56.796.925,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12816,0:56.797.922,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,12817,0:56.797.926,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 32 69 9F 51 D3 EA E8 BD EC 2B 2A CC C5 B7 E6 83… +0,,12821,0:56.798.922,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,12822,0:56.813.928,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E BB 9B D6 C9 98 10 23 67 5D DE 5A 30 C7 CB E7… +0,,12826,0:56.814.925,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,12827,0:56.828.930,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12831,0:56.829.927,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,12832,0:56.829.930,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E BB 9B D6 C9 98 10 23 67 5D DE 5A 30 C7 CB E7… +0,,12836,0:56.830.927,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,12837,0:56.845.932,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 6B A1 A6 50 5B F3 46 92 24 7B FF FA 1A 6E F5… +0,,12841,0:56.846.929,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,12842,0:56.860.934,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12846,0:56.861.931,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,12847,0:56.861.934,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 6B A1 A6 50 5B F3 46 92 24 7B FF FA 1A 6E F5… +0,,12851,0:56.862.931,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,12852,0:56.877.937,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 88 BA B0 FC E6 E3 DD 7B D6 7E D3 F7 10 89 70 07… +0,,12856,0:56.878.934,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,12857,0:56.892.939,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12861,0:56.893.936,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,12862,0:56.893.939,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 88 BA B0 FC E6 E3 DD 7B D6 7E D3 F7 10 89 70 07… +0,,12866,0:56.894.936,15.005.000 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,12867,0:56.909.941,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 50 DD 00 5C CB FC 1C 3E 01 44 EC D2 F0 11 BF D2… +0,,12871,0:56.910.938,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,12872,0:56.924.943,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12876,0:56.925.940,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,12877,0:56.925.943,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 50 DD 00 5C CB FC 1C 3E 01 44 EC D2 F0 11 BF D2… +0,,12881,0:56.926.940,15.004.916 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,12882,0:56.941.946,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 45 27 C2 D8 AF 15 EA EE 8F 68 02 BB 27 6B 70 51… +0,,12886,0:56.942.942,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,12887,0:56.956.948,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12891,0:56.957.945,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,12892,0:56.957.948,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 45 27 C2 D8 AF 15 EA EE 8F 68 02 BB 27 6B 70 51… +0,,12896,0:56.958.945,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,12897,0:56.973.950,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4C 73 66 C1 B3 48 38 9F 4A 34 69 02 BD 0F 0C 08… +0,,12901,0:56.974.947,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,12902,0:56.988.952,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12906,0:56.989.949,2.833 us,,,,,[1 SOF],[Frame: 1633] +0,,12907,0:56.989.952,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4C 73 66 C1 B3 48 38 9F 4A 34 69 02 BD 0F 0C 08… +0,,12911,0:56.990.949,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,12912,0:57.005.954,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 43 5E 6F 7F AB C6 88 78 1F BF 48 AC 25 67 D7 3C… +0,,12916,0:57.006.951,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,12917,0:57.020.957,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12921,0:57.021.953,2.833 us,,,,,[1 SOF],[Frame: 1665] +0,,12922,0:57.021.957,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 43 5E 6F 7F AB C6 88 78 1F BF 48 AC 25 67 D7 3C… +0,,12926,0:57.022.954,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,12927,0:57.037.959,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0C BC DC 6C A5 42 1F 17 AB 15 D8 0F 15 6A 55 E9… +0,,12931,0:57.038.956,14.004.750 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,12932,0:57.052.961,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12936,0:57.053.958,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,12937,0:57.053.961,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0C BC DC 6C A5 42 1F 17 AB 15 D8 0F 15 6A 55 E9… +0,,12941,0:57.054.958,15.004.916 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,12942,0:57.069.963,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E3 C5 C7 F2 34 6C 7E 69 9D 66 C6 78 B1 4D 34 07… +0,,12946,0:57.070.960,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,12947,0:57.084.965,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12951,0:57.085.962,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,12952,0:57.085.966,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E3 C5 C7 F2 34 6C 7E 69 9D 66 C6 78 B1 4D 34 07… +0,,12956,0:57.086.962,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,12957,0:57.101.968,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 BE DE A6 A8 7F 2F C8 9D 98 DE F5 F2 40 DC 95… +0,,12961,0:57.102.965,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,12962,0:57.116.970,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12966,0:57.117.967,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,12967,0:57.117.970,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 BE DE A6 A8 7F 2F C8 9D 98 DE F5 F2 40 DC 95… +0,,12971,0:57.118.967,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,12972,0:57.133.972,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 A7 9E 6A D6 31 B3 60 19 37 68 02 91 0A ED 03… +0,,12976,0:57.134.969,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,12977,0:57.148.974,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12981,0:57.149.971,2.833 us,,,,,[1 SOF],[Frame: 1793] +0,,12982,0:57.149.974,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 A7 9E 6A D6 31 B3 60 19 37 68 02 91 0A ED 03… +0,,12986,0:57.150.971,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,12987,0:57.165.977,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B B1 6B F4 0B 8A 01 47 76 BF 8B 2A 2E 30 90 C3… +0,,12991,0:57.166.974,14.004.750 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,12992,0:57.180.979,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,12996,0:57.181.976,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,12997,0:57.181.979,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B B1 6B F4 0B 8A 01 47 76 BF 8B 2A 2E 30 90 C3… +0,,13001,0:57.182.976,15.004.916 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,13002,0:57.197.981,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 07 4E 82 40 74 7B D4 7E 54 FE D1 7C 0D 35 4A 7B… +0,,13006,0:57.198.978,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,13007,0:57.212.983,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13011,0:57.213.980,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,13012,0:57.213.983,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 07 4E 82 40 74 7B D4 7E 54 FE D1 7C 0D 35 4A 7B… +0,,13016,0:57.214.980,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,13017,0:57.229.986,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 BF 77 8C DE AF 68 29 9B BE 48 EB 93 18 76 8F… +0,,13021,0:57.230.982,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,13022,0:57.244.988,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13026,0:57.245.985,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,13027,0:57.245.988,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 BF 77 8C DE AF 68 29 9B BE 48 EB 93 18 76 8F… +0,,13031,0:57.246.985,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,13032,0:57.261.990,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7B E3 22 CD 98 7B AE 34 F1 B3 FD 3D FC D6 F4 F8… +0,,13036,0:57.262.987,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,13037,0:57.276.992,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13041,0:57.277.989,2.833 us,,,,,[1 SOF],[Frame: 1921] +0,,13042,0:57.277.992,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7B E3 22 CD 98 7B AE 34 F1 B3 FD 3D FC D6 F4 F8… +0,,13046,0:57.278.989,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,13047,0:57.293.994,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BD CE B4 BE F0 04 36 4C 0E 74 AF 47 B5 E1 57 63… +0,,13051,0:57.294.991,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,13052,0:57.308.997,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13056,0:57.309.993,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,13057,0:57.309.997,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BD CE B4 BE F0 04 36 4C 0E 74 AF 47 B5 E1 57 63… +0,,13061,0:57.310.994,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,13062,0:57.325.999,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B5 E8 F0 A1 E3 0E 00 DF 40 A5 DF 31 49 F4 A6 9C… +0,,13066,0:57.326.996,14.004.750 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,13067,0:57.341.001,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13071,0:57.341.998,16.005.125 ms,,,,,[17 SOF],[Frames: 1985 - 2001] +0,,13072,0:57.358.003,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DA 87 EC 76 25 F1 71 FA FD FD 75 34 E8 2A 08 E7… +0,,13076,0:57.359.000,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,13077,0:57.373.005,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13081,0:57.374.002,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,13082,0:57.374.006,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DA 87 EC 76 25 F1 71 FA FD FD 75 34 E8 2A 08 E7… +0,,13086,0:57.375.002,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,13087,0:57.390.008,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D 26 6C 7C E3 C0 0B F5 64 89 A3 0E 60 D9 B1 67… +0,,13091,0:57.391.005,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,13092,0:57.405.010,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13096,0:57.406.007,2.833 us,,,,,[1 SOF],[Frame: 1] +0,,13097,0:57.406.010,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D 26 6C 7C E3 C0 0B F5 64 89 A3 0E 60 D9 B1 67… +0,,13101,0:57.407.007,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,13102,0:57.422.012,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 C7 CC 5B A5 D2 4F 09 04 04 79 FC 70 F1 B4 71… +0,,13106,0:57.423.009,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,13107,0:57.437.014,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13111,0:57.438.011,2.833 us,,,,,[1 SOF],[Frame: 33] +0,,13112,0:57.438.014,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 C7 CC 5B A5 D2 4F 09 04 04 79 FC 70 F1 B4 71… +0,,13116,0:57.439.011,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,13117,0:57.454.017,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 13 BE E8 BE 41 62 CD 47 1B D2 87 2D 75 DA 7D 17… +0,,13121,0:57.455.014,14.004.750 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,13122,0:57.469.019,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13126,0:57.470.016,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,13127,0:57.470.019,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 13 BE E8 BE 41 62 CD 47 1B D2 87 2D 75 DA 7D 17… +0,,13131,0:57.471.016,15.004.916 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,13132,0:57.486.021,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 D4 82 85 A2 15 29 01 8F DA 72 F7 73 25 64 63… +0,,13136,0:57.487.018,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,13137,0:57.501.023,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13141,0:57.502.020,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,13142,0:57.502.023,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 D4 82 85 A2 15 29 01 8F DA 72 F7 73 25 64 63… +0,,13146,0:57.503.020,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,13147,0:57.518.026,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D 39 F7 93 BC 66 06 1D 43 CB 57 65 48 2E EA 87… +0,,13151,0:57.519.022,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,13152,0:57.533.028,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13156,0:57.534.024,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,13157,0:57.534.028,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D 39 F7 93 BC 66 06 1D 43 CB 57 65 48 2E EA 87… +0,,13161,0:57.535.025,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,13162,0:57.550.030,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF 82 9D A9 C8 7A CF 59 D9 A5 38 B9 F7 FD 19 90… +0,,13166,0:57.551.027,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,13167,0:57.565.032,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13171,0:57.566.029,2.833 us,,,,,[1 SOF],[Frame: 161] +0,,13172,0:57.566.032,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF 82 9D A9 C8 7A CF 59 D9 A5 38 B9 F7 FD 19 90… +0,,13176,0:57.567.029,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,13177,0:57.582.034,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 02 02 09 66 F7 B1 3A 51 61 3D 22 EF 62 84 15… +0,,13181,0:57.583.031,14.004.750 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,13182,0:57.597.036,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13186,0:57.598.033,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,13187,0:57.598.037,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 02 02 09 66 F7 B1 3A 51 61 3D 22 EF 62 84 15… +0,,13191,0:57.599.034,15.004.916 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,13192,0:57.614.039,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A6 AB AF DE 7B 8C A2 C1 CE 9B 76 22 EE BB E5 15… +0,,13196,0:57.615.036,14.004.750 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,13197,0:57.629.041,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13201,0:57.630.038,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,13202,0:57.630.041,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A6 AB AF DE 7B 8C A2 C1 CE 9B 76 22 EE BB E5 15… +0,,13206,0:57.631.038,15.004.916 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,13207,0:57.646.043,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C 59 A8 E4 03 6B 89 98 9E 0A 32 21 24 7D 6B 37… +0,,13211,0:57.647.040,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,13212,0:57.661.045,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13216,0:57.662.042,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,13217,0:57.662.046,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C 59 A8 E4 03 6B 89 98 9E 0A 32 21 24 7D 6B 37… +0,,13221,0:57.663.042,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,13222,0:57.678.048,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B7 EE C3 09 35 73 48 31 95 67 D5 6A E4 E9 76 41… +0,,13226,0:57.679.045,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,13227,0:57.693.050,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13231,0:57.694.047,2.833 us,,,,,[1 SOF],[Frame: 289] +0,,13232,0:57.694.050,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B7 EE C3 09 35 73 48 31 95 67 D5 6A E4 E9 76 41… +0,,13236,0:57.695.047,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,13237,0:57.710.052,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 A3 58 A4 44 90 54 81 53 B5 6A 93 C2 AC 49 0A… +0,,13241,0:57.711.049,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,13242,0:57.725.054,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13246,0:57.726.051,2.833 us,,,,,[1 SOF],[Frame: 321] +0,,13247,0:57.726.054,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 A3 58 A4 44 90 54 81 53 B5 6A 93 C2 AC 49 0A… +0,,13251,0:57.727.051,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,13252,0:57.742.057,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF E6 65 D0 CC C5 60 4C 18 9A CC 2A D4 1F EF 81… +0,,13256,0:57.743.054,14.004.750 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,13257,0:57.757.059,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13261,0:57.758.056,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,13262,0:57.758.059,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF E6 65 D0 CC C5 60 4C 18 9A CC 2A D4 1F EF 81… +0,,13266,0:57.759.056,15.004.916 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,13267,0:57.774.061,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 ED CF 00 AE 03 E9 D0 B6 AC 24 19 66 F4 D9 6A 7E… +0,,13271,0:57.775.058,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,13272,0:57.789.063,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13276,0:57.790.060,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,13277,0:57.790.063,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 ED CF 00 AE 03 E9 D0 B6 AC 24 19 66 F4 D9 6A 7E… +0,,13281,0:57.791.060,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,13282,0:57.806.066,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AB C3 FB 17 AE 53 28 FF 78 23 FF 35 BD 4E B5 A3… +0,,13286,0:57.807.062,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,13287,0:57.821.068,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13291,0:57.822.064,2.833 us,,,,,[1 SOF],[Frame: 417] +0,,13292,0:57.822.068,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AB C3 FB 17 AE 53 28 FF 78 23 FF 35 BD 4E B5 A3… +0,,13296,0:57.823.065,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,13297,0:57.838.070,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 82 52 19 CA EC 51 FA A7 31 4B 0F 44 51 D7 C5… +0,,13301,0:57.839.067,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,13302,0:57.853.072,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13306,0:57.854.069,2.833 us,,,,,[1 SOF],[Frame: 449] +0,,13307,0:57.854.072,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 82 52 19 CA EC 51 FA A7 31 4B 0F 44 51 D7 C5… +0,,13311,0:57.855.069,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,13312,0:57.870.074,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 92 12 0B EA 92 90 9C 78 BA D8 7C 15 A3 CF 16 1D… +0,,13316,0:57.871.071,14.004.750 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,13317,0:57.885.076,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13321,0:57.886.073,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,13322,0:57.886.077,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 92 12 0B EA 92 90 9C 78 BA D8 7C 15 A3 CF 16 1D… +0,,13326,0:57.887.073,15.004.916 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,13327,0:57.902.079,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 56 C2 A5 FF 14 74 F4 C0 AA 67 CA AF 87 7C F9 FC… +0,,13331,0:57.903.076,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,13332,0:57.917.081,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13336,0:57.918.078,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,13337,0:57.918.081,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 56 C2 A5 FF 14 74 F4 C0 AA 67 CA AF 87 7C F9 FC… +0,,13341,0:57.919.078,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,13342,0:57.934.083,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E 58 78 A4 49 F6 67 8C F5 AC 54 4C B4 C5 35 0B… +0,,13346,0:57.935.080,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,13347,0:57.949.085,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13351,0:57.950.082,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,13352,0:57.950.085,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E 58 78 A4 49 F6 67 8C F5 AC 54 4C B4 C5 35 0B… +0,,13356,0:57.951.082,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,13357,0:57.966.088,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 81 8E 0A EB F4 AD 65 F4 9B 02 00 84 52 12 E2… +0,,13361,0:57.967.085,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,13362,0:57.981.090,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13366,0:57.982.087,2.833 us,,,,,[1 SOF],[Frame: 577] +0,,13367,0:57.982.090,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 81 8E 0A EB F4 AD 65 F4 9B 02 00 84 52 12 E2… +0,,13371,0:57.983.087,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,13372,0:57.998.092,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A8 3E 68 F5 C5 84 49 DE 02 76 69 5D A4 09 3B 0A… +0,,13376,0:57.999.089,14.004.750 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,13377,0:58.013.094,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13381,0:58.014.091,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,13382,0:58.014.094,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A8 3E 68 F5 C5 84 49 DE 02 76 69 5D A4 09 3B 0A… +0,,13386,0:58.015.091,15.004.916 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,13387,0:58.030.097,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE C9 F5 A4 15 8C 6C 42 C5 B7 E9 94 43 38 8B 87… +0,,13391,0:58.031.093,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,13392,0:58.045.099,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13396,0:58.046.096,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,13397,0:58.046.099,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DE C9 F5 A4 15 8C 6C 42 C5 B7 E9 94 43 38 8B 87… +0,,13401,0:58.047.096,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,13402,0:58.062.101,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D5 30 23 FB B0 B1 10 BC C0 AD 78 E6 AD 42 FD B7… +0,,13406,0:58.063.098,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,13407,0:58.077.103,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13411,0:58.078.100,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,13412,0:58.078.103,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D5 30 23 FB B0 B1 10 BC C0 AD 78 E6 AD 42 FD B7… +0,,13416,0:58.079.100,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,13417,0:58.094.105,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 3A AD 68 DC 67 3B 0D F3 82 93 CD 28 AF 2B 68… +0,,13421,0:58.095.102,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,13422,0:58.109.108,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13426,0:58.110.104,2.833 us,,,,,[1 SOF],[Frame: 705] +0,,13427,0:58.110.108,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 3A AD 68 DC 67 3B 0D F3 82 93 CD 28 AF 2B 68… +0,,13431,0:58.111.105,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,13432,0:58.126.110,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3C 92 D6 A4 E3 04 AB 02 A0 59 5C FA 91 27 09 34… +0,,13436,0:58.127.107,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,13437,0:58.141.112,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13441,0:58.142.109,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,13442,0:58.142.112,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3C 92 D6 A4 E3 04 AB 02 A0 59 5C FA 91 27 09 34… +0,,13446,0:58.143.109,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,13447,0:58.158.114,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 15 04 90 D7 54 BB 34 B2 D2 A7 CF CF FE 07 C5 AE… +0,,13451,0:58.159.111,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,13452,0:58.173.116,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13456,0:58.174.113,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,13457,0:58.174.117,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 15 04 90 D7 54 BB 34 B2 D2 A7 CF CF FE 07 C5 AE… +0,,13461,0:58.175.113,15.004.916 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,13462,0:58.190.119,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 B1 F3 BB ED D1 EE 25 3E 42 10 69 12 E6 11 DD… +0,,13466,0:58.191.116,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,13467,0:58.205.121,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13471,0:58.206.118,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,13472,0:58.206.121,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 B1 F3 BB ED D1 EE 25 3E 42 10 69 12 E6 11 DD… +0,,13476,0:58.207.118,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,13477,0:58.222.123,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FA 02 5F 46 50 96 D5 F8 5D 7F ED 13 B6 16 2D F7… +0,,13481,0:58.223.120,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,13482,0:58.237.125,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13486,0:58.238.122,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,13487,0:58.238.125,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FA 02 5F 46 50 96 D5 F8 5D 7F ED 13 B6 16 2D F7… +0,,13491,0:58.239.122,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,13492,0:58.254.128,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C 27 54 52 67 04 52 04 AC 87 A5 4D E3 5C 7B A3… +0,,13496,0:58.255.125,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,13497,0:58.269.130,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13501,0:58.270.127,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,13502,0:58.270.130,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C 27 54 52 67 04 52 04 AC 87 A5 4D E3 5C 7B A3… +0,,13506,0:58.271.127,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,13507,0:58.286.132,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6C 61 05 89 02 D4 11 E7 9B B1 76 B0 F9 E6 F3 86… +0,,13511,0:58.287.129,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,13512,0:58.301.134,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13516,0:58.302.131,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,13517,0:58.302.134,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6C 61 05 89 02 D4 11 E7 9B B1 76 B0 F9 E6 F3 86… +0,,13521,0:58.303.131,15.004.916 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,13522,0:58.318.137,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 45 DD 4F 9F E9 78 63 B3 1B 2A 2A CC 17 33 7F… +0,,13526,0:58.319.133,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,13527,0:58.333.139,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13531,0:58.334.136,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,13532,0:58.334.139,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 45 DD 4F 9F E9 78 63 B3 1B 2A 2A CC 17 33 7F… +0,,13536,0:58.335.136,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,13537,0:58.350.141,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D 48 8D F6 26 C7 A9 F8 B7 2E 76 31 F7 4E CB 2B… +0,,13541,0:58.351.138,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,13542,0:58.365.143,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13546,0:58.366.140,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,13547,0:58.366.143,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D 48 8D F6 26 C7 A9 F8 B7 2E 76 31 F7 4E CB 2B… +0,,13551,0:58.367.140,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,13552,0:58.382.145,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C B3 94 99 5B D6 DC 61 D8 D4 B2 7E 31 55 7C 62… +0,,13556,0:58.383.142,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,13557,0:58.397.148,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13561,0:58.398.144,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,13562,0:58.398.148,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C B3 94 99 5B D6 DC 61 D8 D4 B2 7E 31 55 7C 62… +0,,13566,0:58.399.145,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,13567,0:58.414.150,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D 32 A5 DA 10 60 62 FA 8C 51 AF CF 3E 2F 76 94… +0,,13571,0:58.415.147,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,13572,0:58.429.152,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13576,0:58.430.149,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,13577,0:58.430.152,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D 32 A5 DA 10 60 62 FA 8C 51 AF CF 3E 2F 76 94… +0,,13581,0:58.431.149,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,13582,0:58.446.154,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 84 33 8E 69 C3 D4 86 9D 17 39 12 2C 8C DF FF 2D… +0,,13586,0:58.447.151,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,13587,0:58.461.156,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13591,0:58.462.153,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,13592,0:58.462.157,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 84 33 8E 69 C3 D4 86 9D 17 39 12 2C 8C DF FF 2D… +0,,13596,0:58.463.153,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,13597,0:58.478.159,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 71 4D E5 3E 1B 27 2E EA C6 E1 39 B3 A7 34 6F A9… +0,,13601,0:58.479.156,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,13602,0:58.493.161,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13606,0:58.494.158,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,13607,0:58.494.161,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 71 4D E5 3E 1B 27 2E EA C6 E1 39 B3 A7 34 6F A9… +0,,13611,0:58.495.158,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,13612,0:58.510.163,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 60 59 D8 DF AA FC 8F FE 17 37 9B 11 66 22 D2 15… +0,,13616,0:58.511.160,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,13617,0:58.525.165,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13621,0:58.526.162,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,13622,0:58.526.165,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 60 59 D8 DF AA FC 8F FE 17 37 9B 11 66 22 D2 15… +0,,13626,0:58.527.162,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,13627,0:58.542.168,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 25 4E 12 E6 27 E4 8B F1 0B 0C 19 B0 36 D7 46… +0,,13631,0:58.543.165,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,13632,0:58.557.170,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13636,0:58.558.167,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,13637,0:58.558.170,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 25 4E 12 E6 27 E4 8B F1 0B 0C 19 B0 36 D7 46… +0,,13641,0:58.559.167,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,13642,0:58.574.172,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 A8 D4 6A 58 9E A9 38 21 EE 00 02 38 B5 16 3D… +0,,13646,0:58.575.169,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,13647,0:58.589.174,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13651,0:58.590.171,16.005.041 ms,,,,,[17 SOF],[Frames: 1185 - 1201] +0,,13652,0:58.606.177,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 29 07 12 B0 03 62 1B 0B BA 83 50 AF A0 46 6F… +0,,13656,0:58.607.173,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,13657,0:58.621.179,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13661,0:58.622.176,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,13662,0:58.622.179,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 29 07 12 B0 03 62 1B 0B BA 83 50 AF A0 46 6F… +0,,13666,0:58.623.176,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,13667,0:58.638.181,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9D D8 48 83 BA 37 1E 1D 31 49 DE 6B 7C 07 30 47… +0,,13671,0:58.639.178,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,13672,0:58.653.183,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13676,0:58.654.180,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,13677,0:58.654.183,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9D D8 48 83 BA 37 1E 1D 31 49 DE 6B 7C 07 30 47… +0,,13681,0:58.655.180,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,13682,0:58.670.185,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 3E B3 BA 57 FE 81 18 FF 23 4C 8B F0 06 76 48… +0,,13686,0:58.671.182,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,13687,0:58.685.188,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13691,0:58.686.184,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,13692,0:58.686.188,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA 3E B3 BA 57 FE 81 18 FF 23 4C 8B F0 06 76 48… +0,,13696,0:58.687.185,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,13697,0:58.702.190,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BD 1C 2D 60 9E E2 FD 6A 30 61 4A 62 9B A1 95 C9… +0,,13701,0:58.703.187,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,13702,0:58.717.192,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13706,0:58.718.189,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,13707,0:58.718.192,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BD 1C 2D 60 9E E2 FD 6A 30 61 4A 62 9B A1 95 C9… +0,,13711,0:58.719.189,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,13712,0:58.734.194,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EB 5C B3 F2 DD BA 0B 54 F6 C0 57 20 2C 5E 87 12… +0,,13716,0:58.735.191,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,13717,0:58.749.196,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13721,0:58.750.193,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,13722,0:58.750.197,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EB 5C B3 F2 DD BA 0B 54 F6 C0 57 20 2C 5E 87 12… +0,,13726,0:58.751.193,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,13727,0:58.766.199,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E EE D2 F6 BD 67 96 87 F7 00 6E A2 27 EB 04 BE… +0,,13731,0:58.767.196,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,13732,0:58.781.201,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13736,0:58.782.198,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,13737,0:58.782.201,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E EE D2 F6 BD 67 96 87 F7 00 6E A2 27 EB 04 BE… +0,,13741,0:58.783.198,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,13742,0:58.798.203,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 F0 73 77 31 6F 29 7B 48 7E 5E 07 7A 7D FD D2… +0,,13746,0:58.799.200,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,13747,0:58.813.205,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13751,0:58.814.202,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,13752,0:58.814.205,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 F0 73 77 31 6F 29 7B 48 7E 5E 07 7A 7D FD D2… +0,,13756,0:58.815.202,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,13757,0:58.830.208,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D 42 2F F4 9C 2E D2 B0 5C 10 96 8E 74 02 C0 3D… +0,,13761,0:58.831.205,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,13762,0:58.845.210,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13766,0:58.846.207,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,13767,0:58.846.210,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D 42 2F F4 9C 2E D2 B0 5C 10 96 8E 74 02 C0 3D… +0,,13771,0:58.847.207,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,13772,0:58.862.212,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 65 6A FA F4 E9 C5 C7 50 03 60 76 2D B6 B6 36… +0,,13776,0:58.863.209,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,13777,0:58.877.214,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13781,0:58.878.211,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,13782,0:58.878.214,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 65 6A FA F4 E9 C5 C7 50 03 60 76 2D B6 B6 36… +0,,13786,0:58.879.211,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,13787,0:58.894.217,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 AC 61 B3 38 A1 AD 0D 46 AE 56 C3 CA BA E0 2D… +0,,13791,0:58.895.213,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,13792,0:58.909.219,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13796,0:58.910.216,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,13797,0:58.910.219,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 AC 61 B3 38 A1 AD 0D 46 AE 56 C3 CA BA E0 2D… +0,,13801,0:58.911.216,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,13802,0:58.926.221,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 8F 31 EF 29 1E DC F6 A0 9F E7 38 86 E6 71 6F… +0,,13806,0:58.927.218,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,13807,0:58.941.223,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13811,0:58.942.220,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,13812,0:58.942.223,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 8F 31 EF 29 1E DC F6 A0 9F E7 38 86 E6 71 6F… +0,,13816,0:58.943.220,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,13817,0:58.958.226,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C 96 55 0C EF EE 2D B6 D5 1C 30 F8 17 27 20 65… +0,,13821,0:58.959.222,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,13822,0:58.973.228,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13826,0:58.974.224,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,13827,0:58.974.228,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C 96 55 0C EF EE 2D B6 D5 1C 30 F8 17 27 20 65… +0,,13831,0:58.975.225,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,13832,0:58.990.230,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FA 2A FE 82 FB D3 30 86 5A 95 3B FD 7A E6 FC 48… +0,,13836,0:58.991.227,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,13837,0:59.005.232,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13841,0:59.006.229,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,13842,0:59.006.232,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FA 2A FE 82 FB D3 30 86 5A 95 3B FD 7A E6 FC 48… +0,,13846,0:59.007.229,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,13847,0:59.022.234,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 3D E4 AC A5 8F BE 64 B4 8E 1C 8F 0D B9 CC EB… +0,,13851,0:59.023.231,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,13852,0:59.037.236,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13856,0:59.038.233,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,13857,0:59.038.237,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 3D E4 AC A5 8F BE 64 B4 8E 1C 8F 0D B9 CC EB… +0,,13861,0:59.039.233,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,13862,0:59.054.239,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5B 88 B0 5B 4B B7 62 93 88 3B 9E 9A 68 C5 B8 51… +0,,13866,0:59.055.236,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,13867,0:59.069.241,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13871,0:59.070.238,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,13872,0:59.070.241,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5B 88 B0 5B 4B B7 62 93 88 3B 9E 9A 68 C5 B8 51… +0,,13876,0:59.071.238,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,13877,0:59.086.243,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 95 E9 4F 96 CE 41 56 96 64 2C EE 3F 1F A2 C2 B4… +0,,13881,0:59.087.240,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,13882,0:59.101.245,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13886,0:59.102.242,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,13887,0:59.102.245,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 95 E9 4F 96 CE 41 56 96 64 2C EE 3F 1F A2 C2 B4… +0,,13891,0:59.103.242,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,13892,0:59.118.248,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9F B3 21 90 58 D7 97 59 2E 9B B8 A3 FB 83 2A 14… +0,,13896,0:59.119.245,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,13897,0:59.133.250,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13901,0:59.134.247,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,13902,0:59.134.250,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9F B3 21 90 58 D7 97 59 2E 9B B8 A3 FB 83 2A 14… +0,,13906,0:59.135.247,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,13907,0:59.150.252,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AF FC 6D 0D 80 1C 4B 33 F5 E8 FF 90 93 6A 9B 56… +0,,13911,0:59.151.249,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,13912,0:59.165.254,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13916,0:59.166.251,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,13917,0:59.166.254,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AF FC 6D 0D 80 1C 4B 33 F5 E8 FF 90 93 6A 9B 56… +0,,13921,0:59.167.251,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,13922,0:59.182.257,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D9 78 5A 3B 72 26 04 F2 56 5D B8 8B 43 05 88 0F… +0,,13926,0:59.183.253,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,13927,0:59.197.259,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13931,0:59.198.255,16.005.041 ms,,,,,[17 SOF],[Frames: 1793 - 1809] +0,,13932,0:59.214.261,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC C1 CD 25 8B 42 D1 54 51 5B 31 20 57 C1 2F AC… +0,,13936,0:59.215.258,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,13937,0:59.229.263,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13941,0:59.230.260,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,13942,0:59.230.263,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC C1 CD 25 8B 42 D1 54 51 5B 31 20 57 C1 2F AC… +0,,13946,0:59.231.260,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,13947,0:59.246.265,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 F8 9D 3B 32 9C 97 C2 84 B0 28 CE 0B F1 F9 31… +0,,13951,0:59.247.262,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,13952,0:59.261.267,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13956,0:59.262.264,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,13957,0:59.262.268,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 F8 9D 3B 32 9C 97 C2 84 B0 28 CE 0B F1 F9 31… +0,,13961,0:59.263.265,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,13962,0:59.278.270,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2E E5 BB 92 B1 3D D8 C2 17 FD 99 5A 93 AA 8C 79… +0,,13966,0:59.279.267,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,13967,0:59.293.272,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13971,0:59.294.269,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,13972,0:59.294.272,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2E E5 BB 92 B1 3D D8 C2 17 FD 99 5A 93 AA 8C 79… +0,,13976,0:59.295.269,15.004.916 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,13977,0:59.310.274,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 F0 9E 99 40 C8 B6 E6 91 2E 49 73 6A 56 DC 0C… +0,,13981,0:59.311.271,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,13982,0:59.325.276,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,13986,0:59.326.273,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,13987,0:59.326.277,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 F0 9E 99 40 C8 B6 E6 91 2E 49 73 6A 56 DC 0C… +0,,13991,0:59.327.273,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,13992,0:59.342.279,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 FC FB AD EF 9D EA C2 A9 98 AE E5 F9 01 55 9B… +0,,13996,0:59.343.276,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,13997,0:59.357.281,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14001,0:59.358.278,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,14002,0:59.358.281,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 FC FB AD EF 9D EA C2 A9 98 AE E5 F9 01 55 9B… +0,,14006,0:59.359.278,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,14007,0:59.374.283,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 FF F0 06 56 33 E4 D2 2E 41 74 94 CE 15 A5 E0… +0,,14011,0:59.375.280,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,14012,0:59.389.285,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14016,0:59.390.282,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,14017,0:59.390.285,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 93 FF F0 06 56 33 E4 D2 2E 41 74 94 CE 15 A5 E0… +0,,14021,0:59.391.282,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,14022,0:59.406.288,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 4D 33 94 8F 10 68 AF EA 2D DE 3B EF E2 BA F5… +0,,14026,0:59.407.285,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,14027,0:59.421.290,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14031,0:59.422.287,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,14032,0:59.422.290,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 4D 33 94 8F 10 68 AF EA 2D DE 3B EF E2 BA F5… +0,,14036,0:59.423.287,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,14037,0:59.438.292,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 00 3E D6 04 E6 DD E1 A6 51 75 3F 1B 33 85 19… +0,,14041,0:59.439.289,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,14042,0:59.453.294,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14046,0:59.454.291,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,14047,0:59.454.294,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 76 00 3E D6 04 E6 DD E1 A6 51 75 3F 1B 33 85 19… +0,,14051,0:59.455.291,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,14052,0:59.470.296,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 51 F4 1B 2E 9F 51 EB CD 16 25 19 82 65 DD 49 20… +0,,14056,0:59.471.293,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,14057,0:59.485.299,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14061,0:59.486.295,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,14062,0:59.486.299,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 51 F4 1B 2E 9F 51 EB CD 16 25 19 82 65 DD 49 20… +0,,14066,0:59.487.296,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,14067,0:59.502.301,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 AA 10 C9 DD E6 C5 DF 1B B5 8B 6E 62 E5 F9 91… +0,,14071,0:59.503.298,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,14072,0:59.517.303,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14076,0:59.518.300,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,14077,0:59.518.303,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 AA 10 C9 DD E6 C5 DF 1B B5 8B 6E 62 E5 F9 91… +0,,14081,0:59.519.300,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,14082,0:59.534.305,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 8B 25 E1 1F DF 7F 0F 4B FA AA 7E E9 DF 05 4F… +0,,14086,0:59.535.302,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,14087,0:59.549.307,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14091,0:59.550.304,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,14092,0:59.550.308,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 8B 25 E1 1F DF 7F 0F 4B FA AA 7E E9 DF 05 4F… +0,,14096,0:59.551.304,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,14097,0:59.566.310,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 00 40 7E A4 49 6E B2 75 FA 3F 2F 69 1E 36 6D B4… +0,,14101,0:59.567.307,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,14102,0:59.581.312,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14106,0:59.582.309,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,14107,0:59.582.312,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 00 40 7E A4 49 6E B2 75 FA 3F 2F 69 1E 36 6D B4… +0,,14111,0:59.583.309,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,14112,0:59.598.314,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 46 60 AA 33 F6 B2 45 81 76 60 FA D1 73 7F 16 17… +0,,14116,0:59.599.311,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,14117,0:59.613.316,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14121,0:59.614.313,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,14122,0:59.614.316,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 46 60 AA 33 F6 B2 45 81 76 60 FA D1 73 7F 16 17… +0,,14126,0:59.615.313,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,14127,0:59.630.319,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FB 0F 98 87 94 E2 61 6B D6 7C 4B A5 5B 91 AD 93… +0,,14131,0:59.631.316,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,14132,0:59.645.321,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14136,0:59.646.318,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,14137,0:59.646.321,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FB 0F 98 87 94 E2 61 6B D6 7C 4B A5 5B 91 AD 93… +0,,14141,0:59.647.318,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,14142,0:59.662.323,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 6A 89 B2 10 92 FA C0 8A C5 47 1C 2E BF D3 A7… +0,,14146,0:59.663.320,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,14147,0:59.677.325,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14151,0:59.678.322,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,14152,0:59.678.325,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 6A 89 B2 10 92 FA C0 8A C5 47 1C 2E BF D3 A7… +0,,14156,0:59.679.322,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,14157,0:59.694.328,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 1A E3 E5 5A B0 EA 93 EE A4 4E 8F C4 1B 10 DC… +0,,14161,0:59.695.324,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,14162,0:59.709.330,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14166,0:59.710.327,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,14167,0:59.710.330,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 1A E3 E5 5A B0 EA 93 EE A4 4E 8F C4 1B 10 DC… +0,,14171,0:59.711.327,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,14172,0:59.726.332,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 C5 C6 83 04 27 B5 55 05 EA 73 BE 1D 9C BD A1… +0,,14176,0:59.727.329,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,14177,0:59.741.334,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14181,0:59.742.331,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,14182,0:59.742.334,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 C5 C6 83 04 27 B5 55 05 EA 73 BE 1D 9C BD A1… +0,,14186,0:59.743.331,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,14187,0:59.758.336,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4A D5 35 BF E1 A4 27 4B F8 E7 79 97 BE 42 DB 40… +0,,14191,0:59.759.333,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,14192,0:59.773.339,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14196,0:59.774.335,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,14197,0:59.774.339,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4A D5 35 BF E1 A4 27 4B F8 E7 79 97 BE 42 DB 40… +0,,14201,0:59.775.336,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,14202,0:59.790.341,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 DA 4C D8 9C 5E 5D 41 16 B5 70 81 01 B0 A8 90… +0,,14206,0:59.791.338,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,14207,0:59.805.343,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14211,0:59.806.340,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,14212,0:59.806.343,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 DA 4C D8 9C 5E 5D 41 16 B5 70 81 01 B0 A8 90… +0,,14216,0:59.807.340,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,14217,0:59.822.345,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0B 6E C6 DF 08 51 55 9F 46 F1 C6 2B CE 33 F7 8D… +0,,14221,0:59.823.342,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,14222,0:59.837.347,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14226,0:59.838.344,16.005.041 ms,,,,,[17 SOF],[Frames: 385 - 401] +0,,14227,0:59.854.350,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 98 CC A5 F7 CD A5 BC EF 7E D3 2F B7 95 C9 FD CB… +0,,14231,0:59.855.347,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,14232,0:59.869.352,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14236,0:59.870.349,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,14237,0:59.870.352,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 98 CC A5 F7 CD A5 BC EF 7E D3 2F B7 95 C9 FD CB… +0,,14241,0:59.871.349,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,14242,0:59.886.354,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 72 95 34 65 17 8B BE DF A1 4F 64 5A BD A4 55 5E… +0,,14246,0:59.887.351,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,14247,0:59.901.356,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14251,0:59.902.353,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,14252,0:59.902.356,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 72 95 34 65 17 8B BE DF A1 4F 64 5A BD A4 55 5E… +0,,14256,0:59.903.353,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,14257,0:59.918.359,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 A5 C8 AD FD AA 19 A0 77 B7 91 C9 B7 0C 44 AF… +0,,14261,0:59.919.356,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,14262,0:59.933.361,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14266,0:59.934.358,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,14267,0:59.934.361,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 A5 C8 AD FD AA 19 A0 77 B7 91 C9 B7 0C 44 AF… +0,,14271,0:59.935.358,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,14272,0:59.950.363,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 8F 13 AC C2 39 B2 0C F5 D5 9E E4 7D C8 B5 8C… +0,,14276,0:59.951.360,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,14277,0:59.965.365,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14281,0:59.966.362,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,14282,0:59.966.365,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 8F 13 AC C2 39 B2 0C F5 D5 9E E4 7D C8 B5 8C… +0,,14286,0:59.967.362,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,14287,0:59.982.368,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 5B 36 4B 1F 41 DB 0A 21 3D 47 DF 7C 3D 0D 8E… +0,,14291,0:59.983.364,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,14292,0:59.997.370,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14296,0:59.998.367,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,14297,0:59.998.370,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 5B 36 4B 1F 41 DB 0A 21 3D 47 DF 7C 3D 0D 8E… +0,,14301,0:59.999.367,15.004.916 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,14302,1:00.014.372,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C2 CB 3C D5 38 B7 0A 34 87 45 B7 E2 77 06 38 88… +0,,14306,1:00.015.369,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,14307,1:00.029.374,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14311,1:00.030.371,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,14312,1:00.030.374,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C2 CB 3C D5 38 B7 0A 34 87 45 B7 E2 77 06 38 88… +0,,14316,1:00.031.371,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,14317,1:00.046.376,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 7C A7 C1 75 66 AB FE 8E 8A 35 3C E5 72 16 2D… +0,,14321,1:00.047.373,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,14322,1:00.061.379,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14326,1:00.062.375,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,14327,1:00.062.379,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 7C A7 C1 75 66 AB FE 8E 8A 35 3C E5 72 16 2D… +0,,14331,1:00.063.376,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,14332,1:00.078.381,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C0 94 D3 29 9D 93 91 41 1A 87 87 86 ED FF 0C 47… +0,,14336,1:00.079.378,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,14337,1:00.093.383,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14341,1:00.094.380,2.833 us,,,,,[1 SOF],[Frame: 641] +0,,14342,1:00.094.383,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C0 94 D3 29 9D 93 91 41 1A 87 87 86 ED FF 0C 47… +0,,14346,1:00.095.380,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,14347,1:00.110.385,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD 04 75 3B CE 1B 91 C5 8A 4D C1 9D 20 B1 A7 82… +0,,14351,1:00.111.382,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,14352,1:00.125.387,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14356,1:00.126.384,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,14357,1:00.126.388,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD 04 75 3B CE 1B 91 C5 8A 4D C1 9D 20 B1 A7 82… +0,,14361,1:00.127.384,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,14362,1:00.142.390,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E 9B CE 5C BB CF 54 58 B5 05 2A E3 88 8D 33 05… +0,,14366,1:00.143.387,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,14367,1:00.157.392,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14371,1:00.158.389,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,14372,1:00.158.392,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E 9B CE 5C BB CF 54 58 B5 05 2A E3 88 8D 33 05… +0,,14376,1:00.159.389,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,14377,1:00.174.394,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FE A9 0C 64 CB 65 C8 5E 5E 0C 00 28 6C F9 8E D4… +0,,14381,1:00.175.391,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,14382,1:00.189.396,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14386,1:00.190.393,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,14387,1:00.190.396,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FE A9 0C 64 CB 65 C8 5E 5E 0C 00 28 6C F9 8E D4… +0,,14391,1:00.191.393,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,14392,1:00.206.399,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 13 7B B5 84 19 C8 52 42 BF 8F 97 B5 C9 73 95 DC… +0,,14396,1:00.207.396,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,14397,1:00.221.401,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14401,1:00.222.398,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,14402,1:00.222.401,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 13 7B B5 84 19 C8 52 42 BF 8F 97 B5 C9 73 95 DC… +0,,14406,1:00.223.398,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,14407,1:00.238.403,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 E8 30 29 0B 41 D2 6D B0 10 00 33 87 6A 9C CD… +0,,14411,1:00.239.400,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,14412,1:00.253.405,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14416,1:00.254.402,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,14417,1:00.254.405,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 E8 30 29 0B 41 D2 6D B0 10 00 33 87 6A 9C CD… +0,,14421,1:00.255.402,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,14422,1:00.270.408,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 29 86 33 8A CE E2 E7 F9 0B E4 97 B7 B2 70 93 73… +0,,14426,1:00.271.404,14.004.750 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,14427,1:00.285.410,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14431,1:00.286.407,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,14432,1:00.286.410,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 29 86 33 8A CE E2 E7 F9 0B E4 97 B7 B2 70 93 73… +0,,14436,1:00.287.407,15.004.916 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,14437,1:00.302.412,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 A9 1C AC 75 DB 1E D4 81 77 FF 29 65 A3 F8 9B… +0,,14441,1:00.303.409,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,14442,1:00.317.414,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14446,1:00.318.411,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,14447,1:00.318.414,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 A9 1C AC 75 DB 1E D4 81 77 FF 29 65 A3 F8 9B… +0,,14451,1:00.319.411,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,14452,1:00.334.416,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A 7E 1A 71 94 AC FF 8B 9A A4 FA 2A 31 19 BA B2… +0,,14456,1:00.335.413,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,14457,1:00.349.419,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14461,1:00.350.415,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,14462,1:00.350.419,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A 7E 1A 71 94 AC FF 8B 9A A4 FA 2A 31 19 BA B2… +0,,14466,1:00.351.416,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,14467,1:00.366.421,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F2 57 E7 8A 23 7D 7B 9A 4D 03 99 8A 5D 48 08 D8… +0,,14471,1:00.367.418,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,14472,1:00.381.423,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14476,1:00.382.420,2.833 us,,,,,[1 SOF],[Frame: 929] +0,,14477,1:00.382.423,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F2 57 E7 8A 23 7D 7B 9A 4D 03 99 8A 5D 48 08 D8… +0,,14481,1:00.383.420,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,14482,1:00.398.425,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 AD CD F1 93 1C 5D 6F AF 81 97 5E 3E BC 93 09… +0,,14486,1:00.399.422,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,14487,1:00.413.427,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14491,1:00.414.424,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,14492,1:00.414.428,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 AD CD F1 93 1C 5D 6F AF 81 97 5E 3E BC 93 09… +0,,14496,1:00.415.424,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,14497,1:00.430.430,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 3A 4A 65 5B 4B 00 47 78 01 F2 74 2F 9E 70 7A… +0,,14501,1:00.431.427,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,14502,1:00.445.432,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14506,1:00.446.429,16.005.125 ms,,,,,[17 SOF],[Frames: 993 - 1009] +0,,14507,1:00.462.434,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 25 A8 5B 71 D7 90 78 2B 52 B5 A6 51 26 D7 50 33… +0,,14511,1:00.463.431,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,14512,1:00.477.436,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14516,1:00.478.433,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,14517,1:00.478.436,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 25 A8 5B 71 D7 90 78 2B 52 B5 A6 51 26 D7 50 33… +0,,14521,1:00.479.433,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,14522,1:00.494.439,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC DB E5 83 0B BE 60 B4 F2 AD 1D 48 06 48 F8 C0… +0,,14526,1:00.495.436,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,14527,1:00.509.441,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14531,1:00.510.438,2.833 us,,,,,[1 SOF],[Frame: 1057] +0,,14532,1:00.510.441,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC DB E5 83 0B BE 60 B4 F2 AD 1D 48 06 48 F8 C0… +0,,14536,1:00.511.438,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,14537,1:00.526.443,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 61 FF 94 46 AE B5 9B B7 49 0F D9 AE C4 C1 38 B6… +0,,14541,1:00.527.440,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,14542,1:00.541.445,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14546,1:00.542.442,2.833 us,,,,,[1 SOF],[Frame: 1089] +0,,14547,1:00.542.445,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 61 FF 94 46 AE B5 9B B7 49 0F D9 AE C4 C1 38 B6… +0,,14551,1:00.543.442,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,14552,1:00.558.448,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 49 E6 39 E3 26 AD 68 9B 47 5D CE 67 17 A0 7A… +0,,14556,1:00.559.444,14.004.750 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,14557,1:00.573.450,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14561,1:00.574.447,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,14562,1:00.574.450,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 49 E6 39 E3 26 AD 68 9B 47 5D CE 67 17 A0 7A… +0,,14566,1:00.575.447,15.004.916 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,14567,1:00.590.452,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 90 E1 FB 10 18 9C EC 4A A6 C6 6F 00 18 AA 61 0E… +0,,14571,1:00.591.449,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,14572,1:00.605.454,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14576,1:00.606.451,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,14577,1:00.606.454,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 90 E1 FB 10 18 9C EC 4A A6 C6 6F 00 18 AA 61 0E… +0,,14581,1:00.607.451,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,14582,1:00.622.456,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D3 77 EC B5 99 22 BE 0D 3F D7 4E 04 9F 8F 4E 4C… +0,,14586,1:00.623.453,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,14587,1:00.637.458,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14591,1:00.638.455,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,14592,1:00.638.459,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D3 77 EC B5 99 22 BE 0D 3F D7 4E 04 9F 8F 4E 4C… +0,,14596,1:00.639.456,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,14597,1:00.654.461,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 47 85 B0 96 B0 19 C8 6A B0 97 98 AB 92 79 A4 1D… +0,,14601,1:00.655.458,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,14602,1:00.669.463,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14606,1:00.670.460,2.833 us,,,,,[1 SOF],[Frame: 1217] +0,,14607,1:00.670.463,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 47 85 B0 96 B0 19 C8 6A B0 97 98 AB 92 79 A4 1D… +0,,14611,1:00.671.460,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,14612,1:00.686.465,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2F D1 91 A5 A9 AF C5 91 F6 47 A9 2D 68 B4 CE 9F… +0,,14616,1:00.687.462,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,14617,1:00.701.467,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14621,1:00.702.464,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,14622,1:00.702.468,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2F D1 91 A5 A9 AF C5 91 F6 47 A9 2D 68 B4 CE 9F… +0,,14626,1:00.703.464,15.004.916 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,14627,1:00.718.470,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 46 E1 03 91 F8 25 C3 B6 FC 79 C2 74 38 9C CE 45… +0,,14631,1:00.719.467,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,14632,1:00.733.472,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14636,1:00.734.469,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,14637,1:00.734.472,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 46 E1 03 91 F8 25 C3 B6 FC 79 C2 74 38 9C CE 45… +0,,14641,1:00.735.469,15.004.916 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,14642,1:00.750.474,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B B9 99 12 66 4A E0 4D 65 B3 10 11 7C AC 0D 92… +0,,14646,1:00.751.471,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,14647,1:00.765.476,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14651,1:00.766.473,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,14652,1:00.766.476,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B B9 99 12 66 4A E0 4D 65 B3 10 11 7C AC 0D 92… +0,,14656,1:00.767.473,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,14657,1:00.782.479,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 22 60 42 B2 B5 44 B1 D9 D2 43 01 E1 02 01 3D… +0,,14661,1:00.783.476,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,14662,1:00.797.481,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14666,1:00.798.478,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,14667,1:00.798.481,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7A 22 60 42 B2 B5 44 B1 D9 D2 43 01 E1 02 01 3D… +0,,14671,1:00.799.478,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,14672,1:00.814.483,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D C2 29 56 03 85 A6 C4 50 5A 83 75 E6 27 96 D8… +0,,14676,1:00.815.480,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,14677,1:00.829.485,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14681,1:00.830.482,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,14682,1:00.830.485,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D C2 29 56 03 85 A6 C4 50 5A 83 75 E6 27 96 D8… +0,,14686,1:00.831.482,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,14687,1:00.846.488,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 97 C0 8A 17 B0 97 51 9D 7A FB E1 84 22 98 19 DF… +0,,14691,1:00.847.484,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,14692,1:00.861.490,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14696,1:00.862.486,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,14697,1:00.862.490,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 97 C0 8A 17 B0 97 51 9D 7A FB E1 84 22 98 19 DF… +0,,14701,1:00.863.487,15.004.916 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,14702,1:00.878.492,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 98 26 00 57 A6 06 25 C8 83 6C D5 C9 4F 0E 31 2B… +0,,14706,1:00.879.489,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,14707,1:00.893.494,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14711,1:00.894.491,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,14712,1:00.894.494,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 98 26 00 57 A6 06 25 C8 83 6C D5 C9 4F 0E 31 2B… +0,,14716,1:00.895.491,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,14717,1:00.910.496,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 2B D1 35 C3 23 73 5A 9D 0E 91 E0 C6 D1 DE 29… +0,,14721,1:00.911.493,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,14722,1:00.925.498,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14726,1:00.926.495,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,14727,1:00.926.499,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 2B D1 35 C3 23 73 5A 9D 0E 91 E0 C6 D1 DE 29… +0,,14731,1:00.927.496,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,14732,1:00.942.501,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5D DF 3E 35 FE A1 A7 A9 12 54 04 9D 80 72 20 53… +0,,14736,1:00.943.498,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,14737,1:00.957.503,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14741,1:00.958.500,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,14742,1:00.958.503,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5D DF 3E 35 FE A1 A7 A9 12 54 04 9D 80 72 20 53… +0,,14746,1:00.959.500,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,14747,1:00.974.505,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D8 23 26 D9 4D E3 89 38 D6 6C 8F 1B C3 C3 46 17… +0,,14751,1:00.975.502,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,14752,1:00.989.507,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14756,1:00.990.504,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,14757,1:00.990.508,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D8 23 26 D9 4D E3 89 38 D6 6C 8F 1B C3 C3 46 17… +0,,14761,1:00.991.504,15.005.000 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,14762,1:01.006.510,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 B1 A7 B8 8F C6 A8 5B D4 91 92 76 74 4C 03 CF… +0,,14766,1:01.007.507,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,14767,1:01.021.512,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14771,1:01.022.509,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,14772,1:01.022.512,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 B1 A7 B8 8F C6 A8 5B D4 91 92 76 74 4C 03 CF… +0,,14776,1:01.023.509,15.004.916 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,14777,1:01.038.514,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C CB BD A7 42 6A EC 1A 21 58 4A C6 C8 3E D7 86… +0,,14781,1:01.039.511,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,14782,1:01.053.516,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14786,1:01.054.513,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,14787,1:01.054.516,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7C CB BD A7 42 6A EC 1A 21 58 4A C6 C8 3E D7 86… +0,,14791,1:01.055.513,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,14792,1:01.070.519,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 47 6B 3D 7E F8 88 75 34 22 5E A4 AF EF 55 22 FA… +0,,14796,1:01.071.515,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,14797,1:01.085.521,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14801,1:01.086.518,16.005.041 ms,,,,,[17 SOF],[Frames: 1633 - 1649] +0,,14802,1:01.102.523,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 00 69 F6 C1 E2 5D 97 AB 26 85 37 F9 95 9F 61… +0,,14806,1:01.103.520,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,14807,1:01.117.525,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14811,1:01.118.522,2.833 us,,,,,[1 SOF],[Frame: 1665] +0,,14812,1:01.118.525,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 00 69 F6 C1 E2 5D 97 AB 26 85 37 F9 95 9F 61… +0,,14816,1:01.119.522,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,14817,1:01.134.527,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 12 3F 7D 76 7B CC 31 5D 0B 16 A3 AD 9D F4 B8 A7… +0,,14821,1:01.135.524,14.004.750 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,14822,1:01.149.530,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14826,1:01.150.526,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,14827,1:01.150.530,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 12 3F 7D 76 7B CC 31 5D 0B 16 A3 AD 9D F4 B8 A7… +0,,14831,1:01.151.527,15.004.916 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,14832,1:01.166.532,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 EB B8 2D CD 5E BC 97 56 59 F8 9A 22 24 9A 3A… +0,,14836,1:01.167.529,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,14837,1:01.181.534,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14841,1:01.182.531,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,14842,1:01.182.534,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 EB B8 2D CD 5E BC 97 56 59 F8 9A 22 24 9A 3A… +0,,14846,1:01.183.531,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,14847,1:01.198.536,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F5 ED C3 61 39 92 E3 F7 43 24 77 93 85 B1 5F 6E… +0,,14851,1:01.199.533,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,14852,1:01.213.538,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14856,1:01.214.535,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,14857,1:01.214.539,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F5 ED C3 61 39 92 E3 F7 43 24 77 93 85 B1 5F 6E… +0,,14861,1:01.215.535,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,14862,1:01.230.541,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1E AE 84 53 38 06 8B 31 28 3B 03 6B 89 60 33 5F… +0,,14866,1:01.231.538,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,14867,1:01.245.543,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14871,1:01.246.540,2.833 us,,,,,[1 SOF],[Frame: 1793] +0,,14872,1:01.246.543,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1E AE 84 53 38 06 8B 31 28 3B 03 6B 89 60 33 5F… +0,,14876,1:01.247.540,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,14877,1:01.262.545,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 2B 86 F9 F9 29 42 DF FB E5 DB A0 63 6C 01 1D… +0,,14881,1:01.263.542,14.004.750 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,14882,1:01.277.547,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14886,1:01.278.544,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,14887,1:01.278.548,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 2B 86 F9 F9 29 42 DF FB E5 DB A0 63 6C 01 1D… +0,,14891,1:01.279.544,15.004.916 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,14892,1:01.294.550,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D5 B8 89 67 00 AA D2 77 3F 0E 8E 37 72 61 4F B8… +0,,14896,1:01.295.547,14.004.750 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,14897,1:01.309.552,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14901,1:01.310.549,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,14902,1:01.310.552,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D5 B8 89 67 00 AA D2 77 3F 0E 8E 37 72 61 4F B8… +0,,14906,1:01.311.549,15.004.916 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,14907,1:01.326.554,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C7 E7 AB C9 E7 8B CA 69 61 00 AB A2 81 B6 05 5C… +0,,14911,1:01.327.551,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,14912,1:01.341.556,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14916,1:01.342.553,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,14917,1:01.342.556,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C7 E7 AB C9 E7 8B CA 69 61 00 AB A2 81 B6 05 5C… +0,,14921,1:01.343.553,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,14922,1:01.358.559,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 F2 8F 71 00 4B 5E DC D7 EC D3 A4 88 95 26 8B… +0,,14926,1:01.359.555,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,14927,1:01.373.561,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14931,1:01.374.558,2.833 us,,,,,[1 SOF],[Frame: 1921] +0,,14932,1:01.374.561,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 F2 8F 71 00 4B 5E DC D7 EC D3 A4 88 95 26 8B… +0,,14936,1:01.375.558,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,14937,1:01.390.563,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1D 51 F7 D2 25 8A 93 B8 20 AF C2 4C 36 4A 35 43… +0,,14941,1:01.391.560,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,14942,1:01.405.565,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14946,1:01.406.562,2.833 us,,,,,[1 SOF],[Frame: 1953] +0,,14947,1:01.406.565,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1D 51 F7 D2 25 8A 93 B8 20 AF C2 4C 36 4A 35 43… +0,,14951,1:01.407.562,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,14952,1:01.422.567,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 B4 76 DF 35 FB 35 3F 49 3C 48 42 64 2C 86 2C… +0,,14956,1:01.423.564,14.004.750 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,14957,1:01.437.570,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14961,1:01.438.566,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,14962,1:01.438.570,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 B4 76 DF 35 FB 35 3F 49 3C 48 42 64 2C 86 2C… +0,,14966,1:01.439.567,15.005.000 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,14967,1:01.454.572,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 17 0B 91 D2 11 79 4A 15 08 FC 30 DA A6 5C 6B 6A… +0,,14971,1:01.455.569,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,14972,1:01.469.574,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14976,1:01.470.571,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,14977,1:01.470.574,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 17 0B 91 D2 11 79 4A 15 08 FC 30 DA A6 5C 6B 6A… +0,,14981,1:01.471.571,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,14982,1:01.486.576,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CB 6C 49 70 38 03 C2 A9 AB 61 19 C8 0A 6C 0E C0… +0,,14986,1:01.487.573,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,14987,1:01.501.578,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,14991,1:01.502.575,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,14992,1:01.502.579,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CB 6C 49 70 38 03 C2 A9 AB 61 19 C8 0A 6C 0E C0… +0,,14996,1:01.503.575,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,14997,1:01.518.581,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C 7A 36 91 19 50 72 A5 A5 B4 45 70 C6 15 41 5C… +0,,15001,1:01.519.578,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,15002,1:01.533.583,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15006,1:01.534.580,2.833 us,,,,,[1 SOF],[Frame: 33] +0,,15007,1:01.534.583,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9C 7A 36 91 19 50 72 A5 A5 B4 45 70 C6 15 41 5C… +0,,15011,1:01.535.580,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,15012,1:01.550.585,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B3 8B 4C 44 7B 44 D3 D4 1A 5D 2F 47 38 74 B1 C8… +0,,15016,1:01.551.582,14.004.750 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,15017,1:01.565.587,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15021,1:01.566.584,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,15022,1:01.566.587,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B3 8B 4C 44 7B 44 D3 D4 1A 5D 2F 47 38 74 B1 C8… +0,,15026,1:01.567.584,15.004.916 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,15027,1:01.582.590,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 98 4E AA D4 EE B6 4E 77 64 6D 33 B6 AD 90 6B 85… +0,,15031,1:01.583.587,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,15032,1:01.597.592,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15036,1:01.598.589,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,15037,1:01.598.592,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 98 4E AA D4 EE B6 4E 77 64 6D 33 B6 AD 90 6B 85… +0,,15041,1:01.599.589,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,15042,1:01.614.594,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CC 87 51 44 BA 92 ED 5D 20 CB A9 0B 1E E6 AC 56… +0,,15046,1:01.615.591,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,15047,1:01.629.596,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15051,1:01.630.593,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,15052,1:01.630.596,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CC 87 51 44 BA 92 ED 5D 20 CB A9 0B 1E E6 AC 56… +0,,15056,1:01.631.593,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,15057,1:01.646.599,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 37 E9 D6 59 C9 4F A5 83 BE F8 F8 9E 4C 8D 20 FB… +0,,15061,1:01.647.595,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,15062,1:01.661.601,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15066,1:01.662.598,2.833 us,,,,,[1 SOF],[Frame: 161] +0,,15067,1:01.662.601,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 37 E9 D6 59 C9 4F A5 83 BE F8 F8 9E 4C 8D 20 FB… +0,,15071,1:01.663.598,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,15072,1:01.678.603,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 0A 23 44 E2 5F 80 44 2F 4E 5A 1A 66 90 A9 D2… +0,,15076,1:01.679.600,14.004.750 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,15077,1:01.693.605,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15081,1:01.694.602,16.005.020 ms,,,,,[17 SOF],[Frames: 193 - 209] +0,,15082,1:01.710.607,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 26 AA 21 83 3F BF 8F D7 AD 30 FD CA 18 F6 CF 2F… +0,,15086,1:01.711.604,14.004.750 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,15087,1:01.725.610,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15091,1:01.726.606,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,15092,1:01.726.610,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 26 AA 21 83 3F BF 8F D7 AD 30 FD CA 18 F6 CF 2F… +0,,15096,1:01.727.607,15.004.916 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,15097,1:01.742.612,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BB 89 2E 9D 28 EA 47 FD A3 D5 B2 57 F0 13 84 60… +0,,15101,1:01.743.609,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,15102,1:01.757.614,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15106,1:01.758.611,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,15107,1:01.758.614,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BB 89 2E 9D 28 EA 47 FD A3 D5 B2 57 F0 13 84 60… +0,,15111,1:01.759.611,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,15112,1:01.774.616,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C 56 DF DC 1F 04 01 78 81 25 F3 20 45 50 44 0C… +0,,15116,1:01.775.613,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,15117,1:01.789.618,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15121,1:01.790.615,2.833 us,,,,,[1 SOF],[Frame: 289] +0,,15122,1:01.790.619,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C 56 DF DC 1F 04 01 78 81 25 F3 20 45 50 44 0C… +0,,15126,1:01.791.615,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,15127,1:01.806.621,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 6B CD 07 97 CC 80 03 B4 F1 AC D1 2D 00 A4 4E… +0,,15131,1:01.807.618,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,15132,1:01.821.623,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15136,1:01.822.620,2.833 us,,,,,[1 SOF],[Frame: 321] +0,,15137,1:01.822.623,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 6B CD 07 97 CC 80 03 B4 F1 AC D1 2D 00 A4 4E… +0,,15141,1:01.823.620,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,15142,1:01.838.625,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 45 8B BA A6 D5 57 16 89 66 78 D1 43 8A 23 05 4A… +0,,15146,1:01.839.622,14.004.750 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,15147,1:01.853.627,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15151,1:01.854.624,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,15152,1:01.854.627,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 45 8B BA A6 D5 57 16 89 66 78 D1 43 8A 23 05 4A… +0,,15156,1:01.855.624,15.004.916 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,15157,1:01.870.630,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 32 EE 3E 24 12 7D 8B 6A EF C4 74 6B 1E 00 EF 5B… +0,,15161,1:01.871.627,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,15162,1:01.885.632,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15166,1:01.886.629,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,15167,1:01.886.632,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 32 EE 3E 24 12 7D 8B 6A EF C4 74 6B 1E 00 EF 5B… +0,,15171,1:01.887.629,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,15172,1:01.902.634,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 81 40 7C 15 08 CD C6 35 FA A7 17 62 DF BB B3 9A… +0,,15176,1:01.903.631,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,15177,1:01.917.636,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15181,1:01.918.633,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,15182,1:01.918.636,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 81 40 7C 15 08 CD C6 35 FA A7 17 62 DF BB B3 9A… +0,,15186,1:01.919.633,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,15187,1:01.934.639,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EA D6 FD 6F 15 55 FA 4C FD 39 03 64 9C AD 94 27… +0,,15191,1:01.935.635,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,15192,1:01.949.641,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15196,1:01.950.638,2.833 us,,,,,[1 SOF],[Frame: 449] +0,,15197,1:01.950.641,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EA D6 FD 6F 15 55 FA 4C FD 39 03 64 9C AD 94 27… +0,,15201,1:01.951.638,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,15202,1:01.966.643,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D C2 32 30 B9 31 F9 E3 8A EF CA 24 23 AB 65 89… +0,,15206,1:01.967.640,14.004.750 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,15207,1:01.981.645,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15211,1:01.982.642,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,15212,1:01.982.645,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D C2 32 30 B9 31 F9 E3 8A EF CA 24 23 AB 65 89… +0,,15216,1:01.983.642,15.004.916 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,15217,1:01.998.647,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C7 68 68 B8 96 0A 4B C3 A4 62 CB CA 86 66 52 C9… +0,,15221,1:01.999.644,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,15222,1:02.013.650,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15226,1:02.014.646,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,15227,1:02.014.650,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C7 68 68 B8 96 0A 4B C3 A4 62 CB CA 86 66 52 C9… +0,,15231,1:02.015.647,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,15232,1:02.030.652,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 66 EB 57 27 C3 9B 53 59 8D 85 07 70 FA BB 61 B6… +0,,15236,1:02.031.649,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,15237,1:02.045.654,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15241,1:02.046.651,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,15242,1:02.046.654,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 66 EB 57 27 C3 9B 53 59 8D 85 07 70 FA BB 61 B6… +0,,15246,1:02.047.651,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,15247,1:02.062.656,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 5C 55 18 A4 B5 88 05 F1 D2 27 D8 A4 2E 11 C3… +0,,15251,1:02.063.653,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,15252,1:02.077.658,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15256,1:02.078.655,2.833 us,,,,,[1 SOF],[Frame: 577] +0,,15257,1:02.078.659,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 5C 55 18 A4 B5 88 05 F1 D2 27 D8 A4 2E 11 C3… +0,,15261,1:02.079.655,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,15262,1:02.094.661,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 15 02 5E 98 30 CD 42 33 13 5A B8 F8 FE 84 F9 81… +0,,15266,1:02.095.658,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,15267,1:02.109.663,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15271,1:02.110.660,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,15272,1:02.110.663,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 15 02 5E 98 30 CD 42 33 13 5A B8 F8 FE 84 F9 81… +0,,15276,1:02.111.660,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,15277,1:02.126.665,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7F F0 23 4F 3D 03 3C 3E D2 2A 67 B7 26 94 DE E8… +0,,15281,1:02.127.662,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,15282,1:02.141.667,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15286,1:02.142.664,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,15287,1:02.142.667,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7F F0 23 4F 3D 03 3C 3E D2 2A 67 B7 26 94 DE E8… +0,,15291,1:02.143.664,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,15292,1:02.158.670,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F7 5E 34 D9 4B BA 76 12 3C E6 08 B2 A4 66 17 35… +0,,15296,1:02.159.667,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,15297,1:02.173.672,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15301,1:02.174.669,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,15302,1:02.174.672,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F7 5E 34 D9 4B BA 76 12 3C E6 08 B2 A4 66 17 35… +0,,15306,1:02.175.669,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,15307,1:02.190.674,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 7E 31 9C 71 1D 60 A7 F8 56 87 A7 D0 78 3F F9… +0,,15311,1:02.191.671,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,15312,1:02.205.676,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15316,1:02.206.673,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,15317,1:02.206.676,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 7E 31 9C 71 1D 60 A7 F8 56 87 A7 D0 78 3F F9… +0,,15321,1:02.207.673,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,15322,1:02.222.679,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 EB 41 21 13 A1 FE DD 3C 16 16 3E 18 EE F8 CB… +0,,15326,1:02.223.675,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,15327,1:02.237.681,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15331,1:02.238.677,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,15332,1:02.238.681,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 EB 41 21 13 A1 FE DD 3C 16 16 3E 18 EE F8 CB… +0,,15336,1:02.239.678,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,15337,1:02.254.683,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 87 F0 9C 48 E5 60 AB 95 07 79 2C 8C 17 98 0C 79… +0,,15341,1:02.255.680,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,15342,1:02.269.685,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15346,1:02.270.682,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,15347,1:02.270.685,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 87 F0 9C 48 E5 60 AB 95 07 79 2C 8C 17 98 0C 79… +0,,15351,1:02.271.682,15.004.916 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,15352,1:02.286.687,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0E BC 64 B6 A0 F3 DA 64 B1 8B AB 9F D8 EC 44 92… +0,,15356,1:02.287.684,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,15357,1:02.301.689,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15361,1:02.302.686,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,15362,1:02.302.690,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0E BC 64 B6 A0 F3 DA 64 B1 8B AB 9F D8 EC 44 92… +0,,15366,1:02.303.687,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,15367,1:02.318.692,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C4 D9 CD 17 D3 82 72 08 C9 E9 0D 22 8F 06 06 1D… +0,,15371,1:02.319.689,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,15372,1:02.333.694,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15376,1:02.334.691,16.005.041 ms,,,,,[17 SOF],[Frames: 833 - 849] +0,,15377,1:02.350.696,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A FE BD 5A 72 35 1F 8B A8 59 47 70 25 6B B7 10… +0,,15381,1:02.351.693,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,15382,1:02.365.698,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15386,1:02.366.695,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,15387,1:02.366.699,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A FE BD 5A 72 35 1F 8B A8 59 47 70 25 6B B7 10… +0,,15391,1:02.367.695,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,15392,1:02.382.701,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 A9 12 61 72 D7 E1 28 C4 BE 7B 66 10 F3 F5 1D… +0,,15396,1:02.383.698,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,15397,1:02.397.703,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15401,1:02.398.700,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,15402,1:02.398.703,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 A9 12 61 72 D7 E1 28 C4 BE 7B 66 10 F3 F5 1D… +0,,15406,1:02.399.700,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,15407,1:02.414.705,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 AC DA 91 A2 42 EA 19 91 F5 66 77 7A 0F 2B 65… +0,,15411,1:02.415.702,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,15412,1:02.429.707,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15416,1:02.430.704,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,15417,1:02.430.707,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 AC DA 91 A2 42 EA 19 91 F5 66 77 7A 0F 2B 65… +0,,15421,1:02.431.704,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,15422,1:02.446.710,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 C0 EE E5 20 A5 0F 8E 0B 44 55 32 E7 32 D7 57… +0,,15426,1:02.447.707,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,15427,1:02.461.712,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15431,1:02.462.709,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,15432,1:02.462.712,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 C0 EE E5 20 A5 0F 8E 0B 44 55 32 E7 32 D7 57… +0,,15436,1:02.463.709,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,15437,1:02.478.714,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 74 03 73 C4 7D C3 21 03 39 51 D3 E3 4C 64 B0… +0,,15441,1:02.479.711,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,15442,1:02.493.716,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15446,1:02.494.713,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,15447,1:02.494.716,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 74 03 73 C4 7D C3 21 03 39 51 D3 E3 4C 64 B0… +0,,15451,1:02.495.713,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,15452,1:02.510.719,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F2 4D 4A CD 19 40 D8 68 94 27 E2 E6 11 C7 26 7A… +0,,15456,1:02.511.715,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,15457,1:02.525.721,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15461,1:02.526.717,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,15462,1:02.526.721,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F2 4D 4A CD 19 40 D8 68 94 27 E2 E6 11 C7 26 7A… +0,,15466,1:02.527.718,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,15467,1:02.542.723,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B 4B CB 9B 06 FE E2 49 12 B4 57 63 A2 FB F8 CA… +0,,15471,1:02.543.720,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,15472,1:02.557.725,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15476,1:02.558.722,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,15477,1:02.558.725,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B 4B CB 9B 06 FE E2 49 12 B4 57 63 A2 FB F8 CA… +0,,15481,1:02.559.722,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,15482,1:02.574.727,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2E C6 DC BD 80 6A 7C 39 C5 2C 30 D0 C7 80 69 C4… +0,,15486,1:02.575.724,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,15487,1:02.589.729,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15491,1:02.590.726,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,15492,1:02.590.730,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2E C6 DC BD 80 6A 7C 39 C5 2C 30 D0 C7 80 69 C4… +0,,15496,1:02.591.726,15.004.916 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,15497,1:02.606.732,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 51 30 A8 92 05 AE E3 E2 B1 37 65 40 94 FC E8 DF… +0,,15501,1:02.607.729,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,15502,1:02.621.734,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15506,1:02.622.731,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,15507,1:02.622.734,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 51 30 A8 92 05 AE E3 E2 B1 37 65 40 94 FC E8 DF… +0,,15511,1:02.623.731,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,15512,1:02.638.736,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B0 96 69 4B 73 CC 00 69 63 B5 94 D3 AE 3D 81 83… +0,,15516,1:02.639.733,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,15517,1:02.653.738,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15521,1:02.654.735,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,15522,1:02.654.739,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B0 96 69 4B 73 CC 00 69 63 B5 94 D3 AE 3D 81 83… +0,,15526,1:02.655.735,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,15527,1:02.670.741,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C4 6E 8F 0E FF 4C 55 5C 0E 51 46 E8 23 E7 1B 0E… +0,,15531,1:02.671.738,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,15532,1:02.685.743,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15536,1:02.686.740,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,15537,1:02.686.743,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C4 6E 8F 0E FF 4C 55 5C 0E 51 46 E8 23 E7 1B 0E… +0,,15541,1:02.687.740,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,15542,1:02.702.745,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E1 07 D9 53 79 72 51 EB 6A B8 01 CC 2E 47 D9 9D… +0,,15546,1:02.703.742,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,15547,1:02.717.747,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15551,1:02.718.744,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,15552,1:02.718.747,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E1 07 D9 53 79 72 51 EB 6A B8 01 CC 2E 47 D9 9D… +0,,15556,1:02.719.744,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,15557,1:02.734.750,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 78 14 0A 66 1C 91 2A 0F ED 0E F5 39 DF 18 82 B2… +0,,15561,1:02.735.746,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,15562,1:02.749.752,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15566,1:02.750.749,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,15567,1:02.750.752,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 78 14 0A 66 1C 91 2A 0F ED 0E F5 39 DF 18 82 B2… +0,,15571,1:02.751.749,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,15572,1:02.766.754,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CE A8 58 E1 23 5C 1E 41 3E 46 D0 EE AC B0 14 27… +0,,15576,1:02.767.751,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,15577,1:02.781.756,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15581,1:02.782.753,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,15582,1:02.782.756,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CE A8 58 E1 23 5C 1E 41 3E 46 D0 EE AC B0 14 27… +0,,15586,1:02.783.753,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,15587,1:02.798.758,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 7B 95 D9 83 9F 40 93 63 60 D0 0D AE 31 04 C0… +0,,15591,1:02.799.755,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,15592,1:02.813.761,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15596,1:02.814.757,2.833 us,,,,,[1 SOF],[Frame: 1313] +0,,15597,1:02.814.761,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 7B 95 D9 83 9F 40 93 63 60 D0 0D AE 31 04 C0… +0,,15601,1:02.815.758,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,15602,1:02.830.763,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A1 D7 94 02 18 69 EA 9C C7 83 A3 7E 38 C1 4C 36… +0,,15606,1:02.831.760,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,15607,1:02.845.765,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15611,1:02.846.762,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,15612,1:02.846.765,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A1 D7 94 02 18 69 EA 9C C7 83 A3 7E 38 C1 4C 36… +0,,15616,1:02.847.762,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,15617,1:02.862.767,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D0 BB BA 54 F9 64 FF AC 6A 0F A6 CF A2 24 B2 D3… +0,,15621,1:02.863.764,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,15622,1:02.877.769,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15626,1:02.878.766,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,15627,1:02.878.770,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D0 BB BA 54 F9 64 FF AC 6A 0F A6 CF A2 24 B2 D3… +0,,15631,1:02.879.766,15.004.916 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,15632,1:02.894.772,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3D 5F 9E 1B 85 0D 43 F9 E4 C1 08 3D 17 E5 15 1D… +0,,15636,1:02.895.769,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,15637,1:02.909.774,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15641,1:02.910.771,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,15642,1:02.910.774,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3D 5F 9E 1B 85 0D 43 F9 E4 C1 08 3D 17 E5 15 1D… +0,,15646,1:02.911.771,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,15647,1:02.926.776,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A1 19 E0 85 16 84 E2 B4 12 80 39 8F 90 D7 3E 9E… +0,,15651,1:02.927.773,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,15652,1:02.941.778,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15656,1:02.942.775,16.005.041 ms,,,,,[17 SOF],[Frames: 1441 - 1457] +0,,15657,1:02.958.781,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 9A 1D FA 02 6E 49 4A 6A 05 84 E0 A3 67 AF 33… +0,,15661,1:02.959.778,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,15662,1:02.973.783,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15666,1:02.974.780,2.833 us,,,,,[1 SOF],[Frame: 1473] +0,,15667,1:02.974.783,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 9A 1D FA 02 6E 49 4A 6A 05 84 E0 A3 67 AF 33… +0,,15671,1:02.975.780,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,15672,1:02.990.785,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A4 CE 09 8A 58 A7 13 17 AA 5D 55 7E 74 F2 7A 29… +0,,15676,1:02.991.782,14.004.833 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,15677,1:03.005.787,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15681,1:03.006.784,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,15682,1:03.006.787,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A4 CE 09 8A 58 A7 13 17 AA 5D 55 7E 74 F2 7A 29… +0,,15686,1:03.007.784,15.004.916 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,15687,1:03.022.790,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 8C 28 96 5F 15 5B C5 7F E1 0E 21 83 5B 9A 70… +0,,15691,1:03.023.786,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,15692,1:03.037.792,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15696,1:03.038.789,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,15697,1:03.038.792,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 8C 28 96 5F 15 5B C5 7F E1 0E 21 83 5B 9A 70… +0,,15701,1:03.039.789,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,15702,1:03.054.794,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 51 67 62 B2 F8 6D 8A E5 B3 34 7D 0D DA 8A D8 0A… +0,,15706,1:03.055.791,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,15707,1:03.069.796,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15711,1:03.070.793,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,15712,1:03.070.796,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 51 67 62 B2 F8 6D 8A E5 B3 34 7D 0D DA 8A D8 0A… +0,,15716,1:03.071.793,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,15717,1:03.086.798,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F5 EF 58 F8 2C 03 56 2D AF CF 4B 02 06 38 29 49… +0,,15721,1:03.087.795,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,15722,1:03.101.801,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15726,1:03.102.797,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,15727,1:03.102.801,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F5 EF 58 F8 2C 03 56 2D AF CF 4B 02 06 38 29 49… +0,,15731,1:03.103.798,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,15732,1:03.118.803,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 43 AA DD CC F6 DD 98 60 07 8A 02 E1 DE 4F E7… +0,,15736,1:03.119.800,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,15737,1:03.133.805,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15741,1:03.134.802,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,15742,1:03.134.805,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 43 AA DD CC F6 DD 98 60 07 8A 02 E1 DE 4F E7… +0,,15746,1:03.135.802,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,15747,1:03.150.807,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 32 0D 46 52 22 B6 1B 28 2A BE 93 EB DC 14 15… +0,,15751,1:03.151.804,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,15752,1:03.165.809,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15756,1:03.166.806,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,15757,1:03.166.810,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 32 0D 46 52 22 B6 1B 28 2A BE 93 EB DC 14 15… +0,,15761,1:03.167.806,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,15762,1:03.182.812,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 3E 3D 93 18 65 92 3C AE 1D 34 91 0C 8C 9F 36… +0,,15766,1:03.183.809,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,15767,1:03.197.814,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15771,1:03.198.811,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,15772,1:03.198.814,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 3E 3D 93 18 65 92 3C AE 1D 34 91 0C 8C 9F 36… +0,,15776,1:03.199.811,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,15777,1:03.214.816,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B6 A4 F4 CA E6 71 1E B4 3E 02 55 58 3D 2C E4 03… +0,,15781,1:03.215.813,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,15782,1:03.229.818,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15786,1:03.230.815,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,15787,1:03.230.818,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B6 A4 F4 CA E6 71 1E B4 3E 02 55 58 3D 2C E4 03… +0,,15791,1:03.231.815,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,15792,1:03.246.821,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1E D2 F4 CB 98 28 F0 F5 FE 17 9A EB C3 14 F7 6F… +0,,15796,1:03.247.818,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,15797,1:03.261.823,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15801,1:03.262.820,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,15802,1:03.262.823,50.729 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1E D2 F4 CB 98 28 F0 F5 FE 17 9A EB C3 14 F7 6F… +0,,15806,1:03.263.820,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,15807,1:03.278.825,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 28 C5 A5 B3 FD 53 C7 07 D2 A3 90 C9 B8 BA B4 AE… +0,,15811,1:03.279.822,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,15812,1:03.293.827,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15816,1:03.294.824,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,15817,1:03.294.827,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 28 C5 A5 B3 FD 53 C7 07 D2 A3 90 C9 B8 BA B4 AE… +0,,15821,1:03.295.824,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,15822,1:03.310.830,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D8 95 37 7A 21 A7 F3 95 56 FD 07 97 46 5E 43 0E… +0,,15826,1:03.311.826,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,15827,1:03.325.832,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15831,1:03.326.829,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,15832,1:03.326.832,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D8 95 37 7A 21 A7 F3 95 56 FD 07 97 46 5E 43 0E… +0,,15836,1:03.327.829,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,15837,1:03.342.834,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 BB 1A C4 51 D4 AD CC 45 9B 7E B2 96 95 B6 D1… +0,,15841,1:03.343.831,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,15842,1:03.357.836,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15846,1:03.358.833,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,15847,1:03.358.836,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 76 BB 1A C4 51 D4 AD CC 45 9B 7E B2 96 95 B6 D1… +0,,15851,1:03.359.833,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,15852,1:03.374.838,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 B5 14 25 71 89 2D B1 BF 0F 2F 18 44 50 1D 7E… +0,,15856,1:03.375.835,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,15857,1:03.389.841,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15861,1:03.390.837,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,15862,1:03.390.841,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 B5 14 25 71 89 2D B1 BF 0F 2F 18 44 50 1D 7E… +0,,15866,1:03.391.838,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,15867,1:03.406.843,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 81 BE 30 80 EB 56 D1 91 75 59 77 33 D3 B3 05… +0,,15871,1:03.407.840,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,15872,1:03.421.845,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15876,1:03.422.842,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,15877,1:03.422.845,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 93 81 BE 30 80 EB 56 D1 91 75 59 77 33 D3 B3 05… +0,,15881,1:03.423.842,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,15882,1:03.438.847,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2F 64 DD E4 37 00 F2 A3 8B CB 42 51 50 86 F6 F0… +0,,15886,1:03.439.844,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,15887,1:03.453.849,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15891,1:03.454.846,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,15892,1:03.454.850,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2F 64 DD E4 37 00 F2 A3 8B CB 42 51 50 86 F6 F0… +0,,15896,1:03.455.846,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,15897,1:03.470.852,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 03 04 8D 4C F5 7F 85 F8 72 27 93 51 76 8A F4 5C… +0,,15901,1:03.471.849,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,15902,1:03.485.854,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15906,1:03.486.851,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,15907,1:03.486.854,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 03 04 8D 4C F5 7F 85 F8 72 27 93 51 76 8A F4 5C… +0,,15911,1:03.487.851,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,15912,1:03.502.856,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 03 27 DE 89 32 91 FC 59 2F 73 52 72 3B 89 77 3E… +0,,15916,1:03.503.853,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,15917,1:03.517.858,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15921,1:03.518.855,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,15922,1:03.518.859,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 03 27 DE 89 32 91 FC 59 2F 73 52 72 3B 89 77 3E… +0,,15926,1:03.519.855,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,15927,1:03.534.861,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 29 07 11 66 78 97 08 3B 8A 7C B4 C6 51 2A D7 28… +0,,15931,1:03.535.858,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,15932,1:03.549.863,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15936,1:03.550.860,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,15937,1:03.550.863,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 29 07 11 66 78 97 08 3B 8A 7C B4 C6 51 2A D7 28… +0,,15941,1:03.551.860,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,15942,1:03.566.865,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C CA A0 F2 21 8B B4 C0 4D 70 4F 6C 18 93 7D 46… +0,,15946,1:03.567.862,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,15947,1:03.581.867,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15951,1:03.582.864,16.005.041 ms,,,,,[17 SOF],[Frames: 33 - 49] +0,,15952,1:03.598.870,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 A8 BB 58 27 50 E8 1C A2 D1 3F 86 54 98 51 1A… +0,,15956,1:03.599.866,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,15957,1:03.613.872,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15961,1:03.614.869,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,15962,1:03.614.872,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 A8 BB 58 27 50 E8 1C A2 D1 3F 86 54 98 51 1A… +0,,15966,1:03.615.869,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,15967,1:03.630.874,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 00 02 9D 34 D6 7A FD 42 6E AF 0F 76 93 64 FD 30… +0,,15971,1:03.631.871,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,15972,1:03.645.876,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15976,1:03.646.873,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,15977,1:03.646.876,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 00 02 9D 34 D6 7A FD 42 6E AF 0F 76 93 64 FD 30… +0,,15981,1:03.647.873,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,15982,1:03.662.878,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D CA 16 BD D8 19 A6 C6 39 80 84 C6 44 41 9B 69… +0,,15986,1:03.663.875,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,15987,1:03.677.881,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,15991,1:03.678.877,2.833 us,,,,,[1 SOF],[Frame: 129] +0,,15992,1:03.678.881,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D CA 16 BD D8 19 A6 C6 39 80 84 C6 44 41 9B 69… +0,,15996,1:03.679.878,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,15997,1:03.694.883,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B0 DF FF C6 42 B9 CF 7B 00 A9 B7 B7 3E B3 22 49… +0,,16001,1:03.695.880,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,16002,1:03.709.885,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16006,1:03.710.882,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,16007,1:03.710.885,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B0 DF FF C6 42 B9 CF 7B 00 A9 B7 B7 3E B3 22 49… +0,,16011,1:03.711.882,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,16012,1:03.726.887,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 67 2B 25 A2 FB 68 DA 16 E4 51 4A 45 76 CF 56… +0,,16016,1:03.727.884,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,16017,1:03.741.889,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16021,1:03.742.886,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,16022,1:03.742.890,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 67 2B 25 A2 FB 68 DA 16 E4 51 4A 45 76 CF 56… +0,,16026,1:03.743.886,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,16027,1:03.758.892,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 43 51 E7 62 43 A9 76 DA 40 B6 17 8D 3E C3 D6 90… +0,,16031,1:03.759.889,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,16032,1:03.773.894,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16036,1:03.774.891,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,16037,1:03.774.894,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 43 51 E7 62 43 A9 76 DA 40 B6 17 8D 3E C3 D6 90… +0,,16041,1:03.775.891,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,16042,1:03.790.896,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 E2 CD C7 09 5D C4 13 E4 63 D7 B7 D5 3A 69 E2… +0,,16046,1:03.791.893,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,16047,1:03.805.898,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16051,1:03.806.895,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,16052,1:03.806.898,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 E2 CD C7 09 5D C4 13 E4 63 D7 B7 D5 3A 69 E2… +0,,16056,1:03.807.895,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,16057,1:03.822.901,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 36 56 7F DD 07 EF 1A 17 BD B0 56 D5 93 A3 33… +0,,16061,1:03.823.898,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,16062,1:03.837.903,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16066,1:03.838.900,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,16067,1:03.838.903,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 36 56 7F DD 07 EF 1A 17 BD B0 56 D5 93 A3 33… +0,,16071,1:03.839.900,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,16072,1:03.854.905,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D 6D FF 35 11 FC DA EF A3 20 F2 47 95 0E DD C6… +0,,16076,1:03.855.902,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,16077,1:03.869.907,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16081,1:03.870.904,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,16082,1:03.870.907,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D 6D FF 35 11 FC DA EF A3 20 F2 47 95 0E DD C6… +0,,16086,1:03.871.904,15.004.916 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,16087,1:03.886.910,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B 68 42 B0 9E 4E 78 96 1A A9 1E F2 CB DB 35 9F… +0,,16091,1:03.887.906,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,16092,1:03.901.912,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16096,1:03.902.908,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,16097,1:03.902.912,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B 68 42 B0 9E 4E 78 96 1A A9 1E F2 CB DB 35 9F… +0,,16101,1:03.903.909,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,16102,1:03.918.914,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D0 19 26 82 87 70 8F FE 95 37 4D 00 AE 17 11 DF… +0,,16106,1:03.919.911,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,16107,1:03.933.916,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16111,1:03.934.913,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,16112,1:03.934.916,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D0 19 26 82 87 70 8F FE 95 37 4D 00 AE 17 11 DF… +0,,16116,1:03.935.913,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,16117,1:03.950.918,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A1 53 5D B6 4A E2 67 5F 84 21 01 29 67 5F 1C A2… +0,,16121,1:03.951.915,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,16122,1:03.965.920,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16126,1:03.966.917,2.833 us,,,,,[1 SOF],[Frame: 417] +0,,16127,1:03.966.921,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A1 53 5D B6 4A E2 67 5F 84 21 01 29 67 5F 1C A2… +0,,16131,1:03.967.918,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,16132,1:03.982.923,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B6 C1 46 F9 09 A8 F5 D5 66 9D D5 9F 24 98 84 48… +0,,16136,1:03.983.920,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,16137,1:03.997.925,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16141,1:03.998.922,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,16142,1:03.998.925,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B6 C1 46 F9 09 A8 F5 D5 66 9D D5 9F 24 98 84 48… +0,,16146,1:03.999.922,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,16147,1:04.014.927,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E7 91 98 AD E2 C7 B3 4B 09 D9 80 39 F8 E9 D3 B6… +0,,16151,1:04.015.924,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,16152,1:04.029.929,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16156,1:04.030.926,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,16157,1:04.030.930,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E7 91 98 AD E2 C7 B3 4B 09 D9 80 39 F8 E9 D3 B6… +0,,16161,1:04.031.926,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,16162,1:04.046.932,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 78 DC 02 19 2B FB 86 AE D0 BC 47 9D DD A1 D0 22… +0,,16166,1:04.047.929,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,16167,1:04.061.934,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16171,1:04.062.931,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,16172,1:04.062.934,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 78 DC 02 19 2B FB 86 AE D0 BC 47 9D DD A1 D0 22… +0,,16176,1:04.063.931,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,16177,1:04.078.936,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5B C5 D2 08 35 9D 82 66 F0 EB CE 9D 2E 00 7B EF… +0,,16181,1:04.079.933,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,16182,1:04.093.938,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16186,1:04.094.935,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,16187,1:04.094.938,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5B C5 D2 08 35 9D 82 66 F0 EB CE 9D 2E 00 7B EF… +0,,16191,1:04.095.935,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,16192,1:04.110.941,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 9A F0 24 13 DF CC 45 FF 9D 3B D1 06 D6 8A EE… +0,,16196,1:04.111.938,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,16197,1:04.125.943,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16201,1:04.126.940,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,16202,1:04.126.943,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 9A F0 24 13 DF CC 45 FF 9D 3B D1 06 D6 8A EE… +0,,16206,1:04.127.940,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,16207,1:04.142.945,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 2E 67 21 35 E1 3F 58 1F 0D 26 0D 27 1C FD 63… +0,,16211,1:04.143.942,14.004.750 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,16212,1:04.157.947,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16216,1:04.158.944,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,16217,1:04.158.947,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 2E 67 21 35 E1 3F 58 1F 0D 26 0D 27 1C FD 63… +0,,16221,1:04.159.944,15.004.916 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,16222,1:04.174.949,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC 35 E5 7A 44 C7 F5 C8 6C A9 4B 0D 57 1D 04 B8… +0,,16226,1:04.175.946,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,16227,1:04.189.952,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16231,1:04.190.948,16.005.041 ms,,,,,[17 SOF],[Frames: 641 - 657] +0,,16232,1:04.206.954,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6B D3 53 DD 36 C7 29 20 19 6E 73 EF 42 5E B0 83… +0,,16236,1:04.207.951,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,16237,1:04.221.956,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16241,1:04.222.953,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,16242,1:04.222.956,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6B D3 53 DD 36 C7 29 20 19 6E 73 EF 42 5E B0 83… +0,,16246,1:04.223.953,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,16247,1:04.238.958,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 40 A0 72 11 6C 73 82 D3 71 05 A3 4C 95 4A C2… +0,,16251,1:04.239.955,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,16252,1:04.253.960,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16256,1:04.254.957,2.833 us,,,,,[1 SOF],[Frame: 705] +0,,16257,1:04.254.961,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA 40 A0 72 11 6C 73 82 D3 71 05 A3 4C 95 4A C2… +0,,16261,1:04.255.957,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,16262,1:04.270.963,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 65 AB 3E B4 33 02 76 C1 73 9B 5E E1 1C 65 EA 5B… +0,,16266,1:04.271.960,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,16267,1:04.285.965,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16271,1:04.286.962,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,16272,1:04.286.965,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 65 AB 3E B4 33 02 76 C1 73 9B 5E E1 1C 65 EA 5B… +0,,16276,1:04.287.962,15.004.916 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,16277,1:04.302.967,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 12 1A 6B 35 2D 54 7E 80 FA 3D F0 4F E2 52 4A 15… +0,,16281,1:04.303.964,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,16282,1:04.317.969,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16286,1:04.318.966,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,16287,1:04.318.969,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 12 1A 6B 35 2D 54 7E 80 FA 3D F0 4F E2 52 4A 15… +0,,16291,1:04.319.966,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,16292,1:04.334.972,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F2 2F 2A A9 6C 53 56 59 99 31 67 2D E8 10 4D 56… +0,,16296,1:04.335.969,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,16297,1:04.349.974,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16301,1:04.350.971,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,16302,1:04.350.974,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F2 2F 2A A9 6C 53 56 59 99 31 67 2D E8 10 4D 56… +0,,16306,1:04.351.971,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,16307,1:04.366.976,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 07 C9 02 C9 F3 BF 6D 76 26 B8 B9 76 97 34 11 47… +0,,16311,1:04.367.973,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,16312,1:04.381.978,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16316,1:04.382.975,2.833 us,,,,,[1 SOF],[Frame: 833] +0,,16317,1:04.382.978,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 07 C9 02 C9 F3 BF 6D 76 26 B8 B9 76 97 34 11 47… +0,,16321,1:04.383.975,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,16322,1:04.398.981,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 63 B5 C3 84 7A 91 C3 E7 DC 28 EA 24 2D 5C CD… +0,,16326,1:04.399.977,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,16327,1:04.413.983,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16331,1:04.414.980,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,16332,1:04.414.983,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 63 B5 C3 84 7A 91 C3 E7 DC 28 EA 24 2D 5C CD… +0,,16336,1:04.415.980,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,16337,1:04.430.985,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E8 28 D9 F5 6E BD DD 44 A9 AC 72 CB 22 C5 28 D9… +0,,16341,1:04.431.982,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,16342,1:04.445.987,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16346,1:04.446.984,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,16347,1:04.446.987,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E8 28 D9 F5 6E BD DD 44 A9 AC 72 CB 22 C5 28 D9… +0,,16351,1:04.447.984,15.004.916 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,16352,1:04.462.989,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 90 F6 36 CC 16 61 DB A0 3E 04 46 1D 89 67 2A C8… +0,,16356,1:04.463.986,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,16357,1:04.477.992,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16361,1:04.478.988,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,16362,1:04.478.992,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 90 F6 36 CC 16 61 DB A0 3E 04 46 1D 89 67 2A C8… +0,,16366,1:04.479.989,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,16367,1:04.494.994,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 58 BB 2B 60 9D 1E A1 9E 89 8D 47 12 62 E3 DC B7… +0,,16371,1:04.495.991,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,16372,1:04.509.996,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16376,1:04.510.993,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,16377,1:04.510.996,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 58 BB 2B 60 9D 1E A1 9E 89 8D 47 12 62 E3 DC B7… +0,,16381,1:04.511.993,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,16382,1:04.526.998,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9A 33 4F C2 7D 86 98 10 76 35 7F 94 76 97 C6 7B… +0,,16386,1:04.527.995,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,16387,1:04.542.000,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16391,1:04.542.997,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,16392,1:04.543.001,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9A 33 4F C2 7D 86 98 10 76 35 7F 94 76 97 C6 7B… +0,,16396,1:04.543.997,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,16397,1:04.559.003,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 05 E1 17 18 00 FD C0 C0 F2 0B 7C DA 3F 39 F5… +0,,16401,1:04.560.000,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,16402,1:04.574.005,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16406,1:04.575.002,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,16407,1:04.575.005,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 05 E1 17 18 00 FD C0 C0 F2 0B 7C DA 3F 39 F5… +0,,16411,1:04.576.002,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,16412,1:04.591.007,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3A A8 19 0F 41 52 78 F1 66 A6 EB 0E 9C A4 E1 EB… +0,,16416,1:04.592.004,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,16417,1:04.606.009,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16421,1:04.607.006,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,16422,1:04.607.009,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3A A8 19 0F 41 52 78 F1 66 A6 EB 0E 9C A4 E1 EB… +0,,16426,1:04.608.006,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,16427,1:04.623.012,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8D BC 57 07 DA 03 0E 75 77 34 C0 54 F4 84 B7 2A… +0,,16431,1:04.624.009,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,16432,1:04.638.014,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16436,1:04.639.011,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,16437,1:04.639.014,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8D BC 57 07 DA 03 0E 75 77 34 C0 54 F4 84 B7 2A… +0,,16441,1:04.640.011,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,16442,1:04.655.016,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 B0 08 B4 2B 1E 96 0A C8 96 4F 3D C1 02 9E 1C… +0,,16446,1:04.656.013,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,16447,1:04.670.018,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16451,1:04.671.015,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,16452,1:04.671.018,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 B0 08 B4 2B 1E 96 0A C8 96 4F 3D C1 02 9E 1C… +0,,16456,1:04.672.015,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,16457,1:04.687.021,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 25 7A 6D 21 03 BD 40 40 42 D8 8A DF 33 72 05 96… +0,,16461,1:04.688.017,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,16462,1:04.702.023,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16466,1:04.703.020,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,16467,1:04.703.023,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 25 7A 6D 21 03 BD 40 40 42 D8 8A DF 33 72 05 96… +0,,16471,1:04.704.020,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,16472,1:04.719.025,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 CE AA B6 3D 29 1F 46 87 66 FC AA 65 EA 4F E1… +0,,16476,1:04.720.022,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,16477,1:04.734.027,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16481,1:04.735.024,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,16482,1:04.735.027,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 CE AA B6 3D 29 1F 46 87 66 FC AA 65 EA 4F E1… +0,,16486,1:04.736.024,15.004.916 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,16487,1:04.751.029,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 02 BA 7C C6 3C 94 98 7F B2 01 C4 BA 66 AE F0 8E… +0,,16491,1:04.752.026,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,16492,1:04.766.032,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16496,1:04.767.028,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,16497,1:04.767.032,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 02 BA 7C C6 3C 94 98 7F B2 01 C4 BA 66 AE F0 8E… +0,,16501,1:04.768.029,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,16502,1:04.783.034,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C DD C6 96 D1 CA 68 AC 02 8B 0D BB 88 64 2E E9… +0,,16506,1:04.784.031,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,16507,1:04.798.036,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16511,1:04.799.033,2.833 us,,,,,[1 SOF],[Frame: 1249] +0,,16512,1:04.799.036,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7C DD C6 96 D1 CA 68 AC 02 8B 0D BB 88 64 2E E9… +0,,16516,1:04.800.033,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,16517,1:04.815.038,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 72 36 1D 5C 09 1D 8F 9B 25 99 A0 78 DD BB DB 30… +0,,16521,1:04.816.035,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,16522,1:04.830.040,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16526,1:04.831.037,16.005.041 ms,,,,,[17 SOF],[Frames: 1281 - 1297] +0,,16527,1:04.847.043,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C5 6F AC 62 10 7D DB 19 4F ED 81 8D 53 7A 56 62… +0,,16531,1:04.848.040,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,16532,1:04.862.045,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16536,1:04.863.042,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,16537,1:04.863.045,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C5 6F AC 62 10 7D DB 19 4F ED 81 8D 53 7A 56 62… +0,,16541,1:04.864.042,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,16542,1:04.879.047,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C7 6E 59 BF 13 D8 D0 8C 61 21 E0 D1 EB E3 47 84… +0,,16546,1:04.880.044,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,16547,1:04.894.049,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16551,1:04.895.046,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,16552,1:04.895.049,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C7 6E 59 BF 13 D8 D0 8C 61 21 E0 D1 EB E3 47 84… +0,,16556,1:04.896.046,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,16557,1:04.911.052,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 C3 54 80 E1 3C DD E7 46 9F C8 96 76 18 C7 71… +0,,16561,1:04.912.049,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,16562,1:04.926.054,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16566,1:04.927.051,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,16567,1:04.927.054,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 C3 54 80 E1 3C DD E7 46 9F C8 96 76 18 C7 71… +0,,16571,1:04.928.051,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,16572,1:04.943.056,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 46 F8 AF BC 93 D2 A5 F0 94 46 CC E8 94 49 E1 35… +0,,16576,1:04.944.053,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,16577,1:04.958.058,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16581,1:04.959.055,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,16582,1:04.959.058,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 46 F8 AF BC 93 D2 A5 F0 94 46 CC E8 94 49 E1 35… +0,,16586,1:04.960.055,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,16587,1:04.975.061,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A8 FF CA D0 DD 10 DD 07 F1 5E 5A 60 E6 83 97 4F… +0,,16591,1:04.976.057,14.004.750 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,16592,1:04.990.063,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16596,1:04.991.060,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,16597,1:04.991.063,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A8 FF CA D0 DD 10 DD 07 F1 5E 5A 60 E6 83 97 4F… +0,,16601,1:04.992.060,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,16602,1:05.007.065,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C 09 CB A1 D1 9D BE F5 70 00 68 D4 BD DC 62 0B… +0,,16606,1:05.008.062,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,16607,1:05.022.067,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16611,1:05.023.064,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,16612,1:05.023.067,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7C 09 CB A1 D1 9D BE F5 70 00 68 D4 BD DC 62 0B… +0,,16616,1:05.024.064,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,16617,1:05.039.069,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A8 F7 D8 44 05 D2 E0 FA ED 57 3E E2 04 4C 9C 83… +0,,16621,1:05.040.066,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,16622,1:05.054.072,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16626,1:05.055.068,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,16627,1:05.055.072,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A8 F7 D8 44 05 D2 E0 FA ED 57 3E E2 04 4C 9C 83… +0,,16631,1:05.056.069,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,16632,1:05.071.074,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 43 B7 E4 91 64 E3 7D 97 55 52 28 BD 18 8D F6… +0,,16636,1:05.072.071,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,16637,1:05.086.076,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16641,1:05.087.073,2.833 us,,,,,[1 SOF],[Frame: 1537] +0,,16642,1:05.087.076,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 43 B7 E4 91 64 E3 7D 97 55 52 28 BD 18 8D F6… +0,,16646,1:05.088.073,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,16647,1:05.103.078,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DF FE 78 73 92 02 A1 33 AF ED 9C 7C 46 7E F5 DC… +0,,16651,1:05.104.075,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,16652,1:05.118.080,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16656,1:05.119.077,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,16657,1:05.119.081,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DF FE 78 73 92 02 A1 33 AF ED 9C 7C 46 7E F5 DC… +0,,16661,1:05.120.077,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,16662,1:05.135.083,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 64 D5 30 FD C1 4B 46 2D B0 A8 93 C4 F8 EE B7 9F… +0,,16666,1:05.136.080,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,16667,1:05.150.085,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16671,1:05.151.082,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,16672,1:05.151.085,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 64 D5 30 FD C1 4B 46 2D B0 A8 93 C4 F8 EE B7 9F… +0,,16676,1:05.152.082,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,16677,1:05.167.087,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 E7 47 D8 24 F7 B7 52 4C 2A 58 EE F1 31 67 26… +0,,16681,1:05.168.084,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,16682,1:05.182.089,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16686,1:05.183.086,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,16687,1:05.183.089,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 E7 47 D8 24 F7 B7 52 4C 2A 58 EE F1 31 67 26… +0,,16691,1:05.184.086,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,16692,1:05.199.092,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 71 F9 A1 C2 93 3D 0D 74 CC A9 E3 63 17 A6 44 DE… +0,,16696,1:05.200.089,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,16697,1:05.214.094,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16701,1:05.215.091,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,16702,1:05.215.094,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 71 F9 A1 C2 93 3D 0D 74 CC A9 E3 63 17 A6 44 DE… +0,,16706,1:05.216.091,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,16707,1:05.231.096,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F6 43 62 4C F0 DD B4 9A 93 17 5D E7 CF C0 D1 C2… +0,,16711,1:05.232.093,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,16712,1:05.246.098,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16716,1:05.247.095,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,16717,1:05.247.098,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F6 43 62 4C F0 DD B4 9A 93 17 5D E7 CF C0 D1 C2… +0,,16721,1:05.248.095,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,16722,1:05.263.101,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 D6 8A 38 3B 70 C5 06 9F 4E F5 5C 23 0B 88 2A… +0,,16726,1:05.264.097,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,16727,1:05.278.103,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16731,1:05.279.100,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,16732,1:05.279.103,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 D6 8A 38 3B 70 C5 06 9F 4E F5 5C 23 0B 88 2A… +0,,16736,1:05.280.100,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,16737,1:05.295.105,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0E 8E 51 74 85 A3 D4 23 9B 79 65 75 E7 1C 62 B8… +0,,16741,1:05.296.102,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,16742,1:05.310.107,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16746,1:05.311.104,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,16747,1:05.311.107,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0E 8E 51 74 85 A3 D4 23 9B 79 65 75 E7 1C 62 B8… +0,,16751,1:05.312.104,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,16752,1:05.327.109,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 54 DE 2E 46 B4 52 CD F0 D3 5D 8C 49 50 65 5B 48… +0,,16756,1:05.328.106,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,16757,1:05.342.111,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16761,1:05.343.108,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,16762,1:05.343.112,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 54 DE 2E 46 B4 52 CD F0 D3 5D 8C 49 50 65 5B 48… +0,,16766,1:05.344.109,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,16767,1:05.359.114,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1D F7 45 A4 E2 87 A9 62 D9 7D E0 E5 FA 8F FE 09… +0,,16771,1:05.360.111,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,16772,1:05.374.116,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16776,1:05.375.113,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,16777,1:05.375.116,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1D F7 45 A4 E2 87 A9 62 D9 7D E0 E5 FA 8F FE 09… +0,,16781,1:05.376.113,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,16782,1:05.391.118,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 E8 4A D4 D8 49 15 8A 46 0A 83 B3 07 9A 08 91… +0,,16786,1:05.392.115,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,16787,1:05.406.120,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16791,1:05.407.117,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,16792,1:05.407.121,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 76 E8 4A D4 D8 49 15 8A 46 0A 83 B3 07 9A 08 91… +0,,16796,1:05.408.117,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,16797,1:05.423.123,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 B7 C5 E8 41 51 AC 7C 6C F0 B7 5B 08 3E 4F B0… +0,,16801,1:05.424.120,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,16802,1:05.438.125,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16806,1:05.439.122,16.005.041 ms,,,,,[17 SOF],[Frames: 1889 - 1905] +0,,16807,1:05.455.127,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 76 53 16 70 52 72 DE BA 90 BA 26 5A 8E BC A3 4B… +0,,16811,1:05.456.124,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,16812,1:05.470.129,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16816,1:05.471.126,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,16817,1:05.471.129,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 76 53 16 70 52 72 DE BA 90 BA 26 5A 8E BC A3 4B… +0,,16821,1:05.472.126,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,16822,1:05.487.132,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EA BD A1 09 F4 93 1B 18 92 E2 AA B3 FB 59 13 58… +0,,16826,1:05.488.129,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,16827,1:05.502.134,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16831,1:05.503.131,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,16832,1:05.503.134,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EA BD A1 09 F4 93 1B 18 92 E2 AA B3 FB 59 13 58… +0,,16836,1:05.504.131,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,16837,1:05.519.136,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BF F4 A6 18 88 5E 35 53 BD 0F EC 8E 97 F0 B0 81… +0,,16841,1:05.520.133,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,16842,1:05.534.138,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16846,1:05.535.135,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,16847,1:05.535.138,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BF F4 A6 18 88 5E 35 53 BD 0F EC 8E 97 F0 B0 81… +0,,16851,1:05.536.135,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,16852,1:05.551.141,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 DE E0 EB 28 99 6E 7F 82 60 E3 3D BA 21 E2 27… +0,,16856,1:05.552.137,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,16857,1:05.566.143,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16861,1:05.567.139,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,16862,1:05.567.143,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 DE E0 EB 28 99 6E 7F 82 60 E3 3D BA 21 E2 27… +0,,16866,1:05.568.140,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,16867,1:05.583.145,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 4E 32 0D FB B1 87 D4 E4 F2 18 8E B7 17 DD AE… +0,,16871,1:05.584.142,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,16872,1:05.598.147,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16876,1:05.599.144,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,16877,1:05.599.147,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 4E 32 0D FB B1 87 D4 E4 F2 18 8E B7 17 DD AE… +0,,16881,1:05.600.144,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,16882,1:05.615.149,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C C7 76 8B B0 4A B1 C7 6A CC 88 93 AE 16 3D 67… +0,,16886,1:05.616.146,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,16887,1:05.630.151,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16891,1:05.631.148,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,16892,1:05.631.152,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C C7 76 8B B0 4A B1 C7 6A CC 88 93 AE 16 3D 67… +0,,16896,1:05.632.149,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,16897,1:05.647.154,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 FC E7 D0 33 01 36 18 BD 52 C9 2B 1A 1D F1 7B… +0,,16901,1:05.648.151,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,16902,1:05.662.156,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16906,1:05.663.153,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,16907,1:05.663.156,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 FC E7 D0 33 01 36 18 BD 52 C9 2B 1A 1D F1 7B… +0,,16911,1:05.664.153,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,16912,1:05.679.158,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 95 F1 A9 EA 73 92 80 5C 9A 57 16 FB 78 87 F3 4A… +0,,16916,1:05.680.155,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,16917,1:05.694.160,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16921,1:05.695.157,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,16922,1:05.695.160,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 95 F1 A9 EA 73 92 80 5C 9A 57 16 FB 78 87 F3 4A… +0,,16926,1:05.696.157,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,16927,1:05.711.163,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A 50 F0 8B 82 AA 4A AF C4 3A 9C 3C A0 44 25 21… +0,,16931,1:05.712.160,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,16932,1:05.726.165,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16936,1:05.727.162,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,16937,1:05.727.165,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A 50 F0 8B 82 AA 4A AF C4 3A 9C 3C A0 44 25 21… +0,,16941,1:05.728.162,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,16942,1:05.743.167,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FE DA 18 41 D9 64 DC 35 05 06 B6 DF C7 8A A2 61… +0,,16946,1:05.744.164,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,16947,1:05.758.169,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16951,1:05.759.166,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,16952,1:05.759.169,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FE DA 18 41 D9 64 DC 35 05 06 B6 DF C7 8A A2 61… +0,,16956,1:05.760.166,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,16957,1:05.775.172,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C3 AB D2 9F 6E A0 88 E4 CA 05 86 54 AE 89 32 43… +0,,16961,1:05.776.168,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,16962,1:05.790.174,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16966,1:05.791.171,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,16967,1:05.791.174,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C3 AB D2 9F 6E A0 88 E4 CA 05 86 54 AE 89 32 43… +0,,16971,1:05.792.171,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,16972,1:05.807.176,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 80 97 6B 0D EC 05 54 26 67 95 DD 53 5D 3C 31 F1… +0,,16976,1:05.808.173,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,16977,1:05.822.178,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16981,1:05.823.175,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,16982,1:05.823.178,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 80 97 6B 0D EC 05 54 26 67 95 DD 53 5D 3C 31 F1… +0,,16986,1:05.824.175,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,16987,1:05.839.180,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 04 F3 DE DD 31 E6 97 11 89 92 25 82 DA 1E 1B… +0,,16991,1:05.840.177,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,16992,1:05.854.183,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,16996,1:05.855.179,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,16997,1:05.855.183,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 04 F3 DE DD 31 E6 97 11 89 92 25 82 DA 1E 1B… +0,,17001,1:05.856.180,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,17002,1:05.871.185,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FD 03 90 EE 8B 24 2C F3 EA 53 CF 9F 2A A4 B5 B6… +0,,17006,1:05.872.182,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,17007,1:05.886.187,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17011,1:05.887.184,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,17012,1:05.887.187,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FD 03 90 EE 8B 24 2C F3 EA 53 CF 9F 2A A4 B5 B6… +0,,17016,1:05.888.184,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,17017,1:05.903.189,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C A8 60 EB 4C D1 74 0A DD 6A 16 B1 8F CA BF 5D… +0,,17021,1:05.904.186,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,17022,1:05.918.191,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17026,1:05.919.188,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,17027,1:05.919.192,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C A8 60 EB 4C D1 74 0A DD 6A 16 B1 8F CA BF 5D… +0,,17031,1:05.920.188,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,17032,1:05.935.194,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DA 76 9F 28 0E B4 FE 34 96 42 2F 20 E4 2E 3E C3… +0,,17036,1:05.936.191,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,17037,1:05.950.196,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17041,1:05.951.193,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,17042,1:05.951.196,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DA 76 9F 28 0E B4 FE 34 96 42 2F 20 E4 2E 3E C3… +0,,17046,1:05.952.193,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,17047,1:05.967.198,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D0 BA A2 13 AF DF 3E 80 AB 88 C2 F7 78 45 AA B5… +0,,17051,1:05.968.195,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,17052,1:05.982.200,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17056,1:05.983.197,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,17057,1:05.983.200,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D0 BA A2 13 AF DF 3E 80 AB 88 C2 F7 78 45 AA B5… +0,,17061,1:05.984.197,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,17062,1:05.999.203,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 10 02 58 A6 E5 D0 28 0F 48 CF 49 65 E3 B0 66… +0,,17066,1:06.000.200,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,17067,1:06.014.205,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17071,1:06.015.202,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,17072,1:06.015.205,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 10 02 58 A6 E5 D0 28 0F 48 CF 49 65 E3 B0 66… +0,,17076,1:06.016.202,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,17077,1:06.031.207,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 57 A0 4F F8 97 12 9A DA 47 70 4E F6 FC 84 63… +0,,17081,1:06.032.204,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,17082,1:06.046.209,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17086,1:06.047.206,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,17087,1:06.047.209,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 57 A0 4F F8 97 12 9A DA 47 70 4E F6 FC 84 63… +0,,17091,1:06.048.206,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,17092,1:06.063.212,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D BC 07 94 71 65 5C BA 46 73 97 98 20 41 C5 FA… +0,,17096,1:06.064.208,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,17097,1:06.078.214,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17101,1:06.079.211,16.005.041 ms,,,,,[17 SOF],[Frames: 481 - 497] +0,,17102,1:06.095.216,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 67 B1 8F 38 2F DB 76 8C 52 26 3E 5B AC 8A F4… +0,,17106,1:06.096.213,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,17107,1:06.110.218,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17111,1:06.111.215,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,17112,1:06.111.218,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 67 B1 8F 38 2F DB 76 8C 52 26 3E 5B AC 8A F4… +0,,17116,1:06.112.215,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,17117,1:06.127.220,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 53 B6 58 13 27 F2 14 6F 48 1A FF 78 6A 26 03… +0,,17121,1:06.128.217,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,17122,1:06.142.223,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17126,1:06.143.219,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,17127,1:06.143.223,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 53 B6 58 13 27 F2 14 6F 48 1A FF 78 6A 26 03… +0,,17131,1:06.144.220,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,17132,1:06.159.225,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B BD CD 61 06 AF 73 47 01 17 A9 5D 0A 56 99 B8… +0,,17136,1:06.160.222,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,17137,1:06.174.227,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17141,1:06.175.224,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,17142,1:06.175.227,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B BD CD 61 06 AF 73 47 01 17 A9 5D 0A 56 99 B8… +0,,17146,1:06.176.224,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,17147,1:06.191.229,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 CB 27 64 A0 A0 EE C1 2D 2F 44 B2 E0 C1 2B EC… +0,,17151,1:06.192.226,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,17152,1:06.206.231,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17156,1:06.207.228,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,17157,1:06.207.232,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 CB 27 64 A0 A0 EE C1 2D 2F 44 B2 E0 C1 2B EC… +0,,17161,1:06.208.228,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,17162,1:06.223.234,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FE C2 4A C2 FA 11 7E AE 12 60 05 1A D8 2E 0B F5… +0,,17166,1:06.224.231,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,17167,1:06.238.236,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17171,1:06.239.233,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,17172,1:06.239.236,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FE C2 4A C2 FA 11 7E AE 12 60 05 1A D8 2E 0B F5… +0,,17176,1:06.240.233,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,17177,1:06.255.238,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 30 2E F6 D8 80 A8 C2 8B A5 C2 96 3F 3B C6 8A CE… +0,,17181,1:06.256.235,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,17182,1:06.270.240,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17186,1:06.271.237,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,17187,1:06.271.240,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 30 2E F6 D8 80 A8 C2 8B A5 C2 96 3F 3B C6 8A CE… +0,,17191,1:06.272.237,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,17192,1:06.287.243,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F7 85 59 4A FA 12 1F DD 24 5F F3 1C CD 17 FF 15… +0,,17196,1:06.288.240,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,17197,1:06.302.245,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17201,1:06.303.242,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,17202,1:06.303.245,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F7 85 59 4A FA 12 1F DD 24 5F F3 1C CD 17 FF 15… +0,,17206,1:06.304.242,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,17207,1:06.319.247,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 47 D7 4A 0E D1 E6 51 B8 E0 70 08 56 0D 46 8C… +0,,17211,1:06.320.244,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,17212,1:06.334.249,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17216,1:06.335.246,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,17217,1:06.335.249,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 47 D7 4A 0E D1 E6 51 B8 E0 70 08 56 0D 46 8C… +0,,17221,1:06.336.246,15.004.916 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,17222,1:06.351.252,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C6 E5 46 FF F1 DE 45 01 CB 2B 51 83 F1 40 FF BA… +0,,17226,1:06.352.248,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,17227,1:06.366.254,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17231,1:06.367.251,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,17232,1:06.367.254,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C6 E5 46 FF F1 DE 45 01 CB 2B 51 83 F1 40 FF BA… +0,,17236,1:06.368.251,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,17237,1:06.383.256,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E 71 F2 21 96 C6 35 E0 27 00 D9 9A 8D DD 55 CD… +0,,17241,1:06.384.253,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,17242,1:06.398.258,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17246,1:06.399.255,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,17247,1:06.399.258,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E 71 F2 21 96 C6 35 E0 27 00 D9 9A 8D DD 55 CD… +0,,17251,1:06.400.255,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,17252,1:06.415.260,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 27 5E 81 10 BC CA 3F 77 40 6B 81 74 A2 C2 7B 1B… +0,,17256,1:06.416.257,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,17257,1:06.430.263,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17261,1:06.431.259,2.833 us,,,,,[1 SOF],[Frame: 833] +0,,17262,1:06.431.263,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 27 5E 81 10 BC CA 3F 77 40 6B 81 74 A2 C2 7B 1B… +0,,17266,1:06.432.260,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,17267,1:06.447.265,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 9F EF C6 D6 BA 2B 8D 03 CD E0 FA 22 EF C5 46… +0,,17271,1:06.448.262,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,17272,1:06.462.267,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17276,1:06.463.264,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,17277,1:06.463.267,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 9F EF C6 D6 BA 2B 8D 03 CD E0 FA 22 EF C5 46… +0,,17281,1:06.464.264,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,17282,1:06.479.269,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 1E 3F 5F CB D3 EA 1C D2 AC 00 A4 52 9A 1F F4… +0,,17286,1:06.480.266,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,17287,1:06.494.271,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17291,1:06.495.268,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,17292,1:06.495.272,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 1E 3F 5F CB D3 EA 1C D2 AC 00 A4 52 9A 1F F4… +0,,17296,1:06.496.268,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,17297,1:06.511.274,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 8F 1D C2 3A E2 66 AA 46 80 C1 F5 E4 96 BF B8… +0,,17301,1:06.512.271,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,17302,1:06.526.276,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17306,1:06.527.273,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,17307,1:06.527.276,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 8F 1D C2 3A E2 66 AA 46 80 C1 F5 E4 96 BF B8… +0,,17311,1:06.528.273,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,17312,1:06.543.278,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 BB 1F 5C 0A 7E 97 A3 0E 33 66 3E 8F A8 A6 EF… +0,,17316,1:06.544.275,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,17317,1:06.558.280,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17321,1:06.559.277,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,17322,1:06.559.280,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 BB 1F 5C 0A 7E 97 A3 0E 33 66 3E 8F A8 A6 EF… +0,,17326,1:06.560.277,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,17327,1:06.575.283,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 D8 18 51 38 D4 05 B2 C6 B5 D4 5B 2F BD 3C CC… +0,,17331,1:06.576.280,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,17332,1:06.590.285,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17336,1:06.591.282,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,17337,1:06.591.285,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 D8 18 51 38 D4 05 B2 C6 B5 D4 5B 2F BD 3C CC… +0,,17341,1:06.592.282,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,17342,1:06.607.287,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E8 6C 70 32 30 7D 5E 85 99 42 63 50 8C DC 98 71… +0,,17346,1:06.608.284,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,17347,1:06.622.289,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17351,1:06.623.286,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,17352,1:06.623.289,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E8 6C 70 32 30 7D 5E 85 99 42 63 50 8C DC 98 71… +0,,17356,1:06.624.286,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,17357,1:06.639.292,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 59 B2 3B 82 0F 4C DA C0 0F 88 5E D9 D2 B1 9E 00… +0,,17361,1:06.640.288,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,17362,1:06.654.294,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17366,1:06.655.291,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,17367,1:06.655.294,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 59 B2 3B 82 0F 4C DA C0 0F 88 5E D9 D2 B1 9E 00… +0,,17371,1:06.656.291,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,17372,1:06.671.296,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 BD 54 2C E8 99 16 2D 1D EA C6 9D BC A2 FA 65… +0,,17376,1:06.672.293,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,17377,1:06.686.298,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17381,1:06.687.295,16.005.041 ms,,,,,[17 SOF],[Frames: 1089 - 1105] +0,,17382,1:06.703.300,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 20 9F 6D 57 E5 AE 21 90 1D 25 4F 1E FC 1B 9A… +0,,17386,1:06.704.297,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,17387,1:06.718.303,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17391,1:06.719.299,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,17392,1:06.719.303,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 20 9F 6D 57 E5 AE 21 90 1D 25 4F 1E FC 1B 9A… +0,,17396,1:06.720.300,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,17397,1:06.735.305,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 1A 3A 59 EF 4B 52 5C D8 B9 EB 7B D4 BC 5E BF… +0,,17401,1:06.736.302,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,17402,1:06.750.307,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17406,1:06.751.304,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,17407,1:06.751.307,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 1A 3A 59 EF 4B 52 5C D8 B9 EB 7B D4 BC 5E BF… +0,,17411,1:06.752.304,15.004.916 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,17412,1:06.767.309,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 59 89 84 26 D2 DF 13 C3 E4 DD 5A D9 03 82 CD 27… +0,,17416,1:06.768.306,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,17417,1:06.782.311,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17421,1:06.783.308,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,17422,1:06.783.312,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 59 89 84 26 D2 DF 13 C3 E4 DD 5A D9 03 82 CD 27… +0,,17426,1:06.784.308,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,17427,1:06.799.314,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A9 7B 27 66 6F D3 28 CA F7 16 44 9A 61 EE 15 22… +0,,17431,1:06.800.311,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,17432,1:06.814.316,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17436,1:06.815.313,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,17437,1:06.815.316,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A9 7B 27 66 6F D3 28 CA F7 16 44 9A 61 EE 15 22… +0,,17441,1:06.816.313,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,17442,1:06.831.318,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 44 EC 00 46 AD D7 1C F9 C9 43 F7 FE 62 39 AD 0E… +0,,17446,1:06.832.315,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,17447,1:06.846.320,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17451,1:06.847.317,2.833 us,,,,,[1 SOF],[Frame: 1249] +0,,17452,1:06.847.320,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 44 EC 00 46 AD D7 1C F9 C9 43 F7 FE 62 39 AD 0E… +0,,17456,1:06.848.317,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,17457,1:06.863.323,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 C7 1A AF 5F 25 11 39 12 E3 60 F6 EA EE 34 0A… +0,,17461,1:06.864.320,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,17462,1:06.878.325,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17466,1:06.879.322,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,17467,1:06.879.325,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 C7 1A AF 5F 25 11 39 12 E3 60 F6 EA EE 34 0A… +0,,17471,1:06.880.322,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,17472,1:06.895.327,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EC 0E 9B 14 35 B0 1F A0 9C 8A 3B 22 E1 98 39 D8… +0,,17476,1:06.896.324,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,17477,1:06.910.329,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17481,1:06.911.326,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,17482,1:06.911.329,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EC 0E 9B 14 35 B0 1F A0 9C 8A 3B 22 E1 98 39 D8… +0,,17486,1:06.912.326,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,17487,1:06.927.332,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B2 C5 AA 25 6E 47 1E F9 FE BD 17 43 BC CC 2B F1… +0,,17491,1:06.928.328,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,17492,1:06.942.334,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17496,1:06.943.330,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,17497,1:06.943.334,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B2 C5 AA 25 6E 47 1E F9 FE BD 17 43 BC CC 2B F1… +0,,17501,1:06.944.331,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,17502,1:06.959.336,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 56 4D 6C 3A CB D1 1D 1F C7 12 F3 96 8A B3 D4 0D… +0,,17506,1:06.960.333,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,17507,1:06.974.338,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17511,1:06.975.335,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,17512,1:06.975.338,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 56 4D 6C 3A CB D1 1D 1F C7 12 F3 96 8A B3 D4 0D… +0,,17516,1:06.976.335,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,17517,1:06.991.340,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C DE 8F 01 AB 23 8F 10 8C E4 82 88 CE F1 56 77… +0,,17521,1:06.992.337,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,17522,1:07.006.342,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17526,1:07.007.339,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,17527,1:07.007.343,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9C DE 8F 01 AB 23 8F 10 8C E4 82 88 CE F1 56 77… +0,,17531,1:07.008.340,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,17532,1:07.023.345,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E7 2C 8A 61 81 4B 0E 14 AD 3A E8 0C B5 94 C3 D8… +0,,17536,1:07.024.342,14.004.750 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,17537,1:07.038.347,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17541,1:07.039.344,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,17542,1:07.039.347,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E7 2C 8A 61 81 4B 0E 14 AD 3A E8 0C B5 94 C3 D8… +0,,17546,1:07.040.344,15.004.916 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,17547,1:07.055.349,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 37 B1 D0 5A C7 69 B1 1B EC 29 34 78 82 37 D2 00… +0,,17551,1:07.056.346,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,17552,1:07.070.351,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17556,1:07.071.348,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,17557,1:07.071.352,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 37 B1 D0 5A C7 69 B1 1B EC 29 34 78 82 37 D2 00… +0,,17561,1:07.072.348,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,17562,1:07.087.354,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2E 18 C8 AA F2 A0 14 A3 09 56 62 51 CD 88 73 7D… +0,,17566,1:07.088.351,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,17567,1:07.102.356,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17571,1:07.103.353,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,17572,1:07.103.356,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2E 18 C8 AA F2 A0 14 A3 09 56 62 51 CD 88 73 7D… +0,,17576,1:07.104.353,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,17577,1:07.119.358,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 C4 C4 21 3A 83 97 7C 45 48 E1 AC 4B CC DD BF… +0,,17581,1:07.120.355,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,17582,1:07.134.360,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17586,1:07.135.357,2.833 us,,,,,[1 SOF],[Frame: 1537] +0,,17587,1:07.135.360,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 C4 C4 21 3A 83 97 7C 45 48 E1 AC 4B CC DD BF… +0,,17591,1:07.136.357,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,17592,1:07.151.363,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D6 28 E0 C0 43 9B DC 36 22 67 D3 0E E8 D3 ED ED… +0,,17596,1:07.152.360,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,17597,1:07.166.365,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17601,1:07.167.362,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,17602,1:07.167.365,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D6 28 E0 C0 43 9B DC 36 22 67 D3 0E E8 D3 ED ED… +0,,17606,1:07.168.362,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,17607,1:07.183.367,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A6 97 F6 92 F5 DE FB 31 80 32 75 61 BD DD 65 54… +0,,17611,1:07.184.364,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,17612,1:07.198.369,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17616,1:07.199.366,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,17617,1:07.199.369,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A6 97 F6 92 F5 DE FB 31 80 32 75 61 BD DD 65 54… +0,,17621,1:07.200.366,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,17622,1:07.215.371,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CE 1C B9 AE 4D B4 B0 D4 21 E4 80 EA 5B E6 51 9B… +0,,17626,1:07.216.368,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,17627,1:07.230.374,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17631,1:07.231.370,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,17632,1:07.231.374,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CE 1C B9 AE 4D B4 B0 D4 21 E4 80 EA 5B E6 51 9B… +0,,17636,1:07.232.371,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,17637,1:07.247.376,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E3 9E A6 BB 81 4E AE 01 72 F2 74 1E 5E 0D 92 7E… +0,,17641,1:07.248.373,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,17642,1:07.262.378,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17646,1:07.263.375,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,17647,1:07.263.378,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E3 9E A6 BB 81 4E AE 01 72 F2 74 1E 5E 0D 92 7E… +0,,17651,1:07.264.375,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,17652,1:07.279.380,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 2A 13 44 49 11 59 D8 26 E4 97 5D 70 79 8B 72… +0,,17656,1:07.280.377,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,17657,1:07.294.382,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17661,1:07.295.379,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,17662,1:07.295.383,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 2A 13 44 49 11 59 D8 26 E4 97 5D 70 79 8B 72… +0,,17666,1:07.296.379,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,17667,1:07.311.385,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4D 43 F9 BE AD 1A 48 54 FE 9E 63 E9 F2 60 C8 C6… +0,,17671,1:07.312.382,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,17672,1:07.326.387,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17676,1:07.327.384,16.005.041 ms,,,,,[17 SOF],[Frames: 1729 - 1745] +0,,17677,1:07.343.389,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D 43 F9 BE AD 1A 48 54 FE 9E 63 E9 F2 60 C8 C6… +0,,17681,1:07.344.386,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,17682,1:07.358.391,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17686,1:07.359.388,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,17687,1:07.359.392,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4D 43 F9 BE AD 1A 48 54 FE 9E 63 E9 F2 60 C8 C6… +0,,17691,1:07.360.388,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,17692,1:07.375.394,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4A 2F 48 F7 B8 48 C5 82 7D 43 2B B0 7B B6 F8 CD… +0,,17696,1:07.376.391,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,17697,1:07.390.396,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17701,1:07.391.393,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,17702,1:07.391.396,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4A 2F 48 F7 B8 48 C5 82 7D 43 2B B0 7B B6 F8 CD… +0,,17706,1:07.392.393,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,17707,1:07.407.398,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 66 30 E5 CF 47 05 27 B9 C1 EE 30 AC CA 5D 50 09… +0,,17711,1:07.408.395,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,17712,1:07.422.400,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17716,1:07.423.397,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,17717,1:07.423.400,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 66 30 E5 CF 47 05 27 B9 C1 EE 30 AC CA 5D 50 09… +0,,17721,1:07.424.397,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,17722,1:07.439.403,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6E 73 1C F0 5A 69 AE E7 E7 8E 9F 3E 6D 41 4F B6… +0,,17726,1:07.440.399,14.004.750 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,17727,1:07.454.405,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17731,1:07.455.402,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,17732,1:07.455.405,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6E 73 1C F0 5A 69 AE E7 E7 8E 9F 3E 6D 41 4F B6… +0,,17736,1:07.456.402,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,17737,1:07.471.407,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C8 FF 6F FB 95 C4 A0 A6 0E 3E 47 15 5B 7D A4 7C… +0,,17741,1:07.472.404,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,17742,1:07.486.409,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17746,1:07.487.406,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,17747,1:07.487.409,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 FF 6F FB 95 C4 A0 A6 0E 3E 47 15 5B 7D A4 7C… +0,,17751,1:07.488.406,15.004.916 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,17752,1:07.503.411,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C B0 B0 D0 6E 80 14 51 A7 23 F1 75 5F 86 45 17… +0,,17756,1:07.504.408,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,17757,1:07.518.414,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17761,1:07.519.410,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,17762,1:07.519.414,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C B0 B0 D0 6E 80 14 51 A7 23 F1 75 5F 86 45 17… +0,,17766,1:07.520.411,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,17767,1:07.535.416,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 07 06 70 1E 70 9F 21 13 5B B2 95 35 04 CC BF BC… +0,,17771,1:07.536.413,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,17772,1:07.550.418,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17776,1:07.551.415,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,17777,1:07.551.418,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 07 06 70 1E 70 9F 21 13 5B B2 95 35 04 CC BF BC… +0,,17781,1:07.552.415,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,17782,1:07.567.420,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CF 34 03 10 11 8F 0B D2 EA D2 A6 51 90 03 A2 03… +0,,17786,1:07.568.417,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,17787,1:07.582.422,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17791,1:07.583.419,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,17792,1:07.583.423,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CF 34 03 10 11 8F 0B D2 EA D2 A6 51 90 03 A2 03… +0,,17796,1:07.584.419,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,17797,1:07.599.425,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9F E9 85 F6 72 F2 8C 4F 2B 63 FA D1 38 D8 2C 42… +0,,17801,1:07.600.422,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,17802,1:07.614.427,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17806,1:07.615.424,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,17807,1:07.615.427,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9F E9 85 F6 72 F2 8C 4F 2B 63 FA D1 38 D8 2C 42… +0,,17811,1:07.616.424,15.005.000 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,17812,1:07.631.429,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BD E1 68 01 65 83 25 59 78 5B 6D 73 D7 32 2D BA… +0,,17816,1:07.632.426,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,17817,1:07.646.431,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17821,1:07.647.428,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,17822,1:07.647.431,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BD E1 68 01 65 83 25 59 78 5B 6D 73 D7 32 2D BA… +0,,17826,1:07.648.428,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,17827,1:07.663.434,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5E BD CF 63 62 71 AE 5D B8 87 FE C3 DB 5D FB 42… +0,,17831,1:07.664.431,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,17832,1:07.678.436,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17836,1:07.679.433,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,17837,1:07.679.436,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5E BD CF 63 62 71 AE 5D B8 87 FE C3 DB 5D FB 42… +0,,17841,1:07.680.433,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,17842,1:07.695.438,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 27 70 D2 A0 C8 47 8F 99 51 DF 86 0D 0F B8 85… +0,,17846,1:07.696.435,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,17847,1:07.710.440,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17851,1:07.711.437,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,17852,1:07.711.440,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 27 70 D2 A0 C8 47 8F 99 51 DF 86 0D 0F B8 85… +0,,17856,1:07.712.437,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,17857,1:07.727.443,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 2D 06 DA 2A 83 D6 C9 BD 81 6F DB 56 92 F6 30… +0,,17861,1:07.728.439,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,17862,1:07.742.445,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17866,1:07.743.442,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,17867,1:07.743.445,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 2D 06 DA 2A 83 D6 C9 BD 81 6F DB 56 92 F6 30… +0,,17871,1:07.744.442,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,17872,1:07.759.447,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 D3 79 8A C8 0D 6D 4C 8D 54 B9 7C D8 C7 92 1C… +0,,17876,1:07.760.444,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,17877,1:07.774.449,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17881,1:07.775.446,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,17882,1:07.775.449,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 31 D3 79 8A C8 0D 6D 4C 8D 54 B9 7C D8 C7 92 1C… +0,,17886,1:07.776.446,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,17887,1:07.791.451,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 C4 D8 48 BE 1E 6D 51 7B 23 F4 E3 96 18 A6 F5… +0,,17891,1:07.792.448,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,17892,1:07.806.454,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17896,1:07.807.450,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,17897,1:07.807.454,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 C4 D8 48 BE 1E 6D 51 7B 23 F4 E3 96 18 A6 F5… +0,,17901,1:07.808.451,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,17902,1:07.823.456,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EC 1E 73 E6 DF 74 79 15 89 C8 C3 D5 85 B3 44 4E… +0,,17906,1:07.824.453,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,17907,1:07.838.458,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17911,1:07.839.455,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,17912,1:07.839.458,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EC 1E 73 E6 DF 74 79 15 89 C8 C3 D5 85 B3 44 4E… +0,,17916,1:07.840.455,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,17917,1:07.855.460,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1A FC 3B 71 24 4E 77 2D 9E A3 E3 D4 2F FB D0 E3… +0,,17921,1:07.856.457,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,17922,1:07.870.462,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17926,1:07.871.459,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,17927,1:07.871.463,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1A FC 3B 71 24 4E 77 2D 9E A3 E3 D4 2F FB D0 E3… +0,,17931,1:07.872.459,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,17932,1:07.887.465,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C B1 3F 6F E5 4E 26 DB 93 B8 75 1D F4 99 CA B4… +0,,17936,1:07.888.462,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,17937,1:07.902.467,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17941,1:07.903.464,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,17942,1:07.903.467,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C B1 3F 6F E5 4E 26 DB 93 B8 75 1D F4 99 CA B4… +0,,17946,1:07.904.464,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,17947,1:07.919.469,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B F8 23 4A 15 77 C0 2C 5B EF 4B A9 15 B3 E6 77… +0,,17951,1:07.920.466,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,17952,1:07.934.471,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17956,1:07.935.468,16.005.041 ms,,,,,[17 SOF],[Frames: 289 - 305] +0,,17957,1:07.951.474,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 0C 3A A1 27 6B 96 4F 48 AD 5C C6 AD 64 DD 5E… +0,,17961,1:07.952.471,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,17962,1:07.966.476,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17966,1:07.967.473,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,17967,1:07.967.476,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 0C 3A A1 27 6B 96 4F 48 AD 5C C6 AD 64 DD 5E… +0,,17971,1:07.968.473,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,17972,1:07.983.478,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A7 78 B1 68 00 4E AF D5 6D 21 22 5D BE 95 11 51… +0,,17976,1:07.984.475,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,17977,1:07.998.480,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17981,1:07.999.477,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,17982,1:07.999.480,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A7 78 B1 68 00 4E AF D5 6D 21 22 5D BE 95 11 51… +0,,17986,1:08.000.477,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,17987,1:08.015.483,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 22 0F 74 29 F9 DF 04 99 D4 B1 FC DF C2 7E EF 47… +0,,17991,1:08.016.479,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,17992,1:08.030.485,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,17996,1:08.031.482,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,17997,1:08.031.485,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 22 0F 74 29 F9 DF 04 99 D4 B1 FC DF C2 7E EF 47… +0,,18001,1:08.032.482,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,18002,1:08.047.487,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B6 0D B8 3B 7A B0 30 8C D9 24 AC 21 0A 3C 06 44… +0,,18006,1:08.048.484,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,18007,1:08.062.489,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18011,1:08.063.486,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,18012,1:08.063.489,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B6 0D B8 3B 7A B0 30 8C D9 24 AC 21 0A 3C 06 44… +0,,18016,1:08.064.486,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,18017,1:08.079.491,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 88 66 C8 A9 EB 09 54 1C 4D 27 69 44 90 2F 0D 23… +0,,18021,1:08.080.488,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,18022,1:08.094.494,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18026,1:08.095.490,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,18027,1:08.095.494,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 88 66 C8 A9 EB 09 54 1C 4D 27 69 44 90 2F 0D 23… +0,,18031,1:08.096.491,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,18032,1:08.111.496,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D D7 31 5D C3 99 84 66 A4 F1 A8 E7 17 2D 48 A7… +0,,18036,1:08.112.493,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,18037,1:08.126.498,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18041,1:08.127.495,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,18042,1:08.127.498,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D D7 31 5D C3 99 84 66 A4 F1 A8 E7 17 2D 48 A7… +0,,18046,1:08.128.495,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,18047,1:08.143.500,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F0 3E 0E E4 C8 D6 4E BF 31 37 E6 4C 00 8F 8C 44… +0,,18051,1:08.144.497,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,18052,1:08.158.502,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18056,1:08.159.499,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,18057,1:08.159.503,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F0 3E 0E E4 C8 D6 4E BF 31 37 E6 4C 00 8F 8C 44… +0,,18061,1:08.160.499,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,18062,1:08.175.505,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F7 72 86 17 F1 47 C9 C0 9F 64 35 99 23 3C 82 0F… +0,,18066,1:08.176.502,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,18067,1:08.190.507,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18071,1:08.191.504,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,18072,1:08.191.507,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F7 72 86 17 F1 47 C9 C0 9F 64 35 99 23 3C 82 0F… +0,,18076,1:08.192.504,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,18077,1:08.207.509,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F0 61 02 18 18 28 9D EF 9F E3 77 89 F2 2F 54 A9… +0,,18081,1:08.208.506,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,18082,1:08.222.511,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18086,1:08.223.508,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,18087,1:08.223.511,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F0 61 02 18 18 28 9D EF 9F E3 77 89 F2 2F 54 A9… +0,,18091,1:08.224.508,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,18092,1:08.239.514,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CB 4C 67 95 7F 74 F5 F8 5E 54 47 00 2B BB 3E 60… +0,,18096,1:08.240.511,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,18097,1:08.254.516,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18101,1:08.255.513,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,18102,1:08.255.516,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CB 4C 67 95 7F 74 F5 F8 5E 54 47 00 2B BB 3E 60… +0,,18106,1:08.256.513,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,18107,1:08.271.518,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 5B C0 7A E8 9A 4D 91 5A 2A AD CD 48 37 62 A6… +0,,18111,1:08.272.515,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,18112,1:08.286.520,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18116,1:08.287.517,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,18117,1:08.287.520,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 5B C0 7A E8 9A 4D 91 5A 2A AD CD 48 37 62 A6… +0,,18121,1:08.288.517,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,18122,1:08.303.523,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 10 0B 80 06 FA BC 06 59 AF 89 36 EE 59 7A 6D C0… +0,,18126,1:08.304.519,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,18127,1:08.318.525,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18131,1:08.319.521,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,18132,1:08.319.525,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 10 0B 80 06 FA BC 06 59 AF 89 36 EE 59 7A 6D C0… +0,,18136,1:08.320.522,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,18137,1:08.335.527,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AE E1 38 0E 2A 74 D7 FB 1F 72 94 84 C8 07 2C AB… +0,,18141,1:08.336.524,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,18142,1:08.350.529,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18146,1:08.351.526,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,18147,1:08.351.529,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AE E1 38 0E 2A 74 D7 FB 1F 72 94 84 C8 07 2C AB… +0,,18151,1:08.352.526,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,18152,1:08.367.531,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D 58 53 C0 65 E1 48 CF 67 F1 58 48 30 19 08 02… +0,,18156,1:08.368.528,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,18157,1:08.382.533,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18161,1:08.383.530,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,18162,1:08.383.534,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D 58 53 C0 65 E1 48 CF 67 F1 58 48 30 19 08 02… +0,,18166,1:08.384.531,15.004.916 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,18167,1:08.399.536,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 B1 B2 66 62 FA CD 71 BE E8 C4 57 D6 7D 22 E8… +0,,18171,1:08.400.533,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,18172,1:08.414.538,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18176,1:08.415.535,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,18177,1:08.415.538,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 B1 B2 66 62 FA CD 71 BE E8 C4 57 D6 7D 22 E8… +0,,18181,1:08.416.535,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,18182,1:08.431.540,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B1 D2 B0 A2 86 FC F3 94 E2 C2 9A 8B E0 16 B4 31… +0,,18186,1:08.432.537,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,18187,1:08.446.542,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18191,1:08.447.539,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,18192,1:08.447.543,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B1 D2 B0 A2 86 FC F3 94 E2 C2 9A 8B E0 16 B4 31… +0,,18196,1:08.448.539,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,18197,1:08.463.545,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 37 0B 9B 51 FB 95 A7 FB 0F 19 51 2F DB BA AD 02… +0,,18201,1:08.464.542,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,18202,1:08.478.547,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18206,1:08.479.544,2.833 us,,,,,[1 SOF],[Frame: 833] +0,,18207,1:08.479.547,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 37 0B 9B 51 FB 95 A7 FB 0F 19 51 2F DB BA AD 02… +0,,18211,1:08.480.544,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,18212,1:08.495.549,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 AE 5B 45 E0 78 8D 6D 0C 30 C6 88 71 D9 E3 65… +0,,18216,1:08.496.546,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,18217,1:08.510.551,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18221,1:08.511.548,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,18222,1:08.511.551,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 AE 5B 45 E0 78 8D 6D 0C 30 C6 88 71 D9 E3 65… +0,,18226,1:08.512.548,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,18227,1:08.527.554,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 A5 BB FD 97 80 4E F8 4B D5 B9 72 3F E7 02 A4… +0,,18231,1:08.528.551,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,18232,1:08.542.556,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18236,1:08.543.553,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,18237,1:08.543.556,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 A5 BB FD 97 80 4E F8 4B D5 B9 72 3F E7 02 A4… +0,,18241,1:08.544.553,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,18242,1:08.559.558,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA D4 0F 83 00 D7 AB C9 71 6C DA BB 7E 5D 0B 3A… +0,,18246,1:08.560.555,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,18247,1:08.574.560,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18251,1:08.575.557,16.005.041 ms,,,,,[17 SOF],[Frames: 929 - 945] +0,,18252,1:08.591.563,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA D4 0F 83 00 D7 AB C9 71 6C DA BB 7E 5D 0B 3A… +0,,18256,1:08.592.559,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,18257,1:08.606.565,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18261,1:08.607.561,16.005.041 ms,,,,,[17 SOF],[Frames: 961 - 977] +0,,18262,1:08.623.567,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9A 33 11 6B D5 3B D9 EF ED DB 1F 25 56 30 92 0E… +0,,18266,1:08.624.564,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,18267,1:08.638.569,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18271,1:08.639.566,2.812 us,,,,,[1 SOF],[Frame: 993] +0,,18272,1:08.639.569,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9A 33 11 6B D5 3B D9 EF ED DB 1F 25 56 30 92 0E… +0,,18276,1:08.640.566,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,18277,1:08.655.571,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EE 12 5E A3 AF B6 80 8B 15 97 87 43 AF DF 86 09… +0,,18281,1:08.656.568,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,18282,1:08.670.573,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18286,1:08.671.570,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,18287,1:08.671.574,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EE 12 5E A3 AF B6 80 8B 15 97 87 43 AF DF 86 09… +0,,18291,1:08.672.570,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,18292,1:08.687.576,50.937 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC 62 0F 75 FB CF BB E0 2F 90 BE DE 5F DE E3 EC… +0,,18296,1:08.688.573,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,18297,1:08.702.578,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18301,1:08.703.575,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,18302,1:08.703.578,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC 62 0F 75 FB CF BB E0 2F 90 BE DE 5F DE E3 EC… +0,,18306,1:08.704.575,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,18307,1:08.719.580,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8C 8F C2 0F 91 9A 21 07 6A 65 B0 5E E8 10 19 D7… +0,,18311,1:08.720.577,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,18312,1:08.734.582,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18316,1:08.735.579,2.833 us,,,,,[1 SOF],[Frame: 1089] +0,,18317,1:08.735.582,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8C 8F C2 0F 91 9A 21 07 6A 65 B0 5E E8 10 19 D7… +0,,18321,1:08.736.579,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,18322,1:08.751.585,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A8 2D 07 2B 76 C1 DF 74 84 B4 A7 00 98 2E 90 8C… +0,,18326,1:08.752.582,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,18327,1:08.766.587,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18331,1:08.767.584,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,18332,1:08.767.587,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A8 2D 07 2B 76 C1 DF 74 84 B4 A7 00 98 2E 90 8C… +0,,18336,1:08.768.584,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,18337,1:08.783.589,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 66 6B A6 C6 E4 E1 64 D3 3F 38 86 ED D0 21 13 5C… +0,,18341,1:08.784.586,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,18342,1:08.798.591,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18346,1:08.799.588,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,18347,1:08.799.591,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 66 6B A6 C6 E4 E1 64 D3 3F 38 86 ED D0 21 13 5C… +0,,18351,1:08.800.588,15.004.916 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,18352,1:08.815.594,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 BB 43 CE 14 FD FC 7F A1 10 1F 77 71 71 AC 3D… +0,,18356,1:08.816.590,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,18357,1:08.830.596,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18361,1:08.831.593,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,18362,1:08.831.596,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 BB 43 CE 14 FD FC 7F A1 10 1F 77 71 71 AC 3D… +0,,18366,1:08.832.593,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,18367,1:08.847.598,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B 92 EB EE 8F 64 E3 42 B8 12 8A C8 90 B9 36 28… +0,,18371,1:08.848.595,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,18372,1:08.862.600,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18376,1:08.863.597,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,18377,1:08.863.600,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B 92 EB EE 8F 64 E3 42 B8 12 8A C8 90 B9 36 28… +0,,18381,1:08.864.597,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,18382,1:08.879.602,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 53 4D 73 22 25 86 EB 68 3E B7 DF 76 03 5E 27 FD… +0,,18386,1:08.880.599,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,18387,1:08.894.605,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18391,1:08.895.601,2.833 us,,,,,[1 SOF],[Frame: 1249] +0,,18392,1:08.895.605,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 53 4D 73 22 25 86 EB 68 3E B7 DF 76 03 5E 27 FD… +0,,18396,1:08.896.602,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,18397,1:08.911.607,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CC 08 38 32 3F 86 D8 2E 25 82 9D A6 A3 67 40 A1… +0,,18401,1:08.912.604,14.004.750 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,18402,1:08.926.609,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18406,1:08.927.606,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,18407,1:08.927.609,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CC 08 38 32 3F 86 D8 2E 25 82 9D A6 A3 67 40 A1… +0,,18411,1:08.928.606,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,18412,1:08.943.611,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 65 FB 75 33 D9 68 93 64 18 D2 DB 9C 46 93 BB 9C… +0,,18416,1:08.944.608,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,18417,1:08.958.613,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18421,1:08.959.610,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,18422,1:08.959.614,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 65 FB 75 33 D9 68 93 64 18 D2 DB 9C 46 93 BB 9C… +0,,18426,1:08.960.610,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,18427,1:08.975.616,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 35 F1 4E 7D 3F E0 42 99 57 FF 42 14 8D 29 3A 74… +0,,18431,1:08.976.613,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,18432,1:08.990.618,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18436,1:08.991.615,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,18437,1:08.991.618,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 35 F1 4E 7D 3F E0 42 99 57 FF 42 14 8D 29 3A 74… +0,,18441,1:08.992.615,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,18442,1:09.007.620,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C2 D4 27 C6 CE 85 DC 90 1F D7 71 AC FF FB 31 67… +0,,18446,1:09.008.617,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,18447,1:09.022.622,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18451,1:09.023.619,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,18452,1:09.023.622,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C2 D4 27 C6 CE 85 DC 90 1F D7 71 AC FF FB 31 67… +0,,18456,1:09.024.619,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,18457,1:09.039.625,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FE 34 08 49 7B E8 8D 30 31 A6 4D 83 29 52 63 C8… +0,,18461,1:09.040.622,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,18462,1:09.054.627,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18466,1:09.055.624,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,18467,1:09.055.627,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FE 34 08 49 7B E8 8D 30 31 A6 4D 83 29 52 63 C8… +0,,18471,1:09.056.624,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,18472,1:09.071.629,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC 9B 3F 15 93 AC D0 05 84 92 64 D3 B7 C9 70 28… +0,,18476,1:09.072.626,14.004.750 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,18477,1:09.086.631,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18481,1:09.087.628,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,18482,1:09.087.631,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC 9B 3F 15 93 AC D0 05 84 92 64 D3 B7 C9 70 28… +0,,18486,1:09.088.628,15.004.916 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,18487,1:09.103.634,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 21 0B 29 4C 5B 9C 02 A2 8F 5B 5E 42 19 8D 09 C8… +0,,18491,1:09.104.630,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,18492,1:09.118.636,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18496,1:09.119.633,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,18497,1:09.119.636,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 21 0B 29 4C 5B 9C 02 A2 8F 5B 5E 42 19 8D 09 C8… +0,,18501,1:09.120.633,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,18502,1:09.135.638,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 C4 56 C3 81 59 15 A6 5F B2 ED A1 55 40 6F 16… +0,,18506,1:09.136.635,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,18507,1:09.150.640,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18511,1:09.151.637,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,18512,1:09.151.640,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 C4 56 C3 81 59 15 A6 5F B2 ED A1 55 40 6F 16… +0,,18516,1:09.152.637,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,18517,1:09.167.642,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 99 CC F3 EA 8B 83 34 3A 49 A9 9A 3F 5C B3 44… +0,,18521,1:09.168.639,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,18522,1:09.182.645,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18526,1:09.183.641,16.005.125 ms,,,,,[17 SOF],[Frames: 1537 - 1553] +0,,18527,1:09.199.647,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7C ED 54 00 F2 EF BA C9 73 8F D7 B7 2F 33 83 00… +0,,18531,1:09.200.644,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,18532,1:09.214.649,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18536,1:09.215.646,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,18537,1:09.215.649,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7C ED 54 00 F2 EF BA C9 73 8F D7 B7 2F 33 83 00… +0,,18541,1:09.216.646,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,18542,1:09.231.651,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 99 4D 00 99 00 C7 FD 83 9A D0 46 F2 33 77 A6… +0,,18546,1:09.232.648,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,18547,1:09.246.653,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18551,1:09.247.650,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,18552,1:09.247.654,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 99 4D 00 99 00 C7 FD 83 9A D0 46 F2 33 77 A6… +0,,18556,1:09.248.650,15.004.916 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,18557,1:09.263.656,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 45 99 05 8B 27 86 9E 4B 72 BF F7 0C ED 34 5D… +0,,18561,1:09.264.653,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,18562,1:09.278.658,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18566,1:09.279.655,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,18567,1:09.279.658,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 45 99 05 8B 27 86 9E 4B 72 BF F7 0C ED 34 5D… +0,,18571,1:09.280.655,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,18572,1:09.295.660,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 99 37 70 FA 41 7D C2 AB CB 50 66 12 00 2B 25 92… +0,,18576,1:09.296.657,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,18577,1:09.310.662,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18581,1:09.311.659,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,18582,1:09.311.662,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 99 37 70 FA 41 7D C2 AB CB 50 66 12 00 2B 25 92… +0,,18586,1:09.312.659,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,18587,1:09.327.665,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B 25 C9 B1 D5 DE 8B 77 44 0D 67 1F F2 A2 D1 4A… +0,,18591,1:09.328.662,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,18592,1:09.342.667,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18596,1:09.343.664,2.833 us,,,,,[1 SOF],[Frame: 1697] +0,,18597,1:09.343.667,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B 25 C9 B1 D5 DE 8B 77 44 0D 67 1F F2 A2 D1 4A… +0,,18601,1:09.344.664,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,18602,1:09.359.669,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D9 55 F0 13 AF 30 41 98 95 F8 9B 3F 9D DE 24 82… +0,,18606,1:09.360.666,14.004.750 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,18607,1:09.374.671,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18611,1:09.375.668,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,18612,1:09.375.671,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D9 55 F0 13 AF 30 41 98 95 F8 9B 3F 9D DE 24 82… +0,,18616,1:09.376.668,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,18617,1:09.391.674,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 56 39 BB 99 B8 DC 0C 7F BD 25 C6 24 90 EC 06 EB… +0,,18621,1:09.392.670,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,18622,1:09.406.676,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18626,1:09.407.673,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,18627,1:09.407.676,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 56 39 BB 99 B8 DC 0C 7F BD 25 C6 24 90 EC 06 EB… +0,,18631,1:09.408.673,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,18632,1:09.423.678,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 0C 2C C4 C4 0E C7 C1 D0 07 1C 06 27 47 B3 FD… +0,,18636,1:09.424.675,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,18637,1:09.438.680,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18641,1:09.439.677,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,18642,1:09.439.680,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 0C 2C C4 C4 0E C7 C1 D0 07 1C 06 27 47 B3 FD… +0,,18646,1:09.440.677,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,18647,1:09.455.682,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 64 14 7D 9D 6F AB A5 6C 57 60 79 B1 3E 9D EF C5… +0,,18651,1:09.456.679,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,18652,1:09.470.685,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18656,1:09.471.681,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,18657,1:09.471.685,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 64 14 7D 9D 6F AB A5 6C 57 60 79 B1 3E 9D EF C5… +0,,18661,1:09.472.682,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,18662,1:09.487.687,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 7B B4 45 E4 62 08 8F 91 D3 7E 8B 29 81 81 4D… +0,,18666,1:09.488.684,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,18667,1:09.502.689,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18671,1:09.503.686,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,18672,1:09.503.689,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 7B B4 45 E4 62 08 8F 91 D3 7E 8B 29 81 81 4D… +0,,18676,1:09.504.686,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,18677,1:09.519.691,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 FD 5A FB 23 33 AF 8E D4 D1 31 BF D4 66 54 BA… +0,,18681,1:09.520.688,14.004.750 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,18682,1:09.534.693,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18686,1:09.535.690,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,18687,1:09.535.694,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 FD 5A FB 23 33 AF 8E D4 D1 31 BF D4 66 54 BA… +0,,18691,1:09.536.690,15.004.916 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,18692,1:09.551.696,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D2 B3 08 77 1C 13 C6 07 BB A7 EF 99 5B 5C A6 82… +0,,18696,1:09.552.693,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,18697,1:09.566.698,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18701,1:09.567.695,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,18702,1:09.567.698,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D2 B3 08 77 1C 13 C6 07 BB A7 EF 99 5B 5C A6 82… +0,,18706,1:09.568.695,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,18707,1:09.583.700,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 20 B9 B9 39 BC 48 F1 CF 16 A6 95 27 DA F1 C5… +0,,18711,1:09.584.697,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,18712,1:09.598.702,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18716,1:09.599.699,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,18717,1:09.599.702,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 20 B9 B9 39 BC 48 F1 CF 16 A6 95 27 DA F1 C5… +0,,18721,1:09.600.699,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,18722,1:09.615.705,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C 79 56 6D AD C1 3B 25 61 98 6A 9A 11 A8 5D BB… +0,,18726,1:09.616.702,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,18727,1:09.630.707,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18731,1:09.631.704,2.916 us,,,,,[1 SOF],[Frame: 1985] +0,,18732,1:09.631.707,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C 79 56 6D AD C1 3B 25 61 98 6A 9A 11 A8 5D BB… +0,,18736,1:09.632.704,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,18737,1:09.647.709,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8E B9 02 08 A7 61 67 FA 7E 62 B0 81 76 B4 7D 74… +0,,18741,1:09.648.706,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,18742,1:09.662.711,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18746,1:09.663.708,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,18747,1:09.663.711,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8E B9 02 08 A7 61 67 FA 7E 62 B0 81 76 B4 7D 74… +0,,18751,1:09.664.708,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,18752,1:09.679.714,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 76 CF 8B 96 8E 79 07 89 EC 4A 18 59 45 11 59… +0,,18756,1:09.680.710,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,18757,1:09.694.716,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18761,1:09.695.713,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,18762,1:09.695.716,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 76 CF 8B 96 8E 79 07 89 EC 4A 18 59 45 11 59… +0,,18766,1:09.696.713,15.004.916 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,18767,1:09.711.718,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 6B 0F BC 19 ED CB 29 29 D0 37 2B 3D 90 C9 E0… +0,,18771,1:09.712.715,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,18772,1:09.726.720,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18776,1:09.727.717,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,18777,1:09.727.720,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 6B 0F BC 19 ED CB 29 29 D0 37 2B 3D 90 C9 E0… +0,,18781,1:09.728.717,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,18782,1:09.743.722,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3F CB D7 4A E9 15 A2 30 02 BB A2 BD F2 A6 C4 4F… +0,,18786,1:09.744.719,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,18787,1:09.758.724,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18791,1:09.759.721,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,18792,1:09.759.725,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3F CB D7 4A E9 15 A2 30 02 BB A2 BD F2 A6 C4 4F… +0,,18796,1:09.760.722,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,18797,1:09.775.727,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 2C 0D 49 8E 0D DE 77 B0 32 9B 49 0F 10 E2 96… +0,,18801,1:09.776.724,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,18802,1:09.790.729,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18806,1:09.791.726,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,18807,1:09.791.729,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 2C 0D 49 8E 0D DE 77 B0 32 9B 49 0F 10 E2 96… +0,,18811,1:09.792.726,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,18812,1:09.807.731,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4F 6E 71 5B E1 87 D8 60 73 46 EC 05 55 49 AC AD… +0,,18816,1:09.808.728,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,18817,1:09.822.733,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18821,1:09.823.730,16.005.041 ms,,,,,[17 SOF],[Frames: 129 - 145] +0,,18822,1:09.839.736,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4F 6E 71 5B E1 87 D8 60 73 46 EC 05 55 49 AC AD… +0,,18826,1:09.840.733,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,18827,1:09.854.738,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18831,1:09.855.735,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,18832,1:09.855.738,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4F 6E 71 5B E1 87 D8 60 73 46 EC 05 55 49 AC AD… +0,,18836,1:09.856.735,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,18837,1:09.871.740,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF 7D 6E 49 70 CB 2D 33 08 D2 8C 28 67 24 C4 E4… +0,,18841,1:09.872.737,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,18842,1:09.886.742,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18846,1:09.887.739,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,18847,1:09.887.742,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF 7D 6E 49 70 CB 2D 33 08 D2 8C 28 67 24 C4 E4… +0,,18851,1:09.888.739,15.004.895 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,18852,1:09.903.745,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D 77 BD 62 FC 3B 04 DC 1B 6D 7D 8F F4 BE 3C 05… +0,,18856,1:09.904.742,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,18857,1:09.918.747,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18861,1:09.919.744,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,18862,1:09.919.747,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D 77 BD 62 FC 3B 04 DC 1B 6D 7D 8F F4 BE 3C 05… +0,,18866,1:09.920.744,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,18867,1:09.935.749,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 89 D1 3B E7 8B 10 15 A1 EA 2A 55 E8 04 F4 5A… +0,,18871,1:09.936.746,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,18872,1:09.950.751,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18876,1:09.951.748,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,18877,1:09.951.751,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 89 D1 3B E7 8B 10 15 A1 EA 2A 55 E8 04 F4 5A… +0,,18881,1:09.952.748,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,18882,1:09.967.754,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 84 50 7C E2 45 56 44 84 51 E8 C0 06 D8 67 E3… +0,,18886,1:09.968.750,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,18887,1:09.982.756,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18891,1:09.983.752,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,18892,1:09.983.756,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 84 50 7C E2 45 56 44 84 51 E8 C0 06 D8 67 E3… +0,,18896,1:09.984.753,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,18897,1:09.999.758,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 02 A0 EE 57 73 0F EF C1 9B 4E 3A F1 FA 65 35 F3… +0,,18901,1:10.000.755,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,18902,1:10.014.760,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18906,1:10.015.757,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,18907,1:10.015.760,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 02 A0 EE 57 73 0F EF C1 9B 4E 3A F1 FA 65 35 F3… +0,,18911,1:10.016.757,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,18912,1:10.031.762,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 26 25 0C 16 72 D6 D3 5E 3A 17 17 A6 F0 32 68 77… +0,,18916,1:10.032.759,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,18917,1:10.046.764,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18921,1:10.047.761,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,18922,1:10.047.765,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 26 25 0C 16 72 D6 D3 5E 3A 17 17 A6 F0 32 68 77… +0,,18926,1:10.048.762,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,18927,1:10.063.767,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 73 9E D7 B7 DD 5C 6D F7 87 94 52 1D D8 19 CF 43… +0,,18931,1:10.064.764,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,18932,1:10.078.769,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18936,1:10.079.766,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,18937,1:10.079.769,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 73 9E D7 B7 DD 5C 6D F7 87 94 52 1D D8 19 CF 43… +0,,18941,1:10.080.766,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,18942,1:10.095.771,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 8D 8C E3 93 41 E4 4F 05 B1 F0 57 65 44 7C 5F… +0,,18946,1:10.096.768,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,18947,1:10.110.773,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18951,1:10.111.770,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,18952,1:10.111.774,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F 8D 8C E3 93 41 E4 4F 05 B1 F0 57 65 44 7C 5F… +0,,18956,1:10.112.770,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,18957,1:10.127.776,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F2 CA 6E DC CC 23 A7 08 E4 5E 1C F6 2B 02 15 C4… +0,,18961,1:10.128.773,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,18962,1:10.142.778,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18966,1:10.143.775,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,18967,1:10.143.778,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F2 CA 6E DC CC 23 A7 08 E4 5E 1C F6 2B 02 15 C4… +0,,18971,1:10.144.775,15.004.916 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,18972,1:10.159.780,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B9 0A 8D BD AF 36 EE 5D 6F 3C 75 E1 11 B6 78 63… +0,,18976,1:10.160.777,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,18977,1:10.174.782,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18981,1:10.175.779,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,18982,1:10.175.782,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B9 0A 8D BD AF 36 EE 5D 6F 3C 75 E1 11 B6 78 63… +0,,18986,1:10.176.779,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,18987,1:10.191.785,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2F AD D4 D8 D1 2F E5 82 E2 5C AC 2D A6 C2 88 48… +0,,18991,1:10.192.781,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,18992,1:10.206.787,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,18996,1:10.207.784,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,18997,1:10.207.787,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2F AD D4 D8 D1 2F E5 82 E2 5C AC 2D A6 C2 88 48… +0,,19001,1:10.208.784,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,19002,1:10.223.789,50.916 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 11 90 93 FD 55 DB EF 2E D0 C0 ED FD CB 52 7F… +0,,19006,1:10.224.786,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,19007,1:10.238.791,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19011,1:10.239.788,2.833 us,,,,,[1 SOF],[Frame: 545] +0,,19012,1:10.239.791,50.916 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 11 90 93 FD 55 DB EF 2E D0 C0 ED FD CB 52 7F… +0,,19016,1:10.240.788,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,19017,1:10.255.793,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D6 BC F5 5B 73 FA 49 CF 8E 76 55 C1 B1 0A CA 9E… +0,,19021,1:10.256.790,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,19022,1:10.270.796,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19026,1:10.271.792,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,19027,1:10.271.796,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D6 BC F5 5B 73 FA 49 CF 8E 76 55 C1 B1 0A CA 9E… +0,,19031,1:10.272.793,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,19032,1:10.287.798,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 03 48 A2 27 9A 84 B7 B5 5E C2 B2 A9 25 AA 49 5F… +0,,19036,1:10.288.795,14.004.750 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,19037,1:10.302.800,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19041,1:10.303.797,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,19042,1:10.303.800,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 03 48 A2 27 9A 84 B7 B5 5E C2 B2 A9 25 AA 49 5F… +0,,19046,1:10.304.797,15.004.916 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,19047,1:10.319.802,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 86 AF E9 C6 3A 38 14 F3 A3 8E 4C 8C FD F5 C9 62… +0,,19051,1:10.320.799,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,19052,1:10.334.804,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19056,1:10.335.801,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,19057,1:10.335.805,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 86 AF E9 C6 3A 38 14 F3 A3 8E 4C 8C FD F5 C9 62… +0,,19061,1:10.336.801,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,19062,1:10.351.807,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 32 7D 87 D9 E4 E3 95 FC 58 CF BF 3E 2C 4A 3F B7… +0,,19066,1:10.352.804,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,19067,1:10.366.809,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19071,1:10.367.806,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,19072,1:10.367.809,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 32 7D 87 D9 E4 E3 95 FC 58 CF BF 3E 2C 4A 3F B7… +0,,19076,1:10.368.806,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,19077,1:10.383.811,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 AE F7 54 C2 39 BD 5B 71 6C C7 AC 71 C8 36 C8… +0,,19081,1:10.384.808,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,19082,1:10.398.813,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19086,1:10.399.810,2.833 us,,,,,[1 SOF],[Frame: 705] +0,,19087,1:10.399.813,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 31 AE F7 54 C2 39 BD 5B 71 6C C7 AC 71 C8 36 C8… +0,,19091,1:10.400.810,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,19092,1:10.415.816,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3F 79 50 12 8E 43 7E 6B EC 7B E8 87 26 7C DC F5… +0,,19096,1:10.416.813,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,19097,1:10.430.818,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19101,1:10.431.815,16.005.041 ms,,,,,[17 SOF],[Frames: 737 - 753] +0,,19102,1:10.447.820,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 39 45 8F B8 17 67 A8 B3 77 87 1A CF E1 B5 FA 3F… +0,,19106,1:10.448.817,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,19107,1:10.462.822,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19111,1:10.463.819,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,19112,1:10.463.822,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 39 45 8F B8 17 67 A8 B3 77 87 1A CF E1 B5 FA 3F… +0,,19116,1:10.464.819,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,19117,1:10.479.825,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BB D3 F4 FB 57 C9 3A B4 B4 D0 50 9D C1 F6 58 48… +0,,19121,1:10.480.821,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,19122,1:10.494.827,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19126,1:10.495.824,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,19127,1:10.495.827,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BB D3 F4 FB 57 C9 3A B4 B4 D0 50 9D C1 F6 58 48… +0,,19131,1:10.496.824,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,19132,1:10.511.829,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 B9 E8 B6 6E 6D D1 7E 85 F2 C8 2A C5 DA 7A 0D… +0,,19136,1:10.512.826,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,19137,1:10.526.831,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19141,1:10.527.828,2.833 us,,,,,[1 SOF],[Frame: 833] +0,,19142,1:10.527.831,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 B9 E8 B6 6E 6D D1 7E 85 F2 C8 2A C5 DA 7A 0D… +0,,19146,1:10.528.828,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,19147,1:10.543.833,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CF 3B 8B 2A A0 A6 84 C8 AA F5 F1 BE 2B C3 F9 8C… +0,,19151,1:10.544.830,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,19152,1:10.558.836,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19156,1:10.559.832,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,19157,1:10.559.836,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CF 3B 8B 2A A0 A6 84 C8 AA F5 F1 BE 2B C3 F9 8C… +0,,19161,1:10.560.833,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,19162,1:10.575.838,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 0C CA AD 2A 36 7E 63 3D 82 1A CE E2 02 A5 D5… +0,,19166,1:10.576.835,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,19167,1:10.590.840,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19171,1:10.591.837,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,19172,1:10.591.840,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 0C CA AD 2A 36 7E 63 3D 82 1A CE E2 02 A5 D5… +0,,19176,1:10.592.837,15.004.916 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,19177,1:10.607.842,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 2C 49 95 8C E3 41 4A D4 FF 80 F4 F6 E6 27 99… +0,,19181,1:10.608.839,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,19182,1:10.622.844,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19186,1:10.623.841,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,19187,1:10.623.845,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA 2C 49 95 8C E3 41 4A D4 FF 80 F4 F6 E6 27 99… +0,,19191,1:10.624.841,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,19192,1:10.639.847,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 79 DE 60 8D E8 A7 41 25 A8 74 5F 42 A7 40 A1… +0,,19196,1:10.640.844,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,19197,1:10.654.849,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19201,1:10.655.846,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,19202,1:10.655.849,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 31 79 DE 60 8D E8 A7 41 25 A8 74 5F 42 A7 40 A1… +0,,19206,1:10.656.846,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,19207,1:10.671.851,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 99 96 78 6E C6 0C 1D BC 33 2B 17 9E 38 F0 B3 67… +0,,19211,1:10.672.848,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,19212,1:10.686.853,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19216,1:10.687.850,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,19217,1:10.687.853,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 99 96 78 6E C6 0C 1D BC 33 2B 17 9E 38 F0 B3 67… +0,,19221,1:10.688.850,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,19222,1:10.703.856,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 60 8D 79 6D 4D C9 FD 5E D1 31 68 E2 15 FC 07 9C… +0,,19226,1:10.704.853,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,19227,1:10.718.858,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19231,1:10.719.855,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,19232,1:10.719.858,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 60 8D 79 6D 4D C9 FD 5E D1 31 68 E2 15 FC 07 9C… +0,,19236,1:10.720.855,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,19237,1:10.735.860,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 60 6D ED E4 E1 10 28 C9 46 42 92 68 EB 94 36 83… +0,,19241,1:10.736.857,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,19242,1:10.750.862,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19246,1:10.751.859,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,19247,1:10.751.862,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 60 6D ED E4 E1 10 28 C9 46 42 92 68 EB 94 36 83… +0,,19251,1:10.752.859,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,19252,1:10.767.865,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD B8 DF 89 01 83 30 50 29 2D 34 87 F9 99 9E C4… +0,,19256,1:10.768.861,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,19257,1:10.782.867,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19261,1:10.783.864,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,19262,1:10.783.867,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD B8 DF 89 01 83 30 50 29 2D 34 87 F9 99 9E C4… +0,,19266,1:10.784.864,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,19267,1:10.799.869,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 52 69 71 6E CC 73 70 27 06 99 01 F6 76 60 25 35… +0,,19271,1:10.800.866,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,19272,1:10.814.871,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19276,1:10.815.868,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,19277,1:10.815.871,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 52 69 71 6E CC 73 70 27 06 99 01 F6 76 60 25 35… +0,,19281,1:10.816.868,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,19282,1:10.831.873,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BA 5E 05 FC 9D B4 3E D0 DA 5C C1 3D 23 42 CA 5F… +0,,19286,1:10.832.870,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,19287,1:10.846.876,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19291,1:10.847.872,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,19292,1:10.847.876,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BA 5E 05 FC 9D B4 3E D0 DA 5C C1 3D 23 42 CA 5F… +0,,19296,1:10.848.873,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,19297,1:10.863.878,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DA 9E 8A 53 D0 DC 4E 9B 64 DE DB 4B 82 B2 F4 46… +0,,19301,1:10.864.875,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,19302,1:10.878.880,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19306,1:10.879.877,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,19307,1:10.879.880,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DA 9E 8A 53 D0 DC 4E 9B 64 DE DB 4B 82 B2 F4 46… +0,,19311,1:10.880.877,15.004.916 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,19312,1:10.895.882,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 84 CD 2F AC 62 CA F6 E8 11 3C 7D 14 31 78 DE DC… +0,,19316,1:10.896.879,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,19317,1:10.910.884,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19321,1:10.911.881,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,19322,1:10.911.885,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 84 CD 2F AC 62 CA F6 E8 11 3C 7D 14 31 78 DE DC… +0,,19326,1:10.912.881,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,19327,1:10.927.887,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 08 8E 8F 67 9C FA C0 0B D7 65 E4 03 3E 1C B6 E0… +0,,19331,1:10.928.884,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,19332,1:10.942.889,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19336,1:10.943.886,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,19337,1:10.943.889,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 08 8E 8F 67 9C FA C0 0B D7 65 E4 03 3E 1C B6 E0… +0,,19341,1:10.944.886,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,19342,1:10.959.891,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A 2A DE 3F 8A 78 2D B6 3B 27 A9 2D DF 2A 01 53… +0,,19346,1:10.960.888,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,19347,1:10.974.893,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19351,1:10.975.890,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,19352,1:10.975.893,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A 2A DE 3F 8A 78 2D B6 3B 27 A9 2D DF 2A 01 53… +0,,19356,1:10.976.890,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,19357,1:10.991.896,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AC 48 C2 EE B3 A2 B4 8A 83 B3 85 3E 9D 7D C7 5A… +0,,19361,1:10.992.893,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,19362,1:11.006.898,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19366,1:11.007.895,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,19367,1:11.007.898,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AC 48 C2 EE B3 A2 B4 8A 83 B3 85 3E 9D 7D C7 5A… +0,,19371,1:11.008.895,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,19372,1:11.023.900,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7A 42 3E B4 A5 A6 A6 88 96 95 E5 B4 75 29 C4 84… +0,,19376,1:11.024.897,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,19377,1:11.038.902,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19381,1:11.039.899,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,19382,1:11.039.902,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7A 42 3E B4 A5 A6 A6 88 96 95 E5 B4 75 29 C4 84… +0,,19386,1:11.040.899,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,19387,1:11.055.905,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F2 E7 A6 25 8E 13 74 8C C3 FA 6F 18 02 D8 69 18… +0,,19391,1:11.056.901,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,19392,1:11.070.907,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19396,1:11.071.904,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,19397,1:11.071.907,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F2 E7 A6 25 8E 13 74 8C C3 FA 6F 18 02 D8 69 18… +0,,19401,1:11.072.904,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,19402,1:11.087.909,50.645 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 61 E2 61 AF 01 D3 36 D1 12 54 FE 24 BD 80 FD 6A… +0,,19406,1:11.088.906,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,19407,1:11.102.911,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19411,1:11.103.908,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,19412,1:11.103.911,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 61 E2 61 AF 01 D3 36 D1 12 54 FE 24 BD 80 FD 6A… +0,,19416,1:11.104.908,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,19417,1:11.119.913,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 8B 5A FF 44 67 7E 34 C8 9D 01 6A D3 F1 E8 51… +0,,19421,1:11.120.910,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,19422,1:11.134.916,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19426,1:11.135.912,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,19427,1:11.135.916,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 8B 5A FF 44 67 7E 34 C8 9D 01 6A D3 F1 E8 51… +0,,19431,1:11.136.913,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,19432,1:11.151.918,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 E8 D0 5D E8 4A D6 84 8E CE 87 30 AD 00 54 CC… +0,,19436,1:11.152.915,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,19437,1:11.166.920,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19441,1:11.167.917,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,19442,1:11.167.920,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 E8 D0 5D E8 4A D6 84 8E CE 87 30 AD 00 54 CC… +0,,19446,1:11.168.917,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,19447,1:11.183.922,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 BB F4 0C A1 9E 85 20 C0 71 66 4F 06 22 6C 62… +0,,19451,1:11.184.919,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,19452,1:11.198.924,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19456,1:11.199.921,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,19457,1:11.199.925,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 BB F4 0C A1 9E 85 20 C0 71 66 4F 06 22 6C 62… +0,,19461,1:11.200.921,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,19462,1:11.215.927,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E9 9E 04 A0 87 1B F3 08 A2 DB C4 2F 4E DF B3 25… +0,,19466,1:11.216.924,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,19467,1:11.230.929,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19471,1:11.231.926,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,19472,1:11.231.929,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E9 9E 04 A0 87 1B F3 08 A2 DB C4 2F 4E DF B3 25… +0,,19476,1:11.232.926,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,19477,1:11.247.931,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9E 14 B3 32 D9 CB 77 44 94 5D 86 AC 9A FC DA 0D… +0,,19481,1:11.248.928,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,19482,1:11.262.933,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19486,1:11.263.930,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,19487,1:11.263.933,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9E 14 B3 32 D9 CB 77 44 94 5D 86 AC 9A FC DA 0D… +0,,19491,1:11.264.930,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,19492,1:11.279.936,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 BE EC 97 B1 67 73 47 67 EF 9A A9 E4 F3 E6 74… +0,,19496,1:11.280.933,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,19497,1:11.294.938,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19501,1:11.295.935,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,19502,1:11.295.938,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 BE EC 97 B1 67 73 47 67 EF 9A A9 E4 F3 E6 74… +0,,19506,1:11.296.935,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,19507,1:11.311.940,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 4D E0 19 F6 7A 38 17 11 BF 94 DD 04 88 79 BE… +0,,19511,1:11.312.937,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,19512,1:11.326.942,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19516,1:11.327.939,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,19517,1:11.327.942,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 4D E0 19 F6 7A 38 17 11 BF 94 DD 04 88 79 BE… +0,,19521,1:11.328.939,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,19522,1:11.343.945,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C9 61 BB 82 E6 2F 87 63 F8 7C C6 27 10 26 7C 0B… +0,,19526,1:11.344.941,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,19527,1:11.358.947,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19531,1:11.359.943,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,19532,1:11.359.947,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C9 61 BB 82 E6 2F 87 63 F8 7C C6 27 10 26 7C 0B… +0,,19536,1:11.360.944,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,19537,1:11.375.949,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B DD 5B AC D6 71 2D BB E7 CB FB 31 C0 81 31 16… +0,,19541,1:11.376.946,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,19542,1:11.390.951,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19546,1:11.391.948,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,19547,1:11.391.951,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B DD 5B AC D6 71 2D BB E7 CB FB 31 C0 81 31 16… +0,,19551,1:11.392.948,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,19552,1:11.407.953,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A6 88 F9 0C 87 7D 79 82 0B 94 BA FD EF 2F 8C D1… +0,,19556,1:11.408.950,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,19557,1:11.422.955,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19561,1:11.423.952,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,19562,1:11.423.956,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A6 88 F9 0C 87 7D 79 82 0B 94 BA FD EF 2F 8C D1… +0,,19566,1:11.424.953,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,19567,1:11.439.958,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F1 0A B2 4F E0 02 61 0D B9 0E 5D EE 76 5A 12 3A… +0,,19571,1:11.440.955,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,19572,1:11.454.960,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19576,1:11.455.957,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,19577,1:11.455.960,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F1 0A B2 4F E0 02 61 0D B9 0E 5D EE 76 5A 12 3A… +0,,19581,1:11.456.957,15.004.916 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,19582,1:11.471.962,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 C8 43 34 79 28 F2 E6 E9 48 7D 98 18 56 6F 63… +0,,19586,1:11.472.959,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,19587,1:11.486.964,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19591,1:11.487.961,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,19592,1:11.487.965,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 C8 43 34 79 28 F2 E6 E9 48 7D 98 18 56 6F 63… +0,,19596,1:11.488.961,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,19597,1:11.503.967,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 96 FD B4 23 3B 70 24 10 58 47 66 0C 61 49 0D 26… +0,,19601,1:11.504.964,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,19602,1:11.518.969,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19606,1:11.519.966,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,19607,1:11.519.969,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 96 FD B4 23 3B 70 24 10 58 47 66 0C 61 49 0D 26… +0,,19611,1:11.520.966,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,19612,1:11.535.971,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 67 F5 56 FA 58 5A 3E 81 36 FE DF D3 1D 6F 84… +0,,19616,1:11.536.968,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,19617,1:11.550.973,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19621,1:11.551.970,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,19622,1:11.551.973,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 67 F5 56 FA 58 5A 3E 81 36 FE DF D3 1D 6F 84… +0,,19626,1:11.552.970,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,19627,1:11.567.976,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0B 61 4F C7 1C 9B 25 BA D5 DF EA AC 3C 9F 55 85… +0,,19631,1:11.568.972,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,19632,1:11.582.978,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19636,1:11.583.975,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,19637,1:11.583.978,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0B 61 4F C7 1C 9B 25 BA D5 DF EA AC 3C 9F 55 85… +0,,19641,1:11.584.975,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,19642,1:11.599.980,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1F 89 9A B3 36 EB 27 8F 1C 7A B0 2E 93 04 71 35… +0,,19646,1:11.600.977,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,19647,1:11.614.982,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19651,1:11.615.979,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,19652,1:11.615.982,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1F 89 9A B3 36 EB 27 8F 1C 7A B0 2E 93 04 71 35… +0,,19656,1:11.616.979,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,19657,1:11.631.984,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 84 5E A8 C1 0B AF 16 D9 29 EE 65 23 FE F9 C0 BD… +0,,19661,1:11.632.981,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,19662,1:11.646.987,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19666,1:11.647.983,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,19667,1:11.647.987,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 84 5E A8 C1 0B AF 16 D9 29 EE 65 23 FE F9 C0 BD… +0,,19671,1:11.648.984,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,19672,1:11.663.989,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 50 A6 65 9F 73 55 82 3C C1 1C E0 4D FB 30 51… +0,,19676,1:11.664.986,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,19677,1:11.678.991,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19681,1:11.679.988,16.005.125 ms,,,,,[17 SOF],[Frames: 1985 - 2001] +0,,19682,1:11.695.993,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6A 15 0D 46 17 6C 6C 7B FF 71 D7 46 D9 B6 B8 A2… +0,,19686,1:11.696.990,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,19687,1:11.710.996,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19691,1:11.711.992,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,19692,1:11.711.996,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6A 15 0D 46 17 6C 6C 7B FF 71 D7 46 D9 B6 B8 A2… +0,,19696,1:11.712.992,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,19697,1:11.727.998,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 AD 97 37 22 80 A1 7F 83 77 EA AF 16 DB 47 9C… +0,,19701,1:11.728.995,14.004.750 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,19702,1:11.743.000,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19706,1:11.743.997,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,19707,1:11.744.000,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 AD 97 37 22 80 A1 7F 83 77 EA AF 16 DB 47 9C… +0,,19711,1:11.744.997,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,19712,1:11.760.002,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BE 33 BD A2 B8 59 58 85 2C 41 F1 DE 6D 6C E5 FF… +0,,19716,1:11.760.999,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,19717,1:11.775.004,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19721,1:11.776.001,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,19722,1:11.776.004,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BE 33 BD A2 B8 59 58 85 2C 41 F1 DE 6D 6C E5 FF… +0,,19726,1:11.777.001,15.004.916 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,19727,1:11.792.007,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6A DD 2B 58 66 06 2C E6 9E 01 CE B9 BA 54 C7 F8… +0,,19731,1:11.793.004,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,19732,1:11.807.009,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19736,1:11.808.006,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,19737,1:11.808.009,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6A DD 2B 58 66 06 2C E6 9E 01 CE B9 BA 54 C7 F8… +0,,19741,1:11.809.006,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,19742,1:11.824.011,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC 7B 1C 59 BE 2B 70 C3 B4 F6 A9 4F E1 74 5B A1… +0,,19746,1:11.825.008,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,19747,1:11.839.013,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19751,1:11.840.010,2.833 us,,,,,[1 SOF],[Frame: 97] +0,,19752,1:11.840.013,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC 7B 1C 59 BE 2B 70 C3 B4 F6 A9 4F E1 74 5B A1… +0,,19756,1:11.841.010,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,19757,1:11.856.016,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A 0B F6 6E 3D CB 74 2F F0 EC 9A FD 84 CC 0B 9E… +0,,19761,1:11.857.012,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,19762,1:11.871.018,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19766,1:11.872.015,2.833 us,,,,,[1 SOF],[Frame: 129] +0,,19767,1:11.872.018,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A 0B F6 6E 3D CB 74 2F F0 EC 9A FD 84 CC 0B 9E… +0,,19771,1:11.873.015,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,19772,1:11.888.020,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8E 69 60 99 31 E9 DC FF 64 CF 85 48 13 27 C9 08… +0,,19776,1:11.889.017,14.004.750 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,19777,1:11.903.022,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19781,1:11.904.019,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,19782,1:11.904.022,50.645 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8E 69 60 99 31 E9 DC FF 64 CF 85 48 13 27 C9 08… +0,,19786,1:11.905.019,15.004.916 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,19787,1:11.920.024,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B 16 E7 4B F6 8D C7 B7 B3 14 C9 94 4C 9C 8A 00… +0,,19791,1:11.921.021,14.004.750 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,19792,1:11.935.027,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19796,1:11.936.023,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,19797,1:11.936.027,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B 16 E7 4B F6 8D C7 B7 B3 14 C9 94 4C 9C 8A 00… +0,,19801,1:11.937.024,15.004.916 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,19802,1:11.952.029,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 22 44 B2 54 44 A1 3B 99 0F DF 21 CC FE 56 E6… +0,,19806,1:11.953.026,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,19807,1:11.967.031,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19811,1:11.968.028,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,19812,1:11.968.031,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 22 44 B2 54 44 A1 3B 99 0F DF 21 CC FE 56 E6… +0,,19816,1:11.969.028,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,19817,1:11.984.033,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 74 D6 2E 62 9F E7 7F A6 B4 21 BE C2 6A E5 5E… +0,,19821,1:11.985.030,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,19822,1:11.999.035,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19826,1:12.000.032,2.833 us,,,,,[1 SOF],[Frame: 257] +0,,19827,1:12.000.036,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 74 D6 2E 62 9F E7 7F A6 B4 21 BE C2 6A E5 5E… +0,,19831,1:12.001.032,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,19832,1:12.016.038,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DA 67 8F 2D C4 AF A3 E1 5C C4 A4 3E 68 00 90 D1… +0,,19836,1:12.017.035,14.004.750 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,19837,1:12.031.040,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19841,1:12.032.037,2.833 us,,,,,[1 SOF],[Frame: 289] +0,,19842,1:12.032.040,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DA 67 8F 2D C4 AF A3 E1 5C C4 A4 3E 68 00 90 D1… +0,,19846,1:12.033.037,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,19847,1:12.048.042,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 A1 5C EE DE 5C 90 9B 4D 61 0E 2C 73 A5 91 2B… +0,,19851,1:12.049.039,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,19852,1:12.063.044,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19856,1:12.064.041,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,19857,1:12.064.044,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 A1 5C EE DE 5C 90 9B 4D 61 0E 2C 73 A5 91 2B… +0,,19861,1:12.065.041,15.004.916 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,19862,1:12.080.047,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CB 0D C3 51 E4 74 17 86 8B 47 71 7D 44 45 4B 9B… +0,,19866,1:12.081.044,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,19867,1:12.095.049,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19871,1:12.096.046,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,19872,1:12.096.049,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CB 0D C3 51 E4 74 17 86 8B 47 71 7D 44 45 4B 9B… +0,,19876,1:12.097.046,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,19877,1:12.112.051,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 29 F5 B4 0B E2 AB B9 DD CA AC F9 E1 F2 34 8B 61… +0,,19881,1:12.113.048,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,19882,1:12.127.053,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19886,1:12.128.050,2.812 us,,,,,[1 SOF],[Frame: 385] +0,,19887,1:12.128.053,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 29 F5 B4 0B E2 AB B9 DD CA AC F9 E1 F2 34 8B 61… +0,,19891,1:12.129.050,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,19892,1:12.144.056,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD B6 CA 57 21 C4 D3 BC 7C 63 E6 44 25 5D 75 65… +0,,19896,1:12.145.052,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,19897,1:12.159.058,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19901,1:12.160.055,2.833 us,,,,,[1 SOF],[Frame: 417] +0,,19902,1:12.160.058,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD B6 CA 57 21 C4 D3 BC 7C 63 E6 44 25 5D 75 65… +0,,19906,1:12.161.055,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,19907,1:12.176.060,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8F D0 37 0F 53 0E 76 05 F4 2C F7 F7 54 72 9A 85… +0,,19911,1:12.177.057,14.004.750 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,19912,1:12.191.062,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19916,1:12.192.059,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,19917,1:12.192.062,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8F D0 37 0F 53 0E 76 05 F4 2C F7 F7 54 72 9A 85… +0,,19921,1:12.193.059,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,19922,1:12.208.064,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 30 D1 F2 DB 5D 2E E6 1B 52 EC 80 6A 40 87 52… +0,,19926,1:12.209.061,14.004.750 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,19927,1:12.223.067,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19931,1:12.224.063,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,19932,1:12.224.067,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 30 D1 F2 DB 5D 2E E6 1B 52 EC 80 6A 40 87 52… +0,,19936,1:12.225.064,15.004.916 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,19937,1:12.240.069,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8F 99 57 D2 1C 65 7E 46 4D 22 89 4A F4 D7 14 24… +0,,19941,1:12.241.066,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,19942,1:12.255.071,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19946,1:12.256.068,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,19947,1:12.256.071,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8F 99 57 D2 1C 65 7E 46 4D 22 89 4A F4 D7 14 24… +0,,19951,1:12.257.068,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,19952,1:12.272.073,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC C9 B4 C2 8B ED A6 E1 74 22 F5 46 3D C0 0B FF… +0,,19956,1:12.273.070,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,19957,1:12.287.075,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19961,1:12.288.072,16.005.041 ms,,,,,[17 SOF],[Frames: 545 - 561] +0,,19962,1:12.304.078,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 9D 15 49 9C 27 DC 86 44 B9 89 2F 22 B9 CE 96… +0,,19966,1:12.305.075,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,19967,1:12.319.080,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19971,1:12.320.077,2.833 us,,,,,[1 SOF],[Frame: 577] +0,,19972,1:12.320.080,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 9D 15 49 9C 27 DC 86 44 B9 89 2F 22 B9 CE 96… +0,,19976,1:12.321.077,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,19977,1:12.336.082,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 8C 86 59 7D A0 27 D7 C8 A8 C5 8D D8 D1 A7 92… +0,,19981,1:12.337.079,14.004.750 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,19982,1:12.351.084,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,19986,1:12.352.081,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,19987,1:12.352.084,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 8C 86 59 7D A0 27 D7 C8 A8 C5 8D D8 D1 A7 92… +0,,19991,1:12.353.081,15.004.916 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,19992,1:12.368.087,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BB 02 9B 66 6E 92 E6 5B 9A 1E 75 5C 55 B6 1D 86… +0,,19996,1:12.369.084,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,19997,1:12.383.089,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20001,1:12.384.086,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,20002,1:12.384.089,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BB 02 9B 66 6E 92 E6 5B 9A 1E 75 5C 55 B6 1D 86… +0,,20006,1:12.385.086,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,20007,1:12.400.091,50.687 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 A8 56 51 DC 9E A2 8E 24 A2 69 ED DC 22 09 94… +0,,20011,1:12.401.088,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,20012,1:12.415.093,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20016,1:12.416.090,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,20017,1:12.416.093,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 A8 56 51 DC 9E A2 8E 24 A2 69 ED DC 22 09 94… +0,,20021,1:12.417.090,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,20022,1:12.432.096,50.312 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A8 D0 2C 6A F0 56 CE CB 04 B1 94 41 12 5D BB E7… +0,,20026,1:12.433.092,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,20027,1:12.447.098,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20031,1:12.448.095,2.833 us,,,,,[1 SOF],[Frame: 705] +0,,20032,1:12.448.098,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A8 D0 2C 6A F0 56 CE CB 04 B1 94 41 12 5D BB E7… +0,,20036,1:12.449.095,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,20037,1:12.464.100,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 14 A8 AE 80 AE 7C 46 AE 67 1D 89 A3 11 8D 4C 8F… +0,,20041,1:12.465.097,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,20042,1:12.479.102,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20046,1:12.480.099,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,20047,1:12.480.102,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 14 A8 AE 80 AE 7C 46 AE 67 1D 89 A3 11 8D 4C 8F… +0,,20051,1:12.481.099,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,20052,1:12.496.104,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A 96 CC 54 A9 38 F9 00 B3 DF 0A CC CE BE 39 51… +0,,20056,1:12.497.101,14.004.750 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,20057,1:12.511.107,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20061,1:12.512.103,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,20062,1:12.512.107,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2A 96 CC 54 A9 38 F9 00 B3 DF 0A CC CE BE 39 51… +0,,20066,1:12.513.104,15.004.916 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,20067,1:12.528.109,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CC 3C EC AA 7C 23 2E 93 8B A6 42 DA 84 4A 05 9F… +0,,20071,1:12.529.106,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,20072,1:12.543.111,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20076,1:12.544.108,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,20077,1:12.544.111,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CC 3C EC AA 7C 23 2E 93 8B A6 42 DA 84 4A 05 9F… +0,,20081,1:12.545.108,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,20082,1:12.560.113,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BD D0 50 FA 0E 95 FA CD 74 92 31 28 A5 64 87 2D… +0,,20086,1:12.561.110,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,20087,1:12.575.115,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20091,1:12.576.112,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,20092,1:12.576.116,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BD D0 50 FA 0E 95 FA CD 74 92 31 28 A5 64 87 2D… +0,,20096,1:12.577.112,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,20097,1:12.592.118,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 32 B5 A4 CA 9F FC F7 C8 81 8E AA AB C6 85 DC BA… +0,,20101,1:12.593.115,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,20102,1:12.607.120,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20106,1:12.608.117,2.833 us,,,,,[1 SOF],[Frame: 865] +0,,20107,1:12.608.120,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 32 B5 A4 CA 9F FC F7 C8 81 8E AA AB C6 85 DC BA… +0,,20111,1:12.609.117,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,20112,1:12.624.122,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D B6 80 1E D9 76 E0 65 70 14 95 FF 21 89 39 29… +0,,20116,1:12.625.119,14.004.750 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,20117,1:12.639.124,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20121,1:12.640.121,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,20122,1:12.640.124,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D B6 80 1E D9 76 E0 65 70 14 95 FF 21 89 39 29… +0,,20126,1:12.641.121,15.004.916 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,20127,1:12.656.127,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 72 2B 44 AF ED 17 62 A7 36 16 AD 65 69 C7 7E 79… +0,,20131,1:12.657.124,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,20132,1:12.671.129,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20136,1:12.672.126,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,20137,1:12.672.129,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 72 2B 44 AF ED 17 62 A7 36 16 AD 65 69 C7 7E 79… +0,,20141,1:12.673.126,15.004.916 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,20142,1:12.688.131,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C8 F4 AE 4F 47 D2 F6 97 1E C1 5D 16 99 F4 67 70… +0,,20146,1:12.689.128,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,20147,1:12.703.133,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20151,1:12.704.130,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,20152,1:12.704.133,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 F4 AE 4F 47 D2 F6 97 1E C1 5D 16 99 F4 67 70… +0,,20156,1:12.705.130,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,20157,1:12.720.136,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DB 39 CC 87 8F 6B EE EB C8 72 C1 C1 6C 16 3F 8F… +0,,20161,1:12.721.132,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,20162,1:12.735.138,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20166,1:12.736.134,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,20167,1:12.736.138,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DB 39 CC 87 8F 6B EE EB C8 72 C1 C1 6C 16 3F 8F… +0,,20171,1:12.737.135,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,20172,1:12.752.140,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 94 F4 6D 99 85 0D 86 C5 54 52 1B BD 52 81 C3 13… +0,,20176,1:12.753.137,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,20177,1:12.767.142,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20181,1:12.768.139,2.833 us,,,,,[1 SOF],[Frame: 1025] +0,,20182,1:12.768.142,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 94 F4 6D 99 85 0D 86 C5 54 52 1B BD 52 81 C3 13… +0,,20186,1:12.769.139,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,20187,1:12.784.144,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 47 8D C0 6D EB E2 D6 75 83 57 EC DE 26 D7 60… +0,,20191,1:12.785.141,14.004.750 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,20192,1:12.799.146,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20196,1:12.800.143,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,20197,1:12.800.147,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 47 8D C0 6D EB E2 D6 75 83 57 EC DE 26 D7 60… +0,,20201,1:12.801.144,15.004.916 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,20202,1:12.816.149,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 06 1A E6 CB 76 01 F4 DC 50 38 59 41 43 E2 A2 16… +0,,20206,1:12.817.146,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,20207,1:12.831.151,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20211,1:12.832.148,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,20212,1:12.832.151,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 06 1A E6 CB 76 01 F4 DC 50 38 59 41 43 E2 A2 16… +0,,20216,1:12.833.148,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,20217,1:12.848.153,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E 58 10 07 7F 52 63 19 50 67 A7 87 53 49 0D 11… +0,,20221,1:12.849.150,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,20222,1:12.863.155,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20226,1:12.864.152,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,20227,1:12.864.156,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E 58 10 07 7F 52 63 19 50 67 A7 87 53 49 0D 11… +0,,20231,1:12.865.152,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,20232,1:12.880.158,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BF 52 1C E2 C1 CC 5A 39 96 59 CF 5B F3 95 95 1E… +0,,20236,1:12.881.155,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,20237,1:12.895.160,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20241,1:12.896.157,2.916 us,,,,,[1 SOF],[Frame: 1153] +0,,20242,1:12.896.160,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BF 52 1C E2 C1 CC 5A 39 96 59 CF 5B F3 95 95 1E… +0,,20246,1:12.897.157,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,20247,1:12.912.162,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 5D 7B 48 56 3A D4 97 B5 C9 04 E8 6F 7F 6E 86… +0,,20251,1:12.913.159,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,20252,1:12.927.164,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20256,1:12.928.161,16.005.020 ms,,,,,[17 SOF],[Frames: 1185 - 1201] +0,,20257,1:12.944.167,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 67 E9 99 B4 1A 38 05 69 AD 64 3A 04 F4 71 E6 9B… +0,,20261,1:12.945.164,14.004.750 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,20262,1:12.959.169,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20266,1:12.960.166,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,20267,1:12.960.169,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 67 E9 99 B4 1A 38 05 69 AD 64 3A 04 F4 71 E6 9B… +0,,20271,1:12.961.166,15.004.916 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,20272,1:12.976.171,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 C6 C2 51 5D 49 76 7A 74 47 3B 7E 17 4E 13 61… +0,,20276,1:12.977.168,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,20277,1:12.991.173,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20281,1:12.992.170,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,20282,1:12.992.173,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 C6 C2 51 5D 49 76 7A 74 47 3B 7E 17 4E 13 61… +0,,20286,1:12.993.170,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,20287,1:13.008.176,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5F 53 C4 23 BD 4F 74 8E B9 0A 41 26 D6 DC 1E 24… +0,,20291,1:13.009.172,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,20292,1:13.023.178,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20296,1:13.024.174,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,20297,1:13.024.178,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5F 53 C4 23 BD 4F 74 8E B9 0A 41 26 D6 DC 1E 24… +0,,20301,1:13.025.175,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,20302,1:13.040.180,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D E3 77 31 4C 2D EF CD 96 2B 55 7E EB C3 93 4F… +0,,20306,1:13.041.177,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,20307,1:13.055.182,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20311,1:13.056.179,2.833 us,,,,,[1 SOF],[Frame: 1313] +0,,20312,1:13.056.182,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D E3 77 31 4C 2D EF CD 96 2B 55 7E EB C3 93 4F… +0,,20316,1:13.057.179,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,20317,1:13.072.184,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF 07 9C 44 8B 04 EA AC 7F 02 99 89 6E 9C 78 A7… +0,,20321,1:13.073.181,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,20322,1:13.087.186,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20326,1:13.088.183,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,20327,1:13.088.187,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF 07 9C 44 8B 04 EA AC 7F 02 99 89 6E 9C 78 A7… +0,,20331,1:13.089.183,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,20332,1:13.104.189,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4E BE 8A F7 08 C7 C5 C3 7C B3 20 AA 56 B1 8A 2E… +0,,20336,1:13.105.186,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,20337,1:13.119.191,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20341,1:13.120.188,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,20342,1:13.120.191,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4E BE 8A F7 08 C7 C5 C3 7C B3 20 AA 56 B1 8A 2E… +0,,20346,1:13.121.188,15.004.916 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,20347,1:13.136.193,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 16 8F 6C A7 3B D8 D1 C2 0C 5C 92 84 C0 66 5A 2B… +0,,20351,1:13.137.190,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,20352,1:13.151.195,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20356,1:13.152.192,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,20357,1:13.152.195,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 16 8F 6C A7 3B D8 D1 C2 0C 5C 92 84 C0 66 5A 2B… +0,,20361,1:13.153.192,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,20362,1:13.168.198,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 49 82 96 5D 1C 67 62 F7 A4 9A F5 47 B6 36 93… +0,,20366,1:13.169.195,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,20367,1:13.183.200,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20371,1:13.184.197,2.833 us,,,,,[1 SOF],[Frame: 1441] +0,,20372,1:13.184.200,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 49 82 96 5D 1C 67 62 F7 A4 9A F5 47 B6 36 93… +0,,20376,1:13.185.197,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,20377,1:13.200.202,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 31 29 EB 13 90 68 28 CB 35 F9 4A 08 DE 72 3E 29… +0,,20381,1:13.201.199,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,20382,1:13.215.204,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20386,1:13.216.201,2.833 us,,,,,[1 SOF],[Frame: 1473] +0,,20387,1:13.216.204,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 31 29 EB 13 90 68 28 CB 35 F9 4A 08 DE 72 3E 29… +0,,20391,1:13.217.201,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,20392,1:13.232.207,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FB 65 78 5F BE 1C CE 81 48 4B 71 B7 10 11 D7 27… +0,,20396,1:13.233.203,14.004.833 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,20397,1:13.247.209,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20401,1:13.248.206,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,20402,1:13.248.209,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FB 65 78 5F BE 1C CE 81 48 4B 71 B7 10 11 D7 27… +0,,20406,1:13.249.206,15.004.916 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,20407,1:13.264.211,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 83 02 D3 BE 02 9E 0E 81 08 4F 84 2D E9 6A 0A E6… +0,,20411,1:13.265.208,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,20412,1:13.279.213,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20416,1:13.280.210,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,20417,1:13.280.213,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 83 02 D3 BE 02 9E 0E 81 08 4F 84 2D E9 6A 0A E6… +0,,20421,1:13.281.210,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,20422,1:13.296.216,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B4 9C 3D 1B 3E 6C 99 17 39 E5 45 FF B7 F1 4E 30… +0,,20426,1:13.297.212,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,20427,1:13.311.218,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20431,1:13.312.214,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,20432,1:13.312.218,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B4 9C 3D 1B 3E 6C 99 17 39 E5 45 FF B7 F1 4E 30… +0,,20436,1:13.313.215,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,20437,1:13.328.220,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 ED 92 7D F1 7E 70 9B E0 8F 79 40 45 33 89 57… +0,,20441,1:13.329.217,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,20442,1:13.343.222,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20446,1:13.344.219,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,20447,1:13.344.222,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 ED 92 7D F1 7E 70 9B E0 8F 79 40 45 33 89 57… +0,,20451,1:13.345.219,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,20452,1:13.360.224,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 48 DB A4 5C 1F 9D 26 A3 C5 21 D5 8A 25 59 99 AB… +0,,20456,1:13.361.221,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,20457,1:13.375.226,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20461,1:13.376.223,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,20462,1:13.376.227,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 48 DB A4 5C 1F 9D 26 A3 C5 21 D5 8A 25 59 99 AB… +0,,20466,1:13.377.223,15.004.895 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,20467,1:13.392.229,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C1 23 1E 4C 27 4D EC E1 8C 3E D5 9E 29 29 97 DD… +0,,20471,1:13.393.226,14.004.750 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,20472,1:13.407.231,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20476,1:13.408.228,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,20477,1:13.408.231,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C1 23 1E 4C 27 4D EC E1 8C 3E D5 9E 29 29 97 DD… +0,,20481,1:13.409.228,15.004.916 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,20482,1:13.424.233,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1B 47 8C 4D 58 22 C5 E7 EB F9 54 71 B3 0A 5C 6B… +0,,20486,1:13.425.230,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,20487,1:13.439.235,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20491,1:13.440.232,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,20492,1:13.440.235,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1B 47 8C 4D 58 22 C5 E7 EB F9 54 71 B3 0A 5C 6B… +0,,20496,1:13.441.232,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,20497,1:13.456.238,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 39 AB FC 72 F4 51 93 59 D1 6E 46 B7 42 67 BB E7… +0,,20501,1:13.457.235,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,20502,1:13.471.240,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20506,1:13.472.237,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,20507,1:13.472.240,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 39 AB FC 72 F4 51 93 59 D1 6E 46 B7 42 67 BB E7… +0,,20511,1:13.473.237,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,20512,1:13.488.242,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3B 59 06 9F FF A3 0B 67 B3 1E C3 47 F1 26 37 94… +0,,20516,1:13.489.239,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,20517,1:13.503.244,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20521,1:13.504.241,2.916 us,,,,,[1 SOF],[Frame: 1761] +0,,20522,1:13.504.244,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3B 59 06 9F FF A3 0B 67 B3 1E C3 47 F1 26 37 94… +0,,20526,1:13.505.241,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,20527,1:13.520.247,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A0 31 7A 6C 5F 37 F1 E5 6D 67 98 02 74 46 52 FE… +0,,20531,1:13.521.243,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,20532,1:13.535.249,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20536,1:13.536.246,16.005.041 ms,,,,,[17 SOF],[Frames: 1793 - 1809] +0,,20537,1:13.552.251,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D3 46 E6 F2 A2 60 38 B1 7C A6 99 4F 81 2D B1 3B… +0,,20541,1:13.553.248,14.004.750 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,20542,1:13.567.253,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20546,1:13.568.250,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,20547,1:13.568.253,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D3 46 E6 F2 A2 60 38 B1 7C A6 99 4F 81 2D B1 3B… +0,,20551,1:13.569.250,15.004.916 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,20552,1:13.584.255,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B 07 FC 18 32 17 3C DF 42 39 8B DE 10 46 48 B4… +0,,20556,1:13.585.252,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,20557,1:13.599.258,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20561,1:13.600.254,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,20562,1:13.600.258,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B 07 FC 18 32 17 3C DF 42 39 8B DE 10 46 48 B4… +0,,20566,1:13.601.255,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,20567,1:13.616.260,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 97 8A 86 DC ED 40 03 DE A4 81 F9 77 E9 E2 1E 9B… +0,,20571,1:13.617.257,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,20572,1:13.631.262,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20576,1:13.632.259,16.005.041 ms,,,,,[17 SOF],[Frames: 1889 - 1905] +0,,20577,1:13.648.264,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0F 9D 62 3A 92 A7 E4 8D 2C 6B 45 E7 28 69 D9 D9… +0,,20581,1:13.649.261,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,20582,1:13.663.267,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20586,1:13.664.263,2.833 us,,,,,[1 SOF],[Frame: 1921] +0,,20587,1:13.664.267,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0F 9D 62 3A 92 A7 E4 8D 2C 6B 45 E7 28 69 D9 D9… +0,,20591,1:13.665.263,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,20592,1:13.680.269,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 04 4B 95 5F 50 05 F3 A2 83 C9 DF 32 17 63 FB 32… +0,,20596,1:13.681.266,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,20597,1:13.695.271,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20601,1:13.696.268,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,20602,1:13.696.271,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 04 4B 95 5F 50 05 F3 A2 83 C9 DF 32 17 63 FB 32… +0,,20606,1:13.697.268,15.004.916 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,20607,1:13.712.273,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0E 33 4E 28 7A 72 3F 79 29 E6 63 1C 34 F8 6F 57… +0,,20611,1:13.713.270,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,20612,1:13.727.275,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20616,1:13.728.272,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,20617,1:13.728.276,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0E 33 4E 28 7A 72 3F 79 29 E6 63 1C 34 F8 6F 57… +0,,20621,1:13.729.272,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,20622,1:13.744.278,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A 88 63 FF 53 9F 63 84 B2 26 91 71 79 6F 18 72… +0,,20626,1:13.745.275,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,20627,1:13.759.280,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20631,1:13.760.277,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,20632,1:13.760.280,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A 88 63 FF 53 9F 63 84 B2 26 91 71 79 6F 18 72… +0,,20636,1:13.761.277,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,20637,1:13.776.282,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BE 15 BF 71 4C 95 BC 45 26 D8 7C 80 0D FA 78 F8… +0,,20641,1:13.777.279,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,20642,1:13.791.284,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20646,1:13.792.281,2.833 us,,,,,[1 SOF],[Frame: 1] +0,,20647,1:13.792.284,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BE 15 BF 71 4C 95 BC 45 26 D8 7C 80 0D FA 78 F8… +0,,20651,1:13.793.281,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,20652,1:13.808.287,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C8 32 2F 65 B1 C0 97 A0 DA E4 B9 04 3B 47 47 22… +0,,20656,1:13.809.283,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,20657,1:13.823.289,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20661,1:13.824.286,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,20662,1:13.824.289,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 32 2F 65 B1 C0 97 A0 DA E4 B9 04 3B 47 47 22… +0,,20666,1:13.825.286,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,20667,1:13.840.291,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 88 16 89 95 61 AB 9D EA 12 A0 10 47 9A D1 6A 6D… +0,,20671,1:13.841.288,14.004.750 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,20672,1:13.855.293,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20676,1:13.856.290,2.812 us,,,,,[1 SOF],[Frame: 65] +0,,20677,1:13.856.293,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 88 16 89 95 61 AB 9D EA 12 A0 10 47 9A D1 6A 6D… +0,,20681,1:13.857.290,15.004.916 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,20682,1:13.872.295,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 60 33 AB 7F A0 97 8C D8 D9 E9 D7 61 56 B2 E9 4C… +0,,20686,1:13.873.292,14.004.770 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,20687,1:13.887.298,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20691,1:13.888.294,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,20692,1:13.888.298,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 60 33 AB 7F A0 97 8C D8 D9 E9 D7 61 56 B2 E9 4C… +0,,20696,1:13.889.295,15.004.895 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,20697,1:13.904.300,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D F8 B7 CC D0 E6 01 05 81 19 08 71 AE 75 57 77… +0,,20701,1:13.905.297,14.004.770 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,20702,1:13.919.302,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20706,1:13.920.299,2.833 us,,,,,[1 SOF],[Frame: 129] +0,,20707,1:13.920.302,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D F8 B7 CC D0 E6 01 05 81 19 08 71 AE 75 57 77… +0,,20711,1:13.921.299,15.004.895 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,20712,1:13.936.304,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0D E2 10 C3 04 10 84 3B 8D 36 E4 AD 21 E2 97 BB… +0,,20716,1:13.937.301,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,20717,1:13.951.306,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20721,1:13.952.303,2.833 us,,,,,[1 SOF],[Frame: 161] +0,,20722,1:13.952.307,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0D E2 10 C3 04 10 84 3B 8D 36 E4 AD 21 E2 97 BB… +0,,20726,1:13.953.303,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,20727,1:13.968.309,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A DF EE 09 F5 F4 EB AE A2 80 89 D4 22 89 68 71… +0,,20731,1:13.969.306,14.004.750 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,20732,1:13.983.311,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20736,1:13.984.308,2.812 us,,,,,[1 SOF],[Frame: 193] +0,,20737,1:13.984.311,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A DF EE 09 F5 F4 EB AE A2 80 89 D4 22 89 68 71… +0,,20741,1:13.985.308,15.004.916 ms,,,,,[16 SOF],[Frames: 194 - 209] +0,,20742,1:14.000.313,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 39 EC FC 01 A8 42 4A 30 BF C0 1C D4 71 1B FB 17… +0,,20746,1:14.001.310,14.004.750 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,20747,1:14.015.315,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20751,1:14.016.312,2.812 us,,,,,[1 SOF],[Frame: 225] +0,,20752,1:14.016.315,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 39 EC FC 01 A8 42 4A 30 BF C0 1C D4 71 1B FB 17… +0,,20756,1:14.017.312,15.004.916 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,20757,1:14.032.318,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 CF B9 8B 71 DB F4 9C 48 B8 3F 46 F2 2A 3A 07 91… +0,,20761,1:14.033.315,14.004.770 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,20762,1:14.047.320,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20766,1:14.048.317,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,20767,1:14.048.320,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 CF B9 8B 71 DB F4 9C 48 B8 3F 46 F2 2A 3A 07 91… +0,,20771,1:14.049.317,15.004.895 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,20772,1:14.064.322,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 47 44 F8 65 20 F3 4B 67 5B F4 EE 63 DB DD C9… +0,,20776,1:14.065.319,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,20777,1:14.079.324,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20781,1:14.080.321,2.833 us,,,,,[1 SOF],[Frame: 289] +0,,20782,1:14.080.324,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 47 44 F8 65 20 F3 4B 67 5B F4 EE 63 DB DD C9… +0,,20786,1:14.081.321,15.004.895 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,20787,1:14.096.327,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C8 E0 BD A5 59 01 65 0A 31 C8 C6 D5 7D 69 68 48… +0,,20791,1:14.097.323,14.004.750 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,20792,1:14.111.329,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20796,1:14.112.325,2.833 us,,,,,[1 SOF],[Frame: 321] +0,,20797,1:14.112.329,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 C8 E0 BD A5 59 01 65 0A 31 C8 C6 D5 7D 69 68 48… +0,,20801,1:14.113.326,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,20802,1:14.128.331,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 D4 88 FA 6D D4 DC 95 E1 26 88 1F 73 A0 CD 9A… +0,,20806,1:14.129.328,14.004.750 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,20807,1:14.143.333,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20811,1:14.144.330,2.812 us,,,,,[1 SOF],[Frame: 353] +0,,20812,1:14.144.333,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 D4 88 FA 6D D4 DC 95 E1 26 88 1F 73 A0 CD 9A… +0,,20816,1:14.145.330,15.004.916 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,20817,1:14.160.335,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 3F 16 1E 42 B3 54 61 9F B5 DC 2A A5 6A 05 DE… +0,,20821,1:14.161.332,14.004.770 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,20822,1:14.175.337,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20826,1:14.176.334,16.005.041 ms,,,,,[17 SOF],[Frames: 385 - 401] +0,,20827,1:14.192.340,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D BF CC 9C A0 9A 9A B0 D9 EC 6E 90 D8 B5 E4 5D… +0,,20831,1:14.193.337,14.004.770 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,20832,1:14.207.342,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20836,1:14.208.339,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,20837,1:14.208.342,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D BF CC 9C A0 9A 9A B0 D9 EC 6E 90 D8 B5 E4 5D… +0,,20841,1:14.209.339,15.004.895 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,20842,1:14.224.344,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 91 A2 A1 7F 6C F5 23 E1 4F 2A F8 42 74 A0 32… +0,,20846,1:14.225.341,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,20847,1:14.239.346,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20851,1:14.240.343,2.833 us,,,,,[1 SOF],[Frame: 449] +0,,20852,1:14.240.347,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 91 A2 A1 7F 6C F5 23 E1 4F 2A F8 42 74 A0 32… +0,,20856,1:14.241.343,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,20857,1:14.256.349,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A5 DB E5 E8 DA FA 2D 63 3E 41 4A 34 C2 F7 EA C0… +0,,20861,1:14.257.346,14.004.750 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,20862,1:14.271.351,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20866,1:14.272.348,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,20867,1:14.272.351,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A5 DB E5 E8 DA FA 2D 63 3E 41 4A 34 C2 F7 EA C0… +0,,20871,1:14.273.348,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,20872,1:14.288.353,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC 54 8B C2 FB 87 39 26 4D 99 29 22 7E C6 42 F7… +0,,20876,1:14.289.350,14.004.750 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,20877,1:14.303.355,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20881,1:14.304.352,2.812 us,,,,,[1 SOF],[Frame: 513] +0,,20882,1:14.304.355,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC 54 8B C2 FB 87 39 26 4D 99 29 22 7E C6 42 F7… +0,,20886,1:14.305.352,15.004.916 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,20887,1:14.320.358,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6E 8E F9 6C D1 BF 3A 65 30 5E 71 0E 49 A9 5D 31… +0,,20891,1:14.321.355,14.004.770 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,20892,1:14.335.360,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20896,1:14.336.357,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,20897,1:14.336.360,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6E 8E F9 6C D1 BF 3A 65 30 5E 71 0E 49 A9 5D 31… +0,,20901,1:14.337.357,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,20902,1:14.352.362,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 39 73 BD A6 B8 74 2A 31 B2 2E 37 81 2D 8F C4 95… +0,,20906,1:14.353.359,14.004.770 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,20907,1:14.367.364,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20911,1:14.368.361,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,20912,1:14.368.364,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 39 73 BD A6 B8 74 2A 31 B2 2E 37 81 2D 8F C4 95… +0,,20916,1:14.369.361,15.004.895 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,20917,1:14.384.367,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 23 41 61 82 54 97 64 0E A3 93 30 38 39 02 CF 4D… +0,,20921,1:14.385.363,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,20922,1:14.399.369,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20926,1:14.400.365,2.833 us,,,,,[1 SOF],[Frame: 609] +0,,20927,1:14.400.369,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 23 41 61 82 54 97 64 0E A3 93 30 38 39 02 CF 4D… +0,,20931,1:14.401.366,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,20932,1:14.416.371,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 4D 91 D4 0F 75 94 95 D4 E2 72 6D BD CF DE 1F… +0,,20936,1:14.417.368,14.004.750 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,20937,1:14.431.373,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20941,1:14.432.370,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,20942,1:14.432.373,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 4D 91 D4 0F 75 94 95 D4 E2 72 6D BD CF DE 1F… +0,,20946,1:14.433.370,15.004.916 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,20947,1:14.448.375,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2C 2D F7 42 1D F4 AD FB 0A 13 DE 16 81 EB EE 3F… +0,,20951,1:14.449.372,14.004.750 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,20952,1:14.463.377,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20956,1:14.464.374,2.812 us,,,,,[1 SOF],[Frame: 673] +0,,20957,1:14.464.378,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2C 2D F7 42 1D F4 AD FB 0A 13 DE 16 81 EB EE 3F… +0,,20961,1:14.465.374,15.004.916 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,20962,1:14.480.380,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2D 94 A4 8D E6 C8 EB FF FC 90 B5 E7 01 A7 17 E2… +0,,20966,1:14.481.377,14.004.770 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,20967,1:14.495.382,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20971,1:14.496.379,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,20972,1:14.496.382,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2D 94 A4 8D E6 C8 EB FF FC 90 B5 E7 01 A7 17 E2… +0,,20976,1:14.497.379,15.004.895 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,20977,1:14.512.384,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 79 06 7F 0A 8F B1 B8 55 AC F1 04 AE C8 70 33 37… +0,,20981,1:14.513.381,14.004.770 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,20982,1:14.527.386,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,20986,1:14.528.383,2.833 us,,,,,[1 SOF],[Frame: 737] +0,,20987,1:14.528.386,50.687 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 79 06 7F 0A 8F B1 B8 55 AC F1 04 AE C8 70 33 37… +0,,20991,1:14.529.383,15.004.895 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,20992,1:14.544.389,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D1 43 C7 1F 09 71 BD 90 01 01 A6 35 62 E0 FD 53… +0,,20996,1:14.545.386,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,20997,1:14.559.391,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21001,1:14.560.388,2.833 us,,,,,[1 SOF],[Frame: 769] +0,,21002,1:14.560.391,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D1 43 C7 1F 09 71 BD 90 01 01 A6 35 62 E0 FD 53… +0,,21006,1:14.561.388,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,21007,1:14.576.393,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD E9 BA 67 6E CC 10 27 C7 37 F0 B4 FA 89 97 3B… +0,,21011,1:14.577.390,14.004.750 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,21012,1:14.591.395,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21016,1:14.592.392,2.812 us,,,,,[1 SOF],[Frame: 801] +0,,21017,1:14.592.395,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DD E9 BA 67 6E CC 10 27 C7 37 F0 B4 FA 89 97 3B… +0,,21021,1:14.593.392,15.004.916 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,21022,1:14.608.398,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 68 8A 13 25 18 69 82 95 8F 21 5E 7E C3 B6 10 FD… +0,,21026,1:14.609.394,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,21027,1:14.623.400,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21031,1:14.624.397,2.812 us,,,,,[1 SOF],[Frame: 833] +0,,21032,1:14.624.400,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 68 8A 13 25 18 69 82 95 8F 21 5E 7E C3 B6 10 FD… +0,,21036,1:14.625.397,15.004.895 ms,,,,,[16 SOF],[Frames: 834 - 849] +0,,21037,1:14.640.402,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 30 E5 33 EB F7 A9 7F CC 16 33 87 50 CE 98 8C 10… +0,,21041,1:14.641.399,14.004.770 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,21042,1:14.655.404,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21046,1:14.656.401,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,21047,1:14.656.404,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 30 E5 33 EB F7 A9 7F CC 16 33 87 50 CE 98 8C 10… +0,,21051,1:14.657.401,15.004.895 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,21052,1:14.672.406,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1D F3 87 0D F4 E7 9F 63 3E D0 88 D1 B1 A9 63 24… +0,,21056,1:14.673.403,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,21057,1:14.687.409,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21061,1:14.688.405,2.833 us,,,,,[1 SOF],[Frame: 897] +0,,21062,1:14.688.409,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1D F3 87 0D F4 E7 9F 63 3E D0 88 D1 B1 A9 63 24… +0,,21066,1:14.689.406,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,21067,1:14.704.411,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D7 45 E9 1D 4B 3D 33 1F A0 61 65 20 83 82 EB B8… +0,,21071,1:14.705.408,14.004.750 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,21072,1:14.719.413,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21076,1:14.720.410,2.833 us,,,,,[1 SOF],[Frame: 929] +0,,21077,1:14.720.413,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D7 45 E9 1D 4B 3D 33 1F A0 61 65 20 83 82 EB B8… +0,,21081,1:14.721.410,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,21082,1:14.736.415,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 E6 C0 28 7C 44 21 7C EE CC E9 08 2A C1 BC 0D… +0,,21086,1:14.737.412,14.004.750 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,21087,1:14.751.417,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21091,1:14.752.414,2.812 us,,,,,[1 SOF],[Frame: 961] +0,,21092,1:14.752.418,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 E6 C0 28 7C 44 21 7C EE CC E9 08 2A C1 BC 0D… +0,,21096,1:14.753.414,15.004.916 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,21097,1:14.768.420,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 C5 66 20 1A 85 55 45 0C D1 4F 9A 78 12 AA 7A 62… +0,,21101,1:14.769.417,14.004.770 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,21102,1:14.783.422,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21106,1:14.784.419,16.005.125 ms,,,,,[17 SOF],[Frames: 993 - 1009] +0,,21107,1:14.800.424,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 36 CC 29 A8 FA A0 81 CF 9A 91 1F B4 51 C1 A4 D4… +0,,21111,1:14.801.421,14.004.770 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,21112,1:14.815.426,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21116,1:14.816.423,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,21117,1:14.816.426,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 36 CC 29 A8 FA A0 81 CF 9A 91 1F B4 51 C1 A4 D4… +0,,21121,1:14.817.423,15.004.895 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,21122,1:14.832.429,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC F5 07 17 4E 6A 6D D9 98 9B C6 79 FB 7F F1 78… +0,,21126,1:14.833.426,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,21127,1:14.847.431,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21131,1:14.848.428,2.833 us,,,,,[1 SOF],[Frame: 1057] +0,,21132,1:14.848.431,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC F5 07 17 4E 6A 6D D9 98 9B C6 79 FB 7F F1 78… +0,,21136,1:14.849.428,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,21137,1:14.864.433,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6F C4 E6 66 07 55 80 8B A7 A8 CC 97 C9 83 56 53… +0,,21141,1:14.865.430,14.004.750 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,21142,1:14.879.435,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21146,1:14.880.432,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,21147,1:14.880.435,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6F C4 E6 66 07 55 80 8B A7 A8 CC 97 C9 83 56 53… +0,,21151,1:14.881.432,15.004.916 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,21152,1:14.896.438,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A4 C9 7C B0 EC FE 72 4A 36 35 F7 23 85 39 A4 79… +0,,21156,1:14.897.434,14.004.750 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,21157,1:14.911.440,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21161,1:14.912.437,2.812 us,,,,,[1 SOF],[Frame: 1121] +0,,21162,1:14.912.440,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A4 C9 7C B0 EC FE 72 4A 36 35 F7 23 85 39 A4 79… +0,,21166,1:14.913.437,15.004.916 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,21167,1:14.928.442,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 78 0C A9 A4 8C 95 30 CE 57 23 BE 4B AC FF 74… +0,,21171,1:14.929.439,14.004.770 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,21172,1:14.943.444,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21176,1:14.944.441,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,21177,1:14.944.444,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 78 0C A9 A4 8C 95 30 CE 57 23 BE 4B AC FF 74… +0,,21181,1:14.945.441,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,21182,1:14.960.446,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 0A EA 31 40 55 FF A6 C5 FA 22 37 69 EC 3F 3D E3… +0,,21186,1:14.961.443,14.004.770 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,21187,1:14.975.449,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21191,1:14.976.445,2.833 us,,,,,[1 SOF],[Frame: 1185] +0,,21192,1:14.976.449,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 0A EA 31 40 55 FF A6 C5 FA 22 37 69 EC 3F 3D E3… +0,,21196,1:14.977.446,15.004.895 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,21197,1:14.992.451,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B6 9C 6C EE D4 98 03 CE 55 77 13 95 DC 31 1C C4… +0,,21201,1:14.993.448,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,21202,1:15.007.453,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21206,1:15.008.450,2.833 us,,,,,[1 SOF],[Frame: 1217] +0,,21207,1:15.008.453,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B6 9C 6C EE D4 98 03 CE 55 77 13 95 DC 31 1C C4… +0,,21211,1:15.009.450,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,21212,1:15.024.455,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EF 4E 6D BC 3E 2B B9 51 8F 9A B9 EB E4 20 DD 83… +0,,21216,1:15.025.452,14.004.750 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,21217,1:15.039.457,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21221,1:15.040.454,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,21222,1:15.040.458,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 EF 4E 6D BC 3E 2B B9 51 8F 9A B9 EB E4 20 DD 83… +0,,21226,1:15.041.454,15.004.916 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,21227,1:15.056.460,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A E7 8F BA D5 27 AD B1 41 F0 E3 73 6D 18 7C 17… +0,,21231,1:15.057.457,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,21232,1:15.071.462,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21236,1:15.072.459,2.812 us,,,,,[1 SOF],[Frame: 1281] +0,,21237,1:15.072.462,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A E7 8F BA D5 27 AD B1 41 F0 E3 73 6D 18 7C 17… +0,,21241,1:15.073.459,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,21242,1:15.088.464,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 69 5A CF EE 0E AA 62 40 09 2A 1C EC 94 D1 A4 2D… +0,,21246,1:15.089.461,14.004.770 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,21247,1:15.103.466,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21251,1:15.104.463,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,21252,1:15.104.466,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 69 5A CF EE 0E AA 62 40 09 2A 1C EC 94 D1 A4 2D… +0,,21256,1:15.105.463,15.004.895 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,21257,1:15.120.469,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A2 7C 12 43 0A C2 EB 80 4A ED 40 D8 6C 39 95 34… +0,,21261,1:15.121.466,14.004.770 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,21262,1:15.135.471,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21266,1:15.136.468,2.833 us,,,,,[1 SOF],[Frame: 1345] +0,,21267,1:15.136.471,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A2 7C 12 43 0A C2 EB 80 4A ED 40 D8 6C 39 95 34… +0,,21271,1:15.137.468,15.004.895 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,21272,1:15.152.473,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4B E4 EB 24 2A 68 D3 6F BD 53 5F 21 02 6C 9B 8F… +0,,21276,1:15.153.470,14.004.750 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,21277,1:15.167.475,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21281,1:15.168.472,2.833 us,,,,,[1 SOF],[Frame: 1377] +0,,21282,1:15.168.475,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4B E4 EB 24 2A 68 D3 6F BD 53 5F 21 02 6C 9B 8F… +0,,21286,1:15.169.472,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,21287,1:15.184.478,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9B FF F4 08 0D C5 18 F3 CA CD 83 0F 1C 8D E7 C3… +0,,21291,1:15.185.474,14.004.750 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,21292,1:15.199.480,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21296,1:15.200.477,2.812 us,,,,,[1 SOF],[Frame: 1409] +0,,21297,1:15.200.480,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9B FF F4 08 0D C5 18 F3 CA CD 83 0F 1C 8D E7 C3… +0,,21301,1:15.201.477,15.004.916 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,21302,1:15.216.482,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D CA E9 F3 D2 F1 A1 99 AA C2 97 C8 55 22 D4 AC… +0,,21306,1:15.217.479,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,21307,1:15.231.484,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21311,1:15.232.481,2.812 us,,,,,[1 SOF],[Frame: 1441] +0,,21312,1:15.232.484,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D CA E9 F3 D2 F1 A1 99 AA C2 97 C8 55 22 D4 AC… +0,,21316,1:15.233.481,15.004.895 ms,,,,,[16 SOF],[Frames: 1442 - 1457] +0,,21317,1:15.248.486,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1C 90 FF 08 CA F0 AD E5 5A A9 48 26 BB 82 22 81… +0,,21321,1:15.249.483,14.004.770 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,21322,1:15.263.489,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21326,1:15.264.485,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,21327,1:15.264.489,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1C 90 FF 08 CA F0 AD E5 5A A9 48 26 BB 82 22 81… +0,,21331,1:15.265.486,15.004.895 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,21332,1:15.280.491,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 5A 0F 28 5F 1D 84 82 BB 4F E8 5F 32 A9 E0 05 71… +0,,21336,1:15.281.488,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,21337,1:15.295.493,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21341,1:15.296.490,2.833 us,,,,,[1 SOF],[Frame: 1505] +0,,21342,1:15.296.493,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 5A 0F 28 5F 1D 84 82 BB 4F E8 5F 32 A9 E0 05 71… +0,,21346,1:15.297.490,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,21347,1:15.312.495,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 4F D2 0D B9 0C 7B 01 D3 C2 74 18 AB E8 61 22 B3… +0,,21351,1:15.313.492,14.004.750 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,21352,1:15.327.497,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21356,1:15.328.494,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,21357,1:15.328.498,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 4F D2 0D B9 0C 7B 01 D3 C2 74 18 AB E8 61 22 B3… +0,,21361,1:15.329.494,15.005.000 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,21362,1:15.344.500,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 9C FC 8C 98 C5 D9 FC 19 60 EA FF 74 8C A8 73 A6… +0,,21366,1:15.345.497,14.004.750 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,21367,1:15.359.502,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21371,1:15.360.499,2.812 us,,,,,[1 SOF],[Frame: 1569] +0,,21372,1:15.360.502,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 9C FC 8C 98 C5 D9 FC 19 60 EA FF 74 8C A8 73 A6… +0,,21376,1:15.361.499,15.004.916 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,21377,1:15.376.504,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 80 94 D0 29 8F A8 1F 36 71 D2 09 29 9B EC 9E 36… +0,,21381,1:15.377.501,14.004.770 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,21382,1:15.391.506,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21386,1:15.392.503,2.812 us,,,,,[1 SOF],[Frame: 1601] +0,,21387,1:15.392.506,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 80 94 D0 29 8F A8 1F 36 71 D2 09 29 9B EC 9E 36… +0,,21391,1:15.393.503,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,21392,1:15.408.509,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7E 6F 7E 4D 0A 19 3E 84 F2 F4 23 D6 99 C8 5F 7C… +0,,21396,1:15.409.506,14.004.770 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,21397,1:15.423.511,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21401,1:15.424.508,16.005.041 ms,,,,,[17 SOF],[Frames: 1633 - 1649] +0,,21402,1:15.440.513,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 99 79 FA 26 25 B1 E2 FE 60 5C 3F E4 3E 8C C9… +0,,21406,1:15.441.510,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,21407,1:15.455.515,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21411,1:15.456.512,2.833 us,,,,,[1 SOF],[Frame: 1665] +0,,21412,1:15.456.515,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 99 79 FA 26 25 B1 E2 FE 60 5C 3F E4 3E 8C C9… +0,,21416,1:15.457.512,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,21417,1:15.472.518,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 57 87 DA 4D F9 23 FD BD 60 20 43 FA AD A7 4B 13… +0,,21421,1:15.473.514,14.004.750 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,21422,1:15.487.520,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21426,1:15.488.517,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,21427,1:15.488.520,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 57 87 DA 4D F9 23 FD BD 60 20 43 FA AD A7 4B 13… +0,,21431,1:15.489.517,15.004.916 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,21432,1:15.504.522,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 FC 4F 07 2A 20 D2 5D C4 D9 1B 24 9A 6F D5 DE 94… +0,,21436,1:15.505.519,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,21437,1:15.519.524,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21441,1:15.520.521,2.812 us,,,,,[1 SOF],[Frame: 1729] +0,,21442,1:15.520.524,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 FC 4F 07 2A 20 D2 5D C4 D9 1B 24 9A 6F D5 DE 94… +0,,21446,1:15.521.521,15.004.916 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,21447,1:15.536.526,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E7 99 96 87 38 8E 44 E3 7C 26 BA 0A 12 46 CA 82… +0,,21451,1:15.537.523,14.004.770 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,21452,1:15.551.528,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21456,1:15.552.525,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,21457,1:15.552.529,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E7 99 96 87 38 8E 44 E3 7C 26 BA 0A 12 46 CA 82… +0,,21461,1:15.553.526,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,21462,1:15.568.531,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 24 77 1E 0E 4F 2E D3 35 B5 81 18 B8 44 1D 88 23… +0,,21466,1:15.569.528,14.004.770 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,21467,1:15.583.533,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21471,1:15.584.530,2.833 us,,,,,[1 SOF],[Frame: 1793] +0,,21472,1:15.584.533,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 24 77 1E 0E 4F 2E D3 35 B5 81 18 B8 44 1D 88 23… +0,,21476,1:15.585.530,15.004.895 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,21477,1:15.600.535,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DC 18 AF F9 3F 69 90 47 94 7A AA D8 BF 1A B6 0F… +0,,21481,1:15.601.532,14.004.750 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,21482,1:15.615.537,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21486,1:15.616.534,2.916 us,,,,,[1 SOF],[Frame: 1825] +0,,21487,1:15.616.538,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DC 18 AF F9 3F 69 90 47 94 7A AA D8 BF 1A B6 0F… +0,,21491,1:15.617.534,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,21492,1:15.632.540,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 75 E8 0A C9 05 FA 44 20 9C BC 21 91 41 36 32 57… +0,,21496,1:15.633.537,14.004.750 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,21497,1:15.647.542,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21501,1:15.648.539,2.812 us,,,,,[1 SOF],[Frame: 1857] +0,,21502,1:15.648.542,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 75 E8 0A C9 05 FA 44 20 9C BC 21 91 41 36 32 57… +0,,21506,1:15.649.539,15.004.916 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,21507,1:15.664.544,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 62 E8 93 28 C8 9E 42 FC 8A 63 75 72 7C 20 7A E2… +0,,21511,1:15.665.541,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,21512,1:15.679.546,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21516,1:15.680.543,2.812 us,,,,,[1 SOF],[Frame: 1889] +0,,21517,1:15.680.546,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 62 E8 93 28 C8 9E 42 FC 8A 63 75 72 7C 20 7A E2… +0,,21521,1:15.681.543,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,21522,1:15.696.549,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 47 0C E3 8B C1 8B EB 8D 1B F5 7E C3 77 42 94… +0,,21526,1:15.697.546,14.004.854 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,21527,1:15.711.551,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21531,1:15.712.548,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,21532,1:15.712.551,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 47 0C E3 8B C1 8B EB 8D 1B F5 7E C3 77 42 94… +0,,21536,1:15.713.548,15.004.895 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,21537,1:15.728.553,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 83 EC 17 C7 F4 D3 28 FB 8D EA 13 18 FC BE 4B C9… +0,,21541,1:15.729.550,14.004.770 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,21542,1:15.743.555,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21546,1:15.744.552,2.833 us,,,,,[1 SOF],[Frame: 1953] +0,,21547,1:15.744.555,50.833 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 83 EC 17 C7 F4 D3 28 FB 8D EA 13 18 FC BE 4B C9… +0,,21551,1:15.745.552,15.004.895 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,21552,1:15.760.558,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E3 45 A9 2A FB 11 B6 59 2B 60 AC B7 9A 05 0B 6B… +0,,21556,1:15.761.554,14.004.750 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,21557,1:15.775.560,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21561,1:15.776.556,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,21562,1:15.776.560,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E3 45 A9 2A FB 11 B6 59 2B 60 AC B7 9A 05 0B 6B… +0,,21566,1:15.777.557,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,21567,1:15.792.562,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B8 DB 28 70 76 41 D7 4F 9C 59 99 7B 75 3E 25 AC… +0,,21571,1:15.793.559,14.004.833 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,21572,1:15.807.564,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21576,1:15.808.561,2.895 us,,,,,[1 SOF],[Frame: 2017] +0,,21577,1:15.808.564,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B8 DB 28 70 76 41 D7 4F 9C 59 99 7B 75 3E 25 AC… +0,,21581,1:15.809.561,15.005.000 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,21582,1:15.824.566,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 18 61 8F D9 89 25 6C 31 42 7E 34 92 B6 EF 24 24… +0,,21586,1:15.825.563,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,21587,1:15.839.568,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21591,1:15.840.565,2.812 us,,,,,[1 SOF],[Frame: 1] +0,,21592,1:15.840.569,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 18 61 8F D9 89 25 6C 31 42 7E 34 92 B6 EF 24 24… +0,,21596,1:15.841.566,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,21597,1:15.856.571,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 53 28 AC FB 39 17 19 45 DD 5B 3D C2 9A D6 86 99… +0,,21601,1:15.857.568,14.004.770 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,21602,1:15.871.573,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21606,1:15.872.570,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,21607,1:15.872.573,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 53 28 AC FB 39 17 19 45 DD 5B 3D C2 9A D6 86 99… +0,,21611,1:15.873.570,15.004.895 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,21612,1:15.888.575,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E4 17 EA 04 90 1F A4 55 E9 AB F5 2D 34 73 79 42… +0,,21616,1:15.889.572,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,21617,1:15.903.577,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21621,1:15.904.574,2.833 us,,,,,[1 SOF],[Frame: 65] +0,,21622,1:15.904.577,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E4 17 EA 04 90 1F A4 55 E9 AB F5 2D 34 73 79 42… +0,,21626,1:15.905.574,15.004.895 ms,,,,,[16 SOF],[Frames: 66 - 81] +0,,21627,1:15.920.580,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8E F3 AE 8F A7 67 2D F6 B2 FC 29 D5 8D 3F 97 97… +0,,21631,1:15.921.577,14.004.750 ms,,,,,[15 SOF],[Frames: 82 - 96] +0,,21632,1:15.935.582,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21636,1:15.936.579,2.812 us,,,,,[1 SOF],[Frame: 97] +0,,21637,1:15.936.582,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8E F3 AE 8F A7 67 2D F6 B2 FC 29 D5 8D 3F 97 97… +0,,21641,1:15.937.579,15.004.916 ms,,,,,[16 SOF],[Frames: 98 - 113] +0,,21642,1:15.952.584,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 51 58 55 F6 2E 37 4F C6 9B 87 03 97 AC DD BB 40… +0,,21646,1:15.953.581,14.004.750 ms,,,,,[15 SOF],[Frames: 114 - 128] +0,,21647,1:15.967.586,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21651,1:15.968.583,2.812 us,,,,,[1 SOF],[Frame: 129] +0,,21652,1:15.968.586,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 51 58 55 F6 2E 37 4F C6 9B 87 03 97 AC DD BB 40… +0,,21656,1:15.969.583,15.004.916 ms,,,,,[16 SOF],[Frames: 130 - 145] +0,,21657,1:15.984.589,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F4 3A 34 E1 66 90 2A BA 8F B3 B8 CD EA 03 44 42… +0,,21661,1:15.985.585,14.004.770 ms,,,,,[15 SOF],[Frames: 146 - 160] +0,,21662,1:15.999.591,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21666,1:16.000.588,2.812 us,,,,,[1 SOF],[Frame: 161] +0,,21667,1:16.000.591,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F4 3A 34 E1 66 90 2A BA 8F B3 B8 CD EA 03 44 42… +0,,21671,1:16.001.588,15.004.895 ms,,,,,[16 SOF],[Frames: 162 - 177] +0,,21672,1:16.016.593,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DD AA 61 68 B4 EE 54 6A 7C 51 11 99 AB 6A F5 55… +0,,21676,1:16.017.590,14.004.770 ms,,,,,[15 SOF],[Frames: 178 - 192] +0,,21677,1:16.031.595,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21681,1:16.032.592,16.005.041 ms,,,,,[17 SOF],[Frames: 193 - 209] +0,,21682,1:16.048.597,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A B9 ED 0C 75 DC 6B 35 00 B0 BF 38 82 24 09 2D… +0,,21686,1:16.049.594,14.004.770 ms,,,,,[15 SOF],[Frames: 210 - 224] +0,,21687,1:16.063.600,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21691,1:16.064.596,2.833 us,,,,,[1 SOF],[Frame: 225] +0,,21692,1:16.064.600,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A B9 ED 0C 75 DC 6B 35 00 B0 BF 38 82 24 09 2D… +0,,21696,1:16.065.597,15.004.895 ms,,,,,[16 SOF],[Frames: 226 - 241] +0,,21697,1:16.080.602,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 DE EE 0E CF E6 93 70 86 4F E8 6F 74 39 EB 83 75… +0,,21701,1:16.081.599,14.004.750 ms,,,,,[15 SOF],[Frames: 242 - 256] +0,,21702,1:16.095.604,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21706,1:16.096.601,2.812 us,,,,,[1 SOF],[Frame: 257] +0,,21707,1:16.096.604,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 DE EE 0E CF E6 93 70 86 4F E8 6F 74 39 EB 83 75… +0,,21711,1:16.097.601,15.004.916 ms,,,,,[16 SOF],[Frames: 258 - 273] +0,,21712,1:16.112.606,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D9 76 1B 8D 0A C5 DA 9E AC 53 57 FB 64 78 F9 DB… +0,,21716,1:16.113.603,14.004.770 ms,,,,,[15 SOF],[Frames: 274 - 288] +0,,21717,1:16.127.608,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21721,1:16.128.605,2.812 us,,,,,[1 SOF],[Frame: 289] +0,,21722,1:16.128.609,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D9 76 1B 8D 0A C5 DA 9E AC 53 57 FB 64 78 F9 DB… +0,,21726,1:16.129.605,15.004.916 ms,,,,,[16 SOF],[Frames: 290 - 305] +0,,21727,1:16.144.611,50.354 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A6 9C 30 01 30 1C C2 B1 0B 70 12 D9 82 99 B6 B6… +0,,21731,1:16.145.608,14.004.770 ms,,,,,[15 SOF],[Frames: 306 - 320] +0,,21732,1:16.159.613,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21736,1:16.160.610,2.812 us,,,,,[1 SOF],[Frame: 321] +0,,21737,1:16.160.613,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A6 9C 30 01 30 1C C2 B1 0B 70 12 D9 82 99 B6 B6… +0,,21741,1:16.161.610,15.004.895 ms,,,,,[16 SOF],[Frames: 322 - 337] +0,,21742,1:16.176.615,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 73 A3 00 13 41 FE EC BB 10 DD 44 C2 40 C8 4C 67… +0,,21746,1:16.177.612,14.004.770 ms,,,,,[15 SOF],[Frames: 338 - 352] +0,,21747,1:16.191.617,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21751,1:16.192.614,2.833 us,,,,,[1 SOF],[Frame: 353] +0,,21752,1:16.192.617,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 73 A3 00 13 41 FE EC BB 10 DD 44 C2 40 C8 4C 67… +0,,21756,1:16.193.614,15.004.895 ms,,,,,[16 SOF],[Frames: 354 - 369] +0,,21757,1:16.208.620,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 91 50 A7 10 C2 25 7F 09 05 97 B8 B8 19 85 0E 97… +0,,21761,1:16.209.617,14.004.750 ms,,,,,[15 SOF],[Frames: 370 - 384] +0,,21762,1:16.223.622,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21766,1:16.224.619,2.833 us,,,,,[1 SOF],[Frame: 385] +0,,21767,1:16.224.622,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 91 50 A7 10 C2 25 7F 09 05 97 B8 B8 19 85 0E 97… +0,,21771,1:16.225.619,15.004.895 ms,,,,,[16 SOF],[Frames: 386 - 401] +0,,21772,1:16.240.624,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B2 7C 67 F8 00 C5 1A AB CD D6 D1 0A EE 7C B3 E3… +0,,21776,1:16.241.621,14.004.750 ms,,,,,[15 SOF],[Frames: 402 - 416] +0,,21777,1:16.255.626,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21781,1:16.256.623,2.812 us,,,,,[1 SOF],[Frame: 417] +0,,21782,1:16.256.626,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B2 7C 67 F8 00 C5 1A AB CD D6 D1 0A EE 7C B3 E3… +0,,21786,1:16.257.623,15.004.916 ms,,,,,[16 SOF],[Frames: 418 - 433] +0,,21787,1:16.272.629,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 B0 1B 3B D7 1B AB 9B 3F 5D DF 76 B5 BD E1 E8 7F… +0,,21791,1:16.273.625,14.004.770 ms,,,,,[15 SOF],[Frames: 434 - 448] +0,,21792,1:16.287.631,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21796,1:16.288.628,2.812 us,,,,,[1 SOF],[Frame: 449] +0,,21797,1:16.288.631,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 B0 1B 3B D7 1B AB 9B 3F 5D DF 76 B5 BD E1 E8 7F… +0,,21801,1:16.289.628,15.004.895 ms,,,,,[16 SOF],[Frames: 450 - 465] +0,,21802,1:16.304.633,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 74 32 05 5B 89 B3 43 64 2F 95 1B CA A1 84 50 A1… +0,,21806,1:16.305.630,14.004.770 ms,,,,,[15 SOF],[Frames: 466 - 480] +0,,21807,1:16.319.635,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21811,1:16.320.632,2.812 us,,,,,[1 SOF],[Frame: 481] +0,,21812,1:16.320.635,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 74 32 05 5B 89 B3 43 64 2F 95 1B CA A1 84 50 A1… +0,,21816,1:16.321.632,15.004.895 ms,,,,,[16 SOF],[Frames: 482 - 497] +0,,21817,1:16.336.637,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 38 BF F5 06 1A CC 68 7F 2D 80 93 87 C3 79 59 5E… +0,,21821,1:16.337.634,14.004.770 ms,,,,,[15 SOF],[Frames: 498 - 512] +0,,21822,1:16.351.640,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21826,1:16.352.636,2.833 us,,,,,[1 SOF],[Frame: 513] +0,,21827,1:16.352.640,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 38 BF F5 06 1A CC 68 7F 2D 80 93 87 C3 79 59 5E… +0,,21831,1:16.353.637,15.004.895 ms,,,,,[16 SOF],[Frames: 514 - 529] +0,,21832,1:16.368.642,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 01 4A 11 8E 57 8F 24 D9 64 C1 31 37 F8 1D F2 73… +0,,21836,1:16.369.639,14.004.750 ms,,,,,[15 SOF],[Frames: 530 - 544] +0,,21837,1:16.383.644,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21841,1:16.384.641,2.812 us,,,,,[1 SOF],[Frame: 545] +0,,21842,1:16.384.644,50.479 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 01 4A 11 8E 57 8F 24 D9 64 C1 31 37 F8 1D F2 73… +0,,21846,1:16.385.641,15.004.895 ms,,,,,[16 SOF],[Frames: 546 - 561] +0,,21847,1:16.400.646,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 34 73 70 59 D6 53 2A 68 B0 B4 3F 82 66 D6 F5 72… +0,,21851,1:16.401.643,14.004.750 ms,,,,,[15 SOF],[Frames: 562 - 576] +0,,21852,1:16.415.648,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21856,1:16.416.645,2.812 us,,,,,[1 SOF],[Frame: 577] +0,,21857,1:16.416.649,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 34 73 70 59 D6 53 2A 68 B0 B4 3F 82 66 D6 F5 72… +0,,21861,1:16.417.645,15.004.916 ms,,,,,[16 SOF],[Frames: 578 - 593] +0,,21862,1:16.432.651,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC 18 4D F0 26 76 F6 E0 24 AC FB 70 74 2C 2B CF… +0,,21866,1:16.433.648,14.004.770 ms,,,,,[15 SOF],[Frames: 594 - 608] +0,,21867,1:16.447.653,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21871,1:16.448.650,2.812 us,,,,,[1 SOF],[Frame: 609] +0,,21872,1:16.448.653,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC 18 4D F0 26 76 F6 E0 24 AC FB 70 74 2C 2B CF… +0,,21876,1:16.449.650,15.004.895 ms,,,,,[16 SOF],[Frames: 610 - 625] +0,,21877,1:16.464.655,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7D 34 62 BA 2F AB 8D 0B BF 7B B0 B4 97 1F CF B1… +0,,21881,1:16.465.652,14.004.770 ms,,,,,[15 SOF],[Frames: 626 - 640] +0,,21882,1:16.479.657,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21886,1:16.480.654,2.812 us,,,,,[1 SOF],[Frame: 641] +0,,21887,1:16.480.657,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7D 34 62 BA 2F AB 8D 0B BF 7B B0 B4 97 1F CF B1… +0,,21891,1:16.481.654,15.004.895 ms,,,,,[16 SOF],[Frames: 642 - 657] +0,,21892,1:16.496.660,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A0 5D EE 79 55 AF 08 74 DA C6 61 2C FB B0 2E 4D… +0,,21896,1:16.497.657,14.004.770 ms,,,,,[15 SOF],[Frames: 658 - 672] +0,,21897,1:16.511.662,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21901,1:16.512.659,2.833 us,,,,,[1 SOF],[Frame: 673] +0,,21902,1:16.512.662,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A0 5D EE 79 55 AF 08 74 DA C6 61 2C FB B0 2E 4D… +0,,21906,1:16.513.659,15.004.895 ms,,,,,[16 SOF],[Frames: 674 - 689] +0,,21907,1:16.528.664,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A3 95 FA 53 4D FC 22 ED 83 8A 14 8F 1C E8 0F 93… +0,,21911,1:16.529.661,14.004.750 ms,,,,,[15 SOF],[Frames: 690 - 704] +0,,21912,1:16.543.666,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21916,1:16.544.663,2.812 us,,,,,[1 SOF],[Frame: 705] +0,,21917,1:16.544.666,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A3 95 FA 53 4D FC 22 ED 83 8A 14 8F 1C E8 0F 93… +0,,21921,1:16.545.663,15.004.916 ms,,,,,[16 SOF],[Frames: 706 - 721] +0,,21922,1:16.560.669,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 74 A3 9B DE 66 EA 58 17 B2 BB AC F5 F8 DD 50 89… +0,,21926,1:16.561.665,14.004.750 ms,,,,,[15 SOF],[Frames: 722 - 736] +0,,21927,1:16.575.671,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21931,1:16.576.668,2.812 us,,,,,[1 SOF],[Frame: 737] +0,,21932,1:16.576.671,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 74 A3 9B DE 66 EA 58 17 B2 BB AC F5 F8 DD 50 89… +0,,21936,1:16.577.668,15.004.916 ms,,,,,[16 SOF],[Frames: 738 - 753] +0,,21937,1:16.592.673,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 61 F0 A2 3E D5 B0 13 D5 51 BC 1C 62 19 AE 19 75… +0,,21941,1:16.593.670,14.004.770 ms,,,,,[15 SOF],[Frames: 754 - 768] +0,,21942,1:16.607.675,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21946,1:16.608.672,2.812 us,,,,,[1 SOF],[Frame: 769] +0,,21947,1:16.608.675,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 61 F0 A2 3E D5 B0 13 D5 51 BC 1C 62 19 AE 19 75… +0,,21951,1:16.609.672,15.004.895 ms,,,,,[16 SOF],[Frames: 770 - 785] +0,,21952,1:16.624.677,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 7E A3 7E 11 5F 8A 2B 95 97 97 35 F3 09 BA F3 CD… +0,,21956,1:16.625.674,14.004.770 ms,,,,,[15 SOF],[Frames: 786 - 800] +0,,21957,1:16.639.680,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21961,1:16.640.676,2.833 us,,,,,[1 SOF],[Frame: 801] +0,,21962,1:16.640.680,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 7E A3 7E 11 5F 8A 2B 95 97 97 35 F3 09 BA F3 CD… +0,,21966,1:16.641.677,15.004.895 ms,,,,,[16 SOF],[Frames: 802 - 817] +0,,21967,1:16.656.682,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2A 85 A8 2E FC BD 67 99 55 5E 4D 20 D9 51 C9 AB… +0,,21971,1:16.657.679,14.004.770 ms,,,,,[15 SOF],[Frames: 818 - 832] +0,,21972,1:16.671.684,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21976,1:16.672.681,16.005.041 ms,,,,,[17 SOF],[Frames: 833 - 849] +0,,21977,1:16.688.686,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E6 79 66 BF CC D2 1D F6 80 17 90 C2 CB C9 50 10… +0,,21981,1:16.689.683,14.004.750 ms,,,,,[15 SOF],[Frames: 850 - 864] +0,,21982,1:16.703.688,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,21986,1:16.704.685,2.812 us,,,,,[1 SOF],[Frame: 865] +0,,21987,1:16.704.689,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E6 79 66 BF CC D2 1D F6 80 17 90 C2 CB C9 50 10… +0,,21991,1:16.705.685,15.004.916 ms,,,,,[16 SOF],[Frames: 866 - 881] +0,,21992,1:16.720.691,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 20 8A F5 C7 9D DD C3 9E 0C 32 1D 36 D2 1D 8E 64… +0,,21996,1:16.721.688,14.004.770 ms,,,,,[15 SOF],[Frames: 882 - 896] +0,,21997,1:16.735.693,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22001,1:16.736.690,2.812 us,,,,,[1 SOF],[Frame: 897] +0,,22002,1:16.736.693,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 20 8A F5 C7 9D DD C3 9E 0C 32 1D 36 D2 1D 8E 64… +0,,22006,1:16.737.690,15.004.895 ms,,,,,[16 SOF],[Frames: 898 - 913] +0,,22007,1:16.752.695,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 91 42 67 39 E2 34 47 2B 97 8B DE F3 09 5B 04 20… +0,,22011,1:16.753.692,14.004.770 ms,,,,,[15 SOF],[Frames: 914 - 928] +0,,22012,1:16.767.697,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22016,1:16.768.694,2.812 us,,,,,[1 SOF],[Frame: 929] +0,,22017,1:16.768.697,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 91 42 67 39 E2 34 47 2B 97 8B DE F3 09 5B 04 20… +0,,22021,1:16.769.694,15.004.895 ms,,,,,[16 SOF],[Frames: 930 - 945] +0,,22022,1:16.784.700,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BC 85 75 3A EE 9C E6 F6 74 5A 97 86 5D 90 68 48… +0,,22026,1:16.785.697,14.004.770 ms,,,,,[15 SOF],[Frames: 946 - 960] +0,,22027,1:16.799.702,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22031,1:16.800.699,2.833 us,,,,,[1 SOF],[Frame: 961] +0,,22032,1:16.800.702,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BC 85 75 3A EE 9C E6 F6 74 5A 97 86 5D 90 68 48… +0,,22036,1:16.801.699,15.004.895 ms,,,,,[16 SOF],[Frames: 962 - 977] +0,,22037,1:16.816.704,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 93 C2 E7 8E A2 64 38 A7 F4 20 C4 D5 25 CE 4D 2E… +0,,22041,1:16.817.701,14.004.750 ms,,,,,[15 SOF],[Frames: 978 - 992] +0,,22042,1:16.831.706,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22046,1:16.832.703,2.833 us,,,,,[1 SOF],[Frame: 993] +0,,22047,1:16.832.706,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 93 C2 E7 8E A2 64 38 A7 F4 20 C4 D5 25 CE 4D 2E… +0,,22051,1:16.833.703,15.004.979 ms,,,,,[16 SOF],[Frames: 994 - 1009] +0,,22052,1:16.848.709,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 13 1B 35 37 BB 4E F4 3D 3D 41 14 55 B4 B2 85 25… +0,,22056,1:16.849.705,14.004.750 ms,,,,,[15 SOF],[Frames: 1010 - 1024] +0,,22057,1:16.863.711,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22061,1:16.864.708,2.812 us,,,,,[1 SOF],[Frame: 1025] +0,,22062,1:16.864.711,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 13 1B 35 37 BB 4E F4 3D 3D 41 14 55 B4 B2 85 25… +0,,22066,1:16.865.708,15.004.916 ms,,,,,[16 SOF],[Frames: 1026 - 1041] +0,,22067,1:16.880.713,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 05 88 70 0E 10 4D 9F 00 42 65 9F DB 73 E6 11 D2… +0,,22071,1:16.881.710,14.004.770 ms,,,,,[15 SOF],[Frames: 1042 - 1056] +0,,22072,1:16.895.715,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22076,1:16.896.712,2.812 us,,,,,[1 SOF],[Frame: 1057] +0,,22077,1:16.896.715,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 05 88 70 0E 10 4D 9F 00 42 65 9F DB 73 E6 11 D2… +0,,22081,1:16.897.712,15.004.895 ms,,,,,[16 SOF],[Frames: 1058 - 1073] +0,,22082,1:16.912.717,50.395 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 73 DA 25 D2 EE ED D1 5D B9 79 68 A8 72 D6 A9 91… +0,,22086,1:16.913.714,14.004.770 ms,,,,,[15 SOF],[Frames: 1074 - 1088] +0,,22087,1:16.927.719,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22091,1:16.928.716,2.812 us,,,,,[1 SOF],[Frame: 1089] +0,,22092,1:16.928.720,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 73 DA 25 D2 EE ED D1 5D B9 79 68 A8 72 D6 A9 91… +0,,22096,1:16.929.717,15.004.895 ms,,,,,[16 SOF],[Frames: 1090 - 1105] +0,,22097,1:16.944.722,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8A D0 4D F6 3D 8D A3 CD 4E 9A 71 49 B3 0E 57 D7… +0,,22101,1:16.945.719,14.004.770 ms,,,,,[15 SOF],[Frames: 1106 - 1120] +0,,22102,1:16.959.724,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22106,1:16.960.721,2.833 us,,,,,[1 SOF],[Frame: 1121] +0,,22107,1:16.960.724,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8A D0 4D F6 3D 8D A3 CD 4E 9A 71 49 B3 0E 57 D7… +0,,22111,1:16.961.721,15.004.895 ms,,,,,[16 SOF],[Frames: 1122 - 1137] +0,,22112,1:16.976.726,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 19 47 62 00 6B DF 34 E8 4D 49 E0 7F 70 91 DB 9E… +0,,22116,1:16.977.723,14.004.750 ms,,,,,[15 SOF],[Frames: 1138 - 1152] +0,,22117,1:16.991.728,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22121,1:16.992.725,2.895 us,,,,,[1 SOF],[Frame: 1153] +0,,22122,1:16.992.729,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 19 47 62 00 6B DF 34 E8 4D 49 E0 7F 70 91 DB 9E… +0,,22126,1:16.993.725,15.004.895 ms,,,,,[16 SOF],[Frames: 1154 - 1169] +0,,22127,1:17.008.731,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 80 E4 9F AC 74 89 5A 33 6B DF DA 2F 5B 20 15 41… +0,,22131,1:17.009.728,14.004.750 ms,,,,,[15 SOF],[Frames: 1170 - 1184] +0,,22132,1:17.023.733,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22136,1:17.024.730,2.812 us,,,,,[1 SOF],[Frame: 1185] +0,,22137,1:17.024.733,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 80 E4 9F AC 74 89 5A 33 6B DF DA 2F 5B 20 15 41… +0,,22141,1:17.025.730,15.004.916 ms,,,,,[16 SOF],[Frames: 1186 - 1201] +0,,22142,1:17.040.735,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 71 4F 23 A0 B6 BD B3 A6 E2 48 CC E3 D1 D2 D3 83… +0,,22146,1:17.041.732,14.004.770 ms,,,,,[15 SOF],[Frames: 1202 - 1216] +0,,22147,1:17.055.737,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22151,1:17.056.734,2.812 us,,,,,[1 SOF],[Frame: 1217] +0,,22152,1:17.056.737,50.354 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 71 4F 23 A0 B6 BD B3 A6 E2 48 CC E3 D1 D2 D3 83… +0,,22156,1:17.057.734,15.004.895 ms,,,,,[16 SOF],[Frames: 1218 - 1233] +0,,22157,1:17.072.740,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 42 72 28 5C FC BB D9 43 6E 9F 41 CA 03 88 04 E8… +0,,22161,1:17.073.737,14.004.770 ms,,,,,[15 SOF],[Frames: 1234 - 1248] +0,,22162,1:17.087.742,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22166,1:17.088.739,2.812 us,,,,,[1 SOF],[Frame: 1249] +0,,22167,1:17.088.742,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 42 72 28 5C FC BB D9 43 6E 9F 41 CA 03 88 04 E8… +0,,22171,1:17.089.739,15.004.895 ms,,,,,[16 SOF],[Frames: 1250 - 1265] +0,,22172,1:17.104.744,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 6D 68 80 B2 E5 18 7D D1 08 64 A2 53 3D 77 71 AE… +0,,22176,1:17.105.741,14.004.770 ms,,,,,[15 SOF],[Frames: 1266 - 1280] +0,,22177,1:17.119.746,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22181,1:17.120.743,2.833 us,,,,,[1 SOF],[Frame: 1281] +0,,22182,1:17.120.746,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 6D 68 80 B2 E5 18 7D D1 08 64 A2 53 3D 77 71 AE… +0,,22186,1:17.121.743,15.004.895 ms,,,,,[16 SOF],[Frames: 1282 - 1297] +0,,22187,1:17.136.749,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 55 45 F1 6E 59 23 EA 3E 9D E3 79 53 05 E6 B7 B5… +0,,22191,1:17.137.745,14.004.750 ms,,,,,[15 SOF],[Frames: 1298 - 1312] +0,,22192,1:17.151.751,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22196,1:17.152.747,2.812 us,,,,,[1 SOF],[Frame: 1313] +0,,22197,1:17.152.751,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 55 45 F1 6E 59 23 EA 3E 9D E3 79 53 05 E6 B7 B5… +0,,22201,1:17.153.748,15.004.916 ms,,,,,[16 SOF],[Frames: 1314 - 1329] +0,,22202,1:17.168.753,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 85 93 69 F0 07 98 B2 48 4B 55 EF 94 EB CE FD 7F… +0,,22206,1:17.169.750,14.004.750 ms,,,,,[15 SOF],[Frames: 1330 - 1344] +0,,22207,1:17.183.755,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22211,1:17.184.752,2.812 us,,,,,[1 SOF],[Frame: 1345] +0,,22212,1:17.184.755,50.562 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 85 93 69 F0 07 98 B2 48 4B 55 EF 94 EB CE FD 7F… +0,,22216,1:17.185.752,15.004.916 ms,,,,,[16 SOF],[Frames: 1346 - 1361] +0,,22217,1:17.200.757,50.437 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 D5 52 3A AD 17 5F 96 50 0B D2 02 96 D7 61 2F 77… +0,,22221,1:17.201.754,14.004.770 ms,,,,,[15 SOF],[Frames: 1362 - 1376] +0,,22222,1:17.215.759,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22226,1:17.216.756,2.812 us,,,,,[1 SOF],[Frame: 1377] +0,,22227,1:17.216.760,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 D5 52 3A AD 17 5F 96 50 0B D2 02 96 D7 61 2F 77… +0,,22231,1:17.217.757,15.004.895 ms,,,,,[16 SOF],[Frames: 1378 - 1393] +0,,22232,1:17.232.762,50.750 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 59 BF 75 3F C3 23 21 74 0D BF 3F DA 3C 6C 55 A4… +0,,22236,1:17.233.759,14.004.770 ms,,,,,[15 SOF],[Frames: 1394 - 1408] +0,,22237,1:17.247.764,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22241,1:17.248.761,2.833 us,,,,,[1 SOF],[Frame: 1409] +0,,22242,1:17.248.764,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 59 BF 75 3F C3 23 21 74 0D BF 3F DA 3C 6C 55 A4… +0,,22246,1:17.249.761,15.004.895 ms,,,,,[16 SOF],[Frames: 1410 - 1425] +0,,22247,1:17.264.766,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 EC 66 64 36 91 B1 68 06 4A CF 14 D2 EE D3 71 9C… +0,,22251,1:17.265.763,14.004.770 ms,,,,,[15 SOF],[Frames: 1426 - 1440] +0,,22252,1:17.279.768,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22256,1:17.280.765,16.005.041 ms,,,,,[17 SOF],[Frames: 1441 - 1457] +0,,22257,1:17.296.771,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E0 A6 97 12 B0 75 09 AE C2 57 EC 5C A5 D5 78 6E… +0,,22261,1:17.297.768,14.004.750 ms,,,,,[15 SOF],[Frames: 1458 - 1472] +0,,22262,1:17.311.773,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22266,1:17.312.770,2.812 us,,,,,[1 SOF],[Frame: 1473] +0,,22267,1:17.312.773,50.395 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E0 A6 97 12 B0 75 09 AE C2 57 EC 5C A5 D5 78 6E… +0,,22271,1:17.313.770,15.004.916 ms,,,,,[16 SOF],[Frames: 1474 - 1489] +0,,22272,1:17.328.775,50.604 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 71 87 EF E1 C3 79 05 1B BC 3A 7E 9F 9A B3 52 01… +0,,22276,1:17.329.772,14.004.854 ms,,,,,[15 SOF],[Frames: 1490 - 1504] +0,,22277,1:17.343.777,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22281,1:17.344.774,2.812 us,,,,,[1 SOF],[Frame: 1505] +0,,22282,1:17.344.777,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 71 87 EF E1 C3 79 05 1B BC 3A 7E 9F 9A B3 52 01… +0,,22286,1:17.345.774,15.004.895 ms,,,,,[16 SOF],[Frames: 1506 - 1521] +0,,22287,1:17.360.780,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 3E A7 33 B7 87 45 D0 50 16 1F A6 16 77 E5 CF AA… +0,,22291,1:17.361.776,14.004.770 ms,,,,,[15 SOF],[Frames: 1522 - 1536] +0,,22292,1:17.375.782,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22296,1:17.376.779,2.812 us,,,,,[1 SOF],[Frame: 1537] +0,,22297,1:17.376.782,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 3E A7 33 B7 87 45 D0 50 16 1F A6 16 77 E5 CF AA… +0,,22301,1:17.377.779,15.004.979 ms,,,,,[16 SOF],[Frames: 1538 - 1553] +0,,22302,1:17.392.784,50.833 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 41 1D 11 E1 EF B2 B4 E4 C4 D2 D7 56 B4 83 F8 35… +0,,22306,1:17.393.781,14.004.770 ms,,,,,[15 SOF],[Frames: 1554 - 1568] +0,,22307,1:17.407.786,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22311,1:17.408.783,2.833 us,,,,,[1 SOF],[Frame: 1569] +0,,22312,1:17.408.786,50.770 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 41 1D 11 E1 EF B2 B4 E4 C4 D2 D7 56 B4 83 F8 35… +0,,22316,1:17.409.783,15.004.895 ms,,,,,[16 SOF],[Frames: 1570 - 1585] +0,,22317,1:17.424.788,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 BE BA BD 83 3F 5C A2 7A 72 F1 8A 31 24 8B F8 DF… +0,,22321,1:17.425.785,14.004.750 ms,,,,,[15 SOF],[Frames: 1586 - 1600] +0,,22322,1:17.439.791,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22326,1:17.440.787,2.833 us,,,,,[1 SOF],[Frame: 1601] +0,,22327,1:17.440.791,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 BE BA BD 83 3F 5C A2 7A 72 F1 8A 31 24 8B F8 DF… +0,,22331,1:17.441.788,15.004.895 ms,,,,,[16 SOF],[Frames: 1602 - 1617] +0,,22332,1:17.456.793,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 82 2B 44 73 4D 7C 6D 4F 06 B1 D4 60 C6 71 E4 05… +0,,22336,1:17.457.790,14.004.750 ms,,,,,[15 SOF],[Frames: 1618 - 1632] +0,,22337,1:17.471.795,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22341,1:17.472.792,2.812 us,,,,,[1 SOF],[Frame: 1633] +0,,22342,1:17.472.795,50.333 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 82 2B 44 73 4D 7C 6D 4F 06 B1 D4 60 C6 71 E4 05… +0,,22346,1:17.473.792,15.004.916 ms,,,,,[16 SOF],[Frames: 1634 - 1649] +0,,22347,1:17.488.797,50.770 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 2B 6D F3 E7 F3 47 B2 E6 44 1B 92 40 10 AE 12 84… +0,,22351,1:17.489.794,14.004.770 ms,,,,,[15 SOF],[Frames: 1650 - 1664] +0,,22352,1:17.503.799,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22356,1:17.504.796,2.812 us,,,,,[1 SOF],[Frame: 1665] +0,,22357,1:17.504.800,50.750 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 2B 6D F3 E7 F3 47 B2 E6 44 1B 92 40 10 AE 12 84… +0,,22361,1:17.505.796,15.004.895 ms,,,,,[16 SOF],[Frames: 1666 - 1681] +0,,22362,1:17.520.802,50.479 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AD E9 AB D2 8E AA 62 E7 41 42 89 3A A0 A7 8F 34… +0,,22366,1:17.521.799,14.004.770 ms,,,,,[15 SOF],[Frames: 1682 - 1696] +0,,22367,1:17.535.804,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22371,1:17.536.801,2.812 us,,,,,[1 SOF],[Frame: 1697] +0,,22372,1:17.536.804,50.520 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 AD E9 AB D2 8E AA 62 E7 41 42 89 3A A0 A7 8F 34… +0,,22376,1:17.537.801,15.004.895 ms,,,,,[16 SOF],[Frames: 1698 - 1713] +0,,22377,1:17.552.806,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 00 22 85 5E E6 EC 30 C3 BA 8E C6 DB 17 AD F9 95… +0,,22381,1:17.553.803,14.004.770 ms,,,,,[15 SOF],[Frames: 1714 - 1728] +0,,22382,1:17.567.808,50.979 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22386,1:17.568.805,2.833 us,,,,,[1 SOF],[Frame: 1729] +0,,22387,1:17.568.808,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 00 22 85 5E E6 EC 30 C3 BA 8E C6 DB 17 AD F9 95… +0,,22391,1:17.569.805,15.004.895 ms,,,,,[16 SOF],[Frames: 1730 - 1745] +0,,22392,1:17.584.811,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 24 49 0D BB 8E 8F D0 90 E0 72 81 E4 97 6C C3 DA… +0,,22396,1:17.585.808,14.004.750 ms,,,,,[15 SOF],[Frames: 1746 - 1760] +0,,22397,1:17.599.813,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22401,1:17.600.810,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,22402,1:17.600.813,50.416 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 24 49 0D BB 8E 8F D0 90 E0 72 81 E4 97 6C C3 DA… +0,,22406,1:17.601.810,15.004.895 ms,,,,,[16 SOF],[Frames: 1762 - 1777] +0,,22407,1:17.616.815,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 F3 A2 AC 73 66 1D 33 65 7F A6 6F 16 F9 D4 08 38… +0,,22411,1:17.617.812,14.004.750 ms,,,,,[15 SOF],[Frames: 1778 - 1792] +0,,22412,1:17.631.817,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22416,1:17.632.814,2.812 us,,,,,[1 SOF],[Frame: 1793] +0,,22417,1:17.632.817,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 F3 A2 AC 73 66 1D 33 65 7F A6 6F 16 F9 D4 08 38… +0,,22421,1:17.633.814,15.004.916 ms,,,,,[16 SOF],[Frames: 1794 - 1809] +0,,22422,1:17.648.820,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 89 5E DB FC B4 1B AC 8A E4 79 38 95 B2 05 45 32… +0,,22426,1:17.649.816,14.004.770 ms,,,,,[15 SOF],[Frames: 1810 - 1824] +0,,22427,1:17.663.822,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22431,1:17.664.819,2.895 us,,,,,[1 SOF],[Frame: 1825] +0,,22432,1:17.664.822,50.583 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 89 5E DB FC B4 1B AC 8A E4 79 38 95 B2 05 45 32… +0,,22436,1:17.665.819,15.004.895 ms,,,,,[16 SOF],[Frames: 1826 - 1841] +0,,22437,1:17.680.824,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 A8 E1 F0 F6 D1 E2 F8 1B 03 1F D6 76 43 2E 66 DA… +0,,22441,1:17.681.821,14.004.770 ms,,,,,[15 SOF],[Frames: 1842 - 1856] +0,,22442,1:17.695.826,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22446,1:17.696.823,2.833 us,,,,,[1 SOF],[Frame: 1857] +0,,22447,1:17.696.826,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 A8 E1 F0 F6 D1 E2 F8 1B 03 1F D6 76 43 2E 66 DA… +0,,22451,1:17.697.823,15.004.895 ms,,,,,[16 SOF],[Frames: 1858 - 1873] +0,,22452,1:17.712.828,50.583 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 1D CC C4 A4 BB 48 52 5C 46 FE 52 59 2C 9C 5E BB… +0,,22456,1:17.713.825,14.004.770 ms,,,,,[15 SOF],[Frames: 1874 - 1888] +0,,22457,1:17.727.831,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22461,1:17.728.827,2.833 us,,,,,[1 SOF],[Frame: 1889] +0,,22462,1:17.728.831,50.604 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 1D CC C4 A4 BB 48 52 5C 46 FE 52 59 2C 9C 5E BB… +0,,22466,1:17.729.828,15.004.895 ms,,,,,[16 SOF],[Frames: 1890 - 1905] +0,,22467,1:17.744.833,50.666 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 33 F1 74 FA 9D A3 EF CA 8F B3 59 33 9B 91 BD 67… +0,,22471,1:17.745.830,14.004.833 ms,,,,,[15 SOF],[Frames: 1906 - 1920] +0,,22472,1:17.759.835,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22476,1:17.760.832,2.812 us,,,,,[1 SOF],[Frame: 1921] +0,,22477,1:17.760.835,50.666 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 33 F1 74 FA 9D A3 EF CA 8F B3 59 33 9B 91 BD 67… +0,,22481,1:17.761.832,15.004.916 ms,,,,,[16 SOF],[Frames: 1922 - 1937] +0,,22482,1:17.776.837,50.333 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 E2 29 5A 9E 9B 85 94 8B 05 C0 21 63 49 8B 63 8C… +0,,22486,1:17.777.834,14.004.750 ms,,,,,[15 SOF],[Frames: 1938 - 1952] +0,,22487,1:17.791.839,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22491,1:17.792.836,2.812 us,,,,,[1 SOF],[Frame: 1953] +0,,22492,1:17.792.840,50.312 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 E2 29 5A 9E 9B 85 94 8B 05 C0 21 63 49 8B 63 8C… +0,,22496,1:17.793.836,15.004.916 ms,,,,,[16 SOF],[Frames: 1954 - 1969] +0,,22497,1:17.808.842,50.520 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 13 80 95 DD 59 55 E5 D8 99 AA 17 F5 43 6D 64 4B… +0,,22501,1:17.809.839,14.004.770 ms,,,,,[15 SOF],[Frames: 1970 - 1984] +0,,22502,1:17.823.844,51.000 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22506,1:17.824.841,2.895 us,,,,,[1 SOF],[Frame: 1985] +0,,22507,1:17.824.844,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 13 80 95 DD 59 55 E5 D8 99 AA 17 F5 43 6D 64 4B… +0,,22511,1:17.825.841,15.004.979 ms,,,,,[16 SOF],[Frames: 1986 - 2001] +0,,22512,1:17.840.846,50.562 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 8E 29 42 0B 6C 60 AC E1 98 BA EF 0C 31 B6 0A 32… +0,,22516,1:17.841.843,14.004.854 ms,,,,,[15 SOF],[Frames: 2002 - 2016] +0,,22517,1:17.855.848,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22521,1:17.856.845,2.916 us,,,,,[1 SOF],[Frame: 2017] +0,,22522,1:17.856.849,50.500 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 8E 29 42 0B 6C 60 AC E1 98 BA EF 0C 31 B6 0A 32… +0,,22526,1:17.857.845,15.004.979 ms,,,,,[16 SOF],[Frames: 2018 - 2033] +0,,22527,1:17.872.851,50.416 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 49 D6 29 39 4D 9E B7 BA 86 2C 4B 98 20 57 46 D8… +0,,22531,1:17.873.848,14.004.770 ms,,,,,[15 SOF],[Frames: 2034 - 0] +0,,22532,1:17.887.853,50.895 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22536,1:17.888.850,2.833 us,,,,,[1 SOF],[Frame: 1] +0,,22537,1:17.888.853,50.437 us,64 B,,01,01,OUT txn,05 00 1D 01 00 00 20 00 49 D6 29 39 4D 9E B7 BA 86 2C 4B 98 20 57 46 D8… +0,,22541,1:17.889.850,15.004.895 ms,,,,,[16 SOF],[Frames: 2 - 17] +0,,22542,1:17.904.855,50.500 us,64 B,,01,01,OUT txn,27 00 16 23 00 00 20 00 AD 1B 7C 84 88 E7 FE E7 C4 2B D3 D6 BA 65 DA 94… +0,,22546,1:17.905.852,14.004.750 ms,,,,,[15 SOF],[Frames: 18 - 32] +0,,22547,1:17.919.857,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22551,1:17.920.854,2.812 us,,,,,[1 SOF],[Frame: 33] +0,,22552,1:17.920.857,50.479 us,64 B,,01,01,OUT txn,13 00 16 0F 00 06 0C 00 CA 3D CE FC D4 F7 41 57 01 0D E7 84 BA 65 DA 94… +0,,22556,1:17.921.854,15.004.916 ms,,,,,[16 SOF],[Frames: 34 - 49] +0,,22557,1:17.936.860,50.520 us,64 B,,01,01,OUT txn,05 00 16 01 00 02 0C 00 CA 3D CE FC D4 F7 41 57 01 0D E7 84 BA 65 DA 94… +0,,22561,1:17.937.856,14.004.770 ms,,,,,[15 SOF],[Frames: 50 - 64] +0,,22562,1:17.951.862,50.916 us,64 B,,01,02,IN txn,06 00 16 01 00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00… +0,,22566,1:17.952.859,41.008.500 ms,,,,,[42 SOF],[Frames: 65 - 106] +0,,22567,1:17.983.866,4.583 us,,,01,02,[1 IN-NAK], +0,,22568,1:17.994.154,,,,,, / , +0,,22569,1:18.761.750,,,,,, / , +0,,22570,1:18.941.662,,,,,, / , +0,,22571,1:19.007.714,,,,,, / , +0,,22572,1:19.491.928,,,,,, / , +0,,22573,1:19.523.678,,,,,, / , +0,,22574,1:19.524.077,31.007.125 ms,,,,,[32 SOF],[Frames: 1636 - 1667] +0,,22575,1:19.555.084,12.979 us,8 B,,00,00,SETUP txn,80 06 00 01 00 00 40 00 +0,,22579,1:19.556.081,2.833 us,,,,,[1 SOF],[Frame: 1668] +0,,22580,1:19.556.084,20.250 us,18 B,,00,00,IN txn,12 01 00 02 00 00 00 40 35 12 21 AB 01 00 01 02 00 01 +0,,22584,1:19.557.081,1.002.958 ms,,,,,[2 SOF],[Frames: 1669 - 1670] +0,,22585,1:19.558.085,7.645 us,0 B,,00,00,OUT txn, +0,,22589,1:19.559.081,2.833 us,,,,,[1 SOF],[Frame: 1671] +0,,22590,1:19.559.159,,,,,, / , +0,,22591,1:19.585.387,,,,,, / , +0,,22592,1:19.586.085,31.007.125 ms,,,,,[32 SOF],[Frames: 1698 - 1729] +0,,22593,1:19.617.093,12.979 us,8 B,,00,00,SETUP txn,00 05 01 00 00 00 00 00 +0,,22597,1:19.618.090,2.833 us,,,,,[1 SOF],[Frame: 1730] +0,,22598,1:19.618.093,8.250 us,0 B,,00,00,IN txn, +0,,22602,1:19.619.090,29.006.854 ms,,,,,[30 SOF],[Frames: 1731 - 1760] +0,,22603,1:19.648.098,13.000 us,8 B,,01,00,SETUP txn,80 06 00 01 00 00 12 00 +0,,22607,1:19.649.094,2.895 us,,,,,[1 SOF],[Frame: 1761] +0,,22608,1:19.649.097,20.229 us,18 B,,01,00,IN txn,12 01 00 02 00 00 00 40 35 12 21 AB 01 00 01 02 00 01 +0,,22612,1:19.650.094,2.812 us,,,,,[1 SOF],[Frame: 1762] +0,,22613,1:19.650.097,7.666 us,0 B,,01,00,OUT txn, +0,,22617,1:19.651.094,1.002.958 ms,,,,,[2 SOF],[Frames: 1763 - 1764] +0,,22618,1:19.652.098,13.083 us,8 B,,01,00,SETUP txn,80 06 00 02 00 00 FF 00 +0,,22622,1:19.653.095,2.812 us,,,,,[1 SOF],[Frame: 1765] +0,,22623,1:19.653.098,35.562 us,41 B,,01,00,IN txn,09 02 29 00 01 01 00 C0 F0 09 04 00 00 02 03 00 00 00 09 21 10 01 00 01… +0,,22627,1:19.654.095,1.003.041 ms,,,,,[2 SOF],[Frames: 1766 - 1767] +0,,22628,1:19.655.098,7.666 us,0 B,,01,00,OUT txn, +0,,22632,1:19.656.095,1.002.958 ms,,,,,[2 SOF],[Frames: 1768 - 1769] +0,,22633,1:19.657.099,13.083 us,8 B,,01,00,SETUP txn,80 06 00 03 00 00 FF 00 +0,,22637,1:19.658.095,2.812 us,,,,,[1 SOF],[Frame: 1770] +0,,22638,1:19.658.098,10.895 us,4 B,,01,00,IN txn,04 03 09 04 +0,,22642,1:19.659.095,1.002.958 ms,,,,,[2 SOF],[Frames: 1771 - 1772] +0,,22643,1:19.660.099,7.666 us,0 B,,01,00,OUT txn, +0,,22647,1:19.661.096,1.002.958 ms,,,,,[2 SOF],[Frames: 1773 - 1774] +0,,22648,1:19.662.099,13.083 us,8 B,,01,00,SETUP txn,80 06 02 03 09 04 FF 00 +0,,22652,1:19.663.096,2.833 us,,,,,[1 SOF],[Frame: 1775] +0,,22653,1:19.663.099,28.250 us,30 B,,01,00,IN txn,1E 03 53 00 46 00 43 00 33 00 30 00 20 00 4A 00 6F 00 79 00 73 00 74 00… +0,,22657,1:19.664.096,1.002.958 ms,,,,,[2 SOF],[Frames: 1776 - 1777] +0,,22658,1:19.665.099,7.645 us,0 B,,01,00,OUT txn, +0,,22662,1:19.666.096,1.002.958 ms,,,,,[2 SOF],[Frames: 1778 - 1779] +0,,22663,1:19.667.100,13.000 us,8 B,,01,00,SETUP txn,80 06 00 06 00 00 0A 00 +0,,22667,1:19.668.097,2.812 us,,,,,[1 SOF],[Frame: 1780] +0,,22668,1:19.668.100,4.562 us,,,01,00,IN txn (STALL), +0,,22671,1:19.669.097,1.556.218.812 s,,,,,[1557 SOF],[Frames: 1781 - 1289] +0,,22672,1:21.225.316,13.000 us,8 B,,01,00,SETUP txn,80 06 00 01 00 00 12 00 +0,,22676,1:21.226.313,2.812 us,,,,,[1 SOF],[Frame: 1290] +0,,22677,1:21.226.316,20.250 us,18 B,,01,00,IN txn,12 01 00 02 00 00 00 40 35 12 21 AB 01 00 01 02 00 01 +0,,22681,1:21.227.313,2.812 us,,,,,[1 SOF],[Frame: 1291] +0,,22682,1:21.227.316,7.645 us,0 B,,01,00,OUT txn, +0,,22686,1:21.228.313,1.002.958 ms,,,,,[2 SOF],[Frames: 1292 - 1293] +0,,22687,1:21.229.318,13.000 us,8 B,,01,00,SETUP txn,80 06 00 02 00 00 09 00 +0,,22691,1:21.230.313,2.812 us,,,,,[1 SOF],[Frame: 1294] +0,,22692,1:21.230.317,14.312 us,9 B,,01,00,IN txn,09 02 29 00 01 01 00 C0 F0 +0,,22696,1:21.231.314,2.833 us,,,,,[1 SOF],[Frame: 1295] +0,,22697,1:21.231.317,7.666 us,0 B,,01,00,OUT txn, +0,,22701,1:21.232.314,1.002.958 ms,,,,,[2 SOF],[Frames: 1296 - 1297] +0,,22702,1:21.233.317,12.979 us,8 B,,01,00,SETUP txn,80 06 00 02 00 00 29 00 +0,,22706,1:21.234.314,2.833 us,,,,,[1 SOF],[Frame: 1298] +0,,22707,1:21.234.317,35.583 us,41 B,,01,00,IN txn,09 02 29 00 01 01 00 C0 F0 09 04 00 00 02 03 00 00 00 09 21 10 01 00 01… +0,,22711,1:21.235.314,2.812 us,,,,,[1 SOF],[Frame: 1299] +0,,22712,1:21.235.317,7.666 us,0 B,,01,00,OUT txn, +0,,22716,1:21.236.314,1.002.958 ms,,,,,[2 SOF],[Frames: 1300 - 1301] +0,,22717,1:21.237.318,13.000 us,8 B,,01,00,SETUP txn,00 09 01 00 00 00 00 00 +0,,22721,1:21.238.315,2.895 us,,,,,[1 SOF],[Frame: 1302] +0,,22722,1:21.238.318,8.229 us,0 B,,01,00,IN txn, +0,,22726,1:21.239.315,1.002.958 ms,,,,,[2 SOF],[Frames: 1303 - 1304] +0,,22727,1:21.240.318,13.000 us,8 B,,01,00,SETUP txn,21 0A 00 00 00 00 00 00 +0,,22731,1:21.241.315,2.812 us,,,,,[1 SOF],[Frame: 1305] +0,,22732,1:21.241.318,4.583 us,,,01,00,IN txn (STALL), +0,,22735,1:21.242.315,2.003.083 ms,,,,,[3 SOF],[Frames: 1306 - 1308] +0,,22736,1:21.244.319,12.979 us,8 B,,01,00,SETUP txn,81 06 00 22 00 00 A3 00 +0,,22740,1:21.245.315,2.812 us,,,,,[1 SOF],[Frame: 1309] +0,,22741,1:21.245.319,51.062 us,64 B,,01,00,IN txn,05 01 09 04 A1 01 A1 02 75 08 95 04 15 00 26 FF 00 35 00 46 FF 00 09 30… +0,,22745,1:21.246.316,2.833 us,,,,,[1 SOF],[Frame: 1310] +0,,22746,1:21.246.319,31.750 us,35 B,,01,00,IN txn,02 06 00 FF 75 01 95 08 25 01 45 01 09 01 81 02 C0 A1 02 75 08 95 08 46… +0,,22750,1:21.247.316,1.002.958 ms,,,,,[2 SOF],[Frames: 1311 - 1312] +0,,22751,1:21.248.321,7.645 us,0 B,,01,00,OUT txn, +0,,22755,1:21.249.316,1.999.280.291 s,,,,,[2000 SOF],[Frames: 1313 - 1264] [Periodic Timeout] +0,,22756,1:21.280.324,1.984.279.979 s,,,01,01,[63 IN-NAK],[Periodic Timeout] +0,,22757,1:23.249.594,1.999.280.291 s,,,,,[2000 SOF],[Frames: 1265 - 1216] [Periodic Timeout] +0,,22758,1:23.296.603,1.984.279.979 s,,,01,01,[63 IN-NAK],[Periodic Timeout] +0,,22759,1:25.249.871,1.999.280.291 s,,,,,[2000 SOF],[Frames: 1217 - 1168] [Periodic Timeout] +0,,22760,1:25.312.883,1.984.279.958 s,,,01,01,[63 IN-NAK],[Periodic Timeout] +0,,22761,1:27.250.149,1.999.280.291 s,,,,,[2000 SOF],[Frames: 1169 - 1120] [Periodic Timeout] +0,,22762,1:27.329.163,1.984.279.979 s,,,01,01,[63 IN-NAK],[Periodic Timeout] +0,,22763,1:29.250.426,1.999.280.291 s,,,,,[2000 SOF],[Frames: 1121 - 1072] [Periodic Timeout] +0,,22764,1:29.345.443,1.984.279.958 s,,,01,01,[63 IN-NAK],[Periodic Timeout] +0,,22765,1:31.250.704,1.798.252.375 s,,,,,[1799 SOF],[Frames: 1073 - 823] +0,,22766,1:31.361.723,1.664.235.541 s,,,01,01,[53 IN-NAK], +0,,22767,1:33.048.954,,,,,,Capture stopped,[Fri 03 Jun 2016 12:01:34 BST] diff --git a/fwupd-1.8.6/plugins/ebitdo/data/update.tdc b/fwupd-1.8.6/plugins/ebitdo/data/update.tdc new file mode 100644 index 0000000000000000000000000000000000000000..5c5585e79bf8f238ecb3efb68df8b12fdaaf0145 Binary files /dev/null and b/fwupd-1.8.6/plugins/ebitdo/data/update.tdc differ diff --git a/fwupd-1.8.6/plugins/ebitdo/ebitdo-legacy.quirk b/fwupd-1.8.6/plugins/ebitdo/ebitdo-legacy.quirk new file mode 100644 index 0000000000000000000000000000000000000000..2165a075dabe08fb0588c31e707b53b1962c5236 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/ebitdo-legacy.quirk @@ -0,0 +1,9 @@ +# This is the ID assigned to STMicroelectronics, and also seems to be used by other vendors who +# did not change the default from the devkit. Install this quirk file if your 8bitdo controller +# uses the legacy bootloader from 2018. +# +# See https://github.com/fwupd/fwupd/issues/4180 for more information + +[USB\VID_0483&PID_5750] +Plugin = ebitdo +Flags = is-bootloader diff --git a/fwupd-1.8.6/plugins/ebitdo/ebitdo.quirk b/fwupd-1.8.6/plugins/ebitdo/ebitdo.quirk new file mode 100644 index 0000000000000000000000000000000000000000..0c9ea47a5a4e8172481e76ad7af0effb20024914 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/ebitdo.quirk @@ -0,0 +1,117 @@ +# bootloader +[USB\VID_2DC8&PID_5750] +Plugin = ebitdo +Flags = is-bootloader +InstallDuration = 120 +[USB\VID_0483&PID_5760] +Plugin = ebitdo +Flags = is-bootloader,will-disappear +InstallDuration = 120 + +# FC30 +[USB\VID_1235&PID_AB11] +Plugin = ebitdo +Flags = ~is-bootloader +[USB\VID_2DC8&PID_AB11] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# NES30 +[USB\VID_1235&PID_AB12] +Plugin = ebitdo +Flags = ~is-bootloader +[USB\VID_2DC8&PID_AB12] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# SFC30 +[USB\VID_1235&PID_AB21] +Plugin = ebitdo +Flags = ~is-bootloader +[USB\VID_2DC8&PID_AB21] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# SNES30 +[USB\VID_1235&PID_AB20] +Plugin = ebitdo +Flags = ~is-bootloader +[USB\VID_2DC8&PID_AB20] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# FC30PRO +[USB\VID_1002&PID_9000] +Plugin = ebitdo +Flags = ~is-bootloader +[USB\VID_2DC8&PID_9000] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# NES30PRO +[USB\VID_2002&PID_9000] +Plugin = ebitdo +Flags = will-disappear +[USB\VID_2DC8&PID_9001] +Plugin = ebitdo +Flags = will-disappear +InstallDuration = 120 + +# FC30_ARCADE +[USB\VID_8000&PID_1002] +Plugin = ebitdo +Flags = ~is-bootloader +[USB\VID_2DC8&PID_1002] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# SF30 PRO/SN30 PRO +## Dinput mode (Start + B) +[USB\VID_2DC8&PID_6000] +Plugin = ebitdo +Flags = will-disappear +[USB\VID_2DC8&PID_6001] +Plugin = ebitdo +Flags = will-disappear +InstallDuration = 120 + +# SN30 PRO+ +## Dinput mode (Start + B) +[USB\VID_2DC8&PID_6002] +Plugin = ebitdo +Flags = will-disappear +InstallDuration = 120 + +# M30 +[USB\VID_2DC8&PID_5006] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# SN30v2 +[USB\VID_2DC8&PID_9012] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# SN30 Pro for Android +[USB\VID_2DC8&PID_2100] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 +[USB\VID_2DC8&PID_2101] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 + +# N30 Pro 2 +[USB\VID_2DC8&PID_9015] +Plugin = ebitdo +Flags = ~is-bootloader +InstallDuration = 120 diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-common.c b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-common.c new file mode 100644 index 0000000000000000000000000000000000000000..1d43fc555dc50a843f672acdef696537d8bbf5af --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-common.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ebitdo-common.h" + +const gchar * +fu_ebitdo_pkt_type_to_string(FuEbitdoPktType cmd) +{ + if (cmd == FU_EBITDO_PKT_TYPE_USER_CMD) + return "user-cmd"; + if (cmd == FU_EBITDO_PKT_TYPE_USER_DATA) + return "user-data"; + if (cmd == FU_EBITDO_PKT_TYPE_MID_CMD) + return "mid-cmd"; + return NULL; +} + +const gchar * +fu_ebitdo_pkt_cmd_to_string(FuEbitdoPktCmd cmd) +{ + if (cmd == FU_EBITDO_PKT_CMD_FW_UPDATE_DATA) + return "fw-update-data"; + if (cmd == FU_EBITDO_PKT_CMD_FW_UPDATE_HEADER) + return "fw-update-header"; + if (cmd == FU_EBITDO_PKT_CMD_FW_UPDATE_OK) + return "fw-update-ok"; + if (cmd == FU_EBITDO_PKT_CMD_FW_UPDATE_ERROR) + return "fw-update-error"; + if (cmd == FU_EBITDO_PKT_CMD_FW_GET_VERSION) + return "fw-get-version"; + if (cmd == FU_EBITDO_PKT_CMD_FW_SET_VERSION) + return "fw-set-version"; + if (cmd == FU_EBITDO_PKT_CMD_FW_SET_ENCODE_ID) + return "fw-set-encode-id"; + if (cmd == FU_EBITDO_PKT_CMD_ACK) + return "ack"; + if (cmd == FU_EBITDO_PKT_CMD_NAK) + return "nak"; + if (cmd == FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA) + return "update-firmware-data"; + if (cmd == FU_EBITDO_PKT_CMD_TRANSFER_ABORT) + return "transfer-abort"; + if (cmd == FU_EBITDO_PKT_CMD_VERIFICATION_ID) + return "verification-id"; + if (cmd == FU_EBITDO_PKT_CMD_GET_VERIFICATION_ID) + return "get-verification-id"; + if (cmd == FU_EBITDO_PKT_CMD_VERIFY_ERROR) + return "verify-error"; + if (cmd == FU_EBITDO_PKT_CMD_VERIFY_OK) + return "verify-ok"; + if (cmd == FU_EBITDO_PKT_CMD_TRANSFER_TIMEOUT) + return "transfer-timeout"; + if (cmd == FU_EBITDO_PKT_CMD_GET_VERSION) + return "get-version"; + if (cmd == FU_EBITDO_PKT_CMD_GET_VERSION_RESPONSE) + return "get-version-response"; + return NULL; +} + +void +fu_ebitdo_dump_pkt(FuEbitdoPkt *hdr) +{ + g_print("PktLength: 0x%02x\n", hdr->pkt_len); + g_print("PktType: 0x%02x [%s]\n", hdr->type, fu_ebitdo_pkt_type_to_string(hdr->type)); + g_print("CmdSubtype: 0x%02x [%s]\n", + hdr->subtype, + fu_ebitdo_pkt_cmd_to_string(hdr->subtype)); + g_print("CmdLen: 0x%04x\n", GUINT16_FROM_LE(hdr->cmd_len)); + g_print("Cmd: 0x%02x [%s]\n", hdr->cmd, fu_ebitdo_pkt_cmd_to_string(hdr->cmd)); + g_print("Payload Len: 0x%04x\n", GUINT16_FROM_LE(hdr->payload_len)); +} diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-common.h b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-common.h new file mode 100644 index 0000000000000000000000000000000000000000..690896e5fb4d6f2512b241c517e4a3d5e9a6dee0 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-common.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* little endian */ +typedef struct __attribute__((packed)) { + guint8 pkt_len; + guint8 type; /* FuEbitdoPktType */ + guint8 subtype; /* FuEbitdoPktCmd */ + guint16 cmd_len; + guint8 cmd; /* FuEbitdoPktCmd */ + guint16 payload_len; /* optional */ +} FuEbitdoPkt; + +#define FU_EBITDO_USB_TIMEOUT 5000 /* ms */ +#define FU_EBITDO_USB_BOOTLOADER_EP_IN 0x82 +#define FU_EBITDO_USB_BOOTLOADER_EP_OUT 0x01 +#define FU_EBITDO_USB_RUNTIME_EP_IN 0x81 +#define FU_EBITDO_USB_RUNTIME_EP_OUT 0x02 +#define FU_EBITDO_USB_EP_SIZE 64 /* bytes */ + +typedef enum { + FU_EBITDO_PKT_TYPE_USER_CMD = 0x00, + FU_EBITDO_PKT_TYPE_USER_DATA = 0x01, + FU_EBITDO_PKT_TYPE_MID_CMD = 0x02, + FU_EBITDO_PKT_TYPE_LAST +} FuEbitdoPktType; + +/* commands */ +typedef enum { + FU_EBITDO_PKT_CMD_FW_UPDATE_DATA = 0x00, /* update firmware data */ + FU_EBITDO_PKT_CMD_FW_UPDATE_HEADER = 0x01, /* update firmware header */ + FU_EBITDO_PKT_CMD_FW_UPDATE_OK = 0x02, /* mark update as successful */ + FU_EBITDO_PKT_CMD_FW_UPDATE_ERROR = 0x03, /* update firmware error */ + FU_EBITDO_PKT_CMD_FW_GET_VERSION = 0x04, /* get cur firmware vision */ + FU_EBITDO_PKT_CMD_FW_SET_VERSION = 0x05, /* set firmware version */ + FU_EBITDO_PKT_CMD_FW_SET_ENCODE_ID = 0x06, /* set app firmware encode ID */ + FU_EBITDO_PKT_CMD_ACK = 0x14, /* acknowledge */ + FU_EBITDO_PKT_CMD_NAK = 0x15, /* negative acknowledge */ + FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA = 0x16, /* update firmware data */ + FU_EBITDO_PKT_CMD_TRANSFER_ABORT = 0x18, /* aborts transfer */ + FU_EBITDO_PKT_CMD_VERIFICATION_ID = 0x19, /* verification id (only BT?) */ + FU_EBITDO_PKT_CMD_GET_VERIFICATION_ID = 0x1a, /* verification id (only BT) */ + FU_EBITDO_PKT_CMD_VERIFY_ERROR = 0x1b, /* verification error */ + FU_EBITDO_PKT_CMD_VERIFY_OK = 0x1c, /* verification successful */ + FU_EBITDO_PKT_CMD_TRANSFER_TIMEOUT = 0x1d, /* send or receive data timeout */ + FU_EBITDO_PKT_CMD_GET_VERSION = 0x21, /* get fw ver, joystick mode */ + FU_EBITDO_PKT_CMD_GET_VERSION_RESPONSE = 0x22, /* get fw version response */ + FU_EBITDO_PKT_CMD_FW_LAST +} FuEbitdoPktCmd; + +const gchar * +fu_ebitdo_pkt_cmd_to_string(FuEbitdoPktCmd cmd); +const gchar * +fu_ebitdo_pkt_type_to_string(FuEbitdoPktType type); +void +fu_ebitdo_dump_pkt(FuEbitdoPkt *hdr); diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-device.c b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-device.c new file mode 100644 index 0000000000000000000000000000000000000000..181f33de2a2b4b0ba6f3983220d00667e86a416d --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-device.c @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-ebitdo-common.h" +#include "fu-ebitdo-device.h" +#include "fu-ebitdo-firmware.h" + +struct _FuEbitdoDevice { + FuUsbDevice parent_instance; + guint32 serial[9]; +}; + +G_DEFINE_TYPE(FuEbitdoDevice, fu_ebitdo_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_ebitdo_device_send(FuEbitdoDevice *self, + FuEbitdoPktType type, + FuEbitdoPktCmd subtype, + FuEbitdoPktCmd cmd, + const guint8 *in, + gsize in_len, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 packet[FU_EBITDO_USB_EP_SIZE] = {0}; + gsize actual_length; + guint8 ep_out = FU_EBITDO_USB_RUNTIME_EP_OUT; + g_autoptr(GError) error_local = NULL; + FuEbitdoPkt *hdr = (FuEbitdoPkt *)packet; + + /* different */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + ep_out = FU_EBITDO_USB_BOOTLOADER_EP_OUT; + + /* check size */ + if (in_len > 64 - 8) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "input buffer too large"); + return FALSE; + } + + /* packet[0] is the total length of the packet */ + hdr->type = type; + hdr->subtype = subtype; + + /* do we have a payload */ + if (in_len > 0) { + hdr->cmd_len = GUINT16_TO_LE(in_len + 3); + hdr->cmd = cmd; + hdr->payload_len = GUINT16_TO_LE(in_len); + if (!fu_memcpy_safe(packet, + sizeof(packet), + 0x08, /* dst */ + in, + in_len, + 0x0, /* src */ + in_len, + error)) + return FALSE; + hdr->pkt_len = (guint8)(in_len + 7); + } else { + hdr->cmd_len = GUINT16_TO_LE(in_len + 1); + hdr->cmd = cmd; + hdr->pkt_len = 5; + } + + /* debug */ + if (g_getenv("FWUPD_EBITDO_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "->DEVICE", packet, (gsize)hdr->pkt_len + 1); + fu_ebitdo_dump_pkt(hdr); + } + + /* get data from device */ + if (!g_usb_device_interrupt_transfer(usb_device, + ep_out, + packet, + FU_EBITDO_USB_EP_SIZE, + &actual_length, + FU_EBITDO_USB_TIMEOUT, + NULL, /* cancellable */ + &error_local)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to send to device on ep 0x%02x: %s", + (guint)FU_EBITDO_USB_BOOTLOADER_EP_OUT, + error_local->message); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ebitdo_device_receive(FuEbitdoDevice *self, guint8 *out, gsize out_len, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 packet[FU_EBITDO_USB_EP_SIZE] = {0}; + gsize actual_length; + guint8 ep_in = FU_EBITDO_USB_RUNTIME_EP_IN; + g_autoptr(GError) error_local = NULL; + FuEbitdoPkt *hdr = (FuEbitdoPkt *)packet; + + /* different */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + ep_in = FU_EBITDO_USB_BOOTLOADER_EP_IN; + + /* get data from device */ + if (!g_usb_device_interrupt_transfer(usb_device, + ep_in, + packet, + FU_EBITDO_USB_EP_SIZE, + &actual_length, + FU_EBITDO_USB_TIMEOUT, + NULL, /* cancellable */ + &error_local)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to retrieve from device on ep 0x%02x: %s", + (guint)ep_in, + error_local->message); + return FALSE; + } + + /* debug */ + if (g_getenv("FWUPD_EBITDO_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "<-DEVICE", packet, actual_length); + fu_ebitdo_dump_pkt(hdr); + } + + /* get-version (booloader) */ + if (hdr->type == FU_EBITDO_PKT_TYPE_USER_CMD && + hdr->subtype == FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA && + hdr->cmd == FU_EBITDO_PKT_CMD_FW_GET_VERSION) { + if (out != NULL) { + if (hdr->payload_len < out_len) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "payload too small, expected %" G_GSIZE_FORMAT + " got %u", + out_len, + hdr->payload_len); + return FALSE; + } + if (!fu_memcpy_safe(out, + out_len, + 0x0, /* dst */ + packet, + sizeof(packet), + sizeof(FuEbitdoPkt), /* src */ + out_len, + error)) + return FALSE; + } + return TRUE; + } + + /* get-version (firmware) -- not a packet, just raw data! */ + if (hdr->pkt_len == FU_EBITDO_PKT_CMD_GET_VERSION_RESPONSE) { + if (out != NULL) { + if (out_len != 4) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "outbuf size wrong, expected 4 got %" G_GSIZE_FORMAT, + out_len); + return FALSE; + } + if (!fu_memcpy_safe(out, + out_len, + 0x0, /* dst */ + packet, + sizeof(packet), + 0x1, /* src */ + 4, + error)) + return FALSE; + } + return TRUE; + } + + /* verification-id response */ + if (hdr->type == FU_EBITDO_PKT_TYPE_USER_CMD && + hdr->subtype == FU_EBITDO_PKT_CMD_VERIFICATION_ID) { + if (out != NULL) { + if (hdr->cmd_len != out_len) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "outbuf size wrong, expected %" G_GSIZE_FORMAT + " got %i", + out_len, + hdr->cmd_len); + return FALSE; + } + if (!fu_memcpy_safe(out, + out_len, + 0x0, /* dst */ + packet, + sizeof(packet), + sizeof(FuEbitdoPkt) - 3, /* src */ + hdr->cmd_len, + error)) + return FALSE; + } + return TRUE; + } + + /* update-firmware-data */ + if (hdr->type == FU_EBITDO_PKT_TYPE_USER_CMD && + hdr->subtype == FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA && hdr->payload_len == 0x00) { + if (hdr->cmd != FU_EBITDO_PKT_CMD_ACK) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "write failed, got %s", + fu_ebitdo_pkt_cmd_to_string(hdr->cmd)); + return FALSE; + } + return TRUE; + } + + /* unhandled */ + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unexpected device response"); + return FALSE; +} + +static void +fu_ebitdo_device_set_version(FuEbitdoDevice *self, guint32 version) +{ + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf("%u.%02u", version / 100, version % 100); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version_raw(FU_DEVICE(self), version); + fu_device_set_version(FU_DEVICE(self), tmp); +} + +static gboolean +fu_ebitdo_device_validate(FuEbitdoDevice *self, GError **error) +{ + const gchar *ven; + const gchar *allowlist[] = {"8Bitdo", "8BitDo", "SFC30", NULL}; + + /* this is a new, always valid, VID */ + if (fu_usb_device_get_vid(FU_USB_DEVICE(self)) == 0x2dc8) + return TRUE; + + /* verify the vendor prefix against a allowlist */ + ven = fu_device_get_vendor(FU_DEVICE(self)); + if (ven == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "could not check vendor descriptor: "); + return FALSE; + } + for (guint i = 0; allowlist[i] != NULL; i++) { + if (g_str_has_prefix(ven, allowlist[i])) + return TRUE; + } + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "vendor '%s' did not match allowlist, " + "probably not a 8BitDo device…", + ven); + return FALSE; +} + +static gboolean +fu_ebitdo_device_open(FuDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + FuEbitdoDevice *self = FU_EBITDO_DEVICE(device); + + /* FuUsbDevice->open */ + if (!FU_DEVICE_CLASS(fu_ebitdo_device_parent_class)->open(device, error)) + return FALSE; + + /* open, then ensure this is actually 8BitDo hardware */ + if (!fu_ebitdo_device_validate(self, error)) + return FALSE; + if (!g_usb_device_claim_interface(usb_device, + 0, /* 0 = idx? */ + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + 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}; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_ebitdo_device_parent_class)->setup(device, error)) + return FALSE; + + /* in firmware mode */ + if (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_GET_VERSION, + 0, + NULL, + 0, /* in */ + error)) { + return FALSE; + } + if (!fu_ebitdo_device_receive(self, + (guint8 *)&version_tmp, + sizeof(version_tmp), + error)) { + return FALSE; + } + tmp = (gdouble)GUINT32_FROM_LE(version_tmp); + fu_ebitdo_device_set_version(self, tmp); + return TRUE; + } + + /* get version */ + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA, + FU_EBITDO_PKT_CMD_FW_GET_VERSION, + NULL, + 0, /* in */ + error)) { + return FALSE; + } + if (!fu_ebitdo_device_receive(self, (guint8 *)&version_tmp, sizeof(version_tmp), error)) { + return FALSE; + } + tmp = (gdouble)GUINT32_FROM_LE(version_tmp); + fu_ebitdo_device_set_version(self, tmp); + + /* get verification ID */ + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_GET_VERIFICATION_ID, + 0x00, /* cmd */ + NULL, + 0, + error)) { + return FALSE; + } + if (!fu_ebitdo_device_receive(self, (guint8 *)&serial_tmp, sizeof(serial_tmp), error)) { + return FALSE; + } + for (guint i = 0; i < 9; i++) + self->serial[i] = GUINT32_FROM_LE(serial_tmp[i]); + + /* success */ + return TRUE; +} + +static gboolean +fu_ebitdo_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FwupdRequest) request = fwupd_request_new(); + + /* not required */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* generate a message if not already set from the metadata */ + if (fu_device_get_update_message(device) == NULL) { + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GString) msg = g_string_new(NULL); + g_string_append(msg, "Not in bootloader mode: Disconnect the controller, "); + switch (g_usb_device_get_pid(usb_device)) { + case 0xab11: /* FC30 */ + case 0xab12: /* NES30 */ + case 0xab21: /* SFC30 */ + case 0xab20: /* SNES30 */ + case 0x9012: /* SN30v2 */ + g_string_append(msg, + "hold down L+R+START for 3 seconds until " + "both LED lights flashing, "); + break; + case 0x9000: /* FC30PRO */ + case 0x9001: /* NES30PRO */ + g_string_append(msg, + "hold down RETURN+POWER for 3 seconds until " + "both LED lights flashing, "); + break; + case 0x1002: /* FC30-ARCADE */ + g_string_append(msg, + "hold down L1+R1+HOME for 3 seconds until " + "both blue LED and green LED blink, "); + break; + case 0x6000: /* SF30 pro: Dinput mode */ + case 0x6001: /* SN30 pro: Dinput mode */ + case 0x6002: /* 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; + case 0x2100: /* SN30 for Android */ + case 0x2101: /* SN30 for Android */ + g_string_append(msg, + "press and hold LB+RB+Xbox buttons " + "both white LED and green LED blink, "); + break; + case 0x9015: /* N30 Pro 2 */ + g_string_append(msg, + "press and hold L1+R1+START buttons " + "until the yellow LED blinks, "); + break; + default: + g_string_append(msg, "do what it says in the manual, "); + break; + } + g_string_append(msg, "then re-connect controller"); + fu_device_set_update_message(device, msg->str); + } + + /* wait */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* emit request */ + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_REMOVE_REPLUG); + fwupd_request_set_message(request, fu_device_get_update_message(device)); + fwupd_request_set_image(request, fu_device_get_update_image(device)); + fu_device_emit_request(device, request); + return TRUE; +} + +static gboolean +fu_ebitdo_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuEbitdoDevice *self = FU_EBITDO_DEVICE(device); + const guint8 *buf; + gsize bufsz = 0; + guint32 serial_new[3]; + g_autoptr(GBytes) fw_hdr = NULL; + g_autoptr(GBytes) fw_payload = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) chunks = NULL; + const guint32 app_key_index[16] = {0x186976e5, + 0xcac67acd, + 0x38f27fee, + 0x0a4948f1, + 0xb75b7753, + 0x1f8ffa5c, + 0xbff8cf43, + 0xc4936167, + 0x92bd03f0, + 0x5573c6ed, + 0x57d8845b, + 0x827197ac, + 0xb91901c9, + 0x3917edfe, + 0xbcd6344f, + 0xcf9e23b5}; + + /* not in bootloader mode */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NEEDS_USER_ACTION, + "Not in bootloader mode"); + return FALSE; + } + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "header"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 2, NULL); + + /* get header and payload */ + fw_hdr = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_HEADER, error); + if (fw_hdr == NULL) + return FALSE; + fw_payload = fu_firmware_get_bytes(firmware, error); + if (fw_payload == NULL) + return FALSE; + + /* set up the firmware header */ + buf = g_bytes_get_data(fw_hdr, &bufsz); + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA, + FU_EBITDO_PKT_CMD_FW_UPDATE_HEADER, + buf, + bufsz, + error)) { + g_prefix_error(error, "failed to set up firmware header: "); + return FALSE; + } + if (!fu_ebitdo_device_receive(self, NULL, 0, error)) { + g_prefix_error(error, "failed to get ACK for fw update header: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* flash the firmware in 32 byte blocks */ + chunks = fu_chunk_array_new_from_bytes(fw_payload, 0x0, 0x0, 32); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (g_getenv("FWUPD_EBITDO_VERBOSE") != NULL) { + g_debug("writing %u bytes to 0x%04x of 0x%04x", + fu_chunk_get_data_sz(chk), + fu_chunk_get_address(chk), + fu_chunk_get_data_sz(chk)); + } + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA, + FU_EBITDO_PKT_CMD_FW_UPDATE_DATA, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write firmware @0x%04x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + if (!fu_ebitdo_device_receive(self, NULL, 0, error)) { + g_prefix_error(error, + "failed to get ACK for write firmware @0x%04x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + chunks->len); + } + fu_progress_step_done(progress); + + /* set the "encode id" which is likely a checksum, bluetooth pairing + * or maybe just security-through-obscurity -- also note: + * SET_ENCODE_ID enforces no read for success?! */ + serial_new[0] = self->serial[0] ^ app_key_index[self->serial[0] & 0x0f]; + serial_new[1] = self->serial[1] ^ app_key_index[self->serial[1] & 0x0f]; + serial_new[2] = self->serial[2] ^ app_key_index[self->serial[2] & 0x0f]; + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA, + FU_EBITDO_PKT_CMD_FW_SET_ENCODE_ID, + (guint8 *)serial_new, + sizeof(serial_new), + error)) { + g_prefix_error(error, "failed to set encoding ID: "); + return FALSE; + } + + /* mark flash as successful */ + if (!fu_ebitdo_device_send(self, + FU_EBITDO_PKT_TYPE_USER_CMD, + FU_EBITDO_PKT_CMD_UPDATE_FIRMWARE_DATA, + FU_EBITDO_PKT_CMD_FW_UPDATE_OK, + NULL, + 0, + error)) { + g_prefix_error(error, "failed to mark firmware as successful: "); + return FALSE; + } + if (!fu_ebitdo_device_receive(self, NULL, 0, &error_local)) { + g_prefix_error(&error_local, "failed to get ACK for mark firmware as successful: "); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { + fu_device_set_remove_delay(device, 0); + g_debug("%s", error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static gboolean +fu_ebitdo_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + g_autoptr(GError) error_local = NULL; + + /* when doing a soft-reboot the device does not re-enumerate properly + * so manually reboot the GUsbDevice */ + if (!g_usb_device_reset(usb_device, &error_local)) { + g_prefix_error(&error_local, "failed to force-reset device: "); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { + fu_device_set_remove_delay(device, 0); + g_debug("%s", error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* not all 8bito devices come back in the right mode */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) + fu_device_set_remove_delay(device, 0); + else + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success! */ + return TRUE; +} + +static gboolean +fu_ebitdo_device_probe(FuDevice *device, GError **error) +{ + /* allowed, but requires manual bootloader step */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_remove_delay(device, FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + + /* set name and vendor */ + fu_device_set_summary(device, "A redesigned classic game controller"); + fu_device_set_vendor(device, "8BitDo"); + + /* add a hardcoded icon name */ + fu_device_add_icon(device, "input-gaming"); + + /* only the bootloader can do the update */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_add_counterpart_guid(device, "USB\\VID_0483&PID_5750"); + fu_device_add_counterpart_guid(device, "USB\\VID_2DC8&PID_5750"); + } + + /* success */ + return TRUE; +} + +static void +fu_ebitdo_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_ebitdo_device_init(FuEbitdoDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.8bitdo"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EBITDO_FIRMWARE); +} + +static void +fu_ebitdo_device_class_init(FuEbitdoDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_ebitdo_device_write_firmware; + klass_device->setup = fu_ebitdo_device_setup; + klass_device->detach = fu_ebitdo_device_detach; + klass_device->attach = fu_ebitdo_device_attach; + klass_device->open = fu_ebitdo_device_open; + klass_device->probe = fu_ebitdo_device_probe; + klass_device->set_progress = fu_ebitdo_set_progress; +} diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-device.h b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-device.h new file mode 100644 index 0000000000000000000000000000000000000000..1ff386038d6537c6e180f95fc89e758ecbda4612 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_EBITDO_DEVICE (fu_ebitdo_device_get_type()) +G_DECLARE_FINAL_TYPE(FuEbitdoDevice, fu_ebitdo_device, FU, EBITDO_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-firmware.c b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..a3a000e2d3309f05c9106b0804a42089517ee134 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-firmware.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ebitdo-firmware.h" + +struct _FuEbitdoFirmware { + FuFirmwareClass parent_instance; +}; + +G_DEFINE_TYPE(FuEbitdoFirmware, fu_ebitdo_firmware, FU_TYPE_FIRMWARE) + +/* little endian */ +typedef struct __attribute__((packed)) { + guint32 version; + guint32 destination_addr; + guint32 destination_len; + guint32 reserved[4]; +} FuEbitdoFirmwareHeader; + +static gboolean +fu_ebitdo_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuEbitdoFirmwareHeader *hdr; + guint32 payload_len; + g_autofree gchar *version = NULL; + g_autoptr(FuFirmware) img_hdr = fu_firmware_new(); + g_autoptr(GBytes) fw_hdr = NULL; + g_autoptr(GBytes) fw_payload = NULL; + + /* corrupt */ + if (g_bytes_get_size(fw) < sizeof(FuEbitdoFirmwareHeader)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware too small for header"); + return FALSE; + } + + /* check the file size */ + hdr = (FuEbitdoFirmwareHeader *)g_bytes_get_data(fw, NULL); + payload_len = (guint32)(g_bytes_get_size(fw) - sizeof(FuEbitdoFirmwareHeader)); + if (payload_len != GUINT32_FROM_LE(hdr->destination_len)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file size incorrect, expected 0x%04x got 0x%04x", + (guint)GUINT32_FROM_LE(hdr->destination_len), + (guint)payload_len); + return FALSE; + } + + /* check if this is firmware */ + for (guint i = 0; i < 4; i++) { + if (hdr->reserved[i] != 0x0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "data invalid, reserved[%u] = 0x%04x", + i, + hdr->reserved[i]); + return FALSE; + } + } + + /* parse version */ + version = g_strdup_printf("%.2f", GUINT32_FROM_LE(hdr->version) / 100.f); + fu_firmware_set_version(firmware, version); + fu_firmware_set_version_raw(firmware, GUINT32_FROM_LE(hdr->version)); + + /* add header */ + fw_hdr = fu_bytes_new_offset(fw, 0x0, sizeof(FuEbitdoFirmwareHeader), error); + if (fw_hdr == NULL) + return FALSE; + fu_firmware_set_id(img_hdr, FU_FIRMWARE_ID_HEADER); + fu_firmware_set_bytes(img_hdr, fw_hdr); + fu_firmware_add_image(firmware, img_hdr); + + /* add payload */ + fw_payload = fu_bytes_new_offset(fw, sizeof(FuEbitdoFirmwareHeader), payload_len, error); + if (fw_payload == NULL) + return FALSE; + fu_firmware_set_id(firmware, FU_FIRMWARE_ID_PAYLOAD); + fu_firmware_set_addr(firmware, GUINT32_FROM_LE(hdr->destination_addr)); + fu_firmware_set_bytes(firmware, fw_payload); + return TRUE; +} + +static GBytes * +fu_ebitdo_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + /* header then payload */ + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, fu_firmware_get_addr(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN); + for (guint i = 0; i < 4; i++) + fu_byte_array_append_uint32(buf, 0, G_LITTLE_ENDIAN); + fu_byte_array_append_bytes(buf, blob); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_ebitdo_firmware_init(FuEbitdoFirmware *self) +{ +} + +static void +fu_ebitdo_firmware_class_init(FuEbitdoFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_ebitdo_firmware_parse; + klass_firmware->write = fu_ebitdo_firmware_write; +} + +FuFirmware * +fu_ebitdo_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_EBITDO_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-firmware.h b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..6729d26f907d890868f068206c0d094661c9fdbb --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-firmware.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_EBITDO_FIRMWARE (fu_ebitdo_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuEbitdoFirmware, fu_ebitdo_firmware, FU, EBITDO_FIRMWARE, FuFirmware) + +FuFirmware * +fu_ebitdo_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-plugin.c b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..7e071ca12e5a04825c399a846f03528320f3bbfc --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ebitdo-device.h" +#include "fu-ebitdo-firmware.h" +#include "fu-ebitdo-plugin.h" + +struct _FuEbitdoPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuEbitdoPlugin, fu_ebitdo_plugin, FU_TYPE_PLUGIN) + +static void +fu_ebitdo_plugin_init(FuEbitdoPlugin *self) +{ +} + +static void +fu_ebitdo_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_EBITDO_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_EBITDO_FIRMWARE); +} + +static void +fu_ebitdo_plugin_class_init(FuEbitdoPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_ebitdo_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-plugin.h b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6c6244bbc5381169e43f29fc24380a6b7459be7d --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/fu-ebitdo-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuEbitdoPlugin, fu_ebitdo_plugin, FU, EBITDO_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/ebitdo/meson.build b/fwupd-1.8.6/plugins/ebitdo/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..de65a50a94ed56623c921d1a7512ace86065e2c9 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/meson.build @@ -0,0 +1,17 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginEbitdo"'] + +plugin_quirks += files('ebitdo.quirk') +plugin_builtins += static_library('fu_plugin_ebitdo', + sources: [ + 'fu-ebitdo-plugin.c', + 'fu-ebitdo-common.c', + 'fu-ebitdo-device.c', + 'fu-ebitdo-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/ebitdo/tests/ebitdo.bin b/fwupd-1.8.6/plugins/ebitdo/tests/ebitdo.bin new file mode 100644 index 0000000000000000000000000000000000000000..b29828eea2940f5757c5682004e56afe1977b710 Binary files /dev/null and b/fwupd-1.8.6/plugins/ebitdo/tests/ebitdo.bin differ diff --git a/fwupd-1.8.6/plugins/ebitdo/tests/ebitdo.builder.xml b/fwupd-1.8.6/plugins/ebitdo/tests/ebitdo.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..b941cdbd513733fe0a8190f67e82b26b3c3d0f79 --- /dev/null +++ b/fwupd-1.8.6/plugins/ebitdo/tests/ebitdo.builder.xml @@ -0,0 +1,5 @@ + + 0x10002 + 0x3000 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/elanfp/README.md b/fwupd-1.8.6/plugins/elanfp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f74613eab2f01a40020556925052ba2ec8fe6e3e --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/README.md @@ -0,0 +1,31 @@ +Elan Fingerprint Sensor Support +================================= + +Introduction +------------ + +The plugin used for update firmware for fingerprint sensors from Elan. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* tw.com.emc.elanfp + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_04F3&PID_0C7E&REV_0304` +* `USB\VID_04F3&PID_0C7E` +* `USB\VID_04F3` + +Vendor ID Security +------------------ + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x04F3` diff --git a/fwupd-1.8.6/plugins/elanfp/elanfp.quirk b/fwupd-1.8.6/plugins/elanfp/elanfp.quirk new file mode 100644 index 0000000000000000000000000000000000000000..f0be29d3c999a2ed95888af146e13ce16182fa1b --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/elanfp.quirk @@ -0,0 +1,7 @@ +[USB\VID_04F3&PID_0C7E] +Plugin = elanfp +[USB\VID_04F3&PID_0C82] +Plugin = elanfp +[USB\VID_04F3&PID_0C88] +Plugin = elanfp +Flags = usb-bulk-transfer diff --git a/fwupd-1.8.6/plugins/elanfp/fu-elanfp-device.c b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-device.c new file mode 100644 index 0000000000000000000000000000000000000000..f0b2c2c6fa6bd4d0a53250e8f063904b044ee66e --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-device.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2021 Michael Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-elanfp-device.h" +#include "fu-elanfp-firmware.h" + +#define ELAN_EP_CMD_OUT (0x01 | 0x00) +#define ELAN_EP_CMD_IN (0x02 | 0x80) +#define ELAN_EP_MOC_CMD_IN (0x04 | 0x80) +#define ELAN_EP_IMG_IN (0x03 | 0x80) +#define ELANFP_USB_INTERFACE 0 + +#define CTRL_SEND_TIMEOUT_MS 3000 +#define BULK_SEND_TIMEOUT_MS 3000 +#define BULK_RECV_TIMEOUT_MS 3000 + +#define REPORT_ID_FW_VERSION_FEATURE 0x20 +#define REPORT_ID_OFFER_COMMAND 0x25 +#define REPORT_ID_OFFER_RESPONSE 0x25 +#define REPORT_ID_PAYLOAD_COMMAND 0x20 +#define REPORT_ID_PAYLOAD_RESPONSE 0x22 + +#define REQTYPE_GET_VERSION 0xC1 +#define REQTYPE_COMMAND 0x41 + +#define TAG_SEND_COMMAND 0xFA + +/** + * FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER: + * + * Use usb bulk transfer. + */ +#define FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER (1 << 0) + +struct _FuElanfpDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE(FuElanfpDevice, fu_elanfp_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_elanfp_iap_send_command(FuElanfpDevice *self, + guint8 request_type, + guint8 request, + const guint8 *buf, + gsize bufsz, + gsize rspsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual = 0; + gsize sendsz = bufsz + 1; + guint8 start_index = 0x01; + guint8 buftmp[64] = {request, 0}; + + if (buf != NULL) { + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER)) { + buftmp[0] = TAG_SEND_COMMAND; + buftmp[1] = rspsz; + buftmp[2] = bufsz + 1; + buftmp[3] = request; + start_index = 0x04; + sendsz = bufsz + 1 + 3; + } + if (!fu_memcpy_safe(buftmp, + sizeof(buftmp), + start_index, /* dst */ + buf, + bufsz, + 0x0, /* src */ + bufsz, + error)) + return FALSE; + } + + if (fu_device_has_private_flag(FU_DEVICE(self), FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER)) { + if (!g_usb_device_bulk_transfer(usb_device, + ELAN_EP_CMD_OUT, + buftmp, + sendsz, + &actual, + BULK_SEND_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error(error, "failed to send command (bulk): "); + return FALSE; + } + } else { + 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_INTERFACE, + request, /* request */ + 0x00, /* value */ + 0x00, /* index */ + buftmp, + sendsz, + &actual, + CTRL_SEND_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error(error, "failed to send command (ctrl transfer): "); + return FALSE; + } + } + + if (actual != sendsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "send length (%u) is not match with the request (%u)", + (guint)actual, + (guint)bufsz + 1); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_elanfp_iap_recv_status(FuElanfpDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 endpoint = ELAN_EP_CMD_IN; + gsize actual = 0; + + if (fu_device_has_private_flag(FU_DEVICE(self), FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER)) { + endpoint = ELAN_EP_IMG_IN; + } + + if (!g_usb_device_bulk_transfer(usb_device, + endpoint, + buf, + bufsz, + &actual, + BULK_RECV_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error(error, "failed to receive status: "); + return FALSE; + } + if (actual != bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "received length (%u) is not match with the request (%u)", + (guint)actual, + (guint)bufsz); + + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_elanfp_device_do_xfer(FuElanfpDevice *self, + guint8 *outbuf, + gsize outlen, + guint8 *inbuf, + gsize inlen, + gboolean allow_less, + gsize *rxed_count, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual = 0; + + /* send data out */ + if (outbuf != NULL && outlen > 0) { + if (!g_usb_device_bulk_transfer(usb_device, + ELAN_EP_CMD_OUT, + outbuf, + outlen, + &actual, + BULK_SEND_TIMEOUT_MS, + NULL, + error)) { + return FALSE; + } + if (actual != outlen) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "only sent %" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT " bytes", + actual, + outlen); + return FALSE; + } + } + + /* read reply back */ + if (inbuf != NULL && inlen > 0) { + actual = 0; + if (!g_usb_device_bulk_transfer(usb_device, + ELAN_EP_IMG_IN, + inbuf, + inlen, + &actual, + BULK_RECV_TIMEOUT_MS, + NULL, + error)) { + return FALSE; + } + if (actual != inlen && !allow_less) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_PARTIAL_INPUT, + "only received %" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT " bytes", + actual, + outlen); + return FALSE; + } + } + + if (rxed_count != NULL) + *rxed_count = actual; + + return TRUE; +} + +static gboolean +fu_elanfp_device_setup(FuDevice *device, GError **error) +{ + FuElanfpDevice *self = FU_ELANFP_DEVICE(device); + guint16 fw_ver; + guint8 usb_buf[2] = {0x40, 0x19}; + g_autofree gchar *fw_ver_str = NULL; + + /* get version */ + if (!fu_elanfp_device_do_xfer(self, + (guint8 *)&usb_buf, + sizeof(usb_buf), + usb_buf, + sizeof(usb_buf), + TRUE, + NULL, + error)) { + g_prefix_error(error, "failed to device setup: "); + return FALSE; + } + fw_ver = fu_memread_uint16(usb_buf, G_BIG_ENDIAN); + fw_ver_str = g_strdup_printf("%04x", fw_ver); + fu_device_set_version(device, fw_ver_str); + + /* success */ + return TRUE; +} + +static gboolean +fu_elanfp_device_write_payload(FuElanfpDevice *self, + FuFirmware *payload, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + /* write each chunk */ + chunks = fu_firmware_get_chunks(payload, error); + if (chunks == NULL) + return FALSE; + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 databuf[60] = {0}; + guint8 recvbuf[17] = {0}; + + /* flags */ + if (i == 0) + databuf[0] = FU_CFU_DEVICE_FLAG_FIRST_BLOCK; + else if (i == chunks->len - 1) + databuf[0] = FU_CFU_DEVICE_FLAG_LAST_BLOCK; + + /* length */ + databuf[1] = fu_chunk_get_data_sz(chk); + + /* sequence number */ + if (!fu_memwrite_uint16_safe(databuf, + sizeof(databuf), + 0x2, + i + 1, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* address */ + if (!fu_memwrite_uint32_safe(databuf, + sizeof(databuf), + 0x4, + fu_chunk_get_address(chk), + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* data */ + if (!fu_memcpy_safe(databuf, + sizeof(databuf), + 0x8, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "memory copy for payload fail: "); + return FALSE; + } + if (!fu_elanfp_iap_send_command(self, + REQTYPE_COMMAND, + REPORT_ID_PAYLOAD_COMMAND, + databuf, + sizeof(databuf), + sizeof(recvbuf), + error)) { + g_prefix_error(error, "send payload command fail: "); + return FALSE; + } + if (!fu_elanfp_iap_recv_status(self, recvbuf, sizeof(recvbuf), error)) { + g_prefix_error(error, "received payload status fail: "); + return FALSE; + } + if (recvbuf[5] != FU_CFU_DEVICE_STATUS_SUCCESS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to send chunk %u: %s", + i + 1, + fu_cfu_device_status_to_string(recvbuf[5])); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elanfp_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuElanfpDevice *self = FU_ELANFP_DEVICE(device); + guint i; + struct { + const gchar *tag; + guint8 offer_idx; + guint8 payload_idx; + } items[] = { + {"A", FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A, FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A}, + {"B", FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B, FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B}, + {NULL, FU_ELANTP_FIRMWARE_IDX_END, FU_ELANTP_FIRMWARE_IDX_END}}; + g_autoptr(FuFirmware) payload = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "offer"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "payload"); + + /* send offers */ + for (i = 0; items[i].tag != NULL; i++) { + g_autoptr(GBytes) offer = NULL; + guint8 recvbuf[17] = {0}; + + offer = fu_firmware_get_image_by_idx_bytes(firmware, items[i].offer_idx, error); + if (offer == NULL) + return FALSE; + if (!fu_elanfp_iap_send_command(self, + REQTYPE_COMMAND, + REPORT_ID_OFFER_COMMAND, + g_bytes_get_data(offer, NULL), + g_bytes_get_size(offer), + g_bytes_get_size(offer) + 1, + error)) { + g_prefix_error(error, "send offer command fail: "); + return FALSE; + } + if (!fu_elanfp_iap_recv_status(self, recvbuf, sizeof(recvbuf), error)) { + g_prefix_error(error, "received offer status fail: "); + return FALSE; + } + g_debug("offer-%s status:%s reject:%s", + items[i].tag, + fu_cfu_device_offer_to_string(recvbuf[13]), + fu_cfu_device_reject_to_string(recvbuf[9])); + if (recvbuf[13] == FU_CFU_DEVICE_OFFER_ACCEPT) + break; + } + if (items[i].tag == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no CFU offer was accepted"); + return FALSE; + } + fu_progress_step_done(progress); + + /* send payload */ + payload = fu_firmware_get_image_by_idx(firmware, items[i].payload_idx, error); + if (payload == NULL) + return FALSE; + if (!fu_elanfp_device_write_payload(self, payload, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_elanfp_device_init(FuElanfpDevice *device) +{ + FuElanfpDevice *self = FU_ELANFP_DEVICE(device); + + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_remove_delay(FU_DEVICE(self), 5000); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elanfp"); + fu_device_set_name(FU_DEVICE(self), "Fingerprint Sensor"); + fu_device_set_summary(FU_DEVICE(self), "Match-On-Chip Fingerprint Sensor"); + fu_device_set_vendor(FU_DEVICE(self), "Elan"); + fu_device_set_install_duration(FU_DEVICE(self), 10); + fu_device_set_firmware_size_min(FU_DEVICE(self), 0x20000); + fu_device_set_firmware_size_max(FU_DEVICE(self), 0x90000); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ELANFP_FIRMWARE); + fu_usb_device_add_interface(FU_USB_DEVICE(self), ELANFP_USB_INTERFACE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER, + "usb-bulk-transfer"); +} + +static void +fu_elanfp_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_elanfp_device_class_init(FuElanfpDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_elanfp_device_setup; + klass_device->write_firmware = fu_elanfp_device_write_firmware; + klass_device->set_progress = fu_elanfp_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/elanfp/fu-elanfp-device.h b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-device.h new file mode 100644 index 0000000000000000000000000000000000000000..896e7e798fbbf8d081ed9330d6455fc2a52cfb37 --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Michael Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ELANFP_DEVICE (fu_elanfp_device_get_type()) +G_DECLARE_FINAL_TYPE(FuElanfpDevice, fu_elanfp_device, FU, ELANFP_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/elanfp/fu-elanfp-firmware.c b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..394458ee10983ad923a4da8fc8bc642910871ce8 --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-firmware.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 Michael Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-elanfp-firmware.h" + +struct _FuElanfpFirmware { + FuFirmwareClass parent_instance; + guint32 format_version; +}; + +G_DEFINE_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU_TYPE_FIRMWARE) + +#define FU_ELANFP_FIRMWARE_HEADER_MAGIC 0x46325354 + +static void +fu_elanfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "format_version", self->format_version); +} + +static gboolean +fu_elanfp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "format_version", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + self->format_version = tmp; + + /* success */ + return TRUE; +} + +static gboolean +fu_elanfp_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint32 magic = 0; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, + &magic, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != FU_ELANFP_FIRMWARE_HEADER_MAGIC) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic, expected 0x%04X got 0x%04X", + (guint32)FU_ELANFP_FIRMWARE_HEADER_MAGIC, + magic); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elanfp_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); + const guint8 *buf; + gsize bufsz; + guint img_cnt = 0; + + /* file format version */ + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x4, + &self->format_version, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* read indexes */ + offset += 0x10; + while (1) { + guint32 start_addr = 0; + guint32 length = 0; + guint32 fwtype = 0; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) img = NULL; + + /* check sanity */ + if (img_cnt++ > 256) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "too many images detected"); + return FALSE; + } + + /* type, reserved, start-addr, len */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x0, + &fwtype, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* check not already added */ + img = fu_firmware_get_image_by_idx(firmware, fwtype, NULL); + if (img != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "already parsed image with fwtype 0x%x", + fwtype); + return FALSE; + } + + /* done */ + if (fwtype == FU_ELANTP_FIRMWARE_IDX_END) + break; + switch (fwtype) { + case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A: + case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B: + img = fu_cfu_offer_new(); + break; + case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A: + case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B: + img = fu_cfu_payload_new(); + break; + default: + img = fu_firmware_new(); + break; + } + fu_firmware_set_idx(img, fwtype); + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x8, + &start_addr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_firmware_set_addr(img, start_addr); + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0xC, + &length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (length == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "zero size fwtype 0x%x not supported", + fwtype); + return FALSE; + } + blob = fu_bytes_new_offset(fw, start_addr, length, error); + if (blob == NULL) + return FALSE; + if (!fu_firmware_parse(img, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + fu_firmware_add_image(firmware, img); + + offset += 0x10; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_elanfp_firmware_write(FuFirmware *firmware, GError **error) +{ + FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); + gsize offset = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + /* S2F_HEADER */ + fu_byte_array_append_uint32(buf, 0x46325354, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, self->format_version, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* ICID, assumed */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ + + /* S2F_INDEX */ + offset += 0x10 + ((imgs->len + 1) * 0x10); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ + fu_byte_array_append_uint32(buf, offset, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN); + offset += g_bytes_get_size(blob); + } + + /* end of index */ + fu_byte_array_append_uint32(buf, FU_ELANTP_FIRMWARE_IDX_END, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */ + + /* data */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_elanfp_firmware_init(FuElanfpFirmware *self) +{ +} + +static void +fu_elanfp_firmware_class_init(FuElanfpFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_elanfp_firmware_check_magic; + klass_firmware->parse = fu_elanfp_firmware_parse; + klass_firmware->write = fu_elanfp_firmware_write; + klass_firmware->export = fu_elanfp_firmware_export; + klass_firmware->build = fu_elanfp_firmware_build; +} + +FuFirmware * +fu_elanfp_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ELANFP_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/elanfp/fu-elanfp-firmware.h b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..9ec5be576d0993ca736cc8f5f8b84ab80305a3f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-firmware.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Michael Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ELANFP_FIRMWARE (fu_elanfp_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU, ELANFP_FIRMWARE, FuFirmware) + +#define FU_ELANTP_FIRMWARE_IDX_FIRMWAREVERSION 0x00 +#define FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A 0x72 +#define FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B 0x73 +#define FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A 0x74 +#define FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B 0x75 +#define FU_ELANTP_FIRMWARE_IDX_END 0xFF + +FuFirmware * +fu_elanfp_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/elanfp/fu-elanfp-plugin.c b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..84c9f766aba97c5676f4a7022fc9e3f11f4693db --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-elanfp-device.h" +#include "fu-elanfp-firmware.h" +#include "fu-elanfp-plugin.h" + +struct _FuElanfpPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuElanfpPlugin, fu_elanfp_plugin, FU_TYPE_PLUGIN) + +static void +fu_elanfp_plugin_init(FuElanfpPlugin *self) +{ +} + +static void +fu_elanfp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ELANFP_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ELANFP_FIRMWARE); +} + +static void +fu_elanfp_plugin_class_init(FuElanfpPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_elanfp_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/elanfp/fu-elanfp-plugin.h b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..c7ca2f1f465ebc54243171717967b9332c964749 --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/fu-elanfp-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuElanfpPlugin, fu_elanfp_plugin, FU, ELANFP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/elanfp/meson.build b/fwupd-1.8.6/plugins/elanfp/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..55dc8b0a956e5cb1c21b8d71cbaa9df0676fd25f --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/meson.build @@ -0,0 +1,16 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginElanfp"'] + +plugin_quirks += files('elanfp.quirk') +plugin_builtins += static_library('fu_plugin_elanfp', + sources: [ + 'fu-elanfp-plugin.c', + 'fu-elanfp-device.c', + 'fu-elanfp-firmware.c' # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/elanfp/tests/elanfp.bin b/fwupd-1.8.6/plugins/elanfp/tests/elanfp.bin new file mode 100644 index 0000000000000000000000000000000000000000..470010c73fdde01aac8a0b37bcc36dc76e2b4829 Binary files /dev/null and b/fwupd-1.8.6/plugins/elanfp/tests/elanfp.bin differ diff --git a/fwupd-1.8.6/plugins/elanfp/tests/elanfp.builder.xml b/fwupd-1.8.6/plugins/elanfp/tests/elanfp.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..3fe127b2c64d97c32172813e6dad0ae4be65344d --- /dev/null +++ b/fwupd-1.8.6/plugins/elanfp/tests/elanfp.builder.xml @@ -0,0 +1,33 @@ + + 0x123 + + 0x72 + 0x1234 + 0x5678 + 0x1 + + + 0x73 + 0x1234 + 0x5678 + 0x2 + + + 0x74 + + + aGVsbG8gd29ybGQ= + 0x8001234 + + + + + 0x75 + + + aGVsbG8gd29ybGQ= + 0x8001234 + + + + diff --git a/fwupd-1.8.6/plugins/elantp/README.md b/fwupd-1.8.6/plugins/elantp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f92ff675eb528718ea7d1a5b689a701e0438376c --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/README.md @@ -0,0 +1,77 @@ +# Elan TouchPad + +## Introduction + +This plugin allows updating Touchpad devices from Elan. Devices are enumerated +using HID and raw I²C nodes. The I²C mode is used for ABS devices and firmware +recovery of HID devices. + +## 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: + +* tw.com.emc.elantp + +## GUID Generation + +These device uses the standard DeviceInstanceId values, e.g. + +* `HIDRAW\VEN_04F3&DEV_3010` + +Additionally another instance ID is added which corresponds to the module ID: + +* `HIDRAW\VEN_04F3&DEV_3010&MOD_1234` + +These devices also use custom GUID values for the IC configuration, e.g. + +* `ELANTP\ICTYPE_09` + + Additionally another instance ID is added which corresponds to the IC type & module ID: + +* `ELANTP\ICTYPE_09&MOD_1234` + + Additionally another instance ID is added which corresponds to the IC Type & module ID and Driver in order to distinguish HID/ABS devices: + +* `ELANTP\ICTYPE_09&MOD_1234&DRIVER_HID` -> HID Device +* `ELANTP\ICTYPE_09&MOD_1234&DRIVER_ELAN_I2C` -> ABS Device + +## Update Behavior + +The device usually presents in HID/ABS mode, and the firmware is written to the +device by switching to a IAP mode where the touchpad is nonfunctional. +Once complete the device is reset to get out of IAP mode and to load the new +firmware version. + +For HID devices, on flash failure the device is nonfunctional, but is recoverable +by writing to the i2c device. This is typically much slower than updating the +device using HID and also requires a model-specific HWID quirk to match. + +For ABS devices, on flash failure the device is nonfunctional, but it could be +recovered by the same i2c device. + +## Vendor ID Security + +The vendor ID is set from the HID vendor, for example set to `HIDRAW:0x17EF` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### ElantpIcPageCount + +The IC page count. + +Since: 1.4.6 + +### ElantpIapPassword + +The IAP password. + +Since: 1.4.6 + +## External Interface Access + +This plugin requires ioctl access to `HIDIOCSFEATURE` and `HIDIOCGFEATURE`. diff --git a/fwupd-1.8.6/plugins/elantp/elantp.quirk b/fwupd-1.8.6/plugins/elantp/elantp.quirk new file mode 100644 index 0000000000000000000000000000000000000000..aba8b1fd6689784624e086d5e4fbbac1338ce3ab --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/elantp.quirk @@ -0,0 +1,111 @@ +[HIDRAW\VEN_04F3] +Plugin = elantp +GType = FuElantpHidDevice + +# ThinkPad X1 Carbon Gen 9 +[6c87726f-b545-549e-840a-189422ea21d0] +Flags = elantp-recovery + +# Lenovo X1 Nano Gen1 +[4c20262a-aee0-5d6e-ba72-6d29b23fe350] +Flags = elantp-recovery + +# Lenovo ThinkPad X13 Yoga Gen 2 +[34874ca5-54f0-5a4f-9161-e03910d14b75] +Flags = elantp-recovery + +# Lenovo ThinkPad X13 Gen 2 - 20XHFVT007 +[e5319542-ca16-5d93-bd0f-2d25f547a846] +Flags = elantp-recovery + +# Lenovo ThinkPad X13 Gen 2 - 20XHFVT003 +[ea5ea62f-ec8d-5f5d-8231-0816c89d75d6] +Flags = elantp-recovery + +# Acer Aspire V3-372T +[513cde3d-d939-59bd-a634-5c1645ebb93b] +Flags = elantp-recovery + +# absolute report device on ACPI +[I2C\MODALIAS_acpi:ELAN0000:] +Plugin = elantp +GType = FuElantpI2cDevice +ElantpI2cTargetAddress = 0x15 +Flags = elantp-absolute + +# absolute report device on Device Tree +[I2C\MODALIAS_of:NtrackpadT(null)Celan,ekth3000] +Plugin = elantp +GType = FuElantpI2cDevice +ElantpI2cTargetAddress = 0x15 +Flags = elantp-absolute + +# recovery device +[I2C\NAME_Synopsys-DesignWare-I2C-adapter] +Plugin = elantp +GType = FuElantpI2cDevice +ElantpI2cTargetAddress = 0x15 + +[ELANTP\ICTYPE_00] +ElantpIcPageCount = 0x200 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_03] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_06] +ElantpIcPageCount = 0x200 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_07] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_08] +ElantpIcPageCount = 0x200 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_0A] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0xE15A + +[ELANTP\ICTYPE_0B] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_0C] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_0E] +ElantpIcPageCount = 0x280 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_09] +ElantpIcPageCount = 0x300 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_0D] +ElantpIcPageCount = 0x380 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_10] +ElantpIcPageCount = 0x400 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_11] +ElantpIcPageCount = 0x500 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_13] +ElantpIcPageCount = 0x800 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_14] +ElantpIcPageCount = 0x400 +ElantpIapPassword = 0x1EA5 + +[ELANTP\ICTYPE_15] +ElantpIcPageCount = 0x400 +ElantpIapPassword = 0x1EA5 diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-common.h b/fwupd-1.8.6/plugins/elantp/fu-elantp-common.h new file mode 100644 index 0000000000000000000000000000000000000000..7f63baa8e33484b17fd5ad45d422c3331fa52ecc --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-common.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define ETP_CMD_GET_HID_DESCRIPTOR 0x0001 +#define ETP_CMD_GET_HARDWARE_ID 0x0100 +#define ETP_CMD_GET_MODULE_ID 0x0101 +#define ETP_CMD_I2C_FW_CHECKSUM 0x030F +#define ETP_CMD_I2C_FW_VERSION 0x0102 +#define ETP_CMD_I2C_IAP 0x0311 +#define ETP_CMD_I2C_IAP_CHECKSUM 0x0315 +#define ETP_CMD_I2C_IAP_CTRL 0x0310 +#define ETP_CMD_I2C_IAP_ICBODY 0x0110 +#define ETP_CMD_I2C_IAP_RESET 0x0314 +#define ETP_CMD_I2C_IAP_VERSION 0x0111 +#define ETP_CMD_I2C_IAP_VERSION_2 0x0110 +#define ETP_CMD_I2C_OSM_VERSION 0x0103 +#define ETP_CMD_I2C_GET_HID_ID 0x0100 +#define ETP_CMD_I2C_IAP_TYPE 0x0304 + +#define ETP_I2C_IAP_TYPE_REG 0x0040 + +#define ETP_I2C_ENABLE_REPORT 0x0800 + +#define ETP_I2C_IAP_RESET 0xF0F0 +#define ETP_I2C_MAIN_MODE_ON (1 << 9) + +#define ETP_I2C_IAP_REG_L 0x01 +#define ETP_I2C_IAP_REG_H 0x06 + +#define ETP_FW_IAP_INTF_ERR (1 << 4) +#define ETP_FW_IAP_PAGE_ERR (1 << 5) +#define ETP_FW_IAP_CHECK_PW (1 << 7) +#define ETP_FW_IAP_LAST_FIT (1 << 9) + +#define ELANTP_DELAY_COMPLETE 1200 /* ms */ +#define ELANTP_DELAY_RESET 30 /* ms */ +#define ELANTP_DELAY_UNLOCK 100 /* ms */ +#define ELANTP_DELAY_WRITE_BLOCK 35 /* ms */ +#define ELANTP_DELAY_WRITE_BLOCK_512 50 /* ms */ diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-firmware.c b/fwupd-1.8.6/plugins/elantp/fu-elantp-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..784b084dba3f284ff194c5fb02ec48ea9e4d9e7c --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-firmware.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-elantp-common.h" +#include "fu-elantp-firmware.h" + +struct _FuElantpFirmware { + FuFirmwareClass parent_instance; + guint16 module_id; + guint16 iap_addr; +}; + +G_DEFINE_TYPE(FuElantpFirmware, fu_elantp_firmware, FU_TYPE_FIRMWARE) + +/* firmware block update */ +#define ETP_IAP_START_ADDR_WRDS 0x0083 + +const guint8 elantp_signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF}; + +guint16 +fu_elantp_firmware_get_module_id(FuElantpFirmware *self) +{ + g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0); + return self->module_id; +} + +guint16 +fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self) +{ + g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0); + return self->iap_addr; +} + +static void +fu_elantp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "iap_addr", self->iap_addr); + fu_xmlb_builder_insert_kx(bn, "module_id", self->module_id); +} + +static gboolean +fu_elantp_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + gsize bufsz = g_bytes_get_size(fw); + const guint8 *buf = g_bytes_get_data(fw, NULL); + + for (gsize i = 0; i < sizeof(elantp_signature); i++) { + guint8 tmp = 0x0; + if (!fu_memread_uint8_safe(buf, + bufsz, + bufsz - sizeof(elantp_signature) + i, + &tmp, + error)) + return FALSE; + if (tmp != elantp_signature[i]) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "signature[%u] invalid: got 0x%2x, expected 0x%02x", + (guint)i, + tmp, + elantp_signature[i]); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware); + gsize bufsz = 0; + guint16 iap_addr_wrds; + guint16 module_id_wrds; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* presumably in words */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + ETP_IAP_START_ADDR_WRDS * 2, + &iap_addr_wrds, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (iap_addr_wrds < ETP_IAP_START_ADDR_WRDS || iap_addr_wrds > 0x7FFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "IAP address invalid: 0x%x", + iap_addr_wrds); + return FALSE; + } + self->iap_addr = iap_addr_wrds * 2; + + /* read module ID */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + self->iap_addr, + &module_id_wrds, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (module_id_wrds > 0x7FFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "module ID address invalid: 0x%x", + module_id_wrds); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + module_id_wrds * 2, + &self->module_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware); + guint64 tmp; + + /* two simple properties */ + tmp = xb_node_query_text_as_uint(n, "module_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->module_id = tmp; + tmp = xb_node_query_text_as_uint(n, "iap_addr", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->iap_addr = tmp; + + /* success */ + return TRUE; +} + +static GBytes * +fu_elantp_firmware_write(FuFirmware *firmware, GError **error) +{ + FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + /* only one image supported */ + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return NULL; + + /* lets build a simple firmware like this: + * ------ 0x0 + * HEADER (containing IAP offset and module ID) + * ------ ~0x10a + * DATA + * ------ + * SIGNATURE + * ------ + */ + fu_byte_array_set_size(buf, self->iap_addr + 0x2 + 0x2, 0x00); + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + ETP_IAP_START_ADDR_WRDS * 2, + self->iap_addr / 2, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + self->iap_addr, + (self->iap_addr + 2) / 2, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + self->iap_addr + 0x2, + self->module_id, + G_LITTLE_ENDIAN, + error)) + return NULL; + fu_byte_array_append_bytes(buf, blob); + g_byte_array_append(buf, elantp_signature, sizeof(elantp_signature)); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_elantp_firmware_init(FuElantpFirmware *self) +{ +} + +static void +fu_elantp_firmware_class_init(FuElantpFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_elantp_firmware_check_magic; + klass_firmware->parse = fu_elantp_firmware_parse; + klass_firmware->build = fu_elantp_firmware_build; + klass_firmware->write = fu_elantp_firmware_write; + klass_firmware->export = fu_elantp_firmware_export; +} + +FuFirmware * +fu_elantp_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ELANTP_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-firmware.h b/fwupd-1.8.6/plugins/elantp/fu-elantp-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..ecc6fb36da325145624a368ae3f696d7729d4e74 --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ELANTP_FIRMWARE (fu_elantp_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuElantpFirmware, fu_elantp_firmware, FU, ELANTP_FIRMWARE, FuFirmware) + +FuFirmware * +fu_elantp_firmware_new(void); +guint16 +fu_elantp_firmware_get_module_id(FuElantpFirmware *self); +guint16 +fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self); diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-hid-device.c b/fwupd-1.8.6/plugins/elantp/fu-elantp-hid-device.c new file mode 100644 index 0000000000000000000000000000000000000000..55630e011ae7e88faad8dbd647efe36c3a6c1a30 --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-hid-device.c @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-elantp-common.h" +#include "fu-elantp-firmware.h" +#include "fu-elantp-hid-device.h" + +struct _FuElantpHidDevice { + FuUdevDevice parent_instance; + guint16 ic_page_count; + guint16 iap_type; + guint16 iap_ctrl; + guint16 iap_password; + guint16 module_id; + guint16 fw_page_size; + guint8 pattern; +}; + +G_DEFINE_TYPE(FuElantpHidDevice, fu_elantp_hid_device, FU_TYPE_UDEV_DEVICE) + +#define FU_ELANTP_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static gboolean +fu_elantp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error); + +static void +fu_elantp_hid_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + fu_string_append_kx(str, idt, "ModuleId", self->module_id); + fu_string_append_kx(str, idt, "Pattern", self->pattern); + fu_string_append_kx(str, idt, "FwPageSize", self->fw_page_size); + fu_string_append_kx(str, idt, "IcPageCount", self->ic_page_count); + fu_string_append_kx(str, idt, "IapType", self->iap_type); + fu_string_append_kx(str, idt, "IapCtrl", self->iap_ctrl); +} + +static gboolean +fu_elantp_hid_device_probe(FuDevice *device, GError **error) +{ + /* check is valid */ + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "hidraw") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct subsystem=%s, expected hidraw", + fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device))); + return FALSE; + } + + /* i2c-hid */ + if (fu_udev_device_get_model(FU_UDEV_DEVICE(device)) < 0x3000 || + fu_udev_device_get_model(FU_UDEV_DEVICE(device)) >= 0x4000) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not i2c-hid touchpad"); + return FALSE; + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static gboolean +fu_elantp_hid_device_send_cmd(FuElantpHidDevice *self, + guint8 *tx, + gsize txsz, + guint8 *rx, + gsize rxsz, + GError **error) +{ + g_autofree guint8 *buf = NULL; + gsize bufsz = rxsz + 3; + + if (g_getenv("FWUPD_ELANTP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SetReport", tx, txsz); + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCSFEATURE(txsz), + tx, + NULL, + FU_ELANTP_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + if (rxsz == 0) + return TRUE; + + /* GetFeature */ + buf = g_malloc0(bufsz); + buf[0] = tx[0]; /* report number */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGFEATURE(bufsz), + buf, + NULL, + FU_ELANTP_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + if (g_getenv("FWUPD_ELANTP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetReport", buf, bufsz); + + /* success */ + return fu_memcpy_safe(rx, + rxsz, + 0x0, /* dst */ + buf, + bufsz, + 0x3, /* src */ + rxsz, + error); +} + +static gboolean +fu_elantp_hid_device_read_cmd(FuElantpHidDevice *self, + guint16 reg, + guint8 *rx, + gsize rxsz, + GError **error) +{ + guint8 buf[5] = {0x0d, 0x05, 0x03}; + fu_memwrite_uint16(buf + 0x3, reg, G_LITTLE_ENDIAN); + return fu_elantp_hid_device_send_cmd(self, buf, sizeof(buf), rx, rxsz, error); +} + +static gint +fu_elantp_hid_device_write_cmd(FuElantpHidDevice *self, guint16 reg, guint16 cmd, GError **error) +{ + guint8 buf[5] = {0x0d}; + fu_memwrite_uint16(buf + 0x1, reg, G_LITTLE_ENDIAN); + fu_memwrite_uint16(buf + 0x3, cmd, G_LITTLE_ENDIAN); + return fu_elantp_hid_device_send_cmd(self, buf, sizeof(buf), NULL, 0, error); +} + +static gboolean +fu_elantp_hid_device_ensure_iap_ctrl(FuElantpHidDevice *self, GError **error) +{ + guint8 buf[2] = {0x0}; + if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read IAPControl: "); + return FALSE; + } + self->iap_ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + + /* in bootloader mode? */ + if ((self->iap_ctrl & ETP_I2C_MAIN_MODE_ON) == 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); + + return TRUE; +} + +static gboolean +fu_elantp_hid_device_setup(FuDevice *device, GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + FuUdevDevice *udev_device = FU_UDEV_DEVICE(device); + guint16 fwver; + guint16 iap_ver; + guint16 tmp; + guint8 buf[2] = {0x0}; + guint8 ic_type; + g_autofree gchar *version_bl = NULL; + g_autofree gchar *version = NULL; + + /* get pattern */ + if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read HID ID: "); + return FALSE; + } + tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + self->pattern = tmp != 0xffff ? (tmp & 0xff00) >> 8 : 0; + + /* get current firmware version */ + if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_FW_VERSION, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read fw version: "); + return FALSE; + } + fwver = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + if (fwver == 0xFFFF || fwver == ETP_CMD_I2C_FW_VERSION) + fwver = 0; + version = fu_version_from_uint16(fwver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version(device, version); + + /* get IAP firmware version */ + if (!fu_elantp_hid_device_read_cmd(self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION + : ETP_CMD_I2C_IAP_VERSION_2, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + iap_ver = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + } + version_bl = fu_version_from_uint16(iap_ver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version_bootloader(device, version_bl); + + /* get module ID */ + if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_GET_MODULE_ID, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read module ID: "); + return FALSE; + } + self->module_id = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + + /* define the extra instance IDs */ + fu_device_add_instance_u16(device, "VEN", fu_udev_device_get_vendor(udev_device)); + fu_device_add_instance_u16(device, "DEV", fu_udev_device_get_model(udev_device)); + fu_device_add_instance_u16(device, "MOD", self->module_id); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "MOD", NULL)) + return FALSE; + + /* get OSM version */ + if (!fu_elantp_hid_device_read_cmd(self, + ETP_CMD_I2C_OSM_VERSION, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read OSM version: "); + return FALSE; + } + tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_hid_device_read_cmd(self, + ETP_CMD_I2C_IAP_ICBODY, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read IC body: "); + return FALSE; + } + ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + + /* define the extra instance IDs (ic_type + module_id + driver) */ + fu_device_add_instance_u8(device, "ICTYPE", ic_type); + fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", NULL); + fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", "MOD", NULL); + fu_device_add_instance_str(device, "DRIVER", "HID"); + fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", "MOD", "DRIVER", NULL); + + /* no quirk entry */ + if (self->ic_page_count == 0x0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no page count for ELANTP\\ICTYPE_%02X", + ic_type); + return FALSE; + } + + /* The ic_page_count is based on 64 bytes/page. */ + fu_device_set_firmware_size(device, (guint64)self->ic_page_count * (guint64)64); + + /* is in bootloader mode */ + if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_elantp_hid_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + guint16 module_id; + g_autoptr(FuFirmware) firmware = fu_elantp_firmware_new(); + + /* check is compatible with hardware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + module_id = fu_elantp_firmware_get_module_id(FU_ELANTP_FIRMWARE(firmware)); + if (self->module_id != module_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got 0x%04x, expected 0x%04x", + module_id, + self->module_id); + return NULL; + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_elantp_hid_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + FuElantpFirmware *firmware_elantp = FU_ELANTP_FIRMWARE(firmware); + gsize bufsz = 0; + guint16 checksum = 0; + guint16 checksum_device = 0; + guint16 iap_addr; + const guint8 *buf; + guint8 csum_buf[2] = {0x0}; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 30, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, "reset"); + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* detach */ + if (!fu_elantp_hid_device_detach(device, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write each block */ + buf = g_bytes_get_data(fw, &bufsz); + iap_addr = fu_elantp_firmware_get_iap_addr(firmware_elantp); + chunks = fu_chunk_array_new(buf + iap_addr, bufsz - iap_addr, 0x0, 0x0, self->fw_page_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint16 csum_tmp = + fu_sum16w(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); + gsize blksz = self->fw_page_size + 3; + g_autofree guint8 *blk = g_malloc0(blksz); + + /* write block */ + blk[0] = 0x0B; /* report ID */ + if (!fu_memcpy_safe(blk, + blksz, + 0x1, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_memwrite_uint16(blk + fu_chunk_get_data_sz(chk) + 1, csum_tmp, G_LITTLE_ENDIAN); + + if (!fu_elantp_hid_device_send_cmd(self, blk, blksz, NULL, 0, error)) + return FALSE; + g_usleep(self->fw_page_size == 512 ? ELANTP_DELAY_WRITE_BLOCK_512 * 1000 + : ELANTP_DELAY_WRITE_BLOCK * 1000); + + if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) + return FALSE; + if (self->iap_ctrl & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "bootloader reports failed write: 0x%x", + self->iap_ctrl); + return FALSE; + } + + /* update progress */ + checksum += csum_tmp; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* verify the written checksum */ + if (!fu_elantp_hid_device_read_cmd(self, + ETP_CMD_I2C_IAP_CHECKSUM, + csum_buf, + sizeof(csum_buf), + error)) + return FALSE; + if (!fu_memread_uint16_safe(csum_buf, + sizeof(csum_buf), + 0x0, + &checksum_device, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (checksum != checksum_device) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "checksum failed 0x%04x != 0x%04x", + checksum, + checksum_device); + return FALSE; + } + fu_progress_step_done(progress); + + /* wait for a reset */ + fu_progress_sleep(fu_progress_get_child(progress), ELANTP_DELAY_COMPLETE); + fu_progress_step_done(progress); + return TRUE; +} + +static gboolean +fu_elantp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + guint16 iap_ver; + guint16 ic_type; + guint8 buf[2] = {0x0}; + guint16 tmp; + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("in bootloader mode, reset IC"); + if (!fu_elantp_hid_device_write_cmd(self, + ETP_CMD_I2C_IAP_RESET, + ETP_I2C_IAP_RESET, + error)) + return FALSE; + g_usleep(ELANTP_DELAY_RESET * 1000); + } + + /* get OSM version */ + if (!fu_elantp_hid_device_read_cmd(self, + ETP_CMD_I2C_OSM_VERSION, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read OSM version: "); + return FALSE; + } + tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_hid_device_read_cmd(self, + ETP_CMD_I2C_IAP_ICBODY, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read IC body: "); + return FALSE; + } + ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + + /* get IAP firmware version */ + if (!fu_elantp_hid_device_read_cmd(self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION + : ETP_CMD_I2C_IAP_VERSION_2, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + iap_ver = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + } + + /* set the page size */ + self->fw_page_size = 64; + if (ic_type >= 0x10) { + if (iap_ver >= 1) { + /* set the IAP type, presumably some kind of ABI */ + if (iap_ver >= 2 && (ic_type == 0x14 || ic_type == 0x15)) { + self->fw_page_size = 512; + } else { + self->fw_page_size = 128; + } + + if (!fu_elantp_hid_device_write_cmd(self, + ETP_CMD_I2C_IAP_TYPE, + self->fw_page_size / 2, + error)) + return FALSE; + if (!fu_elantp_hid_device_read_cmd(self, + ETP_CMD_I2C_IAP_TYPE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read IAP type: "); + return FALSE; + } + self->iap_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN); + if (self->iap_type != self->fw_page_size / 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to set IAP type"); + return FALSE; + } + } + } + if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_IAP, self->iap_password, error)) + return FALSE; + g_usleep(ELANTP_DELAY_UNLOCK * 1000); + if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) + return FALSE; + if ((self->iap_ctrl & ETP_FW_IAP_CHECK_PW) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "unexpected bootloader password"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_hid_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* reset back to runtime */ + if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) + return FALSE; + g_usleep(ELANTP_DELAY_RESET * 1000); + if (!fu_elantp_hid_device_write_cmd(self, + ETP_CMD_I2C_IAP_RESET, + ETP_I2C_ENABLE_REPORT, + error)) { + g_prefix_error(error, "cannot enable TP report: "); + return FALSE; + } + if (!fu_elantp_hid_device_write_cmd(self, 0x0306, 0x003, error)) { + g_prefix_error(error, "cannot switch to TP PTP mode: "); + return FALSE; + } + if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_hid_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "ElantpIcPageCount") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->ic_page_count = (guint16)tmp; + return TRUE; + } + if (g_strcmp0(key, "ElantpIapPassword") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->iap_password = (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_elantp_hid_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_elantp_hid_device_init(FuElantpHidDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_summary(FU_DEVICE(self), "Touchpad"); + fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); + fu_device_set_priority(FU_DEVICE(self), 1); /* better than i2c */ + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE | + FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK); +} + +static void +fu_elantp_hid_device_finalize(GObject *object) +{ + G_OBJECT_CLASS(fu_elantp_hid_device_parent_class)->finalize(object); +} + +static void +fu_elantp_hid_device_class_init(FuElantpHidDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_elantp_hid_device_finalize; + klass_device->to_string = fu_elantp_hid_device_to_string; + klass_device->attach = fu_elantp_hid_device_attach; + klass_device->set_quirk_kv = fu_elantp_hid_device_set_quirk_kv; + klass_device->setup = fu_elantp_hid_device_setup; + klass_device->reload = fu_elantp_hid_device_setup; + klass_device->write_firmware = fu_elantp_hid_device_write_firmware; + klass_device->prepare_firmware = fu_elantp_hid_device_prepare_firmware; + klass_device->probe = fu_elantp_hid_device_probe; + klass_device->set_progress = fu_elantp_hid_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-hid-device.h b/fwupd-1.8.6/plugins/elantp/fu-elantp-hid-device.h new file mode 100644 index 0000000000000000000000000000000000000000..436a7094ac56fcc5c2c625940fe77c3b835f842e --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-hid-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ELANTP_HID_DEVICE (fu_elantp_hid_device_get_type()) +G_DECLARE_FINAL_TYPE(FuElantpHidDevice, fu_elantp_hid_device, FU, ELANTP_HID_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-i2c-device.c b/fwupd-1.8.6/plugins/elantp/fu-elantp-i2c-device.c new file mode 100644 index 0000000000000000000000000000000000000000..04015cbd4818b803806edcea869b3d071ad9b356 --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-i2c-device.c @@ -0,0 +1,842 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-elantp-common.h" +#include "fu-elantp-firmware.h" +#include "fu-elantp-i2c-device.h" + +struct _FuElantpI2cDevice { + FuUdevDevice parent_instance; + guint16 i2c_addr; + guint16 ic_page_count; + guint16 iap_type; + guint16 iap_ctrl; + guint16 iap_password; + guint16 module_id; + guint16 fw_page_size; + guint8 pattern; + gchar *bind_path; + gchar *bind_id; +}; + +G_DEFINE_TYPE(FuElantpI2cDevice, fu_elantp_i2c_device, FU_TYPE_UDEV_DEVICE) + +#define FU_ELANTP_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static gboolean +fu_elantp_i2c_device_detach(FuDevice *device, FuProgress *progress, GError **error); + +static void +fu_elantp_i2c_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + fu_string_append_kx(str, idt, "I2cAddr", self->i2c_addr); + fu_string_append_kx(str, idt, "ModuleId", self->module_id); + fu_string_append_kx(str, idt, "Pattern", self->pattern); + fu_string_append_kx(str, idt, "FwPageSize", self->fw_page_size); + fu_string_append_kx(str, idt, "IcPageCount", self->ic_page_count); + fu_string_append_kx(str, idt, "IapType", self->iap_type); + fu_string_append_kx(str, idt, "IapCtrl", self->iap_ctrl); + fu_string_append(str, idt, "BindPath", self->bind_path); + fu_string_append(str, idt, "BindId", self->bind_id); +} + +static gboolean +fu_elantp_i2c_device_writeln(const gchar *fn, const gchar *buf, GError **error) +{ + int fd; + g_autoptr(FuIOChannel) io = NULL; + + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "%s does not exist", fn); + return FALSE; + } + + fd = open(fn, O_WRONLY); + if (fd < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "could not open %s", + fn); + return FALSE; + } + + io = fu_io_channel_unix_new(fd); + return fu_io_channel_write_raw(io, + (const guint8 *)buf, + strlen(buf), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error); +} + +static gboolean +fu_elantp_i2c_device_rebind_driver(FuElantpI2cDevice *self, GError **error) +{ + if (self->bind_path == NULL || self->bind_id == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no Path or ID for rebind driver"); + return FALSE; + } + + if (!fu_elantp_i2c_device_writeln(g_build_filename(self->bind_path, "unbind", NULL), + self->bind_id, + error)) + return FALSE; + if (!fu_elantp_i2c_device_writeln(g_build_filename(self->bind_path, "bind", NULL), + self->bind_id, + error)) + return FALSE; + + g_debug("rebind driver of %s", self->bind_id); + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_probe(FuDevice *device, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + + /* check is valid */ + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "i2c") == 0) { + g_autoptr(GPtrArray) i2c_buses = NULL; + FuUdevDevice *i2c_device = + fu_udev_device_get_parent_with_subsystem(FU_UDEV_DEVICE(device), "i2c"); + if (i2c_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "did not find the i2c parent for device"); + return FALSE; + } + + i2c_buses = fu_udev_device_get_children_with_subsystem(i2c_device, "i2c-dev"); + if (i2c_buses->len == 1) { + FuUdevDevice *bus_device = g_object_ref(g_ptr_array_index(i2c_buses, 0)); + if (bus_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "did not find the i2c-dev children for device"); + return FALSE; + } + + g_debug("Found I2C bus at %s, using this device", + fu_udev_device_get_sysfs_path(bus_device)); + self->bind_path = + g_build_filename("/sys/bus/i2c/drivers", + fu_udev_device_get_driver(FU_UDEV_DEVICE(device)), + NULL); + self->bind_id = g_path_get_basename( + fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device))); + fu_udev_device_set_dev(FU_UDEV_DEVICE(device), + fu_udev_device_get_dev(bus_device)); + } + } + + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "i2c-dev") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct subsystem=%s, expected i2c-dev", + fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device))); + return FALSE; + } + if (fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no device file"); + return FALSE; + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "i2c", error); +} + +static gboolean +fu_elantp_i2c_device_send_cmd(FuElantpI2cDevice *self, + guint8 *tx, + gssize txsz, + guint8 *rx, + gssize rxsz, + GError **error) +{ + if (g_getenv("FWUPD_ELANTP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Write", tx, txsz); + if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(self), 0, tx, txsz, error)) + return FALSE; + if (rxsz == 0) + return TRUE; + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), 0, rx, rxsz, error)) + return FALSE; + if (g_getenv("FWUPD_ELANTP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Read", rx, rxsz); + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_write_cmd(FuElantpI2cDevice *self, guint16 reg, guint16 cmd, GError **error) +{ + guint8 buf[4]; + fu_memwrite_uint16(buf + 0x0, reg, G_LITTLE_ENDIAN); + fu_memwrite_uint16(buf + 0x2, cmd, G_LITTLE_ENDIAN); + return fu_elantp_i2c_device_send_cmd(self, buf, sizeof(buf), NULL, 0, error); +} + +static gboolean +fu_elantp_i2c_device_read_cmd(FuElantpI2cDevice *self, + guint16 reg, + guint8 *rx, + gsize rxsz, + GError **error) +{ + guint8 buf[2]; + fu_memwrite_uint16(buf + 0x0, reg, G_LITTLE_ENDIAN); + return fu_elantp_i2c_device_send_cmd(self, buf, sizeof(buf), rx, rxsz, error); +} + +static gboolean +fu_elantp_i2c_device_ensure_iap_ctrl(FuElantpI2cDevice *self, GError **error) +{ + guint8 buf[2] = {0x0}; + if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read IAPControl: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &self->iap_ctrl, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* in bootloader mode? */ + if ((self->iap_ctrl & ETP_I2C_MAIN_MODE_ON) == 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); + + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_setup(FuDevice *device, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + guint16 fwver; + guint16 iap_ver; + guint16 tmp; + guint16 pid; + guint16 vid; + guint8 buf[30] = {0x0}; + guint8 ic_type; + g_autofree gchar *version_bl = NULL; + g_autofree gchar *version = NULL; + + /* read the I2C descriptor */ + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_GET_HID_DESCRIPTOR, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to get HID descriptor: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 20, &vid, G_LITTLE_ENDIAN, error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, sizeof(buf), 22, &pid, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* set the vendor ID */ + if (vid != 0x0000) { + g_autofree gchar *vendor_id = NULL; + vendor_id = g_strdup_printf("HIDRAW:0x%04X", vid); + fu_device_add_vendor_id(device, vendor_id); + } + + /* add GUIDs in order of priority */ + fu_device_add_instance_u16(device, "VID", vid); + fu_device_add_instance_u16(device, "PID", pid); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VID", "PID", NULL)) + return FALSE; + + /* get pattern */ + if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read I2C ID: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) + return FALSE; + self->pattern = tmp != 0xffff ? (tmp & 0xff00) >> 8 : 0; + + /* get current firmware version */ + if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_I2C_FW_VERSION, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read fw version: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &fwver, G_LITTLE_ENDIAN, error)) + return FALSE; + if (fwver == 0xFFFF || fwver == ETP_CMD_I2C_FW_VERSION) + fwver = 0; + version = fu_version_from_uint16(fwver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version(device, version); + + /* get IAP firmware version */ + if (!fu_elantp_i2c_device_read_cmd(self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION + : ETP_CMD_I2C_IAP_VERSION_2, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x0, + &iap_ver, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + version_bl = fu_version_from_uint16(iap_ver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version_bootloader(device, version_bl); + + /* get module ID */ + if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_GET_MODULE_ID, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read module ID: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x0, + &self->module_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* define the extra instance IDs */ + fu_device_add_instance_u16(device, "VEN", vid); + fu_device_add_instance_u16(device, "DEV", pid); + fu_device_add_instance_u16(device, "MOD", self->module_id); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "MOD", NULL)) + return FALSE; + + /* get OSM version */ + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_I2C_OSM_VERSION, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read OSM version: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) + return FALSE; + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_I2C_IAP_ICBODY, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read IC body: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) + return FALSE; + ic_type = tmp & 0xFF; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + + /* define the extra instance IDs (ic_type + module_id + driver) */ + fu_device_add_instance_u8(device, "ICTYPE", ic_type); + fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", NULL); + fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", "MOD", NULL); + if (fu_device_has_private_flag(device, FU_ELANTP_I2C_DEVICE_ABSOLUTE)) { + fu_device_add_instance_str(device, "DRIVER", "ELAN_I2C"); + } else { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_instance_str(device, "DRIVER", "HID"); + } + fu_device_build_instance_id(device, NULL, "ELANTP", "ICTYPE", "MOD", "DRIVER", NULL); + + /* no quirk entry */ + if (self->ic_page_count == 0x0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no page count for ELANTP\\ICTYPE_%02X", + ic_type); + return FALSE; + } + fu_device_set_firmware_size(device, (guint64)self->ic_page_count * (guint64)64); + + /* is in bootloader mode */ + if (!fu_elantp_i2c_device_ensure_iap_ctrl(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_open(FuDevice *device, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + gint addr = self->i2c_addr; + guint8 tx_buf[] = {0x02, 0x01}; + + /* FuUdevDevice->open */ + if (!FU_DEVICE_CLASS(fu_elantp_i2c_device_parent_class)->open(device, error)) + return FALSE; + + /* set target address */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(device), + I2C_SLAVE, + GINT_TO_POINTER(addr), + NULL, + FU_ELANTP_DEVICE_IOCTL_TIMEOUT, + NULL)) { + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(device), + I2C_SLAVE_FORCE, + GINT_TO_POINTER(addr), + NULL, + FU_ELANTP_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, + "failed to set target address to 0x%x: ", + self->i2c_addr); + return FALSE; + } + } + + /* read i2c device */ + return fu_udev_device_pwrite(FU_UDEV_DEVICE(device), 0x0, tx_buf, sizeof(tx_buf), error); +} + +static FuFirmware * +fu_elantp_i2c_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + guint16 module_id; + g_autoptr(FuFirmware) firmware = fu_elantp_firmware_new(); + + /* check is compatible with hardware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + module_id = fu_elantp_firmware_get_module_id(FU_ELANTP_FIRMWARE(firmware)); + if (self->module_id != module_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got 0x%04x, expected 0x%04x", + module_id, + self->module_id); + return NULL; + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_elantp_i2c_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + FuElantpFirmware *firmware_elantp = FU_ELANTP_FIRMWARE(firmware); + gsize bufsz = 0; + guint16 checksum = 0; + guint16 checksum_device = 0; + guint16 iap_addr; + const guint8 *buf; + guint8 csum_buf[2] = {0x0}; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, NULL); + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* detach */ + if (!fu_elantp_i2c_device_detach(device, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write each block */ + buf = g_bytes_get_data(fw, &bufsz); + iap_addr = fu_elantp_firmware_get_iap_addr(firmware_elantp); + chunks = fu_chunk_array_new(buf + iap_addr, bufsz - iap_addr, 0x0, 0x0, self->fw_page_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint16 csum_tmp = + fu_sum16w(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); + gsize blksz = self->fw_page_size + 4; + g_autofree guint8 *blk = g_malloc0(blksz); + + /* write block */ + blk[0] = ETP_I2C_IAP_REG_L; + blk[1] = ETP_I2C_IAP_REG_H; + if (!fu_memcpy_safe(blk, + blksz, + 0x2, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + fu_memwrite_uint16(blk + fu_chunk_get_data_sz(chk) + 2, csum_tmp, G_LITTLE_ENDIAN); + + if (!fu_elantp_i2c_device_send_cmd(self, blk, blksz, NULL, 0, error)) + return FALSE; + g_usleep(self->fw_page_size == 512 ? ELANTP_DELAY_WRITE_BLOCK_512 * 1000 + : ELANTP_DELAY_WRITE_BLOCK * 1000); + + if (!fu_elantp_i2c_device_ensure_iap_ctrl(self, error)) + return FALSE; + if (self->iap_ctrl & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "bootloader reports failed write: 0x%x", + self->iap_ctrl); + return FALSE; + } + + /* update progress */ + checksum += csum_tmp; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* verify the written checksum */ + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_I2C_IAP_CHECKSUM, + csum_buf, + sizeof(csum_buf), + error)) + return FALSE; + if (!fu_memread_uint16_safe(csum_buf, + sizeof(csum_buf), + 0x0, + &checksum_device, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (checksum != checksum_device) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "checksum failed 0x%04x != 0x%04x", + checksum, + checksum_device); + return FALSE; + } + fu_progress_step_done(progress); + + /* wait for a reset */ + fu_progress_sleep(fu_progress_get_child(progress), ELANTP_DELAY_COMPLETE); + fu_progress_step_done(progress); + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint16 iap_ver; + guint16 ic_type; + guint8 buf[2] = {0x0}; + guint16 tmp; + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("in bootloader mode, reset IC"); + if (!fu_elantp_i2c_device_write_cmd(self, + ETP_CMD_I2C_IAP_RESET, + ETP_I2C_IAP_RESET, + error)) + return FALSE; + g_usleep(ELANTP_DELAY_RESET * 1000); + } + /* get OSM version */ + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_I2C_OSM_VERSION, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read OSM version: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) + return FALSE; + if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_I2C_IAP_ICBODY, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read IC body: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x0, + &ic_type, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } else { + ic_type = (tmp >> 8) & 0xFF; + } + + /* get IAP firmware version */ + if (!fu_elantp_i2c_device_read_cmd(self, + self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION + : ETP_CMD_I2C_IAP_VERSION_2, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read bootloader version: "); + return FALSE; + } + if (self->pattern >= 1) { + iap_ver = buf[1]; + } else { + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x0, + &iap_ver, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* set the page size */ + self->fw_page_size = 64; + if (ic_type >= 0x10) { + if (iap_ver >= 1) { + if (iap_ver >= 2 && (ic_type == 0x14 || ic_type == 0x15)) { + self->fw_page_size = 512; + } else { + self->fw_page_size = 128; + } + /* set the IAP type, presumably some kind of ABI */ + if (!fu_elantp_i2c_device_write_cmd(self, + ETP_CMD_I2C_IAP_TYPE, + self->fw_page_size / 2, + error)) + return FALSE; + if (!fu_elantp_i2c_device_read_cmd(self, + ETP_CMD_I2C_IAP_TYPE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read IAP type: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x0, + &self->iap_type, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (self->iap_type != self->fw_page_size / 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to set IAP type"); + return FALSE; + } + } + } + if (!fu_elantp_i2c_device_write_cmd(self, ETP_CMD_I2C_IAP, self->iap_password, error)) + return FALSE; + g_usleep(ELANTP_DELAY_UNLOCK * 1000); + if (!fu_elantp_i2c_device_ensure_iap_ctrl(self, error)) + return FALSE; + if ((self->iap_ctrl & ETP_FW_IAP_CHECK_PW) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "unexpected bootloader password"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* reset back to runtime */ + if (!fu_elantp_i2c_device_write_cmd(self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) + return FALSE; + g_usleep(ELANTP_DELAY_RESET * 1000); + if (!fu_elantp_i2c_device_write_cmd(self, + ETP_CMD_I2C_IAP_RESET, + ETP_I2C_ENABLE_REPORT, + error)) { + g_prefix_error(error, "cannot enable TP report: "); + return FALSE; + } + + if (!fu_elantp_i2c_device_ensure_iap_ctrl(self, error)) + return FALSE; + + if (fu_device_has_private_flag(device, FU_ELANTP_I2C_DEVICE_ABSOLUTE)) { + g_autoptr(GError) error_local = NULL; + + if (!fu_elantp_i2c_device_write_cmd(self, 0x0300, 0x001, error)) { + g_prefix_error(error, "cannot switch to TP ABS mode: "); + return FALSE; + } + + if (!fu_elantp_i2c_device_rebind_driver(self, &error_local)) { + if (g_error_matches(error_local, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED)) { + g_debug("%s", error_local->message); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + } else { + if (!fu_elantp_i2c_device_write_cmd(self, 0x0306, 0x003, error)) { + g_prefix_error(error, "cannot switch to TP PTP mode: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_elantp_i2c_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "ElantpIcPageCount") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->ic_page_count = (guint16)tmp; + return TRUE; + } + if (g_strcmp0(key, "ElantpIapPassword") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->iap_password = (guint16)tmp; + return TRUE; + } + if (g_strcmp0(key, "ElantpI2cTargetAddress") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->i2c_addr = (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_elantp_i2c_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_elantp_i2c_device_init(FuElantpI2cDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_summary(FU_DEVICE(self), "Touchpad (I²C)"); + fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_ELANTP_I2C_DEVICE_ABSOLUTE, + "elantp-absolute"); +} + +static void +fu_elantp_i2c_device_finalize(GObject *object) +{ + FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(object); + g_free(self->bind_path); + g_free(self->bind_id); + G_OBJECT_CLASS(fu_elantp_i2c_device_parent_class)->finalize(object); +} + +static void +fu_elantp_i2c_device_class_init(FuElantpI2cDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_elantp_i2c_device_finalize; + klass_device->to_string = fu_elantp_i2c_device_to_string; + klass_device->attach = fu_elantp_i2c_device_attach; + klass_device->set_quirk_kv = fu_elantp_i2c_device_set_quirk_kv; + klass_device->setup = fu_elantp_i2c_device_setup; + klass_device->reload = fu_elantp_i2c_device_setup; + klass_device->write_firmware = fu_elantp_i2c_device_write_firmware; + klass_device->prepare_firmware = fu_elantp_i2c_device_prepare_firmware; + klass_device->probe = fu_elantp_i2c_device_probe; + klass_device->open = fu_elantp_i2c_device_open; + klass_device->set_progress = fu_elantp_i2c_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-i2c-device.h b/fwupd-1.8.6/plugins/elantp/fu-elantp-i2c-device.h new file mode 100644 index 0000000000000000000000000000000000000000..fc6e19da3dc7d1d785cbf88f67fdd84484d9af41 --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-i2c-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_ELANTP_I2C_DEVICE_ABSOLUTE (1 << 0) + +#define FU_TYPE_ELANTP_I2C_DEVICE (fu_elantp_i2c_device_get_type()) +G_DECLARE_FINAL_TYPE(FuElantpI2cDevice, fu_elantp_i2c_device, FU, ELANTP_I2C_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-plugin.c b/fwupd-1.8.6/plugins/elantp/fu-elantp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..d5397d8a4f9297ca64d49c6df002d54c8b669e15 --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-plugin.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-elantp-firmware.h" +#include "fu-elantp-hid-device.h" +#include "fu-elantp-i2c-device.h" +#include "fu-elantp-plugin.h" + +struct _FuElantpPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuElantpPlugin, fu_elantp_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_elantp_plugin_device_created(FuPlugin *plugin, FuDevice *dev, GError **error) +{ + if (fu_device_get_specialized_gtype(dev) == FU_TYPE_ELANTP_I2C_DEVICE && + !fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "elantp-recovery") && + !fu_device_has_private_flag(dev, FU_ELANTP_I2C_DEVICE_ABSOLUTE)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not required"); + return FALSE; + } + return TRUE; +} + +static void +fu_elantp_plugin_init(FuElantpPlugin *self) +{ +} + +static void +fu_elantp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "ElantpI2cTargetAddress"); + fu_context_add_quirk_key(ctx, "ElantpIapPassword"); + fu_context_add_quirk_key(ctx, "ElantpIcPageCount"); + fu_plugin_add_udev_subsystem(plugin, "i2c"); + fu_plugin_add_udev_subsystem(plugin, "i2c-dev"); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ELANTP_FIRMWARE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ELANTP_I2C_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ELANTP_HID_DEVICE); +} + +static void +fu_elantp_plugin_class_init(FuElantpPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_elantp_plugin_constructed; + plugin_class->device_created = fu_elantp_plugin_device_created; +} diff --git a/fwupd-1.8.6/plugins/elantp/fu-elantp-plugin.h b/fwupd-1.8.6/plugins/elantp/fu-elantp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..dbfca9c29041af682f0a7d6f11d3bb1c0ebdd584 --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-elantp-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuElantpPlugin, fu_elantp_plugin, FU, ELANTP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/elantp/fu-self-test.c b/fwupd-1.8.6/plugins/elantp/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..04fb7c8999ab8beac8aec33a26edce725a318e4e --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/fu-self-test.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-elantp-firmware.h" + +static void +fu_elantp_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_elantp_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_elantp_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "elantp.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + 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("/elantp/firmware{xml}", fu_elantp_firmware_xml_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/elantp/meson.build b/fwupd-1.8.6/plugins/elantp/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..446f0247b7ee1c1453dde19d323549652135272d --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/meson.build @@ -0,0 +1,45 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginElantp"'] + +plugin_quirks += files('elantp.quirk') +plugin_builtin_elantp = static_library('fu_plugin_elantp', + sources: [ + 'fu-elantp-plugin.c', + 'fu-elantp-firmware.c', # fuzzing + 'fu-elantp-hid-device.c', + 'fu-elantp-i2c-device.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_elantp + +if get_option('tests') + install_data(['tests/elantp.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'elantp-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_elantp, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('elantp-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/elantp/tests/elantp.bin b/fwupd-1.8.6/plugins/elantp/tests/elantp.bin new file mode 100644 index 0000000000000000000000000000000000000000..19393791d12e1dcc84e8637ea069afd4ff77e11c Binary files /dev/null and b/fwupd-1.8.6/plugins/elantp/tests/elantp.bin differ diff --git a/fwupd-1.8.6/plugins/elantp/tests/elantp.builder.xml b/fwupd-1.8.6/plugins/elantp/tests/elantp.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..1ce3cede38a6be1f00dcee73377056bdd3b6606a --- /dev/null +++ b/fwupd-1.8.6/plugins/elantp/tests/elantp.builder.xml @@ -0,0 +1,5 @@ + + 0xe00 + 0x2 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/emmc/README.md b/fwupd-1.8.6/plugins/emmc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6f511b23dffb52f51edc3222e2541236cb5fe22b --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/README.md @@ -0,0 +1,34 @@ +# eMMC + +## Introduction + +This plugin reads the sysfs attributes corresponding to eMMC devices. +It uses the kernel MMC API for flashing devices. + +## Protocol + +eMMC devices support the `org.jedec.mmc` protocol. + +## GUID Generation + +These devices use the following instance values: + +* `EMMC\%NAME%` +* `EMMC\%NAME%&%REV%` +* `EMMC\%MANFID%&%OEMID%` +* `EMMC\%MANFID%&%OEMID%&%NAME%` +* `EMMC\%MANFID%&%NAME%&%REV%` +* `EMMC\%MANFID%&%OEMID%&%NAME%&%REV%` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, but it is +only activated when the device is rebooted. + +## Vendor ID Security + +The vendor ID is set from the EMMC vendor, for example set to `EMMC:{$manfid}` + +## External Interface Access + +This plugin requires ioctl `MMC_IOC_CMD` and `MMC_IOC_MULTI_CMD` access. diff --git a/fwupd-1.8.6/plugins/emmc/emmc.quirk b/fwupd-1.8.6/plugins/emmc/emmc.quirk new file mode 100644 index 0000000000000000000000000000000000000000..2d8fd38e663b89ba82231fe448eba722437bae2a --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/emmc.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[BLOCK] +Plugin = emmc diff --git a/fwupd-1.8.6/plugins/emmc/fu-emmc-device.c b/fwupd-1.8.6/plugins/emmc/fu-emmc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..59da03ac8e8779634acaa8efbdf421f09982e1cb --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/fu-emmc-device.c @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-emmc-device.h" + +/* From kernel linux/major.h */ +#define MMC_BLOCK_MAJOR 179 + +/* From kernel linux/mmc/mmc.h */ +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ + +/* From kernel linux/mmc/core.h */ +#define MMC_RSP_PRESENT (1 << 0) +#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ +#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */ +#define MMC_CMD_AC (0 << 5) +#define MMC_CMD_ADTC (1 << 5) +#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ +#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1) +#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1 | MMC_RSP_SPI_BUSY) +#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) + +/* EXT_CSD fields */ +#define EXT_CSD_SUPPORTED_MODES 493 /* RO */ +#define EXT_CSD_FFU_FEATURES 492 /* RO */ +#define EXT_CSD_FFU_ARG_3 490 /* RO */ +#define EXT_CSD_FFU_ARG_2 489 /* RO */ +#define EXT_CSD_FFU_ARG_1 488 /* RO */ +#define EXT_CSD_FFU_ARG_0 487 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_3 305 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_2 304 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_1 303 /* RO */ +#define EXT_CSD_NUM_OF_FW_SEC_PROG_0 302 /* RO */ +#define EXT_CSD_REV 192 +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_MODE_CONFIG 30 +#define EXT_CSD_MODE_OPERATION_CODES 29 /* W */ +#define EXT_CSD_FFU_STATUS 26 /* R */ +#define EXT_CSD_REV_V5_1 8 +#define EXT_CSD_REV_V5_0 7 + +/* EXT_CSD field definitions */ +#define EXT_CSD_NORMAL_MODE (0x00) +#define EXT_CSD_FFU_MODE (0x01) +#define EXT_CSD_FFU_INSTALL (0x01) +#define EXT_CSD_FFU (1 << 0) +#define EXT_CSD_UPDATE_DISABLE (1 << 0) +#define EXT_CSD_CMD_SET_NORMAL (1 << 0) + +#define FU_EMMC_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +struct _FuEmmcDevice { + FuUdevDevice parent_instance; + guint32 sect_size; +}; + +G_DEFINE_TYPE(FuEmmcDevice, fu_emmc_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_emmc_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuEmmcDevice *self = FU_EMMC_DEVICE(device); + FU_DEVICE_CLASS(fu_emmc_device_parent_class)->to_string(device, idt, str); + fu_string_append_ku(str, idt, "SectorSize", self->sect_size); +} + +static const gchar * +fu_emmc_device_get_manufacturer(guint64 mmc_id) +{ + switch (mmc_id) { + case 0x00: + case 0x44: + return "SanDisk"; + case 0x02: + return "Kingston/Sandisk"; + case 0x03: + case 0x11: + return "Toshiba"; + case 0x13: + return "Micron"; + case 0x15: + return "Samsung/Sandisk/LG"; + case 0x37: + return "Kingmax"; + case 0x70: + case 0x2c: + return "Kingston"; + default: + return NULL; + } + return NULL; +} + +static gboolean +fu_emmc_device_get_sysattr_guint64(GUdevDevice *device, + const gchar *name, + guint64 *val_out, + GError **error) +{ + const gchar *sysfs; + + sysfs = g_udev_device_get_sysfs_attr(device, name); + if (sysfs == NULL) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed get %s", name); + return FALSE; + } + + *val_out = g_ascii_strtoull(sysfs, NULL, 16); + + return TRUE; +} + +static gboolean +fu_emmc_device_probe(FuDevice *device, GError **error) +{ + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + guint64 flag; + guint64 oemid = 0; + guint64 manfid = 0; + const gchar *tmp; + g_autoptr(GUdevDevice) udev_parent = NULL; + g_autofree gchar *vendor_id = NULL; + g_autoptr(GRegex) dev_regex = NULL; + + udev_parent = g_udev_device_get_parent_with_subsystem(udev_device, "mmc", NULL); + if (udev_parent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no MMC parent"); + return FALSE; + } + + /* look for only the parent node */ + if (g_strcmp0(g_udev_device_get_devtype(udev_device), "disk") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct devtype=%s, expected disk", + g_udev_device_get_devtype(udev_device)); + return FALSE; + } + + /* ignore *rpmb and *boot* mmc block devices */ + dev_regex = g_regex_new("mmcblk\\d$", 0, 0, NULL); + tmp = g_udev_device_get_name(udev_device); + if (tmp == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device has no name"); + return FALSE; + } + if (!g_regex_match(dev_regex, tmp, 0, NULL)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not raw mmc block device, devname=%s", + g_udev_device_get_name(udev_device)); + return FALSE; + } + + /* doesn't support FFU */ + if (!fu_emmc_device_get_sysattr_guint64(udev_parent, "ffu_capable", &flag, error)) + return FALSE; + if (flag == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s does not support field firmware updates", + fu_device_get_name(device)); + return FALSE; + } + + /* name */ + tmp = g_udev_device_get_sysfs_attr(udev_parent, "name"); + if (tmp == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s does not have 'name' sysattr", + fu_device_get_name(device)); + return FALSE; + } + fu_device_add_instance_strsafe(device, "NAME", tmp); + fu_device_build_instance_id(device, NULL, "EMMC", "NAME", NULL); + fu_device_set_name(device, tmp); + + /* firmware version */ + tmp = g_udev_device_get_sysfs_attr(udev_parent, "fwrev"); + if (tmp != NULL) { + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_version(device, tmp); + } + fu_device_add_instance_strsafe(device, "REV", tmp); + fu_device_build_instance_id(device, NULL, "EMMC", "NAME", "REV", NULL); + + /* manfid + oemid, manfid + oemid + name */ + if (!fu_emmc_device_get_sysattr_guint64(udev_parent, "manfid", &manfid, error)) + return FALSE; + if (!fu_emmc_device_get_sysattr_guint64(udev_parent, "oemid", &oemid, error)) + return FALSE; + fu_device_add_instance_u16(device, "MAN", manfid); + fu_device_add_instance_u16(device, "OEM", oemid); + fu_device_build_instance_id(device, NULL, "EMMC", "MAN", "OEM", NULL); + fu_device_build_instance_id(device, NULL, "EMMC", "MAN", "OEM", "NAME", NULL); + fu_device_build_instance_id(device, NULL, "EMMC", "MAN", "NAME", "REV", NULL); + fu_device_build_instance_id(device, NULL, "EMMC", "MAN", "OEM", "NAME", "REV", NULL); + + /* set the vendor */ + tmp = g_udev_device_get_sysfs_attr(udev_parent, "manfid"); + vendor_id = g_strdup_printf("EMMC:%s", tmp); + fu_device_add_vendor_id(device, vendor_id); + fu_device_set_vendor(device, fu_emmc_device_get_manufacturer(manfid)); + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "mmc", error)) + return FALSE; + + /* internal */ + if (!fu_emmc_device_get_sysattr_guint64(udev_device, "removable", &flag, error)) + return FALSE; + if (flag == 0) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + + return TRUE; +} + +static gboolean +fu_emmc_read_extcsd(FuEmmcDevice *self, guint8 *ext_csd, gsize ext_csd_sz, GError **error) +{ + struct mmc_ioc_cmd idata = { + .write_flag = 0, + .opcode = MMC_SEND_EXT_CSD, + .arg = 0, + .flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, + .blksz = 512, + .blocks = 1, + }; + mmc_ioc_cmd_set_data(idata, ext_csd); + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + MMC_IOC_CMD, + (guint8 *)&idata, + NULL, + FU_EMMC_DEVICE_IOCTL_TIMEOUT, + error); +} + +static gboolean +fu_emmc_validate_extcsd(FuDevice *device, GError **error) +{ + FuEmmcDevice *self = FU_EMMC_DEVICE(device); + guint8 ext_csd[512] = {0x0}; + + if (!fu_emmc_read_extcsd(FU_EMMC_DEVICE(device), ext_csd, sizeof(ext_csd), error)) + return FALSE; + if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "FFU is only available on devices >= " + "MMC 5.0, not supported in %s", + fu_device_get_name(device)); + return FALSE; + } + if ((ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "FFU is not supported in %s", + fu_device_get_name(device)); + return FALSE; + } + if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware update was disabled in %s", + fu_device_get_name(device)); + return FALSE; + } + self->sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096; + + return TRUE; +} + +static gboolean +fu_emmc_device_setup(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_validate = NULL; + if (!fu_emmc_validate_extcsd(device, &error_validate)) + g_debug("%s", error_validate->message); + else + fu_device_add_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_UPDATABLE); + + return TRUE; +} + +static FuFirmware * +fu_emmc_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuEmmcDevice *self = FU_EMMC_DEVICE(device); + gsize fw_size = g_bytes_get_size(fw); + + /* check alignment */ + if ((fw_size % self->sect_size) > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware data size (%" G_GSIZE_FORMAT ") is not aligned", + fw_size); + return NULL; + } + + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_emmc_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuEmmcDevice *self = FU_EMMC_DEVICE(device); + gsize fw_size = 0; + gsize total_done; + guint32 arg; + guint32 sect_done = 0; + guint8 ext_csd[512]; + guint failure_cnt = 0; + g_autofree struct mmc_ioc_multi_cmd *multi_cmd = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "ffu"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 45, NULL); + + if (!fu_emmc_read_extcsd(FU_EMMC_DEVICE(device), ext_csd, sizeof(ext_csd), error)) + return FALSE; + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + fw_size = g_bytes_get_size(fw); + + /* set CMD ARG */ + arg = ext_csd[EXT_CSD_FFU_ARG_0] | ext_csd[EXT_CSD_FFU_ARG_1] << 8 | + ext_csd[EXT_CSD_FFU_ARG_2] << 16 | ext_csd[EXT_CSD_FFU_ARG_3] << 24; + + /* prepare multi_cmd to be sent */ + multi_cmd = g_malloc0(sizeof(struct mmc_ioc_multi_cmd) + 3 * sizeof(struct mmc_ioc_cmd)); + multi_cmd->num_of_cmds = 3; + + /* put device into ffu mode */ + multi_cmd->cmds[0].opcode = MMC_SWITCH; + multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_FFU_MODE << 8) | EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + multi_cmd->cmds[0].write_flag = 1; + + /* send image chunk */ + multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK; + multi_cmd->cmds[1].blksz = self->sect_size; + multi_cmd->cmds[1].blocks = 1; + multi_cmd->cmds[1].arg = arg; + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + multi_cmd->cmds[1].write_flag = 1; + + /* return device into normal mode */ + multi_cmd->cmds[2].opcode = MMC_SWITCH; + multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (EXT_CSD_MODE_CONFIG << 16) | + (EXT_CSD_NORMAL_MODE << 8) | EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + multi_cmd->cmds[2].write_flag = 1; + fu_progress_step_done(progress); + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + self->sect_size); + while (sect_done == 0) { + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + mmc_ioc_cmd_set_data(multi_cmd->cmds[1], fu_chunk_get_data(chk)); + + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + MMC_IOC_MULTI_CMD, + (guint8 *)multi_cmd, + NULL, + FU_EMMC_DEVICE_IOCTL_TIMEOUT, + error)) { + g_autoptr(GError) error_local = NULL; + g_prefix_error(error, "multi-cmd failed: "); + /* multi-cmd ioctl failed before exiting from ffu mode */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + MMC_IOC_CMD, + (guint8 *)&multi_cmd->cmds[2], + NULL, + FU_EMMC_DEVICE_IOCTL_TIMEOUT, + &error_local)) { + g_prefix_error(error, "%s: ", error_local->message); + } + return FALSE; + } + + if (!fu_emmc_read_extcsd(self, ext_csd, sizeof(ext_csd), error)) + return FALSE; + + /* if we need to restart the download */ + sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] | + ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 | + ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 | + ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24; + if (sect_done == 0) { + if (failure_cnt >= 3) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "programming failed"); + return FALSE; + } + failure_cnt++; + g_debug("programming failed: retrying (%u)", failure_cnt); + break; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + } + fu_progress_step_done(progress); + + /* sanity check */ + total_done = (gsize)sect_done * (gsize)self->sect_size; + if (total_done != fw_size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "firmware size and number of sectors written " + "mismatch (%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT "):", + total_done, + fw_size); + return FALSE; + } + + /* check mode operation for ffu install*/ + if (!ext_csd[EXT_CSD_FFU_FEATURES]) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } else { + /* re-enter ffu mode and install the firmware */ + multi_cmd->num_of_cmds = 2; + + /* set ext_csd to install mode */ + multi_cmd->cmds[1].opcode = MMC_SWITCH; + multi_cmd->cmds[1].blksz = 0; + multi_cmd->cmds[1].blocks = 0; + multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_MODE_OPERATION_CODES << 16) | + (EXT_CSD_FFU_INSTALL << 8) | EXT_CSD_CMD_SET_NORMAL; + multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + multi_cmd->cmds[1].write_flag = 1; + + /* send ioctl with multi-cmd */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + MMC_IOC_MULTI_CMD, + (guint8 *)multi_cmd, + NULL, + FU_EMMC_DEVICE_IOCTL_TIMEOUT, + error)) { + g_autoptr(GError) error_local = NULL; + /* In case multi-cmd ioctl failed before exiting from ffu mode */ + g_prefix_error(error, "multi-cmd failed setting install mode: "); + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + MMC_IOC_CMD, + (guint8 *)&multi_cmd->cmds[2], + NULL, + FU_EMMC_DEVICE_IOCTL_TIMEOUT, + &error_local)) { + g_prefix_error(error, "%s: ", error_local->message); + } + return FALSE; + } + + /* return status */ + if (!fu_emmc_read_extcsd(self, ext_csd, sizeof(ext_csd), error)) + return FALSE; + if (ext_csd[EXT_CSD_FFU_STATUS] != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "FFU install failed: %d", + ext_csd[EXT_CSD_FFU_STATUS]); + return FALSE; + } + } + fu_progress_step_done(progress); + + return TRUE; +} + +static void +fu_emmc_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_emmc_device_init(FuEmmcDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "org.jedec.mmc"); + fu_device_add_icon(FU_DEVICE(self), "media-memory"); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); +} + +static void +fu_emmc_device_finalize(GObject *object) +{ + G_OBJECT_CLASS(fu_emmc_device_parent_class)->finalize(object); +} + +static void +fu_emmc_device_class_init(FuEmmcDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_emmc_device_finalize; + klass_device->setup = fu_emmc_device_setup; + klass_device->to_string = fu_emmc_device_to_string; + klass_device->prepare_firmware = fu_emmc_device_prepare_firmware; + klass_device->probe = fu_emmc_device_probe; + klass_device->write_firmware = fu_emmc_device_write_firmware; + klass_device->set_progress = fu_emmc_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/emmc/fu-emmc-device.h b/fwupd-1.8.6/plugins/emmc/fu-emmc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..042c19171a6cfadcc7146fe254e2c30a49ce7aec --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/fu-emmc-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_EMMC_DEVICE (fu_emmc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuEmmcDevice, fu_emmc_device, FU, EMMC_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/emmc/fu-emmc-plugin.c b/fwupd-1.8.6/plugins/emmc/fu-emmc-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..51f4f2c1abf598702ac78d6ec833ae83e0c6afc1 --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/fu-emmc-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-emmc-device.h" +#include "fu-emmc-plugin.h" + +struct _FuEmmcPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuEmmcPlugin, fu_emmc_plugin, FU_TYPE_PLUGIN) + +static void +fu_emmc_plugin_init(FuEmmcPlugin *self) +{ +} + +static void +fu_emmc_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "block"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_EMMC_DEVICE); +} + +static void +fu_emmc_plugin_class_init(FuEmmcPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_emmc_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/emmc/fu-emmc-plugin.h b/fwupd-1.8.6/plugins/emmc/fu-emmc-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..e306f9d7d41d943bd11da8f58a7c9d6f18c7ae76 --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/fu-emmc-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuEmmcPlugin, fu_emmc_plugin, FU, EMMC_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/emmc/meson.build b/fwupd-1.8.6/plugins/emmc/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..6555182b9fc1d9deac60268747cfff1cb337e8df --- /dev/null +++ b/fwupd-1.8.6/plugins/emmc/meson.build @@ -0,0 +1,17 @@ +if get_option('plugin_emmc').require(gudev.found(), + error_message: 'gudev is needed for plugin_emmc').allowed() + +cargs = ['-DG_LOG_DOMAIN="FuPluginEmmc"'] + +plugin_quirks += files('emmc.quirk') +plugin_builtins += static_library('fu_plugin_emmc', + sources: [ + 'fu-emmc-plugin.c', + 'fu-emmc-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/ep963x/README.md b/fwupd-1.8.6/plugins/ep963x/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f54c2fcfe6cc700d162fcbcd34ef6834456500d1 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/README.md @@ -0,0 +1,36 @@ +# Explore EP963x + +## Introduction + +The EP963x is a generic MCU used in many different products. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* tw.com.exploretech.ep963x + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_17EF&PID_7226&REV_0001` +* `USB\VID_17EF&PID_7226` +* `USB\VID_17EF` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with +the same USB PID in an unlocked mode. On attach the device again re-enumerates +back to the runtime locked mode. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x17EF` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/ep963x/ep963x.quirk b/fwupd-1.8.6/plugins/ep963x/ep963x.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b1f89fa63c9dacf7cf95521aa53cecafc728ff38 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/ep963x.quirk @@ -0,0 +1,2 @@ +[USB\VID_17EF&PID_7226] +Plugin = ep963x diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-common.c b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-common.c new file mode 100644 index 0000000000000000000000000000000000000000..5158ccbe83061e2338e9f3a4e86495f27fb1c980 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-common.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ep963x-common.h" + +const gchar * +fu_ep963x_smbus_strerror(guint8 val) +{ + if (val == FU_EP963_SMBUS_ERROR_NONE) + return "none"; + if (val == FU_EP963_SMBUS_ERROR_ADDRESS) + return "address"; + if (val == FU_EP963_SMBUS_ERROR_NO_ACK) + return "no-ack"; + if (val == FU_EP963_SMBUS_ERROR_ARBITRATION) + return "arbitration"; + if (val == FU_EP963_SMBUS_ERROR_COMMAND) + return "command"; + if (val == FU_EP963_SMBUS_ERROR_TIMEOUT) + return "timeout"; + if (val == FU_EP963_SMBUS_ERROR_BUSY) + return "busy"; + return "unknown"; +} diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-common.h b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-common.h new file mode 100644 index 0000000000000000000000000000000000000000..625312428cb689a727680940c0979d4d561edb23 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-common.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_EP963_FIRMWARE_SIZE 0x1f000 + +#define FU_EP963_TRANSFER_BLOCK_SIZE 0x200 /* 512 */ +#define FU_EP963_TRANSFER_CHUNK_SIZE 0x04 +#define FU_EP963_FEATURE_ID1_SIZE 0x08 + +#define FU_EP963_USB_CONTROL_ID 0x01 + +#define FU_EP963_ICP_ENTER 0x40 +#define FU_EP963_ICP_EXIT 0x82 +#define FU_EP963_ICP_BANK 0x83 +#define FU_EP963_ICP_ADDRESS 0x84 +#define FU_EP963_ICP_READBLOCK 0x85 +#define FU_EP963_ICP_WRITEBLOCK 0x86 +#define FU_EP963_ICP_MCUID 0x87 +#define FU_EP963_ICP_DONE 0x5A + +#define FU_EP963_OPCODE_SMBUS_READ 0x01 +#define FU_EP963_OPCODE_ERASE_SPI 0x02 +#define FU_EP963_OPCODE_RESET_BLOCK_INDEX 0x03 +#define FU_EP963_OPCODE_WRITE_BLOCK_DATA 0x04 +#define FU_EP963_OPCODE_PROGRAM_SPI_BLOCK 0x05 +#define FU_EP963_OPCODE_PROGRAM_SPI_FINISH 0x06 +#define FU_EP963_OPCODE_GET_SPI_CHECKSUM 0x07 +#define FU_EP963_OPCODE_PROGRAM_EP_FLASH 0x08 +#define FU_EP963_OPCODE_GET_EP_CHECKSUM 0x09 +#define FU_EP963_OPCODE_START_THROW_PAGE 0x0B +#define FU_EP963_OPCODE_GET_EP_SITE_TYPE 0x0C +#define FU_EP963_OPCODE_COMMAND_VERSION 0x10 +#define FU_EP963_OPCODE_COMMAND_STATUS 0x20 +#define FU_EP963_OPCODE_SUBMCU_ENTER_ICP 0x30 +#define FU_EP963_OPCODE_SUBMCU_RESET_BLOCK_IDX 0x31 +#define FU_EP963_OPCODE_SUBMCU_WRITE_BLOCK_DATA 0x32 +#define FU_EP963_OPCODE_SUBMCU_PROGRAM_BLOCK 0x33 +#define FU_EP963_OPCODE_SUBMCU_PROGRAM_FINISHED 0x34 + +#define FU_EP963_UF_CMD_VERSION 0x00 +#define FU_EP963_UF_CMD_ENTERISP 0x01 +#define FU_EP963_UF_CMD_PROGRAM 0x02 +#define FU_EP963_UF_CMD_READ 0x03 +#define FU_EP963_UF_CMD_MODE 0x04 + +/* byte 0x02 */ +#define FU_EP963_USB_STATE_READY 0x00 +#define FU_EP963_USB_STATE_BUSY 0x01 +#define FU_EP963_USB_STATE_FAIL 0x02 +#define FU_EP963_USB_STATE_UNKNOWN 0xff + +/* byte 0x07 */ +#define FU_EP963_SMBUS_ERROR_NONE 0x00 +#define FU_EP963_SMBUS_ERROR_ADDRESS 0x01 +#define FU_EP963_SMBUS_ERROR_NO_ACK 0x02 +#define FU_EP963_SMBUS_ERROR_ARBITRATION 0x04 +#define FU_EP963_SMBUS_ERROR_COMMAND 0x08 +#define FU_EP963_SMBUS_ERROR_TIMEOUT 0x10 +#define FU_EP963_SMBUS_ERROR_BUSY 0x20 + +const gchar * +fu_ep963x_smbus_strerror(guint8 val); diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-device.c b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-device.c new file mode 100644 index 0000000000000000000000000000000000000000..23d1244eb8d444aed91aec942de8479e0d0824d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-device.c @@ -0,0 +1,381 @@ +/*# + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ep963x-common.h" +#include "fu-ep963x-device.h" +#include "fu-ep963x-firmware.h" + +struct _FuEp963xDevice { + FuHidDevice parent_instance; +}; + +G_DEFINE_TYPE(FuEp963xDevice, fu_ep963x_device, FU_TYPE_HID_DEVICE) + +#define FU_EP963_DEVICE_TIMEOUT 5000 /* ms */ + +static gboolean +fu_ep963x_device_write(FuEp963xDevice *self, + guint8 ctrl_id, + guint8 cmd, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 bufhw[FU_EP963_FEATURE_ID1_SIZE] = { + ctrl_id, + cmd, + 0x0, + }; + if (buf != NULL) { + if (!fu_memcpy_safe(bufhw, + sizeof(bufhw), + 0x02, /* dst */ + buf, + bufsz, + 0x0, /* src */ + bufsz, + error)) + return FALSE; + } + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x00, + bufhw, + sizeof(bufhw), + FU_EP963_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + + /* wait for hardware */ + g_usleep(100 * 1000); + return TRUE; +} + +static gboolean +fu_ep963x_device_write_icp(FuEp963xDevice *self, + guint8 cmd, + const guint8 *buf, + gsize bufsz, + guint8 *bufout, + gsize bufoutsz, + GError **error) +{ + /* wait for hardware */ + for (guint i = 0; i < 5; i++) { + guint8 bufhw[FU_EP963_FEATURE_ID1_SIZE] = { + FU_EP963_USB_CONTROL_ID, + cmd, + }; + if (!fu_ep963x_device_write(self, FU_EP963_USB_CONTROL_ID, cmd, buf, bufsz, error)) + return FALSE; + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + 0x00, + bufhw, + sizeof(bufhw), + FU_EP963_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + return FALSE; + } + if (bufhw[2] == FU_EP963_USB_STATE_READY) { + /* optional data */ + if (bufout != NULL) { + if (!fu_memcpy_safe(bufout, + bufoutsz, + 0x0, + bufhw, + sizeof(bufhw), + 0x02, + bufoutsz, + error)) + return FALSE; + } + return TRUE; + } + g_usleep(100 * 1000); + } + + /* failed */ + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "failed to wait for icp-done"); + return FALSE; +} + +static gboolean +fu_ep963x_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuEp963xDevice *self = FU_EP963X_DEVICE(device); + const guint8 buf[] = {'E', 'P', '9', '6', '3'}; + g_autoptr(GError) error_local = NULL; + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + + if (!fu_ep963x_device_write_icp(self, + FU_EP963_ICP_ENTER, + buf, + sizeof(buf), /* in */ + NULL, + 0x0, /* out */ + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to detach: %s", + error_local->message); + return FALSE; + } + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_ep963x_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuEp963xDevice *self = FU_EP963X_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + if (!fu_ep963x_device_write(self, + FU_EP963_USB_CONTROL_ID, + FU_EP963_OPCODE_SUBMCU_PROGRAM_FINISHED, + NULL, + 0, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to boot to runtime: %s", + error_local->message); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_ep963x_device_setup(FuDevice *device, GError **error) +{ + FuEp963xDevice *self = FU_EP963X_DEVICE(device); + guint8 buf[] = {0x0}; + g_autofree gchar *version = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_ep963x_device_parent_class)->setup(device, error)) + return FALSE; + + /* get version */ + if (!fu_ep963x_device_write_icp(self, + FU_EP963_UF_CMD_VERSION, + NULL, + 0, /* in */ + buf, + sizeof(buf), /* out */ + error)) { + return FALSE; + } + version = g_strdup_printf("%i", buf[0]); + fu_device_set_version(device, version); + + /* the VID and PID are unchanged between bootloader modes */ + if (buf[0] == 0x00) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ep963x_device_wait_cb(FuDevice *device, gpointer user_data, GError **error) +{ + guint8 bufhw[FU_EP963_FEATURE_ID1_SIZE] = { + FU_EP963_USB_CONTROL_ID, + FU_EP963_OPCODE_SUBMCU_PROGRAM_BLOCK, + 0xFF, + }; + if (!fu_hid_device_get_report(FU_HID_DEVICE(device), + 0x00, + bufhw, + sizeof(bufhw), + FU_EP963_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + return FALSE; + } + if (bufhw[2] != FU_EP963_USB_STATE_READY) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_BUSY, "hardware is not ready"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ep963x_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuEp963xDevice *self = FU_EP963X_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) blocks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "icp"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* reset the block index */ + if (!fu_ep963x_device_write(self, + FU_EP963_USB_CONTROL_ID, + FU_EP963_OPCODE_SUBMCU_ENTER_ICP, + NULL, + 0, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to reset block index: %s", + error_local->message); + return FALSE; + } + fu_progress_step_done(progress); + + /* write each block */ + blocks = fu_chunk_array_new_from_bytes(fw, 0x00, 0x00, FU_EP963_TRANSFER_BLOCK_SIZE); + for (guint i = 0; i < blocks->len; i++) { + FuChunk *chk2 = g_ptr_array_index(blocks, i); + guint8 buf[] = {i}; + g_autoptr(GPtrArray) chunks = NULL; + + /* set the block index */ + if (!fu_ep963x_device_write(self, + FU_EP963_USB_CONTROL_ID, + FU_EP963_OPCODE_SUBMCU_RESET_BLOCK_IDX, + buf, + sizeof(buf), + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to reset block index: %s", + error_local->message); + return FALSE; + } + + /* 4 byte chunks */ + chunks = fu_chunk_array_new(fu_chunk_get_data(chk2), + fu_chunk_get_data_sz(chk2), + fu_chunk_get_address(chk2), + 0x0, + FU_EP963_TRANSFER_CHUNK_SIZE); + for (guint j = 0; j < chunks->len; j++) { + FuChunk *chk = g_ptr_array_index(chunks, j); + g_autoptr(GError) error_loop = NULL; + + /* copy data and write */ + if (!fu_ep963x_device_write(self, + FU_EP963_USB_CONTROL_ID, + FU_EP963_OPCODE_SUBMCU_WRITE_BLOCK_DATA, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + &error_loop)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write 0x%x: %s", + (guint)fu_chunk_get_address(chk), + error_loop->message); + return FALSE; + } + } + + /* program block */ + if (!fu_ep963x_device_write(self, + FU_EP963_USB_CONTROL_ID, + FU_EP963_OPCODE_SUBMCU_PROGRAM_BLOCK, + buf, + sizeof(buf), + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write 0x%x: %s", + (guint)fu_chunk_get_address(chk2), + error_local->message); + return FALSE; + } + + /* wait for program finished */ + if (!fu_device_retry(device, fu_ep963x_device_wait_cb, 5, NULL, error)) + return FALSE; + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_ep963x_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_ep963x_device_init(FuEp963xDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.exploretech.ep963x"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_firmware_size(FU_DEVICE(self), FU_EP963_FIRMWARE_SIZE); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EP963X_FIRMWARE); + fu_device_retry_set_delay(FU_DEVICE(self), 100); +} + +static void +fu_ep963x_device_class_init(FuEp963xDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_ep963x_device_write_firmware; + klass_device->attach = fu_ep963x_device_attach; + klass_device->detach = fu_ep963x_device_detach; + klass_device->setup = fu_ep963x_device_setup; + klass_device->set_progress = fu_ep963x_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-device.h b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f6d979635fbcba8ff0273d268b1ca60c7ff73239 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_EP963X_DEVICE (fu_ep963x_device_get_type()) +G_DECLARE_FINAL_TYPE(FuEp963xDevice, fu_ep963x_device, FU, EP963X_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-firmware.c b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..991e3a892b9bdd2210ff2212f386baae58983770 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-firmware.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-ep963x-common.h" +#include "fu-ep963x-firmware.h" + +struct _FuEp963xFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuEp963xFirmware, fu_ep963x_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_ep963x_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint8 magic[5] = {0x0}; + + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + 16, + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, "EP963", sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid EP963x binary file"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ep963x_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize len = g_bytes_get_size(fw); + + /* check size */ + if (len != FU_EP963_FIRMWARE_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware size expected 0x%x, got 0x%x", + (guint)FU_EP963_FIRMWARE_SIZE, + (guint)len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_ep963x_firmware_init(FuEp963xFirmware *self) +{ +} + +static void +fu_ep963x_firmware_class_init(FuEp963xFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_ep963x_firmware_check_magic; + klass_firmware->parse = fu_ep963x_firmware_parse; +} + +FuFirmware * +fu_ep963x_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_EP963X_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-firmware.h b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..ea4a87b27040a26aeb1b8c7ab9d8efdac37f6d25 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-firmware.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_EP963X_FIRMWARE (fu_ep963x_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuEp963xFirmware, fu_ep963x_firmware, FU, EP963X_FIRMWARE, FuFirmware) + +FuFirmware * +fu_ep963x_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-plugin.c b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..2c1793cf287256e1422c796588db0a8cdf36ddca --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-plugin.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-ep963x-device.h" +#include "fu-ep963x-firmware.h" +#include "fu-ep963x-plugin.h" + +struct _FuEp963XPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuEp963XPlugin, fu_ep963x_plugin, FU_TYPE_PLUGIN) + +static void +fu_ep963x_plugin_init(FuEp963XPlugin *self) +{ +} + +static void +fu_ep963x_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "ep963x"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_EP963X_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_EP963X_FIRMWARE); +} + +static void +fu_ep963x_plugin_class_init(FuEp963XPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_ep963x_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/ep963x/fu-ep963x-plugin.h b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..c436f899ac4035c4644b58839e330356e0d3eceb --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/fu-ep963x-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuEp963XPlugin, fu_ep963x_plugin, FU, EP963X_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/ep963x/meson.build b/fwupd-1.8.6/plugins/ep963x/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..a9727a440ad26d41b948fd920b84076e1d9bc623 --- /dev/null +++ b/fwupd-1.8.6/plugins/ep963x/meson.build @@ -0,0 +1,17 @@ +if get_option('plugin_ep963x').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginEp963x"'] + +plugin_quirks += files('ep963x.quirk') +plugin_builtins += static_library('fu_plugin_ep963x', + sources: [ + 'fu-ep963x-common.c', + 'fu-ep963x-device.c', + 'fu-ep963x-firmware.c', + 'fu-ep963x-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/fastboot/README.md b/fwupd-1.8.6/plugins/fastboot/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2f277822b5a3ec5c16aa0e2ec67a54cc9dc108ef --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/README.md @@ -0,0 +1,62 @@ +# Fastboot + +## 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` + +## Update Behavior + +A fastboot device usually presents in runtime mode (or with no interface), +but if the user puts the device into fastboot mode using a physical button +it then enumerates with a USB descriptor. On attach the device reboots to +runtime mode which *may* mean the device "goes away". + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Quirk Use + +This plugin uses the following plugin-specific quirk: + +### FastbootBlockSize + +Block size to use for transfers. + +Since: 1.2.2 + +### FastbootOperationDelay + +Time in ms to delay after a read or write operation. + +Since: 1.7.4 + +## Vendor ID Security + +The vendor ID is set from the USB vendor, for example `USB:0x18D1` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/fastboot/data/android/flashfile.xml b/fwupd-1.8.6/plugins/fastboot/data/android/flashfile.xml new file mode 100644 index 0000000000000000000000000000000000000000..ca70589d7fffa867bf0816abd5a3158dceab6f05 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/data/android/flashfile.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/fwupd-1.8.6/plugins/fastboot/data/lsusb.txt b/fwupd-1.8.6/plugins/fastboot/data/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..424c956f9cc99f642df013053a443af6ae529654 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/data/lsusb.txt @@ -0,0 +1,58 @@ +Bus 001 Device 025: ID 18d1:4ee0 Google Inc. Nexus 4 (bootloader) +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x18d1 Google Inc. + idProduct 0x4ee0 Nexus 4 (bootloader) + bcdDevice 1.00 + iManufacturer 1 Google + iProduct 2 Android + iSerial 3 034412a082919b5c + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0020 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 66 + bInterfaceProtocol 3 + iInterface 4 fastboot + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/fastboot/data/qfil/partition_nand.xml b/fwupd-1.8.6/plugins/fastboot/data/qfil/partition_nand.xml new file mode 100644 index 0000000000000000000000000000000000000000..503318cce101c67f332bf700f9760faa0c08ce1a --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/data/qfil/partition_nand.xml @@ -0,0 +1,21 @@ + + + + 0xAA7D1B9A + 0x1F7D48BC + + 0x4 + + + 0:SBL + 0x8 + 0x2 + 0 + 0xFF + 0x01 + 0x00 + 0xFE + sbl1.mbn + + + diff --git a/fwupd-1.8.6/plugins/fastboot/fastboot.quirk b/fwupd-1.8.6/plugins/fastboot/fastboot.quirk new file mode 100644 index 0000000000000000000000000000000000000000..3b615868afbafe25873f6904a08000e1bb85ec44 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/fastboot.quirk @@ -0,0 +1,8 @@ +# All fastboot devices +[USB\CLASS_FF&SUBCLASS_42&PROT_03] +Plugin = fastboot + +# Quectel EG25-G modem +[USB\VID_18D1&PID_D00D] +FastbootBlockSize = 16384 +FastbootOperationDelay = 250 diff --git a/fwupd-1.8.6/plugins/fastboot/fu-fastboot-device.c b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-device.c new file mode 100644 index 0000000000000000000000000000000000000000..747c66a60fcca7a8309b9f5886faf35d42471429 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-device.c @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#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 */ +#define FASTBOOT_US_TO_MS 1000 + +struct _FuFastbootDevice { + FuUsbDevice parent_instance; + gboolean secure; + guint blocksz; + guint operation_delay; +}; + +G_DEFINE_TYPE(FuFastbootDevice, fu_fastboot_device, FU_TYPE_USB_DEVICE) + +static void +fu_fastboot_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); + fu_string_append_kx(str, idt, "BlockSize", self->blocksz); + fu_string_append_kb(str, idt, "Secure", self->secure); +} + +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; + fu_usb_device_add_interface(FU_USB_DEVICE(self), g_usb_interface_get_number(intf)); + 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) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + gboolean ret; + gsize actual_len = 0; + g_autofree guint8 *buf2 = NULL; + + /* make mutable */ + buf2 = fu_memdup_safe(buf, buflen, error); + if (buf2 == NULL) + return FALSE; + + 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); + + /* give device some time to handle action */ + g_usleep(self->operation_delay * FASTBOOT_US_TO_MS); + + 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, + FuProgress *progress, + 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); + /* give device some time to handle action */ + g_usleep(self->operation_delay * FASTBOOT_US_TO_MS); + + 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_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); + else if (g_strcmp0(tmp, "writing flash") == 0) + fu_progress_set_status(progress, 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, NULL, FU_FASTBOOT_DEVICE_READ_FLAG_NONE, error)) { + g_prefix_error(error, "failed to getvar %s: ", key); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_fastboot_device_cmd(FuDevice *device, + const gchar *cmd, + FuProgress *progress, + FuFastbootDeviceReadFlags flags, + GError **error) +{ + if (!fu_fastboot_device_writestr(device, cmd, error)) + return FALSE; + if (!fu_fastboot_device_read(device, NULL, progress, flags, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_fastboot_device_flash(FuDevice *device, + const gchar *partition, + FuProgress *progress, + GError **error) +{ + g_autofree gchar *tmp = g_strdup_printf("flash:%s", partition); + return fu_fastboot_device_cmd(device, + tmp, + progress, + FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, + error); +} + +static gboolean +fu_fastboot_device_download(FuDevice *device, GBytes *fw, FuProgress *progress, GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); + gsize sz = g_bytes_get_size(fw); + g_autofree gchar *tmp = g_strdup_printf("download:%08x", (guint)sz); + g_autoptr(GPtrArray) chunks = NULL; + + /* tell the client the size of data to expect */ + if (!fu_fastboot_device_cmd(device, + tmp, + progress, + FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, + error)) + return FALSE; + + /* send the data in chunks */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + self->blocksz); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_fastboot_device_write(device, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_step_done(progress); + } + if (!fu_fastboot_device_read(device, + NULL, + progress, + 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; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_fastboot_device_parent_class)->setup(device, error)) + return FALSE; + + /* 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_format(device, FWUPD_VERSION_FORMAT_PAIR); + 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, + FuFirmware *firmware, + XbNode *part, + FuProgress *progress, + 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_firmware_get_image_by_id_bytes(firmware, 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, progress, error)) + return FALSE; + return fu_fastboot_device_flash(device, partition, progress, error); +} + +static gboolean +fu_fastboot_device_write_motorola_part(FuDevice *device, + FuFirmware *firmware, + XbNode *part, + FuProgress *progress, + 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, + progress, + 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"}, + {G_CHECKSUM_SHA256, "SHA256"}, + {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_firmware_get_image_by_id_bytes(firmware, 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, progress, error)) + return FALSE; + return fu_fastboot_device_flash(device, partition, progress, error); + } + + /* dumb operation that doesn't expect a response */ + if (g_strcmp0(op, "boot") == 0 || g_strcmp0(op, "continue") == 0 || + g_strcmp0(op, "reboot") == 0 || g_strcmp0(op, "reboot-bootloader") == 0 || + g_strcmp0(op, "powerdown") == 0) { + return fu_fastboot_device_cmd(device, + op, + progress, + 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, + FuFirmware *firmware, + FuProgress *progress, + 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_firmware_get_image_by_id_bytes(firmware, "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; + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, parts->len); + for (guint i = 0; i < parts->len; i++) { + XbNode *part = g_ptr_array_index(parts, i); + if (!fu_fastboot_device_write_motorola_part(device, + firmware, + part, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fastboot_device_write_qfil(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + 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_firmware_get_image_by_id_bytes(firmware, "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; + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, parts->len); + for (guint i = 0; i < parts->len; i++) { + XbNode *part = g_ptr_array_index(parts, i); + if (!fu_fastboot_device_write_qfil_part(device, + firmware, + part, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fastboot_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) manifest = NULL; + + /* load the manifest of operations */ + manifest = fu_firmware_get_image_by_id(firmware, "partition_nand.xml", NULL); + if (manifest != NULL) + return fu_fastboot_device_write_qfil(device, firmware, progress, error); + manifest = fu_firmware_get_image_by_id(firmware, "flashfile.xml", NULL); + if (manifest != NULL) + return fu_fastboot_device_write_motorola(device, firmware, progress, 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_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); + guint64 tmp = 0; + + /* load from quirks */ + if (g_strcmp0(key, "FastbootBlockSize") == 0) { + if (!fu_strtoull(value, &tmp, 0x40, 0x100000, error)) + return FALSE; + self->blocksz = tmp; + return TRUE; + } + if (g_strcmp0(key, "FastbootOperationDelay") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXSIZE, error)) + return FALSE; + self->operation_delay = 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_fastboot_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + if (!fu_fastboot_device_cmd(device, + "reboot", + progress, + FU_FASTBOOT_DEVICE_READ_FLAG_NONE, + error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_fastboot_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_fastboot_device_init(FuFastbootDevice *self) +{ + /* this is a safe default, even using USBv1 */ + self->blocksz = 512; + /* no delay is applied by default after a read or write operation */ + self->operation_delay = 0; + fu_device_add_protocol(FU_DEVICE(self), "com.google.fastboot"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_remove_delay(FU_DEVICE(self), FASTBOOT_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ARCHIVE_FIRMWARE); +} + +static void +fu_fastboot_device_class_init(FuFastbootDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_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_device->set_progress = fu_fastboot_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/fastboot/fu-fastboot-device.h b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-device.h new file mode 100644 index 0000000000000000000000000000000000000000..eade008a17f3bb322424930205f5dd4c10b40960 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FASTBOOT_DEVICE (fu_fastboot_device_get_type()) +G_DECLARE_FINAL_TYPE(FuFastbootDevice, fu_fastboot_device, FU, FASTBOOT_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/fastboot/fu-fastboot-plugin.c b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..f4f6610198e743428d8fedda77a826f30ac9cc99 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-plugin.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) FIXMEFIXMEFIXMEFIXMEFIXME2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fastboot-device.h" +#include "fu-fastboot-plugin.h" + +struct _FuFastbootPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuFastbootPlugin, fu_fastboot_plugin, FU_TYPE_PLUGIN) + +static void +fu_fastboot_plugin_init(FuFastbootPlugin *self) +{ +} + +static void +fu_fastboot_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "FastbootBlockSize"); + fu_context_add_quirk_key(ctx, "FastbootOperationDelay"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_FASTBOOT_DEVICE); +} + +static void +fu_fastboot_plugin_class_init(FuFastbootPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_fastboot_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/fastboot/fu-fastboot-plugin.h b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..bc015c385207f3f293166f1e88e9b64265d9b4df --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/fu-fastboot-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuFastbootPlugin, fu_fastboot_plugin, FU, FASTBOOT_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/fastboot/meson.build b/fwupd-1.8.6/plugins/fastboot/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d25aa2cdc62a6fc071be8a5924272afa8e0dbea1 --- /dev/null +++ b/fwupd-1.8.6/plugins/fastboot/meson.build @@ -0,0 +1,16 @@ +if get_option('plugin_fastboot').require(gusb.found(), + error_message: 'gusb is needed for plugin_fastboot').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginFastboot"'] + +plugin_quirks += files('fastboot.quirk') +plugin_builtins += static_library('fu_plugin_fastboot', + sources: [ + 'fu-fastboot-plugin.c', + 'fu-fastboot-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/flashrom/README.md b/fwupd-1.8.6/plugins/flashrom/README.md new file mode 100644 index 0000000000000000000000000000000000000000..14f580f16f6de784cae310f776399a25d2a59af0 --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/README.md @@ -0,0 +1,73 @@ +# Flashrom + +## Introduction + +This plugin uses `libflashrom` to update the system firmware. It can be used +to update BIOS or ME regions of the flash. Device for ME region is created +only if "Intel SPI" plugin indicates that such a region exists, which makes +"Intel SPI" a dependency of this plugin for doing ME updates. + +## 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 + +## Coreboot Version String + +The coreboot version string can have an optional prefix (see below). +After the optional prefix the *major*, *minor* string follows and finally +the *build string*, containing the exact commit and repository state, follows. + +For example `4.10-989-gc8a4e4b9c5-dirty` + +### Exception on Lenovo devices + +The thinkpad_acpi kernel module requires a specific pattern in the DMI version +string. To satisfy those requirements coreboot adds the CBETxxxx prefix to the +DMI version string on all Lenovo devices. + +For example `CBET4000 4.10-989-gc8a4e4b9c5-dirty` + +The coreboot DMI version string always starts with `CBET`. + +## GUID Generation + +Internal device uses hardware ID values which are derived from SMBIOS. + +* HardwareID-3 +* HardwareID-4 +* HardwareID-5 +* HardwareID-6 +* HardwareID-10 + +They should match the values provided by `fwupdtool hwids` or the +`ComputerHardwareIds.exe` Windows utility. + +One more GUID has the following form: + +* `FLASHROM\VENDOR_{manufacturer}&PRODUCT_{product}®ION_{ifd_region_name}` + +Its purpose is to target specific regions of the flash as defined by IFD (Intel +SPI Flash Descriptor), examples: + +* `FLASHROM\VENDOR_Notebook&PRODUCT_NS50MU®ION_BIOS` +* `FLASHROM\VENDOR_Notebook&PRODUCT_NS50MU®ION_ME` + +## Update Behavior + +The firmware is deployed to the SPI chip when the machine is in normal runtime +mode, but it is only used when the device is rebooted. + +## Vendor ID Security + +The vendor ID is set from the BIOS vendor, for example `DMI:Google` + +## External Interface Access + +This plugin requires access to all interfaces that `libflashrom` has been compiled for. +This typically is `/sys/bus/spi` but there may be other interfaces as well. diff --git a/fwupd-1.8.6/plugins/flashrom/flashrom.quirk b/fwupd-1.8.6/plugins/flashrom/flashrom.quirk new file mode 100644 index 0000000000000000000000000000000000000000..e2e342a6206cb1ac5dc84a22cb1f2b8dc74f8944 --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/flashrom.quirk @@ -0,0 +1,123 @@ +# Purism +[a0ce5085-2dea-5086-ae72-45810a186ad0] +Plugin = flashrom + +# Libretrend +[52b68c34-6b31-5ecc-8a5c-de37e666ccd5] +Plugin = flashrom +VersionFormat = quad + +# StarLite Mk II (HwId - AMI) +[013b60e5-1023-5bee-8ae5-14cae21377b7] +Plugin = flashrom + +# StarLite Mk II (HwId - coreboot) +[0130a1a1-f888-5977-820a-6214bf1d6ab2] +Plugin = flashrom + +# StarLite Mk III (HwId) +[d5521faa-c50b-5d64-971d-8fd400030c51] +Plugin = flashrom + +# StarLite Mk IV (HwId) +[0fc25c8c-ffa8-54ad-a216-d13cfe75bee4] +Plugin = flashrom + +# StarLabTop Mk III (HwId - AMI) +[013b60e5-1023-5bee-8ae5-14cae21377b7] +Plugin = flashrom + +# StarLabTop Mk III (HwId - coreboot) +[8f8ca82b-30e1-5907-bc9d-4257a49898d4] +Plugin = flashrom + +# StarLabTop Mk IV (HwId) +[baf1d04e-fd16-5e6a-93cc-1c23d171f879] +Plugin = flashrom + +# StarBookMk V (HwId) +[85aba599-addd-5985-a2e8-eddb41c61ba3] +Plugin = flashrom + +# StarLabTop Mk III (coreboot GUID) +[d33219e2-b84c-53a8-a624-27af9752dba6] +Branch = coreboot +Flags = reset-cmos +PciBcrAddr = 0x0 + +# StarLite Mk II (coreboot GUID) +[2993474c-b4c4-5b7c-b2e1-a471d8d328a5] +Branch = coreboot +Flags = reset-cmos +PciBcrAddr = 0x0 + +# StarLite Mk II (AMI GUID) +[0676539d-150f-5f07-89b9-fe0afd98c44e] +FirmwareSizeMax = 0x800000 + +# StarLite Mk III (coreboot GUID) +[3d2f164a-8818-58fd-a082-6c60a67e21a6] +Branch = coreboot +Flags = reset-cmos +PciBcrAddr = 0x0 + +# StarLite Mk III (AMI GUID) +[ec375a72-9ed9-5a21-b1da-5e7f00dcada1] +FirmwareSizeMax = 0x800000 + +# StarLite Mk IV (coreboot GUID) +[5dc1dd5b-761e-5146-8ac2-1fdd8445f2ff] +Branch = coreboot +Flags = reset-cmos +PciBcrAddr = 0x0 + +# StarLite Mk IV (AMI GUID) +[32edd806-13a0-5b0f-a8e9-656a0e147369] +FirmwareSizeMax = 0x800000 + +# StarLabTop Mk IV (coreboot GUID) +[0ee5867c-93f0-5fb4-adf1-9d686ea1183a] +Branch = coreboot +Flags = reset-cmos +PciBcrAddr = 0x0 + +# StarBook Mk V (coreboot GUID) +[54c96fef-31e7-5011-a3ff-ea8e855d9acd] +Branch = coreboot +Flags = reset-cmos +PciBcrAddr = 0x0 + +# NovaCustom NV4x (HwId) +[25b6ea34-8f52-598e-a27a-31e03014dbe3] +Plugin = flashrom +[59caeff5-6a3c-595b-aab1-56500d0fecbc] +Plugin = flashrom + +# NovaCustom NV4x (Dasharo GUID) +[9a8c30e2-1b7e-5b28-9632-fc2fbf8cd0ba] +Branch = dasharo +VersionFormat = triplet +[41a8fb3d-213a-5a8a-b0f4-e2a7c55aaf80] +Branch = dasharo +VersionFormat = triplet + +# NovaCustom NS5x (HwId) +[bab4f96c-34e4-5b92-a785-4e4489b1c395] +Plugin = flashrom + +# NovaCustom NS5x (Dasharo GUID) +[43455705-4c7d-5440-b285-8362b67a5743] +Branch = dasharo +VersionFormat = triplet + +# Tuxedo InifinityBook S14 Gen6 (HwId) +[6c80d85b-d0b6-5ee2-99d4-ec28dd32febd] +Plugin = flashrom +[FLASHROM\GUID_6c80d85b-d0b6-5ee2-99d4-ec28dd32febd] +Flags = fn-m-me-unlock + +# Tuxedo InifinityBook S15 Gen6 (HwId) +[60f53465-e8fc-5122-b79b-f7b03f063037] +Plugin = flashrom +[FLASHROM\GUID_60f53465-e8fc-5122-b79b-f7b03f063037] +Flags = fn-m-me-unlock diff --git a/fwupd-1.8.6/plugins/flashrom/fu-flashrom-cmos.c b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-cmos.c new file mode 100644 index 0000000000000000000000000000000000000000..77f2cc6591260d3c9eb55157a43fb46a59810a7c --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-cmos.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2021 Sean Rhodes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#include "config.h" + +#ifdef HAVE_IO_H +#include +#endif + +#include "fu-flashrom-cmos.h" + +#ifdef HAVE_IO_H +static gboolean +fu_flashrom_cmos_write(guint8 addr, guint8 val) +{ + guint8 tmp; + + /* Reject addresses in the second bank */ + if (addr >= 128) + return FALSE; + + /* Write the value to CMOS */ + outb(addr, RTC_BASE_PORT); + outb(val, RTC_BASE_PORT + 1); + + /* Read the value back from CMOS */ + outb(addr, RTC_BASE_PORT); + tmp = inb(RTC_BASE_PORT + 1); + + /* Check the read value against the written */ + return (tmp == val); +} +#endif + +gboolean +fu_flashrom_cmos_reset(GError **error) +{ +#ifdef HAVE_IO_H + /* Call ioperm() to grant us access to ports 0x70 and 0x71 */ + if (ioperm(RTC_BASE_PORT, 2, TRUE) < 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to gain access to ports 0x70 and 0x71"); + return FALSE; + } + + /* Write a default value to the CMOS checksum */ + if ((!fu_flashrom_cmos_write(CMOS_CHECKSUM_OFFSET, 0xff)) || + (!fu_flashrom_cmos_write(CMOS_CHECKSUM_OFFSET + 1, 0xff))) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "failed to reset CMOS"); + return FALSE; + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no support"); + return FALSE; +#endif +} diff --git a/fwupd-1.8.6/plugins/flashrom/fu-flashrom-cmos.h b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-cmos.h new file mode 100644 index 0000000000000000000000000000000000000000..8a6dc8e5d89b9811e7a37274af43faace0116633 --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-cmos.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Sean Rhodes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* From coreboot's src/include/pc80/mc146818rtc.h file */ +#define RTC_BASE_PORT 0x70 + +/* + * This is the offset of the first of the two checksum bytes + * we may want to figure out how we can determine this dynamically + * during execution. + */ +#define CMOS_CHECKSUM_OFFSET 123 + +gboolean +fu_flashrom_cmos_reset(GError **error); diff --git a/fwupd-1.8.6/plugins/flashrom/fu-flashrom-device.c b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b144e54902048550ec25c1a99f961ea5660b1783 --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-device.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2017 Richard Hughes + * Copyright (C) 2021 Daniel Campello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-flashrom-cmos.h" +#include "fu-flashrom-device.h" + +/* + * Flag to determine if the CMOS checksum should be reset after the flash + * is reprogrammed. This will force the CMOS defaults to be reloaded on + * the next boot. + */ +#define FU_FLASHROM_DEVICE_FLAG_RESET_CMOS (1 << 0) + +/* + * Flag to determine if manual ME unlocking by pressing Fn + M is supported. + */ +#define FU_FLASHROM_DEVICE_FLAG_FN_M_ME_UNLOCK (1 << 1) + +struct _FuFlashromDevice { + FuUdevDevice parent_instance; + FuIfdRegion region; + struct flashrom_flashctx *flashctx; + struct flashrom_layout *layout; +}; + +G_DEFINE_TYPE(FuFlashromDevice, fu_flashrom_device, FU_TYPE_UDEV_DEVICE) + +enum { PROP_0, PROP_FLASHCTX, PROP_REGION, PROP_LAST }; + +static gboolean +fu_flashrom_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + if (g_strcmp0(key, "PciBcrAddr") == 0) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + fu_device_set_metadata_integer(device, "PciBcrAddr", tmp); + return TRUE; + } + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported"); + return FALSE; +} + +static gboolean +fu_flashrom_device_probe(FuDevice *device, GError **error) +{ + const gchar *dev_name = NULL; + const gchar *sysfs_path = NULL; + + sysfs_path = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)); + if (sysfs_path != NULL) { + g_autofree gchar *physical_id = NULL; + physical_id = g_strdup_printf("DEVNAME=%s", sysfs_path); + fu_device_set_physical_id(device, physical_id); + } + dev_name = fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), "name", NULL); + if (dev_name != NULL) { + fu_device_add_instance_id_full(device, + dev_name, + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + } + return TRUE; +} + +static gboolean +fu_flashrom_device_open(FuDevice *device, GError **error) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(device); + struct flashrom_layout *layout; + + /* get the flash size from the device if not already been quirked */ + if (fu_device_get_firmware_size_max(device) == 0) { + gsize flash_size = flashrom_flash_getsize(self->flashctx); + if (flash_size == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash size zero"); + return FALSE; + } + fu_device_set_firmware_size_max(device, flash_size); + } + + if (flashrom_layout_read_from_ifd(&layout, self->flashctx, NULL, 0)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read layout from Intel ICH descriptor"); + return FALSE; + } + + /* update only one specific region of the flash and do not touch others */ + if (flashrom_layout_include_region(layout, fu_ifd_region_to_string(self->region))) { + flashrom_layout_release(layout); + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid region name"); + return FALSE; + } + + /* flashrom_layout_set() doesn't transfer ownership, so we must manage layout's lifetime */ + self->layout = layout; + flashrom_layout_set(self->flashctx, self->layout); + + return TRUE; +} + +static gboolean +fu_flashrom_device_close(FuDevice *device, GError **error) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(device); + + if (self->layout != NULL) { + flashrom_layout_release(self->layout); + self->layout = NULL; + + flashrom_layout_set(self->flashctx, NULL); + } + + return TRUE; +} + +static GBytes * +fu_flashrom_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(device); + gint rc; + gsize bufsz = fu_device_get_firmware_size_max(device); + g_autofree guint8 *buf = g_malloc0(bufsz); + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + rc = flashrom_image_read(self->flashctx, buf, bufsz); + if (rc != 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_READ, "failed to read flash [%i]", rc); + return NULL; + } + return g_bytes_new_take(g_steal_pointer(&buf), bufsz); +} + +static gboolean +fu_flashrom_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autofree gchar *firmware_orig = NULL; + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *basename = NULL; + + /* if the original firmware doesn't exist, grab it now */ + basename = g_strdup_printf("flashrom-%s.bin", fu_device_get_id(device)); + localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + firmware_orig = g_build_filename(localstatedir, "builder", basename, NULL); + if (!fu_path_mkdir_parent(firmware_orig, error)) + return FALSE; + if (!g_file_test(firmware_orig, G_FILE_TEST_EXISTS)) { + g_autoptr(GBytes) buf = NULL; + buf = fu_flashrom_device_dump_firmware(device, progress, error); + if (buf == NULL) { + g_prefix_error(error, "failed to back up original firmware: "); + return FALSE; + } + if (!fu_bytes_set_contents(firmware_orig, buf, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_flashrom_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(device); + gsize sz = 0; + gint rc; + const guint8 *buf; + g_autoptr(GBytes) blob_fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 10, NULL); + + /* read early */ + blob_fw = fu_firmware_get_bytes(firmware, error); + if (blob_fw == NULL) + return FALSE; + buf = g_bytes_get_data(blob_fw, &sz); + + /* write region */ + if (sz != fu_device_get_firmware_size_max(device)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid image size 0x%x, expected 0x%x", + (guint)sz, + (guint)fu_device_get_firmware_size_max(device)); + return FALSE; + } + rc = flashrom_image_write(self->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_progress_step_done(progress); + + if (flashrom_image_verify(self->flashctx, (void *)buf, sz)) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "image verify failed"); + return FALSE; + } + fu_progress_step_done(progress); + + /* Check if CMOS needs a reset */ + if (fu_device_has_private_flag(device, FU_FLASHROM_DEVICE_FLAG_RESET_CMOS)) { + g_debug("Attempting CMOS Reset"); + if (!fu_flashrom_cmos_reset(error)) { + g_prefix_error(error, "failed CMOS reset: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_flashrom_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_flashrom_device_init(FuFlashromDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_protocol(FU_DEVICE(self), "org.flashrom"); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_set_physical_id(FU_DEVICE(self), "flashrom"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_FLASHROM_DEVICE_FLAG_RESET_CMOS, + "reset-cmos"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_FLASHROM_DEVICE_FLAG_FN_M_ME_UNLOCK, + "fn-m-me-unlock"); +} + +static void +fu_flashrom_device_constructed(GObject *obj) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(obj); + fu_device_add_instance_id(FU_DEVICE(self), "main-system-firmware"); +} + +static void +fu_flashrom_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(object); + switch (prop_id) { + case PROP_FLASHCTX: + g_value_set_pointer(value, self->flashctx); + break; + case PROP_REGION: + g_value_set_uint(value, self->region); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_flashrom_device_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(object); + switch (prop_id) { + case PROP_FLASHCTX: + self->flashctx = g_value_get_pointer(value); + break; + case PROP_REGION: + self->region = g_value_get_uint(value); + fu_device_set_logical_id(FU_DEVICE(self), fu_ifd_region_to_string(self->region)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_flashrom_device_finalize(GObject *object) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(object); + if (self->layout != NULL) + flashrom_layout_release(self->layout); + + G_OBJECT_CLASS(fu_flashrom_device_parent_class)->finalize(object); +} + +static void +fu_flashrom_device_class_init(FuFlashromDeviceClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + object_class->get_property = fu_flashrom_device_get_property; + object_class->set_property = fu_flashrom_device_set_property; + + /** + * FuFlashromDevice:region: + * + * The IFD region that's being managed. + */ + pspec = g_param_spec_uint("region", + 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_REGION, pspec); + + /** + * FuFlashromDevice:flashctx: + * + * The JSON root member for the device. + */ + pspec = + g_param_spec_pointer("flashctx", + NULL, + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FLASHCTX, pspec); + + object_class->constructed = fu_flashrom_device_constructed; + object_class->finalize = fu_flashrom_device_finalize; + klass_device->set_quirk_kv = fu_flashrom_device_set_quirk_kv; + klass_device->probe = fu_flashrom_device_probe; + klass_device->open = fu_flashrom_device_open; + klass_device->close = fu_flashrom_device_close; + klass_device->set_progress = fu_flashrom_device_set_progress; + klass_device->prepare = fu_flashrom_device_prepare; + klass_device->dump_firmware = fu_flashrom_device_dump_firmware; + klass_device->write_firmware = fu_flashrom_device_write_firmware; +} + +FuDevice * +fu_flashrom_device_new(FuContext *ctx, struct flashrom_flashctx *flashctx, FuIfdRegion region) +{ + return FU_DEVICE(g_object_new(FU_TYPE_FLASHROM_DEVICE, + "context", + ctx, + "flashctx", + flashctx, + "region", + region, + NULL)); +} + +FuIfdRegion +fu_flashrom_device_get_region(FuFlashromDevice *self) +{ + return self->region; +} + +gboolean +fu_flashrom_device_unlock(FuFlashromDevice *self, GError **error) +{ + if (fu_flashrom_device_get_region(self) == FU_IFD_REGION_ME && + fu_device_has_private_flag(FU_DEVICE(self), FU_FLASHROM_DEVICE_FLAG_FN_M_ME_UNLOCK)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "\n" + "ME region should be unlocked manually the following way:\n" + " 1. Power off your device\n" + " 2. Press and keep holding Fn + M during the next step\n" + " 3. Press power on button"); + return FALSE; + } + + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unlocking of device %s is not supported", + fu_device_get_name(FU_DEVICE(self))); + return FALSE; +} diff --git a/fwupd-1.8.6/plugins/flashrom/fu-flashrom-device.h b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-device.h new file mode 100644 index 0000000000000000000000000000000000000000..2ad1951e86db3b3404da2113acec043dc857fc7e --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-device.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Richard Hughes + * Copyright (C) 2021 Daniel Campello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FLASHROM_DEVICE (fu_flashrom_device_get_type()) +G_DECLARE_FINAL_TYPE(FuFlashromDevice, fu_flashrom_device, FU, FLASHROM_DEVICE, FuUdevDevice) + +struct flashrom_flashctx; + +FuDevice * +fu_flashrom_device_new(FuContext *ctx, struct flashrom_flashctx *flashctx, FuIfdRegion region); + +FuIfdRegion +fu_flashrom_device_get_region(FuFlashromDevice *self); + +gboolean +fu_flashrom_device_unlock(FuFlashromDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/flashrom/fu-flashrom-plugin.c b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..953795585f32ba82ab299befb8bf251f589af36f --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/fu-flashrom-plugin.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2017 Richard Hughes + * Copyright (C) 2019 9elements Agency GmbH + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-flashrom-device.h" + +#define SELFCHECK_TRUE 1 + +struct FuPluginData { + struct flashrom_flashctx *flashctx; + struct flashrom_programmer *flashprog; + gchar *guid; /* GUID from quirks that activated this plugin */ +}; + +typedef FuPluginData FuFlashromPlugin; +#define FU_FLASHROM_PLUGIN(o) fu_plugin_get_data(FU_PLUGIN(o)) + +static void +fu_flashrom_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuFlashromPlugin *self = FU_FLASHROM_PLUGIN(plugin); + if (self->guid != NULL) + fu_string_append(str, idt, "Guid", self->guid); +} + +static int +fu_flashrom_plugin_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 + g_autofree gchar *str = fu_strstrip(tmp); + if (g_strcmp0(str, "OK.") == 0 || g_strcmp0(str, ".") == 0) + return 0; + switch (lvl) { + case FLASHROM_MSG_ERROR: + case FLASHROM_MSG_WARN: + g_warning("%s", str); + break; + case FLASHROM_MSG_INFO: + g_debug("%s", str); + break; + case FLASHROM_MSG_DEBUG: + case FLASHROM_MSG_DEBUG2: + if (g_getenv("FWUPD_FLASHROM_VERBOSE") != NULL) + g_debug("%s", str); + break; + case FLASHROM_MSG_SPEW: + break; + default: + break; + } + return 0; +} + +static void +fu_flashrom_plugin_device_set_version(FuPlugin *plugin, FuDevice *device) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *version; + const gchar *version_major; + const gchar *version_minor; + + /* as-is */ + version = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VERSION); + if (version != NULL) { + /* some Lenovo hardware requires a specific prefix for the EC, + * so strip it before we use ensure-semver */ + if (strlen(version) > 9 && g_str_has_prefix(version, "CBET")) + version += 9; + + /* this may not "stick" if there are no numeric chars */ + fu_device_set_version(device, version); + if (fu_device_get_version(device) != NULL) + return; + } + + /* component parts only */ + version_major = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE); + version_minor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_MINOR_RELEASE); + if (version_major != NULL && version_minor != NULL) { + g_autofree gchar *tmp = g_strdup_printf("%s.%s.0", version_major, version_minor); + fu_device_set_version(device, tmp); + return; + } +} +static void +fu_flashrom_plugin_device_set_bios_info(FuPlugin *plugin, FuDevice *device) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + const guint8 *buf; + gsize bufsz; + guint32 bios_char = 0x0; + g_autoptr(GBytes) bios_table = NULL; + + /* get SMBIOS info */ + bios_table = fu_context_get_smbios_data(ctx, FU_SMBIOS_STRUCTURE_TYPE_BIOS); + if (bios_table == NULL) + return; + + /* ROM size if not already been quirked */ + buf = g_bytes_get_data(bios_table, &bufsz); + if (fu_device_get_firmware_size_max(device) == 0) { + guint8 bios_sz = 0x0; + if (fu_memread_uint8_safe(buf, bufsz, 0x9, &bios_sz, NULL)) { + guint64 firmware_size = (bios_sz + 1) * 64 * 1024; + fu_device_set_firmware_size_max(device, firmware_size); + } + } + + /* BIOS characteristics */ + if (fu_memread_uint32_safe(buf, bufsz, 0xa, &bios_char, G_LITTLE_ENDIAN, NULL)) { + if ((bios_char & (1 << 11)) == 0) { + fu_device_inhibit(device, + "bios-characteristics", + "Not supported from SMBIOS"); + } + } +} + +static void +fu_flashrom_plugin_device_set_hwids(FuPlugin *plugin, FuDevice *device) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + static const gchar *hwids[] = { + "HardwareID-3", + "HardwareID-4", + "HardwareID-5", + "HardwareID-6", + "HardwareID-10", + /* a more useful one for coreboot branch detection */ + FU_HWIDS_KEY_MANUFACTURER "&" FU_HWIDS_KEY_FAMILY "&" FU_HWIDS_KEY_PRODUCT_NAME + "&" FU_HWIDS_KEY_PRODUCT_SKU "&" FU_HWIDS_KEY_BIOS_VENDOR, + }; + /* don't include FU_HWIDS_KEY_BIOS_VERSION */ + for (guint i = 0; i < G_N_ELEMENTS(hwids); i++) { + g_autofree gchar *str = NULL; + str = fu_context_get_hwid_replace_value(ctx, hwids[i], NULL); + if (str != NULL) + fu_device_add_instance_id(device, str); + } +} + +static FuDevice * +fu_flashrom_plugin_add_device(FuPlugin *plugin, + const gchar *guid, + FuIfdRegion region, + GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + FuFlashromPlugin *self = FU_FLASHROM_PLUGIN(plugin); + const gchar *dmi_vendor; + const gchar *product = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_PRODUCT_NAME); + const gchar *vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER); + const gchar *region_str = fu_ifd_region_to_string(region); + g_autofree gchar *name = g_strdup_printf("%s (%s)", product, region_str); + g_autoptr(FuDevice) device = fu_flashrom_device_new(ctx, self->flashctx, region); + + fu_device_set_name(device, name); + fu_device_set_vendor(device, vendor); + + fu_device_add_instance_str(device, "VENDOR", vendor); + fu_device_add_instance_str(device, "PRODUCT", product); + fu_device_add_instance_strup(device, "REGION", region_str); + if (!fu_device_build_instance_id(device, + error, + "FLASHROM", + "VENDOR", + "PRODUCT", + "REGION", + NULL)) + return NULL; + + /* add this so we can attach board-specific quirks */ + fu_device_add_instance_str(FU_DEVICE(device), "GUID", guid); + if (!fu_device_build_instance_id(FU_DEVICE(device), error, "FLASHROM", "GUID", NULL)) + return NULL; + + /* use same VendorID logic as with UEFI */ + dmi_vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VENDOR); + if (dmi_vendor != NULL) { + g_autofree gchar *vendor_id = g_strdup_printf("DMI:%s", dmi_vendor); + fu_device_add_vendor_id(FU_DEVICE(device), vendor_id); + } + fu_flashrom_plugin_device_set_version(plugin, device); + fu_flashrom_plugin_device_set_hwids(plugin, device); + fu_flashrom_plugin_device_set_bios_info(plugin, device); + if (!fu_device_setup(device, error)) + return NULL; + + /* success */ + fu_plugin_device_add(plugin, device); + return g_steal_pointer(&device); +} + +static void +fu_flashrom_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + g_autoptr(FuDevice) me_device = NULL; + FuFlashromPlugin *self = FU_FLASHROM_PLUGIN(plugin); + const gchar *me_region_str = fu_ifd_region_to_string(FU_IFD_REGION_ME); + + /* we're only interested in a device from intel-spi plugin that corresponds to ME + * region of IFD */ + if (g_strcmp0(fu_device_get_plugin(device), "intel_spi") != 0) + return; + if (g_strcmp0(fu_device_get_logical_id(device), me_region_str) != 0) + return; + + me_device = fu_flashrom_plugin_add_device(plugin, self->guid, FU_IFD_REGION_ME, NULL); + if (me_device == NULL) + return; + + /* unlock operation requires device to be locked */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_LOCKED)) + fu_device_add_flag(me_device, FWUPD_DEVICE_FLAG_LOCKED); +} + +static gboolean +fu_flashrom_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuFlashromPlugin *self = FU_FLASHROM_PLUGIN(plugin); + g_autoptr(FuDevice) device = + fu_flashrom_plugin_add_device(plugin, self->guid, FU_IFD_REGION_BIOS, error); + return (device != NULL); +} + +/* finds GUID that activated this plugin */ +static const gchar * +fu_flashrom_plugin_find_guid(FuPlugin *plugin, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + GPtrArray *hwids = fu_context_get_hwid_guids(ctx); + + for (guint i = 0; i < hwids->len; i++) { + const gchar *guid = g_ptr_array_index(hwids, i); + const gchar *plugin_name = + fu_context_lookup_quirk_by_id(ctx, guid, FU_QUIRKS_PLUGIN); + if (g_strcmp0(plugin_name, "flashrom") == 0) + return guid; + } + + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no HwIDs found"); + return NULL; +} + +static gboolean +fu_flashrom_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + gint rc; + const gchar *guid; + FuFlashromPlugin *self = FU_FLASHROM_PLUGIN(plugin); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 5, "find-guid"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 90, "init"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 5, "probe"); + + guid = fu_flashrom_plugin_find_guid(plugin, error); + if (guid == NULL) + return FALSE; + fu_progress_step_done(progress); + + self->guid = g_strdup(guid); + + 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_flashrom_plugin_debug_cb); + fu_progress_step_done(progress); + + if (flashrom_programmer_init(&self->flashprog, "internal", NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "programmer initialization failed"); + return FALSE; + } + + rc = flashrom_flash_probe(&self->flashctx, self->flashprog, NULL); + if (rc == 3) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash probe failed: multiple chips were found"); + return FALSE; + } + if (rc == 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash probe failed: no chip was found"); + return FALSE; + } + if (rc != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash probe failed: unknown error"); + return FALSE; + } + fu_progress_step_done(progress); + + return TRUE; +} + +static gboolean +fu_flashrom_plugin_unlock(FuPlugin *self, FuDevice *device, GError **error) +{ + return fu_flashrom_device_unlock(FU_FLASHROM_DEVICE(device), error); +} + +static void +fu_flashrom_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + + (void)fu_plugin_alloc_data(plugin, sizeof(FuFlashromPlugin)); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "coreboot"); /* obsoleted */ + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_REQUIRE_HWID); +} + +static void +fu_flashrom_plugin_finalize(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuFlashromPlugin *self = FU_FLASHROM_PLUGIN(plugin); + if (self->flashctx != NULL) + flashrom_flash_release(self->flashctx); + if (self->flashprog != NULL) + flashrom_programmer_shutdown(self->flashprog); + g_free(self->guid); + + /* G_OBJECT_CLASS(fu_flashrom_plugin_parent_class)->finalize() not required as modular */ +} + +void +fu_plugin_init_vfuncs(FuPluginVfuncs *vfuncs) +{ + vfuncs->constructed = fu_flashrom_plugin_constructed; + vfuncs->finalize = fu_flashrom_plugin_finalize; + vfuncs->to_string = fu_flashrom_plugin_to_string; + vfuncs->device_registered = fu_flashrom_plugin_device_registered; + vfuncs->startup = fu_flashrom_plugin_startup; + vfuncs->coldplug = fu_flashrom_plugin_coldplug; + vfuncs->unlock = fu_flashrom_plugin_unlock; +} diff --git a/fwupd-1.8.6/plugins/flashrom/meson.build b/fwupd-1.8.6/plugins/flashrom/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..a859cd818be00bfdbf91cd599a6cbd0c6533ba2a --- /dev/null +++ b/fwupd-1.8.6/plugins/flashrom/meson.build @@ -0,0 +1,26 @@ +if get_option('plugin_flashrom').enabled() or \ + (get_option('plugin_flashrom').auto() and libflashrom.found()) +cargs = ['-DG_LOG_DOMAIN="FuPluginFlashrom"'] + +plugin_quirks += files('flashrom.quirk') + +shared_module('fu_plugin_flashrom', + sources: [ + 'fu-flashrom-device.c', + 'fu-flashrom-plugin.c', + 'fu-flashrom-cmos.c', + ], + include_directories: plugin_incdirs, + install: true, + install_dir: libdir_pkg, + link_with: plugin_libs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + dependencies: [ + plugin_deps, + libflashrom, + ], +) +endif diff --git a/fwupd-1.8.6/plugins/focalfp/README.md b/fwupd-1.8.6/plugins/focalfp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b70b5058e97ee17e4634014961c5989b1f466c59 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/README.md @@ -0,0 +1,44 @@ +# Focal TouchPad + +## Introduction + +This plugin allows updating Touchpad devices from Focal. Devices are enumerated +using HID . The I²C mode is used for firmware recovery. + +## 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: + +* tw.com.focalfp + +## GUID Generation + +These device uses the standard DeviceInstanceId values, e.g. + +* `HIDRAW\VEN_2808&DEV_0106` + +## Update Behavior + +The device usually presents in HID mode, and the firmware is written to the +device by switching to a IAP mode where the touchpad is nonfunctional. +Once complete the device is reset to get out of IAP mode and to load the new +firmware version. + +On flash failure the device is nonfunctional, but is recoverable by writing +to the i2c device. This is typically much slower than updating the device +using HID and also requires a model-specific HWID quirk to match. + +## Vendor ID Security + +The vendor ID is set from the HID vendor, for example set to `HIDRAW:0x17EF` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +## External Interface Access + +This plugin requires ioctl access to `HIDIOCSFEATURE` and `HIDIOCGFEATURE`. diff --git a/fwupd-1.8.6/plugins/focalfp/focalfp.quirk b/fwupd-1.8.6/plugins/focalfp/focalfp.quirk new file mode 100644 index 0000000000000000000000000000000000000000..78bdabb30e5c5b58ad4f735a194ca1a2617f1245 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/focalfp.quirk @@ -0,0 +1,3 @@ +[HIDRAW\VEN_2808&DEV_0106] +Plugin = focalfp +GType = FuFocalfpHidDevice diff --git a/fwupd-1.8.6/plugins/focalfp/fu-focalfp-firmware.c b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..9612f27f267a78fa729e767c0af218975eccd5e4 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-firmware.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 Shihwei Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-focalfp-firmware.h" + +struct _FuFocalfpFirmware { + FuFirmwareClass parent_instance; + guint16 start_address; + guint32 checksum; +}; + +G_DEFINE_TYPE(FuFocalfpFirmware, fu_focalfp_firmware, FU_TYPE_FIRMWARE) + +/* firmware block update */ +#define FOCAL_NAME_START_ADDR_WRDS 0x011E + +const guint8 focalfp_signature[] = {0xFF}; + +guint32 +fu_focalfp_firmware_get_checksum(FuFocalfpFirmware *self) +{ + g_return_val_if_fail(FU_IS_FOCALFP_FIRMWARE(self), 0); + return self->checksum; +} + +static void +fu_focalfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuFocalfpFirmware *self = FU_FOCALFP_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "start_address", self->start_address); + fu_xmlb_builder_insert_kx(bn, "checksum", self->checksum); +} + +static gboolean +fu_focalfp_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFocalfpFirmware *self = FU_FOCALFP_FIRMWARE(firmware); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* start address */ + if (!fu_memread_uint16_safe(buf, + bufsz, + FOCAL_NAME_START_ADDR_WRDS, + &self->start_address, + G_BIG_ENDIAN, + error)) { + return FALSE; + } + if (self->start_address != 0x582e) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "force pad address invalid: 0x%x", + self->start_address); + return FALSE; + } + /* calculate checksum */ + for (guint32 i = 0; i < bufsz; i += 4) { + guint32 value = 0; + if (!fu_memread_uint32_safe(buf, bufsz, i, &value, G_LITTLE_ENDIAN, error)) + return FALSE; + self->checksum ^= value; + } + self->checksum += 1; + + /* success */ + return TRUE; +} + +static void +fu_focalfp_firmware_init(FuFocalfpFirmware *self) +{ +} + +static void +fu_focalfp_firmware_class_init(FuFocalfpFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_focalfp_firmware_parse; + klass_firmware->export = fu_focalfp_firmware_export; +} + +FuFirmware * +fu_focalfp_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_FOCALFP_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/focalfp/fu-focalfp-firmware.h b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..3b4cb2eeb1bc3f58776bb44cd2ec4911a25518e5 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-firmware.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2022 Shihwei Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FOCALFP_FIRMWARE (fu_focalfp_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuFocalfpFirmware, fu_focalfp_firmware, FU, FOCALFP_FIRMWARE, FuFirmware) + +FuFirmware * +fu_focalfp_firmware_new(void); +guint32 +fu_focalfp_firmware_get_checksum(FuFocalfpFirmware *self); diff --git a/fwupd-1.8.6/plugins/focalfp/fu-focalfp-hid-device.c b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-hid-device.c new file mode 100644 index 0000000000000000000000000000000000000000..5047cc457d480a1b810cee017efcfb9b25c95bc4 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-hid-device.c @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2022 Shihwei Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-focalfp-firmware.h" +#include "fu-focalfp-hid-device.h" + +struct _FuFocalfpHidDevice { + FuUdevDevice parent_instance; +}; + +G_DEFINE_TYPE(FuFocalfpHidDevice, fu_focalfp_hid_device, FU_TYPE_UDEV_DEVICE) + +#define FU_FOCALFP_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +#define CMD_ENTER_UPGRADE_MODE 0x40 +#define CMD_CHECK_CURRENT_STATE 0x41 +#define CMD_READY_FOR_UPGRADE 0x42 +#define CMD_SEND_DATA 0x43 +#define CMD_UPGRADE_CHECKSUM 0x44 +#define CMD_EXIT_UPRADE_MODE 0x45 +#define CMD_USB_READ_UPGRADE_ID 0x46 +#define CMD_USB_ERASE_FLASH 0x47 +#define CMD_USB_BOOT_READ 0x48 +#define CMD_USB_BOOT_BOOTLOADERVERSION 0x49 +#define CMD_READ_REGISTER 0x50 +#define CMD_WRITE_REGISTER 0x51 +#define CMD_ACK 0xf0 +#define CMD_NACK 0xff + +#define FIRST_PACKET 0x00 +#define MID_PACKET 0x01 +#define END_PACKET 0x02 +#define REPORT_SIZE 64 +#define MAX_USB_PACKET_SIZE 56 + +static gboolean +fu_focalfp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error); + +static gboolean +fu_focalfp_hid_device_probe(FuDevice *device, GError **error) +{ + /* check is valid */ + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "hidraw") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct subsystem=%s, expected hidraw", + fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device))); + return FALSE; + } + + /* i2c-hid */ + if (fu_udev_device_get_model(FU_UDEV_DEVICE(device)) != 0x0106) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not i2c-hid touchpad"); + return FALSE; + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static guint8 +fu_focaltp_buffer_generate_checksum(const guint8 *buf, gsize bufsz) +{ + guint8 checksum = 0; + for (gsize i = 0; i < bufsz; i++) + checksum ^= buf[i]; + checksum++; + return checksum; +} + +static gboolean +fu_focalfp_hid_device_io(FuFocalfpHidDevice *self, + guint8 *wbuf, + gsize wbufsz, + guint8 *rbuf, + gsize rbufsz, + GError **error) +{ + /* SetReport */ + if (wbuf != NULL && wbufsz > 0) { + guint8 buf[64] = {0x06, 0xff, 0xff}; + guint8 cmdlen = 4 + wbufsz; + buf[3] = cmdlen; + if (!fu_memcpy_safe(buf, sizeof(buf), 0x04, wbuf, wbufsz, 0x00, wbufsz, error)) + return FALSE; + buf[cmdlen] = fu_focaltp_buffer_generate_checksum(&buf[1], cmdlen - 1); + if (g_getenv("FWUPD_FOCALFP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SetReport", buf, sizeof(buf)); + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCSFEATURE(cmdlen + 1), + buf, + NULL, + FU_FOCALFP_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + } + + /* GetReport */ + if (rbuf != NULL && rbufsz > 0) { + guint8 buf[64] = {0x06}; + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGFEATURE(sizeof(buf)), + buf, + NULL, + FU_FOCALFP_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + if (g_getenv("FWUPD_FOCALFP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetReport", buf, sizeof(buf)); + if (!fu_memcpy_safe(rbuf, rbufsz, 0x0, buf, sizeof(buf), 0x00, rbufsz, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_focalfp_buffer_check_cmd_crc(const guint8 *buf, gsize bufsz, guint8 cmd, GError **error) +{ + guint8 csum = 0; + guint8 csum_actual; + + /* check was correct response */ + if (buf[4] != cmd) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got cmd 0x%02x, expected 0x%02x", + buf[4], + cmd); + return FALSE; + } + + /* check crc */ + if (!fu_memread_uint8_safe(buf, bufsz, buf[3], &csum, error)) + return FALSE; + csum_actual = fu_focaltp_buffer_generate_checksum(buf + 1, buf[3] - 1); + if (csum != csum_actual) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got checksum 0x%02x, expected 0x%02x", + csum, + csum_actual); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_focalfp_hid_device_read_reg_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 buf[64] = {0x0}; + guint8 *val = (guint8 *)user_data; + + if (!fu_focalfp_hid_device_io(self, NULL, 0, buf, 8, error)) + return FALSE; + + /* check was correct response */ + if (!fu_focalfp_buffer_check_cmd_crc(buf, sizeof(buf), CMD_READ_REGISTER, error)) + return FALSE; + + /* success */ + *val = buf[6]; + return TRUE; +} + +static gboolean +fu_focalfp_hid_device_read_reg(FuFocalfpHidDevice *self, + guint8 reg_address, + guint8 *val, /* out */ + GError **error) +{ + guint8 buf[64] = {CMD_READ_REGISTER, reg_address}; + + /* write */ + if (!fu_focalfp_hid_device_io(self, buf, 2, NULL, 0, error)) + return FALSE; + + /* read */ + return fu_device_retry_full(FU_DEVICE(self), + fu_focalfp_hid_device_read_reg_cb, + 5, + 1 /* ms */, + val, + error); +} + +/* enter upgrade mode */ +static gboolean +fu_focalfp_hid_device_enter_upgrade_mode(FuFocalfpHidDevice *self, GError **error) +{ + guint8 wbuf[64] = {CMD_ENTER_UPGRADE_MODE}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) { + g_prefix_error(error, "failed to CMD_ENTER_UPGRADE_MODE: "); + return FALSE; + } + + /* check was correct response */ + return fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error); +} + +/* get bootloader current state */ +static gboolean +fu_focalfp_hid_device_check_current_state(FuFocalfpHidDevice *self, guint8 *val, GError **error) +{ + guint8 wbuf[64] = {CMD_CHECK_CURRENT_STATE}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 7, error)) + return FALSE; + + /* check was correct response */ + if (!fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_CHECK_CURRENT_STATE, error)) + return FALSE; + + /* success */ + *val = rbuf[5]; + return TRUE; +} + +static gboolean +fu_focalfp_hid_device_wait_for_upgrade_ready_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 wbuf[64] = {CMD_READY_FOR_UPGRADE}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 7, error)) + return FALSE; + + /* check was correct response */ + return fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_READY_FOR_UPGRADE, error); +} + +/* wait for ready */ +static gboolean +fu_focalfp_hid_device_wait_for_upgrade_ready(FuFocalfpHidDevice *self, + guint retries, + GError **error) +{ + return fu_device_retry_full(FU_DEVICE(self), + fu_focalfp_hid_device_wait_for_upgrade_ready_cb, + retries, + 500, + NULL, + error); +} + +static gboolean +fu_focalfp_hid_device_read_update_id_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint16 *us_ic_id = (guint16 *)user_data; + guint8 wbuf[64] = {CMD_USB_READ_UPGRADE_ID}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 8, error)) + return FALSE; + + /* check was correct response */ + if (!fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_USB_READ_UPGRADE_ID, error)) + return FALSE; + + /* success */ + *us_ic_id = fu_memread_uint16(rbuf + 5, G_BIG_ENDIAN); + return TRUE; +} + +/* get bootload id */ +static gboolean +fu_focalfp_hid_device_read_update_id(FuFocalfpHidDevice *self, guint16 *us_ic_id, GError **error) +{ + return fu_device_retry_full(FU_DEVICE(self), + fu_focalfp_hid_device_read_update_id_cb, + 10, + 1 /* ms */, + us_ic_id, + error); +} + +/* erase flash */ +static gboolean +fu_focalfp_hid_device_erase_flash(FuFocalfpHidDevice *self, GError **error) +{ + guint8 wbuf[64] = {CMD_USB_ERASE_FLASH}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) + return FALSE; + + /* check was correct response */ + return fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error); +} + +static gboolean +fu_focalfp_hid_device_send_data_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, NULL, 0, rbuf, 7, error)) + return FALSE; + + /* check was correct response */ + return fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error); +} + +/* send write data */ +static gboolean +fu_focalfp_hid_device_send_data(FuFocalfpHidDevice *self, + guint8 packet_type, + const guint8 *buf, + guint8 bufsz, + GError **error) +{ + guint8 wbuf[64] = {CMD_SEND_DATA, packet_type}; + + /* sanity check */ + if (bufsz > REPORT_SIZE - 8) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "data length 0x%x invalid", + bufsz); + return FALSE; + } + + if (!fu_memcpy_safe(wbuf, sizeof(wbuf), 0x02, buf, bufsz, 0x00, bufsz, error)) + return FALSE; + if (!fu_focalfp_hid_device_io(self, wbuf, bufsz + 2, NULL, 0, error)) + return FALSE; + + return fu_device_retry_full(FU_DEVICE(self), + fu_focalfp_hid_device_send_data_cb, + 4, + 1 /* ms */, + NULL, + error); +} + +/* get checksum for write done */ +static gboolean +fu_focalfp_hid_device_checksum_upgrade(FuFocalfpHidDevice *self, guint32 *val, GError **error) +{ + guint8 wbuf[64] = {CMD_UPGRADE_CHECKSUM}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 7 + 3, error)) + return FALSE; + + /* check was correct response */ + if (!fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_UPGRADE_CHECKSUM, error)) + return FALSE; + + /* success */ + return fu_memread_uint32_safe(rbuf, sizeof(rbuf), 0x05, val, G_LITTLE_ENDIAN, error); +} + +static gboolean +fu_focalfp_hid_device_setup(FuDevice *device, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint16 fwver; + guint8 buf[2] = {0x0}; + g_autofree gchar *version = NULL; + + /* get current firmware version */ + if (!fu_focalfp_hid_device_read_reg(self, 0xA6, buf, error)) { + g_prefix_error(error, "failed to read version1: "); + return FALSE; + } + if (!fu_focalfp_hid_device_read_reg(self, 0xAD, buf + 1, error)) { + g_prefix_error(error, "failed to read version2: "); + return FALSE; + } + fwver = fu_memread_uint16(buf, G_BIG_ENDIAN); + version = fu_version_from_uint16(fwver, FWUPD_VERSION_FORMAT_HEX); + fu_device_set_version(device, version); + + /* success */ + return TRUE; +} + +static gboolean +fu_focalfp_hid_device_write_chunks(FuFocalfpHidDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 uc_packet_type = MID_PACKET; + + if (i == 0) + uc_packet_type = FIRST_PACKET; + else if (i == chunks->len - 1) + uc_packet_type = END_PACKET; + + if (!fu_focalfp_hid_device_send_data(self, + uc_packet_type, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "failed to write chunk %u: ", i); + return FALSE; + } + if (!fu_focalfp_hid_device_wait_for_upgrade_ready(self, 100, error)) { + g_prefix_error(error, "failed to wait for chunk %u: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_focalfp_hid_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + const guint32 UPGRADE_ID = 0x582E; + guint16 us_ic_id = 0; + guint32 checksum = 0; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 89, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 89, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "reset"); + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* check chip id and erase flash */ + if (!fu_focalfp_hid_device_wait_for_upgrade_ready(self, 6, error)) + return FALSE; + if (!fu_focalfp_hid_device_read_update_id(self, &us_ic_id, error)) + return FALSE; + if (us_ic_id != UPGRADE_ID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got us_ic_id 0x%02x, expected 0x%02x", + us_ic_id, + (guint)UPGRADE_ID); + return FALSE; + } + if (!fu_focalfp_hid_device_erase_flash(self, error)) + return FALSE; + g_usleep(1000 * 1000); + if (!fu_focalfp_hid_device_wait_for_upgrade_ready(self, 20, error)) + return FALSE; + fu_progress_step_done(progress); + + /* send packet data */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, MAX_USB_PACKET_SIZE); + if (!fu_focalfp_hid_device_write_chunks(self, + chunks, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* write flash end and check ready (fw calculate checksum) */ + g_usleep(50 * 1000); + if (!fu_focalfp_hid_device_wait_for_upgrade_ready(self, 5, error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify checksum */ + if (!fu_focalfp_hid_device_checksum_upgrade(self, &checksum, error)) + return FALSE; + if (checksum != fu_focalfp_firmware_get_checksum(FU_FOCALFP_FIRMWARE(firmware))) { + g_usleep(500 * 1000); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "device checksum invalid, got 0x%02x, expected 0x%02x", + checksum, + fu_focalfp_firmware_get_checksum(FU_FOCALFP_FIRMWARE(firmware))); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +/* called after attach, but only when the firmware has been updated */ +static gboolean +fu_focalfp_hid_device_reload(FuDevice *device, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 idbuf[2] = {0x0}; + + g_usleep(500 * 1000); + if (!fu_focalfp_hid_device_read_reg(self, 0x9F, &idbuf[0], error)) + return FALSE; + if (!fu_focalfp_hid_device_read_reg(self, 0xA3, &idbuf[1], error)) + return FALSE; + g_debug("id1=%x, id2=%x", idbuf[1], idbuf[0]); + if (idbuf[1] != 0x58 && idbuf[0] != 0x22) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware id invalid, got 0x%02x:0x%02x, expected 0x%02x:0x%02x", + idbuf[1], + idbuf[0], + (guint)0x58, + (guint)0x22); + return FALSE; + } + + return fu_focalfp_hid_device_setup(device, error); +} + +static gboolean +fu_focalfp_hid_device_detach_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 uc_mode = 0; + + if (!fu_focalfp_hid_device_enter_upgrade_mode(self, error)) { + g_prefix_error(error, "failed to enter upgrade mode: "); + return FALSE; + } + + /* get current state */ + if (!fu_focalfp_hid_device_check_current_state(self, &uc_mode, error)) + return FALSE; + + /* 1: upgrade mode; 2: fw mode */ + if (uc_mode != 1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got uc_mode 0x%02x, expected 0x%02x", + uc_mode, + (guint)1); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* enter upgrade mode */ +static gboolean +fu_focalfp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 wbuf[64] = {CMD_ENTER_UPGRADE_MODE}; + guint8 rbuf[64] = {0x0}; + + /* command to go from APP --> Bootloader -- but we do not check crc */ + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) { + g_prefix_error(error, "failed to CMD_ENTER_UPGRADE_MODE: "); + return FALSE; + } + g_usleep(200 * 1000); + + /* second command : bootloader normal mode --> bootloader upgrade mode */ + if (!fu_device_retry_full(device, + fu_focalfp_hid_device_detach_cb, + 3, + 200 /* ms */, + progress, + error)) + return FALSE; + + /* success */ + g_usleep(200 * 1000); + return TRUE; +} + +/* exit upgrade mode */ +static gboolean +fu_focalfp_hid_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); + guint8 wbuf[64] = {CMD_EXIT_UPRADE_MODE}; + guint8 rbuf[64] = {0x0}; + + if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) + return FALSE; + + /* check was correct response */ + if (!fu_focalfp_buffer_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error)) + return FALSE; + + /* success */ + g_usleep(500 * 1000); + return TRUE; +} + +static void +fu_focalfp_hid_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_focalfp_hid_device_init(FuFocalfpHidDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_firmware_size(FU_DEVICE(self), 0x1E000); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_FOCALFP_FIRMWARE); + fu_device_set_summary(FU_DEVICE(self), "Forcepad"); + fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.focalfp"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE | + FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK); +} + +static void +fu_focalfp_hid_device_class_init(FuFocalfpHidDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->attach = fu_focalfp_hid_device_attach; + klass_device->detach = fu_focalfp_hid_device_detach; + klass_device->setup = fu_focalfp_hid_device_setup; + klass_device->reload = fu_focalfp_hid_device_reload; + klass_device->write_firmware = fu_focalfp_hid_device_write_firmware; + klass_device->probe = fu_focalfp_hid_device_probe; + klass_device->set_progress = fu_focalfp_hid_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/focalfp/fu-focalfp-hid-device.h b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-hid-device.h new file mode 100644 index 0000000000000000000000000000000000000000..9143fa9a8b3450a2ba8fc374c8f728ef96729bb8 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-hid-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2022 Shihwei Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FOCALFP_HID_DEVICE (fu_focalfp_hid_device_get_type()) +G_DECLARE_FINAL_TYPE(FuFocalfpHidDevice, + fu_focalfp_hid_device, + FU, + FOCALFP_HID_DEVICE, + FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/focalfp/fu-focalfp-plugin.c b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..369c504c7f266070832ebaf1eb2859958188bb05 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-plugin.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Shihwei Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-focalfp-firmware.h" +#include "fu-focalfp-hid-device.h" +#include "fu-focalfp-plugin.h" + +struct _FuFocalfpPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuFocalfpPlugin, fu_focalfp_plugin, FU_TYPE_PLUGIN) + +static void +fu_focalfp_plugin_init(FuFocalfpPlugin *self) +{ +} + +static void +fu_focalfp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_FOCALFP_FIRMWARE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_FOCALFP_HID_DEVICE); +} + +static void +fu_focalfp_plugin_class_init(FuFocalfpPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_focalfp_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/focalfp/fu-focalfp-plugin.h b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..b895f6c7cd9f0a026a2e1918aee0d9693283644a --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/fu-focalfp-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuFocalfpPlugin, fu_focalfp_plugin, FU, FOCALFP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/focalfp/meson.build b/fwupd-1.8.6/plugins/focalfp/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..565845643a8df640194934ee3e6dd4d7056f2982 --- /dev/null +++ b/fwupd-1.8.6/plugins/focalfp/meson.build @@ -0,0 +1,18 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginFocalfp"'] + +plugin_quirks += files('focalfp.quirk') +plugin_builtins += static_library('fu_plugin_focalfp', + sources: [ + 'fu-focalfp-plugin.c', + 'fu-focalfp-firmware.c', + 'fu-focalfp-hid-device.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/fpc/README.md b/fwupd-1.8.6/plugins/fpc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..995b62dec825118744a50e212396ee1defc8cd2e --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/README.md @@ -0,0 +1,35 @@ +# FPC Fingerprint Sensor + +## Introduction + +The plugin used for update firmware for fingerprint sensors from FPC. + +## 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.fingerprints.dfupc + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_10A5&PID_FFE0&REV_0001` +* `USB\VID_10A5&PID_FFE0` +* `USB\VID_10A5` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x10A5` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/fpc/fpc.quirk b/fwupd-1.8.6/plugins/fpc/fpc.quirk new file mode 100644 index 0000000000000000000000000000000000000000..d24e2ad5264ba6af885b7f4927e1697197160b0a --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/fpc.quirk @@ -0,0 +1,9 @@ +# FPC Fingerprint sensor +[USB\VID_10A5&PID_FFE0] +Plugin = fpc +[USB\VID_10A5&PID_FFE1] +Plugin = fpc +Flags = moh-device,rts +[USB\VID_10A5&PID_9800] +Plugin = fpc +Flags = moh-device,rts diff --git a/fwupd-1.8.6/plugins/fpc/fu-fpc-device.c b/fwupd-1.8.6/plugins/fpc/fu-fpc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..132ca365b3c6dd9b0b48e2753bf484c06d445956 --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/fu-fpc-device.c @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2022 Haowei Lo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-fpc-device.h" + +#define FPC_USB_INTERFACE 0 +#define FPC_USB_TRANSFER_TIMEOUT 1500 /* ms */ +#define FPC_FLASH_BLOCK_SIZE_DEFAULT 2048 /* 2048 */ +#define FPC_FLASH_BLOCK_SIZE_4096 4096 /* 4096 */ + +#define FPC_CMD_DFU_DETACH 0x00 +#define FPC_CMD_DFU_DNLOAD 0x01 +#define FPC_CMD_DFU_GETSTATUS 0x03 +#define FPC_CMD_DFU_CLRSTATUS 0x04 +#define FPC_CMD_DFU_GET_FW_STATUS 0x09 + +#define FPC_CMD_BOOT0 0x04 +#define FPC_CMD_GET_STATE 0x0B + +#define FPC_DEVICE_MOC_STATE_LEN 68 +#define FPC_DEVICE_MOH_STATE_LEN 72 +#define FPC_DEVICE_DFU_FW_STATUS_LEN 8 +#define FPC_DFU_MAX_ATTEMPTS 50 + +#define FPC_DEVICE_DFU_MODE_CLASS 0xFE +#define FPC_DEVICE_DFU_MODE_PORT 0x02 +#define FPC_DEVICE_NORMAL_MODE_CLASS 0xFF +#define FPC_DEVICE_NORMAL_MODE_PORT 0xFF + +#define FPC_DFU_DNBUSY 0x04 + +/** + * FU_FPC_DEVICE_FLAG_MOH_DEVICE: + * + * Device is a moh device + */ +#define FU_FPC_DEVICE_FLAG_MOH_DEVICE (1 << 0) + +/** + * FU_FPC_DEVICE_FLAG_LEGACY_DFU: + * + * Device supports legacy dfu mode + */ + +#define FU_FPC_DEVICE_FLAG_LEGACY_DFU (1 << 1) + +/** + * FU_FPC_DEVICE_FLAG_RTS_DEVICE: + * + * Device is a RTS device + */ + +#define FU_FPC_DEVICE_FLAG_RTS_DEVICE (1 << 2) + +struct _FuFpcDevice { + FuUsbDevice parent_instance; + guint32 max_block_size; +}; + +G_DEFINE_TYPE(FuFpcDevice, fu_fpc_device, FU_TYPE_USB_DEVICE) + +typedef struct __attribute__((packed)) { + guint8 bstatus; + guint8 bmax_payload_size; + guint8 reserved[2]; + guint8 bstate; + guint8 reserved2; +} FuFpcDfuStatus; + +static gboolean +fu_fpc_device_dfu_cmd(FuFpcDevice *self, + guint8 request, + guint16 value, + guint8 *data, + gsize length, + gboolean device2host, + gboolean type_vendor, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_len = 0; + + if (data == NULL && length > 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid input data"); + return FALSE; + } + + if (!g_usb_device_control_transfer(usb_device, + device2host ? G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST + : G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + type_vendor ? G_USB_DEVICE_REQUEST_TYPE_VENDOR + : G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + request, + value, + 0x0000, + data, + length, + length ? &actual_len : NULL, + FPC_USB_TRANSFER_TIMEOUT, + NULL, + error)) + return FALSE; + if (actual_len != length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only sent 0x%04x of 0x%04x", + (guint)actual_len, + (guint)length); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_fpc_device_fw_cmd(FuFpcDevice *self, + guint8 request, + guint8 *data, + gsize length, + gboolean device2host, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_len = 0; + + if (data == NULL && length > 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid input data"); + return FALSE; + } + + if (!g_usb_device_control_transfer(usb_device, + device2host ? G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST + : G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + request, + 0x0000, + 0x0000, + data, + length, + length ? &actual_len : NULL, + FPC_USB_TRANSFER_TIMEOUT, + NULL, + error)) + return FALSE; + if (actual_len != length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only sent 0x%04x of 0x%04x", + (guint)actual_len, + (guint)length); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_fpc_device_setup_mode(FuFpcDevice *self, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autoptr(GPtrArray) intfs = NULL; + + intfs = g_usb_device_get_interfaces(usb_device, error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == FPC_DEVICE_DFU_MODE_CLASS && + g_usb_interface_get_protocol(intf) == FPC_DEVICE_DFU_MODE_PORT) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; + } else if (g_usb_interface_get_class(intf) == FPC_DEVICE_NORMAL_MODE_CLASS && + g_usb_interface_get_protocol(intf) == FPC_DEVICE_NORMAL_MODE_PORT) { + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; + } else { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "device is not supported by this plugin"); + return FALSE; + } + } + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no update interface found"); + return FALSE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} + +static gboolean +fu_fpc_device_setup_version(FuFpcDevice *self, GError **error) +{ + guint32 version = 0; + gsize data_len = 0; + FuEndianType endian_type = G_LITTLE_ENDIAN; + g_autofree guint8 *data = NULL; + g_autofree gchar *str_version = NULL; + + if (fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_RTS_DEVICE)) + endian_type = G_BIG_ENDIAN; + + if (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_MOH_DEVICE)) { + data_len = FPC_DEVICE_MOH_STATE_LEN; + } else { + data_len = FPC_DEVICE_MOC_STATE_LEN; + } + + data = g_malloc0(data_len); + if (!fu_fpc_device_fw_cmd(self, FPC_CMD_GET_STATE, data, data_len, TRUE, error)) + return FALSE; + + if (!fu_memread_uint32_safe(data, data_len, 0, &version, endian_type, error)) + return FALSE; + + } else { + if (!fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_LEGACY_DFU)) { + if (!fu_fpc_device_dfu_cmd(self, + FPC_CMD_DFU_CLRSTATUS, + 0x0000, + NULL, + 0, + FALSE, + FALSE, + error)) + return FALSE; + } + + data = g_malloc0(FPC_DEVICE_DFU_FW_STATUS_LEN); + if (!fu_fpc_device_dfu_cmd(self, + FPC_CMD_DFU_GET_FW_STATUS, + 0x0000, + data, + FPC_DEVICE_DFU_FW_STATUS_LEN, + TRUE, + TRUE, + error)) + return FALSE; + + if (!fu_memread_uint32_safe(data, + FPC_DEVICE_DFU_FW_STATUS_LEN, + 4, + &version, + endian_type, + error)) + return FALSE; + } + + /* set display version */ + str_version = fu_version_from_uint32(version, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version_raw(FU_DEVICE(self), (guint64)version); + fu_device_set_version(FU_DEVICE(self), str_version); + return TRUE; +} + +static gboolean +fu_fpc_device_check_dfu_status(FuDevice *device, gpointer user_data, GError **error) +{ + FuFpcDevice *self = FU_FPC_DEVICE(device); + FuFpcDfuStatus *dfu_status = (FuFpcDfuStatus *)user_data; + + if (!fu_fpc_device_dfu_cmd(self, + FPC_CMD_DFU_GETSTATUS, + 0x0000, + (guint8 *)dfu_status, + sizeof(FuFpcDfuStatus), + TRUE, + FALSE, + error)) { + g_prefix_error(error, "failed to get status: "); + return FALSE; + } + + if (dfu_status->bstatus != 0 || dfu_status->bstate == FPC_DFU_DNBUSY) { + /* device is not in correct status/state */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "dfu status error [0x%x, 0x%x]", + dfu_status->bstatus, + dfu_status->bstate); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_fpc_device_update_init(FuFpcDevice *self, GError **error) +{ + FuFpcDfuStatus dfu_status = {0}; + + if (!fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_LEGACY_DFU)) { + if (!fu_fpc_device_dfu_cmd(self, + FPC_CMD_DFU_CLRSTATUS, + 0x0000, + NULL, + 0, + FALSE, + FALSE, + error)) { + g_prefix_error(error, "failed to clear status: "); + return FALSE; + } + } + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_fpc_device_check_dfu_status, + FPC_DFU_MAX_ATTEMPTS, + 20, + (gpointer)&dfu_status, + error)) + return FALSE; + + if (dfu_status.bmax_payload_size > 0 || + fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_RTS_DEVICE)) + self->max_block_size = FPC_FLASH_BLOCK_SIZE_4096; + else + self->max_block_size = FPC_FLASH_BLOCK_SIZE_DEFAULT; + + return TRUE; +} + +static void +fu_fpc_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuFpcDevice *self = FU_FPC_DEVICE(device); + + fu_string_append_kx(str, idt, "Max block size", self->max_block_size); + fu_string_append_kb(str, + idt, + "LegacyDfu", + fu_device_has_private_flag(device, FU_FPC_DEVICE_FLAG_LEGACY_DFU)); + fu_string_append_kb(str, + idt, + "MocDevice", + !fu_device_has_private_flag(device, FU_FPC_DEVICE_FLAG_MOH_DEVICE)); + if (fu_device_has_private_flag(device, FU_FPC_DEVICE_FLAG_MOH_DEVICE)) { + fu_string_append_kb( + str, + idt, + "RtsDevice", + fu_device_has_private_flag(device, FU_FPC_DEVICE_FLAG_RTS_DEVICE)); + } +} + +static gboolean +fu_fpc_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuFpcDevice *self = FU_FPC_DEVICE(device); + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + if (!fu_fpc_device_dfu_cmd(self, FPC_CMD_DFU_DETACH, 0x0000, NULL, 0, FALSE, FALSE, error)) + return FALSE; + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_fpc_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuFpcDevice *self = FU_FPC_DEVICE(device); + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + if (!fu_fpc_device_fw_cmd(self, FPC_CMD_BOOT0, NULL, 0, FALSE, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_fpc_device_setup(FuDevice *device, GError **error) +{ + FuFpcDevice *self = FU_FPC_DEVICE(device); + + /* setup */ + if (!FU_DEVICE_CLASS(fu_fpc_device_parent_class)->setup(device, error)) + return FALSE; + + if (!fu_fpc_device_setup_mode(self, error)) { + g_prefix_error(error, "failed to get device mode: "); + return FALSE; + } + + /* ensure version */ + if (!fu_fpc_device_setup_version(self, error)) { + g_prefix_error(error, "failed to get firmware version: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fpc_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuFpcDevice *self = FU_FPC_DEVICE(device); + FuFpcDfuStatus dfu_status = {0}; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "check"); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* don't auto-boot firmware */ + if (!fu_fpc_device_update_init(self, &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to initial update: %s", + error_local->message); + return FALSE; + } + fu_progress_step_done(progress); + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, + 0x00, /* page_sz */ + self->max_block_size); + + /* write each block */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) req = g_byte_array_new(); + + g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + + if (!fu_fpc_device_dfu_cmd(self, + FPC_CMD_DFU_DNLOAD, + (guint16)i, + req->data, + (gsize)req->len, + FALSE, + FALSE, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_local->message); + return FALSE; + } + + if (!fu_device_retry_full(device, + fu_fpc_device_check_dfu_status, + FPC_DFU_MAX_ATTEMPTS, + 20, + (gpointer)&dfu_status, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_local->message); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + + if (!fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_LEGACY_DFU)) { + /* exit fw download loop. send null package */ + if (!fu_fpc_device_dfu_cmd(self, + FPC_CMD_DFU_DNLOAD, + 0, + NULL, + 0, + FALSE, + FALSE, + error)) { + g_prefix_error(error, "fail to exit dnload loop:"); + return FALSE; + } + } + fu_progress_step_done(progress); + + if (!fu_device_retry_full(device, + fu_fpc_device_check_dfu_status, + FPC_DFU_MAX_ATTEMPTS, + 20, + (gpointer)&dfu_status, + error)) + return FALSE; + + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_fpc_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_fpc_device_init(FuFpcDevice *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_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_remove_delay(FU_DEVICE(self), 5000); + fu_device_add_protocol(FU_DEVICE(self), "com.fingerprints.dfupc"); + fu_device_set_summary(FU_DEVICE(self), "FPC fingerprint sensor"); + fu_device_set_install_duration(FU_DEVICE(self), 15); + fu_device_set_firmware_size_min(FU_DEVICE(self), 0x10000); + fu_device_set_firmware_size_max(FU_DEVICE(self), 0x64000); + fu_usb_device_add_interface(FU_USB_DEVICE(self), FPC_USB_INTERFACE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_FPC_DEVICE_FLAG_MOH_DEVICE, + "moh-device"); + fu_device_register_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_RTS_DEVICE, "rts"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_FPC_DEVICE_FLAG_LEGACY_DFU, + "legacy-dfu"); +} + +static void +fu_fpc_device_class_init(FuFpcDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_fpc_device_to_string; + klass_device->write_firmware = fu_fpc_device_write_firmware; + klass_device->setup = fu_fpc_device_setup; + klass_device->reload = fu_fpc_device_setup; + klass_device->attach = fu_fpc_device_attach; + klass_device->detach = fu_fpc_device_detach; + klass_device->set_progress = fu_fpc_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/fpc/fu-fpc-device.h b/fwupd-1.8.6/plugins/fpc/fu-fpc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..999498f125735ae39138022edbf8987d022d992d --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/fu-fpc-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Haowei Lo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FPC_DEVICE (fu_fpc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuFpcDevice, fu_fpc_device, FU, FPC_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/fpc/fu-fpc-plugin.c b/fwupd-1.8.6/plugins/fpc/fu-fpc-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..987280cdbdb161d3882e949a0aa825151027e527 --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/fu-fpc-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Haowei Lo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fpc-device.h" +#include "fu-fpc-plugin.h" + +struct _FuFpcPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuFpcPlugin, fu_fpc_plugin, FU_TYPE_PLUGIN) + +static void +fu_fpc_plugin_init(FuFpcPlugin *self) +{ +} + +static void +fu_fpc_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_FPC_DEVICE); +} + +static void +fu_fpc_plugin_class_init(FuFpcPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_fpc_constructed; +} diff --git a/fwupd-1.8.6/plugins/fpc/fu-fpc-plugin.h b/fwupd-1.8.6/plugins/fpc/fu-fpc-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..8679754e03f7a52c570e64d21f9f577ddcd8163e --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/fu-fpc-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuFpcPlugin, fu_fpc_plugin, FU, FPC_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/fpc/meson.build b/fwupd-1.8.6/plugins/fpc/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..21c8660bcd26d6e67aa66a01d8e9d88cbe7303c5 --- /dev/null +++ b/fwupd-1.8.6/plugins/fpc/meson.build @@ -0,0 +1,15 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginFpc"'] + +plugin_quirks += join_paths(meson.current_source_dir(), 'fpc.quirk') +plugin_builtins += static_library('fu_plugin_fpc', + sources: [ + 'fu-fpc-device.c', + 'fu-fpc-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/fresco-pd/README.md b/fwupd-1.8.6/plugins/fresco-pd/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0fe019fed2f6e4db9d750ca8594cb79c32911488 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/README.md @@ -0,0 +1,39 @@ +# Fresco PD + +## Introduction + +This plugin is used to update Power Devlivery devices by Fresco. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecifed binary format. + +This plugin supports the following protocol ID: + +* com.frescologic.pd + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1D5C&PID_7102&REV_0001` +* `USB\VID_1D5C&PID_7102` +* `USB\VID_1D5C` + +These devices also use custom GUID values, e.g. + +* `USB\VID_1D5C&PID_7102&CID_01` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1D5C` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/fresco-pd/fresco-pd.quirk b/fwupd-1.8.6/plugins/fresco-pd/fresco-pd.quirk new file mode 100644 index 0000000000000000000000000000000000000000..7937808f8f64be533650853fcc3baa5a9e8dca61 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fresco-pd.quirk @@ -0,0 +1,7 @@ +# FL7102 +[USB\VID_1D5C&PID_7102] +Plugin = fresco_pd + +# FL7112 +[USB\VID_1D5C&PID_7112] +Plugin = fresco_pd diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-common.c b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-common.c new file mode 100644 index 0000000000000000000000000000000000000000..5633c2a2d1070023de42d50d3bfd5a984ce975f0 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-common.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 Fresco Logic + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fresco-pd-common.h" + +gchar * +fu_fresco_pd_version_from_buf(const guint8 ver[4]) +{ + if (ver[3] == 1 || ver[3] == 2) + return g_strdup_printf("%u.%u.%u.%u", ver[0], ver[1], ver[2], ver[3]); + return g_strdup_printf("%u.%u.%u.%u", ver[3], ver[1], ver[2], ver[0]); +} diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-common.h b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-common.h new file mode 100644 index 0000000000000000000000000000000000000000..f979de61f0e1466e5b37dd807b1f9b8fe743f6c0 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-common.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2020 Fresco Logic + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gchar * +fu_fresco_pd_version_from_buf(const guint8 ver[4]); diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-device.c b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..43ab55e96524a429b4c55fdfe115bb12085d2687 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-device.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2020 Fresco Logic + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fresco-pd-common.h" +#include "fu-fresco-pd-device.h" +#include "fu-fresco-pd-firmware.h" + +struct _FuFrescoPdDevice { + FuUsbDevice parent_instance; + guint8 customer_id; +}; + +G_DEFINE_TYPE(FuFrescoPdDevice, fu_fresco_pd_device, FU_TYPE_USB_DEVICE) + +static void +fu_fresco_pd_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuFrescoPdDevice *self = FU_FRESCO_PD_DEVICE(device); + fu_string_append_ku(str, idt, "CustomerID", self->customer_id); +} + +static gboolean +fu_fresco_pd_device_transfer_read(FuFrescoPdDevice *self, + guint16 offset, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_length = 0; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(bufsz != 0, FALSE); + + /* to device */ + if (g_getenv("FWUPD_FRESCO_PD_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "read", buf, bufsz); + 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, + 0x40, + 0x0, + offset, + buf, + bufsz, + &actual_length, + 5000, + NULL, + error)) { + g_prefix_error(error, "failed to read from offset 0x%x: ", offset); + return FALSE; + } + if (bufsz != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "read 0x%x bytes of 0x%x", + (guint)actual_length, + bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fresco_pd_device_transfer_write(FuFrescoPdDevice *self, + guint16 offset, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_length = 0; + + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(bufsz != 0, FALSE); + + /* to device */ + if (g_getenv("FWUPD_FRESCO_PD_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "write", buf, bufsz); + 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, + 0x41, + 0x0, + offset, + buf, + bufsz, + &actual_length, + 5000, + NULL, + error)) { + g_prefix_error(error, "failed to write offset 0x%x: ", offset); + return FALSE; + } + if (bufsz != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "wrote 0x%x bytes of 0x%x", + (guint)actual_length, + bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_fresco_pd_device_read_byte(FuFrescoPdDevice *self, guint16 offset, guint8 *buf, GError **error) +{ + return fu_fresco_pd_device_transfer_read(self, offset, buf, 1, error); +} + +static gboolean +fu_fresco_pd_device_write_byte(FuFrescoPdDevice *self, guint16 offset, guint8 buf, GError **error) +{ + return fu_fresco_pd_device_transfer_write(self, offset, &buf, 1, error); +} + +static gboolean +fu_fresco_pd_device_set_byte(FuFrescoPdDevice *self, guint16 offset, guint8 val, GError **error) +{ + guint8 buf = 0x0; + if (!fu_fresco_pd_device_read_byte(self, offset, &buf, error)) + return FALSE; + if (buf == val) + return TRUE; + return fu_fresco_pd_device_write_byte(self, offset, val, error); +} + +static gboolean +fu_fresco_pd_device_and_byte(FuFrescoPdDevice *self, guint16 offset, guint8 val, GError **error) +{ + guint8 buf = 0xff; + if (!fu_fresco_pd_device_read_byte(self, offset, &buf, error)) + return FALSE; + buf &= val; + return fu_fresco_pd_device_write_byte(self, offset, buf, error); +} + +static gboolean +fu_fresco_pd_device_or_byte(FuFrescoPdDevice *self, guint16 offset, guint8 val, GError **error) +{ + guint8 buf; + if (!fu_fresco_pd_device_read_byte(self, offset, &buf, error)) + return FALSE; + buf |= val; + return fu_fresco_pd_device_write_byte(self, offset, buf, error); +} + +static gboolean +fu_fresco_pd_device_setup(FuDevice *device, GError **error) +{ + FuFrescoPdDevice *self = FU_FRESCO_PD_DEVICE(device); + guint8 ver[4] = {0x0}; + g_autofree gchar *version = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_fresco_pd_device_parent_class)->setup(device, error)) + return FALSE; + + /* read existing device version */ + for (guint i = 0; i < 4; i++) { + if (!fu_fresco_pd_device_transfer_read(self, 0x3000 + i, &ver[i], 1, error)) { + g_prefix_error(error, "failed to read device version [%u]: ", i); + return FALSE; + } + } + version = fu_fresco_pd_version_from_buf(ver); + fu_device_set_version(FU_DEVICE(self), version); + + /* get customer ID */ + self->customer_id = ver[1]; + + /* add extra instance ID */ + fu_device_add_instance_u8(device, "CID", self->customer_id); + return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "CID", NULL); +} + +static FuFirmware * +fu_fresco_pd_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuFrescoPdDevice *self = FU_FRESCO_PD_DEVICE(device); + guint8 customer_id; + g_autoptr(FuFirmware) firmware = fu_fresco_pd_firmware_new(); + + /* check firmware is suitable */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + customer_id = fu_fresco_pd_firmware_get_customer_id(FU_FRESCO_PD_FIRMWARE(firmware)); + if (customer_id != self->customer_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "device is incompatible with firmware x.%u.x.x", + customer_id); + return NULL; + } + return g_steal_pointer(&firmware); +} + +static gboolean +fu_fresco_pd_device_panther_reset_device(FuFrescoPdDevice *self, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + g_debug("resetting target device"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* ignore when the device reset before completing the transaction */ + if (!fu_fresco_pd_device_or_byte(self, 0xA003, 1 << 3, &error_local)) { + if (g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_FAILED)) { + g_debug("ignoring %s", error_local->message); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to reset device: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_fresco_pd_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuFrescoPdDevice *self = FU_FRESCO_PD_DEVICE(device); + const guint8 *buf; + gsize bufsz = 0x0; + guint16 begin_addr = 0x6420; + guint8 config[3] = {0x0}; + guint8 start_symbols[2] = {0x0}; + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "enable-mtp-write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, "copy-mmio"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 46, "customize"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "boot"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, NULL); + + /* get default blob, which we know is already bigger than FirmwareMin */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + buf = g_bytes_get_data(fw, &bufsz); + + /* get start symbols, and be slightly paranoid */ + if (!fu_memcpy_safe(start_symbols, + sizeof(start_symbols), + 0x0, /* dst */ + buf, + bufsz, + 0x4000, /* src */ + sizeof(start_symbols), + error)) + return FALSE; + + /* 0xA001 = b'0 + * 0x6C00 = b'0 + * 0x6C04 = 0x08 */ + g_debug("disable MCU, and enable mtp write"); + if (!fu_fresco_pd_device_and_byte(self, 0xa001, ~(1 << 2), error)) { + g_prefix_error(error, "failed to disable MCU bit 2: "); + return FALSE; + } + if (!fu_fresco_pd_device_and_byte(self, 0x6c00, ~(1 << 1), error)) { + g_prefix_error(error, "failed to disable MCU bit 1: "); + return FALSE; + } + if (!fu_fresco_pd_device_write_byte(self, 0x6c04, 0x08, error)) { + g_prefix_error(error, "failed to disable MCU: "); + return FALSE; + } + + /* fill safe code in the boot code */ + for (guint16 i = 0; i < 0x400; i += 3) { + for (guint j = 0; j < 3; j++) { + if (!fu_fresco_pd_device_read_byte(self, + begin_addr + i + j, + &config[j], + error)) { + g_prefix_error(error, "failed to read config byte %u: ", j); + return FALSE; + } + } + if (config[0] == start_symbols[0] && config[1] == start_symbols[1]) { + begin_addr = 0x6420 + i; + break; + } + if (config[0] == 0 && config[1] == 0 && config[2] == 0) + break; + } + g_debug("begin_addr: 0x%04x", begin_addr); + for (guint i = begin_addr + 3; i < (guint)begin_addr + 0x400; i += 3) { + for (guint j = 0; j < 3; j++) { + if (!fu_fresco_pd_device_read_byte(self, i + j, &config[j], error)) { + g_prefix_error(error, "failed to read config byte %u: ", j); + return FALSE; + } + } + if (config[0] == 0x74 && config[1] == 0x06 && config[2] != 0x22) { + if (!fu_fresco_pd_device_write_byte(self, i + 2, 0x22, error)) + return FALSE; + } else if (config[0] == 0x6c && config[1] == 0x00 && config[2] != 0x01) { + if (!fu_fresco_pd_device_write_byte(self, i + 2, 0x01, error)) + return FALSE; + } else if (config[0] == 0x00 && config[1] == 0x00 && config[2] != 0x00) + break; + } + fu_progress_step_done(progress); + + /* copy buf offset [0 - 0x3FFFF] to mmio address [0x2000 - 0x5FFF] */ + g_debug("fill firmware body"); + for (guint16 byte_index = 0; byte_index < 0x4000; byte_index++) { + if (!fu_fresco_pd_device_set_byte(self, + byte_index + 0x2000, + buf[byte_index], + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)byte_index + 1, + 0x4000); + } + fu_progress_step_done(progress); + + /* write file buf 0x4200 ~ 0x4205, 6 bytes to internal address 0x6600 ~ 0x6605 + * write file buf 0x4210 ~ 0x4215, 6 bytes to internal address 0x6610 ~ 0x6615 + * write file buf 0x4220 ~ 0x4225, 6 bytes to internal address 0x6620 ~ 0x6625 + * write file buf 0x4230, 1 byte, to internal address 0x6630 */ + g_debug("update customize data"); + for (guint16 byte_index = 0; byte_index < 6; byte_index++) { + if (!fu_fresco_pd_device_set_byte(self, + 0x6600 + byte_index, + buf[0x4200 + byte_index], + error)) + return FALSE; + if (!fu_fresco_pd_device_set_byte(self, + 0x6610 + byte_index, + buf[0x4210 + byte_index], + error)) + return FALSE; + if (!fu_fresco_pd_device_set_byte(self, + 0x6620 + byte_index, + buf[0x4220 + byte_index], + error)) + return FALSE; + } + if (!fu_fresco_pd_device_set_byte(self, 0x6630, buf[0x4230], error)) + return FALSE; + fu_progress_step_done(progress); + + /* overwrite firmware file's boot code area (0x4020 ~ 0x41ff) to the area on the device + * marked by begin_addr example: if the begin_addr = 0x6420, then copy file buf [0x4020 ~ + * 0x41ff] to device offset[0x6420 ~ 0x65ff] */ + g_debug("write boot configuration area"); + for (guint16 byte_index = 0; byte_index < 0x1e0; byte_index += 3) { + if (!fu_fresco_pd_device_set_byte(self, + begin_addr + byte_index + 0, + buf[0x4020 + byte_index], + error)) + return FALSE; + if (!fu_fresco_pd_device_set_byte(self, + begin_addr + byte_index + 1, + buf[0x4021 + byte_index], + error)) + return FALSE; + if (!fu_fresco_pd_device_set_byte(self, + begin_addr + byte_index + 2, + buf[0x4022 + byte_index], + error)) + return FALSE; + } + fu_progress_step_done(progress); + + /* reset the device */ + if (!fu_fresco_pd_device_panther_reset_device(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_fresco_pd_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_fresco_pd_device_init(FuFrescoPdDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_protocol(FU_DEVICE(self), "com.frescologic.pd"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_install_duration(FU_DEVICE(self), 15); + fu_device_set_remove_delay(FU_DEVICE(self), 20000); + fu_device_set_firmware_size(FU_DEVICE(self), 0x4400); +} + +static void +fu_fresco_pd_device_class_init(FuFrescoPdDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_fresco_pd_device_to_string; + klass_device->setup = fu_fresco_pd_device_setup; + klass_device->write_firmware = fu_fresco_pd_device_write_firmware; + klass_device->prepare_firmware = fu_fresco_pd_device_prepare_firmware; + klass_device->set_progress = fu_fresco_pd_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-device.h b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..b0a7fffe0020afa14d6d9ff4e230963298e87a82 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FRESCO_PD_DEVICE (fu_fresco_pd_device_get_type()) +G_DECLARE_FINAL_TYPE(FuFrescoPdDevice, fu_fresco_pd_device, FU, FRESCO_PD_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-firmware.c b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..57702a36eaa640c6a69d563fd9e00a1905e61ef4 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-firmware.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Fresco Logic + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fresco-pd-common.h" +#include "fu-fresco-pd-firmware.h" + +struct _FuFrescoPdFirmware { + FuFirmwareClass parent_instance; + guint8 customer_id; +}; + +G_DEFINE_TYPE(FuFrescoPdFirmware, fu_fresco_pd_firmware, FU_TYPE_FIRMWARE) + +guint8 +fu_fresco_pd_firmware_get_customer_id(FuFrescoPdFirmware *self) +{ + return self->customer_id; +} + +static void +fu_fresco_pd_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuFrescoPdFirmware *self = FU_FRESCO_PD_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "customer_id", self->customer_id); +} + +static gboolean +fu_fresco_pd_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuFrescoPdFirmware *self = FU_FRESCO_PD_FIRMWARE(firmware); + guint8 ver[4] = {0x0}; + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *version = NULL; + + /* read version block */ + if (!fu_memcpy_safe(ver, + sizeof(ver), + 0x0, /* dst */ + buf, + bufsz, + 0x1000, /* src */ + sizeof(ver), + error)) + return FALSE; + + /* customer ID is always the 2nd byte */ + self->customer_id = ver[1]; + + /* set version number */ + version = fu_fresco_pd_version_from_buf(ver); + fu_firmware_set_version(firmware, version); + return TRUE; +} + +static void +fu_fresco_pd_firmware_init(FuFrescoPdFirmware *self) +{ +} + +static void +fu_fresco_pd_firmware_class_init(FuFrescoPdFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_fresco_pd_firmware_parse; + klass_firmware->export = fu_fresco_pd_firmware_export; +} + +FuFirmware * +fu_fresco_pd_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_FRESCO_PD_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-firmware.h b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..863a9014534c7caf2150f5b7e4db71d1015427ac --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-firmware.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 Fresco Logic + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_FRESCO_PD_FIRMWARE (fu_fresco_pd_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuFrescoPdFirmware, fu_fresco_pd_firmware, FU, FRESCO_PD_FIRMWARE, FuFirmware) + +FuFirmware * +fu_fresco_pd_firmware_new(void); +guint8 +fu_fresco_pd_firmware_get_customer_id(FuFrescoPdFirmware *self); diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-plugin.c b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..1b15971ed03d8d5db9240af86921458b27839c98 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fresco-pd-device.h" +#include "fu-fresco-pd-firmware.h" +#include "fu-fresco-pd-plugin.h" + +struct _FuFrescoPdPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuFrescoPdPlugin, fu_fresco_pd_plugin, FU_TYPE_PLUGIN) + +static void +fu_fresco_pd_plugin_init(FuFrescoPdPlugin *self) +{ +} + +static void +fu_fresco_pd_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_FRESCO_PD_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_FRESCO_PD_FIRMWARE); +} + +static void +fu_fresco_pd_plugin_class_init(FuFrescoPdPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_fresco_pd_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-plugin.h b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..3d08f447f84f787f62c476215428863aa33941ea --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/fu-fresco-pd-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuFrescoPdPlugin, fu_fresco_pd_plugin, FU, FRESCO_PD_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/fresco-pd/lsusb.txt b/fwupd-1.8.6/plugins/fresco-pd/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..e91fb4c9b2ac109b66a81ce13990af3897f14611 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/lsusb.txt @@ -0,0 +1,72 @@ +Bus 003 Device 002: ID 1d5c:7102 Fresco Logic Generic Billboard Device +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.01 + bDeviceClass 17 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x1d5c Fresco Logic + idProduct 0x7102 + bcdDevice 1.00 + iManufacturer 1 Fresco Logic, Inc + iProduct 2 Generic Billboard Device + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0012 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 0mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 17 + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 +Binary Object Store Descriptor: + bLength 5 + bDescriptorType 15 + wTotalLength 0x0050 + bNumDeviceCaps 3 + USB 2.0 Extension Device Capability: + bLength 7 + bDescriptorType 16 + bDevCapabilityType 2 + bmAttributes 0x00000006 + BESL Link Power Management (LPM) Supported + Container ID Device Capability: + bLength 20 + bDescriptorType 16 + bDevCapabilityType 4 + bReserved 0 + ContainerID {00000000-0000-0000-0000-000000000000} + Billboard Capability: + bLength 48 + bDescriptorType 16 + bDevCapabilityType 13 + iAddtionalInfoURL 4 www.frescologic.com + bNumberOfAlternateModes 1 + bPreferredAlternateMode 0 + VCONN Power 0 1W + bmConfigured 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + bcdVersion 1.10 + bAdditionalFailureInfo 0 + bReserved 0 + Alternate Modes supported by Device Container: + Alternate Mode 0 : Alternate Mode configuration successful + wSVID[0] 0x0000 + bAlternateMode[0] 0 + iAlternateModeString[0] 0 +Device Status: 0x0001 + Self Powered diff --git a/fwupd-1.8.6/plugins/fresco-pd/meson.build b/fwupd-1.8.6/plugins/fresco-pd/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..489e190816a8bc3d4d36d1a2e96dfbf20812ee66 --- /dev/null +++ b/fwupd-1.8.6/plugins/fresco-pd/meson.build @@ -0,0 +1,17 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginFrescoPd"'] + +plugin_quirks += files('fresco-pd.quirk') +plugin_builtins += static_library('fu_plugin_fresco_pd', + sources: [ + 'fu-fresco-pd-plugin.c', + 'fu-fresco-pd-common.c', + 'fu-fresco-pd-device.c', + 'fu-fresco-pd-firmware.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/genesys/README.md b/fwupd-1.8.6/plugins/genesys/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0227db2082e3ad78b11edda98bbe5861aeaa9b97 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/README.md @@ -0,0 +1,158 @@ +# Genesys Logic + +## Introduction + +This plugin allows updating the Genesys Logic USB Hub devices. + +* GL3521 +* GL3523 +* GL3590 + +Additionally, this plugin allows updating the MStar Semiconductor Scaler connected via an I²C bus. + +* TSUM G + +## Firmware Format + +The daemon will decompress the cabinet archives and extract the firmware blob in an unspecified binary file format. + +This plugin supports the following protocol IDs: + +* com.genesys.usbhub +* com.mstarsemi.scaler + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values for the USB Hub, e.g. + +* GenesysLogic USB2.0 Hub: `USB\VID_05E3&PID_0610` +* HP USB-C Controller: `USB\VID_03F0&PID_0610` + +Additionally, some customized instance IDs are added. e.g. + +* GenesysLogic USB2.0 Hub: `USB\VID_03F0&PID_0610&IC_352330&BONDING_0F` +* GenesysLogic USB2.0 Hub: `USB\VID_03F0&PID_0610&VENDOR_GENESYSLOGIC&IC_352330&BONDING_0F&PORTNUM_23&VENDORSUP_C09B5DD3-1A23-51D2-995A-F7366AAB3CA4` +* HP M24fd USB-C Hub: `USB\VID_03F0&PID_0610&PUBKEY_AB859399-95B8-5817-B521-9AD8CC7F5BD6` +* HP M27fd USB-C Hub: `USB\VID_03F0&PID_0610&PUBKEY_6BE97D77-C2BA-5AA2-B7DF-B9B318BEC2B5` + +These devices also use custom GUID values for the Scaler, e.g. + +* HP M24fd USB-C Monitor: `GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_B335BDCE-7073-5D0E-9BD3-9B69C1A6899F&PANELREV_RIM101` +* HP M27fd USB-C Monitor: `GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_847A3650-8648-586B-83C8-8B53714F37E3&PANELREV_RIM101` + +The Public Key is product-specific and is required to identify the product. + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### has-mstar-scaler + +USB Hub has a MStar Semiconductor Scaler attached via I²C. + +Since 1.7.6. + +### has-public-key + +Device has a public-key appended to firmware. + +Since 1.8.0 + +### GenesysUsbhubSwitchRequest + +USB Hub Switch Request value. + +* HP Mxfd FHD Monitors: `0xA1` + +Since 1.7.6. + +### GenesysUsbhubReadRequest + +USB Hub Read Request value. + +* HP Mxfd FHD Monitors: `0xA2` + +Since 1.7.6. + +### GenesysUsbhubWriteRequest + +USB Hub Write Request value. + +* HP Mxfd FHD Monitors: `0xA3` + +Since 1.7.6. + +### use-i2c-ch0 + +Scalar uses I²C channel 0. + +Since 1.7.6. + +### pause-r2-cpu + +Scalar pause R2 CPU. + +Since 1.7.6. + +### GenesysScalerDeviceTransferSize + +Scaler Block size to use for transfers. + +* MStar Semiconductor TSUM G: `0x40` + +Since 1.7.6. + +### GenesysScalerGpioOutputRegister + +Scaler GPIO Output Register value. + +* MStar Semiconductor TSUM G: `0x0426` + +Since 1.7.6. + +### GenesysScalerGpioEnableRegister + +Scaler GPIO Enable Register value. + +* MStar Semiconductor TSUM G: `0x0428` + +Since 1.7.6. + +### GenesysScalerGpioValue + +Scaler GPIO value. + +* MStar Semiconductor TSUM G: `0x01` + +Since 1.7.6. + +### GenesysScalerCfiFlashId + +CFI Flash Id. + +* HP M24fd USB-C Monitor: `0xC22016` +* HP M27fd USB-C Monitor: `0xC84016` + +Since 1.8.2. + +## Runtime Requirement + +The USB Hub devices and its attached Scaler require libgusb version [0.3.8][1] or later to be detected. + +## Update Behavior + +The devices are independently updated at runtime using USB control transfers. + +The firmware is deployed when the device is in normal runtime mode, and the device will reset when the new firmware has been written. + +The HP Mxfd FHD Monitors must be connected to host via the USB-C cable to apply an update. The devices remain functional during the update; the Scaler update is ~10 minute long. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, for example set to `USB:0x03F0` for HP. + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. + +[1]: https://github.com/hughsie/libgusb/commit/4e118c154dde70e196c4381bd97790a9413c3552 diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-common.h b/fwupd-1.8.6/plugins/genesys/fu-genesys-common.h new file mode 100644 index 0000000000000000000000000000000000000000..20ab8ac390b1cf0d5fa69e436ca98e8caae2e07f --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-common.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +typedef struct { + guint8 N[0x206]; + guint8 E[0x00c]; +} FuGenesysPublicKey; + +typedef struct { + guint8 reg; + guint8 expected_val; +} FuGenesysWaitFlashRegisterHelper; + +typedef enum { + ISP_MODEL_UNKNOWN, + + /* hub */ + ISP_MODEL_HUB_GL3510, + ISP_MODEL_HUB_GL3521, + ISP_MODEL_HUB_GL3523, + ISP_MODEL_HUB_GL3590, + ISP_MODEL_HUB_GL7000, + ISP_MODEL_HUB_GL3525, + + /* pd */ + ISP_MODEL_PD_GL9510, +} FuGenesysModel; + +typedef struct { + FuGenesysModel model; + gint32 revision; +} FuGenesysChip; + +typedef struct __attribute__((packed)) { + guint8 tool_string_version; + + /* byte arrays are ASCII encoded and not NUL terminated */ + guint8 mask_project_code[4]; + guint8 mask_project_hardware[1]; /* 0=a, 1=b... */ + guint8 mask_project_firmware[2]; /* 01,02,03... */ + guint8 mask_project_ic_type[6]; /* 352310=GL3523-10 (ASCII string) */ + + guint8 running_project_code[4]; + guint8 running_project_hardware[1]; + guint8 running_project_firmware[2]; + guint8 running_project_ic_type[6]; + + guint8 firmware_version[4]; /* MMmm=MM.mm (ASCII string) */ +} FuGenesysStaticToolString; + +#define GENESYS_USBHUB_FW_SIG_OFFSET 0xFC +#define GENESYS_USBHUB_FW_SIG_LEN 4 +#define GENESYS_USBHUB_FW_SIG_TEXT_HUB "XROM" +#define GENESYS_USBHUB_FW_SIG_TEXT_HUB_SIGN "SROM" + +#define GENESYS_USBHUB_CODE_SIZE_OFFSET 0xFB +#define GENESYS_USBHUB_VERSION_OFFSET 0x10E +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3521 0x221 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523 0x221 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3590 0x241 diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-plugin.c b/fwupd-1.8.6/plugins/genesys/fu-genesys-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..211007d8ba0fc0c754005422d5e73c619f7c3c69 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-plugin.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-genesys-plugin.h" +#include "fu-genesys-scaler-firmware.h" +#include "fu-genesys-usbhub-device.h" +#include "fu-genesys-usbhub-firmware.h" + +struct _FuGenesysPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuGenesysPlugin, fu_genesys_plugin, FU_TYPE_PLUGIN) + +static void +fu_genesys_plugin_init(FuGenesysPlugin *self) +{ +} + +static void +fu_genesys_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "GenesysScalerCfiFlashId"); + fu_context_add_quirk_key(ctx, "GenesysScalerGpioOutputRegister"); + fu_context_add_quirk_key(ctx, "GenesysScalerGpioEnableRegister"); + fu_context_add_quirk_key(ctx, "GenesysScalerGpioValue"); + fu_context_add_quirk_key(ctx, "GenesysUsbhubReadRequest"); + fu_context_add_quirk_key(ctx, "GenesysUsbhubSwitchRequest"); + fu_context_add_quirk_key(ctx, "GenesysUsbhubWriteRequest"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_GENESYS_USBHUB_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_GENESYS_USBHUB_FIRMWARE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_GENESYS_SCALER_FIRMWARE); +} + +static void +fu_genesys_plugin_class_init(FuGenesysPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_genesys_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-plugin.h b/fwupd-1.8.6/plugins/genesys/fu-genesys-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..dff7810cc974d89f1e4e878ecd96eee3ae03c002 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuGenesysPlugin, fu_genesys_plugin, FU, GENESYS_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-device.c b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-device.c new file mode 100644 index 0000000000000000000000000000000000000000..0ca4cfc94e5119d005b6e055dc37ec3cc3cdfe9a --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-device.c @@ -0,0 +1,1979 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-genesys-common.h" +#include "fu-genesys-scaler-device.h" +#include "fu-genesys-scaler-firmware.h" + +#define GENESYS_SCALER_BANK_SIZE 0x200000U + +#define GENESYS_SCALER_MSTAR_READ 0x7a +#define GENESYS_SCALER_MSTAR_WRITE 0x7b +#define GENESYS_SCALER_MSTAR_DATA_OUT 0x7c +#define GENESYS_SCALER_MSTAR_DATA_IN 0x7f + +#define GENESYS_SCALER_CMD_DDCCI_FIRMWARE_PACKET_VERSION 0x06 + +#define GENESYS_SCALER_CMD_DATA_WRITE 0x10 +#define GENESYS_SCALER_CMD_DATA_READ 0x11 +#define GENESYS_SCALER_CMD_DATA_END 0x12 + +#define GENESYS_SCALER_INFO 0xA4 + +#define GENESYS_SCALER_USB_TIMEOUT 5000 /* 5s */ + +/** + * FU_SCALER_FLAG_PAUSE_R2_CPU: + * + * Pause R2 CPU. + * + * Since 1.7.6 + */ +#define FU_SCALER_FLAG_PAUSE_R2_CPU (1 << 1) +/** + * FU_SCALER_FLAG_USE_I2C_CH0: + * + * Use I2C ch0. + * + * Since 1.7.6 + */ +#define FU_SCALER_FLAG_USE_I2C_CH0 (1 << 0) + +typedef struct { + guint8 req_read; + guint8 req_write; +} FuGenesysVendorCommand; + +typedef struct { + guint8 stage; + guint8 model; + guint8 major; + guint8 minor; +} FuGenesysScalerFirmwarePacketVersion; + +struct _FuGenesysScalerDevice { + FuDevice parent_instance; + guint8 level; + FuGenesysPublicKey public_key; + guint32 cfi_flash_id; + FuCfiDevice *cfi_device; + FuGenesysVendorCommand vc; + guint32 sector_size; + guint32 page_size; + guint32 transfer_size; + guint16 gpio_out_reg; + guint16 gpio_en_reg; + guint8 gpio_val; +}; + +G_DEFINE_TYPE(FuGenesysScalerDevice, fu_genesys_scaler_device, FU_TYPE_DEVICE) + +static gboolean +fu_genesys_scaler_device_enter_serial_debug_mode(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x53, 0x45, 0x52, 0x44, 0x42}; + + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error entering Serial Debug Mode: "); + return FALSE; + } + + g_usleep(1000); /* 1ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_exit_serial_debug_mode(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x45}; + + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error exiting Serial Debug Mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_enter_single_step_mode(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data1[] = {0x10, 0xc0, 0xc1, 0x53}; + guint8 data2[] = {0x10, 0x1f, 0xc1, 0x53}; + + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data1, /* data */ + sizeof(data1), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error entering Single Step Mode: "); + return FALSE; + } + + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data2, /* data */ + sizeof(data2), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error entering Single Step Mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_exit_single_step_mode(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x10, 0xc0, 0xc1, 0xff}; + + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error exiting Single Step Mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_enter_debug_mode(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x10, 0x00, 0x00, 0x00}; + + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error entering Debug Mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_mst_i2c_bus_ctrl(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x35, 0x71}; + + for (guint i = 0; i < sizeof(data); i++) { + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + &data[i], /* data */ + sizeof(data[i]), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending i2c bus ctrl 0x%02x: ", data[i]); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_mst_i2c_bus_switch_to_ch0(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x80, 0x82, 0x84, 0x51, 0x7f, 0x37, 0x61}; + + for (guint i = 0; i < sizeof(data); i++) { + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + &data[i], /* data */ + sizeof(data[i]), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending i2c bus ch0 0x%02x: ", data[i]); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_mst_i2c_bus_switch_to_ch4(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x80, 0x82, 0x85, 0x53, 0x7f}; + + for (guint i = 0; i < sizeof(data); i++) { + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + &data[i], /* data */ + sizeof(data[i]), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending i2c bus ch4 0x%02x: ", data[i]); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_disable_wp(FuGenesysScalerDevice *self, gboolean disable, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data_out[] = {0x10, + 0x00 /* gpio_out_reg_h */, + 0x00 /* gpio_out_reg_l */, + 0x00 /* gpio_out_val */}; + guint8 data_en[] = {0x10, + 0x00 /* gpio_en_reg_h */, + 0x00 /* gpio_en_reg_l */, + 0x00 /* gpio_en_val */}; + + /* disable write protect [output] */ + + data_out[1] = (self->gpio_out_reg & 0xff00) >> 8; + data_out[2] = self->gpio_out_reg & 0x00ff; + + /* read gpio-out register */ + 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, + self->vc.req_write, + 0x0003, /* value */ + 0x0000, /* idx */ + data_out, /* data */ + sizeof(data_out) - 1, /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading GPIO-Out Register 0x%02x%02x: ", + data_out[1], + data_out[2]); + return FALSE; + } + + 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, + self->vc.req_read, + 0x0003, /* value */ + 0x0000, /* idx */ + &data_out[3], /* data */ + sizeof(data_out[3]), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading GPIO-Out Register 0x%02x%02x: ", + data_out[1], + data_out[2]); + return FALSE; + } + + if (data_out[3] == 0xff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "error reading GPIO-Out Register 0x%02x%02x: ", + data_out[1], + data_out[2]); + return FALSE; + } + + if (disable) + data_out[3] |= self->gpio_val; /* pull high */ + else + data_out[3] &= ~self->gpio_val; /* pull low */ + + /* write gpio-out register */ + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data_out, /* data */ + sizeof(data_out), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error writing GPIO-Out Register 0x%02x%02x=0x%02x: ", + data_out[1], + data_out[2], + data_out[3]); + return FALSE; + } + + /* disable write protect [enable] */ + + data_en[1] = (self->gpio_en_reg & 0xff00) >> 8; + data_en[2] = self->gpio_en_reg & 0x00ff; + + /* read gpio-enable register */ + 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, + self->vc.req_write, + 0x0003, /* value */ + 0x0000, /* idx */ + data_en, /* data */ + sizeof(data_en) - 1, /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error writing GPIO-Enable Register 0x%02x%02x: ", + data_en[1], + data_en[2]); + return FALSE; + } + + 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, + self->vc.req_read, + 0x0003, /* value */ + 0x0000, /* idx */ + &data_en[3], /* data */ + sizeof(data_en[3]), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading GPIO-Out Register 0x%02x%02x: ", + data_en[1], + data_en[2]); + return FALSE; + } + + if (data_en[3] == 0xff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "error reading GPIO-Enable Register 0x%02x%02x: ", + data_en[1], + data_en[2]); + return FALSE; + } + + data_en[3] &= ~self->gpio_val; + + /* write gpio-enable register */ + 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, + self->vc.req_write, + 0x0001, /* value */ + 0x0000, /* idx */ + data_en, /* data */ + sizeof(data_en), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error writing GPIO-Enable Register 0x%02x%02x=0x%02x: ", + data_en[1], + data_en[2], + data_en[3]); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_pause_r2_cpu(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x10, 0x00, 0x10, 0x0F, 0xD7, 0x00}; + + /* + * MST9U only! + * + * Pause R2 CPU for preventing Scaler entering Power Saving Mode also + * need for Disable SPI Flash Write Protect Mode. + */ + 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, + self->vc.req_write, + 0x0003, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data) - 1, /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading register 0x%02x%02x%02x%02x%02x: ", + data[0], + data[1], + data[2], + data[3], + data[4]); + return FALSE; + } + + 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, + self->vc.req_read, + 0x0003, /* value */ + 0x0000, /* idx */ + &data[5], /* data */ + sizeof(data[5]), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading register 0x%02x%02x%02x%02x%02x: ", + data[0], + data[1], + data[2], + data[3], + data[4]); + return FALSE; + } + + if (data[5] == 0xff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "error reading register 0x%02x%02x%02x%02x%02x: ", + data[0], + data[1], + data[2], + data[3], + data[4]); + return FALSE; + } + + data[5] |= 0x80; + 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, + self->vc.req_write, + 0x0003, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error writing register 0x%02x%02x%02x%02x%02x: ", + data[0], + data[1], + data[2], + data[3], + data[4]); + return FALSE; + } + + g_usleep(200000); /* 200ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_set_isp_mode(FuDevice *device, gpointer user_data, GError **error) +{ + FuGenesysScalerDevice *self = user_data; + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x4d, 0x53, 0x54, 0x41, 0x52}; + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + return FALSE; + } + + g_usleep(1000); /* 1ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_enter_isp_mode(FuGenesysScalerDevice *self, GError **error) +{ + /* + * Enter ISP mode: + * + * Note: MStar application note say execute twice to avoid race + * condition + */ + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_genesys_scaler_device_set_isp_mode, + 2, + 1000 /* 1ms */, + self, + error)) { + g_prefix_error(error, "error entering ISP mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_exit_isp_mode(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x24}; + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error exiting ISP mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + + /* + * Important: do not change the order below; otherwise, unexpected + * condition occurs. + */ + + if (!fu_genesys_scaler_device_enter_serial_debug_mode(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_enter_single_step_mode(self, error)) + return FALSE; + + if (fu_device_has_private_flag(device, FU_SCALER_FLAG_USE_I2C_CH0)) + if (!fu_genesys_scaler_device_mst_i2c_bus_switch_to_ch0(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_enter_debug_mode(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_mst_i2c_bus_ctrl(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_disable_wp(self, TRUE, error)) + return FALSE; + + if (fu_device_has_private_flag(device, FU_SCALER_FLAG_PAUSE_R2_CPU)) { + if (!fu_genesys_scaler_device_mst_i2c_bus_switch_to_ch4(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_mst_i2c_bus_ctrl(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_pause_r2_cpu(self, error)) + return FALSE; + } + + if (!fu_genesys_scaler_device_enter_isp_mode(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + + if (!fu_genesys_scaler_device_exit_single_step_mode(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_exit_serial_debug_mode(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_exit_isp_mode(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_get_level(FuGenesysScalerDevice *self, guint8 *level, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + + 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, + GENESYS_SCALER_INFO, + 0x0004, /* value */ + 0x0000, /* idx */ + level, /* data */ + 1, /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error getting level: "); + return FALSE; + } + + g_usleep(100000); /* 100ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_get_version(FuGenesysScalerDevice *self, + guint8 *buf, + guint bufsz, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + + 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, + GENESYS_SCALER_INFO, + 0x0005, /* value */ + 0x0000, /* idx */ + buf, /* data */ + bufsz, /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error getting version: "); + return FALSE; + } + + g_usleep(100000); /* 100ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_get_public_key(FuGenesysScalerDevice *self, + guint8 *buf, + guint bufsz, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + const gsize data_size = 0x20; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_mutable_new(buf, bufsz, 0, 0, data_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!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, + GENESYS_SCALER_INFO, + 0x0006, /* value */ + fu_chunk_get_address(chk), /* idx */ + fu_chunk_get_data_out(chk), /* data */ + fu_chunk_get_data_sz(chk), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error getting public key: "); + return FALSE; + } + + g_usleep(100000); /* 100ms */ + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_read_flash(FuGenesysScalerDevice *self, + guint addr, + guint8 *buf, + guint bufsz, + FuProgress *progress, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data1[] = { + GENESYS_SCALER_CMD_DATA_WRITE, + 0x00, /* Read Data command */ + (addr & 0x00ff0000) >> 16, + (addr & 0x0000ff00) >> 8, + (addr & 0x000000ff), + }; + guint8 data2[] = { + GENESYS_SCALER_CMD_DATA_READ, + }; + guint8 data3[] = { + GENESYS_SCALER_CMD_DATA_END, + }; + g_autoptr(GPtrArray) chunks = NULL; + + if (!fu_cfi_device_get_cmd(self->cfi_device, FU_CFI_DEVICE_CMD_READ_DATA, &data1[1], error)) + return FALSE; + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data1, /* data */ + sizeof(data1), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error reading flash at 0x%06x: ", addr); + return FALSE; + } + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data2, /* data */ + sizeof(data2), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error reading flash at 0x%06x: ", addr); + return FALSE; + } + + chunks = fu_chunk_array_mutable_new(buf, bufsz, addr, 0, self->transfer_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!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, + self->vc.req_read, + 0x0000, /* value */ + 0x0000, /* idx */ + fu_chunk_get_data_out(chk), /* data */ + fu_chunk_get_data_sz(chk), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading flash at 0x%06x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data3, /* data */ + sizeof(data3), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error reading flash at 0x%06x: ", addr); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_wait_flash_control_register_cb(FuDevice *dev, + gpointer user_data, + GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(dev); + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + FuGenesysWaitFlashRegisterHelper *helper = user_data; + guint8 status = 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, + self->vc.req_read, + helper->reg << 8 | 0x04, /* value */ + 0x0000, /* idx */ + &status, /* data */ + sizeof(status), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error reading flash control register: "); + return FALSE; + } + + if ((status & 0x81) != helper->expected_val) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong value in flash control register"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_flash_control_write_enable(FuGenesysScalerDevice *self, GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data1[] = { + GENESYS_SCALER_CMD_DATA_WRITE, + 0x00, /* Write Enable command */ + }; + guint8 data2[] = { + GENESYS_SCALER_CMD_DATA_END, + }; + + if (!fu_cfi_device_get_cmd(self->cfi_device, FU_CFI_DEVICE_CMD_WRITE_EN, &data1[1], error)) + return FALSE; + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data1, /* data */ + sizeof(data1), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending flash control write enable: "); + return FALSE; + } + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data2, /* data */ + sizeof(data2), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending flash control write enable: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_flash_control_write_status(FuGenesysScalerDevice *self, + guint8 status, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data1[] = { + GENESYS_SCALER_CMD_DATA_WRITE, + 0x00, /* Write Status command */ + status, + }; + guint8 data2[] = { + GENESYS_SCALER_CMD_DATA_END, + }; + + if (!fu_cfi_device_get_cmd(self->cfi_device, + FU_CFI_DEVICE_CMD_WRITE_STATUS, + &data1[1], + error)) + return FALSE; + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data1, /* data */ + sizeof(data1), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending flash control write status 0x%02x: ", status); + return FALSE; + } + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data2, /* data */ + sizeof(data2), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error sending flash control write status 0x%02x: ", status); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_flash_control_sector_erase(FuGenesysScalerDevice *self, + guint addr, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + FuGenesysWaitFlashRegisterHelper helper = { + .reg = 0x00, /* Read Status command */ + .expected_val = 0, + }; + guint8 data1[] = { + GENESYS_SCALER_CMD_DATA_WRITE, + 0x00, /* Sector Erase command */ + (addr & 0x00ff0000) >> 16, + (addr & 0x0000ff00) >> 8, + (addr & 0x000000ff), + }; + guint8 data2[] = { + GENESYS_SCALER_CMD_DATA_END, + }; + + if (!fu_cfi_device_get_cmd(self->cfi_device, + FU_CFI_DEVICE_CMD_READ_STATUS, + &helper.reg, + error)) + return FALSE; + + if (!fu_cfi_device_get_cmd(self->cfi_device, + FU_CFI_DEVICE_CMD_SECTOR_ERASE, + &data1[1], + error)) + return FALSE; + + if (!fu_genesys_scaler_device_flash_control_write_enable(self, error)) + return FALSE; + + if (!fu_genesys_scaler_device_flash_control_write_status(self, 0x00, error)) + return FALSE; + + /* 5s */ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_genesys_scaler_device_wait_flash_control_register_cb, + 100, + 50, /* 50ms */ + &helper, + error)) { + g_prefix_error(error, "error waiting for flash control read status register: "); + return FALSE; + } + + if (!fu_genesys_scaler_device_flash_control_write_enable(self, error)) + return FALSE; + + if (!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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data1, /* data */ + sizeof(data1), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error sending flash control erase at address 0x%06x: ", + addr); + return FALSE; + } + + 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, + self->vc.req_write, + 0x0000, /* value */ + 0x0000, /* idx */ + data2, /* data */ + sizeof(data2), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error sending flash control erase at address 0x%06x: ", + addr); + return FALSE; + } + + /* 5s */ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_genesys_scaler_device_wait_flash_control_register_cb, + 100, + 50, /* 50ms */ + &helper, + error)) { + g_prefix_error(error, "error waiting for flash control read status register: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_erase_flash(FuGenesysScalerDevice *self, + guint addr, + guint bufsz, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new(NULL, bufsz, addr, 0, self->sector_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!fu_genesys_scaler_device_flash_control_sector_erase(self, + fu_chunk_get_address(chk), + error)) { + g_prefix_error(error, + "error erasing flash at address 0x%06x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_flash_control_page_program(FuGenesysScalerDevice *self, + guint addr, + const guint8 *buf, + guint bufsz, + FuProgress *progress, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + FuGenesysWaitFlashRegisterHelper helper = { + .reg = 0x00, /* Read Status command */ + .expected_val = 0, + }; + guint8 data1[] = { + GENESYS_SCALER_CMD_DATA_WRITE, + 0x00, /* Page Program command */ + (addr & 0x00ff0000) >> 16, + (addr & 0x0000ff00) >> 8, + (addr & 0x000000ff), + }; + gsize datasz = 0; + g_autoptr(GPtrArray) chunks = NULL; + g_autofree guint8 *data = NULL; + + if (!fu_cfi_device_get_cmd(self->cfi_device, + FU_CFI_DEVICE_CMD_READ_STATUS, + &helper.reg, + error)) + return FALSE; + + if (!fu_cfi_device_get_cmd(self->cfi_device, FU_CFI_DEVICE_CMD_PAGE_PROG, &data1[1], error)) + return FALSE; + + datasz = sizeof(data1) + bufsz; + data = g_malloc0(datasz); + if (!fu_memcpy_safe(data, + datasz, + 0, /* dst */ + data1, + sizeof(data1), + 0, /* src */ + sizeof(data1), + error)) + return FALSE; + if (!fu_memcpy_safe(data, + datasz, + sizeof(data1), /* dst */ + buf, + bufsz, + 0, /* src */ + bufsz, + error)) + return FALSE; + + chunks = + fu_chunk_array_mutable_new(data, datasz, addr + sizeof(data1), 0, self->transfer_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint16 index = 0x0010 * (i + 1); + /* last chunk */ + if ((i + 1) == chunks->len) + index |= 0x0080; + + 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, + self->vc.req_write, + index, /* value */ + 0x0000, /* idx */ + fu_chunk_get_data_out(chk), /* data */ + fu_chunk_get_data_sz(chk), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error( + error, + "error sending flash control page program at address 0x%06x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* 200ms */ + if (!fu_device_retry(FU_DEVICE(self), + fu_genesys_scaler_device_wait_flash_control_register_cb, + 20, + &helper, + error)) { + g_prefix_error(error, "error waiting for flash control read status register: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_write_sector(FuGenesysScalerDevice *self, + guint addr, + const guint8 *buf, + guint bufsz, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new(buf, bufsz, addr, 0, self->page_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!fu_genesys_scaler_device_flash_control_page_program( + self, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_write_flash(FuGenesysScalerDevice *self, + guint addr, + const guint8 *buf, + guint bufsz, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new(buf, bufsz, addr, 0, self->sector_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!fu_genesys_scaler_device_write_sector(self, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static guint8 +fu_genesys_scaler_device_calculate_checksum(guint8 *buf, gsize bufsz) +{ + guint8 checksum = 0x00; + + for (gsize i = 0; i < bufsz; i++) + checksum ^= buf[i]; + + return checksum; +} + +static gboolean +fu_genesys_scaler_device_get_ddcci_data(FuGenesysScalerDevice *self, + guint8 cmd, + guint8 *buf, + guint bufsz, + GError **error) +{ + FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent_device)); + guint8 data[] = {0x6e, 0x51, 0x83, 0xcd, 0x01, 0x00 /* command */, 0x00 /* checksum */}; + + data[5] = cmd; + data[6] = fu_genesys_scaler_device_calculate_checksum(data, 6); + + 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, + GENESYS_SCALER_MSTAR_DATA_OUT, + 0x0000, /* value */ + 0x0000, /* idx */ + data, /* data */ + sizeof(data), /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error setting dddci data: "); + return FALSE; + } + + g_usleep(100000); /* 100ms */ + + 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, + GENESYS_SCALER_MSTAR_DATA_IN, + 0x0001, /* value */ + 0x0000, /* idx */ + buf, /* data */ + bufsz, /* data length */ + NULL, /* actual length */ + GENESYS_SCALER_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error getting dddci data: "); + return FALSE; + } + + g_usleep(100000); /* 100ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_get_firmware_packet_version(FuGenesysScalerDevice *self, + FuGenesysScalerFirmwarePacketVersion *ver, + GError **error) +{ + guint8 buf[0x40]; + guint8 offset = 4; + + if (!fu_genesys_scaler_device_get_ddcci_data( + self, + GENESYS_SCALER_CMD_DDCCI_FIRMWARE_PACKET_VERSION, + buf, + sizeof(buf), + error)) + return FALSE; + + if (buf[0] == 0x6f && buf[1] == 0x6e) { + gsize len = buf[2] ^ 0x80; + guint8 checksum; + guint8 checksum_tmp = 0x0; + + if (len > sizeof(buf) - 3) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "error dddci length too large, got 0x%x, expected <= 0x%zx: ", + (guint)len, + sizeof(buf)); + return FALSE; + } + + buf[0] = 0x50; /* drifted value */ + checksum = fu_genesys_scaler_device_calculate_checksum(buf, len + 3); + if (!fu_memread_uint8_safe(buf, sizeof(buf), len + 3, &checksum_tmp, error)) + return FALSE; + if (checksum_tmp != checksum) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "error dddci checksum mismatch, got 0x%02x, expected 0x%02x", + checksum_tmp, + checksum); + return FALSE; + } + + offset = 7; + } + + ver->stage = buf[offset]; + ver->model = buf[offset + 1]; + ver->major = buf[offset + 2]; + ver->minor = buf[offset + 3]; + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_probe(FuDevice *device, GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + FuGenesysScalerFirmwarePacketVersion ver; + guint8 buf[7 + 1] = {0}; + g_autofree gchar *guid = NULL; + g_autofree gchar *version = NULL; + g_autofree gchar *panelrev = NULL; + + if (!fu_genesys_scaler_device_get_level(self, &self->level, error)) + return FALSE; + + if (!fu_genesys_scaler_device_get_public_key(self, + (guint8 *)&self->public_key, + sizeof(self->public_key), + error)) + return FALSE; + if (memcmp(self->public_key.N, "N = ", 4) != 0 || + memcmp(self->public_key.E, "E = ", 4) != 0) { + if (g_getenv("FWUPD_GENESYS_SCALER_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "PublicKey", + (const guint8 *)&self->public_key, + sizeof(self->public_key)); + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "invalid public-key"); + return FALSE; + } + guid = fwupd_guid_hash_data((const guint8 *)&self->public_key, + sizeof(self->public_key), + FWUPD_GUID_FLAG_NONE); + + if (!fu_genesys_scaler_device_get_version(self, buf, sizeof(buf), error)) + return FALSE; + /* ?xIM123; where ? is 0x06 (length?) */ + panelrev = fu_strsafe((const gchar *)&buf[1], 6); + + if (!fu_genesys_scaler_device_get_firmware_packet_version(self, &ver, error)) + return FALSE; + + version = g_strdup_printf("%d.%d.%d.%d", ver.stage, ver.model, ver.major, ver.minor); + fu_device_set_version(device, version); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_logical_id(device, "scaler"); + + /* add instance ID */ + fu_device_add_instance_str(device, "MSTAR", "TSUM_G"); + fu_device_add_instance_strup(device, "PUBKEY", guid); + fu_device_add_instance_strup(device, "PANELREV", panelrev); + fu_device_build_instance_id(device, NULL, "GENESYS_SCALER", "MSTAR", "PUBKEY", NULL); + fu_device_build_instance_id(device, + NULL, + "GENESYS_SCALER", + "MSTAR", + "PUBKEY", + "PANELREV", + NULL); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + + self->vc.req_read = GENESYS_SCALER_MSTAR_READ; + self->vc.req_write = GENESYS_SCALER_MSTAR_WRITE; + if (self->level != 1) { + self->vc.req_read += 3; + self->vc.req_write += 3; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_scaler_device_setup(FuDevice *device, GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + guint64 size_min = fu_device_get_firmware_size_max(device); + guint64 size; + guint32 sector_size; + guint32 page_size; + g_autofree gchar *flash_id = NULL; + + flash_id = g_strdup_printf("%06X", self->cfi_flash_id); + self->cfi_device = fu_cfi_device_new(fu_device_get_context(FU_DEVICE(self)), flash_id); + if (!fu_device_setup(FU_DEVICE(self->cfi_device), error)) + return FALSE; + + sector_size = fu_cfi_device_get_sector_size(self->cfi_device); + if (sector_size != 0) + self->sector_size = sector_size; + page_size = fu_cfi_device_get_page_size(self->cfi_device); + if (page_size != 0) + self->page_size = page_size; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE)) + size_min *= 2; + + size = fu_device_get_firmware_size_max(FU_DEVICE(self->cfi_device)); + if (size != 0 && size < size_min) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "CFI device too small, got 0x%x, expected >= 0x%x", + (guint)size, + (guint)size_min); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_genesys_scaler_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + gsize size = fu_cfi_device_get_size(self->cfi_device); + g_autofree guint8 *buf = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 99, NULL); + + /* require detach -> attach */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return NULL; + fu_progress_step_done(progress); + + buf = g_malloc0(size); + if (!fu_genesys_scaler_device_read_flash(self, + 0, + buf, + size, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_bytes_new_take(g_steal_pointer(&buf), size); +} + +static FuFirmware * +fu_genesys_scaler_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + g_autoptr(FuFirmware) firmware = fu_genesys_scaler_firmware_new(); + g_autoptr(GBytes) blob_payload = NULL; + g_autoptr(GBytes) blob_public_key = NULL; + + /* parse firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check public-key */ + blob_public_key = + fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_SIGNATURE, error); + if (blob_public_key == NULL) + return NULL; + if (g_getenv("FWUPD_GENESYS_SCALER_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "PublicKey", + g_bytes_get_data(blob_public_key, NULL), + g_bytes_get_size(blob_public_key)); + } + if (memcmp(g_bytes_get_data(blob_public_key, NULL), + &self->public_key, + sizeof(self->public_key)) != 0 && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "mismatch public-key"); + return NULL; + } + + /* check size */ + blob_payload = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_PAYLOAD, error); + if (blob_payload == NULL) + return NULL; + if (g_bytes_get_size(blob_payload) > fu_device_get_firmware_size_max(device)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware too large, got 0x%x, expected <= 0x%x", + (guint)g_bytes_get_size(blob_payload), + (guint)fu_device_get_firmware_size_max(device)); + return NULL; + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_genesys_scaler_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + guint addr = 0; + gsize size; + const guint8 *data; + g_autofree guint8 *buf = NULL; + g_autoptr(FuFirmware) payload = NULL; + g_autoptr(GBytes) fw_payload = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 4, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 54, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 42, NULL); + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE)) + addr = GENESYS_SCALER_BANK_SIZE; + + payload = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_PAYLOAD, error); + if (payload == NULL) + return FALSE; + fw_payload = fu_firmware_get_bytes(payload, error); + if (fw_payload == NULL) + return FALSE; + data = g_bytes_get_data(fw_payload, &size); + if (data == NULL) + return FALSE; + + if (!fu_genesys_scaler_device_erase_flash(self, + addr, + size, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_genesys_scaler_device_write_flash(self, + addr, + data, + size, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + buf = g_malloc0(size); + if (!fu_genesys_scaler_device_read_flash(self, + addr, + buf, + size, + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_memcmp_safe(buf, size, data, size, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_genesys_scaler_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_genesys_scaler_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + gchar public_key_e[6 + 1] = {0}; + gchar public_key_n[0x200 + 1] = {0}; + g_autoptr(GError) error_local_e = NULL; + g_autoptr(GError) error_local_n = NULL; + + fu_string_append_kx(str, idt, "Level", self->level); + if (fu_memcpy_safe((guint8 *)public_key_e, + sizeof(public_key_e), + 0, /* dst */ + (const guint8 *)&self->public_key, + sizeof(self->public_key), + sizeof(self->public_key) - 2 - (sizeof(public_key_e) - 1), /* src */ + sizeof(public_key_e) - 1, + &error_local_e)) { + fu_string_append(str, idt, "PublicKeyE", public_key_e); + } else { + g_debug("ignoring public-key parameter E: %s", error_local_e->message); + } + if (fu_memcpy_safe((guint8 *)public_key_n, + sizeof(public_key_n), + 0, /* dst */ + (const guint8 *)&self->public_key, + sizeof(self->public_key), + 4, /* src */ + sizeof(public_key_n) - 1, + &error_local_n)) { + fu_string_append(str, idt, "PublicKeyN", public_key_n); + } else { + g_debug("ignoring public-key parameter N: %s", error_local_n->message); + } + fu_string_append_kx(str, idt, "ReadRequestRead", self->vc.req_read); + fu_string_append_kx(str, idt, "WriteRequest", self->vc.req_write); + fu_string_append_kx(str, idt, "SectorSize", self->sector_size); + fu_string_append_kx(str, idt, "PageSize", self->page_size); + fu_string_append_kx(str, idt, "TransferSize", self->transfer_size); + fu_string_append_kx(str, idt, "GpioOutputRegister", self->gpio_out_reg); + fu_string_append_kx(str, idt, "GpioEnableRegister", self->gpio_en_reg); + fu_string_append_kx(str, idt, "GpioValue", self->gpio_val); + fu_string_append_kx(str, idt, "CfiFlashId", self->cfi_flash_id); +} + +static gboolean +fu_genesys_scaler_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + guint64 tmp; + + if (g_strcmp0(key, "GenesysScalerDeviceTransferSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->transfer_size = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysScalerGpioOutputRegister") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->gpio_out_reg = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysScalerGpioEnableRegister") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->gpio_en_reg = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysScalerGpioValue") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + self->gpio_val = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysScalerCfiFlashId") == 0) { + if (!fu_strtoull(value, &tmp, 0, 0x00ffffffU, error)) + return FALSE; + self->cfi_flash_id = tmp; + + /* success */ + return TRUE; + } + + /* failure */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_genesys_scaler_device_init(FuGenesysScalerDevice *self) +{ + fu_device_set_vendor(FU_DEVICE(self), "MStar Semiconductor"); + fu_device_set_name(FU_DEVICE(self), "TSUMG"); + fu_device_add_protocol(FU_DEVICE(self), "com.mstarsemi.scaler"); + fu_device_retry_set_delay(FU_DEVICE(self), 10); /* 10ms */ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_register_private_flag(FU_DEVICE(self), + FU_SCALER_FLAG_PAUSE_R2_CPU, + "pause-r2-cpu"); + fu_device_register_private_flag(FU_DEVICE(self), FU_SCALER_FLAG_USE_I2C_CH0, "use-i2c-ch0"); + fu_device_set_install_duration(FU_DEVICE(self), 730); /* 12min 10s */ + + self->sector_size = 0x1000; /* 4KB */ + self->page_size = 0x100; /* 256B */ + self->transfer_size = 0x40; /* 64B */ + fu_device_set_firmware_size(FU_DEVICE(self), GENESYS_SCALER_BANK_SIZE); /* 2MB */ +} + +static void +fu_genesys_scaler_device_class_init(FuGenesysScalerDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_genesys_scaler_device_probe; + klass_device->setup = fu_genesys_scaler_device_setup; + klass_device->dump_firmware = fu_genesys_scaler_device_dump_firmware; + klass_device->prepare_firmware = fu_genesys_scaler_device_prepare_firmware; + klass_device->write_firmware = fu_genesys_scaler_device_write_firmware; + klass_device->set_progress = fu_genesys_scaler_device_set_progress; + klass_device->detach = fu_genesys_scaler_device_detach; + klass_device->attach = fu_genesys_scaler_device_attach; + klass_device->to_string = fu_genesys_scaler_device_to_string; + klass_device->set_quirk_kv = fu_genesys_scaler_device_set_quirk_kv; +} + +FuGenesysScalerDevice * +fu_genesys_scaler_device_new(FuContext *ctx) +{ + FuGenesysScalerDevice *device = NULL; + device = g_object_new(FU_TYPE_GENESYS_SCALER_DEVICE, "context", ctx, NULL); + return device; +} diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-device.h b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ca59191ded150d06ae2fa5b0d6ccf5c4210d759f --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_GENESYS_SCALER_DEVICE (fu_genesys_scaler_device_get_type()) +G_DECLARE_FINAL_TYPE(FuGenesysScalerDevice, + fu_genesys_scaler_device, + FU, + GENESYS_SCALER_DEVICE, + FuDevice) + +FuGenesysScalerDevice * +fu_genesys_scaler_device_new(FuContext *ctx); diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-firmware.c b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..a35b0783b6e20c178f37e7e788cb1fc0cd18cde8 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-firmware.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-genesys-scaler-firmware.h" + +struct _FuGenesysScalerFirmware { + FuFirmwareClass parent_instance; + FuGenesysPublicKey public_key; +}; + +G_DEFINE_TYPE(FuGenesysScalerFirmware, fu_genesys_scaler_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_genesys_scaler_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(FuFirmware) firmware_payload = NULL; + g_autoptr(FuFirmware) firmware_public_key = NULL; + g_autoptr(GBytes) blob_payload = NULL; + g_autoptr(GBytes) blob_public_key = NULL; + + if (!fu_memcpy_safe((guint8 *)&self->public_key, + sizeof(self->public_key), + 0, /* dst */ + buf, + bufsz, + bufsz - sizeof(self->public_key), /* src */ + sizeof(self->public_key), + error)) + return FALSE; + fu_dump_raw(G_LOG_DOMAIN, + "PublicKey", + (const guint8 *)&self->public_key, + sizeof(self->public_key)); + if (memcmp(self->public_key.N, "N = ", 4) != 0 || + memcmp(self->public_key.E, "E = ", 4) != 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid public-key"); + return FALSE; + } + + /* set payload */ + blob_payload = g_bytes_new(buf, bufsz - sizeof(self->public_key)); + firmware_payload = fu_firmware_new_from_bytes(blob_payload); + fu_firmware_set_id(firmware_payload, FU_FIRMWARE_ID_PAYLOAD); + fu_firmware_add_image(firmware, firmware_payload); + + /* set public-key */ + blob_public_key = g_bytes_new(&self->public_key, sizeof(self->public_key)); + firmware_public_key = fu_firmware_new_from_bytes(blob_public_key); + fu_firmware_set_id(firmware_public_key, FU_FIRMWARE_ID_SIGNATURE); + fu_firmware_add_image(firmware, firmware_public_key); + + /* success */ + return TRUE; +} + +static void +fu_genesys_scaler_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware); + gchar N[0x200 + 1] = {'\0'}; + gchar E[0x006 + 1] = {'\0'}; + + memcpy(N, self->public_key.N + 4, sizeof(N) - 1); + fu_xmlb_builder_insert_kv(bn, "N", N); + + memcpy(E, self->public_key.E + 4, sizeof(E) - 1); + fu_xmlb_builder_insert_kv(bn, "E", E); +} + +static gboolean +fu_genesys_scaler_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware); + const gchar *tmp; + + /* optional properties */ + tmp = xb_node_query_text(n, "N", NULL); + if (tmp != NULL) { + if (!fu_memcpy_safe((guint8 *)&self->public_key.N, + sizeof(self->public_key.N), + 0x0, /* dst */ + (const guint8 *)tmp, + strlen(tmp), + 0x0, /* src */ + strlen(tmp), + error)) + return FALSE; + } + + tmp = xb_node_query_text(n, "E", NULL); + if (tmp != NULL) { + if (!fu_memcpy_safe((guint8 *)&self->public_key.E, + sizeof(self->public_key.E), + 0x0, /* dst */ + (const guint8 *)tmp, + strlen(tmp), + 0x0, /* src */ + strlen(tmp), + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_genesys_scaler_firmware_write(FuFirmware *firmware, GError **error) +{ + FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + /* payload */ + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + + /* public-key */ + g_byte_array_append(buf, (const guint8 *)&self->public_key, sizeof(self->public_key)); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_genesys_scaler_firmware_init(FuGenesysScalerFirmware *self) +{ +} + +static void +fu_genesys_scaler_firmware_class_init(FuGenesysScalerFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_genesys_scaler_firmware_parse; + klass_firmware->export = fu_genesys_scaler_firmware_export; + klass_firmware->build = fu_genesys_scaler_firmware_build; + klass_firmware->write = fu_genesys_scaler_firmware_write; +} + +FuFirmware * +fu_genesys_scaler_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_GENESYS_SCALER_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-firmware.h b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..bab466f4b7747b6f22b13560f1e8af2d7d1a7216 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-scaler-firmware.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-genesys-common.h" + +#define FU_TYPE_GENESYS_SCALER_FIRMWARE (fu_genesys_scaler_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuGenesysScalerFirmware, + fu_genesys_scaler_firmware, + FU, + GENESYS_SCALER_FIRMWARE, + FuFirmware) + +#define GENESYS_SCALER_BANK_SIZE 0x200000U + +FuFirmware * +fu_genesys_scaler_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-device.c b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-device.c new file mode 100644 index 0000000000000000000000000000000000000000..bfa7a1b14b857d7a75f10438fe4c96bb8c40c547 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-device.c @@ -0,0 +1,1576 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-genesys-common.h" +#include "fu-genesys-scaler-device.h" +#include "fu-genesys-usbhub-device.h" +#include "fu-genesys-usbhub-firmware.h" + +/** + * FU_GENESYS_USBHUB_FLAG_HAS_MSTAR_SCALER: + * + * Device has a MStar scaler attached via I2C. + * + * Since 1.7.6 + */ +#define FU_GENESYS_USBHUB_FLAG_HAS_MSTAR_SCALER (1 << 0) +/** + * FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY: + * + * Device has a public-key appended to firmware. + * + * Since 1.8.0 + */ +#define FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY (1 << 1) + +#define GENESYS_USBHUB_STATIC_TOOL_DESC_IDX_USB_3_0 0x84 +#define GENESYS_USBHUB_DYNAMIC_TOOL_DESC_IDX_USB_3_0 0x85 +#define GENESYS_USBHUB_STATIC_TOOL_DESC_IDX_USB_2_0 0x81 +#define GENESYS_USBHUB_DYNAMIC_TOOL_DESC_IDX_USB_2_0 0x82 +#define GENESYS_USBHUB_FW_INFO_DESC_IDX 0x83 +#define GENESYS_USBHUB_VENDOR_SUPPORT_DESC_IDX 0x86 + +#define GENESYS_USBHUB_GL_HUB_VERIFY 0x71 +#define GENESYS_USBHUB_GL_HUB_SWITCH 0x81 +#define GENESYS_USBHUB_GL_HUB_READ 0x82 +#define GENESYS_USBHUB_GL_HUB_WRITE 0x83 + +#define GENESYS_USBHUB_ENCRYPT_REGION_START 0x01 +#define GENESYS_USBHUB_ENCRYPT_REGION_END 0x15 + +#define GL3523_PUBLIC_KEY_LEN 0x212 +#define GL3523_SIG_LEN 0x100 + +#define GENESYS_USBHUB_USB_TIMEOUT 5000 /* ms */ +#define GENESYS_USBHUB_FLASH_WRITE_TIMEOUT 500 /* ms */ + +typedef enum { + TOOL_STRING_VERSION_9BYTE_DYNAMIC, + TOOL_STRING_VERSION_BONDING, + TOOL_STRING_VERSION_BONDING_QC, + TOOL_STRING_VERSION_VENDOR_SUPPORT, + TOOL_STRING_VERSION_MULTI_TOKEN, + TOOL_STRING_VERSION_2ND_DYNAMIC, + TOOL_STRING_VERSION_RESERVED, + TOOL_STRING_VERSION_13BYTE_DYNAMIC, +} FuGenesysToolStringVersion; + +typedef struct __attribute__((packed)) { + guint8 running_mode; /* 'M' for mask code, the others for bank code */ + + guint8 ss_port_number; /* super-speed port number */ + guint8 hs_port_number; /* high-speed port number */ + + guint8 ss_connection_status; /* bit field. ON = DFP is a super-speed device */ + guint8 hs_connection_status; /* bit field. ON = DFP is a high-speed device */ + guint8 fs_connection_status; /* bit field. ON = DFP is a full-speed device */ + guint8 ls_connection_status; /* bit field. ON = DFP is a low-speed device */ + + guint8 charging; /* bit field. ON = DFP is a charging port */ + guint8 non_removable_port_status; /* bit field. ON = DFP is a non-removable port */ + + /* + * Bonding reports Hardware register status for GL3523: + * 2 / 4 ports : 1 means 4 ports, 0 means 2 ports + * MTT / STT : 1 means Multi Token Transfer, 0 means Single TT + * Type - C : 1 means disable, 0 means enable + * QC : 1 means disable, 0 means enable + * Flash dump location : 1 means 32KB offset bank 1, 0 means 0 offset bank 0. + * + * Tool string Version 1: + * Bit3 : Flash dump location + * BIT2 : Type - C + * BIT1 : MTT / STT + * BIT0 : 2 / 4 ports + * + * Tool string Version 2 or newer : + * Bit4 : Flash dump location + * BIT3 : Type - C + * BIT2 : MTT / STT + * BIT1 : 2 / 4 ports + * BIT0 : QC + * + * Default use '0'~'F', plus Bit4 may over value, should extract that. + * + * Bonding for GL3590: + * Bit7 : Flash dump location, 0 means bank 0, 1 means bank 1. + */ + guint8 bonding; + + guint8 reserved[22]; +} FuGenesysDynamicToolString; + +typedef enum { + BANK_MASK_CODE, + BANK_FIRST, + BANK_SECOND, +} FuGenesysRunningBank; + +#define GL3523_BONDING_VALID_BIT 0x0F +#define GL3590_BONDING_VALID_BIT 0x7F + +#define GL3523_BONDING_FLASH_DUMP_LOCATION_BIT 1 << 4 +#define GL3590_BONDING_FLASH_DUMP_LOCATION_BIT 1 << 7 + +typedef enum { + ISP_EXIT, + ISP_ENTER, +} FuGenesysIspMode; + +typedef struct __attribute__((packed)) { + guint8 tool_version[6]; /* ISP tool defined by itself */ + guint8 address_mode; + guint8 build_fw_time[12]; /* YYYYMMDDhhmm */ + guint8 update_fw_time[12]; /* YYYYMMDDhhmm */ +} FuGenesysFirmwareInfoToolString; + +typedef struct __attribute__((packed)) { + guint8 version[2]; + guint8 supports[29]; +} FuGenesysVendorSupportToolString; + +typedef struct { + guint8 req_switch; + guint8 req_read; + guint8 req_write; +} FuGenesysVendorCommandSetting; + +struct _FuGenesysUsbhubDevice { + FuUsbDevice parent_instance; + FuGenesysStaticToolString static_ts; + FuGenesysDynamicToolString dynamic_ts; + FuGenesysFirmwareInfoToolString fwinfo_ts; + FuGenesysVendorSupportToolString vs_ts; + FuGenesysVendorCommandSetting vcs; + FuGenesysChip chip; + guint32 running_bank; + guint8 bonding; + gboolean flash_dump_location_bit; + guint32 flash_erase_delay; + guint32 flash_write_delay; + guint32 flash_block_size; + guint32 flash_sector_size; + guint32 flash_rw_size; + + guint32 fw_bank_addr[2]; + guint16 fw_bank_vers[2]; + guint32 code_size; /* 0: get from device */ + guint32 fw_data_total_count; + guint32 extend_size; + gboolean read_first_bank; + gboolean write_recovery_bank; + + FuGenesysPublicKey public_key; + FuCfiDevice *cfi_device; +}; + +G_DEFINE_TYPE(FuGenesysUsbhubDevice, fu_genesys_usbhub_device, FU_TYPE_USB_DEVICE) + +#if G_USB_CHECK_VERSION(0, 3, 8) +static gboolean +fu_genesys_usbhub_device_mstar_scaler_setup(FuGenesysUsbhubDevice *self, GError **error) +{ + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + g_autoptr(FuGenesysScalerDevice) scaler_device = fu_genesys_scaler_device_new(ctx); + + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(scaler_device)); + + /* success */ + return TRUE; +} +#endif + +static gboolean +fu_genesys_usbhub_device_read_flash(FuGenesysUsbhubDevice *self, + guint start_addr, + guint8 *buf, + guint bufsz, + FuProgress *progress, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_mutable_new(buf, bufsz, start_addr, 0x0, self->flash_rw_size); + if (progress != NULL) { + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + } + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!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, + self->vcs.req_read, + (fu_chunk_get_address(chk) & 0x0f0000) >> + 4, /* value */ + fu_chunk_get_address(chk) & 0xffff, /* idx */ + fu_chunk_get_data_out(chk), /* data */ + fu_chunk_get_data_sz(chk), /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error reading flash at 0x%04x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + if (progress != NULL) + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_reset(FuGenesysUsbhubDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + + /* send data to device */ + 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, + self->vcs.req_switch, + 0x0003, /* value */ + 0, /* idx */ + NULL, /* data */ + 0, /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error resetting device: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +#if G_USB_CHECK_VERSION(0, 3, 8) +static FuCfiDevice * +fu_genesys_usbhub_device_cfi_setup(FuGenesysUsbhubDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + const guint8 rdid_dummy_addr[] = {0x01, 0x02}; + const guint8 rdid_cmd[] = {0x9f, 0x90, 0xAB, 0x1D, 0x15, 0x4D, 0x4B}; + + for (guint8 i = 0; i < G_N_ELEMENTS(rdid_cmd); i++) { + for (guint8 j = 0; j < G_N_ELEMENTS(rdid_dummy_addr); j++) { + guint16 val = ((guint16)rdid_cmd[i] << 8) | rdid_dummy_addr[j]; + guint8 buf[2 * 3] = {0}; /* 2 x 3-bytes JEDEC-ID-bytes */ + g_autoptr(GError) error_local = NULL; + g_autoptr(FuCfiDevice) cfi_device = NULL; + g_autofree gchar *flash_id = NULL; + + 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, + self->vcs.req_read, + val, /* value */ + 0, /* idx */ + buf, /* data */ + sizeof(buf), /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "error reading flash chip: "); + return NULL; + } + + flash_id = g_strdup_printf("%02X%02X%02X", buf[0], buf[1], buf[2]); + cfi_device = + fu_cfi_device_new(fu_device_get_context(FU_DEVICE(self)), flash_id); + if (cfi_device == NULL) + continue; + + if (!fu_device_setup(FU_DEVICE(cfi_device), &error_local)) { + g_debug("ignoring %s: %s", flash_id, error_local->message); + continue; + } + + if (fu_device_get_name(FU_DEVICE(cfi_device)) == NULL) + continue; + + if (g_getenv("FWUPD_GENESYS_USBHUB_VERBOSE") != NULL) { + guint len; + + /* + * The USB vendor command loops over the JEDEC-ID-bytes. + * + * Therefore, the CFI is 3-bytes long if the first 3-bytes are + * identical to the last 3-bytes. + */ + if (buf[0] == buf[3] && buf[1] == buf[4] && buf[2] == buf[5]) + len = 3; + else + len = 2; + + fu_dump_raw(G_LOG_DOMAIN, "Flash ID", buf, len); + g_debug("CFI: %s", fu_device_get_name(FU_DEVICE(cfi_device))); + } + + return g_steal_pointer(&cfi_device); + } + } + + /* failure */ + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no CFI device found"); + return NULL; +} +#endif + +static gboolean +fu_genesys_usbhub_device_wait_flash_status_register_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + guint8 status = 0; + FuGenesysWaitFlashRegisterHelper *helper = user_data; + + 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, + self->vcs.req_read, + helper->reg << 8 | 0x02, /* value */ + 0, /* idx */ + &status, /* data */ + 1, /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error getting flash status register (0x%02x): ", + helper->reg); + return FALSE; + } + if (status != helper->expected_val) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong value in flash status register"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_set_isp_mode(FuGenesysUsbhubDevice *self, + FuGenesysIspMode mode, + 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, + self->vcs.req_switch, + mode, /* value */ + 0, /* idx */ + NULL, /* data */ + 0, /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error setting isp mode - " + "control transfer error (reg 0x%02x) ", + self->vcs.req_switch); + return FALSE; + } + + if (mode == ISP_ENTER) { + FuGenesysWaitFlashRegisterHelper helper = {.reg = 5, .expected_val = 0}; + + /* 150ms */ + if (!fu_device_retry(FU_DEVICE(self), + fu_genesys_usbhub_device_wait_flash_status_register_cb, + 5, + &helper, + error)) { + g_prefix_error(error, "error setting isp mode: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_authentication_request(FuGenesysUsbhubDevice *self, + guint8 offset_start, + guint8 offset_end, + guint8 data_check, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 buf = 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, + GENESYS_USBHUB_GL_HUB_VERIFY, + (offset_end << 8) | offset_start, /* value */ + 0, /* idx */ + &buf, /* data */ + 1, /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "control transfer error (req: 0x%0x): ", + (guint)GENESYS_USBHUB_GL_HUB_VERIFY); + return FALSE; + } + 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, + GENESYS_USBHUB_GL_HUB_VERIFY, + (offset_end << 8) | offset_start, /* value */ + 1 | (data_check << 8), /* idx */ + &buf, /* data */ + 1, /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "control transfer error (req: 0x%0x): ", + (guint)GENESYS_USBHUB_GL_HUB_VERIFY); + return FALSE; + } + if (buf != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "device authentication failed"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_authenticate(FuGenesysUsbhubDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 low_byte; + guint8 high_byte; + guint8 temp_byte; + guint8 offset_start; + guint8 offset_end; + guint8 *fwinfo = (guint8 *)&self->fwinfo_ts; + + if (self->vcs.req_switch == GENESYS_USBHUB_GL_HUB_SWITCH) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device authentication not supported"); + return FALSE; + } + + low_byte = g_usb_device_get_release(usb_device) & 0xff; + high_byte = (g_usb_device_get_release(usb_device) & 0xff00) >> 8; + temp_byte = low_byte ^ high_byte; + + offset_start = g_random_int_range(GENESYS_USBHUB_ENCRYPT_REGION_START, + GENESYS_USBHUB_ENCRYPT_REGION_END - 1); + offset_end = g_random_int_range(offset_start + 1, GENESYS_USBHUB_ENCRYPT_REGION_END); + for (guint8 i = offset_start; i <= offset_end; i++) { + temp_byte ^= fwinfo[i]; + } + if (!fu_genesys_usbhub_device_authentication_request(self, + offset_start, + offset_end, + temp_byte, + error)) { + g_prefix_error(error, "error authenticating device: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +#if G_USB_CHECK_VERSION(0, 3, 8) +static gboolean +fu_genesys_usbhub_device_get_descriptor_data(GBytes *desc_bytes, + guint8 *dst, + guint dst_size, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(desc_bytes, &bufsz); + + if (bufsz <= 2) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "data is too small"); + return FALSE; + } + + /* discard first 2 bytes (desc. length and type) */ + buf += 2; + bufsz -= 2; + for (gsize i = 0, j = 0; i < bufsz && j < dst_size; i += 2, j++) + dst[j] = buf[i]; + + /* legacy hub replies "USB2.0 Hub" or "USB3.0 Hub" */ + if (memcmp(dst, "USB", 3) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "tool string unsupported"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_check_fw_signature(FuGenesysUsbhubDevice *self, + int bank_num, + GError **error) +{ + guint8 sig[GENESYS_USBHUB_FW_SIG_LEN] = {0}; + g_return_val_if_fail(bank_num < 2, FALSE); + + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[bank_num] + + GENESYS_USBHUB_FW_SIG_OFFSET, + sig, + GENESYS_USBHUB_FW_SIG_LEN, + NULL, + error)) { + g_prefix_error(error, + "error getting fw signature (bank %d) from device: ", + bank_num); + return FALSE; + } + if (memcmp(sig, GENESYS_USBHUB_FW_SIG_TEXT_HUB, GENESYS_USBHUB_FW_SIG_LEN) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "wrong firmware signature"); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* read the code size from the firmware stored in the device */ +static gboolean +fu_genesys_usbhub_device_get_code_size(FuGenesysUsbhubDevice *self, int bank_num, GError **error) +{ + guint8 kbs = 0; + g_return_val_if_fail(bank_num < 2, FALSE); + + if (!fu_genesys_usbhub_device_check_fw_signature(self, bank_num, error)) + return FALSE; + + /* get code size from device */ + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[bank_num] + + GENESYS_USBHUB_CODE_SIZE_OFFSET, + &kbs, + 1, + NULL, + error)) { + g_prefix_error(error, "error getting fw size from device: "); + return FALSE; + } + self->code_size = 1024 * kbs; + + /* success */ + return TRUE; +} + +static gint +fu_genesys_tsdigit_value(gchar c) +{ + if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + return g_ascii_digit_value(c); +} + +static gboolean +fu_genesys_usbhub_device_get_bonding_and_flash_dump_location_bit(FuGenesysUsbhubDevice *self, + GError **error) +{ + gint bonding; + + /* bonding is not supported */ + if (fu_genesys_tsdigit_value(self->static_ts.tool_string_version) < + TOOL_STRING_VERSION_BONDING || + self->dynamic_ts.bonding == 0) { + /* success */ + return TRUE; + } + + if (self->chip.model == ISP_MODEL_HUB_GL3590) { + self->bonding = (self->dynamic_ts.bonding & GL3590_BONDING_VALID_BIT); + self->flash_dump_location_bit = + self->dynamic_ts.bonding & GL3590_BONDING_FLASH_DUMP_LOCATION_BIT; + + /* success */ + return TRUE; + } + + bonding = fu_genesys_tsdigit_value((gchar)self->dynamic_ts.bonding); + if (bonding == -1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong bonding value 0x%02x", + self->dynamic_ts.bonding); + return FALSE; + } + + if (fu_genesys_tsdigit_value(self->static_ts.tool_string_version) < + TOOL_STRING_VERSION_BONDING_QC) + bonding <<= 1; + self->bonding = (bonding & GL3523_BONDING_VALID_BIT); + self->flash_dump_location_bit = bonding & GL3523_BONDING_FLASH_DUMP_LOCATION_BIT; + + /* success */ + return TRUE; +} +#endif + +static gboolean +fu_genesys_usbhub_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + if (fu_device_has_private_flag(device, FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY)) { + if (!fu_genesys_usbhub_device_authenticate(self, error)) + return FALSE; + } + if (!fu_genesys_usbhub_device_set_isp_mode(self, ISP_ENTER, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + if (!fu_genesys_usbhub_device_reset(self, error)) + return FALSE; + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static GBytes * +fu_genesys_usbhub_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + gsize size = fu_cfi_device_get_size(self->cfi_device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree guint8 *buf = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 99, NULL); + + /* require detach -> attach */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return NULL; + fu_progress_step_done(progress); + + buf = g_malloc0(size); + if (!fu_genesys_usbhub_device_read_flash(self, + 0, + buf, + size, + fu_progress_get_child(progress), + error)) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_bytes_new_take(g_steal_pointer(&buf), size); +} + +static gboolean +fu_genesys_usbhub_device_setup(FuDevice *device, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 8) + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + gsize bufsz; + guint32 block_size; + guint32 sector_size; + guint64 revision_tmp = 0; + guint16 version_raw; + guint8 static_idx = 0; + guint8 dynamic_idx = 0; + gchar rev[3] = {0}; + gint tool_string_version = 0; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) static_buf = NULL; + g_autoptr(GBytes) dynamic_buf = NULL; + g_autoptr(GBytes) fw_buf = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GBytes) blob = NULL; + g_autofree guint8 *buf = NULL; + g_autofree gchar *ic_type = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_genesys_usbhub_device_parent_class)->setup(device, error)) { + g_prefix_error(error, "error setuping device: "); + return FALSE; + } + + /* [DEBUG] - additional info from device: + * release version: g_usb_device_get_release(usb_device) + */ + + /* read standard string descriptors */ + if (g_usb_device_get_spec(usb_device) >= 0x300) { + static_idx = GENESYS_USBHUB_STATIC_TOOL_DESC_IDX_USB_3_0; + dynamic_idx = GENESYS_USBHUB_DYNAMIC_TOOL_DESC_IDX_USB_3_0; + } else { + static_idx = GENESYS_USBHUB_STATIC_TOOL_DESC_IDX_USB_2_0; + dynamic_idx = GENESYS_USBHUB_DYNAMIC_TOOL_DESC_IDX_USB_2_0; + } + + /* + * Read/parse vendor-specific string descriptors and use that + * data to setup device attributes. + */ + static_buf = + g_usb_device_get_string_descriptor_bytes_full(usb_device, + static_idx, + G_USB_DEVICE_LANGID_ENGLISH_UNITED_STATES, + 64, + error); + if (static_buf == NULL) { + g_prefix_error(error, "failed to get static tool info from device: "); + return FALSE; + } + if (!fu_genesys_usbhub_device_get_descriptor_data(static_buf, + (guint8 *)&self->static_ts, + sizeof(FuGenesysStaticToolString), + error)) { + g_prefix_error(error, "failed to get static tool info from device: "); + return FALSE; + } + if (g_getenv("FWUPD_GENESYS_USBHUB_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "Static info", + (guint8 *)&self->static_ts, + sizeof(FuGenesysStaticToolString)); + } + + if (memcmp(self->static_ts.mask_project_ic_type, "3521", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3521; + } else if (memcmp(self->static_ts.mask_project_ic_type, "3523", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3523; + } else if (memcmp(self->static_ts.mask_project_ic_type, "3590", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3590; + } else { + ic_type = fu_strsafe((const gchar *)&self->static_ts.mask_project_ic_type, + sizeof(self->static_ts.mask_project_ic_type)); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "IC type %s not supported", + ic_type); + return FALSE; + } + memcpy(rev, &self->static_ts.mask_project_ic_type[4], 2); + + if (!fu_strtoull(rev, &revision_tmp, 0, G_MAXINT32, error)) { + g_prefix_error(error, "failed to parse %s: ", rev); + return FALSE; + } + self->chip.revision = revision_tmp; + + dynamic_buf = + g_usb_device_get_string_descriptor_bytes_full(usb_device, + dynamic_idx, + G_USB_DEVICE_LANGID_ENGLISH_UNITED_STATES, + 64, + error); + if (dynamic_buf == NULL) { + g_prefix_error(error, "failed to get dynamic tool info from device: "); + return FALSE; + } + if (!fu_genesys_usbhub_device_get_descriptor_data(dynamic_buf, + (guint8 *)&self->dynamic_ts, + sizeof(FuGenesysDynamicToolString), + error)) { + g_prefix_error(error, "failed to get dynamic tool info from device: "); + return FALSE; + } + if (g_getenv("FWUPD_GENESYS_USBHUB_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "Dynamic info", + (guint8 *)&self->dynamic_ts, + sizeof(FuGenesysDynamicToolString)); + } + + fw_buf = + g_usb_device_get_string_descriptor_bytes_full(usb_device, + GENESYS_USBHUB_FW_INFO_DESC_IDX, + G_USB_DEVICE_LANGID_ENGLISH_UNITED_STATES, + 64, + error); + if (fw_buf == NULL) { + g_prefix_error(error, "failed to get firmware info from device: "); + return FALSE; + } + if (!fu_genesys_usbhub_device_get_descriptor_data(fw_buf, + (guint8 *)&self->fwinfo_ts, + sizeof(FuGenesysFirmwareInfoToolString), + error)) { + g_prefix_error(error, "failed to get firmware info from device: "); + return FALSE; + } + if (g_getenv("FWUPD_GENESYS_USBHUB_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "Fw info", + (guint8 *)&self->fwinfo_ts, + sizeof(FuGenesysFirmwareInfoToolString)); + } + + tool_string_version = fu_genesys_tsdigit_value(self->static_ts.tool_string_version); + if (tool_string_version >= TOOL_STRING_VERSION_VENDOR_SUPPORT) { + g_autoptr(GBytes) vendor_buf = g_usb_device_get_string_descriptor_bytes_full( + usb_device, + GENESYS_USBHUB_VENDOR_SUPPORT_DESC_IDX, + G_USB_DEVICE_LANGID_ENGLISH_UNITED_STATES, + 64, + error); + if (vendor_buf == NULL) { + g_prefix_error(error, "failed to get vendor support info from device: "); + return FALSE; + } + if (!fu_genesys_usbhub_device_get_descriptor_data( + vendor_buf, + (guint8 *)&self->vs_ts, + sizeof(FuGenesysVendorSupportToolString), + error)) { + g_prefix_error(error, "failed to get vendor support info from device: "); + return FALSE; + } + if (g_getenv("FWUPD_GENESYS_USBHUB_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "Vendor support", + (guint8 *)&self->vs_ts, + sizeof(FuGenesysVendorSupportToolString)); + } + } + + if (fu_device_has_private_flag(device, FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY)) { + if (!fu_genesys_usbhub_device_authenticate(self, error)) + return FALSE; + } + if (!fu_genesys_usbhub_device_set_isp_mode(self, ISP_ENTER, error)) + return FALSE; + /* setup cfi device */ + self->cfi_device = fu_genesys_usbhub_device_cfi_setup(self, error); + if (self->cfi_device == NULL) + return FALSE; + block_size = fu_cfi_device_get_block_size(self->cfi_device); + if (block_size != 0) + self->flash_block_size = block_size; + sector_size = fu_cfi_device_get_sector_size(self->cfi_device); + if (sector_size != 0) + self->flash_sector_size = sector_size; + + /* get bonding and flash dump location bit */ + if (!fu_genesys_usbhub_device_get_bonding_and_flash_dump_location_bit(self, error)) + return FALSE; + fu_device_add_instance_u8(device, "BONDING", self->bonding); + + if (self->dynamic_ts.running_mode == 'M') { + self->running_bank = BANK_MASK_CODE; + } else if (self->flash_dump_location_bit) { + self->running_bank = BANK_SECOND; + } else { + self->running_bank = BANK_FIRST; + } + + /* setup firmware parameters */ + switch (self->chip.model) { + case ISP_MODEL_HUB_GL3521: + self->code_size = 0x5000; + self->fw_bank_addr[0] = 0x0000; + self->fw_data_total_count = 0x5000; + break; + case ISP_MODEL_HUB_GL3523: { + self->fw_bank_addr[0] = 0x0000; + self->fw_bank_addr[1] = 0x8000; + + if (fu_device_has_private_flag(device, FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY)) + self->extend_size = GL3523_PUBLIC_KEY_LEN + GL3523_SIG_LEN; + + if (self->chip.revision == 50) { + self->fw_data_total_count = 0x8000; + if (!fu_genesys_usbhub_device_get_code_size(self, 0, error)) + return FALSE; + } else { + self->fw_data_total_count = 0x6000; + self->code_size = self->fw_data_total_count; + } + break; + } + case ISP_MODEL_HUB_GL3590: + if (!fu_genesys_usbhub_device_get_code_size(self, 0, error)) + return FALSE; + self->fw_bank_addr[0] = 0x0000; + self->fw_bank_addr[1] = 0x10000; + self->fw_data_total_count = 0x10000; + break; + default: + break; + } + fu_device_set_firmware_size_max(device, self->fw_data_total_count + self->extend_size); + + /* verify firmware integrity */ + bufsz = self->fw_data_total_count + self->extend_size; + buf = g_malloc0(bufsz); + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[0], + buf, + bufsz, + NULL, + error)) + return FALSE; + blob = g_bytes_new_take(g_steal_pointer(&buf), bufsz); + firmware = fu_genesys_usbhub_firmware_new(); + if (!fu_firmware_parse(firmware, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, &error_local)) { + g_debug("ignoring firmware: %s", error_local->message); + self->fw_bank_vers[0] = 0; + } else { + version_raw = fu_firmware_get_version_raw(firmware); + if (version_raw != 0xffff) + self->fw_bank_vers[0] = version_raw; + } + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE)) { + gsize address = self->fw_bank_addr[0]; + gsize bufsz_dual; + g_autoptr(FuFirmware) firmware_dual = NULL; + g_autoptr(GError) error_local_dual = NULL; + g_autoptr(GBytes) blob_dual = NULL; + g_autofree guint8 *buf_dual = NULL; + + /* verify dual firmware integrity */ + bufsz_dual = self->fw_data_total_count + self->extend_size; + buf_dual = g_malloc0(bufsz_dual); + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[1], + buf_dual, + bufsz_dual, + NULL, + error)) + return FALSE; + blob_dual = g_bytes_new_take(g_steal_pointer(&buf_dual), bufsz_dual); + firmware_dual = fu_genesys_usbhub_firmware_new(); + if (!fu_firmware_parse(firmware_dual, + blob_dual, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &error_local_dual)) { + g_debug("ignoring recovery firmware: %s", error_local_dual->message); + self->fw_bank_vers[1] = 0; + } else { + version_raw = fu_firmware_get_version_raw(firmware_dual); + if (version_raw != 0xffff) + self->fw_bank_vers[1] = version_raw; + } + + /* write recovery needed? */ + if (self->fw_bank_vers[0] == 0 && self->fw_bank_vers[1] == 0) { + /* first bank and recovery are both blanks: write fw on both */ + address = self->fw_bank_addr[1]; + } else if (self->fw_bank_vers[0] > self->fw_bank_vers[1]) { + /* first bank is more recent than recovery: write fw on recovery first */ + address = self->fw_bank_addr[1]; + } else { + /* recovery is more recent than first bank: write fw on first bank only */ + address = self->fw_bank_addr[0]; + } + + self->read_first_bank = + (self->chip.model == ISP_MODEL_HUB_GL3523) && self->fw_bank_vers[0] != 0; + self->write_recovery_bank = address == self->fw_bank_addr[1]; + } + + /* has public key */ + if (fu_device_has_private_flag(device, FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY)) { + g_autofree gchar *guid = NULL; + if (!fu_memcpy_safe((guint8 *)&self->public_key, + sizeof(self->public_key), + 0, /* dst */ + g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + self->fw_data_total_count, /* src */ + sizeof(self->public_key), + error)) + return FALSE; + if (memcmp(&self->public_key.N, "N = ", 4) != 0 && + memcmp(&self->public_key.E, "E = ", 4) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "invalid public-key"); + return FALSE; + } + guid = fwupd_guid_hash_data((const guint8 *)&self->public_key, + sizeof(self->public_key), + FWUPD_GUID_FLAG_NONE); + fu_device_add_instance_strup(device, "PUBKEY", guid); + } + + /* add specific product info */ + ic_type = fu_strsafe((const gchar *)self->static_ts.mask_project_ic_type, + sizeof(self->static_ts.mask_project_ic_type)); + fu_device_add_instance_str(device, "IC", ic_type); + + if (self->running_bank != BANK_MASK_CODE) { + const gchar *vendor = fwupd_device_get_vendor(FWUPD_DEVICE(device)); + guint8 portnum = fu_genesys_tsdigit_value(self->dynamic_ts.ss_port_number) << 4 | + fu_genesys_tsdigit_value(self->dynamic_ts.hs_port_number); + g_autofree gchar *guid = NULL; + + guid = fwupd_guid_hash_data((const guint8 *)&self->vs_ts, + sizeof(self->vs_ts), + FWUPD_GUID_FLAG_NONE); + fu_device_add_instance_strup(device, "VENDOR", vendor); + fu_device_add_instance_u8(device, "PORTNUM", portnum); + fu_device_add_instance_strup(device, "VENDORSUP", guid); + } + + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "IC", NULL); + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "IC", "BONDING", NULL); + fu_device_build_instance_id(device, + NULL, + "USB", + "VID", + "PID", + "VENDOR", + "IC", + "BONDING", + "PORTNUM", + "VENDORSUP", + NULL); + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "PUBKEY", NULL); + + /* have MStar scaler */ + if (fu_device_has_private_flag(device, FU_GENESYS_USBHUB_FLAG_HAS_MSTAR_SCALER)) + if (!fu_genesys_usbhub_device_mstar_scaler_setup(self, error)) + return FALSE; + + /* success */ + return TRUE; +#else + /* failure */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "GUsb version is too old, " + "fwupd needs to be rebuilt against 0.3.8 or later"); + return FALSE; +#endif +} + +static void +fu_genesys_usbhub_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + fu_string_append_kx(str, idt, "FlashEraseDelay", self->flash_erase_delay); + fu_string_append_kx(str, idt, "FlashWriteDelay", self->flash_write_delay); + fu_string_append_kx(str, idt, "FlashBlockSize", self->flash_block_size); + fu_string_append_kx(str, idt, "FlashSectorSize", self->flash_sector_size); + fu_string_append_kx(str, idt, "FlashRwSize", self->flash_rw_size); + fu_string_append_kx(str, idt, "FwBank0Addr", self->fw_bank_addr[0]); + fu_string_append_kx(str, idt, "FwBank0Vers", self->fw_bank_vers[0]); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE)) { + fu_string_append_kx(str, idt, "FwBank1Addr", self->fw_bank_addr[1]); + fu_string_append_kx(str, idt, "FwBank1Vers", self->fw_bank_vers[1]); + } + fu_string_append_kx(str, idt, "CodeSize", self->code_size); + fu_string_append_kx(str, idt, "FwDataTotalCount", self->fw_data_total_count); + fu_string_append_kx(str, idt, "ExtendSize", self->extend_size); +} + +static FuFirmware * +fu_genesys_usbhub_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + g_autoptr(FuFirmware) firmware = fu_genesys_usbhub_firmware_new(); + + /* parse firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* has public-key */ + if (g_bytes_get_size(fw) >= fu_firmware_get_size(firmware) + sizeof(self->public_key)) { + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + if (g_getenv("FWUPD_GENESYS_USBHUB_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "PublicKey", buf, bufsz); + if (memcmp(buf + fu_firmware_get_size(firmware), + &self->public_key, + sizeof(self->public_key)) != 0 && + (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "mismatch public-key"); + return NULL; + } + } + + /* check size */ + if (g_bytes_get_size(fw) > fu_device_get_firmware_size_max(device)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware too large, got 0x%x, expected <= 0x%x", + (guint)g_bytes_get_size(fw), + (guint)fu_device_get_firmware_size_max(device)); + return NULL; + } + + /* success */ + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_genesys_usbhub_device_erase_flash(FuGenesysUsbhubDevice *self, + guint start_addr, + guint len, + FuProgress *progress, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + FuGenesysWaitFlashRegisterHelper helper = {.reg = 5, .expected_val = 0}; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new(NULL, + len, + start_addr, + self->flash_block_size, + self->flash_sector_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint16 sectornum = fu_chunk_get_address(chk) / self->flash_sector_size; + guint16 blocknum = fu_chunk_get_page(chk); + guint16 index = (0x01 << 8) | (sectornum << 4) | blocknum; + + 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, + self->vcs.req_write, + 0x2001, /* value */ + index, /* idx */ + NULL, /* data */ + 0, /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error erasing flash at sector 0x%02x in block 0x%02x", + sectornum, + blocknum); + return FALSE; + } + + /* 8s */ + if (!fu_device_retry(FU_DEVICE(self), + fu_genesys_usbhub_device_wait_flash_status_register_cb, + self->flash_erase_delay / 30, + &helper, + error)) { + g_prefix_error(error, "error erasing flash: "); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_write_flash(FuGenesysUsbhubDevice *self, + guint start_addr, + const guint8 *buf, + guint bufsz, + FuProgress *progress, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + FuGenesysWaitFlashRegisterHelper helper = {.reg = 5, .expected_val = 0}; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = + fu_chunk_array_new(buf, bufsz, start_addr, self->flash_block_size, self->flash_rw_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autofree guint8 *chkbuf_mut = NULL; + + chkbuf_mut = + fu_memdup_safe(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), error); + if (chkbuf_mut == NULL) + return FALSE; + 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, + self->vcs.req_write, + (fu_chunk_get_page(chk) & 0x000f) + << 12, /* value */ + fu_chunk_get_address(chk) & 0x00ffff, /* idx */ + chkbuf_mut, /* data */ + fu_chunk_get_data_sz(chk), /* data length */ + NULL, /* actual length */ + GENESYS_USBHUB_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "error writing flash at 0x%02x%04x: ", + fu_chunk_get_page(chk), + fu_chunk_get_address(chk)); + return FALSE; + } + + /* 5s */ + if (!fu_device_retry(FU_DEVICE(self), + fu_genesys_usbhub_device_wait_flash_status_register_cb, + self->flash_write_delay / 30, + &helper, + error)) { + g_prefix_error(error, "error writing flash: "); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_write_recovery(FuGenesysUsbhubDevice *self, + GBytes *blob, + FuProgress *progress, + GError **error) +{ + gsize bufsz = 0; + g_autofree guint8 *buf = NULL; + g_autofree guint8 *buf_verify = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + if (self->read_first_bank) + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 30, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 20, NULL); + + /* reuse fw on first bank for GL3523 */ + if (self->read_first_bank) { + bufsz = self->code_size; + if (bufsz == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "code size is zero"); + return FALSE; + } + + buf = g_malloc0(bufsz); + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[0], + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } else { + bufsz = g_bytes_get_size(blob); + buf = fu_memdup_safe(g_bytes_get_data(blob, NULL), bufsz, error); + if (buf == NULL) + return FALSE; + } + + /* erase */ + if (!fu_genesys_usbhub_device_erase_flash(self, + self->fw_bank_addr[1], + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + if (!fu_genesys_usbhub_device_write_flash(self, + self->fw_bank_addr[1], + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + buf_verify = g_malloc0(bufsz); + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[1], + buf_verify, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_memcmp_safe(buf_verify, bufsz, buf, bufsz, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + g_autoptr(GBytes) blob = NULL; + g_autofree guint8 *buf_verify = NULL; + + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return FALSE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + if (self->write_recovery_bank) { + if (self->read_first_bank) + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 120, NULL); + else + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, NULL); + } + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 30, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 20, NULL); + + /* write fw to recovery bank first? */ + if (self->write_recovery_bank) { + if (!fu_genesys_usbhub_device_write_recovery(self, + blob, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* write fw to first bank then */ + if (!fu_genesys_usbhub_device_erase_flash(self, + self->fw_bank_addr[0], + g_bytes_get_size(blob), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_genesys_usbhub_device_write_flash(self, + self->fw_bank_addr[0], + g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + buf_verify = g_malloc0(g_bytes_get_size(blob)); + if (!fu_genesys_usbhub_device_read_flash(self, + self->fw_bank_addr[0], + buf_verify, + g_bytes_get_size(blob), + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_memcmp_safe(buf_verify, + g_bytes_get_size(blob), + g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_genesys_usbhub_device_set_progress(FuDevice *device, FuProgress *progress) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + + fu_progress_set_id(progress, G_STRLOC); + if (self->write_recovery_bank) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 30, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 70, "reload"); + } else { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 15, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 85, "reload"); + } +} + +static gboolean +fu_genesys_usbhub_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); + guint64 tmp; + + if (g_strcmp0(key, "GenesysUsbhubDeviceTransferSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->flash_rw_size = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysUsbhubSwitchRequest") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->vcs.req_switch = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysUsbhubReadRequest") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->vcs.req_read = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysUsbhubWriteRequest") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->vcs.req_write = tmp; + + /* success */ + return TRUE; + } + + /* failure */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_genesys_usbhub_device_init(FuGenesysUsbhubDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_protocol(FU_DEVICE(self), "com.genesys.usbhub"); + fu_device_retry_set_delay(FU_DEVICE(self), 30); /* 30ms */ + fu_device_set_remove_delay(FU_DEVICE(self), 5000); /* 5s */ + fu_device_register_private_flag(FU_DEVICE(self), + FU_GENESYS_USBHUB_FLAG_HAS_MSTAR_SCALER, + "has-mstar-scaler"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY, + "has-public-key"); + fu_device_set_install_duration(FU_DEVICE(self), 9); /* 9 s */ + + self->vcs.req_switch = GENESYS_USBHUB_GL_HUB_SWITCH; + self->vcs.req_read = GENESYS_USBHUB_GL_HUB_READ; + self->vcs.req_write = GENESYS_USBHUB_GL_HUB_WRITE; + self->flash_erase_delay = 8000; /* 8s */ + self->flash_write_delay = 500; /* 500ms */ + self->flash_block_size = 0x10000; /* 64KB */ + self->flash_sector_size = 0x1000; /* 4KB */ + self->flash_rw_size = 0x40; /* 64B */ +} + +static void +fu_genesys_usbhub_device_class_init(FuGenesysUsbhubDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_genesys_usbhub_device_setup; + klass_device->dump_firmware = fu_genesys_usbhub_device_dump_firmware; + klass_device->prepare_firmware = fu_genesys_usbhub_device_prepare_firmware; + klass_device->write_firmware = fu_genesys_usbhub_device_write_firmware; + klass_device->set_progress = fu_genesys_usbhub_device_set_progress; + klass_device->detach = fu_genesys_usbhub_device_detach; + klass_device->attach = fu_genesys_usbhub_device_attach; + klass_device->to_string = fu_genesys_usbhub_device_to_string; + klass_device->set_quirk_kv = fu_genesys_usbhub_device_set_quirk_kv; +} diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-device.h b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-device.h new file mode 100644 index 0000000000000000000000000000000000000000..b16b51bc1b3430d0b0b4d1dd89eed8cf26be3b19 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_GENESYS_USBHUB_DEVICE (fu_genesys_usbhub_device_get_type()) +G_DECLARE_FINAL_TYPE(FuGenesysUsbhubDevice, + fu_genesys_usbhub_device, + FU, + GENESYS_USBHUB_DEVICE, + FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-firmware.c b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..9dd9df746652c1e8f4fad7b2976ecc8b909e7d3f --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-firmware.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-genesys-common.h" +#include "fu-genesys-usbhub-firmware.h" + +struct _FuGenesysUsbhubFirmware { + FuFirmwareClass parent_instance; + FuGenesysStaticToolString static_ts; + FuGenesysChip chip; +}; + +G_DEFINE_TYPE(FuGenesysUsbhubFirmware, fu_genesys_usbhub_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_genesys_usbhub_firmware_get_chip(FuGenesysUsbhubFirmware *self, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 project_ic_type[6]; + + if (!fu_memcpy_safe(project_ic_type, + sizeof(project_ic_type), + 0, /* dst */ + buf, + bufsz, + GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523 + 8, /* src */ + sizeof(project_ic_type), + error)) + return FALSE; + + if (memcmp(project_ic_type, "3521", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3521; + self->chip.revision = 10 * (project_ic_type[4] - '0') + (project_ic_type[5] - '0'); + return TRUE; + } + + if (memcmp(project_ic_type, "3523", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3523; + self->chip.revision = 10 * (project_ic_type[4] - '0') + (project_ic_type[5] - '0'); + return TRUE; + } + + if (!fu_memcpy_safe(project_ic_type, + sizeof(project_ic_type), + 0, /* dst */ + buf, + bufsz, + GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3590 + 8, /* src */ + sizeof(project_ic_type), + error)) + return FALSE; + + if (memcmp(project_ic_type, "3590", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3590; + self->chip.revision = 10 * (project_ic_type[4] - '0') + (project_ic_type[5] - '0'); + return TRUE; + } + + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Unsupported IC"); + return FALSE; +} + +static gboolean +fu_genesys_usbhub_firmware_verify(const guint8 *buf, gsize bufsz, guint16 code_size, GError **error) +{ + guint16 fw_checksum, checksum; + + /* check code-size */ + if (code_size < sizeof(checksum)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "code-size is too small: %u bytes", + code_size); + return FALSE; + } + + /* get checksum */ + if (!fu_memread_uint16_safe(buf, + bufsz, + code_size - sizeof(checksum), + &fw_checksum, + G_BIG_ENDIAN, + error)) + return FALSE; + + /* calculate checksum */ + checksum = fu_sum16(buf, code_size - sizeof(checksum)); + if (checksum != fw_checksum) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "checksum mismatch, got 0x%04x, expected 0x%04x", + checksum, + fw_checksum); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_genesys_usbhub_firmware_check_magic(FuFirmware *firmware, + GBytes *fw, + gsize offset, + GError **error) +{ + guint8 magic[4] = {0x0}; + + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset + GENESYS_USBHUB_FW_SIG_OFFSET, + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, GENESYS_USBHUB_FW_SIG_TEXT_HUB, sizeof(magic)) != 0 && + memcmp(magic, GENESYS_USBHUB_FW_SIG_TEXT_HUB_SIGN, sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "signature not supported"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_genesys_usbhub_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + guint32 code_size = 0; + guint32 static_ts_offset = 0; + guint16 version_raw = 0; + g_autofree gchar *version = NULL; + + /* get chip */ + if (!fu_genesys_usbhub_firmware_get_chip(self, buf, bufsz, error)) + return FALSE; + + switch (self->chip.model) { + case ISP_MODEL_HUB_GL3521: + static_ts_offset = GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3521; + break; + case ISP_MODEL_HUB_GL3523: + static_ts_offset = GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523; + break; + case ISP_MODEL_HUB_GL3590: + static_ts_offset = GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3590; + break; + default: + break; + } + + /* get static tool string */ + if (!fu_memcpy_safe((guint8 *)&self->static_ts, + sizeof(self->static_ts), + 0, /* dst */ + buf, + bufsz, + static_ts_offset, /* src */ + sizeof(self->static_ts), + error)) + return FALSE; + + /* deduce code size */ + switch (self->chip.model) { + case ISP_MODEL_HUB_GL3521: + code_size = 0x5000; + break; + case ISP_MODEL_HUB_GL3523: { + code_size = 0x6000; + if (self->chip.revision == 50) { + guint8 kbs = 0; + + if (!fu_memread_uint8_safe(buf, + bufsz, + GENESYS_USBHUB_CODE_SIZE_OFFSET, + &kbs, + error)) + return FALSE; + code_size = 1024 * kbs; + } + break; + } + case ISP_MODEL_HUB_GL3590: { + guint8 kbs = 0; + if (!fu_memread_uint8_safe(buf, + bufsz, + GENESYS_USBHUB_CODE_SIZE_OFFSET, + &kbs, + error)) + return FALSE; + code_size = 1024 * kbs; + break; + } + default: + break; + } + fu_firmware_set_size(firmware, code_size); + + /* calculate checksum */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) + if (!fu_genesys_usbhub_firmware_verify(buf, bufsz, code_size, error)) + return FALSE; + + /* get firmware version */ + if (!fu_memread_uint16_safe(buf, + bufsz, + GENESYS_USBHUB_VERSION_OFFSET, + &version_raw, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_firmware_set_version_raw(firmware, version_raw); + version = + g_strdup_printf("%02x.%02x", (version_raw & 0xFF00U) >> 8, (version_raw & 0x00FFU)); + fu_firmware_set_version(firmware, version); + + /* success */ + return TRUE; +} + +static GBytes * +fu_genesys_usbhub_firmware_write(FuFirmware *firmware, GError **error) +{ + FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + guint16 code_size = 0x6000; + guint16 checksum; + + /* fixed size */ + fu_byte_array_set_size(buf, code_size, 0x00); + + /* signature */ + if (!fu_memcpy_safe(buf->data, + buf->len, + GENESYS_USBHUB_FW_SIG_OFFSET, /* dst */ + (const guint8 *)GENESYS_USBHUB_FW_SIG_TEXT_HUB, + GENESYS_USBHUB_FW_SIG_LEN, + 0x0, /* src */ + GENESYS_USBHUB_FW_SIG_LEN, + error)) + return NULL; + + /* static tool string */ + if (!fu_memcpy_safe(buf->data, + buf->len, + GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523, /* dst */ + (const guint8 *)&self->static_ts, + sizeof(self->static_ts), + 0x0, /* src */ + sizeof(self->static_ts), + error)) + return NULL; + + /* checksum */ + checksum = fu_sum16(buf->data, code_size - sizeof(checksum)); + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + code_size - sizeof(guint16), + checksum, + G_BIG_ENDIAN, + error)) + return NULL; + + /* version */ + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + GENESYS_USBHUB_VERSION_OFFSET, + 0x1234, // TODO: parse from firmware version string + G_BIG_ENDIAN, + error)) + return NULL; + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gchar * +fu_genesys_usbhub_firmware_get_project_ic_type_string(const gchar *tmp) +{ + return g_strdup_printf("GL%c%c%c%c-%c%c", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]); +} + +static void +fu_genesys_usbhub_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware); + g_autofree gchar *tool_string_version = NULL; + g_autofree gchar *mask_project_code = NULL; + g_autofree gchar *mask_project_hardware = NULL; + g_autofree gchar *mask_project_firmware = NULL; + g_autofree gchar *mask_project_ic_type = NULL; + g_autofree gchar *running_project_code = NULL; + g_autofree gchar *running_project_hardware = NULL; + g_autofree gchar *running_project_firmware = NULL; + g_autofree gchar *running_project_ic_type = NULL; + + tool_string_version = fu_strsafe((const gchar *)&self->static_ts.tool_string_version, + sizeof(self->static_ts.tool_string_version)); + fu_xmlb_builder_insert_kv(bn, "tool_string_version", tool_string_version); + + mask_project_code = fu_strsafe((const gchar *)&self->static_ts.mask_project_code, + sizeof(self->static_ts.mask_project_code)); + fu_xmlb_builder_insert_kv(bn, "mask_project_code", mask_project_code); + + mask_project_hardware = fu_strsafe((const gchar *)&self->static_ts.mask_project_hardware, + sizeof(self->static_ts.mask_project_hardware)); + if (mask_project_hardware != NULL) + mask_project_hardware[0] += 0x10; /* '1' -> 'A'... */ + fu_xmlb_builder_insert_kv(bn, "mask_project_hardware", mask_project_hardware); + + mask_project_firmware = fu_strsafe((const gchar *)&self->static_ts.mask_project_firmware, + sizeof(self->static_ts.mask_project_firmware)); + fu_xmlb_builder_insert_kv(bn, "mask_project_firmware", mask_project_firmware); + + mask_project_ic_type = fu_genesys_usbhub_firmware_get_project_ic_type_string( + (const gchar *)self->static_ts.mask_project_ic_type); + fu_xmlb_builder_insert_kv(bn, "mask_project_ic_type", mask_project_ic_type); + + running_project_code = fu_strsafe((const gchar *)&self->static_ts.running_project_code, + sizeof(self->static_ts.running_project_code)); + fu_xmlb_builder_insert_kv(bn, "running_project_code", running_project_code); + + running_project_hardware = + fu_strsafe((const gchar *)&self->static_ts.running_project_hardware, + sizeof(self->static_ts.running_project_hardware)); + if (running_project_hardware != NULL) + running_project_hardware[0] += 0x10; /* '1' -> 'A'... */ + fu_xmlb_builder_insert_kv(bn, "running_project_hardware", running_project_hardware); + + running_project_firmware = + fu_strsafe((const gchar *)&self->static_ts.running_project_firmware, + sizeof(self->static_ts.running_project_firmware)); + fu_xmlb_builder_insert_kv(bn, "running_project_firmware", running_project_firmware); + + running_project_ic_type = fu_genesys_usbhub_firmware_get_project_ic_type_string( + (const gchar *)self->static_ts.running_project_ic_type); + fu_xmlb_builder_insert_kv(bn, "running_project_ic_type", running_project_ic_type); +} + +static gboolean +fu_genesys_usbhub_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware); + const gchar *tmp; + guint64 tmp64; + + /* optional properties */ + tmp64 = xb_node_query_text_as_uint(n, "tool_string_version", NULL); + if (tmp64 != G_MAXUINT64) { + if (tmp64 > G_MAXUINT8) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid tool_string_version"); + return FALSE; + } + self->static_ts.tool_string_version = tmp64; + } + + /* mask_project_code */ + tmp = xb_node_query_text(n, "mask_project_code", NULL); + if (tmp != NULL) { + gsize len = strlen(tmp); + if (len != 4) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid mask_project_code %s, got 0x%x length", + tmp, + (guint)len); + return FALSE; + } + if (!fu_memcpy_safe((guint8 *)&self->static_ts.mask_project_code, + sizeof(self->static_ts.mask_project_code), + 0x0, /* dst */ + (const guint8 *)tmp, + len, + 0x0, /* src */ + len, + error)) + return FALSE; + } + + /* mask_project_ic_type */ + tmp = xb_node_query_text(n, "mask_project_ic_type", NULL); + if (tmp != NULL) { + gsize len = strlen(tmp); + if (len != 6) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid mask_project_ic_type %s, got 0x%x length", + tmp, + (guint)len); + return FALSE; + } + if (!fu_memcpy_safe((guint8 *)&self->static_ts.mask_project_ic_type, + sizeof(self->static_ts.mask_project_ic_type), + 0x0, /* dst */ + (const guint8 *)tmp, + len, + 0x0, /* src */ + len, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_genesys_usbhub_firmware_init(FuGenesysUsbhubFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_genesys_usbhub_firmware_class_init(FuGenesysUsbhubFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_genesys_usbhub_firmware_check_magic; + klass_firmware->parse = fu_genesys_usbhub_firmware_parse; + klass_firmware->export = fu_genesys_usbhub_firmware_export; + klass_firmware->build = fu_genesys_usbhub_firmware_build; + klass_firmware->write = fu_genesys_usbhub_firmware_write; +} + +FuFirmware * +fu_genesys_usbhub_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_GENESYS_USBHUB_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-firmware.h b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..98438163a34bdbe823e49d123f23a2c39e3aa370 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/fu-genesys-usbhub-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_GENESYS_USBHUB_FIRMWARE (fu_genesys_usbhub_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuGenesysUsbhubFirmware, + fu_genesys_usbhub_firmware, + FU, + GENESYS_USBHUB_FIRMWARE, + FuFirmware) + +FuFirmware * +fu_genesys_usbhub_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/genesys/genesys.quirk b/fwupd-1.8.6/plugins/genesys/genesys.quirk new file mode 100644 index 0000000000000000000000000000000000000000..5767ebf022becb9074223db1eb684ffe2ac229ca --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/genesys.quirk @@ -0,0 +1,60 @@ +# Genesys Logic USB Hubs + +[USB\VID_05E3&PID_0610] +Plugin = genesys + +# Google Servo Dock +[USB\VID_05E3&PID_0610&VENDOR_GOOGLE&IC_359010&BONDING_6A&PORTNUM_25&VENDORSUP_E8A9A9E4-6C17-5F9C-B7BD-CDA49FE66D75] +Name = Servo Dock + +# HP M2xfd + +# usbhub +[USB\VID_03F0&PID_0610] +Plugin = genesys +Name = HP USB-C Controller +Flags = dual-image,has-public-key +GenesysUsbhubSwitchRequest = 0xA1 +GenesysUsbhubReadRequest = 0xA2 +GenesysUsbhubWriteRequest = 0xA3 + +[USB\VID_03F0&PID_0610&PUBKEY_AB859399-95B8-5817-B521-9AD8CC7F5BD6] +Plugin = genesys +Name = HP M24fd USB-C Hub +Flags = dual-image,has-public-key,has-mstar-scaler +GenesysUsbhubSwitchRequest = 0xA1 +GenesysUsbhubReadRequest = 0xA2 +GenesysUsbhubWriteRequest = 0xA3 + +[USB\VID_03F0&PID_0610&PUBKEY_6BE97D77-C2BA-5AA2-B7DF-B9B318BEC2B5] +Plugin = genesys +Name = HP M27fd USB-C Hub +Flags = dual-image,has-public-key,has-mstar-scaler +GenesysUsbhubSwitchRequest = 0xA1 +GenesysUsbhubReadRequest = 0xA2 +GenesysUsbhubWriteRequest = 0xA3 + +# scaler +[GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_B335BDCE-7073-5D0E-9BD3-9B69C1A6899F&PANELREV_RIM101] +Name = HP M24fd USB-C Monitor +Flags = use-i2c-ch0 +GenesysScalerGpioOutputRegister = 0x0426 +GenesysScalerGpioEnableRegister = 0x0428 +GenesysScalerGpioValue = 0x01 +GenesysScalerCfiFlashId = 0xC22016 + +[GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_847A3650-8648-586B-83C8-8B53714F37E3&PANELREV_AIM101] +Name = HP M27fd USB-C Monitor +Flags = use-i2c-ch0 +GenesysScalerGpioOutputRegister = 0x0426 +GenesysScalerGpioEnableRegister = 0x0428 +GenesysScalerGpioValue = 0x01 +GenesysScalerCfiFlashId = 0xC84016 + +[GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_847A3650-8648-586B-83C8-8B53714F37E3&PANELREV_RIM101] +Name = HP M27fd USB-C Monitor +Flags = use-i2c-ch0 +GenesysScalerGpioOutputRegister = 0x0426 +GenesysScalerGpioEnableRegister = 0x0428 +GenesysScalerGpioValue = 0x01 +GenesysScalerCfiFlashId = 0xC84016 diff --git a/fwupd-1.8.6/plugins/genesys/meson.build b/fwupd-1.8.6/plugins/genesys/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..2d5c6e026ccf3123da6089bb69391ea72086f817 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/meson.build @@ -0,0 +1,18 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginGenesys"'] + +plugin_quirks += files('genesys.quirk') +plugin_builtins += static_library('fu_plugin_genesys', + sources: [ + 'fu-genesys-scaler-firmware.c', # fuzzing + 'fu-genesys-usbhub-firmware.c', # fuzzing + 'fu-genesys-scaler-device.c', + 'fu-genesys-usbhub-device.c', + 'fu-genesys-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/genesys/tests/genesys-scaler.bin b/fwupd-1.8.6/plugins/genesys/tests/genesys-scaler.bin new file mode 100644 index 0000000000000000000000000000000000000000..baa3648c4a5d3eec4c2c9710aa957935aa2f95eb Binary files /dev/null and b/fwupd-1.8.6/plugins/genesys/tests/genesys-scaler.bin differ diff --git a/fwupd-1.8.6/plugins/genesys/tests/genesys-scaler.builder.xml b/fwupd-1.8.6/plugins/genesys/tests/genesys-scaler.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..251e4a49a4b9ecf3089bf089cc151b1944ed4552 --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/tests/genesys-scaler.builder.xml @@ -0,0 +1,4 @@ + + aGVsbG8gd29ybGQ= + A + diff --git a/fwupd-1.8.6/plugins/genesys/tests/genesys-usbhub.bin b/fwupd-1.8.6/plugins/genesys/tests/genesys-usbhub.bin new file mode 100644 index 0000000000000000000000000000000000000000..8b500ac1eb2f65af90ed40b83e95e1274cee4c24 Binary files /dev/null and b/fwupd-1.8.6/plugins/genesys/tests/genesys-usbhub.bin differ diff --git a/fwupd-1.8.6/plugins/genesys/tests/genesys-usbhub.builder.xml b/fwupd-1.8.6/plugins/genesys/tests/genesys-usbhub.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..94d6c9d495688f932039b64d97a6e1d72553a43b --- /dev/null +++ b/fwupd-1.8.6/plugins/genesys/tests/genesys-usbhub.builder.xml @@ -0,0 +1,5 @@ + + aGVsbG8gd29ybGQ= + 1 + 352310 + diff --git a/fwupd-1.8.6/plugins/goodix-moc/README.md b/fwupd-1.8.6/plugins/goodix-moc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a150a6644450282dafd208ccc7b2dc831ca123fb --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/README.md @@ -0,0 +1,35 @@ +# Goodix Fingerprint Sensor + +## Introduction + +The plugin used for update firmware for fingerprint sensors from Goodix. + +## 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.goodix.goodixmoc + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_27C6&PID_6001&REV_0001` +* `USB\VID_27C6&PID_6001` +* `USB\VID_27C6` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x27C6` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-common.h b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-common.h new file mode 100644 index 0000000000000000000000000000000000000000..f0f39671c552563ef80e887dc01c22efc817da46 --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-common.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* protocol */ +#define GX_CMD_ACK 0xAA +#define GX_CMD_VERSION 0xD0 +#define GX_CMD_RESET 0xB4 +#define GX_CMD_UPGRADE 0x80 +#define GX_CMD_UPGRADE_INIT 0x00 +#define GX_CMD_UPGRADE_DATA 0x01 +#define GX_CMD1_DEFAULT 0x00 + +#define GX_SIZE_CRC32 4 + +/* type covert */ +#define MAKE_CMD_EX(cmd0, cmd1) ((guint16)(((cmd0) << 8) | (cmd1))) + +typedef struct { + guint8 format[2]; + guint8 fwtype[8]; + guint8 fwversion[8]; + guint8 customer[8]; + guint8 mcu[8]; + guint8 sensor[8]; + guint8 algversion[8]; + guint8 interface[8]; + guint8 protocol[8]; + guint8 flashVersion[8]; + guint8 reserved[62]; +} GxfpVersionInfo; + +typedef struct { + guint8 cmd; + gboolean configured; +} GxfpAckMsg; + +typedef struct { + guint8 result; + union { + GxfpAckMsg ack_msg; + GxfpVersionInfo version_info; + }; +} GxfpCmdResp; + +typedef enum { + GX_PKG_TYPE_NORMAL = 0x80, + GX_PKG_TYPE_EOP = 0, +} GxPkgType; + +typedef struct __attribute__((__packed__)) { + guint8 cmd0; + guint8 cmd1; + guint8 pkg_flag; + guint8 reserved; + guint16 len; + guint8 crc8; + guint8 rev_crc8; +} GxfpPkgHeader; diff --git a/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-device.c b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..3d582b6298e1af645bf323048cd562991eaf9c0b --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-device.c @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-goodixmoc-common.h" +#include "fu-goodixmoc-device.h" + +struct _FuGoodixMocDevice { + FuUsbDevice parent_instance; + guint8 dummy_seq; +}; + +G_DEFINE_TYPE(FuGoodixMocDevice, fu_goodixmoc_device, FU_TYPE_USB_DEVICE) + +#define GX_USB_BULK_EP_IN (3 | 0x80) +#define GX_USB_BULK_EP_OUT (1 | 0x00) +#define GX_USB_INTERFACE 0 + +#define GX_USB_DATAIN_TIMEOUT 2000 /* ms */ +#define GX_USB_DATAOUT_TIMEOUT 200 /* ms */ +#define GX_FLASH_TRANSFER_BLOCK_SIZE 1000 /* 1000 */ + +static gboolean +goodixmoc_device_cmd_send(FuGoodixMocDevice *self, + guint8 cmd0, + guint8 cmd1, + GxPkgType type, + GByteArray *req, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint32 crc_all = 0; + guint32 crc_hdr = 0; + gsize actual_len = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* build header */ + fu_byte_array_append_uint8(buf, cmd0); + fu_byte_array_append_uint8(buf, cmd1); + fu_byte_array_append_uint8(buf, type); /* pkg_flag */ + fu_byte_array_append_uint8(buf, self->dummy_seq++); /* reserved */ + fu_byte_array_append_uint16(buf, req->len + GX_SIZE_CRC32, G_LITTLE_ENDIAN); + crc_hdr = fu_crc8(buf->data, buf->len); + fu_byte_array_append_uint8(buf, crc_hdr); + fu_byte_array_append_uint8(buf, ~crc_hdr); + g_byte_array_append(buf, req->data, req->len); + crc_all = fu_crc32(buf->data, buf->len); + fu_byte_array_append_uint32(buf, crc_all, G_LITTLE_ENDIAN); + + /* send zero length package */ + if (!g_usb_device_bulk_transfer(usb_device, + GX_USB_BULK_EP_OUT, + NULL, + 0, + NULL, + GX_USB_DATAOUT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to req: "); + return FALSE; + } + if (g_getenv("FWUPD_GOODIXFP_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "REQST", + buf->data, + buf->len, + 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + + /* send data */ + if (!g_usb_device_bulk_transfer(usb_device, + GX_USB_BULK_EP_OUT, + buf->data, + buf->len, + &actual_len, + GX_USB_DATAOUT_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to req: "); + return FALSE; + } + if (actual_len != buf->len) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid length"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +goodixmoc_device_cmd_recv(FuGoodixMocDevice *self, + GxfpCmdResp *presponse, + gboolean data_reply, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint32 crc_actual = 0; + guint32 crc_calculated = 0; + gsize actual_len = 0; + gsize offset = 0; + + g_return_val_if_fail(presponse != NULL, FALSE); + + /* + * package format + * | zlp | ack | zlp | data | + */ + while (1) { + guint16 header_len = 0x0; + guint8 header_cmd0 = 0x0; + g_autoptr(GByteArray) reply = g_byte_array_new(); + fu_byte_array_set_size(reply, GX_FLASH_TRANSFER_BLOCK_SIZE, 0x00); + if (!g_usb_device_bulk_transfer(usb_device, + GX_USB_BULK_EP_IN, + reply->data, + reply->len, + &actual_len, /* allowed to return short read */ + GX_USB_DATAIN_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to reply: "); + return FALSE; + } + + /* receive zero length package */ + if (actual_len == 0) + continue; + if (g_getenv("FWUPD_GOODIXFP_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "REPLY", + reply->data, + actual_len, + 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + + /* parse package header */ + if (!fu_memread_uint8_safe(reply->data, reply->len, 0x0, &header_cmd0, error)) + return FALSE; + if (!fu_memread_uint16_safe(reply->data, + reply->len, + 0x4, + &header_len, + G_LITTLE_ENDIAN, + error)) + return FALSE; + offset = sizeof(GxfpPkgHeader) + header_len - GX_SIZE_CRC32; + crc_actual = fu_crc32(reply->data, offset); + if (!fu_memread_uint32_safe(reply->data, + reply->len, + offset, + &crc_calculated, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (crc_actual != crc_calculated) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid checksum, got 0x%x, expected 0x%x", + crc_calculated, + crc_actual); + return FALSE; + } + + /* parse package data */ + if (!fu_memread_uint8_safe(reply->data, + reply->len, + sizeof(GxfpPkgHeader) + 0x00, + &presponse->result, + error)) + return FALSE; + if (header_cmd0 == GX_CMD_ACK) { + if (header_len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid bufsz"); + return FALSE; + } + if (!fu_memread_uint8_safe(reply->data, + reply->len, + sizeof(GxfpPkgHeader) + 0x01, + &presponse->ack_msg.cmd, + error)) + return FALSE; + } else if (header_cmd0 == GX_CMD_VERSION) { + if (!fu_memcpy_safe((guint8 *)&presponse->version_info, + sizeof(presponse->version_info), + 0x0, /* dst */ + reply->data, + reply->len, + sizeof(GxfpPkgHeader) + 0x01, /* src */ + sizeof(GxfpVersionInfo), + error)) + return FALSE; + } + + /* continue after ack received */ + if (header_cmd0 == GX_CMD_ACK && data_reply) + continue; + break; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_goodixmoc_device_cmd_xfer(FuGoodixMocDevice *device, + guint8 cmd0, + guint8 cmd1, + GxPkgType type, + GByteArray *req, + GxfpCmdResp *presponse, + gboolean data_reply, + GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + if (!goodixmoc_device_cmd_send(self, cmd0, cmd1, type, req, error)) + return FALSE; + return goodixmoc_device_cmd_recv(self, presponse, data_reply, error); +} + +static gboolean +fu_goodixmoc_device_setup_version(FuGoodixMocDevice *self, GError **error) +{ + GxfpCmdResp rsp = {0}; + g_autofree gchar *version = NULL; + g_autoptr(GByteArray) req = g_byte_array_new(); + + fu_byte_array_append_uint8(req, 0); /* dummy */ + if (!fu_goodixmoc_device_cmd_xfer(self, + GX_CMD_VERSION, + GX_CMD1_DEFAULT, + GX_PKG_TYPE_EOP, + req, + &rsp, + TRUE, + error)) + return FALSE; + version = g_strndup((const gchar *)rsp.version_info.fwversion, + sizeof(rsp.version_info.fwversion)); + fu_device_set_version(FU_DEVICE(self), version); + return TRUE; +} + +static gboolean +fu_goodixmoc_device_update_init(FuGoodixMocDevice *self, GError **error) +{ + GxfpCmdResp rsp = {0}; + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* update initial */ + if (!fu_goodixmoc_device_cmd_xfer(self, + GX_CMD_UPGRADE, + GX_CMD_UPGRADE_INIT, + GX_PKG_TYPE_EOP, + req, + &rsp, + TRUE, + error)) { + g_prefix_error(error, "failed to send initial update: "); + return FALSE; + } + + /* check result */ + if (rsp.result != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "initial update failed [0x%x]", + rsp.result); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_goodixmoc_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + GxfpCmdResp rsp = {0}; + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* reset device */ + if (!fu_goodixmoc_device_cmd_xfer(self, + GX_CMD_RESET, + 0x03, + GX_PKG_TYPE_EOP, + req, + &rsp, + FALSE, + error)) { + g_prefix_error(error, "failed to send reset device: "); + return FALSE; + } + + /* check result */ + if (rsp.result != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to reset device [0x%x]", + rsp.result); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_goodixmoc_device_setup(FuDevice *device, GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_goodixmoc_device_parent_class)->setup(device, error)) + return FALSE; + + /* ensure version */ + if (!fu_goodixmoc_device_setup_version(self, error)) { + g_prefix_error(error, "failed to get firmware version: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_goodixmoc_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); + GxPkgType pkg_eop = GX_PKG_TYPE_NORMAL; + GxfpCmdResp rsp = {0}; + gboolean wait_data_reply = FALSE; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, + 0x00, /* page_sz */ + GX_FLASH_TRANSFER_BLOCK_SIZE); + + /* don't auto-boot firmware */ + if (!fu_goodixmoc_device_update_init(self, &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to initial update: %s", + error_local->message); + return FALSE; + } + fu_progress_step_done(progress); + + /* write each block */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) req = g_byte_array_new(); + g_autoptr(GError) error_block = NULL; + + g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + + /* the last chunk */ + if (i == chunks->len - 1) { + wait_data_reply = TRUE; + pkg_eop = GX_PKG_TYPE_EOP; + } + if (!fu_goodixmoc_device_cmd_xfer(self, + GX_CMD_UPGRADE, + GX_CMD_UPGRADE_DATA, + pkg_eop, + req, + &rsp, + wait_data_reply, + &error_block)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_block->message); + return FALSE; + } + + /* check update status */ + if (wait_data_reply && rsp.result != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to verify firmware [0x%x]", + rsp.result); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_goodixmoc_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_goodixmoc_device_init(FuGoodixMocDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_remove_delay(FU_DEVICE(self), 5000); + fu_device_add_protocol(FU_DEVICE(self), "com.goodix.goodixmoc"); + fu_device_set_name(FU_DEVICE(self), "Fingerprint Sensor"); + fu_device_set_summary(FU_DEVICE(self), "Match-On-Chip fingerprint sensor"); + fu_device_set_vendor(FU_DEVICE(self), "Goodix"); + fu_device_set_install_duration(FU_DEVICE(self), 10); + fu_device_set_firmware_size_min(FU_DEVICE(self), 0x20000); + fu_device_set_firmware_size_max(FU_DEVICE(self), 0x30000); + fu_usb_device_add_interface(FU_USB_DEVICE(self), GX_USB_INTERFACE); +} + +static void +fu_goodixmoc_device_class_init(FuGoodixMocDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_goodixmoc_device_write_firmware; + klass_device->setup = fu_goodixmoc_device_setup; + klass_device->attach = fu_goodixmoc_device_attach; + klass_device->set_progress = fu_goodixmoc_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-device.h b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..18465909521a07260249863f2efd7ed76388a336 --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_GOODIXMOC_DEVICE (fu_goodixmoc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuGoodixMocDevice, fu_goodixmoc_device, FU, GOODIXMOC_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-plugin.c b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..a9aa3eae7bbc94ef233a63bf19e6ca72bfbfb0cd --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Richard Hughes + * Copyright (C) 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-goodixmoc-device.h" +#include "fu-goodixmoc-plugin.h" + +struct _FuGoodixMocPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuGoodixMocPlugin, fu_goodixmoc_plugin, FU_TYPE_PLUGIN) + +static void +fu_goodixmoc_plugin_init(FuGoodixMocPlugin *self) +{ +} + +static void +fu_goodixmoc_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "goodixmoc"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_GOODIXMOC_DEVICE); +} + +static void +fu_goodixmoc_plugin_class_init(FuGoodixMocPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_goodixmoc_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-plugin.h b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..b254a253d41e7ffd7fee0b0a6858f117ffefd0c0 --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/fu-goodixmoc-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuGoodixMocPlugin, fu_goodixmoc_plugin, FU, GOODIXMOC_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/goodix-moc/goodixmoc.quirk b/fwupd-1.8.6/plugins/goodix-moc/goodixmoc.quirk new file mode 100644 index 0000000000000000000000000000000000000000..270cc8e667301b2f0d6e772aafe10df69fe40cac --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/goodixmoc.quirk @@ -0,0 +1,13 @@ +# Goodix Fingerprint sensor +[USB\VID_27C6&PID_60A2] +Plugin = goodixmoc +[USB\VID_27C6&PID_6384] +Plugin = goodixmoc +[USB\VID_27C6&PID_639C] +Plugin = goodixmoc +[USB\VID_27C6&PID_63AC] +Plugin = goodixmoc +[USB\VID_27C6&PID_6594] +Plugin = goodixmoc +[USB\VID_27C6&PID_6496] +Plugin = goodixmoc diff --git a/fwupd-1.8.6/plugins/goodix-moc/meson.build b/fwupd-1.8.6/plugins/goodix-moc/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..820566a1f6cac95fbe31a288f8d9d094a49aa255 --- /dev/null +++ b/fwupd-1.8.6/plugins/goodix-moc/meson.build @@ -0,0 +1,15 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginGoodixMoc"'] + +plugin_quirks += files('goodixmoc.quirk') +plugin_builtins += static_library('fu_plugin_goodixmoc', + sources: [ + 'fu-goodixmoc-device.c', + 'fu-goodixmoc-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/gpio/README.md b/fwupd-1.8.6/plugins/gpio/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e10bdfff61e8b4483e831bfe7e6be46d244cfc58 --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/README.md @@ -0,0 +1,34 @@ +# GPIO + +## Introduction + +This plugin sets GPIO outputs either high or low before and/or after an +update has been deployed. + +## GUID Generation + +These device use GPIO `gpiochip_info.label` values, e.g. + +* `GPIO\ID_INT3450:00` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### GpioForUpdate + +The GPIO bit to set before the update is deployed e.g. `INT3450:00,SPI_MUX,high`. +After the update has finished, the bits are returned to the default state. + +For example, to set GPIO pin 2 low for the duration of the ColorHug device update +this could be added to the quirk file: + + [USB\VID_273F&PID_1001] + GpioForUpdate=fake-gpio-chip,2,low + +Since: 1.7.6 + +## External Interface Access + +This plugin requires ioctl `GPIO_GET_CHIPINFO_IOCTL` and `GPIO_V2_GET_LINE_IOCTL` +access on `/dev/gpiochip*` devices. diff --git a/fwupd-1.8.6/plugins/gpio/fu-gpio-device.c b/fwupd-1.8.6/plugins/gpio/fu-gpio-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b8d65b7eff25b4333e2c30862a7dde508973ca8b --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/fu-gpio-device.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-gpio-device.h" + +struct _FuGpioDevice { + FuUdevDevice parent_instance; + guint num_lines; + gint fd; /* valid when the GPIO bit is assigned */ +}; + +G_DEFINE_TYPE(FuGpioDevice, fu_gpio_device, FU_TYPE_UDEV_DEVICE) + +#define FU_GPIO_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static void +fu_gpio_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuGpioDevice *self = FU_GPIO_DEVICE(device); + FU_DEVICE_CLASS(fu_gpio_device_parent_class)->to_string(device, idt, str); + fu_string_append_ku(str, idt, "NumLines", self->num_lines); + fu_string_append_kb(str, idt, "FdOpen", self->fd > 0); +} + +static gboolean +fu_gpio_device_probe(FuDevice *device, GError **error) +{ + /* no device file */ + if (fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no device file"); + return FALSE; + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "gpio", error); +} + +static gboolean +fu_gpio_device_setup(FuDevice *device, GError **error) +{ + FuGpioDevice *self = FU_GPIO_DEVICE(device); + struct gpiochip_info info = {0x0}; + + /* get info */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + GPIO_GET_CHIPINFO_IOCTL, + (guint8 *)&info, + NULL, + FU_GPIO_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to get chipinfo: "); + return FALSE; + } + + /* sanity check */ + self->num_lines = info.lines; + if (self->num_lines == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "0 lines is not supported"); + return FALSE; + } + + /* label is optional, but name is always set */ + if (info.label[0] != '\0') { + g_autofree gchar *logical_id = fu_strsafe(info.label, sizeof(info.label)); + fu_device_set_logical_id(device, logical_id); + + /* add instance ID */ + fu_device_add_instance_strsafe(device, "ID", logical_id); + if (!fu_device_build_instance_id(device, error, "GPIO", "ID", NULL)) + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_gpio_device_unassign(FuGpioDevice *self, GError **error) +{ + if (self->fd < 0) + return TRUE; + g_debug("unsetting %s", fu_device_get_logical_id(FU_DEVICE(self))); + if (!g_close(self->fd, error)) + return FALSE; + self->fd = -1; + return TRUE; +} + +static gboolean +fu_gpio_device_assign_full(FuGpioDevice *self, guint64 line, gboolean value, GError **error) +{ + const gchar consumer[] = "fwupd"; + struct gpio_v2_line_request req = { + .num_lines = 1, + req.offsets[0] = line, + .config.flags = GPIO_V2_LINE_FLAG_OUTPUT, + .config.num_attrs = 1, + .config.attrs[0].attr.values = value ? 0x1 : 0x0, + .config.attrs[0].mask = 0x1, + }; + + /* this is useful if we have contention with other tools */ + if (!fu_memcpy_safe((guint8 *)req.consumer, + sizeof(req.consumer), + 0x0, /* dst */ + (const guint8 *)consumer, + sizeof(consumer), + 0x0, /* src */ + sizeof(consumer), + error)) + return FALSE; + + /* slightly weird API, but roll with it */ + g_debug("setting %s:0x%02x → %i", + fu_device_get_logical_id(FU_DEVICE(self)), + (guint)line, + value); + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + GPIO_V2_GET_LINE_IOCTL, + (guint8 *)&req, + NULL, + FU_GPIO_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to assign: "); + return FALSE; + } + + /* success */ + self->fd = req.fd; + return TRUE; +} + +gboolean +fu_gpio_device_assign(FuGpioDevice *self, const gchar *id, gboolean value, GError **error) +{ + guint64 line = G_MAXUINT64; + + /* sanity check */ + if (self->fd > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "GPIO %s already in use", + id); + return FALSE; + } + + /* specified as a number, or look for @id as named pin */ + if (fu_strtoull(id, &line, 0, self->num_lines - 1, NULL)) { + struct gpio_v2_line_info info = {.offset = line}; + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + GPIO_V2_GET_LINEINFO_IOCTL, + (guint8 *)&info, + NULL, + FU_GPIO_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to get lineinfo: "); + return FALSE; + } + } else { + for (guint i = 0; i < self->num_lines; i++) { + struct gpio_v2_line_info info = {.offset = i}; + g_autofree gchar *name = NULL; + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + GPIO_V2_GET_LINEINFO_IOCTL, + (guint8 *)&info, + NULL, + FU_GPIO_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to get lineinfo: "); + return FALSE; + } + name = fu_strsafe(info.name, sizeof(info.name)); + if (g_strcmp0(name, id) == 0) { + line = i; + break; + } + } + } + if (line == G_MAXUINT64) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "failed to find %s", id); + return FALSE; + } + return fu_gpio_device_assign_full(self, line, value, error); +} + +static void +fu_gpio_device_init(FuGpioDevice *self) +{ + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), FU_UDEV_DEVICE_FLAG_OPEN_READ); +} + +static void +fu_gpio_device_finalize(GObject *object) +{ + FuGpioDevice *self = FU_GPIO_DEVICE(object); + if (self->fd > 0) + g_close(self->fd, NULL); + G_OBJECT_CLASS(fu_gpio_device_parent_class)->finalize(object); +} + +static void +fu_gpio_device_class_init(FuGpioDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_gpio_device_finalize; + klass_device->to_string = fu_gpio_device_to_string; + klass_device->setup = fu_gpio_device_setup; + klass_device->probe = fu_gpio_device_probe; +} diff --git a/fwupd-1.8.6/plugins/gpio/fu-gpio-device.h b/fwupd-1.8.6/plugins/gpio/fu-gpio-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5c05fc9479a9bdf1875bdf35e82c5f53b290a4df --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/fu-gpio-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_GPIO_DEVICE (fu_gpio_device_get_type()) +G_DECLARE_FINAL_TYPE(FuGpioDevice, fu_gpio_device, FU, GPIO_DEVICE, FuUdevDevice) + +gboolean +fu_gpio_device_assign(FuGpioDevice *self, const gchar *id, gboolean value, GError **error); +gboolean +fu_gpio_device_unassign(FuGpioDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/gpio/fu-gpio-plugin.c b/fwupd-1.8.6/plugins/gpio/fu-gpio-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..337a886043d80acae4786867e6f7d3fee9248db7 --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/fu-gpio-plugin.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-gpio-device.h" +#include "fu-gpio-plugin.h" + +struct _FuGpioPlugin { + FuPlugin parent_instance; + GPtrArray *current_logical_ids; /* element-type: utf-8 */ +}; + +G_DEFINE_TYPE(FuGpioPlugin, fu_gpio_plugin, FU_TYPE_PLUGIN) + +static void +fu_gpio_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuGpioPlugin *self = FU_GPIO_PLUGIN(plugin); + for (guint i = 0; i < self->current_logical_ids->len; i++) { + const gchar *current_logical_id = g_ptr_array_index(self->current_logical_ids, i); + g_autofree gchar *title = g_strdup_printf("CurrentLogicalId[0x%02x]", i); + fu_string_append(str, idt, title, current_logical_id); + } +} + +static gboolean +fu_gpio_plugin_parse_level(const gchar *str, gboolean *ret, GError **error) +{ + if (g_strcmp0(str, "high") == 0) { + *ret = TRUE; + return TRUE; + } + if (g_strcmp0(str, "low") == 0) { + *ret = FALSE; + return TRUE; + } + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot parse level, got %s and expected high|low", + str); + return FALSE; +} + +static gboolean +fu_gpio_plugin_process_quirk(FuPlugin *plugin, const gchar *str, GError **error) +{ + FuGpioPlugin *self = FU_GPIO_PLUGIN(plugin); + FuDevice *device_tmp; + gboolean value = FALSE; + g_auto(GStrv) split = g_strsplit(str, ",", -1); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* sanity check */ + if (g_strv_length(split) != 3) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid format, CHIP_NAME,PIN_NAME,LEVEL, got '%s'", + str); + return FALSE; + } + if (!fu_gpio_plugin_parse_level(split[2], &value, error)) + return FALSE; + device_tmp = fu_plugin_cache_lookup(plugin, split[0]); + if (device_tmp == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "GPIO device %s not found", + split[0]); + return FALSE; + } + locker = fu_device_locker_new(device_tmp, error); + if (locker == NULL) + return FALSE; + if (!fu_gpio_device_assign(FU_GPIO_DEVICE(device_tmp), split[1], value, error)) { + g_prefix_error(error, "failed to assign %s: ", split[0]); + return FALSE; + } + + /* success */ + g_ptr_array_add(self->current_logical_ids, g_strdup(fu_device_get_logical_id(device_tmp))); + return TRUE; +} + +static gboolean +fu_gpio_plugin_prepare(FuPlugin *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + GPtrArray *guids = fu_device_get_guids(device); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + const gchar *str; + str = fu_context_lookup_quirk_by_id(fu_plugin_get_context(self), + guid, + "GpioForUpdate"); + if (str == NULL) + continue; + if (!fu_gpio_plugin_process_quirk(self, str, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_gpio_plugin_cleanup(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuGpioPlugin *self = FU_GPIO_PLUGIN(plugin); + g_autoptr(GPtrArray) current_logical_ids = NULL; + + /* deep copy to local to clear transaction array */ + current_logical_ids = + g_ptr_array_copy(self->current_logical_ids, (GCopyFunc)g_strdup, NULL); + g_ptr_array_set_size(self->current_logical_ids, 0); + + /* close the fds we opened during ->prepare */ + for (guint i = 0; i < current_logical_ids->len; i++) { + FuDevice *device_tmp; + const gchar *current_logical_id = g_ptr_array_index(current_logical_ids, i); + + device_tmp = fu_plugin_cache_lookup(plugin, current_logical_id); + if (device_tmp == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "GPIO device %s no longer found", + current_logical_id); + return FALSE; + } + if (!fu_gpio_device_unassign(FU_GPIO_DEVICE(device_tmp), error)) { + g_prefix_error(error, "failed to unassign %s: ", current_logical_id); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_gpio_plugin_device_added(FuPlugin *self, FuDevice *device) +{ + fu_plugin_cache_add(self, fu_device_get_logical_id(device), device); +} + +static void +fu_gpio_plugin_init(FuGpioPlugin *self) +{ + self->current_logical_ids = g_ptr_array_new_with_free_func(g_free); +} + +static void +fu_gpio_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "GpioForUpdate"); + fu_plugin_add_udev_subsystem(plugin, "gpio"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_GPIO_DEVICE); +} + +static void +fu_gpio_finalize(GObject *obj) +{ + FuGpioPlugin *self = FU_GPIO_PLUGIN(obj); + g_ptr_array_unref(self->current_logical_ids); + G_OBJECT_CLASS(fu_gpio_plugin_parent_class)->finalize(obj); +} + +static void +fu_gpio_plugin_class_init(FuGpioPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_gpio_plugin_constructed; + object_class->finalize = fu_gpio_finalize; + plugin_class->to_string = fu_gpio_plugin_to_string; + plugin_class->prepare = fu_gpio_plugin_prepare; + plugin_class->cleanup = fu_gpio_plugin_cleanup; + plugin_class->device_added = fu_gpio_plugin_device_added; +} diff --git a/fwupd-1.8.6/plugins/gpio/fu-gpio-plugin.h b/fwupd-1.8.6/plugins/gpio/fu-gpio-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..a3c0b3bb6b31e4d775cfd8ff83f53cb0755c8147 --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/fu-gpio-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuGpioPlugin, fu_gpio_plugin, FU, GPIO_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/gpio/gpio.quirk b/fwupd-1.8.6/plugins/gpio/gpio.quirk new file mode 100644 index 0000000000000000000000000000000000000000..0400f0533f60bf29dd1355a3cff41854998bd39f --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/gpio.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[GPIO] +Plugin = gpio diff --git a/fwupd-1.8.6/plugins/gpio/meson.build b/fwupd-1.8.6/plugins/gpio/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..63b7b47a238418c8cc23be094fc078a82fc3e90c --- /dev/null +++ b/fwupd-1.8.6/plugins/gpio/meson.build @@ -0,0 +1,20 @@ +gpio_header = cc.has_header_symbol('linux/gpio.h', 'GPIO_V2_LINE_FLAG_OUTPUT', required: get_option('plugin_gpio')) + +if gpio_header and gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginGpio"'] + +plugin_quirks += files('gpio.quirk') +plugin_builtins += static_library('fu_plugin_gpio', + sources: [ + 'fu-gpio-plugin.c', + 'fu-gpio-device.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/hailuck/README.md b/fwupd-1.8.6/plugins/hailuck/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cc09ad57fc2e68e96cc2431bf4c5569f4b90303e --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/README.md @@ -0,0 +1,42 @@ +# Hailuck + +## Introduction + +Hailuck produce the firmware used on the keyboard and trackpad used in the +Pinebook Pro laptops. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* com.hailuck.kbd +* com.hailuck.tp + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_0603&PID_1020` + +## Update Behavior + +The keyboard device usually presents in runtime mode, but on detach it +re-enumerates with a different USB VID and PID in bootloader mode. On attach +the device again re-enumerates back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +The touchpad firmware is deployed when the device is in normal runtime mode, +and the device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x0603` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/hailuck/data/lspci-bl.txt b/fwupd-1.8.6/plugins/hailuck/data/lspci-bl.txt new file mode 100644 index 0000000000000000000000000000000000000000..7bde15eb64b9fc8e03382f0b970af19df6b347c7 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/data/lspci-bl.txt @@ -0,0 +1,47 @@ +Bus 003 Device 003: ID 0603:1020 Novatek Microelectronics Corp. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x0603 Novatek Microelectronics Corp. + idProduct 0x1020 + bcdDevice 3.01 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0022 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 10 diff --git a/fwupd-1.8.6/plugins/hailuck/data/lspci.txt b/fwupd-1.8.6/plugins/hailuck/data/lspci.txt new file mode 100644 index 0000000000000000000000000000000000000000..d2cb00f5c28c3acb3e27118c4851a8c41789ba88 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/data/lspci.txt @@ -0,0 +1,89 @@ +Bus 003 Device 008: ID 258a:001e HAILUCK CO.,LTD USB KEYBOARD +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x258a + idProduct 0x001e + bcdDevice 1.00 + iManufacturer 1 HAILUCK CO.,LTD + iProduct 2 USB KEYBOARD + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x003b + bNumInterfaces 2 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 65 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 10 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 487 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 10 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-bl-device.c b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-bl-device.c new file mode 100644 index 0000000000000000000000000000000000000000..fc60127ffd446a56bfa985769d701cb5306fa6e0 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-bl-device.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-hailuck-bl-device.h" +#include "fu-hailuck-common.h" +#include "fu-hailuck-kbd-firmware.h" + +struct _FuHailuckBlDevice { + FuHidDevice parent_instance; +}; + +G_DEFINE_TYPE(FuHailuckBlDevice, fu_hailuck_bl_device, FU_TYPE_HID_DEVICE) + +static gboolean +fu_hailuck_bl_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 buf[6] = { + FU_HAILUCK_REPORT_ID_SHORT, + FU_HAILUCK_CMD_ATTACH, + }; + if (!fu_hid_device_set_report(FU_HID_DEVICE(device), + buf[0], + buf, + sizeof(buf), + 1000, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + if (!g_usb_device_reset(fu_usb_device_get_dev(FU_USB_DEVICE(device)), error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_hailuck_bl_device_probe(FuDevice *device, GError **error) +{ + /* add instance ID */ + fu_device_add_instance_str(device, "MODE", "KBD"); + return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "MODE", NULL); +} + +static gboolean +fu_hailuck_bl_device_read_block_start(FuHailuckBlDevice *self, guint32 length, GError **error) +{ + guint8 buf[6] = { + FU_HAILUCK_REPORT_ID_SHORT, + FU_HAILUCK_CMD_READ_BLOCK_START, + }; + fu_memwrite_uint16(buf + 4, length, G_LITTLE_ENDIAN); + return fu_hid_device_set_report(FU_HID_DEVICE(self), + buf[0], + buf, + sizeof(buf), + 100, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error); +} + +static gboolean +fu_hailuck_bl_device_read_block(FuHailuckBlDevice *self, + guint8 *data, + gsize data_sz, + GError **error) +{ + gsize bufsz = data_sz + 2; + g_autofree guint8 *buf = g_malloc0(bufsz); + + buf[0] = FU_HAILUCK_REPORT_ID_LONG; + buf[1] = FU_HAILUCK_CMD_READ_BLOCK; + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + buf[0], + buf, + bufsz, + 2000, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + if (!fu_memcpy_safe(data, + data_sz, + 0x0, /* dst */ + buf, + bufsz, + 0x02, /* src */ + data_sz, + error)) + return FALSE; + + /* success */ + g_usleep(10000); + return TRUE; +} + +static GBytes * +fu_hailuck_bl_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuHailuckBlDevice *self = FU_HAILUCK_BL_DEVICE(device); + gsize fwsz = fu_device_get_firmware_size_max(device); + g_autoptr(GByteArray) fwbuf = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + /* tell device amount of data to send */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + if (!fu_hailuck_bl_device_read_block_start(self, fwsz, error)) + return NULL; + + /* receive data back */ + fu_byte_array_set_size(fwbuf, fwsz, 0x00); + chunks = fu_chunk_array_mutable_new(fwbuf->data, fwbuf->len, 0x0, 0x0, 2048); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_hailuck_bl_device_read_block(self, + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) + return NULL; + fu_progress_step_done(progress); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&fwbuf)); +} + +static gboolean +fu_hailuck_bl_device_erase(FuHailuckBlDevice *self, FuProgress *progress, GError **error) +{ + guint8 buf[6] = { + FU_HAILUCK_REPORT_ID_SHORT, + FU_HAILUCK_CMD_ERASE, + }; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + buf[0], + buf, + sizeof(buf), + 100, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + fu_progress_sleep(progress, 2000); + return TRUE; +} + +static gboolean +fu_hailuck_bl_device_write_block_start(FuHailuckBlDevice *self, guint32 length, GError **error) +{ + guint8 buf[6] = { + FU_HAILUCK_REPORT_ID_SHORT, + FU_HAILUCK_CMD_WRITE_BLOCK_START, + }; + fu_memwrite_uint16(buf + 4, length, G_LITTLE_ENDIAN); + return fu_hid_device_set_report(FU_HID_DEVICE(self), + buf[0], + buf, + sizeof(buf), + 100, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error); +} + +static gboolean +fu_hailuck_bl_device_write_block(FuHailuckBlDevice *self, + const guint8 *data, + gsize data_sz, + GError **error) +{ + gsize bufsz = data_sz + 2; + g_autofree guint8 *buf = g_malloc0(bufsz); + + buf[0] = FU_HAILUCK_REPORT_ID_LONG; + buf[1] = FU_HAILUCK_CMD_WRITE_BLOCK; + if (!fu_memcpy_safe(buf, + bufsz, + 0x02, /* dst */ + data, + data_sz, + 0x0, /* src */ + data_sz, + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + buf[0], + buf, + bufsz, + 2000, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + + /* success */ + g_usleep(10000); + return TRUE; +} + +static gboolean +fu_hailuck_bl_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuHailuckBlDevice *self = FU_HAILUCK_BL_DEVICE(device); + FuChunk *chk0; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_new = NULL; + g_autoptr(GPtrArray) chunks = NULL; + g_autofree guint8 *chk0_data = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write-blk0"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 9, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* erase all contents */ + if (!fu_hailuck_bl_device_erase(self, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* tell device amount of data to expect */ + if (!fu_hailuck_bl_device_write_block_start(self, g_bytes_get_size(fw), error)) + return FALSE; + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x00, 2048); + + /* intentionally corrupt first chunk so that CRC fails */ + chk0 = g_ptr_array_index(chunks, 0); + chk0_data = fu_memdup_safe(fu_chunk_get_data(chk0), fu_chunk_get_data_sz(chk0), error); + if (chk0_data == NULL) + return FALSE; + chk0_data[0] = 0x00; + if (!fu_hailuck_bl_device_write_block(self, chk0_data, fu_chunk_get_data_sz(chk0), error)) + return FALSE; + + /* send the rest of the chunks */ + for (guint i = 1; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_hailuck_bl_device_write_block(self, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + chunks->len); + } + fu_progress_step_done(progress); + + /* retry write of first block */ + if (!fu_hailuck_bl_device_write_block_start(self, g_bytes_get_size(fw), error)) + return FALSE; + if (!fu_hailuck_bl_device_write_block(self, + fu_chunk_get_data(chk0), + fu_chunk_get_data_sz(chk0), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + fw_new = fu_hailuck_bl_device_dump_firmware(device, fu_progress_get_child(progress), error); + fu_progress_step_done(progress); + return fu_bytes_compare(fw, fw_new, error); +} + +static void +fu_hailuck_bl_device_init(FuHailuckBlDevice *self) +{ + fu_device_set_firmware_size(FU_DEVICE(self), 0x4000); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_HAILUCK_KBD_FIRMWARE); + fu_device_add_protocol(FU_DEVICE(self), "com.hailuck.kbd"); + fu_device_set_name(FU_DEVICE(self), "Keyboard [bootloader]"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); + fu_hid_device_add_flag(FU_HID_DEVICE(self), FU_HID_DEVICE_FLAG_NO_KERNEL_REBIND); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_hailuck_bl_device_class_init(FuHailuckBlDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->dump_firmware = fu_hailuck_bl_device_dump_firmware; + klass_device->write_firmware = fu_hailuck_bl_device_write_firmware; + klass_device->attach = fu_hailuck_bl_device_attach; + klass_device->probe = fu_hailuck_bl_device_probe; +} diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-bl-device.h b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-bl-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ae0fed5065a9f2276496c1256e2a8cac144ad486 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-bl-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_HAILUCK_BL_DEVICE (fu_hailuck_bl_device_get_type()) +G_DECLARE_FINAL_TYPE(FuHailuckBlDevice, fu_hailuck_bl_device, FU, HAILUCK_BL_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-common.c b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-common.c new file mode 100644 index 0000000000000000000000000000000000000000..6d23f2b8b0b9cba41c69c4e5e960028866782344 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-common.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-hailuck-common.h" + +const gchar * +fu_hailuck_cmd_to_string(guint8 cmd) +{ + if (cmd == FU_HAILUCK_CMD_ERASE) + return "erase"; + if (cmd == FU_HAILUCK_CMD_READ_BLOCK_START) + return "read-block-start"; + if (cmd == FU_HAILUCK_CMD_WRITE_BLOCK_START) + return "write-block-start"; + if (cmd == FU_HAILUCK_CMD_READ_BLOCK) + return "read-block"; + if (cmd == FU_HAILUCK_CMD_WRITE_BLOCK) + return "write-block"; + if (cmd == FU_HAILUCK_CMD_GET_STATUS) + return "get-status"; + if (cmd == FU_HAILUCK_CMD_DETACH) + return "detach"; + if (cmd == FU_HAILUCK_CMD_ATTACH) + return "attach"; + if (cmd == FU_HAILUCK_CMD_WRITE_TP) + return "write-tp"; + if (cmd == FU_HAILUCK_CMD_I2C_CHECK_CHECKSUM) + return "i2c-check-checksum"; + if (cmd == FU_HAILUCK_CMD_I2C_ENTER_BL) + return "i2c-enter-bl"; + if (cmd == FU_HAILUCK_CMD_I2C_ERASE) + return "i2c-erase"; + if (cmd == FU_HAILUCK_CMD_I2C_PROGRAM) + return "i2c-program"; + if (cmd == FU_HAILUCK_CMD_I2C_VERIFY_BLOCK) + return "i2c-verify-block"; + if (cmd == FU_HAILUCK_CMD_I2C_VERIFY_CHECKSUM) + return "i2c-verify-checksum"; + if (cmd == FU_HAILUCK_CMD_I2C_PROGRAMPASS) + return "i2c-programpass"; + if (cmd == FU_HAILUCK_CMD_I2C_END_PROGRAM) + return "i2c-end-program"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-common.h b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-common.h new file mode 100644 index 0000000000000000000000000000000000000000..9e87339d3dc54d5704b180db3475c575439460af --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-common.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_HAILUCK_REPORT_ID_SHORT 0x05 +#define FU_HAILUCK_REPORT_ID_LONG 0x06 + +#define FU_HAILUCK_CMD_ERASE 0x45 +#define FU_HAILUCK_CMD_READ_BLOCK_START 0x52 +#define FU_HAILUCK_CMD_ATTACH 0x55 /* guessed */ +#define FU_HAILUCK_CMD_WRITE_BLOCK_START 0x57 +#define FU_HAILUCK_CMD_READ_BLOCK 0x72 +#define FU_HAILUCK_CMD_DETACH 0x75 /* guessed */ +#define FU_HAILUCK_CMD_WRITE_BLOCK 0x77 +#define FU_HAILUCK_CMD_GET_STATUS 0xA1 +#define FU_HAILUCK_CMD_WRITE_TP 0xD0 /* guessed */ +#define FU_HAILUCK_CMD_I2C_CHECK_CHECKSUM 0xF0 +#define FU_HAILUCK_CMD_I2C_ENTER_BL 0xF1 +#define FU_HAILUCK_CMD_I2C_ERASE 0xF2 +#define FU_HAILUCK_CMD_I2C_PROGRAM 0xF3 +#define FU_HAILUCK_CMD_I2C_VERIFY_BLOCK 0xF4 +#define FU_HAILUCK_CMD_I2C_VERIFY_CHECKSUM 0xF5 +#define FU_HAILUCK_CMD_I2C_PROGRAMPASS 0xF6 +#define FU_HAILUCK_CMD_I2C_END_PROGRAM 0xF7 + +const gchar * +fu_hailuck_cmd_to_string(guint8 cmd); diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-device.c b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..dfda6881347f9b412422541e4d3f8d6fcb6bb1bd --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-device.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-hailuck-common.h" +#include "fu-hailuck-kbd-device.h" +#include "fu-hailuck-tp-device.h" + +struct _FuHailuckKbdDevice { + FuHidDevice parent_instance; +}; + +G_DEFINE_TYPE(FuHailuckKbdDevice, fu_hailuck_kbd_device, FU_TYPE_HID_DEVICE) + +static gboolean +fu_hailuck_kbd_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 buf[6] = {FU_HAILUCK_REPORT_ID_SHORT, FU_HAILUCK_CMD_DETACH}; + if (!fu_hid_device_set_report(FU_HID_DEVICE(device), + buf[0], + buf, + sizeof(buf), + 1000, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_hailuck_kbd_device_probe(FuDevice *device, GError **error) +{ + g_autoptr(FuHailuckTpDevice) tp_device = fu_hailuck_tp_device_new(FU_DEVICE(device)); + + /* add extra keyboard-specific GUID */ + fu_device_add_instance_str(device, "MODE", "KBD"); + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "MODE", NULL)) + return FALSE; + + /* add touchpad */ + if (!fu_device_probe(FU_DEVICE(tp_device), error)) + return FALSE; + + /* assume the TP has the same version as the keyboard */ + fu_device_set_version(FU_DEVICE(tp_device), fu_device_get_version(device)); + fu_device_set_version_format(FU_DEVICE(tp_device), fu_device_get_version_format(device)); + fu_device_add_child(device, FU_DEVICE(tp_device)); + + /* success */ + return TRUE; +} + +static void +fu_hailuck_kbd_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_hailuck_kbd_device_init(FuHailuckKbdDevice *self) +{ + fu_device_set_firmware_size(FU_DEVICE(self), 0x4000); + fu_device_add_protocol(FU_DEVICE(self), "com.hailuck.kbd"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); + fu_hid_device_set_interface(FU_HID_DEVICE(self), 0x1); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_hailuck_kbd_device_class_init(FuHailuckKbdDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->detach = fu_hailuck_kbd_device_detach; + klass_device->probe = fu_hailuck_kbd_device_probe; + klass_device->set_progress = fu_hailuck_kbd_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-device.h b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f70e75500fdab6e630df6693453e90a1756d3f3d --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_HAILUCK_KBD_DEVICE (fu_hailuck_kbd_device_get_type()) +G_DECLARE_FINAL_TYPE(FuHailuckKbdDevice, fu_hailuck_kbd_device, FU, HAILUCK_KBD_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-firmware.c b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..9064eaf74cda72f1014b0befb51d92267b6ccb4c --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-firmware.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-hailuck-kbd-firmware.h" + +struct _FuHailuckKbdFirmware { + FuIhexFirmwareClass parent_instance; +}; + +G_DEFINE_TYPE(FuHailuckKbdFirmware, fu_hailuck_kbd_firmware, FU_TYPE_IHEX_FIRMWARE) + +static gboolean +fu_hailuck_kbd_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + GPtrArray *records = fu_ihex_firmware_get_records(FU_IHEX_FIRMWARE(firmware)); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) fw_new = NULL; + + for (guint j = 0; j < records->len; j++) { + FuIhexFirmwareRecord *rcd = g_ptr_array_index(records, j); + if (rcd->record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EOF) + break; + if (rcd->record_type != FU_IHEX_FIRMWARE_RECORD_TYPE_DATA) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only record 0x0 supported, got 0x%02x", + rcd->record_type); + return FALSE; + } + if (rcd->data->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "record 0x%x had zero size", + j); + return FALSE; + } + if (rcd->addr + rcd->data->len > buf->len) { + if (rcd->addr + rcd->data->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "buffer would have zero size"); + return FALSE; + } + fu_byte_array_set_size(buf, rcd->addr + rcd->data->len, 0x00); + } + if (!fu_memcpy_safe(buf->data, + buf->len, + rcd->addr, + rcd->data->data, + rcd->data->len, + 0x0, + rcd->data->len, + error)) + return FALSE; + } + + /* set the main function executed on system init */ + if (buf->len > 0x37FD && buf->data[1] == 0x38 && buf->data[2] == 0x00) { + buf->data[0] = buf->data[0x37FB]; + buf->data[1] = buf->data[0x37FC]; + buf->data[2] = buf->data[0x37FD]; + buf->data[0x37FB] = 0x00; + buf->data[0x37FC] = 0x00; + buf->data[0x37FD] = 0x00; + } + + /* whole image */ + fw_new = g_byte_array_free_to_bytes(g_steal_pointer(&buf)); + fu_firmware_set_bytes(firmware, fw_new); + return TRUE; +} + +static void +fu_hailuck_kbd_firmware_init(FuHailuckKbdFirmware *self) +{ +} + +static void +fu_hailuck_kbd_firmware_class_init(FuHailuckKbdFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_hailuck_kbd_firmware_parse; +} + +FuFirmware * +fu_hailuck_kbd_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_HAILUCK_KBD_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-firmware.h b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..5f515f52a24cd70f9d2c82477b90fa769b10c8cf --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-kbd-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_HAILUCK_KBD_FIRMWARE (fu_hailuck_kbd_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuHailuckKbdFirmware, + fu_hailuck_kbd_firmware, + FU, + HAILUCK_KBD_FIRMWARE, + FuIhexFirmware) + +FuFirmware * +fu_hailuck_kbd_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-plugin.c b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..4fdbf0ef3e011755efbdcd05d3079829c02a94d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-plugin.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-hailuck-bl-device.h" +#include "fu-hailuck-kbd-device.h" +#include "fu-hailuck-kbd-firmware.h" +#include "fu-hailuck-plugin.h" + +struct _FuHailuckPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuHailuckPlugin, fu_hailuck_plugin, FU_TYPE_PLUGIN) + +static void +fu_hailuck_plugin_init(FuHailuckPlugin *self) +{ +} + +static void +fu_hailuck_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_HAILUCK_KBD_FIRMWARE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_HAILUCK_BL_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_HAILUCK_KBD_DEVICE); +} + +static void +fu_hailuck_plugin_class_init(FuHailuckPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_hailuck_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-plugin.h b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..5e36933534f6b40b3828e5a9ab3537c41017e554 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuHailuckPlugin, fu_hailuck_plugin, FU, HAILUCK_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-tp-device.c b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-tp-device.c new file mode 100644 index 0000000000000000000000000000000000000000..6b58c2bd200c7c6ae257617ea566ed88474a3518 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-tp-device.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-hailuck-common.h" +#include "fu-hailuck-tp-device.h" + +struct _FuHailuckTpDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuHailuckTpDevice, fu_hailuck_tp_device, FU_TYPE_DEVICE) + +static gboolean +fu_hailuck_tp_device_probe(FuDevice *device, GError **error) +{ + /* add extra touchpad-specific GUID */ + fu_device_add_instance_str(device, "MODE", "TP"); + return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "MODE", NULL); +} + +typedef struct { + guint8 type; + guint8 success; /* if 0xff, then cmd-0x10 */ +} FuHailuckTpDeviceReq; + +static gboolean +fu_hailuck_tp_device_cmd_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + FuHailuckTpDeviceReq *req = (FuHailuckTpDeviceReq *)user_data; + guint8 buf[6] = { + FU_HAILUCK_REPORT_ID_SHORT, + FU_HAILUCK_CMD_GET_STATUS, + req->type, + }; + guint8 success_tmp = req->success; + if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), + buf[0], + buf, + sizeof(buf), + 1000, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + if (!fu_hid_device_get_report(FU_HID_DEVICE(parent), + buf[0], + buf, + sizeof(buf), + 2000, + FU_HID_DEVICE_FLAG_IS_FEATURE | + FU_HID_DEVICE_FLAG_ALLOW_TRUNC, + error)) + return FALSE; + if (success_tmp == 0xff) + success_tmp = req->type - 0x10; + if (buf[0] != FU_HAILUCK_REPORT_ID_SHORT || buf[1] != success_tmp) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "report mismatch for type=0x%02x[%s]: " + "expected=0x%02x, received=0x%02x", + req->type, + fu_hailuck_cmd_to_string(req->type), + success_tmp, + buf[1]); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_hailuck_tp_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + const guint block_size = 1024; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + FuHailuckTpDeviceReq req = { + .type = 0xff, + .success = 0xff, + }; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 85, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "end-program"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 3, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "pass"); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* erase */ + req.type = FU_HAILUCK_CMD_I2C_ERASE; + if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { + g_prefix_error(error, "failed to erase: "); + return FALSE; + } + g_usleep(10000); + fu_progress_step_done(progress); + + /* write */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, block_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* write block */ + fu_byte_array_append_uint8(buf, FU_HAILUCK_REPORT_ID_LONG); + fu_byte_array_append_uint8(buf, FU_HAILUCK_CMD_WRITE_TP); + fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); + g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + fu_byte_array_append_uint8(buf, 0xEE); + fu_byte_array_append_uint8(buf, 0xD2); + fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); + if (buf->len != block_size + 16) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "packet mismatch: len=0x%04x, expected=0x%04x", + buf->len, + block_size + 16); + return FALSE; + } + if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), + buf->data[0], + buf->data, + buf->len, + 1000, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) { + g_prefix_error(error, "failed to write block 0x%x: ", i); + return FALSE; + } + g_usleep(150 * 1000); + + /* verify block */ + req.type = FU_HAILUCK_CMD_I2C_VERIFY_BLOCK; + if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { + g_prefix_error(error, "failed to verify block 0x%x: ", i); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + chunks->len); + } + g_usleep(50 * 1000); + fu_progress_step_done(progress); + + /* end-program */ + req.type = FU_HAILUCK_CMD_I2C_END_PROGRAM; + if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { + g_prefix_error(error, "failed to end program: "); + return FALSE; + } + g_usleep(50 * 1000); + fu_progress_step_done(progress); + + /* verify checksum */ + req.type = FU_HAILUCK_CMD_I2C_VERIFY_CHECKSUM; + if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { + g_prefix_error(error, "failed to verify: "); + return FALSE; + } + g_usleep(50 * 1000); + fu_progress_step_done(progress); + + /* signal that programming has completed */ + req.type = FU_HAILUCK_CMD_I2C_PROGRAMPASS; + req.success = 0x0; + if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { + g_prefix_error(error, "failed to program: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_hailuck_tp_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_hailuck_tp_device_init(FuHailuckTpDevice *self) +{ + fu_device_retry_set_delay(FU_DEVICE(self), 50); /* ms */ + fu_device_set_firmware_size(FU_DEVICE(self), 0x6018); + fu_device_add_protocol(FU_DEVICE(self), "com.hailuck.tp"); + fu_device_set_logical_id(FU_DEVICE(self), "TP"); + fu_device_set_name(FU_DEVICE(self), "Touchpad"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_hailuck_tp_device_class_init(FuHailuckTpDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_hailuck_tp_device_write_firmware; + klass_device->probe = fu_hailuck_tp_device_probe; + klass_device->set_progress = fu_hailuck_tp_device_set_progress; +} + +FuHailuckTpDevice * +fu_hailuck_tp_device_new(FuDevice *device) +{ + FuHailuckTpDevice *self; + self = g_object_new(FU_TYPE_HAILUCK_TP_DEVICE, "parent", device, NULL); + return FU_HAILUCK_TP_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/hailuck/fu-hailuck-tp-device.h b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-tp-device.h new file mode 100644 index 0000000000000000000000000000000000000000..c5f8420812389fd468a28ecd08a87e8e7a601912 --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/fu-hailuck-tp-device.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_HAILUCK_TP_DEVICE (fu_hailuck_tp_device_get_type()) +G_DECLARE_FINAL_TYPE(FuHailuckTpDevice, fu_hailuck_tp_device, FU, HAILUCK_TP_DEVICE, FuDevice) + +FuHailuckTpDevice * +fu_hailuck_tp_device_new(FuDevice *parent); diff --git a/fwupd-1.8.6/plugins/hailuck/hailuck.quirk b/fwupd-1.8.6/plugins/hailuck/hailuck.quirk new file mode 100644 index 0000000000000000000000000000000000000000..90726ff4f4b0a18d96471e71c42c8d90a66fc23e --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/hailuck.quirk @@ -0,0 +1,25 @@ +# bootloader + +[USB\VID_0603&PID_1020] +Plugin = hailuck +GType = FuHailuckBlDevice +#Flags = is-bootloader + +[USB\VID_258A&PID_001E] +Plugin = hailuck +GType = FuHailuckKbdDevice +Vendor = PINE64 +CounterpartGuid = USB\VID_0603&PID_1020 + +[USB\VID_258A&PID_001E&MODE_KBD] +Name = Keyboard + +[USB\VID_258A&PID_001F] +Plugin = hailuck +GType = FuHailuckKbdDevice +CounterpartGuid = USB\VID_0603&PID_1020 + +[USB\VID_258A&PID_000D] +Plugin = hailuck +GType = FuHailuckKbdDevice +CounterpartGuid = USB\VID_0603&PID_1020 diff --git a/fwupd-1.8.6/plugins/hailuck/meson.build b/fwupd-1.8.6/plugins/hailuck/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..13274e20da4947477e71e406f0e111daec22bb5c --- /dev/null +++ b/fwupd-1.8.6/plugins/hailuck/meson.build @@ -0,0 +1,19 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginNovatek"'] + +plugin_quirks += files('hailuck.quirk') +plugin_builtins += static_library('fu_plugin_hailuck', + sources: [ + 'fu-hailuck-common.c', + 'fu-hailuck-bl-device.c', + 'fu-hailuck-kbd-device.c', + 'fu-hailuck-kbd-firmware.c', # fuzzing + 'fu-hailuck-tp-device.c', + 'fu-hailuck-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/intel-spi/README.md b/fwupd-1.8.6/plugins/intel-spi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..516b135b0814615070a1510e501a7bdeffcc7db5 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/README.md @@ -0,0 +1,11 @@ +# Intel SPI + +## Introduction + +This plugin verifies the SPI contents, typically an Intel Flash descriptor. +The result will be stored in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/dev/port` and thus will not work if the +kernel is locked down. diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-ifd-device.c b/fwupd-1.8.6/plugins/intel-spi/fu-ifd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..6ac907d664cf2a3e0ef8040d404e5ece984b94f6 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-ifd-device.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ifd-device.h" +#include "fu-intel-spi-device.h" + +typedef struct { + FuIfdRegion region; + guint32 offset; + FuIfdAccess access[FU_IFD_REGION_MAX]; +} FuIfdDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuIfdDevice, fu_ifd_device, FU_TYPE_DEVICE) + +#define GET_PRIVATE(o) (fu_ifd_device_get_instance_private(o)) + +static void +fu_ifd_device_set_region(FuIfdDevice *self, FuIfdRegion region) +{ + FuIfdDevicePrivate *priv = GET_PRIVATE(self); + const gchar *region_str = fu_ifd_region_to_string(region); + + priv->region = region; + fu_device_set_name(FU_DEVICE(self), fu_ifd_region_to_name(region)); + fu_device_set_logical_id(FU_DEVICE(self), region_str); + + /* add instance ID */ + fu_device_add_instance_strup(FU_DEVICE(self), "NAME", region_str); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "IFD", "NAME", NULL); +} + +static void +fu_ifd_device_set_freg(FuIfdDevice *self, guint32 freg) +{ + FuIfdDevicePrivate *priv = GET_PRIVATE(self); + guint32 freg_base = FU_IFD_FREG_BASE(freg); + guint32 freg_limt = FU_IFD_FREG_LIMIT(freg); + guint32 freg_size = (freg_limt - freg_base) + 1; + + priv->offset = freg_base; + fu_device_set_firmware_size(FU_DEVICE(self), freg_size); +} + +void +fu_ifd_device_set_access(FuIfdDevice *self, FuIfdRegion region, FuIfdAccess access) +{ + FuIfdDevicePrivate *priv = GET_PRIVATE(self); + priv->access[region] = access; +} + +static void +fu_ifd_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuIfdDevice *self = FU_IFD_DEVICE(device); + FuIfdDevicePrivate *priv = GET_PRIVATE(self); + + fu_string_append(str, idt, "Region", fu_ifd_region_to_string(priv->region)); + fu_string_append_kx(str, idt, "Offset", priv->offset); + + for (guint i = 0; i < FU_IFD_REGION_MAX; i++) { + g_autofree gchar *title = NULL; + if (priv->access[i] == FU_IFD_ACCESS_NONE) + continue; + title = g_strdup_printf("Access[%s]", fu_ifd_region_to_string(i)); + fu_string_append(str, idt, title, fu_ifd_access_to_string(priv->access[i])); + } +} + +static GBytes * +fu_ifd_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuIfdDevice *self = FU_IFD_DEVICE(device); + FuIfdDevicePrivate *priv = GET_PRIVATE(self); + FuDevice *parent = fu_device_get_parent(device); + guint64 total_size = fu_device_get_firmware_size_max(device); + return fu_intel_spi_device_dump(FU_INTEL_SPI_DEVICE(parent), + device, + priv->offset, + total_size, + progress, + error); +} + +static FuFirmware * +fu_ifd_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuIfdDevice *self = FU_IFD_DEVICE(device); + FuIfdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = fu_ifd_image_new(); + g_autoptr(GBytes) blob = NULL; + + blob = fu_ifd_device_dump_firmware(device, progress, error); + if (blob == NULL) + return NULL; + if (priv->region == FU_IFD_REGION_BIOS) + firmware = fu_ifd_bios_new(); + else + firmware = fu_ifd_image_new(); + if (!fu_firmware_parse(firmware, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return NULL; + return g_steal_pointer(&firmware); +} + +static void +fu_ifd_device_init(FuIfdDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_icon(FU_DEVICE(self), "computer"); +} + +static void +fu_ifd_device_class_init(FuIfdDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_ifd_device_to_string; + klass_device->dump_firmware = fu_ifd_device_dump_firmware; + klass_device->read_firmware = fu_ifd_device_read_firmware; +} + +FuDevice * +fu_ifd_device_new(FuContext *ctx, FuIfdRegion region, guint32 freg) +{ + FuIfdDevice *self = FU_IFD_DEVICE(g_object_new(FU_TYPE_IFD_DEVICE, "context", ctx, NULL)); + fu_ifd_device_set_region(self, region); + fu_ifd_device_set_freg(self, freg); + return FU_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-ifd-device.h b/fwupd-1.8.6/plugins/intel-spi/fu-ifd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..4a945f445c8c838d5929afc7cd98ba6355ef9ad3 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-ifd-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_IFD_DEVICE (fu_ifd_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuIfdDevice, fu_ifd_device, FU, IFD_DEVICE, FuDevice) + +struct _FuIfdDeviceClass { + FuDeviceClass parent_class; +}; + +FuDevice * +fu_ifd_device_new(FuContext *ctx, FuIfdRegion region, guint32 freg); +void +fu_ifd_device_set_access(FuIfdDevice *self, FuIfdRegion region, FuIfdAccess access); diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-common.c b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-common.c new file mode 100644 index 0000000000000000000000000000000000000000..0f42f40a46ed61b0bb97eacb7c1700e207009eae --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-common.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include "fu-intel-spi-common.h" + +FuIntelSpiKind +fu_intel_spi_kind_from_string(const gchar *kind) +{ + if (g_strcmp0(kind, "ich9") == 0) + return FU_INTEL_SPI_KIND_ICH9; + if (g_strcmp0(kind, "pch100") == 0) + return FU_INTEL_SPI_KIND_PCH100; + if (g_strcmp0(kind, "apl") == 0) + return FU_INTEL_SPI_KIND_APL; + if (g_strcmp0(kind, "c620") == 0) + return FU_INTEL_SPI_KIND_C620; + if (g_strcmp0(kind, "ich0") == 0) + return FU_INTEL_SPI_KIND_ICH0; + if (g_strcmp0(kind, "ich2345") == 0) + return FU_INTEL_SPI_KIND_ICH2345; + if (g_strcmp0(kind, "ich6") == 0) + return FU_INTEL_SPI_KIND_ICH6; + if (g_strcmp0(kind, "pch200") == 0) + return FU_INTEL_SPI_KIND_PCH200; + if (g_strcmp0(kind, "pch300") == 0) + return FU_INTEL_SPI_KIND_PCH300; + if (g_strcmp0(kind, "pch400") == 0) + return FU_INTEL_SPI_KIND_PCH400; + if (g_strcmp0(kind, "poulsbo") == 0) + return FU_INTEL_SPI_KIND_POULSBO; + return FU_INTEL_SPI_KIND_UNKNOWN; +} + +const gchar * +fu_intel_spi_kind_to_string(FuIntelSpiKind kind) +{ + if (kind == FU_INTEL_SPI_KIND_ICH9) + return "ich9"; + if (kind == FU_INTEL_SPI_KIND_PCH100) + return "pch100"; + if (kind == FU_INTEL_SPI_KIND_APL) + return "apl"; + if (kind == FU_INTEL_SPI_KIND_C620) + return "c620"; + if (kind == FU_INTEL_SPI_KIND_ICH0) + return "ich0"; + if (kind == FU_INTEL_SPI_KIND_ICH2345) + return "ich2345"; + if (kind == FU_INTEL_SPI_KIND_ICH6) + return "ich6"; + if (kind == FU_INTEL_SPI_KIND_PCH200) + return "pch200"; + if (kind == FU_INTEL_SPI_KIND_PCH300) + return "pch300"; + if (kind == FU_INTEL_SPI_KIND_PCH400) + return "pch400"; + if (kind == FU_INTEL_SPI_KIND_POULSBO) + return "poulsbo"; + return NULL; +} + +guint16 +fu_mmio_read16(gconstpointer addr, goffset offset) +{ + addr = (guint8 *)addr + offset; + return *(volatile const guint16 *)addr; +} + +guint32 +fu_mmio_read32(gconstpointer addr, goffset offset) +{ + addr = (guint8 *)addr + offset; + return *(volatile const guint32 *)addr; +} + +void +fu_mmio_write16(gpointer addr, goffset offset, guint16 val) +{ + addr = (guint8 *)addr + offset; + *(volatile guint16 *)addr = val; +} + +void +fu_mmio_write32(gpointer addr, goffset offset, guint32 val) +{ + addr = (guint8 *)addr + offset; + *(volatile guint32 *)addr = val; +} + +guint32 +fu_mmio_read32_le(gconstpointer addr, goffset offset) +{ + return GUINT32_FROM_LE(fu_mmio_read32(addr, offset)); +} + +void +fu_mmio_write32_le(gpointer addr, goffset offset, guint32 val) +{ + fu_mmio_write32(addr, offset, GUINT32_TO_LE(val)); +} diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-common.h b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-common.h new file mode 100644 index 0000000000000000000000000000000000000000..d32d1885b3c08b1f51d6a2776fa493c791fa201c --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-common.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define ICH9_REG_BFPR 0x00 +#define ICH9_REG_HSFS 0x04 +#define ICH9_REG_HSFC 0x06 +#define ICH9_REG_FADDR 0x08 +#define ICH9_REG_RESRVD 0x0C +#define ICH9_REG_FDATA0 0x10 +#define ICH9_REG_FDATAN 0x14 + +#define ICH9_REG_FRAP 0x50 +#define ICH9_REG_FREG0 0x54 +#define ICH9_REG_PR0 0x74 +#define ICH9_REG_FDOC 0xB0 +#define ICH9_REG_FDOD 0xB4 + +#define PCH100_REG_FDOC 0xB4 +#define PCH100_REG_FDOD 0xB8 +#define PCH100_REG_FPR0 0x84 +#define PCH100_REG_GPR0 0x98 + +#define PCH100_FADDR_FLA 0x07ffffff + +#define PCH100_HSFC_FCYCLE (0xf << 1) + +#define FDOC_FDSI (0x3F << 2) +#define FDOC_FDSS (0x03 << 12) + +#define HSFS_FDONE (0x01 << 0) +#define HSFS_FCERR (0x01 << 1) +#define HSFS_AEL (0x01 << 2) +#define HSFS_BERASE (0x03 << 3) +#define HSFS_SCIP (0x01 << 5) +#define HSFS_FDOPSS (0x01 << 13) +#define HSFS_FDV (0x01 << 14) +#define HSFS_FLOCKDN (0x01 << 15) + +#define HSFC_FGO (0x01 << 0) +#define HSFC_FCYCLE (0x03 << 1) +#define HSFC_FDBC (0x3f << 8) +#define HSFC_SME (0x01 << 15) + +typedef enum { + FU_INTEL_SPI_KIND_UNKNOWN, + FU_INTEL_SPI_KIND_APL, + FU_INTEL_SPI_KIND_C620, + FU_INTEL_SPI_KIND_ICH0, + FU_INTEL_SPI_KIND_ICH2345, + FU_INTEL_SPI_KIND_ICH6, + FU_INTEL_SPI_KIND_ICH9, + FU_INTEL_SPI_KIND_PCH100, + FU_INTEL_SPI_KIND_PCH200, + FU_INTEL_SPI_KIND_PCH300, + FU_INTEL_SPI_KIND_PCH400, + FU_INTEL_SPI_KIND_POULSBO, + FU_INTEL_SPI_KIND_LAST +} FuIntelSpiKind; + +FuIntelSpiKind +fu_intel_spi_kind_from_string(const gchar *kind); +const gchar * +fu_intel_spi_kind_to_string(FuIntelSpiKind kind); + +guint16 +fu_mmio_read16(gconstpointer addr, goffset offset); +void +fu_mmio_write16(gpointer addr, goffset offset, guint16 val); + +guint32 +fu_mmio_read32(gconstpointer addr, goffset offset); +void +fu_mmio_write32(gpointer addr, goffset offset, guint32 val); + +guint32 +fu_mmio_read32_le(gconstpointer addr, goffset offset); +void +fu_mmio_write32_le(gpointer addr, goffset offset, guint32 val); diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-device.c b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..8d3fa37d388395f1e4b2aa8be9d9e25d4f406149 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-device.c @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: GPL-2+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif + +#include + +#include "fu-ifd-device.h" +#include "fu-intel-spi-common.h" +#include "fu-intel-spi-device.h" +#include "fu-pci-device.h" + +struct _FuIntelSpiDevice { + FuDevice parent_instance; + FuIntelSpiKind kind; + gchar *spibar_proxy; + guint32 phys_spibar; + gpointer spibar; + guint16 hsfs; + guint16 frap; + guint32 freg[4]; + guint32 flvalsig; + guint32 descriptor_map0; + guint32 descriptor_map1; + guint32 descriptor_map2; + guint32 components_rcd; + guint32 illegal_jedec; + guint32 flpb; + guint32 flash_master[4]; + guint32 protected_range[4]; +}; + +#define FU_INTEL_SPI_PHYS_SPIBAR_SIZE 0x10000 /* bytes */ +#define FU_INTEL_SPI_READ_TIMEOUT 10 /* ms */ + +#define PCI_BASE_ADDRESS_0 0x0010 + +#define HSFS_FDOPSS_BIT 13 /* Flash Descriptor Override Pin-Strap Status */ +#define HSFS_FDV_BIT 14 /* Flash Descriptor Valid */ + +/** + * FU_INTEL_SPI_DEVICE_FLAG_ICH: + * + * Device is an I/O Controller Hub. + */ +#define FU_INTEL_SPI_DEVICE_FLAG_ICH (1 << 0) +/** + * FU_INTEL_SPI_DEVICE_FLAG_PCH: + * + * Device is a Platform Controller Hub. + */ +#define FU_INTEL_SPI_DEVICE_FLAG_PCH (1 << 1) + +G_DEFINE_TYPE(FuIntelSpiDevice, fu_intel_spi_device, FU_TYPE_DEVICE) + +static void +fu_intel_spi_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + fu_string_append(str, idt, "Kind", fu_intel_spi_kind_to_string(self->kind)); + fu_string_append_kx(str, idt, "SPIBAR", self->phys_spibar); + fu_string_append_kx(str, idt, "HSFS", self->hsfs); + fu_string_append_kx(str, idt, "FRAP", self->frap); + for (guint i = 0; i < 4; i++) { + g_autofree gchar *title = g_strdup_printf("FREG%u", i); + fu_string_append_kx(str, idt, title, self->freg[i]); + } + for (guint i = 0; i < 4; i++) { + g_autofree gchar *title = g_strdup_printf("FLMSTR%u", i); + fu_string_append_kx(str, idt, title, self->flash_master[i]); + } + fu_string_append_kx(str, idt, "FLVALSIG", self->flvalsig); + fu_string_append_kx(str, idt, "FLMAP0", self->descriptor_map0); + fu_string_append_kx(str, idt, "FLMAP1", self->descriptor_map1); + fu_string_append_kx(str, idt, "FLMAP2", self->descriptor_map2); + fu_string_append_kx(str, idt, "FLCOMP", self->components_rcd); + fu_string_append_kx(str, idt, "FLILL", self->illegal_jedec); + fu_string_append_kx(str, idt, "FLPB", self->flpb); + + /* PRx */ + for (guint i = 0; i < 4; i++) { + guint32 limit = 0; + guint32 base = 0; + FuIfdAccess access = FU_IFD_ACCESS_NONE; + g_autofree gchar *title = NULL; + g_autofree gchar *tmp = NULL; + + if (self->protected_range[i] == 0x0) + continue; + if ((self->protected_range[i] >> 31) & 0b1) + access |= FU_IFD_ACCESS_WRITE; + if ((self->protected_range[i] >> 15) & 0b1) + access |= FU_IFD_ACCESS_READ; + if (access != FU_IFD_ACCESS_NONE) { + base = ((self->protected_range[i] >> 0) & 0x1FFF) << 12; + limit = (((self->protected_range[i] >> 16) & 0x1FFF) << 12) | 0xFFFF; + } + title = g_strdup_printf("PR%u", i); + tmp = g_strdup_printf("blocked %s from 0x%x to 0x%x [0x%x]", + fu_ifd_access_to_string(access), + base, + limit, + self->protected_range[i]); + fu_string_append(str, idt, title, tmp); + } +} + +static gboolean +fu_intel_spi_device_open(FuDevice *device, GError **error) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + int fd; + g_autoptr(GInputStream) istr = NULL; + + /* this will fail if the kernel is locked down */ + fd = open("/dev/mem", O_SYNC | O_RDWR); + if (fd == -1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, +#ifdef HAVE_ERRNO_H + "failed to open /dev/mem: %s", + strerror(errno)); +#else + "failed to open /dev/mem"); +#endif + return FALSE; + } + istr = g_unix_input_stream_new(fd, TRUE); + if (istr == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to create input stream"); + return FALSE; + } + self->spibar = mmap(NULL, + FU_INTEL_SPI_PHYS_SPIBAR_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + self->phys_spibar); + if (self->spibar == MAP_FAILED) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, +#ifdef HAVE_ERRNO_H + "failed to mmap SPIBAR: %s", + strerror(errno)); +#else + "failed to mmap SPIBAR"); +#endif + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_intel_spi_device_close(FuDevice *device, GError **error) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + + /* close */ + if (self->spibar != NULL) { + if (munmap(self->spibar, FU_INTEL_SPI_PHYS_SPIBAR_SIZE) == -1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, +#ifdef HAVE_ERRNO_H + "failed to unmap SPIBAR: %s", + strerror(errno)); +#else + "failed to unmap SPIBAR"); +#endif + return FALSE; + } + self->spibar = NULL; + } + + /* success */ + return TRUE; +} + +static guint32 +fu_intel_spi_device_read_reg(FuIntelSpiDevice *self, guint8 section, guint16 offset) +{ + guint32 control = 0; + control |= (((guint32)section) << 12) & FDOC_FDSS; + control |= (((guint32)offset) << 2) & FDOC_FDSI; + fu_mmio_write32_le(self->spibar, PCH100_REG_FDOC, control); + return fu_mmio_read32_le(self->spibar, PCH100_REG_FDOD); +} + +static void +fu_intel_spi_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + FuIfdAccess access_global = FU_IFD_ACCESS_NONE; + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR); + fu_security_attrs_append(attrs, attr); + + /* check for read access from other regions */ + for (guint j = FU_IFD_REGION_BIOS; j < 4; j++) { + FuIfdAccess access; + access = + fu_ifd_region_to_access(FU_IFD_REGION_DESC, self->flash_master[j - 1], TRUE); + fwupd_security_attr_add_metadata(attr, + fu_ifd_region_to_string(j), + fu_ifd_access_to_string(access)); + access_global |= access; + } + + /* any region can write to the flash descriptor */ + if (access_global & FU_IFD_ACCESS_WRITE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* FLOCKDN is unset */ + if ((self->hsfs >> 15 & 0b1) == 0) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static gboolean +fu_intel_spi_device_probe(FuDevice *device, GError **error) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + + /* verify this was set in the quirk file */ + if (self->kind == FU_INTEL_SPI_KIND_UNKNOWN) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "IntelSpiKind not set"); + return FALSE; + } + + /* use a hidden PCI device to get the RCBA */ + if (self->spibar_proxy != NULL) { + g_autoptr(FuDevice) pcidev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get SPIBAR from a hidden (VID set to 0xFFFF) PCI device */ + pcidev = fu_pci_device_new(self->spibar_proxy, error); + if (pcidev == NULL) + return FALSE; + locker = fu_device_locker_new(pcidev, error); + if (locker == NULL) + return FALSE; + self->phys_spibar = + fu_pci_device_read_config(FU_PCI_DEVICE(pcidev), PCI_BASE_ADDRESS_0); + if (self->phys_spibar == 0 || self->phys_spibar == G_MAXUINT32) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "SPIBAR not valid: 0x%x", + self->phys_spibar); + return FALSE; + } + } + + /* specified explicitly as a physical address */ + if (self->phys_spibar == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "IntelSpiBar not set"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_intel_spi_device_setup(FuDevice *device, GError **error) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + guint64 total_size = 0; + guint8 comp1_density; + guint8 comp2_density; + gboolean me_is_locked; + guint16 reg_pr0 = fu_device_has_private_flag(device, FU_INTEL_SPI_DEVICE_FLAG_ICH) + ? ICH9_REG_PR0 + : PCH100_REG_FPR0; + + /* dump everything */ + if (g_getenv("FWUPD_INTEL_SPI_VERBOSE") != NULL) { + for (guint i = 0; i < 0xff; i += 4) { + guint32 tmp = fu_mmio_read32(self->spibar, i); + g_print("SPIBAR[0x%02x] = 0x%x\n", i, tmp); + } + } + + /* read from descriptor */ + self->hsfs = fu_mmio_read16(self->spibar, ICH9_REG_HSFS); + self->frap = fu_mmio_read16(self->spibar, ICH9_REG_FRAP); + for (guint i = FU_IFD_REGION_DESC; i < 4; i++) + self->freg[i] = fu_mmio_read32(self->spibar, ICH9_REG_FREG0 + i * 4); + self->flvalsig = fu_intel_spi_device_read_reg(self, 0, 0); + self->descriptor_map0 = fu_intel_spi_device_read_reg(self, 0, 1); + self->descriptor_map1 = fu_intel_spi_device_read_reg(self, 0, 2); + self->descriptor_map2 = fu_intel_spi_device_read_reg(self, 0, 3); + self->components_rcd = fu_intel_spi_device_read_reg(self, 1, 0); + self->illegal_jedec = fu_intel_spi_device_read_reg(self, 1, 1); + self->flpb = fu_intel_spi_device_read_reg(self, 1, 2); + + for (guint i = 0; i < 4; i++) + self->flash_master[i] = fu_intel_spi_device_read_reg(self, 3, i); + for (guint i = 0; i < 4; i++) { + self->protected_range[i] = + fu_mmio_read32(self->spibar, reg_pr0 + i * sizeof(guint32)); + } + + /* set size */ + comp1_density = (self->components_rcd & 0x0f) >> 0; + if (comp1_density != 0xf) + total_size += 1ull << (19 + comp1_density); + comp2_density = (self->components_rcd & 0xf0) >> 4; + if (comp2_density != 0xf) + total_size += 1ull << (19 + comp2_density); + fu_device_set_firmware_size(device, total_size); + + me_is_locked = !(self->hsfs & (1 << HSFS_FDV_BIT)) || /* assume locked if not valid */ + (self->hsfs & (1 << HSFS_FDOPSS_BIT)); /* use status bit if valid */ + /* add children */ + for (guint i = FU_IFD_REGION_BIOS; i < 4; i++) { + g_autoptr(FuDevice) child = NULL; + if (self->freg[i] == 0x0) + continue; + child = fu_ifd_device_new(fu_device_get_context(device), i, self->freg[i]); + for (guint j = 1; j < 4; j++) { + FuIfdAccess access; + access = fu_ifd_region_to_access(i, self->flash_master[j - 1], TRUE); + fu_ifd_device_set_access(FU_IFD_DEVICE(child), j, access); + } + + if (i == FU_IFD_REGION_ME && me_is_locked) + fu_device_add_flag(child, FWUPD_DEVICE_FLAG_LOCKED); + + fu_device_add_child(device, child); + } + + return TRUE; +} + +static gboolean +fu_intel_spi_device_wait(FuIntelSpiDevice *self, guint timeout_ms, GError **error) +{ + g_usleep(1); + for (guint i = 0; i < timeout_ms * 100; i++) { + guint16 hsfs = fu_mmio_read16(self->spibar, ICH9_REG_HSFS); + if (hsfs & HSFS_FDONE) + return TRUE; + if (hsfs & HSFS_FCERR) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "HSFS transaction error"); + return FALSE; + } + g_usleep(10); + } + g_set_error(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "HSFS timed out"); + return FALSE; +} + +static void +fu_intel_spi_device_set_addr(FuIntelSpiDevice *self, guint32 addr) +{ + guint32 addr_old = fu_mmio_read32(self->spibar, ICH9_REG_FADDR) & ~PCH100_FADDR_FLA; + fu_mmio_write32(self->spibar, ICH9_REG_FADDR, (addr & PCH100_FADDR_FLA) | addr_old); +} + +GBytes * +fu_intel_spi_device_dump(FuIntelSpiDevice *self, + FuDevice *device, + guint32 offset, + guint32 length, + FuProgress *progress, + GError **error) +{ + guint8 block_len = 0x40; + g_autoptr(GByteArray) buf = g_byte_array_sized_new(length); + + /* set FDONE, FCERR, AEL */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + fu_mmio_write16(self->spibar, ICH9_REG_HSFS, fu_mmio_read16(self->spibar, ICH9_REG_HSFS)); + for (guint32 addr = offset; addr < offset + length; addr += block_len) { + guint16 hsfc; + guint32 buftmp32 = 0; + + /* set up read */ + fu_intel_spi_device_set_addr(self, addr); + hsfc = fu_mmio_read16(self->spibar, ICH9_REG_HSFC); + hsfc &= ~PCH100_HSFC_FCYCLE; + hsfc &= ~HSFC_FDBC; + + /* set byte count */ + hsfc |= ((block_len - 1) << 8) & HSFC_FDBC; + hsfc |= HSFC_FGO; + fu_mmio_write16(self->spibar, ICH9_REG_HSFC, hsfc); + if (!fu_intel_spi_device_wait(self, FU_INTEL_SPI_READ_TIMEOUT, error)) { + g_prefix_error(error, "failed @0x%x: ", addr); + return NULL; + } + + /* copy out data */ + for (guint i = 0; i < block_len; i++) { + if (i % 4 == 0) + buftmp32 = fu_mmio_read32(self->spibar, ICH9_REG_FDATA0 + i); + fu_byte_array_append_uint8(buf, buftmp32 >> ((i % 4) * 8)); + } + + /* progress */ + fu_progress_set_percentage_full(progress, addr - offset + block_len, length); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static GBytes * +fu_intel_spi_device_dump_firmware2(FuDevice *device, FuProgress *progress, GError **error) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + guint64 total_size = fu_device_get_firmware_size_max(device); + return fu_intel_spi_device_dump(self, device, 0x0, total_size, progress, error); +} + +static GBytes * +fu_intel_spi_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + return fu_intel_spi_device_dump_firmware2(device, progress, error); +} + +static FuFirmware * +fu_intel_spi_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_ifd_firmware_new(); + g_autoptr(GBytes) blob = NULL; + + blob = fu_intel_spi_device_dump_firmware2(device, progress, error); + if (blob == NULL) + return NULL; + if (!fu_firmware_parse(firmware, blob, FWUPD_INSTALL_FLAG_NONE, error)) + return NULL; + return g_steal_pointer(&firmware); +} + +static gboolean +fu_intel_spi_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(device); + if (g_strcmp0(key, "IntelSpiBar") == 0) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->phys_spibar = tmp; + return TRUE; + } + if (g_strcmp0(key, "IntelSpiKind") == 0) { + /* validate */ + self->kind = fu_intel_spi_kind_from_string(value); + if (self->kind == FU_INTEL_SPI_KIND_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "%s not supported", + value); + return FALSE; + } + + /* get things like SPIBAR */ + fu_device_add_instance_strup(device, "ID", value); + return fu_device_build_instance_id(device, error, "INTEL_SPI_CHIPSET", "ID", NULL); + } + if (g_strcmp0(key, "IntelSpiBarProxy") == 0) { + self->spibar_proxy = g_strdup(value); + return TRUE; + } + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported"); + return FALSE; +} + +static void +fu_intel_spi_device_finalize(GObject *object) +{ + FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE(object); + g_free(self->spibar_proxy); + G_OBJECT_CLASS(fu_intel_spi_device_parent_class)->finalize(object); +} + +static void +fu_intel_spi_device_init(FuIntelSpiDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_set_physical_id(FU_DEVICE(self), "intel_spi"); + fu_device_register_private_flag(FU_DEVICE(self), FU_INTEL_SPI_DEVICE_FLAG_ICH, "ich"); + fu_device_register_private_flag(FU_DEVICE(self), FU_INTEL_SPI_DEVICE_FLAG_PCH, "pch"); +} + +static void +fu_intel_spi_device_class_init(FuIntelSpiDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_intel_spi_device_finalize; + klass_device->to_string = fu_intel_spi_device_to_string; + klass_device->probe = fu_intel_spi_device_probe; + klass_device->setup = fu_intel_spi_device_setup; + klass_device->dump_firmware = fu_intel_spi_device_dump_firmware; + klass_device->read_firmware = fu_intel_spi_device_read_firmware; + klass_device->open = fu_intel_spi_device_open; + klass_device->close = fu_intel_spi_device_close; + klass_device->set_quirk_kv = fu_intel_spi_device_set_quirk_kv; + klass_device->add_security_attrs = fu_intel_spi_device_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-device.h b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..d7fb7f84553391410d3247c07e4fa9f6dadb7acd --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-device.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_INTEL_SPI_DEVICE (fu_intel_spi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIntelSpiDevice, fu_intel_spi_device, FU, INTEL_SPI_DEVICE, FuDevice) + +GBytes * +fu_intel_spi_device_dump(FuIntelSpiDevice *self, + FuDevice *device, + guint32 offset, + guint32 length, + FuProgress *progress, + GError **error); diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-plugin.c b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..39f6ed2e3263946e7a2a3c1c1ccf49782bebb1b4 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-plugin.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-intel-spi-device.h" +#include "fu-intel-spi-plugin.h" + +struct _FuIntelSpiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuIntelSpiPlugin, fu_intel_spi_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_intel_spi_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + if (fu_kernel_locked_down()) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported when kernel locked down"); + return FALSE; + } + return TRUE; +} + +static void +fu_intel_spi_plugin_init(FuIntelSpiPlugin *self) +{ +} + +static void +fu_intel_spi_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "IntelSpiKind"); + fu_context_add_quirk_key(ctx, "IntelSpiBar"); + fu_context_add_quirk_key(ctx, "IntelSpiBarProxy"); + fu_context_add_quirk_key(ctx, "IntelSpiBiosCntl"); + fu_plugin_add_udev_subsystem(plugin, "pci"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_SPI_DEVICE); +} + +static void +fu_intel_spi_plugin_class_init(FuIntelSpiPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_intel_spi_plugin_constructed; + plugin_class->startup = fu_intel_spi_plugin_startup; +} diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-plugin.h b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..33f72df910a4b41737f18c381ef77abb5cf036f0 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-intel-spi-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIntelSpiPlugin, fu_intel_spi_plugin, FU, INTEL_SPI_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-pci-device.c b/fwupd-1.8.6/plugins/intel-spi/fu-pci-device.c new file mode 100644 index 0000000000000000000000000000000000000000..05b01eb3288391760ca252d6e819fd41934ef56c --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-pci-device.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-pci-device.h" + +typedef struct { + guint32 bus; + guint32 dev; + guint32 fun; +} FuPciDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuPciDevice, fu_pci_device, FU_TYPE_DEVICE) + +#define PCI_CONFIG_ADDRESS 0x0CF8 +#define PCI_CONFIG_DATA 0x0CFC + +#define GET_PRIVATE(o) (fu_pci_device_get_instance_private(o)) + +guint32 +fu_pci_device_read_config(FuPciDevice *self, guint32 addr) +{ + FuPciDevicePrivate *priv = GET_PRIVATE(self); + guint32 val = 0x80000000; + + /* we have to do this horrible port access as the PCI device is not + * visible to even the kernel as the vendor ID is set as 0xFFFF */ + val |= priv->bus << 16; + val |= priv->dev << 11; + val |= priv->fun << 8; + val |= addr; + + /* we do this multiple times until we get the same result for every + * request as the port is shared between the kernel and all processes */ + for (guint cnt = 0; cnt < 0xff; cnt++) { + guint32 results[0x20] = {0x0}; + gboolean consistent = TRUE; + + /* fill up array */ + for (guint i = 0; i < G_N_ELEMENTS(results); i++) { + outl(val, PCI_CONFIG_ADDRESS); + results[i] = inl(PCI_CONFIG_DATA); + } + + /* check they are all the same */ + for (guint i = 0; i < G_N_ELEMENTS(results); i++) { + if (results[0] != results[i]) { + consistent = FALSE; + break; + } + } + + /* success */ + if (consistent) + return results[0]; + } + + /* failed */ + return G_MAXUINT32; +} + +static gboolean +fu_pci_device_open(FuDevice *device, GError **error) +{ + /* this will fail if userspace is locked down */ + if (ioperm(PCI_CONFIG_ADDRESS, 64, 1) < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open port: %s", + strerror(errno)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pci_device_close(FuDevice *device, GError **error) +{ + /* this might fail if userspace is locked down */ + if (ioperm(PCI_CONFIG_ADDRESS, 64, 0) < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open port: %s", + strerror(errno)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_pci_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuPciDevice *self = FU_PCI_DEVICE(device); + FuPciDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append_kx(str, idt, "Bus", priv->bus); + fu_string_append_kx(str, idt, "Dev", priv->dev); + fu_string_append_kx(str, idt, "Fun", priv->fun); +} + +static gboolean +fu_pci_device_parse_bdf(FuPciDevice *self, const gchar *bdf, GError **error) +{ + FuPciDevicePrivate *priv = GET_PRIVATE(self); + guint64 bus_tmp; + guint64 dev_tmp; + guint64 fun_tmp; + g_auto(GStrv) split = g_strsplit_set(bdf, ":.", 0); + + /* parse the BDF */ + if (g_strv_length(split) != 3) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "%s invalid, expected '00:1f.5'", + bdf); + return FALSE; + } + bus_tmp = g_ascii_strtoull(split[0], NULL, 16); + dev_tmp = g_ascii_strtoull(split[1], NULL, 16); + fun_tmp = g_ascii_strtoull(split[2], NULL, 16); + if (bus_tmp > 0xff || dev_tmp > 0x1f || fun_tmp > 0x7) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "%s invalid, expected '00:1f.5'", + bdf); + return FALSE; + } + + /* success */ + priv->bus = bus_tmp; + priv->dev = dev_tmp; + priv->fun = fun_tmp; + return TRUE; +} + +static void +fu_pci_device_init(FuPciDevice *self) +{ + fu_device_set_physical_id(FU_DEVICE(self), "PCI"); +} + +static void +fu_pci_device_class_init(FuPciDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_pci_device_to_string; + klass_device->open = fu_pci_device_open; + klass_device->close = fu_pci_device_close; +} + +FuDevice * +fu_pci_device_new(const gchar *bdf, GError **error) +{ + g_autoptr(FuPciDevice) self = FU_PCI_DEVICE(g_object_new(FU_TYPE_PCI_DEVICE, NULL)); + if (!fu_pci_device_parse_bdf(self, bdf, error)) + return NULL; + return FU_DEVICE(g_steal_pointer(&self)); +} diff --git a/fwupd-1.8.6/plugins/intel-spi/fu-pci-device.h b/fwupd-1.8.6/plugins/intel-spi/fu-pci-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f9baf28144ddecb03cce283cccbcc520bbb371bb --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/fu-pci-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PCI_DEVICE (fu_pci_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuPciDevice, fu_pci_device, FU, PCI_DEVICE, FuDevice) + +struct _FuPciDeviceClass { + FuDeviceClass parent_class; +}; + +FuDevice * +fu_pci_device_new(const gchar *bdf, GError **error); +guint32 +fu_pci_device_read_config(FuPciDevice *self, guint32 addr); diff --git a/fwupd-1.8.6/plugins/intel-spi/generate-quirk.py b/fwupd-1.8.6/plugins/intel-spi/generate-quirk.py new file mode 100755 index 0000000000000000000000000000000000000000..dd089c346c07e37c9b47a495f27e8aec67348464 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/generate-quirk.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2021 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import sys + + +class Chipset: + def __init__(self, spibar=None, bios_cntl=0x0, spibar_proxy=None, flags=None): + + self.bios_cntl = bios_cntl + self.spibar_proxy = spibar_proxy + self.spibar = spibar + self.flags = flags + + +if __name__ == "__main__": + + if len(sys.argv) != 2: + print("required: /path/to/chipset_enable.c") + sys.exit(1) + + chipsets = { + "apl": Chipset(flags="ich", bios_cntl=0xDC, spibar_proxy="00:0d.2"), + "c620": Chipset(flags="pch", bios_cntl=0xDC, spibar_proxy="00:1f.5"), + "ich0": Chipset(flags="ich", bios_cntl=0x4E), + "ich2345": Chipset(flags="ich", bios_cntl=0x4E), + "ich6": Chipset(flags="ich", bios_cntl=0xDC), + "pch100": Chipset(flags="pch", bios_cntl=0xDC, spibar_proxy="00:1f.5"), + "pch200": Chipset(flags="pch", bios_cntl=0xDC, spibar_proxy="00:1f.5"), + "pch300": Chipset(flags="pch", bios_cntl=0xDC, spibar_proxy="00:1f.5"), + "pch400": Chipset(flags="pch", bios_cntl=0xDC, spibar_proxy="00:1f.5"), + "poulsbo": Chipset(flags="ich", bios_cntl=0xD8), + } + + devices = {"PCI\VEN_8086&DEV_A0A4": "pch100", "PCI\VEN_8086&DEV_9D24": "pch200"} + + with open("intel-spi.quirk", "w") as out_f: + with open(sys.argv[1], "r") as in_f: + lines = in_f.read().split("\n") + for line in lines: + if line.find("0x8086") == -1: + continue + if line.find("Sample") != -1: + continue + for char in ["}", "{", '"', " ", "\t"]: + line = line.replace(char, "") + ven, dev, _, _, _, _, kind, _ = line.split(",") + + if kind.startswith("enable_flash_"): + kind = kind[13:] + if kind not in chipsets: + print("ignoring {}...".format(kind)) + continue + + devices["PCI\VEN_{}&DEV_{}".format(ven[2:], dev[2:].upper())] = kind + + for device in devices: + kind = devices[device] + out_f.write("[{}]\n".format(device)) + out_f.write("Plugin = intel_spi\n") + out_f.write("IntelSpiKind = {}\n\n".format(kind)) + + for kind in sorted(chipsets): + cs = chipsets[kind] + out_f.write("\n[INTEL_SPI_CHIPSET\\ID_{}]\n".format(kind.upper())) + if cs.spibar: + out_f.write("IntelSpiBar = 0x{:x}\n".format(cs.spibar)) + if cs.spibar_proxy: + out_f.write("IntelSpiBarProxy = {}\n".format(cs.spibar_proxy)) + if cs.bios_cntl: + out_f.write("IntelSpiBiosCntl = 0x{:X}\n".format(cs.bios_cntl)) + if cs.flags: + out_f.write("Flags = {}\n".format(cs.flags)) diff --git a/fwupd-1.8.6/plugins/intel-spi/intel-spi.quirk b/fwupd-1.8.6/plugins/intel-spi/intel-spi.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b4071cefbc3ebd5370daedc4dd0a9e22f6e5ffa5 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/intel-spi.quirk @@ -0,0 +1,450 @@ +[PCI\VEN_8086&DEV_A0A4] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D24] +Plugin = intel_spi +IntelSpiKind = pch200 + +[PCI\VEN_8086&DEV_2410] +Plugin = intel_spi +IntelSpiKind = ich0 + +[PCI\VEN_8086&DEV_2420] +Plugin = intel_spi +IntelSpiKind = ich0 + +[PCI\VEN_8086&DEV_2440] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_244C] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_2450] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_2480] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_248C] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_24C0] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_24CC] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_24D0] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_25A1] +Plugin = intel_spi +IntelSpiKind = ich2345 + +[PCI\VEN_8086&DEV_2640] +Plugin = intel_spi +IntelSpiKind = ich6 + +[PCI\VEN_8086&DEV_2641] +Plugin = intel_spi +IntelSpiKind = ich6 + +[PCI\VEN_8086&DEV_2642] +Plugin = intel_spi +IntelSpiKind = ich6 + +[PCI\VEN_8086&DEV_2670] +Plugin = intel_spi +IntelSpiKind = ich6 + +[PCI\VEN_8086&DEV_8119] +Plugin = intel_spi +IntelSpiKind = poulsbo + +[PCI\VEN_8086&DEV_9D43] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D46] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D48] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D4B] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D4E] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D50] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D53] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D56] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D58] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_9D84] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_0284] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_0285] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_A143] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A144] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A145] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A146] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A147] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A148] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A149] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A14A] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A14D] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A14E] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A150] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A151] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A152] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A153] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A154] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A155] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A1A4] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C0] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C1] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C2] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C3] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C4] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C5] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C6] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C7] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C8] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1C9] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1CA] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1CB] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1CC] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A1CD] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A240] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A241] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A242] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A243] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A244] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A245] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A246] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A247] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A248] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A249] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_1BCA] +Plugin = intel_spi +IntelSpiKind = c620 + +[PCI\VEN_8086&DEV_A2C4] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2C5] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2C6] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2C7] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2C8] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2C9] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2CA] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2CC] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_A2D2] +Plugin = intel_spi +IntelSpiKind = pch100 + +[PCI\VEN_8086&DEV_5AE8] +Plugin = intel_spi +IntelSpiKind = apl + +[PCI\VEN_8086&DEV_5AF0] +Plugin = intel_spi +IntelSpiKind = apl + +[PCI\VEN_8086&DEV_A303] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A304] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A305] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A306] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A308] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A309] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A30A] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A30C] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A30D] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_A30E] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_3482] +Plugin = intel_spi +IntelSpiKind = pch300 + +[PCI\VEN_8086&DEV_0684] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_0685] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_0687] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_068C] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_068D] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_068E] +Plugin = intel_spi +IntelSpiKind = pch400 + +[PCI\VEN_8086&DEV_0697] +Plugin = intel_spi +IntelSpiKind = pch400 + + +[INTEL_SPI_CHIPSET\ID_APL] +IntelSpiBarProxy = 00:0d.2 +IntelSpiBiosCntl = 0xDC +Flags = ich + +[INTEL_SPI_CHIPSET\ID_C620] +IntelSpiBarProxy = 00:1f.5 +IntelSpiBiosCntl = 0xDC +Flags = pch + +[INTEL_SPI_CHIPSET\ID_ICH0] +IntelSpiBiosCntl = 0x4E +Flags = ich + +[INTEL_SPI_CHIPSET\ID_ICH2345] +IntelSpiBiosCntl = 0x4E +Flags = ich + +[INTEL_SPI_CHIPSET\ID_ICH6] +IntelSpiBiosCntl = 0xDC +Flags = ich + +[INTEL_SPI_CHIPSET\ID_PCH100] +IntelSpiBarProxy = 00:1f.5 +IntelSpiBiosCntl = 0xDC +Flags = pch + +[INTEL_SPI_CHIPSET\ID_PCH200] +IntelSpiBarProxy = 00:1f.5 +IntelSpiBiosCntl = 0xDC +Flags = pch + +[INTEL_SPI_CHIPSET\ID_PCH300] +IntelSpiBarProxy = 00:1f.5 +IntelSpiBiosCntl = 0xDC +Flags = pch + +[INTEL_SPI_CHIPSET\ID_PCH400] +IntelSpiBarProxy = 00:1f.5 +IntelSpiBiosCntl = 0xDC +Flags = pch + +[INTEL_SPI_CHIPSET\ID_POULSBO] +IntelSpiBiosCntl = 0xD8 +Flags = ich diff --git a/fwupd-1.8.6/plugins/intel-spi/meson.build b/fwupd-1.8.6/plugins/intel-spi/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..9900752ca8b1fb52eae12a2fad176538676dfe70 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-spi/meson.build @@ -0,0 +1,24 @@ +if get_option('plugin_intel_spi') + +if not lzma.found() or \ + host_machine.system() != 'linux' or \ + (host_cpu != 'x86' and host_cpu != 'x86_64') + error('unsupported configuration for intel_spi') +endif +cargs = ['-DG_LOG_DOMAIN="FuPluginIntelSpi"'] + +plugin_quirks += files('intel-spi.quirk') +plugin_builtins += static_library('fu_plugin_intel_spi', + sources: [ + 'fu-ifd-device.c', + 'fu-intel-spi-common.c', + 'fu-intel-spi-device.c', + 'fu-pci-device.c', + 'fu-intel-spi-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/intel-usb4/README.md b/fwupd-1.8.6/plugins/intel-usb4/README.md new file mode 100644 index 0000000000000000000000000000000000000000..27757fbf7bed127641e323a0ce98d93c539a2e17 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/README.md @@ -0,0 +1,37 @@ +# Intel USB4 + +## Introduction + +This plugin supports the Goshen Ridge hardware which is a USB-4 controller from Intel. +These devices can updated using multiple interfaces, but this plugin only uses the XHCI interface. + +## 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 generation scheme, which is quite intentionally identical to thunderbolt +plugin: + +* `TBT-{nvm_vendor_id}{nvm_product_id}` + +## Update Behavior + +By default the USB4 controller will reboot at the end of the update. + +Some devices (e.g. inside some Dell docks) will instead be updated the next time the USB-C plug +from the dock is unplugged from the host, or when activated manually. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x8087` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-device.c b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-device.c new file mode 100644 index 0000000000000000000000000000000000000000..a1564c64d6f89fc8c29ec54205873fe46276c486 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-device.c @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2021 Intel Corporation. + * Copyright (C) 2021 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-intel-usb4-device.h" + +#define GR_USB_INTERFACE_NUMBER 0x0 +#define GR_USB_BLOCK_SIZE 64 + +/* bmRequest type */ +#define USB_REQ_TYPE_GET_MMIO 0xc0 /* bm Request type */ +#define USB_REQ_TYPE_SET_MMIO 0x40 /* bm Request type */ + +/* bRequest */ +#define REQ_HUB_GET_MMIO 64 +#define REQ_HUB_SET_MMIO 65 + +/* wValue*/ +#define MBOX_ACCESS (1 << 10) + +/* wIndex, mailbox register offset */ +/* First 16 registers are Data[0]-Data[15] registers */ +#define MBOX_REG_METADATA 16 +#define MBOX_REG 17 /* no name? */ + +/* mask for the MBOX_REG register that has no name */ +#define MBOX_ERROR (1 << 6) /* of the u8 status field */ +#define MBOX_OPVALID (1 << 7) /* of the u8 status field */ + +#define MBOX_TIMEOUT 3000 + +/* HUB operation OP codes */ +#define OP_NVM_WRITE 0x20 +#define OP_NVM_AUTH_WRITE 0x21 +#define OP_NVM_READ 0x22 +#define OP_NVM_SET_OFFSET 0x23 +#define OP_DROM_READ 0x24 + +/* NVM metadata offset and length fields are in dword units */ +/* note that these won't work for DROM read */ +#define NVM_OFFSET_TO_METADATA(p) ((((p) / 4) & 0x3fffff) << 2) /* bits 23:2 */ +#define NVM_LENGTH_TO_METADATA(p) ((((p) / 4) & 0xf) << 24) /* bits 27:24 */ + +/* Default length for NVM READ */ +#define NVM_READ_LENGTH 0x224 + +struct mbox_regx { + guint16 opcode; + guint8 rsvd; + guint8 status; +} __attribute__((packed)); + +struct _FuIntelUsb4Device { + FuUsbDevice parent_instance; + guint blocksz; + guint8 intf_nr; + /* from DROM */ + guint16 nvm_vendor_id; + guint16 nvm_model_id; + /* from DIGITAL */ + guint16 nvm_device_id; +}; + +G_DEFINE_TYPE(FuIntelUsb4Device, fu_intel_usb4_device, FU_TYPE_USB_DEVICE) + +/* wIndex contains the hub register offset, value BIT[10] is "access to + * mailbox", rest of values are vendor specific or rsvd */ +static gboolean +fu_intel_usb4_device_get_mmio(FuDevice *device, guint16 mbox_reg, guint8 *buf, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + struct mbox_regx *regx; + + 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, + REQ_HUB_GET_MMIO, /* request */ + MBOX_ACCESS, /* value */ + mbox_reg, /* index */ + (guint8 *)buf, /* data */ + 4, /* length */ + NULL, /* actual length */ + MBOX_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, + "GET_MMIO failed to set control on mbox register index [0x%x]: ", + mbox_reg); + return FALSE; + } + /* verify status for specific hub mailbox register */ + if (mbox_reg == MBOX_REG) { + regx = (struct mbox_regx *)buf; + + /* error status bit */ + if (regx->status & MBOX_ERROR) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "GET_MMIO opcode [0x%x] nonzero error bit in status [0x%x]", + regx->opcode, + regx->status); + return FALSE; + } + + /* operation valid (OV) bit should be 0'b */ + if (regx->status & MBOX_OPVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "GET_MMIO opcode [0x%x] nonzero OV bit in status [0x%x]", + regx->opcode, + regx->status); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_intel_usb4_device_set_mmio(FuDevice *device, guint16 mbox_reg, guint8 *buf, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + + 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, + REQ_HUB_SET_MMIO, /* request */ + MBOX_ACCESS, /* value */ + mbox_reg, /* index */ + (guint8 *)buf, /* data */ + 4, /* length */ + NULL, /* actual length */ + MBOX_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to set mmio 0x%x: ", mbox_reg); + return FALSE; + } + return TRUE; +} + +/* + * Read up to 64 bytes of data from the mbox data registers to a buffer. + * The mailbox can hold 64 bytes of data in 16 doubleword data registers. + * To get data from NVM or DROM to mbox registers issue a NVM Read or DROM + * read operation before reading the mbox data registers. + */ +static gboolean +fu_intel_usb4_device_mbox_data_read(FuDevice *device, guint8 *data, guint8 length, GError **error) +{ + guint8 *ptr = data; + + if (length > 64 || length % 4) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid firmware data read length %u", + length); + return FALSE; + } + /* read 4 bytes per iteration */ + for (gint i = 0; i < length / 4; i++) { + if (!fu_intel_usb4_device_get_mmio(device, i, ptr, error)) { + g_prefix_error(error, "failed to read mbox data registers: "); + return FALSE; + } + ptr += 4; + } + return TRUE; +} + +/* + * The mailbox can hold 64 bytes in 16 doubleword data registers. + * A NVM write operation writes data from these registers to NVM + * at the set offset + */ +static gboolean +fu_intel_usb4_device_mbox_data_write(FuDevice *device, + const guint8 *data, + guint8 length, + GError **error) +{ + guint8 *ptr = (guint8 *)data; + + if (length > 64 || length % 4) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid firmware data write length %u", + length); + return FALSE; + } + + /* writes 4 bytes per iteration */ + for (gint i = 0; i < length / 4; i++) { + if (!fu_intel_usb4_device_set_mmio(device, i, ptr, error)) + return FALSE; + ptr += 4; + } + return TRUE; +} + +static gboolean +fu_intel_usb4_device_operation(FuDevice *device, guint16 opcode, guint8 *metadata, GError **error) +{ + struct mbox_regx *regx; + gint max_tries = 100; + guint8 buf[4] = {0x0}; + + regx = (struct mbox_regx *)buf; + regx->opcode = GUINT16_TO_LE(opcode); + regx->status = MBOX_OPVALID; + + /* Write metadata register for operations that use it */ + switch (opcode) { + case OP_NVM_WRITE: + case OP_NVM_AUTH_WRITE: + break; + case OP_NVM_READ: + case OP_NVM_SET_OFFSET: + case OP_DROM_READ: + if (metadata == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "hub opcode 0x%x requires metadata", + opcode); + return FALSE; + } + if (!fu_intel_usb4_device_set_mmio(device, MBOX_REG_METADATA, metadata, error)) { + g_prefix_error(error, "failed to write metadata %s: ", metadata); + return FALSE; + } + break; + default: + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid hub opcode: 0x%x", + opcode); + return FALSE; + } + + /* write the operation and poll completion or error */ + if (!fu_intel_usb4_device_set_mmio(device, MBOX_REG, buf, error)) + return FALSE; + + /* leave early as successful USB4 AUTH resets the device immediately */ + if (opcode == OP_NVM_AUTH_WRITE) + return TRUE; + + for (gint i = 0; i <= max_tries; i++) { + g_autoptr(GError) error_local = NULL; + if (fu_intel_usb4_device_get_mmio(device, MBOX_REG, buf, &error_local)) + return TRUE; + if (i == max_tries) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "maximum tries exceeded: "); + } + g_usleep((gulong)10000); + } + return FALSE; +} + +static gboolean +fu_intel_usb4_device_nvm_read(FuDevice *device, + guint8 *buf, + guint32 length, + guint32 nvm_addr, + GError **error) +{ + guint8 tmpbuf[64] = {0x0}; + + while (length > 0) { + guint32 unaligned_bytes = nvm_addr % 4; + guint32 padded_len; + guint32 nbytes; + guint8 metadata[4]; + + if (length + unaligned_bytes < 64) { + nbytes = length; + padded_len = unaligned_bytes + length; + + /* align end to full dword boundary */ + if (padded_len % 4) + padded_len = (padded_len & ~0x3) + 4; + } else { + padded_len = 64; + nbytes = padded_len - unaligned_bytes; + } + + /* set nvm read offset in dwords */ + fu_memwrite_uint32(metadata, NVM_OFFSET_TO_METADATA(nvm_addr), G_LITTLE_ENDIAN); + + /* and length field in dwords, note 0 means 16 dwords */ + metadata[3] = (padded_len / 4) & 0xf; + + /* ask hub to read up to 64 bytes from NVM to mbox data regs */ + if (!fu_intel_usb4_device_operation(device, OP_NVM_READ, metadata, error)) { + g_prefix_error(error, "hub NVM read error: "); + return FALSE; + } + /* read the data from mbox data regs into our buffer */ + if (!fu_intel_usb4_device_mbox_data_read(device, tmpbuf, padded_len, error)) { + g_prefix_error(error, "hub firmware mbox data read error: "); + return FALSE; + } + if (!fu_memcpy_safe(buf, + length, + 0x0, + tmpbuf, + sizeof(tmpbuf), + unaligned_bytes, + nbytes, + error)) + return FALSE; + + buf += nbytes; + nvm_addr += nbytes; + length -= nbytes; + } + + return TRUE; +} + +static gboolean +fu_intel_usb4_device_nvm_write(FuDevice *device, + GBytes *blob, + guint32 nvm_addr, + FuProgress *progress, + GError **error) +{ + guint8 metadata[4]; + g_autoptr(GPtrArray) chunks = NULL; + + if (nvm_addr % 4 != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Invalid NVM write offset 0x%x, must be DW aligned: ", + nvm_addr); + return FALSE; + } + if (g_bytes_get_size(blob) < 64 || g_bytes_get_size(blob) % 64) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Invalid NVM length 0x%x, must be 64 byte aligned: ", + (guint)g_bytes_get_size(blob)); + return FALSE; + } + + /* set initial offset, must be DW aligned */ + fu_memwrite_uint32(metadata, NVM_OFFSET_TO_METADATA(nvm_addr), G_LITTLE_ENDIAN); + if (!fu_intel_usb4_device_operation(device, OP_NVM_SET_OFFSET, metadata, error)) { + g_prefix_error(error, "hub NVM set offset error: "); + return FALSE; + } + + /* write data in 64 byte blocks */ + chunks = fu_chunk_array_new_from_bytes(blob, 0x0, 0x0, 64); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + /* write data to mbox data regs */ + if (!fu_intel_usb4_device_mbox_data_write(device, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "hub mbox data write error: "); + return FALSE; + } + /* ask hub to write 64 bytes from data regs to NVM */ + if (!fu_intel_usb4_device_operation(device, OP_NVM_WRITE, NULL, error)) { + g_prefix_error(error, "hub NVM write operation error: "); + return FALSE; + } + + /* done */ + fu_progress_step_done(progress); + } + + /* success */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); + return TRUE; +} + +static gboolean +fu_intel_usb4_device_activate(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + if (!fu_intel_usb4_device_operation(device, OP_NVM_AUTH_WRITE, NULL, error)) { + g_prefix_error(error, "NVM authenticate failed: "); + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED); + return FALSE; + } + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_SUCCESS); + return TRUE; +} + +static FuFirmware * +fu_intel_usb4_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuIntelUsb4Device *self = FU_INTEL_USB4_DEVICE(device); + guint16 fw_vendor_id; + guint16 fw_model_id; + g_autoptr(FuFirmware) firmware = fu_intel_thunderbolt_firmware_new(); + + /* get vid:pid:rev */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check is compatible */ + fw_vendor_id = fu_intel_thunderbolt_nvm_get_vendor_id(FU_INTEL_THUNDERBOLT_NVM(firmware)); + fw_model_id = fu_intel_thunderbolt_nvm_get_model_id(FU_INTEL_THUNDERBOLT_NVM(firmware)); + if (self->nvm_vendor_id != fw_vendor_id || self->nvm_model_id != fw_model_id) { + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware 0x%04x:0x%04x does not match device 0x%04x:0x%04x", + fw_vendor_id, + fw_model_id, + self->nvm_vendor_id, + self->nvm_model_id); + return NULL; + } + g_warning("firmware 0x%04x:0x%04x does not match device 0x%04x:0x%04x", + fw_vendor_id, + fw_model_id, + self->nvm_vendor_id, + self->nvm_model_id); + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_intel_usb4_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GBytes) fw_image = NULL; + + g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + + /* get payload */ + fw_image = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_PAYLOAD, error); + if (fw_image == NULL) + return FALSE; + + /* firmware install */ + if (!fu_intel_usb4_device_nvm_write(device, fw_image, 0, progress, error)) + return FALSE; + + /* success, but needs activation */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART)) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + fu_device_set_version(device, fu_firmware_get_version(firmware)); + return TRUE; + } + + /* activate, wait for replug */ + if (!fu_intel_usb4_device_operation(device, OP_NVM_AUTH_WRITE, NULL, error)) { + g_prefix_error(error, "NVM authenticate failed: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_intel_usb4_device_setup(FuDevice *device, GError **error) +{ + FuIntelUsb4Device *self = FU_INTEL_USB4_DEVICE(device); + guint8 buf[NVM_READ_LENGTH] = {0x0}; + g_autofree gchar *name = NULL; + g_autoptr(FuFirmware) fw = fu_intel_thunderbolt_nvm_new(); + g_autoptr(GBytes) blob = NULL; + + /* read from device and parse firmware */ + if (!fu_intel_usb4_device_nvm_read(device, buf, sizeof(buf), 0, error)) { + g_prefix_error(error, "NVM read error: "); + return FALSE; + } + blob = g_bytes_new(buf, sizeof(buf)); + if (!fu_firmware_parse(fw, blob, FWUPD_INSTALL_FLAG_NONE, error)) { + g_prefix_error(error, "NVM parse error: "); + return FALSE; + } + self->nvm_vendor_id = fu_intel_thunderbolt_nvm_get_vendor_id(FU_INTEL_THUNDERBOLT_NVM(fw)); + self->nvm_model_id = fu_intel_thunderbolt_nvm_get_model_id(FU_INTEL_THUNDERBOLT_NVM(fw)); + self->nvm_device_id = fu_intel_thunderbolt_nvm_get_device_id(FU_INTEL_THUNDERBOLT_NVM(fw)); + + name = g_strdup_printf("TBT-%04x%04x", self->nvm_vendor_id, self->nvm_model_id); + fu_device_add_instance_id(device, name); + fu_device_set_version(device, fu_firmware_get_version(fw)); + return TRUE; +} + +static void +fu_intel_usb4_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuIntelUsb4Device *self = FU_INTEL_USB4_DEVICE(device); + fu_string_append_kx(str, idt, "NvmVendorId", self->nvm_vendor_id); + fu_string_append_kx(str, idt, "NvmModelId", self->nvm_model_id); + fu_string_append_kx(str, idt, "NvmDeviceId", self->nvm_device_id); +} + +static void +fu_intel_usb4_device_init(FuIntelUsb4Device *self) +{ + self->intf_nr = GR_USB_INTERFACE_NUMBER; + self->blocksz = GR_USB_BLOCK_SIZE; + fu_device_add_protocol(FU_DEVICE(self), "com.intel.thunderbolt"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION); +} + +static void +fu_intel_usb4_device_class_init(FuIntelUsb4DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_intel_usb4_device_to_string; + klass_device->setup = fu_intel_usb4_device_setup; + klass_device->prepare_firmware = fu_intel_usb4_device_prepare_firmware; + klass_device->write_firmware = fu_intel_usb4_device_write_firmware; + klass_device->activate = fu_intel_usb4_device_activate; +} diff --git a/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-device.h b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ec0f1bd106a6bc2421ac6093cd1617bd73ad3d25 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-device.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2021 Intel Corporation. + * Copyright (C) 2021 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 + +#define FU_TYPE_INTEL_USB4_DEVICE (fu_intel_usb4_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIntelUsb4Device, fu_intel_usb4_device, FU, INTEL_USB4_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-plugin.c b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..6c7022904f46c2c20862603d8c0a0785fad85cdd --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-intel-usb4-device.h" +#include "fu-intel-usb4-plugin.h" + +struct _FuIntelUsb4Plugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuIntelUsb4Plugin, fu_intel_usb4_plugin, FU_TYPE_PLUGIN) + +static void +fu_intel_usb4_plugin_init(FuIntelUsb4Plugin *self) +{ +} + +static void +fu_intel_usb4_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_USB4_DEVICE); +} + +static void +fu_intel_usb4_plugin_class_init(FuIntelUsb4PluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_intel_usb4_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-plugin.h b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..c6a7c32022f92cd05100f7644837f0676620d80d --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/fu-intel-usb4-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIntelUsb4Plugin, fu_intel_usb4_plugin, FU, INTEL_USB4_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/intel-usb4/intel-usb4.quirk b/fwupd-1.8.6/plugins/intel-usb4/intel-usb4.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b855f1263202c8fc17ab2511154766665a8f1304 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/intel-usb4.quirk @@ -0,0 +1,2 @@ +[USB\VID_8087&PID_0B40] +Plugin = intel_usb4 diff --git a/fwupd-1.8.6/plugins/intel-usb4/meson.build b/fwupd-1.8.6/plugins/intel-usb4/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..2bd2503b7f7eed5da855b3f3d5cf24325066f8c1 --- /dev/null +++ b/fwupd-1.8.6/plugins/intel-usb4/meson.build @@ -0,0 +1,18 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginIntelUsb4"'] + +plugin_quirks += files('intel-usb4.quirk') +plugin_builtins += static_library('fu_plugin_intel_usb4', + sources: [ + 'fu-intel-usb4-device.c', + 'fu-intel-usb4-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + gudev, + ], +) +endif diff --git a/fwupd-1.8.6/plugins/iommu/README.md b/fwupd-1.8.6/plugins/iommu/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2ee57793c72635974102055dd78437064ae0fe81 --- /dev/null +++ b/fwupd-1.8.6/plugins/iommu/README.md @@ -0,0 +1,9 @@ +# Linux IOMMU + +## Introduction + +This plugin checks if an IOMMU is available on the system. + +## External Interface Access + +This plugin requires no extra access. diff --git a/fwupd-1.8.6/plugins/iommu/fu-iommu-plugin.c b/fwupd-1.8.6/plugins/iommu/fu-iommu-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..043a5e5b3aea7fb8478755c5b594bf72e252074e --- /dev/null +++ b/fwupd-1.8.6/plugins/iommu/fu-iommu-plugin.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-iommu-plugin.h" + +struct _FuIommuPlugin { + FuPlugin parent_instance; + gboolean has_iommu; +}; + +G_DEFINE_TYPE(FuIommuPlugin, fu_iommu_plugin, FU_TYPE_PLUGIN) + +static void +fu_iommu_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuIommuPlugin *self = FU_IOMMU_PLUGIN(plugin); + fu_string_append_kb(str, idt, "HasIommu", self->has_iommu); +} + +static gboolean +fu_iommu_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuIommuPlugin *self = FU_IOMMU_PLUGIN(plugin); + + /* interesting device? */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "iommu") != 0) + return TRUE; + self->has_iommu = TRUE; + + return TRUE; +} + +static void +fu_iommu_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuIommuPlugin *self = FU_IOMMU_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_IOMMU); + fu_security_attrs_append(attrs, attr); + + fu_security_attr_add_bios_target_value(attr, "AmdVt", "enable"); + fu_security_attr_add_bios_target_value(attr, "IOMMU", "enable"); + fu_security_attr_add_bios_target_value(attr, "VtForDirectIo", "enable"); + + if (!self->has_iommu) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_iommu_plugin_init(FuIommuPlugin *self) +{ +} + +static void +fu_iommu_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "iommu"); +} + +static void +fu_iommu_plugin_class_init(FuIommuPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_iommu_plugin_constructed; + plugin_class->to_string = fu_iommu_plugin_to_string; + plugin_class->backend_device_added = fu_iommu_plugin_backend_device_added; + plugin_class->add_security_attrs = fu_iommu_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/iommu/fu-iommu-plugin.h b/fwupd-1.8.6/plugins/iommu/fu-iommu-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..1ae061b4342e5e043723c7893d623abd573e9450 --- /dev/null +++ b/fwupd-1.8.6/plugins/iommu/fu-iommu-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIommuPlugin, fu_iommu_plugin, FU, IOMMU_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/iommu/iommu.quirk b/fwupd-1.8.6/plugins/iommu/iommu.quirk new file mode 100644 index 0000000000000000000000000000000000000000..bdb29556cd6f3996c2f5f11f6330b3affe806844 --- /dev/null +++ b/fwupd-1.8.6/plugins/iommu/iommu.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[IOMMU] +Plugin = iommu diff --git a/fwupd-1.8.6/plugins/iommu/meson.build b/fwupd-1.8.6/plugins/iommu/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..8f6d226ee956d5762a0f475e1d6b3b1eeff31695 --- /dev/null +++ b/fwupd-1.8.6/plugins/iommu/meson.build @@ -0,0 +1,17 @@ +if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginIommu"'] + +plugin_quirks += files('iommu.quirk') +plugin_builtins += static_library('fu_plugin_iommu', + sources: [ + 'fu-iommu-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: [ + fwupdplugin, + fwupd, + ], + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/jabra/README.md b/fwupd-1.8.6/plugins/jabra/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6e3d81d488c520c764025e1f460b91c139e250db --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/README.md @@ -0,0 +1,40 @@ +# Jabra + +## Introduction + +This plugin is used to detach the Jabra device to DFU mode. + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_0B0E&PID_0412` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### JabraMagic + +Two magic bytes sent to detach into DFU mode. + +Since: 1.3.3 + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different USB VID and PID in DFU APP mode. The device is then further detached +by the `dfu` plugin. + +On DFU attach the device again re-enumerates back to the Jabra runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x0A12` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/jabra/fu-jabra-device.c b/fwupd-1.8.6/plugins/jabra/fu-jabra-device.c new file mode 100644 index 0000000000000000000000000000000000000000..a76ff0604cc6c2b4991ad28bceddac7f64d591c2 --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/fu-jabra-device.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-jabra-device.h" + +struct _FuJabraDevice { + FuUsbDevice parent_instance; + gchar *magic; +}; + +G_DEFINE_TYPE(FuJabraDevice, fu_jabra_device, FU_TYPE_USB_DEVICE) + +static void +fu_jabra_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuJabraDevice *self = FU_JABRA_DEVICE(device); + fu_string_append(str, idt, "Magic", self->magic); +} + +static guint8 +_g_usb_device_get_interface_for_class(GUsbDevice *dev, guint8 intf_class, GError **error) +{ + g_autoptr(GPtrArray) intfs = NULL; + intfs = g_usb_device_get_interfaces(dev, error); + if (intfs == NULL) + return 0xff; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == intf_class) + return g_usb_interface_get_number(intf); + } + return 0xff; +} + +/* slightly weirdly, this magic turns the device into appIDLE, so we + * need the DFU plugin to further detach us into dfuIDLE */ +static gboolean +fu_jabra_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuJabraDevice *self = FU_JABRA_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + gsize magiclen = strlen(self->magic); + guint8 adr = 0x00; + guint8 rep = 0x00; + guint8 iface_hid; + guint8 buf[33] = {0x00}; + g_autoptr(GError) error_local = NULL; + + /* parse string and create magic packet */ + if (!fu_firmware_strparse_uint8_safe(self->magic, magiclen, 0, &rep, error)) + return FALSE; + if (!fu_firmware_strparse_uint8_safe(self->magic, magiclen, 2, &adr, error)) + return FALSE; + buf[0] = rep; + buf[1] = adr; + buf[2] = 0x00; + buf[3] = 0x01; + buf[4] = 0x85; + buf[5] = 0x07; + + /* detach the HID interface from the kernel driver */ + iface_hid = + _g_usb_device_get_interface_for_class(usb_device, G_USB_DEVICE_CLASS_HID, &error_local); + if (iface_hid == 0xff) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot find HID interface: %s", + error_local->message); + return FALSE; + } + g_debug("claiming interface 0x%02x", iface_hid); + if (!g_usb_device_claim_interface(usb_device, + (gint)iface_hid, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot claim interface 0x%02x: %s", + iface_hid, + error_local->message); + return FALSE; + } + + /* send magic to device */ + 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, + 0x09, + 0x0200 | rep, + 0x0003, + buf, + 33, + NULL, + FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, + NULL, /* cancellable */ + &error_local)) { + g_debug("whilst sending magic: %s, ignoring", error_local->message); + } + + /* wait for device to re-appear and be added to the dfu plugin */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_jabra_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + FuJabraDevice *self = FU_JABRA_DEVICE(device); + + if (g_strcmp0(key, "JabraMagic") == 0) { + if (value != NULL && strlen(value) == 4) { + self->magic = g_strdup(value); + return TRUE; + } + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unsupported jabra quirk format"); + return FALSE; + } + + /* failed */ + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "quirk key not supported"); + return FALSE; +} + +static void +fu_jabra_device_init(FuJabraDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_remove_delay(FU_DEVICE(self), 20000); /* 10+10s! */ + fu_device_add_protocol(FU_DEVICE(self), "org.usb.dfu"); +} + +static void +fu_jabra_device_finalize(GObject *object) +{ + FuJabraDevice *self = FU_JABRA_DEVICE(object); + g_free(self->magic); + G_OBJECT_CLASS(fu_jabra_device_parent_class)->finalize(object); +} + +static void +fu_jabra_device_class_init(FuJabraDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_jabra_device_finalize; + klass_device->to_string = fu_jabra_device_to_string; + klass_device->prepare = fu_jabra_device_prepare; + klass_device->set_quirk_kv = fu_jabra_device_set_quirk_kv; +} diff --git a/fwupd-1.8.6/plugins/jabra/fu-jabra-device.h b/fwupd-1.8.6/plugins/jabra/fu-jabra-device.h new file mode 100644 index 0000000000000000000000000000000000000000..cef1637956b917c9a92f5390f3a5a7897aa428b0 --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/fu-jabra-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_JABRA_DEVICE (fu_jabra_device_get_type()) +G_DECLARE_FINAL_TYPE(FuJabraDevice, fu_jabra_device, FU, JABRA_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/jabra/fu-jabra-plugin.c b/fwupd-1.8.6/plugins/jabra/fu-jabra-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..4eeb0547af2aa1a3f5047428ec2e97986d6c9f18 --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/fu-jabra-plugin.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-jabra-device.h" +#include "fu-jabra-plugin.h" + +struct _FuJabraPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuJabraPlugin, fu_jabra_plugin, FU_TYPE_PLUGIN) + +/* slightly weirdly, this takes us from appIDLE back into the actual + * runtime mode where the device actually works */ +static gboolean +fu_jabra_plugin_cleanup(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + GUsbDevice *usb_device; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; + + /* check for a property on the *dfu* FuDevice, which is also why we + * can't just rely on using FuDevice->cleanup() */ + if (!fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_ATTACH_EXTRA_RESET)) + return TRUE; + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); + usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + if (!g_usb_device_reset(usb_device, &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot reset USB device: %s [%i]", + error_local->message, + error_local->code); + return FALSE; + } + + /* wait for device to re-appear */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_jabra_plugin_init(FuJabraPlugin *self) +{ +} + +static void +fu_jabra_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "JabraMagic"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_JABRA_DEVICE); +} + +static void +fu_jabra_plugin_class_init(FuJabraPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_jabra_plugin_constructed; + plugin_class->cleanup = fu_jabra_plugin_cleanup; +} diff --git a/fwupd-1.8.6/plugins/jabra/fu-jabra-plugin.h b/fwupd-1.8.6/plugins/jabra/fu-jabra-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..11839cf4159188ac40a0ab767e572d51f79f7235 --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/fu-jabra-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuJabraPlugin, fu_jabra_plugin, FU, JABRA_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/jabra/jabra.quirk b/fwupd-1.8.6/plugins/jabra/jabra.quirk new file mode 100644 index 0000000000000000000000000000000000000000..ec9702f87bfda1f596241e1d2e8b6b7365778979 --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/jabra.quirk @@ -0,0 +1,23 @@ +# Jabra 410 [runtime] +[USB\VID_0B0E&PID_0412] +Plugin = jabra +JabraMagic = 0201 +CounterpartGuid = USB\VID_0B0E&PID_0411 + +# Jabra 510 [runtime] +[USB\VID_0B0E&PID_0420] +Plugin = jabra +JabraMagic = 0201 +CounterpartGuid = USB\VID_0B0E&PID_0421 + +# Jabra 710 [runtime] +[USB\VID_0B0E&PID_2475] +Plugin = jabra +JabraMagic = 0508 +CounterpartGuid = USB\VID_0B0E&PID_0982 + +# Jabra 810 [runtime] +[USB\VID_0B0E&PID_2456] +Plugin = jabra +JabraMagic = 0508 +CounterpartGuid = USB\VID_0B0E&PID_0971 diff --git a/fwupd-1.8.6/plugins/jabra/meson.build b/fwupd-1.8.6/plugins/jabra/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f48a7620825635b45592f5106e33ebdcf53e0015 --- /dev/null +++ b/fwupd-1.8.6/plugins/jabra/meson.build @@ -0,0 +1,15 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginJabra"'] + +plugin_quirks += files('jabra.quirk') +plugin_builtins += static_library('fu_plugin_jabra', + sources: [ + 'fu-jabra-plugin.c', + 'fu-jabra-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/kinetic-dp/fu-kinetic-dp-plugin.c b/fwupd-1.8.6/plugins/kinetic-dp/fu-kinetic-dp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..e9bea3edb46a42cefe06da8b7fe0c01e89d30e54 --- /dev/null +++ b/fwupd-1.8.6/plugins/kinetic-dp/fu-kinetic-dp-plugin.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) FIXMEFIXMEFIXMEFIXMEFIXME + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-kinetic-dp-device.h" +#include "fu-kinetic-dp-plugin.h" + +struct _FuKineticDpPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuKineticDpPlugin, fu_kinetic_dp_plugin, FU_TYPE_PLUGIN) + +static void +fu_kinetic_dp_plugin_init(FuKineticDpPlugin *self) +{ +} + +static void +fu_kinetic_dp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(FU_PLUGIN(self)); +} + +static void +fu_kinetic_dp_finalize(GObject *obj) +{ + FuKineticDpPlugin *self = FU_KINETIC_DP_PLUGIN(obj); + G_OBJECT_CLASS(fu_kinetic_dp_plugin_parent_class)->finalize(obj); +} + +static void +fu_kinetic_dp_plugin_class_init(FuKineticDpPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_kinetic_dp_plugin_constructed; + object_class->finalize = fu_kinetic_dp_finalize; + plugin_class->startup = fu_kinetic_dp_plugin_startup; +} diff --git a/fwupd-1.8.6/plugins/kinetic-dp/fu-kinetic-dp-plugin.h b/fwupd-1.8.6/plugins/kinetic-dp/fu-kinetic-dp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..41de2996e45d4c1475bc0e954250f1779b673125 --- /dev/null +++ b/fwupd-1.8.6/plugins/kinetic-dp/fu-kinetic-dp-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuKineticDpPlugin, fu_kinetic_dp_plugin, FU, KINETIC_DP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/README.md b/fwupd-1.8.6/plugins/lenovo-thinklmi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..030f7cbf786ba94cafeeefbe1670e0425d1e4309 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/README.md @@ -0,0 +1,12 @@ +# Lenovo ThinkLMI + +## Introduction + +This allows checking whether the firmware on a Lenovo system is configured to +allow UEFI capsule updates using the thinklmi kernel module. + +## External Interface Access + +This plugin requires: + +* read access to `/sys/class/firmware-attributes`. diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-lenovo-thinklmi-plugin.c b/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-lenovo-thinklmi-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..789c7ae0276135552088dd61353561e9dff56c1a --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-lenovo-thinklmi-plugin.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2021 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-lenovo-thinklmi-plugin.h" + +struct _FuLenovoThinklmiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuLenovoThinklmiPlugin, fu_lenovo_thinklmi_plugin, FU_TYPE_PLUGIN) + +#define SLEEP_MODE "com.thinklmi.SleepState" +#define BOOT_ORDER_LOCK "com.thinklmi.BootOrderLock" + +static gboolean +fu_lenovo_thinklmi_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + const gchar *hwid = + fu_context_get_hwid_value(fu_plugin_get_context(plugin), FU_HWIDS_KEY_MANUFACTURER); + if (g_strcmp0(hwid, "LENOVO") != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported manufacturer"); + return FALSE; + } + + return TRUE; +} + +static void +fu_lenovo_thinklmi_plugin_cpu_registered(FuContext *ctx, FuDevice *device) +{ + /* Ryzen 6000 doesn't support S3 even if the BIOS offers it */ + if (fu_device_has_instance_id(device, "CPUID\\PRO_0&FAM_19&MOD_44")) { + FwupdBiosSetting *attr = fu_context_get_bios_setting(ctx, SLEEP_MODE); + + if (attr != NULL) { + g_debug("Setting %s to read-only", fwupd_bios_setting_get_name(attr)); + fwupd_bios_setting_set_read_only(attr, TRUE); + } + } +} + +static void +fu_lenovo_thinklmi_plugin_uefi_capsule_registered(FuContext *ctx, FuDevice *device) +{ + FwupdBiosSetting *attr; + + /* check if boot order lock is turned on */ + attr = fu_context_get_bios_setting(ctx, BOOT_ORDER_LOCK); + if (attr == NULL) { + g_debug("failed to find %s in cache\n", BOOT_ORDER_LOCK); + return; + } + if (g_strcmp0(fwupd_bios_setting_get_current_value(attr), "Enable") == 0) { + fu_device_inhibit(device, + "uefi-capsule-bootorder", + "BootOrder is locked in firmware setup"); + } + + /* check if we're pending for a reboot */ + if (fu_context_get_bios_setting_pending_reboot(ctx)) { + fu_device_inhibit(device, + "uefi-capsule-pending-reboot", + "UEFI BIOS settings update pending reboot"); + } +} + +static void +fu_lenovo_thinklmi_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + if (g_strcmp0(fu_device_get_plugin(device), "uefi_capsule") == 0) { + fu_lenovo_thinklmi_plugin_uefi_capsule_registered(fu_plugin_get_context(plugin), + device); + } else if (g_strcmp0(fu_device_get_plugin(device), "cpu") == 0) { + fu_lenovo_thinklmi_plugin_cpu_registered(fu_plugin_get_context(plugin), device); + } +} + +static void +fu_lenovo_thinklmi_plugin_init(FuLenovoThinklmiPlugin *self) +{ +} + +static void +fu_lenovo_thinklmi_plugin_class_init(FuLenovoThinklmiPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->startup = fu_lenovo_thinklmi_plugin_startup; + plugin_class->device_registered = fu_lenovo_thinklmi_plugin_device_registered; +} diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-lenovo-thinklmi-plugin.h b/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-lenovo-thinklmi-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..a2bf7f53376ea5b2d36181ee5b01ed09c4525c86 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-lenovo-thinklmi-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLenovoThinklmiPlugin, + fu_lenovo_thinklmi_plugin, + FU, + LENOVO_THINKLMI_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-self-test.c b/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..55635d1b42645a56e072d35d333483e9181a91f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/fu-self-test.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2021 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "../uefi-capsule/fu-uefi-capsule-plugin.h" +#include "fu-bios-settings-private.h" +#include "fu-context-private.h" +#include "fu-device-private.h" +#include "fu-lenovo-thinklmi-plugin.h" +#include "fu-plugin-private.h" + +typedef struct { + FuContext *ctx; + FuPlugin *plugin_uefi_capsule; + FuPlugin *plugin_lenovo_thinklmi; +} FuTest; + +static void +_plugin_device_added_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + FuDevice **dev = (FuDevice **)user_data; + *dev = device; +} + +static gboolean +fu_test_self_init(FuTest *self, GError **error_global) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + + g_test_expect_message("FuBiosSettings", G_LOG_LEVEL_WARNING, "*KERNEL*BUG*"); + + ret = fu_context_load_quirks(ctx, + FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_context_load_hwinfo(ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_context_reload_bios_settings(ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_test_assert_expected_messages(); + + self->plugin_uefi_capsule = + fu_plugin_new_from_gtype(fu_uefi_capsule_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->plugin_uefi_capsule, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + self->plugin_lenovo_thinklmi = + fu_plugin_new_from_gtype(fu_lenovo_thinklmi_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->plugin_lenovo_thinklmi, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + self->ctx = fu_plugin_get_context(self->plugin_lenovo_thinklmi); + return TRUE; +} + +static FuDevice * +fu_test_probe_fake_esrt(FuTest *self) +{ + gboolean ret; + gulong added_id; + FuDevice *dev = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + + added_id = g_signal_connect(FU_PLUGIN(self->plugin_uefi_capsule), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + &dev); + + ret = fu_plugin_runner_coldplug(self->plugin_uefi_capsule, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_nonnull(dev); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_signal_handler_disconnect(self->plugin_uefi_capsule, added_id); + return g_object_ref(dev); +} + +static void +fu_plugin_lenovo_thinklmi_bootorder_locked(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *test_dir = + g_test_build_filename(G_TEST_DIST, "tests", "firmware-attributes", "locked", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + + ret = fu_context_reload_bios_settings(self->ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + + dev = fu_test_probe_fake_esrt(self); + fu_plugin_runner_device_register(self->plugin_lenovo_thinklmi, dev); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); +} + +static void +fu_plugin_lenovo_thinklmi_bootorder_unlocked(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *test_dir = + g_test_build_filename(G_TEST_DIST, "tests", "firmware-attributes", "unlocked", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + + ret = fu_context_reload_bios_settings(self->ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + dev = fu_test_probe_fake_esrt(self); + fu_plugin_runner_device_register(self->plugin_lenovo_thinklmi, dev); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)); +} + +static void +fu_plugin_lenovo_thinklmi_reboot_pending(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *test_dir = g_test_build_filename(G_TEST_DIST, + "tests", + "firmware-attributes", + "reboot-pending", + NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + + ret = fu_context_reload_bios_settings(self->ctx, &error); + g_assert_no_error(error); + g_assert_true(ret); + dev = fu_test_probe_fake_esrt(self); + fu_plugin_runner_device_register(self->plugin_lenovo_thinklmi, dev); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)); +} + +static void +fu_test_self_free(FuTest *self) +{ + if (self->plugin_uefi_capsule != NULL) + g_object_unref(self->plugin_uefi_capsule); + if (self->plugin_lenovo_thinklmi != NULL) + g_object_unref(self->plugin_lenovo_thinklmi); + g_free(self); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_self_free) +#pragma clang diagnostic pop + +int +main(int argc, char **argv) +{ + g_autofree gchar *sysfsdir = NULL; + g_autofree gchar *testdatadir = NULL; + g_autofree gchar *confdir = NULL; + g_autofree gchar *test_dir = NULL; + g_autoptr(FuTest) self = g_new0(FuTest, 1); + g_autoptr(GError) error = NULL; + + g_test_init(&argc, &argv, NULL); + + /* starting thinklmi dir to make startup pass */ + test_dir = + g_test_build_filename(G_TEST_DIST, "tests", "firmware-attributes", "locked", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + + /* starting ESRT path */ + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + + /* change behavior of UEFI plugin for test mode */ + sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + (void)g_setenv("FWUPD_UEFI_ESP_PATH", sysfsdir, TRUE); + (void)g_setenv("FWUPD_UEFI_TEST", "1", TRUE); + + /* to load daemon.conf */ + confdir = g_test_build_filename(G_TEST_DIST, "tests", "etc", "fwupd", NULL); + (void)g_setenv("CONFIGURATION_DIRECTORY", confdir, TRUE); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + + g_assert_cmpint(g_mkdir_with_parents("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); + + /* tests go here */ + if (!fu_test_self_init(self, &error)) { + g_test_skip(error->message); + return 0; + } + g_test_add_data_func("/fwupd/plugin{lenovo-think-lmi:bootorder-locked}", + self, + fu_plugin_lenovo_thinklmi_bootorder_locked); + g_test_add_data_func("/fwupd/plugin{lenovo-think-lmi:bootorder-unlocked}", + self, + fu_plugin_lenovo_thinklmi_bootorder_unlocked); + g_test_add_data_func("/fwupd/plugin{lenovo-think-lmi:reboot-pending}", + self, + fu_plugin_lenovo_thinklmi_reboot_pending); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/meson.build b/fwupd-1.8.6/plugins/lenovo-thinklmi/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..387fb59398b6eb92b3f8bca4b95338dd7c4208ab --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/meson.build @@ -0,0 +1,42 @@ +if get_option('plugin_uefi_capsule').disable_auto_if(host_machine.system() != 'linux').allowed() + +cargs = ['-DG_LOG_DOMAIN="FuPluginLenovoThinkLmi"'] + +plugin_builtin_lenovo_thinklmi = static_library('fu_plugin_lenovo_thinklmi', + sources: [ + 'fu-lenovo-thinklmi-plugin.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_lenovo_thinklmi + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + e = executable( + 'lenovo-thinklmi-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + fwupd, + fwupdplugin, + plugin_builtin_lenovo_thinklmi, + plugin_builtin_uefi_capsule, + ], + c_args: [ + cargs, + ], + ) + test('lenovo-thinklmi-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 new file mode 100644 index 0000000000000000000000000000000000000000..8fbb1772616b13ae00a5aa56bacd27217d63013c Binary files /dev/null and b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 differ diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/capsule_flags b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/capsule_flags new file mode 100644 index 0000000000000000000000000000000000000000..1de090ae8ed9ac22623d518ecdc2166abedfce0f --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/capsule_flags @@ -0,0 +1 @@ +0xfe diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_class b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_class new file mode 100644 index 0000000000000000000000000000000000000000..eee3dd810e31b391f9351c90e208fcef6801ef17 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_class @@ -0,0 +1 @@ +ddc0ee61-e7f0-4e7d-acc5-c070a398838e diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_type b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_type new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_type @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_version b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_version new file mode 100644 index 0000000000000000000000000000000000000000..e3ece010ba31607418270eff0fb7806dbcf83b2b --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/fw_version @@ -0,0 +1 @@ +65586 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/last_attempt_status b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/last_attempt_status new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/last_attempt_status @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/last_attempt_version b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/last_attempt_version new file mode 100644 index 0000000000000000000000000000000000000000..5d90037c1956a958384293f2e6eb045d622e2953 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/last_attempt_version @@ -0,0 +1 @@ +18472960 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version new file mode 100644 index 0000000000000000000000000000000000000000..a0709178f6c0f8e97e2b390585d2cbc06b6d4eef --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/esrt/entries/entry0/lowest_supported_fw_version @@ -0,0 +1 @@ +65582 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/fw_platform_size b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/fw_platform_size new file mode 100644 index 0000000000000000000000000000000000000000..900731ffd51ffc82db488b6554f719de735f12bd --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/efi/fw_platform_size @@ -0,0 +1 @@ +64 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/etc/fwupd/daemon.conf b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/etc/fwupd/daemon.conf new file mode 100644 index 0000000000000000000000000000000000000000..7ec52aab87ac6dea8a48c456b98cfacd5e482688 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/etc/fwupd/daemon.conf @@ -0,0 +1,2 @@ +[fwupd] +Manufacturer=LENOVO diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/locked/thinklmi/attributes/BootOrderLock/current_value b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/locked/thinklmi/attributes/BootOrderLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bc0ba0e9217aaef57fbfde1690b4fdec553ee03d --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/locked/thinklmi/attributes/BootOrderLock/current_value @@ -0,0 +1 @@ +Enable diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/locked/thinklmi/attributes/pending_reboot b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/locked/thinklmi/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/locked/thinklmi/attributes/pending_reboot @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/reboot-pending/thinklmi/attributes/BootOrderLock/current_value b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/reboot-pending/thinklmi/attributes/BootOrderLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/reboot-pending/thinklmi/attributes/BootOrderLock/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/reboot-pending/thinklmi/attributes/pending_reboot b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/reboot-pending/thinklmi/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/reboot-pending/thinklmi/attributes/pending_reboot @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/unlocked/thinklmi/attributes/BootOrderLock/current_value b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/unlocked/thinklmi/attributes/BootOrderLock/current_value new file mode 100644 index 0000000000000000000000000000000000000000..bac54c7dfa4576a47235949d38c7c5f7aa7a7281 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/unlocked/thinklmi/attributes/BootOrderLock/current_value @@ -0,0 +1 @@ +Disable diff --git a/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/unlocked/thinklmi/attributes/pending_reboot b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/unlocked/thinklmi/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/plugins/lenovo-thinklmi/tests/firmware-attributes/unlocked/thinklmi/attributes/pending_reboot @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/plugins/linux-lockdown/README.md b/fwupd-1.8.6/plugins/linux-lockdown/README.md new file mode 100644 index 0000000000000000000000000000000000000000..adefa49a8a4139fb6605840e04d5a255e70728a4 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-lockdown/README.md @@ -0,0 +1,10 @@ +# Linux Kernel Lockdown + +## Introduction + +This plugin checks if the currently running kernel is locked down. The result +will be stored in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/sys/kernel/security`. diff --git a/fwupd-1.8.6/plugins/linux-lockdown/fu-linux-lockdown-plugin.c b/fwupd-1.8.6/plugins/linux-lockdown/fu-linux-lockdown-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..5081915cb19a97c24cba1509499809a3aab9ab28 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-lockdown/fu-linux-lockdown-plugin.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-linux-lockdown-plugin.h" + +typedef enum { + FU_PLUGIN_LINUX_LOCKDOWN_UNKNOWN, + FU_PLUGIN_LINUX_LOCKDOWN_INVALID, + FU_PLUGIN_LINUX_LOCKDOWN_NONE, + FU_PLUGIN_LINUX_LOCKDOWN_INTEGRITY, + FU_PLUGIN_LINUX_LOCKDOWN_CONFIDENTIALITY, +} FuPluginLinuxLockdown; + +struct _FuLinuxLockdownPlugin { + FuPlugin parent_instance; + GFile *file; + GFileMonitor *monitor; + FuPluginLinuxLockdown lockdown; +}; + +G_DEFINE_TYPE(FuLinuxLockdownPlugin, fu_linux_lockdown_plugin, FU_TYPE_PLUGIN) + +static const gchar * +fu_linux_lockdown_plugin_to_string(FuPluginLinuxLockdown lockdown) +{ + if (lockdown == FU_PLUGIN_LINUX_LOCKDOWN_NONE) + return "none"; + if (lockdown == FU_PLUGIN_LINUX_LOCKDOWN_INTEGRITY) + return "integrity"; + if (lockdown == FU_PLUGIN_LINUX_LOCKDOWN_CONFIDENTIALITY) + return "confidentiality"; + if (lockdown == FU_PLUGIN_LINUX_LOCKDOWN_INVALID) + return "invalid"; + return NULL; +} + +static void +fu_linux_lockdown_plugin_rescan(FuPlugin *plugin) +{ + FuLinuxLockdownPlugin *self = FU_LINUX_LOCKDOWN_PLUGIN(plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + + /* load file */ + if (!g_file_load_contents(self->file, NULL, &buf, &bufsz, NULL, NULL)) { + self->lockdown = FU_PLUGIN_LINUX_LOCKDOWN_INVALID; + } else if (g_strstr_len(buf, bufsz, "[none]") != NULL) { + self->lockdown = FU_PLUGIN_LINUX_LOCKDOWN_NONE; + } else if (g_strstr_len(buf, bufsz, "[integrity]") != NULL) { + self->lockdown = FU_PLUGIN_LINUX_LOCKDOWN_INTEGRITY; + } else if (g_strstr_len(buf, bufsz, "[confidentiality]") != NULL) { + self->lockdown = FU_PLUGIN_LINUX_LOCKDOWN_CONFIDENTIALITY; + } else { + self->lockdown = FU_PLUGIN_LINUX_LOCKDOWN_UNKNOWN; + } + + /* update metadata */ + fu_plugin_add_report_metadata(plugin, + "LinuxLockdown", + fu_linux_lockdown_plugin_to_string(self->lockdown)); +} + +static void +fu_linux_lockdown_plugin_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN(user_data); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_linux_lockdown_plugin_rescan(plugin); + fu_context_security_changed(ctx); +} + +static gboolean +fu_linux_lockdown_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuLinuxLockdownPlugin *self = FU_LINUX_LOCKDOWN_PLUGIN(plugin); + g_autofree gchar *path = NULL; + g_autofree gchar *fn = NULL; + + path = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_SECURITY); + fn = g_build_filename(path, "lockdown", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Kernel doesn't offer lockdown support."); + return FALSE; + } + self->file = g_file_new_for_path(fn); + self->monitor = g_file_monitor(self->file, G_FILE_MONITOR_NONE, NULL, error); + if (self->monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(self->monitor), + "changed", + G_CALLBACK(fu_linux_lockdown_plugin_changed_cb), + plugin); + fu_linux_lockdown_plugin_rescan(plugin); + return TRUE; +} + +static void +fu_linux_lockdown_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuLinuxLockdownPlugin *self = FU_LINUX_LOCKDOWN_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append(attrs, attr); + + if (self->lockdown == FU_PLUGIN_LINUX_LOCKDOWN_UNKNOWN) { + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + return; + } + + /* load file */ + if (self->lockdown == FU_PLUGIN_LINUX_LOCKDOWN_INVALID) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (self->lockdown == FU_PLUGIN_LINUX_LOCKDOWN_NONE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_linux_lockdown_plugin_to_string2(FuPlugin *plugin, guint idt, GString *str) +{ + FuLinuxLockdownPlugin *self = FU_LINUX_LOCKDOWN_PLUGIN(plugin); + fu_string_append(str, idt, "Lockdown", fu_linux_lockdown_plugin_to_string(self->lockdown)); +} + +static void +fu_linux_lockdown_plugin_init(FuLinuxLockdownPlugin *self) +{ +} + +static void +fu_linux_lockdown_finalize(GObject *obj) +{ + FuLinuxLockdownPlugin *self = FU_LINUX_LOCKDOWN_PLUGIN(obj); + if (self->file != NULL) + g_object_unref(self->file); + if (self->monitor != NULL) { + g_file_monitor_cancel(self->monitor); + g_object_unref(self->monitor); + } + G_OBJECT_CLASS(fu_linux_lockdown_plugin_parent_class)->finalize(obj); +} + +static void +fu_linux_lockdown_plugin_class_init(FuLinuxLockdownPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_linux_lockdown_finalize; + plugin_class->to_string = fu_linux_lockdown_plugin_to_string2; + plugin_class->startup = fu_linux_lockdown_plugin_startup; + plugin_class->add_security_attrs = fu_linux_lockdown_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/linux-lockdown/fu-linux-lockdown-plugin.h b/fwupd-1.8.6/plugins/linux-lockdown/fu-linux-lockdown-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..b626576388cafda6bb79d2e0698ca54766e23c2a --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-lockdown/fu-linux-lockdown-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLinuxLockdownPlugin, + fu_linux_lockdown_plugin, + FU, + LINUX_LOCKDOWN_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/linux-lockdown/meson.build b/fwupd-1.8.6/plugins/linux-lockdown/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..0a6d26be0e8a3933b639863e37b5ca852043ac9c --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-lockdown/meson.build @@ -0,0 +1,13 @@ +if hsi +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxLockdown"'] + +plugin_builtins += static_library('fu_plugin_linux_lockdown', + sources: [ + 'fu-linux-lockdown-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/linux-sleep/README.md b/fwupd-1.8.6/plugins/linux-sleep/README.md new file mode 100644 index 0000000000000000000000000000000000000000..828e8e8428de65c485e660988aaee0e36e18ddd2 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-sleep/README.md @@ -0,0 +1,10 @@ +# Linux Kernel Sleep + +## Introduction + +This plugin checks if s3 sleep is available. The result will be stored in an +security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/power/mem_sleep`. diff --git a/fwupd-1.8.6/plugins/linux-sleep/fu-linux-sleep-plugin.c b/fwupd-1.8.6/plugins/linux-sleep/fu-linux-sleep-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..7fb78c9b7ad5c974bce7b87f93167249363b4183 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-sleep/fu-linux-sleep-plugin.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-linux-sleep-plugin.h" + +struct _FuLinuxSleepPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuLinuxSleepPlugin, fu_linux_sleep_plugin, FU_TYPE_PLUGIN) + +static void +fu_linux_sleep_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GFile) file = g_file_new_for_path("/sys/power/mem_sleep"); + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM); + fu_security_attrs_append(attrs, attr); + + /* load file */ + if (!g_file_load_contents(file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path(file); + g_warning("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + if (g_strstr_len(buf, bufsz, "[deep]") != NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_linux_sleep_plugin_init(FuLinuxSleepPlugin *self) +{ +} + +static void +fu_linux_sleep_plugin_class_init(FuLinuxSleepPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->add_security_attrs = fu_linux_sleep_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/linux-sleep/fu-linux-sleep-plugin.h b/fwupd-1.8.6/plugins/linux-sleep/fu-linux-sleep-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..807838965aac257edc37a0a6b1db6585ae476a70 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-sleep/fu-linux-sleep-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLinuxSleepPlugin, fu_linux_sleep_plugin, FU, LINUX_SLEEP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/linux-sleep/meson.build b/fwupd-1.8.6/plugins/linux-sleep/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d509f27d65d87b25c5ab7da5fe93df6f6f7d854b --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-sleep/meson.build @@ -0,0 +1,13 @@ +if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxSleep"'] + +plugin_builtins += static_library('fu_plugin_linux_sleep', + sources: [ + 'fu-linux-sleep-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/linux-swap/README.md b/fwupd-1.8.6/plugins/linux-swap/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e840370b71dda1ca1b58eaaaf166b7cc30009574 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/README.md @@ -0,0 +1,10 @@ +# Linux Swap + +## Introduction + +This plugin checks if the currently available swap partitions and files are +all encrypted. The result will be stored in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/proc` diff --git a/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap-plugin.c b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..03134b1aed73a85905c7fdc36ebd60c3b2e6a6c4 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap-plugin.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-linux-swap-plugin.h" +#include "fu-linux-swap.h" + +struct _FuLinuxSwapPlugin { + FuPlugin parent_instance; + GFile *file; + GFileMonitor *monitor; +}; + +G_DEFINE_TYPE(FuLinuxSwapPlugin, fu_linux_swap_plugin, FU_TYPE_PLUGIN) + +static void +fu_linux_swap_plugin_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN(user_data); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_security_changed(ctx); +} + +static gboolean +fu_linux_swap_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuLinuxSwapPlugin *self = FU_LINUX_SWAP_PLUGIN(plugin); + g_autofree gchar *fn = NULL; + g_autofree gchar *procfs = NULL; + + procfs = fu_path_from_kind(FU_PATH_KIND_PROCFS); + fn = g_build_filename(procfs, "swaps", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Kernel doesn't offer swap support."); + return FALSE; + } + self->file = g_file_new_for_path(fn); + self->monitor = g_file_monitor(self->file, G_FILE_MONITOR_NONE, NULL, error); + if (self->monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(self->monitor), + "changed", + G_CALLBACK(fu_linux_swap_plugin_changed_cb), + plugin); + return TRUE; +} + +static void +fu_linux_swap_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuLinuxSwapPlugin *self = FU_LINUX_SWAP_PLUGIN(plugin); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + if (self->file == NULL) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append(attrs, attr); + + /* load list of swaps */ + if (!g_file_load_contents(self->file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path(self->file); + g_warning("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + swap = fu_linux_swap_new(buf, bufsz, &error_local); + if (swap == NULL) { + g_autofree gchar *fn = g_file_get_path(self->file); + g_warning("could not parse %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* none configured */ + if (!fu_linux_swap_get_enabled(swap)) { + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* add security attribute */ + if (!fu_linux_swap_get_encrypted(swap)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED); +} + +static void +fu_linux_swap_plugin_init(FuLinuxSwapPlugin *self) +{ +} + +static void +fu_linux_swap_finalize(GObject *obj) +{ + FuLinuxSwapPlugin *self = FU_LINUX_SWAP_PLUGIN(obj); + if (self->file != NULL) + g_object_unref(self->file); + if (self->monitor != NULL) { + g_file_monitor_cancel(self->monitor); + g_object_unref(self->monitor); + } + G_OBJECT_CLASS(fu_linux_swap_plugin_parent_class)->finalize(obj); +} + +static void +fu_linux_swap_plugin_class_init(FuLinuxSwapPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_linux_swap_finalize; + plugin_class->startup = fu_linux_swap_plugin_startup; + plugin_class->add_security_attrs = fu_linux_swap_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap-plugin.h b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..499c679bf63ab5120be4b83465a3177a5d3a6ad3 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLinuxSwapPlugin, fu_linux_swap_plugin, FU, LINUX_SWAP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap.c b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap.c new file mode 100644 index 0000000000000000000000000000000000000000..58961d896130c423ac41652846f8b0ab08d08d1b --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-linux-swap.h" + +struct _FuLinuxSwap { + GObject parent_instance; + guint encrypted_cnt; + guint enabled_cnt; +}; + +G_DEFINE_TYPE(FuLinuxSwap, fu_linux_swap, G_TYPE_OBJECT) + +static gchar * +fu_strdup_nospaces(const gchar *line) +{ + GString *str = g_string_new(NULL); + for (guint i = 0; line[i] != '\0' && !g_ascii_isspace(line[i]); i++) + g_string_append_c(str, line[i]); + return g_string_free(str, FALSE); +} + +static gboolean +fu_linux_swap_verify_partition(FuLinuxSwap *self, const gchar *fn, GError **error) +{ + g_autoptr(FuVolume) volume = NULL; + + /* find the device */ + volume = fu_volume_new_by_device(fn, error); + if (volume == NULL) + return FALSE; + + /* this isn't technically encrypted, but isn't on disk in plaintext */ + if (g_str_has_prefix(fn, "/dev/zram")) { + g_debug("%s is zram, assuming encrypted", fn); + self->encrypted_cnt++; + return TRUE; + } + + /* is this mount point encrypted */ + if (fu_volume_is_encrypted(volume)) { + g_debug("%s partition is encrypted", fn); + self->encrypted_cnt++; + } else { + g_debug("%s partition is unencrypted", fn); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_linux_swap_verify_file(FuLinuxSwap *self, const gchar *fn, GError **error) +{ + guint32 devnum; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + g_autoptr(FuVolume) volume = NULL; + + /* get the device number for the file */ + file = g_file_new_for_path(fn); + info = g_file_query_info(file, + G_FILE_ATTRIBUTE_UNIX_DEVICE, + G_FILE_QUERY_INFO_NONE, + NULL, + error); + if (info == NULL) + return FALSE; + devnum = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_DEVICE); + + /* find the device */ + volume = fu_volume_new_by_devnum(devnum, error); + if (volume == NULL) + return FALSE; + + /* is this mount point encrypted */ + if (fu_volume_is_encrypted(volume)) { + g_debug("%s file is encrypted", fn); + self->encrypted_cnt++; + } else { + g_debug("%s file is unencrypted", fn); + } + + /* success */ + return TRUE; +} + +FuLinuxSwap * +fu_linux_swap_new(const gchar *buf, gsize bufsz, GError **error) +{ + g_autoptr(FuLinuxSwap) self = g_object_new(FU_TYPE_LINUX_SWAP, NULL); + g_auto(GStrv) lines = NULL; + + /* look at each line in /proc/swaps */ + if (bufsz == 0) + bufsz = strlen(buf); + lines = fu_strsplit(buf, bufsz, "\n", -1); + if (g_strv_length(lines) > 2) { + for (guint i = 1; lines[i] != NULL && lines[i][0] != '\0'; i++) { + g_autofree gchar *fn = NULL; + g_autofree gchar *ty = NULL; + + /* split */ + if (g_utf8_strlen(lines[i], -1) < 45) + continue; + fn = fu_strdup_nospaces(lines[i]); + ty = fu_strdup_nospaces(lines[i] + 40); + + /* partition, so use UDisks to see if backed by crypto */ + if (g_strcmp0(ty, "partition") == 0) { + self->enabled_cnt++; + if (!fu_linux_swap_verify_partition(self, fn, error)) + return NULL; + } else if (g_strcmp0(ty, "file") == 0) { + self->enabled_cnt++; + if (!fu_linux_swap_verify_file(self, fn, error)) + return NULL; + } else { + g_warning("unknown swap type: %s [%s]", ty, fn); + } + } + } + return g_steal_pointer(&self); +} + +/* success if *all* the swap devices are encrypted */ +gboolean +fu_linux_swap_get_encrypted(FuLinuxSwap *self) +{ + g_return_val_if_fail(FU_IS_LINUX_SWAP(self), FALSE); + return self->enabled_cnt > 0 && self->enabled_cnt == self->encrypted_cnt; +} + +gboolean +fu_linux_swap_get_enabled(FuLinuxSwap *self) +{ + g_return_val_if_fail(FU_IS_LINUX_SWAP(self), FALSE); + return self->enabled_cnt > 0; +} + +static void +fu_linux_swap_class_init(FuLinuxSwapClass *klass) +{ +} + +static void +fu_linux_swap_init(FuLinuxSwap *self) +{ +} diff --git a/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap.h b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap.h new file mode 100644 index 0000000000000000000000000000000000000000..d291a747ef502e268d8fbcde4f4eb54a67875876 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/fu-linux-swap.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_LINUX_SWAP (fu_linux_swap_get_type()) +G_DECLARE_FINAL_TYPE(FuLinuxSwap, fu_linux_swap, FU, LINUX_SWAP, GObject) + +FuLinuxSwap * +fu_linux_swap_new(const gchar *buf, gsize bufsz, GError **error); +gboolean +fu_linux_swap_get_enabled(FuLinuxSwap *self); +gboolean +fu_linux_swap_get_encrypted(FuLinuxSwap *self); diff --git a/fwupd-1.8.6/plugins/linux-swap/fu-self-test.c b/fwupd-1.8.6/plugins/linux-swap/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..97a4f671ca0df1ebeb6fb7200c53b47f3833a70b --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/fu-self-test.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-linux-swap.h" + +static void +fu_linux_swap_none_func(void) +{ + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(GError) error = NULL; + + swap = fu_linux_swap_new("Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n", 0, &error); + g_assert_no_error(error); + g_assert_nonnull(swap); + g_assert_false(fu_linux_swap_get_enabled(swap)); + g_assert_false(fu_linux_swap_get_encrypted(swap)); +} + +static void +fu_linux_swap_plain_func(void) +{ + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(GError) error = NULL; + + swap = + fu_linux_swap_new("Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n" + "/dev/nvme0n1p4 partition\t5962748\t0\t-2\n", + 0, + &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_test_skip(error->message); + return; + } + if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + g_test_skip(error->message); + return; + } + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED)) { + g_test_skip(error->message); + return; + } + g_assert_no_error(error); + g_assert_nonnull(swap); +} + +static void +fu_linux_swap_encrypted_func(void) +{ + g_autoptr(FuLinuxSwap) swap = NULL; + g_autoptr(GError) error = NULL; + + swap = + fu_linux_swap_new("Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n" + "/dev/dm-1 partition\t5962748\t0\t-2\n", + 0, + &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) || + g_error_matches(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_test_skip(error->message); + return; + } + g_assert_no_error(error); + g_assert_nonnull(swap); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/linux-swap/none", fu_linux_swap_none_func); + g_test_add_func("/linux-swap/plain", fu_linux_swap_plain_func); + g_test_add_func("/linux-swap/encrypted", fu_linux_swap_encrypted_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/linux-swap/meson.build b/fwupd-1.8.6/plugins/linux-swap/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..a01184176702800c8a62cf80536735ae3321da79 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-swap/meson.build @@ -0,0 +1,34 @@ +if hsi +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxSwap"'] + +plugin_builtin_linux_swap = static_library('fu_plugin_linux_swap', + sources: [ + 'fu-linux-swap-plugin.c', + 'fu-linux-swap.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_linux_swap + +if get_option('tests') + e = executable( + 'linux-swap-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_linux_swap, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('linux-swap-self-test', e) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/linux-tainted/README.md b/fwupd-1.8.6/plugins/linux-tainted/README.md new file mode 100644 index 0000000000000000000000000000000000000000..641626bc378d64597197a99be1ec44500eb66840 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-tainted/README.md @@ -0,0 +1,10 @@ +# Linux Kernel Tainted + +## Introduction + +This plugin checks if the currently running kernel is tainted. The result will +be stored in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/kernel/tainted`. diff --git a/fwupd-1.8.6/plugins/linux-tainted/fu-linux-tainted-plugin.c b/fwupd-1.8.6/plugins/linux-tainted/fu-linux-tainted-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..fbc63f243b5e8eb4eee60e77b15c7730dcb163a9 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-tainted/fu-linux-tainted-plugin.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-linux-tainted-plugin.h" + +struct _FuLinuxTaintedPlugin { + FuPlugin parent_instance; + GFile *file; + GFileMonitor *monitor; +}; + +G_DEFINE_TYPE(FuLinuxTaintedPlugin, fu_linux_tainted_plugin, FU_TYPE_PLUGIN) + +#define KERNEL_TAINT_FLAG_PROPRIETARY_MODULE (1 << 0) +#define KERNEL_TAINT_FLAG_MODULE_FORCE_LOAD (1 << 1) +#define KERNEL_TAINT_FLAG_KERNEL_OUT_OF_SPEC (1 << 2) +#define KERNEL_TAINT_FLAG_MODULE_FORCE_UNLOAD (1 << 3) +#define KERNEL_TAINT_FLAG_PROCESSOR_MCE (1 << 4) +#define KERNEL_TAINT_FLAG_BAD_PAGE (1 << 5) +#define KERNEL_TAINT_FLAG_REQUESTED_BY_USERSPACE (1 << 6) +#define KERNEL_TAINT_FLAG_KERNEL_DIED (1 << 7) +#define KERNEL_TAINT_FLAG_ACPI_OVERRIDDEN (1 << 8) +#define KERNEL_TAINT_FLAG_KERNEL_ISSUED_WARNING (1 << 9) +#define KERNEL_TAINT_FLAG_STAGING_DRIVER_LOADED (1 << 10) +#define KERNEL_TAINT_FLAG_FIRMWARE_WORKAROUND_APPLIED (1 << 11) +#define KERNEL_TAINT_FLAG_EXTERNAL_MODULE_LOADED (1 << 12) +#define KERNEL_TAINT_FLAG_UNSIGNED_MODULE_LOADED (1 << 13) +#define KERNEL_TAINT_FLAG_SOFT_LOCKUP_OCCURRED (1 << 14) +#define KERNEL_TAINT_FLAG_KERNEL_LIVE_PATCHED (1 << 15) +#define KERNEL_TAINT_FLAG_AUXILIARY_TAINT (1 << 16) +#define KERNEL_TAINT_FLAG_STRUCT_RANDOMIZATION_PLUGIN (1 << 17) +#define KERNEL_TAINT_FLAG_IN_KERNEL_TEST (1 << 18) + +static void +fu_linux_tainted_plugin_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN(user_data); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_security_changed(ctx); +} + +static gboolean +fu_linux_tainted_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuLinuxTaintedPlugin *self = FU_LINUX_TAINTED_PLUGIN(plugin); + g_autofree gchar *fn = NULL; + g_autofree gchar *procfs = NULL; + + procfs = fu_path_from_kind(FU_PATH_KIND_PROCFS); + fn = g_build_filename(procfs, "sys", "kernel", "tainted", NULL); + self->file = g_file_new_for_path(fn); + self->monitor = g_file_monitor(self->file, G_FILE_MONITOR_NONE, NULL, error); + if (self->monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(self->monitor), + "changed", + G_CALLBACK(fu_linux_tainted_plugin_changed_cb), + plugin); + return TRUE; +} + +static void +fu_linux_tainted_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuLinuxTaintedPlugin *self = FU_LINUX_TAINTED_PLUGIN(plugin); + gsize bufsz = 0; + guint64 value = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fu_security_attrs_append(attrs, attr); + + /* startup failed */ + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) { + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + return; + } + + /* load file */ + if (!g_file_load_contents(self->file, NULL, &buf, &bufsz, NULL, &error_local)) { + g_autofree gchar *fn = g_file_get_path(self->file); + g_warning("could not open %s: %s", fn, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* do not assume NUL terminated */ + str = g_strndup(buf, bufsz); + if (!fu_strtoull(str, &value, 0, G_MAXUINT64, &error_local)) { + g_warning("could not parse %s: %s", str, error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* only some taint flags are important */ + if ((value & KERNEL_TAINT_FLAG_PROPRIETARY_MODULE) > 0 || + (value & KERNEL_TAINT_FLAG_MODULE_FORCE_LOAD) > 0 || + (value & KERNEL_TAINT_FLAG_MODULE_FORCE_UNLOAD) > 0 || + (value & KERNEL_TAINT_FLAG_STAGING_DRIVER_LOADED) > 0 || + (value & KERNEL_TAINT_FLAG_EXTERNAL_MODULE_LOADED) > 0 || + (value & KERNEL_TAINT_FLAG_UNSIGNED_MODULE_LOADED) > 0 || + (value & KERNEL_TAINT_FLAG_ACPI_OVERRIDDEN) > 0 || + (value & KERNEL_TAINT_FLAG_AUXILIARY_TAINT) > 0) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_TAINTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED); +} + +static void +fu_linux_tainted_plugin_init(FuLinuxTaintedPlugin *self) +{ +} + +static void +fu_linux_tainted_finalize(GObject *obj) +{ + FuLinuxTaintedPlugin *self = FU_LINUX_TAINTED_PLUGIN(obj); + if (self->file != NULL) + g_object_unref(self->file); + if (self->monitor != NULL) { + g_file_monitor_cancel(self->monitor); + g_object_unref(self->monitor); + } + G_OBJECT_CLASS(fu_linux_tainted_plugin_parent_class)->finalize(obj); +} + +static void +fu_linux_tainted_plugin_class_init(FuLinuxTaintedPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_linux_tainted_finalize; + plugin_class->startup = fu_linux_tainted_plugin_startup; + plugin_class->add_security_attrs = fu_linux_tainted_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/linux-tainted/fu-linux-tainted-plugin.h b/fwupd-1.8.6/plugins/linux-tainted/fu-linux-tainted-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..eff6818e69c2dd0e28df2a61b0c4b23b0d1e37c1 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-tainted/fu-linux-tainted-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLinuxTaintedPlugin, + fu_linux_tainted_plugin, + FU, + LINUX_TAINTED_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/linux-tainted/meson.build b/fwupd-1.8.6/plugins/linux-tainted/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..3796410e6f02bcdaf575c5cf360bfe2c0600e041 --- /dev/null +++ b/fwupd-1.8.6/plugins/linux-tainted/meson.build @@ -0,0 +1,13 @@ +if hsi +cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxTainted"'] + +plugin_builtins += static_library('fu_plugin_linux_tainted', + sources: [ + 'fu-linux-tainted-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/logind/README.md b/fwupd-1.8.6/plugins/logind/README.md new file mode 100644 index 0000000000000000000000000000000000000000..602d65e06690021c7ed13f67a889877336a5d772 --- /dev/null +++ b/fwupd-1.8.6/plugins/logind/README.md @@ -0,0 +1,14 @@ +# logind + +## Introduction + +This plugin is used to ensure that the machine does not enter a low power mode +when updates are being performed. + +## Vendor ID Security + +This protocol does not create a device and thus requires no vendor ID set. + +## External Interface Access + +This plugin requires access to the dbus interface `org.freedesktop.login1`. diff --git a/fwupd-1.8.6/plugins/logind/fu-logind-plugin.c b/fwupd-1.8.6/plugins/logind/fu-logind-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..cd9b8f1de80c0bf09f84615b5d8dc92dcfbcff46 --- /dev/null +++ b/fwupd-1.8.6/plugins/logind/fu-logind-plugin.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-logind-plugin.h" + +struct _FuLogindPlugin { + FuPlugin parent_instance; + GDBusProxy *logind_proxy; + gint logind_fd; +}; + +G_DEFINE_TYPE(FuLogindPlugin, fu_logind_plugin, FU_TYPE_PLUGIN) + +static void +fu_logind_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuLogindPlugin *self = FU_LOGIND_PLUGIN(plugin); + fu_string_append_kx(str, idt, "LogindFd", self->logind_fd); +} + +static gboolean +fu_logind_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuLogindPlugin *self = FU_LOGIND_PLUGIN(plugin); + g_autofree gchar *name_owner = NULL; + + self->logind_proxy = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + NULL, + error); + if (self->logind_proxy == NULL) { + g_prefix_error(error, "failed to connect to logind: "); + return FALSE; + } + name_owner = g_dbus_proxy_get_name_owner(self->logind_proxy); + if (name_owner == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no owner for %s", + g_dbus_proxy_get_name(self->logind_proxy)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logind_plugin_prepare(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogindPlugin *self = FU_LOGIND_PLUGIN(plugin); + g_autoptr(GError) error_local = NULL; + g_autoptr(GUnixFDList) out_fd_list = NULL; + g_autoptr(GVariant) res = NULL; + const gchar *what = "shutdown:sleep:idle:handle-power-key:handle-suspend-key:" + "handle-hibernate-key:handle-lid-switch"; + + /* already inhibited */ + if (self->logind_fd != 0) + return TRUE; + + /* not yet connected */ + if (self->logind_proxy == NULL) { + g_warning("no logind connection to use"); + return TRUE; + } + + /* block shutdown and idle */ + res = g_dbus_proxy_call_with_unix_fd_list_sync( + self->logind_proxy, + "Inhibit", + g_variant_new("(ssss)", what, PACKAGE_NAME, "Firmware Update in Progress", "block"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* fd_list */ + &out_fd_list, + NULL, /* GCancellable */ + &error_local); + if (res == NULL) { + g_warning("failed to Inhibit using logind: %s", error_local->message); + return TRUE; + } + + /* keep fd as cookie */ + if (g_unix_fd_list_get_length(out_fd_list) != 1) { + g_warning("invalid response from logind"); + return TRUE; + } + self->logind_fd = g_unix_fd_list_get(out_fd_list, 0, NULL); + g_debug("opened logind fd %i", self->logind_fd); + return TRUE; +} + +static gboolean +fu_logind_plugin_cleanup(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogindPlugin *self = FU_LOGIND_PLUGIN(plugin); + if (self->logind_fd == 0) + return TRUE; + g_debug("closed logind fd %i", self->logind_fd); + if (!g_close(self->logind_fd, error)) + return FALSE; + self->logind_fd = 0; + return TRUE; +} + +static void +fu_logind_plugin_init(FuLogindPlugin *self) +{ +} + +static void +fu_logind_finalize(GObject *obj) +{ + FuLogindPlugin *self = FU_LOGIND_PLUGIN(obj); + if (self->logind_fd != 0) + g_close(self->logind_fd, NULL); + if (self->logind_proxy != NULL) + g_object_unref(self->logind_proxy); + G_OBJECT_CLASS(fu_logind_plugin_parent_class)->finalize(obj); +} + +static void +fu_logind_plugin_class_init(FuLogindPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_logind_finalize; + plugin_class->to_string = fu_logind_plugin_to_string; + plugin_class->startup = fu_logind_plugin_startup; + plugin_class->cleanup = fu_logind_plugin_cleanup; + plugin_class->prepare = fu_logind_plugin_prepare; +} diff --git a/fwupd-1.8.6/plugins/logind/fu-logind-plugin.h b/fwupd-1.8.6/plugins/logind/fu-logind-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..8f576a2d5d2959cac157d86c73760100e25ea4ea --- /dev/null +++ b/fwupd-1.8.6/plugins/logind/fu-logind-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLogindPlugin, fu_logind_plugin, FU, LOGIND_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/logind/meson.build b/fwupd-1.8.6/plugins/logind/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..22a8bfdcbb6a45e2105209ce5ebc0dc0a78c6f86 --- /dev/null +++ b/fwupd-1.8.6/plugins/logind/meson.build @@ -0,0 +1,13 @@ +if libsystemd.found() or elogind.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginLogind"'] + +plugin_builtins += static_library('fu_plugin_logind', + sources: [ + 'fu-logind-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/README.md b/fwupd-1.8.6/plugins/logitech-bulkcontroller/README.md new file mode 100644 index 0000000000000000000000000000000000000000..67df69a590377db950c386da9e4ded985b340ae6 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/README.md @@ -0,0 +1,40 @@ +# Logitech Video Collaboration + +## Introduction + +This plugin can upgrade the firmware on Logitech Video Collaboration products (Rally Bar and Rally Bar Mini), using USB bulk transfer. + +## 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.logitech.vc.proto + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_046D&PID_089B` +* `USB\VID_046D&PID_08D3` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +## Update Behavior + +The peripheral firmware is deployed when the device is in normal runtime mode, +and the device will reset when the new firmware has been written. + +## Design Notes + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x046D` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c new file mode 100644 index 0000000000000000000000000000000000000000..340686625efbabfb6bf7367ea91a066a154e33e0 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-bulkcontroller-common.h" +#include "usb_msg.pb-c.h" + +static void +proto_manager_set_header(Logi__Device__Proto__Header *header_msg) +{ + gint64 timestamp_tv; + + g_return_if_fail(header_msg != NULL); + + timestamp_tv = g_get_real_time(); + header_msg->id = g_uuid_string_random(); + header_msg->timestamp = g_strdup_printf("%" G_GINT64_FORMAT, timestamp_tv / 1000); +} + +GByteArray * +proto_manager_generate_get_device_info_request(void) +{ + GByteArray *buf = g_byte_array_new(); + Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; + Logi__Device__Proto__GetDeviceInfoRequest get_deviceinfo_msg = + LOGI__DEVICE__PROTO__GET_DEVICE_INFO_REQUEST__INIT; + Logi__Device__Proto__UsbMsg usb_msg = LOGI__DEVICE__PROTO__USB_MSG__INIT; + Logi__Device__Proto__Request request_msg = LOGI__DEVICE__PROTO__REQUEST__INIT; + request_msg.payload_case = LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_GET_DEVICE_INFO_REQUEST; + request_msg.get_device_info_request = &get_deviceinfo_msg; + + proto_manager_set_header(&header_msg); + usb_msg.header = &header_msg; + usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; + usb_msg.request = &request_msg; + + fu_byte_array_set_size(buf, logi__device__proto__usb_msg__get_packed_size(&usb_msg), 0x00); + logi__device__proto__usb_msg__pack(&usb_msg, (unsigned char *)buf->data); + return buf; +} + +GByteArray * +proto_manager_generate_transition_to_device_mode_request(void) +{ + GByteArray *buf = g_byte_array_new(); + Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; + Logi__Device__Proto__TransitionToDeviceModeRequest transition_to_device_mode_msg = + LOGI__DEVICE__PROTO__TRANSITION_TO_DEVICE_MODE_REQUEST__INIT; + Logi__Device__Proto__UsbMsg usb_msg = LOGI__DEVICE__PROTO__USB_MSG__INIT; + Logi__Device__Proto__Request request_msg = LOGI__DEVICE__PROTO__REQUEST__INIT; + request_msg.payload_case = + LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_TRANSITION_TO_DEVICEMODE_REQUEST; + request_msg.transition_to_devicemode_request = &transition_to_device_mode_msg; + + proto_manager_set_header(&header_msg); + usb_msg.header = &header_msg; + usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; + usb_msg.request = &request_msg; + + fu_byte_array_set_size(buf, logi__device__proto__usb_msg__get_packed_size(&usb_msg), 0x00); + logi__device__proto__usb_msg__pack(&usb_msg, (unsigned char *)buf->data); + return buf; +} + +GByteArray * +proto_manager_generate_set_device_time_request(void) +{ + GByteArray *buf = g_byte_array_new(); +#if GLIB_CHECK_VERSION(2, 57, 1) + g_autoptr(GTimeZone) tz = g_time_zone_new_local(); +#else + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); +#endif + + Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; + Logi__Device__Proto__SetDeviceTimeRequest set_devicetime_msg = + LOGI__DEVICE__PROTO__SET_DEVICE_TIME_REQUEST__INIT; + Logi__Device__Proto__UsbMsg usb_msg = LOGI__DEVICE__PROTO__USB_MSG__INIT; + Logi__Device__Proto__Request request_msg = LOGI__DEVICE__PROTO__REQUEST__INIT; + request_msg.payload_case = LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_SET_DEVICE_TIME_REQUEST; + request_msg.set_device_time_request = &set_devicetime_msg; + +#if GLIB_CHECK_VERSION(2, 57, 1) + set_devicetime_msg.ts = (g_get_real_time() / 1000) + SET_TIME_DELAY_MS; + set_devicetime_msg.time_zone = g_strdup_printf("%s", g_time_zone_get_identifier(tz)); +#else + set_devicetime_msg.ts = (g_date_time_to_unix(dt) * 1000) + SET_TIME_DELAY_MS; + set_devicetime_msg.time_zone = g_strdup_printf("%s", "UTC"); +#endif + proto_manager_set_header(&header_msg); + usb_msg.header = &header_msg; + usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; + usb_msg.request = &request_msg; + + fu_byte_array_set_size(buf, logi__device__proto__usb_msg__get_packed_size(&usb_msg), 0x00); + logi__device__proto__usb_msg__pack(&usb_msg, (unsigned char *)buf->data); + return buf; +} + +GByteArray * +proto_manager_decode_message(const guint8 *data, + guint32 len, + FuLogitechBulkcontrollerProtoId *proto_id, + GError **error) +{ + g_autoptr(GByteArray) buf_decoded = g_byte_array_new(); + guint32 success = 0; + guint32 error_code = 0; + Logi__Device__Proto__UsbMsg *usb_msg = + logi__device__proto__usb_msg__unpack(NULL, len, (const unsigned char *)data); + if (usb_msg == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unable to unpack data"); + return NULL; + } + + switch (usb_msg->message_case) { + case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_ACK: + *proto_id = kProtoId_Ack; + break; + case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_RESPONSE: + if (!usb_msg->response) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no USB response"); + return NULL; + } + switch (usb_msg->response->payload_case) { + case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_GET_DEVICE_INFO_RESPONSE: + if (usb_msg->response->get_device_info_response) { + const gchar *tmp = + usb_msg->response->get_device_info_response->payload; + *proto_id = kProtoId_GetDeviceInfoResponse; + if (tmp != NULL) + g_byte_array_append(buf_decoded, + (const guint8 *)tmp, + strlen(tmp)); + } + break; + case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_TRANSITION_TO_DEVICEMODE_RESPONSE: + if (usb_msg->response->transition_to_devicemode_response) { + *proto_id = kProtoId_TransitionToDeviceModeResponse; + success = + usb_msg->response->transition_to_devicemode_response->success + ? 1 + : 0; + error_code = + usb_msg->response->transition_to_devicemode_response->error; + fu_byte_array_append_uint32(buf_decoded, success, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf_decoded, + error_code, + G_LITTLE_ENDIAN); + } + break; + default: + break; + }; + break; + case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_EVENT: + if (!usb_msg->response) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no USB event"); + return NULL; + } + switch (usb_msg->event->payload_case) { + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_KONG_EVENT: + if (usb_msg->event->kong_event) { + const gchar *tmp = usb_msg->event->kong_event->mqtt_event; + *proto_id = kProtoId_KongEvent; + if (tmp != NULL) + g_byte_array_append(buf_decoded, + (const guint8 *)tmp, + strlen(tmp)); + } + break; + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_HANDSHAKE_EVENT: + if (usb_msg->event->handshake_event) { + *proto_id = kProtoId_HandshakeEvent; + } + break; + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_CRASH_DUMP_AVAILABLE_EVENT: + *proto_id = kProtoId_CrashDumpAvailableEvent; + break; + default: + break; + }; + break; + default: + break; + }; + logi__device__proto__usb_msg__free_unpacked(usb_msg, NULL); + return g_steal_pointer(&buf_decoded); +} + +const gchar * +fu_logitech_bulkcontroller_device_status_to_string(FuLogitechBulkcontrollerDeviceStatus status) +{ + if (status == kDeviceStateUnknown) + return "Unknown"; + if (status == kDeviceStateOffline) + return "Offline"; + if (status == kDeviceStateOnline) + return "Online"; + if (status == kDeviceStateIdle) + return "Idle"; + if (status == kDeviceStateInUse) + return "InUse"; + if (status == kDeviceStateAudioOnly) + return "AudioOnly"; + if (status == kDeviceStateEnumerating) + return "Enumerating"; + return NULL; +} + +const gchar * +fu_logitech_bulkcontroller_device_update_state_to_string( + FuLogitechBulkcontrollerDeviceUpdateState update_state) +{ + if (update_state == kUpdateStateUnknown) + return "Unknown"; + if (update_state == kUpdateStateCurrent) + return "Current"; + if (update_state == kUpdateStateAvailable) + return "Available"; + if (update_state == kUpdateStateStarting) + return "Starting"; + if (update_state == kUpdateStateDownloading) + return "Downloading"; + if (update_state == kUpdateStateReady) + return "Ready"; + if (update_state == kUpdateStateUpdating) + return "Updating"; + if (update_state == kUpdateStateScheduled) + return "Scheduled"; + if (update_state == kUpdateStateError) + return "Error"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h new file mode 100644 index 0000000000000000000000000000000000000000..3e3cd19e81b5de83de6140d88b0b4bed70601f2f --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "usb_msg.pb-c.h" + +#define SET_TIME_DELAY_MS 500 /* send future time to keep PC & device time as close as possible */ + +typedef enum { + kDeviceStateUnknown = -1, + kDeviceStateOffline, + kDeviceStateOnline, + kDeviceStateIdle, + kDeviceStateInUse, + kDeviceStateAudioOnly, + kDeviceStateEnumerating +} FuLogitechBulkcontrollerDeviceStatus; + +typedef enum { + kUpdateStateUnknown = -1, + kUpdateStateCurrent, + kUpdateStateAvailable, + kUpdateStateStarting = 3, + kUpdateStateDownloading, + kUpdateStateReady, + kUpdateStateUpdating, + kUpdateStateScheduled, + kUpdateStateError +} FuLogitechBulkcontrollerDeviceUpdateState; + +typedef enum { + kProtoId_UnknownId, + kProtoId_GetDeviceInfoResponse, + kProtoId_TransitionToDeviceModeResponse, + kProtoId_Ack, + kProtoId_KongEvent, + kProtoId_HandshakeEvent, + kProtoId_CrashDumpAvailableEvent +} FuLogitechBulkcontrollerProtoId; + +const gchar * +fu_logitech_bulkcontroller_device_status_to_string(FuLogitechBulkcontrollerDeviceStatus status); +const gchar * +fu_logitech_bulkcontroller_device_update_state_to_string( + FuLogitechBulkcontrollerDeviceUpdateState update_state); +GByteArray * +proto_manager_generate_get_device_info_request(void); +GByteArray * +proto_manager_generate_transition_to_device_mode_request(void); +GByteArray * +proto_manager_generate_set_device_time_request(void); +GByteArray * +proto_manager_decode_message(const guint8 *data, + guint32 len, + FuLogitechBulkcontrollerProtoId *proto_id, + GError **error); diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c new file mode 100644 index 0000000000000000000000000000000000000000..e30003882a6123625c127666f2f168ae83e951a6 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-logitech-bulkcontroller-common.h" +#include "fu-logitech-bulkcontroller-device.h" + +/* SYNC interface follows TLSV (Type, Length, SequenceID, Value) protocol */ +/* UPD interface follows TLV (Type, Length, Value) protocol */ +/* Payload size limited to 8k for both interfaces */ +#define UPD_PACKET_HEADER_SIZE (2 * sizeof(guint32)) +#define SYNC_PACKET_HEADER_SIZE (3 * sizeof(guint32)) +#define HASH_TIMEOUT 30000 +#define MAX_DATA_SIZE 8192 /* 8k */ +#define PAYLOAD_SIZE MAX_DATA_SIZE - UPD_PACKET_HEADER_SIZE +#define UPD_INTERFACE_SUBPROTOCOL_ID 117 +#define SYNC_INTERFACE_SUBPROTOCOL_ID 118 +#define BULK_TRANSFER_TIMEOUT 1000 +#define HASH_VALUE_SIZE 16 +#define LENGTH_OFFSET 0x4 +#define COMMAND_OFFSET 0x0 +#define SYNC_ACK_PAYLOAD_LENGTH 5 +#define MAX_RETRIES 5 +#define MAX_HANDSHAKE_RETRIES 3 +#define MAX_WAIT_COUNT 150 + +enum { SHA_256, SHA_512, MD5 }; + +enum { EP_OUT, EP_IN, EP_LAST }; + +enum { BULK_INTERFACE_UPD, BULK_INTERFACE_SYNC }; + +typedef enum { + CMD_CHECK_BUFFERSIZE = 0xCC00, + CMD_INIT = 0xCC01, + CMD_START_TRANSFER = 0xCC02, + CMD_DATA_TRANSFER = 0xCC03, + CMD_END_TRANSFER = 0xCC04, + CMD_UNINIT = 0xCC05, + CMD_BUFFER_READ = 0xCC06, + CMD_BUFFER_WRITE = 0xCC07, + CMD_UNINIT_BUFFER = 0xCC08, + CMD_ACK = 0xFF01, + CMD_TIMEOUT = 0xFF02, + CMD_NACK = 0xFF03 +} UsbCommands; + +struct _FuLogitechBulkcontrollerDevice { + FuUsbDevice parent_instance; + guint sync_ep[EP_LAST]; + guint update_ep[EP_LAST]; + guint sync_iface; + guint update_iface; + FuLogitechBulkcontrollerDeviceStatus status; + FuLogitechBulkcontrollerDeviceUpdateState update_status; + guint update_progress; /* percentage value */ + gboolean is_sync_transfer_in_progress; +}; + +typedef struct { + FuLogitechBulkcontrollerDevice *self; /* no-ref */ + GByteArray *device_response; + GByteArray *buf_pkt; + GMainLoop *loop; + GError *error; +} FuLogitechBulkcontrollerHelper; + +G_DEFINE_TYPE(FuLogitechBulkcontrollerDevice, fu_logitech_bulkcontroller_device, FU_TYPE_USB_DEVICE) + +static void +fu_logitech_bulkcontroller_helper_free(FuLogitechBulkcontrollerHelper *helper) +{ + if (helper->error != NULL) + g_error_free(helper->error); + g_byte_array_unref(helper->buf_pkt); + g_byte_array_unref(helper->device_response); + g_main_loop_unref(helper->loop); + g_slice_free(FuLogitechBulkcontrollerHelper, helper); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuLogitechBulkcontrollerHelper, + fu_logitech_bulkcontroller_helper_free) +#pragma clang diagnostic pop + +static void +fu_logitech_bulkcontroller_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + fu_string_append_kx(str, idt, "SyncIface", self->sync_iface); + fu_string_append_kx(str, idt, "UpdateIface", self->update_iface); + fu_string_append(str, + idt, + "Status", + fu_logitech_bulkcontroller_device_status_to_string(self->status)); + fu_string_append( + str, + idt, + "UpdateState", + fu_logitech_bulkcontroller_device_update_state_to_string(self->update_status)); +} + +static gboolean +fu_logitech_bulkcontroller_device_probe(FuDevice *device, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autoptr(GPtrArray) intfs = NULL; + + intfs = g_usb_device_get_interfaces(fu_usb_device_get_dev(FU_USB_DEVICE(self)), error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == G_USB_DEVICE_CLASS_VENDOR_SPECIFIC && + g_usb_interface_get_protocol(intf) == 0x1) { + if (g_usb_interface_get_subclass(intf) == SYNC_INTERFACE_SUBPROTOCOL_ID) { + g_autoptr(GPtrArray) endpoints = + g_usb_interface_get_endpoints(intf); + self->sync_iface = g_usb_interface_get_number(intf); + if (endpoints == NULL) + continue; + for (guint j = 0; j < endpoints->len; j++) { + GUsbEndpoint *ep = g_ptr_array_index(endpoints, j); + if (j == EP_OUT) + self->sync_ep[EP_OUT] = + g_usb_endpoint_get_address(ep); + else + self->sync_ep[EP_IN] = + g_usb_endpoint_get_address(ep); + } + } else if (g_usb_interface_get_subclass(intf) == + UPD_INTERFACE_SUBPROTOCOL_ID) { + g_autoptr(GPtrArray) endpoints = + g_usb_interface_get_endpoints(intf); + self->update_iface = g_usb_interface_get_number(intf); + if (endpoints == NULL) + continue; + for (guint j = 0; j < endpoints->len; j++) { + GUsbEndpoint *ep = g_ptr_array_index(endpoints, j); + if (j == EP_OUT) + self->update_ep[EP_OUT] = + g_usb_endpoint_get_address(ep); + else + self->update_ep[EP_IN] = + g_usb_endpoint_get_address(ep); + } + } + } + } + fu_usb_device_add_interface(FU_USB_DEVICE(self), self->update_iface); + fu_usb_device_add_interface(FU_USB_DEVICE(self), self->sync_iface); + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} + +static gboolean +fu_logitech_bulkcontroller_device_send(FuLogitechBulkcontrollerDevice *self, + GByteArray *buf, + gint interface_id, + GError **error) +{ + gsize transferred = 0; + gint ep; + GCancellable *cancellable = NULL; + g_return_val_if_fail(buf != NULL, FALSE); + + if (interface_id == BULK_INTERFACE_SYNC) { + ep = self->sync_ep[EP_OUT]; + } else if (interface_id == BULK_INTERFACE_UPD) { + ep = self->update_ep[EP_OUT]; + } else { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "interface is invalid"); + return FALSE; + } + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + ep, + (guint8 *)buf->data, + buf->len, + &transferred, + BULK_TRANSFER_TIMEOUT, + cancellable, + error)) { + g_prefix_error(error, "failed to send using bulk transfer: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_recv(FuLogitechBulkcontrollerDevice *self, + GByteArray *buf, + gint interface_id, + guint timeout, + GError **error) +{ + gsize received_length = 0; + gint ep; + g_return_val_if_fail(buf != NULL, FALSE); + + if (interface_id == BULK_INTERFACE_SYNC) { + ep = self->sync_ep[EP_IN]; + } else if (interface_id == BULK_INTERFACE_UPD) { + ep = self->update_ep[EP_IN]; + } else { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "interface is invalid"); + return FALSE; + } + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + ep, + buf->data, + buf->len, + &received_length, + timeout, + NULL, + error)) { + g_prefix_error(error, "failed to receive using bulk transfer: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_send_upd_cmd(FuLogitechBulkcontrollerDevice *self, + guint32 cmd, + GByteArray *buf, + GError **error) +{ + guint32 cmd_tmp = 0x0; + guint timeout = BULK_TRANSFER_TIMEOUT; + g_autoptr(GByteArray) buf_pkt = g_byte_array_new(); + g_autoptr(GByteArray) buf_ack = g_byte_array_new(); + + fu_byte_array_append_uint32(buf_pkt, cmd, G_LITTLE_ENDIAN); /* Type(T) : Command type */ + fu_byte_array_append_uint32(buf_pkt, + buf != NULL ? buf->len : 0, + G_LITTLE_ENDIAN); /*Length(L) : Length of payload */ + if (buf != NULL) { + g_byte_array_append(buf_pkt, + buf->data, + buf->len); /* Value(V) : Actual payload data */ + } + if (!fu_logitech_bulkcontroller_device_send(self, buf_pkt, BULK_INTERFACE_UPD, error)) + return FALSE; + + /* receiving INIT ACK */ + fu_byte_array_set_size(buf_ack, MAX_DATA_SIZE, 0x00); + + /* extending the bulk transfer timeout value, as android device takes some time to + calculate Hash and respond */ + if (CMD_END_TRANSFER == cmd) + timeout = HASH_TIMEOUT; + + if (!fu_logitech_bulkcontroller_device_recv(self, + buf_ack, + BULK_INTERFACE_UPD, + timeout, + error)) + return FALSE; + + if (!fu_memread_uint32_safe(buf_ack->data, + buf_ack->len, + COMMAND_OFFSET, + &cmd_tmp, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cmd_tmp != CMD_ACK) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "not CMD_ACK, got %x", cmd); + return FALSE; + } + if (!fu_memread_uint32_safe(buf_ack->data, + buf_ack->len, + UPD_PACKET_HEADER_SIZE, + &cmd_tmp, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cmd_tmp != cmd) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid upd message received, expected %x, got %x", + cmd, + cmd_tmp); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_send_sync_cmd(FuLogitechBulkcontrollerDevice *self, + guint32 cmd, + GByteArray *buf, + GError **error) +{ + g_autoptr(GByteArray) buf_pkt = g_byte_array_new(); + + fu_byte_array_append_uint32(buf_pkt, cmd, G_LITTLE_ENDIAN); /* Type(T) : Command type */ + fu_byte_array_append_uint32(buf_pkt, + buf != NULL ? buf->len : 0, + G_LITTLE_ENDIAN); /*Length(L) : Length of payload */ + fu_byte_array_append_uint32(buf_pkt, + g_random_int_range(0, G_MAXUINT16), + G_LITTLE_ENDIAN); /*Sequence(S) : Sequence ID of the data */ + if (buf != NULL) { + g_byte_array_append(buf_pkt, + buf->data, + buf->len); /* Value(V) : Actual payload data */ + } + if (!fu_logitech_bulkcontroller_device_send(self, buf_pkt, BULK_INTERFACE_SYNC, error)) + return FALSE; + return TRUE; +} + +static gchar * +fu_logitech_bulkcontroller_device_compute_hash(GBytes *data) +{ + guint8 md5buf[HASH_VALUE_SIZE] = {0}; + gsize data_len = sizeof(md5buf); + GChecksum *checksum = g_checksum_new(G_CHECKSUM_MD5); + g_checksum_update(checksum, g_bytes_get_data(data, NULL), g_bytes_get_size(data)); + g_checksum_get_digest(checksum, (guint8 *)&md5buf, &data_len); + return g_base64_encode(md5buf, sizeof(md5buf)); +} + +static gboolean +fu_logitech_bulkcontroller_device_json_parser(FuDevice *device, + GByteArray *decoded_pkt, + GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + JsonArray *json_devices; + JsonNode *json_root; + JsonObject *json_device; + JsonObject *json_object; + JsonObject *json_payload; + g_autoptr(JsonParser) json_parser = json_parser_new(); + + /* parse JSON reply */ + if (!json_parser_load_from_data(json_parser, + (const gchar *)decoded_pkt->data, + decoded_pkt->len, + error)) { + g_prefix_error(error, "failed to parse json data: "); + return FALSE; + } + json_root = json_parser_get_root(json_parser); + if (json_root == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON root"); + return FALSE; + } + json_object = json_node_get_object(json_root); + json_payload = json_object_get_object_member(json_object, "payload"); + if (json_payload == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON payload"); + return FALSE; + } + json_devices = json_object_get_array_member(json_payload, "devices"); + if (json_devices == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON devices"); + return FALSE; + } + json_device = json_array_get_object_element(json_devices, 0); + if (json_device == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON device"); + return FALSE; + } + if (json_object_has_member(json_device, "name")) + fu_device_set_name(device, json_object_get_string_member(json_device, "name")); + if (json_object_has_member(json_device, "sw")) + fu_device_set_version(device, json_object_get_string_member(json_device, "sw")); + if (json_object_has_member(json_device, "type")) + fu_device_add_instance_id(device, + json_object_get_string_member(json_device, "type")); + if (json_object_has_member(json_device, "status")) + self->status = json_object_get_int_member(json_device, "status"); + if (json_object_has_member(json_device, "updateStatus")) + self->update_status = json_object_get_int_member(json_device, "updateStatus"); + /* updateProgress only available while firmware upgrade is going on */ + if (json_object_has_member(json_device, "updateProgress")) + self->update_progress = json_object_get_int_member(json_device, "updateProgress"); + + return TRUE; +} + +/* async callback handler : read data from sync endpoint continuously */ +static void +fu_logitech_bulkcontroller_device_sync_cb(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + FuLogitechBulkcontrollerHelper *helper = (FuLogitechBulkcontrollerHelper *)user_data; + FuLogitechBulkcontrollerDevice *self = helper->self; + guint32 cmd_tmp = 0x0; + guint64 cmd_tmp_64 = 0x0; + guint64 cmd_res = 0x0; + guint32 response_length = 0; + guint8 ack_payload[SYNC_ACK_PAYLOAD_LENGTH] = {0}; + g_autoptr(GByteArray) buf_ack = g_byte_array_new(); + g_autoptr(GError) error_local = NULL; + + if (!g_usb_device_bulk_transfer_finish(G_USB_DEVICE(source_object), res, &error_local)) { + g_propagate_prefixed_error(&helper->error, + g_steal_pointer(&error_local), + "failed to finish using bulk transfer: "); + g_main_loop_quit(helper->loop); + return; + } + if (!fu_memread_uint32_safe(helper->buf_pkt->data, + helper->buf_pkt->len, + COMMAND_OFFSET, + &cmd_tmp, + G_LITTLE_ENDIAN, + &helper->error)) { + g_prefix_error(&helper->error, "failed to retrieve payload command: "); + g_main_loop_quit(helper->loop); + return; + } + if (!fu_memread_uint32_safe(helper->buf_pkt->data, + helper->buf_pkt->len, + LENGTH_OFFSET, + &response_length, + G_LITTLE_ENDIAN, + &helper->error)) { + g_prefix_error(&helper->error, "failed to retrieve payload length: "); + g_main_loop_quit(helper->loop); + return; + } + if (!fu_memread_uint64_safe(helper->buf_pkt->data, + helper->buf_pkt->len, + SYNC_PACKET_HEADER_SIZE, + &cmd_tmp_64, + G_LITTLE_ENDIAN, + &helper->error)) { + g_prefix_error(&helper->error, "failed to retrieve payload data: "); + g_main_loop_quit(helper->loop); + return; + } + if (!fu_memcpy_safe((guint8 *)ack_payload, + sizeof(ack_payload), + 0x0, + (guint8 *)&cmd_tmp_64, + sizeof(cmd_tmp_64), + 0x0, + SYNC_ACK_PAYLOAD_LENGTH, + &helper->error)) { + g_prefix_error(&helper->error, "failed to copy payload data: "); + g_main_loop_quit(helper->loop); + return; + } + + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) + g_debug("Received 0x%x message on sync interface", cmd_tmp); + switch (cmd_tmp) { + case CMD_ACK: + if (!fu_strtoull((const char *)ack_payload, + &cmd_res, + 0, + G_MAXUINT32, + &error_local)) { + g_propagate_prefixed_error(&helper->error, + g_steal_pointer(&error_local), + "failed to parse ack payload cmd: "); + g_main_loop_quit(helper->loop); + return; + } + if (cmd_res == CMD_BUFFER_WRITE) { + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_UNINIT_BUFFER, + NULL, + &helper->error)) { + g_prefix_error(&helper->error, + "failed to send %d while processing %d: ", + CMD_UNINIT_BUFFER, + CMD_BUFFER_WRITE); + g_main_loop_quit(helper->loop); + return; + } + } else if (cmd_res != CMD_UNINIT_BUFFER) { + g_set_error(&helper->error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid message received: expected %s, but received %d: ", + (const gchar *)ack_payload, + CMD_UNINIT_BUFFER); + g_main_loop_quit(helper->loop); + return; + } + break; + case CMD_BUFFER_READ: + g_byte_array_append(helper->device_response, + helper->buf_pkt->data + SYNC_PACKET_HEADER_SIZE, + response_length); + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_autofree gchar *strsafe = + fu_strsafe((const gchar *)helper->device_response->data, + helper->device_response->len); + g_debug("Received data on sync interface. length: %u, buffer: %s", + helper->device_response->len, + strsafe); + } + fu_byte_array_append_uint32(buf_ack, cmd_tmp, G_LITTLE_ENDIAN); + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_ACK, + buf_ack, + &helper->error)) { + g_prefix_error(&helper->error, + "failed to send %d while processing %d: ", + CMD_ACK, + CMD_BUFFER_READ); + g_main_loop_quit(helper->loop); + return; + } + break; + case CMD_UNINIT_BUFFER: + fu_byte_array_append_uint32(buf_ack, cmd_tmp, G_LITTLE_ENDIAN); + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_ACK, + buf_ack, + &helper->error)) { + g_prefix_error(&helper->error, + "failed to send %d while processing %d: ", + CMD_ACK, + CMD_UNINIT_BUFFER); + g_main_loop_quit(helper->loop); + return; + } + self->is_sync_transfer_in_progress = FALSE; + break; + default: + break; + } + + g_main_loop_quit(helper->loop); + return; +} + +static gboolean +fu_logitech_bulkcontroller_device_startlistening_sync(FuLogitechBulkcontrollerDevice *self, + GByteArray *device_response, + GError **error) +{ + gint max_retry = MAX_RETRIES * 2; + self->is_sync_transfer_in_progress = TRUE; + + while (self->is_sync_transfer_in_progress) { + g_autoptr(FuLogitechBulkcontrollerHelper) helper = + g_slice_new0(FuLogitechBulkcontrollerHelper); + max_retry--; + helper->self = self; + helper->buf_pkt = g_byte_array_new(); + helper->loop = g_main_loop_new(NULL, FALSE); + helper->device_response = g_byte_array_ref(device_response); + + fu_byte_array_set_size(helper->buf_pkt, MAX_DATA_SIZE, 0x00); + g_usb_device_bulk_transfer_async(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + self->sync_ep[EP_IN], + helper->buf_pkt->data, + helper->buf_pkt->len, + BULK_TRANSFER_TIMEOUT, + NULL, /* cancellable */ + fu_logitech_bulkcontroller_device_sync_cb, + helper); + g_main_loop_run(helper->loop); + + /* handle error scenario, e.g. device no longer responding */ + if (max_retry == 0) { + self->is_sync_transfer_in_progress = FALSE; + if (helper->error != NULL) { + g_propagate_prefixed_error(error, + g_steal_pointer(&helper->error), + "failed after %i retries: ", + MAX_RETRIES); + } else { + g_set_error(&helper->error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed after %i retries: ", + MAX_RETRIES); + } + return FALSE; + } + + /* just show to console */ + if (helper->error != NULL) + g_warning("async error %s", helper->error->message); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_get_data(FuDevice *device, gboolean send_req, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autoptr(GByteArray) decoded_pkt = g_byte_array_new(); + g_autoptr(GByteArray) device_response = g_byte_array_new(); + FuLogitechBulkcontrollerProtoId proto_id = kProtoId_UnknownId; + + /* sending GetDeviceInfoRequest. Device reports quite a few matrix, including status, + * progress etc + * Two ways to get data from device: + * 1. Listen for the data broadcasted by device, while firmware upgrade is going on + * 2. Make explicit request to the device. Used when data is needed before/after firmware + * upgrade + */ + if (send_req) { + g_autoptr(GByteArray) device_request = g_byte_array_new(); + device_request = proto_manager_generate_get_device_info_request(); + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_BUFFER_WRITE, + device_request, + error)) { + g_prefix_error( + error, + "failed to send write buffer packet for device info request: "); + return FALSE; + } + } + if (!fu_logitech_bulkcontroller_device_startlistening_sync(self, device_response, error)) { + g_prefix_error(error, "failed to receive data packet for device info request: "); + return FALSE; + } + /* handle error scenario, e.g. CMD_UNINIT_BUFFER arrived before CMD_BUFFER_READ */ + if (device_response->len == 0) { + g_prefix_error(error, + "failed to receive expected packet for device info request: "); + return FALSE; + } + decoded_pkt = proto_manager_decode_message(device_response->data, + device_response->len, + &proto_id, + error); + if (decoded_pkt == NULL) { + g_prefix_error(error, "failed to unpack packet for device info request: "); + return FALSE; + } + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_autofree gchar *strsafe = + fu_strsafe((const gchar *)decoded_pkt->data, decoded_pkt->len); + g_debug("Received device response: id: %u, length %u, data: %s", + proto_id, + device_response->len, + strsafe); + } + if (proto_id != kProtoId_GetDeviceInfoResponse && proto_id != kProtoId_KongEvent) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "incorrect response for device info request"); + return FALSE; + } + if (!fu_logitech_bulkcontroller_device_json_parser(device, decoded_pkt, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_send_upd_init_cmd_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + return fu_logitech_bulkcontroller_device_send_upd_cmd(self, CMD_INIT, NULL, error); +} + +static gboolean +fu_logitech_bulkcontroller_device_write_fw(FuLogitechBulkcontrollerDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, PAYLOAD_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) data_pkt = g_byte_array_new(); + g_byte_array_append(data_pkt, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, + CMD_DATA_TRANSFER, + data_pkt, + error)) { + g_prefix_error(error, "failed to send data packet 0x%x: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + gboolean query_device = FALSE; /* query or listen for events, periodically broadcasted */ + gint max_wait = MAX_WAIT_COUNT; /* if firmware upgrade is taking forever to finish */ + guint max_no_response_count = MAX_RETRIES; /* device doesn't respond */ + guint no_response_count = 0; + g_autofree gchar *base64hash = NULL; + g_autoptr(GByteArray) end_pkt = g_byte_array_new(); + g_autoptr(GByteArray) start_pkt = g_byte_array_new(); + g_autoptr(GBytes) fw = NULL; + g_autofree gchar *old_firmware_version = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 48, "device-write-blocks"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "end-transfer"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "uninit"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 49, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* sending INIT. Retry if device is not in IDLE state to receive the file */ + if (!fu_device_retry(device, + fu_logitech_bulkcontroller_device_send_upd_init_cmd_cb, + MAX_RETRIES, + NULL, + error)) { + g_prefix_error(error, + "failed to write init transfer packet: please reboot the device: "); + return FALSE; + } + + /* transfer sent */ + fu_byte_array_append_uint64(start_pkt, g_bytes_get_size(fw), G_LITTLE_ENDIAN); + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, + CMD_START_TRANSFER, + start_pkt, + error)) { + g_prefix_error(error, "failed to write start transfer packet: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* push each block to device */ + if (!fu_logitech_bulkcontroller_device_write_fw(self, + fw, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* sending end transfer */ + base64hash = fu_logitech_bulkcontroller_device_compute_hash(fw); + fu_byte_array_append_uint32(end_pkt, 1, G_LITTLE_ENDIAN); /* update */ + fu_byte_array_append_uint32(end_pkt, 0, G_LITTLE_ENDIAN); /* force */ + fu_byte_array_append_uint32(end_pkt, MD5, G_LITTLE_ENDIAN); /* checksum type */ + g_byte_array_append(end_pkt, (const guint8 *)base64hash, strlen(base64hash)); + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, + CMD_END_TRANSFER, + end_pkt, + error)) { + g_prefix_error(error, "failed to write end transfer transfer packet: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* send uninit */ + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, CMD_UNINIT, NULL, error)) { + g_prefix_error(error, "failed to write finish transfer packet: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* + * image file pushed. Device validates and uploads new image on inactive partition. + * Restart sync cb, to get the update progress + * Normally status changes as follows: + * While image being pushed: kUpdateStateCurrent->kUpdateStateDownloading (~5minutes) + * After image push is complete: kUpdateStateDownloading->kUpdateStateReady + * Validating image: kUpdateStateReady->kUpdateStateStarting + * Uploading image: kUpdateStateStarting->kUpdateStateUpdating + * Upload finished: kUpdateStateUpdating->kUpdateStateCurrent (~5minutes) + * After upload is finished, device reboots itself + */ + g_usleep(G_TIME_SPAN_SECOND); + /* save the current firmware version for troubleshooting purpose */ + old_firmware_version = g_strdup(fu_device_get_version(device)); + do { + g_autoptr(GError) error_local = NULL; + /* skip explicit device query as long as device is publishing update events + * (kProtoId_KongEvent) */ + if (self->update_progress == 100) { + query_device = TRUE; + } else { + query_device = (no_response_count == 0) ? FALSE : TRUE; + } + g_usleep(500 * G_TIME_SPAN_MILLISECOND); + + /* lost Success/Failure message, device rebooting */ + if (no_response_count == max_no_response_count) { + g_debug("device not responding, rebooting..."); + break; + } + + /* update device obj with latest info from the device */ + if (!fu_logitech_bulkcontroller_device_get_data(device, + query_device, + &error_local)) { + no_response_count++; + g_debug("no response for device info request %u", no_response_count); + fu_progress_reset(fu_progress_get_child(progress)); + continue; + } + + /* device responsive, no error and not rebooting yet */ + no_response_count = 0; + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_debug("firmware update status: %s. progress: %u", + fu_logitech_bulkcontroller_device_update_state_to_string( + self->update_status), + self->update_progress); + } + + /* existing device image version is same as newly pushed image */ + if (self->update_status == kUpdateStateError) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware upgrade failed"); + return FALSE; + } + if (self->update_status == kUpdateStateCurrent) { + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_debug("new firmware version: %s, old firmware version: %s, " + "rebooting...", + fu_device_get_version(device), + old_firmware_version); + } + break; + } + if (self->update_progress == 100) { + /* wait for state change: kUpdateStateUpdating->kUpdateStateCurrent + * device no longer broadcast fu related events, need to query device + * explicitly now + */ + g_usleep(G_USEC_PER_SEC); + fu_progress_reset(fu_progress_get_child(progress)); + continue; + } + + /* only update the child if the percentage is bigger -- which means the progressbar + * may stall, but will never go backwards */ + if (self->update_progress > + fu_progress_get_percentage(fu_progress_get_child(progress))) { + fu_progress_set_percentage(fu_progress_get_child(progress), + self->update_progress); + } + } while (max_wait--); + if (max_wait <= 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware upgrade timeout: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_get_handshake_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + FuLogitechBulkcontrollerProtoId proto_id = kProtoId_UnknownId; + g_autoptr(GByteArray) decoded_pkt = g_byte_array_new(); + g_autoptr(GByteArray) device_response = g_byte_array_new(); + g_autoptr(GError) error_local = NULL; + + if (!fu_logitech_bulkcontroller_device_startlistening_sync(self, + device_response, + &error_local)) { + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) + g_debug("failed to receive data packet for handshake request"); + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to receive data packet for handshake request"); + return FALSE; + } + + /* handle error scenario, e.g. CMD_UNINIT_BUFFER arrived before CMD_BUFFER_READ */ + if (device_response->len == 0) { + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) + g_debug("failed to receive expected packet for handshake request"); + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to receive expected packet for handshake request"); + return FALSE; + } + + decoded_pkt = proto_manager_decode_message(device_response->data, + device_response->len, + &proto_id, + &error_local); + if (decoded_pkt == NULL) { + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) + g_debug("failed to unpack packet for handshake request"); + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to unpack packet for handshake request"); + return FALSE; + } + + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_autofree gchar *strsafe = + fu_strsafe((const gchar *)decoded_pkt->data, decoded_pkt->len); + g_debug("Received initialization response: id: %u, length %u, data: %s", + proto_id, + device_response->len, + strsafe); + } + + /* skip optional initialization events -- not an error if these events are missed */ + if (proto_id != kProtoId_HandshakeEvent) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid initialization message received: %u", + proto_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_set_time(FuDevice *device, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autoptr(GByteArray) device_request = g_byte_array_new(); + g_autoptr(GByteArray) decoded_pkt = g_byte_array_new(); + g_autoptr(GByteArray) device_response = g_byte_array_new(); + FuLogitechBulkcontrollerProtoId proto_id = kProtoId_UnknownId; + + /* send SetDeviceTimeRequest to sync device clock with host */ + device_request = proto_manager_generate_set_device_time_request(); + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_BUFFER_WRITE, + device_request, + error)) { + g_prefix_error(error, + "failed to send write buffer packet for set device time request: "); + return FALSE; + } + if (!fu_logitech_bulkcontroller_device_startlistening_sync(self, device_response, error)) { + g_prefix_error(error, + "failed to receive data packet for set device time request: "); + return FALSE; + } + /* handle error scenario, e.g. CMD_UNINIT_BUFFER arrived before CMD_BUFFER_READ */ + if (device_response->len == 0) { + g_prefix_error(error, + "failed to receive expected packet for set device time request: "); + return FALSE; + } + decoded_pkt = proto_manager_decode_message(device_response->data, + device_response->len, + &proto_id, + error); + if (decoded_pkt == NULL) { + g_prefix_error(error, "failed to unpack packet for set device time request: "); + return FALSE; + } + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_autofree gchar *strsafe = + fu_strsafe((const gchar *)decoded_pkt->data, decoded_pkt->len); + g_debug("Received device response while processing set device time request: id: " + "%u, length %u, data: %s", + proto_id, + device_response->len, + strsafe); + } + if (proto_id != kProtoId_Ack) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "incorrect response for set device time request"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_setup(FuDevice *device, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autoptr(GByteArray) device_request = g_byte_array_new(); + g_autoptr(GByteArray) decoded_pkt = g_byte_array_new(); + g_autoptr(GByteArray) device_response = g_byte_array_new(); + FuLogitechBulkcontrollerProtoId proto_id = kProtoId_UnknownId; + guint32 success = 0; + guint32 error_code = 0; + g_autoptr(GError) error_local = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_logitech_bulkcontroller_device_parent_class)->setup(device, error)) + return FALSE; + + /* check for initialization events generated by the device + * no error check needed here, possibly missed */ + if (!fu_device_retry(device, + fu_logitech_bulkcontroller_device_get_handshake_cb, + MAX_HANDSHAKE_RETRIES, + NULL, + &error_local)) { + g_warning("failed to receive initialization events: %s", error_local->message); + } + + /* + * device supports USB_Device mode, Appliance mode and BYOD mode. + * Only USB_Device mode is supported here. + * Ensure it is running in USB_Device mode + * Response has two data: Request succeeded or failed, and error code in case of failure + */ + device_request = proto_manager_generate_transition_to_device_mode_request(); + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_BUFFER_WRITE, + device_request, + error)) { + g_prefix_error(error, + "failed to send buffer write packet for transition mode request: "); + return FALSE; + } + if (!fu_logitech_bulkcontroller_device_startlistening_sync(self, device_response, error)) { + g_prefix_error(error, + "failed to receive data packet for transition mode request: "); + return FALSE; + } + + /* handle error scenario, e.g. CMD_UNINIT_BUFFER arrived before CMD_BUFFER_READ */ + if (device_response->len == 0) { + g_prefix_error(error, + "failed to receive expected packet for transition mode request: "); + return FALSE; + } + decoded_pkt = proto_manager_decode_message(device_response->data, + device_response->len, + &proto_id, + error); + if (decoded_pkt == NULL) { + g_prefix_error(error, "failed to unpack packet for transition mode request: "); + return FALSE; + } + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_autofree gchar *strsafe = + fu_strsafe((const gchar *)decoded_pkt->data, decoded_pkt->len); + g_debug("Received transition mode response: id: %u, length %u, data: %s", + proto_id, + device_response->len, + strsafe); + } + if (proto_id != kProtoId_TransitionToDeviceModeResponse) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "incorrect response for transition mode request"); + return FALSE; + } + if (!fu_memread_uint32_safe(decoded_pkt->data, + decoded_pkt->len, + COMMAND_OFFSET, + &success, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to retrieve result for transition mode request: "); + return FALSE; + } + if (!fu_memread_uint32_safe(decoded_pkt->data, + decoded_pkt->len, + LENGTH_OFFSET, + &error_code, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, + "failed to retrieve error code for transition mode request: "); + return FALSE; + } + if (g_getenv("FWUPD_LOGITECH_BULKCONTROLLER_VERBOSE") != NULL) { + g_debug("Received transition mode response. Success: %u, Error: %u", + success, + error_code); + } + if (!success) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "transition mode request failed. error: %u", + error_code); + return FALSE; + } + + /* set device time */ + if (!fu_logitech_bulkcontroller_device_set_time(device, error)) + return FALSE; + + /* load current device data */ + if (!fu_logitech_bulkcontroller_device_get_data(device, TRUE, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_logitech_bulkcontroller_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_logitech_bulkcontroller_device_init(FuLogitechBulkcontrollerDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.vc.proto"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_retry_set_delay(FU_DEVICE(self), 1000); + fu_device_set_remove_delay(FU_DEVICE(self), 100000); /* >1 min to finish init */ +} + +static void +fu_logitech_bulkcontroller_device_class_init(FuLogitechBulkcontrollerDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_logitech_bulkcontroller_device_to_string; + klass_device->write_firmware = fu_logitech_bulkcontroller_device_write_firmware; + klass_device->probe = fu_logitech_bulkcontroller_device_probe; + klass_device->setup = fu_logitech_bulkcontroller_device_setup; + klass_device->set_progress = fu_logitech_bulkcontroller_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.h b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.h new file mode 100644 index 0000000000000000000000000000000000000000..2bbce5e34309b66717071a91fcd2eb2484f005eb --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_LOGITECH_BULKCONTROLLER_DEVICE (fu_logitech_bulkcontroller_device_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechBulkcontrollerDevice, + fu_logitech_bulkcontroller_device, + FU, + LOGITECH_BULKCONTROLLER_DEVICE, + FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..804a875d5a6504dad0afafdd9b8fb2f9b3a69d3a --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-bulkcontroller-device.h" +#include "fu-logitech-bulkcontroller-plugin.h" + +struct _FuLogitechBulkcontrollerPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuLogitechBulkcontrollerPlugin, fu_logitech_bulkcontroller_plugin, FU_TYPE_PLUGIN) + +static void +fu_logitech_bulkcontroller_plugin_init(FuLogitechBulkcontrollerPlugin *self) +{ +} + +static void +fu_logitech_bulkcontroller_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_BULKCONTROLLER_DEVICE); +} + +static void +fu_logitech_bulkcontroller_plugin_class_init(FuLogitechBulkcontrollerPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_logitech_bulkcontroller_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.h b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..1d8a66f61c97ce85f5c5a79cf54854827067f948 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLogitechBulkcontrollerPlugin, + fu_logitech_bulkcontroller_plugin, + FU, + LOGITECH_BULKCONTROLLER_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk b/fwupd-1.8.6/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk new file mode 100644 index 0000000000000000000000000000000000000000..4eae110ce58df3dc1f4b0c8e1740dd02d2cb72d2 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk @@ -0,0 +1,10 @@ +# TODO: revisit InstallDuration + +[USB\VID_046D&PID_089B] +Plugin = logitech_bulkcontroller +InstallDuration = 1500 + +[USB\VID_046D&PID_08D3] +Plugin = logitech_bulkcontroller +InstallDuration = 1500 +Flags = is-mini diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/meson.build b/fwupd-1.8.6/plugins/logitech-bulkcontroller/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1adec88d3af18f4f74eea064604a9a3ba52cdf44 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/meson.build @@ -0,0 +1,20 @@ +if gusb.found() and protobufc.found() and protoc.found() + +cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechBulkController"'] + +plugin_quirks += files('logitech-bulkcontroller.quirk') + +subdir('proto') +plugin_builtins += static_library('fu_plugin_logitech_bulkcontroller', + sources: [ + generated, + 'fu-logitech-bulkcontroller-common.c', + 'fu-logitech-bulkcontroller-device.c', + 'fu-logitech-bulkcontroller-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/antiflicker.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/antiflicker.proto new file mode 100644 index 0000000000000000000000000000000000000000..5dbc2bc296b8dc6cfa42827944ec398ecab54e27 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/antiflicker.proto @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_common.proto"; + +/** + * This message data structure holds information about the + * current AntiFlicker configuration. + * + */ +message AntiFlickerConfiguration +{ + enum Mode { + NTSC_60HZ = 0; + PAL_50HZ = 1; + } + + Mode mode = 1; +} + +message SetAntiFlickerConfigurationRequest +{ + AntiFlickerConfiguration.Mode mode = 1; +} + +message SetAntiFlickerConfigurationResponse +{ + bool success = 1; + + repeated Error errors = 2; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/ble_cfg.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/ble_cfg.proto new file mode 100644 index 0000000000000000000000000000000000000000..f1306847db6032d9a859e363f204456a60ce7663 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/ble_cfg.proto @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_common.proto"; + +message SetBLECfgRequest +{ + /** + * (REQUIRED) If true, BLE is enabled and active otherwise disabled + */ + bool BLE_ON = 1; +} + +message SetBLECfgResponse +{ + bool success = 1; + repeated Error errors = 2; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/crash_info.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/crash_info.proto new file mode 100644 index 0000000000000000000000000000000000000000..1598468530bc2a585943d3a650bd82512ca5bb97 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/crash_info.proto @@ -0,0 +1,285 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Kong as an Android device can accumulate + * crash debug information during its operation. + * When Kong is running in device mode, those + * crash dump files need to be copied over to + * PC and uploaded to S3. + * Note, if Kong is running in host mode, uploaded + * files, and then moved to device mode, will it + * copy the same files over? + * + * This message requests that crash dump files be + * copied over to PC + * + * EXPECTED RESPONSE + * SendCrashDumpResponse + * + */ +message SendCrashDumpRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Crash dump information. Most of these + * are supplied by the crash analytics service, so lets + * pass this information along. + */ +message CrashDumpInfo +{ + /** + * the filename + */ + string file_name = 1; + + /** + * the serial number + */ + string device_id = 2; + + /** + * the software version + */ + string software_version = 3; + + /** + * the file size + */ + uint64 file_size = 4; + + /** + * timestamp + */ + uint64 timestamp = 5; + + /** + * md5 for file + */ + string md5 = 6; + + /** + * the device type . Kong|Diddy + */ + string device_type = 7; + + /** + * the device mode. Hosted|Appliance + */ + string device_mode = 8; + + /** + * the report type. BugReport|EventLog,Diagnostics + */ + string report_type = 9; + + /** + * the content type. application/zip | text/plain | application/json + */ + string content_type = 10; +} + +/** + * Response which contains the crash dump file name + * information and bool value to indicate will send + * file + */ +message SendCrashDumpResponse +{ + /** + * (OPTIONAL) + * If crash dump exists, this variable + * contains the file name of crash dump + * that will be copied over. + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * bool value to indicate will send file + * true if sending file over. + * false if no file to send. + * If true, caller will look at CrashDumpInfo + */ + bool will_send_file = 2; + + /** + * (OPTIONAL) + * Crash dump info + */ + CrashDumpInfo crash_dump_info = 3; +} + +message SendCrashDumpRequestv2 +{ + /** + * The attestation challenge. + * (REQUIRED) + */ + string challenge = 1; + + /** + * Time to live + * (REQUIRED) + */ + int32 ttl = 2; +} + +/** + * Response which contains the crash dump file name + * information, bool value to indicate will send + * file, body of the request and signature + */ +message SendCrashDumpResponsev2 +{ + /** + * (OPTIONAL) + * If crash dump exists, this variable + * contains the file name of crash dump + * that will be copied over. + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * bool value to indicate will send file + * true if sending file over. + * false if no file to send. + * If true, caller will look at CrashDumpInfo + */ + bool will_send_file = 2; + + /** + * (OPTIONAL) + * The get upload url body. This is a json string + */ + string body = 3; + + /** + * (OPTIONAL) + * The get upload url body signature. + */ + string signature = 4; +} + +/** + * This is event sent from PC or Kong to indicate + * Success + */ +message SendCrashDumpEvent +{ + /** + * (REQUIRED) + * Contains the file name of crash dump + * that is being sent or in process of being + * received + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * Transfer state. + * true indicates file was received without errors and bug report file was + * uploaded false means an error occurred + */ + bool success = 2; +} + +/** + * Place holder for Android requesting that a crash dump copy + * get initiated from PC side + */ +message CrashDumpAvailableEvent +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Ask device to generate a bug report. This could be + * for gathering logcat, system logs, etc. + * Similar to SendCrashDumpRequestv2, but bug report generation is on + * demand. + * EXPECTED RESPONSE: + * GenerateCrashDumpResponse + * It should follow the same flow as described here + * https://docs.google.com/document/d/1D5nx1nenDu9ucZbYPXlNNxFEN1tx3W7k044mvi74x28/edit#heading=h.a9wyfbpb2282 + */ +message GenerateCrashDumpRequest +{ + /** + * The attestation challenge. + * (REQUIRED) + */ + string challenge = 1; + + /** + * Time to live + * (REQUIRED) + */ + int32 ttl = 2; + + /** + * The note to include in the bug report. This could be empty. + * (OPTIONAL) + */ + string note = 3; +} + +/** + * Response which contains the + * crash dump file name information, + * bool value to indicate will send file, + * body of the request and signature. + * Similar to SendCrashDumpResponsev2, but bug report generation is on + * demand. + * It should follow the same flow as described here + * https://docs.google.com/document/d/1D5nx1nenDu9ucZbYPXlNNxFEN1tx3W7k044mvi74x28/edit#heading=h.a9wyfbpb2282 + */ +message GenerateCrashDumpResponse +{ + /** + * (OPTIONAL) + * If crash dump exists, this variable + * contains the file name of crash dump + * that will be copied over. + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * bool value to indicate will send file + * true if sending file over. + * false if no file to send. + */ + bool will_send_file = 2; + + /** + * (OPTIONAL) + * The get upload url body. This is a json string + */ + string body = 3; + + /** + * (OPTIONAL) + * The get upload url body signature. + */ + string signature = 4; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_attestation.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_attestation.proto new file mode 100644 index 0000000000000000000000000000000000000000..ac3554d80599fab2cc7a6035f687790562ca0cdf --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_attestation.proto @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request for certificate chain + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * GetCertificateChainResponse + */ +message GetCertificateChainRequest +{ + /** + * attestation challenge + */ + string attestation = 1; + + /** + * time to live + */ + int32 ttl = 2; +} + +/** + * Get certificate chain response + */ +message GetCertificateChainResponse +{ + /** + * array of certs + */ + repeated string certchain = 1; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_common.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_common.proto new file mode 100644 index 0000000000000000000000000000000000000000..4d46dd0bb744e6dabf4ba45686d1fecea71cd27f --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_common.proto @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * This error messages describe a failure that was encountered + * by the Sync service and primarily consist of an error code + * and a short, human-readable message. Therefore, if a client + * receives a message with a field reserved for Error messages, + * it is prudent that the application first check if there are + * errors before doing any further processing of the message. + */ +message Error +{ + /** + * (REQUIRED) Error code. + */ + uint32 error_code = 1; + + /** + * (OPTIONAL) Short, human-readable error message. If no + * message is available, then this will be an empty string. + */ + string error_message = 2; + + /** + * (OPTIONAL) A URI to a log file or some other document + * that contains more detailed information about the error. + * If such a file is not available, this will be an empty + * string. + */ + string error_log_uri = 3; + + /** + * (OPTIONAL) An optional JSON string with additional + * metadata that may be useful to the client. + */ + string json_metadata = 4; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_info.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_info.proto new file mode 100644 index 0000000000000000000000000000000000000000..2efe5182654abef1e34a67c827017cc002d1fe56 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_info.proto @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request Device information + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * GetDeviceInfoResponse + */ +message GetDeviceInfoRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Get device information response + */ +message GetDeviceInfoResponse +{ + /** + * payload contains actual mqtt message + */ + string payload = 1; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_mode.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_mode.proto new file mode 100644 index 0000000000000000000000000000000000000000..426e85a7b8928d4247c660e0cdfec3d2dbace2d0 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_mode.proto @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Behavior change as of 1/28/2021 EE + * Kong sync-agent should not deprovision when this message is + * received. If would just start forwarding events to PC when message is + * received. + * + * (Legacy) + * Request to transition to device mode + * Kong could be provisioned in Host mode. This message + * will ask Kong to deprovisioned/remove host mode provisioning + * data. + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * TransitionToDeviceModeResponse + */ +message TransitionToDeviceModeRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Request to transition to device mode response + */ +message TransitionToDeviceModeResponse +{ + /** + * boolean value to indicate Kong was able to transition to + * device mode. If Kong is not provisioned, should just respond + * with true value. + * set to false if error was encountered during transition, and Kong + * wasn't able to transition (is this possible?) + */ + bool success = 1; + + /** + * the error in integer if success was false + */ + int32 error = 2; + + /** + * the error description + */ + string error_description = 3; +} + +/** + * Added 1/28/2021 EE + * Request to deprovision Kong + * This request is sent by PC sync-agent when PC + * is provisioned. + * Kong sync-agent should deprovision (if provisioned) + * + * EXPECTED RESPONSE + * SetDeprovisionResponse + */ +message SetDeprovisionRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Response to deprovision request + */ +message SetDeprovisionResponse +{ + /** + * boolean value to indicate Kong was able to deprovision Kong. + * If Kong is not provisioned, should just respond + * with true value. + * set to false if error was encountered during deprovisioning. + */ + bool success = 1; + + /** + * the error in integer if success was false + */ + int32 error = 2; + + /** + * the error description + */ + string error_description = 3; +} + +/** + * Added 3/22/2021 EE + * For sending a certificate as data. There are currently + * 2 known certificate that will be transferred - Root CA, and 802.1x cert. + * Upon receipt, sync-agent should verify using the supplied hash + * and write the data to the file system. + * + * EXPECTED RESPONSE + * SendCertificateDataResponse + */ +message SendCertificateDataRequest +{ + /** + * The certificate type + */ + enum CertType { + /** + * Reserved. Do not use. + */ + RESERVED = 0; + /** + * Root CA + */ + ROOT_CA = 1; + /** + * 802.1x cert + */ + NET_CONFIG = 2; + } + + /** + * (REQUIRED) + * The certificate type + */ + CertType cert_type = 1; + + /** + * (REQUIRED) + * the certificate file name + */ + string file_name = 2; + + /** + * (REQUIRED) + * the certificate data + */ + bytes cert_data = 3; + + /** + * (REQUIRED) + * the certificate md5 hash + */ + string md5 = 4; +} + +/** + * Response to SendCertificateData Request + */ +message SendCertificateDataResponse +{ + /** + * (REQUIRED) + * boolean value to indicate data was received, hash verified . + * set to false if error was encountered during transfer and verification. + */ + bool success = 1; + + /** + * (OPTIONAL) + * the error in integer if success was false + */ + int32 error = 2; + + /** + * (OPTIONAL) + * the error description if there are errors + */ + string error_description = 3; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_request.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_request.proto new file mode 100644 index 0000000000000000000000000000000000000000..3969c73d304d5cdf0b2d8edf7415702be8bca05b --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_request.proto @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request to reboot device + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * RebootDeviceResponse + */ +message RebootDeviceRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; + + /** + * A timestamp indicating when the reboot request + * was initiated. + * The device should include this entry as part of the event information + * it sends back to PC during a reboot request. + */ + uint64 iat = 2; +} + +/** + * Reboot device response + */ +message RebootDeviceResponse +{ + /** + * bool value to indicate reboot was requested. If there are errors + * while requesting a device to reboot, should set the value to false + */ + bool success = 1; +} + +/** + * This message requests that the speaker boost audio setting be changed. + * The device should send a device info event after this setting request are + * handled. + * + * EXPECTED RESPONSE + * SetSpeakerBoostResponse + * + */ +message SetSpeakerBoostRequest +{ + /** + * (REQUIRED) The speaker boost setting to be set + * + * If value is 0, the request is to disable. If 1, + * the request is to enable. + */ + int32 speaker_boost = 1; +} + +message SetSpeakerBoostResponse +{ + /** + * (REQUIRED) set to true if the audio setting request was successfully sent, + * false otherwise + */ + bool success = 1; +} + +/** + * This message requests that the noise reduction audio setting be changed. + * The device should send a device info event after this setting request are + * handled. + * + * EXPECTED RESPONSE + * SetNoiseReductionResponse + * + */ +message SetNoiseReductionRequest +{ + /** + * (REQUIRED) The noise reduction setting to be set + * + * If value is 0, the request is to disable. If 1, + * the request is to enable. + */ + int32 noise_reduction = 1; +} + +message SetNoiseReductionResponse +{ + /** + * (REQUIRED) set to true if the audio setting request was successfully sent, + * false otherwise + */ + bool success = 1; +} + +/** + * This message requests that the reverb mode audio setting be changed. + * The device should send a device info event after this setting request are + * handled. + * + * EXPECTED RESPONSE + * SetReverbModeResponse + * + */ +message SetReverbModeRequest +{ + /** + * Reverb mode enumeration + */ + enum ReverbMode { + DISABLED = 0; + MILD = 1; + NORMAL = 2; + AGGRESSIVE = 3; + } + + /** + * (REQUIRED) The reverb mode setting to be set + * + * see Reverb mode enumeration + */ + ReverbMode reverb_mode = 1; +} + +message SetReverbModeResponse +{ + /** + * (REQUIRED) set to true if the setting request was successfully sent, false + * otherwise + */ + bool success = 1; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_time.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_time.proto new file mode 100644 index 0000000000000000000000000000000000000000..7db54028fe9faa25b12790f1a68302945e725dee --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/device_time.proto @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request for setting device time + * This is to be included in UsbMsg + */ +message SetDeviceTimeRequest +{ + /** + * utc timestamp. + */ + uint64 ts = 1; + /** + * the time zone. + */ + string time_zone = 2; +} + +/** + * Send an ack as the response + */ diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/firmware_update.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/firmware_update.proto new file mode 100644 index 0000000000000000000000000000000000000000..67e90424998387c5a8e9349e09107719f1d8de25 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/firmware_update.proto @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request to start update + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * UpdateNowResponse + */ +message UpdateNowRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Update now response + */ +message UpdateNowResponse +{ + /** + * bool value to indicate update was started + */ + bool started = 1; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/meson.build b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..161a7df39728c0b6769c1c1e8ce2bfdad8cbeeae --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/meson.build @@ -0,0 +1,21 @@ + +gen = generator(protoc, \ + output: ['@BASENAME@.pb-c.c', '@BASENAME@.pb-c.h'], + arguments: ['--proto_path=@CURRENT_SOURCE_DIR@', '--c_out=@BUILD_DIR@', '@INPUT@']) + +src = [ + 'antiflicker.proto', + 'ble_cfg.proto', + 'crash_info.proto', + 'device_attestation.proto', + 'device_common.proto', + 'device_info.proto', + 'device_mode.proto', + 'device_request.proto', + 'device_time.proto', + 'firmware_update.proto', + 'rightsight.proto', + 'ota_manifest.proto', + 'usb_msg.proto', + ] +generated = gen.process(src) diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/ota_manifest.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/ota_manifest.proto new file mode 100644 index 0000000000000000000000000000000000000000..3efed3a85e45978ce038b9e228ce15526b2880b4 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/ota_manifest.proto @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request device to create a GetManifestv2 body. See + * https://docs.google.com/document/d/1l31A1TWhtJC0xR8GwuNtiGN4vPLURRsj5ZcC1uEIwVQ/edit#heading=h.ctbthi1iyxw1 + * + * + * This is to be included in UsbMsg + * + * EXPECTED RESPONSE + * GetManifestBodyResponse + */ +message GetManifestBodyRequest +{ + /** + * The attestation challenge. + * (REQUIRED) + */ + string challenge = 1; + + /** + * The manifest version. + * (REQUIRED) + */ + string version = 2; + + /** + * The channel. Dont use if empty or null + * (OPTIONAL) + */ + string channel = 3; + + /** + * The meta info in json format. This + * field usually comes from PC. + * (OPTIONAL) + */ + string meta_info = 4; + + /** + * Time to live + * (REQUIRED) + */ + int32 ttl = 5; +} + +/** + * GetManifestv2 body response + */ +message GetManifestBodyResponse +{ + /** + * The get manifest body. This is a json string + */ + string body = 1; + + /** + * The get manifest body signature. + */ + string signature = 2; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/rightsight.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/rightsight.proto new file mode 100644 index 0000000000000000000000000000000000000000..771ed4e1e74fc2f9e6c6d55542f84488d035f7af --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/rightsight.proto @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_common.proto"; + +/** + * This message data structure holds information about the + * current RightSight configuration. + * + */ +message RightSightConfiguration +{ + /** + * Enumeration of modes that the RightSight service can be in. + */ + enum Mode { + /** + * This does not indicate a default value. + * + */ + DO_NOT_USE = 0; + + /** + * The camera will continually pan, tilt, and zoom + * to properly frame everyone during a meeting. + */ + DYNAMIC = 1; + + /** + * The camera will pan, tilt, and zoom to properly in + * the meeting only when the call starts. + */ + ON_CALL_START = 2; + } + + /** + * (REQUIRED) If true, RightSight is enabled and active. + */ + bool enabled = 1; + + /** + * (REQUIRED) The current mode that RightSight is in. + */ + Mode mode = 2; + + /** + * (REQUIRED) A timestamp indicating when the RightSight + * settings were last modified. This is the number of + * milliseconds since the epoch. + */ + uint64 last_modified = 3; +} + +/** + * RightSight is an auto-framing feature that is available in Kong. + * With RightSight enabled, your device will automatically pan, tilt, and zoom + * the camera lens in order to capture all meeting participants + * within the image frame. This feature can be set to one of two + * modes: dynamic and on call start. When in dynamic mode, the + * device will actively pan, tilt, and zoom the camera lens when + * appropriate in order to keep all participants in frame during + * the entire course of the meeting. When in on call start mode, + * the camera lens will pan, tilt, and zoom to capture everybody + * in frame only when the meeting starts. + * + * When RightSight is enabled, it is set + * to dynamic mode by default. + * + * This message requests that the RightSight configuration + * settings be changed. + * + * EXPECTED RESPONSE + * SetRightSightConfigurationResponse + * + */ +message SetRightSightConfigurationRequest +{ + /** + * (REQUIRED) If true, requests that RightSight be + * turned on. If false, indicates that + * RightSight should be turned off. + */ + bool enabled = 1; + + /** + * (REQUIRED) The mode for RightSight to be in. A value is + * required, but if none is provided, then this will + * default to DYNAMIC mode. + * + * If enabled is set to false, then this will effectively + * do nothing as RightSight is turned off. + */ + RightSightConfiguration.Mode mode = 2; +} + +/** + * Response which contains the RightSight configuration that was + * set as a result of the request. + */ +message SetRightSightConfigurationResponse +{ + /** + * (OPTIONAL) If any errors occurred while processing the + * request, then this field should be set accordingly. + */ + repeated Error errors = 1; + + /** + * (REQUIRED) The RightSight configuration that was set on + * the product. + */ + RightSightConfiguration right_sight_configuration = 2; +} diff --git a/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/usb_msg.proto b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/usb_msg.proto new file mode 100644 index 0000000000000000000000000000000000000000..95f40cfa2b3c46c6a0cdcc2bb0b7a1598694bc44 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-bulkcontroller/proto/usb_msg.proto @@ -0,0 +1,204 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_info.proto"; +import "firmware_update.proto"; +import "crash_info.proto"; +import "device_mode.proto"; +import "device_attestation.proto"; +import "rightsight.proto"; +import "ota_manifest.proto"; +import "device_time.proto"; +import "ble_cfg.proto"; +import "antiflicker.proto"; +import "device_request.proto"; + +/** + * + * Header message to be included in UsbMsg. This contains + * message metadata that aids in processing of messages + */ +message Header +{ + /** + * A unique id of the message. If responding after receiving + * data, the value stored in this field should be used in the ack message + * msgId field + */ + string id = 1; + /** + * A timestamp indicating when the message was + * sent. This is the number of milliseconds that have + * elapsed since the epoch, in string format + */ + string timestamp = 2; +} + +/** + * The Ack message. + * This is to be included in UsbMsg + */ +message Acknowledge +{ + /** + * The message Id. This should be the same value + * in UsbMsg.Header.id field + */ + string msgId = 1; + + /** + * The message processing result. true indicates message was + * successfully processed, false otherwise. + */ + bool success = 2; +} + +/** + * The Kong Event message. + * Anything that is not part of + * Request/Response messaging, but is being sent to mqtt distributor + * should be considered as a KongEvent, and forwarded to device host. + * This is to be included in UsbMsg + */ +message KongEvent +{ + /** + * mqtt_event contains actual mqtt message + */ + string mqtt_event = 1; +} + +/** + * Sent by Kong sync-agent. + * If Kong sync-agent starts-up and it is in Device mode, then + * it can send this event. When PC sync-agent receives this event, + * it should send a TransitionToDeviceModeRequest. + * This is to be included in UsbMsg + */ +message HandshakeEvent +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * The enclosing message. + * This is the root message of all messagesszx + */ +message UsbMsg +{ + /** + * Header for the message containing additional + * message metadata. + */ + Header header = 1; + + /** + * The actual message being sent. One of these must be + * included + */ + oneof message + { + /** + * Ack message + */ + Acknowledge ack = 2; + /** + * Request message + */ + Request request = 3; + /** + * Response message + */ + Response response = 4; + /** + * Event + */ + Event event = 5; + } +} + +/** + * The Request message. + * This is to be included in UsbMsg + */ +message Request +{ + oneof payload + { + GetDeviceInfoRequest get_device_info_request = 2; + UpdateNowRequest update_now_request = 3; + SendCrashDumpRequest crash_dump_request = 4; + TransitionToDeviceModeRequest transition_to_devicemode_request = 5; + GetCertificateChainRequest get_certificate_chain_request = 6; + SetRightSightConfigurationRequest set_right_sight_configuration_request = 7; + GetManifestBodyRequest get_manifest_body_request = 8; + SendCrashDumpRequestv2 crash_dump_request_v2 = 9; + SetDeviceTimeRequest set_device_time_request = 10; + SetAntiFlickerConfigurationRequest set_anti_flicker_configuration_request = 11; + SetBLECfgRequest set_ble_cfg_request = 12; + SetDeprovisionRequest set_deprovision_request = 13; + RebootDeviceRequest reboot_device_request = 14; + SetSpeakerBoostRequest speaker_boost_request = 15; + SetNoiseReductionRequest noise_reduction_request = 16; + SetReverbModeRequest reverb_mode_request = 17; + GenerateCrashDumpRequest generate_bug_report_request = 18; + SendCertificateDataRequest send_certificate_data_request = 19; + } +} + +/** + * The Response message. + * This is to be included in UsbMsg + */ +message Response +{ + oneof payload + { + GetDeviceInfoResponse get_device_info_response = 2; + UpdateNowResponse update_now_response = 3; + SendCrashDumpResponse crash_dump_response = 4; + TransitionToDeviceModeResponse transition_to_devicemode_response = 5; + GetCertificateChainResponse get_certificate_chain_response = 6; + SetRightSightConfigurationResponse set_right_sight_configuration_response = 7; + GetManifestBodyResponse get_manifest_body_response = 8; + SendCrashDumpResponsev2 crash_dump_response_v2 = 9; + SetAntiFlickerConfigurationResponse set_anti_flicker_configuration_response = 11; + SetBLECfgResponse set_ble_cfg_response = 12; + SetDeprovisionResponse set_deprovision_response = 13; + RebootDeviceResponse reboot_device_response = 14; + + SetSpeakerBoostResponse speaker_boost_response = 15; + SetNoiseReductionResponse noise_reduction_response = 16; + SetReverbModeResponse reverb_mode_response = 17; + GenerateCrashDumpResponse generate_bug_report_response = 18; + SendCertificateDataResponse send_certificate_data_response = 19; + } +} + +/** + * The Event message. + * This is to be included in UsbMsg + */ +message Event +{ + oneof payload + { + KongEvent kong_event = 1; + SendCrashDumpEvent send_crash_dump_event = 2; + CrashDumpAvailableEvent crash_dump_available_event = 3; + HandshakeEvent handshake_event = 4; + } +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/README.md b/fwupd-1.8.6/plugins/logitech-hidpp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..695d7f3d30632b67dcf8d93e99c3e6c4643dddc4 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/README.md @@ -0,0 +1,147 @@ +# Logitech HID + +## Introduction + +This plugin can flash the firmware on: + +* Logitech Unifying USB receivers, both the Nordic (U0007) device and the + Texas Instruments (U0008) versions +* Logitech Bolt USB receivers +* Unifying peripherals through the Unifying receiver +* Peripherals through the Bolt receiver and directly through BLE + +This plugin will not work with the different "Nano" USB receiver (U0010) as it does +not use the Unifying protocol. + +Some bootloader protocol information was taken from the [Mousejack](https://www.mousejack.com/) project, +specifically logitech-usb-restore.py and unifying.py. Other documentation was +supplied by Logitech. + +Additional constants were taken from the [https://pwr-Solaar.github.io/Solaar/](Solaar) 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 + +The Unifying receivers and peripherals 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&MOD_B33B405B0000` +* `HIDRAW\VEN_046D&MOD_B33B405B0000&ENT_05` +* `HIDRAW\VEN_046D&DEV_C52B` +* `HIDRAW\VEN_046D&DEV_C52B&ENT_05` +* `HIDRAW\VEN_046D` + +One additional legacy instance ID is added for periperals: + +* `UFY\VID_046D&PID_C52B` + +The Bolt USB receiver and peripherals use HID raw DeviceInstanceId values +regardless of their mode. This might change once these devices are +handled by the Logitech Linux driver instead of by the generic hid +driver. + +## Vendor ID Security + +The vendor ID is set from the vendor ID, in this instance set to `USB:0x046D` +in bootloader and `HIDRAW:0x046D` in runtime mode. + +## Update Behavior + +Due to the variety of devices supported and the differences in how +they're enumerated, the update behavior is slightly different between +them. + +In all cases, the devices have to be put in bootloader mode to run the +DFU process. While in bootloader mode, the user won't be able to use the +device. For receivers, that also means that while they're in bootloader +mode, the peripherals paired to them won't work during the update. + +A Unifying receiver presents in runtime mode, but on detach re-enumerates with a +different USB PID in a bootloader mode. On attach the device again re-enumerates +back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +The Bolt receiver enumerates as a hidraw device both in runtime and +bootloader mode, but with different HIDRAW devIDs. + +Peripherals paired to a receiver are enumerated as separate hidraw +devices but those device files can't be used for DFU. Instead, all the +DFU-related messages need to be piped through the hidraw device file of +the receiver. They are polled and queried by the receiver and listed as +its children. Note that this will likely change once the Logitech Linux +driver supports Bolt devices. + +Bolt peripherals directly connected to the host through BLE are +enumerated as individual hidraw devices and can be upgraded through +their hidraw device files. + +## Design Notes + +When a USB receiver is detected in bootloader mode we detach the hidraw driver from +the kernel and use raw control transfers. This ensures that we don't accidentally +corrupt the uploading firmware. For application firmware we use hidraw which +means the hardware keeps working while probing, and also allows us to detect +paired devices. + +### How the code is organized + +Here's how the different devices are handled in the plugin: + +* Unifying receiver in runtime mode: FuLogitechHidPpRuntimeUnifying + (fu-logitech-hidpp-runtime-unifying.c) +* Unifying receiver in bootloader mode: + * Nordic chipset: FuLogitechHidPpBootloaderNordic + (fu-logitech-hidpp-bootloader-nordic.c) + * TI chipset: FuLogitechHidPpBootloaderTexas + (fu-logitech-hidpp-bootloader-texas.c) +* Bolt receiver in runtime mode: FuLogitechHidPpRuntimeBolt + (fu-logitech-hidpp-runtime-bolt.c) +* Bolt receiver in bootloader mode and all peripherals: + FuLogitechHidPpDevice (fu-logitech-hidpp-device.c) + +FuLogitechHidPpDevice effectively handles all devices that use the +HID++2.0 protocol. + +Every device contains two updatable entities, the main application FW +and the radio stack FW (SoftDevice). The latter will show up as a child +device of the actual device and is handled by FuLogitechHidPpRadio +(fu-logitech-hidpp-radio.c), which simply defers to the parent device +for most operations. + +### Plugin-specific flags + +Even though the same code handles multiple different devices, there are +some inherent differences in them that makes it necessary to handle some +exceptional behaviors sometimes. + +In order to do that there are a few specific flags that can be used to +tweak the plugin code for certain device types: + +* rebind-attach: some devices will have their device file unbound and + re-bound after reset, so the device object can't be simply re-probed + using the same file descriptor. +* force-receiver-id: this flag is used to differentiate the receiver device in + FuLogitechHidPpDevice, since the receiver has a specific HID++ ID. +* ble: used to differentiate devices in BLE mode. They require all the + reports to be _long_. + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/dump.csv.gz b/fwupd-1.8.6/plugins/logitech-hidpp/data/dump.csv.gz new file mode 100644 index 0000000000000000000000000000000000000000..ca74fa8db7693e0fd462e3a45bd82c120713390f Binary files /dev/null and b/fwupd-1.8.6/plugins/logitech-hidpp/data/dump.csv.gz differ diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/dump.tdc b/fwupd-1.8.6/plugins/logitech-hidpp/data/dump.tdc new file mode 100644 index 0000000000000000000000000000000000000000..270249646c6ceb69cda6348575bd946ed1cbd5f8 Binary files /dev/null and b/fwupd-1.8.6/plugins/logitech-hidpp/data/dump.tdc differ diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0007-bootloader.txt b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0007-bootloader.txt new file mode 100644 index 0000000000000000000000000000000000000000..45ecbc97a72f9141a2a97ba24cbd87f59846439f --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0007-bootloader.txt @@ -0,0 +1,56 @@ +Bus 001 Device 036: ID 046d:aaaa Logitech, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 32 + idVendor 0x046d Logitech, Inc. + idProduct 0xaaaa + bcdDevice 1.02 + iManufacturer 1 + iProduct 2 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 98mA + 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.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 25 + 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 0x0020 1x 32 bytes + bInterval 1 diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0007.txt b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0007.txt new file mode 100644 index 0000000000000000000000000000000000000000..9e0704da9477223869a11784ea2319045a891426 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0007.txt @@ -0,0 +1,118 @@ +Bus 001 Device 049: ID 046d:c52b Logitech, Inc. Unifying Receiver +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x046d Logitech, Inc. + idProduct 0xc52b Unifying Receiver + bcdDevice 12.07 + iManufacturer 1 Logitech + iProduct 2 USB Receiver + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 84 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 RQR12.07_B0029 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 98mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 59 + Report Descriptor: (length is 59) + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 8 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 2 Mouse + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 148 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 2 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 1 + 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 93 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 2 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-bootloader-old.txt b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-bootloader-old.txt new file mode 100644 index 0000000000000000000000000000000000000000..4796006c3e23daa1932268f88c08697752077155 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-bootloader-old.txt @@ -0,0 +1,79 @@ + +Bus 003 Device 036: ID 046d:aaac Logitech, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 32 + idVendor 0x046d Logitech, Inc. + idProduct 0xaaac + bcdDevice 3.01 + iManufacturer 1 Logitech + iProduct 2 USB BootLoader + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 4 BOT03.01_B0008 + bmAttributes 0x80 + (Bus Powered) + MaxPower 98mA + 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.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 25 + Report Descriptor: (length is 25) + Item(Global): Usage Page, data= [ 0xb0 0xff ] 65456 + (null) + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x20 ] 32 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_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 0x0020 1x 32 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-bootloader.txt b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-bootloader.txt new file mode 100644 index 0000000000000000000000000000000000000000..4bc5a4049c0f5656dbc34e3dab9a50ab491fffd0 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-bootloader.txt @@ -0,0 +1,79 @@ + +Bus 003 Device 039: ID 046d:aaac Logitech, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 32 + idVendor 0x046d Logitech, Inc. + idProduct 0xaaac + bcdDevice 3.00 + iManufacturer 1 Logitech + iProduct 2 USB BootLoader + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 4 BOT03.00_B0006 + bmAttributes 0x80 + (Bus Powered) + MaxPower 98mA + 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.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 25 + Report Descriptor: (length is 25) + Item(Global): Usage Page, data= [ 0xb0 0xff ] 65456 + (null) + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x20 ] 32 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_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 0x0020 1x 32 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-old.txt b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-old.txt new file mode 100644 index 0000000000000000000000000000000000000000..901a5305a2f6a53240ee96a90c59d54d040486d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008-old.txt @@ -0,0 +1,367 @@ + +Bus 003 Device 033: ID 046d:c52b Logitech, Inc. Unifying Receiver +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 32 + idVendor 0x046d Logitech, Inc. + idProduct 0xc52b Unifying Receiver + bcdDevice 24.01 + iManufacturer 1 Logitech + iProduct 2 USB Receiver + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 84 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 RQR24.01_B0023 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 98mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 59 + Report Descriptor: (length is 59) + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x06 ] 6 + Keyboard + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Usage Page, data= [ 0x07 ] 7 + Keyboard + Item(Local ): Usage Minimum, data= [ 0xe0 ] 224 + Control Left + Item(Local ): Usage Maximum, data= [ 0xe7 ] 231 + GUI Right + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0x01 ] 1 + Item(Global): Report Size, data= [ 0x01 ] 1 + Item(Global): Report Count, data= [ 0x08 ] 8 + Item(Main ): Input, data= [ 0x02 ] 2 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): Input, data= [ 0x03 ] 3 + Constant Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report Count, data= [ 0x05 ] 5 + Item(Global): Usage Page, data= [ 0x08 ] 8 + LEDs + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + NumLock + Item(Local ): Usage Maximum, data= [ 0x05 ] 5 + Kana + Item(Main ): Output, 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(Global): Report Size, data= [ 0x03 ] 3 + Item(Main ): Output, data= [ 0x01 ] 1 + Constant Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report Count, data= [ 0x06 ] 6 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xa4 0x00 ] 164 + Item(Global): Usage Page, data= [ 0x07 ] 7 + Keyboard + Item(Local ): Usage Minimum, data= [ 0x00 ] 0 + No Event + Item(Local ): Usage Maximum, data= [ 0xa4 0x00 ] 164 + ExSel + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_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 0x0008 1x 8 bytes + bInterval 8 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 2 Mouse + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 148 + Report Descriptor: (length is 148) + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x02 ] 2 + Mouse + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x02 ] 2 + Item(Local ): Usage, data= [ 0x01 ] 1 + Pointer + Item(Main ): Collection, data= [ 0x00 ] 0 + Physical + Item(Global): Usage Page, data= [ 0x09 ] 9 + Buttons + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + Button 1 (Primary) + Item(Local ): Usage Maximum, data= [ 0x10 ] 16 + (null) + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0x01 ] 1 + Item(Global): Report Count, data= [ 0x10 ] 16 + Item(Global): Report Size, data= [ 0x01 ] 1 + 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= [ 0x01 ] 1 + Generic Desktop Controls + Item(Global): Logical Minimum, data= [ 0x01 0xf8 ] 63489 + Item(Global): Logical Maximum, data= [ 0xff 0x07 ] 2047 + Item(Global): Report Size, data= [ 0x0c ] 12 + Item(Global): Report Count, data= [ 0x02 ] 2 + Item(Local ): Usage, data= [ 0x30 ] 48 + Direction-X + Item(Local ): Usage, data= [ 0x31 ] 49 + Direction-Y + Item(Main ): Input, data= [ 0x06 ] 6 + Data Variable Relative No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Logical Minimum, data= [ 0x81 ] 129 + Item(Global): Logical Maximum, data= [ 0x7f ] 127 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Local ): Usage, data= [ 0x38 ] 56 + Wheel + Item(Main ): Input, data= [ 0x06 ] 6 + Data Variable Relative No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Usage Page, data= [ 0x0c ] 12 + Consumer + Item(Local ): Usage, data= [ 0x38 0x02 ] 568 + AC Pan + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Main ): Input, data= [ 0x06 ] 6 + Data Variable Relative No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x0c ] 12 + Consumer + Item(Local ): Usage, data= [ 0x01 ] 1 + Consumer Control + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x03 ] 3 + Item(Global): Report Size, data= [ 0x10 ] 16 + Item(Global): Report Count, data= [ 0x02 ] 2 + Item(Global): Logical Minimum, data= [ 0x01 ] 1 + Item(Global): Logical Maximum, data= [ 0x8c 0x02 ] 652 + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + Consumer Control + Item(Local ): Usage Maximum, data= [ 0x8c 0x02 ] 652 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x80 ] 128 + System Control + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x04 ] 4 + Item(Global): Report Size, data= [ 0x02 ] 2 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Global): Logical Minimum, data= [ 0x01 ] 1 + Item(Global): Logical Maximum, data= [ 0x03 ] 3 + Item(Local ): Usage, data= [ 0x82 ] 130 + System Sleep + Item(Local ): Usage, data= [ 0x81 ] 129 + System Power Down + Item(Local ): Usage, data= [ 0x83 ] 131 + System Wake Up + Item(Main ): Input, data= [ 0x60 ] 96 + Data Array Absolute No_Wrap Linear + No_Preferred_State Null_State Non_Volatile Bitfield + Item(Global): Report Size, data= [ 0x06 ] 6 + Item(Main ): Input, data= [ 0x03 ] 3 + Constant Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0xbc 0xff ] 65468 + (null) + Item(Local ): Usage, data= [ 0x88 ] 136 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x08 ] 8 + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + (null) + Item(Local ): Usage Maximum, data= [ 0xff ] 255 + (null) + Item(Global): Logical Minimum, data= [ 0x01 ] 1 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 2 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 1 + 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 98 + Report Descriptor: (length is 98) + Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 + (null) + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x10 ] 16 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x06 ] 6 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 + (null) + Item(Local ): Usage, data= [ 0x02 ] 2 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x11 ] 17 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x13 ] 19 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x02 ] 2 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x02 ] 2 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 + (null) + Item(Local ): Usage, data= [ 0x04 ] 4 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x20 ] 32 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x0e ] 14 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x41 ] 65 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x41 ] 65 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report ID, data= [ 0x21 ] 33 + Item(Global): Report Count, data= [ 0x1f ] 31 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x42 ] 66 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x42 ] 66 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 2 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008.txt b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d424b3c81fc8761d471447c6218321ff179227c --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/data/lsusb-U0008.txt @@ -0,0 +1,366 @@ +Bus 003 Device 032: ID 046d:c52b Logitech, Inc. Unifying Receiver +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 32 + idVendor 0x046d Logitech, Inc. + idProduct 0xc52b Unifying Receiver + bcdDevice 24.05 + iManufacturer 1 Logitech + iProduct 2 USB Receiver + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 84 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 RQR24.05_B0029 + bmAttributes 0xa0 + (Bus Powered) + Remote Wakeup + MaxPower 98mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 1 Keyboard + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 59 + Report Descriptor: (length is 59) + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x06 ] 6 + Keyboard + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Usage Page, data= [ 0x07 ] 7 + Keyboard + Item(Local ): Usage Minimum, data= [ 0xe0 ] 224 + Control Left + Item(Local ): Usage Maximum, data= [ 0xe7 ] 231 + GUI Right + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0x01 ] 1 + Item(Global): Report Size, data= [ 0x01 ] 1 + Item(Global): Report Count, data= [ 0x08 ] 8 + Item(Main ): Input, data= [ 0x02 ] 2 + Data Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): Input, data= [ 0x03 ] 3 + Constant Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report Count, data= [ 0x05 ] 5 + Item(Global): Usage Page, data= [ 0x08 ] 8 + LEDs + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + NumLock + Item(Local ): Usage Maximum, data= [ 0x05 ] 5 + Kana + Item(Main ): Output, 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(Global): Report Size, data= [ 0x03 ] 3 + Item(Main ): Output, data= [ 0x01 ] 1 + Constant Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report Count, data= [ 0x06 ] 6 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xa4 0x00 ] 164 + Item(Global): Usage Page, data= [ 0x07 ] 7 + Keyboard + Item(Local ): Usage Minimum, data= [ 0x00 ] 0 + No Event + Item(Local ): Usage Maximum, data= [ 0xa4 0x00 ] 164 + ExSel + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_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 0x0008 1x 8 bytes + bInterval 8 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 1 Boot Interface Subclass + bInterfaceProtocol 2 Mouse + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 148 + Report Descriptor: (length is 148) + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x02 ] 2 + Mouse + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x02 ] 2 + Item(Local ): Usage, data= [ 0x01 ] 1 + Pointer + Item(Main ): Collection, data= [ 0x00 ] 0 + Physical + Item(Global): Usage Page, data= [ 0x09 ] 9 + Buttons + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + Button 1 (Primary) + Item(Local ): Usage Maximum, data= [ 0x10 ] 16 + (null) + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0x01 ] 1 + Item(Global): Report Count, data= [ 0x10 ] 16 + Item(Global): Report Size, data= [ 0x01 ] 1 + 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= [ 0x01 ] 1 + Generic Desktop Controls + Item(Global): Logical Minimum, data= [ 0x01 0xf8 ] 63489 + Item(Global): Logical Maximum, data= [ 0xff 0x07 ] 2047 + Item(Global): Report Size, data= [ 0x0c ] 12 + Item(Global): Report Count, data= [ 0x02 ] 2 + Item(Local ): Usage, data= [ 0x30 ] 48 + Direction-X + Item(Local ): Usage, data= [ 0x31 ] 49 + Direction-Y + Item(Main ): Input, data= [ 0x06 ] 6 + Data Variable Relative No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Logical Minimum, data= [ 0x81 ] 129 + Item(Global): Logical Maximum, data= [ 0x7f ] 127 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Local ): Usage, data= [ 0x38 ] 56 + Wheel + Item(Main ): Input, data= [ 0x06 ] 6 + Data Variable Relative No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Usage Page, data= [ 0x0c ] 12 + Consumer + Item(Local ): Usage, data= [ 0x38 0x02 ] 568 + AC Pan + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Main ): Input, data= [ 0x06 ] 6 + Data Variable Relative No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x0c ] 12 + Consumer + Item(Local ): Usage, data= [ 0x01 ] 1 + Consumer Control + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x03 ] 3 + Item(Global): Report Size, data= [ 0x10 ] 16 + Item(Global): Report Count, data= [ 0x02 ] 2 + Item(Global): Logical Minimum, data= [ 0x01 ] 1 + Item(Global): Logical Maximum, data= [ 0x8c 0x02 ] 652 + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + Consumer Control + Item(Local ): Usage Maximum, data= [ 0x8c 0x02 ] 652 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x01 ] 1 + Generic Desktop Controls + Item(Local ): Usage, data= [ 0x80 ] 128 + System Control + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x04 ] 4 + Item(Global): Report Size, data= [ 0x02 ] 2 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Global): Logical Minimum, data= [ 0x01 ] 1 + Item(Global): Logical Maximum, data= [ 0x03 ] 3 + Item(Local ): Usage, data= [ 0x82 ] 130 + System Sleep + Item(Local ): Usage, data= [ 0x81 ] 129 + System Power Down + Item(Local ): Usage, data= [ 0x83 ] 131 + System Wake Up + Item(Main ): Input, data= [ 0x60 ] 96 + Data Array Absolute No_Wrap Linear + No_Preferred_State Null_State Non_Volatile Bitfield + Item(Global): Report Size, data= [ 0x06 ] 6 + Item(Main ): Input, data= [ 0x03 ] 3 + Constant Variable Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0xbc 0xff ] 65468 + (null) + Item(Local ): Usage, data= [ 0x88 ] 136 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x08 ] 8 + Item(Local ): Usage Minimum, data= [ 0x01 ] 1 + (null) + Item(Local ): Usage Maximum, data= [ 0xff ] 255 + (null) + Item(Global): Logical Minimum, data= [ 0x01 ] 1 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x01 ] 1 + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 2 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 1 + 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 98 + Report Descriptor: (length is 98) + Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 + (null) + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x10 ] 16 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x06 ] 6 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x01 ] 1 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 + (null) + Item(Local ): Usage, data= [ 0x02 ] 2 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x11 ] 17 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x13 ] 19 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x02 ] 2 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x02 ] 2 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 + (null) + Item(Local ): Usage, data= [ 0x04 ] 4 + (null) + Item(Main ): Collection, data= [ 0x01 ] 1 + Application + Item(Global): Report ID, data= [ 0x20 ] 32 + Item(Global): Report Size, data= [ 0x08 ] 8 + Item(Global): Report Count, data= [ 0x0e ] 14 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x41 ] 65 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x41 ] 65 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Global): Report ID, data= [ 0x21 ] 33 + Item(Global): Report Count, data= [ 0x1f ] 31 + Item(Global): Logical Minimum, data= [ 0x00 ] 0 + Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 + Item(Local ): Usage, data= [ 0x42 ] 66 + (null) + Item(Main ): Input, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Local ): Usage, data= [ 0x42 ] 66 + (null) + Item(Main ): Output, data= [ 0x00 ] 0 + Data Array Absolute No_Wrap Linear + Preferred_State No_Null_Position Non_Volatile Bitfield + Item(Main ): End Collection, data=none + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 2 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c new file mode 100644 index 0000000000000000000000000000000000000000..8820c318ed6e00b09ee85f0503165276eae49b43 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-hidpp-bootloader-nordic.h" +#include "fu-logitech-hidpp-common.h" + +struct _FuLogitechHidPpBootloaderNordic { + FuLogitechHidPpBootloader parent_instance; +}; + +G_DEFINE_TYPE(FuLogitechHidPpBootloaderNordic, + fu_logitech_hidpp_bootloader_nordic, + FU_TYPE_UNIFYING_BOOTLOADER) + +static gchar * +fu_logitech_hidpp_bootloader_nordic_get_hw_platform_id(FuLogitechHidPpBootloader *self, + GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_HW_PLATFORM_ID; + if (!fu_logitech_hidpp_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_logitech_hidpp_bootloader_nordic_get_fw_version(FuLogitechHidPpBootloader *self, GError **error) +{ + guint16 micro; + + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_FW_VERSION; + if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { + g_prefix_error(error, "failed to get firmware version: "); + return NULL; + } + + /* RRRxx.yy_Bzzzz + * 012345678901234*/ + micro = (guint16)fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 10) << 8; + micro += fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 12); + return fu_logitech_hidpp_format_version( + "RQR", + fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 3), + fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 6), + micro); +} + +static gboolean +fu_logitech_hidpp_bootloader_nordic_setup(FuDevice *device, GError **error) +{ + FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER(device); + g_autofree gchar *hw_platform_id = NULL; + g_autofree gchar *version_fw = NULL; + g_autoptr(GError) error_local = NULL; + + /* FuLogitechHidPpBootloader->setup */ + if (!FU_DEVICE_CLASS(fu_logitech_hidpp_bootloader_nordic_parent_class) + ->setup(device, error)) + return FALSE; + + /* get MCU */ + hw_platform_id = fu_logitech_hidpp_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_logitech_hidpp_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(device, "RQR12.00_B0000"); + } else { + fu_device_set_version(device, version_fw); + } + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_bootloader_nordic_write_signature(FuLogitechHidPpBootloader *self, + guint16 addr, + guint8 len, + const guint8 *data, + GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = 0xC0; + req->addr = addr; + req->len = len; + memcpy(req->data, data, req->len); + if (!fu_logitech_hidpp_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_logitech_hidpp_bootloader_nordic_write(FuLogitechHidPpBootloader *self, + guint16 addr, + guint8 len, + const guint8 *data, + GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_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_logitech_hidpp_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_logitech_hidpp_bootloader_nordic_erase(FuLogitechHidPpBootloader *self, + guint16 addr, + GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_ERASE_PAGE; + req->addr = addr; + req->len = 0x01; + if (!fu_logitech_hidpp_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_logitech_hidpp_bootloader_nordic_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER(device); + const FuLogitechHidPpBootloaderRequest *payload; + guint16 addr; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) reqs = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_BOOTLOADER_FLAG_IS_SIGNED)) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 4, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 13, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write0"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 82, "reset vector"); + } else { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 22, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 72, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write0"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 6, "reset-vector"); + } + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* erase firmware pages up to the bootloader */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); + for (addr = fu_logitech_hidpp_bootloader_get_addr_lo(self); + addr < fu_logitech_hidpp_bootloader_get_addr_hi(self); + addr += fu_logitech_hidpp_bootloader_get_blocksize(self)) { + if (!fu_logitech_hidpp_bootloader_nordic_erase(self, addr, error)) + return FALSE; + } + fu_progress_step_done(progress); + + /* transfer payload */ + reqs = fu_logitech_hidpp_bootloader_parse_requests(self, fw, error); + if (reqs == NULL) + return FALSE; + fu_progress_set_status(progress, 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_logitech_hidpp_bootloader_nordic_write_signature(self, + payload->addr, + payload->len, + payload->data, + error); + } else { + res = fu_logitech_hidpp_bootloader_nordic_write(self, + payload->addr, + payload->len, + payload->data, + error); + } + + if (!res) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), i + 1, reqs->len); + } + fu_progress_step_done(progress); + + /* send the first managed packet last, excluding the reset vector */ + payload = g_ptr_array_index(reqs, 0); + if (!fu_logitech_hidpp_bootloader_nordic_write(self, + payload->addr + 1, + payload->len - 1, + payload->data + 1, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* reset vector */ + if (!fu_logitech_hidpp_bootloader_nordic_write(self, 0x0000, 0x01, payload->data, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_logitech_hidpp_bootloader_nordic_class_init(FuLogitechHidPpBootloaderNordicClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_logitech_hidpp_bootloader_nordic_write_firmware; + klass_device->setup = fu_logitech_hidpp_bootloader_nordic_setup; +} + +static void +fu_logitech_hidpp_bootloader_nordic_init(FuLogitechHidPpBootloaderNordic *self) +{ +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.h new file mode 100644 index 0000000000000000000000000000000000000000..b5aec09eea24e5fa1d41e90ff23c84953df0e02d --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-logitech-hidpp-bootloader.h" + +#define FU_TYPE_UNIFYING_BOOTLOADER_NORDIC (fu_logitech_hidpp_bootloader_nordic_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechHidPpBootloaderNordic, + fu_logitech_hidpp_bootloader_nordic, + FU, + UNIFYING_BOOTLOADER_NORDIC, + FuLogitechHidPpBootloader) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c new file mode 100644 index 0000000000000000000000000000000000000000..1d9f6b9dd941c70cb1783e18e2d30da949b4551c --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-hidpp-bootloader-texas.h" +#include "fu-logitech-hidpp-common.h" + +struct _FuLogitechHidPpBootloaderTexas { + FuLogitechHidPpBootloader parent_instance; +}; + +G_DEFINE_TYPE(FuLogitechHidPpBootloaderTexas, + fu_logitech_hidpp_bootloader_texas, + FU_TYPE_UNIFYING_BOOTLOADER) + +static gboolean +fu_logitech_hidpp_bootloader_texas_erase_all(FuLogitechHidPpBootloader *self, GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; + req->len = 0x01; /* magic number */ + req->data[0] = 0x00; /* magic number */ + if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { + g_prefix_error(error, "failed to erase all pages: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_hidpp_bootloader_texas_compute_and_test_crc(FuLogitechHidPpBootloader *self, + GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM; + req->len = 0x01; /* magic number */ + req->data[0] = 0x03; /* magic number */ + if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { + g_prefix_error(error, "failed to compute and test CRC: "); + 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_logitech_hidpp_bootloader_texas_flash_ram_buffer(FuLogitechHidPpBootloader *self, + guint16 addr, + GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_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_logitech_hidpp_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_logitech_hidpp_bootloader_texas_clear_ram_buffer(FuLogitechHidPpBootloader *self, GError **error) +{ + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_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_logitech_hidpp_bootloader_request(self, req, error)) { + g_prefix_error(error, "failed to clear ram buffer @%04x: ", req->addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_hidpp_bootloader_texas_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER(device); + const FuLogitechHidPpBootloaderRequest *payload; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) reqs = NULL; + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_BOOTLOADER_FLAG_IS_SIGNED)) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 3, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, "clear"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 18, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 79, NULL); + } else { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 11, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, "clear"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 75, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 12, NULL); + } + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* transfer payload */ + reqs = fu_logitech_hidpp_bootloader_parse_requests(self, fw, error); + if (reqs == NULL) + return FALSE; + + /* erase all flash pages */ + if (!fu_logitech_hidpp_bootloader_texas_erase_all(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* set existing RAM buffer to 0xff's */ + if (!fu_logitech_hidpp_bootloader_texas_clear_ram_buffer(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write to RAM buffer */ + 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; + if (!fu_memcpy_safe(req->data, + req->len, + 0x0, /* dst */ + payload->data, + payload->len, + 0x0, /* src */ + payload->len, + error)) + return FALSE; + if (!fu_logitech_hidpp_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); + if (g_getenv("FWUPD_LOGITECH_HIDPP_VERBOSE") != NULL) { + g_debug("addr flush @ 0x%04x for 0x%04x", + payload->addr, + addr_start); + } + if (!fu_logitech_hidpp_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_progress_set_percentage_full(fu_progress_get_child(progress), i + 1, reqs->len); + } + fu_progress_step_done(progress); + + /* check CRC */ + if (!fu_logitech_hidpp_bootloader_texas_compute_and_test_crc(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_bootloader_texas_setup(FuDevice *device, GError **error) +{ + /* FuLogitechHidPpBootloader->setup */ + if (!FU_DEVICE_CLASS(fu_logitech_hidpp_bootloader_texas_parent_class)->setup(device, error)) + return FALSE; + fu_device_set_version(device, "RQR24.00_B0000"); + return TRUE; +} + +static void +fu_logitech_hidpp_bootloader_texas_class_init(FuLogitechHidPpBootloaderTexasClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_logitech_hidpp_bootloader_texas_write_firmware; + klass_device->setup = fu_logitech_hidpp_bootloader_texas_setup; +} + +static void +fu_logitech_hidpp_bootloader_texas_init(FuLogitechHidPpBootloaderTexas *self) +{ +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.h new file mode 100644 index 0000000000000000000000000000000000000000..acd30670b7e097102d172494b87dae33a867d7ab --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-logitech-hidpp-bootloader.h" + +#define FU_TYPE_UNIFYING_BOOTLOADER_TEXAS (fu_logitech_hidpp_bootloader_texas_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechHidPpBootloaderTexas, + fu_logitech_hidpp_bootloader_texas, + FU, + UNIFYING_BOOTLOADER_TEXAS, + FuLogitechHidPpBootloader) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c new file mode 100644 index 0000000000000000000000000000000000000000..c14b898082cfed17693ea1107dd671f8ea703c1a --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-logitech-hidpp-bootloader.h" +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-hidpp.h" + +typedef struct { + guint16 flash_addr_lo; + guint16 flash_addr_hi; + guint16 flash_blocksize; +} FuLogitechHidPpBootloaderPrivate; + +#define FU_UNIFYING_DEVICE_EP1 0x81 +#define FU_UNIFYING_DEVICE_EP3 0x83 + +G_DEFINE_TYPE_WITH_PRIVATE(FuLogitechHidPpBootloader, + fu_logitech_hidpp_bootloader, + FU_TYPE_HID_DEVICE) + +#define GET_PRIVATE(o) (fu_logitech_hidpp_bootloader_get_instance_private(o)) + +static void +fu_logitech_hidpp_bootloader_to_string(FuDevice *device, guint idt, GString *str) +{ + FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER(device); + FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE(self); + fu_string_append_kx(str, idt, "FlashAddrHigh", priv->flash_addr_hi); + fu_string_append_kx(str, idt, "FlashAddrLow", priv->flash_addr_lo); + fu_string_append_kx(str, idt, "FlashBlockSize", priv->flash_blocksize); +} + +FuLogitechHidPpBootloaderRequest * +fu_logitech_hidpp_bootloader_request_new(void) +{ + FuLogitechHidPpBootloaderRequest *req = g_new0(FuLogitechHidPpBootloaderRequest, 1); + return req; +} + +GPtrArray * +fu_logitech_hidpp_bootloader_parse_requests(FuLogitechHidPpBootloader *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(FuLogitechHidPpBootloaderRequest) payload = NULL; + guint8 rec_type = 0x00; + guint16 offset = 0x0000; + guint16 addr = 0x0; + gboolean exit = FALSE; + gsize linesz = strlen(lines[i]); + + /* skip empty lines */ + tmp = lines[i]; + if (linesz < 5) + continue; + + payload = fu_logitech_hidpp_bootloader_request_new(); + payload->len = fu_logitech_hidpp_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; + } + if (!fu_firmware_strparse_uint16_safe(tmp, linesz, 0x03, &addr, error)) + return NULL; + payload->addr = addr; + payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; + + rec_type = fu_logitech_hidpp_buffer_read_uint8(tmp + 0x07); + + switch (rec_type) { + case 0x00: /* data */ + break; + case 0x01: /* EOF */ + exit = TRUE; + break; + case 0x03: /* start segment address */ + /* this is used to specify the start address, + it is doesn't matter in this context so we can + safely ignore it */ + continue; + case 0x04: /* extended linear address */ + if (!fu_firmware_strparse_uint16_safe(tmp, linesz, 0x09, &offset, error)) + return NULL; + if (offset != 0x0000) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "extended linear addresses with offset different from " + "0 are not supported"); + return NULL; + } + continue; + case 0x05: /* start linear address */ + /* this is used to specify the start address, + it is doesn't matter in this context so we can + safely ignore it */ + continue; + case 0xFD: /* custom - vendor */ + /* record type of 0xFD indicates signature data */ + payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE; + break; + default: + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "intel hex file record type %02x not supported", + rec_type); + return NULL; + } + + if (exit) + break; + + /* read the data, but skip the checksum byte */ + for (guint j = 0; j < payload->len; j++) { + const gchar *ptr = tmp + 0x09 + (j * 2); + 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_logitech_hidpp_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_logitech_hidpp_bootloader_get_addr_hi(self)) { + g_debug("skipping write @ %04x", payload->addr); + continue; + } + + /* skip the header */ + if (payload->addr < fu_logitech_hidpp_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_logitech_hidpp_bootloader_get_addr_lo(FuLogitechHidPpBootloader *self) +{ + FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UNIFYING_BOOTLOADER(self), 0x0000); + return priv->flash_addr_lo; +} + +guint16 +fu_logitech_hidpp_bootloader_get_addr_hi(FuLogitechHidPpBootloader *self) +{ + FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UNIFYING_BOOTLOADER(self), 0x0000); + return priv->flash_addr_hi; +} + +guint16 +fu_logitech_hidpp_bootloader_get_blocksize(FuLogitechHidPpBootloader *self) +{ + FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UNIFYING_BOOTLOADER(self), 0x0000); + return priv->flash_blocksize; +} + +static gboolean +fu_logitech_hidpp_bootloader_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER(device); + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_REBOOT; + if (!fu_logitech_hidpp_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_logitech_hidpp_bootloader_set_bl_version(FuLogitechHidPpBootloader *self, GError **error) +{ + guint16 build; + guint8 major; + guint8 minor; + g_autofree gchar *version = NULL; + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + + /* call into hardware */ + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_BL_VERSION; + if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { + g_prefix_error(error, "failed to get firmware version: "); + return FALSE; + } + + /* BOTxx.yy_Bzzzz + * 012345678901234 */ + build = (guint16)fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 10) << 8; + build += fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 12); + major = fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 3); + minor = fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 6); + version = fu_logitech_hidpp_format_version("BOT", major, minor, build); + if (version == NULL) { + g_prefix_error(error, "failed to format firmware version: "); + return FALSE; + } + fu_device_set_version_bootloader(FU_DEVICE(self), version); + + if ((major == 0x01 && minor >= 0x04) || (major == 0x03 && minor >= 0x02)) { + fu_device_add_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_BOOTLOADER_FLAG_IS_SIGNED); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); + } else { + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifying"); + } + return TRUE; +} + +static gboolean +fu_logitech_hidpp_bootloader_setup(FuDevice *device, GError **error) +{ + FuLogitechHidPpBootloader *self = FU_UNIFYING_BOOTLOADER(device); + FuLogitechHidPpBootloaderPrivate *priv = GET_PRIVATE(self); + g_autoptr(FuLogitechHidPpBootloaderRequest) req = + fu_logitech_hidpp_bootloader_request_new(); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_logitech_hidpp_bootloader_parent_class)->setup(device, error)) + return FALSE; + + /* get memory map */ + req->cmd = FU_UNIFYING_BOOTLOADER_CMD_GET_MEMINFO; + if (!fu_logitech_hidpp_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_memread_uint16(req->data + 0, G_BIG_ENDIAN); + priv->flash_addr_hi = fu_memread_uint16(req->data + 2, G_BIG_ENDIAN); + priv->flash_blocksize = fu_memread_uint16(req->data + 4, G_BIG_ENDIAN); + + /* get bootloader version */ + return fu_logitech_hidpp_bootloader_set_bl_version(self, error); +} + +gboolean +fu_logitech_hidpp_bootloader_request(FuLogitechHidPpBootloader *self, + FuLogitechHidPpBootloaderRequest *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; + if (!fu_memcpy_safe(buf_request, + sizeof(buf_request), + 0x04, /* dst */ + req->data, + sizeof(req->data), + 0x0, /* src */ + sizeof(req->data), + error)) + return FALSE; + + /* send request */ + if (g_getenv("FWUPD_LOGITECH_HIDPP_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "host->device", buf_request, sizeof(buf_request)); + } + if (usb_device != NULL) { + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf_request, + sizeof(buf_request), + FU_UNIFYING_DEVICE_TIMEOUT_MS, + FU_HID_DEVICE_FLAG_NONE, + 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_LOGITECH_HIDPP_VERBOSE") != NULL) { + fu_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_LOGITECH_HIDPP_VERBOSE") != NULL) { + fu_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_logitech_hidpp_bootloader_init(FuLogitechHidPpBootloader *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_icon(FU_DEVICE(self), "preferences-desktop-keyboard"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_name(FU_DEVICE(self), "Unifying Receiver"); + fu_device_set_summary(FU_DEVICE(self), "Miniaturised USB wireless receiver (bootloader)"); + fu_device_set_remove_delay(FU_DEVICE(self), FU_UNIFYING_DEVICE_TIMEOUT_MS); + fu_device_register_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_BOOTLOADER_FLAG_IS_SIGNED, + "is-signed"); + fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x00); +} + +static void +fu_logitech_hidpp_bootloader_class_init(FuLogitechHidPpBootloaderClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_logitech_hidpp_bootloader_to_string; + klass_device->attach = fu_logitech_hidpp_bootloader_attach; + klass_device->setup = fu_logitech_hidpp_bootloader_setup; +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.h new file mode 100644 index 0000000000000000000000000000000000000000..497adcc7dc0eaf45f1f900921b6baf2719f1b15a --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_UNIFYING_BOOTLOADER (fu_logitech_hidpp_bootloader_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuLogitechHidPpBootloader, + fu_logitech_hidpp_bootloader, + FU, + UNIFYING_BOOTLOADER, + FuHidDevice) + +struct _FuLogitechHidPpBootloaderClass { + FuHidDeviceClass parent_class; +}; + +/** + * FU_LOGITECH_HIDPP_BOOTLOADER_FLAG_IS_SIGNED: + * + * Device requires signed firmware. + * + * Since: 1.7.0 + */ +#define FU_LOGITECH_HIDPP_BOOTLOADER_FLAG_IS_SIGNED (1 << 0) + +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 +} FuLogitechHidPpBootloaderCmd; + +/* packet to and from device */ +typedef struct __attribute__((packed)) { + guint8 cmd; + guint16 addr; + guint8 len; + guint8 data[28]; +} FuLogitechHidPpBootloaderRequest; + +FuLogitechHidPpBootloaderRequest * +fu_logitech_hidpp_bootloader_request_new(void); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuLogitechHidPpBootloaderRequest, g_free); +#pragma clang diagnostic pop + +GPtrArray * +fu_logitech_hidpp_bootloader_parse_requests(FuLogitechHidPpBootloader *self, + GBytes *fw, + GError **error); +gboolean +fu_logitech_hidpp_bootloader_request(FuLogitechHidPpBootloader *self, + FuLogitechHidPpBootloaderRequest *req, + GError **error); + +guint16 +fu_logitech_hidpp_bootloader_get_addr_lo(FuLogitechHidPpBootloader *self); +guint16 +fu_logitech_hidpp_bootloader_get_addr_hi(FuLogitechHidPpBootloader *self); +guint16 +fu_logitech_hidpp_bootloader_get_blocksize(FuLogitechHidPpBootloader *self); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-common.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-common.c new file mode 100644 index 0000000000000000000000000000000000000000..87700b6ac0fa0a758b8cc6edf65fdcbfe4631dfb --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-common.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include + +#include "fu-logitech-hidpp-common.h" + +guint8 +fu_logitech_hidpp_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_logitech_hidpp_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_logitech_hidpp_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]) || name[i] == '\0') + 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 --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-common.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-common.h new file mode 100644 index 0000000000000000000000000000000000000000..5642406168c6c5469a11a20f7bea29a3d27f10a4 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-common.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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 +#define FU_UNIFYING_DEVICE_PID_BOOTLOADER_BOLT 0xab07 + +/* Signed firmware are very long to verify on the device */ +#define FU_UNIFYING_DEVICE_TIMEOUT_MS 30000 + +/* Polling intervals (ms) */ +#define FU_HIDPP_DEVICE_POLLING_INTERVAL 30000 +#define FU_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL 5000 + +#define FU_HIDPP_VERSION_BLE 0xFE + +guint8 +fu_logitech_hidpp_buffer_read_uint8(const gchar *str); +guint16 +fu_logitech_hidpp_buffer_read_uint16(const gchar *str); + +gchar * +fu_logitech_hidpp_format_version(const gchar *name, guint8 major, guint8 minor, guint16 build); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-device.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-device.c new file mode 100644 index 0000000000000000000000000000000000000000..75736cfdb82e4ffe1fed35abaf1448e875f4fa87 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-device.c @@ -0,0 +1,1414 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-device.h" +#include "fu-logitech-hidpp-hidpp.h" +#include "fu-logitech-hidpp-radio.h" +#include "fu-logitech-hidpp-runtime-bolt.h" + +typedef struct { + guint8 cached_fw_entity; + /* + * Device index: + * - HIDPP_DEVICE_IDX_RECEIVER for the receiver + * - HIDPP_DEVICE_IDX_BLE for BLE devices + * - pairing slot for paired Bolt devices. + */ + guint8 device_idx; + guint16 hidpp_pid; + guint8 hidpp_version; + FuIOChannel *io_channel; + gchar *model_id; + GPtrArray *feature_index; /* of FuLogitechHidPpHidppMap */ +} FuLogitechHidPpDevicePrivate; + +typedef struct { + guint8 idx; + guint16 feature; +} FuLogitechHidPpHidppMap; + +G_DEFINE_TYPE_WITH_PRIVATE(FuLogitechHidPpDevice, fu_logitech_hidpp_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_logitech_hidpp_device_get_instance_private(o)) + +typedef enum { + FU_HIDPP_DEVICE_KIND_KEYBOARD, + FU_HIDPP_DEVICE_KIND_REMOTE_CONTROL, + FU_HIDPP_DEVICE_KIND_NUMPAD, + FU_HIDPP_DEVICE_KIND_MOUSE, + FU_HIDPP_DEVICE_KIND_TOUCHPAD, + FU_HIDPP_DEVICE_KIND_TRACKBALL, + FU_HIDPP_DEVICE_KIND_PRESENTER, + FU_HIDPP_DEVICE_KIND_RECEIVER, + FU_HIDPP_DEVICE_KIND_LAST +} FuLogitechHidPpDeviceKind; + +void +fu_logitech_hidpp_device_set_device_idx(FuLogitechHidPpDevice *self, guint8 device_idx) +{ + FuLogitechHidPpDevicePrivate *priv; + g_return_if_fail(FU_IS_HIDPP_DEVICE(self)); + priv = GET_PRIVATE(self); + priv->device_idx = device_idx; +} + +guint16 +fu_logitech_hidpp_device_get_hidpp_pid(FuLogitechHidPpDevice *self) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_HIDPP_DEVICE(self), G_MAXUINT16); + return priv->hidpp_pid; +} + +void +fu_logitech_hidpp_device_set_hidpp_pid(FuLogitechHidPpDevice *self, guint16 hidpp_pid) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_HIDPP_DEVICE(self)); + priv->hidpp_pid = hidpp_pid; +} + +const gchar * +fu_logitech_hidpp_device_get_model_id(FuLogitechHidPpDevice *self) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_HIDPP_DEVICE(self), NULL); + return priv->model_id; +} + +static void +fu_logitech_hidpp_device_set_model_id(FuLogitechHidPpDevice *self, const gchar *model_id) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_HIDPP_DEVICE(self)); + if (g_strcmp0(priv->model_id, model_id) == 0) + return; + g_free(priv->model_id); + priv->model_id = g_strdup(model_id); +} + +static const gchar * +fu_logitech_hidpp_device_get_icon(FuLogitechHidPpDeviceKind kind) +{ + if (kind == FU_HIDPP_DEVICE_KIND_KEYBOARD) + return "input-keyboard"; + if (kind == FU_HIDPP_DEVICE_KIND_REMOTE_CONTROL) + return "pda"; // ish + if (kind == FU_HIDPP_DEVICE_KIND_NUMPAD) + return "input-dialpad"; + if (kind == FU_HIDPP_DEVICE_KIND_MOUSE) + return "input-mouse"; + if (kind == FU_HIDPP_DEVICE_KIND_TOUCHPAD) + return "input-touchpad"; + if (kind == FU_HIDPP_DEVICE_KIND_TRACKBALL) + return "input-mouse"; // ish + if (kind == FU_HIDPP_DEVICE_KIND_PRESENTER) + return "pda"; // ish + if (kind == FU_HIDPP_DEVICE_KIND_RECEIVER) + return "preferences-desktop-keyboard"; + return NULL; +} + +static const gchar * +fu_logitech_hidpp_device_get_summary(FuLogitechHidPpDeviceKind kind) +{ + if (kind == FU_HIDPP_DEVICE_KIND_KEYBOARD) + return "Unifying Keyboard"; + if (kind == FU_HIDPP_DEVICE_KIND_REMOTE_CONTROL) + return "Unifying Remote Control"; + if (kind == FU_HIDPP_DEVICE_KIND_NUMPAD) + return "Unifying Number Pad"; + if (kind == FU_HIDPP_DEVICE_KIND_MOUSE) + return "Unifying Mouse"; + if (kind == FU_HIDPP_DEVICE_KIND_TOUCHPAD) + return "Unifying Touchpad"; + if (kind == FU_HIDPP_DEVICE_KIND_TRACKBALL) + return "Unifying Trackball"; + if (kind == FU_HIDPP_DEVICE_KIND_PRESENTER) + return "Unifying Presenter"; + if (kind == FU_HIDPP_DEVICE_KIND_RECEIVER) + return "Unifying Receiver"; + return NULL; +} + +static const gchar * +fu_logitech_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_UNIFIED_BATTERY) + return "UnifiedBattery"; + if (feature == HIDPP_FEATURE_DFU_CONTROL) + return "DfuControl"; + if (feature == HIDPP_FEATURE_DFU_CONTROL_SIGNED) + return "DfuControlSigned"; + if (feature == HIDPP_FEATURE_DFU_CONTROL_BOLT) + return "DfuControlBolt"; + if (feature == HIDPP_FEATURE_DFU) + return "Dfu"; + return NULL; +} + +static gboolean +fu_logitech_hidpp_device_ping(FuLogitechHidPpDevice *self, GError **error) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + gdouble version; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + GPtrArray *children = NULL; + + /* handle failure */ + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = 0x00; /* rootIndex */ + msg->function_id = 0x01 << 4; /* ping */ + msg->data[0] = 0x00; + msg->data[1] = 0x00; + msg->data[2] = 0xaa; /* user-selected value */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, &error_local)) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { + priv->hidpp_version = 1; + return TRUE; + } + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE)) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNREACHABLE); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* device no longer asleep */ + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNREACHABLE); + children = fu_device_get_children(FU_DEVICE(self)); + for (guint i = 0; i < children->len; i++) { + FuDevice *radio = g_ptr_array_index(children, i); + fu_device_remove_flag(radio, FWUPD_DEVICE_FLAG_UNREACHABLE); + } + + /* if the device index is unset, grab it from the reply */ + if (priv->device_idx == HIDPP_DEVICE_IDX_UNSET && + msg->device_id != HIDPP_DEVICE_IDX_UNSET) { + priv->device_idx = msg->device_id; + g_debug("Device index is %02x", priv->device_idx); + } + + /* format version in BCD format */ + if (priv->hidpp_version != FU_HIDPP_VERSION_BLE) { + version = (gdouble)msg->data[0] + ((gdouble)msg->data[1]) / 100.f; + priv->hidpp_version = (guint)version; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_close(FuDevice *device, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + + if (priv->io_channel != NULL) { + if (!fu_io_channel_shutdown(priv->io_channel, error)) + return FALSE; + g_clear_object(&priv->io_channel); + } + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_poll(FuDevice *device, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + const guint timeout = 1; /* ms */ + g_autoptr(GError) error_local = NULL; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open */ + locker = fu_device_locker_new(self, error); + if (locker == NULL) + return FALSE; + + /* flush pending data */ + msg->device_id = priv->device_idx; + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_receive(priv->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_logitech_hidpp_device_ping(self, &error_local)) { + g_warning("failed to ping %s: %s", + fu_device_get_name(FU_DEVICE(self)), + error_local->message); + return TRUE; + } + + /* this is the first time the device has been active */ + if (priv->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_logitech_hidpp_device_open(FuDevice *device, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + const gchar *devpath = g_udev_device_get_device_file(udev_device); + + /* open */ + priv->io_channel = fu_io_channel_new_file(devpath, error); + if (priv->io_channel == NULL) + return FALSE; + + return TRUE; +} + +static void +fu_logitech_hidpp_map_to_string(FuLogitechHidPpHidppMap *map, guint idt, GString *str) +{ + g_autofree gchar *title = g_strdup_printf("Feature%02x", map->idx); + g_autofree gchar *tmp = g_strdup_printf("%s [0x%04x]", + fu_logitech_hidpp_feature_to_string(map->feature), + map->feature); + fu_string_append(str, idt, title, tmp); +} + +static void +fu_logitech_hidpp_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_logitech_hidpp_device_parent_class)->to_string(device, idt, str); + + fu_string_append_ku(str, idt, "HidppVersion", priv->hidpp_version); + fu_string_append_ku(str, idt, "HidppPid", priv->hidpp_pid); + fu_string_append_kx(str, idt, "DeviceIdx", priv->device_idx); + fu_string_append(str, idt, "ModelId", priv->model_id); + for (guint i = 0; i < priv->feature_index->len; i++) { + FuLogitechHidPpHidppMap *map = g_ptr_array_index(priv->feature_index, i); + fu_logitech_hidpp_map_to_string(map, idt, str); + } +} + +static guint8 +fu_logitech_hidpp_device_feature_get_idx(FuLogitechHidPpDevice *self, guint16 feature) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + + for (guint i = 0; i < priv->feature_index->len; i++) { + FuLogitechHidPpHidppMap *map = g_ptr_array_index(priv->feature_index, i); + if (map->feature == feature) + return map->idx; + } + return 0x00; +} + +static gboolean +fu_logitech_hidpp_device_create_radio_child(FuLogitechHidPpDevice *self, + guint8 entity, + guint16 build, + GError **error) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + g_autofree gchar *instance_id = NULL; + g_autofree gchar *logical_id = NULL; + g_autofree gchar *radio_version = NULL; + g_autoptr(FuLogitechHidPpRadio) radio = NULL; + GPtrArray *children = fu_device_get_children(FU_DEVICE(self)); + + /* sanity check */ + if (priv->model_id == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "model ID not set"); + return FALSE; + } + + radio_version = g_strdup_printf("0x%.4x", build); + radio = fu_logitech_hidpp_radio_new(ctx, entity); + fu_device_set_physical_id(FU_DEVICE(radio), fu_device_get_physical_id(FU_DEVICE(self))); + /* + * Use the parent logical id as well as the model id for the + * logical id of the radio child device. This allows the radio + * devices of two devices of the same type (same device type, + * BLE mode) to coexist correctly. + */ + logical_id = + g_strdup_printf("%s-%s", fu_device_get_logical_id(FU_DEVICE(self)), priv->model_id); + fu_device_set_logical_id(FU_DEVICE(radio), logical_id); + instance_id = g_strdup_printf("HIDRAW\\VEN_%04X&MOD_%s&ENT_05", + (guint)FU_UNIFYING_DEVICE_VID, + priv->model_id); + fu_device_add_instance_id(FU_DEVICE(radio), instance_id); + fu_device_set_version(FU_DEVICE(radio), radio_version); + if (!fu_device_setup(FU_DEVICE(radio), error)) + return FALSE; + + /* remove old radio device if it already existed */ + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index(children, i); + if (g_strcmp0(fu_device_get_physical_id(FU_DEVICE(radio)), + fu_device_get_physical_id(child)) == 0 && + g_strcmp0(fu_device_get_logical_id(FU_DEVICE(radio)), + fu_device_get_logical_id(child)) == 0) { + fu_device_remove_child(FU_DEVICE(self), child); + break; + } + } + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(radio)); + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_fetch_firmware_info(FuLogitechHidPpDevice *self, GError **error) +{ + guint8 idx; + guint8 entity_count; + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + gboolean radio_ok = FALSE; + + /* get the feature index */ + idx = fu_logitech_hidpp_device_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 = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* getCount */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->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 = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x01 << 4; /* getInfo */ + msg->data[0] = i; + if (!fu_logitech_hidpp_transfer(priv->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_logitech_hidpp_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); + priv->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); + } else if (msg->data[0] == 5 && + fu_device_has_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO)) { + if (!fu_logitech_hidpp_device_create_radio_child(self, i, build, error)) { + g_prefix_error(error, "failed to create radio: "); + return FALSE; + } + radio_ok = TRUE; + } + } + + /* the device is probably in bootloader mode and the last SoftDevice FW upgrade failed */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO) && + !radio_ok) { + g_debug("no radio found, creating a fake one for recovery"); + if (!fu_logitech_hidpp_device_create_radio_child(self, 1, 0, error)) { + g_prefix_error(error, "failed to create radio: "); + return FALSE; + } + } + + /* not an error, the device just doesn't support this */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_fetch_model_id(FuLogitechHidPpDevice *self, GError **error) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GString) str = g_string_new(NULL); + + /* get the (optional) feature index */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_I_FIRMWARE_INFO); + if (idx == 0x00) + return TRUE; + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* getDeviceInfo */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to get the model ID: "); + return FALSE; + } + + /* ignore extendedModelID in data[13] */ + for (guint i = 7; i < 13; i++) + g_string_append_printf(str, "%02X", msg->data[i]); + fu_logitech_hidpp_device_set_model_id(self, str->str); + + /* add one more instance ID */ + fu_device_add_instance_u16(FU_DEVICE(self), "VEN", FU_UNIFYING_DEVICE_VID); + fu_device_add_instance_str(FU_DEVICE(self), "MOD", priv->model_id); + return fu_device_build_instance_id(FU_DEVICE(self), error, "HIDRAW", "VEN", "MOD", NULL); +} + +static gboolean +fu_logitech_hidpp_device_fetch_battery_level(FuLogitechHidPpDevice *self, GError **error) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + + /* try using HID++2.0 */ + if (priv->hidpp_version >= 2.f) { + guint8 idx; + + /* try the Unified Battery feature first */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_UNIFIED_BATTERY); + if (idx != 0x00) { + gboolean socc = FALSE; /* state of charge capability */ + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* get_capabilities */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to get battery info: "); + return FALSE; + } + if (msg->data[1] & 0x02) + socc = TRUE; + msg->function_id = 0x01 << 4; /* get_status */ + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to get battery info: "); + return FALSE; + } + if (socc) { + fu_device_set_battery_level(FU_DEVICE(self), msg->data[0]); + } else { + switch (msg->data[1]) { + case 1: /* critical */ + fu_device_set_battery_level(FU_DEVICE(self), 5); + break; + case 2: /* low */ + fu_device_set_battery_level(FU_DEVICE(self), 20); + break; + case 4: /* good */ + fu_device_set_battery_level(FU_DEVICE(self), 55); + break; + case 8: /* full */ + fu_device_set_battery_level(FU_DEVICE(self), 90); + break; + default: + g_warning("unknown battery level: 0x%02x", msg->data[1]); + break; + } + } + return TRUE; + } else { + /* fall back to the legacy Battery Level feature */ + idx = fu_logitech_hidpp_device_feature_get_idx( + self, + HIDPP_FEATURE_BATTERY_LEVEL_STATUS); + if (idx != 0x00) { + g_autoptr(FuLogitechHidPpHidppMsg) msg = + fu_logitech_hidpp_msg_new(); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* GetBatteryLevelStatus */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to get battery info: "); + return FALSE; + } + if (msg->data[0] != 0x00) + fu_device_set_battery_level(FU_DEVICE(self), msg->data[0]); + return TRUE; + } + } + } + + /* try HID++1.0 battery mileage */ + if (priv->hidpp_version == 1.f) { + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = HIDPP_SUBID_GET_REGISTER; + msg->function_id = HIDPP_REGISTER_BATTERY_MILEAGE << 4; + msg->hidpp_version = priv->hidpp_version; + if (fu_logitech_hidpp_transfer(priv->io_channel, msg, NULL)) { + if (msg->data[0] != 0x7F) + fu_device_set_battery_level(FU_DEVICE(self), msg->data[0]); + else + g_warning("unknown battery level: 0x%02x", msg->data[0]); + return TRUE; + } + + /* try HID++1.0 battery status instead */ + msg->function_id = HIDPP_REGISTER_BATTERY_STATUS << 4; + if (fu_logitech_hidpp_transfer(priv->io_channel, msg, NULL)) { + switch (msg->data[0]) { + case 1: /* 0 - 10 */ + fu_device_set_battery_level(FU_DEVICE(self), 5); + break; + case 3: /* 11 - 30 */ + fu_device_set_battery_level(FU_DEVICE(self), 20); + break; + case 5: /* 31 - 80 */ + fu_device_set_battery_level(FU_DEVICE(self), 55); + break; + case 7: /* 81 - 100 */ + fu_device_set_battery_level(FU_DEVICE(self), 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_logitech_hidpp_feature_search(FuDevice *device, guint16 feature, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + FuLogitechHidPpHidppMap *map; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + + /* find the idx for the feature */ + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + 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 = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, + "failed to get idx for feature %s [0x%04x]: ", + fu_logitech_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_logitech_hidpp_feature_to_string(feature), + feature); + return FALSE; + } + + /* add to map */ + map = g_new0(FuLogitechHidPpHidppMap, 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", + fu_logitech_hidpp_feature_to_string(feature), + feature, + map->idx); + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_probe(FuDevice *device, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error)) + return FALSE; + + /* nearly... */ + fu_device_add_vendor_id(device, "USB:0x046D"); + + /* + * All devices connected to a Bolt receiver share the same + * physical id, make them unique by using their pairing slot + * (device index) as a basis for their logical id. + */ + if (priv->device_idx != HIDPP_DEVICE_IDX_UNSET && + priv->device_idx != HIDPP_DEVICE_IDX_BLE) { + g_autoptr(GString) id_str = g_string_new(NULL); + g_string_append_printf(id_str, "DEV_IDX=%d", priv->device_idx); + fu_device_set_logical_id(device, id_str->str); + } + + /* this is a non-standard extension */ + fu_device_add_instance_u16(FU_DEVICE(self), + "VID", + fu_udev_device_get_vendor(FU_UDEV_DEVICE(device))); + fu_device_add_instance_u16(FU_DEVICE(self), + "PID", + fu_udev_device_get_model(FU_UDEV_DEVICE(device))); + return fu_device_build_instance_id(FU_DEVICE(self), error, "UFY", "VID", "PID", NULL); +} + +static gboolean +fu_logitech_hidpp_device_setup(FuDevice *device, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + const guint16 map_features[] = {HIDPP_FEATURE_GET_DEVICE_NAME_TYPE, + HIDPP_FEATURE_I_FIRMWARE_INFO, + HIDPP_FEATURE_BATTERY_LEVEL_STATUS, + HIDPP_FEATURE_UNIFIED_BATTERY, + HIDPP_FEATURE_DFU_CONTROL, + HIDPP_FEATURE_DFU_CONTROL_SIGNED, + HIDPP_FEATURE_DFU_CONTROL_BOLT, + HIDPP_FEATURE_DFU, + HIDPP_FEATURE_ROOT}; + + if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_BLE)) { + priv->hidpp_version = FU_HIDPP_VERSION_BLE; + priv->device_idx = HIDPP_DEVICE_IDX_BLE; + /* + * Set the logical ID for BLE devices. Note that for BLE + * devices, physical_id = HID_PHYS = MAC of the BT adapter, + * logical_id = HID_UNIQ = MAC of the device. The physical id is + * not enough to differentiate two BLE devices connected to the + * same adapter. This is done here because private flags + * are not loaded when the probe method runs, so we + * can't tell the device is in BLE mode. + */ + if (!fu_udev_device_set_logical_id(FU_UDEV_DEVICE(device), "hid", error)) + return FALSE; + /* + * BLE devices might not be ready for ping right after + * they come up -> wait a bit before pinging. + */ + g_usleep(G_USEC_PER_SEC); + } + if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_FORCE_RECEIVER_ID)) + priv->device_idx = HIDPP_DEVICE_IDX_RECEIVER; + + /* ping device to get HID++ version */ + if (!fu_logitech_hidpp_device_ping(self, error)) + return FALSE; + + /* did not get ID */ + if (priv->device_idx == HIDPP_DEVICE_IDX_UNSET) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no HID++ ID"); + return FALSE; + } + + /* add known root for HID++2.0 */ + g_ptr_array_set_size(priv->feature_index, 0); + if (priv->hidpp_version >= 2.f) { + FuLogitechHidPpHidppMap *map = g_new0(FuLogitechHidPpHidppMap, 1); + map->idx = 0x00; + map->feature = HIDPP_FEATURE_ROOT; + g_ptr_array_add(priv->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_logitech_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 model ID, typically something like B3630000000000 */ + if (!fu_logitech_hidpp_device_fetch_model_id(self, error)) + return FALSE; + + /* try using HID++2.0 */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_GET_DEVICE_NAME_TYPE); + if (idx != 0x00) { + const gchar *tmp; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x02 << 4; /* getDeviceType */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to get device type: "); + return FALSE; + } + + /* add nice-to-have data */ + tmp = fu_logitech_hidpp_device_get_summary(msg->data[0]); + if (tmp != NULL) + fu_device_set_summary(FU_DEVICE(device), tmp); + tmp = fu_logitech_hidpp_device_get_icon(msg->data[0]); + if (tmp != NULL) + fu_device_add_icon(FU_DEVICE(device), tmp); + } + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_DFU_CONTROL); + if (idx != 0x00) { + fu_device_add_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_remove_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifying"); + } + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_DFU_CONTROL_BOLT); + if (idx == 0x00) + idx = fu_logitech_hidpp_device_feature_get_idx(self, + HIDPP_FEATURE_DFU_CONTROL_SIGNED); + if (idx != 0x00) { + /* check the feature is available */ + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x00 << 4; /* getDfuStatus */ + msg->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(priv->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 { + fu_device_remove_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + fu_device_add_protocol(FU_DEVICE(device), "com.logitech.unifyingsigned"); + fu_device_add_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_DFU); + if (idx != 0x00) { + 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"); + } + /* we do not actually know which protocol when in recovery mode, + * so force the metadata to have the specific regex set up */ + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifying"); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); + } + + /* get the firmware information */ + if (!fu_logitech_hidpp_device_fetch_firmware_info(self, error)) + return FALSE; + + /* get the battery level */ + if (!fu_logitech_hidpp_device_fetch_battery_level(self, error)) + return FALSE; + + /* poll for pings to track active state */ + fu_device_set_poll_interval(device, FU_HIDPP_DEVICE_POLLING_INTERVAL); + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + + /* these may require user action */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_DFU_CONTROL_BOLT); + if (idx == 0x00) + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_DFU_CONTROL); + if (idx != 0x00) { + FuDevice *parent; + g_autoptr(FwupdRequest) request = fwupd_request_new(); + g_autoptr(GError) error_local = NULL; + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = priv->device_idx; + 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 = priv->hidpp_version; + msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | + FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, &error_local)) { + if (fu_device_has_private_flag( + device, + FU_LOGITECH_HIDPP_DEVICE_FLAG_NO_REQUEST_REQUIRED)) { + g_debug("ignoring %s", error_local->message); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to put device into DFU mode: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* so we detect off then on */ + parent = fu_device_get_parent(device); + if (parent != NULL) + fu_device_set_poll_interval(parent, 500); + + /* generate a message if not already set */ + if (!fu_device_has_private_flag( + device, + FU_LOGITECH_HIDPP_DEVICE_FLAG_NO_REQUEST_REQUIRED)) { + if (fu_device_get_update_message(device) == NULL) { + g_autofree gchar *str = NULL; + str = g_strdup_printf( + "%s needs to be manually restarted to complete the update. " + "Please turn it off and on.", + fu_device_get_name(device)); + fu_device_set_update_message(device, str); + } + fwupd_request_set_message(request, fu_device_get_update_message(device)); + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_REMOVE_REPLUG); + fu_device_emit_request(device, request); + } + return TRUE; + } + + /* this can reboot all by itself */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, HIDPP_FEATURE_DFU_CONTROL_SIGNED); + if (idx != 0x00) { + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = priv->device_idx; + 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_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to put device into DFU mode: "); + return FALSE; + } + g_usleep(200 * 1000); + return fu_logitech_hidpp_device_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_logitech_hidpp_device_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_logitech_hidpp_device_write_firmware_pkt(FuLogitechHidPpDevice *self, + guint8 idx, + guint8 cmd, + const guint8 *data, + GError **error) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + guint32 packet_cnt; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + + /* send firmware data */ + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = cmd << 4; /* dfuStart or dfuCmdDataX */ + msg->hidpp_version = priv->hidpp_version; + /* enable transfer workaround for devices paired to Bolt receiver */ + if (priv->device_idx != HIDPP_DEVICE_IDX_UNSET && priv->device_idx != HIDPP_DEVICE_IDX_BLE) + msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_RETRY_STUCK; + if (!fu_memcpy_safe(msg->data, + sizeof(msg->data), + 0x0, /* dst */ + data, + 16, + 0x0, /* src */ + 16, + error)) + return FALSE; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, error)) { + g_prefix_error(error, "failed to supply program data: "); + return FALSE; + } + + /* check error */ + if (!fu_memread_uint32_safe(msg->data, + sizeof(msg->data), + 0x0, + &packet_cnt, + G_BIG_ENDIAN, + error)) + return FALSE; + if (g_getenv("FWUPD_LOGITECH_HIDPP_VERBOSE") != NULL) + g_debug("packet_cnt=0x%04x", packet_cnt); + if (fu_logitech_hidpp_device_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(FuLogitechHidPpHidppMsg) msg2 = fu_logitech_hidpp_msg_new(); + msg2->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID; + if (!fu_logitech_hidpp_receive(priv->io_channel, msg2, 15000, error)) + return FALSE; + if (fu_logitech_hidpp_msg_is_reply(msg, msg2)) { + g_autoptr(GError) error2 = NULL; + if (!fu_logitech_hidpp_device_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_logitech_hidpp_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + gsize sz = 0; + const guint8 *data; + guint8 cmd = 0x04; + guint8 idx; + g_autoptr(GBytes) fw = NULL; + + /* if we're in bootloader mode, we should be able to get this feature */ + idx = fu_logitech_hidpp_device_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; + } + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* flash hardware -- the first data byte is the fw entity */ + data = g_bytes_get_data(fw, &sz); + if (priv->cached_fw_entity != data[0]) { + g_debug("updating cached entity 0x%x with 0x%x", priv->cached_fw_entity, data[0]); + priv->cached_fw_entity = data[0]; + } + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + for (gsize i = 0; i < sz / 16; i++) { + /* send packet and wait for reply */ + if (g_getenv("FWUPD_LOGITECH_HIDPP_VERBOSE") != NULL) + g_debug("send data at addr=0x%04x", (guint)i * 16); + if (!fu_logitech_hidpp_device_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_progress_set_percentage_full(progress, (i + 1) * 16, sz); + } + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_reprobe_cb(FuDevice *device, gpointer user_data, GError **error) +{ + return fu_logitech_hidpp_device_setup(device, error); +} + +gboolean +fu_logitech_hidpp_device_attach(FuLogitechHidPpDevice *self, + guint8 entity, + FuProgress *progress, + GError **error) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + FuDevice *device = FU_DEVICE(self); + guint8 idx; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* if we're in bootloader mode, we should be able to get this feature */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, 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_LONG; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x05 << 4; /* restart */ + msg->data[0] = entity; /* fwEntity */ + msg->hidpp_version = priv->hidpp_version; + msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID | + FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SWID | // inferred? + FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; + if (!fu_logitech_hidpp_transfer(priv->io_channel, msg, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("ignoring '%s' on reset", error_local->message); + } else { + g_prefix_error(&error_local, "failed to restart device: "); + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH)) { + fu_device_set_poll_interval(device, 0); + /* + * Wait for device to become ready after flashing. + * Possible race condition: after the device is reset, Linux might enumerate it as + * a different hidraw device depending on timing. + */ + fu_progress_sleep(progress, 1000); /* ms */ + } else { + /* device file hasn't been unbound/re-bound, just probe again */ + if (!fu_device_retry(device, fu_logitech_hidpp_device_reprobe_cb, 10, NULL, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_attach_cached(FuDevice *device, FuProgress *progress, GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + + if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH)) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return fu_logitech_hidpp_device_attach(self, priv->cached_fw_entity, progress, error); +} + +static gboolean +fu_logitech_hidpp_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(device); + if (g_strcmp0(key, "LogitechHidppModelId") == 0) { + fu_logitech_hidpp_device_set_model_id(self, 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_logitech_hidpp_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_logitech_hidpp_device_finalize(GObject *object) +{ + FuLogitechHidPpDevice *self = FU_HIDPP_DEVICE(object); + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + g_ptr_array_unref(priv->feature_index); + g_free(priv->model_id); + G_OBJECT_CLASS(fu_logitech_hidpp_device_parent_class)->finalize(object); +} + +static gboolean +fu_logitech_hidpp_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + if (parent != NULL) + fu_device_set_poll_interval(parent, FU_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL); + + return TRUE; +} + +static void +fu_logitech_hidpp_device_class_init(FuLogitechHidPpDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_logitech_hidpp_device_finalize; + klass_device->setup = fu_logitech_hidpp_device_setup; + klass_device->open = fu_logitech_hidpp_device_open; + klass_device->close = fu_logitech_hidpp_device_close; + klass_device->write_firmware = fu_logitech_hidpp_device_write_firmware; + klass_device->attach = fu_logitech_hidpp_device_attach_cached; + klass_device->detach = fu_logitech_hidpp_device_detach; + klass_device->poll = fu_logitech_hidpp_device_poll; + klass_device->to_string = fu_logitech_hidpp_device_to_string; + klass_device->probe = fu_logitech_hidpp_device_probe; + klass_device->set_quirk_kv = fu_logitech_hidpp_device_set_quirk_kv; + klass_device->cleanup = fu_logitech_hidpp_device_cleanup; + klass_device->set_progress = fu_logitech_hidpp_device_set_progress; +} + +static void +fu_logitech_hidpp_device_init(FuLogitechHidPpDevice *self) +{ + FuLogitechHidPpDevicePrivate *priv = GET_PRIVATE(self); + priv->device_idx = HIDPP_DEVICE_IDX_UNSET; + priv->feature_index = g_ptr_array_new_with_free_func(g_free); + 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_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_vendor(FU_DEVICE(self), "Logitech"); + fu_device_retry_set_delay(FU_DEVICE(self), 1000); + fu_device_register_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_DEVICE_FLAG_FORCE_RECEIVER_ID, + "force-receiver-id"); + fu_device_register_private_flag(FU_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_FLAG_BLE, "ble"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH, + "rebind-attach"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_DEVICE_FLAG_NO_REQUEST_REQUIRED, + "no-request-required"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO, + "add-radio"); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + fu_device_set_battery_threshold(FU_DEVICE(self), 20); +} + +FuLogitechHidPpDevice * +fu_logitech_hidpp_device_new(FuUdevDevice *parent) +{ + FuLogitechHidPpDevice *self = NULL; + FuLogitechHidPpDevicePrivate *priv; + self = g_object_new(FU_TYPE_HIDPP_DEVICE, + "context", + fu_device_get_context(FU_DEVICE(parent)), + "physical-id", + fu_device_get_physical_id(FU_DEVICE(parent)), + "udev-device", + fu_udev_device_get_dev(parent), + NULL); + priv = GET_PRIVATE(self); + priv->io_channel = fu_logitech_hidpp_runtime_get_io_channel(FU_HIDPP_RUNTIME(parent)); + return self; +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-device.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f8c4a87b1ebf472e4fa3f68ccb78cc14599070f3 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-device.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_HIDPP_DEVICE (fu_logitech_hidpp_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuLogitechHidPpDevice, + fu_logitech_hidpp_device, + FU, + HIDPP_DEVICE, + FuUdevDevice) + +struct _FuLogitechHidPpDeviceClass { + FuUdevDeviceClass parent_class; + /* TODO: overridable methods */ +}; + +/** + * FU_LOGITECH_HIDPP_DEVICE_FLAG_FORCE_RECEIVER_ID: + * + * Device is a unifying or Bolt receiver. + * + * Since: 1.7.0 + */ +#define FU_LOGITECH_HIDPP_DEVICE_FLAG_FORCE_RECEIVER_ID (1 << 0) + +/** + * FU_LOGITECH_HIDPP_DEVICE_FLAG_BLE: + * + * Device is connected using Bluetooth Low Energy. + * + * Since: 1.7.0 + */ +#define FU_LOGITECH_HIDPP_DEVICE_FLAG_BLE (1 << 1) + +/** + * FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH: + * + * The device file is automatically unbound and re-bound after the + * device is attached. + * + * Since: 1.7.0 + */ +#define FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH (1 << 2) + +/** + * FU_LOGITECH_HIDPP_DEVICE_FLAG_NO_REQUEST_REQUIRED: + * + * No user-action is required for detach and attach. + * + * Since: 1.7.0 + */ +#define FU_LOGITECH_HIDPP_DEVICE_FLAG_NO_REQUEST_REQUIRED (1 << 3) + +/** + * FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO: + * + * The device should add a softdevice (index 0x5), typically a radio. + * + * Since: 1.7.0 + */ +#define FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO (1 << 5) + +void +fu_logitech_hidpp_device_set_device_idx(FuLogitechHidPpDevice *self, guint8 device_idx); +guint16 +fu_logitech_hidpp_device_get_hidpp_pid(FuLogitechHidPpDevice *self); +void +fu_logitech_hidpp_device_set_hidpp_pid(FuLogitechHidPpDevice *self, guint16 hidpp_pid); +const gchar * +fu_logitech_hidpp_device_get_model_id(FuLogitechHidPpDevice *self); +gboolean +fu_logitech_hidpp_device_attach(FuLogitechHidPpDevice *self, + guint8 entity, + FuProgress *progress, + GError **error); +FuLogitechHidPpDevice * +fu_logitech_hidpp_device_new(FuUdevDevice *parent); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c new file mode 100644 index 0000000000000000000000000000000000000000..e03bca4ca7a07c3b46d832738099410cc06c1099 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-hidpp-hidpp-msg.h" +#include "fu-logitech-hidpp-hidpp.h" + +FuLogitechHidPpHidppMsg * +fu_logitech_hidpp_msg_new(void) +{ + return g_new0(FuLogitechHidPpHidppMsg, 1); +} + +const gchar * +fu_logitech_hidpp_msg_dev_id_to_string(FuLogitechHidPpHidppMsg *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + if (msg->device_id == HIDPP_DEVICE_IDX_WIRED) + return "wired"; + if (msg->device_id == HIDPP_DEVICE_IDX_RECEIVER) + return "receiver"; + if (msg->device_id == HIDPP_DEVICE_IDX_UNSET) + return "unset"; + return NULL; +} + +const gchar * +fu_logitech_hidpp_msg_rpt_id_to_string(FuLogitechHidPpHidppMsg *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_logitech_hidpp_msg_get_payload_length(FuLogitechHidPpHidppMsg *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_logitech_hidpp_msg_fcn_id_to_string(FuLogitechHidPpHidppMsg *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"; + if (msg->function_id == BOLT_REGISTER_RECEIVER_FW_INFORMATION) + return "receiver-fw-information"; + break; + default: + break; + } + return NULL; +} + +const gchar * +fu_logitech_hidpp_msg_sub_id_to_string(FuLogitechHidPpHidppMsg *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_logitech_hidpp_msg_is_reply(FuLogitechHidPpHidppMsg *msg1, FuLogitechHidPpHidppMsg *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_IDX_UNSET && + msg2->device_id != HIDPP_DEVICE_IDX_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_logitech_hidpp_msg_is_error(FuLogitechHidPpHidppMsg *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_logitech_hidpp_msg_copy(FuLogitechHidPpHidppMsg *msg_dst, const FuLogitechHidPpHidppMsg *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_logitech_hidpp_msg_is_hidpp10_compat(FuLogitechHidPpHidppMsg *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_logitech_hidpp_msg_verify_swid(FuLogitechHidPpHidppMsg *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 --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h new file mode 100644 index 0000000000000000000000000000000000000000..e9c651427b08ab93994e503b19d809287ddfc724 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +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, + FU_UNIFYING_HIDPP_MSG_FLAG_RETRY_STUCK = 1 << 4, + /*< private >*/ + FU_UNIFYING_HIDPP_MSG_FLAG_LAST +} FuLogitechHidPpHidppMsgFlags; + +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; +} FuLogitechHidPpHidppMsg; + +/* 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(FuLogitechHidPpHidppMsg, g_free); +#pragma clang diagnostic pop + +FuLogitechHidPpHidppMsg * +fu_logitech_hidpp_msg_new(void); +void +fu_logitech_hidpp_msg_copy(FuLogitechHidPpHidppMsg *msg_dst, + const FuLogitechHidPpHidppMsg *msg_src); +gsize +fu_logitech_hidpp_msg_get_payload_length(FuLogitechHidPpHidppMsg *msg); +gboolean +fu_logitech_hidpp_msg_is_reply(FuLogitechHidPpHidppMsg *msg1, FuLogitechHidPpHidppMsg *msg2); +gboolean +fu_logitech_hidpp_msg_is_hidpp10_compat(FuLogitechHidPpHidppMsg *msg); +gboolean +fu_logitech_hidpp_msg_is_error(FuLogitechHidPpHidppMsg *msg, GError **error); +gboolean +fu_logitech_hidpp_msg_verify_swid(FuLogitechHidPpHidppMsg *msg); + +const gchar * +fu_logitech_hidpp_msg_dev_id_to_string(FuLogitechHidPpHidppMsg *msg); +const gchar * +fu_logitech_hidpp_msg_rpt_id_to_string(FuLogitechHidPpHidppMsg *msg); +const gchar * +fu_logitech_hidpp_msg_sub_id_to_string(FuLogitechHidPpHidppMsg *msg); +const gchar * +fu_logitech_hidpp_msg_fcn_id_to_string(FuLogitechHidPpHidppMsg *msg); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c new file mode 100644 index 0000000000000000000000000000000000000000..dc16fa192e1d0ecfa4fbe60380e9ee5af4578301 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-hidpp.h" + +static gchar * +fu_logitech_hidpp_msg_to_string(FuLogitechHidPpHidppMsg *msg) +{ + const gchar *tmp; + g_autoptr(GError) error = NULL; + g_autoptr(GString) flags_str = g_string_new(NULL); + g_autoptr(GString) 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_logitech_hidpp_msg_rpt_id_to_string(msg)); + tmp = fu_logitech_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_logitech_hidpp_msg_sub_id_to_string(msg)); + g_string_append_printf(str, + "function-id: %02x [%s]\n", + msg->function_id, + fu_logitech_hidpp_msg_fcn_id_to_string(msg)); + if (!fu_logitech_hidpp_msg_is_error(msg, &error)) { + g_string_append_printf(str, "error: %s\n", error->message); + } + return g_string_free(g_steal_pointer(&str), FALSE); +} + +gboolean +fu_logitech_hidpp_send(FuIOChannel *io_channel, + FuLogitechHidPpHidppMsg *msg, + guint timeout, + GError **error) +{ + gsize len = fu_logitech_hidpp_msg_get_payload_length(msg); + FuIOChannelFlags write_flags = FU_IO_CHANNEL_FLAG_FLUSH_INPUT; + + /* only for HID++2.0 */ + if (msg->hidpp_version >= 2.f) + msg->function_id |= FU_UNIFYING_HIDPP_MSG_SW_ID; + + /* force long reports for BLE-direct devices */ + if (msg->hidpp_version == FU_HIDPP_VERSION_BLE) { + msg->report_id = HIDPP_REPORT_ID_LONG; + len = 20; + } + + /* detailed debugging */ + if (g_getenv("FWUPD_LOGITECH_HIDPP_VERBOSE") != NULL) { + g_autofree gchar *str = fu_logitech_hidpp_msg_to_string(msg); + fu_dump_raw(G_LOG_DOMAIN, "host->device", (guint8 *)msg, len); + g_print("%s", str); + } + + /* only use blocking IO when it will be a short timeout for reboot */ + if ((msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) == 0) + write_flags |= FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO; + + /* HID */ + if (!fu_io_channel_write_raw(io_channel, (guint8 *)msg, len, timeout, write_flags, error)) { + g_prefix_error(error, "failed to send: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_logitech_hidpp_receive(FuIOChannel *io_channel, + FuLogitechHidPpHidppMsg *msg, + guint timeout, + GError **error) +{ + gsize read_size = 0; + + if (!fu_io_channel_read_raw(io_channel, + (guint8 *)msg, + sizeof(FuLogitechHidPpHidppMsg), + &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_LOGITECH_HIDPP_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "device->host", (guint8 *)msg, read_size); + if (read_size < fu_logitech_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_logitech_hidpp_msg_get_payload_length(msg)); + return FALSE; + } + + /* detailed debugging */ + if (g_getenv("FWUPD_LOGITECH_HIDPP_VERBOSE") != NULL) { + g_autofree gchar *str = fu_logitech_hidpp_msg_to_string(msg); + g_print("%s", str); + } + + /* success */ + return TRUE; +} + +gboolean +fu_logitech_hidpp_transfer(FuIOChannel *io_channel, FuLogitechHidPpHidppMsg *msg, GError **error) +{ + guint timeout = FU_UNIFYING_DEVICE_TIMEOUT_MS; + guint ignore_cnt = 0; + g_autoptr(FuLogitechHidPpHidppMsg) msg_tmp = fu_logitech_hidpp_msg_new(); + + /* increase timeout for some operations */ + if (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT) + timeout *= 10; + + /* send request */ + if (!fu_logitech_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 (msg->flags & FU_UNIFYING_HIDPP_MSG_FLAG_RETRY_STUCK) { + g_autoptr(GError) error_local = NULL; + /* retry the send once case the device is "stuck" */ + if (!fu_logitech_hidpp_receive(io_channel, msg_tmp, 1000, &error_local)) { + if (!fu_logitech_hidpp_send(io_channel, msg, timeout, error)) { + return FALSE; + } + if (!fu_logitech_hidpp_receive(io_channel, + msg_tmp, + timeout, + error)) { + g_prefix_error(error, "failed to receive: "); + return FALSE; + } + } + } else { + if (!fu_logitech_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_logitech_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_logitech_hidpp_msg_is_error(msg_tmp, error)) + return FALSE; + + /* is valid reply */ + if (fu_logitech_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_logitech_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_logitech_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_logitech_hidpp_msg_copy(msg, msg_tmp); + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.h new file mode 100644 index 0000000000000000000000000000000000000000..f7a6f9472bbd2b58592ffee324856917f7e19e10 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* + * Based on the HID++ documentation provided by Nestor Lopez Casado at: + * https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing + */ +#define HIDPP_DEVICE_IDX_WIRED 0x00 +#define HIDPP_DEVICE_IDX_RECEIVER 0xFF +#define HIDPP_DEVICE_IDX_BLE 0xFF +#define HIDPP_DEVICE_IDX_UNSET 0x00 + +#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 + +/* + * Bolt registers + */ +#define BOLT_REGISTER_HIDPP_REPORTING 0x00 +#define BOLT_REGISTER_CONNECTION_STATE 0x02 +#define BOLT_REGISTER_DEVICE_ACTIVITY 0xB3 +#define BOLT_REGISTER_PAIRING_INFORMATION 0xB5 +#define BOLT_REGISTER_PERFORM_DEVICE_DISCOVERY 0xC0 +#define BOLT_REGISTER_PERFORM_DEVICE_PAIRING 0xC1 +#define BOLT_REGISTER_RESET 0xF2 +#define BOLT_REGISTER_RECEIVER_FW_INFORMATION 0xF4 +#define BOLT_REGISTER_DFU_CONTROL 0xF5 +#define BOLT_REGISTER_UNIQUE_IDENTIFIER 0xFB + +/* + * 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_CONTROL_BOLT 0x00c3 +#define HIDPP_FEATURE_DFU 0x00d0 +#define HIDPP_FEATURE_BATTERY_LEVEL_STATUS 0x1000 +#define HIDPP_FEATURE_UNIFIED_BATTERY 0x1004 +#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-logitech-hidpp-hidpp-msg.h" + +gboolean +fu_logitech_hidpp_send(FuIOChannel *self, + FuLogitechHidPpHidppMsg *msg, + guint timeout, + GError **error); +gboolean +fu_logitech_hidpp_receive(FuIOChannel *self, + FuLogitechHidPpHidppMsg *msg, + guint timeout, + GError **error); +gboolean +fu_logitech_hidpp_transfer(FuIOChannel *self, FuLogitechHidPpHidppMsg *msg, GError **error); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..613f7c266373294b221d5dbdcc3ded5e19d1c4e4 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-hidpp-bootloader-nordic.h" +#include "fu-logitech-hidpp-bootloader-texas.h" +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-device.h" +#include "fu-logitech-hidpp-plugin.h" +#include "fu-logitech-hidpp-runtime-bolt.h" +#include "fu-logitech-hidpp-runtime-unifying.h" + +struct _FuLogitechHidppPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuLogitechHidppPlugin, fu_logitech_hidpp_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_logitech_hidpp_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + /* check the kernel has CONFIG_HIDRAW */ + if (!g_file_test("/sys/class/hidraw", G_FILE_TEST_IS_DIR)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no kernel support for CONFIG_HIDRAW"); + return FALSE; + } + return TRUE; +} + +static void +fu_logitech_hidpp_plugin_init(FuLogitechHidppPlugin *self) +{ +} + +static void +fu_logitech_hidpp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "LogitechHidppModelId"); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "unifying"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_UNIFYING_BOOTLOADER_NORDIC); + fu_plugin_add_device_gtype(plugin, FU_TYPE_UNIFYING_BOOTLOADER_TEXAS); + fu_plugin_add_device_gtype(plugin, FU_TYPE_HIDPP_RUNTIME_UNIFYING); + fu_plugin_add_device_gtype(plugin, FU_TYPE_HIDPP_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_HIDPP_RUNTIME_BOLT); +} + +static void +fu_logitech_hidpp_plugin_class_init(FuLogitechHidppPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_logitech_hidpp_plugin_constructed; + plugin_class->startup = fu_logitech_hidpp_plugin_startup; +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..5876ced805d5a6e364277e5ecf2311de9bca53b6 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLogitechHidppPlugin, + fu_logitech_hidpp_plugin, + FU, + LOGITECH_HIDPP_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c new file mode 100644 index 0000000000000000000000000000000000000000..c0f9be4b7b1a749c45a4dbd7a102c7e86ed68707 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-hidpp-device.h" +#include "fu-logitech-hidpp-radio.h" + +struct _FuLogitechHidPpRadio { + FuDevice parent_instance; + guint8 entity; +}; + +G_DEFINE_TYPE(FuLogitechHidPpRadio, fu_logitech_hidpp_radio, FU_TYPE_DEVICE) + +static void +fu_logitech_hidpp_radio_to_string(FuDevice *device, guint idt, GString *str) +{ + FuLogitechHidPpRadio *self = FU_HIDPP_RADIO(device); + fu_string_append_kx(str, idt, "Entity", self->entity); +} + +static gboolean +fu_logitech_hidpp_radio_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuLogitechHidPpRadio *self = FU_HIDPP_RADIO(device); + FuDevice *parent = fu_device_get_parent(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return fu_logitech_hidpp_device_attach(FU_HIDPP_DEVICE(parent), + self->entity, + progress, + error); +} + +static gboolean +fu_logitech_hidpp_radio_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + if (!fu_device_has_flag(parent, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return fu_device_detach_full(parent, progress, error); +} + +static gboolean +fu_logitech_hidpp_radio_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GBytes) fw = NULL; + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* open */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware(parent, fw, progress, flags, error); +} + +static void +fu_logitech_hidpp_radio_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 3, "reload"); +} + +static void +fu_logitech_hidpp_radio_init(FuLogitechHidPpRadio *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_vendor(FU_DEVICE(self), "Logitech"); + fu_device_set_name(FU_DEVICE(self), "Radio"); + fu_device_set_install_duration(FU_DEVICE(self), 270); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_internal_flag(FU_DEVICE(self), + FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_BATTERY); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); +} + +static void +fu_logitech_hidpp_radio_class_init(FuLogitechHidPpRadioClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->detach = fu_logitech_hidpp_radio_detach; + klass_device->attach = fu_logitech_hidpp_radio_attach; + klass_device->write_firmware = fu_logitech_hidpp_radio_write_firmware; + klass_device->to_string = fu_logitech_hidpp_radio_to_string; + klass_device->set_progress = fu_logitech_hidpp_radio_set_progress; +} + +FuLogitechHidPpRadio * +fu_logitech_hidpp_radio_new(FuContext *ctx, guint8 entity) +{ + FuLogitechHidPpRadio *self = NULL; + + self = g_object_new(FU_TYPE_LOGITECH_HIDPP_RADIO, "context", ctx, NULL); + self->entity = entity; + return self; +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h new file mode 100644 index 0000000000000000000000000000000000000000..ed684a86b511c6947d54ccf6b7656230b59ad605 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_LOGITECH_HIDPP_RADIO (fu_logitech_hidpp_radio_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechHidPpRadio, fu_logitech_hidpp_radio, FU, HIDPP_RADIO, FuDevice) + +FuLogitechHidPpRadio * +fu_logitech_hidpp_radio_new(FuContext *ctx, guint8 entity); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c new file mode 100644 index 0000000000000000000000000000000000000000..52a7cc0d7b560317a77ebf556ed44eec2f60ea2a --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-device.h" +#include "fu-logitech-hidpp-hidpp.h" +#include "fu-logitech-hidpp-radio.h" +#include "fu-logitech-hidpp-runtime-bolt.h" + +struct _FuLogitechHidPpRuntimeBolt { + FuLogitechHidPpRuntime parent_instance; + guint8 pairing_slots; +}; + +G_DEFINE_TYPE(FuLogitechHidPpRuntimeBolt, fu_logitech_hidpp_runtime_bolt, FU_TYPE_HIDPP_RUNTIME) + +static gboolean +fu_logitech_hidpp_runtime_bolt_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + + msg->report_id = HIDPP_REPORT_ID_LONG; + msg->device_id = HIDPP_DEVICE_IDX_RECEIVER; + msg->sub_id = HIDPP_SUBID_SET_LONG_REGISTER; + msg->function_id = BOLT_REGISTER_DFU_CONTROL; + msg->data[0] = 1; /* Enable DFU */ + msg->data[4] = 'P'; + msg->data[5] = 'R'; + msg->data[6] = 'E'; + msg->hidpp_version = 1; + msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT; + if (!fu_logitech_hidpp_send(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + FU_UNIFYING_DEVICE_TIMEOUT_MS, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_WRITE)) { + g_debug("failed to detach to bootloader: %s", error_local->message); + } else { + g_prefix_error(&error_local, "failed to detach to bootloader: "); + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_logitech_hidpp_runtime_bolt_to_string(FuDevice *device, guint idt, GString *str) +{ + FuLogitechHidPpRuntimeBolt *self = FU_HIDPP_RUNTIME_BOLT(device); + + FU_DEVICE_CLASS(fu_logitech_hidpp_runtime_bolt_parent_class)->to_string(device, idt, str); + fu_string_append_ku(str, idt, "PairingSlots", self->pairing_slots); +} + +static FuLogitechHidPpDevice * +fu_logitech_hidpp_runtime_bolt_find_paired_device(FuDevice *device, guint16 hidpp_pid) +{ + GPtrArray *children = fu_device_get_children(device); + + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index(children, i); + if (FU_IS_HIDPP_DEVICE(child) && + fu_logitech_hidpp_device_get_hidpp_pid(FU_HIDPP_DEVICE(child)) == hidpp_pid) + return FU_HIDPP_DEVICE(g_object_ref(child)); + } + + return NULL; +} + +static gchar * +fu_logitech_hidpp_runtime_bolt_query_device_name(FuLogitechHidPpRuntime *self, + guint8 slot, + GError **error) +{ + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GString) dev_name = g_string_new(NULL); + guint namelen; + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_IDX_RECEIVER; + msg->sub_id = HIDPP_SUBID_GET_LONG_REGISTER; + msg->function_id = BOLT_REGISTER_PAIRING_INFORMATION; + msg->data[0] = 0x60 | slot; /* device name */ + msg->data[1] = 1; + msg->hidpp_version = 1; + if (!fu_logitech_hidpp_transfer(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + error)) { + g_prefix_error(error, "failed to retrieve the device name for slot %d: ", slot); + return NULL; + } + namelen = msg->data[2]; + g_string_append_len(dev_name, (const char *)(&(msg->data[3])), namelen); + + return g_string_free(g_steal_pointer(&dev_name), FALSE); +} + +static gboolean +fu_logitech_hidpp_runtime_bolt_update_paired_device(FuLogitechHidPpRuntimeBolt *self, + FuLogitechHidPpHidppMsg *msg, + GError **error) +{ + FuLogitechHidPpRuntime *runtime = FU_HIDPP_RUNTIME(self); + gboolean reachable = FALSE; + guint16 hidpp_pid; + g_autoptr(FuLogitechHidPpDevice) child = NULL; + + if ((msg->data[0] & 0x40) == 0) + reachable = TRUE; + hidpp_pid = (msg->data[1] << 8) | msg->data[2]; + + child = fu_logitech_hidpp_runtime_bolt_find_paired_device(FU_DEVICE(self), hidpp_pid); + if (child != NULL) { + g_debug("%s [%s] is reachable:%i", + fu_device_get_name(FU_DEVICE(child)), + fu_device_get_name(FU_DEVICE(child)), + reachable); + if (reachable) { + g_autoptr(FuDeviceLocker) locker = NULL; + + /* known paired & reachable */ + fu_device_probe_invalidate(FU_DEVICE(child)); + locker = fu_device_locker_new(FU_DEVICE(child), error); + if (locker == NULL) { + g_prefix_error(error, "cannot rescan paired device: "); + return FALSE; + } + fu_device_remove_flag(FU_DEVICE(child), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } else { + GPtrArray *children = NULL; + /* any successful 'ping' will clear this */ + fu_device_add_flag(FU_DEVICE(child), FWUPD_DEVICE_FLAG_UNREACHABLE); + children = fu_device_get_children(FU_DEVICE(child)); + for (guint i = 0; i < children->len; i++) { + FuDevice *radio = g_ptr_array_index(children, i); + fu_device_add_flag(radio, FWUPD_DEVICE_FLAG_UNREACHABLE); + } + } + } else if (reachable) { + g_autofree gchar *name = NULL; + + /* unknown paired device, reachable state */ + name = fu_logitech_hidpp_runtime_bolt_query_device_name(runtime, + msg->device_id, + error); + if (name == NULL) + return FALSE; + child = fu_logitech_hidpp_device_new(FU_UDEV_DEVICE(self)); + fu_device_set_name(FU_DEVICE(child), name); + fu_logitech_hidpp_device_set_device_idx(child, msg->device_id); + fu_logitech_hidpp_device_set_hidpp_pid(child, hidpp_pid); + if (!fu_device_probe(FU_DEVICE(child), error)) + return FALSE; + if (!fu_device_setup(FU_DEVICE(child), error)) + return FALSE; + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(child)); + } else { + /* unknown paired device, unreachable state */ + g_warning("unknown paired device 0x%0x in slot %d (unreachable)", + hidpp_pid, + msg->device_id); + } + + return TRUE; +} + +void +fu_logitech_hidpp_runtime_bolt_poll_peripherals(FuDevice *device) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimeBolt *bolt = FU_HIDPP_RUNTIME_BOLT(device); + + for (guint i = 1; i <= bolt->pairing_slots; i++) { + g_autofree gchar *name = NULL; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + guint16 hidpp_pid; + + name = fu_logitech_hidpp_runtime_bolt_query_device_name(self, i, &error_local); + if (name == NULL) { + g_debug("Can't query paired device name for slot %u", i); + continue; + } + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_IDX_RECEIVER; + msg->sub_id = HIDPP_SUBID_GET_LONG_REGISTER; + msg->function_id = BOLT_REGISTER_PAIRING_INFORMATION; + msg->data[0] = 0x50 | i; /* pairing information */ + msg->hidpp_version = 1; + if (!fu_logitech_hidpp_transfer(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + &error_local)) + continue; + hidpp_pid = (msg->data[2] << 8) | msg->data[3]; + if ((msg->data[1] & 0x40) == 0) { + /* paired device is reachable */ + g_autoptr(FuLogitechHidPpDevice) child = NULL; + child = fu_logitech_hidpp_device_new(FU_UDEV_DEVICE(device)); + fu_device_set_install_duration(FU_DEVICE(child), 270); + fu_device_add_private_flag(FU_DEVICE(child), + FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO); + fu_device_set_name(FU_DEVICE(child), name); + fu_logitech_hidpp_device_set_device_idx(child, i); + fu_logitech_hidpp_device_set_hidpp_pid(child, hidpp_pid); + if (!fu_device_probe(FU_DEVICE(child), &error_local)) + continue; + if (!fu_device_setup(FU_DEVICE(child), &error_local)) + continue; + fu_device_add_child(device, FU_DEVICE(child)); + } + } +} + +static gboolean +fu_logitech_hidpp_runtime_bolt_process_notification(FuLogitechHidPpRuntimeBolt *self, + FuLogitechHidPpHidppMsg *msg) +{ + g_autoptr(GError) error_local = NULL; + + /* HID++1.0 error */ + if (!fu_logitech_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: + if (!fu_logitech_hidpp_runtime_bolt_update_paired_device(self, + msg, + &error_local)) { + g_warning("failed to update paired device status: %s", + error_local->message); + return FALSE; + } + break; + case HIDPP_SUBID_LINK_QUALITY: + g_debug("ignoring link quality message"); + break; + case HIDPP_SUBID_ERROR_MSG: + g_debug("ignoring error message"); + break; + default: + g_debug("unknown SubID %02x", msg->sub_id); + break; + } + } + + return TRUE; +} + +static FuLogitechHidPpHidppMsg * +fu_logitech_hidpp_runtime_bolt_find_newest_msg(GPtrArray *msgs, guint8 device_id, guint8 sub_id) +{ + for (guint i = 0; i < msgs->len; i++) { + FuLogitechHidPpHidppMsg *msg = g_ptr_array_index(msgs, msgs->len - (i + 1)); + if (msg->device_id == device_id && msg->sub_id == sub_id) + return msg; + } + return NULL; +} + +static gboolean +fu_logitech_hidpp_runtime_bolt_poll(FuDevice *device, GError **error) +{ + FuLogitechHidPpRuntime *runtime = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimeBolt *self = FU_HIDPP_RUNTIME_BOLT(device); + const guint timeout = 1; /* ms */ + g_autoptr(GPtrArray) msgs = g_ptr_array_new_with_free_func(g_free); + + /* open -- not a locker as we have no kernel driver */ + if (!fu_device_open(device, error)) + return FALSE; + + /* drain all the pending messages into the array */ + while (TRUE) { + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + msg->hidpp_version = 1; + if (!fu_logitech_hidpp_receive(fu_logitech_hidpp_runtime_get_io_channel(runtime), + msg, + timeout, + &error_local)) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) + break; + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "error polling Bolt receiver: "); + return FALSE; + } + g_ptr_array_add(msgs, g_steal_pointer(&msg)); + } + + /* process messages in order, but discard any message with a newer version */ + for (guint i = 0; i < msgs->len; i++) { + FuLogitechHidPpHidppMsg *msg = g_ptr_array_index(msgs, i); + FuLogitechHidPpHidppMsg *msg_newest; + + /* find the newest message with the matching device and sub-IDs */ + msg_newest = fu_logitech_hidpp_runtime_bolt_find_newest_msg(msgs, + msg->device_id, + msg->sub_id); + if (msg != msg_newest) { + g_debug("ignoring duplicate message device-id:%02x [%s] sub-id:%02x [%s]", + msg->device_id, + fu_logitech_hidpp_msg_dev_id_to_string(msg), + msg->sub_id, + fu_logitech_hidpp_msg_sub_id_to_string(msg)); + continue; + } + fu_logitech_hidpp_runtime_bolt_process_notification(self, msg); + } + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_bolt_setup_internal(FuDevice *device, GError **error) +{ + FuContext *ctx = fu_device_get_context(device); + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimeBolt *bolt = FU_HIDPP_RUNTIME_BOLT(device); + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_IDX_RECEIVER; + msg->sub_id = HIDPP_SUBID_GET_LONG_REGISTER; + msg->function_id = BOLT_REGISTER_PAIRING_INFORMATION; + msg->data[0] = 0x02; /* FW Version (contains the number of pairing slots) */ + msg->hidpp_version = 1; + if (!fu_logitech_hidpp_transfer(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + error)) { + g_prefix_error(error, "failed to fetch the number of pairing slots: "); + return FALSE; + } + bolt->pairing_slots = msg->data[8]; + + /* + * TODO: Iterate only over the first three entity indexes for + * now. + */ + for (guint i = 0; i < 3; i++) { + guint16 version_raw = 0; + g_autofree gchar *version = NULL; + g_autoptr(FuLogitechHidPpRadio) radio = NULL; + g_autoptr(GString) radio_version = NULL; + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_IDX_RECEIVER; + msg->sub_id = HIDPP_SUBID_GET_LONG_REGISTER; + msg->function_id = BOLT_REGISTER_RECEIVER_FW_INFORMATION; + msg->data[0] = i; + msg->hidpp_version = 1; + if (!fu_logitech_hidpp_transfer(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + error)) { + g_prefix_error(error, "failed to read device config: "); + return FALSE; + } + + switch (msg->data[0]) { + case 0: + /* main application */ + if (!fu_memread_uint16_safe(msg->data, + sizeof(msg->data), + 0x03, + &version_raw, + G_BIG_ENDIAN, + error)) + return FALSE; + version = fu_logitech_hidpp_format_version("MPR", + msg->data[1], + msg->data[2], + version_raw); + fu_device_set_version(device, version); + break; + case 1: + /* bootloader */ + if (!fu_memread_uint16_safe(msg->data, + sizeof(msg->data), + 0x03, + &version_raw, + G_BIG_ENDIAN, + error)) + return FALSE; + version = fu_logitech_hidpp_format_version("BOT", + msg->data[1], + msg->data[2], + version_raw); + fu_device_set_version_bootloader(device, version); + break; + case 5: + /* SoftDevice */ + radio_version = g_string_new(NULL); + radio = fu_logitech_hidpp_radio_new(ctx, i); + fu_device_add_instance_u16( + FU_DEVICE(radio), + "VEN", + fu_udev_device_get_vendor(FU_UDEV_DEVICE(device))); + fu_device_add_instance_u16( + FU_DEVICE(radio), + "DEV", + fu_udev_device_get_model(FU_UDEV_DEVICE(device))); + fu_device_add_instance_u8(FU_DEVICE(radio), "ENT", msg->data[0]); + fu_device_set_physical_id(FU_DEVICE(radio), + fu_device_get_physical_id(device)); + fu_device_set_logical_id(FU_DEVICE(radio), "Receiver_SoftDevice"); + if (!fu_device_build_instance_id(FU_DEVICE(radio), + error, + "HIDRAW", + "VEN", + "DEV", + "ENT", + NULL)) + return FALSE; + if (!fu_memread_uint16_safe(msg->data, + sizeof(msg->data), + 0x03, + &version_raw, + G_BIG_ENDIAN, + error)) + return FALSE; + g_string_append_printf(radio_version, "0x%.4x", version_raw); + fu_device_set_version(FU_DEVICE(radio), radio_version->str); + fu_device_add_child(device, FU_DEVICE(radio)); + break; + default: + break; + } + } + + /* enable HID++ notifications */ + if (!fu_logitech_hidpp_runtime_enable_notifications(self, error)) { + g_prefix_error(error, "failed to enable notifications: "); + return FALSE; + } + fu_logitech_hidpp_runtime_bolt_poll_peripherals(device); + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_bolt_setup(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + for (guint i = 0; i < 5; i++) { + g_clear_error(&error_local); + /* 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_logitech_hidpp_runtime_bolt_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_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; +} + +static void +fu_logitech_hidpp_runtime_bolt_class_init(FuLogitechHidPpRuntimeBoltClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->detach = fu_logitech_hidpp_runtime_bolt_detach; + klass_device->setup = fu_logitech_hidpp_runtime_bolt_setup; + klass_device->poll = fu_logitech_hidpp_runtime_bolt_poll; + klass_device->to_string = fu_logitech_hidpp_runtime_bolt_to_string; +} + +static void +fu_logitech_hidpp_runtime_bolt_init(FuLogitechHidPpRuntimeBolt *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + fu_device_set_vendor(FU_DEVICE(self), "Logitech"); + fu_device_set_name(FU_DEVICE(self), "Bolt Receiver"); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h new file mode 100644 index 0000000000000000000000000000000000000000..c941c0b751109f981592b6f6da07b58029c328ff --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-logitech-hidpp-runtime.h" + +#define FU_TYPE_HIDPP_RUNTIME_BOLT (fu_logitech_hidpp_runtime_bolt_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechHidPpRuntimeBolt, + fu_logitech_hidpp_runtime_bolt, + FU, + HIDPP_RUNTIME_BOLT, + FuLogitechHidPpRuntime) + +void +fu_logitech_hidpp_runtime_bolt_poll_peripherals(FuDevice *device); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c new file mode 100644 index 0000000000000000000000000000000000000000..c37d33dd9b0f88132a1def4a8fa8fe8d0f941c86 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-hidpp.h" +#include "fu-logitech-hidpp-runtime-unifying.h" + +struct _FuLogitechHidPpRuntimeUnifying { + FuLogitechHidPpRuntime parent_instance; +}; + +G_DEFINE_TYPE(FuLogitechHidPpRuntimeUnifying, + fu_logitech_hidpp_runtime_unifying, + FU_TYPE_HIDPP_RUNTIME) +#define GET_PRIVATE(o) (fu_logitech_hidpp_runtime_unifying_get_instance_private(o)) + +static gboolean +fu_logitech_hidpp_runtime_unifying_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_IDX_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_logitech_hidpp_send(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + FU_UNIFYING_DEVICE_TIMEOUT_MS, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_WRITE)) { + g_debug("failed to detach to bootloader: %s", error_local->message); + } else { + g_prefix_error(&error_local, "failed to detach to bootloader: "); + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_unifying_setup_internal(FuDevice *device, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_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(FuLogitechHidPpHidppMsg) msg = fu_logitech_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_IDX_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_logitech_hidpp_transfer(fu_logitech_hidpp_runtime_get_io_channel(self), + msg, + error)) { + g_prefix_error(error, "failed to read device config: "); + return FALSE; + } + if (!fu_memcpy_safe(config, + sizeof(config), + i * 2, /* dst */ + msg->data, + sizeof(msg->data), + 0x1, /* src */ + 2, + error)) + return FALSE; + } + + /* get firmware version */ + version_fw = fu_logitech_hidpp_format_version("RQR", + config[2], + config[3], + (guint16)config[4] << 8 | config[5]); + fu_device_set_version(device, version_fw); + + /* get bootloader version */ + if (fu_logitech_hidpp_runtime_get_version_bl_major(self) > 0) { + g_autofree gchar *version_bl = NULL; + version_bl = fu_logitech_hidpp_format_version( + "BOT", + fu_logitech_hidpp_runtime_get_version_bl_major(self), + config[8], + config[9]); + fu_device_set_version_bootloader(FU_DEVICE(device), version_bl); + + /* is the USB receiver expecting signed firmware */ + if ((fu_logitech_hidpp_runtime_get_version_bl_major(self) == 0x01 && + config[8] >= 0x04) || + (fu_logitech_hidpp_runtime_get_version_bl_major(self) == 0x03 && + config[8] >= 0x02)) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_protocol(device, "com.logitech.unifyingsigned"); + } + } + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(device, "com.logitech.unifying"); + } + + /* enable HID++ notifications */ + if (!fu_logitech_hidpp_runtime_enable_notifications(self, error)) { + g_prefix_error(error, "failed to enable notifications: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_unifying_setup(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + for (guint i = 0; i < 5; i++) { + g_clear_error(&error_local); + /* 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_logitech_hidpp_runtime_unifying_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_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; +} + +static void +fu_logitech_hidpp_runtime_unifying_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 70, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 27, "reload"); +} + +static void +fu_logitech_hidpp_runtime_unifying_class_init(FuLogitechHidPpRuntimeUnifyingClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->detach = fu_logitech_hidpp_runtime_unifying_detach; + klass_device->setup = fu_logitech_hidpp_runtime_unifying_setup; + klass_device->set_progress = fu_logitech_hidpp_runtime_unifying_set_progress; +} + +static void +fu_logitech_hidpp_runtime_unifying_init(FuLogitechHidPpRuntimeUnifying *self) +{ +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h new file mode 100644 index 0000000000000000000000000000000000000000..7690abda7926482068184f0cbd53a568952fedd8 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-logitech-hidpp-runtime.h" + +#define FU_TYPE_HIDPP_RUNTIME_UNIFYING (fu_logitech_hidpp_runtime_unifying_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechHidPpRuntimeUnifying, + fu_logitech_hidpp_runtime_unifying, + FU, + HIDPP_RUNTIME_UNIFYING, + FuLogitechHidPpRuntime) diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c new file mode 100644 index 0000000000000000000000000000000000000000..38629a7964eeb128365693e29e15ebb9e4068199 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-hidpp-common.h" +#include "fu-logitech-hidpp-hidpp.h" +#include "fu-logitech-hidpp-runtime.h" + +typedef struct { + guint8 version_bl_major; + FuIOChannel *io_channel; +} FuLogitechHidPpRuntimePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuLogitechHidPpRuntime, fu_logitech_hidpp_runtime, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_logitech_hidpp_runtime_get_instance_private(o)) + +FuIOChannel * +fu_logitech_hidpp_runtime_get_io_channel(FuLogitechHidPpRuntime *self) +{ + FuLogitechHidPpRuntimePrivate *priv; + g_return_val_if_fail(FU_IS_HIDPP_RUNTIME(self), NULL); + priv = GET_PRIVATE(self); + return priv->io_channel; +} + +void +fu_logitech_hidpp_runtime_set_io_channel(FuLogitechHidPpRuntime *self, FuIOChannel *io_channel) +{ + FuLogitechHidPpRuntimePrivate *priv; + g_return_if_fail(FU_IS_HIDPP_RUNTIME(self)); + priv = GET_PRIVATE(self); + priv->io_channel = io_channel; +} + +guint8 +fu_logitech_hidpp_runtime_get_version_bl_major(FuLogitechHidPpRuntime *self) +{ + FuLogitechHidPpRuntimePrivate *priv; + g_return_val_if_fail(FU_IS_HIDPP_RUNTIME(self), 0); + priv = GET_PRIVATE(self); + return priv->version_bl_major; +} + +void +fu_logitech_hidpp_runtime_set_version_bl_major(FuLogitechHidPpRuntime *self, + guint8 version_bl_major) +{ + FuLogitechHidPpRuntimePrivate *priv; + g_return_if_fail(FU_IS_HIDPP_RUNTIME(self)); + priv = GET_PRIVATE(self); + priv->version_bl_major = version_bl_major; +} + +static void +fu_logitech_hidpp_runtime_to_string(FuDevice *device, guint idt, GString *str) +{ + FU_DEVICE_CLASS(fu_logitech_hidpp_runtime_parent_class)->to_string(device, idt, str); +} + +gboolean +fu_logitech_hidpp_runtime_enable_notifications(FuLogitechHidPpRuntime *self, GError **error) +{ + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + FuLogitechHidPpRuntimePrivate *priv = GET_PRIVATE(self); + + msg->report_id = HIDPP_REPORT_ID_SHORT; + msg->device_id = HIDPP_DEVICE_IDX_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_logitech_hidpp_transfer(priv->io_channel, msg, error); +} + +static gboolean +fu_logitech_hidpp_runtime_close(FuDevice *device, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimePrivate *priv = GET_PRIVATE(self); + + if (priv->io_channel != NULL) { + if (!fu_io_channel_shutdown(priv->io_channel, error)) + return FALSE; + g_clear_object(&priv->io_channel); + } + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_poll(FuDevice *device, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimePrivate *priv = GET_PRIVATE(self); + const guint timeout = 1; /* ms */ + g_autoptr(GError) error_local = NULL; + g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(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_logitech_hidpp_receive(priv->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_logitech_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 error message"); + break; + default: + g_debug("unknown SubID %02x", msg->sub_id); + break; + } + } + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_open(FuDevice *device, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimePrivate *priv = GET_PRIVATE(self); + 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 */ + priv->io_channel = fu_io_channel_new_file(devpath, error); + if (priv->io_channel == NULL) + return FALSE; + + /* poll for notifications */ + fu_device_set_poll_interval(device, FU_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL); + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_runtime_probe(FuDevice *device, GError **error) +{ + FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidPpRuntimePrivate *priv = GET_PRIVATE(self); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + guint16 release = 0xffff; + g_autoptr(GUdevDevice) udev_parent = NULL; + g_autoptr(GUdevDevice) udev_parent_usb_interface = NULL; + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(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; + const gchar *interface_str; + 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(device, devid2); + priv->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(device, devid2); + priv->version_bl_major = 0x03; + break; + case 0x0500: + /* Bolt */ + udev_parent_usb_interface = + g_udev_device_get_parent_with_subsystem(udev_device, + "usb", + "usb_interface"); + interface_str = + g_udev_device_get_property(udev_parent_usb_interface, "INTERFACE"); + if (interface_str == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "INTERFACE property not found in parent device"); + return FALSE; + } + if (g_strcmp0(interface_str, "3/0/0") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "skipping hidraw device"); + return FALSE; + } + devid2 = g_strdup_printf("USB\\VID_%04X&PID_%04X", + (guint)FU_UNIFYING_DEVICE_VID, + (guint)FU_UNIFYING_DEVICE_PID_BOOTLOADER_BOLT); + fu_device_add_counterpart_guid(device, devid2); + priv->version_bl_major = 0x03; + break; + default: + g_warning("bootloader release %04x invalid", release); + break; + } + } + return TRUE; +} + +static void +fu_logitech_hidpp_runtime_finalize(GObject *object) +{ + G_OBJECT_CLASS(fu_logitech_hidpp_runtime_parent_class)->finalize(object); +} + +static void +fu_logitech_hidpp_runtime_class_init(FuLogitechHidPpRuntimeClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_logitech_hidpp_runtime_finalize; + klass_device->open = fu_logitech_hidpp_runtime_open; + klass_device->probe = fu_logitech_hidpp_runtime_probe; + klass_device->close = fu_logitech_hidpp_runtime_close; + klass_device->poll = fu_logitech_hidpp_runtime_poll; + klass_device->to_string = fu_logitech_hidpp_runtime_to_string; +} + +static void +fu_logitech_hidpp_runtime_init(FuLogitechHidPpRuntime *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_icon(FU_DEVICE(self), "usb-receiver"); + fu_device_set_name(FU_DEVICE(self), "Unifying Receiver"); + fu_device_set_summary(FU_DEVICE(self), "Miniaturised USB wireless receiver"); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h new file mode 100644 index 0000000000000000000000000000000000000000..02a33246d9a6abc104126b2c977565790310b5a2 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_HIDPP_RUNTIME (fu_logitech_hidpp_runtime_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuLogitechHidPpRuntime, + fu_logitech_hidpp_runtime, + FU, + HIDPP_RUNTIME, + FuUdevDevice) + +struct _FuLogitechHidPpRuntimeClass { + FuUdevDeviceClass parent_class; +}; + +gboolean +fu_logitech_hidpp_runtime_enable_notifications(FuLogitechHidPpRuntime *self, GError **error); +FuIOChannel * +fu_logitech_hidpp_runtime_get_io_channel(FuLogitechHidPpRuntime *self); +void +fu_logitech_hidpp_runtime_set_io_channel(FuLogitechHidPpRuntime *self, FuIOChannel *io_channel); +guint8 +fu_logitech_hidpp_runtime_get_version_bl_major(FuLogitechHidPpRuntime *self); +void +fu_logitech_hidpp_runtime_set_version_bl_major(FuLogitechHidPpRuntime *self, + guint8 version_bl_major); diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-self-test.c b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..feb8360f6308531588387f561b3d9d2af4676863 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/fu-logitech-hidpp-self-test.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-hidpp-common.h" + +static void +fu_logitech_hidpp_common(void) +{ + guint8 u8; + guint16 u16; + g_autofree gchar *ver1 = NULL; + + u8 = fu_logitech_hidpp_buffer_read_uint8("12"); + g_assert_cmpint(u8, ==, 0x12); + u16 = fu_logitech_hidpp_buffer_read_uint16("1234"); + g_assert_cmpint(u16, ==, 0x1234); + + ver1 = fu_logitech_hidpp_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_logitech_hidpp_common); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/logitech-hidpp.quirk b/fwupd-1.8.6/plugins/logitech-hidpp/logitech-hidpp.quirk new file mode 100644 index 0000000000000000000000000000000000000000..a8b0e1c84bd2ec887092ac12f56caec6d0f154c5 --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/logitech-hidpp.quirk @@ -0,0 +1,180 @@ +# Unifying Receiver +[HIDRAW\VEN_046D&DEV_C52B] +Plugin = logitech_hidpp +GType = FuLogitechHidPpRuntimeUnifying +VendorId = USB:0x046D +InstallDuration = 30 + +# Bolt Receiver (runtime) +[HIDRAW\VEN_046D&DEV_C548] +Plugin = logitech_hidpp +GType = FuLogitechHidPpRuntimeBolt +VendorId = USB:0x046D +InstallDuration = 30 + +# Bolt Receiver (bootloader) +[HIDRAW\VEN_046D&DEV_AB07] +Plugin = logitech_hidpp +Name = Bolt Receiver +Vendor = Logitech +GType = FuLogitechHidPpDevice +CounterpartGuid = HIDRAW\VEN_046D&DEV_C548 +InstallDuration = 30 +Flags = rebind-attach,force-receiver-id,replug-match-guid,add-radio +LogitechHidppModelId = B601C5480000 + +# Bolt receiver radio (bootloader) +[HIDRAW\VEN_046D&MOD_B601C5480000&ENT_05] +CounterpartGuid = HIDRAW\VEN_046D&DEV_C548&ENT_05 +Flags = is-bootloader + +# Nordic +[USB\VID_046D&PID_AAAA] +Plugin = logitech_hidpp +GType = FuLogitechHidPpBootloaderNordic +FirmwareSizeMin = 0x4000 +CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 30 + +# Nordic Pico +[USB\VID_046D&PID_AAAE] +Plugin = logitech_hidpp +GType = FuLogitechHidPpBootloaderNordic +FirmwareSizeMin = 0x4000 +CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 30 + +# Texas +[USB\VID_046D&PID_AAAC] +Plugin = logitech_hidpp +GType = FuLogitechHidPpBootloaderTexas +FirmwareSizeMin = 0x4000 +CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 18 + +# Texas Pico +[USB\VID_046D&PID_AAAD] +Plugin = logitech_hidpp +GType = FuLogitechHidPpBootloaderTexas +FirmwareSizeMin = 0x4000 +CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 18 + +# K780 (through Unifying receiver) +[HIDRAW\VEN_046D&DEV_405B] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +ParentGuid = HIDRAW\VEN_046D&DEV_C52B +InstallDuration = 150 + +# MR0077 +[HIDRAW\VEN_046D&MOD_B02800000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio +InstallDuration = 270 + +# MR0077 (BLE direct) +[HIDRAW\VEN_046D&DEV_B028] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach +InstallDuration = 270 + +# YR0073 +[HIDRAW\VEN_046D&MOD_B36300000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio +InstallDuration = 270 + +# YR0073 (BLE direct) +[HIDRAW\VEN_046D&DEV_B363] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach +InstallDuration = 270 + +# M650 +[HIDRAW\VEN_046D&MOD_B02A00000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,no-request-required +InstallDuration = 270 + +# M650 (BLE direct) +[HIDRAW\VEN_046D&DEV_B02A] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach,no-request-required +InstallDuration = 270 + +# M750 +[HIDRAW\VEN_046D&MOD_B02C00000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,no-request-required +InstallDuration = 270 + +# M750 (BLE direct) +[HIDRAW\VEN_046D&DEV_B02C] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach,no-request-required +InstallDuration = 270 + +# M650 For Business +[HIDRAW\VEN_046D&MOD_B03200000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,no-request-required +InstallDuration = 270 + +# M650 For Business (BLE direct) +[HIDRAW\VEN_046D&DEV_B032] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach,no-request-required +InstallDuration = 270 + +# M550 +[HIDRAW\VEN_046D&MOD_B02B00000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,no-request-required +InstallDuration = 270 + +# M550 (BLE direct) +[HIDRAW\VEN_046D&DEV_B02B] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach,no-request-required +InstallDuration = 270 + +# K650 +[HIDRAW\VEN_046D&MOD_B36F00000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,no-request-required +InstallDuration = 270 + +# K650 (BLE direct) +[HIDRAW\VEN_046D&DEV_B36F] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach,no-request-required +InstallDuration = 270 + +# K650 For Business +[HIDRAW\VEN_046D&MOD_B37000000000] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,no-request-required +InstallDuration = 270 + +# K650 For Business (BLE direct) +[HIDRAW\VEN_046D&DEV_B370] +Plugin = logitech_hidpp +GType = FuLogitechHidPpDevice +Flags = add-radio,ble,rebind-attach,no-request-required +InstallDuration = 270 diff --git a/fwupd-1.8.6/plugins/logitech-hidpp/meson.build b/fwupd-1.8.6/plugins/logitech-hidpp/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..7b47afd5ecaed1f86ca49c2cd41ac317e8eb683d --- /dev/null +++ b/fwupd-1.8.6/plugins/logitech-hidpp/meson.build @@ -0,0 +1,43 @@ +if gudev.found() and gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechHidPp"'] + +plugin_quirks += files('logitech-hidpp.quirk') +plugin_builtin_logitech_hidpp = static_library('fu_plugin_logitech_hidpp', + sources: [ + 'fu-logitech-hidpp-plugin.c', + 'fu-logitech-hidpp-bootloader.c', + 'fu-logitech-hidpp-bootloader-nordic.c', + 'fu-logitech-hidpp-bootloader-texas.c', + 'fu-logitech-hidpp-common.c', + 'fu-logitech-hidpp-hidpp.c', + 'fu-logitech-hidpp-device.c', + 'fu-logitech-hidpp-hidpp-msg.c', + 'fu-logitech-hidpp-runtime.c', + 'fu-logitech-hidpp-runtime-unifying.c', + 'fu-logitech-hidpp-runtime-bolt.c', + 'fu-logitech-hidpp-radio.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_logitech_hidpp + +if get_option('tests') + e = executable( + 'logitech-hidpp-self-test', + sources: [ + 'fu-logitech-hidpp-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_logitech_hidpp, + ], + c_args: cargs, + ) + test('logitech-hidpp-self-test', e) +endif +endif diff --git a/fwupd-1.8.6/plugins/meson.build b/fwupd-1.8.6/plugins/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..703b20c433c05ce5f22d60a571ad337a4b70e0ce --- /dev/null +++ b/fwupd-1.8.6/plugins/meson.build @@ -0,0 +1,106 @@ +# some of these are controlled by meson tristate features +plugin_deps = [ + gio, + giounix, + gnutls, + gmodule, + gudev, + gusb, + libarchive, + libjsonglib, + libxmlb, + protobufc, +] + +# this is used by lenovo-thinklmi and dell +subdir('uefi-capsule') + +subdir('acpi-dmar') +subdir('acpi-facp') +subdir('acpi-ivrs') +subdir('acpi-phat') +subdir('amd-pmc') +subdir('amt') +subdir('analogix') +subdir('android-boot') +subdir('ata') +subdir('bcm57xx') +subdir('bios') +subdir('ccgx') +subdir('cfu') +subdir('ch341a') +subdir('colorhug') +subdir('corsair') +subdir('cpu') +subdir('cros-ec') +subdir('dell') +subdir('dell-dock') +subdir('dell-esrt') +subdir('dfu') +subdir('dfu-csr') +subdir('ebitdo') +subdir('elantp') +subdir('elanfp') +subdir('emmc') +subdir('ep963x') +subdir('fastboot') +subdir('flashrom') +subdir('focalfp') +subdir('fpc') +subdir('fresco-pd') +subdir('genesys') +subdir('goodix-moc') +subdir('gpio') +subdir('hailuck') +subdir('intel-spi') +subdir('intel-usb4') +subdir('iommu') +subdir('jabra') +subdir('lenovo-thinklmi') +subdir('linux-lockdown') +subdir('linux-sleep') +subdir('linux-swap') +subdir('linux-tainted') +subdir('logind') +subdir('logitech-hidpp') +subdir('logitech-bulkcontroller') +subdir('modem-manager') +subdir('msr') +subdir('mtd') +subdir('nitrokey') +subdir('nordic-hid') +subdir('nvme') +subdir('optionrom') +subdir('parade-lspcon') +subdir('pci-bcr') +subdir('pci-mei') +subdir('pci-psp') +subdir('pixart-rf') +subdir('powerd') +subdir('realtek-mst') +subdir('redfish') +subdir('rts54hid') +subdir('rts54hub') +subdir('steelseries') +subdir('scsi') +subdir('superio') +subdir('synaptics-cape') +subdir('synaptics-cxaudio') +subdir('synaptics-mst') +subdir('synaptics-prometheus') +subdir('synaptics-rmi') +subdir('system76-launch') +subdir('test') +subdir('thelio-io') +subdir('thunderbolt') +subdir('tpm') +subdir('uefi-dbx') +subdir('uefi-pk') +subdir('uefi-recovery') +subdir('uf2') +subdir('upower') +subdir('usi-dock') +subdir('vbe') +subdir('vli') +subdir('wacom-raw') +subdir('wacom-usb') diff --git a/fwupd-1.8.6/plugins/migrate.py b/fwupd-1.8.6/plugins/migrate.py new file mode 100755 index 0000000000000000000000000000000000000000..227623ccd07b3885f49f2916f2a991d05be49fc8 --- /dev/null +++ b/fwupd-1.8.6/plugins/migrate.py @@ -0,0 +1,132 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2022 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# +# pylint: disable=invalid-name,missing-docstring,consider-using-f-string + +import glob +import os +import sys +import subprocess + + +def _convert(plugin_namespace: str, inp: str) -> str: + + template_snake = plugin_namespace.replace("-", "_") + template_camel = "".join(part.title() for part in plugin_namespace.split("-")) + template_upper = template_snake.upper() + + return ( + inp.replace("xxx", template_snake) + .replace( + "fu_plugin_{}_".format(template_snake), + "fu_{}_plugin_".format(template_snake), + ) + .replace("plugin_class", "plugin_class") + .replace("Xxx", template_camel) + .replace("XXX", template_upper) + ) + + +templateh = """/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuXxxPlugin, fu_xxx_plugin, FU, XXX_PLUGIN, FuPlugin) +""" + +templatec = """/* + * Copyright (C) FIXMEFIXMEFIXMEFIXMEFIXME + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-xxx-device.h" +#include "fu-xxx-plugin.h" + +struct _FuXxxPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuXxxPlugin, fu_xxx_plugin, FU_TYPE_PLUGIN) + +static void +fu_xxx_plugin_init(FuXxxPlugin *self) +{ +} + +static void +fu_xxx_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); +} + +static void +fu_xxx_finalize(GObject *obj) +{ + FuXxxPlugin *self = FU_XXX_PLUGIN(obj); + G_OBJECT_CLASS(fu_xxx_plugin_parent_class)->finalize(obj); +} + +static void +fu_xxx_plugin_class_init(FuXxxPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_xxx_constructed; + object_class->finalize = fu_xxx_finalize; + plugin_class->startup = fu_xxx_plugin_startup; +} +""" + +if len(sys.argv) > 1: + plugins = sys.argv[1:] +else: + plugins = sorted(glob.iglob("*")) + +for dirname in plugins: + + basename = dirname.replace("/", "") + if not os.path.isdir(basename): + continue + + plugin_namespace = { + "pixart-rf": "pxi", + "synaptics-prometheus": "synaprom", + "wacom-usb": "wac", + "goodix-moc": "goodixmoc", + }.get(basename, basename) + + newfnc = os.path.join(basename, "fu-{}-plugin.c".format(plugin_namespace)) + newfnh = os.path.join(basename, "fu-{}-plugin.h".format(plugin_namespace)) + + if not os.path.exists(newfnh): + with open(newfnh, "w") as f: + f.write(_convert(plugin_namespace, templateh)) + if not os.path.exists(newfnc): + with open(newfnc, "w") as f: + f.write(_convert(plugin_namespace, templatec)) + subprocess.run( + [ + "geany", + newfnc, + os.path.join(basename, "fu-plugin-{}.c".format(plugin_namespace)), + ] + ) + else: + with open(newfnc, "r") as f: + data = f.read() + with open(newfnc, "w") as f: + f.write(_convert(plugin_namespace, data)) diff --git a/fwupd-1.8.6/plugins/modem-manager/README.md b/fwupd-1.8.6/plugins/modem-manager/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3d4755ae019796d9798f7cb0039f256330aa306d --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/README.md @@ -0,0 +1,88 @@ +# 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` +* `PCI\VID_105B&PID_E0AB&REV_0000&CARRIER_VODAFONE` +* `PCI\VID_105B&PID_E0AB&REV_0000` +* `PCI\VID_105B&PID_E0AB` +* `PCI\VID_105B` +* `PCI\VID_1EAC&PID_1001` +* `PCI\VID_1EAC&PID_1002` +* `PCI\VID_1EAC` + +## Quirk Use + +This plugin uses the following plugin-specific quirk: + +### ModemManagerBranchAtCommand + +AT command to execute to determine the firmware branch currently installed on the modem. + +Since: 1.7.4 + +## Vendor ID Security + +The vendor ID is set from the USB or PCI vendor, for example `USB:0x413C` `PCI:0x105B` + +## 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 Protocol: `com.google.fastboot` + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the fastboot and runtime modes are treated as the same device. + +## 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. + +Update protocol: `com.qualcomm.qmi_pdc` + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the fastboot and runtime modes are treated as the same device. + +## Update method: mbim-qdu + +If the device supports the 'mbim-qdu' update method, the contents of the CAB +file should include a package named as 'Firmware_*.7z' which is a compressed +ota.bin file that will be downloaded to the ota partition of the device. + +Update protocol: `com.qualcomm.mbim_qdu` + +## Update method: firehose + +If the device supports the 'firehose' update method, it should have QCDM port +exposed and the contents of the CAB file should contain 'firehose-rawprogram.xml'. +The device is then switched to the emergencly download mode (EDL) and flashed +with files described in 'firehose-rawprogram.xml'. + +Update protocol: `com.qualcomm.firehose` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb` and `/dev/bus/pci`. diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-firehose-updater.c b/fwupd-1.8.6/plugins/modem-manager/fu-firehose-updater.c new file mode 100644 index 0000000000000000000000000000000000000000..7d5a93128f55f60bc600aad76d4148ee2d78edbd --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-firehose-updater.c @@ -0,0 +1,889 @@ +/* + * Copyright (C) 2020 Aleksander Morgado + * Copyright (C) 2021 Ivan Mikhanchuk + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-firehose-updater.h" + +/* Maximum amount of non-"response" (e.g. "log") XML messages that can be + * received from the module when expecting a "response". This is just a safe + * upper limit to avoid reading forever. */ +#define MAX_RECV_MESSAGES 100 + +/* When initializing the conversation with the firehose interpreter, the + * first step is to receive and process a bunch of messages sent by the + * module. The initial timeout to receive the first message is longer in case + * the module needs some initialization time itself; all the messages after + * the first one are expected to be received much quicker. The default timeout + * value should not be extremely long because the initialization phase ends + * when we don't receive more messages, so it's expected that the timeout will + * fully elapse after the last message sent by the module. */ +#define INITIALIZE_INITIAL_TIMEOUT_MS 3000 +#define INITIALIZE_TIMEOUT_MS 250 + +/* Maximum amount of time to wait for a message from the module. */ +#define DEFAULT_RECV_TIMEOUT_MS 15000 + +/* The first configure attempt sent to the module will include all the defaults + * listed below. If the module replies with a NAK specifying a different + * (shorter) max payload size to use, the second configure attempt will be done + * with that new suggested max payload size value. Only 2 configure attempts are + * therefore expected. */ +#define MAX_CONFIGURE_ATTEMPTS 2 + +/* Defaults for the firehose configuration step. The max payload size to target + * in bytes may end up being a different if the module requests a shorter one. + */ +#define CONFIGURE_MEMORY_NAME "nand" +#define CONFIGURE_VERBOSE 0 +#define CONFIGURE_ALWAYS_VALIDATE 0 +#define CONFIGURE_MAX_DIGEST_TABLE_SIZE_IN_BYTES 2048 +#define CONFIGURE_MAX_PAYLOAD_SIZE_TO_TARGET_IN_BYTES 8192 +#define CONFIGURE_ZLP_AWARE_HOST 1 +#define CONFIGURE_SKIP_STORAGE_INIT 0 + +struct _FuFirehoseUpdater { + GObject parent_instance; + gchar *port; + FuSaharaLoader *sahara; + FuIOChannel *io_channel; +}; + +G_DEFINE_TYPE(FuFirehoseUpdater, fu_firehose_updater, G_TYPE_OBJECT) + +static void +fu_firehose_updater_log_message(const gchar *action, GBytes *msg) +{ + const gchar *msg_data; + gsize msg_size; + g_autofree gchar *msg_strsafe = NULL; + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") == NULL) + return; + + msg_data = (const gchar *)g_bytes_get_data(msg, &msg_size); + if (msg_size > G_MAXINT) + return; + + msg_strsafe = fu_strsafe(msg_data, msg_size); + + g_debug("%s: %.*s", action, (gint)msg_size, msg_strsafe); +} + +static GBytes * +fu_firehose_read(FuFirehoseUpdater *self, guint timeout_ms, FuIOChannelFlags flags, GError **error) +{ + if (self->sahara != NULL) { + GByteArray *bytearr = fu_sahara_loader_qdl_read(self->sahara, error); + return bytearr == NULL ? NULL : g_byte_array_free_to_bytes(bytearr); + } + + return fu_io_channel_read_bytes(self->io_channel, -1, timeout_ms, flags, error); +} + +static gboolean +fu_firehose_write(FuFirehoseUpdater *self, + GBytes *bytes, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + if (self->sahara != NULL) + return fu_sahara_loader_qdl_write_bytes(self->sahara, bytes, error); + + return fu_io_channel_write_bytes(self->io_channel, bytes, timeout_ms, flags, error); +} + +static gboolean +validate_program_action(XbNode *program, FuArchive *archive, GError **error) +{ + const gchar *filename_attr; + GBytes *file; + gsize file_size; + guint64 computed_num_partition_sectors; + guint64 num_partition_sectors; + guint64 sector_size_in_bytes; + + filename_attr = xb_node_get_attr(program, "filename"); + if (filename_attr == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Missing 'filename' attribute in 'program' action"); + return FALSE; + } + + /* contents of the CAB file are flat, no subdirectories; look for the + * exact filename */ + file = fu_archive_lookup_by_fn(archive, filename_attr, error); + if (file == NULL) + return FALSE; + + file_size = g_bytes_get_size(file); + + num_partition_sectors = xb_node_get_attr_as_uint(program, "num_partition_sectors"); + if (num_partition_sectors == G_MAXUINT64) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Missing 'num_partition_sectors' attribute in 'program' action for " + "filename '%s'", + filename_attr); + return FALSE; + } + sector_size_in_bytes = xb_node_get_attr_as_uint(program, "SECTOR_SIZE_IN_BYTES"); + if (sector_size_in_bytes == G_MAXUINT64) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Missing 'SECTOR_SIZE_IN_BYTES' attribute in 'program' action for " + "filename '%s'", + filename_attr); + return FALSE; + } + + computed_num_partition_sectors = file_size / sector_size_in_bytes; + if ((file_size % sector_size_in_bytes) != 0) + computed_num_partition_sectors++; + + if (computed_num_partition_sectors != num_partition_sectors) { + g_set_error( + error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Invalid 'num_partition_sectors' in 'program' action for filename '%s': " + "expected %" G_GUINT64_FORMAT " instead of %" G_GUINT64_FORMAT " bytes", + filename_attr, + computed_num_partition_sectors, + num_partition_sectors); + return FALSE; + } + + xb_node_set_data(program, "fwupd:ProgramFile", file); + return TRUE; +} + +gboolean +fu_firehose_validate_rawprogram(GBytes *rawprogram, + FuArchive *archive, + XbSilo **out_silo, + GPtrArray **out_action_nodes, + GError **error) +{ + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbNode) data_node = NULL; + g_autoptr(GPtrArray) action_nodes = NULL; + + if (!xb_builder_source_load_bytes(source, rawprogram, XB_BUILDER_SOURCE_FLAG_NONE, error)) + return FALSE; + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + + data_node = xb_silo_get_root(silo); + action_nodes = xb_node_get_children(data_node); + if (action_nodes == NULL || action_nodes->len == 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "No actions given"); + return FALSE; + } + + for (guint i = 0; i < action_nodes->len; i++) { + XbNode *n = g_ptr_array_index(action_nodes, i); + if ((g_strcmp0(xb_node_get_element(n), "program") == 0) && + !validate_program_action(n, archive, error)) { + return FALSE; + } + } + + *out_silo = g_steal_pointer(&silo); + *out_action_nodes = g_steal_pointer(&action_nodes); + return TRUE; +} + +gboolean +fu_firehose_updater_open(FuFirehoseUpdater *self, GError **error) +{ + if (fu_sahara_loader_qdl_is_open(self->sahara)) { + g_debug("using sahara qdl port for firehose"); + return TRUE; + } + + /* sanity check */ + if (self->port == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no firehose port provided for filename"); + return FALSE; + } + + g_debug("opening firehose port..."); + + if (self->port != NULL) { + self->io_channel = fu_io_channel_new_file(self->port, error); + return self->io_channel != NULL; + } + + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "No device to write firehose commands to"); + return FALSE; +} + +gboolean +fu_firehose_updater_close(FuFirehoseUpdater *self, GError **error) +{ + if (self->io_channel != NULL) { + g_debug("closing firehose port..."); + if (!fu_io_channel_shutdown(self->io_channel, error)) + return FALSE; + g_clear_object(&self->io_channel); + } + return TRUE; +} + +static gboolean +fu_firehose_updater_check_operation_result(XbNode *node, gboolean *out_rawmode) +{ + g_warn_if_fail(g_strcmp0(xb_node_get_element(node), "response") == 0); + if (g_strcmp0(xb_node_get_attr(node, "value"), "ACK") != 0) + return FALSE; + if (out_rawmode) + *out_rawmode = (g_strcmp0(xb_node_get_attr(node, "rawmode"), "true") == 0); + return TRUE; +} + +static gboolean +fu_firehose_updater_process_response(GBytes *rsp_bytes, + XbSilo **out_silo, + XbNode **out_response_node, + GError **error) +{ + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbNode) data_node = NULL; + g_autoptr(GPtrArray) action_nodes = NULL; + + if (!xb_builder_source_load_bytes(source, rsp_bytes, XB_BUILDER_SOURCE_FLAG_NONE, error)) + return FALSE; + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + + data_node = xb_silo_get_root(silo); + if (data_node == NULL) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing root data node"); + return FALSE; + } + + action_nodes = xb_node_get_children(data_node); + if (action_nodes != NULL) { + for (guint j = 0; j < action_nodes->len; j++) { + XbNode *node = g_ptr_array_index(action_nodes, j); + + if (g_strcmp0(xb_node_get_element(node), "response") == 0) { + if (out_silo) + *out_silo = g_steal_pointer(&silo); + if (out_response_node) + *out_response_node = g_object_ref(node); + return TRUE; + } + + if (g_strcmp0(xb_node_get_element(node), "log") == 0) { + const gchar *value_attr = xb_node_get_attr(node, "value"); + if (value_attr) + g_debug("device log: %s", value_attr); + } + } + } + + if (out_silo != NULL) + *out_silo = NULL; + if (out_response_node != NULL) + *out_response_node = NULL; + return TRUE; +} + +static gboolean +fu_firehose_updater_send_and_receive(FuFirehoseUpdater *self, + GByteArray *take_cmd_bytearray, + XbSilo **out_silo, + XbNode **out_response_node, + GError **error) +{ + if (take_cmd_bytearray) { + const gchar *cmd_header = "\n\n"; + const gchar *cmd_trailer = ""; + g_autoptr(GBytes) cmd_bytes = NULL; + + g_byte_array_prepend(take_cmd_bytearray, + (const guint8 *)cmd_header, + strlen(cmd_header)); + g_byte_array_append(take_cmd_bytearray, + (const guint8 *)cmd_trailer, + strlen(cmd_trailer)); + cmd_bytes = g_byte_array_free_to_bytes(take_cmd_bytearray); + + fu_firehose_updater_log_message("writing", cmd_bytes); + if (!fu_firehose_write(self, + cmd_bytes, + 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, + error)) { + g_prefix_error(error, "Failed to write command: "); + return FALSE; + } + } + + for (guint i = 0; i < MAX_RECV_MESSAGES; i++) { + g_autoptr(GBytes) rsp_bytes = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbNode) response_node = NULL; + + rsp_bytes = fu_firehose_read(self, + DEFAULT_RECV_TIMEOUT_MS, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); + if (rsp_bytes == NULL) { + g_prefix_error(error, "Failed to read XML message: "); + return FALSE; + } + + fu_firehose_updater_log_message("reading", rsp_bytes); + if (!fu_firehose_updater_process_response(rsp_bytes, + &silo, + &response_node, + error)) { + g_prefix_error(error, "Failed to parse XML message: "); + return FALSE; + } + + if (silo != NULL && response_node != NULL) { + *out_silo = g_steal_pointer(&silo); + *out_response_node = g_steal_pointer(&response_node); + return TRUE; + } + + /* continue until we get a 'response_node' */ + } + + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "Didn't get any response in the last %d messages", + MAX_RECV_MESSAGES); + return FALSE; +} + +static gboolean +fu_firehose_updater_initialize(FuFirehoseUpdater *self, GError **error) +{ + guint n_msg = 0; + + for (guint i = 0; i < MAX_RECV_MESSAGES; i++) { + g_autoptr(GBytes) rsp_bytes = NULL; + guint timeout = (i == 0 ? INITIALIZE_INITIAL_TIMEOUT_MS : INITIALIZE_TIMEOUT_MS); + + rsp_bytes = fu_firehose_read(self, timeout, FU_IO_CHANNEL_FLAG_SINGLE_SHOT, NULL); + if (rsp_bytes == NULL) + break; + + fu_firehose_updater_log_message("reading", rsp_bytes); + if (!fu_firehose_updater_process_response(rsp_bytes, NULL, NULL, error)) { + g_prefix_error(error, "Failed to parse XML message: "); + return FALSE; + } + + n_msg++; + } + + if (n_msg == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Couldn't read initial firehose messages from device"); + return FALSE; + } + + return TRUE; +} + +static guint +fu_firehose_updater_configure(FuFirehoseUpdater *self, GError **error) +{ + gint max_payload_size = CONFIGURE_MAX_PAYLOAD_SIZE_TO_TARGET_IN_BYTES; + + for (guint i = 0; i < MAX_CONFIGURE_ATTEMPTS; i++) { + GByteArray *cmd_bytearray = NULL; + g_autoptr(XbSilo) rsp_silo = NULL; + g_autoptr(XbNode) rsp_node = NULL; + GString *cmd_str = g_string_new(NULL); + + g_string_append_printf(cmd_str, ""); + + cmd_bytearray = g_bytes_unref_to_array(g_string_free_to_bytes(cmd_str)); + + if (!fu_firehose_updater_send_and_receive(self, + cmd_bytearray, + &rsp_silo, + &rsp_node, + error)) { + g_prefix_error(error, "Failed to run configure command: "); + return 0; + } + + /* retry if we're told to use a different max payload size */ + if (!fu_firehose_updater_check_operation_result(rsp_node, NULL)) { + guint64 suggested_max_payload_size; + g_autoptr(XbNode) root = NULL; + + root = xb_silo_get_root(rsp_silo); + suggested_max_payload_size = + xb_node_get_attr_as_uint(root, "MaxPayloadSizeToTargetInBytes"); + if ((suggested_max_payload_size > G_MAXINT) || + ((gint)suggested_max_payload_size == max_payload_size)) { + break; + } + + suggested_max_payload_size = max_payload_size; + continue; + } + + /* if operation is successful, return the max payload size we requested */ + return max_payload_size; + } + + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Configure operation failed"); + return 0; +} + +static gboolean +fu_firehose_updater_reset(FuFirehoseUpdater *self, GError **error) +{ + guint recv_cnt = 20; + const gchar *cmd_str = ""; + GByteArray *cmd_bytearray = NULL; + g_autoptr(XbSilo) rsp_silo = NULL; + g_autoptr(XbNode) rsp_node = NULL; + + cmd_bytearray = + g_byte_array_append(g_byte_array_new(), (const guint8 *)cmd_str, strlen(cmd_str)); + if (!fu_firehose_updater_send_and_receive(self, + cmd_bytearray, + &rsp_silo, + &rsp_node, + error)) { + g_prefix_error(error, "Failed to run reset command: "); + return FALSE; + } + + if (!fu_firehose_updater_check_operation_result(rsp_node, NULL)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Reset operation failed"); + return FALSE; + } + + /* read out all of the remaining messages. otherwise modem won't go into reset */ + while (--recv_cnt && + fu_firehose_updater_send_and_receive(self, NULL, &rsp_silo, &rsp_node, NULL)) + ; + + g_warn_if_fail(recv_cnt > 0); + + return TRUE; +} + +static gboolean +fu_firehose_updater_send_program_file(FuFirehoseUpdater *self, + const gchar *program_filename, + GBytes *program_file, + gsize payload_size, + gsize sector_size, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + FuChunk *chk; + + chunks = fu_chunk_array_new_from_bytes(program_file, 0, 0, payload_size); + /* last block needs to be padded to the next sector_size, + * so that we always send full sectors */ + chk = g_ptr_array_index(chunks, chunks->len - 1); + if ((fu_chunk_get_data_sz(chk) % sector_size) != 0) { + g_autoptr(GBytes) padded_bytes = NULL; + gsize padded_sz = sector_size * (fu_chunk_get_data_sz(chk) / sector_size + 1); + + padded_bytes = fu_bytes_pad(fu_chunk_get_bytes(chk), padded_sz); + fu_chunk_set_bytes(chk, padded_bytes); + + g_return_val_if_fail(fu_chunk_get_data_sz(chk) == padded_sz, FALSE); + } + for (guint i = 0; i < chunks->len; i++) { + chk = g_ptr_array_index(chunks, i); + + /* log only in blocks of 250 plus first/last */ + if (i == 0 || i == (chunks->len - 1) || (i + 1) % 250 == 0) + g_debug("sending %u bytes in block %u/%u of file '%s'", + fu_chunk_get_data_sz(chk), + i + 1, + chunks->len, + program_filename); + + if (!fu_firehose_write(self, + fu_chunk_get_bytes(chk), + 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, + error)) { + g_prefix_error(error, + "Failed to write block %u/%u of file '%s': ", + i + 1, + chunks->len, + program_filename); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_firehose_updater_actions_validate(GPtrArray *action_nodes, + guint max_payload_size, + GError **error) +{ + g_return_val_if_fail(action_nodes != NULL, FALSE); + + for (guint i = 0; i < action_nodes->len; i++) { + const gchar *name = NULL; + const gchar *program_filename = NULL; + GBytes *program_file = NULL; + guint64 program_sector_size_in_bytes = 0; + + XbNode *node = g_ptr_array_index(action_nodes, i); + const gchar *action = xb_node_get_element(node); + + if (g_strcmp0(action, "program") != 0) + continue; + + name = "fwupd:ProgramFile"; + program_file = xb_node_get_data(node, name); + if (program_file == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to validate program file: failed to get %s", + name); + return FALSE; + } + name = "filename"; + program_filename = xb_node_get_attr(node, name); + if (program_filename == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to validate program file: failed to get %s", + name); + return FALSE; + } + name = "SECTOR_SIZE_IN_BYTES"; + program_sector_size_in_bytes = xb_node_get_attr_as_uint(node, name); + if (program_sector_size_in_bytes > max_payload_size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to validate program file '%s' command: " + "requested sector size bigger (%" G_GUINT64_FORMAT " bytes) " + "than maximum payload size agreed with device (%u bytes)", + program_filename, + program_sector_size_in_bytes, + max_payload_size); + return FALSE; + } + } + + return TRUE; +} + +static gsize +fu_firehose_updater_actions_get_total_file_size(GPtrArray *action_nodes) +{ + gsize total_bytes = 0; + g_return_val_if_fail(action_nodes != NULL, 0); + + for (guint i = 0; i < action_nodes->len; i++) { + GBytes *program_file = NULL; + XbNode *node = g_ptr_array_index(action_nodes, i); + const gchar *action = xb_node_get_element(node); + + if (g_strcmp0(action, "program") != 0) + continue; + + program_file = xb_node_get_data(node, "fwupd:ProgramFile"); + + if (program_file != NULL) + total_bytes += g_bytes_get_size(program_file); + } + + return total_bytes; +} + +static gboolean +fu_firehose_updater_run_action_program(FuFirehoseUpdater *self, + XbNode *node, + gboolean rawmode, + guint max_payload_size, + gsize *sent_bytes, + GError **error) +{ + GBytes *program_file = NULL; + const gchar *program_filename = NULL; + guint64 program_sector_size = 0; + guint payload_size = 0; + g_autoptr(XbSilo) rsp_silo = NULL; + g_autoptr(XbNode) rsp_node = NULL; + + program_file = xb_node_get_data(node, "fwupd:ProgramFile"); + if (program_file == NULL) + return FALSE; + program_filename = xb_node_get_attr(node, "filename"); + if (program_filename == NULL) + return FALSE; + program_sector_size = xb_node_get_attr_as_uint(node, "SECTOR_SIZE_IN_BYTES"); + if (program_sector_size == G_MAXUINT64) + return FALSE; + + if (rawmode == FALSE) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to download program file '%s': rawmode not enabled", + program_filename); + return FALSE; + } + + while ((payload_size + (guint)program_sector_size) <= max_payload_size) + payload_size += (guint)program_sector_size; + + g_debug("sending program file '%s' (0x%x bytes)", + program_filename, + (guint)g_bytes_get_size(program_file)); + if (!fu_firehose_updater_send_program_file(self, + program_filename, + program_file, + payload_size, + program_sector_size, + error)) { + g_prefix_error(error, "Failed to send program file '%s': ", program_filename); + return FALSE; + } + + g_debug("waiting for program file download confirmation..."); + if (!fu_firehose_updater_send_and_receive(self, NULL, &rsp_silo, &rsp_node, error)) { + g_prefix_error(error, + "Download confirmation not received for file '%s': ", + program_filename); + return FALSE; + } + + if (!fu_firehose_updater_check_operation_result(rsp_node, &rawmode)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Download confirmation failed for file '%s'", + program_filename); + return FALSE; + } + + if (rawmode != FALSE) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Download confirmation failed for file '%s': rawmode still enabled", + program_filename); + return FALSE; + } + + if (sent_bytes != NULL) + *sent_bytes += g_bytes_get_size(program_file); + + return TRUE; +} + +static gboolean +fu_firehose_updater_run_action(FuFirehoseUpdater *self, + XbNode *node, + guint max_payload_size, + gsize *sent_bytes, + GError **error) +{ + const gchar *action; + gchar *cmd_str = NULL; + gboolean rawmode = FALSE; + GByteArray *cmd_bytearray = NULL; + g_autoptr(XbSilo) rsp_silo = NULL; + g_autoptr(XbNode) rsp_node = NULL; + + action = xb_node_get_element(node); + +#if LIBXMLB_CHECK_VERSION(0, 2, 2) + cmd_str = xb_node_export(node, XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, error); +#else + cmd_str = xb_node_export(node, XB_NODE_EXPORT_FLAG_NONE, error); +#endif + if (cmd_str == NULL) + return FALSE; + cmd_bytearray = g_byte_array_new_take((guint8 *)cmd_str, strlen(cmd_str)); + + g_debug("running command '%s'...", action); + if (!fu_firehose_updater_send_and_receive(self, + cmd_bytearray, + &rsp_silo, + &rsp_node, + error)) { + g_prefix_error(error, "Failed to run command '%s': ", action); + return FALSE; + } + + if (!fu_firehose_updater_check_operation_result(rsp_node, &rawmode)) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Command '%s' failed", action); + return FALSE; + } + + if (g_strcmp0(action, "program") == 0) + return fu_firehose_updater_run_action_program(self, + node, + rawmode, + max_payload_size, + sent_bytes, + error); + + return TRUE; +} + +static gboolean +fu_firehose_updater_run_actions(FuFirehoseUpdater *self, + XbSilo *silo, + GPtrArray *action_nodes, + guint max_payload_size, + FuProgress *progress, + GError **error) +{ + gsize sent_bytes = 0; + gsize total_bytes = 0; + + g_warn_if_fail(action_nodes->len > 0); + + if (!fu_firehose_updater_actions_validate(action_nodes, max_payload_size, error)) + return FALSE; + + total_bytes = fu_firehose_updater_actions_get_total_file_size(action_nodes); + + for (guint i = 0; i < action_nodes->len; i++) { + XbNode *node = g_ptr_array_index(action_nodes, i); + + if (!fu_firehose_updater_run_action(self, + node, + max_payload_size, + &sent_bytes, + error)) + return FALSE; + + fu_progress_set_percentage_full(progress, sent_bytes, total_bytes); + } + + return TRUE; +} + +gboolean +fu_firehose_updater_write(FuFirehoseUpdater *self, + XbSilo *silo, + GPtrArray *action_nodes, + FuProgress *progress, + GError **error) +{ + guint max_payload_size; + gboolean result; + g_autoptr(GError) error_local = NULL; + + if (!fu_firehose_updater_initialize(self, error)) + return FALSE; + + max_payload_size = fu_firehose_updater_configure(self, error); + if (max_payload_size == 0) + return FALSE; + + result = fu_firehose_updater_run_actions(self, + silo, + action_nodes, + max_payload_size, + progress, + error); + + if (!fu_firehose_updater_reset(self, &error_local)) { + if (result) + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + return result; +} + +static void +fu_firehose_updater_init(FuFirehoseUpdater *self) +{ +} + +static void +fu_firehose_updater_finalize(GObject *object) +{ + FuFirehoseUpdater *self = FU_FIREHOSE_UPDATER(object); + g_warn_if_fail(self->io_channel == NULL); + g_free(self->port); + g_object_unref(self->sahara); + G_OBJECT_CLASS(fu_firehose_updater_parent_class)->finalize(object); +} + +static void +fu_firehose_updater_class_init(FuFirehoseUpdaterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_firehose_updater_finalize; +} + +FuFirehoseUpdater * +fu_firehose_updater_new(const gchar *port, FuSaharaLoader *sahara) +{ + FuFirehoseUpdater *self = g_object_new(FU_TYPE_FIREHOSE_UPDATER, NULL); + if (port != NULL) + self->port = g_strdup(port); + if (sahara != NULL) + self->sahara = g_object_ref(sahara); + return self; +} diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-firehose-updater.h b/fwupd-1.8.6/plugins/modem-manager/fu-firehose-updater.h new file mode 100644 index 0000000000000000000000000000000000000000..391142568187ece88278733a32a1aad0ff971a5d --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-firehose-updater.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 Aleksander Morgado + * Copyright (C) 2021 Ivan Mikhanchuk + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-sahara-loader.h" + +#define FU_TYPE_FIREHOSE_UPDATER (fu_firehose_updater_get_type()) +G_DECLARE_FINAL_TYPE(FuFirehoseUpdater, fu_firehose_updater, FU, FIREHOSE_UPDATER, GObject) + +FuFirehoseUpdater * +fu_firehose_updater_new(const gchar *port, FuSaharaLoader *sahara); + +gboolean +fu_firehose_updater_open(FuFirehoseUpdater *self, GError **error); +gboolean +fu_firehose_updater_write(FuFirehoseUpdater *self, + XbSilo *silo, + GPtrArray *action_nodes, + FuProgress *progress, + GError **error); +gboolean +fu_firehose_updater_close(FuFirehoseUpdater *self, GError **error); + +/* helpers */ + +gboolean +fu_firehose_validate_rawprogram(GBytes *rawprogram, + FuArchive *archive, + XbSilo **out_silo, + GPtrArray **out_action_nodes, + GError **error); diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-mbim-qdu-updater.c b/fwupd-1.8.6/plugins/modem-manager/fu-mbim-qdu-updater.c new file mode 100644 index 0000000000000000000000000000000000000000..d4044baf9d70120750feef364f93f22c072c35a0 --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mbim-qdu-updater.c @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2021 Jarvis Jiang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "fu-mbim-qdu-updater.h" +#include "fu-mm-utils.h" + +#if MBIM_CHECK_VERSION(1, 25, 3) +#define FU_MBIM_QDU_MAX_OPEN_ATTEMPTS 8 + +struct _FuMbimQduUpdater { + GObject parent_instance; + gchar *mbim_port; + MbimDevice *mbim_device; +}; + +G_DEFINE_TYPE(FuMbimQduUpdater, fu_mbim_qdu_updater, G_TYPE_OBJECT) + +typedef struct { + GMainLoop *mainloop; + MbimDevice *mbim_device; + GError *error; + guint open_attempts; +} OpenContext; + +static void +fu_mbim_qdu_updater_mbim_device_open_attempt(OpenContext *ctx); + +static void +fu_mbim_qdu_updater_mbim_device_open_ready(GObject *mbim_device, + GAsyncResult *res, + gpointer user_data) +{ + OpenContext *ctx = (OpenContext *)user_data; + + g_assert(ctx->open_attempts > 0); + + if (!mbim_device_open_full_finish(MBIM_DEVICE(mbim_device), res, &ctx->error)) { + ctx->open_attempts--; + if (ctx->open_attempts == 0) { + g_clear_object(&ctx->mbim_device); + g_main_loop_quit(ctx->mainloop); + return; + } + + /* retry */ + g_debug("error: couldn't open mbim device: %s", ctx->error->message); + g_clear_error(&ctx->error); + fu_mbim_qdu_updater_mbim_device_open_attempt(ctx); + return; + } + + g_main_loop_quit(ctx->mainloop); +} + +static void +fu_mbim_qdu_updater_mbim_device_open_attempt(OpenContext *ctx) +{ + /* all communication through the proxy */ + MbimDeviceOpenFlags open_flags = MBIM_DEVICE_OPEN_FLAGS_PROXY; + + g_debug("trying to open MBIM device..."); + mbim_device_open_full(ctx->mbim_device, + open_flags, + 10, + NULL, + fu_mbim_qdu_updater_mbim_device_open_ready, + ctx); +} + +static void +fu_mbim_qdu_updater_mbim_device_new_ready(GObject *source, GAsyncResult *res, gpointer user_data) +{ + OpenContext *ctx = (OpenContext *)user_data; + + ctx->mbim_device = mbim_device_new_finish(res, &ctx->error); + if (ctx->mbim_device == NULL) { + g_main_loop_quit(ctx->mainloop); + return; + } + + fu_mbim_qdu_updater_mbim_device_open_attempt(ctx); +} + +gboolean +fu_mbim_qdu_updater_open(FuMbimQduUpdater *self, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + g_autoptr(GFile) mbim_device_file = g_file_new_for_path(self->mbim_port); + OpenContext ctx = { + .mainloop = mainloop, + .mbim_device = NULL, + .error = NULL, + .open_attempts = FU_MBIM_QDU_MAX_OPEN_ATTEMPTS, + }; + + mbim_device_new(mbim_device_file, NULL, fu_mbim_qdu_updater_mbim_device_new_ready, &ctx); + g_main_loop_run(mainloop); + + /* either we have all device or otherwise error is set */ + if (ctx.mbim_device != NULL) { + g_warn_if_fail(ctx.error == NULL); + self->mbim_device = ctx.mbim_device; + /* success */ + return TRUE; + } + + g_warn_if_fail(ctx.error != NULL); + g_warn_if_fail(ctx.mbim_device == NULL); + g_propagate_error(error, ctx.error); + return FALSE; +} + +typedef struct { + GMainLoop *mainloop; + MbimDevice *mbim_device; + GError *error; +} CloseContext; + +static void +fu_mbim_qdu_updater_mbim_device_close_ready(GObject *mbim_device, + GAsyncResult *res, + gpointer user_data) +{ + CloseContext *ctx = (CloseContext *)user_data; + + /* ignore errors when closing */ + mbim_device_close_finish(MBIM_DEVICE(mbim_device), res, &ctx->error); + g_clear_object(&ctx->mbim_device); + g_main_loop_quit(ctx->mainloop); +} + +gboolean +fu_mbim_qdu_updater_close(FuMbimQduUpdater *self, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + CloseContext ctx = { + .mainloop = mainloop, + .mbim_device = g_steal_pointer(&self->mbim_device), + .error = NULL, + }; + + if (ctx.mbim_device == NULL) + return TRUE; + + mbim_device_close(ctx.mbim_device, + 5, + NULL, + fu_mbim_qdu_updater_mbim_device_close_ready, + &ctx); + g_main_loop_run(mainloop); + + /* we should always have both device cleared, and optionally error set */ + g_warn_if_fail(ctx.mbim_device == NULL); + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return FALSE; + } + + /* update attach right after this */ + return TRUE; +} + +typedef struct { + GMainLoop *mainloop; + GError *error; + gchar *firmware_version; +} GetFirmwareVersionContext; + +static void +fu_mbim_qdu_updater_caps_query_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) +{ + GetFirmwareVersionContext *ctx = user_data; + g_autofree gchar *firmware_version = NULL; + g_autoptr(MbimMessage) response = NULL; + + response = mbim_device_command_finish(device, res, &ctx->error); + if (!response || !mbim_message_response_get_result(response, + MBIM_MESSAGE_TYPE_COMMAND_DONE, + &ctx->error)) { + g_debug("error: operation failed: %s", ctx->error->message); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!mbim_message_device_caps_response_parse(response, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &firmware_version, + NULL, + &ctx->error)) { + g_debug("error: couldn't parse response message: %s", ctx->error->message); + g_main_loop_quit(ctx->mainloop); + return; + } + + g_debug("[%s] Successfully request modem to query caps", + mbim_device_get_path_display(device)); + + ctx->firmware_version = g_strdup(firmware_version); + + g_main_loop_quit(ctx->mainloop); +} + +static void +fu_mbim_qdu_updater_caps_query(MbimDevice *device, GetFirmwareVersionContext *ctx) +{ + g_autoptr(MbimMessage) request = NULL; + + request = mbim_message_device_caps_query_new(NULL); + + mbim_device_command(device, + request, + 10, + NULL, + (GAsyncReadyCallback)fu_mbim_qdu_updater_caps_query_ready, + ctx); +} + +gchar * +fu_mbim_qdu_updater_check_ready(FuMbimQduUpdater *self, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + GetFirmwareVersionContext ctx = { + .mainloop = mainloop, + .error = NULL, + .firmware_version = NULL, + }; + + fu_mbim_qdu_updater_caps_query(self->mbim_device, &ctx); + + g_main_loop_run(mainloop); + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return NULL; + } + + return ctx.firmware_version; +} + +typedef struct { + GMainLoop *mainloop; + MbimDevice *mbim_device; + GError *error; + GBytes *blob; + GArray *digest; + GPtrArray *chunks; + guint chunk_sent; + FuDevice *device; + FuProgress *progress; +} WriteContext; + +static void +fu_mbim_qdu_updater_file_write_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) +{ + WriteContext *ctx = user_data; + g_autoptr(MbimMessage) response = NULL; + + response = mbim_device_command_finish(device, res, &ctx->error); + if (!response || !mbim_message_response_get_result(response, + MBIM_MESSAGE_TYPE_COMMAND_DONE, + &ctx->error)) { + g_debug("error: operation failed: %s", ctx->error->message); + g_ptr_array_unref(ctx->chunks); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!mbim_message_qdu_file_write_response_parse(response, &ctx->error)) { + g_debug("error: couldn't parse response message: %s", ctx->error->message); + g_ptr_array_unref(ctx->chunks); + g_main_loop_quit(ctx->mainloop); + return; + } + + ctx->chunk_sent++; + fu_progress_set_percentage_full(ctx->progress, + (gsize)ctx->chunk_sent, + (gsize)ctx->chunks->len); + if (ctx->chunk_sent < ctx->chunks->len) { + FuChunk *chk = g_ptr_array_index(ctx->chunks, ctx->chunk_sent); + g_autoptr(MbimMessage) request = + mbim_message_qdu_file_write_set_new(fu_chunk_get_data_sz(chk), + (const guint8 *)fu_chunk_get_data(chk), + NULL); + mbim_device_command(ctx->mbim_device, + request, + 10, + NULL, + (GAsyncReadyCallback)fu_mbim_qdu_updater_file_write_ready, + ctx); + return; + } + + g_ptr_array_unref(ctx->chunks); + g_main_loop_quit(ctx->mainloop); +} + +static void +fu_mbim_qdu_updater_file_open_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) +{ + WriteContext *ctx = user_data; + guint32 out_max_transfer_size; + FuChunk *chk = NULL; + g_autoptr(MbimMessage) request = NULL; + g_autoptr(MbimMessage) response = NULL; + + response = mbim_device_command_finish(device, res, &ctx->error); + if (!response || !mbim_message_response_get_result(response, + MBIM_MESSAGE_TYPE_COMMAND_DONE, + &ctx->error)) { + g_debug("error: operation failed: %s", ctx->error->message); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!mbim_message_qdu_file_open_response_parse(response, + &out_max_transfer_size, + NULL, + &ctx->error)) { + g_debug("error: couldn't parse response message: %s", ctx->error->message); + g_main_loop_quit(ctx->mainloop); + return; + } + + ctx->chunks = fu_chunk_array_new_from_bytes(ctx->blob, + 0x00, /* start addr */ + 0x00, /* page_sz */ + out_max_transfer_size); + chk = g_ptr_array_index(ctx->chunks, 0); + request = mbim_message_qdu_file_write_set_new(fu_chunk_get_data_sz(chk), + (const guint8 *)fu_chunk_get_data(chk), + NULL); + mbim_device_command(ctx->mbim_device, + request, + 10, + NULL, + (GAsyncReadyCallback)fu_mbim_qdu_updater_file_write_ready, + ctx); +} + +static void +fu_mbim_qdu_updater_session_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) +{ + WriteContext *ctx = user_data; + g_autoptr(MbimMessage) response = NULL; + g_autoptr(MbimMessage) request = NULL; + + response = mbim_device_command_finish(device, res, &ctx->error); + if (!response || !mbim_message_response_get_result(response, + MBIM_MESSAGE_TYPE_COMMAND_DONE, + &ctx->error)) { + g_debug("error: operation failed: %s", ctx->error->message); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!mbim_message_qdu_update_session_response_parse(response, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &ctx->error)) { + g_debug("error: couldn't parse response message: %s", ctx->error->message); + g_main_loop_quit(ctx->mainloop); + return; + } + + g_debug("[%s] Successfully request modem to update session", + mbim_device_get_path_display(device)); + + request = mbim_message_qdu_file_open_set_new(MBIM_QDU_FILE_TYPE_LITTLE_ENDIAN_PACKAGE, + g_bytes_get_size(ctx->blob), + NULL); + mbim_device_command(device, + request, + 10, + NULL, + (GAsyncReadyCallback)fu_mbim_qdu_updater_file_open_ready, + ctx); +} + +static void +fu_mbim_qdu_updater_set_update_session(MbimDevice *device, WriteContext *ctx) +{ + g_autoptr(MbimMessage) request = NULL; + + request = mbim_message_qdu_update_session_set_new(MBIM_QDU_SESSION_ACTION_START, + MBIM_QDU_SESSION_TYPE_LE, + NULL); + + mbim_device_command(device, + request, + 10, + NULL, + (GAsyncReadyCallback)fu_mbim_qdu_updater_session_ready, + ctx); +} + +static GArray * +fu_mbim_qdu_updater_get_checksum(GBytes *blob) +{ + gsize file_size; + gsize hash_size; + GArray *digest; + g_autoptr(GChecksum) checksum = NULL; + + /* get checksum, to be used as unique id */ + file_size = g_bytes_get_size(blob); + hash_size = g_checksum_type_get_length(G_CHECKSUM_SHA256); + checksum = g_checksum_new(G_CHECKSUM_SHA256); + g_checksum_update(checksum, g_bytes_get_data(blob, NULL), file_size); + + /* libqmi expects a GArray of bytes, not a GByteArray */ + digest = g_array_sized_new(FALSE, FALSE, sizeof(guint8), hash_size); + g_array_set_size(digest, hash_size); + g_checksum_get_digest(checksum, (guint8 *)digest->data, &hash_size); + + return digest; +} + +GArray * +fu_mbim_qdu_updater_write(FuMbimQduUpdater *self, + const gchar *filename, + GBytes *blob, + FuDevice *device, + FuProgress *progress, + GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + g_autoptr(GArray) digest = fu_mbim_qdu_updater_get_checksum(blob); + g_autoptr(GPtrArray) chunks = NULL; + WriteContext ctx = { + .mainloop = mainloop, + .mbim_device = self->mbim_device, + .error = NULL, + .blob = blob, + .digest = digest, + .chunks = chunks, + .chunk_sent = 0, + .device = device, + .progress = progress, + }; + + fu_mbim_qdu_updater_set_update_session(self->mbim_device, &ctx); + + g_main_loop_run(mainloop); + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return NULL; + } + + return g_steal_pointer(&digest); +} + +MbimDevice * +fu_mbim_qdu_updater_get_mbim_device(FuMbimQduUpdater *self) +{ + return self->mbim_device; +} + +static void +fu_mbim_qdu_updater_init(FuMbimQduUpdater *self) +{ +} + +static void +fu_mbim_qdu_updater_finalize(GObject *object) +{ + FuMbimQduUpdater *self = FU_MBIM_QDU_UPDATER(object); + g_warn_if_fail(self->mbim_device == NULL); + g_free(self->mbim_port); + G_OBJECT_CLASS(fu_mbim_qdu_updater_parent_class)->finalize(object); +} + +static void +fu_mbim_qdu_updater_class_init(FuMbimQduUpdaterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_mbim_qdu_updater_finalize; +} + +FuMbimQduUpdater * +fu_mbim_qdu_updater_new(const gchar *path) +{ + FuMbimQduUpdater *self = g_object_new(FU_TYPE_MBIM_QDU_UPDATER, NULL); + self->mbim_port = g_strdup(path); + return self; +} + +#endif /* MBIM_CHECK_VERSION(1,25,3) */ diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-mbim-qdu-updater.h b/fwupd-1.8.6/plugins/modem-manager/fu-mbim-qdu-updater.h new file mode 100644 index 0000000000000000000000000000000000000000..a9bb610bf97e3bc7ba381e820ad0a82dc28a28f5 --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mbim-qdu-updater.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Jarvis Jiang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-mm-device.h" + +#if MBIM_CHECK_VERSION(1, 25, 3) +#define FU_TYPE_MBIM_QDU_UPDATER (fu_mbim_qdu_updater_get_type()) +G_DECLARE_FINAL_TYPE(FuMbimQduUpdater, fu_mbim_qdu_updater, FU, MBIM_QDU_UPDATER, GObject) + +FuMbimQduUpdater * +fu_mbim_qdu_updater_new(const gchar *mbim_port); +gboolean +fu_mbim_qdu_updater_open(FuMbimQduUpdater *self, GError **error); +GArray * +fu_mbim_qdu_updater_write(FuMbimQduUpdater *self, + const gchar *filename, + GBytes *blob, + FuDevice *device, + FuProgress *progress, + GError **error); +gchar * +fu_mbim_qdu_updater_check_ready(FuMbimQduUpdater *self, GError **error); +gboolean +fu_mbim_qdu_updater_close(FuMbimQduUpdater *self, GError **error); +MbimDevice * +fu_mbim_qdu_updater_get_mbim_device(FuMbimQduUpdater *self); + +#endif /* MBIM_CHECK_VERSION(1,25,3) */ diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-mm-device.c b/fwupd-1.8.6/plugins/modem-manager/fu-mm-device.c new file mode 100644 index 0000000000000000000000000000000000000000..6f3315f6c414ba168eb6130716cf610a2c02e9fb --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mm-device.c @@ -0,0 +1,2056 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#include "fu-mbim-qdu-updater.h" +#include "fu-mm-device.h" +#include "fu-mm-utils.h" +#include "fu-qmi-pdc-updater.h" + +#if MM_CHECK_VERSION(1, 17, 2) +#include "fu-firehose-updater.h" +#endif + +#include "fu-sahara-loader.h" + +/* Amount of time for the modem to boot in fastboot mode. */ +#define FU_MM_DEVICE_REMOVE_DELAY_RE_ENUMERATE 20000 /* ms */ + +/* Amount of time for the modem to be re-probed and exposed in MM after being + * uninhibited. The timeout is long enough to cover the worst case, where the + * modem boots without SIM card inserted (and therefore the initialization + * may be very slow) and also where carrier config switching is explicitly + * required (e.g. if switching from the default (DF) to generic (GC).*/ +#define FU_MM_DEVICE_REMOVE_DELAY_REPROBE 120000 /* ms */ + +#define FU_MM_DEVICE_AT_RETRIES 3 +#define FU_MM_DEVICE_AT_DELAY 3000 /* ms */ + +/* Amount of time for the modem to get firmware version */ +#define MAX_WAIT_TIME_SECS 150 /* s */ + +/** + * FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE + * + * If no AT response is expected when entering fastboot mode. + */ +#define FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE (1 << 0) + +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; + gchar *branch_at; + gint port_at_ifnum; + gint port_qmi_ifnum; + gint port_mbim_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; + + /* mbim-qdu update logic */ + gchar *port_mbim; +#if MBIM_CHECK_VERSION(1, 25, 3) + FuMbimQduUpdater *mbim_qdu_updater; +#endif /* MBIM_CHECK_VERSION(1,25,3) */ + + /* firehose update handling */ + gchar *port_qcdm; + gchar *port_edl; + FuSaharaLoader *sahara_loader; +#if MM_CHECK_VERSION(1, 17, 2) + FuFirehoseUpdater *firehose_updater; +#endif + /* for sahara */ + FuUsbDevice *usb_device; + + /* firmware path */ + gchar *firmware_path; + gchar *restore_firmware_path; +}; + +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, guint idt, GString *str) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + if (self->port_at != NULL) + fu_string_append(str, idt, "AtPort", self->port_at); + if (self->port_qmi != NULL) + fu_string_append(str, idt, "QmiPort", self->port_qmi); + if (self->port_mbim != NULL) + fu_string_append(str, idt, "MbimPort", self->port_mbim); + if (self->port_qcdm != NULL) + fu_string_append(str, idt, "QcdmPort", self->port_qcdm); +} + +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; +} + +gint +fu_mm_device_get_port_qmi_ifnum(FuMmDevice *device) +{ + g_return_val_if_fail(FU_IS_MM_DEVICE(device), -1); + return device->port_qmi_ifnum; +} + +gint +fu_mm_device_get_port_mbim_ifnum(FuMmDevice *device) +{ + g_return_val_if_fail(FU_IS_MM_DEVICE(device), -1); + return device->port_mbim_ifnum; +} + +static gboolean +validate_firmware_update_method(MMModemFirmwareUpdateMethod methods, GError **error) +{ + static const MMModemFirmwareUpdateMethod supported_combinations[] = { + MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, + MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC | MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, +#if MM_CHECK_VERSION(1, 17, 1) + MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU, +#endif /* MM_CHECK_VERSION(1,17,1) */ +#if MM_CHECK_VERSION(1, 17, 2) + MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE, +#endif +#if MM_CHECK_VERSION(1, 19, 1) + MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA, +#endif + }; + g_autofree gchar *methods_str = NULL; + + methods_str = mm_modem_firmware_update_method_build_string_from_mask(methods); + for (guint i = 0; i < G_N_ELEMENTS(supported_combinations); i++) { + if (supported_combinations[i] == methods) { + g_debug("valid firmware update combination: %s", methods_str); + return TRUE; + } + } + + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid firmware update combination: %s", + methods_str); + return FALSE; +} + +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; + GPtrArray *vendors; + g_autoptr(MMFirmwareUpdateSettings) update_settings = NULL; + g_autofree gchar *device_sysfs_path = NULL; + g_autofree gchar *device_bus = 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; + } + + /* make sure the combination is supported */ + if (!validate_firmware_update_method(self->update_methods, error)) + return FALSE; + + /* various fastboot commands */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { + const gchar *tmp; + tmp = mm_firmware_update_settings_get_fastboot_at(update_settings); + if (tmp == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem does not set fastboot command"); + return FALSE; + } + self->detach_fastboot_at = g_strdup(tmp); + } + + /* 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; + } + } + fu_device_add_protocol(device, "com.google.fastboot"); + } + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) { + for (guint i = 0; i < n_ports; i++) { + if ((ports[i].type == MM_MODEM_PORT_TYPE_QMI) || + (ports[i].type == MM_MODEM_PORT_TYPE_MBIM)) { + self->port_qmi = g_strdup_printf("/dev/%s", ports[i].name); + break; + } + } + /* only set if fastboot wasn't already set */ + if (fu_device_get_protocols(device)->len == 0) + fu_device_add_protocol(device, "com.qualcomm.qmi_pdc"); + } +#if MM_CHECK_VERSION(1, 17, 1) + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) { + for (guint i = 0; i < n_ports; i++) { + if (ports[i].type == MM_MODEM_PORT_TYPE_MBIM) { + self->port_mbim = g_strdup_printf("/dev/%s", ports[i].name); + break; + } + } + fu_device_add_protocol(device, "com.qualcomm.mbim_qdu"); + } +#endif /* MM_CHECK_VERSION(1,17,1) */ +#if MM_CHECK_VERSION(1, 17, 2) + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) { + for (guint i = 0; i < n_ports; i++) { + if (ports[i].type == MM_MODEM_PORT_TYPE_QCDM) + self->port_qcdm = g_strdup_printf("/dev/%s", ports[i].name); + else if (ports[i].type == MM_MODEM_PORT_TYPE_MBIM) + self->port_mbim = g_strdup_printf("/dev/%s", ports[i].name); + /* to read secboot status */ + else if (ports[i].type == MM_MODEM_PORT_TYPE_AT) + self->port_at = g_strdup_printf("/dev/%s", ports[i].name); + } + fu_device_add_protocol(device, "com.qualcomm.firehose"); + } +#endif + + 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 MM_CHECK_VERSION(1, 17, 1) + /* a mbim port is required for mbim-qdu */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) && + (self->port_mbim == NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find MBIM port"); + return FALSE; + } + +#endif /* MM_CHECK_VERSION(1,17,1) */ +#if MM_CHECK_VERSION(1, 17, 2) + /* a qcdm or mbim port is required for firehose */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) && + (self->port_qcdm == NULL && self->port_mbim == NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find QCDM port"); + return FALSE; + } +#endif + + if (self->port_at != NULL) { + fu_mm_utils_get_port_info(self->port_at, + &device_bus, + &device_sysfs_path, + &self->port_at_ifnum, + NULL); + } + if (self->port_qmi != NULL) { + g_autofree gchar *qmi_device_sysfs_path = NULL; + g_autofree gchar *qmi_device_bus = NULL; + fu_mm_utils_get_port_info(self->port_qmi, + &qmi_device_bus, + &qmi_device_sysfs_path, + &self->port_qmi_ifnum, + NULL); + if (device_sysfs_path == NULL && qmi_device_sysfs_path != NULL) { + device_sysfs_path = g_steal_pointer(&qmi_device_sysfs_path); + } else if (g_strcmp0(device_sysfs_path, qmi_device_sysfs_path) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device sysfs path: %s != %s", + device_sysfs_path, + qmi_device_sysfs_path); + return FALSE; + } + if (device_bus == NULL && qmi_device_bus != NULL) { + device_bus = g_steal_pointer(&qmi_device_bus); + } else if (g_strcmp0(device_bus, qmi_device_bus) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device bus: %s != %s", + device_bus, + qmi_device_bus); + return FALSE; + } + } + if (self->port_mbim != NULL) { + g_autofree gchar *mbim_device_sysfs_path = NULL; + g_autofree gchar *mbim_device_bus = NULL; + fu_mm_utils_get_port_info(self->port_mbim, + &mbim_device_bus, + &mbim_device_sysfs_path, + &self->port_mbim_ifnum, + NULL); + if (device_sysfs_path == NULL && mbim_device_sysfs_path != NULL) { + device_sysfs_path = g_steal_pointer(&mbim_device_sysfs_path); + } else if (g_strcmp0(device_sysfs_path, mbim_device_sysfs_path) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device sysfs path: %s != %s", + device_sysfs_path, + mbim_device_sysfs_path); + return FALSE; + } + if (device_bus == NULL && mbim_device_bus != NULL) { + device_bus = g_steal_pointer(&mbim_device_bus); + } else if (g_strcmp0(device_bus, mbim_device_bus) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device bus: %s != %s", + device_bus, + mbim_device_bus); + return FALSE; + } + } + + if (self->port_qcdm != NULL) { + g_autofree gchar *qcdm_device_sysfs_path = NULL; + g_autofree gchar *qcdm_device_bus = NULL; + fu_mm_utils_get_port_info(self->port_qcdm, + &qcdm_device_bus, + &qcdm_device_sysfs_path, + NULL, + NULL); + if (device_sysfs_path == NULL && qcdm_device_sysfs_path != NULL) { + device_sysfs_path = g_steal_pointer(&qcdm_device_sysfs_path); + } else if (g_strcmp0(device_sysfs_path, qcdm_device_sysfs_path) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device sysfs path: %s != %s", + device_sysfs_path, + qcdm_device_sysfs_path); + return FALSE; + } + if (device_bus == NULL && qcdm_device_bus != NULL) { + device_bus = g_steal_pointer(&qcdm_device_bus); + } else if (g_strcmp0(device_bus, qcdm_device_bus) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device bus: %s != %s", + device_bus, + qcdm_device_bus); + return FALSE; + } + } + + /* if no device sysfs file, error out */ + if (device_sysfs_path == NULL || device_bus == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find device details"); + 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); + for (guint i = 0; device_ids[i] != NULL; i++) + fu_device_add_instance_id(device, device_ids[i]); + vendors = fu_device_get_vendor_ids(device); + if (vendors == NULL || vendors->len == 0) { + g_autofree gchar *path = NULL; + g_autofree gchar *value_str = NULL; + g_autoptr(GError) error_local = NULL; + + if (g_strcmp0(device_bus, "USB") == 0) + path = g_build_filename(device_sysfs_path, "idVendor", NULL); + else if (g_strcmp0(device_bus, "PCI") == 0) + path = g_build_filename(device_sysfs_path, "vendor", NULL); + + if (path == NULL) { + g_warning("failed to set vendor ID: unsupported bus: %s", device_bus); + } else if (!g_file_get_contents(path, &value_str, NULL, &error_local)) { + g_warning("failed to set vendor ID: %s", error_local->message); + } else { + guint64 value_int; + + /* note: the string value may be prefixed with '0x' (e.g. when reading + * the PCI 'vendor' attribute, or not prefixed with anything, as in the + * USB 'idVendor' attribute. */ + value_int = g_ascii_strtoull(value_str, NULL, 16); + if (value_int > G_MAXUINT16) { + g_warning("failed to set vendor ID: invalid value: %s", value_str); + } else { + g_autofree gchar *vendor_id = + g_strdup_printf("%s:0x%04X", device_bus, (guint)value_int); + fu_device_add_vendor_id(device, vendor_id); + } + } + } + + /* fix up vendor name */ + if (g_strcmp0(fu_device_get_vendor(device), "QUALCOMM INCORPORATED") == 0) + fu_device_set_vendor(device, "Qualcomm"); + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_device_probe_udev(FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + + /* an at port is required for fastboot */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && + (self->port_at == NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find AT port"); + return FALSE; + } + + /* 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); + } +} + +#if MM_CHECK_VERSION(1, 17, 2) +static gboolean +fu_mm_device_io_open_qcdm(FuMmDevice *self, GError **error) +{ + /* sanity check */ + if (self->port_qcdm == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no QCDM port provided for filename"); + return FALSE; + } + + /* open device */ + self->io_channel = fu_io_channel_new_file(self->port_qcdm, error); + if (self->io_channel == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_device_qcdm_cmd(FuMmDevice *self, const guint8 *cmd, gsize cmd_len, GError **error) +{ + g_autoptr(GBytes) qcdm_req = NULL; + g_autoptr(GBytes) qcdm_res = NULL; + + /* command */ + qcdm_req = g_bytes_new(cmd, cmd_len); + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "writing", qcdm_req); + if (!fu_io_channel_write_bytes(self->io_channel, + qcdm_req, + 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, + error)) { + g_prefix_error(error, "failed to write qcdm command: "); + return FALSE; + } + + /* response */ + qcdm_res = fu_io_channel_read_bytes(self->io_channel, + -1, + 1500, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); + if (qcdm_res == NULL) { + g_prefix_error(error, "failed to read qcdm response: "); + return FALSE; + } + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "read", qcdm_res); + + /* command == response */ + if (g_bytes_compare(qcdm_res, qcdm_req) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read valid qcdm response"); + return FALSE; + } + + return TRUE; +} +#endif /* MM_CHECK_VERSION(1,17,2) */ + +typedef struct { + const gchar *cmd; + gboolean has_response; +} FuMmDeviceAtCmdHelper; + +static gboolean +fu_mm_device_at_cmd_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + FuMmDeviceAtCmdHelper *helper = (FuMmDeviceAtCmdHelper *)user_data; + 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", helper->cmd); + + /* command */ + at_req = g_bytes_new(cmd_cr, strlen(cmd_cr)); + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "writing", at_req); + if (!fu_io_channel_write_bytes(self->io_channel, + at_req, + 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, + error)) { + g_prefix_error(error, "failed to write %s: ", helper->cmd); + return FALSE; + } + + /* AT command has no response, return TRUE */ + if (!helper->has_response) { + g_debug("No response expected for AT command: '%s', assuming succeed", helper->cmd); + return TRUE; + } + + /* 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: ", helper->cmd); + return FALSE; + } + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_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", + helper->cmd); + return FALSE; + } + + /* return error if AT command failed */ + if (g_strrstr(buf, "\r\nOK\r\n") == NULL) { + 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", + helper->cmd, + tmp); + return FALSE; + } + + /* set firmware branch if returned */ + if (self->branch_at != NULL && g_strcmp0(helper->cmd, self->branch_at) == 0) { + /* + * example AT+GETFWBRANCH response: + * + * \r\nFOSS-002 \r\n\r\nOK\r\n + * + * remove \r\n, and OK to get branch name + */ + g_auto(GStrv) parts = g_strsplit(buf, "\r\n", -1); + + for (int j = 0; parts[j] != NULL; j++) { + /* Ignore empty strings, and OK responses */ + if (g_strcmp0(parts[j], "") != 0 && g_strcmp0(parts[j], "OK") != 0) { + /* Set branch */ + fu_device_set_branch(FU_DEVICE(self), parts[j]); + g_debug("Firmware branch reported as '%s'", parts[j]); + break; + } + } + } + + if (g_strcmp0(helper->cmd, "AT+QSECBOOT=\"status\"") == 0) { + /* + * example AT+QSECBOOT="status" response: + * + * \r\n+QSECBOOT: "STATUS",1\r\n\r\nOK\r\n + * + * Secure boot status: + * 1 - enabled + * 0 - disabled + */ + g_auto(GStrv) parts = g_strsplit(buf, "\r\n", -1); + + for (int j = 0; parts[j] != NULL; j++) { + if (g_strcmp0(parts[j], "+QSECBOOT: \"status\",1") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + break; + } + if (g_strcmp0(parts[j], "+QSECBOOT: \"status\",0") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + break; + } + } + } + + if (g_strcmp0(helper->cmd, "AT+QCFG=\"secbootstat\"") == 0) { + /* + * example AT+QSECBOOT="status" response: + * + * \r\n+QSECBOOT: "STATUS",1\r\n\r\nOK\r\n + * + * Secure boot status: + * 1 - enabled + * 0 - disabled + */ + g_auto(GStrv) parts = g_strsplit(buf, "\r\n", -1); + + for (int j = 0; parts[j] != NULL; j++) { + if (g_strcmp0(parts[j], "+QCFG: \"secbootstat\",1") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + break; + } + if (g_strcmp0(parts[j], "+QCFG: \"secbootstat\",0") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + break; + } + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_device_at_cmd(FuMmDevice *self, const gchar *cmd, gboolean has_response, GError **error) +{ + FuMmDeviceAtCmdHelper helper = {.cmd = cmd, .has_response = has_response}; + return fu_device_retry_full(FU_DEVICE(self), + fu_mm_device_at_cmd_cb, + FU_MM_DEVICE_AT_RETRIES, + FU_MM_DEVICE_AT_DELAY, + &helper, + error); +} + +static gboolean +fu_mm_device_io_open(FuMmDevice *self, GError **error) +{ + /* sanity check */ + if (self->port_at == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no AT port provided for filename"); + return FALSE; + } + + /* open device */ + self->io_channel = fu_io_channel_new_file(self->port_at, error); + if (self->io_channel == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_device_io_close(FuMmDevice *self, GError **error) +{ + if (self->io_channel != NULL) { + if (!fu_io_channel_shutdown(self->io_channel, error)) + return FALSE; + g_clear_object(&self->io_channel); + } + return TRUE; +} + +static gboolean +fu_mm_device_detach_fastboot(FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + gboolean has_response = TRUE; + + /* boot to fastboot mode */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_mm_device_io_open, + (FuDeviceLockerFunc)fu_mm_device_io_close, + error); + + /* expect response for fastboot AT command */ + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE)) { + has_response = FALSE; + } + + if (locker == NULL) + return FALSE; + if (!fu_mm_device_at_cmd(self, "AT", TRUE, error)) + return FALSE; + if (!fu_mm_device_at_cmd(self, self->detach_fastboot_at, has_response, error)) { + g_prefix_error(error, "rebooting into fastboot not supported: "); + return FALSE; + } + + /* success */ + fu_device_set_remove_delay(device, FU_MM_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* This plugin supports several methods to download firmware: + * fastboot, qmi-pdc, firehose. A modem may require one of those, + * or several, depending on the update type or the modem type. + * + * The first time this detach() method is executed is always for a + * FuMmDevice that was created from a MM-exposed modem, which is the + * moment when we're going to decide the amount of retries we need to + * flash all firmware. + * + * If the FuMmModem is created from a MM-exposed modem and... + * a) we only support fastboot, we just trigger the fastboot detach. + * b) we support both fastboot and qmi-pdc, we will set the + * ANOTHER_WRITE_REQUIRED flag in the device and we'll trigger + * the fastboot detach. + * c) we only support firehose, skip detach and switch to embedded + * downloader mode (EDL) during write_firmware. + * + * If the FuMmModem is created from udev events... + * c) it means we're in the extra required write that was flagged + * in an earlier detach(), and we need to perform the qmi-pdc + * update procedure at this time, so we just exit without any + * detach. + */ + + /* FuMmDevice created from MM... */ + if (self->omodem != NULL) { + /* both fastboot and qmi-pdc supported? another write required */ + if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && + (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC)) { + g_debug("both fastboot and qmi-pdc supported, so the upgrade requires " + "another write"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + } + /* 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; +} 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 gboolean +fu_mm_qmi_pdc_archive_iterate_mcfg(FuArchive *archive, + const gchar *filename, + GBytes *bytes, + gpointer user_data, + GError **error) +{ + FuMmArchiveIterateCtx *ctx = user_data; + FuMmFileInfo *file_info; + + /* filenames should be named as 'mcfg.*.mbn', e.g.: mcfg.A2.018.mbn */ + if (!g_str_has_prefix(filename, "mcfg.") || !g_str_has_suffix(filename, ".mbn")) + return TRUE; + + file_info = g_new0(FuMmFileInfo, 1); + file_info->filename = g_strdup(filename); + file_info->bytes = g_bytes_ref(bytes); + file_info->active = + fu_mm_should_be_active(fu_device_get_version(FU_DEVICE(ctx->device)), filename); + g_ptr_array_add(ctx->file_infos, file_info); + return TRUE; +} + +static gboolean +fu_mm_device_qmi_open(FuMmDevice *self, GError **error) +{ + self->qmi_pdc_updater = fu_qmi_pdc_updater_new(self->port_qmi); + return fu_qmi_pdc_updater_open(self->qmi_pdc_updater, error); +} + +static gboolean +fu_mm_device_qmi_close(FuMmDevice *self, GError **error) +{ + g_autoptr(FuQmiPdcUpdater) updater = NULL; + + updater = g_steal_pointer(&self->qmi_pdc_updater); + return fu_qmi_pdc_updater_close(updater, error); +} + +static gboolean +fu_mm_device_qmi_close_no_error(FuMmDevice *self, GError **error) +{ + g_autoptr(FuQmiPdcUpdater) updater = NULL; + + updater = g_steal_pointer(&self->qmi_pdc_updater); + fu_qmi_pdc_updater_close(updater, NULL); + return TRUE; +} + +static gboolean +fu_mm_device_write_firmware_qmi_pdc(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, + }; + + /* 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 */ + if (!fu_archive_iterate(archive, + fu_mm_qmi_pdc_archive_iterate_mcfg, + &archive_context, + error)) + return FALSE; + + for (guint i = 0; i < file_infos->len; i++) { + FuMmFileInfo *file_info = g_ptr_array_index(file_infos, i); + file_info->digest = + fu_qmi_pdc_updater_write(archive_context.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; +} + +#if MM_CHECK_VERSION(1, 17, 1) && MBIM_CHECK_VERSION(1, 25, 3) +typedef struct { + FuDevice *device; + GMainLoop *mainloop; + gchar *version; + GError *error; +} FuMmGetFirmwareVersionCtx; + +static gboolean +fu_mm_device_mbim_open(FuMmDevice *self, GError **error) +{ + self->mbim_qdu_updater = fu_mbim_qdu_updater_new(self->port_mbim); + return fu_mbim_qdu_updater_open(self->mbim_qdu_updater, error); +} + +static gboolean +fu_mm_device_mbim_close(FuMmDevice *self, GError **error) +{ + g_autoptr(FuMbimQduUpdater) updater = NULL; + updater = g_steal_pointer(&self->mbim_qdu_updater); + return fu_mbim_qdu_updater_close(updater, error); +} + +static gboolean +fu_mm_device_locker_new_timeout(gpointer user_data) +{ + FuMmGetFirmwareVersionCtx *ctx = user_data; + + g_main_loop_quit(ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static gboolean +fu_mm_device_get_firmware_version_mbim_timeout(gpointer user_data) +{ + FuMmGetFirmwareVersionCtx *ctx = user_data; + FuMmDevice *self = FU_MM_DEVICE(ctx->device); + + g_clear_error(&ctx->error); + ctx->version = fu_mbim_qdu_updater_check_ready(self->mbim_qdu_updater, &ctx->error); + g_main_loop_quit(ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static gchar * +fu_mm_device_get_firmware_version_mbim(FuDevice *device, GError **error) +{ + GTimer *timer = g_timer_new(); + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + FuMmGetFirmwareVersionCtx ctx = { + .device = device, + .mainloop = mainloop, + .version = NULL, + .error = NULL, + }; + + while (ctx.version == NULL && g_timer_elapsed(timer, NULL) < MAX_WAIT_TIME_SECS) { + g_autoptr(FuDeviceLocker) locker = NULL; + + g_clear_error(&ctx.error); + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_mm_device_mbim_open, + (FuDeviceLockerFunc)fu_mm_device_mbim_close, + &ctx.error); + + if (locker == NULL) { + g_timeout_add_seconds(20, fu_mm_device_locker_new_timeout, &ctx); + g_main_loop_run(mainloop); + continue; + } + + g_timeout_add_seconds(10, fu_mm_device_get_firmware_version_mbim_timeout, &ctx); + g_main_loop_run(mainloop); + } + + g_timer_destroy(timer); + + if (ctx.version == NULL && ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return NULL; + } + + return ctx.version; +} + +static gboolean +fu_mm_device_writeln(const gchar *fn, const gchar *buf, GError **error) +{ + int fd; + g_autoptr(FuIOChannel) io = NULL; + + fd = open(fn, O_WRONLY); + if (fd < 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "could not open %s", fn); + return FALSE; + } + io = fu_io_channel_unix_new(fd); + return fu_io_channel_write_raw(io, + (const guint8 *)buf, + strlen(buf), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error); +} + +static gboolean +fu_mm_device_write_firmware_mbim_qdu(FuDevice *device, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + GBytes *data; + XbNode *part = NULL; + const gchar *filename = NULL; + const gchar *csum; + FuMmDevice *self = FU_MM_DEVICE(device); + g_autofree gchar *device_sysfs_path = NULL; + g_autofree gchar *autosuspend_delay_filename = NULL; + g_autofree gchar *csum_actual = NULL; + g_autofree gchar *version = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GArray) digest = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbSilo) silo = NULL; + + /* decompress entire archive ahead of time */ + archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_mm_device_mbim_open, + (FuDeviceLockerFunc)fu_mm_device_mbim_close, + error); + if (locker == NULL) + return FALSE; + + /* load the manifest of operations */ + data = 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; + + part = xb_silo_query_first(silo, "parts/part", error); + if (part == NULL) + return FALSE; + filename = xb_node_get_attr(part, "filename"); + csum = xb_node_get_attr(part, "MD5"); + data = fu_archive_lookup_by_fn(archive, filename, error); + if (data == NULL) + return FALSE; + csum_actual = g_compute_checksum_for_bytes(G_CHECKSUM_MD5, data); + if (g_strcmp0(csum, csum_actual) != 0) { + g_debug("[%s] MD5 not matched", filename); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "[%s] MD5 not matched", + filename); + return FALSE; + } else { + g_debug("[%s] MD5 matched", filename); + } + + /* autosuspend delay updated for a proper firmware update */ + fu_mm_utils_get_port_info(self->port_mbim, NULL, &device_sysfs_path, NULL, NULL); + autosuspend_delay_filename = + g_build_filename(device_sysfs_path, "/power/autosuspend_delay_ms", NULL); + if (!fu_mm_device_writeln(autosuspend_delay_filename, "10000", error)) + return FALSE; + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + digest = fu_mbim_qdu_updater_write(self->mbim_qdu_updater, + filename, + data, + device, + progress, + error); + if (digest == NULL) + return FALSE; + if (!fu_device_locker_close(locker, error)) + return FALSE; + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + version = fu_mm_device_get_firmware_version_mbim(device, error); + if (version == NULL) + return FALSE; + + return TRUE; +} + +#endif /* MM_CHECK_VERSION(1,17,1) && MBIM_CHECK_VERSION(1,25,3) */ + +#if MM_CHECK_VERSION(1, 17, 2) +static gboolean +fu_mm_find_device_file(FuDevice *device, gpointer userdata, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + const gchar *subsystem = (const gchar *)userdata; + + return fu_mm_utils_find_device_file(fu_device_get_physical_id(FU_DEVICE(self)), + subsystem, + &self->port_edl, + error); +} + +static gboolean +fu_mm_device_find_edl_port(FuDevice *device, const gchar *subsystem, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + + g_clear_pointer(&self->port_edl, g_free); + + return fu_device_retry_full(device, + fu_mm_find_device_file, + 30, + 250, + (gpointer)subsystem, + error); +} + +static gboolean +fu_mm_device_qcdm_switch_to_edl(FuDevice *device, gpointer userdata, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + g_autoptr(GError) error_local = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + static const guint8 emergency_download[] = {0x4b, 0x65, 0x01, 0x00, 0x54, 0x0f, 0x7e}; + + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_mm_device_io_open_qcdm, + (FuDeviceLockerFunc)fu_mm_device_io_close, + &error_local); + + if (locker == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) { + return fu_mm_device_find_edl_port(device, "wwan", error); + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + if (!fu_mm_device_qcdm_cmd(self, + emergency_download, + G_N_ELEMENTS(emergency_download), + error)) + return FALSE; + + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "Device haven't switched to EDL yet"); + return FALSE; +} + +#if MBIM_CHECK_VERSION(1, 27, 5) +static void +fu_mm_device_switch_to_edl_mbim_ready(MbimDevice *device, GAsyncResult *res, GMainLoop *loop) +{ + /* No need to check for a response since MBIM + * port goes away without sending one */ + + g_main_loop_quit(loop); +} + +static gboolean +fu_mm_device_mbim_switch_to_edl(FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(MbimMessage) message = NULL; + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_mm_device_mbim_open, + (FuDeviceLockerFunc)fu_mm_device_mbim_close, + error); + if (locker == NULL) + return FALSE; + + message = mbim_message_qdu_quectel_reboot_set_new(MBIM_QDU_QUECTEL_REBOOT_TYPE_EDL, NULL); + mbim_device_command(fu_mbim_qdu_updater_get_mbim_device(self->mbim_qdu_updater), + message, + 5, + NULL, + (GAsyncReadyCallback)fu_mm_device_switch_to_edl_mbim_ready, + mainloop); + + g_main_loop_run(mainloop); + + return TRUE; +} +#endif // MBIM_CHECK_VERSION(1, 27, 5) + +static gboolean +fu_mm_device_firehose_open(FuMmDevice *self, GError **error) +{ + self->firehose_updater = fu_firehose_updater_new(self->port_edl, self->sahara_loader); + return fu_firehose_updater_open(self->firehose_updater, error); +} + +static gboolean +fu_mm_device_firehose_close(FuMmDevice *self, GError **error) +{ + g_autoptr(FuFirehoseUpdater) updater = NULL; + + updater = g_steal_pointer(&self->firehose_updater); + return fu_firehose_updater_close(updater, error); +} + +static gboolean +fu_mm_device_firehose_write(FuMmDevice *self, + XbSilo *rawprogram_silo, + GPtrArray *rawprogram_actions, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_mm_device_firehose_open, + (FuDeviceLockerFunc)fu_mm_device_firehose_close, + error); + if (locker == NULL) + return FALSE; + return fu_firehose_updater_write(self->firehose_updater, + rawprogram_silo, + rawprogram_actions, + progress, + error); +} + +#if MM_CHECK_VERSION(1, 19, 1) && MBIM_CHECK_VERSION(1, 25, 7) +static gboolean +fu_mm_device_sahara_open(FuMmDevice *self, GError **error) +{ + self->sahara_loader = fu_sahara_loader_new(); + return fu_sahara_loader_open(self->sahara_loader, self->usb_device, error); +} + +static gboolean +fu_mm_device_sahara_close(FuMmDevice *self, GError **error) +{ + g_autoptr(FuSaharaLoader) loader = NULL; + + loader = g_steal_pointer(&self->sahara_loader); + return fu_sahara_loader_close(loader, error); +} +#endif // MM_CHECK_VERSION(1, 19, 1) && MBIM_CHECK_VERSION(1, 25, 7) + +static gboolean +fu_mm_setup_firmware_dir(FuMmDevice *self, GError **error) +{ + g_autofree gchar *cachedir = NULL; + g_autofree gchar *mm_fw_dir = NULL; + + /* create a directory to store firmware files for modem-manager plugin */ + cachedir = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); + mm_fw_dir = g_build_filename(cachedir, "modem-manager", "firmware", NULL); + if (g_mkdir_with_parents(mm_fw_dir, 0700) == -1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to create '%s': %s", + mm_fw_dir, + g_strerror(errno)); + return FALSE; + } + + if (!fu_kernel_set_firmware_search_path(mm_fw_dir, error)) + return FALSE; + + self->firmware_path = g_steal_pointer(&mm_fw_dir); + + return TRUE; +} + +static gboolean +fu_mm_copy_firehose_prog(FuMmDevice *self, GBytes *prog, GError **error) +{ + g_autofree gchar *qcom_fw_dir = NULL; + g_autofree gchar *firehose_file_path = NULL; + + qcom_fw_dir = g_build_filename(self->firmware_path, "qcom", NULL); + if (!fu_path_mkdir_parent(qcom_fw_dir, error)) + return FALSE; + + firehose_file_path = g_build_filename(qcom_fw_dir, "prog_firehose_sdx24.mbn", NULL); + + if (!fu_bytes_set_contents(firehose_file_path, prog, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_mm_prepare_firmware_search_path(FuMmDevice *self, GError **error) +{ + self->restore_firmware_path = fu_kernel_get_firmware_search_path(NULL); + + return fu_mm_setup_firmware_dir(self, error); +} + +static gboolean +fu_mm_restore_firmware_search_path(FuMmDevice *self, GError **error) +{ + if (self->restore_firmware_path != NULL && strlen(self->restore_firmware_path) > 0) + return fu_kernel_set_firmware_search_path(self->restore_firmware_path, error); + + return fu_kernel_reset_firmware_search_path(error); +} + +static gboolean +fu_mm_device_write_firmware_firehose(FuDevice *device, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + MMModem *modem = mm_object_peek_modem(self->omodem); + GBytes *firehose_rawprogram; + GBytes *firehose_prog; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(XbSilo) firehose_rawprogram_silo = NULL; + g_autoptr(GPtrArray) firehose_rawprogram_actions = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + + /* decompress entire archive ahead of time */ + archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + /* lookup and validate firehose-rawprogram actions */ + firehose_rawprogram = fu_archive_lookup_by_fn(archive, "firehose-rawprogram.xml", error); + if (firehose_rawprogram == NULL) + return FALSE; + if (!fu_firehose_validate_rawprogram(firehose_rawprogram, + archive, + &firehose_rawprogram_silo, + &firehose_rawprogram_actions, + error)) { + g_prefix_error(error, "Invalid firehose rawprogram manifest: "); + return FALSE; + } + + /* lookup firehose-prog bootloader */ + firehose_prog = fu_archive_lookup_by_fn(archive, "firehose-prog.mbn", error); + if (firehose_prog == NULL) + return FALSE; + fu_progress_step_done(progress); + + /* Firehose program needs to be loaded to the modem before firehose update process can + * start. Generally, modems use Sahara protocol to load the firehose binary. + * + * In case of MHI PCI modems, the mhi-pci-generic driver reads the firehose binary from the + * firmware-loader and writes it to the modem. + **/ + if (g_strv_contains(mm_modem_get_drivers(modem), "mhi-pci-generic") && + self->port_qcdm != NULL) { + /* modify firmware search path and restore it before function returns */ + locker = fu_device_locker_new_full( + self, + (FuDeviceLockerFunc)fu_mm_prepare_firmware_search_path, + (FuDeviceLockerFunc)fu_mm_restore_firmware_search_path, + error); + if (locker == NULL) + return FALSE; + + /* firehose modems that use mhi_pci drivers require firehose binary + * to be present in the firmware-loader search path. */ + if (!fu_mm_copy_firehose_prog(self, firehose_prog, error)) + return FALSE; + /* trigger emergency download mode, up to 30s retrying until the QCDM + * port goes away; this takes us to the EDL (embedded downloader) execution + * environment */ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_mm_device_qcdm_switch_to_edl, + 30, + 1000, + NULL, + error)) + return FALSE; + + g_debug("found edl port: %s", self->port_edl); + } +#if MM_CHECK_VERSION(1, 19, 1) && MBIM_CHECK_VERSION(1, 25, 7) + else if ((FU_MM_DEVICE(self)->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA) && + self->port_mbim != NULL) { + /* switch to emergency download (EDL) execution environment */ + if (!fu_mm_device_mbim_switch_to_edl(device, error)) + return FALSE; + + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_mm_device_sahara_open, + (FuDeviceLockerFunc)fu_mm_device_sahara_close, + error); + if (locker == NULL) + return FALSE; + + /* use sahara port to load firehose binary */ + if (!fu_sahara_loader_run(self->sahara_loader, firehose_prog, error)) + return FALSE; + } +#endif // MM_CHECK_VERSION(1, 19, 1) && MBIM_CHECK_VERSION(1, 25, 7) + else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "suitable port not found"); + return FALSE; + } + fu_progress_step_done(progress); + + /* download all files in the firehose-rawprogram manifest via Firehose */ + if (!fu_mm_device_firehose_write(self, + firehose_rawprogram_silo, + firehose_rawprogram_actions, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* flag as restart again, the module is switching to modem mode */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); + return TRUE; +} + +#endif /* MM_CHECK_VERSION(1,17,2) */ + +static gboolean +fu_mm_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* lock device */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* 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); + +#if MM_CHECK_VERSION(1, 17, 1) && MBIM_CHECK_VERSION(1, 25, 3) + /* mbim qdu write operation */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) + return fu_mm_device_write_firmware_mbim_qdu(device, fw, progress, error); + +#endif /* MM_CHECK_VERSION(1,17,1) && MBIM_CHECK_VERSION(1,25,3) */ +#if MM_CHECK_VERSION(1, 17, 2) + /* firehose operation */ + if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) + return fu_mm_device_write_firmware_firehose(device, fw, progress, error); +#endif + + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "unsupported update method"); + return FALSE; +} + +static gboolean +fu_mm_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + + /* load from quirks */ + if (g_strcmp0(key, "ModemManagerBranchAtCommand") == 0) { + self->branch_at = g_strdup(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_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, FuProgress *progress, 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 gboolean +fu_mm_device_setup_branch_at(FuMmDevice *self, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* nothing to do if there is no AT port available or + * ModemManagerBranchAtCommand quirk is not set */ + if (self->port_at == NULL || self->branch_at == NULL) + return TRUE; + + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Firmware branches are not supported if the devices is signed"); + return FALSE; + } + + /* Create IO channel to send AT commands to the modem */ + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_mm_device_io_open, + (FuDeviceLockerFunc)fu_mm_device_io_close, + error); + if (locker == NULL) + return FALSE; + + if (!fu_mm_device_at_cmd(self, self->branch_at, TRUE, error)) + return FALSE; + + if (fu_device_get_branch(self) != NULL) + g_debug("using firmware branch: %s", fu_device_get_branch(self)); + else + g_debug("using firmware branch: default"); + + return TRUE; +} + +static void +fu_mm_device_setup_secboot_status_quectel(FuMmDevice *self) +{ + const gchar *version = fu_device_get_version(FU_DEVICE(self)); + g_autofree gchar *name = NULL; + + const gchar *at_cmd[] = {"AT+QSECBOOT=\"status\"", "AT+QCFG=\"secbootstat\"", NULL}; + + struct { + const gchar *name; + const gchar *version; + } secboot[] = {{"EM05GF", "EM05GFAR07A07M1G_01.005.01.005"}, + {"EM05CE", "EM05CEFCR08A16M1G_LNV"}, + {NULL, NULL}}; + + if (self->port_at != NULL) { + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; + + /* Create IO channel to send AT commands to the modem */ + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_mm_device_io_open, + (FuDeviceLockerFunc)fu_mm_device_io_close, + &error_local); + if (locker == NULL) { + g_debug("failed to open AT port: %s", error_local->message); + return; + } + + /* try to query sec boot status with AT commands */ + for (guint i = 0; at_cmd[i] != NULL; i++) { + g_autoptr(GError) error_loop = NULL; + if (!fu_mm_device_at_cmd(self, at_cmd[i], TRUE, &error_loop)) { + g_debug("AT command failed (%s): %s", + at_cmd[i], + error_loop->message); + } else { + return; + } + } + } + + /* find model name and compare with table from Quectel */ + if (version == NULL) + return; + name = g_strndup(version, 6); + for (guint i = 0; secboot[i].name != NULL; i++) { + if (g_strcmp0(name, secboot[i].name) == 0) { + if (fu_version_compare(version, + secboot[i].version, + FWUPD_VERSION_FORMAT_PLAIN) >= 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } else { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + } + return; + } + } +} + +static void +fu_mm_device_setup_secboot_status(FuDevice *device) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + + if (fu_device_has_vendor_id(device, "USB:0x2C7C") || + fu_device_has_vendor_id(device, "PCI:0x1EAC")) + fu_mm_device_setup_secboot_status_quectel(self); +} + +static gboolean +fu_mm_device_setup(FuDevice *device, GError **error) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + fu_mm_device_setup_secboot_status(device); + + if (!fu_mm_device_setup_branch_at(self, &error_local)) + g_warning("Failed to set firmware branch: %s", error_local->message); + + return TRUE; +} + +static void +fu_mm_device_incorporate(FuDevice *device, FuDevice *donor_device) +{ + FuMmDevice *self = FU_MM_DEVICE(device); + FuMmDevice *donor = FU_MM_DEVICE(donor_device); + + self->update_methods = fu_mm_device_get_update_methods(donor); + self->detach_fastboot_at = g_strdup(fu_mm_device_get_detach_fastboot_at(donor)); + self->inhibition_uid = g_strdup(fu_mm_device_get_inhibition_uid(donor)); + self->port_at_ifnum = fu_mm_device_get_port_at_ifnum(donor); + self->port_qmi_ifnum = fu_mm_device_get_port_qmi_ifnum(donor); + self->port_mbim_ifnum = fu_mm_device_get_port_mbim_ifnum(donor); + g_set_object(&self->manager, donor->manager); +} + +static void +fu_mm_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_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_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_summary(FU_DEVICE(self), "Mobile broadband device"); + fu_device_add_icon(FU_DEVICE(self), "modem"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE, + "detach-at-fastboot-has-no-response"); +} + +static void +fu_mm_device_finalize(GObject *object) +{ + FuMmDevice *self = FU_MM_DEVICE(object); + if (self->usb_device != NULL) + g_object_unref(self->usb_device); + if (self->attach_idle) + g_source_remove(self->attach_idle); + if (self->qmi_pdc_active_id) + g_array_unref(self->qmi_pdc_active_id); + if (self->manager != NULL) + g_object_unref(self->manager); + if (self->omodem != NULL) + g_object_unref(self->omodem); + g_free(self->detach_fastboot_at); + g_free(self->branch_at); + g_free(self->port_at); + g_free(self->port_qmi); + g_free(self->port_mbim); + g_free(self->port_qcdm); + g_free(self->inhibition_uid); + g_free(self->firmware_path); + g_free(self->restore_firmware_path); + 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->setup = fu_mm_device_setup; + klass_device->reload = fu_mm_device_setup; + klass_device->to_string = fu_mm_device_to_string; + klass_device->set_quirk_kv = fu_mm_device_set_quirk_kv; + 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; + klass_device->set_progress = fu_mm_device_set_progress; + klass_device->incorporate = fu_mm_device_incorporate; + + /** + * FuMmDevice::attach-finished: + * @self: the #FuMmDevice instance that emitted the signal + * + * The ::attach-finished signal is emitted when the device has attached. + **/ + signals[SIGNAL_ATTACH_FINISHED] = g_signal_new("attach-finished", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +FuMmDevice * +fu_mm_device_new(FuContext *ctx, MMManager *manager, MMObject *omodem) +{ + FuMmDevice *self = g_object_new(FU_TYPE_MM_DEVICE, "context", ctx, NULL); + self->manager = g_object_ref(manager); + self->omodem = g_object_ref(omodem); + self->port_at_ifnum = -1; + self->port_qmi_ifnum = -1; + self->port_mbim_ifnum = -1; + return self; +} + +FuMmDevice * +fu_mm_shadow_device_new(FuMmDevice *device) +{ + FuMmDevice *shadow_device = NULL; + shadow_device = g_object_new(FU_TYPE_MM_DEVICE, + "context", + fu_device_get_context(FU_DEVICE(device)), + NULL); + fu_device_incorporate(FU_DEVICE(shadow_device), FU_DEVICE(device)); + return shadow_device; +} + +FuUsbDevice * +fu_mm_device_get_usb_device(FuMmDevice *self) +{ + g_return_val_if_fail(FU_IS_MM_DEVICE(self), NULL); + return self->usb_device; +} + +void +fu_mm_device_set_usb_device(FuMmDevice *self, FuUsbDevice *usb_device) +{ + g_return_if_fail(FU_IS_MM_DEVICE(self)); + g_return_if_fail(FU_IS_USB_DEVICE(usb_device)); + g_set_object(&self->usb_device, usb_device); +} + +FuMmDevice * +fu_mm_device_udev_new(FuContext *ctx, MMManager *manager, FuMmDevice *shadow_device) +{ + FuMmDevice *self = g_object_new(FU_TYPE_MM_DEVICE, "context", ctx, NULL); + g_debug("creating udev-based mm device at %s", + fu_device_get_physical_id(FU_DEVICE(shadow_device))); + fu_device_incorporate(FU_DEVICE(self), FU_DEVICE(shadow_device)); + 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)); + + if (g_str_equal(subsystem, "usbmisc") && self->port_qmi == NULL && ifnum >= 0 && + ifnum == self->port_qmi_ifnum) { + 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 --git a/fwupd-1.8.6/plugins/modem-manager/fu-mm-device.h b/fwupd-1.8.6/plugins/modem-manager/fu-mm-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f82fbfd0c9c31086da6c167ae9f4954b52d70b75 --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mm-device.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 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 + +#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(FuContext *ctx, MMManager *manager, MMObject *omodem); +FuUsbDevice * +fu_mm_device_get_usb_device(FuMmDevice *self); +void +fu_mm_device_set_usb_device(FuMmDevice *self, FuUsbDevice *usb_device); +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); +gint +fu_mm_device_get_port_qmi_ifnum(FuMmDevice *device); +gint +fu_mm_device_get_port_mbim_ifnum(FuMmDevice *device); +MMModemFirmwareUpdateMethod +fu_mm_device_get_update_methods(FuMmDevice *device); + +FuMmDevice * +fu_mm_shadow_device_new(FuMmDevice *device); +FuMmDevice * +fu_mm_device_udev_new(FuContext *ctx, MMManager *manager, FuMmDevice *shadow_device); +void +fu_mm_device_udev_add_port(FuMmDevice *device, + const gchar *subsystem, + const gchar *path, + gint ifnum); + +#endif /* __FU_MM_DEVICE_H */ diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-mm-plugin.c b/fwupd-1.8.6/plugins/modem-manager/fu-mm-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..a096d8738271d851a92c681f202c7b8d32b14649 --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mm-plugin.c @@ -0,0 +1,567 @@ + +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#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 */ + +/* out-of-tree modem-power driver is unsupported */ +#define MODEM_POWER_SYSFS_PATH "/sys/class/modem-power" + +struct FuPluginData { + MMManager *manager; + gboolean manager_ready; + GUdevClient *udev_client; + GFileMonitor *modem_power_monitor; + guint udev_timeout_id; + + /* when a device is inhibited from MM, we store all relevant details + * ourselves to recreate a functional device object even without MM + */ + FuMmDevice *shadow_device; +}; + +typedef FuPluginData FuModemManagerPlugin; +#define FU_MODEM_MANAGER_PLUGIN(o) fu_plugin_get_data(FU_PLUGIN(o)) + +static void +fu_mm_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + fu_string_append_kb(str, idt, "ManagerReady", self->manager_ready); + if (self->shadow_device != NULL) + fu_string_append(str, idt, "ShadowDevice", fu_device_get_id(self->shadow_device)); +} + +static void +fu_mm_plugin_load(FuContext *ctx) +{ + fu_context_add_quirk_key(ctx, "ModemManagerBranchAtCommand"); +} + +static void +fu_mm_plugin_udev_device_removed(FuPlugin *plugin) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + FuMmDevice *dev; + + if (self->shadow_device == NULL) + return; + + dev = fu_plugin_cache_lookup(plugin, + fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); + if (dev == NULL) + return; + + /* once the first port is gone, consider device is gone */ + fu_plugin_cache_remove(plugin, fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); + fu_plugin_device_remove(plugin, FU_DEVICE(dev)); + + /* no need to wait for more ports, cancel that right away */ + if (self->udev_timeout_id != 0) { + g_source_remove(self->udev_timeout_id); + self->udev_timeout_id = 0; + } +} + +static void +fu_mm_plugin_uninhibit_device(FuPlugin *plugin) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + g_autoptr(FuMmDevice) shadow_device = NULL; + + g_clear_object(&self->udev_client); + + /* get the device removed from the plugin cache before uninhibiting */ + fu_mm_plugin_udev_device_removed(plugin); + + shadow_device = g_steal_pointer(&self->shadow_device); + if (self->manager != NULL && shadow_device != NULL) { + const gchar *inhibition_uid = fu_mm_device_get_inhibition_uid(shadow_device); + g_debug("uninhibit modemmanager device with uid %s", inhibition_uid); + mm_manager_uninhibit_device_sync(self->manager, inhibition_uid, NULL, NULL); + } +} + +static gboolean +fu_mm_plugin_udev_device_ports_timeout(gpointer user_data) +{ + FuPlugin *plugin = user_data; + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + FuMmDevice *dev; + g_autoptr(GError) error = NULL; + + g_return_val_if_fail(self->shadow_device != NULL, G_SOURCE_REMOVE); + self->udev_timeout_id = 0; + + dev = fu_plugin_cache_lookup(plugin, + fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); + if (dev != NULL) { + if (!fu_device_probe(FU_DEVICE(dev), &error)) { + g_debug("failed to probe MM device: %s", error->message); + } else { + fu_plugin_device_add(plugin, FU_DEVICE(dev)); + } + } + + return G_SOURCE_REMOVE; +} + +static void +fu_mm_plugin_udev_device_ports_timeout_reset(FuPlugin *plugin) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + + g_return_if_fail(self->shadow_device != NULL); + if (self->udev_timeout_id != 0) + g_source_remove(self->udev_timeout_id); + self->udev_timeout_id = g_timeout_add_seconds(FU_MM_UDEV_DEVICE_PORTS_TIMEOUT, + fu_mm_plugin_udev_device_ports_timeout, + plugin); +} + +static void +fu_mm_plugin_udev_device_port_added(FuPlugin *plugin, + const gchar *subsystem, + const gchar *path, + gint ifnum) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + FuMmDevice *existing; + g_autoptr(FuMmDevice) dev = NULL; + + g_return_if_fail(self->shadow_device != NULL); + + existing = + fu_plugin_cache_lookup(plugin, + fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); + if (existing != NULL) { + /* add port to existing device */ + fu_mm_device_udev_add_port(existing, subsystem, path, ifnum); + fu_mm_plugin_udev_device_ports_timeout_reset(plugin); + return; + } + + /* create device and add to cache */ + dev = fu_mm_device_udev_new(fu_plugin_get_context(plugin), + self->manager, + self->shadow_device); + fu_mm_device_udev_add_port(dev, subsystem, path, ifnum); + fu_plugin_cache_add(plugin, fu_device_get_physical_id(FU_DEVICE(self->shadow_device)), dev); + + /* wait a bit before probing, in case more ports get added */ + fu_mm_plugin_udev_device_ports_timeout_reset(plugin); +} + +static gboolean +fu_mm_plugin_udev_uevent_cb(GUdevClient *udev, + const gchar *action, + GUdevDevice *device, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN(user_data); + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(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; + g_autofree gchar *device_bus = NULL; + gint ifnum = -1; + + if (action == NULL || subsystem == NULL || self->shadow_device == NULL || name == NULL) + return TRUE; + + /* ignore if loading port info fails */ + if (!fu_mm_utils_get_udev_port_info(device, &device_bus, &device_sysfs_path, &ifnum, NULL)) + return TRUE; + + /* ignore non-USB and non-PCI events */ + if (g_strcmp0(device_bus, "USB") != 0 && g_strcmp0(device_bus, "PCI") != 0) + return TRUE; + + /* ignore all events for ports not owned by our device */ + if (g_strcmp0(device_sysfs_path, + fu_device_get_physical_id(FU_DEVICE(self->shadow_device))) != 0) + 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 shadow_device modem: %s (ifnum %d)", path, ifnum); + fu_mm_plugin_udev_device_port_added(plugin, subsystem, path, ifnum); + } else if (g_str_equal(action, "remove")) { + g_debug("removed port from shadow_device modem: %s", path); + fu_mm_plugin_udev_device_removed(plugin); + } + + return TRUE; +} + +static gboolean +fu_mm_plugin_inhibit_device(FuPlugin *plugin, FuDevice *device, GError **error) +{ + const gchar *inhibition_uid; + static const gchar *subsystems[] = {"tty", "usbmisc", "wwan", NULL}; + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + g_autoptr(FuMmDevice) shadow_device = NULL; + + fu_mm_plugin_uninhibit_device(plugin); + + shadow_device = fu_mm_shadow_device_new(FU_MM_DEVICE(device)); + inhibition_uid = fu_mm_device_get_inhibition_uid(shadow_device); + g_debug("inhibit modemmanager device with uid %s", inhibition_uid); + if (!mm_manager_inhibit_device_sync(self->manager, inhibition_uid, NULL, error)) + return FALSE; + + /* setup shadow_device device info */ + self->shadow_device = g_steal_pointer(&shadow_device); + + /* only do modem port monitoring using udev if the module is expected + * to reset itself into a fully different layout, e.g. a fastboot device */ + if (fu_mm_device_get_update_methods(FU_MM_DEVICE(device)) & + MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { + self->udev_client = g_udev_client_new(subsystems); + g_signal_connect(G_UDEV_CLIENT(self->udev_client), + "uevent", + G_CALLBACK(fu_mm_plugin_udev_uevent_cb), + plugin); + } + + return TRUE; +} + +static void +fu_mm_plugin_ensure_modem_power_inhibit(FuPlugin *plugin, FuDevice *device) +{ + if (g_file_test(MODEM_POWER_SYSFS_PATH, G_FILE_TEST_EXISTS)) { + fu_device_inhibit(device, + "modem-power", + "The modem-power kernel driver cannot be used"); + } else { + fu_device_uninhibit(device, "modem-power"); + } +} + +static void +fu_mm_plugin_device_add(FuPlugin *plugin, MMObject *modem) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + const gchar *object_path = mm_object_get_path(modem); + g_autoptr(FuMmDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + g_debug("added modem: %s", object_path); + + if (fu_plugin_cache_lookup(plugin, object_path) != NULL) { + g_warning("MM device already added, ignoring"); + return; + } + dev = fu_mm_device_new(fu_plugin_get_context(plugin), self->manager, modem); + if (!fu_device_setup(FU_DEVICE(dev), &error)) { + g_debug("failed to probe MM device: %s", error->message); + return; + } + fu_mm_plugin_ensure_modem_power_inhibit(plugin, FU_DEVICE(dev)); + fu_plugin_device_add(plugin, FU_DEVICE(dev)); + fu_plugin_cache_add(plugin, object_path, dev); + fu_plugin_cache_add(plugin, fu_device_get_physical_id(FU_DEVICE(dev)), dev); +} + +static void +fu_mm_plugin_device_added_cb(MMManager *manager, MMObject *modem, FuPlugin *plugin) +{ + fu_mm_plugin_device_add(plugin, modem); +} + +static void +fu_mm_plugin_device_removed_cb(MMManager *manager, MMObject *modem, FuPlugin *plugin) +{ + const gchar *object_path = mm_object_get_path(modem); + FuMmDevice *dev = fu_plugin_cache_lookup(plugin, object_path); + MMModemFirmwareUpdateMethod update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; + + if (dev == NULL) + return; + g_debug("removed modem: %s", mm_object_get_path(modem)); + +#if MM_CHECK_VERSION(1, 19, 1) + /* No information will be displayed during the upgrade process if the + * device is removed, the main reason is that device is "removed" from + * ModemManager, but it still exists in the system */ + update_methods = + MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA; +#elif MM_CHECK_VERSION(1, 17, 1) + update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU; +#endif + + if (!(fu_mm_device_get_update_methods(FU_MM_DEVICE(dev)) & update_methods)) { + fu_plugin_cache_remove(plugin, object_path); + fu_plugin_device_remove(plugin, FU_DEVICE(dev)); + } +} + +static void +fu_mm_plugin_teardown_manager(FuPlugin *plugin) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + + if (self->manager_ready) { + g_debug("ModemManager no longer available"); + g_signal_handlers_disconnect_by_func(self->manager, + G_CALLBACK(fu_mm_plugin_device_added_cb), + plugin); + g_signal_handlers_disconnect_by_func(self->manager, + G_CALLBACK(fu_mm_plugin_device_removed_cb), + plugin); + self->manager_ready = FALSE; + } +} + +static void +fu_mm_plugin_setup_manager(FuPlugin *plugin) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + const gchar *version = mm_manager_get_version(self->manager); + GList *list; + + if (fu_version_compare(version, MM_REQUIRED_VERSION, FWUPD_VERSION_FORMAT_TRIPLET) < 0) { + g_warning("ModemManager %s is available, but need at least %s", + version, + MM_REQUIRED_VERSION); + return; + } + + g_debug("ModemManager %s is available", version); + + g_signal_connect(G_DBUS_OBJECT_MANAGER(self->manager), + "object-added", + G_CALLBACK(fu_mm_plugin_device_added_cb), + plugin); + g_signal_connect(G_DBUS_OBJECT_MANAGER(self->manager), + "object-removed", + G_CALLBACK(fu_mm_plugin_device_removed_cb), + plugin); + + list = g_dbus_object_manager_get_objects(G_DBUS_OBJECT_MANAGER(self->manager)); + for (GList *l = list; l != NULL; l = g_list_next(l)) { + MMObject *modem = MM_OBJECT(l->data); + fu_mm_plugin_device_add(plugin, modem); + g_object_unref(modem); + } + g_list_free(list); + + self->manager_ready = TRUE; +} + +static void +fu_mm_plugin_name_owner_updated(FuPlugin *plugin) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + g_autofree gchar *name_owner = NULL; + name_owner = g_dbus_object_manager_client_get_name_owner( + G_DBUS_OBJECT_MANAGER_CLIENT(self->manager)); + if (name_owner != NULL) + fu_mm_plugin_setup_manager(plugin); + else + fu_mm_plugin_teardown_manager(plugin); +} + +static gboolean +fu_mm_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + g_signal_connect_swapped(MM_MANAGER(self->manager), + "notify::name-owner", + G_CALLBACK(fu_mm_plugin_name_owner_updated), + plugin); + fu_mm_plugin_name_owner_updated(plugin); + return TRUE; +} + +static void +fu_mm_plugin_modem_power_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN(user_data); + GPtrArray *devices = fu_plugin_get_devices(plugin); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + fu_mm_plugin_ensure_modem_power_inhibit(plugin, device); + } +} + +static gboolean +fu_mm_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GFile) file = g_file_new_for_path(MODEM_POWER_SYSFS_PATH); + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) + return FALSE; + self->manager = mm_manager_new_sync(connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NULL, + error); + if (self->manager == NULL) + return FALSE; + + /* detect presence of unsupported modem-power driver */ + self->modem_power_monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); + if (self->modem_power_monitor == NULL) + return FALSE; + g_signal_connect(self->modem_power_monitor, + "changed", + G_CALLBACK(fu_mm_plugin_modem_power_changed_cb), + plugin); + + return TRUE; +} + +static gboolean +fu_mm_plugin_detach(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) +{ + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* inhibit device and track it inside the plugin, not bound to the + * lifetime of the FuMmDevice, because that object will only exist for + * as long as the ModemManager device exists, and inhibiting will + * implicitly remove the device from ModemManager. */ + if (self->shadow_device == NULL) { + if (!fu_mm_plugin_inhibit_device(plugin, device, error)) + return FALSE; + } + + /* reset */ + if (!fu_device_detach_full(device, progress, error)) { + fu_mm_plugin_uninhibit_device(plugin); + return FALSE; + } + + /* note: wait for replug set by device if it really needs it */ + return TRUE; +} + +static void +fu_mm_plugin_device_attach_finished(gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN(user_data); + fu_mm_plugin_uninhibit_device(plugin); +} + +static gboolean +fu_mm_plugin_attach(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* schedule device attach asynchronously, which is extremely important + * so that engine can setup the device "waiting" logic before the actual + * attach procedure happens (which will reset the module if it worked + * properly) */ + if (!fu_device_attach_full(device, progress, error)) + return FALSE; + + /* this signal will always be emitted asynchronously */ + g_signal_connect_swapped(FU_DEVICE(device), + "attach-finished", + G_CALLBACK(fu_mm_plugin_device_attach_finished), + plugin); + + return TRUE; +} + +static gboolean +fu_mm_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuDevice *device_tmp; + g_autoptr(GUdevDevice) udev_device = NULL; + + /* interesting device? */ + if (!FU_IS_USB_DEVICE(device)) + return TRUE; + + /* look up the FuMmDevice for the USB device that just appeared */ + udev_device = fu_usb_device_find_udev_device(FU_USB_DEVICE(device), error); + if (udev_device == NULL) + return FALSE; + device_tmp = fu_plugin_cache_lookup(plugin, g_udev_device_get_sysfs_path(udev_device)); + if (device_tmp == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s not added by ModemManager", + g_udev_device_get_sysfs_path(udev_device)); + return FALSE; + } + fu_mm_device_set_usb_device(FU_MM_DEVICE(device_tmp), FU_USB_DEVICE(device)); + return TRUE; +} + +static void +fu_mm_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + (void)fu_plugin_alloc_data(plugin, sizeof(FuModemManagerPlugin)); +} + +static void +fu_mm_plugin_finalize(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); + + fu_mm_plugin_uninhibit_device(plugin); + + if (self->udev_timeout_id) + g_source_remove(self->udev_timeout_id); + if (self->udev_client) + g_object_unref(self->udev_client); + if (self->manager != NULL) + g_object_unref(self->manager); + if (self->modem_power_monitor != NULL) + g_object_unref(self->modem_power_monitor); + + /* G_OBJECT_CLASS(fu_mm_plugin_parent_class)->finalize() not required as modular */ +} + +void +fu_plugin_init_vfuncs(FuPluginVfuncs *vfuncs) +{ + vfuncs->load = fu_mm_plugin_load; + vfuncs->constructed = fu_mm_plugin_constructed; + vfuncs->finalize = fu_mm_plugin_finalize; + vfuncs->to_string = fu_mm_plugin_to_string; + vfuncs->startup = fu_mm_plugin_startup; + vfuncs->coldplug = fu_mm_plugin_coldplug; + vfuncs->attach = fu_mm_plugin_attach; + vfuncs->detach = fu_mm_plugin_detach; + vfuncs->backend_device_added = fu_mm_plugin_backend_device_added; +} diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-mm-utils.c b/fwupd-1.8.6/plugins/modem-manager/fu-mm-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..af691dd28b8d44e97cfa23db6387880141a876cc --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mm-utils.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-mm-utils.h" + +static gchar * +find_device_bus_subsystem(GUdevDevice *device) +{ + g_autoptr(GUdevDevice) iter = NULL; + + iter = g_object_ref(device); + while (iter != NULL) { + g_autoptr(GUdevDevice) next = NULL; + const gchar *subsys; + + /* stop search as soon as we find a parent object + * of one of the bus subsystems supported in + * ModemManager */ + subsys = g_udev_device_get_subsystem(iter); + if ((g_strcmp0(subsys, "usb") == 0) || (g_strcmp0(subsys, "pcmcia") == 0) || + (g_strcmp0(subsys, "pci") == 0) || (g_strcmp0(subsys, "platform") == 0) || + (g_strcmp0(subsys, "pnp") == 0) || (g_strcmp0(subsys, "sdio") == 0)) { + return g_ascii_strup(subsys, -1); + } + + next = g_udev_device_get_parent(iter); + g_set_object(&iter, next); + } + + /* no more parents to check */ + return NULL; +} + +gboolean +fu_mm_utils_get_udev_port_info(GUdevDevice *device, + gchar **out_device_bus, + gchar **out_device_sysfs_path, + gint *out_port_usb_ifnum, + GError **error) +{ + gint port_usb_ifnum = -1; + g_autoptr(GUdevDevice) parent = NULL; + g_autofree gchar *device_sysfs_path = NULL; + g_autofree gchar *device_bus = NULL; + + /* lookup the main bus the device is in; for supported devices it will + * usually be either 'PCI' or 'USB' */ + device_bus = find_device_bus_subsystem(device); + if (device_bus == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to lookup device info: bus not found"); + return FALSE; + } + + if (g_strcmp0(device_bus, "USB") == 0) { + /* ID_USB_INTERFACE_NUM is set on the port device itself */ + const gchar *aux = g_udev_device_get_property(device, "ID_USB_INTERFACE_NUM"); + if (aux != NULL) + port_usb_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); + } + } else if (g_strcmp0(device_bus, "PCI") == 0) { + /* the first parent in the 'pci' subsystem is our physical device */ + parent = g_udev_device_get_parent(device); + while (parent != NULL) { + g_autoptr(GUdevDevice) next = NULL; + + if (g_strcmp0(g_udev_device_get_subsystem(parent), "pci") == 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); + } + } else { + /* other subsystems, we don't support firmware upgrade for those */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "device bus unsupported: %s", + device_bus); + return FALSE; + } + + if (device_sysfs_path == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to lookup device info: physical device not found"); + return FALSE; + } + + if (out_port_usb_ifnum != NULL) + *out_port_usb_ifnum = port_usb_ifnum; + if (out_device_sysfs_path != NULL) + *out_device_sysfs_path = g_steal_pointer(&device_sysfs_path); + if (out_device_bus != NULL) + *out_device_bus = g_steal_pointer(&device_bus); + return TRUE; +} + +gboolean +fu_mm_utils_get_port_info(const gchar *path, + gchar **out_device_bus, + gchar **out_device_sysfs_path, + gint *out_port_usb_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_bus, + out_device_sysfs_path, + out_port_usb_ifnum, + error); +} + +gboolean +fu_mm_utils_find_device_file(const gchar *device_sysfs_path, + const gchar *subsystem, + gchar **out_device_file, + GError **error) +{ + GList *devices; + g_autoptr(GUdevClient) client = NULL; + g_autofree gchar *device_file = NULL; + + g_return_val_if_fail(out_device_file != NULL, FALSE); + + client = g_udev_client_new(NULL); + devices = g_udev_client_query_by_subsystem(client, subsystem); + for (GList *l = devices; l != NULL; l = g_list_next(l)) { + if (g_str_has_prefix(g_udev_device_get_sysfs_path(G_UDEV_DEVICE(l->data)), + device_sysfs_path)) { + device_file = g_strdup(g_udev_device_get_device_file(l->data)); + if (device_file != NULL) + break; + } + } + g_list_free_full(devices, g_object_unref); + + if (device_file == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to find %s port in device %s", + subsystem, + device_sysfs_path); + return FALSE; + } + + *out_device_file = g_steal_pointer(&device_file); + + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-mm-utils.h b/fwupd-1.8.6/plugins/modem-manager/fu-mm-utils.h new file mode 100644 index 0000000000000000000000000000000000000000..8b2eb4403d8d07216a55f113a6c082323f7b30fe --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-mm-utils.h @@ -0,0 +1,32 @@ +/* + * 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 + +gboolean +fu_mm_utils_get_udev_port_info(GUdevDevice *dev, + gchar **device_bus, + gchar **device_sysfs_path, + gint *port_usb_ifnum, + GError **error); +gboolean +fu_mm_utils_get_port_info(const gchar *path, + gchar **device_bus, + gchar **device_sysfs_path, + gint *port_usb_ifnum, + GError **error); +gboolean +fu_mm_utils_find_device_file(const gchar *device_sysfs_path, + const gchar *subsystem, + gchar **out_device_file, + GError **error); + +#endif /* __FU_MM_UTILS_H */ diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-qmi-pdc-updater.c b/fwupd-1.8.6/plugins/modem-manager/fu-qmi-pdc-updater.c new file mode 100644 index 0000000000000000000000000000000000000000..a97a5be4aec3d13794203859583f048b93266d3e --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-qmi-pdc-updater.c @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#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; + +#if !QMI_CHECK_VERSION(1, 26, 0) +#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 +#endif + +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(QMI_CLIENT_PDC(ctx->qmi_client), + "load-config", + G_CALLBACK(fu_qmi_pdc_updater_load_config_indication), + ctx); + + /* don't wait forever */ + g_warn_if_fail(ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds(5, fu_qmi_pdc_updater_load_config_timeout, ctx); +} + +static void +fu_qmi_pdc_updater_load_config(WriteContext *ctx) +{ + g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL; + g_autoptr(GArray) chunk = NULL; + gsize full_size; + gsize chunk_size; + g_autoptr(GError) error = NULL; + + input = qmi_message_pdc_load_config_input_new(); + qmi_message_pdc_load_config_input_set_token(input, ctx->token++, NULL); + + full_size = g_bytes_get_size(ctx->blob); + if ((ctx->offset + QMI_LOAD_CHUNK_SIZE) > full_size) + chunk_size = full_size - ctx->offset; + else + chunk_size = QMI_LOAD_CHUNK_SIZE; + + chunk = g_array_sized_new(FALSE, FALSE, sizeof(guint8), chunk_size); + g_array_set_size(chunk, chunk_size); + if (!fu_memcpy_safe((guint8 *)chunk->data, + chunk_size, + 0x0, /* dst */ + (const guint8 *)g_bytes_get_data(ctx->blob, NULL), /* src */ + g_bytes_get_size(ctx->blob), + ctx->offset, + chunk_size, + &error)) { + g_critical("failed to copy chunk: %s", error->message); + } + + qmi_message_pdc_load_config_input_set_config_chunk(input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + ctx->digest, + full_size, + chunk, + NULL); + ctx->offset += chunk_size; + + qmi_client_pdc_load_config(ctx->qmi_client, + input, + 10, + NULL, + fu_qmi_pdc_updater_load_config_ready, + ctx); +} + +static GArray * +fu_qmi_pdc_updater_get_checksum(GBytes *blob) +{ + gsize file_size; + gsize hash_size; + GArray *digest; + g_autoptr(GChecksum) checksum = NULL; + + /* get checksum, to be used as unique id */ + file_size = g_bytes_get_size(blob); + hash_size = g_checksum_type_get_length(G_CHECKSUM_SHA1); + checksum = g_checksum_new(G_CHECKSUM_SHA1); + g_checksum_update(checksum, g_bytes_get_data(blob, NULL), file_size); + /* libqmi expects a GArray of bytes, not a GByteArray */ + digest = g_array_sized_new(FALSE, FALSE, sizeof(guint8), hash_size); + g_array_set_size(digest, hash_size); + g_checksum_get_digest(checksum, (guint8 *)digest->data, &hash_size); + + return digest; +} + +GArray * +fu_qmi_pdc_updater_write(FuQmiPdcUpdater *self, const gchar *filename, GBytes *blob, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + g_autoptr(GArray) digest = fu_qmi_pdc_updater_get_checksum(blob); + WriteContext ctx = { + .mainloop = mainloop, + .qmi_client = self->qmi_client, + .error = NULL, + .indication_id = 0, + .timeout_id = 0, + .blob = blob, + .digest = digest, + .offset = 0, + .token = 0, + }; + + fu_qmi_pdc_updater_load_config(&ctx); + g_main_loop_run(mainloop); + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return NULL; + } + + return g_steal_pointer(&digest); +} + +typedef struct { + GMainLoop *mainloop; + QmiClientPdc *qmi_client; + GError *error; + gulong indication_id; + guint timeout_id; + GArray *digest; + guint token; +} ActivateContext; + +#if !QMI_CHECK_VERSION(1, 26, 0) +#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 +#endif + +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 + * reset itself, we can consider this a successful operation. + * Note: not using g_error_matches() to avoid matching the domain, because the + * error may be either QMI_CORE_ERROR_TIMEOUT or MBIM_CORE_ERROR_TIMEOUT (same + * numeric value), and we don't want to build-depend on libmbim just for this. + */ + if (ctx->error->code == QMI_CORE_ERROR_TIMEOUT) { + g_debug("request to activate configuration timed out: assuming device " + "reset is ongoing"); + g_clear_error(&ctx->error); + } + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!qmi_message_pdc_activate_config_output_get_result(output, &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + /* When we activate the config, if the operation is successful, we'll just + * see the modem going away completely. So, do not consider an error the timeout + * waiting for the Activate Config indication, as that is actually a good + * thing. + */ + g_warn_if_fail(ctx->indication_id == 0); + ctx->indication_id = + g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), + "activate-config", + G_CALLBACK(fu_qmi_pdc_updater_activate_config_indication), + ctx); + + /* don't wait forever */ + g_warn_if_fail(ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds(5, fu_qmi_pdc_updater_activate_config_timeout, ctx); +} + +static void +fu_qmi_pdc_updater_activate_config(ActivateContext *ctx) +{ + g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL; + + input = qmi_message_pdc_activate_config_input_new(); + qmi_message_pdc_activate_config_input_set_config_type(input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + NULL); + qmi_message_pdc_activate_config_input_set_token(input, ctx->token++, NULL); + + g_debug("activating selected configuration..."); + qmi_client_pdc_activate_config(ctx->qmi_client, + input, + 5, + NULL, + fu_qmi_pdc_updater_activate_config_ready, + ctx); +} + +#if !QMI_CHECK_VERSION(1, 26, 0) +#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 +#endif + +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(QMI_CLIENT_PDC(ctx->qmi_client), + "set-selected-config", + G_CALLBACK(fu_qmi_pdc_updater_set_selected_config_indication), + ctx); + + /* don't wait forever */ + g_warn_if_fail(ctx->timeout_id == 0); + ctx->timeout_id = + g_timeout_add_seconds(5, fu_qmi_pdc_updater_set_selected_config_timeout, ctx); +} + +static void +fu_qmi_pdc_updater_set_selected_config(ActivateContext *ctx) +{ + g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL; + 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 --git a/fwupd-1.8.6/plugins/modem-manager/fu-qmi-pdc-updater.h b/fwupd-1.8.6/plugins/modem-manager/fu-qmi-pdc-updater.h new file mode 100644 index 0000000000000000000000000000000000000000..e6bc496c92cf808f0b3115f441dd87a8865f21ba --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-qmi-pdc-updater.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_QMI_PDC_UPDATER_H +#define __FU_QMI_PDC_UPDATER_H + +#include + +#define FU_TYPE_QMI_PDC_UPDATER (fu_qmi_pdc_updater_get_type()) +G_DECLARE_FINAL_TYPE(FuQmiPdcUpdater, fu_qmi_pdc_updater, FU, QMI_PDC_UPDATER, GObject) + +FuQmiPdcUpdater * +fu_qmi_pdc_updater_new(const gchar *qmi_port); +gboolean +fu_qmi_pdc_updater_open(FuQmiPdcUpdater *self, GError **error); +GArray * +fu_qmi_pdc_updater_write(FuQmiPdcUpdater *self, + const gchar *filename, + GBytes *blob, + GError **error); +gboolean +fu_qmi_pdc_updater_activate(FuQmiPdcUpdater *self, GArray *digest, GError **error); +gboolean +fu_qmi_pdc_updater_close(FuQmiPdcUpdater *self, GError **error); + +#endif /* __FU_QMI_PDC_UPDATER_H */ diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-sahara-loader.c b/fwupd-1.8.6/plugins/modem-manager/fu-sahara-loader.c new file mode 100644 index 0000000000000000000000000000000000000000..89c620fbea22772f5bf60e771e8bc78d448005fc --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-sahara-loader.c @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2021 Quectel Wireless Solutions Co., Ltd. + * Ivan Mikhanchuk + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-sahara-loader.h" + +#define SAHARA_VERSION 2 +#define SAHARA_VERSION_COMPATIBLE 1 + +#define SAHARA_RAW_BUFFER_SIZE (4 * 1024) + +#define IO_TIMEOUT_MS 15000 + +struct _FuSaharaLoader { + GObject parent_instance; + + FuUsbDevice *usb_device; + + int ep_in; + int ep_out; + gsize maxpktsize_in; + gsize maxpktsize_out; +}; + +G_DEFINE_TYPE(FuSaharaLoader, fu_sahara_loader, G_TYPE_OBJECT) + +/* Protocol definitions */ +typedef enum { + SAHARA_NO_CMD_ID = 0, + SAHARA_HELLO_ID, + SAHARA_HELLO_RESPONSE_ID, + SAHARA_READ_DATA_ID, + SAHARA_END_OF_IMAGE_TX_ID, + SAHARA_DONE_ID, + SAHARA_DONE_RESP_ID, + SAHARA_RESET_ID, + SAHARA_RESET_RESPONSE_ID, + SAHARA_READ_DATA_64_BIT_ID = 0x12, + SAHARA_LAST_CMD_ID +} FuSaharaCommandId; + +typedef enum { + SAHARA_STATUS_SUCCESS = 0, + SAHARA_STATUS_FAILED, + SAHARA_STATUS_LAST +} FuSaharaStatusCode; + +typedef enum { + SAHARA_MODE_IMAGE_TX_PENDING, + SAHARA_MODE_IMAGE_TX_COMPLETE, + SAHARA_MODE_LAST +} FuSaharaMode; + +/* Sahara packet definition */ +struct sahara_packet { + guint32 command_id; + guint32 length; + + union { + struct { + guint32 version; + guint32 version_compatible; + guint32 max_packet_length; + guint32 mode; + } hello; + struct { + guint32 version; + guint32 version_compatible; + guint32 status; + guint32 mode; + guint32 reserved[6]; + } hello_response; + struct { + guint32 image_id; + guint32 offset; + guint32 length; + } read_data; + struct { + guint32 image_id; + guint32 status; + } end_of_image_transfer; + /* done packet = header only */ + struct { + guint32 image_transfer_status; + } done_response; + /* reset packet = header only */ + /* reset response packet = header only */ + struct { + guint64 image_id; + guint64 offset; + guint64 length; + } read_data_64bit; + }; +} __attribute((packed)); + +/* helper functions */ +static FuSaharaCommandId +sahara_packet_get_command_id(GByteArray *packet) +{ + return ((struct sahara_packet *)(packet->data))->command_id; +} + +static FuSaharaCommandId +sahara_packet_get_length(GByteArray *packet) +{ + return ((struct sahara_packet *)(packet->data))->length; +} + +/* IO functions */ +static gboolean +fu_sahara_loader_find_interface(FuSaharaLoader *self, FuUsbDevice *usb_device, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + g_autoptr(GPtrArray) intfs = NULL; + GUsbDevice *g_usb_device = fu_usb_device_get_dev(usb_device); + + /* all sahara devices use the same vid:pid pair */ + if (g_usb_device_get_vid(g_usb_device) != 0x05c6 || + g_usb_device_get_pid(g_usb_device) != 0x9008) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Wrong device and/or vendor id: 0x%04x 0x%04x", + g_usb_device_get_vid(g_usb_device), + g_usb_device_get_pid(g_usb_device)); + return FALSE; + } + + /* Parse usb interfaces and find suitable endpoints */ + intfs = g_usb_device_get_interfaces(g_usb_device, error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == 0xFF && + g_usb_interface_get_subclass(intf) == 0xFF && + g_usb_interface_get_protocol(intf) == 0xFF) { + GUsbEndpoint *ep; + g_autoptr(GPtrArray) endpoints = NULL; + + endpoints = g_usb_interface_get_endpoints(intf); + if (endpoints == NULL || endpoints->len == 0) + continue; + + for (guint j = 0; j < endpoints->len; j++) { + ep = g_ptr_array_index(endpoints, j); + if (g_usb_endpoint_get_direction(ep) == + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST) { + self->ep_in = g_usb_endpoint_get_address(ep); + self->maxpktsize_in = + g_usb_endpoint_get_maximum_packet_size(ep); + } else { + self->ep_out = g_usb_endpoint_get_address(ep); + self->maxpktsize_out = + g_usb_endpoint_get_maximum_packet_size(ep); + } + } + + fu_usb_device_add_interface(usb_device, g_usb_interface_get_number(intf)); + + return TRUE; + } + } + + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no update interface found"); + return FALSE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} + +gboolean +fu_sahara_loader_open(FuSaharaLoader *self, FuUsbDevice *usb_device, GError **error) +{ + if (!fu_sahara_loader_find_interface(self, usb_device, error)) + return FALSE; + if (!fu_device_open(FU_DEVICE(usb_device), error)) + return FALSE; + + self->usb_device = g_object_ref(usb_device); + + return TRUE; +} + +gboolean +fu_sahara_loader_close(FuSaharaLoader *self, GError **error) +{ + if (!fu_device_close(FU_DEVICE(self->usb_device), error)) + return FALSE; + g_clear_object(&self->usb_device); + return TRUE; +} + +gboolean +fu_sahara_loader_qdl_is_open(FuSaharaLoader *self) +{ + g_return_val_if_fail(self != NULL, FALSE); + + return fu_usb_device_is_open(self->usb_device); +} + +GByteArray * +fu_sahara_loader_qdl_read(FuSaharaLoader *self, GError **error) +{ +#ifdef HAVE_GUSB + gsize actual_len = 0; + g_autoptr(GByteArray) buf = g_byte_array_sized_new(SAHARA_RAW_BUFFER_SIZE); + fu_byte_array_set_size(buf, SAHARA_RAW_BUFFER_SIZE, 0x00); + + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(self->usb_device), + self->ep_in, + buf->data, + buf->len, + &actual_len, + IO_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error(error, "failed to do bulk transfer (read): "); + return NULL; + } + + g_byte_array_set_size(buf, actual_len); + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + g_debug("received %" G_GSIZE_FORMAT " bytes", actual_len); + + return g_steal_pointer(&buf); +#else + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "GUsb not supported"); + return NULL; +#endif +} + +gboolean +fu_sahara_loader_qdl_write(FuSaharaLoader *self, const guint8 *data, gsize sz, GError **error) +{ +#ifdef HAVE_GUSB + gsize actual_len = 0; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GByteArray) bytes = NULL; + + /* copy const data to mutable GByteArray */ + bytes = g_byte_array_sized_new(sz); + bytes = g_byte_array_append(bytes, data, sz); + chunks = fu_chunk_array_mutable_new(bytes->data, bytes->len, 0, 0, self->maxpktsize_out); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(self->usb_device), + self->ep_out, + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + &actual_len, + IO_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error(error, "failed to do bulk transfer (write data): "); + return FALSE; + } + if (actual_len != fu_chunk_get_data_sz(chk)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only wrote %" G_GSIZE_FORMAT "bytes", + actual_len); + return FALSE; + } + } + if (sz % self->maxpktsize_out == 0) { + /* sent zlp packet if needed */ + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(self->usb_device), + self->ep_out, + NULL, + 0, + NULL, + IO_TIMEOUT_MS, + NULL, + error)) { + g_prefix_error(error, "failed to do bulk transfer (write zlp): "); + return FALSE; + } + } + + return TRUE; +#else + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "GUsb not supported"); + return FALSE; +#endif +} + +gboolean +fu_sahara_loader_qdl_write_bytes(FuSaharaLoader *self, GBytes *bytes, GError **error) +{ + gsize sz; + const guint8 *data = g_bytes_get_data(bytes, &sz); + return fu_sahara_loader_qdl_write(self, data, sz, error); +} + +static gboolean +fu_sahara_loader_write_prog(FuSaharaLoader *self, + guint32 offset, + guint32 length, + GBytes *prog, + GError **error) +{ + gsize sz; + const guint8 *data = g_bytes_get_data(prog, &sz); + + g_return_val_if_fail(offset + length <= sz, FALSE); + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) { + g_debug("SENDING --> RAW_DATA: %u bytes (offset = %u, total = %" G_GSIZE_FORMAT ")", + length, + offset, + sz); + } + return fu_sahara_loader_qdl_write(self, &data[offset], length, error); +} + +static gboolean +fu_sahara_loader_send_packet(FuSaharaLoader *self, GByteArray *pkt, GError **error) +{ + guint8 *data = pkt->data; + gsize sz = pkt->len; + + g_return_val_if_fail(pkt != NULL, FALSE); + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "tx packet", pkt->data, pkt->len); + return fu_sahara_loader_qdl_write(self, data, sz, error); +} + +/* packet composers */ +static GByteArray * +fu_sahara_create_byte_array_from_packet(const struct sahara_packet *pkt) +{ + GByteArray *self; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(pkt != NULL, NULL); + + self = g_byte_array_sized_new(pkt->length); + fu_byte_array_set_size(self, pkt->length, 0x00); + if (!fu_memcpy_safe(self->data, + self->len, + 0, + (gconstpointer)pkt, + sizeof(struct sahara_packet), + 0, + pkt->length, + &error_local)) { + g_debug("sahara create packet failed: %s", error_local->message); + return NULL; + } + + return self; +} + +static GByteArray * +fu_sahara_loader_compose_reset_packet(void) +{ + guint32 len = 0x08; + struct sahara_packet pkt = {.command_id = GUINT32_TO_LE(SAHARA_RESET_ID), + .length = GUINT32_TO_LE(len), + {{0}}}; + + return fu_sahara_create_byte_array_from_packet(&pkt); +} + +static GByteArray * +fu_sahara_loader_compose_hello_response_packet(FuSaharaMode mode) +{ + guint32 len = 0x30; + struct sahara_packet pkt = {.command_id = GUINT32_TO_LE(SAHARA_HELLO_RESPONSE_ID), + .length = GUINT32_TO_LE(len), + {{0}}}; + + pkt.hello_response.version = GUINT32_TO_LE(SAHARA_VERSION); + pkt.hello_response.version_compatible = GUINT32_TO_LE(SAHARA_VERSION_COMPATIBLE); + pkt.hello_response.status = GUINT32_TO_LE(SAHARA_STATUS_SUCCESS); + pkt.hello_response.mode = GUINT32_TO_LE(SAHARA_MODE_IMAGE_TX_PENDING); + + return fu_sahara_create_byte_array_from_packet(&pkt); +} + +static GByteArray * +fu_sahara_loader_compose_done_packet(void) +{ + guint32 len = 0x08; + struct sahara_packet pkt = {.command_id = GUINT32_TO_LE(SAHARA_DONE_ID), + .length = GUINT32_TO_LE(len), + {{0}}}; + + return fu_sahara_create_byte_array_from_packet(&pkt); +} + +static gboolean +fu_sahara_loader_send_reset_packet(FuSaharaLoader *self, GError **error) +{ + g_autoptr(GByteArray) rx_packet = NULL; + g_autoptr(GByteArray) tx_packet = NULL; + + tx_packet = fu_sahara_loader_compose_reset_packet(); + if (!fu_sahara_loader_send_packet(self, tx_packet, error)) { + g_prefix_error(error, "Failed to send reset packet: "); + return FALSE; + } + + rx_packet = fu_sahara_loader_qdl_read(self, error); + if (rx_packet == NULL || + sahara_packet_get_command_id(rx_packet) != SAHARA_RESET_RESPONSE_ID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to receive RESET_RESPONSE packet"); + return FALSE; + } + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + g_debug("reset succeeded"); + return TRUE; +} + +static gboolean +fu_sahara_loader_wait_hello_rsp(FuSaharaLoader *self, GError **error) +{ + g_autoptr(GByteArray) rx_packet = NULL; + g_autoptr(GByteArray) tx_packet = NULL; + + rx_packet = fu_sahara_loader_qdl_read(self, error); + if (rx_packet == NULL) { + g_autoptr(GByteArray) ping = NULL; + ping = g_byte_array_sized_new(1); + g_byte_array_set_size(ping, 1); + fu_sahara_loader_send_packet(self, ping, NULL); + rx_packet = fu_sahara_loader_qdl_read(self, error); + } + + g_return_val_if_fail(rx_packet != NULL, FALSE); + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "rx packet", rx_packet->data, rx_packet->len); + + if (sahara_packet_get_command_id(rx_packet) != SAHARA_HELLO_ID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Received a different packet while waiting for the HELLO packet"); + fu_sahara_loader_send_reset_packet(self, NULL); + return FALSE; + } + + tx_packet = fu_sahara_loader_compose_hello_response_packet(SAHARA_MODE_IMAGE_TX_PENDING); + + return fu_sahara_loader_send_packet(self, tx_packet, error); +} + +/* main routine */ +gboolean +fu_sahara_loader_run(FuSaharaLoader *self, GBytes *prog, GError **error) +{ + g_return_val_if_fail(prog != NULL, FALSE); + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + g_debug("STATE -- SAHARA_WAIT_HELLO"); + if (!fu_sahara_loader_wait_hello_rsp(self, error)) + return FALSE; + + while (TRUE) { + guint32 command_id; + struct sahara_packet *pkt; + g_autoptr(GByteArray) rx_packet = NULL; + g_autoptr(GByteArray) tx_packet = NULL; + g_autoptr(GError) error_local = NULL; + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) + g_debug("STATE -- SAHARA_WAIT_COMMAND"); + rx_packet = fu_sahara_loader_qdl_read(self, error); + if (rx_packet == NULL) + break; + if (rx_packet->len != sahara_packet_get_length(rx_packet)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Received packet length is not matching"); + break; + } + + if (g_getenv("FWUPD_MODEM_MANAGER_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "rx_packet", rx_packet->data, rx_packet->len); + } + + command_id = sahara_packet_get_command_id(rx_packet); + pkt = (struct sahara_packet *)(rx_packet->data); + if (command_id == SAHARA_HELLO_ID) { + tx_packet = fu_sahara_loader_compose_hello_response_packet( + SAHARA_MODE_IMAGE_TX_PENDING); + fu_sahara_loader_send_packet(self, tx_packet, &error_local); + } else if (command_id == SAHARA_READ_DATA_ID) { + guint32 offset = pkt->read_data.offset; + guint32 length = pkt->read_data.length; + fu_sahara_loader_write_prog(self, offset, length, prog, &error_local); + } else if (command_id == SAHARA_READ_DATA_64_BIT_ID) { + guint64 offset = pkt->read_data_64bit.offset; + guint64 length = pkt->read_data_64bit.length; + fu_sahara_loader_write_prog(self, offset, length, prog, &error_local); + } else if (command_id == SAHARA_END_OF_IMAGE_TX_ID) { + guint32 status = pkt->end_of_image_transfer.status; + if (status == SAHARA_STATUS_SUCCESS) { + tx_packet = fu_sahara_loader_compose_done_packet(); + fu_sahara_loader_send_packet(self, tx_packet, &error_local); + } + } else if (command_id == SAHARA_DONE_RESP_ID) { + return TRUE; + } else { + g_warning("Unexpected packet received: cmd_id = %u, len = %u", + command_id, + sahara_packet_get_length(rx_packet)); + } + + if (error_local != NULL) + g_warning("%s", error_local->message); + } + + fu_sahara_loader_send_reset_packet(self, NULL); + return FALSE; +} + +static void +fu_sahara_loader_init(FuSaharaLoader *self) +{ +} + +static void +fu_sahara_loader_finalize(GObject *object) +{ + G_OBJECT_CLASS(fu_sahara_loader_parent_class)->finalize(object); +} + +static void +fu_sahara_loader_class_init(FuSaharaLoaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_sahara_loader_finalize; +} + +FuSaharaLoader * +fu_sahara_loader_new(void) +{ + return g_object_new(FU_TYPE_SAHARA_LOADER, NULL); +} diff --git a/fwupd-1.8.6/plugins/modem-manager/fu-sahara-loader.h b/fwupd-1.8.6/plugins/modem-manager/fu-sahara-loader.h new file mode 100644 index 0000000000000000000000000000000000000000..1baa7466f196d6eb9d9fd98c099e0c4d9ff99047 --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/fu-sahara-loader.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Quectel Wireless Solutions Co., Ltd. + * Ivan Mikhanchuk + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SAHARA_LOADER (fu_sahara_loader_get_type()) +G_DECLARE_FINAL_TYPE(FuSaharaLoader, fu_sahara_loader, FU, SAHARA_LOADER, GObject) + +FuSaharaLoader * +fu_sahara_loader_new(void); + +gboolean +fu_sahara_loader_qdl_is_open(FuSaharaLoader *self); +GByteArray * +fu_sahara_loader_qdl_read(FuSaharaLoader *self, GError **error); +gboolean +fu_sahara_loader_qdl_write(FuSaharaLoader *self, const guint8 *data, gsize sz, GError **error); +gboolean +fu_sahara_loader_qdl_write_bytes(FuSaharaLoader *self, GBytes *bytes, GError **error); + +gboolean +fu_sahara_loader_open(FuSaharaLoader *self, FuUsbDevice *usb_device, GError **error); +gboolean +fu_sahara_loader_run(FuSaharaLoader *self, GBytes *prog, GError **error); +gboolean +fu_sahara_loader_close(FuSaharaLoader *self, GError **error); diff --git a/fwupd-1.8.6/plugins/modem-manager/meson.build b/fwupd-1.8.6/plugins/modem-manager/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..12396b6891bd8c3626baa5da4513a8db6929755c --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/meson.build @@ -0,0 +1,38 @@ +libmm_glib = dependency('mm-glib', version: '>= 1.10.0', required: get_option('plugin_modem_manager')) +libqmi_glib = dependency('qmi-glib', version: '>= 1.23.1', required: get_option('plugin_modem_manager')) +libmbim_glib = dependency('mbim-glib', version: '>= 1.22.0', required: get_option('plugin_modem_manager')) + +if libmm_glib.found() and \ + libqmi_glib.found() and \ + libmbim_glib.found() and \ + get_option('plugin_modem_manager').require(gudev.found(), + error_message: 'gudev is needed for plugin_modem_manager').allowed() + +cargs = ['-DG_LOG_DOMAIN="FuPluginMm"'] +cargs +=['-DMM_REQUIRED_VERSION="1.10.0"'] + +plugin_quirks += files('modem-manager.quirk') + +shared_module('fu_plugin_modem_manager', + sources: [ + 'fu-mm-plugin.c', + 'fu-mm-device.c', + 'fu-qmi-pdc-updater.c', + 'fu-mbim-qdu-updater.c', + 'fu-firehose-updater.c', + 'fu-sahara-loader.c', + 'fu-mm-utils.c' + ], + include_directories: plugin_incdirs, + install: true, + install_dir: libdir_pkg, + c_args: cargs, + link_with: plugin_libs, + dependencies: [ + plugin_deps, + libmm_glib, + libqmi_glib, + libmbim_glib, + ], +) +endif diff --git a/fwupd-1.8.6/plugins/modem-manager/modem-manager.quirk b/fwupd-1.8.6/plugins/modem-manager/modem-manager.quirk new file mode 100644 index 0000000000000000000000000000000000000000..4e45861f3037d10fad3e31e1f88d60b0370563e4 --- /dev/null +++ b/fwupd-1.8.6/plugins/modem-manager/modem-manager.quirk @@ -0,0 +1,60 @@ + +# DW5821e +[USB\VID_413C&PID_81D7] +Summary = Dell DW5821e LTE modem +CounterpartGuid = USB\VID_413C&PID_81D6 + +# DW5821e in fastboot mode +[USB\VID_413C&PID_81D6] +Summary = Dell DW5821e LTE modem (fastboot) +CounterpartGuid = USB\VID_413C&PID_81D7 + +# DW5821e/eSIM +[USB\VID_413C&PID_81E0] +Summary = Dell DW5821e/eSIM LTE modem +CounterpartGuid = USB\VID_413C&PID_81E1 + +# DW5821e/eSIM in fastboot mode +[USB\VID_413C&PID_81E1] +Summary = Dell DW5821e/eSIM LTE modem (fastboot) +CounterpartGuid = USB\VID_413C&PID_81E0 + +# T77W968 +[USB\VID_0489&PID_E0B4] +Summary = Foxconn T77w968 LTE modem +CounterpartGuid = USB\VID_0489&PID_E0B7 + +# T77W968 in fastboot mode +[USB\VID_0489&PID_E0B7] +Summary = Foxconn T77w968 LTE modem (fastboot) +CounterpartGuid = USB\VID_0489&PID_E0B4 + +# T77W968/eSIM +[USB\VID_0489&PID_E0B5] +Summary = Foxconn T77w968/eSIM LTE modem +CounterpartGuid = USB\VID_0489&PID_E0B8 + +# T77W968/eSIM in fastboot mode +[USB\VID_0489&PID_E0B8] +Summary = Foxconn T77w968/eSIM LTE modem (fastboot) +CounterpartGuid = USB\VID_0489&PID_E0B5 + +# Quectel EG25-G +[USB\VID_2C7C&PID_0125] +Summary = Quectel EG25-G modem +CounterpartGuid = USB\VID_18D1&PID_D00D +Flags = detach-at-fastboot-has-no-response +ModemManagerBranchAtCommand = AT+GETFWBRANCH +Plugin = modem_manager + +# Quectel EG25-G in fastboot mode +[USB\VID_18D1&PID_D00D] +Summary = Quectel EG25-G modem (fastboot) +CounterpartGuid = USB\VID_2C7C&PID_0125 +Flags = detach-at-fastboot-has-no-response +ModemManagerBranchAtCommand = AT+GETFWBRANCH + +# Qualcomm Sahara device +[USB\VID_05C6&PID_9008] +Summary = Qualcomm based modems in EDL (sahara) +Plugin = modem_manager diff --git a/fwupd-1.8.6/plugins/msr/README.md b/fwupd-1.8.6/plugins/msr/README.md new file mode 100644 index 0000000000000000000000000000000000000000..39323a3517a746efe539f66eb28513ab780a1bbd --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/README.md @@ -0,0 +1,16 @@ +# MSR + +## Introduction + +This plugin checks if the Model-specific registers (MSRs) indicate the +Direct Connect Interface (DCI) is enabled. + +DCI allows debugging of Intel processors using the USB3 port. DCI should +always be disabled and locked on production hardware as it allows the +attacker to disable other firmware protection methods. + +The result will be stored in a security attribute for HSI. + +## External Interface Access + +This plugin requires read access to `/sys/class/msr`. diff --git a/fwupd-1.8.6/plugins/msr/fu-msr-plugin.c b/fwupd-1.8.6/plugins/msr/fu-msr-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..c3e2568e603ec7fe5e67c164e2c1ae0bad7d179c --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/fu-msr-plugin.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-msr-plugin.h" + +typedef union { + guint32 data; + struct { + guint32 enabled : 1; + guint32 rsrvd : 29; + guint32 locked : 1; + guint32 debug_occurred : 1; + } __attribute__((packed)) fields; +} FuMsrIa32Debug; + +typedef union { + guint64 data; + struct { + guint32 lock_ro : 1; + guint32 enable : 1; + guint32 key_select : 1; + guint32 save_key_for_standby : 1; + guint32 policy_encryption_algo : 4; + guint32 reserved1 : 23; + guint32 bypass_enable : 1; + guint32 mk_tme_keyid_bits : 4; + guint32 reserved2 : 12; + guint32 mk_tme_crypto_algs : 16; + } __attribute__((packed)) fields; +} FuMsrIa32TmeActivation; + +typedef union { + guint32 data; + struct { + guint32 unknown0 : 23; /* 0 -> 22 inc */ + guint32 sme_is_enabled : 1; + guint32 unknown1 : 8; + } __attribute__((packed)) fields; +} FuMsrAMD64Syscfg; + +typedef union { + guint32 data; + struct { + guint32 sev_is_enabled : 1; + guint32 unknown0 : 31; + } __attribute__((packed)) fields; +} FuMsrAMD64Sev; + +struct _FuMsrPlugin { + FuPlugin parent_instance; + gboolean ia32_debug_supported; + gboolean ia32_tme_supported; + FuMsrIa32Debug ia32_debug; + FuMsrIa32TmeActivation ia32_tme_activation; + gboolean amd64_syscfg_supported; + gboolean amd64_sev_supported; + FuMsrAMD64Syscfg amd64_syscfg; + FuMsrAMD64Sev amd64_sev; +}; + +G_DEFINE_TYPE(FuMsrPlugin, fu_msr_plugin, FU_TYPE_PLUGIN) + +#define PCI_MSR_IA32_DEBUG_INTERFACE 0xc80 +#define PCI_MSR_IA32_TME_ACTIVATION 0x982 +#define PCI_MSR_IA32_BIOS_SIGN_ID 0x8b +#define PCI_MSR_AMD64_SYSCFG 0xC0010010 +#define PCI_MSR_AMD64_SEV 0xC0010131 + +static void +fu_msr_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + if (self->ia32_debug_supported) { + fu_string_append_kb(str, + idt, + "Ia32DebugInterfaceEnabled", + self->ia32_debug.fields.enabled); + fu_string_append_kb(str, + idt, + "Ia32DebugInterfaceLocked", + self->ia32_debug.fields.locked); + fu_string_append_kb(str, + idt, + "Ia32DebugInterfaceDebugOccurred", + self->ia32_debug.fields.debug_occurred); + } + if (self->ia32_tme_supported) { + fu_string_append_kb(str, + idt, + "Ia32TmeActivateLockRo", + self->ia32_tme_activation.fields.lock_ro); + fu_string_append_kb(str, + idt, + "Ia32TmeActivateEnable", + self->ia32_tme_activation.fields.enable); + fu_string_append_kb(str, + idt, + "Ia32TmeActivateBypassEnable", + self->ia32_tme_activation.fields.bypass_enable); + } + if (self->amd64_syscfg_supported) { + fu_string_append_kb(str, + idt, + "Amd64SyscfgSmeIsEnabled", + self->amd64_syscfg.fields.sme_is_enabled); + } + if (self->amd64_sev_supported) { + fu_string_append_kb(str, + idt, + "Amd64SevIsEnabled", + self->amd64_sev.fields.sev_is_enabled); + } +} + +static gboolean +fu_msr_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + guint eax = 0; + guint ebx = 0; + guint ecx = 0; + + if (!g_file_test("/dev/cpu", G_FILE_TEST_IS_DIR)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing kernel support"); + return FALSE; + } + + /* sdbg is supported: https://en.wikipedia.org/wiki/CPUID */ + if (fu_cpu_get_vendor() == FU_CPU_VENDOR_INTEL) { + if (!fu_cpuid(0x01, NULL, NULL, &ecx, NULL, error)) + return FALSE; + self->ia32_debug_supported = ((ecx >> 11) & 0x1) > 0; + if (!fu_cpuid(0x07, NULL, NULL, &ecx, NULL, error)) + return FALSE; + self->ia32_tme_supported = ((ecx >> 13) & 0x1) > 0; + } + + /* indicates support for SME and SEV */ + if (fu_cpu_get_vendor() == FU_CPU_VENDOR_AMD) { + if (!fu_cpuid(0x8000001f, &eax, &ebx, NULL, NULL, error)) + return FALSE; + g_debug("SME/SEV check MSR: eax 0%x, ebx 0%x", eax, ebx); + self->amd64_syscfg_supported = ((eax >> 0) & 0x1) > 0; + self->amd64_sev_supported = ((eax >> 1) & 0x1) > 0; + } + + return TRUE; +} + +static gboolean +fu_msr_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + FuDevice *device_cpu = fu_plugin_cache_lookup(plugin, "cpu"); + guint8 buf[8] = {0x0}; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree gchar *basename = NULL; + + /* interesting device? */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "msr") != 0) + return TRUE; + + /* we only care about the first processor */ + basename = g_path_get_basename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device))); + if (g_strcmp0(basename, "msr0") != 0) + return TRUE; + + /* open the config */ + fu_device_set_physical_id(FU_DEVICE(device), "msr"); + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* grab Intel MSR */ + if (self->ia32_debug_supported) { + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), + PCI_MSR_IA32_DEBUG_INTERFACE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "could not read IA32_DEBUG_INTERFACE: "); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + sizeof(buf), + 0x0, + &self->ia32_debug.data, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + if (self->ia32_tme_supported) { + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), + PCI_MSR_IA32_TME_ACTIVATION, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "could not read IA32_DEBUG_INTERFACE: "); + return FALSE; + } + if (!fu_memread_uint64_safe(buf, + sizeof(buf), + 0x0, + &self->ia32_tme_activation.data, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* grab AMD MSRs */ + if (self->amd64_syscfg_supported) { + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), + PCI_MSR_AMD64_SYSCFG, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "could not read PCI_MSR_AMD64_SYSCFG: "); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + sizeof(buf), + 0x0, + &self->amd64_syscfg.data, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + if (self->amd64_sev_supported) { + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), + PCI_MSR_AMD64_SEV, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "could not read PCI_MSR_AMD64_SEV: "); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + sizeof(buf), + 0x0, + &self->amd64_sev.data, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* get microcode version */ + if (device_cpu != NULL) { + guint32 ver_raw; + guint8 offset; + + if (!fu_cpuid(0x1, NULL, NULL, NULL, NULL, error)) + return FALSE; + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), + PCI_MSR_IA32_BIOS_SIGN_ID, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "could not read IA32_BIOS_SIGN_ID: "); + return FALSE; + } + fu_dump_raw(G_LOG_DOMAIN, "IA32_BIOS_SIGN_ID", buf, sizeof(buf)); + offset = fu_cpu_get_vendor() == FU_CPU_VENDOR_AMD ? 0x0 : 0x4; + if (!fu_memread_uint32_safe(buf, + sizeof(buf), + offset, + &ver_raw, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (ver_raw != 0 && ver_raw != G_MAXUINT32) { + FwupdVersionFormat verfmt = fu_device_get_version_format(device_cpu); + g_autofree gchar *ver_str = NULL; + ver_str = fu_version_from_uint32(ver_raw, verfmt); + g_debug("setting microcode version to %s", ver_str); + fu_device_set_version(device_cpu, ver_str); + fu_device_set_version_raw(device_cpu, ver_raw); + } + } + + /* success */ + return TRUE; +} + +static void +fu_msr_plugin_device_registered(FuPlugin *plugin, FuDevice *dev) +{ + if (g_strcmp0(fu_device_get_plugin(dev), "cpu") == 0) { + fu_plugin_cache_add(plugin, "cpu", dev); + return; + } +} + +static void +fu_plugin_add_security_attr_dci_enabled(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + FuDevice *device = fu_plugin_cache_lookup(plugin, "cpu"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* this MSR is only valid for a subset of Intel CPUs */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_INTEL) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED); + if (device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(device)); + fu_security_attrs_append(attrs, attr); + + /* check fields */ + if (!self->ia32_debug_supported) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + return; + } + if (self->ia32_debug.fields.enabled) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_plugin_add_security_attr_intel_tme_enabled(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* this MSR is only valid for a subset of Intel CPUs */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_INTEL) + return; + + /* create attr (which should already have been created in the cpu plugin) */ + attr = fu_security_attrs_get_by_appstream_id(attrs, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + if (attr == NULL) { + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + fu_security_attrs_append(attrs, attr); + } + + /* check fields */ + if (!self->ia32_tme_supported) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + if (!self->ia32_tme_activation.fields.enable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_remove_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + return; + } + if (self->ia32_tme_activation.fields.bypass_enable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED); + fwupd_security_attr_remove_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + return; + } + if (!self->ia32_tme_activation.fields.lock_ro) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_remove_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } +} + +static void +fu_plugin_add_security_attr_dci_locked(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + FuDevice *device = fu_plugin_cache_lookup(plugin, "cpu"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* this MSR is only valid for a subset of Intel CPUs */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_INTEL) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED); + if (device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(device)); + fu_security_attrs_append(attrs, attr); + + /* check fields */ + if (!self->ia32_debug_supported) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + return; + } + if (!self->ia32_debug.fields.locked) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static gboolean +fu_msr_plugin_safe_kernel_for_sme(FuPlugin *plugin, GError **error) +{ + g_autofree gchar *min = fu_plugin_get_config_value(plugin, "MinimumSmeKernelVersion"); + + if (min == NULL) { + g_debug("Ignoring kernel safety checks"); + return TRUE; + } + return fu_kernel_check_version(min, error); +} + +static gboolean +fu_msr_plugin_kernel_enabled_sme(GError **error) +{ + g_autofree gchar *buf = NULL; + gsize bufsz = 0; + if (!g_file_get_contents("/proc/cpuinfo", &buf, &bufsz, error)) + return FALSE; + if (bufsz > 0) { + g_auto(GStrv) tokens = fu_strsplit(buf, bufsz, " ", -1); + for (guint i = 0; tokens[i] != NULL; i++) { + if (g_strcmp0(tokens[i], "sme") == 0) + return TRUE; + } + } + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "sme support not enabled by kernel"); + return FALSE; +} + +static void +fu_plugin_add_security_attr_amd_sme_enabled(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuMsrPlugin *self = FU_MSR_PLUGIN(plugin); + FuDevice *device = fu_plugin_cache_lookup(plugin, "cpu"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + + /* this MSR is only valid for a subset of AMD CPUs */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_AMD) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + if (device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(device)); + fu_security_attrs_append(attrs, attr); + + /* check fields */ + if (!self->amd64_syscfg_supported) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + if (!self->amd64_syscfg.fields.sme_is_enabled) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + if (!fu_msr_plugin_safe_kernel_for_sme(plugin, &error_local)) { + g_debug("Unable to properly detect SME: %s", error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_UNKNOWN); + return; + } + + if (!(fu_msr_plugin_kernel_enabled_sme(&error_local))) { + g_debug("%s", error_local->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED); + fwupd_security_attr_add_obsolete(attr, "pci_psp"); +} + +static void +fu_msr_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + fu_plugin_add_security_attr_dci_enabled(plugin, attrs); + fu_plugin_add_security_attr_dci_locked(plugin, attrs); + fu_plugin_add_security_attr_amd_sme_enabled(plugin, attrs); + fu_plugin_add_security_attr_intel_tme_enabled(plugin, attrs); +} + +static void +fu_msr_plugin_init(FuMsrPlugin *self) +{ +} + +static void +fu_msr_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "msr"); +} + +static void +fu_msr_plugin_class_init(FuMsrPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_msr_plugin_constructed; + plugin_class->to_string = fu_msr_plugin_to_string; + plugin_class->startup = fu_msr_plugin_startup; + plugin_class->backend_device_added = fu_msr_plugin_backend_device_added; + plugin_class->add_security_attrs = fu_msr_plugin_add_security_attrs; + plugin_class->device_registered = fu_msr_plugin_device_registered; +} diff --git a/fwupd-1.8.6/plugins/msr/fu-msr-plugin.h b/fwupd-1.8.6/plugins/msr/fu-msr-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..f283e832a524fe52591b1c30d7f4c1f802e1d82f --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/fu-msr-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuMsrPlugin, fu_msr_plugin, FU, MSR_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/msr/fwupd-msr.conf b/fwupd-1.8.6/plugins/msr/fwupd-msr.conf new file mode 100644 index 0000000000000000000000000000000000000000..3e5ee7fa152a5c140b53e4d676e919177f909dee --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/fwupd-msr.conf @@ -0,0 +1 @@ +msr diff --git a/fwupd-1.8.6/plugins/msr/meson.build b/fwupd-1.8.6/plugins/msr/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..23ec5ec82739d429a43b6866e8c5dab5a60674a7 --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/meson.build @@ -0,0 +1,24 @@ +if hsi and has_cpuid +cargs = ['-DG_LOG_DOMAIN="FuPluginMsr"'] + +plugin_quirks += files('msr.quirk') + +if libsystemd.found() +install_data(['fwupd-msr.conf'], + install_dir: systemd_modules_load_dir, +) +endif + +install_data(['msr.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') +) +plugin_builtins += static_library('fu_plugin_msr', + sources: [ + 'fu-msr-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/msr/msr.conf b/fwupd-1.8.6/plugins/msr/msr.conf new file mode 100644 index 0000000000000000000000000000000000000000..30a7ef5b912a1c60aaa5e31388d8df6c202c3715 --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/msr.conf @@ -0,0 +1,5 @@ +[msr] + +# Minimum kernel version to allow probing for sme flag +MinimumSmeKernelVersion=5.18.0 + diff --git a/fwupd-1.8.6/plugins/msr/msr.quirk b/fwupd-1.8.6/plugins/msr/msr.quirk new file mode 100644 index 0000000000000000000000000000000000000000..2ef33d0374cbf0703744674d98472333f95ce72d --- /dev/null +++ b/fwupd-1.8.6/plugins/msr/msr.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[MSR] +Plugin = msr diff --git a/fwupd-1.8.6/plugins/mtd/README.md b/fwupd-1.8.6/plugins/mtd/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6eefb8849e51c05e7af6cdc7df5fc07daa2711a8 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/README.md @@ -0,0 +1,36 @@ +# MTD + +## Introduction + +The Memory Technology Device (MTD) interface is a way of abstracting flash devices as if they were +normal block devices. + +See for more details. + +This plugin supports the following protocol ID: + +* org.infradead.mtd + +## GUID Generation + +These devices use custom DeviceInstanceId values built from the device `NAME` and DMI data, e.g. + +* `MTD\NAME_Factory` +* `MTD\VENDOR_foo&NAME_baz` +* `MTD\VENDOR_foo&PRODUCT_bar&NAME_baz` + +If the `FirmwareGType` quirk is set for the device then the firmware is read back from the device at +daemon startup and parsed for the version number. +In the event the firmware has multiple child images then the device GUIDs are used as firmware IDs. + +## Update Behavior + +The MTD device is erased in chunks, written and then read back to verify. + +## Vendor ID Security + +The vendor ID is set from the system vendor, for example `DMI:LENOVO` + +## External Interface Access + +This plugin requires read/write access to `/dev/mtd`. diff --git a/fwupd-1.8.6/plugins/mtd/fu-mtd-device.c b/fwupd-1.8.6/plugins/mtd/fu-mtd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..987d9ba2b2c8f110aba79c727fb6fb89db1443c8 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/fu-mtd-device.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_MTD_USER_H +#include +#endif + +#include "fu-mtd-device.h" + +struct _FuMtdDevice { + FuUdevDevice parent_instance; + guint64 erasesize; +}; + +G_DEFINE_TYPE(FuMtdDevice, fu_mtd_device, FU_TYPE_UDEV_DEVICE) + +#define FU_MTD_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static void +fu_mtd_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuMtdDevice *self = FU_MTD_DEVICE(device); + if (self->erasesize > 0) + fu_string_append_kx(str, idt, "EraseSize", self->erasesize); +} + +static gboolean +fu_mtd_device_setup(FuDevice *device, GError **error) +{ + GPtrArray *instance_ids; + GType firmware_gtype = fu_device_get_firmware_gtype(device); + const gchar *fn; + g_autoptr(FuFirmware) firmware_child = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) imgs = NULL; + + /* nothing to do */ + if (firmware_gtype == G_TYPE_INVALID) + return TRUE; + + /* read entire contents */ + fn = fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)); + if (fn == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as no device file"); + return FALSE; + } + file = g_file_new_for_path(fn); + firmware = g_object_new(firmware_gtype, NULL); + if (!fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, error)) + return FALSE; + + /* find the firmware child that matches any of the device GUID, then use the first + * child that have a version, and finally use the main firmware as a fallback */ + instance_ids = fu_device_get_instance_ids(device); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string(instance_id); + firmware_child = fu_firmware_get_image_by_id(firmware, guid, NULL); + if (firmware_child != NULL) + break; + } + imgs = fu_firmware_get_images(firmware); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *firmare_tmp = g_ptr_array_index(imgs, i); + if (fu_firmware_get_version(firmare_tmp) != NULL || + fu_firmware_get_version_raw(firmare_tmp) != 0) { + firmware_child = g_object_ref(firmare_tmp); + break; + } + } + if (firmware_child == NULL) + firmware_child = g_object_ref(firmware); + + /* copy over the version */ + if (fu_firmware_get_version(firmware_child) != NULL) + fu_device_set_version(device, fu_firmware_get_version(firmware_child)); + if (fu_firmware_get_version_raw(firmware_child) != G_MAXUINT64) + fu_device_set_version_raw(device, fu_firmware_get_version_raw(firmware_child)); + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_open(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* FuUdevDevice->open */ + if (!FU_DEVICE_CLASS(fu_mtd_device_parent_class)->open(device, &error_local)) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + error_local->message); + return FALSE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_probe(FuDevice *device, GError **error) +{ + FuContext *ctx = fu_device_get_context(device); + FuMtdDevice *self = FU_MTD_DEVICE(device); + const gchar *name; + const gchar *vendor; + guint64 flags = 0; + guint64 size = 0; + g_autoptr(GError) error_local = NULL; + + /* set physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "mtd", error)) + return FALSE; + + /* flags have to exist */ + if (!fu_udev_device_get_sysfs_attr_uint64(FU_UDEV_DEVICE(device), + "flags", + &flags, + &error_local)) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no MTD flags"); + return FALSE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* get name */ + name = fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), "name", NULL); + if (name != NULL) + fu_device_set_name(FU_DEVICE(self), name); + + /* set vendor ID as the BIOS vendor */ + vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER); + if (vendor != NULL) { + g_autofree gchar *vendor_id = g_strdup_printf("DMI:%s", vendor); + fu_device_add_vendor_id(device, vendor_id); + } + + /* use vendor and product as an optional instance ID prefix */ + fu_device_add_instance_strsafe(device, "NAME", name); + fu_device_add_instance_strsafe(device, "VENDOR", vendor); + fu_device_add_instance_strsafe(device, + "PRODUCT", + fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_PRODUCT_NAME)); + fu_device_build_instance_id(device, NULL, "MTD", "NAME", NULL); + fu_device_build_instance_id(device, NULL, "MTD", "VENDOR", "NAME", NULL); + fu_device_build_instance_id(device, NULL, "MTD", "VENDOR", "PRODUCT", "NAME", NULL); + + /* get properties about the device */ + if (!fu_udev_device_get_sysfs_attr_uint64(FU_UDEV_DEVICE(device), "size", &size, error)) + return FALSE; + fu_device_set_firmware_size_max(device, size); +#ifdef HAVE_MTD_USER_H + if ((flags & MTD_NO_ERASE) == 0) { + if (!fu_udev_device_get_sysfs_attr_uint64(FU_UDEV_DEVICE(device), + "erasesize", + &self->erasesize, + error)) + return FALSE; + } + if (flags & MTD_WRITEABLE) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); +#endif + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_erase(FuMtdDevice *self, GBytes *fw, FuProgress *progress, GError **error) +{ +#ifdef HAVE_MTD_USER_H + g_autoptr(GPtrArray) chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, self->erasesize); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + /* erase each chunk */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + struct erase_info_user erase = { + .start = fu_chunk_get_address(chk), + .length = fu_chunk_get_data_sz(chk), + }; + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + 2, + (guint8 *)&erase, + NULL, + FU_MTD_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to erase @0x%x: ", (guint)erase.start); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as mtd-user.h is unavailable"); + return FALSE; +#endif +} + +static gboolean +fu_mtd_device_write(FuMtdDevice *self, GPtrArray *chunks, FuProgress *progress, GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + /* rewind */ + if (!fu_udev_device_seek(FU_UDEV_DEVICE(self), 0x0, error)) { + g_prefix_error(error, "failed to rewind: "); + return FALSE; + } + + /* write each chunk */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(self), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_verify(FuMtdDevice *self, GPtrArray *chunks, FuProgress *progress, GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + /* verify each chunk */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autofree guint8 *buf = g_malloc0(fu_chunk_get_data_sz(chk)); + g_autoptr(GBytes) blob1 = fu_chunk_get_bytes(chk); + g_autoptr(GBytes) blob2 = NULL; + + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), + fu_chunk_get_address(chk), + buf, + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to read @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + blob2 = g_bytes_new_static(buf, fu_chunk_get_data_sz(chk)); + if (!fu_bytes_compare(blob1, blob2, error)) { + g_prefix_error(error, + "failed to verify @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_write_verify(FuMtdDevice *self, GBytes *fw, FuProgress *progress, GError **error) +{ + g_autoptr(GPtrArray) chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, 10 * 1024); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 50, NULL); + + /* write */ + if (!fu_mtd_device_write(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + if (!fu_mtd_device_verify(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static GBytes * +fu_mtd_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMtdDevice *self = FU_MTD_DEVICE(device); + gsize bufsz = fu_device_get_firmware_size_max(device); + g_autofree guint8 *buf = g_malloc0(bufsz); + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + + /* read each chunk */ + chunks = fu_chunk_array_mutable_new(buf, bufsz, 0x0, 0x0, 10 * 1024); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), + fu_chunk_get_address(chk), + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to read @0x%x: ", + (guint)fu_chunk_get_address(chk)); + return NULL; + } + fu_progress_step_done(progress); + } + + /* success */ + return g_bytes_new_take(g_steal_pointer(&buf), bufsz); +} + +static gboolean +fu_mtd_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMtdDevice *self = FU_MTD_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + + /* get data to write */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + if (g_bytes_get_size(fw) > fu_device_get_firmware_size_max(device)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware too large, got 0x%x, expected <= 0x%x", + (guint)g_bytes_get_size(fw), + (guint)fu_device_get_firmware_size_max(device)); + return FALSE; + } + + /* just one step required */ + if (self->erasesize == 0) + return fu_mtd_device_write_verify(self, fw, progress, error); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + + /* erase */ + if (!fu_mtd_device_erase(self, fw, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + if (!fu_mtd_device_write_verify(self, fw, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_mtd_device_init(FuMtdDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "Memory Technology Device"); + fu_device_add_protocol(FU_DEVICE(self), "org.infradead.mtd"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_add_icon(FU_DEVICE(self), "drive-harddisk-solidstate"); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE | + FU_UDEV_DEVICE_FLAG_OPEN_SYNC); +} + +static void +fu_mtd_device_class_init(FuMtdDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->open = fu_mtd_device_open; + klass_device->probe = fu_mtd_device_probe; + klass_device->setup = fu_mtd_device_setup; + klass_device->to_string = fu_mtd_device_to_string; + klass_device->dump_firmware = fu_mtd_device_dump_firmware; + klass_device->write_firmware = fu_mtd_device_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/mtd/fu-mtd-device.h b/fwupd-1.8.6/plugins/mtd/fu-mtd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5fd90e4448b1b6065af1d95968910dbe1a756da7 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/fu-mtd-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_MTD_DEVICE (fu_mtd_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMtdDevice, fu_mtd_device, FU, MTD_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/mtd/fu-mtd-plugin.c b/fwupd-1.8.6/plugins/mtd/fu-mtd-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..43091d1289c9ab9abec7a56645daae159e23ff88 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/fu-mtd-plugin.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-mtd-device.h" +#include "fu-mtd-plugin.h" + +struct _FuMtdPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuMtdPlugin, fu_mtd_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_mtd_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ +#ifndef HAVE_MTD_USER_H + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not compiled with mtd support"); + return FALSE; +#endif + return TRUE; +} + +static void +fu_mtd_plugin_init(FuMtdPlugin *self) +{ +} + +static void +fu_mtd_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "mtd"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_MTD_DEVICE); +} + +static void +fu_mtd_plugin_class_init(FuMtdPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_mtd_plugin_constructed; + plugin_class->startup = fu_mtd_plugin_startup; +} diff --git a/fwupd-1.8.6/plugins/mtd/fu-mtd-plugin.h b/fwupd-1.8.6/plugins/mtd/fu-mtd-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..d097010f8d185dd86bf57ed7b109dafd0256f6a0 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/fu-mtd-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuMtdPlugin, fu_mtd_plugin, FU, MTD_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/mtd/fu-self-test.c b/fwupd-1.8.6/plugins/mtd/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..d96f7a8503dc764b05780886ecbc4139af2d4418 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/fu-self-test.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-mtd-device.h" + +static void +fu_test_mtd_device_func(void) +{ +#ifdef HAVE_GUDEV + gsize bufsz; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(NULL); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) fw2 = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GRand) rand = g_rand_new_with_seed(0); + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + g_autoptr(GUdevDevice) udev_device = NULL; + const gchar *dev_name; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + udev_device = + g_udev_client_query_by_sysfs_path(udev_client, "/sys/devices/virtual/mtd/mtd0"); + if (udev_device == NULL) { + g_test_skip("could not find mtdram device"); + return; + } + dev_name = g_udev_device_get_property(udev_device, "DEVNAME"); + if (g_strcmp0(dev_name, "/dev/mtd0") != 0) { + g_test_skip("DEVNAME not /dev/mtd0"); + return; + } + if (!g_file_test(dev_name, G_FILE_TEST_EXISTS)) { + g_test_skip("/dev/mtd0 doesn't exist"); + return; + } + + /* create device */ + device = g_object_new(FU_TYPE_MTD_DEVICE, "context", ctx, "udev-device", udev_device, NULL); + locker = fu_device_locker_new(device, &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("no permission to read mtdram device"); + return; + } + g_assert_no_error(error); + g_assert_nonnull(locker); + + dev_name = fu_device_get_name(device); + if (g_strcmp0(dev_name, "mtdram test device") != 0) { + g_test_skip("device is not mtdram test device"); + return; + } + + bufsz = fu_device_get_firmware_size_max(device); + g_assert_cmpint(bufsz, ==, 0x400000); + + /* create a random payload exactly the same size */ + for (gsize i = 0; i < bufsz; i++) + fu_byte_array_append_uint8(buf, g_rand_int_range(rand, 0x00, 0xFF)); + fw = g_byte_array_free_to_bytes(g_steal_pointer(&buf)); + + /* write with a verify */ + ret = fu_device_write_firmware(device, fw, progress, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* dump back */ + fu_progress_reset(progress); + fw2 = fu_device_dump_firmware(device, progress, &error); + g_assert_no_error(error); + g_assert_nonnull(fw2); + + /* verify */ + ret = fu_bytes_compare(fw, fw2, &error); + g_assert_no_error(error); + g_assert_true(ret); +#else + g_test_skip("no GUdev support"); +#endif +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + g_test_init(&argc, &argv, NULL); + (void)g_setenv("FWUPD_MTD_VERBOSE", "1", TRUE); + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + g_test_add_func("/mtd/device", fu_test_mtd_device_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/mtd/meson.build b/fwupd-1.8.6/plugins/mtd/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..316948e858a98067f523b434b0b60f1c68163414 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/meson.build @@ -0,0 +1,36 @@ +if get_option('plugin_mtd').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginMtd"'] + +plugin_quirks += files('mtd.quirk') +plugin_builtin_mtd = static_library('fu_plugin_mtd', + sources: [ + 'fu-mtd-plugin.c', + 'fu-mtd-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_mtd + +if get_option('tests') + e = executable( + 'mtd-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_mtd, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('mtd-self-test', e) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/mtd/mtd.quirk b/fwupd-1.8.6/plugins/mtd/mtd.quirk new file mode 100644 index 0000000000000000000000000000000000000000..e77d76bfa1bad287de7b13c4a7f8d72ab9319f10 --- /dev/null +++ b/fwupd-1.8.6/plugins/mtd/mtd.quirk @@ -0,0 +1,6 @@ +[MTD] +Plugin = mtd + +[MTD\VENDOR_PINE64&PRODUCT_PinePhone-Pro&NAME_spi1.0] +Name = Tow-Boot platform firmware +FirmwareGType = FuUswidFirmware diff --git a/fwupd-1.8.6/plugins/nitrokey/README.md b/fwupd-1.8.6/plugins/nitrokey/README.md new file mode 100644 index 0000000000000000000000000000000000000000..93773390434c94cd4d4d90c984bcba58d22a286d --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/README.md @@ -0,0 +1,38 @@ +# Nitrokey + +## Introduction + +This plugin is used to get the correct version number on Nitrokey storage +devices. These devices have updatable firmware but so far no updates are +available from the vendor. + +The device is switched to a DFU bootloader only when the secret firmware pin +is entered into the nitrokey-app tool. This cannot be automated. + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_20A0&PID_4109&REV_0001` +* `USB\VID_20A0&PID_4109` +* `USB\VID_20A0` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different USB VID and PID in DFU mode. The device is then handled by the `dfu` +plugin. + +On DFU attach the device again re-enumerates back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x20A0` +in runtime mode and `USB:0x03EB` in bootloader mode. + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-common.c b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-common.c new file mode 100644 index 0000000000000000000000000000000000000000..5d47b4f1fdf1163e1e312a7bba7047103efdc34a --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-common.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-nitrokey-common.h" + +static guint32 +fu_nitrokey_perform_crc32_mutate(guint32 crc, guint32 data) +{ + crc = crc ^ data; + for (guint i = 0; i < 32; i++) { + if (crc & 0x80000000) { + /* polynomial used in STM32 */ + crc = (crc << 1) ^ 0x04C11DB7; + } else { + crc = (crc << 1); + } + } + return crc; +} + +guint32 +fu_nitrokey_perform_crc32(const guint8 *data, gsize size) +{ + guint32 crc = 0xffffffff; + g_autofree guint32 *data_aligned = NULL; + data_aligned = g_new0(guint32, (size / 4) + 1); + memcpy(data_aligned, data, size); + for (gsize idx = 0; idx * 4 < size; idx++) { + guint32 data_aligned_le = GUINT32_FROM_LE(data_aligned[idx]); + crc = fu_nitrokey_perform_crc32_mutate(crc, data_aligned_le); + } + return crc; +} diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-common.h b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-common.h new file mode 100644 index 0000000000000000000000000000000000000000..7657602bbc794b148172b4a7cd26f69ee242e9d4 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-common.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +guint32 +fu_nitrokey_perform_crc32(const guint8 *data, gsize size); + +#define NITROKEY_TRANSACTION_TIMEOUT 100 /* ms */ +#define NITROKEY_NR_RETRIES 5 + +#define NITROKEY_REQUEST_DATA_LENGTH 59 +#define NITROKEY_REPLY_DATA_LENGTH 53 + +#define NITROKEY_CMD_GET_DEVICE_STATUS (0x20 + 14) + +typedef struct __attribute__((packed)) { + guint8 command; + guint8 payload[NITROKEY_REQUEST_DATA_LENGTH]; + guint32 crc; +} NitrokeyHidRequest; + +typedef struct __attribute__((packed)) { + guint8 device_status; + guint8 command_id; + guint32 last_command_crc; + guint8 last_command_status; + guint8 payload[NITROKEY_REPLY_DATA_LENGTH]; + guint32 crc; +} NitrokeyHidResponse; + +/* based from libnitrokey/stick20_commands.h from libnitrokey v3.4.1 */ +typedef struct __attribute__((packed)) { + guint8 _padding[18]; /* stick20_commands.h:132 // 26 - 8 = 18 */ + guint8 SendCounter; + guint8 SendDataType; + guint8 FollowBytesFlag; + guint8 SendSize; + guint16 MagicNumber_StickConfig; + guint8 ReadWriteFlagUncryptedVolume; + guint8 ReadWriteFlagCryptedVolume; + guint8 VersionMajor; + guint8 VersionMinor; + guint8 VersionReservedByte; + guint8 VersionBuildIteration; + guint8 ReadWriteFlagHiddenVolume; + guint8 FirmwareLocked; + guint8 NewSDCardFound; + guint8 SDFillWithRandomChars; + guint32 ActiveSD_CardID; + guint8 VolumeActiceFlag; + guint8 NewSmartCardFound; + guint8 UserPwRetryCount; + guint8 AdminPwRetryCount; + guint32 ActiveSmartCardID; + guint8 StickKeysNotInitiated; +} NitrokeyGetDeviceStatusPayload; diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-device.c b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-device.c new file mode 100644 index 0000000000000000000000000000000000000000..30025001ffa88f6777b944bb90d39c8715d97416 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-device.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-nitrokey-common.h" +#include "fu-nitrokey-device.h" + +G_DEFINE_TYPE(FuNitrokeyDevice, fu_nitrokey_device, FU_TYPE_HID_DEVICE) + +typedef struct { + guint8 command; + const guint8 *buf_in; + gsize buf_in_sz; + guint8 *buf_out; + gsize buf_out_sz; +} NitrokeyRequest; + +static gboolean +nitrokey_execute_cmd_cb(FuDevice *device, gpointer user_data, GError **error) +{ + NitrokeyRequest *req = (NitrokeyRequest *)user_data; + NitrokeyHidResponse res; + guint32 crc_tmp; + guint8 buf[64]; + + /* create the request */ + memset(buf, 0x00, sizeof(buf)); + buf[0] = req->command; + if (req->buf_in != NULL) + memcpy(&buf[1], req->buf_in, req->buf_in_sz); + crc_tmp = fu_nitrokey_perform_crc32(buf, sizeof(buf) - 4); + fu_memwrite_uint32(&buf[NITROKEY_REQUEST_DATA_LENGTH + 1], crc_tmp, G_LITTLE_ENDIAN); + + /* send request */ + if (!fu_hid_device_set_report(FU_HID_DEVICE(device), + 0x0002, + buf, + sizeof(buf), + NITROKEY_TRANSACTION_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + + /* get response */ + memset(buf, 0x00, sizeof(buf)); + if (!fu_hid_device_get_report(FU_HID_DEVICE(device), + 0x0002, + buf, + sizeof(buf), + NITROKEY_TRANSACTION_TIMEOUT, + FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + + /* verify this is the answer to the question we asked */ + memcpy(&res, buf, sizeof(buf)); + if (GUINT32_FROM_LE(res.last_command_crc) != crc_tmp) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got response CRC %x, expected %x", + GUINT32_FROM_LE(res.last_command_crc), + crc_tmp); + return FALSE; + } + + /* verify the response checksum */ + crc_tmp = fu_nitrokey_perform_crc32(buf, sizeof(res) - 4); + if (GUINT32_FROM_LE(res.crc) != crc_tmp) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "got packet CRC %x, expected %x", + GUINT32_FROM_LE(res.crc), + crc_tmp); + return FALSE; + } + + /* copy out the payload */ + if (req->buf_out != NULL) + memcpy(req->buf_out, &res.payload, req->buf_out_sz); + + /* success */ + return TRUE; +} + +static gboolean +nitrokey_execute_cmd_full(FuDevice *device, + guint8 command, + const guint8 *buf_in, + gsize buf_in_sz, + guint8 *buf_out, + gsize buf_out_sz, + GError **error) +{ + NitrokeyRequest req = { + .command = command, + .buf_in = buf_in, + .buf_in_sz = buf_in_sz, + .buf_out = buf_out, + .buf_out_sz = buf_out_sz, + }; + g_return_val_if_fail(buf_in_sz <= NITROKEY_REQUEST_DATA_LENGTH, FALSE); + g_return_val_if_fail(buf_out_sz <= NITROKEY_REPLY_DATA_LENGTH, FALSE); + return fu_device_retry(device, nitrokey_execute_cmd_cb, NITROKEY_NR_RETRIES, &req, error); +} + +static gboolean +fu_nitrokey_device_setup(FuDevice *device, GError **error) +{ + NitrokeyGetDeviceStatusPayload payload; + guint8 buf_reply[NITROKEY_REPLY_DATA_LENGTH]; + g_autofree gchar *version = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_nitrokey_device_parent_class)->setup(device, error)) + return FALSE; + + /* get firmware version */ + if (!nitrokey_execute_cmd_full(device, + NITROKEY_CMD_GET_DEVICE_STATUS, + NULL, + 0, + buf_reply, + sizeof(buf_reply), + error)) { + g_prefix_error(error, "failed to do get firmware version: "); + return FALSE; + } + if (g_getenv("FWUPD_NITROKEY_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "payload", buf_reply, sizeof(buf_reply)); + memcpy(&payload, buf_reply, sizeof(payload)); + version = g_strdup_printf("%u.%u", payload.VersionMajor, payload.VersionMinor); + fu_device_set_version(FU_DEVICE(device), version); + + /* success */ + return TRUE; +} + +static void +fu_nitrokey_device_init(FuNitrokeyDevice *self) +{ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_add_protocol(FU_DEVICE(self), "org.usb.dfu"); + fu_device_retry_set_delay(FU_DEVICE(self), 100); +} + +static void +fu_nitrokey_device_class_init(FuNitrokeyDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_nitrokey_device_setup; +} diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-device.h b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f8c91e38ee6938d714ecb50fe1acf02e678c0887 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_NITROKEY_DEVICE (fu_nitrokey_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuNitrokeyDevice, fu_nitrokey_device, FU, NITROKEY_DEVICE, FuHidDevice) + +struct _FuNitrokeyDeviceClass { + FuHidDeviceClass parent_class; +}; diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-plugin.c b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..53c76ad01cf45675dcf753065efc1360eee56944 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nitrokey-device.h" +#include "fu-nitrokey-plugin.h" + +struct _FuNitrokeyPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuNitrokeyPlugin, fu_nitrokey_plugin, FU_TYPE_PLUGIN) + +static void +fu_nitrokey_plugin_init(FuNitrokeyPlugin *self) +{ +} + +static void +fu_nitrokey_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_NITROKEY_DEVICE); +} + +static void +fu_nitrokey_plugin_class_init(FuNitrokeyPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_nitrokey_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-plugin.h b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..9394de94e5b84e9d4b704f2caf6bd8fa8c692669 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-nitrokey-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuNitrokeyPlugin, fu_nitrokey_plugin, FU, NITROKEY_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/nitrokey/fu-self-test.c b/fwupd-1.8.6/plugins/nitrokey/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..7359f614276764548d496e13e0be57acf0389366 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/fu-self-test.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#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, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f}; + g_assert_cmpint(fu_nitrokey_perform_crc32(buf, 16), ==, 0x081B46CA); + g_assert_cmpint(fu_nitrokey_perform_crc32(buf, 15), ==, 0xED7320AB); +} + +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/nitrokey", fu_nitrokey_func); + g_test_add_func("/fwupd/nitrokey-version-static", fu_nitrokey_version_test_static); + g_test_add_func("/fwupd/nitrokey-version", fu_nitrokey_version_test); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/nitrokey/lsusb.txt b/fwupd-1.8.6/plugins/nitrokey/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..199c2185e09b1ea31b2f3b9ea674d6b8dae6f7a6 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/lsusb.txt @@ -0,0 +1,217 @@ +Bus 001 Device 007: ID 20a0:4109 Clay Logic +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x20a0 Clay Logic + idProduct 0x4109 + bcdDevice 1.00 + iManufacturer 1 Nitrokey + iProduct 2 Nitrokey Storage + iSerial 3 0000000000000 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 141 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 8 Mass Storage + bInterfaceSubClass 6 SCSI + bInterfaceProtocol 80 Bulk-Only + iInterface 0 + 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 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 11 Chip/SmartCard + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + ChipCard Interface Descriptor: + bLength 54 + bDescriptorType 33 + bcdCCID 1.10 (Warning: Only accurate for version 1.0) + nMaxSlotIndex 0 + bVoltageSupport 2 3.0V + dwProtocols 2 T=1 + dwDefaultClock 3600 + dwMaxiumumClock 3600 + bNumClockSupported 0 + dwDataRate 9677 bps + dwMaxDataRate 116129 bps + bNumDataRatesSupp. 0 + dwMaxIFSD 261 + dwSyncProtocols 00000000 + dwMechanical 00000000 + dwFeatures 000104BA + Auto configuration based on ATR + Auto voltage selection + Auto clock change + Auto baud rate change + Auto PPS made by CCID + Auto IFSD exchange + TPDU level exchange + dwMaxCCIDMsgLen 271 + bClassGetResponse 00 + bClassEnvelope 00 + wlcdLayout none + bPINSupport 0 + bMaxCCIDBusySlots 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 16 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x04 EP 4 OUT + 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 0x85 EP 5 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 1 Keyboard + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 71 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x86 EP 6 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 +Device Qualifier (for other device speed): + bLength 10 + bDescriptorType 6 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + bNumConfigurations 1 +Device Status: 0x0000 + (Bus Powered) + +Bus 001 Device 055: ID 03eb:2ff1 Atmel Corp. at32uc3a3 DFU bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x03eb Atmel Corp. + idProduct 0x2ff1 at32uc3a3 DFU bootloader + bcdDevice 10.00 + iManufacturer 1 ATMEL + iProduct 2 AT32UC3A DFU + iSerial 3 1.0.3 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 27 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 15 + Will Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 0 milliseconds + wTransferSize 65535 bytes + bcdDFUVersion 1.01 +Device Status: 0x0001 + Self Powered diff --git a/fwupd-1.8.6/plugins/nitrokey/meson.build b/fwupd-1.8.6/plugins/nitrokey/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d3e9908f037a62019a2b00152c6938770a897564 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/meson.build @@ -0,0 +1,39 @@ +if get_option('plugin_nitrokey').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginNitrokey"'] + +plugin_quirks += files('nitrokey.quirk') +plugin_builtin_nitrokey = static_library('fu_plugin_nitrokey', + sources: [ + 'fu-nitrokey-device.c', + 'fu-nitrokey-common.c', + 'fu-nitrokey-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_nitrokey + +if get_option('tests') + e = executable( + 'nitrokey-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + valgrind, + ], + link_with: [ + plugin_libs, + plugin_builtin_nitrokey, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('nitrokey-self-test', e) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/nitrokey/nitrokey.quirk b/fwupd-1.8.6/plugins/nitrokey/nitrokey.quirk new file mode 100644 index 0000000000000000000000000000000000000000..a624348657cbe154f2c03f77fd73962e67e253f1 --- /dev/null +++ b/fwupd-1.8.6/plugins/nitrokey/nitrokey.quirk @@ -0,0 +1,7 @@ +# Nitrokey Storage +[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 --git a/fwupd-1.8.6/plugins/nordic-hid/README.md b/fwupd-1.8.6/plugins/nordic-hid/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9f49f34a3aae8eb1763374e848b86708c1388d3e --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/README.md @@ -0,0 +1,58 @@ +# Nordic Semiconductor HID + +## Introduction + +This plugin is able flash the firmware for the hardware supported by `nRF52-Desktop`. +Tested with the following devices: + +* [nrf52840dk development kit](https://www.nordicsemi.com/Products/nRF52840) +* [nRF52840 Dongle](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle) +* nRF52840 Gaming Mouse +* nRF52832 Desktop Keyboard + +The plugin is using Nordic Semiconductor +[HID config channel](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/applications/nrf_desktop/doc/config_channel.html) +to perform devices update. + +## Firmware Format + +The cabinet file contains ZIP archive prepared by Nordic Semiconductor. +This ZIP archive includes 2 signed image blobs for the target +device, one firmware blob per application slot, and the `manifest.json` file with the metadata description. +At the moment only [nRF Secure Immutable Bootloader](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/samples/bootloader/README.html#bootloader) +aka "B0" is supported and tested. + +This plugin supports the following protocol ID: + +* Nordic HID Config Channel: com.nordic.hidcfgchannel + +## GUID Generation + +For GUID generation the standard HIDRAW DeviceInstanceId values are used +with the addition of the target board and bootloader name: + +* `HIDRAW\VEN_1915&DEV_52DE&BOARD_nrf52840dk&BL_B0` -> 22952036-c346-5755-9646-7bf766b28922 +* `HIDRAW\VEN_1915&DEV_52DE&BOARD_nrf52840dk&BL_MCUBOOT` -> 43b38427-fdf5-5400-a23c-f3eb7ea00e7c + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### NordicHidBootloader + +Explicitly set the expected bootloader type: "B0" or "MCUBOOT" +This quirk must be set for devices without support of `bootloader variant` DFU option. + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the HID vendor ID, in this instance set +to `HIDRAW:0x1915`. + +## External Interface Access + +This plugin requires ioctl `HIDIOCSFEATURE` and `HIDIOCGFEATURE` access. diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-archive.c b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-archive.c new file mode 100644 index 0000000000000000000000000000000000000000..0fcc2880d0b4fed344390474be55bf15599e0563 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-archive.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nordic-hid-archive.h" +#include "fu-nordic-hid-firmware-b0.h" +#include "fu-nordic-hid-firmware-mcuboot.h" + +/* current version format is 0 */ +#define MAX_VERSION_FORMAT 0 + +struct _FuNordicHidArchive { + FuFirmwareClass parent_instance; +}; + +G_DEFINE_TYPE(FuNordicHidArchive, fu_nordic_hid_archive, FU_TYPE_FIRMWARE) + +static gboolean +fu_nordic_hid_archive_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + JsonNode *json_root_node; + JsonObject *json_obj; + JsonArray *json_files; + guint manifest_ver; + guint files_cnt = 0; + GBytes *manifest = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + + /* load archive */ + archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + manifest = fu_archive_lookup_by_fn(archive, "manifest.json", error); + if (manifest == NULL) + return FALSE; + + /* parse JSON */ + if (!json_parser_load_from_data(parser, + (const gchar *)g_bytes_get_data(manifest, NULL), + (gssize)g_bytes_get_size(manifest), + error)) { + g_prefix_error(error, "manifest not in JSON format: "); + return FALSE; + } + json_root_node = json_parser_get_root(parser); + if (json_root_node == NULL || !JSON_NODE_HOLDS_OBJECT(json_root_node)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest invalid as has no root"); + return FALSE; + } + + json_obj = json_node_get_object(json_root_node); + if (!json_object_has_member(json_obj, "format-version")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest has invalid format"); + return FALSE; + } + + manifest_ver = json_object_get_int_member(json_obj, "format-version"); + if (manifest_ver > MAX_VERSION_FORMAT) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unsupported manifest version"); + return FALSE; + } + + json_files = json_object_get_array_member(json_obj, "files"); + if (json_files == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest invalid as has no 'files' array"); + return FALSE; + } + + files_cnt = json_array_get_length(json_files); + if (files_cnt == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest invalid as contains no update images"); + return FALSE; + } + + for (guint i = 0; i < files_cnt; i++) { + const gchar *filename = NULL; + const gchar *bootloader_name = NULL; + guint image_addr = 0; + JsonObject *obj = json_array_get_object_element(json_files, i); + GBytes *blob = NULL; + g_autoptr(FuFirmware) image = NULL; + g_autofree gchar *image_id = NULL; + g_auto(GStrv) board_split = NULL; + + if (!json_object_has_member(obj, "file")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest invalid as has no file name for the image"); + return FALSE; + } + filename = json_object_get_string_member(obj, "file"); + blob = fu_archive_lookup_by_fn(archive, filename, error); + if (blob == NULL) + return FALSE; + + if (json_object_has_member(obj, "version_B0")) { + bootloader_name = "B0"; + image = g_object_new(FU_TYPE_NORDIC_HID_FIRMWARE_B0, NULL); + } else if (json_object_has_member(obj, "version_MCUBOOT")) { + bootloader_name = "MCUBOOT"; + image = g_object_new(FU_TYPE_NORDIC_HID_FIRMWARE_MCUBOOT, NULL); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "only B0 and MCUboot bootloaders are supported"); + return FALSE; + } + + /* the "board" field contains board name before "_" symbol */ + if (!json_object_has_member(obj, "board")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest invalid as has no target board information"); + return FALSE; + } + board_split = g_strsplit(json_object_get_string_member(obj, "board"), "_", -1); + if (board_split[0] == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "manifest invalid as has no target board information"); + return FALSE; + } + /* images for B0 bootloader are listed in strict order: + * this is guaranteed by producer set the id format as + * __N, i.e "nrf52840dk_B0_bank0". + * For MCUBoot bootloader only the single image is available */ + image_id = g_strdup_printf("%s_%s_bank%01u", board_split[0], bootloader_name, i); + if (!fu_firmware_parse(image, blob, flags | FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + + fu_firmware_set_id(image, image_id); + fu_firmware_set_idx(image, i); + if (json_object_has_member(obj, "load_address")) { + image_addr = json_object_get_int_member(obj, "load_address"); + fu_firmware_set_addr(image, image_addr); + } + fu_firmware_add_image(firmware, image); + } + + /* success */ + return TRUE; +} + +static void +fu_nordic_hid_archive_init(FuNordicHidArchive *self) +{ +} + +static void +fu_nordic_hid_archive_class_init(FuNordicHidArchiveClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_nordic_hid_archive_parse; +} diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-archive.h b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-archive.h new file mode 100644 index 0000000000000000000000000000000000000000..8c707e13ed83c771e52d4d3a026d69d4ed8aaaf2 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-archive.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_NORDIC_HID_ARCHIVE (fu_nordic_hid_archive_get_type()) +G_DECLARE_FINAL_TYPE(FuNordicHidArchive, + fu_nordic_hid_archive, + FU, + NORDIC_HID_ARCHIVE, + FuArchiveFirmware) diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c new file mode 100644 index 0000000000000000000000000000000000000000..2856e2e4247a25af259d6698f6f8652225c00fb5 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c @@ -0,0 +1,1236 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_HIDRAW_H +#include +#include +#endif +#include + +#include "fu-nordic-hid-archive.h" +#include "fu-nordic-hid-cfg-channel.h" + +#define HID_REPORT_ID 6 +#define REPORT_SIZE 30 +#define REPORT_DATA_MAX_LEN (REPORT_SIZE - 5) +#define HWID_LEN 8 +#define END_OF_TRANSFER_CHAR 0x0a +#define INVALID_PEER_ID 0xFF + +#define FU_NORDIC_HID_CFG_CHANNEL_RETRIES 10 +#define FU_NORDIC_HID_CFG_CHANNEL_RETRY_DELAY 50 /* ms */ +#define FU_NORDIC_HID_CFG_CHANNEL_DFU_RETRY_DELAY 500 /* ms */ + +#define FU_NORDIC_HID_CFG_CHANNEL_IOCTL_TIMEOUT 5000 /* ms */ + +typedef enum { + CONFIG_STATUS_PENDING, + CONFIG_STATUS_GET_MAX_MOD_ID, + CONFIG_STATUS_GET_HWID, + CONFIG_STATUS_GET_BOARD_NAME, + CONFIG_STATUS_INDEX_PEERS, + CONFIG_STATUS_GET_PEER, + CONFIG_STATUS_SET, + CONFIG_STATUS_FETCH, + CONFIG_STATUS_SUCCESS, + CONFIG_STATUS_TIMEOUT, + CONFIG_STATUS_REJECT, + CONFIG_STATUS_WRITE_FAIL, + CONFIG_STATUS_DISCONNECTED, + CONFIG_STATUS_FAULT = 99, +} FuNordicCfgStatus; + +typedef enum { + DFU_STATE_INACTIVE, + DFU_STATE_ACTIVE, + DFU_STATE_STORING, + DFU_STATE_CLEANING, +} FuNordicCfgSyncState; + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 recipient; + guint8 event_id; + guint8 status; + guint8 data_len; + guint8 data[REPORT_DATA_MAX_LEN]; +} FuNordicCfgChannelMsg; + +typedef struct { + guint8 idx; + gchar *name; +} FuNordicCfgChannelModuleOption; + +typedef struct { + guint8 idx; + gchar *name; + GPtrArray *options; /* of FuNordicCfgChannelModuleOption */ +} FuNordicCfgChannelModule; + +typedef struct { + guint8 status; + guint8 *buf; + gsize bufsz; +} FuNordicCfgChannelRcvHelper; + +typedef struct { + guint8 dfu_state; + guint32 img_length; + guint32 img_csum; + guint32 offset; + guint16 sync_buffer_size; +} FuNordicCfgChannelDfuInfo; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuNordicCfgChannelMsg, g_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuNordicCfgChannelDfuInfo, g_free); + +struct _FuNordicHidCfgChannel { + FuUdevDevice parent_instance; + gchar *board_name; + gchar *bl_name; + guint8 flash_area_id; + guint32 flashed_image_len; + guint8 peer_id; + GPtrArray *modules; /* of FuNordicCfgChannelModule */ +}; + +G_DEFINE_TYPE(FuNordicHidCfgChannel, fu_nordic_hid_cfg_channel, FU_TYPE_UDEV_DEVICE) + +static void +fu_nordic_hid_cfg_channel_module_option_free(FuNordicCfgChannelModuleOption *opt) +{ + g_free(opt->name); + g_free(opt); +} + +static void +fu_nordic_hid_cfg_channel_module_free(FuNordicCfgChannelModule *mod) +{ + if (mod->options != NULL) + g_ptr_array_unref(mod->options); + g_free(mod->name); + g_free(mod); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuNordicCfgChannelModule, fu_nordic_hid_cfg_channel_module_free); + +#ifdef HAVE_HIDRAW_H +static FuUdevDevice * +fu_nordic_hid_cfg_channel_get_udev_device(FuNordicHidCfgChannel *self, GError **error) +{ + FuDevice *parent; + + /* ourselves */ + if (self->peer_id == 0) + return FU_UDEV_DEVICE(self); + + /* parent */ + parent = fu_device_get_parent(FU_DEVICE(self)); + if (parent == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no parent for peer 0x%02x", + self->peer_id); + return NULL; + } + return FU_UDEV_DEVICE(parent); +} +#endif + +static gboolean +fu_nordic_hid_cfg_channel_send(FuNordicHidCfgChannel *self, + guint8 *buf, + gsize bufsz, + GError **error) +{ +#ifdef HAVE_HIDRAW_H + FuUdevDevice *udev_device = fu_nordic_hid_cfg_channel_get_udev_device(self, error); + if (udev_device == NULL) + return FALSE; + if (g_getenv("FWUPD_NORDIC_HID_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Sent", buf, bufsz); + if (!fu_udev_device_ioctl(udev_device, + HIDIOCSFEATURE(bufsz), + buf, + NULL, + FU_NORDIC_HID_CFG_CHANNEL_IOCTL_TIMEOUT, + error)) + return FALSE; + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_nordic_hid_cfg_channel_receive(FuNordicHidCfgChannel *self, + guint8 *buf, + gsize bufsz, + GError **error) +{ + g_autoptr(FuNordicCfgChannelMsg) recv_msg = g_new0(FuNordicCfgChannelMsg, 1); +#ifdef HAVE_HIDRAW_H + FuUdevDevice *udev_device = fu_nordic_hid_cfg_channel_get_udev_device(self, error); + if (udev_device == NULL) + return FALSE; + for (gint i = 1; i < 100; i++) { + recv_msg->report_id = HID_REPORT_ID; + recv_msg->recipient = self->peer_id; + if (!fu_udev_device_ioctl(udev_device, + HIDIOCGFEATURE(sizeof(*recv_msg)), + (guint8 *)recv_msg, + NULL, + FU_NORDIC_HID_CFG_CHANNEL_IOCTL_TIMEOUT, + error)) + return FALSE; + /* if the device is busy it return 06 00 00 00 00 response */ + if (recv_msg->report_id == HID_REPORT_ID && + (recv_msg->recipient | recv_msg->event_id | recv_msg->status | + recv_msg->data_len)) + break; + g_usleep(i * 50); + } + if (!fu_memcpy_safe(buf, + bufsz, + 0, + (guint8 *)recv_msg, + sizeof(*recv_msg), + 0, + sizeof(*recv_msg), + error)) { + return FALSE; + } + + if (g_getenv("FWUPD_NORDIC_HID_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Received", buf, bufsz); + /* + * [TODO]: Possibly add the report-id fix for Bluez versions < 5.56: + * https://github.com/bluez/bluez/commit/35a2c50437cca4d26ac6537ce3a964bb509c9b62 + * + * See fu_pxi_ble_device_get_feature() in + * plugins/pixart-rf/fu-pxi-ble-device.c for an example. + */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_nordic_hid_cfg_channel_receive_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuNordicCfgChannelRcvHelper *args = (FuNordicCfgChannelRcvHelper *)user_data; + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(device); + FuNordicCfgChannelMsg *recv_msg = NULL; + + if (!fu_nordic_hid_cfg_channel_receive(self, args->buf, args->bufsz, error)) + return FALSE; + recv_msg = (FuNordicCfgChannelMsg *)args->buf; + if (recv_msg->status != args->status) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "received status: 0x%02x, expected: 0x%02x", + recv_msg->status, + args->status); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* + * fu_nordic_hid_cfg_channel_get_event_id: + * @module_name: module name, NULL for generic operations + * @option_name: option name, NULL for generic module operations + * + * Construct Event ID from module and option names. + * + * Returns: %TRUE if module/option pair found + */ +static gboolean +fu_nordic_hid_cfg_channel_get_event_id(FuNordicHidCfgChannel *self, + const gchar *module_name, + const gchar *option_name, + guint8 *event_id) +{ + FuNordicCfgChannelModule *mod = NULL; + guint id = 0; + + *event_id = 0; + + /* for generic operations */ + if (module_name == NULL) + return TRUE; + + for (id = 0; id < self->modules->len; id++) { + mod = g_ptr_array_index(self->modules, id); + if (g_strcmp0(module_name, mod->name) == 0) + break; + } + if (mod == NULL || id > 0x0f) + return FALSE; + + *event_id = id << 4; + + /* for generic module operations */ + if (option_name == NULL) + return TRUE; + + for (guint i = 0; i < mod->options->len && i <= 0x0f; i++) { + FuNordicCfgChannelModuleOption *opt = g_ptr_array_index(mod->options, i); + if (g_strcmp0(option_name, opt->name) == 0) { + *event_id = (id << 4) + opt->idx; + return TRUE; + } + } + + /* module have no requested option */ + return FALSE; +} + +static gboolean +fu_nordic_hid_cfg_channel_cmd_send_by_id(FuNordicHidCfgChannel *self, + guint8 event_id, + guint8 status, + guint8 *data, + guint8 data_len, + GError **error) +{ + g_autoptr(FuNordicCfgChannelMsg) msg = g_new0(FuNordicCfgChannelMsg, 1); + + msg->report_id = HID_REPORT_ID; + msg->recipient = self->peer_id; + msg->event_id = event_id; + msg->status = status; + msg->data_len = 0; + + if (data != NULL) { + if (data_len > REPORT_DATA_MAX_LEN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "requested to send %d bytes, while maximum is %d", + data_len, + REPORT_DATA_MAX_LEN); + return FALSE; + } + if (!fu_memcpy_safe(msg->data, + REPORT_DATA_MAX_LEN, + 0, + data, + data_len, + 0, + data_len, + error)) + return FALSE; + msg->data_len = data_len; + } + + if (!fu_nordic_hid_cfg_channel_send(self, (guint8 *)msg, sizeof(*msg), error)) { + g_prefix_error(error, "failed to send: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_cmd_send(FuNordicHidCfgChannel *self, + const gchar *module_name, + const gchar *option_name, + guint8 status, + guint8 *data, + guint8 data_len, + GError **error) +{ + guint8 event_id = 0; + + if (!fu_nordic_hid_cfg_channel_get_event_id(self, module_name, option_name, &event_id)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "requested non-existing module %s with option %s", + module_name, + option_name); + return FALSE; + } + + if (!fu_nordic_hid_cfg_channel_cmd_send_by_id(self, + event_id, + status, + data, + data_len, + error)) { + g_prefix_error(error, "failed to send: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_cmd_receive(FuNordicHidCfgChannel *self, + guint8 status, + FuNordicCfgChannelMsg *res, + GError **error) +{ + FuNordicCfgChannelRcvHelper helper; + + res->report_id = HID_REPORT_ID; + helper.status = status; + helper.buf = (guint8 *)res; + helper.bufsz = sizeof(*res); + if (!fu_device_retry(FU_DEVICE(self), + fu_nordic_hid_cfg_channel_receive_cb, + FU_NORDIC_HID_CFG_CHANNEL_RETRIES, + &helper, + error)) { + g_prefix_error(error, "Failed on receive: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_add_peers(FuNordicHidCfgChannel *self, GError **error) +{ + guint cnt = 0; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + g_autoptr(GError) error_local = NULL; + + if (self->peer_id != 0) + return TRUE; + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + NULL, + NULL, + CONFIG_STATUS_INDEX_PEERS, + NULL, + 0, + error)) + return FALSE; + if (fu_nordic_hid_cfg_channel_cmd_receive(self, + CONFIG_STATUS_DISCONNECTED, + res, + &error_local)) { + /* no peers */ + return TRUE; + } + + /* Peers available */ + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + while (cnt++ <= 0xFF) { + g_autoptr(FuNordicHidCfgChannel) peer = NULL; + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + NULL, + NULL, + CONFIG_STATUS_GET_PEER, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + /* end of the list */ + if (res->data[8] == INVALID_PEER_ID) + return TRUE; + + if (g_getenv("FWUPD_NORDIC_HID_VERBOSE") != NULL) + g_debug("detected peer: 0x%02x", res->data[8]); + + peer = fu_nordic_hid_cfg_channel_new(res->data[8]); + /* prohibit to close close parent's communication descriptor */ + fu_device_add_internal_flag(FU_DEVICE(peer), + FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + /* probe&setup are the part of adding child */ + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(peer)); + } + + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE, "too many peers detected"); + return FALSE; +} + +static gboolean +fu_nordic_hid_cfg_channel_get_board_name(FuNordicHidCfgChannel *self, GError **error) +{ + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + NULL, + NULL, + CONFIG_STATUS_GET_BOARD_NAME, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + self->board_name = fu_strsafe((const gchar *)res->data, res->data_len); + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_get_bl_name(FuNordicHidCfgChannel *self, GError **error) +{ + guint8 event_id = 0; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + /* query for the bootloader name if the board support it */ + if (fu_nordic_hid_cfg_channel_get_event_id(self, "dfu", "module_variant", &event_id)) { + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + "dfu", + "module_variant", + CONFIG_STATUS_FETCH, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + /* check if not set via quirk */ + if (self->bl_name != NULL && + strncmp(self->bl_name, (const char *)res->data, res->data_len) != 0) { + g_set_error( + error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "bootloader in qiurk file is '%s' while the board is supporting '%s'", + self->bl_name, + g_strndup((const gchar *)res->data, res->data_len)); + return FALSE; + } + self->bl_name = fu_strsafe((const gchar *)res->data, res->data_len); + } else if (g_getenv("FWUPD_NORDIC_HID_VERBOSE") != NULL) { + g_debug("the board have no support of bootloader runtime detection"); + } + + if (self->bl_name == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "the bootloader is not detected nor set via quirk"); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* + * NOTE: + * For devices connected directly to the host, + * hw_id = HID_UNIQ = logical_id. + */ +static gboolean +fu_nordic_hid_cfg_channel_get_hwid(FuNordicHidCfgChannel *self, GError **error) +{ + guint8 hw_id[HWID_LEN] = {0x0}; + g_autofree gchar *physical_id = NULL; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + NULL, + NULL, + CONFIG_STATUS_GET_HWID, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + if (!fu_memcpy_safe(hw_id, HWID_LEN, 0, res->data, REPORT_DATA_MAX_LEN, 0, HWID_LEN, error)) + return FALSE; + + /* allows to detect the single device connected via several interfaces */ + physical_id = g_strdup_printf("%s-%02x%02x%02x%02x%02x%02x%02x%02x-%s", + self->board_name, + hw_id[0], + hw_id[1], + hw_id[2], + hw_id[3], + hw_id[4], + hw_id[5], + hw_id[6], + hw_id[7], + self->bl_name); + fu_device_set_physical_id(FU_DEVICE(self), physical_id); + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_load_module_opts(FuNordicHidCfgChannel *self, + FuNordicCfgChannelModule *mod, + GError **error) +{ + for (guint8 i = 0; i < 0xFF; i++) { + FuNordicCfgChannelModuleOption *opt = NULL; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + if (!fu_nordic_hid_cfg_channel_cmd_send_by_id(self, + mod->idx << 4, + CONFIG_STATUS_FETCH, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + /* res->data: option name */ + if (res->data[0] == END_OF_TRANSFER_CHAR) + break; + opt = g_new0(FuNordicCfgChannelModuleOption, 1); + opt->name = fu_strsafe((const gchar *)res->data, res->data_len); + opt->idx = i; + g_ptr_array_add(mod->options, opt); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_load_module_info(FuNordicHidCfgChannel *self, + guint8 module_idx, + GError **error) +{ + g_autoptr(FuNordicCfgChannelModule) mod = g_new0(FuNordicCfgChannelModule, 1); + + mod->idx = module_idx; + mod->options = g_ptr_array_new_with_free_func( + (GDestroyNotify)fu_nordic_hid_cfg_channel_module_option_free); + if (!fu_nordic_hid_cfg_channel_load_module_opts(self, mod, error)) + return FALSE; + /* module description is the 1st loaded option */ + if (mod->options->len > 0) { + FuNordicCfgChannelModuleOption *opt = g_ptr_array_index(mod->options, 0); + mod->name = g_strdup(opt->name); + if (!g_ptr_array_remove_index(mod->options, 0)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot remove option"); + return FALSE; + } + } + + /* success */ + g_ptr_array_add(self->modules, g_steal_pointer(&mod)); + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_get_modinfo(FuNordicHidCfgChannel *self, GError **error) +{ + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + NULL, + NULL, + CONFIG_STATUS_GET_MAX_MOD_ID, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + /* res->data[0]: maximum module idx */ + for (guint i = 0; i <= res->data[0]; i++) { + if (!fu_nordic_hid_cfg_channel_load_module_info(self, i, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_dfu_fwinfo(FuNordicHidCfgChannel *self, GError **error) +{ + guint16 ver_rev; + guint32 ver_build_nr; + g_autofree gchar *version = NULL; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + "dfu", + "fwinfo", + CONFIG_STATUS_FETCH, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + /* parsing fwinfo answer */ + /* TODO: add banks amount into quirk */ + if (res->data[0] > 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid flash area returned by device"); + return FALSE; + } + /* set the target flash ID area */ + self->flash_area_id = res->data[0] ^ 1; + /* always use the bank 0 for MCUBOOT bootloader */ + if (g_strcmp0(self->bl_name, "MCUBOOT") == 0) + self->flash_area_id = 0; + + if (!fu_memread_uint32_safe(res->data, + REPORT_SIZE, + 0x01, + &self->flashed_image_len, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(res->data, REPORT_SIZE, 0x07, &ver_rev, G_LITTLE_ENDIAN, error)) + return FALSE; + if (!fu_memread_uint32_safe(res->data, + REPORT_SIZE, + 0x09, + &ver_build_nr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + version = g_strdup_printf("%u.%u.%u.%u", res->data[4], res->data[5], ver_rev, ver_build_nr); + fu_device_set_version(FU_DEVICE(self), version); + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_dfu_reboot(FuNordicHidCfgChannel *self, GError **error) +{ + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + "dfu", + "reboot", + CONFIG_STATUS_FETCH, + NULL, + 0, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + if (res->data_len != 1 || res->data[0] != 0x01) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "reboot data was invalid"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_dfu_sync_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(device); + FuNordicCfgChannelRcvHelper *args = (FuNordicCfgChannelRcvHelper *)user_data; + g_autoptr(FuNordicCfgChannelMsg) recv_msg = g_new0(FuNordicCfgChannelMsg, 1); + + /* allow to sync buffer more precisely and without annoying messages + * it may take some time and depending on device workload */ + for (gint i = 1; i < 30; i++) { + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + "dfu", + "sync", + CONFIG_STATUS_FETCH, + NULL, + 0, + error)) + return FALSE; + + recv_msg->report_id = HID_REPORT_ID; + g_usleep(i * 5000); + if (!fu_nordic_hid_cfg_channel_receive(self, + (guint8 *)recv_msg, + sizeof(*recv_msg), + error)) { + return FALSE; + } + if (recv_msg->data_len != 0x0F) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "incorrect length of reply"); + return FALSE; + } + if (recv_msg->data[0] == DFU_STATE_INACTIVE || + recv_msg->data[0] == DFU_STATE_ACTIVE) { + break; + } + } + + if (recv_msg->data[0] != args->status) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "sync received status: 0x%02x, expected: 0x%02x", + recv_msg->data[0], + args->status); + return FALSE; + } + + return fu_memcpy_safe(args->buf, + args->bufsz, + 0, + (guint8 *)recv_msg, + sizeof(*recv_msg), + 0, + sizeof(*recv_msg), + error); +} + +static gboolean +fu_nordic_hid_cfg_channel_dfu_sync(FuNordicHidCfgChannel *self, + FuNordicCfgChannelDfuInfo *dfu_info, + guint8 expecting_state, + GError **error) +{ + FuNordicCfgChannelRcvHelper helper; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + helper.status = expecting_state; + helper.buf = (guint8 *)res; + helper.bufsz = sizeof(*res); + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_nordic_hid_cfg_channel_dfu_sync_cb, + FU_NORDIC_HID_CFG_CHANNEL_RETRIES, + FU_NORDIC_HID_CFG_CHANNEL_DFU_RETRY_DELAY, + &helper, + error)) { + g_prefix_error(error, "failed on dfu sync: "); + return FALSE; + } + dfu_info->dfu_state = res->data[0]; + if (!fu_memread_uint32_safe(res->data, + REPORT_SIZE, + 0x01, + &dfu_info->img_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(res->data, + REPORT_SIZE, + 0x05, + &dfu_info->img_csum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(res->data, + REPORT_SIZE, + 0x09, + &dfu_info->offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(res->data, + REPORT_SIZE, + 0x0D, + &dfu_info->sync_buffer_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_dfu_start(FuNordicHidCfgChannel *self, + gsize img_length, + guint32 img_crc, + guint32 offset, + GError **error) +{ + guint8 data[REPORT_DATA_MAX_LEN] = {0}; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + /* sanity check */ + if (img_length > G_MAXUINT32) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "payload was too large"); + return FALSE; + } + + if (!fu_memwrite_uint32_safe(data, + REPORT_DATA_MAX_LEN, + 0x00, + (guint32)img_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memwrite_uint32_safe(data, + REPORT_DATA_MAX_LEN, + 0x04, + img_crc, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memwrite_uint32_safe(data, + REPORT_DATA_MAX_LEN, + 0x08, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + "dfu", + "start", + CONFIG_STATUS_SET, + data, + 0x0C, + error)) + return FALSE; + return fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error); +} + +static gboolean +fu_nordic_hid_cfg_channel_probe(FuDevice *device, GError **error) +{ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static gboolean +fu_nordic_hid_cfg_channel_setup(FuDevice *device, GError **error) +{ + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(device); + + /* get the board name */ + if (!fu_nordic_hid_cfg_channel_get_board_name(self, error)) + return FALSE; + /* detect available modules first */ + if (!fu_nordic_hid_cfg_channel_get_modinfo(self, error)) + return FALSE; + /* detect bootloader type */ + if (!fu_nordic_hid_cfg_channel_get_bl_name(self, error)) + return FALSE; + /* set the physical id based on name, HW id and bootloader type of the board + * to detect if the device is connected via several interfaces */ + if (!fu_nordic_hid_cfg_channel_get_hwid(self, error)) + return FALSE; + /* get device info and version */ + if (!fu_nordic_hid_cfg_channel_dfu_fwinfo(self, error)) + return FALSE; + /* check if any peer is connected via this device */ + if (!fu_nordic_hid_cfg_channel_add_peers(self, error)) + return FALSE; + + /* generate the custom visible name for the device if absent */ + if (fu_device_get_name(device) == NULL) { + const gchar *physical_id = NULL; + physical_id = fu_device_get_physical_id(device); + fu_device_set_name(device, physical_id); + } + + /* generate IDs */ + fu_device_add_instance_strsafe(device, "BOARD", self->board_name); + fu_device_add_instance_strsafe(device, "BL", self->bl_name); + return fu_device_build_instance_id(device, + error, + "HIDRAW", + "VEN", + "DEV", + "BOARD", + "BL", + NULL); +} + +static void +fu_nordic_hid_cfg_channel_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_nordic_hid_cfg_channel_module_to_string(FuNordicCfgChannelModule *mod, guint idt, GString *str) +{ + for (guint i = 0; i < mod->options->len; i++) { + FuNordicCfgChannelModuleOption *opt = g_ptr_array_index(mod->options, i); + g_autofree gchar *title = g_strdup_printf("Option%02x", i); + fu_string_append(str, idt, title, opt->name); + } +} + +static void +fu_nordic_hid_cfg_channel_to_string(FuDevice *device, guint idt, GString *str) +{ + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(device); + fu_string_append(str, idt, "BoardName", self->board_name); + fu_string_append(str, idt, "Bootloader", self->bl_name); + fu_string_append_kx(str, idt, "FlashAreaId", self->flash_area_id); + fu_string_append_kx(str, idt, "FlashedImageLen", self->flashed_image_len); + fu_string_append_kx(str, idt, "PeerId", self->peer_id); + for (guint i = 0; i < self->modules->len; i++) { + FuNordicCfgChannelModule *mod = g_ptr_array_index(self->modules, i); + g_autofree gchar *title = g_strdup_printf("Module%02x", i); + fu_string_append(str, idt, title, mod->name); + fu_nordic_hid_cfg_channel_module_to_string(mod, idt + 1, str); + } +} + +static gboolean +fu_nordic_hid_cfg_channel_write_firmware_chunk(FuNordicHidCfgChannel *self, + FuChunk *chk, + gboolean is_last, + GError **error) +{ + guint32 chunk_len; + guint32 offset = 0; + guint8 sync_state = DFU_STATE_ACTIVE; + g_autoptr(FuNordicCfgChannelDfuInfo) dfu_info = g_new0(FuNordicCfgChannelDfuInfo, 1); + + chunk_len = fu_chunk_get_data_sz(chk); + while (offset < chunk_len) { + guint8 data_len; + guint8 data[REPORT_DATA_MAX_LEN] = {0}; + g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + + data_len = ((offset + REPORT_DATA_MAX_LEN) < chunk_len) + ? REPORT_DATA_MAX_LEN + : (guint8)(chunk_len - offset); + + if (!fu_memcpy_safe(data, + REPORT_DATA_MAX_LEN, + 0, + fu_chunk_get_data(chk), + chunk_len, + offset, + data_len, + error)) { + return FALSE; + } + + if (!fu_nordic_hid_cfg_channel_cmd_send(self, + "dfu", + "data", + CONFIG_STATUS_SET, + data, + data_len, + error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) + return FALSE; + + offset += data_len; + } + + /* sync should return inactive for the last chunk */ + if (is_last) + sync_state = DFU_STATE_INACTIVE; + return fu_nordic_hid_cfg_channel_dfu_sync(self, dfu_info, sync_state, error); +} + +static gboolean +fu_nordic_hid_cfg_channel_write_firmware_blob(FuNordicHidCfgChannel *self, + GBytes *blob, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuNordicCfgChannelDfuInfo) dfu_info = g_new0(FuNordicCfgChannelDfuInfo, 1); + g_autoptr(GPtrArray) chunks = NULL; + + if (!fu_nordic_hid_cfg_channel_dfu_sync(self, dfu_info, DFU_STATE_ACTIVE, error)) + return FALSE; + + chunks = fu_chunk_array_new_from_bytes(blob, 0, 0, dfu_info->sync_buffer_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + gboolean is_last = (i == chunks->len - 1); + if (!fu_nordic_hid_cfg_channel_write_firmware_chunk(self, chk, is_last, error)) { + g_prefix_error(error, "chunk %u: ", fu_chunk_get_idx(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(device); + guint32 checksum; + g_autofree gchar *csum_str = NULL; + g_autofree gchar *image_id = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuNordicCfgChannelDfuInfo) dfu_info = g_new0(FuNordicCfgChannelDfuInfo, 1); + + /* select correct firmware per target board, bootloader and bank */ + image_id = + g_strdup_printf("%s_%s_bank%01u", self->board_name, self->bl_name, self->flash_area_id); + firmware = fu_firmware_get_image_by_id(firmware, image_id, error); + if (firmware == NULL) + return FALSE; + + /* explicitly request a custom checksum calculation */ + csum_str = fu_firmware_get_checksum(firmware, -1, error); + if (csum_str == NULL) + return FALSE; + /* expecting checksum string in hex */ + checksum = g_ascii_strtoull(csum_str, NULL, 16); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + + /* TODO: check if there is unfinished operation before? */ + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return FALSE; + if (!fu_nordic_hid_cfg_channel_dfu_sync(self, dfu_info, DFU_STATE_INACTIVE, error)) + return FALSE; + if (!fu_nordic_hid_cfg_channel_dfu_start(self, + g_bytes_get_size(blob), + checksum, + 0x0 /* offset */, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + if (!fu_nordic_hid_cfg_channel_write_firmware_blob(self, + blob, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* attach */ + if (!fu_nordic_hid_cfg_channel_dfu_reboot(self, error)) + return FALSE; + fu_progress_step_done(progress); + return TRUE; +} + +static gboolean +fu_nordic_hid_cfg_channel_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(device); + + if (g_strcmp0(key, "NordicHidBootloader") == 0) { + if (g_strcmp0(value, "B0") == 0) + self->bl_name = g_strdup("B0"); + else if (g_strcmp0(value, "MCUBOOT") == 0) + self->bl_name = g_strdup("MCUBOOT"); + else { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "must be 'B0' or 'MCUBOOT'"); + return FALSE; + } + 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_nordic_hid_cfg_channel_finalize(GObject *object) +{ + FuNordicHidCfgChannel *self = FU_NORDIC_HID_CFG_CHANNEL(object); + g_free(self->board_name); + g_free(self->bl_name); + g_ptr_array_unref(self->modules); + G_OBJECT_CLASS(fu_nordic_hid_cfg_channel_parent_class)->finalize(object); +} + +static void +fu_nordic_hid_cfg_channel_class_init(FuNordicHidCfgChannelClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + klass_device->probe = fu_nordic_hid_cfg_channel_probe; + klass_device->set_progress = fu_nordic_hid_cfg_channel_set_progress; + klass_device->set_quirk_kv = fu_nordic_hid_cfg_channel_set_quirk_kv; + klass_device->setup = fu_nordic_hid_cfg_channel_setup; + klass_device->to_string = fu_nordic_hid_cfg_channel_to_string; + klass_device->write_firmware = fu_nordic_hid_cfg_channel_write_firmware; + object_class->finalize = fu_nordic_hid_cfg_channel_finalize; +} + +static void +fu_nordic_hid_cfg_channel_init(FuNordicHidCfgChannel *self) +{ + self->modules = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_nordic_hid_cfg_channel_module_free); + + fu_device_set_vendor(FU_DEVICE(self), "Nordic"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_add_protocol(FU_DEVICE(self), "com.nordic.hidcfgchannel"); + fu_device_retry_set_delay(FU_DEVICE(self), FU_NORDIC_HID_CFG_CHANNEL_RETRY_DELAY); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_NORDIC_HID_ARCHIVE); +} + +FuNordicHidCfgChannel * +fu_nordic_hid_cfg_channel_new(guint8 id) +{ + FuNordicHidCfgChannel *self = g_object_new(FU_TYPE_NORDIC_HID_CFG_CHANNEL, NULL); + self->peer_id = id; + return self; +} diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-cfg-channel.h b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-cfg-channel.h new file mode 100644 index 0000000000000000000000000000000000000000..cd0fe00dbb4b4981e6887b162d89bf4aae0a4ddb --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-cfg-channel.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_NORDIC_HID_CFG_CHANNEL (fu_nordic_hid_cfg_channel_get_type()) +G_DECLARE_FINAL_TYPE(FuNordicHidCfgChannel, + fu_nordic_hid_cfg_channel, + FU, + NORDIC_HID_CFG_CHANNEL, + FuUdevDevice) + +FuNordicHidCfgChannel * +fu_nordic_hid_cfg_channel_new(guint8 id); diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c new file mode 100644 index 0000000000000000000000000000000000000000..22854135505e2f23ccb3669458b33e072132789d --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nordic-hid-firmware-b0.h" + +#define UPDATE_IMAGE_MAGIC_COMMON 0x281ee6de +#define UPDATE_IMAGE_MAGIC_FWINFO 0x8fcebb4c +#define UPDATE_IMAGE_MAGIC_NRF52 0x00003402 +#define UPDATE_IMAGE_MAGIC_NRF53 0x00003502 + +struct _FuNordicHidFirmwareB0 { + FuNordicHidFirmwareClass parent_instance; +}; + +G_DEFINE_TYPE(FuNordicHidFirmwareB0, fu_nordic_hid_firmware_b0, FU_TYPE_NORDIC_HID_FIRMWARE) + +static GBytes * +fu_nordic_hid_firmware_b0_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + fu_byte_array_append_uint32(buf, UPDATE_IMAGE_MAGIC_COMMON, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, UPDATE_IMAGE_MAGIC_FWINFO, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, UPDATE_IMAGE_MAGIC_NRF52, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x00, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x00, G_LITTLE_ENDIAN); + /* version */ + fu_byte_array_append_uint32(buf, 0x63, G_LITTLE_ENDIAN); + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_nordic_hid_firmware_b0_read_fwinfo(FuFirmware *firmware, + guint8 const *buf, + gsize bufsz, + GError **error) +{ + guint32 magic_common; + guint32 magic_fwinfo; + guint32 magic_compat; + guint32 offset; + guint32 hdr_offset[5] = {0x0000, 0x0200, 0x400, 0x800, 0x1000}; + guint8 ver_major = 0; + guint8 ver_minor = 0; + guint16 ver_rev = 0; + guint32 ver_build_nr = 0; + g_autofree gchar *version = NULL; + + /* find correct offset to fwinfo */ + for (guint32 i = 0; i < G_N_ELEMENTS(hdr_offset); i++) { + offset = hdr_offset[i]; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset, + &magic_common, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x04, + &magic_fwinfo, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x08, + &magic_compat, + G_LITTLE_ENDIAN, + error)) + return FALSE; + /* version */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x14, + &ver_build_nr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (magic_common != UPDATE_IMAGE_MAGIC_COMMON || + magic_fwinfo != UPDATE_IMAGE_MAGIC_FWINFO) + continue; + switch (magic_compat) { + case UPDATE_IMAGE_MAGIC_NRF52: + case UPDATE_IMAGE_MAGIC_NRF53: + /* currently only the build number is saved into the image */ + version = g_strdup_printf("%u.%u.%u.%u", + ver_major, + ver_minor, + ver_rev, + ver_build_nr); + fu_firmware_set_version(firmware, version); + return TRUE; + default: + break; + } + } + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to validate the update binary"); + return FALSE; +} + +static gboolean +fu_nordic_hid_firmware_b0_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + + if (!FU_FIRMWARE_CLASS(fu_nordic_hid_firmware_b0_parent_class) + ->parse(firmware, fw, offset, flags, error)) + return FALSE; + + buf = g_bytes_get_data(fw, &bufsz); + if (buf == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to get the image binary"); + return FALSE; + } + + return fu_nordic_hid_firmware_b0_read_fwinfo(firmware, buf, bufsz, error); +} + +static void +fu_nordic_hid_firmware_b0_init(FuNordicHidFirmwareB0 *self) +{ +} + +static void +fu_nordic_hid_firmware_b0_class_init(FuNordicHidFirmwareB0Class *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_nordic_hid_firmware_b0_parse; + klass_firmware->write = fu_nordic_hid_firmware_b0_write; +} + +FuFirmware * +fu_nordic_hid_firmware_b0_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_NORDIC_HID_FIRMWARE_B0, NULL)); +} diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-b0.h b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-b0.h new file mode 100644 index 0000000000000000000000000000000000000000..54b91b990863d9b2a0818b6dbbaa4f727fac8a6e --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-b0.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-nordic-hid-firmware.h" + +#define FU_TYPE_NORDIC_HID_FIRMWARE_B0 (fu_nordic_hid_firmware_b0_get_type()) +G_DECLARE_FINAL_TYPE(FuNordicHidFirmwareB0, + fu_nordic_hid_firmware_b0, + FU, + NORDIC_HID_FIRMWARE_B0, + FuNordicHidFirmware) + +FuFirmware * +fu_nordic_hid_firmware_b0_new(void); diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c new file mode 100644 index 0000000000000000000000000000000000000000..3bc48c7e63d5a02922d928f633a365f200e04ec8 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nordic-hid-firmware-mcuboot.h" + +#define IMAGE_MAGIC 0x96f3b83d +#define IMAGE_TLV_INFO_MAGIC 0x6907 +#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908 + +struct _FuNordicHidFirmwareMcuboot { + FuNordicHidFirmwareClass parent_instance; +}; + +G_DEFINE_TYPE(FuNordicHidFirmwareMcuboot, + fu_nordic_hid_firmware_mcuboot, + FU_TYPE_NORDIC_HID_FIRMWARE) + +static GBytes * +fu_nordic_hid_firmware_mcuboot_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = fu_firmware_get_bytes_with_patches(firmware, error); + + if (blob == NULL) + return NULL; + + /* https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/mcuboot/design.html#image-format + */ + fu_byte_array_append_uint32(buf, IMAGE_MAGIC, G_LITTLE_ENDIAN); + /* load_addr */ + fu_byte_array_append_uint32(buf, 0x00, G_LITTLE_ENDIAN); + /* hdr_size */ + fu_byte_array_append_uint16(buf, 0x20, G_LITTLE_ENDIAN); + /* protect_tlv_size */ + fu_byte_array_append_uint16(buf, 0x00, G_LITTLE_ENDIAN); + /* img_size */ + fu_byte_array_append_uint32(buf, (guint32)g_bytes_get_size(blob), G_LITTLE_ENDIAN); + /* flags */ + fu_byte_array_append_uint32(buf, 0x00, G_LITTLE_ENDIAN); + /* version */ + fu_byte_array_append_uint8(buf, 0x01); + fu_byte_array_append_uint8(buf, 0x02); + fu_byte_array_append_uint16(buf, 0x03, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x63, G_LITTLE_ENDIAN); + /* pad */ + fu_byte_array_append_uint32(buf, 0xffffffff, G_LITTLE_ENDIAN); + /* payload */ + fu_byte_array_append_bytes(buf, blob); + /* TLV magic and total */ + fu_byte_array_append_uint16(buf, IMAGE_TLV_INFO_MAGIC, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(buf, 0x00, G_LITTLE_ENDIAN); + + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +/* simple validation of the image */ +static gboolean +fu_nordic_hid_firmware_mcuboot_validate(FuFirmware *firmware, + guint8 const *buf, + gsize bufsz, + GError **error) +{ + guint32 magic; + guint16 hdr_size; + guint32 img_size; + guint8 ver_major; + guint8 ver_minor; + guint16 ver_rev; + guint32 ver_build_nr; + guint16 magic_tlv; + g_autofree gchar *version = NULL; + + if (!fu_memread_uint32_safe(buf, bufsz, 0, &magic, G_LITTLE_ENDIAN, error)) + return FALSE; + if (magic != IMAGE_MAGIC) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect image magic"); + return FALSE; + } + /* ignore load_addr */ + if (!fu_memread_uint16_safe(buf, bufsz, 8, &hdr_size, G_LITTLE_ENDIAN, error)) + return FALSE; + /* ignore protect_tlv_size */ + if (!fu_memread_uint32_safe(buf, bufsz, 12, &img_size, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* ignore TLVs themselves + * https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/mcuboot/design.html#protected-tlvs + * check the magic values only */ + if (!fu_memread_uint16_safe(buf, + bufsz, + hdr_size + img_size, + &magic_tlv, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (magic_tlv != IMAGE_TLV_INFO_MAGIC && magic_tlv != IMAGE_TLV_PROT_INFO_MAGIC) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect TLV info magic"); + return FALSE; + } + + /* version */ + if (!fu_memread_uint8_safe(buf, bufsz, 0x14, &ver_major, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, 0x15, &ver_minor, error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, bufsz, 0x16, &ver_rev, G_LITTLE_ENDIAN, error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, bufsz, 0x18, &ver_build_nr, G_LITTLE_ENDIAN, error)) + return FALSE; + version = g_strdup_printf("%u.%u.%u.%u", ver_major, ver_minor, ver_rev, ver_build_nr); + + fu_firmware_set_version(firmware, version); + + return TRUE; +} + +static gboolean +fu_nordic_hid_firmware_mcuboot_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + + if (!FU_FIRMWARE_CLASS(fu_nordic_hid_firmware_mcuboot_parent_class) + ->parse(firmware, fw, offset, flags, error)) + return FALSE; + + buf = g_bytes_get_data(fw, &bufsz); + if (buf == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to get the image binary"); + return FALSE; + } + + return fu_nordic_hid_firmware_mcuboot_validate(firmware, buf, bufsz, error); +} + +static void +fu_nordic_hid_firmware_mcuboot_init(FuNordicHidFirmwareMcuboot *self) +{ +} + +static void +fu_nordic_hid_firmware_mcuboot_class_init(FuNordicHidFirmwareMcubootClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_nordic_hid_firmware_mcuboot_parse; + klass_firmware->write = fu_nordic_hid_firmware_mcuboot_write; +} + +FuFirmware * +fu_nordic_hid_firmware_mcuboot_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_NORDIC_HID_FIRMWARE_MCUBOOT, NULL)); +} diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.h b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.h new file mode 100644 index 0000000000000000000000000000000000000000..7d282fcc6cd91d0726e6d3032172634406a86052 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-nordic-hid-firmware.h" + +#define FU_TYPE_NORDIC_HID_FIRMWARE_MCUBOOT (fu_nordic_hid_firmware_mcuboot_get_type()) +G_DECLARE_FINAL_TYPE(FuNordicHidFirmwareMcuboot, + fu_nordic_hid_firmware_mcuboot, + FU, + NORDIC_HID_FIRMWARE_MCUBOOT, + FuNordicHidFirmware) + +FuFirmware * +fu_nordic_hid_firmware_mcuboot_new(void); diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware.c b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..077b98790e9cc912839d2137bd010a784132ad5e --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-nordic-hid-firmware.h" + +typedef struct { + guint32 crc32; +} FuNordicHidFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuNordicHidFirmware, fu_nordic_hid_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_nordic_hid_firmware_get_instance_private(o)) + +static void +fu_nordic_hid_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuNordicHidFirmware *self = FU_NORDIC_HID_FIRMWARE(firmware); + FuNordicHidFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "crc32", priv->crc32); +} + +static gchar * +fu_nordic_hid_firmware_get_checksum(FuFirmware *firmware, GChecksumType csum_kind, GError **error) +{ + FuNordicHidFirmware *self = FU_NORDIC_HID_FIRMWARE(firmware); + FuNordicHidFirmwarePrivate *priv = GET_PRIVATE(self); + if (!fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "unable to calculate the checksum of the update binary"); + return NULL; + } + return g_strdup_printf("%x", priv->crc32); +} + +static guint32 +fu_nordic_hid_firmware_crc32(const guint8 *buf, gsize bufsz) +{ + guint crc32 = 0x01; + /* maybe skipped "^" step in fu_crc32_full()? + * according https://github.com/madler/zlib/blob/master/crc32.c#L225 */ + crc32 ^= 0xFFFFFFFFUL; + return fu_crc32_full(buf, bufsz, crc32, 0xEDB88320); +} + +static gboolean +fu_nordic_hid_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuNordicHidFirmware *self = FU_NORDIC_HID_FIRMWARE(firmware); + FuNordicHidFirmwarePrivate *priv = GET_PRIVATE(self); + const guint8 *buf; + gsize bufsz = 0; + + buf = g_bytes_get_data(fw, &bufsz); + if (buf == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to get the image binary"); + return FALSE; + } + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); + priv->crc32 = fu_nordic_hid_firmware_crc32(buf, bufsz); + + /* success */ + return TRUE; +} + +static void +fu_nordic_hid_firmware_init(FuNordicHidFirmware *self) +{ +} + +static void +fu_nordic_hid_firmware_class_init(FuNordicHidFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + + klass_firmware->export = fu_nordic_hid_firmware_export; + klass_firmware->get_checksum = fu_nordic_hid_firmware_get_checksum; + klass_firmware->parse = fu_nordic_hid_firmware_parse; +} diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware.h b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..d36ba4b1f4635d60c42f9cb0c4da3147bce30020 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-firmware.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_NORDIC_HID_FIRMWARE (fu_nordic_hid_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuNordicHidFirmware, + fu_nordic_hid_firmware, + FU, + NORDIC_HID_FIRMWARE, + FuFirmware) + +struct _FuNordicHidFirmwareClass { + FuFirmwareClass parent_class; +}; diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-plugin.c b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..de87c2a76bf5ad157354f2bdb0c77b70ec9546fd --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-plugin.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nordic-hid-archive.h" +#include "fu-nordic-hid-cfg-channel.h" +#include "fu-nordic-hid-firmware-b0.h" +#include "fu-nordic-hid-firmware-mcuboot.h" +#include "fu-nordic-hid-plugin.h" + +struct _FuNordicHidPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuNordicHidPlugin, fu_nordic_hid_plugin, FU_TYPE_PLUGIN) + +static void +fu_nordic_hid_plugin_init(FuNordicHidPlugin *self) +{ +} + +static void +fu_nordic_hid_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "NordicHidBootloader"); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_NORDIC_HID_CFG_CHANNEL); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_NORDIC_HID_ARCHIVE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_NORDIC_HID_FIRMWARE_B0); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_NORDIC_HID_FIRMWARE_MCUBOOT); +} + +static void +fu_nordic_hid_plugin_class_init(FuNordicHidPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_nordic_hid_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-plugin.h b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..9aabaecea0d248c59ef5b15a80e2ac77eeaaf934 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/fu-nordic-hid-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuNordicHidPlugin, fu_nordic_hid_plugin, FU, NORDIC_HID_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/nordic-hid/meson.build b/fwupd-1.8.6/plugins/nordic-hid/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..b33a615519e404fd2eedbd523522f8e71a4266c1 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/meson.build @@ -0,0 +1,19 @@ +if gudev.found() and gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginNordicHid"'] + +plugin_quirks += files('nordic-hid.quirk') +plugin_builtins += static_library('fu_plugin_nordic_hid', + sources: [ + 'fu-nordic-hid-plugin.c', + 'fu-nordic-hid-cfg-channel.c', + 'fu-nordic-hid-firmware.c', + 'fu-nordic-hid-firmware-b0.c', + 'fu-nordic-hid-firmware-mcuboot.c', + 'fu-nordic-hid-archive.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/nordic-hid/nordic-hid.quirk b/fwupd-1.8.6/plugins/nordic-hid/nordic-hid.quirk new file mode 100644 index 0000000000000000000000000000000000000000..1264c4d2e0bd3989df0da3794317c23aac036b94 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/nordic-hid.quirk @@ -0,0 +1,12 @@ +# Mouse nRF52 Desktop +[HIDRAW\VEN_1915&DEV_52DE] +Plugin = nordic_hid +GType = FuNordicHidCfgChannel +#NordicHidBootloader = B0 +#NordicHidBootloader = MCUBOOT + +# Nordic Semiconductor ASA Dongle nRF52 Desktop +[HIDRAW\VEN_1915&DEV_52DC] +Plugin = nordic_hid +GType = FuNordicHidCfgChannel +NordicHidBootloader = B0 diff --git a/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-b0.bin b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-b0.bin new file mode 100644 index 0000000000000000000000000000000000000000..4fcfe6c6f867e7e004ddcd5213aed8ac5652ecb5 Binary files /dev/null and b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-b0.bin differ diff --git a/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-b0.builder.xml b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-b0.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..c3bebe324424f4489f0e144c5f2b8522d501f214 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-b0.builder.xml @@ -0,0 +1,3 @@ + + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-mcuboot.bin b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-mcuboot.bin new file mode 100644 index 0000000000000000000000000000000000000000..528c4b779a952a1da9b6a7db603e1e2c3184b683 Binary files /dev/null and b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-mcuboot.bin differ diff --git a/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-mcuboot.builder.xml b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-mcuboot.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..22a4c18cb829624b821596c8a7420005bc83f208 --- /dev/null +++ b/fwupd-1.8.6/plugins/nordic-hid/tests/nordic-hid-mcuboot.builder.xml @@ -0,0 +1,3 @@ + + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/nvme/README.md b/fwupd-1.8.6/plugins/nvme/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ff951e24876301ab4f23aa1ba16ab704e61de6d2 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/README.md @@ -0,0 +1,65 @@ +# 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. + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, but it is +only activated when the system is either restarted or in some cases shutdown. + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### NvmeBlockSize + +The block size used for NVMe writes + +Since: 1.1.3 + +### Flags + +* `force-align` if image should be padded, since 1.2.4 + +## Vendor ID Security + +The vendor ID is set from the udev vendor, for example set to `NVME:0x1179` + +## External Interface Access + +This plugin requires ioctl `NVME_IOCTL_ADMIN_CMD` access. diff --git a/fwupd-1.8.6/plugins/nvme/fu-nvme-common.c b/fwupd-1.8.6/plugins/nvme/fu-nvme-common.c new file mode 100644 index 0000000000000000000000000000000000000000..6a33d21197cf0c721e06d128779de26ed5c8e794 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-nvme-common.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nvme-common.h" + +const gchar * +fu_nvme_status_to_string(guint32 status) +{ + switch (status) { + case NVME_SC_SUCCESS: + return "Command completed successfully"; + case NVME_SC_INVALID_OPCODE: + return "Associated command opcode field is not valid"; + case NVME_SC_INVALID_FIELD: + return "Unsupported value in a defined field"; + case NVME_SC_CMDID_CONFLICT: + return "Command identifier is already in use"; + case NVME_SC_DATA_XFER_ERROR: + return "Error while trying to transfer the data or metadata"; + case NVME_SC_POWER_LOSS: + return "Command aborted due to power loss notification"; + case NVME_SC_INTERNAL: + return "Internal error"; + case NVME_SC_ABORT_REQ: + return "Command Abort request"; + case NVME_SC_ABORT_QUEUE: + return "Delete I/O Submission Queue request"; + case NVME_SC_FUSED_FAIL: + return "Other command in a fused operation failing"; + case NVME_SC_FUSED_MISSING: + return "Missing Fused Command"; + case NVME_SC_INVALID_NS: + return "Namespace or the format of that namespace is invalid"; + case NVME_SC_CMD_SEQ_ERROR: + return "Protocol violation in a multicommand sequence"; + case NVME_SC_SANITIZE_FAILED: + return "No recovery actions has been successfully completed"; + case NVME_SC_SANITIZE_IN_PROGRESS: + return "A sanitize operation is in progress"; + case NVME_SC_LBA_RANGE: + return "LBA exceeds the size of the namespace"; + case NVME_SC_NS_WRITE_PROTECTED: + return "Namespace is write protected by the host"; + case NVME_SC_CAP_EXCEEDED: + return "Capacity of the namespace to be exceeded"; + case NVME_SC_NS_NOT_READY: + return "Namespace is not ready to be accessed"; + case NVME_SC_RESERVATION_CONFLICT: + return "Conflict with a reservation on the accessed namespace"; + case NVME_SC_CQ_INVALID: + return "Completion Queue does not exist"; + case NVME_SC_QID_INVALID: + return "Invalid queue identifier specified"; + case NVME_SC_QUEUE_SIZE: + return "Invalid queue size"; + case NVME_SC_ABORT_LIMIT: + return "Outstanding Abort commands has exceeded the limit"; + case NVME_SC_ABORT_MISSING: + return "Abort command is missing"; + case NVME_SC_ASYNC_LIMIT: + return "Outstanding Async commands has been exceeded"; + case NVME_SC_FIRMWARE_SLOT: + return "Slot is invalid or read only"; + case NVME_SC_FIRMWARE_IMAGE: + return "Image specified for activation is invalid"; + case NVME_SC_INVALID_VECTOR: + return "Creation failed due to an invalid interrupt vector"; + case NVME_SC_INVALID_LOG_PAGE: + return "Log page indicated is invalid"; + case NVME_SC_INVALID_FORMAT: + return "LBA Format specified is not supported"; + case NVME_SC_FW_NEEDS_CONV_RESET: + return "commit was successful, but activation requires reset"; + case NVME_SC_INVALID_QUEUE: + return "Failed to delete the I/O Completion Queue specified"; + case NVME_SC_FEATURE_NOT_SAVEABLE: + return "Feature Identifier does not support a saveable value"; + case NVME_SC_FEATURE_NOT_CHANGEABLE: + return "Feature Identifier is not able to be changed"; + case NVME_SC_FEATURE_NOT_PER_NS: + return "Feature Identifier specified is not namespace specific"; + case NVME_SC_FW_NEEDS_SUBSYS_RESET: + return "Commit was successful, activation requires NVM Subsystem"; + case NVME_SC_FW_NEEDS_RESET: + return "Commit was successful, activation requires a reset"; + case NVME_SC_FW_NEEDS_MAX_TIME: + return "Would exceed the Maximum Time for Firmware Activation"; + case NVME_SC_FW_ACIVATE_PROHIBITED: + return "Image specified is being prohibited from activation"; + case NVME_SC_OVERLAPPING_RANGE: + return "Image has overlapping ranges"; + case NVME_SC_NS_INSUFFICENT_CAP: + return "Requires more free space than is currently available"; + case NVME_SC_NS_ID_UNAVAILABLE: + return "Number of namespaces supported has been exceeded"; + case NVME_SC_NS_ALREADY_ATTACHED: + return "Controller is already attached to the namespace"; + case NVME_SC_NS_IS_PRIVATE: + return "Namespace is private"; + case NVME_SC_NS_NOT_ATTACHED: + return "Controller is not attached to the namespace"; + case NVME_SC_THIN_PROV_NOT_SUPP: + return "Thin provisioning is not supported by the controller"; + case NVME_SC_CTRL_LIST_INVALID: + return "Controller list provided is invalid"; + case NVME_SC_BP_WRITE_PROHIBITED: + return "Trying to modify a Boot Partition while it is locked"; + case NVME_SC_BAD_ATTRIBUTES: + return "Bad attributes"; + case NVME_SC_WRITE_FAULT: + return "Write data could not be committed to the media"; + case NVME_SC_READ_ERROR: + return "Read data could not be recovered from the media"; + case NVME_SC_GUARD_CHECK: + return "End-to-end guard check failure"; + case NVME_SC_APPTAG_CHECK: + return "End-to-end application tag check failure"; + case NVME_SC_REFTAG_CHECK: + return "End-to-end reference tag check failure"; + case NVME_SC_COMPARE_FAILED: + return "Miscompare during a Compare command"; + case NVME_SC_ACCESS_DENIED: + return "Access denied"; + case NVME_SC_UNWRITTEN_BLOCK: + return "Read from an LBA range containing a unwritten block"; + case NVME_SC_ANA_PERSISTENT_LOSS: + return "Namespace is in the ANA Persistent Loss state"; + case NVME_SC_ANA_INACCESSIBLE: + return "Namespace being in the ANA Inaccessible state"; + case NVME_SC_ANA_TRANSITION: + return "Namespace transitioning between Async Access states"; + default: + return "Unknown"; + } +} diff --git a/fwupd-1.8.6/plugins/nvme/fu-nvme-common.h b/fwupd-1.8.6/plugins/nvme/fu-nvme-common.h new file mode 100644 index 0000000000000000000000000000000000000000..ce87f6b1450500ba132937276108f2994980c827 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-nvme-common.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +enum { + /* + * Generic Command Status: + */ + NVME_SC_SUCCESS = 0x0, + NVME_SC_INVALID_OPCODE = 0x1, + NVME_SC_INVALID_FIELD = 0x2, + NVME_SC_CMDID_CONFLICT = 0x3, + NVME_SC_DATA_XFER_ERROR = 0x4, + NVME_SC_POWER_LOSS = 0x5, + NVME_SC_INTERNAL = 0x6, + NVME_SC_ABORT_REQ = 0x7, + NVME_SC_ABORT_QUEUE = 0x8, + NVME_SC_FUSED_FAIL = 0x9, + NVME_SC_FUSED_MISSING = 0xa, + NVME_SC_INVALID_NS = 0xb, + NVME_SC_CMD_SEQ_ERROR = 0xc, + NVME_SC_SGL_INVALID_LAST = 0xd, + NVME_SC_SGL_INVALID_COUNT = 0xe, + NVME_SC_SGL_INVALID_DATA = 0xf, + NVME_SC_SGL_INVALID_METADATA = 0x10, + NVME_SC_SGL_INVALID_TYPE = 0x11, + + NVME_SC_SGL_INVALID_OFFSET = 0x16, + NVME_SC_SGL_INVALID_SUBTYPE = 0x17, + + NVME_SC_SANITIZE_FAILED = 0x1C, + NVME_SC_SANITIZE_IN_PROGRESS = 0x1D, + + NVME_SC_NS_WRITE_PROTECTED = 0x20, + + NVME_SC_LBA_RANGE = 0x80, + NVME_SC_CAP_EXCEEDED = 0x81, + NVME_SC_NS_NOT_READY = 0x82, + NVME_SC_RESERVATION_CONFLICT = 0x83, + + /* + * Command Specific Status: + */ + NVME_SC_CQ_INVALID = 0x100, + NVME_SC_QID_INVALID = 0x101, + NVME_SC_QUEUE_SIZE = 0x102, + NVME_SC_ABORT_LIMIT = 0x103, + NVME_SC_ABORT_MISSING = 0x104, + NVME_SC_ASYNC_LIMIT = 0x105, + NVME_SC_FIRMWARE_SLOT = 0x106, + NVME_SC_FIRMWARE_IMAGE = 0x107, + NVME_SC_INVALID_VECTOR = 0x108, + NVME_SC_INVALID_LOG_PAGE = 0x109, + NVME_SC_INVALID_FORMAT = 0x10a, + NVME_SC_FW_NEEDS_CONV_RESET = 0x10b, + NVME_SC_INVALID_QUEUE = 0x10c, + NVME_SC_FEATURE_NOT_SAVEABLE = 0x10d, + NVME_SC_FEATURE_NOT_CHANGEABLE = 0x10e, + NVME_SC_FEATURE_NOT_PER_NS = 0x10f, + NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x110, + NVME_SC_FW_NEEDS_RESET = 0x111, + NVME_SC_FW_NEEDS_MAX_TIME = 0x112, + NVME_SC_FW_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); diff --git a/fwupd-1.8.6/plugins/nvme/fu-nvme-device.c b/fwupd-1.8.6/plugins/nvme/fu-nvme-device.c new file mode 100644 index 0000000000000000000000000000000000000000..e3fb8517c42d0721e4deb2c93683c58d91e12546 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-nvme-device.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#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; + guint64 write_block_size; +}; + +/** + * FU_NVME_DEVICE_FLAG_FORCE_ALIGN: + * + * Force alignment of the firmware file. + */ +#define FU_NVME_DEVICE_FLAG_FORCE_ALIGN (1 << 0) + +G_DEFINE_TYPE(FuNvmeDevice, fu_nvme_device, FU_TYPE_UDEV_DEVICE) + +#define FU_NVME_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static void +fu_nvme_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuNvmeDevice *self = FU_NVME_DEVICE(device); + FU_DEVICE_CLASS(fu_nvme_device_parent_class)->to_string(device, idt, str); + fu_string_append_ku(str, idt, "PciDepth", 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 = 0; + guint32 err; + + /* submit admin command */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + NVME_IOCTL_ADMIN_CMD, + (guint8 *)cmd, + &rc, + FU_NVME_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to issue admin command 0x%02x: ", cmd->opcode); + 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_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_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_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) + fu_device_set_version(FU_DEVICE(self), sr); + + /* 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; + if (g_getenv("FWUPD_NVME_VERBOSE") != NULL) + 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("FWUPD_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"); +} + +/* + * Returns: + * %TRUE: device is in PCI subsystem + * %FALSE: device is, probably, NVMe-over-Fabrics + */ +static gboolean +fu_nvme_device_is_pci(FuDevice *device, GError **error) +{ + g_autoptr(GUdevDevice) device_tmp = NULL; + GUdevDevice *gdev; + + gdev = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + + device_tmp = g_udev_device_get_parent_with_subsystem(gdev, "pci", NULL); + if (device_tmp == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device is not on PCI subsystem"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_nvme_device_probe(FuDevice *device, GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE(device); + + /* FuUdevDevice->probe */ + if (!FU_DEVICE_CLASS(fu_nvme_device_parent_class)->probe(device, error)) + return FALSE; + + /* fix up vendor name so we can remove it from the product name */ + if (g_strcmp0(fu_device_get_vendor(FU_DEVICE(device)), "Samsung Electronics Co Ltd") == 0) + fu_device_set_vendor(FU_DEVICE(device), "Samsung"); + + /* ignore non-PCI NVMe devices */ + if (!fu_nvme_device_is_pci(device, error)) + return FALSE; + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(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(FU_UDEV_DEVICE(device), "pci"); + if (self->pci_depth <= 2) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + } + + /* all devices need at least a warm reset, but some quirked drives + * need a full "cold" shutdown and startup */ + if (!fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + fu_device_add_flag(device, 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_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE(device); + g_autoptr(GBytes) fw2 = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + guint64 block_size = self->write_block_size > 0 ? self->write_block_size : 0x1000; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 10, "commit"); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* some vendors provide firmware files whose sizes are not multiples + * of blksz *and* the device won't accept blocks of different sizes */ + if (fu_device_has_private_flag(device, FU_NVME_DEVICE_FLAG_FORCE_ALIGN)) { + fw2 = fu_bytes_align(fw, block_size, 0xff); + } else { + fw2 = g_bytes_ref(fw); + } + + /* write each block */ + chunks = fu_chunk_array_new_from_bytes(fw2, + 0x00, /* start_addr */ + 0x00, /* page_sz */ + block_size); /* block size */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_nvme_device_fw_download(self, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, "failed to write chunk %u: ", i); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* 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; + } + fu_progress_step_done(progress); + + /* success! */ + 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) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->write_block_size = 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_nvme_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_nvme_device_init(FuNvmeDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_summary(FU_DEVICE(self), "NVM Express solid state drive"); + fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_add_protocol(FU_DEVICE(self), "org.nvmexpress"); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | + FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT); + fu_device_register_private_flag(FU_DEVICE(self), + FU_NVME_DEVICE_FLAG_FORCE_ALIGN, + "force-align"); +} + +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); + 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->setup = fu_nvme_device_setup; + klass_device->write_firmware = fu_nvme_device_write_firmware; + klass_device->probe = fu_nvme_device_probe; + klass_device->set_progress = fu_nvme_device_set_progress; +} + +FuNvmeDevice * +fu_nvme_device_new_from_blob(FuContext *ctx, const guint8 *buf, gsize sz, GError **error) +{ + g_autoptr(FuNvmeDevice) self = NULL; + self = g_object_new(FU_TYPE_NVME_DEVICE, "context", ctx, NULL); + if (!fu_nvme_device_parse_cns(self, buf, sz, error)) + return NULL; + return g_steal_pointer(&self); +} diff --git a/fwupd-1.8.6/plugins/nvme/fu-nvme-device.h b/fwupd-1.8.6/plugins/nvme/fu-nvme-device.h new file mode 100644 index 0000000000000000000000000000000000000000..de2f3c2ba29788c94afe65b4df3cf95732afb60c --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-nvme-device.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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_from_blob(FuContext *ctx, const guint8 *buf, gsize sz, GError **error); diff --git a/fwupd-1.8.6/plugins/nvme/fu-nvme-plugin.c b/fwupd-1.8.6/plugins/nvme/fu-nvme-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..ef136f713126155eec543b18d5bcf1a2f6069fd1 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-nvme-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-nvme-device.h" +#include "fu-nvme-plugin.h" + +struct _FuNvmePlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuNvmePlugin, fu_nvme_plugin, FU_TYPE_PLUGIN) + +static void +fu_nvme_plugin_init(FuNvmePlugin *self) +{ +} + +static void +fu_nvme_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "nvme"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_NVME_DEVICE); +} + +static void +fu_nvme_plugin_class_init(FuNvmePluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_nvme_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/nvme/fu-nvme-plugin.h b/fwupd-1.8.6/plugins/nvme/fu-nvme-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..05d22a8cde474d46e6812f2be0ad3d4dec8ba7d4 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-nvme-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuNvmePlugin, fu_nvme_plugin, FU, NVME_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/nvme/fu-self-test.c b/fwupd-1.8.6/plugins/nvme/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..e1c8afdbe6c660bb9c9992722e5d7d700c2b0a17 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/fu-self-test.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-context-private.h" +#include "fu-device-private.h" +#include "fu-nvme-device.h" + +static void +fu_nvme_cns_func(void) +{ + gboolean ret; + gsize sz; + const gchar *ci = g_getenv("CI_NETWORK"); + g_autofree gchar *data = NULL; + g_autofree gchar *path = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuNvmeDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + path = g_test_build_filename(G_TEST_DIST, "tests", "TOSHIBA_THNSN5512GPU7.bin", NULL); + + if (!g_file_test(path, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing TOSHIBA_THNSN5512GPU7.bin"); + return; + } + ret = g_file_get_contents(path, &data, &sz, &error); + g_assert_no_error(error); + g_assert_true(ret); + dev = fu_nvme_device_new_from_blob(ctx, (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(FuContext) ctx = fu_context_new(); + g_autoptr(GDir) dir = NULL; + + /* may or may not exist */ + path = g_test_build_filename(G_TEST_DIST, "tests", "blobs", NULL); + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + 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(ctx, (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_autofree gchar *testdatadir = NULL; + 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); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + /* 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 --git a/fwupd-1.8.6/plugins/nvme/meson.build b/fwupd-1.8.6/plugins/nvme/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..19d2d32a3c8dfd3c058e33b0e6f968ffb8040f0d --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/meson.build @@ -0,0 +1,47 @@ +nvme_header = cc.has_header('linux/nvme_ioctl.h', required: get_option('plugin_nvme')) + +if nvme_header and \ + get_option('plugin_nvme').require(gudev.found(), + error_message: 'gudev is needed for plugin_nvme').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginNvme"'] + +plugin_quirks += files('nvme.quirk') +plugin_builtin_nvme = static_library('fu_plugin_nvme', + sources: [ + 'fu-nvme-plugin.c', + 'fu-nvme-common.c', + 'fu-nvme-device.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_nvme + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_DATADIR_QUIRKS', meson.current_source_dir()) + e = executable( + 'nvme-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_nvme, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('nvme-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/nvme/nvme.quirk b/fwupd-1.8.6/plugins/nvme/nvme.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b545b097879c46073f02bfcc12ad4ed9c3c127c3 --- /dev/null +++ b/fwupd-1.8.6/plugins/nvme/nvme.quirk @@ -0,0 +1,45 @@ +# match all devices with this udev subsystem +[NVME] +Plugin = nvme + +# Phison +[NVME\VEN_1987] +Flags = force-align,needs-shutdown + +# Kingston +[NVME\VEN_2646] +Flags = needs-shutdown + +# KIOXIA +[NVME\VEN_1179] +Flags = signed-payload +[NVME\VEN_1E0F] +Flags = signed-payload + +# Samsung +[NVME\VEN_144D] +Flags = signed-payload + +# SSSTC +[NVME\VEN_14A4] +Flags = signed-payload +[NVME\VEN_1E95] +Flags = signed-payload + +# SK Hynix +[NVME\VEN_1C5C] +Flags = signed-payload + +# Kingston +[NVME\VEN_2646&DEV_5013] +Flags = signed-payload +[NVME\VEN_2646&DEV_500A] +Flags = signed-payload +[NVME\VEN_2646&DEV_500B] +Flags = unsigned-payload +[NVME\VEN_2646&DEV_5012] +Flags = unsigned-payload + +# Western Digital +[NVME\VEN_101C] +Flags = signed-payload diff --git a/fwupd-1.8.6/plugins/optionrom/README.md b/fwupd-1.8.6/plugins/optionrom/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ae0a29475089fcf10ee5c65df45c2cb4ca3fea08 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/README.md @@ -0,0 +1,20 @@ +# OptionROM + +## Introduction + +This plugin is also able to read and parse the firmware of some PCI devices +which allows some host state verification to be done. + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `PCI\VEN_%04X&DEV_%04X` + +## Vendor ID Security + +The device is not upgradable and thus requires no vendor ID set. + +## External Interface Access + +This plugin requires read access to the rom file of PCI devices (`/sys/class/pci_bus/*/device/rom`) diff --git a/fwupd-1.8.6/plugins/optionrom/fu-optionrom-device.c b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-device.c new file mode 100644 index 0000000000000000000000000000000000000000..83e5a6d284a3cfb68df86e1c37d767b30fdbb879 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-device.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-optionrom-device.h" + +struct _FuOptionromDevice { + FuUdevDevice parent_instance; +}; + +G_DEFINE_TYPE(FuOptionromDevice, fu_optionrom_device, FU_TYPE_UDEV_DEVICE) + +static gboolean +fu_optionrom_device_probe(FuDevice *device, GError **error) +{ + g_autofree gchar *fn = NULL; + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + + /* does the device even have ROM? */ + fn = g_build_filename(g_udev_device_get_sysfs_path(udev_device), "rom", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unable to read firmware from device"); + return FALSE; + } + + /* FuUdevDevice->probe -- needed by FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT */ + if (!FU_DEVICE_CLASS(fu_optionrom_device_parent_class)->probe(device, error)) + return FALSE; + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error); +} + +static GBytes * +fu_optionrom_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuUdevDevice *udev_device = FU_UDEV_DEVICE(device); + guint number_reads = 0; + g_autofree gchar *fn = NULL; + g_autofree gchar *rom_fn = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GError) error_local = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GInputStream) stream = NULL; + + /* open the file */ + rom_fn = g_build_filename(fu_udev_device_get_sysfs_path(udev_device), "rom", NULL); + if (rom_fn == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Unable to read firmware from device"); + return NULL; + } + + /* open file */ + file = g_file_new_for_path(rom_fn); + stream = G_INPUT_STREAM(g_file_read(file, NULL, &error_local)); + if (stream == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + error_local->message); + return NULL; + } + + /* we have to enable the read for devices */ + fn = g_file_get_path(file); + if (g_str_has_prefix(fn, "/sys")) { + g_autoptr(GFileOutputStream) output_stream = NULL; + output_stream = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error); + if (output_stream == NULL) + return NULL; + if (g_output_stream_write(G_OUTPUT_STREAM(output_stream), "1", 1, NULL, error) < 0) + return NULL; + } + + /* ensure we got enough data to fill the buffer */ + while (TRUE) { + gssize sz; + guint8 tmp[32 * 1024] = {0x0}; + sz = g_input_stream_read(stream, tmp, sizeof(tmp), NULL, error); + if (sz == 0) + break; + g_debug("ROM returned 0x%04x bytes", (guint)sz); + if (sz < 0) + return NULL; + g_byte_array_append(buf, tmp, sz); + + /* check the firmware isn't serving us small chunks */ + if (number_reads++ > 1024) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware not fulfilling requests"); + return NULL; + } + } + if (buf->len < 512) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware too small: %u bytes", + buf->len); + return NULL; + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_optionrom_device_init(FuOptionromDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_logical_id(FU_DEVICE(self), "rom"); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | + FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT); +} + +static void +fu_optionrom_device_finalize(GObject *object) +{ + G_OBJECT_CLASS(fu_optionrom_device_parent_class)->finalize(object); +} + +static void +fu_optionrom_device_class_init(FuOptionromDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_optionrom_device_finalize; + klass_device->dump_firmware = fu_optionrom_device_dump_firmware; + klass_device->probe = fu_optionrom_device_probe; +} diff --git a/fwupd-1.8.6/plugins/optionrom/fu-optionrom-device.h b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-device.h new file mode 100644 index 0000000000000000000000000000000000000000..94b04733cf1eda1b9212f5195cf5e9ad41e38bb6 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_OPTIONROM_DEVICE (fu_optionrom_device_get_type()) +G_DECLARE_FINAL_TYPE(FuOptionromDevice, fu_optionrom_device, FU, OPTIONROM_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/optionrom/fu-optionrom-plugin.c b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..7aa6b8979a832a4f8655a9c77a6432975969f395 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015-2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-optionrom-device.h" +#include "fu-optionrom-plugin.h" + +struct _FuOptionromPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuOptionromPlugin, fu_optionrom_plugin, FU_TYPE_PLUGIN) + +static void +fu_optionrom_plugin_init(FuOptionromPlugin *self) +{ +} + +static void +fu_optionrom_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "pci"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "udev"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_OPTIONROM_DEVICE); +} + +static void +fu_optionrom_plugin_class_init(FuOptionromPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_optionrom_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/optionrom/fu-optionrom-plugin.h b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..7ba9f80b12ea46a6375c9804ec59c4e7a889f468 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/fu-optionrom-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuOptionromPlugin, fu_optionrom_plugin, FU, OPTIONROM_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/optionrom/fu-self-test.c b/fwupd-1.8.6/plugins/optionrom/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..94110a1858c478585f5c62bf80158c069cd4ca65 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/fu-self-test.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "fu-plugin-private.h" +#include "fu-rom.h" + +static void +fu_rom_func(void) +{ + struct { + FuRomKind kind; + const gchar *fn; + const gchar *ver; + guint16 vendor; + guint16 model; + } data[] = { + {FU_ROM_KIND_ATI, + "Asus.9800PRO.256.unknown.031114.rom", + "008.015.041.001", + 0x1002, + 0x4e48}, + {FU_ROM_KIND_ATI, /* atombios */ + "Asus.R9290X.4096.131014.rom", + "015.039.000.006.003515", + 0x1002, + 0x67b0}, + {FU_ROM_KIND_ATI, /* atombios, with serial */ + "Asus.HD7970.3072.121018.rom", + "015.023.000.002.000000", + 0x1002, + 0x6798}, + {FU_ROM_KIND_NVIDIA, "Asus.GTX480.1536.100406_1.rom", "70.00.1A.00.02", 0x10de, 0x06c0}, + {FU_ROM_KIND_NVIDIA, /* nvgi */ + "Asus.GTX980.4096.140905.rom", + "84.04.1F.00.02", + 0x10de, + 0x13c0}, + {FU_ROM_KIND_NVIDIA, /* nvgi, with serial */ + "Asus.TitanBlack.6144.140212.rom", + "80.80.4E.00.01", + 0x10de, + 0x100c}, + {FU_ROM_KIND_UNKNOWN, NULL, NULL, 0x0000, 0x0000}}; + + for (guint i = 0; data[i].fn != NULL; i++) { + gboolean ret; + g_autoptr(GError) error = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(FuRom) rom = NULL; + g_autoptr(GFile) file = NULL; + rom = fu_rom_new(); + g_assert_nonnull(rom); + + /* load file */ + filename = g_test_build_filename(G_TEST_DIST, "tests", data[i].fn, NULL); + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) + continue; + g_print("\nparsing %s...", filename); + file = g_file_new_for_path(filename); + ret = fu_rom_load_file(rom, file, FU_ROM_LOAD_FLAG_BLANK_PPID, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpstr(fu_rom_get_version(rom), ==, data[i].ver); + g_assert_cmpint(fu_rom_get_kind(rom), ==, data[i].kind); + g_assert_cmpint(fu_rom_get_vendor(rom), ==, data[i].vendor); + g_assert_cmpint(fu_rom_get_model(rom), ==, data[i].model); + } +} + +static void +fu_rom_all_func(void) +{ + GDir *dir; + g_autofree gchar *path = NULL; + + /* may or may not exist */ + path = g_test_build_filename(G_TEST_DIST, "tests", "roms", NULL); + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + return; + g_print("\n"); + dir = g_dir_open(path, 0, NULL); + do { + const gchar *fn; + gboolean ret; + g_autoptr(GError) error = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(FuRom) rom = NULL; + g_autoptr(GFile) file = NULL; + + fn = g_dir_read_name(dir); + if (fn == NULL) + break; + filename = g_build_filename(path, fn, NULL); + g_print("\nparsing %s...", filename); + file = g_file_new_for_path(filename); + rom = fu_rom_new(); + ret = fu_rom_load_file(rom, file, FU_ROM_LOAD_FLAG_BLANK_PPID, NULL, &error); + if (!ret) { + g_print("%s %s : %s\n", + fu_rom_kind_to_string(fu_rom_get_kind(rom)), + filename, + error->message); + continue; + } + g_assert_cmpstr(fu_rom_get_version(rom), !=, NULL); + g_assert_cmpstr(fu_rom_get_version(rom), !=, "\0"); + g_assert_cmpint(fu_rom_get_kind(rom), !=, FU_ROM_KIND_UNKNOWN); + } while (TRUE); +} + +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/rom", fu_rom_func); + g_test_add_func("/fwupd/rom{all}", fu_rom_all_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/optionrom/fuzzing/header-data-payload.rom b/fwupd-1.8.6/plugins/optionrom/fuzzing/header-data-payload.rom new file mode 100644 index 0000000000000000000000000000000000000000..e98dff2e20b6a381fa7a9e418f596ab373a0f3b6 Binary files /dev/null and b/fwupd-1.8.6/plugins/optionrom/fuzzing/header-data-payload.rom differ diff --git a/fwupd-1.8.6/plugins/optionrom/fuzzing/header-no-data.rom b/fwupd-1.8.6/plugins/optionrom/fuzzing/header-no-data.rom new file mode 100644 index 0000000000000000000000000000000000000000..bc67c07f0ad155c474d3ca04c8bf9320df11a5c5 Binary files /dev/null and b/fwupd-1.8.6/plugins/optionrom/fuzzing/header-no-data.rom differ diff --git a/fwupd-1.8.6/plugins/optionrom/fuzzing/ifr-header-data-payload.rom b/fwupd-1.8.6/plugins/optionrom/fuzzing/ifr-header-data-payload.rom new file mode 100644 index 0000000000000000000000000000000000000000..8a0af3e7cba9b4b89027f144fa61ec4f8772c91e Binary files /dev/null and b/fwupd-1.8.6/plugins/optionrom/fuzzing/ifr-header-data-payload.rom differ diff --git a/fwupd-1.8.6/plugins/optionrom/fuzzing/naked-ifr.rom b/fwupd-1.8.6/plugins/optionrom/fuzzing/naked-ifr.rom new file mode 100644 index 0000000000000000000000000000000000000000..fd6dd295fa7dd7a3c294fe44424466ccaa7a9f02 Binary files /dev/null and b/fwupd-1.8.6/plugins/optionrom/fuzzing/naked-ifr.rom differ diff --git a/fwupd-1.8.6/plugins/optionrom/meson.build b/fwupd-1.8.6/plugins/optionrom/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..0026f24c24bfe11783c4bae7e14ad092637e0f86 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/meson.build @@ -0,0 +1,15 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginOptionrom"'] + +plugin_quirks += files('optionrom.quirk') +plugin_builtins += static_library('fu_plugin_optionrom', + sources: [ + 'fu-optionrom-plugin.c', + 'fu-optionrom-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/optionrom/optionrom.quirk b/fwupd-1.8.6/plugins/optionrom/optionrom.quirk new file mode 100644 index 0000000000000000000000000000000000000000..fa0d26a7805a0a0dc5ac36cedb66817b1cb83a85 --- /dev/null +++ b/fwupd-1.8.6/plugins/optionrom/optionrom.quirk @@ -0,0 +1,3 @@ +# match all devices with this udev subsystem +[PCI] +Plugin = optionrom diff --git a/fwupd-1.8.6/plugins/parade-lspcon/README.md b/fwupd-1.8.6/plugins/parade-lspcon/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6372f941a40d2ef103787a9f4ef0ea4812555604 --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/README.md @@ -0,0 +1,52 @@ +# Parade LSPCON + +## Introduction + +This plugin updates the firmware of HDMI level shifter and protocol converter +(LSPCON) devices made by Parade Technologies, such as the PS175. + +These devices communicate over I²C, via either the DisplayPort aux channel or a +dedicated bus- this plugin uses a dedicated bus declared by system firmware for, +flashing, and reads the device firmware version from DPCD. Quirks specify the +DisplayPort bus over which DPCD is read for a given system. + +Firmware is stored on an external flash attached to an SPI bus on the device. +The attached flash is assumed to be compatible with the W25Q20 series of +devices, in particular supporting a 64k Block Erase command (0xD8) with 24-bit +address and Write Enable for Volatile Status Register (0x05). + +## Firmware Format + +The device firmware is in an unspecified binary format that is written directly +to an inactive partition of the Flash attached to the device. + +This plugin supports the following protocol ID: + +* com.paradetech.ps176 + +## GUID Generation + +Devices use an extra instance ID derived from SMBIOS, e.g. + +* `I2C\NAME_1AF80175:00:00&FAMILY_Google_Hatch` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### ParadeLspconAuxDeviceName + +The sysfs name of the `drm_dp_aux_dev` over which device version should be read. + +Since: 1.6.2 + +## Vendor ID security + +The vendor ID is specified by system firmware (such as ACPI tables) and is +part of the device's name as read from sysfs. + +## External Interface Access + +This plugin requires access to the DisplayPort aux channel to read DPCD, such +as `/dev/drm_dp_aux0` as well as the i2c bus attached to the device, such as +`/dev/i2c-7`. diff --git a/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-device.c b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-device.c new file mode 100644 index 0000000000000000000000000000000000000000..42c7143eb091f1e800958cb4b55c6c11e048b39d --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-device.c @@ -0,0 +1,842 @@ +/* + * Copyright (C) 2021 Peter Marheine + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-parade-lspcon-device.h" + +/* device registers are split into pages, where + * each page has its own I2C address */ +#define I2C_ADDR_PAGE2 0x4A +#define REG_ADDR_CLT2SPI 0x82 +/* FLASH_ADDR_* are the upper 16 bits of the 24-bit flash address that gets + * mapped into page 7. Writing 0x01, 0x42 will map the 256 bytes from 0x420100 + * into page 7. */ +#define REG_ADDR_FLASH_ADDR_LO 0x8E +#define REG_ADDR_FLASH_ADDR_HI 0x8F +/* 16-deep SPI write and read buffer FIFOs */ +#define REG_ADDR_WR_FIFO 0x90 +#define REG_ADDR_RD_FIFO 0x91 +/* Low nibble is write operation length, high nibble for read commands. + * Reset to 0 after command completion. */ +#define REG_ADDR_SPI_LEN 0x92 + +#define REG_ADDR_SPI_CTL 0x93 +/* set to do a write-only transaction */ +#define SPI_CTL_NOREAD 0x04 +/* set to begin executing command */ +#define SPI_CTL_TRIGGER 0x01 + +/* operation status fields: set to 1 when operation begins, 2 when command has been + * sent, reset to 0 when command completed */ +#define REG_ADDR_SPI_STATUS 0x9e +/* byte programming */ +#define SPI_STATUS_BP_MASK 0x03 +/* sector erase */ +#define SPI_STATUS_SE_MASK 0x0C +/* chip erase */ +#define SPI_STATUS_CE_MASK 0x30 + +/* write WR_PROTECT_DISABLE to permit flash write operations */ +#define REG_ADDR_WR_PROTECT 0xB3 +#define WR_PROTECT_DISABLE 0x10 + +/* MPU control register */ +#define REG_ADDR_MPU 0xBC + +/* write a magic sequence to this register to enable writes to + * mapped memory via page 7, or anything else to disable */ +#define REG_ADDR_MAP_WRITE 0xDA + +#define I2C_ADDR_PAGE5 0x4D +#define REG_ADDR_ACTIVE_PARTITION 0x0E + +#define I2C_ADDR_PAGE7 0x4F + +#define FLASH_BLOCK_SIZE 0x10000 + +#define FU_PARADE_LSPCON_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +/* + * user1: 0x10000 - 0x20000 + * user2: 0x20000 - 0x30000 + * flag: 0x00002 - 0x00004 + */ +struct _FuParadeLspconDevice { + FuI2cDevice parent_instance; + guint8 active_partition; + gchar *aux_device_name; +}; + +G_DEFINE_TYPE(FuParadeLspconDevice, fu_parade_lspcon_device, FU_TYPE_I2C_DEVICE) + +static void +fu_parade_lspcon_device_init(FuParadeLspconDevice *self) +{ + FuDevice *device = FU_DEVICE(self); + fu_device_set_vendor(device, "Parade Technologies"); + fu_device_add_vendor_id(device, "PCI:0x1AF8"); + fu_device_add_protocol(device, "com.paradetech.ps176"); + fu_device_add_icon(device, "video-display"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_firmware_size(device, 0x10000); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_parade_lspcon_device_finalize(GObject *object) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(object); + g_free(self->aux_device_name); + G_OBJECT_CLASS(fu_parade_lspcon_device_parent_class)->finalize(object); +} + +static gboolean +fu_parade_lspcon_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + + if (g_strcmp0(key, "ParadeLspconAuxDeviceName") == 0) { + self->aux_device_name = g_strdup(value); + return TRUE; + } + return FU_DEVICE_CLASS(fu_parade_lspcon_device_parent_class) + ->set_quirk_kv(device, key, value, error); +} + +static gboolean +fu_parade_lspcon_device_probe(FuDevice *device, GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + FuContext *context = fu_device_get_context(device); + + /* FuI2cDevice->probe */ + if (!FU_DEVICE_CLASS(fu_parade_lspcon_device_parent_class)->probe(device, error)) + return FALSE; + + /* custom instance IDs to get device quirks */ + fu_device_add_instance_str(device, + "FAMILY", + fu_context_get_hwid_value(context, FU_HWIDS_KEY_FAMILY)); + if (!fu_device_build_instance_id_quirk(device, error, "I2C", "NAME", "FAMILY", NULL)) + return FALSE; + + /* should know which aux device over which we read DPCD version */ + if (self->aux_device_name == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ParadeLspconAuxDeviceName must be specified"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_parade_lspcon_ensure_i2c_address(FuParadeLspconDevice *self, guint8 address, GError **error) +{ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + I2C_SLAVE, + (guint8 *)(guintptr)address, + NULL, + FU_PARADE_LSPCON_DEVICE_IOCTL_TIMEOUT, + error)) { + g_prefix_error(error, "failed to set I2C address: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_parade_lspcon_device_open(FuDevice *device, GError **error) +{ + if (!FU_DEVICE_CLASS(fu_parade_lspcon_device_parent_class)->open(device, error)) + return FALSE; + + /* general assumption is that page 2 is selected: code that uses another address + * should use an address guard to ensure it gets reset */ + return fu_parade_lspcon_ensure_i2c_address(FU_PARADE_LSPCON_DEVICE(device), + I2C_ADDR_PAGE2, + error); +} + +/** + * creates a scope in which the device's target I2C address is something + * other than page 2, and resets it to page 2 when the scope is left. + */ +typedef struct { + FuParadeLspconDevice *device; +} FuParadeLspconI2cAddressGuard; + +static FuParadeLspconI2cAddressGuard * +fu_parade_lspcon_i2c_address_guard_new(FuParadeLspconDevice *self, + guint8 new_address, + GError **error) +{ + FuParadeLspconI2cAddressGuard *out; + + if (!fu_parade_lspcon_ensure_i2c_address(self, new_address, error)) + return NULL; + out = g_new0(FuParadeLspconI2cAddressGuard, 1); + out->device = self; + return out; +} + +static void +fu_parade_lspcon_i2c_address_guard_free(FuParadeLspconI2cAddressGuard *guard) +{ + g_autoptr(GError) error_local = NULL; + if (!fu_parade_lspcon_ensure_i2c_address(guard->device, I2C_ADDR_PAGE2, &error_local)) { + g_warning("failed to set page2 back: %s", error_local->message); + } + g_free(guard); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuParadeLspconI2cAddressGuard, + fu_parade_lspcon_i2c_address_guard_free); + +static gboolean +fu_parade_lspcon_write_register(FuParadeLspconDevice *self, + guint8 register_addr, + guint8 value, + GError **error) +{ + guint8 transaction[] = {register_addr, value}; + return fu_i2c_device_write(FU_I2C_DEVICE(self), transaction, sizeof(transaction), error); +} + +static gboolean +fu_parade_lspcon_read_register(FuParadeLspconDevice *self, + guint8 register_addr, + guint8 *value, + GError **error) +{ + FuI2cDevice *i2c_device = FU_I2C_DEVICE(self); + if (!fu_i2c_device_write(i2c_device, ®ister_addr, 0x1, error)) + return FALSE; + return fu_i2c_device_read(i2c_device, value, 0x1, error); +} + +/* map the page containing the given address into page 7 */ +static gboolean +fu_parade_lspcon_map_page(FuParadeLspconDevice *self, guint32 address, GError **error) +{ + if (!fu_parade_lspcon_write_register(self, REG_ADDR_FLASH_ADDR_HI, address >> 16, error)) + return FALSE; + return fu_parade_lspcon_write_register(self, REG_ADDR_FLASH_ADDR_LO, address >> 8, error); +} + +/* wait until the specified register masked with mask reads the expected + * value, up to 10 seconds */ +static gboolean +fu_parade_lspcon_poll_register(FuParadeLspconDevice *self, + guint8 register_address, + guint8 mask, + guint8 expected, + GError **error) +{ + guint8 value; + g_autoptr(GTimer) timer = g_timer_new(); + + do { + if (!fu_parade_lspcon_read_register(self, register_address, &value, error)) + return FALSE; + if ((value & mask) == expected) + return TRUE; + } while (g_timer_elapsed(timer, NULL) <= 10.0); + + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "register %x did not read %x (mask %x) within 10 seconds: read %x", + register_address, + expected, + mask, + value); + return FALSE; +} + +static gboolean +fu_parade_lspcon_flash_read(FuParadeLspconDevice *self, + guint32 base_address, + guint8 *data, + const gsize len, + FuProgress *progress, + GError **error) +{ + FuI2cDevice *i2c_device = FU_I2C_DEVICE(self); + gsize offset = 0; + + while (offset < len) { + /* page 7 reads always start from the base of the mapped window- we'll + * read the whole page then pull out the parts we care about, using the + * full page everywhere except possibly in the first and last reads */ + guint8 page_data[256] = {0x0}; + guint8 page_data_start = base_address & 0xFF; + gsize page_data_take = MIN((gssize)len, 256 - page_data_start); + g_autoptr(FuParadeLspconI2cAddressGuard) guard = NULL; + + if (!fu_parade_lspcon_map_page(self, base_address, error)) + return FALSE; + guard = fu_parade_lspcon_i2c_address_guard_new(self, I2C_ADDR_PAGE7, error); + if (guard == NULL) + return FALSE; + if (!fu_i2c_device_read(i2c_device, page_data, 256, error)) + return FALSE; + + if (!fu_memcpy_safe(data, + len, + offset, + page_data, + sizeof(page_data), + page_data_start, + page_data_take, + error)) + return FALSE; + base_address += page_data_take; + offset += page_data_take; + + fu_progress_set_percentage_full(progress, offset, len); + } + + return TRUE; +} + +static gboolean +fu_parade_lspcon_flash_transmit_command(FuParadeLspconDevice *self, + const guint8 *command, + gsize command_len, + GError **error) +{ + /* write length field is 4 bits wide */ + g_return_val_if_fail(command_len > 0 && command_len <= 16, FALSE); + + /* fill transmit buffer */ + for (gsize i = 0; i < command_len; i++) { + if (!fu_parade_lspcon_write_register(self, REG_ADDR_WR_FIFO, command[i], error)) + return FALSE; + } + + /* set command length */ + if (!fu_parade_lspcon_write_register(self, REG_ADDR_SPI_LEN, command_len - 1, error)) + return FALSE; + + /* execute operation */ + return fu_parade_lspcon_write_register(self, + REG_ADDR_SPI_CTL, + SPI_CTL_NOREAD | SPI_CTL_TRIGGER, + error); +} + +/* + * set the flash Write Enable Latch, permitting the next program, erase or + * status register write operation. + */ +static gboolean +fu_parade_lspcon_flash_enable_write(FuParadeLspconDevice *self, GError **error) +{ + const guint8 write_enable[] = {0x06}; + return fu_parade_lspcon_flash_transmit_command(self, + write_enable, + sizeof(write_enable), + error); +} + +static gboolean +fu_parade_lspcon_flash_read_status(FuParadeLspconDevice *self, guint8 *value, GError **error) +{ + if (!fu_parade_lspcon_write_register(self, REG_ADDR_WR_FIFO, 0x05, error)) + return FALSE; + if (!fu_parade_lspcon_write_register(self, REG_ADDR_SPI_LEN, 0, error)) + return FALSE; + if (!fu_parade_lspcon_write_register(self, REG_ADDR_SPI_CTL, SPI_CTL_TRIGGER, error)) + return FALSE; + + /* wait for command completion */ + if (!fu_parade_lspcon_poll_register(self, REG_ADDR_SPI_CTL, SPI_CTL_TRIGGER, 0, error)) + return FALSE; + + /* read SR value */ + return fu_parade_lspcon_read_register(self, REG_ADDR_RD_FIFO, value, error); +} + +/* poll the flash status register for operation completion */ +static gboolean +fu_parade_lspcon_flash_wait_ready(FuParadeLspconDevice *self, GError **error) +{ + g_autoptr(GTimer) timer = g_timer_new(); + + do { + guint8 status_register; + if (!fu_parade_lspcon_flash_read_status(self, &status_register, error)) + return FALSE; + + /* BUSY bit clears on completion */ + if ((status_register & 1) == 0) + return TRUE; + + /* flash operations generally take between 1ms and 4s; polling + * at 1000 Hz is still quite responsive and not overly slow */ + g_usleep(G_TIME_SPAN_MILLISECOND); + } while (g_timer_elapsed(timer, NULL) <= 10.0); + + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "flash did not become ready within 10 seconds"); + return FALSE; +} + +static gboolean +fu_parade_lspcon_flash_write(FuParadeLspconDevice *self, + guint32 base_address, + GBytes *data, + FuProgress *progress, + GError **error) +{ + FuI2cDevice *i2c_device = FU_I2C_DEVICE(self); + const guint8 unlock_writes[] = {0xaa, 0x55, 0x50, 0x41, 0x52, 0x44}; + gsize data_len = g_bytes_get_size(data); + g_autoptr(GPtrArray) chunks = NULL; + + /* address must be 256-byte aligned */ + g_return_val_if_fail((base_address & 0xFF) == 0, FALSE); + g_debug("flash write %" G_GSIZE_FORMAT " bytes at %#x", + g_bytes_get_size(data), + base_address); + + /* unlock map writes by writing the magic sequence */ + for (gsize i = 0; i < sizeof(unlock_writes); i++) { + if (!fu_parade_lspcon_write_register(self, + REG_ADDR_MAP_WRITE, + unlock_writes[i], + error)) + return FALSE; + } + + /* reset clt2SPI, required before write */ + if (!fu_parade_lspcon_write_register(self, REG_ADDR_CLT2SPI, 0x20, error)) + return FALSE; + g_usleep(100 * G_TIME_SPAN_MILLISECOND); + if (!fu_parade_lspcon_write_register(self, REG_ADDR_CLT2SPI, 0, error)) + return FALSE; + + chunks = fu_chunk_array_new_from_bytes(data, base_address, 0, 256); + for (gsize i = 0; i < chunks->len; i++) { + FuChunk *chunk = g_ptr_array_index(chunks, i); + guint32 address = fu_chunk_get_address(chunk); + guint32 chunk_size = fu_chunk_get_data_sz(chunk); + guint8 write_data[257] = {0x0}; + g_autoptr(FuParadeLspconI2cAddressGuard) guard = NULL; + + /* map target address range in page 7 */ + if (!fu_parade_lspcon_map_page(self, address, error)) + return FALSE; + + /* write data to page 7 memory window */ + guard = fu_parade_lspcon_i2c_address_guard_new(self, I2C_ADDR_PAGE7, error); + if (guard == NULL) + return FALSE; + + /* page write is prefixed with an offset: + * we always start from offset 0 */ + if (!fu_memcpy_safe(write_data, + sizeof(write_data), + 1, + fu_chunk_get_data(chunk), + chunk_size, + 0, + chunk_size, + error)) + return FALSE; + + if (!fu_i2c_device_write(i2c_device, write_data, chunk_size + 1, error)) + return FALSE; + + fu_progress_set_percentage_full(progress, address - base_address, data_len); + } + + /* re-lock map writes */ + return fu_parade_lspcon_write_register(self, REG_ADDR_MAP_WRITE, 0, error); +} + +static gboolean +fu_parade_lspcon_flash_erase_block(FuParadeLspconDevice *self, + guint32 base_address, + guint32 size, + GError **error) +{ + const guint8 block_erase[] = {0xd8, base_address >> 16, base_address >> 8, base_address}; + + /* address must be block-aligned */ + g_return_val_if_fail((base_address & (FLASH_BLOCK_SIZE - 1)) == 0, FALSE); + g_return_val_if_fail(size == FLASH_BLOCK_SIZE, FALSE); + + g_debug("flash erase block at %#x", base_address); + + if (!fu_parade_lspcon_flash_enable_write(self, error)) + return FALSE; + + if (!fu_parade_lspcon_flash_transmit_command(self, block_erase, sizeof(block_erase), error)) + return FALSE; + + /* wait for command completion */ + if (!fu_parade_lspcon_poll_register(self, + REG_ADDR_SPI_STATUS, + SPI_STATUS_SE_MASK, + 0, + error)) + return FALSE; + + /* wait for flash to complete erase */ + return fu_parade_lspcon_flash_wait_ready(self, error); +} + +static gboolean +fu_parade_lspcon_probe_active_flash_partition(FuParadeLspconDevice *self, + guint8 *partition, + GError **error) +{ + guint8 data = 0x0; + g_autoptr(FuParadeLspconI2cAddressGuard) guard = NULL; + + /* read currently-running flash partition number */ + guard = fu_parade_lspcon_i2c_address_guard_new(self, I2C_ADDR_PAGE5, error); + if (guard == NULL) + return FALSE; + if (!fu_parade_lspcon_read_register(self, REG_ADDR_ACTIVE_PARTITION, &data, error)) + return FALSE; + + *partition = data; + return TRUE; +} + +static gboolean +fu_parade_lspcon_device_reload(FuDevice *device, GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + guint32 oui; + guint8 version_buf[2] = {0x0}; + g_autofree gchar *version = NULL; + g_autofree gchar *oui_string = NULL; + g_autolist(GUdevDevice) aux_devices = NULL; + g_autoptr(FuDeviceLocker) aux_device_locker = NULL; + g_autoptr(FuUdevDevice) aux_device = NULL; + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + g_autoptr(GUdevEnumerator) enumerator = g_udev_enumerator_new(udev_client); + + /* determine active partition for flashing later */ + if (!fu_parade_lspcon_probe_active_flash_partition(self, &self->active_partition, error)) + return FALSE; + g_debug("device reports running from partition %d", self->active_partition); + if (self->active_partition < 1 || self->active_partition > 3) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unexpected active flash partition: %d", + self->active_partition); + return FALSE; + } + + /* find the drm_dp_aux_dev specified by quirks that is connected to the + * LSPCON, in order to read DPCD from it */ + if (self->aux_device_name == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no DP aux device specified, unable to query LSPCON"); + return FALSE; + } + g_udev_enumerator_add_match_subsystem(enumerator, "drm_dp_aux_dev"); + g_udev_enumerator_add_match_sysfs_attr(enumerator, "name", self->aux_device_name); + aux_devices = g_udev_enumerator_execute(enumerator); + if (aux_devices == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to locate a DP aux device named \"%s\"", + self->aux_device_name); + return FALSE; + } + if (g_list_length(aux_devices) > 1) { + g_list_free_full(aux_devices, g_object_unref); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "found multiple DP aux devices with name \"%s\"", + self->aux_device_name); + return FALSE; + } + aux_device = fu_udev_device_new(fu_device_get_context(device), aux_devices->data); + g_debug("using aux dev %s", fu_udev_device_get_sysfs_path(aux_device)); + + /* the following open() requires the device have IDs set */ + if (!fu_udev_device_set_physical_id(aux_device, "drm_dp_aux_dev", error)) + return FALSE; + + /* open device to read version from DPCD */ + if ((aux_device_locker = fu_device_locker_new(aux_device, error)) == NULL) + return FALSE; + + /* DPCD address 00500-00502: device OUI */ + if (!fu_udev_device_pread(aux_device, 0x500, (guint8 *)&oui, 3, error)) + return FALSE; + oui = GUINT32_FROM_BE(oui) >> 8; + oui_string = g_strdup_printf("OUI:%06X", oui); + fu_device_add_vendor_id(device, oui_string); + + if (oui != 0x001CF8) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device OUI %06X does not match expected value for Paradetech", + oui); + return FALSE; + } + + /* DPCD address 0x50A, 0x50B: branch device firmware + * major and minor revision */ + if (!fu_udev_device_pread(aux_device, 0x50a, version_buf, sizeof(version_buf), error)) + return FALSE; + version = g_strdup_printf("%d.%d", version_buf[0], version_buf[1]); + fu_device_set_version(device, version); + return TRUE; +} + +static gboolean +fu_parade_lspcon_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + const guint8 write_sr_volatile[] = {0x50}; + const guint8 write_sr_disable_bp[] = { + 0x01, /* write SR */ + 0x80, /* write protect follows /WP signal, no block protection */ + 0x00}; + const guint8 write_sr_enable_bp[] = {0x01, 0x8c, 0x00}; + /* if the boot partition is active we could flash either, but prefer + * the first */ + const guint8 target_partition = self->active_partition == 1 ? 2 : 1; + const guint32 target_address = target_partition << 16; + const guint8 flag_data[] = {0x55, 0xaa, target_partition, 1 - target_partition}; + const guint8 *buf; + gsize bufsz; + g_autofree guint8 *readback_buf = NULL; + g_autoptr(GBytes) blob_fw = NULL; + g_autoptr(GBytes) flag_data_bytes = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 5, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 70, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 25, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 3, "device-write-boot"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 2, "device-verify-boot"); + + blob_fw = fu_firmware_get_bytes(firmware, error); + if (blob_fw == NULL) + return FALSE; + + buf = g_bytes_get_data(blob_fw, &bufsz); + if (bufsz != FLASH_BLOCK_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid image size %#" G_GSIZE_MODIFIER "x, expected %#x", + bufsz, + (unsigned)FLASH_BLOCK_SIZE); + return FALSE; + } + + /* deassert flash /WP */ + if (!fu_parade_lspcon_write_register(self, REG_ADDR_WR_PROTECT, WR_PROTECT_DISABLE, error)) + return FALSE; + + /* disable flash protection until next power-off */ + if (!fu_parade_lspcon_flash_transmit_command(self, + write_sr_volatile, + sizeof(write_sr_volatile), + error)) + return FALSE; + if (!fu_parade_lspcon_flash_transmit_command(self, + write_sr_disable_bp, + sizeof(write_sr_disable_bp), + error)) + return FALSE; + + /* wait for SR write to complete */ + if (!fu_parade_lspcon_flash_wait_ready(self, error)) + return FALSE; + + /* erase entire target partition (one flash block) */ + if (!fu_parade_lspcon_flash_erase_block(self, target_address, bufsz, error)) { + g_prefix_error(error, "failed to erase flash partition %d: ", target_partition); + return FALSE; + } + fu_progress_step_done(progress); + + /* write image */ + if (!fu_parade_lspcon_flash_write(self, + target_address, + blob_fw, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, + "failed to write firmware to partition %d: ", + target_partition); + return FALSE; + } + fu_progress_step_done(progress); + + /* read back written image to verify */ + readback_buf = g_malloc0(bufsz); + if (!fu_parade_lspcon_flash_read(self, + target_address, + readback_buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_memcmp_safe(buf, bufsz, readback_buf, bufsz, error)) { + g_prefix_error(error, "flash contents do not match: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* erase flag partition */ + if (!fu_parade_lspcon_flash_erase_block(self, 0, FLASH_BLOCK_SIZE, error)) + return FALSE; + + /* write flag indicating device should boot the target partition */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + flag_data_bytes = g_bytes_new_static(flag_data, sizeof(flag_data)); + if (!fu_parade_lspcon_flash_write(self, + 0, + flag_data_bytes, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify flag partition */ + if (!fu_parade_lspcon_flash_read(self, + 0, + readback_buf, + sizeof(flag_data), + fu_progress_get_child(progress), + error)) + return FALSE; + if (!fu_memcmp_safe(flag_data, + sizeof(flag_data), + readback_buf, + MIN(sizeof(flag_data), bufsz), + error)) { + g_prefix_error(error, "flag partition contents do not match: "); + return FALSE; + } + + /* re-enable flash protection */ + if (!fu_parade_lspcon_flash_transmit_command(self, + write_sr_volatile, + sizeof(write_sr_volatile), + error)) + return FALSE; + if (!fu_parade_lspcon_flash_transmit_command(self, + write_sr_enable_bp, + sizeof(write_sr_enable_bp), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* reassert /WP to flash */ + return fu_parade_lspcon_write_register(self, REG_ADDR_WR_PROTECT, 0, error); +} + +static gboolean +fu_parade_lspcon_set_mpu_running(FuParadeLspconDevice *self, gboolean running, GError **error) +{ + /* reset */ + if (!fu_parade_lspcon_write_register(self, REG_ADDR_MPU, 0xc0, error)) + return FALSE; + + /* release reset, set MPU active or not */ + return fu_parade_lspcon_write_register(self, REG_ADDR_MPU, running ? 0 : 0x40, error); +} + +static gboolean +fu_parade_lspcon_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + return fu_parade_lspcon_set_mpu_running(self, FALSE, error); +} + +static gboolean +fu_parade_lspcon_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + return fu_parade_lspcon_set_mpu_running(self, TRUE, error); +} + +static GBytes * +fu_parade_lspcon_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuParadeLspconDevice *self = FU_PARADE_LSPCON_DEVICE(device); + g_autofree guint8 *data = g_malloc0(FLASH_BLOCK_SIZE); + + if (!fu_parade_lspcon_flash_read(self, + self->active_partition * FLASH_BLOCK_SIZE, + data, + FLASH_BLOCK_SIZE, + progress, + error)) + return NULL; + return g_bytes_new_take(g_steal_pointer(&data), FLASH_BLOCK_SIZE); +} + +static void +fu_parade_lspcon_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_parade_lspcon_device_class_init(FuParadeLspconDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *klass_object = G_OBJECT_CLASS(klass); + + klass_object->finalize = fu_parade_lspcon_device_finalize; + klass_device->set_quirk_kv = fu_parade_lspcon_device_set_quirk_kv; + klass_device->probe = fu_parade_lspcon_device_probe; + klass_device->setup = fu_parade_lspcon_device_reload; + klass_device->open = fu_parade_lspcon_device_open; + klass_device->reload = fu_parade_lspcon_device_reload; + klass_device->detach = fu_parade_lspcon_device_detach; + klass_device->write_firmware = fu_parade_lspcon_device_write_firmware; + klass_device->attach = fu_parade_lspcon_device_attach; + klass_device->dump_firmware = fu_parade_lspcon_device_dump_firmware; + klass_device->set_progress = fu_parade_lspcon_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-device.h b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-device.h new file mode 100644 index 0000000000000000000000000000000000000000..0ab85b8c1f4d599a92884763c8725588fa352d6b --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Peter Marheine + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PARADE_LSPCON_DEVICE (fu_parade_lspcon_device_get_type()) +G_DECLARE_FINAL_TYPE(FuParadeLspconDevice, + fu_parade_lspcon_device, + FU, + PARADE_LSPCON_DEVICE, + FuI2cDevice) diff --git a/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-plugin.c b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..06e934076f63581bf91a62c774b7327836efa51b --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-plugin.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Peter Marheine + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-parade-lspcon-device.h" +#include "fu-parade-lspcon-plugin.h" + +struct _FuParadeLspconPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuParadeLspconPlugin, fu_parade_lspcon_plugin, FU_TYPE_PLUGIN) + +static void +fu_parade_lspcon_plugin_init(FuParadeLspconPlugin *self) +{ +} + +static void +fu_parade_lspcon_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "ParadeLspconAuxDeviceName"); + fu_plugin_add_udev_subsystem(plugin, "i2c"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_PARADE_LSPCON_DEVICE); +} + +static void +fu_parade_lspcon_plugin_class_init(FuParadeLspconPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_parade_lspcon_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-plugin.h b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..1682fec25bca622b76800dc27e1c8bc092812cf0 --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/fu-parade-lspcon-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuParadeLspconPlugin, + fu_parade_lspcon_plugin, + FU, + PARADE_LSPCON_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/parade-lspcon/meson.build b/fwupd-1.8.6/plugins/parade-lspcon/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..dae891179308b667adac6efae88d18974e90fee2 --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/meson.build @@ -0,0 +1,20 @@ +if get_option('plugin_parade_lspcon').require(gudev.found(), + error_message: 'gudev is needed for plugin_parade_lspcon').allowed() + +cargs = ['-DG_LOG_DOMAIN="FuPluginParadeLspcon"'] + +plugin_quirks += files('parade-lspcon.quirk') +plugin_builtins += static_library('fu_plugin_parade_lspcon', + sources: [ + 'fu-parade-lspcon-device.c', + 'fu-parade-lspcon-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/parade-lspcon/parade-lspcon.quirk b/fwupd-1.8.6/plugins/parade-lspcon/parade-lspcon.quirk new file mode 100644 index 0000000000000000000000000000000000000000..f3594454c87513ed9260cc4860c96e373aa1571b --- /dev/null +++ b/fwupd-1.8.6/plugins/parade-lspcon/parade-lspcon.quirk @@ -0,0 +1,10 @@ +# Parade PS175 +[I2C\NAME_1AF80175:00] +Plugin = parade_lspcon +Name = PS175 + +# "Puff" Chromeboxes +[I2C\NAME_1AF80175:00&FAMILY_Google_Hatch] +ParadeLspconAuxDeviceName = DPDDC-B +[I2C\NAME_1AF80175:00&FAMILY_Google_Puff] +ParadeLspconAuxDeviceName = AUX B/DDI B/PHY B diff --git a/fwupd-1.8.6/plugins/pci-bcr/README.md b/fwupd-1.8.6/plugins/pci-bcr/README.md new file mode 100644 index 0000000000000000000000000000000000000000..32770f8e7d8577fb3b23e9ee088ebffefae8ca44 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-bcr/README.md @@ -0,0 +1,10 @@ +# PCI BIOS Control Register + +## Introduction + +This plugin checks if the system SPI chip is locked. The result will be stored +in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to the config space of PCI devices (`/sys/class/pci_bus/*/device/config`) diff --git a/fwupd-1.8.6/plugins/pci-bcr/config b/fwupd-1.8.6/plugins/pci-bcr/config new file mode 100644 index 0000000000000000000000000000000000000000..7d79f10fbee5a4d3f8443503d26591c517628adc Binary files /dev/null and b/fwupd-1.8.6/plugins/pci-bcr/config differ diff --git a/fwupd-1.8.6/plugins/pci-bcr/fu-pci-bcr-plugin.c b/fwupd-1.8.6/plugins/pci-bcr/fu-pci-bcr-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..201decfd9e358edbac1cb2ca4d628d5235368ffc --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-bcr/fu-pci-bcr-plugin.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-pci-bcr-plugin.h" + +struct _FuPciBcrPlugin { + FuPlugin parent_instance; + gboolean has_device; + guint8 bcr_addr; + guint8 bcr; +}; + +G_DEFINE_TYPE(FuPciBcrPlugin, fu_pci_bcr_plugin, FU_TYPE_PLUGIN) + +#define BCR_WPD (1 << 0) +#define BCR_BLE (1 << 1) +#define BCR_SMM_BWP (1 << 5) + +static void +fu_pci_bcr_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + fu_string_append_kb(str, idt, "HasDevice", self->has_device); + fu_string_append_kx(str, idt, "BcrAddr", self->bcr_addr); + fu_string_append_kx(str, idt, "Bcr", self->bcr); +} + +static void +fu_pci_bcr_plugin_set_updatable(FuPlugin *plugin, FuDevice *dev) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + if ((self->bcr & BCR_WPD) == 0 && (self->bcr & BCR_BLE) > 0) { + fu_device_inhibit(dev, "bcr-locked", "BIOS locked"); + } else { + fu_device_uninhibit(dev, "bcr-locked"); + } +} + +static void +fu_pci_bcr_plugin_device_registered(FuPlugin *plugin, FuDevice *dev) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + if (g_strcmp0(fu_device_get_plugin(dev), "cpu") == 0 || + g_strcmp0(fu_device_get_plugin(dev), "flashrom") == 0) { + guint tmp = fu_device_get_metadata_integer(dev, "PciBcrAddr"); + if (tmp != G_MAXUINT && self->bcr_addr != tmp) { + g_debug("overriding BCR addr from 0x%02x to 0x%02x", self->bcr_addr, tmp); + self->bcr_addr = tmp; + } + } + if (g_strcmp0(fu_device_get_plugin(dev), "flashrom") == 0 && + fu_device_has_instance_id(dev, "main-system-firmware")) { + /* PCI\VEN_8086 added first */ + if (self->has_device) { + fu_pci_bcr_plugin_set_updatable(plugin, dev); + return; + } + fu_plugin_cache_add(plugin, "main-system-firmware", dev); + } +} + +static void +fu_plugin_add_security_attr_bioswe(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + FuDevice *msf_device = fu_plugin_cache_lookup(plugin, "main-system-firmware"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE); + if (msf_device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(msf_device)); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (!self->has_device) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* load file */ + if ((self->bcr & BCR_WPD) == 1) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); +} + +static void +fu_plugin_add_security_attr_ble(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + FuDevice *msf_device = fu_plugin_cache_lookup(plugin, "main-system-firmware"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_SPI_BLE); + if (msf_device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(msf_device)); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (!self->has_device) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* load file */ + if ((self->bcr & BCR_BLE) == 0) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attr_smm_bwp(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + FuDevice *msf_device = fu_plugin_cache_lookup(plugin, "main-system-firmware"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP); + if (msf_device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(msf_device)); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (!self->has_device) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* load file */ + if ((self->bcr & BCR_SMM_BWP) == 0) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static gboolean +fu_pci_bcr_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuPciBcrPlugin *self = FU_PCI_BCR_PLUGIN(plugin); + FuDevice *device_msf; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* not supported */ + if (self->bcr_addr == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "BCR not supported on this platform"); + return FALSE; + } + + /* interesting device? */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "pci") != 0) + return TRUE; + + /* open the config */ + fu_udev_device_set_flags(FU_UDEV_DEVICE(device), FU_UDEV_DEVICE_FLAG_USE_CONFIG); + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error)) + return FALSE; + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* grab BIOS Control Register */ + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), self->bcr_addr, &self->bcr, 1, error)) { + g_prefix_error(error, "could not read BCR: "); + return FALSE; + } + + /* main-system-firmware device added first, probably from flashrom */ + device_msf = fu_plugin_cache_lookup(plugin, "main-system-firmware"); + if (device_msf != NULL) + fu_pci_bcr_plugin_set_updatable(plugin, device_msf); + + /* success */ + self->has_device = TRUE; + return TRUE; +} + +static void +fu_pci_bcr_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + /* only Intel */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_INTEL) + return; + + /* add attrs */ + fu_plugin_add_security_attr_bioswe(plugin, attrs); + fu_plugin_add_security_attr_ble(plugin, attrs); + fu_plugin_add_security_attr_smm_bwp(plugin, attrs); +} + +static void +fu_pci_bcr_plugin_init(FuPciBcrPlugin *self) +{ + /* this is true except for some Atoms */ + self->bcr_addr = 0xdc; +} + +static void +fu_pci_bcr_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "PciBcrAddr"); + fu_plugin_add_udev_subsystem(plugin, "pci"); +} + +static void +fu_pci_bcr_plugin_class_init(FuPciBcrPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_pci_bcr_plugin_constructed; + plugin_class->to_string = fu_pci_bcr_plugin_to_string; + plugin_class->add_security_attrs = fu_pci_bcr_plugin_add_security_attrs; + plugin_class->device_registered = fu_pci_bcr_plugin_device_registered; + plugin_class->backend_device_added = fu_pci_bcr_plugin_backend_device_added; +} diff --git a/fwupd-1.8.6/plugins/pci-bcr/fu-pci-bcr-plugin.h b/fwupd-1.8.6/plugins/pci-bcr/fu-pci-bcr-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..4d5a8bddda3cc897740c785f1ad34dfff31c6dca --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-bcr/fu-pci-bcr-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuPciBcrPlugin, fu_pci_bcr_plugin, FU, PCI_BCR_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/pci-bcr/meson.build b/fwupd-1.8.6/plugins/pci-bcr/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d24da517ee0e671f4ae3bef85d3f416877d41fc8 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-bcr/meson.build @@ -0,0 +1,14 @@ +if hsi +cargs = ['-DG_LOG_DOMAIN="FuPluginPciBcr"'] + +plugin_quirks += files('pci-bcr.quirk') +plugin_builtins += static_library('fu_plugin_pci_bcr', + sources: [ + 'fu-pci-bcr-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/pci-bcr/pci-bcr.quirk b/fwupd-1.8.6/plugins/pci-bcr/pci-bcr.quirk new file mode 100644 index 0000000000000000000000000000000000000000..52d930109ba5097061a64cdc210edb8242d049ac --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-bcr/pci-bcr.quirk @@ -0,0 +1,9 @@ +# ISA bridge i.e. 00:1F.0 +# -> drivers/mfd/lpc_ich.c +[PCI\VEN_8086&CLASS_060100] +Plugin = pci_bcr + +# PCI devices, i.e. 00:1F.5 +# -> drivers/mtd/spi-nor/controllers/intel-spi-pci.c +[PCI\VEN_8086&CLASS_0C8000] +Plugin = pci_bcr diff --git a/fwupd-1.8.6/plugins/pci-mei/README.md b/fwupd-1.8.6/plugins/pci-mei/README.md new file mode 100644 index 0000000000000000000000000000000000000000..359fe4e6bd909d9b2018b821afc08bea31538b61 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/README.md @@ -0,0 +1,10 @@ +# PCI MEI + +## Introduction + +This plugin checks if the ME is in Manufacturing Mode. The result will be stored +in an security attribute for HSI. + +## External Interface Access + +This plugin requires read access to the config space of PCI devices (`/sys/class/pci_bus/*/device/config`) diff --git a/fwupd-1.8.6/plugins/pci-mei/fu-mei-common.c b/fwupd-1.8.6/plugins/pci-mei/fu-mei-common.c new file mode 100644 index 0000000000000000000000000000000000000000..1477aa6c877a47675b50903e3eaecdf1a3d0a0d2 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/fu-mei-common.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-mei-common.h" + +const gchar * +fu_mei_common_family_to_string(FuMeiFamily family) +{ + if (family == FU_MEI_FAMILY_SPS) + return "SPS"; + if (family == FU_MEI_FAMILY_TXE) + return "TXE"; + if (family == FU_MEI_FAMILY_ME) + return "ME"; + if (family == FU_MEI_FAMILY_CSME) + return "CSME"; + return "AMT"; +} + +static gint +fu_mei_common_cmp_version(FuMeiVersion *vers1, FuMeiVersion *vers2) +{ + guint16 vers1buf[] = { + vers1->major, + vers1->minor, + vers1->hotfix, + vers1->buildno, + }; + guint16 vers2buf[] = { + vers2->major, + vers2->minor, + vers2->hotfix, + vers2->buildno, + }; + for (guint i = 0; i < 4; i++) { + if (vers1buf[i] < vers2buf[i]) + return -1; + if (vers1buf[i] > vers2buf[i]) + return 1; + } + return 0; +} + +FuMeiIssue +fu_mei_common_is_csme_vulnerable(FuMeiVersion *vers) +{ + struct { + guint8 major_eq; + guint8 minor_eq; + guint8 hotfix_ge; + } verdata[] = {{11, 8, 92}, + {11, 12, 92}, + {11, 22, 92}, + {12, 0, 90}, + {13, 0, 60}, + {13, 30, 30}, + {13, 50, 20}, + {14, 1, 65}, + {14, 5, 45}, + {15, 0, 40}, + {15, 40, 20}, + {0, 0, 0}}; + for (guint i = 0; verdata[i].major_eq != 0; i++) { + if (vers->major == verdata[i].major_eq && vers->minor == verdata[i].minor_eq) { + return vers->hotfix >= verdata[i].hotfix_ge ? FU_MEI_ISSUE_PATCHED + : FU_MEI_ISSUE_VULNERABLE; + } + } + return FU_MEI_ISSUE_NOT_VULNERABLE; +} + +FuMeiIssue +fu_mei_common_is_txe_vulnerable(FuMeiVersion *vers) +{ + struct { + guint8 major_eq; + guint8 minor_eq; + guint8 hotfix_ge; + } verdata[] = {{3, 1, 92}, {4, 0, 45}, {0, 0, 0}}; + for (guint i = 0; verdata[i].major_eq != 0; i++) { + if (vers->major == verdata[i].major_eq && vers->minor == verdata[i].minor_eq) { + return vers->hotfix >= verdata[i].hotfix_ge ? FU_MEI_ISSUE_PATCHED + : FU_MEI_ISSUE_VULNERABLE; + } + } + return FU_MEI_ISSUE_NOT_VULNERABLE; +} + +FuMeiIssue +fu_mei_common_is_sps_vulnerable(FuMeiVersion *vers) +{ + if (vers->major == 3 || vers->major > 5) + return FU_MEI_ISSUE_NOT_VULNERABLE; + if (vers->major == 4) { + if (vers->hotfix < 44) + return FU_MEI_ISSUE_VULNERABLE; + if (vers->platform == 0xA) { /* Purley */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 1, + .hotfix = 4, + .buildno = 339, + }; + if (fu_mei_common_cmp_version(vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0xE) { /* Bakerville */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 0, + .hotfix = 4, + .buildno = 112, + }; + if (fu_mei_common_cmp_version(vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0xB) { /* Harrisonville */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 0, + .hotfix = 4, + .buildno = 193, + }; + if (fu_mei_common_cmp_version(vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0x9) { /* Greenlow */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 1, + .hotfix = 4, + .buildno = 88, + }; + if (vers->minor < 1) + return FU_MEI_ISSUE_NOT_VULNERABLE; + if (fu_mei_common_cmp_version(vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } else if (vers->platform == 0xD) { /* MonteVista */ + FuMeiVersion ver2 = { + .major = 4, + .minor = 8, + .hotfix = 4, + .buildno = 51, + }; + if (fu_mei_common_cmp_version(vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } + return FU_MEI_ISSUE_NOT_VULNERABLE; + } + if (vers->major == 5) { + if (vers->platform == 0x10) { /* Mehlow */ + FuMeiVersion ver2 = {5, 1, 3, 89}; + if (fu_mei_common_cmp_version(vers, &ver2) < 0) + return FU_MEI_ISSUE_VULNERABLE; + } + return FU_MEI_ISSUE_NOT_VULNERABLE; + } + return FU_MEI_ISSUE_PATCHED; +} + +/* HFS1[3:0] Current Working State Values */ +static const char *me_cws_values[] = { + [ME_HFS_CWS_RESET] = "reset", + [ME_HFS_CWS_INIT] = "initializing", + [ME_HFS_CWS_REC] = "recovery", + [ME_HFS_CWS_TEST] = "test", + [ME_HFS_CWS_DISABLED] = "disabled", + [ME_HFS_CWS_NORMAL] = "normal", + [ME_HFS_CWS_WAIT] = "wait", + [ME_HFS_CWS_TRANS] = "transition", + [ME_HFS_CWS_INVALID] = "invalid", +}; + +/* HFS1[8:6] Current Operation State Values */ +static const char *me_opstate_values[] = { + [ME_HFS_STATE_PREBOOT] = "preboot", + [ME_HFS_STATE_M0_UMA] = "m0-with-uma", + [ME_HFS_STATE_M3] = "m3-without-uma", + [ME_HFS_STATE_M0] = "m0-without-uma", + [ME_HFS_STATE_BRINGUP] = "bring-up", + [ME_HFS_STATE_ERROR] = "error", +}; + +/* HFS[19:16] Current Operation Mode Values */ +static const char *me_opmode_values[] = { + [ME_HFS_MODE_NORMAL] = "normal", + [ME_HFS_MODE_DEBUG] = "debug", + [ME_HFS_MODE_DIS] = "disable", + [ME_HFS_MODE_OVER_JMPR] = "override-jumper", + [ME_HFS_MODE_OVER_MEI] = "override-mei", + [ME_HFS_MODE_UNKNOWN_6] = "unknown-6", + [ME_HFS_MODE_MAYBE_SPS] = "maybe-sps", +}; + +/* HFS[15:12] Error Code Values */ +static const char *me_error_values[] = { + [ME_HFS_ERROR_NONE] = "no-error", + [ME_HFS_ERROR_UNCAT] = "uncategorized-failure", + [ME_HFS_ERROR_DISABLED] = "disabled", + [ME_HFS_ERROR_IMAGE] = "image-failure", + [ME_HFS_ERROR_DEBUG] = "debug-failure", +}; + +void +fu_mei_hfsts1_to_string(FuMeiHfsts1 hfsts1, guint idt, GString *str) +{ + fu_string_append(str, idt, "WorkingState", me_cws_values[hfsts1.fields.working_state]); + fu_string_append_kb(str, idt, "MfgMode", hfsts1.fields.mfg_mode); + fu_string_append_kb(str, idt, "FptBad", hfsts1.fields.fpt_bad); + fu_string_append(str, + idt, + "OperationState", + me_opstate_values[hfsts1.fields.operation_state]); + fu_string_append_kb(str, idt, "FwInitComplete", hfsts1.fields.fw_init_complete); + fu_string_append_kb(str, idt, "FtBupLdFlr", hfsts1.fields.ft_bup_ld_flr); + fu_string_append_kb(str, idt, "UpdateInProgress", hfsts1.fields.update_in_progress); + fu_string_append(str, idt, "ErrorCode", me_error_values[hfsts1.fields.error_code]); + fu_string_append(str, idt, "OperationMode", me_opmode_values[hfsts1.fields.operation_mode]); + fu_string_append_kx(str, idt, "ResetCount", hfsts1.fields.reset_count); + fu_string_append_kb(str, idt, "BootOptions_present", hfsts1.fields.boot_options_present); + fu_string_append_kb(str, idt, "BistFinished", hfsts1.fields.bist_finished); + fu_string_append_kb(str, idt, "BistTestState", hfsts1.fields.bist_test_state); + fu_string_append_kb(str, idt, "BistResetRequest", hfsts1.fields.bist_reset_request); + fu_string_append_kx(str, idt, "CurrentPowerSource", hfsts1.fields.current_power_source); + fu_string_append_kb(str, idt, "D3SupportValid", hfsts1.fields.d3_support_valid); + fu_string_append_kb(str, idt, "D0i3SupportValid", hfsts1.fields.d0i3_support_valid); +} + +void +fu_mei_hfsts2_to_string(FuMeiHfsts2 hfsts2, guint idt, GString *str) +{ + fu_string_append_kb(str, idt, "NftpLoadFailure", hfsts2.fields.nftp_load_failure); + fu_string_append_kx(str, idt, "IccProgStatus", hfsts2.fields.icc_prog_status); + fu_string_append_kb(str, idt, "InvokeMebx", hfsts2.fields.invoke_mebx); + fu_string_append_kb(str, idt, "CpuReplaced", hfsts2.fields.cpu_replaced); + fu_string_append_kb(str, idt, "Rsvd0", hfsts2.fields.rsvd0); + fu_string_append_kb(str, idt, "MfsFailure", hfsts2.fields.mfs_failure); + fu_string_append_kb(str, idt, "WarmResetRqst", hfsts2.fields.warm_reset_rqst); + fu_string_append_kb(str, idt, "CpuReplacedValid", hfsts2.fields.cpu_replaced_valid); + fu_string_append_kb(str, idt, "LowPowerState", hfsts2.fields.low_power_state); + fu_string_append_kb(str, idt, "MePowerGate", hfsts2.fields.me_power_gate); + fu_string_append_kb(str, idt, "IpuNeeded", hfsts2.fields.ipu_needed); + fu_string_append_kb(str, idt, "ForcedSafeBoot", hfsts2.fields.forced_safe_boot); + fu_string_append_kx(str, idt, "Rsvd1", hfsts2.fields.rsvd1); + fu_string_append_kb(str, idt, "ListenerChange", hfsts2.fields.listener_change); + fu_string_append_kx(str, idt, "StatusData", hfsts2.fields.status_data); + fu_string_append_kx(str, idt, "CurrentPmevent", hfsts2.fields.current_pmevent); + fu_string_append_kx(str, idt, "Phase", hfsts2.fields.phase); +} + +void +fu_mei_hfsts3_to_string(FuMeiHfsts3 hfsts3, guint idt, GString *str) +{ + fu_string_append_kx(str, idt, "Chunk0", hfsts3.fields.chunk0); + fu_string_append_kx(str, idt, "Chunk1", hfsts3.fields.chunk1); + fu_string_append_kx(str, idt, "Chunk2", hfsts3.fields.chunk2); + fu_string_append_kx(str, idt, "Chunk3", hfsts3.fields.chunk3); + fu_string_append_kx(str, idt, "FwSku", hfsts3.fields.fw_sku); + fu_string_append_kb(str, idt, "EncryptKeyCheck", hfsts3.fields.encrypt_key_check); + fu_string_append_kb(str, idt, "PchConfigChange", hfsts3.fields.pch_config_change); + fu_string_append_kb(str, + idt, + "IbbVerificationResult", + hfsts3.fields.ibb_verification_result); + fu_string_append_kb(str, idt, "IbbVerificationDone", hfsts3.fields.ibb_verification_done); + fu_string_append_kx(str, idt, "Reserved11", hfsts3.fields.reserved_11); + fu_string_append_kx(str, idt, "ActualIbbSize", hfsts3.fields.actual_ibb_size * 1024); + fu_string_append_ku(str, idt, "NumberOfChunks", hfsts3.fields.number_of_chunks); + fu_string_append_kb(str, idt, "EncryptKeyOverride", hfsts3.fields.encrypt_key_override); + fu_string_append_kb(str, idt, "PowerDownMitigation", hfsts3.fields.power_down_mitigation); +} + +void +fu_mei_hfsts4_to_string(FuMeiHfsts4 hfsts4, guint idt, GString *str) +{ + fu_string_append_kx(str, idt, "Rsvd0", hfsts4.fields.rsvd0); + fu_string_append_kb(str, idt, "EnforcementFlow", hfsts4.fields.enforcement_flow); + fu_string_append_kb(str, idt, "SxResumeType", hfsts4.fields.sx_resume_type); + fu_string_append_kb(str, idt, "Rsvd1", hfsts4.fields.rsvd1); + fu_string_append_kb(str, idt, "TpmsDisconnected", hfsts4.fields.tpms_disconnected); + fu_string_append_kb(str, idt, "Rvsd2", hfsts4.fields.rvsd2); + fu_string_append_kb(str, idt, "FwstsValid", hfsts4.fields.fwsts_valid); + fu_string_append_kb(str, idt, "BootGuardSelfTest", hfsts4.fields.boot_guard_self_test); + fu_string_append_kx(str, idt, "Rsvd3", hfsts4.fields.rsvd3); +} + +void +fu_mei_hfsts5_to_string(FuMeiHfsts5 hfsts5, guint idt, GString *str) +{ + fu_string_append_kb(str, idt, "AcmActive", hfsts5.fields.acm_active); + fu_string_append_kb(str, idt, "Valid", hfsts5.fields.valid); + fu_string_append_kb(str, idt, "ResultCodeSource", hfsts5.fields.result_code_source); + fu_string_append_kx(str, idt, "ErrorStatusCode", hfsts5.fields.error_status_code); + fu_string_append_kx(str, idt, "AcmDoneSts", hfsts5.fields.acm_done_sts); + fu_string_append_kx(str, idt, "TimeoutCount", hfsts5.fields.timeout_count); + fu_string_append_kb(str, idt, "ScrtmIndicator", hfsts5.fields.scrtm_indicator); + fu_string_append_kx(str, idt, "IncBootGuardAcm", hfsts5.fields.inc_boot_guard_acm); + fu_string_append_kx(str, idt, "IncKeyManifest", hfsts5.fields.inc_key_manifest); + fu_string_append_kx(str, idt, "IncBootPolicy", hfsts5.fields.inc_boot_policy); + fu_string_append_kx(str, idt, "Rsvd0", hfsts5.fields.rsvd0); + fu_string_append_kb(str, idt, "StartEnforcement", hfsts5.fields.start_enforcement); +} + +void +fu_mei_hfsts6_to_string(FuMeiHfsts6 hfsts6, guint idt, GString *str) +{ + fu_string_append_kb(str, idt, "ForceBootGuardAcm", hfsts6.fields.force_boot_guard_acm); + fu_string_append_kb(str, idt, "CpuDebugDisable", hfsts6.fields.cpu_debug_disable); + fu_string_append_kb(str, idt, "BspInitDisable", hfsts6.fields.bsp_init_disable); + fu_string_append_kb(str, idt, "ProtectBiosEnv", hfsts6.fields.protect_bios_env); + fu_string_append_kx(str, idt, "Rsvd0", hfsts6.fields.rsvd0); + fu_string_append_kx(str, idt, "ErrorEnforcePolicy", hfsts6.fields.error_enforce_policy); + fu_string_append_kb(str, idt, "MeasuredBoot", hfsts6.fields.measured_boot); + fu_string_append_kb(str, idt, "VerifiedBoot", hfsts6.fields.verified_boot); + fu_string_append_kx(str, idt, "BootGuardAcmsvn", hfsts6.fields.boot_guard_acmsvn); + fu_string_append_kx(str, idt, "Kmsvn", hfsts6.fields.kmsvn); + fu_string_append_kx(str, idt, "Bpmsvn", hfsts6.fields.bpmsvn); + fu_string_append_kx(str, idt, "KeyManifestId", hfsts6.fields.key_manifest_id); + fu_string_append_kb(str, idt, "BootPolicyStatus", hfsts6.fields.boot_policy_status); + fu_string_append_kb(str, idt, "Error", hfsts6.fields.error); + fu_string_append_kb(str, idt, "BootGuardDisable", hfsts6.fields.boot_guard_disable); + fu_string_append_kb(str, idt, "FpfDisable", hfsts6.fields.fpf_disable); + fu_string_append_kb(str, idt, "FpfSocLock", hfsts6.fields.fpf_soc_lock); + fu_string_append_kb(str, idt, "TxtSupport", hfsts6.fields.txt_support); +} diff --git a/fwupd-1.8.6/plugins/pci-mei/fu-mei-common.h b/fwupd-1.8.6/plugins/pci-mei/fu-mei-common.h new file mode 100644 index 0000000000000000000000000000000000000000..3c575aab1bb183ee7dfa2f7c8037d4beb415375a --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/fu-mei-common.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +typedef enum { + FU_MEI_FAMILY_UNKNOWN, + FU_MEI_FAMILY_SPS, + FU_MEI_FAMILY_TXE, + FU_MEI_FAMILY_ME, + FU_MEI_FAMILY_CSME, +} FuMeiFamily; + +typedef enum { + FU_MEI_ISSUE_UNKNOWN, + FU_MEI_ISSUE_NOT_VULNERABLE, + FU_MEI_ISSUE_VULNERABLE, + FU_MEI_ISSUE_PATCHED, +} FuMeiIssue; + +typedef struct { + guint8 platform; + guint8 major; + guint8 minor; + guint8 hotfix; + guint16 buildno; +} FuMeiVersion; + +/* Host Firmware Status register 1 */ +typedef union { + guint32 data; + struct { + guint32 working_state : 4; + guint32 mfg_mode : 1; + guint32 fpt_bad : 1; + guint32 operation_state : 3; + guint32 fw_init_complete : 1; + guint32 ft_bup_ld_flr : 1; + guint32 update_in_progress : 1; + guint32 error_code : 4; + guint32 operation_mode : 4; + guint32 reset_count : 4; + guint32 boot_options_present : 1; + guint32 bist_finished : 1; + guint32 bist_test_state : 1; + guint32 bist_reset_request : 1; + guint32 current_power_source : 2; + guint32 d3_support_valid : 1; + guint32 d0i3_support_valid : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts1; + +/* Host Firmware Status Register 2 */ +typedef union { + guint32 data; + struct { + guint32 nftp_load_failure : 1; + guint32 icc_prog_status : 2; + guint32 invoke_mebx : 1; + guint32 cpu_replaced : 1; + guint32 rsvd0 : 1; + guint32 mfs_failure : 1; + guint32 warm_reset_rqst : 1; + guint32 cpu_replaced_valid : 1; + guint32 low_power_state : 1; + guint32 me_power_gate : 1; + guint32 ipu_needed : 1; + guint32 forced_safe_boot : 1; + guint32 rsvd1 : 2; + guint32 listener_change : 1; + guint32 status_data : 8; + guint32 current_pmevent : 4; + guint32 phase : 4; + } __attribute__((packed)) fields; +} FuMeiHfsts2; + +/* Host Firmware Status Register 3 */ +typedef union { + guint32 data; + struct { + guint32 chunk0 : 1; + guint32 chunk1 : 1; + guint32 chunk2 : 1; + guint32 chunk3 : 1; + guint32 fw_sku : 3; + guint32 encrypt_key_check : 1; + guint32 pch_config_change : 1; + guint32 ibb_verification_result : 1; + guint32 ibb_verification_done : 1; + guint32 reserved_11 : 3; + guint32 actual_ibb_size : 14; + guint32 number_of_chunks : 2; + guint32 encrypt_key_override : 1; + guint32 power_down_mitigation : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts3; + +/* Host Firmware Status Register 4 */ +typedef union { + guint32 data; + struct { + guint32 rsvd0 : 9; + guint32 enforcement_flow : 1; + guint32 sx_resume_type : 1; + guint32 rsvd1 : 1; + guint32 tpms_disconnected : 1; + guint32 rvsd2 : 1; + guint32 fwsts_valid : 1; + guint32 boot_guard_self_test : 1; + guint32 rsvd3 : 16; + } __attribute__((packed)) fields; +} FuMeiHfsts4; + +/* Host Firmware Status Register 5 */ +typedef union { + guint32 data; + struct { + guint32 acm_active : 1; + guint32 valid : 1; + guint32 result_code_source : 1; + guint32 error_status_code : 5; + guint32 acm_done_sts : 1; + guint32 timeout_count : 7; + guint32 scrtm_indicator : 1; + guint32 inc_boot_guard_acm : 4; + guint32 inc_key_manifest : 4; + guint32 inc_boot_policy : 4; + guint32 rsvd0 : 2; + guint32 start_enforcement : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts5; + +/* Host Firmware Status Register 6 */ +typedef union { + guint32 data; + struct { + guint32 force_boot_guard_acm : 1; + guint32 cpu_debug_disable : 1; + guint32 bsp_init_disable : 1; + guint32 protect_bios_env : 1; + guint32 rsvd0 : 2; + guint32 error_enforce_policy : 2; + guint32 measured_boot : 1; + guint32 verified_boot : 1; + guint32 boot_guard_acmsvn : 4; + guint32 kmsvn : 4; + guint32 bpmsvn : 4; + guint32 key_manifest_id : 4; + guint32 boot_policy_status : 1; + guint32 error : 1; + guint32 boot_guard_disable : 1; + guint32 fpf_disable : 1; + guint32 fpf_soc_lock : 1; + guint32 txt_support : 1; + } __attribute__((packed)) fields; +} FuMeiHfsts6; + +#define ME_HFS_CWS_RESET 0 +#define ME_HFS_CWS_INIT 1 +#define ME_HFS_CWS_REC 2 +#define ME_HFS_CWS_TEST 3 +#define ME_HFS_CWS_DISABLED 4 +#define ME_HFS_CWS_NORMAL 5 +#define ME_HFS_CWS_WAIT 6 +#define ME_HFS_CWS_TRANS 7 +#define ME_HFS_CWS_INVALID 8 + +#define ME_HFS_STATE_PREBOOT 0 +#define ME_HFS_STATE_M0_UMA 1 +#define ME_HFS_STATE_M3 4 +#define ME_HFS_STATE_M0 5 +#define ME_HFS_STATE_BRINGUP 6 +#define ME_HFS_STATE_ERROR 7 + +#define ME_HFS_ERROR_NONE 0 +#define ME_HFS_ERROR_UNCAT 1 +#define ME_HFS_ERROR_DISABLED 2 +#define ME_HFS_ERROR_IMAGE 3 +#define ME_HFS_ERROR_DEBUG 4 + +#define ME_HFS_MODE_NORMAL 0 +#define ME_HFS_MODE_DEBUG 2 +#define ME_HFS_MODE_DIS 3 +#define ME_HFS_MODE_OVER_JMPR 4 +#define ME_HFS_MODE_OVER_MEI 5 +#define ME_HFS_MODE_UNKNOWN_6 6 +#define ME_HFS_MODE_MAYBE_SPS 7 + +#define ME_HFS_ENFORCEMENT_POLICY_NOTHING 0b00 +#define ME_HFS_ENFORCEMENT_POLICY_SHUTDOWN_TO 0b01 +#define ME_HFS_ENFORCEMENT_POLICY_SHUTDOWN_NOW 0b11 + +const gchar * +fu_mei_common_family_to_string(FuMeiFamily family); +FuMeiIssue +fu_mei_common_is_csme_vulnerable(FuMeiVersion *vers); +FuMeiIssue +fu_mei_common_is_txe_vulnerable(FuMeiVersion *vers); +FuMeiIssue +fu_mei_common_is_sps_vulnerable(FuMeiVersion *vers); + +void +fu_mei_hfsts1_to_string(FuMeiHfsts1 hfsts1, guint idt, GString *str); +void +fu_mei_hfsts2_to_string(FuMeiHfsts2 hfsts2, guint idt, GString *str); +void +fu_mei_hfsts3_to_string(FuMeiHfsts3 hfsts3, guint idt, GString *str); +void +fu_mei_hfsts4_to_string(FuMeiHfsts4 hfsts4, guint idt, GString *str); +void +fu_mei_hfsts5_to_string(FuMeiHfsts5 hfsts5, guint idt, GString *str); +void +fu_mei_hfsts6_to_string(FuMeiHfsts6 hfsts6, guint idt, GString *str); diff --git a/fwupd-1.8.6/plugins/pci-mei/fu-pci-mei-plugin.c b/fwupd-1.8.6/plugins/pci-mei/fu-pci-mei-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..37171ba49b1188b55ef9739543169e73d973fac2 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/fu-pci-mei-plugin.c @@ -0,0 +1,582 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-mei-common.h" +#include "fu-pci-mei-plugin.h" + +struct _FuPciMeiPlugin { + FuPlugin parent_instance; + FuDevice *pci_device; + FuMeiHfsts1 hfsts1; + FuMeiHfsts2 hfsts2; + FuMeiHfsts3 hfsts3; + FuMeiHfsts4 hfsts4; + FuMeiHfsts5 hfsts5; + FuMeiHfsts6 hfsts6; + FuMeiFamily family; + FuMeiVersion vers; + FuMeiIssue issue; +}; + +G_DEFINE_TYPE(FuPciMeiPlugin, fu_pci_mei_plugin, FU_TYPE_PLUGIN) + +#define PCI_CFG_HFS_1 0x40 +#define PCI_CFG_HFS_2 0x48 +#define PCI_CFG_HFS_3 0x60 +#define PCI_CFG_HFS_4 0x64 +#define PCI_CFG_HFS_5 0x68 +#define PCI_CFG_HFS_6 0x6c + +static void +fu_pci_mei_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + fu_string_append(str, idt, "HFSTS1", NULL); + fu_mei_hfsts1_to_string(self->hfsts1, idt + 1, str); + fu_string_append(str, idt, "HFSTS2", NULL); + fu_mei_hfsts2_to_string(self->hfsts2, idt + 1, str); + fu_string_append(str, idt, "HFSTS3", NULL); + fu_mei_hfsts3_to_string(self->hfsts3, idt + 1, str); + fu_string_append(str, idt, "HFSTS4", NULL); + fu_mei_hfsts4_to_string(self->hfsts4, idt + 1, str); + fu_string_append(str, idt, "HFSTS5", NULL); + fu_mei_hfsts5_to_string(self->hfsts5, idt + 1, str); + fu_string_append(str, idt, "HFSTS6", NULL); + fu_mei_hfsts6_to_string(self->hfsts6, idt + 1, str); +} + +static FuMeiFamily +fu_mei_detect_family(FuPlugin *plugin) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + guint8 ver = self->vers.major; + + if (ver == 1 || ver == 2) { + if (self->hfsts1.fields.operation_mode == 0xf) + return FU_MEI_FAMILY_SPS; + return FU_MEI_FAMILY_TXE; + } + if (ver == 3 || ver == 4 || ver == 5) + return FU_MEI_FAMILY_TXE; + if (ver == 6 || ver == 7 || ver == 8 || ver == 9 || ver == 10) + return FU_MEI_FAMILY_ME; + if (ver == 11 || ver == 12 || ver == 13 || ver == 14 || ver == 15 || ver == 16) + return FU_MEI_FAMILY_CSME; + return FU_MEI_FAMILY_UNKNOWN; +} + +static gboolean +fu_mei_parse_fwvers(FuPlugin *plugin, const gchar *fwvers, GError **error) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + guint64 tmp64 = 0; + g_auto(GStrv) lines = NULL; + g_auto(GStrv) sections = NULL; + g_auto(GStrv) split = NULL; + + /* we only care about the first version */ + lines = g_strsplit(fwvers, "\n", -1); + if (g_strv_length(lines) < 1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected data, got %s", + fwvers); + return FALSE; + } + + /* split platform : version */ + sections = g_strsplit(lines[0], ":", -1); + if (g_strv_length(sections) != 2) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected platform:major.minor.micro.build, got %s", + lines[0]); + return FALSE; + } + + /* parse platform and versions */ + if (!fu_strtoull(sections[0], &tmp64, 0, G_MAXUINT8, error)) { + g_prefix_error(error, "failed to process platform version %s: ", sections[0]); + return FALSE; + } + self->vers.platform = tmp64; + split = g_strsplit(sections[1], ".", -1); + if (g_strv_length(split) != 4) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "expected major.minor.micro.build, got %s", + sections[1]); + return FALSE; + } + + if (!fu_strtoull(split[0], &tmp64, 0, G_MAXUINT8, error)) { + g_prefix_error(error, "failed to process major version %s: ", split[0]); + return FALSE; + } + self->vers.major = tmp64; + if (!fu_strtoull(split[1], &tmp64, 0, G_MAXUINT8, error)) { + g_prefix_error(error, "failed to process minor version %s: ", split[1]); + return FALSE; + } + self->vers.minor = tmp64; + if (!fu_strtoull(split[2], &tmp64, 0, G_MAXUINT8, error)) { + g_prefix_error(error, "failed to process hotfix version %s: ", split[2]); + return FALSE; + } + self->vers.hotfix = tmp64; + if (!fu_strtoull(split[3], &tmp64, 0, G_MAXUINT16, error)) { + g_prefix_error(error, "failed to process buildno version %s: ", split[3]); + return FALSE; + } + self->vers.buildno = tmp64; + + /* check the AMT version for issues using the data from: + * https://downloadcenter.intel.com/download/28632 */ + self->family = fu_mei_detect_family(plugin); + if (self->family == FU_MEI_FAMILY_CSME) + self->issue = fu_mei_common_is_csme_vulnerable(&self->vers); + else if (self->family == FU_MEI_FAMILY_TXE) + self->issue = fu_mei_common_is_txe_vulnerable(&self->vers); + else if (self->family == FU_MEI_FAMILY_SPS) + self->issue = fu_mei_common_is_sps_vulnerable(&self->vers); + if (g_getenv("FWUPD_MEI_VERBOSE") != NULL) { + g_debug("%s version parsed as %u.%u.%u", + fu_mei_common_family_to_string(self->family), + self->vers.major, + self->vers.minor, + self->vers.hotfix); + } + return TRUE; +} + +static gboolean +fu_pci_mei_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + const gchar *fwvers; + guint8 buf[4] = {0x0}; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* interesting device? */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "pci") != 0) + return TRUE; + + /* open the config */ + fu_udev_device_set_flags(FU_UDEV_DEVICE(device), FU_UDEV_DEVICE_FLAG_USE_CONFIG); + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error)) + return FALSE; + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* grab MEI config registers */ + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), PCI_CFG_HFS_1, buf, sizeof(buf), error)) { + g_prefix_error(error, "could not read HFS1: "); + return FALSE; + } + self->hfsts1.data = fu_memread_uint32(buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), PCI_CFG_HFS_2, buf, sizeof(buf), error)) { + g_prefix_error(error, "could not read HFS2: "); + return FALSE; + } + self->hfsts2.data = fu_memread_uint32(buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), PCI_CFG_HFS_3, buf, sizeof(buf), error)) { + g_prefix_error(error, "could not read HFS3: "); + return FALSE; + } + self->hfsts3.data = fu_memread_uint32(buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), PCI_CFG_HFS_4, buf, sizeof(buf), error)) { + g_prefix_error(error, "could not read HFS4: "); + return FALSE; + } + self->hfsts4.data = fu_memread_uint32(buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), PCI_CFG_HFS_5, buf, sizeof(buf), error)) { + g_prefix_error(error, "could not read HFS5: "); + return FALSE; + } + self->hfsts5.data = fu_memread_uint32(buf, G_LITTLE_ENDIAN); + if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), PCI_CFG_HFS_6, buf, sizeof(buf), error)) { + g_prefix_error(error, "could not read HFS6: "); + return FALSE; + } + self->hfsts6.data = fu_memread_uint32(buf, G_LITTLE_ENDIAN); + g_set_object(&self->pci_device, device); + + /* check firmware version */ + fwvers = fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), "mei/mei0/fw_ver", NULL); + if (fwvers != NULL) { + if (!fu_mei_parse_fwvers(plugin, fwvers, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_plugin_add_security_attrs_manufacturing_mode(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* Manufacturing Mode */ + fwupd_security_attr_add_metadata(attr, + "kind", + fu_mei_common_family_to_string(self->family)); + if (self->hfsts1.fields.mfg_mode) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static void +fu_plugin_add_security_attrs_override_strap(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* Flash Descriptor Security Override Strap */ + fwupd_security_attr_add_metadata(attr, + "kind", + fu_mei_common_family_to_string(self->family)); + if (self->hfsts1.fields.operation_mode == ME_HFS_MODE_OVER_JMPR) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); +} + +static void +fu_plugin_add_security_attrs_bootguard_enabled(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* not supported */ + if (self->family == FU_MEI_FAMILY_TXE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* disabled at runtime? */ + if (self->hfsts6.fields.boot_guard_disable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static void +fu_plugin_add_security_attrs_bootguard_verified(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* not supported */ + if (self->family == FU_MEI_FAMILY_TXE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* actively disabled */ + if (self->hfsts6.fields.boot_guard_disable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* measured boot is not sufficient, verified is required */ + if (!self->hfsts6.fields.verified_boot) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard_acm(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* not supported */ + if (self->family == FU_MEI_FAMILY_TXE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* actively disabled */ + if (self->hfsts6.fields.boot_guard_disable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* ACM protection required */ + if (!self->hfsts6.fields.force_boot_guard_acm) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard_policy(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* not supported */ + if (self->family == FU_MEI_FAMILY_TXE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* actively disabled */ + if (self->hfsts6.fields.boot_guard_disable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* policy must be to immediately shutdown */ + if (self->hfsts6.fields.error_enforce_policy != ME_HFS_ENFORCEMENT_POLICY_SHUTDOWN_NOW) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard_otp(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP); + fu_security_attrs_append(attrs, attr); + + /* no device */ + if (self->pci_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* not supported */ + if (self->family == FU_MEI_FAMILY_TXE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + /* actively disabled */ + if (self->hfsts6.fields.boot_guard_disable) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* ensure vendor set the FPF OTP fuse */ + if (!self->hfsts6.fields.fpf_soc_lock) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_plugin_add_security_attrs_bootguard(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + fu_plugin_add_security_attrs_bootguard_enabled(plugin, attrs); + fu_plugin_add_security_attrs_bootguard_verified(plugin, attrs); + fu_plugin_add_security_attrs_bootguard_acm(plugin, attrs); + fu_plugin_add_security_attrs_bootguard_policy(plugin, attrs); + fu_plugin_add_security_attrs_bootguard_otp(plugin, attrs); +} + +static void +fu_plugin_add_security_attrs_mei_version(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(plugin); + g_autofree gchar *version = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_MEI_VERSION); + fu_security_attrs_append(attrs, attr); + + /* not enabled */ + if (self->pci_device == NULL) { + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + return; + } + + /* format version as string */ + version = g_strdup_printf("%u:%u.%u.%u.%u", + self->vers.platform, + self->vers.major, + self->vers.minor, + self->vers.hotfix, + self->vers.buildno); + if (self->issue == FU_MEI_ISSUE_UNKNOWN) { + g_warning("ME family not supported for %s", version); + return; + } + fwupd_security_attr_add_metadata(attr, "version", version); + fwupd_security_attr_add_metadata(attr, + "kind", + fu_mei_common_family_to_string(self->family)); + + /* Flash Descriptor Security Override Strap */ + if (self->issue == FU_MEI_ISSUE_VULNERABLE) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_mei_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + /* only Intel */ + if (fu_cpu_get_vendor() != FU_CPU_VENDOR_INTEL) + return; + + fu_plugin_add_security_attrs_manufacturing_mode(plugin, attrs); + fu_plugin_add_security_attrs_override_strap(plugin, attrs); + fu_plugin_add_security_attrs_bootguard(plugin, attrs); + fu_plugin_add_security_attrs_mei_version(plugin, attrs); +} + +static void +fu_pci_mei_plugin_init(FuPciMeiPlugin *self) +{ +} + +static void +fu_pci_mei_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "pci"); +} + +static void +fu_pci_mei_finalize(GObject *obj) +{ + FuPciMeiPlugin *self = FU_PCI_MEI_PLUGIN(obj); + if (self->pci_device != NULL) + g_object_unref(self->pci_device); + G_OBJECT_CLASS(fu_pci_mei_plugin_parent_class)->finalize(obj); +} + +static void +fu_pci_mei_plugin_class_init(FuPciMeiPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_pci_mei_plugin_constructed; + object_class->finalize = fu_pci_mei_finalize; + plugin_class->to_string = fu_pci_mei_plugin_to_string; + plugin_class->add_security_attrs = fu_pci_mei_plugin_add_security_attrs; + plugin_class->backend_device_added = fu_pci_mei_plugin_backend_device_added; +} diff --git a/fwupd-1.8.6/plugins/pci-mei/fu-pci-mei-plugin.h b/fwupd-1.8.6/plugins/pci-mei/fu-pci-mei-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..1e15f152b320ad8be88a51290a7ac34fb734815b --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/fu-pci-mei-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuPciMeiPlugin, fu_pci_mei_plugin, FU, PCI_MEI_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/pci-mei/meson.build b/fwupd-1.8.6/plugins/pci-mei/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..ee9f7b55f5501c88d68d20bb0c34caac8e855d10 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/meson.build @@ -0,0 +1,15 @@ +if hsi +cargs = ['-DG_LOG_DOMAIN="FuPluginPciMei"'] + +plugin_quirks += files('pci-mei.quirk') +plugin_builtins += static_library('fu_plugin_pci_mei', + sources: [ + 'fu-pci-mei-plugin.c', + 'fu-mei-common.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/pci-mei/pci-mei.quirk b/fwupd-1.8.6/plugins/pci-mei/pci-mei.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b663d1e618b5f385282e146404f79010d44ab704 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-mei/pci-mei.quirk @@ -0,0 +1,2 @@ +[PCI\DRIVER_mei_me] +Plugin = pci_mei diff --git a/fwupd-1.8.6/plugins/pci-psp/README.md b/fwupd-1.8.6/plugins/pci-psp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e2956a8ee759e8287762ab48b04969875299211c --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/README.md @@ -0,0 +1,16 @@ +# PCI PSP + +## Introduction + +This plugin checks all information reported from the AMD Platform Security processor into +the operating system on select SOCs. + +The lack of these sysfs files does *NOT* indicate the lack of these security features, it only +indicates the lack of the ability to export it to the operating system. + +The availability of the sysfs files indicates that the PSP supports exporting this information +into the operating system. + +## External Interface Access + +This plugin requires read only access to attributes located within `/sys/bus/pci/devices/`. diff --git a/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-device.c b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-device.c new file mode 100644 index 0000000000000000000000000000000000000000..5d3726fbebbc673564ed84ac116b52bddb4e4d0a --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-device.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2022 Advanced Micro Devices Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-pci-psp-device.h" + +struct _FuPciPspDevice { + FuUdevDevice parent_instance; +}; + +G_DEFINE_TYPE(FuPciPspDevice, fu_pci_psp_device, FU_TYPE_UDEV_DEVICE) + +static gboolean +fu_pci_psp_device_get_attr(FwupdSecurityAttr *attr, + const gchar *path, + const gchar *file, + gboolean *out, + GError **error) +{ + guint64 val = 0; + g_autofree gchar *fn = g_build_filename(path, file, NULL); + g_autofree gchar *buf = NULL; + gsize bufsz = 0; + + if (!g_file_get_contents(fn, &buf, &bufsz, error)) { + g_prefix_error(error, "could not open %s: ", fn); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + return FALSE; + } + if (!fu_strtoull(buf, &val, 0, G_MAXUINT32, error)) + return FALSE; + *out = val ? TRUE : FALSE; + return TRUE; +} + +static void +fu_pci_psp_device_add_security_attrs_tsme(FuDevice *device, + const gchar *path, + FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + gboolean val; + + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + fu_security_attrs_append(attrs, attr); + + if (!fu_pci_psp_device_get_attr(attr, path, "tsme_status", &val, &error_local)) { + g_debug("%s", error_local->message); + return; + } + + /* BIOS knob used on Lenovo systems */ + fu_security_attr_add_bios_target_value(attr, "com.thinklmi.TSME", "enable"); + + if (!val) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + return; + } + + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED); + fwupd_security_attr_add_obsolete(attr, "msr"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_psp_device_add_security_attrs_fused_part(FuDevice *device, + const gchar *path, + FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + gboolean val; + + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED); + fu_security_attrs_append(attrs, attr); + + if (!fu_pci_psp_device_get_attr(attr, path, "fused_part", &val, &error_local)) { + g_debug("%s", error_local->message); + return; + } + + if (!val) { + g_debug("part is not fused"); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_psp_device_add_security_attrs_debug_locked_part(FuDevice *device, + const gchar *path, + FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + gboolean val; + + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED); + fu_security_attrs_append(attrs, attr); + + if (!fu_pci_psp_device_get_attr(attr, path, "debug_lock_on", &val, &error_local)) { + g_debug("%s", error_local->message); + return; + } + + if (!val) { + g_debug("debug lock disabled"); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_psp_device_add_security_attrs_rollback_protection(FuDevice *device, + const gchar *path, + FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + gboolean val; + + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION); + fu_security_attrs_append(attrs, attr); + + if (!fu_pci_psp_device_get_attr(attr, path, "anti_rollback_status", &val, &error_local)) { + g_debug("%s", error_local->message); + return; + } + + if (!val) { + g_debug("rollback protection not enforced"); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_psp_device_add_security_attrs_rom_armor(FuDevice *device, + const gchar *path, + FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + gboolean val; + + /* create attr */ + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION); + fu_security_attrs_append(attrs, attr); + + if (!fu_pci_psp_device_get_attr(attr, path, "rom_armor_enforced", &val, &error_local)) { + g_debug("%s", error_local->message); + return; + } + + if (!val) { + g_debug("ROM armor not enforced"); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_psp_device_add_security_attrs_rpmc(FuDevice *device, + const gchar *path, + FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error_local = NULL; + gboolean val; + + /* create attr */ + attr = + fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION); + fu_security_attrs_append(attrs, attr); + + if (!fu_pci_psp_device_get_attr(attr, path, "rpmc_spirom_available", &val, &error_local)) { + g_debug("%s", error_local->message); + return; + } + + if (!val) { + g_debug("no RPMC compatible SPI rom present"); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); + return; + } + + if (!fu_pci_psp_device_get_attr(attr, + path, + "rpmc_production_enabled", + &val, + &error_local)) { + g_debug("%s", error_local->message); + return; + } + + if (!val) { + g_debug("no RPMC compatible SPI rom present"); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_pci_psp_device_set_missing_data(FuDevice *device, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + + attr = fu_device_security_attr_new(device, FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU); + fwupd_security_attr_add_obsolete(attr, "cpu"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + fu_security_attrs_append(attrs, attr); +} + +static void +fu_pci_psp_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) +{ + const gchar *sysfs_path = NULL; + g_autofree gchar *test_file = NULL; + + /* ccp not loaded */ + if (device != NULL) { + sysfs_path = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)); + test_file = g_build_filename(sysfs_path, "tsme_status", NULL); + } + if (sysfs_path == NULL || !g_file_test(test_file, G_FILE_TEST_EXISTS)) { + fu_pci_psp_device_set_missing_data(device, attrs); + return; + } + + fu_pci_psp_device_add_security_attrs_tsme(device, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_fused_part(device, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_debug_locked_part(device, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_rollback_protection(device, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_rpmc(device, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_rom_armor(device, sysfs_path, attrs); +} + +static void +fu_pci_psp_device_init(FuPciPspDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "Secure Processor"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_parent_guid(FU_DEVICE(self), "cpu"); + fu_device_set_vendor(FU_DEVICE(self), "AMD"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_physical_id(FU_DEVICE(self), "pci-psp"); +} + +static void +fu_pci_psp_device_class_init(FuPciPspDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->add_security_attrs = fu_pci_psp_device_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-device.h b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-device.h new file mode 100644 index 0000000000000000000000000000000000000000..44990342b39e93beffdaa2f4de929e2a50f3f38d --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Advanced Micro Devices Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PCI_PSP_DEVICE (fu_pci_psp_device_get_type()) +G_DECLARE_FINAL_TYPE(FuPciPspDevice, fu_pci_psp_device, FU, PCI_PSP_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-plugin.c b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..aa871a57f758ec7099a7c2cb4b5b7e89fe2a7775 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Advanced Micro Devices Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-pci-psp-device.h" +#include "fu-pci-psp-plugin.h" + +struct _FuPciPspPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuPciPspPlugin, fu_pci_psp_plugin, FU_TYPE_PLUGIN) + +static void +fu_pci_psp_plugin_init(FuPciPspPlugin *self) +{ +} + +static void +fu_pci_psp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "pci"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_PCI_PSP_DEVICE); +} + +static void +fu_pci_psp_plugin_class_init(FuPciPspPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_pci_psp_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-plugin.h b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..aa67ead595c8634dd403b50d60b45b5316468ead --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/fu-pci-psp-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuPciPspPlugin, fu_pci_psp_plugin, FU, PCI_PSP_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/pci-psp/meson.build b/fwupd-1.8.6/plugins/pci-psp/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..193dfbdba2d69d6e80dd05b793eae705fbd011cd --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/meson.build @@ -0,0 +1,15 @@ +if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginPciPsp"'] + +plugin_quirks += files('pci-psp.quirk') +plugin_builtins += static_library('fu_plugin_pci_psp', + sources: [ + 'fu-pci-psp-plugin.c', + 'fu-pci-psp-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/pci-psp/pci-psp.quirk b/fwupd-1.8.6/plugins/pci-psp/pci-psp.quirk new file mode 100644 index 0000000000000000000000000000000000000000..1f0709d336162b914489ef6a3d23ffdcbb0c1e11 --- /dev/null +++ b/fwupd-1.8.6/plugins/pci-psp/pci-psp.quirk @@ -0,0 +1,2 @@ +[PCI\DRIVER_ccp] +Plugin = pci_psp diff --git a/fwupd-1.8.6/plugins/pixart-rf/README.md b/fwupd-1.8.6/plugins/pixart-rf/README.md new file mode 100644 index 0000000000000000000000000000000000000000..82e67df946d802bf7f31f6f5e8586fe2c8232103 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/README.md @@ -0,0 +1,40 @@ +# PixArt RF Devices + +## Introduction + +This plugin allows the user to update any supported Pixart RF Device using a +custom HID-based OTA protocol + +## 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.pixart.rf + +## GUID Generation + +These devices use the standard HIDRAW DeviceInstanceId values for both +Pixart Imaging, Inc and Primax Electronics, Ltd, e.g. + +* `HIDRAW\VEN_093A&DEV_2801` +* `HIDRAW\VEN_0461&DEV_4EEF` +* `HIDRAW\VEN_0461&DEV_4EEF&NAME_${NAME}` +* `HIDRAW\VEN_0461&DEV_4EEF&MODEL_${MODEL_NAME}` + +Additionally, a custom GUID values including the name is used, e.g. + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x093A` + +## External Interface Access + +This plugin requires ioctl `HIDIOCSFEATURE` and `HIDIOCGFEATURE` access. diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-ble-device.c b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-ble-device.c new file mode 100644 index 0000000000000000000000000000000000000000..c3005b1571edcffee18cffa813bbaf189d7f7b4e --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-ble-device.c @@ -0,0 +1,927 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_HIDRAW_H +#include +#include +#endif + +#include + +#include "fu-pxi-ble-device.h" +#include "fu-pxi-common.h" +#include "fu-pxi-firmware.h" + +#define PXI_HID_DEV_OTA_INPUT_REPORT_ID 0x05 +#define PXI_HID_DEV_OTA_RETRANSMIT_REPORT_ID 0x06 +#define PXI_HID_DEV_OTA_FEATURE_REPORT_ID 0x07 + +#define PXI_HID_DEV_OTA_REPORT_USAGE_PAGE 0xff02u +#define PXI_HID_DEV_OTA_RETRANSMIT_USAGE_PAGE 0xff01u + +#define ERR_COMMAND_SUCCESS 0x0 + +#define FU_PXI_DEVICE_OBJECT_SIZE_MAX 4096 /* bytes */ +#define FU_PXI_BLE_DEVICE_OTA_BUF_SZ 512 /* bytes */ +#define FU_PXI_BLE_DEVICE_NOTIFY_RET_LEN 4 /* bytes */ +#define FU_PXI_BLE_DEVICE_FW_INFO_RET_LEN 8 /* bytes */ + +#define FU_PXI_BLE_DEVICE_NOTIFY_TIMEOUT_MS 5000 + +#define FU_PXI_BLE_DEVICE_SET_REPORT_RETRIES 10 + +/* OTA target selection */ +enum ota_process_setting { + OTA_MAIN_FW, /* Main firmware */ + OTA_HELPER_FW, /* Helper firmware */ + OTA_EXTERNAL_RESOURCE, /* External resource */ +}; + +struct _FuPxiBleDevice { + FuUdevDevice parent_instance; + struct ota_fw_state fwstate; + guint8 retransmit_id; + gchar *model_name; +}; + +G_DEFINE_TYPE(FuPxiBleDevice, fu_pxi_ble_device, FU_TYPE_UDEV_DEVICE) + +#ifdef HAVE_HIDRAW_H +static gboolean +fu_pxi_ble_device_get_raw_info(FuPxiBleDevice *self, struct hidraw_devinfo *info, GError **error) +{ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGRAWINFO, + (guint8 *)info, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + return TRUE; +} +#endif + +static void +fu_pxi_ble_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(device); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_pxi_ble_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "ModelName", self->model_name); + fu_pxi_ota_fw_state_to_string(&self->fwstate, idt, str); + fu_string_append_kx(str, idt, "RetransmitID", self->retransmit_id); +} + +static FuFirmware * +fu_pxi_ble_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(device); + const gchar *model_name; + g_autoptr(FuFirmware) firmware = fu_pxi_firmware_new(); + + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check is compatible with hardware */ + model_name = fu_pxi_firmware_get_model_name(FU_PXI_FIRMWARE(firmware)); + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if (self->model_name == NULL || model_name == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "legacy device or firmware detected, " + "--force required"); + return NULL; + } + if (g_strcmp0(self->model_name, model_name) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incompatible firmware, got %s, expected %s.", + model_name, + self->model_name); + return NULL; + } + } + + return g_steal_pointer(&firmware); +} + +#ifdef HAVE_HIDRAW_H +static gboolean +fu_pxi_ble_device_set_feature_cb(FuDevice *device, gpointer user_data, GError **error) +{ + GByteArray *req = (GByteArray *)user_data; + return fu_udev_device_ioctl(FU_UDEV_DEVICE(device), + HIDIOCSFEATURE(req->len), + (guint8 *)req->data, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error); +} +#endif + +static gboolean +fu_pxi_ble_device_set_feature(FuPxiBleDevice *self, GByteArray *req, GError **error) +{ +#ifdef HAVE_HIDRAW_H + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "SetFeature", req->data, req->len); + } + return fu_device_retry(FU_DEVICE(self), + fu_pxi_ble_device_set_feature_cb, + FU_PXI_BLE_DEVICE_SET_REPORT_RETRIES, + req, + error); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_ble_device_get_feature(FuPxiBleDevice *self, guint8 *buf, guint bufsz, GError **error) +{ +#ifdef HAVE_HIDRAW_H + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGFEATURE(bufsz), + buf, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetFeature", buf, bufsz); + + /* prepend the report-id and cmd for versions of bluez that do not have + * https://github.com/bluez/bluez/commit/35a2c50437cca4d26ac6537ce3a964bb509c9b62 */ + if (bufsz > 2 && buf[0] != PXI_HID_DEV_OTA_FEATURE_REPORT_ID) { + g_debug("doing fixup for old bluez version"); + memmove(buf + 2, buf, bufsz - 2); + buf[0] = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; + buf[1] = 0x0; + } + + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_ble_device_search_hid_usage_page(guint8 *report_descriptor, + gint size, + guint8 *usage_page, + guint8 usage_page_sz) +{ + gint pos = 0; + + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "target usage_page", usage_page, usage_page_sz); + } + + while (pos < size) { + /* HID info define by HID specification */ + guint8 item = report_descriptor[pos]; + guint8 report_size = item & 0x03; + guint8 report_tag = item & 0xF0; + guint8 usage_page_tmp[4] = {0x00}; + + report_size = (report_size == 3) ? 4 : report_size; + + if (report_tag != 0) { + pos += report_size + 1; + continue; + } + + memmove(usage_page_tmp, &report_descriptor[pos + 1], report_size); + if (memcmp(usage_page, usage_page_tmp, usage_page_sz) == 0) { + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) { + g_debug("hit item: %x ", item); + fu_dump_raw(G_LOG_DOMAIN, "usage_page", usage_page, report_size); + g_debug("hit pos %d", pos); + } + return TRUE; /* finished processing */ + } + pos += report_size + 1; + } + + return FALSE; /* finished processing */ +} + +static gboolean +fu_pxi_ble_device_check_support_report_id(FuPxiBleDevice *self, GError **error) +{ +#ifdef HAVE_HIDRAW_H + gint desc_size = 0; + g_autoptr(GByteArray) req = g_byte_array_new(); + + struct hidraw_report_descriptor rpt_desc; + + /* Get Report Descriptor Size */ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGRDESCSIZE, + (guint8 *)&desc_size, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + + rpt_desc.size = desc_size; + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGRDESC, + (guint8 *)&rpt_desc, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "HID descriptor", rpt_desc.value, rpt_desc.size); + + /* check ota retransmit feature report usage page exist or not */ + fu_byte_array_append_uint16(req, PXI_HID_DEV_OTA_RETRANSMIT_USAGE_PAGE, G_LITTLE_ENDIAN); + if (!fu_pxi_ble_device_search_hid_usage_page(rpt_desc.value, + rpt_desc.size, + req->data, + req->len)) { + /* replace retransmit report id with feature report id, if retransmit report id not + * found */ + self->retransmit_id = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; + } + return TRUE; + +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE +#endif +} + +static gboolean +fu_pxi_ble_device_fw_ota_check_retransmit(FuPxiBleDevice *self, GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* write fw ota retransmit command to reset the ota state */ + fu_byte_array_append_uint8(req, self->retransmit_id); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_OTA_RETRANSMIT); + return fu_pxi_ble_device_set_feature(self, req, error); +} + +static gboolean +fu_pxi_ble_device_check_support_resume(FuPxiBleDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + guint16 checksum_tmp = 0x0; + + /* get the default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* check offset is invalid or not */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, FU_PXI_DEVICE_OBJECT_SIZE_MAX); + if (self->fwstate.offset > chunks->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "offset from device is invalid: " + "got 0x%x, current maximum 0x%x", + self->fwstate.offset, + chunks->len); + return FALSE; + } + + /* calculate device current checksum */ + for (guint i = 0; i < self->fwstate.offset; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + checksum_tmp += fu_sum16(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + } + + /* check current file is different with previous fw bin or not */ + if (self->fwstate.checksum != checksum_tmp) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "checksum is different from previous fw: " + "got 0x%04x, expected 0x%04x", + self->fwstate.checksum, + checksum_tmp); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_wait_notify(FuPxiBleDevice *self, + goffset port, + guint8 *status, + guint16 *checksum, + GError **error) +{ + g_autoptr(GTimer) timer = g_timer_new(); + guint8 res[FU_PXI_BLE_DEVICE_OTA_BUF_SZ] = {0}; + guint8 cmd_status = 0x0; + + /* skip the wrong report id ,and keep polling until result is correct */ + while (g_timer_elapsed(timer, NULL) * 1000.f < FU_PXI_BLE_DEVICE_NOTIFY_TIMEOUT_MS) { + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), + port, + res, + (FU_PXI_BLE_DEVICE_NOTIFY_RET_LEN + 1) - port, + error)) + return FALSE; + if (res[0] == PXI_HID_DEV_OTA_INPUT_REPORT_ID) + break; + } + + /* timeout */ + if (res[0] != PXI_HID_DEV_OTA_INPUT_REPORT_ID) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Timed-out waiting for HID report"); + return FALSE; + } + /* get the opcode if status is not null */ + if (status != NULL) { + guint8 status_tmp = 0x0; + if (!fu_memread_uint8_safe(res, sizeof(res), 0x1, &status_tmp, error)) + return FALSE; + /* need check command result if command is fw upgrade */ + if (status_tmp == FU_PXI_DEVICE_CMD_FW_UPGRADE) { + if (!fu_memread_uint8_safe(res, sizeof(res), 0x2, &cmd_status, error)) + return FALSE; + if (cmd_status != ERR_COMMAND_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cmd status was 0x%02x", + cmd_status); + return FALSE; + } + } + + /* propagate */ + *status = status_tmp; + } + if (checksum != NULL) { + if (!fu_memread_uint16_safe(res, + sizeof(res), + 0x3, + checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_fw_object_create(FuPxiBleDevice *self, FuChunk *chk, GError **error) +{ + guint8 opcode = 0; + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* request */ + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE); + fu_byte_array_append_uint32(req, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(req, fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); + if (!fu_pxi_ble_device_set_feature(self, req, error)) + return FALSE; + + /* check object create success or not */ + if (!fu_pxi_ble_device_wait_notify(self, 0x0, &opcode, NULL, error)) + return FALSE; + if (opcode != FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "FwObjectCreate opcode got 0x%02x, expected 0x%02x", + opcode, + FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_write_payload(FuPxiBleDevice *self, FuChunk *chk, GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + return fu_pxi_ble_device_set_feature(self, req, error); +} + +static gboolean +fu_pxi_ble_device_write_chunk(FuPxiBleDevice *self, FuChunk *chk, GError **error) +{ + guint32 prn = 0; + guint16 checksum; + guint16 checksum_device = 0; + g_autoptr(GPtrArray) chunks = NULL; + + /* send create fw object command */ + if (!fu_pxi_ble_device_fw_object_create(self, chk, error)) + return FALSE; + + /* write payload */ + chunks = fu_chunk_array_new(fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_chunk_get_address(chk), + 0x0, + self->fwstate.mtu_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk2 = g_ptr_array_index(chunks, i); + if (!fu_pxi_ble_device_write_payload(self, chk2, error)) + return FALSE; + prn++; + /* wait notify from device when PRN over threshold write or + * offset reach max object sz or write offset reach fw length */ + if (prn >= self->fwstate.prn_threshold || i == chunks->len - 1) { + guint8 opcode = 0; + if (!fu_pxi_ble_device_wait_notify(self, + 0x0, + &opcode, + &checksum_device, + error)) + return FALSE; + if (opcode != FU_PXI_DEVICE_CMD_FW_WRITE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "FwWrite opcode invalid 0x%02x", + opcode); + return FALSE; + } + prn = 0; + } + } + + /* the last chunk */ + checksum = fu_sum16(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + self->fwstate.checksum += checksum; + if (checksum_device != self->fwstate.checksum) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "checksum fail, got 0x%04x, expected 0x%04x", + checksum_device, + checksum); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_reset(FuPxiBleDevice *self, GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_MCU_RESET); /* OTA reset command */ + fu_byte_array_append_uint8(req, OTA_RESET); /* OTA reset reason */ + + if (!fu_pxi_ble_device_set_feature(self, req, error)) { + g_prefix_error(error, "failed to reset: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_fw_ota_init(FuPxiBleDevice *self, GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* write fw ota init command */ + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_OTA_INIT); + return fu_pxi_ble_device_set_feature(self, req, error); +} + +static gboolean +fu_pxi_ble_device_fw_ota_init_new(FuPxiBleDevice *self, gsize bufsz, GError **error) +{ + guint8 res[FU_PXI_BLE_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 fw_version[10] = {0x0}; + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* write fw ota init new command */ + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW); + fu_byte_array_append_uint32(req, bufsz, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(req, 0x0); /* OTA setting */ + g_byte_array_append(req, fw_version, sizeof(fw_version)); + if (!fu_pxi_ble_device_set_feature(self, req, error)) + return FALSE; + + /* delay for BLE device read command */ + g_usleep(10 * 1000); + + /* read fw ota init new command */ + res[0] = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; + res[1] = FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW; + if (!fu_pxi_ble_device_get_feature(self, res, sizeof(res), error)) + return FALSE; + + /* shared state */ + if (!fu_pxi_ota_fw_state_parse(&self->fwstate, res, sizeof(res), 0x05, error)) + return FALSE; + if (self->fwstate.spec_check_result != OTA_SPEC_CHECK_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "FwInitNew spec check fail: %s [0x%02x]", + fu_pxi_spec_check_result_to_string(self->fwstate.spec_check_result), + self->fwstate.spec_check_result); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_fw_upgrade(FuPxiBleDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + const gchar *version; + guint8 fw_version[5] = {0x0}; + guint8 opcode = 0; + guint16 checksum; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GByteArray) req = g_byte_array_new(); + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + checksum = fu_sum16_bytes(fw); + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_UPGRADE); + fu_byte_array_append_uint32(req, g_bytes_get_size(fw), G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(req, checksum, G_LITTLE_ENDIAN); + version = fu_firmware_get_version(firmware); + if (!fu_memcpy_safe(fw_version, + sizeof(fw_version), + 0x0, /* dst */ + (guint8 *)version, + strlen(version), + 0x0, /* src */ + strlen(version), + error)) + return FALSE; + g_byte_array_append(req, fw_version, sizeof(fw_version)); + + /* send fw upgrade command */ + if (!fu_pxi_ble_device_set_feature(self, req, error)) + return FALSE; + + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "fw upgrade", req->data, req->len); + + /* wait fw upgrade command result */ + if (!fu_pxi_ble_device_wait_notify(self, 0x1, &opcode, NULL, error)) { + g_prefix_error(error, + "FwUpgrade command fail, " + "fw-checksum: 0x%04x fw-size: %" G_GSIZE_FORMAT ": ", + checksum, + g_bytes_get_size(fw)); + return FALSE; + } + if (opcode != FU_PXI_DEVICE_CMD_FW_UPGRADE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "FwUpgrade opcode invalid 0x%02x", + opcode); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GError) error_local = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 9, "ota-init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "check-support-resume"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, NULL); + + /* get the default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* send fw ota retransmit command to reset status */ + if (!fu_pxi_ble_device_fw_ota_check_retransmit(self, error)) { + g_prefix_error(error, "failed to OTA check retransmit: "); + return FALSE; + } + /* send fw ota init command */ + if (!fu_pxi_ble_device_fw_ota_init(self, error)) + return FALSE; + if (!fu_pxi_ble_device_fw_ota_init_new(self, g_bytes_get_size(fw), error)) + return FALSE; + fu_progress_step_done(progress); + + /* prepare write fw into device */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, FU_PXI_DEVICE_OBJECT_SIZE_MAX); + if (!fu_pxi_ble_device_check_support_resume(self, + firmware, + fu_progress_get_child(progress), + &error_local)) { + g_debug("do not resume: %s", error_local->message); + self->fwstate.offset = 0; + self->fwstate.checksum = 0; + } + fu_progress_step_done(progress); + + /* write fw into device */ + for (guint i = self->fwstate.offset; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_pxi_ble_device_write_chunk(self, chk, error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)self->fwstate.offset + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* fw upgrade command */ + if (!fu_pxi_ble_device_fw_upgrade(self, firmware, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* send device reset command */ + if (!fu_pxi_ble_device_reset(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_fw_get_info(FuPxiBleDevice *self, GError **error) +{ + guint8 res[FU_PXI_BLE_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 opcode = 0x0; + guint16 checksum = 0; + g_autofree gchar *version_str = NULL; + g_autoptr(GByteArray) req = g_byte_array_new(); + + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_GET_INFO); + if (!fu_pxi_ble_device_set_feature(self, req, error)) + return FALSE; + + /* delay for BLE device read command */ + g_usleep(10 * 1000); + + res[0] = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; + res[1] = FU_PXI_DEVICE_CMD_FW_GET_INFO; + + if (!fu_pxi_ble_device_get_feature(self, res, FU_PXI_BLE_DEVICE_FW_INFO_RET_LEN + 3, error)) + return FALSE; + if (!fu_memread_uint8_safe(res, sizeof(res), 0x4, &opcode, error)) + return FALSE; + if (opcode != FU_PXI_DEVICE_CMD_FW_GET_INFO) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "FwGetInfo opcode invalid 0x%02x", + opcode); + return FALSE; + } + /* set current version */ + version_str = g_strndup((gchar *)res + 0x6, 5); + fu_device_set_version(FU_DEVICE(self), version_str); + + /* add current checksum */ + if (!fu_memread_uint16_safe(res, sizeof(res), 0xb, &checksum, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_get_model_info(FuPxiBleDevice *self, GError **error) +{ + guint8 res[FU_PXI_BLE_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 opcode = 0x0; + guint8 model_name[FU_PXI_DEVICE_MODEL_NAME_LEN] = {0x0}; + g_autoptr(GByteArray) req = g_byte_array_new(); + + fu_byte_array_append_uint8(req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8(req, FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL); + + if (!fu_pxi_ble_device_set_feature(self, req, error)) + return FALSE; + + /* delay for BLE device read command */ + g_usleep(10 * 1000); + + res[0] = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; + if (!fu_pxi_ble_device_get_feature(self, res, sizeof(res), error)) + return FALSE; + if (!fu_memread_uint8_safe(res, sizeof(res), 0x4, &opcode, error)) + return FALSE; + + /* old firmware */ + if (opcode != FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL) + return TRUE; + + /* get model from res */ + if (!fu_memcpy_safe(model_name, + sizeof(model_name), + 0x0, /* dst */ + (guint8 *)res, + sizeof(res), + 0x6, /* src */ + sizeof(model_name), + error)) + return FALSE; + g_clear_pointer(&self->model_name, g_free); + if (model_name[0] != 0x00 && model_name[0] != 0xFF) + self->model_name = g_strndup((gchar *)model_name, sizeof(model_name)); + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_ble_device_probe(FuDevice *device, GError **error) +{ + /* set the logical and physical ID */ + if (!fu_udev_device_set_logical_id(FU_UDEV_DEVICE(device), "hid", error)) + return FALSE; + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static gboolean +fu_pxi_ble_device_setup_guid(FuPxiBleDevice *self, GError **error) +{ +#ifdef HAVE_HIDRAW_H + FuDevice *device = FU_DEVICE(self); + struct hidraw_devinfo hid_raw_info = {0x0}; + g_autoptr(GString) dev_name = NULL; + g_autoptr(GString) model_name = NULL; + + /* extra GUID with device name */ + if (!fu_pxi_ble_device_get_raw_info(self, &hid_raw_info, error)) + return FALSE; + dev_name = g_string_new(fu_device_get_name(device)); + g_string_ascii_up(dev_name); + fu_string_replace(dev_name, " ", "_"); + + /* extra GUID with model name*/ + model_name = g_string_new(self->model_name); + g_string_ascii_up(model_name); + fu_string_replace(model_name, " ", "_"); + + /* generate IDs */ + fu_device_add_instance_u16(device, "VEN", hid_raw_info.vendor); + fu_device_add_instance_u16(device, "DEV", hid_raw_info.product); + fu_device_add_instance_str(device, "NAME", dev_name->str); + fu_device_add_instance_str(device, "MODEL", model_name->str); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "NAME", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "MODEL", NULL)) + return FALSE; +#endif + return TRUE; +} + +static gboolean +fu_pxi_ble_device_setup(FuDevice *device, GError **error) +{ + FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(device); + + if (!fu_pxi_ble_device_check_support_report_id(self, error)) { + g_prefix_error(error, "failed to check report id: "); + return FALSE; + } + if (!fu_pxi_ble_device_fw_ota_check_retransmit(self, error)) { + g_prefix_error(error, "failed to OTA check retransmit: "); + return FALSE; + } + if (!fu_pxi_ble_device_fw_ota_init(self, error)) { + g_prefix_error(error, "failed to OTA init: "); + return FALSE; + } + if (!fu_pxi_ble_device_fw_get_info(self, error)) { + g_prefix_error(error, "failed to get info: "); + return FALSE; + } + if (!fu_pxi_ble_device_get_model_info(self, error)) { + g_prefix_error(error, "failed to get model: "); + return FALSE; + } + if (!fu_pxi_ble_device_setup_guid(self, error)) { + g_prefix_error(error, "failed to setup GUID: "); + return FALSE; + } + + return TRUE; +} + +static void +fu_pxi_ble_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_pxi_ble_device_init(FuPxiBleDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_vendor_id(FU_DEVICE(self), "USB:0x093A"); + fu_device_add_protocol(FU_DEVICE(self), "com.pixart.rf"); + fu_device_retry_set_delay(FU_DEVICE(self), 50); + self->retransmit_id = PXI_HID_DEV_OTA_RETRANSMIT_REPORT_ID; +} + +static void +fu_pxi_ble_device_finalize(GObject *object) +{ + FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(object); + g_free(self->model_name); + G_OBJECT_CLASS(fu_pxi_ble_device_parent_class)->finalize(object); +} + +static void +fu_pxi_ble_device_class_init(FuPxiBleDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_pxi_ble_device_finalize; + klass_device->probe = fu_pxi_ble_device_probe; + klass_device->setup = fu_pxi_ble_device_setup; + klass_device->to_string = fu_pxi_ble_device_to_string; + klass_device->write_firmware = fu_pxi_ble_device_write_firmware; + klass_device->prepare_firmware = fu_pxi_ble_device_prepare_firmware; + klass_device->set_progress = fu_pxi_ble_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-ble-device.h b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-ble-device.h new file mode 100644 index 0000000000000000000000000000000000000000..213630c5567d3754b46e505c4f70e362773dbed4 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-ble-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PXI_BLE_DEVICE (fu_pxi_ble_device_get_type()) + +G_DECLARE_FINAL_TYPE(FuPxiBleDevice, fu_pxi_ble_device, FU, PXI_BLE_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-common.c b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-common.c new file mode 100644 index 0000000000000000000000000000000000000000..10955afd1cc7e10bc34f52141d1baf51ba24c2c1 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-common.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2021 Jimmy Yu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-pxi-common.h" + +const gchar * +fu_pxi_spec_check_result_to_string(guint8 spec_check_result) +{ + if (spec_check_result == OTA_SPEC_CHECK_OK) + return "ok"; + if (spec_check_result == OTA_FW_OUT_OF_BOUNDS) + return "fw-out-of-bounds"; + if (spec_check_result == OTA_PROCESS_ILLEGAL) + return "process-illegal"; + if (spec_check_result == OTA_RECONNECT) + return "reconnect"; + if (spec_check_result == OTA_FW_IMG_VERSION_ERROR) + return "fw-img-version-error"; + if (spec_check_result == OTA_DEVICE_LOW_BATTERY) + return "device-low-battery"; + return NULL; +} + +const gchar * +fu_pxi_receiver_cmd_result_to_string(guint8 result) +{ + if (result == OTA_RSP_OK) + return "ok"; + if (result == OTA_RSP_FINISH) + return "ota-response-finish"; + if (result == OTA_RSP_FAIL) + return "ota-response-fail"; + if (result == OTA_RSP_CODE_ERROR) + return "ota-response-error"; + return NULL; +} + +void +fu_pxi_ota_fw_state_to_string(struct ota_fw_state *fwstate, guint idt, GString *str) +{ + fu_string_append_kx(str, idt, "Status", fwstate->status); + fu_string_append_kx(str, idt, "NewFlow", fwstate->new_flow); + fu_string_append_kx(str, idt, "CurrentObjectOffset", fwstate->offset); + fu_string_append_kx(str, idt, "CurrentChecksum", fwstate->checksum); + fu_string_append_kx(str, idt, "MaxObjectSize", fwstate->max_object_size); + fu_string_append_kx(str, idt, "MtuSize", fwstate->mtu_size); + fu_string_append_kx(str, idt, "PacketReceiptNotificationThreshold", fwstate->prn_threshold); + fu_string_append(str, + idt, + "SpecCheckResult", + fu_pxi_spec_check_result_to_string(fwstate->spec_check_result)); +} + +gboolean +fu_pxi_ota_fw_state_parse(struct ota_fw_state *fwstate, + const guint8 *buf, + gsize bufsz, + gsize offset, + GError **error) +{ + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x00, &fwstate->status, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x01, &fwstate->new_flow, error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + 0x2, + &fwstate->offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + 0x4, + &fwstate->checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + 0x06, + &fwstate->max_object_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + 0x0A, + &fwstate->mtu_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + 0x0C, + &fwstate->prn_threshold, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x0E, &fwstate->spec_check_result, error)) + return FALSE; + + /* success */ + return TRUE; +} +gboolean +fu_pxi_composite_receiver_cmd(guint8 opcode, + guint8 sn, + guint8 target, + GByteArray *wireless_mod_cmd, + GByteArray *ota_cmd, + GError **error) +{ + guint8 checksum = 0x0; + guint8 hid_sn = sn; + guint8 len = 0x0; + guint8 ota_sn = sn + 1; + guint8 rf_cmd_code = FU_PXI_BLE_DEVICE_RF_CMD_CODE; + guint8 rid = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; + + if (ota_cmd == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "ota cmd is NULL"); + return FALSE; + } + + /* append ota dispatch header */ + fu_byte_array_append_uint8(wireless_mod_cmd, opcode); /* wirelss module ota op code */ + fu_byte_array_append_uint8(wireless_mod_cmd, ota_sn); /* wirelss module ota command sn */ + + /* append ota command length and content */ + for (guint idx = 0; idx < ota_cmd->len; idx++) + fu_byte_array_append_uint8(wireless_mod_cmd, ota_cmd->data[idx]); + + /* append target of wireless module and hid command serial number */ + g_byte_array_prepend(wireless_mod_cmd, &target, 0x01); /* target */ + g_byte_array_prepend(wireless_mod_cmd, &hid_sn, 0x01); /* hid command sn */ + + /* prepend length and rf command code */ + len = wireless_mod_cmd->len; + g_byte_array_prepend(wireless_mod_cmd, &len, 0x01); + g_byte_array_prepend(wireless_mod_cmd, &rf_cmd_code, 0x01); /* command code */ + + /* prepend checksum */ + checksum = fu_sum8(wireless_mod_cmd->data, wireless_mod_cmd->len); + g_byte_array_prepend(wireless_mod_cmd, &checksum, 0x01); + + /* prepend feature report id */ + g_byte_array_prepend(wireless_mod_cmd, &rid, 0x01); + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-common.h b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-common.h new file mode 100644 index 0000000000000000000000000000000000000000..4f679670c495a06ae3bac1efe33b582b6dfc5cdd --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-common.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 Jimmy Yu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define PXI_HID_WIRELESS_DEV_OTA_REPORT_ID 0x03 + +#define FU_PXI_DEVICE_CMD_FW_OTA_INIT 0x10u +#define FU_PXI_DEVICE_CMD_FW_WRITE 0x17u +#define FU_PXI_DEVICE_CMD_FW_UPGRADE 0x18u +#define FU_PXI_DEVICE_CMD_FW_MCU_RESET 0x22u +#define FU_PXI_DEVICE_CMD_FW_GET_INFO 0x23u +#define FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE 0x25u +#define FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW 0x27u +#define FU_PXI_DEVICE_CMD_FW_OTA_RETRANSMIT 0x28u +#define FU_PXI_DEVICE_CMD_FW_OTA_DISCONNECT 0x29u +#define FU_PXI_DEVICE_CMD_FW_OTA_GET_NUM_OF_MODELS 0x2au +#define FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL 0x2bu +#define FU_PXI_DEVICE_CMD_FW_OTA_PAYLOAD_CONTENT 0x40u +#define FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC 0x41u +#define FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW_CHECK 0x42u + +#define FU_PXI_BLE_DEVICE_RF_CMD_CODE 0x65u +#define FU_PXI_BLE_DEVICE_RF_CMD_HID_SN 0x0 +#define FU_PXI_BLE_DEVICE_RF_CMD_HID_SN 0x0 + +#define FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER 0 +#define FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ 64 /* bytes */ +#define FU_PXI_DEVICE_MODEL_NAME_LEN 12 /* bytes */ + +#define FU_PXI_DEVICE_OBJECT_SIZE_MAX 4096 /* bytes */ + +#define FU_PXI_WIRELESS_DEVICE_RETRY_MAXIMUM 1000 + +#define FU_PXI_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +/* OTA spec check result */ +enum ota_spec_check_result { + OTA_SPEC_CHECK_OK = 1, /* Spec check ok */ + OTA_FW_OUT_OF_BOUNDS = 2, /* OTA firmware size out of bound */ + OTA_PROCESS_ILLEGAL = 3, /* Illegal OTA process */ + OTA_RECONNECT = 4, /* Inform OTA app do reconnect */ + OTA_FW_IMG_VERSION_ERROR = 5, /* FW image file version check error */ + OTA_DEVICE_LOW_BATTERY = 6, /* Device is under low battery */ + OTA_SPEC_CHECK_MAX_NUM, /* Max number of OTA driver defined error code */ +}; + +/* pixart device model structure */ +struct ota_fw_dev_model { + guint8 status; + guint8 name[FU_PXI_DEVICE_MODEL_NAME_LEN]; + guint8 type; + guint8 target; + guint8 version[5]; + guint16 checksum; +}; + +/* pixart fw info structure */ +struct ota_fw_info { + guint8 status; + guint8 version[5]; + guint16 checksum; +}; + +/* ota disconnect reason */ +enum ota_disconnect_reason { + OTA_CODE_JUMP = 1, /* OTA code jump */ + OTA_UPDATE_DONE = 2, /* OTA update done */ + OTA_RESET, /* OTA reset */ +}; + +/* ota rsp code for wireless module */ +enum wireless_module_type { + OTA_WIRELESS_MODULE_TYPE_MOUSE, + OTA_WIRELESS_MODULE_TYPE_KEYBOARD, + OTA_WIRELESS_MODULE_TYPE_RECEIVER, +}; + +/* ota rsp code for wireless module */ +enum wireless_module_ota_rsp_code { + OTA_RSP_OK, + OTA_RSP_FINISH, + OTA_RSP_FAIL, + OTA_RSP_CODE_ERROR, + OTA_RSP_WRITE_PKT_LEN_ERROR, + OTA_RSP_WRITE_PKT_TOTAL_SIZE_ERROR, + OTA_RSP_READ_PKT_LEN_ERROR, + OTA_RSP_NOT_READY, +}; + +struct ota_fw_state { + guint8 status; + guint8 new_flow; + guint16 offset; + guint16 checksum; + guint32 max_object_size; + guint16 mtu_size; + guint16 prn_threshold; + guint8 spec_check_result; +}; + +gboolean +fu_pxi_composite_receiver_cmd(guint8 opcode, + guint8 sn, + guint8 target, + GByteArray *wireless_mod_cmd, + GByteArray *ota_cmd, + GError **error); +const gchar * +fu_pxi_receiver_cmd_result_to_string(guint8 result); +const gchar * +fu_pxi_spec_check_result_to_string(guint8 spec_check_result); + +void +fu_pxi_ota_fw_state_to_string(struct ota_fw_state *fwstate, guint idt, GString *str); +gboolean +fu_pxi_ota_fw_state_parse(struct ota_fw_state *fwstate, + const guint8 *buf, + gsize bufsz, + gsize offset, + GError **error); diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-firmware.c b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..54fde2fb3f1319858d737e9a58f37d26bcd8dc13 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-firmware.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-pxi-firmware.h" + +#define PIXART_RF_FW_HEADER_SIZE 32 /* bytes */ +#define PIXART_RF_FW_HEADER_TAG_OFFSET 24 + +#define PIXART_RF_FW_HEADER_MAGIC 0x55AA55AA55AA55AA + +struct _FuPxiFirmware { + FuFirmware parent_instance; + gchar *model_name; +}; + +G_DEFINE_TYPE(FuPxiFirmware, fu_pxi_firmware, FU_TYPE_FIRMWARE) + +const gchar * +fu_pxi_firmware_get_model_name(FuPxiFirmware *self) +{ + g_return_val_if_fail(FU_IS_PXI_FIRMWARE(self), NULL); + return self->model_name; +} + +static void +fu_pxi_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE(firmware); + fu_xmlb_builder_insert_kv(bn, "model_name", self->model_name); +} + +static gboolean +fu_pxi_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint64 magic = 0; + + /* is a footer */ + if (!fu_memread_uint64_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + g_bytes_get_size(fw) - PIXART_RF_FW_HEADER_SIZE + + PIXART_RF_FW_HEADER_TAG_OFFSET, + &magic, + G_BIG_ENDIAN, + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (magic != PIXART_RF_FW_HEADER_MAGIC) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid magic, expected 0x%08X got 0x%08X", + (guint32)PIXART_RF_FW_HEADER_MAGIC, + (guint32)magic); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE(firmware); + const guint8 *buf; + gsize bufsz = 0; + guint32 version_raw = 0; + guint8 fw_header[PIXART_RF_FW_HEADER_SIZE]; + guint8 model_name[FU_PXI_DEVICE_MODEL_NAME_LEN] = {0x0}; + g_autofree gchar *version = NULL; + + /* get fw header from buf */ + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_memcpy_safe(fw_header, + sizeof(fw_header), + 0x0, + buf, + bufsz, + bufsz - sizeof(fw_header), + sizeof(fw_header), + error)) { + g_prefix_error(error, "failed to read fw header: "); + return FALSE; + } + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "fw header", fw_header, sizeof(fw_header)); + } + + /* set fw version */ + version_raw = (((guint32)(fw_header[0] - '0')) << 16) + + (((guint32)(fw_header[2] - '0')) << 8) + (guint32)(fw_header[4] - '0'); + fu_firmware_set_version_raw(firmware, version_raw); + version = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_DELL_BIOS); + fu_firmware_set_version(firmware, version); + + /* set fw model name */ + if (!fu_memcpy_safe(model_name, + sizeof(model_name), + 0x0, + fw_header, + sizeof(fw_header), + 0x05, + sizeof(model_name), + error)) { + g_prefix_error(error, "failed to get fw model name: "); + return FALSE; + } + self->model_name = g_strndup((gchar *)model_name, sizeof(model_name)); + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE(firmware); + const gchar *tmp; + + /* optional properties */ + tmp = xb_node_query_text(n, "model_name", NULL); + if (tmp != NULL) + self->model_name = g_strdup(tmp); + + /* success */ + return TRUE; +} + +static GBytes * +fu_pxi_firmware_write(FuFirmware *firmware, GError **error) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE(firmware); + guint8 fw_header[PIXART_RF_FW_HEADER_SIZE] = {0x0}; + guint64 version_raw = fu_firmware_get_version_raw(firmware); + g_autoptr(GByteArray) buf = NULL; + g_autoptr(GBytes) blob = NULL; + const guint8 tag[] = { + 0x55, + 0xAA, + 0x55, + 0xAA, + 0x55, + 0xAA, + 0x55, + 0xAA, + }; + + /* data first */ + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return NULL; + buf = g_byte_array_sized_new(g_bytes_get_size(blob) + sizeof(fw_header)); + fu_byte_array_append_bytes(buf, blob); + + /* footer */ + if (!fu_memcpy_safe(fw_header, + sizeof(fw_header), + PIXART_RF_FW_HEADER_TAG_OFFSET, /* dst */ + tag, + sizeof(tag), + 0x0, /* src */ + sizeof(tag), + error)) + return NULL; + fw_header[0] = ((version_raw >> 16) & 0xff) + '0'; + fw_header[1] = '.'; + fw_header[2] = ((version_raw >> 8) & 0xff) + '0'; + fw_header[3] = '.'; + fw_header[4] = ((version_raw >> 0) & 0xff) + '0'; + if (!g_ascii_isdigit(fw_header[0]) || !g_ascii_isdigit(fw_header[2]) || + !g_ascii_isdigit(fw_header[4])) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot write invalid version number 0x%x", + (guint)version_raw); + return NULL; + } + if (self->model_name != NULL) { + gsize model_namesz = MIN(strlen(self->model_name), FU_PXI_DEVICE_MODEL_NAME_LEN); + if (!fu_memcpy_safe(fw_header, + sizeof(fw_header), + 0x05, /* dst */ + (const guint8 *)self->model_name, + model_namesz, + 0x0, /* src */ + model_namesz, + error)) { + g_prefix_error(error, "failed to get fw model name: "); + return NULL; + } + } + + g_byte_array_append(buf, fw_header, sizeof(fw_header)); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_pxi_firmware_init(FuPxiFirmware *self) +{ +} + +static void +fu_pxi_firmware_finalize(GObject *object) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE(object); + g_free(self->model_name); + G_OBJECT_CLASS(fu_pxi_firmware_parent_class)->finalize(object); +} + +static void +fu_pxi_firmware_class_init(FuPxiFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_pxi_firmware_finalize; + klass_firmware->check_magic = fu_pxi_firmware_check_magic; + klass_firmware->parse = fu_pxi_firmware_parse; + klass_firmware->build = fu_pxi_firmware_build; + klass_firmware->write = fu_pxi_firmware_write; + klass_firmware->export = fu_pxi_firmware_export; +} + +FuFirmware * +fu_pxi_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_PXI_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-firmware.h b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..32d1331c910308396b5611f77ae4f7be9f3ad3f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_PXI_DEVICE_MODEL_NAME_LEN 12 /* bytes */ + +#define FU_TYPE_PXI_FIRMWARE (fu_pxi_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuPxiFirmware, fu_pxi_firmware, FU, PXI_FIRMWARE, FuFirmware) + +FuFirmware * +fu_pxi_firmware_new(void); +const gchar * +fu_pxi_firmware_get_model_name(FuPxiFirmware *self); diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-plugin.c b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..c5018410ae15b36fa6bee336f2d3f3da80554476 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-plugin.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-pxi-ble-device.h" +#include "fu-pxi-firmware.h" +#include "fu-pxi-plugin.h" +#include "fu-pxi-receiver-device.h" + +struct _FuPxiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuPxiPlugin, fu_pxi_plugin, FU_TYPE_PLUGIN) + +static void +fu_pxi_plugin_init(FuPxiPlugin *self) +{ +} + +static void +fu_pxi_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "pixart_rf"); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_PXI_BLE_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_PXI_RECEIVER_DEVICE); + fu_plugin_add_firmware_gtype(plugin, "pixart", FU_TYPE_PXI_FIRMWARE); +} + +static void +fu_pxi_plugin_class_init(FuPxiPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_pxi_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-plugin.h b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..636923d99d5fdb4ba0b04862f7ef058f18b7a690 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuPxiPlugin, fu_pxi_plugin, FU, PXI_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-receiver-device.c b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-receiver-device.c new file mode 100644 index 0000000000000000000000000000000000000000..4ffd652330c393bf3803e3172160de0f33a61e70 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-receiver-device.c @@ -0,0 +1,902 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#include "config.h" + +#ifdef HAVE_HIDRAW_H +#include +#include +#endif + +#include + +#include "fu-pxi-firmware.h" +#include "fu-pxi-receiver-device.h" +#include "fu-pxi-wireless-device.h" + +struct _FuPxiReceiverDevice { + FuUdevDevice parent_instance; + struct ota_fw_state fwstate; + guint8 sn; + guint vendor; + guint product; +}; + +G_DEFINE_TYPE(FuPxiReceiverDevice, fu_pxi_receiver_device, FU_TYPE_UDEV_DEVICE) + +#ifdef HAVE_HIDRAW_H +static gboolean +fu_pxi_receiver_device_get_raw_info(FuPxiReceiverDevice *self, + struct hidraw_devinfo *info, + GError **error) +{ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGRAWINFO, + (guint8 *)info, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + return TRUE; +} +#endif + +static void +fu_pxi_receiver_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + fu_pxi_ota_fw_state_to_string(&self->fwstate, idt, str); + fu_string_append_kx(str, idt, "Vendor", self->vendor); + fu_string_append_kx(str, idt, "Product", self->product); +} + +static gboolean +fu_pxi_receiver_device_set_feature(FuPxiReceiverDevice *self, + const guint8 *buf, + guint bufsz, + GError **error) +{ +#ifdef HAVE_HIDRAW_H + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SetFeature", buf, bufsz); + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCSFEATURE(bufsz), + (guint8 *)buf, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_receiver_device_get_feature(FuPxiReceiverDevice *self, + guint8 *buf, + guint bufsz, + GError **error) +{ +#ifdef HAVE_HIDRAW_H + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGFEATURE(bufsz), + buf, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetFeature", buf, bufsz); + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_receiver_device_fw_ota_init_new(FuPxiReceiverDevice *device, gsize bufsz, GError **error) +{ + guint8 fw_version[10] = {0x0}; + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + + fu_byte_array_append_uint8(ota_cmd, 0X06); /* ota init new command length */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW); /* ota ota init new op code */ + fu_byte_array_append_uint32(ota_cmd, bufsz, G_LITTLE_ENDIAN); /* fw size */ + fu_byte_array_append_uint8(ota_cmd, 0x0); /* ota setting */ + g_byte_array_append(ota_cmd, fw_version, sizeof(fw_version)); /* ota version */ + + self->sn++; + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_fw_ota_ini_new_check(FuPxiReceiverDevice *device, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + + /* ota command */ + fu_byte_array_append_uint8(ota_cmd, 0x1); /* ota command length */ + fu_byte_array_append_uint8(ota_cmd, + FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW_CHECK); /* ota command */ + self->sn++; + + /* get pixart wireless module ota command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW_CHECK, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + /* delay for wireless module device read command */ + g_usleep(5 * 1000); + buf[0] = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; + if (!fu_pxi_receiver_device_get_feature(self, buf, sizeof(buf), error)) + return FALSE; + + /* shared state */ + return fu_pxi_ota_fw_state_parse(&self->fwstate, buf, sizeof(buf), 0x09, error); +} + +static gboolean +fu_pxi_receiver_device_get_cmd_response(FuPxiReceiverDevice *device, + guint8 *buf, + guint bufsz, + GError **error) +{ + guint16 retry = 0; + while (1) { + guint8 sn = 0x0; + memset(buf, 0, bufsz); + buf[0] = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; + + g_usleep(5 * 1000); + + if (!fu_pxi_receiver_device_get_feature(device, buf, bufsz, error)) + return FALSE; + + if (!fu_memread_uint8_safe(buf, bufsz, 0x4, &sn, error)) + return FALSE; + + if (device->sn != sn) + retry++; + else + break; + + if (retry == FU_PXI_WIRELESS_DEVICE_RETRY_MAXIMUM) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "reach retry maximum, hid sn fail, " + "got 0x%04x, expected 0x%04x", + sn, + device->sn); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_check_crc(FuDevice *device, guint16 checksum, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 status = 0x0; + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + + /* ota check crc command */ + fu_byte_array_append_uint8(ota_cmd, 0x3); /* ota command length */ + fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC); /* ota command */ + fu_byte_array_append_uint16(ota_cmd, checksum, G_LITTLE_ENDIAN); /* checkesum */ + + /* increase the serial number */ + self->sn++; + + /* get pixart wireless module for ota check crc command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + if (!fu_pxi_receiver_device_get_cmd_response(self, buf, sizeof(buf), error)) + return FALSE; + + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x5, &status, error)) + return FALSE; + + if (status == OTA_RSP_CODE_ERROR) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "checksum error: expected 0x%04x", + checksum); + return FALSE; + } + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_fw_object_create(FuDevice *device, FuChunk *chk, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 status = 0x0; + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + + /* ota object create command */ + fu_byte_array_append_uint8(ota_cmd, 0x9); /* ota command length */ + fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE); /* ota command */ + fu_byte_array_append_uint32(ota_cmd, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(ota_cmd, fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); + + /* increase the serial number */ + self->sn++; + + /* get pixart wireless module for ota object create command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + if (!fu_pxi_receiver_device_get_cmd_response(self, buf, sizeof(buf), error)) + return FALSE; + + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x5, &status, error)) + return FALSE; + + if (status != OTA_RSP_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cmd rsp check fail: %s [0x%02x]", + fu_pxi_receiver_cmd_result_to_string(status), + status); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_write_payload(FuDevice *device, FuChunk *chk, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 status = 0x0; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + + /* ota write payload command */ + fu_byte_array_append_uint8(ota_cmd, fu_chunk_get_data_sz(chk)); /* ota command length */ + g_byte_array_append(ota_cmd, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk)); /* payload content */ + + /* increase the serial number */ + self->sn++; + + /* get pixart wireless module for writes payload command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_PAYLOAD_CONTENT, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + /* get the write payload command response */ + if (!fu_pxi_receiver_device_get_cmd_response(self, buf, sizeof(buf), error)) + return FALSE; + + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x5, &status, error)) + return FALSE; + + if (status != OTA_RSP_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cmd rsp check fail: %s [0x%02x]", + fu_pxi_receiver_cmd_result_to_string(status), + status); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_write_chunk(FuDevice *device, FuChunk *chk, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + guint32 prn = 0; + guint16 checksum; + g_autoptr(GPtrArray) chunks = NULL; + + /* send create fw object command */ + if (!fu_pxi_receiver_device_fw_object_create(device, chk, error)) + return FALSE; + + /* write payload */ + chunks = fu_chunk_array_new(fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_chunk_get_address(chk), + 0x0, + self->fwstate.mtu_size); + + /* the checksum of chunk */ + checksum = fu_sum16(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + self->fwstate.checksum += checksum; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk2 = g_ptr_array_index(chunks, i); + if (!fu_pxi_receiver_device_write_payload(device, chk2, error)) + return FALSE; + prn++; + /* check crc at fw when PRN over threshold write or + * offset reach max object sz or write offset reach fw length */ + if (prn >= self->fwstate.prn_threshold || i == chunks->len - 1) { + if (!fu_pxi_receiver_device_check_crc(device, + self->fwstate.checksum, + error)) + return FALSE; + prn = 0; + } + } + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_fw_upgrade(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + const gchar *version; + guint8 fw_version[5] = {0x0}; + guint8 res[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 result = 0x0; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 5, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 95, NULL); + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* ota fw upgrade command */ + fu_byte_array_append_uint8(ota_cmd, 0x0c); /* ota fw upgrade command length */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_UPGRADE); /* ota fw upgrade command opccode */ + fu_byte_array_append_uint32(ota_cmd, + g_bytes_get_size(fw), + G_LITTLE_ENDIAN); /* ota fw upgrade command fw size */ + fu_byte_array_append_uint16(ota_cmd, + fu_sum16_bytes(fw), + G_LITTLE_ENDIAN); /* ota fw upgrade command checksum */ + + version = fu_firmware_get_version(firmware); + if (!fu_memcpy_safe(fw_version, + sizeof(fw_version), + 0x0, /* dst */ + (guint8 *)version, + strlen(version), + 0x0, /* src */ + sizeof(fw_version), + error)) + return FALSE; + + g_byte_array_append(ota_cmd, fw_version, sizeof(fw_version)); + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ota_cmd ", ota_cmd->data, ota_cmd->len); + self->sn++; + + /* get pixart wireless module ota command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_UPGRADE, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* send ota fw upgrade command */ + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + /* delay for wireless module device read command */ + g_usleep(5 * 1000); + + if (!fu_pxi_receiver_device_get_cmd_response(self, res, sizeof(res), error)) + return FALSE; + + if (!fu_memread_uint8_safe(res, sizeof(res), 0x5, &result, error)) + return FALSE; + if (result != OTA_RSP_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cmd rsp check fail: %s [0x%02x]", + fu_pxi_receiver_cmd_result_to_string(result), + result); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_reset(FuDevice *device, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + + /* ota mcu reset command */ + fu_byte_array_append_uint8(ota_cmd, 0x1); /* ota mcu reset command */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_MCU_RESET); /* ota mcu reset command op code */ + fu_byte_array_append_uint8(ota_cmd, OTA_RESET); /* ota mcu reset command reason */ + + self->sn++; + /* get pixart wireless module ota command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_MCU_RESET, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + + /* send ota mcu reset command */ + return fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error); +} + +static gboolean +fu_pxi_receiver_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 9, "ota-init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, NULL); + + /* get the default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* send fw ota init command */ + if (!fu_pxi_receiver_device_fw_ota_init_new(self, g_bytes_get_size(fw), error)) + return FALSE; + + if (!fu_pxi_receiver_device_fw_ota_ini_new_check(self, error)) + return FALSE; + fu_progress_step_done(progress); + + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, FU_PXI_DEVICE_OBJECT_SIZE_MAX); + /* prepare write fw into device */ + self->fwstate.offset = 0; + self->fwstate.checksum = 0; + + /* write fw into device */ + for (guint i = self->fwstate.offset; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_pxi_receiver_device_write_chunk(device, chk, error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + self->fwstate.offset + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* fw upgrade command */ + if (!fu_pxi_receiver_device_fw_upgrade(device, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* delay for wireless module device read command */ + g_usleep(5 * 1000); + + /* send device reset command */ + if (!fu_pxi_receiver_device_reset(device, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_get_peripheral_info(FuPxiReceiverDevice *device, + struct ota_fw_dev_model *model, + GError **error) +{ + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint16 checksum = 0; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + + fu_byte_array_append_uint8(ota_cmd, 0x1); /* ota init new command length */ + fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL); + device->sn++; + + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL, + device->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + if (!fu_pxi_receiver_device_set_feature(device, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + g_usleep(5 * 1000); + buf[0] = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; + + if (!fu_pxi_receiver_device_get_feature(device, buf, sizeof(buf), error)) + return FALSE; + + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "model_info", buf, sizeof(buf)); + + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x9, &model->status, error)) + return FALSE; + + if (!fu_memcpy_safe(model->name, + FU_PXI_DEVICE_MODEL_NAME_LEN, + 0x0, /* dst */ + buf, + FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ, + 0xa, /* src */ + FU_PXI_DEVICE_MODEL_NAME_LEN, + error)) + return FALSE; + + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x16, &model->type, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x17, &model->target, error)) + return FALSE; + + if (!fu_memcpy_safe(model->version, + 5, + 0x0, /* dst */ + buf, + sizeof(buf), + 0x18, /* src */ + 5, + error)) + return FALSE; + + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x1D, &checksum, G_LITTLE_ENDIAN, error)) + + return FALSE; + + /* set current version and model name */ + model->checksum = checksum; + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) { + g_autofree gchar *version_str = g_strndup((gchar *)model->version, 5); + g_debug("checksum %x", model->checksum); + g_debug("version_str %s", version_str); + } + + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_get_peripheral_num(FuPxiReceiverDevice *device, + guint8 *num_of_models, + GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); + + fu_byte_array_append_uint8(ota_cmd, 0x1); /* ota init new command length */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_OTA_GET_NUM_OF_MODELS); /* ota ota init new op code */ + + self->sn++; + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_GET_NUM_OF_MODELS, + self->sn, + FU_PXI_WIRELESS_DEVICE_TARGET_RECEIVER, + receiver_device_cmd, + ota_cmd, + error)) + return FALSE; + if (!fu_pxi_receiver_device_set_feature(self, + receiver_device_cmd->data, + receiver_device_cmd->len, + error)) + return FALSE; + + g_usleep(5 * 1000); + + buf[0] = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; + if (!fu_pxi_receiver_device_get_feature(device, buf, sizeof(buf), error)) + return FALSE; + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, "buf from get model num", buf, sizeof(buf)); + } + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0xA, num_of_models, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_add_peripherals(FuPxiReceiverDevice *device, guint idx, GError **error) +{ +#ifdef HAVE_HIDRAW_H + struct ota_fw_dev_model model = {0x0}; + g_autofree gchar *model_name = NULL; + g_autofree gchar *model_version = NULL; + + /* get the all wireless peripherals info */ + if (!fu_pxi_receiver_device_get_peripheral_info(device, &model, error)) + return FALSE; + model_version = g_strndup((gchar *)model.version, 5); + model_name = g_strndup((gchar *)model.name, FU_PXI_DEVICE_MODEL_NAME_LEN); + + if (model.type == OTA_WIRELESS_MODULE_TYPE_RECEIVER) { + fu_device_set_version(FU_DEVICE(device), model_version); + fu_device_add_instance_u16(FU_DEVICE(device), "VEN", device->vendor); + fu_device_add_instance_u16(FU_DEVICE(device), "DEV", device->product); + fu_device_add_instance_str(FU_DEVICE(device), "MODEL", model_name); + if (!fu_device_build_instance_id(FU_DEVICE(device), + error, + "HIDRAW", + "VEN", + "DEV", + "MODEL", + NULL)) + return FALSE; + } else { + g_autoptr(FuPxiWirelessDevice) child = fu_pxi_wireless_device_new(&model); + g_autofree gchar *logical_id = g_strdup_printf("IDX:0x%02x", idx); + fu_device_add_instance_u16(FU_DEVICE(child), "VEN", device->vendor); + fu_device_add_instance_u16(FU_DEVICE(child), "DEV", device->product); + fu_device_add_instance_str(FU_DEVICE(child), "MODEL", model_name); + if (!fu_device_build_instance_id(FU_DEVICE(child), + error, + "HIDRAW", + "VEN", + "DEV", + "MODEL", + NULL)) + return FALSE; + fu_device_set_name(FU_DEVICE(child), model_name); + fu_device_set_version(FU_DEVICE(child), model_version); + fu_device_set_logical_id(FU_DEVICE(child), logical_id); + fu_device_add_child(FU_DEVICE(device), FU_DEVICE(child)); + } + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_receiver_device_setup_guid(FuPxiReceiverDevice *device, GError **error) +{ +#ifdef HAVE_HIDRAW_H + struct hidraw_devinfo hid_raw_info = {0x0}; + g_autofree gchar *devid = NULL; + g_autoptr(GString) dev_name = NULL; + + /* extra GUID with device name */ + if (!fu_pxi_receiver_device_get_raw_info(device, &hid_raw_info, error)) + return FALSE; + + device->vendor = (guint)hid_raw_info.vendor; + device->product = (guint)hid_raw_info.product; + + dev_name = g_string_new(fu_device_get_name(FU_DEVICE(device))); + g_string_ascii_up(dev_name); + fu_string_replace(dev_name, " ", "_"); + devid = g_strdup_printf("HIDRAW\\VEN_%04X&DEV_%04X&NAME_%s", + (guint)hid_raw_info.vendor, + (guint)hid_raw_info.product, + dev_name->str); + fu_device_add_instance_id(FU_DEVICE(device), devid); + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_receiver_device_check_peripherals(FuPxiReceiverDevice *device, GError **error) +{ + guint8 num = 0; + + /* add wireless peripherals */ + if (!fu_pxi_receiver_device_get_peripheral_num(device, &num, error)) + return FALSE; + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + g_debug("peripheral num: %u", num); + for (guint8 idx = 0; idx < num; idx++) { + if (!fu_pxi_receiver_device_add_peripherals(device, idx, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_setup(FuDevice *device, GError **error) +{ + FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); + + if (!fu_pxi_receiver_device_setup_guid(self, error)) { + g_prefix_error(error, "failed to setup GUID: "); + return FALSE; + } + if (!fu_pxi_receiver_device_fw_ota_init_new(self, 0x0000, error)) { + g_prefix_error(error, "failed to OTA init new: "); + return FALSE; + } + if (!fu_pxi_receiver_device_fw_ota_ini_new_check(self, error)) { + g_prefix_error(error, "failed to OTA init new check: "); + return FALSE; + } + if (!fu_pxi_receiver_device_check_peripherals(self, error)) { + g_prefix_error(error, "failed to add wireless module: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_pxi_receiver_device_probe(FuDevice *device, GError **error) +{ + /* set the logical and physical ID */ + if (!fu_udev_device_set_logical_id(FU_UDEV_DEVICE(device), "hid", error)) + return FALSE; + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static void +fu_pxi_receiver_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_pxi_receiver_device_init(FuPxiReceiverDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_icon(FU_DEVICE(self), "usb-receiver"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_vendor_id(FU_DEVICE(self), "USB:0x093A"); + fu_device_add_protocol(FU_DEVICE(self), "com.pixart.rf"); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_PXI_FIRMWARE); +} + +static void +fu_pxi_receiver_device_class_init(FuPxiReceiverDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_pxi_receiver_device_to_string; + klass_device->setup = fu_pxi_receiver_device_setup; + klass_device->probe = fu_pxi_receiver_device_probe; + klass_device->write_firmware = fu_pxi_receiver_device_write_firmware; + klass_device->set_progress = fu_pxi_receiver_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-receiver-device.h b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-receiver-device.h new file mode 100644 index 0000000000000000000000000000000000000000..93341312f8eb1186c0337cf62dabe9882f9378e9 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-receiver-device.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-pxi-common.h" + +#define FU_TYPE_PXI_RECEIVER_DEVICE (fu_pxi_receiver_device_get_type()) + +G_DECLARE_FINAL_TYPE(FuPxiReceiverDevice, + fu_pxi_receiver_device, + FU, + PXI_RECEIVER_DEVICE, + FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-wireless-device.c b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-wireless-device.c new file mode 100644 index 0000000000000000000000000000000000000000..05f6587f8fae2e50b5b48faac6423e94931d9039 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-wireless-device.c @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#include "config.h" + +#ifdef HAVE_HIDRAW_H +#include +#include +#endif + +#include + +#include "fu-pxi-common.h" +#include "fu-pxi-firmware.h" +#include "fu-pxi-receiver-device.h" +#include "fu-pxi-wireless-device.h" + +#define FU_PXI_WIRELESS_DEV_DELAY_US 50000 + +struct _FuPxiWirelessDevice { + FuDevice parent_instance; + struct ota_fw_state fwstate; + guint8 sn; + struct ota_fw_dev_model model; +}; + +G_DEFINE_TYPE(FuPxiWirelessDevice, fu_pxi_wireless_device, FU_TYPE_DEVICE) + +static void +fu_pxi_wireless_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + fu_pxi_ota_fw_state_to_string(&self->fwstate, idt, str); + fu_string_append(str, idt, "ModelName", (gchar *)self->model.name); + fu_string_append_kx(str, idt, "ModelType", self->model.type); + fu_string_append_kx(str, idt, "ModelTarget", self->model.target); +} + +static gboolean +fu_pxi_wireless_device_set_feature(FuDevice *self, const guint8 *buf, guint bufsz, GError **error) +{ +#ifdef HAVE_HIDRAW_H + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SetFeature", buf, bufsz); + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCSFEATURE(bufsz), + (guint8 *)buf, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean +fu_pxi_wireless_device_get_feature(FuDevice *self, guint8 *buf, guint bufsz, GError **error) +{ +#ifdef HAVE_HIDRAW_H + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGFEATURE(bufsz), + buf, + NULL, + FU_PXI_DEVICE_IOCTL_TIMEOUT, + error)) { + return FALSE; + } + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetFeature", buf, bufsz); + return TRUE; +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static FuPxiReceiverDevice * +fu_pxi_wireless_device_get_parent(FuDevice *self, GError **error) +{ + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); + if (parent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); + return NULL; + } + return FU_PXI_RECEIVER_DEVICE(FU_UDEV_DEVICE(parent)); +} + +static gboolean +fu_pxi_wireless_device_get_cmd_response(FuPxiWirelessDevice *device, + guint8 *buf, + guint bufsz, + GError **error) +{ + FuPxiReceiverDevice *parent; + guint16 retry = 0; + guint8 status = 0x0; + + parent = fu_pxi_wireless_device_get_parent(FU_DEVICE(device), error); + if (parent == NULL) + return FALSE; + + while (1) { + guint8 sn = 0x0; + memset(buf, 0, bufsz); + buf[0] = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; + + g_usleep(5 * 1000); + + if (!fu_pxi_wireless_device_get_feature(FU_DEVICE(parent), buf, bufsz, error)) + return FALSE; + + if (!fu_memread_uint8_safe(buf, bufsz, 0x4, &sn, error)) + return FALSE; + + if (device->sn != sn) + retry++; + else + break; + + if (retry == FU_PXI_WIRELESS_DEVICE_RETRY_MAXIMUM) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "reach retry maximum hid sn fail, got 0x%04x, expected 0x%04x", + sn, + device->sn); + return FALSE; + } + + /*if wireless device not reply to receiver, keep to wait */ + if (!fu_memread_uint8_safe(buf, bufsz, 0x5, &status, error)) + return FALSE; + if (status == OTA_RSP_NOT_READY) { + retry = 0x0; + g_debug("OTA_RSP_NOT_READY"); + } + } + return TRUE; +} + +static gboolean +fu_pxi_wireless_device_check_crc(FuDevice *device, guint16 checksum, GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint16 checksum_device = 0x0; + guint8 status = 0x0; + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + /* ota check crc command */ + fu_byte_array_append_uint8(ota_cmd, 0x3); /* ota command length */ + fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC); /* ota command */ + fu_byte_array_append_uint16(ota_cmd, checksum, G_LITTLE_ENDIAN); /* checkesum */ + + /* increase the serial number */ + self->sn++; + + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + if (!fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error)) + return FALSE; + if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) + return FALSE; + + if (g_getenv("FWUPD_PIXART_RF_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "crc buf", buf, sizeof(buf)); + + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x5, &status, error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + 0x6, + &checksum_device, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (status == OTA_RSP_CODE_ERROR) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "checksum fail, got 0x%04x, expected 0x%04x", + checksum_device, + checksum); + return FALSE; + } + if (status != OTA_RSP_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "status:%s", + fu_pxi_receiver_cmd_result_to_string(status)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_wireless_device_fw_object_create(FuDevice *device, FuChunk *chk, GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + /* ota object create command */ + fu_byte_array_append_uint8(ota_cmd, 0x9); /* ota command length */ + fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE); /* ota command */ + fu_byte_array_append_uint32(ota_cmd, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(ota_cmd, fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); + + /* increase the serial number */ + self->sn++; + + /* get pixart wireless module for ota object create command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OBJECT_CREATE, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + + /* delay for wireless module device get command response*/ + g_usleep(FU_PXI_WIRELESS_DEV_DELAY_US); + + return fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error); +} + +static gboolean +fu_pxi_wireless_device_write_payload(FuDevice *device, FuChunk *chk, GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 status = 0x0; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + /* ota write payload command */ + fu_byte_array_append_uint8(ota_cmd, fu_chunk_get_data_sz(chk)); /* ota command length */ + g_byte_array_append(ota_cmd, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk)); /* payload content */ + + /* increase the serial number */ + self->sn++; + + /* get pixart wireless module for ota write payload command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_PAYLOAD_CONTENT, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + if (!fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error)) + return FALSE; + + /* delay for wireless module device get command response*/ + g_usleep(FU_PXI_WIRELESS_DEV_DELAY_US); + + if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x5, &status, error)) + return FALSE; + if (status != OTA_RSP_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cmd rsp check fail: %s [0x%02x]", + fu_pxi_receiver_cmd_result_to_string(status), + status); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_wireless_device_write_chunk(FuDevice *device, FuChunk *chk, GError **error) +{ + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + guint16 checksum; + guint32 prn = 0; + g_autoptr(GPtrArray) chunks = NULL; + + /* send create fw object command */ + if (!fu_pxi_wireless_device_fw_object_create(device, chk, error)) + return FALSE; + + /* write payload */ + chunks = fu_chunk_array_new(fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_chunk_get_address(chk), + 0x0, + self->fwstate.mtu_size); + + /* calculate checksum of chunk */ + checksum = fu_sum16(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + self->fwstate.checksum += checksum; + + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk2 = g_ptr_array_index(chunks, i); + if (!fu_pxi_wireless_device_write_payload(device, chk2, error)) + return FALSE; + prn++; + /* check crc at fw when PRN over threshold write or + * offset reach max object sz or write offset reach fw length */ + if (prn >= self->fwstate.prn_threshold || i == (chunks->len - 1)) { + if (!fu_pxi_wireless_device_check_crc(device, + self->fwstate.checksum, + error)) + return FALSE; + prn = 0; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pxi_wireless_device_fw_ota_init_new(FuDevice *device, gsize bufsz, GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + guint8 fw_version[10] = {0x0}; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + fu_byte_array_append_uint8(ota_cmd, 0X06); /* ota init new command length */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW); /* ota ota init new op code */ + fu_byte_array_append_uint32(ota_cmd, bufsz, G_LITTLE_ENDIAN); /* fw size */ + fu_byte_array_append_uint8(ota_cmd, 0x0); /* ota setting */ + g_byte_array_append(ota_cmd, fw_version, sizeof(fw_version)); /* ota version */ + + /* increase the serial number */ + self->sn++; + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + return fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error); +} + +static gboolean +fu_pxi_wireless_device_fw_ota_ini_new_check(FuDevice *device, GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; + guint8 status = 0x0; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + /* ota command */ + fu_byte_array_append_uint8(ota_cmd, 0x1); /* ota command length */ + fu_byte_array_append_uint8(ota_cmd, + FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW_CHECK); /* ota command */ + + /* increase the serial number */ + self->sn++; + + /* get pixart wireless module ota command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW_CHECK, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + if (!fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error)) + return FALSE; + + /* delay for wireless module device get command response*/ + g_usleep(FU_PXI_WIRELESS_DEV_DELAY_US); + + if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x5, &status, error)) + return FALSE; + if (status != OTA_RSP_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cmd rsp check fail: %s [0x%02x]", + fu_pxi_receiver_cmd_result_to_string(status), + status); + return FALSE; + } + + /* shared state */ + return fu_pxi_ota_fw_state_parse(&self->fwstate, buf, sizeof(buf), 0x09, error); +} + +static gboolean +fu_pxi_wireless_device_fw_upgrade(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + const gchar *version; + guint8 fw_version[5] = {0x0}; + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 5, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 95, NULL); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* ota fw upgrade command */ + fu_byte_array_append_uint8(ota_cmd, 0x0c); /* ota fw upgrade command length */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_UPGRADE); /* ota fw upgrade command opccode */ + fu_byte_array_append_uint32(ota_cmd, + g_bytes_get_size(fw), + G_LITTLE_ENDIAN); /* ota fw upgrade command fw size */ + fu_byte_array_append_uint16(ota_cmd, + fu_sum16_bytes(fw), + G_LITTLE_ENDIAN); /* ota fw upgrade command checksum */ + + version = fu_firmware_get_version(firmware); + if (!fu_memcpy_safe(fw_version, + sizeof(fw_version), + 0x0, /* dst */ + (guint8 *)version, + strlen(version), + 0x0, /* src */ + sizeof(fw_version), + error)) + return FALSE; + + g_byte_array_append(ota_cmd, fw_version, sizeof(fw_version)); + + self->sn++; + /* get pixart wireless module ota command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_UPGRADE, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* send ota fw upgrade command */ + if (!fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error)) + return FALSE; + fu_progress_step_done(progress); + return TRUE; +} + +static gboolean +fu_pxi_wireless_device_reset(FuDevice *device, GError **error) +{ + FuPxiReceiverDevice *parent; + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); + g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); + + /* proxy */ + parent = fu_pxi_wireless_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + + /* ota mcu reset command */ + fu_byte_array_append_uint8(ota_cmd, 0x1); /* ota mcu reset command */ + fu_byte_array_append_uint8( + ota_cmd, + FU_PXI_DEVICE_CMD_FW_MCU_RESET); /* ota mcu reset command op code */ + fu_byte_array_append_uint8(ota_cmd, OTA_RESET); /* ota mcu reset command reason */ + + self->sn++; + /* get pixart wireless module ota command */ + if (!fu_pxi_composite_receiver_cmd(FU_PXI_DEVICE_CMD_FW_MCU_RESET, + self->sn, + self->model.target, + receiver_cmd, + ota_cmd, + error)) + return FALSE; + + /* send ota mcu reset command */ + return fu_pxi_wireless_device_set_feature(FU_DEVICE(parent), + receiver_cmd->data, + receiver_cmd->len, + error); +} + +static gboolean +fu_pxi_wireless_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 9, "ota-init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, NULL); + + /* get the default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* send fw ota init command */ + if (!fu_pxi_wireless_device_fw_ota_init_new(device, g_bytes_get_size(fw), error)) + return FALSE; + if (!fu_pxi_wireless_device_fw_ota_ini_new_check(device, error)) + return FALSE; + fu_progress_step_done(progress); + + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, FU_PXI_DEVICE_OBJECT_SIZE_MAX); + /* prepare write fw into device */ + self->fwstate.offset = 0; + self->fwstate.checksum = 0; + + /* write fw into device */ + for (guint i = self->fwstate.offset; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_pxi_wireless_device_write_chunk(device, chk, error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + self->fwstate.offset + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* fw upgrade command */ + if (!fu_pxi_wireless_device_fw_upgrade(device, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* send device reset command */ + g_usleep(FU_PXI_WIRELESS_DEV_DELAY_US); + if (!fu_pxi_wireless_device_reset(device, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_pxi_wireless_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_pxi_wireless_device_init(FuPxiWirelessDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_vendor_id(FU_DEVICE(self), "USB:0x093A"); + fu_device_add_protocol(FU_DEVICE(self), "com.pixart.rf"); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_PXI_FIRMWARE); +} + +static void +fu_pxi_wireless_device_class_init(FuPxiWirelessDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_pxi_wireless_device_write_firmware; + klass_device->to_string = fu_pxi_wireless_device_to_string; + klass_device->set_progress = fu_pxi_wireless_device_set_progress; +} + +FuPxiWirelessDevice * +fu_pxi_wireless_device_new(struct ota_fw_dev_model *model) +{ + FuPxiWirelessDevice *self = NULL; + self = g_object_new(FU_TYPE_PXI_WIRELESS_DEVICE, NULL); + + self->model.status = model->status; + for (guint idx = 0; idx < FU_PXI_DEVICE_MODEL_NAME_LEN; idx++) + self->model.name[idx] = model->name[idx]; + self->model.type = model->type; + self->model.target = model->target; + self->sn = model->target; + return self; +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-wireless-device.h b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-wireless-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5c88290b4f276928ac7c69efcc04fe2c078856ad --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-pxi-wireless-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Jimmy Yu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-pxi-common.h" + +#define FU_TYPE_PXI_WIRELESS_DEVICE (fu_pxi_wireless_device_get_type()) + +G_DECLARE_FINAL_TYPE(FuPxiWirelessDevice, fu_pxi_wireless_device, FU, PXI_WIRELESS_DEVICE, FuDevice) + +FuPxiWirelessDevice * +fu_pxi_wireless_device_new(struct ota_fw_dev_model *model); diff --git a/fwupd-1.8.6/plugins/pixart-rf/fu-self-test.c b/fwupd-1.8.6/plugins/pixart-rf/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..74a70a161183a551ccd039ae65658130865f9ceb --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/fu-self-test.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-pxi-firmware.h" + +static void +fu_pxi_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_pxi_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_pxi_firmware_new(); + g_autoptr(FuFirmware) firmware3 = fu_pxi_firmware_new(); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "pixart.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + fw = fu_firmware_write(firmware1, &error); + g_assert_no_error(error); + g_assert_nonnull(fw); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + + /* ensure we can parse */ + ret = fu_firmware_parse(firmware3, fw, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +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("/pxi/firmware{xml}", fu_pxi_firmware_xml_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/pixart-rf/meson.build b/fwupd-1.8.6/plugins/pixart-rf/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1c52f2b83de613ab5f6ff701c26f41f92886abf5 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/meson.build @@ -0,0 +1,44 @@ +if get_option('plugin_pixart_rf').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginPixartRf"'] + +plugin_quirks += files('pixart-rf.quirk') +plugin_builtin_pxi = static_library('fu_plugin_pxi', + sources: [ + 'fu-pxi-plugin.c', + 'fu-pxi-common.c', + 'fu-pxi-ble-device.c', + 'fu-pxi-receiver-device.c', + 'fu-pxi-wireless-device.c', + 'fu-pxi-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: plugin_deps, + link_with: plugin_libs, +) +plugin_builtins += plugin_builtin_pxi + +if get_option('tests') + install_data(['tests/pixart.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'pxi-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_pxi, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('pxi-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/pixart-rf/pixart-rf.quirk b/fwupd-1.8.6/plugins/pixart-rf/pixart-rf.quirk new file mode 100644 index 0000000000000000000000000000000000000000..f702fa71316c91d073222a5214a10e56e175edb4 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/pixart-rf.quirk @@ -0,0 +1,189 @@ +# Pixart 2801 +[HIDRAW\VEN_093A&DEV_2801] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2860 +[HIDRAW\VEN_093A&DEV_2860] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2802 +[HIDRAW\VEN_093A&DEV_2802] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2822 +[HIDRAW\VEN_093A&DEV_2822] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2861 +[HIDRAW\VEN_093A&DEV_2861] +Plugin = pixart_rf +GType = FuPxiReceiverDevice + +# Pixart 2862 +[HIDRAW\VEN_093A&DEV_2862] +Plugin = pixart_rf +GType = FuPxiReceiverDevice + +# Pixart 2452 +[HIDRAW\VEN_093A&DEV_2452] +Plugin = pixart_rf +GType = FuPxiReceiverDevice + +# Primax Mouse +[HIDRAW\VEN_0461&DEV_4EEF] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Primax US KB +[HIDRAW\VEN_0461&DEV_4EEE] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Primax UK KB +[HIDRAW\VEN_0461&DEV_4EF4] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Primax JP KB +[HIDRAW\VEN_0461&DEV_4EF5] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Chicony Cheetos keyboard +[HIDRAW\VEN_04F2&DEV_2051] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Chicony Cheetos keyboard v2 +[HIDRAW\VEN_04F2&DEV_2125] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Chicony Cheetos keyboard UK +[HIDRAW\VEN_04F2&DEV_2188] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Chicony Cheetos keyboard v2 UK +[HIDRAW\VEN_04F2&DEV_2189] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Chicony Cheetos mouse +[HIDRAW\VEN_04F2&DEV_2103] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Elecom Bluetooth Keyboard +[HIDRAW\VEN_056E&DEV_1095] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Targus BT Mouse AMB844 +[HIDRAW\VEN_1048&DEV_1013] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Targus BT Keyboard AKB872 +[HIDRAW\VEN_1048&DEV_3003] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Targus BT Keyboard AKB869 +[HIDRAW\VEN_1048&DEV_3004] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# AKR123 Bluetooth Keyboard US +[HIDRAW\VEN_04F2&DEV_2199] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# AKR123 Bluetooth Keyboard UK +[HIDRAW\VEN_04F2&DEV_2174] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# AMR120 Bluetooth Mouse +[HIDRAW\VEN_04F2&DEV_2198] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# j5 Wireless Keyboard +[HIDRAW\VEN_2DE5&DEV_2125] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# j5 Wireless Mouse +[HIDRAW\VEN_2DE5&DEV_2103] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# CTL Bluetooth Keyboard +[HIDRAW\VEN_04F2&DEV_2179] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# CTL Bluetooth Mouse +[HIDRAW\VEN_04F2&DEV_2178] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# MW150BT +[HIDRAW\VEN_04F2&DEV_2177] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# CKW150BTUK +[HIDRAW\VEN_04F2&DEV_2176] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# CKW150BTUS +[HIDRAW\VEN_04F2&DEV_2175] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Primax Black Mocha KB +[HIDRAW\VEN_0461&DEV_4EFA] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Primax Vivaldi2 Mouse +[HIDRAW\VEN_03F0&DEV_614A] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# HP 320 BTKB +[HIDRAW\VEN_0461&DEV_4F01] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2403 +[HIDRAW\VEN_093A&DEV_2403] +Plugin = pixart_rf +GType = FuPxiReceiverDevice + +# Pixart 2463 +[HIDRAW\VEN_093A&DEV_2463] +Plugin = pixart_rf +GType = FuPxiReceiverDevice + +# Pixart 2832 +[HIDRAW\VEN_093A&DEV_2832] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2851 +[HIDRAW\VEN_093A&DEV_2851] +Plugin = pixart_rf +GType = FuPxiBleDevice + +# Pixart 2852 +[HIDRAW\VEN_093A&DEV_2852] +Plugin = pixart_rf +GType = FuPxiBleDevice diff --git a/fwupd-1.8.6/plugins/pixart-rf/tests/pixart.bin b/fwupd-1.8.6/plugins/pixart-rf/tests/pixart.bin new file mode 100644 index 0000000000000000000000000000000000000000..866bcfe6af0e4fb1a1126076a1a98b758ab9766b Binary files /dev/null and b/fwupd-1.8.6/plugins/pixart-rf/tests/pixart.bin differ diff --git a/fwupd-1.8.6/plugins/pixart-rf/tests/pixart.builder.xml b/fwupd-1.8.6/plugins/pixart-rf/tests/pixart.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..81659bd3538a95b1d0365bdf0fdb384ce54eb5c6 --- /dev/null +++ b/fwupd-1.8.6/plugins/pixart-rf/tests/pixart.builder.xml @@ -0,0 +1,5 @@ + + 0x00010203 + Test + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/powerd/README.md b/fwupd-1.8.6/plugins/powerd/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8d4015bd0d4324fc68ac542aebb96e410152d462 --- /dev/null +++ b/fwupd-1.8.6/plugins/powerd/README.md @@ -0,0 +1,18 @@ +Powerd Support +============== + +Introduction +------------ + +This plugin is used to ensure that some updates are not done on battery power +and that there is sufficient battery power for certain updates that are. + +Vendor ID Security +------------------ + +This protocol does not create a device and thus requires no vendor ID set. + +External interface access +------------------------- + +This plugin requires access to the `org.chromium.PowerManager` DBus interface. diff --git a/fwupd-1.8.6/plugins/powerd/fu-powerd-plugin.c b/fwupd-1.8.6/plugins/powerd/fu-powerd-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..dc110cc81c68feb02114d9a8a4ca1f4cf823bfb3 --- /dev/null +++ b/fwupd-1.8.6/plugins/powerd/fu-powerd-plugin.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2021 Twain Byrnes + * Copyright (C) 2021 George Popoola + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-powerd-plugin.h" + +struct _FuPowerdPlugin { + FuPlugin parent_instance; + GDBusProxy *proxy; /* nullable */ +}; + +G_DEFINE_TYPE(FuPowerdPlugin, fu_powerd_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_powerd_plugin_create_suspend_file(GError **error) +{ + g_autofree gchar *lockdir = NULL; + g_autofree gchar *inhibitsuspend_filename = NULL; + g_autofree gchar *getpid_str = NULL; + + lockdir = fu_path_from_kind(FU_PATH_KIND_LOCKDIR); + inhibitsuspend_filename = g_build_filename(lockdir, "power_override", "fwupd.lock", NULL); + getpid_str = g_strdup_printf("%d", getpid()); + if (!g_file_set_contents(inhibitsuspend_filename, getpid_str, -1, error)) { + g_prefix_error(error, "lock file unable to be created"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_powerd_plugin_delete_suspend_file(GError **error) +{ + g_autoptr(GError) local_error = NULL; + g_autofree gchar *lockdir = NULL; + g_autoptr(GFile) inhibitsuspend_file = NULL; + + lockdir = fu_path_from_kind(FU_PATH_KIND_LOCKDIR); + inhibitsuspend_file = + g_file_new_build_filename(lockdir, "power_override", "fwupd.lock", NULL); + if (!g_file_delete(inhibitsuspend_file, NULL, &local_error) && + !g_error_matches(local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&local_error), + "lock file unable to be deleted"); + return FALSE; + } + return TRUE; +} + +static void +fu_powerd_plugin_rescan(FuPlugin *plugin, GVariant *parameters) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + guint32 power_type; + guint32 current_state; + gdouble current_level; + + g_variant_get(parameters, "(uud)", &power_type, ¤t_state, ¤t_level); + + /* checking if percentage is invalid */ + if (current_level < 1 || current_level > 100) + current_level = FWUPD_BATTERY_LEVEL_INVALID; + + fu_context_set_battery_state(ctx, current_state); + fu_context_set_battery_level(ctx, current_level); +} + +static void +fu_powerd_plugin_proxy_changed_cb(GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + FuPlugin *plugin) +{ + if (!g_str_equal(signal_name, "BatteryStatePoll")) + return; + fu_powerd_plugin_rescan(plugin, parameters); +} + +static gboolean +fu_powerd_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuPowerdPlugin *self = FU_POWERD_PLUGIN(plugin); + g_autofree gchar *name_owner = NULL; + + if (!fu_powerd_plugin_delete_suspend_file(error)) + return FALSE; + + /* establish proxy for method call to powerd */ + self->proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.chromium.PowerManager", + "/org/chromium/PowerManager", + "org.chromium.PowerManager", + NULL, + error); + + if (self->proxy == NULL) { + g_prefix_error(error, "failed to connect to powerd: "); + return FALSE; + } + name_owner = g_dbus_proxy_get_name_owner(self->proxy); + if (name_owner == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no service that owns the name for %s", + g_dbus_proxy_get_name(self->proxy)); + return FALSE; + } + + fu_powerd_plugin_rescan(plugin, + g_dbus_proxy_call_sync(self->proxy, + "GetBatteryState", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + G_SOURCE_REMOVE)); + + g_signal_connect(G_DBUS_PROXY(self->proxy), + "g-signal", + G_CALLBACK(fu_powerd_plugin_proxy_changed_cb), + plugin); + + return TRUE; +} + +static gboolean +fu_powerd_plugin_prepare(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + return fu_powerd_plugin_create_suspend_file(error); +} + +static gboolean +fu_powerd_plugin_cleanup(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + return fu_powerd_plugin_delete_suspend_file(error); +} + +static void +fu_powerd_plugin_init(FuPowerdPlugin *self) +{ +} + +static void +fu_powerd_finalize(GObject *obj) +{ + FuPowerdPlugin *self = FU_POWERD_PLUGIN(obj); + if (self->proxy != NULL) + g_object_unref(self->proxy); + G_OBJECT_CLASS(fu_powerd_plugin_parent_class)->finalize(obj); +} + +static void +fu_powerd_plugin_class_init(FuPowerdPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_powerd_finalize; + plugin_class->startup = fu_powerd_plugin_startup; + plugin_class->cleanup = fu_powerd_plugin_cleanup; + plugin_class->prepare = fu_powerd_plugin_prepare; +} diff --git a/fwupd-1.8.6/plugins/powerd/fu-powerd-plugin.h b/fwupd-1.8.6/plugins/powerd/fu-powerd-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..be4d37dba9fd2a189b8b5b07635c2a4d4544a97b --- /dev/null +++ b/fwupd-1.8.6/plugins/powerd/fu-powerd-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuPowerdPlugin, fu_powerd_plugin, FU, POWERD_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/powerd/meson.build b/fwupd-1.8.6/plugins/powerd/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..186ee887c92914e890f7239c293b910f1b9624d6 --- /dev/null +++ b/fwupd-1.8.6/plugins/powerd/meson.build @@ -0,0 +1,15 @@ +if get_option('plugin_powerd').disable_auto_if(host_machine.system() != 'linux').require(gio.version().version_compare('>=2.58'), + error_message: 'gio 2.58 or later needed for powerd').allowed() + cargs = ['-DG_LOG_DOMAIN="FuPluginPowerd"'] + +plugin_builtins += static_library('fu_plugin_powerd', + sources: [ + 'fu-powerd-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +endif diff --git a/fwupd-1.8.6/plugins/realtek-mst/README.md b/fwupd-1.8.6/plugins/realtek-mst/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6e1751b8809be296ea49a447bbfd28dda6bd180c --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/README.md @@ -0,0 +1,53 @@ +# Realtek MST + +## Introduction + +This plugin updates the firmware of DisplayPort MST hub devices made by Realtek, +such as the RTD2141b and RTD2142. + +These devices communicate over I²C, via the DisplayPort aux channel. Devices +are declared by system firmware, and quirks specify the aux channel to which +the device is connected for a given system. + +System firmware must specify the device's presence because while they can be +identified partially through the presence of Realtek's OUI in the Branch +Device OUI fields of DPCD (DisplayPort Configuration Data), they do not have +unique Device Identification strings. + +This plugin was neither written, verified, supported or endorsed by Realtek +Semiconductor Corp. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format, which is written to the partition of the +device flash that is not currently running. + +This plugin supports the following protocol ID: + +* com.realtek.rtd2142 + +## GUID Generation + +Devices use an extra instance ID derived from SMBIOS, e.g. + +* `I2C\NAME_10EC2142:00&FAMILY_Google_Hatch` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### RealtekMstDpAuxName + +Specifies the name of the drm_dp_aux_dev channel over which the device should be reached. + +Since: 1.6.2 + +## Vendor ID security + +The vendor ID is specified by system firmware (such as ACPI tables). + +## External Interface Access + +This plugin requires access to i2c buses associated with the specified +DisplayPort aux channel, usually `/dev/i2c-5` or similar. diff --git a/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-device.c b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-device.c new file mode 100644 index 0000000000000000000000000000000000000000..140f7be268e15132c8b1865a58261ddfa40369f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-device.c @@ -0,0 +1,967 @@ +/* + * Copyright (C) 2021 Peter Marheine + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#include +#include + +#include "fu-realtek-mst-device.h" + +/* firmware debug address */ +#define I2C_ADDR_DEBUG 0x35 +/* programming address */ +#define I2C_ADDR_ISP 0x4a + +/* some kind of operation attribute bits */ +#define REG_CMD_ATTR 0x60 +/* write set to begin executing, cleared when done */ +#define CMD_ERASE_BUSY 0x01 + +/* 24-bit address for commands */ +#define REG_CMD_ADDR_HI 0x64 +#define REG_CMD_ADDR_MID 0x65 +#define REG_CMD_ADDR_LO 0x66 + +/* register for erase commands */ +#define REG_ERASE_OPCODE 0x61 +#define CMD_OPCODE_ERASE_SECTOR 0x20 +#define CMD_OPCODE_ERASE_BLOCK 0xD8 + +/* register for read commands */ +#define REG_READ_OPCODE 0x6A +#define CMD_OPCODE_READ 0x03 + +/* register for write commands */ +#define REG_WRITE_OPCODE 0x6D +#define CMD_OPCODE_WRITE 0x02 + +/* mode register address */ +#define REG_MCU_MODE 0x6F +/* when bit is set in mode register, ISP mode is active */ +#define MCU_MODE_ISP (1 << 7) +/* write set to begin write, reset by device when complete */ +#define MCU_MODE_WRITE_BUSY (1 << 5) +/* when bit is clear, write buffer contains data */ +#define MCU_MODE_WRITE_BUF (1 << 4) + +/* write data into write buffer */ +#define REG_WRITE_FIFO 0x70 +/* number of bytes to write minus 1 (0xff means 256 bytes) */ +#define REG_WRITE_LEN 0x71 + +/* Indirect registers allow access to registers with 16-bit addresses. Write + * 0x9F to the LO register, then the top byte of the address to HI, the + * bottom byte of the address to LO, then read or write HI to read or write + * the value of the target register. */ +#define REG_INDIRECT_LO 0xF4 +#define REG_INDIRECT_HI 0xF5 + +/* GPIO configuration/access registers */ +#define REG_GPIO88_CONFIG 0x104F +#define REG_GPIO88_VALUE 0xFE3F + +/* flash chip properties */ +#define FLASH_SIZE 0x100000 +#define FLASH_SECTOR_SIZE 4096 +#define FLASH_BLOCK_SIZE 65536 + +/* MST flash layout */ +#define FLASH_USER1_ADDR 0x10000 +#define FLASH_FLAG1_ADDR 0xfe304 +#define FLASH_USER2_ADDR 0x80000 +#define FLASH_FLAG2_ADDR 0xff304 +#define FLASH_USER_SIZE 0x70000 + +enum dual_bank_mode { + DUAL_BANK_USER_ONLY = 0, + DUAL_BANK_DIFF = 1, + DUAL_BANK_COPY = 2, + DUAL_BANK_USER_ONLY_FLAG = 3, + DUAL_BANK_MAX_VALUE = 3, +}; + +enum flash_bank { + FLASH_BANK_BOOT = 0, + FLASH_BANK_USER1 = 1, + FLASH_BANK_USER2 = 2, + FLASH_BANK_MAX_VALUE = 2, + FLASH_BANK_INVALID = 255, +}; + +struct dual_bank_info { + gboolean is_enabled; + enum dual_bank_mode mode; + enum flash_bank active_bank; + guint8 user1_version[2]; + guint8 user2_version[2]; +}; + +struct _FuRealtekMstDevice { + FuI2cDevice parent_instance; + gchar *dp_aux_dev_name; + gchar *dp_card_kernel_name; + enum flash_bank active_bank; +}; + +G_DEFINE_TYPE(FuRealtekMstDevice, fu_realtek_mst_device, FU_TYPE_I2C_DEVICE) + +#define FU_REALTEK_MST_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static gboolean +fu_realtek_mst_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + + if (g_strcmp0(key, "RealtekMstDpAuxName") == 0) { + self->dp_aux_dev_name = g_strdup(value); + } else if (g_strcmp0(key, "RealtekMstDrmCardKernelName") == 0) { + self->dp_card_kernel_name = g_strdup(value); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported quirk key: %s", + key); + return FALSE; + } + return TRUE; +} + +static FuUdevDevice * +locate_i2c_bus(const GPtrArray *i2c_devices) +{ + for (guint i = 0; i < i2c_devices->len; i++) { + FuUdevDevice *i2c_device = g_ptr_array_index(i2c_devices, i); + FuUdevDevice *bus_device; + g_autoptr(GPtrArray) i2c_buses = + fu_udev_device_get_children_with_subsystem(i2c_device, "i2c-dev"); + + if (i2c_buses->len == 0) { + g_debug("no i2c-dev found under %s", + fu_udev_device_get_sysfs_path(i2c_device)); + continue; + } + if (i2c_buses->len > 1) { + g_debug("ignoring %u additional i2c-dev under %s", + i2c_buses->len - 1, + fu_udev_device_get_sysfs_path(i2c_device)); + } + + bus_device = g_object_ref(g_ptr_array_index(i2c_buses, 0)); + g_debug("Found I2C bus at %s, using this device", + fu_udev_device_get_sysfs_path(bus_device)); + return bus_device; + } + return NULL; +} + +static gboolean +fu_realtek_mst_device_use_aux_dev(FuRealtekMstDevice *self, GError **error) +{ + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + g_autoptr(GUdevEnumerator) udev_enumerator = g_udev_enumerator_new(udev_client); + g_autoptr(GList) matches = NULL; + FuUdevDevice *bus_device = NULL; + + g_udev_enumerator_add_match_subsystem(udev_enumerator, "drm_dp_aux_dev"); + g_udev_enumerator_add_match_sysfs_attr(udev_enumerator, "name", self->dp_aux_dev_name); + matches = g_udev_enumerator_execute(udev_enumerator); + + /* from a drm_dp_aux_dev with the given name, locate its sibling i2c + * device and in turn the i2c-dev under that representing the actual + * I2C bus that runs over DPDDC on the port represented by the + * drm_dp_aux_dev */ + for (GList *element = matches; element != NULL; element = element->next) { + g_autoptr(FuUdevDevice) device = NULL; + g_autoptr(GPtrArray) i2c_devices = NULL; + + device = fu_udev_device_new(fu_device_get_context(FU_DEVICE(self)), element->data); + if (bus_device != NULL) { + g_debug("Ignoring additional aux device %s", + fu_udev_device_get_sysfs_path(device)); + continue; + } + + i2c_devices = fu_udev_device_get_siblings_with_subsystem(device, "i2c"); + bus_device = locate_i2c_bus(i2c_devices); + } + + if (bus_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "did not find an i2c-dev associated with DP aux \"%s\"", + self->dp_aux_dev_name); + return FALSE; + } + fu_udev_device_set_dev(FU_UDEV_DEVICE(self), fu_udev_device_get_dev(bus_device)); + return TRUE; +} + +static gboolean +fu_realtek_mst_device_use_drm_card(FuRealtekMstDevice *self, GError **error) +{ + g_autoptr(GUdevClient) udev_client = g_udev_client_new(NULL); + g_autoptr(GUdevEnumerator) enumerator = g_udev_enumerator_new(udev_client); + g_autoptr(GList) drm_devices = NULL; + g_autoptr(FuUdevDevice) bus_device = NULL; + + /* from a drm device with the given name, find an i2c device under it + * and in turn an i2c-dev device representing the DPDDC bus */ + g_debug("search for DRM device with name %s", self->dp_card_kernel_name); + g_udev_enumerator_add_match_subsystem(enumerator, "drm"); + g_udev_enumerator_add_match_name(enumerator, self->dp_card_kernel_name); + drm_devices = g_udev_enumerator_execute(enumerator); + for (GList *element = drm_devices; element != NULL; element = element->next) { + g_autoptr(FuUdevDevice) drm_device = NULL; + g_autoptr(GPtrArray) i2c_devices = NULL; + + drm_device = + fu_udev_device_new(fu_device_get_context(FU_DEVICE(self)), element->data); + if (bus_device != NULL) { + g_debug("Ignoring additional drm device %s", + fu_udev_device_get_sysfs_path(drm_device)); + continue; + } + + i2c_devices = fu_udev_device_get_children_with_subsystem(drm_device, "i2c"); + bus_device = locate_i2c_bus(i2c_devices); + } + + if (bus_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "did not find an i2c-dev associated with drm device %s", + self->dp_card_kernel_name); + return FALSE; + } + fu_udev_device_set_dev(FU_UDEV_DEVICE(self), fu_udev_device_get_dev(bus_device)); + return TRUE; +} + +static gboolean +mst_ensure_device_address(FuRealtekMstDevice *self, guint8 address, GError **error) +{ + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + I2C_SLAVE, + (guint8 *)(guintptr)address, + NULL, + FU_REALTEK_MST_DEVICE_IOCTL_TIMEOUT, + error); +} + +/** Write a value to a device register */ +static gboolean +mst_write_register(FuRealtekMstDevice *self, guint8 address, guint8 value, GError **error) +{ + const guint8 command[] = {address, value}; + return fu_i2c_device_write(FU_I2C_DEVICE(self), command, sizeof(command), error); +} + +static gboolean +mst_write_register_multi(FuRealtekMstDevice *self, + guint8 address, + const guint8 *data, + gsize count, + GError **error) +{ + g_autofree guint8 *command = g_malloc0(count + 1); + memcpy(command + 1, data, count); + command[0] = address; + return fu_i2c_device_write(FU_I2C_DEVICE(self), command, count + 1, error); +} + +/** Read a register from the device */ +static gboolean +mst_read_register(FuRealtekMstDevice *self, guint8 address, guint8 *value, GError **error) +{ + if (!fu_i2c_device_write(FU_I2C_DEVICE(self), &address, 0x1, error)) + return FALSE; + return fu_i2c_device_read(FU_I2C_DEVICE(self), value, 0x1, error); +} + +static gboolean +mst_set_indirect_address(FuRealtekMstDevice *self, guint16 address, GError **error) +{ + if (!mst_write_register(self, REG_INDIRECT_LO, 0x9F, error)) + return FALSE; + if (!mst_write_register(self, REG_INDIRECT_HI, address >> 8, error)) + return FALSE; + return mst_write_register(self, REG_INDIRECT_LO, address, error); +} + +static gboolean +mst_read_register_indirect(FuRealtekMstDevice *self, guint16 address, guint8 *value, GError **error) +{ + if (!mst_set_indirect_address(self, address, error)) + return FALSE; + return mst_read_register(self, REG_INDIRECT_HI, value, error); +} + +static gboolean +mst_write_register_indirect(FuRealtekMstDevice *self, guint16 address, guint8 value, GError **error) +{ + if (!mst_set_indirect_address(self, address, error)) + return FALSE; + return mst_write_register(self, REG_INDIRECT_HI, value, error); +} + +/** + * Wait until a device register reads an expected value. + * + * Waiting up to @timeout_seconds, poll the given @address for the read value + * bitwise-ANDed with @mask to be equal to @expected. + * + * Returns an error if the timeout expires or in case of an I/O error. + */ +static gboolean +mst_poll_register(FuRealtekMstDevice *self, + guint8 address, + guint8 mask, + guint8 expected, + guint timeout_seconds, + GError **error) +{ + guint8 value; + g_autoptr(GTimer) timer = g_timer_new(); + + if (!mst_read_register(self, address, &value, error)) + return FALSE; + while ((value & mask) != expected && g_timer_elapsed(timer, NULL) <= timeout_seconds) { + g_usleep(G_TIME_SPAN_MILLISECOND); + if (!mst_read_register(self, address, &value, error)) + return FALSE; + } + if ((value & mask) == expected) + return TRUE; + + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "register %x still reads %x after %us, wanted %x (mask %x)", + address, + value, + timeout_seconds, + expected, + mask); + return FALSE; +} + +static gboolean +mst_set_gpio88(FuRealtekMstDevice *self, gboolean level, GError **error) +{ + guint8 value; + + /* ensure pin is configured as push-pull GPIO */ + if (!mst_read_register_indirect(self, REG_GPIO88_CONFIG, &value, error)) + return FALSE; + if (!mst_write_register_indirect(self, REG_GPIO88_CONFIG, (value & 0xF0) | 1, error)) + return FALSE; + + /* set output level */ + g_debug("set pin 88 = %d", level); + if (!mst_read_register_indirect(self, REG_GPIO88_VALUE, &value, error)) + return FALSE; + return mst_write_register_indirect(self, + REG_GPIO88_VALUE, + (value & 0xFE) | (level != FALSE), + error); +} + +static gboolean +fu_realtek_mst_device_probe(FuDevice *device, GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + FuContext *context = fu_device_get_context(device); + + /* FuI2cDevice->probe */ + if (!FU_DEVICE_CLASS(fu_realtek_mst_device_parent_class)->probe(device, error)) + return FALSE; + + /* add custom instance ID and load matching quirks */ + fu_device_add_instance_str(device, + "FAMILY", + fu_context_get_hwid_value(context, FU_HWIDS_KEY_FAMILY)); + if (!fu_device_build_instance_id_quirk(device, error, "I2C", "NAME", "FAMILY", NULL)) + return FALSE; + + /* having loaded quirks, check this device is supported */ + if (self->dp_aux_dev_name != NULL) { + if (!fu_realtek_mst_device_use_aux_dev(self, error)) + return FALSE; + } else if (self->dp_card_kernel_name != NULL) { + if (!fu_realtek_mst_device_use_drm_card(self, error)) + return FALSE; + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "one of RealtekMstDpAuxName or RealtekMstDrmCardKernelName" + " must be specified"); + return FALSE; + } + + /* locate its sibling i2c device and use that instead */ + + /* success */ + return TRUE; +} + +static gboolean +fu_realtek_mst_device_get_dual_bank_info(FuRealtekMstDevice *self, + struct dual_bank_info *info, + GError **error) +{ + const guint8 request[] = {0x01}; + guint8 response[11] = {0x0}; + + if (!mst_ensure_device_address(self, I2C_ADDR_DEBUG, error)) + return FALSE; + + /* switch to DDCCI mode */ + if (!mst_write_register(self, 0xca, 0x09, error)) + return FALSE; + + /* wait for mode switch to complete */ + g_usleep(200 * G_TIME_SPAN_MILLISECOND); + + /* request dual bank state and read back */ + if (!fu_i2c_device_write(FU_I2C_DEVICE(self), request, sizeof(request), error)) + return FALSE; + if (!fu_i2c_device_read(FU_I2C_DEVICE(self), response, sizeof(response), error)) + return FALSE; + + if (response[0] != 0xca || response[1] != 9) { + /* unexpected response code or length usually means the current + * firmware doesn't support dual-bank mode at all */ + g_debug("unexpected response code %#x, length %d", response[0], response[1]); + info->is_enabled = FALSE; + return TRUE; + } + + /* enable flag, assume anything other than 1 is unsupported */ + if (response[2] != 1) { + info->is_enabled = FALSE; + return TRUE; + } + info->is_enabled = TRUE; + + info->mode = response[3]; + if (info->mode > DUAL_BANK_MAX_VALUE) { + g_debug("unexpected dual bank mode value %#x", info->mode); + info->is_enabled = FALSE; + return TRUE; + } + + info->active_bank = response[4]; + if (info->active_bank > FLASH_BANK_MAX_VALUE) { + g_debug("unexpected active flash bank value %#x", info->active_bank); + info->is_enabled = FALSE; + return TRUE; + } + + info->user1_version[0] = response[5]; + info->user1_version[1] = response[6]; + info->user2_version[0] = response[7]; + info->user2_version[1] = response[8]; + /* last two bytes of response are reserved */ + return TRUE; +} + +static gboolean +fu_realtek_mst_device_probe_version(FuDevice *device, GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + struct dual_bank_info info = {0x0}; + guint8 *active_version; + g_autofree gchar *version_str = NULL; + + /* ensure probed state is cleared in case of error */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + self->active_bank = FLASH_BANK_INVALID; + fu_device_set_version(device, NULL); + + if (!fu_realtek_mst_device_get_dual_bank_info(FU_REALTEK_MST_DEVICE(self), &info, error)) + return FALSE; + + if (!info.is_enabled) { + fu_device_inhibit(device, "dual-bank", "Dual-bank mode is not enabled"); + return TRUE; + } + if (info.mode != DUAL_BANK_DIFF) { + fu_device_inhibit(device, "dual-bank", "Can only update from dual-bank-diff mode"); + return TRUE; + } + /* dual-bank mode seems to be fully supported, so we can update + * regardless of the active bank- if it's FLASH_BANK_BOOT, updating is + * possible even if the current version is unknown */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_uninhibit(device, "dual-bank"); + + g_debug("device is currently running from bank %u", info.active_bank); + g_return_val_if_fail(info.active_bank <= FLASH_BANK_MAX_VALUE, FALSE); + self->active_bank = info.active_bank; + + g_debug("firmware version reports user1 %d.%d, user2 %d.%d", + info.user1_version[0], + info.user1_version[1], + info.user2_version[0], + info.user2_version[1]); + if (info.active_bank == FLASH_BANK_USER1) + active_version = info.user1_version; + else if (info.active_bank == FLASH_BANK_USER2) + active_version = info.user2_version; + else + /* only user bank versions are reported, can't tell otherwise */ + return TRUE; + + version_str = g_strdup_printf("%u.%u", active_version[0], active_version[1]); + fu_device_set_version(FU_DEVICE(self), version_str); + return TRUE; +} + +static gboolean +flash_iface_read(FuRealtekMstDevice *self, + guint32 address, + guint8 *buf, + const gsize buf_size, + FuProgress *progress, + GError **error) +{ + gsize bytes_read = 0; + guint8 byte; + const guint8 req[] = {0x70}; + + g_return_val_if_fail(address < FLASH_SIZE, FALSE); + g_return_val_if_fail(buf_size <= FLASH_SIZE, FALSE); + + g_debug("read %#" G_GSIZE_MODIFIER "x bytes from %#08x", buf_size, address); + + /* read must start one byte prior to the desired address and ignore the + * first byte of data, since the first read value is unpredictable */ + address = (address - 1) & 0xFFFFFF; + if (!mst_write_register(self, REG_CMD_ADDR_HI, address >> 16, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_MID, address >> 8, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_LO, address, error)) + return FALSE; + if (!mst_write_register(self, REG_READ_OPCODE, CMD_OPCODE_READ, error)) + return FALSE; + + /* ignore first byte of data */ + if (!fu_i2c_device_write(FU_I2C_DEVICE(self), req, sizeof(req), error)) + return FALSE; + if (!fu_i2c_device_read(FU_I2C_DEVICE(self), &byte, 0x1, error)) + return FALSE; + + while (bytes_read < buf_size) { + /* read up to 256 bytes in one transaction */ + gsize read_len = buf_size - bytes_read; + if (read_len > 256) + read_len = 256; + + if (!fu_i2c_device_read(FU_I2C_DEVICE(self), buf + bytes_read, read_len, error)) + return FALSE; + + bytes_read += read_len; + fu_progress_set_percentage_full(progress, bytes_read, buf_size); + } + return TRUE; +} + +static gboolean +flash_iface_erase_sector(FuRealtekMstDevice *self, guint32 address, GError **error) +{ + /* address must be 4k-aligned */ + g_return_val_if_fail((address & 0xFFF) == 0, FALSE); + g_debug("sector erase %#08x-%#08x", address, address + FLASH_SECTOR_SIZE); + + /* sector address */ + if (!mst_write_register(self, REG_CMD_ADDR_HI, address >> 16, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_MID, address >> 8, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_LO, address, error)) + return FALSE; + /* command type + WREN */ + if (!mst_write_register(self, REG_CMD_ATTR, 0xB8, error)) + return FALSE; + /* sector erase opcode */ + if (!mst_write_register(self, REG_ERASE_OPCODE, CMD_OPCODE_ERASE_SECTOR, error)) + return FALSE; + /* begin operation and wait for completion */ + if (!mst_write_register(self, REG_CMD_ATTR, 0xB8 | CMD_ERASE_BUSY, error)) + return FALSE; + return mst_poll_register(self, REG_CMD_ATTR, CMD_ERASE_BUSY, 0, 10, error); +} +static gboolean +flash_iface_erase_block(FuRealtekMstDevice *self, guint32 address, GError **error) +{ + /* address must be 64k-aligned */ + g_return_val_if_fail((address & 0xFFFF) == 0, FALSE); + g_debug("block erase %#08x-%#08x", address, address + FLASH_BLOCK_SIZE); + + /* block address */ + if (!mst_write_register(self, REG_CMD_ADDR_HI, address >> 16, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_MID, 0, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_LO, 0, error)) + return FALSE; + /* command type + WREN */ + if (!mst_write_register(self, REG_CMD_ATTR, 0xB8, error)) + return FALSE; + /* block erase opcode */ + if (!mst_write_register(self, REG_ERASE_OPCODE, CMD_OPCODE_ERASE_BLOCK, error)) + return FALSE; + /* begin operation and wait for completion */ + if (!mst_write_register(self, REG_CMD_ATTR, 0xB8 | CMD_ERASE_BUSY, error)) + return FALSE; + return mst_poll_register(self, REG_CMD_ATTR, CMD_ERASE_BUSY, 0, 10, error); +} + +static gboolean +flash_iface_write(FuRealtekMstDevice *self, + guint32 address, + GBytes *data, + FuProgress *progress, + GError **error) +{ + gsize total_size = g_bytes_get_size(data); + g_autoptr(GPtrArray) chunks = fu_chunk_array_new_from_bytes(data, address, 0, 256); + + g_debug("write %#" G_GSIZE_MODIFIER "x bytes at %#08x", total_size, address); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chunk = g_ptr_array_index(chunks, i); + guint32 chunk_address = fu_chunk_get_address(chunk); + guint32 chunk_size = fu_chunk_get_data_sz(chunk); + + /* write opcode */ + if (!mst_write_register(self, REG_WRITE_OPCODE, CMD_OPCODE_WRITE, error)) + return FALSE; + /* write length */ + if (!mst_write_register(self, REG_WRITE_LEN, chunk_size - 1, error)) + return FALSE; + /* target address */ + if (!mst_write_register(self, REG_CMD_ADDR_HI, chunk_address >> 16, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_MID, chunk_address >> 8, error)) + return FALSE; + if (!mst_write_register(self, REG_CMD_ADDR_LO, chunk_address, error)) + return FALSE; + /* ensure write buffer is empty */ + if (!mst_poll_register(self, + REG_MCU_MODE, + MCU_MODE_WRITE_BUF, + MCU_MODE_WRITE_BUF, + 10, + error)) { + g_prefix_error(error, "failed waiting for write buffer to clear: "); + return FALSE; + } + /* write data into FIFO */ + if (!mst_write_register_multi(self, + REG_WRITE_FIFO, + fu_chunk_get_data(chunk), + chunk_size, + error)) + return FALSE; + /* begin operation and wait for completion */ + if (!mst_write_register(self, + REG_MCU_MODE, + MCU_MODE_ISP | MCU_MODE_WRITE_BUSY, + error)) + return FALSE; + if (!mst_poll_register(self, REG_MCU_MODE, MCU_MODE_WRITE_BUSY, 0, 10, error)) { + g_prefix_error(error, + "timed out waiting for write at %#x to complete: ", + address); + return FALSE; + } + fu_progress_set_percentage_full(progress, i + 1, chunks->len); + } + + return TRUE; +} + +static gboolean +fu_realtek_mst_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + + if (!mst_ensure_device_address(self, I2C_ADDR_ISP, error)) + return FALSE; + + /* Switch to programming mode (stops regular operation) */ + if (!mst_write_register(self, REG_MCU_MODE, MCU_MODE_ISP, error)) + return FALSE; + g_debug("wait for ISP mode ready"); + if (!mst_poll_register(self, REG_MCU_MODE, MCU_MODE_ISP, MCU_MODE_ISP, 60, error)) + return FALSE; + + /* magic value makes the MCU clock run faster than normal; this both + * helps programming performance and fixes flakiness where register + * writes sometimes get nacked for no apparent reason */ + if (!mst_write_register_indirect(self, 0x06A0, 0x74, error)) + return FALSE; + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + + /* Disable hardware write protect, assuming Flash ~WP is connected to + * device pin 88, a GPIO. */ + return mst_set_gpio88(self, 1, error); +} + +static gboolean +fu_realtek_mst_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + /* write an inactive bank: USER2 if USER1 is active, otherwise USER1 + * (including if the boot bank is active) */ + guint32 base_addr = self->active_bank == FLASH_BANK_USER1 ? FLASH_USER2_ADDR + : FLASH_USER1_ADDR; + guint32 flag_addr = self->active_bank == FLASH_BANK_USER1 ? FLASH_FLAG2_ADDR + : FLASH_FLAG1_ADDR; + GBytes *firmware_bytes = fu_firmware_get_bytes(firmware, error); + const guint8 flag_data[] = {0xaa, 0xaa, 0xaa, 0xff, 0xff}; + g_autofree guint8 *readback_buf = g_malloc0(FLASH_USER_SIZE); + + g_return_val_if_fail(g_bytes_get_size(firmware_bytes) == FLASH_USER_SIZE, FALSE); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 70, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 9, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "flag"); + + if (!mst_ensure_device_address(self, I2C_ADDR_ISP, error)) + return FALSE; + + /* erase old image */ + g_debug("erase old image from %#x", base_addr); + for (guint32 offset = 0; offset < FLASH_USER_SIZE; offset += FLASH_BLOCK_SIZE) { + if (!flash_iface_erase_block(self, base_addr + offset, error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + offset + FLASH_BLOCK_SIZE, + FLASH_USER_SIZE); + } + fu_progress_step_done(progress); + + /* write new image */ + g_debug("write new image to %#x", base_addr); + if (!flash_iface_write(self, + base_addr, + firmware_bytes, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + if (!flash_iface_read(self, + base_addr, + readback_buf, + FLASH_USER_SIZE, + fu_progress_get_child(progress), + error)) + return FALSE; + if (memcmp(g_bytes_get_data(firmware_bytes, NULL), readback_buf, FLASH_USER_SIZE) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "flash contents after write do not match firmware image"); + return FALSE; + } + fu_progress_step_done(progress); + + /* Erase old flag and write new one. The MST appears to modify the + * flag value once booted, so we always write the same value here and + * it picks up what we've updated. */ + if (!flash_iface_erase_sector(self, flag_addr & ~(FLASH_SECTOR_SIZE - 1), error)) + return FALSE; + if (!flash_iface_write(self, + flag_addr, + g_bytes_new_static(flag_data, sizeof(flag_data)), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_realtek_mst_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + guint32 bank_address; + g_autofree guint8 *image_bytes = NULL; + + if (self->active_bank == FLASH_BANK_USER1) + bank_address = FLASH_USER1_ADDR; + else if (self->active_bank == FLASH_BANK_USER2) + bank_address = FLASH_USER2_ADDR; + else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot read firmware from bank %u", + self->active_bank); + return NULL; + } + + image_bytes = g_malloc0(FLASH_USER_SIZE); + if (!mst_ensure_device_address(self, I2C_ADDR_ISP, error)) + return NULL; + if (!flash_iface_read(self, bank_address, image_bytes, FLASH_USER_SIZE, progress, error)) + return NULL; + return fu_firmware_new_from_bytes( + g_bytes_new_take(g_steal_pointer(&image_bytes), FLASH_USER_SIZE)); +} + +static GBytes * +fu_realtek_mst_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + g_autofree guint8 *flash_contents = g_malloc0(FLASH_SIZE); + + if (!mst_ensure_device_address(self, I2C_ADDR_ISP, error)) + return NULL; + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + if (!flash_iface_read(self, 0, flash_contents, FLASH_SIZE, progress, error)) + return NULL; + fu_progress_set_status(progress, FWUPD_STATUS_IDLE); + + return g_bytes_new_take(g_steal_pointer(&flash_contents), FLASH_SIZE); +} + +static gboolean +fu_realtek_mst_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(device); + guint8 value; + + if (!mst_ensure_device_address(self, I2C_ADDR_ISP, error)) + return FALSE; + + /* re-enable hardware write protect via GPIO */ + if (!mst_set_gpio88(self, 0, error)) + return FALSE; + + if (!mst_read_register(self, REG_MCU_MODE, &value, error)) + return FALSE; + if ((value & MCU_MODE_ISP) != 0) { + g_autoptr(GError) error_local = NULL; + + g_debug("resetting device to exit ISP mode"); + + /* Set register EE bit 2 to request reset. This write can fail + * spuriously, so we ignore the write result and verify the device is + * no longer in programming mode after giving it time to reset. */ + if (!mst_read_register(self, 0xEE, &value, error)) + return FALSE; + if (!mst_write_register(self, 0xEE, value | 2, &error_local)) { + g_debug("write spuriously failed, ignoring: %s", error_local->message); + } + + /* allow device some time to reset */ + g_usleep(G_USEC_PER_SEC); + + /* verify device has exited programming mode and actually reset */ + if (!mst_read_register(self, REG_MCU_MODE, &value, error)) + return FALSE; + if ((value & MCU_MODE_ISP) == MCU_MODE_ISP) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NEEDS_USER_ACTION, + "device failed to reset when requested"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN); + return FALSE; + } + } else { + g_debug("device is already in normal mode"); + } + + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static void +fu_realtek_mst_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_realtek_mst_device_init(FuRealtekMstDevice *self) +{ + self->active_bank = FLASH_BANK_INVALID; + + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rtd2142"); + fu_device_set_vendor(FU_DEVICE(self), "Realtek"); + fu_device_add_vendor_id(FU_DEVICE(self), "PCI:0x10EC"); + fu_device_set_summary(FU_DEVICE(self), "DisplayPort MST hub"); + fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_set_firmware_size(FU_DEVICE(self), FLASH_USER_SIZE); +} + +static void +fu_realtek_mst_device_finalize(GObject *object) +{ + FuRealtekMstDevice *self = FU_REALTEK_MST_DEVICE(object); + g_free(self->dp_aux_dev_name); + g_free(self->dp_card_kernel_name); + G_OBJECT_CLASS(fu_realtek_mst_device_parent_class)->finalize(object); +} + +static void +fu_realtek_mst_device_class_init(FuRealtekMstDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *klass_object = G_OBJECT_CLASS(klass); + + klass_object->finalize = fu_realtek_mst_device_finalize; + klass_device->probe = fu_realtek_mst_device_probe; + klass_device->set_quirk_kv = fu_realtek_mst_device_set_quirk_kv; + klass_device->setup = fu_realtek_mst_device_probe_version; + klass_device->detach = fu_realtek_mst_device_detach; + klass_device->attach = fu_realtek_mst_device_attach; + klass_device->write_firmware = fu_realtek_mst_device_write_firmware; + klass_device->reload = fu_realtek_mst_device_probe_version; + /* read active image */ + klass_device->read_firmware = fu_realtek_mst_device_read_firmware; + /* dump whole flash */ + klass_device->dump_firmware = fu_realtek_mst_device_dump_firmware; + klass_device->set_progress = fu_realtek_mst_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-device.h b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-device.h new file mode 100644 index 0000000000000000000000000000000000000000..4ff9a991d88d72b74cc1ad3ec93ef54bb9a829f6 --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Peter Marheine + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_REALTEK_MST_DEVICE (fu_realtek_mst_device_get_type()) +G_DECLARE_FINAL_TYPE(FuRealtekMstDevice, fu_realtek_mst_device, FU, REALTEK_MST_DEVICE, FuI2cDevice) diff --git a/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-plugin.c b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..e535ef44c1d7fdf758867d209d518e9f6002e512 --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-plugin.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Peter Marheine + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-realtek-mst-device.h" +#include "fu-realtek-mst-plugin.h" + +struct _FuRealtekMstPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuRealtekMstPlugin, fu_realtek_mst_plugin, FU_TYPE_PLUGIN) + +static void +fu_realtek_mst_plugin_init(FuRealtekMstPlugin *self) +{ +} + +static void +fu_realtek_mst_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "RealtekMstDpAuxName"); + fu_context_add_quirk_key(ctx, "RealtekMstDrmCardKernelName"); + fu_plugin_add_udev_subsystem(plugin, "i2c"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_REALTEK_MST_DEVICE); +} + +static void +fu_realtek_mst_plugin_class_init(FuRealtekMstPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_realtek_mst_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-plugin.h b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..0ff9ac804683b803a889735422ec15556263551f --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/fu-realtek-mst-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuRealtekMstPlugin, fu_realtek_mst_plugin, FU, REALTEK_MST_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/realtek-mst/meson.build b/fwupd-1.8.6/plugins/realtek-mst/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..6beec9023dad0d7675def8dfaabbbc1f4a649a4f --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/meson.build @@ -0,0 +1,19 @@ +if get_option('plugin_realtek_mst').require(gudev.found(), + error_message: 'gudev is needed for plugin_realtek_mst').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginRealtekMst"'] + +plugin_quirks += files('realtek-mst.quirk') +plugin_builtins += static_library('fu_plugin_realtek_mst', + sources: [ + 'fu-realtek-mst-device.c', + 'fu-realtek-mst-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/realtek-mst/realtek-mst.quirk b/fwupd-1.8.6/plugins/realtek-mst/realtek-mst.quirk new file mode 100644 index 0000000000000000000000000000000000000000..ba41d6e233ec7ccf3ec675108a2ba678917ae76f --- /dev/null +++ b/fwupd-1.8.6/plugins/realtek-mst/realtek-mst.quirk @@ -0,0 +1,13 @@ +[I2C\NAME_10EC2142:00] +Plugin = realtek_mst +Name = RTD2142 + +[I2C\NAME_10EC2142:00&FAMILY_Google_Hatch] +RealtekMstDpAuxName = DPDDC-C + +[I2C\NAME_10EC2141:00] +Plugin = realtek_mst +Name = RTD2141B + +[I2C\NAME_10EC2141:00&FAMILY_Google_Zork] +RealtekMstDrmCardKernelName = card0-DP-1 diff --git a/fwupd-1.8.6/plugins/redfish/README.md b/fwupd-1.8.6/plugins/redfish/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b7de25d2e886a9155e838db6334f61be47ca5561 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/README.md @@ -0,0 +1,110 @@ +# Redfish + +## 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` property +without modification if it is a valid GUID. If the property is not a GUID then +the vendor instance ID is used instead: + +* `REDFISH\\VENDOR_${RedfishManufacturer}&SOFTWAREID_${RedfishSoftwareId}` + +Additionally, this Instance ID is added for quirk and parent matching: + +* `REDFISH\VENDOR_${RedfishManufacturer}&ID_${RedfishId}` + +## Update Behavior + +The firmware will be deployed as appropriate. The Redfish API does not specify +when the firmware will actually be written to the SPI device. + +## Vendor ID Security + +No vendor ID is set as there is no vendor field in the schema. + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### RedfishResetPreDelay + +Delay in ms to use before querying the manager after a cleanup reset, default 0ms. + +Since: 1.8.0 + +### RedfishResetPostDelay + +Delay in ms to use before querying /redfish/v1/UpdateService after a cleanup reset, +default 0ms. + +Since: 1.8.0 + +## Setting Service IP Manually + +The service IP may not be automatically discoverable due to the absence of +Type 42 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: + +```shell +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: + +```json + { + "@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 + +```shell +curl http://192.168.0.133:80/redfish/v1/ +``` + +or + +```shell +curl -k https://192.168.0.133:443/redfish/v1/ +``` + +## External Interface Access + +This requires HTTP access to a given URL. diff --git a/fwupd-1.8.6/plugins/redfish/fu-ipmi-device.c b/fwupd-1.8.6/plugins/redfish/fu-ipmi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..d436a2af6704ea384d4a9d1ff64fb5e52a334158 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-ipmi-device.c @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fu-ipmi-device.h" + +#define FU_IPMI_DEVICE_TIMEOUT 1500 /* ms */ + +#define FU_IPMI_TRANSACTION_RETRY_COUNT 5 +#define FU_IPMI_TRANSACTION_RETRY_DELAY 200 /* ms */ + +/* not defined in linux/ipmi_msgdefs.h */ +#define IPMI_SET_USER_ACCESS 0x43 +#define IPMI_SET_USER_NAME 0x45 +#define IPMI_GET_USER_NAME 0x46 +#define IPMI_SET_USER_PASSWORD 0x47 + +#define IPMI_PASSWORD_DISABLE_USER 0x00 +#define IPMI_PASSWORD_ENABLE_USER 0x01 +#define IPMI_PASSWORD_SET_PASSWORD 0x02 +#define IPMI_PASSWORD_TEST_PASSWORD 0x03 + +/* these are not provided in ipmi_msgdefs.h */ +#define IPMI_INVALID_COMMAND_ON_LUN_ERR 0xC2 +#define IPMI_OUT_OF_SPACE_ERR 0xC4 +#define IPMI_CANCELLED_OR_INVALID_ERR 0xC5 +#define IPMI_OUT_OF_RANGE_ERR 0xC9 +#define IPMI_CANNOT_RETURN_DATA_ERR 0xCA +#define IPMI_NOT_FOUND_ERR 0xCB +#define IPMI_INVALID_DATA_FIELD_ERR 0xCC +#define IPMI_COMMAND_ILLEGAL_ERR 0xCD +#define IPMI_RESPONSE_NOT_PROVIDED_ERR 0xCE +#define IPMI_DUPLICATED_REQUEST_ERR 0xCF +#define IPMI_SDR_IN_UPDATE_MODE_ERR 0xD0 +#define IPMI_DESTINATION_UNAVAILABLE_ERR 0xD3 +#define IPMI_INSUFFICIENT_PRIVILEGE_ERR 0xD4 +#define IPMI_COMMAND_DISABLED_ERR 0xD6 + +#ifndef IPMI_DEVICE_IN_UPDATE_MODE_ERR +#define IPMI_DEVICE_IN_UPDATE_MODE_ERR 0xD1 +#endif +#ifndef IPMI_DEVICE_IN_INIT_ERR +#define IPMI_DEVICE_IN_INIT_ERR 0xD2 +#endif + +struct _FuIpmiDevice { + FuUdevDevice parent_instance; + glong seq; + guint8 device_id; + guint8 device_rev; + guint8 version_ipmi; +}; + +G_DEFINE_TYPE(FuIpmiDevice, fu_ipmi_device, FU_TYPE_UDEV_DEVICE) + +#define FU_IPMI_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static void +fu_ipmi_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuIpmiDevice *self = FU_IPMI_DEVICE(device); + fu_string_append_kx(str, idt, "DeviceId", self->device_id); + fu_string_append_kx(str, idt, "DeviceRev", self->device_rev); + fu_string_append_kx(str, idt, "VersionIpmi", self->version_ipmi); +} + +static gboolean +fu_ipmi_device_send(FuIpmiDevice *self, + guint8 netfn, + guint8 cmd, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + g_autofree guint8 *buf2 = fu_memdup_safe(buf, bufsz, NULL); + struct ipmi_system_interface_addr addr = {.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE, + .channel = IPMI_BMC_CHANNEL}; + struct ipmi_req req = { + .addr = (guint8 *)&addr, + .addr_len = sizeof(addr), + .msgid = self->seq++, + .msg.data = buf2, + .msg.data_len = (guint16)bufsz, + .msg.netfn = netfn, + .msg.cmd = cmd, + }; + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL && buf2 != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ipmi-send", buf2, bufsz); + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + IPMICTL_SEND_COMMAND, + (guint8 *)&req, + NULL, + FU_IPMI_DEVICE_IOCTL_TIMEOUT, + error); +} + +static gboolean +fu_ipmi_device_recv(FuIpmiDevice *self, + guint8 *netfn, + guint8 *cmd, + glong *seq, + guint8 *buf, + gsize bufsz, + gsize *len, /* optional, out */ + GError **error) +{ + struct ipmi_addr addr = {0}; + struct ipmi_recv recv = { + .addr = (guint8 *)&addr, + .addr_len = sizeof(addr), + .msg.data = buf, + .msg.data_len = bufsz, + }; + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + IPMICTL_RECEIVE_MSG_TRUNC, + (guint8 *)&recv, + NULL, + FU_IPMI_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL && buf != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ipmi-recv", buf, bufsz); + if (netfn != NULL) + *netfn = recv.msg.netfn; + if (cmd != NULL) + *cmd = recv.msg.cmd; + if (seq != NULL) + *seq = recv.msgid; + if (len != NULL) + *len = (gsize)recv.msg.data_len; + return TRUE; +} + +static gboolean +fu_ipmi_device_lock(GObject *device, GError **error) +{ + FuIpmiDevice *self = FU_IPMI_DEVICE(device); + struct flock lock = {.l_type = F_WRLCK, .l_whence = SEEK_SET}; + if (fcntl(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), F_SETLKW, &lock) == -1) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "error locking IPMI device: %m"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_ipmi_device_unlock(GObject *device, GError **error) +{ + FuIpmiDevice *self = FU_IPMI_DEVICE(device); + struct flock lock = {.l_type = F_UNLCK}; + if (fcntl(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), F_SETLKW, &lock) == -1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "error unlocking IPMI device: %m"); + return FALSE; + } + return TRUE; +} + +static const gchar * +fu_ipmi_device_errcode_to_string(guint8 errcode) +{ + if (errcode == IPMI_CC_NO_ERROR) + return "no-error"; + if (errcode == IPMI_NODE_BUSY_ERR) + return "node-busy"; + if (errcode == IPMI_INVALID_COMMAND_ERR) + return "invalid-command"; + if (errcode == IPMI_TIMEOUT_ERR) + return "timeout"; + if (errcode == IPMI_ERR_MSG_TRUNCATED) + return "msg-truncated"; + if (errcode == IPMI_REQ_LEN_INVALID_ERR) + return "req-len-invalid"; + if (errcode == IPMI_REQ_LEN_EXCEEDED_ERR) + return "req-len-exceeded"; + if (errcode == IPMI_DEVICE_IN_UPDATE_MODE_ERR) + return "device-in-update-mode"; + if (errcode == IPMI_DEVICE_IN_INIT_ERR) + return "device-in-init"; + if (errcode == IPMI_NOT_IN_MY_STATE_ERR) + return "not-in-my-state"; + if (errcode == IPMI_LOST_ARBITRATION_ERR) + return "lost-arbitration"; + if (errcode == IPMI_BUS_ERR) + return "bus-error"; + if (errcode == IPMI_NAK_ON_WRITE_ERR) + return "nak-on-write"; + if (errcode == IPMI_ERR_UNSPECIFIED) + return "unspecified"; + /* these are not defined in ipmi_msgdefs.h but used in reality */ + if (errcode == IPMI_INVALID_COMMAND_ON_LUN_ERR) + return "invalid-command-on-lun"; + if (errcode == IPMI_OUT_OF_SPACE_ERR) + return "out-of-space"; + if (errcode == IPMI_CANCELLED_OR_INVALID_ERR) + return "cancelled-or-invalid"; + if (errcode == IPMI_OUT_OF_RANGE_ERR) + return "out-of-range"; + if (errcode == IPMI_CANNOT_RETURN_DATA_ERR) + return "cannot-return-data"; + if (errcode == IPMI_NOT_FOUND_ERR) + return "not-found"; + if (errcode == IPMI_INVALID_DATA_FIELD_ERR) + return "invalid-data-field"; + if (errcode == IPMI_COMMAND_ILLEGAL_ERR) + return "command-illegal"; + if (errcode == IPMI_RESPONSE_NOT_PROVIDED_ERR) + return "response-not-provided"; + if (errcode == IPMI_DUPLICATED_REQUEST_ERR) + return "duplicated-request"; + if (errcode == IPMI_SDR_IN_UPDATE_MODE_ERR) + return "sdr-in-update-mode"; + if (errcode == IPMI_DESTINATION_UNAVAILABLE_ERR) + return "destination-unavailable"; + if (errcode == IPMI_INSUFFICIENT_PRIVILEGE_ERR) + return "insufficient-privilege"; + if (errcode == IPMI_COMMAND_DISABLED_ERR) + return "command-disabled"; + return "unknown"; +} + +static gboolean +fu_ipmi_device_errcode_to_error(guint8 errcode, GError **error) +{ + /* success */ + if (errcode == IPMI_CC_NO_ERROR) + return TRUE; + + /* data not found, seemingly Lenovo specific */ + if (errcode == IPMI_INVALID_DATA_FIELD_ERR || errcode == IPMI_NOT_FOUND_ERR) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "CC error: %s [0x%02X]", + fu_ipmi_device_errcode_to_string(errcode), + errcode); + return FALSE; + } + + /* fallback */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "CC error: %s [0x%02X]", + fu_ipmi_device_errcode_to_string(errcode), + errcode); + return FALSE; +} + +typedef struct { + guint8 netfn; + guint8 cmd; + const guint8 *req_buf; + gsize req_bufsz; + guint8 *resp_buf; + gsize resp_bufsz; + gsize *resp_len; + gint timeout_ms; +} FuIpmiDeviceTransactionHelper; + +static gboolean +fu_ipmi_device_transaction_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuIpmiDevice *self = FU_IPMI_DEVICE(device); + FuIpmiDeviceTransactionHelper *helper = (FuIpmiDeviceTransactionHelper *)user_data; + GPollFD pollfds[1]; + gsize resp_buf2sz = helper->resp_bufsz + 1; + gsize resp_len2 = 0; + g_autoptr(GTimer) timer = g_timer_new(); + g_autoptr(FuDeviceLocker) lock = NULL; + g_autofree guint8 *resp_buf2 = g_malloc0(resp_buf2sz); + + lock = fu_device_locker_new_full(self, fu_ipmi_device_lock, fu_ipmi_device_unlock, error); + if (lock == NULL) + return FALSE; + + if (!fu_ipmi_device_send(self, + helper->netfn, + helper->cmd, + helper->req_buf, + helper->req_bufsz, + error)) + return FALSE; + + pollfds[0].fd = fu_udev_device_get_fd(FU_UDEV_DEVICE(self)); + pollfds[0].events = POLLIN; + + for (;;) { + guint8 resp_netfn = 0; + guint8 resp_cmd = 0; + glong seq = 0; + gint rc; + + rc = g_poll(pollfds, + 1, + helper->timeout_ms - (g_timer_elapsed(timer, NULL) * 1000.f)); + if (rc < 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "poll() error %m"); + return FALSE; + } + if (rc == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "timeout waiting for response " + "(netfn %d, cmd %d)", + helper->netfn, + helper->cmd); + return FALSE; + } + + if (!(pollfds[0].revents & POLLIN)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unexpected status"); + return FALSE; + } + + if (!fu_ipmi_device_recv(self, + &resp_netfn, + &resp_cmd, + &seq, + resp_buf2, + resp_buf2sz, + &resp_len2, + error)) + return FALSE; + + if (seq != self->seq - 1) { + g_debug("out-of-sequence reply: " + "expected %ld, got %ld", + self->seq, + seq); + if (g_timer_elapsed(timer, NULL) * 1000.f >= helper->timeout_ms) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "timed out"); + return FALSE; + } + } else { + if (!fu_ipmi_device_errcode_to_error(resp_buf2[0], error)) + return FALSE; + if (helper->resp_buf != NULL) { + if (!fu_memcpy_safe(helper->resp_buf, + helper->resp_bufsz, + 0x0, /* dst */ + resp_buf2, + resp_buf2sz, + 0x01, /* src */ + helper->resp_bufsz, + error)) + return FALSE; + } + if (helper->resp_len != NULL) + *helper->resp_len = resp_len2 - 1; + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) { + g_debug("IPMI netfn: %02x->%02x, cmd: %02x->%02x", + helper->netfn, + resp_netfn, + helper->cmd, + resp_cmd); + } + break; + } + } + return TRUE; +} + +static gboolean +fu_ipmi_device_transaction(FuIpmiDevice *self, + guint8 netfn, + guint8 cmd, + const guint8 *req_buf, + gsize req_bufsz, + guint8 *resp_buf, /* optional */ + gsize resp_bufsz, + gsize *resp_len, /* optional, out */ + gint timeout_ms, + GError **error) +{ + FuIpmiDeviceTransactionHelper helper = { + .netfn = netfn, + .cmd = cmd, + .req_buf = req_buf, + .req_bufsz = req_bufsz, + .resp_buf = resp_buf, + .resp_bufsz = resp_bufsz, + .resp_len = resp_len, + .timeout_ms = timeout_ms, + }; + fu_device_retry_add_recovery(FU_DEVICE(self), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, NULL); + return fu_device_retry_full(FU_DEVICE(self), + fu_ipmi_device_transaction_cb, + FU_IPMI_TRANSACTION_RETRY_COUNT, + FU_IPMI_TRANSACTION_RETRY_DELAY, + &helper, + error); +} + +static gboolean +fu_ipmi_device_probe(FuDevice *device, GError **error) +{ + FuIpmiDevice *self = FU_IPMI_DEVICE(device); + const gchar *physical_ids[] = {"/dev/ipmi0", "/dev/ipmi/0", "/dev/ipmidev/0", NULL}; + + /* look for the IPMI device */ + for (guint i = 0; physical_ids[i] != NULL; i++) { + if (g_file_test(physical_ids[i], G_FILE_TEST_EXISTS)) { + fu_device_set_physical_id(FU_DEVICE(self), physical_ids[i]); + return TRUE; + } + } + + /* cannot continue */ + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no BMC device found"); + return FALSE; +} + +static gboolean +fu_ipmi_device_setup(FuDevice *device, GError **error) +{ + FuIpmiDevice *self = FU_IPMI_DEVICE(device); + gsize resp_len = 0; + guint8 resp[16] = {0}; + + /* get IPMI versions */ + if (!fu_ipmi_device_transaction(self, + IPMI_NETFN_APP_REQUEST, + IPMI_GET_DEVICE_ID_CMD, + NULL, + 0, + resp, + sizeof(resp), + &resp_len, + FU_IPMI_DEVICE_TIMEOUT, + error)) + return FALSE; + if (resp_len == 11 || resp_len == 15) { + guint8 bcd; + g_autoptr(GString) str = g_string_new(NULL); + + self->device_id = resp[0]; + self->device_rev = resp[1]; + bcd = resp[3] & 0x0f; + bcd += 10 * (resp[4] >> 3); + /* rev1.rev2.aux_revision */ + g_string_append_printf(str, "%u.%02u", resp[2], bcd); + if (resp_len == 15) { + g_string_append_printf(str, + ".%02x%02x%02x%02x", + resp[11], + resp[12], + resp[13], + resp[14]); + } + fu_device_set_version(device, str->str); + bcd = resp[4] & 0x0f; + bcd += 10 * (resp[4] >> 4); + self->version_ipmi = bcd; + } else { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to parse DEVICE_ID_CMD response (sz: %" G_GSIZE_FORMAT ")", + resp_len); + return FALSE; + } + + /* success */ + return TRUE; +} + +gchar * +fu_ipmi_device_get_user_password(FuIpmiDevice *self, guint8 user_id, GError **error) +{ + const guint8 req[1] = {user_id}; + guint8 resp[0x10] = {0}; + gsize resp_len = 0; + + g_return_val_if_fail(FU_IS_IPMI_DEVICE(self), NULL); + g_return_val_if_fail(user_id != 0x0, NULL); + + /* run transaction */ + if (!fu_ipmi_device_transaction(self, + IPMI_NETFN_APP_REQUEST, + IPMI_GET_USER_NAME, + req, + sizeof(req), + resp, + sizeof(resp), + &resp_len, + FU_IPMI_DEVICE_TIMEOUT, + error)) { + g_prefix_error(error, "failed to get username: "); + return NULL; + } + if (resp_len != sizeof(resp)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to retrieve username from IPMI, got 0x%x bytes", + (guint)resp_len); + return NULL; + } + + /* success */ + return fu_strsafe((const gchar *)resp, resp_len); +} + +gboolean +fu_ipmi_device_set_user_name(FuIpmiDevice *self, + guint8 user_id, + const gchar *username, + GError **error) +{ + guint8 req[0x11] = {user_id}; + gsize username_sz; + + g_return_val_if_fail(FU_IS_IPMI_DEVICE(self), FALSE); + g_return_val_if_fail(user_id != 0x0, FALSE); + g_return_val_if_fail(username != NULL, FALSE); + + /* copy into buffer */ + username_sz = strlen(username); + if (!fu_memcpy_safe(req, + sizeof(req), + 0x1, /* dst */ + (guint8 *)username, + username_sz, + 0x0, /* src */ + username_sz, + error)) { + g_prefix_error(error, "username invalid: "); + return FALSE; + } + + /* run transaction */ + if (!fu_ipmi_device_transaction(self, + IPMI_NETFN_APP_REQUEST, + IPMI_SET_USER_NAME, + req, + sizeof(req), + NULL, /* resp */ + 0, + NULL, + FU_IPMI_DEVICE_TIMEOUT, + error)) { + g_prefix_error(error, "failed to set user %02x name: ", user_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_ipmi_device_set_user_enable(FuIpmiDevice *self, guint8 user_id, gboolean value, GError **error) +{ + guint8 op = value ? IPMI_PASSWORD_ENABLE_USER : IPMI_PASSWORD_DISABLE_USER; + const guint8 req[] = {user_id, op}; + + g_return_val_if_fail(FU_IS_IPMI_DEVICE(self), FALSE); + g_return_val_if_fail(user_id != 0x0, FALSE); + + /* run transaction */ + if (!fu_ipmi_device_transaction(self, + IPMI_NETFN_APP_REQUEST, + IPMI_SET_USER_PASSWORD, + req, + sizeof(req), + NULL, /* resp */ + 0, + NULL, + FU_IPMI_DEVICE_TIMEOUT, + error)) { + g_prefix_error(error, "failed to set user %02x enable: ", user_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_ipmi_device_set_user_password(FuIpmiDevice *self, + guint8 user_id, + const gchar *password, + GError **error) +{ + guint8 req[0x12] = {user_id, IPMI_PASSWORD_SET_PASSWORD}; + gsize password_sz; + + g_return_val_if_fail(FU_IS_IPMI_DEVICE(self), FALSE); + g_return_val_if_fail(user_id != 0x0, FALSE); + g_return_val_if_fail(password != NULL, FALSE); + + /* copy into buffer */ + password_sz = strlen(password); + if (!fu_memcpy_safe(req, + sizeof(req), + 0x2, /* dst */ + (guint8 *)password, + password_sz, + 0x0, /* src */ + password_sz, + error)) { + g_prefix_error(error, "password invalid: "); + return FALSE; + } + + /* run transaction */ + if (!fu_ipmi_device_transaction(self, + IPMI_NETFN_APP_REQUEST, + IPMI_SET_USER_PASSWORD, + req, + sizeof(req), + NULL, /* resp */ + 0, + NULL, + FU_IPMI_DEVICE_TIMEOUT, + error)) { + g_prefix_error(error, "failed to set user %02x password: ", user_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_ipmi_device_set_user_priv(FuIpmiDevice *self, + guint8 user_id, + guint8 priv_limit, + guint8 channel, + GError **error) +{ + const guint8 req[] = {channel, user_id, priv_limit, 0x0}; + + g_return_val_if_fail(FU_IS_IPMI_DEVICE(self), FALSE); + g_return_val_if_fail(user_id != 0x0, FALSE); + g_return_val_if_fail(channel <= 0x0F, FALSE); + g_return_val_if_fail(priv_limit <= 0x0F, FALSE); + + /* run transaction */ + if (!fu_ipmi_device_transaction(self, + IPMI_NETFN_APP_REQUEST, + IPMI_SET_USER_ACCESS, + req, + sizeof(req), + NULL, /* resp */ + 0, + NULL, + FU_IPMI_DEVICE_TIMEOUT, + error)) { + g_prefix_error(error, + "failed to set user %02x privs of 0x%02x, 0x%02x: ", + user_id, + priv_limit, + channel); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_ipmi_device_init(FuIpmiDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "IPMI"); + fu_device_set_summary(FU_DEVICE(self), "Intelligent Platform Management Interface"); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); +} + +static void +fu_ipmi_device_class_init(FuIpmiDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_ipmi_device_probe; + klass_device->setup = fu_ipmi_device_setup; + klass_device->to_string = fu_ipmi_device_to_string; +} + +FuIpmiDevice * +fu_ipmi_device_new(FuContext *ctx) +{ + FuIpmiDevice *self; + self = g_object_new(FU_TYPE_IPMI_DEVICE, "context", ctx, "device-file", "/dev/ipmi0", NULL); + return self; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-ipmi-device.h b/fwupd-1.8.6/plugins/redfish/fu-ipmi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..b1f45ee0c245d7cdc3642d5e5be54e7fa9ff71ea --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-ipmi-device.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_IPMI_DEVICE (fu_ipmi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIpmiDevice, fu_ipmi_device, FU, IPMI_DEVICE, FuUdevDevice) + +FuIpmiDevice * +fu_ipmi_device_new(FuContext *ctx); +gchar * +fu_ipmi_device_get_user_password(FuIpmiDevice *self, guint8 user_id, GError **error); +gboolean +fu_ipmi_device_set_user_name(FuIpmiDevice *self, + guint8 user_id, + const gchar *username, + GError **error); +gboolean +fu_ipmi_device_set_user_password(FuIpmiDevice *self, + guint8 user_id, + const gchar *password, + GError **error); +gboolean +fu_ipmi_device_set_user_enable(FuIpmiDevice *self, guint8 user_id, gboolean enable, GError **error); +gboolean +fu_ipmi_device_set_user_priv(FuIpmiDevice *self, + guint8 user_id, + guint8 priv_limit, + guint8 channel, + GError **error); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-backend.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-backend.c new file mode 100644 index 0000000000000000000000000000000000000000..239efe024394a8afc4134e7c018e0c367ee41a7f --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-backend.c @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-redfish-backend.h" +#include "fu-redfish-common.h" +#include "fu-redfish-legacy-device.h" +#include "fu-redfish-multipart-device.h" +#include "fu-redfish-request.h" +#include "fu-redfish-smbios.h" +#include "fu-redfish-smc-device.h" + +struct _FuRedfishBackend { + FuBackend parent_instance; + gchar *hostname; + gchar *username; + gchar *password; + guint port; + gchar *update_uri_path; + gchar *push_uri_path; + gboolean use_https; + gboolean cacheck; + gboolean wildcard_targets; + gint64 max_image_size; /* bytes */ + GType device_gtype; + GHashTable *request_cache; /* str:GByteArray */ + CURLSH *curlsh; +}; + +G_DEFINE_TYPE(FuRedfishBackend, fu_redfish_backend, FU_TYPE_BACKEND) + +FuRedfishRequest * +fu_redfish_backend_request_new(FuRedfishBackend *self) +{ + FuRedfishRequest *request = g_object_new(FU_TYPE_REDFISH_REQUEST, NULL); + CURL *curl; +#ifdef HAVE_LIBCURL_7_62_0 + CURLU *uri; +#else + g_autofree gchar *uri_base = NULL; +#endif + g_autofree gchar *user_agent = NULL; + g_autofree gchar *port = g_strdup_printf("%u", self->port); + + /* set the cache location */ + fu_redfish_request_set_cache(request, self->request_cache); + fu_redfish_request_set_curlsh(request, self->curlsh); + + /* set up defaults */ + curl = fu_redfish_request_get_curl(request); +#ifdef HAVE_LIBCURL_7_62_0 + uri = fu_redfish_request_get_uri(request); + (void)curl_url_set(uri, CURLUPART_SCHEME, self->use_https ? "https" : "http", 0); + (void)curl_url_set(uri, CURLUPART_HOST, self->hostname, 0); + (void)curl_url_set(uri, CURLUPART_PORT, port, 0); + (void)curl_easy_setopt(curl, CURLOPT_CURLU, uri); +#else + uri_base = + g_strdup_printf("%s://%s:%s", self->use_https ? "https" : "http", self->hostname, port); + fu_redfish_request_set_uri_base(request, uri_base); +#endif + + /* since DSP0266 makes Basic Authorization a requirement, + * it is safe to use Basic Auth for all implementations */ + (void)curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (glong)CURLAUTH_BASIC); + (void)curl_easy_setopt(curl, CURLOPT_TIMEOUT, (glong)180); + (void)curl_easy_setopt(curl, CURLOPT_USERNAME, self->username); + (void)curl_easy_setopt(curl, CURLOPT_PASSWORD, self->password); + + /* setup networking */ + user_agent = g_strdup_printf("%s/%s", PACKAGE_NAME, PACKAGE_VERSION); + (void)curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent); + (void)curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60L); + if (!self->cacheck) { + (void)curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + (void)curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + } + + /* success */ + return request; +} + +static gboolean +fu_redfish_backend_coldplug_member(FuRedfishBackend *self, JsonObject *member, GError **error) +{ + g_autoptr(FuDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; + + /* create of the correct type */ + dev = g_object_new(self->device_gtype, + "context", + fu_backend_get_context(FU_BACKEND(self)), + "backend", + self, + "member", + member, + NULL); + + /* some vendors do not specify the Targets array when updating */ + if (self->wildcard_targets) + fu_device_add_private_flag(dev, FU_REDFISH_DEVICE_FLAG_WILDCARD_TARGETS); + + /* probe + setup */ + locker = fu_device_locker_new(dev, &error_local); + if (locker == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("failed to setup: %s", error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + if (self->max_image_size != 0) + fu_device_set_firmware_size_max(dev, (guint64)self->max_image_size); + fu_backend_device_added(FU_BACKEND(self), dev); + return TRUE; +} + +static gboolean +fu_redfish_backend_coldplug_collection(FuRedfishBackend *self, + JsonObject *collection, + GError **error) +{ + JsonArray *members = json_object_get_array_member(collection, "Members"); + for (guint i = 0; i < json_array_get_length(members); i++) { + JsonObject *json_obj; + JsonObject *member_id; + const gchar *member_uri; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(self); + + 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; + } + + /* create the device for the member */ + if (!fu_redfish_request_perform(request, + member_uri, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + if (!fu_redfish_backend_coldplug_member(self, json_obj, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_redfish_backend_coldplug_inventory(FuRedfishBackend *self, JsonObject *inventory, GError **error) +{ + JsonObject *json_obj; + const gchar *collection_uri; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(self); + + 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; + } + + if (!fu_redfish_request_perform(request, + collection_uri, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + return fu_redfish_backend_coldplug_collection(self, json_obj, error); +} + +static void +fu_redfish_backend_check_wildcard_targets(FuRedfishBackend *self) +{ + g_autoptr(GPtrArray) devices = fu_backend_get_devices(FU_BACKEND(self)); + g_autoptr(GHashTable) device_by_id0 = g_hash_table_new(g_str_hash, g_str_equal); + + /* does the SoftwareId exist from a different device */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_old; + FuDevice *device_tmp = g_ptr_array_index(devices, i); + GPtrArray *ids = fu_device_get_instance_ids(device_tmp); + const gchar *id0 = g_ptr_array_index(ids, 0); + device_old = g_hash_table_lookup(device_by_id0, id0); + if (device_old == NULL) { + g_hash_table_insert(device_by_id0, (gpointer)device_tmp, (gpointer)id0); + continue; + } + fu_device_add_flag(device_tmp, FWUPD_DEVICE_FLAG_WILDCARD_INSTALL); + fu_device_add_flag(device_old, FWUPD_DEVICE_FLAG_WILDCARD_INSTALL); + } +} + +static void +fu_redfish_backend_set_push_uri_path(FuRedfishBackend *self, const gchar *push_uri_path) +{ + g_free(self->push_uri_path); + self->push_uri_path = g_strdup(push_uri_path); +} + +static gboolean +fu_redfish_backend_has_smc_update_path(JsonObject *update_svc) +{ + JsonObject *tmp_obj; + const gchar *tmp_str; + + if (!json_object_has_member(update_svc, "Actions")) + return FALSE; + tmp_obj = json_object_get_object_member(update_svc, "Actions"); + if (tmp_obj == NULL || !json_object_has_member(tmp_obj, "#UpdateService.StartUpdate")) + return FALSE; + tmp_obj = json_object_get_object_member(tmp_obj, "#UpdateService.StartUpdate"); + if (tmp_obj == NULL || !json_object_has_member(tmp_obj, "target")) + return FALSE; + tmp_str = json_object_get_string_member(tmp_obj, "target"); + return g_str_equal(tmp_str, "/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate"); +} + +static gboolean +fu_redfish_backend_coldplug(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuRedfishBackend *self = FU_REDFISH_BACKEND(backend); + JsonObject *json_obj; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(self); + + /* nothing set */ + if (self->update_uri_path == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no update_uri_path"); + return FALSE; + } + + /* get the update service */ + if (!fu_redfish_request_perform(request, + self->update_uri_path, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + if (!json_object_has_member(json_obj, "ServiceEnabled")) { + if (!json_object_get_boolean_member(json_obj, "ServiceEnabled")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "service is not enabled"); + return FALSE; + } + } + if (self->push_uri_path == NULL && + json_object_has_member(json_obj, "MultipartHttpPushUri")) { + const gchar *tmp = json_object_get_string_member(json_obj, "MultipartHttpPushUri"); + if (tmp != NULL) { + if (fu_redfish_backend_has_smc_update_path(json_obj)) { + self->device_gtype = FU_TYPE_REDFISH_SMC_DEVICE; + } else { + self->device_gtype = FU_TYPE_REDFISH_MULTIPART_DEVICE; + } + fu_redfish_backend_set_push_uri_path(self, tmp); + } + } + if (self->push_uri_path == NULL && json_object_has_member(json_obj, "HttpPushUri")) { + const gchar *tmp = json_object_get_string_member(json_obj, "HttpPushUri"); + if (tmp != NULL) { + self->device_gtype = FU_TYPE_REDFISH_LEGACY_DEVICE; + fu_redfish_backend_set_push_uri_path(self, tmp); + } + } + if (self->push_uri_path == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HttpPushUri and MultipartHttpPushUri are invalid"); + return FALSE; + } + if (json_object_has_member(json_obj, "MaxImageSizeBytes")) { + self->max_image_size = json_object_get_int_member(json_obj, "MaxImageSizeBytes"); + } + if (json_object_has_member(json_obj, "FirmwareInventory")) { + JsonObject *tmp = json_object_get_object_member(json_obj, "FirmwareInventory"); + return fu_redfish_backend_coldplug_inventory(self, tmp, error); + } + if (json_object_has_member(json_obj, "SoftwareInventory")) { + JsonObject *tmp = json_object_get_object_member(json_obj, "SoftwareInventory"); + return fu_redfish_backend_coldplug_inventory(self, tmp, error); + } + + /* work out if we have multiple devices with the same SoftwareId */ + if (self->wildcard_targets) + fu_redfish_backend_check_wildcard_targets(self); + + /* success */ + return TRUE; +} + +static void +fu_redfish_backend_set_update_uri_path(FuRedfishBackend *self, const gchar *update_uri_path) +{ + /* not changed */ + if (g_strcmp0(self->update_uri_path, update_uri_path) == 0) + return; + + g_free(self->update_uri_path); + self->update_uri_path = g_strdup(update_uri_path); +} + +static gboolean +fu_redfish_backend_setup(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuRedfishBackend *self = FU_REDFISH_BACKEND(backend); + JsonObject *json_obj; + JsonObject *json_update_service = NULL; + const gchar *data_id; + const gchar *version = NULL; + const gchar *uuid = NULL; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(self); + + /* sanity check */ + if (self->port == 0 || self->port > G_MAXUINT16) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid port specified: 0x%x", + self->port); + return FALSE; + } + + /* try to connect */ + if (!fu_redfish_request_perform(request, + "/redfish/v1/", + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + if (json_object_has_member(json_obj, "ServiceVersion")) { + version = json_object_get_string_member(json_obj, "ServiceVersion"); + } else if (json_object_has_member(json_obj, "RedfishVersion")) { + version = json_object_get_string_member(json_obj, "RedfishVersion"); + } + if (json_object_has_member(json_obj, "UUID")) + uuid = json_object_get_string_member(json_obj, "UUID"); + g_debug("Version: %s", version); + g_debug("UUID: %s", uuid); + + if (json_object_has_member(json_obj, "UpdateService")) + json_update_service = json_object_get_object_member(json_obj, "UpdateService"); + if (json_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(json_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; + } + fu_redfish_backend_set_update_uri_path(self, data_id); + return TRUE; +} + +static void +fu_redfish_backend_invalidate(FuBackend *backend) +{ + FuRedfishBackend *self = FU_REDFISH_BACKEND(backend); + g_hash_table_remove_all(self->request_cache); +} + +void +fu_redfish_backend_set_hostname(FuRedfishBackend *self, const gchar *hostname) +{ + g_free(self->hostname); + self->hostname = g_strdup(hostname); +} + +void +fu_redfish_backend_set_port(FuRedfishBackend *self, guint port) +{ + self->port = port; +} + +void +fu_redfish_backend_set_https(FuRedfishBackend *self, gboolean use_https) +{ + self->use_https = use_https; +} + +void +fu_redfish_backend_set_cacheck(FuRedfishBackend *self, gboolean cacheck) +{ + self->cacheck = cacheck; +} + +void +fu_redfish_backend_set_wildcard_targets(FuRedfishBackend *self, gboolean wildcard_targets) +{ + self->wildcard_targets = wildcard_targets; +} + +void +fu_redfish_backend_set_username(FuRedfishBackend *self, const gchar *username) +{ + g_free(self->username); + self->username = g_strdup(username); +} + +const gchar * +fu_redfish_backend_get_username(FuRedfishBackend *self) +{ + return self->username; +} + +void +fu_redfish_backend_set_password(FuRedfishBackend *self, const gchar *password) +{ + g_free(self->password); + self->password = g_strdup(password); +} + +const gchar * +fu_redfish_backend_get_push_uri_path(FuRedfishBackend *self) +{ + return self->push_uri_path; +} + +static void +fu_redfish_backend_to_string(FuBackend *backend, guint idt, GString *str) +{ + FuRedfishBackend *self = FU_REDFISH_BACKEND(backend); + fu_string_append(str, idt, "Hostname", self->hostname); + fu_string_append(str, idt, "Username", self->username); + fu_string_append_kb(str, idt, "Password", self->password != NULL); + fu_string_append_ku(str, idt, "Port", self->port); + fu_string_append(str, idt, "UpdateUriPath", self->update_uri_path); + fu_string_append(str, idt, "PushUriPath", self->push_uri_path); + fu_string_append_kb(str, idt, "UseHttps", self->use_https); + fu_string_append_kb(str, idt, "Cacheck", self->cacheck); + fu_string_append_kb(str, idt, "WildcardTargets", self->wildcard_targets); + fu_string_append_kx(str, idt, "MaxImageSize", self->max_image_size); + fu_string_append(str, idt, "DeviceGType", g_type_name(self->device_gtype)); +} + +static void +fu_redfish_backend_finalize(GObject *object) +{ + FuRedfishBackend *self = FU_REDFISH_BACKEND(object); + g_hash_table_unref(self->request_cache); + curl_share_cleanup(self->curlsh); + 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_OBJECT_CLASS(fu_redfish_backend_parent_class)->finalize(object); +} + +static void +fu_redfish_backend_class_init(FuRedfishBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + klass_backend->coldplug = fu_redfish_backend_coldplug; + klass_backend->setup = fu_redfish_backend_setup; + klass_backend->invalidate = fu_redfish_backend_invalidate; + klass_backend->to_string = fu_redfish_backend_to_string; + object_class->finalize = fu_redfish_backend_finalize; +} + +static void +fu_redfish_backend_init(FuRedfishBackend *self) +{ + self->use_https = TRUE; + self->device_gtype = FU_TYPE_REDFISH_DEVICE; + self->request_cache = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)g_byte_array_unref); + self->curlsh = curl_share_init(); + curl_share_setopt(self->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + curl_share_setopt(self->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + curl_share_setopt(self->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); + curl_share_setopt(self->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); +} + +FuRedfishBackend * +fu_redfish_backend_new(FuContext *ctx) +{ + return FU_REDFISH_BACKEND(g_object_new(FU_REDFISH_TYPE_BACKEND, + "name", + "redfish", + "can-invalidate", + TRUE, + "context", + ctx, + NULL)); +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-backend.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-backend.h new file mode 100644 index 0000000000000000000000000000000000000000..b4168ce9888b594f26755066b76e18b2ede0351c --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-backend.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-redfish-request.h" + +#define FU_REDFISH_TYPE_BACKEND (fu_redfish_backend_get_type()) + +G_DECLARE_FINAL_TYPE(FuRedfishBackend, fu_redfish_backend, FU, REDFISH_BACKEND, FuBackend) + +FuRedfishBackend * +fu_redfish_backend_new(FuContext *ctx); +void +fu_redfish_backend_set_hostname(FuRedfishBackend *self, const gchar *hostname); +void +fu_redfish_backend_set_username(FuRedfishBackend *self, const gchar *username); +const gchar * +fu_redfish_backend_get_username(FuRedfishBackend *self); +void +fu_redfish_backend_set_password(FuRedfishBackend *self, const gchar *password); +void +fu_redfish_backend_set_port(FuRedfishBackend *self, guint port); +void +fu_redfish_backend_set_https(FuRedfishBackend *self, gboolean use_https); +void +fu_redfish_backend_set_cacheck(FuRedfishBackend *self, gboolean cacheck); +void +fu_redfish_backend_set_wildcard_targets(FuRedfishBackend *self, gboolean wildcard_targets); +const gchar * +fu_redfish_backend_get_push_uri_path(FuRedfishBackend *self); +FuRedfishRequest * +fu_redfish_backend_request_new(FuRedfishBackend *self); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-common.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-common.c new file mode 100644 index 0000000000000000000000000000000000000000..114042c4281e5fe42291016dcf09a628dd17a307 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-common.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-redfish-common.h" + +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); +} + +gchar * +fu_redfish_common_buffer_to_mac(const guint8 *buffer) +{ + GString *str = g_string_new(NULL); + for (guint i = 0; i < 6; i++) { + g_string_append_printf(str, "%02X", buffer[i]); + if (i != 5) + g_string_append(str, ":"); + } + return g_string_free(str, FALSE); +} + +gchar * +fu_redfish_common_fix_version(const gchar *version) +{ + g_auto(GStrv) split = NULL; + + g_return_val_if_fail(version != NULL, NULL); + + /* not valid */ + if (g_strcmp0(version, "-*") == 0) + return NULL; + + /* find the section preficed with "v" */ + split = g_strsplit(version, " ", -1); + for (guint i = 0; split[i] != NULL; i++) { + if (g_str_has_prefix(split[i], "v")) { + g_debug("using %s for %s", split[i] + 1, version); + return g_strdup(split[i] + 1); + } + } + + /* find the thing with dots */ + for (guint i = 0; split[i] != NULL; i++) { + if (g_strstr_len(split[i], -1, ".")) { + if (g_strcmp0(split[i], version) != 0) + g_debug("using %s for %s", split[i], version); + return g_strdup(split[i]); + } + } + + /* we failed to do anything clever */ + return g_strdup(version); +} + +/* parses a Lenovo XCC-format version like "11A-1.02" */ +gboolean +fu_redfish_common_parse_version_lenovo(const gchar *version, + gchar **out_build, /* out */ + gchar **out_version, /* out */ + GError **error) +{ + g_auto(GStrv) versplit = g_strsplit(version, "-", -1); + + /* sanity check */ + if (g_strv_length(versplit) != 2) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not two sections"); + return FALSE; + } + if (strlen(versplit[0]) != 3) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid length first section"); + return FALSE; + } + + /* milestone */ + if (!g_ascii_isdigit(versplit[0][0]) || !g_ascii_isdigit(versplit[0][1])) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "milestone number invalid"); + return FALSE; + } + + /* build is only one letter from A -> Z */ + if (!g_ascii_isalpha(versplit[0][2])) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "build letter invalid"); + return FALSE; + } + + /* success */ + if (out_build != NULL) + *out_build = g_strdup(versplit[0]); + if (out_version != NULL) + *out_version = g_strdup(versplit[1]); + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-common.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-common.h new file mode 100644 index 0000000000000000000000000000000000000000..28d1fc107f305550f3166f21f3a9a92c6950fc59 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-common.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* SMBIOS */ +#define REDFISH_SMBIOS_TABLE_TYPE 0x2a /* 42 */ + +#define REDFISH_PROTOCOL_REDFISH_OVER_IP 0x04 + +#define REDFISH_CONTROLLER_INTERFACE_TYPE_NETWORK_HOST 0x40 + +#define REDFISH_INTERFACE_TYPE_USB_NETWORK 0x02 +#define REDFISH_INTERFACE_TYPE_PCI_NETWORK 0x03 +#define REDFISH_INTERFACE_TYPE_USB_NETWORK_V2 0x04 +#define REDFISH_INTERFACE_TYPE_PCI_NETWORK_V2 0x05 + +#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 "16faa37e-4b6a-4891-9028-242de65a3b70" + +#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 */ +gchar * +fu_redfish_common_buffer_to_ipv4(const guint8 *buffer); +gchar * +fu_redfish_common_buffer_to_ipv6(const guint8 *buffer); +gchar * +fu_redfish_common_buffer_to_mac(const guint8 *buffer); + +gchar * +fu_redfish_common_fix_version(const gchar *version); +gboolean +fu_redfish_common_parse_version_lenovo(const gchar *version, + gchar **out_build, + gchar **out_version, + GError **error); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-device.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-device.c new file mode 100644 index 0000000000000000000000000000000000000000..4bc41ec36d332aa007b7ea6637259e2fc3e5d3b7 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-device.c @@ -0,0 +1,930 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-redfish-backend.h" +#include "fu-redfish-common.h" +#include "fu-redfish-device.h" + +typedef struct { + FuRedfishBackend *backend; + JsonObject *member; + guint64 milestone; + gchar *build; + guint reset_pre_delay; /* default of 0ms */ + guint reset_post_delay; /* default of 0ms */ +} FuRedfishDevicePrivate; + +enum { PROP_0, PROP_BACKEND, PROP_MEMBER, PROP_LAST }; + +G_DEFINE_TYPE_WITH_PRIVATE(FuRedfishDevice, fu_redfish_device, FU_TYPE_DEVICE) + +#define GET_PRIVATE(o) (fu_redfish_device_get_instance_private(o)) + +static void +fu_redfish_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(device); + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + if (priv->milestone > 0x0) + fu_string_append_kx(str, idt, "Milestone", priv->milestone); + if (priv->build != NULL) + fu_string_append(str, idt, "Build", priv->build); + fu_string_append_ku(str, idt, "ResetPretDelay", priv->reset_pre_delay); + fu_string_append_ku(str, idt, "ResetPostDelay", priv->reset_post_delay); +} + +static void +fu_redfish_device_set_device_class(FuRedfishDevice *self, const gchar *tmp) +{ + if (g_strcmp0(tmp, "NetworkController") == 0) { + fu_device_add_icon(FU_DEVICE(self), "network-wired"); + return; + } + if (g_strcmp0(tmp, "MassStorageController") == 0) { + fu_device_add_icon(FU_DEVICE(self), "drive-multidisk"); + return; + } + if (g_strcmp0(tmp, "DisplayController") == 0) { + fu_device_add_icon(FU_DEVICE(self), "video-display"); + return; + } + if (g_strcmp0(tmp, "DockingStation") == 0) { + fu_device_add_icon(FU_DEVICE(self), "dock"); + return; + } + if (g_strcmp0(tmp, "WirelessController") == 0) { + fu_device_add_icon(FU_DEVICE(self), "network-wireless"); + return; + } + g_debug("no icon mapping for %s", tmp); +} + +static gboolean +fu_redfish_device_probe_related_pcie_item(FuRedfishDevice *self, const gchar *uri, GError **error) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + JsonObject *json_obj; + guint64 vendor_id = 0x0; + guint64 model_id = 0x0; + guint64 subsystem_vendor_id = 0x0; + guint64 subsystem_model_id = 0x0; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(priv->backend); + + /* get URI */ + if (!fu_redfish_request_perform(request, + uri, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON | + FU_REDFISH_REQUEST_PERFORM_FLAG_USE_CACHE, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + + /* optional properties */ + if (json_object_has_member(json_obj, "DeviceClass")) { + const gchar *tmp = json_object_get_string_member(json_obj, "DeviceClass"); + if (tmp != NULL && tmp[0] != '\0') + fu_redfish_device_set_device_class(self, tmp); + } + if (json_object_has_member(json_obj, "VendorId")) { + const gchar *tmp = json_object_get_string_member(json_obj, "VendorId"); + if (tmp != NULL && tmp[0] != '\0') { + if (!fu_strtoull(tmp, &vendor_id, 0, G_MAXUINT16, error)) + return FALSE; + } + } + if (json_object_has_member(json_obj, "DeviceId")) { + const gchar *tmp = json_object_get_string_member(json_obj, "DeviceId"); + if (tmp != NULL && tmp[0] != '\0') { + if (!fu_strtoull(tmp, &model_id, 0, G_MAXUINT16, error)) + return FALSE; + } + } + if (json_object_has_member(json_obj, "SubsystemVendorId")) { + const gchar *tmp = json_object_get_string_member(json_obj, "SubsystemVendorId"); + if (tmp != NULL && tmp[0] != '\0') { + if (!fu_strtoull(tmp, &subsystem_vendor_id, 0, G_MAXUINT16, error)) + return FALSE; + } + } + if (json_object_has_member(json_obj, "SubsystemId")) { + const gchar *tmp = json_object_get_string_member(json_obj, "SubsystemId"); + if (tmp != NULL && tmp[0] != '\0') { + if (!fu_strtoull(tmp, &subsystem_model_id, 0, G_MAXUINT16, error)) + return FALSE; + } + } + + /* add vendor ID */ + if (vendor_id != 0x0) { + g_autofree gchar *vendor_id_str = NULL; + vendor_id_str = g_strdup_printf("PCI:0x%04X", (guint)vendor_id); + fu_device_add_vendor_id(FU_DEVICE(self), vendor_id_str); + } + + /* add more instance IDs if possible */ + if (vendor_id != 0x0) + fu_device_add_instance_u16(FU_DEVICE(self), "VEN", vendor_id); + if (model_id != 0x0) + fu_device_add_instance_u16(FU_DEVICE(self), "DEV", model_id); + if (subsystem_vendor_id != 0x0 && subsystem_model_id != 0x0) { + g_autofree gchar *subsys = NULL; + subsys = g_strdup_printf("%04X%04X", + (guint)subsystem_vendor_id, + (guint)subsystem_model_id); + fu_device_add_instance_str(FU_DEVICE(self), "SUBSYS", subsys); + } + fu_device_build_instance_id(FU_DEVICE(self), NULL, "PCI", "VEN", "DEV", NULL); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "PCI", "VEN", "DEV", "SUBSYS", NULL); + + /* success */ + return TRUE; +} + +static gboolean +fu_redfish_device_probe_related_pcie_functions(FuRedfishDevice *self, + const gchar *uri, + GError **error) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + JsonObject *json_obj; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(priv->backend); + + /* get URI */ + if (!fu_redfish_request_perform(request, + uri, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON | + FU_REDFISH_REQUEST_PERFORM_FLAG_USE_CACHE, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + + if (json_object_has_member(json_obj, "Members")) { + JsonArray *members_array = json_object_get_array_member(json_obj, "Members"); + for (guint i = 0; i < json_array_get_length(members_array); i++) { + JsonObject *related_item; + related_item = json_array_get_object_element(members_array, i); + if (json_object_has_member(related_item, "@odata.id")) { + const gchar *id = + json_object_get_string_member(related_item, "@odata.id"); + if (!fu_redfish_device_probe_related_pcie_item(self, id, error)) + return FALSE; + } + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_redfish_device_probe_related_item(FuRedfishDevice *self, const gchar *uri, GError **error) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + JsonObject *json_obj; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(priv->backend); + + /* get URI */ + if (!fu_redfish_request_perform(request, + uri, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON | + FU_REDFISH_REQUEST_PERFORM_FLAG_USE_CACHE, + error)) + return FALSE; + json_obj = fu_redfish_request_get_json_object(request); + + /* optional properties */ + if (json_object_has_member(json_obj, "SerialNumber")) { + const gchar *tmp = json_object_get_string_member(json_obj, "SerialNumber"); + if (tmp != NULL && tmp[0] != '\0' && g_strcmp0(tmp, "N/A") != 0) + fu_device_set_serial(FU_DEVICE(self), tmp); + } + if (json_object_has_member(json_obj, "HotPluggable")) { + /* this is better than the heuristic we get from the device name */ + if (json_object_get_boolean_member(json_obj, "HotPluggable")) + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + else + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + } + + /* sometimes an array, sometimes an object! */ + if (json_object_has_member(json_obj, "PCIeFunctions")) { + JsonNode *pcie_functions = json_object_get_member(json_obj, "PCIeFunctions"); + if (JSON_NODE_HOLDS_OBJECT(pcie_functions)) { + JsonObject *obj = json_node_get_object(pcie_functions); + if (json_object_has_member(obj, "@odata.id")) { + const gchar *id = json_object_get_string_member(obj, "@odata.id"); + if (!fu_redfish_device_probe_related_pcie_functions(self, + id, + error)) + return FALSE; + } + } + } + return TRUE; +} + +/* parses a Lenovo XCC-format version like "11A-1.02" */ +static gboolean +fu_redfish_device_set_version_lenovo(FuRedfishDevice *self, const gchar *version, GError **error) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *out_build = NULL; + g_autofree gchar *out_version = NULL; + + /* split up Lenovo format */ + if (!fu_redfish_common_parse_version_lenovo(version, &out_build, &out_version, error)) + return FALSE; + + /* split out milestone */ + priv->milestone = g_ascii_strtoull(out_build, NULL, 10); + if (priv->milestone == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "version milestone invalid"); + return FALSE; + } + + /* odd numbered builds are unsigned */ + if (priv->milestone % 2 != 0) { + fu_device_add_private_flag(FU_DEVICE(self), FU_REDFISH_DEVICE_FLAG_UNSIGNED_BUILD); + } + + /* build is only one letter from A -> Z */ + if (!g_ascii_isalpha(out_build[2])) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "build letter invalid"); + return FALSE; + } + priv->build = g_strndup(out_build + 2, 1); + fu_device_set_version(FU_DEVICE(self), out_version); + fu_device_set_version_format(FU_DEVICE(self), fu_version_guess_format(out_version)); + return TRUE; +} + +static void +fu_redfish_device_set_version(FuRedfishDevice *self, const gchar *tmp) +{ + /* OEM specific */ + if (g_strcmp0(fu_device_get_vendor(FU_DEVICE(self)), "Lenovo") == 0) { + g_autoptr(GError) error_local = NULL; + if (!fu_redfish_device_set_version_lenovo(self, tmp, &error_local)) { + g_debug("failed to parse Lenovo version %s: %s", tmp, error_local->message); + } + } + + /* fallback */ + if (fu_device_get_version(FU_DEVICE(self)) == NULL) { + g_autofree gchar *ver = fu_redfish_common_fix_version(tmp); + if (ver != NULL) { + fu_device_set_version(FU_DEVICE(self), ver); + fu_device_set_version_format(FU_DEVICE(self), fu_version_guess_format(ver)); + } + } +} + +static void +fu_redfish_device_set_version_lowest(FuRedfishDevice *self, const gchar *tmp) +{ + /* OEM specific */ + if (g_strcmp0(fu_device_get_vendor(FU_DEVICE(self)), "Lenovo") == 0) { + g_autoptr(GError) error_local = NULL; + g_autofree gchar *out_version = NULL; + if (!fu_redfish_common_parse_version_lenovo(tmp, + NULL, + &out_version, + &error_local)) { + g_debug("failed to parse Lenovo version %s: %s", tmp, error_local->message); + } + fu_device_set_version_lowest(FU_DEVICE(self), out_version); + } + + /* fallback */ + if (fu_device_get_version_lowest(FU_DEVICE(self)) == NULL) { + g_autofree gchar *ver = fu_redfish_common_fix_version(tmp); + fu_device_set_version_lowest(FU_DEVICE(self), ver); + } +} + +static void +fu_redfish_device_set_name(FuRedfishDevice *self, const gchar *name) +{ + /* useless */ + if (g_str_has_prefix(name, "Firmware:")) + name += 9; + + /* device type */ + if (g_str_has_prefix(name, "DEVICE-")) { + name += 7; + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + } else if (g_str_has_prefix(name, "DISK-")) { + name += 5; + fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + } else if (g_str_has_prefix(name, "POWER-")) { + name += 6; + fu_device_add_icon(FU_DEVICE(self), "ac-adapter"); + fu_device_set_summary(FU_DEVICE(self), "Redfish power supply unit"); + } else { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + } + + /* heuristics */ + if (g_strcmp0(name, "BMC") == 0) + fu_device_set_summary(FU_DEVICE(self), "Redfish baseboard management controller"); + if (g_str_has_suffix(name, "HBA") == 0) + fu_device_set_summary(FU_DEVICE(self), "Redfish host bus adapter"); + + /* success */ + fu_device_set_name(FU_DEVICE(self), name); +} + +static void +fu_redfish_device_set_vendor(FuRedfishDevice *self, const gchar *vendor) +{ + g_autofree gchar *vendor_upper = NULL; + g_autofree gchar *vendor_id = NULL; + + /* fixup a common mistake */ + if (g_strcmp0(vendor, "LEN") == 0 || g_strcmp0(vendor, "LNVO") == 0) + vendor = "Lenovo"; + fu_device_set_vendor(FU_DEVICE(self), vendor); + + /* add vendor-id */ + vendor_upper = g_ascii_strup(vendor, -1); + g_strdelimit(vendor_upper, " ", '_'); + vendor_id = g_strdup_printf("REDFISH:%s", vendor_upper); + fu_device_add_vendor_id(FU_DEVICE(self), vendor_id); +} + +static void +fu_redfish_backend_smc_license_check(FuDevice *device) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(device); + FuRedfishBackend *backend = fu_redfish_device_get_backend(self); + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + g_autoptr(GError) error_local = NULL; + + /* see if we don't get an license error */ + if (!fu_redfish_request_perform(request, + fu_redfish_backend_get_push_uri_path(backend), + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_MISSING_LICENSE); + else + g_debug("supermicro license check returned %s\n", error_local->message); + } +} + +static gboolean +fu_redfish_device_probe(FuDevice *dev, GError **error) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(dev); + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + JsonObject *member = priv->member; + const gchar *guid = NULL; + g_autofree gchar *guid_lower = NULL; + + /* required to POST later */ + if (!json_object_has_member(member, "@odata.id")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no @odata.id string"); + return FALSE; + } + fu_device_set_physical_id(dev, "Redfish-Inventory"); + fu_device_set_logical_id(dev, json_object_get_string_member(member, "@odata.id")); + if (json_object_has_member(member, "Id")) { + const gchar *tmp = json_object_get_string_member(member, "Id"); + if (tmp != NULL) + fu_device_set_backend_id(dev, tmp); + } + + /* get SoftwareId, falling back to vendor-specific versions */ + 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"); + } + } + + /* GUID is required */ + if (guid == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no GUID for device"); + return FALSE; + } + + /* device properties */ + if (json_object_has_member(member, "Manufacturer")) { + const gchar *tmp = json_object_get_string_member(member, "Manufacturer"); + if (tmp != NULL && tmp[0] != '\0') + fu_redfish_device_set_vendor(self, tmp); + } + + /* the version can encode the instance ID suffix */ + if (json_object_has_member(member, "Version")) { + const gchar *tmp = json_object_get_string_member(member, "Version"); + if (tmp != NULL && tmp[0] != '\0') + fu_redfish_device_set_version(self, tmp); + } + + /* ReleaseDate may or may not have a timezone */ + if (json_object_has_member(member, "ReleaseDate")) { + const gchar *tmp = json_object_get_string_member(member, "ReleaseDate"); + if (tmp != NULL && tmp[0] != '\0') { + g_autoptr(GDateTime) dt = NULL; + g_autoptr(GTimeZone) tz = g_time_zone_new_utc(); + dt = g_date_time_new_from_iso8601(tmp, tz); + if (dt != NULL) { + guint64 unixtime = (guint64)g_date_time_to_unix(dt); + fu_device_set_version_build_date(dev, unixtime); + } else { + g_warning("failed to parse ISO8601 %s", tmp); + } + } + } + + /* add IDs */ + fu_device_add_instance_strsafe(dev, "VENDOR", fu_device_get_vendor(dev)); + fu_device_add_instance_str(dev, "SOFTWAREID", guid); + fu_device_add_instance_str(dev, "ID", fu_device_get_backend_id(dev)); + + /* some vendors use a GUID, others use an ID like BMC-AFBT-10 */ + guid_lower = g_ascii_strdown(guid, -1); + if (fwupd_guid_is_valid(guid_lower)) { + fu_device_add_guid(dev, guid_lower); + } else { + if (fu_device_has_private_flag(dev, FU_REDFISH_DEVICE_FLAG_UNSIGNED_BUILD)) + fu_device_add_instance_str(dev, "TYPE", "UNSIGNED"); + + fu_device_build_instance_id(dev, + NULL, + "REDFISH", + "VENDOR", + "SOFTWAREID", + "TYPE", + NULL); + fu_device_build_instance_id(dev, NULL, "REDFISH", "VENDOR", "SOFTWAREID", NULL); + } + + /* used for quirking and parenting */ + fu_device_build_instance_id(dev, NULL, "REDFISH", "VENDOR", "ID", NULL); + + if (json_object_has_member(member, "Name")) { + const gchar *tmp = json_object_get_string_member(member, "Name"); + if (tmp != NULL && tmp[0] != '\0') + fu_redfish_device_set_name(self, tmp); + } + if (json_object_has_member(member, "LowestSupportedVersion")) { + const gchar *tmp = json_object_get_string_member(member, "LowestSupportedVersion"); + if (tmp != NULL && tmp[0] != '\0') + fu_redfish_device_set_version_lowest(self, tmp); + } + if (json_object_has_member(member, "Description")) { + const gchar *tmp = json_object_get_string_member(member, "Description"); + if (tmp != NULL && tmp[0] != '\0') + fu_device_set_description(dev, tmp); + } + + /* reasons why the device might not be updatable */ + if (json_object_has_member(member, "Updateable")) { + if (json_object_get_boolean_member(member, "Updateable")) + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); + } + if (fu_device_has_private_flag(dev, FU_REDFISH_DEVICE_FLAG_IS_BACKUP)) + fu_device_inhibit(dev, "is-backup", "Is a backup partition"); + else + fu_device_uninhibit(dev, "is-backup"); + + /* use related items to set extra instance IDs */ + if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE) && + json_object_has_member(member, "RelatedItem")) { + JsonArray *related_item_array = json_object_get_array_member(member, "RelatedItem"); + for (guint i = 0; i < json_array_get_length(related_item_array); i++) { + JsonObject *related_item; + related_item = json_array_get_object_element(related_item_array, i); + if (json_object_has_member(related_item, "@odata.id")) { + const gchar *id = + json_object_get_string_member(related_item, "@odata.id"); + if (!fu_redfish_device_probe_related_item(self, id, error)) + return FALSE; + } + } + } + + /* for Supermicro check whether we have a proper Redfish license installed */ + if (g_strcmp0("SMCI", fu_device_get_vendor(dev)) == 0) + fu_redfish_backend_smc_license_check(dev); + + /* success */ + return TRUE; +} + +FuRedfishBackend * +fu_redfish_device_get_backend(FuRedfishDevice *self) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + return priv->backend; +} + +typedef struct { + FwupdError error_code; + gchar *location; + gboolean completed; + GHashTable *messages_seen; + FuProgress *progress; +} FuRedfishDevicePollCtx; + +static void +fu_redfish_device_poll_set_message_id(FuRedfishDevice *self, + FuRedfishDevicePollCtx *ctx, + const gchar *message_id) +{ + /* ignore */ + if (g_strcmp0(message_id, "TaskEvent.1.0.TaskProgressChanged") == 0 || + g_strcmp0(message_id, "TaskEvent.1.0.TaskCompletedWarning") == 0 || + g_strcmp0(message_id, "TaskEvent.1.0.TaskCompletedOK") == 0 || + g_strcmp0(message_id, "Base.1.6.Success") == 0) + return; + + /* set flags */ + if (g_strcmp0(message_id, "Base.1.10.ResetRequired") == 0) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + return; + } + + /* set error code */ + if (g_strcmp0(message_id, "Update.1.0.AwaitToActivate") == 0) { + ctx->error_code = FWUPD_ERROR_NEEDS_USER_ACTION; + return; + } + if (g_strcmp0(message_id, "Update.1.0.TransferFailed") == 0) { + ctx->error_code = FWUPD_ERROR_WRITE; + return; + } + if (g_strcmp0(message_id, "Update.1.0.ActivateFailed") == 0) { + ctx->error_code = FWUPD_ERROR_INVALID_FILE; + return; + } + if (g_strcmp0(message_id, "Update.1.0.VerificationFailed") == 0 || + g_strcmp0(message_id, "LenovoFirmwareUpdateRegistry.1.0.UpdateVerifyFailed") == 0) { + ctx->error_code = FWUPD_ERROR_INVALID_FILE; + return; + } + if (g_strcmp0(message_id, "Update.1.0.ApplyFailed") == 0) { + ctx->error_code = FWUPD_ERROR_WRITE; + return; + } + + /* set status */ + if (g_strcmp0(message_id, "Update.1.1.TargetDetermined") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_LOADING); + return; + } + if (g_strcmp0(message_id, "LenovoFirmwareUpdateRegistry.1.0.UpdateAssignment") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_LOADING); + return; + } + if (g_strcmp0(message_id, "LenovoFirmwareUpdateRegistry.1.0.PayloadApplyInProgress") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_DEVICE_WRITE); + return; + } + if (g_strcmp0(message_id, "LenovoFirmwareUpdateRegistry.1.0.PayloadApplyCompleted") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_IDLE); + return; + } + if (g_strcmp0(message_id, "LenovoFirmwareUpdateRegistry.1.0.UpdateVerifyInProgress") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_DEVICE_VERIFY); + return; + } + if (g_strcmp0(message_id, "Update.1.1.TransferringToComponent") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_LOADING); + return; + } + if (g_strcmp0(message_id, "Update.1.1.VerifyingAtComponent") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_DEVICE_VERIFY); + return; + } + if (g_strcmp0(message_id, "Update.1.1.UpdateInProgress") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_DEVICE_WRITE); + return; + } + if (g_strcmp0(message_id, "Update.1.1.UpdateSuccessful") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_IDLE); + return; + } + if (g_strcmp0(message_id, "Update.1.1.InstallingOnComponent") == 0) { + fu_progress_set_status(ctx->progress, FWUPD_STATUS_DEVICE_WRITE); + return; + } +} + +static gboolean +fu_redfish_device_poll_task_once(FuRedfishDevice *self, FuRedfishDevicePollCtx *ctx, GError **error) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + JsonObject *json_obj; + const gchar *message = "Unknown failure"; + const gchar *state_tmp; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(priv->backend); + + /* create URI and poll */ + if (!fu_redfish_request_perform(request, + ctx->location, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + + /* percentage is optional */ + json_obj = fu_redfish_request_get_json_object(request); + if (json_object_has_member(json_obj, "PercentComplete")) { + gint64 pc = json_object_get_int_member(json_obj, "PercentComplete"); + if (pc >= 0 && pc <= 100) + fu_progress_set_percentage(ctx->progress, (guint)pc); + } + + /* print all messages we've not seen yet */ + if (json_object_has_member(json_obj, "Messages")) { + JsonArray *json_msgs = json_object_get_array_member(json_obj, "Messages"); + guint json_msgs_sz = json_array_get_length(json_msgs); + + for (guint i = 0; i < json_msgs_sz; i++) { + JsonObject *json_message = json_array_get_object_element(json_msgs, i); + const gchar *message_id = NULL; + g_autofree gchar *message_key = NULL; + + /* set additional device properties */ + if (json_object_has_member(json_message, "MessageId")) + message_id = + json_object_get_string_member(json_message, "MessageId"); + if (json_object_has_member(json_message, "Message")) + message = json_object_get_string_member(json_message, "Message"); + + /* ignore messages we've seen before */ + message_key = g_strdup_printf("%s;%s", message_id, message); + if (g_hash_table_contains(ctx->messages_seen, message_key)) { + g_debug("ignoring %s", message_key); + continue; + } + g_hash_table_add(ctx->messages_seen, g_steal_pointer(&message_key)); + + /* use the message */ + g_debug("message #%u [%s]: %s", i, message_id, message); + fu_redfish_device_poll_set_message_id(self, ctx, message_id); + } + } + + /* use taskstate to set context */ + if (!json_object_has_member(json_obj, "TaskState")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no TaskState for task manager"); + return FALSE; + } + state_tmp = json_object_get_string_member(json_obj, "TaskState"); + g_debug("TaskState now %s", state_tmp); + if (g_strcmp0(state_tmp, "Completed") == 0) { + ctx->completed = TRUE; + return TRUE; + } + if (g_strcmp0(state_tmp, "Cancelled") == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Task was cancelled"); + return FALSE; + } + if (g_strcmp0(state_tmp, "Exception") == 0 || + g_strcmp0(state_tmp, "UserIntervention") == 0) { + g_set_error_literal(error, FWUPD_ERROR, ctx->error_code, message); + return FALSE; + } + + /* try again */ + return TRUE; +} + +static FuRedfishDevicePollCtx * +fu_redfish_device_poll_ctx_new(FuProgress *progress, const gchar *location) +{ + FuRedfishDevicePollCtx *ctx = g_new0(FuRedfishDevicePollCtx, 1); + ctx->messages_seen = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + ctx->location = g_strdup(location); + ctx->error_code = FWUPD_ERROR_INTERNAL; + ctx->progress = g_object_ref(progress); + return ctx; +} + +static void +fu_redfish_device_poll_ctx_free(FuRedfishDevicePollCtx *ctx) +{ + g_hash_table_unref(ctx->messages_seen); + g_object_unref(ctx->progress); + g_free(ctx->location); + g_free(ctx); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuRedfishDevicePollCtx, fu_redfish_device_poll_ctx_free) +#pragma clang diagnostic pop + +gboolean +fu_redfish_device_poll_task(FuRedfishDevice *self, + const gchar *location, + FuProgress *progress, + GError **error) +{ + const guint timeout = 2400; + g_autoptr(GTimer) timer = g_timer_new(); + g_autoptr(FuRedfishDevicePollCtx) ctx = fu_redfish_device_poll_ctx_new(progress, location); + + /* sleep and then reprobe hardware */ + do { + g_usleep(G_USEC_PER_SEC); + if (!fu_redfish_device_poll_task_once(self, ctx, error)) + return FALSE; + if (ctx->completed) + return TRUE; + } while (g_timer_elapsed(timer, NULL) < timeout); + + /* success */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to poll %s for success after %u seconds", + location, + timeout); + return FALSE; +} + +guint +fu_redfish_device_get_reset_pre_delay(FuRedfishDevice *self) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + return priv->reset_pre_delay; +} + +guint +fu_redfish_device_get_reset_post_delay(FuRedfishDevice *self) +{ + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + return priv->reset_post_delay; +} + +static gboolean +fu_redfish_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(device); + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp = 0; + + if (g_strcmp0(key, "RedfishResetPreDelay") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, error)) + return FALSE; + priv->reset_pre_delay = tmp; + return TRUE; + } + if (g_strcmp0(key, "RedfishResetPostDelay") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, error)) + return FALSE; + priv->reset_post_delay = tmp; + return TRUE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_redfish_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(object); + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_BACKEND: + g_value_set_object(value, priv->backend); + break; + case PROP_MEMBER: + g_value_set_pointer(value, priv->member); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_redfish_device_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(object); + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_BACKEND: + g_set_object(&priv->backend, g_value_get_object(value)); + break; + case PROP_MEMBER: + priv->member = json_object_ref(g_value_get_pointer(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_redfish_device_init(FuRedfishDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "Redfish device"); + fu_device_add_protocol(FU_DEVICE(self), "org.dmtf.redfish"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_register_private_flag(FU_DEVICE(self), + FU_REDFISH_DEVICE_FLAG_IS_BACKUP, + "is-backup"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_REDFISH_DEVICE_FLAG_UNSIGNED_BUILD, + "unsigned-build"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_REDFISH_DEVICE_FLAG_WILDCARD_TARGETS, + "wildcard-targets"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_REDFISH_DEVICE_FLAG_MANAGER_RESET, + "manager-reset"); +} + +static void +fu_redfish_device_finalize(GObject *object) +{ + FuRedfishDevice *self = FU_REDFISH_DEVICE(object); + FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + if (priv->backend != NULL) + g_object_unref(priv->backend); + if (priv->member != NULL) + json_object_unref(priv->member); + g_free(priv->build); + G_OBJECT_CLASS(fu_redfish_device_parent_class)->finalize(object); +} + +static void +fu_redfish_device_class_init(FuRedfishDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + object_class->get_property = fu_redfish_device_get_property; + object_class->set_property = fu_redfish_device_set_property; + object_class->finalize = fu_redfish_device_finalize; + + klass_device->to_string = fu_redfish_device_to_string; + klass_device->probe = fu_redfish_device_probe; + klass_device->set_quirk_kv = fu_redfish_device_set_quirk_kv; + + /** + * FuRedfishDevice:backend: + * + * The backend that added the device. + */ + pspec = + g_param_spec_object("backend", + NULL, + NULL, + FU_TYPE_BACKEND, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_BACKEND, pspec); + + /** + * FuRedfishDevice:member: + * + * The JSON root member for the device. + */ + pspec = + g_param_spec_pointer("member", + NULL, + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_MEMBER, pspec); +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-device.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-device.h new file mode 100644 index 0000000000000000000000000000000000000000..8f789b2766efbad6a579d81936da5002e6332b2f --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-device.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-redfish-backend.h" + +#define FU_TYPE_REDFISH_DEVICE (fu_redfish_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuRedfishDevice, fu_redfish_device, FU, REDFISH_DEVICE, FuDevice) + +struct _FuRedfishDeviceClass { + FuDeviceClass parent_class; +}; + +/** + * FU_REDFISH_DEVICE_FLAG_IS_BACKUP: + * + * The device is the other half of a dual image firmware. + */ +#define FU_REDFISH_DEVICE_FLAG_IS_BACKUP (1 << 0) + +/** + * FU_REDFISH_DEVICE_FLAG_UNSIGNED_BUILD: + * + * Use unsigned development builds. + */ +#define FU_REDFISH_DEVICE_FLAG_UNSIGNED_BUILD (1 << 1) + +/** + * FU_REDFISH_DEVICE_FLAG_MANAGER_RESET: + * + * Reset the manager (typically the BMC) after updating this device. + */ +#define FU_REDFISH_DEVICE_FLAG_MANAGER_RESET (1 << 2) + +/** + * FU_REDFISH_DEVICE_FLAG_WILDCARD_TARGETS: + * + * Do not specify the `odata.id` in the multipart update Targets array and allow + * the BMC to deploy the firmware onto all compatible hardware. + * + * To use this option the payload must contain metadata that restricts it to a + * specific SoftwareId. + */ +#define FU_REDFISH_DEVICE_FLAG_WILDCARD_TARGETS (1 << 3) + +FuRedfishBackend * +fu_redfish_device_get_backend(FuRedfishDevice *self); +gboolean +fu_redfish_device_poll_task(FuRedfishDevice *self, + const gchar *location, + FuProgress *progress, + GError **error); +guint +fu_redfish_device_get_reset_pre_delay(FuRedfishDevice *self); +guint +fu_redfish_device_get_reset_post_delay(FuRedfishDevice *self); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-legacy-device.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-legacy-device.c new file mode 100644 index 0000000000000000000000000000000000000000..5d156b4eff03aba8309c727aecc719633618e01b --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-legacy-device.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-redfish-legacy-device.h" +#include "fu-redfish-request.h" + +struct _FuRedfishLegacyDevice { + FuRedfishDevice parent_instance; +}; + +G_DEFINE_TYPE(FuRedfishLegacyDevice, fu_redfish_legacy_device, FU_TYPE_REDFISH_DEVICE) + +static gboolean +fu_redfish_legacy_device_detach(FuDevice *dev, FuProgress *progress, GError **error) +{ + FuRedfishLegacyDevice *self = FU_REDFISH_LEGACY_DEVICE(dev); + FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + g_autoptr(JsonBuilder) builder = json_builder_new(); + + /* create header */ + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "HttpPushUriTargetsBusy"); + json_builder_add_boolean_value(builder, TRUE); + json_builder_set_member_name(builder, "HttpPushUriTargets"); + json_builder_begin_array(builder); + json_builder_add_string_value(builder, fu_device_get_logical_id(FU_DEVICE(self))); + json_builder_end_array(builder); + json_builder_end_object(builder); + + /* patch the two fields */ + return fu_redfish_request_perform_full(request, + "/redfish/v1/UpdateService", + "PATCH", + builder, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error); +} + +static gboolean +fu_redfish_legacy_device_attach(FuDevice *dev, FuProgress *progress, GError **error) +{ + FuRedfishLegacyDevice *self = FU_REDFISH_LEGACY_DEVICE(dev); + FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + g_autoptr(JsonBuilder) builder = json_builder_new(); + + /* create header */ + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "HttpPushUriTargetsBusy"); + json_builder_add_boolean_value(builder, FALSE); + json_builder_set_member_name(builder, "HttpPushUriTargets"); + json_builder_begin_array(builder); + json_builder_end_array(builder); + json_builder_end_object(builder); + + /* patch the two fields */ + return fu_redfish_request_perform_full(request, + "/redfish/v1/UpdateService", + "PATCH", + builder, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error); +} + +static gboolean +fu_redfish_legacy_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRedfishLegacyDevice *self = FU_REDFISH_LEGACY_DEVICE(device); + FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + CURL *curl; + JsonObject *json_obj; + const gchar *location; + g_autoptr(FuRedfishRequest) request = NULL; + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* POST data */ + request = fu_redfish_backend_request_new(backend); + curl = fu_redfish_request_get_curl(request); + (void)curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, g_bytes_get_data(fw, NULL)); + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)g_bytes_get_size(fw)); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_redfish_request_perform(request, + fu_redfish_backend_get_push_uri_path(backend), + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + + /* poll the task for progress */ + json_obj = fu_redfish_request_get_json_object(request); + if (!json_object_has_member(json_obj, "@odata.id")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no task returned for %s", + fu_redfish_backend_get_push_uri_path(backend)); + return FALSE; + } + location = json_object_get_string_member(json_obj, "@odata.id"); + return fu_redfish_device_poll_task(FU_REDFISH_DEVICE(self), location, progress, error); +} + +static void +fu_redfish_legacy_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_redfish_legacy_device_init(FuRedfishLegacyDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "Redfish legacy device"); +} + +static void +fu_redfish_legacy_device_class_init(FuRedfishLegacyDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->attach = fu_redfish_legacy_device_attach; + klass_device->detach = fu_redfish_legacy_device_detach; + klass_device->write_firmware = fu_redfish_legacy_device_write_firmware; + klass_device->set_progress = fu_redfish_legacy_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-legacy-device.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-legacy-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ec35b383adaf945c8f09358e5778a4580df53402 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-legacy-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-redfish-device.h" + +#define FU_TYPE_REDFISH_LEGACY_DEVICE (fu_redfish_legacy_device_get_type()) +G_DECLARE_FINAL_TYPE(FuRedfishLegacyDevice, + fu_redfish_legacy_device, + FU, + REDFISH_LEGACY_DEVICE, + FuRedfishDevice) diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-multipart-device.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-multipart-device.c new file mode 100644 index 0000000000000000000000000000000000000000..78d1872b53dedda707fdbf5f40fa289c080e16be --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-multipart-device.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-redfish-multipart-device.h" +#include "fu-redfish-request.h" + +struct _FuRedfishMultipartDevice { + FuRedfishDevice parent_instance; +}; + +G_DEFINE_TYPE(FuRedfishMultipartDevice, fu_redfish_multipart_device, FU_TYPE_REDFISH_DEVICE) + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(curl_mime, curl_mime_free) + +static GString * +fu_redfish_multipart_device_get_parameters(FuRedfishMultipartDevice *self) +{ + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(JsonGenerator) json_generator = json_generator_new(); + g_autoptr(JsonNode) json_root = NULL; + + /* create header */ + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Targets"); + json_builder_begin_array(builder); + if (!fu_device_has_private_flag(FU_DEVICE(self), FU_REDFISH_DEVICE_FLAG_WILDCARD_TARGETS)) { + const gchar *logical_id = fu_device_get_logical_id(FU_DEVICE(self)); + json_builder_add_string_value(builder, logical_id); + } + json_builder_end_array(builder); + json_builder_set_member_name(builder, "@Redfish.OperationApplyTime"); + json_builder_add_string_value(builder, "Immediate"); + json_builder_end_object(builder); + + /* export as a string */ + json_root = json_builder_get_root(builder); + json_generator_set_pretty(json_generator, TRUE); + json_generator_set_root(json_generator, json_root); + json_generator_to_gstring(json_generator, str); + return g_steal_pointer(&str); +} + +static gboolean +fu_redfish_multipart_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRedfishMultipartDevice *self = FU_REDFISH_MULTIPART_DEVICE(device); + FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + CURL *curl; + JsonObject *json_obj; + curl_mimepart *part; + const gchar *location; + g_autoptr(curl_mime) mime = NULL; + g_autoptr(FuRedfishRequest) request = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GString) params = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* create the multipart request */ + request = fu_redfish_backend_request_new(backend); + curl = fu_redfish_request_get_curl(request); + mime = curl_mime_init(curl); + (void)curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + + params = fu_redfish_multipart_device_get_parameters(self); + part = curl_mime_addpart(mime); + curl_mime_name(part, "UpdateParameters"); + (void)curl_mime_type(part, "application/json"); + (void)curl_mime_data(part, params->str, CURL_ZERO_TERMINATED); + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) + g_debug("request: %s", params->str); + + part = curl_mime_addpart(mime); + curl_mime_name(part, "UpdateFile"); + (void)curl_mime_type(part, "application/octet-stream"); + (void)curl_mime_filedata(part, "firmware.bin"); + (void)curl_mime_data(part, g_bytes_get_data(fw, NULL), g_bytes_get_size(fw)); + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_redfish_request_perform(request, + fu_redfish_backend_get_push_uri_path(backend), + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + if (fu_redfish_request_get_status_code(request) != 202) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to upload: %li", + fu_redfish_request_get_status_code(request)); + return FALSE; + } + + /* prefer the header, otherwise fall back to the response */ + json_obj = fu_redfish_request_get_json_object(request); + if (json_object_has_member(json_obj, "TaskMonitor")) { + const gchar *tmp = json_object_get_string_member(json_obj, "TaskMonitor"); + g_debug("task manager for cleanup is %s", tmp); + } + + /* poll the task for progress */ + if (!json_object_has_member(json_obj, "@odata.id")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no task returned for %s", + fu_redfish_backend_get_push_uri_path(backend)); + return FALSE; + } + location = json_object_get_string_member(json_obj, "@odata.id"); + return fu_redfish_device_poll_task(FU_REDFISH_DEVICE(self), location, progress, error); +} + +static void +fu_redfish_multipart_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_redfish_multipart_device_init(FuRedfishMultipartDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "Redfish multipart device"); +} + +static void +fu_redfish_multipart_device_class_init(FuRedfishMultipartDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_redfish_multipart_device_write_firmware; + klass_device->set_progress = fu_redfish_multipart_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-multipart-device.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-multipart-device.h new file mode 100644 index 0000000000000000000000000000000000000000..d076a391792cbd3751bbb3fa046a94bcc90a66fd --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-multipart-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-redfish-device.h" + +#define FU_TYPE_REDFISH_MULTIPART_DEVICE (fu_redfish_multipart_device_get_type()) +G_DECLARE_FINAL_TYPE(FuRedfishMultipartDevice, + fu_redfish_multipart_device, + FU, + REDFISH_MULTIPART_DEVICE, + FuRedfishDevice) diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-network-device.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-network-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b095f6e40faa60281a90f949b1b33492ce44a034 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-network-device.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-redfish-network-device.h" +#include "fu-redfish-network.h" + +struct _FuRedfishNetworkDevice { + GObject parent_instance; + gchar *object_path; +}; + +G_DEFINE_TYPE(FuRedfishNetworkDevice, fu_redfish_network_device, G_TYPE_OBJECT) + +const gchar * +fu_redfish_network_device_state_to_string(FuRedfishNetworkDeviceState state) +{ + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN) + return "unknown"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_UNMANAGED) + return "unmanaged"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_UNAVAILABLE) + return "unavailable"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_DISCONNECTED) + return "disconnected"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_PREPARE) + return "prepare"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_CONFIG) + return "config"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_NEED_AUTH) + return "need-auth"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_IP_CONFIG) + return "ip-config"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_IP_CHECK) + return "ip-check"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_SECONDARIES) + return "secondaries"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_ACTIVATED) + return "activated"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_DEACTIVATING) + return "deactivating"; + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_FAILED) + return "failed"; + return NULL; +} + +gboolean +fu_redfish_network_device_get_state(FuRedfishNetworkDevice *self, + FuRedfishNetworkDeviceState *state, + GError **error) +{ + g_autoptr(GVariant) retval = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + + g_return_val_if_fail(FU_IS_REDFISH_NETWORK_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect to device */ + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + NETWORK_MANAGER_SERVICE_NAME, + self->object_path, + NETWORK_MANAGER_INTERFACE_DEVICE, + NULL, + error); + if (proxy == NULL) + return FALSE; + retval = g_dbus_proxy_get_cached_property(proxy, "State"); + if (retval == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_CONNECTED, + "could not find State"); + return FALSE; + } + if (state != NULL) + *state = g_variant_get_uint32(retval); + return TRUE; +} + +gboolean +fu_redfish_network_device_connect(FuRedfishNetworkDevice *self, GError **error) +{ + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GTimer) timer = g_timer_new(); + g_autoptr(GVariant) success = NULL; + + g_return_val_if_fail(FU_IS_REDFISH_NETWORK_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect to manager */ + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + NETWORK_MANAGER_SERVICE_NAME, + NETWORK_MANAGER_PATH, + NETWORK_MANAGER_INTERFACE, + NULL, + error); + if (proxy == NULL) + return FALSE; + + /* activate with some good defaults */ + success = g_dbus_proxy_call_sync(proxy, + "ActivateConnection", + g_variant_new("(ooo)", "/", self->object_path, "/"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (success == NULL) + return FALSE; + + /* wait until the network interface comes up */ + do { + FuRedfishNetworkDeviceState state = FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN; + if (!fu_redfish_network_device_get_state(self, &state, error)) + return FALSE; + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) { + g_debug("%s device state is now %s [%u]", + self->object_path, + fu_redfish_network_device_state_to_string(state), + state); + } + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_ACTIVATED) + return TRUE; + g_usleep(50 * 1000); + } while (g_timer_elapsed(timer, NULL) < 5.f); + + /* timed out */ + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "could not activate connection"); + return FALSE; +} + +gchar * +fu_redfish_network_device_get_address(FuRedfishNetworkDevice *self, GError **error) +{ + g_autofree gchar *address = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GDBusProxy) proxy2 = NULL; + g_autoptr(GVariant) addr_data = NULL; + g_autoptr(GVariant) ip4_config = NULL; + + g_return_val_if_fail(FU_IS_REDFISH_NETWORK_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect to device */ + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + NETWORK_MANAGER_SERVICE_NAME, + self->object_path, + NETWORK_MANAGER_INTERFACE_DEVICE, + NULL, + error); + if (proxy == NULL) + return NULL; + ip4_config = g_dbus_proxy_get_cached_property(proxy, "Ip4Config"); + if (ip4_config == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_CONNECTED, + "could not find IPv4 config"); + return NULL; + } + proxy2 = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + NETWORK_MANAGER_SERVICE_NAME, + g_variant_get_string(ip4_config, NULL), + NETWORK_MANAGER_INTERFACE_IP4_CONFIG, + NULL, + error); + if (proxy2 == NULL) + return NULL; + + addr_data = g_dbus_proxy_get_cached_property(proxy2, "AddressData"); + if (addr_data != NULL) { + g_autoptr(GVariant) addr_data0 = g_variant_get_child_value(addr_data, 0); + g_autoptr(GVariantDict) dict = g_variant_dict_new(addr_data0); + g_variant_dict_lookup(dict, "address", "s", &address); + } + if (address == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_CONNECTED, + "could not find IP address for device"); + return NULL; + } + + /* success */ + return g_steal_pointer(&address); +} + +FuRedfishNetworkDevice * +fu_redfish_network_device_new(const gchar *object_path) +{ + FuRedfishNetworkDevice *self = g_object_new(FU_TYPE_REDFISH_NETWORK_DEVICE, NULL); + self->object_path = g_strdup(object_path); + return self; +} + +static void +fu_redfish_network_device_init(FuRedfishNetworkDevice *self) +{ +} + +static void +fu_redfish_network_device_finalize(GObject *object) +{ + FuRedfishNetworkDevice *self = FU_REDFISH_NETWORK_DEVICE(object); + g_free(self->object_path); + G_OBJECT_CLASS(fu_redfish_network_device_parent_class)->finalize(object); +} + +static void +fu_redfish_network_device_class_init(FuRedfishNetworkDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_redfish_network_device_finalize; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-network-device.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-network-device.h new file mode 100644 index 0000000000000000000000000000000000000000..604eca86e76bb24852304a9b465ed56ca31ee6e9 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-network-device.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_REDFISH_NETWORK_DEVICE (fu_redfish_network_device_get_type()) +G_DECLARE_FINAL_TYPE(FuRedfishNetworkDevice, + fu_redfish_network_device, + FU, + REDFISH_NETWORK_DEVICE, + GObject) + +typedef enum { + FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN = 0, + FU_REDFISH_NETWORK_DEVICE_STATE_UNMANAGED = 10, + FU_REDFISH_NETWORK_DEVICE_STATE_UNAVAILABLE = 20, + FU_REDFISH_NETWORK_DEVICE_STATE_DISCONNECTED = 30, + FU_REDFISH_NETWORK_DEVICE_STATE_PREPARE = 40, + FU_REDFISH_NETWORK_DEVICE_STATE_CONFIG = 50, + FU_REDFISH_NETWORK_DEVICE_STATE_NEED_AUTH = 60, + FU_REDFISH_NETWORK_DEVICE_STATE_IP_CONFIG = 70, + FU_REDFISH_NETWORK_DEVICE_STATE_IP_CHECK = 80, + FU_REDFISH_NETWORK_DEVICE_STATE_SECONDARIES = 90, + FU_REDFISH_NETWORK_DEVICE_STATE_ACTIVATED = 100, + FU_REDFISH_NETWORK_DEVICE_STATE_DEACTIVATING = 110, + FU_REDFISH_NETWORK_DEVICE_STATE_FAILED = 120, +} FuRedfishNetworkDeviceState; + +const gchar * +fu_redfish_network_device_state_to_string(FuRedfishNetworkDeviceState state); +FuRedfishNetworkDevice * +fu_redfish_network_device_new(const gchar *object_path); +gboolean +fu_redfish_network_device_get_state(FuRedfishNetworkDevice *self, + FuRedfishNetworkDeviceState *state, + GError **error); +gchar * +fu_redfish_network_device_get_address(FuRedfishNetworkDevice *self, GError **error); +gboolean +fu_redfish_network_device_connect(FuRedfishNetworkDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-network.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-network.c new file mode 100644 index 0000000000000000000000000000000000000000..6566cc4e62f58b4733cef793cc315b44cfbc5444 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-network.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-redfish-network.h" + +typedef struct { + FuRedfishNetworkDevice *device; + const gchar *mac_addr; + guint16 vid; + guint16 pid; +} FuRedfishNetworkMatchHelper; + +static gboolean +fu_redfish_network_device_match_device(FuRedfishNetworkMatchHelper *helper, + const gchar *object_path, + GError **error) +{ + g_autoptr(GDBusProxy) proxy = NULL; + + /* connect to device */ + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + NETWORK_MANAGER_SERVICE_NAME, + object_path, + NETWORK_MANAGER_INTERFACE_DEVICE, + NULL, + error); + if (proxy == NULL) { + g_prefix_error(error, "failed to connect to interface %s: ", object_path); + return FALSE; + } + + /* compare MAC address different */ + if (helper->mac_addr != NULL) { + const gchar *mac_addr = NULL; + g_autoptr(GVariant) hw_address = NULL; + + hw_address = g_dbus_proxy_get_cached_property(proxy, "HwAddress"); + if (hw_address == NULL) + return TRUE; + mac_addr = g_variant_get_string(hw_address, NULL); + + /* verify */ + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) + g_debug("mac_addr=%s", mac_addr); + if (g_strcmp0(mac_addr, helper->mac_addr) == 0) + helper->device = fu_redfish_network_device_new(object_path); + } + + /* compare VID:PID */ + if (helper->vid != 0x0 && helper->pid != 0x0) { +#ifdef HAVE_GUDEV + const gchar *sysfs_path = NULL; + const gchar *tmp; + guint16 pid = 0; + guint16 vid = 0; + g_autoptr(GVariant) udi = NULL; + g_autoptr(GUdevClient) udev_client = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + + udi = g_dbus_proxy_get_cached_property(proxy, "Udi"); + if (udi == NULL) + return TRUE; + sysfs_path = g_variant_get_string(udi, NULL); + + /* get the VID and PID */ + udev_client = g_udev_client_new(NULL); + udev_device = g_udev_client_query_by_sysfs_path(udev_client, sysfs_path); + if (udev_device == NULL) + return TRUE; + tmp = g_udev_device_get_property(udev_device, "ID_VENDOR_ID"); + if (tmp != NULL) + vid = g_ascii_strtoull(tmp, NULL, 16); + tmp = g_udev_device_get_property(udev_device, "ID_MODEL_ID"); + if (tmp != NULL) + pid = g_ascii_strtoull(tmp, NULL, 16); + + /* verify */ + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) + g_debug("%s: 0x%04x, 0x%04x", sysfs_path, vid, pid); + if (vid == helper->vid && pid == helper->pid) + helper->device = fu_redfish_network_device_new(object_path); +#else + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no UDev support"); + return FALSE; +#endif + } + + /* assume success */ + return TRUE; +} + +static gboolean +fu_redfish_network_device_match(FuRedfishNetworkMatchHelper *helper, GError **error) +{ + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GVariant) devices = NULL; + g_auto(GStrv) paths = NULL; + + /* get devices */ + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + NETWORK_MANAGER_SERVICE_NAME, + NETWORK_MANAGER_PATH, + NETWORK_MANAGER_INTERFACE, + NULL, + &error_local); + if (proxy == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "D-Bus is not running"); + return FALSE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to construct proxy for %s: ", + NETWORK_MANAGER_SERVICE_NAME); + return FALSE; + } + devices = g_dbus_proxy_call_sync(proxy, + "GetDevices", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error_local); + if (devices == NULL) { + if (g_error_matches(error_local, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "NetworkManager is not running"); + return FALSE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to call GetDevices() on %s: ", + g_dbus_proxy_get_name(proxy)); + return FALSE; + } + + /* look at each device */ + g_variant_get(devices, "(^ao)", &paths); + for (guint i = 0; paths[i] != NULL; i++) { + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) + g_debug("device %u: %s", i, paths[i]); + if (!fu_redfish_network_device_match_device(helper, paths[i], error)) + return FALSE; + if (helper->device != NULL) + break; + } + if (helper->device == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "could not find device"); + return FALSE; + } + return TRUE; +} + +FuRedfishNetworkDevice * +fu_redfish_network_device_for_mac_addr(const gchar *mac_addr, GError **error) +{ + FuRedfishNetworkMatchHelper helper = { + .mac_addr = mac_addr, + }; + if (!fu_redfish_network_device_match(&helper, error)) { + g_prefix_error(error, "missing %s: ", mac_addr); + return NULL; + } + return helper.device; +} + +FuRedfishNetworkDevice * +fu_redfish_network_device_for_vid_pid(guint16 vid, guint16 pid, GError **error) +{ + FuRedfishNetworkMatchHelper helper = { + .vid = vid, + .pid = pid, + }; + if (!fu_redfish_network_device_match(&helper, error)) { + g_prefix_error(error, "missing 0x%04x:0x%04x: ", vid, pid); + return NULL; + } + return helper.device; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-network.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-network.h new file mode 100644 index 0000000000000000000000000000000000000000..2a991d69f5aa4667211bbfb9ace09ccce342163a --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-network.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-redfish-network-device.h" + +#define NETWORK_MANAGER_SERVICE_NAME "org.freedesktop.NetworkManager" +#define NETWORK_MANAGER_INTERFACE "org.freedesktop.NetworkManager" +#define NETWORK_MANAGER_INTERFACE_IP4_CONFIG "org.freedesktop.NetworkManager.IP4Config" +#define NETWORK_MANAGER_INTERFACE_DEVICE "org.freedesktop.NetworkManager.Device" +#define NETWORK_MANAGER_PATH "/org/freedesktop/NetworkManager" + +FuRedfishNetworkDevice * +fu_redfish_network_device_for_mac_addr(const gchar *mac_addr, GError **error); +FuRedfishNetworkDevice * +fu_redfish_network_device_for_vid_pid(guint16 vid, guint16 pid, GError **error); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-plugin.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..9c91c0b18978ea43056cd587139ab9f886cf1114 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-plugin.c @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_LINUX_IPMI_H +#include "fu-ipmi-device.h" +#endif + +#include "fu-redfish-backend.h" +#include "fu-redfish-common.h" +#include "fu-redfish-device.h" +#include "fu-redfish-network.h" +#include "fu-redfish-plugin.h" +#include "fu-redfish-smbios.h" + +#define FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY 10 /* seconds */ + +struct _FuRedfishPlugin { + FuPlugin parent_instance; + FuRedfishBackend *backend; + FuRedfishSmbios *smbios; /* nullable */ +}; + +G_DEFINE_TYPE(FuRedfishPlugin, fu_redfish_plugin, FU_TYPE_PLUGIN) + +static void +fu_redfish_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + fu_backend_add_string(FU_BACKEND(self->backend), idt, str); + if (self->smbios != NULL) { + g_autofree gchar *smbios = fu_firmware_to_string(FU_FIRMWARE(self->smbios)); + fu_string_append(str, idt, "Smbios", smbios); + } +} + +static gchar * +fu_common_generate_password(guint length) +{ + GString *str = g_string_sized_new(length); + + /* get a random password string */ + while (str->len < length) { + gchar tmp = (gchar)g_random_int_range(0x0, 0xff); + if (g_ascii_isalnum(tmp)) + g_string_append_c(str, tmp); + } + return g_string_free(str, FALSE); +} + +static gboolean +fu_redfish_plugin_change_expired(FuPlugin *plugin, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + g_autofree gchar *password_new = fu_common_generate_password(15); + g_autofree gchar *uri = NULL; + g_autoptr(FuRedfishRequest) request = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + + /* select correct, falling back to default for old fwupd versions */ + uri = fu_plugin_get_config_value(plugin, "UserUri"); + if (uri == NULL) { + uri = g_strdup("/redfish/v1/AccountService/Accounts/2"); + if (!fu_plugin_set_config_value(plugin, "UserUri", uri, error)) + return FALSE; + } + + /* now use Redfish to change the temporary password to the actual password */ + request = fu_redfish_backend_request_new(self->backend); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Password"); + json_builder_add_string_value(builder, password_new); + json_builder_end_object(builder); + if (!fu_redfish_request_perform_full(request, + uri, + "PATCH", + builder, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + fu_redfish_backend_set_password(self->backend, password_new); + + /* success */ + return fu_plugin_set_config_value(plugin, "Password", password_new, error); +} + +static gboolean +fu_redfish_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the list of devices */ + if (!fu_backend_coldplug(FU_BACKEND(self->backend), progress, &error_local)) { + /* did the user password expire? */ + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_AUTH_EXPIRED)) { + if (!fu_redfish_plugin_change_expired(plugin, error)) + return FALSE; + if (!fu_backend_coldplug(FU_BACKEND(self->backend), progress, error)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_AUTH_REQUIRED); + return FALSE; + } + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + devices = fu_backend_get_devices(FU_BACKEND(self->backend)); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + if (fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "reset-required")) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_plugin_device_add(plugin, device); + } + + /* this is no longer relevant */ + if (devices->len > 0) { + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "bios"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "uefi_capsule"); + } + return TRUE; +} + +void +fu_redfish_plugin_set_credentials(FuPlugin *plugin, const gchar *username, const gchar *password) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + + fu_redfish_backend_set_username(self->backend, username); + fu_redfish_backend_set_password(self->backend, password); +} + +static gboolean +fu_redfish_plugin_discover_uefi_credentials(FuPlugin *plugin, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + gsize bufsz = 0; + guint32 indications = 0x0; + g_autofree gchar *userpass_safe = NULL; + g_autofree guint8 *buf = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GBytes) userpass = NULL; + + /* get the uint32 specifying if there are EFI variables set */ + if (!fu_efivar_get_data(REDFISH_EFI_INFORMATION_GUID, + REDFISH_EFI_INFORMATION_INDICATIONS, + &buf, + &bufsz, + NULL, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, bufsz, 0x0, &indications, G_LITTLE_ENDIAN, error)) + return FALSE; + if ((indications & 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_efivar_get_data_bytes(REDFISH_EFI_INFORMATION_GUID, + REDFISH_EFI_INFORMATION_OS_CREDENTIALS, + NULL, + 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_backend_set_username(self->backend, split[0]); + fu_redfish_backend_set_password(self->backend, split[1]); + return TRUE; +} + +static gboolean +fu_redfish_plugin_discover_smbios_table(FuPlugin *plugin, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *smbios_data_fn; + g_autoptr(FuRedfishSmbios) smbios = fu_redfish_smbios_new(); + g_autoptr(GBytes) smbios_data = NULL; + + /* is optional if not in self tests */ + smbios_data_fn = g_getenv("FWUPD_REDFISH_SMBIOS_DATA"); + if (smbios_data_fn != NULL) { + smbios_data = fu_bytes_get_contents(smbios_data_fn, error); + if (smbios_data == NULL) + return FALSE; + } else { + smbios_data = fu_context_get_smbios_data(ctx, REDFISH_SMBIOS_TABLE_TYPE); + if (smbios_data == NULL) + return TRUE; + } + if (!fu_firmware_parse(FU_FIRMWARE(smbios), + smbios_data, + FWUPD_INSTALL_FLAG_NO_SEARCH, + error)) { + g_prefix_error(error, "failed to parse SMBIOS table entry type 42: "); + return FALSE; + } + + /* success */ + g_set_object(&self->smbios, smbios); + return TRUE; +} + +static gboolean +fu_redfish_plugin_autoconnect_network_device(FuRedfishPlugin *self, GError **error) +{ + g_autofree gchar *hostname = NULL; + g_autoptr(FuRedfishNetworkDevice) device = NULL; + + /* we have no data */ + if (self->smbios == NULL) + return TRUE; + + /* get IP, falling back to hostname, then MAC, then VID:PID */ + hostname = g_strdup(fu_redfish_smbios_get_ip_addr(self->smbios)); + if (hostname == NULL) + hostname = g_strdup(fu_redfish_smbios_get_hostname(self->smbios)); + if (device == NULL) { + const gchar *mac_addr = fu_redfish_smbios_get_mac_addr(self->smbios); + if (mac_addr != NULL) { + g_autoptr(GError) error_network = NULL; + device = fu_redfish_network_device_for_mac_addr(mac_addr, &error_network); + if (device == NULL) + g_debug("failed to get device: %s", error_network->message); + } + } + if (device == NULL) { + guint16 vid = fu_redfish_smbios_get_vid(self->smbios); + guint16 pid = fu_redfish_smbios_get_pid(self->smbios); + if (vid != 0x0 && pid != 0x0) { + g_autoptr(GError) error_network = NULL; + device = fu_redfish_network_device_for_vid_pid(vid, pid, &error_network); + if (device == NULL) + g_debug("failed to get device: %s", error_network->message); + } + } + + /* autoconnect device if required */ + if (device != NULL) { + FuRedfishNetworkDeviceState state = FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN; + if (!fu_redfish_network_device_get_state(device, &state, error)) + return FALSE; + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) { + g_debug("device state is now %s [%u]", + fu_redfish_network_device_state_to_string(state), + state); + } + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_DISCONNECTED) { + if (!fu_redfish_network_device_connect(device, error)) + return FALSE; + } + if (hostname == NULL) { + hostname = fu_redfish_network_device_get_address(device, error); + if (hostname == NULL) + return FALSE; + } + } + if (hostname == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "no hostname"); + return FALSE; + } + fu_redfish_backend_set_hostname(self->backend, hostname); + fu_redfish_backend_set_port(self->backend, fu_redfish_smbios_get_port(self->smbios)); + return TRUE; +} + +#ifdef HAVE_LINUX_IPMI_H + +static gboolean +fu_redfish_plugin_ipmi_create_user(FuPlugin *plugin, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + const gchar *username_fwupd = "fwupd"; + guint8 user_id = G_MAXUINT8; + g_autofree gchar *password_new = fu_common_generate_password(15); + g_autofree gchar *password_tmp = fu_common_generate_password(15); + g_autofree gchar *uri = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuIpmiDevice) device = fu_ipmi_device_new(fu_plugin_get_context(plugin)); + g_autoptr(FuRedfishRequest) request = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + + /* create device */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + + /* check for existing user, and if not then remember the first spare slot */ + for (guint8 i = 2; i < 0xFF; i++) { + g_autofree gchar *username = fu_ipmi_device_get_user_password(device, i, NULL); + if (username == NULL && user_id == G_MAXUINT8) { + g_debug("KCS slot %u free", i); + user_id = i; + continue; + } + if (g_strcmp0(username, "fwupd") == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "fwupd user already exists in KCS slot %u", + (guint)i); + return FALSE; + } + } + if (user_id == G_MAXUINT8) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "all KCS slots full, cannot create user"); + return FALSE; + } + + /* create a user with appropriate permissions */ + if (!fu_ipmi_device_set_user_name(device, user_id, username_fwupd, error)) + return FALSE; + if (!fu_ipmi_device_set_user_enable(device, user_id, TRUE, error)) + return FALSE; + if (!fu_ipmi_device_set_user_priv(device, user_id, 0x4, 1, error)) + return FALSE; + if (!fu_ipmi_device_set_user_password(device, user_id, password_tmp, error)) + return FALSE; + fu_redfish_backend_set_username(self->backend, username_fwupd); + fu_redfish_backend_set_password(self->backend, password_tmp); + + /* wait for Redfish to sync */ + g_usleep(2 * G_USEC_PER_SEC); + + /* now use Redfish to change the temporary password to the actual password */ + request = fu_redfish_backend_request_new(self->backend); + uri = g_strdup_printf("/redfish/v1/AccountService/Accounts/%u", (guint)user_id - 1); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Password"); + json_builder_add_string_value(builder, password_new); + json_builder_end_object(builder); + if (!fu_redfish_request_perform_full(request, + uri, + "PATCH", + builder, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) + return FALSE; + fu_redfish_backend_set_password(self->backend, password_new); + + /* success */ + if (!fu_plugin_set_config_value(plugin, "UserUri", uri, error)) + return FALSE; + if (!fu_plugin_set_config_value(plugin, "Username", username_fwupd, error)) + return FALSE; + if (!fu_plugin_set_config_value(plugin, "Password", password_new, error)) + return FALSE; + + return TRUE; +} +#endif + +static gboolean +fu_redfish_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + g_autofree gchar *ca_check_str = NULL; + g_autofree gchar *password = NULL; + g_autofree gchar *redfish_uri = NULL; + g_autofree gchar *username = NULL; + g_autoptr(GError) error_uefi = NULL; + + /* optional */ + if (!fu_redfish_plugin_discover_smbios_table(plugin, error)) + return FALSE; + if (!fu_redfish_plugin_autoconnect_network_device(self, error)) + return FALSE; + if (!fu_redfish_plugin_discover_uefi_credentials(plugin, &error_uefi)) { + g_debug("failed to get username and password automatically: %s", + error_uefi->message); + } + + /* override with the conf file */ + redfish_uri = fu_plugin_get_config_value(plugin, "Uri"); + if (redfish_uri != NULL) { + const gchar *ip_str = NULL; + g_auto(GStrv) split = NULL; + guint64 port = 0; + + if (g_str_has_prefix(redfish_uri, "https://")) { + fu_redfish_backend_set_https(self->backend, TRUE); + ip_str = redfish_uri + strlen("https://"); + port = 443; + } else if (g_str_has_prefix(redfish_uri, "http://")) { + fu_redfish_backend_set_https(self->backend, FALSE); + ip_str = redfish_uri + strlen("http://"); + port = 80; + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid scheme"); + return FALSE; + } + + split = g_strsplit(ip_str, ":", 2); + fu_redfish_backend_set_hostname(self->backend, split[0]); + if (g_strv_length(split) > 1) + port = g_ascii_strtoull(split[1], NULL, 10); + if (port == 0 || port == G_MAXUINT64) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no valid port specified"); + return FALSE; + } + fu_redfish_backend_set_port(self->backend, port); + } + username = fu_plugin_get_config_value(plugin, "Username"); + if (username != NULL) + fu_redfish_backend_set_username(self->backend, username); + password = fu_plugin_get_config_value(plugin, "Password"); + if (password != NULL) + fu_redfish_backend_set_password(self->backend, password); + ca_check_str = fu_plugin_get_config_value(plugin, "CACheck"); + if (ca_check_str != NULL) { + gboolean ca_check = fu_plugin_get_config_value_boolean(plugin, "CACheck"); + fu_redfish_backend_set_cacheck(self->backend, ca_check); + } + if (fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "wildcard-targets")) + fu_redfish_backend_set_wildcard_targets(self->backend, TRUE); + +#ifdef HAVE_LINUX_IPMI_H + /* we got neither a type 42 entry or config value, lets try IPMI */ + if (fu_redfish_backend_get_username(self->backend) == NULL) { + if (!fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "ipmi-create-user")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no username and password specified, " + "and no vendor quirk for 'ipmi-create-user'"); + return FALSE; + } + if (!fu_plugin_get_config_value_boolean(plugin, "IpmiDisableCreateUser")) { + g_debug("attempting to create user using IPMI"); + if (!fu_redfish_plugin_ipmi_create_user(plugin, error)) + return FALSE; + } + } +#endif + + return fu_backend_setup(FU_BACKEND(self->backend), progress, error); +} + +static gboolean +fu_redfish_plugin_cleanup_setup_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(user_data); + FuProgress *progress = fu_progress_new(G_STRLOC); + + /* the network adaptor might not auto-connect when coming back */ + if (!fu_redfish_plugin_autoconnect_network_device(self, error)) + return FALSE; + return fu_backend_setup(FU_BACKEND(self->backend), progress, error); +} + +static gboolean +fu_redfish_plugin_cleanup_coldplug_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(user_data); + FuProgress *progress = fu_progress_new(G_STRLOC); + if (!fu_redfish_plugin_autoconnect_network_device(self, error)) + return FALSE; + return fu_redfish_plugin_coldplug(FU_PLUGIN(self), progress, error); +} + +static gboolean +fu_redfish_plugin_cleanup(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + guint64 reset_timeout = 0; + g_autofree gchar *restart_timeout_str = NULL; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(self->backend); + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(GPtrArray) devices = NULL; + + /* nothing to do */ + if (!fu_device_has_private_flag(device, FU_REDFISH_DEVICE_FLAG_MANAGER_RESET)) + return TRUE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "manager-reboot"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "pre-delay"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 67, "poll-manager"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 18, "post-delay"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 9, "recoldplug"); + + /* ask the BMC to reboot */ + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "ResetType"); + json_builder_add_string_value(builder, "ForceRestart"); + json_builder_end_object(builder); + if (!fu_redfish_request_perform_full(request, + "/redfish/v1/Managers/1/Actions/Manager.Reset", + "POST", + builder, + FU_REDFISH_REQUEST_PERFORM_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to reset manager: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* remove all the devices */ + devices = fu_backend_get_devices(FU_BACKEND(self->backend)); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices, i); + fu_backend_device_removed(FU_BACKEND(self->backend), device_tmp); + } + + /* work around manager bugs... */ + fu_backend_invalidate(FU_BACKEND(self->backend)); + if (fu_redfish_device_get_reset_pre_delay(FU_REDFISH_DEVICE(device)) > 0) { + fu_progress_sleep(fu_progress_get_child(progress), + fu_redfish_device_get_reset_pre_delay(FU_REDFISH_DEVICE(device))); + } + fu_progress_step_done(progress); + + /* read the config file to work out how long to wait */ + restart_timeout_str = fu_plugin_get_config_value(plugin, "ManagerResetTimeout"); + if (restart_timeout_str != NULL) + fu_strtoull(restart_timeout_str, &reset_timeout, 1, 86400, NULL); + if (reset_timeout == 0) { + g_warning("no valid ManagerResetTimeout, falling back to default"); + reset_timeout = 1800; + } + + /* wait for the BMC to come back */ + if (!fu_device_retry_full(device, + fu_redfish_plugin_cleanup_setup_cb, + reset_timeout / FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY, + FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY * 1000, + self, + error)) { + g_prefix_error(error, "manager failed to come back from setup: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* work around manager bugs... */ + if (fu_redfish_device_get_reset_post_delay(FU_REDFISH_DEVICE(device)) > 0) { + fu_progress_sleep( + fu_progress_get_child(progress), + fu_redfish_device_get_reset_post_delay(FU_REDFISH_DEVICE(device))); + } + fu_progress_step_done(progress); + + /* get the new list of devices */ + if (!fu_device_retry_full(device, + fu_redfish_plugin_cleanup_coldplug_cb, + reset_timeout / FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY, + FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY * 1000, + self, + error)) { + g_prefix_error(error, "manager failed to come back from coldplug: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_redfish_plugin_init(FuRedfishPlugin *self) +{ +} + +static void +fu_redfish_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(plugin); + fu_context_add_quirk_key(ctx, "RedfishResetPreDelay"); + fu_context_add_quirk_key(ctx, "RedfishResetPostDelay"); + self->backend = fu_redfish_backend_new(ctx); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_REDFISH_SMBIOS); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_SECURE_CONFIG); +} + +static void +fu_redfish_finalize(GObject *obj) +{ + FuRedfishPlugin *self = FU_REDFISH_PLUGIN(obj); + if (self->smbios != NULL) + g_object_unref(self->smbios); + g_object_unref(self->backend); + G_OBJECT_CLASS(fu_redfish_plugin_parent_class)->finalize(obj); +} + +static void +fu_redfish_plugin_class_init(FuRedfishPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_redfish_plugin_constructed; + object_class->finalize = fu_redfish_finalize; + plugin_class->to_string = fu_redfish_plugin_to_string; + plugin_class->startup = fu_redfish_plugin_startup; + plugin_class->coldplug = fu_redfish_plugin_coldplug; + plugin_class->cleanup = fu_redfish_plugin_cleanup; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-plugin.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..a3ed913ab999554f13df678bbac7ad73e707e9f0 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-plugin.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuRedfishPlugin, fu_redfish_plugin, FU, REDFISH_PLUGIN, FuPlugin) + +void +fu_redfish_plugin_set_credentials(FuPlugin *plugin, const gchar *username, const gchar *password); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-request.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-request.c new file mode 100644 index 0000000000000000000000000000000000000000..fc18c827e3029999ab0081f7e6454bc2b1aa8a65 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-request.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-redfish-request.h" + +struct _FuRedfishRequest { + GObject parent_instance; + CURL *curl; +#ifdef HAVE_LIBCURL_7_62_0 + CURLU *uri; +#else + gchar *uri_base; +#endif + GByteArray *buf; + glong status_code; + JsonParser *json_parser; + JsonObject *json_obj; + GHashTable *cache; /* nullable */ +}; + +G_DEFINE_TYPE(FuRedfishRequest, fu_redfish_request, G_TYPE_OBJECT) + +typedef gchar curlptr; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(curlptr, curl_free) + +JsonObject * +fu_redfish_request_get_json_object(FuRedfishRequest *self) +{ + g_return_val_if_fail(FU_IS_REDFISH_REQUEST(self), NULL); + return self->json_obj; +} + +CURL * +fu_redfish_request_get_curl(FuRedfishRequest *self) +{ + g_return_val_if_fail(FU_IS_REDFISH_REQUEST(self), NULL); + return self->curl; +} + +#ifdef HAVE_LIBCURL_7_62_0 +CURLU * +fu_redfish_request_get_uri(FuRedfishRequest *self) +{ + g_return_val_if_fail(FU_IS_REDFISH_REQUEST(self), NULL); + return self->uri; +} +#else +void +fu_redfish_request_set_uri_base(FuRedfishRequest *self, const gchar *uri_base) +{ + g_return_if_fail(FU_IS_REDFISH_REQUEST(self)); + self->uri_base = g_strdup(uri_base); +} +#endif + +glong +fu_redfish_request_get_status_code(FuRedfishRequest *self) +{ + g_return_val_if_fail(FU_IS_REDFISH_REQUEST(self), G_MAXLONG); + return self->status_code; +} + +static gboolean +fu_redfish_request_load_json(FuRedfishRequest *self, GByteArray *buf, GError **error) +{ + JsonNode *json_root; + + /* load */ + if (buf->data == NULL || buf->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "there was no JSON payload"); + return FALSE; + } + if (!json_parser_load_from_data(self->json_parser, + (const gchar *)buf->data, + (gssize)buf->len, + error)) { + return FALSE; + } + json_root = json_parser_get_root(self->json_parser); + if (json_root == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no JSON root node"); + return FALSE; + } + self->json_obj = json_node_get_object(json_root); + if (self->json_obj == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "no JSON object"); + return FALSE; + } + + /* dump for humans */ + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) { + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(JsonGenerator) json_generator = json_generator_new(); + json_generator_set_pretty(json_generator, TRUE); + json_generator_set_root(json_generator, json_root); + json_generator_to_gstring(json_generator, str); + g_debug("response: %s", str->str); + } + + /* unauthorized */ + if (json_object_has_member(self->json_obj, "error")) { + FwupdError error_code = FWUPD_ERROR_INTERNAL; + JsonObject *json_error; + const gchar *id = NULL; + const gchar *msg = "Unknown failure"; + + /* extended error present */ + json_error = json_object_get_object_member(self->json_obj, "error"); + if (json_object_has_member(json_error, "@Message.ExtendedInfo")) { + JsonArray *json_error_array; + json_error_array = + json_object_get_array_member(json_error, "@Message.ExtendedInfo"); + if (json_array_get_length(json_error_array) > 0) { + JsonObject *json_error2; + json_error2 = json_array_get_object_element(json_error_array, 0); + if (json_object_has_member(json_error2, "MessageId")) + id = + json_object_get_string_member(json_error2, "MessageId"); + if (json_object_has_member(json_error2, "Message")) + msg = json_object_get_string_member(json_error2, "Message"); + } + } else { + if (json_object_has_member(json_error, "code")) + id = json_object_get_string_member(json_error, "code"); + if (json_object_has_member(json_error, "message")) + msg = json_object_get_string_member(json_error, "message"); + } + if (g_strcmp0(id, "Base.1.8.AccessDenied") == 0) + error_code = FWUPD_ERROR_AUTH_FAILED; + else if (g_strcmp0(id, "Base.1.8.PasswordChangeRequired") == 0) + error_code = FWUPD_ERROR_AUTH_EXPIRED; + else if (g_strcmp0(id, "SMC.1.0.OemLicenseNotPassed") == 0) + error_code = FWUPD_ERROR_NOT_SUPPORTED; + else if (g_strcmp0(id, "SMC.1.0.OemFirmwareAlreadyInUpdateMode") == 0) + error_code = FWUPD_ERROR_ALREADY_PENDING; + else if (g_strcmp0(id, "SMC.1.0.OemBiosUpdateIsInProgress") == 0) + error_code = FWUPD_ERROR_ALREADY_PENDING; + g_set_error_literal(error, FWUPD_ERROR, error_code, msg); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_redfish_request_perform(FuRedfishRequest *self, + const gchar *path, + FuRedfishRequestPerformFlags flags, + GError **error) +{ + CURLcode res; +#ifdef HAVE_LIBCURL_7_62_0 + g_autoptr(curlptr) uri_str = NULL; +#else + g_autofree gchar *uri_str = NULL; +#endif + + g_return_val_if_fail(FU_IS_REDFISH_REQUEST(self), FALSE); + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(self->status_code == 0, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* already in cache? */ + if (flags & FU_REDFISH_REQUEST_PERFORM_FLAG_USE_CACHE && self->cache != NULL) { + GByteArray *buf = g_hash_table_lookup(self->cache, path); + if (buf != NULL) { + if (flags & FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON) + return fu_redfish_request_load_json(self, buf, error); + g_byte_array_unref(self->buf); + self->buf = g_byte_array_ref(buf); + return TRUE; + } + } + + /* do request */ +#ifdef HAVE_LIBCURL_7_62_0 + (void)curl_url_set(self->uri, CURLUPART_PATH, path, 0); + (void)curl_url_get(self->uri, CURLUPART_URL, &uri_str, 0); +#else + uri_str = g_strdup_printf("%s%s", self->uri_base, path); + if (curl_easy_setopt(self->curl, CURLOPT_URL, uri_str) != CURLE_OK) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to create message for URI"); + return FALSE; + } +#endif + res = curl_easy_perform(self->curl); + curl_easy_getinfo(self->curl, CURLINFO_RESPONSE_CODE, &self->status_code); + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) { + g_autofree gchar *str = NULL; + str = g_strndup((const gchar *)self->buf->data, self->buf->len); + g_debug("%s: %s [%li]", uri_str, str, self->status_code); + } + + /* check result */ + if (res != CURLE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to request %s: %s", + uri_str, + curl_easy_strerror(res)); + return FALSE; + } + + /* load JSON */ + if (flags & FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON) { + if (!fu_redfish_request_load_json(self, self->buf, error)) { + g_prefix_error(error, "failed to parse %s: ", uri_str); + return FALSE; + } + } + + /* save to cache */ + if (self->cache != NULL) + g_hash_table_insert(self->cache, g_strdup(path), g_byte_array_ref(self->buf)); + + /* success */ + return TRUE; +} + +typedef struct curl_slist _curl_slist; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_curl_slist, curl_slist_free_all) + +gboolean +fu_redfish_request_perform_full(FuRedfishRequest *self, + const gchar *path, + const gchar *request, + JsonBuilder *builder, + FuRedfishRequestPerformFlags flags, + GError **error) +{ + g_autoptr(_curl_slist) hs = NULL; + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(JsonGenerator) json_generator = json_generator_new(); + g_autoptr(JsonNode) json_root = NULL; + + g_return_val_if_fail(FU_IS_REDFISH_REQUEST(self), FALSE); + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(request != NULL, FALSE); + g_return_val_if_fail(builder != NULL, FALSE); + + /* export as a string */ + json_root = json_builder_get_root(builder); + json_generator_set_pretty(json_generator, TRUE); + json_generator_set_root(json_generator, json_root); + json_generator_to_gstring(json_generator, str); + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) + g_debug("request to %s: %s", path, str->str); + + /* patch */ + (void)curl_easy_setopt(self->curl, CURLOPT_CUSTOMREQUEST, request); + (void)curl_easy_setopt(self->curl, CURLOPT_POSTFIELDS, str->str); + (void)curl_easy_setopt(self->curl, CURLOPT_POSTFIELDSIZE, (long)str->len); + hs = curl_slist_append(hs, "Content-Type: application/json"); + (void)curl_easy_setopt(self->curl, CURLOPT_HTTPHEADER, hs); + return fu_redfish_request_perform(self, path, flags, error); +} + +static size_t +fu_redfish_request_write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + GByteArray *buf = (GByteArray *)userdata; + gsize realsize = size * nmemb; + g_byte_array_append(buf, (const guint8 *)ptr, realsize); + return realsize; +} + +void +fu_redfish_request_set_cache(FuRedfishRequest *self, GHashTable *cache) +{ + g_return_if_fail(FU_IS_REDFISH_REQUEST(self)); + g_return_if_fail(cache != NULL); + g_return_if_fail(self->cache == NULL); + self->cache = g_hash_table_ref(cache); +} + +void +fu_redfish_request_set_curlsh(FuRedfishRequest *self, CURLSH *curlsh) +{ + g_return_if_fail(FU_IS_REDFISH_REQUEST(self)); + g_return_if_fail(curlsh != NULL); + (void)curl_easy_setopt(self->curl, CURLOPT_SHARE, curlsh); +} + +static void +fu_redfish_request_init(FuRedfishRequest *self) +{ + self->curl = curl_easy_init(); +#ifdef HAVE_LIBCURL_7_62_0 + self->uri = curl_url(); +#endif + self->buf = g_byte_array_new(); + self->json_parser = json_parser_new(); + (void)curl_easy_setopt(self->curl, CURLOPT_WRITEFUNCTION, fu_redfish_request_write_cb); + (void)curl_easy_setopt(self->curl, CURLOPT_WRITEDATA, self->buf); +} + +static void +fu_redfish_request_finalize(GObject *object) +{ + FuRedfishRequest *self = FU_REDFISH_REQUEST(object); + if (self->cache != NULL) + g_hash_table_unref(self->cache); + g_object_unref(self->json_parser); + g_byte_array_unref(self->buf); + curl_easy_cleanup(self->curl); +#ifdef HAVE_LIBCURL_7_62_0 + curl_url_cleanup(self->uri); +#else + g_free(self->uri_base); +#endif + G_OBJECT_CLASS(fu_redfish_request_parent_class)->finalize(object); +} + +static void +fu_redfish_request_class_init(FuRedfishRequestClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_redfish_request_finalize; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-request.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-request.h new file mode 100644 index 0000000000000000000000000000000000000000..0b8ceb463469843de38aabd83a71bc1bc02588a7 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-request.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +#define FU_TYPE_REDFISH_REQUEST (fu_redfish_request_get_type()) +G_DECLARE_FINAL_TYPE(FuRedfishRequest, fu_redfish_request, FU, REDFISH_REQUEST, GObject) + +typedef enum { + FU_REDFISH_REQUEST_PERFORM_FLAG_NONE = 0, + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON = 1 << 0, + FU_REDFISH_REQUEST_PERFORM_FLAG_USE_CACHE = 1 << 1, +} FuRedfishRequestPerformFlags; + +gboolean +fu_redfish_request_perform(FuRedfishRequest *self, + const gchar *path, + FuRedfishRequestPerformFlags flags, + GError **error); +gboolean +fu_redfish_request_perform_full(FuRedfishRequest *self, + const gchar *path, + const gchar *request, + JsonBuilder *builder, + FuRedfishRequestPerformFlags flags, + GError **error); +JsonObject * +fu_redfish_request_get_json_object(FuRedfishRequest *self); +CURL * +fu_redfish_request_get_curl(FuRedfishRequest *self); +void +fu_redfish_request_set_curlsh(FuRedfishRequest *self, CURLSH *curlsh); +#ifdef HAVE_LIBCURL_7_62_0 +CURLU * +fu_redfish_request_get_uri(FuRedfishRequest *self); +#else +void +fu_redfish_request_set_uri_base(FuRedfishRequest *self, const gchar *uri_base); +#endif +glong +fu_redfish_request_get_status_code(FuRedfishRequest *self); +void +fu_redfish_request_set_cache(FuRedfishRequest *self, GHashTable *cache); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-smbios.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-smbios.c new file mode 100644 index 0000000000000000000000000000000000000000..b9404de90d84c1b5da3082f437521112289c7ae7 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-smbios.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-redfish-common.h" +#include "fu-redfish-smbios.h" + +struct _FuRedfishSmbios { + FuFirmwareClass parent_instance; + guint16 port; + gchar *hostname; + gchar *mac_addr; + gchar *ip_addr; + guint16 vid; + guint16 pid; +}; + +G_DEFINE_TYPE(FuRedfishSmbios, fu_redfish_smbios, FU_TYPE_FIRMWARE) + +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; + /* optional service_hostname goes here */ +} FuRedfishProtocolOverIp; + +guint16 +fu_redfish_smbios_get_port(FuRedfishSmbios *self) +{ + return self->port; +} + +guint16 +fu_redfish_smbios_get_vid(FuRedfishSmbios *self) +{ + return self->vid; +} + +guint16 +fu_redfish_smbios_get_pid(FuRedfishSmbios *self) +{ + return self->pid; +} + +const gchar * +fu_redfish_smbios_get_hostname(FuRedfishSmbios *self) +{ + return self->hostname; +} + +const gchar * +fu_redfish_smbios_get_mac_addr(FuRedfishSmbios *self) +{ + return self->mac_addr; +} + +const gchar * +fu_redfish_smbios_get_ip_addr(FuRedfishSmbios *self) +{ + return self->ip_addr; +} + +static void +fu_redfish_smbios_set_hostname(FuRedfishSmbios *self, const gchar *hostname) +{ + g_free(self->hostname); + self->hostname = g_strdup(hostname); +} + +static void +fu_redfish_smbios_set_mac_addr(FuRedfishSmbios *self, const gchar *mac_addr) +{ + g_free(self->mac_addr); + self->mac_addr = g_strdup(mac_addr); +} + +static void +fu_redfish_smbios_set_ip_addr(FuRedfishSmbios *self, const gchar *ip_addr) +{ + g_free(self->ip_addr); + self->ip_addr = g_strdup(ip_addr); +} + +static void +fu_redfish_smbios_set_port(FuRedfishSmbios *self, guint16 port) +{ + self->port = port; +} + +static void +fu_redfish_smbios_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware); + fu_xmlb_builder_insert_kx(bn, "port", self->port); + fu_xmlb_builder_insert_kv(bn, "hostname", self->hostname); + fu_xmlb_builder_insert_kv(bn, "mac_addr", self->mac_addr); + fu_xmlb_builder_insert_kv(bn, "ip_addr", self->ip_addr); + fu_xmlb_builder_insert_kx(bn, "vid", self->vid); + fu_xmlb_builder_insert_kx(bn, "pid", self->pid); +} + +static gboolean +fu_redfish_smbios_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware); + const gchar *tmp; + guint64 tmpu; + + /* optional properties */ + tmpu = xb_node_query_text_as_uint(n, "port", NULL); + if (tmpu != G_MAXUINT64) + fu_redfish_smbios_set_port(self, (guint16)tmpu); + tmpu = xb_node_query_text_as_uint(n, "vid", NULL); + if (tmpu != G_MAXUINT64) + self->vid = (guint16)tmpu; + tmpu = xb_node_query_text_as_uint(n, "pid", NULL); + if (tmpu != G_MAXUINT64) + self->pid = (guint16)tmpu; + tmp = xb_node_query_text(n, "hostname", NULL); + if (tmp != NULL) + fu_redfish_smbios_set_hostname(self, tmp); + tmp = xb_node_query_text(n, "mac_addr", NULL); + if (tmp != NULL) + fu_redfish_smbios_set_mac_addr(self, tmp); + tmp = xb_node_query_text(n, "ip_addr", NULL); + if (tmp != NULL) + fu_redfish_smbios_set_ip_addr(self, tmp); + + /* success */ + return TRUE; +} + +static gboolean +fu_redfish_smbios_parse_interface_data(FuRedfishSmbios *self, + GBytes *fw, + gsize offset, + GError **error) +{ + gsize bufsz = 0; + gsize offset_mac_addr = G_MAXSIZE; + gsize offset_vid_pid = G_MAXSIZE; + guint8 interface_type = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* parse the data depending on the interface type */ + if (!fu_memread_uint8_safe(buf, bufsz, offset, &interface_type, error)) + return FALSE; + offset++; + switch (interface_type) { + case REDFISH_INTERFACE_TYPE_USB_NETWORK: + case REDFISH_INTERFACE_TYPE_PCI_NETWORK: + offset_vid_pid = 0x00; + break; + case REDFISH_INTERFACE_TYPE_USB_NETWORK_V2: + offset_vid_pid = 0x01; + offset_mac_addr = 0x06; + break; + case REDFISH_INTERFACE_TYPE_PCI_NETWORK_V2: + offset_vid_pid = 0x01; + offset_mac_addr = 0x09; + break; + default: + g_debug("unknown Network Interface"); + break; + } + + /* MAC address */ + if (offset_mac_addr != G_MAXSIZE) { + guint8 mac_addr[6] = {0x0}; + g_autofree gchar *mac_addr_str = NULL; + if (!fu_memcpy_safe(mac_addr, + sizeof(mac_addr), + 0x0, /* dst */ + buf, + bufsz, + offset + offset_mac_addr, /* src */ + sizeof(mac_addr), + error)) + return FALSE; + mac_addr_str = fu_redfish_common_buffer_to_mac(mac_addr); + fu_redfish_smbios_set_mac_addr(self, mac_addr_str); + } + + /* VID:PID */ + if (offset_vid_pid != G_MAXSIZE) { + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + offset_vid_pid, + &self->vid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + offset_vid_pid + 0x02, + &self->pid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_redfish_smbios_parse_over_ip(FuRedfishSmbios *self, GBytes *fw, gsize offset, GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + guint8 hostname_length = 0x0; + guint8 service_ip_address_format = 0x0; + guint16 service_ip_port = 0x0; + guint8 service_ip_address[16] = {0x0}; + + /* port */ + if (!fu_memread_uint16_safe(buf, + bufsz, + offset + + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_ip_port), + &service_ip_port, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_redfish_smbios_set_port(self, service_ip_port); + + /* IP address */ + if (!fu_memcpy_safe( + service_ip_address, + sizeof(service_ip_address), + 0x0, /* dst */ + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_ip_address), /* src */ + sizeof(service_ip_address), + error)) + return FALSE; + if (!fu_memread_uint8_safe( + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_ip_address_format), + &service_ip_address_format, + error)) + return FALSE; + if (service_ip_address_format == REDFISH_IP_ADDRESS_FORMAT_V4) { + g_autofree gchar *tmp = NULL; + tmp = fu_redfish_common_buffer_to_ipv4(service_ip_address); + fu_redfish_smbios_set_ip_addr(self, tmp); + } else if (service_ip_address_format == REDFISH_IP_ADDRESS_FORMAT_V6) { + g_autofree gchar *tmp = NULL; + tmp = fu_redfish_common_buffer_to_ipv6(service_ip_address); + fu_redfish_smbios_set_ip_addr(self, tmp); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "address format is invalid"); + return FALSE; + } + + /* hostname */ + if (!fu_memread_uint8_safe( + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_hostname_len), + &hostname_length, + error)) + return FALSE; + if (hostname_length > 0) { + g_autofree gchar *hostname = g_malloc0(hostname_length + 1); + if (!fu_memcpy_safe((guint8 *)hostname, + hostname_length, + 0x0, /* dst */ + buf, + bufsz, + offset + sizeof(FuRedfishProtocolOverIp), /* src */ + hostname_length, + error)) + return FALSE; + fu_redfish_smbios_set_hostname(self, hostname); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_redfish_smbios_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware); + gsize bufsz = 0; + guint8 protocol_rcds = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* check size */ + if (bufsz < 0x09 + offset) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "SMBIOS entry too small: %" G_GSIZE_FORMAT, + bufsz); + return FALSE; + } + + /* check type */ + if (buf[offset + 0x0] != REDFISH_SMBIOS_TABLE_TYPE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "not Management Controller Host Interface"); + return FALSE; + } + if (buf[offset + 0x1] != bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "size of table 0x%x does not match binary 0x%x", + buf[0x1], + (guint)bufsz); + return FALSE; + } + + /* check interface type */ + if (buf[offset + 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[offset + 0x05] > 0) { + if (!fu_redfish_smbios_parse_interface_data(self, fw, 0x06, error)) + return FALSE; + } + + /* parse protocol records */ + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x06 + buf[0x05], &protocol_rcds, error)) + return FALSE; + offset += 0x07 + buf[0x05]; + g_debug("protocol_rcds: %u", protocol_rcds); + for (guint i = 0; i < protocol_rcds; i++) { + guint8 protocol_id = 0; + guint8 protocol_sz = 0; + if (!fu_memread_uint8_safe(buf, bufsz, offset, &protocol_id, error)) + return FALSE; + if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x1, &protocol_sz, error)) + return FALSE; + if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) { + if (!fu_redfish_smbios_parse_over_ip(self, fw, offset + 0x2, error)) + return FALSE; + } else { + g_debug("ignoring protocol ID 0x%02x", protocol_id); + } + offset += protocol_sz + 1; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_redfish_smbios_write(FuFirmware *firmware, GError **error) +{ + FuRedfishProtocolOverIp proto = {0x0}; + FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware); + gsize hostname_sz = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + if (self->hostname != NULL) + hostname_sz = strlen(self->hostname); + fu_byte_array_append_uint8(buf, REDFISH_SMBIOS_TABLE_TYPE); + fu_byte_array_append_uint8(buf, 0x6D + hostname_sz); /* length */ + fu_byte_array_append_uint16(buf, 0x1234, G_LITTLE_ENDIAN); /* handle */ + fu_byte_array_append_uint8(buf, REDFISH_CONTROLLER_INTERFACE_TYPE_NETWORK_HOST); + fu_byte_array_append_uint8(buf, 0x09); /* iface datalen */ + fu_byte_array_append_uint8(buf, REDFISH_INTERFACE_TYPE_USB_NETWORK); /* iface */ + fu_byte_array_append_uint16(buf, self->vid, G_LITTLE_ENDIAN); /* iface:VID */ + fu_byte_array_append_uint16(buf, self->pid, G_LITTLE_ENDIAN); /* iface:PID */ + fu_byte_array_append_uint8(buf, 0x02); /* iface:serialsz */ + fu_byte_array_append_uint8(buf, 0x03); /* iType */ + fu_byte_array_append_uint8(buf, 'S'); /* iface:serial */ + fu_byte_array_append_uint8(buf, 'n'); /* iface:serial */ + fu_byte_array_append_uint8(buf, 0x1); /* nr protocol rcds */ + + /* protocol record */ + fu_byte_array_append_uint8(buf, REDFISH_PROTOCOL_REDFISH_OVER_IP); + fu_byte_array_append_uint8(buf, sizeof(FuRedfishProtocolOverIp) + hostname_sz); + if (!fu_memwrite_uint16_safe((guint8 *)&proto, + sizeof(proto), + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_ip_port), + self->port, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memwrite_uint8_safe( + (guint8 *)&proto, + sizeof(proto), + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_ip_address_format), + REDFISH_IP_ADDRESS_FORMAT_V4, + error)) + return NULL; + if (!fu_memwrite_uint8_safe( + (guint8 *)&proto, + sizeof(proto), + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_ip_assignment_type), + REDFISH_IP_ASSIGNMENT_TYPE_STATIC, + error)) + return NULL; + if (self->hostname != NULL) + hostname_sz = strlen(self->hostname); + if (hostname_sz > 0) { + if (!fu_memwrite_uint8_safe( + (guint8 *)&proto, + sizeof(proto), + G_STRUCT_OFFSET(FuRedfishProtocolOverIp, service_hostname_len), + hostname_sz, + error)) { + g_prefix_error(error, "cannot write length: "); + return NULL; + } + } + g_byte_array_append(buf, (guint8 *)&proto, sizeof(proto)); + if (hostname_sz > 0) + g_byte_array_append(buf, (guint8 *)self->hostname, hostname_sz); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_redfish_smbios_finalize(GObject *object) +{ + FuRedfishSmbios *self = FU_REDFISH_SMBIOS(object); + g_free(self->hostname); + g_free(self->mac_addr); + g_free(self->ip_addr); + G_OBJECT_CLASS(fu_redfish_smbios_parent_class)->finalize(object); +} + +static void +fu_redfish_smbios_init(FuRedfishSmbios *self) +{ +} + +static void +fu_redfish_smbios_class_init(FuRedfishSmbiosClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_redfish_smbios_finalize; + klass_firmware->parse = fu_redfish_smbios_parse; + klass_firmware->write = fu_redfish_smbios_write; + klass_firmware->build = fu_redfish_smbios_build; + klass_firmware->export = fu_redfish_smbios_export; +} + +FuRedfishSmbios * +fu_redfish_smbios_new(void) +{ + return FU_REDFISH_SMBIOS(g_object_new(FU_TYPE_REDFISH_SMBIOS, NULL)); +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-smbios.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-smbios.h new file mode 100644 index 0000000000000000000000000000000000000000..d77e7b8ed781e2fa6135da2487410c7ebc0ea854 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-smbios.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_REDFISH_SMBIOS (fu_redfish_smbios_get_type()) +G_DECLARE_FINAL_TYPE(FuRedfishSmbios, fu_redfish_smbios, FU, REDFISH_SMBIOS, FuFirmware) + +FuRedfishSmbios * +fu_redfish_smbios_new(void); + +guint16 +fu_redfish_smbios_get_port(FuRedfishSmbios *self); +guint16 +fu_redfish_smbios_get_vid(FuRedfishSmbios *self); +guint16 +fu_redfish_smbios_get_pid(FuRedfishSmbios *self); +const gchar * +fu_redfish_smbios_get_hostname(FuRedfishSmbios *self); +const gchar * +fu_redfish_smbios_get_mac_addr(FuRedfishSmbios *self); +const gchar * +fu_redfish_smbios_get_ip_addr(FuRedfishSmbios *self); diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-smc-device.c b/fwupd-1.8.6/plugins/redfish/fu-redfish-smc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..2626ec892274da0a7549b33f6c14e437ec9578c4 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-smc-device.c @@ -0,0 +1,242 @@ +/* + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-redfish-backend.h" +#include "fu-redfish-common.h" +#include "fu-redfish-smc-device.h" + +struct _FuRedfishSmcDevice { + FuRedfishDevice parent_instance; +}; + +G_DEFINE_TYPE(FuRedfishSmcDevice, fu_redfish_smc_device, FU_TYPE_REDFISH_DEVICE) + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(curl_mime, curl_mime_free) + +static const gchar * +fu_redfish_smc_device_get_task(JsonObject *json_obj) +{ + JsonObject *tmp_obj; + JsonArray *tmp_ary; + const gchar *msgid; + + if (!json_object_has_member(json_obj, "Accepted")) + return NULL; + tmp_obj = json_object_get_object_member(json_obj, "Accepted"); + if (tmp_obj == NULL || !json_object_has_member(tmp_obj, "@Message.ExtendedInfo")) + return NULL; + tmp_ary = json_object_get_array_member(tmp_obj, "@Message.ExtendedInfo"); + if (tmp_ary == NULL || json_array_get_length(tmp_ary) != 1) + return NULL; + tmp_obj = json_array_get_object_element(tmp_ary, 0); + if (tmp_obj == NULL) + return NULL; + msgid = json_object_get_string_member(tmp_obj, "MessageId"); + if (g_strcmp0(msgid, "SMC.1.0.OemSimpleupdateAcceptedMessage") != 0) + return NULL; + tmp_ary = json_object_get_array_member(tmp_obj, "MessageArgs"); + if (tmp_ary == NULL) + return NULL; + if (json_array_get_length(tmp_ary) != 1) + return NULL; + return json_array_get_string_element(tmp_ary, 0); +} + +static GString * +fu_redfish_smc_device_get_parameters(FuRedfishSmcDevice *self) +{ + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(JsonGenerator) json_generator = json_generator_new(); + g_autoptr(JsonNode) json_root = NULL; + + /* create header */ + /* https://supermicro.com/manuals/other/RedishRefGuide.pdf */ + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Targets"); + json_builder_begin_array(builder); + json_builder_add_string_value(builder, "/redfish/v1/Systems/1/Bios"); + json_builder_end_array(builder); + json_builder_set_member_name(builder, "@Redfish.OperationApplyTime"); + json_builder_add_string_value(builder, "OnStartUpdateRequest"); + json_builder_set_member_name(builder, "Oem"); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Supermicro"); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "BIOS"); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "PreserveME"); + json_builder_add_boolean_value(builder, TRUE); + json_builder_set_member_name(builder, "PreserveNVRAM"); + json_builder_add_boolean_value(builder, TRUE); + json_builder_set_member_name(builder, "PreserveSMBIOS"); + json_builder_add_boolean_value(builder, TRUE); + json_builder_set_member_name(builder, "BackupBIOS"); + json_builder_add_boolean_value(builder, FALSE); + json_builder_end_object(builder); + json_builder_end_object(builder); + json_builder_end_object(builder); + json_builder_end_object(builder); + + /* export as a string */ + json_root = json_builder_get_root(builder); + json_generator_set_pretty(json_generator, TRUE); + json_generator_set_root(json_generator, json_root); + json_generator_to_gstring(json_generator, str); + + return g_steal_pointer(&str); +} + +static gboolean +fu_redfish_smc_device_start_update(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(device)); + JsonObject *json_obj; + CURL *curl; + const gchar *location = NULL; + g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + + curl = fu_redfish_request_get_curl(request); + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + + if (!fu_redfish_request_perform( + request, + "/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate", + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) { + if (g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + } + return FALSE; + } + json_obj = fu_redfish_request_get_json_object(request); + + location = fu_redfish_smc_device_get_task(json_obj); + if (location == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no task returned for %s", + fu_redfish_backend_get_push_uri_path(backend)); + return FALSE; + } + return fu_redfish_device_poll_task(FU_REDFISH_DEVICE(device), location, progress, error); +} + +static gboolean +fu_redfish_smc_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRedfishSmcDevice *self = FU_REDFISH_SMC_DEVICE(device); + FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + CURL *curl; + JsonObject *json_obj; + curl_mimepart *part; + const gchar *location = NULL; + gboolean ret; + g_autoptr(curl_mime) mime = NULL; + g_autoptr(FuRedfishRequest) request = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GString) params = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 50, "apply"); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* create the multipart for uploading the image request */ + request = fu_redfish_backend_request_new(backend); + curl = fu_redfish_request_get_curl(request); + mime = curl_mime_init(curl); + (void)curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + + params = fu_redfish_smc_device_get_parameters(self); + part = curl_mime_addpart(mime); + curl_mime_name(part, "UpdateParameters"); + (void)curl_mime_type(part, "application/json"); + (void)curl_mime_data(part, params->str, CURL_ZERO_TERMINATED); + if (g_getenv("FWUPD_REDFISH_VERBOSE") != NULL) + g_debug("request: %s", params->str); + + part = curl_mime_addpart(mime); + curl_mime_name(part, "UpdateFile"); + (void)curl_mime_type(part, "application/octet-stream"); + (void)curl_mime_filedata(part, "firmware.bin"); + (void)curl_mime_data(part, g_bytes_get_data(fw, NULL), g_bytes_get_size(fw)); + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_redfish_request_perform(request, + fu_redfish_backend_get_push_uri_path(backend), + FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, + error)) { + if (g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_ALREADY_PENDING)) { + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + } + return FALSE; + } + if (fu_redfish_request_get_status_code(request) != 202) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to upload: %li", + fu_redfish_request_get_status_code(request)); + return FALSE; + } + json_obj = fu_redfish_request_get_json_object(request); + + /* poll the verify task for progress */ + location = fu_redfish_smc_device_get_task(json_obj); + if (location == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no task returned for %s", + fu_redfish_backend_get_push_uri_path(backend)); + return FALSE; + } + + if (!fu_redfish_device_poll_task(FU_REDFISH_DEVICE(self), + location, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + ret = fu_redfish_smc_device_start_update(device, fu_progress_get_child(progress), error); + fu_progress_step_done(progress); + return ret; +} + +static void +fu_redfish_smc_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_redfish_smc_device_init(FuRedfishSmcDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "Redfish Supermicro device"); +} + +static void +fu_redfish_smc_device_class_init(FuRedfishSmcDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_redfish_smc_device_write_firmware; + klass_device->set_progress = fu_redfish_smc_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/redfish/fu-redfish-smc-device.h b/fwupd-1.8.6/plugins/redfish/fu-redfish-smc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..7c0b337ac0b5dda36923a9dfb70fc6ce67b438f0 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-redfish-smc-device.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-redfish-backend.h" +#include "fu-redfish-device.h" + +#define FU_TYPE_REDFISH_SMC_DEVICE (fu_redfish_smc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuRedfishSmcDevice, + fu_redfish_smc_device, + FU, + REDFISH_SMC_DEVICE, + FuRedfishDevice) + +struct _FuRedfishSmcDeviceClass { + FuRedfishDeviceClass parent_class; +}; diff --git a/fwupd-1.8.6/plugins/redfish/fu-self-test.c b/fwupd-1.8.6/plugins/redfish/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..d793c4d2cdbe358484955ff667f67f5378751dfb --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/fu-self-test.c @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-context-private.h" +#include "fu-device-private.h" +#ifdef HAVE_LINUX_IPMI_H +#include "fu-ipmi-device.h" +#endif +#include "fu-plugin-private.h" +#include "fu-redfish-common.h" +#include "fu-redfish-network.h" +#include "fu-redfish-plugin.h" +#include "fu-redfish-smc-device.h" + +typedef struct { + FuPlugin *plugin; + FuPlugin *smc_plugin; + FuPlugin *unlicensed_plugin; +} FuTest; + +static void +fu_test_self_init(FuTest *self) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, + FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* generic BMC */ + self->plugin = fu_plugin_new_from_gtype(fu_redfish_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->plugin, progress, &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) { + g_test_skip("no redfish.py running"); + g_clear_error(&error); + } else { + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_coldplug(self->plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + } + + /* supermicro BMC */ + self->smc_plugin = fu_plugin_new_from_gtype(fu_redfish_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->smc_plugin, progress, &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) { + g_test_skip("no redfish.py running"); + g_clear_error(&error); + } else { + g_assert_no_error(error); + g_assert_true(ret); + fu_redfish_plugin_set_credentials(self->smc_plugin, "smc_username", "password2"); + ret = fu_plugin_runner_coldplug(self->smc_plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + } + + /* unlicensed supermicro BMC */ + self->unlicensed_plugin = fu_plugin_new_from_gtype(fu_redfish_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(self->unlicensed_plugin, progress, &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) { + g_test_skip("no redfish.py running"); + } else { + g_assert_no_error(error); + g_assert_true(ret); + fu_redfish_plugin_set_credentials(self->unlicensed_plugin, + "unlicensed_username", + "password2"); + ret = fu_plugin_runner_coldplug(self->unlicensed_plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + } +} + +static void +fu_test_redfish_ipmi_func(void) +{ +#ifdef HAVE_LINUX_IPMI_H + gboolean ret; + g_autoptr(FuIpmiDevice) device = fu_ipmi_device_new(NULL); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *username = NULL; + g_autofree gchar *str = NULL; + + /* sanity check */ + if (!g_file_test("/dev/ipmi0", G_FILE_TEST_EXISTS)) { + g_test_skip("no IPMI hardware"); + return; + } + + /* create device */ + locker = fu_device_locker_new(device, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) { + g_test_skip("permission denied for access to IPMI hardware"); + return; + } + g_assert_no_error(error); + g_assert_nonnull(locker); + str = fu_device_to_string(FU_DEVICE(device)); + g_debug("%s", str); + + /* add user that can do redfish commands */ + if (g_getenv("FWUPD_REDFISH_SELF_TEST") == NULL) { + g_test_skip("not doing destructive tests"); + return; + } + ret = fu_ipmi_device_set_user_name(device, 0x04, "fwupd", &error); + g_assert_no_error(error); + g_assert_true(ret); + username = fu_ipmi_device_get_user_password(device, 0x04, &error); + g_assert_no_error(error); + g_assert_nonnull(username); + g_debug("username=%s", username); + ret = fu_ipmi_device_set_user_enable(device, 0x04, TRUE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_ipmi_device_set_user_priv(device, 0x04, 0x4, 1, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_ipmi_device_set_user_password(device, 0x04, "Passw0rd123", &error); + g_assert_no_error(error); + g_assert_true(ret); +#else + g_test_skip("no linux/ipmi.h, so skipping"); +#endif +} + +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; + g_autofree gchar *maca = 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"); + maca = fu_redfish_common_buffer_to_mac(buf); + g_assert_cmpstr(maca, ==, "00:01:02:03:04:05"); +} + +static void +fu_test_redfish_common_version_func(void) +{ + struct { + const gchar *in; + const gchar *op; + } strs[] = {{"1.2.3", "1.2.3"}, + {"P50 v1.2.3 PROD", "1.2.3"}, + {"P50 1.2.3 DEV", "1.2.3"}, + {NULL, NULL}}; + for (guint i = 0; strs[i].in != NULL; i++) { + g_autofree gchar *tmp = fu_redfish_common_fix_version(strs[i].in); + g_assert_cmpstr(tmp, ==, strs[i].op); + } +} + +static void +fu_test_redfish_common_lenovo_func(void) +{ + struct { + const gchar *in; + gboolean ret; + const gchar *build; + const gchar *version; + } values[] = {{"11A-1.02", TRUE, "11A", "1.02"}, + {"11A-0.00", TRUE, "11A", "0.00"}, + {"99Z-9.99", TRUE, "99Z", "9.99"}, + {"9-9-9.99", FALSE, NULL, NULL}, + {"999-9.99", FALSE, NULL, NULL}, + {"ACB-9.99", FALSE, NULL, NULL}, + {NULL, FALSE, NULL, NULL}}; + for (guint i = 0; values[i].in != NULL; i++) { + gboolean ret; + g_autofree gchar *build = NULL; + g_autofree gchar *version = NULL; + ret = fu_redfish_common_parse_version_lenovo(values[i].in, &build, &version, NULL); + g_assert_cmpint(ret, ==, values[i].ret); + g_assert_cmpstr(build, ==, values[i].build); + g_assert_cmpstr(version, ==, values[i].version); + } +} + +static void +fu_test_redfish_network_mac_addr_func(void) +{ + FuRedfishNetworkDeviceState state = FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN; + gboolean ret; + g_autofree gchar *ip_addr = NULL; + g_autoptr(FuRedfishNetworkDevice) device = NULL; + g_autoptr(GError) error = NULL; + + device = fu_redfish_network_device_for_mac_addr("00:13:F7:29:C2:D8", &error); + if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_test_skip("no hardware"); + return; + } + if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_autofree gchar *str = g_strdup_printf("not supported: %s", error->message); + g_test_skip(str); + return; + } + g_assert_no_error(error); + g_assert_nonnull(device); + ret = fu_redfish_network_device_get_state(device, &state, &error); + g_assert_no_error(error); + g_assert_true(ret); + if (state == FU_REDFISH_NETWORK_DEVICE_STATE_DISCONNECTED) { + ret = fu_redfish_network_device_connect(device, &error); + g_assert_no_error(error); + g_assert_true(ret); + } + ip_addr = fu_redfish_network_device_get_address(device, &error); + g_assert_no_error(error); + g_assert_nonnull(ip_addr); +} + +static void +fu_test_redfish_network_vid_pid_func(void) +{ + g_autofree gchar *ip_addr = NULL; + g_autoptr(FuRedfishNetworkDevice) device = NULL; + g_autoptr(GError) error = NULL; + + device = fu_redfish_network_device_for_vid_pid(0x0707, 0x0201, &error); + if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_test_skip("no hardware"); + return; + } + if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_autofree gchar *str = g_strdup_printf("not supported: %s", error->message); + g_test_skip(str); + return; + } + g_assert_no_error(error); + g_assert_nonnull(device); + ip_addr = fu_redfish_network_device_get_address(device, &error); + g_assert_no_error(error); + g_assert_nonnull(ip_addr); +} + +static void +fu_test_redfish_devices_func(gconstpointer user_data) +{ + FuDevice *dev; + FuTest *self = (FuTest *)user_data; + GPtrArray *devices; + g_autofree gchar *devstr0 = NULL; + g_autofree gchar *devstr1 = NULL; + + devices = fu_plugin_get_devices(self->plugin); + g_assert_nonnull(devices); + if (devices->len == 0) { + g_test_skip("no redfish support"); + return; + } + g_assert_cmpint(devices->len, ==, 2); + + /* BMC */ + dev = g_ptr_array_index(devices, 1); + devstr0 = fu_device_to_string(dev); + g_debug("%s", devstr0); + g_assert_cmpstr(fu_device_get_id(dev), ==, "62c1cd95692c5225826cf8568a460427ea3b1827"); + g_assert_cmpstr(fu_device_get_name(dev), ==, "BMC Firmware"); + g_assert_cmpstr(fu_device_get_vendor(dev), ==, "Lenovo"); + g_assert_cmpstr(fu_device_get_version(dev), ==, "1.02"); + g_assert_cmpstr(fu_device_get_version_lowest(dev), ==, "0.12"); + g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR); + g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish")); + g_assert_true( + fu_device_has_guid(dev, "REDFISH\\VENDOR_Lenovo&SOFTWAREID_UEFI-AFE1-6&TYPE_UNSIGNED")); + g_assert_true(fu_device_has_vendor_id(dev, "REDFISH:LENOVO")); + + /* BIOS */ + dev = g_ptr_array_index(devices, 0); + devstr1 = fu_device_to_string(dev); + g_debug("%s", devstr1); + g_assert_cmpstr(fu_device_get_id(dev), ==, "562313e34c756a05a2e878861377765582bbf971"); + g_assert_cmpstr(fu_device_get_name(dev), ==, "BIOS Firmware"); + g_assert_cmpstr(fu_device_get_vendor(dev), ==, "Contoso"); + g_assert_cmpstr(fu_device_get_version(dev), ==, "1.45"); + g_assert_cmpstr(fu_device_get_serial(dev), ==, "12345"); + g_assert_cmpstr(fu_device_get_version_lowest(dev), ==, "1.10"); + g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR); + g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)); + g_assert_true(fu_device_has_icon(dev, "network-wired")); + g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish")); + g_assert_true(fu_device_has_guid(dev, "fee82a67-6ce2-4625-9f44-237ad2402c28")); + g_assert_true(fu_device_has_guid(dev, "a6d3294e-37e5-50aa-ae2f-c0c457af16f3")); + g_assert_true(fu_device_has_vendor_id(dev, "REDFISH:CONTOSO")); +} + +static void +fu_test_redfish_unlicensed_devices_func(gconstpointer user_data) +{ + FuDevice *dev; + FuTest *self = (FuTest *)user_data; + GPtrArray *devices; + + devices = fu_plugin_get_devices(self->unlicensed_plugin); + g_assert_nonnull(devices); + if (devices->len == 0) { + g_test_skip("no redfish support"); + return; + } + g_assert_cmpint(devices->len, ==, 2); + + dev = g_ptr_array_index(devices, 0); + g_assert_true(FU_IS_REDFISH_SMC_DEVICE(dev)); + g_assert_true(fu_device_has_inhibit( + dev, + fwupd_device_problem_to_string(FWUPD_DEVICE_PROBLEM_MISSING_LICENSE))); + + dev = g_ptr_array_index(devices, 1); + g_assert_true(FU_IS_REDFISH_SMC_DEVICE(dev)); + g_assert_true(fu_device_has_inhibit( + dev, + fwupd_device_problem_to_string(FWUPD_DEVICE_PROBLEM_MISSING_LICENSE))); +} + +static void +fu_test_redfish_smc_devices_func(gconstpointer user_data) +{ + FuDevice *dev; + FuTest *self = (FuTest *)user_data; + GPtrArray *devices; + + devices = fu_plugin_get_devices(self->smc_plugin); + g_assert_nonnull(devices); + if (devices->len == 0) { + g_test_skip("no redfish support"); + return; + } + g_assert_cmpint(devices->len, ==, 2); + + dev = g_ptr_array_index(devices, 0); + g_assert_true(FU_IS_REDFISH_SMC_DEVICE(dev)); + + dev = g_ptr_array_index(devices, 1); + g_assert_true(FU_IS_REDFISH_SMC_DEVICE(dev)); +} + +static void +fu_test_redfish_update_func(gconstpointer user_data) +{ + FuDevice *dev; + FuTest *self = (FuTest *)user_data; + GPtrArray *devices; + gboolean ret; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob_fw = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + devices = fu_plugin_get_devices(self->plugin); + g_assert_nonnull(devices); + if (devices->len == 0) { + g_test_skip("no redfish support"); + return; + } + g_assert_cmpint(devices->len, ==, 2); + + /* BMC */ + dev = g_ptr_array_index(devices, 1); + blob_fw = g_bytes_new_static("hello", 5); + ret = fu_plugin_runner_write_firmware(self->plugin, + dev, + blob_fw, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); + + /* try again */ + ret = fu_plugin_runner_write_firmware(self->plugin, + dev, + blob_fw, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE); + g_assert_false(ret); +} + +static void +fu_test_redfish_smc_update_func(gconstpointer user_data) +{ + FuDevice *dev; + FuTest *self = (FuTest *)user_data; + GPtrArray *devices; + gboolean ret; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) blob_fw = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + devices = fu_plugin_get_devices(self->smc_plugin); + g_assert_nonnull(devices); + if (devices->len == 0) { + g_test_skip("no redfish support"); + return; + } + g_assert_cmpint(devices->len, ==, 2); + + /* BMC */ + dev = g_ptr_array_index(devices, 1); + blob_fw = g_bytes_new_static("hello", 5); + ret = fu_plugin_runner_write_firmware(self->plugin, + dev, + blob_fw, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* stuck update */ + blob_fw = g_bytes_new_static("stuck", 5); + ret = fu_plugin_runner_write_firmware(self->plugin, + dev, + blob_fw, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &error); + g_assert_false(ret); + g_assert_true(fu_device_has_inhibit( + dev, + fwupd_device_problem_to_string(FWUPD_DEVICE_PROBLEM_UPDATE_PENDING))); +} + +static void +fu_test_self_free(FuTest *self) +{ + if (self->plugin != NULL) + g_object_unref(self->plugin); + g_free(self); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_self_free) +#pragma clang diagnostic pop + +int +main(int argc, char **argv) +{ + g_autoptr(FuTest) self = g_new0(FuTest, 1); + g_autofree gchar *smbios_data_fn = NULL; + g_autofree gchar *testdatadir = NULL; + + g_test_init(&argc, &argv, NULL); + + (void)g_setenv("FWUPD_REDFISH_VERBOSE", "1", TRUE); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + smbios_data_fn = g_build_filename(testdatadir, "redfish-smbios.bin", NULL); + (void)g_setenv("FWUPD_REDFISH_SMBIOS_DATA", smbios_data_fn, TRUE); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + (void)g_setenv("CONFIGURATION_DIRECTORY", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + fu_test_self_init(self); + g_test_add_func("/redfish/ipmi", fu_test_redfish_ipmi_func); + g_test_add_func("/redfish/common", fu_test_redfish_common_func); + g_test_add_func("/redfish/common{version}", fu_test_redfish_common_version_func); + g_test_add_func("/redfish/common{lenovo}", fu_test_redfish_common_lenovo_func); + g_test_add_func("/redfish/network{mac_addr}", fu_test_redfish_network_mac_addr_func); + g_test_add_func("/redfish/network{vid_pid}", fu_test_redfish_network_vid_pid_func); + g_test_add_data_func("/redfish/unlicensed_plugin{devices}", + self, + fu_test_redfish_unlicensed_devices_func); + g_test_add_data_func("/redfish/smc_plugin{devices}", + self, + fu_test_redfish_smc_devices_func); + g_test_add_data_func("/redfish/smc_plugin{update}", self, fu_test_redfish_smc_update_func); + g_test_add_data_func("/redfish/plugin{devices}", self, fu_test_redfish_devices_func); + g_test_add_data_func("/redfish/plugin{update}", self, fu_test_redfish_update_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/redfish/meson.build b/fwupd-1.8.6/plugins/redfish/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1e35b6ce97b200dbcaa243beade1441e7037aee1 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/meson.build @@ -0,0 +1,77 @@ +if get_option('plugin_redfish').require(libcurl.found(), + error_message: 'curl is needed for plugin_redfish').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginRedfish"'] + +plugin_quirks += files('redfish.quirk') + +ipmi_src = [] +if have_linux_ipmi + ipmi_src += 'fu-ipmi-device.c' +endif + +plugin_builtin_redfish = static_library('fu_plugin_redfish', + sources: [ + 'fu-redfish-plugin.c', + 'fu-redfish-backend.c', + 'fu-redfish-common.c', # fuzzing + 'fu-redfish-device.c', + 'fu-redfish-smc-device.c', + 'fu-redfish-legacy-device.c', + 'fu-redfish-multipart-device.c', + 'fu-redfish-network.c', + 'fu-redfish-network-device.c', + 'fu-redfish-request.c', + 'fu-redfish-smbios.c', # fuzzing + ipmi_src, + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + libcurl, + ], +) +plugin_builtins += plugin_builtin_redfish + +install_data(['redfish.conf'], + install_dir: join_paths(sysconfdir, 'fwupd'), + install_mode: 'rw-r-----', +) + +if get_option('tests') + install_data(['tests/redfish-smbios.bin'], + install_dir: join_paths(installed_test_datadir, 'tests')) + install_data(['tests/redfish.conf'], + install_dir: join_paths(installed_test_datadir, 'tests')) + install_data(['tests/efi/efivars/RedfishIndications-16faa37e-4b6a-4891-9028-242de65a3b70'], + install_dir: join_paths(installed_test_datadir, 'tests', 'efi', 'efivars')) + install_data(['tests/efi/efivars/RedfishOSCredentials-16faa37e-4b6a-4891-9028-242de65a3b70'], + install_dir: join_paths(installed_test_datadir, 'tests', 'efi', 'efivars')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + e = executable( + 'redfish-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: [ + plugin_deps, + libcurl, + ], + link_with: [ + fwupd, + fwupdplugin, + plugin_builtin_redfish, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('redfish-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/redfish/redfish.conf b/fwupd-1.8.6/plugins/redfish/redfish.conf new file mode 100644 index 0000000000000000000000000000000000000000..d47bdf0dd22a66feb21248bfd41e348052b0a2b5 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/redfish.conf @@ -0,0 +1,20 @@ +[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: FALSE +#CACheck= + +# Do not use IPMI KCS to create an initial user account if no SMBIOS data +IpmiDisableCreateUser=False + +# Amount of time in seconds to wait for a BMC restart +ManagerResetTimeout=1800 diff --git a/fwupd-1.8.6/plugins/redfish/redfish.quirk b/fwupd-1.8.6/plugins/redfish/redfish.quirk new file mode 100644 index 0000000000000000000000000000000000000000..3f2790c9d5e64efed897432ed8a61e6799642d94 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/redfish.quirk @@ -0,0 +1,24 @@ +# Lenovo ThinkSystem +[42f00735-c9ab-5374-bd63-a5deee5881e0] +Flags = wildcard-targets,reset-required,ipmi-create-user + +[REDFISH\VENDOR_Lenovo&ID_BMC-Backup] +ParentGuid = REDFISH\VENDOR_Lenovo&ID_BMC-Primary +Flags = dual-image,is-backup,no-probe + +[REDFISH\VENDOR_Lenovo&ID_BMC-Primary] +Flags = dual-image,manager-reset +RedfishResetPreDelay = 10000 +RedfishResetPostDelay = 40000 + +[REDFISH\VENDOR_Lenovo&ID_LXPM] +Flags = ~updatable +[REDFISH\VENDOR_Lenovo&ID_LXPMLinuxDriver] +Flags = ~updatable +ParentGuid = REDFISH\VENDOR_Lenovo&ID_LXPM +[REDFISH\VENDOR_Lenovo&ID_LXPMWindowsDriver] +Flags = ~updatable +ParentGuid = REDFISH\VENDOR_Lenovo&ID_LXPM + +[REDFISH\VENDOR_SMCI&ID_Backup_BIOS] +Flags = is-backup,no-probe diff --git a/fwupd-1.8.6/plugins/redfish/tests/daemon.conf b/fwupd-1.8.6/plugins/redfish/tests/daemon.conf new file mode 120000 index 0000000000000000000000000000000000000000..e04a687c99b37b0eaebe7130ec8bfe1b6653b75b --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/tests/daemon.conf @@ -0,0 +1 @@ +../../../data/daemon.conf \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/redfish/tests/efi/efivars/RedfishIndications-16faa37e-4b6a-4891-9028-242de65a3b70 b/fwupd-1.8.6/plugins/redfish/tests/efi/efivars/RedfishIndications-16faa37e-4b6a-4891-9028-242de65a3b70 new file mode 100644 index 0000000000000000000000000000000000000000..ecaec12339c4d61e3f4fc22b10078b213b32ad52 Binary files /dev/null and b/fwupd-1.8.6/plugins/redfish/tests/efi/efivars/RedfishIndications-16faa37e-4b6a-4891-9028-242de65a3b70 differ diff --git a/fwupd-1.8.6/plugins/redfish/tests/efi/efivars/RedfishOSCredentials-16faa37e-4b6a-4891-9028-242de65a3b70 b/fwupd-1.8.6/plugins/redfish/tests/efi/efivars/RedfishOSCredentials-16faa37e-4b6a-4891-9028-242de65a3b70 new file mode 100644 index 0000000000000000000000000000000000000000..62aa17caed59235b1045fcd1084829e2ca05a155 Binary files /dev/null and b/fwupd-1.8.6/plugins/redfish/tests/efi/efivars/RedfishOSCredentials-16faa37e-4b6a-4891-9028-242de65a3b70 differ diff --git a/fwupd-1.8.6/plugins/redfish/tests/redfish-smbios.bin b/fwupd-1.8.6/plugins/redfish/tests/redfish-smbios.bin new file mode 100644 index 0000000000000000000000000000000000000000..048ab6c5740e8ee2dd1f4db784fc7cfd23dfc963 Binary files /dev/null and b/fwupd-1.8.6/plugins/redfish/tests/redfish-smbios.bin differ diff --git a/fwupd-1.8.6/plugins/redfish/tests/redfish-smbios.builder.xml b/fwupd-1.8.6/plugins/redfish/tests/redfish-smbios.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..6368a0395670851d4dc61b9c5faf1ad68dac8307 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/tests/redfish-smbios.builder.xml @@ -0,0 +1,7 @@ + + 0x1234 + localhost + 0.0.0.0 + 0x9876 + 0x5678 + diff --git a/fwupd-1.8.6/plugins/redfish/tests/redfish.conf b/fwupd-1.8.6/plugins/redfish/tests/redfish.conf new file mode 100644 index 0000000000000000000000000000000000000000..1675d33d32d9a91bb9f9918256cb24f9c752c612 --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/tests/redfish.conf @@ -0,0 +1,4 @@ +[redfish] +Uri=http://localhost:4661 +Username=username2 +Password=password2 diff --git a/fwupd-1.8.6/plugins/redfish/tests/redfish.py b/fwupd-1.8.6/plugins/redfish/tests/redfish.py new file mode 100755 index 0000000000000000000000000000000000000000..30d662775bb62ce5b66df106cb7f182cf79890ca --- /dev/null +++ b/fwupd-1.8.6/plugins/redfish/tests/redfish.py @@ -0,0 +1,472 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2021 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ + +import json + +from flask import Flask, Response, request + +app = Flask(__name__) + +HARDCODED_SMC_USERNAME = "smc_username" +HARDCODED_UNL_USERNAME = "unlicensed_username" +HARDCODED_USERNAMES = {"username2", HARDCODED_SMC_USERNAME, HARDCODED_UNL_USERNAME} +HARDCODED_PASSWORD = "password2" + +app._percentage545: int = 0 +app._percentage546: int = 0 + + +def _failure(msg: str, status=400): + res = { + "error": {"message": msg}, + } + return Response(response=json.dumps(res), status=401, mimetype="application/json") + + +@app.route("/redfish/v1/") +def index(): + + # reset counter + app._percentage545 = 0 + app._percentage546 = 0 + + # check password from the config file + try: + if ( + not request.authorization["username"] in HARDCODED_USERNAMES + or request.authorization["password"] != HARDCODED_PASSWORD + ): + return _failure("unauthorised", status=401) + except (KeyError, TypeError): + return _failure("invalid") + + res = { + "@odata.id": "/redfish/v1/", + "RedfishVersion": "1.6.0", + "UUID": "92384634-2938-2342-8820-489239905423", + "UpdateService": {"@odata.id": "/redfish/v1/UpdateService"}, + } + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/UpdateService") +def update_service(): + + res = { + "@odata.id": "/redfish/v1/UpdateService", + "@odata.type": "#UpdateService.v1_8_0.UpdateService", + "FirmwareInventory": { + "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory" + }, + "HttpPushUri": "/FWUpdate", + "HttpPushUriOptions": { + "HttpPushUriApplyTime": { + "ApplyTime": "Immediate", + } + }, + "HttpPushUriOptionsBusy": False, + "ServiceEnabled": True, + } + + if request.authorization["username"] == HARDCODED_UNL_USERNAME: + res["MultipartHttpPushUri"] = "/FWUpdate-unlicensed" + res["Actions"] = { + "#UpdateService.StartUpdate": { + "target": "/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate" + } + } + elif request.authorization["username"] == HARDCODED_SMC_USERNAME: + res["MultipartHttpPushUri"] = "/FWUpdate-smc" + res["Actions"] = { + "#UpdateService.StartUpdate": { + "target": "/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate" + } + } + else: + res["MultipartHttpPushUri"] = "/FWUpdate" + + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/UpdateService/FirmwareInventory") +def firmware_inventory(): + + res = { + "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory", + "@odata.type": "#SoftwareInventoryCollection.SoftwareInventoryCollection", + "Members": [ + {"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/BMC"}, + {"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/BIOS"}, + ], + "Members@odata.count": 2, + } + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/UpdateService/FirmwareInventory/BMC") +def firmware_inventory_bmc(): + + res = { + "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/BMC", + "@odata.type": "#SoftwareInventory.v1_2_3.SoftwareInventory", + "Id": "BMC", + "LowestSupportedVersion": "11A-0.12", + "Name": "Lenovo BMC Firmware", + "RelatedItem": [{"@odata.id": "/redfish/v1/Managers/BMC"}], + "SoftwareId": "UEFI-AFE1-6", + "UefiDevicePaths": ["BMC(0x1,0x0ABCDEF)"], + "Updateable": True, + "Version": "11A-1.02", + "ReleaseDate": "2019-03-15T00:00:00", + } + if request.authorization["username"] in { + HARDCODED_UNL_USERNAME, + HARDCODED_SMC_USERNAME, + }: + res["Manufacturer"] = "SMCI" + else: + res["Manufacturer"] = "Lenovo" + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/Managers/BMC") +def redfish_managers_bmc(): + + res = {} + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/Chassis/1/PCIeDevices/slot_3/PCIeFunctions/slot_2.00") +def firmware_chassis_pcie_function_slot2(): + + res = { + "VendorId": "0x14e4", + "FunctionId": 1, + "SubsystemId": "0x4042", + "DeviceClass": "NetworkController", + "SubsystemVendorId": "0x17aa", + "DeviceId": "0x165f", + "RevisionId": "0x00", + "ClassCode": "0x020000", + } + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/Chassis/1/PCIeDevices/slot_3/PCIeFunctions") +def firmware_chassis_pcie_functions(): + + res = { + "Members": [ + { + "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/slot_3/PCIeFunctions/slot_2.00" + } + ], + } + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/Systems/437XR1138R2") +def firmware_systems_slot7(): + + res = { + "SerialNumber": "12345", + "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/slot_3", + "PCIeFunctions": { + "@odata.id": "/redfish/v1/Chassis/1/PCIeDevices/slot_3/PCIeFunctions" + }, + "DeviceType": "SingleFunction", + } + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/UpdateService/FirmwareInventory/BIOS") +def firmware_inventory_bios(): + + res = { + "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/BIOS", + "@odata.type": "#SoftwareInventory.v1_2_3.SoftwareInventory", + "Id": "BIOS", + "LowestSupportedVersion": "P79 v1.10", + "Name": "Contoso BIOS Firmware", + "RelatedItem": [{"@odata.id": "/redfish/v1/Systems/437XR1138R2"}], + "ReleaseDate": "2017-12-06T12:00:00", + "SoftwareId": "FEE82A67-6CE2-4625-9F44-237AD2402C28", + "Updateable": True, + "Version": "P79 v1.45", + "ReleaseDate": "2019-03-15T00:00:00Z", + } + if request.authorization["username"] in { + HARDCODED_UNL_USERNAME, + HARDCODED_SMC_USERNAME, + }: + res["Manufacturer"] = "SMCI" + else: + res["Manufacturer"] = "Contoso" + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/TaskService/999") +def task_manager(): + res = { + "@odata.id": "/redfish/v1/TaskService/999", + "@odata.type": "#Task.v1_4_3.Task", + "Id": "545", + "Name": "Task 545", + } + return Response(json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/TaskService/Tasks/545") +def task_status_545(): + + res = { + "@odata.id": "/redfish/v1/TaskService/Tasks/545", + "@odata.type": "#Task.v1_4_3.Task", + "Id": "545", + "Name": "Task 545", + "PercentComplete": app._percentage545, + } + if app._percentage545 == 0: + res["TaskState"] = "Running" + elif app._percentage545 in [25, 50, 75]: + res["TaskState"] = "Running" + res["TaskStatus"] = "OK" + res["Messages"] = [ + { + "Message": "Applying image", + "MessageId": "Update.1.1.TransferringToComponent", + } + ] + elif app._percentage545 == 100: + res["TaskState"] = "Completed" + res["TaskStatus"] = "OK" + res["Messages"] = [ + { + "Message": "Applying image", + "MessageId": "Update.1.1.TransferringToComponent", + }, + { + "Message": "A reset is required", + "MessageId": "Base.1.10.ResetRequired", + }, + { + "Message": "Task completed OK", + "MessageId": "TaskEvent.1.0.TaskCompletedOK", + }, + ] + else: + res["TaskState"] = "Exception" + res["TaskStatus"] = "Warning" + res["Messages"] = [ + { + "Message": "Error verifying image", + "MessageId": "Update.1.0.ApplyFailed", + "Severity": "Warning", + } + ] + app._percentage545 += 25 + return Response(response=json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/redfish/v1/TaskService/Tasks/546") +def task_status_546(): + + res = { + "@odata.type": "#Task.v1_4_3.Task", + "@odata.id": "/redfish/v1/TaskService/Tasks/546", + "Id": "546", + "Name": "BIOS Verify", + "TaskState": "Running", + "StartTime": "2022-09-29T14:50:54+00:00", + "PercentComplete": app._percentage546, + "HidePayload": True, + "TaskMonitor": "/redfish/v1/TaskMonitor/gd5n5ffS4gi9r6YKVZmgIIaj8ECLfnc", + "TaskStatus": "OK", + "Messages": [ + { + "MessageId": "", + "RelatedProperties": [""], + "Message": "", + "MessageArgs": [""], + "Severity": "", + } + ], + "Oem": {}, + } + if app._percentage546 == 0: + res["TaskState"] = "Running" + elif app._percentage546 in [25, 50, 75]: + res["TaskState"] = "Running" + elif app._percentage546 == 100: + res["TaskState"] = "Completed" + app._percentage546 += 25 + return Response(response=json.dumps(res), status=200, mimetype="application/json") + + +@app.route("/FWUpdate-unlicensed", methods=["GET"]) +def fwupdate_unlicensed(): + + res = { + "error": { + "code": "Base.v1_4_0.GeneralError", + "Message": "A general error has occurred. See ExtendedInfo for more information.", + "@Message.ExtendedInfo": [ + { + "MessageId": "SMC.1.0.OemLicenseNotPassed", + "Severity": "Warning", + "Resolution": "Please check if there was the next step with respective API to execute.", + "Message": "The BIOS firmware update was already in update mode.", + "MessageArgs": ["BIOS"], + "RelatedProperties": ["EnterUpdateMode_StatusCheck"], + } + ], + } + } + return Response(json.dumps(res), status=405, mimetype="application/json") + data = json.loads(request.form["UpdateParameters"]) + if data["@Redfish.OperationApplyTime"] != "Immediate": + return _failure("apply invalid") + if data["Targets"][0] != "/redfish/v1/UpdateService/FirmwareInventory/BMC": + return _failure("id invalid") + fileitem = request.files["UpdateFile"] + if not fileitem.filename.endswith(".bin"): + return _failure("filename invalid") + if fileitem.read().decode() != "hello": + return _failure("data invalid") + res = { + "Version": "P79 v1.45", + "@odata.id": "/redfish/v1/TaskService/Tasks/545", + "TaskMonitor": "/redfish/v1/TaskService/999", + } + # Location set to the URI of a task monitor. + return Response( + json.dumps(res), + status=202, + mimetype="application/json", + headers={"Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/545"}, + ) + + +@app.route("/FWUpdate-smc", methods=["POST"]) +def fwupdate_smc(): + + data = json.loads(request.form["UpdateParameters"]) + if data["@Redfish.OperationApplyTime"] != "OnStartUpdateRequest": + return _failure("apply invalid") + if data["Targets"][0] != "/redfish/v1/Systems/1/Bios": + return _failure("id invalid") + fileitem = request.files["UpdateFile"] + if not fileitem.filename.endswith(".bin"): + return _failure("filename invalid") + filecontents = fileitem.read().decode() + if filecontents == "hello": + app._percentage546 = 0 + res = { + "Accepted": { + "code": "Base.v1_4_0.Accepted", + "Message": "Successfully Accepted Request. Please see the location header and ExtendedInfo for more information.", + "@Message.ExtendedInfo": [ + { + "MessageId": "SMC.1.0.OemSimpleupdateAcceptedMessage", + "Severity": "Ok", + "Resolution": "No resolution was required.", + "Message": "Please also check Task Resource /redfish/v1/TaskService/Tasks/546 to see more information.", + "MessageArgs": ["/redfish/v1/TaskService/Tasks/546"], + "RelatedProperties": ["BiosVerifyAccepted"], + } + ], + } + } + # Location set to the URI of a task monitor. + return Response( + json.dumps(res), + status=202, + mimetype="application/json", + headers={ + "Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/546" + }, + ) + elif filecontents == "stuck": + res = { + "error": { + "code": "Base.v1_4_0.GeneralError", + "Message": "A general error has occurred. See ExtendedInfo for more information.", + "@Message.ExtendedInfo": [ + { + "MessageId": "SMC.1.0.OemFirmwareAlreadyInUpdateMode", + "Severity": "Warning", + "Resolution": "Please check if there was the next step with respective API to execute.", + "Message": "The BIOS firmware update was already in update mode.", + "MessageArgs": ["BIOS"], + "RelatedProperties": ["EnterUpdateMode_StatusCheck"], + } + ], + } + } + return Response(json.dumps(res), status=405, mimetype="application/json") + else: + return _failure("data invalid") + + +@app.route("/FWUpdate", methods=["POST"]) +def fwupdate(): + + data = json.loads(request.form["UpdateParameters"]) + if data["@Redfish.OperationApplyTime"] != "Immediate": + return _failure("apply invalid") + if data["Targets"][0] != "/redfish/v1/UpdateService/FirmwareInventory/BMC": + return _failure("id invalid") + fileitem = request.files["UpdateFile"] + if not fileitem.filename.endswith(".bin"): + return _failure("filename invalid") + if fileitem.read().decode() != "hello": + return _failure("data invalid") + res = { + "Version": "P79 v1.45", + "@odata.id": "/redfish/v1/TaskService/Tasks/545", + "TaskMonitor": "/redfish/v1/TaskService/999", + } + # Location set to the URI of a task monitor. + return Response( + json.dumps(res), + status=202, + mimetype="application/json", + headers={"Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/545"}, + ) + + +@app.route( + "/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate", methods=["POST"] +) +def startupdate(): + res = { + "Accepted": { + "code": "Base.v1_4_0.Accepted", + "Message": "Successfully Accepted Request. Please see the location header and ExtendedInfo for more information.", + "@Message.ExtendedInfo": [ + { + "MessageId": "SMC.1.0.OemSimpleupdateAcceptedMessage", + "Severity": "Ok", + "Resolution": "No resolution was required.", + "Message": "Please also check Task Resource /redfish/v1/TaskService/Tasks/546 to see more information.", + "MessageArgs": ["/redfish/v1/TaskService/Tasks/546"], + "RelatedProperties": ["BiosUpdateAccepted"], + } + ], + } + } + app._percentage546 = 0 + return Response( + json.dumps(res), + status=202, + mimetype="application/json", + headers={"Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/546"}, + ) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=4661) diff --git a/fwupd-1.8.6/plugins/rts54hid/README.md b/fwupd-1.8.6/plugins/rts54hid/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2f395a52ed40f809331707dfbf499975670088a8 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/README.md @@ -0,0 +1,65 @@ +# Realtek RTS54HID + +## Introduction + +This plugin allows the user to update any supported hub and attached downstream +ICs using a custom HID-based flashing protocol. It does not support any RTS54xx +device using the HUB update protocol. + +Other devices connected to the RTS54HIDxx using I2C will be supported soon. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format. + +This plugin supports the following protocol ID: + +* com.realtek.rts54 + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_0BDA&PID_1100&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` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x0BDA` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### Rts54TargetAddr + +The target address of a child module. + +Since: 1.1.3 + +### Rts54I2cSpeed + +The I2C speed to operate at (0, 1, 2). + +Since: 1.1.3 + +### Rts54RegisterAddrLen + +The I2C register address length of commands. + +Since: 1.1.3 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-common.h b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-common.h new file mode 100644 index 0000000000000000000000000000000000000000..fd3319debc73676d91519ac7484fc2bbe04d2081 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-common.h @@ -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_RTS54FU_HID_REPORT_LENGTH 0xc0 + +/* [vendor-cmd:64] [data-payload:128] */ +#define FU_RTS54HID_CMD_BUFFER_OFFSET_DATA 0x40 + +typedef struct __attribute__((packed)) { + guint8 target_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 --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-device.c b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-device.c new file mode 100644 index 0000000000000000000000000000000000000000..2a3b9796e4d34870eb1efd957e10ab3706fcd6dd --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-device.c @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-rts54hid-common.h" +#include "fu-rts54hid-device.h" + +struct _FuRts54HidDevice { + FuHidDevice parent_instance; + gboolean fw_auth; + gboolean dual_bank; +}; + +G_DEFINE_TYPE(FuRts54HidDevice, fu_rts54hid_device, FU_TYPE_HID_DEVICE) + +static void +fu_rts54hid_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); + fu_string_append_kb(str, idt, "FwAuth", self->fw_auth); + fu_string_append_kb(str, idt, "DualBank", self->dual_bank); +} + +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_RTS54FU_HID_REPORT_LENGTH] = {0}; + + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to set clock-mode=%i: ", enable); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_reset_to_flash(FuRts54HidDevice *self, GError **error) +{ + FuRts54HidCmdBuffer cmd_buffer = { + .cmd = FU_RTS54HID_CMD_WRITE_DATA, + .ext = FU_RTS54HID_EXT_RESET2FLASH, + .dwregaddr = 0, + .bufferlen = 0, + .parameters = 0, + }; + guint8 buf[FU_RTS54FU_HID_REPORT_LENGTH] = {0}; + + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to soft reset: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_write_flash(FuRts54HidDevice *self, + guint32 addr, + const guint8 *data, + guint16 data_sz, + GError **error) +{ + 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_RTS54FU_HID_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); + + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_memcpy_safe(buf, + sizeof(buf), + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, /* dst */ + data, + data_sz, + 0x0, /* src */ + data_sz, + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write flash @%08x: ", (guint)addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_verify_update_fw(FuRts54HidDevice *self, FuProgress *progress, GError **error) +{ + 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_RTS54FU_HID_REPORT_LENGTH] = {0}; + + /* set then get */ + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + fu_progress_sleep(progress, 4000); + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + /* check device status */ + if (buf[0] != 0x01) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "firmware flash failed"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hid_device_erase_spare_bank(FuRts54HidDevice *self, GError **error) +{ + 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_RTS54FU_HID_REPORT_LENGTH] = {0}; + + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to erase spare bank: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_device_ensure_status(FuRts54HidDevice *self, GError **error) +{ + 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_RTS54FU_HID_REPORT_LENGTH] = {0}; + g_autofree gchar *version = NULL; + + /* set then get */ + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + /* check the hardware capabilities */ + self->dual_bank = (buf[7] & 0xf0) == 0x80; + self->fw_auth = (buf[13] & 0x02) > 0; + + /* hub version is more accurate than bcdVersion */ + version = g_strdup_printf("%x.%x", buf[10], buf[11]); + fu_device_set_version(FU_DEVICE(self), version); + return TRUE; +} + +static gboolean +fu_rts54hid_device_setup(FuDevice *device, GError **error) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_rts54hid_device_parent_class)->setup(device, error)) + return FALSE; + + /* check this device is correct */ + if (!fu_rts54hid_device_ensure_status(self, error)) + return FALSE; + + /* both conditions must be set */ + if (!self->fw_auth) { + fu_device_set_update_error(device, "device does not support authentication"); + } else if (!self->dual_bank) { + fu_device_set_update_error(device, "device does not support dual-bank updating"); + } else { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hid_device_close(FuDevice *device, GError **error) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); + + /* set MCU to normal clock rate */ + if (!fu_rts54hid_device_set_clock_mode(self, FALSE, error)) + return FALSE; + + /* FuHidDevice->close */ + return FU_DEVICE_CLASS(fu_rts54hid_device_parent_class)->close(device, error); +} + +static gboolean +fu_rts54hid_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 46, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 52, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reset"); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* set MCU to high clock rate for better ISP performance */ + if (!fu_rts54hid_device_set_clock_mode(self, TRUE, error)) + return FALSE; + + /* erase spare flash bank only if it is not empty */ + if (!fu_rts54hid_device_erase_spare_bank(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write each block */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + FU_RTS54HID_TRANSFER_BLOCK_SIZE); + 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, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* get device to authenticate the firmware */ + if (!fu_rts54hid_device_verify_update_fw(self, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* send software reset to run available flash code */ + if (!fu_rts54hid_device_reset_to_flash(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_rts54hid_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 62, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 38, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_rts54hid_device_init(FuRts54HidDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rts54"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_rts54hid_device_class_init(FuRts54HidDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_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_device->close = fu_rts54hid_device_close; + klass_device->set_progress = fu_rts54hid_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-device.h b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-device.h new file mode 100644 index 0000000000000000000000000000000000000000..b88d949da8945636ad001776bbeada0635993d3e --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_RTS54HID_DEVICE (fu_rts54hid_device_get_type()) +G_DECLARE_FINAL_TYPE(FuRts54HidDevice, fu_rts54hid_device, FU, RTS54HID_DEVICE, FuHidDevice) + +#define FU_RTS54HID_DEVICE_TIMEOUT 1000 /* ms */ diff --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-module.c b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-module.c new file mode 100644 index 0000000000000000000000000000000000000000..439d85a465cff9416180ff6ec7da3d1a8a3d9815 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-module.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-rts54hid-common.h" +#include "fu-rts54hid-device.h" +#include "fu-rts54hid-module.h" + +struct _FuRts54HidModule { + FuDevice parent_instance; + guint8 target_addr; + guint8 i2c_speed; + guint8 register_addr_len; +}; + +G_DEFINE_TYPE(FuRts54HidModule, fu_rts54hid_module, FU_TYPE_DEVICE) + +static void +fu_rts54hid_module_to_string(FuDevice *module, guint idt, GString *str) +{ + FuRts54HidModule *self = FU_RTS54HID_MODULE(module); + fu_string_append_kx(str, idt, "TargetAddr", self->target_addr); + fu_string_append_kx(str, idt, "I2cSpeed", self->i2c_speed); + fu_string_append_kx(str, idt, "RegisterAddrLen", self->register_addr_len); +} + +static FuRts54HidDevice * +fu_rts54hid_module_get_parent(FuRts54HidModule *self, GError **error) +{ + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); + if (parent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); + return NULL; + } + return FU_RTS54HID_DEVICE(parent); +} + +static gboolean +fu_rts54hid_module_i2c_write(FuRts54HidModule *self, + const guint8 *data, + guint8 data_sz, + GError **error) +{ + FuRts54HidDevice *parent; + 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 = {.target_addr = self->target_addr, + .data_sz = self->register_addr_len, + .speed = self->i2c_speed | 0x80}, + }; + guint8 buf[FU_RTS54FU_HID_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; + + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_memcpy_safe(buf, + sizeof(buf), + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, /* dst */ + data, + data_sz, + 0x0, /* src */ + data_sz, + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write i2c @%04x: ", self->target_addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_rts54hid_module_i2c_read(FuRts54HidModule *self, + guint32 cmd, + guint8 *data, + guint8 data_sz, + GError **error) +{ + FuRts54HidDevice *parent; + 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 = {.target_addr = self->target_addr, + .data_sz = self->register_addr_len, + .speed = self->i2c_speed | 0x80}, + }; + guint8 buf[FU_RTS54FU_HID_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 */ + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x0, /* dst */ + (const guint8 *)&cmd_buffer, + sizeof(cmd_buffer), + 0x0, /* src */ + sizeof(cmd_buffer), + error)) + return FALSE; + if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT * 2, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write i2c @%04x: ", self->target_addr); + return FALSE; + } + if (!fu_hid_device_get_report(FU_HID_DEVICE(parent), + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + return fu_memcpy_safe(data, + data_sz, + 0x0, + buf, + sizeof(buf), + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, + data_sz, + error); +} + +static gboolean +fu_rts54hid_module_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuRts54HidModule *self = FU_RTS54HID_MODULE(device); + guint64 tmp = 0; + + /* load target address from quirks */ + if (g_strcmp0(key, "Rts54TargetAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->target_addr = tmp; + return TRUE; + } + + /* load i2c speed from quirks */ + if (g_strcmp0(key, "Rts54I2cSpeed") == 0) { + if (!fu_strtoull(value, &tmp, 0, FU_RTS54HID_I2C_SPEED_LAST - 1, error)) + return FALSE; + self->i2c_speed = tmp; + return TRUE; + } + + /* load register address length from quirks */ + if (g_strcmp0(key, "Rts54RegisterAddrLen") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + self->register_addr_len = 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_rts54hid_module_write_firmware(FuDevice *module, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54HidModule *self = FU_RTS54HID_MODULE(module); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* 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_progress_set_status(progress, 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, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + /* update progress */ + fu_progress_set_percentage_full(progress, (gsize)i + 1, (gsize)chunks->len); + } + + /* success! */ + return TRUE; +} + +static void +fu_rts54hid_module_init(FuRts54HidModule *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); +} + +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; +} + +FuRts54HidModule * +fu_rts54hid_module_new(void) +{ + FuRts54HidModule *self = NULL; + self = g_object_new(FU_TYPE_RTS54HID_MODULE, NULL); + return self; +} diff --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-module.h b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-module.h new file mode 100644 index 0000000000000000000000000000000000000000..c24351da09a1fcd07991ca54199b7a41a1eb7ce8 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-module.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_RTS54HID_MODULE (fu_rts54hid_module_get_type()) +G_DECLARE_FINAL_TYPE(FuRts54HidModule, fu_rts54hid_module, FU, RTS54HID_MODULE, FuDevice) + +FuRts54HidModule * +fu_rts54hid_module_new(void); diff --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-plugin.c b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..29b4fc39ba7cb895122de646be1c20ae1f7db382 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-plugin.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-rts54hid-device.h" +#include "fu-rts54hid-module.h" +#include "fu-rts54hid-plugin.h" + +struct _FuRts54HidPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuRts54HidPlugin, fu_rts54hid_plugin, FU_TYPE_PLUGIN) + +static void +fu_rts54hid_plugin_init(FuRts54HidPlugin *self) +{ +} + +static void +fu_rts54hid_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "Rts54TargetAddr"); + fu_context_add_quirk_key(ctx, "Rts54I2cSpeed"); + fu_context_add_quirk_key(ctx, "Rts54RegisterAddrLen"); + fu_plugin_set_name(plugin, "rts54hid"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HID_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HID_MODULE); +} + +static void +fu_rts54hid_plugin_class_init(FuRts54HidPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_rts54hid_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-plugin.h b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..7c2b2614edd7e2079aa1daf6058691373493a645 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/fu-rts54hid-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuRts54HidPlugin, fu_rts54hid_plugin, FU, RTS54HID_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/rts54hid/meson.build b/fwupd-1.8.6/plugins/rts54hid/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..8c9bccb460dd01201cbfbd54b837325ba4b7bbb0 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/meson.build @@ -0,0 +1,16 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginRts54Hid"'] + +plugin_quirks += files('rts54hid.quirk') +plugin_builtins += static_library('fu_plugin_rts54hid', + sources: [ + 'fu-rts54hid-device.c', + 'fu-rts54hid-module.c', + 'fu-rts54hid-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/rts54hid/rts54hid.quirk b/fwupd-1.8.6/plugins/rts54hid/rts54hid.quirk new file mode 100644 index 0000000000000000000000000000000000000000..aedaeaa2f588fd361f2ee1bb92385884415b2f40 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hid/rts54hid.quirk @@ -0,0 +1,17 @@ +# Realtek USBHub (HID Device) +[USB\VID_0BDA&PID_5A00] +Plugin = rts54hid +GType = FuRts54HidDevice +FirmwareSizeMin = 0x10000 +FirmwareSizeMax = 0x40000 +Children = FuRts54HidModule|USB\VID_0BDA&PID_5A00&I2C_01 + +# this is a fictitious example... +[USB\VID_0BDA&PID_5A00&I2C_01] +Plugin = rts54hid +Name = HDMI Converter +Flags = updatable +FirmwareSize = 0x20000 +Rts54TargetAddr = 0x00 +Rts54I2cSpeed = 0x00 +Rts54RegisterAddrLen = 0x04 diff --git a/fwupd-1.8.6/plugins/rts54hub/README.md b/fwupd-1.8.6/plugins/rts54hub/README.md new file mode 100644 index 0000000000000000000000000000000000000000..50b39409d199668f611fba814d1ee055877b115b --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/README.md @@ -0,0 +1,40 @@ +# Realtek RTS54 HUB + +## 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 IDs: + +* com.realtek.rts54 +* com.realtek.rts54.i2c + +## 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` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x0BDA` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/rts54hub/data/lsusb.txt b/fwupd-1.8.6/plugins/rts54hub/data/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..16dd2fecc552196800427ef0898303cb3f1fb05a --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/data/lsusb.txt @@ -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 --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-device.c b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-device.c new file mode 100644 index 0000000000000000000000000000000000000000..edae19d5ca303cb4dae74ca97f16cc4373fc9614 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-device.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#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 1000 /* 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 + +#define FU_RTS54HUB_I2C_CONFIG_REQUEST 0xF6 +#define FU_RTS54HUB_I2C_WRITE_REQUEST 0xC6 +#define FU_RTS54HUB_I2C_READ_REQUEST 0xD6 + +typedef enum { + FU_RTS54HUB_VENDOR_CMD_NONE = 0x00, + FU_RTS54HUB_VENDOR_CMD_STATUS = 1 << 0, + FU_RTS54HUB_VENDOR_CMD_FLASH = 1 << 1, +} FuRts54HubVendorCmd; + +static void +fu_rts54hub_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuRts54HubDevice *self = FU_RTS54HUB_DEVICE(device); + fu_string_append_kb(str, idt, "FwAuth", self->fw_auth); + fu_string_append_kb(str, idt, "DualBank", self->dual_bank); + fu_string_append_kb(str, idt, "RunningOnFlash", self->running_on_flash); +} + +gboolean +fu_rts54hub_device_i2c_config(FuRts54HubDevice *self, + guint8 target_addr, + guint8 sub_length, + FuRts54HubI2cSpeed speed, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint16 value = 0; + guint16 index = 0x8080; + + value = ((guint16)target_addr << 8) | sub_length; + index += speed; + 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, + FU_RTS54HUB_I2C_CONFIG_REQUEST, + value, /* value */ + index, /* idx */ + NULL, + 0, /* data */ + NULL, /* actual */ + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to issue i2c Conf cmd 0x%02x: ", target_addr); + return FALSE; + } + return TRUE; +} + +gboolean +fu_rts54hub_device_i2c_write(FuRts54HubDevice *self, + guint32 sub_addr, + const guint8 *data, + gsize datasz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autofree guint8 *datarw = fu_memdup_safe(data, datasz, error); + if (datarw == NULL) + return FALSE; + 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, + FU_RTS54HUB_I2C_WRITE_REQUEST, + sub_addr, /* value */ + 0x0000, /* idx */ + datarw, + datasz, /* data */ + NULL, + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write I2C"); + return FALSE; + } + return TRUE; +} + +gboolean +fu_rts54hub_device_i2c_read(FuRts54HubDevice *self, + guint32 sub_addr, + guint8 *data, + gsize datasz, + 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_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + FU_RTS54HUB_I2C_READ_REQUEST, + 0x0000, + sub_addr, + data, + datasz, + NULL, + FU_RTS54HUB_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read I2C: "); + return FALSE; + } + return TRUE; +} + +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 = NULL; + + /* make mutable */ + datarw = fu_memdup_safe(data, datasz, error); + if (datarw == NULL) + return FALSE; + + 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; +} + +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); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_rts54hub_device_parent_class)->setup(device, error)) + return FALSE; + + /* check this device is correct */ + if (!fu_rts54hub_device_vendor_cmd(self, FU_RTS54HUB_VENDOR_CMD_STATUS, error)) { + g_prefix_error(error, "failed to vendor enable: "); + 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(FuDevice *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; + } + } + + /* FuUsbDevice->close */ + return FU_DEVICE_CLASS(fu_rts54hub_device_parent_class)->close(device, error); +} + +static gboolean +fu_rts54hub_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54HubDevice *self = FU_RTS54HUB_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 46, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 52, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* 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 */ + if (!fu_rts54hub_device_erase_flash(self, 1, error)) + return FALSE; + fu_progress_step_done(progress); + + /* 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; + } + + /* write each block */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + FU_RTS54HUB_DEVICE_BLOCK_SIZE); + 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, + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* get device to authenticate the firmware */ + if (!fu_rts54hub_device_flash_authentication(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* send software reset to run available flash code */ + if (!fu_rts54hub_device_reset_flash(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* 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 FuFirmware * +fu_rts54hub_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + guint8 tmp = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + if (!fu_memread_uint8_safe(buf, bufsz, 0x7ef3, &tmp, error)) + return NULL; + if ((tmp & 0xf0) != 0x80) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware needs to be dual bank"); + return NULL; + } + return fu_firmware_new_from_bytes(fw); +} + +static void +fu_rts54hub_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 62, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 38, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_rts54hub_device_init(FuRts54HubDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rts54"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + 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); + 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_device->close = fu_rts54hub_device_close; + klass_device->set_progress = fu_rts54hub_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-device.h b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-device.h new file mode 100644 index 0000000000000000000000000000000000000000..40150975d725ef542d3ac42fdc24ecbf03640e0f --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-device.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_RTS54HUB_DEVICE (fu_rts54hub_device_get_type()) + +typedef enum { + FU_RTS54HUB_I2C_SPEED_100K, + FU_RTS54HUB_I2C_SPEED_200K, + FU_RTS54HUB_I2C_SPEED_300K, + FU_RTS54HUB_I2C_SPEED_400K, + FU_RTS54HUB_I2C_SPEED_500K, + FU_RTS54HUB_I2C_SPEED_600K, + FU_RTS54HUB_I2C_SPEED_700K, + FU_RTS54HUB_I2C_SPEED_800K, + FU_RTS54HUB_I2C_SPEED_LAST +} FuRts54HubI2cSpeed; + +G_DECLARE_FINAL_TYPE(FuRts54HubDevice, fu_rts54hub_device, FU, RTS54HUB_DEVICE, FuUsbDevice) + +gboolean +fu_rts54hub_device_vendor_cmd(FuRts54HubDevice *self, guint8 value, GError **error); +gboolean +fu_rts54hub_device_i2c_config(FuRts54HubDevice *self, + guint8 target_addr, + guint8 sub_length, + FuRts54HubI2cSpeed speed, + GError **error); +gboolean +fu_rts54hub_device_i2c_write(FuRts54HubDevice *self, + guint32 sub_addr, + const guint8 *data, + gsize datasz, + GError **error); +gboolean +fu_rts54hub_device_i2c_read(FuRts54HubDevice *self, + guint32 sub_addr, + guint8 *data, + gsize datasz, + GError **error); diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-plugin.c b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..5422a18fad7c137c31544ad8b67a3ce5484c1138 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-plugin.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-rts54hub-device.h" +#include "fu-rts54hub-plugin.h" +#include "fu-rts54hub-rtd21xx-background.h" +#include "fu-rts54hub-rtd21xx-foreground.h" + +struct _FuRts54HubPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuRts54HubPlugin, fu_rts54hub_plugin, FU_TYPE_PLUGIN) + +static void +fu_rts54hub_plugin_init(FuRts54HubPlugin *self) +{ +} + +static void +fu_rts54hub_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "Rts54TargetAddr"); + fu_context_add_quirk_key(ctx, "Rts54I2cSpeed"); + fu_context_add_quirk_key(ctx, "Rts54RegisterAddrLen"); + fu_plugin_set_name(plugin, "rts54hub"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_RTD21XX_BACKGROUND); + fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_RTD21XX_FOREGROUND); +} + +static void +fu_rts54hub_plugin_class_init(FuRts54HubPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_rts54hub_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-plugin.h b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..f78b9e7927b9fbe6c16e8d271c7299d7dfe1ab9f --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuRts54HubPlugin, fu_rts54hub_plugin, FU, RTS54HUB_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c new file mode 100644 index 0000000000000000000000000000000000000000..8664ec33504ec11a1c6491de1f594c3effaf47ba --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2021 Realtek Corporation + * Copyright (C) 2021 Ricky Wu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-rts54hub-device.h" +#include "fu-rts54hub-rtd21xx-background.h" + +struct _FuRts54hubRtd21xxBackground { + FuRts54hubRtd21xxDevice parent_instance; +}; + +G_DEFINE_TYPE(FuRts54hubRtd21xxBackground, + fu_rts54hub_rtd21xx_background, + FU_TYPE_RTS54HUB_RTD21XX_DEVICE) + +#define ISP_DATA_BLOCKSIZE 32 +#define ISP_PACKET_SIZE 257 + +typedef enum { + ISP_CMD_FW_UPDATE_START = 0x01, + ISP_CMD_FW_UPDATE_ISP_DONE = 0x02, + ISP_CMD_GET_FW_INFO = 0x03, + ISP_CMD_FW_UPDATE_EXIT = 0x04, + ISP_CMD_GET_PROJECT_ID_ADDR = 0x05, + ISP_CMD_SYNC_IDENTIFY_CODE = 0x06, +} IspCmd; + +static gboolean +fu_rts54hub_rtd21xx_ensure_version_unlocked(FuRts54hubRtd21xxBackground *self, GError **error) +{ + guint8 buf_rep[7] = {0x00}; + guint8 buf_req[] = {ISP_CMD_GET_FW_INFO}; + g_autofree gchar *version = NULL; + + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_OPCODE, + buf_req, + sizeof(buf_req), + error)) { + g_prefix_error(error, "failed to get version number: "); + return FALSE; + } + + /* wait for device ready */ + g_usleep(300000); + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + 0x00, + buf_rep, + sizeof(buf_rep), + error)) { + g_prefix_error(error, "failed to get version number: "); + return FALSE; + } + + /* set version */ + version = g_strdup_printf("%u.%u", buf_rep[1], buf_rep[2]); + fu_device_set_version(FU_DEVICE(self), version); + + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_background_detach_raw(FuRts54hubRtd21xxBackground *self, GError **error) +{ + guint8 buf = 0x02; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + 0x6A, + 0x31, + &buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to detach: "); + return FALSE; + } + + /* wait for device ready */ + g_usleep(300000); + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_background_detach_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuRts54hubRtd21xxBackground *self = FU_RTS54HUB_RTD21XX_BACKGROUND(device); + guint8 status = 0xfe; + + if (!fu_rts54hub_rtd21xx_background_detach_raw(self, error)) + return FALSE; + if (!fu_rts54hub_rtd21xx_device_read_status_raw(FU_RTS54HUB_RTD21XX_DEVICE(self), + &status, + error)) + return FALSE; + if (status != ISP_STATUS_IDLE_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "detach status was 0x%02x", + status); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_background_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_device_retry(device, fu_rts54hub_rtd21xx_background_detach_cb, 100, NULL, error); +} + +static gboolean +fu_rts54hub_rtd21xx_background_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuRts54hubRtd21xxDevice *self = FU_RTS54HUB_RTD21XX_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + guint8 buf[] = {ISP_CMD_FW_UPDATE_EXIT}; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + if (!fu_rts54hub_rtd21xx_device_i2c_write(self, + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_OPCODE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to attach: "); + return FALSE; + } + fu_progress_sleep(progress, 1000); + + /* success */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_background_setup(FuDevice *device, GError **error) +{ + FuRts54hubRtd21xxBackground *self = FU_RTS54HUB_RTD21XX_BACKGROUND(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get version */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return FALSE; + if (!fu_rts54hub_rtd21xx_ensure_version_unlocked(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_background_reload(FuDevice *device, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open parent device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_rts54hub_rtd21xx_background_setup(device, error); +} + +static gboolean +fu_rts54hub_rtd21xx_background_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54hubRtd21xxBackground *self = FU_RTS54HUB_RTD21XX_BACKGROUND(device); + const guint8 *fwbuf; + gsize fwbufsz = 0; + guint32 project_addr; + guint8 project_id_count; + guint8 read_buf[10] = {0x0}; + guint8 write_buf[ISP_PACKET_SIZE] = {0x0}; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "setup"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "exit"); + + /* open device */ + locker = fu_device_locker_new(self, error); + if (locker == NULL) + return FALSE; + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + fwbuf = g_bytes_get_data(fw, &fwbufsz); + + /* get project ID address */ + write_buf[0] = ISP_CMD_GET_PROJECT_ID_ADDR; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "failed to get project ID address: "); + return FALSE; + } + + /* read back 6 bytes data */ + g_usleep(I2C_DELAY_AFTER_SEND * 40); + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + 0x00, + read_buf, + 6, + error)) { + g_prefix_error(error, "failed to read project ID: "); + return FALSE; + } + if (read_buf[0] != ISP_STATUS_IDLE_SUCCESS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed project ID with error 0x%02x: ", + read_buf[0]); + return FALSE; + } + + /* verify project ID */ + project_addr = fu_memread_uint32(read_buf + 1, G_BIG_ENDIAN); + project_id_count = read_buf[5]; + write_buf[0] = ISP_CMD_SYNC_IDENTIFY_CODE; + if (!fu_memcpy_safe(write_buf, + sizeof(write_buf), + 0x1, /* dst */ + fwbuf, + fwbufsz, + project_addr, /* src */ + project_id_count, + error)) { + g_prefix_error(error, "failed to write project ID from 0x%04x: ", project_addr); + return FALSE; + } + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_OPCODE, + write_buf, + project_id_count + 1, + error)) { + g_prefix_error(error, "failed to send fw update start cmd: "); + return FALSE; + } + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + + /* background FW update start command */ + write_buf[0] = ISP_CMD_FW_UPDATE_START; + fu_memwrite_uint16(write_buf + 1, ISP_DATA_BLOCKSIZE, G_BIG_ENDIAN); + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_OPCODE, + write_buf, + 3, + error)) { + g_prefix_error(error, "failed to send fw update start cmd: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* send data */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + ISP_DATA_BLOCKSIZE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), + NULL, + error)) + return FALSE; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_ISP_DATA_OPCODE, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write @0x%04x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* update finish command */ + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + write_buf[0] = ISP_CMD_FW_UPDATE_ISP_DONE; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_BACKGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "failed update finish cmd: "); + return FALSE; + } + + /* exit fw mode */ + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_rts54hub_rtd21xx_background_init(FuRts54hubRtd21xxBackground *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); +} + +static void +fu_rts54hub_rtd21xx_background_class_init(FuRts54hubRtd21xxBackgroundClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_rts54hub_rtd21xx_background_setup; + klass_device->reload = fu_rts54hub_rtd21xx_background_reload; + klass_device->attach = fu_rts54hub_rtd21xx_background_attach; + klass_device->detach = fu_rts54hub_rtd21xx_background_detach; + klass_device->write_firmware = fu_rts54hub_rtd21xx_background_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-background.h b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-background.h new file mode 100644 index 0000000000000000000000000000000000000000..bac6e9500662c805e9ae8d70b5b7329770d7483e --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-background.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Ricky Wu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-rts54hub-rtd21xx-device.h" + +#define FU_TYPE_RTS54HUB_RTD21XX_BACKGROUND (fu_rts54hub_rtd21xx_background_get_type()) +G_DECLARE_FINAL_TYPE(FuRts54hubRtd21xxBackground, + fu_rts54hub_rtd21xx_background, + FU, + RTS54HUB_RTD21XX_BACKGROUND, + FuRts54hubRtd21xxDevice) diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c new file mode 100644 index 0000000000000000000000000000000000000000..17496b7b091ab9210afaadb30db157951edcd993 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2021 Realtek Corporation + * Copyright (C) 2021 Ricky Wu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-rts54hub-device.h" +#include "fu-rts54hub-rtd21xx-device.h" + +typedef struct { + guint8 target_addr; + guint8 i2c_speed; + guint8 register_addr_len; +} FuRts54hubRtd21xxDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuRts54hubRtd21xxDevice, fu_rts54hub_rtd21xx_device, FU_TYPE_DEVICE) +#define GET_PRIVATE(o) (fu_rts54hub_rtd21xx_device_get_instance_private(o)) + +typedef enum { + VENDOR_CMD_DISABLE = 0x00, + VENDOR_CMD_ENABLE = 0x01, + VENDOR_CMD_ACCESS_FLASH = 0x02, +} VendorCmd; + +static void +fu_rts54hub_rtd21xx_device_to_string(FuDevice *module, guint idt, GString *str) +{ + FuRts54hubRtd21xxDevice *self = FU_RTS54HUB_RTD21XX_DEVICE(module); + FuRts54hubRtd21xxDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append_kx(str, idt, "TargetAddr", priv->target_addr); + fu_string_append_kx(str, idt, "I2cSpeed", priv->i2c_speed); + fu_string_append_kx(str, idt, "RegisterAddrLen", priv->register_addr_len); +} + +static FuRts54HubDevice * +fu_rts54hub_rtd21xx_device_get_parent(FuRts54hubRtd21xxDevice *self, GError **error) +{ + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); + if (parent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); + return NULL; + } + return FU_RTS54HUB_DEVICE(parent); +} + +static gboolean +fu_rts54hub_rtd21xx_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuRts54hubRtd21xxDevice *self = FU_RTS54HUB_RTD21XX_DEVICE(device); + FuRts54hubRtd21xxDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp = 0; + + /* load target address from quirks */ + if (g_strcmp0(key, "Rts54TargetAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->target_addr = tmp; + return TRUE; + } + + /* load i2c speed from quirks */ + if (g_strcmp0(key, "Rts54I2cSpeed") == 0) { + if (!fu_strtoull(value, &tmp, 0, FU_RTS54HUB_I2C_SPEED_LAST - 1, error)) + return FALSE; + priv->i2c_speed = tmp; + return TRUE; + } + + /* load register address length from quirks */ + if (g_strcmp0(key, "Rts54RegisterAddrLen") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->register_addr_len = tmp; + return TRUE; + } + + /* failed */ + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "quirk key not supported"); + return FALSE; +} + +gboolean +fu_rts54hub_rtd21xx_device_i2c_write(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + const guint8 *data, + gsize datasz, + GError **error) +{ + FuRts54HubDevice *parent; + FuRts54hubRtd21xxDevicePrivate *priv = GET_PRIVATE(self); + + parent = fu_rts54hub_rtd21xx_device_get_parent(self, error); + if (parent == NULL) + return FALSE; + if (!fu_rts54hub_device_vendor_cmd(parent, VENDOR_CMD_ENABLE, error)) + return FALSE; + + if (target_addr != priv->target_addr) { + if (!fu_rts54hub_device_i2c_config(parent, + target_addr, + 1, + FU_RTS54HUB_I2C_SPEED_200K, + error)) + return FALSE; + priv->target_addr = target_addr; + } + if (!fu_rts54hub_device_i2c_write(parent, sub_addr, data, datasz, error)) { + g_prefix_error(error, "failed to write I2C @0x%02x:%02x: ", target_addr, sub_addr); + return FALSE; + } + g_usleep(I2C_DELAY_AFTER_SEND); + return TRUE; +} + +gboolean +fu_rts54hub_rtd21xx_device_i2c_read(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + guint8 *data, + gsize datasz, + GError **error) +{ + FuRts54HubDevice *parent; + FuRts54hubRtd21xxDevicePrivate *priv = GET_PRIVATE(self); + + parent = fu_rts54hub_rtd21xx_device_get_parent(self, error); + if (parent == NULL) + return FALSE; + if (!fu_rts54hub_device_vendor_cmd(parent, VENDOR_CMD_ENABLE, error)) + return FALSE; + if (target_addr != priv->target_addr) { + if (!fu_rts54hub_device_i2c_config(parent, + target_addr, + 1, + FU_RTS54HUB_I2C_SPEED_200K, + error)) + return FALSE; + priv->target_addr = target_addr; + } + if (!fu_rts54hub_device_i2c_read(parent, sub_addr, data, datasz, error)) { + g_prefix_error(error, "failed to read I2C: "); + return FALSE; + } + return TRUE; +} + +gboolean +fu_rts54hub_rtd21xx_device_read_status_raw(FuRts54hubRtd21xxDevice *self, + guint8 *status, + GError **error) +{ + guint8 buf = 0x00; + if (!fu_rts54hub_rtd21xx_device_i2c_read(self, + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_STATUS, + &buf, + sizeof(buf), + error)) + return FALSE; + if (status != NULL) + *status = buf; + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_device_read_status_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuRts54hubRtd21xxDevice *self = FU_RTS54HUB_RTD21XX_DEVICE(device); + guint8 status = 0xfd; + if (!fu_rts54hub_rtd21xx_device_read_status_raw(self, &status, error)) + return FALSE; + if (status == ISP_STATUS_BUSY) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "status was 0x%02x", status); + return FALSE; + } + return TRUE; +} + +gboolean +fu_rts54hub_rtd21xx_device_read_status(FuRts54hubRtd21xxDevice *self, + guint8 *status, + GError **error) +{ + return fu_device_retry(FU_DEVICE(self), + fu_rts54hub_rtd21xx_device_read_status_cb, + 4200, + status, + error); +} + +static void +fu_rts54hub_rtd21xx_device_init(FuRts54hubRtd21xxDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rts54.i2c"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_install_duration(FU_DEVICE(self), 100); /* seconds */ + fu_device_set_logical_id(FU_DEVICE(self), "I2C"); + fu_device_retry_set_delay(FU_DEVICE(self), 30); /* ms */ +} + +static void +fu_rts54hub_rtd21xx_device_class_init(FuRts54hubRtd21xxDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_rts54hub_rtd21xx_device_to_string; + klass_device->set_quirk_kv = fu_rts54hub_rtd21xx_device_set_quirk_kv; +} diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h new file mode 100644 index 0000000000000000000000000000000000000000..6070663745e0721e41587f3a9c2cd3406b67b3fe --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 Ricky Wu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_RTS54HUB_RTD21XX_DEVICE (fu_rts54hub_rtd21xx_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuRts54hubRtd21xxDevice, + fu_rts54hub_rtd21xx_device, + FU, + RTS54HUB_RTD21XX_DEVICE, + FuDevice) + +struct _FuRts54hubRtd21xxDeviceClass { + FuDeviceClass parent_class; +}; + +#define I2C_DELAY_AFTER_SEND 5000 /* us */ + +#define UC_ISP_TARGET_ADDR 0x3A +#define UC_FOREGROUND_STATUS 0x31 +#define UC_FOREGROUND_OPCODE 0x33 +#define UC_FOREGROUND_ISP_DATA_OPCODE 0x34 +#define UC_BACKGROUND_OPCODE 0x31 +#define UC_BACKGROUND_ISP_DATA_OPCODE 0x32 + +typedef enum { + ISP_STATUS_BUSY = 0xBB, /* host must wait for device */ + ISP_STATUS_IDLE_SUCCESS = 0x11, /* previous command was OK */ + ISP_STATUS_IDLE_FAILURE = 0x12, /* previous command failed */ +} IspStatus; + +gboolean +fu_rts54hub_rtd21xx_device_read_status(FuRts54hubRtd21xxDevice *self, + guint8 *status, + GError **error); +gboolean +fu_rts54hub_rtd21xx_device_read_status_raw(FuRts54hubRtd21xxDevice *self, + guint8 *status, + GError **error); +gboolean +fu_rts54hub_rtd21xx_device_i2c_read(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + guint8 *data, + gsize datasz, + GError **error); +gboolean +fu_rts54hub_rtd21xx_device_i2c_write(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + const guint8 *data, + gsize datasz, + GError **error); diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c new file mode 100644 index 0000000000000000000000000000000000000000..9cad2b93f85c1de7aaf0dffe257836122806b8c7 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2021 Realtek Corporation + * Copyright (C) 2021 Ricky Wu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-rts54hub-device.h" +#include "fu-rts54hub-rtd21xx-foreground.h" + +struct _FuRts54hubRtd21xxForeground { + FuRts54hubRtd21xxDevice parent_instance; +}; + +G_DEFINE_TYPE(FuRts54hubRtd21xxForeground, + fu_rts54hub_rtd21xx_foreground, + FU_TYPE_RTS54HUB_RTD21XX_DEVICE) + +#define ISP_DATA_BLOCKSIZE 256 +#define ISP_PACKET_SIZE 257 + +typedef enum { + ISP_CMD_ENTER_FW_UPDATE = 0x01, + ISP_CMD_GET_PROJECT_ID_ADDR = 0x02, + ISP_CMD_SYNC_IDENTIFY_CODE = 0x03, + ISP_CMD_GET_FW_INFO = 0x04, + ISP_CMD_FW_UPDATE_START = 0x05, + ISP_CMD_FW_UPDATE_ISP_DONE = 0x06, + ISP_CMD_FW_UPDATE_RESET = 0x07, + ISP_CMD_FW_UPDATE_EXIT = 0x08, +} IspCmd; + +static gboolean +fu_rts54hub_rtd21xx_ensure_version_unlocked(FuRts54hubRtd21xxForeground *self, GError **error) +{ + guint8 buf_rep[7] = {0x00}; + guint8 buf_req[] = {ISP_CMD_GET_FW_INFO}; + g_autofree gchar *version = NULL; + + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + buf_req, + sizeof(buf_req), + error)) { + g_prefix_error(error, "failed to get version number: "); + return FALSE; + } + + /* wait for device ready */ + g_usleep(300000); + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + 0x00, + buf_rep, + sizeof(buf_rep), + error)) { + g_prefix_error(error, "failed to get version number: "); + return FALSE; + } + /* set version */ + version = g_strdup_printf("%u.%u", buf_rep[1], buf_rep[2]); + fu_device_set_version(FU_DEVICE(self), version); + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_detach_raw(FuRts54hubRtd21xxForeground *self, GError **error) +{ + guint8 buf = 0x03; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + 0x6A, + 0x31, + &buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to detach: "); + return FALSE; + } + /* wait for device ready */ + g_usleep(300000); + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_detach_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); + guint8 status = 0xfe; + + if (!fu_rts54hub_rtd21xx_foreground_detach_raw(self, error)) + return FALSE; + if (!fu_rts54hub_rtd21xx_device_read_status_raw(FU_RTS54HUB_RTD21XX_DEVICE(self), + &status, + error)) + return FALSE; + if (status != ISP_STATUS_IDLE_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "detach status was 0x%02x", + status); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_device_retry(device, fu_rts54hub_rtd21xx_foreground_detach_cb, 100, NULL, error); +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); + guint8 buf[] = {ISP_CMD_FW_UPDATE_RESET}; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + /* exit fw mode */ + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to ISP_CMD_FW_UPDATE_RESET: "); + return FALSE; + } + + /* the device needs some time to restart with the new firmware before + * it can be queried again */ + fu_progress_sleep(progress, 60000); + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_exit(FuDevice *device, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); + guint8 buf[] = {ISP_CMD_FW_UPDATE_EXIT}; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to ISP_CMD_FW_UPDATE_EXIT"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_setup(FuDevice *device, GError **error) +{ + FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get version */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_rts54hub_rtd21xx_foreground_exit, + error); + if (locker == NULL) + return FALSE; + if (!fu_rts54hub_rtd21xx_ensure_version_unlocked(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_reload(FuDevice *device, GError **error) +{ + FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open parent device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_rts54hub_rtd21xx_foreground_setup(device, error); +} + +static gboolean +fu_rts54hub_rtd21xx_foreground_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); + const guint8 *fwbuf; + gsize fwbufsz = 0; + guint32 project_addr; + guint8 project_id_count; + guint8 read_buf[10] = {0x0}; + guint8 write_buf[ISP_PACKET_SIZE] = {0x0}; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "setup"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "finish"); + + /* open device */ + locker = fu_device_locker_new(self, error); + if (locker == NULL) + return FALSE; + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + fwbuf = g_bytes_get_data(fw, &fwbufsz); + + /* enable ISP high priority */ + write_buf[0] = ISP_CMD_ENTER_FW_UPDATE; + write_buf[1] = 0x01; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 2, + error)) { + g_prefix_error(error, "failed to enable ISP: "); + return FALSE; + } + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + + /* get project ID address */ + write_buf[0] = ISP_CMD_GET_PROJECT_ID_ADDR; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "failed to get project ID address: "); + return FALSE; + } + + /* read back 6 bytes data */ + g_usleep(I2C_DELAY_AFTER_SEND * 40); + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_STATUS, + read_buf, + 6, + error)) { + g_prefix_error(error, "failed to read project ID: "); + return FALSE; + } + if (read_buf[0] != ISP_STATUS_IDLE_SUCCESS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed project ID with error 0x%02x: ", + read_buf[0]); + return FALSE; + } + + /* verify project ID */ + project_addr = fu_memread_uint32(read_buf + 1, G_BIG_ENDIAN); + project_id_count = read_buf[5]; + write_buf[0] = ISP_CMD_SYNC_IDENTIFY_CODE; + if (!fu_memcpy_safe(write_buf, + sizeof(write_buf), + 0x1, /* dst */ + fwbuf, + fwbufsz, + project_addr, /* src */ + project_id_count, + error)) { + g_prefix_error(error, "failed to write project ID from 0x%04x: ", project_addr); + return FALSE; + } + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + project_id_count + 1, + error)) { + g_prefix_error(error, "failed to send fw update start cmd: "); + return FALSE; + } + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + + /* foreground FW update start command */ + write_buf[0] = ISP_CMD_FW_UPDATE_START; + fu_memwrite_uint16(write_buf + 1, ISP_DATA_BLOCKSIZE, G_BIG_ENDIAN); + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 3, + error)) { + g_prefix_error(error, "failed to send fw update start cmd: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* send data */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + ISP_DATA_BLOCKSIZE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), + NULL, + error)) + return FALSE; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_ISP_DATA_OPCODE, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write @0x%04x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* update finish command */ + if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) + return FALSE; + write_buf[0] = ISP_CMD_FW_UPDATE_ISP_DONE; + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + UC_ISP_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "failed update finish cmd: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_rts54hub_rtd21xx_foreground_init(FuRts54hubRtd21xxForeground *self) +{ +} + +static void +fu_rts54hub_rtd21xx_foreground_class_init(FuRts54hubRtd21xxForegroundClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_rts54hub_rtd21xx_foreground_setup; + klass_device->reload = fu_rts54hub_rtd21xx_foreground_reload; + klass_device->attach = fu_rts54hub_rtd21xx_foreground_attach; + klass_device->detach = fu_rts54hub_rtd21xx_foreground_detach; + klass_device->write_firmware = fu_rts54hub_rtd21xx_foreground_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.h b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.h new file mode 100644 index 0000000000000000000000000000000000000000..7df8e3c038c21f1faf70f654df215e5cff1f72bc --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Ricky Wu + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-rts54hub-rtd21xx-device.h" + +#define FU_TYPE_RTS54HUB_RTD21XX_FOREGROUND (fu_rts54hub_rtd21xx_foreground_get_type()) +G_DECLARE_FINAL_TYPE(FuRts54hubRtd21xxForeground, + fu_rts54hub_rtd21xx_foreground, + FU, + RTS54HUB_RTD21XX_FOREGROUND, + FuRts54hubRtd21xxDevice) diff --git a/fwupd-1.8.6/plugins/rts54hub/meson.build b/fwupd-1.8.6/plugins/rts54hub/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..a1a3e800bd0d31ac41fe96c5e4a469c32868f617 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/meson.build @@ -0,0 +1,18 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginRts54Hub"'] + +plugin_quirks += files('rts54hub.quirk') +plugin_builtins += static_library('fu_plugin_rts54hub', + sources: [ + 'fu-rts54hub-device.c', + 'fu-rts54hub-rtd21xx-device.c', + 'fu-rts54hub-rtd21xx-background.c', + 'fu-rts54hub-rtd21xx-foreground.c', + 'fu-rts54hub-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/rts54hub/rts54hub.quirk b/fwupd-1.8.6/plugins/rts54hub/rts54hub.quirk new file mode 100644 index 0000000000000000000000000000000000000000..ba194423d83ae7158f695ed66467ff8e21e20d45 --- /dev/null +++ b/fwupd-1.8.6/plugins/rts54hub/rts54hub.quirk @@ -0,0 +1,43 @@ +# RTS5423 Development Board +[USB\VID_0BDA&PID_5B00] +Plugin = rts54hub +GType = FuRts54HubDevice +FirmwareSizeMin = 0x20000 +FirmwareSizeMax = 0x40000 + +# Lenovo HotRod +[USB\VID_17EF&PID_30BF] +Plugin = rts54hub +GType = FuRts54HubDevice +Vendor = Lenovo +FirmwareSizeMin = 0x20000 +FirmwareSizeMax = 0x40000 +Children = FuRts54hubRtd21xxForeground|USB\VID_17EF&PID_30BF&I2C_01 +[USB\VID_17EF&PID_30BF&I2C_01] +Plugin = rts54hub +Name = HDMI Converter +Flags = updatable +FirmwareSize = 0x30000 +Rts54TargetAddr = 0x20 +Rts54I2cSpeed = 0x2 +Rts54RegisterAddrLen = 0x04 + +# Acer D501 Dock +[USB\VID_2BEF&PID_1009] +Plugin = rts54hub +GType = FuRts54HubDevice +Name = Acer D501 Dock USB Hub +FirmwareSizeMin = 0x10000 +FirmwareSizeMax = 0x40000 +Children = FuRts54hubRtd21xxBackground|USB\VID_2BEF&PID_1009&I2C_01 +[USB\VID_2BEF&PID_1009&I2C_01] +Plugin = rts54hub +Vendor = Realtek +Name = Acer D501 Dock HDMI Converter +Flags = updatable +FirmwareSizeMin = 0x10000 +FirmwareSizeMax = 0x90000 +Rts54TargetAddr = 0x20 +Rts54I2cSpeed = 0x2 +Rts54RegisterAddrLen = 0x04 + diff --git a/fwupd-1.8.6/plugins/scsi/README.md b/fwupd-1.8.6/plugins/scsi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9227d8a1ba80670a2b5b43770fae36b9c3ed4bde --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/README.md @@ -0,0 +1,34 @@ +# SCSI + +## Introduction + +This plugin adds support for SCSI storage hardware. Most SCSI devices are enumerated and some UFS +devices may also be updatable. + +Firmware is sent in 4kB chunks and activated on next reboot only. + +## 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.jedec.ufs + +## GUID Generation + +These device use the SCSI DeviceInstanceId values, e.g. + +* `SCSI\VEN_HP&DEV_EG0900JETKB&REV_HPD4` +* `SCSI\VEN_HP&DEV_EG0900JETKB` +* `SCSI\VEN_HP` + +## Vendor ID Security + +The vendor ID is set from the vendor, for example set to `SCSI:HP` + +## External Interface Access + +This plugin requires only reading from sysfs for enumeration, but requires using a `sg_io ioctl` +for UFS updates. diff --git a/fwupd-1.8.6/plugins/scsi/fu-scsi-device.c b/fwupd-1.8.6/plugins/scsi/fu-scsi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b1a877cee255d00728ad8bc5eaa411caa40c4864 --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/fu-scsi-device.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-scsi-device.h" + +struct _FuScsiDevice { + FuUdevDevice parent_instance; + guint64 ffu_timeout; +}; + +G_DEFINE_TYPE(FuScsiDevice, fu_scsi_device, FU_TYPE_UDEV_DEVICE) + +#define BUFFER_VENDOR_MODE 0x01 +#define BUFFER_DUFS_MODE 0x02 +#define BUFFER_FFU_MODE 0x0E +#define BUFFER_EHS_MODE 0x1C + +#define SENSE_BUFF_LEN 18 +#define WRITE_BUF_CMDLEN 10 +#define READ_BUF_CMDLEN 10 +#define WRITE_BUFFER_CMD 0x3B +#define READ_BUFFER_CMD 0x3C + +#define FU_SCSI_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static void +fu_scsi_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuScsiDevice *self = FU_SCSI_DEVICE(device); + FU_DEVICE_CLASS(fu_scsi_device_parent_class)->to_string(device, idt, str); + fu_string_append_kx(str, idt, "FfuTimeout", self->ffu_timeout); +} + +static gboolean +fu_scsi_device_probe(FuDevice *device, GError **error) +{ + FuScsiDevice *self = FU_SCSI_DEVICE(device); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + guint64 removable = 0; + g_autofree gchar *vendor_id = NULL; + g_autoptr(FuUdevDevice) ufshci_parent = NULL; + const gchar *subsystem_parents[] = {"pci", "platform", NULL}; + + /* check is valid */ + if (g_strcmp0(g_udev_device_get_devtype(udev_device), "disk") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct devtype=%s, expected disk", + g_udev_device_get_devtype(udev_device)); + return FALSE; + } + if (!g_udev_device_get_property_as_boolean(udev_device, "ID_SCSI")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "has no ID_SCSI"); + return FALSE; + } + + /* vendor sanity */ + if (g_strcmp0(fu_device_get_vendor(device), "ATA") == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no assigned vendor"); + return FALSE; + } + + vendor_id = g_strdup_printf("SCSI:%s", fu_device_get_vendor(device)); + fu_device_add_vendor_id(device, vendor_id); + + /* the ufshci controller could really be on any bus... search in order of priority */ + for (guint i = 0; subsystem_parents[i] != NULL && ufshci_parent == NULL; i++) { + ufshci_parent = fu_udev_device_get_parent_with_subsystem(FU_UDEV_DEVICE(device), + subsystem_parents[i]); + } + if (ufshci_parent != NULL) { + guint64 ufs_features = 0; + + /* check if this is a UFS device */ + g_debug("found ufshci controller at %s", + fu_udev_device_get_sysfs_path(ufshci_parent)); + if (fu_udev_device_get_sysfs_attr_uint64(ufshci_parent, + "device_descriptor/ufs_features", + &ufs_features, + NULL)) { + fu_device_set_summary(device, "UFS device"); + /* least significant bit specifies FFU capability */ + if (ufs_features & 0x1) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_internal_flag(FU_DEVICE(self), + FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_add_protocol(device, "org.jedec.ufs"); + } + if (!fu_udev_device_get_sysfs_attr_uint64(ufshci_parent, + "device_descriptor/ffu_timeout", + &self->ffu_timeout, + error)) { + g_prefix_error(error, "no ffu timeout specified: "); + return FALSE; + } + } + } + + /* add GUIDs */ + fu_device_add_instance_strsafe(device, "VEN", fu_device_get_vendor(device)); + fu_device_add_instance_strsafe(device, "DEV", fu_device_get_name(device)); + fu_device_add_instance_strsafe(device, "REV", fu_device_get_version(device)); + if (!fu_device_build_instance_id_quirk(device, error, "SCSI", "VEN", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "SCSI", "VEN", "DEV", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "SCSI", "VEN", "DEV", "REV", NULL)) + return FALSE; + + /* is internal? */ + if (fu_udev_device_get_sysfs_attr_uint64(FU_UDEV_DEVICE(device), + "removable", + &removable, + NULL)) { + if (removable == 0x0) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "scsi:scsi_target", error); +} + +static FuFirmware * +fu_scsi_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + fu_firmware_set_alignment(firmware, FU_FIRMWARE_ALIGNMENT_4K); + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + return g_steal_pointer(&firmware); +} + +static const gchar * +fu_scsi_device_sense_key_to_string(guint8 key) +{ + if (key == 0x00) + return "no-sense"; + if (key == 0x01) + return "recovered-error"; + if (key == 0x02) + return "not-ready"; + if (key == 0x03) + return "medium-error"; + if (key == 0x04) + return "hardware-error"; + if (key == 0x05) + return "illegal-request"; + if (key == 0x06) + return "unit-attention"; + if (key == 0x07) + return "data-protect"; + if (key == 0x08) + return "blank-check"; + if (key == 0x09) + return "vendor-specific"; + if (key == 0x0A) + return "copy-aborted"; + if (key == 0x0B) + return "aborted-command"; + if (key == 0x0C) + return "equal"; + if (key == 0x0D) + return "volume-overflow"; + if (key == 0x0E) + return "miscompare"; + return NULL; +} + +static gboolean +fu_scsi_device_send_scsi_cmd_v3(FuScsiDevice *self, + const guint8 *cdb, + guint8 cdbsz, + const guint8 *buf, + guint32 bufsz, + gint dir, + GError **error) +{ + guint8 sense_buffer[SENSE_BUFF_LEN] = {0}; + struct sg_io_hdr io_hdr = {.interface_id = 'S'}; + + io_hdr.cmd_len = cdbsz; + io_hdr.mx_sb_len = sizeof(sense_buffer); + io_hdr.dxfer_direction = dir; + io_hdr.dxfer_len = bufsz; + io_hdr.dxferp = (guint8 *)buf; + /* pointer to command buf */ + io_hdr.cmdp = (guint8 *)cdb; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 60000; /* ms */ + + if (g_getenv("FWUPD_SCSI_VERBOSE") != NULL) + g_debug("cmd=0x%x len=0x%x", cdb[0], (guint)bufsz); + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + SG_IO, + (guint8 *)&io_hdr, + NULL, + FU_SCSI_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + + if (io_hdr.status) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Command fail with status %x, senseKey %s, asc 0x%02x, ascq 0x%02x", + io_hdr.status, + fu_scsi_device_sense_key_to_string(sense_buffer[2]), + sense_buffer[12], + sense_buffer[13]); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_scsi_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuScsiDevice *self = FU_SCSI_DEVICE(device); + guint32 chunksz = 0x1000; + guint32 offset = 0; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* prepare chunks */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x00, 0x00, chunksz); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + fu_progress_set_steps(progress, chunks->len); + + /* write each block */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 cdb[WRITE_BUF_CMDLEN] = {WRITE_BUFFER_CMD, + BUFFER_FFU_MODE, + 0x0 /* buf_id */}; + + fu_memwrite_uint24(cdb + 3, offset, G_BIG_ENDIAN); + fu_memwrite_uint24(cdb + 6, fu_chunk_get_data_sz(chk), G_BIG_ENDIAN); + if (!fu_scsi_device_send_scsi_cmd_v3(self, + cdb, + sizeof(cdb), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + SG_DXFER_TO_DEV, + error)) { + g_prefix_error(error, + "SG_IO WRITE BUFFER data error for v3 chunk 0x%x: ", + fu_chunk_get_idx(chk)); + + return FALSE; + } + + /* chunk done */ + fu_progress_step_done(progress); + offset += fu_chunk_get_data_sz(chk); + } + + /* success! */ + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + return TRUE; +} + +static void +fu_scsi_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_scsi_device_init(FuScsiDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_summary(FU_DEVICE(self), "SCSI device"); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_SYNC | + FU_UDEV_DEVICE_FLAG_IOCTL_RETRY); +} + +static void +fu_scsi_device_class_init(FuScsiDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_scsi_device_to_string; + klass_device->probe = fu_scsi_device_probe; + klass_device->prepare_firmware = fu_scsi_device_prepare_firmware; + klass_device->write_firmware = fu_scsi_device_write_firmware; + klass_device->set_progress = fu_scsi_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/scsi/fu-scsi-device.h b/fwupd-1.8.6/plugins/scsi/fu-scsi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..c6c488ee70f3ee6f12f91306daf29f4d52df15ff --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/fu-scsi-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SCSI_DEVICE (fu_scsi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuScsiDevice, fu_scsi_device, FU, SCSI_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/scsi/fu-scsi-plugin.c b/fwupd-1.8.6/plugins/scsi/fu-scsi-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..4e3b1494764193a635d826a1fd474707c4da3bb1 --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/fu-scsi-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-scsi-device.h" +#include "fu-scsi-plugin.h" + +struct _FuScsiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuScsiPlugin, fu_scsi_plugin, FU_TYPE_PLUGIN) + +static void +fu_scsi_plugin_init(FuScsiPlugin *self) +{ +} + +static void +fu_scsi_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "block"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SCSI_DEVICE); +} + +static void +fu_scsi_plugin_class_init(FuScsiPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_scsi_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/scsi/fu-scsi-plugin.h b/fwupd-1.8.6/plugins/scsi/fu-scsi-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..fa20e67c8b5faaec3476ec4acc1fc656f39644f1 --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/fu-scsi-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuScsiPlugin, fu_scsi_plugin, FU, SCSI_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/scsi/meson.build b/fwupd-1.8.6/plugins/scsi/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1dfe8f01f1f4b525607fbc91ed9b713b66d6cb67 --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/meson.build @@ -0,0 +1,19 @@ +if get_option('plugin_scsi').require(gudev.found(), + error_message: 'gudev is needed for plugin_scsi').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginScsi"'] + +plugin_quirks += files('scsi.quirk') +plugin_builtins += static_library('fu_plugin_scsi', + sources: [ + 'fu-scsi-plugin.c', + 'fu-scsi-device.c', + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/scsi/scsi.quirk b/fwupd-1.8.6/plugins/scsi/scsi.quirk new file mode 100644 index 0000000000000000000000000000000000000000..07254469bdf0d4d95f9b8601ebc2d72579854d89 --- /dev/null +++ b/fwupd-1.8.6/plugins/scsi/scsi.quirk @@ -0,0 +1,24 @@ +[BLOCK] +Plugin = scsi + +[SCSI\VEN_LSI&DEV_VirtualSES] +Flags = no-probe +[SCSI\VEN_Marvell&DEV_Console] +Flags = no-probe + +[SCSI\VEN_HP&DEV_EK0800JVYPN&REV_HPD5] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_EK0800JVYPN&REV_HPD6] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_EO1600JVYPP&REV_HPD5] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_EO1600JVYPP&REV_HPD6] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_MK0800JVYPQ&REV_HPD5] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_MK0800JVYPQ&REV_HPD6] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_MO1600JVYPR&REV_HPD5] +Issue = MTX-6f54e8621f4c490a918defdab6 +[SCSI\VEN_HP&DEV_MO1600JVYPR&REV_HPD6] +Issue = MTX-6f54e8621f4c490a918defdab6 diff --git a/fwupd-1.8.6/plugins/steelseries/README.md b/fwupd-1.8.6/plugins/steelseries/README.md new file mode 100644 index 0000000000000000000000000000000000000000..347068be41eb086544bbe0a33148b759f8cc1a2f --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/README.md @@ -0,0 +1,72 @@ +# SteelSeries + +## Introduction + +This plugin allows to update firmware on SteelSeries gamepads: + +* Stratus Duo +* Stratus Duo USB wireless adapter +* Stratus+ +* Aerox 3 Wireless +* Rival 3 Wireless + +SteelSeries Rival 100 gaming mice support is limited by getting the correct +version number. These mice have updatable firmware but so far no updates are +available from the vendor. + +This plugin supports the following protocol IDs: + +* com.steelseries.fizz +* com.steelseries.gamepad +* com.steelseries.sonic + +## 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` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### Flags:is-receiver + +The device is a USB receiver. + +Since 1.8.1 + +## Update Behavior + +### Gamepad + +Gamepad and/or its wireless adapter must be connected to host via USB cable +to apply an update. The device is switched to bootloader mode to flash +updates, and is reset automatically to new firmware after flashing. + +### Mice + +The device is not upgradable and thus requires no vendor ID set. + +### Wireless Mice + +### Rival 3 Wireless + +The mouse switch button underneath must be set to 2.4G, and its 2.4G USB +Wireless adapter must be connected to host. + +### Aerox 3 Wireless + +The mouse switch button underneath must be set to 2.4G, and its 2.4G USB +Wireless adapter must be connected to host; or the mouse must be connected to +host via the USB-A to USB-C cable. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1038` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-device.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-device.c new file mode 100644 index 0000000000000000000000000000000000000000..da702c3f406c52de60f6b8dcca2432f0eabe28b4 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-device.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-device.h" + +typedef struct { + gint iface_idx_offset; + guint8 iface_idx; + guint8 ep; + gsize ep_in_size; +} FuSteelseriesDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuSteelseriesDevice, fu_steelseries_device, FU_TYPE_USB_DEVICE) +#define GET_PRIVATE(o) (fu_steelseries_device_get_instance_private(o)) + +/* @iface_idx_offset can be negative to specify from the end */ +void +fu_steelseries_device_set_iface_idx_offset(FuSteelseriesDevice *self, gint iface_idx_offset) +{ + FuSteelseriesDevicePrivate *priv = GET_PRIVATE(self); + priv->iface_idx_offset = iface_idx_offset; +} + +gboolean +fu_steelseries_device_cmd(FuSteelseriesDevice *self, + guint8 *data, + gsize datasz, + gboolean answer, + GError **error) +{ + FuSteelseriesDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize actual_len = 0; + gboolean ret; + + 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, + 0x09, + 0x0200, + priv->iface_idx, + data, + datasz, + &actual_len, + STEELSERIES_TRANSACTION_TIMEOUT, + NULL, + error); + if (!ret) { + g_prefix_error(error, "failed to do control transfer: "); + 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; + } + + /* cleanup the buffer before receiving any data */ + memset(data, 0x00, datasz); + + /* do not expect the answer from device */ + if (answer != TRUE) + return TRUE; + + ret = g_usb_device_interrupt_transfer(usb_device, + priv->ep, + data, + priv->ep_in_size, + &actual_len, + STEELSERIES_TRANSACTION_TIMEOUT, + NULL, + error); + if (!ret) { + g_prefix_error(error, "failed to do EP transfer: "); + return FALSE; + } + if (actual_len != priv->ep_in_size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", + actual_len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_device_probe(FuDevice *device, GError **error) +{ +#if G_USB_CHECK_VERSION(0, 3, 3) + FuSteelseriesDevice *self = FU_STEELSERIES_DEVICE(device); + FuSteelseriesDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + GUsbInterface *iface = NULL; + GUsbEndpoint *ep = NULL; + guint8 ep_id; + guint16 packet_size; + g_autoptr(GPtrArray) ifaces = NULL; + g_autoptr(GPtrArray) endpoints = NULL; + + ifaces = g_usb_device_get_interfaces(usb_device, error); + if (ifaces == NULL) + return FALSE; + + /* use the correct interface for interrupt transfer, either specifying an absolute offset, + * or a negative offset value for the "last" one */ + if (priv->iface_idx_offset >= 0) { + if ((guint)priv->iface_idx_offset > ifaces->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "update interface 0x%x not found", + (guint)priv->iface_idx_offset); + return FALSE; + } + priv->iface_idx = priv->iface_idx_offset; + } else { + priv->iface_idx = ifaces->len - 1; + } + iface = g_ptr_array_index(ifaces, priv->iface_idx); + + endpoints = g_usb_interface_get_endpoints(iface); + /* expecting to have only one endpoint for communication */ + if (endpoints == NULL || endpoints->len != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "endpoint not found"); + return FALSE; + } + + ep = g_ptr_array_index(endpoints, 0); + ep_id = g_usb_endpoint_get_address(ep); + packet_size = g_usb_endpoint_get_maximum_packet_size(ep); + + priv->ep = ep_id; + priv->ep_in_size = packet_size; + + fu_usb_device_add_interface(FU_USB_DEVICE(self), priv->iface_idx); + + /* success */ + return TRUE; +#else + /* failed */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "this version of GUsb is not supported"); + return FALSE; +#endif +} + +static void +fu_steelseries_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuSteelseriesDevice *self = FU_STEELSERIES_DEVICE(device); + FuSteelseriesDevicePrivate *priv = GET_PRIVATE(self); + + FU_DEVICE_CLASS(fu_steelseries_device_parent_class)->to_string(device, idt, str); + + fu_string_append_kx(str, idt, "Interface", priv->iface_idx); + fu_string_append_kx(str, idt, "Endpoint", priv->ep); +} + +static void +fu_steelseries_device_init(FuSteelseriesDevice *self) +{ + fu_device_register_private_flag(FU_DEVICE(self), + FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER, + "is-receiver"); +} + +static void +fu_steelseries_device_class_init(FuSteelseriesDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_steelseries_device_to_string; + klass_device->probe = fu_steelseries_device_probe; +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-device.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-device.h new file mode 100644 index 0000000000000000000000000000000000000000..d1775bf807c847e2bfcf429e4c41b3490a64f6fa --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-device.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2021 Denis Pynkin + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_STEELSERIES_DEVICE (fu_steelseries_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuSteelseriesDevice, + fu_steelseries_device, + FU, + STEELSERIES_DEVICE, + FuUsbDevice) + +struct _FuSteelseriesDeviceClass { + FuUsbDeviceClass parent_class; +}; + +#define STEELSERIES_BUFFER_CONTROL_SIZE 64 +#define STEELSERIES_TRANSACTION_TIMEOUT 5000 + +/** + * FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER: + * + * The device is a USB receiver. + * + * Since 1.8.1 + */ +#define FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER (1 << 0) + +void +fu_steelseries_device_set_iface_idx_offset(FuSteelseriesDevice *self, gint iface_idx_offset); +gboolean +fu_steelseries_device_cmd(FuSteelseriesDevice *self, + guint8 *data, + gsize datasz, + gboolean answer, + GError **error); diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-firmware.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..fc1ae5aaafab13c874eb8be80a2856fa672f6f61 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-firmware.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-steelseries-firmware.h" + +struct _FuSteelseriesFirmware { + FuFirmwareClass parent_instance; + guint32 checksum; +}; + +G_DEFINE_TYPE(FuSteelseriesFirmware, fu_steelseries_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_steelseries_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSteelseriesFirmware *self = FU_STEELSERIES_FIRMWARE(firmware); + guint32 checksum_tmp; + guint32 checksum; + + if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + g_bytes_get_size(fw) - sizeof(checksum), + &checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + checksum_tmp = + fu_crc32(g_bytes_get_data(fw, NULL), g_bytes_get_size(fw) - sizeof(checksum_tmp)); + if (checksum_tmp != checksum) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "checksum mismatch, got 0x%08x, expected 0x%08x", + checksum_tmp, + checksum); + return FALSE; + } + g_debug("ignoring checksum mismatch, got 0x%08x, expected 0x%08x", + checksum_tmp, + checksum); + } + + self->checksum = checksum; + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); + + /* success */ + return TRUE; +} + +static void +fu_steelseries_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuSteelseriesFirmware *self = FU_STEELSERIES_FIRMWARE(firmware); + + fu_xmlb_builder_insert_kx(bn, "checksum", self->checksum); +} + +static void +fu_steelseries_firmware_init(FuSteelseriesFirmware *self) +{ +} + +static void +fu_steelseries_firmware_class_init(FuSteelseriesFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_steelseries_firmware_parse; + klass_firmware->export = fu_steelseries_firmware_export; +} + +FuFirmware * +fu_steelseries_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_STEELSERIES_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-firmware.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..bb8363a4613fca45ba929121cbc5ca78f502cd5c --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-firmware.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_STEELSERIES_FIRMWARE (fu_steelseries_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesFirmware, + fu_steelseries_firmware, + FU, + STEELSERIES_FIRMWARE, + FuFirmware) + +FuFirmware * +fu_steelseries_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-hid.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-hid.c new file mode 100644 index 0000000000000000000000000000000000000000..7763ee8467905944ebfbaebfb730ab72319c48ee --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-hid.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-fizz-hid.h" + +#define STEELSERIES_BUFFER_REPORT_SIZE 64 + 1 +#define STEELSERIES_REPORT_TIMEOUT 5000 + +#define STEELSERIES_HID_GET_REPORT 0x04U + +#define STEELSERIES_HID_VERSION_COMMAND 0x90U +#define STEELSERIES_HID_VERSION_REPORT_ID_OFFSET 0x00U +#define STEELSERIES_HID_VERSION_COMMAND_OFFSET 0x01U +#define STEELSERIES_HID_VERSION_MODE_OFFSET 0x02U + +struct _FuSteelseriesFizzHid { + FuUdevDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesFizzHid, fu_steelseries_fizz_hid, FU_TYPE_UDEV_DEVICE) + +static gboolean +fu_steelseries_fizz_hid_command(FuDevice *device, guint8 *data, gsize datasz, GError **error) +{ + gboolean ret; + + ret = fu_udev_device_pwrite(FU_UDEV_DEVICE(device), 0, data, datasz, error); + if (!ret) { + g_prefix_error(error, "failed to write report: "); + return FALSE; + } + + /* cleanup the buffer before receiving any data */ + memset(data, 0x00, datasz); + + ret = fu_udev_device_pread(FU_UDEV_DEVICE(device), 0, data, datasz, error); + if (!ret) { + g_prefix_error(error, "failed to read report: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gchar * +fu_steelseries_fizz_hid_get_version(FuDevice *device, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_REPORT_SIZE] = {0}; + const guint8 report_id = STEELSERIES_HID_GET_REPORT; + const guint8 cmd = STEELSERIES_HID_VERSION_COMMAND; + const guint8 mode = 0U; /* string */ + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_HID_VERSION_REPORT_ID_OFFSET, + report_id, + error)) + return NULL; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_HID_VERSION_COMMAND_OFFSET, + cmd, + error)) + return NULL; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_HID_VERSION_MODE_OFFSET, + mode, + error)) + return NULL; + + if (g_getenv("FWUPD_STEELSERIES_HID_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Version", data, sizeof(data)); + if (!fu_steelseries_fizz_hid_command(device, data, sizeof(data), error)) + return NULL; + if (g_getenv("FWUPD_STEELSERIES_HID_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Version", data, sizeof(data)); + + /* success */ + return fu_strsafe((const gchar *)&data[1], sizeof(data) - 1); +} + +static gboolean +fu_steelseries_fizz_hid_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FwupdRequest) request = NULL; + g_autofree gchar *msg = NULL; + + /* the user has to do something */ + msg = g_strdup_printf( + "%s needs to be manually connected either via the USB cable, " + "or via the 2.4G USB Wireless adapter to start the update. " + "Please plug either the USB-C cable and put the switch button underneath to off, " + "or the 2.4G USB Wireless adapter and put the switch button underneath to 2.4G.", + fu_device_get_name(device)); + request = fwupd_request_new(); + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_PRESS_UNLOCK); + fwupd_request_set_message(request, msg); + fu_device_emit_request(device, request); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_hid_setup(FuDevice *device, GError **error) +{ + g_autofree gchar *version = NULL; + + version = fu_steelseries_fizz_hid_get_version(device, error); + if (version == NULL) + return FALSE; + fu_device_set_version(device, version); + + /* success */ + return TRUE; +} + +static void +fu_steelseries_fizz_hid_class_init(FuSteelseriesFizzHidClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->setup = fu_steelseries_fizz_hid_setup; + klass_device->detach = fu_steelseries_fizz_hid_detach; +} + +static void +fu_steelseries_fizz_hid_init(FuSteelseriesFizzHid *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_physical_id(FU_DEVICE(self), "hid"); + fu_device_add_protocol(FU_DEVICE(self), "com.steelseries.fizz"); + fu_device_set_remove_delay(FU_DEVICE(self), 300000); /* 5min */ +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-hid.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-hid.h new file mode 100644 index 0000000000000000000000000000000000000000..4c488077f7bdbfcfaf2dbc5fc29e33cde235e8f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-hid.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_STEELSERIES_FIZZ_HID (fu_steelseries_fizz_hid_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesFizzHid, + fu_steelseries_fizz_hid, + FU, + STEELSERIES_FIZZ_HID, + FuHidDevice) diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-tunnel.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-tunnel.c new file mode 100644 index 0000000000000000000000000000000000000000..553434d70bec5fb6ad0d3aa32af62972b91d1b81 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-tunnel.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-firmware.h" +#include "fu-steelseries-fizz-tunnel.h" + +struct _FuSteelseriesFizzTunnel { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesFizzTunnel, fu_steelseries_fizz_tunnel, FU_TYPE_DEVICE) + +static gboolean +fu_steelseries_fizz_tunnel_ping(FuDevice *device, gboolean *reached, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint8 status; + guint8 level; + g_autoptr(GError) error_local = NULL; + + if (!fu_steelseries_fizz_get_connection_status(parent, &status, error)) { + g_prefix_error(error, "failed to get connection status: "); + return FALSE; + } + g_debug("ConnectionStatus: %u", status); + *reached = status != STEELSERIES_FIZZ_CONNECTION_STATUS_NOT_CONNECTED; + if (!*reached) { + /* success */ + return TRUE; + } + + /* ping device anyway */ + if (!fu_steelseries_fizz_get_battery_level(fu_device_get_parent(device), + TRUE, + &level, + &error_local)) { + *reached = FALSE; + + if (!g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_TIMED_OUT)) { + /* failure */ + *error = g_steal_pointer(&error_local); + return FALSE; + } + + /* success */ + return TRUE; + } + g_debug("BatteryLevel: 0x%02x", level); + /* + * CHARGING: Most significant bit. When bit is set to 1 it means battery is currently + * charging/plugged in + * + * LEVEL: 7 least significant bit value of the battery. Values are between 2-21, to get % + * you can do (LEVEL - 1) * 5 + */ + fu_device_set_battery_level(device, + ((level & STEELSERIES_FIZZ_BATTERY_LEVEL_STATUS_BITS) - 1U) * + 5U); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_tunnel_wait_for_reconnect_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint8 status; + + if (!fu_steelseries_fizz_get_connection_status(parent, &status, error)) { + g_prefix_error(error, "failed to get connection status: "); + return FALSE; + } + g_debug("ConnectionStatus: %u", status); + if (status == STEELSERIES_FIZZ_CONNECTION_STATUS_NOT_CONNECTED) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "device is unreachable"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_tunnel_wait_for_reconnect(FuDevice *device, guint delay, GError **error) +{ + return fu_device_retry_full(device, + fu_steelseries_fizz_tunnel_wait_for_reconnect_cb, + delay / 1000, + 1000, + NULL, + error); +} + +static gboolean +fu_steelseries_fizz_tunnel_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint remove_delay = fu_device_get_remove_delay(device); + g_autoptr(GError) error_local = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 67, "sleep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 33, NULL); + + if (!fu_steelseries_fizz_reset(parent, + TRUE, + STEELSERIES_FIZZ_RESET_MODE_NORMAL, + &error_local)) + g_warning("failed to reset: %s", error_local->message); + fu_progress_step_done(progress); + + /* wait for receiver to reset the connection status to 0 */ + fu_progress_sleep(fu_progress_get_child(progress), 2000); /* 2 s */ + remove_delay -= 2000; + fu_progress_step_done(progress); + + if (!fu_steelseries_fizz_tunnel_wait_for_reconnect(device, remove_delay, error)) { + g_prefix_error(error, "device %s did not come back", fu_device_get_id(device)); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_tunnel_probe(FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(parent)); + guint16 release; + + /* set the version if the release has been set */ + release = g_usb_device_get_release(usb_device); + if (release != 0x0 && + fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN) { + g_autofree gchar *version = NULL; + version = fu_version_from_uint16(release, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version(device, version); + } + + /* add GUIDs in order of priority */ + fu_device_add_instance_str(device, "PROTOCOL", "FIZZ_TUNNEL"); + fu_device_add_instance_u16(device, "VID", g_usb_device_get_vid(usb_device)); + fu_device_add_instance_u16(device, "PID", g_usb_device_get_pid(usb_device)); + fu_device_add_instance_u16(device, "REV", release); + fu_device_build_instance_id_quirk(device, NULL, "STEELSERIES", "VID", "PROTOCOL", NULL); + fu_device_build_instance_id(device, NULL, "STEELSERIES", "VID", "PID", "PROTOCOL", NULL); + fu_device_build_instance_id(device, + NULL, + "STEELSERIES", + "VID", + "PID", + "REV", + "PROTOCOL", + NULL); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_tunnel_setup(FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint32 calculated_crc; + guint32 stored_crc; + gboolean reached; + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *version = NULL; + + /* ping */ + if (!fu_steelseries_fizz_tunnel_ping(device, &reached, &error_local)) { + g_debug("ignoring error on ping: %s", error_local->message); + + /* success */ + return TRUE; + } + if (!reached) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNREACHABLE); + + /* success */ + return TRUE; + } + + version = fu_steelseries_fizz_get_version(parent, TRUE, error); + if (version == NULL) { + g_prefix_error(error, "failed to get version: "); + return FALSE; + } + fu_device_set_version(device, version); + + if (!fu_steelseries_fizz_get_crc32_fs(parent, + TRUE, + fs, + id, + &calculated_crc, + &stored_crc, + error)) { + g_prefix_error(error, + "failed to get file CRC32 from FS 0x%02x ID 0x%02x: ", + fs, + id); + return FALSE; + } + + if (calculated_crc != stored_crc) { + g_warning("%s: checksum mismatch, got 0x%08x, expected 0x%08x", + fu_device_get_name(device), + calculated_crc, + stored_crc); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_tunnel_poll(FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint32 calculated_crc; + guint32 stored_crc; + gboolean reached; + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree gchar *version = NULL; + + /* open */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + if (!fu_steelseries_fizz_tunnel_ping(device, &reached, error)) { + g_prefix_error(error, "failed to ping: "); + return FALSE; + } + + if (!reached) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNREACHABLE); + + /* success */ + return TRUE; + } + + /* "deferred" setup */ + version = fu_steelseries_fizz_get_version(parent, TRUE, &error_local); + if (version == NULL) { + g_debug("ignoring error on version: %s", error_local->message); + + /* success */ + return TRUE; + } + fu_device_set_version(device, version); + + if (!fu_steelseries_fizz_get_crc32_fs(parent, + TRUE, + fs, + id, + &calculated_crc, + &stored_crc, + &error_local)) { + g_debug("ignoring error on get file CRC32 from FS 0x%02x ID 0x%02x: %s", + fs, + id, + error_local->message); + + /* success */ + return TRUE; + } + + if (calculated_crc != stored_crc) + g_warning("%s: checksum mismatch, got 0x%08x, expected 0x%08x", + fu_device_get_name(device), + calculated_crc, + stored_crc); + + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_UNREACHABLE); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_tunnel_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, NULL); + + if (!fu_steelseries_fizz_write_firmware_fs(parent, + TRUE, + fs, + id, + firmware, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_steelseries_fizz_tunnel_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + g_autoptr(FuFirmware) firmware = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 100, NULL); + + firmware = fu_steelseries_fizz_read_firmware_fs(parent, + TRUE, + fs, + id, + fu_device_get_firmware_size_max(device), + fu_progress_get_child(progress), + error); + if (firmware == NULL) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_steal_pointer(&firmware); +} + +static void +fu_steelseries_fizz_tunnel_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 6, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_steelseries_fizz_tunnel_class_init(FuSteelseriesFizzTunnelClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->attach = fu_steelseries_fizz_tunnel_attach; + klass_device->probe = fu_steelseries_fizz_tunnel_probe; + klass_device->setup = fu_steelseries_fizz_tunnel_setup; + klass_device->poll = fu_steelseries_fizz_tunnel_poll; + klass_device->write_firmware = fu_steelseries_fizz_tunnel_write_firmware; + klass_device->read_firmware = fu_steelseries_fizz_tunnel_read_firmware; + klass_device->set_progress = fu_steelseries_fizz_tunnel_set_progress; +} + +static void +fu_steelseries_fizz_tunnel_init(FuSteelseriesFizzTunnel *self) +{ + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_AUTO_PAUSE_POLLING); + fu_device_add_protocol(FU_DEVICE(self), "com.steelseries.fizz"); + fu_device_set_logical_id(FU_DEVICE(self), "tunnel"); + fu_device_set_install_duration(FU_DEVICE(self), 38); /* 38 s */ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); /* 10 s */ + fu_device_set_poll_interval(FU_DEVICE(self), 60000); /* 1 min */ + fu_device_set_battery_threshold(FU_DEVICE(self), 20); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_STEELSERIES_FIRMWARE); +} + +FuSteelseriesFizzTunnel * +fu_steelseries_fizz_tunnel_new(FuSteelseriesFizz *parent) +{ + return g_object_new(FU_TYPE_STEELSERIES_FIZZ_TUNNEL, "parent", FU_DEVICE(parent), NULL); +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-tunnel.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-tunnel.h new file mode 100644 index 0000000000000000000000000000000000000000..2f9136e1260460cc4819fac7dbc4bc0ed34f86a5 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz-tunnel.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-steelseries-fizz.h" + +#define FU_TYPE_STEELSERIES_FIZZ_TUNNEL (fu_steelseries_fizz_tunnel_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesFizzTunnel, + fu_steelseries_fizz_tunnel, + FU, + STEELSERIES_FIZZ_TUNNEL, + FuDevice) + +FuSteelseriesFizzTunnel * +fu_steelseries_fizz_tunnel_new(FuSteelseriesFizz *parent); diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz.c new file mode 100644 index 0000000000000000000000000000000000000000..afa83996b768f33a421ed73b32d0149d29306adc --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz.c @@ -0,0 +1,959 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-firmware.h" +#include "fu-steelseries-fizz-tunnel.h" +#include "fu-steelseries-fizz.h" + +#define STEELSERIES_BUFFER_TRANSFER_SIZE 52 + +#define STEELSERIES_FIZZ_COMMAND_ERROR_SUCCESS 0 +#define STEELSERIES_FIZZ_COMMAND_ERROR_FILE_NOT_FOUND 1 +#define STEELSERIES_FIZZ_COMMAND_ERROR_FILE_TOO_SHORT 2 +#define STEELSERIES_FIZZ_COMMAND_ERROR_FLASH_FAILED 3 +#define STEELSERIES_FIZZ_COMMAND_ERROR_PERMISSION_DENIED 4 +#define STEELSERIES_FIZZ_COMMAND_ERROR_OPERATION_NO_SUPPORTED 5 + +#define STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT 1U << 6 + +#define STEELSERIES_FIZZ_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_ERROR_OFFSET 0x01U + +#define STEELSERIES_FIZZ_VERSION_COMMAND 0x90U +#define STEELSERIES_FIZZ_VERSION_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_VERSION_MODE_OFFSET 0x01U + +#define STEELSERIES_FIZZ_BATTERY_LEVEL_COMMAND 0x92U +#define STEELSERIES_FIZZ_BATTERY_LEVEL_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_BATTERY_LEVEL_LEVEL_OFFSET 0x01U + +#define STEELSERIES_FIZZ_PAIRED_STATUS_COMMAND 0xBBU +#define STEELSERIES_FIZZ_PAIRED_STATUS_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_PAIRED_STATUS_STATUS_OFFSET 0x01U + +#define STEELSERIES_FIZZ_CONNECTION_STATUS_COMMAND 0xBCU +#define STEELSERIES_FIZZ_CONNECTION_STATUS_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_CONNECTION_STATUS_STATUS_OFFSET 0x01U + +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_COMMAND 0x03U +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_FILESYSTEM_OFFSET 0x01U +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_ID_OFFSET 0x02U +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_SIZE_OFFSET 0x03U +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_OFFSET_OFFSET 0x05U +#define STEELSERIES_FIZZ_WRITE_ACCESS_FILE_DATA_OFFSET 0x09U + +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_COMMAND 0x83U +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_FILESYSTEM_OFFSET 0x01U +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_ID_OFFSET 0x02U +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_SIZE_OFFSET 0x03U +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_OFFSET_OFFSET 0x05U +#define STEELSERIES_FIZZ_READ_ACCESS_FILE_DATA_OFFSET 0x02U + +#define STEELSERIES_FIZZ_ERASE_FILE_COMMAND 0x02U +#define STEELSERIES_FIZZ_ERASE_FILE_COMMAND_OFFSET 0x0U +#define STEELSERIES_FIZZ_ERASE_FILE_FILESYSTEM_OFFSET 0x1U +#define STEELSERIES_FIZZ_ERASE_FILE_ID_OFFSET 0x2U + +#define STEELSERIES_FIZZ_RESET_COMMAND 0x01U +#define STEELSERIES_FIZZ_RESET_COMMAND_OFFSET 0x0U +#define STEELSERIES_FIZZ_RESET_MODE_OFFSET 0x1U + +#define STEELSERIES_FIZZ_FILE_CRC32_COMMAND 0x84U +#define STEELSERIES_FIZZ_FILE_CRC32_COMMAND_OFFSET 0x00U +#define STEELSERIES_FIZZ_FILE_CRC32_FILESYSTEM_OFFSET 0x01U +#define STEELSERIES_FIZZ_FILE_CRC32_ID_OFFSET 0x02U +#define STEELSERIES_FIZZ_FILE_CRC32_CALCULATED_CRC_OFFSET 0x02U +#define STEELSERIES_FIZZ_FILE_CRC32_STORED_CRC_OFFSET 0x06U + +struct _FuSteelseriesFizz { + FuSteelseriesDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesFizz, fu_steelseries_fizz, FU_TYPE_STEELSERIES_DEVICE) + +static gboolean +fu_steelseries_fizz_command_error_to_error(guint8 cmd, guint8 err, GError **error) +{ + /* success */ + if (err == STEELSERIES_FIZZ_COMMAND_ERROR_SUCCESS) + return TRUE; + + if (err == STEELSERIES_FIZZ_COMMAND_ERROR_FILE_NOT_FOUND) { + g_set_error(error, + G_IO_ERROR, + G_FILE_ERROR_NOENT, + "command 0x%02x returned error 0x%02x", + cmd, + err); + return FALSE; + } + + /* targeted offset is past the file end */ + if (err == STEELSERIES_FIZZ_COMMAND_ERROR_FILE_TOO_SHORT) { + g_set_error(error, + G_IO_ERROR, + G_FILE_ERROR_NOSPC, + "command 0x%02x returned error 0x%02x", + cmd, + err); + return FALSE; + } + + /* when internal flash returns error */ + if (err == STEELSERIES_FIZZ_COMMAND_ERROR_FLASH_FAILED) { + g_set_error(error, + G_IO_ERROR, + G_FILE_ERROR_IO, + "command 0x%02x returned error 0x%02x", + cmd, + err); + return FALSE; + } + + /* USB API doesn't have permission to access this file */ + if (err == STEELSERIES_FIZZ_COMMAND_ERROR_PERMISSION_DENIED) { + g_set_error(error, + G_IO_ERROR, + G_FILE_ERROR_ACCES, + "command 0x%02x returned error 0x%02x", + cmd, + err); + return FALSE; + } + + /* USB API doesn't have permission to access this file */ + if (err == STEELSERIES_FIZZ_COMMAND_ERROR_OPERATION_NO_SUPPORTED) { + g_set_error(error, + G_IO_ERROR, + G_FILE_ERROR_PERM, + "command 0x%02x returned error 0x%02x", + cmd, + err); + return FALSE; + } + + /* fallback */ + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "command 0x%02x returned error 0x%02x", + cmd, + err); + return FALSE; +} + +static gboolean +fu_steelseries_fizz_command_and_check_error(FuDevice *device, + guint8 *data, + gsize datasz, + GError **error) +{ + gint gerr = G_FILE_ERROR_FAILED; + const guint8 command = data[0]; + guint8 err; + guint8 cmd; + + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), data, datasz, TRUE, error)) + return FALSE; + + if (!fu_memread_uint8_safe(data, datasz, STEELSERIES_FIZZ_COMMAND_OFFSET, &cmd, error)) + return FALSE; + + if (cmd != command) { + g_set_error(error, + G_IO_ERROR, + gerr, + "command invalid, got 0x%02x, expected 0x%02x", + cmd, + command); + return FALSE; + } + + if (!fu_memread_uint8_safe(data, datasz, STEELSERIES_FIZZ_ERROR_OFFSET, &err, error)) + return FALSE; + + return fu_steelseries_fizz_command_error_to_error(cmd, err, error); +} + +gchar * +fu_steelseries_fizz_get_version(FuDevice *device, gboolean tunnel, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_VERSION_COMMAND; + const guint8 mode = 0U; /* string */ + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_VERSION_COMMAND_OFFSET, + cmd, + error)) + return NULL; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_VERSION_MODE_OFFSET, + mode, + error)) + return NULL; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Version", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return NULL; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Version", data, sizeof(data)); + + /* success */ + return fu_strsafe((const gchar *)data, sizeof(data)); +} + +gboolean +fu_steelseries_fizz_write_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + const guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_WRITE_ACCESS_FILE_COMMAND; + g_autoptr(GPtrArray) chunks = NULL; + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + chunks = fu_chunk_array_new(buf, bufsz, 0x0, 0x0, STEELSERIES_BUFFER_TRANSFER_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + const guint16 size = fu_chunk_get_data_sz(chk); + const guint32 offset = fu_chunk_get_address(chk); + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_WRITE_ACCESS_FILE_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_WRITE_ACCESS_FILE_FILESYSTEM_OFFSET, + fs, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_WRITE_ACCESS_FILE_ID_OFFSET, + id, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_FIZZ_WRITE_ACCESS_FILE_SIZE_OFFSET, + size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint32_safe(data, + sizeof(data), + STEELSERIES_FIZZ_WRITE_ACCESS_FILE_OFFSET_OFFSET, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memcpy_safe(data, + sizeof(data), + STEELSERIES_FIZZ_WRITE_ACCESS_FILE_DATA_OFFSET, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "AccessFile", data, sizeof(data)); + if (!fu_steelseries_fizz_command_and_check_error(device, data, sizeof(data), error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "AccessFile", data, sizeof(data)); + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_erase_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_ERASE_FILE_COMMAND; + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_ERASE_FILE_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_ERASE_FILE_FILESYSTEM_OFFSET, + fs, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_ERASE_FILE_ID_OFFSET, + id, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "EraseFile", data, sizeof(data)); + if (!fu_steelseries_fizz_command_and_check_error(device, data, sizeof(data), error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "EraseFile", data, sizeof(data)); + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_reset(FuDevice *device, gboolean tunnel, guint8 mode, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_RESET_COMMAND; + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_RESET_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_RESET_MODE_OFFSET, + mode, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Reset", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_get_crc32_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + guint32 *calculated_crc, + guint32 *stored_crc, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_FILE_CRC32_COMMAND; + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_FILE_CRC32_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_FILE_CRC32_FILESYSTEM_OFFSET, + fs, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_FILE_CRC32_ID_OFFSET, + id, + error)) + + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "FileCRC32", data, sizeof(data)); + if (!fu_steelseries_fizz_command_and_check_error(device, data, sizeof(data), error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "FileCRC32", data, sizeof(data)); + + if (!fu_memread_uint32_safe(data, + sizeof(data), + STEELSERIES_FIZZ_FILE_CRC32_CALCULATED_CRC_OFFSET, + calculated_crc, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memread_uint32_safe(data, + sizeof(data), + STEELSERIES_FIZZ_FILE_CRC32_STORED_CRC_OFFSET, + stored_crc, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_read_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_READ_ACCESS_FILE_COMMAND; + g_autoptr(GPtrArray) chunks = NULL; + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + chunks = fu_chunk_array_mutable_new(buf, bufsz, 0x0, 0x0, STEELSERIES_BUFFER_TRANSFER_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + const guint16 size = fu_chunk_get_data_sz(chk); + const guint32 offset = fu_chunk_get_address(chk); + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_READ_ACCESS_FILE_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_READ_ACCESS_FILE_FILESYSTEM_OFFSET, + fs, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_READ_ACCESS_FILE_ID_OFFSET, + id, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_FIZZ_READ_ACCESS_FILE_SIZE_OFFSET, + size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint32_safe(data, + sizeof(data), + STEELSERIES_FIZZ_READ_ACCESS_FILE_OFFSET_OFFSET, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "AccessFile", data, sizeof(data)); + if (!fu_steelseries_fizz_command_and_check_error(device, data, sizeof(data), error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "AccessFile", data, sizeof(data)); + + if (!fu_memcpy_safe(fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + 0x00, + data, + sizeof(data), + STEELSERIES_FIZZ_READ_ACCESS_FILE_DATA_OFFSET, + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_get_battery_level(FuDevice *device, + gboolean tunnel, + guint8 *level, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + guint8 cmd = STEELSERIES_FIZZ_BATTERY_LEVEL_COMMAND; + + if (tunnel) + cmd |= STEELSERIES_FIZZ_COMMAND_TUNNEL_BIT; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_BATTERY_LEVEL_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "BatteryLevel", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "BatteryLevel", data, sizeof(data)); + + if (!fu_memread_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_BATTERY_LEVEL_LEVEL_OFFSET, + level, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_get_paired_status(FuDevice *device, guint8 *status, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + const guint8 cmd = STEELSERIES_FIZZ_PAIRED_STATUS_COMMAND; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_PAIRED_STATUS_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "PairedStatus", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "PairedStatus", data, sizeof(data)); + + if (!fu_memread_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_PAIRED_STATUS_STATUS_OFFSET, + status, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_get_connection_status(FuDevice *device, guint8 *status, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + const guint8 cmd = STEELSERIES_FIZZ_CONNECTION_STATUS_COMMAND; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_CONNECTION_STATUS_COMMAND_OFFSET, + cmd, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ConnectionStatus", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ConnectionStatus", data, sizeof(data)); + + if (!fu_memread_uint8_safe(data, + sizeof(data), + STEELSERIES_FIZZ_CONNECTION_STATUS_STATUS_OFFSET, + status, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + if (!fu_steelseries_fizz_reset(device, + FALSE, + STEELSERIES_FIZZ_RESET_MODE_NORMAL, + &error_local)) + g_warning("failed to reset: %s", error_local->message); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_setup(FuDevice *device, GError **error) +{ + guint32 calculated_crc; + guint32 stored_crc; + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + g_autofree gchar *version = NULL; + + /* in bootloader mode */ + if (fu_device_has_private_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_steelseries_fizz_parent_class)->setup(device, error)) + return FALSE; + + /* skip if in bootloader mode */ + if (fu_device_has_private_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* it is a USB receiver */ + if (fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) { + guint8 status; + + if (!fu_steelseries_fizz_get_paired_status(device, &status, error)) { + g_prefix_error(error, "failed to get paired status: "); + return FALSE; + } + if (status != 0) { + g_autoptr(FuSteelseriesFizzTunnel) mouse_device = + fu_steelseries_fizz_tunnel_new(FU_STEELSERIES_FIZZ(device)); + + fu_device_add_child(device, FU_DEVICE(mouse_device)); + } + + fs = STEELSERIES_FIZZ_FILESYSTEM_RECEIVER; + id = STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_BACKUP_APP_ID; + } + + version = fu_steelseries_fizz_get_version(device, FALSE, error); + if (version == NULL) { + g_prefix_error(error, "failed to get version: "); + return FALSE; + } + fu_device_set_version(device, version); + + /* it is a USB receiver */ + if (fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) { + fs = STEELSERIES_FIZZ_FILESYSTEM_RECEIVER; + id = STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_BACKUP_APP_ID; + } + + if (!fu_steelseries_fizz_get_crc32_fs(device, + FALSE, + fs, + id, + &calculated_crc, + &stored_crc, + error)) { + g_prefix_error(error, "failed to get CRC32 FS 0x%02x ID 0x%02x: ", fs, id); + return FALSE; + } + + if (calculated_crc != stored_crc) + g_warning("%s: checksum mismatch, got 0x%08x, expected 0x%08x", + fu_device_get_name(device), + calculated_crc, + stored_crc); + + /* success */ + return TRUE; +} + +gboolean +fu_steelseries_fizz_write_firmware_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + guint32 calculated_crc; + guint32 stored_crc; + const guint8 *buf; + gsize bufsz; + g_autoptr(GBytes) blob = NULL; + + fu_progress_set_id(progress, G_STRLOC); + if (tunnel) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 13, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 87, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + } else { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 38, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 60, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 2, NULL); + } + + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return FALSE; + buf = fu_bytes_get_data_safe(blob, &bufsz, error); + if (buf == NULL) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "File", buf, bufsz); + if (!fu_steelseries_fizz_erase_fs(device, tunnel, fs, id, error)) { + g_prefix_error(error, "failed to erase FS 0x%02x ID 0x%02x: ", fs, id); + return FALSE; + } + fu_progress_step_done(progress); + if (!fu_steelseries_fizz_write_fs(device, + tunnel, + fs, + id, + buf, + bufsz, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write FS 0x%02x ID 0x%02x: ", fs, id); + return FALSE; + } + fu_progress_step_done(progress); + + if (!fu_steelseries_fizz_get_crc32_fs(device, + tunnel, + fs, + id, + &calculated_crc, + &stored_crc, + error)) { + g_prefix_error(error, "failed to get CRC32 FS 0x%02x ID 0x%02x: ", fs, id); + return FALSE; + } + if (calculated_crc != stored_crc) { + g_warning("%s: checksum mismatch, got 0x%08x, expected 0x%08x", + fu_device_get_name(device), + calculated_crc, + stored_crc); + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_fizz_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + + /* it is a USB receiver */ + if (fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) { + fs = STEELSERIES_FIZZ_FILESYSTEM_RECEIVER; + id = STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_BACKUP_APP_ID; + } + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, 1); + + if (!fu_steelseries_fizz_write_firmware_fs(device, + FALSE, + fs, + id, + firmware, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +FuFirmware * +fu_steelseries_fizz_read_firmware_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + gsize size, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_steelseries_firmware_new(); + g_autoptr(GBytes) blob = NULL; + g_autofree guint8 *buf = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 100, NULL); + + buf = g_malloc0(size); + if (!fu_steelseries_fizz_read_fs(device, + tunnel, + fs, + id, + buf, + size, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to read FS 0x%02x ID 0x%02x: ", fs, id); + return NULL; + } + fu_progress_step_done(progress); + + if (g_getenv("FWUPD_STEELSERIES_FIZZ_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Firmware", buf, size); + blob = g_bytes_new_take(g_steal_pointer(&buf), size); + if (!fu_firmware_parse(firmware, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return NULL; + + /* success */ + return g_steal_pointer(&firmware); +} + +static FuFirmware * +fu_steelseries_fizz_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 fs = STEELSERIES_FIZZ_FILESYSTEM_MOUSE; + guint8 id = STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID; + g_autoptr(FuFirmware) firmware = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 100, NULL); + + /* it is a USB receiver */ + if (fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) { + fs = STEELSERIES_FIZZ_FILESYSTEM_RECEIVER; + id = STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_BACKUP_APP_ID; + } + + firmware = fu_steelseries_fizz_read_firmware_fs(device, + FALSE, + fs, + id, + fu_device_get_firmware_size_max(device), + fu_progress_get_child(progress), + error); + if (firmware == NULL) + return NULL; + fu_progress_step_done(progress); + + /* success */ + return g_steal_pointer(&firmware); +} + +static void +fu_steelseries_fizz_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 82, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 18, "reload"); +} + +static void +fu_steelseries_fizz_class_init(FuSteelseriesFizzClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->attach = fu_steelseries_fizz_attach; + klass_device->setup = fu_steelseries_fizz_setup; + klass_device->write_firmware = fu_steelseries_fizz_write_firmware; + klass_device->read_firmware = fu_steelseries_fizz_read_firmware; + klass_device->set_progress = fu_steelseries_fizz_set_progress; +} + +static void +fu_steelseries_fizz_init(FuSteelseriesFizz *self) +{ + fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), 0x03); + + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_protocol(FU_DEVICE(self), "com.steelseries.fizz"); + fu_device_set_install_duration(FU_DEVICE(self), 13); /* 13 s */ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); /* 40 s */ + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_STEELSERIES_FIRMWARE); +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz.h new file mode 100644 index 0000000000000000000000000000000000000000..d7239b2a67542c242bc99776c562ce902d953f4b --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-fizz.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-steelseries-device.h" + +#define FU_TYPE_STEELSERIES_FIZZ (fu_steelseries_fizz_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesFizz, + fu_steelseries_fizz, + FU, + STEELSERIES_FIZZ, + FuSteelseriesDevice) + +FuSteelseriesFizz * +fu_steelseries_fizz_new(FuDevice *self); + +#define STEELSERIES_FIZZ_FILESYSTEM_RECEIVER 0x01U +#define STEELSERIES_FIZZ_FILESYSTEM_MOUSE 0x02U + +#define STEELSERIES_FIZZ_CONNECTION_STATUS_NOT_CONNECTED 0x00U + +#define STEELSERIES_FIZZ_RESET_MODE_NORMAL 0x00U +#define STEELSERIES_FIZZ_RESET_MODE_BOOTLOADER 0x01U + +#define STEELSERIES_FIZZ_BATTERY_LEVEL_CHARGING_BIT 0x80U +#define STEELSERIES_FIZZ_BATTERY_LEVEL_STATUS_BITS 0x7fU + +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_MAIN_BOOT_ID 0x01U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_FSDATA_FILE_ID 0x02U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_FACTORY_SETTINGS_ID 0x03U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_MAIN_APP_ID 0x04U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_BACKUP_APP_ID 0x05U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_PROFILES_MOUSE_ID 0x06U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_PROFILES_LIGHTING_ID 0x0fU +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_PROFILES_DEVICE_ID 0x10U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_PROFILES_RESERVED_ID 0x11U +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_RECOVERY_ID 0x0dU +#define STEELSERIES_FIZZ_RECEIVER_FILESYSTEM_FREE_SPACE_ID 0xf1U + +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_SOFT_DEVICE_ID 0x00U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_PROFILES_MOUSE_ID 0x06U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_MAIN_APP_ID 0x07U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_BACKUP_APP_ID 0x08U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_MSB_DATA_ID 0x09U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_FACTORY_SETTINGS_ID 0x0aU +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_FSDATA_FILE_ID 0x0bU +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_MAIN_BOOT_ID 0x0cU +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_RECOVERY_ID 0x0eU +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_PROFILES_LIGHTING_ID 0x0fU +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_PROFILES_DEVICE_ID 0x10U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_FDS_PAGES_ID 0x12U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_PROFILES_BLUETOOTH_ID 0x13U +#define STEELSERIES_FIZZ_MOUSE_FILESYSTEM_FREE_SPACE_ID 0xf0U + +gchar * +fu_steelseries_fizz_get_version(FuDevice *device, gboolean tunnel, GError **error); +gboolean +fu_steelseries_fizz_read_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error); +gboolean +fu_steelseries_fizz_write_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + const guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error); +gboolean +fu_steelseries_fizz_erase_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + GError **error); +gboolean +fu_steelseries_fizz_reset(FuDevice *device, gboolean tunnel, guint8 mode, GError **error); +gboolean +fu_steelseries_fizz_get_crc32_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + guint32 *calculated_crc, + guint32 *stored_crc, + GError **error); +FuFirmware * +fu_steelseries_fizz_read_firmware_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + gsize size, + FuProgress *progress, + GError **error); +gboolean +fu_steelseries_fizz_write_firmware_fs(FuDevice *device, + gboolean tunnel, + guint8 fs, + guint8 id, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +gboolean +fu_steelseries_fizz_get_battery_level(FuDevice *device, + gboolean tunnel, + guint8 *level, + GError **error); +gboolean +fu_steelseries_fizz_get_paired_status(FuDevice *device, guint8 *status, GError **error); +gboolean +fu_steelseries_fizz_get_connection_status(FuDevice *device, guint8 *status, GError **error); diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-gamepad.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-gamepad.c new file mode 100644 index 0000000000000000000000000000000000000000..5d1fd81a35534f92752889a5457b6c187889449f --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-gamepad.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-gamepad.h" + +#define STEELSERIES_BUFFER_TRANSFER_SIZE 32 + +struct _FuSteelseriesGamepad { + FuSteelseriesDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesGamepad, fu_steelseries_gamepad, FU_TYPE_STEELSERIES_DEVICE) + +static gboolean +fu_steelseries_gamepad_cmd_erase(FuDevice *device, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0xA1, 0xAA, 0x55}; + + /* USB receiver for gamepad is using different options */ + if (fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) { + /* USB receiver */ + data[8] = 0xD0; + data[9] = 0x01; + } else { + /* gamepad */ + data[9] = 0x02; + /* magic is needed for newer gamepad */ + data[13] = 0x02; + } + + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) { + g_prefix_error(error, "unable erase flash block: "); + return FALSE; + } + + /* timeout to give some time to erase */ + g_usleep(20000); + + return TRUE; +} + +static gboolean +fu_steelseries_gamepad_setup(FuDevice *device, GError **error) +{ + g_autofree gchar *bootloader_version = NULL; + g_autofree gchar *version = NULL; + guint16 fw_ver; + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* get version of FW and bootloader */ + data[0] = 0x12; + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + + if (!fu_memread_uint16_safe(data, sizeof(data), 0x01, &fw_ver, G_LITTLE_ENDIAN, error)) + return FALSE; + version = fu_version_from_uint16(fw_ver, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version(FU_DEVICE(device), version); + + if (!fu_memread_uint16_safe(data, sizeof(data), 0x03, &fw_ver, G_LITTLE_ENDIAN, error)) + return FALSE; + bootloader_version = fu_version_from_uint16(fw_ver, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version_bootloader(device, bootloader_version); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_gamepad_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0xA6, 0xAA, 0x55}; + g_autoptr(GError) error_local = NULL; + + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* switch to runtime mode */ + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + &error_local)) + g_debug("ignoring error on reset: %s", error_local->message); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + return TRUE; +} + +static gboolean +fu_steelseries_gamepad_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0x02, 0x08}; + g_autoptr(GError) error_local = NULL; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* switch to bootloader mode */ + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + &error_local)) + g_debug("ignoring error on reset: %s", error_local->message); + + /* controller will be renumbered after switching to bootloader mode */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + return TRUE; +} + +static gboolean +fu_steelseries_gamepad_write_firmware_chunks(FuDevice *device, + GPtrArray *chunks, + FuProgress *progress, + guint32 *checksum, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + + for (guint id = 0; id < chunks->len; id++) { + FuChunk *chunk = g_ptr_array_index(chunks, id); + guint16 chunk_checksum; + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0xA3}; + + /* block ID */ + if (!fu_memwrite_uint16_safe(data, + STEELSERIES_BUFFER_CONTROL_SIZE, + 0x01, + (guint16)id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + /* 32B of data only */ + if (!fu_memcpy_safe(data, + STEELSERIES_BUFFER_CONTROL_SIZE, + 0x03, + fu_chunk_get_data(chunk), + STEELSERIES_BUFFER_TRANSFER_SIZE, + 0, + fu_chunk_get_data_sz(chunk), + error)) + return FALSE; + + /* block checksum */ + /* probably not necessary */ + chunk_checksum = fu_sum16(data + 3, STEELSERIES_BUFFER_TRANSFER_SIZE); + if (!fu_memwrite_uint16_safe(data, + STEELSERIES_BUFFER_CONTROL_SIZE, + 0x03 + STEELSERIES_BUFFER_TRANSFER_SIZE, + chunk_checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + *checksum += (guint32)chunk_checksum; + + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) { + g_prefix_error(error, "unable to flash block %u: ", id); + return FALSE; + } + /* timeout to give some time to flash the block on device */ + g_usleep(10000); + fu_progress_step_done(progress); + } + + return TRUE; +} + +static gboolean +fu_steelseries_gamepad_write_checksum(FuDevice *device, guint32 checksum, GError **error) +{ + /* write checksum */ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0xA5, 0xAA, 0x55}; + + if (!fu_memwrite_uint32_safe(data, + STEELSERIES_BUFFER_CONTROL_SIZE, + 0x03, + checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) { + g_prefix_error(error, "unable to write checksum: "); + return FALSE; + } + + /* validate checksum */ + if (data[0] != 0xA5 || data[1] != 0xAA || data[2] != 0x55 || data[3] != 0x01) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Controller is unable to validate checksum"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_steelseries_gamepad_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + guint32 checksum = 0; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return FALSE; + + chunks = fu_chunk_array_new_from_bytes(blob, 0, 0, STEELSERIES_BUFFER_TRANSFER_SIZE); + + if (chunks->len > (G_MAXUINT16 + 1)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "too lot of firmware chunks for the device"); + return FALSE; + } + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + + /* erase all first */ + if (!fu_steelseries_gamepad_cmd_erase(device, error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_steelseries_gamepad_write_firmware_chunks(device, + chunks, + fu_progress_get_child(progress), + &checksum, + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_steelseries_gamepad_write_checksum(device, checksum, error)) + return FALSE; + fu_progress_step_done(progress); + + return TRUE; +} + +static void +fu_steelseries_gamepad_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 93, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "reload"); +} + +static void +fu_steelseries_gamepad_class_init(FuSteelseriesGamepadClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->setup = fu_steelseries_gamepad_setup; + klass_device->attach = fu_steelseries_gamepad_attach; + klass_device->detach = fu_steelseries_gamepad_detach; + klass_device->write_firmware = fu_steelseries_gamepad_write_firmware; + klass_device->set_progress = fu_steelseries_gamepad_set_progress; +} + +static void +fu_steelseries_gamepad_init(FuSteelseriesGamepad *self) +{ + fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), -1); + + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); + + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_protocol(FU_DEVICE(self), "com.steelseries.gamepad"); + + fu_device_set_firmware_size_max(FU_DEVICE(self), + (G_MAXUINT16 + 1) * STEELSERIES_BUFFER_TRANSFER_SIZE); +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-gamepad.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-gamepad.h new file mode 100644 index 0000000000000000000000000000000000000000..bd5806c0fba35428d514bd0ffff05f2795427c64 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-gamepad.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-steelseries-device.h" + +#define FU_TYPE_STEELSERIES_GAMEPAD (fu_steelseries_gamepad_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesGamepad, + fu_steelseries_gamepad, + FU, + STEELSERIES_GAMEPAD, + FuSteelseriesDevice) diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-mouse.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-mouse.c new file mode 100644 index 0000000000000000000000000000000000000000..7a2a5fde2172c21dc42d9865fdadf537abe2e73f --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-mouse.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-steelseries-mouse.h" + +#define STEELSERIES_TRANSACTION_TIMEOUT 1000 /* ms */ + +struct _FuSteelseriesMouse { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesMouse, fu_steelseries_mouse, FU_TYPE_USB_DEVICE) + +static gboolean +fu_steelseries_mouse_setup(FuDevice *device, GError **error) +{ + 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; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_steelseries_mouse_parent_class)->setup(device, error)) + return FALSE; + + memset(data, 0x00, sizeof(data)); + data[0] = 0x16; + 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, + 0x09, + 0x0200, + 0x0000, + data, + sizeof(data), + &actual_len, + STEELSERIES_TRANSACTION_TIMEOUT, + NULL, + error); + if (!ret) { + g_prefix_error(error, "failed to do control transfer: "); + return FALSE; + } + if (actual_len != 32) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only wrote %" G_GSIZE_FORMAT "bytes", + actual_len); + return FALSE; + } + ret = g_usb_device_interrupt_transfer(usb_device, + 0x81, /* EP1 IN */ + data, + sizeof(data), + &actual_len, + STEELSERIES_TRANSACTION_TIMEOUT, + NULL, + error); + if (!ret) { + g_prefix_error(error, "failed to do EP1 transfer: "); + return FALSE; + } + if (actual_len != 32) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "only read %" G_GSIZE_FORMAT "bytes", + actual_len); + return FALSE; + } + version = g_strdup_printf("%i.%i.%i", data[0], data[1], data[2]); + fu_device_set_version(FU_DEVICE(device), version); + + /* success */ + return TRUE; +} + +static void +fu_steelseries_mouse_init(FuSteelseriesMouse *self) +{ + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x00); +} + +static void +fu_steelseries_mouse_class_init(FuSteelseriesMouseClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_steelseries_mouse_setup; +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-mouse.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-mouse.h new file mode 100644 index 0000000000000000000000000000000000000000..d4d86d722ec1a389a270a086876a4cfa621ac667 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-mouse.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_STEELSERIES_MOUSE (fu_steelseries_mouse_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesMouse, fu_steelseries_mouse, FU, STEELSERIES_MOUSE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-plugin.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..2c32115116641e7f78f45a1dbbe7493f69c17806 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-plugin.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-fizz-hid.h" +#include "fu-steelseries-fizz-tunnel.h" +#include "fu-steelseries-fizz.h" +#include "fu-steelseries-gamepad.h" +#include "fu-steelseries-mouse.h" +#include "fu-steelseries-plugin.h" +#include "fu-steelseries-sonic.h" + +struct _FuSteelseriesPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesPlugin, fu_steelseries_plugin, FU_TYPE_PLUGIN) + +static void +fu_steelseries_plugin_init(FuSteelseriesPlugin *self) +{ +} + +static void +fu_steelseries_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_FIZZ); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_FIZZ_HID); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_FIZZ_TUNNEL); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_GAMEPAD); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_MOUSE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_SONIC); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); +} + +static void +fu_steelseries_plugin_class_init(FuSteelseriesPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_steelseries_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-plugin.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..f8cc68a9b0c7e261b38eaef3fb81eacd9d21f470 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSteelseriesPlugin, fu_steelseries_plugin, FU, STEELSERIES_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-sonic.c b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-sonic.c new file mode 100644 index 0000000000000000000000000000000000000000..ac5556572b201cbfb3489eb8ff34fcc09cd6b9ce --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-sonic.c @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-steelseries-sonic.h" + +#define STEELSERIES_BUFFER_FLASH_TRANSFER_SIZE 128 +#define STEELSERIES_BUFFER_RAM_TRANSFER_SIZE 48 + +#define STEELSERIES_SONIC_WIRELESS_STATUS_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_WIRELESS_STATUS_VALUE_OFFSET 0x0U + +#define STEELSERIES_SONIC_BATTERY_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_BATTERY_BAT_MODE_OFFSET 0x1U +#define STEELSERIES_SONIC_BATTERY_VALUE_OFFSET 0x0U + +#define STEELSERIES_SONIC_READ_FROM_RAM_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_READ_FROM_RAM_OFFSET_OFFSET 0x2U +#define STEELSERIES_SONIC_READ_FROM_RAM_SIZE_OFFSET 0x4U +#define STEELSERIES_SONIC_READ_FROM_RAM_DATA_OFFSET 0x0U + +#define STEELSERIES_SONIC_READ_FROM_FLASH_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_READ_FROM_FLASH_CHIPID_OFFSET 0x2U +#define STEELSERIES_SONIC_READ_FROM_FLASH_OFFSET_OFFSET 0x4U +#define STEELSERIES_SONIC_READ_FROM_FLASH_SIZE_OFFSET 0x8U + +#define STEELSERIES_SONIC_WRITE_TO_RAM_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_WRITE_TO_RAM_OFFSET_OFFSET 0x2U +#define STEELSERIES_SONIC_WRITE_TO_RAM_SIZE_OFFSET 0x4U +#define STEELSERIES_SONIC_WRITE_TO_RAM_DATA_OFFSET 0x6U + +#define STEELSERIES_SONIC_WRITE_TO_FLASH_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_WRITE_TO_FLASH_CHIPID_OFFSET 0x2U +#define STEELSERIES_SONIC_WRITE_TO_FLASH_OFFSET_OFFSET 0x4U +#define STEELSERIES_SONIC_WRITE_TO_FLASH_SIZE_OFFSET 0x8U + +#define STEELSERIES_SONIC_ERASE_OPCODE_OFFSET 0x0U +#define STEELSERIES_SONIC_ERASE_CHIPID_OFFSET 0x2U + +#define STEELSERIES_SONIC_RESTART_CHIPID_OFFSET 0x0U + +typedef enum { + STEELSERIES_SONIC_CHIP_NORDIC = 0, + STEELSERIES_SONIC_CHIP_HOLTEK, + STEELSERIES_SONIC_CHIP_MOUSE, + /*< private >*/ + STEELSERIES_SONIC_CHIP_LAST, +} SteelseriesSonicChip; + +typedef enum { + STEELSERIES_SONIC_WIRELESS_STATE_OFF, /* WDS not initiated, radio is off */ + STEELSERIES_SONIC_WIRELESS_STATE_IDLE, /* WDS initiated, USB receiver is transmitting beacon + * (mouse will not have this state) */ + STEELSERIES_SONIC_WIRELESS_STATE_SEARCH, /* WDS initiated, mouse is trying to synchronize to + * receiver (receiver will not have this state) */ + STEELSERIES_SONIC_WIRELESS_STATE_LOCKED, /* USB receiver and mouse are synchronized, but not + * necessarily connected. */ + STEELSERIES_SONIC_WIRELESS_STATE_CONNECTED, /* USB receiver and mouse are connected. */ + STEELSERIES_SONIC_WIRELESS_STATE_TERMINATED, /* Mouse has been disconnected from the USB + * receiver. + */ +} SteelseriesSonicWirelessStatus; + +const guint16 STEELSERIES_SONIC_READ_FROM_RAM_OPCODE[] = {0x00c3U, 0x00c3U, 0x0083U}; +const guint16 STEELSERIES_SONIC_READ_FROM_FLASH_OPCODE[] = {0x00c5U, 0x00c5U, 0x0085U}; +const guint16 STEELSERIES_SONIC_WRITE_TO_RAM_OPCODE[] = {0x0043U, 0x0043U, 0x0003U}; +const guint16 STEELSERIES_SONIC_WRITE_TO_FLASH_OPCODE[] = {0x0045U, 0x0045U, 0x0005U}; +const guint16 STEELSERIES_SONIC_ERASE_OPCODE[] = {0x0048U, 0x0048U, 0x0008U}; +const guint16 STEELSERIES_SONIC_RESTART_OPCODE[] = {0x0041U, 0x0041U, 0x0001U}; +const guint16 STEELSERIES_SONIC_CHIP[] = {0x0002U, 0x0003U, 0x0002U}; +const guint32 STEELSERIES_SONIC_FIRMWARE_SIZE[] = {0x9000U, 0x4000U, 0x12000U}; +const gchar *STEELSERIES_SONIC_FIRMWARE_ID[] = {"app-nordic.bin", + "app-holtek.bin", + "mouse-app.bin"}; +const guint STEELSERIES_SONIC_WRITE_PROGRESS_STEP_VALUE[][2] = {{5, 95}, {11, 89}, {3, 97}}; + +struct _FuSteelseriesSonic { + FuSteelseriesDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSteelseriesSonic, fu_steelseries_sonic, FU_TYPE_STEELSERIES_DEVICE) + +static gboolean +fu_steelseries_sonic_wireless_status(FuDevice *device, + SteelseriesSonicWirelessStatus *status, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + const guint16 opcode = 0xE8U; /* USB receiver */ + guint8 value; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_SONIC_WIRELESS_STATUS_OPCODE_OFFSET, + opcode, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "WirelessStatus", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "WirelessStatus", data, sizeof(data)); + if (!fu_memread_uint8_safe(data, + sizeof(data), + STEELSERIES_SONIC_WIRELESS_STATUS_VALUE_OFFSET, + &value, + error)) + return FALSE; + *status = value; + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_battery_state(FuDevice *device, guint16 *value, GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + const guint16 opcode = 0xAAU; + const guint8 bat_mode = 0x01U; /* percentage */ + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_SONIC_BATTERY_OPCODE_OFFSET, + opcode, + error)) + return FALSE; + + if (!fu_memwrite_uint8_safe(data, + sizeof(data), + STEELSERIES_SONIC_BATTERY_BAT_MODE_OFFSET, + bat_mode, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "BatteryState", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "BatteryState", data, sizeof(data)); + if (!fu_memread_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_BATTERY_VALUE_OFFSET, + value, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_read_from_ram(FuDevice *device, + SteelseriesSonicChip chip, + guint32 address, + guint8 *buf, + guint16 bufsz, + FuProgress *progress, + GError **error) +{ + const guint16 opcode = STEELSERIES_SONIC_READ_FROM_RAM_OPCODE[chip]; + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = + fu_chunk_array_mutable_new(buf, bufsz, 0x0, 0x0, STEELSERIES_BUFFER_RAM_TRANSFER_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + const guint16 offset = fu_chunk_get_address(chk); + const guint16 size = fu_chunk_get_data_sz(chk); + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_RAM_OPCODE_OFFSET, + opcode, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_RAM_OFFSET_OFFSET, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_RAM_SIZE_OFFSET, + size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + TRUE, + error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ReadFromRAM", data, sizeof(data)); + + if (!fu_memcpy_safe(fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + 0x0, + data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_RAM_DATA_OFFSET, + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_read_from_flash(FuDevice *device, + SteelseriesSonicChip chip, + guint32 address, + guint8 *buf, + guint32 bufsz, + FuProgress *progress, + GError **error) +{ + const guint16 opcode = STEELSERIES_SONIC_READ_FROM_FLASH_OPCODE[chip]; + const guint16 chipid = STEELSERIES_SONIC_CHIP[chip]; + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_mutable_new(buf, + bufsz, + address, + 0x0, + STEELSERIES_BUFFER_FLASH_TRANSFER_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + const guint32 offset = fu_chunk_get_address(chk); + const guint16 size = fu_chunk_get_data_sz(chk); + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_FLASH_OPCODE_OFFSET, + opcode, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_FLASH_CHIPID_OFFSET, + chipid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint32_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_FLASH_OFFSET_OFFSET, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_READ_FROM_FLASH_SIZE_OFFSET, + size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "ReadFromFlash", data, sizeof(data)); + + /* timeout to give some time to read from flash to ram */ + g_usleep(15000); /* 15 ms */ + + if (!fu_steelseries_sonic_read_from_ram(device, + chip, + offset, + fu_chunk_get_data_out(chk), + size, + fu_progress_get_child(progress), + error)) + return FALSE; + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_write_to_ram(FuDevice *device, + SteelseriesSonicChip chip, + guint16 address, + const guint8 *buf, + guint16 bufsz, + FuProgress *progress, + GError **error) +{ + const guint16 opcode = STEELSERIES_SONIC_WRITE_TO_RAM_OPCODE[chip]; + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new(buf, bufsz, 0x0, 0x0, STEELSERIES_BUFFER_RAM_TRANSFER_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + const guint16 offset = fu_chunk_get_address(chk); + const guint16 size = fu_chunk_get_data_sz(chk); + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_RAM_OPCODE_OFFSET, + opcode, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_RAM_OFFSET_OFFSET, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_RAM_SIZE_OFFSET, + size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memcpy_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_RAM_DATA_OFFSET, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "WriteToRAM", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) + return FALSE; + + /* timeout to give some time to write to ram */ + g_usleep(15000); /* 15 ms */ + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_write_to_flash(FuDevice *device, + SteelseriesSonicChip chip, + guint32 address, + const guint8 *buf, + guint32 bufsz, + FuProgress *progress, + GError **error) +{ + const guint16 opcode = STEELSERIES_SONIC_WRITE_TO_FLASH_OPCODE[chip]; + const guint16 chipid = STEELSERIES_SONIC_CHIP[chip]; + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_chunk_array_new(buf, bufsz, 0x0, 0x0, STEELSERIES_BUFFER_FLASH_TRANSFER_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + const guint32 offset = fu_chunk_get_address(chk); + const guint16 size = fu_chunk_get_data_sz(chk); + + if (!fu_steelseries_sonic_write_to_ram(device, + chip, + offset, + fu_chunk_get_data(chk), + size, + fu_progress_get_child(progress), + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_FLASH_OPCODE_OFFSET, + opcode, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_FLASH_CHIPID_OFFSET, + chipid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint32_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_FLASH_OFFSET_OFFSET, + offset, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_WRITE_TO_FLASH_SIZE_OFFSET, + size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "WriteToFlash", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) + return FALSE; + + /* timeout to give some time to write from ram to flash */ + g_usleep(15000); /* 15 ms */ + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_erase(FuDevice *device, + SteelseriesSonicChip chip, + FuProgress *progress, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + const guint16 opcode = STEELSERIES_SONIC_ERASE_OPCODE[chip]; + const guint16 chipid = STEELSERIES_SONIC_CHIP[chip]; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); + fu_progress_set_steps(progress, 1); + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_ERASE_OPCODE_OFFSET, + opcode, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_ERASE_CHIPID_OFFSET, + chipid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Erase", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) + return FALSE; + + /* timeout to give some time to erase flash */ + fu_progress_sleep(fu_progress_get_child(progress), 1000); /* 1 s */ + + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_restart(FuDevice *device, + SteelseriesSonicChip chip, + FuProgress *progress, + GError **error) +{ + guint8 data[STEELSERIES_BUFFER_CONTROL_SIZE] = {0}; + const guint16 opcode = STEELSERIES_SONIC_RESTART_OPCODE[chip]; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); + fu_progress_set_steps(progress, 1); + + if (!fu_memwrite_uint16_safe(data, + sizeof(data), + STEELSERIES_SONIC_RESTART_CHIPID_OFFSET, + opcode, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "Restart", data, sizeof(data)); + if (!fu_steelseries_device_cmd(FU_STEELSERIES_DEVICE(device), + data, + sizeof(data), + FALSE, + error)) + return FALSE; + + /* timeout to give some time to restart chip */ + fu_progress_sleep(progress, 3000); /* 3 s */ + + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_wait_for_connect_cb(FuDevice *device, gpointer user_data, GError **error) +{ + SteelseriesSonicWirelessStatus *wl_status = (SteelseriesSonicWirelessStatus *)user_data; + + if (!fu_steelseries_sonic_wireless_status(device, wl_status, error)) { + g_prefix_error(error, "failed to get wireless status: "); + return FALSE; + } + g_debug("WirelessStatus: %u", *wl_status); + if (*wl_status != STEELSERIES_SONIC_WIRELESS_STATE_CONNECTED) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "device is unreachable"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_wait_for_connect(FuDevice *device, guint delay, GError **error) +{ + SteelseriesSonicWirelessStatus wl_status; + g_autoptr(FwupdRequest) request = NULL; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *msg = NULL; + + if (!fu_steelseries_sonic_wireless_status(device, &wl_status, error)) { + g_prefix_error(error, "failed to get wireless status: "); + return FALSE; + } + g_debug("WirelessStatus: %u", wl_status); + if (wl_status == STEELSERIES_SONIC_WIRELESS_STATE_CONNECTED) { + /* success */ + return TRUE; + } + + /* the user has to do something */ + msg = g_strdup_printf("%s needs to be connected to start the update. " + "Please put the switch button underneath to 2.4G, or " + "click on any button to reconnect it.", + fu_device_get_name(device)); + request = fwupd_request_new(); + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_PRESS_UNLOCK); + fwupd_request_set_message(request, msg); + fu_device_emit_request(device, request); + + if (!fu_device_retry_full(device, + fu_steelseries_sonic_wait_for_connect_cb, + delay / 1000, + 1000, + &wl_status, + &error_local)) + g_debug("%s", error_local->message); + if (wl_status != STEELSERIES_SONIC_WIRELESS_STATE_CONNECTED) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NEEDS_USER_ACTION, msg); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + SteelseriesSonicChip chip; + g_autoptr(FwupdRequest) request = NULL; + g_autofree gchar *msg = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 50, "mouse"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 50, "holtek"); + + /* mouse */ + chip = STEELSERIES_SONIC_CHIP_MOUSE; + if (!fu_steelseries_sonic_restart(device, chip, fu_progress_get_child(progress), error)) { + g_prefix_error(error, "failed to restart chip %u: ", chip); + return FALSE; + } + fu_progress_step_done(progress); + + /* USB receiver (nordic, holtek; same command) */ + chip = STEELSERIES_SONIC_CHIP_HOLTEK; + if (!fu_steelseries_sonic_restart(device, chip, fu_progress_get_child(progress), error)) { + g_prefix_error(error, "failed to restart chip %u: ", chip); + return FALSE; + } + fu_progress_step_done(progress); + + /* the user has to do something */ + msg = g_strdup_printf("%s needs to be manually restarted to complete the update. " + "Please unplug the 2.4G USB Wireless adapter and then re-plug it.", + fu_device_get_name(device)); + request = fwupd_request_new(); + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_REMOVE_REPLUG); + fwupd_request_add_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + fwupd_request_set_message(request, msg); + fu_device_emit_request(device, request); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + guint16 bat_state; + + if (!fu_steelseries_sonic_wait_for_connect(device, + fu_device_get_remove_delay(device), + error)) + return FALSE; + + if (!fu_steelseries_sonic_battery_state(device, &bat_state, error)) { + g_prefix_error(error, "failed to get battery state: "); + return FALSE; + } + g_debug("BatteryState: %u%%", bat_state); + fu_device_set_battery_level(device, bat_state); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_write_chip(FuDevice *device, + SteelseriesSonicChip chip, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + const guint8 *buf; + gsize bufsz; + g_autoptr(FuFirmware) fw = NULL; + g_autoptr(GBytes) blob = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_ERASE, + STEELSERIES_SONIC_WRITE_PROGRESS_STEP_VALUE[chip][0], + NULL); + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_WRITE, + STEELSERIES_SONIC_WRITE_PROGRESS_STEP_VALUE[chip][1], + NULL); + + fw = fu_firmware_get_image_by_id(firmware, STEELSERIES_SONIC_FIRMWARE_ID[chip], error); + if (fw == NULL) + return FALSE; + blob = fu_firmware_get_bytes(fw, error); + if (blob == NULL) + return FALSE; + buf = fu_bytes_get_data_safe(blob, &bufsz, error); + if (buf == NULL) + return FALSE; + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, STEELSERIES_SONIC_FIRMWARE_ID[chip], buf, bufsz); + if (!fu_steelseries_sonic_erase(device, chip, fu_progress_get_child(progress), error)) { + g_prefix_error(error, "failed to erase chip %u: ", chip); + return FALSE; + } + fu_progress_step_done(progress); + if (!fu_steelseries_sonic_write_to_flash(device, + chip, + 0x0, + buf, + bufsz, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write to flash chip %u: ", chip); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_steelseries_sonic_read_chip(FuDevice *device, + SteelseriesSonicChip chip, + FuProgress *progress, + GError **error) +{ + guint32 bufsz; + g_autoptr(GBytes) blob = NULL; + g_autofree guint8 *buf = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, 1); + + bufsz = STEELSERIES_SONIC_FIRMWARE_SIZE[chip]; + buf = g_malloc0(bufsz); + if (!fu_steelseries_sonic_read_from_flash(device, + chip, + 0x0, + buf, + bufsz, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to read from flash chip %u: ", chip); + return NULL; + } + fu_progress_step_done(progress); + + blob = g_bytes_new_take(g_steal_pointer(&buf), bufsz); + return fu_firmware_new_from_bytes(blob); +} + +static gboolean +fu_steelseries_sonic_verify_chip(FuDevice *device, + SteelseriesSonicChip chip, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuFirmware) fw_tmp = NULL; + g_autoptr(FuFirmware) fw = NULL; + g_autoptr(GBytes) blob_tmp = NULL; + g_autoptr(GBytes) blob = NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 100, NULL); + + fw = fu_firmware_get_image_by_id(firmware, STEELSERIES_SONIC_FIRMWARE_ID[chip], error); + if (fw == NULL) + return FALSE; + blob = fu_firmware_get_bytes(fw, error); + if (blob == NULL) + return FALSE; + fw_tmp = + fu_steelseries_sonic_read_chip(device, chip, fu_progress_get_child(progress), error); + if (fw_tmp == NULL) { + g_prefix_error(error, "failed to read from flash chip %u: ", chip); + return FALSE; + } + blob_tmp = fu_firmware_get_bytes(fw_tmp, error); + if (blob_tmp == NULL) + return FALSE; + if (!fu_bytes_compare(blob_tmp, blob, error)) { + if (g_getenv("FWUPD_STEELSERIES_SONIC_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "Verify", + g_bytes_get_data(blob_tmp, NULL), + g_bytes_get_size(blob_tmp)); + } + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_steelseries_sonic_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + SteelseriesSonicChip chip; + g_autoptr(FuFirmware) firmware = fu_archive_firmware_new(); + g_autoptr(FuFirmware) firmware_nordic = NULL; + g_autoptr(FuFirmware) firmware_holtek = NULL; + g_autoptr(FuFirmware) firmware_mouse = NULL; + + if (!fu_steelseries_sonic_wait_for_connect(device, + fu_device_get_remove_delay(device), + error)) + return NULL; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 18, "nordic"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 8, "holtek"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 73, "mouse"); + + fu_archive_firmware_set_format(FU_ARCHIVE_FIRMWARE(firmware), FU_ARCHIVE_FORMAT_ZIP); + fu_archive_firmware_set_compression(FU_ARCHIVE_FIRMWARE(firmware), + FU_ARCHIVE_COMPRESSION_NONE); + + /* nordic */ + chip = STEELSERIES_SONIC_CHIP_NORDIC; + firmware_nordic = + fu_steelseries_sonic_read_chip(device, chip, fu_progress_get_child(progress), error); + if (firmware_nordic == NULL) + return NULL; + fu_firmware_set_id(firmware_nordic, STEELSERIES_SONIC_FIRMWARE_ID[chip]); + fu_firmware_add_image(firmware, firmware_nordic); + fu_progress_step_done(progress); + + /* holtek */ + chip = STEELSERIES_SONIC_CHIP_HOLTEK; + firmware_holtek = + fu_steelseries_sonic_read_chip(device, chip, fu_progress_get_child(progress), error); + if (firmware_holtek == NULL) + return NULL; + fu_firmware_set_id(firmware_holtek, STEELSERIES_SONIC_FIRMWARE_ID[chip]); + fu_firmware_add_image(firmware, firmware_holtek); + fu_progress_step_done(progress); + + /* mouse */ + chip = STEELSERIES_SONIC_CHIP_MOUSE; + firmware_mouse = + fu_steelseries_sonic_read_chip(device, chip, fu_progress_get_child(progress), error); + if (firmware_mouse == NULL) + return NULL; + fu_firmware_set_id(firmware_mouse, STEELSERIES_SONIC_FIRMWARE_ID[chip]); + fu_firmware_add_image(firmware, firmware_mouse); + fu_progress_step_done(progress); + + /* success */ + fu_firmware_set_id(firmware, FU_FIRMWARE_ID_PAYLOAD); + return g_steal_pointer(&firmware); +} + +static gboolean +fu_steelseries_sonic_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + SteelseriesSonicChip chip; + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 34, "device-write-mouse"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 30, "device-verify-mouse"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 17, "device-write-nordic"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 7, "device-verify-nordic"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 8, "device-write-holtek"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 3, "device-verify-holtek"); + + /* mouse */ + chip = STEELSERIES_SONIC_CHIP_MOUSE; + if (!fu_steelseries_sonic_write_chip(device, + chip, + firmware, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + if (!fu_steelseries_sonic_verify_chip(device, + chip, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* nordic */ + chip = STEELSERIES_SONIC_CHIP_NORDIC; + if (!fu_steelseries_sonic_write_chip(device, + chip, + firmware, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + if (!fu_steelseries_sonic_verify_chip(device, + chip, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* holtek */ + chip = STEELSERIES_SONIC_CHIP_HOLTEK; + if (!fu_steelseries_sonic_write_chip(device, + STEELSERIES_SONIC_CHIP_HOLTEK, + firmware, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + if (!fu_steelseries_sonic_verify_chip(device, + chip, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_steelseries_sonic_parse_firmware(FuFirmware *firmware, FwupdInstallFlags flags, GError **error) +{ + guint32 checksum_tmp; + guint32 checksum; + g_autoptr(GBytes) blob = NULL; + + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return FALSE; + + if (!fu_memread_uint32_safe(g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + g_bytes_get_size(blob) - sizeof(checksum), + &checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + checksum_tmp = + fu_crc32(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob) - sizeof(checksum_tmp)); + checksum_tmp = ~checksum_tmp; + if (checksum_tmp != checksum) { + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "checksum mismatch for %s, got 0x%08x, expected 0x%08x", + fu_firmware_get_id(firmware), + checksum_tmp, + checksum); + return FALSE; + } + g_debug("ignoring checksum mismatch, got 0x%08x, expected 0x%08x", + checksum_tmp, + checksum); + } + + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_steelseries_sonic_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + SteelseriesSonicChip chip; + g_autoptr(FuFirmware) firmware_nordic = NULL; + g_autoptr(FuFirmware) firmware_holtek = NULL; + g_autoptr(FuFirmware) firmware_mouse = NULL; + g_autoptr(FuFirmware) firmware = NULL; + + firmware = fu_archive_firmware_new(); + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* mouse */ + chip = STEELSERIES_SONIC_CHIP_MOUSE; + firmware_mouse = + fu_firmware_get_image_by_id(firmware, STEELSERIES_SONIC_FIRMWARE_ID[chip], error); + if (firmware_mouse == NULL) + return NULL; + if (!fu_steelseries_sonic_parse_firmware(firmware_mouse, flags, error)) + return NULL; + + /* nordic */ + chip = STEELSERIES_SONIC_CHIP_NORDIC; + firmware_nordic = + fu_firmware_get_image_by_id(firmware, STEELSERIES_SONIC_FIRMWARE_ID[chip], error); + if (firmware_nordic == NULL) + return NULL; + if (!fu_steelseries_sonic_parse_firmware(firmware_nordic, flags, error)) + return NULL; + + /* holtek */ + chip = STEELSERIES_SONIC_CHIP_HOLTEK; + firmware_holtek = + fu_firmware_get_image_by_id(firmware, STEELSERIES_SONIC_FIRMWARE_ID[chip], error); + if (firmware_holtek == NULL) + return NULL; + if (!fu_steelseries_sonic_parse_firmware(firmware_holtek, flags, error)) + return NULL; + + /* success */ + return g_steal_pointer(&firmware); +} + +static void +fu_steelseries_sonic_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 92, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 3, "reload"); +} + +static void +fu_steelseries_sonic_class_init(FuSteelseriesSonicClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->attach = fu_steelseries_sonic_attach; + klass_device->prepare = fu_steelseries_sonic_prepare; + klass_device->read_firmware = fu_steelseries_sonic_read_firmware; + klass_device->write_firmware = fu_steelseries_sonic_write_firmware; + klass_device->prepare_firmware = fu_steelseries_sonic_prepare_firmware; + klass_device->set_progress = fu_steelseries_sonic_set_progress; +} + +static void +fu_steelseries_sonic_init(FuSteelseriesSonic *self) +{ + fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), -1); + + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_add_protocol(FU_DEVICE(self), "com.steelseries.sonic"); + fu_device_set_install_duration(FU_DEVICE(self), 120); /* 2 min */ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); /* 40 s */ + fu_device_set_battery_level(FU_DEVICE(self), FWUPD_BATTERY_LEVEL_INVALID); + fu_device_set_battery_threshold(FU_DEVICE(self), 20); +} diff --git a/fwupd-1.8.6/plugins/steelseries/fu-steelseries-sonic.h b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-sonic.h new file mode 100644 index 0000000000000000000000000000000000000000..0c9e185586e65e3c5a04083c1f7d11b34ace46c4 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/fu-steelseries-sonic.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Gaël PORTAY + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-steelseries-device.h" + +#define FU_TYPE_STEELSERIES_SONIC (fu_steelseries_sonic_get_type()) +G_DECLARE_FINAL_TYPE(FuSteelseriesSonic, + fu_steelseries_sonic, + FU, + STEELSERIES_SONIC, + FuSteelseriesDevice) diff --git a/fwupd-1.8.6/plugins/steelseries/meson.build b/fwupd-1.8.6/plugins/steelseries/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..b723a49c4005a0cbd7ca2219b5c061fff69df626 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/meson.build @@ -0,0 +1,22 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginSteelSeries"'] + +plugin_quirks += files('steelseries.quirk') +plugin_builtins += static_library('fu_plugin_steelseries', + sources: [ + 'fu-steelseries-plugin.c', + 'fu-steelseries-device.c', + 'fu-steelseries-firmware.c', + 'fu-steelseries-fizz-hid.c', + 'fu-steelseries-fizz-tunnel.c', + 'fu-steelseries-fizz.c', + 'fu-steelseries-mouse.c', + 'fu-steelseries-gamepad.c', + 'fu-steelseries-sonic.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/steelseries/steelseries.quirk b/fwupd-1.8.6/plugins/steelseries/steelseries.quirk new file mode 100644 index 0000000000000000000000000000000000000000..8a4f2614b0340dd6fd948d902bcbb1b514b2d126 --- /dev/null +++ b/fwupd-1.8.6/plugins/steelseries/steelseries.quirk @@ -0,0 +1,106 @@ +# Rival 100 +[USB\VID_1038&PID_1702] +Plugin = steelseries +GType = FuSteelseriesMouse +Summary = An optical gaming mouse +Icon = input-mouse + +# Rival 3 Wireless +[USB\VID_1038&PID_1830] +Plugin = steelseries +GType = FuSteelseriesSonic +Icon = input-mouse + +# Aerox 3 Wireless +[HIDRAW\VEN_0111&DEV_183A] +Plugin = steelseries +GType = FuSteelseriesFizzHid +Name = Aerox 3 Wireless Mouse via Bluetooth +Icon = input-mouse + +[USB\VID_1038&PID_1838] +Plugin = steelseries +GType = FuSteelseriesFizz +Name = Aerox 3 Wireless USB Receiver +Icon = usb-receiver +CounterpartGuid = USB\VID_1038&PID_1839 +FirmwareSize = 0x23000 +Flags = is-receiver + +[USB\VID_1038&PID_1839] +Plugin = steelseries +GType = FuSteelseriesFizz +Name = Aerox 3 Wireless USB Receiver bootloader +Icon = usb-receiver +FirmwareSize = 0x23000 +Flags = is-bootloader,is-receiver,~usable-during-update + +[STEELSERIES\VID_1038&PID_1838&PROTOCOL_FIZZ_TUNNEL] +Plugin = steelseries +GType = FuSteelseriesFizzTunnel +Name = Aerox 3 Wireless Mouse via USB Receiver +Icon = input-mouse +FirmwareSize = 0x27000 + +[USB\VID_1038&PID_183A] +Plugin = steelseries +GType = FuSteelseriesFizz +Name = Aerox 3 Wireless Mouse +Icon = input-mouse +CounterpartGuid = USB\VID_103B&PID_1839,HIDRAW\VEN_0111&DEV_183A +FirmwareSize = 0x27000 + +[USB\VID_1038&PID_183B] +Plugin = steelseries +GType = FuSteelseriesFizz +Name = Aerox 3 Wireless Mouse bootloader +Icon = input-mouse +FirmwareSize = 0x27000 +Flags = is-bootloader,~usable-during-update + +# Stratus Duo RX +[USB\VID_1038&PID_1430] +Plugin = steelseries +GType = FuSteelseriesGamepad +Name = Stratus Duo RX +Icon = usb-receiver +CounterpartGuid = USB\VID_1038&PID_1432 +Flags = is-receiver + +[USB\VID_1038&PID_1432] +Plugin = steelseries +GType = FuSteelseriesGamepad +Name = Stratus Duo RX bootloader +Icon = usb-receiver +CounterpartGuid = USB\VID_1038&PID_1430 +Flags = is-receiver,is-bootloader + +# Stratus Duo +[USB\VID_1038&PID_1431] +Plugin = steelseries +GType = FuSteelseriesGamepad +Name = Stratus Duo Gamepad +Icon = input-gaming +CounterpartGuid = USB\VID_1038&PID_1433 + +[USB\VID_1038&PID_1433] +Plugin = steelseries +GType = FuSteelseriesGamepad +Name = Stratus Duo bootloader +CounterpartGuid = USB\VID_1038&PID_1431 +Flags = is-bootloader + +# Stratus+ +[USB\VID_1038&PID_1434] +Plugin = steelseries +GType = FuSteelseriesGamepad +Name = Stratus+ gamepad +Icon = input-gaming +CounterpartGuid = USB\VID_1038&PID_1435 + +[USB\VID_1038&PID_1435] +Plugin = steelseries +GType = FuSteelseriesGamepad +Name = Stratus+ bootloader +CounterpartGuid = USB\VID_1038&PID_1434 +Flags = is-bootloader diff --git a/fwupd-1.8.6/plugins/superio/README.md b/fwupd-1.8.6/plugins/superio/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e5b77d15d9d08c37b916cc989b6d14553bda731e --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/README.md @@ -0,0 +1,65 @@ +# 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 [the Wikipedia page](https://en.wikipedia.org/wiki/Super_I/O) for more +details about SuperIO and what the EC actually does. + +Other useful links: + +* +* +* +* +* + +## GUID Generation + +These devices use a custom GUID generated using the SuperIO chipset name: + +* `SuperIO-$(chipset)`, for example `SuperIO-IT8512` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, but it is +only activated on machine reboot. The firmware write is normally scheduled to be +done very early in the boot process to minimize the chance the EC chip locking +up if the user is actually using the keyboard controller. + +## Vendor ID Security + +The vendor ID is set from the baseboard vendor, for example `DMI:Star Labs` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### SuperioControlPort + +Control (status/command) port number, e.g. `0x66` + +Since: 1.6.2 + +### SuperioDataPort + +Data port number, e.g. `0x62` + +Since: 1.6.2 + +### SuperioAutoloadAction + +Autoload action, specified by ITE: `none`, `disable`, `on`, `off` + +Since: 1.6.2 + +### SuperioTimeout + +Maximum wait time in ms (default value is `250`) + +Since: 1.6.2 + +## External Interface Access + +This plugin requires access to raw system memory via `inb`/`outb`. diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-common.c b/fwupd-1.8.6/plugins/superio/fu-superio-common.c new file mode 100644 index 0000000000000000000000000000000000000000..37b1e828f5dfd14a54300779a9697ff5d092f1a4 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-common.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-superio-common.h" + +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 --git a/fwupd-1.8.6/plugins/superio/fu-superio-common.h b/fwupd-1.8.6/plugins/superio/fu-superio-common.h new file mode 100644 index 0000000000000000000000000000000000000000..0a6f5aa3accebecb461a1f25c180c852e480ad11 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-common.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* 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); diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-device.c b/fwupd-1.8.6/plugins/superio/fu-superio-device.c new file mode 100644 index 0000000000000000000000000000000000000000..2ceec1521668f181721f7e87c60ddf3c31a7abd9 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-device.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2021, TUXEDO Computers GmbH + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-superio-common.h" +#include "fu-superio-device.h" + +#define FU_PLUGIN_SUPERIO_DEFAULT_TIMEOUT 250 /* ms */ + +typedef struct { + gchar *chipset; + guint timeout_ms; + guint16 port; + guint16 data_port; + guint16 control_port; + guint16 id; +} FuSuperioDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuSuperioDevice, fu_superio_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_superio_device_get_instance_private(o)) + +enum { PROP_0, PROP_CHIPSET, PROP_LAST }; + +gboolean +fu_superio_device_io_read(FuSuperioDevice *self, guint8 addr, guint8 *data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + + if (priv->port == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "port isn't set"); + return FALSE; + } + + if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(self), priv->port, &addr, 1, error)) + return FALSE; + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), priv->port + 1, data, 1, error)) + return FALSE; + return TRUE; +} + +gboolean +fu_superio_device_io_read16(FuSuperioDevice *self, guint8 addr, guint16 *data, GError **error) +{ + guint8 msb; + guint8 lsb; + if (!fu_superio_device_io_read(self, addr, &msb, error)) + return FALSE; + if (!fu_superio_device_io_read(self, addr + 1, &lsb, error)) + return FALSE; + *data = ((guint16)msb << 8) | (guint16)lsb; + return TRUE; +} + +gboolean +fu_superio_device_io_write(FuSuperioDevice *self, guint8 addr, guint8 data, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + + if (priv->port == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "port isn't set"); + return FALSE; + } + + if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(self), priv->port, &addr, 1, error)) + return FALSE; + if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(self), priv->port + 1, &data, 1, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_superio_device_set_ldn(FuSuperioDevice *self, guint8 ldn, GError **error) +{ + return fu_superio_device_io_write(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_io_read(self, i, &buf[i], error)) + return FALSE; + } + + /* get the i/o base addresses */ + if (!fu_superio_device_io_read16(self, SIO_LDNxx_IDX_IOBAD0, &iobad0, error)) + return FALSE; + if (!fu_superio_device_io_read16(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); + if (g_getenv("FWUPD_SUPERIO_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, str->str, buf, sizeof(buf)); + return TRUE; +} + +static void +fu_superio_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_superio_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "Chipset", priv->chipset); + fu_string_append_kx(str, idt, "Id", priv->id); + fu_string_append_kx(str, idt, "Port", priv->port); + fu_string_append_kx(str, idt, "DataPort", priv->data_port); + fu_string_append_kx(str, idt, "ControlPort", priv->control_port); +} + +static gboolean +fu_superio_device_check_id(FuSuperioDevice *self, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + guint16 id_tmp; + + /* no quirk entry */ + if (priv->id == 0x0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "invalid SuperioId"); + return FALSE; + } + + /* can't check the ID, assume it's correct */ + if (priv->port == 0) + return TRUE; + + /* check ID, which can be done from any LDN */ + if (!fu_superio_device_io_read16(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_udev_device_pread(FU_UDEV_DEVICE(self), + priv->control_port, + &status, + 1, + error)) + return FALSE; + if (g_timer_elapsed(timer, NULL) * 1000.0f > priv->timeout_ms) + 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_data(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_udev_device_pread(FU_UDEV_DEVICE(self), priv->data_port, data, 1, error); +} + +gboolean +fu_superio_device_ec_write_data(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_udev_device_pwrite(FU_UDEV_DEVICE(self), priv->data_port, &data, 1, error); +} + +gboolean +fu_superio_device_ec_write_cmd(FuSuperioDevice *self, guint8 cmd, GError **error) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + if (!fu_superio_device_wait_for(self, SIO_STATUS_EC_IBF, FALSE, error)) + return FALSE; + return fu_udev_device_pwrite(FU_UDEV_DEVICE(self), priv->control_port, &cmd, 1, 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_udev_device_pread(FU_UDEV_DEVICE(self), + priv->control_port, + &status, + 1, + error)) + return FALSE; + if ((status & SIO_STATUS_EC_OBF) == 0) + break; + if (!fu_udev_device_pread(FU_UDEV_DEVICE(self), priv->data_port, &unused, 1, error)) + return FALSE; + if (g_timer_elapsed(timer, NULL) * 1000.f > priv->timeout_ms) { + 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_reg_read(FuSuperioDevice *self, guint8 address, guint8 *data, GError **error) +{ + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_READ, error)) + return FALSE; + if (!fu_superio_device_ec_write_data(self, address, error)) + return FALSE; + return fu_superio_device_ec_read_data(self, data, error); +} + +gboolean +fu_superio_device_reg_write(FuSuperioDevice *self, guint8 address, guint8 data, GError **error) +{ + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_WRITE, error)) + return FALSE; + if (!fu_superio_device_ec_write_data(self, address, error)) + return FALSE; + return fu_superio_device_ec_write_data(self, data, error); +} + +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) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + + /* check ID is correct */ + if (!fu_superio_device_check_id(self, error)) { + g_prefix_error(error, "failed to probe id: "); + return FALSE; + } + + /* discover the data port and control port from PM1 */ + if (priv->data_port == 0 && priv->control_port == 0) { + /* 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_io_read16(self, + SIO_LDNxx_IDX_IOBAD0, + &priv->data_port, + error)) + return FALSE; + + /* get the PM1 IOBAD1 address */ + if (!fu_superio_device_io_read16(self, + SIO_LDNxx_IDX_IOBAD1, + &priv->control_port, + error)) + return FALSE; + } + + /* sanity check that EC is usable */ + if (!fu_superio_device_wait_for(self, SIO_STATUS_EC_IBF, FALSE, error)) { + g_prefix_error(error, "sanity check: "); + 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_reg_read(self, i, &buf[i], &error_local)) { + g_debug("param: 0x%02x = %s", i, error_local->message); + continue; + } + } + fu_dump_raw(G_LOG_DOMAIN, "EC Registers", buf, sizeof(buf)); + } + + /* success */ + return TRUE; +} + +static FuFirmware * +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}; + 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 fu_firmware_new_from_bytes(fw); + } + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "did not detect signature in firmware image"); + return NULL; +} + +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; + 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; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gboolean +fu_superio_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp = 0; + + if (g_strcmp0(key, "SuperioAutoloadAction") == 0) + return TRUE; + if (g_strcmp0(key, "SuperioId") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + priv->id = tmp; + return TRUE; + } + if (g_strcmp0(key, "SuperioPort") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + priv->port = tmp; + return TRUE; + } + if (g_strcmp0(key, "SuperioControlPort") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + priv->control_port = tmp; + return TRUE; + } + if (g_strcmp0(key, "SuperioDataPort") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, error)) + return FALSE; + priv->data_port = tmp; + return TRUE; + } + if (g_strcmp0(key, "SuperioTimeout") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, error)) + return FALSE; + priv->timeout_ms = tmp; + 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_superio_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_superio_device_init(FuSuperioDevice *self) +{ + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + + priv->timeout_ms = FU_PLUGIN_SUPERIO_DEFAULT_TIMEOUT; + + fu_device_set_physical_id(FU_DEVICE(self), "/dev/port"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.ite.superio"); + 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) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(object); + FuSuperioDevicePrivate *priv = GET_PRIVATE(self); + g_free(priv->chipset); + + 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; + + /** + * FuSuperioDevice:chipset: + * + * The SuperIO chipset name being used. + */ + 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); + + object_class->finalize = fu_superio_device_finalize; + klass_device->to_string = fu_superio_device_to_string; + klass_device->set_quirk_kv = fu_superio_device_set_quirk_kv; + klass_device->probe = fu_superio_device_probe; + klass_device->setup = fu_superio_device_setup; + klass_device->prepare_firmware = fu_superio_device_prepare_firmware; + klass_device->set_progress = fu_superio_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-device.h b/fwupd-1.8.6/plugins/superio/fu-superio-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5c1d482e3023a76a4e0ad32502ee0165134ff0e4 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-device.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SUPERIO_DEVICE (fu_superio_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuSuperioDevice, fu_superio_device, FU, SUPERIO_DEVICE, FuUdevDevice) + +struct _FuSuperioDeviceClass { + FuUdevDeviceClass parent_class; +}; + +gboolean +fu_superio_device_ec_read_data(FuSuperioDevice *self, guint8 *data, GError **error); +gboolean +fu_superio_device_ec_write_data(FuSuperioDevice *self, guint8 data, GError **error); +gboolean +fu_superio_device_ec_write_cmd(FuSuperioDevice *self, guint8 cmd, GError **error); +gboolean +fu_superio_device_reg_read(FuSuperioDevice *self, guint8 address, guint8 *data, GError **error); +gboolean +fu_superio_device_reg_write(FuSuperioDevice *self, guint8 address, guint8 data, GError **error); +gboolean +fu_superio_device_io_read(FuSuperioDevice *self, guint8 addr, guint8 *data, GError **error); +gboolean +fu_superio_device_io_read16(FuSuperioDevice *self, guint8 addr, guint16 *data, GError **error); +gboolean +fu_superio_device_io_write(FuSuperioDevice *self, guint8 addr, guint8 data, GError **error); diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-it55-device.c b/fwupd-1.8.6/plugins/superio/fu-superio-it55-device.c new file mode 100644 index 0000000000000000000000000000000000000000..4f276eb34c22d870fa357addf1780b8558ad379a --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-it55-device.c @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2021, TUXEDO Computers GmbH + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-superio-common.h" +#include "fu-superio-it55-device.h" + +/* ROM of IT5570 consists of 64KB blocks. Blocks can be further subdivided in + * 256-byte chunks, which is especially visible when erasing the ROM. This is + * because in case of erasure offset within a block is specified in chunks (even + * though erasure is done one kilobyte at a time). + * + * Accessing ROM requires entering a special mode, which should be always left + * to restore normal operation of EC (handling of buttons, keyboard, etc.). */ + +#define SIO_CMD_EC_WRITE_BLOCK 0x02 +#define SIO_CMD_EC_READ_BLOCK 0x03 +#define SIO_CMD_EC_ERASE_KBYTE 0x05 +#define SIO_CMD_EC_WRITE_1ST_KBYTE 0x06 +#define EC_ROM_ACCESS_ON_1 0xDE +#define EC_ROM_ACCESS_ON_2 0xDC +#define EC_ROM_ACCESS_OFF 0xFE + +#define BLOCK_SIZE 0x10000 +#define CHUNK_SIZE 0x100 +#define CHUNKS_IN_KBYTE 0x4 +#define CHUNKS_IN_BLOCK 0x100 + +#define MAX_FLASHING_ATTEMPTS 5 + +typedef enum { + AUTOLOAD_NO_ACTION, + AUTOLOAD_DISABLE, + AUTOLOAD_SET_ON, + AUTOLOAD_SET_OFF, +} AutoloadAction; + +struct _FuEcIt55Device { + FuSuperioDevice parent_instance; + gchar *prj_name; + AutoloadAction autoload_action; +}; + +G_DEFINE_TYPE(FuEcIt55Device, fu_superio_it55_device, FU_TYPE_SUPERIO_DEVICE) + +static void +fu_superio_it55_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE(device); + + /* FuSuperioDevice->to_string */ + FU_DEVICE_CLASS(fu_superio_it55_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "PrjName", self->prj_name); + fu_string_append_kx(str, idt, "AutoloadAction", self->autoload_action); +} + +static gboolean +fu_superio_it55_device_ec_project(FuSuperioDevice *device, GError **error) +{ + FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE(device); + gchar project[16] = {0x0}; + + if (!fu_superio_device_ec_write_cmd(device, SIO_CMD_EC_GET_NAME_STR, error)) + return FALSE; + + for (guint i = 0; i < sizeof(project) - 1; ++i) { + guint8 tmp = 0; + if (!fu_superio_device_ec_read_data(device, &tmp, error)) { + g_prefix_error(error, "failed to read firmware project: "); + return FALSE; + } + if (tmp == '$') + break; + project[i] = tmp; + } + + self->prj_name = g_strdup(project); + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it55_device_ec_version(FuSuperioDevice *self, GError **error) +{ + gchar version[16] = {'1', '.', '\0'}; + + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_GET_VERSION_STR, error)) + return FALSE; + + for (guint i = 2; i < sizeof(version) - 1; i++) { + guint8 tmp = 0; + if (!fu_superio_device_ec_read_data(self, &tmp, error)) { + g_prefix_error(error, "failed to read firmware version: "); + return FALSE; + } + if (tmp == '$') + break; + + version[i] = tmp; + } + + fu_device_set_version(FU_DEVICE(self), version); + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it55_device_ec_size(FuSuperioDevice *self, GError **error) +{ + guint8 tmp = 0; + + if (!fu_superio_device_reg_read(self, 0xf9, &tmp, error)) + return FALSE; + switch (tmp & 0xf0) { + case 0xf0: + fu_device_set_firmware_size(FU_DEVICE(self), BLOCK_SIZE * 4); + break; + case 0x40: + fu_device_set_firmware_size(FU_DEVICE(self), BLOCK_SIZE * 3); + break; + default: + fu_device_set_firmware_size(FU_DEVICE(self), BLOCK_SIZE * 2); + break; + } + + return TRUE; +} + +static gboolean +fu_superio_it55_device_setup(FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + + /* FuSuperioDevice->setup */ + if (!FU_DEVICE_CLASS(fu_superio_it55_device_parent_class)->setup(device, error)) + return FALSE; + + /* basic initialization */ + if (!fu_superio_device_reg_write(self, 0xf9, 0x20, error) || + !fu_superio_device_reg_write(self, 0xfa, 0x02, error) || + !fu_superio_device_reg_write(self, 0xfb, 0x00, error) || + !fu_superio_device_reg_write(self, 0xf8, 0xb1, error)) { + g_prefix_error(error, "initialization: "); + return FALSE; + } + + /* Order of interactions with EC below matters. Additionally, reading EC + * project seems to be mandatory for successful firmware operations. + * Test after making changes here! */ + + /* get size from the EC */ + if (!fu_superio_it55_device_ec_size(self, error)) + return FALSE; + + /* get installed firmware project from the EC */ + if (!fu_superio_it55_device_ec_project(self, error)) + return FALSE; + + /* get installed firmware version from the EC */ + if (!fu_superio_it55_device_ec_version(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static GBytes * +fu_plugin_superio_patch_autoload(FuDevice *device, GBytes *fw, GError **error) +{ + FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE(device); + guint offset; + gsize sz = 0; + const guint8 *unpatched = g_bytes_get_data(fw, &sz); + gboolean small_flash = (sz <= BLOCK_SIZE * 2); + g_autofree guint8 *patched = NULL; + + if (self->autoload_action == AUTOLOAD_NO_ACTION) + return g_bytes_ref(fw); + + for (offset = 0; offset < sz - 6; ++offset) { + if (unpatched[offset] == 0xa5 && + (unpatched[offset + 1] == 0xa5 || unpatched[offset + 1] == 0xa4) && + unpatched[offset + 5] == 0x5a) + break; + } + + if (offset >= sz - 6) + return g_bytes_ref(fw); + + /* not big enough */ + if (offset + 8 >= sz) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "image is too small to patch"); + return NULL; + } + + patched = fu_memdup_safe(unpatched, sz, error); + if (patched == NULL) + return NULL; + + if (self->autoload_action == AUTOLOAD_DISABLE) { + patched[offset + 2] = (small_flash ? 0x94 : 0x85); + patched[offset + 8] = 0x00; + } else if (self->autoload_action == AUTOLOAD_SET_ON) { + patched[offset + 2] = (small_flash ? 0x94 : 0x85); + patched[offset + 8] = (small_flash ? 0x7f : 0xbe); + } else if (self->autoload_action == AUTOLOAD_SET_OFF) { + patched[offset + 2] = (small_flash ? 0xa5 : 0xb5); + patched[offset + 8] = 0xaa; + } + + return g_bytes_new_take(g_steal_pointer(&patched), sz); +} + +/* progress callback is optional to not affect device progress during writing + * firmware */ +static GBytes * +fu_superio_it55_device_get_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + guint64 fwsize = fu_device_get_firmware_size_min(device); + guint64 block_count = (fwsize + BLOCK_SIZE - 1) / BLOCK_SIZE; + goffset offset = 0; + g_autofree guint8 *buf = NULL; + + buf = g_malloc0(fwsize); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, block_count); + for (guint i = 0; i < block_count; ++i) { + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_READ_BLOCK, error) || + !fu_superio_device_ec_write_cmd(self, i, error)) + return NULL; + + for (guint j = 0; j < BLOCK_SIZE; ++j, ++offset) { + if (!fu_superio_device_ec_read_data(self, &buf[offset], error)) + return NULL; + } + fu_progress_step_done(progress); + } + + return g_bytes_new_take(g_steal_pointer(&buf), fwsize); +} + +static GBytes * +fu_superio_it55_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* require detach -> attach */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return NULL; + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + return fu_superio_it55_device_get_firmware(device, progress, error); +} + +static gboolean +fu_superio_it55_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* leave ROM access mode */ + if (!fu_superio_device_ec_write_cmd(self, EC_ROM_ACCESS_OFF, error)) + return FALSE; + + /* success */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_superio_it55_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + /* enter ROM access mode */ + if (!fu_superio_device_ec_write_cmd(self, EC_ROM_ACCESS_ON_1, error) || + !fu_superio_device_ec_write_cmd(self, EC_ROM_ACCESS_ON_2, error)) + return FALSE; + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_superio_it55_device_erase(FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + guint64 fwsize = fu_device_get_firmware_size_min(device); + guint64 chunk_count = (fwsize + CHUNK_SIZE - 1) / CHUNK_SIZE; + + for (guint i = 0; i < chunk_count; i += CHUNKS_IN_KBYTE) { + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_ERASE_KBYTE, error) || + !fu_superio_device_ec_write_cmd(self, i / CHUNKS_IN_BLOCK, error) || + !fu_superio_device_ec_write_cmd(self, i % CHUNKS_IN_BLOCK, error) || + !fu_superio_device_ec_write_cmd(self, 0x00, error)) + return FALSE; + + g_usleep(1000); + } + + g_usleep(100000); + return TRUE; +} + +static gboolean +fu_superio_it55_device_write_attempt(FuDevice *device, + GBytes *firmware, + FuProgress *progress, + GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + const guint8 *fw_data = NULL; + g_autoptr(GBytes) erased_fw = NULL; + g_autoptr(GBytes) written_fw = NULL; + g_autoptr(GPtrArray) blocks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write-blk0"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 9, NULL); + + if (!fu_superio_it55_device_erase(device, error)) + return FALSE; + + erased_fw = + fu_superio_it55_device_get_firmware(device, fu_progress_get_child(progress), error); + if (erased_fw == NULL) { + g_prefix_error(error, "failed to read erased firmware"); + return FALSE; + } + if (!fu_bytes_is_empty(erased_fw)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "firmware was not erased"); + return FALSE; + } + fu_progress_step_done(progress); + + /* write everything but the first kilobyte */ + blocks = fu_chunk_array_new_from_bytes(firmware, 0x00, 0x00, BLOCK_SIZE); + for (guint i = 0; i < blocks->len; ++i) { + FuChunk *block = g_ptr_array_index(blocks, i); + gboolean first = (i == 0); + guint32 offset = 0; + guint32 bytes_left = fu_chunk_get_data_sz(block); + const guint8 *data = fu_chunk_get_data(block); + + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_WRITE_BLOCK, error) || + !fu_superio_device_ec_write_cmd(self, 0x00, error) || + !fu_superio_device_ec_write_cmd(self, i, error) || + !fu_superio_device_ec_write_cmd(self, first ? 0x04 : 0x00, error) || + !fu_superio_device_ec_write_cmd(self, 0x00, error)) + return FALSE; + + for (guint j = 0; j < CHUNKS_IN_BLOCK; ++j) { + if (first && j < CHUNKS_IN_KBYTE) { + offset += CHUNK_SIZE; + bytes_left -= CHUNK_SIZE; + continue; + } + + for (guint k = 0; k < CHUNK_SIZE; ++k) { + if (bytes_left == 0) { + if (!fu_superio_device_ec_write_data(self, 0xff, error)) + return FALSE; + continue; + } + + if (!fu_superio_device_ec_write_data(self, data[offset], error)) + return FALSE; + + ++offset; + --bytes_left; + } + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)blocks->len); + } + fu_progress_step_done(progress); + + /* now write the first kilobyte */ + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_WRITE_1ST_KBYTE, error)) + return FALSE; + fw_data = g_bytes_get_data(firmware, NULL); + for (guint i = 0; i < CHUNK_SIZE * CHUNKS_IN_KBYTE; ++i) + if (!fu_superio_device_ec_write_data(self, fw_data[i], error)) + return FALSE; + fu_progress_step_done(progress); + + g_usleep(1000); + + written_fw = + fu_superio_it55_device_get_firmware(device, fu_progress_get_child(progress), error); + if (written_fw == NULL) { + g_prefix_error(error, "failed to read firmware"); + return FALSE; + } + if (!fu_bytes_compare(written_fw, firmware, error)) { + g_prefix_error(error, "firmware verification"); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it55_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + gsize fwsize; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_patched = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + + /* require detach -> attach */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return FALSE; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + fwsize = g_bytes_get_size(fw); + if (fwsize < 1024) { + g_prefix_error(error, "firmware is too small: %u", (guint)fwsize); + return FALSE; + } + + fw_patched = fu_plugin_superio_patch_autoload(device, fw, error); + if (fw_patched == NULL) + return FALSE; + fu_progress_step_done(progress); + + /* try this many times; the failure-to-flash case leaves you without a + * keyboard and future boot may completely fail */ + for (guint i = 1;; ++i) { + g_autoptr(GError) error_chk = NULL; + if (fu_superio_it55_device_write_attempt(device, + fw_patched, + fu_progress_get_child(progress), + &error_chk)) + break; + + if (i == MAX_FLASHING_ATTEMPTS) { + g_propagate_error(error, g_steal_pointer(&error_chk)); + return FALSE; + } + + g_warning("failure %u: %s", i, error_chk->message); + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gchar * +fu_ec_extract_field(GBytes *fw, const gchar *name, GError **error) +{ + guint offset; + gsize prefix_len; + gsize fwsz = 0; + const gchar *value; + const guint8 *buf = g_bytes_get_data(fw, &fwsz); + g_autofree gchar *field = g_strdup_printf("%s:", name); + + prefix_len = strlen(field); + + for (offset = 0; offset < fwsz - prefix_len; ++offset) { + if (memcmp(&buf[offset], field, prefix_len) == 0) + break; + } + + if (offset >= fwsz - prefix_len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "did not find %s field in the firmware image", + name); + return NULL; + } + + offset += prefix_len; + value = (const gchar *)&buf[offset]; + + for (; offset < fwsz; ++offset) { + if (buf[offset] == '$') + return g_strndup(value, (const gchar *)&buf[offset] - value); + } + + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "couldn't extract %s field value from the firmware image", + name); + return NULL; +} + +static FuFirmware * +fu_superio_it55_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE(device); + g_autofree gchar *date = NULL; + g_autofree gchar *prj_name = NULL; + g_autofree gchar *version = NULL; + + prj_name = fu_ec_extract_field(fw, "PRJ", error); + if (prj_name == NULL) + return NULL; + + version = fu_ec_extract_field(fw, "VER", error); + if (version == NULL) + version = g_strdup("(unknown version)"); + + date = fu_ec_extract_field(fw, "DATE", error); + if (date == NULL) + date = g_strdup("(unknown build date)"); + + g_debug("New firmware: %s %s built on %s", prj_name, version, date); + if (g_strcmp0(prj_name, self->prj_name) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware targets %s instead of %s", + prj_name, + self->prj_name); + return NULL; + } + + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_superio_it55_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE(device); + + /* FuSuperioDevice->set_quirk_kv */ + if (!FU_DEVICE_CLASS(fu_superio_it55_device_parent_class) + ->set_quirk_kv(device, key, value, error)) + return FALSE; + + if (g_strcmp0(key, "SuperioAutoloadAction") == 0) { + if (g_strcmp0(value, "none") == 0) { + self->autoload_action = AUTOLOAD_NO_ACTION; + } else if (g_strcmp0(value, "disable") == 0) { + self->autoload_action = AUTOLOAD_DISABLE; + } else if (g_strcmp0(value, "on") == 0) { + self->autoload_action = AUTOLOAD_SET_ON; + } else if (g_strcmp0(value, "off") == 0) { + self->autoload_action = AUTOLOAD_SET_OFF; + } else { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid value"); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_superio_it55_device_init(FuEcIt55Device *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); + /* version string example: 1.07.02TR1 */ + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); +} + +static void +fu_superio_it55_device_finalize(GObject *obj) +{ + FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE(obj); + g_free(self->prj_name); + G_OBJECT_CLASS(fu_superio_it55_device_parent_class)->finalize(obj); +} + +static void +fu_superio_it55_device_class_init(FuEcIt55DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + G_OBJECT_CLASS(klass)->finalize = fu_superio_it55_device_finalize; + klass_device->to_string = fu_superio_it55_device_to_string; + klass_device->attach = fu_superio_it55_device_attach; + klass_device->detach = fu_superio_it55_device_detach; + klass_device->dump_firmware = fu_superio_it55_device_dump_firmware; + klass_device->write_firmware = fu_superio_it55_device_write_firmware; + klass_device->setup = fu_superio_it55_device_setup; + klass_device->prepare_firmware = fu_superio_it55_device_prepare_firmware; + klass_device->set_quirk_kv = fu_superio_it55_device_set_quirk_kv; +} diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-it55-device.h b/fwupd-1.8.6/plugins/superio/fu-superio-it55-device.h new file mode 100644 index 0000000000000000000000000000000000000000..021a4b1b981070b0a9ecdb4fb8afe1698eac8671 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-it55-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021, TUXEDO Computers GmbH + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-superio-device.h" + +#define FU_TYPE_EC_IT55_DEVICE (fu_superio_it55_device_get_type()) +G_DECLARE_FINAL_TYPE(FuEcIt55Device, + fu_superio_it55_device, + FU, + SUPERIO_IT55_DEVICE, + FuSuperioDevice) diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-it85-device.c b/fwupd-1.8.6/plugins/superio/fu-superio-it85-device.c new file mode 100644 index 0000000000000000000000000000000000000000..a050627d54c7062cc1214642edeed9b5de1960d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-it85-device.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#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) +{ + g_autoptr(GString) str = g_string_new(NULL); + if (!fu_superio_device_ec_write_cmd(self, idx, error)) + return NULL; + for (guint i = 0; i < 0xff; i++) { + guint8 c = 0; + if (!fu_superio_device_ec_read_data(self, &c, error)) + return NULL; + if (c == '$') + break; + g_string_append_c(str, c); + } + return g_string_free(g_steal_pointer(&str), FALSE); +} + +static gboolean +fu_superio_it85_device_setup(FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + guint8 size_tmp = 0; + g_autofree gchar *name = NULL; + g_autofree gchar *version = NULL; + + /* FuSuperioDevice->setup */ + if (!FU_DEVICE_CLASS(fu_superio_it85_device_parent_class)->setup(device, error)) + return FALSE; + + /* get EC size */ + if (!fu_superio_device_reg_read(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); + return TRUE; +} + +static void +fu_superio_it85_device_init(FuSuperioIt85Device *self) +{ +} + +static void +fu_superio_it85_device_class_init(FuSuperioIt85DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_superio_it85_device_setup; +} diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-it85-device.h b/fwupd-1.8.6/plugins/superio/fu-superio-it85-device.h new file mode 100644 index 0000000000000000000000000000000000000000..c66647318a0e63f93faf21f0fa921a3109576aa4 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-it85-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-superio-device.h" + +#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) diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-it89-device.c b/fwupd-1.8.6/plugins/superio/fu-superio-it89-device.c new file mode 100644 index 0000000000000000000000000000000000000000..7a7779a308622ac095f2fb4e9155e991e41fbb92 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-it89-device.c @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#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_io_write(self, SIO_LDNxx_IDX_D2ADR, SIO_DEPTH2_I2EC_ADDRH, error)) + return FALSE; + if (!fu_superio_device_io_write(self, SIO_LDNxx_IDX_D2DAT, addr >> 8, error)) + return FALSE; + if (!fu_superio_device_io_write(self, SIO_LDNxx_IDX_D2ADR, SIO_DEPTH2_I2EC_ADDRL, error)) + return FALSE; + if (!fu_superio_device_io_write(self, SIO_LDNxx_IDX_D2DAT, addr & 0xff, error)) + return FALSE; + if (!fu_superio_device_io_write(self, SIO_LDNxx_IDX_D2ADR, SIO_DEPTH2_I2EC_DATA, error)) + return FALSE; + return fu_superio_device_io_read(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(FuDevice *device, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + guint8 version_tmp[2] = {0x00}; + g_autofree gchar *version = NULL; + + /* FuSuperioDevice->setup */ + if (!FU_DEVICE_CLASS(fu_superio_it89_device_parent_class)->setup(device, error)) + return FALSE; + + /* 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_reg_read(self, 0x00, &version_tmp[0], error)) { + g_prefix_error(error, "failed to get version major: "); + return FALSE; + } + if (!fu_superio_device_reg_read(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); + + /* 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_write_cmd(self, SIO_EC_PMC_PM1DOSCI, error)) + return FALSE; + if (!fu_superio_device_ec_write_cmd(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_write_cmd(self, SIO_EC_PMC_PM1DOCMI, error)) + return FALSE; + if (!fu_superio_device_ec_write_cmd(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_write_cmd(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_write_cmd(self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read_data(self, &tmp, error)) + return FALSE; + } while ((tmp & SIO_STATUS_EC_OBF) != 0); + + /* watch SCI events */ + return fu_superio_device_ec_write_cmd(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_write_cmd(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_write_cmd(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_write_cmd(self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read_data(self, &tmp, error)) + return FALSE; + } while ((tmp & SIO_STATUS_EC_IBF) != 0); + + /* watch SCI events */ + return fu_superio_device_ec_write_cmd(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_write_cmd(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_write_cmd(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_write_cmd(self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read_data(self, &tmp, error)) + return FALSE; + } while ((tmp & 3) != SIO_STATUS_EC_IBF); + + /* watch SCI events */ + return fu_superio_device_ec_write_cmd(self, SIO_EC_PMC_PM1DISCI, error); +} + +static GBytes * +fu_superio_it89_device_read_addr(FuSuperioDevice *self, + guint32 addr, + guint size, + FuProgress *progress, + 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_write_cmd(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); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, size); + for (guint i = 0; i < size; i++) { + if (!fu_superio_device_ec_write_cmd(self, SIO_EC_PMC_PM1DI, error)) + return NULL; + if (!fu_superio_device_ec_read_data(self, &buf[i], error)) + return NULL; + + /* update progress */ + fu_progress_step_done(progress); + } + + /* 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 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_write_cmd(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_write_cmd(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_write_cmd(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_write_cmd(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 = fu_memdup_safe(buf, sz, error); + if (buf2 == NULL) + return NULL; + buf2[signature_offset] = 0x7f; + return g_bytes_new_take(g_steal_pointer(&buf2), sz); +} + +static GBytes * +fu_superio_it89_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + guint64 fwsize = fu_device_get_firmware_size_min(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* require detach -> attach */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return NULL; + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + return fu_superio_it89_device_read_addr(self, 0x0, fwsize, progress, error); +} + +static FuFirmware * +fu_superio_it89_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) fw = NULL; + + blob = fu_superio_it89_device_dump_firmware(device, progress, error); + fw = fu_plugin_superio_fix_signature(self, blob, error); + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_superio_it89_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + + /* re-enable HOSTWA -- use 0xfd for LCFC */ + if (!fu_superio_device_ec_write_cmd(self, SIO_CMD_EC_ENABLE_HOST_WA, error)) + return FALSE; + + /* success */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_superio_it89_device_detach(FuDevice *device, FuProgress *progress, 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_write_cmd(self, SIO_CMD_EC_DISABLE_HOST_WA, error)) + return FALSE; + if (!fu_superio_device_ec_read_data(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(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_superio_it89_device_check_eflash(FuSuperioDevice *self, FuProgress *progress, 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, progress, error); + if (fw == NULL) { + g_prefix_error(error, "failed to read signature bytes: "); + return FALSE; + } + + /* cannot flash here without keyboard programmer */ + if (!fu_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, + FuProgress *progress, + GError **error) +{ + g_autoptr(GBytes) fw1 = NULL; + g_autoptr(GBytes) fw2 = NULL; + g_autoptr(GBytes) fw3 = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, "page-erase"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 21, "check-erase"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 59, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 20, NULL); + + /* erase page */ + if (!fu_superio_it89_device_erase_addr(self, fu_chunk_get_address(chk), error)) { + g_prefix_error(error, + "failed to erase @0x%04x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + + /* check erased */ + fw1 = fu_superio_it89_device_read_addr(self, + fu_chunk_get_address(chk), + fu_chunk_get_data_sz(chk), + fu_progress_get_child(progress), + error); + if (fw1 == NULL) { + g_prefix_error(error, + "failed to read erased bytes @0x%04x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + if (!fu_bytes_is_empty(fw1)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "sector was not erased"); + return FALSE; + } + fu_progress_step_done(progress); + + /* skip empty page */ + fw2 = g_bytes_new_static(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + if (fu_bytes_is_empty(fw2)) { + fu_progress_finished(progress); + return TRUE; + } + + /* write page */ + if (!fu_superio_it89_device_write_addr(self, fu_chunk_get_address(chk), fw2, error)) { + g_prefix_error(error, + "failed to write @0x%04x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + + /* verify page */ + fw3 = fu_superio_it89_device_read_addr(self, + fu_chunk_get_address(chk), + fu_chunk_get_data_sz(chk), + fu_progress_get_child(progress), + error); + if (fw3 == NULL) { + g_prefix_error(error, + "failed to read written " + "bytes @0x%04x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + if (!fu_bytes_compare(fw2, fw3, error)) { + g_prefix_error(error, + "failed to verify @0x%04x: ", + (guint)fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + + /* 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_write_cmd(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_write_cmd(self, SIO_EC_PMC_PM1DI, error)) + return FALSE; + if (!fu_superio_device_ec_read_data(self, &id[i], error)) + return FALSE; + } + + /* watch SCI events */ + return fu_superio_device_ec_write_cmd(self, SIO_EC_PMC_PM1DISCI, error); +} + +static gboolean +fu_superio_it89_device_write_chunks(FuSuperioDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len - 1); + 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, + fu_progress_get_child(progress), + &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); + fu_progress_reset(fu_progress_get_child(progress)); + } + + /* set progress */ + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_superio_it89_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSuperioDevice *self = FU_SUPERIO_DEVICE(device); + guint8 id[4] = {0x0}; + g_autoptr(GBytes) fw_fixed = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "check-eflash"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, 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, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* disable the mirroring of e-flash */ + if (g_getenv("FWUPD_SUPERIO_DISABLE_MIRROR") != NULL) { + fw_fixed = fu_plugin_superio_fix_signature(self, fw, error); + 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); + if (!fu_superio_it89_device_write_chunks(self, + chunks, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + 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); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_superio_it89_device_class_init(FuSuperioIt89DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_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->dump_firmware = fu_superio_it89_device_dump_firmware; + klass_device->write_firmware = fu_superio_it89_device_write_firmware; + klass_device->setup = fu_superio_it89_device_setup; +} diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-it89-device.h b/fwupd-1.8.6/plugins/superio/fu-superio-it89-device.h new file mode 100644 index 0000000000000000000000000000000000000000..2f7a92beb3aed76eda2772a6879d7f103f74b0b8 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-it89-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-superio-device.h" + +#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) diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-plugin.c b/fwupd-1.8.6/plugins/superio/fu-superio-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..99ca43a015701f562fd2b575ecd5a6dd6cb835a2 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-plugin.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2021, TUXEDO Computers GmbH + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-superio-it55-device.h" +#include "fu-superio-it85-device.h" +#include "fu-superio-it89-device.h" +#include "fu-superio-plugin.h" + +struct _FuSuperioPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSuperioPlugin, fu_superio_plugin, FU_TYPE_PLUGIN) + +#define FU_QUIRKS_SUPERIO_GTYPE "SuperioGType" + +static gboolean +fu_superio_plugin_coldplug_chipset(FuPlugin *plugin, const gchar *guid, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *dmi_vendor; + const gchar *chipset; + GType custom_gtype; + g_autoptr(FuSuperioDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get chipset */ + chipset = fu_context_lookup_quirk_by_id(ctx, guid, FU_QUIRKS_SUPERIO_GTYPE); + if (chipset == NULL) + return TRUE; + + /* create IT89xx, IT89xx or IT5570 */ + custom_gtype = g_type_from_name(chipset); + if (custom_gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "SuperIO GType %s unsupported", + chipset); + return FALSE; + } + dev = g_object_new(custom_gtype, + "device-file", + "/dev/port", + "chipset", + chipset, + "context", + ctx, + NULL); + + /* add this so we can attach all the other quirks */ + fu_device_add_instance_str(FU_DEVICE(dev), "GUID", guid); + if (!fu_device_build_instance_id(FU_DEVICE(dev), error, "SUPERIO", "GUID", NULL)) + return FALSE; + + /* set ID and ports via quirks */ + if (!fu_device_probe(FU_DEVICE(dev), error)) + return FALSE; + + /* set vendor ID as the motherboard vendor */ + dmi_vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BASEBOARD_MANUFACTURER); + if (dmi_vendor != NULL) { + g_autofree gchar *vendor_id = g_strdup_printf("DMI:%s", dmi_vendor); + fu_device_add_vendor_id(FU_DEVICE(dev), vendor_id); + } + + /* 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_superio_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + GPtrArray *hwids; + + if (fu_kernel_locked_down()) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported when kernel locked down"); + return FALSE; + } + + hwids = fu_context_get_hwid_guids(ctx); + for (guint i = 0; i < hwids->len; i++) { + const gchar *guid = g_ptr_array_index(hwids, i); + if (!fu_superio_plugin_coldplug_chipset(plugin, guid, error)) + return FALSE; + } + return TRUE; +} + +static void +fu_superio_plugin_init(FuSuperioPlugin *self) +{ +} + +static void +fu_superio_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "SuperioGType"); + fu_context_add_quirk_key(ctx, "SuperioId"); + fu_context_add_quirk_key(ctx, "SuperioPort"); + fu_context_add_quirk_key(ctx, "SuperioControlPort"); + fu_context_add_quirk_key(ctx, "SuperioDataPort"); + fu_context_add_quirk_key(ctx, "SuperioTimeout"); + fu_context_add_quirk_key(ctx, "SuperioAutoloadAction"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_EC_IT55_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SUPERIO_IT85_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SUPERIO_IT89_DEVICE); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown"); +} + +static void +fu_superio_plugin_class_init(FuSuperioPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_superio_plugin_constructed; + plugin_class->coldplug = fu_superio_plugin_coldplug; +} diff --git a/fwupd-1.8.6/plugins/superio/fu-superio-plugin.h b/fwupd-1.8.6/plugins/superio/fu-superio-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..a581e57c7301027a6a66ed51d33ab14720fb2c2b --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/fu-superio-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSuperioPlugin, fu_superio_plugin, FU, SUPERIO_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/superio/meson.build b/fwupd-1.8.6/plugins/superio/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..986bcb5d86016522c09ab4424f54f267f5515b72 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/meson.build @@ -0,0 +1,19 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginSuperio"'] + +plugin_quirks += files('superio.quirk') +plugin_builtins += static_library('fu_plugin_superio', + sources: [ + 'fu-superio-plugin.c', + 'fu-superio-device.c', + 'fu-superio-it55-device.c', + 'fu-superio-it85-device.c', + 'fu-superio-it89-device.c', + 'fu-superio-common.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/superio/superio.quirk b/fwupd-1.8.6/plugins/superio/superio.quirk new file mode 100644 index 0000000000000000000000000000000000000000..b437a8da5ee5a3ad6d66a13b6b875966cd037ed7 --- /dev/null +++ b/fwupd-1.8.6/plugins/superio/superio.quirk @@ -0,0 +1,86 @@ +# N13xWU +[992f1bc7-f8ee-567a-88dd-30e5158d72ed] +SuperioGType = FuSuperioIt85Device +[SUPERIO\GUID_992f1bc7-f8ee-567a-88dd-30e5158d72ed] +SuperioId = 0x8587 +SuperioPort = 0x2e + +# W740SU +[f00d8c4e-dce2-51c3-89d6-6cbc5fc5cdbb] +SuperioGType = FuSuperioIt85Device +[SUPERIO\GUID_f00d8c4e-dce2-51c3-89d6-6cbc5fc5cdbb] +SuperioId = 0x8587 +SuperioPort = 0x2e + +# Star LabTop Mk III (HwId - AMI) +[013b60e5-1023-5bee-8ae5-14cae21377b7] +SuperioGType = FuSuperioIt89Device +[SUPERIO\GUID_013b60e5-1023-5bee-8ae5-14cae21377b7] +SuperioId = 0x8987 +SuperioPort = 0x4e +InstallDuration = 20 +Flags = unsigned-payload + +# Star LabTop MK III (HwId - coreboot) +[8f8ca82b-30e1-5907-bc9d-4257a49898d4] +SuperioGType = FuSuperioIt89Device +[SUPERIO\GUID_8f8ca82b-30e1-5907-bc9d-4257a49898d4] +SuperioId = 0x8987 +SuperioPort = 0x4e +InstallDuration = 20 +Flags = unsigned-payload + +# Star LabTop Mk IV (HwId) +[baf1d04e-fd16-5e6a-93cc-1c23d171f879] +SuperioGType = FuSuperioIt89Device +[SUPERIO\GUID_baf1d04e-fd16-5e6a-93cc-1c23d171f879] +SuperioId = 0x8987 +SuperioPort = 0x4e +InstallDuration = 20 + +# StarBook Mk V (HwId) +[85aba599-addd-5985-a2e8-eddb41c61ba3] +SuperioGType = FuSuperioIt89Device +[SUPERIO\GUID_85aba599-addd-5985-a2e8-eddb41c61ba3] +SuperioId = 0x5570 +SuperioPort = 0x4e +InstallDuration = 20 +Flags = signed-payload + +# Star Lite Mk II (HwId) +[013b60e5-1023-5bee-8ae5-14cae21377b7] +SuperioGType = FuSuperioIt89Device +[SUPERIO\GUID_013b60e5-1023-5bee-8ae5-14cae21377b7] +SuperioId = 0x8987 +SuperioPort = 0x4e +InstallDuration = 20 +Flags = unsigned-payload + +# Star Lite Mk III (HwId) +[d5521faa-c50b-5d64-971d-8fd400030c51] +SuperioGType = FuSuperioIt89Device +[SUPERIO\GUID_d5521faa-c50b-5d64-971d-8fd400030c51] +SuperioId = 0x8987 +SuperioPort = 0x4e +InstallDuration = 20 +Flags = signed-payload + +# Tuxedo InifinityBook S14 Gen6 +[6c80d85b-d0b6-5ee2-99d4-ec28dd32febd] +SuperioGType = FuEcIt55Device +[SUPERIO\GUID_6c80d85b-d0b6-5ee2-99d4-ec28dd32febd] +SuperioId = 0x5570 +SuperioControlPort = 0x66 +SuperioDataPort = 0x62 +SuperioAutoloadAction = disable +SuperioTimeout = 650 + +# Tuxedo InifinityBook S15 Gen6 +[60f53465-e8fc-5122-b79b-f7b03f063037] +SuperioGType = FuEcIt55Device +[SUPERIO\GUID_60f53465-e8fc-5122-b79b-f7b03f063037] +SuperioId = 0x5570 +SuperioControlPort = 0x66 +SuperioDataPort = 0x62 +SuperioAutoloadAction = disable +SuperioTimeout = 650 diff --git a/fwupd-1.8.6/plugins/synaptics-cape/README.md b/fwupd-1.8.6/plugins/synaptics-cape/README.md new file mode 100644 index 0000000000000000000000000000000000000000..70bde599deaa8f44326720fe08ba90c19175c2d3 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/README.md @@ -0,0 +1,42 @@ +# Synaptics CAPE devices + +## Introduction + +This plugin is used to update Synaptics CAPE based audio devices. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob. + +This plugin supports the following protocol ID: + +* com.synaptics.cape + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1395&PID_0293` + +These devices also use custom GUID values, e.g. + +* `SYNAPTICS_CAPE\CX31993` +* `SYNAPTICS_CAPE\CX31988` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1395` + +### Plugin-specific flags + +* use-in-report-interrupt: some devices will support IN_REPORT that allow host communicate with + device over interrupt instead of control endpoint, since: 1.7.0 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-device.c b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-device.c new file mode 100644 index 0000000000000000000000000000000000000000..76b01f1dbba9e43ccda17caebfae148ca33be5c8 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-device.c @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2021 Synaptics Incorporated + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-synaptics-cape-device.h" +#include "fu-synaptics-cape-firmware.h" + +/* defines timings */ +#define FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_WRITE_TIMEOUT 20000 /* us */ +#define FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_READ_TIMEOUT 30000 /* us */ +#define FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_RETRY_INTERVAL 10 /* ms */ +#define FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_RETRY_TIMEOUT 300 /* ms */ +#define FU_SYNAPTICS_CAPE_DEVICE_USB_RESET_DELAY_MS 3000 /* ms */ + +/* defines CAPE command constant values and macro */ +#define FU_SYNAPTICS_CAPE_DEVICE_GOLEM_REPORT_ID 1 /* HID report id */ + +#define FU_SYNAPTICS_CAPE_CMD_MAX_DATA_LEN 13 /* number of guint32 */ +#define FU_SYNAPTICS_CAPE_CMD_WRITE_DATAL_LEN 8 /* number of guint32 */ + +#define FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL 0xb32d2300u + +/* CAPE command return codes */ +#define FU_SYNAPTICS_CAPE_MODULE_RC_GENERIC_FAILURE (-1025) +#define FU_SYNAPTICS_CAPE_MODULE_RC_ALREADY_EXISTS (-1026) +#define FU_SYNAPTICS_CAPE_MODULE_RC_NULL_APP_POINTER (-1027) +#define FU_SYNAPTICS_CAPE_MODULE_RC_NULL_MODULE_POINTER (-1028) +#define FU_SYNAPTICS_CAPE_MODULE_RC_NULL_STREAM_POINTER (-1029) +#define FU_SYNAPTICS_CAPE_MODULE_RC_NULL_POINTER (-1030) + +#define FU_SYNAPTICS_CAPE_MODULE_RC_BAD_APP_ID (-1031) +#define FU_SYNAPTICS_CAPE_MODULE_RC_MODULE_TYPE_HAS_NO_API (-1034) +#define FU_SYNAPTICS_CAPE_MODULE_RC_BAD_MAGIC_NUMBER (-1052) +#define FU_SYNAPTICS_CAPE_MODULE_RC_CMD_MODE_UNSUPPORTED (-1056) + +#define FU_SYNAPTICS_CMD_GET_FLAG 0x100 /* GET flag */ + +#define FU_SYNAPTICS_CAPE_FM3_HID_INTR_IN_EP 0x83 + +/* CAPE message structure, Little endian */ +typedef struct __attribute__((packed)) { + gint16 data_len : 16; /* data length in dwords */ + guint16 cmd_id : 15; /* command id */ + guint16 reply : 1; /* host want a reply from device, 1 = true */ + guint32 module_id; /* module id */ + guint32 data[FU_SYNAPTICS_CAPE_CMD_MAX_DATA_LEN]; /* command data */ +} FuCapCmd; + +/* CAPE HID report structure */ +typedef struct __attribute__((packed)) { + guint16 report_id; /* two bytes of report id, this should be 1 */ + FuCapCmd cmd; +} FuCapCmdHidReport; + +/* CAPE commands */ +typedef enum { + FU_SYNAPTICS_CMD_FW_UPDATE_START = 0xC8, /* notifies firmware update started */ + FU_SYNAPTICS_CMD_FW_UPDATE_WRITE = 0xC9, /* updates firmware data */ + FU_SYNAPTICS_CMD_FW_UPDATE_END = 0xCA, /* notifies firmware update finished */ + FU_SYNAPTICS_CMD_MCU_SOFT_RESET = 0xAF, /* reset device*/ + FU_SYNAPTICS_CMD_FW_GET_ACTIVE_PARTITION = 0x1CF, /* gets cur active partition number */ + FU_SYNAPTICS_CMD_GET_VERSION = 0x103, /* gets cur firmware version */ +} FuCommand; + +/* CAPE fwupd device structure */ +struct _FuSynapticsCapeDevice { + FuHidDevice parent_instance; + guint32 active_partition; /* active partition, either 1 or 2 */ +}; + +/** + * FU_SYNAPTICS_CAPE_DEVICE_FLAG_USE_IN_REPORT_INTERRUPT: + * + * gets HID REPORT via Interrupt instead of Control endpoint. + */ +#define FU_SYNAPTICS_CAPE_DEVICE_FLAG_USE_IN_REPORT_INTERRUPT (1 << 0) + +G_DEFINE_TYPE(FuSynapticsCapeDevice, fu_synaptics_cape_device, FU_TYPE_HID_DEVICE) + +/* sends SET_REPORT to device */ +static gboolean +fu_synaptics_cape_device_set_report(FuSynapticsCapeDevice *self, + const FuCapCmdHidReport *data, + GError **error) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (g_getenv("FWUPD_SYNAPTICS_CAPE_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SetReport", (guint8 *)data, sizeof(*data)); + + return fu_hid_device_set_report(FU_HID_DEVICE(self), + FU_SYNAPTICS_CAPE_DEVICE_GOLEM_REPORT_ID, + (guint8 *)data, + sizeof(*data), + FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_WRITE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error); +} + +/* gets HID report over control ep */ +static gboolean +fu_synaptics_cape_device_get_report(FuSynapticsCapeDevice *self, + FuCapCmdHidReport *data, + GError **error) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + FU_SYNAPTICS_CAPE_DEVICE_GOLEM_REPORT_ID, + (guint8 *)data, + sizeof(*data), + FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_READ_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + if (g_getenv("FWUPD_SYNAPTICS_CAPE_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetReport", (guint8 *)data, sizeof(*data)); + + /* success */ + return TRUE; +} + +/* gets HID report over interrupt ep */ +static gboolean +fu_synaptics_cape_device_get_report_intr(FuSynapticsCapeDevice *self, + FuCapCmdHidReport *data, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(FU_HID_DEVICE(self))); + gsize actual_len = 0; + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + if (!g_usb_device_interrupt_transfer(usb_device, + FU_SYNAPTICS_CAPE_FM3_HID_INTR_IN_EP, + (guint8 *)data, + sizeof(*data), + &actual_len, + FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_RETRY_TIMEOUT * 1000, + NULL, + error)) { + g_prefix_error(error, "failed to get report over interrupt ep: "); + return FALSE; + } + + if (g_getenv("FWUPD_SYNAPTICS_CAPE_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "GetReport", (guint8 *)data, sizeof(*data)); + + /* success */ + return TRUE; +} + +/* dump CAPE command error if any */ +static gboolean +fu_synaptics_cape_device_rc_set_error(const FuCapCmd *rsp, GError **error) +{ + g_return_val_if_fail(rsp != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (rsp->data_len >= 0) + return TRUE; + + switch (rsp->data_len) { + case FU_SYNAPTICS_CAPE_MODULE_RC_GENERIC_FAILURE: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: generic failure"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_ALREADY_EXISTS: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: already exists"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_NULL_APP_POINTER: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: null app pointer"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_NULL_MODULE_POINTER: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: null module pointer"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_NULL_POINTER: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: null pointer"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_BAD_APP_ID: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: bad app id"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_MODULE_TYPE_HAS_NO_API: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: has no api"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_BAD_MAGIC_NUMBER: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: bad magic number"); + break; + case FU_SYNAPTICS_CAPE_MODULE_RC_CMD_MODE_UNSUPPORTED: + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "CMD ERROR: mode unsupported"); + break; + default: + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "CMD ERROR: unknown error: %d", + rsp->data_len); + } + + /* success */ + return FALSE; +} + +/* sends a FuCapCmd structure command to device to get the response in the same structure */ +static gboolean +fu_synaptics_cape_device_sendcmd_ex(FuSynapticsCapeDevice *self, + FuCapCmd *req, + gulong delay_us, + GError **error) +{ + FuCapCmdHidReport report = {0}; + guint elapsed_ms = 0; + gboolean is_get = FALSE; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(req != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* first two bytes are report id */ + report.report_id = GINT16_TO_LE(FU_SYNAPTICS_CAPE_DEVICE_GOLEM_REPORT_ID); + + if (!fu_memcpy_safe((guint8 *)&report.cmd, + sizeof(report.cmd), + 0, /* dst */ + (const guint8 *)req, + sizeof(*req), + 0, /* src */ + sizeof(*req), + error)) + return FALSE; + + /* sets data length to MAX for any GET commands */ + if (FU_SYNAPTICS_CMD_GET_FLAG & report.cmd.cmd_id) { + is_get = TRUE; + report.cmd.data_len = GINT16_TO_LE(FU_SYNAPTICS_CAPE_CMD_MAX_DATA_LEN); + } else { + report.cmd.data_len = GINT16_TO_LE(report.cmd.data_len); + } + + report.cmd.cmd_id = GUINT16_TO_LE(report.cmd.cmd_id); + report.cmd.module_id = GUINT32_TO_LE(report.cmd.module_id); + + if (!fu_synaptics_cape_device_set_report(self, &report, error)) { + g_prefix_error(error, "failed to send: "); + return FALSE; + } + + if (delay_us > 0) + g_usleep(delay_us); + + /* waits for the command to complete. There are two appraoches to get status from device: + * 1. gets IN_REPORT over interrupt endpoint. device won't reply until a command operation + * has completed. This works only on devices support interrupt endpoint. + * 2. polls GET_REPORT over control endpoint. device will return 'reply==0' before a + * command operation has completed. + */ + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_SYNAPTICS_CAPE_DEVICE_FLAG_USE_IN_REPORT_INTERRUPT)) { + if (!fu_synaptics_cape_device_get_report_intr(self, &report, &error_local)) { + /* ignoring io error for software reset command */ + if ((req->cmd_id == FU_SYNAPTICS_CMD_MCU_SOFT_RESET) && + (req->module_id == FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL) && + (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE) || + g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_FAILED))) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to get IN_REPORT: "); + return FALSE; + } + } else { + for (; elapsed_ms < FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_RETRY_TIMEOUT; + elapsed_ms += FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_RETRY_INTERVAL) { + if (!fu_synaptics_cape_device_get_report(self, &report, &error_local)) { + /* ignoring io error for software reset command */ + if ((req->cmd_id == FU_SYNAPTICS_CMD_MCU_SOFT_RESET) && + (req->module_id == FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL) && + (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE) || + g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_FAILED))) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to get GET_REPORT: "); + return FALSE; + } + if (report.cmd.reply) + break; + g_usleep(FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_RETRY_INTERVAL * 1000); + } + } + + if (!report.cmd.reply) { + if (error != NULL) + g_prefix_error(error, "send command time out:: "); + else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware don't respond to command"); + return FALSE; + } + + /* copies returned data if it is GET command */ + if (is_get) { + req->data_len = (gint16)fu_memread_uint16((guint8 *)&report.cmd, G_LITTLE_ENDIAN); + + for (int i = 0; i < FU_SYNAPTICS_CAPE_CMD_MAX_DATA_LEN; i++) + req->data[i] = GUINT32_FROM_LE(report.cmd.data[i]); + } + + return fu_synaptics_cape_device_rc_set_error(&report.cmd, error); +} + +/* a simple version of sendcmd_ex without returned data */ +static gboolean +fu_synaptics_cape_device_sendcmd(FuSynapticsCapeDevice *self, + const guint32 module_id, + const guint32 cmd_id, + const guint32 *data, + const guint32 data_len, + const gulong delay_us, + GError **error) +{ + FuCapCmd cmd = {0}; + const guint32 dataszbyte = data_len * sizeof(guint32); + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + cmd.cmd_id = GUINT16_TO_LE(cmd_id); + cmd.module_id = GUINT32_TO_LE(module_id); + + if (data_len != 0 && data != NULL) { + cmd.data_len = data_len; + if (!fu_memcpy_safe((guint8 *)cmd.data, + sizeof(cmd.data), + 0, /* dst */ + (const guint8 *)data, + dataszbyte, + 0, /* src */ + dataszbyte, + error)) + return FALSE; + } + return fu_synaptics_cape_device_sendcmd_ex(self, &cmd, delay_us, error); +} + +static void +fu_synaptics_cape_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuSynapticsCapeDevice *self = FU_SYNAPTICS_CAPE_DEVICE(device); + + g_return_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self)); + + fu_string_append_ku(str, idt, "ActivePartition", self->active_partition); +} + +/* resets device */ +static gboolean +fu_synaptics_cape_device_reset(FuSynapticsCapeDevice *self, GError **error) +{ + g_autoptr(GTimer) timer = g_timer_new(); + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_synaptics_cape_device_sendcmd(self, + FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL, + FU_SYNAPTICS_CMD_MCU_SOFT_RESET, + NULL, + 0, + 0, + error)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "reset command is not supported"); + + return FALSE; + } + + g_usleep(1000 * FU_SYNAPTICS_CAPE_DEVICE_USB_RESET_DELAY_MS); + + g_debug("reset took %.2lfms", g_timer_elapsed(timer, NULL) * 1000); + + /* success */ + return TRUE; +} + +/** + * fu_synaptics_cape_device_get_active_partition: + * @self: a #FuSynapticsCapeDevice + * @error: return location for an error + * + * updates active partition information to FuSynapticsCapeDevice::active_partition + * + * Returns: returns TRUE if operation is successful, otherwise, return FALSE if + * unsuccessful. + * + **/ +static gboolean +fu_synaptics_cape_device_setup_active_partition(FuSynapticsCapeDevice *self, GError **error) +{ + FuCapCmd cmd = {0}; + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + cmd.cmd_id = FU_SYNAPTICS_CMD_FW_GET_ACTIVE_PARTITION; + cmd.module_id = FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL; + + if (!fu_synaptics_cape_device_sendcmd_ex(self, &cmd, 0, error)) + return FALSE; + + self->active_partition = GUINT32_FROM_LE(cmd.data[0]); + + if (self->active_partition != 1 && self->active_partition != 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "partition number out of range, returned partition number is %u", + self->active_partition); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* gets version number from device and saves to FU_DEVICE */ +static gboolean +fu_synaptics_cape_device_setup_version(FuSynapticsCapeDevice *self, GError **error) +{ + guint32 version_raw; + FuCapCmd cmd = {0}; + g_autofree gchar *version_str = NULL; + + cmd.cmd_id = GUINT16_TO_LE(FU_SYNAPTICS_CMD_GET_VERSION); + cmd.module_id = GUINT32_TO_LE(FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL); + cmd.data_len = GUINT16_TO_LE(4); + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* gets version number from device */ + fu_synaptics_cape_device_sendcmd_ex(self, &cmd, 0, error); + + /* the version number are stored in lowest byte of a sequence of returned data */ + version_raw = + (GUINT32_FROM_LE(cmd.data[0]) << 24) | ((GUINT32_FROM_LE(cmd.data[1]) & 0xFF) << 16) | + ((GUINT32_FROM_LE(cmd.data[2]) & 0xFF) << 8) | (GUINT32_FROM_LE(cmd.data[3]) & 0xFF); + + version_str = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(FU_DEVICE(self), version_str); + fu_device_set_version_raw(FU_DEVICE(self), version_raw); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cape_device_setup(FuDevice *device, GError **error) +{ + FuSynapticsCapeDevice *self = FU_SYNAPTICS_CAPE_DEVICE(device); + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_synaptics_cape_device_parent_class)->setup(device, error)) + return FALSE; + + if (!fu_synaptics_cape_device_setup_version(self, error)) { + g_prefix_error(error, "failed to get firmware version info: "); + return FALSE; + } + + if (!fu_synaptics_cape_device_setup_active_partition(self, error)) { + g_prefix_error(error, "failed to get active partition info: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_synaptics_cape_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCapeDevice *self = FU_SYNAPTICS_CAPE_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + g_autoptr(FuFirmware) firmware = fu_synaptics_cape_firmware_new(); + gsize offset = 0; + g_autoptr(GBytes) new_fw = NULL; + + /* a "fw" includes two firmware data for each partition, we need to divide a 'fw' into + * two equal parts. + */ + gsize bufsz = g_bytes_get_size(fw); + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), NULL); + g_return_val_if_fail(usb_device != NULL, NULL); + g_return_val_if_fail(fw != NULL, NULL); + g_return_val_if_fail(firmware != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if ((guint32)bufsz % 4 != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "data not aligned to 32 bits"); + return NULL; + } + + /* checks file size */ + if (bufsz < FW_CAPE_HID_HEADER_SIZE * 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "file size is too small"); + return NULL; + } + + /* uses second partition if active partition is 1 */ + if (self->active_partition == 1) + offset = bufsz / 2; + + new_fw = fu_bytes_new_offset(fw, offset, bufsz / 2, error); + if (new_fw == NULL) + return NULL; + if (!fu_firmware_parse(firmware, new_fw, flags, error)) + return NULL; + + /* verify if correct device */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + const guint16 vid = + fu_synaptics_cape_firmware_get_vid(FU_SYNAPTICS_CAPE_FIRMWARE(firmware)); + const guint16 pid = + fu_synaptics_cape_firmware_get_pid(FU_SYNAPTICS_CAPE_FIRMWARE(firmware)); + if (vid != 0x0 && pid != 0x0 && + (g_usb_device_get_vid(usb_device) != vid || + g_usb_device_get_pid(usb_device) != pid)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "USB vendor or product incorrect, " + "got: %04X:%04X expected %04X:%04X", + vid, + pid, + g_usb_device_get_vid(usb_device), + g_usb_device_get_pid(usb_device)); + return NULL; + } + } + + /* success */ + return g_steal_pointer(&firmware); +} + +/* sends firmware header to device */ +static gboolean +fu_synaptics_cape_device_write_firmware_header(FuSynapticsCapeDevice *self, + GBytes *fw, + GError **error) +{ + const guint8 *buf = NULL; + gsize bufsz = 0; + g_autofree guint32 *buf32 = NULL; + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + buf = g_bytes_get_data(fw, &bufsz); + + /* checks size */ + if (bufsz != 20) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware header is not 20 bytes"); + return FALSE; + } + + /* 32 bit align */ + buf32 = g_new0(guint32, bufsz / sizeof(guint32)); + if (!fu_memcpy_safe((guint8 *)buf32, + bufsz, + 0x0, /* dst */ + buf, + bufsz, + 0x0, /* src */ + bufsz, + error)) + return FALSE; + return fu_synaptics_cape_device_sendcmd(self, + FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL, + FU_SYNAPTICS_CMD_FW_UPDATE_START, + buf32, + bufsz / sizeof(guint32), + 0, + error); +} + +/* sends firmware image to device */ +static gboolean +fu_synaptics_cape_device_write_firmware_image(FuSynapticsCapeDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = NULL; + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + chunks = + fu_chunk_array_new_from_bytes(fw, + 0x00, + 0x00, + sizeof(guint32) * FU_SYNAPTICS_CAPE_CMD_WRITE_DATAL_LEN); + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + gsize bufsz = fu_chunk_get_data_sz(chk); + g_autofree guint32 *buf32 = NULL; + + /* 32 bit align */ + buf32 = g_new0(guint32, bufsz / sizeof(guint32)); + if (!fu_memcpy_safe((guint8 *)buf32, + bufsz, + 0x0, /* dst */ + fu_chunk_get_data(chk), + bufsz, + 0x0, /* src */ + bufsz, + error)) + return FALSE; + + if (!fu_synaptics_cape_device_sendcmd(self, + FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL, + FU_SYNAPTICS_CMD_FW_UPDATE_WRITE, + buf32, + bufsz / sizeof(guint32), + 0, + error)) { + g_prefix_error(error, "failed send on chk %u: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +/* performs firmware update */ +static gboolean +fu_synaptics_cape_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCapeDevice *self = FU_SYNAPTICS_CAPE_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_header = NULL; + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_DEVICE(self), FALSE); + g_return_val_if_fail(firmware != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "device-write-hdr"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 69, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 29, NULL); + + fw_header = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_HEADER, error); + if (fw_header == NULL) + return FALSE; + if (!fu_synaptics_cape_device_write_firmware_header(self, fw_header, error)) { + g_prefix_error(error, "update header failed: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* performs the actual write */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + if (!fu_synaptics_cape_device_write_firmware_image(self, + fw, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "update image failed: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* verify the firmware image */ + if (!fu_synaptics_cape_device_sendcmd(self, + FU_SYNAPTICS_CAPE_CMD_APP_ID_CTRL, + FU_SYNAPTICS_CMD_FW_UPDATE_END, + NULL, + 0, + 0, + error)) { + g_prefix_error(error, "failed to verify firmware: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* sends software reset to boot into the newly flashed firmware */ + if (!fu_synaptics_cape_device_reset(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_synaptics_cape_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_synaptics_cape_device_init(FuSynapticsCapeDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "audio-card"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_install_duration(FU_DEVICE(self), 3); /* seconds */ + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.cape"); + fu_device_retry_set_delay(FU_DEVICE(self), 100); /* ms */ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_SYNAPTICS_CAPE_DEVICE_FLAG_USE_IN_REPORT_INTERRUPT, + "use-in-report-interrupt"); +} + +static void +fu_synaptics_cape_device_class_init(FuSynapticsCapeDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_synaptics_cape_device_to_string; + klass_device->setup = fu_synaptics_cape_device_setup; + klass_device->write_firmware = fu_synaptics_cape_device_write_firmware; + klass_device->prepare_firmware = fu_synaptics_cape_device_prepare_firmware; + klass_device->set_progress = fu_synaptics_cape_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-device.h b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-device.h new file mode 100644 index 0000000000000000000000000000000000000000..a232dd25913c0b248212d9c3a23e32dddf9f5f7d --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Synaptics Incorporated + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_CAPE_DEVICE (fu_synaptics_cape_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsCapeDevice, + fu_synaptics_cape_device, + FU, + SYNAPTICS_CAPE_DEVICE, + FuHidDevice) diff --git a/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-firmware.c b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..3056f8fe812f053d57ffa49e20b5aaac57d165e1 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-firmware.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2021 Synaptics Incorporated + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-synaptics-cape-firmware.h" + +typedef struct __attribute__((packed)) { + guint32 data[8]; +} FuCapeHidFwCmdUpdateWritePar; + +struct _FuSynapticsCapeFirmware { + FuFirmware parent_instance; + guint16 vid; + guint16 pid; +}; + +/* firmware update command structure, little endian */ +typedef struct __attribute__((packed)) { + guint32 vid; /* USB vendor id */ + guint32 pid; /* USB product id */ + guint32 fw_update_type; /* firmware update type */ + guint32 fw_signature; /* firmware identifier */ + guint32 crc_value; /* used to detect accidental changes to fw data */ +} FuCapeHidFwCmdUpdateStartPar; + +typedef struct __attribute__((packed)) { + FuCapeHidFwCmdUpdateStartPar par; + guint16 version_w; /* firmware version is four parts number "z.y.x.w", this is last part */ + guint16 version_x; /* firmware version, third part */ + guint16 version_y; /* firmware version, second part */ + guint16 version_z; /* firmware version, first part */ + guint32 reserved3; +} FuCapeHidFileHeader; + +G_DEFINE_TYPE(FuSynapticsCapeFirmware, fu_synaptics_cape_firmware, FU_TYPE_FIRMWARE) + +guint16 +fu_synaptics_cape_firmware_get_vid(FuSynapticsCapeFirmware *self) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_FIRMWARE(self), 0); + return self->vid; +} + +guint16 +fu_synaptics_cape_firmware_get_pid(FuSynapticsCapeFirmware *self) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_FIRMWARE(self), 0); + return self->pid; +} + +static void +fu_synaptics_cape_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuSynapticsCapeFirmware *self = FU_SYNAPTICS_CAPE_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "vid", self->vid); + fu_xmlb_builder_insert_kx(bn, "pid", self->pid); +} + +static gboolean +fu_synaptics_cape_firmware_parse_header(FuSynapticsCapeFirmware *self, + FuFirmware *firmware, + GBytes *fw, + GError **error) +{ + gsize bufsz = 0x0; + guint16 version_w = 0; + guint16 version_x = 0; + guint16 version_y = 0; + guint16 version_z = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *version_str = NULL; + g_autoptr(FuFirmware) img_hdr = fu_firmware_new(); + g_autoptr(GBytes) fw_hdr = NULL; + + g_return_val_if_fail(FU_IS_SYNAPTICS_CAPE_FIRMWARE(self), FALSE); + g_return_val_if_fail(fw != NULL, FALSE); + g_return_val_if_fail(firmware != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* the input fw image size should be the same as header size */ + if (bufsz < sizeof(FuCapeHidFileHeader)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "not enough data to parse header"); + return FALSE; + } + + if (!fu_memread_uint16_safe(buf, + bufsz, + FW_CAPE_HID_HEADER_OFFSET_VID, + &self->vid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + FW_CAPE_HID_HEADER_OFFSET_PID, + &self->pid, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + FW_CAPE_HID_HEADER_OFFSET_VER_W, + &version_w, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + FW_CAPE_HID_HEADER_OFFSET_VER_X, + &version_x, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + FW_CAPE_HID_HEADER_OFFSET_VER_Y, + &version_y, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + bufsz, + FW_CAPE_HID_HEADER_OFFSET_VER_Z, + &version_z, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + version_str = g_strdup_printf("%u.%u.%u.%u", version_z, version_y, version_x, version_w); + fu_firmware_set_version(FU_FIRMWARE(self), version_str); + + fw_hdr = fu_bytes_new_offset(fw, 0, sizeof(FuCapeHidFwCmdUpdateStartPar), error); + if (fw_hdr == NULL) + return FALSE; + + fu_firmware_set_id(img_hdr, FU_FIRMWARE_ID_HEADER); + fu_firmware_set_bytes(img_hdr, fw_hdr); + fu_firmware_add_image(firmware, img_hdr); + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cape_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCapeFirmware *self = FU_SYNAPTICS_CAPE_FIRMWARE(firmware); + const gsize bufsz = g_bytes_get_size(fw); + const gsize headsz = sizeof(FuCapeHidFileHeader); + g_autoptr(GBytes) fw_header = NULL; + g_autoptr(GBytes) fw_body = NULL; + + /* check minimum size */ + if (bufsz < sizeof(FuCapeHidFileHeader)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "not enough data to parse header, size "); + return FALSE; + } + + if ((guint32)bufsz % 4 != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "data not aligned to 32 bits"); + return FALSE; + } + + fw_header = fu_bytes_new_offset(fw, 0x0, headsz, error); + if (fw_header == NULL) + return FALSE; + if (!fu_synaptics_cape_firmware_parse_header(self, firmware, fw_header, error)) + return FALSE; + + fw_body = fu_bytes_new_offset(fw, headsz, bufsz - headsz, error); + if (fw_body == NULL) + return FALSE; + fu_firmware_set_id(firmware, FU_FIRMWARE_ID_PAYLOAD); + fu_firmware_set_bytes(firmware, fw_body); + return TRUE; +} + +static GBytes * +fu_synaptics_cape_firmware_write(FuFirmware *firmware, GError **error) +{ + FuSynapticsCapeFirmware *self = FU_SYNAPTICS_CAPE_FIRMWARE(firmware); + guint64 ver = fu_firmware_get_version_raw(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) payload = NULL; + + /* header */ + fu_byte_array_append_uint32(buf, self->vid, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, self->pid, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* update type */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* identifier */ + fu_byte_array_append_uint32(buf, 0xffff, G_LITTLE_ENDIAN); /* crc_value */ + fu_byte_array_append_uint16(buf, ver >> 0, G_LITTLE_ENDIAN); /* version w */ + fu_byte_array_append_uint16(buf, ver >> 16, G_LITTLE_ENDIAN); /* version x */ + fu_byte_array_append_uint16(buf, ver >> 32, G_LITTLE_ENDIAN); /* version y */ + fu_byte_array_append_uint16(buf, ver >> 48, G_LITTLE_ENDIAN); /* version z */ + fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */ + + /* payload */ + payload = fu_firmware_get_bytes_with_patches(firmware, error); + if (payload == NULL) + return NULL; + fu_byte_array_append_bytes(buf, payload); + fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_32, 0xFF); + + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_synaptics_cape_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuSynapticsCapeFirmware *self = FU_SYNAPTICS_CAPE_FIRMWARE(firmware); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "vid", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->vid = tmp; + tmp = xb_node_query_text_as_uint(n, "pid", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + self->pid = tmp; + + /* success */ + return TRUE; +} + +static void +fu_synaptics_cape_firmware_init(FuSynapticsCapeFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_synaptics_cape_firmware_class_init(FuSynapticsCapeFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_synaptics_cape_firmware_parse; + klass_firmware->export = fu_synaptics_cape_firmware_export; + klass_firmware->write = fu_synaptics_cape_firmware_write; + klass_firmware->build = fu_synaptics_cape_firmware_build; +} + +FuFirmware * +fu_synaptics_cape_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_CAPE_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-firmware.h b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..4b17f71f5ba46fc303b935379fb0f6264f9e45ea --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-firmware.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Synaptics Incorporated + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_CAPE_FIRMWARE (fu_synaptics_cape_firmware_get_type()) + +G_DECLARE_FINAL_TYPE(FuSynapticsCapeFirmware, + fu_synaptics_cape_firmware, + FU, + SYNAPTICS_CAPE_FIRMWARE, + FuSrecFirmware) + +FuFirmware * +fu_synaptics_cape_firmware_new(void); + +guint16 +fu_synaptics_cape_firmware_get_vid(FuSynapticsCapeFirmware *self); + +guint16 +fu_synaptics_cape_firmware_get_pid(FuSynapticsCapeFirmware *self); + +#define FW_CAPE_HID_HEADER_OFFSET_VID 0x0 +#define FW_CAPE_HID_HEADER_OFFSET_PID 0x4 +#define FW_CAPE_HID_HEADER_OFFSET_UPDATE_TYPE 0x8 +#define FW_CAPE_HID_HEADER_OFFSET_SIGNATURE 0xc +#define FW_CAPE_HID_HEADER_OFFSET_CRC 0x10 +#define FW_CAPE_HID_HEADER_OFFSET_VER_W 0x14 +#define FW_CAPE_HID_HEADER_OFFSET_VER_X 0x16 +#define FW_CAPE_HID_HEADER_OFFSET_VER_Y 0x18 +#define FW_CAPE_HID_HEADER_OFFSET_VER_Z 0x1A + +#define FW_CAPE_HID_HEADER_SIZE 32 /* =sizeof(FuCapeHidFileHeader) */ diff --git a/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-plugin.c b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..df8e1cb30c13d89a6e06f71a8d6b3e17f365141c --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-plugin.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Synaptics Incorporated + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-cape-device.h" +#include "fu-synaptics-cape-firmware.h" +#include "fu-synaptics-cape-plugin.h" + +struct _FuSynapticsCapePlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSynapticsCapePlugin, fu_synaptics_cape_plugin, FU_TYPE_PLUGIN) + +static void +fu_synaptics_cape_plugin_init(FuSynapticsCapePlugin *self) +{ +} + +static void +fu_synaptics_cape_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_CAPE_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_CAPE_FIRMWARE); +} + +static void +fu_synaptics_cape_plugin_class_init(FuSynapticsCapePluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_synaptics_cape_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-plugin.h b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..3e8ef11445886aabd11ae086a9679a4509d64fdc --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/fu-synaptics-cape-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSynapticsCapePlugin, + fu_synaptics_cape_plugin, + FU, + SYNAPTICS_CAPE_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/synaptics-cape/meson.build b/fwupd-1.8.6/plugins/synaptics-cape/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..d8f574a6d889d8724a2e55549f0e11fe88c162e6 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/meson.build @@ -0,0 +1,16 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsCape"'] + +plugin_quirks += files('synaptics-cape.quirk') +plugin_builtins += static_library('fu_plugin_synaptics_cape', + sources: [ + 'fu-synaptics-cape-plugin.c', + 'fu-synaptics-cape-device.c', + 'fu-synaptics-cape-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/synaptics-cape/synaptics-cape.quirk b/fwupd-1.8.6/plugins/synaptics-cape/synaptics-cape.quirk new file mode 100644 index 0000000000000000000000000000000000000000..01e3708deec23ddd6666f420b41da1e4f014061f --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/synaptics-cape.quirk @@ -0,0 +1,100 @@ +# EPOS Raw Plus +[USB\VID_1395&PID_0280] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0281] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +# RAW Teams +[USB\VID_1395&PID_0294] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0295] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0296] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0297] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0298] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0299] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0400] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +# EPOS Morgan-T +[USB\VID_1395&PID_0200] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0288] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0289] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_028A] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_028B] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_028C] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_028D] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_028E] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_028F] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +# EPOS Morgan-V +[USB\VID_1395&PID_0290] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0291] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0292] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +[USB\VID_1395&PID_0293] +Guid = SYNAPTICS_CAPE\CX31993 +Flags = use-in-report-interrupt + +# USB audio codec USB receiver +[SYNAPTICS_CAPE\CX31993] +Plugin = synaptics_cape +Icon = usb-receiver + +# USB audio codec Hifi +[SYNAPTICS_CAPE\CX31988] +Plugin = synaptics_cape diff --git a/fwupd-1.8.6/plugins/synaptics-cape/tests/synaptics-cape.bin b/fwupd-1.8.6/plugins/synaptics-cape/tests/synaptics-cape.bin new file mode 100644 index 0000000000000000000000000000000000000000..a9154705cdf82de6c4b82708835f26cd92b4dabe Binary files /dev/null and b/fwupd-1.8.6/plugins/synaptics-cape/tests/synaptics-cape.bin differ diff --git a/fwupd-1.8.6/plugins/synaptics-cape/tests/synaptics-cape.builder.xml b/fwupd-1.8.6/plugins/synaptics-cape/tests/synaptics-cape.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..fe8cea0df81edfa1eb7b19344bf1f6454f10e670 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cape/tests/synaptics-cape.builder.xml @@ -0,0 +1,11 @@ + + has-vid-pid + payload + 8.41.24.0 + EFSCh... + + header + + + + diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/README.md b/fwupd-1.8.6/plugins/synaptics-cxaudio/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7fecee86504e05a14c65ab4eb6bdcab3bd174813 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/README.md @@ -0,0 +1,69 @@ +# Conexant Audio + +## Introduction + +This plugin is used to update a small subset of Conexant (now owned by Synaptics) +audio devices. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a modified SREC file format. + +This plugin supports the following protocol ID: + +* com.synaptics.synaptics-cxaudio + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_17EF&PID_3083&REV_0001` +* `USB\VID_17EF&PID_3083` +* `USB\VID_17EF` + +These devices also use custom GUID values, e.g. + +* `SYNAPTICS_CXAUDIO\CX2198X` +* `SYNAPTICS_CXAUDIO\CX21985` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x17EF` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### CxaudioChipIdBase + +Base integer for ChipID. + +Since: 1.3.2 + +### CxaudioSoftwareReset + +If the chip supports self-reset. + +Since: 1.3.2 + +### CxaudioPatch1ValidAddr + +Address of patch location #1. + +Since: 1.3.2 + +### CxaudioPatch2ValidAddr + +Address of patch location #2. + +Since: 1.3.2 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h new file mode 100644 index 0000000000000000000000000000000000000000..996b9b98fc0dda814ff6e280b57fc578c705325f --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* usb */ +#define FU_SYNAPTICS_CXAUDIO_INPUT_REPORT_SIZE 35 +#define FU_SYNAPTICS_CXAUDIO_OUTPUT_REPORT_SIZE 39 +#define FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT 2000 /* ms */ + +/* commands */ +#define FU_SYNAPTICS_CXAUDIO_MEM_WRITEID 0x4 +#define FU_SYNAPTICS_CXAUDIO_MEM_READID 0x5 + +typedef enum { + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_UNKNOWN, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX20562 = 20562, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x = 20700, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2077x = 20770, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2076x = 20760, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2085x = 20850, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2089x = 20890, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2098x = 20980, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2198x = 21980, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_LAST +} FuSynapticsCxaudioDeviceKind; + +typedef enum { + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_ROM, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_LAST +} FuSynapticsCxaudioMemKind; + +/* EEPROM */ +#define FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET 0x0000 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET 0x0020 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH_VERSION_ADDRESS 0x0022 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH2_VERSION_ADDRESS 0x0176 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_SIZE_ADDRESS 0x0005 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_PADDING_SIZE 0x4 /* bytes */ + +#define FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_STRIDX 50 +#define FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_BYTE 0x03 +#define FU_SYNAPTICS_CXAUDIO_MAGIC_BYTE 'L' +#define FU_SYNAPTICS_CXAUDIO_SIGNATURE_BYTE 'S' +#define FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE 'P' +#define FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_PARK_ADDR 0x1000 +#define FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_VERSION_ADDR 0x1001 +#define FU_SYNAPTICS_CXAUDIO_REG_RESET_ADDR 0x0400 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE (8 * 1024) + +typedef guint16 FuSynapticsCxaudioEepromPtr; +typedef struct __attribute__((packed)) { + FuSynapticsCxaudioEepromPtr PatchVersionStringAddress; + guint8 CpxPatchVersion[3]; + guint8 SpxPatchVersion[4]; + guint8 LayoutSignature; + guint8 LayoutVersion; + guint8 ApplicationStatus; + guint16 VendorID; + guint16 ProductID; + guint16 RevisionID; + FuSynapticsCxaudioEepromPtr LanguageStringAddress; + FuSynapticsCxaudioEepromPtr ManufacturerStringAddress; + FuSynapticsCxaudioEepromPtr ProductStringAddress; + FuSynapticsCxaudioEepromPtr SerialNumberStringAddress; +} FuSynapticsCxaudioEepromCustomInfo; + +#define FU_SYNAPTICS_CXAUDIO_EEPROM_APP_STATUS_ADDRESS \ + (FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + \ + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, ApplicationStatus)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_SIGNATURE_ADDRESS \ + (FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + \ + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, LayoutSignature)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_VERSION_ADDRESS \ + (FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + \ + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, LayoutVersion)) + +typedef struct __attribute__((packed)) { + guint8 Length; + guint8 Type; +} FuSynapticsCxaudioEepromStringHeader; + +typedef struct __attribute__((packed)) { + guint8 PatchSignature; + FuSynapticsCxaudioEepromPtr PatchAddress; +} FuSynapticsCxaudioEepromPatchInfo; + +typedef struct __attribute__((packed)) { + guint8 MagicByte; + guint8 EeepromSizeCode; +} FuSynapticsCxaudioEepromValiditySignature; + +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET 0x0014 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_SIZE (sizeof(FuSynapticsCxaudioEepromPatchInfo)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_SIGNATURE_ADDRESS \ + (FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET + \ + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromPatchInfo, PatchSignature)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_PTR_ADDRESS \ + (FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET + \ + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromPatchInfo, PatchAddress)) +#define FU_SYNAPTICS_CXAUDIO_FIRMWARE_SIGNATURE_OFFSET \ + (FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET + \ + sizeof(FuSynapticsCxaudioEepromValiditySignature)) diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c new file mode 100644 index 0000000000000000000000000000000000000000..3d78e23db674e4099f563067768e966291d943ef --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2005 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-synaptics-cxaudio-common.h" +#include "fu-synaptics-cxaudio-device.h" +#include "fu-synaptics-cxaudio-firmware.h" + +struct _FuSynapticsCxaudioDevice { + FuHidDevice parent_instance; + guint32 chip_id_base; + guint32 chip_id; + gboolean serial_number_set; + gboolean sw_reset_supported; + guint32 eeprom_layout_version; + guint32 eeprom_patch2_valid_addr; + guint32 eeprom_patch_valid_addr; + guint32 eeprom_storage_address; + guint32 eeprom_storage_sz; + guint32 eeprom_sz; + guint8 patch_level; +}; + +G_DEFINE_TYPE(FuSynapticsCxaudioDevice, fu_synaptics_cxaudio_device, FU_TYPE_HID_DEVICE) + +static void +fu_synaptics_cxaudio_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); + fu_string_append_ku(str, idt, "ChipIdBase", self->chip_id_base); + fu_string_append_ku(str, idt, "ChipId", self->chip_id); + fu_string_append_kx(str, idt, "EepromLayoutVersion", self->eeprom_layout_version); + fu_string_append_kx(str, idt, "EepromStorageAddress", self->eeprom_storage_address); + fu_string_append_kx(str, idt, "EepromStorageSz", self->eeprom_storage_sz); + fu_string_append_kx(str, idt, "EepromSz", self->eeprom_sz); + fu_string_append_kb(str, idt, "SwResetSupported", self->sw_reset_supported); + fu_string_append_kb(str, idt, "SerialNumberSet", self->serial_number_set); +} + +static gboolean +fu_synaptics_cxaudio_device_output_report(FuSynapticsCxaudioDevice *self, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + /* weird */ + if (buf[0] == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "report 0 not supported"); + return FALSE; + } + + /* to device */ + return fu_hid_device_set_report(FU_HID_DEVICE(self), + buf[0], + buf, + bufsz, + FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT, + FU_HID_DEVICE_FLAG_RETRY_FAILURE, + error); +} + +static gboolean +fu_synaptics_cxaudio_device_input_report(FuSynapticsCxaudioDevice *self, + guint8 ReportID, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + return fu_hid_device_get_report(FU_HID_DEVICE(self), + ReportID, + buf, + bufsz, + FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT, + FU_HID_DEVICE_FLAG_RETRY_FAILURE, + error); +} + +typedef enum { + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_OPERATION_LAST +} FuSynapticsCxaudioOperation; + +typedef enum { + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE = 0, + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY = (1 << 4), +} FuSynapticsCxaudioOperationFlags; + +static gboolean +fu_synaptics_cxaudio_device_operation(FuSynapticsCxaudioDevice *self, + FuSynapticsCxaudioOperation operation, + FuSynapticsCxaudioMemKind mem_kind, + guint32 addr, + guint8 *buf, + guint32 bufsz, + FuSynapticsCxaudioOperationFlags flags, + GError **error) +{ + const guint32 idx_read = 0x1; + const guint32 idx_write = 0x5; + const guint32 payload_max = 0x20; + guint32 size = 0x02800; + g_autoptr(GPtrArray) chunks = NULL; + + g_return_val_if_fail(bufsz > 0, FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + + /* check if memory operation is supported by device */ + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE && + mem_kind == FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_ROM) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "trying to write unwritable section %u", + mem_kind); + return FALSE; + } + + /* check memory address - should be within valid range */ + if (mem_kind == FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM) + size = 0x20000; + if (addr > size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "address out of range 0x%x < 0x%x", + addr, + size); + return FALSE; + } + + /* send to hardware */ + chunks = fu_chunk_array_mutable_new(buf, bufsz, addr, 0x0, payload_max); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + guint8 inbuf[FU_SYNAPTICS_CXAUDIO_INPUT_REPORT_SIZE] = {0}; + guint8 outbuf[FU_SYNAPTICS_CXAUDIO_OUTPUT_REPORT_SIZE] = {0}; + + /* first byte is always report ID */ + outbuf[0] = FU_SYNAPTICS_CXAUDIO_MEM_WRITEID; + + /* set memory address and payload length (if relevant) */ + if (fu_chunk_get_address(chk) >= 64 * 1024) + outbuf[1] |= 1 << 4; + outbuf[2] = fu_chunk_get_data_sz(chk); + fu_memwrite_uint16(outbuf + 3, fu_chunk_get_address(chk), G_BIG_ENDIAN); + + /* set memtype */ + if (mem_kind == FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM) + outbuf[1] |= 1 << 5; + + /* fill the report payload part */ + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE) { + outbuf[1] |= 1 << 6; + if (!fu_memcpy_safe(outbuf, + sizeof(outbuf), + idx_write, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + } + if (!fu_synaptics_cxaudio_device_output_report(self, outbuf, sizeof(outbuf), error)) + return FALSE; + + /* issue additional write directive to read */ + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE && + flags & FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY) { + outbuf[1] &= ~(1 << 6); + if (!fu_synaptics_cxaudio_device_output_report(self, + outbuf, + sizeof(outbuf), + error)) + return FALSE; + } + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_READ || + flags & FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY) { + if (!fu_synaptics_cxaudio_device_input_report( + self, + FU_SYNAPTICS_CXAUDIO_MEM_READID, + inbuf, + sizeof(inbuf), + error)) + return FALSE; + } + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE && + flags & FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY) { + if (!fu_memcmp_safe(outbuf + idx_write, + payload_max, + inbuf + idx_read, + payload_max, + error)) { + g_prefix_error(error, + "failed to verify on packet %u @0x%x: ", + fu_chunk_get_idx(chk), + fu_chunk_get_address(chk)); + return FALSE; + } + } + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_READ) { + if (!fu_memcpy_safe(fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* dst */ + inbuf, + sizeof(inbuf), + idx_read, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_register_clear_bit(FuSynapticsCxaudioDevice *self, + guint32 address, + guint8 bit_position, + GError **error) +{ + guint8 tmp = 0x0; + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, + &tmp, + sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) + return FALSE; + tmp &= ~(1 << bit_position); + return fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, + &tmp, + sizeof(guint8), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error); +} + +static gboolean +fu_synaptics_cxaudio_device_register_set_bit(FuSynapticsCxaudioDevice *self, + guint32 address, + guint8 bit_position, + GError **error) +{ + guint8 tmp = 0x0; + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, + &tmp, + sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) + return FALSE; + tmp |= 1 << bit_position; + return fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, + &tmp, + sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error); +} + +static gchar * +fu_synaptics_cxaudio_device_eeprom_read_string(FuSynapticsCxaudioDevice *self, + guint32 address, + GError **error) +{ + FuSynapticsCxaudioEepromStringHeader header = {0}; + g_autofree gchar *str = NULL; + + /* read header */ + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + address, + (guint8 *)&header, + sizeof(header), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM string header @0x%x: ", address); + return NULL; + } + + /* sanity check */ + if (header.Type != FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_BYTE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM string header type invalid"); + return NULL; + } + if (header.Length < sizeof(header)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM string header length invalid"); + return NULL; + } + + /* allocate buffer + NUL terminator */ + str = g_malloc0(header.Length - sizeof(header) + 1); + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + address + sizeof(header), + (guint8 *)str, + header.Length - sizeof(header), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM string @0x%x: ", address); + return NULL; + } + return g_steal_pointer(&str); +} + +static gboolean +fu_synaptics_cxaudio_device_ensure_patch_level(FuSynapticsCxaudioDevice *self, GError **error) +{ + guint8 tmp = 0x0; + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + self->eeprom_patch_valid_addr, + &tmp, + sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM patch validation byte: "); + return FALSE; + } + if (tmp == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + self->patch_level = 1; + return TRUE; + } + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + self->eeprom_patch2_valid_addr, + &tmp, + sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM patch validation byte: "); + return FALSE; + } + if (tmp == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + self->patch_level = 2; + return TRUE; + } + + /* not sure what to do here */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM patch version undiscoverable"); + return FALSE; +} + +static gboolean +fu_synaptics_cxaudio_device_setup(FuDevice *device, GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + FuSynapticsCxaudioEepromCustomInfo cinfo = {0x0}; + guint32 addr = FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH_VERSION_ADDRESS; + guint8 chip_id_offset = 0x0; + guint8 sigbuf[2] = {0x0}; + guint8 verbuf_fw[4] = {0x0}; + guint8 verbuf_patch[3] = {0x0}; + g_autofree gchar *cap_str = NULL; + g_autofree gchar *chip_id = NULL; + g_autofree gchar *summary = NULL; + g_autofree gchar *version_fw = NULL; + g_autofree gchar *version_patch = NULL; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_synaptics_cxaudio_device_parent_class)->setup(device, error)) + return FALSE; + + /* get the ChipID */ + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + 0x1005, + &chip_id_offset, + sizeof(chip_id_offset), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read ChipID: "); + return FALSE; + } + self->chip_id = self->chip_id_base + chip_id_offset; + + /* add instance ID */ + chip_id = g_strdup_printf("CX%u", self->chip_id); + fu_device_add_instance_str(device, "ID", chip_id); + if (!fu_device_build_instance_id_quirk(device, error, "SYNAPTICS_CXAUDIO", "ID", NULL)) + return FALSE; + + /* set summary */ + summary = g_strdup_printf("CX%u USB audio device", self->chip_id); + fu_device_set_summary(device, summary); + + /* read the EEPROM validity signature */ + if (!fu_synaptics_cxaudio_device_operation( + self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET, + sigbuf, + sizeof(sigbuf), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM signature bytes: "); + return FALSE; + } + + /* blank EEPROM */ + if (sigbuf[0] == 0xff && sigbuf[1] == 0xff) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM is missing or blank"); + return FALSE; + } + + /* is disabled on EVK board using jumper */ + if ((sigbuf[0] == 0x00 && sigbuf[1] == 0x00) || (sigbuf[0] == 0xff && sigbuf[1] == 0x00)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM has been disabled using a jumper"); + return FALSE; + } + + /* check magic byte */ + if (sigbuf[0] != FU_SYNAPTICS_CXAUDIO_MAGIC_BYTE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM magic byte invalid, got 0x%02x expected 0x%02x", + sigbuf[0], + (guint)FU_SYNAPTICS_CXAUDIO_MAGIC_BYTE); + return FALSE; + } + + /* calculate EEPROM size */ + self->eeprom_sz = (guint32)1 << (sigbuf[1] + 8); + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_SIZE_ADDRESS, + sigbuf, + sizeof(sigbuf), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM signature bytes: "); + return FALSE; + } + self->eeprom_storage_sz = fu_memread_uint16(sigbuf, G_LITTLE_ENDIAN); + if (self->eeprom_storage_sz < + self->eeprom_sz - FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_PADDING_SIZE) { + self->eeprom_storage_address = self->eeprom_sz - self->eeprom_storage_sz - + FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_PADDING_SIZE; + } + + /* get EEPROM custom info */ + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET, + (guint8 *)&cinfo, + sizeof(cinfo), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM custom info: "); + return FALSE; + } + if (cinfo.LayoutSignature == FU_SYNAPTICS_CXAUDIO_SIGNATURE_BYTE) + self->eeprom_layout_version = cinfo.LayoutVersion; + g_debug("CpxPatchVersion: %u.%u.%u", + cinfo.CpxPatchVersion[0], + cinfo.CpxPatchVersion[1], + cinfo.CpxPatchVersion[2]); + g_debug("SpxPatchVersion: %u.%u.%u.%u", + cinfo.SpxPatchVersion[0], + cinfo.SpxPatchVersion[1], + cinfo.SpxPatchVersion[2], + cinfo.SpxPatchVersion[3]); + g_debug("VendorID: 0x%04x", cinfo.VendorID); + g_debug("ProductID: 0x%04x", cinfo.ProductID); + g_debug("RevisionID: 0x%04x", cinfo.RevisionID); + g_debug("ApplicationStatus: 0x%02x", cinfo.ApplicationStatus); + + /* serial number, which also allows us to recover it after write */ + if (self->eeprom_layout_version >= 0x01) { + self->serial_number_set = cinfo.SerialNumberStringAddress != 0x0; + if (self->serial_number_set) { + g_autofree gchar *tmp = NULL; + tmp = fu_synaptics_cxaudio_device_eeprom_read_string( + self, + cinfo.SerialNumberStringAddress, + error); + if (tmp == NULL) + return FALSE; + fu_device_set_serial(device, tmp); + } + } + + /* read fw version */ + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_VERSION_ADDR, + verbuf_fw, + sizeof(verbuf_fw), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM firmware version: "); + return FALSE; + } + version_fw = g_strdup_printf("%02X.%02X.%02X.%02X", + verbuf_fw[1], + verbuf_fw[0], + verbuf_fw[3], + verbuf_fw[2]); + fu_device_set_version_bootloader(device, version_fw); + + /* use a different address if a patch is in use */ + if (self->eeprom_patch_valid_addr != 0x0) { + if (!fu_synaptics_cxaudio_device_ensure_patch_level(self, error)) + return FALSE; + } + if (self->patch_level == 2) + addr = FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH2_VERSION_ADDRESS; + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + addr, + verbuf_patch, + sizeof(verbuf_patch), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM patch version: "); + return FALSE; + } + version_patch = + g_strdup_printf("%02X-%02X-%02X", verbuf_patch[0], verbuf_patch[1], verbuf_patch[2]); + fu_device_set_version(device, version_patch); + + /* find out if patch supports additional capabilities (optional) */ + cap_str = + g_usb_device_get_string_descriptor(usb_device, + FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_STRIDX, + NULL); + if (cap_str != NULL) { + g_auto(GStrv) split = g_strsplit(cap_str, ";", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_debug("capability: %s", split[i]); + if (g_strcmp0(split[i], "RESET") == 0) + self->sw_reset_supported = TRUE; + } + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_synaptics_cxaudio_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); + guint32 chip_id_base; + g_autoptr(FuFirmware) firmware = fu_synaptics_cxaudio_firmware_new(); + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + chip_id_base = + fu_synaptics_cxaudio_firmware_get_devtype(FU_SYNAPTICS_CXAUDIO_FIRMWARE(firmware)); + if (chip_id_base != self->chip_id_base) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "device 0x%04u is incompatible with firmware 0x%04u", + self->chip_id_base, + chip_id_base); + return NULL; + } + return g_steal_pointer(&firmware); +} + +static gboolean +fu_synaptics_cxaudio_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); + GPtrArray *records = fu_srec_firmware_get_records(FU_SREC_FIRMWARE(firmware)); + FuSynapticsCxaudioFileKind file_kind; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 3, "park"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "invalidate"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "unpark"); + + /* check if a patch file fits completely into the EEPROM */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index(records, i); + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16) + continue; + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_LAST) + continue; + if (rcd->addr > self->eeprom_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM address 0x%02x is bigger than size 0x%02x", + rcd->addr, + self->eeprom_sz); + return FALSE; + } + } + + /* park the FW: run only the basic functionality until the upgrade is over */ + if (!fu_synaptics_cxaudio_device_register_set_bit( + self, + FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_PARK_ADDR, + 7, + error)) + return FALSE; + g_usleep(10 * 1000); + fu_progress_step_done(progress); + + /* initialize layout signature and version to 0 if transitioning from + * EEPROM layout version 1 => 0 */ + file_kind = + fu_synaptics_cxaudio_firmware_get_file_type(FU_SYNAPTICS_CXAUDIO_FIRMWARE(firmware)); + if (file_kind == FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW && + self->eeprom_layout_version >= 1 && + fu_synaptics_cxaudio_firmware_get_layout_version( + FU_SYNAPTICS_CXAUDIO_FIRMWARE(firmware)) == 0) { + guint8 value = 0; + if (!fu_synaptics_cxaudio_device_operation( + self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_SIGNATURE_ADDRESS, + &value, + sizeof(value), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to initialize layout signature: "); + return FALSE; + } + if (!fu_synaptics_cxaudio_device_operation( + self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_VERSION_ADDRESS, + &value, + sizeof(value), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to initialize layout signature: "); + return FALSE; + } + g_debug("initialized layout signature"); + } + fu_progress_step_done(progress); + + /* perform the actual write */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index(records, i); + if (rcd->kind != FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) + continue; + g_debug("writing @0x%04x len:0x%02x", rcd->addr, rcd->buf->len); + if (!fu_synaptics_cxaudio_device_operation( + self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + rcd->addr, + rcd->buf->data, + rcd->buf->len, + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY, + error)) { + g_prefix_error(error, + "failed to write @0x%04x len:0x%02x: ", + rcd->addr, + rcd->buf->len); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)records->len); + } + fu_progress_step_done(progress); + + /* in case of a full FW upgrade invalidate the old FW patch (if any) + * as it may have not been done by the S37 file */ + if (file_kind == FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW) { + FuSynapticsCxaudioEepromPatchInfo pinfo = {0}; + if (!fu_synaptics_cxaudio_device_operation( + self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET, + (guint8 *)&pinfo, + sizeof(pinfo), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to read EEPROM patch info: "); + return FALSE; + } + if (pinfo.PatchSignature == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + memset(&pinfo, 0x0, sizeof(pinfo)); + if (!fu_synaptics_cxaudio_device_operation( + self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET, + (guint8 *)&pinfo, + sizeof(pinfo), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write empty EEPROM patch info: "); + return FALSE; + } + g_debug("invalidated old FW patch for CX2070x (RAM) device"); + } + } + fu_progress_step_done(progress); + + /* unpark the FW */ + if (!fu_synaptics_cxaudio_device_register_clear_bit( + self, + FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_PARK_ADDR, + 7, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); + guint8 tmp = 1 << 6; + g_autoptr(GError) error_local = NULL; + + /* is disabled on EVK board using jumper */ + if (!self->sw_reset_supported) + return TRUE; + + /* wait for re-enumeration */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* this fails on success */ + if (!fu_synaptics_cxaudio_device_operation(self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + FU_SYNAPTICS_CXAUDIO_REG_RESET_ADDR, + &tmp, + sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + if (g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_FAILED)) { + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "CxaudioChipIdBase") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->chip_id_base = tmp; + return TRUE; + } + if (g_strcmp0(key, "CxaudioSoftwareReset") == 0) + return fu_strtobool(value, &self->sw_reset_supported, error); + if (g_strcmp0(key, "CxaudioPatch1ValidAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->eeprom_patch_valid_addr = tmp; + return TRUE; + } + if (g_strcmp0(key, "CxaudioPatch2ValidAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + self->eeprom_patch2_valid_addr = tmp; + return TRUE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_synaptics_cxaudio_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 3, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 37, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 60, "reload"); +} + +static void +fu_synaptics_cxaudio_device_init(FuSynapticsCxaudioDevice *self) +{ + self->sw_reset_supported = TRUE; + fu_device_add_icon(FU_DEVICE(self), "audio-card"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_install_duration(FU_DEVICE(self), 3); /* seconds */ + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.cxaudio"); + fu_device_retry_set_delay(FU_DEVICE(self), 100); /* ms */ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_synaptics_cxaudio_device_class_init(FuSynapticsCxaudioDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_synaptics_cxaudio_device_to_string; + klass_device->set_quirk_kv = fu_synaptics_cxaudio_device_set_quirk_kv; + klass_device->setup = fu_synaptics_cxaudio_device_setup; + klass_device->write_firmware = fu_synaptics_cxaudio_device_write_firmware; + klass_device->attach = fu_synaptics_cxaudio_device_attach; + klass_device->prepare_firmware = fu_synaptics_cxaudio_device_prepare_firmware; + klass_device->set_progress = fu_synaptics_cxaudio_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h new file mode 100644 index 0000000000000000000000000000000000000000..47aa318ef5930e6214676073e97a8676521172e7 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_CXAUDIO_DEVICE (fu_synaptics_cxaudio_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsCxaudioDevice, + fu_synaptics_cxaudio_device, + FU, + SYNAPTICS_CXAUDIO_DEVICE, + FuHidDevice) diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..f72af93b8491644ca556299163b04dabfd26961e --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2005 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-synaptics-cxaudio-firmware.h" + +struct _FuSynapticsCxaudioFirmware { + FuSrecFirmwareClass parent_instance; + FuSynapticsCxaudioFileKind file_kind; + FuSynapticsCxaudioDeviceKind device_kind; + FuSynapticsCxaudioEepromCustomInfo cinfo; +}; + +G_DEFINE_TYPE(FuSynapticsCxaudioFirmware, fu_synaptics_cxaudio_firmware, FU_TYPE_SREC_FIRMWARE) + +FuSynapticsCxaudioFileKind +fu_synaptics_cxaudio_firmware_get_file_type(FuSynapticsCxaudioFirmware *self) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CXAUDIO_FIRMWARE(self), 0); + return self->file_kind; +} + +FuSynapticsCxaudioDeviceKind +fu_synaptics_cxaudio_firmware_get_devtype(FuSynapticsCxaudioFirmware *self) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CXAUDIO_FIRMWARE(self), 0); + return self->device_kind; +} + +guint8 +fu_synaptics_cxaudio_firmware_get_layout_version(FuSynapticsCxaudioFirmware *self) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_CXAUDIO_FIRMWARE(self), 0); + return self->cinfo.LayoutVersion; +} + +static void +fu_synaptics_cxaudio_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuSynapticsCxaudioFirmware *self = FU_SYNAPTICS_CXAUDIO_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "file_kind", self->file_kind); + fu_xmlb_builder_insert_kx(bn, "device_kind", self->device_kind); + fu_xmlb_builder_insert_kx(bn, "layout_signature", self->cinfo.LayoutSignature); + fu_xmlb_builder_insert_kx(bn, "layout_version", self->cinfo.LayoutVersion); + if (self->cinfo.LayoutVersion >= 1) { + fu_xmlb_builder_insert_kx(bn, "vid", self->cinfo.VendorID); + fu_xmlb_builder_insert_kx(bn, "pid", self->cinfo.ProductID); + fu_xmlb_builder_insert_kx(bn, "rev", self->cinfo.RevisionID); + } +} + +typedef struct { + const gchar *str; + guint32 addr; + guint32 len; +} FuSynapticsCxaudioFirmwareBadblock; + +static void +fu_synaptics_cxaudio_firmware_badblock_add(GPtrArray *badblocks, + const gchar *str, + guint32 addr, + guint32 len) +{ + FuSynapticsCxaudioFirmwareBadblock *bb = g_new0(FuSynapticsCxaudioFirmwareBadblock, 1); + g_debug("created reserved range @0x%04x len:0x%x: %s", addr, len, str); + bb->str = str; + bb->addr = addr; + bb->len = len; + g_ptr_array_add(badblocks, bb); +} + +static gboolean +fu_synaptics_cxaudio_firmware_is_addr_valid(GPtrArray *badblocks, guint32 addr, guint32 len) +{ + for (guint j = 0; j < badblocks->len; j++) { + FuSynapticsCxaudioFirmwareBadblock *bb = g_ptr_array_index(badblocks, j); + if (addr <= bb->addr + bb->len - 1 && addr + len - 1 >= bb->addr) { + g_debug("addr @0x%04x len:0x%x invalid " + "as 0x%02x->0x%02x protected: %s", + addr, + len, + bb->addr, + bb->addr + bb->len - 1, + bb->str); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_firmware_is_record_valid(GPtrArray *badblocks, FuSrecFirmwareRecord *rcd) +{ + /* the entire record is not within an ignored range */ + return fu_synaptics_cxaudio_firmware_is_addr_valid(badblocks, rcd->addr, rcd->buf->len); +} + +static void +fu_synaptics_cxaudio_firmware_avoid_badblocks(GPtrArray *badblocks, GPtrArray *records) +{ + g_autoptr(GPtrArray) records_new = g_ptr_array_new(); + + /* find records that include addresses with blocks we want to avoid */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index(records, i); + FuSrecFirmwareRecord *rcd1; + if (rcd->kind != FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) + continue; + if (fu_synaptics_cxaudio_firmware_is_record_valid(badblocks, rcd)) { + rcd1 = fu_srec_firmware_record_new(rcd->ln, rcd->kind, rcd->addr); + g_byte_array_append(rcd1->buf, rcd->buf->data, rcd->buf->len); + g_ptr_array_add(records_new, rcd1); + continue; + } + g_debug("splitting record @0x%04x len:0x%x as protected", rcd->addr, rcd->buf->len); + for (guint j = 0; j < rcd->buf->len; j++) { + if (!fu_synaptics_cxaudio_firmware_is_addr_valid(badblocks, + rcd->addr + j, + 0x1)) + continue; + rcd1 = fu_srec_firmware_record_new(rcd->ln, rcd->kind, rcd->addr + j); + g_byte_array_append(rcd1->buf, rcd->buf->data + j, 0x1); + g_ptr_array_add(records_new, rcd1); + } + } + + /* swap the old set of records with the new records */ + g_ptr_array_set_size(records, 0); + for (guint i = 0; i < records_new->len; i++) { + FuSrecFirmwareRecord *rcd1 = g_ptr_array_index(records_new, i); + g_ptr_array_add(records, rcd1); + } +} + +static gboolean +fu_synaptics_cxaudio_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCxaudioFirmware *self = FU_SYNAPTICS_CXAUDIO_FIRMWARE(firmware); + GPtrArray *records = fu_srec_firmware_get_records(FU_SREC_FIRMWARE(firmware)); + guint8 dev_kind_candidate = G_MAXUINT8; + g_autofree guint8 *shadow = g_malloc0(FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE); + + /* copy shadow EEPROM */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index(records, i); + if (rcd->kind != FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) + continue; + if (rcd->addr > FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE) + continue; + if (rcd->buf->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "record 0x%x had zero size", + i); + return FALSE; + } + if (!fu_memcpy_safe(shadow, + FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE, + rcd->addr, /* dst */ + rcd->buf->data, + rcd->buf->len, + 0x0, /* src */ + rcd->buf->len, + error)) + return FALSE; + } + + /* parse EEPROM map */ + if (!fu_memcpy_safe((guint8 *)&self->cinfo, + sizeof(FuSynapticsCxaudioEepromCustomInfo), + 0x0, /* dst */ + shadow, + FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE, + FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET, /* src */ + sizeof(FuSynapticsCxaudioEepromCustomInfo), + error)) + return FALSE; + + /* just layout version byte is not enough in case of old CX20562 patch + * files that could have non-zero value of the Layout version */ + if (shadow[FU_SYNAPTICS_CXAUDIO_FIRMWARE_SIGNATURE_OFFSET] == + FU_SYNAPTICS_CXAUDIO_SIGNATURE_BYTE) { + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW; + g_debug("FileKind: CX2070x (FW)"); + } else if (shadow[FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_SIGNATURE_ADDRESS] == + FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_PATCH; + g_debug("FileKind: CX2070x (Patch)"); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "CX20562 is not supported"); + return FALSE; + } + for (guint i = records->len - 3; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index(records, i); + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16) + continue; + if (rcd->buf->len < 2) + continue; + if (memcmp(rcd->buf->data, "CX", 2) == 0) { + dev_kind_candidate = rcd->buf->data[2]; + g_debug("DeviceKind signature suspected 0x%0x", dev_kind_candidate); + break; + } + } + + /* check the signature character to see if it defines the device */ + switch (dev_kind_candidate) { + case '2': /* fallthrough */ /* CX2070x */ + case '4': /* CX2070x-21Z */ + case '6': /* CX2070x-21Z */ + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_PATCH; + g_debug("FileKind: CX2070x overwritten from signature"); + break; + case '3': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2077x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2077X_PATCH; + g_debug("FileKind: CX2077x overwritten from signature"); + break; + case '5': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2076x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2076X_PATCH; + g_debug("FileKind: CX2076x overwritten from signature"); + break; + case '7': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2085x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2085X_PATCH; + g_debug("FileKind: CX2085x overwritten from signature"); + break; + case '8': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2089x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2089X_PATCH; + g_debug("FileKind: CX2089x overwritten from signature"); + break; + case '9': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2098x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2098X_PATCH; + g_debug("FileKind: CX2098x overwritten from signature"); + break; + case 'A': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2198x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2198X_PATCH; + g_debug("FileKind: CX2198x overwritten from signature"); + break; + default: + /* probably future devices */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "DeviceKind signature invalid 0x%x", + dev_kind_candidate); + return FALSE; + } + + /* ignore records with protected content */ + if (self->cinfo.LayoutVersion >= 1) { + g_autoptr(GPtrArray) badblocks = g_ptr_array_new_with_free_func(g_free); + + /* add standard ranges to ignore */ + fu_synaptics_cxaudio_firmware_badblock_add(badblocks, "test mark", 0x00BC, 0x02); + fu_synaptics_cxaudio_firmware_badblock_add( + badblocks, + "application status", + FU_SYNAPTICS_CXAUDIO_EEPROM_APP_STATUS_ADDRESS, + 1); + fu_synaptics_cxaudio_firmware_badblock_add( + badblocks, + "boot bytes", + FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET, + sizeof(FuSynapticsCxaudioEepromValiditySignature) + 1); + + /* serial number address and also string pointer itself if set */ + if (self->cinfo.SerialNumberStringAddress != 0x0) { + FuSynapticsCxaudioEepromPtr addr_tmp; + FuSynapticsCxaudioEepromPtr addr_str; + addr_tmp = FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, + SerialNumberStringAddress); + fu_synaptics_cxaudio_firmware_badblock_add( + badblocks, + "serial number", + addr_tmp, + sizeof(FuSynapticsCxaudioEepromPtr)); + memcpy(&addr_str, shadow + addr_tmp, sizeof(addr_str)); + fu_synaptics_cxaudio_firmware_badblock_add(badblocks, + "serial number data", + addr_str, + shadow[addr_str]); + } + fu_synaptics_cxaudio_firmware_avoid_badblocks(badblocks, records); + } + + /* success */ + return TRUE; +} + +static void +fu_synaptics_cxaudio_firmware_init(FuSynapticsCxaudioFirmware *self) +{ +} + +static void +fu_synaptics_cxaudio_firmware_class_init(FuSynapticsCxaudioFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_synaptics_cxaudio_firmware_parse; + klass_firmware->export = fu_synaptics_cxaudio_firmware_export; +} + +FuFirmware * +fu_synaptics_cxaudio_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_CXAUDIO_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..e667594883058ce9102a0d96f4e2a51223beb339 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-synaptics-cxaudio-common.h" + +#define FU_TYPE_SYNAPTICS_CXAUDIO_FIRMWARE (fu_synaptics_cxaudio_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsCxaudioFirmware, + fu_synaptics_cxaudio_firmware, + FU, + SYNAPTICS_CXAUDIO_FIRMWARE, + FuSrecFirmware) + +typedef enum { + FU_SYNAPTICS_CXAUDIO_FILE_KIND_UNKNOWN, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2077X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2076X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2085X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2089X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2098X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2198X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_LAST +} FuSynapticsCxaudioFileKind; + +FuFirmware * +fu_synaptics_cxaudio_firmware_new(void); +FuSynapticsCxaudioFileKind +fu_synaptics_cxaudio_firmware_get_file_type(FuSynapticsCxaudioFirmware *self); +FuSynapticsCxaudioDeviceKind +fu_synaptics_cxaudio_firmware_get_devtype(FuSynapticsCxaudioFirmware *self); +guint8 +fu_synaptics_cxaudio_firmware_get_layout_version(FuSynapticsCxaudioFirmware *self); diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..98c19529981536704dc36ae6328395bfbe8c24cb --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-cxaudio-device.h" +#include "fu-synaptics-cxaudio-firmware.h" +#include "fu-synaptics-cxaudio-plugin.h" + +struct _FuSynapticsCxaudioPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSynapticsCxaudioPlugin, fu_synaptics_cxaudio_plugin, FU_TYPE_PLUGIN) + +static void +fu_synaptics_cxaudio_plugin_init(FuSynapticsCxaudioPlugin *self) +{ +} + +static void +fu_synaptics_cxaudio_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "CxaudioChipIdBase"); + fu_context_add_quirk_key(ctx, "CxaudioPatch1ValidAddr"); + fu_context_add_quirk_key(ctx, "CxaudioPatch2ValidAddr"); + fu_context_add_quirk_key(ctx, "CxaudioSoftwareReset"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_CXAUDIO_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_CXAUDIO_FIRMWARE); +} + +static void +fu_synaptics_cxaudio_plugin_class_init(FuSynapticsCxaudioPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_synaptics_cxaudio_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.h b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..dfdc46ef8dfb8eabe7457623c1a70c8633b1bb40 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSynapticsCxaudioPlugin, + fu_synaptics_cxaudio_plugin, + FU, + SYNAPTICS_CXAUDIO_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/meson.build b/fwupd-1.8.6/plugins/synaptics-cxaudio/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..47d4b1b6d240030090e989af88278d9b8ba23a77 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/meson.build @@ -0,0 +1,16 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsCxaudio"'] + +plugin_quirks += files('synaptics-cxaudio.quirk') +plugin_builtins += static_library('fu_plugin_synaptics_cxaudio', + sources: [ + 'fu-synaptics-cxaudio-plugin.c', + 'fu-synaptics-cxaudio-device.c', + 'fu-synaptics-cxaudio-firmware.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk b/fwupd-1.8.6/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk new file mode 100644 index 0000000000000000000000000000000000000000..759360046e4ee85a18817644947259fde7e4c8b3 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk @@ -0,0 +1,37 @@ +# ThinkPad TBT3-TR Gen 2 dock +[USB\VID_17EF&PID_3083] +Guid = SYNAPTICS_CXAUDIO\ID_CX2098X +ParentGuid = USB\VID_17EF&PID_307F&HUB_0006 + +# ThinkPad TBT3-MS Gen 2 dock +[USB\VID_17EF&PID_3092] +Guid = SYNAPTICS_CXAUDIO\ID_CX2198X +ParentGuid = USB\VID_17EF&PID_308F + +# ThinkPad USB-C Dock Gen2 Audio +[USB\VID_17EF&PID_A396] +Guid = SYNAPTICS_CXAUDIO\ID_CX2198X +ParentGuid = USB\VID_17EF&PID_A391 + +# ThinkPad USB-C Dock Gen2 Audio as updated in firmware version 49-0E-41 +[USB\VID_17EF&PID_30D1] +Guid = SYNAPTICS_CXAUDIO\ID_CX2198X +ParentGuid = USB\VID_17EF&PID_A391 + +# Google Pixel USB-C headphones +[USB\VID_18D1&PID_5033] +Guid = SYNAPTICS_CXAUDIO\ID_CX2198X + +# Google Pixel USB-C <-> 3.5mm adapter +[USB\VID_18D1&PID_5034] +Guid = SYNAPTICS_CXAUDIO\ID_CX2198X + +[SYNAPTICS_CXAUDIO\ID_CX2098X] +Plugin = synaptics_cxaudio +CxaudioChipIdBase = 20980 + +[SYNAPTICS_CXAUDIO\ID_CX2198X] +Plugin = synaptics_cxaudio +CxaudioChipIdBase = 21980 +CxaudioPatch1ValidAddr = 0x0014 +CxaudioPatch2ValidAddr = 0x0171 diff --git a/fwupd-1.8.6/plugins/synaptics-mst/README.md b/fwupd-1.8.6/plugins/synaptics-mst/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2a1ab62a833ba7132ac4b8f1e9095cec8af21e66 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/README.md @@ -0,0 +1,102 @@ +# Synaptics MST + +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-$(board-ID)` +* `MST-$(device_kind)-$(chip-ID)-$(board-ID)` +* `MST-$(device_kind)-$(board-ID)` +* `MST-$(device_kind)` + +Please refer to the plugin source for more details about how the GUID is +constructed for specific hardware. + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. On some hardware the +MST device may not enumerate if there is no monitor actually plugged in. + +## Vendor ID Security + +The vendor ID is set from the PCI vendor, for example set to `DRM_DP_AUX_DEV:0x$(vid)` + +## Requirements + +### (Kernel) DP Aux Interface + +Kernel 4.6 introduced an DRM DP Aux interface for manipulation of the registers +needed to access an MST hub. +This patch can be backported to earlier kernels: + + +### libsmbios + +At compilation time and runtime you will need libsmbios_c version 2.3.0 or later + +* [source](https://github.com/dell/libsmbios) +* [rpms](https://apps.fedoraproject.org/packages/libsmbios) +* [debs (Debian)](http://tracker.debian.org/pkg/libsmbios) +* [debs (Ubuntu)](http://launchpad.net/ubuntu/+source/libsmbios) + +If you don't want or need this functionality you can use the +`--disable-dell` option. + +## Usage + +Supported devices will be displayed in `# fwupdmgr get-devices` output. + +Here is an example output from a Dell WD15 dock: + +```text +Dell WD15/TB16 wired Dock Synaptics VMM3332 + Guid: 653cd006-5433-57db-8632-0413af4d3fcc + DeviceID: MST-1-1-0-0 + Plugin: synaptics_mst + Flags: allow-online + Version: 3.10.002 + Created: 2017-01-13 + Modified: 2017-01-13 + Trusted: none +``` + +Payloads can be flashed just like any other plugin from LVFS. + +## Supported devices + +Not all Dell systems or accessories contain MST hubs. +Here is a sample list of systems known to support them however: + +* 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 + +## External Interface Access + +This plugin requires read/write access to `/dev/drm_dp_aux*`. diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-self-test.c b/fwupd-1.8.6/plugins/synaptics-mst/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..c41b23325b726d616899098c7535e5635433ba16 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-self-test.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2017 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-context-private.h" +#include "fu-plugin-private.h" +#include "fu-synaptics-mst-firmware.h" +#include "fu-synaptics-mst-plugin.h" + +static void +_plugin_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 +_test_add_fake_devices_from_dir(FuPlugin *plugin, const gchar *path) +{ + const gchar *basename; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GDir) dir = g_dir_open(path, 0, &error); + g_assert_no_error(error); + g_assert_nonnull(dir); + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + while ((basename = g_dir_read_name(dir)) != NULL) { + g_autofree gchar *fn = g_build_filename(path, basename, NULL); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuUdevDevice) dev = NULL; + if (!g_str_has_prefix(basename, "drm_dp_aux")) + continue; + dev = g_object_new(FU_TYPE_UDEV_DEVICE, + "context", + ctx, + "physical-id", + "PCI_SLOT_NAME=0000:3e:00.0", + "logical-id", + basename, + "subsystem", + "drm_dp_aux_dev", + "device-file", + fn, + NULL); + g_debug("creating drm_dp_aux_dev object backed by %s", fn); + ret = + fu_plugin_runner_backend_device_added(plugin, FU_DEVICE(dev), progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + } +} + +/* test with no Synaptics MST devices */ +static void +fu_plugin_synaptics_mst_none_func(void) +{ + gboolean ret; + const gchar *ci = g_getenv("CI_NETWORK"); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = + g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_autofree gchar *filename = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + plugin = fu_plugin_new_from_gtype(fu_synaptics_mst_plugin_get_type(), ctx); + g_signal_connect(FU_PLUGIN(plugin), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + &devices); + ret = fu_plugin_runner_startup(plugin, progress, &error); + if (!ret && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("Skipping tests due to unsupported configuration"); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + + filename = g_test_build_filename(G_TEST_DIST, "tests", "no_devices", NULL); + if (!g_file_test(filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing no_devices"); + return; + } + _test_add_fake_devices_from_dir(plugin, filename); + g_assert_cmpint(devices->len, ==, 0); +} + +/* emulate adding/removing a Dell TB16 dock */ +static void +fu_plugin_synaptics_mst_tb16_func(void) +{ + gboolean ret; + const gchar *ci = g_getenv("CI_NETWORK"); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = + g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_autofree gchar *filename = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + plugin = fu_plugin_new_from_gtype(fu_synaptics_mst_plugin_get_type(), ctx); + g_signal_connect(FU_PLUGIN(plugin), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + &devices); + + ret = fu_plugin_runner_startup(plugin, progress, &error); + if (!ret && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("Skipping tests due to unsupported configuration"); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + + filename = g_test_build_filename(G_TEST_DIST, "tests", "tb16_dock", NULL); + if (!g_file_test(filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing tb16_dock"); + return; + } + _test_add_fake_devices_from_dir(plugin, filename); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + g_autofree gchar *tmp = fu_device_to_string(device); + g_debug("%s", tmp); + } + g_assert_cmpint(devices->len, ==, 2); +} + +static void +fu_synaptics_mst_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_synaptics_mst_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_synaptics_mst_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "synaptics-mst.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "bfcdf3e6ca6cef45543bfbb57509c92aec9a39fb"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + 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); + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + g_assert_cmpint(g_mkdir_with_parents("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); + + /* tests go here */ + g_test_add_func("/fwupd/plugin/synaptics_mst{none}", fu_plugin_synaptics_mst_none_func); + g_test_add_func("/fwupd/plugin/synaptics_mst{tb16}", fu_plugin_synaptics_mst_tb16_func); + g_test_add_func("/fwupd/plugin/synaptics_mst/firmware{xml}", + fu_synaptics_mst_firmware_xml_func); + + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-common.c b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-common.c new file mode 100644 index 0000000000000000000000000000000000000000..303c4370a915c702050258f0357f28af7ebe0b88 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-common.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2021 Apollo Ling + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-mst-common.h" + +const gchar * +fu_synaptics_mst_mode_to_string(FuSynapticsMstMode mode) +{ + if (mode == FU_SYNAPTICS_MST_MODE_DIRECT) + return "DIRECT"; + if (mode == FU_SYNAPTICS_MST_MODE_REMOTE) + return "REMOTE"; + return NULL; +} + +const gchar * +fu_synaptics_mst_family_to_string(FuSynapticsMstFamily family) +{ + if (family == FU_SYNAPTICS_MST_FAMILY_TESLA) + return "tesla"; + if (family == FU_SYNAPTICS_MST_FAMILY_LEAF) + return "leaf"; + if (family == FU_SYNAPTICS_MST_FAMILY_PANAMERA) + return "panamera"; + if (family == FU_SYNAPTICS_MST_FAMILY_CAYENNE) + return "cayenne"; + if (family == FU_SYNAPTICS_MST_FAMILY_SPYDER) + return "spyder"; + return NULL; +} + +FuSynapticsMstFamily +fu_synaptics_mst_family_from_chip_id(guint16 chip_id) +{ + if (chip_id >= 0x7000 && chip_id < 0x8000) + return FU_SYNAPTICS_MST_FAMILY_SPYDER; + if ((chip_id >= 0x6000 && chip_id < 0x7000) || (chip_id >= 0x8000 && chip_id < 0x9000)) + return FU_SYNAPTICS_MST_FAMILY_CAYENNE; + if (chip_id >= 0x5000 && chip_id < 0x6000) + return FU_SYNAPTICS_MST_FAMILY_PANAMERA; + if (chip_id >= 0x3000 && chip_id < 0x4000) + return FU_SYNAPTICS_MST_FAMILY_LEAF; + if (chip_id >= 0x2000 && chip_id < 0x3000) + return FU_SYNAPTICS_MST_FAMILY_TESLA; + return FU_SYNAPTICS_MST_FAMILY_UNKNOWN; +} diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-common.h b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-common.h new file mode 100644 index 0000000000000000000000000000000000000000..b650950fc23c34b467b53f79cd23b1c604765ddb --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-common.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2021 Apollo Ling + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define SYNAPTICS_FLASH_MODE_DELAY 3 /* seconds */ + +/** + * FuSynapticsMstMode: + * @FU_SYNAPTICS_MST_MODE_UNKNOWN: Type invalid or not known + * @FU_SYNAPTICS_MST_MODE_DIRECT: Directly addressable + * @FU_SYNAPTICS_MST_MODE_REMOTE: Requires remote register work + * + * The device type. + **/ +typedef enum { + FU_SYNAPTICS_MST_MODE_UNKNOWN, + FU_SYNAPTICS_MST_MODE_DIRECT, + FU_SYNAPTICS_MST_MODE_REMOTE, + /*< private >*/ + FU_SYNAPTICS_MST_MODE_LAST +} FuSynapticsMstMode; + +typedef enum { + FU_SYNAPTICS_MST_FAMILY_UNKNOWN, + FU_SYNAPTICS_MST_FAMILY_TESLA, + FU_SYNAPTICS_MST_FAMILY_LEAF, + FU_SYNAPTICS_MST_FAMILY_PANAMERA, + FU_SYNAPTICS_MST_FAMILY_CAYENNE, + FU_SYNAPTICS_MST_FAMILY_SPYDER, + /**/ + FU_SYNAPTICS_MST_FAMILY_LAST +} FuSynapticsMstFamily; + +const gchar * +fu_synaptics_mst_mode_to_string(FuSynapticsMstMode mode); +const gchar * +fu_synaptics_mst_family_to_string(FuSynapticsMstFamily family); +FuSynapticsMstFamily +fu_synaptics_mst_family_from_chip_id(guint16 chip_id); diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-connection.c b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-connection.c new file mode 100644 index 0000000000000000000000000000000000000000..52c6580a4b928b800fab9fa835e6d6827e332476 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-connection.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2015 Richard Hughes + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-mst-common.h" +#include "fu-synaptics-mst-connection.h" + +#define UNIT_SIZE 32 +#define MAX_WAIT_TIME 3 /* unit : second */ + +struct _FuSynapticsMstConnection { + GObject parent_instance; + gint fd; /* not owned by the connection */ + guint8 layer; + guint8 remain_layer; + guint8 rad; +}; + +G_DEFINE_TYPE(FuSynapticsMstConnection, fu_synaptics_mst_connection, G_TYPE_OBJECT) + +static void +fu_synaptics_mst_connection_init(FuSynapticsMstConnection *self) +{ +} + +static void +fu_synaptics_mst_connection_class_init(FuSynapticsMstConnectionClass *klass) +{ +} + +static gboolean +fu_synaptics_mst_connection_aux_node_read(FuSynapticsMstConnection *self, + guint32 offset, + guint8 *buf, + gint length, + GError **error) +{ + if (lseek(self->fd, offset, SEEK_SET) != offset) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to lseek to 0x%x on layer:%u, rad:0x%x", + offset, + self->layer, + self->rad); + return FALSE; + } + + if (read(self->fd, buf, length) != length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to read 0x%x bytes on layer:%u, rad:0x%x", + (guint)length, + self->layer, + self->rad); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_connection_aux_node_write(FuSynapticsMstConnection *self, + guint32 offset, + const guint8 *buf, + gint length, + GError **error) +{ + if (lseek(self->fd, offset, SEEK_SET) != offset) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to lseek to 0x%x on layer:%u, rad:0x%x", + offset, + self->layer, + self->rad); + return FALSE; + } + + if (write(self->fd, buf, length) != length) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to write 0x%x bytes on layer:%u, rad:0x%x", + (guint)length, + self->layer, + self->rad); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_connection_bus_read(FuSynapticsMstConnection *self, + guint32 offset, + guint8 *buf, + guint32 length, + GError **error) +{ + return fu_synaptics_mst_connection_aux_node_read(self, offset, buf, length, error); +} + +static gboolean +fu_synaptics_mst_connection_bus_write(FuSynapticsMstConnection *self, + guint32 offset, + const guint8 *buf, + guint32 length, + GError **error) +{ + return fu_synaptics_mst_connection_aux_node_write(self, offset, buf, length, error); +} + +FuSynapticsMstConnection * +fu_synaptics_mst_connection_new(gint fd, guint8 layer, guint rad) +{ + FuSynapticsMstConnection *self = g_object_new(FU_TYPE_SYNAPTICS_MST_CONNECTION, NULL); + self->fd = fd; + self->layer = layer; + self->remain_layer = layer; + self->rad = rad; + return self; +} + +gboolean +fu_synaptics_mst_connection_read(FuSynapticsMstConnection *self, + guint32 offset, + guint8 *buf, + guint32 length, + GError **error) +{ + if (self->layer && self->remain_layer) { + guint8 node; + gboolean result; + + self->remain_layer--; + node = (self->rad >> self->remain_layer * 2) & 0x03; + result = fu_synaptics_mst_connection_rc_get_command(self, + UPDC_READ_FROM_TX_DPCD + node, + length, + offset, + (guint8 *)buf, + error); + self->remain_layer++; + return result; + } + + return fu_synaptics_mst_connection_bus_read(self, offset, buf, length, error); +} + +gboolean +fu_synaptics_mst_connection_write(FuSynapticsMstConnection *self, + guint32 offset, + const guint8 *buf, + guint32 length, + GError **error) +{ + if (self->layer && self->remain_layer) { + guint8 node; + gboolean result; + + self->remain_layer--; + node = (self->rad >> self->remain_layer * 2) & 0x03; + result = fu_synaptics_mst_connection_rc_set_command(self, + UPDC_WRITE_TO_TX_DPCD + node, + length, + offset, + (guint8 *)buf, + error); + self->remain_layer++; + return result; + } + + return fu_synaptics_mst_connection_bus_write(self, offset, buf, length, error); +} + +gboolean +fu_synaptics_mst_connection_rc_set_command(FuSynapticsMstConnection *self, + 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 { + if (data_left > UNIT_SIZE) { + cur_length = UNIT_SIZE; + } else { + cur_length = data_left; + } + + if (cur_length) { + /* write data */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_DATA, + buf, + cur_length, + error)) { + g_prefix_error(error, "failure writing data register: "); + return FALSE; + } + + /* write offset */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_OFFSET, + (guint8 *)&cur_offset, + 4, + error)) { + g_prefix_error(error, "failure writing offset register: "); + return FALSE; + } + + /* write length */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_LEN, + (guint8 *)&cur_length, + 4, + error)) { + g_prefix_error(error, "failure writing length register: "); + return FALSE; + } + } + + /* send command */ + cmd = 0x80 | rc_cmd; + if (!fu_synaptics_mst_connection_write(self, + 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 { + if (!fu_synaptics_mst_connection_read(self, + 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) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "timeout exceeded"); + return FALSE; + } + } 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); + + return FALSE; + } + + buf += cur_length; + cur_offset += cur_length; + data_left -= cur_length; + } while (data_left); + + return TRUE; +} + +gboolean +fu_synaptics_mst_connection_rc_get_command(FuSynapticsMstConnection *self, + guint32 rc_cmd, + guint32 length, + guint32 offset, + guint8 *buf, + GError **error) +{ + guint32 cur_offset = offset; + guint32 cur_length; + gint data_need = length; + guint32 cmd; + guint32 readData = 0; + long deadline; + struct timespec t_spec; + + while (data_need) { + if (data_need > UNIT_SIZE) { + cur_length = UNIT_SIZE; + } else { + cur_length = data_need; + } + + if (cur_length) { + /* write offset */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_OFFSET, + (guint8 *)&cur_offset, + 4, + error)) { + g_prefix_error(error, "failed to write offset: "); + return FALSE; + } + + /* write length */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_LEN, + (guint8 *)&cur_length, + 4, + error)) { + g_prefix_error(error, "failed to write length: "); + return FALSE; + } + } + + /* send command */ + cmd = 0x80 | rc_cmd; + if (!fu_synaptics_mst_connection_write(self, + 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 { + if (!fu_synaptics_mst_connection_read(self, + 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) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "timeout exceeded"); + return FALSE; + } + } 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); + + return FALSE; + } + + if (cur_length) { + if (!fu_synaptics_mst_connection_read(self, + REG_RC_DATA, + buf, + cur_length, + error)) { + g_prefix_error(error, "failed to read data: "); + return FALSE; + } + } + + buf += cur_length; + cur_offset += cur_length; + data_need -= cur_length; + } + + return TRUE; +} + +gboolean +fu_synaptics_mst_connection_rc_special_get_command(FuSynapticsMstConnection *self, + guint32 rc_cmd, + guint32 cmd_length, + guint32 cmd_offset, + guint8 *cmd_data, + guint32 length, + guint8 *buf, + GError **error) +{ + guint32 readData = 0; + guint32 cmd; + long deadline; + struct timespec t_spec; + + if (cmd_length) { + /* write cmd data */ + if (cmd_data != NULL) { + if (!fu_synaptics_mst_connection_write(self, + REG_RC_DATA, + cmd_data, + cmd_length, + error)) { + g_prefix_error(error, "Failed to write command data: "); + return FALSE; + } + } + + /* write offset */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_OFFSET, + (guint8 *)&cmd_offset, + 4, + error)) { + g_prefix_error(error, "failed to write offset: "); + return FALSE; + } + + /* write length */ + if (!fu_synaptics_mst_connection_write(self, + REG_RC_LEN, + (guint8 *)&cmd_length, + 4, + error)) { + g_prefix_error(error, "failed to write length: "); + return FALSE; + } + } + + /* send command */ + cmd = 0x80 | rc_cmd; + if (!fu_synaptics_mst_connection_write(self, 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 { + if (!fu_synaptics_mst_connection_read(self, + 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) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "timeout exceeded"); + return FALSE; + } + } 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); + + return FALSE; + } + + if (length) { + if (!fu_synaptics_mst_connection_read(self, REG_RC_DATA, buf, length, error)) { + g_prefix_error(error, "failed to read length: "); + } + } + + return TRUE; +} + +gboolean +fu_synaptics_mst_connection_enable_rc(FuSynapticsMstConnection *self, GError **error) +{ + const gchar *sc = "PRIUS"; + + for (gint i = 0; i <= self->layer; i++) { + g_autoptr(FuSynapticsMstConnection) connection_tmp = NULL; + connection_tmp = fu_synaptics_mst_connection_new(self->fd, i, self->rad); + if (!fu_synaptics_mst_connection_rc_set_command(connection_tmp, + UPDC_ENABLE_RC, + 5, + 0, + (guint8 *)sc, + error)) { + g_prefix_error(error, "failed to enable remote control: "); + return FALSE; + } + } + + return TRUE; +} + +gboolean +fu_synaptics_mst_connection_disable_rc(FuSynapticsMstConnection *self, GError **error) +{ + for (gint i = self->layer; i >= 0; i--) { + g_autoptr(FuSynapticsMstConnection) connection_tmp = NULL; + connection_tmp = fu_synaptics_mst_connection_new(self->fd, i, self->rad); + if (!fu_synaptics_mst_connection_rc_set_command(connection_tmp, + UPDC_DISABLE_RC, + 0, + 0, + NULL, + error)) { + g_prefix_error(error, "failed to disable remote control: "); + return FALSE; + } + } + + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-connection.h b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-connection.h new file mode 100644 index 0000000000000000000000000000000000000000..c1dacb33c81fa3d097a0504804d83d931460e4a1 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-connection.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2021 Apollo Ling + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_MST_CONNECTION (fu_synaptics_mst_connection_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsMstConnection, + fu_synaptics_mst_connection, + FU, + SYNAPTICS_MST_CONNECTION, + GObject) + +#define ADDR_CUSTOMER_ID 0X10E +#define ADDR_BOARD_ID 0x10F + +#define ADDR_MEMORY_CUSTOMER_ID_CAYENNE 0x9000024E +#define ADDR_MEMORY_BOARD_ID_CAYENNE 0x9000024F +#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 +#define REG_RC_RESULT 0x4B3 +#define REG_RC_LEN 0x4B8 +#define REG_RC_OFFSET 0x4BC +#define REG_RC_DATA 0x4C0 + +#define REG_VENDOR_ID 0x500 +#define REG_CHIP_ID 0x507 +#define REG_FIRMWARE_VERSION 0x50A + +typedef enum { + UPDC_COMMAND_SUCCESS = 0, + UPDC_COMMAND_INVALID, + UPDC_COMMAND_UNSUPPORT, + UPDC_COMMAND_FAILED, + UPDC_COMMAND_DISABLED, +} SynapticsMstUpdcRc; + +typedef enum { + UPDC_ENABLE_RC = 0x01, + UPDC_DISABLE_RC = 0x02, + UPDC_GET_ID = 0x03, + UPDC_GET_VERSION = 0x04, + UPDC_FLASH_MAPPING = 0x07, + UPDC_ENABLE_FLASH_CHIP_ERASE = 0x08, + UPDC_CAL_EEPROM_CHECKSUM = 0x11, + UPDC_FLASH_ERASE = 0x14, + UPDC_CAL_EEPROM_CHECK_CRC8 = 0x16, + UPDC_CAL_EEPROM_CHECK_CRC16 = 0x17, + UPDC_ACTIVATE_FIRMWARE = 0X18, + UPDC_WRITE_TO_EEPROM = 0X20, + UPDC_WRITE_TO_MEMORY = 0x21, + UPDC_WRITE_TO_TX_DPCD = 0x22, + UPDC_READ_FROM_EEPROM = 0x30, + UPDC_READ_FROM_MEMORY = 0x31, + UPDC_READ_FROM_TX_DPCD = 0x32, +} SynapticsMstUpdcCmd; + +FuSynapticsMstConnection * +fu_synaptics_mst_connection_new(gint fd, guint8 layer, guint rad); + +gboolean +fu_synaptics_mst_connection_read(FuSynapticsMstConnection *self, + guint32 offset, + guint8 *buf, + guint32 length, + GError **error); + +gboolean +fu_synaptics_mst_connection_write(FuSynapticsMstConnection *self, + guint32 offset, + const guint8 *buf, + guint32 length, + GError **error); + +gboolean +fu_synaptics_mst_connection_rc_set_command(FuSynapticsMstConnection *self, + guint32 rc_cmd, + guint32 length, + guint32 offset, + const guint8 *buf, + GError **error); + +gboolean +fu_synaptics_mst_connection_rc_get_command(FuSynapticsMstConnection *self, + guint32 rc_cmd, + guint32 length, + guint32 offset, + guint8 *buf, + GError **error); + +gboolean +fu_synaptics_mst_connection_rc_special_get_command(FuSynapticsMstConnection *self, + guint32 rc_cmd, + guint32 cmd_length, + guint32 cmd_offset, + guint8 *cmd_data, + guint32 length, + guint8 *buf, + GError **error); + +gboolean +fu_synaptics_mst_connection_enable_rc(FuSynapticsMstConnection *self, GError **error); + +gboolean +fu_synaptics_mst_connection_disable_rc(FuSynapticsMstConnection *self, GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-device.c b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-device.c new file mode 100644 index 0000000000000000000000000000000000000000..32bd304e8d4b301fad73b509464a46d52916ed34 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-device.c @@ -0,0 +1,1531 @@ +/* + * Copyright (C) 2015 Richard Hughes + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2018 Ryan Chang + * Copyright (C) 2021 Apollo Ling + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-mst-common.h" +#include "fu-synaptics-mst-connection.h" +#include "fu-synaptics-mst-device.h" +#include "fu-synaptics-mst-firmware.h" + +#define FU_SYNAPTICS_MST_ID_CTRL_SIZE 0x1000 +#define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3 + +#define BIT(n) (1 << (n)) +#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 +#define ESM_CODE_SIZE 0x40000 +#define PAYLOAD_SIZE_512K 0x80000 +#define PAYLOAD_SIZE_64K 0x10000 +#define MAX_RETRY_COUNTS 10 +#define BLOCK_UNIT 64 +#define BANKTAG_0 0 +#define BANKTAG_1 1 +#define CRC_8 8 +#define CRC_16 16 +#define REG_ESM_DISABLE 0x2000fc +#define REG_QUAD_DISABLE 0x200fc0 +#define REG_HDCP22_DISABLE 0x200f90 + +#define FLASH_SETTLE_TIME 5000000 /* us */ + +#define CAYENNE_FIRMWARE_SIZE 0x50000 /* bytes */ + +/** + * FU_SYNAPTICS_MST_DEVICE_FLAG_IGNORE_BOARD_ID: + * + * Ignore board ID firmware mismatch. + */ +#define FU_SYNAPTICS_MST_DEVICE_FLAG_IGNORE_BOARD_ID (1 << 0) + +struct _FuSynapticsMstDevice { + FuUdevDevice parent_instance; + gchar *device_kind; + gchar *system_type; + guint64 write_block_size; + FuSynapticsMstFamily family; + FuSynapticsMstMode mode; + guint8 active_bank; + guint8 layer; + guint16 rad; /* relative address */ + guint32 board_id; + guint16 chip_id; +}; + +G_DEFINE_TYPE(FuSynapticsMstDevice, fu_synaptics_mst_device, FU_TYPE_UDEV_DEVICE) + +static void +fu_synaptics_mst_device_finalize(GObject *object) +{ + FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(object); + + g_free(self->device_kind); + g_free(self->system_type); + + G_OBJECT_CLASS(fu_synaptics_mst_device_parent_class)->finalize(object); +} + +static void +fu_synaptics_mst_device_init(FuSynapticsMstDevice *self) +{ + FuUdevDeviceFlags flags = + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT; + if (fu_udev_device_get_dev(FU_UDEV_DEVICE(self)) != NULL) + flags |= FU_UDEV_DEVICE_FLAG_OPEN_WRITE; + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), flags); + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.mst"); + fu_device_set_vendor(FU_DEVICE(self), "Synaptics"); + fu_device_add_vendor_id(FU_DEVICE(self), "DRM_DP_AUX_DEV:0x06CB"); + fu_device_set_summary(FU_DEVICE(self), "Multi-stream transport device"); + fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_register_private_flag(FU_DEVICE(self), + FU_SYNAPTICS_MST_DEVICE_FLAG_IGNORE_BOARD_ID, + "ignore-board-id"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_synaptics_mst_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_synaptics_mst_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "DeviceKind", self->device_kind); + if (self->mode != FU_SYNAPTICS_MST_MODE_UNKNOWN) { + fu_string_append(str, idt, "Mode", fu_synaptics_mst_mode_to_string(self->mode)); + } + if (self->family == FU_SYNAPTICS_MST_FAMILY_PANAMERA) + fu_string_append_kx(str, idt, "ActiveBank", self->active_bank); + fu_string_append_kx(str, idt, "Layer", self->layer); + fu_string_append_kx(str, idt, "Rad", self->rad); + if (self->board_id != 0x0) + fu_string_append_ku(str, idt, "BoardId", self->board_id); + if (self->chip_id != 0x0) + fu_string_append_kx(str, idt, "ChipId", self->chip_id); +} + +static gboolean +fu_synaptics_mst_device_enable_rc(FuSynapticsMstDevice *self, GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + /* in test mode */ + if (fu_udev_device_get_dev(FU_UDEV_DEVICE(self)) == NULL) + return TRUE; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + return fu_synaptics_mst_connection_enable_rc(connection, error); +} + +static gboolean +fu_synaptics_mst_device_disable_rc(FuSynapticsMstDevice *self, GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + /* in test mode */ + if (fu_udev_device_get_dev(FU_UDEV_DEVICE(self)) == NULL) + return TRUE; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + return fu_synaptics_mst_connection_disable_rc(connection, error); +} + +static gboolean +fu_synaptics_mst_device_probe(FuDevice *device, GError **error) +{ + /* FuUdevDevice->probe */ + if (!FU_DEVICE_CLASS(fu_synaptics_mst_device_parent_class)->probe(device, error)) + return FALSE; + + /* get from sysfs if not set from tests */ + if (fu_device_get_logical_id(device) == NULL) { + g_autofree gchar *logical_id = NULL; + logical_id = + g_path_get_basename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device))); + fu_device_set_logical_id(device, logical_id); + } + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci,drm_dp_aux_dev", error); +} + +static gboolean +fu_synaptics_mst_device_get_flash_checksum(FuSynapticsMstDevice *self, + guint32 length, + guint32 offset, + guint32 *checksum, + GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + if (!fu_synaptics_mst_connection_rc_special_get_command(connection, + UPDC_CAL_EEPROM_CHECKSUM, + length, + offset, + NULL, + 4, + (guint8 *)checksum, + error)) { + g_prefix_error(error, "failed to get flash checksum: "); + return FALSE; + } + + return TRUE; +} + +static guint16 +fu_synaptics_mst_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, 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, + 0x8077, 0x0072, 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, 0x80c3, + 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, 0x00f0, 0x80f5, 0x80ff, 0x00fa, + 0x80eb, 0x00ee, 0x00e4, 0x80e1, 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, + 0x80b1, 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, 0x8183, 0x0186, + 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, + 0x01ae, 0x01a4, 0x81a1, 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, 0x0140, 0x8145, 0x814f, + 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, + 0x8167, 0x0162, 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, 0x0110, + 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, 0x8303, 0x0306, 0x030c, 0x8309, + 0x0318, 0x831d, 0x8317, 0x0312, 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, + 0x8321, 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, 0x8353, 0x0356, + 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, + 0x03de, 0x03d4, 0x83d1, 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, 0x0390, 0x8395, 0x839f, + 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, + 0x0294, 0x8291, 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, 0x82e3, + 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, 0x02d0, 0x82d5, 0x82df, 0x02da, + 0x82cb, 0x02ce, 0x02c4, 0x82c1, 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, + 0x0252, 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, 0x0220, 0x8225, + 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, + 0x820d, 0x8207, 0x0202}; + static const guint16 CRC8_table[] = { + 0x00, 0xd5, 0x7f, 0xaa, 0xfe, 0x2b, 0x81, 0x54, 0x29, 0xfc, 0x56, 0x83, 0xd7, 0x02, + 0xa8, 0x7d, 0x52, 0x87, 0x2d, 0xf8, 0xac, 0x79, 0xd3, 0x06, 0x7b, 0xae, 0x04, 0xd1, + 0x85, 0x50, 0xfa, 0x2f, 0xa4, 0x71, 0xdb, 0x0e, 0x5a, 0x8f, 0x25, 0xf0, 0x8d, 0x58, + 0xf2, 0x27, 0x73, 0xa6, 0x0c, 0xd9, 0xf6, 0x23, 0x89, 0x5c, 0x08, 0xdd, 0x77, 0xa2, + 0xdf, 0x0a, 0xa0, 0x75, 0x21, 0xf4, 0x5e, 0x8b, 0x9d, 0x48, 0xe2, 0x37, 0x63, 0xb6, + 0x1c, 0xc9, 0xb4, 0x61, 0xcb, 0x1e, 0x4a, 0x9f, 0x35, 0xe0, 0xcf, 0x1a, 0xb0, 0x65, + 0x31, 0xe4, 0x4e, 0x9b, 0xe6, 0x33, 0x99, 0x4c, 0x18, 0xcd, 0x67, 0xb2, 0x39, 0xec, + 0x46, 0x93, 0xc7, 0x12, 0xb8, 0x6d, 0x10, 0xc5, 0x6f, 0xba, 0xee, 0x3b, 0x91, 0x44, + 0x6b, 0xbe, 0x14, 0xc1, 0x95, 0x40, 0xea, 0x3f, 0x42, 0x97, 0x3d, 0xe8, 0xbc, 0x69, + 0xc3, 0x16, 0xef, 0x3a, 0x90, 0x45, 0x11, 0xc4, 0x6e, 0xbb, 0xc6, 0x13, 0xb9, 0x6c, + 0x38, 0xed, 0x47, 0x92, 0xbd, 0x68, 0xc2, 0x17, 0x43, 0x96, 0x3c, 0xe9, 0x94, 0x41, + 0xeb, 0x3e, 0x6a, 0xbf, 0x15, 0xc0, 0x4b, 0x9e, 0x34, 0xe1, 0xb5, 0x60, 0xca, 0x1f, + 0x62, 0xb7, 0x1d, 0xc8, 0x9c, 0x49, 0xe3, 0x36, 0x19, 0xcc, 0x66, 0xb3, 0xe7, 0x32, + 0x98, 0x4d, 0x30, 0xe5, 0x4f, 0x9a, 0xce, 0x1b, 0xb1, 0x64, 0x72, 0xa7, 0x0d, 0xd8, + 0x8c, 0x59, 0xf3, 0x26, 0x5b, 0x8e, 0x24, 0xf1, 0xa5, 0x70, 0xda, 0x0f, 0x20, 0xf5, + 0x5f, 0x8a, 0xde, 0x0b, 0xa1, 0x74, 0x09, 0xdc, 0x76, 0xa3, 0xf7, 0x22, 0x88, 0x5d, + 0xd6, 0x03, 0xa9, 0x7c, 0x28, 0xfd, 0x57, 0x82, 0xff, 0x2a, 0x80, 0x55, 0x01, 0xd4, + 0x7e, 0xab, 0x84, 0x51, 0xfb, 0x2e, 0x7a, 0xaf, 0x05, 0xd0, 0xad, 0x78, 0xd2, 0x07, + 0x53, 0x86, 0x2c, 0xf9}; + guint8 val; + guint16 remainder = (guint16)crc; + const guint8 *message = payload_data; + + if (type == CRC_8) { + for (guint32 byte = 0; byte < length; ++byte) { + val = (guint8)(message[byte] ^ remainder); + remainder = CRC8_table[val]; + } + } else { + for (guint32 byte = 0; byte < length; ++byte) { + val = (guint8)(message[byte] ^ (remainder >> 8)); + remainder = CRC16_table[val] ^ (remainder << 8); + } + } + + return remainder; +} + +static gboolean +fu_synaptics_mst_device_set_flash_sector_erase(FuSynapticsMstDevice *self, + guint16 rc_cmd, + guint16 offset, + GError **error) +{ + guint16 us_data; + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + /* Need to add Wp control ? */ + us_data = rc_cmd + offset; + + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_FLASH_ERASE, + 2, + 0, + (guint8 *)&us_data, + error)) { + g_prefix_error(error, "can't sector erase flash at offset %x: ", offset); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_update_esm(FuSynapticsMstDevice *self, + const guint8 *payload_data, + FuProgress *progress, + GError **error) +{ + guint32 checksum = 0; + guint32 esm_sz = ESM_CODE_SIZE; + guint32 flash_checksum = 0; + guint32 unit_sz = BLOCK_UNIT; + guint32 write_loops = 0; + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + + if (!fu_synaptics_mst_device_get_flash_checksum(self, + esm_sz, + EEPROM_ESM_OFFSET, + &flash_checksum, + error)) { + return FALSE; + } + + /* ESM checksum same */ + checksum = fu_sum32(payload_data + EEPROM_ESM_OFFSET, esm_sz); + if (checksum == flash_checksum) { + g_debug("ESM checksum already matches"); + return TRUE; + } + g_debug("ESM checksum %x doesn't match expected %x", flash_checksum, checksum); + + /* update ESM firmware */ + write_loops = esm_sz / unit_sz; + for (guint retries_cnt = 0;; retries_cnt++) { + guint32 write_idx = 0; + guint32 write_offset = 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 (!fu_synaptics_mst_device_set_flash_sector_erase(self, + 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 */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, write_loops); + for (guint32 i = 0; i < write_loops; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_EEPROM, + unit_sz, + write_offset, + esm_code_ptr + write_idx, + &error_local)) { + g_warning("failed to write ESM: %s", error_local->message); + break; + } + write_offset += unit_sz; + write_idx += unit_sz; + fu_progress_step_done(progress); + } + + /* check ESM checksum */ + flash_checksum = 0; + if (!fu_synaptics_mst_device_get_flash_checksum(self, + esm_sz, + EEPROM_ESM_OFFSET, + &flash_checksum, + error)) + return FALSE; + + /* ESM update done */ + if (checksum == flash_checksum) + break; + g_debug("attempt %u: ESM checksum %x didn't match %x", + retries_cnt, + flash_checksum, + checksum); + + /* abort */ + if (retries_cnt > MAX_RETRY_COUNTS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum did not match after %u tries", + retries_cnt); + return FALSE; + } + } + g_debug("ESM successfully written"); + + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_update_tesla_leaf_firmware(FuSynapticsMstDevice *self, + guint32 payload_len, + const guint8 *payload_data, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + guint32 data_to_write = 0; + guint32 offset = 0; + guint32 write_loops = 0; + + write_loops = (payload_len / BLOCK_UNIT); + data_to_write = payload_len; + + if (payload_len % BLOCK_UNIT) + write_loops++; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + for (guint32 retries_cnt = 0;; retries_cnt++) { + guint32 checksum; + guint32 flash_checksum = 0; + + if (!fu_synaptics_mst_device_set_flash_sector_erase(self, 0xffff, 0, error)) + return FALSE; + g_debug("Waiting for flash clear to settle"); + g_usleep(FLASH_SETTLE_TIME); + + fu_progress_set_steps(progress, write_loops); + 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; + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_EEPROM, + length, + offset, + payload_data + offset, + &error_local)) { + g_warning("Failed to write flash offset 0x%04x: %s, retrying", + offset, + error_local->message); + /* repeat once */ + if (!fu_synaptics_mst_connection_rc_set_command( + connection, + UPDC_WRITE_TO_EEPROM, + length, + offset, + payload_data + offset, + error)) { + g_prefix_error(error, + "can't write flash offset 0x%04x: ", + offset); + return FALSE; + } + } + offset += length; + data_to_write -= length; + fu_progress_step_done(progress); + } + + /* check data just written */ + if (!fu_synaptics_mst_device_get_flash_checksum(self, + payload_len, + 0, + &flash_checksum, + error)) + return FALSE; + checksum = fu_sum32(payload_data, payload_len); + if (checksum == flash_checksum) + break; + g_debug("attempt %u: checksum %x didn't match %x", + retries_cnt, + flash_checksum, + checksum); + + if (retries_cnt > MAX_RETRY_COUNTS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum %x mismatched %x", + flash_checksum, + checksum); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_get_active_bank_panamera(FuSynapticsMstDevice *self, GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + guint32 buf[16]; + + /* get used bank */ + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + if (!fu_synaptics_mst_connection_rc_get_command(connection, + UPDC_READ_FROM_MEMORY, + ((sizeof(buf) / sizeof(buf[0])) * 4), + (gint)0x20010c, + (guint8 *)buf, + error)) { + g_prefix_error(error, "get active bank failed: "); + return FALSE; + } + if ((buf[0] & BIT(7)) || (buf[0] & BIT(30))) + self->active_bank = BANKTAG_1; + else + self->active_bank = BANKTAG_0; + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_update_panamera_firmware(FuSynapticsMstDevice *self, + guint32 payload_len, + const guint8 *payload_data, + FuProgress *progress, + GError **error) +{ + guint16 crc_tmp = 0; + guint32 fw_size = 0; + guint32 unit_sz = BLOCK_UNIT; + guint32 write_loops = 0; + guint8 bank_to_update = BANKTAG_1; + guint8 readBuf[256]; + guint8 tagData[16]; + struct tm *pTM; + time_t timeptr; + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + /* get used bank */ + if (!fu_synaptics_mst_device_get_active_bank_panamera(self, error)) + return FALSE; + if (self->active_bank == BANKTAG_1) + bank_to_update = BANKTAG_0; + g_debug("bank to update:%x", bank_to_update); + + /* get firmware size */ + if (!fu_memread_uint32_safe(payload_data, + payload_len, + 0x400, + &fw_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fw_size += 0x410; + + /* Current max firmware size is 104K */ + if (fw_size < payload_len) + fw_size = 104 * 1024; + g_debug("Calculated fw size as %u", fw_size); + + /* Update firmware */ + write_loops = fw_size / unit_sz; + if (fw_size % unit_sz) + write_loops++; + + for (guint32 retries_cnt = 0;; retries_cnt++) { + guint32 checksum = 0; + guint32 erase_offset; + guint32 flash_checksum = 0; + guint32 write_idx; + guint32 write_offset; + + /* erase storage */ + erase_offset = bank_to_update * 2; + if (!fu_synaptics_mst_device_set_flash_sector_erase(self, + FLASH_SECTOR_ERASE_64K, + erase_offset++, + error)) + return FALSE; + if (!fu_synaptics_mst_device_set_flash_sector_erase(self, + FLASH_SECTOR_ERASE_64K, + erase_offset, + error)) + return FALSE; + g_debug("Waiting for flash clear to settle"); + g_usleep(FLASH_SETTLE_TIME); + + /* write */ + write_idx = 0; + write_offset = EEPROM_BANK_OFFSET * bank_to_update; + connection = + fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + fu_progress_set_steps(progress, write_loops); + for (guint32 i = 0; i < write_loops; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_EEPROM, + unit_sz, + write_offset, + payload_data + write_idx, + &error_local)) { + g_warning("Write failed: %s, retrying", error_local->message); + /* repeat once */ + if (!fu_synaptics_mst_connection_rc_set_command( + connection, + UPDC_WRITE_TO_EEPROM, + unit_sz, + write_offset, + payload_data + write_idx, + error)) { + g_prefix_error(error, "firmware write failed: "); + return FALSE; + } + } + + write_offset += unit_sz; + write_idx += unit_sz; + fu_progress_step_done(progress); + } + + /* verify CRC */ + checksum = fu_synaptics_mst_device_get_crc(0, 16, fw_size, payload_data); + for (guint32 i = 0; i < 4; i++) { + g_usleep(1000); /* wait crc calculation */ + if (!fu_synaptics_mst_connection_rc_special_get_command( + connection, + UPDC_CAL_EEPROM_CHECK_CRC16, + fw_size, + (EEPROM_BANK_OFFSET * bank_to_update), + NULL, + 4, + (guint8 *)(&flash_checksum), + error)) { + g_prefix_error(error, "Failed to get flash checksum: "); + return FALSE; + } + } + if (checksum == flash_checksum) + break; + if (retries_cnt > MAX_RETRY_COUNTS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "firmware update fail"); + return FALSE; + } + g_usleep(2000); + } + + /* set tag valid */ + time(&timeptr); + pTM = localtime(&timeptr); + memset(tagData, 0, sizeof(tagData)); + memset(readBuf, 0, sizeof(readBuf)); + + tagData[1] = pTM->tm_mon + 1; + tagData[2] = pTM->tm_mday; + tagData[3] = pTM->tm_year + 1900 - 2000; + crc_tmp = fu_synaptics_mst_device_get_crc(0, 16, fw_size, payload_data); + tagData[0] = bank_to_update; + tagData[4] = (crc_tmp >> 8) & 0xff; + tagData[5] = crc_tmp & 0xff; + tagData[15] = (guint8)fu_synaptics_mst_device_get_crc(0, 8, 15, tagData); + g_debug("tag date %x %x %x crc %x %x %x %x", + tagData[1], + tagData[2], + tagData[3], + tagData[0], + tagData[4], + tagData[5], + tagData[15]); + + for (guint32 retries_cnt = 0;; retries_cnt++) { + gboolean match = TRUE; + if (!fu_synaptics_mst_connection_rc_set_command( + connection, + UPDC_WRITE_TO_EEPROM, + 16, + (EEPROM_BANK_OFFSET * bank_to_update + EEPROM_TAG_OFFSET), + tagData, + error)) { + g_prefix_error(error, "failed to write tag: "); + return FALSE; + } + g_usleep(200); + if (!fu_synaptics_mst_connection_rc_get_command( + connection, + UPDC_READ_FROM_EEPROM, + 16, + (EEPROM_BANK_OFFSET * bank_to_update + EEPROM_TAG_OFFSET), + readBuf, + error)) { + g_prefix_error(error, "failed to read tag: "); + return FALSE; + } + for (guint32 i = 0; i < 16; i++) { + if (readBuf[i] != tagData[i]) { + match = FALSE; + break; + } + } + if (match) + break; + if (retries_cnt > MAX_RETRY_COUNTS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "set tag valid fail"); + return FALSE; + } + } + + /* set tag invalid*/ + if (!fu_synaptics_mst_connection_rc_get_command( + connection, + UPDC_READ_FROM_EEPROM, + 1, + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_TAG_OFFSET + 15), + tagData, + error)) { + g_prefix_error(error, "failed to read tag from flash: "); + return FALSE; + } + + for (guint32 retries_cnt = 0;; retries_cnt++) { + /* CRC8 is not 0xff, erase last 4k of bank# */ + if (tagData[0] != 0xff) { + guint32 erase_offset; + /* offset for last 4k of bank# */ + erase_offset = + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_BANK_OFFSET - 0x1000) / + 0x1000; + if (!fu_synaptics_mst_device_set_flash_sector_erase(self, + FLASH_SECTOR_ERASE_4K, + erase_offset, + error)) + return FALSE; + /* CRC8 is 0xff, set it to 0x00 */ + } else { + tagData[1] = 0x00; + if (!fu_synaptics_mst_connection_rc_set_command( + connection, + UPDC_WRITE_TO_EEPROM, + 1, + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_TAG_OFFSET + 15), + &tagData[1], + error)) { + g_prefix_error(error, "failed to clear CRC: "); + return FALSE; + } + } + if (!fu_synaptics_mst_connection_rc_get_command( + connection, + UPDC_READ_FROM_EEPROM, + 1, + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_TAG_OFFSET + 15), + readBuf, + error)) { + g_prefix_error(error, "failed to read CRC from flash: "); + return FALSE; + } + if ((readBuf[0] == 0xff && tagData[0] != 0xff) || + (readBuf[0] == 0x00 && tagData[0] == 0xff)) { + break; + } + if (retries_cnt > MAX_RETRY_COUNTS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "set tag invalid fail"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_panamera_prepare_write(FuSynapticsMstDevice *self, GError **error) +{ + guint32 buf[4] = {0}; + g_autoptr(FuSynapticsMstConnection) connection = NULL; + + /* Need to detect flash mode and ESM first ? */ + /* disable flash Quad mode and ESM/HDCP2.2*/ + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + + /* disable ESM first */ + buf[0] = 0x21; + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_MEMORY, + 4, + (gint)REG_ESM_DISABLE, + (guint8 *)buf, + error)) { + g_prefix_error(error, "ESM disable failed: "); + return FALSE; + } + + /* wait for ESM exit */ + g_usleep(200); + + /* disable QUAD mode */ + if (!fu_synaptics_mst_connection_rc_get_command(connection, + UPDC_READ_FROM_MEMORY, + ((sizeof(buf) / sizeof(buf[0])) * 4), + (gint)REG_QUAD_DISABLE, + (guint8 *)buf, + error)) { + g_prefix_error(error, "quad query failed: "); + return FALSE; + } + + buf[0] = 0x00; + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_MEMORY, + 4, + (gint)REG_QUAD_DISABLE, + (guint8 *)buf, + error)) { + g_prefix_error(error, "quad disable failed: "); + return FALSE; + } + + /* disable HDCP2.2 */ + if (!fu_synaptics_mst_connection_rc_get_command(connection, + UPDC_READ_FROM_MEMORY, + 4, + (gint)REG_HDCP22_DISABLE, + (guint8 *)buf, + error)) { + g_prefix_error(error, "HDCP query failed: "); + return FALSE; + } + + buf[0] = buf[0] & (~BIT(2)); + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_MEMORY, + 4, + (gint)REG_HDCP22_DISABLE, + (guint8 *)buf, + error)) { + g_prefix_error(error, "HDCP disable failed: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_update_cayenne_firmware(FuSynapticsMstDevice *self, + guint32 payload_len, + const guint8 *payload_data, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + guint32 data_to_write = 0; + guint32 offset = 0; + guint32 write_loops = 0; + + /* sanity check */ + if (payload_len < CAYENNE_FIRMWARE_SIZE) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "payload too small, expected >=0x%x", + (guint)CAYENNE_FIRMWARE_SIZE); + return FALSE; + } + + payload_len = CAYENNE_FIRMWARE_SIZE; + write_loops = (payload_len / BLOCK_UNIT); + data_to_write = payload_len; + + if (payload_len % BLOCK_UNIT) + write_loops++; + + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + for (guint32 retries_cnt = 0;; retries_cnt++) { + guint32 checksum = 0; + guint32 flash_checksum = 0; + + if (!fu_synaptics_mst_device_set_flash_sector_erase(self, 0xffff, 0, error)) + return FALSE; + g_debug("Waiting for flash clear to settle"); + g_usleep(FLASH_SETTLE_TIME); + + fu_progress_set_steps(progress, write_loops); + 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; + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_EEPROM, + length, + offset, + payload_data + offset, + &error_local)) { + g_warning("Failed to write flash offset 0x%04x: %s, retrying", + offset, + error_local->message); + /* repeat once */ + if (!fu_synaptics_mst_connection_rc_set_command( + connection, + UPDC_WRITE_TO_EEPROM, + length, + offset, + payload_data + offset, + error)) { + g_prefix_error(error, + "can't write flash offset 0x%04x: ", + offset); + return FALSE; + } + } + offset += length; + data_to_write -= length; + fu_progress_step_done(progress); + } + + /* verify CRC */ + checksum = fu_synaptics_mst_device_get_crc(0, 16, payload_len, payload_data); + if (!fu_synaptics_mst_connection_rc_special_get_command(connection, + UPDC_CAL_EEPROM_CHECK_CRC16, + payload_len, + 0, + NULL, + 4, + (guint8 *)(&flash_checksum), + error)) { + g_prefix_error(error, "Failed to get flash checksum: "); + return FALSE; + } + if (checksum == flash_checksum) + break; + g_debug("attempt %u: checksum %x didn't match %x", + retries_cnt, + flash_checksum, + checksum); + + if (retries_cnt > MAX_RETRY_COUNTS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum %x mismatched %x", + flash_checksum, + checksum); + return FALSE; + } + } + + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_ACTIVATE_FIRMWARE, + 0, + 0, + NULL, + error)) { + g_prefix_error(error, "active firmware failed: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_restart(FuSynapticsMstDevice *self, GError **error) +{ + g_autoptr(FuSynapticsMstConnection) connection = NULL; + guint8 buf[4] = {0xF5, 0, 0, 0}; + gint offset; + g_autoptr(GError) error_local = NULL; + + switch (self->family) { + case FU_SYNAPTICS_MST_FAMILY_TESLA: + case FU_SYNAPTICS_MST_FAMILY_LEAF: + case FU_SYNAPTICS_MST_FAMILY_PANAMERA: + offset = 0x2000FC; + break; + case FU_SYNAPTICS_MST_FAMILY_CAYENNE: + case FU_SYNAPTICS_MST_FAMILY_SPYDER: + offset = 0x2020021C; + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unsupported chip family"); + return FALSE; + } + /* issue the reboot command, ignore return code (triggers before returning) */ + connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + self->layer, + self->rad); + if (!fu_synaptics_mst_connection_rc_set_command(connection, + UPDC_WRITE_TO_MEMORY, + 4, + offset, + (guint8 *)&buf, + &error_local)) + g_debug("failed to restart: %s", error_local->message); + + return TRUE; +} + +static FuFirmware * +fu_synaptics_mst_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); + g_autoptr(FuFirmware) firmware = fu_synaptics_mst_firmware_new(); + + /* check firmware and board ID match */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0 && + !fu_device_has_private_flag(device, FU_SYNAPTICS_MST_DEVICE_FLAG_IGNORE_BOARD_ID)) { + guint16 board_id = + fu_synaptics_mst_firmware_get_board_id(FU_SYNAPTICS_MST_FIRMWARE(firmware)); + if (board_id != self->board_id) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "board ID mismatch, got 0x%04x, expected 0x%04x", + board_id, + self->board_id); + return NULL; + } + } + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_synaptics_mst_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + const guint8 *payload_data; + gsize payload_len; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, NULL); + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + payload_data = g_bytes_get_data(fw, &payload_len); + + /* enable remote control and disable on exit */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART)) { + locker = + fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, + (FuDeviceLockerFunc)fu_synaptics_mst_device_restart, + error); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_set_remove_delay(FU_DEVICE(self), 10000); /* a long time */ + } else { + locker = fu_device_locker_new_full( + self, + (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, + (FuDeviceLockerFunc)fu_synaptics_mst_device_disable_rc, + error); + } + if (locker == NULL) + return FALSE; + + /* update firmware */ + switch (self->family) { + case FU_SYNAPTICS_MST_FAMILY_TESLA: + case FU_SYNAPTICS_MST_FAMILY_LEAF: + if (!fu_synaptics_mst_device_update_tesla_leaf_firmware(self, + payload_len, + payload_data, + progress, + error)) { + g_prefix_error(error, "Firmware update failed: "); + return FALSE; + } + break; + case FU_SYNAPTICS_MST_FAMILY_PANAMERA: + if (!fu_synaptics_mst_device_panamera_prepare_write(self, error)) { + g_prefix_error(error, "Failed to prepare for write: "); + return FALSE; + } + if (!fu_synaptics_mst_device_update_esm(self, payload_data, progress, error)) { + g_prefix_error(error, "ESM update failed: "); + return FALSE; + } + if (!fu_synaptics_mst_device_update_panamera_firmware(self, + payload_len, + payload_data, + progress, + error)) { + g_prefix_error(error, "Firmware update failed: "); + return FALSE; + } + break; + case FU_SYNAPTICS_MST_FAMILY_CAYENNE: + case FU_SYNAPTICS_MST_FAMILY_SPYDER: + if (!fu_synaptics_mst_device_update_cayenne_firmware(self, + payload_len, + payload_data, + progress, + error)) { + g_prefix_error(error, "Firmware update failed: "); + return FALSE; + } + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unsupported chip family"); + return FALSE; + } + fu_progress_step_done(progress); + + /* wait for flash clear to settle */ + fu_progress_sleep(fu_progress_get_child(progress), 2000); + fu_progress_step_done(progress); + return TRUE; +} + +FuSynapticsMstDevice * +fu_synaptics_mst_device_new(FuUdevDevice *device) +{ + FuSynapticsMstDevice *self = g_object_new(FU_TYPE_SYNAPTICS_MST_DEVICE, NULL); + fu_device_incorporate(FU_DEVICE(self), FU_DEVICE(device)); + return self; +} + +static gboolean +fu_synaptics_mst_device_read_board_id(FuSynapticsMstDevice *self, + FuSynapticsMstConnection *connection, + guint8 *byte, + GError **error) +{ + gint offset; + + /* in test mode we need to open a different file node instead */ + if (fu_udev_device_get_dev(FU_UDEV_DEVICE(self)) == NULL) { + g_autofree gchar *filename = NULL; + g_autofree gchar *dirname = NULL; + gint fd; + dirname = g_path_get_dirname(fu_udev_device_get_device_file(FU_UDEV_DEVICE(self))); + filename = g_strdup_printf("%s/remote/%s_eeprom", + dirname, + fu_device_get_logical_id(FU_DEVICE(self))); + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no device exists %s", + filename); + return FALSE; + } + fd = open(filename, O_RDONLY); + if (fd == -1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + "cannot open device %s", + filename); + return FALSE; + } + if (read(fd, byte, 2) != 2) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "error reading EEPROM file %s", + filename); + close(fd); + return FALSE; + } + close(fd); + return TRUE; + } + + switch (self->family) { + case FU_SYNAPTICS_MST_FAMILY_TESLA: + case FU_SYNAPTICS_MST_FAMILY_LEAF: + case FU_SYNAPTICS_MST_FAMILY_PANAMERA: + offset = (gint)ADDR_MEMORY_CUSTOMER_ID; + break; + case FU_SYNAPTICS_MST_FAMILY_CAYENNE: + case FU_SYNAPTICS_MST_FAMILY_SPYDER: + offset = (gint)ADDR_MEMORY_CUSTOMER_ID_CAYENNE; + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unsupported chip family"); + return FALSE; + } + + /* get board ID via MCU address 0x170E instead of flash access due to HDCP2.2 running */ + if (!fu_synaptics_mst_connection_rc_get_command(connection, + UPDC_READ_FROM_MEMORY, + 2, + offset, + byte, + error)) { + g_prefix_error(error, "Memory query failed: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_scan_cascade(FuSynapticsMstDevice *self, guint8 layer, GError **error) +{ + /* in test mode we skip this */ + if (fu_udev_device_get_dev(FU_UDEV_DEVICE(self)) == NULL) + return TRUE; + + /* test each relative address in this layer */ + for (guint16 rad = 0; rad <= 2; rad++) { + guint8 byte[4]; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuSynapticsMstConnection) connection = NULL; + g_autoptr(FuSynapticsMstDevice) device_tmp = NULL; + g_autoptr(GError) error_local = NULL; + + /* enable remote control and disable on exit */ + device_tmp = fu_synaptics_mst_device_new(FU_UDEV_DEVICE(self)); + device_tmp->layer = layer; + device_tmp->rad = rad; + locker = fu_device_locker_new_full( + device_tmp, + (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, + (FuDeviceLockerFunc)fu_synaptics_mst_device_disable_rc, + &error_local); + if (locker == NULL) { + g_debug("no cascade device found: %s", error_local->message); + continue; + } + connection = + fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), + layer + 1, + rad); + if (!fu_synaptics_mst_connection_read(connection, + REG_RC_CAP, + byte, + 1, + &error_local)) { + g_debug("no valid cascade device: %s", error_local->message); + continue; + } + + /* check recursively for more devices */ + if (!fu_device_locker_close(locker, &error_local)) { + g_debug("failed to close parent: %s", error_local->message); + continue; + } + self->mode = FU_SYNAPTICS_MST_MODE_REMOTE; + self->layer = layer + 1; + self->rad = rad; + if (!fu_synaptics_mst_device_scan_cascade(self, layer + 1, error)) + return FALSE; + } + return TRUE; +} + +void +fu_synaptics_mst_device_set_system_type(FuSynapticsMstDevice *self, const gchar *system_type) +{ + g_return_if_fail(FU_IS_SYNAPTICS_MST_DEVICE(self)); + self->system_type = g_strdup(system_type); +} + +static gboolean +fu_synaptics_mst_device_rescan(FuDevice *device, GError **error) +{ + FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); + guint8 buf_vid[4]; + g_autoptr(FuSynapticsMstConnection) connection = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree gchar *version = NULL; + g_autofree gchar *guid0 = NULL; + g_autofree gchar *guid1 = NULL; + g_autofree gchar *guid2 = NULL; + g_autofree gchar *guid3 = NULL; + g_autofree gchar *name = NULL; + const gchar *name_parent = NULL; + const gchar *name_family; + guint8 buf_ver[16]; + FuDevice *parent; + + /* read vendor ID */ + connection = + fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)), 0, 0); + if (!fu_synaptics_mst_connection_read(connection, REG_RC_CAP, buf_vid, 1, error)) { + g_prefix_error(error, "failed to read device: "); + return FALSE; + } + if (buf_vid[0] & 0x04) { + if (!fu_synaptics_mst_connection_read(connection, + REG_VENDOR_ID, + buf_vid, + 3, + error)) { + g_prefix_error(error, "failed to read vendor ID: "); + return FALSE; + } + /* not a correct device */ + if (buf_vid[0] != 0x90 || buf_vid[1] != 0xCC || buf_vid[2] != 0x24) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no device"); + return FALSE; + } + } + + /* direct */ + self->mode = FU_SYNAPTICS_MST_MODE_DIRECT; + self->layer = 0; + self->rad = 0; + + /* enable remote control and disable on exit */ + locker = fu_device_locker_new_full(self, + (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, + (FuDeviceLockerFunc)fu_synaptics_mst_device_disable_rc, + error); + if (locker == NULL) + return FALSE; + + /* read firmware version */ + if (!fu_synaptics_mst_connection_read(connection, REG_FIRMWARE_VERSION, buf_ver, 3, error)) + return FALSE; + + version = g_strdup_printf("%1d.%02d.%02d", buf_ver[0], buf_ver[1], buf_ver[2]); + fu_device_set_version(FU_DEVICE(self), version); + + /* read board chip_id */ + if (!fu_synaptics_mst_connection_read(connection, REG_CHIP_ID, buf_ver, 2, error)) { + g_prefix_error(error, "failed to read chip id: "); + return FALSE; + } + self->chip_id = (buf_ver[0] << 8) | (buf_ver[1]); + if (self->chip_id == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "invalid chip ID"); + return FALSE; + } + self->family = fu_synaptics_mst_family_from_chip_id(self->chip_id); + + /* VMM >= 6 use RSA2048 */ + switch (self->family) { + case FU_SYNAPTICS_MST_FAMILY_TESLA: + case FU_SYNAPTICS_MST_FAMILY_LEAF: + case FU_SYNAPTICS_MST_FAMILY_PANAMERA: + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + break; + case FU_SYNAPTICS_MST_FAMILY_CAYENNE: + case FU_SYNAPTICS_MST_FAMILY_SPYDER: + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + break; + default: + g_warning("family 0x%02x does not indicate unsigned/signed payload", self->family); + break; + } + + /* check the active bank for debugging */ + if (self->family == FU_SYNAPTICS_MST_FAMILY_PANAMERA) { + if (!fu_synaptics_mst_device_get_active_bank_panamera(self, error)) + return FALSE; + } + + /* read board ID */ + if (!fu_synaptics_mst_device_read_board_id(self, connection, buf_ver, error)) + return FALSE; + self->board_id = fu_memread_uint16(buf_ver, G_BIG_ENDIAN); + + /* recursively look for cascade devices */ + if (!fu_device_locker_close(locker, error)) { + g_prefix_error(error, "failed to close parent: "); + return FALSE; + } + if (!fu_synaptics_mst_device_scan_cascade(self, 0, error)) + return FALSE; + + /* set up the device name and kind via quirks */ + guid0 = g_strdup_printf("MST-%u", self->board_id); + fu_device_add_instance_id(FU_DEVICE(self), guid0); + parent = fu_device_get_parent(FU_DEVICE(self)); + if (parent != NULL) + name_parent = fu_device_get_name(parent); + if (name_parent != NULL) { + name = g_strdup_printf("VMM%04x inside %s", self->chip_id, name_parent); + } else { + name = g_strdup_printf("VMM%04x", self->chip_id); + } + fu_device_set_name(FU_DEVICE(self), name); + + /* this is a host system, use system ID */ + name_family = fu_synaptics_mst_family_to_string(self->family); + if (g_strcmp0(self->device_kind, "system") == 0) { + g_autofree gchar *guid = NULL; + guid = + g_strdup_printf("MST-%s-%s-%u", name_family, self->system_type, self->board_id); + fu_device_add_instance_id(FU_DEVICE(self), guid); + + /* docks or something else */ + } else if (self->device_kind != NULL) { + g_auto(GStrv) templates = NULL; + templates = g_strsplit(self->device_kind, ",", -1); + for (guint i = 0; templates[i] != NULL; i++) { + g_autofree gchar *dock_id1 = NULL; + g_autofree gchar *dock_id2 = NULL; + dock_id1 = g_strdup_printf("MST-%s-%u", templates[i], self->board_id); + fu_device_add_instance_id(FU_DEVICE(self), dock_id1); + dock_id2 = g_strdup_printf("MST-%s-vmm%04x-%u", + templates[i], + self->chip_id, + self->board_id); + fu_device_add_instance_id(FU_DEVICE(self), dock_id2); + } + } else { + /* devices are explicit opt-in */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ignoring %s device with no SynapticsMstDeviceKind quirk", + guid0); + return FALSE; + } + + /* detect chip family */ + switch (self->family) { + case FU_SYNAPTICS_MST_FAMILY_TESLA: + fu_device_set_firmware_size_max(device, 0x10000); + fu_device_add_instance_id_full(device, + "MST-tesla", + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + break; + case FU_SYNAPTICS_MST_FAMILY_LEAF: + fu_device_set_firmware_size_max(device, 0x10000); + fu_device_add_instance_id_full(device, + "MST-leaf", + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + break; + case FU_SYNAPTICS_MST_FAMILY_PANAMERA: + fu_device_set_firmware_size_max(device, 0x80000); + fu_device_add_instance_id_full(device, + "MST-panamera", + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + break; + default: + break; + } + + /* add non-standard GUIDs */ + guid1 = g_strdup_printf("MST-%s-vmm%04x-%u", name_family, self->chip_id, self->board_id); + fu_device_add_instance_id(FU_DEVICE(self), guid1); + guid2 = g_strdup_printf("MST-%s-%u", name_family, self->board_id); + fu_device_add_instance_id(FU_DEVICE(self), guid2); + guid3 = g_strdup_printf("MST-%s", name_family); + fu_device_add_instance_id(FU_DEVICE(self), guid3); + + /* this is not a valid customer ID */ + if ((self->board_id >> 8) == 0x0) { + fu_device_inhibit(device, + "invalid-customer-id", + "cannot update as CustomerID is unset"); + } + return TRUE; +} + +static gboolean +fu_synaptics_mst_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); + if (g_strcmp0(key, "SynapticsMstDeviceKind") == 0) { + self->device_kind = g_strdup(value); + return TRUE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_synaptics_mst_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_synaptics_mst_device_class_init(FuSynapticsMstDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_synaptics_mst_device_finalize; + klass_device->to_string = fu_synaptics_mst_device_to_string; + klass_device->set_quirk_kv = fu_synaptics_mst_device_set_quirk_kv; + klass_device->rescan = fu_synaptics_mst_device_rescan; + klass_device->write_firmware = fu_synaptics_mst_device_write_firmware; + klass_device->prepare_firmware = fu_synaptics_mst_device_prepare_firmware; + klass_device->probe = fu_synaptics_mst_device_probe; + klass_device->set_progress = fu_synaptics_mst_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-device.h b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-device.h new file mode 100644 index 0000000000000000000000000000000000000000..3146e144ab6bd4ac9e646841e26232d48a0dc36d --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_MST_DEVICE (fu_synaptics_mst_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsMstDevice, + fu_synaptics_mst_device, + FU, + SYNAPTICS_MST_DEVICE, + FuUdevDevice) + +FuSynapticsMstDevice * +fu_synaptics_mst_device_new(FuUdevDevice *device); +void +fu_synaptics_mst_device_set_system_type(FuSynapticsMstDevice *self, const gchar *system_type); diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-firmware.c b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..7df7ef5802ffd96cab4b612562d71619bc6f079e --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-firmware.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-mst-connection.h" +#include "fu-synaptics-mst-firmware.h" + +struct _FuSynapticsMstFirmware { + FuFirmwareClass parent_instance; + guint16 board_id; +}; + +G_DEFINE_TYPE(FuSynapticsMstFirmware, fu_synaptics_mst_firmware, FU_TYPE_FIRMWARE) + +guint16 +fu_synaptics_mst_firmware_get_board_id(FuSynapticsMstFirmware *self) +{ + g_return_val_if_fail(FU_IS_SYNAPTICS_MST_FIRMWARE(self), 0); + return self->board_id; +} + +static void +fu_synaptics_mst_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "board_id", self->board_id); +} + +static gboolean +fu_synaptics_mst_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware); + const guint8 *buf; + gsize bufsz; + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_memread_uint16_safe(buf, + bufsz, + ADDR_CUSTOMER_ID, + &self->board_id, + G_BIG_ENDIAN, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static GBytes * +fu_synaptics_mst_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + + /* assumed header */ + fu_byte_array_set_size(buf, ADDR_CUSTOMER_ID + sizeof(guint16), 0x00); + if (!fu_memwrite_uint16_safe(buf->data, + buf->len, + ADDR_CUSTOMER_ID, + fu_firmware_get_idx(firmware), + G_BIG_ENDIAN, + error)) + return NULL; + + /* payload */ + blob = fu_firmware_get_bytes_with_patches(firmware, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(buf, blob); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_synaptics_rmi_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "board_id", NULL); + if (tmp != G_MAXUINT64) + self->board_id = tmp; + + /* success */ + return TRUE; +} + +static void +fu_synaptics_mst_firmware_init(FuSynapticsMstFirmware *self) +{ +} + +static void +fu_synaptics_mst_firmware_class_init(FuSynapticsMstFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_synaptics_mst_firmware_parse; + klass_firmware->export = fu_synaptics_mst_firmware_export; + klass_firmware->write = fu_synaptics_mst_firmware_write; + klass_firmware->build = fu_synaptics_rmi_firmware_build; +} + +FuFirmware * +fu_synaptics_mst_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_MST_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-firmware.h b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..e50f407af46f7594122f5dc58d4ad6a6e12517ae --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-firmware.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_MST_FIRMWARE (fu_synaptics_mst_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsMstFirmware, + fu_synaptics_mst_firmware, + FU, + SYNAPTICS_MST_FIRMWARE, + FuFirmware) + +FuFirmware * +fu_synaptics_mst_firmware_new(void); +guint16 +fu_synaptics_mst_firmware_get_board_id(FuSynapticsMstFirmware *self); diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-plugin.c b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..1c77593654c7822ccc517aba4afb7f1b5e8e2898 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-plugin.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-mst-common.h" +#include "fu-synaptics-mst-device.h" +#include "fu-synaptics-mst-firmware.h" +#include "fu-synaptics-mst-plugin.h" + +#define FU_SYNAPTICS_MST_DRM_REPLUG_DELAY 5 /* s */ + +struct _FuSynapticsMstPlugin { + FuPlugin parent_instance; + GPtrArray *devices; + guint drm_changed_id; +}; + +G_DEFINE_TYPE(FuSynapticsMstPlugin, fu_synaptics_mst_plugin, FU_TYPE_PLUGIN) + +static void +fu_synaptics_mst_plugin_device_rescan(FuSynapticsMstPlugin *self, FuDevice *device) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; + + /* open fd */ + locker = fu_device_locker_new(device, &error_local); + if (locker == NULL) { + g_debug("failed to open device %s: %s", + fu_device_get_logical_id(device), + error_local->message); + return; + } + if (!fu_device_rescan(device, &error_local)) { + g_debug("no device found on %s: %s", + fu_device_get_logical_id(device), + error_local->message); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)) + fu_plugin_device_remove(FU_PLUGIN(self), device); + } else { + fu_plugin_device_add(FU_PLUGIN(self), device); + } +} + +/* reprobe all existing devices added by this plugin */ +static void +fu_synaptics_mst_plugin_rescan(FuSynapticsMstPlugin *self) +{ + for (guint i = 0; i < self->devices->len; i++) { + FuDevice *device = FU_DEVICE(g_ptr_array_index(self->devices, i)); + fu_synaptics_mst_plugin_device_rescan(self, device); + } +} + +static gboolean +fu_synaptics_mst_plugin_rescan_cb(gpointer user_data) +{ + FuSynapticsMstPlugin *self = FU_SYNAPTICS_MST_PLUGIN(user_data); + fu_synaptics_mst_plugin_rescan(self); + self->drm_changed_id = 0; + return FALSE; +} + +static gboolean +fu_synaptics_mst_plugin_backend_device_changed(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuSynapticsMstPlugin *self = FU_SYNAPTICS_MST_PLUGIN(plugin); + + /* interesting device? */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "drm") != 0) + return TRUE; + + /* recoldplug all drm_dp_aux_dev devices after a *long* delay */ + if (self->drm_changed_id != 0) + g_source_remove(self->drm_changed_id); + self->drm_changed_id = g_timeout_add_seconds(FU_SYNAPTICS_MST_DRM_REPLUG_DELAY, + fu_synaptics_mst_plugin_rescan_cb, + plugin); + return TRUE; +} + +static gboolean +fu_synaptics_mst_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuSynapticsMstPlugin *self = FU_SYNAPTICS_MST_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuSynapticsMstDevice) dev = NULL; + + /* interesting device? */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + + dev = fu_synaptics_mst_device_new(FU_UDEV_DEVICE(device)); + locker = fu_device_locker_new(dev, error); + if (locker == NULL) + return FALSE; + + /* for SynapticsMstDeviceKind=system devices */ + fu_synaptics_mst_device_set_system_type( + FU_SYNAPTICS_MST_DEVICE(dev), + fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_PRODUCT_SKU)); + + /* this might fail if there is nothing connected */ + fu_synaptics_mst_plugin_device_rescan(self, FU_DEVICE(dev)); + g_ptr_array_add(self->devices, g_steal_pointer(&dev)); + return TRUE; +} + +static gboolean +fu_synaptics_mst_plugin_write_firmware(FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_device_write_firmware(device, blob_fw, progress, flags, error)) + return FALSE; + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART)) + fu_plugin_device_remove(plugin, device); + return TRUE; +} + +static void +fu_synaptics_mst_plugin_init(FuSynapticsMstPlugin *self) +{ + /* devices added by this plugin */ + self->devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_synaptics_mst_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "SynapticsMstDeviceKind"); + fu_plugin_add_udev_subsystem(plugin, "drm"); /* used for uevent only */ + fu_plugin_add_udev_subsystem(plugin, "drm_dp_aux_dev"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_MST_FIRMWARE); +} + +static void +fu_synaptics_mst_finalize(GObject *obj) +{ + FuSynapticsMstPlugin *self = FU_SYNAPTICS_MST_PLUGIN(obj); + if (self->drm_changed_id != 0) + g_source_remove(self->drm_changed_id); + g_ptr_array_unref(self->devices); + G_OBJECT_CLASS(fu_synaptics_mst_plugin_parent_class)->finalize(obj); +} + +static void +fu_synaptics_mst_plugin_class_init(FuSynapticsMstPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_synaptics_mst_plugin_constructed; + object_class->finalize = fu_synaptics_mst_finalize; + plugin_class->write_firmware = fu_synaptics_mst_plugin_write_firmware; + plugin_class->backend_device_added = fu_synaptics_mst_plugin_backend_device_added; + plugin_class->backend_device_changed = fu_synaptics_mst_plugin_backend_device_changed; +} diff --git a/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-plugin.h b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..1800baba281f6a666435cb4c43397c6e3fb44066 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/fu-synaptics-mst-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSynapticsMstPlugin, + fu_synaptics_mst_plugin, + FU, + SYNAPTICS_MST_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/synaptics-mst/meson.build b/fwupd-1.8.6/plugins/synaptics-mst/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..01b7a49f33e68efa6c9024ab619f6b2b4caf8cc7 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/meson.build @@ -0,0 +1,56 @@ +if get_option('plugin_synaptics_mst').require(gudev.found(), + error_message: 'gudev is needed for plugin_synaptics_mst').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsMST"'] + +plugin_quirks += files('synaptics-mst.quirk') + +plugin_builtin_synaptics_mst = static_library('fu_plugin_synaptics_mst', + sources: [ + 'fu-synaptics-mst-plugin.c', + 'fu-synaptics-mst-common.c', + 'fu-synaptics-mst-connection.c', + 'fu-synaptics-mst-device.c', + 'fu-synaptics-mst-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + c_args: [ + cargs, + ], + link_with: plugin_libs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_synaptics_mst + +if get_option('tests') + install_data(['tests/synaptics-mst.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + env.set('FWUPD_DATADIR_QUIRKS', meson.current_source_dir()) + e = executable( + 'synaptics-mst-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + valgrind, + ], + link_with: [ + fwupd, + fwupdplugin, + plugin_builtin_synaptics_mst, + ], + c_args: [ + cargs, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('synaptics-mst-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/synaptics-mst/synaptics-mst-evb.quirk b/fwupd-1.8.6/plugins/synaptics-mst/synaptics-mst-evb.quirk new file mode 100644 index 0000000000000000000000000000000000000000..91b825b8cd8a1883dc3012d314f964d4a1fcb8c5 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/synaptics-mst-evb.quirk @@ -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. +# + +[MST-2] +Name = Synaptics EVB development board +SynapticsMstDeviceKind = panamera_evb + +[MST-panamera_evb-vmm5331-2] +Flags = ignore-board-id diff --git a/fwupd-1.8.6/plugins/synaptics-mst/synaptics-mst.quirk b/fwupd-1.8.6/plugins/synaptics-mst/synaptics-mst.quirk new file mode 100644 index 0000000000000000000000000000000000000000..678c201a1752c47db485639ca285c6ee39ec9bed --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-mst/synaptics-mst.quirk @@ -0,0 +1,54 @@ +# match all devices with this udev subsystem +[DRM_DP_AUX_DEV] +Plugin = synaptics_mst + +# GUID generation for Synaptics MST plugin +# +# SYNAPTICSMST\BID is the 16 bit board ID which contains: +# * Customer ID in first byte +# * Board ID in the second byte +# +# SynapticsMstDeviceKind = system +# * Will map to a GUID containing HwID product SKU +# * These GUIDs will look like MST-${PRODUCTSKU}-${BOARDID} +# SynapticsMstDeviceKind != system +# * Will map to a GUID containing each comma delimited 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 FWUPD_DEVICE_FLAG_SKIPS_RESTART +# + +[MST-272] +Name = Dell X6 Platform +SynapticsMstDeviceKind = system + +[MST-273] +Name = Dell X7 Platform +SynapticsMstDeviceKind = system + +[MST-274] +Name = Dell WD15/TB16/TB18 wired Dock +SynapticsMstDeviceKind = wd15,tb16,tb18 + +[MST-275] +Name = Dell WLD15 Wireless Dock +SynapticsMstDeviceKind = wld15 + +[MST-277] +Name = Dell Rugged Platform +SynapticsMstDeviceKind = system + +# ThinkPad Workstation Dock +[MST-513] +ParentGuid = USB\VID_17EF&PID_305A +SynapticsMstDeviceKind = dock + +# ThinkPad Thunderbolt 3 Workstation Dock +[MST-595] +ParentGuid = TBT-01081720 +SynapticsMstDeviceKind = dock + +[MST-596] +Name = ThinkPad USB-C Dock Gen2 +SynapticsMstDeviceKind = dock diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/README.md b/fwupd-1.8.6/plugins/synaptics-prometheus/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b452f3195eae5917114f6527c7b5309f27dd9235 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/README.md @@ -0,0 +1,38 @@ +# 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` +* `USB\VID_06CB&PID_00A9-cfg` +* `USB\VID_06CB&PID_00A9&CFG1_3483&CFG2_500` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with +the same USB PID in an unlocked mode. On attach the device again re-enumerates +back to the runtime locked mode. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x06CB` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/data/lsusb.txt b/fwupd-1.8.6/plugins/synaptics-prometheus/data/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..13016c09921bb159b3484389e5dd6ed57e357f70 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/data/lsusb.txt @@ -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) diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-self-test.c b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..9fc9b7ff2cf9b9a96f111ddba2ff20a45b4f61a5 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-self-test.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-plugin-private.h" +#include "fu-synaprom-device.h" +#include "fu-synaprom-firmware.h" + +static void +fu_test_synaprom_firmware_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + const guint8 *buf; + gboolean ret; + 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(FuFirmware) firmware2 = NULL; + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new(); + + filename = g_test_build_filename(G_TEST_DIST, "tests", "test.pkg", NULL); + if (!g_file_test(filename, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing test.pkg"); + return; + } + fw = fu_bytes_get_contents(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); + ret = fu_firmware_parse(firmware, fw, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* does not exist */ + blob1 = fu_firmware_get_image_by_id_bytes(firmware, "NotGoingToExist", NULL); + g_assert_null(blob1); + blob1 = fu_firmware_get_image_by_id_bytes(firmware, "cfg-update-header", NULL); + g_assert_null(blob1); + + /* header needs to exist */ + blob1 = fu_firmware_get_image_by_id_bytes(firmware, "mfw-update-header", &error); + g_assert_no_error(error); + g_assert_nonnull(blob1); + buf = g_bytes_get_data(blob1, &sz); + 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); + firmware2 = + fu_synaprom_device_prepare_fw(FU_DEVICE(device), fw, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_nonnull(firmware2); + blob2 = fu_firmware_get_image_by_id_bytes(firmware2, "mfw-update-payload", &error); + g_assert_no_error(error); + g_assert_nonnull(blob2); + buf = g_bytes_get_data(blob2, &sz); + g_assert_cmpint(sz, ==, 2); + g_assert_cmpint(buf[0], ==, 'R'); + g_assert_cmpint(buf[1], ==, 'H'); +} + +static void +fu_synaprom_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_synaprom_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_synaprom_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = + g_test_build_filename(G_TEST_DIST, "tests", "synaptics-prometheus.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + 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); + g_test_add_func("/synaprom/firmware{xml}", fu_synaprom_firmware_xml_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-common.c b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-common.c new file mode 100644 index 0000000000000000000000000000000000000000..05c0000998641d47b25bf000175ea10158183497 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-common.c @@ -0,0 +1,97 @@ +/* + * 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(); + fu_byte_array_append_uint8(blob, cmd); + 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(); + fu_byte_array_set_size(blob, cmdlen, 0x00); + 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 --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-common.h b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-common.h new file mode 100644 index 0000000000000000000000000000000000000000..e46b3ee6a84d0b31680988f82e2bc064e18d4019 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-common.h @@ -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 --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-config.c b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-config.c new file mode 100644 index 0000000000000000000000000000000000000000..b810560f28b7f4caaaacd79cb9ce852685ed7d44 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-config.c @@ -0,0 +1,306 @@ +/* + * 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; + 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; + +/* le */ +typedef struct __attribute__((packed)) { + guint32 product; + guint32 id1; /* verification ID */ + guint32 id2; /* verification ID */ + guint16 version; /* config version */ + guint8 unused[2]; +} FuSynapromFirmwareCfgHeader; + +#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) + +static gboolean +fu_synaprom_config_setup(FuDevice *device, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + FuSynapromConfig *self = FU_SYNAPROM_CONFIG(device); + FuSynapromCmdIotaFind cmd = {0x0}; + FuSynapromIotaConfigVersion cfg; + FuSynapromReplyIotaFindHdr hdr; + g_autofree gchar *configid1_str = NULL; + g_autofree gchar *configid2_str = NULL; + g_autofree gchar *version = NULL; + g_autoptr(GByteArray) reply = NULL; + g_autoptr(GByteArray) request = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* 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(FU_SYNAPROM_DEVICE(parent), + request, + reply, + progress, + 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; + } + if (!fu_memcpy_safe((guint8 *)&cfg, + sizeof(cfg), + 0x0, /* dst */ + reply->data, + reply->len, + sizeof(hdr), /* src */ + sizeof(cfg), + error)) + return FALSE; + self->configid1 = GUINT32_FROM_LE(cfg.config_id1); + self->configid2 = GUINT32_FROM_LE(cfg.config_id2); + g_debug("id1=%u, id2=%u, ver=%u", + self->configid1, + self->configid2, + GUINT16_FROM_LE(cfg.version)); + + /* we should have made these a %08% uint32_t... */ + configid1_str = g_strdup_printf("%u", self->configid1); + configid2_str = g_strdup_printf("%u", self->configid2); + + /* append the configid to the generated GUID */ + fu_device_add_instance_str(device, "CFG1", configid1_str); + fu_device_add_instance_str(device, "CFG2", configid2_str); + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "CFG1", "CFG2", NULL)) + return FALSE; + + /* no downgrades are allowed */ + version = g_strdup_printf("%04u", GUINT16_FROM_LE(cfg.version)); + fu_device_set_version(FU_DEVICE(self), version); + fu_device_set_version_lowest(FU_DEVICE(self), version); + return TRUE; +} + +static FuFirmware * +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(FuFirmware) firmware = fu_synaprom_firmware_new(); + guint32 product; + guint32 id1; + guint32 id2; + + /* parse the firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check the update header product and version */ + blob = fu_firmware_get_image_by_id_bytes(firmware, "cfg-update-header", error); + if (blob == NULL) + return NULL; + if (g_bytes_get_size(blob) != sizeof(hdr)) { + 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_IGNORE_VID_PID) { + 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); + id2 = GUINT32_FROM_LE(hdr.id2); + if (id1 != self->configid1 || id2 != self->configid2) { + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { + g_warning("CFG version not compatible, " + "got %u:%u expected %u:%u", + id1, + id2, + self->configid1, + self->configid2); + } else { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "CFG version not compatible, " + "got %u:%u expected %u:%u", + id1, + id2, + self->configid1, + self->configid2); + return NULL; + } + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_synaprom_config_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_image_by_id_bytes(firmware, "cfg-update-payload", error); + if (fw == NULL) + return FALSE; + + /* I assume the CFG/MFW difference is detected in the device...*/ + return fu_synaprom_device_write_fw(FU_SYNAPROM_DEVICE(parent), fw, progress, error); +} + +static void +fu_synaprom_config_init(FuSynapromConfig *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.prometheus.config"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_logical_id(FU_DEVICE(self), "cfg"); + fu_device_set_name(FU_DEVICE(self), "Prometheus IOTA Config"); + fu_device_set_summary(FU_DEVICE(self), "Fingerprint reader config"); + fu_device_add_icon(FU_DEVICE(self), "touchpad-disabled"); +} + +static void +fu_synaprom_config_constructed(GObject *obj) +{ + FuSynapromConfig *self = FU_SYNAPROM_CONFIG(obj); + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); + 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(parent)), + fu_usb_device_get_pid(FU_USB_DEVICE(parent))); + fu_device_add_instance_id(FU_DEVICE(self), devid); + + G_OBJECT_CLASS(fu_synaprom_config_parent_class)->constructed(obj); +} + +static gboolean +fu_synaprom_config_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + return fu_device_attach_full(parent, progress, error); +} + +static gboolean +fu_synaprom_config_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + return fu_device_detach_full(parent, progress, error); +} + +static void +fu_synaprom_config_class_init(FuSynapromConfigClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_synaprom_config_constructed; + klass_device->write_firmware = fu_synaprom_config_write_firmware; + klass_device->prepare_firmware = fu_synaprom_config_prepare_firmware; + klass_device->setup = fu_synaprom_config_setup; + klass_device->reload = fu_synaprom_config_setup; + klass_device->attach = fu_synaprom_config_attach; + klass_device->detach = fu_synaprom_config_detach; +} + +FuSynapromConfig * +fu_synaprom_config_new(FuSynapromDevice *device) +{ + FuSynapromConfig *self; + self = g_object_new(FU_TYPE_SYNAPROM_CONFIG, "parent", device, NULL); + return FU_SYNAPROM_CONFIG(self); +} diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-config.h b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-config.h new file mode 100644 index 0000000000000000000000000000000000000000..fd8b9ce5897a5703b5587a9e0e50857f4d6cde48 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-config.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-synaprom-device.h" + +#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); diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-device.c b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-device.c new file mode 100644 index 0000000000000000000000000000000000000000..058242f0c5b854c7aee085c1e09ad29e787e15f6 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-device.c @@ -0,0 +1,538 @@ +/* + * 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) + +gboolean +fu_synaprom_device_cmd_send(FuSynapromDevice *device, + GByteArray *request, + GByteArray *reply, + FuProgress *progress, + guint timeout_ms, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + gboolean ret; + gsize actual_len = 0; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 25, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 75, NULL); + + if (g_getenv("FWUPD_SYNAPROM_VERBOSE") != NULL) { + fu_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; + } + fu_progress_step_done(progress); + + 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_dump_full(G_LOG_DOMAIN, + "REPLY", + reply->data, + actual_len, + 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + fu_progress_step_done(progress); + + /* 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; + + /* We decide to skip 10.02.xxxxxx firmware, so we force the minor version from 0x02 + ** to 0x01 to make the devices with 0x02 minor version firmware allow to be updated + ** back to minor version 0x01. */ + if (vmajor == 0x0a && vminor == 0x02) { + g_debug("quirking vminor from %02x to 01", vminor); + vminor = 0x01; + } + + /* set display version */ + str = g_strdup_printf("%02u.%02u.%u", vmajor, vminor, buildnum); + fu_device_set_version(FU_DEVICE(self), str); + + /* 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; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_synaprom_device_parent_class)->setup(device, error)) + return FALSE; + + /* 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, progress, 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 (fu_device_get_children(device)->len == 0 && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + pkt.security[1] & FU_SYNAPROM_SECURITY1_PROD_SENSOR) { + g_autoptr(FuSynapromConfig) cfg = fu_synaprom_config_new(self); + fu_device_add_child(FU_DEVICE(device), FU_DEVICE(cfg)); + } + + /* success */ + return TRUE; +} + +FuFirmware * +fu_synaprom_device_prepare_fw(FuDevice *device, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + FuSynapromFirmwareMfwHeader hdr; + guint32 product; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new(); + + /* parse the firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check the update header product and version */ + blob = fu_firmware_get_image_by_id_bytes(firmware, "mfw-update-header", error); + if (blob == NULL) + return NULL; + if (g_bytes_get_size(blob) != sizeof(hdr)) { + 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_IGNORE_VID_PID) { + 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; + } + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_synaprom_device_write_chunks(FuSynapromDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + GByteArray *chunk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) request = NULL; + g_autoptr(GByteArray) reply = NULL; + + /* patch */ + request = + fu_synaprom_request_new(FU_SYNAPROM_CMD_BOOTLDR_PATCH, chunk->data, chunk->len); + reply = fu_synaprom_reply_new(sizeof(FuSynapromReplyGeneric)); + if (!fu_synaprom_device_cmd_send(self, + request, + reply, + fu_progress_get_child(progress), + 20000, + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +gboolean +fu_synaprom_device_write_fw(FuSynapromDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + gsize offset = 0; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + + /* collect chunks */ + buf = g_bytes_get_data(fw, &bufsz); + chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_byte_array_unref); + while (offset != bufsz) { + guint32 chunksz = 0; + g_autofree guint8 *chunkbuf = NULL; + g_autoptr(GByteArray) chunk = g_byte_array_new(); + + /* get chunk size */ + if (!fu_memread_uint32_safe(buf, bufsz, offset, &chunksz, G_LITTLE_ENDIAN, error)) + return FALSE; + offset += sizeof(guint32); + + /* read out chunk */ + chunkbuf = g_malloc0(chunksz); + if (!fu_memcpy_safe(chunkbuf, + chunksz, + 0x0, /* dst */ + buf, + bufsz, + offset, /* src */ + chunksz, + error)) + return FALSE; + offset += chunksz; + + /* add chunk */ + g_byte_array_append(chunk, chunkbuf, chunksz); + g_ptr_array_add(chunks, g_steal_pointer(&chunk)); + } + fu_progress_step_done(progress); + + /* write chunks */ + if (!fu_synaprom_device_write_chunks(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static gboolean +fu_synaprom_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapromDevice *self = FU_SYNAPROM_DEVICE(device); + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_image_by_id_bytes(firmware, "mfw-update-payload", error); + if (fw == NULL) + return FALSE; + + return fu_synaprom_device_write_fw(self, fw, progress, error); +} + +static gboolean +fu_synaprom_device_attach(FuDevice *device, FuProgress *progress, 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}; + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + 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; + } + if (!g_usb_device_reset(usb_device, error)) { + g_prefix_error(error, "failed to force-reset device: "); + return FALSE; + } + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_synaprom_device_detach(FuDevice *device, FuProgress *progress, 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}; + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + + 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_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); + if (!g_usb_device_reset(usb_device, error)) { + g_prefix_error(error, "failed to force-reset device: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static void +fu_synaprom_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_synaprom_device_init(FuSynapromDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_RETRY_OPEN); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.prometheus"); + 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"); + fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x0); +} + +static void +fu_synaprom_device_class_init(FuSynapromDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_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->reload = fu_synaprom_device_setup; + klass_device->attach = fu_synaprom_device_attach; + klass_device->detach = fu_synaprom_device_detach; + klass_device->set_progress = fu_synaprom_device_set_progress; +} + +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 --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-device.h b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-device.h new file mode 100644 index 0000000000000000000000000000000000000000..9f039b318f02f45dfb85fa31d9a4d740e13473a8 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-device.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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, + FuProgress *progress, + guint timeout_ms, + GError **error); +gboolean +fu_synaprom_device_write_fw(FuSynapromDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error); + +/* for self tests */ +void +fu_synaprom_device_set_version(FuSynapromDevice *self, + guint8 vmajor, + guint8 vminor, + guint32 buildnum); +FuFirmware * +fu_synaprom_device_prepare_fw(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-firmware.c b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..46e1b3a4c9708661d53b6751fd9c6b803edefcdf --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-firmware.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-synaprom-firmware.h" + +struct _FuSynapromFirmware { + FuFirmware parent_instance; + guint32 product_id; +}; + +G_DEFINE_TYPE(FuSynapromFirmware, fu_synaprom_firmware, FU_TYPE_FIRMWARE) + +#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 + +typedef struct __attribute__((packed)) { + guint16 tag; + guint32 bufsz; +} FuSynapromFirmwareHdr; + +/* use only first 12 bit of 16 bits as tag value */ +#define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0 +#define FU_SYNAPROM_FIRMWARE_SIGSIZE 0x0100 + +#define FU_SYNAPROM_FIRMWARE_COUNT_MAX 64 + +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; +} + +static void +fu_synaprom_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "product_id", self->product_id); +} + +static gboolean +fu_synaprom_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + guint img_cnt = 0; + + g_return_val_if_fail(fw != NULL, FALSE); + + buf = g_bytes_get_data(fw, &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 FALSE; + } + bufsz -= FU_SYNAPROM_FIRMWARE_SIGSIZE; + + /* parse each chunk */ + while (offset != bufsz) { + FuSynapromFirmwareHdr header; + guint32 hdrsz; + guint32 tag; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(FuFirmware) img = NULL; + + /* verify item header */ + memcpy(&header, buf, sizeof(header)); + tag = GUINT16_FROM_LE(header.tag); + if (tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "tag 0x%04x is too large", + tag); + return FALSE; + } + + /* sanity check */ + img = fu_firmware_get_image_by_idx(firmware, tag, NULL); + if (img != NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "tag 0x%04x already present in image", + tag); + return FALSE; + } + hdrsz = GUINT32_FROM_LE(header.bufsz); + if (hdrsz == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "empty header for tag 0x%04x", + tag); + return FALSE; + } + 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 FALSE; + } + + /* move pointer to data */ + buf += sizeof(header); + bytes = g_bytes_new(buf, hdrsz); + g_debug("adding 0x%04x (%s) with size 0x%04x", + tag, + fu_synaprom_firmware_tag_to_string(tag), + hdrsz); + img = fu_firmware_new_from_bytes(bytes); + fu_firmware_set_idx(img, tag); + fu_firmware_set_id(img, fu_synaprom_firmware_tag_to_string(tag)); + fu_firmware_add_image(firmware, img); + + /* sanity check */ + if (img_cnt++ > FU_SYNAPROM_FIRMWARE_COUNT_MAX) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "maximum number of images exceeded, " + "maximum is 0x%02x", + (guint)FU_SYNAPROM_FIRMWARE_COUNT_MAX); + return FALSE; + } + + /* next item */ + buf += hdrsz; + } + return TRUE; +} + +static GBytes * +fu_synaprom_firmware_write(FuFirmware *firmware, GError **error) +{ + FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); + GByteArray *blob = g_byte_array_new(); + g_autoptr(GBytes) payload = NULL; + FuSynapromFirmwareMfwHeader hdr = { + .product = GUINT32_TO_LE(self->product_id), + .id = GUINT32_TO_LE(0xff), + .buildtime = GUINT32_TO_LE(0xff), + .buildnum = GUINT32_TO_LE(0xff), + .vmajor = 10, + .vminor = 1, + }; + + /* add header */ + fu_byte_array_append_uint16(blob, FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(blob, sizeof(hdr), G_LITTLE_ENDIAN); + g_byte_array_append(blob, (const guint8 *)&hdr, sizeof(hdr)); + + /* add payload */ + payload = fu_firmware_get_bytes_with_patches(firmware, error); + if (payload == NULL) + return NULL; + fu_byte_array_append_uint16(blob, FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(blob, g_bytes_get_size(payload), G_LITTLE_ENDIAN); + fu_byte_array_append_bytes(blob, payload); + + /* add signature */ + for (guint i = 0; i < FU_SYNAPROM_FIRMWARE_SIGSIZE; i++) + fu_byte_array_append_uint8(blob, 0xff); + return g_byte_array_free_to_bytes(blob); +} + +static gboolean +fu_synaprom_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); + guint64 tmp; + + /* simple properties */ + tmp = xb_node_query_text_as_uint(n, "product_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + self->product_id = tmp; + + /* success */ + return TRUE; +} + +static void +fu_synaprom_firmware_init(FuSynapromFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_synaprom_firmware_class_init(FuSynapromFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_synaprom_firmware_parse; + klass_firmware->write = fu_synaprom_firmware_write; + klass_firmware->export = fu_synaprom_firmware_export; + klass_firmware->build = fu_synaprom_firmware_build; +} + +FuFirmware * +fu_synaprom_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPROM_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-firmware.h b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..40a741c5cb2ee89b4c1278a21848a8ed5a36ac14 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-firmware.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Synaptics Inc + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPROM_FIRMWARE (fu_synaprom_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapromFirmware, fu_synaprom_firmware, FU, SYNAPROM_FIRMWARE, FuFirmware) + +/* 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; + +FuFirmware * +fu_synaprom_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-plugin.c b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..c144096ec22f35fd063c246cfef3d28fa3951bcb --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-plugin.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaprom-device.h" +#include "fu-synaprom-firmware.h" +#include "fu-synaprom-plugin.h" + +struct _FuSynapromPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSynapromPlugin, fu_synaprom_plugin, FU_TYPE_PLUGIN) + +static void +fu_synaprom_plugin_init(FuSynapromPlugin *self) +{ +} + +static void +fu_synaprom_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "synaptics_prometheus"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPROM_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPROM_FIRMWARE); +} + +static void +fu_synaprom_plugin_class_init(FuSynapromPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_synaprom_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-plugin.h b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..276cba3935eeab87d9c53d7cdf7d10f2d8b687c0 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/fu-synaprom-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSynapromPlugin, fu_synaprom_plugin, FU, SYNAPROM_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/meson.build b/fwupd-1.8.6/plugins/synaptics-prometheus/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..9ed52ff12f882c8538a06403f0df0ffdcf41cb2b --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/meson.build @@ -0,0 +1,44 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsPrometheus"'] + +plugin_quirks += files('synaptics-prometheus.quirk') +plugin_builtin_synaprom = static_library('fu_plugin_synaprom', + sources: [ + 'fu-synaprom-plugin.c', + 'fu-synaprom-common.c', + 'fu-synaprom-config.c', + 'fu-synaprom-device.c', + 'fu-synaprom-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_synaprom + +if get_option('tests') + install_data(['tests/synaptics-prometheus.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'synaptics-prometheus-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_synaprom, + ], + c_args: cargs, + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('synaptics-prometheus-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/synaptics-prometheus/synaptics-prometheus.quirk b/fwupd-1.8.6/plugins/synaptics-prometheus/synaptics-prometheus.quirk new file mode 100644 index 0000000000000000000000000000000000000000..36a728e2d0a1ddebf1651faf99ab494cce579d50 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-prometheus/synaptics-prometheus.quirk @@ -0,0 +1,51 @@ +[USB\VID_06CB&PID_00A9] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00BD] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00DF] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00E9] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00C2] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00F9] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00FC] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00D8] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_00F0] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_0103] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_0123] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_0126] +Plugin = synaptics_prometheus +InstallDuration = 2 + +[USB\VID_06CB&PID_0129] +Plugin = synaptics_prometheus +InstallDuration = 2 diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/README.md b/fwupd-1.8.6/plugins/synaptics-rmi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5cfa08fdf32e23e3fafdae92ad437fdc48e5030a --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/README.md @@ -0,0 +1,38 @@ +# Synaptics RMI4 + +## Introduction + +This plugin updates integrated Synaptics RMI4 devices, typically touchpads. + +## GUID Generation + +The HID DeviceInstanceId values are used, e.g. `HIDRAW\VEN_06CB&DEV_4875`. + +These devices also use custom GUID values constructed using the board ID, e.g. + +* `SYNAPTICS_RMI\TM3038-002` +* `SYNAPTICS_RMI\TM3038` + +## Update Behavior + +The device usually presents in HID mode, and the firmware is written to the +device by switching to a SERIO mode where the touchpad is nonfunctional. +Once complete the device is reset to get out of SERIO mode and to load the new +firmware version. + +## Vendor ID Security + +The vendor ID is set from the udev vendor, in this instance set to `HIDRAW:0x06CB` + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a proprietary (but docucumented) file format. + +This plugin supports the following protocol ID: + +* com.synaptics.rmi + +## External Interface Access + +This plugin requires ioctl access to `HIDIOCSFEATURE` and `HIDIOCGFEATURE`. diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-self-test.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..1133802413c642fe05c303c19dc13a23beef90d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-self-test.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-rmi-firmware.h" + +static void +fu_synaptics_rmi_firmware_0x_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_synaptics_rmi_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_synaptics_rmi_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = + g_test_build_filename(G_TEST_DIST, "tests", "synaptics-rmi-0x.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "8b097c034028a69e6416bcc39f312e2fa9247381"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +static void +fu_synaptics_rmi_firmware_10_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_synaptics_rmi_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_synaptics_rmi_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = + g_test_build_filename(G_TEST_DIST, "tests", "synaptics-rmi-10.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "bd85539bb100e5bd6debb00b06b5a7e7fa9bd030"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + 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("/synaptics-rmi/firmware{0x}", fu_synaptics_rmi_firmware_0x_func); + g_test_add_func("/synaptics-rmi/firmware{10}", fu_synaptics_rmi_firmware_10_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-common.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-common.c new file mode 100644 index 0000000000000000000000000000000000000000..894290140bceb156f799f8ec66e29b52f86a1f18 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-common.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include + +#ifdef HAVE_GNUTLS +#include +#include +#endif + +#include "fu-synaptics-rmi-common.h" + +#define RMI_FUNCTION_QUERY_OFFSET 0 +#define RMI_FUNCTION_COMMAND_OFFSET 1 +#define RMI_FUNCTION_CONTROL_OFFSET 2 +#define RMI_FUNCTION_DATA_OFFSET 3 +#define RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET 4 +#define RMI_FUNCTION_NUMBER 5 + +#define RMI_FUNCTION_VERSION_MASK 0x60 +#define RMI_FUNCTION_INTERRUPT_SOURCES_MASK 0x7 + +#ifdef HAVE_GNUTLS +typedef guchar gnutls_data_t; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_data_t, gnutls_free) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pubkey_t, gnutls_pubkey_deinit, NULL) +#pragma clang diagnostic pop +#endif + +guint32 +fu_synaptics_rmi_generate_checksum(const guint8 *data, gsize len) +{ + guint32 lsw = 0xffff; + guint32 msw = 0xffff; + for (gsize i = 0; i < len / 2; i++) { + lsw += fu_memread_uint16(&data[i * 2], G_LITTLE_ENDIAN); + msw += lsw; + lsw = (lsw & 0xffff) + (lsw >> 16); + msw = (msw & 0xffff) + (msw >> 16); + } + return msw << 16 | lsw; +} + +FuSynapticsRmiFunction * +fu_synaptics_rmi_function_parse(GByteArray *buf, + guint16 page_base, + guint interrupt_count, + GError **error) +{ + FuSynapticsRmiFunction *func; + guint8 interrupt_offset; + const guint8 *data = buf->data; + + /* not expected */ + if (buf->len != RMI_DEVICE_PDT_ENTRY_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "PDT entry buffer invalid size %u != %i", + buf->len, + RMI_DEVICE_PDT_ENTRY_SIZE); + return NULL; + } + + func = g_new0(FuSynapticsRmiFunction, 1); + func->query_base = data[RMI_FUNCTION_QUERY_OFFSET] + page_base; + func->command_base = data[RMI_FUNCTION_COMMAND_OFFSET] + page_base; + func->control_base = data[RMI_FUNCTION_CONTROL_OFFSET] + page_base; + func->data_base = data[RMI_FUNCTION_DATA_OFFSET] + page_base; + func->interrupt_source_count = + data[RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET] & RMI_FUNCTION_INTERRUPT_SOURCES_MASK; + func->function_number = data[RMI_FUNCTION_NUMBER]; + func->function_version = + (data[RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET] & RMI_FUNCTION_VERSION_MASK) >> 5; + if (func->interrupt_source_count > 0) { + func->interrupt_reg_num = (interrupt_count + 8) / 8 - 1; + /* set an enable bit for each data source */ + interrupt_offset = interrupt_count % 8; + func->interrupt_mask = 0; + for (guint i = interrupt_offset; + i < (func->interrupt_source_count + interrupt_offset); + i++) + func->interrupt_mask |= 1 << i; + } + return func; +} + +gboolean +fu_synaptics_rmi_device_writeln(const gchar *fn, const gchar *buf, GError **error) +{ + int fd; + g_autoptr(FuIOChannel) io = NULL; + + fd = open(fn, O_WRONLY); + if (fd < 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "could not open %s", fn); + return FALSE; + } + io = fu_io_channel_unix_new(fd); + return fu_io_channel_write_raw(io, + (const guint8 *)buf, + strlen(buf), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error); +} + +gboolean +fu_synaptics_verify_sha256_signature(GBytes *payload, + GBytes *pubkey, + GBytes *signature, + GError **error) +{ +#ifdef HAVE_GNUTLS + gnutls_datum_t hash; + gnutls_datum_t m; + gnutls_datum_t e; + gnutls_datum_t sig; + gnutls_hash_hd_t sha2; + g_auto(gnutls_pubkey_t) pub = NULL; + gint ec; + guint8 exponent[] = {1, 0, 1}; + guint hash_length = gnutls_hash_get_len(GNUTLS_DIG_SHA256); + g_autoptr(gnutls_data_t) hash_data = NULL; + + /* hash firmware data */ + hash_data = gnutls_malloc(hash_length); + gnutls_hash_init(&sha2, GNUTLS_DIG_SHA256); + gnutls_hash(sha2, g_bytes_get_data(payload, NULL), g_bytes_get_size(payload)); + gnutls_hash_deinit(sha2, hash_data); + + /* hash */ + hash.size = hash_length; + hash.data = hash_data; + + /* modulus */ + m.size = g_bytes_get_size(pubkey); + m.data = (guint8 *)g_bytes_get_data(pubkey, NULL); + + /* exponent */ + e.size = sizeof(exponent); + e.data = exponent; + + /* signature */ + sig.size = g_bytes_get_size(signature); + sig.data = (guint8 *)g_bytes_get_data(signature, NULL); + + gnutls_pubkey_init(&pub); + ec = gnutls_pubkey_import_rsa_raw(pub, &m, &e); + if (ec < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to import RSA key: %s", + gnutls_strerror(ec)); + return FALSE; + } + ec = gnutls_pubkey_verify_hash2(pub, GNUTLS_SIGN_RSA_SHA256, 0, &hash, &sig); + if (ec < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to verify firmware: %s", + gnutls_strerror(ec)); + return FALSE; + } +#endif + /* success */ + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-common.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-common.h new file mode 100644 index 0000000000000000000000000000000000000000..7de8a94d8c8e280d03167b114e87d07fe1e2b0b3 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-common.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define RMI_PRODUCT_ID_LENGTH 10 +#define RMI_DEVICE_PDT_ENTRY_SIZE 6 + +typedef struct { + guint16 query_base; + guint16 command_base; + guint16 control_base; + guint16 data_base; + guint8 interrupt_source_count; + guint8 function_number; + guint8 function_version; + guint8 interrupt_reg_num; + guint8 interrupt_mask; +} FuSynapticsRmiFunction; + +guint32 +fu_synaptics_rmi_generate_checksum(const guint8 *data, gsize len); +FuSynapticsRmiFunction * +fu_synaptics_rmi_function_parse(GByteArray *buf, + guint16 page_base, + guint interrupt_count, + GError **error); +gboolean +fu_synaptics_rmi_device_writeln(const gchar *fn, const gchar *buf, GError **error); +gboolean +fu_synaptics_verify_sha256_signature(GBytes *payload, + GBytes *pubkey, + GBytes *signature, + GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-device.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b57b300a05bf9ada06fc3d56b1f0444a72c4715e --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-device.c @@ -0,0 +1,881 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-rmi-common.h" +#include "fu-synaptics-rmi-firmware.h" +#include "fu-synaptics-rmi-ps2-device.h" +#include "fu-synaptics-rmi-v5-device.h" +#include "fu-synaptics-rmi-v6-device.h" +#include "fu-synaptics-rmi-v7-device.h" + +#define RMI_DEVICE_PAGE_SIZE 0x100 +#define RMI_DEVICE_PAGE_SCAN_START 0x00e9 +#define RMI_DEVICE_PAGE_SCAN_END 0x0005 +#define RMI_DEVICE_F01_BASIC_QUERY_LEN 11 + +#define RMI_DEVICE_F01_LTS_RESERVED_SIZE 19 + +#define RMI_DEVICE_F01_QRY1_HAS_LTS (1 << 2) +#define RMI_DEVICE_F01_QRY1_HAS_SENSOR_ID (1 << 3) +#define RMI_DEVICE_F01_QRY1_HAS_PROPS_2 (1 << 7) + +#define RMI_DEVICE_F01_QRY42_DS4_QUERIES (1 << 0) +#define RMI_DEVICE_F01_QRY43_01_PACKAGE_ID (1 << 0) +#define RMI_DEVICE_F01_QRY43_01_BUILD_ID (1 << 1) + +#define RMI_F34_COMMAND_MASK 0x0f +#define RMI_F34_STATUS_MASK 0x07 +#define RMI_F34_STATUS_SHIFT 4 +#define RMI_F34_ENABLED_MASK 0x80 + +#define RMI_F34_COMMAND_V1_MASK 0x3f +#define RMI_F34_STATUS_V1_MASK 0x3f +#define RMI_F34_ENABLED_V1_MASK 0x80 + +#define RMI_F01_CMD_DEVICE_RESET 1 +#define RMI_F01_DEFAULT_RESET_DELAY_MS 100 + +typedef struct { + FuSynapticsRmiFlash flash; + GPtrArray *functions; + FuSynapticsRmiFunction *f01; + FuSynapticsRmiFunction *f34; + guint8 current_page; + guint16 sig_size; /* 0x0 for non-secure update */ + guint8 max_page; + gboolean in_iep_mode; +} FuSynapticsRmiDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuSynapticsRmiDevice, fu_synaptics_rmi_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_synaptics_rmi_device_get_instance_private(o)) + +FuSynapticsRmiFlash * +fu_synaptics_rmi_device_get_flash(FuSynapticsRmiDevice *self) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + return &priv->flash; +} + +static void +fu_synaptics_rmi_flash_to_string(FuSynapticsRmiFlash *flash, guint idt, GString *str) +{ + if (flash->bootloader_id[0] != 0x0) { + g_autofree gchar *tmp = + g_strdup_printf("%02x.%02x", flash->bootloader_id[0], flash->bootloader_id[1]); + fu_string_append(str, idt, "BootloaderId", tmp); + } + fu_string_append_kx(str, idt, "BlockSize", flash->block_size); + fu_string_append_kx(str, idt, "BlockCountFw", flash->block_count_fw); + fu_string_append_kx(str, idt, "BlockCountCfg", flash->block_count_cfg); + fu_string_append_kx(str, idt, "FlashConfigLength", flash->config_length); + fu_string_append_kx(str, idt, "PayloadLength", flash->payload_length); + fu_string_append_kx(str, idt, "BuildID", flash->build_id); +} + +static void +fu_synaptics_rmi_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_synaptics_rmi_device_parent_class)->to_string(device, idt, str); + + fu_string_append_kx(str, idt, "CurrentPage", priv->current_page); + fu_string_append_kx(str, idt, "InIepMode", priv->in_iep_mode); + fu_string_append_kx(str, idt, "MaxPage", priv->max_page); + fu_string_append_kx(str, idt, "SigSize", priv->sig_size); + if (priv->f34 != NULL) { + fu_string_append_kx(str, idt, "BlVer", priv->f34->function_version + 0x5); + } + fu_synaptics_rmi_flash_to_string(&priv->flash, idt, str); +} + +FuSynapticsRmiFunction * +fu_synaptics_rmi_device_get_function(FuSynapticsRmiDevice *self, + guint8 function_number, + GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + if (priv->functions->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no RMI functions, perhaps read the PDT?"); + return NULL; + } + for (guint i = 0; i < priv->functions->len; i++) { + FuSynapticsRmiFunction *func = g_ptr_array_index(priv->functions, i); + if (func->function_number == function_number) + return func; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get RMI function 0x%02x", + function_number); + return NULL; +} + +GByteArray * +fu_synaptics_rmi_device_read(FuSynapticsRmiDevice *self, guint16 addr, gsize req_sz, GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + return klass_rmi->read(self, addr, req_sz, error); +} + +GByteArray * +fu_synaptics_rmi_device_read_packet_register(FuSynapticsRmiDevice *self, + guint16 addr, + gsize req_sz, + GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + if (klass_rmi->read_packet_register == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "packet register reads not supported"); + return NULL; + } + return klass_rmi->read_packet_register(self, addr, req_sz, error); +} + +gboolean +fu_synaptics_rmi_device_write(FuSynapticsRmiDevice *self, + guint16 addr, + GByteArray *req, + FuSynapticsRmiDeviceFlags flags, + GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + return klass_rmi->write(self, addr, req, flags, error); +} + +gboolean +fu_synaptics_rmi_device_set_page(FuSynapticsRmiDevice *self, guint8 page, GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + if (priv->current_page == page) + return TRUE; + if (!klass_rmi->set_page(self, page, error)) + return FALSE; + priv->current_page = page; + return TRUE; +} + +void +fu_synaptics_rmi_device_set_iepmode(FuSynapticsRmiDevice *self, gboolean iepmode) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + priv->in_iep_mode = iepmode; +} + +gboolean +fu_synaptics_rmi_device_get_iepmode(FuSynapticsRmiDevice *self) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + return priv->in_iep_mode; +} + +gboolean +fu_synaptics_rmi_device_write_bus_select(FuSynapticsRmiDevice *self, guint8 bus, GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + if (klass_rmi->write_bus_select == NULL) + return TRUE; + return klass_rmi->write_bus_select(self, bus, error); +} + +gboolean +fu_synaptics_rmi_device_reset(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) req = g_byte_array_new(); + + fu_byte_array_append_uint8(req, RMI_F01_CMD_DEVICE_RESET); + if (!fu_synaptics_rmi_device_write(self, + priv->f01->command_base, + req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE, + error)) + return FALSE; + g_usleep(1000 * RMI_F01_DEFAULT_RESET_DELAY_MS); + return TRUE; +} + +static gboolean +fu_synaptics_rmi_device_scan_pdt(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + guint interrupt_count = 0; + + /* clear old list */ + g_ptr_array_set_size(priv->functions, 0); + + /* scan pages */ + for (guint page = 0; page < priv->max_page; page++) { + gboolean found = FALSE; + guint32 page_start = RMI_DEVICE_PAGE_SIZE * page; + guint32 pdt_start = page_start + RMI_DEVICE_PAGE_SCAN_START; + guint32 pdt_end = page_start + RMI_DEVICE_PAGE_SCAN_END; + + /* set page */ + if (!fu_synaptics_rmi_device_set_page(self, page, error)) + return FALSE; + + /* read out functions */ + for (guint addr = pdt_start; addr >= pdt_end; addr -= RMI_DEVICE_PDT_ENTRY_SIZE) { + g_autofree FuSynapticsRmiFunction *func = NULL; + g_autoptr(GByteArray) res = NULL; + res = fu_synaptics_rmi_device_read(self, + addr, + RMI_DEVICE_PDT_ENTRY_SIZE, + error); + if (res == NULL) { + g_prefix_error(error, + "failed to read page %u PDT entry @ 0x%04x: ", + page, + addr); + return FALSE; + } + func = fu_synaptics_rmi_function_parse(res, + page_start, + interrupt_count, + error); + if (func == NULL) + return FALSE; + if (func->function_number == 0) + break; + interrupt_count += func->interrupt_source_count; + g_ptr_array_add(priv->functions, g_steal_pointer(&func)); + found = TRUE; + } + if (!found) + break; + } + + /* for debug */ + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + for (guint i = 0; i < priv->functions->len; i++) { + FuSynapticsRmiFunction *func = g_ptr_array_index(priv->functions, i); + g_debug("PDT-%02u fn:0x%02x vr:%d sc:%d ms:0x%x " + "db:0x%02x cb:0x%02x cm:0x%02x qb:0x%02x", + i, + func->function_number, + func->function_version, + func->interrupt_source_count, + func->interrupt_mask, + func->data_base, + func->control_base, + func->command_base, + func->query_base); + } + } + + /* success */ + return TRUE; +} + +void +fu_synaptics_rmi_device_set_sig_size(FuSynapticsRmiDevice *self, guint16 sig_size) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + priv->sig_size = sig_size; +} + +guint16 +fu_synaptics_rmi_device_get_sig_size(FuSynapticsRmiDevice *self) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + return priv->sig_size; +} + +void +fu_synaptics_rmi_device_set_max_page(FuSynapticsRmiDevice *self, guint8 max_page) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + priv->max_page = max_page; +} + +guint8 +fu_synaptics_rmi_device_get_max_page(FuSynapticsRmiDevice *self) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + return priv->max_page; +} + +static void +fu_synaptics_rmi_device_set_product_id(FuSynapticsRmiDevice *self, const gchar *product_id) +{ + g_autofree gchar *instance_id = NULL; + g_auto(GStrv) product_id_split = g_strsplit(product_id, "-", 2); + + /* use the product ID as an instance ID */ + instance_id = g_strdup_printf("SYNAPTICS_RMI\\%s", product_id); + fu_device_add_instance_id(FU_DEVICE(self), instance_id); + + /* also add the product ID without the sub-number */ + if (g_strv_length(product_id_split) == 2) { + g_autofree gchar *instance_id_major = NULL; + instance_id_major = g_strdup_printf("SYNAPTICS_RMI\\%s", product_id_split[0]); + fu_device_add_instance_id(FU_DEVICE(self), instance_id_major); + } +} + +static gboolean +fu_synaptics_rmi_device_query_status(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + return klass_rmi->query_status(self, error); +} + +static gboolean +fu_synaptics_rmi_device_query_build_id(FuSynapticsRmiDevice *self, + guint32 *build_id, + GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + if (klass_rmi->query_build_id == NULL) + return TRUE; + return klass_rmi->query_build_id(self, build_id, error); +} + +static gboolean +fu_synaptics_rmi_device_query_product_sub_id(FuSynapticsRmiDevice *self, + guint8 *product_sub_id, + GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + if (klass_rmi->query_product_sub_id == NULL) + return TRUE; + return klass_rmi->query_product_sub_id(self, product_sub_id, error); +} + +static gboolean +fu_synaptics_rmi_device_setup(FuDevice *device, GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + guint16 addr; + guint16 prod_info_addr; + guint8 ds4_query_length = 0; + guint8 product_sub_id = 0; + gboolean has_build_id_query = FALSE; + gboolean has_dds4_queries = FALSE; + gboolean has_lts; + gboolean has_package_id_query = FALSE; + gboolean has_query42; + gboolean has_sensor_id; + g_autofree gchar *bl_ver = NULL; + g_autofree gchar *fw_ver = NULL; + g_autofree gchar *product_id = NULL; + g_autoptr(GByteArray) f01_basic = NULL; + g_autoptr(GByteArray) f01_product_id = NULL; + g_autoptr(GByteArray) f01_ds4 = NULL; + + /* assume reset */ + priv->in_iep_mode = FALSE; + + /* read PDT */ + if (!fu_synaptics_rmi_device_scan_pdt(self, error)) + return FALSE; + priv->f01 = fu_synaptics_rmi_device_get_function(self, 0x01, error); + if (priv->f01 == NULL) + return FALSE; + addr = priv->f01->query_base; + + /* set page */ + if (!fu_synaptics_rmi_device_set_page(self, 0, error)) + return FALSE; + + /* force entering iep mode again */ + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + + f01_basic = fu_synaptics_rmi_device_read(self, addr, RMI_DEVICE_F01_BASIC_QUERY_LEN, error); + if (f01_basic == NULL) { + g_prefix_error(error, "failed to read the basic query: "); + return FALSE; + } + has_lts = (f01_basic->data[1] & RMI_DEVICE_F01_QRY1_HAS_LTS) > 0; + has_sensor_id = (f01_basic->data[1] & RMI_DEVICE_F01_QRY1_HAS_SENSOR_ID) > 0; + has_query42 = (f01_basic->data[1] & RMI_DEVICE_F01_QRY1_HAS_PROPS_2) > 0; + + /* get the product ID */ + addr += 11; + f01_product_id = fu_synaptics_rmi_device_read(self, addr, RMI_PRODUCT_ID_LENGTH, error); + if (f01_product_id == NULL) { + g_prefix_error(error, "failed to read the product id: "); + return FALSE; + } + if (!fu_synaptics_rmi_device_query_product_sub_id(self, &product_sub_id, error)) { + g_prefix_error(error, "failed to query product sub id: "); + return FALSE; + } + if (product_sub_id == 0) { + /* HID */ + product_id = g_strndup((const gchar *)f01_product_id->data, f01_product_id->len); + } else { + /* PS/2 */ + g_autofree gchar *tmp = g_strndup((const gchar *)f01_product_id->data, 6); + product_id = g_strdup_printf("%s-%03d", tmp, product_sub_id); + } + if (product_id != NULL) + fu_synaptics_rmi_device_set_product_id(self, product_id); + + /* force entering iep mode again */ + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + + /* skip */ + prod_info_addr = addr + 6; + addr += 10; + if (has_lts) + addr++; + if (has_sensor_id) + addr++; + if (has_lts) + addr += RMI_DEVICE_F01_LTS_RESERVED_SIZE; + + /* read package ids */ + if (has_query42) { + g_autoptr(GByteArray) f01_tmp = NULL; + f01_tmp = fu_synaptics_rmi_device_read(self, addr++, 1, error); + if (f01_tmp == NULL) { + g_prefix_error(error, "failed to read query 42: "); + return FALSE; + } + has_dds4_queries = (f01_tmp->data[0] & RMI_DEVICE_F01_QRY42_DS4_QUERIES) > 0; + } + if (has_dds4_queries) { + g_autoptr(GByteArray) f01_tmp = NULL; + f01_tmp = fu_synaptics_rmi_device_read(self, addr++, 1, error); + if (f01_tmp == NULL) { + g_prefix_error(error, "failed to read DS4 query length: "); + return FALSE; + } + ds4_query_length = f01_tmp->data[0]; + } + f01_ds4 = fu_synaptics_rmi_device_read(self, addr, 0x1, error); + if (f01_ds4 == NULL) { + g_prefix_error(error, "failed to read F01 Query43: "); + return FALSE; + } + has_package_id_query = (f01_ds4->data[0] & RMI_DEVICE_F01_QRY43_01_PACKAGE_ID) > 0; + has_build_id_query = (f01_ds4->data[0] & RMI_DEVICE_F01_QRY43_01_BUILD_ID) > 0; + addr += ds4_query_length; + if (has_package_id_query) + prod_info_addr++; + if (has_build_id_query) { + g_autoptr(GByteArray) f01_tmp = NULL; + guint8 buf32[4] = {0x0}; + f01_tmp = fu_synaptics_rmi_device_read(self, prod_info_addr, 0x3, error); + if (f01_tmp == NULL) { + g_prefix_error(error, "failed to read build ID bytes: "); + return FALSE; + } + if (!fu_memcpy_safe(buf32, + sizeof(buf32), + 0x0, /* dst */ + f01_tmp->data, + f01_tmp->len, + 0x0, /* src */ + f01_tmp->len, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf32, + sizeof(buf32), + 0x0, + &priv->flash.build_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + + /* read build ID, typically only for PS/2 */ + if (!fu_synaptics_rmi_device_query_build_id(self, &priv->flash.build_id, error)) { + g_prefix_error(error, "failed to query build id: "); + return FALSE; + } + + /* get Function34_Query0,1 */ + priv->f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (priv->f34 == NULL) + return FALSE; + if (priv->f34->function_version == 0x0) { + if (!fu_synaptics_rmi_v5_device_setup(self, error)) { + g_prefix_error(error, "failed to do v5 setup: "); + return FALSE; + } + } else if (priv->f34->function_version == 0x1) { + if (!fu_synaptics_rmi_v6_device_setup(self, error)) { + g_prefix_error(error, "failed to do v6 setup: "); + return FALSE; + } + } else if (priv->f34->function_version == 0x2) { + if (!fu_synaptics_rmi_v7_device_setup(self, error)) { + g_prefix_error(error, "failed to do v7 setup: "); + return FALSE; + } + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "f34 function version 0x%02x unsupported", + priv->f34->function_version); + return FALSE; + } + if (!fu_synaptics_rmi_device_query_status(self, error)) { + g_prefix_error(error, "failed to read bootloader status: "); + return FALSE; + } + + /* set versions */ + fw_ver = g_strdup_printf("%u.%u.%u", + f01_basic->data[2], + f01_basic->data[3], + priv->flash.build_id); + fu_device_set_version(device, fw_ver); + bl_ver = g_strdup_printf("%u.0.0", priv->flash.bootloader_id[1]); + fu_device_set_version_bootloader(device, bl_ver); + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_synaptics_rmi_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = fu_synaptics_rmi_firmware_new(); + g_autoptr(GBytes) bytes_cfg = NULL; + g_autoptr(GBytes) bytes_bin = NULL; + gsize size_expected; + + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check sizes */ + bytes_bin = fu_firmware_get_image_by_id_bytes(firmware, "ui", error); + if (bytes_bin == NULL) + return NULL; + size_expected = ((gsize)priv->flash.block_count_fw * (gsize)priv->flash.block_size) + + fu_synaptics_rmi_firmware_get_sig_size(FU_SYNAPTICS_RMI_FIRMWARE(firmware)); + if (g_bytes_get_size(bytes_bin) != size_expected) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "file firmware invalid size 0x%04x, expected 0x%04x", + (guint)g_bytes_get_size(bytes_bin), + (guint)size_expected); + return NULL; + } + bytes_cfg = fu_firmware_get_image_by_id_bytes(firmware, "config", error); + if (bytes_cfg == NULL) + return NULL; + size_expected = (gsize)priv->flash.block_count_cfg * (gsize)priv->flash.block_size; + if (g_bytes_get_size(bytes_cfg) != size_expected) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "file config invalid size 0x%04x, expected 0x%04x", + (guint)g_bytes_get_size(bytes_cfg), + (guint)size_expected); + return NULL; + } + + return g_steal_pointer(&firmware); +} + +static gboolean +fu_synaptics_rmi_device_poll(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) f34_db = NULL; + + /* get if the last flash read completed successfully */ + f34_db = fu_synaptics_rmi_device_read(self, priv->f34->data_base, 0x1, error); + if (f34_db == NULL) { + g_prefix_error(error, "failed to read f34_db: "); + return FALSE; + } + if ((f34_db->data[0] & 0x1f) != 0x0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "flash status invalid: 0x%x", + (guint)(f34_db->data[0] & 0x1f)); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_synaptics_rmi_device_poll_wait(FuSynapticsRmiDevice *self, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* try to poll every 20ms for up to 400ms */ + for (guint i = 0; i < 20; i++) { + g_usleep(1000 * 20); + g_clear_error(&error_local); + if (fu_synaptics_rmi_device_poll(self, &error_local)) + return TRUE; + g_debug("failed: %s", error_local->message); + } + + /* proxy the last error */ + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; +} + +static gboolean +fu_synaptics_rmi_device_wait_for_attr(FuSynapticsRmiDevice *self, + guint8 source_mask, + guint timeout_ms, + GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + return klass_rmi->wait_for_attr(self, source_mask, timeout_ms, error); +} + +gboolean +fu_synaptics_rmi_device_enter_iep_mode(FuSynapticsRmiDevice *self, + FuSynapticsRmiDeviceFlags flags, + GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + + /* already set */ + if ((flags & FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE) == 0 && priv->in_iep_mode) + return TRUE; + if (klass_rmi->enter_iep_mode != NULL) { + g_debug("enabling RMI iep_mode"); + if (!klass_rmi->enter_iep_mode(self, error)) { + g_prefix_error(error, "failed to enable RMI iep_mode: "); + return FALSE; + } + } + priv->in_iep_mode = TRUE; + return TRUE; +} + +gboolean +fu_synaptics_rmi_device_wait_for_idle(FuSynapticsRmiDevice *self, + guint timeout_ms, + RmiDeviceWaitForIdleFlags flags, + GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + guint8 f34_command; + guint8 f34_enabled; + guint8 f34_status; + g_autoptr(GByteArray) res = NULL; + g_autoptr(GError) error_local = NULL; + + /* try to get report without requesting */ + if (timeout_ms > 0 && !fu_synaptics_rmi_device_wait_for_attr(self, + priv->f34->interrupt_mask, + timeout_ms, + &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to wait for attr: "); + return FALSE; + } + } else if ((flags & RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34) == 0) { + /* device reported idle via an event */ + return TRUE; + } + + /* if for some reason we are not getting attention reports for HID devices + * then we can still continue after the timeout and read F34 status + * but if we have to wait for the timeout to ellapse every time then this + * will be slow */ + if (priv->f34->function_version == 0x1) { + res = fu_synaptics_rmi_device_read(self, priv->flash.status_addr, 0x2, error); + if (res == NULL) + return FALSE; + f34_command = res->data[0] & RMI_F34_COMMAND_V1_MASK; + f34_status = res->data[1] & RMI_F34_STATUS_V1_MASK; + f34_enabled = !!(res->data[1] & RMI_F34_ENABLED_MASK); + } else { + res = fu_synaptics_rmi_device_read(self, priv->flash.status_addr, 0x1, error); + if (res == NULL) + return FALSE; + f34_command = res->data[0] & RMI_F34_COMMAND_MASK; + f34_status = (res->data[0] >> RMI_F34_STATUS_SHIFT) & RMI_F34_STATUS_MASK; + f34_enabled = !!(res->data[0] & RMI_F34_ENABLED_MASK); + } + + /* PS/2 */ + if (FU_IS_SYNAPTICS_RMI_PS2_DEVICE(self)) { + if (f34_command == 0) { + g_debug("F34 zero as PS/2"); + return TRUE; + } + } + + /* is idle */ + if (f34_status == 0x0 && f34_command == 0x0) { + if (f34_enabled == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "idle but enabled unset"); + return FALSE; + } + return TRUE; + } + + /* failed */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "timed out waiting for idle [cmd:0x%x, sta:0x%x, ena:0x%x]", + f34_command, + f34_status, + f34_enabled); + return FALSE; +} + +gboolean +fu_synaptics_rmi_device_disable_sleep(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_GET_CLASS(self); + if (klass_rmi->disable_sleep == NULL) + return TRUE; + return klass_rmi->disable_sleep(self, error); +} + +gboolean +fu_synaptics_rmi_device_write_bootloader_id(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + gint block_data_offset = RMI_F34_BLOCK_DATA_OFFSET; + g_autoptr(GByteArray) bootloader_id_req = g_byte_array_new(); + + if (priv->f34->function_version == 0x1) + block_data_offset = RMI_F34_BLOCK_DATA_V1_OFFSET; + + /* write bootloader_id into F34_Flash_Data0,1 */ + g_byte_array_append(bootloader_id_req, + priv->flash.bootloader_id, + sizeof(priv->flash.bootloader_id)); + if (!fu_synaptics_rmi_device_write(self, + priv->f34->data_base + block_data_offset, + bootloader_id_req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write bootloader_id: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_synaptics_rmi_device_disable_irqs(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) interrupt_disable_req = g_byte_array_new(); + + fu_byte_array_append_uint8(interrupt_disable_req, + priv->f34->interrupt_mask | priv->f01->interrupt_mask); + if (!fu_synaptics_rmi_device_write(self, + priv->f01->control_base + 1, + interrupt_disable_req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to disable interrupts: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + if (priv->f34->function_version == 0x0 || priv->f34->function_version == 0x1) { + return fu_synaptics_rmi_v5_device_write_firmware(device, + firmware, + progress, + flags, + error); + } + if (priv->f34->function_version == 0x2) { + return fu_synaptics_rmi_v7_device_write_firmware(device, + firmware, + progress, + flags, + error); + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "f34 function version 0x%02x unsupported", + priv->f34->function_version); + return FALSE; +} + +static void +fu_synaptics_rmi_device_init(FuSynapticsRmiDevice *self) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.rmi"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + priv->current_page = 0xfe; + priv->functions = g_ptr_array_new_with_free_func(g_free); +} + +static void +fu_synaptics_rmi_device_finalize(GObject *object) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(object); + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + g_ptr_array_unref(priv->functions); + G_OBJECT_CLASS(fu_synaptics_rmi_device_parent_class)->finalize(object); +} + +static void +fu_synaptics_rmi_device_class_init(FuSynapticsRmiDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_synaptics_rmi_device_finalize; + klass_device->to_string = fu_synaptics_rmi_device_to_string; + klass_device->prepare_firmware = fu_synaptics_rmi_device_prepare_firmware; + klass_device->setup = fu_synaptics_rmi_device_setup; + klass_device->write_firmware = fu_synaptics_rmi_device_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-device.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..1bfc683e526593d1c03fa65a66861255cf6504e6 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-device.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-synaptics-rmi-common.h" + +#define FU_TYPE_SYNAPTICS_RMI_DEVICE (fu_synaptics_rmi_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuSynapticsRmiDevice, + fu_synaptics_rmi_device, + FU, + SYNAPTICS_RMI_DEVICE, + FuUdevDevice) + +typedef enum { + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE = 0, + FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE = 1 << 0, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE = 1 << 1, +} FuSynapticsRmiDeviceFlags; + +struct _FuSynapticsRmiDeviceClass { + FuUdevDeviceClass parent_class; + gboolean (*setup)(FuSynapticsRmiDevice *self, GError **error); + gboolean (*query_status)(FuSynapticsRmiDevice *self, GError **error); + gboolean (*write)(FuSynapticsRmiDevice *self, + guint16 addr, + GByteArray *req, + FuSynapticsRmiDeviceFlags flags, + GError **error); + GByteArray *(*read)(FuSynapticsRmiDevice *self, guint16 addr, gsize req_sz, GError **error); + GByteArray *(*read_packet_register)(FuSynapticsRmiDevice *self, + guint16 addr, + gsize req_sz, + GError **error); + gboolean (*wait_for_attr)(FuSynapticsRmiDevice *self, + guint8 source_mask, + guint timeout_ms, + GError **error); + gboolean (*set_page)(FuSynapticsRmiDevice *self, guint8 page, GError **error); + gboolean (*disable_sleep)(FuSynapticsRmiDevice *self, GError **error); + gboolean (*write_bus_select)(FuSynapticsRmiDevice *self, guint8 bus, GError **error); + gboolean (*query_build_id)(FuSynapticsRmiDevice *self, guint32 *build_id, GError **error); + gboolean (*query_product_sub_id)(FuSynapticsRmiDevice *self, + guint8 *product_sub_id, + GError **error); + gboolean (*enter_iep_mode)(FuSynapticsRmiDevice *self, GError **error); +}; + +typedef struct { + guint16 block_count_cfg; + guint16 block_count_fw; + guint16 block_size; + guint16 config_length; + guint16 payload_length; + guint32 build_id; + guint8 bootloader_id[2]; + guint8 status_addr; +} FuSynapticsRmiFlash; + +#define RMI_F34_HAS_NEW_REG_MAP (1 << 0) +#define RMI_F34_HAS_CONFIG_ID (1 << 2) + +#define RMI_F34_BLOCK_DATA_OFFSET 2 +#define RMI_F34_BLOCK_DATA_V1_OFFSET 1 + +#define RMI_F34_ENABLE_WAIT_MS 300 /* ms */ +#define RMI_F34_IDLE_WAIT_MS 20 /* ms */ + +#define RMI_DEVICE_PAGE_SELECT_REGISTER 0xff +#define RMI_DEVICE_BUS_SELECT_REGISTER 0xfe + +typedef enum { + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE = 0, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34 = (1 << 0), +} RmiDeviceWaitForIdleFlags; + +void +fu_synaptics_rmi_device_set_iepmode(FuSynapticsRmiDevice *self, gboolean iepmode); +gboolean +fu_synaptics_rmi_device_get_iepmode(FuSynapticsRmiDevice *self); +gboolean +fu_synaptics_rmi_device_set_page(FuSynapticsRmiDevice *self, guint8 page, GError **error); +gboolean +fu_synaptics_rmi_device_write_bootloader_id(FuSynapticsRmiDevice *self, GError **error); +gboolean +fu_synaptics_rmi_device_disable_irqs(FuSynapticsRmiDevice *self, GError **error); +GByteArray * +fu_synaptics_rmi_device_read(FuSynapticsRmiDevice *self, + guint16 addr, + gsize req_sz, + GError **error); +GByteArray * +fu_synaptics_rmi_device_read_packet_register(FuSynapticsRmiDevice *self, + guint16 addr, + gsize req_sz, + GError **error); +gboolean +fu_synaptics_rmi_device_write(FuSynapticsRmiDevice *self, + guint16 addr, + GByteArray *req, + FuSynapticsRmiDeviceFlags flags, + GError **error); +gboolean +fu_synaptics_rmi_device_reset(FuSynapticsRmiDevice *self, GError **error); +gboolean +fu_synaptics_rmi_device_wait_for_idle(FuSynapticsRmiDevice *self, + guint timeout_ms, + RmiDeviceWaitForIdleFlags flags, + GError **error); +gboolean +fu_synaptics_rmi_device_disable_sleep(FuSynapticsRmiDevice *self, GError **error); +FuSynapticsRmiFlash * +fu_synaptics_rmi_device_get_flash(FuSynapticsRmiDevice *self); +FuSynapticsRmiFunction * +fu_synaptics_rmi_device_get_function(FuSynapticsRmiDevice *self, + guint8 function_number, + GError **error); +gboolean +fu_synaptics_rmi_device_poll_wait(FuSynapticsRmiDevice *self, GError **error); +void +fu_synaptics_rmi_device_set_sig_size(FuSynapticsRmiDevice *self, guint16 sig_size); +guint16 +fu_synaptics_rmi_device_get_sig_size(FuSynapticsRmiDevice *self); +void +fu_synaptics_rmi_device_set_max_page(FuSynapticsRmiDevice *self, guint8 max_page); +guint8 +fu_synaptics_rmi_device_get_max_page(FuSynapticsRmiDevice *self); +gboolean +fu_synaptics_rmi_device_enter_iep_mode(FuSynapticsRmiDevice *self, + FuSynapticsRmiDeviceFlags flags, + GError **error); +gboolean +fu_synaptics_rmi_device_write_bus_select(FuSynapticsRmiDevice *self, guint8 bus, GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..7f47f68bc8f47a2a2d544acac3d8292e1805356b --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-synaptics-rmi-common.h" +#include "fu-synaptics-rmi-firmware.h" + +typedef enum { + RMI_FIRMWARE_KIND_UNKNOWN = 0x00, + RMI_FIRMWARE_KIND_0X = 0x01, + RMI_FIRMWARE_KIND_10 = 0x10, + RMI_FIRMWARE_KIND_LAST, +} RmiFirmwareKind; + +struct _FuSynapticsRmiFirmware { + FuFirmware parent_instance; + RmiFirmwareKind kind; + guint32 checksum; + guint8 io; + guint8 bootloader_version; + guint32 build_id; + guint32 package_id; + guint16 product_info; + gchar *product_id; + guint32 sig_size; +}; + +G_DEFINE_TYPE(FuSynapticsRmiFirmware, fu_synaptics_rmi_firmware, FU_TYPE_FIRMWARE) + +#define RMI_IMG_CHECKSUM_OFFSET 0x00 +#define RMI_IMG_IO_OFFSET 0x06 +#define RMI_IMG_BOOTLOADER_VERSION_OFFSET 0x07 +#define RMI_IMG_IMAGE_SIZE_OFFSET 0x08 +#define RMI_IMG_CONFIG_SIZE_OFFSET 0x0c +#define RMI_IMG_PACKAGE_ID_OFFSET 0x1a +#define RMI_IMG_FW_BUILD_ID_OFFSET 0x50 +#define RMI_IMG_SIGNATURE_SIZE_OFFSET 0x54 +#define RMI_IMG_PRODUCT_ID_OFFSET 0x10 +#define RMI_IMG_PRODUCT_INFO_OFFSET 0x1e +#define RMI_IMG_FW_OFFSET 0x100 + +#define RMI_IMG_V10_CNTR_ADDR_OFFSET 0x0c +#define RMI_IMG_MAX_CONTAINERS 1024 + +typedef struct __attribute__((packed)) { + guint32 content_checksum; + guint16 container_id; + guint8 minor_version; + guint8 major_version; + guint8 reserved_08; + guint8 reserved_09; + guint8 reserved_0a; + guint8 reserved_0b; + guint32 container_option_flags; + guint32 content_options_length; + guint32 content_options_address; + guint32 content_length; + guint32 content_address; +} RmiFirmwareContainerDescriptor; + +typedef enum { + RMI_FIRMWARE_CONTAINER_ID_TOP_LEVEL = 0, + RMI_FIRMWARE_CONTAINER_ID_UI, + RMI_FIRMWARE_CONTAINER_ID_UI_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_BL, + RMI_FIRMWARE_CONTAINER_ID_BL_IMAGE, + RMI_FIRMWARE_CONTAINER_ID_BL_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_BL_LOCKDOWN_INFO, + RMI_FIRMWARE_CONTAINER_ID_PERMANENT_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_GUEST_CODE, + RMI_FIRMWARE_CONTAINER_ID_BL_PROTOCOL_DESCRIPTOR, + RMI_FIRMWARE_CONTAINER_ID_UI_PROTOCOL_DESCRIPTOR, + RMI_FIRMWARE_CONTAINER_ID_RMI_SELF_DISCOVERY, + RMI_FIRMWARE_CONTAINER_ID_RMI_PAGE_CONTENT, + RMI_FIRMWARE_CONTAINER_ID_GENERAL_INFORMATION, + RMI_FIRMWARE_CONTAINER_ID_DEVICE_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_FLASH_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_GUEST_SERIALIZATION, + RMI_FIRMWARE_CONTAINER_ID_GLOBAL_PARAMETERS, + RMI_FIRMWARE_CONTAINER_ID_CORE_CODE, + RMI_FIRMWARE_CONTAINER_ID_CORE_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_DISPLAY_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_EXTERNAL_TOUCH_AFE_CONFIG, + RMI_FIRMWARE_CONTAINER_ID_UTILITY, + RMI_FIRMWARE_CONTAINER_ID_UTILITY_PARAMETER, +} RmiFirmwareContainerId; + +static const gchar * +rmi_firmware_container_id_to_string(RmiFirmwareContainerId container_id) +{ + if (container_id == RMI_FIRMWARE_CONTAINER_ID_TOP_LEVEL) + return "top-level"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_UI) + return "ui"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_UI_CONFIG) + return "ui-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_BL) + return "bl"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_BL_IMAGE) + return "bl-image"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_BL_CONFIG) + return "bl-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_BL_LOCKDOWN_INFO) + return "bl-lockdown-info"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_PERMANENT_CONFIG) + return "permanent-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_GUEST_CODE) + return "guest-code"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_BL_PROTOCOL_DESCRIPTOR) + return "bl-protocol-descriptor"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_UI_PROTOCOL_DESCRIPTOR) + return "ui-protocol-descriptor"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_RMI_SELF_DISCOVERY) + return "rmi-self-discovery"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_RMI_PAGE_CONTENT) + return "rmi-page-content"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_GENERAL_INFORMATION) + return "general-information"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_DEVICE_CONFIG) + return "device-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_FLASH_CONFIG) + return "flash-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_GUEST_SERIALIZATION) + return "guest-serialization"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_GLOBAL_PARAMETERS) + return "global-parameters"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_CORE_CODE) + return "core-code"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_CORE_CONFIG) + return "core-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_DISPLAY_CONFIG) + return "display-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_EXTERNAL_TOUCH_AFE_CONFIG) + return "external-touch-afe-config"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_UTILITY) + return "utility"; + if (container_id == RMI_FIRMWARE_CONTAINER_ID_UTILITY_PARAMETER) + return "utility-parameter"; + return NULL; +} + +static gboolean +fu_synaptics_rmi_firmware_add_image(FuFirmware *firmware, + const gchar *id, + GBytes *fw, + gsize offset, + gsize sz, + GError **error) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(FuFirmware) img = NULL; + + bytes = fu_bytes_new_offset(fw, offset, sz, error); + if (bytes == NULL) + return FALSE; + img = fu_firmware_new_from_bytes(bytes); + fu_firmware_set_id(img, id); + fu_firmware_add_image(firmware, img); + return TRUE; +} + +static void +fu_synaptics_rmi_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "kind", self->kind); + fu_xmlb_builder_insert_kv(bn, "product_id", self->product_id); + if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { + fu_xmlb_builder_insert_kx(bn, "bootloader_version", self->bootloader_version); + fu_xmlb_builder_insert_kx(bn, "io", self->io); + fu_xmlb_builder_insert_kx(bn, "checksum", self->checksum); + fu_xmlb_builder_insert_kx(bn, "build_id", self->build_id); + fu_xmlb_builder_insert_kx(bn, "package_id", self->package_id); + fu_xmlb_builder_insert_kx(bn, "product_info", self->product_info); + fu_xmlb_builder_insert_kx(bn, "sig_size", self->sig_size); + } +} + +static gboolean +fu_synaptics_rmi_firmware_parse_v10(FuFirmware *firmware, GBytes *fw, GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + RmiFirmwareContainerDescriptor desc = {0x0}; + guint16 container_id; + guint32 cntrs_len; + guint32 offset; + guint32 cntr_addr; + guint8 product_id[RMI_PRODUCT_ID_LENGTH] = {0x0}; + gsize sz = 0; + const guint8 *data = g_bytes_get_data(fw, &sz); + + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_V10_CNTR_ADDR_OFFSET, + &cntr_addr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + g_debug("v10 RmiFirmwareContainerDescriptor at 0x%x", cntr_addr); + if (!fu_memcpy_safe((guint8 *)&desc, + sizeof(desc), + 0x0, /* dst */ + data, + sz, + cntr_addr, /* src */ + sizeof(desc), + error)) { + g_prefix_error(error, "RmiFirmwareContainerDescriptor invalid: "); + return FALSE; + } + container_id = GUINT16_FROM_LE(desc.container_id); + if (container_id != RMI_FIRMWARE_CONTAINER_ID_TOP_LEVEL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "toplevel container_id invalid, got 0x%x expected 0x%x", + (guint)container_id, + (guint)RMI_FIRMWARE_CONTAINER_ID_TOP_LEVEL); + return FALSE; + } + offset = GUINT32_FROM_LE(desc.content_address); + if (offset > sz - sizeof(guint32) - sizeof(desc)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "image offset invalid, got 0x%x, size 0x%x", + (guint)offset, + (guint)sz); + return FALSE; + } + cntrs_len = GUINT32_FROM_LE(desc.content_length) / 4; + if (cntrs_len > RMI_IMG_MAX_CONTAINERS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "too many containers in file [%u], maximum is %u", + cntrs_len, + (guint)RMI_IMG_MAX_CONTAINERS); + return FALSE; + } + g_debug("offset=0x%x (cntrs_len=%u)", offset, cntrs_len); + + for (guint32 i = 0; i < cntrs_len; i++) { + guint32 content_addr; + guint32 addr; + guint32 length; + if (!fu_memread_uint32_safe(data, sz, offset, &addr, G_LITTLE_ENDIAN, error)) + return FALSE; + g_debug("parsing RmiFirmwareContainerDescriptor at 0x%x", addr); + if (!fu_memcpy_safe((guint8 *)&desc, + sizeof(desc), + 0x0, /* dst */ + data, + sz, + addr, /* src */ + sizeof(desc), + error)) + return FALSE; + container_id = GUINT16_FROM_LE(desc.container_id); + content_addr = GUINT32_FROM_LE(desc.content_address); + length = GUINT32_FROM_LE(desc.content_length); + g_debug("RmiFirmwareContainerDescriptor 0x%02x @ 0x%x (len 0x%x)", + container_id, + content_addr, + length); + if (length == 0 || length > sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "length invalid, length 0x%x, size 0x%x", + (guint)length, + (guint)sz); + return FALSE; + } + if (content_addr > sz - length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "address invalid, got 0x%x (length 0x%x), size 0x%x", + (guint)content_addr, + (guint)length, + (guint)sz); + return FALSE; + } + switch (container_id) { + case RMI_FIRMWARE_CONTAINER_ID_BL: + if (!fu_memread_uint8_safe(data, + sz, + content_addr, + &self->bootloader_version, + error)) + return FALSE; + break; + case RMI_FIRMWARE_CONTAINER_ID_UI: + case RMI_FIRMWARE_CONTAINER_ID_CORE_CODE: + if (!fu_synaptics_rmi_firmware_add_image(firmware, + "ui", + fw, + content_addr, + length, + error)) + return FALSE; + break; + case RMI_FIRMWARE_CONTAINER_ID_FLASH_CONFIG: + if (!fu_synaptics_rmi_firmware_add_image(firmware, + "flash-config", + fw, + content_addr, + length, + error)) + return FALSE; + break; + case RMI_FIRMWARE_CONTAINER_ID_UI_CONFIG: + case RMI_FIRMWARE_CONTAINER_ID_CORE_CONFIG: + if (!fu_synaptics_rmi_firmware_add_image(firmware, + "config", + fw, + content_addr, + length, + error)) + return FALSE; + break; + case RMI_FIRMWARE_CONTAINER_ID_GENERAL_INFORMATION: + if (length < 0x18 + RMI_PRODUCT_ID_LENGTH) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "content_addr invalid, got 0x%x (length 0x%x)", + content_addr, + (guint)length); + return FALSE; + } + g_clear_pointer(&self->product_id, g_free); + self->io = 1; + if (!fu_memread_uint32_safe(data, + sz, + content_addr, + &self->package_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(data, + sz, + content_addr + 0x04, + &self->build_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memcpy_safe(product_id, + sizeof(product_id), + 0x0, /* dst */ + data, + sz, + content_addr + 0x18, /* src */ + sizeof(product_id), + error)) + return FALSE; + break; + default: + g_debug("unsupported container %s [0x%02x]", + rmi_firmware_container_id_to_string(container_id), + container_id); + break; + } + offset += 4; + } + if (product_id[0] != '\0') { + g_free(self->product_id); + self->product_id = g_strndup((const gchar *)product_id, sizeof(product_id)); + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_firmware_parse_v0x(FuFirmware *firmware, GBytes *fw, GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + guint32 cfg_sz; + guint32 img_sz = 0; + guint32 sig_offset = 0; + gsize sz = 0; + const guint8 *data = g_bytes_get_data(fw, &sz); + + /* main firmware */ + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_IMAGE_SIZE_OFFSET, + &img_sz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (img_sz > 0) { + /* payload, then signature appended */ + if (self->sig_size > 0) { + sig_offset = img_sz - self->sig_size; + if (!fu_synaptics_rmi_firmware_add_image(firmware, + "sig", + fw, + RMI_IMG_FW_OFFSET + sig_offset, + self->sig_size, + error)) + return FALSE; + } + if (!fu_synaptics_rmi_firmware_add_image(firmware, + "ui", + fw, + RMI_IMG_FW_OFFSET, + img_sz, + error)) + return FALSE; + } + + /* config */ + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_CONFIG_SIZE_OFFSET, + &cfg_sz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cfg_sz > 0) { + if (!fu_synaptics_rmi_firmware_add_image(firmware, + "config", + fw, + RMI_IMG_FW_OFFSET + img_sz, + cfg_sz, + error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + gsize sz = 0; + guint32 checksum_calculated; + guint32 firmware_size = 0; + const guint8 *data = g_bytes_get_data(fw, &sz); + + /* check minimum size */ + if (sz < RMI_IMG_FW_OFFSET) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "not enough data to parse header"); + return FALSE; + } + if (sz % 2 != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "data not aligned to 16 bits"); + return FALSE; + } + + /* verify checksum */ + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_CHECKSUM_OFFSET, + &self->checksum, + G_LITTLE_ENDIAN, + error)) + return FALSE; + checksum_calculated = fu_synaptics_rmi_generate_checksum(data + 4, sz - 4); + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if (self->checksum != checksum_calculated) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum verification failed, got 0x%08x, actual 0x%08x", + (guint)self->checksum, + (guint)checksum_calculated); + return FALSE; + } + } + + /* parse legacy image */ + g_clear_pointer(&self->product_id, g_free); + self->io = data[RMI_IMG_IO_OFFSET]; + self->bootloader_version = data[RMI_IMG_BOOTLOADER_VERSION_OFFSET]; + if (self->io == 1) { + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_FW_BUILD_ID_OFFSET, + &self->build_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_PACKAGE_ID_OFFSET, + &self->package_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + self->product_id = + g_strndup((const gchar *)data + RMI_IMG_PRODUCT_ID_OFFSET, RMI_PRODUCT_ID_LENGTH); + if (!fu_memread_uint16_safe(data, + sz, + RMI_IMG_PRODUCT_INFO_OFFSET, + &self->product_info, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_IMAGE_SIZE_OFFSET, + &firmware_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_firmware_set_size(firmware, firmware_size); + + /* parse partitions, but ignore lockdown */ + switch (self->bootloader_version) { + case 2: + case 3: + case 4: + case 5: + case 6: + if ((self->io & 0x10) >> 1) { + if (!fu_memread_uint32_safe(data, + sz, + RMI_IMG_SIGNATURE_SIZE_OFFSET, + &self->sig_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + if (!fu_synaptics_rmi_firmware_parse_v0x(firmware, fw, error)) + return FALSE; + self->kind = RMI_FIRMWARE_KIND_0X; + break; + case 16: + if (!fu_synaptics_rmi_firmware_parse_v10(firmware, fw, error)) + return FALSE; + self->kind = RMI_FIRMWARE_KIND_10; + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unsupported image version 0x%02x", + self->bootloader_version); + return FALSE; + } + + /* success */ + return TRUE; +} + +guint32 +fu_synaptics_rmi_firmware_get_sig_size(FuSynapticsRmiFirmware *self) +{ + return self->sig_size; +} + +static GBytes * +fu_synaptics_rmi_firmware_write_v0x(FuFirmware *firmware, GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + gsize bufsz = 0; + guint32 csum; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) buf_blob = NULL; + + /* default image */ + img = fu_firmware_get_image_by_id(firmware, "ui", error); + if (img == NULL) + return NULL; + buf_blob = fu_firmware_write(img, error); + if (buf_blob == NULL) + return NULL; + bufsz = g_bytes_get_size(buf_blob); + + /* create empty block */ + fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x4 + bufsz, 0x00); + buf->data[RMI_IMG_IO_OFFSET] = 0x0; /* no build_id or package_id */ + buf->data[RMI_IMG_BOOTLOADER_VERSION_OFFSET] = 0x2; /* not hierarchical */ + if (self->product_id != NULL) { + gsize product_id_sz = strlen(self->product_id); + if (!fu_memcpy_safe(buf->data, + buf->len, + RMI_IMG_PRODUCT_ID_OFFSET, /* dst */ + (const guint8 *)self->product_id, + product_id_sz, + 0x0, /* src */ + product_id_sz, + error)) + return NULL; + } + fu_memwrite_uint16(buf->data + RMI_IMG_PRODUCT_INFO_OFFSET, 0x1234, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_IMAGE_SIZE_OFFSET, bufsz, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_CONFIG_SIZE_OFFSET, bufsz, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + 0x0, 0xdead, G_LITTLE_ENDIAN); /* img */ + fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + bufsz, + 0xbeef, + G_LITTLE_ENDIAN); /* config */ + + /* fixup checksum */ + csum = fu_synaptics_rmi_generate_checksum(buf->data + 4, buf->len - 4); + fu_memwrite_uint32(buf->data + RMI_IMG_CHECKSUM_OFFSET, csum, G_LITTLE_ENDIAN); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static GBytes * +fu_synaptics_rmi_firmware_write_v10(FuFirmware *firmware, GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + gsize bufsz; + guint32 csum; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) buf_blob = NULL; + + /* header | desc_hdr | offset_table | desc | flash_config | + * \0x0 \0x20 \0x24 \0x44 |0x48 */ + RmiFirmwareContainerDescriptor desc_hdr = { + .container_id = GUINT16_TO_LE(RMI_FIRMWARE_CONTAINER_ID_TOP_LEVEL), + .content_length = GUINT32_TO_LE(0x1 * 4), /* size of offset table in bytes */ + .content_address = GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x20), /* offset to table */ + }; + guint32 offset_table[] = { + GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x24)}; /* offset to first descriptor */ + RmiFirmwareContainerDescriptor desc = { + .container_id = GUINT16_TO_LE(RMI_FIRMWARE_CONTAINER_ID_FLASH_CONFIG), + .content_length = GUINT32_TO_LE(0x0), + .content_address = GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x44), + }; + + /* default image */ + img = fu_firmware_get_image_by_id(firmware, "ui", error); + if (img == NULL) + return NULL; + buf_blob = fu_firmware_write(img, error); + if (buf_blob == NULL) + return NULL; + bufsz = g_bytes_get_size(buf_blob); + desc.content_length = GUINT32_TO_LE(bufsz); + + /* create empty block */ + fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x48, 0x00); + buf->data[RMI_IMG_IO_OFFSET] = 0x1; + buf->data[RMI_IMG_BOOTLOADER_VERSION_OFFSET] = 16; /* hierarchical */ + if (self->product_id != NULL) { + gsize product_id_sz = strlen(self->product_id); + if (!fu_memcpy_safe(buf->data, + buf->len, + RMI_IMG_PRODUCT_ID_OFFSET, /* dst */ + (const guint8 *)self->product_id, + product_id_sz, + 0x0, /* src */ + product_id_sz, + error)) + return NULL; + } + fu_memwrite_uint32(buf->data + RMI_IMG_FW_BUILD_ID_OFFSET, 0x1234, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_PACKAGE_ID_OFFSET, 0x4321, G_LITTLE_ENDIAN); + fu_memwrite_uint16(buf->data + RMI_IMG_PRODUCT_INFO_OFFSET, 0x3456, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_IMAGE_SIZE_OFFSET, bufsz, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_CONFIG_SIZE_OFFSET, bufsz, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf->data + RMI_IMG_V10_CNTR_ADDR_OFFSET, + RMI_IMG_FW_OFFSET, + G_LITTLE_ENDIAN); + + /* hierarchical section */ + memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x00, &desc_hdr, sizeof(desc_hdr)); + memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x20, offset_table, sizeof(offset_table)); + memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x24, &desc, sizeof(desc)); + fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + 0x44, + 0xfeed, + G_LITTLE_ENDIAN); /* flash_config */ + + /* fixup checksum */ + csum = fu_synaptics_rmi_generate_checksum(buf->data + 4, buf->len - 4); + fu_memwrite_uint32(buf->data + RMI_IMG_CHECKSUM_OFFSET, csum, G_LITTLE_ENDIAN); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_synaptics_rmi_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + const gchar *product_id; + guint64 tmp; + + /* either 0x or 10 */ + tmp = xb_node_query_text_as_uint(n, "kind", NULL); + if (tmp != G_MAXUINT64) + self->kind = tmp; + + /* any string */ + product_id = xb_node_query_text(n, "product_id", NULL); + if (product_id != NULL) { + gsize product_id_sz = strlen(product_id); + if (product_id_sz > RMI_PRODUCT_ID_LENGTH) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "product_id not supported, %u of %u bytes", + (guint)product_id_sz, + (guint)RMI_PRODUCT_ID_LENGTH); + return FALSE; + } + g_free(self->product_id); + self->product_id = g_strdup(product_id); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_synaptics_rmi_firmware_write(FuFirmware *firmware, GError **error) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + + /* two supported container formats */ + if (self->kind == RMI_FIRMWARE_KIND_0X) + return fu_synaptics_rmi_firmware_write_v0x(firmware, error); + if (self->kind == RMI_FIRMWARE_KIND_10) + return fu_synaptics_rmi_firmware_write_v10(firmware, error); + + /* not supported */ + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "kind not supported"); + return NULL; +} + +static void +fu_synaptics_rmi_firmware_init(FuSynapticsRmiFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_synaptics_rmi_firmware_finalize(GObject *obj) +{ + FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(obj); + g_free(self->product_id); + G_OBJECT_CLASS(fu_synaptics_rmi_firmware_parent_class)->finalize(obj); +} + +static void +fu_synaptics_rmi_firmware_class_init(FuSynapticsRmiFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_synaptics_rmi_firmware_finalize; + klass_firmware->parse = fu_synaptics_rmi_firmware_parse; + klass_firmware->export = fu_synaptics_rmi_firmware_export; + klass_firmware->build = fu_synaptics_rmi_firmware_build; + klass_firmware->write = fu_synaptics_rmi_firmware_write; +} + +FuFirmware * +fu_synaptics_rmi_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_RMI_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..5d851d429c2311aad428340e303598790a7dd32b --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYNAPTICS_RMI_FIRMWARE (fu_synaptics_rmi_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsRmiFirmware, + fu_synaptics_rmi_firmware, + FU, + SYNAPTICS_RMI_FIRMWARE, + FuFirmware) + +FuFirmware * +fu_synaptics_rmi_firmware_new(void); +guint32 +fu_synaptics_rmi_firmware_get_sig_size(FuSynapticsRmiFirmware *self); diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c new file mode 100644 index 0000000000000000000000000000000000000000..1ff6e3e9dcaafc3f7c74dfca48abd96c46f2f5c9 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (c) 2020 Synaptics Incorporated. + * Copyright (C) 2012 Andrew Duggan + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-synaptics-rmi-hid-device.h" +#include "fu-synaptics-rmi-v5-device.h" +#include "fu-synaptics-rmi-v7-device.h" + +struct _FuSynapticsRmiHidDevice { + FuSynapticsRmiDevice parent_instance; + FuIOChannel *io_channel; +}; + +G_DEFINE_TYPE(FuSynapticsRmiHidDevice, fu_synaptics_rmi_hid_device, FU_TYPE_SYNAPTICS_RMI_DEVICE) + +#define RMI_WRITE_REPORT_ID 0x9 /* output report */ +#define RMI_READ_ADDR_REPORT_ID 0xa /* output report */ +#define RMI_READ_DATA_REPORT_ID 0xb /* input report */ +#define RMI_ATTN_REPORT_ID 0xc /* input report */ +#define RMI_SET_RMI_MODE_REPORT_ID 0xf /* feature report */ + +#define RMI_DEVICE_DEFAULT_TIMEOUT 2000 + +#define HID_RMI4_REPORT_ID 0 +#define HID_RMI4_READ_INPUT_COUNT 1 +#define HID_RMI4_READ_INPUT_DATA 2 +#define HID_RMI4_READ_OUTPUT_ADDR 2 +#define HID_RMI4_READ_OUTPUT_COUNT 4 +#define HID_RMI4_WRITE_OUTPUT_COUNT 1 +#define HID_RMI4_WRITE_OUTPUT_ADDR 2 +#define HID_RMI4_WRITE_OUTPUT_DATA 4 +#define HID_RMI4_FEATURE_MODE 1 +#define HID_RMI4_ATTN_INTERUPT_SOURCES 1 +#define HID_RMI4_ATTN_DATA 2 + +/* + * This bit disables whatever sleep mode may be selected by the sleep_mode + * field and forces the device to run at full power without sleeping. + */ +#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2) + +/* + * msleep mode controls power management on the device and affects all + * functions of the device. + */ +#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03 + +#define RMI_SLEEP_MODE_NORMAL 0x00 +#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01 + +#define FU_SYNAPTICS_RMI_HID_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static GByteArray * +fu_synaptics_rmi_hid_device_read(FuSynapticsRmiDevice *rmi_device, + guint16 addr, + gsize req_sz, + GError **error) +{ + FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* maximum size */ + if (req_sz > 0xffff) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "data to read was too long"); + return NULL; + } + + /* report then old 1 byte read count */ + fu_byte_array_append_uint8(req, RMI_READ_ADDR_REPORT_ID); + fu_byte_array_append_uint8(req, 0x0); + + /* address */ + fu_byte_array_append_uint16(req, addr, G_LITTLE_ENDIAN); + + /* read output count */ + fu_byte_array_append_uint16(req, req_sz, G_LITTLE_ENDIAN); + + /* request */ + for (guint j = req->len; j < 21; j++) + fu_byte_array_append_uint8(req, 0x0); + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "ReportWrite", + req->data, + req->len, + 80, + FU_DUMP_FLAGS_NONE); + } + if (!fu_io_channel_write_byte_array(self->io_channel, + req, + RMI_DEVICE_DEFAULT_TIMEOUT, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) + return NULL; + + /* keep reading responses until we get enough data */ + while (buf->len < req_sz) { + guint8 input_count_sz = 0; + g_autoptr(GByteArray) res = NULL; + res = fu_io_channel_read_byte_array(self->io_channel, + req_sz, + RMI_DEVICE_DEFAULT_TIMEOUT, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); + if (res == NULL) + return NULL; + if (res->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "response zero sized"); + return NULL; + } + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "ReportRead", + res->data, + res->len, + 80, + FU_DUMP_FLAGS_NONE); + } + + /* ignore non data report events */ + if (res->data[HID_RMI4_REPORT_ID] != RMI_READ_DATA_REPORT_ID) { + g_debug("ignoring report with ID 0x%02x", res->data[HID_RMI4_REPORT_ID]); + continue; + } + if (res->len < HID_RMI4_READ_INPUT_DATA) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "response too small: 0x%02x", + res->len); + return NULL; + } + input_count_sz = res->data[HID_RMI4_READ_INPUT_COUNT]; + if (input_count_sz == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "input count zero"); + return NULL; + } + if (input_count_sz + (guint)HID_RMI4_READ_INPUT_DATA > res->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "underflow 0x%02x from expected 0x%02x", + res->len, + (guint)input_count_sz + HID_RMI4_READ_INPUT_DATA); + return NULL; + } + g_byte_array_append(buf, res->data + HID_RMI4_READ_INPUT_DATA, input_count_sz); + } + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "DeviceRead", + buf->data, + buf->len, + 80, + FU_DUMP_FLAGS_NONE); + } + + return g_steal_pointer(&buf); +} + +static GByteArray * +fu_synaptics_rmi_hid_device_read_packet_register(FuSynapticsRmiDevice *rmi_device, + guint16 addr, + gsize req_sz, + GError **error) +{ + return fu_synaptics_rmi_hid_device_read(rmi_device, addr, req_sz, error); +} + +static gboolean +fu_synaptics_rmi_hid_device_write(FuSynapticsRmiDevice *rmi_device, + guint16 addr, + GByteArray *req, + FuSynapticsRmiDeviceFlags flags, + GError **error) +{ + FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device); + guint8 len = 0x0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* check size */ + if (req != NULL) { + if (req->len > 0xff) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "data to write was too long"); + return FALSE; + } + len = req->len; + } + + /* report */ + fu_byte_array_append_uint8(buf, RMI_WRITE_REPORT_ID); + + /* length */ + fu_byte_array_append_uint8(buf, len); + + /* address */ + fu_byte_array_append_uint16(buf, addr, G_LITTLE_ENDIAN); + + /* optional data */ + if (req != NULL) + g_byte_array_append(buf, req->data, req->len); + + /* pad out to 21 bytes for some reason */ + for (guint i = buf->len; i < 21; i++) + fu_byte_array_append_uint8(buf, 0x0); + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "DeviceWrite", + buf->data, + buf->len, + 80, + FU_DUMP_FLAGS_NONE); + } + + return fu_io_channel_write_byte_array(self->io_channel, + buf, + RMI_DEVICE_DEFAULT_TIMEOUT, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error); +} + +static gboolean +fu_synaptics_rmi_hid_device_wait_for_attr(FuSynapticsRmiDevice *rmi_device, + guint8 source_mask, + guint timeout_ms, + GError **error) +{ + FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device); + g_autoptr(GTimer) timer = g_timer_new(); + + /* wait for event from hardware */ + while (g_timer_elapsed(timer, NULL) * 1000.f < timeout_ms) { + g_autoptr(GByteArray) res = NULL; + g_autoptr(GError) error_local = NULL; + + /* read from fd */ + res = fu_io_channel_read_byte_array(self->io_channel, + HID_RMI4_ATTN_INTERUPT_SOURCES + 1, + timeout_ms, + FU_IO_CHANNEL_FLAG_NONE, + &error_local); + if (res == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) + break; + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "ReportRead", + res->data, + res->len, + 80, + FU_DUMP_FLAGS_NONE); + } + if (res->len < HID_RMI4_ATTN_INTERUPT_SOURCES + 1) { + g_debug("attr: ignoring small read of %u", res->len); + continue; + } + if (res->data[HID_RMI4_REPORT_ID] != RMI_ATTN_REPORT_ID) { + g_debug("attr: ignoring invalid report ID 0x%x", + res->data[HID_RMI4_REPORT_ID]); + continue; + } + + /* success */ + if (source_mask & res->data[HID_RMI4_ATTN_INTERUPT_SOURCES]) + return TRUE; + + /* wrong mask */ + g_debug("source mask did not match: 0x%x", + res->data[HID_RMI4_ATTN_INTERUPT_SOURCES]); + } + + /* urgh */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no attr report, timed out"); + return FALSE; +} + +typedef enum { + HID_RMI4_MODE_MOUSE = 0, + HID_RMI4_MODE_ATTN_REPORTS = 1, + HID_RMI4_MODE_NO_PACKED_ATTN_REPORTS = 2, +} FuSynapticsRmiHidMode; + +static gboolean +fu_synaptics_rmi_hid_device_set_mode(FuSynapticsRmiHidDevice *self, + FuSynapticsRmiHidMode mode, + GError **error) +{ + const guint8 data[] = {0x0f, mode}; + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SetMode", data, sizeof(data)); + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCSFEATURE(sizeof(data)), + (guint8 *)data, + NULL, + FU_SYNAPTICS_RMI_HID_DEVICE_IOCTL_TIMEOUT, + error); +} + +static gboolean +fu_synaptics_rmi_hid_device_open(FuDevice *device, GError **error) +{ + FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(device); + + /* FuUdevDevice->open */ + if (!FU_DEVICE_CLASS(fu_synaptics_rmi_hid_device_parent_class)->open(device, error)) + return FALSE; + + /* set up touchpad so we can query it */ + self->io_channel = fu_io_channel_unix_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(device))); + if (!fu_synaptics_rmi_hid_device_set_mode(self, HID_RMI4_MODE_ATTN_REPORTS, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_hid_device_close(FuDevice *device, GError **error) +{ + FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* turn it back to mouse mode */ + if (!fu_synaptics_rmi_hid_device_set_mode(self, HID_RMI4_MODE_MOUSE, &error_local)) { + /* if just detached for replug, swallow error */ + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + g_debug("ignoring: %s", error_local->message); + } + + fu_udev_device_set_fd(FU_UDEV_DEVICE(device), -1); + g_clear_object(&self->io_channel); + + /* FuUdevDevice->close */ + return FU_DEVICE_CLASS(fu_synaptics_rmi_hid_device_parent_class)->close(device, error); +} + +static gboolean +fu_synaptics_rmi_hid_device_rebind_driver(FuSynapticsRmiDevice *self, GError **error) +{ + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(self)); + const gchar *hid_id; + const gchar *driver; + const gchar *subsystem; + g_autofree gchar *fn_rebind = NULL; + g_autofree gchar *fn_unbind = NULL; + g_autoptr(GUdevDevice) parent_hid = NULL; + g_autoptr(GUdevDevice) parent_i2c = NULL; + + /* get actual HID node */ + parent_hid = g_udev_device_get_parent_with_subsystem(udev_device, "hid", NULL); + if (parent_hid == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no HID parent device for %s", + g_udev_device_get_sysfs_path(udev_device)); + return FALSE; + } + + /* find the physical ID to use for the rebind */ + hid_id = g_udev_device_get_property(parent_hid, "HID_PHYS"); + if (hid_id == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no HID_PHYS in %s", + g_udev_device_get_sysfs_path(parent_hid)); + return FALSE; + } + g_debug("HID_PHYS: %s", hid_id); + + /* build paths */ + parent_i2c = g_udev_device_get_parent_with_subsystem(udev_device, "i2c", NULL); + if (parent_i2c == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no I2C parent device for %s", + g_udev_device_get_sysfs_path(udev_device)); + return FALSE; + } + driver = g_udev_device_get_driver(parent_i2c); + subsystem = g_udev_device_get_subsystem(parent_i2c); + fn_rebind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "bind", NULL); + fn_unbind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "unbind", NULL); + + /* unbind hidraw, then bind it again to get a replug */ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + if (!fu_synaptics_rmi_device_writeln(fn_unbind, hid_id, error)) + return FALSE; + if (!fu_synaptics_rmi_device_writeln(fn_rebind, hid_id, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiFunction *f34; + + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + if (f34->function_version == 0x0 || f34->function_version == 0x1) { + if (!fu_synaptics_rmi_v5_device_detach(device, progress, error)) + return FALSE; + } else if (f34->function_version == 0x2) { + if (!fu_synaptics_rmi_v7_device_detach(device, progress, error)) + return FALSE; + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "f34 function version 0x%02x unsupported", + f34->function_version); + return FALSE; + } + return fu_synaptics_rmi_hid_device_rebind_driver(self, error); +} + +static gboolean +fu_synaptics_rmi_hid_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* reset device */ + if (!fu_synaptics_rmi_device_reset(self, error)) + return FALSE; + + /* rebind to rescan PDT with new firmware running */ + return fu_synaptics_rmi_hid_device_rebind_driver(self, error); +} + +static gboolean +fu_synaptics_rmi_hid_device_set_page(FuSynapticsRmiDevice *self, guint8 page, GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + fu_byte_array_append_uint8(req, page); + if (!fu_synaptics_rmi_device_write(self, + RMI_DEVICE_PAGE_SELECT_REGISTER, + req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to set RMA page 0x%x: ", page); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_hid_device_probe(FuDevice *device, GError **error) +{ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static gboolean +fu_synaptics_rmi_hid_device_disable_sleep(FuSynapticsRmiDevice *rmi_device, GError **error) +{ + FuSynapticsRmiFunction *f01; + g_autoptr(GByteArray) f01_control0 = NULL; + + f01 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error); + if (f01 == NULL) + return FALSE; + f01_control0 = fu_synaptics_rmi_device_read(rmi_device, f01->control_base, 0x1, error); + if (f01_control0 == NULL) { + g_prefix_error(error, "failed to write get f01_control0: "); + return FALSE; + } + f01_control0->data[0] |= RMI_F01_CRTL0_NOSLEEP_BIT; + f01_control0->data[0] = + (f01_control0->data[0] & ~RMI_F01_CTRL0_SLEEP_MODE_MASK) | RMI_SLEEP_MODE_NORMAL; + if (!fu_synaptics_rmi_device_write(rmi_device, + f01->control_base, + f01_control0, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write f01_control0: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_hid_device_query_status(FuSynapticsRmiDevice *rmi_device, GError **error) +{ + FuSynapticsRmiFunction *f34; + f34 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error); + if (f34 == NULL) + return FALSE; + if (f34->function_version == 0x0 || f34->function_version == 0x1) { + return fu_synaptics_rmi_v5_device_query_status(rmi_device, error); + } + if (f34->function_version == 0x2) { + return fu_synaptics_rmi_v7_device_query_status(rmi_device, error); + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "f34 function version 0x%02x unsupported", + f34->function_version); + return FALSE; +} + +static void +fu_synaptics_rmi_hid_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_synaptics_rmi_hid_device_init(FuSynapticsRmiHidDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "Touchpad"); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_synaptics_rmi_device_set_max_page(FU_SYNAPTICS_RMI_DEVICE(self), 0xff); +} + +static void +fu_synaptics_rmi_hid_device_class_init(FuSynapticsRmiHidDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_CLASS(klass); + klass_device->attach = fu_synaptics_rmi_hid_device_attach; + klass_device->detach = fu_synaptics_rmi_hid_device_detach; + klass_device->probe = fu_synaptics_rmi_hid_device_probe; + klass_device->open = fu_synaptics_rmi_hid_device_open; + klass_device->close = fu_synaptics_rmi_hid_device_close; + klass_device->set_progress = fu_synaptics_rmi_hid_device_set_progress; + klass_rmi->write = fu_synaptics_rmi_hid_device_write; + klass_rmi->read = fu_synaptics_rmi_hid_device_read; + klass_rmi->wait_for_attr = fu_synaptics_rmi_hid_device_wait_for_attr; + klass_rmi->set_page = fu_synaptics_rmi_hid_device_set_page; + klass_rmi->query_status = fu_synaptics_rmi_hid_device_query_status; + klass_rmi->read_packet_register = fu_synaptics_rmi_hid_device_read_packet_register; + klass_rmi->disable_sleep = fu_synaptics_rmi_hid_device_disable_sleep; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.h new file mode 100644 index 0000000000000000000000000000000000000000..13dae9b71ba9eb149885666e9ae446a40b4c7a30 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (c) 2012 Synaptics Incorporated. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-synaptics-rmi-device.h" + +#define FU_TYPE_SYNAPTICS_RMI_HID_DEVICE (fu_synaptics_rmi_hid_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsRmiHidDevice, + fu_synaptics_rmi_hid_device, + FU, + SYNAPTICS_RMI_HID_DEVICE, + FuSynapticsRmiDevice) diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-plugin.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..610f61b5ae688a98457de0fce93f4a3868e681f7 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-plugin.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synaptics-rmi-firmware.h" +#include "fu-synaptics-rmi-hid-device.h" +#include "fu-synaptics-rmi-plugin.h" +#include "fu-synaptics-rmi-ps2-device.h" + +struct _FuSynapticsRmiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSynapticsRmiPlugin, fu_synaptics_rmi_plugin, FU_TYPE_PLUGIN) + +static void +fu_synaptics_rmi_plugin_init(FuSynapticsRmiPlugin *self) +{ +} + +static void +fu_synaptics_rmi_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_udev_subsystem(plugin, "serio"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_RMI_HID_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_RMI_PS2_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_RMI_FIRMWARE); +} + +static void +fu_synaptics_rmi_plugin_class_init(FuSynapticsRmiPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_synaptics_rmi_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-plugin.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..a280fa502fb3b379ac7a9fd7b5696ea5abd3eddc --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSynapticsRmiPlugin, + fu_synaptics_rmi_plugin, + FU, + SYNAPTICS_RMI_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c new file mode 100644 index 0000000000000000000000000000000000000000..b5cdbd5eb200f728d13550e359a3bcff6de69524 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c @@ -0,0 +1,1021 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (c) 2020 Synaptics Incorporated. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-rmi-ps2-device.h" +#include "fu-synaptics-rmi-v5-device.h" +#include "fu-synaptics-rmi-v7-device.h" + +struct _FuSynapticsRmiPs2Device { + FuSynapticsRmiDevice parent_instance; + FuIOChannel *io_channel; +}; + +G_DEFINE_TYPE(FuSynapticsRmiPs2Device, fu_synaptics_rmi_ps2_device, FU_TYPE_SYNAPTICS_RMI_DEVICE) + +enum EPS2DataPortCommand { + edpAuxFullRMIBackDoor = 0x7F, + edpAuxAccessModeByte1 = 0xE0, + edpAuxAccessModeByte2 = 0xE1, + edpAuxIBMReadSecondaryID = 0xE1, + edpAuxSetScaling1To1 = 0xE6, + edpAuxSetScaling2To1 = 0xE7, + edpAuxSetResolution = 0xE8, + edpAuxStatusRequest = 0xE9, + edpAuxSetStreamMode = 0xEA, + edpAuxReadData = 0xEB, + edpAuxResetWrapMode = 0xEC, + edpAuxSetWrapMode = 0xEE, + edpAuxSetRemoteMode = 0xF0, + edpAuxReadDeviceType = 0xF2, + edpAuxSetSampleRate = 0xF3, + edpAuxEnable = 0xF4, + edpAuxDisable = 0xF5, + edpAuxSetDefault = 0xF6, + edpAuxResend = 0xFE, + edpAuxReset = 0xFF, +}; + +typedef enum { + esdrTouchPad = 0x47, + esdrStyk = 0x46, + esdrControlBar = 0x44, + esdrRGBControlBar = 0x43, +} ESynapticsDeviceResponse; + +enum EStatusRequestSequence { + esrIdentifySynaptics = 0x00, + esrReadTouchPadModes = 0x01, + esrReadModeByte = 0x01, + esrReadEdgeMargins = 0x02, + esrReadCapabilities = 0x02, + esrReadModelID = 0x03, + esrReadCompilationDate = 0x04, + esrReadSerialNumberPrefix = 0x06, + esrReadSerialNumberSuffix = 0x07, + esrReadResolutions = 0x08, + esrReadExtraCapabilities1 = 0x09, + esrReadExtraCapabilities2 = 0x0A, + esrReadExtraCapabilities3 = 0x0B, + esrReadExtraCapabilities4 = 0x0C, + esrReadExtraCapabilities5 = 0x0D, + esrReadCoordinates = 0x0D, + esrReadExtraCapabilities6 = 0x0E, + esrReadExtraCapabilities7 = 0x0F, +}; + +enum EPS2DataPortStatus { + edpsAcknowledge = 0xFA, + edpsError = 0xFC, + edpsResend = 0xFE, + edpsTimeOut = 0x100 +}; + +enum ESetSampleRateSequence { + essrSetModeByte1 = 0x0A, + essrSetModeByte2 = 0x14, + essrSetModeByte3 = 0x28, + essrSetModeByte4 = 0x3C, + essrSetDeluxeModeByte1 = 0x0A, + essrSetDeluxeModeByte2 = 0x3C, + essrSetDeluxeModeByte3 = 0xC8, + essrFastRecalibrate = 0x50, + essrPassThroughCommandTunnel = 0x28 +}; + +enum EDeviceType { + edtUnknown, + edtTouchPad, +}; + +enum EStickDeviceType { + esdtNone = 0, + esdtIBM, + esdtJYTSyna = 5, + esdtSynaptics = 6, + esdtUnknown = 0xFFFFFFFF +}; + +static gboolean +fu_synaptics_rmi_ps2_device_read_ack(FuSynapticsRmiPs2Device *self, guint8 *pbuf, GError **error) +{ + for (guint i = 0; i < 60; i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_io_channel_read_raw(self->io_channel, + pbuf, + 0x1, + NULL, + 10, + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + &error_local)) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { + g_warning("read timed out: %u", i); + g_usleep(30); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + return TRUE; + } + g_set_error(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "read timed out"); + return FALSE; +} + +/* read a single byte from the touchpad */ +static gboolean +fu_synaptics_rmi_ps2_device_read_byte(FuSynapticsRmiPs2Device *self, + guint8 *pbuf, + guint timeout, + GError **error) +{ + g_return_val_if_fail(timeout > 0, FALSE); + return fu_io_channel_read_raw(self->io_channel, + pbuf, + 0x1, + NULL, + timeout, + FU_IO_CHANNEL_FLAG_NONE, + error); +} + +/* write a single byte to the touchpad and the read the acknowledge */ +static gboolean +fu_synaptics_rmi_ps2_device_write_byte(FuSynapticsRmiPs2Device *self, + guint8 buf, + guint timeout, + FuSynapticsRmiDeviceFlags flags, + GError **error) +{ + gboolean do_write = TRUE; + g_return_val_if_fail(timeout > 0, FALSE); + for (guint i = 0;; i++) { + guint8 res = 0; + g_autoptr(GError) error_local = NULL; + if (do_write) { + if (!fu_io_channel_write_raw(self->io_channel, + &buf, + sizeof(buf), + timeout, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) + return FALSE; + } + do_write = FALSE; + + for (;;) { + /* attempt to read acknowledge... */ + if (!fu_synaptics_rmi_ps2_device_read_ack(self, &res, &error_local)) { + if (i > 3) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "read ack failed: "); + return FALSE; + } + g_warning("read ack failed: %s, retrying", error_local->message); + break; + } + if (res == edpsAcknowledge) + return TRUE; + if (res == edpsResend) { + do_write = TRUE; + g_debug("resend"); + g_usleep(G_USEC_PER_SEC); + break; + } + if (res == edpsError) { + do_write = TRUE; + g_debug("error"); + g_usleep(1000 * 10); + break; + } + g_debug("other response: 0x%x", res); + g_usleep(1000 * 10); + } + if (i >= 3) { + if (flags & FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE) { + /* just break without error return because FW + * will not return ACK for commands like RESET */ + break; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot write byte after retries"); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_set_resolution_sequence(FuSynapticsRmiPs2Device *self, + guint8 arg, + gboolean send_e6s, + GError **error) +{ + /* send set scaling twice if send_e6s */ + for (gint i = send_e6s ? 2 : 1; i > 0; --i) { + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetScaling1To1, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) + return FALSE; + } + for (gint i = 3; i >= 0; --i) { + guint8 ucTwoBitArg = (arg >> (i * 2)) & 0x3; + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetResolution, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + ucTwoBitArg, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_status_request_sequence(FuSynapticsRmiPs2Device *self, + guint8 ucArgument, + guint32 *buf, + GError **error) +{ + gboolean success = FALSE; + + /* allow 3 retries */ + for (guint i = 0; i < 3; ++i) { + g_autoptr(GError) error_local = NULL; + if (!fu_synaptics_rmi_ps2_device_set_resolution_sequence(self, + ucArgument, + FALSE, + &error_local)) { + g_debug("failed set try #%u: %s", i, error_local->message); + continue; + } + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxStatusRequest, + 10, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + &error_local)) { + g_debug("failed write try #%u: %s", i, error_local->message); + continue; + } + success = TRUE; + break; + } + if (success == FALSE) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed"); + return FALSE; + } + + /* read the response from the status request */ + for (gint i = 0; i < 3; ++i) { + guint8 tmp = 0x0; + if (!fu_synaptics_rmi_ps2_device_read_byte(self, &tmp, 10, error)) { + g_prefix_error(error, "failed to read byte: "); + return FALSE; + } + *buf = ((*buf) << 8) | tmp; + } + + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_sample_rate_sequence(FuSynapticsRmiPs2Device *self, + guint8 param, + guint8 arg, + gboolean send_e6s, + GError **error) +{ + /* allow 3 retries */ + for (guint i = 0;; i++) { + g_autoptr(GError) error_local = NULL; + if (i > 0) { + /* always send two E6s when retrying */ + send_e6s = TRUE; + } + if (!fu_synaptics_rmi_ps2_device_set_resolution_sequence(self, + arg, + send_e6s, + &error_local) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetSampleRate, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + &error_local) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + param, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + &error_local)) { + if (i > 3) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + g_warning("failed, will retry: %s", error_local->message); + continue; + } + break; + } + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_detect_synaptics_styk(FuSynapticsRmiPs2Device *self, + gboolean *result, + GError **error) +{ + guint8 buf; + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxIBMReadSecondaryID, + 10, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write IBMReadSecondaryID(0xE1): "); + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_read_byte(self, &buf, 10, error)) { + g_prefix_error(error, "failed to receive IBMReadSecondaryID: "); + return FALSE; + } + if (buf == esdtJYTSyna || buf == esdtSynaptics) + *result = TRUE; + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_query_build_id(FuSynapticsRmiDevice *rmi_device, + guint32 *build_id, + GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + guint32 buf = 0; + gboolean is_synaptics_styk = FALSE; + ESynapticsDeviceResponse esdr; + + if (!fu_synaptics_rmi_ps2_device_status_request_sequence(self, + esrIdentifySynaptics, + &buf, + error)) { + g_prefix_error(error, "failed to request IdentifySynaptics: "); + return FALSE; + } + g_debug("identify Synaptics response = 0x%x", buf); + + esdr = (buf & 0xFF00) >> 8; + if (!fu_synaptics_rmi_ps2_device_detect_synaptics_styk(self, &is_synaptics_styk, error)) { + g_prefix_error(error, "failed to detect Synaptics styk: "); + return FALSE; + } + fu_synaptics_rmi_device_set_iepmode(rmi_device, FALSE); + if (esdr == esdrTouchPad || is_synaptics_styk) { + /* Get the firmware id from the Extra Capabilities 2 Byte + * The firmware id is located in bits 0 - 23 */ + if (!fu_synaptics_rmi_ps2_device_status_request_sequence(self, + esrReadExtraCapabilities2, + build_id, + error)) { + g_prefix_error(error, "failed to read extraCapabilities2: "); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_query_product_sub_id(FuSynapticsRmiDevice *rmi_device, + guint8 *sub_id, + GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + guint32 buf = 0; + if (!fu_synaptics_rmi_ps2_device_status_request_sequence(self, + esrReadCapabilities, + &buf, + error)) { + g_prefix_error(error, + "failed to status_request_sequence read esrReadCapabilities: "); + return FALSE; + } + *sub_id = (buf >> 8) & 0xFF; + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_enter_iep_mode(FuSynapticsRmiDevice *rmi_device, GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + + /* disable stream */ + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxDisable, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to disable stream mode: "); + return FALSE; + } + + /* enable RMI mode */ + if (!fu_synaptics_rmi_ps2_device_sample_rate_sequence(self, + essrSetModeByte2, + edpAuxFullRMIBackDoor, + FALSE, + error)) { + g_prefix_error(error, "failed to enter RMI mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_write_rmi_register(FuSynapticsRmiPs2Device *self, + guint8 addr, + const guint8 *buf, + guint8 buflen, + guint timeout, + FuSynapticsRmiDeviceFlags flags, + GError **error) +{ + g_return_val_if_fail(timeout > 0, FALSE); + + if (!fu_synaptics_rmi_device_enter_iep_mode(FU_SYNAPTICS_RMI_DEVICE(self), + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) + return FALSE; + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetScaling2To1, + timeout, + flags, + error)) { + g_prefix_error(error, "failed to edpAuxSetScaling2To1: "); + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetSampleRate, + timeout, + flags, + error)) { + g_prefix_error(error, "failed to edpAuxSetSampleRate: "); + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_write_byte(self, addr, timeout, flags, error)) { + g_prefix_error(error, "failed to write address: "); + return FALSE; + } + for (guint8 i = 0; i < buflen; i++) { + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetSampleRate, + timeout, + flags, + error)) { + g_prefix_error(error, "failed to set byte %u: ", i); + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_write_byte(self, buf[i], timeout, flags, error)) { + g_prefix_error(error, "failed to write byte %u: ", i); + return FALSE; + } + } + + /* success */ + g_usleep(1000 * 20); + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_read_rmi_register(FuSynapticsRmiPs2Device *self, + guint8 addr, + guint8 *buf, + GError **error) +{ + g_return_val_if_fail(buf != NULL, FALSE); + + if (!fu_synaptics_rmi_device_enter_iep_mode(FU_SYNAPTICS_RMI_DEVICE(self), + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) + return FALSE; + for (guint retries = 0;; retries++) { + g_autoptr(GError) error_local = NULL; + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetScaling2To1, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetSampleRate, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + addr, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxStatusRequest, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write command in Read RMI register: "); + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_read_byte(self, buf, 10, &error_local)) { + if (retries++ > 2) { + g_propagate_prefixed_error( + error, + g_steal_pointer(&error_local), + "failed to read byte @0x%x after %u retries: ", + addr, + retries); + return FALSE; + } + g_debug("failed to read byte @0x%x: %s", addr, error_local->message); + continue; + } + + /* success */ + break; + } + + /* success */ + g_usleep(1000 * 20); + return TRUE; +} + +static GByteArray * +fu_synaptics_rmi_ps2_device_read_rmi_packet_register(FuSynapticsRmiPs2Device *self, + guint8 addr, + guint req_sz, + GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + + if (!fu_synaptics_rmi_device_enter_iep_mode(FU_SYNAPTICS_RMI_DEVICE(self), + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) + return NULL; + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetScaling2To1, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxSetSampleRate, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + addr, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error) || + !fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxStatusRequest, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write command in Read RMI Packet Register: "); + return NULL; + } + for (guint i = 0; i < req_sz; ++i) { + guint8 tmp = 0; + if (!fu_synaptics_rmi_ps2_device_read_byte(self, &tmp, 10, error)) { + g_prefix_error(error, "failed to read byte %u: ", i); + return NULL; + } + fu_byte_array_append_uint8(buf, tmp); + } + + g_usleep(1000 * 20); + return g_steal_pointer(&buf); +} + +static gboolean +fu_synaptics_rmi_ps2_device_query_status(FuSynapticsRmiDevice *rmi_device, GError **error) +{ + FuSynapticsRmiFunction *f34; + g_debug("ps2 query status"); + f34 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error); + if (f34 == NULL) + return FALSE; + if (f34->function_version == 0x0 || f34->function_version == 0x1) { + return fu_synaptics_rmi_v5_device_query_status(rmi_device, error); + } + if (f34->function_version == 0x2) { + return fu_synaptics_rmi_v7_device_query_status(rmi_device, error); + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "f34 function version 0x%02x unsupported", + f34->function_version); + return FALSE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_set_page(FuSynapticsRmiDevice *rmi_device, guint8 page, GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + if (!fu_synaptics_rmi_ps2_device_write_rmi_register(self, + RMI_DEVICE_PAGE_SELECT_REGISTER, + &page, + 1, + 20, + FALSE, + error)) { + g_prefix_error(error, "failed to write page %u: ", page); + return FALSE; + } + return TRUE; +} + +static GByteArray * +fu_synaptics_rmi_ps2_device_read(FuSynapticsRmiDevice *rmi_device, + guint16 addr, + gsize req_sz, + GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + g_autoptr(GByteArray) buf = NULL; + g_autofree gchar *dump = NULL; + + if (!fu_synaptics_rmi_device_set_page(rmi_device, addr >> 8, error)) { + g_prefix_error(error, "failed to set RMI page:"); + return NULL; + } + + for (guint retries = 0;; retries++) { + buf = g_byte_array_new(); + for (guint i = 0; i < req_sz; i++) { + guint8 tmp = 0x0; + if (!fu_synaptics_rmi_ps2_device_read_rmi_register( + self, + (guint8)((addr & 0x00FF) + i), + &tmp, + error)) { + g_prefix_error(error, "failed register read 0x%x: ", addr + i); + return NULL; + } + fu_byte_array_append_uint8(buf, tmp); + } + if (buf->len != req_sz) { + g_debug("buf->len(%u) != req_sz(%u)", buf->len, (guint)req_sz); + if (retries++ > 2) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "buffer length did not match: %u vs %u", + buf->len, + (guint)req_sz); + return NULL; + } + continue; + } + break; + } + dump = g_strdup_printf("R %x", addr); + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, dump, buf->data, buf->len, 80, FU_DUMP_FLAGS_NONE); + } + return g_steal_pointer(&buf); +} + +static GByteArray * +fu_synaptics_rmi_ps2_device_read_packet_register(FuSynapticsRmiDevice *rmi_device, + guint16 addr, + gsize req_sz, + GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + g_autoptr(GByteArray) buf = NULL; + + if (!fu_synaptics_rmi_device_set_page(rmi_device, addr >> 8, error)) { + g_prefix_error(error, "failed to set RMI page:"); + return NULL; + } + + buf = fu_synaptics_rmi_ps2_device_read_rmi_packet_register(self, addr, req_sz, error); + if (buf == NULL) { + g_prefix_error(error, "failed packet register read %x: ", addr); + return NULL; + } + + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + g_autofree gchar *dump = g_strdup_printf("R %x", addr); + fu_dump_full(G_LOG_DOMAIN, dump, buf->data, buf->len, 80, FU_DUMP_FLAGS_NONE); + } + return g_steal_pointer(&buf); +} + +static gboolean +fu_synaptics_rmi_ps2_device_write(FuSynapticsRmiDevice *rmi_device, + guint16 addr, + GByteArray *req, + FuSynapticsRmiDeviceFlags flags, + GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(rmi_device); + if (!fu_synaptics_rmi_device_set_page(rmi_device, addr >> 8, error)) { + g_prefix_error(error, "failed to set RMI page: "); + return FALSE; + } + if (!fu_synaptics_rmi_ps2_device_write_rmi_register(self, + addr & 0x00FF, + req->data, + req->len, + 1000, /* timeout */ + flags, + error)) { + g_prefix_error(error, "failed to write register %x: ", addr); + return FALSE; + } + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + g_autofree gchar *str = g_strdup_printf("W %x", addr); + fu_dump_full(G_LOG_DOMAIN, str, req->data, req->len, 80, FU_DUMP_FLAGS_NONE); + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_write_bus_select(FuSynapticsRmiDevice *rmi_device, + guint8 bus, + GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + fu_byte_array_append_uint8(req, bus); + if (!fu_synaptics_rmi_ps2_device_write(rmi_device, + RMI_DEVICE_BUS_SELECT_REGISTER, + req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write rmi register %u: ", bus); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_probe(FuDevice *device, GError **error) +{ + /* psmouse is the usual mode, but serio is needed for update */ + if (g_strcmp0(fu_udev_device_get_driver(FU_UDEV_DEVICE(device)), "serio_raw") == 0) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "platform", error); +} + +static gboolean +fu_synaptics_rmi_ps2_device_open(FuDevice *device, GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(device); + guint8 buf[2] = {0x0}; + + /* FuUdevDevice->open */ + if (!FU_DEVICE_CLASS(fu_synaptics_rmi_ps2_device_parent_class)->open(device, error)) + return FALSE; + + /* create channel */ + self->io_channel = fu_io_channel_unix_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(device))); + + /* in serio_raw mode */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + /* clear out any data in the serio_raw queue */ + for (guint i = 0; i < 0xffff; i++) { + guint8 tmp = 0; + if (!fu_synaptics_rmi_ps2_device_read_byte(self, &tmp, 20, NULL)) + break; + } + + /* send reset -- may take 300-500ms */ + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxReset, + 600, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to reset: "); + return FALSE; + } + + /* read the 0xAA 0x00 announcing the touchpad is ready */ + if (!fu_synaptics_rmi_ps2_device_read_byte(self, &buf[0], 500, error) || + !fu_synaptics_rmi_ps2_device_read_byte(self, &buf[1], 500, error)) { + g_prefix_error(error, "failed to read 0xAA00: "); + return FALSE; + } + if (buf[0] != 0xAA || buf[1] != 0x00) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to read 0xAA00, got 0x%02X%02X: ", + buf[0], + buf[1]); + return FALSE; + } + + /* disable the device so that it stops reporting finger data */ + if (!fu_synaptics_rmi_ps2_device_write_byte(self, + edpAuxDisable, + 50, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to disable stream mode: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_close(FuDevice *device, GError **error) +{ + FuSynapticsRmiPs2Device *self = FU_SYNAPTICS_RMI_PS2_DEVICE(device); + fu_udev_device_set_fd(FU_UDEV_DEVICE(device), -1); + g_clear_object(&self->io_channel); + + /* FuUdevDevice->close */ + return FU_DEVICE_CLASS(fu_synaptics_rmi_ps2_device_parent_class)->close(device, error); +} + +static gboolean +fu_synaptics_rmi_ps2_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiFunction *f34; + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + + /* put in serio_raw mode so that we can do register writes */ + if (!fu_udev_device_write_sysfs(FU_UDEV_DEVICE(device), "drvctl", "serio_raw", error)) { + g_prefix_error(error, "failed to write to drvctl: "); + return FALSE; + } + + /* rescan device */ + if (!fu_device_close(device, error)) + return FALSE; + if (!fu_device_rescan(device, error)) + return FALSE; + if (!fu_device_open(device, error)) + return FALSE; + + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + if (f34->function_version == 0x0 || f34->function_version == 0x1) { + if (!fu_synaptics_rmi_v5_device_detach(device, progress, error)) + return FALSE; + } else if (f34->function_version == 0x2) { + if (!fu_synaptics_rmi_v7_device_detach(device, progress, error)) + return FALSE; + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "f34 function version 0x%02x unsupported", + f34->function_version); + return FALSE; + } + + /* set iepmode before querying device forcibly because of FW requirement */ + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + + if (!fu_synaptics_rmi_ps2_device_query_status(self, error)) { + g_prefix_error(error, "failed to query status after detach: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_ps2_device_setup(FuDevice *device, GError **error) +{ + /* we can only scan the PDT in serio_raw mode */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + return FU_DEVICE_CLASS(fu_synaptics_rmi_ps2_device_parent_class)->setup(device, error); +} + +static gboolean +fu_synaptics_rmi_ps2_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsRmiDevice *rmi_device = FU_SYNAPTICS_RMI_DEVICE(device); + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* set iepmode before reset device forcibly because of FW requirement */ + fu_synaptics_rmi_device_set_iepmode(rmi_device, FALSE); + + /* delay after writing */ + fu_progress_sleep(progress, 2000); + + /* reset device */ + if (!fu_synaptics_rmi_device_enter_iep_mode(rmi_device, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) + return FALSE; + if (!fu_synaptics_rmi_device_reset(rmi_device, error)) { + g_prefix_error(error, "failed to reset device: "); + return FALSE; + } + + /* delay after reset */ + fu_progress_sleep(progress, 5000); + + /* back to psmouse */ + if (!fu_udev_device_write_sysfs(FU_UDEV_DEVICE(device), "drvctl", "psmouse", error)) { + g_prefix_error(error, "failed to write to drvctl: "); + return FALSE; + } + + /* rescan device */ + return fu_device_rescan(device, error); +} + +static void +fu_synaptics_rmi_ps2_device_init(FuSynapticsRmiPs2Device *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_name(FU_DEVICE(self), "TouchStyk"); + fu_device_set_vendor(FU_DEVICE(self), "Synaptics"); + fu_device_add_vendor_id(FU_DEVICE(self), "HIDRAW:0x06CB"); + fu_synaptics_rmi_device_set_max_page(FU_SYNAPTICS_RMI_DEVICE(self), 0x1); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), + FU_UDEV_DEVICE_FLAG_OPEN_READ | FU_UDEV_DEVICE_FLAG_OPEN_WRITE); +} + +static gboolean +fu_synaptics_rmi_ps2_device_wait_for_attr(FuSynapticsRmiDevice *rmi_device, + guint8 source_mask, + guint timeout_ms, + GError **error) +{ + g_usleep(1000 * timeout_ms); + return TRUE; +} + +static void +fu_synaptics_rmi_ps2_device_class_init(FuSynapticsRmiPs2DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_CLASS(klass); + klass_device->attach = fu_synaptics_rmi_ps2_device_attach; + klass_device->detach = fu_synaptics_rmi_ps2_device_detach; + klass_device->setup = fu_synaptics_rmi_ps2_device_setup; + klass_device->probe = fu_synaptics_rmi_ps2_device_probe; + klass_device->open = fu_synaptics_rmi_ps2_device_open; + klass_device->close = fu_synaptics_rmi_ps2_device_close; + klass_rmi->read = fu_synaptics_rmi_ps2_device_read; + klass_rmi->write = fu_synaptics_rmi_ps2_device_write; + klass_rmi->set_page = fu_synaptics_rmi_ps2_device_set_page; + klass_rmi->query_status = fu_synaptics_rmi_ps2_device_query_status; + klass_rmi->query_build_id = fu_synaptics_rmi_ps2_device_query_build_id; + klass_rmi->query_product_sub_id = fu_synaptics_rmi_ps2_device_query_product_sub_id; + klass_rmi->wait_for_attr = fu_synaptics_rmi_ps2_device_wait_for_attr; + klass_rmi->enter_iep_mode = fu_synaptics_rmi_ps2_device_enter_iep_mode; + klass_rmi->write_bus_select = fu_synaptics_rmi_ps2_device_write_bus_select; + klass_rmi->read_packet_register = fu_synaptics_rmi_ps2_device_read_packet_register; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.h new file mode 100644 index 0000000000000000000000000000000000000000..c910c8c02d795e6b02c9590967c439d0cc394cc9 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Richard Hughes + * Copyright (c) 2020 Synaptics Incorporated. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-synaptics-rmi-device.h" + +#define FU_TYPE_SYNAPTICS_RMI_PS2_DEVICE (fu_synaptics_rmi_ps2_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSynapticsRmiPs2Device, + fu_synaptics_rmi_ps2_device, + FU, + SYNAPTICS_RMI_PS2_DEVICE, + FuSynapticsRmiDevice) diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c new file mode 100644 index 0000000000000000000000000000000000000000..fdb7bbd2c90fb557a4ac96f8f417f3c993cad232 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-rmi-firmware.h" +#include "fu-synaptics-rmi-v5-device.h" + +#define RMI_F34_WRITE_FW_BLOCK 0x02 +#define RMI_F34_ERASE_ALL 0x03 +#define RMI_F34_WRITE_LOCKDOWN_BLOCK 0x04 +#define RMI_F34_WRITE_CONFIG_BLOCK 0x06 +#define RMI_F34_WRITE_SIGNATURE 0x0b +#define RMI_F34_ENABLE_FLASH_PROG 0x0f + +#define RMI_F34_BLOCK_SIZE_OFFSET 1 +#define RMI_F34_FW_BLOCKS_OFFSET 3 +#define RMI_F34_CONFIG_BLOCKS_OFFSET 5 + +#define RMI_F34_ERASE_WAIT_MS (5 * 1000) /* ms */ + +gboolean +fu_synaptics_rmi_v5_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GByteArray) enable_req = g_byte_array_new(); + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + + /* disable interrupts */ + if (!fu_synaptics_rmi_device_disable_irqs(self, error)) + return FALSE; + if (!fu_synaptics_rmi_device_write_bus_select(self, 0, error)) { + g_prefix_error(error, "failed to write bus select: "); + return FALSE; + } + + /* unlock bootloader and rebind kernel driver */ + if (!fu_synaptics_rmi_device_write_bootloader_id(self, error)) + return FALSE; + fu_byte_array_append_uint8(enable_req, RMI_F34_ENABLE_FLASH_PROG); + if (!fu_synaptics_rmi_device_write(self, + flash->status_addr, + enable_req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to enable programming: "); + return FALSE; + } + + g_usleep(1000 * RMI_F34_ENABLE_WAIT_MS); + return TRUE; +} + +static gboolean +fu_synaptics_rmi_v5_device_erase_all(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFunction *f34; + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GByteArray) erase_cmd = g_byte_array_new(); + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* all other versions */ + fu_byte_array_append_uint8(erase_cmd, RMI_F34_ERASE_ALL); + if (!fu_synaptics_rmi_device_write(self, + flash->status_addr, + erase_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE, + error)) { + g_prefix_error(error, "failed to erase core config: "); + return FALSE; + } + g_usleep(1000 * RMI_F34_ERASE_WAIT_MS); + fu_synaptics_rmi_device_set_iepmode(self, FALSE); + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_ERASE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, + error)) { + g_prefix_error(error, "failed to wait for idle for erase: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_v5_device_write_block(FuSynapticsRmiDevice *self, + guint8 cmd, + guint32 address, + const guint8 *data, + gsize datasz, + GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new(); + + g_byte_array_append(req, data, datasz); + fu_byte_array_append_uint8(req, cmd); + if (!fu_synaptics_rmi_device_write(self, + address, + req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE, + error)) { + g_prefix_error(error, "failed to write block @0x%x: ", address); + return FALSE; + } + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_IDLE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to wait for idle @0x%x: ", address); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_v5_device_secure_check(FuDevice *device, + GBytes *payload, + GBytes *signature, + GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiFunction *f34; + guint16 rsa_pubkey_len = fu_synaptics_rmi_device_get_sig_size(self) / 8; + guint16 rsa_block_cnt = rsa_pubkey_len / 3; + guint16 rsa_block_remain = rsa_pubkey_len % 3; + g_autoptr(GByteArray) pubkey_buf = g_byte_array_new(); + g_autoptr(GBytes) pubkey = NULL; + + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "Signature", signature); + + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* parse RSA public key modulus */ + if (rsa_block_remain > 0) + rsa_block_cnt += 1; + for (guint retries = 0;; retries++) { + /* need read another register to reset the offset of packet register */ + if (!fu_synaptics_rmi_v5_device_query_status(self, error)) { + g_prefix_error(error, "failed to read status: "); + return FALSE; + } + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + for (guint16 block_num = 0; block_num < rsa_block_cnt; block_num++) { + g_autoptr(GByteArray) res = NULL; + res = fu_synaptics_rmi_device_read_packet_register( + self, + f34->query_base + 14, /* addr of flash properties + 5 */ + 0x3, + error); + if (res == NULL) + return FALSE; + if (res->len != 0x3) + g_debug("read %u bytes in return", res->len); + if (rsa_block_remain && block_num + 1 == rsa_block_cnt) { + g_byte_array_remove_range(res, + rsa_block_remain, + res->len - rsa_block_remain); + } + for (guint i = 0; i < res->len / 2; i++) { + guint8 tmp = res->data[i]; + res->data[i] = res->data[res->len - i - 1]; + res->data[res->len - i - 1] = tmp; + } + if (rsa_block_remain && block_num + 1 == rsa_block_cnt) { + g_byte_array_prepend(pubkey_buf, res->data, rsa_block_remain); + } else { + g_byte_array_prepend(pubkey_buf, res->data, res->len); + } + } + if (rsa_pubkey_len != pubkey_buf->len) { + if (retries++ > 2) { + g_set_error( + error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "RSA public key length not matched %u: after %u retries: ", + pubkey_buf->len, + retries); + return FALSE; + } + g_byte_array_set_size(pubkey_buf, 0); + continue; + } + + /* success */ + break; + } + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "RSA public key", + pubkey_buf->data, + pubkey_buf->len, + 16, + FU_DUMP_FLAGS_NONE); + } + + /* sanity check size */ + if (rsa_pubkey_len != pubkey_buf->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "RSA public key length did not match: %u != %u: ", + rsa_pubkey_len, + pubkey_buf->len); + return FALSE; + } + pubkey = g_bytes_new(pubkey_buf->data, pubkey_buf->len); + return fu_synaptics_verify_sha256_signature(payload, pubkey, signature, error); +} + +gboolean +fu_synaptics_rmi_v5_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + FuSynapticsRmiFirmware *rmi_firmware = FU_SYNAPTICS_RMI_FIRMWARE(firmware); + guint32 address; + guint32 firmware_length = + fu_firmware_get_size(firmware) - fu_synaptics_rmi_firmware_get_sig_size(rmi_firmware); + g_autoptr(GBytes) bytes_bin = NULL; + g_autoptr(GBytes) bytes_cfg = NULL; + g_autoptr(GBytes) signature_bin = NULL; + g_autoptr(GBytes) firmware_bin = NULL; + g_autoptr(GPtrArray) chunks_bin = NULL; + g_autoptr(GPtrArray) chunks_cfg = NULL; + g_autoptr(GByteArray) req_addr = g_byte_array_new(); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "enter-iep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "write-signature"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "cfg-image"); + + /* we should be in bootloader mode now, but check anyway */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not bootloader, perhaps need detach?!"); + return FALSE; + } + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + + /* check is idle */ + if (!fu_synaptics_rmi_device_wait_for_idle(self, + 0, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, + error)) { + g_prefix_error(error, "not idle: "); + return FALSE; + } + if (fu_synaptics_rmi_firmware_get_sig_size(rmi_firmware) == 0 && + fu_synaptics_rmi_device_get_sig_size(self) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "device secure but firmware not secure"); + return FALSE; + } + if (fu_synaptics_rmi_firmware_get_sig_size(rmi_firmware) != 0 && + fu_synaptics_rmi_device_get_sig_size(self) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "device not secure but firmware secure"); + return FALSE; + } + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* get both images */ + bytes_bin = fu_firmware_get_image_by_id_bytes(firmware, "ui", error); + if (bytes_bin == NULL) + return FALSE; + bytes_cfg = fu_firmware_get_image_by_id_bytes(firmware, "config", error); + if (bytes_cfg == NULL) + return FALSE; + + /* verify signature if set */ + firmware_bin = fu_bytes_new_offset(bytes_bin, 0, firmware_length, error); + if (firmware_bin == NULL) + return FALSE; + signature_bin = fu_firmware_get_image_by_id_bytes(firmware, "sig", NULL); + if (signature_bin != NULL) { + if (!fu_synaptics_rmi_v5_device_secure_check(device, + firmware_bin, + signature_bin, + error)) { + g_prefix_error(error, "secure check failed: "); + return FALSE; + } + } + + /* disable powersaving */ + if (!fu_synaptics_rmi_device_disable_sleep(self, error)) { + g_prefix_error(error, "failed to disable sleep: "); + return FALSE; + } + + /* unlock again */ + if (!fu_synaptics_rmi_device_write_bootloader_id(self, error)) { + g_prefix_error(error, "failed to unlock again: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* erase all */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_synaptics_rmi_v5_device_erase_all(self, error)) { + g_prefix_error(error, "failed to erase all: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* write initial address */ + fu_byte_array_append_uint16(req_addr, 0x0, G_LITTLE_ENDIAN); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base, + req_addr, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write 1st address zero: "); + return FALSE; + } + + /* write each block */ + if (f34->function_version == 0x01) + address = f34->data_base + RMI_F34_BLOCK_DATA_V1_OFFSET; + else + address = f34->data_base + RMI_F34_BLOCK_DATA_OFFSET; + chunks_bin = fu_chunk_array_new_from_bytes(firmware_bin, + 0x00, /* start addr */ + 0x00, /* page_sz */ + flash->block_size); + chunks_cfg = fu_chunk_array_new_from_bytes(bytes_cfg, + 0x00, /* start addr */ + 0x00, /* page_sz */ + flash->block_size); + for (guint i = 0; i < chunks_bin->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks_bin, i); + if (!fu_synaptics_rmi_v5_device_write_block(self, + RMI_F34_WRITE_FW_BLOCK, + address, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write bin block %u: ", + fu_chunk_get_idx(chk)); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks_bin->len); + } + fu_progress_step_done(progress); + + /* payload signature */ + if (signature_bin != NULL && fu_synaptics_rmi_device_get_sig_size(self) != 0) { + FuProgress *progress_child = fu_progress_get_child(progress); + g_autoptr(GPtrArray) chunks_sig = NULL; + chunks_sig = fu_chunk_array_new_from_bytes(signature_bin, + 0x00, /* start addr */ + 0x00, /* page_sz */ + flash->block_size); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base, + req_addr, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write 1st address zero: "); + return FALSE; + } + fu_progress_set_id(progress_child, G_STRLOC); + fu_progress_set_steps(progress_child, chunks_sig->len); + for (guint i = 0; i < chunks_sig->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks_sig, i); + if (!fu_synaptics_rmi_v5_device_write_block(self, + RMI_F34_WRITE_SIGNATURE, + address, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write bin block %u: ", + fu_chunk_get_idx(chk)); + return FALSE; + } + fu_progress_step_done(progress_child); + } + g_usleep(1000 * 1000); + } + fu_progress_step_done(progress); + + if (!fu_synaptics_rmi_device_enter_iep_mode(self, + FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, + error)) + return FALSE; + + /* program the configuration image */ + if (!fu_synaptics_rmi_device_write(self, + f34->data_base, + req_addr, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to 2nd write address zero: "); + return FALSE; + } + for (guint i = 0; i < chunks_cfg->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks_cfg, i); + if (!fu_synaptics_rmi_v5_device_write_block(self, + RMI_F34_WRITE_CONFIG_BLOCK, + address, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write cfg block %u: ", + fu_chunk_get_idx(chk)); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks_cfg->len); + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +gboolean +fu_synaptics_rmi_v5_device_setup(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFunction *f34; + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + guint8 flash_properties2 = 0; + g_autoptr(GByteArray) f34_data0 = NULL; + g_autoptr(GByteArray) f34_data2 = NULL; + g_autoptr(GByteArray) buf_flash_properties2 = NULL; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* get bootloader ID */ + f34_data0 = fu_synaptics_rmi_device_read(self, f34->query_base, 0x2, error); + if (f34_data0 == NULL) { + g_prefix_error(error, "failed to read bootloader ID: "); + return FALSE; + } + flash->bootloader_id[0] = f34_data0->data[0]; + flash->bootloader_id[1] = f34_data0->data[1]; + + /* get flash properties */ + buf_flash_properties2 = fu_synaptics_rmi_device_read(self, f34->query_base + 0x9, 1, error); + if (buf_flash_properties2 == NULL) { + g_prefix_error(error, "failed to read Flash Properties 2: "); + return FALSE; + } + if (!fu_memread_uint8_safe(buf_flash_properties2->data, + buf_flash_properties2->len, + 0x0, /* offset */ + &flash_properties2, + error)) { + g_prefix_error(error, "failed to parse Flash Properties 2: "); + return FALSE; + } + if (flash_properties2 & 0x01) { + guint16 sig_size = 0; + g_autoptr(GByteArray) buf_rsa_key = NULL; + buf_rsa_key = + fu_synaptics_rmi_device_read(self, f34->query_base + 0x9 + 0x1, 2, error); + if (buf_rsa_key == NULL) { + g_prefix_error(error, "failed to read RSA key length: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf_rsa_key->data, + buf_rsa_key->len, + 0x0, /* offset */ + &sig_size, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to parse RSA key length: "); + return FALSE; + } + fu_synaptics_rmi_device_set_sig_size(self, sig_size); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } else { + fu_synaptics_rmi_device_set_sig_size(self, 0); + } + + /* get flash properties */ + f34_data2 = fu_synaptics_rmi_device_read(self, f34->query_base + 0x2, 0x7, error); + if (f34_data2 == NULL) + return FALSE; + if (!fu_memread_uint16_safe(f34_data2->data, + f34_data2->len, + RMI_F34_BLOCK_SIZE_OFFSET, + &flash->block_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(f34_data2->data, + f34_data2->len, + RMI_F34_FW_BLOCKS_OFFSET, + &flash->block_count_fw, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(f34_data2->data, + f34_data2->len, + RMI_F34_CONFIG_BLOCKS_OFFSET, + &flash->block_count_cfg, + G_LITTLE_ENDIAN, + error)) + return FALSE; + flash->status_addr = f34->data_base + RMI_F34_BLOCK_DATA_OFFSET + flash->block_size; + return TRUE; +} + +gboolean +fu_synaptics_rmi_v5_device_query_status(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFunction *f01; + g_autoptr(GByteArray) f01_db = NULL; + + /* f01 */ + f01 = fu_synaptics_rmi_device_get_function(self, 0x01, error); + if (f01 == NULL) + return FALSE; + f01_db = fu_synaptics_rmi_device_read(self, f01->data_base, 0x1, error); + if (f01_db == NULL) { + g_prefix_error(error, "failed to read the f01 data base: "); + return FALSE; + } + if (f01_db->data[0] & 0x40) { + 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); + } + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h new file mode 100644 index 0000000000000000000000000000000000000000..674cabcbc38a75acb31c5f9438305660d9aad00b --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-synaptics-rmi-device.h" + +gboolean +fu_synaptics_rmi_v5_device_detach(FuDevice *device, FuProgress *progress, GError **error); +gboolean +fu_synaptics_rmi_v5_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +gboolean +fu_synaptics_rmi_v5_device_setup(FuSynapticsRmiDevice *self, GError **error); +gboolean +fu_synaptics_rmi_v5_device_query_status(FuSynapticsRmiDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c new file mode 100644 index 0000000000000000000000000000000000000000..4e51b218d3570814c4d846840456fc81d372a861 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-rmi-v6-device.h" + +#define RMI_F34_CONFIG_BLOCKS_OFFSET 2 + +gboolean +fu_synaptics_rmi_v6_device_setup(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + g_autoptr(GByteArray) f34_data0 = NULL; + g_autoptr(GByteArray) f34_data2 = NULL; + g_autoptr(GByteArray) f34_data3 = NULL; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* get bootloader ID */ + f34_data0 = fu_synaptics_rmi_device_read(self, f34->query_base, 0x2, error); + if (f34_data0 == NULL) { + g_prefix_error(error, "failed to read bootloader ID: "); + return FALSE; + } + if (!fu_memread_uint8_safe(f34_data0->data, + f34_data0->len, + 0x0, + &flash->bootloader_id[0], + error)) + return FALSE; + if (!fu_memread_uint8_safe(f34_data0->data, + f34_data0->len, + 0x1, + &flash->bootloader_id[1], + error)) + return FALSE; + + /* get flash properties */ + f34_data2 = fu_synaptics_rmi_device_read(self, f34->query_base + 0x02, 2, error); + if (f34_data2 == NULL) + return FALSE; + + if (!fu_memread_uint16_safe(f34_data2->data, + f34_data2->len, + 0x0, + &flash->block_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + f34_data3 = fu_synaptics_rmi_device_read(self, f34->query_base + 0x03, 8, error); + if (f34_data3 == NULL) + return FALSE; + if (!fu_memread_uint16_safe(f34_data3->data, + f34_data3->len, + 0x0, + &flash->block_count_fw, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(f34_data3->data, + f34_data3->len, + RMI_F34_CONFIG_BLOCKS_OFFSET, + &flash->block_count_cfg, + G_LITTLE_ENDIAN, + error)) + return FALSE; + flash->status_addr = f34->data_base + 2; + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.h new file mode 100644 index 0000000000000000000000000000000000000000..ddd3bd9c6b759b59a18b7898bbaa095b948cd97a --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-synaptics-rmi-device.h" + +gboolean +fu_synaptics_rmi_v6_device_setup(FuSynapticsRmiDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c new file mode 100644 index 0000000000000000000000000000000000000000..3d86bd2c713594c05a9eb911eddbedd7deaa51aa --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c @@ -0,0 +1,724 @@ +/* + * Copyright (C) 2012 Andrew Duggan + * Copyright (C) 2012 Synaptics Inc. + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-synaptics-rmi-v7-device.h" + +#define RMI_F34_ERASE_WAIT_MS 10000 /* ms */ + +typedef enum { + RMI_FLASH_CMD_IDLE = 0x00, + RMI_FLASH_CMD_ENTER_BL, + RMI_FLASH_CMD_READ, + RMI_FLASH_CMD_WRITE, + RMI_FLASH_CMD_ERASE, + RMI_FLASH_CMD_ERASE_AP, + RMI_FLASH_CMD_SENSOR_ID, +} RmiFlashCommand; + +typedef enum { + RMI_PARTITION_ID_NONE = 0x00, + RMI_PARTITION_ID_BOOTLOADER = 0x01, + RMI_PARTITION_ID_DEVICE_CONFIG, + RMI_PARTITION_ID_FLASH_CONFIG, + RMI_PARTITION_ID_MANUFACTURING_BLOCK, + RMI_PARTITION_ID_GUEST_SERIALIZATION, + RMI_PARTITION_ID_GLOBAL_PARAMETERS, + RMI_PARTITION_ID_CORE_CODE, + RMI_PARTITION_ID_CORE_CONFIG, + RMI_PARTITION_ID_GUEST_CODE, + RMI_PARTITION_ID_DISPLAY_CONFIG, + RMI_PARTITION_ID_EXTERNAL_TOUCH_AFE_CONFIG, + RMI_PARTITION_ID_UTILITY_PARAMETER, +} RmiPartitionId; + +static const gchar * +rmi_firmware_partition_id_to_string(RmiPartitionId partition_id) +{ + if (partition_id == RMI_PARTITION_ID_NONE) + return "none"; + if (partition_id == RMI_PARTITION_ID_BOOTLOADER) + return "bootloader"; + if (partition_id == RMI_PARTITION_ID_DEVICE_CONFIG) + return "device-config"; + if (partition_id == RMI_PARTITION_ID_FLASH_CONFIG) + return "flash-config"; + if (partition_id == RMI_PARTITION_ID_MANUFACTURING_BLOCK) + return "manufacturing-block"; + if (partition_id == RMI_PARTITION_ID_GUEST_SERIALIZATION) + return "guest-serialization"; + if (partition_id == RMI_PARTITION_ID_GLOBAL_PARAMETERS) + return "global-parameters"; + if (partition_id == RMI_PARTITION_ID_CORE_CODE) + return "core-code"; + if (partition_id == RMI_PARTITION_ID_CORE_CONFIG) + return "core-config"; + if (partition_id == RMI_PARTITION_ID_GUEST_CODE) + return "guest-code"; + if (partition_id == RMI_PARTITION_ID_DISPLAY_CONFIG) + return "display-config"; + if (partition_id == RMI_PARTITION_ID_EXTERNAL_TOUCH_AFE_CONFIG) + return "external-touch-afe-config"; + if (partition_id == RMI_PARTITION_ID_UTILITY_PARAMETER) + return "utility-parameter"; + return NULL; +} + +gboolean +fu_synaptics_rmi_v7_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + g_autoptr(GByteArray) enable_req = g_byte_array_new(); + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* disable interrupts */ + if (!fu_synaptics_rmi_device_disable_irqs(self, error)) + return FALSE; + + /* enter BL */ + fu_byte_array_append_uint8(enable_req, RMI_PARTITION_ID_BOOTLOADER); + fu_byte_array_append_uint32(enable_req, 0x0, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(enable_req, RMI_FLASH_CMD_ENTER_BL); + fu_byte_array_append_uint8(enable_req, flash->bootloader_id[0]); + fu_byte_array_append_uint8(enable_req, flash->bootloader_id[1]); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 1, + enable_req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to enable programming: "); + return FALSE; + } + + /* wait for idle */ + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_ENABLE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) + return FALSE; + if (!fu_synaptics_rmi_device_poll_wait(self, error)) + return FALSE; + g_usleep(1000 * RMI_F34_ENABLE_WAIT_MS); + return TRUE; +} + +static gboolean +fu_synaptics_rmi_v7_device_erase_all(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFunction *f34; + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GByteArray) erase_cmd = g_byte_array_new(); + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + fu_byte_array_append_uint8(erase_cmd, RMI_PARTITION_ID_CORE_CODE); + fu_byte_array_append_uint32(erase_cmd, 0x0, G_LITTLE_ENDIAN); + if (flash->bootloader_id[1] == 8) { + /* For bootloader v8 */ + fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE_AP); + } else { + /* For bootloader v7 */ + fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE); + } + fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[0]); + fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[1]); + /* for BL8 device, we need hold 1 seconds after querying F34 status to + * avoid not get attention by following giving erase command */ + if (flash->bootloader_id[1] == 8) + g_usleep(1000 * 1000); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 1, + erase_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to unlock erasing: "); + return FALSE; + } + g_usleep(1000 * 100); + if (flash->bootloader_id[1] == 8) { + /* wait for ATTN */ + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_ERASE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to wait for idle: "); + return FALSE; + } + } + if (!fu_synaptics_rmi_device_poll_wait(self, error)) { + g_prefix_error(error, "failed to get flash success: "); + return FALSE; + } + + /* for BL7, we need erase config partition */ + if (flash->bootloader_id[1] == 7) { + g_autoptr(GByteArray) erase_config_cmd = g_byte_array_new(); + + fu_byte_array_append_uint8(erase_config_cmd, RMI_PARTITION_ID_CORE_CONFIG); + fu_byte_array_append_uint32(erase_config_cmd, 0x0, G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(erase_config_cmd, RMI_FLASH_CMD_ERASE); + + g_usleep(1000 * 100); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 1, + erase_config_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to erase core config: "); + return FALSE; + } + + /* wait for ATTN */ + g_usleep(1000 * 100); + if (!fu_synaptics_rmi_device_wait_for_idle( + self, + RMI_F34_ERASE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, + error)) { + g_prefix_error(error, "failed to wait for idle: "); + return FALSE; + } + if (!fu_synaptics_rmi_device_poll_wait(self, error)) { + g_prefix_error(error, "failed to get flash success: "); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_synaptics_rmi_v7_device_write_blocks(FuSynapticsRmiDevice *self, + guint32 address, + const guint8 *data, + guint32 datasz, + GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GPtrArray) chunks = NULL; + + /* write FW blocks */ + chunks = fu_chunk_array_new(data, + datasz, + 0x00, /* start addr */ + 0x00, /* page_sz */ + flash->block_size); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) req = g_byte_array_new(); + g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + if (!fu_synaptics_rmi_device_write(self, + address, + req, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, + "failed to write block @0x%x:%x: ", + address, + fu_chunk_get_address(chk)); + return FALSE; + } + } + + /* wait for idle */ + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_IDLE_WAIT_MS, + RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to wait for idle @0x%x: ", address); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_rmi_v7_device_write_partition(FuSynapticsRmiDevice *self, + RmiPartitionId partition_id, + GBytes *bytes, + FuProgress *progress, + GError **error) +{ + FuSynapticsRmiFunction *f34; + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autoptr(GByteArray) req_offset = g_byte_array_new(); + g_autoptr(GByteArray) req_partition_id = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* write partition id */ + g_debug("writing partition %s…", rmi_firmware_partition_id_to_string(partition_id)); + fu_byte_array_append_uint8(req_partition_id, partition_id); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x1, + req_partition_id, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write flash partition: "); + return FALSE; + } + fu_byte_array_append_uint16(req_offset, 0x0, G_LITTLE_ENDIAN); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x2, + req_offset, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write offset: "); + return FALSE; + } + + /* write partition */ + chunks = + fu_chunk_array_new_from_bytes(bytes, + 0x00, /* start addr */ + 0x00, /* page_sz */ + (gsize)flash->payload_length * (gsize)flash->block_size); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) req_trans_sz = g_byte_array_new(); + g_autoptr(GByteArray) req_cmd = g_byte_array_new(); + fu_byte_array_append_uint16(req_trans_sz, + fu_chunk_get_data_sz(chk) / flash->block_size, + G_LITTLE_ENDIAN); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x3, + req_trans_sz, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write transfer length: "); + return FALSE; + } + fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_WRITE); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x4, + req_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to flash command: "); + return FALSE; + } + if (!fu_synaptics_rmi_v7_device_write_blocks(self, + f34->data_base + 0x5, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_step_done(progress); + } + return TRUE; +} + +gboolean +fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + g_autoptr(GBytes) bytes_bin = NULL; + g_autoptr(GBytes) bytes_cfg = NULL; + g_autoptr(GBytes) bytes_flashcfg = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "disable-sleep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 20, "flash-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 20, "core-code"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 20, "core-config"); + + /* we should be in bootloader mode now, but check anyway */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not bootloader, perhaps need detach?!"); + return FALSE; + } + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* get both images */ + bytes_bin = fu_firmware_get_image_by_id_bytes(firmware, "ui", error); + if (bytes_bin == NULL) + return FALSE; + bytes_cfg = fu_firmware_get_image_by_id_bytes(firmware, "config", error); + if (bytes_cfg == NULL) + return FALSE; + if (flash->bootloader_id[1] == 8) { + bytes_flashcfg = fu_firmware_get_image_by_id_bytes(firmware, "flash-config", error); + if (bytes_flashcfg == NULL) + return FALSE; + } + + /* disable powersaving */ + if (!fu_synaptics_rmi_device_disable_sleep(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* erase all */ + if (!fu_synaptics_rmi_v7_device_erase_all(self, error)) { + g_prefix_error(error, "failed to erase all: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* write flash config for v8 */ + if (bytes_flashcfg != NULL) { + if (!fu_synaptics_rmi_v7_device_write_partition(self, + RMI_PARTITION_ID_FLASH_CONFIG, + bytes_flashcfg, + fu_progress_get_child(progress), + error)) + return FALSE; + } + fu_progress_step_done(progress); + + /* write core code */ + if (!fu_synaptics_rmi_v7_device_write_partition(self, + RMI_PARTITION_ID_CORE_CODE, + bytes_bin, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* write core config */ + if (!fu_synaptics_rmi_v7_device_write_partition(self, + RMI_PARTITION_ID_CORE_CONFIG, + bytes_cfg, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +typedef struct __attribute__((packed)) { + guint16 partition_id; + guint16 partition_len; + guint16 partition_addr; + guint16 partition_prop; +} RmiPartitionTbl; + +G_STATIC_ASSERT(sizeof(RmiPartitionTbl) == 8); + +static gboolean +fu_synaptics_rmi_device_read_flash_config_v7(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + g_autoptr(GByteArray) req_addr_zero = g_byte_array_new(); + g_autoptr(GByteArray) req_cmd = g_byte_array_new(); + g_autoptr(GByteArray) req_partition_id = g_byte_array_new(); + g_autoptr(GByteArray) req_transfer_length = g_byte_array_new(); + g_autoptr(GByteArray) res = NULL; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* set partition id for bootloader 7 */ + fu_byte_array_append_uint8(req_partition_id, RMI_PARTITION_ID_FLASH_CONFIG); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x1, + req_partition_id, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write flash partition id: "); + return FALSE; + } + fu_byte_array_append_uint16(req_addr_zero, 0x0, G_LITTLE_ENDIAN); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x2, + req_addr_zero, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write flash config address: "); + return FALSE; + } + + /* set transfer length */ + fu_byte_array_append_uint16(req_transfer_length, flash->config_length, G_LITTLE_ENDIAN); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x3, + req_transfer_length, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to set transfer length: "); + return FALSE; + } + + /* set command to read */ + fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_READ); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 0x4, + req_cmd, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write command to read: "); + return FALSE; + } + if (!fu_synaptics_rmi_device_poll_wait(self, error)) { + g_prefix_error(error, "failed to wait: "); + return FALSE; + } + + /* read back entire buffer in blocks */ + res = + fu_synaptics_rmi_device_read(self, + f34->data_base + 0x5, + (guint32)flash->block_size * (guint32)flash->config_length, + error); + if (res == NULL) { + g_prefix_error(error, "failed to read: "); + return FALSE; + } + + /* debugging */ + if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) { + fu_dump_full(G_LOG_DOMAIN, + "FlashConfig", + res->data, + res->len, + 80, + FU_DUMP_FLAGS_NONE); + } + + /* parse the config length */ + for (guint i = 0x2; i < res->len; i += sizeof(RmiPartitionTbl)) { + RmiPartitionTbl tbl; + if (!fu_memcpy_safe((guint8 *)&tbl, + sizeof(tbl), + 0x0, /* dst */ + res->data, + res->len, + i, /* src */ + sizeof(tbl), + error)) + return FALSE; + g_debug("found partition %s (0x%02x)", + rmi_firmware_partition_id_to_string(tbl.partition_id), + tbl.partition_id); + if (tbl.partition_id == RMI_PARTITION_ID_CORE_CONFIG) { + flash->block_count_cfg = tbl.partition_len; + continue; + } + if (tbl.partition_id == RMI_PARTITION_ID_CORE_CODE) { + flash->block_count_fw = tbl.partition_len; + continue; + } + if (tbl.partition_id == RMI_PARTITION_ID_NONE) + break; + } + + /* success */ + return TRUE; +} + +gboolean +fu_synaptics_rmi_v7_device_setup(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + guint8 offset; + g_autoptr(GByteArray) f34_data0 = NULL; + g_autoptr(GByteArray) f34_dataX = NULL; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + f34_data0 = fu_synaptics_rmi_device_read(self, f34->query_base, 1, error); + if (f34_data0 == NULL) { + g_prefix_error(error, "failed to read bootloader ID: "); + return FALSE; + } + offset = (f34_data0->data[0] & 0b00000111) + 1; + f34_dataX = fu_synaptics_rmi_device_read(self, f34->query_base + offset, 21, error); + if (f34_dataX == NULL) + return FALSE; + if (!fu_memread_uint8_safe(f34_dataX->data, + f34_dataX->len, + 0x0, + &flash->bootloader_id[0], + error)) + return FALSE; + if (!fu_memread_uint8_safe(f34_dataX->data, + f34_dataX->len, + 0x1, + &flash->bootloader_id[1], + error)) + return FALSE; + if (!fu_memread_uint16_safe(f34_dataX->data, + f34_dataX->len, + 0x07, + &flash->block_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(f34_dataX->data, + f34_dataX->len, + 0x0d, + &flash->config_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(f34_dataX->data, + f34_dataX->len, + 0x0f, + &flash->payload_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(f34_dataX->data, + f34_dataX->len, + 0x02, + &flash->build_id, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* sanity check */ + if ((guint32)flash->block_size * (guint32)flash->config_length > G_MAXUINT16) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "block size 0x%x or config length 0x%x invalid", + flash->block_size, + flash->config_length); + return FALSE; + } + + /* read flash config */ + return fu_synaptics_rmi_device_read_flash_config_v7(self, error); +} + +gboolean +fu_synaptics_rmi_v7_device_query_status(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFunction *f34; + guint8 status; + g_autoptr(GByteArray) f34_data = NULL; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + f34_data = fu_synaptics_rmi_device_read(self, f34->data_base, 0x1, error); + if (f34_data == NULL) { + g_prefix_error(error, "failed to read the f01 data base: "); + return FALSE; + } + status = f34_data->data[0]; + if (status & 0x80) { + 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); + } + if (status == 0x01) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "operation only supported in bootloader mode"); + return FALSE; + } + if (status == 0x02) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "partition ID is not supported by the bootloader"); + return FALSE; + } + if (status == 0x03) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "partition supported, but command not supported"); + return FALSE; + } + if (status == 0x04) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid block offset"); + return FALSE; + } + if (status == 0x05) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid transfer"); + return FALSE; + } + if (status == 0x06) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "partition has not been erased"); + return FALSE; + } + if (status == 0x07) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "flash programming key incorrect"); + return FALSE; + } + if (status == 0x08) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "bad partition table"); + return FALSE; + } + if (status == 0x09) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "transfer checksum failed"); + return FALSE; + } + if (status == 0x1f) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "flash hardware failure"); + return FALSE; + } + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h new file mode 100644 index 0000000000000000000000000000000000000000..05ed595972744e342965870da545cd2d4637739e --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-synaptics-rmi-device.h" + +gboolean +fu_synaptics_rmi_v7_device_detach(FuDevice *device, FuProgress *progress, GError **error); +gboolean +fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +gboolean +fu_synaptics_rmi_v7_device_setup(FuSynapticsRmiDevice *self, GError **error); +gboolean +fu_synaptics_rmi_v7_device_query_status(FuSynapticsRmiDevice *self, GError **error); diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/meson.build b/fwupd-1.8.6/plugins/synaptics-rmi/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f8097438a8ed2cfd52e63b8cbb41432f232ef4ca --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/meson.build @@ -0,0 +1,50 @@ +if get_option('plugin_synaptics_rmi').require(gnutls.found(), + error_message: 'gnutls is needed for plugin_synaptics_rmi').allowed() and \ + get_option('plugin_synaptics_rmi').require(gudev.found(), + error_message: 'gudev is needed for plugin_synaptics_rmi').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsRmi"'] + +plugin_quirks += files('synaptics-rmi.quirk') +plugin_builtin_synaptics_rmi = static_library('fu_plugin_synaptics_rmi', + sources: [ + 'fu-synaptics-rmi-plugin.c', + 'fu-synaptics-rmi-common.c', # fuzzing + 'fu-synaptics-rmi-device.c', + 'fu-synaptics-rmi-hid-device.c', + 'fu-synaptics-rmi-ps2-device.c', + 'fu-synaptics-rmi-v5-device.c', + 'fu-synaptics-rmi-v6-device.c', + 'fu-synaptics-rmi-v7-device.c', + 'fu-synaptics-rmi-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: plugin_deps, + link_with: plugin_libs, +) +plugin_builtins += plugin_builtin_synaptics_rmi + +if get_option('tests') + install_data(['tests/synaptics-rmi-0x.builder.xml','tests/synaptics-rmi-10.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'synaptics-rmi-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_synaptics_rmi, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('synaptics-rmi-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/synaptics-rmi.quirk b/fwupd-1.8.6/plugins/synaptics-rmi/synaptics-rmi.quirk new file mode 100644 index 0000000000000000000000000000000000000000..2179bd92160cbc771bdd63120c81340b38ee0a6d --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/synaptics-rmi.quirk @@ -0,0 +1,17 @@ +# Dell K12A kbd-dock +[HIDRAW\VEN_06CB&DEV_2819] +Plugin = synaptics_rmi +GType = FuSynapticsRmiHidDevice +Vendor = Synaptics +Flags = only-supported + +# LENOVO X1 Nano +[SERIO\FWID_LEN0305-PNP0F13] +Plugin = synaptics_rmi +GType = FuSynapticsRmiPs2Device + +# LENOVO X1 Panther +[SERIO\FWID_LEN0306-PNP0F13] +Plugin = synaptics_rmi +GType = FuSynapticsRmiPs2Device +InstallDuration = 236 diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-0x.bin b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-0x.bin new file mode 100644 index 0000000000000000000000000000000000000000..e6303a2006d9838d4f4a7352c61313d405082dbc Binary files /dev/null and b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-0x.bin differ diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-0x.builder.xml b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-0x.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..dc03811993823d222a5570a648ef784194cf4fd0 --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-0x.builder.xml @@ -0,0 +1,8 @@ + + 0x01 + Example + + ui + ZGF2ZQ== + + diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-10.bin b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-10.bin new file mode 100644 index 0000000000000000000000000000000000000000..4bf9c4b4e87d39312e2e70395d278937c00a7b86 Binary files /dev/null and b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-10.bin differ diff --git a/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-10.builder.xml b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-10.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..25baddc3ccc1d0a1491e815eb0a2aebafb47984f --- /dev/null +++ b/fwupd-1.8.6/plugins/synaptics-rmi/tests/synaptics-rmi-10.builder.xml @@ -0,0 +1,8 @@ + + 0x10 + Example + + ui + ZGF2ZQ== + + diff --git a/fwupd-1.8.6/plugins/system76-launch/README.md b/fwupd-1.8.6/plugins/system76-launch/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4e971e849292bd7b10e1bfffc37fe8c98d108152 --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/README.md @@ -0,0 +1,35 @@ +# System76 Launch + +## Introduction + +This plugin is used to detach the System76 Launch device to DFU mode. + +To switch to bootloader mode a USB packet must be written, as specified by the +System76 EC protocol. + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_3384&PID_0001&REV_0001` +* `USB\VID_3384&PID_0005&REV_0001` +* `USB\VID_3384&PID_0006&REV_0001` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different USB VID and PID in DFU mode. The device is then handled by the `dfu` +plugin. + +On DFU attach the device again re-enumerates back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x3384` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-device.c b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-device.c new file mode 100644 index 0000000000000000000000000000000000000000..990b1c0b91ba71bea0a9c18b2b2cf9b7b69430b3 --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-device.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2021 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-system76-launch-device.h" + +#define SYSTEM76_LAUNCH_CMD_VERSION 3 +#define SYSTEM76_LAUNCH_CMD_RESET 6 +#define SYSTEM76_LAUNCH_TIMEOUT 1000 + +struct _FuSystem76LaunchDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSystem76LaunchDevice, fu_system76_launch_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_system76_launch_device_command(FuDevice *device, guint8 *data, gsize len, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + const guint8 ep_in = 0x82; + const guint8 ep_out = 0x03; + gsize actual_len = 0; + + /* send command */ + if (!g_usb_device_interrupt_transfer(usb_device, + ep_out, + data, + len, + &actual_len, + SYSTEM76_LAUNCH_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to send command: "); + return FALSE; + } + if (actual_len < len) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "command truncated: sent %" G_GSIZE_FORMAT " bytes", + actual_len); + return FALSE; + } + + /* receive response */ + if (!g_usb_device_interrupt_transfer(usb_device, + ep_in, + data, + len, + &actual_len, + SYSTEM76_LAUNCH_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read response: "); + return FALSE; + } + if (actual_len < len) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "response truncated: received %" G_GSIZE_FORMAT " bytes", + actual_len); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_system76_launch_device_version_cb(FuDevice *device, gpointer user_data, GError **error) +{ + guint8 data[32] = {SYSTEM76_LAUNCH_CMD_VERSION, 0}; + g_autofree gchar *version = NULL; + + /* execute version command */ + if (!fu_system76_launch_device_command(device, data, sizeof(data), error)) { + g_prefix_error(error, "failed to execute version command: "); + return FALSE; + } + + version = g_strdup_printf("%s", &data[2]); + fu_device_set_version(device, version); + + return TRUE; +} + +static gboolean +fu_system76_launch_device_setup(FuDevice *device, GError **error) +{ + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_system76_launch_device_parent_class)->setup(device, error)) + return FALSE; + + /* set version */ + return fu_device_retry_full(device, + fu_system76_launch_device_version_cb, + 5, + 500, + NULL, + error); +} + +static gboolean +fu_system76_launch_device_reset(FuDevice *device, guint8 *rc, GError **error) +{ + guint8 data[32] = {SYSTEM76_LAUNCH_CMD_RESET, 0}; + + /* execute reset command */ + if (!fu_system76_launch_device_command(device, data, sizeof(data), error)) { + g_prefix_error(error, "failed to execute reset command: "); + return FALSE; + } + + *rc = data[1]; + return TRUE; +} + +static gboolean +fu_system76_launch_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + guint8 rc = 0x0; + g_autoptr(FwupdRequest) request = fwupd_request_new(); + g_autoptr(GTimer) timer = g_timer_new(); + + /* prompt for unlock if reset was blocked */ + if (!fu_system76_launch_device_reset(device, &rc, error)) + return FALSE; + + /* unlikely, but already unlocked */ + if (rc == 0) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + /* generate a message if not already set */ + if (fu_device_get_update_message(device) == NULL) { + g_autofree gchar *msg = NULL; + const gchar *unlock_keys; + switch (fu_usb_device_get_pid(FU_USB_DEVICE(device))) { + case 0x0001: /* launch_1 */ + unlock_keys = "Fn+Esc"; + break; + default: + unlock_keys = "Left Ctrl+Right Ctrl+Esc"; + break; + } + msg = g_strdup_printf( + "To ensure you have physical access, %s needs to be manually unlocked. " + "Please press %s to unlock and re-run the update.", + fu_device_get_name(device), + unlock_keys); + fu_device_set_update_message(device, msg); + } + + /* the user has to do something */ + fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE); + fwupd_request_set_id(request, FWUPD_REQUEST_ID_PRESS_UNLOCK); + fwupd_request_set_message(request, fu_device_get_update_message(device)); + fu_device_emit_request(device, request); + + /* poll for the user-unlock */ + do { + g_usleep(G_USEC_PER_SEC); + if (!fu_system76_launch_device_reset(device, &rc, error)) + return FALSE; + } while (rc != 0 && + g_timer_elapsed(timer, NULL) * 1000.f < FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + if (rc != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NEEDS_USER_ACTION, + fu_device_get_update_message(device)); + return FALSE; + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_system76_launch_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 30, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 40, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 25, "reload"); +} + +static void +fu_system76_launch_device_init(FuSystem76LaunchDevice *self) +{ + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_protocol(FU_DEVICE(self), "org.usb.dfu"); + fu_device_retry_set_delay(FU_DEVICE(self), 100); + fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x01); +} + +static void +fu_system76_launch_device_class_init(FuSystem76LaunchDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_system76_launch_device_setup; + klass_device->detach = fu_system76_launch_device_detach; + klass_device->set_progress = fu_system76_launch_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-device.h b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-device.h new file mode 100644 index 0000000000000000000000000000000000000000..161fc138df224cae42c016e2db49d640727ad072 --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2021 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_SYSTEM76_LAUNCH_DEVICE (fu_system76_launch_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSystem76LaunchDevice, + fu_system76_launch_device, + FU, + SYSTEM76_LAUNCH_DEVICE, + FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-plugin.c b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..1857feb050912ce7b6fd48b4013db0408d2deae1 --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2021 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-system76-launch-device.h" +#include "fu-system76-launch-plugin.h" + +struct _FuSystem76LaunchPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuSystem76LaunchPlugin, fu_system76_launch_plugin, FU_TYPE_PLUGIN) + +static void +fu_system76_launch_plugin_init(FuSystem76LaunchPlugin *self) +{ +} + +static void +fu_system76_launch_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SYSTEM76_LAUNCH_DEVICE); +} + +static void +fu_system76_launch_plugin_class_init(FuSystem76LaunchPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_system76_launch_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-plugin.h b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..e489b43297bed015983299bbd04be774f9e8e1aa --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/fu-system76-launch-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuSystem76LaunchPlugin, + fu_system76_launch_plugin, + FU, + SYSTEM76_LAUNCH_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/system76-launch/meson.build b/fwupd-1.8.6/plugins/system76-launch/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..4e194aebc86dd50a9748746331b0f8f11f529aee --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/meson.build @@ -0,0 +1,15 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginSystem76Launch"'] + +plugin_quirks += files('system76-launch.quirk') +plugin_builtins += static_library('fu_plugin_system76_launch', + sources: [ + 'fu-system76-launch-plugin.c', + 'fu-system76-launch-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/system76-launch/system76-launch.quirk b/fwupd-1.8.6/plugins/system76-launch/system76-launch.quirk new file mode 100644 index 0000000000000000000000000000000000000000..a5bc2ef708bb7ae5d5768e63659c936c879fadd5 --- /dev/null +++ b/fwupd-1.8.6/plugins/system76-launch/system76-launch.quirk @@ -0,0 +1,14 @@ +# System76 launch_1 +[USB\VID_3384&PID_0001&REV_0001] +Plugin = system76_launch +CounterpartGuid = USB\VID_03EB&PID_2FF4 + +# System76 launch_lite_1 +[USB\VID_3384&PID_0005&REV_0001] +Plugin = system76_launch +CounterpartGuid = USB\VID_03EB&PID_2FF9 + +# System76 launch_2 +[USB\VID_3384&PID_0006&REV_0001] +Plugin = system76_launch +CounterpartGuid = USB\VID_03EB&PID_2FF9 diff --git a/fwupd-1.8.6/plugins/test/README.md b/fwupd-1.8.6/plugins/test/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a2125918e1e8e002e3c31334358b3018b27bdfd7 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/README.md @@ -0,0 +1,22 @@ +# Test + +## Introduction + +This plugin is used when running the self tests in the fwupd project. + +## GUID Generation + +The devices created by this plugin use hardcoded GUIDs that do not correspond +to any kind of DeviceInstanceId values. + +In other cases devices use the standard BLE DeviceInstanceId values, e.g. + +* `USB\VID_2DC8&PID_AB11` + +## Vendor ID Security + +The fake device is only for local testing and thus requires no vendor ID set. + +## External Interface Access + +This plugin requires no extra access. diff --git a/fwupd-1.8.6/plugins/test/fu-test-ble-device.c b/fwupd-1.8.6/plugins/test/fu-test-ble-device.c new file mode 100644 index 0000000000000000000000000000000000000000..5188cf9333cdac753f12898a2ee0dfc0a9b3aa57 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/fu-test-ble-device.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-test-ble-device.h" + +struct _FuTestBleDevice { + FuBluezDevice parent_instance; +}; + +G_DEFINE_TYPE(FuTestBleDevice, fu_test_ble_device, FU_TYPE_BLUEZ_DEVICE) + +static void +fu_test_ble_device_init(FuTestBleDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "org.test.testble"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); +} + +static void +fu_test_ble_device_class_init(FuTestBleDeviceClass *klass) +{ +} diff --git a/fwupd-1.8.6/plugins/test/fu-test-ble-device.h b/fwupd-1.8.6/plugins/test/fu-test-ble-device.h new file mode 100644 index 0000000000000000000000000000000000000000..b6137cac9c8842fdcc2ae757e858b569a6c947b1 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/fu-test-ble-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_TEST_BLE_DEVICE (fu_test_ble_device_get_type()) +G_DECLARE_FINAL_TYPE(FuTestBleDevice, fu_test_ble_device, FU, TEST_BLE_DEVICE, FuBluezDevice) diff --git a/fwupd-1.8.6/plugins/test/fu-test-ble-plugin.c b/fwupd-1.8.6/plugins/test/fu-test-ble-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..58b557a51a77685395828ea275f9927f01a60e64 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/fu-test-ble-plugin.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-test-ble-device.h" +#include "fu-test-ble-plugin.h" + +struct _FuTestBlePlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuTestBlePlugin, fu_test_ble_plugin, FU_TYPE_PLUGIN) + +static void +fu_test_ble_plugin_init(FuTestBlePlugin *self) +{ +} + +static void +fu_xxx_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_TEST_BLE_DEVICE); +} + +static void +fu_test_ble_plugin_class_init(FuTestBlePluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_xxx_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/test/fu-test-ble-plugin.h b/fwupd-1.8.6/plugins/test/fu-test-ble-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..7f2796c979a0c3c6b4213e41cf43cc87ab852441 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/fu-test-ble-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuTestBlePlugin, fu_test_ble_plugin, FU, TEST_BLE_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/test/fu-test-plugin.c b/fwupd-1.8.6/plugins/test/fu-test-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..4dedcade09006a59df11e813c7b3614d5d3d7915 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/fu-test-plugin.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-test-plugin.h" + +struct _FuTestPlugin { + FuPlugin parent_instance; + guint delay_decompress_ms; + guint delay_write_ms; + guint delay_verify_ms; +}; + +G_DEFINE_TYPE(FuTestPlugin, fu_test_plugin, FU_TYPE_PLUGIN) + +static void +fu_test_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuTestPlugin *self = FU_TEST_PLUGIN(plugin); + fu_string_append_ku(str, idt, "DelayDecompressMs", self->delay_decompress_ms); + fu_string_append_ku(str, idt, "DelayWriteMs", self->delay_write_ms); + fu_string_append_ku(str, idt, "DelayVerifyMs", self->delay_verify_ms); +} + +static gboolean +fu_test_plugin_load_xml(FuPlugin *plugin, const gchar *xml, GError **error) +{ + FuTestPlugin *self = FU_TEST_PLUGIN(plugin); + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbNode) delay_decompress_ms = NULL; + g_autoptr(XbNode) delay_verify_ms = NULL; + g_autoptr(XbNode) delay_write_ms = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* build silo */ + if (!xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) + return FALSE; + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + + /* parse markup */ + delay_decompress_ms = xb_silo_query_first(silo, "config/delay_decompress_ms", NULL); + if (delay_decompress_ms != NULL) + self->delay_decompress_ms = xb_node_get_text_as_uint(delay_decompress_ms); + delay_write_ms = xb_silo_query_first(silo, "config/delay_write_ms", NULL); + if (delay_write_ms != NULL) + self->delay_write_ms = xb_node_get_text_as_uint(delay_write_ms); + delay_verify_ms = xb_silo_query_first(silo, "config/delay_verify_ms", NULL); + if (delay_verify_ms != NULL) + self->delay_verify_ms = xb_node_get_text_as_uint(delay_verify_ms); + + /* success */ + return TRUE; +} + +static gboolean +fu_test_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + const gchar *xml = g_getenv("FWUPD_TEST_PLUGIN_XML"); + if (xml != NULL) { + if (!fu_test_plugin_load_xml(plugin, xml, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_test_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(FuDevice) device = NULL; + device = fu_device_new(ctx); + fu_device_set_id(device, "FakeDevice"); + fu_device_add_guid(device, "b585990a-003e-5270-89d5-3705a17f9a43"); + fu_device_set_name(device, "Integrated_Webcam(TM)"); + fu_device_add_icon(device, "preferences-desktop-keyboard"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(device, "com.acme.test"); + fu_device_set_summary(device, "Fake webcam"); + fu_device_set_vendor(device, "ACME Corp."); + fu_device_add_vendor_id(device, "USB:0x046D"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_bootloader(device, "0.1.2"); + fu_device_set_version(device, "1.2.2"); + fu_device_set_version_lowest(device, "1.2.0"); + if (g_strcmp0(g_getenv("FWUPD_PLUGIN_TEST"), "registration") == 0) { + fu_plugin_device_register(plugin, device); + if (fu_device_get_metadata(device, "BestDevice") == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "Device not set by another plugin"); + return FALSE; + } + } + 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(ctx); + fu_device_add_vendor_id(child1, "USB:FFFF"); + fu_device_add_protocol(child1, "com.acme"); + 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_format(child1, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(child1, "1"); + fu_device_add_parent_guid(child1, "b585990a-003e-5270-89d5-3705a17f9a43"); + fu_device_add_flag(child1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(child1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_plugin_device_add(plugin, child1); + + child2 = fu_device_new(ctx); + fu_device_add_vendor_id(child2, "USB:FFFF"); + fu_device_add_protocol(child2, "com.acme"); + 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_format(child2, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(child2, "10"); + fu_device_add_parent_guid(child2, "b585990a-003e-5270-89d5-3705a17f9a43"); + fu_device_add_flag(child2, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(child2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_plugin_device_add(plugin, child2); + } + + return TRUE; +} + +static void +fu_test_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + fu_device_set_metadata(device, "BestDevice", "/dev/urandom"); +} + +static gboolean +fu_test_plugin_verify(FuPlugin *plugin, + FuDevice *device, + FuProgress *progress, + FuPluginVerifyFlags flags, + GError **error) +{ + if (g_strcmp0(fu_device_get_version(device), "1.2.2") == 0) { + fu_device_add_checksum(device, "90d0ad436d21e0687998cd2127b2411135e1f730"); + fu_device_add_checksum( + device, + "921631916a60b295605dbae6a0309f9b64e2401b3de8e8506e109fc82c586e3a"); + return TRUE; + } + if (g_strcmp0(fu_device_get_version(device), "1.2.3") == 0) { + fu_device_add_checksum(device, "7998cd212721e068b2411135e1f90d0ad436d730"); + fu_device_add_checksum( + device, + "dbae6a0309b3de8e850921631916a60b2956056e109fc82c586e3f9b64e2401a"); + return TRUE; + } + if (g_strcmp0(fu_device_get_version(device), "1.2.4") == 0) { + fu_device_add_checksum(device, "2b8546ba805ad10bf8a2e5ad539d53f303812ba5"); + fu_device_add_checksum( + device, + "b546c241029ce4e16c99eb6bfd77b86e4490aa3826ba71b8a4114e96a2d69bcd"); + return TRUE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no checksum for %s", + fu_device_get_version(device)); + return FALSE; +} + +static gchar * +fu_test_plugin_get_version(GBytes *blob_fw) +{ + const gchar *str = g_bytes_get_data(blob_fw, NULL); + guint64 val = 0; + g_autoptr(GError) error_local = NULL; + + if (str == NULL) + return NULL; + if (!fu_strtoull(str, &val, 0, G_MAXUINT32, &error_local)) { + g_debug("invalid version specified: %s", error_local->message); + return NULL; + } + if (val == 0x0) + return NULL; + return fu_version_from_uint32(val, FWUPD_VERSION_FORMAT_TRIPLET); +} + +static gboolean +fu_test_plugin_write_firmware(FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuTestPlugin *self = FU_TEST_PLUGIN(plugin); + const gchar *test = g_getenv("FWUPD_PLUGIN_TEST"); + gboolean requires_activation = g_strcmp0(test, "requires-activation") == 0; + gboolean requires_reboot = g_strcmp0(test, "requires-reboot") == 0; + if (g_strcmp0(test, "fail") == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device was not in supported mode"); + return FALSE; + } + fu_progress_set_status(progress, FWUPD_STATUS_DECOMPRESSING); + for (guint i = 0; i <= self->delay_decompress_ms; i++) { + g_usleep(1000); + fu_progress_set_percentage_full(progress, i, self->delay_decompress_ms); + } + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i <= self->delay_write_ms; i++) { + g_usleep(1000); + fu_progress_set_percentage_full(progress, i, self->delay_write_ms); + } + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_VERIFY); + for (guint i = 0; i <= self->delay_verify_ms; i++) { + g_usleep(1000); + fu_progress_set_percentage_full(progress, i, self->delay_verify_ms); + } + + /* composite test, upgrade composite devices */ + if (g_strcmp0(test, "composite") == 0) { + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PLAIN); + if (g_strcmp0(fu_device_get_logical_id(device), "child1") == 0) { + fu_device_set_version(device, "2"); + return TRUE; + } else if (g_strcmp0(fu_device_get_logical_id(device), "child2") == 0) { + fu_device_set_version(device, "11"); + return TRUE; + } + } + + /* upgrade, or downgrade */ + if (requires_activation) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + } else if (requires_reboot) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } else { + g_autofree gchar *ver = fu_test_plugin_get_version(blob_fw); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + if (ver != NULL) { + fu_device_set_version(device, ver); + } else { + if (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + fu_device_set_version(device, "1.2.2"); + } else { + fu_device_set_version(device, "1.2.3"); + } + } + } + + /* 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; +} + +static gboolean +fu_test_plugin_activate(FuPlugin *plugin, FuDevice *device, FuProgress *process, GError **error) +{ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + return TRUE; +} + +static gboolean +fu_test_plugin_get_results(FuPlugin *plugin, FuDevice *device, GError **error) +{ + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_SUCCESS); + fu_device_set_update_error(device, NULL); + return TRUE; +} + +static gboolean +fu_test_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; +} + +static gboolean +fu_test_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; +} + +static void +fu_test_plugin_init(FuTestPlugin *self) +{ + g_debug("init"); +} + +static void +fu_test_finalize(GObject *obj) +{ + g_debug("destroy"); + G_OBJECT_CLASS(fu_test_plugin_parent_class)->finalize(obj); +} + +static void +fu_test_plugin_class_init(FuTestPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_test_finalize; + plugin_class->to_string = fu_test_plugin_to_string; + plugin_class->composite_cleanup = fu_test_plugin_composite_cleanup; + plugin_class->composite_prepare = fu_test_plugin_composite_prepare; + plugin_class->get_results = fu_test_plugin_get_results; + plugin_class->activate = fu_test_plugin_activate; + plugin_class->write_firmware = fu_test_plugin_write_firmware; + plugin_class->verify = fu_test_plugin_verify; + plugin_class->startup = fu_test_plugin_startup; + plugin_class->coldplug = fu_test_plugin_coldplug; + plugin_class->device_registered = fu_test_plugin_device_registered; +} diff --git a/fwupd-1.8.6/plugins/test/fu-test-plugin.h b/fwupd-1.8.6/plugins/test/fu-test-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..34481ca0d4da3f597a23c1980c6cc353536866d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/test/fu-test-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuTestPlugin, fu_test_plugin, FU, TEST_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/test/meson.build b/fwupd-1.8.6/plugins/test/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..41b53f380715d7eff14f577affbdcd6ce524c60d --- /dev/null +++ b/fwupd-1.8.6/plugins/test/meson.build @@ -0,0 +1,33 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginTest"'] + +install_dummy = false +if get_option('plugin_dummy') + install_dummy = true +endif + +if bluez.allowed() + plugin_quirks += files('test-ble.quirk') +endif + +plugin_builtins += static_library('fu_plugin_test', + sources: [ + 'fu-test-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +if bluez.allowed() +plugin_builtins += static_library('fu_plugin_test_ble', + sources: [ + 'fu-test-ble-plugin.c', + 'fu-test-ble-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/test/test-ble.quirk b/fwupd-1.8.6/plugins/test/test-ble.quirk new file mode 100644 index 0000000000000000000000000000000000000000..96944cbc7b834179f5770a1e6b553a4282ecc69a --- /dev/null +++ b/fwupd-1.8.6/plugins/test/test-ble.quirk @@ -0,0 +1,4 @@ +[BLUETOOTH\VID_0461&PID_4EEE&REV_0001] +Plugin = test_ble +[BLUETOOTH\VID_0461&PID_4EEF&REV_0001] +Plugin = test_ble diff --git a/fwupd-1.8.6/plugins/thelio-io/README.md b/fwupd-1.8.6/plugins/thelio-io/README.md new file mode 100644 index 0000000000000000000000000000000000000000..866e893694c6090c4bded6d6812e7e8b45eb7b0d --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/README.md @@ -0,0 +1,33 @@ +# Thelio IO + +## Introduction + +This plugin is used to detach the Thelio IO device to DFU mode. + +To switch to this mode `1` has to be written to the `bootloader` file +in sysfs. + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1209&PID_1776&REV_0001` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different USB VID and PID in DFU mode. The device is then handled by the `dfu` +plugin. + +On DFU attach the device again re-enumerates back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1209` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-device.c b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-device.c new file mode 100644 index 0000000000000000000000000000000000000000..8c9ee7fac5b6d5ca557f94358e172c84283ac65e --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-device.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-thelio-io-device.h" + +struct _FuThelioIoDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE(FuThelioIoDevice, fu_thelio_io_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_thelio_io_device_probe(FuDevice *device, GError **error) +{ + const gchar *devpath; + g_autofree gchar *fn = NULL; + g_autofree gchar *buf = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + + /* this is the atmel bootloader */ + fu_device_add_counterpart_guid(device, "USB\\VID_03EB&PID_2FF4"); + + /* convert GUsbDevice to GUdevDevice */ + udev_device = fu_usb_device_find_udev_device(FU_USB_DEVICE(device), error); + if (udev_device == NULL) + return FALSE; + + devpath = g_udev_device_get_sysfs_path(udev_device); + if (G_UNLIKELY(devpath == NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return FALSE; + } + + /* pre-1.0.0 firmware versions do not implement this */ + fn = g_build_filename(devpath, "revision", NULL); + if (!g_file_get_contents(fn, &buf, NULL, &error_local)) { + if (g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_FAILED)) { + g_debug("FW revision unimplemented: %s", error_local->message); + fu_device_set_version(device, "0.0.0"); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } else { + fu_device_set_version(device, (const gchar *)buf); + } + + return TRUE; +} + +static gboolean +fu_thelio_io_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + const gchar *devpath; + g_autofree gchar *fn = NULL; + g_autoptr(FuIOChannel) io_channel = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + const guint8 buf[] = {'1', '\n'}; + + /* convert GUsbDevice to GUdevDevice */ + udev_device = fu_usb_device_find_udev_device(FU_USB_DEVICE(device), error); + if (udev_device == NULL) + return FALSE; + devpath = g_udev_device_get_sysfs_path(udev_device); + if (G_UNLIKELY(devpath == NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return FALSE; + } + + fn = g_build_filename(devpath, "bootloader", NULL); + io_channel = fu_io_channel_new_file(fn, error); + if (io_channel == NULL) + return FALSE; + if (!fu_io_channel_write_raw(io_channel, + buf, + sizeof(buf), + 500, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_thelio_io_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_thelio_io_device_init(FuThelioIoDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_protocol(FU_DEVICE(self), "org.usb.dfu"); +} + +static void +fu_thelio_io_device_class_init(FuThelioIoDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_thelio_io_device_probe; + klass_device->detach = fu_thelio_io_device_detach; + klass_device->set_progress = fu_thelio_io_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-device.h b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-device.h new file mode 100644 index 0000000000000000000000000000000000000000..1f010584d2cd24824a7181b5eadebd2f59237187 --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_THELIO_IO_DEVICE (fu_thelio_io_device_get_type()) +G_DECLARE_FINAL_TYPE(FuThelioIoDevice, fu_thelio_io_device, FU, THELIO_IO_DEVICE, FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-plugin.c b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..979d6b5a5255fece007f1b4efedb835f8b7c530b --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-plugin.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-thelio-io-device.h" +#include "fu-thelio-io-plugin.h" + +struct _FuThelioIoPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuThelioIoPlugin, fu_thelio_io_plugin, FU_TYPE_PLUGIN) + +static void +fu_thelio_io_plugin_init(FuThelioIoPlugin *self) +{ +} + +static void +fu_thelio_io_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_THELIO_IO_DEVICE); +} + +static void +fu_thelio_io_plugin_class_init(FuThelioIoPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_thelio_io_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-plugin.h b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..32552f7370d48e32264f5f7da6438d0f0b6d4998 --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/fu-thelio-io-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuThelioIoPlugin, fu_thelio_io_plugin, FU, THELIO_IO_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/thelio-io/meson.build b/fwupd-1.8.6/plugins/thelio-io/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..0ae892860783295a84d3228346693c29f5a8884d --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/meson.build @@ -0,0 +1,15 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginThelioIo"'] + +plugin_quirks += files('thelio-io.quirk') +plugin_builtins += static_library('fu_plugin_thelio_io', + sources: [ + 'fu-thelio-io-plugin.c', + 'fu-thelio-io-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/thelio-io/thelio-io.quirk b/fwupd-1.8.6/plugins/thelio-io/thelio-io.quirk new file mode 100644 index 0000000000000000000000000000000000000000..edb0973d10dec1a20a299fbc9e30b6d9918bd212 --- /dev/null +++ b/fwupd-1.8.6/plugins/thelio-io/thelio-io.quirk @@ -0,0 +1,3 @@ +# System76 Thelio IO +[USB\VID_1209&PID_1776&REV_0001] +Plugin = thelio_io diff --git a/fwupd-1.8.6/plugins/thunderbolt/README.md b/fwupd-1.8.6/plugins/thunderbolt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1fb538df2abc24e2219884e6c34bb9c25a9fcf91 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/README.md @@ -0,0 +1,105 @@ +# Thunderbolt™ + +## Introduction + +Thunderbolt™ is the brand name of a hardware interface developed by Intel that +allows the connection of external peripherals to a computer. +Versions 1 and 2 use the same connector as Mini DisplayPort (MDP), whereas +version 3 uses USB Type-C. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +an unspecified binary file format, with vendor specific header. + +This plugin supports the following protocol ID: + +* com.intel.thunderbolt + +## GUID Generation + +These devices use a custom GUID generation scheme. +When the device is in "safe mode" the GUID is hardcoded using: + +* `TBT-safemode` + +... and when in runtime mode the GUID is: + +* `TBT-$(vid)$(pid)-native` when native, and `TBT-$(vid)$(pid)` otherwise. + +Additionally for host system thunderbolt controllers another GUID is added +containing the domain designation of the controller. This is intended to be +used for systems with multiple host controllers to disambiguiate between controllers. + +* `TBT-$(vid)$(pid)-native-controller$(num)` + +For retimers the only GUID created is as follows: + +* `TBT-$(vid)$(pid)-retimer$index` + +The retimer index is oriented around the physical connection within +the machine. It is important as multiple controllers may otherwise +identify identically. + +## Update Behavior + +For most devices the firmware is written to the device at runtime and the +update is applied immediately. Once complete the controller may reboot +which may cause all connected USB and TBT devices to be reenumerated. + +For some devices and circumstances (such as the Dell WD19 with a new enough +kernel) the device will remain functional for the duration of the update. +The update will complete on unplug. + +If a user sets `DelayedActivation` configuration option then the update will +be staged but not completed until `activate` is separately called such as at +logout or shutdown. + +## Vendor ID Security + +The vendor ID is set from the udev vendor, for example set to `TBT:0x$(vid)` + +## Runtime Power Management + +Thunderbolt controllers are slightly unusual in that they power down completely +when no thunderbolt devices are detected. This poses a problem for fwupd as +it can't coldplug devices to see if there are firmware updates available, and +also can't ensure the controller stays awake during a firmware upgrade. + +On Dell hardware the `Thunderbolt::CanForcePower` metadata value is set as the +system can force the thunderbolt controller on during coldplug or during the +firmware update process. This is typically done calling a SMI or ACPI method +which asserts the GPIO for the duration of the request. + +On non-Dell hardware you will have to insert a Thunderbolt device (e.g. a dock) +into the laptop to be able to update the controller itself. + +## Safe Mode + +Thunderbolt hardware is also slightly unusual in that it goes into "safe mode" +whenever it encounters a critical firmware error, for instance if an update +failed to be completed. In this safe mode you cannot query the controller vendor +or model and therefore the thunderbolt plugin cannot add the correct GUID used +to match it to the correct firmware. + +In this case the metadata value `Thunderbolt::IsSafeMode` is set which would +allow a different plugin to add the correct GUID based on some out-of-band +device discovery. At the moment this only happens on Dell hardware. + +## GUID generation for LVFS + +The GUID for the controller, which must appear in the metadata when uploading an +NVM to LVFS, can be generated by a tool like `appstream-util` (with +`generate-guid` command) or by Python (with +`uuid.uuid5(uuid.NAMESPACE_DNS, 'string')`). + +The format of the string used as input is "TBT-vvvvdddd", where vvvvv is the +vendor ID and dddd is the device ID, both in hex, as appear in the controller's +DROM and exposed in the relevant sysfs attributes. + +If the controller is in native enumeration mode, the string "-native" is added +at the end so the format is "TBT-vvvvdddd-native". + +## External Interface Access + +This plugin requires read/write access to `/sys/bus/thunderbolt`. diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-self-test.c b/fwupd-1.8.6/plugins/thunderbolt/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..996ee3f9c60bb298a5db57c059191bf75c0d6720 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-self-test.c @@ -0,0 +1,1386 @@ +/* + * Copyright (C) 2017 Christian J. Kellner + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fu-context-private.h" +#include "fu-plugin-private.h" +#include "fu-thunderbolt-plugin.h" +#include "fu-udev-device-private.h" + +static gchar * +udev_mock_add_domain(UMockdevTestbed *bed, int id) +{ + gchar *path; + g_autofree gchar *name = NULL; + + name = g_strdup_printf("domain%d", id); + path = umockdev_testbed_add_device(bed, + "thunderbolt", + name, + NULL, + "security", + "secure", + NULL, + "DEVTYPE", + "thunderbolt_domain", + NULL); + + g_assert_nonnull(path); + return path; +} + +static gchar * +udev_mock_add_nvmem(UMockdevTestbed *bed, gboolean active, const char *parent, int id) +{ + g_autofree gchar *name = NULL; + gchar *path; + + name = g_strdup_printf("%s%d", active ? "nvm_active" : "nvm_non_active", id); + path = umockdev_testbed_add_device(bed, "nvmem", name, parent, "nvmem", "", NULL, NULL); + + g_assert_nonnull(path); + return path; +} + +static gchar * +udev_mock_add_usb4_port(UMockdevTestbed *bed, int id) +{ + g_autofree gchar *name = g_strdup_printf("usb4_port%d", id); + gchar *path = umockdev_testbed_add_device(bed, + "thunderbolt", + name, + NULL, + "security", + "secure", + NULL, + "DEVTYPE", + "thunderbolt_usb4_port", + NULL); + + g_assert_nonnull(path); + return path; +} + +typedef struct MockDevice MockDevice; + +struct MockDevice { + const char *name; /* sysfs: device_name */ + const char *id; /* sysfs: device */ + const char *nvm_version; + const char *nvm_parsed_version; + + int delay_ms; + + int domain_id; + + struct MockDevice *children; + + /* optionally filled out */ + const char *uuid; +}; + +typedef struct MockTree MockTree; + +struct MockTree { + const MockDevice *device; + + MockTree *parent; + GPtrArray *children; + + gchar *sysfs_parent; + int sysfs_id; + int sysfs_nvm_id; + + gchar *uuid; + + UMockdevTestbed *bed; + gchar *path; + gchar *nvm_non_active; + gchar *nvm_active; + guint nvm_authenticate; + gchar *nvm_version; + + FuDevice *fu_device; +}; + +static MockTree * +mock_tree_new(MockTree *parent, MockDevice *device, int *id) +{ + MockTree *node = g_slice_new0(MockTree); + int current_id = (*id)++; + + node->device = device; + node->sysfs_id = current_id; + node->sysfs_nvm_id = current_id; + node->parent = parent; + + if (device->uuid) + node->uuid = g_strdup(device->uuid); + else + node->uuid = g_uuid_string_random(); + + node->nvm_version = g_strdup(device->nvm_version); + return node; +} + +static void +mock_tree_free(MockTree *tree) +{ + for (guint i = 0; i < tree->children->len; i++) { + MockTree *child = g_ptr_array_index(tree->children, i); + mock_tree_free(child); + } + + g_ptr_array_free(tree->children, TRUE); + + if (tree->fu_device) + g_object_unref(tree->fu_device); + + g_free(tree->uuid); + if (tree->bed != NULL) { + if (tree->nvm_active) { + umockdev_testbed_uevent(tree->bed, tree->nvm_active, "remove"); + umockdev_testbed_remove_device(tree->bed, tree->nvm_active); + } + + if (tree->nvm_non_active) { + umockdev_testbed_uevent(tree->bed, tree->nvm_non_active, "remove"); + umockdev_testbed_remove_device(tree->bed, tree->nvm_non_active); + } + + if (tree->path) { + umockdev_testbed_uevent(tree->bed, tree->path, "remove"); + umockdev_testbed_remove_device(tree->bed, tree->path); + } + + g_object_unref(tree->bed); + } + + g_free(tree->nvm_version); + g_free(tree->nvm_active); + g_free(tree->nvm_non_active); + g_free(tree->path); + g_free(tree->sysfs_parent); + g_slice_free(MockTree, tree); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(MockTree, mock_tree_free); +#pragma clang diagnostic pop + +static GPtrArray * +mock_tree_init_children(MockTree *node, int *id) +{ + GPtrArray *children = g_ptr_array_new(); + MockDevice *iter; + + for (iter = node->device->children; iter && iter->name; iter++) { + MockTree *child = mock_tree_new(node, iter, id); + g_ptr_array_add(children, child); + child->children = mock_tree_init_children(child, id); + } + + return children; +} + +static MockTree * +mock_tree_init(MockDevice *device) +{ + MockTree *tree; + int devices = 0; + + tree = mock_tree_new(NULL, device, &devices); + tree->children = mock_tree_init_children(tree, &devices); + + return tree; +} + +static void +mock_tree_dump(const MockTree *node, int level) +{ + if (node->path) { + g_debug("%*s * %s [%s] at %s", + level, + " ", + node->device->name, + node->uuid, + node->path); + g_debug("%*s non-active nvmem at %s", level, " ", node->nvm_non_active); + g_debug("%*s active nvmem at %s", level, " ", node->nvm_active); + } else { + g_debug("%*s * %s [%s] %d", + level, + " ", + node->device->name, + node->uuid, + node->sysfs_id); + } + + for (guint i = 0; i < node->children->len; i++) { + const MockTree *child = g_ptr_array_index(node->children, i); + mock_tree_dump(child, level + 2); + } +} + +static void +mock_tree_firmware_verify(const MockTree *node, GBytes *data) +{ + g_autoptr(GFile) nvm_device = NULL; + g_autoptr(GFile) nvm = NULL; + g_autoptr(GInputStream) is = NULL; + g_autoptr(GChecksum) chk = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *sum_data = NULL; + const gchar *sum_disk = NULL; + gsize s; + + sum_data = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, data); + chk = g_checksum_new(G_CHECKSUM_SHA1); + + g_assert_nonnull(node); + g_assert_nonnull(node->nvm_non_active); + + nvm_device = g_file_new_for_path(node->nvm_non_active); + nvm = g_file_get_child(nvm_device, "nvmem"); + + is = (GInputStream *)g_file_read(nvm, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(is); + + do { + g_autoptr(GBytes) b = NULL; + const guchar *d; + + b = g_input_stream_read_bytes(is, 4096, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(is); + + d = g_bytes_get_data(b, &s); + if (s > 0) + g_checksum_update(chk, d, (gssize)s); + + } while (s > 0); + + sum_disk = g_checksum_get_string(chk); + + g_assert_cmpstr(sum_data, ==, sum_disk); +} + +typedef gboolean (*MockTreePredicate)(const MockTree *node, gpointer data); + +static const MockTree * +mock_tree_contains(const MockTree *node, MockTreePredicate predicate, gpointer data) +{ + if (predicate(node, data)) + return node; + + for (guint i = 0; i < node->children->len; i++) { + const MockTree *child; + const MockTree *match; + + child = g_ptr_array_index(node->children, i); + match = mock_tree_contains(child, predicate, data); + if (match != NULL) + return match; + } + + return NULL; +} + +static gboolean +mock_tree_all(const MockTree *node, MockTreePredicate predicate, gpointer data) +{ + if (!predicate(node, data)) + return FALSE; + + for (guint i = 0; i < node->children->len; i++) { + const MockTree *child; + + child = g_ptr_array_index(node->children, i); + if (!mock_tree_all(child, predicate, data)) + return FALSE; + } + + return TRUE; +} + +static gboolean +mock_tree_compare_uuid(const MockTree *node, gpointer data) +{ + const gchar *uuid = (const gchar *)data; + return g_str_equal(node->uuid, uuid); +} + +static const MockTree * +mock_tree_find_uuid(const MockTree *root, const char *uuid) +{ + return mock_tree_contains(root, mock_tree_compare_uuid, (gpointer)uuid); +} + +static gboolean +mock_tree_node_have_fu_device(const MockTree *node, gpointer data) +{ + return node->fu_device != NULL; +} + +static void +write_controller_fw(const gchar *nvm) +{ + g_autoptr(GFile) nvm_device = NULL; + g_autoptr(GFile) nvmem = NULL; + g_autofree gchar *fw_path = NULL; + g_autoptr(GFile) fw_file = NULL; + g_autoptr(GInputStream) is = NULL; + g_autoptr(GOutputStream) os = NULL; + g_autoptr(GError) error = NULL; + gssize n; + + fw_path = g_test_build_filename(G_TEST_DIST, "tests", "minimal-fw-controller.bin", NULL); + fw_file = g_file_new_for_path(fw_path); + g_assert_nonnull(fw_file); + + nvm_device = g_file_new_for_path(nvm); + g_assert_nonnull(nvm_device); + + nvmem = g_file_get_child(nvm_device, "nvmem"); + g_assert_nonnull(nvmem); + + os = (GOutputStream *)g_file_append_to(nvmem, G_FILE_CREATE_NONE, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(os); + + is = (GInputStream *)g_file_read(fw_file, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(is); + + n = g_output_stream_splice(os, is, G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error); + g_assert_no_error(error); + g_assert_cmpuint(n, >, 0); +} + +static gboolean +mock_tree_attach_device(gpointer user_data) +{ + MockTree *tree = (MockTree *)user_data; + const MockDevice *dev = tree->device; + g_autofree gchar *idstr = NULL; + g_autofree gchar *authenticate = NULL; + + g_assert_nonnull(tree); + g_assert_nonnull(tree->sysfs_parent); + g_assert_nonnull(dev); + + idstr = g_strdup_printf("%d-%d", dev->domain_id, tree->sysfs_id); + authenticate = g_strdup_printf("0x%x", tree->nvm_authenticate); + + tree->path = umockdev_testbed_add_device(tree->bed, + "thunderbolt", + idstr, + tree->sysfs_parent, + "device_name", + dev->name, + "device", + dev->id, + "vendor", + "042", + "vendor_name", + "GNOME.org", + "authorized", + "1", + "nvm_authenticate", + authenticate, + "nvm_version", + tree->nvm_version, + "unique_id", + tree->uuid, + NULL, + "DEVTYPE", + "thunderbolt_device", + NULL); + + tree->nvm_non_active = udev_mock_add_nvmem(tree->bed, FALSE, tree->path, tree->sysfs_id); + + tree->nvm_active = udev_mock_add_nvmem(tree->bed, TRUE, tree->path, tree->sysfs_id); + + g_assert_nonnull(tree->path); + g_assert_nonnull(tree->nvm_non_active); + g_assert_nonnull(tree->nvm_active); + + write_controller_fw(tree->nvm_active); + + for (guint i = 0; i < tree->children->len; i++) { + MockTree *child; + + child = g_ptr_array_index(tree->children, i); + + child->bed = g_object_ref(tree->bed); + child->sysfs_parent = g_strdup(tree->path); + + g_timeout_add(child->device->delay_ms, mock_tree_attach_device, child); + } + + return FALSE; +} + +typedef struct SyncContext { + MockTree *tree; + GMainLoop *loop; +} SyncContext; + +static gboolean +on_sync_timeout(gpointer user_data) +{ + SyncContext *ctx = (SyncContext *)user_data; + g_main_loop_quit(ctx->loop); + return FALSE; +} + +static void +sync_device_added(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + SyncContext *ctx = (SyncContext *)user_data; + MockTree *tree = ctx->tree; + const gchar *uuid = fu_device_get_physical_id(device); + MockTree *target; + + target = (MockTree *)mock_tree_find_uuid(tree, uuid); + + if (target == NULL) { + g_critical("Got device that could not be matched: %s", uuid); + return; + } + + if (target->fu_device != NULL) + g_object_unref(target->fu_device); + + target->fu_device = g_object_ref(device); +} + +static void +sync_device_removed(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + SyncContext *ctx = (SyncContext *)user_data; + MockTree *tree = ctx->tree; + const gchar *uuid = fu_device_get_physical_id(device); + MockTree *target; + + target = (MockTree *)mock_tree_find_uuid(tree, uuid); + + if (target == NULL) { + g_warning("Got device that could not be matched: %s", uuid); + return; + } else if (target->fu_device == NULL) { + g_warning("Got remove event for out-of-tree device %s", uuid); + return; + } + + g_object_unref(target->fu_device); + target->fu_device = NULL; +} + +static void +mock_tree_sync(MockTree *root, FuPlugin *plugin, int timeout_ms) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + gulong id_add; + gulong id_del; + SyncContext ctx = { + .tree = root, + .loop = mainloop, + }; + + id_add = g_signal_connect(FU_PLUGIN(plugin), + "device-added", + G_CALLBACK(sync_device_added), + &ctx); + + id_del = g_signal_connect(FU_PLUGIN(plugin), + "device-removed", + G_CALLBACK(sync_device_removed), + &ctx); + + if (timeout_ms > 0) + g_timeout_add(timeout_ms, on_sync_timeout, &ctx); + + g_main_loop_run(mainloop); + + g_signal_handler_disconnect(plugin, id_add); + g_signal_handler_disconnect(plugin, id_del); +} + +typedef struct AttachContext { + /* in */ + MockTree *tree; + GMainLoop *loop; + /* out */ + gboolean complete; + +} AttachContext; + +static void +mock_tree_plugin_device_added(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + AttachContext *ctx = (AttachContext *)user_data; + MockTree *tree = ctx->tree; + const gchar *uuid = fu_device_get_physical_id(device); + MockTree *target; + + target = (MockTree *)mock_tree_find_uuid(tree, uuid); + + if (target == NULL) { + g_warning("Got device that could not be matched: %s", uuid); + return; + } + + g_set_object(&target->fu_device, device); + + if (mock_tree_all(tree, mock_tree_node_have_fu_device, NULL)) { + ctx->complete = TRUE; + g_main_loop_quit(ctx->loop); + } +} + +static gboolean +mock_tree_settle(MockTree *root, FuPlugin *plugin) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + gulong id; + AttachContext ctx = { + .tree = root, + .loop = mainloop, + }; + + id = g_signal_connect(FU_PLUGIN(plugin), + "device-added", + G_CALLBACK(mock_tree_plugin_device_added), + &ctx); + + g_main_loop_run(mainloop); + g_signal_handler_disconnect(plugin, id); + + return ctx.complete; +} + +static gboolean +mock_tree_attach(MockTree *root, UMockdevTestbed *bed, FuPlugin *plugin) +{ + root->bed = g_object_ref(bed); + root->sysfs_parent = udev_mock_add_domain(bed, root->device->domain_id); + root->sysfs_parent = udev_mock_add_usb4_port(bed, 1); + g_assert_nonnull(root->sysfs_parent); + + g_timeout_add(root->device->delay_ms, mock_tree_attach_device, root); + + return mock_tree_settle(root, plugin); +} + +/* the unused parameter makes the function signature compatible + * with 'MockTreePredicate' */ +static gboolean +mock_tree_node_is_detached(const MockTree *node, gpointer unused) +{ + gboolean ret = node->path == NULL; + + /* consistency checks: if ret, make sure we are + * fully detached */ + if (ret) { + g_assert_null(node->nvm_active); + g_assert_null(node->nvm_non_active); + g_assert_null(node->bed); + } else { + g_assert_nonnull(node->nvm_active); + g_assert_nonnull(node->nvm_non_active); + g_assert_nonnull(node->bed); + } + + return ret; +} + +static void +mock_tree_detach(MockTree *node) +{ + UMockdevTestbed *bed; + + if (mock_tree_node_is_detached(node, NULL)) + return; + + for (guint i = 0; i < node->children->len; i++) { + MockTree *child = g_ptr_array_index(node->children, i); + mock_tree_detach(child); + g_free(child->sysfs_parent); + child->sysfs_parent = NULL; + } + + bed = node->bed; + umockdev_testbed_uevent(bed, node->nvm_active, "remove"); + umockdev_testbed_remove_device(bed, node->nvm_active); + + umockdev_testbed_uevent(bed, node->nvm_non_active, "remove"); + umockdev_testbed_remove_device(bed, node->nvm_non_active); + + umockdev_testbed_uevent(bed, node->path, "remove"); + umockdev_testbed_remove_device(bed, node->path); + + g_free(node->path); + g_free(node->nvm_non_active); + g_free(node->nvm_active); + + node->path = NULL; + node->nvm_non_active = NULL; + node->nvm_active = NULL; + + g_object_unref(bed); + node->bed = NULL; +} + +typedef enum UpdateResult { + UPDATE_SUCCESS = 0, + /* nvm_authenticate will report error condition */ + UPDATE_FAIL_DEVICE_INTERNAL = 1, + /* device to be updated will NOT re-appear */ + UPDATE_FAIL_DEVICE_NOSHOW = 2 +} UpdateResult; + +typedef struct UpdateContext { + GFileMonitor *monitor; + + UpdateResult result; + guint timeout; + GBytes *data; + UMockdevTestbed *bed; + FuPlugin *plugin; + + MockTree *node; + gchar *version; +} UpdateContext; + +static void +update_context_free(UpdateContext *ctx) +{ + if (ctx == NULL) + return; + + g_file_monitor_cancel(ctx->monitor); + + g_object_unref(ctx->bed); + g_object_unref(ctx->plugin); + g_object_unref(ctx->monitor); + g_bytes_unref(ctx->data); + g_free(ctx->version); + g_free(ctx); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(UpdateContext, update_context_free); +#pragma clang diagnostic pop + +static gboolean +reattach_tree(gpointer user_data) +{ + UpdateContext *ctx = (UpdateContext *)user_data; + MockTree *node = ctx->node; + + g_debug("Mock update done, reattaching tree..."); + + node->bed = g_object_ref(ctx->bed); + g_timeout_add(node->device->delay_ms, mock_tree_attach_device, node); + + return FALSE; +} + +static void +udev_file_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + UpdateContext *ctx = (UpdateContext *)user_data; + gboolean ok; + gsize len; + g_autofree gchar *data = NULL; + g_autoptr(GError) error = NULL; + + g_debug("Got update trigger"); + ok = g_file_monitor_cancel(monitor); + g_assert_true(ok); + + ok = g_file_load_contents(file, NULL, &data, &len, NULL, &error); + g_assert_no_error(error); + g_assert_true(ok); + + if (!g_str_has_prefix(data, "1")) + return; + + /* verify the firmware is correct */ + mock_tree_firmware_verify(ctx->node, ctx->data); + + g_debug("Removing tree below and including: %s", ctx->node->path); + mock_tree_detach(ctx->node); + + ctx->node->nvm_authenticate = (guint)ctx->result; + + /* update the version only on "success" simulations */ + if (ctx->result == UPDATE_SUCCESS) { + g_free(ctx->node->nvm_version); + ctx->node->nvm_version = g_strdup(ctx->version); + } + + g_debug("Simulating update to '%s' with result: 0x%x", + ctx->version, + ctx->node->nvm_authenticate); + + if (ctx->result == UPDATE_FAIL_DEVICE_NOSHOW) { + g_debug("Simulating no-show fail:" + " device tree will not reappear"); + return; + } + + g_debug("Device tree reattachment in %3.2f seconds", ctx->timeout / 1000.0); + g_timeout_add(ctx->timeout, reattach_tree, ctx); +} + +static UpdateContext * +mock_tree_prepare_for_update(MockTree *node, + FuPlugin *plugin, + const char *version, + GBytes *fw_data, + guint timeout_ms) +{ + UpdateContext *ctx; + g_autoptr(GFile) dir = NULL; + g_autoptr(GFile) f = NULL; + g_autoptr(GError) error = NULL; + GFileMonitor *monitor; + + ctx = g_new0(UpdateContext, 1); + dir = g_file_new_for_path(node->path); + f = g_file_get_child(dir, "nvm_authenticate"); + + monitor = g_file_monitor_file(f, G_FILE_MONITOR_NONE, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(monitor); + + ctx->node = node; + ctx->plugin = g_object_ref(plugin); + ctx->bed = g_object_ref(node->bed); + ctx->timeout = timeout_ms; + ctx->monitor = monitor; + ctx->version = g_strdup(version); + ctx->data = g_bytes_ref(fw_data); + + g_signal_connect(G_FILE_MONITOR(monitor), "changed", G_CALLBACK(udev_file_changed_cb), ctx); + + return ctx; +} + +static MockDevice root_one = { + + .name = "Laptop", + .id = "0x23", + .nvm_version = "20.2", + .nvm_parsed_version = "20.02", + + .children = + (MockDevice[]){ + { + .name = "Thunderbolt Cable", + .id = "0x24", + .nvm_version = "20.0", + .nvm_parsed_version = "20.00", + + .children = (MockDevice[]){{ + .name = "Thunderbolt Dock", + .id = "0x25", + .nvm_version = "10.0", + .nvm_parsed_version = "10.00", + }, + { + NULL, + } + + }, + }, + { + .name = "Thunderbolt Cable", + .id = "0x24", + .nvm_version = "23.0", + .nvm_parsed_version = "23.00", + + .children = (MockDevice[]){{ + .name = "Thunderbolt SSD", + .id = "0x26", + + .nvm_version = "5.0", + .nvm_parsed_version = "05.00", + }, + { + NULL, + }}, + }, + { + NULL, + }, + }, + +}; + +typedef struct TestParam { + gboolean initialize_tree; + gboolean attach_and_coldplug; + + const char *firmware_file; +} TestParam; + +typedef enum TestFlags { + TEST_INITIALIZE_TREE = 1 << 0, + TEST_ATTACH = 1 << 1, + TEST_PREPARE_FIRMWARE = 1 << 2, + + TEST_PREPARE_ALL = TEST_INITIALIZE_TREE | TEST_ATTACH | TEST_PREPARE_FIRMWARE +} TestFlags; + +#define TEST_INIT_FULL (GUINT_TO_POINTER(TEST_PREPARE_ALL)) +#define TEST_INIT_NONE (GUINT_TO_POINTER(0)) + +typedef struct ThunderboltTest { + UMockdevTestbed *bed; + FuPlugin *plugin; + FuContext *ctx; + GUdevClient *udev_client; + + /* if TestParam::initialize_tree */ + MockTree *tree; + + /* if TestParam::firmware_file is nonnull */ + GMappedFile *fw_file; + GBytes *fw_data; + +} ThunderboltTest; + +static void +fu_thunderbolt_gudev_uevent_cb(GUdevClient *gudev_client, + const gchar *action, + GUdevDevice *udev_device, + ThunderboltTest *tt) +{ + if (g_strcmp0(action, "add") == 0) { + g_autoptr(FuUdevDevice) device = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + device = fu_udev_device_new(tt->ctx, udev_device); + if (!fu_device_probe(FU_DEVICE(device), &error_local)) { + g_warning("failed to probe: %s", error_local->message); + return; + } + if (!fu_plugin_runner_backend_device_added(tt->plugin, + FU_DEVICE(device), + progress, + &error_local)) + g_debug("failed to add: %s", error_local->message); + return; + } + if (g_strcmp0(action, "remove") == 0) { + if (tt->tree->fu_device != NULL) + fu_plugin_device_remove(tt->plugin, tt->tree->fu_device); + return; + } + if (g_strcmp0(action, "change") == 0) { + const gchar *uuid = g_udev_device_get_sysfs_attr(udev_device, "unique_id"); + MockTree *target = (MockTree *)mock_tree_find_uuid(tt->tree, uuid); + g_assert_nonnull(target); + fu_udev_device_emit_changed(FU_UDEV_DEVICE(target->fu_device)); + return; + } +} +static void +test_set_up(ThunderboltTest *tt, gconstpointer params) +{ + TestFlags flags = GPOINTER_TO_UINT(params); + gboolean ret; + g_autofree gchar *sysfs = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + const gchar *udev_subsystems[] = {"thunderbolt", NULL}; + + tt->ctx = fu_context_new(); + ret = fu_context_load_quirks(tt->ctx, + FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + tt->bed = umockdev_testbed_new(); + g_assert_nonnull(tt->bed); + + sysfs = umockdev_testbed_get_sys_dir(tt->bed); + g_debug("mock sysfs at %s", sysfs); + + tt->plugin = fu_plugin_new_from_gtype(fu_thunderbolt_plugin_get_type(), tt->ctx); + + g_assert_nonnull(tt->plugin); + + ret = fu_plugin_runner_startup(tt->plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + if (flags & TEST_INITIALIZE_TREE) { + tt->tree = mock_tree_init(&root_one); + g_assert_nonnull(tt->tree); + } + + if (!umockdev_in_mock_environment()) { + g_warning("Need to run with umockdev-wrapper"); + return; + } + + tt->udev_client = g_udev_client_new(udev_subsystems); + g_assert_nonnull(tt->udev_client); + g_signal_connect(G_UDEV_CLIENT(tt->udev_client), + "uevent", + G_CALLBACK(fu_thunderbolt_gudev_uevent_cb), + tt); + + if (flags & TEST_ATTACH) { + g_assert_true(flags & TEST_INITIALIZE_TREE); + + ret = mock_tree_attach(tt->tree, tt->bed, tt->plugin); + g_assert_true(ret); + } + + if (flags & TEST_PREPARE_FIRMWARE) { + g_autofree gchar *fw_path = NULL; + + fw_path = g_test_build_filename(G_TEST_DIST, "tests", "minimal-fw.bin", NULL); + tt->fw_file = g_mapped_file_new(fw_path, FALSE, &error); + g_assert_no_error(error); + g_assert_nonnull(tt->fw_file); + + tt->fw_data = g_mapped_file_get_bytes(tt->fw_file); + g_assert_nonnull(tt->fw_data); + } +} + +static void +test_tear_down(ThunderboltTest *tt, gconstpointer user_data) +{ + g_object_unref(tt->plugin); + g_object_unref(tt->ctx); + g_object_unref(tt->bed); + g_object_unref(tt->udev_client); + + if (tt->tree) + mock_tree_free(tt->tree); + + if (tt->fw_data) + g_bytes_unref(tt->fw_data); + + if (tt->fw_file) + g_mapped_file_unref(tt->fw_file); +} + +static gboolean +test_tree_uuids(const MockTree *node, gpointer data) +{ + const MockTree *root = (MockTree *)data; + const gchar *uuid = node->uuid; + const MockTree *found; + + g_assert_nonnull(uuid); + + g_debug("Looking for %s", uuid); + + found = mock_tree_find_uuid(root, uuid); + g_assert_nonnull(node); + g_assert_nonnull(found); + g_assert_cmpstr(node->uuid, ==, found->uuid); + + /* return false so we traverse the whole tree */ + return FALSE; +} + +static void +test_tree(ThunderboltTest *tt, gconstpointer user_data) +{ + const MockTree *found; + gboolean ret; + g_autoptr(MockTree) tree = NULL; + + tree = mock_tree_init(&root_one); + g_assert_nonnull(tree); + + mock_tree_dump(tree, 0); + + (void)mock_tree_contains(tree, test_tree_uuids, tree); + + found = mock_tree_find_uuid(tree, "nonexistentuuid"); + g_assert_null(found); + + ret = mock_tree_attach(tree, tt->bed, tt->plugin); + g_assert_true(ret); + + mock_tree_detach(tree); + ret = mock_tree_all(tree, mock_tree_node_is_detached, NULL); + g_assert_true(ret); +} + +static void +test_image_validation(ThunderboltTest *tt, gconstpointer user_data) +{ + gboolean ret; + g_autofree gchar *ctl_path = NULL; + g_autofree gchar *fwi_path = NULL; + g_autofree gchar *bad_path = NULL; + g_autoptr(GMappedFile) fwi_file = NULL; + g_autoptr(GMappedFile) ctl_file = NULL; + g_autoptr(GMappedFile) bad_file = NULL; + g_autoptr(GBytes) fwi_data = NULL; + g_autoptr(GBytes) ctl_data = NULL; + g_autoptr(GBytes) bad_data = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(FuFirmware) firmware_fwi = fu_intel_thunderbolt_firmware_new(); + g_autoptr(FuFirmware) firmware_ctl = fu_intel_thunderbolt_nvm_new(); + g_autoptr(FuFirmware) firmware_bad = fu_intel_thunderbolt_nvm_new(); + + /* image as if read from the controller (i.e. no headers) */ + ctl_path = g_test_build_filename(G_TEST_DIST, "tests", "minimal-fw-controller.bin", NULL); + ctl_file = g_mapped_file_new(ctl_path, FALSE, &error); + g_assert_no_error(error); + g_assert_nonnull(ctl_file); + + ctl_data = g_mapped_file_get_bytes(ctl_file); + g_assert_nonnull(ctl_data); + + /* parse controller image */ + ret = fu_firmware_parse(firmware_ctl, ctl_data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* valid firmware update image */ + fwi_path = g_test_build_filename(G_TEST_DIST, "tests", "minimal-fw.bin", NULL); + fwi_file = g_mapped_file_new(fwi_path, FALSE, &error); + g_assert_no_error(error); + g_assert_nonnull(fwi_file); + + fwi_data = g_mapped_file_get_bytes(fwi_file); + g_assert_nonnull(fwi_data); + + /* parse */ + ret = fu_firmware_parse(firmware_fwi, fwi_data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* a wrong/bad firmware update image */ + bad_path = g_test_build_filename(G_TEST_DIST, "tests", "colorhug.bin", NULL); + bad_file = g_mapped_file_new(bad_path, FALSE, &error); + g_assert_no_error(error); + g_assert_nonnull(bad_file); + + bad_data = g_mapped_file_get_bytes(bad_file); + g_assert_nonnull(bad_data); + + /* parse; should fail, bad image */ + ret = fu_firmware_parse(firmware_bad, bad_data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ); + g_debug("expected image validation error [ctl]: %s", error->message); + g_clear_error(&error); + + /* now for some testing ... this should work */ + ret = fu_firmware_check_compatible(firmware_ctl, + firmware_fwi, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +test_change_uevent(ThunderboltTest *tt, gconstpointer user_data) +{ + FuPlugin *plugin = tt->plugin; + MockTree *tree = tt->tree; + gboolean ret; + const gchar *version_after; + + /* test sanity check */ + g_assert_nonnull(tree); + + /* simulate change of version via a change even, i.e. + * without add, remove. */ + umockdev_testbed_set_attribute(tt->bed, tree->path, "nvm_version", "42.23"); + umockdev_testbed_uevent(tt->bed, tree->path, "change"); + + /* we just "wait" for 500ms, should be enough */ + mock_tree_sync(tree, plugin, 500); + + /* the tree should not have changed */ + ret = mock_tree_all(tree, mock_tree_node_have_fu_device, NULL); + g_assert_true(ret); + + /* we should have the version change in the FuDevice */ + version_after = fu_device_get_version(tree->fu_device); + g_assert_cmpstr(version_after, ==, "42.23"); +} + +static void +test_update_working(ThunderboltTest *tt, gconstpointer user_data) +{ + FuPlugin *plugin = tt->plugin; + MockTree *tree = tt->tree; + GBytes *fw_data = tt->fw_data; + gboolean ret; + const gchar *version_after; + g_autoptr(GError) error = NULL; + g_autoptr(UpdateContext) up_ctx = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* test sanity check */ + g_assert_nonnull(tree); + g_assert_nonnull(fw_data); + + /* 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); + g_assert_nonnull(up_ctx); + ret = fu_plugin_runner_write_firmware(plugin, + tree->fu_device, + fw_data, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &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_attach(plugin, tree->fu_device, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + version_after = fu_device_get_version(tree->fu_device); + g_debug("version after update: %s", version_after); + g_assert_cmpstr(version_after, ==, "42.23"); + + /* make sure all pending events have happened */ + ret = mock_tree_settle(tree, plugin); + g_assert_true(ret); + + /* now we check if the every tree node has a corresponding FuDevice, + * this implicitly checks that we are handling uevents correctly + * after the event, and that we are in sync with the udev tree */ + ret = mock_tree_all(tree, mock_tree_node_have_fu_device, NULL); + g_assert_true(ret); +} + +static void +test_update_wd19(ThunderboltTest *tt, gconstpointer user_data) +{ + FuPlugin *plugin = tt->plugin; + MockTree *tree = tt->tree; + GBytes *fw_data = tt->fw_data; + gboolean ret; + const gchar *version_before; + const gchar *version_after; + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* test sanity check */ + g_assert_nonnull(tree); + g_assert_nonnull(fw_data); + + /* simulate a wd19 update which will not disappear / re-appear */ + fu_device_add_flag(tree->fu_device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); + fu_device_add_flag(tree->fu_device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + version_before = fu_device_get_version(tree->fu_device); + + ret = fu_plugin_runner_write_firmware(plugin, + tree->fu_device, + fw_data, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = fu_device_has_flag(tree->fu_device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + g_assert_true(ret); + + version_after = fu_device_get_version(tree->fu_device); + g_assert_cmpstr(version_after, ==, version_before); +} + +static void +test_update_fail(ThunderboltTest *tt, gconstpointer user_data) +{ + FuPlugin *plugin = tt->plugin; + MockTree *tree = tt->tree; + GBytes *fw_data = tt->fw_data; + gboolean ret; + const gchar *version_after; + g_autoptr(GError) error = NULL; + g_autoptr(UpdateContext) up_ctx = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* test sanity check */ + g_assert_nonnull(tree); + g_assert_nonnull(fw_data); + + /* simulate an update, as in test_update_working, + * but simulate an error indicated by the device + */ + 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_write_firmware(plugin, + tree->fu_device, + fw_data, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &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_attach(plugin, tree->fu_device, progress, &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); + + ret = mock_tree_all(tree, mock_tree_node_have_fu_device, NULL); + g_assert_true(ret); +} + +static void +test_update_fail_nowshow(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; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + /* test sanity check */ + g_assert_nonnull(tree); + g_assert_nonnull(fw_data); + + /* simulate an update, as in test_update_working, + * but simulate an error indicated by the device + */ + up_ctx = mock_tree_prepare_for_update(tree, plugin, "42.23", fw_data, 1000); + up_ctx->result = UPDATE_FAIL_DEVICE_NOSHOW; + + ret = fu_plugin_runner_write_firmware(plugin, + tree->fu_device, + fw_data, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + &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_false(ret); +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + g_test_init(&argc, &argv, NULL); + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + g_test_add("/thunderbolt/basic", + ThunderboltTest, + NULL, + test_set_up, + test_tree, + test_tear_down); + + g_test_add("/thunderbolt/image-validation", + ThunderboltTest, + TEST_INIT_NONE, + test_set_up, + test_image_validation, + test_tear_down); + + g_test_add("/thunderbolt/change-uevent", + ThunderboltTest, + GUINT_TO_POINTER(TEST_INITIALIZE_TREE | TEST_ATTACH), + test_set_up, + test_change_uevent, + test_tear_down); + + g_test_add("/thunderbolt/update{working}", + ThunderboltTest, + TEST_INIT_FULL, + test_set_up, + test_update_working, + test_tear_down); + + g_test_add("/thunderbolt/update{failing}", + ThunderboltTest, + TEST_INIT_FULL, + test_set_up, + test_update_fail, + test_tear_down); + + g_test_add("/thunderbolt/update{failing-noshow}", + ThunderboltTest, + TEST_INIT_FULL, + test_set_up, + test_update_fail_nowshow, + test_tear_down); + g_test_add("/thunderbolt/update{delayed_activation}", + ThunderboltTest, + TEST_INIT_FULL, + test_set_up, + test_update_wd19, + test_tear_down); + + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-common.c b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-common.c new file mode 100644 index 0000000000000000000000000000000000000000..c8166b764abca3974882a95664cf08d487c3b673 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-common.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 Christian J. Kellner + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-thunderbolt-common.h" + +static gboolean +fu_thunderbolt_device_check_usb4_port_path(FuUdevDevice *device, + const gchar *attribute, + GError **err) +{ + g_autofree const gchar *path = + g_build_filename(fu_udev_device_get_sysfs_path(device), attribute, NULL); + g_autofree gchar *fn = g_strdup_printf("%s", path); + g_autoptr(GFile) file = g_file_new_for_path(fn); + if (!g_file_query_exists(file, NULL)) { + g_set_error(err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "failed to find %s", fn); + return FALSE; + } + return TRUE; +} + +gboolean +fu_thunderbolt_udev_set_port_offline(FuUdevDevice *device, GError **error) +{ + const gchar *offline = "usb4_port1/offline"; + const gchar *rescan = "usb4_port1/rescan"; + g_autoptr(GError) error_local = NULL; + + if (!fu_thunderbolt_device_check_usb4_port_path(device, offline, &error_local)) { + g_debug("failed to check usb4 offline path: %s", error_local->message); + return TRUE; + } + if (!fu_udev_device_write_sysfs(device, offline, "1", error)) { + g_prefix_error(error, "setting usb4 port offline failed: "); + return FALSE; + } + if (!fu_thunderbolt_device_check_usb4_port_path(device, rescan, &error_local)) { + g_debug("failed to check usb4 rescan path: %s", error_local->message); + return TRUE; + } + if (!fu_udev_device_write_sysfs(device, rescan, "1", error)) { + g_prefix_error(error, "rescan on port failed: "); + return FALSE; + } + return TRUE; +} + +gboolean +fu_thunderbolt_udev_set_port_online(FuUdevDevice *device, GError **error) +{ + FuUdevDevice *udev = FU_UDEV_DEVICE(device); + const gchar *offline = "usb4_port1/offline"; + g_autoptr(GError) error_local = NULL; + + if (!fu_thunderbolt_device_check_usb4_port_path(device, offline, &error_local)) { + g_debug("failed to check usb4 port path: %s", error_local->message); + return TRUE; + } + if (!fu_udev_device_write_sysfs(udev, offline, "0", error)) { + g_prefix_error(error, "setting port online failed: "); + return FALSE; + } + return TRUE; +} + +guint16 +fu_thunderbolt_udev_get_attr_uint16(FuUdevDevice *device, const gchar *name, GError **error) +{ + const gchar *str; + guint64 val; + + str = fu_udev_device_get_sysfs_attr(device, name, error); + if (str == NULL) + return 0x0; + + val = g_ascii_strtoull(str, NULL, 16); + if (val == 0x0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to parse %s", str); + return 0; + } + if (val > G_MAXUINT16) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "%s overflows", name); + return 0x0; + } + return (guint16)val; +} diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-common.h b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-common.h new file mode 100644 index 0000000000000000000000000000000000000000..610f460ed53bfe47cfc9a94b5e7199fd7ff86d86 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-common.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION: + * + * Forces composite device components to be enumerated. + */ +#define FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION (1ull << 1) + +gboolean +fu_thunderbolt_udev_set_port_online(FuUdevDevice *device, GError **error); +gboolean +fu_thunderbolt_udev_set_port_offline(FuUdevDevice *device, GError **error); +guint16 +fu_thunderbolt_udev_get_attr_uint16(FuUdevDevice *device, const gchar *name, GError **error); diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-controller.c b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-controller.c new file mode 100644 index 0000000000000000000000000000000000000000..19039a727eb36f03564ca0894c97b7e7b66db61e --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-controller.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2017 Christian J. Kellner + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-thunderbolt-common.h" +#include "fu-thunderbolt-controller.h" + +typedef enum { + FU_THUNDERBOLT_CONTROLLER_KIND_DEVICE, + FU_THUNDERBOLT_CONTROLLER_KIND_HOST, +} FuThunderboltControllerKind; + +struct _FuThunderboltController { + FuThunderboltDevice parent_instance; + FuThunderboltControllerKind controller_kind; + gboolean safe_mode; + gboolean is_native; + guint16 gen; + guint host_online_timer_id; +}; + +G_DEFINE_TYPE(FuThunderboltController, fu_thunderbolt_controller, FU_TYPE_THUNDERBOLT_DEVICE) + +/* byte offsets in firmware image */ +#define FU_TBT_OFFSET_NATIVE 0x7B +#define FU_TBT_CHUNK_SZ 0x40 + +static void +fu_thunderbolt_controller_check_safe_mode(FuThunderboltController *self) +{ + const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + /* failed to read, for host check for safe mode */ + if (self->controller_kind != FU_THUNDERBOLT_CONTROLLER_KIND_DEVICE) + return; + g_warning("%s is in safe mode -- VID/DID will " + "need to be set by another plugin", + devpath); + self->safe_mode = TRUE; + fu_device_set_version(FU_DEVICE(self), "00.00"); + fu_device_add_instance_id(FU_DEVICE(self), "TBT-safemode"); + fu_device_set_metadata_boolean(FU_DEVICE(self), FU_DEVICE_METADATA_TBT_IS_SAFE_MODE, TRUE); +} + +static const gchar * +fu_thunderbolt_controller_kind_to_string(FuThunderboltController *self) +{ + if (self->controller_kind == FU_THUNDERBOLT_CONTROLLER_KIND_HOST) { + if (self->gen >= 4) + return "USB4 host controller"; + else + return "Thunderbolt host controller"; + } + if (self->controller_kind == FU_THUNDERBOLT_CONTROLLER_KIND_DEVICE) { + if (self->gen >= 4) + return "USB4 device controller"; + else + return "Thunderbolt device controller"; + } + return "Unknown"; +} + +static void +fu_thunderbolt_controller_to_string(FuDevice *device, guint idt, GString *str) +{ + FuThunderboltController *self = FU_THUNDERBOLT_CONTROLLER(device); + + /* FuThunderboltDevice->to_string */ + FU_DEVICE_CLASS(fu_thunderbolt_controller_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "Device Type", fu_thunderbolt_controller_kind_to_string(self)); + fu_string_append_kb(str, idt, "Safe Mode", self->safe_mode); + fu_string_append_kb(str, idt, "Native mode", self->is_native); + fu_string_append_ku(str, idt, "Generation", self->gen); +} + +static gboolean +fu_thunderbolt_controller_probe(FuDevice *device, GError **error) +{ + FuThunderboltController *self = FU_THUNDERBOLT_CONTROLLER(device); + const gchar *unique_id; + g_autofree gchar *parent_name = NULL; + + /* FuUdevDevice->probe */ + if (!FU_DEVICE_CLASS(fu_thunderbolt_controller_parent_class)->probe(device, error)) + return FALSE; + + /* determine if host controller or not */ + parent_name = fu_udev_device_get_parent_name(FU_UDEV_DEVICE(self)); + if (parent_name != NULL && g_str_has_prefix(parent_name, "domain")) + self->controller_kind = FU_THUNDERBOLT_CONTROLLER_KIND_HOST; + unique_id = fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), "unique_id", NULL); + if (unique_id != NULL) + fu_device_set_physical_id(device, unique_id); + + /* success */ + return TRUE; +} + +static gboolean +fu_thunderbolt_controller_read_status_block(FuThunderboltController *self, GError **error) +{ + gsize nr_chunks; + g_autoptr(GFile) nvmem = NULL; + g_autoptr(GBytes) controller_fw = NULL; + g_autoptr(GInputStream) istr = NULL; + g_autoptr(FuFirmware) firmware = NULL; + + nvmem = fu_thunderbolt_device_find_nvmem(FU_THUNDERBOLT_DEVICE(self), TRUE, error); + if (nvmem == NULL) + return FALSE; + + /* 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; + firmware = fu_firmware_new_from_gtypes(controller_fw, + FWUPD_INSTALL_FLAG_NO_SEARCH, + error, + FU_TYPE_INTEL_THUNDERBOLT_NVM, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); + if (firmware == NULL) + return FALSE; + if (FU_IS_INTEL_THUNDERBOLT_FIRMWARE(firmware)) { + self->is_native = + fu_intel_thunderbolt_nvm_is_native(FU_INTEL_THUNDERBOLT_NVM(firmware)); + } + return TRUE; +} + +static gboolean +fu_thunderbolt_controller_can_update(FuThunderboltController *self) +{ + g_autoptr(GError) nvmem_error = NULL; + g_autoptr(GFile) non_active_nvmem = NULL; + + non_active_nvmem = + fu_thunderbolt_device_find_nvmem(FU_THUNDERBOLT_DEVICE(self), FALSE, &nvmem_error); + if (non_active_nvmem == NULL) { + g_debug("%s", nvmem_error->message); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_thunderbolt_controller_set_port_online_cb(gpointer user_data) +{ + FuThunderboltController *self = FU_THUNDERBOLT_CONTROLLER(user_data); + g_autoptr(GError) error_local = NULL; + + if (!fu_thunderbolt_udev_set_port_online(FU_UDEV_DEVICE(self), &error_local)) + g_warning("failed to set online after initial delay: %s", error_local->message); + + /* no longer valid */ + self->host_online_timer_id = 0; + return G_SOURCE_REMOVE; +} + +static gboolean +fu_thunderbolt_controller_setup_usb4(FuThunderboltController *self, GError **error) +{ + if (!fu_thunderbolt_udev_set_port_offline(FU_UDEV_DEVICE(self), error)) + return FALSE; + self->host_online_timer_id = + g_timeout_add_seconds(5, fu_thunderbolt_controller_set_port_online_cb, self); + return TRUE; +} + +static void +fu_thunderbolt_controller_set_signed(FuDevice *device) +{ + FuThunderboltController *self = FU_THUNDERBOLT_CONTROLLER(device); + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + const gchar *tmp; + + /* if it's a USB4 type not of host and generation 3; it's Intel */ + tmp = g_udev_device_get_property(udev_device, "USB4_TYPE"); + if (g_strcmp0(tmp, "host") != 0 && self->gen == 3) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); +} + +static gboolean +fu_thunderbolt_controller_setup(FuDevice *device, GError **error) +{ + FuThunderboltController *self = FU_THUNDERBOLT_CONTROLLER(device); + const gchar *tmp = NULL; + guint16 did; + guint16 vid; + g_autoptr(GError) error_gen = NULL; + g_autoptr(GError) error_version = NULL; + + /* try to read the version */ + if (!fu_thunderbolt_device_get_version(FU_THUNDERBOLT_DEVICE(self), &error_version)) { + if (self->controller_kind != FU_THUNDERBOLT_CONTROLLER_KIND_HOST && + g_error_matches(error_version, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_error(error, g_steal_pointer(&error_version)); + return FALSE; + } + g_debug("%s", error_version->message); + } + + /* these may be missing on ICL or later */ + vid = fu_udev_device_get_vendor(FU_UDEV_DEVICE(self)); + if (vid == 0x0) + g_debug("failed to get Vendor ID"); + + did = fu_udev_device_get_model(FU_UDEV_DEVICE(self)); + if (did == 0x0) + g_debug("failed to get Device ID"); + + /* requires kernel 5.5 or later, non-fatal if not available */ + self->gen = + fu_thunderbolt_udev_get_attr_uint16(FU_UDEV_DEVICE(self), "generation", &error_gen); + if (self->gen == 0) + g_debug("Unable to read generation: %s", error_gen->message); + + if (self->controller_kind == FU_THUNDERBOLT_CONTROLLER_KIND_HOST) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_summary(device, "Unmatched performance for high-speed I/O"); + } else { + tmp = fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(self), "device_name", NULL); + } + + /* set the controller name */ + if (tmp == NULL) + tmp = fu_thunderbolt_controller_kind_to_string(self); + fu_device_set_name(device, tmp); + + /* set vendor string */ + tmp = fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(self), "vendor_name", NULL); + if (tmp != NULL) + fu_device_set_vendor(device, tmp); + + if (fu_device_get_version(device) == NULL) + fu_thunderbolt_controller_check_safe_mode(self); + + if (self->safe_mode) { + fu_device_set_update_error(device, "Device is in safe mode"); + } else { + g_autofree gchar *device_id = NULL; + g_autofree gchar *domain_id = NULL; + if (fu_thunderbolt_controller_can_update(self)) { + const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + g_autofree gchar *vendor_id = NULL; + g_autofree gchar *domain = g_path_get_basename(devpath); + /* USB4 controllers don't have a concept of legacy vs native + * so don't try to read a native attribute from their NVM */ + if (self->controller_kind == FU_THUNDERBOLT_CONTROLLER_KIND_HOST && + self->gen < 4) { + /* read first block of firmware to get the is-native attribute */ + if (!fu_thunderbolt_controller_read_status_block(self, error)) + return FALSE; + } else { + self->is_native = FALSE; + } + domain_id = g_strdup_printf("TBT-%04x%04x%s-controller%s", + (guint)vid, + (guint)did, + self->is_native ? "-native" : "", + domain); + vendor_id = g_strdup_printf("TBT:0x%04X", (guint)vid); + fu_device_add_vendor_id(device, vendor_id); + device_id = g_strdup_printf("TBT-%04x%04x%s", + (guint)vid, + (guint)did, + self->is_native ? "-native" : ""); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + + /* check if device is authorized */ + if (!fu_thunderbolt_device_check_authorized(FU_THUNDERBOLT_DEVICE(self), + error)) + return FALSE; + + } else { + device_id = g_strdup("TBT-fixed"); + } + fu_device_add_instance_id(device, device_id); + if (domain_id != NULL) + fu_device_add_instance_id(device, domain_id); + } + + /* determine if we can update on unplug */ + if (fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), + "nvm_authenticate_on_disconnect", + NULL) != NULL) { + fu_thunderbolt_device_set_auth_method(FU_THUNDERBOLT_DEVICE(self), + "nvm_authenticate_on_disconnect"); + /* flushes image */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + /* forces the device to write to authenticate on disconnect attribute */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); + /* control the order of activation (less relevant; install too though) */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST); + } else { + fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + } + if (self->controller_kind == FU_THUNDERBOLT_CONTROLLER_KIND_HOST && + fu_device_has_private_flag(FU_DEVICE(self), + FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION)) { + g_autoptr(GError) error_local = NULL; + if (!fu_thunderbolt_controller_setup_usb4(self, &error_local)) + g_warning("failed to setup host: %s", error_local->message); + } + + /* set up signed payload attribute */ + fu_thunderbolt_controller_set_signed(device); + + /* success */ + return TRUE; +} + +static gboolean +fu_thunderbolt_controller_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + /* FuThunderboltDevice->write_firmware */ + if (!FU_DEVICE_CLASS(fu_thunderbolt_controller_parent_class) + ->write_firmware(device, firmware, progress, flags, error)) + return FALSE; + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_thunderbolt_controller_init(FuThunderboltController *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); +} + +static void +fu_thunderbolt_controller_finalize(GObject *object) +{ + FuThunderboltController *self = FU_THUNDERBOLT_CONTROLLER(object); + + if (self->host_online_timer_id != 0) + g_source_remove(self->host_online_timer_id); + + G_OBJECT_CLASS(fu_thunderbolt_controller_parent_class)->finalize(object); +} + +static void +fu_thunderbolt_controller_class_init(FuThunderboltControllerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_thunderbolt_controller_finalize; + klass_device->setup = fu_thunderbolt_controller_setup; + klass_device->probe = fu_thunderbolt_controller_probe; + klass_device->to_string = fu_thunderbolt_controller_to_string; + klass_device->write_firmware = fu_thunderbolt_controller_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-controller.h b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-controller.h new file mode 100644 index 0000000000000000000000000000000000000000..5b21ed67acff4e8ed66ccddd17c8d1ef376d3f9b --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-controller.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-thunderbolt-device.h" + +#define FU_TYPE_THUNDERBOLT_CONTROLLER (fu_thunderbolt_controller_get_type()) +G_DECLARE_FINAL_TYPE(FuThunderboltController, + fu_thunderbolt_controller, + FU, + THUNDERBOLT_CONTROLLER, + FuThunderboltDevice) diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-device.c b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-device.c new file mode 100644 index 0000000000000000000000000000000000000000..c14e632cf36a5bab9654f1199a262c70d8c8bb78 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-device.c @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2017 Christian J. Kellner + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "fu-thunderbolt-device.h" + +typedef struct { + const gchar *auth_method; +} FuThunderboltDevicePrivate; + +#define TBT_NVM_RETRY_TIMEOUT 200 /* ms */ +#define FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT 60000 /* ms */ + +G_DEFINE_TYPE_WITH_PRIVATE(FuThunderboltDevice, fu_thunderbolt_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_thunderbolt_device_get_instance_private(o)) + +GFile * +fu_thunderbolt_device_find_nvmem(FuThunderboltDevice *self, gboolean active, GError **error) +{ + const gchar *nvmem_dir = active ? "nvm_active" : "nvm_non_active"; + const gchar *name; + const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + g_autoptr(GDir) d = NULL; + + if (G_UNLIKELY(devpath == NULL)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return NULL; + } + + d = g_dir_open(devpath, 0, error); + if (d == NULL) + return NULL; + + while ((name = g_dir_read_name(d)) != NULL) { + if (g_str_has_prefix(name, nvmem_dir)) { + g_autoptr(GFile) parent = g_file_new_for_path(devpath); + g_autoptr(GFile) nvm_dir = g_file_get_child(parent, name); + return g_file_get_child(nvm_dir, "nvmem"); + } + } + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Could not find non-volatile memory location"); + return NULL; +} + +gboolean +fu_thunderbolt_device_check_authorized(FuThunderboltDevice *self, GError **error) +{ + guint64 status; + g_autofree gchar *attribute = NULL; + const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + /* read directly from file to prevent udev caching */ + g_autofree gchar *safe_path = g_build_path("/", devpath, "authorized", NULL); + + if (!g_file_test(safe_path, G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing authorized attribute"); + return FALSE; + } + + if (!g_file_get_contents(safe_path, &attribute, NULL, error)) + 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 'authorized: %s", + g_strerror(errno)); + return FALSE; + } + if (status == 1 || status == 2) + fu_device_uninhibit(FU_DEVICE(self), "not-authorized"); + else + fu_device_inhibit(FU_DEVICE(self), "not-authorized", "Not authorized"); + + return TRUE; +} + +gboolean +fu_thunderbolt_device_get_version(FuThunderboltDevice *self, GError **error) +{ + const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + g_auto(GStrv) split = NULL; + g_autofree gchar *version_raw = NULL; + g_autofree gchar *version = NULL; + /* read directly from file to prevent udev caching */ + g_autofree gchar *safe_path = g_build_path("/", devpath, "nvm_version", NULL); + + if (!g_file_test(safe_path, G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing nvm_version attribute"); + return FALSE; + } + + for (guint i = 0; i < 50; i++) { + g_autoptr(GError) error_local = NULL; + /* glib can't return a properly mapped -ENODATA but the + * kernel only returns -ENODATA or -EAGAIN */ + if (g_file_get_contents(safe_path, &version_raw, NULL, &error_local)) + break; + g_debug("Attempt %u: Failed to read NVM version", i); + g_usleep(TBT_NVM_RETRY_TIMEOUT * 1000); + /* safe mode probably */ + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + break; + } + + if (version_raw == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to read NVM"); + return FALSE; + } + split = g_strsplit(version_raw, ".", -1); + if (g_strv_length(split) != 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid nvm_version format: %s", + version_raw); + return FALSE; + } + + version = g_strdup_printf("%02x.%02x", + (guint)g_ascii_strtoull(split[0], NULL, 16), + (guint)g_ascii_strtoull(split[1], NULL, 16)); + fu_device_set_version(FU_DEVICE(self), version); + return TRUE; +} + +static void +fu_thunderbolt_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); + FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_thunderbolt_device_parent_class)->to_string(device, idt, str); + + fu_string_append(str, idt, "AuthMethod", priv->auth_method); +} + +void +fu_thunderbolt_device_set_auth_method(FuThunderboltDevice *self, const gchar *auth_method) +{ + FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); + priv->auth_method = auth_method; +} + +static gboolean +fu_thunderbolt_device_activate(FuDevice *device, FuProgress *progress, GError **error) +{ + FuUdevDevice *udev = FU_UDEV_DEVICE(device); + + return fu_udev_device_write_sysfs(udev, "nvm_authenticate", "1", error); +} + +static gboolean +fu_thunderbolt_device_authenticate(FuDevice *device, GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); + FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); + FuUdevDevice *udev = FU_UDEV_DEVICE(device); + + return fu_udev_device_write_sysfs(udev, priv->auth_method, "1", error); +} + +static gboolean +fu_thunderbolt_device_flush_update(FuDevice *device, GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); + FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); + FuUdevDevice *udev = FU_UDEV_DEVICE(device); + + return fu_udev_device_write_sysfs(udev, priv->auth_method, "2", error); +} + +static gboolean +fu_thunderbolt_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + const gchar *attribute; + guint64 status; + + /* now check if the update actually worked */ + attribute = + fu_udev_device_get_sysfs_attr(FU_UDEV_DEVICE(device), "nvm_authenticate", error); + if (attribute == NULL) + 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) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "update failed (status %" G_GINT64_MODIFIER "x)", + status); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_thunderbolt_device_rescan(FuDevice *device, GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); + + /* refresh updatability */ + if (!fu_thunderbolt_device_check_authorized(self, error)) + return FALSE; + + /* refresh the version */ + return fu_thunderbolt_device_get_version(self, error); +} + +static gboolean +fu_thunderbolt_device_write_data(FuThunderboltDevice *self, + GBytes *blob_fw, + FuProgress *progress, + GError **error) +{ + gsize fw_size; + gsize nwritten; + gssize n; + g_autoptr(GFile) nvmem = NULL; + g_autoptr(GOutputStream) os = NULL; + + nvmem = fu_thunderbolt_device_find_nvmem(self, FALSE, error); + if (nvmem == NULL) + return FALSE; + + os = (GOutputStream *)g_file_append_to(nvmem, G_FILE_CREATE_NONE, NULL, error); + + if (os == NULL) + return FALSE; + + nwritten = 0; + fw_size = g_bytes_get_size(blob_fw); + + do { + g_autoptr(GBytes) fw_data = NULL; + + fw_data = fu_bytes_new_offset(blob_fw, nwritten, fw_size - nwritten, error); + if (fw_data == NULL) + return FALSE; + + n = g_output_stream_write_bytes(os, fw_data, NULL, error); + if (n < 0) + return FALSE; + + nwritten += n; + fu_progress_set_percentage_full(progress, nwritten, fw_size); + + } while (nwritten < fw_size); + + if (nwritten != fw_size) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "Could not write all data to nvmem"); + return FALSE; + } + + return g_output_stream_close(os, NULL, error); +} + +static FuFirmware * +fu_thunderbolt_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuFirmware) firmware_old = NULL; + g_autoptr(GBytes) controller_fw = NULL; + g_autoptr(GFile) nvmem = NULL; + + /* parse */ + firmware = fu_firmware_new_from_gtypes(fw, + flags, + error, + FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); + if (firmware == NULL) + return NULL; + + /* get current NVMEM */ + nvmem = fu_thunderbolt_device_find_nvmem(self, TRUE, error); + if (nvmem == NULL) + return NULL; + controller_fw = g_file_load_bytes(nvmem, NULL, NULL, error); + if (controller_fw == NULL) + return NULL; + firmware_old = fu_firmware_new_from_gtypes(controller_fw, + flags, + error, + FU_TYPE_INTEL_THUNDERBOLT_NVM, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); + if (firmware_old == NULL) + return NULL; + if (!fu_firmware_check_compatible(firmware_old, firmware, flags, error)) + return NULL; + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_thunderbolt_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); + g_autoptr(GBytes) blob_fw = NULL; + + /* get default image */ + blob_fw = fu_firmware_get_bytes(firmware, error); + if (blob_fw == NULL) + return FALSE; + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_thunderbolt_device_write_data(self, blob_fw, progress, error)) { + g_prefix_error(error, + "could not write firmware to thunderbolt device at %s: ", + fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self))); + return FALSE; + } + + /* flush the image if supported by kernel and/or device */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { + if (!fu_thunderbolt_device_flush_update(device, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + } + + /* using an active delayed activation flow later (either shutdown or another plugin) */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART)) { + g_debug("Skipping Thunderbolt reset per quirk request"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + return TRUE; + } + + /* authenticate (possibly on unplug if device supports it) */ + if (!fu_thunderbolt_device_authenticate(FU_DEVICE(self), error)) { + g_prefix_error(error, "could not start thunderbolt device upgrade: "); + return FALSE; + } + + /* whether to wait for a device replug or not */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { + fu_device_set_remove_delay(device, FU_PLUGIN_THUNDERBOLT_UPDATE_TIMEOUT); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); + } + + return TRUE; +} + +static gboolean +fu_thunderbolt_device_probe(FuDevice *device, GError **error) +{ + g_autoptr(FuUdevDevice) udev_parent = NULL; + + /* if the PCI ID is Intel then it's signed, no idea otherwise */ + udev_parent = fu_udev_device_get_parent_with_subsystem(FU_UDEV_DEVICE(device), "pci"); + if (udev_parent != NULL) { + if (!fu_device_probe(FU_DEVICE(udev_parent), error)) + return FALSE; + if (fu_udev_device_get_vendor(udev_parent) == 0x8086) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } + + /* success */ + return TRUE; +} + +static void +fu_thunderbolt_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_thunderbolt_device_init(FuThunderboltDevice *self) +{ + FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); + priv->auth_method = "nvm_authenticate"; + fu_device_add_icon(FU_DEVICE(self), "thunderbolt"); + fu_device_add_protocol(FU_DEVICE(self), "com.intel.thunderbolt"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_thunderbolt_device_class_init(FuThunderboltDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->activate = fu_thunderbolt_device_activate; + klass_device->to_string = fu_thunderbolt_device_to_string; + klass_device->probe = fu_thunderbolt_device_probe; + klass_device->prepare_firmware = fu_thunderbolt_device_prepare_firmware; + klass_device->write_firmware = fu_thunderbolt_device_write_firmware; + klass_device->attach = fu_thunderbolt_device_attach; + klass_device->rescan = fu_thunderbolt_device_rescan; + klass_device->set_progress = fu_thunderbolt_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-device.h b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5e240b9a509784c3ae36fb363fce2f1985cd107e --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-device.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_THUNDERBOLT_DEVICE (fu_thunderbolt_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuThunderboltDevice, + fu_thunderbolt_device, + FU, + THUNDERBOLT_DEVICE, + FuUdevDevice) + +struct _FuThunderboltDeviceClass { + FuUdevDeviceClass parent_class; +}; + +gboolean +fu_thunderbolt_device_get_version(FuThunderboltDevice *self, GError **error); +GFile * +fu_thunderbolt_device_find_nvmem(FuThunderboltDevice *self, gboolean active, GError **error); +gboolean +fu_thunderbolt_device_check_authorized(FuThunderboltDevice *self, GError **error); +void +fu_thunderbolt_device_set_auth_method(FuThunderboltDevice *self, const gchar *auth_method); diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-plugin.c b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..2fdd0964525e711c865be5b0e2f84f49f9f02aca --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-plugin.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 Christian J. Kellner + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-thunderbolt-common.h" +#include "fu-thunderbolt-controller.h" +#include "fu-thunderbolt-plugin.h" +#include "fu-thunderbolt-retimer.h" + +struct _FuThunderboltPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuThunderboltPlugin, fu_thunderbolt_plugin, FU_TYPE_PLUGIN) + +/*5 seconds sleep until retimer is available \ + after nvm update*/ +#define FU_THUNDERBOLT_RETIMER_CLEANUP_DELAY 5000000 + +static gboolean +fu_thunderbolt_plugin_safe_kernel(FuPlugin *plugin, GError **error) +{ + g_autofree gchar *minimum_kernel = NULL; + + minimum_kernel = fu_plugin_get_config_value(plugin, "MinimumKernelVersion"); + if (minimum_kernel == NULL) { + g_debug("Ignoring kernel safety checks"); + return TRUE; + } + return fu_kernel_check_version(minimum_kernel, error); +} + +static gboolean +fu_thunderbolt_plugin_device_created(FuPlugin *plugin, FuDevice *dev, GError **error) +{ + fu_plugin_add_rule(plugin, + FU_PLUGIN_RULE_INHIBITS_IDLE, + "thunderbolt requires device wakeup"); + if (fu_plugin_get_config_value_boolean(plugin, "RetimerOfflineMode")) + fu_device_add_private_flag(dev, FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION); + return TRUE; +} + +static void +fu_thunderbolt_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") != 0) + return; + + /* Operating system will handle finishing updates later */ + if (fu_plugin_get_config_value_boolean(plugin, "DelayedActivation") && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { + g_debug("Turning on delayed activation for %s", fu_device_get_name(device)); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SKIPS_RESTART); + fu_device_remove_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + } +} + +static gboolean +fu_thunderbolt_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + return fu_thunderbolt_plugin_safe_kernel(plugin, error); +} + +static gboolean +fu_thunderbolt_plugin_composite_prepare(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index(devices, i); + if ((g_strcmp0(fu_device_get_plugin(dev), "thunderbolt") == 0) && + fu_device_has_private_flag(dev, FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION) && + fu_device_has_internal_flag(dev, FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE)) { + return fu_thunderbolt_retimer_set_parent_port_offline(dev, error); + } + } + return TRUE; +} + +static gboolean +fu_thunderbolt_plugin_composite_cleanup(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index(devices, i); + if ((g_strcmp0(fu_device_get_plugin(dev), "thunderbolt") == 0) && + fu_device_has_private_flag(dev, FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION) && + fu_device_has_internal_flag(dev, FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE)) { + g_usleep(FU_THUNDERBOLT_RETIMER_CLEANUP_DELAY); + return fu_thunderbolt_retimer_set_parent_port_online(dev, error); + } + } + return TRUE; +} + +static void +fu_thunderbolt_plugin_init(FuThunderboltPlugin *self) +{ +} + +static void +fu_thunderbolt_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "thunderbolt"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_THUNDERBOLT_CONTROLLER); + fu_plugin_add_device_gtype(plugin, FU_TYPE_THUNDERBOLT_RETIMER); +} + +static void +fu_thunderbolt_plugin_class_init(FuThunderboltPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_thunderbolt_plugin_constructed; + plugin_class->startup = fu_thunderbolt_plugin_startup; + plugin_class->device_registered = fu_thunderbolt_plugin_device_registered; + plugin_class->device_created = fu_thunderbolt_plugin_device_created; + plugin_class->composite_prepare = fu_thunderbolt_plugin_composite_prepare; + plugin_class->composite_cleanup = fu_thunderbolt_plugin_composite_cleanup; +} diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-plugin.h b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..dba722a607915a7ea67b377c6a137cbdd8baed16 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuThunderboltPlugin, fu_thunderbolt_plugin, FU, THUNDERBOLT_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-retimer.c b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-retimer.c new file mode 100644 index 0000000000000000000000000000000000000000..5c12aabe64fe75fe5b5f845735d268f4e6285a89 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-retimer.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2017 Christian J. Kellner + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-thunderbolt-common.h" +#include "fu-thunderbolt-retimer.h" + +struct _FuThunderboltRetimer { + FuThunderboltDevice parent_instance; +}; + +G_DEFINE_TYPE(FuThunderboltRetimer, fu_thunderbolt_retimer, FU_TYPE_THUNDERBOLT_DEVICE) + +static FuUdevDevice * +fu_thunderbolt_retimer_get_udev_grandparent(FuDevice *device, GError **error) +{ + g_autoptr(GUdevDevice) udev_parent1 = NULL; + g_autoptr(GUdevDevice) udev_parent2 = NULL; + GUdevDevice *udev_device = NULL; + FuThunderboltRetimer *self = FU_THUNDERBOLT_RETIMER(device); + + udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + if (udev_device == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get udev device for retimer"); + return NULL; + } + udev_parent1 = g_udev_device_get_parent(udev_device); + if (udev_parent1 == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get parent device for retimer"); + return NULL; + } + udev_parent2 = g_udev_device_get_parent(udev_parent1); + if (udev_parent2 == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get host router device for retimer"); + return NULL; + } + return fu_udev_device_new(fu_device_get_context(FU_DEVICE(self)), + g_steal_pointer(&udev_parent2)); +} + +gboolean +fu_thunderbolt_retimer_set_parent_port_offline(FuDevice *device, GError **error) +{ + g_autoptr(FuUdevDevice) parent = fu_thunderbolt_retimer_get_udev_grandparent(device, error); + if (parent == NULL) + return FALSE; + return fu_thunderbolt_udev_set_port_offline(parent, error); +} + +gboolean +fu_thunderbolt_retimer_set_parent_port_online(FuDevice *device, GError **error) +{ + g_autoptr(FuUdevDevice) parent = fu_thunderbolt_retimer_get_udev_grandparent(device, error); + if (parent == NULL) + return FALSE; + return fu_thunderbolt_udev_set_port_online(parent, error); +} + +static gboolean +fu_thunderbolt_retimer_probe(FuDevice *device, GError **error) +{ + const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)); + g_autofree gchar *physical_id = g_path_get_basename(devpath); + + /* device */ + if (physical_id != NULL) + fu_device_set_physical_id(device, physical_id); + + return TRUE; +} + +static gboolean +fu_thunderbolt_retimer_setup(FuDevice *device, GError **error) +{ + FuThunderboltRetimer *self = FU_THUNDERBOLT_RETIMER(device); + guint16 did; + guint16 vid; + g_autofree gchar *instance = NULL; + + /* get version */ + if (!fu_thunderbolt_device_get_version(FU_THUNDERBOLT_DEVICE(self), error)) + return FALSE; + + /* as defined in PCIe 4.0 spec */ + vid = fu_udev_device_get_vendor(FU_UDEV_DEVICE(self)); + if (vid == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing vendor id"); + return FALSE; + } + did = fu_udev_device_get_model(FU_UDEV_DEVICE(self)); + if (did == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "missing device id"); + return FALSE; + } + + instance = g_strdup_printf("TBT-%04x%04x-retimer%s", + (guint)vid, + (guint)did, + fu_device_get_physical_id(device)); + fu_device_add_instance_id(device, instance); + + /* hardcoded for now: + * 1. unsure if ID_VENDOR_FROM_DATABASE works in this instance + * 2. we don't recognize anyone else yet + */ + if (fu_device_get_vendor(device) == NULL) + fu_device_set_vendor(device, "Intel"); + + /* success */ + return TRUE; +} + +static void +fu_thunderbolt_retimer_init(FuThunderboltRetimer *self) +{ + fu_device_set_name(FU_DEVICE(self), "USB4 Retimer"); + fu_device_set_summary( + FU_DEVICE(self), + "A physical layer protocol-aware, software-transparent extension device " + "that forms two separate electrical link segments"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE); +} + +static void +fu_thunderbolt_retimer_class_init(FuThunderboltRetimerClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_thunderbolt_retimer_setup; + klass_device->probe = fu_thunderbolt_retimer_probe; +} diff --git a/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-retimer.h b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-retimer.h new file mode 100644 index 0000000000000000000000000000000000000000..40d137129b520002414895d5dc06338161f8d10c --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/fu-thunderbolt-retimer.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-thunderbolt-device.h" + +#define FU_TYPE_THUNDERBOLT_RETIMER (fu_thunderbolt_retimer_get_type()) +G_DECLARE_FINAL_TYPE(FuThunderboltRetimer, + fu_thunderbolt_retimer, + FU, + THUNDERBOLT_RETIMER, + FuThunderboltDevice) + +gboolean +fu_thunderbolt_retimer_set_parent_port_offline(FuDevice *device, GError **error); + +gboolean +fu_thunderbolt_retimer_set_parent_port_online(FuDevice *device, GError **error); diff --git a/fwupd-1.8.6/plugins/thunderbolt/meson.build b/fwupd-1.8.6/plugins/thunderbolt/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..71d930e62384905cba310699ac62eace1e418d45 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/meson.build @@ -0,0 +1,56 @@ +if gudev.found() and (host_cpu == 'x86' or host_cpu == 'x86_64') +cargs = ['-DG_LOG_DOMAIN="FuPluginThunderbolt"'] + +plugin_quirks += files('thunderbolt.quirk') + +plugin_builtin_thunderbolt = static_library('fu_plugin_thunderbolt', + sources: [ + 'fu-thunderbolt-plugin.c', + 'fu-thunderbolt-common.c', + 'fu-thunderbolt-device.c', + 'fu-thunderbolt-retimer.c', + 'fu-thunderbolt-controller.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_thunderbolt + +install_data(['thunderbolt.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') +) +# we use functions from 2.52 in the tests +if get_option('tests') and run_sanitize_unsafe_tests and umockdev.found() and gio.version().version_compare('>= 2.52') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + env.set('FWUPD_DATADIR_QUIRKS', meson.current_source_dir()) + e = executable( + 'thunderbolt-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + gudev, + plugin_deps, + umockdev, + ], + link_with: [ + fwupd, + fwupdplugin, + plugin_builtin_thunderbolt, + ], + c_args: cargs + ) + if get_option('b_sanitize') == 'address' + env.prepend('LD_PRELOAD', 'libasan.so.5', 'libumockdev-preload.so.0', separator: ' ') + else + env.prepend('LD_PRELOAD', 'libumockdev-preload.so.0') + endif + test('thunderbolt-self-test', e, env: env, timeout: 120) +endif +endif diff --git a/fwupd-1.8.6/plugins/thunderbolt/tests/COPYING b/fwupd-1.8.6/plugins/thunderbolt/tests/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..1a770abceb7028f6ab5ec7d5f7e0c0395003d30c --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/tests/COPYING @@ -0,0 +1,24 @@ +Copyright(c) 2017 Intel Corporation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/fwupd-1.8.6/plugins/thunderbolt/tests/colorhug.bin b/fwupd-1.8.6/plugins/thunderbolt/tests/colorhug.bin new file mode 120000 index 0000000000000000000000000000000000000000..b659b386900f0d0e1fcf73f5dd033a5656825d4f --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/tests/colorhug.bin @@ -0,0 +1 @@ +../../../src/tests/colorhug/firmware.bin \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/thunderbolt/tests/minimal-fw-controller.bin b/fwupd-1.8.6/plugins/thunderbolt/tests/minimal-fw-controller.bin new file mode 100644 index 0000000000000000000000000000000000000000..f7d45e415e6a8378010302cf17d789b7de17dafd Binary files /dev/null and b/fwupd-1.8.6/plugins/thunderbolt/tests/minimal-fw-controller.bin differ diff --git a/fwupd-1.8.6/plugins/thunderbolt/tests/minimal-fw.bin b/fwupd-1.8.6/plugins/thunderbolt/tests/minimal-fw.bin new file mode 100644 index 0000000000000000000000000000000000000000..405eb5ecd21a7b44ef515097ac5688b15e4fa458 Binary files /dev/null and b/fwupd-1.8.6/plugins/thunderbolt/tests/minimal-fw.bin differ diff --git a/fwupd-1.8.6/plugins/thunderbolt/thunderbolt.conf b/fwupd-1.8.6/plugins/thunderbolt/thunderbolt.conf new file mode 100644 index 0000000000000000000000000000000000000000..efaecb4bf090ebffe309bbc1255cf74583d27ec0 --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/thunderbolt.conf @@ -0,0 +1,12 @@ +[thunderbolt] + +# Minimum kernel version to allow use of this plugin +# It's important that all backports from this kernel have been +# made if using an older kernel +MinimumKernelVersion=4.13.0 + +# Forces delaying activation until shutdown/logout/reboot +DelayedActivation=false + +# Uses offline mode interface to update retimers +RetimerOfflineMode=false diff --git a/fwupd-1.8.6/plugins/thunderbolt/thunderbolt.quirk b/fwupd-1.8.6/plugins/thunderbolt/thunderbolt.quirk new file mode 100644 index 0000000000000000000000000000000000000000..a4f036a17928ddcf6ace6d6ab5af3e8312ec248d --- /dev/null +++ b/fwupd-1.8.6/plugins/thunderbolt/thunderbolt.quirk @@ -0,0 +1,7 @@ +[THUNDERBOLT\TYPE_THUNDERBOLT_DEVICE] +Plugin = thunderbolt +GType = FuThunderboltController + +[THUNDERBOLT\TYPE_THUNDERBOLT_RETIMER] +Plugin = thunderbolt +GType = FuThunderboltRetimer diff --git a/fwupd-1.8.6/plugins/tpm/README.md b/fwupd-1.8.6/plugins/tpm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9b8d7357f5e3f86d38facb830647ea8690b05630 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/README.md @@ -0,0 +1,40 @@ +# TPM + +## Introduction + +This allows enumerating Trusted Platform Modules, also known as "TPM" devices, +although it does not allow the user to update the firmware on them. + +The TPM Event Log records which events are registered for the PCR0 hash, which +may help in explaining why PCR0 values are differing for some firmware. + +The device exposed is not upgradable in any way and is just for debugging. +The created device will be a child device of the system TPM device, which may +or may not be upgradable. + +## GUID Generation + +These devices use custom GUIDs: + +* `TPM\VEN_$(manufacturer)&DEV_$(type)` +* `TPM\VEN_$(manufacturer)&MOD_$(vendor-string)` +* `TPM\VEN_$(manufacturer)&DEV_$(type)_VER_$(family)`, +* `TPM\VEN_$(manufacturer)&MOD_$(vendor-string)_VER_$(family)` + +...where `family` is either `2.0` or `1.2` + +Example GUIDs from a real system containing a TPM from Intel: + +```text + Guid: 34801700-3a50-5b05-820c-fe14580e4c2d <- TPM\VEN_INTC&DEV_0000 + Guid: 03f304f4-223e-54f4-b2c1-c3cf3b5817c6 <- TPM\VEN_INTC&DEV_0000&VER_2.0 +``` + +## Vendor ID Security + +The device is not upgradable and thus requires no vendor ID set. + +## External Interface Access + +This plugin uses the tpm2-tss library to access the TPM. It requires access to `/sys/class/tpm` +and optionally requires read only access to `/sys/kernel/security/tpm0/binary_bios_measurements`. diff --git a/fwupd-1.8.6/plugins/tpm/fu-self-test.c b/fwupd-1.8.6/plugins/tpm/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..6b11e98196c8a2733ac601d4940879ccf58d8f1c --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-self-test.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-context-private.h" +#include "fu-plugin-private.h" +#include "fu-security-attrs-private.h" +#include "fu-tpm-eventlog-common.h" +#include "fu-tpm-eventlog-parser.h" +#include "fu-tpm-plugin.h" +#include "fu-tpm-v1-device.h" +#include "fu-tpm-v2-device.h" + +static void +fu_tpm_device_1_2_func(void) +{ + FuTpmDevice *device; + GPtrArray *devices; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new(); + g_autoptr(FwupdSecurityAttr) attr0 = NULL; + g_autoptr(FwupdSecurityAttr) attr1 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + g_autoptr(GPtrArray) pcrXs = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load the plugin */ + plugin = fu_plugin_new_from_gtype(fu_tpm_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* get the v1.2 device */ + devices = fu_plugin_get_devices(plugin); + g_assert_cmpint(devices->len, ==, 1); + device = g_ptr_array_index(devices, 0); + g_assert_true(FU_IS_TPM_DEVICE(device)); + + /* verify checksums set correctly */ + pcr0s = fu_tpm_device_get_checksums(device, 0); + g_assert_nonnull(pcr0s); + g_assert_cmpint(pcr0s->len, ==, 1); + pcrXs = fu_tpm_device_get_checksums(device, 999); + g_assert_nonnull(pcrXs); + g_assert_cmpint(pcrXs->len, ==, 0); + + /* verify HSI attributes */ + fu_plugin_runner_add_security_attrs(plugin, attrs); + attr0 = fu_security_attrs_get_by_appstream_id(attrs, FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20); + g_assert_nonnull(attr0); + g_assert_cmpint(fwupd_security_attr_get_result(attr0), + ==, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + + attr1 = fu_security_attrs_get_by_appstream_id(attrs, FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR); + g_assert_nonnull(attr1); + /* Some PCRs are empty, but PCRs 0-7 are set (tests/tpm0/pcrs) */ + g_assert_cmpint(fwupd_security_attr_get_result(attr1), + ==, + FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_tpm_device_2_0_func(void) +{ + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuTpmDevice) device = fu_tpm_v2_device_new(ctx); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + g_autoptr(GPtrArray) pcrXs = NULL; + const gchar *tpm_server_running = g_getenv("TPM_SERVER_RUNNING"); + (void)g_setenv("FWUPD_FORCE_TPM2", "1", TRUE); + +#ifdef HAVE_GETUID + if (tpm_server_running == NULL && (getuid() != 0 || geteuid() != 0)) { + g_test_skip("TPM2.0 tests require simulated TPM2.0 running or need root access " + "with physical TPM"); + g_unsetenv("FWUPD_FORCE_TPM2"); + return; + } +#endif + + if (!fu_device_setup(FU_DEVICE(device), &error)) { + if (tpm_server_running == NULL && + g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_test_skip("no physical or simulated TPM 2.0 device available"); + g_unsetenv("FWUPD_FORCE_TPM2"); + return; + } + } + g_assert_no_error(error); + pcr0s = fu_tpm_device_get_checksums(device, 0); + g_assert_nonnull(pcr0s); + g_assert_cmpint(pcr0s->len, >=, 1); + pcrXs = fu_tpm_device_get_checksums(device, 999); + g_assert_nonnull(pcrXs); + g_assert_cmpint(pcrXs->len, ==, 0); + g_unsetenv("FWUPD_FORCE_TPM2"); +} + +static void +fu_tpm_eventlog_parse_v1_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + const gchar *tmp; + gboolean ret; + gsize bufsz = 0; + g_autofree gchar *fn = NULL; + g_autofree guint8 *buf = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "binary_bios_measurements-v1", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing binary_bios_measurements-v1"); + return; + } + ret = g_file_get_contents(fn, (gchar **)&buf, &bufsz, &error); + g_assert_no_error(error); + g_assert_true(ret); + + items = fu_tpm_eventlog_parser_new(buf, bufsz, FU_TPM_EVENTLOG_PARSER_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_nonnull(items); + + pcr0s = fu_tpm_eventlog_calc_checksums(items, 0, &error); + g_assert_no_error(error); + g_assert_nonnull(pcr0s); + g_assert_cmpint(pcr0s->len, ==, 1); + tmp = g_ptr_array_index(pcr0s, 0); + g_assert_cmpstr(tmp, ==, "543ae96e57b6fc4003531cd0dab1d9ba7f8166e0"); +} + +static void +fu_tpm_eventlog_parse_v2_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + const gchar *tmp; + gboolean ret; + gsize bufsz = 0; + g_autofree gchar *fn = NULL; + g_autofree guint8 *buf = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "binary_bios_measurements-v2", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing binary_bios_measurements-v2"); + return; + } + ret = g_file_get_contents(fn, (gchar **)&buf, &bufsz, &error); + g_assert_no_error(error); + g_assert_true(ret); + + items = fu_tpm_eventlog_parser_new(buf, bufsz, FU_TPM_EVENTLOG_PARSER_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_nonnull(items); + + pcr0s = fu_tpm_eventlog_calc_checksums(items, 0, &error); + g_assert_no_error(error); + g_assert_nonnull(pcr0s); + g_assert_cmpint(pcr0s->len, ==, 2); + tmp = g_ptr_array_index(pcr0s, 0); + g_assert_cmpstr(tmp, ==, "ebead4b31c7c49e193c440cd6ee90bc1b61a3ca6"); + tmp = g_ptr_array_index(pcr0s, 1); + g_assert_cmpstr(tmp, + ==, + "6d9fed68092cfb91c9552bcb7879e75e1df36efd407af67690dc3389a5722fab"); +} + +static void +fu_tpm_empty_pcr_func(void) +{ + gboolean ret; + g_autofree gchar *testdatadir = NULL; + g_auto(GStrv) environ_old = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new(); + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* save environment and set broken PCR data */ + environ_old = g_get_environ(); + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", "empty_pcr", NULL); + (void)g_setenv("FWUPD_SYSFSTPMDIR", testdatadir, TRUE); + + /* load the plugin */ + plugin = fu_plugin_new_from_gtype(fu_tpm_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* verify HSI attr */ + fu_plugin_runner_add_security_attrs(plugin, attrs); + attr = fu_security_attrs_get_by_appstream_id(attrs, FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR); + g_assert_nonnull(attr); + /* PCR 6 is empty (tests/empty_pcr/tpm0/pcrs) */ + g_assert_cmpint(fwupd_security_attr_get_result(attr), + ==, + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + + /* restore default environment */ + (void)g_setenv("FWUPD_SYSFSTPMDIR", + g_environ_getenv(environ_old, "FWUPD_SYSFSTPMDIR"), + TRUE); +} + +int +main(int argc, char **argv) +{ + g_autofree gchar *testdatadir = NULL; + + g_test_init(&argc, &argv, NULL); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSTPMDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_UEFI_TEST", "1", TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/tpm/pcrs1.2", fu_tpm_device_1_2_func); + g_test_add_func("/tpm/pcrs2.0", fu_tpm_device_2_0_func); + g_test_add_func("/tpm/empty-pcr", fu_tpm_empty_pcr_func); + g_test_add_func("/tpm/eventlog-parse{v1}", fu_tpm_eventlog_parse_v1_func); + g_test_add_func("/tpm/eventlog-parse{v2}", fu_tpm_eventlog_parse_v2_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-device.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-device.c new file mode 100644 index 0000000000000000000000000000000000000000..f09f10264a1137c728bca0844143f526e14a2ce3 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-device.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-tpm-device.h" + +typedef struct { + guint idx; + gchar *checksum; +} FuTpmDevicePcrItem; + +typedef struct { + gchar *family; + GPtrArray *items; /* of FuTpmDevicePcrItem */ +} FuTpmDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuTpmDevice, fu_tpm_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_tpm_device_get_instance_private(o)) + +void +fu_tpm_device_set_family(FuTpmDevice *self, const gchar *family) +{ + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_TPM_DEVICE(self)); + priv->family = g_strdup(family); +} + +const gchar * +fu_tpm_device_get_family(FuTpmDevice *self) +{ + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_TPM_DEVICE(self), NULL); + return priv->family; +} + +void +fu_tpm_device_add_checksum(FuTpmDevice *self, guint idx, const gchar *checksum) +{ + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + FuTpmDevicePcrItem *item = g_new0(FuTpmDevicePcrItem, 1); + + g_return_if_fail(FU_IS_TPM_DEVICE(self)); + g_return_if_fail(checksum != NULL); + + item->idx = idx; + item->checksum = g_strdup(checksum); + g_debug("added PCR-%02u=%s", item->idx, item->checksum); + g_ptr_array_add(priv->items, item); +} + +GPtrArray * +fu_tpm_device_get_checksums(FuTpmDevice *self, guint idx) +{ + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(FU_IS_TPM_DEVICE(self), NULL); + + for (guint i = 0; i < priv->items->len; i++) { + FuTpmDevicePcrItem *item = g_ptr_array_index(priv->items, i); + if (item->idx == idx) + g_ptr_array_add(array, g_strdup(item->checksum)); + } + return g_steal_pointer(&array); +} + +static void +fu_tpm_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuTpmDevice *self = FU_TPM_DEVICE(device); + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + if (priv->family != NULL) + fu_string_append(str, idt, "Family", priv->family); +} + +static void +fu_tpm_v2_device_item_free(FuTpmDevicePcrItem *item) +{ + g_free(item->checksum); + g_free(item); +} + +static void +fu_tpm_device_finalize(GObject *object) +{ + FuTpmDevice *self = FU_TPM_DEVICE(object); + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + + g_free(priv->family); + g_ptr_array_unref(priv->items); + + G_OBJECT_CLASS(fu_tpm_device_parent_class)->finalize(object); +} + +static void +fu_tpm_device_init(FuTpmDevice *self) +{ + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + priv->items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_tpm_v2_device_item_free); + fu_device_set_name(FU_DEVICE(self), "TPM"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_udev_device_set_flags(FU_UDEV_DEVICE(self), FU_UDEV_DEVICE_FLAG_NONE); + fu_device_add_instance_id_full(FU_DEVICE(self), + "system-tpm", + FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS); +} + +static void +fu_tpm_device_class_init(FuTpmDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + object_class->finalize = fu_tpm_device_finalize; + klass_device->to_string = fu_tpm_device_to_string; +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-device.h b/fwupd-1.8.6/plugins/tpm/fu-tpm-device.h new file mode 100644 index 0000000000000000000000000000000000000000..32852d881a8965595acd08fe8cb42e1496bf8f23 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-device.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_TPM_DEVICE (fu_tpm_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuTpmDevice, fu_tpm_device, FU, TPM_DEVICE, FuUdevDevice) + +struct _FuTpmDeviceClass { + FuDeviceClass parent_class; + gpointer __reserved[31]; +}; + +void +fu_tpm_device_set_family(FuTpmDevice *self, const gchar *family); +const gchar * +fu_tpm_device_get_family(FuTpmDevice *self); +void +fu_tpm_device_add_checksum(FuTpmDevice *self, guint idx, const gchar *checksum); +GPtrArray * +fu_tpm_device_get_checksums(FuTpmDevice *self, guint idx); diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-common.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-common.c new file mode 100644 index 0000000000000000000000000000000000000000..606774801961adec7ccbbf4dd5fe3e0a0a4a1c0f --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-common.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-tpm-eventlog-common.h" + +const gchar * +fu_tpm_eventlog_pcr_to_string(gint pcr) +{ + if (pcr == 0) + return "BIOS"; + if (pcr == 1) + return "BIOS Configuration"; + if (pcr == 2) + return "Option ROMs"; + if (pcr == 3) + return "Option ROM configuration"; + if (pcr == 4) + return "Initial program loader code"; + if (pcr == 5) + return "Initial program loader code configuration"; + if (pcr == 6) + return "State transitions and wake events"; + if (pcr == 7) + return "Platform manufacturer specific measurements"; + if (pcr >= 8 && pcr <= 15) + return "Static operating system"; + if (pcr == 16) + return "Debug"; + if (pcr == 17) + return "Dynamic root of trust measurement and launch control policy"; + if (pcr >= 18 && pcr <= 22) + return "Trusted OS"; + if (pcr == 23) + return "Application support"; + return "Undefined"; +} + +const gchar * +fu_tpm_eventlog_hash_to_string(TPM2_ALG_ID hash_kind) +{ + if (hash_kind == TPM2_ALG_SHA1) + return "SHA1"; + if (hash_kind == TPM2_ALG_SHA256) + return "SHA256"; + if (hash_kind == TPM2_ALG_SHA384) + return "SHA384"; + if (hash_kind == TPM2_ALG_SHA512) + return "SHA512"; + return NULL; +} + +guint32 +fu_tpm_eventlog_hash_get_size(TPM2_ALG_ID hash_kind) +{ + if (hash_kind == TPM2_ALG_SHA1) + return TPM2_SHA1_DIGEST_SIZE; + if (hash_kind == TPM2_ALG_SHA256) + return TPM2_SHA256_DIGEST_SIZE; + if (hash_kind == TPM2_ALG_SHA384) + return TPM2_SHA384_DIGEST_SIZE; + if (hash_kind == TPM2_ALG_SHA512) + return TPM2_SHA512_DIGEST_SIZE; + return 0; +} + +const gchar * +fu_tpm_eventlog_item_kind_to_string(FuTpmEventlogItemKind event_type) +{ + if (event_type == EV_PREBOOT_CERT) + return "EV_PREBOOT_CERT"; + if (event_type == EV_POST_CODE) + return "EV_POST_CODE"; + if (event_type == EV_NO_ACTION) + return "EV_NO_ACTION"; + if (event_type == EV_SEPARATOR) + return "EV_SEPARATOR"; + if (event_type == EV_ACTION) + return "EV_ACTION"; + if (event_type == EV_EVENT_TAG) + return "EV_EVENT_TAG"; + if (event_type == EV_S_CRTM_CONTENTS) + return "EV_S_CRTM_CONTENTS"; + if (event_type == EV_S_CRTM_VERSION) + return "EV_S_CRTM_VERSION"; + if (event_type == EV_CPU_MICROCODE) + return "EV_CPU_MICROCODE"; + if (event_type == EV_PLATFORM_CONFIG_FLAGS) + return "EV_PLATFORM_CONFIG_FLAGS"; + if (event_type == EV_TABLE_OF_DEVICES) + return "EV_TABLE_OF_DEVICES"; + if (event_type == EV_COMPACT_HASH) + return "EV_COMPACT_HASH"; + if (event_type == EV_NONHOST_CODE) + return "EV_NONHOST_CODE"; + if (event_type == EV_NONHOST_CONFIG) + return "EV_NONHOST_CONFIG"; + if (event_type == EV_NONHOST_INFO) + return "EV_NONHOST_INFO"; + if (event_type == EV_OMIT_BOOT_DEVICE_EVENTS) + return "EV_OMIT_BOOT_DEVICE_EVENTS"; + if (event_type == EV_EFI_EVENT_BASE) + return "EV_EFI_EVENT_BASE"; + if (event_type == EV_EFI_VARIABLE_DRIVER_CONFIG) + return "EV_EFI_VARIABLE_DRIVER_CONFIG"; + if (event_type == EV_EFI_VARIABLE_BOOT) + return "EV_EFI_VARIABLE_BOOT"; + if (event_type == EV_EFI_BOOT_SERVICES_APPLICATION) + return "EV_BOOT_SERVICES_APPLICATION"; + if (event_type == EV_EFI_BOOT_SERVICES_DRIVER) + return "EV_EFI_BOOT_SERVICES_DRIVER"; + if (event_type == EV_EFI_RUNTIME_SERVICES_DRIVER) + return "EV_EFI_RUNTIME_SERVICES_DRIVER"; + if (event_type == EV_EFI_GPT_EVENT) + return "EV_EFI_GPT_EVENT"; + if (event_type == EV_EFI_ACTION) + return "EV_EFI_ACTION"; + if (event_type == EV_EFI_PLATFORM_FIRMWARE_BLOB) + return "EV_EFI_PLATFORM_FIRMWARE_BLOB"; + if (event_type == EV_EFI_HANDOFF_TABLES) + return "EV_EFI_HANDOFF_TABLES"; + if (event_type == EV_EFI_HCRTM_EVENT) + return "EV_EFI_HCRTM_EVENT"; + if (event_type == EV_EFI_VARIABLE_AUTHORITY) + return "EV_EFI_EFI_VARIABLE_AUTHORITY"; + return NULL; +} + +gchar * +fu_tpm_eventlog_strhex(GBytes *blob) +{ + GString *csum = g_string_new(NULL); + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(blob, &bufsz); + for (guint i = 0; i < bufsz; i++) + g_string_append_printf(csum, "%02x", buf[i]); + return g_string_free(csum, FALSE); +} + +gchar * +fu_tpm_eventlog_blobstr(GBytes *blob) +{ + g_return_val_if_fail(blob != NULL, NULL); + return g_base64_encode((const guchar *)g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob)); +} + +typedef struct __attribute__((packed)) { + guint8 Signature[16]; + guint8 StartupLocality; +} TCG_EfiStartupLocalityEvent; + +GPtrArray * +fu_tpm_eventlog_calc_checksums(GPtrArray *items, guint8 pcr, GError **error) +{ + guint cnt_sha1 = 0; + guint cnt_sha256 = 0; + guint8 digest_sha1[TPM2_SHA1_DIGEST_SIZE] = {0x0}; + guint8 digest_sha256[TPM2_SHA256_DIGEST_SIZE] = {0x0}; + gsize digest_sha1_len = sizeof(digest_sha1); + gsize digest_sha256_len = sizeof(digest_sha256); + g_autoptr(GPtrArray) csums = g_ptr_array_new_with_free_func(g_free); + + /* sanity check */ + if (items->len == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no event log data"); + return NULL; + } + + /* take existing PCR hash, append new measurement to that, + * hash that with the same algorithm */ + for (guint i = 0; i < items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index(items, i); + if (item->pcr != pcr) + continue; + + /* if TXT is enabled then the first event for PCR0 should be a StartupLocality */ + if (item->kind == EV_NO_ACTION && item->pcr == 0 && item->blob != NULL && i == 0) { + gsize bufsz; + const guint8 *buf = g_bytes_get_data(item->blob, &bufsz); + if (bufsz == sizeof(TCG_EfiStartupLocalityEvent)) { + /* the final byte indicates the locality from which TPM2_Startup() + * was issued -- which is the initial value of PCR0 */ + if (strncmp((const char *)buf, "StartupLocality", bufsz - 2) == 0) { + digest_sha256[TPM2_SHA256_DIGEST_SIZE - 1] = buf[bufsz - 1]; + digest_sha1[TPM2_SHA1_DIGEST_SIZE - 1] = buf[bufsz - 1]; + continue; + } + } + } + + if (item->checksum_sha1 != NULL) { + g_autoptr(GChecksum) csum_sha1 = g_checksum_new(G_CHECKSUM_SHA1); + g_checksum_update(csum_sha1, (const guchar *)digest_sha1, digest_sha1_len); + g_checksum_update( + csum_sha1, + (const guchar *)g_bytes_get_data(item->checksum_sha1, NULL), + g_bytes_get_size(item->checksum_sha1)); + g_checksum_get_digest(csum_sha1, digest_sha1, &digest_sha1_len); + cnt_sha1++; + } + if (item->checksum_sha256 != NULL) { + g_autoptr(GChecksum) csum_sha256 = g_checksum_new(G_CHECKSUM_SHA256); + g_checksum_update(csum_sha256, + (const guchar *)digest_sha256, + digest_sha256_len); + g_checksum_update( + csum_sha256, + (const guchar *)g_bytes_get_data(item->checksum_sha256, NULL), + g_bytes_get_size(item->checksum_sha256)); + g_checksum_get_digest(csum_sha256, digest_sha256, &digest_sha256_len); + cnt_sha256++; + } + } + if (cnt_sha1 == 0 && cnt_sha256 == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no SHA1 or SHA256 data"); + return NULL; + } + if (cnt_sha1 > 0) { + g_autoptr(GBytes) blob_sha1 = NULL; + blob_sha1 = g_bytes_new_static(digest_sha1, sizeof(digest_sha1)); + g_ptr_array_add(csums, fu_tpm_eventlog_strhex(blob_sha1)); + } + if (cnt_sha256 > 0) { + g_autoptr(GBytes) blob_sha256 = NULL; + blob_sha256 = g_bytes_new_static(digest_sha256, sizeof(digest_sha256)); + g_ptr_array_add(csums, fu_tpm_eventlog_strhex(blob_sha256)); + } + return g_steal_pointer(&csums); +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-common.h b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-common.h new file mode 100644 index 0000000000000000000000000000000000000000..217221ece40bb7a3ef643621f2a21aa228f4b7b2 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-common.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +typedef enum { + EV_PREBOOT_CERT = 0x00000000, + EV_POST_CODE = 0x00000001, + EV_NO_ACTION = 0x00000003, + EV_SEPARATOR = 0x00000004, + EV_ACTION = 0x00000005, + EV_EVENT_TAG = 0x00000006, + EV_S_CRTM_CONTENTS = 0x00000007, + EV_S_CRTM_VERSION = 0x00000008, + EV_CPU_MICROCODE = 0x00000009, + EV_PLATFORM_CONFIG_FLAGS = 0x0000000a, + EV_TABLE_OF_DEVICES = 0x0000000b, + EV_COMPACT_HASH = 0x0000000c, + EV_NONHOST_CODE = 0x0000000f, + EV_NONHOST_CONFIG = 0x00000010, + EV_NONHOST_INFO = 0x00000011, + EV_OMIT_BOOT_DEVICE_EVENTS = 0x00000012, + EV_EFI_EVENT_BASE = 0x80000000, + EV_EFI_VARIABLE_DRIVER_CONFIG = 0x80000001, + EV_EFI_VARIABLE_BOOT = 0x80000002, + EV_EFI_BOOT_SERVICES_APPLICATION = 0x80000003, + EV_EFI_BOOT_SERVICES_DRIVER = 0x80000004, + EV_EFI_RUNTIME_SERVICES_DRIVER = 0x80000005, + EV_EFI_GPT_EVENT = 0x80000006, + EV_EFI_ACTION = 0x80000007, + EV_EFI_PLATFORM_FIRMWARE_BLOB = 0x80000008, + EV_EFI_HANDOFF_TABLES = 0x80000009, + EV_EFI_HCRTM_EVENT = 0x80000010, + EV_EFI_VARIABLE_AUTHORITY = 0x800000e0 +} FuTpmEventlogItemKind; + +typedef struct { + guint8 pcr; + FuTpmEventlogItemKind kind; + GBytes *checksum_sha1; + GBytes *checksum_sha256; + GBytes *blob; +} FuTpmEventlogItem; + +const gchar * +fu_tpm_eventlog_pcr_to_string(gint pcr); +const gchar * +fu_tpm_eventlog_hash_to_string(TPM2_ALG_ID hash_kind); +guint32 +fu_tpm_eventlog_hash_get_size(TPM2_ALG_ID hash_kind); +const gchar * +fu_tpm_eventlog_item_kind_to_string(FuTpmEventlogItemKind event_type); +gchar * +fu_tpm_eventlog_strhex(GBytes *blob); +gchar * +fu_tpm_eventlog_blobstr(GBytes *blob); +GPtrArray * +fu_tpm_eventlog_calc_checksums(GPtrArray *items, guint8 pcr, GError **error); diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-parser.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-parser.c new file mode 100644 index 0000000000000000000000000000000000000000..d57aece9777c6de062e3b3d62b95b3592b515bdd --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-parser.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-tpm-eventlog-parser.h" + +#define FU_TPM_EVENTLOG_V1_IDX_PCR 0x00 +#define FU_TPM_EVENTLOG_V1_IDX_TYPE 0x04 +#define FU_TPM_EVENTLOG_V1_IDX_DIGEST 0x08 +#define FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE 0x1c +#define FU_TPM_EVENTLOG_V1_SIZE 0x20 + +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SIGNATURE 0x00 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_PLATFORM_CLASS 0x10 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SPEC_VERSION_MINOR 0x14 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SPEC_VERSION_MAJOR 0X15 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_SPEC_ERRATA 0x16 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_UINTN_SIZE 0x17 +#define FU_TPM_EVENTLOG_V2_HDR_IDX_NUMBER_OF_ALGS 0x18 + +#define FU_TPM_EVENTLOG_V2_HDR_SIGNATURE "Spec ID Event03" + +#define FU_TPM_EVENTLOG_V2_IDX_PCR 0x00 +#define FU_TPM_EVENTLOG_V2_IDX_TYPE 0x04 +#define FU_TPM_EVENTLOG_V2_IDX_DIGEST_COUNT 0x08 +#define FU_TPM_EVENTLOG_V2_SIZE 0x0c + +static void +fu_tpm_eventlog_parser_item_free(FuTpmEventlogItem *item) +{ + if (item->blob != NULL) + g_bytes_unref(item->blob); + if (item->checksum_sha1 != NULL) + g_bytes_unref(item->checksum_sha1); + if (item->checksum_sha256 != NULL) + g_bytes_unref(item->checksum_sha256); + g_free(item); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTpmEventlogItem, fu_tpm_eventlog_parser_item_free); + +void +fu_tpm_eventlog_item_to_string(FuTpmEventlogItem *item, guint idt, GString *str) +{ + const gchar *tmp; + g_autofree gchar *pcrstr = + g_strdup_printf("%s (%u)", fu_tpm_eventlog_pcr_to_string(item->pcr), item->pcr); + fu_string_append(str, idt, "PCR", pcrstr); + fu_string_append_kx(str, idt, "Type", item->kind); + tmp = fu_tpm_eventlog_item_kind_to_string(item->kind); + if (tmp != NULL) + fu_string_append(str, idt, "Description", tmp); + if (item->checksum_sha1 != NULL) { + g_autofree gchar *csum = fu_tpm_eventlog_strhex(item->checksum_sha1); + fu_string_append(str, idt, "ChecksumSha1", csum); + } + if (item->checksum_sha256 != NULL) { + g_autofree gchar *csum = fu_tpm_eventlog_strhex(item->checksum_sha256); + fu_string_append(str, idt, "ChecksumSha256", csum); + } + if (item->blob != NULL) { + g_autofree gchar *blobstr = fu_tpm_eventlog_blobstr(item->blob); + if (blobstr != NULL) + fu_string_append(str, idt, "BlobStr", blobstr); + } +} + +static GPtrArray * +fu_tpm_eventlog_parser_parse_blob_v2(const guint8 *buf, + gsize bufsz, + FuTpmEventlogParserFlags flags, + GError **error) +{ + guint32 hdrsz = 0x0; + g_autoptr(GPtrArray) items = NULL; + + /* advance over the header block */ + if (!fu_memread_uint32_safe(buf, + bufsz, + FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE, + &hdrsz, + G_LITTLE_ENDIAN, + error)) + return NULL; + items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_tpm_eventlog_parser_item_free); + for (gsize idx = FU_TPM_EVENTLOG_V1_SIZE + hdrsz; idx < bufsz;) { + guint32 pcr = 0; + guint32 event_type = 0; + guint32 digestcnt = 0; + guint32 datasz = 0; + g_autoptr(GBytes) checksum_sha1 = NULL; + g_autoptr(GBytes) checksum_sha256 = NULL; + + /* read entry */ + if (!fu_memread_uint32_safe(buf, + bufsz, + idx + FU_TPM_EVENTLOG_V2_IDX_PCR, + &pcr, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + idx + FU_TPM_EVENTLOG_V2_IDX_TYPE, + &event_type, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + idx + FU_TPM_EVENTLOG_V2_IDX_DIGEST_COUNT, + &digestcnt, + G_LITTLE_ENDIAN, + error)) + return NULL; + + /* read checksum block */ + idx += FU_TPM_EVENTLOG_V2_SIZE; + for (guint i = 0; i < digestcnt; i++) { + guint16 alg_type = 0; + guint32 alg_size = 0; + g_autofree guint8 *digest = NULL; + + /* get checksum type */ + if (!fu_memread_uint16_safe(buf, + bufsz, + idx, + &alg_type, + G_LITTLE_ENDIAN, + error)) + return NULL; + alg_size = fu_tpm_eventlog_hash_get_size(alg_type); + if (alg_size == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "hash algorithm 0x%x size not known", + alg_type); + return NULL; + } + + /* build checksum */ + idx += sizeof(alg_type); + + /* copy hash */ + digest = g_malloc0(alg_size); + if (!fu_memcpy_safe(digest, + alg_size, + 0x0, /* dst */ + buf, + bufsz, + idx, /* src */ + alg_size, + error)) + return NULL; + + /* save this for analysis */ + if (alg_type == TPM2_ALG_SHA1) + checksum_sha1 = + g_bytes_new_take(g_steal_pointer(&digest), alg_size); + else if (alg_type == TPM2_ALG_SHA256) + checksum_sha256 = + g_bytes_new_take(g_steal_pointer(&digest), alg_size); + + /* next block */ + idx += alg_size; + } + + /* read data block */ + if (!fu_memread_uint32_safe(buf, bufsz, idx, &datasz, G_LITTLE_ENDIAN, error)) + return NULL; + if (datasz > 1024 * 1024) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "event log item too large"); + return NULL; + } + + /* save blob if PCR=0 */ + idx += sizeof(datasz); + if (pcr == ESYS_TR_PCR0 || flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS) { + g_autoptr(FuTpmEventlogItem) item = NULL; + + /* build item */ + item = g_new0(FuTpmEventlogItem, 1); + item->pcr = pcr; + item->kind = event_type; + item->checksum_sha1 = g_steal_pointer(&checksum_sha1); + item->checksum_sha256 = g_steal_pointer(&checksum_sha256); + if (datasz > 0) { + g_autofree guint8 *data = g_malloc0(datasz); + if (!fu_memcpy_safe(data, + datasz, + 0x0, /* dst */ + buf, + bufsz, + idx, + datasz, /* src */ + error)) + return NULL; + item->blob = g_bytes_new_take(g_steal_pointer(&data), datasz); + if (g_getenv("FWUPD_TPM_EVENTLOG_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "TpmEvent", item->blob); + } + g_ptr_array_add(items, g_steal_pointer(&item)); + } + + /* next entry */ + idx += datasz; + } + + /* success */ + return g_steal_pointer(&items); +} + +GPtrArray * +fu_tpm_eventlog_parser_new(const guint8 *buf, + gsize bufsz, + FuTpmEventlogParserFlags flags, + GError **error) +{ + gchar sig[] = FU_TPM_EVENTLOG_V2_HDR_SIGNATURE; + g_autoptr(GPtrArray) items = NULL; + + g_return_val_if_fail(buf != NULL, NULL); + + /* look for TCG v2 signature */ + if (!fu_memcpy_safe((guint8 *)sig, + sizeof(sig), + 0x0, /* dst */ + buf, + bufsz, + FU_TPM_EVENTLOG_V1_SIZE, /* src */ + sizeof(sig), + error)) + return NULL; + if (g_strcmp0(sig, FU_TPM_EVENTLOG_V2_HDR_SIGNATURE) == 0) + return fu_tpm_eventlog_parser_parse_blob_v2(buf, bufsz, flags, error); + + /* assume v1 structure */ + items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_tpm_eventlog_parser_item_free); + for (gsize idx = 0; idx < bufsz; idx += FU_TPM_EVENTLOG_V1_SIZE) { + guint32 datasz = 0; + guint32 pcr = 0; + guint32 event_type = 0; + if (!fu_memread_uint32_safe(buf, + bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_PCR, + &pcr, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_TYPE, + &event_type, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE, + &datasz, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (datasz > 1024 * 1024) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "event log item too large"); + return NULL; + } + if (pcr == ESYS_TR_PCR0 || flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS) { + g_autoptr(FuTpmEventlogItem) item = NULL; + guint8 digest[TPM2_SHA1_DIGEST_SIZE] = {0x0}; + + /* copy hash */ + if (!fu_memcpy_safe(digest, + sizeof(digest), + 0x0, /* dst */ + buf, + bufsz, + idx + FU_TPM_EVENTLOG_V1_IDX_DIGEST, /* src */ + sizeof(digest), + error)) + return NULL; + + /* build item */ + item = g_new0(FuTpmEventlogItem, 1); + item->pcr = pcr; + item->kind = event_type; + item->checksum_sha1 = g_bytes_new(digest, sizeof(digest)); + if (datasz > 0) { + g_autofree guint8 *data = g_malloc0(datasz); + if (!fu_memcpy_safe(data, + datasz, + 0x0, /* dst */ + buf, + bufsz, + idx + FU_TPM_EVENTLOG_V1_SIZE, /* src */ + datasz, + error)) + return NULL; + item->blob = g_bytes_new_take(g_steal_pointer(&data), datasz); + if (g_getenv("FWUPD_TPM_EVENTLOG_VERBOSE") != NULL) + fu_dump_bytes(G_LOG_DOMAIN, "TpmEvent", item->blob); + } + g_ptr_array_add(items, g_steal_pointer(&item)); + } + idx += datasz; + } + return g_steal_pointer(&items); +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-parser.h b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-parser.h new file mode 100644 index 0000000000000000000000000000000000000000..086a1d77c854fb0260049fac7c0dfecb9d9fd7f9 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog-parser.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-tpm-eventlog-common.h" + +typedef enum { + FU_TPM_EVENTLOG_PARSER_FLAG_NONE = 0, + FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS = 1 << 0, + FU_TPM_EVENTLOG_PARSER_FLAG_LAST +} FuTpmEventlogParserFlags; + +GPtrArray * +fu_tpm_eventlog_parser_new(const guint8 *buf, + gsize bufsz, + FuTpmEventlogParserFlags flags, + GError **error); +void +fu_tpm_eventlog_item_to_string(FuTpmEventlogItem *item, guint idt, GString *str); diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog.c new file mode 100644 index 0000000000000000000000000000000000000000..3a638d81fde780f285909287f989116d78f56bd4 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-eventlog.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuTpmEventlog" + +#include "config.h" + +#include + +#include +#include +#include +#include +#include + +#include "fu-tpm-eventlog-parser.h" + +static gint +fu_tmp_eventlog_sort_cb(gconstpointer a, gconstpointer b) +{ + FuTpmEventlogItem *item_a = *((FuTpmEventlogItem **)a); + FuTpmEventlogItem *item_b = *((FuTpmEventlogItem **)b); + if (item_a->pcr > item_b->pcr) + return 1; + if (item_a->pcr < item_b->pcr) + return -1; + return 0; +} + +static gboolean +fu_tmp_eventlog_process(const gchar *fn, gint pcr, GError **error) +{ + gsize bufsz = 0; + g_autofree guint8 *buf = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autoptr(GString) str = g_string_new(NULL); + gint max_pcr = 0; + + /* parse this */ + if (!g_file_get_contents(fn, (gchar **)&buf, &bufsz, error)) + return FALSE; + items = fu_tpm_eventlog_parser_new(buf, bufsz, FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS, error); + if (items == NULL) + return FALSE; + g_ptr_array_sort(items, fu_tmp_eventlog_sort_cb); + + for (guint i = 0; i < items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index(items, i); + if (item->pcr > max_pcr) + max_pcr = item->pcr; + if (pcr >= 0 && item->pcr != pcr) + continue; + fu_tpm_eventlog_item_to_string(item, 0, str); + g_string_append(str, "\n"); + } + if (pcr > max_pcr) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid PCR specified: %d", + pcr); + return FALSE; + } + fu_string_append(str, 0, "Reconstructed PCRs", NULL); + for (guint8 i = 0; i <= max_pcr; i++) { + g_autoptr(GPtrArray) pcrs = fu_tpm_eventlog_calc_checksums(items, i, NULL); + if (pcrs == NULL) + continue; + for (guint j = 0; j < pcrs->len; j++) { + const gchar *csum = g_ptr_array_index(pcrs, j); + g_autofree gchar *title = NULL; + g_autofree gchar *pretty = NULL; + if (pcr >= 0 && i != (guint)pcr) + continue; + title = g_strdup_printf("PCR %x", i); + pretty = fwupd_checksum_format_for_display(csum); + fu_string_append(str, 1, title, pretty); + } + } + + /* success */ + g_print("%s", str->str); + return TRUE; +} + +int +main(int argc, char *argv[]) +{ + const gchar *fn; + gboolean verbose = FALSE; + gboolean interactive = isatty(fileno(stdout)) != 0; + gint pcr = -1; + g_autoptr(GError) error = NULL; + g_autoptr(GOptionContext) context = g_option_context_new(NULL); + const GOptionEntry options[] = {{"verbose", + 'v', + 0, + G_OPTION_ARG_NONE, + &verbose, + /* TRANSLATORS: command line option */ + N_("Show extra debugging information"), + NULL}, + {"pcr", + 'p', + 0, + G_OPTION_ARG_INT, + &pcr, + /* TRANSLATORS: command line option */ + N_("Only show single PCR value"), + NULL}, + {NULL}}; + + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + +#ifdef HAVE_GETUID + /* ensure root user */ + if (argc < 2 && 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")); +#endif + + /* TRANSLATORS: program name */ + g_set_application_name(_("fwupd TPM event log utility")); + g_option_context_add_main_entries(context, options, NULL); + g_option_context_set_description(context, + /* TRANSLATORS: CLI description */ + _("This tool will read and parse the TPM event log " + "from the system firmware.")); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + /* 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) { + (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); + (void)g_setenv("FWUPD_TPM_EVENTLOG_VERBOSE", "1", FALSE); + } + + /* allow user to chose a local file */ + fn = argc <= 1 ? "/sys/kernel/security/tpm0/binary_bios_measurements" : argv[1]; + if (!fu_tmp_eventlog_process(fn, pcr, &error)) { + /* TRANSLATORS: failed to read measurements file */ + g_printerr("%s: %s\n", _("Failed to parse file"), error->message); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-plugin.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..c0be3bc2ca151d9ea9974f11acefc1ba4ee24326 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-plugin.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-tpm-eventlog-parser.h" +#include "fu-tpm-plugin.h" +#include "fu-tpm-v1-device.h" +#include "fu-tpm-v2-device.h" + +struct _FuTpmPlugin { + FuPlugin parent_instance; + FuTpmDevice *tpm_device; + FuDevice *bios_device; + GPtrArray *ev_items; /* of FuTpmEventlogItem */ +}; + +G_DEFINE_TYPE(FuTpmPlugin, fu_tpm_plugin, FU_TYPE_PLUGIN) + +static void +fu_tpm_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + if (self->tpm_device != NULL) + fu_string_append(str, idt, "TpmDevice", fu_device_get_id(self->tpm_device)); + if (self->bios_device != NULL) + fu_string_append(str, idt, "BiosDevice", fu_device_get_id(self->bios_device)); +} + +static void +fu_tpm_plugin_set_bios_pcr0s(FuPlugin *plugin) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + g_autoptr(GPtrArray) pcr0s = NULL; + + if (self->tpm_device == NULL) + return; + if (self->bios_device == NULL) + return; + + /* add all the PCR0s */ + pcr0s = fu_tpm_device_get_checksums(self->tpm_device, 0); + if (pcr0s->len == 0) + return; + for (guint i = 0; i < pcr0s->len; i++) { + const gchar *checksum = g_ptr_array_index(pcr0s, i); + fu_device_add_checksum(self->bios_device, checksum); + } + fu_device_add_flag(self->bios_device, FWUPD_DEVICE_FLAG_CAN_VERIFY); +} + +/* set the PCR0 as the device checksum */ +static void +fu_tpm_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + if (fu_device_has_instance_id(device, "main-system-firmware")) { + g_set_object(&self->bios_device, device); + fu_tpm_plugin_set_bios_pcr0s(plugin); + } +} + +static void +fu_tpm_plugin_device_added(FuPlugin *plugin, FuDevice *dev) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + g_autoptr(GPtrArray) pcr0s = NULL; + + g_set_object(&self->tpm_device, FU_TPM_DEVICE(dev)); + fu_plugin_add_report_metadata(plugin, + "TpmFamily", + fu_tpm_device_get_family(FU_TPM_DEVICE(dev))); + + /* ensure */ + fu_tpm_plugin_set_bios_pcr0s(plugin); + + /* add extra plugin metadata */ + pcr0s = fu_tpm_device_get_checksums(self->tpm_device, 0); + for (guint i = 0; i < pcr0s->len; i++) { + const gchar *csum = g_ptr_array_index(pcr0s, i); + GChecksumType csum_type = fwupd_checksum_guess_kind(csum); + if (csum_type == G_CHECKSUM_SHA1) { + fu_plugin_add_report_metadata(plugin, "Pcr0_SHA1", csum); + continue; + } + if (csum_type == G_CHECKSUM_SHA256) { + fu_plugin_add_report_metadata(plugin, "Pcr0_SHA256", csum); + continue; + } + } +} + +static void +fu_tpm_plugin_add_security_attr_version(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20); + fu_security_attrs_append(attrs, attr); + + /* check exists, and in v2.0 mode */ + if (self->tpm_device == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + if (g_strcmp0(fu_tpm_device_get_family(self->tpm_device), "2.0") != 0) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + return; + } + + /* success */ + fwupd_security_attr_add_guids(attr, fu_device_get_guids(FU_DEVICE(self->tpm_device))); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_FOUND); +} + +static void +fu_tpm_plugin_add_security_attr_eventlog(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + gboolean reconstructed = TRUE; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s_calc = NULL; + g_autoptr(GPtrArray) pcr0s_real = NULL; + + /* no TPM device */ + if (self->tpm_device == NULL) + return; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0); + fwupd_security_attr_add_guids(attr, fu_device_get_guids(self->tpm_device)); + fu_security_attrs_append(attrs, attr); + + /* check reconstructed to PCR0 */ + if (self->ev_items == NULL) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + + /* calculate from the eventlog */ + pcr0s_calc = fu_tpm_eventlog_calc_checksums(self->ev_items, 0, &error); + if (pcr0s_calc == NULL) { + g_warning("failed to get eventlog reconstruction: %s", error->message); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* compare against the real PCR0s */ + pcr0s_real = fu_tpm_device_get_checksums(self->tpm_device, 0); + for (guint i = 0; i < pcr0s_real->len; i++) { + const gchar *checksum = g_ptr_array_index(pcr0s_real, i); + reconstructed = FALSE; + for (guint j = 0; j < pcr0s_calc->len; j++) { + const gchar *checksum_tmp = g_ptr_array_index(pcr0s_calc, j); + /* skip unless same algorithm */ + if (strlen(checksum) != strlen(checksum_tmp)) + continue; + g_debug("comparing TPM %s and EVT %s", checksum, checksum_tmp); + if (g_strcmp0(checksum, checksum_tmp) == 0) { + reconstructed = TRUE; + break; + } + } + /* all algorithms must match */ + if (!reconstructed) + break; + } + if (!reconstructed) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_tpm_plugin_add_security_attr_empty(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* no TPM device */ + if (self->tpm_device == NULL) + return; + + /* add attributes */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR); + fwupd_security_attr_add_guids(attr, fu_device_get_guids(self->tpm_device)); + fu_security_attrs_append(attrs, attr); + + /* check PCRs 0 through 7 for empty checksums */ + for (guint pcr = 0; pcr <= 7; pcr++) { + g_autoptr(GPtrArray) checksums = fu_tpm_device_get_checksums(self->tpm_device, pcr); + for (guint i = 0; i < checksums->len; i++) { + const gchar *checksum = g_ptr_array_index(checksums, i); + gboolean empty = TRUE; + + /* empty checksum is zero, so made entirely of zeroes */ + for (guint j = 0; checksum[j] != '\0'; j++) { + if (checksum[j] != '0') { + empty = FALSE; + break; + } + } + if (empty) { + fwupd_security_attr_set_result( + attr, + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + } + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_tpm_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + return; + fu_tpm_plugin_add_security_attr_version(plugin, attrs); + fu_tpm_plugin_add_security_attr_eventlog(plugin, attrs); + fu_tpm_plugin_add_security_attr_empty(plugin, attrs); +} + +static gchar * +fu_tpm_plugin_eventlog_report_metadata(FuPlugin *plugin) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + GString *str = g_string_new(""); + g_autoptr(GPtrArray) pcrs = NULL; + + for (guint i = 0; i < self->ev_items->len; i++) { + FuTpmEventlogItem *item = g_ptr_array_index(self->ev_items, i); + g_autofree gchar *blobstr = NULL; + g_autofree gchar *checksum = NULL; + + if (item->blob == NULL) + continue; + if (item->checksum_sha1 != NULL) + checksum = fu_tpm_eventlog_strhex(item->checksum_sha1); + else if (item->checksum_sha256 != NULL) + checksum = fu_tpm_eventlog_strhex(item->checksum_sha256); + else + continue; + g_string_append_printf(str, "0x%08x %s", item->kind, checksum); + blobstr = fu_tpm_eventlog_blobstr(item->blob); + if (blobstr != NULL) + g_string_append_printf(str, " [%s]", blobstr); + g_string_append(str, "\n"); + } + pcrs = fu_tpm_eventlog_calc_checksums(self->ev_items, 0, NULL); + if (pcrs != NULL) { + for (guint j = 0; j < pcrs->len; j++) { + const gchar *csum = g_ptr_array_index(pcrs, j); + g_string_append_printf(str, "PCR0: %s\n", csum); + } + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + return g_string_free(str, FALSE); +} + +static gboolean +fu_tpm_plugin_coldplug_eventlog(FuPlugin *plugin, GError **error) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + gsize bufsz = 0; + const gchar *fn = "/sys/kernel/security/tpm0/binary_bios_measurements"; + g_autofree gchar *str = NULL; + g_autofree guint8 *buf = NULL; + + /* do not show a warning if no TPM exists, or the kernel is too old */ + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_debug("no %s, so skipping", fn); + return TRUE; + } + if (!g_file_get_contents(fn, (gchar **)&buf, &bufsz, error)) + return FALSE; + if (bufsz == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to read data from %s", + fn); + return FALSE; + } + self->ev_items = + fu_tpm_eventlog_parser_new(buf, bufsz, FU_TPM_EVENTLOG_PARSER_FLAG_NONE, error); + if (self->ev_items == NULL) + return FALSE; + + /* add optional report metadata */ + str = fu_tpm_plugin_eventlog_report_metadata(plugin); + fu_plugin_add_report_metadata(plugin, "TpmEventLog", str); + return TRUE; +} + +static gboolean +fu_tpm_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* best effort */ + if (!fu_tpm_plugin_coldplug_eventlog(plugin, &error_local)) + g_warning("failed to load eventlog: %s", error_local->message); + + /* success */ + return TRUE; +} + +static gboolean +fu_tpm_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); + g_autofree gchar *sysfstpmdir = NULL; + g_autofree gchar *fn_pcrs = NULL; + + /* look for TPM v1.2 */ + sysfstpmdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_TPM); + fn_pcrs = g_build_filename(sysfstpmdir, "tpm0", "pcrs", NULL); + if (g_file_test(fn_pcrs, G_FILE_TEST_EXISTS) && g_getenv("FWUPD_FORCE_TPM2") == NULL) { + self->tpm_device = fu_tpm_v1_device_new(fu_plugin_get_context(plugin)); + g_object_set(self->tpm_device, "device-file", fn_pcrs, NULL); + fu_device_set_physical_id(FU_DEVICE(self->tpm_device), "tpm"); + if (!fu_device_probe(FU_DEVICE(self->tpm_device), error)) + return FALSE; + fu_plugin_device_add(plugin, FU_DEVICE(self->tpm_device)); + } + + /* success */ + return TRUE; +} + +static void +fu_tpm_plugin_init(FuTpmPlugin *self) +{ +} + +static void +fu_tpm_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + + /* old name */ + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "tpm_eventlog"); + fu_plugin_add_udev_subsystem(plugin, "tpm"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_TPM_V2_DEVICE); +} + +static void +fu_tpm_finalize(GObject *obj) +{ + FuTpmPlugin *self = FU_TPM_PLUGIN(obj); + if (self->tpm_device != NULL) + g_object_unref(self->tpm_device); + if (self->bios_device != NULL) + g_object_unref(self->bios_device); + if (self->ev_items != NULL) + g_ptr_array_unref(self->ev_items); + G_OBJECT_CLASS(fu_tpm_plugin_parent_class)->finalize(obj); +} + +static void +fu_tpm_plugin_class_init(FuTpmPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_tpm_plugin_constructed; + object_class->finalize = fu_tpm_finalize; + plugin_class->to_string = fu_tpm_plugin_to_string; + plugin_class->startup = fu_tpm_plugin_startup; + plugin_class->coldplug = fu_tpm_plugin_coldplug; + plugin_class->device_added = fu_tpm_plugin_device_added; + plugin_class->device_registered = fu_tpm_plugin_device_registered; + plugin_class->add_security_attrs = fu_tpm_plugin_add_security_attrs; +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-plugin.h b/fwupd-1.8.6/plugins/tpm/fu-tpm-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..02e6c26f2a26fc718de590a7b8e8a7158762385f --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuTpmPlugin, fu_tpm_plugin, FU, TPM_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-v1-device.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-v1-device.c new file mode 100644 index 0000000000000000000000000000000000000000..2622504de8848f54c52d3041c4a099d547c8f5bc --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-v1-device.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-tpm-v1-device.h" + +struct _FuTpmV1Device { + FuTpmDevice parent_instance; +}; + +G_DEFINE_TYPE(FuTpmV1Device, fu_tpm_v1_device, FU_TYPE_TPM_DEVICE) + +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_tpm_device_parse_line(const gchar *line, gpointer user_data) +{ + FuTpmDevice *self = FU_TPM_DEVICE(user_data); + guint64 idx; + g_autofree gchar *idxstr = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GString) str = NULL; + g_autoptr(GError) error_local = 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_strstrip(split[0]); + if (!fu_strtoull(idxstr, &idx, 0, 64, &error_local)) { + g_debug("unexpected index %s, skipping: %s", idxstr, error_local->message); + return; + } + + /* parse hash */ + str = g_string_new(split[1]); + fu_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); + fu_tpm_device_add_checksum(self, idx, str->str); +} + +static gboolean +fu_tpm_v1_device_probe(FuDevice *device, GError **error) +{ + FuTpmV1Device *self = FU_TPM_V1_DEVICE(device); + g_auto(GStrv) lines = NULL; + g_autofree gchar *buf_pcrs = NULL; + + /* get entire contents */ + if (!g_file_get_contents(fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)), + &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_tpm_device_parse_line(lines[i] + 4, self); + } + return TRUE; +} + +static void +fu_tpm_v1_device_init(FuTpmV1Device *self) +{ +} + +static void +fu_tpm_v1_device_class_init(FuTpmV1DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_tpm_v1_device_probe; +} + +FuTpmDevice * +fu_tpm_v1_device_new(FuContext *ctx) +{ + return FU_TPM_DEVICE(g_object_new(FU_TYPE_TPM_V1_DEVICE, "context", ctx, NULL)); +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-v1-device.h b/fwupd-1.8.6/plugins/tpm/fu-tpm-v1-device.h new file mode 100644 index 0000000000000000000000000000000000000000..3b264f0b16888e0025c8fb8afa686ebd59c5a4f8 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-v1-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-tpm-device.h" + +#define FU_TYPE_TPM_V1_DEVICE (fu_tpm_v1_device_get_type()) +G_DECLARE_FINAL_TYPE(FuTpmV1Device, fu_tpm_v1_device, FU, TPM_V1_DEVICE, FuTpmDevice) + +FuTpmDevice * +fu_tpm_v1_device_new(FuContext *ctx); diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-v2-device.c b/fwupd-1.8.6/plugins/tpm/fu-tpm-v2-device.c new file mode 100644 index 0000000000000000000000000000000000000000..6c138291f8422a8d7adc9fa3ab8e0498398ad4f2 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-v2-device.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-tpm-v2-device.h" + +struct _FuTpmV2Device { + FuTpmDevice parent_instance; +}; + +G_DEFINE_TYPE(FuTpmV2Device, fu_tpm_v2_device, FU_TYPE_TPM_DEVICE) + +static void +Esys_Finalize_autoptr_cleanup(ESYS_CONTEXT *esys_context) +{ + Esys_Finalize(&esys_context); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup) + +static gboolean +fu_tpm_v2_device_probe(FuDevice *device, GError **error) +{ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "tpm", error); +} + +static gboolean +fu_tpm_v2_device_get_uint32(ESYS_CONTEXT *ctx, guint32 query, guint32 *val, GError **error) +{ + TSS2_RC rc; + g_autofree TPMS_CAPABILITY_DATA *capability = NULL; + + g_return_val_if_fail(val != NULL, FALSE); + + rc = Esys_GetCapability(ctx, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + TPM2_CAP_TPM_PROPERTIES, + query, + 1, + NULL, + &capability); + if (rc != TSS2_RC_SUCCESS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "capability request failed for query %x", + query); + return FALSE; + } + if (capability->data.tpmProperties.count == 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "no properties returned for query %x", + query); + return FALSE; + } + if (capability->data.tpmProperties.tpmProperty[0].property != query) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "wrong query returned (got %x expected %x)", + capability->data.tpmProperties.tpmProperty[0].property, + query); + return FALSE; + } + + *val = capability->data.tpmProperties.tpmProperty[0].value; + return TRUE; +} + +static gchar * +fu_tpm_v2_device_get_string(ESYS_CONTEXT *ctx, guint32 query, GError **error) +{ + guint32 val_be = 0; + guint32 val; + gchar result[5] = {'\0'}; + + /* return four bytes */ + if (!fu_tpm_v2_device_get_uint32(ctx, query, &val_be, error)) + return NULL; + val = GUINT32_FROM_BE(val_be); + memcpy(result, (gchar *)&val, 4); + + /* convert non-ASCII into spaces */ + for (guint i = 0; i < 4; i++) { + if (!g_ascii_isgraph(result[i])) + result[i] = 0x20; + } + + return fu_strstrip(result); +} + +/* taken from TCG-TPM-Vendor-ID-Registry-Version-1.01-Revision-1.00.pdf */ +static const gchar * +fu_tpm_v2_device_convert_manufacturer(const gchar *manufacturer) +{ + if (g_strcmp0(manufacturer, "AMD") == 0) + return "AMD"; + if (g_strcmp0(manufacturer, "ATML") == 0) + return "Atmel"; + if (g_strcmp0(manufacturer, "BRCM") == 0) + return "Broadcom"; + if (g_strcmp0(manufacturer, "HPE") == 0) + return "HPE"; + if (g_strcmp0(manufacturer, "IBM") == 0) + return "IBM"; + if (g_strcmp0(manufacturer, "IFX") == 0) + return "Infineon"; + if (g_strcmp0(manufacturer, "INTC") == 0) + return "Intel"; + if (g_strcmp0(manufacturer, "LEN") == 0) + return "Lenovo"; + if (g_strcmp0(manufacturer, "MSFT") == 0) + return "Microsoft"; + if (g_strcmp0(manufacturer, "NSM") == 0) + return "National Semiconductor"; + if (g_strcmp0(manufacturer, "NTZ") == 0) + return "Nationz"; + if (g_strcmp0(manufacturer, "NTC") == 0) + return "Nuvoton Technology"; + if (g_strcmp0(manufacturer, "QCOM") == 0) + return "Qualcomm"; + if (g_strcmp0(manufacturer, "SMSC") == 0) + return "SMSC"; + if (g_strcmp0(manufacturer, "STM") == 0) + return "ST Microelectronics"; + if (g_strcmp0(manufacturer, "SMSN") == 0) + return "Samsung"; + if (g_strcmp0(manufacturer, "SNS") == 0) + return "Sinosun"; + if (g_strcmp0(manufacturer, "TXN") == 0) + return "Texas Instruments"; + if (g_strcmp0(manufacturer, "WEC") == 0) + return "Winbond"; + if (g_strcmp0(manufacturer, "ROCC") == 0) + return "Fuzhou Rockchip"; + if (g_strcmp0(manufacturer, "GOOG") == 0) + return "Google"; + return NULL; +} + +static gboolean +fu_tpm_v2_device_setup_pcrs(FuTpmV2Device *self, ESYS_CONTEXT *ctx, GError **error) +{ + TSS2_RC rc; + g_autofree TPMS_CAPABILITY_DATA *capability_data = NULL; + TPML_PCR_SELECTION pcr_selection_in = { + 0, + }; + g_autofree TPML_DIGEST *pcr_values = NULL; + + /* get hash algorithms supported by the TPM */ + rc = Esys_GetCapability(ctx, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + TPM2_CAP_PCRS, + 0, + 1, + NULL, + &capability_data); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to get hash algorithms supported by TPM"); + return FALSE; + } + + /* fetch PCR 0 for every supported hash algorithm */ + pcr_selection_in.count = capability_data->data.assignedPCR.count; + for (guint i = 0; i < pcr_selection_in.count; i++) { + pcr_selection_in.pcrSelections[i].hash = + capability_data->data.assignedPCR.pcrSelections[i].hash; + pcr_selection_in.pcrSelections[i].sizeofSelect = + capability_data->data.assignedPCR.pcrSelections[i].sizeofSelect; + pcr_selection_in.pcrSelections[i].pcrSelect[0] = 0b00000001; + } + + rc = Esys_PCR_Read(ctx, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + &pcr_selection_in, + NULL, + NULL, + &pcr_values); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to read PCR values from TPM"); + return FALSE; + } + + for (guint i = 0; i < pcr_values->count; i++) { + g_autoptr(GString) str = NULL; + gboolean valid = FALSE; + + str = g_string_new(NULL); + for (guint j = 0; j < pcr_values->digests[i].size; j++) { + gint64 val = pcr_values->digests[i].buffer[j]; + if (val > 0) + valid = TRUE; + g_string_append_printf(str, "%02x", pcr_values->digests[i].buffer[j]); + } + if (valid) { + /* constant PCR index 0, since we only read this single PCR */ + fu_tpm_device_add_checksum(FU_TPM_DEVICE(self), 0, str->str); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_tpm_v2_device_setup(FuDevice *device, GError **error) +{ + FuTpmV2Device *self = FU_TPM_V2_DEVICE(device); + FwupdVersionFormat verfmt; + TSS2_RC rc; + const gchar *tmp; + guint32 tpm_type = 0; + guint32 version1 = 0; + guint32 version2 = 0; + guint64 version_raw; + g_autofree gchar *manufacturer = NULL; + g_autofree gchar *model1 = NULL; + g_autofree gchar *model2 = NULL; + g_autofree gchar *model3 = NULL; + g_autofree gchar *model4 = NULL; + g_autofree gchar *model = NULL; + g_autofree gchar *vendor_id = NULL; + g_autofree gchar *version = NULL; + g_autofree gchar *family = NULL; + g_autoptr(ESYS_CONTEXT) ctx = NULL; + + /* suppress warning messages about missing TCTI libraries for tpm2-tss <2.3 */ + if (g_getenv("FWUPD_UEFI_VERBOSE") == NULL) + (void)g_setenv("TSS2_LOG", "esys+none,tcti+none", FALSE); + + /* setup TSS */ + rc = Esys_Initialize(&ctx, NULL, NULL); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to initialize TPM library"); + return FALSE; + } + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to initialize TPM"); + return FALSE; + } + + /* lookup guaranteed details from TPM */ + family = fu_tpm_v2_device_get_string(ctx, TPM2_PT_FAMILY_INDICATOR, error); + if (family == NULL) { + g_prefix_error(error, "failed to read TPM family: "); + return FALSE; + } + fu_tpm_device_set_family(FU_TPM_DEVICE(self), family); + manufacturer = fu_tpm_v2_device_get_string(ctx, TPM2_PT_MANUFACTURER, error); + if (manufacturer == NULL) { + g_prefix_error(error, "failed to read TPM manufacturer: "); + return FALSE; + } + model1 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_1, error); + if (model1 == NULL) { + g_prefix_error(error, "failed to read TPM vendor string: "); + return FALSE; + } + if (!fu_tpm_v2_device_get_uint32(ctx, TPM2_PT_VENDOR_TPM_TYPE, &tpm_type, error)) { + g_prefix_error(error, "failed to read TPM type: "); + return FALSE; + } + + /* these are not guaranteed by spec and may be NULL */ + model2 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_2, error); + model3 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_3, error); + model4 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_4, error); + model = g_strjoin("", model1, model2, model3, model4, NULL); + + /* add GUIDs to daemon */ + fu_device_add_instance_str(device, "VEN", manufacturer); + fu_device_add_instance_u16(device, "DEV", tpm_type); + fu_device_add_instance_str(device, "MOD", model); + fu_device_add_instance_str(device, "VER", family); + fu_device_build_instance_id(device, NULL, "TPM", "VEN", "DEV", NULL); + fu_device_build_instance_id(device, NULL, "TPM", "VEN", "MOD", NULL); + fu_device_build_instance_id(device, NULL, "TPM", "VEN", "DEV", "VER", NULL); + fu_device_build_instance_id(device, NULL, "TPM", "VEN", "MOD", "VER", NULL); + + /* enforce vendors can only ship updates for their own hardware */ + vendor_id = g_strdup_printf("TPM:%s", manufacturer); + fu_device_add_vendor_id(device, vendor_id); + tmp = fu_tpm_v2_device_convert_manufacturer(manufacturer); + fu_device_set_vendor(device, tmp != NULL ? tmp : manufacturer); + + /* get version */ + if (!fu_tpm_v2_device_get_uint32(ctx, TPM2_PT_FIRMWARE_VERSION_1, &version1, error)) + return FALSE; + if (!fu_tpm_v2_device_get_uint32(ctx, TPM2_PT_FIRMWARE_VERSION_2, &version2, error)) + return FALSE; + version_raw = ((guint64)version1) << 32 | ((guint64)version2); + fu_device_set_version_raw(device, version_raw); + + /* this has to be done after _add_instance_id() sets the quirks */ + verfmt = fu_device_get_version_format(device); + version = fu_version_from_uint64(version_raw, verfmt); + fu_device_set_version_format(device, verfmt); + fu_device_set_version(device, version); + + /* get PCRs */ + return fu_tpm_v2_device_setup_pcrs(self, ctx, error); +} + +static void +fu_tpm_v2_device_init(FuTpmV2Device *self) +{ +} + +static void +fu_tpm_v2_device_class_init(FuTpmV2DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->setup = fu_tpm_v2_device_setup; + klass_device->probe = fu_tpm_v2_device_probe; +} + +FuTpmDevice * +fu_tpm_v2_device_new(FuContext *ctx) +{ + FuTpmV2Device *self; + self = g_object_new(FU_TYPE_TPM_V2_DEVICE, "context", ctx, NULL); + return FU_TPM_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/tpm/fu-tpm-v2-device.h b/fwupd-1.8.6/plugins/tpm/fu-tpm-v2-device.h new file mode 100644 index 0000000000000000000000000000000000000000..2c3fb865c08b262154bf7fd87f6ee0da55570565 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fu-tpm-v2-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-tpm-device.h" + +#define FU_TYPE_TPM_V2_DEVICE (fu_tpm_v2_device_get_type()) +G_DECLARE_FINAL_TYPE(FuTpmV2Device, fu_tpm_v2_device, FU, TPM_V2_DEVICE, FuTpmDevice) + +FuTpmDevice * +fu_tpm_v2_device_new(FuContext *ctx); diff --git a/fwupd-1.8.6/plugins/tpm/fuzzing/v2.bin b/fwupd-1.8.6/plugins/tpm/fuzzing/v2.bin new file mode 100644 index 0000000000000000000000000000000000000000..9d1e77a85a147127bfaac28aea7741f7c42c90b3 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/fuzzing/v2.bin @@ -0,0 +1 @@ +Spec ID Event03 diff --git a/fwupd-1.8.6/plugins/tpm/meson.build b/fwupd-1.8.6/plugins/tpm/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..bab3f8ca910371c540aab984ec88f58c50571d62 --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/meson.build @@ -0,0 +1,74 @@ +tpm2tss_tpm = dependency('tss2-esys', version: '>= 2.0', required: get_option('plugin_tpm')) + +if hsi and \ + tpm2tss_tpm.found() and \ + get_option('plugin_tpm').require(gudev.found(), + error_message: 'gudev is needed for plugin_tpm').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginTpm"'] + +plugin_quirks += files('tpm.quirk') + +plugin_builtin_tpm = static_library('fu_plugin_tpm', + sources: [ + 'fu-tpm-plugin.c', + 'fu-tpm-device.c', + 'fu-tpm-v1-device.c', + 'fu-tpm-v2-device.c', + 'fu-tpm-eventlog-common.c', + 'fu-tpm-eventlog-parser.c', + ], + include_directories: plugin_incdirs, + link_with: [ + fwupdplugin, + fwupd, + ], + c_args: cargs, + dependencies: [ + plugin_deps, + tpm2tss_tpm, + ], +) +plugin_builtins += plugin_builtin_tpm + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + e = executable( + 'tpm-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + tpm2tss_tpm, + ], + link_with: [ + fwupd, + fwupdplugin, + plugin_builtin_tpm, + ], + c_args: cargs + ) + test('tpm-self-test', e, env: env) +endif + +executable( + 'fwupdtpmevlog', + sources: [ + 'fu-tpm-eventlog.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + tpm2tss_tpm, + ], + link_with: [ + plugin_libs, + plugin_builtin_tpm, + ], +) + +endif diff --git a/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/active b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/active new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/active @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/caps b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/caps new file mode 100644 index 0000000000000000000000000000000000000000..af0d3cb00d1992a9790a61294a358e02254ecd9b --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/caps @@ -0,0 +1,3 @@ +Manufacturer: 0x49465800 +TCG version: 1.2 +Firmware version: 6.40 diff --git a/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/enabled b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/enabled new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/enabled @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/owned b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/owned new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/owned @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/pcrs b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/pcrs new file mode 100644 index 0000000000000000000000000000000000000000..1f8a19c916024dc75af19fff391620ecc03c249c --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/empty_pcr/tpm0/pcrs @@ -0,0 +1,24 @@ +PCR-00: 3C 97 99 20 C9 00 99 60 09 27 D5 DA B3 81 EB 95 1E 7F C8 68 +PCR-01: CE 9F A4 B2 01 09 D8 81 14 EA 1A 6D 13 94 CD 45 5F 52 69 23 +PCR-02: 47 09 7A 9A AD C3 26 A4 93 91 26 63 A1 6F DF 53 D7 88 96 8E +PCR-03: B2 A8 3B 0E BF 2F 83 74 29 9A 5B 2B DF C3 1E A9 55 AD 72 36 +PCR-04: A4 A5 87 4C 59 94 8D 9B 93 66 0A F4 19 D8 6F F8 94 36 20 CC +PCR-05: 00 0B 58 00 89 72 EF 6C 2A AC 79 33 C4 AE 67 6B A6 EF CF 6A +PCR-06: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-07: 0A 2A 68 15 85 0D AC B2 D1 F4 E0 C1 F4 56 D5 E2 81 08 6D EA +PCR-08: DB A7 29 4E 49 BA D7 9E 53 99 0A 6E 3A CB 52 97 B9 08 3A 66 +PCR-09: 19 F9 6F 10 83 F5 5B 50 98 26 C3 14 73 43 35 21 1F E6 39 E9 +PCR-10: 37 3D 89 9E 10 0D DD 2D 21 B5 F4 96 8D 4F DC A7 6D 1A C7 BD +PCR-11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-14: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-17: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-18: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-19: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-20: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-21: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-22: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-23: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 diff --git a/fwupd-1.8.6/plugins/tpm/tests/tpm0/active b/fwupd-1.8.6/plugins/tpm/tests/tpm0/active new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/tpm0/active @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/tpm/tests/tpm0/caps b/fwupd-1.8.6/plugins/tpm/tests/tpm0/caps new file mode 100644 index 0000000000000000000000000000000000000000..af0d3cb00d1992a9790a61294a358e02254ecd9b --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/tpm0/caps @@ -0,0 +1,3 @@ +Manufacturer: 0x49465800 +TCG version: 1.2 +Firmware version: 6.40 diff --git a/fwupd-1.8.6/plugins/tpm/tests/tpm0/enabled b/fwupd-1.8.6/plugins/tpm/tests/tpm0/enabled new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/tpm0/enabled @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/tpm/tests/tpm0/owned b/fwupd-1.8.6/plugins/tpm/tests/tpm0/owned new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/tpm0/owned @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/tpm/tests/tpm0/pcrs b/fwupd-1.8.6/plugins/tpm/tests/tpm0/pcrs new file mode 100644 index 0000000000000000000000000000000000000000..004e69048b3a793a3ca522d9af41d34027a6658b --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tests/tpm0/pcrs @@ -0,0 +1,24 @@ +PCR-00: 3C 97 99 20 C9 00 99 60 09 27 D5 DA B3 81 EB 95 1E 7F C8 68 +PCR-01: CE 9F A4 B2 01 09 D8 81 14 EA 1A 6D 13 94 CD 45 5F 52 69 23 +PCR-02: 47 09 7A 9A AD C3 26 A4 93 91 26 63 A1 6F DF 53 D7 88 96 8E +PCR-03: B2 A8 3B 0E BF 2F 83 74 29 9A 5B 2B DF C3 1E A9 55 AD 72 36 +PCR-04: A4 A5 87 4C 59 94 8D 9B 93 66 0A F4 19 D8 6F F8 94 36 20 CC +PCR-05: 00 0B 58 00 89 72 EF 6C 2A AC 79 33 C4 AE 67 6B A6 EF CF 6A +PCR-06: B2 A8 3B 0E BF 2F 83 74 29 9A 5B 2B DF C3 1E A9 55 AD 72 36 +PCR-07: 0A 2A 68 15 85 0D AC B2 D1 F4 E0 C1 F4 56 D5 E2 81 08 6D EA +PCR-08: DB A7 29 4E 49 BA D7 9E 53 99 0A 6E 3A CB 52 97 B9 08 3A 66 +PCR-09: 19 F9 6F 10 83 F5 5B 50 98 26 C3 14 73 43 35 21 1F E6 39 E9 +PCR-10: 37 3D 89 9E 10 0D DD 2D 21 B5 F4 96 8D 4F DC A7 6D 1A C7 BD +PCR-11: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-13: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-14: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-15: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-16: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +PCR-17: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-18: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-19: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-20: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-21: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-22: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +PCR-23: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 diff --git a/fwupd-1.8.6/plugins/tpm/tpm.quirk b/fwupd-1.8.6/plugins/tpm/tpm.quirk new file mode 100644 index 0000000000000000000000000000000000000000..e1307cae2a11bff1eb2a79a94ef4d7a1a6a6cccb --- /dev/null +++ b/fwupd-1.8.6/plugins/tpm/tpm.quirk @@ -0,0 +1,2 @@ +[TPM] +Plugin = tpm diff --git a/fwupd-1.8.6/plugins/uefi-capsule/README.md b/fwupd-1.8.6/plugins/uefi-capsule/README.md new file mode 100644 index 0000000000000000000000000000000000000000..311650f5ff05ab8244b48480d525f71376bebf41 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/README.md @@ -0,0 +1,120 @@ +# UEFI Capsule + +## Introduction + +The Unified Extensible Firmware Interface (UEFI) is a specification that +defines the software interface between an OS and platform firmware. +With the UpdateCapsule boot service it can be used to update system firmware. + +If you don't want or need this functionality you can use the +`-Dplugin_uefi_capsule=disabled` option. + +When this plugin is enabled, the companion UEFI binary may also be built from the [fwupd-efi](https://github.com/fwupd/fwupd-efi) project if not already present on the filesystem. +This behavior can be overridden using the meson option `-Defi_binary=false`. + +For this companion binary to work with secure boot, it will need to be signed by an authority trusted with shim and/or the host environment. + +## Lenovo Specific Behavior + +On Lenovo hardware only the boot label is set to `Linux-Firmware-Updater` rather +than "Linux Firmware Updater" (with spaces) due to long-fixed EFI boot manager +bugs. Many users will have these old BIOS versions installed and so we use the +`use-legacy-bootmgr-desc` quirk to use the safe name. + +On some Lenovo hardware only one capsule is installable due to possible problems +with the UpdateCapsule coalesce operation. As soon as one UEFI device has been +scheduled for update the other UEFI devices found in the ESRT will be marked +as `updatable-hidden` rather than `updatable`. Rebooting will restore them so +they can be updated on next OS boot. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +EFI capsule file format. + +See the [UEFI specification](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 + +## Update Behavior + +### Capsule update on-disk + +Described in [UEFI specification](https://www.uefi.org/sites/default/files/resources/UEFI%20Spec%202_6.pdf) +§ 8.5.5 - Delivery of Capsules via file on Mass Storage device. + +If the firmware supports this, it will be the preferred method of updating on +aarch64 platforms. You can explicitly disable it by by modifying +*DisableCapsuleUpdateOnDisk* in `/etc/fwupd/uefi_capsule.conf`. + +Several models with Insyde firmware have been released where `OsIndications` +advertises support for CoD, but it simply *does not work*. For this reasons +the CoD support is only available by opt-in for x86_64 devices, and can be +specified using the `uefi-allow-cod` plugin flag for the appropriate HwID. + +The spec expects runtime *SetVariable* to be available in order to enable this +feature, we need to set `EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED` +in *OsIndications* variable to trigger processing of submitted capsule on next +reboot. However some firmware implementations (e.g U-Boot), can't set the +variable at runtime, but ignore the variable in next reboot and apply the +capsule anyway. + +The directory \EFI\UpdateCapsule is checked for capsules only within the EFI +system partition on the device specified in the active boot option determine by +reference to *BootNext* variable or *BootOrder* variable processing. Since +setting *BootNext*, for capsule update on-disk, is not yet implemented, the only +available option is place the \EFI\UpdateCapsule within the ESP partition +indicated by the current *BootOrder*. +Note that this will be always needed if your firmware doesn't support +*SetVariable* at runtime (even if *BootNext* functionality is added). + +### Runtime capsule updates + +The firmware is deployed when the OS is running, but it is only written when the +system has been restarted and the `fwupd*.efi` binary has been run. To achieve +this fwupd sets up the EFI `BootNext` variable, creating the new boot entry if +required. + +## 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)}`. + +## Vendor ID Security + +The vendor ID is set from the BIOS vendor, for example `DMI:LENOVO` for all +devices that are not marked as supporting Firmware Management Protocol. For FMP +device no vendor ID is set. + +## 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 compiling with libsmbios support. + +When fwupd has been compiled with this support you will be able to enable UEFI +support on the device by using the `unlock` command. + +## Custom EFI System Partition (ESP) + +Since version 1.1.0 fwupd will autodetect the ESP if it is mounted on +`/boot/efi`, `/boot`, or `/efi`, and UDisks is available on the system. In +other cases the mount point of the ESP needs to be manually specified using the +option *EspLocation* in `/etc/fwupd/daemon.conf`. + +Setting an invalid directory will disable the fwupd plugin. + +## External Interface Access + +This plugin requires: + +* read/write access to the EFI system partition. +* read access to `/sys/firmware/efi/esrt/` +* read access to `/sys/firmware/efi/fw_platform_size` +* read/write access to `/sys/firmware/efi/efivars` diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-self-test.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..614437d8a273dd77700d0166b078e7a5eeff3f5d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-self-test.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-context-private.h" +#include "fu-uefi-backend.h" +#include "fu-uefi-bgrt.h" +#include "fu-uefi-cod-device.h" +#include "fu-uefi-common.h" + +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 = g_test_build_filename(G_TEST_DIST, "tests", "test.bmp", 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 GByteArray * +fu_uefi_cod_device_build_efi_string(const gchar *text) +{ + GByteArray *array = g_byte_array_new(); + glong items_written = 0; + g_autofree gunichar2 *test_utf16 = NULL; + g_autoptr(GError) error = NULL; + + fu_byte_array_append_uint32(array, 0x0, G_LITTLE_ENDIAN); /* attrs */ + test_utf16 = g_utf8_to_utf16(text, -1, NULL, &items_written, &error); + g_assert_no_error(error); + g_assert_nonnull(test_utf16); + g_byte_array_append(array, (const guint8 *)test_utf16, items_written * 2); + return array; +} + +static GByteArray * +fu_uefi_cod_device_build_efi_result(const gchar *guidstr) +{ + GByteArray *array = g_byte_array_new(); + fwupd_guid_t guid = {0x0}; + gboolean ret; + guint8 timestamp[16] = {0x0}; + g_autoptr(GError) error = NULL; + + fu_byte_array_append_uint32(array, 0x0, G_LITTLE_ENDIAN); /* attrs */ + fu_byte_array_append_uint32(array, 0x3A, G_LITTLE_ENDIAN); /* VariableTotalSize */ + fu_byte_array_append_uint32(array, 0xFF, G_LITTLE_ENDIAN); /* Reserved */ + ret = fwupd_guid_from_string(guidstr, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_byte_array_append(array, guid, sizeof(guid)); /* CapsuleGuid */ + g_byte_array_append(array, timestamp, sizeof(timestamp)); /* CapsuleProcessed */ + fu_byte_array_append_uint32(array, + FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT, + G_LITTLE_ENDIAN); /* Status */ + return array; +} + +static void +fu_uefi_cod_device_write_efi_name(const gchar *name, GByteArray *array) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = g_strdup_printf("%s-%s", name, FU_EFIVAR_GUID_EFI_CAPSULE_REPORT); + g_autofree gchar *path = NULL; + path = g_test_build_filename(G_TEST_DIST, "tests", "efi", "efivars", fn, NULL); + ret = g_file_set_contents(path, (gchar *)array->data, array->len, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_uefi_cod_device_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *str = NULL; + + /* these are checked into git and so are not required */ + if (g_getenv("FWUPD_UEFI_CAPSULE_RECREATE_COD_DATA") != NULL) { + g_autoptr(GByteArray) cap0 = NULL; + g_autoptr(GByteArray) cap1 = NULL; + g_autoptr(GByteArray) last = NULL; + g_autoptr(GByteArray) max = NULL; + + last = fu_uefi_cod_device_build_efi_string("Capsule0001"); + max = fu_uefi_cod_device_build_efi_string("Capsule9999"); + cap0 = fu_uefi_cod_device_build_efi_result("99999999-bf9d-540b-b92b-172ce31013c1"); + cap1 = fu_uefi_cod_device_build_efi_result("cc4cbfa9-bf9d-540b-b92b-172ce31013c1"); + fu_uefi_cod_device_write_efi_name("CapsuleLast", last); + fu_uefi_cod_device_write_efi_name("CapsuleMax", max); + fu_uefi_cod_device_write_efi_name("Capsule0000", cap0); + fu_uefi_cod_device_write_efi_name("Capsule0001", cap1); + } + + /* create device */ + dev = g_object_new(FU_TYPE_UEFI_COD_DEVICE, + "fw-class", + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + NULL); + ret = fu_device_get_results(dev, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* debug */ + str = fu_device_to_string(dev); + g_debug("%s", str); + g_assert_cmpint(fu_device_get_update_state(dev), ==, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + g_assert_cmpstr(fu_device_get_update_error(dev), + ==, + "failed to update to 0: battery level is too low"); + g_assert_cmpint(fu_uefi_device_get_status(FU_UEFI_DEVICE(dev)), + ==, + FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT); +} + +static void +fu_uefi_device_func(void) +{ + /* 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_plugin_func(void) +{ + FuUefiDevice *dev; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuBackend) backend = fu_uefi_backend_new(ctx); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + +#ifndef __linux__ + g_test_skip("ESRT data is mocked only on Linux"); + return; +#endif + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add each device */ + ret = fu_backend_coldplug(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + devices = fu_backend_get_devices(backend); + g_assert_cmpint(devices->len, ==, 3); + + /* system firmware */ + dev = g_ptr_array_index(devices, 0); + ret = fu_device_probe(FU_DEVICE(dev), &error); + g_assert_no_error(error); + g_assert_true(ret); + 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); + + /* system firmware */ + dev = g_ptr_array_index(devices, 1); + ret = fu_device_probe(FU_DEVICE(dev), &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_uefi_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); + + /* invalid */ + dev = g_ptr_array_index(devices, 2); + ret = fu_device_probe(FU_DEVICE(dev), &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); +} + +static void +fu_uefi_update_info_func(void) +{ + FuUefiDevice *dev; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuBackend) backend = fu_uefi_backend_new(ctx); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuUefiUpdateInfo) info = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + +#ifndef __linux__ + g_test_skip("ESRT data is mocked only on Linux"); + return; +#endif + + /* add each device */ + ret = fu_backend_coldplug(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + devices = fu_backend_get_devices(backend); + g_assert_cmpint(devices->len, ==, 3); + 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"); + 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_autofree gchar *testdatadir = NULL; + + g_test_init(&argc, &argv, NULL); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSDRIVERDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_UEFI_TEST", "1", TRUE); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/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/cod-device", fu_uefi_cod_device_func); + g_test_add_func("/uefi/update-info", fu_uefi_update_info_func); + g_test_add_func("/uefi/plugin", fu_uefi_plugin_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-freebsd.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-freebsd.c new file mode 100644 index 0000000000000000000000000000000000000000..62032cc7d7f1e0e102f2b2c00e6ac621d3985d7e --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-freebsd.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 3mdeb Embedded Systems Consulting + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#ifdef HAVE_FREEBSD_ESRT +#include +#include +#endif + +#include + +#include "fu-uefi-backend-freebsd.h" +#include "fu-uefi-common.h" +#include "fu-uefi-device.h" + +struct _FuUefiBackendFreebsd { + FuUefiBackend parent_instance; +}; + +G_DEFINE_TYPE(FuUefiBackendFreebsd, fu_uefi_backend_freebsd, FU_TYPE_UEFI_BACKEND) + +#ifdef HAVE_FREEBSD_ESRT + +static FuUefiDevice * +fu_uefi_backend_device_new(FuUefiBackend *self, + struct efi_esrt_entry_v1 *entry, + guint64 idx, + GError **error) +{ + g_autoptr(FuUefiDevice) dev = NULL; + g_autofree gchar *fw_class = NULL; + g_autofree gchar *phys_id = NULL; + uint32_t status; + + uuid_to_string(&entry->fw_class, &fw_class, &status); + if (status != uuid_s_ok) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "uuid_to_string error"); + return NULL; + } + + /* create object */ + dev = g_object_new(fu_uefi_backend_get_device_gtype(self), + "fw-class", + fw_class, + "capsule-flags", + entry->capsule_flags, + "kind", + entry->fw_type, + "fw-version", + entry->fw_version, + "last-attempt-status", + entry->last_attempt_status, + "last-attempt-version", + entry->last_attempt_version, + "fw-version-lowest", + entry->lowest_supported_fw_version, + "fmp-hardware-instance", + (guint64)0x0, + "version-format", + FWUPD_VERSION_FORMAT_NUMBER, + NULL); + + /* set ID */ + phys_id = g_strdup_printf("ESRT/%u", (guint)idx); + fu_device_set_physical_id(FU_DEVICE(dev), phys_id); + return g_steal_pointer(&dev); +} + +#endif + +static gboolean +fu_uefi_backend_freebsd_setup(FuBackend *backend, GError **error) +{ + g_autofree gchar *efi_ver = fu_kenv_get_string("efi-version", error); + if (efi_ver == NULL) { + g_prefix_error(error, "System does not support UEFI mode, no efi-version kenv: "); + return FALSE; + } + if (fu_version_compare(efi_ver, "2.0.0.0", FWUPD_VERSION_FORMAT_QUAD) < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "System does not support UEFI mode, got efi-version of %s", + efi_ver); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_backend_freebsd_coldplug(FuBackend *backend, GError **error) +{ +#ifdef HAVE_FREEBSD_ESRT + FuUefiBackend *self = FU_UEFI_BACKEND(backend); + struct efi_get_table_ioc table = {.uuid = EFI_TABLE_ESRT}; + gint efi_fd; + struct efi_esrt_entry_v1 *entries; + g_autofree struct efi_esrt_table *esrt = NULL; + + efi_fd = g_open("/dev/efi", O_RDONLY, 0); + if (efi_fd < 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Cannot open /dev/efi"); + return FALSE; + } + + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + g_close(efi_fd, NULL); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Cannot determine size of ESRT table"); + return FALSE; + } + + esrt = g_malloc(table.table_len); + if (esrt == NULL) { + g_close(efi_fd, NULL); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Cannot allocate memory for ESRT table"); + return FALSE; + } + + table.buf = esrt; + table.buf_len = table.table_len; + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + g_close(efi_fd, NULL); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Cannot fill ESRT table"); + return FALSE; + } + + entries = (struct efi_esrt_entry_v1 *)esrt->entries; + for (guint i = 0; i < esrt->fw_resource_count; i++) { + g_autoptr(FuUefiDevice) dev = NULL; + dev = fu_uefi_backend_device_new(self, &entries[i], i, error); + if (dev == NULL) + return FALSE; + + fu_backend_device_added(backend, FU_DEVICE(dev)); + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ESRT access API is missing from the kernel"); + return FALSE; +#endif +} + +void +fu_uefi_backend_freebsd_set_device_gtype(FuBackend *backend, GType device_gtype) +{ +} + +static void +fu_uefi_backend_freebsd_init(FuUefiBackendFreebsd *self) +{ +} + +static void +fu_uefi_backend_freebsd_class_init(FuUefiBackendFreebsdClass *klass) +{ + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + klass_backend->setup = fu_uefi_backend_freebsd_setup; + klass_backend->coldplug = fu_uefi_backend_freebsd_coldplug; +} + +FuBackend * +fu_uefi_backend_new(FuContext *ctx) +{ + return g_object_new(FU_TYPE_UEFI_BACKEND_FREEBSD, "name", "uefi", "context", ctx, NULL); +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-freebsd.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-freebsd.h new file mode 100644 index 0000000000000000000000000000000000000000..a70a20a6a88447f3a2c8e0944298f30eb3ddf461 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-freebsd.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-backend.h" + +#define FU_TYPE_UEFI_BACKEND_FREEBSD (fu_uefi_backend_freebsd_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiBackendFreebsd, + fu_uefi_backend_freebsd, + FU, + UEFI_BACKEND_FREEBSD, + FuUefiBackend) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-linux.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-linux.c new file mode 100644 index 0000000000000000000000000000000000000000..b64864d911e63541ef2ef31d584a261eaeefd00e --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-linux.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-uefi-backend-linux.h" +#include "fu-uefi-cod-device.h" +#include "fu-uefi-common.h" +#include "fu-uefi-nvram-device.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 _FuUefiBackendLinux { + FuUefiBackend parent_instance; + gboolean use_rt_set_variable; +}; + +G_DEFINE_TYPE(FuUefiBackendLinux, fu_uefi_backend_linux, FU_TYPE_UEFI_BACKEND) + +/* yes, unsized uint_t */ +static guint +fu_uefi_backend_linux_read(const gchar *path, const gchar *filename) +{ + return fu_uefi_read_file_as_uint64(path, filename); +} + +static FuUefiDevice * +fu_uefi_backend_linux_device_new(FuUefiBackendLinux *self, const gchar *path) +{ + g_autoptr(FuUefiDevice) dev = NULL; + g_autofree gchar *fw_class = NULL; + g_autofree gchar *fw_class_fn = NULL; + + g_return_val_if_fail(path != NULL, NULL); + + /* read values from sysfs */ + fw_class_fn = g_build_filename(path, "fw_class", NULL); + if (g_file_get_contents(fw_class_fn, &fw_class, NULL, NULL)) + g_strdelimit(fw_class, "\n", '\0'); + + /* Create object, assuming a verfmt of NUMBER unless told otherwise by + * a quirk entry or metadata. + * + * 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... */ + dev = g_object_new(fu_uefi_backend_get_device_gtype(FU_UEFI_BACKEND(self)), + "fw-class", + fw_class, + "capsule-flags", + fu_uefi_backend_linux_read(path, "capsule_flags"), + "kind", + fu_uefi_backend_linux_read(path, "fw_type"), + "fw-version", + fu_uefi_backend_linux_read(path, "fw_version"), + "last-attempt-status", + fu_uefi_backend_linux_read(path, "last_attempt_status"), + "last-attempt-version", + fu_uefi_backend_linux_read(path, "last_attempt_version"), + "fw-version-lowest", + fu_uefi_backend_linux_read(path, "lowest_supported_fw_version"), + "fmp-hardware-instance", + (guint64)0x0, + "version-format", + FWUPD_VERSION_FORMAT_NUMBER, + NULL); + + /* u-boot for instance */ + if (!self->use_rt_set_variable) + fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE); + + /* set ID */ + fu_device_set_physical_id(FU_DEVICE(dev), path); + return g_steal_pointer(&dev); +} + +static gboolean +fu_uefi_backend_linux_check_efivarfs(FuUefiBackendLinux *self, GError **error) +{ + g_autofree gchar *sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + g_autofree gchar *sysfsefivardir = g_build_filename(sysfsfwdir, "efi", "efivars", NULL); + g_autoptr(GUnixMountEntry) mount = g_unix_mount_at(sysfsefivardir, NULL); + + /* in the self tests */ + if (g_getenv("FWUPD_UEFI_TEST") != NULL) + return TRUE; + + 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)) { + GType gtype = fu_uefi_backend_get_device_gtype(FU_UEFI_BACKEND(self)); + if (gtype != FU_TYPE_UEFI_COD_DEVICE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "%s is read only and no CoD", + sysfsefivardir); + return FALSE; + } + + /* this is fine! just do not use SetVariable... */ + self->use_rt_set_variable = FALSE; + } + + return TRUE; +} + +static gboolean +fu_uefi_backend_linux_coldplug(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuUefiBackendLinux *self = FU_UEFI_BACKEND_LINUX(backend); + const gchar *fn; + g_autofree gchar *esrt_entries = NULL; + g_autofree gchar *esrt_path = NULL; + g_autofree gchar *sysfsfwdir = NULL; + g_autoptr(GDir) dir = NULL; + + /* make sure that efivarfs is suitable */ + if (!fu_uefi_backend_linux_check_efivarfs(self, error)) + return FALSE; + + /* get the directory of ESRT entries */ + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + esrt_path = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + esrt_entries = g_build_filename(esrt_path, "entries", NULL); + dir = g_dir_open(esrt_entries, 0, error); + if (dir == NULL) + return FALSE; + + /* add each device */ + while ((fn = g_dir_read_name(dir)) != NULL) { + g_autofree gchar *path = g_build_filename(esrt_entries, fn, NULL); + g_autoptr(FuUefiDevice) dev = fu_uefi_backend_linux_device_new(self, path); + fu_backend_device_added(backend, FU_DEVICE(dev)); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uefi_backend_linux_check_smbios_enabled(FuContext *ctx, GError **error) +{ + const guint8 *data; + gsize sz; + g_autoptr(GBytes) bios_information = fu_context_get_smbios_data(ctx, 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; + } + data = g_bytes_get_data(bios_information, &sz); + if (sz < 0x14) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "offset bigger than size %" G_GSIZE_FORMAT, + sz); + return FALSE; + } + if (data[1] < 0x14) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SMBIOS 2.3 not supported"); + return FALSE; + } + if (!(data[0x13] & (1 << 3))) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "System does not support UEFI mode"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_backend_linux_setup(FuBackend *backend, FuProgress *progress, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* using a pre-cooked SMBIOS */ + if (g_getenv("FWUPD_SYSFSFWDIR") != NULL) + return TRUE; + + /* check SMBIOS for 'UEFI Specification is supported' */ + if (!fu_uefi_backend_linux_check_smbios_enabled(fu_backend_get_context(backend), + &error_local)) { + g_autofree gchar *fw = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + g_autofree gchar *fn = g_build_filename(fw, "efi", NULL); + if (g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_warning("SMBIOS BIOS Characteristics Extension Byte 2 is invalid -- " + "UEFI Specification is unsupported, but %s exists: %s", + fn, + error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + return TRUE; +} + +static void +fu_uefi_backend_linux_init(FuUefiBackendLinux *self) +{ + self->use_rt_set_variable = TRUE; +} + +static void +fu_uefi_backend_linux_class_init(FuUefiBackendLinuxClass *klass) +{ + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + klass_backend->coldplug = fu_uefi_backend_linux_coldplug; + klass_backend->setup = fu_uefi_backend_linux_setup; +} + +FuBackend * +fu_uefi_backend_new(FuContext *ctx) +{ + return g_object_new(FU_TYPE_UEFI_BACKEND_LINUX, "name", "uefi", "context", ctx, NULL); +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-linux.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-linux.h new file mode 100644 index 0000000000000000000000000000000000000000..72524ab0a911cb2dec5f9c2359c511a5e54acab4 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend-linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-backend.h" + +#define FU_TYPE_UEFI_BACKEND_LINUX (fu_uefi_backend_linux_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiBackendLinux, + fu_uefi_backend_linux, + FU, + UEFI_BACKEND_LINUX, + FuUefiBackend) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend.c new file mode 100644 index 0000000000000000000000000000000000000000..cbae9c975c1aa1f4782492e48472d745db1df059 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-uefi-backend.h" +#include "fu-uefi-nvram-device.h" + +typedef struct { + GType device_gtype; +} FuUefiBackendPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND) + +#define GET_PRIVATE(o) (fu_uefi_backend_get_instance_private(o)) + +void +fu_uefi_backend_set_device_gtype(FuUefiBackend *self, GType device_gtype) +{ + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + priv->device_gtype = device_gtype; +} + +GType +fu_uefi_backend_get_device_gtype(FuUefiBackend *self) +{ + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + return priv->device_gtype; +} + +static void +fu_uefi_backend_to_string(FuBackend *backend, guint idt, GString *str) +{ + FuUefiBackend *self = FU_UEFI_BACKEND(backend); + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + fu_string_append(str, idt, "DeviceGType", g_type_name(priv->device_gtype)); +} + +/* create virtual object not backed by an ESRT entry */ +FuUefiDevice * +fu_uefi_backend_device_new_from_dev(FuUefiBackend *self, FuDevice *dev) +{ + FuUefiDevice *device; + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + g_return_val_if_fail(fu_device_get_guid_default(dev) != NULL, NULL); + + tmp = fu_device_get_metadata(dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND); + device = + g_object_new(priv->device_gtype, + "fw-class", + fu_device_get_guid_default(dev), + "kind", + fu_uefi_device_kind_from_string(tmp), + "capsule-flags", + fu_device_get_metadata_integer(dev, FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS), + "fw-version", + fu_device_get_metadata_integer(dev, FU_DEVICE_METADATA_UEFI_FW_VERSION), + NULL); + fu_device_incorporate(FU_DEVICE(device), dev); + return device; +} + +FuUefiDevice * +fu_uefi_backend_device_new_from_guid(FuUefiBackend *self, const gchar *guid) +{ + FuUefiDevice *device; + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(guid != NULL, NULL); + + device = g_object_new(priv->device_gtype, "fw-class", guid, NULL); + fu_device_set_version_format(FU_DEVICE(device), FWUPD_VERSION_FORMAT_NUMBER); + return device; +} + +static void +fu_uefi_backend_init(FuUefiBackend *self) +{ + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + priv->device_gtype = FU_TYPE_UEFI_NVRAM_DEVICE; +} + +static void +fu_uefi_backend_class_init(FuUefiBackendClass *klass) +{ + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + klass_backend->to_string = fu_uefi_backend_to_string; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend.h new file mode 100644 index 0000000000000000000000000000000000000000..806fc06a663f720f53561626c390de81adda383b --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-backend.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" + +#define FU_TYPE_UEFI_BACKEND (fu_uefi_backend_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuUefiBackend, fu_uefi_backend, FU, UEFI_BACKEND, FuBackend) + +struct _FuUefiBackendClass { + FuBackendClass parent_class; +}; + +FuBackend * +fu_uefi_backend_new(FuContext *ctx); +void +fu_uefi_backend_set_device_gtype(FuUefiBackend *self, GType device_gtype); +GType +fu_uefi_backend_get_device_gtype(FuUefiBackend *self); + +FuUefiDevice * +fu_uefi_backend_device_new_from_guid(FuUefiBackend *self, const gchar *guid); +FuUefiDevice * +fu_uefi_backend_device_new_from_dev(FuUefiBackend *self, FuDevice *dev); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bgrt.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bgrt.c new file mode 100644 index 0000000000000000000000000000000000000000..c75c3451398564100647804714c68f7c332ce5d3 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bgrt.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#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_path_from_kind(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 --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bgrt.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bgrt.h new file mode 100644 index 0000000000000000000000000000000000000000..14c454512cda6d12d23cc7a864c3bd35dcef4e99 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bgrt.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bootmgr.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bootmgr.c new file mode 100644 index 0000000000000000000000000000000000000000..1fd6f456dc823e7bf3b88b0be769d957d503eab9 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bootmgr.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-uefi-bootmgr.h" +#include "fu-uefi-common.h" +#include "fu-uefi-device.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; + guint i = 0; + guint32 attr = 0; + g_autofree guint16 *boot_order = NULL; + g_autofree guint16 *new_boot_order = NULL; + + /* get the current boot order */ + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "BootOrder", + (guint8 **)&boot_order, + &boot_order_size, + &attr, + error)) + 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); + + attr |= FU_EFIVAR_ATTR_NON_VOLATILE | FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS; + + i = boot_order_size / sizeof(guint16); + new_boot_order[i] = boot_entry; + boot_order_size += sizeof(guint16); + return fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "BootOrder", + (guint8 *)new_boot_order, + boot_order_size, + attr, + error); +} + +static guint16 +fu_uefi_bootmgr_parse_name(const gchar *name) +{ + gint rc; + gint scanned = 0; + guint16 entry = 0; + + /* BootXXXX */ + rc = sscanf(name, "Boot%hX%n", &entry, &scanned); + if (rc != 1 || scanned != 8) + return G_MAXUINT16; + return entry; +} + +gboolean +fu_uefi_bootmgr_verify_fwupd(GError **error) +{ + g_autoptr(GPtrArray) names = NULL; + + names = fu_efivar_get_names(FU_EFIVAR_GUID_EFI_GLOBAL, error); + if (names == NULL) + return FALSE; + for (guint i = 0; i < names->len; i++) { + const gchar *desc; + const gchar *name = g_ptr_array_index(names, i); + efi_load_option *loadopt; + gsize var_data_size = 0; + guint16 entry; + g_autofree guint8 *var_data_tmp = NULL; + g_autoptr(GError) error_local = NULL; + + /* not BootXXXX */ + entry = fu_uefi_bootmgr_parse_name(name); + if (entry == G_MAXUINT16) + continue; + + /* parse key */ + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + name, + &var_data_tmp, + &var_data_size, + NULL, + &error_local)) { + g_debug("failed to get data for name %s: %s", name, error_local->message); + continue; + } + loadopt = (efi_load_option *)var_data_tmp; + if (!efi_loadopt_is_valid(loadopt, var_data_size)) { + g_debug("%s -> load option was invalid", name); + 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("found %s at Boot%04X", desc, entry); + return TRUE; + } + } + + /* did not find */ + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "no 'Linux Firmware Updater' entry found"); + return FALSE; +} + +static gboolean +fu_uefi_setup_bootnext_with_dp(const guint8 *dp_buf, guint8 *opt, gssize opt_size, GError **error) +{ + const gchar *desc = NULL; + const gchar *name = NULL; + efi_load_option *loadopt = NULL; + gsize var_data_size = 0; + guint32 attr; + guint16 boot_next = G_MAXUINT16; + g_autofree guint8 *var_data = NULL; + g_autofree guint8 *set_entries = g_malloc0(G_MAXUINT16); + g_autoptr(GPtrArray) names = NULL; + + names = fu_efivar_get_names(FU_EFIVAR_GUID_EFI_GLOBAL, error); + if (names == NULL) + return FALSE; + for (guint i = 0; i < names->len; i++) { + guint16 entry = 0; + g_autofree guint8 *var_data_tmp = NULL; + g_autoptr(GError) error_local = NULL; + + /* not BootXXXX */ + name = g_ptr_array_index(names, i); + entry = fu_uefi_bootmgr_parse_name(name); + if (entry == G_MAXUINT16) + continue; + + /* mark this as used */ + set_entries[entry] = 1; + + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + name, + &var_data_tmp, + &var_data_size, + &attr, + &error_local)) { + g_debug("failed to get data for name %s: %s", name, error_local->message); + continue; + } + + loadopt = (efi_load_option *)var_data_tmp; + if (!efi_loadopt_is_valid(loadopt, var_data_size)) { + g_debug("%s -> load option was invalid", name); + 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("%s -> '%s' : does not match", name, desc); + continue; + } + + var_data = g_steal_pointer(&var_data_tmp); + boot_next = entry; + break; + } + + /* already exists */ + if (var_data != NULL) { + /* is different than before */ + if (var_data_size != (gsize)opt_size || memcmp(var_data, opt, opt_size) != 0) { + g_debug("%s -> '%s' : updating existing boot entry", name, desc); + efi_loadopt_attr_set(loadopt, LOAD_OPTION_ACTIVE); + if (!fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + name, + opt, + opt_size, + attr, + error)) { + g_prefix_error(error, "could not set boot variable active: "); + return FALSE; + } + } else { + g_debug("%s -> %s : re-using existing boot entry", name, desc); + } + /* create a new one */ + } else { + g_autofree gchar *boot_next_name = NULL; + for (guint16 value = 0; value < G_MAXUINT16; value++) { + if (set_entries[value]) + continue; + boot_next = value; + break; + } + if (boot_next == G_MAXUINT16) { + 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); + g_debug("%s -> creating new entry", boot_next_name); + if (!fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + boot_next_name, + opt, + opt_size, + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error)) { + g_prefix_error(error, "could not set boot variable %s: ", boot_next_name); + 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 */ + if (!fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "BootNext", + (guint8 *)&boot_next, + 2, + FU_EFIVAR_ATTR_NON_VOLATILE | FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error)) { + g_prefix_error(error, "could not set BootNext(%" G_GUINT16_FORMAT "): ", boot_next); + return FALSE; + } + return TRUE; +} + +gboolean +fu_uefi_bootmgr_bootnext(FuDevice *device, + const gchar *esp_path, + const gchar *description, + FuUefiBootmgrFlags flags, + GError **error) +{ + const gchar *filepath; + gboolean use_fwup_path = TRUE; + gboolean secure_boot = 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_TEST") != 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 if we should use shim */ + secure_boot = fu_efivar_secure_boot_enabled(NULL); + if (secure_boot) { + /* test to make sure shim is there if we need it */ + shim_app = fu_uefi_get_esp_app_path(device, esp_path, "shim", error); + if (shim_app == NULL) + return FALSE; + + /* try to fallback to use UEFI removable path if the shim path doesn't exist */ + if (!g_file_test(shim_app, G_FILE_TEST_EXISTS)) { + if (fu_device_has_private_flag( + device, + FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH)) { + g_free(shim_app); + shim_app = + fu_uefi_get_fallback_app_path(device, esp_path, "boot", 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(device, 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; + } + use_fwup_path = FALSE; + } else if ((flags & FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB) > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BROKEN_SYSTEM, + "Secure boot is enabled, but shim isn't installed to " + "%s", + shim_app); + return FALSE; + } + } + + /* test if correct asset in place */ + target_app = fu_uefi_get_esp_app_path(device, 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) { + glong items_written = 0; + 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 = g_utf8_to_utf16(fwup_esp_path, -1, NULL, &items_written, error); + if (loader_str == NULL) + return FALSE; + if (items_written > 0) + loader_sz += (items_written + 1) * 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; + } + return fu_uefi_setup_bootnext_with_dp(dp_buf, opt, opt_size, error); +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bootmgr.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bootmgr.h new file mode 100644 index 0000000000000000000000000000000000000000..0fdd8e221b5bb3527f0f39f8ea4fb6c578d4f2cc --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-bootmgr.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +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_verify_fwupd(GError **error); +gboolean +fu_uefi_bootmgr_bootnext(FuDevice *device, + const gchar *esp_path, + const gchar *description, + FuUefiBootmgrFlags flags, + GError **error); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-capsule-plugin.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-capsule-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..177392f9669f5ceec53e78a8aefef058eea1b065 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-capsule-plugin.c @@ -0,0 +1,1056 @@ +/* + * Copyright (C) 2016 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-uefi-backend.h" +#include "fu-uefi-bgrt.h" +#include "fu-uefi-bootmgr.h" +#include "fu-uefi-capsule-plugin.h" +#include "fu-uefi-cod-device.h" +#include "fu-uefi-common.h" +#include "fu-uefi-grub-device.h" + +struct _FuUefiCapsulePlugin { + FuPlugin parent_instance; + FuUefiBgrt *bgrt; + FuVolume *esp; + FuBackend *backend; + GFile *fwupd_efi_file; + GFileMonitor *fwupd_efi_monitor; +}; + +G_DEFINE_TYPE(FuUefiCapsulePlugin, fu_uefi_capsule_plugin, FU_TYPE_PLUGIN) + +static void +fu_uefi_capsule_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); + fu_backend_add_string(self->backend, idt, str); + if (self->bgrt != NULL) { + fu_string_append_kb(str, + idt, + "BgrtSupported", + fu_uefi_bgrt_get_supported(self->bgrt)); + } +} + +static gboolean +fu_uefi_capsule_plugin_fwupd_efi_parse(FuUefiCapsulePlugin *self, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(FU_PLUGIN(self)); + const guint8 needle[] = "f\0w\0u\0p\0d\0-\0e\0f\0i\0 \0v\0e\0r\0s\0i\0o\0n\0 "; + gsize bufsz = 0; + gsize offset = 0; + guint16 version_tmp[16] = {0x0}; + g_autofree gchar *buf = NULL; + g_autofree gchar *version = NULL; + + /* find the UTF-16 version string */ + if (!g_file_load_contents(self->fwupd_efi_file, NULL, &buf, &bufsz, NULL, error)) + return FALSE; + if (!fu_memmem_safe((const guint8 *)buf, bufsz, needle, sizeof(needle), &offset, error)) { + g_autofree gchar *fn = g_file_get_path(self->fwupd_efi_file); + g_prefix_error(error, "searching %s: ", fn); + return FALSE; + } + + /* align */ + if (!fu_memcpy_safe((guint8 *)version_tmp, + sizeof(version_tmp), + 0x0, /* dst */ + (const guint8 *)buf, + bufsz, + offset + sizeof(needle), + sizeof(version_tmp) - sizeof(guint16), + error)) + return FALSE; + + /* convert to UTF-8 */ + version = g_utf16_to_utf8(version_tmp, -1, NULL, NULL, error); + if (version == NULL) { + g_autofree gchar *fn = g_file_get_path(self->fwupd_efi_file); + g_prefix_error(error, "converting %s: ", fn); + return FALSE; + } + + /* success */ + fu_context_add_runtime_version(ctx, "org.freedesktop.fwupd-efi", version); + return TRUE; +} + +static void +fu_uefi_capsule_plugin_fwupd_efi_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(user_data); + FuContext *ctx = fu_plugin_get_context(FU_PLUGIN(self)); + g_autoptr(GError) error_local = NULL; + + if (!fu_uefi_capsule_plugin_fwupd_efi_parse(self, &error_local)) { + fu_context_add_runtime_version(ctx, "org.freedesktop.fwupd-efi", "1.0"); + g_warning("failed to get new fwupd efi runtime version: %s", error_local->message); + return; + } +} + +static gboolean +fu_uefi_capsule_plugin_fwupd_efi_probe(FuUefiCapsulePlugin *self, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(FU_PLUGIN(self)); + g_autofree gchar *fn = NULL; + + /* find the app binary */ + fn = fu_uefi_get_built_app_path(error); + if (fn == NULL) + return FALSE; + self->fwupd_efi_file = g_file_new_for_path(fn); + self->fwupd_efi_monitor = + g_file_monitor_file(self->fwupd_efi_file, G_FILE_MONITOR_NONE, NULL, error); + if (self->fwupd_efi_monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(self->fwupd_efi_monitor), + "changed", + G_CALLBACK(fu_uefi_capsule_plugin_fwupd_efi_changed_cb), + self); + if (!fu_uefi_capsule_plugin_fwupd_efi_parse(self, error)) { + fu_context_add_runtime_version(ctx, "org.freedesktop.fwupd-efi", "1.0"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_uefi_capsule_plugin_clear_results(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuUefiDevice *device_uefi = FU_UEFI_DEVICE(device); + return fu_uefi_device_clear_status(device_uefi, error); +} + +static void +fu_uefi_capsule_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(GError) error = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT); + fu_security_attrs_append(attrs, attr); + + /* SB not available or disabled */ + if (!fu_efivar_secure_boot_enabled(&error)) { + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); + return; + } + fu_security_attr_add_bios_target_value(attr, "SecureBoot", "enable"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); +} + +static GBytes * +fu_uefi_capsule_plugin_get_splash_data(guint width, guint height, GError **error) +{ + const gchar *const *langs = g_get_language_names(); + g_autofree gchar *datadir_pkg = NULL; + g_autofree gchar *filename_archive = NULL; + g_autofree gchar *langs_str = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GBytes) blob_archive = NULL; + + /* load archive */ + datadir_pkg = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); + filename_archive = g_build_filename(datadir_pkg, "uefi-capsule-ux.tar.xz", NULL); + blob_archive = fu_bytes_get_contents(filename_archive, error); + if (blob_archive == NULL) + return NULL; + archive = fu_archive_new(blob_archive, FU_ARCHIVE_FLAG_NONE, error); + if (archive == NULL) + return NULL; + + /* find the closest locale match, falling back to `en` and `C` */ + for (guint i = 0; langs[i] != NULL; i++) { + GBytes *blob_tmp; + g_autofree gchar *fn = NULL; + if (g_str_has_suffix(langs[i], ".UTF-8")) + continue; + fn = g_strdup_printf("fwupd-%s-%u-%u.bmp", langs[i], width, height); + blob_tmp = fu_archive_lookup_by_fn(archive, fn, NULL); + if (blob_tmp != NULL) { + g_debug("using UX image %s", fn); + return g_bytes_ref(blob_tmp); + } + g_debug("no %s found", fn); + } + + /* we found nothing */ + langs_str = g_strjoinv(",", (gchar **)langs); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to get splash file for %s in %s", + langs_str, + datadir_pkg); + return NULL; +} + +static gboolean +fu_uefi_capsule_plugin_write_splash_data(FuUefiCapsulePlugin *self, + FuDevice *device, + GBytes *blob, + GError **error) +{ + 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 = {0x0}, + .header_size = sizeof(efi_capsule_header_t), + .capsule_image_size = 0}; + g_autofree gchar *esp_path = NULL; + 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 */ + esp_path = fu_volume_get_mount_point(self->esp); + directory = fu_uefi_get_esp_path_for_os(device, esp_path); + basename = g_strdup_printf("fwupd-%s.cap", FU_EFIVAR_GUID_UX_CAPSULE); + fn = g_build_filename(directory, "fw", basename, NULL); + if (!fu_path_mkdir_parent(fn, error)) + return FALSE; + ofile = g_file_new_for_path(fn); + ostream = + G_OUTPUT_STREAM(g_file_replace(ofile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + if (ostream == NULL) + return FALSE; + + if (!fwupd_guid_from_string(FU_EFIVAR_GUID_UX_CAPSULE, + &capsule_header.guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + 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); + if (screen_y == fu_uefi_bgrt_get_height(self->bgrt)) { + header.y_offset = (gdouble)screen_y * 0.8f; + } else { + header.y_offset = + fu_uefi_bgrt_get_yoffset(self->bgrt) + fu_uefi_bgrt_get_height(self->bgrt); + }; + + /* header, payload and image has to add to zero */ + csum += fu_sum8((guint8 *)&capsule_header, sizeof(capsule_header)); + csum += fu_sum8((guint8 *)&header, sizeof(header)); + csum += fu_sum8_bytes(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 */ + return fu_uefi_device_write_update_info(FU_UEFI_DEVICE(device), + fn, + "fwupd-ux-capsule", + FU_EFIVAR_GUID_UX_CAPSULE, + error); +} + +static gboolean +fu_uefi_capsule_plugin_update_splash(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); + guint best_idx = G_MAXUINT; + guint32 lowest_border_pixels = G_MAXUINT; + guint32 screen_height = 768; + guint32 screen_width = 1024; + g_autoptr(GBytes) image_bmp = NULL; + + struct { + guint32 width; + guint32 height; + } sizes[] = {{640, 480}, /* matching the sizes in po/make-images */ + {800, 600}, + {1024, 768}, + {1920, 1080}, + {3840, 2160}, + {5120, 2880}, + {5688, 3200}, + {7680, 4320}, + {0, 0}}; + + /* no UX capsule support, so deleting var if it exists */ + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE)) { + g_debug("not providing UX capsule"); + return fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "fwupd-ux-capsule", error); + } + + /* get the boot graphics resource table data */ + if (!fu_uefi_bgrt_get_supported(self->bgrt)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "BGRT is not supported"); + return FALSE; + } + 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 */ + for (guint i = 0; sizes[i].width != 0; i++) { + guint32 border_pixels; + + /* disregard any images that are bigger than the screen */ + if (sizes[i].width > screen_width) + continue; + if (sizes[i].height > screen_height) + continue; + + /* is this the best fit for the display */ + border_pixels = (screen_width * screen_height) - (sizes[i].width * sizes[i].height); + if (border_pixels < lowest_border_pixels) { + lowest_border_pixels = border_pixels; + best_idx = i; + } + } + if (best_idx == G_MAXUINT) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to find a suitable image to use"); + return FALSE; + } + + /* get the raw data */ + image_bmp = fu_uefi_capsule_plugin_get_splash_data(sizes[best_idx].width, + sizes[best_idx].height, + error); + if (image_bmp == NULL) + return FALSE; + + /* perform the upload */ + return fu_uefi_capsule_plugin_write_splash_data(self, device, image_bmp, error); +} + +static gboolean +fu_uefi_capsule_plugin_write_firmware(FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + const gchar *str; + guint32 flashes_left; + g_autoptr(GError) error_splash = NULL; + + /* 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/fwupd/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); + + /* perform the update */ + fu_progress_set_status(progress, FWUPD_STATUS_SCHEDULING); + if (!fu_uefi_capsule_plugin_update_splash(plugin, device, &error_splash)) { + g_debug("failed to upload UEFI UX capsule text: %s", error_splash->message); + } + + return fu_device_write_firmware(device, blob_fw, progress, flags, error); +} + +static void +fu_uefi_capsule_plugin_load_config(FuPlugin *plugin, FuDevice *device) +{ + gboolean disable_shim; + gboolean fallback_removable_path; + guint64 sz_reqd = FU_UEFI_COMMON_REQUIRED_ESP_FREE_SPACE; + g_autofree gchar *require_esp_free_space = NULL; + g_autoptr(GError) error_local = NULL; + + /* parse free space needed for ESP */ + require_esp_free_space = fu_plugin_get_config_value(plugin, "RequireESPFreeSpace"); + if (require_esp_free_space != NULL) { + if (!fu_strtoull(require_esp_free_space, &sz_reqd, 0, G_MAXUINT64, &error_local)) { + g_warning("invalid ESP free space specified: %s", error_local->message); + } + } + fu_device_set_metadata_integer(device, "RequireESPFreeSpace", sz_reqd); + + /* shim used for SB or not? */ + disable_shim = fu_plugin_get_config_value_boolean(plugin, "DisableShimForSecureBoot"); + if (!disable_shim) + fu_device_add_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB); + + /* check if using UEFI removable path */ + fallback_removable_path = + fu_plugin_get_config_value_boolean(plugin, "FallbacktoRemovablePath"); + if (fallback_removable_path) + fu_device_add_private_flag(device, FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH); +} + +static gboolean +fu_uefi_capsule_plugin_is_esp_linux(FuVolume *esp, GError **error) +{ + const gchar *basenames_root[] = {"grub", "shim", "systemd-boot", NULL}; + const gchar *efi_suffix; + g_autofree gchar *basenames_str = NULL; + g_autofree gchar *mount_point = fu_volume_get_mount_point(esp); + g_auto(GStrv) basenames = g_new0(gchar *, G_N_ELEMENTS(basenames_root)); + g_autoptr(GPtrArray) files = NULL; + + /* build a list of possible files, e.g. grubx64.efi, shimaa64.efi, etc. */ + efi_suffix = fu_uefi_bootmgr_get_suffix(error); + if (efi_suffix == NULL) + return FALSE; + for (guint i = 0; basenames_root[i] != NULL; i++) + basenames[i] = g_strdup_printf("%s%s.efi", basenames_root[i], efi_suffix); + + /* look for any likely basenames */ + files = fu_path_get_files(mount_point, error); + if (files == NULL) + return FALSE; + for (guint i = 0; i < files->len; i++) { + const gchar *fn = g_ptr_array_index(files, i); + g_autofree gchar *basename = g_path_get_basename(fn); + g_autofree gchar *basename_lower = g_utf8_strdown(basename, -1); + if (g_strv_contains((const gchar *const *)basenames, basename_lower)) { + g_debug("found %s which indicates a Linux ESP, using %s", fn, mount_point); + return TRUE; + } + } + + /* failed */ + basenames_str = g_strjoinv("|", (gchar **)basenames); + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "did not find %s in %s", + basenames_str, + mount_point); + return FALSE; +} + +static FuVolume * +fu_uefi_capsule_plugin_get_default_esp(FuPlugin *plugin, GError **error) +{ + g_autoptr(GPtrArray) esp_volumes = NULL; + + /* show which volumes we're choosing from */ + esp_volumes = fu_context_get_esp_volumes(fu_plugin_get_context(plugin), error); + if (esp_volumes == NULL) + return NULL; + if (esp_volumes->len > 1) { + g_autoptr(GString) str = g_string_new(NULL); + for (guint i = 0; i < esp_volumes->len; i++) { + FuVolume *vol = g_ptr_array_index(esp_volumes, i); + if (str->len > 0) + g_string_append_c(str, ','); + g_string_append(str, fu_volume_get_id(vol)); + } + g_debug("more than one ESP possible: %s", str->str); + } + + /* we found more than one: lets look for something plausible */ + if (esp_volumes->len > 1) { + for (guint i = 0; i < esp_volumes->len; i++) { + FuVolume *esp = g_ptr_array_index(esp_volumes, i); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; + + locker = fu_volume_locker(esp, &error_local); + if (locker == NULL) { + g_warning("failed to mount ESP: %s", error_local->message); + continue; + } + if (!fu_uefi_capsule_plugin_is_esp_linux(esp, &error_local)) { + g_debug("not a Linux ESP: %s", error_local->message); + continue; + } + return g_object_ref(esp); + } + + /* we never found anything plausible */ + g_warning("more than one ESP possible -- using %s because it is listed first", + fu_volume_get_id(FU_VOLUME(g_ptr_array_index(esp_volumes, 0)))); + } + + /* "success" */ + return g_object_ref(g_ptr_array_index(esp_volumes, 0)); +} + +static void +fu_uefi_capsule_plugin_register_proxy_device(FuPlugin *plugin, FuDevice *device) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); + g_autoptr(FuUefiDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + + /* load all configuration variables */ + dev = fu_uefi_backend_device_new_from_dev(FU_UEFI_BACKEND(self->backend), device); + fu_uefi_capsule_plugin_load_config(plugin, FU_DEVICE(dev)); + if (self->esp == NULL) + self->esp = fu_uefi_capsule_plugin_get_default_esp(plugin, &error_local); + if (self->esp == NULL) { + fu_device_inhibit(device, "no-esp", error_local->message); + } else { + fu_uefi_device_set_esp(dev, self->esp); + fu_device_uninhibit(device, "no-esp"); + } + fu_plugin_device_add(plugin, FU_DEVICE(dev)); +} + +static void +fu_uefi_capsule_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + if (fu_device_get_metadata(device, FU_DEVICE_METADATA_UEFI_DEVICE_KIND) != NULL) { + if (fu_device_get_guid_default(device) == NULL) { + g_autofree gchar *dbg = fu_device_to_string(device); + g_warning("cannot create proxy device as no GUID: %s", dbg); + return; + } + fu_uefi_capsule_plugin_register_proxy_device(plugin, device); + } +} + +static const gchar * +fu_uefi_capsule_plugin_uefi_type_to_string(FuUefiDeviceKind device_kind) +{ + if (device_kind == FU_UEFI_DEVICE_KIND_UNKNOWN) + return "Unknown Firmware"; + if (device_kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) + return "System Firmware"; + if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) + return "Device Firmware"; + if (device_kind == FU_UEFI_DEVICE_KIND_UEFI_DRIVER) + return "UEFI Driver"; + if (device_kind == FU_UEFI_DEVICE_KIND_FMP) + return "Firmware Management Protocol"; + return NULL; +} + +static gchar * +fu_uefi_capsule_plugin_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_uefi_capsule_plugin_uefi_type_to_string(device_kind)); + if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) + g_string_prepend(display_name, "UEFI "); + return g_string_free(display_name, FALSE); +} + +static gboolean +fu_uefi_capsule_plugin_coldplug_device(FuPlugin *plugin, FuUefiDevice *dev, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + FuUefiDeviceKind device_kind; + + /* probe to get add GUIDs (and hence any quirk fixups) */ + if (!fu_device_probe(FU_DEVICE(dev), error)) + return FALSE; + if (!fu_device_setup(FU_DEVICE(dev), error)) + return FALSE; + + /* if not already set by quirks */ + if (fu_context_has_hwid_flag(ctx, "use-legacy-bootmgr-desc")) { + fu_device_add_private_flag(FU_DEVICE(dev), + FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC); + } + if (fu_context_has_hwid_flag(ctx, "supports-boot-order-lock")) { + fu_device_add_private_flag(FU_DEVICE(dev), + FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK); + } + if (fu_context_has_hwid_flag(ctx, "no-ux-capsule")) + fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE); + if (fu_context_has_hwid_flag(ctx, "no-lid-closed")) + fu_device_add_internal_flag(FU_DEVICE(dev), FU_DEVICE_INTERNAL_FLAG_NO_LID_CLOSED); + + /* set fallback name if nothing else is set */ + device_kind = fu_uefi_device_get_kind(dev); + if (fu_device_get_name(FU_DEVICE(dev)) == NULL) { + g_autofree gchar *name = NULL; + name = fu_uefi_capsule_plugin_get_name_for_type(plugin, device_kind); + if (name != NULL) + fu_device_set_name(FU_DEVICE(dev), name); + if (device_kind != FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { + fu_device_add_internal_flag(FU_DEVICE(dev), + FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY); + } + } + /* set fallback vendor if nothing else is set */ + if (fu_device_get_vendor(FU_DEVICE(dev)) == NULL && + device_kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { + const gchar *vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER); + if (vendor != NULL) + fu_device_set_vendor(FU_DEVICE(dev), vendor); + } + + /* set vendor ID as the BIOS vendor */ + if (device_kind != FU_UEFI_DEVICE_KIND_FMP) { + const gchar *dmi_vendor; + dmi_vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VENDOR); + if (dmi_vendor != NULL) { + g_autofree gchar *vendor_id = g_strdup_printf("DMI:%s", dmi_vendor); + fu_device_add_vendor_id(FU_DEVICE(dev), vendor_id); + } + } + + /* success */ + return TRUE; +} + +static void +fu_uefi_capsule_plugin_test_secure_boot(FuPlugin *plugin) +{ + const gchar *result_str = "Disabled"; + if (fu_efivar_secure_boot_enabled(NULL)) + result_str = "Enabled"; + fu_plugin_add_report_metadata(plugin, "SecureBoot", result_str); +} + +static gboolean +fu_uefi_capsule_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + guint64 nvram_total; + g_autofree gchar *esp_path = NULL; + g_autofree gchar *nvram_total_str = NULL; + g_autoptr(GError) error_local = NULL; + + /* don't let user's environment influence test suite failures */ + if (g_getenv("FWUPD_UEFI_TEST") != NULL) + return TRUE; + + /* for the uploaded report */ + if (fu_context_has_hwid_flag(ctx, "use-legacy-bootmgr-desc")) + fu_plugin_add_report_metadata(plugin, "BootMgrDesc", "legacy"); + + /* some platforms have broken SMBIOS data */ + if (fu_context_has_hwid_flag(ctx, "uefi-force-enable")) + return TRUE; + + /* use GRUB to load updates */ + if (fu_plugin_get_config_value_boolean(plugin, "EnableGrubChainLoad")) { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(self->backend), + FU_TYPE_UEFI_GRUB_DEVICE); + } + + /* check we can use this backend */ + if (!fu_backend_setup(self->backend, progress, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_WRITE)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* are the EFI dirs set up so we can update each device */ + if (!fu_efivar_supported(error)) + return FALSE; + nvram_total = fu_efivar_space_used(error); + if (nvram_total == G_MAXUINT64) + return FALSE; + nvram_total_str = g_strdup_printf("%" G_GUINT64_FORMAT, nvram_total); + fu_plugin_add_report_metadata(plugin, "EfivarNvramUsed", nvram_total_str); + + /* override the default ESP path */ + esp_path = fu_plugin_get_config_value(plugin, "OverrideESPMountPoint"); + if (esp_path != NULL) { + self->esp = fu_volume_new_esp_for_path(esp_path, error); + if (self->esp == NULL) { + g_prefix_error(error, + "invalid OverrideESPMountPoint=%s " + "specified in config: ", + esp_path); + 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; +} + +static gboolean +fu_uefi_capsule_plugin_unlock(FuPlugin *plugin, FuDevice *device, GError **error) +{ + 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; + + 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, + "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_inhibit(device_alt, "alt-device", "Preventing upgrades as alternate"); + + /* make sure that this unlocked device can be updated */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(device, "0.0.0.0"); + return TRUE; +} + +static void +fu_plugin_uefi_update_state_notify_cb(GObject *object, GParamSpec *pspec, FuPlugin *plugin) +{ + FuDevice *device = FU_DEVICE(object); + GPtrArray *devices; + g_autofree gchar *msg = NULL; + + /* device is not in needs-reboot state */ + if (fu_device_get_update_state(device) != FWUPD_UPDATE_STATE_NEEDS_REBOOT) + return; + + /* only do this on hardware that cannot coalesce multiple capsules */ + if (!fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "no-coalesce")) + return; + + /* mark every other device for this plugin as non-updatable */ + msg = g_strdup_printf("Cannot update as %s [%s] needs reboot", + fu_device_get_name(device), + fu_device_get_id(device)); + devices = fu_plugin_get_devices(plugin); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices, i); + if (device_tmp == device) + continue; + fu_device_inhibit(device_tmp, "no-coalesce", msg); + } +} + +static gboolean +fu_uefi_capsule_plugin_check_cod_support(FuPlugin *plugin, GError **error) +{ + gsize bufsz = 0; + guint64 value = 0; + g_autofree guint8 *buf = NULL; +#ifndef __aarch64__ + FuContext *ctx = fu_plugin_get_context(plugin); +#endif + + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "OsIndicationsSupported", + &buf, + &bufsz, + NULL, + error)) { + g_prefix_error(error, "failed to read EFI variable: "); + return FALSE; + } + if (!fu_memread_uint64_safe(buf, bufsz, 0x0, &value, G_LITTLE_ENDIAN, error)) + return FALSE; + if ((value & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Capsule-on-Disk is not supported"); + return FALSE; + } + +#ifndef __aarch64__ + /* several models with Insyde firmware have been released where OsIndications advertises + * support for CoD, but it simply does not work */ + if (!fu_context_has_hwid_flag(ctx, "uefi-allow-cod")) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Capsule-on-Disk is supported, but non-aarch64 platforms " + "require the 'uefi-allow-cod' quirk for it to be used"); + return FALSE; + } +#endif + + /* success */ + return TRUE; +} + +static gboolean +fu_uefi_capsule_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); + const gchar *str; + gboolean has_fde = FALSE; + g_autoptr(GError) error_udisks2 = NULL; + g_autoptr(GError) error_fde = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) devices = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 63, "find-esp"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "check-cod"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 8, "check-bitlocker"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "coldplug"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 26, "add-devices"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "setup-bgrt"); + + if (self->esp == NULL) { + self->esp = fu_uefi_capsule_plugin_get_default_esp(plugin, &error_udisks2); + if (self->esp == NULL) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + g_warning("cannot find default ESP: %s", error_udisks2->message); + } + } + fu_progress_step_done(progress); + + /* firmware may lie */ + if (!fu_plugin_get_config_value_boolean(plugin, "DisableCapsuleUpdateOnDisk")) { + g_autoptr(GError) error_cod = NULL; + if (!fu_uefi_capsule_plugin_check_cod_support(plugin, &error_cod)) { + g_debug("not using CapsuleOnDisk support: %s", error_cod->message); + } else { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(self->backend), + FU_TYPE_UEFI_COD_DEVICE); + } + } + fu_progress_step_done(progress); + + /* warn the user that BitLocker might ask for recovery key after fw update */ + if (!fu_common_check_full_disk_encryption(&error_fde)) { + g_debug("FDE in use, set flag: %s", error_fde->message); + has_fde = TRUE; + } + fu_progress_step_done(progress); + + /* add each device */ + if (!fu_backend_coldplug(self->backend, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + devices = fu_backend_get_devices(self->backend); + for (guint i = 0; i < devices->len; i++) { + FuUefiDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(GError) error_device = NULL; + + if (self->esp != NULL) + fu_uefi_device_set_esp(dev, self->esp); + if (!fu_uefi_capsule_plugin_coldplug_device(plugin, dev, &error_device)) { + if (g_error_matches(error_device, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning("skipping device that failed coldplug: %s", + error_device->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_device)); + return FALSE; + } + fu_device_add_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + + /* only system firmware "BIOS" can change the PCRx registers */ + if (fu_uefi_device_get_kind(dev) == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE && has_fde) + fu_device_add_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_AFFECTS_FDE); + + /* load all configuration variables */ + fu_uefi_capsule_plugin_load_config(plugin, FU_DEVICE(dev)); + + /* watch in case we set needs-reboot in the engine */ + g_signal_connect(FU_DEVICE(dev), + "notify::update-state", + G_CALLBACK(fu_plugin_uefi_update_state_notify_cb), + plugin); + + fu_plugin_device_add(plugin, FU_DEVICE(dev)); + } + fu_progress_step_done(progress); + + /* for debugging problems later */ + fu_uefi_capsule_plugin_test_secure_boot(plugin); + if (!fu_uefi_bgrt_setup(self->bgrt, &error_local)) + g_debug("BGRT setup failed: %s", error_local->message); + str = fu_uefi_bgrt_get_supported(self->bgrt) ? "Enabled" : "Disabled"; + g_debug("UX Capsule support : %s", str); + fu_plugin_add_report_metadata(plugin, "UEFIUXCapsule", str); + fu_progress_step_done(progress); + + return TRUE; +} + +static void +fu_uefi_capsule_plugin_init(FuUefiCapsulePlugin *self) +{ + self->bgrt = fu_uefi_bgrt_new(); +} + +static void +fu_uefi_capsule_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); + g_autoptr(GError) error_local = NULL; + + self->backend = fu_uefi_backend_new(ctx); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "dell"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "acpi_phat"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "uefi"); /* old name */ + + /* add a requirement on the fwupd-efi version -- which can change */ + if (!fu_uefi_capsule_plugin_fwupd_efi_probe(self, &error_local)) + g_debug("failed to get fwupd-efi runtime version: %s", error_local->message); +} + +static void +fu_uefi_capsule_finalize(GObject *obj) +{ + FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(obj); + if (self->esp != NULL) + g_object_unref(self->esp); + if (self->fwupd_efi_file != NULL) + g_object_unref(self->fwupd_efi_file); + if (self->fwupd_efi_monitor != NULL) { + g_file_monitor_cancel(self->fwupd_efi_monitor); + g_object_unref(self->fwupd_efi_monitor); + } + g_object_unref(self->backend); + g_object_unref(self->bgrt); + G_OBJECT_CLASS(fu_uefi_capsule_plugin_parent_class)->finalize(obj); +} + +static void +fu_uefi_capsule_plugin_class_init(FuUefiCapsulePluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_uefi_capsule_plugin_constructed; + object_class->finalize = fu_uefi_capsule_finalize; + plugin_class->to_string = fu_uefi_capsule_plugin_to_string; + plugin_class->clear_results = fu_uefi_capsule_plugin_clear_results; + plugin_class->add_security_attrs = fu_uefi_capsule_plugin_add_security_attrs; + plugin_class->device_registered = fu_uefi_capsule_plugin_device_registered; + plugin_class->startup = fu_uefi_capsule_plugin_startup; + plugin_class->unlock = fu_uefi_capsule_plugin_unlock; + plugin_class->coldplug = fu_uefi_capsule_plugin_coldplug; + plugin_class->write_firmware = fu_uefi_capsule_plugin_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-capsule-plugin.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-capsule-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6042241d2ff3e3d9eeda1071f677ff00f5867e4e --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-capsule-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUefiCapsulePlugin, fu_uefi_capsule_plugin, FU, UEFI_CAPSULE_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-cod-device.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-cod-device.c new file mode 100644 index 0000000000000000000000000000000000000000..dbc8d636219a2ab9b99176d5eafd15b2590192a2 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-cod-device.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-uefi-cod-device.h" +#include "fu-uefi-common.h" + +struct _FuUefiCodDevice { + FuUefiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU_TYPE_UEFI_DEVICE) + +static gboolean +fu_uefi_cod_device_get_results_for_idx(FuDevice *device, guint idx, GError **error) +{ + FuUefiDevice *device_uefi = FU_UEFI_DEVICE(device); + fwupd_guid_t guid = {0x0}; + gsize bufsz = 0; + guint32 status = 0; + guint32 total_size = 0; + g_autofree gchar *guidstr = NULL; + g_autofree gchar *name = NULL; + g_autofree guint8 *buf = NULL; + + /* read out result */ + name = g_strdup_printf("Capsule%04u", idx); + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_CAPSULE_REPORT, + name, + &buf, + &bufsz, + NULL, + error)) { + g_prefix_error(error, "failed to read %s: ", name); + return FALSE; + } + + /* sanity check */ + if (!fu_memread_uint32_safe(buf, bufsz, 0x00, &total_size, G_LITTLE_ENDIAN, error)) + return FALSE; + if (total_size < 0x3A) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "EFI_CAPSULE_RESULT_VARIABLE_HEADER too small"); + return FALSE; + } + + /* verify guid */ + if (!fu_memcpy_safe(guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + 0x08, /* src */ + sizeof(guid), + error)) + return FALSE; + guidstr = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + if (g_strcmp0(guidstr, fu_uefi_device_get_guid(device_uefi)) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "wrong GUID, expected %s, got %s", + fu_uefi_device_get_guid(device_uefi), + guidstr); + return FALSE; + } + + /* get status */ + if (!fu_memread_uint32_safe(buf, bufsz, 0x28, &status, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_uefi_device_set_status(device_uefi, status); + return TRUE; +} + +#define VARIABLE_IDX_SIZE 11 /* of CHAR16 */ + +static gboolean +fu_uefi_cod_device_get_variable_idx(const gchar *name, guint *value, GError **error) +{ + gsize bufsz = 0; + guint64 tmp = 0; + g_autofree guint8 *buf = NULL; + g_autofree gchar *str = NULL; + gunichar2 buf16[VARIABLE_IDX_SIZE] = {0x0}; + + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_CAPSULE_REPORT, name, &buf, &bufsz, NULL, error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)buf16, + sizeof(buf16), + 0x0, /* dst */ + buf, + bufsz, + 0x0, /* src */ + sizeof(buf16), + error)) + return FALSE; + + /* parse the value */ + str = g_utf16_to_utf8(buf16, VARIABLE_IDX_SIZE, NULL, NULL, error); + if (str == NULL) + return FALSE; + if (!g_str_has_prefix(str, "Capsule")) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wrong contents, got %s", + str); + return FALSE; + } + if (!fu_strtoull(str + strlen("Capsule"), &tmp, 0, G_MAXUINT32, error)) + return FALSE; + if (value != NULL) + *value = tmp; + return TRUE; +} + +static gboolean +fu_uefi_cod_device_get_results(FuDevice *device, GError **error) +{ + guint capsule_last = 1024; + + /* tell us where to stop */ + if (!fu_uefi_cod_device_get_variable_idx("CapsuleLast", &capsule_last, error)) + return FALSE; + for (guint i = 0; i <= capsule_last; i++) { + g_autoptr(GError) error_local = NULL; + if (fu_uefi_cod_device_get_results_for_idx(device, i, &error_local)) + return TRUE; + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) && + !g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + /* nothing found */ + return TRUE; +} + +static gboolean +fu_uefi_cod_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + g_autofree gchar *basename = NULL; + g_autofree gchar *cod_path = NULL; + g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self); + g_autoptr(GBytes) fw = NULL; + + /* ensure we have the existing state */ + if (fu_uefi_device_get_guid(self) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot update device info with no GUID"); + return FALSE; + } + + /* copy the capsule */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + basename = g_strdup_printf("fwupd-%s.cap", fu_uefi_device_get_guid(self)); + cod_path = g_build_filename(esp_path, "EFI", "UpdateCapsule", basename, NULL); + if (!fu_path_mkdir_parent(cod_path, error)) + return FALSE; + if (!fu_bytes_set_contents(cod_path, fw, error)) + return FALSE; + + /* + * NOTE: The EFI spec requires setting OsIndications! + * RT->SetVariable is not supported for all hardware, and so when using + * U-Boot, it applies the capsule even if OsIndications isn't set. + * The capsule is then deleted by U-Boot after it has been deployed. + */ + if (!fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE)) { + gsize bufsz = 0; + guint64 os_indications = 0; + g_autofree guint8 *buf = NULL; + g_autoptr(GError) error_local = NULL; + + /* the firmware does not normally populate OsIndications by default */ + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "OsIndications", + &buf, + &bufsz, + NULL, + &error_local)) { + g_debug("failed to read EFI variable: %s", error_local->message); + } else { + if (!fu_memread_uint64_safe(buf, + bufsz, + 0x0, + &os_indications, + G_LITTLE_ENDIAN, + error)) + return FALSE; + } + os_indications |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + if (!fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "OsIndications", + (guint8 *)&os_indications, + sizeof(os_indications), + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error)) { + g_prefix_error(error, "Could not set OsIndications: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_uefi_cod_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) +{ + /* FuUefiDevice */ + FU_DEVICE_CLASS(fu_uefi_cod_device_parent_class)->report_metadata_pre(device, metadata); + g_hash_table_insert(metadata, g_strdup("CapsuleApplyMethod"), g_strdup("cod")); +} + +static void +fu_uefi_cod_device_init(FuUefiCodDevice *self) +{ +} + +static void +fu_uefi_cod_device_class_init(FuUefiCodDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_uefi_cod_device_write_firmware; + klass_device->get_results = fu_uefi_cod_device_get_results; + klass_device->report_metadata_pre = fu_uefi_cod_device_report_metadata_pre; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-cod-device.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-cod-device.h new file mode 100644 index 0000000000000000000000000000000000000000..43ab2c862d60132428726dae780d64777bc18c1e --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-cod-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" + +#define FU_TYPE_UEFI_COD_DEVICE (fu_uefi_cod_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU, UEFI_COD_DEVICE, FuUefiDevice) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-common.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-common.c new file mode 100644 index 0000000000000000000000000000000000000000..e501c54ef5ff348a7371ddf25d83331f6024d9de --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-common.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-uefi-common.h" +#include "fu-uefi-device.h" + +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_path_from_kind(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_fallback_app_path(FuDevice *device, + 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 = g_build_filename(esp_path, "EFI", "boot", NULL); + return g_strdup_printf("%s/%s%s.efi", base, cmd, suffix); +} + +gchar * +fu_uefi_get_esp_app_path(FuDevice *device, 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(device, esp_path); + return g_strdup_printf("%s/%s%s.efi", base, cmd, suffix); +} + +gchar * +fu_uefi_get_built_app_path(GError **error) +{ + const gchar *suffix; + g_autofree gchar *prefix = NULL; + g_autofree gchar *source_path = NULL; + g_autofree gchar *source_path_signed = NULL; + gboolean source_path_exists = FALSE; + gboolean source_path_signed_exists = FALSE; + + suffix = fu_uefi_bootmgr_get_suffix(error); + if (suffix == NULL) + return NULL; + prefix = fu_path_from_kind(FU_PATH_KIND_EFIAPPDIR); + + source_path = g_strdup_printf("%s/fwupd%s.efi", prefix, suffix); + source_path_signed = g_strdup_printf("%s.signed", source_path); + + source_path_exists = g_file_test(source_path, G_FILE_TEST_EXISTS); + source_path_signed_exists = g_file_test(source_path_signed, G_FILE_TEST_EXISTS); + + if (fu_efivar_secure_boot_enabled(NULL)) { + if (!source_path_signed_exists) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "%s cannot be found", + source_path_signed); + return NULL; + } + return g_steal_pointer(&source_path_signed); + } + + if (source_path_exists) + return g_steal_pointer(&source_path); + if (source_path_signed_exists) + return g_steal_pointer(&source_path_signed); + + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "%s and %s cannot be found", + source_path, + source_path_signed); + return NULL; +} + +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_path_from_kind(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 || 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 */ + if (!fu_memread_uint32_safe(buf, bufsz, 10, &ui32, G_LITTLE_ENDIAN, error)) + return FALSE; + 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 */ + if (!fu_memread_uint32_safe(buf, bufsz, 14, &ui32, G_LITTLE_ENDIAN, error)) + return FALSE; + 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) { + if (!fu_memread_uint32_safe(buf, bufsz, 18, width, G_LITTLE_ENDIAN, error)) + return FALSE; + } + if (height != NULL) { + if (!fu_memread_uint32_safe(buf, bufsz, 22, height, G_LITTLE_ENDIAN, error)) + return FALSE; + } + return TRUE; +} + +gchar * +fu_uefi_get_esp_path_for_os(FuDevice *device, const gchar *base) +{ +#ifndef EFI_OS_DIR + const gchar *os_release_id = NULL; + const gchar *id_like = NULL; + g_autofree gchar *esp_path = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GHashTable) os_release = fwupd_get_os_release(&error_local); + /* try to lookup /etc/os-release ID key */ + 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"; + /* if ID key points at something existing return it */ + esp_path = g_build_filename(base, "EFI", os_release_id, NULL); + if (g_file_test(esp_path, G_FILE_TEST_IS_DIR) || os_release == NULL) + return g_steal_pointer(&esp_path); + /* if ID key doesn't exist, try ID_LIKE */ + id_like = g_hash_table_lookup(os_release, "ID_LIKE"); + if (id_like != NULL) { + g_auto(GStrv) split = g_strsplit(id_like, " ", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_autofree gchar *id_like_path = + g_build_filename(base, "EFI", split[i], NULL); + if (g_file_test(id_like_path, G_FILE_TEST_IS_DIR)) { + g_debug("Using ID_LIKE key from os-release"); + return g_steal_pointer(&id_like_path); + } + } + } + return g_steal_pointer(&esp_path); +#else + return g_build_filename(base, "EFI", EFI_OS_DIR, NULL); +#endif +} + +guint64 +fu_uefi_read_file_as_uint64(const gchar *path, const gchar *attr_name) +{ + guint64 tmp = 0; + g_autofree gchar *data = NULL; + g_autofree gchar *fn = g_build_filename(path, attr_name, NULL); + g_autoptr(GError) error_local = NULL; + + if (!g_file_get_contents(fn, &data, NULL, NULL)) + return 0x0; + if (!fu_strtoull(data, &tmp, 0, G_MAXUINT64, &error_local)) { + g_warning("invalid string specified: %s", error_local->message); + return G_MAXUINT64; + } + return tmp; +} + +gboolean +fu_uefi_cmp_asset(const gchar *source, const gchar *target) +{ + gsize len = 0; + g_autofree gchar *source_csum = NULL; + g_autofree gchar *source_data = NULL; + g_autofree gchar *target_csum = 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_csum = 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_csum = g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar *)target_data, len); + return g_strcmp0(target_csum, source_csum) == 0; +} + +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; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-common.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-common.h new file mode 100644 index 0000000000000000000000000000000000000000..f6db48c9c4491fd6e8483316cde07b5205609c13 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-common.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#ifdef HAVE_EFI_TIME_T +#include +#endif + +#define EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET 0x00010000 +#define EFI_CAPSULE_HEADER_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 +#define EFI_CAPSULE_HEADER_FLAGS_INITIATE_RESET 0x00040000 + +#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004ULL + +#ifndef HAVE_EFI_TIME_T +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; +#endif + +typedef struct __attribute__((__packed__)) { + fwupd_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; + fwupd_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_fallback_app_path(FuDevice *device, + const gchar *esp_path, + const gchar *cmd, + GError **error); +gchar * +fu_uefi_get_esp_app_path(FuDevice *device, 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); +gchar * +fu_uefi_get_esp_path_for_os(FuDevice *device, const gchar *esp_path); +guint64 +fu_uefi_read_file_as_uint64(const gchar *path, const gchar *attr_name); +gboolean +fu_uefi_cmp_asset(const gchar *source, const gchar *target); +gboolean +fu_uefi_copy_asset(const gchar *source, const gchar *target, GError **error); +const gchar * +fu_uefi_bootmgr_get_suffix(GError **error); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-device.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-device.c new file mode 100644 index 0000000000000000000000000000000000000000..617c9c337b6ac5023b1c9259870d94a6e5a496b8 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-device.c @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "fu-uefi-common.h" +#include "fu-uefi-device.h" +#include "fu-uefi-devpath.h" + +typedef struct { + FuVolume *esp; + FuDeviceLocker *esp_locker; + 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; + gboolean automounted_esp; +} FuUefiDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUefiDevice, fu_uefi_device, FU_TYPE_DEVICE) + +#define GET_PRIVATE(o) (fu_uefi_device_get_instance_private(o)) + +#define FU_EFI_FMP_CAPSULE_GUID "6dcbd5ed-e82d-4c44-bda1-7194199ad92a" + +enum { + PROP_0, + PROP_FW_CLASS, + PROP_KIND, + PROP_CAPSULE_FLAGS, + PROP_FW_VERSION, + PROP_FW_VERSION_LOWEST, + PROP_LAST_ATTEMPT_STATUS, + PROP_LAST_ATTEMPT_VERSION, + PROP_FMP_HARDWARE_INSTANCE, + PROP_LAST +}; + +void +fu_uefi_device_set_esp(FuUefiDevice *self, FuVolume *esp) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_UEFI_DEVICE(self)); + g_return_if_fail(FU_IS_VOLUME(esp)); + g_set_object(&priv->esp, esp); +} + +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; +} + +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, guint idt, GString *str) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + fu_string_append(str, idt, "Kind", fu_uefi_device_kind_to_string(priv->kind)); + fu_string_append(str, idt, "FwClass", priv->fw_class); + fu_string_append_kx(str, idt, "CapsuleFlags", priv->capsule_flags); + fu_string_append_kx(str, idt, "FwVersion", priv->fw_version); + fu_string_append_kx(str, idt, "FwVersionLowest", priv->fw_version_lowest); + fu_string_append(str, + idt, + "LastAttemptStatus", + fu_uefi_device_status_to_string(priv->last_attempt_status)); + fu_string_append_kx(str, idt, "LastAttemptVersion", priv->last_attempt_version); + if (priv->esp != NULL) { + fu_string_append(str, idt, "EspId", fu_volume_get_id(priv->esp)); + } + fu_string_append_ku(str, + idt, + "RequireESPFreeSpace", + fu_device_get_metadata_integer(device, "RequireESPFreeSpace")); +} + +static void +fu_uefi_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + + /* record if we had an invalid header during update */ + g_hash_table_insert(metadata, + g_strdup("MissingCapsuleHeader"), + g_strdup(priv->missing_header ? "True" : "False")); + + /* where the ESP was mounted during installation */ + g_hash_table_insert(metadata, g_strdup("EspPath"), fu_volume_get_mount_point(priv->esp)); +} + +static void +fu_uefi_device_report_metadata_post(FuDevice *device, GHashTable *metadata) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + + /* the actual last_attempt values */ + g_hash_table_insert(metadata, + g_strdup("LastAttemptStatus"), + g_strdup_printf("0x%x", priv->last_attempt_status)); + g_hash_table_insert(metadata, + g_strdup("LastAttemptVersion"), + g_strdup_printf("0x%x", priv->last_attempt_version)); +} + +FuUefiDeviceKind +fu_uefi_device_get_kind(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0); + return priv->kind; +} + +guint32 +fu_uefi_device_get_version(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0x0); + return priv->fw_version; +} + +guint32 +fu_uefi_device_get_version_lowest(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0x0); + return priv->fw_version_lowest; +} + +guint32 +fu_uefi_device_get_version_error(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0x0); + return priv->last_attempt_version; +} + +guint64 +fu_uefi_device_get_hardware_instance(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0x0); + return priv->fmp_hardware_instance; +} + +FuUefiDeviceStatus +fu_uefi_device_get_status(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0); + return priv->last_attempt_status; +} + +void +fu_uefi_device_set_status(FuUefiDevice *self, FuUefiDeviceStatus status) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + g_autofree gchar *err_msg = NULL; + g_autofree gchar *version_str = NULL; + + g_return_if_fail(FU_IS_UEFI_DEVICE(self)); + + /* cache for later */ + priv->last_attempt_status = status; + + /* all good */ + if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) { + fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_SUCCESS); + return; + } + + /* 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(FU_DEVICE(self), FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + } else { + fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_FAILED); + } + version_str = g_strdup_printf("%u", priv->last_attempt_version); + 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(FU_DEVICE(self), err_msg); +} + +guint32 +fu_uefi_device_get_capsule_flags(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), 0x0); + return priv->capsule_flags; +} + +const gchar * +fu_uefi_device_get_guid(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_UEFI_DEVICE(self), NULL); + return priv->fw_class; +} + +gchar * +fu_uefi_device_build_varname(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + return g_strdup_printf("fwupd-%s-%" G_GUINT64_FORMAT, + priv->fw_class, + priv->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_efivar_get_data(FU_EFIVAR_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_efivar_get_data(FU_EFIVAR_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_efivar_set_data(FU_EFIVAR_GUID_FWUPDATE, + varname, + data, + datasz, + FU_EFIVAR_ATTR_NON_VOLATILE | FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_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_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); +} + +GBytes * +fu_uefi_device_fixup_firmware(FuUefiDevice *self, GBytes *fw, GError **error) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + gsize bufsz; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *guid_new = NULL; + + priv->missing_header = FALSE; + + /* GUID is the first 16 bytes */ + if (bufsz < sizeof(fwupd_guid_t)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Invalid payload"); + return NULL; + } + guid_new = fwupd_guid_to_string((fwupd_guid_t *)buf, FWUPD_GUID_FLAG_MIXED_ENDIAN); + + /* ESRT header matches payload */ + if (g_strcmp0(fu_uefi_device_get_guid(self), guid_new) == 0) { + g_debug("ESRT matches payload GUID"); + return g_bytes_ref(fw); + } else if (g_strcmp0(guid_new, FU_EFI_FMP_CAPSULE_GUID) == 0 || + fu_device_has_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_NO_CAPSULE_HEADER_FIXUP)) { + return g_bytes_ref(fw); + } else { + guint hdrsize = getpagesize(); + fwupd_guid_t esrt_guid = {0x0}; + efi_capsule_header_t header = {0x0}; + g_autoptr(GByteArray) buf_hdr = g_byte_array_new(); + + g_warning("missing or invalid embedded capsule header"); + priv->missing_header = TRUE; + + /* create a fake header with plausible contents */ + header.flags = priv->capsule_flags; + header.header_size = hdrsize; + header.capsule_image_size = bufsz + hdrsize; + if (!fwupd_guid_from_string(fu_uefi_device_get_guid(self), + &esrt_guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) { + g_prefix_error(error, "Invalid ESRT GUID: "); + return NULL; + } + memcpy(&header.guid, &esrt_guid, sizeof(fwupd_guid_t)); + + /* prepend the header to the payload */ + g_byte_array_append(buf_hdr, (const guint8 *)&header, sizeof(header)); + fu_byte_array_set_size(buf_hdr, hdrsize, 0x00); + g_byte_array_append(buf_hdr, buf, bufsz); + return g_byte_array_free_to_bytes(g_steal_pointer(&buf_hdr)); + } +} + +gboolean +fu_uefi_device_write_update_info(FuUefiDevice *self, + const gchar *filename, + const gchar *varname, + const gchar *guid, + GError **error) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + gsize dp_bufsz = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autofree guint8 *dp_buf = NULL; + efi_update_info_t info = { + .update_info_version = 0x7, + .guid = {0x0}, + .capsule_flags = priv->capsule_flags, + .hw_inst = priv->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_TEST") != 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) + return FALSE; + + /* save this header and body to the hardware */ + if (!fwupd_guid_from_string(guid, &info.guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) + return FALSE; + g_byte_array_append(buf, (const guint8 *)&info, sizeof(info)); + g_byte_array_append(buf, dp_buf, dp_bufsz); + return fu_efivar_set_data(FU_EFIVAR_GUID_FWUPDATE, + varname, + buf->data, + buf->len, + FU_EFIVAR_ATTR_NON_VOLATILE | FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error); +} + +static gboolean +fu_uefi_device_check_esp_free(FuDevice *device, GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + guint64 sz_reqd = fu_device_get_metadata_integer(device, "RequireESPFreeSpace"); + if (sz_reqd == G_MAXUINT) { + g_debug("maximum size is not configured"); + return TRUE; + } + return fu_volume_check_free_space(priv->esp, sz_reqd, error); +} + +static gboolean +fu_uefi_check_asset(FuDevice *device, GError **error) +{ + g_autofree gchar *source_app = fu_uefi_get_built_app_path(error); + if (source_app == NULL) { + if (fu_efivar_secure_boot_enabled(NULL)) + g_prefix_error(error, "missing signed bootloader for secure boot: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_uefi_device_cleanup_esp(FuDevice *device, GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *esp_path = fu_volume_get_mount_point(priv->esp); + g_autofree gchar *pattern = NULL; + g_autoptr(GPtrArray) files = NULL; + + /* in case we call capsule install twice before reboot */ + if (fu_efivar_exists(FU_EFIVAR_GUID_EFI_GLOBAL, "BootNext")) + return TRUE; + + /* delete any files matching the glob in the ESP */ + files = fu_path_get_files(esp_path, error); + if (files == NULL) + return FALSE; + pattern = g_build_filename(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 (fu_path_fnmatch(pattern, fn)) { + g_autoptr(GFile) file = g_file_new_for_path(fn); + g_debug("deleting %s", fn); + if (!g_file_delete(file, NULL, error)) + return FALSE; + } + } + + /* delete any old variables */ + if (!fu_efivar_delete_with_glob(FU_EFIVAR_GUID_FWUPDATE, "fwupd*-*", error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_uefi_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + + /* mount if required */ + priv->esp_locker = fu_volume_locker(priv->esp, error); + if (priv->esp_locker == NULL) + return FALSE; + + /* sanity checks */ + if (!fu_uefi_device_cleanup_esp(device, error)) + return FALSE; + if (!fu_uefi_device_check_esp_free(device, error)) + return FALSE; + if (!fu_uefi_check_asset(device, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_uefi_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + + /* unmount ESP if we opened it */ + if (!fu_device_locker_close(priv->esp_locker, error)) + return FALSE; + g_clear_object(&priv->esp_locker); + + return TRUE; +} + +static gboolean +fu_uefi_device_probe(FuDevice *device, GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + FwupdVersionFormat version_format; + g_autofree gchar *version_lowest = NULL; + g_autofree gchar *version = NULL; + + /* broken sysfs? */ + if (priv->fw_class == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read fw_class"); + return FALSE; + } + + /* this is invalid */ + if (!fwupd_guid_is_valid(priv->fw_class)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ESRT GUID '%s' was not valid", + priv->fw_class); + return FALSE; + } + + /* add GUID first, as quirks may set the version format */ + fu_device_add_guid(device, priv->fw_class); + + /* set versions */ + version_format = fu_device_get_version_format(device); + version = fu_version_from_uint32(priv->fw_version, version_format); + fu_device_set_version_format(device, version_format); + fu_device_set_version_raw(device, priv->fw_version); + fu_device_set_version(device, version); + if (priv->fw_version_lowest != 0) { + version_lowest = fu_version_from_uint32(priv->fw_version_lowest, version_format); + fu_device_set_version_lowest_raw(device, priv->fw_version_lowest); + fu_device_set_version_lowest(device, version_lowest); + } + + /* set flags */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT); + fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON); + fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR); + + /* add icons */ + if (priv->kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { + fu_device_add_icon(device, "computer"); + fu_device_add_instance_id(device, "main-system-firmware"); + } + + /* whether to create a missing header */ + if (priv->kind == FU_UEFI_DEVICE_KIND_FMP || + priv->kind == FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE) + fu_device_add_private_flag(device, FU_UEFI_DEVICE_FLAG_NO_CAPSULE_HEADER_FIXUP); + + /* success */ + return TRUE; +} + +static gboolean +fu_uefi_device_get_results(FuDevice *device, GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + + /* just set the update error */ + fu_uefi_device_set_status(self, priv->last_attempt_status); + return TRUE; +} + +gchar * +fu_uefi_device_get_esp_path(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + return fu_volume_get_mount_point(priv->esp); +} + +static void +fu_uefi_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(object); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_FW_CLASS: + priv->fw_class = g_value_dup_string(value); + break; + case PROP_KIND: + priv->kind = g_value_get_uint(value); + break; + case PROP_CAPSULE_FLAGS: + priv->capsule_flags = g_value_get_uint(value); + break; + case PROP_FW_VERSION: + priv->fw_version = g_value_get_uint(value); + break; + case PROP_FW_VERSION_LOWEST: + priv->fw_version_lowest = g_value_get_uint(value); + break; + case PROP_LAST_ATTEMPT_STATUS: + fu_uefi_device_set_status(self, g_value_get_uint(value)); + break; + case PROP_LAST_ATTEMPT_VERSION: + priv->last_attempt_version = g_value_get_uint(value); + break; + case PROP_FMP_HARDWARE_INSTANCE: + priv->fmp_hardware_instance = g_value_get_uint64(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_uefi_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_uefi_device_init(FuUefiDevice *self) +{ + fu_device_set_summary(FU_DEVICE(self), "UEFI ESRT device"); + fu_device_add_protocol(FU_DEVICE(self), "org.uefi.capsule"); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE, + "no-ux-capsule"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE, + "use-shim-unique"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC, + "use-legacy-bootmgr-desc"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK, + "supports-boot-order-lock"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB, + "use-shim-for-sb"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH, + "fallback-to-removable-path"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE, + "no-rt-set-variable"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_NO_CAPSULE_HEADER_FIXUP, + "no-capsule-header-fixup"); +} + +static void +fu_uefi_device_finalize(GObject *object) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(object); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + + g_free(priv->fw_class); + if (priv->esp != NULL) + g_object_unref(priv->esp); + if (priv->esp_locker != NULL) + g_object_unref(priv->esp_locker); + + 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); + GParamSpec *pspec; + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + object_class->set_property = fu_uefi_device_set_property; + 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->prepare = fu_uefi_device_prepare; + klass_device->cleanup = fu_uefi_device_cleanup; + klass_device->report_metadata_pre = fu_uefi_device_report_metadata_pre; + klass_device->report_metadata_post = fu_uefi_device_report_metadata_post; + klass_device->get_results = fu_uefi_device_get_results; + klass_device->set_progress = fu_uefi_device_set_progress; + + /** + * FuUefiDevice:fw-class: + * + * The firmware class, i.e. the ESRT GUID. + */ + pspec = + g_param_spec_string("fw-class", + NULL, + NULL, + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FW_CLASS, pspec); + + /** + * FuUefiDevice:kind: + * + * The device kind. + */ + pspec = g_param_spec_uint("kind", + NULL, + NULL, + FU_UEFI_DEVICE_KIND_UNKNOWN, + FU_UEFI_DEVICE_KIND_LAST - 1, + FU_UEFI_DEVICE_KIND_UNKNOWN, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_KIND, pspec); + + /** + * FuUefiDevice:capsule-flags: + * + * The capsule flags to use for the update. + */ + pspec = g_param_spec_uint("capsule-flags", + NULL, + NULL, + 0, + G_MAXUINT32, + 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_CAPSULE_FLAGS, pspec); + + /** + * FuUefiDevice:fw-version: + * + * The current firmware version. + */ + pspec = g_param_spec_uint("fw-version", + NULL, + NULL, + 0, + G_MAXUINT32, + 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FW_VERSION, pspec); + + /** + * FuUefiDevice:fw-version-lowest: + * + * The lowest possible installable version. + */ + pspec = g_param_spec_uint("fw-version-lowest", + NULL, + NULL, + 0, + G_MAXUINT32, + 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FW_VERSION_LOWEST, pspec); + + /** + * FuUefiDevice:last-attempt-status: + * + * The last attempt status value. + */ + pspec = g_param_spec_uint("last-attempt-status", + NULL, + NULL, + FU_UEFI_DEVICE_STATUS_SUCCESS, + FU_UEFI_DEVICE_STATUS_LAST - 1, + FU_UEFI_DEVICE_STATUS_SUCCESS, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_LAST_ATTEMPT_STATUS, pspec); + + /** + * FuUefiDevice:last-attempt-version: + * + * The last attempt firmware version. + */ + pspec = g_param_spec_uint("last-attempt-version", + NULL, + NULL, + 0, + G_MAXUINT32, + 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_LAST_ATTEMPT_VERSION, pspec); + + /** + * FuUefiDevice:fmp-hardware-instance: + * + * The FMP hardware instance. + */ + pspec = + g_param_spec_uint64("fmp-hardware-instance", + NULL, + NULL, + 0, + G_MAXUINT64, + 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FMP_HARDWARE_INSTANCE, pspec); +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-device.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-device.h new file mode 100644 index 0000000000000000000000000000000000000000..82ada5c303df001c343e8874b3ed837da6c1ac86 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-device.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2015 Peter Jones + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" +#include "fu-uefi-update-info.h" + +#define FU_TYPE_UEFI_DEVICE (fu_uefi_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuUefiDevice, fu_uefi_device, FU, UEFI_DEVICE, FuDevice) + +struct _FuUefiDeviceClass { + FuDeviceClass parent_class; +}; + +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; + +/** + * FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE: + * + * No not use the additional UX capsule. + */ +#define FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE (1 << 0) +/** + * FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE: + * + * Use a unique shim filename to work around a common BIOS bug. + */ +#define FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE (1 << 1) +/** + * FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC: + * + * Use the legacy boot manager description to work around a Lenovo BIOS bug. + */ +#define FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC (1 << 2) +/** + * FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK: + * + * The BIOS might have Boot Order Lock enabled which can cause failures when + * not using grub chainloading or capsule-on-disk. + */ +#define FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK (1 << 3) +/** + * FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH: + * + * Try to fallback to use UEFI removable path if the shim path doesn't exist. + */ +#define FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH (1 << 4) +/** + * FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB: + * + * Use shim to load fwupdx64.efi when SecureBoot is turned on. + */ +#define FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB (1 << 5) +/** + * FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE: + * + * Do not use RT->SetVariable. + */ +#define FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE (1 << 6) +/** + * FU_UEFI_DEVICE_FLAG_NO_CAPSULE_HEADER_FIXUP: + * + * Do not prepend a plausible missing capsule header. + */ +#define FU_UEFI_DEVICE_FLAG_NO_CAPSULE_HEADER_FIXUP (1 << 7) + +FuUefiDeviceKind +fu_uefi_device_kind_from_string(const gchar *kind); + +void +fu_uefi_device_set_esp(FuUefiDevice *self, FuVolume *esp); +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); +gchar * +fu_uefi_device_get_esp_path(FuUefiDevice *self); +gchar * +fu_uefi_device_build_varname(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_device_write_update_info(FuUefiDevice *self, + const gchar *filename, + const gchar *varname, + const gchar *guid, + GError **error); +GBytes * +fu_uefi_device_fixup_firmware(FuUefiDevice *self, GBytes *fw, GError **error); +void +fu_uefi_device_set_status(FuUefiDevice *self, FuUefiDeviceStatus status); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-devpath.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-devpath.c new file mode 100644 index 0000000000000000000000000000000000000000..0a116e149d4306c21d706f7cb5ac78dd03fd7906 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-devpath.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +/* although against style, the order seems to matter on FreeBSD */ +/* clang-format off */ +#include +#include +/* clang-format on */ +#endif + +#include + +#include "fu-uefi-devpath.h" + +#if defined(__FreeBSD__) +#define EFIDP_END_TYPE 0x7f +#define EFIDP_END_ENTIRE 0xff +#endif + +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_dump_full(G_LOG_DOMAIN, + "efidp", + buf + offset, + sz - offset, + 32, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + for (gsize 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 --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-devpath.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-devpath.h new file mode 100644 index 0000000000000000000000000000000000000000..5a1f9320dc34d76c050abf632adcecf4c26574f5 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-devpath.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +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); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-grub-device.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-grub-device.c new file mode 100644 index 0000000000000000000000000000000000000000..bb57cb7b4104cd8b88dfcac29c993b026cd5aad1 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-grub-device.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2021 Mario Limonciello + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uefi-common.h" +#include "fu-uefi-grub-device.h" + +struct _FuUefiGrubDevice { + FuUefiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUefiGrubDevice, fu_uefi_grub_device, FU_TYPE_UEFI_DEVICE) + +static gboolean +fu_uefi_grub_device_mkconfig(FuDevice *device, + const gchar *esp_path, + const gchar *target_app, + GError **error) +{ + const gchar *argv_mkconfig[] = {"", "-o", "/boot/grub/grub.cfg", NULL}; + const gchar *argv_reboot[] = {"", "fwupd", NULL}; + g_autofree gchar *grub_mkconfig = NULL; + g_autofree gchar *grub_reboot = NULL; + g_autofree gchar *grub_target = NULL; + g_autofree gchar *localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + g_autofree gchar *output = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* find grub.conf */ + if (!g_file_test(argv_mkconfig[2], G_FILE_TEST_EXISTS)) + argv_mkconfig[2] = "/boot/grub2/grub.cfg"; + if (!g_file_test(argv_mkconfig[2], G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not find grub.conf"); + return FALSE; + } + + /* find grub-mkconfig */ + grub_mkconfig = fu_path_find_program("grub-mkconfig", NULL); + if (grub_mkconfig == NULL) + grub_mkconfig = fu_path_find_program("grub2-mkconfig", NULL); + if (grub_mkconfig == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not find grub-mkconfig"); + return FALSE; + } + + /* find grub-reboot */ + grub_reboot = fu_path_find_program("grub-reboot", NULL); + if (grub_reboot == NULL) + grub_reboot = fu_path_find_program("grub2-reboot", NULL); + if (grub_reboot == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not find grub-reboot"); + return FALSE; + } + + /* replace ESP info in conf with what we detected */ + g_string_append_printf(str, "EFI_PATH=%s\n", target_app); + fu_string_replace(str, esp_path, ""); + g_string_append_printf(str, "ESP=%s\n", esp_path); + grub_target = g_build_filename(localstatedir, "uefi_capsule.conf", NULL); + if (!g_file_set_contents(grub_target, str->str, -1, error)) + return FALSE; + + /* refresh GRUB configuration */ + argv_mkconfig[0] = grub_mkconfig; + if (!g_spawn_sync(NULL, + (gchar **)argv_mkconfig, + NULL, + G_SPAWN_DEFAULT, + NULL, + NULL, + &output, + NULL, + NULL, + error)) + return FALSE; + if (g_getenv("FWUPD_UPDATE_CAPSULE_VERBOSE") != NULL) + g_debug("%s", output); + + /* make fwupd default */ + argv_reboot[0] = grub_reboot; + return g_spawn_sync(NULL, + (gchar **)argv_reboot, + NULL, + G_SPAWN_DEFAULT, + NULL, + NULL, + NULL, + NULL, + NULL, + error); +} + +static gboolean +fu_uefi_grub_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + const gchar *fw_class = fu_uefi_device_get_guid(self); + g_autofree gchar *basename = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self); + g_autofree gchar *fn = NULL; + g_autofree gchar *source_app = NULL; + g_autofree gchar *target_app = NULL; + g_autofree gchar *varname = fu_uefi_device_build_varname(self); + g_autoptr(GBytes) fixed_fw = NULL; + g_autoptr(GBytes) fw = NULL; + + /* ensure we have the existing state */ + if (fw_class == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot update device info with no GUID"); + return FALSE; + } + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* save the blob to the ESP */ + directory = fu_uefi_get_esp_path_for_os(device, esp_path); + basename = g_strdup_printf("fwupd-%s.cap", fw_class); + fn = g_build_filename(directory, "fw", basename, NULL); + if (!fu_path_mkdir_parent(fn, error)) + return FALSE; + fixed_fw = fu_uefi_device_fixup_firmware(self, fw, error); + if (fixed_fw == NULL) + return FALSE; + if (!fu_bytes_set_contents(fn, fixed_fw, error)) + return FALSE; + + /* skip for self tests */ + if (g_getenv("FWUPD_UEFI_TEST") != NULL) + return TRUE; + + /* delete the logs to save space; use fwupdate to debug the EFI binary */ + if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE")) { + if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE", error)) + return FALSE; + } + if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG")) { + if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG", error)) + return FALSE; + } + + /* set the blob header shared with fwupd.efi */ + if (!fu_uefi_device_write_update_info(self, fn, varname, fw_class, error)) + return FALSE; + + /* 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 if correct asset in place */ + target_app = fu_uefi_get_esp_app_path(device, 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; + } + + /* we are using GRUB instead of NVRAM variables */ + return fu_uefi_grub_device_mkconfig(device, esp_path, target_app, error); +} + +static void +fu_uefi_grub_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) +{ + /* FuUefiDevice */ + FU_DEVICE_CLASS(fu_uefi_grub_device_parent_class)->report_metadata_pre(device, metadata); + g_hash_table_insert(metadata, g_strdup("CapsuleApplyMethod"), g_strdup("grub")); +} + +static void +fu_uefi_grub_device_init(FuUefiGrubDevice *self) +{ +} + +static void +fu_uefi_grub_device_class_init(FuUefiGrubDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_uefi_grub_device_write_firmware; + klass_device->report_metadata_pre = fu_uefi_grub_device_report_metadata_pre; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-grub-device.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-grub-device.h new file mode 100644 index 0000000000000000000000000000000000000000..0e28c4b1a5583587098f0fd21f4bf1af67628270 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-grub-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" + +#define FU_TYPE_UEFI_GRUB_DEVICE (fu_uefi_grub_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiGrubDevice, fu_uefi_grub_device, FU, UEFI_GRUB_DEVICE, FuUefiDevice) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-nvram-device.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-nvram-device.c new file mode 100644 index 0000000000000000000000000000000000000000..28413fb372c41ba411654a8aae97bc9b480dd2ca --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-nvram-device.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2018 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uefi-bootmgr.h" +#include "fu-uefi-common.h" +#include "fu-uefi-nvram-device.h" + +struct _FuUefiNvramDevice { + FuUefiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUefiNvramDevice, fu_uefi_nvram_device, FU_TYPE_UEFI_DEVICE) + +static gboolean +fu_uefi_nvram_device_get_results(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* check if something rudely removed our BOOTXXXX entry */ + if (!fu_uefi_bootmgr_verify_fwupd(&error_local)) { + if (fu_device_has_private_flag(device, + FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK)) { + g_prefix_error(&error_local, + "boot entry missing; " + "perhaps 'Boot Order Lock' enabled in the BIOS: "); + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + } else { + g_prefix_error(&error_local, "boot entry missing: "); + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED); + } + fu_device_set_update_error(device, error_local->message); + return TRUE; + } + + /* parent */ + return FU_DEVICE_CLASS(fu_uefi_nvram_device_parent_class)->get_results(device, error); +} + +static gboolean +fu_uefi_nvram_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiBootmgrFlags bootmgr_flags = FU_UEFI_BOOTMGR_FLAG_NONE; + const gchar *bootmgr_desc = "Linux Firmware Updater"; + const gchar *fw_class = fu_uefi_device_get_guid(self); + g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self); + g_autoptr(GBytes) fixed_fw = NULL; + g_autoptr(GBytes) fw = NULL; + g_autofree gchar *basename = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *varname = fu_uefi_device_build_varname(self); + + /* ensure we have the existing state */ + if (fw_class == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot update device info with no GUID"); + return FALSE; + } + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* save the blob to the ESP */ + directory = fu_uefi_get_esp_path_for_os(device, esp_path); + basename = g_strdup_printf("fwupd-%s.cap", fw_class); + fn = g_build_filename(directory, "fw", basename, NULL); + if (!fu_path_mkdir_parent(fn, error)) + return FALSE; + fixed_fw = fu_uefi_device_fixup_firmware(self, fw, error); + if (fixed_fw == NULL) + return FALSE; + if (!fu_bytes_set_contents(fn, fixed_fw, error)) + return FALSE; + + /* delete the logs to save space; use fwupdate to debug the EFI binary */ + if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE")) { + if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE", error)) + return FALSE; + } + if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG")) { + if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG", error)) + return FALSE; + } + + /* set the blob header shared with fwupd.efi */ + if (!fu_uefi_device_write_update_info(self, fn, varname, fw_class, error)) + return FALSE; + + /* update the firmware before the bootloader runs */ + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB)) + bootmgr_flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB; + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE)) + bootmgr_flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE; + + /* some legacy devices use the old name to deduplicate boot entries */ + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC)) + bootmgr_desc = "Linux-Firmware-Updater"; + if (!fu_uefi_bootmgr_bootnext(device, esp_path, bootmgr_desc, bootmgr_flags, error)) + return FALSE; + + /* success! */ + return TRUE; +} + +static void +fu_uefi_nvram_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) +{ + /* FuUefiDevice */ + FU_DEVICE_CLASS(fu_uefi_nvram_device_parent_class)->report_metadata_pre(device, metadata); + g_hash_table_insert(metadata, g_strdup("CapsuleApplyMethod"), g_strdup("nvram")); +} + +static void +fu_uefi_nvram_device_init(FuUefiNvramDevice *self) +{ +} + +static void +fu_uefi_nvram_device_class_init(FuUefiNvramDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->get_results = fu_uefi_nvram_device_get_results; + klass_device->write_firmware = fu_uefi_nvram_device_write_firmware; + klass_device->report_metadata_pre = fu_uefi_nvram_device_report_metadata_pre; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-nvram-device.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-nvram-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f022f43f8c33c5bd5d0c28b453c3065f7123195d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-nvram-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" + +#define FU_TYPE_UEFI_NVRAM_DEVICE (fu_uefi_nvram_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiNvramDevice, fu_uefi_nvram_device, FU, UEFI_NVRAM_DEVICE, FuUefiDevice) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-tool.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-tool.c new file mode 100644 index 0000000000000000000000000000000000000000..55dc423890be4ffefe3cc23b6341e4073f4fc1cc --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-tool.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "fu-context-private.h" +#include "fu-uefi-backend.h" +#include "fu-uefi-cod-device.h" +#include "fu-uefi-common.h" +#include "fu-uefi-grub-device.h" +#include "fu-uefi-nvram-device.h" +#include "fu-uefi-update-info.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 guint +fu_util_prompt_for_number(guint maxnum) +{ + gint retval; + guint answer = 0; + + do { + char buffer[64]; + + /* swallow the \n at end of line too */ + if (!fgets(buffer, sizeof(buffer), stdin)) + break; + if (strlen(buffer) == sizeof(buffer) - 1) + continue; + + /* get a number */ + retval = sscanf(buffer, "%u", &answer); + + /* positive */ + if (retval == 1 && answer <= maxnum) + break; + + /* TRANSLATORS: the user isn't reading the question */ + g_print(_("Please enter a number from 0 to %u: "), maxnum); + } while (TRUE); + return answer; +} + +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 *type = FALSE; + g_autofree gchar *esp_path = NULL; + g_autofree gchar *flags = FALSE; + g_autoptr(FuUtilPrivate) priv = g_new0(FuUtilPrivate, 1); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) esp_volumes = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuVolume) esp = NULL; + const GOptionEntry options[] = { + {"verbose", + 'v', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &verbose, + /* TRANSLATORS: command line option */ + N_("Show extra debugging information"), + NULL}, + {"version", + '\0', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_version, + /* TRANSLATORS: command line option */ + N_("Display version"), + NULL}, + {"log", + 'L', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_log, + /* TRANSLATORS: command line option */ + N_("Show the debug log from the last attempted update"), + NULL}, + {"list", + 'l', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_list, + /* TRANSLATORS: command line option */ + N_("List supported firmware updates"), + NULL}, + {"supported", + 's', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_supported, + /* TRANSLATORS: command line option */ + N_("Query for firmware update support"), + NULL}, + {"info", + 'i', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_info, + /* TRANSLATORS: command line option */ + N_("Show the information of firmware update status"), + NULL}, + {"enable", + 'e', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_enable, + /* TRANSLATORS: command line option */ + N_("Enable firmware update support on supported systems"), + NULL}, + {"esp-path", + 'p', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &esp_path, + /* TRANSLATORS: command line option */ + N_("Override the default ESP path"), + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + N_("PATH")}, + {"set-debug", + 'd', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_set_debug, + /* TRANSLATORS: command line option */ + N_("Set the debugging flag during update"), + NULL}, + {"unset-debug", + 'D', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_unset_debug, + /* TRANSLATORS: command line option */ + N_("Unset the debugging flag during update"), + NULL}, + {"apply", + 'a', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &apply, + /* TRANSLATORS: command line option */ + N_("Apply firmware updates"), + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + NC_("A single GUID", "GUID")}, + {"method", + 'm', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &type, + /* TRANSLATORS: command line option */ + N_("Device update method"), + "nvram|cod|grub"}, + {"flags", + 'f', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &flags, + /* TRANSLATORS: command line option */ + N_("Use quirk flags when installing firmware"), + NULL}, + {NULL}}; + + setlocale(LC_ALL, ""); + + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* ensure root user */ +#ifdef HAVE_GETUID + 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")); +#endif + + /* get a action_list of the commands */ + priv->context = g_option_context_new(NULL); + g_option_context_set_description( + priv->context, + /* TRANSLATORS: CLI description */ + _("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) { + (void)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) { + g_autoptr(FuVolume) volume = NULL; + volume = fu_volume_new_esp_for_path(esp_path, &error); + if (volume == NULL) { + /* TRANSLATORS: ESP is EFI System Partition */ + g_print("%s: %s\n", _("ESP specified was not valid"), error->message); + return EXIT_FAILURE; + } + fu_context_add_esp_volume(ctx, volume); + } + + /* get the default ESP only */ + esp_volumes = fu_context_get_esp_volumes(ctx, &error); + if (esp_volumes == NULL) { + g_printerr("%s\n", error->message); + return EXIT_FAILURE; + } + if (esp_volumes->len > 1) { + guint idx; + + /* TRANSLATORS: get interactive prompt */ + g_print("%s\n", _("Choose the ESP:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < esp_volumes->len; i++) { + FuVolume *esp_tmp = g_ptr_array_index(esp_volumes, i); + g_autofree gchar *id_type = fu_volume_get_id_type(esp_tmp); + g_print("%u.\t%s (%s)\n", i + 1, fu_volume_get_id(esp_tmp), id_type); + } + idx = fu_util_prompt_for_number(esp_volumes->len); + if (idx == 0) { + /* TRANSLATORS: we asked the user to choose an option and they declined */ + g_printerr("%s\n", _("Request canceled")); + return EXIT_FAILURE; + } + esp = g_object_ref(g_ptr_array_index(esp_volumes, idx - 1)); + } else { + esp = g_object_ref(g_ptr_array_index(esp_volumes, 0)); + } + + /* 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_efivar_get_data(FU_EFIVAR_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 = g_utf16_to_utf8(buf_ucs2, sz / 2, NULL, NULL, &error_local); + if (str == NULL) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + g_print("%s", str); + } + + if (action_list || action_supported || action_info) { + g_autoptr(FuBackend) backend = fu_uefi_backend_new(ctx); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error_local = NULL; + + /* load SMBIOS */ + if (!fu_context_load_hwinfo(ctx, &error_local)) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + + /* add each device */ + if (!fu_backend_coldplug(backend, progress, &error_local)) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + + /* set ESP */ + devices = fu_backend_get_devices(backend); + for (guint i = 0; i < devices->len; i++) { + FuUefiDevice *dev = g_ptr_array_index(devices, i); + fu_uefi_device_set_esp(dev, esp); + } + } + + /* 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} version %" G_GUINT32_FORMAT " can be updated " + "to any version above %" G_GUINT32_FORMAT "\n", + fu_uefi_device_kind_to_string(fu_uefi_device_get_kind(dev)), + fu_uefi_device_get_guid(dev), + fu_uefi_device_get_version(dev), + 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); + g_print("Information for the update status entry %u:\n", i); + if (info == NULL) { + if (g_error_matches(error_local, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND)) { + g_print(" Firmware GUID: {%s}\n", + fu_uefi_device_get_guid(dev)); + g_print(" Update Status: No update info found\n\n"); + } else { + g_printerr("Failed: %s\n\n", error_local->message); + } + continue; + } + 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_efivar_set_data(FU_EFIVAR_GUID_FWUPDATE, + "FWUPDATE_VERBOSE", + &data, + sizeof(data), + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_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_efivar_delete(FU_EFIVAR_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(FuBackend) backend = fu_uefi_backend_new(ctx); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuUefiDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "prepare"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "cleanup"); + + /* load SMBIOS */ + if (!fu_context_load_hwinfo(ctx, &error_local)) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + + /* type is specified, otherwise use default */ + if (type != NULL) { + if (g_strcmp0(type, "nvram") == 0) { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend), + FU_TYPE_UEFI_NVRAM_DEVICE); + } else if (g_strcmp0(type, "grub") == 0) { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend), + FU_TYPE_UEFI_GRUB_DEVICE); + } else if (g_strcmp0(type, "cod") == 0) { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend), + FU_TYPE_UEFI_COD_DEVICE); + } else { + g_printerr("invalid type specified\n"); + return EXIT_FAILURE; + } + } + + if (argv[1] == NULL) { + g_printerr("capsule filename required\n"); + return EXIT_FAILURE; + } + fw = fu_bytes_get_contents(argv[1], &error_local); + if (fw == NULL) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + dev = fu_uefi_backend_device_new_from_guid(FU_UEFI_BACKEND(backend), apply); + fu_uefi_device_set_esp(dev, esp); + if (flags != NULL) + fu_device_set_custom_flags(FU_DEVICE(dev), flags); + if (!fu_device_prepare(FU_DEVICE(dev), + fu_progress_get_child(progress), + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + fu_progress_step_done(progress); + if (!fu_device_write_firmware(FU_DEVICE(dev), + fw, + fu_progress_get_child(progress), + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + fu_progress_step_done(progress); + if (!fu_device_cleanup(FU_DEVICE(dev), + fu_progress_get_child(progress), + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { + g_printerr("failed: %s\n", error_local->message); + return EXIT_FAILURE; + } + fu_progress_step_done(progress); + } + + /* success */ + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-update-info.c b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-update-info.c new file mode 100644 index 0000000000000000000000000000000000000000..a5f034d511002017ebde92fcacb303f80d8a5eba --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-update-info.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uefi-common.h" +#include "fu-uefi-devpath.h" +#include "fu-uefi-update-info.h" + +#define EFIDP_MEDIA_TYPE 0x04 +#define EFIDP_MEDIA_FILE 0x4 + +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 = g_utf16_to_utf8(ucs2file, ucs2sz / sizeof(guint16), NULL, NULL, error); + if (relpath == NULL) + 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; + fwupd_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(fwupd_guid_t)); + self->guid = fwupd_guid_to_string(&guid_tmp, FWUPD_GUID_FLAG_MIXED_ENDIAN); + 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 --git a/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-update-info.h b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-update-info.h new file mode 100644 index 0000000000000000000000000000000000000000..5eebc347f1b054b082168f0d0cd5e8b4081e90b1 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fu-uefi-update-info.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#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); diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fwupd.grub.conf.in b/fwupd-1.8.6/plugins/uefi-capsule/fwupd.grub.conf.in new file mode 100755 index 0000000000000000000000000000000000000000..9c3a22f23a7e3d1c8abe929bab9f6b186d7ce5d2 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fwupd.grub.conf.in @@ -0,0 +1,24 @@ +#! /bin/sh +# SPDX-License-Identifier: LGPL-2.1+ +set -e + +[ -d ${pkgdatadir:?} ] +# shellcheck source=/dev/null +. "$pkgdatadir/grub-mkconfig_lib" + +if [ -f @localstatedir@/lib/fwupd/uefi_capsule.conf ] && + ls /sys/firmware/efi/efivars/fwupd-*-0abba7dc-e516-4167-bbf5-4d9d1c739416 1>/dev/null 2>&1; then + . @localstatedir@/lib/fwupd/uefi_capsule.conf + if [ "${EFI_PATH}" != "" ] && [ "${ESP}" != "" ]; then + echo "Adding Linux Firmware Updater entry" >&2 +cat << EOF +menuentry 'Linux Firmware Updater' \$menuentry_id_option 'fwupd' { +EOF + ${grub_probe:?} + prepare_grub_to_access_device '`${grub_probe} --target=device \${ESP}` | sed -e "s/^/\t/"' +cat << EOF + chainloader ${EFI_PATH} +} +EOF + fi +fi diff --git a/fwupd-1.8.6/plugins/uefi-capsule/fwupdate.1 b/fwupd-1.8.6/plugins/uefi-capsule/fwupdate.1 new file mode 100644 index 0000000000000000000000000000000000000000..513b14ca1ecc3383a4da7331352fd6bf5ddf3574 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/fwupdate.1 @@ -0,0 +1,17 @@ +.\" Report problems in https://github.com/fwupd/fwupd +.TH man 1 "27 May 2022" @PACKAGE_VERSION@ "fwupdate man page" +.SH NAME +fwupdate \-debugging utility for UEFI firmware updates +.SH SYNOPSIS +fwupdate [CMD] +.SH DESCRIPTION +fwupdate is a deprecated tool that allows deploying capsule updates. +.SH OPTIONS +The fwupdate command takes various options depending on the action. +Run \fBfwupdate --help\fR for the full list. +.SH SEE ALSO +fwupdtool(1), fwupdmgr(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Richard Hughes (richard@hughsie.com) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/make-images.py b/fwupd-1.8.6/plugins/uefi-capsule/make-images.py new file mode 100755 index 0000000000000000000000000000000000000000..a5e0ebf9082d6a480ba68c5884acf26890726a80 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/make-images.py @@ -0,0 +1,218 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Peter Jones +# Copyright (C) 2020 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1+ +# +# pylint: disable=wrong-import-position,too-many-locals,unused-argument,too-many-statements +# pylint: disable=invalid-name,too-many-instance-attributes,missing-module-docstring +# pylint: disable=missing-function-docstring,missing-class-docstring,too-few-public-methods + +import os +import sys +import argparse +import tarfile +import math +import io +import struct + +from typing import Dict, Optional, Any + +import cairo +import gi + +gi.require_version("Pango", "1.0") +gi.require_version("PangoCairo", "1.0") +from gi.repository import Pango, PangoCairo + + +def languages(podir: str): + for x in open(os.path.join(podir, "LINGUAS"), "r").readlines(): + yield x.strip() + yield "en" + + +class PotFile: + def __init__(self, fn=None): + self.msgs: Dict[str, str] = {} + if fn: + self.parse(fn) + + def parse(self, fn: str) -> None: + with open(fn, "r") as f: + lang_en: Optional[str] = None + for line in f.read().split("\n"): + if not line: + continue + if line[0] == "#": + continue + try: + key, value = line.split(" ", maxsplit=1) + except ValueError: + continue + if key == "msgid": + lang_en = value[1:-1] + continue + if key == "msgstr" and lang_en: + self.msgs[lang_en] = value[1:-1] + lang_en = None + continue + + +def _cairo_surface_write_to_bmp(img: cairo.ImageSurface) -> bytes: + + data = bytes(img.get_data()) + return ( + b"BM" + + struct.pack( + " int: + + # open output archive + with tarfile.open(args.out, "w:xz", preset=8) as tar: + + for lang in languages(args.podir): + # these are the 1.6:1 of some common(ish) screen widths + if lang == "en": + label_translated: str = args.label + else: + potfile = PotFile(os.path.join(args.podir, "{}.po".format(lang))) + try: + label_translated = potfile.msgs[args.label] + except KeyError: + continue + if label_translated == args.label: + continue + for width, height in ( + (640, 480), + (800, 600), + (1024, 768), + (1280, 720), + (1280, 800), + (1366, 768), + (1536, 864), + (1600, 900), + (1920, 1080), + (1920, 1200), + (2160, 1350), + (2560, 1440), + (3840, 2160), + (5120, 2880), + (5688, 3200), + (7680, 4320), + ): + + # generate PangoLanguage + font_desc = "Sans %.2fpx" % (height / 32,) + fd = Pango.FontDescription(font_desc) + font_option = cairo.FontOptions() + font_option.set_antialias(cairo.ANTIALIAS_SUBPIXEL) + l = Pango.Language.from_string(lang) + + # create surface + img = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1) + cctx = cairo.Context(img) + layout = PangoCairo.create_layout(cctx) + pctx = layout.get_context() + pctx.set_font_description(fd) + pctx.set_language(l) + fs = pctx.load_fontset(fd, l) + PangoCairo.context_set_font_options(pctx, font_option) + + attrs = Pango.AttrList() + length = len(bytes(label_translated, "utf8")) + items = Pango.itemize(pctx, label_translated, 0, length, attrs, None) + gs = Pango.GlyphString() + Pango.shape(label_translated, length, items[0].analysis, gs) + del img, cctx, pctx, layout + + def find_size(fs, f, data): + """find our size, I hope...""" + (ink, log) = gs.extents(f) + if ink.height == 0 or ink.width == 0: + return False + data.update({"log": log, "ink": ink}) + return True + + data: Dict[str, Any] = {} + fs.foreach(find_size, data) + if len(data) == 0: + print("Missing sans fonts") + return 2 + log = data["log"] + ink = data["ink"] + + surface_height = math.ceil(max(ink.height, log.height) / Pango.SCALE) + surface_width = math.ceil(max(ink.width, log.width) / Pango.SCALE) + + x = -math.ceil(log.x / Pango.SCALE) + y = -math.ceil(log.y / Pango.SCALE) + + img = cairo.ImageSurface( + cairo.FORMAT_RGB24, surface_width, surface_height + ) + cctx = cairo.Context(img) + layout = PangoCairo.create_layout(cctx) + pctx = layout.get_context() + PangoCairo.context_set_font_options(pctx, font_option) + + cctx.set_source_rgb(1, 1, 1) + cctx.move_to(x, y - surface_height / 2) + + def do_write(fs, f, data): + """write out glyphs""" + ink = gs.extents(f)[0] + if ink.height == 0 or ink.width == 0: + return False + PangoCairo.show_glyph_string(cctx, f, gs) + return True + + # flip the image to write the bitmap upside-down + mat = cairo.Matrix() + mat.scale(1, -1) + cctx.transform(mat) + + fs.foreach(do_write, None) + img.flush() + + # convert to BMP and add to archive + with io.BytesIO() as io_bmp: + io_bmp.write(_cairo_surface_write_to_bmp(img)) + filename = "fwupd-{}-{}-{}.bmp".format(lang, width, height) + tarinfo = tarfile.TarInfo(filename) + tarinfo.size = io_bmp.tell() + io_bmp.seek(0) + tar.addfile(tarinfo, fileobj=io_bmp) + + # success + return 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Make UX images") + parser.add_argument("--label", help="Update text", required=True) + parser.add_argument("--podir", help="Po location", required=True) + parser.add_argument("--out", help="Output archive", required=True) + sys.exit(main(parser.parse_args())) diff --git a/fwupd-1.8.6/plugins/uefi-capsule/meson.build b/fwupd-1.8.6/plugins/uefi-capsule/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..b4b038313e6071c8a89f1d5fcc8bb0af417cd67f --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/meson.build @@ -0,0 +1,168 @@ +if efiboot.found() and efivar.found() + +cargs = ['-DG_LOG_DOMAIN="FuPluginUefiCapsule"'] + +efi_os_dir = get_option('efi_os_dir') +if efi_os_dir != '' + cargs += '-DEFI_OS_DIR="' + efi_os_dir + '"' +endif + +plugin_quirks += files('uefi-capsule.quirk') + +backend_srcs = ['fu-uefi-backend.c'] +if host_machine.system() == 'linux' + backend_srcs += 'fu-uefi-backend-linux.c' + # replace @localstatedir@ + con2 = configuration_data() + con2.set('localstatedir', localstatedir) + configure_file( + input: 'fwupd.grub.conf.in', + output: '35_fwupd', + configuration: con2, + install: true, + install_dir: join_paths(sysconfdir, 'grub.d') + ) +elif host_machine.system() == 'freebsd' + backend_srcs += 'fu-uefi-backend-freebsd.c' +else + error('no ESRT support for @0@'.format(host_machine.system())) +endif + +plugin_builtin_uefi_capsule = static_library('fu_plugin_uefi_capsule', + sources: [ + 'fu-uefi-capsule-plugin.c', + 'fu-uefi-bgrt.c', + 'fu-uefi-bootmgr.c', + 'fu-uefi-common.c', + 'fu-uefi-cod-device.c', + 'fu-uefi-nvram-device.c', + 'fu-uefi-grub-device.c', + 'fu-uefi-device.c', + 'fu-uefi-devpath.c', + 'fu-uefi-update-info.c', + backend_srcs, + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + platform_deps, + efiboot, + ], +) +plugin_builtins += plugin_builtin_uefi_capsule + +if get_option('compat_cli') +fwupdate = executable( + 'fwupdate', + sources: [ + 'fu-uefi-tool.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + platform_deps, + efiboot, + ], + link_with: [ + plugin_libs, + plugin_builtin_uefi_capsule, + ], + install: true, + install_dir: bindir, + install_rpath: libdir_pkg, + c_args: cargs, +) +endif + +if get_option('compat_cli') and get_option('man') + configure_file( + input: 'fwupdate.1', + output: 'fwupdate.1', + configuration: conf, + install: true, + install_dir: join_paths(mandir, 'man1'), + ) +endif + +install_data(['uefi_capsule.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') +) + +# add all the .po files as inputs to watch +ux_linguas = run_command( + 'cat', files(join_paths(meson.project_source_root(), 'po', 'LINGUAS')), +).stdout().strip().split('\n') +ux_capsule_pofiles = [] +foreach ux_lingua: ux_linguas + ux_capsule_pofiles += join_paths(meson.project_source_root(), 'po', '@0@.po'.format(ux_lingua)) +endforeach + +if get_option('plugin_uefi_capsule_splash') + # add the archive of pregenerated images + custom_target('ux-capsule-tar', + input: [ + join_paths(meson.project_source_root(), 'po', 'LINGUAS'), + files('make-images.py'), + ux_capsule_pofiles, + ], + output: 'uefi-capsule-ux.tar.xz', + command: [ + python3.full_path(), + files('make-images.py'), + '--podir', join_paths(meson.project_source_root(), 'po'), + '--label', 'Installing firmware update…', + '--out', '@OUTPUT@', + ], + install: true, + install_dir: join_paths(datadir, 'fwupd'), + ) +endif + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + e = executable( + 'uefi-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + platform_deps, + efiboot, + ], + link_with: [ + fwupd, + fwupdplugin, + plugin_builtin_uefi_capsule, + ], + c_args: cargs + ) + test('uefi-self-test', e, env: env) + +# to use these do `sudo systemctl edit fwupd.service` and set +# Environment="FWUPD_SYSFSFWDIR=/usr/share/installed-tests/fwupd" +install_data([ + 'tests/efi/esrt/entries/entry0/capsule_flags', + 'tests/efi/esrt/entries/entry0/fw_class', + 'tests/efi/esrt/entries/entry0/fw_type', + 'tests/efi/esrt/entries/entry0/fw_version', + 'tests/efi/esrt/entries/entry0/last_attempt_status', + 'tests/efi/esrt/entries/entry0/last_attempt_version', + 'tests/efi/esrt/entries/entry0/lowest_supported_fw_version', + ], + install_dir: join_paths(installed_test_datadir, 'efi/esrt/entries/entry0'), +) +install_data([ + 'tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3', + ], + install_dir: join_paths(installed_test_datadir, 'efi/efivars'), +) +endif + +endif diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/.gitignore b/fwupd-1.8.6/plugins/uefi-capsule/tests/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3a6ac4554fd9b2a88a1b94c8f7c742b62a743c70 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/.gitignore @@ -0,0 +1,2 @@ +EFI +efi/efivars/fwupd-c34cb672-a81e-5d32-9d89-cbcabe8ec37b-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/image b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/image new file mode 120000 index 0000000000000000000000000000000000000000..978d35f94c1511ae1bf4b6adb314a3893b76928f --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/image @@ -0,0 +1 @@ +../../test.bmp \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/status b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/status new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/status @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/type b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/type new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/type @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/version b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/version new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/version @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/xoffset b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/xoffset new file mode 100644 index 0000000000000000000000000000000000000000..190a18037c64c43e6b11489df4bf0b9eb6d2c9bf --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/xoffset @@ -0,0 +1 @@ +123 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/yoffset b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/yoffset new file mode 100644 index 0000000000000000000000000000000000000000..8d38505c168687ee1b42c6b49f30d45468ab5638 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/acpi/bgrt/yoffset @@ -0,0 +1 @@ +456 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/height b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/height new file mode 100644 index 0000000000000000000000000000000000000000..0c2b7810b5ff9dc14a44270f1f9c3bae28f74855 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/height @@ -0,0 +1 @@ +789 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/width b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/width new file mode 100644 index 0000000000000000000000000000000000000000..8d38505c168687ee1b42c6b49f30d45468ab5638 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/width @@ -0,0 +1 @@ +456 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/Capsule0000-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/Capsule0000-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 0000000000000000000000000000000000000000..834766aeb1c2c2df82778a454b5526bf46cfa5af Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/Capsule0000-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/Capsule0001-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/Capsule0001-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 0000000000000000000000000000000000000000..7655990f59fb8589c16ba73f4983c34452544a0e Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/Capsule0001-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/CapsuleLast-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/CapsuleLast-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 0000000000000000000000000000000000000000..2f697464dfc667839c740d36b2c0a9746a3eb242 Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/CapsuleLast-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 0000000000000000000000000000000000000000..9e6766a5aef29af082da4af47a647cf78e01040f Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 new file mode 100644 index 0000000000000000000000000000000000000000..8fbb1772616b13ae00a5aa56bacd27217d63013c Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/efivars/fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416 differ diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/capsule_flags b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/capsule_flags new file mode 100644 index 0000000000000000000000000000000000000000..1de090ae8ed9ac22623d518ecdc2166abedfce0f --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/capsule_flags @@ -0,0 +1 @@ +0xfe diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_class b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_class new file mode 100644 index 0000000000000000000000000000000000000000..eee3dd810e31b391f9351c90e208fcef6801ef17 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_class @@ -0,0 +1 @@ +ddc0ee61-e7f0-4e7d-acc5-c070a398838e diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_type b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_type new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_type @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_version new file mode 100644 index 0000000000000000000000000000000000000000..e3ece010ba31607418270eff0fb7806dbcf83b2b --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/fw_version @@ -0,0 +1 @@ +65586 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/last_attempt_status b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/last_attempt_status new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/last_attempt_status @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/last_attempt_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/last_attempt_version new file mode 100644 index 0000000000000000000000000000000000000000..5d90037c1956a958384293f2e6eb045d622e2953 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/last_attempt_version @@ -0,0 +1 @@ +18472960 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/lowest_supported_fw_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/lowest_supported_fw_version new file mode 100644 index 0000000000000000000000000000000000000000..a0709178f6c0f8e97e2b390585d2cbc06b6d4eef --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry0/lowest_supported_fw_version @@ -0,0 +1 @@ +65582 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/capsule_flags b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/capsule_flags new file mode 100644 index 0000000000000000000000000000000000000000..ee9d215e49d65569ab701444ffc6fb3670f83d39 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/capsule_flags @@ -0,0 +1 @@ +0x8010 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_class b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_class new file mode 100644 index 0000000000000000000000000000000000000000..7e2ca013b7da11d5bee9d6d45e6f5bbc3626f41a --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_class @@ -0,0 +1 @@ +671d19d0-d43c-4852-98d9-1ce16f9967e4 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_type b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_type new file mode 100644 index 0000000000000000000000000000000000000000..0cfbf08886fca9a91cb753ec8734c84fcbe52c9f --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_type @@ -0,0 +1 @@ +2 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_version new file mode 100644 index 0000000000000000000000000000000000000000..51b89674f148ad366c74a9162d6c26f72e3c9a9b --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/fw_version @@ -0,0 +1 @@ +3090287969 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/last_attempt_status b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/last_attempt_status new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/last_attempt_status @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/last_attempt_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/last_attempt_version new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/last_attempt_version @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/lowest_supported_fw_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/lowest_supported_fw_version new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry1/lowest_supported_fw_version @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/capsule_flags b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/capsule_flags new file mode 120000 index 0000000000000000000000000000000000000000..24b0ea1b5413638c3cb88950027711348efd567e --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/capsule_flags @@ -0,0 +1 @@ +../entry1/capsule_flags \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_class b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_class new file mode 100644 index 0000000000000000000000000000000000000000..44964b11f68322d9cf684f1330fc7710a2ea3374 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_class @@ -0,0 +1 @@ +00000000-0000-0000-0000-000000000000 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_type b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_type new file mode 120000 index 0000000000000000000000000000000000000000..d7a6438a26777c6f97cfce1a162e1a3ddceb3474 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_type @@ -0,0 +1 @@ +../entry1/fw_type \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_version new file mode 120000 index 0000000000000000000000000000000000000000..3d6ce82dce82d27e49bd7f3068ee851eab8de7d3 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/fw_version @@ -0,0 +1 @@ +../entry1/fw_version \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/last_attempt_status b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/last_attempt_status new file mode 120000 index 0000000000000000000000000000000000000000..d8d4cb3890d050b3cc6d44ef8bf5b171110a5f2b --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/last_attempt_status @@ -0,0 +1 @@ +../entry1/last_attempt_status \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/last_attempt_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/last_attempt_version new file mode 120000 index 0000000000000000000000000000000000000000..639dcb18576b8943cddf6d699ac0c39bb7225e92 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/last_attempt_version @@ -0,0 +1 @@ +../entry1/last_attempt_version \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/lowest_supported_fw_version b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/lowest_supported_fw_version new file mode 120000 index 0000000000000000000000000000000000000000..9b389118cae7cd4b84d9fa78be34ff967fb413f8 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/esrt/entries/entry2/lowest_supported_fw_version @@ -0,0 +1 @@ +../entry1/lowest_supported_fw_version \ No newline at end of file diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/fw_platform_size b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/fw_platform_size new file mode 100644 index 0000000000000000000000000000000000000000..900731ffd51ffc82db488b6554f719de735f12bd --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/efi/fw_platform_size @@ -0,0 +1 @@ +64 diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/example/.gitignore b/fwupd-1.8.6/plugins/uefi-capsule/tests/example/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7c2e9bf00fb67787d7eec84537a2c7d1b788f6bd --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/example/.gitignore @@ -0,0 +1,2 @@ +firmware.bin +firmware.cab diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/example/build.sh b/fwupd-1.8.6/plugins/uefi-capsule/tests/example/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..71878aa742f07589b897542062af919bccf65ed6 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/example/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +appstream-util validate-relax firmware.metainfo.xml +echo -n "hello world" > firmware.bin +gcab --create --nopath firmware.cab firmware.bin firmware.metainfo.xml diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/example/firmware.metainfo.xml b/fwupd-1.8.6/plugins/uefi-capsule/tests/example/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..9175b3d73da148996fe315a887a59996d2ecf3af --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/tests/example/firmware.metainfo.xml @@ -0,0 +1,32 @@ + + + + example.firmware + UEFI Firmware + Example UEFI firmware for fwupd + +

    + The test device can be updated using the UEFI capsule plugin. +

    +
    + + ddc0ee61-e7f0-4e7d-acc5-c070a398838e + + https://fwupd.org/ + CC0-1.0 + CC0-1.0 + Richard Hughes + + + + +

    + This release updates a frobnicator to frob faster. +

    +
    +
    +
    + + org.freedesktop.fwupd + +
    diff --git a/fwupd-1.8.6/plugins/uefi-capsule/tests/test.bmp b/fwupd-1.8.6/plugins/uefi-capsule/tests/test.bmp new file mode 100644 index 0000000000000000000000000000000000000000..fe7a8cfbdc86a06ce1e9f03847b59d6ddbe7cc8c Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-capsule/tests/test.bmp differ diff --git a/fwupd-1.8.6/plugins/uefi-capsule/uefi-capsule.quirk b/fwupd-1.8.6/plugins/uefi-capsule/uefi-capsule.quirk new file mode 100644 index 0000000000000000000000000000000000000000..e741d4da5e377baaeaefe6bc9cac318027f08639 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/uefi-capsule.quirk @@ -0,0 +1,39 @@ +# StarLite Mk II +[797f8bae-0ea2-4c0f-8a30-7d10ccfacbc0] +Flags = no-ux-capsule + +# StarLite Mk III +[d9d7b13b-e4db-4f91-8bf6-8952a9caa82a] +Flags = no-ux-capsule + +# Silicom Minnowboard Turbot D0/D1 PLATFORM +[386c2f52-44ec-55d3-b802-47631bfb8451] +Flags = uefi-force-enable + +# Lenovo +[6de5d951-d755-576b-bd09-c5cf66b27234] +Flags = supports-boot-order-lock,use-legacy-bootmgr-desc,no-ux-capsule,no-lid-closed + +# Lenovo ThinkPad X1 Yoga 4th +[1138930e-8df1-5ae0-b946-7b0d9c9b4a79] +Flags = no-coalesce + +# Lenovo ThinkPad X1 Carbon 5th +[595a0c1a-9819-52f6-8ea7-e0924c073f64] +Flags = no-coalesce + +# Lenovo ThinkPad X1 Carbon 7th +[30ddbc9b-8200-5581-8dfd-6bb26e2e42d7] +Flags = no-coalesce + +# Lenovo ThinkPad T460s +[90706264-e399-575b-a9fb-077ead03b9b4] +Flags = no-coalesce + +# Dynabook (né Toshiba) X30, X40 +[28108d08-5027-42c2-a5b8-92d6ede9b97b] +VersionFormat = bcd + +# HP Dev One +[9f7962c7-7d0e-473d-8a01-c168912f1f14] +Flags = no-ux-capsule diff --git a/fwupd-1.8.6/plugins/uefi-capsule/uefi_capsule.conf b/fwupd-1.8.6/plugins/uefi-capsule/uefi_capsule.conf new file mode 100644 index 0000000000000000000000000000000000000000..fc1c3b417e4fe4922ac5c3f0070f2663d81c1028 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-capsule/uefi_capsule.conf @@ -0,0 +1,18 @@ +[uefi_capsule] + +# use GRUB to launch fwupdx64.efi +#EnableGrubChainLoad=false + +# the shim loader is required to chainload the fwupd EFI binary unless +# the fwupd.efi file has been self-signed manually +#DisableShimForSecureBoot=true + +# amount of free space required on the ESP, for example using 0x2000000 for 32Mb +#RequireESPFreeSpace= + +# with the UEFI removable path enabled, the default esp path is set to /EFI/boot +# the shim EFI binary and presumably this is $ESP/EFI/boot/bootx64.efi +#FallbacktoRemovablePath=false + +# allow ignoring the CapsuleOnDisk support advertised by the firmware +#DisableCapsuleUpdateOnDisk=true diff --git a/fwupd-1.8.6/plugins/uefi-dbx/README.md b/fwupd-1.8.6/plugins/uefi-dbx/README.md new file mode 100644 index 0000000000000000000000000000000000000000..324f4f302d678132ace8263efcee0940ede1b1a5 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/README.md @@ -0,0 +1,47 @@ +# UEFI dbx + +## Introduction + +Updating the UEFI revocation database prevents starting EFI binaries with known +security issues, and is typically no longer done from a firmware update due to +the risk of the machine being "bricked" if the bootloader is not updated first. + +This plugin also checks if the UEFI dbx contains all the most recent revoked +checksums. The result will be stored in an security attribute for HSI. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +EFI_SIGNATURE_LIST format. + +See +for details. + +This plugin supports the following protocol ID: + +* org.uefi.dbx + +## GUID Generation + +These devices use the GUID constructed of the uppercase SHA256 of the X509 +certificates found in the system KEK and optionally the EFI architecture. e.g. + +* `UEFI\CRT_{sha256}` +* `UEFI\CRT_{sha256}&ARCH_{arch}` + +...where `arch` is typically one of `IA32`, `X64`, `ARM` or `AA64` + +## Update Behavior + +The firmware is deployed when the machine is in normal runtime mode, but it is +only activated when the system is restarted. + +## Vendor ID Security + +The vendor ID is hardcoded to `UEFI:Microsoft` for all devices. + +## External Interface Access + +This plugin requires: + +* read/write access to `/sys/firmware/efi/efivars` diff --git a/fwupd-1.8.6/plugins/uefi-dbx/dbxtool.1 b/fwupd-1.8.6/plugins/uefi-dbx/dbxtool.1 new file mode 100644 index 0000000000000000000000000000000000000000..ea4453b5dff5c80ed41001b949051f015af8e2d2 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/dbxtool.1 @@ -0,0 +1,22 @@ +.\" Report problems in https://github.com/fwupd/fwupd +.TH man 1 "11 April 2021" @PACKAGE_VERSION@ "dbxtool man page" +.SH NAME +dbxtool \- modify the dbx revokation list +.SH SYNOPSIS +dbxtool [CMD] +.SH DESCRIPTION +.PP +This manual page documents briefly the \fBdbxtool\fR command. +.PP +\fBdbxtool\fR allows a user to operate on the UEFI dbx revokation list. +This tool can be used to list the current dbx contents or update it to a newer +version. +.SH OPTIONS +The dbxtool command takes various options depending on the action. +Run \fBdbxtool --help\fR for the full list. +.SH SEE ALSO +fwupdmgr(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Richard Hughes (richard@hughsie.com) diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-dbxtool.c b/fwupd-1.8.6/plugins/uefi-dbx/fu-dbxtool.c new file mode 100644 index 0000000000000000000000000000000000000000..ddd94622b0f7b26f6f894beae50a215964db95d7 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-dbxtool.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2015 Peter Jones + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include + +#include "fu-context-private.h" +#include "fu-uefi-dbx-common.h" + +/* custom return code */ +#define EXIT_NOTHING_TO_DO 2 + +static void +fu_util_ignore_cb(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ +} + +static FuFirmware * +fu_dbxtool_get_siglist_system(GError **error) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) dbx = fu_efi_signature_list_new(); + blob = fu_efivar_get_data_bytes(FU_EFIVAR_GUID_SECURITY_DATABASE, "dbx", NULL, error); + if (blob == NULL) + return NULL; + if (!fu_firmware_parse(dbx, blob, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return NULL; + return g_steal_pointer(&dbx); +} + +static FuFirmware * +fu_dbxtool_get_siglist_local(const gchar *filename, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) siglist = fu_efi_signature_list_new(); + blob = fu_bytes_get_contents(filename, error); + if (blob == NULL) + return NULL; + if (!fu_firmware_parse(siglist, blob, FWUPD_INSTALL_FLAG_NONE, error)) + return NULL; + return g_steal_pointer(&siglist); +} + +static gboolean +fu_dbxtool_siglist_inclusive(FuFirmware *outer, FuFirmware *inner) +{ + g_autoptr(GPtrArray) sigs = fu_firmware_get_images(inner); + for (guint i = 0; i < sigs->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i); + g_autofree gchar *checksum = NULL; + g_autoptr(FuFirmware) img = NULL; + checksum = fu_firmware_get_checksum(FU_FIRMWARE(sig), G_CHECKSUM_SHA256, NULL); + if (checksum == NULL) + continue; + img = fu_firmware_get_image_by_checksum(outer, checksum, NULL); + if (img == NULL) + return FALSE; + } + return TRUE; +} + +static const gchar * +fu_dbxtool_guid_to_string(const gchar *guid) +{ + if (g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_ZERO) == 0) + return "zero"; + if (g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_MICROSOFT) == 0) + return "microsoft"; + if (g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_OVMF) == 0 || + g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_OVMF_LEGACY) == 0) + return "ovmf"; + return guid; +} + +int +main(int argc, char *argv[]) +{ + gboolean action_apply = FALSE; + gboolean action_list = FALSE; + gboolean action_version = FALSE; + gboolean force = FALSE; + gboolean verbose = FALSE; + g_autofree gchar *dbxfile = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(GOptionContext) context = NULL; + g_autofree gchar *tmp = NULL; + g_autofree gchar *esp_path = NULL; + const GOptionEntry options[] = { + {"verbose", + 'v', + 0, + G_OPTION_ARG_NONE, + &verbose, + /* TRANSLATORS: command line option */ + N_("Show extra debugging information"), + NULL}, + {"version", + '\0', + 0, + G_OPTION_ARG_NONE, + &action_version, + /* TRANSLATORS: command line option */ + N_("Show the calculated version of the dbx"), + NULL}, + {"list", + 'l', + 0, + G_OPTION_ARG_NONE, + &action_list, + /* TRANSLATORS: command line option */ + N_("List entries in dbx"), + NULL}, + {"apply", + 'a', + 0, + G_OPTION_ARG_NONE, + &action_apply, + /* TRANSLATORS: command line option */ + N_("Apply update files"), + NULL}, + {"dbx", + 'd', + 0, + G_OPTION_ARG_STRING, + &dbxfile, + /* TRANSLATORS: command line option */ + N_("Specify the dbx database file"), + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + N_("FILENAME")}, + {"esp-path", + 'p', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &esp_path, + /* TRANSLATORS: command line option */ + N_("Override the default ESP path"), + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + N_("PATH")}, + {"force", + 'f', + 0, + G_OPTION_ARG_NONE, + &force, + /* TRANSLATORS: command line option */ + N_("Apply update even when not advised"), + NULL}, + {NULL}}; + + setlocale(LC_ALL, ""); + + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* get a action_list of the commands */ + context = g_option_context_new(NULL); + g_option_context_set_description( + context, + /* TRANSLATORS: description of dbxtool */ + _("This tool allows an administrator to apply UEFI dbx updates.")); + + /* TRANSLATORS: program name */ + g_set_application_name(_("UEFI dbx Utility")); + g_option_context_add_main_entries(context, options, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + /* 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) { + (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); + } else { + g_log_set_handler(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fu_util_ignore_cb, NULL); + } + + /* override the default ESP path */ + if (esp_path != NULL) { + g_autoptr(FuVolume) volume = NULL; + volume = fu_volume_new_esp_for_path(esp_path, &error); + if (volume == NULL) { + /* TRANSLATORS: ESP is EFI System Partition */ + g_print("%s: %s\n", _("ESP specified was not valid"), error->message); + return EXIT_FAILURE; + } + fu_context_add_esp_volume(ctx, volume); + } + + /* list contents, either of the existing system, or an update */ + if (action_list || action_version) { + guint cnt = 1; + g_autoptr(FuFirmware) dbx = NULL; + g_autoptr(GPtrArray) sigs = NULL; + if (dbxfile != NULL) { + dbx = fu_dbxtool_get_siglist_local(dbxfile, &error); + if (dbx == NULL) { + g_printerr("%s: %s\n", + /* TRANSLATORS: could not read existing system data */ + _("Failed to load local dbx"), + error->message); + return EXIT_FAILURE; + } + } else { + dbx = fu_dbxtool_get_siglist_system(&error); + if (dbx == NULL) { + g_printerr("%s: %s\n", + /* TRANSLATORS: could not read existing system data */ + _("Failed to load system dbx"), + error->message); + return EXIT_FAILURE; + } + } + if (action_version) { + /* TRANSLATORS: the detected version number of the dbx */ + g_print("%s: %s\n", _("Version"), fu_firmware_get_version(dbx)); + return EXIT_SUCCESS; + } + sigs = fu_firmware_get_images(FU_FIRMWARE(dbx)); + for (guint i = 0; i < sigs->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i); + g_autofree gchar *checksum = NULL; + checksum = + fu_firmware_get_checksum(FU_FIRMWARE(sig), G_CHECKSUM_SHA256, NULL); + g_print("%4u: {%s} {%s} %s\n", + cnt++, + fu_dbxtool_guid_to_string(fu_efi_signature_get_owner(sig)), + fu_efi_signature_kind_to_string(fu_efi_signature_get_kind(sig)), + checksum); + } + g_debug("version: %s", fu_firmware_get_version(FU_FIRMWARE(dbx))); + return EXIT_SUCCESS; + } + +#ifdef HAVE_GETUID + /* 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")); + } +#endif + + /* apply update */ + if (action_apply) { + g_autoptr(FuFirmware) dbx_system = NULL; + g_autoptr(FuFirmware) dbx_update = fu_efi_signature_list_new(); + g_autoptr(GBytes) blob = NULL; + + if (dbxfile == NULL) { + /* TRANSLATORS: user did not include a filename parameter */ + g_printerr("%s\n", _("Filename required")); + return EXIT_FAILURE; + } + + /* TRANSLATORS: reading existing dbx from the system */ + g_print("%s\n", _("Parsing system dbx…")); + dbx_system = fu_dbxtool_get_siglist_system(&error); + if (dbx_system == NULL) { + /* TRANSLATORS: could not read existing system data */ + g_printerr("%s: %s\n", _("Failed to load system dbx"), error->message); + return EXIT_FAILURE; + } + + /* TRANSLATORS: reading new dbx from the update */ + g_print("%s\n", _("Parsing dbx update…")); + blob = fu_bytes_get_contents(dbxfile, &error); + if (blob == NULL) { + /* TRANSLATORS: could not read file */ + g_printerr("%s: %s\n", _("Failed to load local dbx"), error->message); + return EXIT_FAILURE; + } + if (!fu_firmware_parse(dbx_update, blob, FWUPD_INSTALL_FLAG_NONE, &error)) { + /* TRANSLATORS: could not parse file */ + g_printerr("%s: %s\n", _("Failed to parse local dbx"), error->message); + return EXIT_FAILURE; + } + + /* check this is a newer dbx update */ + if (!force && fu_dbxtool_siglist_inclusive(dbx_system, dbx_update)) { + g_printerr("%s\n", + /* TRANSLATORS: same or newer update already applied */ + _("Cannot apply as dbx update has already been applied.")); + return EXIT_FAILURE; + } + + /* check if on live media */ + if (fu_common_is_live_media() && !force) { + /* TRANSLATORS: the user is using a LiveCD or LiveUSB install disk */ + g_printerr("%s\n", _("Cannot apply updates on live media")); + return EXIT_FAILURE; + } + + /* validate this is safe to apply */ + if (!force) { + /* TRANSLATORS: ESP refers to the EFI System Partition */ + g_print("%s\n", _("Validating ESP contents…")); + if (!fu_uefi_dbx_signature_list_validate(ctx, + FU_EFI_SIGNATURE_LIST(dbx_update), + &error)) { + g_printerr("%s: %s\n", + /* TRANSLATORS: something with a blocked hash exists + * in the users ESP -- which would be bad! */ + _("Failed to validate ESP contents"), + error->message); + return EXIT_FAILURE; + } + } + + /* TRANSLATORS: actually sending the update to the hardware */ + g_print("%s\n", _("Applying update…")); + if (!fu_efivar_set_data_bytes( + FU_EFIVAR_GUID_SECURITY_DATABASE, + "dbx", + blob, + FU_EFIVAR_ATTR_APPEND_WRITE | + FU_EFIVAR_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS | FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_NON_VOLATILE, + &error)) { + /* TRANSLATORS: dbx file failed to be applied as an update */ + g_printerr("%s: %s\n", _("Failed to apply update"), error->message); + return EXIT_FAILURE; + } + + /* TRANSLATORS: success */ + g_print("%s\n", _("Done!")); + return EXIT_SUCCESS; + } + + /* nothing specified */ + tmp = g_option_context_get_help(context, TRUE, NULL); + /* TRANSLATORS: user did not tell the tool what to do */ + g_printerr("%s\n\n%s", _("No action specified!"), tmp); + return EXIT_FAILURE; +} diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-efi-image.c b/fwupd-1.8.6/plugins/uefi-dbx/fu-efi-image.c new file mode 100644 index 0000000000000000000000000000000000000000..35562dacab2218d6569d1c2789a0f9df7033fc57 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-efi-image.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-efi-image.h" + +struct _FuEfiImage { + GObject parent_instance; + gchar *checksum; +}; + +typedef struct { + gsize offset; + gsize size; + gchar *name; +} FuEfiImageRegion; + +typedef struct __attribute__((packed)) { + guint32 addr; + guint32 size; +} FuEfiImageDataDirEntry; + +G_DEFINE_TYPE(FuEfiImage, fu_efi_image, G_TYPE_OBJECT) + +#define _DOS_OFFSET_SIGNATURE 0x00 +#define _DOS_OFFSET_TO_PE_HEADER 0x3c + +#define _PEI_OFFSET_SIGNATURE 0x00 +#define _PEI_OFFSET_MACHINE 0x04 +#define _PEI_OFFSET_NUMBER_OF_SECTIONS 0x06 +#define _PEI_OFFSET_OPTIONAL_HEADER_SIZE 0x14 +#define _PEI_HEADER_SIZE 0x18 + +#define _PE_OFFSET_SIZE_OF_HEADERS 0x54 +#define _PE_OFFSET_CHECKSUM 0x58 +#define _PE_OFFSET_DEBUG_TABLE_OFFSET 0x98 + +#define _PEP_OFFSET_SIZE_OF_HEADERS 0x54 +#define _PEP_OFFSET_CHECKSUM 0x58 +#define _PEP_OFFSET_DEBUG_TABLE_OFFSET 0xa8 + +#define _SECTION_HEADER_OFFSET_NAME 0x0 +#define _SECTION_HEADER_OFFSET_SIZE 0x10 +#define _SECTION_HEADER_OFFSET_PTR 0x14 +#define _SECTION_HEADER_SIZE 0x28 + +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_AARCH64 0xaa64 + +static gint +fu_efi_image_region_sort_cb(gconstpointer a, gconstpointer b) +{ + const FuEfiImageRegion *r1 = *((const FuEfiImageRegion **)a); + const FuEfiImageRegion *r2 = *((const FuEfiImageRegion **)b); + if (r1->offset < r2->offset) + return -1; + if (r1->offset > r2->offset) + return 1; + return 0; +} + +static FuEfiImageRegion * +fu_efi_image_add_region(GPtrArray *checksum_regions, + const gchar *name, + gsize offset_start, + gsize offset_end) +{ + FuEfiImageRegion *r = g_new0(FuEfiImageRegion, 1); + r->name = g_strdup(name); + r->offset = offset_start; + r->size = offset_end - offset_start; + g_ptr_array_add(checksum_regions, r); + return r; +} + +static void +fu_efi_image_region_free(FuEfiImageRegion *r) +{ + g_free(r->name); + g_free(r); +} + +FuEfiImage * +fu_efi_image_new(GBytes *data, GError **error) +{ + FuEfiImageRegion *r; + const guint8 *buf; + gsize bufsz; + gsize image_bytes = 0; + gsize checksum_offset; + gsize data_dir_debug_offset; + gsize offset_tmp; + guint16 dos_sig = 0; + guint16 machine = 0; + guint16 opthdrsz; + guint16 sections; + guint32 baseaddr = 0; + guint32 cert_table_size; + guint32 header_size; + guint32 nt_sig = 0; + g_autoptr(FuEfiImage) self = g_object_new(FU_TYPE_EFI_IMAGE, NULL); + g_autoptr(GChecksum) checksum = g_checksum_new(G_CHECKSUM_SHA256); + g_autoptr(GPtrArray) checksum_regions = NULL; + + /* verify this is a DOS file */ + buf = fu_bytes_get_data_safe(data, &bufsz, error); + if (buf == NULL) + return NULL; + if (!fu_memread_uint16_safe(buf, + bufsz, + _DOS_OFFSET_SIGNATURE, + &dos_sig, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (dos_sig != 0x5a4d) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Invalid DOS header magic %04x", + dos_sig); + return NULL; + } + + /* verify the PE signature */ + if (!fu_memread_uint32_safe(buf, + bufsz, + _DOS_OFFSET_TO_PE_HEADER, + &baseaddr, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + baseaddr + _PEI_OFFSET_SIGNATURE, + &nt_sig, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (nt_sig != 0x4550) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Invalid PE header signature %08x", + nt_sig); + return NULL; + } + + /* which machine type are we reading */ + if (!fu_memread_uint16_safe(buf, + bufsz, + baseaddr + _PEI_OFFSET_MACHINE, + &machine, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (machine == IMAGE_FILE_MACHINE_AMD64 || machine == IMAGE_FILE_MACHINE_AARCH64) { + /* a.out header directly follows PE header */ + if (!fu_memread_uint16_safe(buf, + bufsz, + baseaddr + _PEI_HEADER_SIZE, + &machine, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (machine != 0x020b) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Invalid a.out machine type %04x", + machine); + return NULL; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + baseaddr + _PEP_OFFSET_SIZE_OF_HEADERS, + &header_size, + G_LITTLE_ENDIAN, + error)) + return NULL; + + checksum_offset = baseaddr + _PEP_OFFSET_CHECKSUM; + + /* now, this is odd. sbsigntools seems to think that we're + * skipping the CertificateTable -- but we actually seems to be + * ignoring Debug instead */ + data_dir_debug_offset = baseaddr + _PEP_OFFSET_DEBUG_TABLE_OFFSET; + + } else if (machine == IMAGE_FILE_MACHINE_I386 || machine == IMAGE_FILE_MACHINE_THUMB) { + /* a.out header directly follows PE header */ + if (!fu_memread_uint16_safe(buf, + bufsz, + baseaddr + _PEI_HEADER_SIZE, + &machine, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (machine != 0x010b) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Invalid a.out machine type %04x", + machine); + return NULL; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + baseaddr + _PE_OFFSET_SIZE_OF_HEADERS, + &header_size, + G_LITTLE_ENDIAN, + error)) + return NULL; + + checksum_offset = baseaddr + _PE_OFFSET_CHECKSUM; + data_dir_debug_offset = baseaddr + _PE_OFFSET_DEBUG_TABLE_OFFSET; + + } else { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Invalid PE header machine %04x", + machine); + return NULL; + } + + /* get sections */ + if (!fu_memread_uint32_safe(buf, + bufsz, + data_dir_debug_offset + sizeof(guint32), + &cert_table_size, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint16_safe(buf, + bufsz, + baseaddr + _PEI_OFFSET_NUMBER_OF_SECTIONS, + §ions, + G_LITTLE_ENDIAN, + error)) + return NULL; + g_debug("number_of_sections: %u", sections); + + /* get header size */ + if (!fu_memread_uint16_safe(buf, + bufsz, + baseaddr + _PEI_OFFSET_OPTIONAL_HEADER_SIZE, + &opthdrsz, + G_LITTLE_ENDIAN, + error)) + return NULL; + g_debug("optional_header_size: 0x%x", opthdrsz); + + /* first region: beginning to checksum_offset field */ + checksum_regions = g_ptr_array_new_with_free_func((GDestroyNotify)fu_efi_image_region_free); + r = fu_efi_image_add_region(checksum_regions, "begin->cksum", 0x0, checksum_offset); + image_bytes += r->size + sizeof(guint32); + + /* second region: end of checksum_offset to certificate table entry */ + r = fu_efi_image_add_region(checksum_regions, + "cksum->datadir[DEBUG]", + checksum_offset + sizeof(guint32), + data_dir_debug_offset); + image_bytes += r->size + sizeof(FuEfiImageDataDirEntry); + + /* third region: end of checksum_offset to end of headers */ + r = fu_efi_image_add_region(checksum_regions, + "datadir[DEBUG]->headers", + data_dir_debug_offset + sizeof(FuEfiImageDataDirEntry), + header_size); + image_bytes += r->size; + + /* add COFF sections */ + offset_tmp = baseaddr + _PEI_HEADER_SIZE + opthdrsz; + for (guint i = 0; i < sections; i++) { + guint32 file_offset = 0; + guint32 file_size = 0; + gchar name[9] = {'\0'}; + + if (!fu_memread_uint32_safe(buf, + bufsz, + offset_tmp + _SECTION_HEADER_OFFSET_PTR, + &file_offset, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (!fu_memread_uint32_safe(buf, + bufsz, + offset_tmp + _SECTION_HEADER_OFFSET_SIZE, + &file_size, + G_LITTLE_ENDIAN, + error)) + return NULL; + if (file_size == 0) + continue; + if (!fu_memcpy_safe((guint8 *)name, + sizeof(name), + 0x0, /* dst */ + buf, + bufsz, + offset_tmp + _SECTION_HEADER_OFFSET_NAME, /* src */ + sizeof(name) - 1, + error)) + return NULL; + r = fu_efi_image_add_region(checksum_regions, + name, + file_offset, + file_offset + file_size); + image_bytes += r->size; + + if (file_offset + r->size > bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file-aligned section %s extends beyond end of file", + r->name); + return NULL; + } + offset_tmp += _SECTION_HEADER_SIZE; + } + + /* make sure in order */ + g_ptr_array_sort(checksum_regions, fu_efi_image_region_sort_cb); + + /* for the data at the end of the image */ + if (image_bytes + cert_table_size < bufsz) { + fu_efi_image_add_region(checksum_regions, + "endjunk", + image_bytes, + bufsz - cert_table_size); + } else if (image_bytes + cert_table_size > bufsz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum_offset areas outside image size"); + return NULL; + } + + /* calculate the checksum we would find in the dbx */ + for (guint i = 0; i < checksum_regions->len; i++) { + r = g_ptr_array_index(checksum_regions, i); + g_debug("region %s: 0x%04x -> 0x%04x [0x%04x]", + r->name, + (guint)r->offset, + (guint)(r->offset + r->size - 1), + (guint)r->size); + g_checksum_update(checksum, (const guchar *)buf + r->offset, (gssize)r->size); + } + self->checksum = g_strdup(g_checksum_get_string(checksum)); + return g_steal_pointer(&self); +} + +const gchar * +fu_efi_image_get_checksum(FuEfiImage *self) +{ + return self->checksum; +} + +static void +fu_efi_image_finalize(GObject *obj) +{ + FuEfiImage *self = FU_EFI_IMAGE(obj); + g_free(self->checksum); + G_OBJECT_CLASS(fu_efi_image_parent_class)->finalize(obj); +} + +static void +fu_efi_image_class_init(FuEfiImageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_efi_image_finalize; +} + +static void +fu_efi_image_init(FuEfiImage *self) +{ +} diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-efi-image.h b/fwupd-1.8.6/plugins/uefi-dbx/fu-efi-image.h new file mode 100644 index 0000000000000000000000000000000000000000..3d35088af7a01715a0c0a70cb71b939da0227430 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-efi-image.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_EFI_IMAGE (fu_efi_image_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiImage, fu_efi_image, FU, EFI_IMAGE, GObject) + +FuEfiImage * +fu_efi_image_new(GBytes *data, GError **error); +const gchar * +fu_efi_image_get_checksum(FuEfiImage *self); diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-self-test.c b/fwupd-1.8.6/plugins/uefi-dbx/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..ae7f7f898e228757a365beac040b494f02e50eb9 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-self-test.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-efi-image.h" +#include "fu-uefi-dbx-common.h" + +static void +fu_efi_image_func(void) +{ + const gchar *ci = g_getenv("CI_NETWORK"); + const gchar *csum = NULL; + g_autofree gchar *fn = NULL; + g_autoptr(FuEfiImage) img = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + + fn = g_test_build_filename(G_TEST_DIST, "tests", "fwupdx64.efi", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS) && ci == NULL) { + g_test_skip("Missing fwupdx64.efi"); + return; + } + g_assert_nonnull(fn); + bytes = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(bytes); + + img = fu_efi_image_new(bytes, &error); + g_assert_no_error(error); + g_assert_nonnull(img); + csum = fu_efi_image_get_checksum(img); + g_assert_cmpstr(csum, + ==, + "e99707d4378140c01eb3f867240d5cc9e237b126d3db0c3b4bbcd3da1720ddff"); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/uefi-dbx/image", fu_efi_image_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-common.c b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-common.c new file mode 100644 index 0000000000000000000000000000000000000000..b7f291439e26a1d2013605a12ec43120218ea3d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-common.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-efi-image.h" +#include "fu-uefi-dbx-common.h" + +gchar * +fu_uefi_dbx_get_authenticode_hash(const gchar *fn, GError **error) +{ + g_autoptr(FuEfiImage) img = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GMappedFile) mmap = NULL; + + g_debug("getting Authenticode hash of %s", fn); + mmap = g_mapped_file_new(fn, FALSE, error); + if (mmap == NULL) + return NULL; + bytes = g_mapped_file_get_bytes(mmap); + + img = fu_efi_image_new(bytes, error); + if (img == NULL) + return NULL; + g_debug("SHA256 was %s", fu_efi_image_get_checksum(img)); + return g_strdup(fu_efi_image_get_checksum(img)); +} + +static gboolean +fu_uefi_dbx_signature_list_validate_volume(FuEfiSignatureList *siglist, + FuVolume *esp, + GError **error) +{ + g_autofree gchar *esp_path = NULL; + g_autoptr(GPtrArray) files = NULL; + + /* get list of files contained in the ESP */ + esp_path = fu_volume_get_mount_point(esp); + if (esp_path == NULL) + return TRUE; + files = fu_path_get_files(esp_path, error); + if (files == NULL) + return FALSE; + + /* verify each file does not exist in the ESP */ + for (guint i = 0; i < files->len; i++) { + const gchar *fn = g_ptr_array_index(files, i); + g_autofree gchar *checksum = NULL; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GError) error_local = NULL; + + /* get checksum of file */ + checksum = fu_uefi_dbx_get_authenticode_hash(fn, &error_local); + if (checksum == NULL) { + g_debug("failed to get checksum for %s: %s", fn, error_local->message); + continue; + } + + /* Authenticode signature is present in dbx! */ + g_debug("fn=%s, checksum=%s", fn, checksum); + img = fu_firmware_get_image_by_checksum(FU_FIRMWARE(siglist), checksum, NULL); + if (img != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NEEDS_USER_ACTION, + "%s Authenticode checksum [%s] is present in dbx", + fn, + checksum); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +gboolean +fu_uefi_dbx_signature_list_validate(FuContext *ctx, FuEfiSignatureList *siglist, GError **error) +{ + g_autoptr(GPtrArray) volumes = NULL; + volumes = fu_context_get_esp_volumes(ctx, error); + if (volumes == NULL) + return FALSE; + for (guint i = 0; i < volumes->len; i++) { + FuVolume *esp = g_ptr_array_index(volumes, i); + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_volume_locker(esp, error); + if (locker == NULL) + return FALSE; + if (!fu_uefi_dbx_signature_list_validate_volume(siglist, esp, error)) + return FALSE; + } + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-common.h b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-common.h new file mode 100644 index 0000000000000000000000000000000000000000..af7390dd2890b0258788c04c7c46e96579554352 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-common.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gchar * +fu_uefi_dbx_get_authenticode_hash(const gchar *fn, GError **error); +gboolean +fu_uefi_dbx_signature_list_validate(FuContext *ctx, FuEfiSignatureList *siglist, GError **error); diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-device.c b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-device.c new file mode 100644 index 0000000000000000000000000000000000000000..e941e0909950e0acdff7f5b5636f6307e9c41152 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-device.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uefi-dbx-common.h" +#include "fu-uefi-dbx-device.h" + +struct _FuUefiDbxDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUefiDbxDevice, fu_uefi_dbx_device, FU_TYPE_DEVICE) + +static gboolean +fu_uefi_dbx_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags install_flags, + GError **error) +{ + const guint8 *buf; + gsize bufsz = 0; + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* write entire chunk to efivarfs */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_efivar_set_data(FU_EFIVAR_GUID_SECURITY_DATABASE, + "dbx", + buf, + bufsz, + FU_EFIVAR_ATTR_APPEND_WRITE | + FU_EFIVAR_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | FU_EFIVAR_ATTR_NON_VOLATILE, + error)) { + return FALSE; + } + + /* success! */ + return TRUE; +} + +static gboolean +fu_uefi_dbx_device_set_version_number(FuDevice *device, GError **error) +{ + g_autoptr(GBytes) dbx_blob = NULL; + g_autoptr(FuFirmware) dbx = fu_efi_signature_list_new(); + + /* use the number of checksums in the dbx as a version number, ignoring + * some owners that do not make sense */ + dbx_blob = fu_efivar_get_data_bytes(FU_EFIVAR_GUID_SECURITY_DATABASE, "dbx", NULL, error); + if (dbx_blob == NULL) + return FALSE; + if (!fu_firmware_parse(dbx, dbx_blob, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + return FALSE; + fu_device_set_version(device, fu_firmware_get_version(dbx)); + fu_device_set_version_lowest(device, fu_firmware_get_version(dbx)); + return TRUE; +} + +static FuFirmware * +fu_uefi_dbx_prepare_firmware(FuDevice *device, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + FuContext *ctx = fu_device_get_context(device); + g_autoptr(FuFirmware) siglist = fu_efi_signature_list_new(); + + /* parse dbx */ + if (!fu_firmware_parse(siglist, fw, flags, error)) + return NULL; + + /* validate this is safe to apply */ + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + // fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_VERIFY); + if (!fu_uefi_dbx_signature_list_validate(ctx, + FU_EFI_SIGNATURE_LIST(siglist), + error)) { + g_prefix_error(error, + "Blocked executable in the ESP, " + "ensure grub and shim are up to date: "); + return NULL; + } + } + + /* default blob */ + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_uefi_dbx_device_probe(FuDevice *device, GError **error) +{ + g_autoptr(FuFirmware) kek = fu_efi_signature_list_new(); + g_autoptr(GBytes) kek_blob = NULL; + g_autoptr(GPtrArray) sigs = NULL; + + /* use each of the certificates in the KEK to generate the GUIDs */ + kek_blob = fu_efivar_get_data_bytes(FU_EFIVAR_GUID_EFI_GLOBAL, "KEK", NULL, error); + if (kek_blob == NULL) + return FALSE; + if (!fu_firmware_parse(kek, kek_blob, FWUPD_INSTALL_FLAG_NONE, error)) + return FALSE; + fu_device_add_instance_strup(device, "ARCH", EFI_MACHINE_TYPE_NAME); + + sigs = fu_firmware_get_images(kek); + for (guint j = 0; j < sigs->len; j++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, j); + g_autofree gchar *checksum = NULL; + + checksum = fu_firmware_get_checksum(FU_FIRMWARE(sig), G_CHECKSUM_SHA256, error); + if (checksum == NULL) + return FALSE; + fu_device_add_instance_strup(device, "CRT", checksum); + fu_device_build_instance_id(device, NULL, "UEFI", "CRT", NULL); + fu_device_build_instance_id(device, NULL, "UEFI", "CRT", "ARCH", NULL); + } + return fu_uefi_dbx_device_set_version_number(device, error); +} + +static void +fu_uefi_dbx_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_uefi_dbx_device_init(FuUefiDbxDevice *self) +{ + fu_device_set_physical_id(FU_DEVICE(self), "dbx"); + fu_device_set_name(FU_DEVICE(self), "UEFI dbx"); + fu_device_set_summary(FU_DEVICE(self), "UEFI revocation database"); + fu_device_add_vendor_id(FU_DEVICE(self), "UEFI:Linux Foundation"); + fu_device_add_protocol(FU_DEVICE(self), "org.uefi.dbx"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_install_duration(FU_DEVICE(self), 1); + fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); + if (!fu_common_is_live_media()) + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_uefi_dbx_device_class_init(FuUefiDbxDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_uefi_dbx_device_probe; + klass_device->write_firmware = fu_uefi_dbx_device_write_firmware; + klass_device->prepare_firmware = fu_uefi_dbx_prepare_firmware; + klass_device->set_progress = fu_uefi_dbx_device_set_progress; +} + +FuUefiDbxDevice * +fu_uefi_dbx_device_new(FuContext *ctx) +{ + FuUefiDbxDevice *self; + self = g_object_new(FU_TYPE_UEFI_DBX_DEVICE, "context", ctx, NULL); + return self; +} diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-device.h b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-device.h new file mode 100644 index 0000000000000000000000000000000000000000..24e1757bfa79151e1e3d6e5828d510034a03187b --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-device.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_UEFI_DBX_DEVICE (fu_uefi_dbx_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiDbxDevice, fu_uefi_dbx_device, FU, UEFI_DBX_DEVICE, FuDevice) + +FuUefiDbxDevice * +fu_uefi_dbx_device_new(FuContext *ctx); diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-plugin.c b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..cb1fdec3bc36df24bd985603e599ff67a966de81 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-plugin.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-uefi-dbx-device.h" +#include "fu-uefi-dbx-plugin.h" + +struct _FuUefiDbxPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuUefiDbxPlugin, fu_uefi_dbx_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_uefi_dbx_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(FuUefiDbxDevice) device = fu_uefi_dbx_device_new(ctx); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "probe"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "setup"); + + if (!fu_device_probe(FU_DEVICE(device), error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_device_setup(FU_DEVICE(device), error)) + return FALSE; + fu_progress_step_done(progress); + + if (fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "no-dbx-updates")) { + fu_device_inhibit(FU_DEVICE(device), + "no-dbx", + "System firmware cannot accept DBX updates"); + } + fu_plugin_device_add(plugin, FU_DEVICE(device)); + return TRUE; +} + +static void +fu_uefi_dbx_plugin_init(FuUefiDbxPlugin *self) +{ +} + +static void +fu_uefi_dbx_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "uefi_capsule"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_EFI_SIGNATURE_LIST); +} + +static void +fu_uefi_dbx_plugin_class_init(FuUefiDbxPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_uefi_dbx_plugin_constructed; + plugin_class->coldplug = fu_uefi_dbx_plugin_coldplug; +} diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-plugin.h b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..793f7bae6a43d73e4d45d93ae17ed98ce1736fb0 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/fu-uefi-dbx-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUefiDbxPlugin, fu_uefi_dbx_plugin, FU, UEFI_DBX_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/uefi-dbx/fuzzing/example.bin b/fwupd-1.8.6/plugins/uefi-dbx/fuzzing/example.bin new file mode 100644 index 0000000000000000000000000000000000000000..5cfda85fe9a60a73f250736ebe6ab54586d7822b Binary files /dev/null and b/fwupd-1.8.6/plugins/uefi-dbx/fuzzing/example.bin differ diff --git a/fwupd-1.8.6/plugins/uefi-dbx/meson.build b/fwupd-1.8.6/plugins/uefi-dbx/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..60ae5a41b484d2408227fba1358a915d45ffa0b7 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/meson.build @@ -0,0 +1,68 @@ +if efiboot.found() and efivar.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginUefiDbx"'] + +plugin_quirks += files('uefi-dbx.quirk') +plugin_builtin_uefi_dbx = static_library('fu_plugin_uefi_dbx', + sources: [ + 'fu-uefi-dbx-plugin.c', + 'fu-uefi-dbx-common.c', + 'fu-uefi-dbx-device.c', + 'fu-efi-image.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_uefi_dbx + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'uefi-dbx-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_uefi_dbx, + ], + c_args: cargs, + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('uefi-dbx-self-test', e, env: env) # added to installed-tests +endif + +dbxtool = executable( + 'dbxtool', + sources: [ + 'fu-dbxtool.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_uefi_dbx, + ], + install: true, + install_dir: bindir, + install_rpath: libdir_pkg, + c_args: cargs, +) + +if get_option('man') + configure_file( + input: 'dbxtool.1', + output: 'dbxtool.1', + configuration: conf, + install: true, + install_dir: join_paths(mandir, 'man1'), + ) +endif +endif diff --git a/fwupd-1.8.6/plugins/uefi-dbx/uefi-dbx.quirk b/fwupd-1.8.6/plugins/uefi-dbx/uefi-dbx.quirk new file mode 100644 index 0000000000000000000000000000000000000000..648b8e4cd78cea4a5361138e5efe14cfd216e4c1 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-dbx/uefi-dbx.quirk @@ -0,0 +1,117 @@ +# Manufacturer=Apple Inc. +[80f95c96-a739-5ef5-8482-3d65cb39ff55] +Flags = no-dbx-updates + +# Manufacturer=FUJITSU +# BaseboardManufacturer=FUJITSU +# BaseboardProduct=FJNBB38 +[71c3a6cd-3e9a-5b49-a4eb-0e2a57dd265b] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=83D5 +[35ed19f7-015a-5da8-adf7-b31dc515c23a] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=83DA +[a8cd7a16-e01e-5cdd-a098-cd5a29868d4c] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=83DD +[580e5a81-1979-5e29-8d16-bb1ddcc43e87] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=83E7 +[c7211192-6168-530e-b42d-650b9f091c7a] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=83E8 +[624f1327-d66b-5f20-865f-9a978dc940ac] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=83E9 +[af7be2cf-a1c2-50ce-b880-c090ee252249] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8401 +[48bd5791-ceaa-5f79-b8e6-c45bdf956c1e] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8460 +[7bf700eb-c41b-5898-9e09-5a26ca10c14e] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8461 +[c9f16517-3849-50cf-8b8a-48773695bfe4] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8462 +[d605b1fa-0eb3-5bd4-9ab4-2c65d7fc2a8b] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8463 +[7e2cbc58-92cf-5504-a1d4-b4f85de526a1] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8464 +[35ccf8b1-7f97-5f84-b3cb-8d343d2b595c] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8584 +[49549ee6-fa8e-5bb7-a346-9e93d2652b5d] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8589 +[23bfe0fb-be17-55a2-86e0-7970254b357c] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8617 +[cb686451-b325-57e4-a4fc-84bddb2ee470] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8618 +[2edc773c-03f9-5d72-966d-5f612f4cf127] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8619 +[2cc829ba-013c-5843-9b60-406e1ac6e106] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8620 +[3f4f41b0-f419-5715-879e-73500cdb3c5d] +Flags = no-dbx-updates diff --git a/fwupd-1.8.6/plugins/uefi-pk/README.md b/fwupd-1.8.6/plugins/uefi-pk/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8dc93566ca693afcd6ac4a0dc92316320dd58769 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-pk/README.md @@ -0,0 +1,16 @@ +# UEFI PK + +## Introduction + +The platform key (PK) specifies the machine owner, typically the OEM +that created the laptop or desktop. + +Several device manufacturers decide to ship the default "AMI Test PK" +platform key instead of a Device Manufacturer specific one. This will +cause an HSI-1 failure. + +## External Interface Access + +This plugin requires: + +* read access to `/sys/firmware/efi/efivars` diff --git a/fwupd-1.8.6/plugins/uefi-pk/fu-uefi-pk-plugin.c b/fwupd-1.8.6/plugins/uefi-pk/fu-uefi-pk-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..2fe50cdbec6e49532e19330577275aa6bb1673bc --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-pk/fu-uefi-pk-plugin.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-uefi-pk-plugin.h" + +struct _FuUefiPkPlugin { + FuPlugin parent_instance; + gboolean has_pk_test_key; +}; + +G_DEFINE_TYPE(FuUefiPkPlugin, fu_uefi_pk_plugin, FU_TYPE_PLUGIN) + +static void +fu_uefi_pk_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuUefiPkPlugin *self = FU_UEFI_PK_PLUGIN(plugin); + fu_string_append_kb(str, idt, "HasPkTestKey", self->has_pk_test_key); +} + +#define FU_UEFI_PK_CHECKSUM_AMI_TEST_KEY "a773113bafaf5129aa83fd0912e95da4fa555f91" + +static void +_gnutls_datum_deinit(gnutls_datum_t *d) +{ + gnutls_free(d->data); + gnutls_free(d); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_datum_t, _gnutls_datum_deinit) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL) +#pragma clang diagnostic pop + +static gboolean +fu_uefi_pk_plugin_parse_buf(FuPlugin *plugin, const gchar *buf, gsize bufsz, GError **error) +{ + FuUefiPkPlugin *self = FU_UEFI_PK_PLUGIN(plugin); + const gchar *needles[] = { + "DO NOT TRUST", + "DO NOT SHIP", + NULL, + }; + for (guint i = 0; needles[i] != NULL; i++) { + if (g_strstr_len(buf, bufsz, needles[i]) != NULL) { + g_debug("got %s, marking unsafe", buf); + self->has_pk_test_key = TRUE; + break; + } + } + return TRUE; +} + +static gboolean +fu_uefi_pk_plugin_parse_signature(FuPlugin *plugin, FuEfiSignature *sig, GError **error) +{ + gchar buf[1024] = {'\0'}; + gnutls_datum_t d = {0}; + gnutls_x509_dn_t dn = {0x0}; + gsize bufsz = sizeof(buf); + int rc; + g_auto(gnutls_x509_crt_t) crt = NULL; + g_autoptr(gnutls_datum_t) subject = NULL; + g_autoptr(GBytes) blob = NULL; + + /* create certificate */ + rc = gnutls_x509_crt_init(&crt); + if (rc < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "crt_init: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + + /* parse certificate */ + blob = fu_firmware_get_bytes(FU_FIRMWARE(sig), error); + if (blob == NULL) + return FALSE; + d.size = g_bytes_get_size(blob); + d.data = (unsigned char *)g_bytes_get_data(blob, NULL); + rc = gnutls_x509_crt_import(crt, &d, GNUTLS_X509_FMT_DER); + if (rc < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "crt_import: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + + /* look in issuer */ + if (gnutls_x509_crt_get_issuer_dn(crt, buf, &bufsz) == GNUTLS_E_SUCCESS) { + if (g_getenv("FWUPD_UEFI_PK_VERBOSE") != NULL) + g_debug("PK issuer: %s", buf); + if (!fu_uefi_pk_plugin_parse_buf(plugin, buf, bufsz, error)) + return FALSE; + } + + /* look in subject */ + subject = (gnutls_datum_t *)gnutls_malloc(sizeof(gnutls_datum_t)); + if (gnutls_x509_crt_get_subject(crt, &dn) == GNUTLS_E_SUCCESS) { + gnutls_x509_dn_get_str(dn, subject); + if (g_getenv("FWUPD_UEFI_PK_VERBOSE") != NULL) + g_debug("PK subject: %s", subject->data); + if (!fu_uefi_pk_plugin_parse_buf(plugin, + (const gchar *)subject->data, + subject->size, + error)) + return FALSE; + } + + /* success, certificate was parsed correctly */ + return TRUE; +} + +static gboolean +fu_uefi_pk_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuUefiPkPlugin *self = FU_UEFI_PK_PLUGIN(plugin); + g_autoptr(FuFirmware) img = NULL; + g_autoptr(FuFirmware) pk = fu_efi_signature_list_new(); + g_autoptr(GBytes) pk_blob = NULL; + g_autoptr(GPtrArray) sigs = NULL; + + pk_blob = fu_efivar_get_data_bytes(FU_EFIVAR_GUID_EFI_GLOBAL, "PK", NULL, error); + if (pk_blob == NULL) + return FALSE; + if (!fu_firmware_parse(pk, pk_blob, FU_FIRMWARE_FLAG_NONE, error)) { + g_prefix_error(error, "failed to parse PK: "); + return FALSE; + } + + /* by checksum */ + img = fu_firmware_get_image_by_checksum(pk, FU_UEFI_PK_CHECKSUM_AMI_TEST_KEY, NULL); + if (img != NULL) + self->has_pk_test_key = TRUE; + + /* by text */ + sigs = fu_firmware_get_images(pk); + for (guint i = 0; i < sigs->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i); + if (!fu_uefi_pk_plugin_parse_signature(plugin, sig, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_uefi_pk_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + if (fu_device_has_instance_id(device, "main-system-firmware")) + fu_plugin_cache_add(plugin, "main-system-firmware", device); +} + +static void +fu_uefi_pk_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) +{ + FuUefiPkPlugin *self = FU_UEFI_PK_PLUGIN(plugin); + FuDevice *msf_device = fu_plugin_cache_lookup(plugin, "main-system-firmware"); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = fu_plugin_security_attr_new(plugin, FWUPD_SECURITY_ATTR_ID_UEFI_PK); + if (msf_device != NULL) + fwupd_security_attr_add_guids(attr, fu_device_get_guids(msf_device)); + fu_security_attrs_append(attrs, attr); + + /* test key is not secure */ + if (self->has_pk_test_key) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); +} + +static void +fu_uefi_pk_plugin_init(FuUefiPkPlugin *self) +{ +} + +static void +fu_uefi_pk_plugin_class_init(FuUefiPkPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->to_string = fu_uefi_pk_plugin_to_string; + plugin_class->add_security_attrs = fu_uefi_pk_plugin_add_security_attrs; + plugin_class->device_registered = fu_uefi_pk_plugin_device_registered; + plugin_class->coldplug = fu_uefi_pk_plugin_coldplug; +} diff --git a/fwupd-1.8.6/plugins/uefi-pk/fu-uefi-pk-plugin.h b/fwupd-1.8.6/plugins/uefi-pk/fu-uefi-pk-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..eeb3bb0ac5d426ab0ddfcfd8929d3b170f1b9a6c --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-pk/fu-uefi-pk-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUefiPkPlugin, fu_uefi_pk_plugin, FU, UEFI_PK_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/uefi-pk/meson.build b/fwupd-1.8.6/plugins/uefi-pk/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c8dcf8e70aab58dcf5ae26c9b99bc3c288af2da0 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-pk/meson.build @@ -0,0 +1,15 @@ +if hsi and \ + get_option('plugin_uefi_pk').require(gnutls.found(), + error_message: 'gnutls is needed for plugin_uefi_pk').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginUefiPk"'] + +plugin_builtins += static_library('fu_plugin_uefi_pk', + sources: [ + 'fu-uefi-pk-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/uefi-recovery/README.md b/fwupd-1.8.6/plugins/uefi-recovery/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fa535c7e7871bf97ff0f70d2a1bc16b3aafc7935 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-recovery/README.md @@ -0,0 +1,22 @@ +# UEFI + +## Introduction + +Some devices have firmware bugs which mean they do not include a valid ESRT +table in old firmware versions. + +Create a 'fake' UEFI device with the lowest possible version so that it can be +updated to a version of firmware which does have an ESRT table. + +## GUID Generation + +All the HwId GUIDs are used for the fake UEFI device, and so should be used in +the firmware metadata for releases that should recover the system. + +## Vendor ID Security + +The vendor ID is set from the BIOS vendor, for example `DMI:LENOVO` + +## External Interface Access + +This plugin requires no extra access. diff --git a/fwupd-1.8.6/plugins/uefi-recovery/fu-uefi-recovery-plugin.c b/fwupd-1.8.6/plugins/uefi-recovery/fu-uefi-recovery-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..e994fd630f7940083812981cec521e66476d3def --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-recovery/fu-uefi-recovery-plugin.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-uefi-recovery-plugin.h" + +struct _FuUefiRecoveryPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuUefiRecoveryPlugin, fu_uefi_recovery_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_uefi_recovery_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + /* are the EFI dirs set up so we can update each device */ + return fu_efivar_supported(error); +} + +static gboolean +fu_uefi_recovery_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuContext *ctx = fu_plugin_get_context(plugin); + GPtrArray *hwids = fu_context_get_hwid_guids(ctx); + const gchar *dmi_vendor; + g_autoptr(FuDevice) device = fu_device_new(fu_plugin_get_context(plugin)); + fu_device_set_id(device, "uefi-recovery"); + fu_device_set_name(device, "System Firmware ESRT Recovery"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "0.0.0"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_set_metadata(device, FU_DEVICE_METADATA_UEFI_DEVICE_KIND, "system-firmware"); + fu_device_add_icon(device, "computer"); + for (guint i = 0; i < hwids->len; i++) { + const gchar *hwid = g_ptr_array_index(hwids, i); + fu_device_add_guid(device, hwid); + } + + /* set vendor ID as the BIOS vendor */ + dmi_vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VENDOR); + if (dmi_vendor != NULL) { + g_autofree gchar *vendor_id = g_strdup_printf("DMI:%s", dmi_vendor); + fu_device_add_vendor_id(device, vendor_id); + } + + fu_plugin_device_register(plugin, device); + return TRUE; +} + +static void +fu_uefi_recovery_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + + /* make sure that UEFI plugin is ready to receive devices */ + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_AFTER, "uefi_capsule"); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_REQUIRE_HWID); +} + +static void +fu_uefi_recovery_plugin_init(FuUefiRecoveryPlugin *self) +{ +} + +static void +fu_uefi_recovery_plugin_class_init(FuUefiRecoveryPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + object_class->constructed = fu_uefi_recovery_plugin_constructed; + plugin_class->coldplug = fu_uefi_recovery_plugin_coldplug; + plugin_class->startup = fu_uefi_recovery_plugin_startup; +} diff --git a/fwupd-1.8.6/plugins/uefi-recovery/fu-uefi-recovery-plugin.h b/fwupd-1.8.6/plugins/uefi-recovery/fu-uefi-recovery-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..dcd339f9eaa6a3bfebc300f810ca0a69480ecb7e --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-recovery/fu-uefi-recovery-plugin.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUefiRecoveryPlugin, + fu_uefi_recovery_plugin, + FU, + UEFI_RECOVERY_PLUGIN, + FuPlugin) diff --git a/fwupd-1.8.6/plugins/uefi-recovery/meson.build b/fwupd-1.8.6/plugins/uefi-recovery/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c1800b9d87d3d6ff495fbcadc56d645e55d8e6ba --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-recovery/meson.build @@ -0,0 +1,16 @@ +if get_option('plugin_uefi_capsule').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginUefiRecovery"'] + +plugin_quirks += files('uefi-recovery.quirk') +plugin_builtins += static_library('fu_plugin_uefi_recovery', + sources: [ + 'fu-uefi-recovery-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: [ + cargs, + ], + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/uefi-recovery/uefi-recovery.quirk b/fwupd-1.8.6/plugins/uefi-recovery/uefi-recovery.quirk new file mode 100644 index 0000000000000000000000000000000000000000..5f40e3e3ffb4373f1f6cf9f855cafc959a49fe20 --- /dev/null +++ b/fwupd-1.8.6/plugins/uefi-recovery/uefi-recovery.quirk @@ -0,0 +1,3 @@ +# Silicom Minnowboard Turbot MNW2MAX1.X64.0100.R01.1811141729 +[ea358e00-39f1-55b6-97be-a39225a585e1] +Plugin = uefi_recovery diff --git a/fwupd-1.8.6/plugins/uf2/README.md b/fwupd-1.8.6/plugins/uf2/README.md new file mode 100644 index 0000000000000000000000000000000000000000..35ef10e6da539dcedeecf2bd4dce2e8eec3b0de6 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/README.md @@ -0,0 +1,61 @@ +# UF2 Devices + +## Introduction + +This plugin allows the user to update any supported UF2 Device by writing +firmware onto a mass storage device. + +A UF2 device exposes a VFAT block device which has a virtual file +`INFO_UF2.TXT` where metadata can be read from. It may also have a the +current firmware exported as a file `CURRENT.UF2` which is in a 512 +byte-block UF2 format. + +Writing any file to the MSD will cause the firmware to be written. +Sometimes the device will restart and the volume will be unmounted and then +mounted again. In some cases the volume may not “come back” until the user +manually puts the device back in programming mode. + +Match the block devices using the VID, PID and UUID, and then create a +UF2 device which can be used to flash firmware. + +Note: We only read metadata from allow-listed IDs to avoid causing regressions +on non-UF2 volumes. To get the UUID you can use commands like: + + udisksctl info -b /dev/sda1 + +The UF2 format is specified [here](https://github.com/Microsoft/uf2>). + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +the UF2 file format. + +This plugin supports the following protocol ID: + +* `com.microsoft.uf2` + +## GUID Generation + +These devices use standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1234&PID_5678` +* `USB\VID_1234&PID_5678&UUID_E478-FA50` + +Additionally, the UF2 Board-ID and Family-ID may be added: + +* `UF2\BOARD_{Board-ID}` +* `UF2\FAMILY_{Family-ID}` + +## Update Behavior + +The firmware is deployed when the device is inserted, and the firmware will +typically be written as the file is copied. + +## Vendor ID Security + +The vendor ID is set from the USB vendor. + +## External Interface Access + +This plugin requires permission to mount, write a file and unmount the mass +storage device. diff --git a/fwupd-1.8.6/plugins/uf2/fu-self-test.c b/fwupd-1.8.6/plugins/uf2/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..52019044345759539d903c0bebf6a86a2396323c --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-self-test.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uf2-firmware.h" + +static void +fu_uf2_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_uf2_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_uf2_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "uf2.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + 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("/uf2/firmware{xml}", fu_uf2_firmware_xml_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/uf2/fu-uf2-device.c b/fwupd-1.8.6/plugins/uf2/fu-uf2-device.c new file mode 100644 index 0000000000000000000000000000000000000000..5f3ee176ac641dba7ea528bd0a2a98b52e64b426 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-uf2-device.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uf2-device.h" +#include "fu-uf2-firmware.h" + +struct _FuUf2Device { + FuUdevDevice parent_instance; + guint64 family_id; + FuVolume *volume; /* non-null when fwupd has mounted it privately */ +}; + +G_DEFINE_TYPE(FuUf2Device, fu_uf2_device, FU_TYPE_UDEV_DEVICE) + +static FuFirmware * +fu_uf2_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + g_autoptr(FuFirmware) firmware = fu_uf2_firmware_new(); + + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* check the family_id matches if we can read the old firmware */ + if (self->family_id > 0 && fu_firmware_get_idx(firmware) > 0 && + self->family_id != fu_firmware_get_idx(firmware)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "family ID was different, expected 0x%08x and got 0x%08x", + (guint)self->family_id, + (guint)fu_firmware_get_idx(firmware)); + return NULL; + } + + /* success: but return the raw data */ + return fu_firmware_new_from_bytes(fw); +} + +static gboolean +fu_uf2_device_probe_current_fw(FuDevice *device, GBytes *fw, GError **error) +{ + g_autofree gchar *csum_sha256 = NULL; + g_autoptr(FuFirmware) firmware = fu_uf2_firmware_new(); + g_autoptr(GBytes) fw_raw = NULL; + + /* parse to get version */ + if (!fu_firmware_parse(firmware, fw, FWUPD_INSTALL_FLAG_NONE, error)) + return FALSE; + if (fu_firmware_get_version(firmware) != NULL) + fu_device_set_version(device, fu_firmware_get_version(firmware)); + + /* add instance ID for quirks */ + if (fu_firmware_get_idx(firmware) != 0x0) { + fu_device_add_instance_u32(device, + "FAMILY", + (guint32)fu_firmware_get_idx(firmware)); + } + (void)fu_device_build_instance_id_quirk(device, NULL, "UF2", "FAMILY", NULL); + + /* add device checksum */ + fw_raw = fu_firmware_get_bytes(firmware, error); + if (fw_raw == NULL) + return FALSE; + csum_sha256 = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, fw_raw); + fu_device_add_checksum(device, csum_sha256); + + /* success */ + return TRUE; +} + +static gchar * +fu_block_device_get_full_path(FuUf2Device *self, const gchar *filename, GError **error) +{ + const gchar *devfile = fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)); + g_autoptr(FuVolume) volume = NULL; + g_autofree gchar *mount_point = NULL; + + /* sanity check */ + if (devfile == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid path: no devfile"); + return NULL; + } + + /* find volume */ + volume = fu_volume_new_by_device(devfile, error); + if (volume == NULL) + return NULL; + + /* success */ + mount_point = fu_volume_get_mount_point(volume); + return g_build_filename(mount_point, filename, NULL); +} + +static gboolean +fu_block_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + gssize wrote; + g_autofree gchar *fn = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GOutputStream) ostr = NULL; + + /* get blob */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* open file for writing; no cleverness */ + fn = fu_block_device_get_full_path(self, "FIRMWARE.UF2", error); + if (fn == NULL) + return FALSE; + file = g_file_new_for_path(fn); + ostr = G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + if (ostr == NULL) + return FALSE; + + /* write in one chunk and let the kernel do the right thing :) */ + wrote = g_output_stream_write(ostr, + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + NULL, + error); + if (wrote < 0) + return FALSE; + if ((gsize)wrote != g_bytes_get_size(fw)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "only wrote 0x%x bytes", + (guint)wrote); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_block_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + g_autofree gchar *fn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GInputStream) istr = NULL; + + /* open for reading */ + fn = fu_block_device_get_full_path(self, "CURRENT.UF2", error); + if (fn == NULL) + return NULL; + file = g_file_new_for_path(fn); + istr = G_INPUT_STREAM(g_file_read(file, NULL, error)); + if (istr == NULL) + return NULL; + + /* read all in one big chunk */ + return fu_bytes_get_contents_stream(istr, G_MAXUINT32, error); +} + +static FuFirmware * +fu_uf2_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_uf2_firmware_new(); + g_autoptr(GBytes) fw = NULL; + + fw = fu_device_dump_firmware(device, progress, error); + if (fw == NULL) + return NULL; + if (!fu_firmware_parse(firmware, fw, FWUPD_INSTALL_FLAG_NONE, error)) + return NULL; + + return g_steal_pointer(&firmware); +} + +static gboolean +fu_uf2_device_volume_mount(FuUf2Device *self, GError **error) +{ + const gchar *devfile = fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)); + + /* mount volume if required */ + self->volume = fu_volume_new_by_device(devfile, error); + if (self->volume == NULL) + return FALSE; + return fu_volume_mount(self->volume, error); +} + +static gboolean +fu_uf2_device_check_volume_mounted_cb(FuDevice *self, gpointer user_data, GError **error) +{ + const gchar *devfile = fu_udev_device_get_device_file(FU_UDEV_DEVICE(user_data)); + g_autoptr(FuVolume) volume = NULL; + + /* mount volume if required */ + volume = fu_volume_new_by_device(devfile, error); + if (volume == NULL) + return FALSE; + if (!fu_volume_is_mounted(volume)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not mounted"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uf2_device_open(FuDevice *device, GError **error) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* wait for the user session to auto-mount the volume -- ideally we want to avoid using + * fu_volume_mount() which would make the volume only accessible by the fwupd user */ + if (!fu_device_retry_full(device, + fu_uf2_device_check_volume_mounted_cb, + 20, /* count */ + 50, /* ms */ + device, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + /* maybe no session running? */ + if (!fu_uf2_device_volume_mount(self, error)) + return FALSE; + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uf2_device_close(FuDevice *device, GError **error) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + + /* we only do this when mounting for the fwupd user */ + if (self->volume != NULL) { + if (!fu_volume_unmount(self->volume, error)) + return FALSE; + g_clear_object(&self->volume); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uf2_device_setup(FuDevice *device, GError **error) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn1 = NULL; + g_autofree gchar *fn2 = NULL; + g_auto(GStrv) lines = NULL; + g_autoptr(GBytes) fw = NULL; + + /* this has to exist */ + fn1 = fu_block_device_get_full_path(self, "INFO_UF2.TXT", error); + if (fn1 == NULL) + return FALSE; + if (!g_file_get_contents(fn1, &buf, &bufsz, error)) + return FALSE; + lines = fu_strsplit(buf, bufsz, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + if (g_str_has_prefix(lines[i], "Model: ")) { + fu_device_set_name(device, lines[i] + 7); + } else if (g_str_has_prefix(lines[i], "Board-ID: ")) { + fu_device_add_instance_strsafe(device, "BOARD", lines[i] + 10); + } + } + fu_device_build_instance_id(device, NULL, "UF2", "BOARD", NULL); + + /* this might exist */ + fn2 = fu_block_device_get_full_path(self, "CURRENT.UF2", error); + fw = fu_bytes_get_contents(fn2, NULL); + if (fw != NULL) { + if (!fu_uf2_device_probe_current_fw(device, fw, error)) + return FALSE; + } else { + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uf2_device_probe(FuDevice *device, GError **error) +{ + GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(device)); + const gchar *tmp; + guint64 vid = 0; + guint64 pid = 0; + + /* check is valid */ + tmp = g_udev_device_get_property(udev_device, "ID_BUS"); + if (g_strcmp0(tmp, "usb") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct ID_BUS=%s, expected usb", + tmp); + return FALSE; + } + tmp = g_udev_device_get_property(udev_device, "ID_FS_TYPE"); + if (g_strcmp0(tmp, "vfat") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct ID_FS_TYPE=%s, expected vfat", + tmp); + return FALSE; + } + + /* set the physical ID */ + if (!fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "block", error)) + return FALSE; + + /* more instance IDs */ + tmp = g_udev_device_get_property(udev_device, "ID_VENDOR_ID"); + if (tmp != NULL) + vid = g_ascii_strtoull(tmp, NULL, 16); + if (vid != 0x0) + fu_device_add_instance_u16(device, "VID", vid); + tmp = g_udev_device_get_property(udev_device, "ID_MODEL_ID"); + if (tmp != NULL) + pid = g_ascii_strtoull(tmp, NULL, 16); + if (pid != 0x0) + fu_device_add_instance_u16(device, "PID", pid); + tmp = g_udev_device_get_property(udev_device, "ID_FS_UUID"); + fu_device_add_instance_str(device, "UUID", tmp); + if (!fu_device_build_instance_id_quirk(device, error, "USB", "VID", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "UUID", NULL)) + return FALSE; + + /* vendor-id */ + if (vid != 0x0) { + g_autofree gchar *vendor_id = g_strdup_printf("USB:0x%04X", (guint)vid); + fu_device_add_vendor_id(device, vendor_id); + } + + /* check the quirk matched to avoid mounting *all* vfat devices */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not marked as updatable in uf2.quirk"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_uf2_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_uf2_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuUf2Device *self = FU_UF2_DEVICE(device); + FU_DEVICE_CLASS(fu_uf2_device_parent_class)->to_string(device, idt, str); + if (self->family_id > 0) + fu_string_append_kx(str, idt, "FamilyId", self->family_id); +} + +static void +fu_uf2_device_init(FuUf2Device *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.microsoft.uf2"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); +} + +static void +fu_uf2_device_finalize(GObject *obj) +{ + FuUf2Device *self = FU_UF2_DEVICE(obj); + + /* should be done by ->close(), but check to be sure */ + if (self->volume != NULL) + g_object_unref(self->volume); + + G_OBJECT_CLASS(fu_uf2_device_parent_class)->finalize(obj); +} + +static void +fu_uf2_device_class_init(FuUf2DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *klass_object = G_OBJECT_CLASS(klass); + klass_object->finalize = fu_uf2_device_finalize; + klass_device->to_string = fu_uf2_device_to_string; + klass_device->probe = fu_uf2_device_probe; + klass_device->setup = fu_uf2_device_setup; + klass_device->open = fu_uf2_device_open; + klass_device->close = fu_uf2_device_close; + klass_device->prepare_firmware = fu_uf2_device_prepare_firmware; + klass_device->set_progress = fu_uf2_device_set_progress; + klass_device->read_firmware = fu_uf2_device_read_firmware; + klass_device->write_firmware = fu_block_device_write_firmware; + klass_device->dump_firmware = fu_block_device_dump_firmware; +} diff --git a/fwupd-1.8.6/plugins/uf2/fu-uf2-device.h b/fwupd-1.8.6/plugins/uf2/fu-uf2-device.h new file mode 100644 index 0000000000000000000000000000000000000000..af82f3b1d566c5540d62fec9c0104964b704d560 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-uf2-device.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_UF2_DEVICE (fu_uf2_device_get_type()) + +G_DECLARE_FINAL_TYPE(FuUf2Device, fu_uf2_device, FU, UF2_DEVICE, FuUdevDevice) diff --git a/fwupd-1.8.6/plugins/uf2/fu-uf2-firmware.c b/fwupd-1.8.6/plugins/uf2/fu-uf2-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..f8e73cacafb39e0f9184ec51773cbb4cce946ed8 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-uf2-firmware.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uf2-firmware.h" + +struct _FuUf2Firmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuUf2Firmware, fu_uf2_firmware, FU_TYPE_FIRMWARE) + +#define FU_UF2_FIRMWARE_MAGIC_START0 0x0A324655u +#define FU_UF2_FIRMWARE_MAGIC_START1 0x9E5D5157u +#define FU_UF2_FIRMWARE_MAGIC_END 0x0AB16F30u + +#define FU_UF2_FIRMWARE_BLOCK_FLAG_NONE 0x00000000 +#define FU_UF2_FIRMWARE_BLOCK_FLAG_NOFLASH 0x00000001 +#define FU_UF2_FIRMWARE_BLOCK_FLAG_IS_CONTAINER 0x00001000 +#define FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_FAMILY 0x00002000 +#define FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_MD5 0x00004000 +#define FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_EXTENSION_TAG 0x00008000 + +#define FU_U2F_FIRMWARE_TAG_VERSION 0x9fc7bc /* semver of firmware file (UTF-8) */ +#define FU_U2F_FIRMWARE_TAG_DESCRIPTION 0x650d9d /* description of device (UTF-8) */ +#define FU_U2F_FIRMWARE_TAG_PAGE_SZ 0x0be9f7 /* page size of target device (uint32_t) */ +#define FU_U2F_FIRMWARE_TAG_SHA1 0xb46db0 /* SHA-2 checksum of firmware */ +#define FU_U2F_FIRMWARE_TAG_DEVICE_ID 0xc8a729 /* device type identifier (uint32_t or uint64_t) */ + +static gboolean +fu_uf2_firmware_parse_chunk(FuUf2Firmware *self, FuChunk *chk, GByteArray *tmp, GError **error) +{ + gsize bufsz = fu_chunk_get_data_sz(chk); + const guint8 *buf = fu_chunk_get_data(chk); + guint32 magic = 0; + guint32 flags = 0; + guint32 addr = 0; + guint32 datasz = 0; + guint32 blockcnt = 0; + guint32 blocktotal = 0; + guint32 family_id = 0; + + /* sanity check */ + if (bufsz != 512) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "chunk size invalid, expected 512 bytes and got %u", + fu_chunk_get_data_sz(chk)); + return FALSE; + } + + /* check magic */ + if (!fu_memread_uint32_safe(buf, bufsz, 0x000, &magic, G_LITTLE_ENDIAN, error)) + return FALSE; + if (magic != FU_UF2_FIRMWARE_MAGIC_START0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "magic bytes #1 failed, expected 0x%08x bytes and got 0x%08x", + FU_UF2_FIRMWARE_MAGIC_START0, + magic); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x004, &magic, G_LITTLE_ENDIAN, error)) + return FALSE; + if (magic != FU_UF2_FIRMWARE_MAGIC_START1) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "magic bytes #2 failed, expected 0x%08x bytes and got 0x%08x", + FU_UF2_FIRMWARE_MAGIC_START1, + magic); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x008, &flags, G_LITTLE_ENDIAN, error)) + return FALSE; + if (flags & FU_UF2_FIRMWARE_BLOCK_FLAG_IS_CONTAINER) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "container U2F firmware not supported"); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x00C, &addr, G_LITTLE_ENDIAN, error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, bufsz, 0x010, &datasz, G_LITTLE_ENDIAN, error)) + return FALSE; + if (datasz > 476) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "data size impossible got 0x%08x", + datasz); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x014, &blockcnt, G_LITTLE_ENDIAN, error)) + return FALSE; + if (blockcnt != fu_chunk_get_idx(chk)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "block count invalid, expected 0x%04x and got 0x%04x", + fu_chunk_get_idx(chk), + blockcnt); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x018, &blocktotal, G_LITTLE_ENDIAN, error)) + return FALSE; + if (blocktotal == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "block count invalid, expected > 0"); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x01C, &family_id, G_LITTLE_ENDIAN, error)) + return FALSE; + if (flags & FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_FAMILY) { + if (family_id == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "family_id required but not supplied"); + return FALSE; + } + } + + /* assume first chunk is representative of firmware */ + if (fu_chunk_get_idx(chk) == 0) { + fu_firmware_set_addr(FU_FIRMWARE(self), addr); + fu_firmware_set_idx(FU_FIRMWARE(self), family_id); + } + + /* just append raw data */ + g_byte_array_append(tmp, buf + 0x020, datasz); + if (flags & FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_MD5) { + if (datasz < 24) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "not enough space for MD5 checksum"); + return FALSE; + } + } + if (flags & FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_EXTENSION_TAG) { + gsize offset = 0x20 + datasz; + while (offset < bufsz) { + guint8 sz = 0; + guint32 tag = 0; + + /* [SZ][TAG][TAG][TAG][TAG][DATA....] */ + if (!fu_memread_uint8_safe(buf, bufsz, offset, &sz, error)) + return FALSE; + if (sz < 4) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid extension tag size"); + return FALSE; + } + if (!fu_memread_uint32_safe(buf, + bufsz, + offset, + &tag, + G_LITTLE_ENDIAN, + error)) + return FALSE; + tag &= 0xFFFFFF; + if (tag == FU_U2F_FIRMWARE_TAG_VERSION) { + g_autofree gchar *utf8buf = g_malloc0(sz); + if (!fu_memcpy_safe((guint8 *)utf8buf, + sz, + 0x0, /* dst */ + buf, + bufsz, + offset + 0x4, /* src */ + sz - 4, + error)) + return FALSE; + fu_firmware_set_version(FU_FIRMWARE(self), utf8buf); + } else if (tag == FU_U2F_FIRMWARE_TAG_DESCRIPTION) { + g_autofree gchar *utf8buf = g_malloc0(sz); + if (!fu_memcpy_safe((guint8 *)utf8buf, + sz, + 0x0, /* dst */ + buf, + bufsz, + offset + 0x4, /* src */ + sz - 4, + error)) + return FALSE; + fu_firmware_set_id(FU_FIRMWARE(self), utf8buf); + } else { + if (g_getenv("FWUPD_FUZZER_RUNNING") == NULL) + g_warning("unknown tag 0x%06x", tag); + } + + /* next! */ + offset += sz; + } + } + if (!fu_memread_uint32_safe(buf, bufsz, 0x1FC, &magic, G_LITTLE_ENDIAN, error)) + return FALSE; + if (magic != FU_UF2_FIRMWARE_MAGIC_END) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "magic bytes #3 failed, expected 0x%08x bytes and got 0x%08x", + FU_UF2_FIRMWARE_MAGIC_END, + magic); + return FALSE; + } + + /* dump */ + if (g_getenv("FWUPD_U2F_VERBOSE") != NULL) { + g_debug("block: 0x%x/0x%x @0x%x", blockcnt, blocktotal - 1, addr); + g_debug("family_id: 0x%x", family_id); + g_debug("flags: 0x%x", flags); + g_debug("datasz: 0x%x", datasz); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uf2_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuUf2Firmware *self = FU_UF2_FIRMWARE(firmware); + g_autoptr(GByteArray) tmp = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* read in fixed sized chunks */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, 512); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_uf2_firmware_parse_chunk(self, chk, tmp, error)) + return FALSE; + } + + /* success */ + blob = g_byte_array_free_to_bytes(g_steal_pointer(&tmp)); + fu_firmware_set_bytes(firmware, blob); + return TRUE; +} + +static GByteArray * +fu_uf2_firmware_write_chunk(FuUf2Firmware *self, FuChunk *chk, guint chk_len, GError **error) +{ + guint32 addr = fu_firmware_get_addr(FU_FIRMWARE(self)); + guint32 family_id = fu_firmware_get_idx(FU_FIRMWARE(self)); + guint32 flags = FU_UF2_FIRMWARE_BLOCK_FLAG_NONE; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GByteArray) datapad = g_byte_array_new(); + + /* sanity check */ + if (fu_chunk_get_data_sz(chk) > 476) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "chunk size invalid, expected < 476 bytes and got %u", + fu_chunk_get_data_sz(chk)); + return NULL; + } + + /* pad out data */ + g_byte_array_append(datapad, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + fu_byte_array_set_size(datapad, 476, 0x0); + + /* optional */ + if (family_id > 0) + flags |= FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_FAMILY; + + /* offset from base address */ + addr += fu_chunk_get_idx(chk) * fu_chunk_get_data_sz(chk); + + /* build UF2 packet */ + fu_byte_array_append_uint32(buf, FU_UF2_FIRMWARE_MAGIC_START0, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, FU_UF2_FIRMWARE_MAGIC_START1, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, flags, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, addr, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, fu_chunk_get_idx(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, chk_len, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, family_id, G_LITTLE_ENDIAN); + g_byte_array_append(buf, datapad->data, datapad->len); + fu_byte_array_append_uint32(buf, FU_UF2_FIRMWARE_MAGIC_END, G_LITTLE_ENDIAN); + + /* success */ + return g_steal_pointer(&buf); +} + +static GBytes * +fu_uf2_firmware_write(FuFirmware *firmware, GError **error) +{ + FuUf2Firmware *self = FU_UF2_FIRMWARE(firmware); + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* data first */ + fw = fu_firmware_get_bytes_with_patches(firmware, error); + if (fw == NULL) + return NULL; + + /* write in chunks */ + chunks = fu_chunk_array_new_from_bytes(fw, fu_firmware_get_addr(firmware), 0x0, 256); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) tmp = NULL; + tmp = fu_uf2_firmware_write_chunk(self, chk, chunks->len, error); + if (tmp == NULL) + return NULL; + g_byte_array_append(buf, tmp->data, tmp->len); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_uf2_firmware_init(FuUf2Firmware *self) +{ +} + +static void +fu_uf2_firmware_class_init(FuUf2FirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_uf2_firmware_parse; + klass_firmware->write = fu_uf2_firmware_write; +} + +FuFirmware * +fu_uf2_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_UF2_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/uf2/fu-uf2-firmware.h b/fwupd-1.8.6/plugins/uf2/fu-uf2-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..0d3e9c5254500df8a628f9922608d36316bd8216 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-uf2-firmware.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_UF2_FIRMWARE (fu_uf2_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuUf2Firmware, fu_uf2_firmware, FU, UF2_FIRMWARE, FuFirmware) + +FuFirmware * +fu_uf2_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/uf2/fu-uf2-plugin.c b/fwupd-1.8.6/plugins/uf2/fu-uf2-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..ebc10a15349a7ab2a6fd567be8ef50963420f30b --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-uf2-plugin.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-uf2-device.h" +#include "fu-uf2-firmware.h" +#include "fu-uf2-plugin.h" + +struct _FuUf2Plugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuUf2Plugin, fu_uf2_plugin, FU_TYPE_PLUGIN) + +static void +fu_uf2_plugin_init(FuUf2Plugin *self) +{ +} + +static void +fu_uf2_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_UF2_DEVICE); + fu_plugin_add_firmware_gtype(plugin, "uf2", FU_TYPE_UF2_FIRMWARE); + fu_plugin_add_udev_subsystem(plugin, "block"); +} + +static void +fu_uf2_plugin_class_init(FuUf2PluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_uf2_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/uf2/fu-uf2-plugin.h b/fwupd-1.8.6/plugins/uf2/fu-uf2-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..b5ba6fd6ec43ebfc634ce1466561aeb23b1e0a02 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/fu-uf2-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUf2Plugin, fu_uf2_plugin, FU, UF2_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/uf2/meson.build b/fwupd-1.8.6/plugins/uf2/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..0fb32cedf16f390cfbb0b7746eb40fc39a02f764 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/meson.build @@ -0,0 +1,43 @@ + +if get_option('plugin_uf2').require(gudev.found(), + error_message: 'gudev is needed for plugin_uf2').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginUf2"'] + +plugin_quirks += files('uf2.quirk') +plugin_builtin_uf2 = static_library('fu_plugin_uf2', + sources: [ + 'fu-uf2-plugin.c', + 'fu-uf2-device.c', + 'fu-uf2-firmware.c', # fuzzing + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: plugin_deps, + link_with: plugin_libs, +) +plugin_builtins += plugin_builtin_uf2 + +if get_option('tests') + install_data(['tests/uf2.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'uf2-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_uf2, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('uf2-self-test', e, env: env) +endif +endif diff --git a/fwupd-1.8.6/plugins/uf2/tests/uf2.bin b/fwupd-1.8.6/plugins/uf2/tests/uf2.bin new file mode 100644 index 0000000000000000000000000000000000000000..2d56c7cfb08ef64f58069c18b83b4d388be8dbe8 Binary files /dev/null and b/fwupd-1.8.6/plugins/uf2/tests/uf2.bin differ diff --git a/fwupd-1.8.6/plugins/uf2/tests/uf2.builder.xml b/fwupd-1.8.6/plugins/uf2/tests/uf2.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..ba46a2e2c77f1de91da301d2a02a418a852925db --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/tests/uf2.builder.xml @@ -0,0 +1,4 @@ + + 0x0100 + aGVsbG8gd29ybGQ= + diff --git a/fwupd-1.8.6/plugins/uf2/uf2.quirk b/fwupd-1.8.6/plugins/uf2/uf2.quirk new file mode 100644 index 0000000000000000000000000000000000000000..45c3a91112415289db2a485b38f2031e6d707612 --- /dev/null +++ b/fwupd-1.8.6/plugins/uf2/uf2.quirk @@ -0,0 +1,159 @@ +# match all devices with this udev subsystem +[BLOCK] +Plugin = uf2 + +# Raspberry Pi RP2 [no CURRENT.UF2] +[USB\VID_2E8A&PID_0003] +Guid = UF2\FAMILY_E48BFF56 +Flags = updatable,will-disappear + +# Adafruit Trinket +[USB\VID_1781&PID_0C9F] +Flags = updatable + +# Adafruit Trinket M0 +[USB\VID_239A&PID_001E] +Flags = updatable + +# Adafruit Feather M0 Express +[USB\VID_239A&PID_001B] +Flags = updatable + +# nRF52840 MDK +[USB\VID_239A&PID_0029] +Flags = updatable + +# the following values are from https://github.com/microsoft/uf2/blob/master/utils/uf2families.json +# although are lightly edited to split up Name and Vendor in the correct way. + +[UF2\FAMILY_16573617] +Name = ATMEGA32 +Vendor = Microchip + +[UF2\FAMILY_1851780A] +Name = SAML21 +Vendor = Microchip + +[UF2\FAMILY_1B57745F] +Name = NRF52 +Vendor = Nordic + +[UF2\FAMILY_1C5F21B0] +Name = ESP32 +Vendor = ESP32 + +[UF2\FAMILY_1E1F432D] +Name = STM32L1 +Vendor = ST + +[UF2\FAMILY_202E3A91] +Name = STM32L0 +Vendor = ST + +[UF2\FAMILY_21460FF0] +Name = STM32WL +Vendor = ST + +[UF2\FAMILY_2ABC77EC] +Name = LPC55 +Vendor = NXP + +[UF2\FAMILY_300F5633] +Name = STM32G0 +Vendor = ST + +[UF2\FAMILY_31D228C6] +Name = GD32F350 +Vendor = GD + +[UF2\FAMILY_04240BDF] +Name = STM32L5 +Vendor = ST + +[UF2\FAMILY_4C71240A] +Name = STM32G4 +Vendor = ST + +[UF2\FAMILY_4FB2D5BD] +Name = MIMXRT10XX +Vendor = NXP + +[UF2\FAMILY_53B80F00] +Name = STM32F7 +Vendor = ST + +[UF2\FAMILY_55114460] +Name = SAMD51 +Vendor = Microchip + +[UF2\FAMILY_57755A57] +Name = STM32F4 +Vendor = ST + +[UF2\FAMILY_5A18069B] +Name = FX2 +Vendor = Cypress + +[UF2\FAMILY_5D1A0A2E] +Name = STM32F2 +Vendor = ST + +[UF2\FAMILY_5EE21072] +Name = STM32F1 +Vendor = ST + +[UF2\FAMILY_647824B6] +Name = STM32F0 +Vendor = ST + +[UF2\FAMILY_68ED2B88] +Name = SAMD21 +Vendor = Microchip + +[UF2\FAMILY_6B846188] +Name = STM32F3 +Vendor = ST + +[UF2\FAMILY_6D0922FA] +Name = STM32F407 +Vendor = ST + +[UF2\FAMILY_6DB66082] +Name = STM32H7 +Vendor = ST + +[UF2\FAMILY_70D16653] +Name = STM32WB +Vendor = ST + +[UF2\FAMILY_7EAB61ED] +Name = ESP8266 + +[UF2\FAMILY_7F83E793] +Name = KL32L2 +Vendor = NXP + +[UF2\FAMILY_8FB060FE] +Name = STM32F407VG +Vendor = ST + +[UF2\FAMILY_ADA52840] +Name = NRF52840 +Vendor = Nordic + +[UF2\FAMILY_BFDD4EEE] +Name = ESP32S2 + +[UF2\FAMILY_C47E5767] +Name = ESP32S3 + +[UF2\FAMILY_D42BA06C] +Name = ESP32C3 + +[UF2\FAMILY_E48BFF56] +Name = RP2040 +Vendor = Raspberry Pi + +[UF2\FAMILY_00FF6919] +Name = STM32L4 +Vendor = ST diff --git a/fwupd-1.8.6/plugins/upower/README.md b/fwupd-1.8.6/plugins/upower/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e3ebb5f65b392fed9c1427f0e360f0bfb5c67c0a --- /dev/null +++ b/fwupd-1.8.6/plugins/upower/README.md @@ -0,0 +1,13 @@ +# UPower + +## Introduction + +This plugin is used to ensure that some updates are not done on battery power. + +## Vendor ID Security + +This protocol does not create a device and thus requires no vendor ID set. + +## External Interface Access + +This plugin requires access to the dbus interface `org.freedesktop.UPower`. diff --git a/fwupd-1.8.6/plugins/upower/fu-upower-plugin.c b/fwupd-1.8.6/plugins/upower/fu-upower-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..8eb4c107f79e902384f9347a5dc7f443c122f5bc --- /dev/null +++ b/fwupd-1.8.6/plugins/upower/fu-upower-plugin.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2016 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-upower-plugin.h" + +struct _FuUpowerPlugin { + FuPlugin parent_instance; + GDBusProxy *proxy; /* nullable */ + GDBusProxy *proxy_manager; /* nullable */ +}; + +typedef enum { + UP_DEVICE_STATE_UNKNOWN, + UP_DEVICE_STATE_CHARGING, + UP_DEVICE_STATE_DISCHARGING, + UP_DEVICE_STATE_EMPTY, + UP_DEVICE_STATE_FULLY_CHARGED, + UP_DEVICE_STATE_PENDING_CHARGE, + UP_DEVICE_STATE_PENDING_DISCHARGE, + UP_DEVICE_STATE_LAST +} UpDeviceState; + +G_DEFINE_TYPE(FuUpowerPlugin, fu_upower_plugin, FU_TYPE_PLUGIN) + +static void +fu_upower_plugin_rescan_devices(FuPlugin *plugin) +{ + FuUpowerPlugin *self = FU_UPOWER_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(GVariant) percentage_val = NULL; + g_autoptr(GVariant) type_val = NULL; + g_autoptr(GVariant) state_val = NULL; + + /* check that we "have" a battery */ + type_val = g_dbus_proxy_get_cached_property(self->proxy, "Type"); + if (type_val == NULL || g_variant_get_uint32(type_val) == 0) { + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_UNKNOWN); + fu_context_set_battery_level(ctx, FWUPD_BATTERY_LEVEL_INVALID); + return; + } + state_val = g_dbus_proxy_get_cached_property(self->proxy, "State"); + if (state_val == NULL || g_variant_get_uint32(state_val) == 0) { + g_warning("failed to query power state"); + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_UNKNOWN); + fu_context_set_battery_level(ctx, FWUPD_BATTERY_LEVEL_INVALID); + return; + } + + /* map from UpDeviceState to FuBatteryState */ + switch (g_variant_get_uint32(state_val)) { + case UP_DEVICE_STATE_CHARGING: + case UP_DEVICE_STATE_PENDING_CHARGE: + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_CHARGING); + break; + case UP_DEVICE_STATE_DISCHARGING: + case UP_DEVICE_STATE_PENDING_DISCHARGE: + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_DISCHARGING); + break; + case UP_DEVICE_STATE_EMPTY: + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_EMPTY); + break; + case UP_DEVICE_STATE_FULLY_CHARGED: + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_FULLY_CHARGED); + break; + default: + fu_context_set_battery_state(ctx, FU_BATTERY_STATE_UNKNOWN); + break; + } + + /* get percentage */ + percentage_val = g_dbus_proxy_get_cached_property(self->proxy, "Percentage"); + if (percentage_val == NULL) { + g_warning("failed to query power percentage level"); + fu_context_set_battery_level(ctx, FWUPD_BATTERY_LEVEL_INVALID); + return; + } + fu_context_set_battery_level(ctx, g_variant_get_double(percentage_val)); +} + +static void +fu_upower_plugin_rescan_manager(FuPlugin *plugin) +{ + FuUpowerPlugin *self = FU_UPOWER_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(GVariant) lid_is_closed = NULL; + g_autoptr(GVariant) lid_is_present = NULL; + + /* check that we "have" a lid */ + lid_is_present = g_dbus_proxy_get_cached_property(self->proxy_manager, "LidIsPresent"); + lid_is_closed = g_dbus_proxy_get_cached_property(self->proxy_manager, "LidIsClosed"); + if (lid_is_present == NULL || lid_is_closed == NULL) { + g_warning("failed to query lid state"); + fu_context_set_lid_state(ctx, FU_LID_STATE_UNKNOWN); + return; + } + if (!g_variant_get_boolean(lid_is_present)) { + fu_context_set_lid_state(ctx, FU_LID_STATE_UNKNOWN); + return; + } + if (g_variant_get_boolean(lid_is_closed)) { + fu_context_set_lid_state(ctx, FU_LID_STATE_CLOSED); + return; + } + fu_context_set_lid_state(ctx, FU_LID_STATE_OPEN); +} + +static void +fu_upower_plugin_proxy_changed_cb(GDBusProxy *proxy, + GVariant *changed_properties, + const GStrv invalidated_properties, + FuPlugin *plugin) +{ + fu_upower_plugin_rescan_manager(plugin); + fu_upower_plugin_rescan_devices(plugin); +} + +static gboolean +fu_upower_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuUpowerPlugin *self = FU_UPOWER_PLUGIN(plugin); + g_autofree gchar *name_owner = NULL; + + self->proxy_manager = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.UPower", + "/org/freedesktop/UPower", + "org.freedesktop.UPower", + NULL, + error); + if (self->proxy_manager == NULL) { + g_prefix_error(error, "failed to connect to upower: "); + return FALSE; + } + self->proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.UPower", + "/org/freedesktop/UPower/devices/DisplayDevice", + "org.freedesktop.UPower.Device", + NULL, + error); + if (self->proxy == NULL) { + g_prefix_error(error, "failed to connect to upower: "); + return FALSE; + } + name_owner = g_dbus_proxy_get_name_owner(self->proxy); + if (name_owner == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no owner for %s", + g_dbus_proxy_get_name(self->proxy)); + return FALSE; + } + g_signal_connect(G_DBUS_PROXY(self->proxy), + "g-properties-changed", + G_CALLBACK(fu_upower_plugin_proxy_changed_cb), + plugin); + g_signal_connect(G_DBUS_PROXY(self->proxy_manager), + "g-properties-changed", + G_CALLBACK(fu_upower_plugin_proxy_changed_cb), + plugin); + + fu_upower_plugin_rescan_devices(plugin); + fu_upower_plugin_rescan_manager(plugin); + + /* success */ + return TRUE; +} + +static void +fu_upower_plugin_init(FuUpowerPlugin *self) +{ +} + +static void +fu_upower_finalize(GObject *obj) +{ + FuUpowerPlugin *self = FU_UPOWER_PLUGIN(obj); + if (self->proxy != NULL) + g_object_unref(self->proxy); + if (self->proxy_manager != NULL) + g_object_unref(self->proxy_manager); + G_OBJECT_CLASS(fu_upower_plugin_parent_class)->finalize(obj); +} + +static void +fu_upower_plugin_class_init(FuUpowerPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_upower_finalize; + plugin_class->startup = fu_upower_plugin_startup; +} diff --git a/fwupd-1.8.6/plugins/upower/fu-upower-plugin.h b/fwupd-1.8.6/plugins/upower/fu-upower-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..e52601abe6f2aaa4c812397b5f85b0c038eb9a3e --- /dev/null +++ b/fwupd-1.8.6/plugins/upower/fu-upower-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUpowerPlugin, fu_upower_plugin, FU, UPOWER_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/upower/meson.build b/fwupd-1.8.6/plugins/upower/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..fb1a1ae7c43fd9810385f6e3bef70e30ba7fde5a --- /dev/null +++ b/fwupd-1.8.6/plugins/upower/meson.build @@ -0,0 +1,14 @@ +if get_option('plugin_upower').disable_auto_if(host_machine.system() != 'linux').allowed() +cargs = ['-DG_LOG_DOMAIN="FuPluginUpower"'] + +plugin_builtins += static_library('fu_plugin_upower', + sources: [ + 'fu-upower-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +endif diff --git a/fwupd-1.8.6/plugins/usi-dock/README.md b/fwupd-1.8.6/plugins/usi-dock/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0cf1a70cb4cc85f07b96bb05f0fc394e34d85989 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/README.md @@ -0,0 +1,37 @@ +# USI Dock + +## Introduction + +This plugin uses the MCU to write all the dock firmware components. The MCU version +is provided by the DMC bcdDevice. + +This plugin supports the following protocol ID: + +* com.usi.dock + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_17EF&PID_7226&REV_0001` +* `USB\VID_17EF&PID_7226` +* `USB\VID_17EF` + +Additionally, some extra "component ID" instance IDs are added. + +* `USB\VID_17EF&PID_7226&CID_TBT4` +* `USB\VID_17EF&PID_7226&CID_USB3` + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with +the same USB PID in an unlocked mode. On attach the device again re-enumerates +back to the runtime locked mode. + +## Vendor ID Security + +The vendor ID is set from the USB vendor. + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/usi-dock/data/lsusb.txt b/fwupd-1.8.6/plugins/usi-dock/data/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..4a9205e36a2538230ddec5e6b9ad199f4697b7ec --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/data/lsusb.txt @@ -0,0 +1,68 @@ +Bus 001 Device 024: ID 17ef:30b4 Lenovo ThinkPad Thunderbolt 4 Dock MCU Controller +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x17ef Lenovo + idProduct 0x30b4 + bcdDevice 1.00 + iManufacturer 1 Lenovo + iProduct 2 ThinkPad Thunderbolt 4 Dock MCU Controller + iSerial 3 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0029 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 39 + 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 + 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 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-child-device.c b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-child-device.c new file mode 100644 index 0000000000000000000000000000000000000000..9d6dab52255fc2e75ec95f92453bb48d8c5cdb14 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-child-device.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-usi-dock-child-device.h" +#include "fu-usi-dock-mcu-device.h" + +struct _FuUsiDockChildDevice { + FuDevice parent_instance; + guint8 chip_idx; +}; + +G_DEFINE_TYPE(FuUsiDockChildDevice, fu_usi_dock_child_device, FU_TYPE_DEVICE) + +void +fu_usi_dock_child_device_set_chip_idx(FuUsiDockChildDevice *self, guint8 chip_idx) +{ + self->chip_idx = chip_idx; +} + +static void +fu_usi_dock_child_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuUsiDockChildDevice *self = FU_USI_DOCK_CHILD_DEVICE(device); + fu_string_append_kx(str, idt, "ChipIdx", self->chip_idx); +} + +/* use the parents parser */ +static FuFirmware * +fu_usi_dock_mcu_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + if (parent == NULL) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no parent"); + return NULL; + } + return fu_device_prepare_firmware(parent, fw, flags, error); +} + +/* only update this specific child component */ +static gboolean +fu_usi_dock_mcu_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuUsiDockChildDevice *self = FU_USI_DOCK_CHILD_DEVICE(device); + FuDevice *parent = fu_device_get_parent(device); + if (parent == NULL) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no parent"); + return FALSE; + } + return fu_usi_dock_mcu_device_write_firmware_with_idx(FU_USI_DOCK_MCU_DEVICE(parent), + firmware, + self->chip_idx, + progress, + flags, + error); +} + +static void +fu_usi_dock_child_device_init(FuUsiDockChildDevice *self) +{ + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN); +} + +static void +fu_usi_dock_child_device_class_init(FuUsiDockChildDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_usi_dock_child_device_to_string; + klass_device->prepare_firmware = fu_usi_dock_mcu_device_prepare_firmware; + klass_device->write_firmware = fu_usi_dock_mcu_device_write_firmware; +} + +FuDevice * +fu_usi_dock_child_new(FuContext *ctx) +{ + return g_object_new(FU_TYPE_USI_DOCK_CHILD_DEVICE, "context", ctx, NULL); +} diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-child-device.h b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-child-device.h new file mode 100644 index 0000000000000000000000000000000000000000..023b5b1f49c16079af5e7dd9f21d247e69a211ff --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-child-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_USI_DOCK_CHILD_DEVICE (fu_usi_dock_child_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUsiDockChildDevice, + fu_usi_dock_child_device, + FU, + USI_DOCK_CHILD_DEVICE, + FuDevice) + +FuDevice * +fu_usi_dock_child_new(FuContext *ctx); +void +fu_usi_dock_child_device_set_chip_idx(FuUsiDockChildDevice *self, guint8 chip_idx); diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-common.c b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-common.c new file mode 100644 index 0000000000000000000000000000000000000000..91baf98fe38a58689e4174fefd9259fab69624a6 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-common.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 Victor Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-usi-dock-common.h" + +const gchar * +fu_usi_dock_spi_state_to_string(guint8 val) +{ + if (val == SPI_STATE_NONE) + return "none"; + if (val == SPI_STATE_SWITCH_SUCCESS) + return "switch-success"; + if (val == SPI_STATE_SWITCH_FAIL) + return "switch-fail"; + if (val == SPI_STATE_CMD_SUCCESS) + return "cmd-success"; + if (val == SPI_STATE_CMD_FAIL) + return "cmd-fail"; + if (val == SPI_STATE_RW_SUCCESS) + return "rw-success"; + if (val == SPI_STATE_RW_FAIL) + return "rw-fail"; + if (val == SPI_STATE_READY) + return "ready"; + if (val == SPI_STATE_BUSY) + return "busy"; + if (val == SPI_STATE_TIMEOUT) + return "timeout"; + if (val == SPI_STATE_FLASH_FOUND) + return "flash-found"; + if (val == SPI_STATE_FLASH_NOT_FOUND) + return "flash-not-found"; + return NULL; +} diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-common.h b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-common.h new file mode 100644 index 0000000000000000000000000000000000000000..e258dbc606081a0724fff656bedeffcba4bb7684 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-common.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 Victor Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define USB_HID_REPORT_ID1 1u +#define USB_HID_REPORT_ID2 2u + +#define USBUID_ISP_DEVICE_CMD_MCU_NONE 0x0 +#define USBUID_ISP_DEVICE_CMD_MCU_STATUS 0x1 +#define USBUID_ISP_DEVICE_CMD_MCU_JUMP2BOOT 0x2 +#define USBUID_ISP_DEVICE_CMD_READ_MCU_VERSIONPAGE 0x3 +#define USBUID_ISP_DEVICE_CMD_SET_I225_PWR 0x4 +#define USBUID_ISP_DEVICE_CMD_DOCK_RESET 0x5 +#define USBUID_ISP_DEVICE_CMD_VERSION_WRITEBACK 0x6 + +#define USBUID_ISP_DEVICE_CMD_FWBUFER_INITIAL 0x01 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_ERASE_FLASH 0x02 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_PROGRAM 0x03 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_WRITE_RESPONSE 0x04 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_READ_STATUS 0x05 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_CHECKSUM 0x06 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_END 0x07 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_TRANSFER_FINISH 0x08 +#define USBUID_ISP_DEVICE_CMD_FWBUFER_ERROR_END 0x09 + +#define USBUID_ISP_INTERNAL_FW_CMD_INITAL 0x0A +#define USBUID_ISP_INTERNAL_FW_CMD_UPDATE_FW 0x0B +#define USBUID_ISP_INTERNAL_FW_CMD_TARGET_CHECKSUM 0x0C +#define USBUID_ISP_INTERNAL_FW_CMD_ISP_END 0x0D +#define USBUID_ISP_CMD_ALL 0xFF + +#define TAG_TAG2_ISP_BOOT 0 /* before Common CMD for bootload, with TAG0, TAG1, CMD */ +#define TAG_TAG2_ISP 0x5a /* before Common, with TAG0, TAG1, CMD */ +#define TAG_TAG2_CMD_MCU 0x6a /* USB->MCU(Common-cmd mode), with TAG0, TAG1, CMD */ +#define TAG_TAG2_CMD_SPI 0x7a /* USB->MCU->SPI(Common-cmd mode), with TAG0, TAG1, CMD */ +#define TAG_TAG2_CMD_I2C 0x8a /* USB->MCU->I2C(Mass data transmission) */ +#define TAG_TAG2_MASS_DATA_MCU 0x6b /* MASS data transfer for MCU 0xA0 */ +#define TAG_TAG2_MASS_DATA_SPI 0x7b /* MASS data transfer for External flash 0xA1 */ +#define TAG_TAG2_MASS_DATA_I2C 0x8b /* MASS data transfer for TBT flash */ + +#define DP_VERSION_FROM_MCU 0x01 /* if in use */ +#define NIC_VERSION_FROM_MCU 0x2 /* if in use */ + +#define External_Valid_Value 0x37 +#define TX_ISP_LENGTH 61 + +#define W25Q16DV_PAGE_SIZE 256 + +#define FIRMWARE_IDX_NONE 0x00 +#define FIRMWARE_IDX_DMC_PD 0x01 +#define FIRMWARE_IDX_DP 0x02 +#define FIRMWARE_IDX_TBT4 0x04 +#define FIRMWARE_IDX_USB3 0x08 +#define FIRMWARE_IDX_USB2 0x10 +#define FIRMWARE_IDX_AUDIO 0x20 +#define FIRMWARE_IDX_I225 0x40 +#define FIRMWARE_IDX_MCU 0x80 + +typedef enum { + SPI_STATE_NONE, + SPI_STATE_SWITCH_SUCCESS, + SPI_STATE_SWITCH_FAIL, + SPI_STATE_CMD_SUCCESS, + SPI_STATE_CMD_FAIL, + SPI_STATE_RW_SUCCESS, + SPI_STATE_RW_FAIL, + SPI_STATE_READY, + SPI_STATE_BUSY, + SPI_STATE_TIMEOUT, + SPI_STATE_FLASH_FOUND, + SPI_STATE_FLASH_NOT_FOUND, +} SPI_BUS_STATE; + +typedef struct { + guint8 DMC[5]; + guint8 PD[5]; + guint8 DP5x[5]; + guint8 DP6x[5]; + guint8 TBT4[5]; + guint8 USB3[5]; + guint8 USB2[5]; + guint8 AUDIO[5]; + guint8 I255[5]; + guint8 MCU[2]; + guint8 bcdVersion[2]; +} IspVersionInMcu_t; + +typedef struct { + guint8 id; + guint8 length; + guint8 mcutag1; + guint8 mcutag2; + guint8 inbuf[59]; + guint8 mcutag3; +} UsiDockSetReportBuf; + +const gchar * +fu_usi_dock_idx_to_string(guint8 val); +const gchar * +fu_usi_dock_spi_state_to_string(guint8 val); diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-dmc-device.c b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-dmc-device.c new file mode 100644 index 0000000000000000000000000000000000000000..4d4c4470e39287b078a4497db22efcfe34039729 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-dmc-device.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-usi-dock-dmc-device.h" + +struct _FuUsiDockDmcDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUsiDockDmcDevice, fu_usi_dock_dmc_device, FU_TYPE_USB_DEVICE) + +static void +fu_usi_dock_dmc_device_parent_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) +{ + FuDevice *parent = fu_device_get_parent(device); + if (parent != NULL) { + g_autoptr(GError) error = NULL; + + /* slightly odd: the MCU device uses the DMC version number */ + g_debug("absorbing DMC version into MCU"); + fu_device_set_version_format(parent, fu_device_get_version_format(device)); + fu_device_set_version(parent, fu_device_get_version(device)); + fu_device_set_serial(parent, fu_device_get_serial(device)); + + /* allow matching firmware */ + fu_device_add_instance_str(parent, "CID", fu_device_get_name(device)); + if (!fu_device_build_instance_id(parent, &error, "USB", "VID", "PID", "CID", NULL)) + g_warning("failed to build ID: %s", error->message); + + /* don't allow firmware updates on this */ + fu_device_set_name(device, "Dock Management Controller Information"); + fu_device_inhibit(device, "dummy", "Use the MCU to update the DMC device"); + } +} + +static void +fu_usi_dock_dmc_device_init(FuUsiDockDmcDevice *self) +{ + g_signal_connect(FU_DEVICE(self), + "notify::parent", + G_CALLBACK(fu_usi_dock_dmc_device_parent_notify_cb), + NULL); +} + +static void +fu_usi_dock_dmc_device_class_init(FuUsiDockDmcDeviceClass *klass) +{ +} diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-dmc-device.h b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-dmc-device.h new file mode 100644 index 0000000000000000000000000000000000000000..7332644b6fdba99d0bcfaa5f8efc7d12d208f9fa --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-dmc-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_USI_DOCK_DMC_DEVICE (fu_usi_dock_dmc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUsiDockDmcDevice, + fu_usi_dock_dmc_device, + FU, + USI_DOCK_DMC_DEVICE, + FuUsbDevice) diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-mcu-device.c b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-mcu-device.c new file mode 100644 index 0000000000000000000000000000000000000000..6e13b842d29a50b15ae7e24d105eb8b584a28c59 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-mcu-device.c @@ -0,0 +1,763 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 Victor Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-usi-dock-child-device.h" +#include "fu-usi-dock-common.h" +#include "fu-usi-dock-dmc-device.h" +#include "fu-usi-dock-mcu-device.h" + +struct _FuUsiDockMcuDevice { + FuHidDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUsiDockMcuDevice, fu_usi_dock_mcu_device, FU_TYPE_HID_DEVICE) + +#define FU_USI_DOCK_MCU_DEVICE_TIMEOUT 5000 /* ms */ + +#define FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP (1 << 0) + +static gboolean +fu_usi_dock_mcu_device_tx(FuUsiDockMcuDevice *self, + guint8 tag2, + const guint8 *inbuf, + gsize inbufsz, + GError **error) +{ + UsiDockSetReportBuf tx_buffer = { + .id = USB_HID_REPORT_ID2, + .length = 0x3 + inbufsz, + .mcutag1 = 0xFE, + .mcutag2 = 0xFF, + .mcutag3 = tag2, + }; + + if (inbuf != NULL) { + if (!fu_memcpy_safe(tx_buffer.inbuf, + sizeof(tx_buffer.inbuf), + 0x0, /* dst */ + inbuf, + inbufsz, + 0x0, /* src */ + inbufsz, + error)) + return FALSE; + } + + /* special cases */ + if (tx_buffer.inbuf[0] == USBUID_ISP_INTERNAL_FW_CMD_UPDATE_FW) { + tx_buffer.inbuf[1] = 0xFF; + } + + return fu_hid_device_set_report(FU_HID_DEVICE(self), + USB_HID_REPORT_ID2, + (guint8 *)&tx_buffer, + sizeof(tx_buffer), + FU_USI_DOCK_MCU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error); +} + +static gboolean +fu_usi_dock_mcu_device_rx(FuUsiDockMcuDevice *self, + guint8 cmd, + guint8 *outbuf, + gsize outbufsz, + GError **error) +{ + guint8 buf[64] = {0}; + + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + USB_HID_REPORT_ID2, + buf, + sizeof(buf), + FU_USI_DOCK_MCU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) { + return FALSE; + } + if (buf[0] != USB_HID_REPORT_ID2) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid ID, expected 0x%02x, got 0x%02x", + USB_HID_REPORT_ID2, + buf[0]); + return FALSE; + } + if (buf[2] != 0xFE || buf[3] != 0xFF) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid tags, expected 0x%02x:0x%02x, got 0x%02x:0x%02x", + 0xFEu, + 0xFFu, + buf[2], + buf[3]); + return FALSE; + } + + if (outbuf != NULL) { + if (!fu_memcpy_safe(outbuf, + outbufsz, + 0x0, /* dst */ + buf, + sizeof(buf), + 0x5, /* src */ + outbufsz, + error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_txrx(FuUsiDockMcuDevice *self, + guint8 tag2, + const guint8 *inbuf, + gsize inbufsz, + guint8 *outbuf, + gsize outbufsz, + GError **error) +{ + if (!fu_usi_dock_mcu_device_tx(self, tag2, inbuf, inbufsz, error)) + return FALSE; + return fu_usi_dock_mcu_device_rx(self, USBUID_ISP_CMD_ALL, outbuf, outbufsz, error); +} + +static gboolean +fu_usi_dock_mcu_device_get_status(FuUsiDockMcuDevice *self, GError **error) +{ + guint8 cmd = USBUID_ISP_DEVICE_CMD_MCU_STATUS; + guint8 response = 0; + + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_MCU, + &cmd, + sizeof(cmd), + &response, + sizeof(response), + error)) + return FALSE; + if (response == 0x1) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_BUSY, "device is busy"); + return FALSE; + } + if (response == 0xFF) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "device timed out"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_enumerate_children(FuUsiDockMcuDevice *self, GError **error) +{ + guint8 inbuf[] = {USBUID_ISP_DEVICE_CMD_READ_MCU_VERSIONPAGE, + DP_VERSION_FROM_MCU | NIC_VERSION_FROM_MCU}; + guint8 outbuf[49] = {0x0}; + struct { + const gchar *name; + guint8 chip_idx; + gsize offset; + } components[] = { + {"DMC", FIRMWARE_IDX_DMC_PD, G_STRUCT_OFFSET(IspVersionInMcu_t, DMC)}, + {"PD", FIRMWARE_IDX_DP, G_STRUCT_OFFSET(IspVersionInMcu_t, PD)}, + {"DP5x", FIRMWARE_IDX_NONE, G_STRUCT_OFFSET(IspVersionInMcu_t, DP5x)}, + {"DP6x", FIRMWARE_IDX_NONE, G_STRUCT_OFFSET(IspVersionInMcu_t, DP6x)}, + {"TBT4", FIRMWARE_IDX_TBT4, G_STRUCT_OFFSET(IspVersionInMcu_t, TBT4)}, + {"USB3", FIRMWARE_IDX_USB3, G_STRUCT_OFFSET(IspVersionInMcu_t, USB3)}, + {"USB2", FIRMWARE_IDX_USB2, G_STRUCT_OFFSET(IspVersionInMcu_t, USB2)}, + {"AUDIO", FIRMWARE_IDX_AUDIO, G_STRUCT_OFFSET(IspVersionInMcu_t, AUDIO)}, + {"I255", FIRMWARE_IDX_I225, G_STRUCT_OFFSET(IspVersionInMcu_t, I255)}, + {"MCU", FIRMWARE_IDX_MCU, G_STRUCT_OFFSET(IspVersionInMcu_t, MCU)}, + {"bcdVersion", FIRMWARE_IDX_NONE, G_STRUCT_OFFSET(IspVersionInMcu_t, bcdVersion)}, + {NULL, 0, 0}}; + + /* assume DP and NIC in-use */ + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_MCU, + inbuf, + sizeof(inbuf), + outbuf, + sizeof(outbuf), + error)) + return FALSE; + + for (guint i = 0; components[i].name != NULL; i++) { + const guint8 *val = outbuf + components[i].offset; + g_autofree gchar *version = NULL; + g_autoptr(FuDevice) child = NULL; + + child = fu_usi_dock_child_new(fu_device_get_context(FU_DEVICE(self))); + if (g_strcmp0(components[i].name, "bcdVersion") == 0) { + if ((val[0] == 0x00 && val[1] == 0x00) || + (val[0] == 0xFF && val[1] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) { + version = g_strdup_printf("%x.%x.%x.%x", + val[0] >> 4, + val[0] & 0xFu, + val[1] >> 4, + val[1] & 0xFu); + fu_device_set_version_format(FU_DEVICE(self), + FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(FU_DEVICE(self), version); + } else { + version = g_strdup_printf("%x.%x.%02x", + val[0] & 0xFu, + val[0] >> 4, + val[1]); + g_debug("ignoring %s --> %s", components[i].name, version); + } + continue; + } else if (g_strcmp0(components[i].name, "DMC") == 0) { + if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || + (val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = g_strdup_printf("%d.%d.%d", val[2], val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(child, version); + fu_device_set_name(child, "Dock Management Controller"); + } else if (g_strcmp0(components[i].name, "PD") == 0) { + if ((val[1] == 0x00 && val[2] == 0x00 && val[3] == 0x00 && + val[4] == 0x00) || + (val[1] == 0xFF && val[2] == 0xFF && val[3] == 0xFF && + val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) { + version = + g_strdup_printf("%d.%d.%d.%d", val[3], val[4], val[1], val[2]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_QUAD); + } else { + version = g_strdup_printf("%d.%d.%d", val[2], val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + } + fu_device_set_version(child, version); + fu_device_set_name(child, "Power Delivery"); + } else if (g_strcmp0(components[i].name, "TBT4") == 0) { + if ((val[1] == 0x00 && val[2] == 0x00 && val[3] == 0x00) || + (val[1] == 0xFF && val[2] == 0xFF && val[3] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = g_strdup_printf("%02x.%02x.%02x", val[1], val[2], val[3]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(child, version); + fu_device_add_icon(child, "thunderbolt"); + fu_device_set_name(child, "Thunderbolt 4 Controller"); + } else if (g_strcmp0(components[i].name, "DP5x") == 0) { + if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || + (val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = g_strdup_printf("%d.%02d.%03d", val[2], val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(child, version); + fu_device_add_icon(child, "video-display"); + fu_device_set_name(child, "Display Port 5"); + } else if (g_strcmp0(components[i].name, "DP6x") == 0) { + if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || + (val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) { + version = + g_strdup_printf("%x.%x.%x.%x", val[3], val[4], val[2], val[1]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_name(child, "USB/PD HUB"); + } else { + version = g_strdup_printf("%d.%02d.%03d", val[2], val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_name(child, "Display Port 6"); + } + fu_device_set_version(child, version); + fu_device_add_icon(child, "video-display"); + } else if (g_strcmp0(components[i].name, "USB3") == 0) { + if ((val[3] == 0x00 && val[4] == 0x00) || + (val[3] == 0xFF && val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = g_strdup_printf("%02X%02X", val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_version(child, version); + fu_device_set_name(child, "USB 3 Hub"); + } else if (g_strcmp0(components[i].name, "USB2") == 0) { + if ((val[0] == 0x00 && val[1] == 0x00 && val[2] == 0x00 && val[3] == 0x00 && + val[4] == 0x00) || + (val[0] == 0xFF && val[1] == 0xFF && val[2] == 0xFF && val[3] == 0xFF && + val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = + g_strdup_printf("%c%c%c%c%c", val[0], val[1], val[2], val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(child, version); + fu_device_set_name(child, "USB 2 Hub"); + } else if (g_strcmp0(components[i].name, "AUDIO") == 0) { + if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || + (val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = g_strdup_printf("%02X-%02X-%02X", val[2], val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(child, version); + fu_device_set_name(child, "Audio Controller"); + } else if (g_strcmp0(components[i].name, "I255") == 0) { + if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || + (val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + version = g_strdup_printf("%x.%x.%x", val[2] >> 4, val[3], val[4]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(child, version); + fu_device_add_icon(child, "network-wired"); + fu_device_set_name(child, "Ethernet Adapter"); + } else if (g_strcmp0(components[i].name, "MCU") == 0) { + if ((val[0] == 0x00 && val[1] == 0x00) || + (val[0] == 0xFF && val[1] == 0xFF)) { + g_debug("ignoring %s", components[i].name); + continue; + } + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) { + version = g_strdup_printf("%x.%x.%x.%x", + val[0] >> 4, + val[0] & 0xFu, + val[1] >> 4, + val[1] & 0xFu); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_QUAD); + } else { + version = g_strdup_printf("%X.%X", val[0], val[1]); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_PLAIN); + } + fu_device_set_version(child, version); + fu_device_set_name(child, "Dock Management Controller"); + } else { + g_warning("unhandled %s", components[i].name); + } + + /* add virtual device */ + fu_device_add_instance_u16(child, + "VID", + fu_usb_device_get_vid(FU_USB_DEVICE(self))); + fu_device_add_instance_u16(child, + "PID", + fu_usb_device_get_pid(FU_USB_DEVICE(self))); + fu_device_add_instance_str(child, "CID", components[i].name); + if (!fu_device_build_instance_id(child, error, "USB", "VID", "PID", "CID", NULL)) + return FALSE; + if (fu_device_get_name(child) == NULL) + fu_device_set_name(child, components[i].name); + fu_device_set_logical_id(child, components[i].name); + fu_usi_dock_child_device_set_chip_idx(FU_USI_DOCK_CHILD_DEVICE(child), + components[i].chip_idx); + fu_device_add_child(FU_DEVICE(self), child); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_setup(FuDevice *device, GError **error) +{ + FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_usi_dock_mcu_device_parent_class)->setup(device, error)) + return FALSE; + + /* get status and component versions */ + if (!fu_usi_dock_mcu_device_get_status(self, error)) + return FALSE; + if (!fu_usi_dock_mcu_device_enumerate_children(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_write_chunk(FuUsiDockMcuDevice *self, FuChunk *chk, GError **error) +{ + guint8 buf[64] = {0x0}; + guint32 length = 0; + guint32 pagesize = fu_chunk_get_data_sz(chk); + + while (pagesize != 0) { + memset(buf, 0x0, sizeof(buf)); + buf[63] = TAG_TAG2_MASS_DATA_SPI; + buf[0] = USB_HID_REPORT_ID2; + + /* set length and buffer */ + if (pagesize >= TX_ISP_LENGTH) { + length = TX_ISP_LENGTH; + pagesize -= TX_ISP_LENGTH; + } else { + length = pagesize; + pagesize = 0; + } + buf[1] = length; + + /* SetReport */ + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x2, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_chunk_get_data_sz(chk) - pagesize - length, /* src */ + length, + error)) + return FALSE; + + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + USB_HID_REPORT_ID2, + buf, + sizeof(buf), + FU_USI_DOCK_MCU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + /* GetReport */ + memset(buf, 0x0, sizeof(buf)); + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + USB_HID_REPORT_ID2, + buf, + sizeof(buf), + FU_USI_DOCK_MCU_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) { + return FALSE; + } + if (buf[0] != USB_HID_REPORT_ID2) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid ID, expected 0x%02x, got 0x%02x", + USB_HID_REPORT_ID2, + buf[0]); + return FALSE; + } + if (buf[63] != TAG_TAG2_CMD_SPI) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid tag2, expected 0x%02x, got 0x%02x", + (guint)TAG_TAG2_CMD_SPI, + buf[58]); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_write_chunks(FuUsiDockMcuDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_usi_dock_mcu_device_write_chunk(self, chk, error)) { + g_prefix_error(error, "failed to write chunk 0x%x", i); + return FALSE; + } + fu_progress_step_done(progress); + } + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_wait_for_spi_ready_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device); + guint8 buf[] = {USBUID_ISP_DEVICE_CMD_FWBUFER_READ_STATUS}; + guint8 val = 0; + + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_SPI, + buf, + sizeof(buf), + &val, + sizeof(val), + error)) + return FALSE; + if (val != SPI_STATE_READY) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "SPI state is %s [0x%02x]", + fu_usi_dock_spi_state_to_string(val), + val); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_wait_for_spi_initial_ready_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device); + guint8 buf[] = {USBUID_ISP_DEVICE_CMD_FWBUFER_INITIAL}; + guint8 val = 0; + + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_SPI, + buf, + sizeof(buf), + &val, + sizeof(val), + error)) + return FALSE; + if (val != SPI_STATE_READY) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "SPI state is %s [0x%02x]", + fu_usi_dock_spi_state_to_string(val), + val); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_wait_for_checksum_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device); + + if (!fu_usi_dock_mcu_device_rx(self, + USBUID_ISP_CMD_ALL, + (guint8 *)user_data, + sizeof(guint8), + error)) + return FALSE; + + /* success */ + return TRUE; +} + +gboolean +fu_usi_dock_mcu_device_write_firmware_with_idx(FuUsiDockMcuDevice *self, + FuFirmware *firmware, + guint8 chip_idx, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + guint8 cmd; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + guint8 checksum = 0xFF; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 5, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 69, "write-external"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 25, "wait-for-checksum"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "internal-flash"); + + /* initial external flash */ + if (!fu_device_retry(FU_DEVICE(self), + fu_usi_dock_mcu_device_wait_for_spi_initial_ready_cb, + 30, + NULL, + error)) { + g_prefix_error(error, "failed to wait for initial: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* erase external flash */ + cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_ERASE_FLASH; + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_SPI, + &cmd, + sizeof(cmd), + NULL, + 0x0, + error)) + return FALSE; + if (!fu_device_retry(FU_DEVICE(self), + fu_usi_dock_mcu_device_wait_for_spi_ready_cb, + 30, + NULL, + error)) { + g_prefix_error(error, "failed to wait for erase: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* write external flash */ + cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_PROGRAM; + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_SPI, + &cmd, + sizeof(cmd), + NULL, + 0x0, + error)) + return FALSE; + + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, W25Q16DV_PAGE_SIZE); + if (!fu_usi_dock_mcu_device_write_chunks(self, + chunks, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* file transfer – finished */ + cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_TRANSFER_FINISH; + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_SPI, + &cmd, + sizeof(cmd), + NULL, + 0x0, + error)) + return FALSE; + + /* MCU checksum */ + if (!fu_device_retry(FU_DEVICE(self), + fu_usi_dock_mcu_device_wait_for_checksum_cb, + 300, + &checksum, + error)) { + g_prefix_error(error, "failed to wait for checksum: "); + return FALSE; + } + + if (checksum != 0x0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "invalid checksum result for CMD_FWBUFER_CHECKSUM, got 0x%02x", + checksum); + return FALSE; + } + fu_progress_step_done(progress); + + /* internal flash */ + cmd = USBUID_ISP_INTERNAL_FW_CMD_UPDATE_FW; + if (!fu_usi_dock_mcu_device_txrx(self, + TAG_TAG2_CMD_MCU, + &cmd, + sizeof(cmd), + NULL, + 0x0, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_usi_dock_mcu_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + return fu_usi_dock_mcu_device_write_firmware_with_idx(FU_USI_DOCK_MCU_DEVICE(device), + firmware, + 0xFF, /* all */ + progress, + flags, + error); +} + +static gboolean +fu_usi_dock_mcu_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + fu_device_set_remove_delay(device, 900000); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static void +fu_usi_dock_mcu_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 6, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_usi_dock_mcu_device_init(FuUsiDockMcuDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + + fu_device_register_private_flag(FU_DEVICE(self), + FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP, + "verfmt-hp"); + fu_hid_device_add_flag(FU_HID_DEVICE(self), FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER); + fu_device_add_protocol(FU_DEVICE(self), "com.usi.dock"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_retry_set_delay(FU_DEVICE(self), 1000); + fu_device_add_icon(FU_DEVICE(self), "dock"); +} + +static void +fu_usi_dock_mcu_device_class_init(FuUsiDockMcuDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + klass_device->write_firmware = fu_usi_dock_mcu_device_write_firmware; + klass_device->attach = fu_usi_dock_mcu_device_attach; + klass_device->setup = fu_usi_dock_mcu_device_setup; + klass_device->set_progress = fu_usi_dock_mcu_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-mcu-device.h b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-mcu-device.h new file mode 100644 index 0000000000000000000000000000000000000000..251d8ad39cb0704497582dad59ffeec7fef54a7b --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-mcu-device.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_USI_DOCK_MCU_DEVICE (fu_usi_dock_mcu_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUsiDockMcuDevice, + fu_usi_dock_mcu_device, + FU, + USI_DOCK_MCU_DEVICE, + FuHidDevice) +gboolean +fu_usi_dock_mcu_device_write_firmware_with_idx(FuUsiDockMcuDevice *self, + FuFirmware *firmware, + guint8 chip_idx, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-plugin.c b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..1ad046d37e4a2cf2f06f9d6ff89a67172e76b70a --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-plugin.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 Richard Hughes + * Copyright (C) 2021 Victor Cheng + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-usi-dock-dmc-device.h" +#include "fu-usi-dock-mcu-device.h" +#include "fu-usi-dock-plugin.h" + +#define USI_DOCK_TBT_INSTANCE_ID "THUNDERBOLT\\VEN_0108&DEV_2031" + +struct _FuUsiDockPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuUsiDockPlugin, fu_usi_dock_plugin, FU_TYPE_PLUGIN) + +static void +fu_usi_dock_plugin_dmc_registered(FuPlugin *plugin, FuDevice *device) +{ + /* usb device from thunderbolt plugin */ + if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") == 0 && + fu_device_has_guid(device, USI_DOCK_TBT_INSTANCE_ID)) { + g_autofree gchar *msg = NULL; + msg = g_strdup_printf("firmware update inhibited by [%s] plugin", + fu_plugin_get_name(plugin)); + fu_device_inhibit(device, "usb-blocked", msg); + } +} + +static void +fu_usi_dock_plugin_init(FuUsiDockPlugin *self) +{ +} + +static void +fu_usi_dock_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_MCU_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_DMC_DEVICE); +} + +static void +fu_usi_dock_plugin_class_init(FuUsiDockPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_usi_dock_plugin_constructed; + plugin_class->device_registered = fu_usi_dock_plugin_dmc_registered; +} diff --git a/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-plugin.h b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..f539eeec35fe6099b0e054e96ef4a3259556c816 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/fu-usi-dock-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuUsiDockPlugin, fu_usi_dock_plugin, FU, USI_DOCK_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/usi-dock/meson.build b/fwupd-1.8.6/plugins/usi-dock/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..eb9765f15f567cf8e8deaee2aa767a6a4646e498 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/meson.build @@ -0,0 +1,18 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginUsiDock"'] + +plugin_quirks += files('usi-dock.quirk') +plugin_builtins += static_library('fu_plugin_usi_dock', + sources: [ + 'fu-usi-dock-common.c', + 'fu-usi-dock-child-device.c', + 'fu-usi-dock-dmc-device.c', + 'fu-usi-dock-mcu-device.c', + 'fu-usi-dock-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +endif diff --git a/fwupd-1.8.6/plugins/usi-dock/usi-dock.quirk b/fwupd-1.8.6/plugins/usi-dock/usi-dock.quirk new file mode 100644 index 0000000000000000000000000000000000000000..98e5f20483992ee058abdb66320571822414d448 --- /dev/null +++ b/fwupd-1.8.6/plugins/usi-dock/usi-dock.quirk @@ -0,0 +1,25 @@ +[USB\VID_17EF&PID_30B4&CID_40B0] +Plugin = usi_dock +GType = FuUsiDockMcuDevice +Name = ThinkPad Thunderbolt 4 Dock + +[USB\VID_17EF&PID_30B4&CID_40B1] +Plugin = usi_dock +GType = FuUsiDockMcuDevice +Name = ThinkPad Universal Thunderbolt 4 Smart Dock + +[USB\VID_17EF&PID_30B4] +Plugin = usi_dock +GType = FuUsiDockMcuDevice +Name = ThinkPad Thunderbolt 4 Dock + +[USB\VID_17EF&PID_30B5] +Plugin = usi_dock +GType = FuUsiDockDmcDevice +ParentGuid = USB\VID_17EF&PID_30B4 + +[USB\VID_03F0&PID_0505] +Plugin = usi_dock +GType = FuUsiDockMcuDevice +Name = USB-C G2 Multiport Hub MCU Controller +Flags = verfmt-hp diff --git a/fwupd-1.8.6/plugins/vbe/README.md b/fwupd-1.8.6/plugins/vbe/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6b6d3366a807bf5983aaaf6cf9748748e0c1dcf5 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/README.md @@ -0,0 +1,265 @@ +# Verified Boot for Embedded (VBE) + +## Introduction + +This plugin is used for boards which use the VBE system. This allows the +platform to be updated from user space. + +## Firmware Format + +Firmware updates are held within a CAB file, a Windows format which allows +files to be collected and compressed, similar to a tar file. The CAB file can be +created using the `gcab` tool, as shown in the VBE `example` directory. + +Inside the CAB file is the firmware itself, in Flat Image Tree (FIT) format. +This is typically called `firmware.fit` and can be created by the `mkimage` tool, +or using device tree tools such as `dtc` and `fdtput`. + +The FIT supports multiple configuration, each of which is intended to update a +particular board type. This allows the same firmware update to be used for a +number of related boards, including sharing certain images, at the expense of +increasing the update size. + +A typical FIT file looks like this: + +```fdt +/ { + timestamp = <0x62a74c6c>; + description = "Firmware image with one or more FDT blobs"; + creator = "U-Boot mkimage 2021.01+dfsg-3ubuntu0~20.04.4"; + #address-cells = <0x00000001>; + images { + firmware-1 { + description = "v1.2.4"; + type = "firmware"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + store-offset = <0x10000>; + data = <...>; + hash-1 { + value = <0xa738ea1c>; + algo = "crc32"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + version = "1.2.4"; + compatible = "pine64,rockpro64-v2.1", "pine64,rockpro64"; + firmware = "firmware-1"; + }; + }; +}; +``` + +Each configuration includes the version of the update, the board(s) it is +compatible with and a list of firmware 'images' in the `firmware` property. +The `compatible` property is optional in the case where there is only one +configuration that applies to all boards that receive this update. + +The images themselves are in a separate `images` node. Each image includes +various fields to indicate its type as well as option hashes. The `data` +property contains the actual firmware data. + +Multiple images can be including in each configuration, but each must have a +different `store-offset` property, indicating the offset from the start of the +firmware region where this particular image is kept. The default store offset +is zero, which is typically suitable if there is only one image. + +It is common to use an 'external' FIT, meaning that the `data` property is +omitted and the data is placed in the same file after the FIT itself. In this +case `data-offset` and `data-size` allow the data to be relocated. The data is +stored started at the next 32-bit aligned file position after the FIT. The +`mkimage` tool allows converting a FIT to an external FIT, with the `-E` flag. + +See the `plugins/vbe/example` directory for an example of building a firmware +update. + +## Operation + +The daemon will decompress the cabinet archive and extract the firmware blob, +then write it to storage. The firmware will be used on the next reboot, so no +changes to firmware happen until there is a reboot. + +This plugin supports the following protocol ID: + +* `org.vbe` + +## Board information + +VBE requires the board firmware to provide information about the firmware within +the device tree passed to the Operating System. + +For systems without device tree, currently the only option is to install a file +for use by fwupd, typically in `/var/lib/fwupd/vbe/system.dtb`. + +In either case, there must be one or more nodes with the required information. +The format depends on which VBE method is used, but the information must be in +a subnode of `chosen/fwupd`. Here is an example: + +```fdt +/ { + compatible = "pine64,rockpro64-v2.1"; + chosen { + fwupd { + firmware { + compatible = "fwupd,vbe-simple"; + cur-version = "1.2.3"; + storage = "/tmp/testfw"; + area-start = <0x100000>; + area-size = <0x100000>; + skip-offset = <0x8000>; + part-uuid = "62db0ccf-03"; + part-id = "3"; + }; + }; + }; +}; +``` + +The first compatible string indicates the board type. Typically it is a single +string, but a list is also supported. VBE matches the string(s) here against +those in the update itself, using the configuration which has the earliest match +(within the list) to the board's compatible string(s). Since more specific +boards are at the start of the compatible string, this allows for one +configuration to have an update for a specific board, while another provides an +update for all other boards. + +The compatible string of the firmware update indicates the VBE method that is +being used. It must have `fwupd` as the manufacturer and the model must start +with `vbe-`. + +Other properties within the node are determined by that method, but some are +common to all: + +* `compatible` - indicates the VBE method to use, in the form `fwupd,`. +* `cur-version` - indicates the version that is currently installed, if this is + known by the firmware. Note that fwupd keeps its own information about the + installed version, so this is not needed by fwupd itself. But it can be useful + for other utilities. + +## Important files + +* `/var/lib/fwupd/vbe/system.dtb` - this file holds the system + information. It overrides the system device tree if any, meaning that the + system device tree is ignored if this file is preset. For systems that don't + support device tree (e.g ACPI systems), this file is needed for VBE to work + +## Available VBE methods + +Note that all VBE methods must subclass `FuVbeDevice` since it provides access to +the device tree and the VBE directory, among other things. + +At present only one method is available: + +* `fwupd,vbe-simple` - writes a single copy of the firmware to media + +### vbe-simple + +With this, only a single copy of the firmware is available so if the write +fails, the board may not boot. This is implemented in the `vbe-simple.c` file and +has the device GUID `ea1b96eb-a430-4033-8708-498b6d98178b` within fwupd. + +Properties for this method are: + +* `compatible` - must be `fwupd,vbe-simple` +* `storage` - device to store firmware in. Two options are supported: a full path + such as `/dev/mmcblk1` or a device number, like `mmc1`. Note that only mmc + is currently supported. +* `area-start` - start offset in bytes of the firmware area within the storage +* `area-size` - size in bytes of the firmware area within the storage +* `skip-offset` - offset to preserve at the start of the firmware area. This + means that the first part of the image is ignored, with just the latter part + being written. For example, if this is 0x200 then the first 512 bytes of the + image (which must be present in the image) are skipped and the bytes after + that are written to the store offset. +* `part-uuid` - the UUID of the partition containing the fwupd state. This is not + used by fwupd at present but may allow the bootloader to check the fwupd + state on boot +* `part-id` - the partition number containing the fwupd state. This is not + used by fwupd at present but may allow the bootloader to check the fwupd + state on boot + +## Finding out more + +You can use the U-Boot mailing list or `u-boot` IRC on `libera.chat` to ask +questions specific to VBE and firmware. + +## Sending patches + +Use the [fwupd developer site](https://fwupd.org/lvfs/docs/developers) to find +information about downloading the code, submitting bugs/feature requests and +sending patches. + +## Vendor ID Security + +This does not update USB devices and thus requires no vendor ID set. + +## External Interface Access + +This plugin requires access to system firmware, e.g. via a file or an eMMC +device. + +## Documentation + +The following documents may help in understanding VBE: + +* [VBE](https://docs.google.com/document/d/e/2PACX-1vQjXLPWMIyVktaTMf8edHZYDrEvMYD_iNzIj1FgPmKF37fpglAC47Tt5cvPBC5fvTdoK-GA5Zv1wifo/pub) +* [VBE Bootflows](https://docs.google.com/document/d/e/2PACX-1vR0OzhuyRJQ8kdeOibS3xB1rVFy3J4M_QKTM5-3vPIBNcdvR0W8EXu9ymG-yWfqthzWoM4JUNhqwydN/pub) +* [VBE Firmware update](https://docs.google.com/document/d/e/2PACX-1vTnlIL17vVbl6TVoTHWYMED0bme7oHHNk-g5VGxblbPiKIdGDALE1HKId8Go5f0g1eziLsv4h9bocbk/pub) +* [FIT](https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt) +* [U-Boot Standard boot](https://u-boot.readthedocs.io/en/latest/develop/bootstd.html) + +## Useful information + +For development you may find the following useful. + +To set up the vbe directory: + +```bash + mkdir /var/lib/fwupd/vbe + chmod a+rwx /var/lib/fwupd/vbe + dtc /path/to/fwupd/plugins/vbe/example/test.dts -o \ + /var/lib/fwupd/vbe/system.dtb +``` + +To build the example: + +```bash + sudo apt install appstream-util u-boot-tools + cd /path/to/fwupd/plugins/vbe/example + dd if=/dev/zero of=update.bin bs=1M count=1 + ./build.sh +``` + +To install the cab on your development computer: + +```bash + # Set up the test file + dd if=/dev/zero of=/tmp/testfw bs=1M count=3 + + sudo build/src/fwupdtool install plugins/vbe/example/Vbe-Board-1.2.4.cab \ + bb3b05a8-ebef-11ec-be98-d3a15278be95 +``` + +To dump out the firmware: + +```bash + sudo rm fw.bin; sudo build/src/fwupdtool firmware-dump fw.bin \ + bb3b05a8-ebef-11ec-be98-d3a15278be95 +``` + +## To do + +Add an update method that actually supports verified boot, including maintaining +update state. The update happens in two passes, the first installing firmware +in the 'B' slot and the second writing it to the 'A' slot, to avoid bricking the +device in the event of a write failure or non-functional firmware. + +Figure out how to select the right update for a board out of many that might be +on LVFS. At present the selection mechanism only works within the FIT +configuration. This probably needs to use the `` piece +of the xml file to specify an identifier for the family of boards supported by +the update. diff --git a/fwupd-1.8.6/plugins/vbe/example/build.sh b/fwupd-1.8.6/plugins/vbe/example/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..6de505b1a9a2f6d131a7991356ed1b6dd781950f --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/example/build.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +appstream-util validate-relax com.Vbe.Board.metainfo.xml + +#dd if=/dev/zero of=update.bin bs=1M count=1 +mkimage -D "-p 0x100" -n "v1.2.4" -O U-Boot -A arm64 -C none -T firmware -f auto -d update.bin firmware.fit +fdtput firmware.fit -t s /configurations/conf-1 version "1.2.4" + +# Make the data external, now that we have finished fiddling with the FDT +mkimage -E -F firmware.fit + +gcab --create --zip --nopath Vbe-Board-1.2.4.cab firmware.fit com.Vbe.Board.metainfo.xml diff --git a/fwupd-1.8.6/plugins/vbe/example/com.Vbe.Board.metainfo.xml b/fwupd-1.8.6/plugins/vbe/example/com.Vbe.Board.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..c355260545ea061a36cdf0b3ae6c30847c2935d5 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/example/com.Vbe.Board.metainfo.xml @@ -0,0 +1,37 @@ + + + + com.Vbe.Laptop.firmware + VBE BoardFirmware + System firmware for a board, for use with VBE + +

    + The board can be updated using Verified Boot for Embedded (VBE). +

    +
    + + + bb3b05a8-ebef-11ec-be98-d3a15278be95 + + http://www.TBD.com/ + CC0-1.0 + Proprietary + Vbe + + + + +

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

    +
    +
    +
    + + + + org.freedesktop.fwupd + + +
    diff --git a/fwupd-1.8.6/plugins/vbe/example/my-file b/fwupd-1.8.6/plugins/vbe/example/my-file new file mode 100644 index 0000000000000000000000000000000000000000..90bfcb510602aa11ae53a42dcec18ea39fbd8cec --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/example/my-file @@ -0,0 +1 @@ +this is a test diff --git a/fwupd-1.8.6/plugins/vbe/example/rockpro64.dts b/fwupd-1.8.6/plugins/vbe/example/rockpro64.dts new file mode 100644 index 0000000000000000000000000000000000000000..c03852159afbbcc07b0d4517cf4fa4caf71493f9 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/example/rockpro64.dts @@ -0,0 +1,24 @@ +/dts-v1/; + +/* + * This file can be used to try our a VBE simple update on ROCKPro64 using + * Debian or some other distribution. + */ + +/ { + compatible = "pine64,rockpro64-v2.1"; + + chosen { + fwupd { + firmware { + compatible = "fwupd,vbe-simple"; + cur-version = "1.2.3"; + bootloader-version = "2022.01"; + storage = "mmc1"; + area-start = <0x0>; + area-size = <0x1000000>; + skip-offset = <0x8000>; + }; + }; + }; +}; diff --git a/fwupd-1.8.6/plugins/vbe/example/test.dts b/fwupd-1.8.6/plugins/vbe/example/test.dts new file mode 100644 index 0000000000000000000000000000000000000000..d306369210be25961c355e46848ebe5f59f3763d --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/example/test.dts @@ -0,0 +1,27 @@ +/dts-v1/; + +/* + * This file is suitable for testing on the host device as it uses a local + * file. To set it up: + * + */ + +/ { + compatible = "pine64,rockpro64-v2.1"; + + chosen { + fwupd { + firmware { + compatible = "fwupd,vbe-simple"; + cur-version = "1.2.3"; + bootloader-version = "2022.01"; + storage = "/tmp/testfw"; + area-start = <0x100000>; + area-size = <0x100000>; + skip-offset = <0x8000>; + part-uuid = "62db0ccf-03"; + part-id = "3"; + }; + }; + }; +}; diff --git a/fwupd-1.8.6/plugins/vbe/fu-vbe-device.c b/fwupd-1.8.6/plugins/vbe/fu-vbe-device.c new file mode 100644 index 0000000000000000000000000000000000000000..8688a95e9a8b6b86df04de79cd69bcfb19c8d87e --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/fu-vbe-device.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Google LLC + * Written by Simon Glass + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vbe-device.h" + +enum { PROP_0, PROP_VBE_METHOD, PROP_FDT_ROOT, PROP_FDT_NODE, PROP_VBE_DIR, PROP_LAST }; + +typedef struct { + FuFdtImage *fdt_root; + FuFdtImage *fdt_node; + gchar **compatible; + gchar *vbe_dir; +} FuVbeDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuVbeDevice, fu_vbe_device, FU_TYPE_DEVICE) +#define GET_PRIVATE(o) (fu_vbe_device_get_instance_private(o)) + +static void +fu_vbe_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuVbeDevice *self = FU_VBE_DEVICE(device); + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + + fu_string_append(str, idt, "VbeDir", priv->vbe_dir); + if (priv->compatible != NULL) { + g_autofree gchar *tmp = g_strjoinv(":", priv->compatible); + fu_string_append(str, idt, "Compatible", tmp); + } +} + +FuFdtImage * +fu_vbe_device_get_fdt_root(FuVbeDevice *self) +{ + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_VBE_DEVICE(self), NULL); + return priv->fdt_root; +} + +FuFdtImage * +fu_vbe_device_get_fdt_node(FuVbeDevice *self) +{ + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_VBE_DEVICE(self), NULL); + return priv->fdt_node; +} + +gchar ** +fu_vbe_device_get_compatible(FuVbeDevice *self) +{ + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_VBE_DEVICE(self), NULL); + return priv->compatible; +} + +const gchar * +fu_vbe_device_get_dir(FuVbeDevice *self) +{ + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_VBE_DEVICE(self), NULL); + return priv->vbe_dir; +} + +static void +fu_vbe_device_init(FuVbeDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_protocol(FU_DEVICE(self), "org.vbe"); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED); + fu_device_set_physical_id(FU_DEVICE(self), "vbe"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_add_icon(FU_DEVICE(self), "computer"); +} + +static gboolean +fu_vbe_device_probe(FuDevice *device, GError **error) +{ + FuVbeDevice *self = FU_VBE_DEVICE(device); + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *version = NULL; + g_autofree gchar *version_bootloader = NULL; + + g_return_val_if_fail(FU_IS_VBE_DEVICE(device), FALSE); + + /* get a list of compatible strings */ + if (!fu_fdt_image_get_attr_strlist(priv->fdt_root, + FU_FIT_FIRMWARE_ATTR_COMPATIBLE, + &priv->compatible, + error)) + return FALSE; + + /* get baseclass shared attributes */ + fu_fdt_image_get_attr_str(priv->fdt_node, "cur-version", &version, NULL); + if (version != NULL) + fu_device_set_version(device, version); + fu_fdt_image_get_attr_str(priv->fdt_node, "bootloader-version", &version_bootloader, NULL); + if (version_bootloader != NULL) + fu_device_set_version_bootloader(device, version_bootloader); + + /* success */ + return TRUE; +} + +static void +fu_vbe_device_constructed(GObject *obj) +{ + FuVbeDevice *self = FU_VBE_DEVICE(obj); + fu_device_add_instance_id(FU_DEVICE(self), "main-system-firmware"); +} + +static void +fu_vbe_device_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuVbeDevice *self = FU_VBE_DEVICE(obj); + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_FDT_ROOT: + g_value_set_object(value, priv->fdt_root); + break; + case PROP_FDT_NODE: + g_value_set_object(value, priv->fdt_node); + break; + case PROP_VBE_DIR: + g_value_set_string(value, priv->vbe_dir); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +fu_vbe_device_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuVbeDevice *self = FU_VBE_DEVICE(obj); + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_FDT_ROOT: + g_set_object(&priv->fdt_root, g_value_get_object(value)); + break; + case PROP_FDT_NODE: + g_set_object(&priv->fdt_node, g_value_get_object(value)); + break; + case PROP_VBE_DIR: + g_free(priv->vbe_dir); + priv->vbe_dir = g_strdup(g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void +fu_vbe_device_finalize(GObject *obj) +{ + FuVbeDevice *self = FU_VBE_DEVICE(obj); + FuVbeDevicePrivate *priv = GET_PRIVATE(self); + g_free(priv->vbe_dir); + g_strfreev(priv->compatible); + if (priv->fdt_root != NULL) + g_object_unref(priv->fdt_root); + if (priv->fdt_node != NULL) + g_object_unref(priv->fdt_node); + G_OBJECT_CLASS(fu_vbe_device_parent_class)->finalize(obj); +} + +static void +fu_vbe_device_class_init(FuVbeDeviceClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + + object_class->get_property = fu_vbe_device_get_property; + object_class->set_property = fu_vbe_device_set_property; + + pspec = + g_param_spec_object("fdt-root", + NULL, + "FDT root containing method parameters", + FU_TYPE_FDT_IMAGE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FDT_ROOT, pspec); + + pspec = + g_param_spec_object("fdt-node", + NULL, + "FDT image within the device tree containing method parameters'", + FU_TYPE_FDT_IMAGE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_FDT_NODE, pspec); + + pspec = + g_param_spec_string("vbe-dir", + NULL, + "Directory containing state file for each VBE method", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_VBE_DIR, pspec); + + object_class->constructed = fu_vbe_device_constructed; + object_class->finalize = fu_vbe_device_finalize; + klass_device->to_string = fu_vbe_device_to_string; + klass_device->probe = fu_vbe_device_probe; +} diff --git a/fwupd-1.8.6/plugins/vbe/fu-vbe-device.h b/fwupd-1.8.6/plugins/vbe/fu-vbe-device.h new file mode 100644 index 0000000000000000000000000000000000000000..049c96a3ef770a32a3b32facb127f95fb6d64430 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/fu-vbe-device.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 Google LLC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_VBE_DEVICE (fu_vbe_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuVbeDevice, fu_vbe_device, FU, VBE_DEVICE, FuDevice) + +struct _FuVbeDeviceClass { + FuDeviceClass parent_class; +}; + +FuFdtImage * +fu_vbe_device_get_fdt_root(FuVbeDevice *self); +FuFdtImage * +fu_vbe_device_get_fdt_node(FuVbeDevice *self); +gchar ** +fu_vbe_device_get_compatible(FuVbeDevice *self); +const gchar * +fu_vbe_device_get_dir(FuVbeDevice *self); diff --git a/fwupd-1.8.6/plugins/vbe/fu-vbe-plugin.c b/fwupd-1.8.6/plugins/vbe/fu-vbe-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..aba93bd2783ef3b8093ed92bff7518d17bb40392 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/fu-vbe-plugin.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Google LLC + * Written by Simon Glass + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vbe-plugin.h" +#include "fu-vbe-simple-device.h" + +struct _FuVbePlugin { + FuPlugin parent_instance; + FuFirmware *fdt; + gchar *vbe_dir; +}; + +G_DEFINE_TYPE(FuVbePlugin, fu_vbe_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_vbe_plugin_coldplug_img(FuPlugin *plugin, + FuFdtImage *fdt_root, + FuFdtImage *fdt_node, + GError **error) +{ + FuVbePlugin *self = FU_VBE_PLUGIN(plugin); + GType device_gtype = G_TYPE_INVALID; + g_autofree gchar *compatible = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(FuDevice) dev = NULL; + + /* we expect 'fwupd,vbe-' */ + if (!fu_fdt_image_get_attr_str(fdt_node, + FU_FIT_FIRMWARE_ATTR_COMPATIBLE, + &compatible, + error)) { + g_prefix_error(error, "missing update mechanism: "); + return FALSE; + } + split = g_strsplit(compatible, ",", 2); + if (g_strv_length(split) != 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "update mechanism is invalid: %s", + compatible); + return FALSE; + } + if (g_strcmp0(split[0], "fwupd") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "update mechanism should have manufacturer of fwupd: %s", + split[0]); + return FALSE; + } + + /* skip past 'vbe-' */ + if (!g_str_has_prefix(split[1], "vbe-")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "update mechanism is missing vbe prefix: %s", + split[1]); + return FALSE; + } + if (g_strcmp0(split[1], "vbe-simple") == 0) { + device_gtype = FU_TYPE_VBE_SIMPLE_DEVICE; + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no driver for VBE method '%s'", + split[1]); + return FALSE; + } + + /* success */ + dev = g_object_new(device_gtype, + "context", + fu_plugin_get_context(plugin), + "fdt-root", + fdt_root, + "fdt-node", + fdt_node, + "vbe-dir", + self->vbe_dir, + NULL); + fu_plugin_device_add(plugin, dev); + return TRUE; +} + +static gboolean +fu_vbe_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuVbePlugin *self = FU_VBE_PLUGIN(plugin); + g_autoptr(FuFdtImage) fdt_root = NULL; + g_autoptr(GPtrArray) fdt_imgs = NULL; + + /* get compatible from root node */ + fdt_root = + fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(self->fdt), "/chosen/fwupd", error); + if (fdt_root == NULL) + return FALSE; + fdt_imgs = fu_firmware_get_images(FU_FIRMWARE(fdt_root)); + for (guint i = 0; i < fdt_imgs->len; i++) { + FuFdtImage *fdt_node = g_ptr_array_index(fdt_imgs, i); + g_autoptr(GError) error_local = NULL; + if (!fu_vbe_plugin_coldplug_img(plugin, fdt_root, fdt_node, &error_local)) { + g_warning("%s", error_local->message); + continue; + } + } + + /* nothing found? */ + if (fu_plugin_get_devices(plugin)->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no valid VBE update mechanism found"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GFile * +fu_vbe_plugin_get_bfname(FuPlugin *plugin) +{ + FuVbePlugin *self = FU_VBE_PLUGIN(plugin); + g_autofree gchar *bfname_local = NULL; + g_autofree gchar *bfname_sys = NULL; + g_autofree gchar *sysfsdir = NULL; + + /* look for override first, fall back to system value */ + bfname_local = g_build_filename(self->vbe_dir, "system.dtb", NULL); + if (g_file_test(bfname_local, G_FILE_TEST_EXISTS)) + return g_file_new_for_path(bfname_local); + + /* actual hardware value */ + sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); + bfname_sys = g_build_filename(sysfsdir, "fdt", NULL); + return g_file_new_for_path(bfname_sys); +} + +static gboolean +fu_vbe_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuVbePlugin *self = FU_VBE_PLUGIN(plugin); + g_autoptr(GFile) file = NULL; + + /* look for override first, fall back to system value */ + file = fu_vbe_plugin_get_bfname(plugin); + if (!fu_firmware_parse_file(self->fdt, file, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) { + g_prefix_error(error, "failed to parse FDT: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_vbe_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) +{ + FuVbePlugin *self = FU_VBE_PLUGIN(plugin); + fu_string_append(str, idt, "VbeDir", self->vbe_dir); +} + +static void +fu_vbe_plugin_init(FuVbePlugin *self) +{ + g_autofree gchar *localstatedir_pkg = NULL; + + /* where we can store the override and also image state */ + localstatedir_pkg = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + self->vbe_dir = g_build_filename(localstatedir_pkg, "vbe", NULL); + self->fdt = fu_fdt_firmware_new(); +} + +static void +fu_vbe_finalize(GObject *obj) +{ + FuVbePlugin *self = FU_VBE_PLUGIN(obj); + g_free(self->vbe_dir); + g_object_unref(self->fdt); + G_OBJECT_CLASS(fu_vbe_plugin_parent_class)->finalize(obj); +} + +static void +fu_vbe_plugin_class_init(FuVbePluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_vbe_finalize; + plugin_class->to_string = fu_vbe_plugin_to_string; + plugin_class->startup = fu_vbe_plugin_startup; + plugin_class->coldplug = fu_vbe_plugin_coldplug; +} diff --git a/fwupd-1.8.6/plugins/vbe/fu-vbe-plugin.h b/fwupd-1.8.6/plugins/vbe/fu-vbe-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..96438ad0fae766d616c547628f7152eaadaab646 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/fu-vbe-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuVbePlugin, fu_vbe_plugin, FU, VBE_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/vbe/fu-vbe-simple-device.c b/fwupd-1.8.6/plugins/vbe/fu-vbe-simple-device.c new file mode 100644 index 0000000000000000000000000000000000000000..85e7f9ac903a89ba17930c56d78f8fb33210ce4b --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/fu-vbe-simple-device.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2022 Richard Hughes + * Copyright (C) 2022 Google LLC + * Written by Simon Glass + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#include + +#include "fu-vbe-simple-device.h" + +/** + * @skip_offset: This allows an initial part of the image to be skipped when + * writing. This means that the first part of the image is ignored, with just + * the latter part being written. For example, if this is 0x200 then the first + * 512 bytes of the image (which must be present in the image) are skipped and + * the bytes after that are written to the store offset. + */ +struct _FuVbeSimpleDevice { + FuVbeDevice parent_instance; + gchar *storage; /* e.g. "mmc1" */ + gchar *devname; /* e.g. /dev/mmcblk1 */ + guint32 area_start; + guint32 area_size; + guint32 skip_offset; + gint fd; +}; + +G_DEFINE_TYPE(FuVbeSimpleDevice, fu_vbe_simple_device, FU_TYPE_VBE_DEVICE) + +static void +fu_vbe_simple_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + + /* FuVbeDevice->to_string */ + FU_DEVICE_CLASS(fu_vbe_simple_device_parent_class)->to_string(device, idt, str); + + if (self->storage != NULL) + fu_string_append(str, idt, "Storage", self->storage); + if (self->devname != NULL) + fu_string_append(str, idt, "Devname", self->devname); + fu_string_append_kx(str, idt, "AreaStart", self->area_start); + fu_string_append_kx(str, idt, "AreaSize", self->area_size); + if (self->skip_offset != 0) + fu_string_append_kx(str, idt, "SkipOffset", self->skip_offset); +} + +static gboolean +fu_vbe_simple_device_parse_devnum(const gchar *str, guint *value, GError **error) +{ + guint64 val64 = 0; + + /* skip non-numeric part */ + while (*str != '\0' && g_ascii_isdigit(*str)) + str++; + + /* convert to uint */ + if (!fu_strtoull(str, &val64, 0x0, G_MAXUINT, error)) + return FALSE; + if (value != NULL) + *value = val64; + return TRUE; +} + +static gboolean +fu_vbe_simple_device_probe(FuDevice *device, GError **error) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + FuFdtImage *fdt_node; + + g_return_val_if_fail(FU_IS_VBE_DEVICE(self), FALSE); + + /* FuVbeDevice->probe */ + if (!FU_DEVICE_CLASS(fu_vbe_simple_device_parent_class)->probe(device, error)) + return FALSE; + + fdt_node = fu_vbe_device_get_fdt_node(FU_VBE_DEVICE(self)); + if (!fu_fdt_image_get_attr_str(fdt_node, "storage", &self->storage, error)) + return FALSE; + + /* if this is an absolute path, use it */ + if (g_str_has_prefix(self->storage, "/")) { + self->devname = g_strdup(self->storage); + } else { + guint devnum = 0; + + /* obtain the 1 from "mmc1" */ + if (!fu_vbe_simple_device_parse_devnum(self->storage, &devnum, error)) { + g_prefix_error(error, "cannot parse storage property %s: ", self->storage); + return FALSE; + } + if (g_str_has_prefix(self->storage, "mmc")) { + self->devname = g_strdup_printf("/dev/mmcblk%u", devnum); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported 'storage' media '%s'", + self->storage); + return FALSE; + } + } + + /* get area */ + if (!fu_fdt_image_get_attr_u32(fdt_node, "area-start", &self->area_start, error)) + return FALSE; + if (!fu_fdt_image_get_attr_u32(fdt_node, "area-size", &self->area_size, error)) + return FALSE; + + /* an optional skip offset to skip everything, which could be useful for testing */ + fu_fdt_image_get_attr_u32(fdt_node, "skip-offset", &self->skip_offset, NULL); + if (self->skip_offset > self->area_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "store offset 0x%x is larger than size 0x%x", + (guint)self->skip_offset, + (guint)self->area_size); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vbe_simple_device_open(FuDevice *device, GError **error) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + + /* open device */ + self->fd = open(self->devname, O_RDWR); + if (self->fd == -1) { +#ifdef HAVE_ERRNO_H + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot open %s [%s]", + self->devname, + strerror(errno)); +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot open %s", + self->devname); + return FALSE; +#endif + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vbe_simple_device_close(FuDevice *device, GError **error) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + + /* close device */ + close(self->fd); + self->fd = -1; + + /* success */ + return TRUE; +} + +static FuFdtImage * +fu_vbe_simple_device_get_cfg_compatible(FuVbeSimpleDevice *self, + FuFirmware *firmware, + GError **error) +{ + gchar **device_compatible; + g_autoptr(FuFdtImage) fdt_configurations = NULL; + g_autoptr(GPtrArray) img_configurations = NULL; + g_autofree gchar *str = NULL; + + /* get all configurations */ + fdt_configurations = + fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(firmware), + "/" FU_FIT_FIRMWARE_ID_CONFIGURATIONS, + error); + if (fdt_configurations == NULL) + return NULL; + img_configurations = fu_firmware_get_images(firmware); + + /* look for a configuration with the device compatible strings in priority order */ + device_compatible = fu_vbe_device_get_compatible(FU_VBE_DEVICE(self)); + for (guint j = 0; device_compatible[j] != NULL; j++) { + for (guint i = 0; i < img_configurations->len; i++) { + FuFdtImage *img = g_ptr_array_index(img_configurations, i); + g_auto(GStrv) img_compatible = NULL; + if (!fu_fdt_image_get_attr_strlist(img, + FU_FIT_FIRMWARE_ATTR_COMPATIBLE, + &img_compatible, + error)) + return NULL; + if (g_strv_contains((const gchar *const *)img_compatible, + device_compatible[j])) + return g_object_ref(img); + } + } + + /* failure */ + str = g_strjoinv(", ", device_compatible); + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "no images found that match %s", str); + return NULL; +} + +static FuFirmware * +fu_vbe_simple_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + g_autofree gchar *version = NULL; + g_auto(GStrv) firmware_ids = NULL; + g_autoptr(FuFdtImage) img_cfg = NULL; + g_autoptr(FuFirmware) firmware = fu_fit_firmware_new(); + g_autoptr(FuFirmware) firmware_container = fu_firmware_new(); + + /* parse all images */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + + /* look for a compatible configuration */ + img_cfg = fu_vbe_simple_device_get_cfg_compatible(self, firmware, error); + if (img_cfg == NULL) + return NULL; + if (!fu_fdt_image_get_attr_str(img_cfg, FU_FIT_FIRMWARE_ATTR_VERSION, &version, error)) + return NULL; + + /* check the firmware images exists */ + if (!fu_fdt_image_get_attr_strlist(img_cfg, "firmware", &firmware_ids, error)) + return NULL; + for (guint i = 0; firmware_ids[i] != NULL; i++) { + g_autofree gchar *path = NULL; + g_autoptr(FuFdtImage) img_firmware = NULL; + path = g_strdup_printf("/%s/%s", FU_FIT_FIRMWARE_ID_IMAGES, firmware_ids[i]); + img_firmware = + fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(firmware), path, error); + if (img_firmware == NULL) + return NULL; + fu_firmware_add_image(firmware_container, FU_FIRMWARE(img_firmware)); + } + + /* success: return the container */ + return g_steal_pointer(&firmware_container); +} + +static gboolean +fu_vbe_simple_device_write_firmware_img(FuVbeSimpleDevice *self, + FuFdtImage *img, + FuProgress *progress, + GError **error) +{ + const guint8 *buf; + gssize rc; + gsize bufsz = 0; + gsize seek_to; + guint32 store_offset = 0; + g_autoptr(GBytes) blob = NULL; + + /* get data */ + blob = fu_fdt_image_get_attr(img, FU_FIT_FIRMWARE_ATTR_DATA, error); + if (blob == NULL) + return FALSE; + buf = g_bytes_get_data(blob, &bufsz); + fu_fdt_image_get_attr_u32(img, "store-offset", &store_offset, NULL); + + /* sanity check */ + if (store_offset + bufsz > self->area_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "image '%s' store_offset=0x%x, bufsz=0x%x, area_size=0x%x", + fu_firmware_get_id(FU_FIRMWARE(img)), + (guint)store_offset, + (guint)bufsz, + (guint)self->area_size); + return FALSE; + } + if (self->skip_offset >= bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "image '%s' skip_offset=0x%x, bufsz=0x%x, area_size=0x%x", + fu_firmware_get_id(FU_FIRMWARE(img)), + (guint)store_offset, + (guint)bufsz, + (guint)self->area_size); + return FALSE; + } + + /* seek to correct address */ + seek_to = self->area_start + store_offset + self->skip_offset; + g_debug("writing image '%s' bufsz 0x%x (skipping 0x%x) to store_offset 0x%x, seek 0x%x\n", + fu_firmware_get_id(FU_FIRMWARE(img)), + (guint)bufsz, + (guint)self->skip_offset, + (guint)store_offset, + (guint)seek_to); + rc = lseek(self->fd, seek_to, SEEK_SET); + if (rc < 0) { +#ifdef HAVE_ERRNO_H + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "cannot seek file '%s' to 0x%x [%s]", + self->devname, + (guint)seek_to, + strerror(errno)); +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "cannot seek file '%s' to 0x%x", + self->devname, + (guint)seek_to); +#endif + return FALSE; + } + + /* write buffer */ + rc = write(self->fd, buf + self->skip_offset, bufsz - self->skip_offset); + if (rc < 0) { +#ifdef HAVE_ERRNO_H + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "cannot write file '%s' [%s]", + self->devname, + strerror(errno)); +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "cannot write file '%s'", + self->devname); +#endif + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vbe_simple_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + g_return_val_if_fail(FU_IS_VBE_DEVICE(self), FALSE); + + /* write each firmware image */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, imgs->len); + for (guint i = 0; i < imgs->len; i++) { + FuFdtImage *img = g_ptr_array_index(imgs, i); + if (!fu_vbe_simple_device_write_firmware_img(self, + img, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_vbe_simple_device_upload(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); + gssize rc; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + /* notify UI */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + + /* seek to start */ + rc = lseek(self->fd, self->area_start, SEEK_SET); + if (rc < 0) { +#ifdef HAVE_ERRNO_H + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cannot seek file %s to 0x%x [%s]", + self->devname, + (guint)self->area_start, + strerror(errno)); +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "cannot seek file %s to 0x%x", + self->devname, + (guint)self->area_start); +#endif + return NULL; + } + + /* process in chunks */ + chunks = fu_chunk_array_new(NULL, self->area_size - self->area_start, 0x0, 0x0, 0x100000); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autofree guint8 *tmpbuf = g_malloc0(fu_chunk_get_data_sz(chk)); + + rc = read(self->fd, tmpbuf, fu_chunk_get_data_sz(chk)); + if (rc != (gssize)fu_chunk_get_data_sz(chk)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "incomplete read of %s @0x%x", + self->devname, + fu_chunk_get_address(chk)); + return NULL; + } + g_byte_array_append(buf, tmpbuf, fu_chunk_get_data_sz(chk)); + fu_progress_step_done(progress); + } + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_vbe_simple_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_vbe_simple_device_init(FuVbeSimpleDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "simple"); + fu_device_set_vendor(FU_DEVICE(self), "U-Boot"); + fu_device_add_vendor_id(FU_DEVICE(self), "VBE:U-Boot"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_lowest(FU_DEVICE(self), "0.0.1"); +} + +static void +fu_vbe_simple_device_constructed(GObject *obj) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(obj); + fu_device_add_guid(FU_DEVICE(self), "bb3b05a8-ebef-11ec-be98-d3a15278be95"); +} + +static void +fu_vbe_simple_device_finalize(GObject *obj) +{ + FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(obj); + g_free(self->devname); + g_free(self->storage); + G_OBJECT_CLASS(fu_vbe_simple_device_parent_class)->finalize(obj); +} + +static void +fu_vbe_simple_device_class_init(FuVbeSimpleDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->constructed = fu_vbe_simple_device_constructed; + object_class->finalize = fu_vbe_simple_device_finalize; + klass_device->to_string = fu_vbe_simple_device_to_string; + klass_device->probe = fu_vbe_simple_device_probe; + klass_device->open = fu_vbe_simple_device_open; + klass_device->close = fu_vbe_simple_device_close; + klass_device->set_progress = fu_vbe_simple_device_set_progress; + klass_device->prepare_firmware = fu_vbe_simple_device_prepare_firmware; + klass_device->write_firmware = fu_vbe_simple_device_write_firmware; + klass_device->dump_firmware = fu_vbe_simple_device_upload; +} diff --git a/fwupd-1.8.6/plugins/vbe/fu-vbe-simple-device.h b/fwupd-1.8.6/plugins/vbe/fu-vbe-simple-device.h new file mode 100644 index 0000000000000000000000000000000000000000..d94490af3bde535f237549c1bd164b72b0e3ee73 --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/fu-vbe-simple-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2022 Google LLC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-vbe-device.h" + +#define FU_TYPE_VBE_SIMPLE_DEVICE (fu_vbe_simple_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVbeSimpleDevice, fu_vbe_simple_device, FU, VBE_SIMPLE_DEVICE, FuVbeDevice) diff --git a/fwupd-1.8.6/plugins/vbe/meson.build b/fwupd-1.8.6/plugins/vbe/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f6e86079959ac986d2c8b9374d56d9b2d1d3109b --- /dev/null +++ b/fwupd-1.8.6/plugins/vbe/meson.build @@ -0,0 +1,25 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginVbe"'] + +plugin_builtins += static_library('fu_plugin_vbe', + sources : [ + 'fu-vbe-plugin.c', + 'fu-vbe-device.c', + 'fu-vbe-simple-device.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + dependencies : [ + plugin_deps, + ], +) diff --git a/fwupd-1.8.6/plugins/vli/README.md b/fwupd-1.8.6/plugins/vli/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4a79a53c5aedac1fb1cfb3e4ed75069244d2f9bb --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/README.md @@ -0,0 +1,93 @@ +# VIA + +## Introduction + +This plugin is used to update USB hubs from VIA. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +an undisclosed binary file format. + +This plugin supports the following protocol ID: + +* com.vli.i2c +* com.vli.pd +* com.vli.usbhub + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_17EF&PID_3083&REV_0001` +* `USB\VID_17EF&PID_3083` +* `USB\VID_17EF` + +All VLI devices also use custom GUID values for the device type, e.g. + +* `USB\VID_17EF&PID_3083&DEV_VL812B3` + +These devices also use custom GUID values for the SPI flash configuration, e.g. + +* `CFI\FLASHID_37303840` +* `CFI\FLASHID_3730` +* `CFI\FLASHID_37` + +Optional PD child devices sharing the SPI flash use two extra GUIDs, e.g. + +* `USB\VID_17EF&PID_3083&DEV_VL102` +* `USB\VID_17EF&PID_3083&APP_26` + +Optional I²C child devices use just one extra GUID, e.g. + +* `USB\VID_17EF&PID_3083&I2C_MSP430` +* `USB\VID_17EF&PID_3083&I2C_PS186` + +## Vendor ID Security + +The vendor ID is set from the USB vendor, for example set to `USB:0x2109` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### VliDeviceKind + +Device kind, e.g. `VL102`. + +Since: 1.3.7 + +### VliSpiAutoDetect + +SPI autodetect (default 0x1). + +Since: 1.3.7 + +### CfiDeviceCmdReadId + +Flash command to read the ID. + +Since: 1.3.3 + +### CfiDeviceCmdReadIdSz + +Size of the ReadId response. + +The `CfiDeviceCmdReadId` and `CfiDeviceCmdReadIdSz` quirks have to be assigned to the device +instance attribute, rather then the flash part as the ID is required to query +the other flash chip parameters. For example: + + [USB\VID_2109&PID_0210] + Plugin = vli + GType = FuVliUsbhubDevice + CfiDeviceCmdReadId = 0xf8 + CfiDeviceCmdReadIdSz = 4 + + # W3IRDFLASHxxx + [CFI\\FLASHID_37303840] + CfiDeviceCmdChipErase = 0xc7 + CfiDeviceCmdSectorErase = 0x20 + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/vli/fu-self-test.c b/fwupd-1.8.6/plugins/vli/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..84e349e88e3548b91b9c4d1649a789b89cb89d05 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-self-test.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-common.h" + +static void +fu_test_common_device_kind_func(void) +{ + for (guint i = 0; i < 0xffff; i++) { + const gchar *tmp = fu_vli_common_device_kind_to_string(i); + if (tmp == NULL) + continue; + g_assert_cmpint(fu_vli_common_device_kind_from_string(tmp), ==, i); + } +} + +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("/vli/common{device-kind}", fu_test_common_device_kind_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-common.c b/fwupd-1.8.6/plugins/vli/fu-vli-common.c new file mode 100644 index 0000000000000000000000000000000000000000..648f145e81016bad6988ecf779ede248b2c2661d --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-common.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vli-common.h" + +const gchar * +fu_vli_common_device_kind_to_string(FuVliDeviceKind device_kind) +{ + if (device_kind == FU_VLI_DEVICE_KIND_VL100) + return "VL100"; + if (device_kind == FU_VLI_DEVICE_KIND_VL101) + return "VL101"; + if (device_kind == FU_VLI_DEVICE_KIND_VL102) + return "VL102"; + if (device_kind == FU_VLI_DEVICE_KIND_VL103) + return "VL103"; + if (device_kind == FU_VLI_DEVICE_KIND_VL104) + return "VL104"; + if (device_kind == FU_VLI_DEVICE_KIND_VL105) + return "VL105"; + if (device_kind == FU_VLI_DEVICE_KIND_VL810) + return "VL810"; + if (device_kind == FU_VLI_DEVICE_KIND_VL811) + return "VL811"; + if (device_kind == FU_VLI_DEVICE_KIND_VL811PB0) + return "VL811PB0"; + if (device_kind == FU_VLI_DEVICE_KIND_VL811PB3) + return "VL811PB3"; + if (device_kind == FU_VLI_DEVICE_KIND_VL812B0) + return "VL812B0"; + if (device_kind == FU_VLI_DEVICE_KIND_VL812B3) + return "VL812B3"; + if (device_kind == FU_VLI_DEVICE_KIND_VL812Q4S) + return "VL812Q4S"; + if (device_kind == FU_VLI_DEVICE_KIND_VL813) + return "VL813"; + if (device_kind == FU_VLI_DEVICE_KIND_VL815) + return "VL815"; + if (device_kind == FU_VLI_DEVICE_KIND_VL817) + return "VL817"; + if (device_kind == FU_VLI_DEVICE_KIND_VL819Q7) + return "VL819Q7"; + if (device_kind == FU_VLI_DEVICE_KIND_VL819Q8) + return "VL819Q8"; + if (device_kind == FU_VLI_DEVICE_KIND_VL820Q7) + return "VL820Q7"; + if (device_kind == FU_VLI_DEVICE_KIND_VL820Q8) + return "VL820Q8"; + if (device_kind == FU_VLI_DEVICE_KIND_VL821Q7) + return "VL821Q7"; + if (device_kind == FU_VLI_DEVICE_KIND_VL821Q8) + return "VL821Q8"; + if (device_kind == FU_VLI_DEVICE_KIND_VL822Q5) + return "VL822Q5"; + if (device_kind == FU_VLI_DEVICE_KIND_VL822Q7) + return "VL822Q7"; + if (device_kind == FU_VLI_DEVICE_KIND_VL822Q8) + return "VL822Q8"; + if (device_kind == FU_VLI_DEVICE_KIND_VL120) + return "VL120"; + if (device_kind == FU_VLI_DEVICE_KIND_VL210) + return "VL210"; + if (device_kind == FU_VLI_DEVICE_KIND_VL211) + return "VL211"; + if (device_kind == FU_VLI_DEVICE_KIND_VL212) + return "VL212"; + if (device_kind == FU_VLI_DEVICE_KIND_MSP430) + return "MSP430"; + if (device_kind == FU_VLI_DEVICE_KIND_PS186) + return "PS186"; + if (device_kind == FU_VLI_DEVICE_KIND_RTD21XX) + return "RTD21XX"; + if (device_kind == FU_VLI_DEVICE_KIND_VL107) + return "VL107"; + if (device_kind == FU_VLI_DEVICE_KIND_VL650) + return "VL650"; + if (device_kind == FU_VLI_DEVICE_KIND_VL830) + return "VL830"; + return NULL; +} + +FuVliDeviceKind +fu_vli_common_device_kind_from_string(const gchar *device_kind) +{ + if (g_strcmp0(device_kind, "VL100") == 0) + return FU_VLI_DEVICE_KIND_VL100; + if (g_strcmp0(device_kind, "VL101") == 0) + return FU_VLI_DEVICE_KIND_VL101; + if (g_strcmp0(device_kind, "VL102") == 0) + return FU_VLI_DEVICE_KIND_VL102; + if (g_strcmp0(device_kind, "VL103") == 0) + return FU_VLI_DEVICE_KIND_VL103; + if (g_strcmp0(device_kind, "VL104") == 0) + return FU_VLI_DEVICE_KIND_VL104; + if (g_strcmp0(device_kind, "VL105") == 0) + return FU_VLI_DEVICE_KIND_VL105; + if (g_strcmp0(device_kind, "VL810") == 0) + return FU_VLI_DEVICE_KIND_VL810; + if (g_strcmp0(device_kind, "VL811") == 0) + return FU_VLI_DEVICE_KIND_VL811; + if (g_strcmp0(device_kind, "VL811PB0") == 0) + return FU_VLI_DEVICE_KIND_VL811PB0; + if (g_strcmp0(device_kind, "VL811PB3") == 0) + return FU_VLI_DEVICE_KIND_VL811PB3; + if (g_strcmp0(device_kind, "VL812B0") == 0) + return FU_VLI_DEVICE_KIND_VL812B0; + if (g_strcmp0(device_kind, "VL812B3") == 0) + return FU_VLI_DEVICE_KIND_VL812B3; + if (g_strcmp0(device_kind, "VL812Q4S") == 0) + return FU_VLI_DEVICE_KIND_VL812Q4S; + if (g_strcmp0(device_kind, "VL813") == 0) + return FU_VLI_DEVICE_KIND_VL813; + if (g_strcmp0(device_kind, "VL815") == 0) + return FU_VLI_DEVICE_KIND_VL815; + if (g_strcmp0(device_kind, "VL817") == 0) + return FU_VLI_DEVICE_KIND_VL817; + if (g_strcmp0(device_kind, "VL819Q7") == 0) + return FU_VLI_DEVICE_KIND_VL819Q7; + if (g_strcmp0(device_kind, "VL819Q8") == 0) + return FU_VLI_DEVICE_KIND_VL819Q8; + if (g_strcmp0(device_kind, "VL820Q7") == 0) + return FU_VLI_DEVICE_KIND_VL820Q7; + if (g_strcmp0(device_kind, "VL820Q8") == 0) + return FU_VLI_DEVICE_KIND_VL820Q8; + if (g_strcmp0(device_kind, "VL821Q7") == 0) + return FU_VLI_DEVICE_KIND_VL821Q7; + if (g_strcmp0(device_kind, "VL821Q8") == 0) + return FU_VLI_DEVICE_KIND_VL821Q8; + if (g_strcmp0(device_kind, "VL822Q5") == 0) + return FU_VLI_DEVICE_KIND_VL822Q5; + if (g_strcmp0(device_kind, "VL822Q7") == 0) + return FU_VLI_DEVICE_KIND_VL822Q7; + if (g_strcmp0(device_kind, "VL822Q8") == 0) + return FU_VLI_DEVICE_KIND_VL822Q8; + if (g_strcmp0(device_kind, "VL120") == 0) + return FU_VLI_DEVICE_KIND_VL120; + if (g_strcmp0(device_kind, "VL210") == 0) + return FU_VLI_DEVICE_KIND_VL210; + if (g_strcmp0(device_kind, "VL211") == 0) + return FU_VLI_DEVICE_KIND_VL211; + if (g_strcmp0(device_kind, "VL212") == 0) + return FU_VLI_DEVICE_KIND_VL212; + if (g_strcmp0(device_kind, "MSP430") == 0) + return FU_VLI_DEVICE_KIND_MSP430; + if (g_strcmp0(device_kind, "PS186") == 0) + return FU_VLI_DEVICE_KIND_PS186; + if (g_strcmp0(device_kind, "RTD21XX") == 0) + return FU_VLI_DEVICE_KIND_RTD21XX; + if (g_strcmp0(device_kind, "VL107") == 0) + return FU_VLI_DEVICE_KIND_VL107; + if (g_strcmp0(device_kind, "VL650") == 0) + return FU_VLI_DEVICE_KIND_VL650; + if (g_strcmp0(device_kind, "VL830") == 0) + return FU_VLI_DEVICE_KIND_VL830; + return FU_VLI_DEVICE_KIND_UNKNOWN; +} + +guint32 +fu_vli_common_device_kind_get_size(FuVliDeviceKind device_kind) +{ + if (device_kind == FU_VLI_DEVICE_KIND_VL100) + return 0x8000; /* 32KB */ + if (device_kind == FU_VLI_DEVICE_KIND_VL101) + return 0xc000; /* 48KB */ + if (device_kind == FU_VLI_DEVICE_KIND_VL102) + return 0x8000; /* 32KB */ + if (device_kind == FU_VLI_DEVICE_KIND_VL103) + return 0x8000; /* 32KB */ + if (device_kind == FU_VLI_DEVICE_KIND_VL104) + return 0xc000; /* 48KB */ + if (device_kind == FU_VLI_DEVICE_KIND_VL105) + return 0xc000; /* 48KB */ + if (device_kind == FU_VLI_DEVICE_KIND_VL210) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL211) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL212) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL810) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL811) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL811PB0) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL811PB3) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL812B0) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL812B3) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL812Q4S) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL813) + return 0x8000; + if (device_kind == FU_VLI_DEVICE_KIND_VL815) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL817) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL819Q7) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL819Q8) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL820Q7) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL820Q8) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL821Q7) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL821Q8) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL822Q5) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL822Q7) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_VL822Q8) + return 0x20000 * 2; + if (device_kind == FU_VLI_DEVICE_KIND_PS186) + return 0x40000; + if (device_kind == FU_VLI_DEVICE_KIND_VL107) + return 0x80000; + if (device_kind == FU_VLI_DEVICE_KIND_VL650) + return 0x40000; + if (device_kind == FU_VLI_DEVICE_KIND_VL830) + return 0x100000; + return 0x0; +} + +guint32 +fu_vli_common_device_kind_get_offset(FuVliDeviceKind device_kind) +{ + if (device_kind == FU_VLI_DEVICE_KIND_VL100) + return 0x10000; + if (device_kind == FU_VLI_DEVICE_KIND_VL101) + return 0x10000; + if (device_kind == FU_VLI_DEVICE_KIND_VL102) + return 0x20000; + if (device_kind == FU_VLI_DEVICE_KIND_VL103) + return 0x20000; + if (device_kind == FU_VLI_DEVICE_KIND_VL104) + return 0x20000; + if (device_kind == FU_VLI_DEVICE_KIND_VL105) + return 0x20000; + return 0x0; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-common.h b/fwupd-1.8.6/plugins/vli/fu-vli-common.h new file mode 100644 index 0000000000000000000000000000000000000000..2cfa9d3e11580d60fa949280d0a93fde6ae058c4 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-common.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +typedef enum { + FU_VLI_DEVICE_KIND_UNKNOWN = 0x0000, + FU_VLI_DEVICE_KIND_VL100 = 0x0100, + FU_VLI_DEVICE_KIND_VL101 = 0x0101, + FU_VLI_DEVICE_KIND_VL102 = 0x0102, + FU_VLI_DEVICE_KIND_VL103 = 0x0103, + FU_VLI_DEVICE_KIND_VL104 = 0x0104, + FU_VLI_DEVICE_KIND_VL105 = 0x0105, + FU_VLI_DEVICE_KIND_VL107 = 0x0107, + FU_VLI_DEVICE_KIND_VL120 = 0x0120, + FU_VLI_DEVICE_KIND_VL210 = 0x0210, + FU_VLI_DEVICE_KIND_VL211 = 0x0211, + FU_VLI_DEVICE_KIND_VL212 = 0x0212, + FU_VLI_DEVICE_KIND_VL650 = 0x0650, + FU_VLI_DEVICE_KIND_VL810 = 0x0810, + FU_VLI_DEVICE_KIND_VL811 = 0x0811, + FU_VLI_DEVICE_KIND_VL811PB0 = 0x8110, + FU_VLI_DEVICE_KIND_VL811PB3 = 0x8113, + FU_VLI_DEVICE_KIND_VL812B0 = 0xa812, + FU_VLI_DEVICE_KIND_VL812B3 = 0xb812, + FU_VLI_DEVICE_KIND_VL812Q4S = 0xc812, + FU_VLI_DEVICE_KIND_VL813 = 0x0813, + FU_VLI_DEVICE_KIND_VL815 = 0x0815, + FU_VLI_DEVICE_KIND_VL817 = 0x0817, + FU_VLI_DEVICE_KIND_VL819Q7 = 0xa819, /* guessed */ + FU_VLI_DEVICE_KIND_VL819Q8 = 0xb819, /* guessed */ + FU_VLI_DEVICE_KIND_VL820Q7 = 0xa820, + FU_VLI_DEVICE_KIND_VL820Q8 = 0xb820, + FU_VLI_DEVICE_KIND_VL821Q7 = 0xa821, /* guessed */ + FU_VLI_DEVICE_KIND_VL821Q8 = 0xb821, /* guessed */ + FU_VLI_DEVICE_KIND_VL822Q5 = 0x0822, /* guessed */ + FU_VLI_DEVICE_KIND_VL822Q7 = 0xa822, /* guessed */ + FU_VLI_DEVICE_KIND_VL822Q8 = 0xb822, /* guessed */ + FU_VLI_DEVICE_KIND_VL830 = 0x0830, + FU_VLI_DEVICE_KIND_MSP430 = 0xf430, /* guessed */ + FU_VLI_DEVICE_KIND_PS186 = 0xf186, /* guessed */ + FU_VLI_DEVICE_KIND_RTD21XX = 0xff00, /* guessed */ +} FuVliDeviceKind; + +const gchar * +fu_vli_common_device_kind_to_string(FuVliDeviceKind device_kind); +FuVliDeviceKind +fu_vli_common_device_kind_from_string(const gchar *device_kind); +guint32 +fu_vli_common_device_kind_get_size(FuVliDeviceKind device_kind); +guint32 +fu_vli_common_device_kind_get_offset(FuVliDeviceKind device_kind); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-device.c new file mode 100644 index 0000000000000000000000000000000000000000..fcab021dd132585884453069819d70d9577ae86b --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-device.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-device.h" + +typedef struct { + FuVliDeviceKind kind; + FuCfiDevice *cfi_device; + gboolean spi_auto_detect; + guint8 spi_cmd_read_id_sz; + guint32 flash_id; +} FuVliDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuVliDevice, fu_vli_device, FU_TYPE_USB_DEVICE) + +#define GET_PRIVATE(o) (fu_vli_device_get_instance_private(o)) + +enum { PROP_0, PROP_KIND, PROP_LAST }; + +FuCfiDevice * +fu_vli_device_get_cfi_device(FuVliDevice *self) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + return priv->cfi_device; +} + +static gboolean +fu_vli_device_spi_write_enable(FuVliDevice *self, GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_write_enable != NULL) { + if (!klass->spi_write_enable(self, error)) { + g_prefix_error(error, "failed to write enable SPI: "); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_device_spi_chip_erase(FuVliDevice *self, GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_chip_erase != NULL) { + if (!klass->spi_chip_erase(self, error)) { + g_prefix_error(error, "failed to erase SPI data: "); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_device_spi_write_status(FuVliDevice *self, guint8 status, GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_write_status != NULL) { + if (!klass->spi_write_status(self, status, error)) { + g_prefix_error(error, "failed to write SPI status 0x%x: ", status); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_device_spi_read_status(FuVliDevice *self, guint8 *status, GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_read_status != NULL) { + if (!klass->spi_read_status(self, status, error)) { + g_prefix_error(error, "failed to read status: "); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_device_spi_sector_erase(FuVliDevice *self, guint32 addr, GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_sector_erase != NULL) { + if (!klass->spi_sector_erase(self, addr, error)) { + g_prefix_error(error, "failed to erase SPI data @0x%x: ", addr); + return FALSE; + } + } + return TRUE; +} + +gboolean +fu_vli_device_spi_read_block(FuVliDevice *self, + guint32 addr, + guint8 *buf, + gsize bufsz, + GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_read_data != NULL) { + if (!klass->spi_read_data(self, addr, buf, bufsz, error)) { + g_prefix_error(error, "failed to read SPI data @0x%x: ", addr); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_device_spi_write_data(FuVliDevice *self, + guint32 addr, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); + if (klass->spi_write_data != NULL) { + if (!klass->spi_write_data(self, addr, buf, bufsz, error)) { + g_prefix_error(error, "failed to write SPI data @0x%x: ", addr); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_device_spi_wait_finish(FuVliDevice *self, GError **error) +{ + const guint32 rdy_cnt = 2; + guint32 cnt = 0; + + for (guint32 idx = 0; idx < 1000; idx++) { + guint8 status = 0x7f; + + /* must get bit[1:0] == 0 twice in a row for success */ + if (!fu_vli_device_spi_read_status(self, &status, error)) + return FALSE; + if ((status & 0x03) == 0x00) { + if (cnt++ >= rdy_cnt) + return TRUE; + } else { + cnt = 0; + } + g_usleep(500 * 1000); + } + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed to wait for SPI"); + return FALSE; +} + +gboolean +fu_vli_device_spi_erase_sector(FuVliDevice *self, guint32 addr, GError **error) +{ + const guint32 bufsz = 0x1000; + + /* erase sector */ + if (!fu_vli_device_spi_write_enable(self, error)) { + g_prefix_error(error, "->spi_write_enable failed: "); + return FALSE; + } + if (!fu_vli_device_spi_write_status(self, 0x00, error)) { + g_prefix_error(error, "->spi_write_status failed: "); + return FALSE; + } + if (!fu_vli_device_spi_write_enable(self, error)) { + g_prefix_error(error, "->spi_write_enable failed: "); + return FALSE; + } + if (!fu_vli_device_spi_sector_erase(self, addr, error)) { + g_prefix_error(error, "->spi_sector_erase failed: "); + return FALSE; + } + if (!fu_vli_device_spi_wait_finish(self, error)) { + g_prefix_error(error, "->spi_wait_finish failed: "); + return FALSE; + } + + /* verify it really was blanked */ + for (guint32 offset = 0; offset < bufsz; offset += FU_VLI_DEVICE_TXSIZE) { + guint8 buf[FU_VLI_DEVICE_TXSIZE] = {0x0}; + if (!fu_vli_device_spi_read_block(self, addr + offset, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read back empty: "); + return FALSE; + } + for (guint i = 0; i < sizeof(buf); i++) { + if (buf[i] != 0xff) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to check blank @0x%x", + addr + offset + i); + return FALSE; + } + } + } + + /* success */ + return TRUE; +} + +GBytes * +fu_vli_device_spi_read(FuVliDevice *self, + guint32 address, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + g_autofree guint8 *buf = g_malloc0(bufsz); + g_autoptr(GPtrArray) chunks = NULL; + + /* get data from hardware */ + chunks = fu_chunk_array_mutable_new(buf, bufsz, address, 0x0, FU_VLI_DEVICE_TXSIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_vli_device_spi_read_block(self, + fu_chunk_get_address(chk), + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "SPI data read failed @0x%x: ", + fu_chunk_get_address(chk)); + return NULL; + } + fu_progress_step_done(progress); + } + return g_bytes_new_take(g_steal_pointer(&buf), bufsz); +} + +gboolean +fu_vli_device_spi_write_block(FuVliDevice *self, + guint32 address, + const guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + g_autofree guint8 *buf_tmp = g_malloc0(bufsz); + + /* sanity check */ + if (bufsz > FU_VLI_DEVICE_TXSIZE) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cannot write 0x%x in one block", + (guint)bufsz); + return FALSE; + } + + /* write */ + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + g_debug("writing 0x%x block @0x%x", (guint)bufsz, address); + if (!fu_vli_device_spi_write_enable(self, error)) { + g_prefix_error(error, "enabling SPI write failed: "); + return FALSE; + } + if (!fu_vli_device_spi_write_data(self, address, buf, bufsz, error)) { + g_prefix_error(error, "SPI data write failed: "); + return FALSE; + } + g_usleep(800); + + /* verify */ + if (!fu_vli_device_spi_read_block(self, address, buf_tmp, bufsz, error)) { + g_prefix_error(error, "SPI data read failed: "); + return FALSE; + } + return fu_memcmp_safe(buf, bufsz, buf_tmp, bufsz, error); +} + +gboolean +fu_vli_device_spi_write(FuVliDevice *self, + guint32 address, + const guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + FuChunk *chk; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write-chk0"); + + /* write SPI data, then CRC bytes last */ + g_debug("writing 0x%x bytes @0x%x", (guint)bufsz, address); + chunks = fu_chunk_array_new(buf, bufsz, 0x0, 0x0, FU_VLI_DEVICE_TXSIZE); + if (chunks->len > 1) { + FuProgress *progress_local = fu_progress_get_child(progress); + fu_progress_set_id(progress_local, G_STRLOC); + fu_progress_set_steps(progress_local, chunks->len - 1); + for (guint i = 1; i < chunks->len; i++) { + chk = g_ptr_array_index(chunks, i); + if (!fu_vli_device_spi_write_block(self, + fu_chunk_get_address(chk) + address, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_progress_get_child(progress_local), + error)) { + g_prefix_error(error, + "failed to write block 0x%x: ", + fu_chunk_get_idx(chk)); + return FALSE; + } + fu_progress_step_done(progress_local); + } + } + fu_progress_step_done(progress); + + /* chk0 */ + chk = g_ptr_array_index(chunks, 0); + if (!fu_vli_device_spi_write_block(self, + fu_chunk_get_address(chk) + address, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write CRC block: "); + return FALSE; + } + fu_progress_step_done(progress); + return TRUE; +} + +gboolean +fu_vli_device_spi_erase_all(FuVliDevice *self, FuProgress *progress, GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 99, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); + + if (!fu_vli_device_spi_write_enable(self, error)) + return FALSE; + if (!fu_vli_device_spi_write_status(self, 0x00, error)) + return FALSE; + if (!fu_vli_device_spi_write_enable(self, error)) + return FALSE; + if (!fu_vli_device_spi_chip_erase(self, error)) + return FALSE; + fu_progress_sleep(fu_progress_get_child(progress), 4000); + fu_progress_step_done(progress); + + /* verify chip was erased */ + for (guint addr = 0; addr < 0x10000; addr += 0x1000) { + guint8 buf[FU_VLI_DEVICE_TXSIZE] = {0x0}; + if (!fu_vli_device_spi_read_block(self, addr, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to read @0x%x: ", addr); + return FALSE; + } + for (guint i = 0; i < sizeof(buf); i++) { + if (buf[i] != 0xff) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to verify erase @0x%x: ", + addr); + return FALSE; + } + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)addr + 0x1000, + (gsize)0x10000); + } + fu_progress_step_done(progress); + return TRUE; +} + +gboolean +fu_vli_device_spi_erase(FuVliDevice *self, + guint32 addr, + gsize sz, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) chunks = fu_chunk_array_new(NULL, sz, addr, 0x0, 0x1000); + g_debug("erasing 0x%x bytes @0x%x", (guint)sz, addr); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + g_debug("erasing @0x%x", fu_chunk_get_address(chk)); + if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), + fu_chunk_get_address(chk), + error)) { + g_prefix_error(error, + "failed to erase FW sector @0x%x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + fu_progress_step_done(progress); + } + return TRUE; +} + +static gchar * +fu_vli_device_get_flash_id_str(FuVliDevice *self) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + if (priv->spi_cmd_read_id_sz == 4) + return g_strdup_printf("%08X", priv->flash_id); + if (priv->spi_cmd_read_id_sz == 2) + return g_strdup_printf("%04X", priv->flash_id); + if (priv->spi_cmd_read_id_sz == 1) + return g_strdup_printf("%02X", priv->flash_id); + return g_strdup_printf("%X", priv->flash_id); +} + +void +fu_vli_device_set_kind(FuVliDevice *self, FuVliDeviceKind device_kind) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + guint32 sz; + + /* set and notify if different */ + if (priv->kind != device_kind) { + priv->kind = device_kind; + g_object_notify(G_OBJECT(self), "kind"); + } + + /* newer chips use SHA-256 and ECDSA-256 */ + switch (device_kind) { + case FU_VLI_DEVICE_KIND_MSP430: + case FU_VLI_DEVICE_KIND_PS186: + case FU_VLI_DEVICE_KIND_RTD21XX: + case FU_VLI_DEVICE_KIND_VL100: + case FU_VLI_DEVICE_KIND_VL101: + case FU_VLI_DEVICE_KIND_VL102: + case FU_VLI_DEVICE_KIND_VL103: + case FU_VLI_DEVICE_KIND_VL104: + case FU_VLI_DEVICE_KIND_VL105: + case FU_VLI_DEVICE_KIND_VL120: + case FU_VLI_DEVICE_KIND_VL210: + case FU_VLI_DEVICE_KIND_VL211: + case FU_VLI_DEVICE_KIND_VL212: + case FU_VLI_DEVICE_KIND_VL810: + case FU_VLI_DEVICE_KIND_VL811: + case FU_VLI_DEVICE_KIND_VL811PB0: + case FU_VLI_DEVICE_KIND_VL811PB3: + case FU_VLI_DEVICE_KIND_VL812B0: + case FU_VLI_DEVICE_KIND_VL812B3: + case FU_VLI_DEVICE_KIND_VL812Q4S: + case FU_VLI_DEVICE_KIND_VL813: + case FU_VLI_DEVICE_KIND_VL815: + case FU_VLI_DEVICE_KIND_VL817: + case FU_VLI_DEVICE_KIND_VL819Q7: + case FU_VLI_DEVICE_KIND_VL819Q8: + case FU_VLI_DEVICE_KIND_VL820Q7: + case FU_VLI_DEVICE_KIND_VL820Q8: + case FU_VLI_DEVICE_KIND_VL821Q7: + case FU_VLI_DEVICE_KIND_VL821Q8: + case FU_VLI_DEVICE_KIND_VL822Q5: + case FU_VLI_DEVICE_KIND_VL822Q7: + case FU_VLI_DEVICE_KIND_VL822Q8: + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + break; + case FU_VLI_DEVICE_KIND_VL107: + case FU_VLI_DEVICE_KIND_VL650: + case FU_VLI_DEVICE_KIND_VL830: + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + break; + default: + g_warning("device kind %s [0x%02x] does not indicate unsigned/signed payload", + fu_vli_common_device_kind_to_string(device_kind), + device_kind); + break; + } + + /* set maximum firmware size */ + sz = fu_vli_common_device_kind_get_size(device_kind); + if (sz > 0x0) + fu_device_set_firmware_size_max(FU_DEVICE(self), sz); + + /* add extra DEV GUID too */ + fu_device_add_instance_str(FU_DEVICE(self), + "DEV", + fu_vli_common_device_kind_to_string(priv->kind)); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "USB", "VID", "PID", "DEV", NULL); +} + +void +fu_vli_device_set_spi_auto_detect(FuVliDevice *self, gboolean spi_auto_detect) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + priv->spi_auto_detect = spi_auto_detect; +} + +FuVliDeviceKind +fu_vli_device_get_kind(FuVliDevice *self) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + return priv->kind; +} + +guint32 +fu_vli_device_get_offset(FuVliDevice *self) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + return fu_vli_common_device_kind_get_offset(priv->kind); +} + +static void +fu_vli_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuVliDevice *self = FU_VLI_DEVICE(device); + FuVliDevicePrivate *priv = GET_PRIVATE(self); + + /* parent */ + FU_DEVICE_CLASS(fu_vli_device_parent_class)->to_string(device, idt, str); + + if (priv->kind != FU_VLI_DEVICE_KIND_UNKNOWN) { + fu_string_append(str, + idt, + "DeviceKind", + fu_vli_common_device_kind_to_string(priv->kind)); + } + fu_string_append_kb(str, idt, "SpiAutoDetect", priv->spi_auto_detect); + if (priv->flash_id != 0x0) { + g_autofree gchar *tmp = fu_vli_device_get_flash_id_str(self); + fu_string_append(str, idt, "FlashId", tmp); + } + fu_device_add_string(FU_DEVICE(priv->cfi_device), idt + 1, str); +} + +static gboolean +fu_vli_device_spi_read_flash_id(FuVliDevice *self, GError **error) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 buf[4] = {0x0}; + guint8 spi_cmd = 0x0; + + if (!fu_cfi_device_get_cmd(priv->cfi_device, FU_CFI_DEVICE_CMD_READ_ID, &spi_cmd, error)) + return FALSE; + 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 | (priv->spi_cmd_read_id_sz * 2), + spi_cmd, + 0x0000, + buf, + sizeof(buf), + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read chip ID: "); + return FALSE; + } + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "SpiCmdReadId", buf, sizeof(buf)); + if (priv->spi_cmd_read_id_sz == 4) { + if (!fu_memread_uint32_safe(buf, + sizeof(buf), + 0x0, + &priv->flash_id, + G_BIG_ENDIAN, + error)) + return FALSE; + } else if (priv->spi_cmd_read_id_sz == 2) { + guint16 tmp = 0; + if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_BIG_ENDIAN, error)) + return FALSE; + priv->flash_id = tmp; + } else if (priv->spi_cmd_read_id_sz == 1) { + guint8 tmp = 0; + if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x0, &tmp, error)) + return FALSE; + priv->flash_id = tmp; + } + return TRUE; +} + +static gboolean +fu_vli_device_setup(FuDevice *device, GError **error) +{ + FuVliDevice *self = FU_VLI_DEVICE(device); + FuVliDevicePrivate *priv = GET_PRIVATE(self); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_vli_device_parent_class)->setup(device, error)) + return FALSE; + + /* get the flash chip attached */ + if (priv->spi_auto_detect) { + if (!fu_vli_device_spi_read_flash_id(self, error)) { + g_prefix_error(error, "failed to read SPI chip ID: "); + return FALSE; + } + if (priv->flash_id != 0x0) { + g_autofree gchar *flash_id = fu_vli_device_get_flash_id_str(self); + + /* use the correct flash device */ + fu_cfi_device_set_flash_id(priv->cfi_device, flash_id); + if (!fu_device_setup(FU_DEVICE(priv->cfi_device), error)) + return FALSE; + + /* add extra instance IDs to include the SPI variant */ + fu_device_add_instance_str(device, "SPI", flash_id); + if (!fu_device_build_instance_id(device, + error, + "USB", + "VID", + "PID", + "SPI", + NULL)) + return FALSE; + fu_device_build_instance_id(device, + NULL, + "USB", + "VID", + "PID", + "SPI", + "REV", + NULL); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) +{ + FuVliDevice *self = FU_VLI_DEVICE(device); + FuVliDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp = 0; + + if (g_strcmp0(key, "CfiDeviceCmdReadIdSz") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->spi_cmd_read_id_sz = tmp; + return TRUE; + } + if (g_strcmp0(key, "VliSpiAutoDetect") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error)) + return FALSE; + priv->spi_auto_detect = tmp > 0; + return TRUE; + } + if (g_strcmp0(key, "VliDeviceKind") == 0) { + FuVliDeviceKind device_kind; + device_kind = fu_vli_common_device_kind_from_string(value); + if (device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "VliDeviceKind %s is not supported", + value); + return FALSE; + } + fu_vli_device_set_kind(self, device_kind); + return TRUE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_vli_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) +{ + FuVliDevice *self = FU_VLI_DEVICE(device); + g_hash_table_insert(metadata, g_strdup("GType"), g_strdup(G_OBJECT_TYPE_NAME(self))); +} + +static void +fu_vli_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + FuVliDevice *self = FU_VLI_DEVICE(object); + FuVliDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_KIND: + g_value_set_uint(value, priv->kind); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_vli_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + FuVliDevice *self = FU_VLI_DEVICE(object); + switch (prop_id) { + case PROP_KIND: + fu_vli_device_set_kind(self, g_value_get_uint(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +fu_vli_device_constructed(GObject *obj) +{ + FuVliDevice *self = FU_VLI_DEVICE(obj); + FuVliDevicePrivate *priv = GET_PRIVATE(self); + priv->cfi_device = fu_cfi_device_new(fu_device_get_context(FU_DEVICE(self)), NULL); +} + +static void +fu_vli_device_init(FuVliDevice *self) +{ + FuVliDevicePrivate *priv = GET_PRIVATE(self); + priv->spi_cmd_read_id_sz = 2; + priv->spi_auto_detect = TRUE; + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER); +} + +static void +fu_vli_device_finalize(GObject *obj) +{ + FuVliDevice *self = FU_VLI_DEVICE(obj); + FuVliDevicePrivate *priv = GET_PRIVATE(self); + g_object_unref(priv->cfi_device); + G_OBJECT_CLASS(fu_vli_device_parent_class)->finalize(obj); +} + +static void +fu_vli_device_class_init(FuVliDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + /* properties */ + object_class->get_property = fu_vli_device_get_property; + object_class->set_property = fu_vli_device_set_property; + object_class->constructed = fu_vli_device_constructed; + object_class->finalize = fu_vli_device_finalize; + + /** + * FuVliDevice:kind: + * + * The kind of VLI device. + */ + pspec = g_param_spec_uint("kind", + NULL, + NULL, + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_KIND, pspec); + + klass_device->to_string = fu_vli_device_to_string; + klass_device->set_quirk_kv = fu_vli_device_set_quirk_kv; + klass_device->setup = fu_vli_device_setup; + klass_device->report_metadata_pre = fu_vli_device_report_metadata_pre; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-device.h new file mode 100644 index 0000000000000000000000000000000000000000..aafe965c1ffd8c8609cdcd80c76aa19414b00cb9 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-device.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-common.h" + +#define FU_TYPE_VLI_DEVICE (fu_vli_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuVliDevice, fu_vli_device, FU, VLI_DEVICE, FuUsbDevice) + +struct _FuVliDeviceClass { + FuUsbDeviceClass parent_class; + gboolean (*spi_chip_erase)(FuVliDevice *self, GError **error); + gboolean (*spi_sector_erase)(FuVliDevice *self, guint32 addr, GError **error); + gboolean (*spi_read_data)(FuVliDevice *self, + guint32 addr, + guint8 *buf, + gsize bufsz, + GError **error); + gboolean (*spi_read_status)(FuVliDevice *self, guint8 *status, GError **error); + gboolean (*spi_write_enable)(FuVliDevice *self, GError **error); + gboolean (*spi_write_data)(FuVliDevice *self, + guint32 addr, + const guint8 *buf, + gsize bufsz, + GError **error); + gboolean (*spi_write_status)(FuVliDevice *self, guint8 status, GError **error); +}; + +#define FU_VLI_DEVICE_TIMEOUT 3000 /* ms */ +#define FU_VLI_DEVICE_TXSIZE 0x20 /* bytes */ + +void +fu_vli_device_set_kind(FuVliDevice *self, FuVliDeviceKind device_kind); +void +fu_vli_device_set_spi_auto_detect(FuVliDevice *self, gboolean spi_auto_detect); +FuVliDeviceKind +fu_vli_device_get_kind(FuVliDevice *self); +guint32 +fu_vli_device_get_offset(FuVliDevice *self); +FuCfiDevice * +fu_vli_device_get_cfi_device(FuVliDevice *self); +gboolean +fu_vli_device_spi_erase_sector(FuVliDevice *self, guint32 addr, GError **error); +gboolean +fu_vli_device_spi_erase_all(FuVliDevice *self, FuProgress *progress, GError **error); +gboolean +fu_vli_device_spi_erase(FuVliDevice *self, + guint32 addr, + gsize sz, + FuProgress *progress, + GError **error); +gboolean +fu_vli_device_spi_read_block(FuVliDevice *self, + guint32 addr, + guint8 *buf, + gsize bufsz, + GError **error); +GBytes * +fu_vli_device_spi_read(FuVliDevice *self, + guint32 address, + gsize bufsz, + FuProgress *progress, + GError **error); +gboolean +fu_vli_device_spi_write_block(FuVliDevice *self, + guint32 address, + const guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error); +gboolean +fu_vli_device_spi_write(FuVliDevice *self, + guint32 address, + const guint8 *buf, + gsize bufsz, + FuProgress *progress, + GError **error); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-common.c b/fwupd-1.8.6/plugins/vli/fu-vli-pd-common.c new file mode 100644 index 0000000000000000000000000000000000000000..35f1fee0306b54e61c7e02c4c1b1404b1bbcf089 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-common.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vli-pd-common.h" + +FuVliDeviceKind +fu_vli_pd_common_guess_device_kind(guint32 fwver) +{ + guint32 tmp = (fwver & 0x0f000000) >> 24; + if (tmp == 0x01 || tmp == 0x02 || tmp == 0x03) + return FU_VLI_DEVICE_KIND_VL100; + if (tmp == 0x04 || tmp == 0x05 || tmp == 0x06) + return FU_VLI_DEVICE_KIND_VL101; + if (tmp == 0x07 || tmp == 0x08) + return FU_VLI_DEVICE_KIND_VL102; + if (tmp == 0x09 || tmp == 0x0a) + return FU_VLI_DEVICE_KIND_VL103; + if (tmp == 0x0b) + return FU_VLI_DEVICE_KIND_VL104; + if (tmp == 0x0c) + return FU_VLI_DEVICE_KIND_VL105; + return FU_VLI_DEVICE_KIND_UNKNOWN; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-common.h b/fwupd-1.8.6/plugins/vli/fu-vli-pd-common.h new file mode 100644 index 0000000000000000000000000000000000000000..436bc8868cd5a9b3611eb8857009309a7c3ec049 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-common.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-common.h" + +typedef struct __attribute__((packed)) { + guint32 fwver; /* BE */ + guint16 vid; /* LE */ + guint16 pid; /* LE */ +} FuVliPdHdr; + +#define VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY 0x4000 +#define VLI_USBHUB_PD_FLASHMAP_ADDR 0x1003 + +FuVliDeviceKind +fu_vli_pd_common_guess_device_kind(guint32 fwver); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-pd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..d02613e72fa727ac6517a02073205c1ea3a5eaf3 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-device.c @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2015 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-pd-device.h" +#include "fu-vli-pd-firmware.h" +#include "fu-vli-pd-parade-device.h" + +struct _FuVliPdDevice { + FuVliDevice parent_instance; +}; + +/** + * FU_VLI_PD_DEVICE_FLAG_HAS_I2C_PS186: + * + * Device has a PS186 attached via I²C. + */ +#define FU_VLI_PD_DEVICE_FLAG_HAS_I2C_PS186 (1 << 0) + +G_DEFINE_TYPE(FuVliPdDevice, fu_vli_pd_device, FU_TYPE_VLI_DEVICE) + +static gboolean +fu_vli_pd_device_read_regs(FuVliPdDevice *self, + guint16 addr, + guint8 *buf, + gsize bufsz, + GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xe0, + ((addr & 0xff) << 8) | 0x01, + addr >> 8, + buf, + bufsz, + NULL, + 1000, + NULL, + error)) { + g_prefix_error(error, "failed to write register @0x%x: ", addr); + return FALSE; + } + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) { + g_autofree gchar *title = g_strdup_printf("ReadRegs@0x%x", addr); + fu_dump_raw(G_LOG_DOMAIN, title, buf, bufsz); + } + return TRUE; +} + +static gboolean +fu_vli_pd_device_read_reg(FuVliPdDevice *self, guint16 addr, guint8 *value, GError **error) +{ + return fu_vli_pd_device_read_regs(self, addr, value, 0x1, error); +} + +static gboolean +fu_vli_pd_device_write_reg(FuVliPdDevice *self, guint16 addr, guint8 value, GError **error) +{ + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) { + g_autofree gchar *title = g_strdup_printf("WriteReg@0x%x", addr); + fu_dump_raw(G_LOG_DOMAIN, title, &value, sizeof(value)); + } + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xe0, + ((addr & 0xff) << 8) | 0x02, + addr >> 8, + &value, + sizeof(value), + NULL, + 1000, + NULL, + error)) { + g_prefix_error(error, "failed to write register @0x%x: ", addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_device_spi_read_status(FuVliDevice *self, guint8 *status, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_READ_STATUS, + &spi_cmd, + error)) + return FALSE; + return g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xc5, + spi_cmd, + 0x0000, + status, + 0x1, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error); +} + +static gboolean +fu_vli_pd_device_spi_read_data(FuVliDevice *self, + guint32 addr, + guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + guint16 index; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_READ_DATA, + &spi_cmd, + error)) + return FALSE; + value = ((addr << 8) & 0xff00) | spi_cmd; + index = addr >> 8; + return g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xc4, + value, + index, + buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error); +} + +static gboolean +fu_vli_pd_device_spi_write_status(FuVliDevice *self, guint8 status, GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_WRITE_STATUS, + &spi_cmd, + error)) + return FALSE; + value = ((guint16)status << 8) | spi_cmd; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd8, + value, + 0x0, + NULL, + 0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + return FALSE; + } + + /* Fix_For_GD_&_EN_SPI_Flash */ + g_usleep(100 * 1000); + return TRUE; +} + +static gboolean +fu_vli_pd_device_spi_write_enable(FuVliDevice *self, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_WRITE_EN, + &spi_cmd, + error)) + return FALSE; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd4, + spi_cmd, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write enable SPI: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_device_spi_chip_erase(FuVliDevice *self, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_CHIP_ERASE, + &spi_cmd, + error)) + return FALSE; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd1, + spi_cmd, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_device_spi_sector_erase(FuVliDevice *self, guint32 addr, GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + guint16 index; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_SECTOR_ERASE, + &spi_cmd, + error)) + return FALSE; + value = ((addr << 8) & 0xff00) | spi_cmd; + index = addr >> 8; + return g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd2, + value, + index, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error); +} + +static gboolean +fu_vli_pd_device_spi_write_data(FuVliDevice *self, + guint32 addr, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + guint16 index; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_PAGE_PROG, + &spi_cmd, + error)) + return FALSE; + value = ((addr << 8) & 0xff00) | spi_cmd; + index = addr >> 8; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xdc, + value, + index, + (guint8 *)buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_device_parade_setup(FuVliPdDevice *self, GError **error) +{ + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + + /* add child */ + dev = fu_vli_pd_parade_device_new(FU_VLI_DEVICE(self)); + if (!fu_device_probe(dev, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("%s", error_local->message); + } else { + g_warning("cannot create I²C parade device: %s", error_local->message); + } + return TRUE; + } + if (!fu_device_setup(dev, error)) { + g_prefix_error(error, "failed to set up parade device: "); + return FALSE; + } + fu_device_add_child(FU_DEVICE(self), dev); + return TRUE; +} + +static gboolean +fu_vli_pd_device_setup(FuDevice *device, GError **error) +{ + FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); + guint32 version_raw; + guint8 verbuf[4] = {0x0}; + guint8 tmp = 0; + g_autofree gchar *version_str = NULL; + + /* FuVliDevice->setup */ + if (!FU_DEVICE_CLASS(fu_vli_pd_device_parent_class)->setup(device, error)) + return FALSE; + + /* get version */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xe2, + 0x0001, + 0x0000, + verbuf, + sizeof(verbuf), + NULL, + 1000, + NULL, + error)) { + g_prefix_error(error, "failed to get version: "); + return FALSE; + } + if (!fu_memread_uint32_safe(verbuf, sizeof(verbuf), 0x0, &version_raw, G_BIG_ENDIAN, error)) + return FALSE; + fu_device_set_version_raw(FU_DEVICE(self), version_raw); + version_str = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(FU_DEVICE(self), version_str); + + /* get device kind if not already in ROM mode */ + if (fu_vli_device_get_kind(FU_VLI_DEVICE(self)) == FU_VLI_DEVICE_KIND_UNKNOWN) { + if (!fu_vli_pd_device_read_reg(self, 0x0018, &tmp, error)) + return FALSE; + switch (tmp & 0xF0) { + case 0x00: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL100); + break; + case 0x10: + /* this is also the code for VL101, but VL102 is more likely */ + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL102); + break; + case 0x80: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL103); + break; + case 0x90: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL104); + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to map 0x0018=0x%02X to device kind", + tmp); + return FALSE; + } + } + + /* get bootloader mode */ + if (!fu_vli_pd_device_read_reg(self, 0x00F7, &tmp, error)) + return FALSE; + if ((tmp & 0x80) == 0x00) + 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); + + /* detect any I²C child, e.g. parade device */ + if (fu_device_has_private_flag(device, FU_VLI_PD_DEVICE_FLAG_HAS_I2C_PS186)) { + if (!fu_vli_pd_device_parade_setup(self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_vli_pd_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); + FuVliDeviceKind device_kind; + g_autoptr(FuFirmware) firmware = fu_vli_pd_firmware_new(); + + /* check size */ + if (g_bytes_get_size(fw) > fu_device_get_firmware_size_max(device)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware too large, got 0x%x, expected <= 0x%x", + (guint)g_bytes_get_size(fw), + (guint)fu_device_get_firmware_size_max(device)); + return NULL; + } + + /* check is compatible with firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + device_kind = fu_vli_pd_firmware_get_kind(FU_VLI_PD_FIRMWARE(firmware)); + if (fu_vli_device_get_kind(FU_VLI_DEVICE(self)) != device_kind) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got %s, expected %s", + fu_vli_common_device_kind_to_string(device_kind), + fu_vli_common_device_kind_to_string( + fu_vli_device_get_kind(FU_VLI_DEVICE(self)))); + return NULL; + } + + /* we could check this against flags */ + g_debug("parsed version: %s", fu_firmware_get_version(firmware)); + return g_steal_pointer(&firmware); +} + +static GBytes * +fu_vli_pd_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* require detach -> attach */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return NULL; + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + return fu_vli_device_spi_read(FU_VLI_DEVICE(self), + 0x0, + fu_device_get_firmware_size_max(device), + progress, + error); +} + +static gboolean +fu_vli_pd_device_write_gpios(FuVliPdDevice *self, GError **error) +{ + /* disable UART-Rx mode */ + if (!fu_vli_pd_device_write_reg(self, 0x0015, 0x7F, error)) + return FALSE; + /* disable 'Watch Mode', chip is not in debug mode */ + if (!fu_vli_pd_device_write_reg(self, 0x0019, 0x00, error)) + return FALSE; + /* GPIO3 output enable, switch/CMOS/Boost control pin */ + if (!fu_vli_pd_device_write_reg(self, 0x001C, 0x02, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_device_write_dual_firmware(FuVliPdDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + const guint8 *buf = NULL; + const guint8 *sbuf = NULL; + gsize bufsz = 0; + gsize sbufsz = 0; + guint16 crc_actual; + guint16 crc_file = 0x0; + guint32 sec_addr = 0x28000; + g_autoptr(GBytes) spi_fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "crc"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 45, "backup"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 45, "primary"); + + /* check spi fw1 crc16 */ + spi_fw = fu_vli_device_spi_read(FU_VLI_DEVICE(self), + fu_vli_device_get_offset(FU_VLI_DEVICE(self)), + fu_device_get_firmware_size_max(FU_DEVICE(self)), + fu_progress_get_child(progress), + error); + if (spi_fw == NULL) + return FALSE; + sbuf = g_bytes_get_data(spi_fw, &sbufsz); + if (sbufsz != 0x8000) + sec_addr = 0x30000; + if (!fu_memread_uint16_safe(sbuf, sbufsz, sbufsz - 2, &crc_file, G_LITTLE_ENDIAN, error)) { + g_prefix_error(error, "failed to read file CRC: "); + return FALSE; + } + crc_actual = fu_crc16(sbuf, sbufsz - 2); + fu_progress_step_done(progress); + + /* update fw2 first if fw1 correct */ + buf = g_bytes_get_data(fw, &bufsz); + if (crc_actual == crc_file) { + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + sec_addr, + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + fu_vli_device_get_offset(FU_VLI_DEVICE(self)), + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* else update fw1 first */ + } else { + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + fu_vli_device_get_offset(FU_VLI_DEVICE(self)), + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + sec_addr, + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_pd_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); + gsize bufsz = 0; + guint8 tmp = 0; + const guint8 *buf = NULL; + g_autoptr(GBytes) fw = NULL; + + /* binary blob */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* write GPIOs in new mode */ + if (!fu_vli_pd_device_write_gpios(self, error)) + return FALSE; + + /* disable write protect in GPIO_3 */ + if (!fu_vli_pd_device_read_reg(self, 0x0003, &tmp, error)) + return FALSE; + if (!fu_vli_pd_device_write_reg(self, 0x0003, tmp | 0x44, error)) + return FALSE; + + /* dual image on VL103 */ + if (fu_vli_device_get_kind(FU_VLI_DEVICE(device)) == FU_VLI_DEVICE_KIND_VL103 && + fu_device_has_flag(device, FWUPD_DEVICE_FLAG_DUAL_IMAGE)) + return fu_vli_pd_device_write_dual_firmware(self, fw, progress, error); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 63, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 37, NULL); + + /* erase */ + if (!fu_vli_device_spi_erase_all(FU_VLI_DEVICE(self), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* write in chunks */ + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + fu_vli_device_get_offset(FU_VLI_DEVICE(self)), + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + + /* success */ + fu_progress_step_done(progress); + return TRUE; +} + +static gboolean +fu_vli_pd_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* sanity check */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + + /* write GPIOs */ + if (!fu_vli_pd_device_write_gpios(self, error)) + return FALSE; + + /* VL103 set ROM sig does not work, so use alternate function */ + if (fu_vli_device_get_kind(FU_VLI_DEVICE(device)) == FU_VLI_DEVICE_KIND_VL103) { + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(device)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xc0, + 0x0000, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + &error_local)) { + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_FAILED)) { + g_debug("ignoring %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to restart device: "); + return FALSE; + } + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + /* patch APP5 FW bug (2AF2 -> 2AE2) on VL100-App5 and VL102 */ + if (fu_vli_device_get_kind(FU_VLI_DEVICE(device)) == FU_VLI_DEVICE_KIND_VL100 || + fu_vli_device_get_kind(FU_VLI_DEVICE(device)) == FU_VLI_DEVICE_KIND_VL102) { + guint8 tmp = 0; + if (!fu_vli_pd_device_read_reg(self, 0x0018, &tmp, error)) + return FALSE; + if (tmp != 0x80) { + if (!fu_vli_pd_device_write_reg(self, 0x2AE2, 0x1E, error)) + return FALSE; + if (!fu_vli_pd_device_write_reg(self, 0x2AE3, 0xC3, error)) + return FALSE; + if (!fu_vli_pd_device_write_reg(self, 0x2AE4, 0x5A, error)) + return FALSE; + if (!fu_vli_pd_device_write_reg(self, 0x2AE5, 0x87, error)) + return FALSE; + } + } + + /* set ROM sig */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(device)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xa0, + 0x0000, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) + return FALSE; + + /* reset from SPI_Code into ROM_Code */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(device)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xb0, + 0x0000, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + &error_local)) { + if (g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_FAILED)) { + g_debug("ignoring %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to restart device: "); + return FALSE; + } + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_vli_pd_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + /* Work around a silicon bug: Once the CC-resistor is removed, the + * CC-host thinks the device is un-plugged and turn off VBUS (power). + * When VL103 is powered-off, VL103 puts a resistor at CC-pin. + * The CC-host will think the device is re-plugged and provides VBUS + * again. Then, VL103 will be powered on and runs new FW. */ + if (fu_vli_device_get_kind(FU_VLI_DEVICE(device)) == FU_VLI_DEVICE_KIND_VL103) { + if (!fu_vli_pd_device_write_reg(self, 0x1201, 0xf6, error)) + return FALSE; + if (!fu_vli_pd_device_write_reg(self, 0x1001, 0xf6, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + + /* sanity check */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* chip reset command works only for non-VL103 */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(device)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xb0, + 0x0000, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + &error_local)) { + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE) || + g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_TIMED_OUT) || + g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_FAILED)) { + g_debug("ignoring %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to restart device: "); + return FALSE; + } + } + + /* replug */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_vli_pd_device_kind_changed_cb(FuVliDevice *device, GParamSpec *pspec, gpointer user_data) +{ + if (fu_vli_device_get_kind(device) == FU_VLI_DEVICE_KIND_VL103) { + /* wait for USB-C timeout */ + fu_device_set_remove_delay(FU_DEVICE(device), 10000); + } +} + +static void +fu_vli_pd_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_vli_pd_device_init(FuVliPdDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_protocol(FU_DEVICE(self), "com.vli.pd"); + fu_device_set_summary(FU_DEVICE(self), "USB power distribution device"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_vli_device_set_spi_auto_detect(FU_VLI_DEVICE(self), FALSE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_VLI_PD_DEVICE_FLAG_HAS_I2C_PS186, + "has-i2c-ps186"); + + /* connect up attach or detach vfuncs when kind is known */ + g_signal_connect(FU_VLI_DEVICE(self), + "notify::kind", + G_CALLBACK(fu_vli_pd_device_kind_changed_cb), + NULL); +} + +static void +fu_vli_pd_device_class_init(FuVliPdDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuVliDeviceClass *klass_vli_device = FU_VLI_DEVICE_CLASS(klass); + klass_device->dump_firmware = fu_vli_pd_device_dump_firmware; + klass_device->write_firmware = fu_vli_pd_device_write_firmware; + klass_device->prepare_firmware = fu_vli_pd_device_prepare_firmware; + klass_device->attach = fu_vli_pd_device_attach; + klass_device->detach = fu_vli_pd_device_detach; + klass_device->setup = fu_vli_pd_device_setup; + klass_device->set_progress = fu_vli_pd_device_set_progress; + klass_vli_device->spi_chip_erase = fu_vli_pd_device_spi_chip_erase; + klass_vli_device->spi_sector_erase = fu_vli_pd_device_spi_sector_erase; + klass_vli_device->spi_read_data = fu_vli_pd_device_spi_read_data; + klass_vli_device->spi_read_status = fu_vli_pd_device_spi_read_status; + klass_vli_device->spi_write_data = fu_vli_pd_device_spi_write_data; + klass_vli_device->spi_write_enable = fu_vli_pd_device_spi_write_enable; + klass_vli_device->spi_write_status = fu_vli_pd_device_spi_write_status; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-pd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..3f63aa0c7fd97e276ae5bb4bdc85550c6eb40b44 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-device.h" + +#define FU_TYPE_VLI_PD_DEVICE (fu_vli_pd_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVliPdDevice, fu_vli_pd_device, FU, VLI_PD_DEVICE, FuVliDevice) diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-firmware.c b/fwupd-1.8.6/plugins/vli/fu-vli-pd-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..d6c30e054cd1842a82166c9635f298ede4d3b90c --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-firmware.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-pd-common.h" +#include "fu-vli-pd-firmware.h" + +struct _FuVliPdFirmware { + FuFirmwareClass parent_instance; + FuVliDeviceKind device_kind; + FuVliPdHdr hdr; +}; + +G_DEFINE_TYPE(FuVliPdFirmware, fu_vli_pd_firmware, FU_TYPE_FIRMWARE) + +FuVliDeviceKind +fu_vli_pd_firmware_get_kind(FuVliPdFirmware *self) +{ + g_return_val_if_fail(FU_IS_VLI_PD_FIRMWARE(self), 0); + return self->device_kind; +} + +guint16 +fu_vli_pd_firmware_get_vid(FuVliPdFirmware *self) +{ + g_return_val_if_fail(FU_IS_VLI_PD_FIRMWARE(self), 0); + return GUINT16_FROM_LE(self->hdr.vid); +} + +guint16 +fu_vli_pd_firmware_get_pid(FuVliPdFirmware *self) +{ + g_return_val_if_fail(FU_IS_VLI_PD_FIRMWARE(self), 0); + return GUINT16_FROM_LE(self->hdr.pid); +} + +static gboolean +fu_vli_pd_firmware_validate_header(FuVliPdFirmware *self) +{ + if (GUINT16_FROM_LE(self->hdr.vid) == 0x2109) + return TRUE; + if (GUINT16_FROM_LE(self->hdr.vid) == 0x17EF) + return TRUE; + if (GUINT16_FROM_LE(self->hdr.vid) == 0x2D01) + return TRUE; + if (GUINT16_FROM_LE(self->hdr.vid) == 0x06C4) + return TRUE; + return FALSE; +} + +static void +fu_vli_pd_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuVliPdFirmware *self = FU_VLI_PD_FIRMWARE(firmware); + fu_xmlb_builder_insert_kv(bn, + "device_kind", + fu_vli_common_device_kind_to_string(self->device_kind)); + fu_xmlb_builder_insert_kx(bn, "vid", fu_vli_pd_firmware_get_vid(self)); + fu_xmlb_builder_insert_kx(bn, "pid", fu_vli_pd_firmware_get_pid(self)); +} + +static gboolean +fu_vli_pd_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuVliPdFirmware *self = FU_VLI_PD_FIRMWARE(firmware); + gsize bufsz = 0; + guint32 fwver; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *fwver_str = NULL; + + /* map header from new offset location */ + if (!fu_memcpy_safe((guint8 *)&self->hdr, + sizeof(self->hdr), + 0x0, + buf, + bufsz, + VLI_USBHUB_PD_FLASHMAP_ADDR, + sizeof(self->hdr), + error)) { + g_prefix_error(error, "failed to read header: "); + return FALSE; + } + + /* fall back to legacy location */ + if (!fu_vli_pd_firmware_validate_header(self)) { + if (!fu_memcpy_safe((guint8 *)&self->hdr, + sizeof(self->hdr), + 0x0, + buf, + bufsz, + VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, + sizeof(self->hdr), + error)) { + g_prefix_error(error, "failed to read header: "); + return FALSE; + } + } + + /* urgh, not found */ + if (!fu_vli_pd_firmware_validate_header(self)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "header invalid, VID not supported"); + return FALSE; + } + + /* guess device kind from fwver */ + fwver = GUINT32_FROM_BE(self->hdr.fwver); + self->device_kind = fu_vli_pd_common_guess_device_kind(fwver); + if (self->device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "version invalid, using 0x%x", + fwver); + return FALSE; + } + fwver_str = fu_version_from_uint32(fwver, FWUPD_VERSION_FORMAT_QUAD); + fu_firmware_set_version(firmware, fwver_str); + fu_firmware_set_version_raw(firmware, fwver); + + /* check size */ + if (bufsz != fu_vli_common_device_kind_get_size(self->device_kind)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "size invalid, got 0x%x expected 0x%x", + (guint)bufsz, + fu_vli_common_device_kind_get_size(self->device_kind)); + return FALSE; + } + + /* check CRC */ + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint16 crc_actual; + guint16 crc_file = 0x0; + if (!fu_memread_uint16_safe(buf, + bufsz, + bufsz - 2, + &crc_file, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to read file CRC: "); + return FALSE; + } + crc_actual = fu_crc16(buf, bufsz - 2); + if (crc_actual != crc_file) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "CRC invalid, got 0x%x expected 0x%x", + crc_file, + crc_actual); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_vli_pd_firmware_init(FuVliPdFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_vli_pd_firmware_class_init(FuVliPdFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_vli_pd_firmware_parse; + klass_firmware->export = fu_vli_pd_firmware_export; +} + +FuFirmware * +fu_vli_pd_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_VLI_PD_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-firmware.h b/fwupd-1.8.6/plugins/vli/fu-vli-pd-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..7731b2c642016f315b31f48b842da6b748ae28b3 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-firmware.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-common.h" + +#define FU_TYPE_VLI_PD_FIRMWARE (fu_vli_pd_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuVliPdFirmware, fu_vli_pd_firmware, FU, VLI_PD_FIRMWARE, FuFirmware) + +FuFirmware * +fu_vli_pd_firmware_new(void); +FuVliDeviceKind +fu_vli_pd_firmware_get_kind(FuVliPdFirmware *self); +guint16 +fu_vli_pd_firmware_get_vid(FuVliPdFirmware *self); +guint16 +fu_vli_pd_firmware_get_pid(FuVliPdFirmware *self); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-parade-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-pd-parade-device.c new file mode 100644 index 0000000000000000000000000000000000000000..85d823c7ed77414e6afa87676a221d7e869787f2 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-parade-device.c @@ -0,0 +1,732 @@ +/* + * Copyright (C) 2015 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-pd-device.h" +#include "fu-vli-pd-parade-device.h" + +struct _FuVliPdParadeDevice { + FuDevice parent_instance; + FuVliDeviceKind device_kind; + guint8 page2; /* base address */ + guint8 page7; /* base address */ +}; + +G_DEFINE_TYPE(FuVliPdParadeDevice, fu_vli_pd_parade_device, FU_TYPE_DEVICE) + +#define FU_VLI_PD_PARADE_I2C_CMD_WRITE 0xa6 +#define FU_VLI_PD_PARADE_I2C_CMD_READ 0xa5 + +static void +fu_vli_pd_parade_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE(device); + fu_string_append(str, + idt, + "DeviceKind", + fu_vli_common_device_kind_to_string(self->device_kind)); + fu_string_append_kx(str, idt, "Page2", self->page2); + fu_string_append_kx(str, idt, "Page7", self->page7); +} + +static gboolean +fu_vli_pd_parade_device_i2c_read(FuVliPdParadeDevice *self, + guint8 page2, + guint8 reg_offset, /* customers addr offset */ + guint8 *buf, + gsize bufsz, + GError **error) +{ + guint16 value; + + /* sanity check */ + if (bufsz > 0x40) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "request too large"); + return FALSE; + } + + /* VL103 FW only Use bits[7:1], so divide by 2 */ + value = ((guint16)reg_offset << 8) | (page2 >> 1); + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + FU_VLI_PD_PARADE_I2C_CMD_READ, + value, + 0x0, + buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read 0x%x:0x%x: ", page2, reg_offset); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_i2c_write(FuVliPdParadeDevice *self, + guint8 page2, + guint8 reg_offset, /* customers addr offset */ + guint8 val, /* only one byte supported */ + GError **error) +{ + guint16 value; + guint16 index; + guint8 buf[2] = {0x0}; /* apparently unused... */ + + /* VL103 FW only Use bits[7:1], so divide by 2 */ + value = ((guint16)reg_offset << 8) | (page2 >> 1); + index = (guint16)val << 8; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + FU_VLI_PD_PARADE_I2C_CMD_WRITE, + value, + index, + buf, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write 0x%x:0x%x: ", page2, reg_offset); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_start_mcu(FuVliPdParadeDevice *self, GError **error) +{ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xBC, 0x00, error)) { + g_prefix_error(error, "failed to start MCU: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_stop_mcu(FuVliPdParadeDevice *self, GError **error) +{ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xBC, 0xC0, error)) { + g_prefix_error(error, "failed to stop MCU: "); + return FALSE; + } + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xBC, 0x40, error)) { + g_prefix_error(error, "failed to stop MCU 2nd: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_set_offset(FuVliPdParadeDevice *self, guint16 addr, GError **error) +{ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x8E, addr >> 8, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x8F, addr & 0xff, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_read_fw_ver(FuVliPdParadeDevice *self, GError **error) +{ + guint8 buf[0x20] = {0x0}; + g_autofree gchar *version_str = NULL; + + /* stop MCU */ + if (!fu_vli_pd_parade_device_stop_mcu(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_offset(self, 0x0, error)) + return FALSE; + g_usleep(1000 * 10); + if (!fu_vli_pd_parade_device_i2c_read(self, self->page7, 0x02, buf, 0x1, error)) + return FALSE; + if (buf[0] != 0x01 && buf[0] != 0x02) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported on this device: buffer was 0x%02x", + buf[0]); + return FALSE; + } + + g_debug("getting FW%X version", buf[0]); + if (!fu_vli_pd_parade_device_set_offset(self, 0x5000 | buf[0], error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_read(self, self->page7, 0x00, buf, sizeof(buf), error)) + return FALSE; + + /* start MCU */ + if (!fu_vli_pd_parade_device_start_mcu(self, error)) + return FALSE; + + /* format version triplet */ + version_str = g_strdup_printf("%u.%u.%u", buf[0], buf[1], buf[2]); + fu_device_set_version(FU_DEVICE(self), version_str); + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_set_wp(FuVliPdParadeDevice *self, gboolean val, GError **error) +{ + return fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xB3, val ? 0x10 : 0x00, error); +} + +static gboolean +fu_vli_pd_parade_device_write_enable(FuVliPdParadeDevice *self, GError **error) +{ + /* Set_WP_High, SPI_WEN_06, Len_00, Trigger_Write, Set_WP_Low */ + if (!fu_vli_pd_parade_device_set_wp(self, TRUE, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, 0x06, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x92, 0x00, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x93, 0x05, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_wp(self, FALSE, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_write_disable(FuVliPdParadeDevice *self, GError **error) +{ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0x00, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_write_status(FuVliPdParadeDevice *self, + guint8 target_status, + GError **error) +{ + /* Set_WP_High, SPI_WSTS_01, Target_Status, Len_01, Trigger_Write, Set_WP_Low */ + if (!fu_vli_pd_parade_device_set_wp(self, TRUE, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, 0x01, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, target_status, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x92, 0x01, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x93, 0x05, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_wp(self, FALSE, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_wait_ready(FuVliPdParadeDevice *self, GError **error) +{ + gboolean ret = FALSE; + guint limit = 100; + guint8 buf = 0x0; + + /* wait for SPI ROM */ + for (guint wait_cnt1 = 0; wait_cnt1 < limit; wait_cnt1++) { + buf = 0xFF; + if (!fu_vli_pd_parade_device_i2c_read(self, + self->page2, + 0x9E, + &buf, + sizeof(buf), + error)) + return FALSE; + /* busy status: + * bit[1,0]:Byte_Program + * bit[3,2]:Sector Erase + * bit[5,4]:Chip Erase */ + if ((buf & 0x0C) == 0) { + ret = TRUE; + break; + } + } + if (!ret) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to wait for SPI not BUSY"); + return FALSE; + } + + /* wait for SPI ROM status clear */ + ret = FALSE; + for (guint wait_cnt1 = 0; wait_cnt1 < limit; wait_cnt1++) { + gboolean ret2 = FALSE; + + /* SPI_RSTS_05, Len_01, Trigger_Read */ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, 0x05, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x92, 0x00, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x93, 0x01, error)) + return FALSE; + + /* wait for cmd done */ + for (guint wait_cnt2 = 0; wait_cnt2 < limit; wait_cnt2++) { + buf = 0xFF; + if (!fu_vli_pd_parade_device_i2c_read(self, + self->page2, + 0x93, + &buf, + sizeof(buf), + error)) + return FALSE; + if ((buf & 0x01) == 0) { + ret2 = TRUE; + break; + } + } + if (!ret2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to wait for SPI CMD done"); + return FALSE; + } + + /* Wait_SPI_STS_00 */ + buf = 0xFF; + if (!fu_vli_pd_parade_device_i2c_read(self, + self->page2, + 0x91, + &buf, + sizeof(buf), + error)) + return FALSE; + if ((buf & 0x01) == 0) { + ret = TRUE; + break; + } + } + if (!ret) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to wait for SPI status clear"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_sector_erase(FuVliPdParadeDevice *self, guint16 addr, GError **error) +{ + /* SPI_SE_20, SPI_Adr_H, SPI_Adr_M, SPI_Adr_L, Len_03, Trigger_Write */ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, 0x20, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, addr >> 8, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, addr & 0xff, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x90, 0x00, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x92, 0x03, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x93, 0x05, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_enable_mapping(FuVliPdParadeDevice *self, GError **error) +{ + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0xAA, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0x55, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0x50, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0x41, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0x52, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xDA, 0x44, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_block_erase(FuVliPdParadeDevice *self, guint8 block_idx, GError **error) +{ + /* erase */ + for (guint idx = 0x00; idx < 0x100; idx += 0x10) { + if (!fu_vli_pd_parade_device_write_enable(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_wp(self, TRUE, error)) + return FALSE; + if (!fu_vli_pd_parade_device_sector_erase(self, + ((guint16)block_idx << 8) | idx, + error)) + return FALSE; + if (!fu_vli_pd_parade_device_wait_ready(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_wp(self, FALSE, error)) + return FALSE; + } + + /* verify */ + for (guint idx = 0; idx < 0x100; idx += 0x10) { + guint8 buf[0x20] = {0xff}; + if (!fu_vli_pd_parade_device_set_offset(self, (block_idx << 8) | idx, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_read(self, self->page7, 0, buf, 0x20, error)) + return FALSE; + for (guint idx2 = 0; idx2 < 0x20; idx2++) { + if (buf[idx2] != 0xFF) { + guint32 addr = (block_idx << 16) + (idx << 8); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Erase failed @0x%x", + addr); + return FALSE; + } + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_block_write(FuVliPdParadeDevice *self, + guint8 block_idx, + const guint8 *txbuf, + GError **error) +{ + for (guint idx = 0; idx < 0x100; idx++) { + if (!fu_vli_pd_parade_device_set_offset(self, (block_idx << 8) | idx, error)) + return FALSE; + for (guint idx2 = 0; idx2 < 0x100; idx2++) { + guint32 buf_offset = (idx << 8) + idx2; + if (!fu_vli_pd_parade_device_i2c_write(self, + self->page7, + (guint8)idx2, + txbuf[buf_offset], + error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_block_read(FuVliPdParadeDevice *self, + guint8 block_idx, + guint8 *buf, + gsize bufsz, + GError **error) +{ + for (guint idx = 0; idx < 0x100; idx++) { + if (!fu_vli_pd_parade_device_set_offset(self, (block_idx << 8) | idx, error)) + return FALSE; + for (guint idx2 = 0; idx2 < 0x100; idx2 += 0x20) { + guint buf_offset = (idx << 8) + idx2; + if (!fu_vli_pd_parade_device_i2c_read(self, + self->page7, + idx2, + buf + buf_offset, + 0x20, + error)) + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_vli_pd_parade_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE(device); + FuVliPdDevice *parent = FU_VLI_PD_DEVICE(fu_device_get_parent(device)); + FuChunk *chk0; + guint8 buf[0x20]; + guint block_idx_tmp; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GByteArray) buf_verify = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_verify = NULL; + g_autoptr(GPtrArray) blocks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 19, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 45, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 36, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + /* stop MPU and reset SPI */ + if (!fu_vli_pd_parade_device_stop_mcu(self, error)) + return FALSE; + + /* 64K block erase */ + if (!fu_vli_pd_parade_device_write_enable(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_write_status(self, 0x00, error)) + return FALSE; + if (!fu_vli_pd_parade_device_wait_ready(self, error)) + return FALSE; + blocks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, 0x10000); + for (guint i = 1; i < blocks->len; i++) { + FuChunk *chk = g_ptr_array_index(blocks, i); + if (!fu_vli_pd_parade_device_block_erase(self, fu_chunk_get_idx(chk), error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + blocks->len); + } + fu_progress_step_done(progress); + + /* load F/W to SPI ROM */ + if (!fu_vli_pd_parade_device_enable_mapping(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x82, 0x20, error)) + return FALSE; /* Reset_CLT2SPI_Interface */ + g_usleep(1000 * 100); + if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0x82, 0x00, error)) + return FALSE; + + /* write blocks */ + for (guint i = 1; i < blocks->len; i++) { + FuChunk *chk = g_ptr_array_index(blocks, i); + if (!fu_vli_pd_parade_device_block_write(self, + fu_chunk_get_idx(chk), + fu_chunk_get_data(chk), + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + blocks->len); + } + if (!fu_vli_pd_parade_device_write_disable(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* add the new boot config into the verify buffer */ + buf_verify = g_byte_array_sized_new(g_bytes_get_size(fw)); + chk0 = g_ptr_array_index(blocks, 0); + g_byte_array_append(buf_verify, fu_chunk_get_data(chk0), fu_chunk_get_data_sz(chk0)); + + /* verify SPI ROM, ignoring the boot config */ + for (guint i = 1; i < blocks->len; i++) { + FuChunk *chk = g_ptr_array_index(blocks, i); + gsize bufsz = fu_chunk_get_data_sz(chk); + g_autofree guint8 *vbuf = g_malloc0(bufsz); + if (!fu_vli_pd_parade_device_block_read(self, + fu_chunk_get_idx(chk), + vbuf, + bufsz, + error)) + return FALSE; + g_byte_array_append(buf_verify, vbuf, bufsz); + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + blocks->len); + } + fw_verify = g_byte_array_free_to_bytes(g_steal_pointer(&buf_verify)); + if (!fu_bytes_compare(fw, fw_verify, error)) + return FALSE; + fu_progress_step_done(progress); + + /* save boot config into Block_0 */ + if (!fu_vli_pd_parade_device_write_enable(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_wp(self, TRUE, error)) + return FALSE; + if (!fu_vli_pd_parade_device_sector_erase(self, 0x0, error)) + return FALSE; + if (!fu_vli_pd_parade_device_wait_ready(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_set_wp(self, FALSE, error)) + return FALSE; + + /* Page_HW_Write_Disable */ + if (!fu_vli_pd_parade_device_enable_mapping(self, error)) + return FALSE; + + block_idx_tmp = 1; + if (!fu_vli_pd_parade_device_set_offset(self, 0x0, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page7, 0x00, 0x55, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, self->page7, 0x01, 0xAA, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, + self->page7, + 0x02, + (guint8)block_idx_tmp, + error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_write(self, + self->page7, + 0x03, + (guint8)(0x01 - block_idx_tmp), + error)) + return FALSE; + if (!fu_vli_pd_parade_device_write_disable(self, error)) + return FALSE; + + /* check boot config data */ + if (!fu_vli_pd_parade_device_set_offset(self, 0x0, error)) + return FALSE; + if (!fu_vli_pd_parade_device_i2c_read(self, self->page7, 0, buf, sizeof(buf), error)) + return FALSE; + if (buf[0] != 0x55 || buf[1] != 0xAA || buf[2] != block_idx_tmp || + buf[3] != 0x01 - block_idx_tmp) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "boot config data error"); + return FALSE; + } + + /* enable write protection */ + if (!fu_vli_pd_parade_device_write_enable(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_write_status(self, 0x8C, error)) + return FALSE; + if (!fu_vli_pd_parade_device_wait_ready(self, error)) + return FALSE; + if (!fu_vli_pd_parade_device_write_disable(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static GBytes * +fu_vli_pd_parade_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliPdDevice *parent = FU_VLI_PD_DEVICE(fu_device_get_parent(device)); + FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GByteArray) fw = g_byte_array_new(); + g_autoptr(GPtrArray) blocks = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return NULL; + + /* stop MPU and reset SPI */ + if (!fu_vli_pd_parade_device_stop_mcu(self, error)) + return NULL; + + /* read */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_VERIFY); + fu_byte_array_set_size(fw, fu_device_get_firmware_size_max(device), 0x00); + blocks = fu_chunk_array_mutable_new(fw->data, fw->len, 0x0, 0x0, 0x10000); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, blocks->len); + for (guint i = 0; i < blocks->len; i++) { + FuChunk *chk = g_ptr_array_index(blocks, i); + if (!fu_vli_pd_parade_device_block_read(self, + fu_chunk_get_idx(chk), + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) + return NULL; + fu_progress_step_done(progress); + } + return g_byte_array_free_to_bytes(g_steal_pointer(&fw)); +} + +static gboolean +fu_vli_pd_parade_device_probe(FuDevice *device, GError **error) +{ + FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE(device); + + /* get version */ + if (!fu_vli_pd_parade_device_read_fw_ver(self, error)) + return FALSE; + + /* use header to populate device info */ + fu_device_add_instance_str(device, + "I2C", + fu_vli_common_device_kind_to_string(self->device_kind)); + return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "I2C", NULL); +} + +static void +fu_vli_pd_parade_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_vli_pd_parade_device_init(FuVliPdParadeDevice *self) +{ + self->device_kind = FU_VLI_DEVICE_KIND_PS186; + self->page2 = 0x14; + self->page7 = 0x1E; + fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_protocol(FU_DEVICE(self), "com.vli.i2c"); + fu_device_set_install_duration(FU_DEVICE(self), 15); /* seconds */ + fu_device_set_logical_id(FU_DEVICE(self), "PS186"); + fu_device_set_summary(FU_DEVICE(self), "DisplayPort 1.4a to HDMI 2.0b protocol converter"); + fu_device_set_firmware_size(FU_DEVICE(self), 0x40000); +} + +static void +fu_vli_pd_parade_device_class_init(FuVliPdParadeDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_vli_pd_parade_device_to_string; + klass_device->probe = fu_vli_pd_parade_device_probe; + klass_device->dump_firmware = fu_vli_pd_parade_device_dump_firmware; + klass_device->write_firmware = fu_vli_pd_parade_device_write_firmware; + klass_device->set_progress = fu_vli_pd_parade_device_set_progress; +} + +FuDevice * +fu_vli_pd_parade_device_new(FuVliDevice *parent) +{ + FuVliPdParadeDevice *self = + g_object_new(FU_TYPE_VLI_PD_PARADE_DEVICE, "parent", parent, NULL); + return FU_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-pd-parade-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-pd-parade-device.h new file mode 100644 index 0000000000000000000000000000000000000000..98508e9ec6f9445a2efb47b37df31904925f1ee1 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-pd-parade-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-pd-common.h" + +#define FU_TYPE_VLI_PD_PARADE_DEVICE (fu_vli_pd_parade_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVliPdParadeDevice, + fu_vli_pd_parade_device, + FU, + VLI_PD_PARADE_DEVICE, + FuDevice) + +FuDevice * +fu_vli_pd_parade_device_new(FuVliDevice *parent); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-plugin.c b/fwupd-1.8.6/plugins/vli/fu-vli-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..b816a041c2686ac9a3ff386f4f0c83bb30b62c73 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-plugin.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vli-pd-device.h" +#include "fu-vli-pd-firmware.h" +#include "fu-vli-plugin.h" +#include "fu-vli-usbhub-device.h" +#include "fu-vli-usbhub-firmware.h" + +struct _FuVliPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuVliPlugin, fu_vli_plugin, FU_TYPE_PLUGIN) + +static void +fu_vli_plugin_init(FuVliPlugin *self) +{ +} + +static void +fu_vli_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "VliDeviceKind"); + fu_context_add_quirk_key(ctx, "VliSpiAutoDetect"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_VLI_USBHUB_FIRMWARE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_VLI_PD_FIRMWARE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_VLI_USBHUB_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_VLI_PD_DEVICE); +} + +static void +fu_vli_plugin_class_init(FuVliPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_vli_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-plugin.h b/fwupd-1.8.6/plugins/vli/fu-vli-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..a5d8425692f471caa4792482b961b2ff72beebeb --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuVliPlugin, fu_vli_plugin, FU, VLI_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-common.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-common.c new file mode 100644 index 0000000000000000000000000000000000000000..b953c098c0292653f2acdea4225bc5ae65762a1c --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-common.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vli-usbhub-common.h" + +guint8 +fu_vli_usbhub_header_crc8(FuVliUsbhubHeader *hdr) +{ + return ~fu_crc8((const guint8 *)hdr, sizeof(*hdr) - 1); +} + +void +fu_vli_usbhub_header_export(FuVliUsbhubHeader *hdr, XbBuilderNode *bn) +{ + fu_xmlb_builder_insert_kx(bn, "dev_id", GUINT16_FROM_BE(hdr->dev_id)); + fu_xmlb_builder_insert_kx(bn, "variant", hdr->variant); + if (hdr->usb2_fw_sz > 0) { + fu_xmlb_builder_insert_kx(bn, "usb2_fw_addr", GUINT16_FROM_BE(hdr->usb2_fw_addr)); + fu_xmlb_builder_insert_kx(bn, "usb2_fw_sz", GUINT16_FROM_BE(hdr->usb2_fw_sz)); + } + fu_xmlb_builder_insert_kx(bn, + "usb3_fw_addr", + ((guint32)hdr->usb3_fw_addr_high) << 16 | + GUINT16_FROM_BE(hdr->usb3_fw_addr)); + fu_xmlb_builder_insert_kx(bn, "usb3_fw_sz", GUINT16_FROM_BE(hdr->usb3_fw_sz)); + if (hdr->prev_ptr != VLI_USBHUB_FLASHMAP_IDX_INVALID) { + fu_xmlb_builder_insert_kx(bn, + "prev_ptr", + VLI_USBHUB_FLASHMAP_IDX_TO_ADDR(hdr->prev_ptr)); + } + if (hdr->next_ptr != VLI_USBHUB_FLASHMAP_IDX_INVALID) { + fu_xmlb_builder_insert_kx(bn, + "next_ptr", + VLI_USBHUB_FLASHMAP_IDX_TO_ADDR(hdr->next_ptr)); + } + fu_xmlb_builder_insert_kb(bn, + "checksum_ok", + hdr->checksum == fu_vli_usbhub_header_crc8(hdr)); +} + +void +fu_vli_usbhub_header_to_string(FuVliUsbhubHeader *hdr, guint idt, GString *str) +{ + g_autoptr(XbBuilderNode) bn = xb_builder_node_new("header"); + g_autofree gchar *xml = NULL; + fu_vli_usbhub_header_export(hdr, bn); + xml = xb_builder_node_export(bn, + XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | +#if LIBXMLB_CHECK_VERSION(0, 2, 2) + XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY | +#endif + XB_NODE_EXPORT_FLAG_FORMAT_INDENT, + NULL); + fu_string_append(str, idt, "xml", xml); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-common.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-common.h new file mode 100644 index 0000000000000000000000000000000000000000..84b8b2e4f2e123eab55088dd75a73a41a3264019 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-common.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-common.h" + +typedef struct __attribute__((packed)) { + guint16 dev_id; /* 0x00, BE */ + guint8 strapping1; /* 0x02 */ + guint8 strapping2; /* 0x03 */ + guint16 usb3_fw_addr; /* 0x04, BE */ + guint16 usb3_fw_sz; /* 0x06, BE */ + guint16 usb2_fw_addr; /* 0x08, BE */ + guint16 usb2_fw_sz; /* 0x0a, BE */ + guint8 usb3_fw_addr_high; /* 0x0c */ + guint8 unknown_0d[3]; /* 0x0d */ + guint8 usb2_fw_addr_high; /* 0x10 */ + guint8 unknown_11[10]; /* 0x11 */ + guint8 inverse_pe41; /* 0x1b */ + guint8 prev_ptr; /* 0x1c, addr / 0x20 */ + guint8 next_ptr; /* 0x1d, addr / 0x20 */ + guint8 variant; /* 0x1e */ + guint8 checksum; /* 0x1f */ +} FuVliUsbhubHeader; + +G_STATIC_ASSERT(sizeof(FuVliUsbhubHeader) == 0x20); + +#define FU_VLI_USBHUB_HEADER_STRAPPING1_SELFW1 (1 << 1) +#define FU_VLI_USBHUB_HEADER_STRAPPING1_76PIN (1 << 2) +#define FU_VLI_USBHUB_HEADER_STRAPPING1_B3UP (1 << 3) +#define FU_VLI_USBHUB_HEADER_STRAPPING1_LPC (1 << 4) +#define FU_VLI_USBHUB_HEADER_STRAPPING1_U1U2 (1 << 5) +#define FU_VLI_USBHUB_HEADER_STRAPPING1_BC (1 << 6) +#define FU_VLI_USBHUB_HEADER_STRAPPING1_Q4S (1 << 7) + +#define FU_VLI_USBHUB_HEADER_STRAPPING2_IDXEN (1 << 0) +#define FU_VLI_USBHUB_HEADER_STRAPPING2_FWRTY (1 << 1) +#define FU_VLI_USBHUB_HEADER_STRAPPING2_SELFW2 (1 << 7) + +#define VLI_USBHUB_FLASHMAP_ADDR_TO_IDX(addr) (addr / 0x20) +#define VLI_USBHUB_FLASHMAP_IDX_TO_ADDR(addr) (addr * 0x20) + +#define VLI_USBHUB_FLASHMAP_IDX_HD1 0x00 /* factory firmware */ +#define VLI_USBHUB_FLASHMAP_IDX_HD2 0x80 /* update firmware */ +#define VLI_USBHUB_FLASHMAP_IDX_INVALID 0xff + +#define VLI_USBHUB_FLASHMAP_ADDR_HD1 0x0 +#define VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP 0x1800 +#define VLI_USBHUB_FLASHMAP_ADDR_HD2 0x1000 +#define VLI_USBHUB_FLASHMAP_ADDR_FW 0x2000 +#define VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY 0x10000 +#define VLI_USBHUB_FLASHMAP_ADDR_PD 0x20000 +#define VLI_USBHUB_FLASHMAP_ADDR_PD_BACKUP 0x30000 + +guint8 +fu_vli_usbhub_header_crc8(FuVliUsbhubHeader *hdr); +void +fu_vli_usbhub_header_to_string(FuVliUsbhubHeader *hdr, guint idt, GString *str); +void +fu_vli_usbhub_header_export(FuVliUsbhubHeader *hdr, XbBuilderNode *bn); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-device.c new file mode 100644 index 0000000000000000000000000000000000000000..0ac99395bed326fe84dd106ef1b5488482074a93 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-device.c @@ -0,0 +1,1254 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-vli-usbhub-common.h" +#include "fu-vli-usbhub-device.h" +#include "fu-vli-usbhub-firmware.h" +#include "fu-vli-usbhub-msp430-device.h" +#include "fu-vli-usbhub-pd-device.h" +#include "fu-vli-usbhub-rtd21xx-device.h" + +struct _FuVliUsbhubDevice { + FuVliDevice parent_instance; + gboolean disable_powersave; + guint8 update_protocol; + FuVliUsbhubHeader hd1_hdr; /* factory */ + FuVliUsbhubHeader hd2_hdr; /* update */ +}; + +G_DEFINE_TYPE(FuVliUsbhubDevice, fu_vli_usbhub_device, FU_TYPE_VLI_DEVICE) + +/** + * FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB: + * + * Use GPIO-B reset to reset the device. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB (1 << 0) +/** + * FU_VLI_USBHUB_DEVICE_FLAG_USB2: + * + * Device is USB-2 speed. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_USB2 (1 << 1) +/** + * FU_VLI_USBHUB_DEVICE_FLAG_USB3: + * + * Device is USB-3 speed. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_USB3 (1 << 2) +/** + * FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813: + * + * Device type VL813 needs unlocking with a custom VDR request. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813 (1 << 3) +/** + * FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD: + * + * Device shares the SPI device with the PD device. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD (1 << 4) +/** + * FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430: + * + * Device has a MSP430 attached via I²C. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430 (1 << 5) +/** + * FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX: + * + * Device has a RTD21XX attached via I²C. + */ +#define FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX (1 << 6) + +static void +fu_vli_usbhub_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(device); + + /* parent */ + FU_DEVICE_CLASS(fu_vli_usbhub_device_parent_class)->to_string(device, idt, str); + + fu_string_append_kb(str, idt, "DisablePowersave", self->disable_powersave); + fu_string_append_kx(str, idt, "UpdateProtocol", self->update_protocol); + if (self->update_protocol >= 0x2) { + fu_string_append(str, idt, "H1Hdr@0x0", NULL); + fu_vli_usbhub_header_to_string(&self->hd1_hdr, idt + 1, str); + if (self->hd2_hdr.dev_id != 0xffff) { + fu_string_append(str, idt, "H2Hdr@0x1000", NULL); + fu_vli_usbhub_header_to_string(&self->hd2_hdr, idt + 1, str); + } + } +} + +static gboolean +fu_vli_usbhub_device_vdr_unlock_813(FuVliUsbhubDevice *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, + 0x85, + 0x8786, + 0x8988, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to UnLock_VL813: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_read_reg(FuVliUsbhubDevice *self, guint16 addr, guint8 *buf, GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + addr >> 8, + addr & 0xff, + 0x0, + buf, + 0x1, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read register 0x%x: ", addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_write_reg(FuVliUsbhubDevice *self, guint16 addr, guint8 value, GError **error) +{ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + addr >> 8, + addr & 0xff, + (guint16)value, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write register 0x%x: ", addr); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_spi_read_status(FuVliDevice *self, guint8 *status, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_READ_STATUS, + &spi_cmd, + error)) + return FALSE; + return g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xc1, + spi_cmd, + 0x0000, + status, + 0x1, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error); +} + +static gboolean +fu_vli_usbhub_device_spi_read_data(FuVliDevice *self, + guint32 addr, + guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + guint16 index; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_READ_DATA, + &spi_cmd, + error)) + return FALSE; + value = ((addr >> 8) & 0xff00) | spi_cmd; + index = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff); + return g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xc4, + value, + index, + buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error); +} + +static gboolean +fu_vli_usbhub_device_spi_write_status(FuVliDevice *self, guint8 status, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_WRITE_STATUS, + &spi_cmd, + error)) + return FALSE; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd1, + spi_cmd, + 0x0000, + &status, + 0x1, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + return FALSE; + } + + /* Fix_For_GD_&_EN_SPI_Flash */ + g_usleep(100 * 1000); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_spi_write_enable(FuVliDevice *self, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_WRITE_EN, + &spi_cmd, + error)) + return FALSE; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd1, + spi_cmd, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write enable SPI: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_spi_chip_erase(FuVliDevice *self, GError **error) +{ + guint8 spi_cmd = 0x0; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_CHIP_ERASE, + &spi_cmd, + error)) + return FALSE; + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd1, + spi_cmd, + 0x0000, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_spi_sector_erase(FuVliDevice *self, guint32 addr, GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + guint16 index; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_SECTOR_ERASE, + &spi_cmd, + error)) + return FALSE; + value = ((addr >> 8) & 0xff00) | spi_cmd; + index = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff); + return g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd4, + value, + index, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error); +} + +static gboolean +fu_vli_usbhub_device_spi_write_data(FuVliDevice *self, + guint32 addr, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 spi_cmd = 0x0; + guint16 value; + guint16 index; + if (!fu_cfi_device_get_cmd(fu_vli_device_get_cfi_device(self), + FU_CFI_DEVICE_CMD_PAGE_PROG, + &spi_cmd, + error)) + return FALSE; + value = ((addr >> 8) & 0xff00) | spi_cmd; + index = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff); + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xd4, + value, + index, + (guint8 *)buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + return FALSE; + } + return TRUE; +} + +#define VL817_ADDR_GPIO_OUTPUT_ENABLE 0xF6A0 /* 0=input, 1=output */ +#define VL817_ADDR_GPIO_SET_OUTPUT_DATA 0xF6A1 /* 0=low, 1=high */ +#define VL817_ADDR_GPIO_GET_INPUT_DATA 0xF6A2 /* 0=low, 1=high */ + +static gboolean +fu_vli_usbhub_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy_with_fallback(device); + g_autoptr(GError) error_local = NULL; + + /* some hardware has to toggle a GPIO to reset the entire PCB */ + if (fu_vli_device_get_kind(FU_VLI_DEVICE(proxy)) == FU_VLI_DEVICE_KIND_VL817 && + fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB)) { + guint8 tmp = 0x0; + + /* set GPIOB output enable */ + g_debug("using GPIO reset for %s", fu_device_get_id(device)); + if (!fu_vli_usbhub_device_read_reg(FU_VLI_USBHUB_DEVICE(proxy), + VL817_ADDR_GPIO_OUTPUT_ENABLE, + &tmp, + error)) + return FALSE; + if (!fu_vli_usbhub_device_write_reg(FU_VLI_USBHUB_DEVICE(proxy), + VL817_ADDR_GPIO_OUTPUT_ENABLE, + tmp | (1 << 1), + error)) + return FALSE; + + /* toggle GPIOB to trigger reset */ + if (!fu_vli_usbhub_device_read_reg(FU_VLI_USBHUB_DEVICE(proxy), + VL817_ADDR_GPIO_SET_OUTPUT_DATA, + &tmp, + error)) + return FALSE; + if (!fu_vli_usbhub_device_write_reg(FU_VLI_USBHUB_DEVICE(proxy), + VL817_ADDR_GPIO_SET_OUTPUT_DATA, + tmp ^ (1 << 1), + error)) + return FALSE; + } else { + /* replug, and ignore the device going away */ + if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(proxy)), + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0xf6, + 0x0040, + 0x0002, + NULL, + 0x0, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + &error_local)) { + if (g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_NO_DEVICE) || + g_error_matches(error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_FAILED)) { + g_debug("ignoring %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to restart device: "); + return FALSE; + } + } + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +/* disable hub sleep states -- not really required by 815~ hubs */ +static gboolean +fu_vli_usbhub_device_disable_u1u2(FuVliUsbhubDevice *self, GError **error) +{ + guint8 buf = 0x0; + + /* clear Reg[0xF8A2] bit_3 & bit_7 -- also + * clear Total Switch / Flag To Disable FW Auto-Reload Function */ + if (!fu_vli_usbhub_device_read_reg(self, 0xf8a2, &buf, error)) + return FALSE; + buf &= 0x77; + if (!fu_vli_usbhub_device_write_reg(self, 0xf8a2, buf, error)) + return FALSE; + + /* clear Reg[0xF832] bit_0 & bit_1 */ + if (!fu_vli_usbhub_device_read_reg(self, 0xf832, &buf, error)) + return FALSE; + buf &= 0xfc; + if (!fu_vli_usbhub_device_write_reg(self, 0xf832, buf, error)) + return FALSE; + + /* clear Reg[0xF920] bit_1 & bit_2 */ + if (!fu_vli_usbhub_device_read_reg(self, 0xf920, &buf, error)) + return FALSE; + buf &= 0xf9; + if (!fu_vli_usbhub_device_write_reg(self, 0xf920, buf, error)) + return FALSE; + + /* set Reg[0xF836] bit_3 */ + if (!fu_vli_usbhub_device_read_reg(self, 0xf836, &buf, error)) + return FALSE; + buf |= 0x08; + if (!fu_vli_usbhub_device_write_reg(self, 0xf836, buf, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_guess_kind(FuVliUsbhubDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint8 b811P812 = 0x0; + guint8 pkgtype = 0x0; + guint8 chipid1 = 0x0; + guint8 chipid2 = 0x0; + guint8 chipid12 = 0x0; + guint8 chipid22 = 0x0; + guint8 chipver = 0x0; + guint8 chipver2 = 0x0; + gint tPid = g_usb_device_get_pid(usb_device) & 0x0fff; + + if (!fu_vli_usbhub_device_read_reg(self, 0xf88c, &chipver, error)) { + g_prefix_error(error, "Read_ChipVer failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf63f, &chipver2, error)) { + g_prefix_error(error, "Read_ChipVer2 failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf800, &b811P812, error)) { + g_prefix_error(error, "Read_811P812 failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf88e, &chipid1, error)) { + g_prefix_error(error, "Read_ChipID1 failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf88f, &chipid2, error)) { + g_prefix_error(error, "Read_ChipID2 failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf64e, &chipid12, error)) { + g_prefix_error(error, "Read_ChipID12 failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf64f, &chipid22, error)) { + g_prefix_error(error, "Read_ChipID22 failed: "); + return FALSE; + } + if (!fu_vli_usbhub_device_read_reg(self, 0xf651, &pkgtype, error)) { + g_prefix_error(error, "Read_820Q7Q8 failed: "); + return FALSE; + } + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) { + g_debug("chipver = 0x%02x", chipver); + g_debug("chipver2 = 0x%02x", chipver2); + g_debug("b811P812 = 0x%02x", b811P812); + g_debug("chipid1 = 0x%02x", chipid1); + g_debug("chipid2 = 0x%02x", chipid2); + g_debug("chipid12 = 0x%02x", chipid12); + g_debug("chipid22 = 0x%02x", chipid22); + g_debug("pkgtype = 0x%02x", pkgtype); + } + + if (chipid2 == 0x35 && chipid1 == 0x07) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL210); + } else if (chipid2 == 0x35 && chipid1 == 0x18) { + if (chipver == 0xF0) { + /* packet type determines device kind for VL819-VL822, minus VL820 */ + switch ((pkgtype >> 1) & 0x07) { + /* VL822Q7 */ + case 0x00: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL822Q7); + break; + /* VL822Q5 */ + case 0x01: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL822Q5); + break; + /* VL822Q8 */ + case 0x02: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL822Q8); + break; + /* VL821Q7 */ + case 0x04: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL821Q7); + break; + /* VL819Q7 */ + case 0x05: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL819Q7); + break; + /* VL821Q8 */ + case 0x06: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL821Q8); + break; + /* VL819Q8 */ + case 0x07: + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL819Q8); + break; + default: + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Packet Type match failed: "); + return FALSE; + } + } else { + if (pkgtype & (1 << 2)) + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL820Q8); + else + fu_vli_device_set_kind(FU_VLI_DEVICE(self), + FU_VLI_DEVICE_KIND_VL820Q7); + } + } else if (chipid2 == 0x35 && chipid1 == 0x31) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL815); + } else if (chipid2 == 0x35 && chipid1 == 0x38) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL817); + } else if (chipid2 == 0x35 && chipid1 == 0x45) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL211); + } else if (chipid22 == 0x35 && chipid12 == 0x53) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL120); + } else if (tPid == 0x810) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL810); + } else if (tPid == 0x811) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL811); + } else if ((b811P812 & ((1 << 5) | (1 << 4))) == 0) { + if (chipver == 0x10) + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL811PB0); + else + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL811PB3); + } else if ((b811P812 & ((1 << 5) | (1 << 4))) == (1 << 4)) { + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL812Q4S); + } else if ((b811P812 & ((1 << 5) | (1 << 4))) == ((1 << 5) | (1 << 4))) { + if (chipver == 0x10) + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL812B0); + else + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL812B3); + } else { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "hardware is not supported"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_probe(FuDevice *device, GError **error) +{ + guint16 usbver = fu_usb_device_get_spec(FU_USB_DEVICE(device)); + + /* quirks now applied... */ + if (usbver > 0x0300 || fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_USB3)) { + fu_device_set_summary(device, "USB 3.x hub"); + /* prefer to show the USB 3 device and only fall back to the + * USB 2 version as a recovery */ + fu_device_set_priority(device, 1); + } else if (usbver > 0x0200 || + fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_USB2)) { + fu_device_set_summary(device, "USB 2.x hub"); + } else { + fu_device_set_summary(device, "USB hub"); + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_pd_setup(FuVliUsbhubDevice *self, GError **error) +{ + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + + /* add child */ + dev = fu_vli_usbhub_pd_device_new(self); + if (!fu_device_probe(dev, error)) + return FALSE; + if (!fu_device_setup(dev, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("%s", error_local->message); + } else { + g_warning("cannot create PD device: %s", error_local->message); + } + return TRUE; + } + fu_device_add_child(FU_DEVICE(self), dev); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_msp430_setup(FuVliUsbhubDevice *self, GError **error) +{ + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + + /* add child */ + dev = fu_vli_usbhub_msp430_device_new(self); + if (!fu_device_probe(dev, error)) + return FALSE; + if (!fu_device_setup(dev, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("%s", error_local->message); + } else { + g_warning("cannot create MSP430 I²C device: %s", error_local->message); + } + return TRUE; + } + fu_device_add_child(FU_DEVICE(self), dev); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_rtd21xx_setup(FuVliUsbhubDevice *self, GError **error) +{ + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + + /* add child */ + dev = fu_vli_usbhub_rtd21xx_device_new(self); + if (!fu_device_probe(dev, error)) + return FALSE; + if (!fu_device_setup(dev, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("%s", error_local->message); + } else { + g_warning("cannot create RTD21XX I²C device: %s", error_local->message); + } + return TRUE; + } + fu_device_add_child(FU_DEVICE(self), dev); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_ready(FuDevice *device, GError **error) +{ + FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(device); + g_autoptr(GError) error_tmp = NULL; + + /* FuUsbDevice->ready */ + if (!FU_DEVICE_CLASS(fu_vli_usbhub_device_parent_class)->ready(device, error)) + return FALSE; + + /* try to read a block of data which will fail for 813-type devices */ + if (fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813) && + !fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), + 0x0, + (guint8 *)&self->hd1_hdr, + sizeof(self->hd1_hdr), + &error_tmp)) { + g_warning("failed to read, trying to unlock 813: %s", error_tmp->message); + if (!fu_vli_usbhub_device_vdr_unlock_813(self, error)) + return FALSE; + if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), + 0x0, + (guint8 *)&self->hd1_hdr, + sizeof(self->hd1_hdr), + error)) { + g_prefix_error(error, "813 unlock fail: "); + return FALSE; + } + g_debug("813 unlock OK"); + /* VL813 & VL210 have same PID (0x0813), and only VL813 can reply */ + fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL813); + } else { + if (!fu_vli_usbhub_device_guess_kind(self, error)) + return FALSE; + } + + /* read HD1 (factory) header */ + if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD1, + (guint8 *)&self->hd1_hdr, + sizeof(self->hd1_hdr), + error)) { + g_prefix_error(error, "failed to read HD1 header: "); + return FALSE; + } + + /* detect update protocol from the device ID */ + switch (GUINT16_FROM_BE(self->hd1_hdr.dev_id) >> 8) { + /* VL810~VL813 */ + case 0x0d: + self->update_protocol = 0x1; + self->disable_powersave = TRUE; + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_install_duration(FU_DEVICE(self), 10); /* seconds */ + break; + /* VL817~ */ + case 0x05: + self->update_protocol = 0x2; + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_install_duration(FU_DEVICE(self), 15); /* seconds */ + break; + default: + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "hardware is not supported, dev_id=0x%x", + (guint)GUINT16_FROM_BE(self->hd1_hdr.dev_id)); + return FALSE; + } + + /* read HD2 (update) header */ + if (self->update_protocol >= 0x2) { + if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD2, + (guint8 *)&self->hd2_hdr, + sizeof(self->hd2_hdr), + error)) { + g_prefix_error(error, "failed to read HD2 header: "); + return FALSE; + } + } + + /* detect the PD child */ + if (fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD)) { + if (!fu_vli_usbhub_device_pd_setup(self, error)) + return FALSE; + } + + /* detect the I²C child */ + if (fu_usb_device_get_spec(FU_USB_DEVICE(self)) >= 0x0300 && + fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430)) { + if (!fu_vli_usbhub_device_msp430_setup(self, error)) + return FALSE; + } + if (fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX)) { + if (!fu_vli_usbhub_device_rtd21xx_setup(self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_vli_usbhub_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(device); + FuVliDeviceKind device_kind; + guint16 device_id; + g_autoptr(FuFirmware) firmware = fu_vli_usbhub_firmware_new(); + + /* check is compatible with firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + device_kind = fu_vli_usbhub_firmware_get_device_kind(FU_VLI_USBHUB_FIRMWARE(firmware)); + if (fu_vli_device_get_kind(FU_VLI_DEVICE(self)) != device_kind) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got %s, expected %s", + fu_vli_common_device_kind_to_string(device_kind), + fu_vli_common_device_kind_to_string( + fu_vli_device_get_kind(FU_VLI_DEVICE(self)))); + return NULL; + } + device_id = fu_vli_usbhub_firmware_get_device_id(FU_VLI_USBHUB_FIRMWARE(firmware)); + if (GUINT16_FROM_BE(self->hd1_hdr.dev_id) != device_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got 0x%04x, expected 0x%04x", + device_id, + (guint)GUINT16_FROM_BE(self->hd1_hdr.dev_id)); + return NULL; + } + + /* we could check this against flags */ + g_debug("parsed version: %s", fu_firmware_get_version(firmware)); + return g_steal_pointer(&firmware); +} + +static gboolean +fu_vli_usbhub_device_update_v1(FuVliUsbhubDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf; + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* erase */ + if (!fu_vli_device_spi_erase_all(FU_VLI_DEVICE(self), + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to erase chip: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* write in chunks */ + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + 0x0, + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + + /* success */ + fu_progress_step_done(progress); + return TRUE; +} + +/* if no header1 or ROM code update, write data directly */ +static gboolean +fu_vli_usbhub_device_update_v2_recovery(FuVliUsbhubDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + + /* erase */ + for (guint32 addr = 0; addr < bufsz; addr += 0x1000) { + if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), addr, error)) { + g_prefix_error(error, "failed to erase sector @0x%x: ", addr); + return FALSE; + } + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)addr, + bufsz); + } + fu_progress_step_done(progress); + + /* write in chunks */ + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD1, + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_hd1_is_valid(FuVliUsbhubHeader *hdr) +{ + if (hdr->prev_ptr != VLI_USBHUB_FLASHMAP_IDX_INVALID) + return FALSE; + if (hdr->checksum != fu_vli_usbhub_header_crc8(hdr)) + return FALSE; + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_hd1_recover(FuVliUsbhubDevice *self, + FuVliUsbhubHeader *hdr, + FuProgress *progress, + GError **error) +{ + /* point to HD2, i.e. updated firmware */ + if (hdr->next_ptr != VLI_USBHUB_FLASHMAP_IDX_HD2) { + hdr->next_ptr = VLI_USBHUB_FLASHMAP_IDX_HD2; + hdr->checksum = fu_vli_usbhub_header_crc8(hdr); + } + + /* write new header block */ + if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD1, + error)) { + g_prefix_error(error, + "failed to erase header1 sector at 0x%x: ", + (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1); + return FALSE; + } + if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD1, + (const guint8 *)hdr, + sizeof(FuVliUsbhubHeader), + progress, + error)) { + g_prefix_error(error, + "failed to write header1 block at 0x%x: ", + (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1); + return FALSE; + } + + /* update the cached copy */ + memcpy(&self->hd1_hdr, hdr, sizeof(self->hd1_hdr)); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_update_v2(FuVliUsbhubDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + gsize buf_fwsz = 0; + guint32 hd1_fw_sz; + guint32 hd2_fw_sz; + guint32 hd2_fw_addr; + guint32 hd2_fw_offset; + const guint8 *buf_fw; + FuVliUsbhubHeader hdr = {0x0}; + g_autoptr(GBytes) fw = NULL; + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* root header is valid */ + if (fu_vli_usbhub_device_hd1_is_valid(&self->hd1_hdr)) { + /* no update has ever been done */ + if (self->hd1_hdr.next_ptr != VLI_USBHUB_FLASHMAP_IDX_HD2) { + /* backup HD1 before recovering */ + if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD2, + error)) { + g_prefix_error(error, "failed to erase sector at header 1: "); + return FALSE; + } + if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, + (const guint8 *)&self->hd1_hdr, + sizeof(hdr), + progress, + error)) { + g_prefix_error(error, "failed to write block at header 1: "); + return FALSE; + } + if (!fu_vli_usbhub_device_hd1_recover(self, + &self->hd1_hdr, + progress, + error)) { + g_prefix_error(error, "failed to write header: "); + return FALSE; + } + } + + /* copy the header from the backup zone */ + } else { + g_debug("HD1 was invalid, reading backup"); + if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, + (guint8 *)&self->hd1_hdr, + sizeof(hdr), + error)) { + g_prefix_error(error, + "failed to read root header from 0x%x: ", + (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP); + return FALSE; + } + if (!fu_vli_usbhub_device_hd1_is_valid(&self->hd1_hdr)) { + g_debug("backup header is also invalid, starting recovery"); + return fu_vli_usbhub_device_update_v2_recovery(self, fw, progress, error); + } + if (!fu_vli_usbhub_device_hd1_recover(self, &self->hd1_hdr, progress, error)) { + g_prefix_error(error, "failed to get root header in backup zone: "); + return FALSE; + } + } + + /* align the update fw address to the sector after the factory size */ + hd1_fw_sz = GUINT16_FROM_BE(self->hd1_hdr.usb3_fw_sz); + if (hd1_fw_sz > 0xF000) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "FW1 size abnormal 0x%x", + (guint)hd1_fw_sz); + return FALSE; + } + hd2_fw_addr = (hd1_fw_sz + 0xfff) & 0xf000; + hd2_fw_addr += VLI_USBHUB_FLASHMAP_ADDR_FW; + + /* get the size and offset of the update firmware */ + buf_fw = g_bytes_get_data(fw, &buf_fwsz); + memcpy(&hdr, buf_fw, sizeof(hdr)); + hd2_fw_sz = GUINT16_FROM_BE(hdr.usb3_fw_sz); + hd2_fw_offset = GUINT16_FROM_BE(hdr.usb3_fw_addr); + g_debug("FW2 @0x%x (length 0x%x, offset 0x%x)", hd2_fw_addr, hd2_fw_sz, hd2_fw_offset); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 72, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 8, "hd2"); + + /* make space */ + if (!fu_vli_device_spi_erase(FU_VLI_DEVICE(self), + hd2_fw_addr, + hd2_fw_sz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* perform the actual write */ + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), + hd2_fw_addr, + buf_fw + hd2_fw_offset, + hd2_fw_sz, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write payload: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* map into header */ + if (!fu_memcpy_safe((guint8 *)&self->hd2_hdr, + sizeof(hdr), + 0x0, + buf_fw, + buf_fwsz, + 0x0, + sizeof(hdr), + error)) { + g_prefix_error(error, "failed to read header: "); + return FALSE; + } + + /* write new HD2 */ + self->hd2_hdr.usb3_fw_addr = GUINT16_TO_BE(hd2_fw_addr & 0xffff); + self->hd2_hdr.usb3_fw_addr_high = (guint8)(hd2_fw_addr >> 16); + self->hd2_hdr.prev_ptr = VLI_USBHUB_FLASHMAP_IDX_HD1; + self->hd2_hdr.next_ptr = VLI_USBHUB_FLASHMAP_IDX_INVALID; + self->hd2_hdr.checksum = fu_vli_usbhub_header_crc8(&self->hd2_hdr); + if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD2, + error)) { + g_prefix_error(error, "failed to erase sectors for HD2: "); + return FALSE; + } + if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), + VLI_USBHUB_FLASHMAP_ADDR_HD2, + (const guint8 *)&self->hd2_hdr, + sizeof(self->hd2_hdr), + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write HD2: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static GBytes * +fu_vli_usbhub_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(device); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + return fu_vli_device_spi_read(FU_VLI_DEVICE(self), + 0x0, + fu_device_get_firmware_size_max(device), + progress, + error); +} + +static gboolean +fu_vli_usbhub_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(device); + + /* disable powersaving if required */ + if (self->disable_powersave) { + if (!fu_vli_usbhub_device_disable_u1u2(self, error)) { + g_prefix_error(error, "disabling powersave failed: "); + return FALSE; + } + } + + /* use correct method */ + if (self->update_protocol == 0x1) + return fu_vli_usbhub_device_update_v1(self, firmware, progress, error); + if (self->update_protocol == 0x2) + return fu_vli_usbhub_device_update_v2(self, firmware, progress, error); + + /* not sure what to do */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "update protocol 0x%x not supported", + self->update_protocol); + return FALSE; +} + +static void +fu_vli_usbhub_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 92, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 7, "reload"); +} + +static void +fu_vli_usbhub_device_init(FuVliUsbhubDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_protocol(FU_DEVICE(self), "com.vli.usbhub"); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PROXY_FALLBACK); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB, + "attach-with-gpiob"); + fu_device_register_private_flag(FU_DEVICE(self), FU_VLI_USBHUB_DEVICE_FLAG_USB2, "usb3"); + fu_device_register_private_flag(FU_DEVICE(self), FU_VLI_USBHUB_DEVICE_FLAG_USB3, "usb2"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813, + "unlock-legacy813"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD, + "has-shared-spi-pd"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430, + "has-msp430"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX, + "has-rtd21xx"); +} + +static void +fu_vli_usbhub_device_class_init(FuVliUsbhubDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuVliDeviceClass *klass_vli_device = FU_VLI_DEVICE_CLASS(klass); + klass_device->probe = fu_vli_usbhub_device_probe; + klass_device->dump_firmware = fu_vli_usbhub_device_dump_firmware; + klass_device->write_firmware = fu_vli_usbhub_device_write_firmware; + klass_device->prepare_firmware = fu_vli_usbhub_device_prepare_firmware; + klass_device->attach = fu_vli_usbhub_device_attach; + klass_device->to_string = fu_vli_usbhub_device_to_string; + klass_device->ready = fu_vli_usbhub_device_ready; + klass_device->set_progress = fu_vli_usbhub_device_set_progress; + klass_vli_device->spi_chip_erase = fu_vli_usbhub_device_spi_chip_erase; + klass_vli_device->spi_sector_erase = fu_vli_usbhub_device_spi_sector_erase; + klass_vli_device->spi_read_data = fu_vli_usbhub_device_spi_read_data; + klass_vli_device->spi_read_status = fu_vli_usbhub_device_spi_read_status; + klass_vli_device->spi_write_data = fu_vli_usbhub_device_spi_write_data; + klass_vli_device->spi_write_enable = fu_vli_usbhub_device_spi_write_enable; + klass_vli_device->spi_write_status = fu_vli_usbhub_device_spi_write_status; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5518d30f97fb923fc61416c65a2e57e68d55c62a --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-device.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-device.h" + +#define FU_TYPE_VLI_USBHUB_DEVICE (fu_vli_usbhub_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVliUsbhubDevice, fu_vli_usbhub_device, FU, VLI_USBHUB_DEVICE, FuVliDevice) diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-firmware.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..79f47d9b608e062a561efdc3c405c5010fc0f53c --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-firmware.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-usbhub-firmware.h" + +struct _FuVliUsbhubFirmware { + FuFirmwareClass parent_instance; + FuVliDeviceKind device_kind; + FuVliUsbhubHeader hdr; +}; + +G_DEFINE_TYPE(FuVliUsbhubFirmware, fu_vli_usbhub_firmware, FU_TYPE_FIRMWARE) + +FuVliDeviceKind +fu_vli_usbhub_firmware_get_device_kind(FuVliUsbhubFirmware *self) +{ + g_return_val_if_fail(FU_IS_VLI_USBHUB_FIRMWARE(self), 0); + return self->device_kind; +} + +guint16 +fu_vli_usbhub_firmware_get_device_id(FuVliUsbhubFirmware *self) +{ + g_return_val_if_fail(FU_IS_VLI_USBHUB_FIRMWARE(self), 0); + return GUINT16_FROM_BE(self->hdr.dev_id); +} + +static void +fu_vli_usbhub_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuVliUsbhubFirmware *self = FU_VLI_USBHUB_FIRMWARE(firmware); + fu_xmlb_builder_insert_kv(bn, + "device_kind", + fu_vli_common_device_kind_to_string(self->device_kind)); + fu_vli_usbhub_header_export(&self->hdr, bn); +} + +static gboolean +fu_vli_usbhub_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubFirmware *self = FU_VLI_USBHUB_FIRMWARE(firmware); + gsize bufsz = 0; + guint16 adr_ofs = 0; + guint16 version = 0x0; + guint8 tmp = 0x0; + guint8 fwtype = 0x0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* map into header */ + if (!fu_memcpy_safe((guint8 *)&self->hdr, + sizeof(self->hdr), + 0x0, + buf, + bufsz, + 0x0, + sizeof(self->hdr), + error)) { + g_prefix_error(error, "failed to read header: "); + return FALSE; + } + + /* get firmware versions */ + switch (GUINT16_FROM_BE(self->hdr.dev_id)) { + case 0x0d12: + /* VL81x */ + if (!fu_memread_uint16_safe(buf, bufsz, 0x1f4c, &version, G_LITTLE_ENDIAN, error)) { + g_prefix_error(error, "failed to get version: "); + return FALSE; + } + version |= (self->hdr.strapping1 >> 4) & 0x07; + if ((version & 0x0f) == 0x04) { + if (!fu_memread_uint8_safe(buf, bufsz, 0x700d, &tmp, error)) { + g_prefix_error(error, "failed to get version increment: "); + return FALSE; + } + if (tmp & 0x40) + version += 1; + } + break; + case 0x0507: + /* VL210 */ + if (!fu_memread_uint16_safe(buf, bufsz, 0x8f0c, &version, G_LITTLE_ENDIAN, error)) { + g_prefix_error(error, "failed to get version: "); + return FALSE; + } + version |= (self->hdr.strapping1 >> 4) & 0x07; + if ((version & 0x0f) == 0x04) + version += 1; + break; + default: + /* U3ID_Address_In_FW_Zone */ + if (!fu_memread_uint16_safe(buf, bufsz, 0x8000, &adr_ofs, G_BIG_ENDIAN, error)) { + g_prefix_error(error, "failed to get offset addr: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + bufsz, + adr_ofs + 0x2000 + 0x04, /* U3-M? */ + &version, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to get offset version: "); + return FALSE; + } + version |= (self->hdr.strapping1 >> 4) & 0x07; + } + + /* version is set */ + if (version != 0x0) { + g_autofree gchar *version_str = NULL; + version_str = fu_version_from_uint16(version, FWUPD_VERSION_FORMAT_BCD); + fu_firmware_set_version(firmware, version_str); + fu_firmware_set_version_raw(firmware, version); + } + + /* get device type from firmware image */ + switch (GUINT16_FROM_BE(self->hdr.dev_id)) { + case 0x0d12: { + guint16 binver1 = 0x0; + guint16 binver2 = 0x0; + guint16 usb2_fw_addr = GUINT16_FROM_BE(self->hdr.usb2_fw_addr) + 0x1ff1; + guint16 usb3_fw_addr = GUINT16_FROM_BE(self->hdr.usb3_fw_addr) + 0x1ffa; + if (!fu_memread_uint16_safe(buf, + bufsz, + usb2_fw_addr, + &binver1, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to get binver1: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, + bufsz, + usb3_fw_addr, + &binver2, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error(error, "failed to get binver2: "); + return FALSE; + } + + /* VL813 == VT3470 */ + if ((binver1 == 0xb770 && binver2 == 0xb770) || + (binver1 == 0xb870 && binver2 == 0xb870)) { + self->device_kind = FU_VLI_DEVICE_KIND_VL813; + + /* VLQ4S == VT3470 (Q4S) */ + } else if (self->hdr.strapping1 & FU_VLI_USBHUB_HEADER_STRAPPING1_Q4S) { + self->device_kind = FU_VLI_DEVICE_KIND_VL812Q4S; + + /* VL812 == VT3470 (812/813) */ + } else if (self->hdr.strapping1 & FU_VLI_USBHUB_HEADER_STRAPPING1_76PIN) { + /* is B3 */ + if (self->hdr.strapping1 & FU_VLI_USBHUB_HEADER_STRAPPING1_B3UP) + self->device_kind = FU_VLI_DEVICE_KIND_VL812B3; + else + self->device_kind = FU_VLI_DEVICE_KIND_VL812B0; + + /* VL811P == VT3470 */ + } else { + /* is B3 */ + if (self->hdr.strapping1 & FU_VLI_USBHUB_HEADER_STRAPPING1_B3UP) + self->device_kind = FU_VLI_DEVICE_KIND_VL811PB3; + else + self->device_kind = FU_VLI_DEVICE_KIND_VL811PB0; + } + break; + } + case 0x0507: + /* VL210 == VT3507 */ + self->device_kind = FU_VLI_DEVICE_KIND_VL210; + break; + case 0x0545: + /* VL211 == VT3545 */ + self->device_kind = FU_VLI_DEVICE_KIND_VL211; + break; + case 0x0518: + /* VL819~VL822 == VT3518 */ + if (!fu_memread_uint8_safe(buf, bufsz, 0x8021, &tmp, error)) { + g_prefix_error(error, "failed to get 820/822 byte: "); + return FALSE; + } + /* Q5/Q7/Q8 requires searching two addresses for offset value */ + if (!fu_memread_uint16_safe(buf, bufsz, 0x8018, &adr_ofs, G_BIG_ENDIAN, error)) { + g_prefix_error(error, "failed to get Q7/Q8 offset mapping: "); + return FALSE; + } + /* VL819, VL821, VL822 */ + if (tmp == 0xF0) { + if (!fu_memread_uint8_safe(buf, bufsz, adr_ofs + 0x2000, &tmp, error)) { + g_prefix_error(error, "failed to get offset version: "); + return FALSE; + } + /* VL819 */ + if ((tmp == 0x05) || (tmp == 0x07)) + fwtype = tmp & 0x7; + else + fwtype = (tmp & 0x1) << 1 | (tmp & 0x2) << 1 | (tmp & 0x4) >> 2; + /* matching Q5/Q7/Q8 */ + switch (fwtype) { + case 0x00: + self->device_kind = FU_VLI_DEVICE_KIND_VL822Q7; + break; + case 0x01: + self->device_kind = FU_VLI_DEVICE_KIND_VL822Q5; + break; + case 0x02: + self->device_kind = FU_VLI_DEVICE_KIND_VL822Q8; + break; + case 0x04: + self->device_kind = FU_VLI_DEVICE_KIND_VL821Q7; + break; + case 0x05: + self->device_kind = FU_VLI_DEVICE_KIND_VL819Q7; + break; + case 0x06: + self->device_kind = FU_VLI_DEVICE_KIND_VL821Q8; + break; + case 0x07: + self->device_kind = FU_VLI_DEVICE_KIND_VL819Q8; + break; + default: + g_prefix_error(error, "failed to match Q5/Q7/Q8 fw type: "); + return FALSE; + } + /* VL820 */ + } else { + if (!fu_memread_uint8_safe(buf, bufsz, 0xf000, &tmp, error)) { + g_prefix_error(error, "failed to get Q7/Q8 difference: "); + return FALSE; + } + if (tmp & (1 << 0)) + self->device_kind = FU_VLI_DEVICE_KIND_VL820Q8; + else + self->device_kind = FU_VLI_DEVICE_KIND_VL820Q7; + } + break; + case 0x0538: + /* VL817 == VT3538 */ + self->device_kind = FU_VLI_DEVICE_KIND_VL817; + break; + case 0x0553: + /* VL120 == VT3553 */ + self->device_kind = FU_VLI_DEVICE_KIND_VL120; + break; + default: + break; + } + + /* success */ + return TRUE; +} + +static void +fu_vli_usbhub_firmware_init(FuVliUsbhubFirmware *self) +{ +} + +static void +fu_vli_usbhub_firmware_class_init(FuVliUsbhubFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_vli_usbhub_firmware_parse; + klass_firmware->export = fu_vli_usbhub_firmware_export; +} + +FuFirmware * +fu_vli_usbhub_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_VLI_USBHUB_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-firmware.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..aa494fcedb87bd6094e2b5e819ab5f74483e33c7 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-firmware.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-vli-usbhub-common.h" + +#define FU_TYPE_VLI_USBHUB_FIRMWARE (fu_vli_usbhub_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuVliUsbhubFirmware, + fu_vli_usbhub_firmware, + FU, + VLI_USBHUB_FIRMWARE, + FuFirmware) + +FuFirmware * +fu_vli_usbhub_firmware_new(void); +FuVliDeviceKind +fu_vli_usbhub_firmware_get_device_kind(FuVliUsbhubFirmware *self); +guint16 +fu_vli_usbhub_firmware_get_device_id(FuVliUsbhubFirmware *self); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-i2c-common.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-i2c-common.c new file mode 100644 index 0000000000000000000000000000000000000000..cc833d78d836ced056ad77ab29d9a4e725aea78f --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-i2c-common.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-vli-usbhub-i2c-common.h" + +gboolean +fu_vli_usbhub_i2c_check_status(FuVliUsbhubI2cStatus status, GError **error) +{ + if (status == FU_VLI_USBHUB_I2C_STATUS_OK) + return TRUE; + if (status == FU_VLI_USBHUB_I2C_STATUS_HEADER) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Incorrect header value of data frame"); + return FALSE; + } + if (status == FU_VLI_USBHUB_I2C_STATUS_COMMAND) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid command data"); + return FALSE; + } + if (status == FU_VLI_USBHUB_I2C_STATUS_ADDRESS) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid address range"); + return FALSE; + } + if (status == FU_VLI_USBHUB_I2C_STATUS_PACKETSIZE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Incorrect payload data length"); + return FALSE; + } + if (status == FU_VLI_USBHUB_I2C_STATUS_CHECKSUM) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Incorrect frame data checksum"); + return FALSE; + } + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Unknown error [0x%02x]", status); + return FALSE; +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-i2c-common.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-i2c-common.h new file mode 100644 index 0000000000000000000000000000000000000000..69d8b02b3a8220ea5c313260f5d913c14ab52559 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-i2c-common.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +typedef enum { + FU_VLI_USBHUB_I2C_STATUS_OK = 0x00, + FU_VLI_USBHUB_I2C_STATUS_HEADER = 0x51, + FU_VLI_USBHUB_I2C_STATUS_COMMAND = 0x52, + FU_VLI_USBHUB_I2C_STATUS_ADDRESS = 0x53, + FU_VLI_USBHUB_I2C_STATUS_PACKETSIZE = 0x54, + FU_VLI_USBHUB_I2C_STATUS_CHECKSUM = 0x55, +} FuVliUsbhubI2cStatus; + +gboolean +fu_vli_usbhub_i2c_check_status(FuVliUsbhubI2cStatus status, GError **error); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-msp430-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-msp430-device.c new file mode 100644 index 0000000000000000000000000000000000000000..fe10a7c38ed6e9dc83db85e384ed16323bcb0e92 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-msp430-device.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-usbhub-common.h" +#include "fu-vli-usbhub-device.h" +#include "fu-vli-usbhub-i2c-common.h" +#include "fu-vli-usbhub-msp430-device.h" + +struct _FuVliUsbhubMsp430Device { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuVliUsbhubMsp430Device, fu_vli_usbhub_msp430_device, FU_TYPE_DEVICE) + +/* Texas Instruments BSL */ +#define I2C_ADDR_WRITE 0x18 +#define I2C_ADDR_READ 0x19 + +#define I2C_CMD_WRITE 0x32 +#define I2C_CMD_READ_STATUS 0x33 +#define I2C_CMD_UPGRADE 0x34 +#define I2C_CMD_READ_VERSIONS 0x40 + +#define I2C_R_VDR 0xa0 /* read vendor command */ +#define I2C_W_VDR 0xb0 /* write vendor command */ + +static gboolean +fu_vli_usbhub_device_i2c_read(FuVliUsbhubDevice *self, + guint8 cmd, + guint8 *buf, + gsize bufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint16 value = ((guint16)I2C_ADDR_WRITE << 8) | cmd; + guint16 index = (guint16)I2C_ADDR_READ << 8; + 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, + I2C_R_VDR, + value, + index, + buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read I2C: "); + return FALSE; + } + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "I2cReadData", buf, bufsz); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_i2c_read_status(FuVliUsbhubDevice *self, + FuVliUsbhubI2cStatus *status, + GError **error) +{ + guint8 buf[1] = {0xff}; + if (!fu_vli_usbhub_device_i2c_read(self, I2C_CMD_READ_STATUS, buf, sizeof(buf), error)) + return FALSE; + if (status != NULL) + *status = buf[0]; + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_i2c_write_data(FuVliUsbhubDevice *self, + guint8 disable_start_bit, + guint8 disable_end_bit, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + guint16 value = (((guint16)disable_start_bit) << 8) | disable_end_bit; + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "I2cWriteData", buf, bufsz); + 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, + I2C_W_VDR, + value, + 0x0, + (guint8 *)buf, + bufsz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write I2C @0x%x: ", value); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_msp430_device_setup(FuDevice *device, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + guint8 buf[11] = {0x0}; + g_autofree gchar *version = NULL; + + /* get versions */ + if (!fu_vli_usbhub_device_i2c_read(parent, + I2C_CMD_READ_VERSIONS, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to read versions: "); + return FALSE; + } + if ((buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x00) || + (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no MSP430 device detected"); + return FALSE; + } + + /* set version */ + version = g_strdup_printf("%x.%x", buf[0], buf[1]); + fu_device_set_version(device, version); + return TRUE; +} + +static gboolean +fu_vli_usbhub_msp430_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubI2cStatus status = 0xff; + g_autoptr(FuDeviceLocker) locker = NULL; + const guint8 buf[] = { + I2C_ADDR_WRITE, + I2C_CMD_UPGRADE, + }; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + if (!fu_vli_usbhub_device_i2c_write_data(parent, 0, 0, buf, sizeof(buf), error)) + return FALSE; + + /* avoid power instability by waiting T1 */ + fu_progress_sleep(progress, 1000); + + /* check the device came back */ + if (!fu_vli_usbhub_device_i2c_read_status(parent, &status, error)) { + g_prefix_error(error, "device did not come back after detach: "); + return FALSE; + } + return fu_vli_usbhub_i2c_check_status(status, error); +} + +typedef struct { + guint8 command; + guint8 buf[0x40]; + gsize bufsz; + guint8 len; +} FuVliUsbhubDeviceRequest; + +static gboolean +fu_vli_usbhub_msp430_device_write_firmware_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuVliUsbhubDeviceRequest *req = (FuVliUsbhubDeviceRequest *)user_data; + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubI2cStatus status = 0xff; + + g_usleep(5 * 1000); + if (fu_usb_device_get_spec(FU_USB_DEVICE(parent)) >= 0x0300 || req->bufsz <= 32) { + if (!fu_vli_usbhub_device_i2c_write_data(parent, 0, 0, req->buf, req->bufsz, error)) + return FALSE; + } else { + /* for U2, hub data buffer <= 32 bytes */ + if (!fu_vli_usbhub_device_i2c_write_data(parent, 0, 1, req->buf, 32, error)) + return FALSE; + if (!fu_vli_usbhub_device_i2c_write_data(parent, + 1, + 0, + req->buf + 32, + req->bufsz - 32, + error)) + return FALSE; + } + + /* end of file, no need to check status */ + if (req->len == 0 && req->buf[6] == 0x01 && req->buf[7] == 0xFF) + return TRUE; + + /* read data to check status */ + g_usleep(5 * 1000); + if (!fu_vli_usbhub_device_i2c_read_status(parent, &status, error)) + return FALSE; + return fu_vli_usbhub_i2c_check_status(status, error); +} + +static gboolean +fu_vli_usbhub_msp430_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + GPtrArray *records = fu_ihex_firmware_get_records(FU_IHEX_FIRMWARE(firmware)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + /* transfer by I²C write, and check status by I²C read */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + fu_progress_set_steps(progress, records->len); + for (guint j = 0; j < records->len; j++) { + FuIhexFirmwareRecord *rcd = g_ptr_array_index(records, j); + FuVliUsbhubDeviceRequest req = {0x0}; + const gchar *line = rcd->buf->str; + + /* length, 16-bit address, type */ + req.len = rcd->byte_cnt; + if (req.len >= sizeof(req.buf) - 7) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "line too long; buffer size is 0x%x bytes", + (guint)sizeof(req.buf)); + return FALSE; + } + + /* write each record directly to the hardware */ + req.buf[0] = I2C_ADDR_WRITE; + req.buf[1] = I2C_CMD_WRITE; + req.buf[2] = 0x3a; /* ':' */ + req.buf[3] = req.len; + if (!fu_firmware_strparse_uint8_safe(line, rcd->buf->len, 3, &req.buf[4], error)) + return FALSE; + if (!fu_firmware_strparse_uint8_safe(line, rcd->buf->len, 5, &req.buf[5], error)) + return FALSE; + if (!fu_firmware_strparse_uint8_safe(line, rcd->buf->len, 7, &req.buf[6], error)) + return FALSE; + for (guint8 i = 0; i < req.len; i++) { + if (!fu_firmware_strparse_uint8_safe(line, + rcd->buf->len, + 9 + (i * 2), + &req.buf[7 + i], + error)) + return FALSE; + } + if (!fu_firmware_strparse_uint8_safe(line, + rcd->buf->len, + 9 + (req.len * 2), + &req.buf[7 + req.len], + error)) + return FALSE; + req.bufsz = req.len + 8; + + /* retry this if it fails */ + if (!fu_device_retry(device, + fu_vli_usbhub_msp430_device_write_firmware_cb, + 5, + &req, + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* the device automatically reboots */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_usbhub_msp430_device_probe(FuDevice *device, GError **error) +{ + FuVliDeviceKind device_kind = FU_VLI_DEVICE_KIND_MSP430; + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + + fu_device_set_name(device, fu_vli_common_device_kind_to_string(device_kind)); + fu_device_set_physical_id(device, fu_device_get_physical_id(FU_DEVICE(parent))); + + /* add instance ID */ + fu_device_add_instance_str(device, "I2C", fu_vli_common_device_kind_to_string(device_kind)); + return fu_device_build_instance_id(device, error, "USB", "VID", "PID", NULL); +} + +static void +fu_vli_usbhub_msp430_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 13, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 85, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_vli_usbhub_msp430_device_init(FuVliUsbhubMsp430Device *self) +{ + fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_protocol(FU_DEVICE(self), "com.vli.i2c"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_logical_id(FU_DEVICE(self), "I2C"); + fu_device_set_summary(FU_DEVICE(self), "I²C dock management device"); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_IHEX_FIRMWARE); + + /* the MSP device reboot takes down the entire hub for ~60 seconds */ + fu_device_set_remove_delay(FU_DEVICE(self), 120 * 1000); +} + +static void +fu_vli_usbhub_msp430_device_class_init(FuVliUsbhubMsp430DeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_vli_usbhub_msp430_device_probe; + klass_device->setup = fu_vli_usbhub_msp430_device_setup; + klass_device->detach = fu_vli_usbhub_msp430_device_detach; + klass_device->write_firmware = fu_vli_usbhub_msp430_device_write_firmware; + klass_device->set_progress = fu_vli_usbhub_msp430_device_set_progress; +} + +FuDevice * +fu_vli_usbhub_msp430_device_new(FuVliUsbhubDevice *parent) +{ + FuVliUsbhubMsp430Device *self = + g_object_new(FU_TYPE_VLI_USBHUB_MSP430_DEVICE, "parent", parent, NULL); + return FU_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-msp430-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-msp430-device.h new file mode 100644 index 0000000000000000000000000000000000000000..10bb93ee4385279641509044421e7749fdee00eb --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-msp430-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_VLI_USBHUB_MSP430_DEVICE (fu_vli_usbhub_msp430_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVliUsbhubMsp430Device, + fu_vli_usbhub_msp430_device, + FU, + VLI_USBHUB_MSP430_DEVICE, + FuDevice) + +FuDevice * +fu_vli_usbhub_msp430_device_new(FuVliUsbhubDevice *parent); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-pd-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-pd-device.c new file mode 100644 index 0000000000000000000000000000000000000000..210cc6c0adab9b835aa7ab3fcab186f2b2495ed7 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-pd-device.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-pd-common.h" +#include "fu-vli-pd-firmware.h" +#include "fu-vli-usbhub-common.h" +#include "fu-vli-usbhub-device.h" +#include "fu-vli-usbhub-pd-device.h" + +struct _FuVliUsbhubPdDevice { + FuDevice parent_instance; + FuVliDeviceKind device_kind; +}; + +G_DEFINE_TYPE(FuVliUsbhubPdDevice, fu_vli_usbhub_pd_device, FU_TYPE_DEVICE) + +static void +fu_vli_usbhub_pd_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); + fu_string_append(str, + idt, + "DeviceKind", + fu_vli_common_device_kind_to_string(self->device_kind)); + fu_string_append_kx(str, + idt, + "FwOffset", + fu_vli_common_device_kind_get_offset(self->device_kind)); + fu_string_append_kx(str, + idt, + "FwSize", + fu_vli_common_device_kind_get_size(self->device_kind)); +} + +static gboolean +fu_vli_usbhub_pd_device_setup(FuDevice *device, GError **error) +{ + FuVliPdHdr hdr = {0x0}; + FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + const gchar *name; + guint32 fwver; + g_autofree gchar *fwver_str = NULL; + + /* legacy location */ + if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(parent), + VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY + + VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, + (guint8 *)&hdr, + sizeof(hdr), + error)) { + g_prefix_error(error, "failed to read legacy PD header: "); + return FALSE; + } + + /* new location */ + if (GUINT16_FROM_LE(hdr.vid) != 0x2109) { + g_debug("PD VID was 0x%04x trying new location", GUINT16_FROM_LE(hdr.vid)); + if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(parent), + VLI_USBHUB_FLASHMAP_ADDR_PD + + VLI_USBHUB_PD_FLASHMAP_ADDR, + (guint8 *)&hdr, + sizeof(hdr), + error)) { + g_prefix_error(error, "failed to read PD header: "); + return FALSE; + } + } + + /* just empty space */ + if (hdr.fwver == G_MAXUINT32) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no PD device header found"); + return FALSE; + } + + /* get version */ + fwver = GUINT32_FROM_BE(hdr.fwver); + self->device_kind = fu_vli_pd_common_guess_device_kind(fwver); + if (self->device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "PD version invalid [0x%x]", + fwver); + return FALSE; + } + name = fu_vli_common_device_kind_to_string(self->device_kind); + fu_device_set_name(device, name); + + /* use header to populate device info */ + fu_device_set_version_raw(device, fwver); + fwver_str = fu_version_from_uint32(fwver, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(device, fwver_str); + + /* add standard GUIDs in order of priority */ + fu_device_add_instance_u16(device, "VID", GUINT16_FROM_LE(hdr.vid)); + fu_device_add_instance_u16(device, "PID", GUINT16_FROM_LE(hdr.pid)); + fu_device_add_instance_u8(device, "APP", fwver & 0xff); + fu_device_add_instance_str(device, "DEV", name); + if (!fu_device_build_instance_id_quirk(device, error, "USB", "VID", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "DEV", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "APP", NULL)) + return FALSE; + + /* these have a backup section */ + if (fu_vli_common_device_kind_get_offset(self->device_kind) == VLI_USBHUB_FLASHMAP_ADDR_PD) + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_usbhub_pd_device_reload(FuDevice *device, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open parent device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_vli_usbhub_pd_device_setup(device, error); +} + +static FuFirmware * +fu_vli_usbhub_pd_device_prepare_firmware(FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); + FuVliDeviceKind device_kind; + g_autoptr(FuFirmware) firmware = fu_vli_pd_firmware_new(); + + /* check is compatible with firmware */ + if (!fu_firmware_parse(firmware, fw, flags, error)) + return NULL; + device_kind = fu_vli_pd_firmware_get_kind(FU_VLI_PD_FIRMWARE(firmware)); + if (self->device_kind != device_kind) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, got %s, expected %s", + fu_vli_common_device_kind_to_string(device_kind), + fu_vli_common_device_kind_to_string(self->device_kind)); + return NULL; + } + + /* we could check this against flags */ + g_debug("parsed version: %s", fu_firmware_get_version(firmware)); + return g_steal_pointer(&firmware); +} + +static GBytes * +fu_vli_usbhub_pd_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return NULL; + + /* read */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + return fu_vli_device_spi_read(FU_VLI_DEVICE(parent), + fu_vli_common_device_kind_get_offset(self->device_kind), + fu_device_get_firmware_size_max(device), + progress, + error); +} + +static gboolean +fu_vli_usbhub_pd_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + gsize bufsz = 0; + const guint8 *buf; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 78, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 22, NULL); + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + /* erase */ + buf = g_bytes_get_data(fw, &bufsz); + if (!fu_vli_device_spi_erase(FU_VLI_DEVICE(parent), + fu_vli_common_device_kind_get_offset(self->device_kind), + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + if (!fu_vli_device_spi_write(FU_VLI_DEVICE(parent), + fu_vli_common_device_kind_get_offset(self->device_kind), + buf, + bufsz, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +/* reboot the parent FuVliUsbhubDevice if we update the FuVliUsbhubPdDevice */ +static gboolean +fu_vli_usbhub_pd_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_device_attach_full(parent, progress, error); +} + +static gboolean +fu_vli_usbhub_pd_device_probe(FuDevice *device, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + fu_device_set_physical_id(device, fu_device_get_physical_id(FU_DEVICE(parent))); + return TRUE; +} + +static void +fu_vli_usbhub_pd_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_vli_usbhub_pd_device_init(FuVliUsbhubPdDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_protocol(FU_DEVICE(self), "com.vli.usbhub"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_install_duration(FU_DEVICE(self), 15); /* seconds */ + fu_device_set_logical_id(FU_DEVICE(self), "PD"); + fu_device_set_summary(FU_DEVICE(self), "USB-C power delivery device"); +} + +static void +fu_vli_usbhub_pd_device_class_init(FuVliUsbhubPdDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_vli_usbhub_pd_device_to_string; + klass_device->probe = fu_vli_usbhub_pd_device_probe; + klass_device->setup = fu_vli_usbhub_pd_device_setup; + klass_device->reload = fu_vli_usbhub_pd_device_reload; + klass_device->attach = fu_vli_usbhub_pd_device_attach; + klass_device->dump_firmware = fu_vli_usbhub_pd_device_dump_firmware; + klass_device->write_firmware = fu_vli_usbhub_pd_device_write_firmware; + klass_device->prepare_firmware = fu_vli_usbhub_pd_device_prepare_firmware; + klass_device->set_progress = fu_vli_usbhub_pd_device_set_progress; +} + +FuDevice * +fu_vli_usbhub_pd_device_new(FuVliUsbhubDevice *parent) +{ + FuVliUsbhubPdDevice *self = + g_object_new(FU_TYPE_VLI_USBHUB_PD_DEVICE, "parent", parent, NULL); + return FU_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-pd-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-pd-device.h new file mode 100644 index 0000000000000000000000000000000000000000..37bfd0193ebd1a70e122187287e1679766398f8a --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-pd-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_VLI_USBHUB_PD_DEVICE (fu_vli_usbhub_pd_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVliUsbhubPdDevice, + fu_vli_usbhub_pd_device, + FU, + VLI_USBHUB_PD_DEVICE, + FuDevice) + +FuDevice * +fu_vli_usbhub_pd_device_new(FuVliUsbhubDevice *parent); diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-rtd21xx-device.c b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-rtd21xx-device.c new file mode 100644 index 0000000000000000000000000000000000000000..03ec02f39c97a77e1d081a6fb793befcf2bf8bde --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-rtd21xx-device.c @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2017 VIA Corporation + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-vli-usbhub-device.h" +#include "fu-vli-usbhub-rtd21xx-device.h" + +struct _FuVliUsbhubRtd21xxDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuVliUsbhubRtd21xxDevice, fu_vli_usbhub_rtd21xx_device, FU_TYPE_DEVICE) + +#define I2C_WRITE_REQUEST 0xB2 +#define I2C_READ_REQUEST 0xA5 + +#define I2C_DELAY_AFTER_SEND 5000 /* us */ + +#define UC_FOREGROUND_TARGET_ADDR 0x3A +#define UC_FOREGROUND_STATUS 0x31 +#define UC_FOREGROUND_OPCODE 0x33 +#define UC_FOREGROUND_ISP_DATA_OPCODE 0x34 + +#define ISP_DATA_BLOCKSIZE 30 +#define ISP_PACKET_SIZE 32 + +typedef enum { + ISP_STATUS_BUSY = 0xBB, /* host must wait for device */ + ISP_STATUS_IDLE_SUCCESS = 0x11, /* previous command was OK */ + ISP_STATUS_IDLE_FAILURE = 0x12, /* previous command failed */ +} IspStatus; + +typedef enum { + ISP_CMD_ENTER_FW_UPDATE = 0x01, + ISP_CMD_GET_PROJECT_ID_ADDR = 0x02, + ISP_CMD_SYNC_IDENTIFY_CODE = 0x03, + ISP_CMD_GET_FW_INFO = 0x04, + ISP_CMD_FW_UPDATE_START = 0x05, + ISP_CMD_FW_UPDATE_ISP_DONE = 0x06, + ISP_CMD_FW_UPDATE_EXIT = 0x07, + ISP_CMD_FW_UPDATE_RESET = 0x08, +} IspCmd; + +static gboolean +fu_vli_usbhub_device_i2c_write(FuVliUsbhubDevice *self, + guint8 target_addr, + guint8 sub_addr, + guint8 *data, + gsize datasz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self)); + gsize bufsz = datasz + 2; + g_autofree guint8 *buf = g_malloc0(bufsz); + + buf[0] = target_addr; + buf[1] = sub_addr; + if (!fu_memcpy_safe(buf, + bufsz, + 0x2, /* dst */ + data, + datasz, + 0x0, /* src */ + datasz, + error)) + return FALSE; + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "I2cWriteData", buf, datasz + 2); + 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, + I2C_WRITE_REQUEST, + 0x0000, + 0x0000, + buf, + datasz + 2, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write I2C @0x%02x:%02x: ", target_addr, sub_addr); + return FALSE; + } + g_usleep(I2C_DELAY_AFTER_SEND); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_i2c_read(FuVliUsbhubDevice *self, + guint8 target_addr, + guint8 sub_addr, + guint8 *data, + gsize datasz, + 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_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + I2C_READ_REQUEST, + 0x0000, + ((guint16)sub_addr << 8) + target_addr, + data, + datasz, + NULL, + FU_VLI_DEVICE_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read I2C: "); + return FALSE; + } + if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL) + fu_dump_raw(G_LOG_DOMAIN, "I2cReadData", data, datasz); + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_rtd21xx_read_status_raw(FuVliUsbhubRtd21xxDevice *self, + guint8 *status, + GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + guint8 buf[] = {0x00}; + if (!fu_vli_usbhub_device_i2c_read(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_STATUS, + buf, + sizeof(buf), + error)) + return FALSE; + if (status != NULL) + *status = buf[0]; + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_rtd21xx_read_status_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuVliUsbhubRtd21xxDevice *self = FU_VLI_USBHUB_RTD21XX_DEVICE(device); + guint8 status = 0xfd; + if (!fu_vli_usbhub_device_rtd21xx_read_status_raw(self, &status, error)) + return FALSE; + if (status == ISP_STATUS_BUSY) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "status was 0x%02x", status); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_rtd21xx_read_status(FuVliUsbhubRtd21xxDevice *self, + guint8 *status, + GError **error) +{ + return fu_device_retry(FU_DEVICE(self), + fu_vli_usbhub_device_rtd21xx_read_status_cb, + 4200, + status, + error); +} + +static gboolean +fu_vli_usbhub_rtd21xx_ensure_version_unlocked(FuVliUsbhubRtd21xxDevice *self, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + guint8 buf_rep[7] = {0x00}; + guint8 buf_req[] = {ISP_CMD_GET_FW_INFO}; + g_autofree gchar *version = NULL; + + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + buf_req, + sizeof(buf_req), + error)) { + g_prefix_error(error, "failed to get version number: "); + return FALSE; + } + + /* wait for device ready */ + g_usleep(300000); + if (!fu_vli_usbhub_device_i2c_read(parent, + UC_FOREGROUND_TARGET_ADDR, + 0x00, + buf_rep, + sizeof(buf_rep), + error)) { + g_prefix_error(error, "failed to get version number: "); + return FALSE; + } + + /* set version */ + version = g_strdup_printf("%u.%u", buf_rep[1], buf_rep[2]); + fu_device_set_version(FU_DEVICE(self), version); + return TRUE; +} + +static gboolean +fu_vli_usbhub_rtd21xx_device_setup(FuDevice *device, GError **error) +{ + FuVliUsbhubRtd21xxDevice *self = FU_VLI_USBHUB_RTD21XX_DEVICE(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get version */ + locker = fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_device_attach, + error); + if (locker == NULL) + return FALSE; + if (!fu_vli_usbhub_rtd21xx_ensure_version_unlocked(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_rtd21xx_detach_raw(FuVliUsbhubRtd21xxDevice *self, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + guint8 buf[] = {0x03}; + if (!fu_vli_usbhub_device_i2c_write(parent, 0x6A, 0x31, buf, sizeof(buf), error)) { + g_prefix_error(error, "failed to detach: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_device_rtd21xx_detach_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuVliUsbhubRtd21xxDevice *self = FU_VLI_USBHUB_RTD21XX_DEVICE(device); + guint8 status = 0xfe; + if (!fu_vli_usbhub_device_rtd21xx_detach_raw(self, error)) + return FALSE; + if (!fu_vli_usbhub_device_rtd21xx_read_status_raw(self, &status, error)) + return FALSE; + if (status != ISP_STATUS_IDLE_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "detach status was 0x%02x", + status); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_vli_usbhub_rtd21xx_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + if (!fu_device_retry(device, fu_vli_usbhub_device_rtd21xx_detach_cb, 100, NULL, error)) + return FALSE; + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_vli_usbhub_rtd21xx_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + guint8 buf[] = {ISP_CMD_FW_UPDATE_RESET}; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + buf, + sizeof(buf), + error)) { + g_prefix_error(error, "failed to attach: "); + return FALSE; + } + + /* success */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_vli_usbhub_rtd21xx_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubRtd21xxDevice *self = FU_VLI_USBHUB_RTD21XX_DEVICE(device); + const guint8 *fwbuf; + gsize fwbufsz = 0; + guint32 project_addr; + guint8 project_id_count; + guint8 read_buf[10] = {0}; + guint8 write_buf[ISP_PACKET_SIZE] = {0}; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "enable-isp"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, NULL); + + /* open device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + /* simple image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + fwbuf = g_bytes_get_data(fw, &fwbufsz); + + /* enable ISP high priority */ + write_buf[0] = ISP_CMD_ENTER_FW_UPDATE; + write_buf[1] = 0x01; + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 2, + error)) { + g_prefix_error(error, "failed to enable ISP: "); + return FALSE; + } + if (!fu_vli_usbhub_device_rtd21xx_read_status(self, NULL, error)) + return FALSE; + + /* get project ID address */ + write_buf[0] = ISP_CMD_GET_PROJECT_ID_ADDR; + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "failed to get project ID address: "); + return FALSE; + } + + /* read back 6 bytes data */ + g_usleep(I2C_DELAY_AFTER_SEND * 40); + if (!fu_vli_usbhub_device_i2c_read(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_STATUS, + read_buf, + 6, + error)) { + g_prefix_error(error, "failed to read project ID: "); + return FALSE; + } + if (read_buf[0] != ISP_STATUS_IDLE_SUCCESS) { + g_prefix_error(error, "failed project ID with error 0x%02x: ", read_buf[0]); + return FALSE; + } + + /* verify project ID */ + if (!fu_memread_uint32_safe(read_buf, + sizeof(read_buf), + 0x1, + &project_addr, + G_BIG_ENDIAN, + error)) + return FALSE; + project_id_count = read_buf[5]; + write_buf[0] = ISP_CMD_SYNC_IDENTIFY_CODE; + if (!fu_memcpy_safe(write_buf, + sizeof(write_buf), + 0x1, /* dst */ + fwbuf, + fwbufsz, + project_addr, /* src */ + project_id_count, + error)) { + g_prefix_error(error, "failed to write project ID from 0x%04x: ", project_addr); + return FALSE; + } + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + project_id_count + 1, + error)) { + g_prefix_error(error, "failed to send fw update start cmd: "); + return FALSE; + } + if (!fu_vli_usbhub_device_rtd21xx_read_status(self, NULL, error)) + return FALSE; + + /* background FW update start command */ + write_buf[0] = ISP_CMD_FW_UPDATE_START; + fu_memwrite_uint16(write_buf + 1, ISP_DATA_BLOCKSIZE, G_BIG_ENDIAN); + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 3, + error)) { + g_prefix_error(error, "failed to send fw update start cmd: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* send data */ + chunks = fu_chunk_array_new_from_bytes(fw, + 0x00, /* start addr */ + 0x00, /* page_sz */ + ISP_DATA_BLOCKSIZE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_vli_usbhub_device_rtd21xx_read_status(self, NULL, error)) + return FALSE; + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_ISP_DATA_OPCODE, + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) { + g_prefix_error(error, + "failed to write @0x%04x: ", + fu_chunk_get_address(chk)); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + /* update finish command */ + if (!fu_vli_usbhub_device_rtd21xx_read_status(self, NULL, error)) + return FALSE; + write_buf[0] = ISP_CMD_FW_UPDATE_ISP_DONE; + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "failed update finish cmd: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* exit background-fw mode */ + if (!fu_vli_usbhub_device_rtd21xx_read_status(self, NULL, error)) + return FALSE; + write_buf[0] = ISP_CMD_FW_UPDATE_EXIT; + if (!fu_vli_usbhub_device_i2c_write(parent, + UC_FOREGROUND_TARGET_ADDR, + UC_FOREGROUND_OPCODE, + write_buf, + 1, + error)) { + g_prefix_error(error, "FwUpdate exit: "); + return FALSE; + } + + /* the device needs some time to restart with the new firmware before + * it can be queried again */ + fu_progress_sleep(progress, 20000); + + /* success */ + fu_progress_step_done(progress); + return TRUE; +} + +static gboolean +fu_vli_usbhub_rtd21xx_device_reload(FuDevice *device, GError **error) +{ + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open parent device */ + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_vli_usbhub_rtd21xx_device_setup(device, error); +} + +static gboolean +fu_vli_usbhub_rtd21xx_device_probe(FuDevice *device, GError **error) +{ + FuVliDeviceKind device_kind = FU_VLI_DEVICE_KIND_RTD21XX; + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + + fu_device_set_name(device, fu_vli_common_device_kind_to_string(device_kind)); + fu_device_set_physical_id(device, fu_device_get_physical_id(FU_DEVICE(parent))); + + /* add instance ID */ + fu_device_add_instance_str(device, "I2C", fu_vli_common_device_kind_to_string(device_kind)); + return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "I2C", NULL); +} + +static void +fu_vli_usbhub_rtd21xx_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_vli_usbhub_rtd21xx_device_init(FuVliUsbhubRtd21xxDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_protocol(FU_DEVICE(self), "com.vli.i2c"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_install_duration(FU_DEVICE(self), 100); /* seconds */ + fu_device_set_logical_id(FU_DEVICE(self), "I2C"); + fu_device_retry_set_delay(FU_DEVICE(self), 30); /* ms */ +} + +static void +fu_vli_usbhub_rtd21xx_device_class_init(FuVliUsbhubRtd21xxDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_vli_usbhub_rtd21xx_device_probe; + klass_device->setup = fu_vli_usbhub_rtd21xx_device_setup; + klass_device->reload = fu_vli_usbhub_rtd21xx_device_reload; + klass_device->attach = fu_vli_usbhub_rtd21xx_device_attach; + klass_device->detach = fu_vli_usbhub_rtd21xx_device_detach; + klass_device->write_firmware = fu_vli_usbhub_rtd21xx_device_write_firmware; + klass_device->set_progress = fu_vli_usbhub_rtd21xx_device_set_progress; +} + +FuDevice * +fu_vli_usbhub_rtd21xx_device_new(FuVliUsbhubDevice *parent) +{ + FuVliUsbhubRtd21xxDevice *self = + g_object_new(FU_TYPE_VLI_USBHUB_RTD21XX_DEVICE, "parent", parent, NULL); + return FU_DEVICE(self); +} diff --git a/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-rtd21xx-device.h b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-rtd21xx-device.h new file mode 100644 index 0000000000000000000000000000000000000000..4f87694e3d5fd691573d4bcc84ea44c2cb4516e8 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/fu-vli-usbhub-rtd21xx-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_VLI_USBHUB_RTD21XX_DEVICE (fu_vli_usbhub_rtd21xx_device_get_type()) +G_DECLARE_FINAL_TYPE(FuVliUsbhubRtd21xxDevice, + fu_vli_usbhub_rtd21xx_device, + FU, + VLI_USBHUB_RTD21XX_DEVICE, + FuDevice) + +FuDevice * +fu_vli_usbhub_rtd21xx_device_new(FuVliUsbhubDevice *parent); diff --git a/fwupd-1.8.6/plugins/vli/meson.build b/fwupd-1.8.6/plugins/vli/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..1a9305a19a9c4d27881c8697b82f8bf16c9d5f93 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/meson.build @@ -0,0 +1,55 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginVliUsbhub"'] + +plugin_quirks += files([ + 'vli-bizlink.quirk', + 'vli-dell.quirk', + 'vli-goodway.quirk', + 'vli-hyper.quirk', + 'vli-lenovo.quirk', + 'vli-samsung.quirk', + ]) +plugin_builtin_vli = static_library('fu_plugin_vli', + sources: [ + 'fu-vli-plugin.c', + 'fu-vli-common.c', + 'fu-vli-device.c', + 'fu-vli-pd-common.c', + 'fu-vli-pd-device.c', + 'fu-vli-pd-firmware.c', + 'fu-vli-pd-parade-device.c', + 'fu-vli-usbhub-common.c', + 'fu-vli-usbhub-device.c', + 'fu-vli-usbhub-firmware.c', + 'fu-vli-usbhub-i2c-common.c', + 'fu-vli-usbhub-msp430-device.c', + 'fu-vli-usbhub-pd-device.c', + 'fu-vli-usbhub-rtd21xx-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_vli + +if get_option('tests') + e = executable( + 'vli-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_vli, + ], + c_args: cargs, + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('vli-self-test', e) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/vli/vli-bizlink.quirk b/fwupd-1.8.6/plugins/vli/vli-bizlink.quirk new file mode 100644 index 0000000000000000000000000000000000000000..4ad246969cfe7b116d7647f8d1ee0f863cd2d4c3 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-bizlink.quirk @@ -0,0 +1,16 @@ +# BizLink USB-C Cayenne +[USB\VID_06C4&PID_C304] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd +CounterpartGuid = USB\VID_06C4&PID_C303 +[USB\VID_06C4&PID_C303] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_06C4&PID_C304 +[USB\VID_06C4&PID_C305] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_06C4&PID_C304 diff --git a/fwupd-1.8.6/plugins/vli/vli-dell.quirk b/fwupd-1.8.6/plugins/vli/vli-dell.quirk new file mode 100644 index 0000000000000000000000000000000000000000..ced686eeda4bbde6c52f87b129a7951e97e89113 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-dell.quirk @@ -0,0 +1,5 @@ +# Dell DA300 +[USB\VID_2109&PID_0820&REV_3003] +Flags = no-probe +[USB\VID_2109&PID_2820&REV_3003] +Flags = no-probe diff --git a/fwupd-1.8.6/plugins/vli/vli-goodway.quirk b/fwupd-1.8.6/plugins/vli/vli-goodway.quirk new file mode 100644 index 0000000000000000000000000000000000000000..de4930740dc4e0ec32cd5922e49af59a79cd575e --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-goodway.quirk @@ -0,0 +1,12 @@ +# Goodway Minibons +[USB\VID_065F&PID_F817] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_065F&PID_F816 +ParentGuid = USB\VID_1F29&PID_7518 +[USB\VID_065F&PID_F816] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_065F&PID_F817 diff --git a/fwupd-1.8.6/plugins/vli/vli-hyper.quirk b/fwupd-1.8.6/plugins/vli/vli-hyper.quirk new file mode 100644 index 0000000000000000000000000000000000000000..06984052d7bbcc4b24770e45b2220f4f312ffcbe --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-hyper.quirk @@ -0,0 +1,11 @@ +# Hyper USB-C Hub +[USB\VID_2D01&PID_0385] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd +CounterpartGuid = USB\VID_2D01&PID_0382 +[USB\VID_2D01&PID_0382] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_2D01&PID_0385 diff --git a/fwupd-1.8.6/plugins/vli/vli-lenovo.quirk b/fwupd-1.8.6/plugins/vli/vli-lenovo.quirk new file mode 100644 index 0000000000000000000000000000000000000000..6b29fc0038c2758ed8d7823a16a48a51b2164621 --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-lenovo.quirk @@ -0,0 +1,282 @@ +# Lenovo CS18 Ultra Dock +[USB\VID_17EF&PID_3070] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_3071 +[USB\VID_17EF&PID_3072&HUB_0006] +ParentGuid = USB\VID_17EF&PID_3072&HUB_0002 +[USB\VID_17EF&PID_3071] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +[USB\VID_17EF&PID_3071&HUB_0002] +ParentGuid = USB\VID_17EF&PID_3071&HUB_0006 + +# Lenovo CS18 Pro and Basic Dock +[USB\VID_17EF&PID_3072] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_3073 +[USB\VID_17EF&PID_3073] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +[USB\VID_17EF&PID_3073&HUB_0006] +ParentGuid = USB\VID_17EF&PID_3073&HUB_0002 + +# Lenovo TR Dock +[USB\VID_17EF&PID_307F&HUB_0002] +ParentGuid = TBT-01081720 +[USB\VID_17EF&PID_307F] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_3080 +[USB\VID_17EF&PID_307F&HUB_0002] +Flags = usb3,has-msp430 +[USB\VID_17EF&PID_307F&HUB_0006] +ParentGuid = USB\VID_17EF&PID_307F&HUB_0002 +[USB\VID_17EF&PID_3080] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +[USB\VID_17EF&PID_3080&HUB_06] +ParentGuid = USB\VID_17EF&PID_3080&HUB_20 +[USB\VID_17EF&PID_3080&HUB_20] +ParentGuid = TBT-01081720 + +# Lenovo CS13 KG Dock +[USB\VID_17EF&PID_1010] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 + +# Lenovo CS13 GD Dock +[USB\VID_17EF&PID_1012] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 + +# Lenovo CS13 MO Dock +[USB\VID_17EF&PID_1013] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 + +# Lenovo USB3 Ultra Dock +[USB\VID_17EF&PID_1014] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 +[USB\VID_17EF&PID_1015] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 +ParentGuid = USB\VID_17EF&PID_1014 + +# Lenovo USB3 Pro Dock +[USB\VID_17EF&PID_1016] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 +[USB\VID_17EF&PID_1018] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 +ParentGuid = USB\VID_17EF&PID_1016 + +# Lenovo Workstation D40 +[USB\VID_17EF&PID_1033] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 + +# Lenovo Workstation S40 +[USB\VID_17EF&PID_1034] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 + +# Lenovo Workstation v40 +[USB\VID_17EF&PID_1035] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 + +# Lenovo One Link Plus +[USB\VID_17EF&PID_1018] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 +[USB\VID_17EF&PID_1019] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,usb3 +ParentGuid = USB\VID_17EF&PID_1018 + +# Lenovo Hybrid dock +[USB\VID_17EF&PID_A356] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_1028 +[USB\VID_17EF&PID_1028] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +[USB\VID_17EF&PID_A357] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +ParentGuid = USB\VID_17EF&PID_A356 +CounterpartGuid = USB\VID_17EF&PID_1029 +[USB\VID_17EF&PID_1029] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_1028 + +# Lenovo Travel hub +[USB\VID_17EF&PID_7216] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_7224 +[USB\VID_17EF&PID_7224] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_7216 + +# Lenovo Travel hub Gen2 +[USB\VID_17EF&PID_721D] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd +CounterpartGuid = USB\VID_17EF&PID_7225 +[USB\VID_17EF&PID_7225] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_721D +[USB\VID_17EF&PID_721C] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = has-rtd21xx +ParentGuid = USB\VID_17EF&PID_721D +[USB\VID_17EF&PID_721C&I2C_REALTEK] +Name = RTD2181S + +# Lenovo USB-C Mini dock +[USB\VID_17EF&PID_3094] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd,attach-with-gpiob +CounterpartGuid = USB\VID_17EF&PID_3095 +[USB\VID_17EF&PID_3095] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2,attach-with-gpiob +ParentGuid = USB\VID_17EF&PID_3094 +[USB\VID_17EF&PID_3097] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_3094 +[USB\VID_17EF&PID_3093] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = has-rtd21xx +ParentGuid = USB\VID_17EF&PID_3094 +[USB\VID_17EF&PID_3093&I2C_REALTEK] +Name = RTD2181S +[USB\VID_17EF&PID_721C&APP_26] +ProxyGuid = USB\VID_17EF&PID_3094 +FirmwareSize = 0x8000 + +# Lenovo Travel Hub 1in3 +[USB\VID_17EF&PID_7228] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_7236 +[USB\VID_17EF&PID_7236] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_7228 + +# Lenovo USB-C 7-in-1 Hub +[USB\VID_17EF&PID_722A] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd +CounterpartGuid = USB\VID_17EF&PID_7229 +[USB\VID_17EF&PID_7229] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_722A + +# Lenovo USB-C to 4 USB-A Hub +[USB\VID_17EF&PID_1039] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3,has-shared-spi-pd,attach-with-gpiob +CounterpartGuid = USB\VID_17EF&PID_103A +[USB\VID_17EF&PID_103A] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_1039 + +# Lenovo Gen2 dock +[USB\VID_17EF&PID_A391] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +CounterpartGuid = USB\VID_17EF&PID_A392 +[USB\VID_17EF&PID_A392] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_A391 +[USB\VID_17EF&PID_A393] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +ParentGuid = USB\VID_17EF&PID_A391 +CounterpartGuid = USB\VID_17EF&PID_A394 +[USB\VID_17EF&PID_A394] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 +ParentGuid = USB\VID_17EF&PID_A392 +[USB\VID_17EF&PID_A395] +Plugin = vli +GType = FuVliUsbhubDevice + +# Lenovo VGA +[USB\VID_17EF&PID_7211] +Plugin = vli +GType = FuVliPdDevice + +# Lenovo HDMI +[USB\VID_17EF&PID_7212] +Plugin = vli +GType = FuVliPdDevice +Flags = dual-image + +# Lenovo +[USB\VID_17EF&PID_7215] +Plugin = vli +GType = FuVliPdDevice +[USB\VID_17EF&PID_7217] +Plugin = vli +GType = FuVliPdDevice +[USB\VID_17EF&PID_7223] +Plugin = vli +GType = FuVliPdDevice +Flags = dual-image diff --git a/fwupd-1.8.6/plugins/vli/vli-noinst.quirk b/fwupd-1.8.6/plugins/vli/vli-noinst.quirk new file mode 100644 index 0000000000000000000000000000000000000000..aa89eb6aee5fc2fb50ab623e28e8522d86399a5a --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-noinst.quirk @@ -0,0 +1,164 @@ +# 3470_Class +[USB\VID_2109&PID_0810] +Plugin = vli +GType = FuVliUsbhubDevice +[USB\VID_2109&PID_0811] +Plugin = vli +GType = FuVliUsbhubDevice +[USB\VID_2109&PID_0812] +Plugin = vli +GType = FuVliUsbhubDevice +[USB\VID_2109&PID_0813] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = needs-unlock-legacy813 +[USB\VID_2109&PID_8110] +Plugin = vli +GType = FuVliUsbhubDevice +[USB\VID_2109&PID_8113] +Plugin = vli +GType = FuVliUsbhubDevice + +# 3507_Class +[USB\VID_2109&PID_0210] +Plugin = vli +GType = FuVliUsbhubDevice + +# 3545_Class +[USB\VID_2109&PID_0211] +Plugin = vli +GType = FuVliUsbhubDevice +[USB\VID_2109&PID_2211] +Plugin = vli +GType = FuVliUsbhubDevice + +# VL817 +[USB\VID_2109&PID_0817] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +[USB\VID_2109&PID_2817] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 + +# VL819 +[USB\VID_2109&PID_0819] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +[USB\VID_2109&PID_2819] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 + +# VL820 +[USB\VID_2109&PID_0820] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +[USB\VID_2109&PID_2820] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 + +# VL822 +[USB\VID_2109&PID_0822] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb3 +[USB\VID_2109&PID_2822] +Plugin = vli +GType = FuVliUsbhubDevice +Flags = usb2 + +# Generic PD +VliDeviceKind = VL100 +[USB\VID_2109&PID_0100] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL100 +[USB\VID_2109&PID_0101] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL101 +[USB\VID_2109&PID_0102] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL102 +[USB\VID_2109&PID_0103] +Plugin = vli +GType = FuVliPdDevice +Flags = dual-image +VliDeviceKind = VL103 +[USB\VID_2109&PID_0104] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL104 +[USB\VID_2109&PID_0105] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL105 +[USB\VID_2109&PID_0106] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL106 +[USB\VID_2109&PID_0107] +Plugin = vli +GType = FuVliPdDevice +VliDeviceKind = VL107 + +# Generic devices +[USB\VID_2109&PID_D101] +Plugin = vli +GType = FuVliPdDevice +[USB\VID_2109&PID_D102] +Plugin = vli +GType = FuVliPdDevice + +[USB\VID_2109&PID_8880] +Plugin = vli +GType = FuVliPdDevice + +# VL671,U3TT, VT3547 +[USB\VID_2109&PID_8881] +Plugin = vli +GType = FuVliPdDevice +[USB\VID_2109&PID_8882] +Plugin = vli +GType = FuVliPdDevice +[USB\VID_2109&PID_8883] +Plugin = vli +GType = FuVliPdDevice + +# Realtek +[USB\VID_2109&PID_8884] +Plugin = vli +GType = FuVliPdDevice + +# VL650, VT3555 +[USB\VID_2109&PID_8885] +Plugin = vli +GType = FuVliPdDevice + +# MStar, VT3518 +[USB\VID_2109&PID_8886] +Plugin = vli +GType = FuVliPdDevice + +[USB\VID_2109&PID_8887] +Plugin = vli +GType = FuVliPdDevice + +[USB\VID_2109&PID_8889] +Plugin = vli +GType = FuVliPdDevice + +# Novatek, VT3538 +[USB\VID_2109&PID_888A] +Plugin = vli +GType = FuVliPdDevice + +[USB\VID_2109&PID_888B] +Plugin = vli +GType = FuVliPdDevice diff --git a/fwupd-1.8.6/plugins/vli/vli-samsung.quirk b/fwupd-1.8.6/plugins/vli/vli-samsung.quirk new file mode 100644 index 0000000000000000000000000000000000000000..e143450953a8cb64e18138c1dc06d7e43fc0a09d --- /dev/null +++ b/fwupd-1.8.6/plugins/vli/vli-samsung.quirk @@ -0,0 +1,7 @@ +# Samsung +[USB\VID_04E8&PID_A025] +Plugin = vli +GType = FuVliPdDevice +[USB\VID_04E8&PID_A048] +Plugin = vli +GType = FuVliPdDevice diff --git a/fwupd-1.8.6/plugins/wacom-raw/README.md b/fwupd-1.8.6/plugins/wacom-raw/README.md new file mode 100644 index 0000000000000000000000000000000000000000..45a20df348ed8fe0229191b53b0085d08a7f01b6 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/README.md @@ -0,0 +1,58 @@ +# Wacom RAW + +## 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`. + +## 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: + +### WacomI2cFlashBlockSize + +Block size to transfer firmware. + +Since: 1.2.4 + +### WacomI2cFlashBaseAddr + +Base address for firmware. + +Since: 1.2.4 + +### WacomI2cFlashSize + +Maximum size of the firmware zone. + +Since: 1.2.4 + +## Update Behavior + +The device usually presents in runtime mode, but on detach re-enumerates with a +different HIDRAW PID in a bootloader mode. On attach the device re-enumerates +back to the runtime mode. + +For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that +the bootloader and runtime modes are treated as the same device. + +## Vendor ID Security + +The vendor ID is set from the udev vendor, in this instance set to `HIDRAW:0x056A` + +## External Interface Access + +This plugin requires ioctl `HIDIOCSFEATURE` access. diff --git a/fwupd-1.8.6/plugins/wacom-raw/data/hid-recorder.txt b/fwupd-1.8.6/plugins/wacom-raw/data/hid-recorder.txt new file mode 100644 index 0000000000000000000000000000000000000000..a76b65db52b0d6d4d64c517acf25161490b12ccd --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/data/hid-recorder.txt @@ -0,0 +1,470 @@ +# WCOM4875:00 056A:4875 +# 0x05, 0x0d, // Usage Page (Digitizers) 0 +# 0x09, 0x04, // Usage (Touch Screen) 2 +# 0xa1, 0x01, // Collection (Application) 4 +# 0x85, 0x0c, // Report ID (12) 6 +# 0x95, 0x01, // Report Count (1) 8 +# 0x75, 0x08, // Report Size (8) 10 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 12 +# 0x15, 0x00, // Logical Minimum (0) 15 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 17 +# 0x09, 0x54, // Usage (Contact Count) 19 +# 0x81, 0x02, // Input (Data,Var,Abs) 21 +# 0x05, 0x0d, // Usage Page (Digitizers) 23 +# 0x09, 0x22, // Usage (Finger) 25 +# 0xa1, 0x02, // Collection (Logical) 27 +# 0x09, 0x42, // Usage (Tip Switch) 29 +# 0x15, 0x00, // Logical Minimum (0) 31 +# 0x25, 0x01, // Logical Maximum (1) 33 +# 0x75, 0x01, // Report Size (1) 35 +# 0x95, 0x01, // Report Count (1) 37 +# 0x81, 0x02, // Input (Data,Var,Abs) 39 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 41 +# 0x09, 0x47, // Usage (Confidence) 43 +# 0x81, 0x02, // Input (Data,Var,Abs) 45 +# 0x95, 0x05, // Report Count (5) 47 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 49 +# 0x75, 0x10, // Report Size (16) 51 +# 0x09, 0x51, // Usage (Contact Id) 53 +# 0x95, 0x01, // Report Count (1) 55 +# 0x81, 0x02, // Input (Data,Var,Abs) 57 +# 0x05, 0x01, // Usage Page (Generic Desktop) 59 +# 0x75, 0x10, // Report Size (16) 61 +# 0x95, 0x01, // Report Count (1) 63 +# 0x55, 0x0e, // Unit Exponent (-2) 65 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 67 +# 0x09, 0x30, // Usage (X) 69 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 71 +# 0x35, 0x00, // Physical Minimum (0) 74 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 76 +# 0x81, 0x02, // Input (Data,Var,Abs) 79 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 81 +# 0x09, 0x31, // Usage (Y) 84 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 86 +# 0x81, 0x02, // Input (Data,Var,Abs) 89 +# 0xc0, // End Collection 91 +# 0x05, 0x0d, // Usage Page (Digitizers) 92 +# 0x09, 0x22, // Usage (Finger) 94 +# 0xa1, 0x02, // Collection (Logical) 96 +# 0x09, 0x42, // Usage (Tip Switch) 98 +# 0x15, 0x00, // Logical Minimum (0) 100 +# 0x25, 0x01, // Logical Maximum (1) 102 +# 0x75, 0x01, // Report Size (1) 104 +# 0x95, 0x01, // Report Count (1) 106 +# 0x81, 0x02, // Input (Data,Var,Abs) 108 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 110 +# 0x09, 0x47, // Usage (Confidence) 112 +# 0x81, 0x02, // Input (Data,Var,Abs) 114 +# 0x95, 0x05, // Report Count (5) 116 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 118 +# 0x75, 0x10, // Report Size (16) 120 +# 0x09, 0x51, // Usage (Contact Id) 122 +# 0x95, 0x01, // Report Count (1) 124 +# 0x81, 0x02, // Input (Data,Var,Abs) 126 +# 0x05, 0x01, // Usage Page (Generic Desktop) 128 +# 0x75, 0x10, // Report Size (16) 130 +# 0x95, 0x01, // Report Count (1) 132 +# 0x55, 0x0e, // Unit Exponent (-2) 134 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 136 +# 0x09, 0x30, // Usage (X) 138 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 140 +# 0x35, 0x00, // Physical Minimum (0) 143 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 145 +# 0x81, 0x02, // Input (Data,Var,Abs) 148 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 150 +# 0x09, 0x31, // Usage (Y) 153 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 155 +# 0x81, 0x02, // Input (Data,Var,Abs) 158 +# 0xc0, // End Collection 160 +# 0x05, 0x0d, // Usage Page (Digitizers) 161 +# 0x09, 0x22, // Usage (Finger) 163 +# 0xa1, 0x02, // Collection (Logical) 165 +# 0x09, 0x42, // Usage (Tip Switch) 167 +# 0x15, 0x00, // Logical Minimum (0) 169 +# 0x25, 0x01, // Logical Maximum (1) 171 +# 0x75, 0x01, // Report Size (1) 173 +# 0x95, 0x01, // Report Count (1) 175 +# 0x81, 0x02, // Input (Data,Var,Abs) 177 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 179 +# 0x09, 0x47, // Usage (Confidence) 181 +# 0x81, 0x02, // Input (Data,Var,Abs) 183 +# 0x95, 0x05, // Report Count (5) 185 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 187 +# 0x75, 0x10, // Report Size (16) 189 +# 0x09, 0x51, // Usage (Contact Id) 191 +# 0x95, 0x01, // Report Count (1) 193 +# 0x81, 0x02, // Input (Data,Var,Abs) 195 +# 0x05, 0x01, // Usage Page (Generic Desktop) 197 +# 0x75, 0x10, // Report Size (16) 199 +# 0x95, 0x01, // Report Count (1) 201 +# 0x55, 0x0e, // Unit Exponent (-2) 203 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 205 +# 0x09, 0x30, // Usage (X) 207 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 209 +# 0x35, 0x00, // Physical Minimum (0) 212 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 214 +# 0x81, 0x02, // Input (Data,Var,Abs) 217 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 219 +# 0x09, 0x31, // Usage (Y) 222 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 224 +# 0x81, 0x02, // Input (Data,Var,Abs) 227 +# 0xc0, // End Collection 229 +# 0x05, 0x0d, // Usage Page (Digitizers) 230 +# 0x09, 0x22, // Usage (Finger) 232 +# 0xa1, 0x02, // Collection (Logical) 234 +# 0x09, 0x42, // Usage (Tip Switch) 236 +# 0x15, 0x00, // Logical Minimum (0) 238 +# 0x25, 0x01, // Logical Maximum (1) 240 +# 0x75, 0x01, // Report Size (1) 242 +# 0x95, 0x01, // Report Count (1) 244 +# 0x81, 0x02, // Input (Data,Var,Abs) 246 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 248 +# 0x09, 0x47, // Usage (Confidence) 250 +# 0x81, 0x02, // Input (Data,Var,Abs) 252 +# 0x95, 0x05, // Report Count (5) 254 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 256 +# 0x75, 0x10, // Report Size (16) 258 +# 0x09, 0x51, // Usage (Contact Id) 260 +# 0x95, 0x01, // Report Count (1) 262 +# 0x81, 0x02, // Input (Data,Var,Abs) 264 +# 0x05, 0x01, // Usage Page (Generic Desktop) 266 +# 0x75, 0x10, // Report Size (16) 268 +# 0x95, 0x01, // Report Count (1) 270 +# 0x55, 0x0e, // Unit Exponent (-2) 272 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 274 +# 0x09, 0x30, // Usage (X) 276 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 278 +# 0x35, 0x00, // Physical Minimum (0) 281 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 283 +# 0x81, 0x02, // Input (Data,Var,Abs) 286 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 288 +# 0x09, 0x31, // Usage (Y) 291 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 293 +# 0x81, 0x02, // Input (Data,Var,Abs) 296 +# 0xc0, // End Collection 298 +# 0x05, 0x0d, // Usage Page (Digitizers) 299 +# 0x09, 0x22, // Usage (Finger) 301 +# 0xa1, 0x02, // Collection (Logical) 303 +# 0x09, 0x42, // Usage (Tip Switch) 305 +# 0x15, 0x00, // Logical Minimum (0) 307 +# 0x25, 0x01, // Logical Maximum (1) 309 +# 0x75, 0x01, // Report Size (1) 311 +# 0x95, 0x01, // Report Count (1) 313 +# 0x81, 0x02, // Input (Data,Var,Abs) 315 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 317 +# 0x09, 0x47, // Usage (Confidence) 319 +# 0x81, 0x02, // Input (Data,Var,Abs) 321 +# 0x95, 0x05, // Report Count (5) 323 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 325 +# 0x75, 0x10, // Report Size (16) 327 +# 0x09, 0x51, // Usage (Contact Id) 329 +# 0x95, 0x01, // Report Count (1) 331 +# 0x81, 0x02, // Input (Data,Var,Abs) 333 +# 0x05, 0x01, // Usage Page (Generic Desktop) 335 +# 0x75, 0x10, // Report Size (16) 337 +# 0x95, 0x01, // Report Count (1) 339 +# 0x55, 0x0e, // Unit Exponent (-2) 341 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 343 +# 0x09, 0x30, // Usage (X) 345 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 347 +# 0x35, 0x00, // Physical Minimum (0) 350 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 352 +# 0x81, 0x02, // Input (Data,Var,Abs) 355 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 357 +# 0x09, 0x31, // Usage (Y) 360 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 362 +# 0x81, 0x02, // Input (Data,Var,Abs) 365 +# 0xc0, // End Collection 367 +# 0x05, 0x0d, // Usage Page (Digitizers) 368 +# 0x27, 0xff, 0xff, 0x00, 0x00, // Logical Maximum (65535) 370 +# 0x75, 0x10, // Report Size (16) 375 +# 0x95, 0x01, // Report Count (1) 377 +# 0x09, 0x56, // Usage (Scan Time) 379 +# 0x81, 0x02, // Input (Data,Var,Abs) 381 +# 0x85, 0x0c, // Report ID (12) 383 +# 0x09, 0x55, // Usage (Contact Max) 385 +# 0x75, 0x08, // Report Size (8) 387 +# 0x95, 0x01, // Report Count (1) 389 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 391 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 394 +# 0x85, 0x0a, // Report ID (10) 396 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 398 +# 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 401 +# 0x96, 0x00, 0x01, // Report Count (256) 403 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 406 +# 0xc0, // End Collection 408 +# 0x06, 0x11, 0xff, // Usage Page (Vendor Usage Page 0xff11) 409 +# 0x09, 0x11, // Usage (Vendor Usage 0x11) 412 +# 0xa1, 0x01, // Collection (Application) 414 +# 0x85, 0x03, // Report ID (3) 416 +# 0xa1, 0x02, // Collection (Logical) 418 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 420 +# 0x75, 0x08, // Report Size (8) 422 +# 0x15, 0x00, // Logical Minimum (0) 424 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 426 +# 0x95, 0x27, // Report Count (39) 429 +# 0x81, 0x02, // Input (Data,Var,Abs) 431 +# 0xc0, // End Collection 433 +# 0x85, 0x02, // Report ID (2) 434 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 436 +# 0x95, 0x01, // Report Count (1) 438 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 440 +# 0x85, 0x03, // Report ID (3) 442 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 444 +# 0x95, 0x3f, // Report Count (63) 446 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 448 +# 0x85, 0x04, // Report ID (4) 450 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 452 +# 0x95, 0x0f, // Report Count (15) 454 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 456 +# 0x85, 0x07, // Report ID (7) 458 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 460 +# 0x96, 0x00, 0x01, // Report Count (256) 462 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 465 +# 0x85, 0x08, // Report ID (8) 467 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 469 +# 0x96, 0x87, 0x00, // Report Count (135) 471 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 474 +# 0x85, 0x09, // Report ID (9) 476 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 478 +# 0x96, 0x3f, 0x00, // Report Count (63) 480 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 483 +# 0x85, 0x0d, // Report ID (13) 485 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 487 +# 0x95, 0x07, // Report Count (7) 489 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 491 +# 0xc0, // End Collection 493 +# 0x05, 0x0d, // Usage Page (Digitizers) 494 +# 0x09, 0x0e, // Usage (Device Configuration) 496 +# 0xa1, 0x01, // Collection (Application) 498 +# 0x85, 0x0e, // Report ID (14) 500 +# 0x09, 0x23, // Usage (Device Settings) 502 +# 0xa1, 0x02, // Collection (Logical) 504 +# 0x09, 0x52, // Usage (Inputmode) 506 +# 0x09, 0x53, // Usage (Device Index) 508 +# 0x15, 0x00, // Logical Minimum (0) 510 +# 0x25, 0x0a, // Logical Maximum (10) 512 +# 0x75, 0x08, // Report Size (8) 514 +# 0x95, 0x02, // Report Count (2) 516 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 518 +# 0xc0, // End Collection 520 +# 0xc0, // End Collection 521 +# 0x05, 0x0d, // Usage Page (Digitizers) 522 +# 0x09, 0x02, // Usage (Pen) 524 +# 0xa1, 0x01, // Collection (Application) 526 +# 0x85, 0x06, // Report ID (6) 528 +# 0xa4, // Push 530 +# 0x09, 0x20, // Usage (Stylus) 531 +# 0xa1, 0x00, // Collection (Physical) 533 +# 0x09, 0x42, // Usage (Tip Switch) 535 +# 0x09, 0x44, // Usage (Barrel Switch) 537 +# 0x09, 0x45, // Usage (Eraser) 539 +# 0x09, 0x3c, // Usage (Invert) 541 +# 0x09, 0x5a, // Usage (Secondary Barrel Switch) 543 +# 0x09, 0x32, // Usage (In Range) 545 +# 0x15, 0x00, // Logical Minimum (0) 547 +# 0x25, 0x01, // Logical Maximum (1) 549 +# 0x75, 0x01, // Report Size (1) 551 +# 0x95, 0x06, // Report Count (6) 553 +# 0x81, 0x02, // Input (Data,Var,Abs) 555 +# 0x95, 0x02, // Report Count (2) 557 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 559 +# 0x05, 0x01, // Usage Page (Generic Desktop) 561 +# 0x09, 0x30, // Usage (X) 563 +# 0x27, 0x70, 0x86, 0x00, 0x00, // Logical Maximum (34416) 565 +# 0x47, 0x70, 0x86, 0x00, 0x00, // Physical Maximum (34416) 570 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 575 +# 0x55, 0x0d, // Unit Exponent (-3) 577 +# 0x75, 0x10, // Report Size (16) 579 +# 0x95, 0x01, // Report Count (1) 581 +# 0x81, 0x02, // Input (Data,Var,Abs) 583 +# 0x09, 0x31, // Usage (Y) 585 +# 0x27, 0x9f, 0x4b, 0x00, 0x00, // Logical Maximum (19359) 587 +# 0x47, 0x9f, 0x4b, 0x00, 0x00, // Physical Maximum (19359) 592 +# 0x81, 0x02, // Input (Data,Var,Abs) 597 +# 0x45, 0x00, // Physical Maximum (0) 599 +# 0x65, 0x00, // Unit (None) 601 +# 0x55, 0x00, // Unit Exponent (0) 603 +# 0x05, 0x0d, // Usage Page (Digitizers) 605 +# 0x09, 0x30, // Usage (Tip Pressure) 607 +# 0x26, 0xff, 0x0f, // Logical Maximum (4095) 609 +# 0x75, 0x10, // Report Size (16) 612 +# 0x81, 0x02, // Input (Data,Var,Abs) 614 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 616 +# 0x09, 0x5b, // Usage (Vendor Usage 0x5b) 619 +# 0x16, 0x00, 0x80, // Logical Minimum (-32768) 621 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 624 +# 0x75, 0x10, // Report Size (16) 627 +# 0x81, 0x02, // Input (Data,Var,Abs) 629 +# 0x05, 0x0d, // Usage Page (Digitizers) 631 +# 0x09, 0x5b, // Usage (Transducer Serial Number) 633 +# 0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483648) 635 +# 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 640 +# 0x75, 0x20, // Report Size (32) 645 +# 0x81, 0x02, // Input (Data,Var,Abs) 647 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 649 +# 0x09, 0x00, // Usage (Undefined) 652 +# 0x75, 0x08, // Report Size (8) 654 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 656 +# 0x15, 0x00, // Logical Minimum (0) 659 +# 0x81, 0x02, // Input (Data,Var,Abs) 661 +# 0x05, 0x0d, // Usage Page (Digitizers) 663 +# 0x09, 0x3b, // Usage (Battery Strength) 665 +# 0x81, 0x02, // Input (Data,Var,Abs) 667 +# 0x65, 0x14, // Unit (Degrees,EngRotation) 669 +# 0x55, 0x00, // Unit Exponent (0) 671 +# 0x16, 0xa6, 0xff, // Logical Minimum (-90) 673 +# 0x26, 0x5a, 0x00, // Logical Maximum (90) 676 +# 0x36, 0xa6, 0xff, // Physical Minimum (-90) 679 +# 0x46, 0x5a, 0x00, // Physical Maximum (90) 682 +# 0x75, 0x08, // Report Size (8) 685 +# 0x09, 0x3d, // Usage (X Tilt) 687 +# 0x81, 0x02, // Input (Data,Var,Abs) 689 +# 0x09, 0x3e, // Usage (Y Tilt) 691 +# 0x81, 0x02, // Input (Data,Var,Abs) 693 +# 0xc0, // End Collection 695 +# 0xb4, // Pop 696 +# 0x85, 0x13, // Report ID (19) 697 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 699 +# 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 702 +# 0x96, 0x00, 0x01, // Report Count (256) 704 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 707 +# 0xc0, // End Collection 709 +# 0x06, 0x11, 0xff, // Usage Page (Vendor Usage Page 0xff11) 710 +# 0x09, 0x02, // Usage (Vendor Usage 0x02) 713 +# 0xa1, 0x01, // Collection (Application) 715 +# 0x85, 0x0b, // Report ID (11) 717 +# 0xa4, // Push 719 +# 0x09, 0x20, // Usage (Vendor Usage 0x20) 720 +# 0xa1, 0x00, // Collection (Physical) 722 +# 0x09, 0x42, // Usage (Vendor Usage 0x42) 724 +# 0x09, 0x44, // Usage (Vendor Usage 0x44) 726 +# 0x09, 0x45, // Usage (Vendor Usage 0x45) 728 +# 0x09, 0x3c, // Usage (Vendor Usage 0x3c) 730 +# 0x09, 0x5a, // Usage (Vendor Usage 0x5a) 732 +# 0x09, 0x32, // Usage (Vendor Usage 0x32) 734 +# 0x15, 0x00, // Logical Minimum (0) 736 +# 0x25, 0x01, // Logical Maximum (1) 738 +# 0x75, 0x01, // Report Size (1) 740 +# 0x95, 0x06, // Report Count (6) 742 +# 0x81, 0x02, // Input (Data,Var,Abs) 744 +# 0x95, 0x02, // Report Count (2) 746 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 748 +# 0x05, 0x01, // Usage Page (Generic Desktop) 750 +# 0x09, 0x30, // Usage (X) 752 +# 0x27, 0x70, 0x86, 0x00, 0x00, // Logical Maximum (34416) 754 +# 0x47, 0x70, 0x86, 0x00, 0x00, // Physical Maximum (34416) 759 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 764 +# 0x55, 0x0d, // Unit Exponent (-3) 766 +# 0x75, 0x10, // Report Size (16) 768 +# 0x95, 0x01, // Report Count (1) 770 +# 0x81, 0x02, // Input (Data,Var,Abs) 772 +# 0x09, 0x31, // Usage (Y) 774 +# 0x27, 0x9f, 0x4b, 0x00, 0x00, // Logical Maximum (19359) 776 +# 0x47, 0x9f, 0x4b, 0x00, 0x00, // Physical Maximum (19359) 781 +# 0x81, 0x02, // Input (Data,Var,Abs) 786 +# 0x45, 0x00, // Physical Maximum (0) 788 +# 0x65, 0x00, // Unit (None) 790 +# 0x55, 0x00, // Unit Exponent (0) 792 +# 0x05, 0x0d, // Usage Page (Digitizers) 794 +# 0x09, 0x30, // Usage (Tip Pressure) 796 +# 0x26, 0xff, 0x0f, // Logical Maximum (4095) 798 +# 0x75, 0x10, // Report Size (16) 801 +# 0x81, 0x02, // Input (Data,Var,Abs) 803 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 805 +# 0x09, 0x5b, // Usage (Vendor Usage 0x5b) 808 +# 0x16, 0x00, 0x80, // Logical Minimum (-32768) 810 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 813 +# 0x75, 0x10, // Report Size (16) 816 +# 0x81, 0x02, // Input (Data,Var,Abs) 818 +# 0x05, 0x0d, // Usage Page (Digitizers) 820 +# 0x09, 0x5b, // Usage (Transducer Serial Number) 822 +# 0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483648) 824 +# 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 829 +# 0x75, 0x20, // Report Size (32) 834 +# 0x81, 0x02, // Input (Data,Var,Abs) 836 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 838 +# 0x09, 0x00, // Usage (Undefined) 841 +# 0x75, 0x08, // Report Size (8) 843 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 845 +# 0x15, 0x00, // Logical Minimum (0) 848 +# 0x81, 0x02, // Input (Data,Var,Abs) 850 +# 0x05, 0x0d, // Usage Page (Digitizers) 852 +# 0x09, 0x3b, // Usage (Battery Strength) 854 +# 0x81, 0x02, // Input (Data,Var,Abs) 856 +# 0x65, 0x14, // Unit (Degrees,EngRotation) 858 +# 0x55, 0x00, // Unit Exponent (0) 860 +# 0x16, 0xa6, 0xff, // Logical Minimum (-90) 862 +# 0x26, 0x5a, 0x00, // Logical Maximum (90) 865 +# 0x36, 0xa6, 0xff, // Physical Minimum (-90) 868 +# 0x46, 0x5a, 0x00, // Physical Maximum (90) 871 +# 0x75, 0x08, // Report Size (8) 874 +# 0x09, 0x3d, // Usage (X Tilt) 876 +# 0x81, 0x02, // Input (Data,Var,Abs) 878 +# 0x09, 0x3e, // Usage (Y Tilt) 880 +# 0x81, 0x02, // Input (Data,Var,Abs) 882 +# 0xc0, // End Collection 884 +# 0xb4, // Pop 885 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 886 +# 0x75, 0x08, // Report Size (8) 889 +# 0x15, 0x00, // Logical Minimum (0) 891 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 893 +# 0x85, 0x05, // Report ID (5) 896 +# 0x09, 0x00, // Usage (Undefined) 898 +# 0x95, 0x3a, // Report Count (58) 900 +# 0x81, 0x02, // Input (Data,Var,Abs) 902 +# 0x85, 0x10, // Report ID (16) 904 +# 0x09, 0x00, // Usage (Undefined) 906 +# 0x95, 0x14, // Report Count (20) 908 +# 0x81, 0x02, // Input (Data,Var,Abs) 910 +# 0x85, 0x0f, // Report ID (15) 912 +# 0x09, 0x00, // Usage (Undefined) 914 +# 0x95, 0x28, // Report Count (40) 916 +# 0x81, 0x02, // Input (Data,Var,Abs) 918 +# 0x85, 0x0f, // Report ID (15) 920 +# 0x09, 0x00, // Usage (Undefined) 922 +# 0x95, 0x07, // Report Count (7) 924 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 926 +# 0x85, 0x11, // Report ID (17) 928 +# 0x09, 0x00, // Usage (Undefined) 930 +# 0x95, 0x09, // Report Count (9) 932 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 934 +# 0x85, 0x05, // Report ID (5) 936 +# 0x09, 0x00, // Usage (Undefined) 938 +# 0x95, 0x08, // Report Count (8) 940 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 942 +# 0x85, 0x10, // Report ID (16) 944 +# 0x09, 0x00, // Usage (Undefined) 946 +# 0x96, 0x3f, 0x00, // Report Count (63) 948 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 951 +# 0x85, 0x0b, // Report ID (11) 953 +# 0x09, 0x00, // Usage (Undefined) 955 +# 0x96, 0x3f, 0x00, // Report Count (63) 957 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 960 +# 0xc0, // End Collection 962 +# 0x05, 0x01, // Usage Page (Generic Desktop) 963 +# 0x09, 0x02, // Usage (Mouse) 965 +# 0xa1, 0x01, // Collection (Application) 967 +# 0x85, 0x01, // Report ID (1) 969 +# 0x09, 0x01, // Usage (Pointer) 971 +# 0xa1, 0x00, // Collection (Physical) 973 +# 0x05, 0x09, // Usage Page (Button) 975 +# 0x19, 0x01, // Usage Minimum (1) 977 +# 0x29, 0x02, // Usage Maximum (2) 979 +# 0x15, 0x00, // Logical Minimum (0) 981 +# 0x25, 0x01, // Logical Maximum (1) 983 +# 0x95, 0x02, // Report Count (2) 985 +# 0x75, 0x01, // Report Size (1) 987 +# 0x81, 0x02, // Input (Data,Var,Abs) 989 +# 0x95, 0x01, // Report Count (1) 991 +# 0x75, 0x06, // Report Size (6) 993 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 995 +# 0x05, 0x01, // Usage Page (Generic Desktop) 997 +# 0x09, 0x30, // Usage (X) 999 +# 0x09, 0x31, // Usage (Y) 1001 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 1003 +# 0x75, 0x10, // Report Size (16) 1006 +# 0x95, 0x02, // Report Count (2) 1008 +# 0x81, 0x02, // Input (Data,Var,Abs) 1010 +# 0xc0, // End Collection 1012 +# 0xc0, // End Collection 1013 diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-aes-device.c b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-aes-device.c new file mode 100644 index 0000000000000000000000000000000000000000..daa511b5af8c2d61f7c015b90db916f8bc57b39b --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-aes-device.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wacom-aes-device.h" +#include "fu-wacom-common.h" + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 cmd; + guint8 echo; + guint32 addr; + guint8 size8; + guint8 data[128]; +} FuWacomRawVerifyResponse; + +struct _FuWacomAesDevice { + FuWacomDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWacomAesDevice, fu_wacom_aes_device, FU_TYPE_WACOM_DEVICE) + +static gboolean +fu_wacom_aes_add_recovery_hwid(FuDevice *device, GError **error) +{ + FuWacomRawRequest cmd = { + .report_id = FU_WACOM_RAW_BL_REPORT_ID_SET, + .cmd = FU_WACOM_RAW_BL_CMD_VERIFY_FLASH, + .echo = 0x01, + .addr = FU_WACOM_RAW_BL_START_ADDR, + .size8 = FU_WACOM_RAW_BL_BYTES_CHECK / 8, + }; + FuWacomRawVerifyResponse rsp = {.report_id = FU_WACOM_RAW_BL_REPORT_ID_GET, + .size8 = 0x00, + .data = {0x00}}; + guint16 pid; + + if (!fu_wacom_device_set_feature(FU_WACOM_DEVICE(device), + (guint8 *)&cmd, + sizeof(cmd), + error)) { + g_prefix_error(error, "failed to send: "); + return FALSE; + } + if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(device), + (guint8 *)&rsp, + sizeof(rsp), + error)) { + g_prefix_error(error, "failed to receive: "); + return FALSE; + } + if (rsp.size8 != cmd.size8) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "firmware does not support this feature"); + return FALSE; + } + + pid = (rsp.data[7] << 8) + (rsp.data[6]); + if ((pid == 0xFFFF) || (pid == 0x0000)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "invalid recovery product ID %04x", + pid); + return FALSE; + } + + /* add recovery IDs */ + fu_device_add_instance_u16(device, "VEN", 0x2D1F); + fu_device_add_instance_u16(device, "DEV", pid); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VID", "PID", NULL)) + return FALSE; + fu_device_add_instance_u16(device, "VEN", 0x056A); + return fu_device_build_instance_id(device, error, "HIDRAW", "VID", "PID", NULL); +} + +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); + g_autoptr(GError) error_local = NULL; + + /* 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"); + /* get the recovery PID if supported */ + if (!fu_wacom_aes_add_recovery_hwid(device, &error_local)) + g_debug("failed to get HwID: %s", error_local->message); + } else { + guint16 fw_ver; + guint8 data[FU_WACOM_RAW_STATUS_REPORT_SZ] = {FU_WACOM_RAW_STATUS_REPORT_ID, 0x0}; + g_autofree gchar *version = NULL; + + if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(self), data, sizeof(data), error)) + return FALSE; + if (!fu_memread_uint16_safe(data, + sizeof(data), + 11, + &fw_ver, + G_LITTLE_ENDIAN, + error)) + return FALSE; + version = g_strdup_printf("%04x.%02x", fw_ver, data[13]); + fu_device_set_version(device, version); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_aes_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE(device); + FuWacomRawRequest req = {.report_id = FU_WACOM_RAW_BL_REPORT_ID_TYPE, + .cmd = FU_WACOM_RAW_BL_TYPE_FINALIZER, + 0x00}; + + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + if (!fu_wacom_device_set_feature(self, (const guint8 *)&req, sizeof(req), error)) { + g_prefix_error(error, "failed to finalize the device: "); + return FALSE; + } + + /* wait for device back to runtime mode */ + g_usleep(500 * 1000); + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_wacom_aes_device_erase_all(FuWacomAesDevice *self, FuProgress *progress, 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; + } + fu_progress_sleep(progress, 2000); + return TRUE; +} + +static gboolean +fu_wacom_aes_device_write_block(FuWacomAesDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + gsize datasz, + GError **error) +{ + gsize blocksz = fu_wacom_device_get_block_sz(FU_WACOM_DEVICE(self)); + 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", + (guint)datasz, + (guint)blocksz); + return FALSE; + } + if (!fu_memcpy_safe((guint8 *)&req.data, + sizeof(req.data), + 0x0, /* dst */ + data, + datasz, + 0x0, /* src */ + datasz, + error)) + return FALSE; + + /* 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, + FuProgress *progress, + GError **error) +{ + FuWacomAesDevice *self = FU_WACOM_AES_DEVICE(device); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + + /* erase */ + if (!fu_wacom_aes_device_erase_all(self, progress, error)) + return FALSE; + fu_progress_step_done(progress); + + /* 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, + fu_chunk_get_idx(chk), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + return TRUE; +} + +static void +fu_wacom_aes_device_init(FuWacomAesDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "Wacom AES Device"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_wacom_aes_device_class_init(FuWacomAesDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + FuWacomDeviceClass *klass_wac_device = FU_WACOM_DEVICE_CLASS(klass); + klass_device->setup = fu_wacom_aes_device_setup; + klass_device->attach = fu_wacom_aes_device_attach; + klass_wac_device->write_firmware = fu_wacom_aes_device_write_firmware; +} + +FuWacomAesDevice * +fu_wacom_aes_device_new(FuUdevDevice *device) +{ + FuWacomAesDevice *self = g_object_new(FU_TYPE_WACOM_AES_DEVICE, NULL); + fu_device_incorporate(FU_DEVICE(self), FU_DEVICE(device)); + return self; +} diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-aes-device.h b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-aes-device.h new file mode 100644 index 0000000000000000000000000000000000000000..0000c02d9f3bc9edd534643a4cfc70b535148dfd --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-aes-device.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wacom-device.h" + +#define FU_TYPE_WACOM_AES_DEVICE (fu_wacom_aes_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWacomAesDevice, fu_wacom_aes_device, FU, WACOM_AES_DEVICE, FuWacomDevice) + +FuWacomAesDevice * +fu_wacom_aes_device_new(FuUdevDevice *device); diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-common.c b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-common.c new file mode 100644 index 0000000000000000000000000000000000000000..fc360d7af6d3eb4ca9cf07d140d74bed289f3e16 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-common.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wacom-common.h" + +gboolean +fu_wacom_common_check_reply(const FuWacomRawRequest *req, + const FuWacomRawResponse *rsp, + GError **error) +{ + if (rsp->report_id != FU_WACOM_RAW_BL_REPORT_ID_GET) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "report ID failed, expected 0x%02x, got 0x%02x", + (guint)FU_WACOM_RAW_BL_REPORT_ID_GET, + req->report_id); + return FALSE; + } + if (req->cmd != rsp->cmd) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cmd failed, expected 0x%02x, got 0x%02x", + req->cmd, + rsp->cmd); + return FALSE; + } + if (req->echo != rsp->echo) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "echo failed, expected 0x%02x, got 0x%02x", + req->echo, + rsp->echo); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wacom_common_rc_set_error(const FuWacomRawResponse *rsp, GError **error) +{ + if (rsp->resp == FU_WACOM_RAW_RC_OK) + return TRUE; + if (rsp->resp == FU_WACOM_RAW_RC_BUSY) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_BUSY, "device is busy"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_MCUTYPE) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "MCU type does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_PID) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "PID does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_CHECKSUM1) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "checksum1 does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_CHECKSUM2) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "checksum2 does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_TIMEOUT) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "command timed out"); + return FALSE; + } + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "unknown error 0x%02x", rsp->resp); + return FALSE; +} + +gboolean +fu_wacom_common_block_is_empty(const guint8 *data, guint16 datasz) +{ + for (guint16 i = 0; i < datasz; i++) { + if (data[i] != 0xff) + return FALSE; + } + return TRUE; +} diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-common.h b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-common.h new file mode 100644 index 0000000000000000000000000000000000000000..245a774ef71882b35af1f6a98575f74678543622 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-common.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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_BL_START_ADDR (0x11FF8) +#define FU_WACOM_RAW_BL_BYTES_CHECK 8 + +#define FU_WACOM_RAW_BL_REPORT_ID_TYPE 0x02 +#define FU_WACOM_RAW_BL_TYPE_FINALIZER 0x00 + +#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); diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-device.c b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-device.c new file mode 100644 index 0000000000000000000000000000000000000000..9de49b740111f3e6eaff869d4b240194646bcb4e --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-device.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-wacom-common.h" +#include "fu-wacom-device.h" + +typedef struct { + 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)) + +#define FU_WACOM_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ + +static void +fu_wacom_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuWacomDevice *self = FU_WACOM_DEVICE(device); + FuWacomDevicePrivate *priv = GET_PRIVATE(self); + + /* FuUdevDevice->to_string */ + FU_DEVICE_CLASS(fu_wacom_device_parent_class)->to_string(device, idt, str); + + fu_string_append_kx(str, idt, "FlashBlockSize", priv->flash_block_size); + fu_string_append_kx(str, idt, "FlashBaseAddr", priv->flash_base_addr); + fu_string_append_kx(str, idt, "FlashSize", priv->flash_size); +} + +gsize +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_full(FU_DEVICE(self), + "WacomEMR_W9013", + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + return TRUE; + } + + /* W9021 */ + if (rsp.resp == 0x45) { + fu_device_add_instance_id_full(FU_DEVICE(self), + "WacomEMR_W9021", + FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); + 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_probe(FuDevice *device, GError **error) +{ + /* set the physical ID */ + return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); +} + +static gboolean +fu_wacom_device_detach(FuDevice *device, FuProgress *progress, 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_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + if (!fu_wacom_device_set_feature(self, 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_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, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE(device); + FuWacomDevicePrivate *priv = GET_PRIVATE(self); + FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* use the correct image from the firmware */ + g_debug("using element at addr 0x%0x", (guint)fu_firmware_get_addr(firmware)); + + /* check start address and size */ + if (fu_firmware_get_addr(firmware) != priv->flash_base_addr) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "base addr invalid: 0x%05x", + (guint)fu_firmware_get_addr(firmware)); + return FALSE; + } + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + if (g_bytes_get_size(fw) > priv->flash_size) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "size is invalid: 0x%05x", + (guint)g_bytes_get_size(fw)); + return FALSE; + } + + /* we're in bootloader mode now */ + if (!fu_wacom_device_check_mode(self, error)) + return FALSE; + if (!fu_wacom_device_set_version_bootloader(self, error)) + return FALSE; + + /* flash chunks */ + chunks = fu_chunk_array_new_from_bytes(fw, + priv->flash_base_addr, + 0x00, /* page_sz */ + priv->flash_block_size); + return klass->write_firmware(device, chunks, progress, error); +} + +gboolean +fu_wacom_device_set_feature(FuWacomDevice *self, const guint8 *data, guint datasz, GError **error) +{ + fu_dump_raw(G_LOG_DOMAIN, "SetFeature", data, datasz); + return fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCSFEATURE(datasz), + (guint8 *)data, + NULL, + FU_WACOM_DEVICE_IOCTL_TIMEOUT, + error); +} + +gboolean +fu_wacom_device_get_feature(FuWacomDevice *self, guint8 *data, guint datasz, GError **error) +{ + if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self), + HIDIOCGFEATURE(datasz), + data, + NULL, + FU_WACOM_DEVICE_IOCTL_TIMEOUT, + error)) + return FALSE; + fu_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); + guint64 tmp = 0; + + if (g_strcmp0(key, "WacomI2cFlashBlockSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXSIZE, error)) + return FALSE; + priv->flash_block_size = tmp; + return TRUE; + } + if (g_strcmp0(key, "WacomI2cFlashBaseAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + priv->flash_base_addr = tmp; + return TRUE; + } + if (g_strcmp0(key, "WacomI2cFlashSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + priv->flash_size = 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_wacom_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_wacom_device_init(FuWacomDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.wacom.raw"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_IHEX_FIRMWARE); +} + +static void +fu_wacom_device_class_init(FuWacomDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_wacom_device_to_string; + klass_device->write_firmware = fu_wacom_device_write_firmware; + klass_device->detach = fu_wacom_device_detach; + klass_device->set_quirk_kv = fu_wacom_device_set_quirk_kv; + klass_device->probe = fu_wacom_device_probe; + klass_device->set_progress = fu_wacom_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-device.h b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-device.h new file mode 100644 index 0000000000000000000000000000000000000000..f82c558c1b05fee646e9c5dbb29180465e553488 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-device.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-wacom-common.h" + +#define FU_TYPE_WACOM_DEVICE (fu_wacom_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuWacomDevice, fu_wacom_device, FU, WACOM_DEVICE, FuUdevDevice) + +struct _FuWacomDeviceClass { + FuUdevDeviceClass parent_class; + gboolean (*write_firmware)(FuDevice *self, + GPtrArray *chunks, + FuProgress *progress, + GError **error); +}; + +typedef enum { + FU_WACOM_DEVICE_CMD_FLAG_NONE = 0, + FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING = 1 << 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK = 1 << 1, +} FuWacomDeviceCmdFlags; + +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); +gsize +fu_wacom_device_get_block_sz(FuWacomDevice *self); +guint +fu_wacom_device_get_base_addr(FuWacomDevice *self); diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-emr-device.c b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-emr-device.c new file mode 100644 index 0000000000000000000000000000000000000000..ca472542ae5a2813dac44ea203f77aa4cc8a52d0 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-emr-device.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-wacom-common.h" +#include "fu-wacom-emr-device.h" + +struct _FuWacomEmrDevice { + FuWacomDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWacomEmrDevice, fu_wacom_emr_device, FU_TYPE_WACOM_DEVICE) + +static gboolean +fu_wacom_emr_device_setup(FuDevice *device, GError **error) +{ + FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE(device); + + /* check MPU type */ + if (!fu_wacom_device_check_mpu(FU_WACOM_DEVICE(self), error)) + return FALSE; + + /* get firmware version */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version(device, "0.0"); + } else { + guint16 fw_ver; + guint8 data[19] = {0x03, 0x0}; /* 0x03 is an unknown ReportID */ + g_autofree gchar *version = NULL; + if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(self), data, sizeof(data), error)) + return FALSE; + if (!fu_memread_uint16_safe(data, + sizeof(data), + 11, + &fw_ver, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + version = fu_version_from_uint16(fw_ver, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device, version); + fu_device_set_version_raw(device, fw_ver); + } + + /* success */ + return TRUE; +} + +static guint8 +fu_wacom_emr_device_calc_checksum(guint8 init1, const guint8 *buf, gsize bufsz) +{ + return init1 + ~(fu_sum8(buf, bufsz)) + 1; +} + +static gboolean +fu_wacom_emr_device_w9013_erase_data(FuWacomEmrDevice *self, GError **error) +{ + 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_attach(FuDevice *device, FuProgress *progress, 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_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + 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; + } + + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_wacom_emr_device_write_block(FuWacomEmrDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + gsize datasz, + GError **error) +{ + gsize blocksz = fu_wacom_device_get_block_sz(FU_WACOM_DEVICE(self)); + 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", + (guint)datasz); + return FALSE; + } + if (datasz != blocksz) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "block size 0x%x != 0x%x untested", + (guint)datasz, + (guint)blocksz); + return FALSE; + } + + /* data */ + memcpy(&req.data, data, datasz); + + /* cmd and data checksums */ + req.data_unused[0] = + fu_wacom_emr_device_calc_checksum(0x05 + 0x00 + 0x4c + 0x00, (const guint8 *)&req, 8); + req.data_unused[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, + FuProgress *progress, + GError **error) +{ + FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE(device); + guint8 idx = 0; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + + /* erase W9013 */ + if (fu_device_has_instance_id(device, "WacomEMR_W9013")) { + 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; + } + fu_progress_step_done(progress); + + /* write */ + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (fu_wacom_common_block_is_empty(fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk))) + continue; + if (!fu_wacom_emr_device_write_block(self, + fu_chunk_get_idx(chk), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)chunks->len); + } + fu_progress_step_done(progress); + + return TRUE; +} + +static void +fu_wacom_emr_device_init(FuWacomEmrDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "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_device->attach = fu_wacom_emr_device_attach; + klass_wac_device->write_firmware = fu_wacom_emr_device_write_firmware; +} + +FuWacomEmrDevice * +fu_wacom_emr_device_new(FuUdevDevice *device) +{ + FuWacomEmrDevice *self = g_object_new(FU_TYPE_WACOM_EMR_DEVICE, NULL); + fu_device_incorporate(FU_DEVICE(self), FU_DEVICE(device)); + return self; +} diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-emr-device.h b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-emr-device.h new file mode 100644 index 0000000000000000000000000000000000000000..d06e8d36b3448354d7cc34d4e1d77c08cb59c372 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-emr-device.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wacom-device.h" + +#define FU_TYPE_WACOM_EMR_DEVICE (fu_wacom_emr_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWacomEmrDevice, fu_wacom_emr_device, FU, WACOM_EMR_DEVICE, FuWacomDevice) + +FuWacomEmrDevice * +fu_wacom_emr_device_new(FuUdevDevice *device); diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-raw-plugin.c b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-raw-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..d0fc8695aec92a5667eec1a08e3ec4760c2a21f3 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-raw-plugin.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-wacom-aes-device.h" +#include "fu-wacom-common.h" +#include "fu-wacom-emr-device.h" +#include "fu-wacom-raw-plugin.h" + +struct _FuWacomRawPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuWacomRawPlugin, fu_wacom_raw_plugin, FU_TYPE_PLUGIN) + +static void +fu_wacom_raw_plugin_init(FuWacomRawPlugin *self) +{ +} + +static void +fu_wacom_raw_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "WacomI2cFlashBlockSize"); + fu_context_add_quirk_key(ctx, "WacomI2cFlashBaseAddr"); + fu_context_add_quirk_key(ctx, "WacomI2cFlashSize"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WACOM_AES_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WACOM_EMR_DEVICE); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); +} + +static void +fu_wacom_raw_plugin_class_init(FuWacomRawPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = fu_wacom_raw_plugin_constructed; +} diff --git a/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-raw-plugin.h b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-raw-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..08afda9cf65e176500fd0d7fde1745d4055e43f5 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/fu-wacom-raw-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuWacomRawPlugin, fu_wacom_raw_plugin, FU, WACOM_RAW_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/wacom-raw/meson.build b/fwupd-1.8.6/plugins/wacom-raw/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..727e0481c15344c8da6ed51ca3362c89da51bc65 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/meson.build @@ -0,0 +1,18 @@ +if gudev.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginWacomRaw"'] + +plugin_quirks += files('wacom-raw.quirk') +plugin_builtins += static_library('fu_plugin_wacom_raw', + sources: [ + 'fu-wacom-raw-plugin.c', + 'fu-wacom-common.c', + 'fu-wacom-device.c', + 'fu-wacom-aes-device.c', + 'fu-wacom-emr-device.c', + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: plugin_deps, + link_with: plugin_libs, +) +endif diff --git a/fwupd-1.8.6/plugins/wacom-raw/wacom-raw.quirk b/fwupd-1.8.6/plugins/wacom-raw/wacom-raw.quirk new file mode 100644 index 0000000000000000000000000000000000000000..cb80266e33d6a24ab8a81e3647ea5f1f81fa19cd --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-raw/wacom-raw.quirk @@ -0,0 +1,137 @@ +# 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. + +#Lenovo X1 Yoga Gen7 5308 +[HIDRAW\VEN_056A&DEV_5308] +Plugin = wacom_raw +Guid = WacomAES + +#Lenovo X1 Yoga Gen7 5309 +[HIDRAW\VEN_056A&DEV_5309] +Plugin = wacom_raw +Guid = WacomAES + +#Lenovo X1 Yoga Gen7 530A +[HIDRAW\VEN_056A&DEV_530A] +Plugin = wacom_raw +Guid = WacomAES + +#Lenovo X1 Yoga Gen7 530B +[HIDRAW\VEN_056A&DEV_530B] +Plugin = wacom_raw +Guid = WacomAES + +#Lenovo X1 Yoga Gen7 52B5 +[HIDRAW\VEN_056A&DEV_52B5] +Plugin = wacom_raw +Guid = WacomAES + +#Lenovo X1 Yoga Gen7 52C4 +[HIDRAW\VEN_056A&DEV_52C4] +Plugin = wacom_raw +Guid = WacomAES + +# Dell Chromebook Enterprise 5300 +[HIDRAW\VEN_2D1F&DEV_4946] +Plugin = wacom_raw +Guid = WacomAES + +# Moffet 14-LGD-TPK +[HIDRAW\VEN_2D1F&DEV_4970] +Plugin = wacom_raw +Guid = WacomAES +Flags = self-recovery + +# Moffet 14-Sharp-HH +[HIDRAW\VEN_2D1F&DEV_4971] +Plugin = wacom_raw +Guid = WacomAES +Flags = self-recovery + +# Moffet 14-Sharp-VIA +[HIDRAW\VEN_2D1F&DEV_4972] +Plugin = wacom_raw +Guid = WacomAES +Flags = self-recovery + +# Dell Latitude 5175 +[HIDRAW\VEN_056A&DEV_4807] +Plugin = wacom_raw +Guid = WacomAES + +# Dell XPS 12 9250 +[HIDRAW\VEN_056A&DEV_4822] +Plugin = wacom_raw +Guid = WacomAES + +# Dell Venue 8 Pro 5855 +[HIDRAW\VEN_056A&DEV_4824] +Plugin = wacom_raw +Guid = WacomAES + +# Dell XPS 13 9365 +[HIDRAW\VEN_056A&DEV_4831] +Plugin = wacom_raw +Guid = WacomAES + +# Dell Latitude 5285 +[HIDRAW\VEN_056A&DEV_484C] +Plugin = wacom_raw +Guid = WacomAES + +# Dell Latitude 7390 2-in-1 +[HIDRAW\VEN_056A&DEV_4841] +Plugin = wacom_raw +Guid = WacomAES + +# Dell XPS-15 9575 +[HIDRAW\VEN_056A&DEV_4875] +Plugin = wacom_raw +Guid = WacomAES + +# Dell Latitude 7400 2-in-1 +[HIDRAW\VEN_056A&DEV_48C9] +Plugin = wacom_raw +Guid = WacomAES + +# Dell XPS-15 9570 +[HIDRAW\VEN_056A&DEV_488F] +Plugin = wacom_raw +Guid = WacomAES + +# Dell XPS 13 7390 2-in-1 +[HIDRAW\VEN_056A&DEV_48ED] +Plugin = wacom_raw +Guid = WacomAES + +# AES bootloader mode +[HIDRAW\VEN_056A&DEV_0094] +Plugin = wacom_raw +Guid = WacomAES +Flags = is-bootloader + +# EMR bootloader mode +[HIDRAW\VEN_056A&DEV_012B] +Plugin = wacom_raw +Guid = WacomEMR +Flags = is-bootloader + +[WacomEMR_W9013] +WacomI2cFlashBlockSize = 64 +WacomI2cFlashBaseAddr = 0x2000 +WacomI2cFlashSize = 0x1e000 + +[WacomEMR_W9021] +WacomI2cFlashBlockSize = 256 +WacomI2cFlashBaseAddr = 0x3000 +WacomI2cFlashSize = 0x3c000 + +[WacomEMR] +GType = FuWacomEmrDevice + +[WacomAES] +GType = FuWacomAesDevice +WacomI2cFlashBlockSize = 128 +WacomI2cFlashBaseAddr = 0x8000 +WacomI2cFlashSize = 0x28000 diff --git a/fwupd-1.8.6/plugins/wacom-usb/README.md b/fwupd-1.8.6/plugins/wacom-usb/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8bf4f8fb8eca4dc98eddbed39e33f0eeaf7fa983 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/README.md @@ -0,0 +1,50 @@ +# Wacom USB + +## 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+ license. + +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` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, for example set to `USB:0x056A` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/fwupd-1.8.6/plugins/wacom-usb/data/lsusb.txt b/fwupd-1.8.6/plugins/wacom-usb/data/lsusb.txt new file mode 100644 index 0000000000000000000000000000000000000000..216f0b72c1c812872194a665961a204e3d4208d8 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/data/lsusb.txt @@ -0,0 +1,58 @@ +Bus 001 Device 023: ID 056a:0378 Wacom Co., Ltd CTL-6100WL [Intuos BT (M)] +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x056a Wacom Co., Ltd + idProduct 0x0378 CTL-6100WL [Intuos BT (M)] + bcdDevice 1.66 + iManufacturer 1 Wacom Co.,Ltd. + iProduct 2 Intuos BT M + iSerial 3 8BH00U2012294 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0022 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.10 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 759 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-self-test.c b/fwupd-1.8.6/plugins/wacom-usb/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..eab6e7a024883f6c18e025bf3d184ef397b51739 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-self-test.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-wac-common.h" +#include "fu-wac-firmware.h" + +static void +fu_wac_firmware_parse_func(void) +{ + gboolean ret; + g_autofree gchar *fn = NULL; + g_autoptr(FuFirmware) firmware = fu_wac_firmware_new(); + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GBytes) blob_block = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + + /* parse the test file */ + fn = g_test_build_filename(G_TEST_DIST, "tests", "test.wac", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_test_skip("no data file found"); + return; + } + bytes = fu_bytes_get_contents(fn, &error); + g_assert_no_error(error); + g_assert_nonnull(bytes); + ret = fu_firmware_parse(firmware, bytes, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* get image data */ + img = fu_firmware_get_image_by_id(firmware, 0, &error); + g_assert_no_error(error); + g_assert_nonnull(img); + + /* get block */ + blob_block = fu_firmware_write_chunk(img, 0x8008000, 1024, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_block); + fu_wac_buffer_dump("IMG", + FU_WAC_REPORT_ID_MODULE, + g_bytes_get_data(blob_block, NULL), + g_bytes_get_size(blob_block)); +} +static void +fu_wac_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_wac_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_wac_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "wacom-usb.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "346f6196449b356777cf241f6edb039d503b88a1"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_type_ensure(FU_TYPE_SREC_FIRMWARE); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + + /* log everything */ + (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); + + /* tests go here */ + g_test_add_func("/wac/firmware{parse}", fu_wac_firmware_parse_func); + g_test_add_func("/wac/firmware{xml}", fu_wac_firmware_xml_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-android-device.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-android-device.c new file mode 100644 index 0000000000000000000000000000000000000000..2055e5adaebaf485de77ddce801b723c32bd8714 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-android-device.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wac-android-device.h" + +struct _FuWacAndroidDevice { + FuHidDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWacAndroidDevice, fu_wac_android_device, FU_TYPE_HID_DEVICE) + +static void +fu_wac_android_device_init(FuWacAndroidDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.wacom.usb"); + fu_device_add_icon(FU_DEVICE(self), "input-tablet"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_inhibit(FU_DEVICE(self), + "hw", + "Switch into PC mode by holding down the " + "two outermost ExpressKeys for 4 seconds"); +} + +static void +fu_wac_android_device_class_init(FuWacAndroidDeviceClass *klass) +{ +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-android-device.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-android-device.h new file mode 100644 index 0000000000000000000000000000000000000000..5ba8ee613ac8157d8af870759e7d3517f240893d --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-android-device.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_WAC_ANDROID_DEVICE (fu_wac_android_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWacAndroidDevice, fu_wac_android_device, FU, WAC_ANDROID_DEVICE, FuHidDevice) diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-common.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-common.c new file mode 100644 index 0000000000000000000000000000000000000000..a64dcad9a782e742c8e72411eb3a2e5e76f63497 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-common.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-wac-common.h" + +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_dump_raw(G_LOG_DOMAIN, tmp, buf, sz); +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-common.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-common.h new file mode 100644 index 0000000000000000000000000000000000000000..863ea8d3ff8fe1df07a516d1e2faf5c5ab6e4a9f --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-common.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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 + +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); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-device.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-device.c new file mode 100644 index 0000000000000000000000000000000000000000..c250aa2650677e45998b2d46086f7356f3f2740e --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-device.c @@ -0,0 +1,948 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-wac-common.h" +#include "fu-wac-device.h" +#include "fu-wac-firmware.h" +#include "fu-wac-module-bluetooth-id6.h" +#include "fu-wac-module-bluetooth.h" +#include "fu-wac-module-touch.h" + +typedef struct { + 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 { + FuHidDevice 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_HID_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_flash_descriptor_to_string(FuWacFlashDescriptor *fd, guint idt, GString *str) +{ + fu_string_append_kx(str, idt, "StartAddr", fd->start_addr); + fu_string_append_kx(str, idt, "BlockSize", fd->block_sz); + fu_string_append_kx(str, idt, "WriteSize", fd->write_sz & ~0x8000); + fu_string_append_kb(str, idt, "Protected", fu_wav_device_flash_descriptor_is_wp(fd)); +} + +static void +fu_wac_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuWacDevice *self = FU_WAC_DEVICE(device); + g_autoptr(GString) status_str = NULL; + + if (self->firmware_index != 0xffff) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", self->firmware_index); + fu_string_append(str, idt, "FwIndex", tmp); + } + if (self->loader_ver > 0) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", (guint)self->loader_ver); + fu_string_append(str, idt, "LoaderVer", tmp); + } + if (self->read_data_sz > 0) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", (guint)self->read_data_sz); + fu_string_append(str, idt, "ReadDataSize", tmp); + } + if (self->write_word_sz > 0) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", (guint)self->write_word_sz); + fu_string_append(str, idt, "WriteWordSize", tmp); + } + if (self->write_block_sz > 0) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", (guint)self->write_block_sz); + fu_string_append(str, idt, "WriteBlockSize", tmp); + } + if (self->nr_flash_blocks > 0) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", (guint)self->nr_flash_blocks); + fu_string_append(str, idt, "NrFlashBlocks", tmp); + } + if (self->configuration != 0xffff) { + g_autofree gchar *tmp = g_strdup_printf("0x%04x", (guint)self->configuration); + fu_string_append(str, idt, "Configuration", tmp); + } + if (g_getenv("FWUPD_WACOM_USB_VERBOSE") != NULL) { + for (guint i = 0; i < self->flash_descriptors->len; i++) { + FuWacFlashDescriptor *fd = g_ptr_array_index(self->flash_descriptors, i); + g_autofree gchar *title = g_strdup_printf("FlashDescriptor%02u", i); + fu_string_append(str, idt, title, NULL); + fu_wac_device_flash_descriptor_to_string(fd, idt + 1, str); + } + } + status_str = fu_wac_device_status_to_string(self->status_word); + fu_string_append(str, idt, "Status", status_str->str); +} + +gboolean +fu_wac_device_get_feature_report(FuWacDevice *self, + guint8 *buf, + gsize bufsz, + FuHidDeviceFlags flags, + GError **error) +{ + guint8 cmd = buf[0]; + + /* hit hardware */ + if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + cmd, + buf, + bufsz, + FU_WAC_DEVICE_TIMEOUT, + flags | FU_HID_DEVICE_FLAG_IS_FEATURE, + error)) + return FALSE; + + /* check packet */ + 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, + FuHidDeviceFlags flags, + GError **error) +{ + /* hit hardware */ + if (g_getenv("FWUPD_WAC_EMULATE") != NULL) + return TRUE; + return fu_hid_device_set_report(FU_HID_DEVICE(self), + buf[0], + buf, + bufsz, + FU_WAC_DEVICE_TIMEOUT, + flags | FU_HID_DEVICE_FLAG_IS_FEATURE, + error); +} + +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_HID_DEVICE_FLAG_NONE, error)) + return FALSE; + + /* parse */ + for (guint i = 0; i < self->nr_flash_blocks; i++) { + g_autofree FuWacFlashDescriptor *fd = g_new0(FuWacFlashDescriptor, 1); + const guint blksz = 0x0A; + if (!fu_memread_uint32_safe(buf, + sz, + (i * blksz) + 1, + &fd->start_addr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint32_safe(buf, + sz, + (i * blksz) + 5, + &fd->block_sz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memread_uint16_safe(buf, + sz, + (i * blksz) + 9, + &fd->write_sz, + G_LITTLE_ENDIAN, + error)) + return FALSE; + g_ptr_array_add(self->flash_descriptors, g_steal_pointer(&fd)); + } + if (self->flash_descriptors->len > G_MAXUINT16) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "too many flash descriptors for hardware: 0x%x", + self->flash_descriptors->len); + return FALSE; + } + + 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_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + self->status_word = fu_memread_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_HID_DEVICE_FLAG_NONE, error)) + return FALSE; + + /* parse */ + updater_version = fu_memread_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_memread_uint32(buf + 5 + (i * 4), G_LITTLE_ENDIAN); + if (g_getenv("FWUPD_WACOM_USB_VERBOSE") != NULL) + 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_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + self->firmware_index = fu_memread_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_HID_DEVICE_FLAG_NONE, + error)) + return FALSE; + + /* parse */ + self->loader_ver = fu_memread_uint16(buf + 1, G_LITTLE_ENDIAN); + self->read_data_sz = fu_memread_uint16(buf + 3, G_LITTLE_ENDIAN); + self->write_word_sz = fu_memread_uint16(buf + 5, G_LITTLE_ENDIAN); + self->write_block_sz = fu_memread_uint16(buf + 7, G_LITTLE_ENDIAN); + self->nr_flash_blocks = fu_memread_uint16(buf + 9, G_LITTLE_ENDIAN); + self->configuration = fu_memread_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_memwrite_uint32(buf + 1, addr, G_LITTLE_ENDIAN); + if (sz > 0) { + if (!fu_memcpy_safe(buf, + bufsz, + 0x5, /* dst */ + tmp, + sz, + 0x0, /* src */ + sz, + error)) + return FALSE; + } + + /* hit hardware */ + return fu_wac_device_set_feature_report(self, buf, bufsz, FU_HID_DEVICE_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_memwrite_uint32(buf + 1, addr, G_LITTLE_ENDIAN); + + /* hit hardware */ + return fu_wac_device_set_feature_report(self, + buf, + sizeof(buf), + FU_HID_DEVICE_FLAG_NONE, + error); +} + +static 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_HID_DEVICE_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_memwrite_uint16(buf + 1, block_nr, G_LITTLE_ENDIAN); + fu_memwrite_uint32(buf + 3, checksum, G_LITTLE_ENDIAN); + + /* hit hardware */ + return fu_wac_device_set_feature_report(self, + buf, + sizeof(buf), + FU_HID_DEVICE_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_memwrite_uint16(buf + 1, block_nr, G_LITTLE_ENDIAN); + + /* hit hardware */ + return fu_wac_device_set_feature_report(self, + buf, + sizeof(buf), + FU_HID_DEVICE_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_HID_DEVICE_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_HID_DEVICE_FLAG_NONE, + error); +} + +static gboolean +fu_wac_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWacDevice *self = FU_WAC_DEVICE(device); + gsize blocks_done = 0; + gsize blocks_total = 0; + g_autofree guint32 *csum_local = NULL; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GHashTable) fd_blobs = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 2, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + + /* get current selected device */ + if (!fu_wac_device_ensure_firmware_index(self, error)) + return FALSE; + + /* use the correct image from the firmware */ + img = fu_firmware_get_image_by_idx(firmware, self->firmware_index == 1 ? 1 : 0, error); + if (img == NULL) + return FALSE; + g_debug("using image at addr 0x%0x", (guint)fu_firmware_get_addr(img)); + + /* enter flash mode */ + if (!fu_wac_device_switch_to_flash_loader(self, error)) + return FALSE; + + /* 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; + fu_progress_step_done(progress); + + /* clear all checksums of pages */ + for (guint 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; + } + fu_progress_step_done(progress); + + /* 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 (guint 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 = fu_firmware_write_chunk(img, fd->start_addr, fd->block_sz, NULL); + if (blob_tmp == NULL) + break; + blob_block = fu_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); + + /* write the data into the flash page */ + csum_local = g_new0(guint32, self->flash_descriptors->len); + for (guint 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_bytes_is_empty(blob_block)) { + g_debug("empty block, ignoring"); + fu_progress_set_percentage_full(fu_progress_get_child(progress), + 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 = fu_chunk_get_bytes(chk); + if (!fu_wac_device_write_block(self, + fu_chunk_get_address(chk), + blob_chunk, + error)) + return FALSE; + } + + /* calculate expected checksum and save to device RAM */ + csum_local[i] = GUINT32_TO_LE(fu_sum32w_bytes(blob_block, G_LITTLE_ENDIAN)); + if (g_getenv("FWUPD_WACOM_USB_VERBOSE") != NULL) + 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_progress_set_percentage_full(fu_progress_get_child(progress), + blocks_done++, + blocks_total); + } + fu_progress_step_done(progress); + + /* check at least one block was written */ + if (blocks_done == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "empty firmware image or all blocks write-protected"); + return FALSE; + } + + /* calculate CRC inside device */ + for (guint i = 0; i < self->flash_descriptors->len; i++) { + if (!fu_wac_device_calculate_checksum_of_block(self, i, error)) + return FALSE; + } + + /* read all CRC of all pages and verify with local CRC */ + if (!fu_wac_device_ensure_checksums(self, error)) + return FALSE; + for (guint 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_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; + } + if (g_getenv("FWUPD_WACOM_USB_VERBOSE") != NULL) + g_debug("matched checksum at block %u of 0x%08x", i, csum_rom); + } + fu_progress_step_done(progress); + + /* store host CRC into flash */ + if (!fu_wac_device_write_checksum_table(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + 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 *name_id6 = NULL; + g_autofree gchar *version = NULL; + g_autoptr(FuWacModule) module = NULL; + g_autoptr(FuWacModule) module_id6 = NULL; + guint16 fw_ver; + + /* it can take up to 5s to get the new version after a fw update */ + for (guint i = 0; i < 5; i++) { + guint8 buf[] = {[0] = FU_WAC_REPORT_ID_GET_FIRMWARE_VERSION_BLUETOOTH, + [1 ... 14] = 0xff}; + if (!fu_wac_device_get_feature_report(self, + buf, + sizeof(buf), + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error(error, "Failed to get GetFirmwareVersionBluetooth: "); + return FALSE; + } + if (!fu_memread_uint16_safe(buf, sizeof(buf), 1, &fw_ver, G_LITTLE_ENDIAN, error)) + return FALSE; + if (fw_ver != 0) + break; + g_usleep(G_USEC_PER_SEC); + } + version = fu_version_from_uint16(fw_ver, FWUPD_VERSION_FORMAT_BCD); + + /* Success! But legacy bluetooth can't tell us which module the device needs. + * Initialize both and rely on the firmware update containing the appropriate + * package. + */ + name = g_strdup_printf("%s [Legacy Bluetooth Module]", fu_device_get_name(FU_DEVICE(self))); + module = fu_wac_module_bluetooth_new(fu_device_get_context(FU_DEVICE(self)), 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); + fu_device_set_version_raw(FU_DEVICE(module), fw_ver); + + name_id6 = g_strdup_printf("%s [Legacy Bluetooth Module (ID6)]", + fu_device_get_name(FU_DEVICE(self))); + module_id6 = + fu_wac_module_bluetooth_id6_new(fu_device_get_context(FU_DEVICE(self)), usb_device); + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module_id6)); + fu_device_set_name(FU_DEVICE(module_id6), name_id6); + fu_device_set_version(FU_DEVICE(module_id6), version); + fu_device_set_version_raw(FU_DEVICE(module_id6), fw_ver); + 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}; + guint16 boot_ver; + + if (!fu_wac_device_get_feature_report(self, + buf, + sizeof(buf), + FU_HID_DEVICE_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 */ + if (!fu_memread_uint16_safe(buf, sizeof(buf), 1, &boot_ver, G_BIG_ENDIAN, error)) + return FALSE; + version_bootloader = fu_version_from_uint16(boot_ver, FWUPD_VERSION_FORMAT_BCD); + fu_device_set_version_bootloader(FU_DEVICE(self), version_bootloader); + fu_device_set_version_bootloader_raw(FU_DEVICE(self), boot_ver); + + /* 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; + guint16 ver; + + if (!fu_memread_uint16_safe(buf, + sizeof(buf), + (i * 4) + 5, + &ver, + G_BIG_ENDIAN, + error)) + return FALSE; + version = fu_version_from_uint16(ver, FWUPD_VERSION_FORMAT_BCD); + + switch (fw_type) { + case FU_WAC_MODULE_FW_TYPE_TOUCH: + module = fu_wac_module_touch_new(fu_device_get_context(FU_DEVICE(self)), + 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); + fu_device_set_version_raw(FU_DEVICE(module), ver); + break; + case FU_WAC_MODULE_FW_TYPE_BLUETOOTH: + module = fu_wac_module_bluetooth_new(fu_device_get_context(FU_DEVICE(self)), + 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); + fu_device_set_version_raw(FU_DEVICE(module), ver); + break; + case FU_WAC_MODULE_FW_TYPE_BLUETOOTH_ID6: + module = + fu_wac_module_bluetooth_id6_new(fu_device_get_context(FU_DEVICE(self)), + usb_device); + name = g_strdup_printf("%s [Bluetooth Module (ID6)]", + 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); + fu_device_set_version_raw(FU_DEVICE(module), ver); + break; + case FU_WAC_MODULE_FW_TYPE_MAIN: + fu_device_set_version(FU_DEVICE(self), version); + fu_device_set_version_raw(FU_DEVICE(self), ver); + break; + default: + g_warning("unknown submodule type 0x%0x", fw_type); + break; + } + } + return TRUE; +} + +static gboolean +fu_wac_device_setup(FuDevice *device, GError **error) +{ + FuWacDevice *self = FU_WAC_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_wac_device_parent_class)->setup(device, error)) + 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 */ + return TRUE; +} + +static gboolean +fu_wac_device_close(FuDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(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); + + /* FuUsbDevice->close */ + return FU_DEVICE_CLASS(fu_wac_device_parent_class)->close(device, error); +} + +static gboolean +fu_wac_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return fu_wac_device_update_reset(FU_WAC_DEVICE(device), error); +} + +static void +fu_wac_device_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +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_protocol(FU_DEVICE(self), "com.wacom.usb"); + fu_device_add_icon(FU_DEVICE(self), "input-tablet"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); + fu_device_set_install_duration(FU_DEVICE(self), 10); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_WAC_FIRMWARE); +} + +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); + 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_device->cleanup = fu_wac_device_cleanup; + klass_device->close = fu_wac_device_close; + klass_device->set_progress = fu_wac_device_set_progress; +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-device.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-device.h new file mode 100644 index 0000000000000000000000000000000000000000..e0900546aa07b5709c66d4c4e25fdaf773a44989 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-device.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_WAC_DEVICE (fu_wac_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWacDevice, fu_wac_device, FU, WAC_DEVICE, FuHidDevice) + +gboolean +fu_wac_device_get_feature_report(FuWacDevice *self, + guint8 *buf, + gsize bufsz, + FuHidDeviceFlags flags, + GError **error); +gboolean +fu_wac_device_set_feature_report(FuWacDevice *self, + guint8 *buf, + gsize bufsz, + FuHidDeviceFlags flags, + GError **error); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-firmware.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..757803a55543e725c65ac77840d866de4c3dbf6f --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-firmware.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-wac-firmware.h" + +struct _FuWacFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuWacFirmware, fu_wac_firmware, FU_TYPE_FIRMWARE) + +#define FU_WAC_FIRMWARE_TOKENS_MAX 100000 /* lines */ +#define FU_WAC_FIRMWARE_SECTIONS_MAX 10 + +typedef struct { + guint32 addr; + guint32 sz; + guint32 prog_start_addr; +} FuFirmwareWacHeaderRecord; + +typedef struct { + FuFirmware *firmware; + FwupdInstallFlags flags; + GPtrArray *header_infos; + GString *image_buffer; + guint8 images_cnt; +} FuWacFirmwareTokenHelper; + +static gboolean +fu_wac_firmware_tokenize_cb(GString *token, guint token_idx, gpointer user_data, GError **error) +{ + FuWacFirmwareTokenHelper *helper = (FuWacFirmwareTokenHelper *)user_data; + g_autofree gchar *cmd = NULL; + + /* sanity check */ + if (token_idx > FU_WAC_FIRMWARE_TOKENS_MAX) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "file has too many lines"); + return FALSE; + } + + /* remove WIN32 line endings */ + g_strdelimit(token->str, "\r\x1a", '\0'); + token->len = strlen(token->str); + + /* ignore blank lines */ + cmd = g_strndup(token->str, 2); + if (g_strcmp0(cmd, "") == 0) + return TRUE; + + /* Wacom-specific metadata */ + if (g_strcmp0(cmd, "WA") == 0) { + /* header info record */ + if (token->len > 3 && memcmp(token->str + 2, "COM", 3) == 0) { + guint8 header_image_cnt = 0; + if (token->len != 40) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid header, got %" G_GSIZE_FORMAT " bytes", + token->len); + return FALSE; + } + + /* sanity check */ + if (helper->header_infos->len > FU_WAC_FIRMWARE_SECTIONS_MAX) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "too many metadata sections: %u", + helper->header_infos->len); + return FALSE; + } + if (!fu_firmware_strparse_uint4_safe(token->str, + token->len, + 5, + &header_image_cnt, + error)) + return FALSE; + for (guint j = 0; j < header_image_cnt; j++) { + g_autofree FuFirmwareWacHeaderRecord *hdr = NULL; + hdr = g_new0(FuFirmwareWacHeaderRecord, 1); + if (!fu_firmware_strparse_uint32_safe(token->str, + token->len, + (j * 16) + 6, + &hdr->addr, + error)) + return FALSE; + if (!fu_firmware_strparse_uint32_safe(token->str, + token->len, + (j * 16) + 14, + &hdr->sz, + error)) + return FALSE; + g_debug("header_fw%u_addr: 0x%x", j, hdr->addr); + g_debug("header_fw%u_sz: 0x%x", j, hdr->sz); + g_ptr_array_add(helper->header_infos, g_steal_pointer(&hdr)); + } + return TRUE; + } + + /* firmware headline record */ + if (token->len == 13) { + FuFirmwareWacHeaderRecord *hdr; + guint8 idx = 0; + if (!fu_firmware_strparse_uint4_safe(token->str, + token->len, + 2, + &idx, + error)) + return FALSE; + if (idx == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "headline %u invalid", + idx); + return FALSE; + } + if (idx > helper->header_infos->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "headline %u exceeds header count %u", + idx, + helper->header_infos->len); + return FALSE; + } + if (idx - 1 != helper->images_cnt) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "headline %u is not in sorted order", + idx); + return FALSE; + } + hdr = g_ptr_array_index(helper->header_infos, idx - 1); + if (!fu_firmware_strparse_uint32_safe(token->str, + token->len, + 3, + &hdr->prog_start_addr, + error)) + return FALSE; + 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); + return TRUE; + } + + g_debug("unknown Wacom-specific metadata"); + return TRUE; + } + + /* start */ + if (g_strcmp0(cmd, "S0") == 0) { + if (helper->image_buffer->len > 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "duplicate S0 without S7"); + return FALSE; + } + g_string_append_printf(helper->image_buffer, "%s\n", token->str); + return TRUE; + } + + /* these are things we want to include in the image */ + if (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 (helper->image_buffer->len == 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "%s without S0", cmd); + return FALSE; + } + g_string_append_printf(helper->image_buffer, "%s\n", token->str); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid SREC command on line %u: %s", + token_idx + 1, + cmd); + return FALSE; + } + + /* end */ + if (g_strcmp0(cmd, "S7") == 0) { + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) fw_srec = NULL; + g_autoptr(FuFirmware) firmware_srec = fu_srec_firmware_new(); + g_autoptr(FuFirmware) img = fu_firmware_new(); + FuFirmwareWacHeaderRecord *hdr; + + /* get the correct relocated start address */ + if (helper->images_cnt >= helper->header_infos->len) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s without header", + cmd); + return FALSE; + } + hdr = g_ptr_array_index(helper->header_infos, helper->images_cnt); + + if (helper->image_buffer->len == 0) { + 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(helper->image_buffer->str, helper->image_buffer->len); + if (!fu_firmware_parse_full(firmware_srec, + blob, + hdr->addr, + helper->flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + error)) + return FALSE; + fw_srec = fu_firmware_get_bytes(firmware_srec, error); + if (fw_srec == NULL) + return FALSE; + fu_firmware_set_bytes(img, fw_srec); + fu_firmware_set_addr(img, fu_firmware_get_addr(firmware_srec)); + fu_firmware_set_idx(img, helper->images_cnt); + fu_firmware_add_image(helper->firmware, img); + helper->images_cnt++; + + /* clear the image buffer */ + g_string_set_size(helper->image_buffer, 0); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wac_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error) +{ + guint8 magic[5] = {0x0}; + + if (!fu_memcpy_safe(magic, + sizeof(magic), + 0, /* dst */ + g_bytes_get_data(fw, NULL), + g_bytes_get_size(fw), + offset, + sizeof(magic), + error)) { + g_prefix_error(error, "failed to read magic: "); + return FALSE; + } + if (memcmp(magic, "WACOM", sizeof(magic)) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid .wac prefix"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wac_firmware_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GString) image_buffer = g_string_new(NULL); + FuWacFirmwareTokenHelper helper = {.firmware = firmware, + .flags = flags, + .header_infos = header_infos, + .image_buffer = image_buffer, + .images_cnt = 0}; + + /* tokenize */ + if (!fu_strsplit_full((const gchar *)buf + offset, + bufsz - offset, + "\n", + fu_wac_firmware_tokenize_cb, + &helper, + error)) + return FALSE; + + /* verify data is complete */ + if (helper.image_buffer->len > 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "truncated data: no S7"); + return FALSE; + } + + /* ensure this matched the header */ + if (helper.header_infos->len != helper.images_cnt) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "not enough images %u for header count %u", + helper.images_cnt, + header_infos->len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static guint8 +fu_wac_firmware_calc_checksum(GByteArray *buf) +{ + return fu_sum8(buf->data, buf->len) ^ 0xFF; +} + +static GBytes * +fu_wac_firmware_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(GByteArray) buf_hdr = g_byte_array_new(); + + /* fw header */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + fu_byte_array_append_uint32(buf_hdr, fu_firmware_get_addr(img), G_BIG_ENDIAN); + fu_byte_array_append_uint32(buf_hdr, fu_firmware_get_size(img), G_BIG_ENDIAN); + } + g_string_append_printf(str, "WACOM%u", images->len); + for (guint i = 0; i < buf_hdr->len; i++) + g_string_append_printf(str, "%02X", buf_hdr->data[i]); + g_string_append_printf(str, "%02X\n", fu_wac_firmware_calc_checksum(buf_hdr)); + + /* payload */ + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) img_blob = NULL; + g_autoptr(GByteArray) buf_img = g_byte_array_new(); + + /* img header */ + g_string_append_printf(str, "WA%u", (guint)fu_firmware_get_idx(img) + 1); + fu_byte_array_append_uint32(buf_img, fu_firmware_get_addr(img), G_BIG_ENDIAN); + for (guint j = 0; j < buf_img->len; j++) + g_string_append_printf(str, "%02X", buf_img->data[j]); + g_string_append_printf(str, "%02X\n", fu_wac_firmware_calc_checksum(buf_img)); + + /* srec */ + img_blob = fu_firmware_write(img, error); + if (img_blob == NULL) + return NULL; + g_string_append_len(str, + (const gchar *)g_bytes_get_data(img_blob, NULL), + g_bytes_get_size(img_blob)); + } + + /* success */ + return g_string_free_to_bytes(g_steal_pointer(&str)); +} + +static void +fu_wac_firmware_init(FuWacFirmware *self) +{ +} + +static void +fu_wac_firmware_class_init(FuWacFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->check_magic = fu_wac_firmware_check_magic; + klass_firmware->parse = fu_wac_firmware_parse; + klass_firmware->write = fu_wac_firmware_write; +} + +FuFirmware * +fu_wac_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_WAC_FIRMWARE, NULL)); +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-firmware.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-firmware.h new file mode 100644 index 0000000000000000000000000000000000000000..514df7fd4b4f0bbd4320910a035c278535d3fa08 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-firmware.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_WAC_FIRMWARE (fu_wac_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuWacFirmware, fu_wac_firmware, FU, WAC_FIRMWARE, FuFirmware) + +FuFirmware * +fu_wac_firmware_new(void); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c new file mode 100644 index 0000000000000000000000000000000000000000..84a8a315a092c97254cbfb5259618c9a1c284037 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2021 Jason Gerecke + * + * 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-id6.h" + +struct _FuWacModuleBluetoothId6 { + FuWacModule parent_instance; +}; + +G_DEFINE_TYPE(FuWacModuleBluetoothId6, fu_wac_module_bluetooth_id6, FU_TYPE_WAC_MODULE) + +#define FU_WAC_MODULE_BLUETOOTH_ID6_CRC8_POLYNOMIAL 0x31 +#define FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ 256 +#define FU_WAC_MODULE_BLUETOOTH_ID6_START_NORMAL 0x00 +#define FU_WAC_MODULE_BLUETOOTH_ID6_START_FULLERASE 0xFE + +typedef struct { + guint8 preamble[2]; + guint8 crc; + guint8 addr[4]; + guint8 cdata[FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ]; +} FuWacModuleBluetoothId6BlockData; + +static guint8 +fu_wac_module_bluetooth_id6_reverse_bits(guint8 value) +{ + guint8 reverse = 0; + + for (gint i = 0; i < 8; i++) { + reverse <<= 1; + reverse |= (value & 0x01); + value >>= 1; + } + return reverse; +} + +static guint8 +fu_wac_module_bluetooth_id6_calculate_crc(const guint8 *data, gsize sz) +{ + guint8 crc = ~fu_crc8_full(data, sz, 0x00, FU_WAC_MODULE_BLUETOOTH_ID6_CRC8_POLYNOMIAL); + return fu_wac_module_bluetooth_id6_reverse_bits(crc); +} + +static GPtrArray * +fu_wac_module_bluetooth_id6_parse_blocks(const guint8 *data, gsize sz, GError **error) +{ + const guint8 preamble[] = {0x00, 0x01}; + GPtrArray *blocks = g_ptr_array_new_with_free_func(g_free); + for (guint addr = 0x0; addr < sz; addr += FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ) { + g_autofree FuWacModuleBluetoothId6BlockData *bd = NULL; + gsize cdata_sz = FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ; + + bd = g_new0(FuWacModuleBluetoothId6BlockData, 1); + memcpy(bd->preamble, preamble, sizeof(preamble)); + bd->addr[0] = 0; + bd->addr[1] = 0; + bd->addr[2] = 0; + bd->addr[3] = 0; + memset(bd->cdata, 0xff, FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ); + + /* if file is not in multiples of payload size */ + if (addr + FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ >= sz) + cdata_sz = sz - addr; + if (!fu_memcpy_safe(bd->cdata, + sizeof(bd->cdata), + 0x0, /* dst */ + data, + sz, + addr, /* src */ + cdata_sz, + error)) + return NULL; + bd->crc = fu_wac_module_bluetooth_id6_calculate_crc( + bd->cdata, + FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ); + g_ptr_array_add(blocks, g_steal_pointer(&bd)); + } + return blocks; +} + +static gboolean +fu_wac_module_bluetooth_id6_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWacModule *self = FU_WAC_MODULE(device); + const guint8 *data; + gsize len = 0; + const guint8 buf_start[] = {FU_WAC_MODULE_BLUETOOTH_ID6_START_NORMAL}; + g_autoptr(GPtrArray) blocks = NULL; + g_autoptr(GBytes) blob_start = g_bytes_new_static(buf_start, 1); + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 8, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 59, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 33, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* build each data packet */ + data = g_bytes_get_data(fw, &len); + blocks = fu_wac_module_bluetooth_id6_parse_blocks(data, len, error); + if (blocks == NULL) + return FALSE; + + /* start, which will erase the module */ + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_START, + blob_start, + fu_progress_get_child(progress), + FU_WAC_MODULE_ERASE_TIMEOUT, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* data */ + for (guint i = 0; i < blocks->len; i++) { + FuWacModuleBluetoothId6BlockData *bd = g_ptr_array_index(blocks, i); + guint8 buf[FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ + 7]; + g_autoptr(GBytes) blob_chunk = NULL; + + /* build data packet */ + memset(buf, 0xff, sizeof(buf)); + memcpy(&buf[0], bd->preamble, 2); + buf[2] = bd->crc; + memcpy(&buf[3], bd->addr, 4); + memcpy(&buf[7], 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, + fu_progress_get_child(progress), + FU_WAC_MODULE_WRITE_TIMEOUT, + error)) + return FALSE; + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + blocks->len); + } + fu_progress_step_done(progress); + + /* end */ + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_END, + NULL, + fu_progress_get_child(progress), + FU_WAC_MODULE_COMMIT_TIMEOUT, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static void +fu_wac_module_bluetooth_id6_init(FuWacModuleBluetoothId6 *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_install_duration(FU_DEVICE(self), 120); +} + +static void +fu_wac_module_bluetooth_id6_class_init(FuWacModuleBluetoothId6Class *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_wac_module_bluetooth_id6_write_firmware; +} + +FuWacModule * +fu_wac_module_bluetooth_id6_new(FuContext *context, GUsbDevice *usb_device) +{ + FuWacModule *module = NULL; + module = g_object_new(FU_TYPE_WAC_MODULE_BLUETOOTH_ID6, + "context", + context, + "usb-device", + usb_device, + "fw-type", + FU_WAC_MODULE_FW_TYPE_BLUETOOTH_ID6, + NULL); + return module; +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth-id6.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth-id6.h new file mode 100644 index 0000000000000000000000000000000000000000..39e0ca42466490254b7219f4267f5d0bc899ccbd --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth-id6.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2021 Jason Gerecke + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wac-module.h" + +#define FU_TYPE_WAC_MODULE_BLUETOOTH_ID6 (fu_wac_module_bluetooth_id6_get_type()) +G_DECLARE_FINAL_TYPE(FuWacModuleBluetoothId6, + fu_wac_module_bluetooth_id6, + FU, + WAC_MODULE_BLUETOOTH_ID6, + FuWacModule) + +FuWacModule * +fu_wac_module_bluetooth_id6_new(FuContext *context, GUsbDevice *usb_device); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth.c new file mode 100644 index 0000000000000000000000000000000000000000..d839d1292b7f17fcfa32ca4467a5cd276b8d48c2 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth.c @@ -0,0 +1,227 @@ +/* + * 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, + GError **error) +{ + 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) { + g_autofree FuWacModuleBluetoothBlockData *bd = NULL; + 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; + if (!fu_memcpy_safe(bd->cdata, + sizeof(bd->cdata), + 0x0, /* dst */ + data, + sz, + addr, /* src */ + cdata_sz, + error)) + return NULL; + bd->crc = fu_wac_module_bluetooth_calculate_crc(bd->cdata, + FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ); + g_ptr_array_add(blocks, g_steal_pointer(&bd)); + } + return blocks; +} + +static gboolean +fu_wac_module_bluetooth_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWacModule *self = FU_WAC_MODULE(device); + const guint8 *data; + gsize len = 0; + const guint8 buf_start[] = {0x00}; + g_autoptr(GPtrArray) blocks = NULL; + g_autoptr(GBytes) blob_start = g_bytes_new_static(buf_start, 1); + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 79, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* build each data packet */ + data = g_bytes_get_data(fw, &len); + blocks = fu_wac_module_bluetooth_parse_blocks(data, len, TRUE, error); + if (blocks == NULL) + return FALSE; + + /* start, which will erase the module */ + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_START, + blob_start, + fu_progress_get_child(progress), + FU_WAC_MODULE_ERASE_TIMEOUT, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* data */ + 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, + fu_progress_get_child(progress), + FU_WAC_MODULE_WRITE_TIMEOUT, + error)) + return FALSE; + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + blocks->len); + } + fu_progress_step_done(progress); + + /* end */ + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_END, + NULL, + fu_progress_get_child(progress), + FU_WAC_MODULE_FINISH_TIMEOUT, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + 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(FuContext *context, GUsbDevice *usb_device) +{ + FuWacModule *module = NULL; + module = g_object_new(FU_TYPE_WAC_MODULE_BLUETOOTH, + "context", + context, + "usb-device", + usb_device, + "fw-type", + FU_WAC_MODULE_FW_TYPE_BLUETOOTH, + NULL); + return module; +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth.h new file mode 100644 index 0000000000000000000000000000000000000000..16a52877e1e0c070d8312ffb598c2242e512f08c --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-bluetooth.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wac-module.h" + +#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(FuContext *context, GUsbDevice *usb_device); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-touch.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-touch.c new file mode 100644 index 0000000000000000000000000000000000000000..47b9964b50d71a73fd8da6ac2f1deeadce595d4c --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-touch.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-wac-device.h" +#include "fu-wac-module-touch.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, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWacModule *self = FU_WAC_MODULE(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, NULL); + + g_debug("using element at addr 0x%0x", (guint)fu_firmware_get_addr(firmware)); + + /* build each data packet */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + chunks = fu_chunk_array_new_from_bytes(fw, + fu_firmware_get_addr(firmware), + 0x0, /* page_sz */ + 128); /* packet_sz */ + + /* start, which will erase the module */ + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_START, + NULL, + fu_progress_get_child(progress), + FU_WAC_MODULE_ERASE_TIMEOUT, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* data */ + 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] = fu_chunk_get_idx(chk) + 1; + fu_memwrite_uint32(&buf[2], fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + buf[6] = 0x10; /* no idea! */ + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x07, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + blob_chunk = g_bytes_new(buf, sizeof(buf)); + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_DATA, + blob_chunk, + fu_progress_get_child(progress), + FU_WAC_MODULE_WRITE_TIMEOUT, + error)) { + g_prefix_error(error, "failed to write block %u: ", fu_chunk_get_idx(chk)); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + i + 1, + chunks->len); + } + fu_progress_step_done(progress); + + /* end */ + if (!fu_wac_module_set_feature(self, + FU_WAC_MODULE_COMMAND_END, + NULL, + fu_progress_get_child(progress), + FU_WAC_MODULE_FINISH_TIMEOUT, + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + 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); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_IHEX_FIRMWARE); +} + +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(FuContext *context, GUsbDevice *usb_device) +{ + FuWacModule *module = NULL; + module = g_object_new(FU_TYPE_WAC_MODULE_TOUCH, + "context", + context, + "usb-device", + usb_device, + "fw-type", + FU_WAC_MODULE_FW_TYPE_TOUCH, + NULL); + return module; +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-touch.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-touch.h new file mode 100644 index 0000000000000000000000000000000000000000..963863223fa9afcd022b2f976a5d27f1c9da5e86 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module-touch.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-wac-module.h" + +#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(FuContext *context, GUsbDevice *usb_device); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module.c new file mode 100644 index 0000000000000000000000000000000000000000..920407def600ec2a84bd30e2d883aa19814e619c --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module.c @@ -0,0 +1,403 @@ +/* + * 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.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_BLUETOOTH_ID6) + return "bluetooth-id6"; + 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, guint idt, GString *str) +{ + FuWacModule *self = FU_WAC_MODULE(device); + FuWacModulePrivate *priv = GET_PRIVATE(self); + fu_string_append(str, idt, "FwType", fu_wac_module_fw_type_to_string(priv->fw_type)); + fu_string_append(str, idt, "Status", fu_wac_module_status_to_string(priv->status)); + fu_string_append(str, idt, "Command", 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_HID_DEVICE_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 and status */ + if (priv->command != buf[2] || priv->status != buf[3]) { + priv->command = buf[2]; + priv->status = buf[3]; + if (g_getenv("FWUPD_WACOM_VERBOSE") != NULL) { + 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 */ + FuProgress *progress, + guint busy_timeout, + 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 = busy_timeout * 100; + guint8 buf[] = {[0] = FU_WAC_REPORT_ID_MODULE, + [1] = priv->fw_type, + [2] = command, + [3 ... FU_WAC_PACKET_LEN - 1] = 0xff}; + + /* sanity check */ + g_return_val_if_fail(FU_IS_WAC_MODULE(self), FALSE); + g_return_val_if_fail(FU_IS_WAC_DEVICE(parent_device), FALSE); + + /* verify the size of the blob */ + if (blob != NULL) { + data = g_bytes_get_data(blob, &len); + if (!fu_memcpy_safe(buf, + sizeof(buf), + 0x03, /* dst */ + data, + len, + 0x0, /* src */ + len, + error)) { + g_prefix_error(error, "Submodule blob larger than buffer: "); + return FALSE; + } + } + + /* tell the daemon the current status */ + switch (command) { + case FU_WAC_MODULE_COMMAND_START: + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); + break; + case FU_WAC_MODULE_COMMAND_DATA: + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + break; + case FU_WAC_MODULE_COMMAND_END: + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_VERIFY); + break; + default: + break; + } + + /* send to hardware */ + if (!fu_wac_device_set_feature_report(parent_device, + buf, + len + 3, + FU_HID_DEVICE_FLAG_ALLOW_TRUNC, + error)) { + g_prefix_error(error, "failed to set module feature: "); + return FALSE; + } + + /* 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 gboolean +fu_wac_module_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *parent = fu_device_get_parent(device); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + return fu_device_cleanup(parent, progress, flags, error); +} + +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) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.wacom.usb"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +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_add_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_set_progress(FuDevice *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_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; + + /** + * FuWacModule:usb-device: + * + * The parent USB device to use. + */ + 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); + + /** + * FuWacModule:fw-type: + * + * The firmware kind. + */ + 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; + klass_device->cleanup = fu_wac_module_cleanup; + klass_device->set_progress = fu_wac_module_set_progress; +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module.h new file mode 100644 index 0000000000000000000000000000000000000000..0e9bf4e5b924ec0f2b4fde6ab44dbf67926c6ec8 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-module.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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_BLUETOOTH_ID6 0x06 +#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 + +#define FU_WAC_MODULE_WRITE_TIMEOUT 1 +#define FU_WAC_MODULE_ERASE_TIMEOUT 15 +#define FU_WAC_MODULE_FINISH_TIMEOUT 1 +#define FU_WAC_MODULE_COMMIT_TIMEOUT 80 + +gboolean +fu_wac_module_set_feature(FuWacModule *self, + guint8 command, + GBytes *blob, + FuProgress *progress, + guint busy_timeout, + GError **error); diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-plugin.c b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..8f1f351d2af4f347042f81fbfca6064db07854f8 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-plugin.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-wac-android-device.h" +#include "fu-wac-device.h" +#include "fu-wac-firmware.h" +#include "fu-wac-plugin.h" + +struct _FuWacPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuWacPlugin, fu_wac_plugin, FU_TYPE_PLUGIN) + +static gboolean +fu_wac_plugin_write_firmware(FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + 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, progress, flags, error); +} + +static void +fu_wac_plugin_init(FuWacPlugin *self) +{ +} + +static void +fu_wac_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_set_name(plugin, "wacom_usb"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WAC_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WAC_ANDROID_DEVICE); + fu_plugin_add_firmware_gtype(plugin, "wacom", FU_TYPE_WAC_FIRMWARE); +} + +static void +fu_wac_plugin_class_init(FuWacPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = fu_wac_plugin_constructed; + plugin_class->write_firmware = fu_wac_plugin_write_firmware; +} diff --git a/fwupd-1.8.6/plugins/wacom-usb/fu-wac-plugin.h b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6c05693d492cc712f5fca0286cd33205cc983567 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/fu-wac-plugin.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuWacPlugin, fu_wac_plugin, FU, WAC_PLUGIN, FuPlugin) diff --git a/fwupd-1.8.6/plugins/wacom-usb/meson.build b/fwupd-1.8.6/plugins/wacom-usb/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c142e32222d054ec4257efce2ce92442f0ead2ff --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/meson.build @@ -0,0 +1,48 @@ +if gusb.found() +cargs = ['-DG_LOG_DOMAIN="FuPluginWacomUsb"'] + +plugin_quirks += files('wacom-usb.quirk') +plugin_builtin_wac = static_library('fu_plugin_wac', + sources: [ + 'fu-wac-common.c', + 'fu-wac-android-device.c', + 'fu-wac-device.c', + 'fu-wac-firmware.c', # fuzzing + 'fu-wac-module.c', + 'fu-wac-module-bluetooth.c', + 'fu-wac-module-bluetooth-id6.c', + 'fu-wac-module-touch.c', + 'fu-wac-plugin.c', + ], + include_directories: plugin_incdirs, + c_args: cargs, + dependencies: plugin_deps, + link_with: plugin_libs, +) +plugin_builtins += plugin_builtin_wac + +if get_option('tests') + install_data(['tests/wacom-usb.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests')) + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'wacom-usb-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_wac, + ], + c_args: cargs, + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + ) + test('wacom-usb-self-test', e, env: env) # added to installed-tests +endif +endif diff --git a/fwupd-1.8.6/plugins/wacom-usb/tests/wacom-usb.bin b/fwupd-1.8.6/plugins/wacom-usb/tests/wacom-usb.bin new file mode 100644 index 0000000000000000000000000000000000000000..d5186aeaba8bb1a036cdf88ab0dfa66dcf5c2dca --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/tests/wacom-usb.bin @@ -0,0 +1,11 @@ +WACOM2080080000000000B080400000000000B55 +WA10800800077 +S0030000FC +S3100800800068656C6C6F20776F726C640B +S5030001FB +S70500000000FA +WA208040000F3 +S0030000FC +S3100804000068656C6C6F20776F726C6487 +S5030001FB +S70500000000FA diff --git a/fwupd-1.8.6/plugins/wacom-usb/tests/wacom-usb.builder.xml b/fwupd-1.8.6/plugins/wacom-usb/tests/wacom-usb.builder.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ca3d8f17bf24421b10b6100fce1886c2ad87a57 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/tests/wacom-usb.builder.xml @@ -0,0 +1,12 @@ + + + 0x0 + 0x8008000 + aGVsbG8gd29ybGQ= + + + 0x1 + 0x8040000 + aGVsbG8gd29ybGQ= + + diff --git a/fwupd-1.8.6/plugins/wacom-usb/wacom-usb.quirk b/fwupd-1.8.6/plugins/wacom-usb/wacom-usb.quirk new file mode 100644 index 0000000000000000000000000000000000000000..08cab6b6ee7a1faf77d56505a8d67b7f916b0bf5 --- /dev/null +++ b/fwupd-1.8.6/plugins/wacom-usb/wacom-usb.quirk @@ -0,0 +1,83 @@ + +# Intuos Pro medium (2nd-gen USB) [PTH-660] +[USB\VID_056A&PID_0357] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version + +# Intuos Pro large (2nd-gen USB) [PTH-860] +[USB\VID_056A&PID_0358] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version + +# Intuos S 3rd-gen (USB) [CTL-4100] +[USB\VID_056A&PID_0374] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version,no-serial-number + +# Intuos S 3rd-gen (USB) [CTL-4100 - Android Mode] +[USB\VID_2D1F&PID_0374] +Plugin = wacom_usb +GType = FuWacAndroidDevice + +# Intuos M 3rd-gen (USB) [CTL-6100] +[USB\VID_056A&PID_0375] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version,no-serial-number + +# Intuos M 3rd-gen (USB) [CTL-6100 - Android Mode] +[USB\VID_2D1F&PID_0375] +Plugin = wacom_usb +GType = FuWacAndroidDevice + +# Intuos BT S 3rd-gen (USB) [CTL-4100WL] +[USB\VID_056A&PID_0376] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version,no-serial-number + +# Intuos BT S 3rd-gen (USB) [CTL-4100WL - Android Mode] +[USB\VID_2D1F&PID_0376] +Plugin = wacom_usb +GType = FuWacAndroidDevice + +# Intuos BT M 3rd-gen (USB) [CTL-6100WL] +[USB\VID_056A&PID_0378] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version,no-serial-number + +# Intuos BT M 3rd-gen (USB) [CTL-6100WL - Android Mode] +[USB\VID_2D1F&PID_0378] +Plugin = wacom_usb +GType = FuWacAndroidDevice + +# Intuos Pro Small (2nd-gen USB) [PTH-460] +[USB\VID_056A&PID_0392] +GType = FuWacDevice +Plugin = wacom_usb + +# Intuos BT S 3rd-gen, Rev 2 (USB) [CTL-4100WL] +[USB\VID_056A&PID_03C5] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version,no-serial-number + +# Intuos BT S 3rd-gen, Rev 2 (USB) [CTL-4100WL - Android Mode] +[USB\VID_2D1F&PID_03C5] +Plugin = wacom_usb +GType = FuWacAndroidDevice + +# Intuos BT M 3rd-gen, Rev 2 (USB) [CTL-6100WL] +[USB\VID_056A&PID_03C7] +Plugin = wacom_usb +GType = FuWacDevice +Flags = use-runtime-version,no-serial-number + +# Intuos BT M 3rd-gen, Rev 2 (USB) [CTL-6100WL - Android Mode] +[USB\VID_2D1F&PID_03C7] +Plugin = wacom_usb +GType = FuWacAndroidDevice diff --git a/fwupd-1.8.6/po/.gitignore b/fwupd-1.8.6/po/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e3a8c5db201cf5e125c2f28effd93a079200d8d0 --- /dev/null +++ b/fwupd-1.8.6/po/.gitignore @@ -0,0 +1 @@ +*.pot diff --git a/fwupd-1.8.6/po/LINGUAS b/fwupd-1.8.6/po/LINGUAS new file mode 100644 index 0000000000000000000000000000000000000000..56aab3eaa86319463423cb0d2e2fc1e42dcc1559 --- /dev/null +++ b/fwupd-1.8.6/po/LINGUAS @@ -0,0 +1,41 @@ +af +ast +ca +cs +da +de +en_GB +eo +es +eu +fi +fr +fur +gl +he +hi +hr +hu +id +it +ja +ka +kk +ko +ky +lt +nl +oc +pa +pl +pt +pt_BR +ru +si +sk +sr +sv +tr +uk +zh_CN +zh_TW diff --git a/fwupd-1.8.6/po/POTFILES.in b/fwupd-1.8.6/po/POTFILES.in new file mode 100644 index 0000000000000000000000000000000000000000..fb1a1d115cfdcac8a60d83436008669dff18d6ab --- /dev/null +++ b/fwupd-1.8.6/po/POTFILES.in @@ -0,0 +1,21 @@ +data/remotes.d/lvfs.metainfo.xml +data/remotes.d/lvfs-testing.metainfo.xml +libfwupdplugin/fu-bios-settings.c +libfwupdplugin/tests/bios-attrs/dell-xps13-9310/strings.txt +policy/org.freedesktop.fwupd.policy.in +plugins/dfu/fu-dfu-tool.c +plugins/tpm/fu-tpm-eventlog.c +plugins/uefi-capsule/fu-uefi-capsule-plugin.c +plugins/uefi-capsule/fu-uefi-tool.c +plugins/uefi-dbx/fu-dbxtool.c +src/fu-debug.c +src/fu-engine-helper.c +src/fu-main.c +src/fu-offline.c +src/fu-progressbar.c +src/fu-remote-list.c +src/fu-security-attr-common.c +src/fu-tool.c +src/fu-util.c +src/fu-util-bios-setting.c +src/fu-util-common.c diff --git a/fwupd-1.8.6/po/POTFILES.skip b/fwupd-1.8.6/po/POTFILES.skip new file mode 100644 index 0000000000000000000000000000000000000000..aff3fbe05c0abdde740396fb88e21aca7fb72107 --- /dev/null +++ b/fwupd-1.8.6/po/POTFILES.skip @@ -0,0 +1 @@ +data/org.freedesktop.fwupd.metainfo.xml diff --git a/fwupd-1.8.6/po/af.po b/fwupd-1.8.6/po/af.po new file mode 100644 index 0000000000000000000000000000000000000000..58e2ab090f9a909df4d20879577bb4464bd1c9e5 --- /dev/null +++ b/fwupd-1.8.6/po/af.po @@ -0,0 +1,303 @@ +# 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" + +#. TRANSLATORS: 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: 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: 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 the daemon state changes +msgid "Changed" +msgstr "Verander" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolesom" + +#. 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" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Pak tans uit…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +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:" + +#. TRANSLATORS: success +#. 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: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Geaktiveer" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Vee tans uit…" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Lêernaam" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Lêernaamhandtekening" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Gevind" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. 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…" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +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: Suffix: the HSI result +msgid "OK" +msgstr "Goed" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Wagwoord" + +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: reading from the flash chips +msgid "Reading…" +msgstr "Lees tans…" + +#. 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: 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: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Skeduleer tans…" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Wys toestelle wat nie bygewerk kan word nie" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Opsomming" + +msgid "Target" +msgstr "Teiken" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipe" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Onbekend" + +#. 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: 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: the detected version number of the dbx +msgid "Version" +msgstr "Weergawe" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Wag tans…" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Skryf tans…" diff --git a/fwupd-1.8.6/po/ast.po b/fwupd-1.8.6/po/ast.po new file mode 100644 index 0000000000000000000000000000000000000000..c9181d05cead2a87289002b10e713976c3fe1d9f --- /dev/null +++ b/fwupd-1.8.6/po/ast.po @@ -0,0 +1,80 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# enolp , 2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: ast\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Amestóse" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Encaboxóse" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Camudóse" + +#. TRANSLATORS: this is the encryption method used when writing +msgid "Cipher" +msgstr "Cifráu" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Alcontróse" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "ID" + +msgid "Mode" +msgstr "Mou" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +#. TRANSLATORS: section header for the release name +msgid "Name" +msgstr "Nome" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protocolu" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Rexón" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Desanicióse" + +#. TRANSLATORS: serial number, e.g. '00012345' +msgid "Serial" +msgstr "Serial" + +#. TRANSLATORS: device state, i.e. appIDLE +msgid "State" +msgstr "Estáu" + +#. TRANSLATORS: probably not run as root... +#. TRANSLATORS: device has failed to report status +#. TRANSLATORS: device status, e.g. "OK" +msgid "Status" +msgstr "Estáu" + +#. TRANSLATORS: transfer size in bytes +msgid "Transfer Size" +msgstr "Tamañu de tresferencia" diff --git a/fwupd-1.8.6/po/ca.po b/fwupd-1.8.6/po/ca.po new file mode 100644 index 0000000000000000000000000000000000000000..2d5fb6155cdb9e4907fca562292f68d31d22ae21 --- /dev/null +++ b/fwupd-1.8.6/po/ca.po @@ -0,0 +1,3079 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Antoni Bella Pérez , 2017-2022 +# Robert Antoni Buj i Gelonch , 2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: ca\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: 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: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Actualitza el BMC %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s Actualització de la bateria" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s Actualitza el microprogramari de la CPU" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s Actualització de la càmera" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Actualització de la configuració %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "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 "Actualització del dispositiu %s" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Actualitza la pantalla %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "Actualització del controlador %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 incrustat %s" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "Actualització de la unitat flaix %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s Actualització del teclat" + +#. 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: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s Actualització del ratolí" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Actualització del controlador de xarxa %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "Actualització de l'SSD %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Actualització del controlador d'emmagatzematge %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 "Actualització del sistema %s" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s Actualització del TPM" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Actualització del controlador Thunderbolt %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s Actualització del ratolí tàctil" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s actualització del receptor USB" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Actualització de %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s i tots els dispositius connectats poden no ser usables en actualitzar." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s ha aparegut: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s ha canviat: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s ha desaparegut: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s actualment no es pot actualitzar" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Mode de fabricació %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s haurà d'estar connectat durant tota l'actualització per a evitar danys." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s haurà de romandre connectat a una font d'alimentació durant tota l'actualització per a evitar danys." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Superposa %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s versió %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Versió %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dia" +msgstr[1] "%u dies" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] " %u dispositiu té una actualització de microprogramari disponible." +msgstr[1] "%u dispositius tenen una actualització de microprogramari disponible." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u dispositiu no és la millor configuració coneguda." +msgstr[1] "%u dispositius no són la millor configuració coneguda." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hora" +msgstr[1] "%u hores" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "Està admès %u dispositiu local" +msgstr[1] "Estan admesos %u dispositius locals" + +#. 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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (llindar %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsolet)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "Un PCR en el TPM ara té un valor no vàlid" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "Protecció de reinjecció de microprogramari d'AMD" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "Protecció d'escriptura del microprogramari d'AMD" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "Protecció de versió antiga d'AMD" + +#. TRANSLATORS: longer description +msgid "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "La protecció de versió antiga d'AMD evita que el programari del dispositiu es desactualitzi a una versió anterior amb problemes de seguretat." + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Acció requerida:" + +#. 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: the age of the metadata +msgid "Age" +msgstr "Antiguitat" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Voleu acceptar i habilitar el remot?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Àlies per a «%s»" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Tots els PCR en el TPM ara són vàlids" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Tots els PCR en el TPM són vàlids" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Tots els dispositius del mateix tipus s'actualitzaran al mateix temps" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permet tornar a la versió anterior del microprogramari" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permetre tornar a instal·lar les versions existents del microprogramari" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Permet canviar de branca de microprogramari" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Branca alternativa" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplica les actualitzacions de microprogramari" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplica una actualització fins i tot quan no se us aconselli." + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplica els fitxers d'actualització." + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "S'està aplicant l'actualització…" + +#. 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: stop nagging the user +msgid "Ask again next time?" +msgstr "Demana-m'ho de nou la pròxima vegada?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Adjunta al mode microprogramari." + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "S'està autenticant..." + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Es requereixen els detalls d'autenticació" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Es requereix autenticació per a desactualitzar el microprogramari en un dispositiu extraïble" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Es requereix autenticació per a desactualitzar el microprogramari en aquesta màquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Cal autenticació per a modificar les opcions de configuració del BIOS" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +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 read BIOS settings" +msgstr "Cal autenticació per a llegir les opcions de configuració del BIOS" + +#. 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 microprogramari" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Es requereix autenticació per a desbloquejar un dispositiu" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Es requereix autenticació per a actualitzar el microprogramari en un dispositiu extraïble" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Es requereix autenticació per a actualitzar el microprogramari en aquesta màquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Es requereix autenticació per a actualitzar les sumes de verificació emmagatzemades pels dispositius" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Informa automàticament" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Voleu pujar-lo automàticament cada vegada?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "Actualitzacions del BIOS lliurades mitjançant LVFS o Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "CONSTRUCTOR_XML NOM_FITXER_DEST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Bateria" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincula el controlador actual." + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Fitxers del microprogramari bloquejat:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Versió bloquejada" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Microprogramari bloquejat:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Bloqueja la instal·lació d'un microprogramari específic." + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versió del carregador d'arrencada" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Branca" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Construeix un fitxer de microprogramari." + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancel·la" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "S'ha cancel·lat" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "No s'ha pogut aplicar perquè l'actualització de la dbx ja s'ha aplicat." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "No s'han pogut aplicar les actualitzacions en els suports en viu" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "S'ha canviat" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Comprova que la suma criptogràfica coincideix amb el microprogramari." + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Suma de verificació" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Trieu una branca:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Trieu un dispositiu:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Trieu un tipus de microprogramari:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Trieu un alliberament:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Trieu un volum:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose the ESP:" +msgstr "Trieu l'ESP:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Esborra els resultats de l'última actualització." + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "No s'ha trobat cap ordre" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Sostinguda per la comunitat" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Canvi de configuració suggerit" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "La configuració només la pot llegir l'administrador del sistema" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Converteix un fitxer de microprogramari." + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Creat" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Crítica" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Està disponible la verificació de la suma criptogràfica" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Valor actual" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versió actual" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ID_DISPOSITIU|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilitat DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opcions per a la depuració" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "S'està descomprimint..." + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descripció" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Separa del mode carregador d'arrencada." + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detalls" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Cal desviar-se de la millor configuració coneguda?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Etiquetes del dispositiu" + +#. 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:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "L'alimentació de la bateria del dispositiu és massa baixa" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr " L'energia de la bateria és massa baixa (%u%%, requereix %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "El dispositiu pot recuperar fallades de flaix" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "El dispositiu no es pot emprar mentre la tapa està tancada" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "S'ha canviat el dispositiu:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "És obligatori el microprogramari del dispositiu per a comprovar la versió" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "El dispositiu està emulat" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "El dispositiu està bloquejat" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "El dispositiu és necessari per a instal·lar totes les versions subministrades" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "El dispositiu és inabastable" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "El dispositiu no és accessible o es troba fora de l'abast" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "El dispositiu es podrà usar durant tota l'actualització" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "El dispositiu està esperant que s'apliqui l'actualització" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "S'ha eliminat el dispositiu:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "El dispositiu requereix que es connecti l'alimentació" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Es proporcionen actualitzacions de programari de dispositiu per a aquest dispositiu." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Etapes d'actualització del dispositiu" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "El dispositiu admet el canvi a una branca diferent de microprogramari" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Mètode d'actualització del dispositiu" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Cal activar l'actualització del dispositiu" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "El dispositiu farà una còpia de seguretat del microprogramari abans d'instal·lar" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "El dispositiu no tornarà a aparèixer un cop finalitzada l'actualització" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Els dispositius que s'han actualitzat correctament:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Els dispositius que no s'han actualitzat correctament:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositius sense actualitzacions de microprogramari disponibles:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositius amb la versió més recent del microprogramari disponible:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "No s'ha trobat cap dispositiu amb GUID coincident" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Inhabilitada" + +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "No comprovar si hi ha un historial sense informar" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "No comprovar si la baixada des del remot ha d'estar habilitada" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "No verifiquis ni demanis reiniciar després d'actualitzar" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "No incloure el prefix de domini del registre" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "No incloure el prefix de la marca de temps" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "No realitzar les comprovacions de seguretat al dispositiu" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "No demanis pels dispositius" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "No escriure a la base de dades de l'historial" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Enteneu les conseqüències de canviar la branca del microprogramari?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Voleu inhabilitar aquesta característica per a futures actualitzacions?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Voleu refrescar ara aquest remot?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Voleu pujar automàticament els informes per a futures actualitzacions?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "No demanis autenticació (pot mostrar menys detalls)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Fet!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Voleu desactualitzar %s des de %s a %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Desactualitza el microprogramari en un dispositiu." + +#. 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'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: command description +msgid "Download a file" +msgstr "Descarrega un fitxer" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "S'està descarregant..." + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Bolca les dades al SMBIOS des d'un fitxer." + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durada" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "L'ESP especificat no era vàlid" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Cada sistema haurà de tenir proves per a assegurar la seguretat del microprogramari." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Amfitrió emulat" + +msgid "Enable" +msgstr "Habilita" + +#. 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Voleu habilitar el remot nou?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Voleu habilitar aquest remot?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Habilitat" + +msgid "Enabled fwupdate debugging" +msgstr "La depuració del «fwupdate» està habilitada" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Activat si hi ha cap coincidència de maquinari" + +#. 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." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Encriptada" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM encriptada" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "La RAM encriptada fa impossible que es pugui llegir la informació que s'emmagatzema a la memòria del dispositiu si es treu i s'accedeix al xip de memòria." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Final de la vida" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Enumeració" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Esborra tot l'historial de les actualitzacions de microprogramari." + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "S'està esborrant..." + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Surt després d'un petit retard" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Surt una vegada s'hagi carregat el motor" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Exporta a XML una estructura de fitxers del microprogramari" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extreu un blob de microprogramari en imatges." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FITXER" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FITXER [ID_DISPOSITIU|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NOM_FITXER" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "NOM_FITXER CERTIFICAT CLAU_PRIVADA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NOM_FITXER NOM_ALT_DISPOSITIU|ID_ALT_DISPOSITIU" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NOM_FITXER NOM_ALT_DISPOSITIU|ID_ALT_DISPOSITIU [NOM_ALT_IMATGE|NOM_ALT_IMTAGE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NOM_FITXER ID_DISPOSITIU" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "NOM_FITXER DESPLAÇAMENT DADES [TIPUS_MICROPROGRAMARI]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NOM_FITXER [ID_DISPOSITIU|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NOM_FITXER [TIPUS_MICROPROGRAMARI]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NOM_FITXER_ORIG NOM_FITXER_DEST [TIPUS_MICROPROGRAMARI_ORIG] [TIPUS_MICROPROGRAMARI_DEST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "NOM_DE_FITXER|SUMA_DE_VERIFICACIÓ_1[,SUMA_DE_VERIFICACIÓ_2][,SUMA_DE_VERIFICACIÓ_3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Ha fallat" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Ha fallat en aplicar l'actualització" + +#. TRANSLATORS: error message for Windows +#, c-format +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Ha fallat en connectar al servei de Windows, assegureu-vos que s'està executant." + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Ha fallat en connectar amb el dimoni" + +#. 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Ha fallat en carregar una dbx local" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "No s'han pogut carregar les peculiaritats" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Ha fallat en carregar una dbx del sistema" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Ha fallat en bloquejar" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Ha fallat en analitzar els arguments" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Ha fallat en analitzar el fitxer" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Ha fallat en analitzar les etiquetes per a --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Ha fallat en analitzar una dbx local" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Ha fallat en validar el contingut ESP" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Fals" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nom del fitxer" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Signatura del nom del fitxer" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Nom del fitxer d'origen" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "El nom del fitxer és obligatori" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra amb un conjunt d'etiquetes del dispositiu amb un prefix «~» per a excloure, p. ex., «internal,~needs-reboot»" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Certificació del microprogramari" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "La certificació del microprogramari comprova el programari del dispositiu mitjançant una còpia de referència, per a assegurar-se que no ha estat canviat." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Descriptor de microprogramari del BIOS" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "El descriptor de microprogramari del BIOS protegeix que la memòria de microprogramari del dispositiu no es mantingui alterada." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Regió de microprogramari del BIOS" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "La regió de microprogramari del BIOS protegeix que la memòria de microprogramari del dispositiu no es mantingui alterada." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI base del microprogramari" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Servei de D-Bus per a l'actualització de microprogramari" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Dimoni per a l'actualització de microprogramari" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Verificació de l'actualitzador de microprogramari" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "La verificació de l'actualitzador de microprogramari comprova que el programari emprat per a l'actualització no ha estat alterat." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Actualitzacions de microprogramari" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilitat per al microprogramari" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Protecció d'escriptura del microprogramari" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Bloca la protecció d'escriptura del microprogramari" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "La protecció d'escriptura del microprogramari protegeix que la memòria de microprogramari del dispositiu sigui manipulada." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Certificació del microprogramari" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "El microprogramari ja està bloquejat" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "El microprogramari ja no està bloquejat" + +#. 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] "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." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Última actualització de les metadades de microprogramari: fa %s. Empreu «--force» per a actualitzar de nou." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Actualitzacions del microprogramari" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Actualitzacions de microprogramari inhabilitades: executeu «fwupdmgr unlock» per a habilitar-les" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Etiquetes" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Força l'acció relaxant algunes comprovacions del temps d'execució" + +msgid "Force the action ignoring all warnings" +msgstr "Força l'acció ignorant tots els avisos" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "S'ha trobat" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "S'ha detectat un encriptatge de tot el disc" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Els secrets d'encriptatge de tot el disc es poden invalidar en actualitzar" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Plataforma fusionada" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Plataforma fusionada" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "El GUID" +msgstr[1] "Els GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Obtén les opcions de configuració del BIOS" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Obté totes les etiquetes admeses per «fwupd»." + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obté tots els dispositius que admeten actualitzacions de microprogramari." + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obté 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." + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Obté els remots configurats." + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obté els atributs de seguretat de l'amfitrió." + +#. 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 blocked firmware" +msgstr "Obté la llista del microprogramari bloquejat." + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Obté la llista d'actualitzacions per al maquinari connectat." + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Obté els alliberaments per a un dispositiu." + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Obté els resultats de l'última actualització." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "FITXER-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "El maquinari està esperant a ser endollat" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Alta" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Esdeveniments de seguretat a l'amfitrió" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "L'ID de seguretat de l'amfitrió (HSI) no està admès" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de seguretat de l'amfitrió:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "Protecció IOMMU" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "La protecció IOMMU impedeix que els dispositius connectats accedeixin a parts no autoritzades de la memòria del sistema." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Està inhabilitada la protecció IOMMU de dispositius" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Està habilitada la protecció IOMMU de dispositius" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Està ociós..." + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora les comprovacions estrictes de SSL quan es baixin els fitxers" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignora les falles de la suma de verificació del microprogramari" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignora les falles de discrepància del maquinari del microprogramari" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignora les comprovacions de seguretat de validació" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "S'ignoren les comprovacions estrictes de SSL, per a fer-ho automàticament en el futur, exporteu DISABLE_SSL_STRICT en el vostre entorn" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Durada de la instal·lació" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instal·la un blob de microprogramari en un dispositiu." + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instal·la un fitxer de microprogramari en aquest maquinari." + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Instal·la un microprogramari específic en un dispositiu, tots els dispositius possibles també s'instal·laran una vegada que coincideixi el CAB" + +msgid "Install old version of signed system firmware" +msgstr "Instal·la la versió antiga del microprogramari signat per al sistema" + +msgid "Install old version of unsigned system firmware" +msgstr "Instal·la la versió antiga del microprogramari sense signar per al sistema" + +msgid "Install signed device firmware" +msgstr "Instal·la microprogramari signat per al dispositiu" + +msgid "Install signed system firmware" +msgstr "Instal·la microprogramari signat per al sistema" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instal·la primer al dispositiu pare" + +msgid "Install unsigned device firmware" +msgstr "Instal·la microprogramari sense signar per al dispositiu" + +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à instal·lant a %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "La instal·lació d'aquesta actualització també pot anul·lar qualsevol garantia del dispositiu." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Sencer" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Protegit amb l'ACM per al BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Protegit amb l'ACM per al BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Política d'error del BootGuard d'Intel" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "La política d'error del BootGuard d'Intel assegura que el dispositiu no continuarà l'inici si s'ha alterat el seu programari de dispositiu." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Fusible del BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fusible OTP del BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Arrencada verificada per BootGuard d'Intel" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política d'error del BootGuard d'Intel" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "El BootGuard d'Intel impedeix que el programari de dispositiu sense autorització funcioni quan s'inicia el dispositiu." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Arrencada verificada per al BootGuard d'Intel" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "CET d'Intel actiu" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "CET d'Intel habilitat" + +#. TRANSLATORS: longer description +msgid "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "La tecnologia de compliment del flux de control d'Intel detecta i impedeix certs mètodes per a executar programari maliciós en el dispositiu." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Mode de fabricació del motor de gestió d'Intel" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Superposa el motor de gestió d'Intel" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "El fet de superposar el motor de gestió d'Intel desactivarà les comprovacions per a la manipulació de programari del dispositiu." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Versió del motor de gestió d'Intel" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "SMAP d'Intel" + +#. TRANSLATORS: longer description +msgid "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "La prevenció de l'accés al mode de supervisor d'Intel assegura que les parts crítiques de la memòria del dispositiu no són accedides per programes menys segurs." + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Dispositiu intern" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "No vàlid" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Arguments no vàlids" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Està desactualitzada" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Està en el mode carregador d'arrencada" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Està actualitzada" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problema" +msgstr[1] "Problemes" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "CLAU,VALOR" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "El nucli ja no està contaminat" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "El nucli està contaminat" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Està inhabilitat el bloqueig de parts del nucli" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Està habilitat el bloqueig de parts del nucli" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr " Anell de claus" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "UBICACIÓ" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Última modificació" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Manca menys d'un minut" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Llicència" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Bloqueig del nucli Linux" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "El mode de bloqueig del nucli Linux evita que els comptes d'administrador (root) accedir i canviar a parts crítiques del programari del sistema." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "L'intercanvi del nucli de Linux desa temporalment informació en el disc mentre treballeu. Si la informació no està protegida, algú podria accedir-hi si obté el disc." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Verificació del nucli Linux" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "La verificació del nucli de Linux s'assegura que el programari crític del sistema no ha estat alterat. L'ús de controladors de dispositiu que no es proporcionen amb el sistema pot evitar que aquesta característica de seguretat funcioni correctament." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Intercanvi de Linux" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Servei de microprogramari del proveïdor Linux (microprogramari estable)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Servei de microprogramari del proveïdor Linux (microprogramari en proves)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Nucli Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Bloqueig del nucli Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Intercanvi de Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Llista les entrades en la dbx." + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Llista les actualitzacions de microprogramari compatibles" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Llista els tipus de microprogramari disponibles." + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Llista els fitxers en l'ESP." + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "S'està carregant..." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Blocada" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Baixa" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Mode de fabricació MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Superposa el MEI" + +msgid "MEI version" +msgstr "Versió del MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Habilita manualment connectors específics" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "El mode de fabricació s'empra quan el dispositiu s'està fabricant i les característiques de seguretat encara no estan habilitades." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Longitud màxima" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Valor màxim" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Mitjana" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Signatura de les metadades" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI de les metadades" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Les metadades es poden obtenir des del servei de microprogramari del proveïdor Linux." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versió mínima" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Longitud mínima" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Valor mínim" + +#. 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" + +#. 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." + +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 per als esdeveniments." + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Munta l'ESP." + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "Nota: aquest programa només pot funcionar correctament com a «root»" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Requereix un reinici després de la instal·lació" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Cal reiniciar" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Requereix aturar després de la instal·lació" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Versió nova" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "No s'ha especificat cap acció!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "No hi ha cap desactualització per a %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "No s'ha trobat cap ID de microprogramari" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "No hi ha cap llançament disponible" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Actualment, no hi ha cap remot habilitat, de manera que no hi ha metadades disponibles." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "No hi ha cap remot disponible" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Dispositius no actualitzables" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "No hi ha cap actualització disponible" + +msgid "No updates available for remaining devices" +msgstr "No hi ha cap actualització disponible per als dispositius restants" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "No s'han aplicat les actualitzacions" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "No aprovada" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "No s'han proporcionat prou dades per a fer un càlcul HSI." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "No s'ha trobat" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "No admesa" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "D'acord" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "Bé!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostra només un únic valor de PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Empra només IPFS quan es baixin els fitxers" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Només es permeten actualitzacions de la versió" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Sortida en el format JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Preferència sobre el camí ESP predeterminat" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "CAMÍ" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Analitza i mostra els detalls sobre un fitxer de microprogramari." + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "S'està analitzant l'actualització de la dbx..." + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "S'està analitzant la dbx del sistema..." + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Contrasenya" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Apedaça un blob de microprogramari amb un desplaçament conegut" + +msgid "Payload" +msgstr "Part útil" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Pendent" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentatge completat" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Voleu realitzar l'operació?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Depuració de la plataforma" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "La depuració de la plataforma permet que es desactivin les característiques de seguretat del dispositiu. Això només ho han d'emprar els fabricants de maquinari." + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Abans de continuar, assegureu-vos que teniu la clau de recuperació del volum." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Introduïu un número del 0 al %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Manquen les dependències del connector" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Valors possibles" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "Protecció DMA durant la prearrencada" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protecció DMA durant la prearrencada" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "La protecció DMA durant la prearrencada està desactivada" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "La protecció DMA durant la prearrencada està activada" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "La protecció de DMA en la prearrencada impedeix que els dispositius accedeixin a la memòria del sistema després de connectar-los a l'ordinador." + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Versió anterior" + +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" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problemes" + +msgid "Proceed with upload?" +msgstr "Voleu continuar amb l'enviament?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Comprovacions de seguretat del processador" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Propietari" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consulta el suport per a l'actualització del microprogramari" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ID_REMOT" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ID_REMOT CLAU VALOR" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Només lectura" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Llegeix un blob de microprogramari des d'un dispositiu." + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Llegeix un nicroprogramari des d'un dispositiu" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Llegeix el microprogramari des del dispositiu a un fitxer." + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Llegeix el microprogramari des d'una partició a un fitxer." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Llegint des de %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "S'està llegint..." + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "S'està tornant a arrencar..." + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Refresca les metadades des del servidor remot." + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Voleu reinstal·lar %s a %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Torna a instal·lar el microprogramari actual en el dispositiu." + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Torna a instal·lar el microprogramari a un dispositiu." + +#. 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 "S'està reinstal·lant %s amb %s... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Branca de llançament" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Etiquetes de llançament" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID de llançament" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID remot" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Substitueix les dades en un fitxer de microprogramari existent." + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI de l'informe" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Informat al servidor remot" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Sol·licitud cancel·lada" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "No s'ha trobat el sistema de fitxers «efivarfs» requerit" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "No s'ha trobat el maquinari requerit" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Requereix un carregador d'arrencada" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Requereix connexió a Internet" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Voleu reiniciar ara?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Voleu reiniciar 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..." + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Es recuperen les opcions de configuració del BIOS. Si no es transmeten arguments, es retornarà tota la configuració" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Retorna tots els ID del maquinari de la màquina." + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Protecció de versió antiga" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Per a més informació, executeu «fwupdmgr get-upgrades»." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Per a completar aquesta acció, executeu «fwupdmgr sync-bkc»." + +#. 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" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Per a veure executa sense «%s»" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "El nucli executat és massa antic" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufix d'execució" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "VALOR DE CONFIGURACIÓ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "CONFIG_1 VALOR_1 [CONFIG_2] [VALOR_2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Descriptor del BIOS per a l'SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Regió del BIOS per a l'SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloca l'SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Protecció de repetició per a l'SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escriu l'SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Protecció d'escriptura per a l'SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSISTEMA CONTROLADOR [ID_DISPOSITIU|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Desa un fitxer que permet la generació dels ID de maquinari" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Augment escalar" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Planifica la instal·lació per al següent reinici quan sigui possible" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planificació..." + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Està inhabilitada l'arrencada segura" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Està habilitada l'arrencada segura" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Per a més detalls, vegeu %s." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Per a més informació, vegeu %s." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Dispositiu seleccionat" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volum seleccionat" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Número de sèrie" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Estableix la configuració «%s» del BIOS a «%s»." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Estableix una configuració del BIOS" + +msgid "Set one or more BIOS settings" +msgstr "Estableix una o més opcions de configuració del BIOS" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Estableix l'indicador de depuració durant l'actualització" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Estableix una o més opcions de configuració del BIOS" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Estableix la llista de microprogramari aprovat" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Tipus de configuració" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "La configuració s'aplicarà després de reiniciar el sistema" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Comparteix l'historial de microprogramari amb els desenvolupadors." + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Mostra tots els resultats" + +#. TRANSLATORS: command line option +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 domains" +msgstr "Mostra 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 informació de depuració addicional." + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostra l'historial de les actualitzacions de microprogramari." + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostra la informació detallada del connector" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra la versió calculada de la dbx." + +#. 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 "Voleu aturar-lo ara?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Signa un microprogramari amb una clau nova" + +msgid "Sign data using the client certificate" +msgstr "Signa les dades emprant el certificat del client" + +#. TRANSLATORS: command description +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 enviades amb el certificat del client" + +msgid "Signature" +msgstr "Signatura" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Part útil signada" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Mida" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Alguns dels secrets de la plataforma es poden invalidar en actualitzar aquest microprogramari." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Origen" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifiqueu el proveïdor/ID del producte del dispositiu DFU" + +#. TRANSLATORS: command line option +msgid "Specify a filename to use to save backend events" +msgstr "Especifiqueu un nom de fitxer que s'usarà per a desar els esdeveniments del dorsal" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especifica el fitxer de base de dades dbx." + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifiqueu el nombre de bytes per a la transferència USB" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Cadena" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Amb èxit" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "S'han activat correctament tots els dispositius" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "El remot s'ha inhabilitat correctament" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "S'han baixat les metadades noves amb èxit:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "S'ha habilitat i refrescat el remot amb èxit" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "El remot s'ha habilitat correctament" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "El microprogramari s'ha instal·lat correctament" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "S'ha modificat amb èxit el valor de la configuració" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "El remot ha estat modificat amb èxit" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "S'han refrescat manualment amb èxit les metadades" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "S'han actualitzat correctament les sumes de verificació del dispositiu" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "S'ha enviat %u informe correctament" +msgstr[1] "S'han enviat %u informes correctament" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "S'han verificat correctament les sumes de verificació del dispositiu" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Resum" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Admesa" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "CPU admesa" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Admès en el servidor remot" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Suspèn a inactiu" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Suspèn a la RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspèn a inactiu permet que el dispositiu s'adormi ràpidament per a estalviar energia. Si bé el dispositiu ha estat suspès, es podria treure físicament la memòria i accedir a la informació." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspèn a la RAM permet que el dispositiu s'adormi ràpidament per a estalviar energia. Si bé el dispositiu ha estat suspès, es podria treure físicament la memòria i accedir a la informació." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspensió a inactiu" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspensió a la RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Voleu canviar la branca des de %s a %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Canvia la branca de microprogramari en el dispositiu." + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Sincronitza les versions de microprogramari a la millor configuració coneguda de l'amfitrió" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "L'energia del sistema és massa baixa per a realitzar l'actualització" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr " L'energia del sistema és massa baixa per a realitzar l'actualització (%u%%, requereix %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "El sistema requereix una font d'alimentació externa" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "El TPM (mòdul de plataforma de confiança) és un xip d'ordinador que detecta quan s'han manipulat els components de maquinari." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrucció PCR0 del TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "La reconstrucció PCR0 del TPM no és vàlida" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "La reconstrucció PCR0 del TPM ara és vàlida" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "Configuració de la plataforma TPM" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "Reconstrucció TPM" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Els PCR buits en el TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM versió 2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Etiqueta" +msgstr[1] "Etiquetes" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Contaminada" + +msgid "Target" +msgstr "Objectiu" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Prova un dispositiu emprant un manifest JSON" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "El motor de gestió d'Intel controla els components del dispositiu i necessita tenir una versió recent per a evitar problemes de seguretat." + +#. 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: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "La configuració de la plataforma TPM (mòdul de plataforma de confiança) s'empra per a comprovar si s'ha modificat el procés d'inici del dispositiu." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "La reconstrucció del TPM (mòdul de plataforma de confiança) s'empra per a comprovar si s'ha modificat el procés d'inici del dispositiu." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "El PCR0 del TPM difereix de la reconstrucció." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "La clau de la plataforma UEFI s'empra per a determinar si el programari del dispositiu prové d'una font de confiança." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "El dimoni ha carregat codi de tercers i ja no és admès pels desenvolupadors originals!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "La versió del dispositiu no coincideix: obtinguda %s, esperada %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "El microprogramari des de %s no és proporcionat per %s, el proveïdor de maquinari." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "El rellotge del sistema no s'ha configurat correctament i pot fallar la descàrrega de fitxers." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "El proveïdor no ha subministrat les notes de llançament." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Hi ha dispositius amb problemes:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "No hi ha cap fitxer de microprogramari bloquejat." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "No hi ha cap microprogramari aprovat." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Aquest dispositiu es revertirà a %s quan es realitzi l'ordre %s." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Aquest microprogramari és proporcionat pels membres de la comunitat LVFS i no és proporcionada (o admesa) pel proveïdor de maquinari original." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Aquest paquet no ha estat validat, i per això podria no funcionar adequadament." + +#. 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é microprogramari que no està embargat, però encara l'ha de provar el proveïdor del maquinari. Haureu d'assegurar-vos que teniu una manera de desactualitzar manualment el microprogramari si l'actualització del microprogramari no funciona." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "Aquest sistema no admet la configuració del microprogramari" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Aquest sistema té problemes d'execució HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Aquest sistema té un nivell de seguretat HSI baix." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Aquesta eina permet a un administrador aplicar les actualitzacions des de la dbx UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Aquesta eina permet a un administrador depurar el funcionament d'«UpdateCapsule»." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Aquesta eina permet que un administrador consulti i controli el dimoni «fwupd», el qual li permetrà realitzar accions com instal·lar o degradar la versió de microprogramari." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Aquesta eina permet a un administrador usar els connectors de «fwupd» sense estar instal·lats en el sistema amfitrió." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "Aquesta eina pot canviar la configuració del BIOS «%s» des de «%s» a «%s» de forma automàtica, però només estarà activa una vegada es reiniciï l'ordinador." + +#. 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: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Aquesta eina llegirà i analitzarà el registre d'esdeveniments TPM des del microprogramari del sistema." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Per a ignorar aquest avís, useu --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Fallada transitòria" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Cert" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Metadades de confiança" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Part útil de confiança" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipus" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "No s'ha detectat o configurat la partició ESP de la UEFI" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitat per al microprogramari UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "Clau de la plataforma UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "Arrencada segura UEFI" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "L'arrencada segura UEFI evita que es carregui programari maliciós quan s'inicia el dispositiu." + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Les actualitzacions de la càpsula UEFI no estan disponibles o habilitades a la configuració del microprogramari" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitat dbx UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "El microprogramari UEFI no es pot actualitzar en el mode BIOS heretat" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Clau de la plataforma UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Arrancada segura de la UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "No s'ha pogut connectar amb el servei" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "No es pot trobar atribut" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Desvincula el controlador actual." + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Microprogramari desbloquejat:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Desbloqueja la instal·lació d'un microprogramari específic." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "No està encriptada" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Desconegut" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositiu desconegut" + +msgid "Unlock the device to allow access" +msgstr "Desbloqueja el dispositiu per a permetre l'accés" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desblocada" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Desbloqueja el dispositiu per a accedir al microprogramari." + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Desmunta l'ESP." + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "No estableixis l'indicador de depuració durant l'actualització" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Part útil sense signar" + +#. 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: Suffix: the HSI result +msgid "Untainted" +msgstr "Sense contaminar" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Actualitzable" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Error en actualitzar" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Imatge d'actualització" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Missatge de l'actualització" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Estat en actualitzar" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Que falli en actualitzar és un problema conegut, visiteu aquest URL per a obtenir més informació:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Voleu actualitzar ara?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "L'actualització requereix un reinici" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Actualitza la suma criptogràfica emmagatzemada amb el contingut actual de la ROM." + +msgid "Update the stored device verification information" +msgstr "Actualitza la informació de verificació dels dispositius emmagatzemats" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Actualitza les metadades emmagatzemades amb el contingut actual." + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Actualitza tots els dispositius especificats a la versió més recent del microprogramari o tots els dispositius sense especificar" + +msgid "Updating" +msgstr "S'està actualitzant" + +#. 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 "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: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Voleu actualitzar %s des de %s a %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Voleu enviar l'informe ara?" + +#. 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 "L'enviament dels informes de microprogramari ajudarà als proveïdors de maquinari a identificar amb rapidesa les actualitzacions fallides i satisfactòries sobre els dispositius reals." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgència" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Empreu «fwupdmgr --help» per a l'ajuda" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Empreu «fwupdtool --help» per a l'ajuda" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa les etiquetes peculiars en instal·lar el microprogramari" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "S'ha notificat a l'usuari" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nom d'usuari" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Vàlid" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "S'està validant el contingut ESP..." + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variant" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Venedor" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "S'està verificant..." + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versió" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "AVÍS:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "S'està esperant…" + +#. 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." + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Escriu el microprogramari des d'un fitxer a dins d'una partició." + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Fitxer d'escriptura:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "S'està escrivint..." + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Heu d'assegurar-vos que esteu còmode restablint la configuració des de la configuració del microprogramari del sistema, ja que aquest canvi pot fer que el sistema no arrenqui a Linux o que causi alguna altra inestabilitat en el sistema." + +#. 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." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "El vostre maquinari pot danyar-se amb aquest microprogramari, i la instal·lació d'aquesta versió pot anul·lar qualsevol garantia amb %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "El vostre sistema està establert a la BKC de %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[SUMA_DE_VERIFICACIÓ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ID_DISPOSITIU|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID_DISPOSITIU|GUID] [BRANCA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ID_DISPOSITIU|GUID] [VERSIÓ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FITXER SIGNATURA_FITXER ID_REMOT]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NOM_FITXER_1] [NOM_FITXER_2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[CONFIG_1] [ CONFIG_2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[CONFIG_1] [CONFIG_2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[FITXER-HWIDS-SMBIOS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "predeterminat" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Utilitat per al registre d'esdeveniments TPM del fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Connectors del «fwupd»" diff --git a/fwupd-1.8.6/po/cs.po b/fwupd-1.8.6/po/cs.po new file mode 100644 index 0000000000000000000000000000000000000000..df04c358331e7e383d5ed6df0793918f34145a04 --- /dev/null +++ b/fwupd-1.8.6/po/cs.po @@ -0,0 +1,3079 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Ascii Wolf , 2017,2019-2020 +# Ascii Wolf , 2017 +# Marek Černocký , 2016,2018 +# Pavel Borecki , 2020-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: cs\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" + +#. TRANSLATORS: 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ývají %.0f minuty" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Aktualizace pro zařízení pro vestavěnou zprávu (BMC) %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Aktualizace pro akumulátor %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Aktualizace mikrokódu pro procesor %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Aktualizace pro kameru %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Aktualizace nastavení pro %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Aktualizace spotřebitelského ME v %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 "Aktualizace pro řadič %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 "Aktualizace podnikového ME v %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 "Aktualizace pro zařízení %s" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Aktualizace pro displej %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s aktualizace disku" + +#. 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 "Aktualizace vestavěného řadiče (EC) v %s" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "%s aktualizace flash disku" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Aktualizace pro klávesnici %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 "Aktualizace ME v %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Aktualizace pro myš %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Aktualizace síťové karty %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s aktualizace SSD" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Aktualizace pro %s řadič úložiště" + +#. 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 "Celková aktualizace pro %s" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Aktualizace pro TPM %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Aktualizace pro řadič Thunderbolt v %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Aktualizace pro touchpad %s" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "Aktualizace USB přijímače %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 "Aktualizace pro %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s a k němu připojená zařízení nemusí být v průběhu aktualizace použitelná." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s se objevilo: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s se změnilo: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s zmizelo: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s v tuto chvíli není možné aktualizovat" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "režim pro výrobce v %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "Je třeba, aby %s zůstalo v průběhu aktualizace připojené, jinak hrozí poškození." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "Je třeba, aby %s bylo po dobu aktualizace připojené ke zdroji napájení z elektrické sítě, jinak hrozí poškození." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "hw přepnutí %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s verze %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Verze %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u den" +msgstr[1] "%u dny" +msgstr[2] "%u dnů" +msgstr[3] "%u dny" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "Je k dispozici novější firmware pro %u zařízení." +msgstr[1] "Jsou k dispozici novější firmware pro %u zařízení." +msgstr[2] "Jsou k dispozici novější firmware pro %u zařízení." +msgstr[3] "Jsou k dispozici novější firmware pro %u zařízení." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "Firmware na %u zařízení není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." +msgstr[1] "Firmware na %u zařízeních není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." +msgstr[2] "Firmware na %u zařízeních není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." +msgstr[3] "Firmware na %u zařízeních není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hodina" +msgstr[1] "%u hodiny" +msgstr[2] "%u hodin" +msgstr[3] "%u hodiny" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u zařízení v počítači podporováno" +msgstr[1] "%u zařízení v počítači podporovány" +msgstr[2] "%u zařízení v počítači podporováno" +msgstr[3] "%u zařízení v počítači podporovány" + +#. 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 minuty" + +#. 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 sekundy" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (dolní hranice %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(zastaralé)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "PCR registr v TPM má nyní neplatnou hodnotu" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "AMD – zamezení znovupoužití dříve použitého" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD – zamezení zápisu firmware" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "AMD – zamezení instalace starší verze" + +#. TRANSLATORS: longer description +msgid "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "AMD – zamezení instalace starší verze brání přechodu na starší verzi software zařízení (mohly by obsahovat už známé problémy v zabezpečení)." + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Vyžadovaná akce:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktivovat zařízení" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktivovat čekající zařízení" + +msgid "Activate the new firmware on the device" +msgstr "Aktivovat nový firmware na zařízení" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktivace aktualizovaného firmwaru" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktivuje se aktualizace firmware pro" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +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 +msgid "Alias to %s" +msgstr "Alternativa (alias) pro %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Všechny PCR registry jsou nyní platné" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Všechny PCR registry jsou platné" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Veškerá zařízení stejného typu budu aktualizována naráz" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Povolit návrat ke starší verzi firmwaru" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Povolit přeinstalaci stávající verze firmwaru" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Umožnit přepnutí varianty firmware" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Alternativní větev" + +#. 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 počítače." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Některá z aktualizací vyžaduje pro dokončení vypnutí počítače." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Na všechny dotazy odpovědět ano" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Provést aktualizace firmwaru" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Provést aktualizaci i když to není doporučeno" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Použít soubory s aktualizací" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Provádění aktualizace…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Schválený firmware:" +msgstr[1] "Schválené firmwary:" +msgstr[2] "Schválených firmwarů:" +msgstr[3] "Schválené firmwary:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Ptát se i příště?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Napojit do režimu firmwaru" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Ověřování se…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Je zapotřebí podrobností k ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "K návratu na starší verzi firmwaru na výměnném zařízení je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "K návratu na starší verzi firmwaru na tomto počítači je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Pro změnu BIOS nastavení je třeba se ověřit" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Ke změně nastaveného vzdáleného zdroje, který se používá pro aktualizace firmwarů, je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Ke změně nastavení procesu služby je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "Pro načtení nastavení BIOS je třeba se ověřit" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Pro nastavení seznamu schválených firmwarů je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Pro podepsání dat klientským certifikátem je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Pro přepnutí na novou verzi firmwaru je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Pro odemknutí zařízení je požadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "K aktualizaci firmwaru na výměnném zařízení je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "K aktualizaci firmwaru na tomto počítači je vyžadováno ověření se" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "K aktualizaci uložených kontrolních součtů pro zařízení je vyžadováno ověření se" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatické odesílání hlášení" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Pokaždé automaticky odeslat?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "Aktualizace BIOS doručené prostřednictvím LVFS nebo Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "XML-PRO-SESTAVENÍ CÍLOVÝ-SOUBOR" + +msgid "BYTES" +msgstr "BAJTŮ" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Akumulátor/baterie" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Napojit nový ovladač jádra" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blokované soubory s firmware:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Blokovaná verze" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokuje se firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Zablokovat instalaci konkrétního firmwaru" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Verze zavaděče firmware" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Větev" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Sestavit soubor s firmware" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Storno" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Zrušeno" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Tuto aktualizaci dbx už není možné použít, protože už je používána." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Aktualizace není možné provést při provozování systému z instalačního média (live)" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Změněno" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Zkontrolovat zda se kryptografický otisk shoduje s firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolní součet" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Zvolte větev firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Vyberte zařízení:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Zvolte typ firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Vyberte verzi:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Zvolte svazek:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Smazat výsledky z poslední aktualizace" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Příkaz nenalezen" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Podporováno komunitou" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Doporučena změna nastavení" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Převést soubor s firmware" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Vytvořeno" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritická" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Je k dispozici kryptografické ověření otisku dat" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Stávající hodnota" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Stávající verze" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "IDENTIF-ZAŘÍZENÍ|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Nástroj pro práci s DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Předvolby ladění" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Rozbalování…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Popis" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Odpojit do režimu zavaděče" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Podrobnosti" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Odchýlit se od osvědčené kombinace verzí firmwarů všech zařízení?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Příznaky zařízení" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Identifikátor zařízení" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Přidáno zařízení:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Akumulátor/baterie v zařízení je příliš vybitý" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Akumulátor/baterie v zařízení je příliš vybitý (%u%%, vyžaduje %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Zařízení se dovede vzpamatovat z nezdaru při zápisu firmware" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Zařízení není možné používat pokud je víko s displejem přiklopené" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Změněno zařízení:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Je třeba, aby firmware zařízení měl kontrolu verze" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Zařízení je emulováno" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Zařízení je uzamčeno" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Zařízení vyžaduje instalaci všech poskytnutých vydání" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Zařízení není dosažitelné" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Zařízení není dosažitelné nebo se nachází mimo dosah signálu" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Zařízení je možné používat i v průběhu aktualizace" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Zařízení čeká na instalaci aktualizací" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Odebráno zařízení:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Zařízení vyžaduje, aby bylo připojeno napájení z elektrické zásuvky" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Pro toto zařízení jsou poskytovány aktualizace softwaru." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Zařízení podporuje rozfázované aktualizace" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Zařízení podporuje přepínání mezi různými větvemi firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Metoda aktualizace zařízení" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "U daného zařízení je třeba nejdříve aktivovat režim aktualizace" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Zařízení provede zálohu stávajícího firmware před instalací nového" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Po dokončení aktualizace se zařízení hned znovu neobjeví" + +#. 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:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Zařízení, pro která nejsou k dispozici aktualizace firmware:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Zařízení s nejnovějšími dostupnými verzemi firmware:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Nenalezena žádná zařízení, která mají odpovídající GUID identifikátory" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Zakázáno" + +msgid "Disabled fwupdate debugging" +msgstr "Vypnuto ladění fwupdate" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Zakázat daný 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 unreported history" +msgstr "Nekontrolovat nenahlášení historie" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Nekontrolovat zda by mělo být zapnuté stahování ze vzdálených zdrojů" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Po aktualizaci nekontrolovat nebo se neptat na restart" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Nepřidávat předponu oblasti zaznamenávání" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Nepřidávat předponu s datem a časem" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Neprovádět přípravné kontroly zařízení" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Nedotazovat se na zařízení" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Nezapisovat do databáze historie" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Rozumíte důsledkům změny větve firmwaru?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Chcete pro budoucí aktualizace tuto funkci vypnout?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Chcete nyní tento vzdálený zdroj znovu načíst?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Chcete pro budoucí aktualizace odesílat hlášení automaticky?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Nepožadovat ověření se (následkem toho může být k dispozici méně podrobností)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Hotovo!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Vrátit se ke starší verzi u %s a to z %s na %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Vrátit firmware v zařízení na starší verzi" + +#. 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 "Vrací se %s z verze %s na starší %s…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Vracení %s na starší verzi…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Stáhnout si soubor" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Stahování…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Vypsat data SMBIOS ze souboru" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Délka instalace" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Určený ESP oddíl není platný" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Každý systém by měl mít testy pro zajištění zabezpečení firmware." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Emulovaný stroj" + +msgid "Enable" +msgstr "Zapnout" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Povolit podporu aktualizace firmwaru na podporovaných systémech" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Povolit nový vzdálený zdroj?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Povolit tento vzdálený zdroj?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Povoleno" + +msgid "Enabled fwupdate debugging" +msgstr "Zapnuto ladění fwupdate" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Zapnuto pokud je odpovídající hardware" + +#. 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 funkce je na vaše vlastní riziko. To znamená, že v případě problémů způsobených aktualizací je třeba, abyste se obrátili přímo na výrobce daného vybavení. Na adresu $OS_RELEASE:BUG_REPORT_URL$ by měly být hlášeny pouze problémy s aktualizačním procesem jako takovým." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Povolení tohoto vzdáleného zdroje je na vaše vlastní riziko." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Šifrováno" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Šifrovaná RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Šifrovaná operační paměť znemožňuje čtení informací z paměti zařízení v případě, že je z něj paměťový modul vyjmut a pokoušeno se z něho číst." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Podpora od výrobce skončila" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Výčet" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Smazat veškerou historii aktualizací firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Mazání…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Skončit po krátké prodlevě" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Skončit po načtení výkonné části" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Exportovat strukturu souboru s firmwarem do XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Rozbalit firmware z blobu do obrazů" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "SOUBOR" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "SOUBOR [IDENTIF-ZAŘÍZENÍ|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "SOUBOR" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "SOUBOR CERTIFIKÁT SOUKROMÝ-KLÍČ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "SOUBOR ALTERN-NÁZEV-ZAŘÍZENÍ|ALTERN-IDENTIF-ZAŘÍZENÍ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "SOUBOR ALTERN-NÁZEV-ZAŘÍZENÍ|ALTENRN-IDENTIF-ZAŘÍZENÍ [ALTERN-NÁZEV-OBRAZU|ALTERN-IDENTIF-OBRAZU]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "SOUBOR IDENTIFIKÁTOR-ZAŘÍZENÍ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "SOUBOR POSUN DATA [TYP-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "SOUBOR [IDENTIFIKÁTOR-ZAŘÍZ|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "SOUBOR [TYP-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "ZDROJOVÝ-SOUBOR CÍLOVÝ-SOUBOR [ZDROJ-TYP-FIRMWARE] [CÍL-TYP-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "SOUBOR|KONTROLNÍ-SOUČET1[,KONTROLNÍ-SOUČET2][,KONTROLNÍ-SOUČET3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Nezdařilo se" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Aktualizaci se nepodařilo provést" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Nepodařilo se spojit s procesem služby" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Nepodařilo se získat čekající zařízení" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Aktualizaci firmware se nepodařilo nainstalovat" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Nepodařilo se načíst místní dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Načtení zvláštních požadavků (quirks) se nezdařilo" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Nepodařilo se načíst systémové dbx" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Nepodařilo se uzamknout" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Zpracování argumentů se nezdařilo" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Soubor se nepodařilo zpracovat" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Nepodařilo se zpracovat příznaky pro --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Nepodařilo se zpracovat místní dbx" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Nepodařilo se restartovat" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Nepodařilo se nastavit režim startovací obrazovky systému" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Nepodařilo se ověřit obsah ESP oddílu" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Nepravda" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Soubor" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Podpis souboru" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Odkud soubor" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Je zapotřebí zadat název souboru" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtrovat podle sady příznaků zařízení (s tím, že předpona ~ slouží pro vynechání), např. 'internal,~needs-reboot' (vestavěné, nevyžadující restart)" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Atestace firmware" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Atestace firmware kontroluje software zařízení pomocí referenční kopie, aby bylo jisté, že nebyl změněn." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "BIOS popisovač firmware" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "Firmware BIOS Descriptor chrání paměť firmware zařízení proti úpravám." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "BIOS oblast firmware" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "Firmware BIOS Region chrání paměť firmware zařízení proti úpravám." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Základ URI pro firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Služba D-Bus pro aktualizaci firmwaru" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Proces služby pro aktualizaci firmwaru" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Ověřování nástroje pro aktualizaci firmware" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Ověřování nástroje pro aktualizaci firmware kontroluje, že se software, použitým pro aktualizaci, nebylo manipulováno." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Aktualizace firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Nástroj pro práci s firmwary" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Ochrana firmware vůči přepsání" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Zámek ochrany firmware vůči přepsání" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Zamezení zápisu firmware chrání paměť firmware zařízení proti úpravám." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Atestace firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware už je blokován" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware ještě není blokován" + +#. 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 o firmwarech nebyla aktualizována už celý %u den a nemusí být proto aktuální." +msgstr[1] "Metadata o firmwarech nebyla aktualizována už celé %u dny a nemusí být proto aktuální." +msgstr[2] "Metadata o firmwarech nebyla aktualizována už celých %u dní a nemusí být proto aktuální." +msgstr[3] "Metadata o firmwarech nebyla aktualizována už celé %u dny a nemusí být proto aktuální." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Metadata ohledně firmwarů byla naposledy aktualizována před pouhými: %s. Pokud přesto chcete znovu načíst, použijte --force." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Aktualizace firmware" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Aktualizace firmware jsou zakázané. Pokud je chcete povolit, spusťte „fwupdmgr unlock“" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Příznaky" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Vynutit akci uvolněním některých kontrol při vykonávání" + +msgid "Force the action ignoring all warnings" +msgstr "Ignorovat veškerá varování a navzdory jim akci vynutit" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Nalezeno" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Zjištěno celodiskové šifrování" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Při aktualizaci mohou být zneplatněna tajemství pro celodiskové šifrování" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Platforma s jednorázovým nastavením (fuse)" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Platforma s jednorázovým nastavením (fuse)" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID identifikátory" +msgstr[2] "GUID identifikátorů" +msgstr[3] "GUID identifikátory" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Získat nastavení BIOS" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Vypsat všechny příznaky zařízení podporované fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Vypsat všechna zařízení podporující aktualizaci firmwaru" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Vypsat všechny povolené zásuvné moduly registrované v systému" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Zjistit podrobnosti o souboru s firmwarem" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Zjistit nastavené vzdálené zdroje" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Zjistit atributy zabezpečení hostitele" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Získat seznam schválených firmwarů" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Získat seznam blokovaných firmwarů" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Vypsat seznam aktualizací pro připojený hardware" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Vypsat vydání pro zařízení" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Vypsat výsledky z poslední aktualizace" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "SOUBOR-S-IDENTIF-HARDWARE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hardware čeká na opětovné připojení" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Vysoká" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Události zabezpečení na hostiteli" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Identifikátor zabezpečení hostitele:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "Ochrana IOMMU" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "Ochrana IOMMU brání připojeným zařízení v přístupu k částem systémové paměti, ke kterým nejsou pověřené k přístupu." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "ochrana IOMMU zařízení vypnuta" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "ochrana IOMMU zařízení zapnuta" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Nečinné…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Při stahování souborů ignorovat některé nedostatky SSL certifikátů" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignorovat nesprávný kontrolní součet firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignorovat neshody firmware s hardware" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignorovat výsledky přípravných kontrol" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorují se striktní SSL kontroly. Pokud chcete, aby se tak příště stalo automaticky, nastavte (a exportujte) proměnnou prostředí DISABLE_SSL_STRICT" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Délka instalace" + +#. 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" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Nainstalovat konkrétní firmware na zařízení, veškerá možná zařízení budou také nainstalována, pokud jim CAB odpovídá" + +msgid "Install old version of signed system firmware" +msgstr "Instalace starší verze podepsaného systémového firmware" + +msgid "Install old version of unsigned system firmware" +msgstr "Instalace starší verze nepodepsaného systémového firmware" + +msgid "Install signed device firmware" +msgstr "Instalace podepsaného firmwaru zařízení" + +msgid "Install signed system firmware" +msgstr "Instalace podepsaného systémového firmwaru" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Nejprve nainstalovat do nadřazeného zařízení" + +msgid "Install unsigned device firmware" +msgstr "Instalace nepodepsaného firmwaru zařízení" + +msgid "Install unsigned system firmware" +msgstr "Instalace nepodepsaného systémového firmwaru" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalace firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Instaluje se aktualizace firmwaru…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instalace na %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Instalace této aktualizace také může zneplatnit jakékoli záruky na zařízení." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Celé číslo" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Chráněno Intel BootGuard ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Chráněno Intel BootGuard ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Co Intel BootGuard udělá v případě chyb" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Zásada chyb Intel BootGuard zajišťuje, že zařízení nebude pokračovat ve startu, pokud se software zařízení bylo manipulováno." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Jednoráz. trvalé přepnutí (fuse) pro Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Jednorázové trvalé přepnutí (OTP fuse) pro Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Ověřované zavádění s Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Co Intel BootGuard udělá v případě chyb" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard brání fungování neověřeného software zařízení, když je zařízení spouštěno." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Ověřované zavádění s Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktivní" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET podporováno" + +#. TRANSLATORS: longer description +msgid "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Technologie Intel Control-Flow Enforcement (vynucování řízení toku) zjišťuje a brání určitým metodám spouštění škodlivého software na zařízení." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Intel Management Engine – režim pro výrobce" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Intel Management Engine – přebití" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "Přebití Intel Management Engine vypíná kontroly manipulace se software zařízení." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Verze Intel Management Engine" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: longer description +msgid "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Intel Supervisor Mode Access Prevention zajišťuje, že kritické části paměti zařízení nejsou přístupné méně bezpečným programům." + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Vestavěné zařízení" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Neplatné" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Neplatné argumenty" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Je starší verzí" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Je v režimu zavaděče firmware" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Je novější verzí" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problém" +msgstr[1] "Problémy" +msgstr[2] "Problémů" +msgstr[3] "Problémy" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "KLÍČ,HODNOTA" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Jádro už není pozměněno" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Jádro je pozměněno" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Uzamčení jádra vypnuto" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Uzamčení jádra zapnuto" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Klíčenka" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "UMÍSTĚNÍ" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Naposledy změněno" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Zbývá méně než jedna minuta" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licence" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Uzamčení linuxového jádra" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Uzamčený režim linuxového jádra brání účtům s právy správy (root) v přístupu a změnám kritických částí systémového software." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Odkládací prostor (swap) linuxového jádra za provozu dočasně ukládá informace na disk. Pokud tyto nejsou chráněny, může se k nim někdo dostat, pokud získá přístup k disku." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Ověření linuxového jádra" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "Ověřování neporučenosti linuxového jádra zajišťuje, že s kritickým systémovým software nebylo manipulováno. Použití ovladačů zařízení, které nejsou poskytovány systémem, mohou této bezpečnostní funkci bránit ve správném fungování." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linuxový odkládací prostor" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linuxové jádro" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Uzamčení linuxového jádra" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linuxový odkládací prostor" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Vypsat položky v dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Vypsat podporované aktualizace firmwarů" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Vypsat typy firmwaru, které jsou k dispozici" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Vypsat soubory na ESP oddílu" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Načítání…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Zamčeno" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Nízká" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "režim pro výrobce v MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "hw přepnutí MEI" + +msgid "MEI version" +msgstr "Verze MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Ručně zapnout konkrétní zásuvné moduly" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Režim pro výrobce je používán při výrobě zařízení a funkce pro zabezpečení ještě nejsou zapnuté." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Nejdelší platná délka" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Nejvyšší platná hodnota" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Střední" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Podpis metadat" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI pro metadata" + +#. 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 Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Nejstarší použitelná verze" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Nejkratší platné délka" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Nejnižší platná hodnota" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Nesoulad verzí procesu služby a klienta, namísto toho použijte %s" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Změnit hodnotu v nastavení procesu služby" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Změnit zadaný vzdálený zdroj" + +msgid "Modify a configured remote" +msgstr "Upravit nastavený vzdálený zdroj" + +msgid "Modify daemon configuration" +msgstr "Upravit nastavení procesu služby" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Sledovat události v procesu služby" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Připojit ESP oddíl" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "POZN.: Tento program může správně fungovat jen pod uživatelem root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Po instalaci vyžaduje restart" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Vyžaduje restart" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Po instalaci vyžaduje vypnutí" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nová verze" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Není zadána žádné akce!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Pro %s nejsou k dispozici žádné starší verze" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nenalezeny žádné identifikátory firmware" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nezjištěn žádný hardware vybavený pro aktualizaci firmware v něm." + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nebyl nalezen žádný zásuvný modul" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nejsou k dispozici žádná vydání" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "V tuto chvíli nejsou povolené žádné vzdálené zdroje, takže nejsou k dispozici žádná metadata." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nejsou k dispozici žádné vzdálené zdroje" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Žádná zařízení, u kterých by bylo možné zaktualizovat" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Nejsou k dispozici žádné aktualizace" + +msgid "No updates available for remaining devices" +msgstr "Pro zbývající zařízení nejsou k dispozici žádné aktualizace" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nebyly provedeny žádné aktualizace" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Neschváleno" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "Nezadán dostatek údajů pro provedení výpočtu HSI." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nenalezeno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nepodporováno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "V pořádku" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Zobrazit pouze jedinou PCR hodnotu" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "IFPS použít pouze při stahování souborů" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Zařízení umožňuje pouze aktualizace na novější verze" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Výstup ve formátu JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Přepsat výchozí popis umístění na ESP oddílu" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "POPIS-UMÍSTĚNÍ" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Zpracovat a zobrazit podrobnosti o souboru s firmwarem" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Zpracovávání aktualizace dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Zpracovávání systémového dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Heslo" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Opravit blob firmware na známém posunu" + +msgid "Payload" +msgstr "Obsah" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Čeká" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Dokončeno procent" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Provést operaci" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Ladění platformy" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "Ladění platformy umožňuje vypínat funkce zabezpečení zařízení. Mělo by být používáno pouze výrobci zařízení." + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Než budete pokračovat, zajistěte si, abyste měli k dispozici záchranný klíč pro svazek." + +#. 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: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Chybí komponenty, na kterých zásuvný modul závisí" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Možné hodnoty" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "Ochrana přímého přístupu do paměti před startem operačního systému" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Ochrana přímého přístupu do paměti před startem operačního systému" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Ochrana přímého přístupu do paměti před startem operačního systému je vypnuta" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Ochrana přímého přístupu do paměti před startem operačního systému je zapnuta" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Předchozí verze" + +msgid "Print the version number" +msgstr "Vypsat číslo verze" + +msgid "Print verbose debug statements" +msgstr "Vypsat podrobná ladicí hlášení" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Priorita" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problémy" + +msgid "Proceed with upload?" +msgstr "Pokračovat v nahrávání?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Kontroly zabezpečení procesoru" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Proprietární" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Dotázat se na podporu aktualizace firmwaru" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "IDENTIFIKÁTOR-VZDÁLENÉHO-ZDROJE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "HODNOTA KLÍČE IDENTIF-VZDÁLENÉHO-ZDROJE" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Pouze pro čtení" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Vyčíst blob firmwaru ze zařízení" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Vyčíst firmware ze zařízení" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Vyčíst firmware ze zařízení a zapsat ho do souboru" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Vyčíst firmware z jednoho oddílu do souboru" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Čtení z %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Čtení…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Restartování…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Aktualizovat metadata ze vzdáleného serveru" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Přeinstalovat %s na %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Přeinstalovat stávající firmware na zařízení" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Přeinstalovat firmware na zařízení" + +#. 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 "Přeinstalovává se %s na %s…" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Větev vydání" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Příznaky vydání" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Identif. vydání" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Identif. vzdáleného zdroje" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Nahradit data ve stávajícím souboru s firmwarem" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI pro hlášení" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Nahlášeno na vzdálený server" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Nebyl nalezen potřebný souborový systém efivarfs" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Požadovaný hardware nenalezen" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Vyžaduje ruční přepnutí do zavaděče firmware" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Vyžaduje připojení k Internetu" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Restartovat nyní?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Restartovat proces služby, aby se změny uplatnily?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Restartování zařízení…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Získává nastavení z BIOS. Pokud nejsou uvedeny žádné argumenty, jsou vypsána veškerá nastavení" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Vypsat všechny identifikátory hardwaru počítače" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Zamezení instalaci starší verze" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Podrobnosti získáte spuštěním `fwupdmgr get-upgrades`." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Pokud chcete vyřešit tuto část, spusťte `fwupdmgr sync-bkc`" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Při použití install-blob spustit zásuvný modul rutina vyčištění složeného" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Při použití install-blob spustit zásuvný modul rutina připravení složeného" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Pokud chcete jen zobrazit, spusťte bez „%s“" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Provozované jádro systému je příliš staré" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Přípona běhového prostředí" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "HODNOTA NASTAVENI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "NASTAVENÍ1 HODNOTA1 [NASTAVENÍ2] [HODNOTA2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Popisovač SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Oblast SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI uzamčení" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Ochrana SPI před replay" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI zápis" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Ochrana zápisu do SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "PODSYSTÉM OVLADAČ [IDENTIF-ZAŘÍZENÍ|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Uložit soubor který umožňuje vytváření identifikátorů hardware" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Skalární přírůstek" + +#. 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" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Plánování aktualizace…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot vypnut" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot zapnut" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Podrobnosti viz %s." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Podrobnosti viz %s." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Vybrané zařízení" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Vybraný svazek" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Sériové číslo" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Nastavit volbu v BIOS „%s“ pomocí „%s." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Provést nastavení v BIOS" + +msgid "Set one or more BIOS settings" +msgstr "Nastavit jedno nebo více nastavení v BIOS" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Během aktualizace nastavit příznak ladění" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Nastaví jedno nebo více nastavení v BIOS" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Nastavuje seznam schválených firmwarů" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Typ nastavení" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Nastavení se projeví po restartu stroje" + +#. 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 all results" +msgstr "Zobrazit všechny výsledky" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Zobrazit verzi klienta a procesu služby" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Zobrazit podrobnější informace z procesu služby pro konkrétní oblast" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Zapnout vypisování ladicích informací pro všechny oblasti" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Zobrazit předvolby 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 calculated version of the dbx" +msgstr "Zobrazit vypočtenou verzi dbx" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Zobrazit záznam ladicích informací 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: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Vypnout nyní?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Podepsat firmware novým klíčem" + +msgid "Sign data using the client certificate" +msgstr "Podepsat data klientským certifikátem" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Podepsat data klientským certifikátem" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Podepsat nahraná data klientským certifikátem" + +msgid "Signature" +msgstr "Podpis" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Podepsaný obsah" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Velikost" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Při aktualizaci tohoto firmware, mohou být některá tajemství, uložená v rámci platformy, zneplatněna." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Zdroj" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Zadejte identifikátor(y) výrobce/produktu DFU zařízení" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Zadejte soubor dbx databáze" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Zadejte počet bajtů pro jednotlivé přenosy přes USB sběrnici" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Řetězec" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Úspěch" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Všechna zařízení úspěšně aktivována" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Vzdálený zdroj úspěšně zakázán" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Nová metadata úspěšně stažena:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Vzdálený zdroj úspěšně povolen a znovu načten" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Vzdálený zdroj úspěšně povolen" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware úspěšně nainstalován" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Hodnota v nastavení úspěšně změněna" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Vzdálený zdroj úspěšně změněn" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadata úspěšně ručně aktualizována" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Kontrolní součty pro zařízení úspěšně zaktualizovány" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Úspěšně odesláno %u hlášení" +msgstr[1] "Úspěšně odeslána %u hlášení" +msgstr[2] "Úspěšně odesláno %u hlášení" +msgstr[3] "Úspěšně odeslána %u hlášení" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Kontrolní součty pro zařízení úspěšně ověřeny" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Souhrn" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Podporováno" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Podporovaný procesor" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Podporováno na vzdáleném serveru" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Uspat do nečinnosti" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Uspat do paměti" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Uspání do nečinnosti umožňuje zařízení rychlé přejít do režimu spánku a šetřit tak energií. V době, kdy je zařízení uspané, je možné z něho fyzicky vyjmout operační paměť a číst z ní informace." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Uspání do paměti umožňuje zařízení rychlé přejít do režimu spánku a šetřit tak energií. V době, kdy je zařízení uspané, je možné z něho fyzicky vyjmout operační paměť a číst z ní informace." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Uspat do nečinnosti" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Uspat do paměti" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Přepnout větev z %s na %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Přepnout větev firmware na zařízení" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Synchronizovat verze firmwarů do stavu osvědčené kombinace" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Akumulátor notebooku není dostatečně nabitý, aby aktualizaci bylo možné provést" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "Akumulátor notebooku není dostatečně nabitý, aby aktualizaci bylo možné provést (%u%%, vyžaduje %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Počítač vyžaduje externí napájení" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (Trusted Platform Module) je počítačový čip, který zjišťuje zda bylo s hardwarovými součástmi manipulováno." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Rekonstrukce TPM PCR0" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "Rekonstrukce TPM PCR0 není platná" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "Rekonstrukce TPM PCR0 je nyní platná" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "Nastavení TPM platformy" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "Rekonstrukce TPM" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Prázdné PCR registry v TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Štítek" +msgstr[1] "Štítky" +msgstr[2] "Štítků" +msgstr[3] "Štítky" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "„Poskrvněno“" + +msgid "Target" +msgstr "Cíl" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Vyzkoušet zařízení pomocí JSON manifestu" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "Intel Management Engine ovládá součásti zařízení a aby bylo zamezeno problémům se zabezpečením, je třeba mít jeho nejnovější verzi." + +#. 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 bezplatná služba, které funguje jako nezávislý právní subjekt a nemá žádné vazby na systém $OS_RELEASE:NAME$. Kompatibilita aktualizací firmware s připojenými zařízeními, či vámi používaným systémem, nemusí být poskytovatelem vámi používané distribuce ověřována. Veškerý firmware je poskytován pouze přímo od výrobců daného vybavení." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "Nastavení TPM (Trusted Platform Module) platformy slouží k ověření, zda nebylo s procesem startu zařízení manipulováno." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "TPM (Trusted Platform Module) Reconstruction slouží k ověření, zda nebylo s procesem startu zařízení manipulováno." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "PCR0 v TPM se liší od rekonstrukce." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "UEFI Platform Key slouží ke ověřování zda software na zařízení pochází z důvěryhodného zdroje." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Proces služby načetl spustitelný kód, pocházející od třetí strany. Vývojáři původního procesu služby už proto jeho podporu nemají ve svých rukou!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Verze zařízení neodpovídá: obdrženo %s, očekáváno %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Firmware z %s nepochází od %s (výrobce daného hardware)." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Systémové hodiny nemají správný čas – stahování souborů se kvůli tomu nemusí zdařit." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Výrobce neposkytl žádné poznámky k vydání." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Jsou zde zařízení, která mají problémy:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Nejsou zde žádné blokované soubory s firmware" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Není k dispozici žádný schválený firmware." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Vykonáním příkazu %s bude toto zařízení vráceno zpět na verzi %s." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Tento firmware je poskytován členy komunity LVFS a není poskytován (ani podporován) původním výrobcem hardware." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Tento balíček nebyl ověřen – může se stát, že nebude fungovat správně." + +#. 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 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 vzdálený zdroj obsahuje firmware, který už sice je možné šířit, ale zatím je u výrobce stále ještě ve stádiu testování. Měli byste si zajistit, že znáte způsob, jak se ručně vrátit ke starší verzi firmwaru, kdyby při aktualizaci došlo k nezdaru." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "Tento systém nepodporuje nastavování firmware" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Tento systém má problémy s běhovým prostředím HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Tento systém má nízkou úroveň zabezpečení HSI." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Tento nástroj správci umožňuje provádět UEFI dbx aktualizace." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Tento nástroj správci umožňuje provádět ladění operace UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Tento nástroj správci umožňuje dotazovat a ovládat proces služby fwupd a tím provádět akce jako např. instalaci nebo návrat ke starší verzi firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Tento nástroj správci umožňuje použít zásuvné moduly pro fwupd bez toho, aby se instalovaly na hostitelský systém." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "Tento nástroj může změnit nastavení BIOS „%s“ z „%sȜ na „%s“ automaticky, ale toto se projeví až po restartu počítače." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Tento nástroj může používat pouze uživatel s oprávněními na úrovni správce systému (root)" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Tento nástroj vyčte ze systémového firmwaru záznam událostí v TPM a zpracuje ho." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Pokud chcete toto varování ignorovat, použijte --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Přechodný nezdar" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Pravda" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Důvěryhodná metadata" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Důvěryhodný obsah" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Typ" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Nezjištěn (či nenastaven) UEFI ESP oddíl" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Nástroj pro práci s UEFI firmwarem" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "Klíč platformy v UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI Secure Boot" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "UEFI Secure Boot brání načtení záškodnickému softwaru při spouštění zařízení." + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Nejsou k dispozici (nebo jsou vypnuté v nastavení firmware) aktualizace typu UEFI capsule" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Nástroj pro UEFI dbx" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI firmware není možné aktualizovat, pokud je přepnutý do režimu legacy BIOS" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Klíč platformy v UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Nedaří se připojit ke službě" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Atribut se nedaří nalézt" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Odpojit stávající ovladač" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Odblokovává se firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Odblokovat instalaci konkrétního firmwaru" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Nešifrováno" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Neznámo" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Neznámé zařízení" + +msgid "Unlock the device to allow access" +msgstr "Odemknout zařízení a umožnit tak do něj přístup" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Odemčeno" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Odemknout zařízení pro přístup k firmwaru" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Odpojit ESP oddíl" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Během aktualizace zrušit příznak ladění" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Nepodepsaný obsah" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Nepodporovaná verze procesu služby %s, verze klienta je %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "„Neposkvrněno“" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Možné aktualizovat" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Chyba při aktualizaci" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Aktualizovat obraz" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Zpráva z aktualizace" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stav aktualizace" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Problém s nezdařenou aktualizací je už znám, další informace naleznete na této adrese:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Aktualizovat nyní?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Aktualizace vyžaduje restart" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Aktualizovat uložený kryptografický otisk stávajícím obsahem ROM paměti" + +msgid "Update the stored device verification information" +msgstr "Aktualizovat uložené informace ohledně ověření správnosti pro zařízení" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Aktualizovat uložená metadata stávajícím obsahem" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Aktualizuje firmware veškerých zadaných zařízení na nejnovější verzi, případně na všech zařízeních, pokud nejsou žádná vyjmenována" + +msgid "Updating" +msgstr "Aktualizuje se" + +#. 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 "Aktualizuje se %s z verze %s na %s…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Aktualizace %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Přejít na novější verzi u %s a to z %s na %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Odeslat 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 "Posíláním hlášení o firmwarech pomůžete výrobcům hardwaru rychle rozpoznat nezdařené a úspěšné aktualizace na zařízeních, tak jak jsou provozována v reálných podmínkách." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Naléhavost" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Nápovědu si zobrazíte použitím příkazu fwupdmgr --help" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Nápovědu obdržíte spuštěním fwupdtool --help" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Při instalaci firmware použít příznaky pro kompatibilitu (quirk)" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Uživatel byl upozorněn" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Uživatelské jméno" + +msgid "VID:PID" +msgstr "IDENTIF-VÝROBCE:IDENTIF-PRODUKTU" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Platné" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Ověřování obsahu ESP oddílu…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Varianta" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Výrobce" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Ověřování zapsaného…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Verze" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "VAROVÁNÍ:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Čekání…" + +#. 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í" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Zapsat firmware ze souboru do jednoho oddílu" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Zapisování souboru:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Zapisování…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Měli byste ověřit že dokážete vrátit nastavení zpět z rozhraní firmware, protože tato změna může způsobit že systém nenastartuje do Linuxu nebo způsobí jiné nestability systému." + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "Kompatibilita aktualizací firmware s připojenými zařízeními, či vámi používaným systémem, nemusí být poskytovatelem vámi používané distribuce ověřována. " + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Použitím tohoto firmware může být hardware poškozen a instalací tohoto vydání může být ztracena záruka od %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Váš systém má osvědčené uspořádání %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[KONTROLNÍ-SOUČET]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[IDENTIF-ZAŘÍZENÍ|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[IDENTIF-ZAŘÍZENÍ|GUID] [VĚTEV]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[IDENTIF-ZAŘÍZENÍ|GUID] [VERZE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[SOUBOR PODPIS_SOUBORU IDENTIF-VZDÁLENÉHO-ZDROJE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[SOUBOR1] [SOUBOR2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[NASTAVENi1] [NASTAVENI2]…" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[NASTAVENÍ1] [NASTAVENÍ2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SOUBOR-S-SMBIOS|SOUBOR-S-IDENTIF-HARDWARE]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "výchozí" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd nástroj pro práci se záznamy událostí v TPM" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "zásuvné moduly pro fwupd" diff --git a/fwupd-1.8.6/po/da.po b/fwupd-1.8.6/po/da.po new file mode 100644 index 0000000000000000000000000000000000000000..6f18fd55ade58c793e03451ce55b71ce8bb9e800 --- /dev/null +++ b/fwupd-1.8.6/po/da.po @@ -0,0 +1,2244 @@ +# 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-2021 +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" + +#. TRANSLATORS: 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: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s Opdatering for batteri" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s opdatering af CPU-mikrokode" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s Opdatering for kamera" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Opdatering for konfiguration %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s ME-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 "Opdatering af controller %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 "Opdatering af indlejret controller %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s Opdatering for tastatur" + +#. 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: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s Opdatering for mus" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Opdatering af netværksgrænseflade %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Opdatering af lagercontroller %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: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s Opdatering for TPM" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Opdatering af Thunderbolt-controller %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s Opdatering for pegeplade" + +#. 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: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s og alle tilsluttede enheder vil måske ikke være anvendelige under opdatering." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s-fabrikstilstand" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s skal være tilsluttet under hele opdateringen, for at undgå skade." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s skal være tilsluttet en strømkilde under hele opdateringen, for at undgå skade." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s-tilsidesættelse" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s-version" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dag" +msgstr[1] "%u dage" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u enhed har en tilgængelig firmwareopgradering." +msgstr[1] "%u enheder har en tilgængelig firmwareopgradering." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u time" +msgstr[1] "%u timer" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u lokal enhed understøttes" +msgstr[1] "%u lokale enheder understøttes" + +#. 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: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(forældet)" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Handling kræves:" + +#. 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: 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: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Alle enheder af den samme type opdateres samtidigt" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Tillad nedgradering af firmwareversioner" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Tillad geninstallering af eksisterende firmwareversioner" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Tillad skift af firmwaregren" + +#. 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 line option +msgid "Apply firmware updates" +msgstr "Anvend firmwareopdateringer" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Anvend opdatering, selv når det ikke tilrådes" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Anvend opdateringsfiler" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Anvender opdatering …" + +#. 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: stop nagging the user +msgid "Ask again next time?" +msgstr "Spørg igen næste gang?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Tilkobl til firmwaretilstand" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autentificerer …" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Der kræves autentifikationsdetaljer" + +#. 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: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatisk rapportering" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Upload automatisk hver gang?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML FILNAVN-DST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Bind ny kernedriver" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blokerede firmwarefiler:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokerer firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blokerer en bestemt firmware fra at blive installeret" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Opstartsindlæser version" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Gren" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Byg en firmwarefil" + +#. 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: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Kan ikke anvende eftersom dbx-opdatering allerede er blevet anvendt." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Kan ikke anvende opdateringer på livemedier" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Ændret" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Tjekker om den kryptografiske hash passer med firmwaren" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Checksum" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Vælg en gren:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Vælg en enhed:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Vælg en firmwaretype:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Vælg en udgivelse:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Vælg et diskområde:" + +#. 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 a firmware file" +msgstr "Konverter en firmwarefil" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Oprettet" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritisk" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Bekræftelse af kryptografisk hash er tilgængelig" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Nuværende version" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ENHEDS-ID|GUID" + +#. 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: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Beskrivelse" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Frakobl til opstartsindlæsertilstand" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detaljer" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Enhedsflag" + +#. 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: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Enheden kan gendannes efter mislykkedes flash" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Enhed ændret:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Enhedsfirmware kræves for at have et versiontjek" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Enheden er låst" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Enhed kræves for at installere alle leverede udgivelser" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Enheden er utilgængelig" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Enheden kan anvendes under hele opdateringen" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Enhed fjernet:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Enhedstrin-opdateringer" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Enheden understøtter at der kan skiftes til en anden gren med firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Opdateringsmetode for enhed" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Enhedsopdatering behøver aktivering" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Enheden sikkerhedskopierer firmwaren inden installation" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Enheden vises ikke igen når opdateringen er færdig" + +#. 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:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Enheder uden nogen tilgængelige firmwareopdateringer: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Enheder med den seneste tilgængelige firmwareversion:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Deaktiveret" + +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 unreported history" +msgstr "Tjek ikke efter historik som ikke er blevet rapporteret" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Tjek ikke om download af fjerne skal aktiveres" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Tjek ikke eller spørg om genstart, efter opdatering" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Medtag ikke præfiks for logdomæne" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Medtag ikke tidsstempelpræfiks" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Udfør ikke sikkerhedstjek af enhed" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Skriv ikke til historikdatabasen" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Forstår du de konsekvenser som er forbundet med ændring af firmwaregrenen?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Vil du deaktivere funktionaliteten for fremtidige opdateringer?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Vil du opdatere fjernen nu?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Vil du uploade rapporter automatisk for fremtidige opdateringer?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Færdig!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Nedgrader %s fra %s til %s?" + +#. 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: command description +msgid "Download a file" +msgstr "Download en fil" + +#. 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: length of time the update takes to apply +msgid "Duration" +msgstr "Varighed" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Angivet ESP var ikke gyldig" + +#. 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Aktivér ny fjern?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Aktivér fjernen?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "Krypteret" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Krypteret RAM" + +#. 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: command description +msgid "Export a firmware file structure to XML" +msgstr "Eksportér en firmwarefilstruktur til XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Udpak en firmwareblob til aftryk" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FIL" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FIL [ENHEDS-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "FILNAVN" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FILNAVN CERTIFIKAT PRIVAT-NØGLE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FILNAVN ENHED-ALT-NAVN|ENHED-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "FILNAVN ENHED-ALT-NAVN|ENHED-ALT-ID [AFTRYK-ALT-NAVN|AFTRYK-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FILNAVN ENHEDS-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FILNAVN [ENHEDS-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILNAVN [FIRMWARETYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "FILNAVN-KILDE FILNAVN-DST [FIRMWARETYPE-KILDE] [FIRMWARE-TYPE-DST]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Mislykkedes" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Kunne ikke anvende opdatering" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Kunne ikke oprette forbindelse til dæmonen" + +#. 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Kunne ikke indlæse lokal dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Kunne ikke indlæse quirks" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Kunne ikke indlæse systemets dbx" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Kunne ikke låse" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Kunne ikke fortolke argumenter" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Kunne ikke fortolke fil" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Kunne ikke fortolke flag for --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Kunne ikke fortolke lokal dbx" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Kunne ikke validere ESP-indhold" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Filnavn" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Filnavnets underskrift" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Filnavnets kilde" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Filnavn kræves" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtrer med et sæt enhedsflag med et ~-præfiks for at udelukke, f.eks. 'intern,~behøver-genstart'" + +#. TRANSLATORS: 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: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Firmwareattest" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware er allerede blokeret" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware er ikke allerede blokeret" + +#. 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." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Sidste opdatering af firmwaremetadata: %s siden. Brug --force til at opdatere igen." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Firmwareopdateringer" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Firmwareopdateringer deaktiveret; kør 'fwupdmgr unlock' for at aktivere" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Flag" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Gennemtving handlingen ved at afslappe visse runtimetjek" + +msgid "Force the action ignoring all warnings" +msgstr "Gennemtving handlingen og ignorer alle advarsler" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Fundet" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID'er" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Hent alle enhedsflag som understøttes af fwupd" + +#. 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 host security attributes" +msgstr "Henter værtens sikkerhedsattributter" + +#. 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 blocked firmware" +msgstr "Henter listen over blokerede firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Henter listen over opdateringer for tilsluttet hardware" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Henter resultaterne fra en enhed" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Henter resultaterne fra den sidste opdatering" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWID'ER-FIL" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hardwaren venter på at bliver gentilkoblet" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Høj" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Værtens sikkerheds-ID:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Inaktiv …" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignorer strikse SSL-tjek ved download af filer" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignorer mislykkede checksum for firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignorer mislykkedes firmwarehardware som ikke passer sammen" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignorer sikkerhedstjek af bekræftelse" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorerer strikse SSL-tjeks. Eksportér DISABLE_SSL_STRICT i dit miljø for at gøre det automatisk i fremtiden" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Varighed for installation" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Installer en firmwareblob på en enhed" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Installer en firmwarefil på hardwaren" + +msgid "Install signed device firmware" +msgstr "Installer enhedsfirmware der er underskrevet" + +msgid "Install signed system firmware" +msgstr "Installer systemfirmware der er underskrevet" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Installer først til forælderenhed" + +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 …" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM-beskyttet" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP-fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard regler om fejl" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard bekræftet start" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktiv" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET-aktiveret" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Intern enhed" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Ugyldig" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Er i opstartsindlæsertilstand" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problemstilling" +msgstr[1] "Problemstillinger" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "NØGLE,VÆRDI" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Nøglering" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "PLACERING" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Sidst redigeret" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Mindre end et minut tilbage" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licens" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux-kerne" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Nedlukning af Linux-kerne" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux-swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Vis poster i dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Vis understøttede firmwareopdateringer" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Vis de tilgængelige firmwaretyper" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Viser filer på ESP'en" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Indlæser …" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Låst" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Lav" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI-fabrikstilstand" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI-tilsidesættelse" + +msgid "MEI version" +msgstr "MEI-version" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Aktivér bestemte plugins manuelt" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Mellem" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Underskrift for metadata" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadata-URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata kan hentes fra Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimum version" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Dæmon og klient passer ikke sammen, brug %s i stedet" + +#. 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: command description +msgid "Mounts the ESP" +msgstr "Monterer ESP'en" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Genstart efter installation er nødvendig" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Genstart er nødvendig" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Nedlukning efter installation er nødvendig" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Ny version" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Der er ikke angivet nogen handling!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Ingen nedgraderinger til %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Fandt ingen firmware-id'er" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "Ingen tilgængelige udgivelser" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Der er ikke nogen metadata da der ikke er aktiveret nogen fjerne." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Ingen tilgængelige fjerne" + +msgid "No updates available for remaining devices" +msgstr "Ingen opdateringer tilgængelige for tilbageværende enheder" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Der blev ikke anvendt nogen opdateringer" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Ikke fundet" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Ikke-understøttet" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Vis kun én PCR-værdi" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Brug kun IPFS når filer downloades" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Kun versionsopgraderinger er tilladte" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Output i JSON-format" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Tilsidesæt standard-ESP-stien" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "STI" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Fortolk og vis deltaljer om en firmwarefil" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Fortolker dbx-opdatering …" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Fortolker systemets dbx …" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Adgangskode" + +msgid "Payload" +msgstr "Nyttelast" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Afventer" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Procent fuldført" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Udfør handling?" + +#. 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: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Plugin-afhængigheder mangler" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Præopstart DMA-beskyttelse" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Forrige version" + +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: a non-free software license +msgid "Proprietary" +msgstr "Proprietær" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Forespørg om understøttelse af firmwareopdatering" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "FJERN-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "FJERN-ID NØGLE VÆRDI" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Læs en firmwareblob fra en enhed" + +#. 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: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Læser fra %s …" + +#. 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 "Opdater metadata fra fjernserver" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Geninstaller %s til %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Geninstaller den nuværende firmware på enheden" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Geninstaller firmware på en enhed" + +#. 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: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Udgivelsesgren" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Fjern-id" + +#. 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: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Rapporteret til fjernserver" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Krævede efivarfs-filsystem blev ikke fundet" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Det krævede hardware blev ikke fundet" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Kræver en opstartsindlæser" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Kræver internetforbindelse" + +#. 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: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Kør `fwupdmgr get-upgrades` for mere information." + +#. 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" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Kørende kerne er for gammel" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Suffiks for runtime" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS-beskriver" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS-region" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI-lås" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI-skriv" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "UNDERSYSTEM DRIVER [ENHEDS-ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Gem en fil der gør det muligt at generere hardware-id'er" + +#. 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: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planlægger …" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Se %s for mere information." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Valgte enhed" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Valgte diskområde" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Serienummer" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Indstil fejlsøgningsflaget under opdatering" + +#. 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 all results" +msgstr "Vis alle resultater" + +#. 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 calculated version of the dbx" +msgstr "Vis den udregnede version af dbx'en" + +#. 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Underskriv en firmware med en ny nøgle" + +msgid "Sign data using the client certificate" +msgstr "Underskriv data med klientcertifikat" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Størrelse" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Kilde" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Angiv producent-/produkt-id('er) for DFU-enhed" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Angiv dbx-databasefilen" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Angiv antal bytes pr. USB-overførsel" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Lykkedes" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Det lykkedes at aktivere alle enheder" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Det lykkedes at deaktivere fjern" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Det lykkedes at downloade ny metadata: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Det lykkedes at aktivere og opdatere fjernen" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Det lykkedes at aktivere fjern" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Det lykkedes at installere firmware" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Det lykkedes at redigere konfigurationsværdi" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Det lykkedes at redigere fjern" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Det lykkedes at opdatere metadata manuelt" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Opdatering af enhedens tjeksumme lykkedes" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Det lykkedes at uploade %u rapport" +msgstr[1] "Det lykkedes at uploade %u rapporter" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Bekræftelse af enhedens tjeksumme lykkedes" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Opsummering" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Understøttet" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Understøttes på fjernserver" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspender-til-inaktiv" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspender-til-ram" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Skift gren fra %s til %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Skift firmwaregrenen på enheden" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Systemet kræver ekstern strømkilde" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEKST" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0-rekonstruktion" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Tainted" + +msgid "Target" +msgstr "Mål" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Test en enhed med et JSON-manifest" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0'en er ikke magen til rekonstruktionen." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Dæmonen har indlæst tredjepartskode og understøttes ikke længere af opstrømsudviklerne!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Firmwaren fra %s leveres ikke af hardwareleverandøren %s." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Systemets ur er ikke blevet indstillet korrekt og download af filer kan mislykkes." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Der er ikke nogen blokerede firmwarefiler" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Der er ikke nogen godkendt firmware." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Pakken er ikke blevet valideret — den virker måske ikke ordentligt." + +#. 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: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Systemet har problemer med HSI-runtime." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Systemet har et lavt HSI-sikkerhedsniveau." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Værktøjet giver en administrator mulighed for at anvende UEFI-dbx-opdateringer." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Værktøjet giver en administrator mulighed for at fejlfinde UpdateCapsule-handling." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Værktøjet giver en administrator mulighed for at sætte i kø og styre fwupd-dæmonen, hvorved denne kan udføre handlinger såsom at installere eller nedgradere firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Værktøjet giver en administrator mulighed for at bruge fwupd-plugins uden at installere dem på værtssystemet." + +#. 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: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Værktøjet læser og fortolker TPM-hændelsesloggen fra systemfirmwaren." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Forbigående mislykkedes" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Type" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP-partition ikke registreret eller konfigureret" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI-firmwareredskab" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI-kapselopdateringer ikke tilgængelige eller aktiveret i firmwareopsætning" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI-dbx-redskab" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI-firmware kan ikke opdateres i forældet BIOS-tilstand" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI-platformsnøgle" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI sikkeropstart" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Kan ikke oprette forbindelse til tjeneste" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Fjern binding af nuværende driver" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Fjerner blokering af firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Fjerner blokering af en bestemt firmware fra at blive installeret" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Dekrypteret" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Ukendt" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Ukendt enhed" + +msgid "Unlock the device to allow access" +msgstr "Lås enheden op for at tillade adgang" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Låst op" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Låser op for enheden for at få adgang til firmwaren" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Afmonterer ESP'en" + +#. 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 "Ikke-understøttet dæmonversion %s, klientversionen er %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Untainted" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Kan opdateres" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Fejl ved opdatering" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Opdateringsmeddelelse" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Opdateringstilstand" + +#. 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?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Opdatering kræver en genstart" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Opdater den gemte kryptografiske hash med indholdet fra den nuværende ROM" + +msgid "Update the stored device verification information" +msgstr "Opdaterer de gemte informationer om enhedsverifikation" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Opdater det gemte metadata med det nuværende indhold" + +msgid "Updating" +msgstr "Opdaterer" + +#. 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: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Opgrader %s fra %s til %s?" + +#. 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: how important the release is +msgid "Urgency" +msgstr "Vigtighed" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Brug fwupdmgr --help for hjælp" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Brug fwupdtool --help for hjælp" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Brug quirk-flag ved installation af firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Brugeren er blevet underrettet" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Brugernavn" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Gyldig" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validerer ESP-indhold …" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variant" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Producent" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verificerer …" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "ADVARSEL:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Venter …" + +#. 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: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Skriver fil:" + +#. 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." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Der er mulighed for at din hardware kan tage skade hvis firmwaren bruges og installation af udgivelsen kan ugyldiggøre garantien med %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ENHEDS-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ENHEDS-ID|GUID] [GREN]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FIL FILUNDERSKRIFT FJERN-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[FILNAVN1] [FILNAVN2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FIL|HWID'ER-FIL]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "standard" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM-hændelseslogredskab" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd-plugins" diff --git a/fwupd-1.8.6/po/de.po b/fwupd-1.8.6/po/de.po new file mode 100644 index 0000000000000000000000000000000000000000..5f66325f92e26d7a46258d16d19b6bae77080629 --- /dev/null +++ b/fwupd-1.8.6/po/de.po @@ -0,0 +1,2634 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Ettore Atalan , 2018,2021-2022 +# Marco Tedaldi , 2015 +# Wolfgang Stöggl , 2015 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: 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: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC-Aktualisierung" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s Akku-Aktualisierung" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU-Microcode-Aktualisierung" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s Kameraaktualisierung" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s Konfigurationsaktualisierung" + +#. 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 Controller-Aktualisierung" + +#. 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 Geräteaktualisierung" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s Bildschirmaktualisierung" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s Laufwerksaktualisierung" + +#. 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 Eingebetteter-Controller-Aktualisierung" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s Tastaturaktualisierung" + +#. 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-Aktualisierung" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s Mausaktualisierung" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s Netzwerkschnittstellenaktualisierung" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s SSD-Aktualisierung" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s Speicher-Controller-Aktualisierung" + +#. 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 Systemaktualisierung" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM-Aktualisierung" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt-Controller-Aktualisierung" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s Tastfeld-Aktualisierung" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s USB-Empfänger-Aktualisierung" + +#. 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 Aktualisierung" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s und alle angeschlossenen Geräte sind während der Aktualisierung möglicherweise nicht nutzbar." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s erschienen: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s geändert: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s verschwunden: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s ist derzeit nicht aktualisierbar" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s Herstellungsmodus" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s muss während der gesamten Dauer der Aktualisierung angeschlossen bleiben, um Schäden zu vermeiden." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s muss während der gesamten Dauer der Aktualisierung an eine Stromquelle angeschlossen bleiben, um Schäden zu vermeiden." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s überschreiben" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s Version" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u Tag" +msgstr[1] "%u Tage" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "Für %u Gerät ist eine Firmware-Aktualisierung verfügbar." +msgstr[1] "Für %u Geräte ist eine Firmware-Aktualisierung verfügbar." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u Gerät ist nicht die beste bekannte Konfiguration." +msgstr[1] "%u Geräte sind nicht die beste bekannte Konfiguration." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u Stunde" +msgstr[1] "%u Stunden" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u lokales Gerät unterstützt" +msgstr[1] "%u lokale Geräte unterstützt" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u Minute" +msgstr[1] "%u Minuten" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u Sekunde" +msgstr[1] "%u Sekunden" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (Schwellenwert %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(veraltet)" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD-Firmware-Schreibschutz" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Aktion erforderlich:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Geräte aktivieren" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Ausstehende Geräte aktivieren" + +msgid "Activate the new firmware on the device" +msgstr "Die neue Firmware auf dem Gerät aktivieren" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Firmware-Aktualisierung wird aktiviert" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Firmware-Aktualisierung wird aktiviert für" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Alter" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Zustimmen und die Gegenstelle aktivieren?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Verweis auf %s" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Alle Geräte desselben Typs werden zur gleichen Zeit aktualisiert" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Herabstufung von Firmware-Versionen zulassen" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Neuinstallation vorhandener Firmware-Versionen erlauben" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Wechsel des Firmware-Zweigs erlauben" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Alternativer Zweig" + +#. 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: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Für eine Aktualisierung muss das System zum Abschluss heruntergefahren werden." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Alle Fragen mit Ja beantworten" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Firmware-Aktualisierungen anwenden" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aktualisierung auch dann anwenden, wenn dies nicht empfohlen wird" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aktualisierungsdateien anwenden" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aktualisierung wird angewendet …" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Genehmigte Firmware:" +msgstr[1] "Genehmigte Firmware:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Nächstes Mal wieder nachfragen?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "An den Firmware-Modus anhängen" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Authentifizierung …" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Authentifizierungsdetails sind erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +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 "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 modify BIOS settings" +msgstr "Zum Ändern der BIOS-Einstellungen ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Zum Ändern einer konfigurierten Gegenstelle, die für Firmware-Aktualisierungen verwendet wird, ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Legitimierung ist zum Verändern der Daemon-Konfiguration notwendig" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "Zum Lesen der BIOS-Einstellungen ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Zum Festlegen der Liste der zugelassenen Firmware ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Zum Signieren von Daten mit dem Client-Zertifikat ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Für den Wechsel zur neuen Firmware-Version ist eine Authentifizierung erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Legitimation ist zum Entsperren eines Geräts erforderlich" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "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 "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: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatische Berichterstattung" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Jedes Mal automatisch hochladen?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "BIOS-Aktualisierungen werden über LVFS oder Windows Update bereitgestellt" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Akku" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Neuen Kernel-Treiber einbinden" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blockierte Firmware-Dateien:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Blockierte Version" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Firmware wird blockiert:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blockiert die Installation einer bestimmten Firmware" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Bootloader-Version" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Zweig" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Eine Firmware-Datei bauen" + +#. 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" +msgstr "Abgebrochen" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Eine bereits angewandte dbx-Aktualisierung kann nicht angewendet werden." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Aktualisierungen können nicht auf Live-Medien angewendet werden" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Geändert" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Prüfsumme" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Wählen Sie einen Zweig aus:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Wählen Sie ein Gerät aus:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Wählen Sie einen Firmware-Typ aus:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Wählen Sie eine Version aus:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Wählen Sie einen Datenträger:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Bereinigt die Ergebnisse der letzten Aktualisierung" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Befehl nicht gefunden" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Vorgeschlagene Konfigurationsänderung" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Eine Firmware-Datei konvertieren" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Erstellt" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritisch" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Kryptografische Hash-Verifizierung ist verfügbar" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Aktueller Wert" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Aktuelle Version" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "GERÄTEKENNUNG|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU-Dienstprogramm" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Debug Optionen" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Entpacken …" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Beschreibung" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Vom Bootloader-Modus lösen" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Details" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Von der besten bekannten Konfiguration abweichen?" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Gerätekennung" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Gerät hinzugefügt:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Geräteakku ist zu schwach" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Geräteakkuleistung ist zu gering (%u%%, erfordert %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Das Gerät kann sich nach Fehlern beim Aufspielen wiederherstellen" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Gerät kann nicht verwendet werden, wenn der Deckel geschlossen ist" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Gerät geändert:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Eine Versionsprüfung der Geräte-Firmware ist erforderlich" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Gerät wird emuliert" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Gerät ist gesperrt" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Das Gerät muss alle bereitgestellten Versionen nacheinander installieren." + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Gerät ist nicht erreichbar" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Gerät ist unerreichbar oder außerhalb der Funkreichweite" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Das Gerät ist während der Dauer der Aktualisierung nutzbar" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Gerät wartet auf die Anwendung der Aktualisierung" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Gerät entfernt:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Gerät muss an eine Wechselstromquelle angeschlossen werden" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Für dieses Gerät werden Gerätesoftware-Aktualisierungen bereitgestellt." + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Gerät unterstützt den Wechsel zu einem anderen Zweig der Firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Geräteaktualisierungsmethode" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Geräteaktualisierung erfordert Aktivierung" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Das Gerät sichert die Firmware vor der Installation" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Gerät wird nach Abschluss der Aktualisierung nicht wieder angezeigt" + +#. 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:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Geräte mit keinen verfügbaren Firmware-Aktualisierungen: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Geräte mit der neuesten verfügbaren Firmware-Version:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Es wurden keine Geräte mit passenden GUIDs gefunden" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Deaktiviert" + +msgid "Disabled fwupdate debugging" +msgstr "fwupdate-Defektlokalisierung deaktivieren" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Deaktiviert eine gegebene Gegenstelle" + +#. 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 unreported history" +msgstr "Nicht auf nicht erfassten Verlauf prüfen" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Nicht prüfen, ob Gegenstellen zum Herunterladen aktiviert werden sollen" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Nach der Aktualisierung nicht prüfen oder zum Neustart auffordern" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Zeitstempel-Präfix nicht einbeziehen" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Keine Gerätesicherheitsüberprüfungen durchführen" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Nicht nach Geräten fragen" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Nicht in die Verlaufsdatenbank schreiben" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Verstehen Sie die Folgen einer Änderung des Firmware-Zweigs?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Möchten Sie diese Funktion für zukünftige Aktualisierungen deaktivieren?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Möchten Sie diese Gegenstelle jetzt auffrischen?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Möchten Sie Berichte für künftige Aktualisierungen automatisch hochladen?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Nicht zur Authentifizierung auffordern (es werden möglicherweise weniger Details angezeigt)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Fertig." + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "%s von %s auf %s herabstufen?" + +#. 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 "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: command description +msgid "Download a file" +msgstr "Herunterladen einer Datei" + +#. 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: length of time the update takes to apply +msgid "Duration" +msgstr "Dauer" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Das angegebene ESP war nicht gültig" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Jedes System sollte Tests haben, um die Sicherheit der Firmware zu gewährleisten." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Emulierter Host" + +msgid "Enable" +msgstr "Aktivieren" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Firmware-Aktualisierungsunterstützung auf unterstützten Systemen aktivieren" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Neue Gegenstelle aktivieren?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Diese Gegenstelle aktivieren?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Aktiviert" + +msgid "Enabled fwupdate debugging" +msgstr "fwupdate-Defektlokalisierung aktivieren" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Aktiviert eine gegebene Gegenstelle" + +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: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Die Aktivierung dieser Gegenstelle erfolgt auf eigene Gefahr." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Verschlüsselt" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Verschlüsselter RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Verschlüsselter RAM macht es unmöglich, dass im Gerätespeicher gespeicherte Informationen gelesen werden können, wenn der Speicherchip entfernt und darauf zugegriffen wird." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Lebensende" + +#. 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" +msgstr "Verlassen nach einer kurzen Verzögerung" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Nach dem Laden der Engine beenden" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Eine Firmware-Dateistruktur nach XML exportieren" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Einen Firmware-Blob in Abbilder extrahieren" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "DATEI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "DATEI [GERÄTEKENNUNG|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "DATEINAME" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "DATEINAME ZERTIFIKAT PRIVATER-SCHLÜSSEL" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "DATEINAME GERÄT-ALT-NAME|GERÄT-ALT-KENNUNG" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "DATEINAME GERÄT-ALT-NAME|GERÄT-ALT-KENNUNG [ABBILD-ALT-NAME|ABBILD-ALT-KENNUNG]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "DATEINAME GERÄTEKENNUNG" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "DATEINAME [GERÄTEKENNUNG|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "DATEINAME [FIRMWARE-TYP]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "DATEINAME-QUELLE DATEINAME-ZIEL [FIRMWARE-TYP-QUELLE] [FIRMWARE-TYP-ZIEL]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "DATEINAME|PRÜFSUMME1[,PRÜFSUMME2][,PRÜFSUMME3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Fehlgeschlagen" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Anwenden der Aktualisierung ist fehlgeschlagen" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Verbindung mit dem Daemon ist fehlgeschlagen" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Abrufen ausstehender Geräte ist fehlgeschlagen" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Installieren der Firmware-Aktualisierung ist fehlgeschlagen" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Laden der lokalen dbx ist fehlgeschlagen" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Laden der Macken ist fehlgeschlagen" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Laden der System-dbx ist fehlgeschlagen" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Sperren ist fehlgeschlagen" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Parsen der Argumente ist fehlgeschlagen" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Parsen der Datei ist fehlgeschlagen" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Parsen der lokalen dbx ist fehlgeschlagen" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Neustart ist fehlgeschlagen" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Festlegen des Begrüßungsmodus ist fehlgeschlagen" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Validierung des ESP-Inhalts ist fehlgeschlagen" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Falsch" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Dateiname" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Dateinamen-Signatur" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Dateinamen-Quelle" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Dateiname erforderlich" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Firmware-Attestierung" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Bei der Firmware-Attestierung wird die Gerätesoftware anhand einer Referenzkopie überprüft, um sicherzustellen, dass sie nicht geändert wurde." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Firmware-BIOS-Deskriptor" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "Firmware-BIOS-Deskriptor schützt den Firmware-Speicher des Geräts vor Manipulationen." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Firmware-BIOS-Region" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "Firmware-BIOS-Region schützt den Firmware-Speicher des Geräts vor Manipulationen." + +#. 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 "Hintergrundprogramm für Firmware-Aktualisierung" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Firmware-Aktualisierungsprogramm-Verifizierung" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Die Firmware-Aktualisierungsprogramm-Verifizierung prüft, ob die für die Aktualisierung verwendete Software nicht manipuliert wurde." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Firmware-Aktualisierungen" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Firmware-Dienstprogramm" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Firmware-Schreibschutz" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Firmware-Schreibschutz-Sperre" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Firmware-Schreibschutz schützt den Firmware-Speicher des Geräts vor Manipulationen." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Firmware-Attestierung" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware ist bereits blockiert" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware ist nicht bereits blockiert." + +#. 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." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Firmware-Aktualisierungen" + +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." + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Aktion durch Lockerung einiger Laufzeitüberprüfungen erzwingen" + +msgid "Force the action ignoring all warnings" +msgstr "Aktion erzwingen, bei der alle Warnungen ignoriert werden" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Gefunden" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Vollständige Festplattenverschlüsselung erkannt" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Die Geheimnisse der vollständigen Festplattenverschlüsselung können bei der Aktualisierung ungültig werden" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "BIOS-Einstellungen abrufen" + +#. 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" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Ruft die konfigurierten Gegenstellen ab" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Ruft die Sicherheitsattribute des Hosts ab" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Holt die Liste der genehmigten Firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Holt die Liste der blockierten Firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Ermittelt die Liste der Aktualisierungen für angeschlossene Hardware" + +#. 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" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-DATEI" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hardware wartet darauf, wieder angeschlossen zu werden" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Hoch" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Host-Sicherheitsereignisse" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU-Schutz" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "IOMMU-Schutz verhindert, dass angeschlossene Geräte auf nicht autorisierte Teile des Systemspeichers zugreifen." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU-Geräteschutz deaktiviert" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU-Geräteschutz aktiviert" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Bereit …" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Strenge SSL-Prüfungen beim Herunterladen von Dateien ignorieren" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Fehlerhafte Firmware-Prüfsummen ignorieren" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Firmware-Hardware-Nichtübereinstimmungsfehler ignorieren" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Validierungssicherheitsüberprüfungen ignorieren" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Installationsdauer" + +#. 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 signed system firmware" +msgstr "Alte Version der signierten System-Firmware installieren" + +msgid "Install old version of unsigned system firmware" +msgstr "Alte Version der nicht-signierten System-Firmware installieren" + +msgid "Install signed device firmware" +msgstr "Signierte Geräte-Firmware installieren" + +msgid "Install signed system firmware" +msgstr "Signierte System-Firmware installieren" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Zuerst auf dem übergeordneten Gerät installieren" + +msgid "Install unsigned device firmware" +msgstr "Nicht-signierte Geräte-Firmware installieren" + +msgid "Install unsigned system firmware" +msgstr "Nicht-signierte System-Firmware installieren" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Firmware wird installiert …" + +#. 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 …" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Durch die Installation dieser Aktualisierung kann auch die Gerätegarantie erlöschen." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Ganzzahl" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM-geschützt" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard-Fehlerrichtlinie" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard verhindert, dass nicht autorisierte Gerätesoftware beim Start des Geräts ausgeführt wird." + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktiv" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET aktiviert" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Internes Gerät" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Ungültig" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Ungültige Argumente" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Ist Zurückstufung" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Ist im Bootloader-Modus" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Ist Höherstufung" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Anliegen" +msgstr[1] "Anliegen" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "SCHLÜSSEL,WERT" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Kernel-Sperrung deaktiviert" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Kernel-Sperrung aktiviert" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Schlüsselbund" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "ORT" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Zuletzt geändert" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Weniger als eine Minute verbleiben" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Lizenz" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Linux-Kernel-Sperrung" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Linux-Kernel-Verifizierung" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux-Auslagerung" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux-Kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux-Kernel-Sperrung" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux-Auslagerung" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Einträge in dbx auflisten" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Unterstützte Firmware-Aktualisierungen auflisten" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Die verfügbaren Firmware-Typen auflisten" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Listet die Dateien auf dem ESP auf" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Laden …" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Gesperrt" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Niedrig" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI-Herstellungsmodus" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI überschreiben" + +msgid "MEI version" +msgstr "MEI-Version" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Manuelles Aktivieren bestimmter Plugins" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Der Herstellungsmodus wird verwendet, wenn das Gerät hergestellt wird und die Sicherheitsfunktionen noch nicht aktiviert sind." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Maximale Länge" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Maximaler Wert" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Mittel" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metadaten-Signatur" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadaten-URI" + +#. 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." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimale Version" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Minimale Länge" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Minimaler Wert" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifiziert eine gegebene Gegenstelle" + +msgid "Modify a configured remote" +msgstr "Eine konfigurierte Gegenstelle modifizieren" + +msgid "Modify daemon configuration" +msgstr "Daemon-Konfiguration bearbeiten" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Hintergrundprogramm auf Ereignisse überwachen" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Hängt das ESP ein" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "HINWEIS: Dieses Programm funktioniert möglicherweise nur als root korrekt" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Benötigt einen Neustart nach der Installation" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Benötigt Neustart" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Benötigt ein Herunterfahren nach der Installation" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Neue Version" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Keine Aktion angegeben!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Keine Herabstufungen für %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Keine Firmware-Kennungen gefunden" + +#. 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" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Keine Freigaben verfügbar" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Da derzeit keine Gegenstellen aktiviert sind, sind keine Metadaten verfügbar." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Keine Gegenstelle verfügbar" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Keine aktualisierbaren Geräte" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Keine Aktualisierungen verfügbar" + +msgid "No updates available for remaining devices" +msgstr "Für die verbleibenden Geräte sind keine Aktualisierungen verfügbar" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Es wurden keine Aktualisierungen angewendet" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Nicht genehmigt" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nicht gefunden" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nicht unterstützt" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Ok" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Nur einzelnen PCR-Wert anzeigen" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "IPFS nur beim Herunterladen von Dateien verwenden" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Nur Versionsaktualisierungen sind erlaubt" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Ausgabe im JSON-Format" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Standard-ESP-Pfad überschreiben" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PFAD" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Details zu einer Firmware-Datei parsen und anzeigen" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "dbx-Aktualisierung wird geparst …" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "System-dbx wird geparst …" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Passwort" + +msgid "Payload" +msgstr "Nutzdaten" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Ausstehend" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Prozent abgeschlossen" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Operation durchführen?" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Bitte stellen Sie sicher, dass Sie den Wiederherstellungsschlüssel für den Datenträger haben, bevor Sie fortfahren." + +#. 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: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Fehlende Plugin-Abhängigkeiten" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Mögliche Werte" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "DMA-Schutz vor dem Booten" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "DMA-Schutz vor dem Booten" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "DMA-Schutz vor dem Booten ist deaktiviert" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "DMA-Schutz vor dem Booten ist aktiviert" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Vorherige Version" + +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" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Probleme" + +msgid "Proceed with upload?" +msgstr "Mit dem Hochladen fortfahren?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Prozessor-Sicherheitsprüfungen" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Proprietär" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Abfrage der Unterstützung für Firmware-Aktualisierungen" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Nur lesen" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Einen Firmware-Blob von einem Gerät lesen" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Firmware von einem Gerät lesen" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Firmware von Gerät in Datei schreiben" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Firmware von einzelner Partition in Datei lesen" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Von %s wird gelesen …" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lesen …" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Wird neu gestartet …" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Metadaten von entferntem Server aktualisieren" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "%s auf %s neu installieren?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Aktuelle Firmware auf dem Gerät neu installieren" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Firmware auf einem Gerät neu installieren" + +#. 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 "Erneute Installation von %s mit %s …" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Veröffentlichungszweig" + +#. 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: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "An den entfernten Server gemeldet" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Erforderliches efivarfs-Dateisystem wurde nicht gefunden" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Erforderliche Hardware wurde nicht gefunden" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Erfordert einen Bootloader" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Erfordert Internetverbindung" + +#. 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 "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "BIOS-Einstellungen abrufen. Wenn keine Argumente übergeben werden, werden alle Einstellungen zurückgegeben" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Alle Hardware-Kennungen für das System zurückgeben" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Führen Sie `fwupdmgr get-upgrades` für weitere Informationen aus." + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Laufender Kernel ist zu alt" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Laufzeit-Suffix" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "EINSTELLUNGSWERT" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "EINSTELLUNG1 WERT1 [EINSTELLUNG2] [WERT2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI-BIOS-Deskriptor" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS-Region" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI sperren" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI schreiben" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "SPI-Schreibschutz" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSYSTEM TREIBER [GERÄTEKENNUNG|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Eine Datei speichern, die die Erstellung von Hardware-Kennungen ermöglicht" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Skalares Inkrement" + +#. 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: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Einplanen …" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot deaktiviert" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot aktiviert" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Siehe %s für weitere Details." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Siehe %s für weitere Informationen." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Ausgewähltes Gerät" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Ausgewählter Datenträger" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Seriennummer" + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Eine BIOS-Einstellung festlegen" + +msgid "Set one or more BIOS settings" +msgstr "Eine oder mehrere BIOS-Einstellungen festlegen" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Legt eine oder mehrere BIOS-Einstellungen fest" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Legt die Liste der zugelassenen Firmware fest" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Einstellungstyp" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Einstellungen werden nach einem Neustart des Systems wirksam" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Firmware-Verlauf mit den Entwicklern teilen" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Alle Ergebnisse anzeigen" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Client- und Hintergrundprogramm-Versionen anzeigen" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Ausführliche Daemon-Informationen für eine bestimme Domäne anzeigen" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Informationen zur Fehlerdiagnose für alle Domänen 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 calculated version of the dbx" +msgstr "Berechnete Version der dbx 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" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Jetzt herunterfahren?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Eine Firmware mit einem neuen Schlüssel signieren" + +msgid "Sign data using the client certificate" +msgstr "Daten mit dem Client-Zertifikat signieren" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Daten mit dem Client-Zertifikat signieren" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Die hochgeladenen Daten mit dem Client-Zertifikat signieren" + +msgid "Signature" +msgstr "Signatur" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Signierte Nutzdaten" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Größe" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Einige der Plattformgeheimnisse können durch die Aktualisierung dieser Firmware ungültig werden." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Quelle" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "dbx-Datenbankdatei angeben" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Geben Sie die Anzahl der Bytes pro USB-Übertragung an" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Zeichenkette" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Erfolg" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Alle Geräte erfolgreich aktiviert" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Erfolgreich deaktivierte Gegenstelle" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Neue Metadaten wurden erfolgreich heruntergeladen: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Gegenstelle erfolgreich aktiviert und aufgefrischt" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Erfolgreich aktivierte Gegenstelle " + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Erfolgreich installierte Firmware" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Konfigurationswert erfolgreich geändert" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Erfolgreich modifizierte Gegenstelle" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadaten erfolgreich manuell aufgefrischt" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Die Geräte-Prüfsummen wurden erfolgreich aktualisiert" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "%u Bericht erfolgreich hochgeladen" +msgstr[1] "%u Berichte erfolgreich hochgeladen" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Geräteprüfsummen erfolgreich verifiziert" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Zusammenfassung" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Unterstützt" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Unterstützte CPU" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Unterstützt auf dem entfernten Server" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "In Ruhezustand versetzen" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "In Bereitschaft versetzen" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Zweig von %s nach %s wechseln?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Den Firmware-Zweig auf dem Gerät wechseln" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Synchronisierung der Firmware-Versionen mit der besten bekannten Konfiguration des Hosts" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Systemleistung ist zu gering, um die Aktualisierung durchzuführen" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "Systemleistung ist zu gering, um die Aktualisierung durchzuführen (%u%%, erfordert %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "System benötigt externe Stromquelle" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (Trusted Platform Module) ist ein Computerchip, der erkennt, wenn Hardwarekomponenten manipuliert wurden." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM-PCR0-Rekonstruktion" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "TPM-Plattformkonfiguration" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "TPM-Rekonstruktion" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Verdorben" + +msgid "Target" +msgstr "Ziel" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Ein Gerät mit einem JSON-Manifest testen" + +#. 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: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "Die TPM (Trusted Platform Module)-Plattformkonfiguration wird verwendet, um zu überprüfen, ob der Gerätestartprozess geändert wurde." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "Die TPM (Trusted Platform Module)-Rekonstruktion wird verwendet, um zu überprüfen, ob der Gerätestartprozess geändert wurde." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "Der UEFI-Plattformschlüssel wird verwendet, um festzustellen, ob die Gerätesoftware aus einer vertrauenswürdigen Quelle stammt." + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Die Geräteversion stimmte nicht überein: %s erhalten, %s erwartet" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Die Firmware von %s wird nicht von %s, dem Hardwarehersteller, geliefert." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Die Systemuhr wurde nicht korrekt eingestellt und das Herunterladen von Dateien kann fehlschlagen." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Der Anbieter hat keine Versionshinweise zur Verfügung gestellt." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Es gibt Geräte mit Problemen:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Es gibt keine blockierten Firmware-Dateien" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Es gibt keine genehmigte Firmware." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Diese Firmware wird von Mitgliedern der LVFS-Gemeinschaft zur Verfügung gestellt und wird nicht vom ursprünglichen Hardwareanbieter bereitgestellt (oder unterstützt)." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Dieses Paket wurde nicht validiert und könnte nicht richtig funktionieren." + +#. 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" + +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 "Diese Gegenstelle enthält Firmware, die nicht unter Embargo steht, sondern vom Hardwareanbieter noch getestet wird. Sie sollten sicherstellen, dass Sie eine Möglichkeit haben, die Firmware manuell zurückzustufen, falls die Firmware-Aktualisierung fehlschlägt." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "Dieses System unterstützt keine Firmware-Einstellungen" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Dieses System hat HSI-Laufzeitprobleme." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Dieses System hat eine niedrige HSI-Sicherheitsstufe." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Mit diesem Werkzeug kann ein Administrator UEFI dbx-Aktualisierungen anwenden." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Dieses Werkzeug kann nur von dem Benutzer root verwendet werden" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Dieses Werkzeug liest und parst das TPM-Ereignisprotokoll aus der System-Firmware." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Vorübergehender Fehler" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Wahr" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Vertrauenswürdige Metadaten" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Vertrauenswürdige Nutzdaten" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Typ" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP-Partition nicht erkannt oder konfiguriert" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI-Firmware-Dienstprogramm" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI-Plattformschlüssel" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "UEFI Secure Boot verhindert, dass bösartige Software beim Start des Geräts geladen wird." + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx-Dienstprogramm" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI-Firmware kann im veralteten BIOS-Modus nicht aktualisiert werden" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI-Plattformschlüssel" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Sicherer UEFI-Boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Es konnte keine Verbindung zum Dienst hergestellt werden" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Aktuellen Treiber entbinden" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Firmware entblocken:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Entblockt die Installation einer bestimmten Firmware" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Entschlüsselt" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Unbekannt" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Unbekanntes Gerät" + +msgid "Unlock the device to allow access" +msgstr "Das Gerät entsperren, um Zugriff zu ermöglichen" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Entsperrt" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Entsperrt das Gerät für Zugriff auf die Firmware" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Hängt das ESP aus" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Unsignierte Nutzdaten" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Unverdorben" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Aktualisierbar" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Aktualisierungsfehler" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Aktualisierungsmeldung" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Aktualisierungsstatus" + +#. 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?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Aktualisierung erfordert einen Neustart" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Den gespeicherten kryptografischen Hash mit dem aktuellen ROM-Inhalt aktualisieren" + +msgid "Update the stored device verification information" +msgstr "Gespeicherte Geräteverifizierungsinformationen aktualisieren" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Die gespeicherten Metadaten mit dem aktuellen Inhalt aktualisieren" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Aktualisiert alle angegebenen Geräte auf die neueste Firmware-Version, oder alle Geräte, wenn nicht angegeben" + +msgid "Updating" +msgstr "Wird aktualisiert" + +#. 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 "Aktualisieren von %s von %s nach %s …" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s wird aktualisiert …" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "%s von %s auf %s aktualisieren?" + +#. 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: how important the release is +msgid "Urgency" +msgstr "Dringlichkeit" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Verwenden Sie fwupdmgr --help für Hilfe" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Verwenden Sie fwupdtool --help für Hilfe" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Benutzer wurde benachrichtigt" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Benutzername" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Gültig" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validierung der ESP-Inhalte …" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variante" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Anbieter" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Überprüfung …" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "WARNUNG:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Warten …" + +#. 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" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Firmware aus Datei in einzelne Partition schreiben" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Datei wird geschrieben:" + +#. 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." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Ihre Hardware kann durch die Verwendung dieser Firmware beschädigt werden und die Installation dieser Version kann zum Erlöschen jeglicher Garantie mit %s führen." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[PRÜFSUMME]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[GERÄTEKENNUNG|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[GERÄTEKENNUNG|GUID] [ZWEIG]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[DATEINAME1] [DATEINAME2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[EINSTELLUNG1] [ EINSTELLUNG2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[EINSTELLUNG1] [EINSTELLUNG2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-DATEI|HWIDS-DATEI]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "Standard" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM-Ereignisprotokolldienstprogramm" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd-Plugins" diff --git a/fwupd-1.8.6/po/en_GB.po b/fwupd-1.8.6/po/en_GB.po new file mode 100644 index 0000000000000000000000000000000000000000..4950a9c7c5747456e67fc440dd0daf2a2d24a272 --- /dev/null +++ b/fwupd-1.8.6/po/en_GB.po @@ -0,0 +1,3079 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Andi Chandler , 2019-2020,2022 +# Richard Hughes , 2015,2017-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: en_GB\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: 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: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC Update" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s Battery Update" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU Microcode Update" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s Camera Update" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s Configuration Update" + +#. 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 Update" + +#. 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 Controller Update" + +#. 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 Update" + +#. 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 Device Update" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s Display Update" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s Drive Update" + +#. 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 Embedded Controller Update" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "%s Flash Drive Update" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s Keyboard Update" + +#. 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 Update" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s Mouse Update" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s Network Interface Update" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s SSD Update" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s Storage Controller Update" + +#. 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 System Update" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM Update" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt Controller Update" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s Touchpad Update" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s USB Receiver Update" + +#. 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 Update" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s and all connected devices may not be usable while updating." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s appeared: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s changed: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s disappeared: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s is not currently updatable" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s manufacturing mode" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s must remain connected for the duration of the update to avoid damage." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s must remain plugged into a power source for the duration of the update to avoid damage." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s override" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s version" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u day" +msgstr[1] "%u days" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u device has a firmware upgrade available." +msgstr[1] "%u devices have a firmware upgrade available." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u device is not the best known configuration." +msgstr[1] "%u devices are not the best known configuration." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hour" +msgstr[1] "%u hours" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u local device supported" +msgstr[1] "%u local devices supported" + +#. 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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (threshold %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleted)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "A TPM PCR is now an invalid value" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "AMD Firmware Replay Protection" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD Firmware Write Protection" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "AMD Rollback Protection" + +#. TRANSLATORS: longer description +msgid "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Action Required:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Activate devices" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Activate pending devices" + +msgid "Activate the new firmware on the device" +msgstr "Activate the new firmware on the device" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Activating firmware update" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Activating firmware update for" + +#. TRANSLATORS: the age of the metadata +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" +msgstr "Alias to %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "All TPM PCRs are now valid" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "All TPM PCRs are valid" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "All devices of the same type will be updated at the same time" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Allow downgrading firmware versions" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Allow reinstalling existing firmware versions" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Allow switching firmware branch" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Alternate branch" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Apply firmware updates" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Apply update even when not advised" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Apply update files" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Applying update…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Approved firmware:" +msgstr[1] "Approved firmware:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Ask again next time?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Attach to firmware mode" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Authenticating…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Authentication details are required" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Authentication is required to downgrade the firmware on a removable device" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Authentication is required to downgrade the firmware on this machine" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Authentication is required to modify BIOS settings" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Authentication is required to modify a configured remote used for firmware updates" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Authentication is required to modify daemon configuration" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "Authentication is required to read BIOS settings" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Authentication is required to set the list of approved firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Authentication is required to sign data using the client certificate" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Authentication is required to switch to the new firmware version" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Authentication is required to unlock a device" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Authentication is required to update the firmware on a removable device" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Authentication is required to update the firmware on this machine" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Authentication is required to update the stored checksums for the device" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatic Reporting" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Automatically upload every time?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "BIOS updates delivered via LVFS or Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML FILENAME-DST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Battery" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Bind new kernel driver" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blocked firmware files:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Blocked version" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blocking firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blocks a specific firmware from being installed" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Bootloader Version" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Branch" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Build a firmware file" + +#. 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" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Cannot apply as dbx update has already been applied." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Cannot apply updates on live media" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Changed" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Checks cryptographic hash matches firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Checksum" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Choose a branch:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Choose a device:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Choose a firmware type:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Choose a release:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Choose a volume:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose the ESP:" +msgstr "Choose the ESP:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Clears the results from the last update" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Command not found" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Community supported" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Configuration Change Suggested" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "Configuration is only readable by the system administrator" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Convert a firmware file" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Created" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Critical" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Cryptographic hash verification is available" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Current Value" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Current version" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "DEVICE-ID|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU Utility" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Debugging Options" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Decompressing…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Description" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Detach to bootloader mode" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Details" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Deviate from the best known configuration?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Device Flags" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Device ID" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Device added:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Device battery power is too low" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Device battery power is too low (%u%%, requires %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Device can recover flash failures" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Device cannot be used while the lid is closed" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Device changed:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Device firmware is required to have a version check" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Device is emulated" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Device is locked" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Device is required to install all provided releases" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Device is unreachable" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Device is unreachable, or out of wireless range" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Device is usable for the duration of the update" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Device is waiting for the update to be applied" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Device removed:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Device requires AC power to be connected" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Device software updates are provided for this device." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Device stages updates" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Device supports switching to a different branch of firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Device update method" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Device update needs activation" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Device will backup firmware before installing" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Device will not re-appear after update completes" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Devices that have been updated successfully:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Devices that were not updated correctly:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Devices with no available firmware updates: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Devices with the latest available firmware version:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Did not find any devices with matching GUIDs" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Disabled" + +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 unreported history" +msgstr "Do not check for unreported history" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Do not check if download remotes should be enabled" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Do not check or prompt for reboot after update" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Do not include log domain prefix" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Do not include timestamp prefix" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Do not perform device safety checks" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Do not prompt for devices" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Do not write to the history database" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Do you understand the consequences of changing the firmware branch?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Do you want to disable this feature for future updates?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Do you want to refresh this remote now?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Do you want to upload reports automatically for future updates?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Don't prompt for authentication (less details may be shown)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Done!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Downgrade %s from %s to %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Downgrades the firmware on a device" + +#. 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 "Downgrading %s from %s to %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Downgrading %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Download a file" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Downloading…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Dump SMBIOS data from a file" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Duration" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ESP specified was not valid" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Each system should have tests to ensure firmware security." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Emulated host" + +msgid "Enable" +msgstr "Enable" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Enable firmware update support on supported systems" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Enable new remote?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Enable this remote?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Enabled" + +msgid "Enabled fwupdate debugging" +msgstr "Enabled fwupdate debugging" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Enabled if hardware matches" + +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "Encrypted" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Encrypted RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "End of life" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Enumeration" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Erase all firmware update history" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Erasing…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Exit after a small delay" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Exit after the engine has loaded" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Export a firmware file structure to XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extract a firmware blob to images" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FILE [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "FILENAME" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FILENAME CERTIFICATE PRIVATE-KEY" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FILENAME DEVICE-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "FILENAME OFFSET DATA [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FILENAME [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILENAME [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Failed" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Failed to apply update" + +#. TRANSLATORS: error message for Windows +#, c-format +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Failed to connect to Windows service, please ensure it's running." + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Failed to connect to daemon" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Failed to get pending devices" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Failed to install firmware update" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Failed to load local dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Failed to load quirks" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Failed to load system dbx" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Failed to lock" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Failed to parse arguments" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Failed to parse file" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Failed to parse flags for --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Failed to parse local dbx" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Failed to reboot" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Failed to set splash mode" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Failed to validate ESP contents" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "False" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Filename" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Filename Signature" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Filename Source" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Filename required" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Firmware Attestation" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Firmware BIOS Descriptor" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "Firmware BIOS Descriptor protects device firmware memory from being tampered with." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Firmware BIOS Region" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "Firmware BIOS Region protects device firmware memory from being tampered with." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Firmware Base URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Firmware Update D-Bus Service" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Firmware Update Daemon" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Firmware Updater Verification" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Firmware Updater Verification checks that software used for updating has not been tampered with." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Firmware Updates" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Firmware Utility" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Firmware Write Protection" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Firmware Write Protection Lock" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Firmware Write Protection protects device firmware memory from being tampered with." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Firmware attestation" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware is already blocked" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware is not already blocked" + +#. 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." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Firmware metadata last refresh: %s ago. Use --force to refresh again." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Firmware updates" + +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." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Firmware updates disabled; run 'fwupdmgr unlock' to enable" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Flags" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Force the action by relaxing some runtime checks" + +msgid "Force the action ignoring all warnings" +msgstr "Force the action ignoring all warnings" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Found" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Full Disk Encryption Detected" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Full disk encryption secrets may be invalidated when updating" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Fused Platform" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Fused platform" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Get BIOS settings" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Get all device flags supported by fwupd" + +#. 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" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Gets the configured remotes" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Gets the host security attributes" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Gets the list of approved firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Gets the list of blocked firmware" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Gets the list of updates for connected hardware" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Gets the releases for a device" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Gets the results from the last update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FILE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hardware is waiting to be replugged" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "High" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Host Security Events" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "Host Security ID (HSI) is not supported" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Host Security ID:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU Protection" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "IOMMU Protection prevents connected devices from accessing unauthorised parts of system memory." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU device protection disabled" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU device protection enabled" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Idle…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignore SSL strict checks when downloading files" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignore firmware checksum failures" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignore firmware hardware mismatch failures" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignore validation safety checks" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Install Duration" + +#. 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" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" + +msgid "Install old version of signed system firmware" +msgstr "Install old version of signed system firmware" + +msgid "Install old version of unsigned system firmware" +msgstr "Install old version of unsigned system firmware" + +msgid "Install signed device firmware" +msgstr "Install signed device firmware" + +msgid "Install signed system firmware" +msgstr "Install signed system firmware" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Install to parent device first" + +msgid "Install unsigned device firmware" +msgstr "Install unsigned device firmware" + +msgid "Install unsigned system firmware" +msgstr "Install unsigned system firmware" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Installing Firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Installing firmware update…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installing on %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Installing this update may also void any device warranty." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Integer" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard ACM Protected" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM protected" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Intel BootGuard Error Policy" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard Fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Intel BootGuard Verified Boot" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard error policy" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard prevents unauthorised device software from operating when the device is started." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard verified boot" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET Active" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET Enabled" + +#. TRANSLATORS: longer description +msgid "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Intel Management Engine Manufacturing Mode" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Intel Management Engine Override" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "Intel Management Engine Override disables checks for device software tampering." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Intel Management Engine Version" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: longer description +msgid "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Internal device" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Invalid" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Invalid arguments" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Is downgrade" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Is in bootloader mode" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Is upgrade" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Issue" +msgstr[1] "Issues" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "KEY,VALUE" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Kernel is no longer tainted" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Kernel is tainted" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Kernel lockdown disabled" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Kernel lockdown enabled" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Keyring" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOCATION" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Last modified" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Less than one minute remaining" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licence" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Linux Kernel Lockdown" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Linux Kernel Verification" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux Swap" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel lockdown" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "List entries in dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "List supported firmware updates" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "List the available firmware types" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Lists files on the ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Loading…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Locked" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Low" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI manufacturing mode" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI override" + +msgid "MEI version" +msgstr "MEI version" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Manually enable specific plugins" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Maximum length" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Maximum value" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Medium" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metadata Signature" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadata URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata can be obtained from the Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimum Version" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Minimum length" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Minimum value" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Mismatched daemon and client, use %s instead" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Modifies a daemon configuration value" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifies a given remote" + +msgid "Modify a configured remote" +msgstr "Modify a configured remote" + +msgid "Modify daemon configuration" +msgstr "Modify daemon configuration" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Monitor the daemon for events" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Mounts the ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "NOTE: This program may only work correctly as root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Needs a reboot after installation" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Needs reboot" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Needs shutdown after installation" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "New version" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "No action specified!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "No downgrades for %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "No firmware IDs found" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "No releases available" + +#. 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." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "No remotes available" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "No updatable devices" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "No updates available" + +msgid "No updates available for remaining devices" +msgstr "No updates available for remaining devices" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "No updates were applied" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Not approved" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "Not enough data was provided to make an HSI calculation." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Not found" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Not supported" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Only show single PCR value" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Only use IPFS when downloading files" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Only version upgrades are allowed" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Output in JSON format" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Override the default ESP path" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PATH" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Parse and show details about a firmware file" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Parsing dbx update…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Parsing system dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Password" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Patch a firmware blob at a known offset" + +msgid "Payload" +msgstr "Payload" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Pending" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentage complete" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Perform operation?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Platform Debugging" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Please ensure you have the volume recovery key before continuing." + +#. 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: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Plugin dependencies missing" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Possible Values" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "Pre-boot DMA Protection" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Pre-boot DMA protection" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Pre-boot DMA protection is disabled" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Pre-boot DMA protection is enabled" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Previous version" + +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" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problems" + +msgid "Proceed with upload?" +msgstr "Proceed with upload?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Processor Security Checks" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Proprietary" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Query for firmware update support" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "REMOTE-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "REMOTE-ID KEY VALUE" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Read Only" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Read a firmware blob from a device" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Read a firmware from a device" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Read firmware from device into a file" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Read firmware from one partition into a file" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Reading from %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Reading…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Rebooting…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Refresh metadata from remote server" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Reinstall %s to %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Reinstall current firmware on the device" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstall firmware on a device" + +#. 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 "Reinstalling %s with %s... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Release Branch" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Release Flags" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Release ID" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Remote ID" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Replace data in an existing firmware file" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Report URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Reported to remote server" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Request cancelled" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Required efivarfs filesystem was not found" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Required hardware was not found" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Requires a bootloader" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Requires internet connection" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Restart now?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Restart the daemon to make the change effective?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Restarting device…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Retrieve BIOS settings. If no arguments are passed all settings are returned" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Return all the hardware IDs for the machine" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Rollback protection" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Run `fwupdmgr get-upgrades` for more information." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Run `fwupdmgr sync-bkc` to complete this action." + +#. 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" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Run without '%s' to see" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Running kernel is too old" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Runtime Suffix" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "SETTING VALUE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "SETTING1 VALUE1 [SETTING2] [VALUE2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS Descriptor" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS region" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI lock" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "SPI replay protection" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI write" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "SPI write protection" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Save a file that allows generation of hardware IDs" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Scalar Increment" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Schedule installation for next reboot when possible" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Scheduling…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot disabled" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot enabled" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "See %s for more details." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "See %s for more information." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Selected device" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Selected volume" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Serial Number" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Set BIOS setting '%s' using '%s'." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Set a BIOS setting" + +msgid "Set one or more BIOS settings" +msgstr "Set one or more BIOS settings" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Set the debugging flag during update" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Sets one or more BIOS settings" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Sets the list of approved firmware" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Setting type" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Settings will apply after system reboots" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Share firmware history with the developers" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Show all results" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Show client and daemon versions" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Show daemon verbose information for a particular domain" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Show debugging information for all domains" + +#. 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" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Show history of firmware updates" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Show plugin verbose information" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Show the calculated version of the dbx" + +#. 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Sign a firmware with a new key" + +msgid "Sign data using the client certificate" +msgstr "Sign data using the client certificate" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Sign data using the client certificate" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Sign the uploaded data with the client certificate" + +msgid "Signature" +msgstr "Signature" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Signed Payload" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Size" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Some of the platform secrets may be invalidated when updating this firmware." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Source" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Specify Vendor/Product ID(s) of DFU device" + +#. TRANSLATORS: command line option +msgid "Specify a filename to use to save backend events" +msgstr "Specify a filename to use to save backend events" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Specify the dbx database file" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Specify the number of bytes per USB transfer" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "String" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Success" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Successfully activated all devices" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Successfully disabled remote" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Successfully downloaded new metadata: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Successfully enabled and refreshed remote" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Successfully enabled remote" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Successfully installed firmware" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Successfully modified configuration value" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Successfully modified remote" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Successfully refreshed metadata manually" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Successfully updated device checksums" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Successfully uploaded %u report" +msgstr[1] "Successfully uploaded %u reports" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Successfully verified device checksums" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Summary" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Supported" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Supported CPU" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Supported on remote server" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Suspend To Idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Suspend To RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-ram" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Switch branch from %s to %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Switch the firmware branch on the device" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Sync firmware versions to the host best known configuration" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "System power is too low to perform the update" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "System power is too low to perform the update (%u%%, requires %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "System requires external power source" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 reconstruction" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0 reconstruction is invalid" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "TPM PCR0 reconstruction is now valid" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "TPM Platform Configuration" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "TPM Reconstruction" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "TPM empty PCRs" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Tag" +msgstr[1] "Tags" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Tainted" + +msgid "Target" +msgstr "Target" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Test a device using a JSON manifest" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." + +#. 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: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "The TPM PCR0 differs from reconstruction." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "The UEFI Platform Key is used to determine if device software comes from a trusted source." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "The device version did not match: got %s, expected %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "The firmware from %s is not supplied by %s, the hardware vendor." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "The system clock has not been set correctly and downloading files may fail." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "The vendor did not supply any release notes." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "There are devices with issues:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "There are no blocked firmware files" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "There is no approved firmware." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "This device will be reverted back to %s when the %s command is performed." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "This package has not been validated, it may not work properly." + +#. 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 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: error message +msgid "This system doesn't support firmware settings" +msgstr "This system doesn't support firmware settings" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "This system has HSI runtime issues." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "This system has a low HSI security level." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "This tool allows an administrator to apply UEFI dbx updates." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "This tool allows an administrator to debug UpdateCapsule operation." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "This tool allows an administrator to use the fwupd plugins without being installed on the host system." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "This tool can only be used by the root user" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "This tool will read and parse the TPM event log from the system firmware." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "To ignore this warning, use --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Transient failure" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "True" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Trusted metadata" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Trusted payload" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Type" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP partition not detected or configured" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI Firmware Utility" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI Platform Key" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI Secure Boot" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "UEFI Secure Boot prevents malicious software from being loaded when the device starts." + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI capsule updates not available or enabled in firmware setup" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx Utility" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI firmware can not be updated in legacy BIOS mode" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI platform key" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Unable to connect to service" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Unable to find attribute" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Unbind current driver" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Unblocking firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Unblocks a specific firmware from being installed" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Unencrypted" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Unknown" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Unknown Device" + +msgid "Unlock the device to allow access" +msgstr "Unlock the device to allow access" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Unlocked" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Unlocks the device for firmware access" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Unmounts the ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Unset the debugging flag during update" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Unsigned Payload" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Unsupported daemon version %s, client version is %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Untainted" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Updatable" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Update Error" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Update Image" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Update Message" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Update State" + +#. 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?" +msgstr "Update now?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Update requires a reboot" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Update the stored cryptographic hash with current ROM contents" + +msgid "Update the stored device verification information" +msgstr "Update the stored device verification information" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Update the stored metadata with current contents" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Updates all specified devices to latest firmware version, or all devices if unspecified" + +msgid "Updating" +msgstr "Updating" + +#. 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 "Updating %s from %s to %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Updating %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Upgrade %s from %s to %s?" + +#. 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: how important the release is +msgid "Urgency" +msgstr "Urgency" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Use fwupdmgr --help for help" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Use fwupdtool --help for help" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Use quirk flags when installing firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "User has been notified" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Username" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valid" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validating ESP contents…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variant" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Vendor" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verifying…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "WARNING:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Waiting…" + +#. 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" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Write firmware from file into one partition" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Writing file:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Writing…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." + +#. 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." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Your system is set up to the BKC of %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[DEVICE-ID|GUID] [BRANCH]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[DEVICE-ID|GUID] [VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FILE FILE_SIG REMOTE-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[FILENAME1] [FILENAME2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[SETTING1] [ SETTING2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[SETTING1] [SETTING2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FILE|HWIDS-FILE]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "default" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM event log utility" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd plugins" diff --git a/fwupd-1.8.6/po/eo.po b/fwupd-1.8.6/po/eo.po new file mode 100644 index 0000000000000000000000000000000000000000..4ae925c6a975f3bb28f568c8b9eb11f232753b9c --- /dev/null +++ b/fwupd-1.8.6/po/eo.po @@ -0,0 +1,89 @@ +# 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: multiline description of device +msgid "Description" +msgstr "Priskribo" + +#. success +msgid "Done!" +msgstr "Farita!" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Trovita" + +msgid "ID" +msgstr "ID" + +msgid "Mode" +msgstr "Reĝimo" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +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" diff --git a/fwupd-1.8.6/po/es.po b/fwupd-1.8.6/po/es.po new file mode 100644 index 0000000000000000000000000000000000000000..c22944b729dcec628eb130d56ea76ffe69931910 --- /dev/null +++ b/fwupd-1.8.6/po/es.po @@ -0,0 +1,2586 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Adolfo Jayme-Barrientos, 2021 +# Adolfo Jayme-Barrientos, 2021 +# Francisco Serrador, 2022 +# Francisco Serrador , 2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Spanish (http://www.transifex.com/freedesktop/fwupd/language/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Actualización %s BMC" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Actualización %s de Batería" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Actualización %s de microcódigo CPU" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Actualización %s de Cámara" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Actualización %s de Configuración" + +#. 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 "Actualización %s ME del Cliente" + +#. 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 "Actualización %s del Controlador" + +#. 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 "Actualización %s ME de Fabricante" + +#. 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ón %s de Dispositivo" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Actualización %s de Pantalla" + +#. 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 "Actualización %s de Controlador Empotrado" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Actualización %s de teclado" + +#. 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 "Actualización %s ME" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Actualización %s del ratón" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Actualización %s del Interfaz de Red" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Actualización %s del Controlador de Almacenaje" + +#. 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ón %s del Sistema" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Actualización %s de TPM" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Actualización %s del Controlador Thunderbolt" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Actualización %s de Touchpad" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "Receptor USB %s Actualizado" + +#. 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 "Actualización %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s y todos los dispositivos conectados tal vez no están utilizables mientras se actualizan." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s ha aparecido: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s ha cambiado: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s ha desaparecido: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s no es actualizable actualmente" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s modo de fabricante" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s debe permanecer conectado para la duración de la actualización para evitar daño." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s debe permanecer enchufado en un agente de alimentación para la duración de la actualización para evitar daño." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s sobrecarga" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Version %s" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u dispositivo tiene una actualización disponible." +msgstr[1] "%u dispositivos tienen una actualización disponible." +msgstr[2] "%u dispositivos tienen una actualización disponible." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u dispositivo no es la mejor configuración conocida." +msgstr[1] "%u dispositivos no son la mejor configuración conocida" +msgstr[2] "%u dispositivos no son la mejor configuración conocida." + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u segundo" +msgstr[1] "%u segundos" +msgstr[2] "%u segundos" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (umbral %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleto)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "Un PCR TRM ahora es un valor no válido" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Operación requerida:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Activar dispositivos" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Activar dispositivos pendientes" + +msgid "Activate the new firmware on the device" +msgstr "Activa el firmware nuevo en el dispositivo" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Activando actualización del firmware" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Activando actualización del firmware para" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Edad" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "¿Acuerda y habilita el remoto?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias para %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Todos los PCR TMP son válidos ahora" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Todos los PCR TMP son válidos" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Todos los dispositivos del mismo tipo serán actualizados a la vez" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permitir bajar versiones de firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permitir reinstalar versiones de firmware existentes" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Permitir intercambiar rama de firmware" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Ramificación alterna" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Una actualización requiere un rearranque para completarla." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Una actualización requiere que el sistema se apague para completarla." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Responda sí a todas las preguntas" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplica actualiaciones de firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplica actualización incluso cuando no sea advertido" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplicar archivos de actualización" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aplicando actualización…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware aprobado" +msgstr[1] "Firmware aprobado" +msgstr[2] "Firmware aprobado" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "¿Quiere solicitar de nuevo la siguiente vez?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Acoplar al modo firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autenticando…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Detalles requeridos para la autenticación" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Se requiere autenticación para bajar la versión del firmware en un dispositivo extraíble" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Se requiere autenticación para bajar de versión el firmware en esta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Se requiere autenticación para modificar un remoto configurado para actualizaciones de firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Se requiere autenticación para modificar la configuración del demonio" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "La autenticación es requisito para fijar el listado de firmware aprobado" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Se requiere autenticación para firmar datos utilizando el certificado cliente" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Se requiere autenticación para cambiar a la versión nueva del firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "La autenticación es requerida para desbloquear un dispositivo" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Se requiere autenticación para actualizar el firmware en un dispositivo extraíble" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Autenticación es requerida para actualizar el firmware en esta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Se requiere autenticación para actualizar el comprobante sumatorio almacenado para el dispositivo" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Informes Automáticos" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "¿Subo automáticamente cada vez?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML NOMBREARCHIVO-DST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Batería" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincula nuevo controlador del kernel" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Archivos de firmware bloqueados:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Versión bloqueada" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Firmware bloqueado:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Bloquea un origen del firmware específico siendo instalado" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versión del Arranque" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Rama" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Compila un archivo firmware" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancelar" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Cancelado" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "No se puede aplicar como actualización dbx ya ha sido aplicado." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "No se puede aplicar actualizaciones en medio vivo" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Cambiado" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Comprueba coincidencias hash criptográficas del firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Sumatorio" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Escoja una rama:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Elija un dispositivo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Elija un tipo de firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Elija una publicación:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Escoja un volumen:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Vacía el resultado desde la última actualización" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "No se encontró la orden" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Comunitariamente mantenido" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Convierta un archivo firmware" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Creado" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Crítica" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Hay disponible verificación hash criptográfica" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versión actual" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "DEVICE-ID|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilidad MDF" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opciones de depuración" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Descomprimiendo…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descripción" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Suelta un modo de cargador de arranque" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detalles" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "¿Desviado desde la configuración mejor conocida?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Indicadores del Dispositivo" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ID de dispositivo" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Dispositivo añadido:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "La carga de batería del dispositivo es muy baja" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "La carga de la batería del dispositivo es muy baja (%u%%, requiere %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "El dispositivo puede recuperar errores de flash" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Dispositivo no puede ser utilizado mientras la tapa está cerrada" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositivo modificado:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "El dispositivo firmware es necesario para hacer una comprobación de versión" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "El dispositivo está bloqueado" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "El dispositivo es requerido para instalar todas las publicaciones proporcionadas" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Dispositivo no alcanzable" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "El dispositivo no es alcanzable, o está fuera del conexión sin cableado" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "El dispositivo es utilizable para la duración de la actualización" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "El dispositivo está esperando a que se actualice para ser aplicado" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositivo extraído:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "El dispositivo requiere electricidad AC para conectarlo" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Escenarios de actualizaciones del dispositivo" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Dispositivo mantiene intercambio a una rama de firmware diferente" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Método para actualizar firmware" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Actualización de dispositivo requiere activación" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Dispositivo respaldará el firmware antes de instalar" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Dispositivo no re-aparecerá tras actualización termine" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositivos que han sido actualizados correctamente:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Dispositivos que no fueron actualizados correctamente:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositivos sin actualizaciones del firmware disponible: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositivos con la última versión disponible del firmware:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "No encontró ningún dispositivo con los GUID coincidan" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Deshabilitado" + +msgid "Disabled fwupdate debugging" +msgstr "Deshabilitó depuración de fwupdate" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Desactiva un remoto proporcionado" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Representar la versión" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "No marcar para metadatos antiguos" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "No marcar para historial no comunicado" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "No marcar si las descargas remotas serían activadas" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "No marque o solicite para rearrancar tras actualizar" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "No incluye prefijo del boletín de dominio" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "No incluye prefijo de hora" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "No realiza comprobaciones seguras del dispositivo" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "No se solicita para dispositivos" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "No se lee a la base de datos histórica" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "¿Comprende las consecuencias de cambiar la rama del firmware?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "¿No desea deshabilitar esta característica para actualizaciones futuras?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "¿Quiere recargar ahora este remoto?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "¿Desea subir repositorios automáticamente para actualizaciones futuras?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "¡Hecho!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "¿Degradar %s desde %s a %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Degrada el firmware en un 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 "Degradando %s desde %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Degradando %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Descargar un archivo" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Descargando…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Vuelca datos SMBIOS desde un archivo" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Duración" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ESP especificado no fue válido" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Activa mantenimiento de la actualización del firmware sobre los sistemas mantenidos" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "¿Habilitar remoto nuevo?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "¿Habilitar este remoto?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Habilitado" + +msgid "Enabled fwupdate debugging" +msgstr "Depuración de fwupdate activada" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Habilitado si coincide con el hardware" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Activa un remoto proporcionado" + +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 "Active esta funcionalidad bajo su responsabilidad, lo que significa que tiene que contactar con el fabricante del equipo original ante cualquier problema causado por estas actualizaciones. Solo se deben reportar los problemas con el mismo proceso de actualización en $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Habilitar este remoto es realizado a su propio riesgo." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Cifrado" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM cifrada" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Final de vida" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Elimina todo el historial de actualización del firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Eliminando…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Salir después de una pequeña pausa" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Salir después que sea cargado el motor" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Exporta una estructura del archivo firmware a XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extrae una gota de firmware a imágenes" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "ARCHIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "ARCHIVO [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NOMBRE-DE-ARCHIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "ID-ARCHIVO CERTIFICA LLAVE-PRIVADA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NOMBRE-ARCHIVO DISP.-ALT-NOMBRE|DISP.-ALT-ID [IMAGEN-ALT-NOMBRE|IMAGEN-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "ID-NOMBRE ID-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "NOMBRE-ARCHIVO DESPLAZAMIENTO [TIPO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NOMBRE-ARCHIVO [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NOMBRE-ARCHIVO [TIPO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NOMRE-ARCHIVO-SRC NOMBRE-ARCHIVO-DST [TIPO-FIRMWARE-SRC] [TIPO-FIRMWARE-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "ID-ARCHIVO|COMPROBANTE1[,COMPROBANTE2][,COMPROBANTE3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Erróneo" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Error al aplicar actualización" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Erróneo al conectar al demonio" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Erróneo al obtener dispositivos pendientes" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Erróneo al instalar actualización de firmware" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Erróneo en carga dbx local" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Error al cargar soluciones alternativas" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Erróneo en carga dbx del sistema" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Erróneo al bloquear" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "No se pudieron procesar los argumentos" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "No se pudo procesar el archivo" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Erróneo al interpretar indicadores para --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Erróneo al interpretar dbx local" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Erróneo al rearrancar" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Erróneo al fijar el modo presentación" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Error al validar el contenido de ESP" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "ID del archivo" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Firma del ID de archivo" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Nombre del Archivo Origen" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Se requiere el nombre de archivo" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtro con un conjunto de indicadores del dispositivo utilizando un prefijo ~ a excluir, p. ej. 'internet, ~necesita-rearrancar'" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI de Firmware Base" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Actualizar Firmware de Servicio D-Bus" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Demonio de Actualización de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilidad Firmware" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Garantía de firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware ya está bloqueado" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware aún no está bloqueado" + +#. 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] "Los metadatos de firmware no han sido actualizados para %u día y puede no estar al día." +msgstr[1] "Los metadatos de firmware no han sido actualizados para %u días y puede no estar al día." +msgstr[2] "Los metadatos de firmware no han sido actualizados para %u días y puede no estar al día." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Última recarga de metadatos del firmware: %s antes. Emplee --force para recargar de nuevo." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Actualizaciones del firmware" + +msgid "Firmware updates are not supported on this machine." +msgstr "Las actualizaciones de firmware no están mantenidos en esta máquina." + +msgid "Firmware updates are supported on this machine." +msgstr "Las actualizaciones de firmware están mantenidas en esta máquina." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Actualizaciones firmware desactivada; ejecute 'fwupdmgr unlock' para habilitar" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Indicadores" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Fuerza la acción relajando algunas comprobaciones en tiempo de ejecución" + +msgid "Force the action ignoring all warnings" +msgstr "Fuerza la operación descartando todos los avisos" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Encontrado" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Detectado Cifrado de Disco Completo" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Secretos de cifrado de disco completo tal vez está invalidado cuando modernice" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Plataforma fusionada" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Obtenga todos los indicadores del dispositivo admitido por fwpd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obtiene todos los dispositivos que mantengan actualizaciones de firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obtiene todos los complementos registrados activados con el sistema" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obtiene detalles acerca de un archivo firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Obtiene los remotos configurados" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obtiene los atributos de seguridad del hospedaje" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Obtiene el listado de firmware aprobado" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Obtiene el listado de firmware bloqueado" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Obtiene el listado de actualizaciones para hardware conectado" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Obtiene las publicaciones para un dispositivo" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Obtiene los resultados desde la última actualización" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-ARCHIVO" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "El hardware está esperando que sea enchufado" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Alta" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Eventos de Seguridad de Hospedaje" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de seguridad de hospedaje:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Protección de dispositivo IOMMU desactivado" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Protección de dispositivo IOMMU activado" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Inactivo…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Descarta comprobaciones estrictas de SSL cuando descargue archivos" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Hace caso omiso a errores del sumatorio del firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Hace caso omiso a errores del concordancia del hardware" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Hace caso omiso a comprobaciones validación de seguridad" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Descartando comprobaciones estrictas SSL, para hacer esto automáticamente en la futura exportación DISABLE_SSL_STRICT dentro de su entorno" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Duración de Instalación" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instala una gota del firmware en un dispositivo" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instala un archivo de firmware en este hardware" + +msgid "Install old version of signed system firmware" +msgstr "Instala versión antigua de firmware del sistema firmado" + +msgid "Install old version of unsigned system firmware" +msgstr "Instalar una versión antigua de firmware del sistema no firmado" + +msgid "Install signed device firmware" +msgstr "Instalar firmware del dispositivo firmado" + +msgid "Install signed system firmware" +msgstr "Instalar firmware del sistema firmado" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instala primero el dispositivo antecedente" + +msgid "Install unsigned device firmware" +msgstr "Instalar firmware del dispositivo no firmado" + +msgid "Install unsigned system firmware" +msgstr "Instalar firmware del sistema sin firmar" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalando Firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Instalando actualización del firmware…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instalando en %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Instalando esta actualización tal vez anule cualquier garantía del dispositivo." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protegido Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Error de normativa Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Arranque verificado Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET Activo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET Habilitada" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Dispositivo interno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "No válida" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Está degradada" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Está dentro del modo cargador de arranque" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Está modernizada" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "CLAVE,VALOR" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "El Kernel no está manoseado por más tiempo" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "El Kernel está manoseado" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Bloqueo desactivado del kernel" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Bloqueo activado del kernel" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Anillo" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOCALIZACIÓN" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Último modificado" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Falta menos de un minuto" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Reconocimiento" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "«Linux Vendor Firmware Service» (firmware estable)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "«Linux Vendor Firmware Service» (firmware de pruebas)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Kernel Linux bloqueado" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Intercambio (swap) de Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Listado de apuntes en dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Enumera actualizaciones de firmware mantenidas" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Enumera los tipos de firmware disponibles" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Enumera archivos en la ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Cargando…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloqueado" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Baja" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modo manufacturante MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Sobrecarga MEI" + +msgid "MEI version" +msgstr "Versión MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Activa manualmente complementos específicos" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Media" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Firma Metadatos" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI Metadatos" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadatos pueden obtenerse desde el Servicio de Firmware del Proveedor Linux." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versión Mínima" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "No coincidió demonio y cliente, utilice %s en su lugar" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Codifica un valor de configuración del demonio" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifica un remoto proporcionado" + +msgid "Modify a configured remote" +msgstr "Modifica un remoto configurado" + +msgid "Modify daemon configuration" +msgstr "Modifica la configuración del demonio" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Monitoriza al demonio para eventos" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monta el ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "NOTA: este programa tal vez solamente funciona correctamente como root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Necesita rearrancar tras la instalación" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Requiere reinicio" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Necesita apagar tras la instalación" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Versión nueva" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "No se especificó ninguna acción." + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Ninguna degradación para %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Ningún ID de firmware encontrado" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Ningún hardware detectado con capacidad de actualización del firmware" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Ningún complemento encontrado" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Ninguna liberación disponible" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Ningunos remotos están activados actualmente tal que ningún metadato está disponible." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Ningún remoto disponible" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Ningún dispositivo actualizable" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "No hay actualizaciones disponibles" + +msgid "No updates available for remaining devices" +msgstr "No hay actualizaciones disponibles para dispositivos restantes" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Ninguna actualización fue aplicada" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "No aprobado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "No encontrado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "No compatible" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Aceptar" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "Aceptar" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Solamente muestra un valor único de PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Solamente utiliza IPFS cuando descargue archivos" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Solamente las modernizaciones de versión están permitidas" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Formato de salida en JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Supera la ruta del ESP predeterminado" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "RUTA" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Interpreta y muestra detalles acerca de un archivo de firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Interpretando actualización dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Interpretando sistema dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Contraseña" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Parchea una gota de firmware en un desplazamiento conocido" + +msgid "Payload" +msgstr "Carga útil" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Pendiente" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Porcentaje completo" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "¿Realizar operación?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Depuración de Plataforma" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Asegure que tenga la llave de recuperación del volumen antes de continuar." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Introduzca un número desde 0 hasta %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dependencias ausentes del complemento" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protección DMA de pre-arranque" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Protección DMA de pre-arranque está desactivada" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Protección DMA de pre-arranque está activada" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Versión anterior" + +msgid "Print the version number" +msgstr "Declara el número de la versión" + +msgid "Print verbose debug statements" +msgstr "Declara enunciados detallados de depuración" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioridad" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problemas" + +msgid "Proceed with upload?" +msgstr "¿Proceder con subida?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Titular" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Solicita actualizaciones de mantenimiento del firmware" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ID-REMOTO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ID-REMOTO CLAVE VALOR" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Lea una gota del firmware desde un dispositivo" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Lea un firmware desde un dispositivo" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Lea firmware desde el dispositivo al archivo" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Lea el firmware desde una partición a un archivo" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Leyendo desde %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Leyendo…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Reiniciando…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Recarga metadatos desde servidor remoto" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "¿Reinstalar %s a %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Reinstala firmware actual en el dispositivo" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstalar firmware en un dispositivo" + +#. 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 "Reinstalar %s con %s... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Ramificación de Publicación" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Indicadores de Publicación" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID Publicación" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID Remoto" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Remplaza datos dentro de un archivo firmware existente" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI de Comunicado" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Comunicado para servidor remoto" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Sistema de archivos efivarfs requerido no fue encontrado" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Hardware requerido no fue encontrado" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Requiere un cargador de arranque" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Requiere conexión a Internet" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "¿Reiniciar ahora?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "¿Reinicio el demonio para crear el cambio efectivo?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Reiniciando dispositivo…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Devuelve todos los ID de hardware para la máquina" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Retrocede protección" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Ejecute `fwupdmgr get-upgrades` para más información." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Ejecute `fwupdmgr sync-bkc` para completar esta operación." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Ejecuta el complemento compuesto vacía rutina cuando utiliza gota de instalación" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Ejecute el complemento compuesto de rutina preparada cuando utilice install-blob" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Ejecutando kernel que es demasiado antiguo" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufijo de tiempo de ejecución" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Descriptor BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Región BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Candado SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "SPI solicita protección" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escritura SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Protección de escritura SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "CONTROLADOR DE SUBSISTEMA [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Guarda un archivo que permita generación de los ID hardware" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Planifica instalación para siguiente rearranque cuando sea posible" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planificando…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Arranque Seguro desactivado" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Arranque Seguro activado" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Vea %s para más detalles." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Vea %s para más información." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Dispositivo seleccionado" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volumen seleccionado" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Número de Serie" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Fija el indicador de depuración durante la actualización" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Fija el listado de firmware aprobado" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Compartir historial de firmware con los desarrolladores" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Muestra todos los resultados" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Mostrar versiones del cliente y el servicio" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Muestra verborrea de información del demonio para un dominio particular" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Muestra información de depuración para todos los dominios" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostrar información extra de depuración" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Muestra dispositivos que no son actualizables" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostrar información de depuración adicional" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Muestra historial de actualizaciones del firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Muestra información de complementos verborreos" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Muestra la versión calculada del dbx" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Muestra el boletín de depuración desde la última actualización intentada" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Muestra la información del estado de actualización del firmware" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "¿Apago ahora?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Firma un firmware con una llave nueva" + +msgid "Sign data using the client certificate" +msgstr "Datos de firma utilizando el certificado cliente" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Datos de firma utilizando el certificado cliente" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Firma los datos subidos con el certificado del cliente" + +msgid "Signature" +msgstr "Firma" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Carga Firmada" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Tamaño" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Alguno de los secretos de la plataforma tal vez sea invalidada cuando actualice este firmware." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Origen" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifica los ID(s) de Proveedor/Fabricante del dispositivo DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Específicamente el archivo de la base de datos sbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifique el número de bytes por transferencia USB" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Correcto" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Se activaron correctamente todos los dispositivos" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remoto desactivado correctamente" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Descarga correctamente metadatos nuevos: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Remoto activado y recargado correctamente" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remoto activado correctamente" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware instalado correctamente" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Valor de configuración modificado correctamente" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remoto modificado correctamente" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadatos actualizados manualmente correctamente" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Sumatorio de dispositivo actualizado correctamente" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Se ha subido %u parte correctamente" +msgstr[1] "Se han subido %u partes correctamente" +msgstr[2] "Se han subido %u partes correctamente" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Comprobación correctamente verificada del dispositivo" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Totales" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Mantenido" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "CPU admitida" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Mantenido en servidor remoto" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspendido-a-descanso" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspender a RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "¿Ramificación intercambiada desde %s hasta %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Intercambiar rama firmware en el dispositivo" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Sincroniza versiones firmware a la mejor configuración conocida en el hospedaje" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Energía del sistema es demasiado baja para realizar la actualización" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "La carga del sistema es muy baja para realizar una actualización (%u%%, requiere %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Sistema requiere energía externa" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXTO" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrucción PCR0 de TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "Reconstrucción PCR0 de TPM no es válida" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "Reconstrucción PCR0 de TPM ahora es válida" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Los PCR vacíos de TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Etiqueta" +msgstr[1] "Etiquetas" +msgstr[2] "Etiquetas" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Manoseado" + +msgid "Target" +msgstr "Destino" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testea un dispositivo utilizando una declaración JSON" + +#. 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 es un servicio libre que opera como una entidad independiente legal y no tiene contacto con $OS_RELEASE:NAME$. Su distribuidor puede no haber verificado ninguna de las actualizaciones del firmware por compatibilidad con su sistema o dispositivos conectados. Todo el firmware se distribuye únicamente a través el fabricante del equipo original." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "El PCR0 TPM difiere desde reconstrucción." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "¡El demonio ha cargado código de 3ª parte y ya no está mantenido por los desarrolladores actuales!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "La versión del dispositivo no coincidió: obtuvo %s, esperaba %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "El firmware desde %s no está suministrado por %s, el fabricante del hardware." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "El reloj del sistema no ha sido fijado correctamente y los archivos descargados pueden fallar." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "El proveedor no admite ninguna nota de publicación." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Hay dispositivos con problemas:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "No hay archivos firmware bloqueados" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "No hay ningún firmware aprobado." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Este dispositivo será revertido a %s cuando la instrucción %s sea realizada." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Este firmware se proporcionó por miembros de la comunidad LVFS y no proporcionó (o mantuvo) por el proveedor del hardware original." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Este paquete no ha sido validado, quizá no funciona apropiadamente." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Este programa solamente puede funcionar correctamente como 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 "Este remoto contiene firmware el cual no está embargado, pero aún está siendo probado por el proveedor de hardware. Debería asegurarse que tenga una manera de degradar el firmware manualmente si falla la actualización del firmware." + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Este sistema tiene problemas HSI en tiempo de ejecución." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Este sistema tiene un nivel de seguridad HSI bajo." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Esta herramienta permite un administrados para aplicar actualizaciones dbx de UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Esta herramienta permite un administrador depura operación UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Esta herramienta permite un administrados para solicitar y controla el demonio fwupd, permitiéndolos entonces realizar acciones tales como instalar o bajar la versión del firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Esta herramienta permite que un administrador utilice los complementos fwupd sin ser instalados en el sistema hospedado." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Esta herramienta solo puede ser utilizado por el usuario root" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Esta herramienta leerá y analizará sintácticamente el boletín de evento TPM desde el firmware del sistema." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Transición errónea" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Metadatos confiados" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Carga confiada" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipo" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partición ESP UEFI no detectada o configurada" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilidad Firmware de UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Actualizaciones de cápsulas UEFI no disponible o activada en configuración de firmware" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilidad dbx UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Firmware UEFI no puede ser actualizado en modo de BIOS heredada" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Clave UEFI de la plataforma" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Arranque seguro UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Imposible conectar al servicio" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Desvincula controlador actual" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Desbloquear firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Desbloquea un firmware específico para que sea instalado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Descifrado" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Desconocida" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositivo Desconocido" + +msgid "Unlock the device to allow access" +msgstr "Desbloquea el dispositivo para permitir acceso" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desbloqueado" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Desbloquea el dispositivo para acceso al firmware" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Desmontajes de ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Suelta el indicador de depuración durante la actualización" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Carga No Firmada" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Versión %s no mantenida del demonio, versión de cliente es %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "No manoseado" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Actualizable" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Actualizar Error" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Actualizar Imagen" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Actualizar Mensaje" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Actualizar Estado" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Error de actualización es un problema conocido, visite este URL para más información:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "¿Actualizar ahora?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Actualización requiere un rearranque" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Actualiza el hash criptográfico almacenado con el contenido de la actual ROM" + +msgid "Update the stored device verification information" +msgstr "Actualiza la información de verificación del dispositivo almacenado" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Actualiza los metadatos almacenados con contenido actual" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Actualiza todos los dispositivos especificados a la última versión del firmware, o todos los dispositivos si no especificados" + +msgid "Updating" +msgstr "Actualizando" + +#. 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 "Actualizar %s desde %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Actualizando %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "¿Moderniza %s desde %s hasta %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "¿Carga informe ahora?" + +#. 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 "Subiendo comunicados firmware ayuda proveedores de hardware para identificar rápidamente fallos y actualizaciones correctas en dispositivos reales." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgencia" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Emplee fwupdmgr --help para ayuda" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Emplee fwupdtool --help para ayuda" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Emplee indicadores quirk cuando instale firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "El usuario ha sido notificado" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "ID de Usuario" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Válido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validación de contenidos ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variante" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Proveedor" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verificando…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versión" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "AVISO:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Esperando…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Vigía para cambios del hardware" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Escribe el firmware desde archivo al dispositivo" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Escribe el firmware desde archivo a una partición" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Escribiendo archivo:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Escribiendo…" + +#. 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 "Su distribuidor tal vez no ha verificado ninguna de las actualizaciones de firmware para compatibilidad con su sistema o dispositivos conectados." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Su hardware tal vez está dañado utilizando este firmware, e instalando esta publicación tal vez anula cualquier garantía con %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Su sistema está actualizado a la BKC de %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[COMPROBANTE-SUMATORIO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID-DISPOSITIVO|GUID] [RAMIFICACIÓN]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ID-DISPOSITIVO|GUID] [VERSIÓN]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[ARCHIVO FIRMA_ARCH ID-REMOTO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[ARCHIVO-NOMBRE1] [ARCHIVO-NOMBRE2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[ARCHIVO-SMBIOS|ARCHIVO-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "por defecto" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM de evento de utilidad de boletín" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "complementos fwpd" diff --git a/fwupd-1.8.6/po/eu.po b/fwupd-1.8.6/po/eu.po new file mode 100644 index 0000000000000000000000000000000000000000..5614fddecce002642ea25eb979dbf2b1418a72a1 --- /dev/null +++ b/fwupd-1.8.6/po/eu.po @@ -0,0 +1,61 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# assar , 2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: eu\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Gehitua" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Aldatua" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Ez da komandoa aurkitu" + +#. TRANSLATORS: detected a DFU device +msgid "Found" +msgstr "Aurkitua" + +#. TRANSLATORS: Appstream ID for the hardware type +msgid "ID" +msgstr "IDa" + +#. TRANSLATORS: interface name, e.g. "Flash" +#. TRANSLATORS: device name, e.g. 'ColorHug2' +#. TRANSLATORS: section header for the release name +msgid "Name" +msgstr "Izena" + +#. TRANSLATORS: DFU protocol version, e.g. 1.1 +msgid "Protocol" +msgstr "Protokoloa" + +#. TRANSLATORS: these are areas of memory on the chip +msgid "Region" +msgstr "Eskualdea" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Kendua" + +#. TRANSLATORS: probably not run as root... +#. TRANSLATORS: device has failed to report status +#. TRANSLATORS: device status, e.g. "OK" +msgid "Status" +msgstr "Egoera" diff --git a/fwupd-1.8.6/po/fi.po b/fwupd-1.8.6/po/fi.po new file mode 100644 index 0000000000000000000000000000000000000000..c62a2b105076160fa6853544613aad2f229d4988 --- /dev/null +++ b/fwupd-1.8.6/po/fi.po @@ -0,0 +1,2607 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Jiri Grönroos , 2017-2018,2020-2022 +# Kimmo Kujansuu , 2019-2022 +# Timo Jyrinki , 2021 +# Ville-Pekka Vainio , 2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: fi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minuutti jäljellä" +msgstr[1] "%.0f minuuttia jäljellä" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Laitteen %s akkupäivitys" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Laitteen %s suorittimen mikrokoodipäivitys" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Laitteen %s kamerapäivitys" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Laitteen %s asetusten päivitys" + +#. 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 "Laitteen %s kuluttajan 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 "\"%s\"-ohjaimen 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 "Laitteen %s yrityksen 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 "Laitteen %s laitepä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 "Laitteen %s sulautetun ohjaimen päivitys" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Laitteen %s näppäimistö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 "Laitteen %s ME-päivitys" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Laitteen%s hiiripäivitys" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "”%s” -verkkolaitteen päivitys" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "”%s” -tallennustilaohjaimen 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 "Laitteen %s järjestelmäpäivitys" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Laitteen%s TPM-päivitys" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Laitteen %s Thunderbolt-ohjaimen päivitys" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Laitteen %s kosketuslevypä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 "Laitteen %s päivitys" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s ja kaikki kytketyt laitteet eivät välttämättä ole käyttökelpoisia päivityksen aikana." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s näkyy: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s muutettu: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s kadonnut: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s ei ole tällä hetkellä päivitettävissä" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s-tuotantotila (manufacturing mode)" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s on oltava kytkettynä päivityksen ajaksi vaurioiden välttämiseksi." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s on oltava kytkettynä virtalähteeseen päivityksen ajaksi vaurioiden välttämiseksi." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s ohita" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s-versio" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u päivä" +msgstr[1] "%u päivää" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u laitteessa on saatavilla firmware -päivitys." +msgstr[1] "%u laitteelle on saatavilla laiteohjelmistopäivitys." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u laitteet eivät ole tunnetuin kokoonpano." +msgstr[1] "%u laitteet eivät ole tunnetuin kokoonpano." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u tunti" +msgstr[1] "%u tuntia" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%upaikallisia laitteita tuettu" +msgstr[1] "%utuettua paikallista laitetta" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuutti" +msgstr[1] "%u minuuttia" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekunti" +msgstr[1] "%u sekuntia" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(vanhentunut)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "TPM PCR on nyt virheellisellä arvolla" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Toimia vaaditaan:" + +#. 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" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Laiteohjelmistopäivityksen aktivointi" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Käynnistetään laiteohjelmiston päivitys" + +#. 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äksytäänkö ja otetaanko lähde käyttöön?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Toinen nimi komennolle %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Kaikki TPM PCR:t ovat nyt kelvollisia" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Kaikki TPM PCR:t ovat kelvollisia" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Kaikki samantyyppiset laitteet päivitetään samaan aikaan" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Salli laiteohjelmistoversioiden alentaminen" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Salli laiteohjelmiston nykyisten versioiden asentaminen uudelleen" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Salli laiteohjelmiston haaran vaihtaminen" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Vaihtoehtoinen haara" + +#. 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 line option +msgid "Apply firmware updates" +msgstr "Toteuta laiteohjelmistopäivitykset" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Asenna päivitys myös silloin, kun sitä ei suositella" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Ota päivitystiedostot käyttöön" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Asennetaan päivitystä..." + +#. 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: stop nagging the user +msgid "Ask again next time?" +msgstr "Kysytäänkö uudestaan ensi kerralla?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Liity laiteohjelmistotilaan" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Tunnistaudutaan…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Todennus vaaditaan" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Erillisen laitteen laiteohjelmiston version alentaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Tämän järjestelmän laiteohjelmiston version alentaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "BIOS-asetusten muuttaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Laiteohjelmistojen päivityslähteen asetusten muokkaaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Taustaprosessin asetusten muokkaaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "BIOS-asetusten lukeminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Hyväksyttyjen laiteohjelmistojen luettelon tallentaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Tietojen allekirjoittaminen asiakaan sertifikaatin avulla vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Uuden laiteohjelmistoversion käyttöönotto vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Laitteen lukituksen avaaminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Erillisen laitteen laiteohjelmiston päivittäminen vaatii tunnistautumisen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Tämän järjestelmän laiteohjelmiston 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 "Tallennettujen tarkistussummien päivitys vaatii tunnistautumisen" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automaattinen raportointi" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Lähetetäänkö automaattisesti joka kerta?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "BIOS-päivitykset toimitetaan LVFS:n tai Windows Updaten kautta" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "LUONTI-XML TIEDOSTONIMI-KOHDE" + +msgid "BYTES" +msgstr "TAVUA" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Kytke uusi ytimen ajuri" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Estetyt laiteohjelmistotiedostot:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Estetty versio" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Laiteohjelmiston estäminen:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Estää tietyn laiteohjelmiston asentamisen" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Käynnistyslataimen versio" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Haara" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Luo laiteohjelmistotiedosto" + +#. 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" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Ei voida asentaa, koska dbx-päivitys on jo asennettu." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Päivityksiä ei voi asentaa live-medialla" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Muutettu" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Tarkistaa, että kryptografinen tiiviste vastaa laiteohjelmistoa" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Tarkistussumma" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Valitse haara:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Valitse laite:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Valitse laiteohjelmiston tyyppi:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Valitse julkaisu:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Valitse asema:" + +#. 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: is not supported by the vendor +msgid "Community supported" +msgstr "Yhteisön tukema" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Muunna laiteohjelmistotiedosto" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Luotu" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kriittinen" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Kryptografisen tiivisteen tarkistus on käytettävissä" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Nykyinen arvo" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Nykyinen versio" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "LAITETUNNUS|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU-apuohjelma" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Vianjäljitysvalinnat" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Puretaan…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Kuvaus" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Irrota käynnistyslataimen tilaan" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Tiedot" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Laitteen liput" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Laitetunnus" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Laite lisätty:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Laitteen akun lataus on liian vähissä" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Laite voi palautua asennuksen virheistä" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Laitetta ei voi käyttää kun kansi on suljettu" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Laite muutettu:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Laiteohjelmisto vaatii version tarkistamista" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Laite on lukittu" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Laite edellyttää asentamaan kaikki toimitetut versiot" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Laite ei ole tavoitettavissa" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Laitetta voidaan käyttää päivityksen aikana" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Laite odottaa päivityksen toteuttamista" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Laite poistettu:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Laite vaatii verkkovirran olevan kytketty" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Laite valmistelee päivitykset" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Laite tukee vaihtamista toiseen laiteohjelmiston haaraan" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Laitteen päivitysmenetelmä" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Laitteen päivitys tarvitsee aktivointia" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Laite varmuuskopioi laiteohjelmiston ennen asennusta" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Laitetta ei näytetä uudelleen päivityksen päätyttyä" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Onnistuneesti päivitetyt laitteet:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Laitteet, joita ei päivitetty oikein:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Laitteet, joille ei ole saatavilla laiteohjelmiston päivityksiä:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Laitteet, joille on asennettu uusin versio laiteohjelmistosta:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Laitteita ei löytynyt vastaavilla GUID-tunnuksilla" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Ei käytössä" + +msgid "Disabled fwupdate debugging" +msgstr "fwupdaten vianjäljitys ei ole käytössä" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Poista annettu lähde" + +#. 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 metatietojen vanhentumista" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Älä tarkista ilmoittamattomien historiaa" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Älä tarkista, pitäisikö verkkolähteitä ottaa käyttöön" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Älä pyydä tai tarkista uudelleenkäynnistystä päivityksen jälkeen" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Älä sisällytä lokiin toimialueen etuliitettä" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Älä sisällytä aikaleiman etuliitettä" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Älä suorita laitteen turvallisuustarkastuksia" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Älä pyydä laitteita" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Älä kirjoita historiatietokantaan" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Ymmärrätkö laiteohjelmiston haaran vaihtamisen seuraukset?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Haluatko poistaa tämän ominaisuuden käytöstä tulevia päivityksiä varten?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Haluatko päivittää tämän lähteen nyt?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Haluatko lähettää raportteja automaattisesti tulevia päivityksiä varten?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Valmis!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Alennetaanko laitteen %s laiteohjelmisto versiosta %s versioon %s?" + +#. 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 "Alennetaan laitteen %s ohjelmisto versiosta %s versioon %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Alennetaan laitetta %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Lataa tiedosto" + +#. 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: length of time the update takes to apply +msgid "Duration" +msgstr "Kesto" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Annettu ESP-polku on virheellinen" + +msgid "Enable" +msgstr "Käytä" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Ota laiteohjelmiston päivitystuki käyttöön tuetuissa järjestelmissä" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Otetaanko uusi lähde käyttöön?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Otetaanko tämä lähde käyttöön?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Käytössä" + +msgid "Enabled fwupdate debugging" +msgstr "fwupdaten vianjäljitys on käytössä" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Käytössä, jos laitteisto vastaa" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Ota käyttöön annettu lähde" + +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 ilmoittaa 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 lähteen käyttöönotto tapahtuu omalla vastuullasi." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Salattu" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Salattu RAM-muisti" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Käyttöaika loppu" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Enumeraatio" + +#. 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…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +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: command description +msgid "Export a firmware file structure to XML" +msgstr "Vie laiteohjelmiston tiedostorakenne XML-muotoon" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Pura laiteohjelmiston \"blob\" levykuviksi" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "TIEDOSTO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "TIEDOSTO [LAITETUNNUS|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "TIEDOSTONIMI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "TIEDOSTONIMI SERTIFIKAATTI YKSITYINEN AVAIN" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "TIEDOSTONIMI LAITTEEN-VAIHTOEHT-NIMI|LAITTEEN-VAIHTOEHT-TUNNUS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "TIEDOSTONIMI LAITTEEN-VAIHTOEHT-NIMI|LAITTEEN-VAIHTOEHT-TUNNUS [LEVYKUVAN-VAIHTOEHT-NIMI|LEVYKUVAN-VAIHTOEH-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "TIEDOSTONIMI LAITETUNNUS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "TIEDOSTONIMI [LAITETUNNUS|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "TIEDOSTONIMI [LAITEOHJELMISTON-TYYPPI]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "TIEDOSTONIMI-LÄHDE TIEDOSTONIMI-KOHDE [LAITEOHJELMISTON-TYYPPI-LÄHDE] [LAITEOHJELMISTON-TYYPPI-KOHDE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Epäonnistui" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Päivityksen asentaminen epäonnistui" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Yhteys taustaprosessiin epäonnistui" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Vireillä olevien laitteiden haku epäonnistui" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Laiteohjelmiston päivityksen asennus epäonnistui" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Paikallisen dbx-tiedoston lataus epäonnistui" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Erikoisominaisuuksien (quirks) lataaminen epäonnistui" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Järjestelmän dbx-tiedoston lataus epäonnistui" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Lukitseminen epäonnistui" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Parametrien jäsentäminen epäonnistui" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Tiedoston jäsentäminen epäonnistui" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "--filter-lippujen jäsentäminen ei onnistunut" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Paikallisen dbx-tiedoston lukeminen epäonnistui" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Uudelleenkäynnistys epäonnistui" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Splash-tilan asettaminen epäonnistui" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "ESP:n sisällön vahvistaminen epäonnistui" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Epätosi" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Tiedostonimi" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Tiedoston allekirjoitus" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Tiedostonimen lähde" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Tiedostonimi vaaditaan" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Suodata käyttäen laitelippuja. ~ poissulkee, esimerkiksi \"internal,~needs-reboot\"" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Laiteohjelmistojen perus-URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Laiteohjelmistopäivityksen D-Bus-palvelu" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Firmware-päivityksen taustaprosessi" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Laiteohjelmistopäivitykset" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Laiteohjelmistotyökalu" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Laiteohjelmiston vahvistaminen" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Laiteohjelmisto on jo estetty" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Laiteohjelmistoa ei ole jo estetty" + +#. 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 %u päivään ja ne eivät välttämättä ole ajan tasalla." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Firmwaren metatiedot päivitetty: %s. Käytä --force päivittääksesi ne uudelleen." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Laiteohjelmistopäivitykset" + +msgid "Firmware updates are not supported on this machine." +msgstr "Tämä laite ei tue laiteohjelmistopäivityksiä" + +msgid "Firmware updates are supported on this machine." +msgstr "Tämä laite tukee laiteohjelmistopäivityksiä" + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Laiteohjelmiston päivitykset poistettu käytöstä; suorita 'fwupdmgr unlock' ottaaksesi ne käyttöön" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Liput" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Pakota toiminta vapauttamalla joitakin ajonaikaisia tarkistuksia" + +msgid "Force the action ignoring all warnings" +msgstr "Pakota toimenpide, älä huomioi varoituksia" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Löydetty" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Levyn salaus havaittu" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Levyn salaus voi mitätöityä päivityksen aikana" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUIDs" +msgstr[1] "GUID:t" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Hae BIOS-asetukset" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Hae kaikki fwupd:n tukemat laiteliput" + +#. 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 "Hae kaikki järjestelmään rekisteröidyt laajennukset" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Hae tietoja laiteohjelmistotiedostosta" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Hae konfiguroidut lähteet" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Hae koneen tietoturva-attribuutit" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Hae hyväksytyn laiteohjelmiston luettelo" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Hakee estettyjen laiteohjelmistojen luettelon" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Hae luettelo liitettyjen laitteiden päivityksistä" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Hakee laitteen julkaisut" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Hakee viimeisimmän päivityksen tulokset" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-TIEDOSTO" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Laitteisto odottaa uudelleenkytkemistä" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Korkea" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Koneen suojaustapahtumia" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "Host Security ID (HSI) ei ole tuettu" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Koneen tietoturvatunniste:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU-laitteen suojaus poistettu" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU-laitteen suojaus käytössä" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Jouten…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ohita tiukat SSL-tarkistukset tiedostoja ladattaessa" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ohita laiteohjelmiston tarkistussumman virheet" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ohita laitteiden yhteensopimattomuuden virheet" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ohita turvatarkastukset" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ohitetaan tiukat SSL-tarkistukset. Jos haluat tehdä tämän jatkossa automaattisesti, ota käyttöön DISABLE_SSL_STRICT-ympäristömuuttuja" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Asennuksen kesto" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Asenna laiteohjelmisto (\"blob\") laitteeseen" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Asenna laiteohjelmistotiedosto tähän laitteistoon" + +msgid "Install old version of signed system firmware" +msgstr "Asenna allekirjoitetun järjestelmän firmware:n vanha versio" + +msgid "Install old version of unsigned system firmware" +msgstr "Asenna allekirjoittamattoman järjestelmän firmware:n vanha versio" + +msgid "Install signed device firmware" +msgstr "Asenna allekirjoitettu laiteohjelmisto" + +msgid "Install signed system firmware" +msgstr "Asenna allekirjoitettu järjestelmän laiteohjelmisto" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Asenna ensin päälaitteelle" + +msgid "Install unsigned device firmware" +msgstr "Asenna allekirjoittamaton laiteohjelmisto" + +msgid "Install unsigned system firmware" +msgstr "Asenna allekirjoittamaton järjestelmän laiteohjelmisto" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Asennetaan laiteohjelmistoa…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Asennetaan laiteohjelmstopäivitystä…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Asentaa laitteelle %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Tämän päivityksen asentaminen saattaa kumota laitteen takuun." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Kokonaisluku" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM -suojattu" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuardin OTP-sulake" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard -virhekäytäntö" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuardilla vahvistettu käynnistys" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktiivinen" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET käytössä" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Sisäinen laite" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Virheellinen" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Virheelliset argumentit" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "On vanhempi" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "On käynnistyslataintilassa" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "On päivitys" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Kysymykset" +msgstr[1] "Ongelmat" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "AVAIN,ARVO" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Kernel ei ole enää saastunut" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Kernel on saastunut" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Kernel lukitus poistettu käytöstä" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Kernel lukitus käytössä" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Avainnippu" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "SIJAINTI" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Viimeksi muokattu" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Jäljellä on alle minuutti" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Lisenssi" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux-laitetoimittajien laiteohjelmistopalvelu (vakaat laiteohjelmistot)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux-laitetoimittajien laiteohjelmistopalvelu (testattavat laiteohjelmistot)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel lukitus" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linuxin näennäismuisti (swap)" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Listaa dbx:n merkinnät" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Luettelo tuetuista laiteohjelmistopäivityksistä" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Luettelo käytettävissä olevista laiteohjelmistotyypeistä" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Luetteloi ESP:n tiedostot" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Ladataan…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Lukittu" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Matala" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI-tuotantotekniikka (manufacturing mode)" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI-ohitus" + +msgid "MEI version" +msgstr "MEI-versio" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Ota manuaalisesti tietyt laajennukset käyttöön" + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Enimmäispituus" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Enimmäisarvo" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Keskitaso" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metatietojen allekirjoitus" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metatietojen URI" + +#. 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: smallest version number installable on device +msgid "Minimum Version" +msgstr "Vähimmäisversio" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Vähimmäispituus" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Vähimmäisarvo" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Taustaprosessi ja asiakasohjelma eivät vastaa toisiaan, käytä sen sijaan %s" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Muokkaa taustaprosessin asetusarvoa" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Muokkaa annettua lähdettä" + +msgid "Modify a configured remote" +msgstr "Muokkaa lähteen asetuksia" + +msgid "Modify daemon configuration" +msgstr "Muokkaa taustaprosessin asetuksia" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Seuraa tapahtumien taustaprosessia" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Liitä ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "HUOMAA: Ohjelma saattaa toimia oikein vain root käyttäjänä" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Tarvitsee uudelleenkäynnistyksen asennuksen jälkeen" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Uudelleenkäynnistys tarvitaan" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Tarvitsee sammutuksen asennuksen jälkeen" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Uusi versio" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Toimintoa ei ole määritetty!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Ei versioalennusta laitteelle %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Laiteohjelmiston tunnuksia ei löytynyt" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Ei löytynyt laitteita, joille voisi asentaa laiteohjelmistopäivityksiä" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Liitännäisiä ei löytynyt" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Ei julkaisuja saatavilla" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Mitään lähteitä ei ole tällä hetkellä käytössä, joten metatietoja ei ole saatavilla." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Lähteitä ei saatavilla" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Ei päivitettäviä laitteita" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Päivityksiä ei ole saatavilla" + +msgid "No updates available for remaining devices" +msgstr "Muille laitteille ei ole saatavilla päivityksiä" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Päivityksiä ei toteutettu" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Hylätty" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Ei löydy" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Ei tueta" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Näytä vain yksi PCR-arvo" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Käytä vain IPFS:ää kun lataat tiedostoja" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Vain versioiden päivitykset ovat sallittuja" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Tuloste JSON-muodossa" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Ohita oletusarvoinen ESP-polku" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "POLKU" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Jäsennä ja näytä tiedot laiteohjelmistotiedostosta" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Jäsennetään dbx-päivitystä..." + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Jäsennetään järjestelmän dbx:ää..." + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Salasana" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Korjaa laiteohjelmiston blob tunnetulla vastineella" + +msgid "Payload" +msgstr "Tietosisältö" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Odottaa" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Valmis prosenteissa" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Suoritetaanko toiminto?" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Varmista, että sinulla on aseman palautusavain ennen kuin jatkat." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Anna numero 0 -%u" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Laajennuksen riippuvuudet puuttuvat" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Mahdolliset arvot" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Käynnistystä edeltävä DMA-suojaus" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Käynnistystä edeltävä DMA-suojaus on poistettu käytöstä" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Käynnistystä edeltävä DMA-suojaus on käytössä" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Aiempi versio" + +msgid "Print the version number" +msgstr "Tulosta versionumero" + +msgid "Print verbose debug statements" +msgstr "Tulosta vianjäljitystiedot" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioriteetti" + +msgid "Proceed with upload?" +msgstr "Jatketaanko lähettämistä?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Suorittimen turvallisuustarkastukset" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Suljettu" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Kysely laiteohjelmistopäivityksen tuesta" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "LÄHDETUNNUS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "LÄHDETUNNUS AVAIN ARVO" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Vain luku" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Lue laiteohjelmiston \"blob\" laitteesta" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Lue laiteohjelmisto laitteelta" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Lue laiteohjelmisto laitteesta tiedostoon" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Lue laiteohjelmisto osiolta tiedostoon" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lukee laitteelta %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Luetaan…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Käynnistetään uudelleen..." + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Päivitä metatiedot etäpalvelimelta" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Asennetaanko laitteeseen %s uudelleen laiteohjelmiston versio%s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Asenna nykyinen laiteohjelmisto laitteeseen uudelleen" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Asenna laiteohjelmisto uudelleen laitteelle" + +#. 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 "Asennetaan laitteelle %s uudelleen versio %s..." + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Julkaisuhaara" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Julkaisu ID-tunnus" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Etätunnus" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Vaihda tiedot olemassa olevaan laiteohjelmistotiedostoon" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Ilmoitus-URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Raportoitu etäpalvelimelle" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Pyyntö peruttu" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Vaadittua efivarfs-tiedostojärjestelmää ei löytynyt" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Vaadittavaa laitteistoa ei löytynyt" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Vaatii käynnistyslataimen" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Vaatii internetyhteyden" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Käynnistetäänkö uudelleen nyt?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Käynnistetäänkö 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 koneen laitetunnukset" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Suorita `fwupdmgr get-upgrades` saadaksesi lisätietoja." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Suorita \"fwupdmgr sync-bkc\" suorittaaksesi tämä toiminto loppuun." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Suorita liitännäisen yhteinen siivousrutiini, kun käytetään komentoa install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Suorita liitännäisen yhteinen valmistelurutiini, kun käytetään komentoa install-blob" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Ajossa oleva ydin on liian vanha" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Ajonaikainen pääte" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS tunnus" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI-BIOS-alue" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI-lukko" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI-kirjoitus" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "ALIJÄRJESTELMÄ AJURI [LAITETUNNUS|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Tallenna tiedosto, joka mahdollistaa laitetunnusten luomisen" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Ajasta asennus uudelleenkäynnistykselle mahdollisuuksien mukaan" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Ajoitetaan…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot pois käytöstä" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot käytössä" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Katso %s saadaksesi lisätietoja." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Valittu laite" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Valittu asema" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Sarjanumero" + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Aseta BIOS-asetus" + +msgid "Set one or more BIOS settings" +msgstr "Aseta yksi tai useampi BIOS-asetus" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Aseta virheenkorjauksen lippu päivityksen aikana" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Asettaa yhden tai useamman BIOS-asetuksen" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Tallentaa hyväksyttyjen laiteohjelmistojen luettelon" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Asetukset tulevat voimaan, kun järjestelmä on käynnistetty uudelleen" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Jaa laiteohjelmiston historia kehittäjien kanssa" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Näytä kaikki tulokset" + +#. 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ä laiteohjelmistopäivitysten historia" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Näytä liitännäisten täydelliset tiedot" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Näytä dbx laskettu versio" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Näytä virheenjäljitysloki viimeisestä päivitysyrityksestä" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Näytä tiedot laiteohjelmiston päivitystilasta" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Sammutetaanko nyt?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Allekirjoita laiteohjelmisto uudella avaimella" + +msgid "Sign data using the client certificate" +msgstr "Allekirjoita tietoja käyttämällä asiakkaan sertifikaattia" + +#. TRANSLATORS: command description +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 lähetetyt tiedot asiakkaan sertifikaatilla" + +msgid "Signature" +msgstr "Allekirjoitus" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Koko" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Jotkut salaukset voidaan mitätöidä tätä firmware-ohjelmistoa päivitettäessä." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Lähde" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Määritä DFU-laitteen toimittaja- tai tuotetunnus" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Määritä dbx-tietokantatiedosto" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Määritä tavujen määrä USB-siirtoa kohti" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Merkkijono" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Onnistui" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Kaikkien laitteiden aktivointi onnistui" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Lähteen poistaminen onnistui" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Uusien metatietojen lataus onnistui:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Lähteen käyttöönotto ja päivitys onnistui" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Lähteen käyttöönotto onnistui" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Laiteohjelmiston asennus onnistui" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Asetusarvon muokkaaminen onnistui" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Lähteen muokkaus onnistui" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metatietojen manuaalinen päivitys onnistui" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Laitteiden tarkistussummien päivitys onnistui" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Raporttien lataus %u onnistui" +msgstr[1] "%u raportin lähetys onnistui" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Laitteiden tarkistussummat vahvistettu onnistuneesti" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Yhteenveto" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Tuettu" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Tuettu etäpalvelimella" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Keskeytä tyhjäkäynnille" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Keskeytä muistiin" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Vaihdatko haaran %s haaraksi %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Vaihda laitteen laiteohjelmiston haaraa" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Järjestelmän akun varaus ei riitä päivityksen toteuttamiseen" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Järjestelmä vaatii ulkoisen virtalähteen" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEKSTI" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 -jälleenrakennus" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0 jälleenrakennus on virheellinen" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "TPM tyhjät PCR:t" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Ydin on tainted-tilassa" + +msgid "Target" +msgstr "Kohde" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testaa laitetta käyttäen JSON-manifestia" + +#. 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ä jakelijaan $OS_RELEASE:NAME$. Jakelijasi ei ehkä ole varmistanut laiteohjelmistopäivitysten yhteensopivuutta järjestelmän tai liitettyjen laitteiden kanssa. Kaikki laiteohjelmistot tulevat alkuperäisiltä laitevalmistajilta." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 eroaa uudelleenrakennetusta." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Palvelu on ladannut 3. osapuolen koodia, eivätkä kehittäjät enää jatkossa tue sitä!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Laitteen versio ei täsmää: sain %s, odotettu %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Laiteohjelmiston on toimittanut %s eikä laitteen toimittaja %s." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Järjestelmän kelloa ei ole asetettu oikein ja tiedostojen lataaminen saattaa epäonnistua." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Valmistaja ei toimittanut julkaisutietoja." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Estettyjä laiteohjelmistotiedostoja ei ole" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Hyväksyttyä laiteohjelmistoa ei ole." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Tämä laiteohjelmisto on tarjolla LVFS-yhteisöjäsenten toimesta, sitä ei ole tarjottu (tai tuettu) alkuperäisen laitevalmistajan toimesta." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Tätä pakettia ei ole vahvistettu, se ei välttämättä toimi oikein." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Tämä ohjelma toimii oikein vain root-käyttäjän oikeuksin" + +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ä lähde sisältää laiteohjelmiston, joka ei ole vientikiellossa, mutta jota laitteistotoimittaja testaa edelleen. Varmista, että voit päivittää laiteohjelmiston takaisin vanhempaan versioon manuaalisesti, jos päivitys epäonnistuu." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "Tämä järjestelmä ei tue laiteohjelmiston asetuksia" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Järjestelmässä on HSI-suoritusajan ongelma." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Tämän järjestelmän HSI-tietoturvataso on alhainen." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Tämän työkalun avulla järjestelmänvalvoja voi asentaa UEFI-dbx-päivityksiä." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Tämän työkalun avulla järjestelmänvalvoja voi jäljittää UpdateCapsulen toimintaa." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Tämän työkalun avulla järjestelmänvalvoja voi kysellä ja hallita fwupd-palvelua, jolloin voi suorittaa toimintoja, kuten laiteohjelmiston asennus tai päivittäminen." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Tämän työkalun avulla järjestelmänvalvoja voi käyttää fwupd-laajennuksia asentamatta niitä järjestelmään." + +#. 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: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Tämä työkalu lukee ja jäsentää TPM-tapahtumalokin järjestelmän laiteohjelmistosta." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Ohimenevä häiriö" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Tosi" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Luotettu metatieto" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Luotettu lataus" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tyyppi" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFIn ESP-osiota ei havaittu tai määritetty" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI-laiteohjelmistoapuohjelma" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI-kapselipäivitykset eivät ole saatavilla tai niitä ei ole otettu käyttöön laiteohjelmiston asetuksissa" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI-dbx-apuohjelma" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI-laiteohjelmiston päivitys ei onnistu vanhassa BIOS-tilassa" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI-alustan avain" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI-turvakäynnistys (secure boot)" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Yhteys palveluun ei onnistu" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Irrota nykyinen ajuri" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Laiteohjelmiston eston poistaminen:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Poistaa asennuseston laiteohjelmistosta" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Salaamaton" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Tuntematon" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Tuntematon laite" + +msgid "Unlock the device to allow access" +msgstr "Avaa laitteen lukitus salliaksesi käytön" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Auki" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Avaa laitteen lukituksen, jotta laiteohjelmistoon on pääsy" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Irrota ESP" + +#. 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 "Taustaprosessin versiota %s ei tueta, asiakasohjelman versio on %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Ydin ei ole tainted-tilassa" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Päivitettävissä" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Päivitysvirhe" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Päivityksen viesti" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Päivityksen tila" + +#. 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äivitetäänkö nyt?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Päivitys edellyttää uudelleenkäynnistyksen" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Päivitä tallennettu kryptografinen tiiviste nykyisellä ROM-sisällöllä" + +msgid "Update the stored device verification information" +msgstr "Päivitä laitteen tallennetut vahvistustiedot" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Päivitä tallennetut metatiedot nykyisellä sisällöllä" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Päivittää kaikki määritetyt laitteet uusimpaan firmware versioon tai niihin joita ei ole määritetty" + +msgid "Updating" +msgstr "Päivittää" + +#. 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 "Päivitetään laitteen %s ohjelmisto versiosta %s versioon %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Päivittää laitetta %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Päivitetäänkö laitteen %s laiteohjelmisto versiosta %s versioon %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Lähetetäänkö 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 "Laiteohjelmistoraporttien lähettäminen auttaa laitteistotoimittajia tunnistamaan nopeasti virheelliset ja onnistuneet päivitykset todellisissa laitteissa." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Kiireellisyys" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Suorita fwupdmgr --help nähdäksesi ohjeet" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Suorita fwupdtool --help nähdäksesi ohjeet" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Käytä erikoisominaisuuslippuja (quirk flags) asennettaessa laiteohjelmistoa" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Käyttäjälle on ilmoitettu" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Käyttäjätunnus" + +msgid "VID:PID" +msgstr "VID:PID (toimittajatunnus:tuotetunnus)" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Kunnossa" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Vahvistetaan ESP:n sisältöä..." + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Muunnelma" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Toimittaja" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Vahvistetaan…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versio" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "VAROITUS:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Odotetaan…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Seuraa laitteiston muutoksia" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Kirjoita laiteohjelmisto tiedostosta laitteeseen" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Kirjoita laiteohjelmisto tiedostosta osioon" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Kirjoitetaan tiedosto:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Kirjoitetaan…" + +#. 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 varmistanut laiteohjelmistopäivitysten yhteensopivuutta järjestelmän tai liitettyjen laitteiden kanssa." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Laitteistosi saattaa vaurioitua tämän laiteohjelmiston käytöllä. Asentaminen voi mitätöidä toimittajan %s takuun." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Järjestelmäsi on asetettu BKC %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[TARKISTESUMMA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[LAITETUNNUS|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[LAITETUNNUS|GUID] [HAARA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[TIEDOSTO TIEDOSTON_ALLEKIRJOITUS LÄHDETUNNUS]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[TIEDOSTONIMI1] [TIEDOSTONIMI2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-TIEDOSTO|HWIDS-TIEDOSTO]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "oletus" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd:n TPM-tapahtumalokin apuohjelma" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd-liitännäiset" diff --git a/fwupd-1.8.6/po/fr.po b/fwupd-1.8.6/po/fr.po new file mode 100644 index 0000000000000000000000000000000000000000..6bb9dd012031fd70b58c95de4546b4d58df2c7bd --- /dev/null +++ b/fwupd-1.8.6/po/fr.po @@ -0,0 +1,1096 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Claude Paroz , 2021 +# Corentin Noël , 2020 +# Franck , 2015 +# Julien Humbert , 2020-2021 +# Yolopix ​, 2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minute restante" +msgstr[1] "%.0f minutes restantes" +msgstr[2] "%.0f minutes restantes" + +#. 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 "Mise à jour de %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Version %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u jour" +msgstr[1] "%u jours" +msgstr[2] "%u jours" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "Une mise à jour de micrologiciel est disponible pour %u appareil." +msgstr[1] "Une mise à jour de micrologiciel est disponible pour %u appareils." +msgstr[2] "Une mise à jour de micrologiciel est disponible pour %u appareils." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u heure" +msgstr[1] "%u heures" +msgstr[2] "%u heures" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minute" +msgstr[1] "%u minutes" +msgstr[2] "%u minutes" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u seconde" +msgstr[1] "%u secondes" +msgstr[2] "%u secondes" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsolète)" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Activation de la mise à jour du micrologiciel" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Activation de la mise à jour de micrologiciel pour" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Âge" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias de %s" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Une mise à jour nécessite un redémarrage pour se terminer." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Une mise à jour nécessite que le système soit éteint pour se terminer." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Répondre oui à toutes les questions" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Appliquer les mises à jour de micrologiciels" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Appliquer la mise à jour même quand ce n'est pas conseillé" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Appliquer les fichiers de mise à jour" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Application de la mise à jour…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Micrologiciel approuvé :" +msgstr[1] "Micrologiciels approuvés :" +msgstr[2] "Micrologiciels approuvés :" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Demander à nouveau la prochaine fois ?" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Authentification…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Une authentification est nécessaire pour définir la liste des micrologiciels approuvés" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Une authentification est nécessaire pour signer les données en utilisant le certificat du client" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Authentification requise pour déverrouiller un périphérique" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Authentification requise pour mettre à jour le micrologiciel sur un périphérique amovible" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Une authentification est nécessaire pour mettre à jour le micrologiciel sur cette machine" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Rapports automatiques" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Envoyer automatiquement à chaque fois ?" + +msgid "BYTES" +msgstr "OCTETS" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Version du chargeur d’amorçage" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Branche" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Annuler" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Annulé" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Impossible d'appliquer car la mise à jour dbx a déjà été appliquée." + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Modifié" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Vérifie que l'empreinte cryptographique correspond au micrologiciel" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Somme de contrôle" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Choisir une branche :" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Choisissez un appareil :" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Choisissez un type de micrologiciel :" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Choisir un volume :" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Commande non trouvée" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Critique" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Version actuelle" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Options de débogage" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Décompression…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Description" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Détails" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Drapeaux de périphérique" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Périphérique ajouté :" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Périphérique modifié :" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Le périphérique est verrouillé" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Périphérique retiré :" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Méthode de mise à jour du périphérique" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Désactivé" + +msgid "Disabled fwupdate debugging" +msgstr "Débogage fwupdate désactivé" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Afficher la version" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Ne pas vérifier d'anciennes métadonnées" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Ne pas écrire dans la base de données de l'historique" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Terminé !" + +#. 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 "Rétrogradation de %s de %s en %s" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Téléchargement…" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durée" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Activé" + +msgid "Enabled fwupdate debugging" +msgstr "Débogage fwupdate activé" + +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 "L'activation de cette fonctionnalité est à vos propres risques, ce qui signifie que vous devrez contacter le fabricant d'origine de votre équipement au sujet de tout problème éventuel causé par ces mises à jour. Seuls les problèmes liés au processus de mise à jour doivent être signalés à $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Chiffré" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM chiffrée" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Effacer tout l'historique de mise à jour du micrologiciel" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Effacement…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Quitter après un bref délai" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Quitter après le chargement du moteur" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FICHIER" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Échec" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Échec d'application de la mise à jour" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Impossible d'obtenir les périphériques en attente" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Échec d'installation de la mise à jour du micrologiciel" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Échec de chargement du dbx local" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Échec de chargement du dbx système" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Échec de verrouillage" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Echec de l'analyse des paramètres" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Échec d'analyse du dbx local" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Échec du redémarrage" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Échec de validation des contenus ESP" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nom de fichier" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Signature de nom de fichier" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Source de nom de fichier" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nom de fichier obligatoire" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI de base du micrologiciel" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Service D-Bus de mise à jour des micrologiciels" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Service de mise à jour de micrologiciel" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Attestation de micrologiciel" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Le micrologiciel est déjà bloqué" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Le micrologiciel n'est pas déjà bloqué" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Mises à jour de micrologiciels" + +msgid "Firmware updates are not supported on this machine." +msgstr "Les mises à jour de micrologiciels ne sont pas prises en charge sur cette machine." + +msgid "Firmware updates are supported on this machine." +msgstr "Les mises à jour de micrologiciels sont prises en charge sur cette machine." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Les mises à jour de micrologiciel sont désactivées ; exécutez «fwupdmgr unlock» pour les activer" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Drapeaux" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Trouvé" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Chiffrement complet du disque détecté" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" +msgstr[2] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obtenir la liste des périphériques supportant les mises à jour de micrologiciel" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obtenir les détails d'un fichier de micrologiciel" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Haute" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Identifiant de sécurité de l'hôte :" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "En attente…" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Durée d'installation" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Installer un fichier de micrologiciel sur ce matériel" + +msgid "Install signed device firmware" +msgstr "Installer le micrologiciel signé du périphérique" + +msgid "Install signed system firmware" +msgstr "Installer le micrologiciel signé du système" + +msgid "Install unsigned system firmware" +msgstr "Installer le micrologiciel non signé du système" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Installation de micrologiciel…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Installation de la mise à jour du micrologiciel..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installation sur %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET actif" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET activé" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Périphérique interne" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Invalide" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "CLÉ,VALEUR" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Trousseau de clés" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Dernière modification" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Moins d’une minute restante" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licence" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Noyau Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Espace d'échange (swap) Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Afficher la liste des entrées de la base dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Liste les mises à jour de micrologiciel supportées" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Chargement…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Verrouillé" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Faible" + +msgid "MEI version" +msgstr "Version MEI" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Moyenne" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Signature de métadonnées" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI de métadonnées" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Version minimum" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nouvelle version" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Aucune action indiquée !" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Aucune rétrogradation pour %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Aucun identifiant de micrologiciel trouvé" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Aucun matériel ayant des capacités de mise à jour du micrologiciel n'a été détecté" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Aucun greffon trouvé" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Aucune mise à jour n'a été appliquée" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Non trouvé" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Non pris en charge" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "N'utiliser IPFS que lors du téléchargement de fichiers" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "CHEMIN" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Analyse de la mise à jour dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Analyse de la base dbx système…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Mot de passe" + +msgid "Payload" +msgstr "Charge utile" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Effectuer l'opération ?" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Veuillez saisir un nombre de 0 à %u :" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dépendances de greffons manquantes" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Version précédente" + +msgid "Print the version number" +msgstr "Afficher le numéro de version" + +msgid "Print verbose debug statements" +msgstr "Afficher les instructions de débogage verbeuses" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Priorité" + +msgid "Proceed with upload?" +msgstr "Continuer avec le téléversement ?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Propriétaire" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lecture depuis %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lecture…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Redémarrage…" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Réinstaller le micrologiciel sur un périphérique" + +#. 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 "Réinstallation de %s en %s" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI des rapports" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Système de fichier efivarfs requis non trouvé" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Le matériel requis n'a pas été trouvé" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Redémarrer maintenant ?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Redémarrer le service pour rendre la modification effective ?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Redémarrage du périphérique…" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Exécutez «fwupdmgr get-upgrades» pour plus d'informations." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Exécutez `fwupdmgr sync-bkc` pour terminer cette action." + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planification..." + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Démarrage sécurisé désactivé" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Démarrage sécurisé activé" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Voir %s pour plus d'informations." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Périphérique sélectionné" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume sélectionné" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Numéro de série" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Définir la liste des micrologiciels approuvés" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Partager l'historique du micrologiciel avec les développeurs" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Afficher tous les résultats" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Montrer les options de débogage" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Montre des informations de débogage complémentaires" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Afficher la version calculée de la base dbx" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Éteindre maintenant ?" + +msgid "Sign data using the client certificate" +msgstr "Signer les données en utilisant le certificat du client" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Signer les données en utilisant le certificat du client" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Signer les données envoyées avec le certificat du client" + +msgid "Signature" +msgstr "Signature" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Taille" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Source" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Indiquer le fichier de base de données dbx" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Tous les périphériques ont été activés avec succès" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "La valeur de configuration a été modifiée avec succès" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Succès de l'envoi de %u rapport" +msgstr[1] "Succès de l'envoi de %u rapports" +msgstr[2] "Succès de l'envoi de %u rapports" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Résumé" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Pris en charge" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Le système nécessite une source d'alimentation externe" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXTE" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +msgid "Target" +msgstr "Cible" + +#. 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 est un service libre qui opère en tant qu'entité légale indépendante et n'est aucunement connecté à $OS_RELEASE:NAME$. Votre distributeur n'a pas forcément vérifié la compatibilité des mises à jour de micrologiciel avec votre système ou avec les appareils connectés. Tous les micrologiciels ne sont fournis que par le fabricant original de votre équipement." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Il n'y a aucun micrologiciel approuvé." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Ce paquet n'a pas été validé, il peut ne pas fonctionner correctement." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Cet outil permet à un administrateur d'appliquer les mises à jour dbx UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Cet outil permet à un administrateur de déboguer l'opération UpdateCapsule." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Cet outil ne peut être utilisé que par l'utilisateur root" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Type" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partition ESP UEFI non détectée ou non configurée" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitaire de micrologiciel UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Les mises à jour de capsule UEFI ne sont pas disponibles ou pas activées dans la configuration du micrologiciel" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitaire dbx UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Clé de plate-forme UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Démarrage sécurisé UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Impossible de se connecter au service" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Déchiffré" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Inconnu" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Déverrouillé" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Déverrouille le périphérique pour l'accès au micrologiciel" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Mise à jour possible" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Erreur de mise à jour" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Message de mise à jour" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "État de mise à jour" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Cet échec de mise à jour est un problème connu, visitez cette URL pour plus d'informations :" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Mettre à jour maintenant ?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "La mise à jour nécessite un redémarrage" + +msgid "Updating" +msgstr "Mise à jour" + +#. 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 "Mise à jour de %s de %s en %s" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Mise à jour de %s…" + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgence" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Utilisez fwupdmgr --help pour obtenir de l'aide" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nom d’utilisateur" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valide" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validation des contenus ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variante" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Fournisseur" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Vérification…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "ATTENTION :" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "En attente…" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Écriture du fichier :" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Écriture…" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[SOMME DE CONTRÔLE]" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Greffons fwupd" diff --git a/fwupd-1.8.6/po/fur.po b/fwupd-1.8.6/po/fur.po new file mode 100644 index 0000000000000000000000000000000000000000..492ae35ed487e19a49d8fc9abc547aedf9a50ed5 --- /dev/null +++ b/fwupd-1.8.6/po/fur.po @@ -0,0 +1,858 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Fabio Tomat , 2017-2018,2020 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: fur\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Al mancjie %.0f minût" +msgstr[1] "A mancjin %.0f minûts" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u dispositîf al à un inzornament firmware disponibil." +msgstr[1] "%u dispositîfs a àn un inzornament firmware disponibil." + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u dispositîf locâl supuartât" +msgstr[1] "%u dispositîfs locâi supuartâts" + +msgid "Activate the new firmware on the device" +msgstr "Ative il gnûf firmware sul dispositîf" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Ativazion inzornament firmware" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Etât" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Acetâ e abilitâ il rimot?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias a %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permet di tornâ indaûr aes versions di firmware precedentis" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permet di tornâ a instalâ lis versions dal firmware esistentis" + +#. 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î." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Un inzornament, par completâsi, al à bisugne che si distudedi il sisteme." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Rispuint di sì a dutis lis domandis" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Apliche i inzornaments firmware" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware aprovât:" +msgstr[1] "Firmware aprovâts:" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Daûr a autenticâ…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "La autenticazion e je necessarie par tornâ indaûr ae version precedente dal firmware suntun dispositîf estraibil " + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "La autenticazion e je necessarie par tornâ indaûr ae version precedente dal firmware su cheste machine" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "La autenticazion e je necessarie par modificâ un rimot configurât, doprât pai inzornaments dal firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "La autenticazion e je necessarie par modificâ la configurazion dal demoni" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "La autenticazion e je necessarie par stabilî la liste dai firmware aprovâts" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "La autenticazion e je necessarie par firmâ i dâts doprant il certificât dal client" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "La autenticazion e je necessarie par passâ ae gnove version dal firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "La autenticazion e je necessarie par sblocâ un dispositîf" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "La autenticazion e je necessarie par inzornâ il firmware suntun dispositîf estraibil" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "La autenticazion e je necessarie par inzornâ il firmware su cheste machine" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "La autenticazion e je necessarie par inzornâ i checksum archiviâts pal dispositîf" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Anule" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Anulât" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Modificât" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Checksum" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Sielç un dispositîf:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Sielç un gjenar di firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Sielç une publicazion:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Al nete i risultâts dal ultin inzornament" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Comant no cjatât" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilitât DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opzions di debug" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Daûr a decomprimi…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descrizion" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Dispositîf zontât:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositîf modificât:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositîf gjavât:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositîfs che a son stâts inzornâts cun sucès:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Dispositîfs che no son stâts inzornâts ben:" + +msgid "Disabled fwupdate debugging" +msgstr "Disabilite il debug di fwupdate" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "No sta includi il prefìs il domini dal regjistri" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "No sta includi il prefìs date/ore" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "No sta eseguî i controi di sigurece dal dispositîf" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Fat!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Al torne indaûr ae version precedente dal firmware suntun dispositîf" + +#. 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 "Daûr a tornâ indaûr ae version precedente di %s de %s ae %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Daûr a degradâ di version %s…" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Daûr a discjariâ…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Scrîf jù i dâts SMBIOS di un file" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "L'ESP specificât nol jere valit" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Abilite il supuart dal inzornament dal firmware sui sistemis supuartâts" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Abilitâ chest rimot?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Abilitât" + +msgid "Enabled fwupdate debugging" +msgstr "Abilite il debug di fwupdate" + +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 abilite cheste funzionalitât a propri pericul, che al significhe che, par ogni probleme causât di chescj inzornaments, si à di contatâ il produtôr origjinâl dal imprest. Dome i problemis che si àn cul sôl procès di inzornament a àn di sei inviâts a $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "La abilitazion di chest rimot e ven fate a to pericul." + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Scancele dute la cronologjie dai inzornaments dal firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Daûr a scancelâ…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Jes dopo un piçul ritart" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Jes dopo che il motôr al à cjariât" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "No si è rivâts a conetisi al demoni" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "No si è rivâts a otignî i dispositîfs in spiete" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "No si è rivâts a instalâ l'inzornament dal firmware" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "No si è rivâts a analizâ i argoments" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "No si è rivâts a analizâ il file" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr " No si è rivâts a tornâ a inviâ il sisteme" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "No si è rivâts a stabilî la modalitât splash" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Non file" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Firme non file" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI de base dal firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Servizi D-Bus inzornament firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Demoni di inzornament firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilitât firmware" + +#. 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] "I metadâts dal firmware no son stâts inzornâts par %u zornade e a podaressin jessi vielis." +msgstr[1] "I metadâts dal firmware no son stâts inzornâts par %u dîs e a podaressin jessi vielis." + +msgid "Firmware updates are not supported on this machine." +msgstr "Su cheste machine no son supuartâts i inzornaments firmware." + +msgid "Firmware updates are supported on this machine." +msgstr "Su cheste machine a son supuartâts i inzornaments firmware." + +msgid "Force the action ignoring all warnings" +msgstr "Sfuarce la azion ignorant ducj i avertiments" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Cjatât" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Oten ducj i dispositîfs che a supuartin i inzornaments dal firmware" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Al oten detais su un file di firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Al oten i rimots configurâts" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Al oten la liste di inzornaments pal hardware tacât" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Al oten lis publicazions par un dispositîf" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Al oten i risultâts dal ultin inzornament" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "In polse…" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instale un file firmware su chest hardware" + +msgid "Install signed device firmware" +msgstr "Instasle firmware di dispositîf firmât" + +msgid "Install signed system firmware" +msgstr "Instale firmware di sisteme firmât" + +msgid "Install unsigned device firmware" +msgstr "Instale firmware di dispositîf cence firme" + +msgid "Install unsigned system firmware" +msgstr "Instale firmware di sisteme cence firme" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Daûr a instalâ il firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Daûr a instalâ l'inzornament dal firmware…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Daûr a instalâ su %s…" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Puarteclâfs" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Al mancje mancul di un minût" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Servizi Firmware dal vendidôr di Linux (firmware stabil)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Servizi Firmware dal vendidôr di Linux (firmware di prove)" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Liste dai inzornaments di firmware supuartâts" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Daûr a cjariâ…" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI metadata" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Al modifiche un rimot furnît" + +msgid "Modify a configured remote" +msgstr "Modifiche un rimot configurât" + +msgid "Modify daemon configuration" +msgstr "Modifiche la configurazion dal demoni" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Monitore il demoni pai events" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nissune azion specificade!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Nissune degradazion di version par %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nissun ID di firmware cjatât" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nissun hardware rilevât un funzionalitâts di inzornament dal firmware" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nissun plugin cjatât" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nissune publicazion disponibile" + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nissun rimot disponibil" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nol è stât aplicât nissun inzornament" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Va ben" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostre dome il valôr PCR" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Passe parsore al valôr dal percors ESP predefinît" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Password" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentuâl di completament" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Inserìs un numar di 0 a %u: " + +msgid "Print the version number" +msgstr "Stampe il numar de version" + +msgid "Print verbose debug statements" +msgstr "Stampe lis declarazions di debug in maniere prolisse" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioritât" + +msgid "Proceed with upload?" +msgstr "Procedi cul inviament?" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Interogazion pal supuart dai inzornaments firmware" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Lei il firmware dal dispositîf intun file" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Lei il firmware di une partizion intun file" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Daûr a lei di %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Daûr a lei…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Daûr a tornâ a inviâ il sisteme…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Inzorne i metadâts dal servidôr rimot" + +#. 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 "Daûr a tornâ a instalâ %s cun %s... " + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID rimot" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Sostituìs i dâts intun file di firmware esistent" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI segnalazion" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Al à bisugne de conession a internet" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Tornâ a inviâ cumò?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Tornâ a inviâ il demoni par rindi efetive la modifiche?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Daûr a tornâ a inviâ il dispositîf…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Torne ducj i ID dal hardware pe machine" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Eseguìs `fwupdmgr get-upgrades` par vê plui informazions." + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Planifiche la instalazion pe volte sucessive che si torne a inviâ cuant che al è pussibil" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Daûr a planificâ…" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Dispositîf selezionât" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Stabilìs la opzion di debug dilunc l'inzornament" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Al stabilìs la liste dai firmware aprovâts" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Condivît la conologjie dai firmware cui svilupadôrs" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Mostre versions di client e demoni" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Mostre lis informazions prolissis dal demoni par un domini particolâr" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Mostre lis informazions di debug par ducj i dominis" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostre opzions di debug" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostre i dispositîfs che no si puedin inzornâ" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostre informazions di debug adizionâls" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostre la cronologjie dai inzoronaments dal firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostre lis informazions prolissis dai plugin" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostre il regjistri dal debug dal ultin tentatîf di inzornament" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Mostre lis informazions sul stât dal inzornament dal firmware" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Distudâ cumò?" + +msgid "Sign data using the client certificate" +msgstr "Firme i dâts doprant il certificât dal client" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Firme i dâts doprant il certificât dal client" + +msgid "Signature" +msgstr "Firme" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Specifiche Vendidôr/ID prodot dal dispositîf DFU" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Specifiche il numar di byte par trasferiment USB" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Rimot disabilitât cun sucès" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Gnûf metadât discjariât cun sucès:" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Rimot abilitât cun sucès" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware instalât cun sucès" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Valôr di configurazion modificât cun sucès" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Rimot modificât cun sucès" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadât inzornât a man cun sucès" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "%u rapuart inviât cun sucès" +msgstr[1] "%u rapuarts inviâts cun sucès" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Sintesi" + +msgid "Target" +msgstr "Destinazion" + +#. 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 "Il LVFS — Servizi firmware dal vendidôr di linux — al è un servizi gratuit che al opere come entitât legâl indipendente e no à conession cun $OS_RELEASE:NAME$. Il to distributôr al podarès no vê verificât la compatibilitât di nissun dai inzornaments firmware cul vuestri sisteme o cui dispositîfs tacâts. Ducj i firmware a son furnîts dome dal produtôr origjinâl dal imprest." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "No'nd è nissun firmware aprovât." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Chest program al pues lavorâ in maniere juste dome come 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 "Chest rimot al conten firmware che no son sot di embargo, ma a son ancjemò in prove dal vendidôr dal hardware. Si à di sigurâsi di vê une maniere par puartâ indaûr a man il firmware ae version precedente, se l'inzornament al falìs." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Chest strument al pues jessi doprât dome dal utent root" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Gjenar" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitât Firmware UEFI" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "No cognossût" + +msgid "Unlock the device to allow access" +msgstr "Sbloche il dispositîf par permeti l'acès" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Al sbloche il dispositîf pal acès al firmware" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Gjave la opzion di debug dilunc l'inzornament" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Version %s dal demoni no supuartade, la version dal client e je la %s" + +#. 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:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Inzornâ cumò?" + +msgid "Update the stored device verification information" +msgstr "Inzorne lis informazions di verifiche dal dispositîf archiviadis" + +#. 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 "Daûr a inzornâ %s di %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Daûr a inzornâ %s…" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Inviâ il rapuart cumò?" + +#. 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 "Inviâ i rapuarts dal firmware al jude i vendidôrs di hardware a identificâ subite i inzornaments bogns e falimentârs sui dispositîfs reâi." + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Non utent" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Daûr a verificâ…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "In spiete…" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Scrîf il firmware dal file intal dispositîf" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Scrîf il firmware dal file intune partizion" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Daûr a scrivi…" + +#. 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 "Il to distributôr al podarès no vê verificât nissun dai inzornaments firmware pe compatibilitât cui dispositîfs tacâts o cul to sisteme." diff --git a/fwupd-1.8.6/po/gl.po b/fwupd-1.8.6/po/gl.po new file mode 100644 index 0000000000000000000000000000000000000000..db26c92adc3ed1075f4d4b6eadea18d893fd0990 --- /dev/null +++ b/fwupd-1.8.6/po/gl.po @@ -0,0 +1,966 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Fran Diéguez , 2020 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Galician (http://www.transifex.com/freedesktop/fwupd/language/gl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: gl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] " Falta %.0f minuto" +msgstr[1] " Faltan %.0f minutos" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modo de fabricación %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Activar os dispositivos pendentes" + +msgid "Activate the new firmware on the device" +msgstr "Activar o novo firmware no dispositivo" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Aceptar e activar o remoto?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias a %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permitir a desactualización de versións de firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permitir a reinstalación de versións de firmware existentes" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplicar actualizacións de firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplicar actualización incluso cando non se avise" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplicar ficheiros de actualización" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aplicando actualización…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware aprovados:" +msgstr[1] "Firmware aprovado:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Preguntar de novo a seguinte vez?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Anexarse ao modo de firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autenticando…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Requírese autenticación para desactualizar o firmware nun dispositivo extraíbel" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Requírese autenticación para desactualizar o 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 "Requírese autenticación para modificar a configuración do remoto usado para actualizar firmwares" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Requírese autenticación para modificar a configuración do demonio" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Requírese autenticación para estabelecer a lista do firmware aprovado" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Requírese autenticación para asinar os datos usando o certificado do cliente" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Requírese autenticación para trocar a unha nova versión do firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Requírese autenticación para desbloquear un dispositivo" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Requírese autenticación para actualizar o firmware nun dispositivo extraíbel" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Requírese autenticación para actualizar o firmware nesta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Requírese autenticación para actualizar as sumas de verificación para o dispositivo" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancelar" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Cancelado" + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Non foi posíbel aplicar as actualizacións no soporte multimedia" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Cambiado" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Seleccione un dispositivo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Escolla o tipo de firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Escolla a publicación:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Escolla un volume:" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Orde non atopada" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Converter un ficheiro de firmware" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilidad DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opcións de depuración" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Descomprimindo…" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Desanexarse ao modo de firmware" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Dispositivo engadido:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositivo cambiado" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositivo retirado:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositivos que foron actualizados con éxito:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Desactivado" + +msgid "Disabled fwupdate debugging" +msgstr "Desactivar a depuración de fwupdate" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Mostrar versión" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Non incluír o dominio do rexistro" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Non incluír o prefixo de marca de tempo" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Non levar a cabo comprobacións de dispositivo" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Feito!" + +#. 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 "Desactualizando %s desde %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Desactualizando %s" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Descargando…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Volcar os datos da SMBIOS desde un ficheiro " + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "O ESP especificado non é válido" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Activar a compatibilidade de actualización de firmware nos sistemas admitidos" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Desexa activar este remoto?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Activad" + +msgid "Enabled fwupdate debugging" +msgstr "Activar a depuración de fwupdate" + +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 "Active esta funcionalidade baixo o seu risco, o que significa que ten que contactar co seu fabricante de equipamento orixinal se ten calquera problema con estas actualizacións. Só os problemas co proceso de actualización en si deberían enviarse en $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Cifrad" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM cifrada" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Borrando…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Saír despois dun pequeno atraso" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Saír despois de que se cargue o motor" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Fallido" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Produciuse un fallo ao aplicar a actualización" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Produciuse un fallo ao conectarse ao demoni" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Produciuse un fallo ao obter a lista de dispositivos pendentes" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Produciuse un fallo ao instalar a actualización do firmware" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Produciuse un fallo ao cargar o dbx local" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Produciuse un fallo ao cargar o dbx do sistema" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Produciuse un fallo ao analizar os argumentos" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Produciuse un fallo ao analizar o ficheiro" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Produciuse un fallo ao analizar as bandeiras para --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Produciuse un fallo ao analizar a dbx local" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Produciuse un fallo ao reiniciar" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Produciuse un fallo ao modo splash" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Produciuse un fallo ao validar os contidos do ESP" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Requírese un nome de ficheiro" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Servizo D-Bus da Actualización do Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Demonio de Actualización de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilidade de Firmware" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Actualizacións de firmware" + +msgid "Firmware updates are not supported on this machine." +msgstr "Esta máquina non admite as actualizacións de firmware." + +msgid "Firmware updates are supported on this machine." +msgstr "Esta máquina admite as actualizacións de firmware." + +msgid "Force the action ignoring all warnings" +msgstr "Forzar a acción ignorando todas as advertencias" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Atopado" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Obter todas as bandeiras de dispositivos admitidos por fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obtén todos os dispositivos que admiten actualizacións de firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obter todos os engadidos activos rexistrados no sistema" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obtén a información sobre o ficheiro de firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Obten os remotos configurados" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obtén os atributos de seguranza do equipo" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Obtén a lista de actualizacións para o hardware conectado" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de seguranza do equipo:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Ocioso…" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instalar un blob de firmware nun dispositivo" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instalar un ficheiro de firmware neste hardware" + +msgid "Install signed device firmware" +msgstr "Instalar firmware de dispositivo asinado" + +msgid "Install signed system firmware" +msgstr "Instalar firmware do sistema asinado" + +msgid "Install unsigned device firmware" +msgstr "Instalar firmware de dispositivo non asinado" + +msgid "Install unsigned system firmware" +msgstr "Instalar firmware do sistema non asinado" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalando Firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Instalando actualización do firmware…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instalando en %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protexido Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política de erro de Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Arrinque verificado de Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET activo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET activado" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "SMAP de Intel" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Non válido" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Falta menos dun minuto" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Servizo de Linux Vendor Firmware (firmware estábel)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Servizo de Linux Vendor Firmware (firmware de probas)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Núcleo de Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Bloqueo de kernel de Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap de Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Mostrar as entradas no dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Mostrar actualizacións de firmware compatíbeis" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Lista todos os tipos de firmware dispoñíbeis" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Cargando…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloqueado" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modo de fabricación MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Omitir MEI" + +msgid "MEI version" +msgstr "Versión do MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Activar manualmente engadidos específicos" + +msgid "Modify a configured remote" +msgstr "Modificar un remoto configuración" + +msgid "Modify daemon configuration" +msgstr "Modificación configuración do demonio" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Monitorizar eventos no demonio" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Non se require ningunha acción!" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Non se atoparon IDs de firmware" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Non se atoparon engadidos" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Non hai publicacións dispoñíbeis" + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Non hai remotos dispoñíbeis" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Non se aplicou ningunha actualización" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Non atopado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Non compatíbel" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostrar só un valor de PCR" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Sobrescribir a ruta predefinida do ESP" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Analixar e mostrar a información dun ficheiro de firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Analizando a dbx de actualizacións…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Analizando o dbx do sistema…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Porcentaxe completado" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protección de DMA pre-arrinque" + +msgid "Print the version number" +msgstr "Imprime o número de versión" + +msgid "Print verbose debug statements" +msgstr "Imprime as sentencias de depuración verbosas" + +msgid "Proceed with upload?" +msgstr "Desexa seguir coa subida?" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consultar compatibilidade da actualización do firmware" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Ler un blob de firmware desde un dispositivo" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Ler firmware desde o dispositivo nun ficheiro" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Ler firmware desde unha partición nun ficheiro" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lendo de %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Lendo…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Reiniciando…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Actualiza os metadatos desde un servidor remoto" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstalar firmware nun dispositivo" + +#. 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 "Reinstalando %s con %s... " + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Substituír datos nun ficheiro de firmware existente" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Require conexión a internet" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Desexa reiniciar o demonio para facer o cambio efectivo?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Reiniciando dispositivo…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Devolve todos os IDs do hardware da máquina" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Rexión da BIOS Do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloqueo do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escritura do SPI" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planificando…" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Seleccionar un dispositivo" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume seleccionado" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Establecer a bandeira de depuración durante a actualización" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Estabelece a lista do firmware aprobado" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Mostrar as versións de cliente e demonio" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostrar opcións de depuración" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostrar os dispositivos que non son actualizábeis" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostrar información de depuración adicional" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostrar o historial das actualizacións de firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostrar información de depuración do engadido" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostrar a versión calculada do dbx" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostrar rexistro de depuración desde o último intento de actualización" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Mostrar a información de estado da actualización do firmware" + +msgid "Sign data using the client certificate" +msgstr "Asinar os datos usando o certificafo do cliente" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Asinar os datos usando o certificafo do cliente" + +msgid "Signature" +msgstr "Sinatura" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifique os IDs do Fabricante/Produto do dispositivo DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especificar o ficheiro de base de datos dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifique o número de bytes por transferenvia USB" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remoto desactivado correctamente" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remoto activado correctamente" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Instalación do firmware exitosa" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remoto modficado correctamente" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Comprobáronse correctamente as sumas de verificación do dispositivo" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Compatíbel" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-a-ocioso" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspender-a-ram" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrución do PCR0 de TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +msgid "Target" +msgstr "Obxectivo" + +#. 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 é o servizo que opera como unha entidade legal independente e non ten ligazón con $OS_RELEASE:NAME$. O seu distribuidor podería non ter que comprobar se as actualizacións de firmware teñen compatibilidade co seu sistema ou dispositivos conectados. Todos os firmware son fornecidos por fabricantes de equipamento orixinal." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Este programa podería funcionar correctamente só como root" + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Esta ferrametne só pode ser usada por un usuario root" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilidade de firmware de UEFI" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilidade UEFI dbx" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Arrinque seguro de UEFI" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Descifrado" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Descoñecido" + +msgid "Unlock the device to allow access" +msgstr "Desbloquear o dispositivo para permitir o acceso" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desbloqueado" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Quitar a bandeira de depuración durante a actualización" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Actualizar agora" + +msgid "Update the stored device verification information" +msgstr "Actualizar a información de verificación almacenada no dispositivo" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Actualizaar os metadatos almacenados cos contidos actuais" + +msgid "Updating" +msgstr "Actualizando" + +#. 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 "Actualizando %s desde %s a %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Actualizando %s…" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Desexa subir o informe agora?" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Usar fwupdtool --help para obter axuda" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Válido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validando os contidos do ESP…" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verificando…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versión" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Agardando…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Facer seguimento dos cambios de hardware" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Escribir firmware desde un ficheiro nun dispositivo" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Escribir firmware desde un ficheiro nunha partición" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Escribindo…" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Utilidade de rexistro de eventos de TPM de fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Engadidos de fwupd" diff --git a/fwupd-1.8.6/po/he.po b/fwupd-1.8.6/po/he.po new file mode 100644 index 0000000000000000000000000000000000000000..a3551425c28b72dccd62142ca2e7b06bf1a5fe1f --- /dev/null +++ b/fwupd-1.8.6/po/he.po @@ -0,0 +1,1742 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# dhead666 , 2015 +# gk , 2015 +# Omer I.S. , 2021 +# Yaron Shahrabani , 2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: he\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" + +#. TRANSLATORS: 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: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "עדכון סוללה %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "עדכון מצלמה %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "עדכון הגדרות %s" + +#. TRANSLATORS: 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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "עדכון מקלדת %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "עדכון עכבר %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "עדכון מנשק רשת %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "עדכון בקר אחסון %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "עדכון מערכת %s" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "עדכון TPM %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad 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: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s וכל ההתקנים המחוברים עשויים לא להיות שמישים בעת העדכון." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "מצב ייצור של %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "גרסת %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "יום %u" +msgstr[1] "יומיים (%u)" +msgstr[2] "%u ימים" +msgstr[3] "%u ימים" + +#. 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: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "יש תמיכה בהתקן מקומי %u" +msgstr[1] "יש תמיכה ב־%u התקנים מקומיים" +msgstr[2] "יש תמיכה ב־%u התקנים מקומיים" +msgstr[3] "יש תמיכה ב־%u התקנים מקומיים" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "דקה %u" +msgstr[1] "%u דקות" +msgstr[2] "%u דקות" +msgstr[3] "%u דקות" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "שנייה %u" +msgstr[1] "%u שניות" +msgstr[2] "%u שניות" +msgstr[3] "%u שניות" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(לא תקף עוד)" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "פעולה נדרשת:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "הפעלת התקנים" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "ההתקנים הממתינים מופעלים" + +msgid "Activate the new firmware on the device" +msgstr "הפעלת קושחה חדשה בהתקן" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "עדכון הקושחה מופעל" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "עדכון הקושחה מופעל עבור" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "גיל" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "להסכים ולהפעיל את המרוחק?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "כינוי עבור %s" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "כל ההתקנים מאותו הסוג יעודכנו באותו הזמן" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "לאפשר שנמוך גרסאות קושחה" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "לאפשר להתקין מחדש גרסאות קושחה קיימות" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "לאפשר מעבר ענפי קושחה" + +#. TRANSLATORS: 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 line option +msgid "Apply firmware updates" +msgstr "החלת עדכוני קושחה" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "החלת עדכון אפילו אם לא מומלץ" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "החלת קובצי עדכון" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "העדכון חל…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "קושחה מאומתת:" +msgstr[1] "קושחה מאומתת:" +msgstr[2] "קושחה מאומתת:" +msgstr[3] "קושחה מאומתת:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "לשאול שוב בפעם הבאה?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "הצמדה למצב קושחה" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "מתבצע אימות…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "פרטי אימות נחוצים" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "נדרש אימות כדי לשנמך חומרה על התקן נשלף" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "נדרש אימות כדי לשנמך את הקושחה במכונה הזאת" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "נדרש אימות כדי לשנות מרוחק מוגדר לעדכוני קושחה" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "נדרש אימות לשינוי הגדרות הסוכן" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "נדרש אימות כדי לשחרר התקן" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "נדרש אימות כדי לעדכן חומרה על התקן נשלף" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "אימות משתמש נדרש לעדכון קושחה מערכת זו" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "נדרש אימות כדי לעדכן את סכומי הביקורת עבור ההתקן" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "דיווח אוטומטי" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "להעלות אוטומטית בכל פעם?" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "איגוד למנהל התקן ליבה חדש" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "קובצי קושחה חסומים:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "קושחה חוסמת:" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "גרסת מנהל טעינה" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "ענף" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +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 "בוטל" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "השתנה" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "בודק שהגיבוב הקריפטוגרפי תואם לקושחה" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "סכום ביקורת" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "נא לבחור ענף:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "נא לבחור התקן:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "נא לבחור סוג קושחה" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "נא לבחור מהדורה:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "נא לבחור כרך:" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "פקודה לא נמצאה" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "המרת קובץ קושחה" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "מועד יצירה" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "קריטי" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "גרסה נוכחית" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "כלי עדכון קושחת התקן" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "אפשרויות ניפוי שגיאות" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "מתבצעת פריסה…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "תיאור" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "ניתוק למצב מנהל טעינה" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "פרטים" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +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 "נוסף התקן:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "השתנה התקן:" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "ההתקן נעול" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "ההתקן אינו נגיש" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "הוסר התקנים:" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "שיטת עדכון התקן" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "עדכון ההתקן דורש הפעלה" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "התקנים שעודכנו כראוי:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "התקנים שלא עודכנו כראוי:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "התקנים ללא עדכוני קושחה זמינים:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "התקנים עם גרסת הקושחה העדכנית ביותר שזמינה:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "מושבת" + +#. 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 unreported history" +msgstr "לא לבדוק היסטוריה לא מדווחת" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "לא לבדוק אם המרוחקים שהתקבלו אמורים להיות מופעלים" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "לא לבדוק או לבקש הפעלה מחדש לאחר עדכון" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "לא לבצע בדיקות בטיחות על ההתקן" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "לא לכתוב למסד הנתונים של ההיסטוריה" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "האם השלכות מעבר ענף הקושחה ברורות לך?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "להשבתי את היכולת הזאת לעדכונים עתידיים?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "לרענן את המרוחק הזה כעת?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "להעלות את הדוחות אוטומטית לעדכונים עתידיים?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "הסתיים!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "לשנמך את %s מגרסה %s אל %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "משנמך קושחה על התקן" + +#. TRANSLATORS: 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..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s משונמך…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "הורדת קובץ" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "מתבצעת הורדה…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "משיכת נתוני SMBIOS מקובץ" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "משך" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ה־ESP שצוין לא היה תקף" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "להפעיל מרוחק חדש?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "להפעיל את המרוחק הזה?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "מופעל" + +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "מוצפן" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "זיכרון מוצפן" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "למחוק את כל היסטוריית עדכוני הקושחה" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "מתבצעת מחיקה…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "יציאה לאחר השהייה קצרה" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "יציאה לאחר טעינת מנוע התכנה" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "ייצוא מבנה קובצי הקושחה ל־XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "חילוץ מקטע בינרי מהקושחה לקובצי דמות" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "נכשל" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "החלת העדכון נכשלה" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "טעינת ה־dbx המקומי נכשלה" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "טעינת התיקונים תואמי ההתקן נכשלה" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "טעינת ה־dbx של המערכת נכשלה" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "הנעילה נכשלה" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr " פענוח הארגומנטים נכשל" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "ניתוח הקובץ נכשל" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "פענוח הדגלונים עבור ‎--filter נכשל" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "פענוח ה־dbx המקומי נכשל" + +#. 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: filename of the local file +msgid "Filename" +msgstr "שם קובץ" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "חתימת שם קובץ" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "מקור שם קובץ" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "נדרש שם קובץ" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "כתובת בסיס קושחה" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "שירות D-Bus עדכון קושחה" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "שדון עדכון קושחה" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "כלי קושחה" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "הקושחה כבר חסומה" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "הקושחה אינה חסומה כבר" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "עדכוני קושחה" + +msgid "Firmware updates are not supported on this machine." +msgstr "במערכת הזאת אין תמיכה בעדכוני קושחה." + +msgid "Firmware updates are supported on this machine." +msgstr "במערכת הזאת יש תמיכה בעדכוני קושחה." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "עדכוני קושחה מושבתים, נא להריץ את ‚fwupdmgr unlock’ כדי להפעיל." + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "דגלים" + +msgid "Force the action ignoring all warnings" +msgstr "אילוץ הפעולה תוך התעלמות מכל האזהרות" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "נמצא" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "מקבל את כל דגלוני ההתקנים שנתמכים על ידי fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "מציג כל המכשירים התומכים בעדכוני קושחה" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "קבלת כל התוספים הפעילים שנרשמו במערכת" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "מציג פרטים אודות קובץ קושחה" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "מושכת את המרוחקים המוגדרים" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "מושך את מאפייני האבטחה של המארח" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "מקבל את רשימת הקושחות שעברו אישור" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "מקבל את רשימת הקושחות החסומות" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "מקבל את רשימת העדכונים לחומרה שמחוברת" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "מקבל את התוצאות מהעדכון האחרון" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "החומרה ממתינה לחיבור מחדש" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "גבוה" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "מזהה אבטחת מארח:" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "בהמתנה…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "התעלמות מבדיקות מחמירות של SSL בעת הורדת קבצים" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "התעלמות מכשלונות סיכום ביקורת של קושחות" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "התעלמות מכשלי התאמה בין קושחה לחומרה" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "התעלמות מאימות בדיקות בטיחות" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "משך התקנה" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "התקנת מקטע בינרי של קושחה על התקן" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "מתקין קובץ קושחה בחומרה זו" + +msgid "Install signed device firmware" +msgstr "התקנת קושחת התקן חתומה" + +msgid "Install signed system firmware" +msgstr "התקנת קושחת מערכת חתומה" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "יש להתקין להתקן הורה תחילה" + +msgid "Install unsigned device firmware" +msgstr "התקנת קושחת התקן שלא נחתמה" + +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…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "BootGuard של אינטל" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "מדיניות שגיאות של BootGuard של אינטל" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "CET של אינטל פעיל" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "CET של אינטל מאופשר" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "SMAP של אינטל" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "התקן פנימי" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "שגוי" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "במצב מנהל טעינה" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "תקלה" +msgstr[1] "תקלות" +msgstr[2] "תקלות" +msgstr[3] "תקלות" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "מחזיק מפתחות" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "שינוי אחרון" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "נותרה פחות מדקה" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "רישיון" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "שירות קושחת יצרנים ללינוקס (קושחה יציבה)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "שירות קושחת יצרנים ללינוקס (קושחה ניסיונית)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "הליבה של לינוקס" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "אזור החלפה של לינוקס" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "הצגת רשומות ב־dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "הצגת עדכוני הקושחה הנתמכים" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "הצגת סוגי הקושחות הזמינים" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "מציג קבצים ב־ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "בטעינה…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "נעול" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "נמוך" + +msgid "MEI version" +msgstr "גרסת MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "להפעיל ידנית תוספים מסוימים" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "בינוני" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "חתימת נתוני על" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "כתובת נתוני על" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "גרסה מזערית" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "משנה מרוחק נתון" + +msgid "Modify a configured remote" +msgstr "שינוי מרוחק מוגדר" + +msgid "Modify daemon configuration" +msgstr "שינוי הגדרות סוכן" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "מעקב אחר הסוכן לאיתור אירועים" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "מעגן את ה־ESP" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "דורש הפעלה מחדש לאחר ההתקנה" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "דורש כיבוי לאחר ההתקנה" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "גרסה חדשה" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "לא צוינה פעולה!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "אין שנמוכים עבור %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "לא נמצאו מזהי קושחה" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "לא אותרה חומרה בעלת יכולת עדכון קושחה" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "לא נמצאו תוספים" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "אין מהדורות זמינות" + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "אין מרוחקים זמינים" + +msgid "No updates available for remaining devices" +msgstr "אין עדכונים זמינים להתקנים שנותרו" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "לא הוחלו עדכונים" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "לא נמצא" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "לא נתמך" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "אישור" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "להשתמש ב־IPFS רק בעת הורדת קבצים" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "מותר עדכוני גרסאות בלבד" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "פלט במבנה JSON" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "פענוח והצגת פרטים על קובץ קושחה" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "עדכון ה־dbx מפוענח…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "ה־dbx של המערכת מפוענח…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "סיסמה" + +msgid "Payload" +msgstr "מטען" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "אחוזים שהושלמו" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "לבצע פעולה?" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "נא למלא מספר מ־0 עד %u:" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "תלויות התוסף חסרות" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "גרסה קודמת" + +msgid "Print the version number" +msgstr "הצגת מספר הגרסה" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "עדיפות" + +msgid "Proceed with upload?" +msgstr "להמשיך בהעלאה?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "קנייני" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "תשאול תמיכה בעדכון קושחה" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "קריאת מקטע בינרי של קושחה מהתקן" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "קריאת קושחה מהתקן לקובץ" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "קריאת קושחה ממחיצה אחת לקובץ" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "נקרא מתוך %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "מתבצעת קריאה…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "מתבצעת הפעלה מחדש…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "רענון נתוני העל משרת מרוחק" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "להתקין את %s אל %s מחדש?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "התקנת הקושחה הנוכחית על ההתקן מחדש" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "התקנת קושחה על התקן מחדש" + +#. TRANSLATORS: the 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 "מתקין מחדש %s עם %s..." + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "ענף הפצה" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "מזהה מרוחק" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "החלפת נתונים בקובץ קושחה קיים" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "כתובת לדיווח" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "דווח לשרת המרוחק" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "מערכת קבצים efivarfs נחוצה לא נמצאה" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "החומרה הנדרשת לא נמצאה" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "דורש מנהל טעינה" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "דורש חיבור לאינטרנט" + +#. 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 "החזרת כל מזהי החומרה למכונה" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "הליבה הפעילה ישנה מדי" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "סיומת סביבת הרצה" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "נעילת SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "כתיבת SPI" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "מתבצע תזמון…" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "ניתן לפנות אל %s למידע נוסף." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "התקן נבחר" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "כרך נבחר" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "מספר סידורי" + +#. TRANSLATORS: 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 all results" +msgstr "הצגת כל התוצאות" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "הצגת גרסאות לקוח וסוכן" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "הצג אפשרויות ניפוי שגיאות" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "להציג התקנים שלא ניתן לעדכן" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "הצגת מידע ניפוי שגיאות מורחב" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "הצגת היסטוריית עדכוני קושחה" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "הצגת הגרסה המחושבת של ה־dbx" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "להציג את המידע על מצב עדכון הקושחה" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "לכבות כעת?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "לחתום על חומרה עם מפתח חדש" + +msgid "Sign data using the client certificate" +msgstr "לחתום על הנתונים בעזרת אישור הלקוח" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "לחתום על הנתונים בעזרת אישור הלקוח" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "לחתום על הנתונים שנשלחים עם אישור הלקוח" + +msgid "Signature" +msgstr "חתימה" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "גודל" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "מקור" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "ציון קובץ מסד הנתוני מסוג dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "ציון מספר הבתים להעברה בודדת דרך USB" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "כל ההתקנים הופעלו בהצלחה" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "נתוני על חדשים התקבלו בהצלחה:" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "המרוחק הופעל בהצלחה" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "הקושחה הותקנה בהצלחה" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "ערך התצורה נערך בהצלחה" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "המרוחק נערך בהצלחה" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "נתוני העל התרעננו ידנית בהצלחה" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "תקציר" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "נתמך" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "נתמך בשרת המרוחק" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "השהיה למצב המתנה" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "השהיה לזיכרון" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "לעבור ענף מ־%s אל %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "החלפת ענף הקושחה בהתקן" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "המערכת דורשת מקור חשמל חיצוני" + +msgid "Target" +msgstr "יעד" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "בדיקת התקן עם מניפסט JSON" + +#. 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: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "הקושחה הזאת מבית %s אינה מסופקת על ידי %s, יצרן החומרה" + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "שעון המערכת אינו מכוון והורדת קבצים עלולה להיכשל." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "אין קובצי קושחה חסומים" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "אין קושחה שעברה אימות." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +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: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "למערכת יש תקלות בסביבת הרצת ה־HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "רמת אבטחת ה־HSI של המערכת נמוכה." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "כלי זה מאפשר להנהלה להחיל עדכוני dbx ב־UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "כלי זה מאפשר למנהלי המערכת לתשאל ולשלוט בסוכן fwupd, הוא מאפשר להם לבצע פעולות כגון התקנת או שנמוך קושחה." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "כלי זה מאפשר להנהלה להשתמש בתוספי fwupd מבלי שיותקנו על המערכת המארחת." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "בכלי הזה יכול להשתמש רק משתמש העל" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "כלי זה יקרא ויפענח את יומן האירועים של ה־TPM מקושחת המערכת." + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "סוג" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "מחיצת ESP של UEFI לא זוהתה או הוגדרה" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "כלי קושחת UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "עדכוני כמוסה של UEFI אינם זמינים או מופעלים בתצורת הקושחה" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "כלי dbx ל־UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "אי אפשר לעדכן קושחת UEFI במצב BIOS מיושן" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "מפתח פלטפורמה ב־UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "טעינה מאובטחת של UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "לא ניתן להתחבר לשירות" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "ביטול איגוד מנהל ההתקן הנוכחי" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "קושחה לא חוסמת:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "לא מוצפן" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "לא ידוע" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "התקן לא ידוע" + +msgid "Unlock the device to allow access" +msgstr "יש לשחרר את ההתקן כדי לאפשר גישה" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "משוחרר" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "משחרר את חסימת ההתקן לגישת קושחה" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "מנתק את ה־ESP" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "גרסת הסוכן %s אינה נתמכת, גרסת הלקוח היא %s" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "ניתן לעדכון" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "שגיאת עדכון" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "הודעת עדכון" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "מצב עדכון" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "כשל בעדכון זאת תקלה מוכרת, יש לבקר בכתובת למידע נוסף:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "לעדכן עכשיו?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "העדכון דורש הפעלה מחדש" + +msgid "Update the stored device verification information" +msgstr "עדכון פרטי אימות ההתקן המאוחסנים" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "עדכון נתוני העל המאוחסנים בתכנים נוכחיים" + +msgid "Updating" +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 "Updating %s from %s to %s... " +msgstr "מעדכן %s מ־%s ל־%s..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s מתעדכן…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "לשדרג את %s מגרסה %s אל %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "להעלות דוח עכשיו?" + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "דחיפות" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "יש להשתמש ב־fwupdmgr --help לקבלת עזרה" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "יש להשתמש ב־fwupdtool --help לקבלת עזרה" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "נשלחה הודעה למשתמש" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "שם משתמש" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "תקף" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "תכני ה־ESP עוברים אימות…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "הגוון" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "ספק" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "מתבצע אימות…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "גרסה" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "אזהרה:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "בהמתנה…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "מעקב אחר שינויים בחומרה" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "כתיבת קושחה מקובץ להתקן" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "כתיבת קושחה מקובץ למחיצה אחת" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "קובץ נכתב:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "מתבצעת כתיבה…" + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "יכול להיות שהחומרה שלך תיפגע עקב השימוש בקושחה הזאת והתקנת המהדורה הזאת עשויה לפגוע באחריות עם %s." + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "בררת מחדל" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "תוספים של fwupd" diff --git a/fwupd-1.8.6/po/hi.po b/fwupd-1.8.6/po/hi.po new file mode 100644 index 0000000000000000000000000000000000000000..4417c958e17846fbb8eb38bfa26a097043550753 --- /dev/null +++ b/fwupd-1.8.6/po/hi.po @@ -0,0 +1,94 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Prashant Gupta , 2015 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: hi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%s का उपनाम " + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "फर्मवेयर अपडेट के लिए प्रमाणीकरण चाहिए " + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "डिबगिंग के विकल्प " + +#. success +msgid "Done!" +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 तक अधोगति हो रही है " + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "थोड़ी देरी के बाद बहार जाएँ " + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "इंजन के लोड हो जाने पर बहार जाएँ " + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "आर्गुमेंट पार्स करने में असफल " + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "फर्मवेयर अपडेट डी-बस सेवा " + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "फर्मवेयर अपडेट का समर्थन करने वाली सभी युक्तियाँ प्राप्त करें " + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "फर्मवेयर फाइल की अधिक जानकारी प्राप्त करें " + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "फर्मवेयर फाइल को इस हार्डवेयर पर स्थापित करें " + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "अपडेट की क्षमता वाला हार्डवेयर उपलब्ध नहीं " + +#. 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 "%s को %s से दोबारा स्थापित करा जा रहा है " + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "डिबगिंग के विकल्प दिखाए " + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +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 "Updating %s from %s to %s... " +msgstr "%s को %s से %s तक अपडेट करा जा रहा है " diff --git a/fwupd-1.8.6/po/hr.po b/fwupd-1.8.6/po/hr.po new file mode 100644 index 0000000000000000000000000000000000000000..6f046e8db52120a1498511ea6a2e894093394684 --- /dev/null +++ b/fwupd-1.8.6/po/hr.po @@ -0,0 +1,3096 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# FIRST AUTHOR , 2016 +# gogo , 2016 +# Milo Ivir , 2020-2021 +# Milo Ivir , 2021 +# gogo , 2016-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: hr\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" + +#. TRANSLATORS: 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: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC nadopuna" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s nadopuna baterije" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s nadopuna CPU mikrokôda" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s nadopuna kamere" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s nadopuna podešavanja" + +#. 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 nadopuna potrošačkog pogona upravljanja (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 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 nadopuna korporativnog pogona upravljanja (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 nadopuna uređaja" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s nadopuna zaslona" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s nadopuna diska" + +#. 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 nadopuna ugrađenog upravljača" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "%s nadopuna flash memorije" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s nadopuna tipkovnice" + +#. 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 nadopuna pogona upravljanja (ME)" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s nadopuna miša" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s nadopuna mrežnog sučelja" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s nadopuna SSD diska" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s nadopuna kontrolera pohrane" + +#. 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 nadopuna sustava" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM nadopuna" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s nadopuna Thunderbolt kontrolera" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s touchpad nadopuna" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s nadopuna USB prijemnika" + +#. 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: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s i svi spojeni uređaji možda neće biti upotrebljivi tijekom nadopune." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s pojavljen: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s promijenjen: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s nestao: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s trenutno nije nadopunjiv" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s način rada za proizvođače" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s mora ostati spojen tijekom trajanja nadopune kako bi se izbjeglo oštećenje." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s mora ostati spojen s izvorom energije tijekom trajanja nadopune kako bi se izbjeglo oštećenje." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s zaobilaženje" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s inačica" + +#. 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: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u uređaj ima dostupnu nadopunu firmvera." +msgstr[1] "%u uređaja imaju dostupnu nadopunu firmvera." +msgstr[2] "%u uređaja ima dostupnu nadopunu firmvera." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u uređaj nema najbolje poznato podešavanje." +msgstr[1] "%u uređaja nema najbolje poznato podešavanje." +msgstr[2] "%u uređaja nema najbolje poznato podešavanje." + +#. 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: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u lokalni uređaj je podržan" +msgstr[1] "%u lokalna uređaja je podržano" +msgstr[2] "%u lokalnih uređaja je podržano" + +#. 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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (najmanja inačica %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(zastarjelo)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "TPM PCR je sada nevaljana vrijednost" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "AMD firmver zaštita ponavljanja" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD firmver zaštita zpisivanja" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "AMD zaštita od vraćanja na stariju inačicu" + +#. TRANSLATORS: longer description +msgid "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "AMD zaštita od vraćanja na stariju inačicu sprječava nadogradnju softvera uređaja na stariju inačicu koja ima sigurnosne probleme." + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Radnja je potrebna:" + +#. 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 firmver na uređaju" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktivacija nadopune firmvera" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktivacija nadopune firmvera za" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Dob" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Slažete li se s omogućavanjem udaljene lokacije?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Zamjena za %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Svi TPM PCR-ovi su sada valjani" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Svi TPM PCR-ovi su valjani" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Svi uređaji iste vrste će biti nadopunjeni u isto vrijeme" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Dopusti vraćanje starije inačice firmvera" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Dopusti ponovnu instalaciju firmvera postojeće inačice" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Dopusti prebacivanje između firmver grana" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Alternativni ogranak" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Primijeni nadopune firmvera" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Primijeni nadopunu iako nije preporučljivo" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Primijeni nadopunu datoteka" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Primjena nadopuna…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Odobreni firmver:" +msgstr[1] "Odobreni firmveri:" +msgstr[2] "Odobreni firmveri:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Upitaj ponovno sljedeći put?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Prebaci u način firmvera" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Ovjeravanje…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Potrebne su pojedinosti ovjere" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Potrebna je ovjera za vraćanje starije inačice firmvera na uklonjivom uređaju" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Potrebna je ovjera za vraćanje starije inačicu firmvera na ovom računalu" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Potrebna je ovjera za promjenu BIOS postavki" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Potrebna je ovjera za promjenu udaljene lokacije koja se koristi za nadopunu firmvera" + +#. 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 read BIOS settings" +msgstr "Potrebna je ovjera za čitanje BIOS postavki" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Potrebna je ovjera za postavljanje popisa odobrenih firmvera" + +#. 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 firmvera" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Potrebna je ovjera za otključavanje uređaja" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Potrebna je ovjera za nadopunu firmvera na uklonjivom uređaju" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Potrebna je ovjera za nadopunu firmvera na ovom računalu" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Potrebna je ovjera za nadopunu spremljenog kontrolnog zbroja uređaja" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatsko izvještavanje" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Automatski pošalji svaki put?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "BIOS nadopune isporučene putem LVFS-a ili Windows ažuriranja" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "IZGRADITELJ-XML NAZIV DATOTEKE-DST" + +msgid "BYTES" +msgstr "BAJTA" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Baterija" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Poveži novi kernel upravljački program" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Datoteke blokiranog firmvera:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Blokirana inačica" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokiranje firmvera:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blokira instalaciju određenog firmvera" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Inačica učitača pokretanja" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Ogranak" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Izgradi datoteku firmvera" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Odustani" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Prekinuto" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Nemoguća primjena kao dbx nadopune jer je već primijenjeno." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Nemoguća primjena na live medij" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Promijenjeno" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Provjeri podudarnost kriptografske jedinstvene vrijednosti firmvera" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolni zbroj" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Odaberi granu:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Odaberite uređaj:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Odaberi vrstu firmvera:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Odaberi izdanje:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Odaberite uređaj:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose the ESP:" +msgstr "Odaberi ESP:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Uklanja rezultate posljednje nadopune" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Naredba nije pronađena" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Podržano od strane zajednice" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Predložena promjena podešavanja" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "Podešavanje može čitati samo administrator sustava" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Pretvori datoteku firmvera" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Stvoreno" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritična" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Provjera kriptografske jedinstvene vrijednosti je dostupna" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Trenutna vrijednost" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Trenutna inačica" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "UREĐAJ-ID|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU pomagalo" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Mogućnosti otklanjanja greške" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Raspakiravanje…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Opis" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Prebaci u način učitača pokretanja" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Pojedinosti" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Odustani od najbolje poznatog podešavanja?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Oznaka uređaja" + +#. 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:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Energija baterije uređaja je preniska" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "energija baterije uređaja je preniska (%u%%, zahtijeva %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Uređaj se može oporaviti od neuspjelog zapisivanja frimvera" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Uređaj se ne može koristiti dok je poklopac zaslona zatvoren" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Uređaj promijenjen:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Potreban je firmver uređaja za provjeru inačice" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Uređaj je emuliran" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Uređaj je zaključan" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Uređaj je potreban za instalaciju svih dostupnih izdanja" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Uređaj je nedostupan" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Uređaj je nedostupan ili je izvan dometa bežične mreže" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Uređaj se može koristiti tijekom trajanja nadopune" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Uređaj čeka na primjenu nadopune" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Uređaj uklonjen:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Uređaj zahtijeva vanjski izvor energije" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Nadopune softvera uređaja su isporučene za ovaj uređaj." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Nadopune uređaja u fazama" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Uređaj podržava prebacivanje na drugačiji ogranak firmvera" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Način nadopune frimvera" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Nadopuni uređaja je potrebna aktivacija" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Uređaj će sigurnosno kopirati firmver prije instalacije" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Uređaj se neće ponovno pojaviti nakon završetka nadopune" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Uređaji koji su uspješno nadopunjeni:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Uređaji koji nisu ispravno nadopunjeni:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Uređaji bez dostupnih nadopuna firmvera: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Uređaji s najnovijom dostupnom inačicom firmvera:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Nije pronađen niti jedan uređaj s podudarajućim GUID-ovima" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Onemogućeno" + +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Ne provjeravaj neprijavljenu povijest" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Ne provjeravaj trebaju li preuzete udaljene lokacije biti omogućene" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Ne provjeravaj ili upitaj za ponovno pokretanje nakon nadopune" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Ne uključuj prefiks domene zapisa" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Ne uključuj prefiks vremena" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Nemoj izvoditi provjere sigurnosti uređaja" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Ne upitaj za uređaje" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Ne zapisuj bazu podataka povijesti" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Razumijete li posljedice promjene ogranka firmvera?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Želite li onemogućiti ovu značajku za buduće nadopune?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Želite li odmah provjeriti ovu udaljenu lokaciju?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Želite li poslati izvještaje automatski za buduće nadopune?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Ne pitaj za ovjeru (može biti prikazano manje pojedinosti)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Završeno!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Vrati %s na stariju inačicu sa %s na %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Vraća stariju inačicu firmvera na uređaju" + +#. 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 "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: command description +msgid "Download a file" +msgstr "Preuzmi datoteku" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Preuzimanje…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Ispiši opširnije podatke SMBIOS-a iz datoteke" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Trajanje" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Određeni ESP nije ispravan" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Svaki uređaj bi se trebao testirati firmver, kako bi se osigurala sigurnost firmvera." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Emulirani poslužitelj" + +msgid "Enable" +msgstr "Omogući" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Omogući podršku nadopune firmvera na podržanim sustavima" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Omogući udaljenu lokaciju?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Omogući ovu udaljenu lokaciju?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Omogućeno" + +msgid "Enabled fwupdate debugging" +msgstr "Omogući fwupdate otklanjanje grešaka" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Omogućeno ako se hardver podudara" + +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "Šifrirano" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Šifrirani RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Šifrirani RAM čini nemogućim čitanje informacija spremljenih u memoriji uređaja ako je memorijski čip uklonjen i može mu se pristupiti." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Kraj podrške" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Nabrajanje" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Obriši svu povijest nadopune firmvera" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Brisanje…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Izađi nakon kratke odgode" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Izađi nakon učitavanja pogona" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Izvezi strukturu datoteke frimvera u XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Izdvoji blob firmvera u slike" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "DATOTEKA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "DATOTEKA [UREĐAJ-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NAZIV DATOTEKE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "PRIVATNI-KLJUČ NAZIV DATOTEKE VJERODAJNICE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NAZIV DATOTEKE UREĐAJA-ALT-NAZIV|UREĐAJ-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NAZIV DATOTEKE UREĐAJA-ALT-NAZIV|UREĐAJ-ALT-ID [SLIKA-ALT-NAZIV|SLIKA-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NAZIV DATOTEKE ID-UREĐAJA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "NAZIV DATOTEKE POMAKA PODATAKA [FIRMVER-VRSTA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NAZIV DATOTEKA [UREĐAJ-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NAZIV DATOTEKE [FIRMVER-VRSTA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NAZIV DATOTEKE-SRC NAZIV DATOTEKE-DST [FIRMVER-VRSTA-SRC] [FIRMVER-VRSTA-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "NAZIV DATOTEKE|KONTROLNI ZBROJ1[,KONTROLNI ZBROJ2][,KONTROLNI ZBROJ3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Neuspjelo" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Neuspjela primjena nadopune" + +#. TRANSLATORS: error message for Windows +#, c-format +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Neuspjelo povezivanje s Windows uslugom, provjerite je li pokrenuta." + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Neuspjelo povezivanje s pozadinskim programom" + +#. 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 firmvera" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Neuspjelo učitavanje lokalnog dbx-a" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Neuspjelo učitavanje okolnosti uređaja" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Neuspjelo učitavanje dbx-a sustava" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Neuspjelo zaključavanje" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Neuspjela obrada argumenata" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Neuspjela obrada datoteke" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Neuspjela obrada oznake za --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Neuspjela obrada lokalnog dbx-a" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Neuspjelo ponovno pokretanje" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Neuspjelo postavljanje splash načina" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Neuspjela provjera ESP sadržaja" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Laž" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Naziv datoteke" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Potpis naziva datoteke" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Izvor naziva datoteke" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Potreban je naziv datoteke" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtar sa skupom oznaka uređaja koji koristi ~ prefiks za izuzimanje, npr. 'internal,~needs-reboot'" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Frimver provjera" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Frimver provjera provjerava softver uređaja koristeći izvornu kopiju, kako bi se osiguralo da nije mijenjan." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Frimver BIOS opisnik" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "Frimver BIOS opisnik štiti memoriju frimvera uređaja od neovlaštenog mijenjanja." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Firmver BIOS područje" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "Firmver BIOS područje štiti memoriju firmvera uređaja od nevlaštenog mijenjanja." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Osnovni URI firmvera" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Firmver nadopuna D-Bus usluge" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Pozadinski program nadopune firmvera" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Provjera firmver nadopunitelja" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Provjera firmver nadopunitelja provjerava je li softver za nadopunu neovlašteno mijenjan." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Nadopune frimvera" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Firmver pomagalo" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Firmver zaštita zapisivanja" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Frimver zaključana zaštita zapisivanja" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Firmver zaštita zapisivanja štiti memoriju firmvera uređaja od nevlaštenog mijenjanja." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Frimver provjera" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmver je već blokiran" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmver još nije blokiran" + +#. 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] "Metapodaci firmvera nisu nadopunjeni %u dan i možda nisu najnoviji." +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." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Posljednja provjera metapodatka frimvera: prije %s. Koristite --force za ponovnu provjeru." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Nadopune frimvera" + +msgid "Firmware updates are not supported on this machine." +msgstr "Nadopuna firmvera nije podržana na ovom računalu." + +msgid "Firmware updates are supported on this machine." +msgstr "Nadopuna firmvera je podržana na ovom računalu." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Nadopune firmvera su onemogućene ; pokrenite 'fwupdmgr unlock' za omogućavanje" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Oznake" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Prisili radnju zanemarujući neke provjere vremena pokretanja" + +msgid "Force the action ignoring all warnings" +msgstr "Prisili radnju zanemarujući sva upozorenja" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Pronađeno" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Potpuno šifriranje diska je otkriveno" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Tajne potpunog šifriranja diska mogu biti poništene pri nadopuni" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Pregorjela platforma" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Pregorjela platforma" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" +msgstr[2] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Prikaži BIOS postavke" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Prikaži sve oznake uređaja koje podržava fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Prikaži sve uređaje koji podržavaju nadopunu firmvera" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Prikaži sve omogućene priključke registrirane sa sustavom" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Prikaži pojedinosti datoteke firmvera" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Prikazuje udaljena podešavanja" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Prikaži sigurnosne značajke poslužitelja" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Preuzima popis odobrenih firmvera" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Preuzima popis blokiranih firmvera" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Prikaži popis nadopuna za povezani hardver" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Prikazuje izdanja za uređaj" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Prikaži rezultate posljednje nadopune" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-DATOTEKA" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hardver čeka da ponovno bude spojen" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Visoka" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Događaji sigurnosti računala" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "ID sigurnosti sustava (HSI) nije podržan" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Sigurnosni ID poslužitelja:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU zaštita" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "IOMMU zaštita sprječava pristup povezanih urađaja neovlaštenim dijelovima memorije." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Onemogućena zaštita IOMMU uređaja" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Omogućena zaštita IOMMU uređaja" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Mirovanje…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Zanemari SSL ograničene provjere pri preuzimanju datoteka" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Zanemari neuspjele kontrolne zbrojeve firmvera" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Zanemari neuspjela poklapanja hardvera firmvera" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Zanemari provjeru sigurnosti uređaja" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Zanemaruju se stroge SSL provjere, kako bi se ovo ubuduće automatski izvezlo DISABLE_SSL_STRICT u svojem okruženju" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Trajanje instalacije" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instaliraj blob firmvera na uređaj" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instaliraj datoteku firmvera na ovaj uređaj" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Instaliraj određeni firmver na uređaj, svi mogući uređaji će biti instalirani jednom kada se CAB podudara" + +msgid "Install old version of signed system firmware" +msgstr "Instaliraj staru inačicu potpisanog frimvera sustava" + +msgid "Install old version of unsigned system firmware" +msgstr "Instaliraj staru inačicu nepotpisanog frimvera sustava" + +msgid "Install signed device firmware" +msgstr "Instaliraj firmver potpisan uređajem" + +msgid "Install signed system firmware" +msgstr "Instaliraj firmver potpisan sustavom" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instaliraj prvo u nadređeni uređaj" + +msgid "Install unsigned device firmware" +msgstr "Instaliraj firmver nepotpisan uređajem" + +msgid "Install unsigned system firmware" +msgstr "Instaliraj firmver nepotpisan sustavom" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Instalacija firmvera…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Instalacija nadopune firmvera…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Instaliram na %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Instaliranje ove nadopune može poništiti svako jamstvo uređaja." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Cijeli broj" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard ACM zaštićen" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM zaštićen" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Intel BootGuard pravilo greške" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Intel BootGuard pravilo greške osigurava da se uređaj ne nastavlja pokretati ako je njegov softver uređaja neovlašteno mijenjan." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard OTP osigurač" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP osigurač" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Intel BootGuard provjereno pokretanje" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard pravilo greške" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard sprječava pristup neovlaštenog softvera uređaja iz operativnog sustava kada se uređaj pokrene." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard provjereno pokretanje" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktivan" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET omogućen" + +#. TRANSLATORS: longer description +msgid "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Intel Control-Flow Enforcement Technology otkriva i sprječava određene načine pokretanja zlonamjernog softvera na uređaju." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Način rada Intel pogona upravljanja" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Zaobilaženje Intel pogona upravljanja" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "Zaobilaženje Intel pogona upravljanja (Intel Management Engine) onemogućava provjere za neovlaštni softver uređaja." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Inačica Intel pogona upravljanja" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: longer description +msgid "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Intel Supervisor Mode Access Prevention osigurava da manje sigurni programi ne pristupaju ključnim dijelovima memorije uređaja." + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Unutrašnji uređaj" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Nevaljano" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Nevaljani argumenti" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Je nadogradnja na stariju inačicu" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "U načinu učitača pokretanja je" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Je nadogradnja" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problem" +msgstr[1] "Problemi" +msgstr[2] "Problemi" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "KLJUČ,VRIJEDNOST" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Kernel više nije kontaminiran" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Kernel je kontaminiran" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Zaključavanje kernela onemogućeno" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Zaključavanje kernela omogućeno" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Skup ključeva" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOKACIJA" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Posljednja promjena" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Preostalo je manje od minute" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licenca" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Linux kernel zaključavanje" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Linux Kernel zaključavanje sprječava administratorske (korijenske) račune da pristupe i mijenjaju ključne dijelove softvera sustava." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Linux Kernel Swap privremeno sprema informacije na disk dok radite. Ako informacije nisu zaštićene, netko im može pristupiti ako je u posjedu diska." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Linux Kernel provjera" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "Linux Kernel provjera osigurava da ključni softver sustava nije mijenjan. Korištenje upravljačkih progrma uređaja koji se ne isporučuju sustavom može sprječiti ispravan rad ove značajke." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux swap" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Firmver Usluga Linux Proizvođača (LVFS) (stabilni firmver)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Firmver Usluga Linux Proizvođača (LVFS) (testni firmver)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel zaključavanje" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Prikaži unose u dbx-u" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Prikaži nadopune podržanih firmvera" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Prikaži dostupne vrste firmvera" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Prikazuje datoteke na ESP-u" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Učitavanje…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Zaključano" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Niska" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI način rada za proizvođače" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI zaobilaženje" + +msgid "MEI version" +msgstr "MEI inačica" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Ručno omogući određene priključke" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Način rada za proizvođače se koristi kada je uređaj proizveden i sigurnosne značajke još nisu omogućene." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Najveća duljina" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Najviša vrijednost" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Srednja" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metapodaci potpisa" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI metapodataka" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metapodaci se mogu dobiti od Firmver Usluge Linux Proizvođača (LVFS)." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Najmanja inačica" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Najmanja duljina" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Najmanja vrijednost" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Pozadinski program i klijent se ne podudaraju, umjesto koristite %s" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Mijenja vrijednost podešavanja pozadinskog programa" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Promjena zadane udaljene lokacije" + +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" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Montira ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "NAPOMENA: Ovaj program može samo raditi samo s korijenskim ovlastima" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Potrebno je ponovno pokretanje nakon instalacije" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Zahtijeva ponovno pokretanje" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Potrebno je isključivanje nakon instalacije" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nova inačica" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nema zadane radnje!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Nema starijih inačica za %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nema pronađenog ID firmvera" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nema otkrivenog hardvera s mogućnosti nadopune firmvera" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nema pronađenih priključaka" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nema dostupnih izdanja" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Trenutno nema omogućenih udaljenih lokacija stoga nema dostupnih metapodataka." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nema dostupnih udaljenih lokacija" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Nema nadopunjivih uređaja" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Nema dostupnih nadopuna" + +msgid "No updates available for remaining devices" +msgstr "Nema dostupnih nadopuna za preostale uređaje" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nema primijenjenih nadopuna" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Nije odobreno" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "Nedovoljno podataka je dostupno za HIS izračun." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nije pronađeno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nije podržano" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "U redu" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "U redu!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Prikaži samo jednu PRC vrijednost" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Koristi samo IPFS pri preuzimanju datoteka" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Samo nadogradnje inačice su dopuštene" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Izlaz u JSON formatu" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Zaobiđi zadanu ESP putanju" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PUTANJA" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Obradi i prikaži pojedinosti datoteke firmvera" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Obrada dbx nadopune…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Obrada dbx-a sustava…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Lozinka" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Zakrpaj datoteku firmvera na poznati pomak" + +msgid "Payload" +msgstr "Sadržaj prijenosa" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Na čekanju" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Postotak završetka" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Obavi radnju?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Otklanjanje grešaka platforme" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "Otklanjanje grešaka platforme dopušta onemogućavanje sigurnosnih značajki uređaja. Ovo bi trebali koristit samo proizvođači hardvera." + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Pobrinite se da imate ključ oporavka uređaja prije nastavka." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Odaberite broj od 0 do %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Zavisnosti priključka nedostaju" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Moguće vrijednosti" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "DMA zaštita predpokretanja" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "DMA zaštita predpokretanja" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "DMA zaštita prije pokretanja je onemogućena" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "DMA zaštita prije pokretanja je omogućena" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "DMA zaštita predpokretanja sprječava pristup uređaja memoriji sustava nakon što se povežu s računalom. " + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Prijašnja inačica" + +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" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problemi" + +msgid "Proceed with upload?" +msgstr "Nastavi sa slanjem?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Sigurnosna provjera procesora" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Vlasnički" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Zatraži podršku nadopune firmvera" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "UDALJENA_LOKACIJA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "VRIJEDNOST KLJUČA ID-UDALJENE_LOKACIJE" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Dozvola čitanja" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Očitaj blob firmvera s uređaja" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Očitaj firmver s uređaja" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Očitaj firmver iz uređaja u datoteku" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Očitaj firmver iz jedne particije u datoteku" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Čitanje iz %s…" + +#. TRANSLATORS: reading from the flash chips +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" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Ponovno instaliraj %s na %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Ponovno instaliraj trenutni firmver na uređaj" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Ponovno instaliraj firmver na uređaj" + +#. 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 "Ponovna instalacija %s inačice %s... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Ogranak izdanja" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Oznake izdanja" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID izdanja" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Udaljeni ID" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Zamijeni podatke u postojećoj datoteci firmvera" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI izvještaja" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Prijavljeno na udaljenom poslužitelju" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Zahtjev je prekinut" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Potrebni efivarfs datotečni sustav nije pronađen" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Potreban hardver nije pronađen" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Zahtijeva učitača pokretanja" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Potreban je pristup internetu" + +#. TRANSLATORS: reboot to apply the update +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…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Preuzmi BIOS postavke. Ako se argumenati ne proslijede, postavke su nepromijenjene" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Vrati sve ID-ove hardvera za uređaj" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Zaštita od vraćanja na stariju inačicu" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Pokrenite `fwupdmgr get-upgrades` za više informacija." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Za završavanje ove radnje pokreni `fwupdmgr sync-bkc`." + +#. 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" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Pokrenite bez '%s' za prikaz" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Pokrenuti kernel je prestar" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufiks vremenskog izvršavanja" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "VRIJEDNOST POSTAVKE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "POSTAVKA1 VRIJEDNOST1 [POSTAVKA2] [VRIJEDNOST2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS opisnik" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS područje" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI zaključavanje" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Zaštita od SPI ponavljanja" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI zapisivanje" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Zaštita od SPI zapisivanja" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "UPRAVLJAČKI PROGRAM PODSUSTAVA [UREĐAJ-ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Spremi datoteku koja omogućuje stvaranje ID-ova hardvera" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Skalarni umnožak" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Zakaži instalaciju pri sljedećem pokretanju kada je moguće" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Zakazivanje…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Sigurno pokretanje (SecureBoot) onemogućeno" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Sigurno pokretanje (SecureBoot) omogućeno" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Pogledajte %s za više informacija." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Pogledajte %s za više informacija." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Odabrani uređaj" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Odabrani uređaj" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Serijski broj" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Postavi BIOS postavku '%s' koristeći '%s'." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Postavi BIOS postavku" + +msgid "Set one or more BIOS settings" +msgstr "Postavi jednu ili više BIOS postavki" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Postavi oznaku otklanjanja grešaka tijekom nadopune" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Postavlja jednu ili više BIOS postavki" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Postavlja popis odobrenih firmvera" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Vrsta postavke" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Postavke će se primijeniti nakon ponovnog pokretanja sustava" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Podijeli povijest firmvera sa razvijateljima" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Prikaži sve rezultate" + +#. TRANSLATORS: command line option +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 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" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Prikaži povijest nadopune metapodataka" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Prikaži dodatne informacije priključka" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Prikaži predviđenu inčicu DBX-a" + +#. 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 firmvera" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Odmah isključi?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Potpiši frimver s novim ključem" + +msgid "Sign data using the client certificate" +msgstr "Potpiši podatke koristeći vjerodajnicu klijenta" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Potpisani sadržaj prijenosa" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Veličina" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Određene tajne platforme mogu biti poništene tijekom nadopune ovog firmvera." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Izvor" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Odredi ID-ove Proizvođača/Proizvoda DFU uređaja" + +#. TRANSLATORS: command line option +msgid "Specify a filename to use to save backend events" +msgstr "Odredi naziv datoteke za spremanje pozadinskih događaja" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Odredi datoteku dbx baze podataka" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Odredi broj bajtova po USB prijenosu" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Izraz" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Uspješno" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Svi uređaji su uspješno aktivirani" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Udaljena lokacija je uspješno onemogućena" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Novi metapodaci su uspješno preuzeti: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Udaljena lokacija je uspješno omogućena i provjerena" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Udaljena lokacija je uspješno omogućena" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmver je uspješno instaliran" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Vrijednost podešavanja je uspješno promijenjenja" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Udaljena lokacija je uspješno promijenjena" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metapodaci su ručno uspješno osvježni" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Kontrolni zbroj uređaja je uspješno nadopunjen" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Uspješno poslan %u izvještaj" +msgstr[1] "Uspješno poslana %u izvještaja" +msgstr[2] "Uspješno poslano %u izvještaja" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Kontrolni zbroj uređaja je uspješno provjeren" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Sažetak" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Podržano" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Podržan je CPU" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Podržano na udaljenom poslužitelju" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Suspendiraj u mirovanje" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Suspendiraj u RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspendiraj u mirovanje omogućuje uređaju brz odlazak na spavanje u svrhu štednje energije. Dok je uređaj suspendiran, njegova se memorija može fizički ukloniti i na taj način pristupiti informacijama." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspendiraj u RAM omogućuje uređaju brz odlazak na spavanje u svrhu štednje energije. Dok je uređaj suspendiran, njegova se memorija može fizički ukloniti i na taj način pristupiti informacijama." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspendiraj-u-mirovanje" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspendiraj-u-ram" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Prebaci ogranak sa %s na %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Zamijeni ogranak firmvera na uređaju" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Uskladi inačicu firmvera s najbolje poznatim podešavanjem na poslužitelju" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Energija sustava je preniska za obavljanje nadopune" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "energija sustava je preniska za obavljanje nadopune (%u%%, zahtijeva %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Sustav zahtijeva vanjski izvor energije" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEKST" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (Pouzdani Modul Platforme) je računalni čip koji otkriva neovlaštene promjene hardverskih komponenta." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 rekonstrukcija" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0 rekonstrukcija je nevaljana" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "TPM PCR0 rekonstrukcija je sada valjana" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "Podešavanje TPM platforme" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "TPM rekonstrukcija" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "TPM prazani PCR-ovi" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Oznaka" +msgstr[1] "Oznake" +msgstr[2] "Oznake" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Pokvareno" + +msgid "Target" +msgstr "Odredište" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testiraj uređaj koristeći JSON manifest" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "Intel pogon upravljanja (Intel Management Engine) upravlja komponentama uređaja i mora biti najnovije inačice kako bi se izbjegli sigurnosni problemi." + +#. 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 (Firmver 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 firmvera za kompatibilnost s vašim sustavom ili priključenim uređajima. Svi firmveri su pružani od strane izvornih proizvođača opreme." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "Podešavanje TPM (Pouzdani Modul Platforme) platforme se koristi za provjeru je li proces pokretanja uređaja mijenjan." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "TPM (Pouzdani Modul Platforme) rekonstrukcija se koristi za provjeru je li proces pokretanja uređaja mijenjan." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 razlikuje se od rekonstrukcije." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "UEFI ključ platforme se koristi za otkrivanje dolazi li softver uređaja iz pouzdanih izvora." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Pozadinski program je učitao kȏd treće strane i više nije podržan od upstream razvijatelja!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Inačica uređaja se ne podudara: dobivena %s, očekivana %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "%s firmver nije isporučen od strane %s dobavljača hardvera." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Sat sustava nije pravilno postavljen i preuzimanje datoteka možda ne uspije." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Proizvođač nije objavio nikakve bilješke izdanja." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Postoje uređaji s problemima:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Nema blokiranih firmvera" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Ne postoji odobreni firmver." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Ovaj uređaj će biti vraćen natrag na %s kada se %s naredba pokrene." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Ovaj firmver je omogućen od strane LVFS članova zajednice i nije omogućen (ili podržan) od strane izvornog proizvođača hardvera." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Ovaj paket još nije provjeren, možda neće ispravno raditi." + +#. 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 firmver 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 firmvera ako nadopuna firmvera ne uspije." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "Ovaj sustav ne podržava postavke firmvera" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Ovaj sustav ima HSI probleme s vremenskim izvršavanjem." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Ovaj sustav ima nisku HSI sigurnosnu razinu." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Ovaj alat omogućuje administratoru primjenu UEFI dbx nadopuna." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Ovaj alat dopušta administratoru otklanjanje grešaka u UpdateCapsule radnji." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Ovaj alat dopušta administratoru postavljanje upita i upravljanje fwupd pozadinskim programom, što omogućuje obavljanje radnji poput instalacije i vraćanje starije inačice frimvera." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Ovaj alat dopušta administratoru korištenje fwupd priključaka bez instalacije na sustavu." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "Ovaj alat može promijeniti BIOS postavku '%s' iz '%s' u '%s' automatski, ali će biti aktivno nakon ponovnog pokretanja računala." + +#. 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: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Ovo pomagalo će očitati i obraditi TPM zapis događaja iz frimvera sustava." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Za zanemraivanje ovog upozorenja, koristite --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Privremeni kvar" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Istina" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Pouzdani metapodaci" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Pouzdan sadržaj prijenosa" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Vrsta" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP particija nije otkrivena ili podešena" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI firmver pomagalo" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI ključ platforme" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI sigurno pokretanje (SecureBoot)" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "UEFI sigurno pokretanje (SecureBoot) sprječava učitavanje zlonamjernog softvera pri pokretanju uređaja." + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Nadopune UEFI kapsule nisu dostupne ili omogućene u frimver postavljanju" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx pomagalo" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI firmver ne može se nadopuniti u zastarjelom BIOS načinu" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI ključ platforme" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI sigurno pokretanje (SecureBoot)" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Neuspjelo povezivanje s udaljenom uslugom" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Nemoguć pronalazak svojstva" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Prekini povezivanje s trenutnim upravljačkim programom" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Neblokiranje firmvera:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Deblokira instalaciju određenog firmvera" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Nije šifrirano" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Nepoznat" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Nepoznat uređaj" + +msgid "Unlock the device to allow access" +msgstr "Otključaj uređaj za dopuštenje pristupa" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Otključano" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Otključava uređaj za pristup firmveru" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Demontira ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Ukloni oznaku otklanjanja grešaka tijekom nadopune" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Nepotpisani sadržaj prijenosa" + +#. 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: Suffix: the HSI result +msgid "Untainted" +msgstr "Ispravno" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Nadopunjivo" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Greška nadopune" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Slika nadopune" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Poruka nadopune" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stanje nadopune" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Neuspješna nadopuna je poznat problem, posjetite ovaj URL za više informacija:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Nadopuni odmah?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Nadopuna zahtijeva ponovno pokretanje" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Nadopuni spremljenu kriptografsku jedinstvenu vrijednost s trenutnim sadržajem ROM-a" + +msgid "Update the stored device verification information" +msgstr "Nadopuni spremljenu informaciju provjere uređaja" + +#. 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 specified devices to latest firmware version, or all devices if unspecified" +msgstr "Nadopuni sve navedene uređaje na najnovije inačice firmvera, ili sve uređaje ako nisu navedeni" + +msgid "Updating" +msgstr "Nadopunjivanje" + +#. 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 "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: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Nadogradi %s sa %s na %s?" + +#. TRANSLATORS: ask the user to upload +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 firmvera pomaže proizvođačima hardvera brzo otkrivanje nedostatka i brzu nadopunu na stvarnim uređajima." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Hitnost" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Koristite fwupdmgr --help za prikaz pomoći" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Koristite fwupdtool --help za prikaz pomoći" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Koristi oznake okolnosti računala tijekom instalacije firmvera" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Korisnik je obaviješten" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Korisničko ime" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valjano" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Provjera ESP sadržaja…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Varijanta" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Proizvođač" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Provjeravanje…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Inačica" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "UPOZORENJE:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Čekanje…" + +#. 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 firmver iz datoteke u uređaj" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Zapiši firmver iz datoteke u jednu particiju uređaja" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Zapisivanje datoteke:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Zapisivanje…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Trebali bi osigurati da možete vratiti postavke iz podešavanja firmvera sustava, pošto ova promjena može uzrokovati nemogućnost pokretanja u Linux sustav ili uzrokovati druge nestabilnosti sustava." + +#. 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 firmvera za kompatibilnost s vašim sustavom ili priključenim uređajima." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Vaš hardver se može oštetiti upotrebom ovog firmvera, instalacija ovog izdanja može poništiti jamstvo sa %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Vaš sustav je postavljen na BKC od %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[KONTROLNI ZBROJ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[UREĐAJ-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[UREĐAJ-ID|GUID] [OGRANAK]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ID|GUID-UREĐAJA] [INAČICA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[DATOTEKA POTPIS_DATOTEKE ID-UDALJENE_LOKACIJE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NAZIV DATOTEKE1] [NAZIV DATOTEKE2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[POSTAVKA1] [POSTAVKAG2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[POSTAVKA1] [POSTAVKA2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-DATOTEKA|HWIDS-DATOTEKA]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "zadano" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM pomagalo zapisa događaja" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd priključci" diff --git a/fwupd-1.8.6/po/hu.po b/fwupd-1.8.6/po/hu.po new file mode 100644 index 0000000000000000000000000000000000000000..8cd84d653529d2b72d2114f27105ca7639e95ec4 --- /dev/null +++ b/fwupd-1.8.6/po/hu.po @@ -0,0 +1,1277 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# 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" +"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" +"Content-Transfer-Encoding: 8bit\n" +"Language: hu\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: 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: 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 fogyasztói ME frissíté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 vezérlőfrissíté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 vállalati ME 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: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt vezérlőfrissí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: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "Lehet, hogy a(z) %s és az összes kapcsolódó eszköz használhatatlan lesz a frissítés alatt." + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "A károsodás elkerülése miatt szükséges, hogy a(z) %s kapcsolódva maradjon a frissítés során." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "A károsodás elkerülése miatt szükséges, hogy a(z) %s csatlakoztatva maradjon egy áramforráshoz a frissítés során." + +#. 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: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u helyi eszköz támogatott" +msgstr[1] "%u helyi eszköz támogatott" + +#. 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: the age of the metadata +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" +msgstr "Álnév ehhez: %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Firmware verziók visszafejlesztésének engedélyezése" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "A meglévő firmware verziók újratelepítésének engedélyezése" + +#. 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" + +#. 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 to firmware mode" +msgstr "Csatlakoztatás a firmware módhoz" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Hitelesítés…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Hitelesítés szükséges a firmware visszafejlesztéséhez egy cserélhető eszközön" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Hitelesítés szükséges a firmware visszafejlesztéséhez ezen a gépen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +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 modify daemon configuration" +msgstr "Hitelesítés szükséges a démon konfigurációjának 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" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Hitelesítés szükséges a firmware frissítéséhez egy cserélhető eszközön" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Hitelesítés szükséges a firmware frissítéséhez ezen a gépen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Hitelesítés szükséges az eszköz tárolt ellenőrzőösszegeinek frissítéséhez" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatikus jelentés" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Rendszerbetöltő verziója" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Mégse" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Megszakítva" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Módosítva" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Ellenőrzi, hogy a kriptográfiai hash egyezik-e a firmware-rel" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Ellenőrzőösszeg" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Válasszon eszközt:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Válasszon firmware típust:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Válasszon kiadást:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Törli a legutóbbi frissítésből származó eredményeket" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "A parancs nem található" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Kriptográfiai hash ellenőrzés érhető el" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Jelenlegi verzió" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU segédprogram" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Hibakeresési beállítások" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Kibontás…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Leírás" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Leválasztás a rendszerbetöltő módhoz" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Részletek" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Eszközjelzők" + +#. 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:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Az eszköz képes helyreállni a felülírási hibák után" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Eszköz módosítva:" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Az eszköz zárolt" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Az eszköz használható marad a frissítés ideje alatt" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Eszköz eltávolítva:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Az eszköz szakaszosan frissít" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Az eszközfrissítés aktiválást igényel" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Az eszköz a frissítés végeztével újra meg fog jelenni" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Eszközök, melyek sikeresen frissítve lettek:" + +#. TRANSLATORS: a list of failed updates +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Ne ellenőrizze a nem jelentett előzményeket" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Ne tartalmazza a naplózási tartomány előtagot" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Ne tartalmazza az időbélyeg előtagot" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Ne végezzen eszköz biztonsági ellenőrzéseket" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Ne írjon az előzmények adatbázisába" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Kész!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "A firmware visszafejlesztése az eszközön" + +#. 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 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…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "SMBIOS adatok kiírása egy fájlból" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Hossz" + +#. 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: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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 "Erase all firmware update history" +msgstr "Firmware frissítési előzmények törlése" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Törlés…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Kilépés egy kis késleltetés után" + +#. TRANSLATORS: exit straight away, used for automatic profiling +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: 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" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Nem sikerült feldolgozni az argumentumokat" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "A --filter jelzőinek feldolgozása sikertelen" + +#. 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: filename of the local file +msgid "Filename" +msgstr "Fájlnév" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Fájlnév aláírása" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Szűrés eszközjelzők megadásával, ~ előtag a kihagyáshoz, például „internal,~needs-reboot”" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Firmware kiindulópont URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Firmware frissítés D-Bus szolgáltatás" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Firmware frissítő démon" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Firmware segédprogram" + +#. 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] "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: description of plugin state, e.g. disabled +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: Suffix: the HSI result +msgid "Found" +msgstr "Megtalálva" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID-ok" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Az fwupd által támogatott összes eszközjelző 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" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Lekéri a beállított távoli tárolókat" + +#. 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" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Lekéri az eszközhöz tartozó kiadásokat" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "A legutóbbi frissítésből származó eredményeket kéri le" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "A hardver újracsatlakoztatásra vár" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Üresjárat…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "A szogorú SSL ellenőrzések mellőzése a fájlok letöltésekor" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Biztonsági ellenőrzések mellőzése" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Telepítés hossza" + +#. 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 signed device firmware" +msgstr "Aláírt eszköz firmware telepítése" + +msgid "Install signed system firmware" +msgstr "Aláírt rendszer firmware telepítése" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Elsőként telepítés a szülő eszközre" + +msgid "Install unsigned device firmware" +msgstr "Nem aláírt eszköz firmware telepítése" + +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…" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Belső eszköz" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Rendszerbetöltő módban van" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Probléma" +msgstr[1] "Problémák" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Kulcstartó" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Utoljára módosítva" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Kevesebb mint egy perc van hátra" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licenc" + +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 line option +msgid "List supported firmware updates" +msgstr "A támogatott firmware-frissítések listázása" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Az elérhető firmware típusok listázása" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Betöltés…" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metaadatok aláírása" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metaadat URI" + +#. 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." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Legkisebb verzió" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Eltérő démon és kliens, inkább ezt használja: %s" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "A megadott távoli tároló módosítása" + +msgid "Modify a configured remote" +msgstr "A beállított távoli tároló módosítása" + +msgid "Modify daemon configuration" +msgstr "Démon konfigurációjának módosítása" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "A démon eseményeinek figyelése" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "A telepítés után újraindítást igényel" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "A telepítés után kikapcsolást igényel" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Új verzi" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nincs művelet megadva!" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nem találhatók firmware azonosítók" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "Nincsenek elérhető kiadások" + +#. 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: no repositories to download from +msgid "No remotes available" +msgstr "Nincsenek elérhető távoli tárolók" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nem lett frissítés alkalmazva" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Az alapértelmezett ESP útvonal felülbírálása" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "A firmware fájl részleteinek értelmezése és megjelenítése" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Jelszó" + +msgid "Payload" +msgstr "Tartalom" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Százalék kész" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Adjon meg egy számot 0 és %u között:" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Előző verzió" + +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" + +msgid "Proceed with upload?" +msgstr "Folytatja a feltöltést?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Tulajdonosi" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Firmware frissítési támogatás lekérdezése" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Egy firmware blob beolvasása egy eszközről" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Firmware beolvasása eszközről egy fájlba" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Firmware beolvasása egy partícióról fájlba" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Olvasás a(z) %s eszközről…" + +#. TRANSLATORS: reading from the flash chips +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" + +#. 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 "%s újratelepítése ezzel: %s…" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Távoli azonosító" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Adatok cseréje egy meglévő firmware fájlban" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Jelentési URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Jelentve a távoli kiszolgálónak" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Rendszerbetöltő szükséges" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Internetkapcsolat szükséges" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Újraindítja most?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Újraindítja a démont a változás életbe léptetéséhez?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Eszköz újraindítása…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "A géphez tartozó összes hardverazonosító visszaadása" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "A bővítmény összetett tisztítási rutinjának futtatása az install-blob használatakor" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "A bővítmény összetett előkészítési rutinjának futtatása az install-blob használatakor" + +#. 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" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Ütemezés…" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Kiválasztott eszköz" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Sorozatszám" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "A hibakeresési jelző beállítása frissítéskor" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Ügyfél és démon verziók megjelenítése" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "A démon részletes információinak megjelenítése egy adott tartományhoz" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Hibakeresési információk megjelenítése az összes tartományhoz" + +#. 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" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Firmware frissítési előzmények megtekintése" + +#. TRANSLATORS: this is for plugin development +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" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Méret" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Forrá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: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Az összes eszköz sikeresen aktiválva" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Távoli tároló sikeresen letiltva" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Új metaadatok sikeresen letöltve:" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Távoli tároló sikeresen engedélyezve" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware sikeresen telepítve" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Konfigurációs érték sikeresen módosítva" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Távoli tároló sikeresen módosítva" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metaadatok kézi frissítése sikeres" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Eszköz ellenőrzőösszegek sikeresen frissítve" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "%u jelentés sikeresen feltöltve" +msgstr[1] "%u jelentés sikeresen feltöltve" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Eszköz ellenőrzőösszegek sikeresen ellenőrizve" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Összegzés" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Távoli kiszolgálón nem támogatott " + +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 "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 type, e.g. remote or local +msgid "Type" +msgstr "Típus" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI firmware segédprogram" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Ismeretlen" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Ismeretlen eszköz" + +msgid "Unlock the device to allow access" +msgstr "Eszköz feloldása hozzáférés engedélyezéséhez" + +#. TRANSLATORS: command description +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: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Nem támogatott démonverzió: %s, a kliensverzió %s" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Frissíthet" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Frissítési hiba" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Frissítési üzenet" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Frissítés állapota" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "A feltöltési hiba ismert probléma, további információkért látogassa meg ezt az URL-t:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Frissíti most?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "A frissítés újraindítást igényel" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "A tárolt kriptográfiai hash frissítése a jelenlegi ROM tartalmával" + +msgid "Update the stored device verification information" +msgstr "A tárolt eszközellenőrzési információk frissítése" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "A tárolt metaadatok frissítése a jelenlegi tartalommal" + +#. 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 "%s frissítése: %s -> %s…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s frissítése…" + +#. TRANSLATORS: ask the user to upload +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: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Kerülőmegoldás jelzők használata a firmware telepítésekor" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "A felhasználó értesítve" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Felhasználónév" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Változat" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Gyártó" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Ellenőrzés…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Verzió" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Várakozás…" + +#. 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" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Firmware írása fájlból egy partícióra" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Írás…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "A disztribúció szállítója nem biztos, hogy ellenőrízte a firmware frissítés kompatibilitását a rendszerével és a kapcsolódó eszközeivel." diff --git a/fwupd-1.8.6/po/id.po b/fwupd-1.8.6/po/id.po new file mode 100644 index 0000000000000000000000000000000000000000..312ee53c06a528e8b1fd144eaa01a898841674d5 --- /dev/null +++ b/fwupd-1.8.6/po/id.po @@ -0,0 +1,2314 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Andika Triwidada , 2017-2019 +# Andika Triwidada , 2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: id\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f menit tersisa" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Pembaruan Baterai %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Pembaruan Microcode CPU %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Pembaruan Kamera %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Pembaruan Konfigurasi %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Pembaruan ME Konsumen %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 "Pembaruan Pengontrol %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 "Pembaruan ME Korporat %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 "Pembaruan Perangkat %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 "Pembaruan Pengontrol Tertanam %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Pembaruan Papan Ketik %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 "Pembaruan ME %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Pembaruan Tetikus %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Pembaruan Antar Muka Jaringan %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Pembaruan Pengontrol Penyimpanan %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 "Pembaruan Sistem %s" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Pembaruan TPM %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Pembaruan Pengontrol Thunderbolt %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Pembaruan Touchpad %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 "Pembaruan %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s dan semua perangkat yang terhubung mungkin tidak dapat digunakan saat memperbarui." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s muncul: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s berubah: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s menghilang: %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s mode manufaktur" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s harus tetap terhubung selama pembaruan untuk menghindari kerusakan." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s harus tetap terhubung ke sumber tenaga selama pembaruan untuk menghindari kerusakan." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s menimpa" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "versi %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u hari" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u perangkat memiliki pembaruan firmware." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u jam" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u perangkat lokal didukung" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u menit" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u detik" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(usang)" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Tindakan Diperlukan:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Mengaktifkan perangkat" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktifkan perangkat yang tertunda" + +msgid "Activate the new firmware on the device" +msgstr "Aktifkan firmware baru pada perangkat" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Mengaktifkan pemutakhiran firmware" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Mengaktifkan pembaruan firmware untuk" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Usia" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Setuju dan aktifkan remote?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias ke %s" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Semua perangkat dengan tipe yang sama akan diperbarui pada saat yang sama" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Izinkan penuruntingkatan versi firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Izinkan menginstal ulang versi firmware yang ada" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Perbolehkan beralih cabang firmware" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Suatu pembaruan memerlukan boot ulang agar lengkap." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Pembaruan membutuhkan sistem untuk dimatikan untuk menyelesaikan." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Menjawab ya untuk semua pertanyaan" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Terapkan pembaruan firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Terapkan pembaruan bahkan ketika tidak disarankan" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Menerapkan berkas pembaruan" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Menerapkan pembaruan…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Firmware yang disetujui:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Tanya lagi lain kali?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Cantolkan ke mode firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Mengautentikasi…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Detail otentikasi diperlukan" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Autentikasi diperlukan untuk menuruntingkatkan firmware pada perangkat lepas pasang" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Autentikasi diperlukan untuk menuruntingkatkan firmware pada mesin ini" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Autentikasi diperlukan untuk mengubah sebuah remote yang ditata yang dipakai untuk pembaruan firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Autentikasi diperlukan untuk mengubah konfigurasi daemon" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Autentikasi diperlukan untuk mengatur daftar firmware yang disetujui" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Autentikasi diperlukan untuk menandatangani data menggunakan sertifikat klien" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Autentikasi diperlukan untuk beralih ke versi firmware baru" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Autentikasi diperlukan untuk membuka kunci suatu perangkat" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Autentikasi diperlukan untuk memutakhirkan firmware pada perangkat lepas pasang" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Autentikasi diperlukan untuk memutakhirkan firmware pada mesin ini" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Autentikasi diperlukan untuk memutakhirkan checksum tersimpan bagi perangkat" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Pelaporan Otomatis" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Unggah secara otomatis setiap kali?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML NAMABERKAS-DST" + +msgid "BYTES" +msgstr "BYTE" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Mengikat driver kernel baru" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Berkas firmware yang diblokir:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Memblokir firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Memblokir firmware tertentu agar tidak diinstal" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versi Bootloader" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Cabang" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Build berkas firmware" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Batal" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Dibatalkan" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Tidak dapat diterapkan karena pembaruan dbx telah diterapkan." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Tidak dapat menerapkan pembaruan di media live" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Diubah" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Memeriksa apakah hash kriptografis cocok dengan firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Checksum" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Pilih cabang:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Pilih suatu peranti:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Pilih tipe firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Pilih sebuah rilis:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Pilih volume:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Membersihkan hasil dari pemutakhiran terakhir" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Perintah tidak ditemukan" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Mengonversi berkas firmware" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Dibuat" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritis" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Verifikasi hash kriptografis tersedia" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versi saat ini" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ID-PERANTI|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilitas DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opsi Pengawakutuan" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Mendekompresi…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Deskripsi" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Lepaskan ke mode bootloader" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Rincian" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Flag Perangkat" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ID Perangkat" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Perangkat ditambahkan:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Perangkat dapat memulihkan kegagalan flash" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Perangkat diubah:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Firmware perangkat harus memiliki pemeriksaan versi" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Perangkat terkunci" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Perangkat perlu menginstal semua rilis yang disediakan" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Perangkat tidak terjangkau" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Perangkat dapat digunakan selama durasi pembaruan" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Perangkat dilepas:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Pembaruan tahap perangkat" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Perangkat mendukung beralih ke cabang firmware yang berbeda" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Metode pembaruan perangkat" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Pembaruan perangkat membutuhkan aktivasi" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Perangkat akan mencadangkan firmware sebelum menginstal" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Perangkat tidak akan muncul kembali setelah pembaruan selesai" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Peranti yang sukses diperbarui:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Peranti yang tidak diperbarui dengan benar:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Perangkat tanpa pembaruan firmware yang tersedia: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Perangkat dengan versi firmware terbaru yang tersedia:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Tidak menemukan perangkat dengan GUID yang cocok" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Dinonaktifkan" + +msgid "Disabled fwupdate debugging" +msgstr "Dinonaktifkan fwupdate debugging" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Menonaktifkan remote yang diberikan" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Tampilkan versi" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Jangan periksa untuk metadata lama" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Jangan periksa untuk riwayat yang tak dilaporkan" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Jangan periksa apakah remote unduhan harus diaktifkan" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Jangan memeriksa atau meminta reboot setelah pembaruan" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Jangan sertakan awalan domain log" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Jangan sertakan awalan stempel waktu" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Jangan melakukan pemeriksaan keamanan perangkat" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Jangan menulis ke basis data riwayat" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Apakah Anda memahami konsekuensi dari mengubah cabang firmware?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Apakah Anda ingin menonaktifkan fitur ini untuk pembaruan di masa mendatang?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Apakah Anda ingin menyegarkan remote ini sekarang?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Apakah Anda ingin mengunggah laporan secara otomatis untuk pembaruan di masa mendatang?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Selesai!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Menuruntingkatkan %s dari %s ke %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Menuruntingkatkan firmware pada suatu peranti" + +#. 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 "Menuruntingkatkan %s dari %s ke %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Menuruntingkatkan %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Unduh suatu berkas" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Mengunduh…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Curahkan data SMBIOS dari suatu berkas" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durasi" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ESP yang ditentukan tidak valid" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Aktifkan dukungan pembaruan firmware pada sistem yang didukung" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Aktifkan remote baru?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Aktifkan remote ini?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Difungsikan" + +msgid "Enabled fwupdate debugging" +msgstr "Diaktifkan debugging fwupdate" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Mengaktifkan remote yang diberikan" + +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 "Mengaktifkan fungsi ini dilakukan dengan risiko Anda sendiri, yang berarti Anda harus menghubungi produsen peralatan asli Anda mengenai masalah yang disebabkan oleh pembaruan ini. Hanya masalah dengan proses pembaruan itu sendiri yang harus diajukan pada $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Mengaktifkan remote ini dilakukan dengan risiko Anda sendiri." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Terenkripsi" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM terenkripsi" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Menghapus semua riwayat pembaruan firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Menghapus…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Keluar setelah tundaan sejenak" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Keluar setelah mesin telah dimuat" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Mengekspor struktur berkas firmware ke XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Mengekstrak blob firmware ke image" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "BERKAS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "BERKAS [ID-PERANTI|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NAMABERKAS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "NAMABERKAS SERTIFIKAT KUNCI-PRIVAT" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NAMABERKAS NAMA-ALT-PERANGKAT|ID-ALT-PERANGKAT" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NAMABERKAS NAMA-ALT-PERANGKAT|ID-ALT-PERANGKAT [NAMA-ALT-IMAGE|ID-ALT-IMAGE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NAMABERKAS ID-PERANTI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NAMABERKAS [ID-PERANTI|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NAMABERKAS [TIPE-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NAMABERKAS-SRC NAMABERKAS-DST [TIPE-FIRMWARE-SRC] [TIPE-FIRMWARE-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "NAMABERKAS|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Gagal" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Gagal menerapkan pembaruan" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Gagal menyambung ke daemon" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Gagal mendapatkan perangkat yang tertunda" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Gagal menginstal pembaruan firmware" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Gagal memuat dbx lokal" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Gagal memuat quirk" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Gagal memuat dbx sistem" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Gagal mengunci" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Gagal mengurai argumen" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Gagal mengurai berkas" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Gagal mengurai flag untuk --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Gagal mengurai dbx lokal" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Gagal reboot" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Gagal mengatur mode splash" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Gagal memvalidasi konten ESP" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nama Berkas" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Tanda Tangan Nama Berkas" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Sumber Nama Berkas" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nama berkas diperlukan" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Memfilter dengan suatu set flag perangkat menggunakan awalan ~ untuk mengecualikan, mis. 'internal, ~needs-reboot'" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI Basis Firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Layanan D-Bus Pemutakhiran Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Daemon Pemutakhiran Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilitas Firmware" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Pengesahan firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Firmware sudah diblokir" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Firmware belum diblokir" + +#. 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 firmware belum diperbarui selama %uhari dan mungkin tidak mutakhir." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Penyegaran terakhir metadata firmware: %s yang lalu. Gunakan --force untuk menyegarkan lagi." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Pemutakhiran firmware" + +msgid "Firmware updates are not supported on this machine." +msgstr "Pembaruan firmware tidak didukung pada mesin ini." + +msgid "Firmware updates are supported on this machine." +msgstr "Pembaruan firmware didukung pada mesin ini." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Pembaruan firmware dinonaktifkan; jalankan 'fwupdmgr unlock' untuk mengaktifkan" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Flag" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Paksa tindakan dengan merelaksasi beberapa pemeriksaan runtime" + +msgid "Force the action ignoring all warnings" +msgstr "Paksa tindakan mengabaikan semua peringatan" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Ditemukan" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Enkripsi Disk Lengkap Terdeteksi" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Rahasia enkripsi disk lengkap mungkin jadi tidak valid saat memperbarui" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Dapatkan semua flag perangkat yang didukung oleh fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Dapatkan semua perangkat yang mendukung pemutakhiran firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Dapatkan semua plugin yang diaktifkan yang terdaftar dengan sistem" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Dapatkan rincian tentang suatu berkas firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Dapatkan remote-remote yang terkonfigurasi" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Mendapatkan atribut keamanan host" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Dapatkan daftar firmware yang disetujui" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Mendapatkan daftar firmware yang diblokir" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Dapatkan daftar pemutakhiran bagi perangkat keras yang tersambung" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Mendapatkan rilis-rilis bagi sebuah peranti" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Mendapatkan hasil dari pemutakhiran terakhir" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "BERKAS-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Perangkat keras sedang menunggu untuk ditancapkan kembali" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Tinggi" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Kejadian Keamanan Host" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID Keamanan Host:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Proteksi perangkat IOMMU dinonaktifkan" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Proteksi perangkat IOMMU diaktifkan" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Menganggur…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Abaikan pemeriksaan ketat SSL saat mengunduh berkas" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Abaikan kegagalan checksum firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Abaikan kegagalan ketidakcocokan perangkat keras firmware" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Mengbaikan pemeriksaan keamanan validasi" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Mengabaikan pemeriksaan ketat SSL, untuk melakukan ini secara otomatis di masa depan, eksporlah DISABLE_SSL_STRICT di lingkungan Anda" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Durasi Instalasi" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instal blob firmware pada perangkat" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Pasang suatu berkas firmware pada perangkat keras ini" + +msgid "Install old version of signed system firmware" +msgstr "Menginstal versi lama firmware sistem yang ditandatangani" + +msgid "Install old version of unsigned system firmware" +msgstr "Menginstal versi lama firmware sistem yang tidak ditandatangani" + +msgid "Install signed device firmware" +msgstr "Pasang firmware perangkat yang ditandatangani" + +msgid "Install signed system firmware" +msgstr "Pasang firmware sistem yang ditandatangani" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instal ke perangkat induk terlebih dahulu" + +msgid "Install unsigned device firmware" +msgstr "Pasang firmware perangkat yang tak ditandatangani" + +msgid "Install unsigned system firmware" +msgstr "Pasang firmware sistem yang tak ditandatangani" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Menginstal Firmware…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Sedang memasang pembaruan firmware…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Memasang pada %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard terproteksi ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Kebijakan kesalahan Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Boot terverifikasi Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET Aktif" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET Diaktifkan" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Perangkat internal" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Tak valid" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Sedang dalam mode bootloader" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Masalah" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "KUNCI,NILAI" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Penguncian kernel dinonaktifkan" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Penguncian kernel diaktifkan" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Keyring" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOKASI" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Modifikasi terakhir" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Kurang dari satu menit tersisa" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Lisensi" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Layanan Firmware Vendor Linux (firmware stabil)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Layanan Firmware Vendor Linux (firmware uji)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Penguncian kernel Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Membuat daftar entri di dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Daftar pembaruan firmware yang didukung" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Daftar tipe firmware yang tersedia" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Daftar berkas di ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Memuat…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Terkunci" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Rendah" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Mode manufaktur MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI menimpa" + +msgid "MEI version" +msgstr "Versi MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Aktifkan plugin tertentu secara manual" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Sedang" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Tanda Tangan Metadata" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI Metadata" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata dapat diperoleh dari Layanan Firmware Vendor Linux." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versi Minimum" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Daemon dan klien tidak cocok, gunakan %s sebagai gantinya" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Memodifikasi nilai konfigurasi daemon" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Mengubah suatu remote yang diberikan" + +msgid "Modify a configured remote" +msgstr "Ubah suatu remote yang ditata" + +msgid "Modify daemon configuration" +msgstr "Ubah konfigurasi daemon" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Pantau daemon untuk kejadian" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Kait ESP" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Membutuhkan reboot setelah instalasi" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Perlu reboot" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Perlu dimatikan setelah instalasi" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Versi baru" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Tidak ada tindakan yang ditentukan!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Tidak ada penuruntingkatan untuk %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Tidak ada ID firmware yang ditemukan" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Tidak terdeteksi perangkat keras dengan kapabilitas pemutakhiran firmware" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Tidak ada plugin yang ditemukan" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Tidak ada rilis yang tersedia" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Saat ini tidak ada remote yang diaktifkan sehingga metadata tidak tersedia." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Tidak ada remote yang tersedia" + +msgid "No updates available for remaining devices" +msgstr "Tidak ada pembaruan yang tersedia untuk perangkat yang tersisa" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Tidak ada pembaruan yang diterapkan" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Tidak ditemukan" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Tak didukung" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Hanya tampilkan nilai PCR tunggal" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Hanya menggunakan IPFS saat mengunduh berkas" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Hanya peningkatan versi yang diizinkan" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Keluaran dalam format JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Timpa path ESP default" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PATH" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Uraikan dan tampilkan detail tentang berkas firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Mengurai pembaruan dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Mengurai dbx sistem…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Kata Sandi" + +msgid "Payload" +msgstr "Payload" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Tertunda" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Persentase selesai" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Lakukan operasi?" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Pastikan Anda memiliki kunci pemulihan volume sebelum melanjutkan." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Silakan masukkan angka dari 0 hingga %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dependensi plugin kurang" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Perlindungan DMA pra-boot" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Versi sebelumnya" + +msgid "Print the version number" +msgstr "Cetak nomor versi" + +msgid "Print verbose debug statements" +msgstr "Cetak pernyataan awakutu rinci" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Prioritas" + +msgid "Proceed with upload?" +msgstr "Lanjutkan mengunggah?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Proprietari" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Menanyakan dukungan pembaruan firmware" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ID-REMOTE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ID-REMOTE KUNCI NILAI" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Baca blob firmware dari perangkat" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Baca firmware dari perangkat ke dalam suatu berkas" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Baca firmware dari satu partisi ke dalam suatu berkas" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Membaca dari %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Membaca…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Reboot…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Segarkan metadata dari server remote" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Pasang ulang %s ke %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Menginstal ulang firmware saat ini di perangkat" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Menginstal ulang firmware pada perangkat" + +#. 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 "Memasang ulang %s dengan %s... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Cabang Rilis" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID Remote" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Gantikan data dalam suatu berkas firmware yang telah ada" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI Lapor" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Dilaporkan ke server remote" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Sistem berkas efivarfs yang diperlukan tidak ditemukan" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Perangkat keras yang diperlukan tidak ditemukan" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Membutuhkan bootloader" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Memerlukan koneksi internet" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Mulai ulang sekarang?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Mulai ulang daemon untuk membuat perubahan itu efektif?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Memulai ulang perangkat…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Memberikan kembalian semua ID perangkat keras bagi mesin" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Jalankan `fwupdmgr get-upgrade` untuk informasi lebih lanjut." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Menjalankan rutin pembersihan komposit plugin saat menggunakan install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Menjalankan rutin persiapan komposit saat menggunakan install-blob" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Kernel yang sedang berjalan terlalu tua" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Akhiran Runtime" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Deskriptor BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Wilayah BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI kunci" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI tulis" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSISTEM DRIVER [ID-PERANTI|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Menyimpan berkas yang memungkinkan pembuatan ID perangkat keras" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Menjadwalkan instalasi untuk boot ulang selanjutnya bila mungkin" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Menjadwalkan…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot diaktifkan" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Lihat %s untuk informasi lebih lanjut." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Perangkat yang dipilih" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume yang dipilih" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Nomor Seri" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Atur flag debugging selama pembaruan" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Setel daftar firmware yang disetujui" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Membagikan riwayat firmware dengan para pengembang" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Tampilkan semua hasil" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Tampilkan versi daemon dan klien" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Tampilkan informasi daemon rinci untuk domain tertentu" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Tampilkan informasi debug untuk semua domain" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Tampilkan opsi debugging" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Tampilkan peranti yang tidak dapat dimutakhirkan" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Tampilkan informasi pengawakutuan ekstra" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Tampilkan riwayat pembaruan firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Tampilkan informasi rinci pengaya" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Memperlihatkan versi terhitung dari dbx" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Tampilkan log debug dari pembaruan yang terakhir kali dicoba" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Tampilkan informasi status pembaruan firmware" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Matikan sekarang?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Menandatangani firmware dengan kunci baru" + +msgid "Sign data using the client certificate" +msgstr "Tanda tangani data menggunakan sertifikat klien" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Tanda tangani data menggunakan sertifikat klien" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Menanda tangani data yang diunggah dengan sertifikat klien" + +msgid "Signature" +msgstr "Tanda Tangan" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Ukuran" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Beberapa rahasia platform mungkin menjadi tidak valid saat memperbarui firmware ini." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Sumber" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Tentukan ID Produk/Vendor perangkat DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Menentukan berkas basis data dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Tentukan jumlah byte per transfer USB" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Sukses" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Berhasil mengaktifkan semua perangkat" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remote yang berhasil dinonaktifkan" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Metadata baru berhasil diunduh: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Berhasil mengaktifkan dan menyegarkan remote" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remote yang berhasil diaktifkan" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware berhasil diinstal" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Nilai konfigurasi yang berhasil dimodifikasi" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remote yang berhasil dimodifikasi" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadata berhasil disegarkan secara manual" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Checksum perangkat berhasil diperbarui" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Berhasil mengunggah %u laporan" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Checksum perangkat berhasil diverifikasi" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Ringkasan" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Didukung" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Didukung di server remote" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-ram" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Beralih cabang dari %s ke %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Beralih cabang firmware pada perangkat" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Sistem membutuhkan sumber daya eksternal" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEKS" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Rekonstruksi TPM PCR0" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Ternoda" + +msgid "Target" +msgstr "Target" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Menguji perangkat menggunakan manifest JSON" + +#. 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 adalah layanan gratis yang beroperasi sebagai badan hukum independen dan tidak memiliki koneksi dengan $OS_RELEASE:NAME$. Distributor Anda mungkin belum memverifikasi pembaruan firmware untuk kompatibilitas dengan sistem Anda atau perangkat yang terhubung. Semua firmware hanya disediakan oleh pabrikan peralatan asli." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 berbeda dengan rekonstruksi." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Daemon telah memuat kode pihak ke-3 dan tidak lagi didukung oleh pengembang hulu!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Versi perangkat tidak cocok: mendapat %s, diharapkan %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Firmware dari %s tidak disediakan oleh %s, vendor perangkat keras." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Jam sistem belum diatur dengan benar dan mengunduh berkas mungkin gagal." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Tidak ada berkas firmware yang diblokir" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Tidak ada firmware yang disetujui." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Paket ini belum divalidasi, mungkin tidak berfungsi dengan baik." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Program ini hanya dapat berfungsi dengan benar sebagai 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 "Remote ini berisi firmware yang tidak diembargo, tetapi masih sedang diuji oleh vendor perangkat keras. Anda harus memastikan Anda memiliki cara untuk menurunkan versi firmware secara manual jika pembaruan firmware gagal." + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Sistem ini memiliki masalah runtime HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Sistem ini memiliki tingkat keamanan HSI yang rendah." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Alat ini memungkinkan administrator untuk menerapkan pembaruan UEFI dbx." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Alat ini memungkinkan administrator untuk men-debug operasi UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Alat ini memungkinkan administrator untuk kuiri dan mengontrol daemon fwupd, yang memungkinkan mereka untuk melakukan tindakan seperti menginstal atau menurun-tingkatkan firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Alat ini memungkinkan administrator untuk menggunakan plugin fwupd tanpa diinstal pada sistem host." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Alat ini hanya bisa dipakai oleh pengguna root" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Alat ini akan membaca dan mengurai log kejadian TPM dari firmware sistem." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Kegagalan transien" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipe" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partisi UEFI ESP tidak terdeteksi atau dikonfigurasi" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitas Firmware UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Pembaruan kapsul UEFI tidak tersedia atau diaktifkan dalam penyiapan firmware" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitas dbx UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Firmware UEFI tidak dapat diperbarui dalam mode BIOS warisan" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Kunci platform UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Boot aman UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Tidak dapat terhubung ke layanan" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Lepas ikatan driver saat ini" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Membuka blokir firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Membuka blokir firmware tertentu agar tidak diinstal" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Tidak terenkripsi" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Tidak diketahui" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Perangkat Tidak Dikenal" + +msgid "Unlock the device to allow access" +msgstr "Buka kunci perangkat untuk mengizinkan akses" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Tak terkunci" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Membuka kunci perangkat bagi akses firmware" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Lepas kait ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Batalkan flag debugging selama pembaruan" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Versi daemon tidak didukung %s, versi klien adalah %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Tidak ternoda" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Dapat diperbarui" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Kesalahan Pembaruan" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Pesan Pembaruan" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Keadaan Pembaruan" + +#. 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:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Perbarui sekarang?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Pembaruan membutuhkan reboot" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Memperbarui hash kriptografi yang disimpan dengan konten ROM saat ini" + +msgid "Update the stored device verification information" +msgstr "Mutakhirkan informasi verifikasi perangkat yang tersimpan" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Perbarui metadata yang disimpan dengan konten saat ini" + +msgid "Updating" +msgstr "Memutakhirkan" + +#. 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 "Memutakhirkan %s dari %s ke %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Memutakhirkan %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Meningkatkan %s dari %s ke %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Unggah laporan sekarang?" + +#. 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 "Mengunggah laporan firmware membantu vendor perangkat keras untuk dengan cepat mengidentifikasi pembaruan yang gagal dan berhasil pada perangkat nyata." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgensi" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Gunakan fwupdmgr --help untuk bantuan" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Gunakan fwupdtool --help untuk bantuan" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Gunakan flag quirk ketika menginstal firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Pengguna telah diberitahu" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nama Pengguna" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valid" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Memvalidasi konten ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Varian" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Vendor" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verifikasi…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versi" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "PERINGATAN:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Menunggu…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Awasi perubahan perangkat keras" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Tulis firmware dari berkas ke dalam perangkat" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Tulis firmware dari berkas ke dalam suatu partisi" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Menulis berkas:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Menulis…" + +#. 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 "Distributor Anda mungkin belum memverifikasi pembaruan firmware untuk kompatibilitas dengan sistem Anda atau perangkat yang terhubung." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Perangkat keras Anda mungkin rusak menggunakan firmware ini, dan menginstal rilis ini dapat membatalkan garansi apa pun dengan %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ID-PERANTI|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID-PERANTI|GUID] [CABANG]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[BERKAS TTD_BERKAS ID-REMOTE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NAMABERKAS1] [NAMABERKAS2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[BERKAS-SMBIOS|BERKAS-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "baku" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Utilitas log kejadian TPM fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "plugin fwupd" diff --git a/fwupd-1.8.6/po/it.po b/fwupd-1.8.6/po/it.po new file mode 100644 index 0000000000000000000000000000000000000000..ca0ec48f697d4621b7444d29305df3aa2a27df3f --- /dev/null +++ b/fwupd-1.8.6/po/it.po @@ -0,0 +1,2760 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Gianvito Cavasoli , 2016 +# Milo Casagrande , 2017-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" + +#. TRANSLATORS: 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" +msgstr[2] "Mancano %.0f minuti" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Aggiornamento BMC %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Aggiornamento batteria %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Aggiornamento microcode CPU %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Aggiornamento fotocamera %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Aggiornamento configurazione %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "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: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Aggiornamento schermo %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "Aggiornamento unità %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: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "Aggiornamento unità dati %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Aggiornamento tastiera %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: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Aggiornamento mouse %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Aggiornamento interfaccia di rete %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "Aggiornamento SSD %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Aggiornamento controller di archiviazione %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: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Aggiornamento TPM %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Aggiornamento unità di controllo Thunderbolt %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Aggiornamento touchpad %s" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "Aggiornamento ricevitore USB %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Aggiornamento di %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s e tutti i dispositivi collegati potrebbero non essere utilizzabili durante l'aggiornamento." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s apparso: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s è cambiato: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s scomparso: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s non può essere aggiornato" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modalità costruttore %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s deve rimanere collegato durante l'aggiornamento per evitare possibili danni." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s deve rimanere collegato alla rete elettrica durante l'aggiornamento per evitare possibili danni." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Override %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Versione %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u giorno" +msgstr[1] "%u giorni" +msgstr[2] "%u giorni" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u dispositivo ha un aggiornamento firmware disponibile." +msgstr[1] "%u dispositivi hanno un aggiornamento firmware disponibile." +msgstr[2] "%u dispositivi hanno un aggiornamento firmware disponibile." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u dispositivo non dispone della configurazione migliore." +msgstr[1] "%u dispositivi non dispongono della configurazione migliore." +msgstr[2] "%u dispositivi non dispongono della configurazione migliore." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u ora" +msgstr[1] "%u ore" +msgstr[2] "%u ore" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u dispositivo locale supportato" +msgstr[1] "%u dispositivi locali supportati" +msgstr[2] "%u dispositivi locali supportati" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuto" +msgstr[1] "%u minuti" +msgstr[2] "%u minuti" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u secondo" +msgstr[1] "%u secondi" +msgstr[2] "%u secondi" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (soglia %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleto)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "Un PCR TPM è ora un valore non valido" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Azione richiesta:" + +#. 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: the age of the metadata +msgid "Age" +msgstr "Età" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Accettare e abilitare il remoto?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias di %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Tutti i PCR TPM non sono validi" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Tutti i PCR TPM sono validi" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Tutti i dispositivi dello stesso tipo saranno aggiornati allo stesso tempo" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Consente di tornare alle precedenti versioni del firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Consente la re-installazione di versioni esistenti del firmware" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Consente il cambio del ramo firmware" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Ramo alternativo" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Applica aggiornamenti firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Applica aggiornamento anche se non consigliato" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Applica file di aggiornamento" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Applicazione aggiornamento…" + +#. 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:" +msgstr[2] "Firmware approvati:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Chiedere ancora la prossima volta?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Collega in modalità firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autenticazione…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Sono richiesti i dettagli di autenticazione" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "È richiesto autenticarsi per tornare al precedente firmware su un dispositivo rimovibile" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "È richiesto autenticarsi tornare al precedente firmware su questa macchina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "È richiesto autenticarsi per modificare le impostazioni BIOS" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +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 read BIOS settings" +msgstr "È richiesto autenticarsi per leggere le impostazioni BIOS" + +#. 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" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "È richiesto autenticarsi per aggiornare il firmware su un dispositivo rimovibile" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "È richiesto autenticarsi per aggiornare il firmware su questa macchina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "È richiesto autenticarsi per aggiornare il codice di controllo del dispositivo salvato" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Rapporti automatici" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Caricare automaticamente ogni volta?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "Aggiornamenti BIOS forniti tramite LVFS o Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML NOMEFILE-DST" + +msgid "BYTES" +msgstr "BYTE" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Batteria" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincola in nuovo driver kernel" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "File di firmware bloccati:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Versione bloccata" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Firmware bloccato:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blocca un firmware specifico così da non installarlo" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versione bootloader" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Ramo" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Compila una file firmware" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Annulla" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Annullato" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Impossibile applicare poiché l'aggiornamento dbx è già stato applicato." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Impossibile applicare gli aggiornamenti su supporti live" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Modificato" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Verifica che l'hash crittografico corrisponda col firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Codice di controllo" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Scegliere un ramo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Scegliere un dispositivo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Scegliere un tipo di firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Scegliere un rilascio:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Scegliere un volume:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Pulisce i risultati dell'ultimo aggiornamento" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Comando non trovato" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Supportata dalla comunità" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Converte un file firmware" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Creato" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Critica" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "È disponibile la verifica dell'hash crittografico" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Valore attuale" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versione attuale" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ID-DISPOSITIVO|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Strumento DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opzioni di debug" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Estrazione…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descrizione" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Scollega in modalità bootloader" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Dettagli" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Allontanarsi dalla migliore configurazione nota?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Flag dispositivo" + +#. 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:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "La carica della batteria del dispositivo è troppo bassa (%u%%, richiesto %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Il dispositivo è in grado di correggere problemi di flash" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Il dispositivo non può essere utilizzato mentre il coperchio è chiuso" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositivo modificato:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "È richiesto il firmware dispositivo per eseguire un controllo della versione" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Il dispositivo è emulato" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Il dispositivo è bloccato" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "È richiesto un dispositivo per installare tutti i rilasci forniti" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Il dispositivo non è raggiungibile" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Il dispositivo è utilizzabile per la durata dell'aggiornamento" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Il dispositivo è in attesa del completamento dell'aggiornamento" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositivo rimosso:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Il dispositivo richiede di essere collegato all'alimentazione elettrica" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Il dispositivo applica gli aggiornamenti a fasi" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Il dispositivo supporta il passaggio a un ramo diverso del firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Metodo di aggiornamento del dispositivo" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "L'aggiornamento del dispositivo richiede l'attivazione" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Il dispositivo eseguirà un backup del firmware prima dell'installazione" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Il dispositivo non comparirà nuovamente dopo l'aggiornamento" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositivi aggiornati con successo:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Dispositivi non aggiornati correttamente:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositivi senza aggiornamenti firmware:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositivi con l'ultima versione disponibile del firmware:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Nessun dispositivo trovato con GUID corrispondenti" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Disabilitato" + +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Non controlla la cronologia non segnalata " + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Non controlla se lo scaricamento dai remoti deve essere abilitato" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Non controlla o chiede se è necessario riavviare dopo un aggiornamento" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Non include il prefisso del dominio" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Non include il prefisso della marcatura temporale" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Non esegue i controlli di sicurezza del dispositivo" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Non chiede i dispositivi" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Non scrive la cronologia" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Le conseguenze del cambio di ramo del firmware sono state comprese?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Disabilitare questa funzionalità per i prossimi aggiornamenti?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Aggiornare questo remoto ora?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Caricare automaticamente i resoconti per i prossimi aggiornamenti?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Fatto." + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Arretrare %s da %s a %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Torna a una vecchia versione del firmware su un 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 "Arretramento di %s da %s a %s..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Arretramento di %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Scarica un file" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Scaricamento…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Scarica i dati SMBIOS da un file" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durata" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "ESP specificata non era valida" + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Host emulato" + +msgid "Enable" +msgstr "Abilita" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Abilita il supporto all'aggiornamento firmware sui sistemi compatibili" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Abilitare il nuovo remoto?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Abilitare questo remoto?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Abilitato" + +msgid "Enabled fwupdate debugging" +msgstr "Debug fwupdate abilitato" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Abilitato se corrisponde all'hardware" + +#. 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." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Cifrato" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM cifrata" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Fine vita" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Elimina tutta la cronologia degli aggiornamenti firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Eliminazione…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Esce dopo una breve attesa" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Esce dopo che il motore è stato caricato" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Esporta la struttura di un file firmware in XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Estrae un blob firmware in immagini" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FILE [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NOMEFILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "NOMEFILE CERTIFICATO CHIAVE-PRIVATA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NOMEFILE NOME-ALT-DISPOSITIVO|ID-ALT-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NOMEFILE NOME-ALT-DISPOSITIVO|ID-ALT-DISPOSITIVO [NOME-ALT-IMMAGINE|ID-ALT-IMMAGINE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NOMEFILE ID-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "FILENAME OFFSET DATA [TIPO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NOMEFILE [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NOMEFILE [TIPO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NOMEFILE-SRC NOMEFILE-DST [TIPO-FIRMWARE-SRC] [TIPO-FIRMWARE-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "NOMEFILE|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Non riuscito" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Applicazione aggiornamento non riuscita" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Connessione al demone non riuscita" + +#. 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Caricamento dbx locale non riuscito" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Caricamento stranezze non riuscito" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Caricamento dbx di sistema non riuscito" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Blocco non riuscito" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Analisi degli argomenti non riuscita" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Lettura del file non riuscita" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Analisi del flag --filter non riuscita" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Lettura dbx locale 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Verifica contenuti ESP non riuscita" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Falso" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nome file" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Firma nome file" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Sorgente nome file" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nome file richiesto" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra tramite un insieme di flag utilizzando ~ per escludere, per esempio «internal, ~needs-reboot»" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI di base del firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Servizio D-Bus di aggiornamento firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Demone di aggiornamento firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Strumento gestione firmware" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Convalida firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Il firmware è già bloccato" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Il firmware non è bloccato" + +#. 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] "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." +msgstr[2] "I metadati del firmware non sono stati controllati per %u giorni e potrebbero non essere aggiornati." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Ultimo aggiornamento metadati firmware: %s fa. Usare --force per aggiornare nuovamente." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Aggiornamenti firmware" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Aggiornamenti firmware disabilitati; eseguire «fwupdmgr unlock» per abilitarli" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Flag" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Forza l'azione riducendo alcuni controlli runtime" + +msgid "Force the action ignoring all warnings" +msgstr "Forza l'azione ignorando gli avvisi" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Trovato" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Rilevata cifratura del disco" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "I segreti di cifratura del disco potrebbero essere invalidati durante l'aggiornamento" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" +msgstr[2] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Recupero impostazioni BIOS" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Ottiene tutti i flag dispositivo supportati da fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Ottiene tutti i dispositivi che supportano gli aggiornamenti del firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Recupera tutti i plugin registrati nel sistema" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Ottiene le informazioni su un file di firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Ottiene i remoti configurati" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Recupera gli attributi di sicurezza dell'host" + +#. 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 blocked firmware" +msgstr "Recupera l'elenco del firmware bloccato" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Ottiene l'elenco degli aggiornamenti per l'hardware connesso" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Ottiene i rilasci di un dispositivo" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Ottiene i risultati dell'ultimo aggiornamento" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "FILE-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "L'hardware è in attesa di essere ricollegato" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Elevata" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Eventi sicurezza host" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID sicurezza host:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Protezione dispositivo IOMMU disabilitata" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Protezione dispositivo IOMMU abilitata" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Inattivo…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora controlli SSL nello scaricare i file" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignora i checksum del firmware non riusciti" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignora corrispondenze errate firmware hardware" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignora i controlli di sicurezza di validazione" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Controlli SSL ignorati, per fare ciò automaticamente in futuro, esportare DISABLE_SSL_STRICT nel proprio ambiente" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Tempo di installazione" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Installa blob firmware su un dispositivo" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Installa un file di firmware su questo hardware" + +msgid "Install old version of signed system firmware" +msgstr "Installa versione precedente del firmware firmato di sistema" + +msgid "Install old version of unsigned system firmware" +msgstr "Installa versione precedente del firmware non firmato di sistema" + +msgid "Install signed device firmware" +msgstr "Installa firmware firmato del dispositivo" + +msgid "Install signed system firmware" +msgstr "Installa firmware firmato di sistema" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Installare sul dispositivo genitore prima" + +msgid "Install unsigned device firmware" +msgstr "Installa firmware non firmato del dispositivo" + +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…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "L'installazione dell'aggiornamento potrebbe annullare la garanzia del dispositivo." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Intero" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protetto da Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fusibile OTP Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Regole di errore Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Avvio verificato da Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET attivo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET abilitato" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Dispositivo interno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Non valido" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Parametri non validi" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "È retrocessione" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "È in modalità bootloader" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "È aggiornamento" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problema" +msgstr[1] "Problemi" +msgstr[2] "Problemi" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "CHIAVE,VALORE" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Il kernel è di nuovo integro" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Il kernel non è integro" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Lockdown kernel disabilitato" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Lockdown kernel abilitato" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Portachiavi" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "POSIZIONE" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Ultima modifica" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Manca meno di un minuto" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licenza" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (firmware stabile)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (firmware in prova)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Lockdown kernel Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Elenca le voci nel dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Elenca gli aggiornamenti firmware supportati" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Elenca tutti i tipi di firmware disponibili" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Elenca i file nell'ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Caricamento…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloccato" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Bassa" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modalità costruttore MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Override MEI" + +msgid "MEI version" +msgstr "Versione MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Abilita manualmente i plugin selezionati" + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Lunghezza massima" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Valore massimo" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Media" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Firma metadati" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI metadati" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadati possono essere scaricati da Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versione minima" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Lunghezza minima" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Valore minimo" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Versioni di demone e client non corrispondenti, usare %s" + +#. 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" + +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" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monta l'ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "Nota: questo programma potrebbe funzionare correttamente solo come root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Necessita un riavvio dopo l'installazione" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Richiesto riavvio" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Necessita l'arresto dopo l'installazione" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nuova versione" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nessuna azione specificata." + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Nessuna versione precedente per %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nessun ID firmware trovato" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Non è stato rilevato nessun hardware con capacità di aggiornamento del firmware" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Non è stato trovato alcun plugin" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nessuna versione disponibile" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Non è abilitato alcun remoto e non sono disponibili metadati." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nessun server disponibile" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Nessun dispositivo aggiornabile" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Nessun aggiornamento disponibile" + +msgid "No updates available for remaining devices" +msgstr "Nessun aggiornamento disponibile per i dispositivi rimanenti" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Non è stato applicato alcun aggiornamento" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Non approvata" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "I dati forniti non sono sufficienti per eseguire un calcolo HSI." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Non trovato" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Non supportato" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Fatto" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "Ok" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostra solo il valore PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Usa IPFS solo quando si scaricano file" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Sono consentiti solo avanzamenti di versione" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Output in formato JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Sovrascrive il percorso ESP predefinito" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PERCORSO" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Legge e mostra i dettagli riguardo a un file firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Lettura aggiornamento dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Lettura dbx di sistema…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Password" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Esegue una patch di un blob firmware a un offset noto" + +msgid "Payload" +msgstr "Carico" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "In attesa" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentuale completamento" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Eseguire l'operazione?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Debug piattaforma" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Assicurarsi di avere la chiave di ripristino prima di continuare." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Inserire un numero tra 0 e %u:" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dipendenze plugin mancanti" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Valori possibili" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Protezione DMA pre-boot" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "La protezione pre-boot DMA non è abilitata" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "La protezione pre-boot DMA è abilitata" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Versione precedente" + +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à" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Errori" + +msgid "Proceed with upload?" +msgstr "Procedere con il caricamento?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Proprietaria" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Interroga il supporto per gli aggiornamenti firmware" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ID-REMOTO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ID-REMOTO CHIAVE VALORE" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Sola lettura" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Legge un blob firmware da un dispositivo" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Legge un firmware da un dispositivo" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Legge il firmware dal dispositivo in un file" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Legge il firmware da una partizione in un file" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lettura da %s…" + +#. TRANSLATORS: reading from the flash chips +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" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Reinstallare %s con %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Installa nuovamente il firmware attuale sul dispositivo" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Installa nuovamente il firmware su un dispositivo" + +#. 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 "Reinstallazione di %s con %s..." + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Ramo di rilascio" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Flag di rilascio" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID release" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID remoto" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Sostituisce i dati su un file di firmware esistente" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI del rapporto" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Segnalato al server remoto" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Il file system richiesto efivarfs non è stato trovato" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "L'hardware richiesto non è stato trovato" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Richiede un bootloader" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Richiede una connessione a Internet" + +#. TRANSLATORS: reboot to apply the update +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…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Fornisce tutti gli ID hardware per un dispositivo" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Protezione rollback" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Per maggiori informazioni eseguire «fwupdmgr get-upgrades»." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Eseguire «fwupdmgr sync-bkc» per completare questa azione." + +#. 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" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Eseguire senza «%s» per vedere" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "La versione del kernel in esecuzione è troppo vecchia" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Suffisso di runtime" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "IMPOSTAZIONE VALORE" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Descrittore BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Regione BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Blocco SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Protezione replay SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Scrittura SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Protezione scrittura SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SOTTOSISTEMA DRIVER [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Salva una file che consente di generare ID hardware" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Incremento scalare" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Pianifica l'installazione al prossimo riavvio quando è possibile" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Pianificazione…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure boot disabilitato" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure boot abilitato" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Per maggiori informazioni, consultare %s." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Per maggiori informazioni, consultare %s." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Dispositivo selezionato" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume selezionato" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Numero di serie" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Configura l'impostazione BIOS «%s» utilizzando «%s»." + +msgid "Set one or more BIOS settings" +msgstr "Configura una o più impostazioni BIOS" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Imposta il flag di debug durante l'aggiornamento" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Configura una o più impostazioni BIOS" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Imposta l'elenco dei firmware approvati" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Tipo di impostazione" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Le impostazioni saranno applicate dopo il riavvio" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Condivide la cronologia del firmware con gli sviluppatori" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Mostra tutti i risultati" + +#. TRANSLATORS: command line option +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 domains" +msgstr "Mostra informazioni di debug per tutti i domini" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostra le opzioni di debug" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostra dispositivi non aggiornabili" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostra maggiori informazioni di debug" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostra la cronologia degli aggiornamenti firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostra informazioni dettagliate del plugin" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra la versione calcolata del dbx" + +#. 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Firma un firmware con una chiave nuova" + +msgid "Sign data using the client certificate" +msgstr "Firma i dati col certificato del client" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Dati firmati" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Dimensione" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Alcuni dei segreti della piattaforma potrebbero essere invalidati durante l'aggiornamento del firmware" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Sorgente" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Specifica vendor/ID prodotto di un dispositivo DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Indica il file del database dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Specifica il numero di byte per trasferimento USB" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Stringa" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Completato" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Attivazione di tutti i dispositivi avvenuta con successo" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remoto disabilitato con successo" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Nuovi metadati scaricati con successo:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Remoto abilitato e aggiornato con successo" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remoto abilitato con successo" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware installato con successo" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Valore della configurazione modificato con successo" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remoto modificato con successo" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadati aggiornati manualmente con successo" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Codici di controllo del dispositivo aggiornati con successo" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "%u rapporto caricato con successo" +msgstr[1] "%u rapporti caricati con successo" +msgstr[2] "%u rapporti caricati con successo" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Codici di controllo del dispositivo verificati con successo" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Riepilogo" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Supportato" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "CPU supportata" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Supportato sul server remoto" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-ram" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Passare dal ramo %s a %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Cambia il ramo del firmware sul dispositivo" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Sincronizza le versioni del firmware con quelle migliori note dell'host" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Il sistema richiede una sorgente elettrica esterna" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TESTO" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Ricostruzione PCR0 TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "La ricostruzione PCR0 TPM non è valida" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "La ricostruzione PCR0 TPM è ora valida" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "PCR TPM vuoti" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Tag" +msgstr[1] "Tag" +msgstr[2] "Tag" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Non integro" + +msgid "Target" +msgstr "Obiettivo" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Verifica un dispositivo usando un manifesto JSON" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 è diverso dalla ricostruzione." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Il demone ha caricato codice di terze parti e non è più supportato dagli sviluppatori." + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "La versione del dispositivo non corrisponde: trovato %s, atteso %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Il firmware su %s non è fornito da %s, il fornitore dell'hardware." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "L'orologio di sistema non è stato impostato correttamente e lo scaricamento di file potrebbe non riuscire." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Il produttore non ha fornito note di rilascio." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Dispositivi con problemi:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Non ci sono file di firmware bloccati" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Non ci sono firmware approvati." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Questo dispositivo verrà retrocesso alla versione %s all'esecuzione del comando %s. " + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Questo firmware è fornito dalla comunità LVFS e non viene fornito o supportato dal produttore originale dell'hardware." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Questo pacchetto non è stato verificato, potrebbe non funzionare correttamente." + +#. 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 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: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Questo sistema presenta dei problemi di runtime HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Questo sistema ha un livello di sicurezza HSI basso." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Questo strumento consente a un amministratore di applicare aggiornamenti UEFI dbx." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Questo strumento consente a un amministratore di eseguire debug sulle operazioni UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Questo strumento consente a un amministratore di inviare richieste e controllare il demone fwupd per eseguire azioni come l'installazione o la retrocessione del firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Questo strumento consente a un amministratore di utilizzare i plugin fwupd senza che siano installati sul sistema host." + +#. 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: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Questo strumento legge e analizza il registro eventi TPM dal firmware di sistema." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Per ignorare questo avviso, usare --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Errore transitorio" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Vero" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Metadati verificati" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Dati verificati" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipo" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partizione ESP UEFI non rilavata o non configurata" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Strumento firmware UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Aggiornamenti capsula UEFI non disponibili o non abilitati nella configurazione firmware" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Strumento EUFI dbx" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Impossibile aggiornare il firmware UEFI in modalità BIOS legacy" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Chiave piattaforma UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Avvio sicuro UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Impossibile collegarsi al servizio" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Impossibile trovare l'attributo" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Svincola il driver attuale" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Sblocco del firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Sblocca un firmware specifico dal non essere installato" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Non cifrato" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Sconosciuto" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositivo sconosciuto" + +msgid "Unlock the device to allow access" +msgstr "Sblocco del dispositivo per consentire l'accesso" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Sbloccato" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Sblocca il dispositivo per accedere al firmware" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Smonta l'ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Ripristina il flag di debug durante l'aggiornamento" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Dati non firmati" + +#. 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: Suffix: the HSI result +msgid "Untainted" +msgstr "Integro" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Aggiornabile" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Errore aggiornamento" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Aggiorna immagine" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Messaggio aggiornamento" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stato aggiornamento" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Questo è un problema noto, consultare il seguente URL per maggiori informazioni:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Aggiornare ora?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "L'aggiornamento richiede il riavvio" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Aggiorna l'hash crittografico archiviato con il contenuto della ROM" + +msgid "Update the stored device verification information" +msgstr "Aggiornamento delle informazioni di verifica del dispositivo salvate" + +#. 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 specified devices to latest firmware version, or all devices if unspecified" +msgstr "Aggiorna tutti i dispositivi specificati all'ultima versione firmware o tutti i dispositivi se non specificato" + +msgid "Updating" +msgstr "Aggiornamento in corso" + +#. 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 "Aggiornamento di %s da %s a %s..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Aggiornamento di %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Aggiornare %s da %s a %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Caricare il rapporto ora?" + +#. 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 "Inviare resoconti sul firmware aiuta gli sviluppatori a identificare velocemente aggiornamenti eseguiti con successo o non riusciti su dispositivi reali." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgenza" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Usare «fwupdmgr --help» per maggiori informazioni" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Usare «fwupdtool --help» per maggiori informazioni" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa flag quirk nell'installare il firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Notifica inviata all'utente" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nome utente" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Valido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Verifica contenuti ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variante" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Fornitore" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verifica…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versione" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "Attenzione:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Attesa…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Controlla le modifiche hardware" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Scrive il firmware dal file nel dispositivo" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Scrive il firmware dal file in una partizione" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Scrittura file:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Scrittura…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "La propria distribuzione potrebbe non aver verificato la compatibilità degli aggiornamenti firmware col proprio sistema o col dispositivo collegato." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Utilizzando questo firmware, l'hardware potrebbe danneggiarsi; inoltre, l'installazione di questa versione potrebbe annullare la garanzia con %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Il sistema è impostato per il BKC di %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID-DISPOSITIVO|GUID] [BRANCH]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ID|GUID-DISPOSITIVO] [VERSIONE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FILE SIG_FILE ID-REMOTO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NOMEFILE1] [NOMEFILE2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[IMPOSTAZIONE1] [ IMPOSTAZIONE2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[FILE-SMBIOS|FILE-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "predefinito" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Strumento registro eventi TPM fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Plugin fwupd" diff --git a/fwupd-1.8.6/po/its/appdata.its b/fwupd-1.8.6/po/its/appdata.its new file mode 100644 index 0000000000000000000000000000000000000000..c41680c7e669a085caaade519545592a42b664f1 --- /dev/null +++ b/fwupd-1.8.6/po/its/appdata.its @@ -0,0 +1,14 @@ + + + + + diff --git a/fwupd-1.8.6/po/its/appdata.loc b/fwupd-1.8.6/po/its/appdata.loc new file mode 100644 index 0000000000000000000000000000000000000000..97dd7eb314cf05b85df873eb0d8e1795c75347e3 --- /dev/null +++ b/fwupd-1.8.6/po/its/appdata.loc @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fwupd-1.8.6/po/ja.po b/fwupd-1.8.6/po/ja.po new file mode 100644 index 0000000000000000000000000000000000000000..5feba4f00f196a41d375ee25ae53539966790f97 --- /dev/null +++ b/fwupd-1.8.6/po/ja.po @@ -0,0 +1,193 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Green, 2021 +# Nobuhiro Iwamatsu , 2021 +# Takuro Onoue , 2021 +# YOSHIDUMI, Rentaro, 2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Japanese (http://www.transifex.com/freedesktop/fwupd/language/ja/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ja\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +msgid "Activate the new firmware on the device" +msgstr "機器上で新しいファームウェアを実行可能にする" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%sの別名" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "外付け機器でファームウェアをダウングレードするには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "この機器のファームウェアをダウングレードするには認証が必要です。" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "ファームウェア更新用の遠隔構成を変更するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "デーモン構成の変更には認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "機器を開錠するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "外付け機器でファームウェアを更新するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "当機でファームウェアを更新するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "保存された機器の検査合計を更新するには認証が必要です" + +msgid "BYTES" +msgstr "バイト数" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "コマンドが見付かりません" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU ユーティリティ" + +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: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "ファイル名" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "ファイル名 機器の代替名|機器の代替ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "ファイル名 機器の代替名|機器の代替ID [イメージの代替名|イメージの代替ID]" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "引数の解析に失敗しました" + +msgid "Force the action ignoring all warnings" +msgstr "警告を全て無視して処理を強行" + +msgid "Install signed device firmware" +msgstr "署名済みデバイスファームウェアを導入する" + +msgid "Install signed system firmware" +msgstr "署名済みシステムファームウェアを導入する" + +msgid "Install unsigned device firmware" +msgstr "署名のないデバイスファームウェアを導入する" + +msgid "Install unsigned system firmware" +msgstr "署名のないシステムファームウェアを導入する" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux ベンダーファームウェアサービス (安定版ファームウェア)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux ベンダーファームウェアサービス (試験版ファームウェア)" + +msgid "Modify a configured remote" +msgstr "遠隔構成を変更する" + +msgid "Modify daemon configuration" +msgstr "デーモン構成を変更する" + +msgid "Print the version number" +msgstr "版数を表示する" + +msgid "Print verbose debug statements" +msgstr "冗長な診断明細を表示する" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "ファームウェアを機器からファイルに読み込む" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "ファームウェアをひとつの領域からファイルに読み込む" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "既存のファームウェアファイル内のデータを置き換えます" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "認証済みファームウェアの一覧を設定する" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "追加のデバッグ情報を表示します" + +msgid "Sign data using the client certificate" +msgstr "クライアント証明書を用いてデータに署名する" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +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: 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$ とは関係ありません。ディストリビューターは、システムまたは接続されているデバイスとの互換性について、ファームウェアの更新を確認していない可能性があります。すべてのファームウェアは、元の機器の製造元からのみ提供されています。" + +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 "この遠隔側ソースには、輸出禁止ではないファームウェアが含まれていますが、ハードウェアベンダーによって未だテスト中です。ファームウェアの更新に失敗した場合は、ファームウェアを手動でダウングレードする方法を確保しておく必要があります。" + +msgid "Unlock the device to allow access" +msgstr "アクセスを許可するために機器を開錠してください" + +msgid "Update the stored device verification information" +msgstr "保存された機器の検証情報を更新する" + +msgid "VID:PID" +msgstr "ベンダーID:製品ID" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "ファームウェアをファイルから機器に書き込む" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "ファームウェアをファイルから 1 つの領域に書き込む" diff --git a/fwupd-1.8.6/po/ka.po b/fwupd-1.8.6/po/ka.po new file mode 100644 index 0000000000000000000000000000000000000000..826878ab4b132ddd56c8d69407cc166a1b092775 --- /dev/null +++ b/fwupd-1.8.6/po/ka.po @@ -0,0 +1,1308 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Georgian (http://www.transifex.com/freedesktop/fwupd/language/ka/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ka\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC -ის განახლება" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s ელემენტის განახლება" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU მიკროკოდის განახლება" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s კამერის განახლება" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s კონფიგურაციის განახლება" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s მომხმარებლის ME-ის განახლება" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s კონტროლერის განახლება" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s კორპორატიული ME-ის განახლება" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s მოწყობილობის განახლება" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s ეკრანის განახლება" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s დისკის განახლება" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s ჩადგმული კონტროლერის განახლება" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "%s Flash დისკის განახლება" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s კლავიატურის განახლება" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME-ის განახლება" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s თაგუნას განახლება" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s ქსელის ინტერფეისის განახლება" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s SSD -ის განახლება" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s საცავის კონტროლერის განახლება" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s სისტემის განახლება" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM განახლება" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt კონტროლერის განახლება" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s თაჩპედის განახლება" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s USB მიმღების განახლება" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s განახლება" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s ამჟამად განახლებადი არაა" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s ვერსია" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u დღე" +msgstr[1] "%u დღე" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u საათი" +msgstr[1] "%u საათი" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minute" +msgstr[1] "%u minutes" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u წამი" +msgstr[1] "%u წამი" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(მოძველებული)" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "AMD-ის მიკროკოდის თავიდან ჩაწერისგან დაცვა" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD-ის მიკროკოდში ჩაწერისგან დაცვა" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "AMD-ის მიკროკოდის ვერსიის ჩამოწევისგან დაცვა" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "მიკროკოდის განახლების აქტივაცია" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "ასაკი" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%s-ის მეტსახელი" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "ერთი ტიპის მოწყობილობების ერთდროულად განახლდება" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "ალტერნატიული ბრენჩი" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "მიკროკოდის განახლებების გადატარება" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "განახლების ფალების გადატარება" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "განახლების გადატარება…" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "ავთენტიკაცია…" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "ელემენტი" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "დაბლოკილი ვერსია" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "ჩამტვირთავი კოდის ვერსია" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +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 "შეწყვეტილია" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "შეცვლილია" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "საკონტროლო რიცხვი" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "აირჩიეთ ბრენჩი:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "აირჩიეთ მიკროკოდის ტიპი:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "აირჩიეთ ტომი:" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "ბრძანება ვერ ვიპოვე" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "საზგადოების მიერ მხარდაჭერილი" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "შექმნილია" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "კრიტიკული" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "ხელმისაწვდომია ჰეშის კრიპტოგრაფიული შემოწმება" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "მიმდინარე მნიშვნელობა" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "მიმდინარე ვერსია" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "DEVICE-ID|GUID" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "გამართვის პარამეტრები" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "გაშლა…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "აღწერილობა" + +#. TRANSLATORS: more details about the update link +msgid "Details" +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 "მოწყობილობა დაემატა:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "მოწყობილობის ელემენტის სიმძლავრე მეტისმეტად დაბალია" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "მოწყობილობის ელემენტის სიმძლავრე მეტისმეტად დაბალია(%u%%, საჭიროა %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "მოწყობილობას შეუძლია ფლეშის ავარიის გადატანა" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "მოწყობილობას ვერ გამოიყენებთ, თუ ის დახურულია" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "მოწყობილობა შეიცვალა:" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "მოწყობილობა ჩაკეტილია" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "მოწყობილობა მიუწვდომელია ან გასულია უსადენო ინტერნეტის წვდომის არეალიდან" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "მოწყობილობა განახლების დროსაც გამოყენებადია" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "მოწყობილობა განახლების დასრულებას ელოდება" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "მოწყობილობა წაშლილია:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "მოწყობილობას ესაჭიროება 220V კვება" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "მოწყობილობის დონის განახლება" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "მოწყობილობის განახლების მეთოდი" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "მოწყობილობის განახლებას აქტივაცია ესაჭიროება" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "განახლების დასრულების შემდეგ მოწყობილობა აღარ გამოჩნდება" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "გამორთულია" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "ვერსის ჩვენება" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "გნებავთ ეს დაშორებული ახლა განაახლოთ?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "მზადაა!" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "გადმოწერა…" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "ხანგრძლოვობა" + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "ემულირებული ჰოსტი" + +msgid "Enable" +msgstr "ჩართვა" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "ჩართულია" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "დაშიფრული" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "დაშიფრული RAM" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "ძალიან ძველი" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "წაშლა…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "გამოსვლამდე მცირე დაყოვნება" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "გამოსვლა ძრავის ჩატვირთვის შემდეგ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "ფაილის სახელი" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FILENAME CERTIFICATE PRIVATE-KEY" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FILENAME DEVICE-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "FILENAME OFFSET DATA [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FILENAME [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILENAME [FIRMWARE-TYPE]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "შეცდომა" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "არგუმენტების დამუშავების შეცდომა" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "ფაილის დამუშავების შეცდომა" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "გადატვირთვის შეცდომა" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "მცდარი" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "ფაილის სახელი" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "ფაილის სახელის ხელმოწერა" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "ფაილის სახელის წყარო" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "ფაილის სახელი აუცილებელია" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "მიკროკოდის ატესტაცია" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "მიკროკოდის BIOS-ის დესკრიპტორი" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "მიკროკოდის BIOS-ის რეგიონი" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "მიკროკოდის გამნახლებლის შემოწმება" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "მიკროკოდის განახლებები" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "მიკროკოდის ჩაწერის დაცვა" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "მიკროკოდის ჩაწერის დაცვის დაბლოკვა" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "მიკროკოდის ატესტაცია" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "მიკროკოდის განახლებები" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "ალმები" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "ნაპოვნი" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FILE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "აპარატურა თავიდან შეერთებას ელოდება" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "მაღალი" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ჰოსტის უსაფრთხოების ID:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU უსაფრთხოება" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU მოწყობილობის უსაფრთხოება გამორთულია" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU მოწყობილობის უსაფრთხოება ჩართულია" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "უქმე…" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "დადასტურების შემოწმების ტესტების იგნორი" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "დაყენების დრო" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +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: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "მთელი რიცხვი" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard ACM-ით დაცული" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM-ით დაცული" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Intel BootGuard-ის შეცდომების პოლიტიკა" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard-ის პატრუქი" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard-ის OTP პატრუქი" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Intel BootGuard-ით შემოწმებული ჩატვირთვა" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard-ის შეცდომების პოლიტიკა" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard-ით შემოწმებული ჩატვირთვა" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET ჩართულია" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET ჩართულია" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Intel Management Engine -ის მწარმოებლის რეჟიმი" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Intel Management Engine -ის გადაფარვა" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Intel Management Engine -ის ვერსია" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "შიდა მოწყობილობა" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "არასწორი" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "არასწორი არგუმენტები" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "ვერსია ჩამოიწევა" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "ჩამტვირთავი კოდის რეჟიმშია" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "გაუმჯობესდება" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Პრობლემა" +msgstr[1] "საკითხები" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "ბრელოკი" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "ბოლო ცვლილება" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "ლიცენზია" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Linux-ის ბირთვი დაბლოკილია" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Linux-ის ბირთვის შემოწმება" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux Swap" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux-ის ბირთვი დაბლოკილია" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux Swap" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "მიკროკოდის მხარდაჭერილი განახლებების სია" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "ჩატვირთვა…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "დაბლოკილი" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "დაბალი" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI მწარმოებლის რეჟიმი" + +msgid "MEI version" +msgstr "MEI-ის ვერსია" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "საშუალო" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "მეტამონაცემების ხელმოწერა" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "მეტამონაცემების URI" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "მინიმალური ვერსია" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "დაყენების შემდეგ საჭიროებს გადატვირთვას" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "დაყენების შემდეგ საჭიროებს გამორთვას" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "ახალი ვერსია" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "ქმედება მითითებული არაა!" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "მიკროკოდის ID-ები ვერ ვიპოვე" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "დამატებები ნაპოვნი არაა" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "განახლებადი მოწყობილობები ვერ ვიპოვე" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "განახლებების ვერ ვიპოვე" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "ნებადაურთველი" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "ნაპოვნი არაა" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "მხარდაუჭერელია" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "დიახ!" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "დაშვებულია მხოლოდ ვერსიების განახლება" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "ბილიკი" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Password" + +msgid "Payload" +msgstr "შემცველობა" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "დარჩენილი" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "პროცენტები დასრულებულია" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "შევასრულო ოპერაცია?" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "გაგრძელების წინ დარწმუნდით, რომ ტომის აღდგენის გასაღები ნამდვილად გაქვთ." + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "ჩატვირთვამდელი DMA-ის დაცვა" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "პრიორიტეტი" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "პრობლემები" + +msgid "Proceed with upload?" +msgstr "დავიწყო ატვირთვა?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "პროცესორის უსაფრთხოების შემოწმება" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "დახურული კოდი" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "მხოლოდ კითხვისთვის" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "წაკითხვა…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "გადატვირთვა…" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "რელიზის ალმები" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "რელიზის ID" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "დაშორებულის ID" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "ანგარიშის URI" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "საჭიროებს ჩამტვირთავ კოდს" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "გადავტვირთო ახლა?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "მოწყობილობის გადატვირთვა…" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS-ის დესკრიპტორი" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS-ის რეგიონი" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI ბლოკი" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI ჩაწერა" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "რიგში ჩაყენება…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "დაცული ჩატვირთვა გამორთულია" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "არჩეული ტომი" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "სერიული ნომერი" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "ყველა შედეგის ჩვენება" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "გამართვის დამატებითი ინფორმაციის ჩვენება" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "გამოვრთო ახლა?" + +msgid "Signature" +msgstr "ხელმოწერა" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "ზომა" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "ამ მიკროკოდის განახლებისას შეილება პლატფორმის ზოგიერი საიდუმლო დაიკარგოს." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "წყარო" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "სტრიქონი" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "წარმატება" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "დაშორებული წარმატებით გამოირთო" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "დაშორებული წარმატებით ჩაირთო და განახლდა" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "დაშორებული წარმატებით ჩაირთო" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "დაშორებული წარმატებით შეიცვალა" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "შეჯამება" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "მხარდაჭერილი" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "მხარდაჭერილი CPU" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "უქმობამდე შეჩერება" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "RAM-ში შეჩერება" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "შეჩერება" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "RAM-ში შეჩერება" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "სისტემის კვების სიმძლავრე განახლებისთვის მეტისმეტად დაბალია" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "სისტემის კვების სიმძლავრე განახლებისთვის მეტისმეტად დაბალია(%u%%, საჭიროა %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "სისტემას კვების გარე წყარო ესაჭიროება" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "ტექსტი" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "TPM პლატფორმის მორგება" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "TPM -ის რემონტი" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "იარლიყი" +msgstr[1] "ჭდეები" + +msgid "Target" +msgstr "სამიზნე წერტილი" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "ჭეშმარიტი" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "სანდო მეტამონაცემები" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "სანდო დატვირთვა" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "ტიპით" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI პლატფორმის გასაღები" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI უსაფრთხო ჩატვირთვა" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI პლატფორმის გასაღები" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI უსაფრთხო ჩატვირთვა" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "დაუშიფრავი" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "უცნობია" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "უცნობი მოწყობილობა" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "განბლოკილი" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "განახლებადი" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "განახლების შეცდომა" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "ასლის განახლება" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "განახლების შეტყობინება" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "განვაახლო ახლა?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "განახლება გადატვირთვას საჭიროებს" + +msgid "Updating" +msgstr "განახლება" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s-ის განახლება..." + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "მომხმარებელი გაფრთხილებულია" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "მომხმარებელი" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "სწორი" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "ვარიანტი" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "მომწოდებელი" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "შემოწმება…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "ვერსია" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "გაფთხილება:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "მოლოდინი…" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "ფაილის ჩაწერა:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "ჩაწერა…" + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "ამ მიკროკოდით თქვენი აპარატურა შეიძლება დაზიანდეს. ამავე დროს ამ რელიზის დაყენებამ შეიძლება %s-ზე გარანტია მოგიხსნათ." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FILE|HWIDS-FILE]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "ნაგულისხმები" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd დამატებები" diff --git a/fwupd-1.8.6/po/kk.po b/fwupd-1.8.6/po/kk.po new file mode 100644 index 0000000000000000000000000000000000000000..799aa994b017dad4e71d11f5bbe55c10a8edbf52 --- /dev/null +++ b/fwupd-1.8.6/po/kk.po @@ -0,0 +1,49 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Baurzhan Muftakhidinov , 2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: kk\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Қосылған" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Бас тартылған" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Өзгертілген" + +#. TRANSLATORS: read from device to host +msgid "Erasing" +msgstr "Өшірілуде" + +#. TRANSLATORS: read from device to host +msgid "Reading" +msgstr "Оқылуда" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Өшірілген" + +#. TRANSLATORS: read from device to host +msgid "Verifying" +msgstr "Тексерілуде" + +#. TRANSLATORS: write from host to device +msgid "Writing" +msgstr "Жазылуда" diff --git a/fwupd-1.8.6/po/ko.po b/fwupd-1.8.6/po/ko.po new file mode 100644 index 0000000000000000000000000000000000000000..a3511ad4fb484d777694b2cf3d9243ecec8c5d32 --- /dev/null +++ b/fwupd-1.8.6/po/ko.po @@ -0,0 +1,2228 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Seong-ho Cho , 2017,2019,2021 +# Shinjo Park , 2018-2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: ko\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f분 남음" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s 배터리 업데이트" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU 마이크로코드 업데이트" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s 카메라 업데이트" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s 설정 업데이트" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s 소비자용 ME 업데이트" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s 컨트롤러 업데이트" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s 기업용 ME 업데이트" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s 장치 업데이트" + +#. TRANSLATORS: 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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s 키보드 업데이트" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME 업데이트" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s 마우스 업데이트" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s 네트워크 인터페이스 업데이트" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s 저장 장치 컨트롤러 업데이트" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s 시스템 업데이트" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM 업데이트" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt 컨트롤러 업데이트" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s 터치패드 업데이트" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s 업데이트" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "업데이트 중에는 %s 및 연결된 모든 장치를 사용할 수 없을 수도 있습니다." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s 제조 모드" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "장치 손상을 방지하려면 업데이트 중 %s의 연결을 계속 유지해야 합니다." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "장치 손상을 방지하려면 업데이트 중 %s을(를) 전원에 계속 연결해 두어야 합니다." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s 재정의" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s 버전" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u일" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "장치 %u개의 펌웨어를 업데이트할 수 있습니다." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u시간" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +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: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(사용되지 않음)" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "동작이 필요합니다:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "장치 활성화" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "대기 중인 장치 활성화" + +msgid "Activate the new firmware on the device" +msgstr "장치에 새 펌웨어 활성화" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "펌웨어 업데이트 활성화 중" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "다음 장치의 펌웨어 업데이트 활성화 중:" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "경과 기간" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "동의하며 원격 저장소를 활성화하시겠습니까?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%s의 별칭" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "동일한 모든 형식의 장치를 동시 업데이트합니다" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "펌웨어 다운그레이드 허용" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "기존 펌웨어 버전 재설치 허용" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "펌웨어 브랜치 전환 허용" + +#. TRANSLATORS: 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 line option +msgid "Apply firmware updates" +msgstr "펌웨어 업데이트 적용" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "필요하지 않은 경우에도 업데이트 적용" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "업데이트 파일 적용" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "업데이트 적용 중..." + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "허용된 펌웨어:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "다음에도 다시 확인하시겠습니까?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "펌웨어 모드에 연결" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "인증 중…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "인증 세부 절차가 필요합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "이동식 장치의 펌웨어를 이전 버전으로 되돌리려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "이 머신에 이전 버전의 펌웨어를 설치하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "펌웨어 업데이트에 사용할 원격 설정을 수정하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "데몬 설정을 수정하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "장치 잠금을 해제하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "이동식 장치의 펌웨어를 업데이트하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "이 머신의 펌웨어를 업데이트하려면 인증해야 합니다" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "저장된 체크섬을 업데이트하려면 인증해야 합니다" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "자동 보고" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "매번 자동으로 업로드하시겠습니까?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML FILENAME-DST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "새 커널 드라이버 바인드" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "차단된 펌웨어 파일:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "차단할 펌웨어:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "지정한 펌웨어 설치 방지" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "부트로더 버전" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "브랜치" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +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 "취소함" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "dbx 업데이트가 이미 적용되었기 때문에 적용할 수 없습니다." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "라이브 미디어에서 실행 중일 때는 업데이트를 적용할 수 없음" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "바뀜" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "암호화 해시가 펌웨어와 일치하는지 확인" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "체크섬" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "브랜치 선택:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "장치를 선택하십시오:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "펌웨어 종류를 선택하십시오:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "출시 버전을 선택하십시오:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "볼륨 선택:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "최근 업데이트 결과 지우기" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "명령을 찾을 수 없습니다" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "펌웨어 파일 변환" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "생성됨" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "매우 높음" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "암호화 해시 검사 사용 가능" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "현재 버전" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "DEVICE-ID|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU 유틸리티" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "디버깅 옵션" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "압축 해제 중…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "설명" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "부트로더 모드로 전환" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "자세한 정보" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +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 "장치 추가됨:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "장치 업데이트 실패 시 복구 가능" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "장치 상태 바뀜:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "장치 펌웨어에서 버전 확인을 지원해야 함" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "장치가 잠김" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "장치에 제공된 모든 릴리스를 설치해야 함" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "장치에 접근할 수 없습니다" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "업데이트 중 장치를 사용할 수 있음" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "장치 제거됨:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "안전한 업데이트 지원" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "장치에서 펌웨어 브랜치 전환을 지원함" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "장치 업데이트 방식" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "장치 업데이트 활성화 필요" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "장치에 펌웨어를 설치하기 전에 백업을 수행함" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "업데이트 후 장치가 다시 표시되지 않음" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "성공적으로 업데이트된 장치:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "올바르게 업데이트되지 않은 장치:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "펌웨어 업데이트를 사용할 수 없는 장치:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "최신 펌웨어가 설치된 장치:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +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 unreported history" +msgstr "보고되지 않은 과거 기록 검사하지 않기" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "원격 저장소에서 다운로드 활성화 여부를 검사하지 않기" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "업데이트 후 다시 시작 묻거나 검사하지 않기" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "로그 도메인 접두사를 포함하지 않기" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "timestamp 접두사를 포함하지 않기" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "장치 안정성 검사를 실행하지 않음" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "과거 기록 데이터베이스에 기록하지 않기" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "펌웨어 브랜치 변경 조건을 이해했습니까?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "다음에 업데이트할 때 이 기능을 비활성화하시겠습니까?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "원격 저장소를 새로 고치시겠습니까?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "다음에 업데이트할 때 보고서를 자동으로 업로드하시겠습니까?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "완료!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "%s을(를) %s에서 %s(으)로 다운그레이드하시겠습니까?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "장치 펌웨어 다운그레이드" + +#. TRANSLATORS: 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(으)로 다운그레이드 중..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s 다운그레이드 중..." + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "파일 다운로드" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "다운로드 중…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "파일에 저장된 SMBIOS 데이터 덤프 출력" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "예상 시간" + +#. TRANSLATORS: 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "새 원격 저장소를 활성화하시겠습니까?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "이 원격 저장소를 활성화하시겠습니까?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "암호화됨" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "암호화된 RAM" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "모든 펌웨어 업데이트 기록 지우기" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "지우는 중…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "짧은 대기 시간 경과 후 나가기" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "엔진을 불러온 후 나가기" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "펌웨어 파일 구조를 XML로 내보내기" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "펌웨어 바이너리 파일에서 이미지 추출" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FILE [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "FILENAME" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FILENAME CERTIFICATE PRIVATE-KEY" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FILENAME DEVICE-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FILENAME [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILENAME [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "실패함" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "업데이트를 적용할 수 없음" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "로컬 dbx를 불러올 수 없음" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "특이 사항을 불러올 수 없음" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "시스템 dbx를 불러올 수 없음" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "잠글 수 없음" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "인자 해석에 실패했습니다" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "파일을 해석할 수 없음" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "--filter에 전달한 플래그 해석에 실패함" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "로컬 dbx를 해석할 수 없음" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "ESP 내용을 검사할 수 없음" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "파일 이름" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "파일 이름 서명" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "파일 이름 원본" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "파일 이름이 필요함" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "플래그로 장치를 필터하며, ~ 기호를 앞에 붙이면 제외할 수 있습니다. 예: 'internal,~needs-reboot'" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "펌웨어 기본 URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "펌웨어 업데이트 D-Bus 서비스" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "펌웨어 업데이트 데몬" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "펌웨어 유틸리티" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "펌웨어 검증" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "펌웨어가 이미 차단됨" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "펌웨어가 이미 차단되어 있지 않음" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "펌웨어 메타데이터가 %u일 동안 업데이트되지 않았으므로 최신 정보가 누락되었을 수도 있습니다." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "펌웨어 메타데이터 최근 새로 고침: %s 전. 다시 새로 고치려면 --force 옵션을 사용하십시오." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "펌웨어 업데이트" + +msgid "Firmware updates are not supported on this machine." +msgstr "이 머신에서 펌웨어 업데이트를 지원하지 않습니다." + +msgid "Firmware updates are supported on this machine." +msgstr "이 머신에서 펌웨어 업데이트를 지원합니다." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "펌웨어 업그레이드 비활성화됨. 활성화하려면 'fwupdmgr unlock' 명령을 실행하십시오" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "플래그" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "런타임 체크를 비활성화하여 강제로 작업 실행" + +msgid "Force the action ignoring all warnings" +msgstr "모든 경고를 무시하고 작업 강제 진행" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "찾음" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "fwupd를 지원하는 모든 장치 정보 가져오기" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "펌웨어 업데이트를 지원하는 모든 장치 정보 가져오기" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "시스템에 등록된 모든 활성 플러그인 가져오기" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "펌웨어 파일 세부 정보 가져오기" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "원격 설정 정보 가져오기" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "호스트 보안 속성 가져오기" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "허용된 펌웨어 목록 가져오기" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "설치 방지된 펌웨어 목록 가져오기" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "연결한 하드웨어의 업데이트 목록을 가져옵니다" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "장치 펌웨어 릴리스 가져오기" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "최근 업데이트 결과 가져오기" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FILE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "하드웨어를 다시 연결해야 함" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "높음" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "호스트 보안 ID:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "대기 중…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "파일을 다운로드할 때 SSL 엄격한 검사 건너뛰기" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "펌웨어 체크섬 오류 무시" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "펌웨어 하드웨어 일치 오류 무시" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "장치 안정성 검사 무시" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "SSL 엄격한 검사 무시, 차후에 자동으로 적용하려면 DISABLE_SSL_STRICT 환경 변수를 지정해야 함" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +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 signed device firmware" +msgstr "서명된 장치 펌웨어 설치" + +msgid "Install signed system firmware" +msgstr "서명된 시스템 펌웨어 설치" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "부모 장치에 먼저 설치" + +msgid "Install unsigned device firmware" +msgstr "서명되지 않은 장치 펌웨어 설치" + +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에 설치 중..." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM 보호됨" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP 퓨즈" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard 오류 정책" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard 검증된 부트" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET 활성" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET 활성화됨" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "내장 장치" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "올바르지 않음" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "현재 부트로더 모드에 있음" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "문제점" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "KEY,VALUE" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "키 모음" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "<위치>" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "마지막으로 수정한 날짜" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "1분 미만 남음" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "라이선스" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "리눅스 제조사 펌웨어 서비스(안정 버전 펌웨어)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "리눅스 제조사 펌웨어 서비스(테스트 버전 펌웨어)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "리눅스 커널" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "리눅스 커널 Lockdown" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "리눅스 스왑" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "dbx의 항목 표시" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "지원하는 펌웨어 업데이트 목록 표시" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "사용 가능한 펌웨어 종류 표시" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "ESP에 저장된 파일 표시" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "불러오는 중…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "잠김" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "낮음" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI 제조 모드" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI 재정의" + +msgid "MEI version" +msgstr "MEI 버전" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "지정한 플러그인을 수동으로 활성화" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "중간" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "메타데이터 서명" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "메타데이터 URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "리눅스 제조사 펌웨어 서비스에서 메타데이터를 가져올 수 있습니다." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "최소 버전" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "데몬과 클라이언트가 일치하지 않음, %s을(를) 대신 사용함" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "데몬 설정값 수정" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "지정한 원격 저장소 수정" + +msgid "Modify a configured remote" +msgstr "원격 설정 수정" + +msgid "Modify daemon configuration" +msgstr "데몬 설정 수정" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "데몬 이벤트 감시" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "ESP 마운트" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "설치 후 다시 시작 필요함" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "다시 시작 필요" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "설치 후 종료 필요함" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "새 버전" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "동작을 지정하지 않았습니다!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "%s의 다운그레이드 없음" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "펌웨어 ID를 찾을 수 없음" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "사용 가능한 릴리스 없음" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "원격 저장소가 설정되지 않았으므로 메타데이터를 사용할 수 없습니다." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "원격 저장소 없음" + +msgid "No updates available for remaining devices" +msgstr "남은 장치의 업데이트가 없습니다" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "적용된 업데이트 없음" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "찾을 수 없음" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "지원하지 않음" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "확인" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "단일 PCR 값만 표시" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "파일을 다운로드할 때 IPFS만 사용" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "버전 업그레이드만 가능합니다" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "JSON 형식으로 출력" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "기본 ESP 경로 재정의" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PATH" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "펌웨어 파일을 처리하고 세부 정보 표시" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "dbx 업데이트 해석 중..." + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "시스템 dbx 해석 중..." + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "암호" + +msgid "Payload" +msgstr "페이로드" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "대기 중" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "진행률" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "작업을 수행하시겠습니까?" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "0에서 %u까지의 숫자 중 하나를 입력하십시오:" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "플러그인 의존성을 찾을 수 없음" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "부트 전 DMA 보호" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "이전 버전" + +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: a non-free software license +msgid "Proprietary" +msgstr "독점적" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "펌웨어 업데이트 지원 조회" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "REMOTE-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "REMOTE-ID KEY VALUE" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "장치에서 펌웨어 바이너리 읽기" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "장치 펌웨어를 읽어 파일에 기록" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "파티션에서 펌웨어를 읽어 파일에 기록" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "%s에서 읽는 중..." + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "읽는 중…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "다시 시작하는 중..." + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "원격 서버에서 메타데이터 새로 고침" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "%s에 %s을(를) 다시 설치하시겠습니까?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "장치에 현재 펌웨어 다시 설치" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "장치에 펌웨어 다시 설치" + +#. TRANSLATORS: the 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 "%s에 %s 다시 설치하는 중..." + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "릴리스 브랜치" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "원격 ID" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "기존 펌웨어 파일의 데이터 교체" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "보고서 URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "원격 서버에 보고됨" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "필요한 efivarfs 파일 시스템을 찾을 수 없음" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "필요한 하드웨어를 찾을 수 없음" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "부트로더 모드 진입 필요함" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "인터넷 연결 필요" + +#. 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 반환" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "더 많은 정보를 보려면 `fwupdmgr get-upgrades` 명령을 실행하십시오." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "install-blob 사용 시 플러그인 정리 루틴 실행" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "install-blob 사용 시 플러그인 준비 루틴 실행" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "실행 커널이 너무 오래되었습니다" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "런타임 접미사" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS 설명자" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS 영역" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI 잠금" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI 기록" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "하드웨어 ID를 생성할 수 있는 파일 저장" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "가능하다면 다음에 다시 시작할 때 설치 예약" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "작업 계획 중…" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "자세한 정보는 %s 사이트를 참조하십시오." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "선택한 장치" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "선택한 볼륨" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "일련 번호" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +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 all results" +msgstr "모든 결과 표시" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "클라이언트와 데몬 버전 표시" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "지정한 도메인의 자세한 데몬 정보 표시" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "모든 도메인의 디버깅 정보 표시" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "디버깅 옵션을 표시합니다" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "업데이트할 수 없는 장치 표시" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "추가 디버깅 정보 표시" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "펌웨어 업데이트 기록 보기" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "자세한 플러그인 정보 표시" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "dbx의 계산된 버전 표시" + +#. 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 "지금 종료하시겠습니까?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "새 키로 펌웨어 서명" + +msgid "Sign data using the client certificate" +msgstr "클라이언트 인증서로 데이터 서명" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "클라이언트 인증서로 데이터 서명" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "클라이언트 인증서로 업로드된 데이터 서명" + +msgid "Signature" +msgstr "서명" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "크기" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "원본" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "DFU 장치의 제조사/제품 ID 지정" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "dbx 데이터베이스 파일 지정" + +msgid "Specify the number of bytes per USB transfer" +msgstr "USB 전송 당 바이트 수 지정" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "성공" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "모든 장치를 활성화함" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "원격 저장소를 비활성화함" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "새 메타데이터를 다운로드함:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "원격 저장소를 활성화하고 새로 고침" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "원격 저장소를 활성화함" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "펌웨어 설치 성공" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "설정을 수정함" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "원격 저장소를 수정함" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "메타데이터를 수동으로 업데이트함" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "장치 체크섬을 업데이트함" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "보고서 %u개 업로드함" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "장치 체크섬을 검증함" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "요약" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "지원함" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "원격 서버에서 지원함" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Idle 절전" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "RAM 절전" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "브랜치를 %s에서 %s(으)로 전환하시겠습니까?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "장치의 펌웨어 브랜치 전환" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "시스템에 외부 전원을 연결해야 함" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 재구축" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "오염됨" + +msgid "Target" +msgstr "대상" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "JSON 매니페스트 장치를 시험합니다" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0이 재구축한 값과 다릅니다." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "데몬에서 제3자 코드를 불러왔으며 업스트림 개발자는 더 이상 지원하지 않습니다!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "%s의 펌웨어는 하드웨어 제조사 %s에서 제공하지 않았습니다." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "시스템 시계가 올바르게 설정되어 있지 않습니다. 파일 다운로드가 실패할 수도 있습니다." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "차단된 펌웨어 파일 없음" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "허용된 펌웨어가 없습니다." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "이 패키지는 검증되지 않았습니다. 올바르게 작동하지 않을 수도 있습니다." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "이 프로그램은 루트 권한으로만 올바르게 작동할 수도 있습니다" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "이 원격 저장소에서는 하드웨어 제조사에서 검증 단계를 진행 중인 펌웨어를 배포합니다. 펌웨어 업데이트 도중 및 이후 문제가 발생했을 경우를 대비하여 직접 펌웨어를 다운그레이드할 방편을 확보하기를 추천합니다." + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "시스템에 HSI 런타임 문제가 있습니다." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "시스템에서 HSI 보안 등급 낮음을 사용 중입니다." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "이 도구를 사용하면 시스템 관리자가 UEFI dbx 업데이트를 적용할 수 있습니다." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "이 도구를 사용하면 시스템 관리자가 UpdateCapsule 작업을 디버그할 수 있습니다." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "이 도구를 사용하면 시스템 관리자가 펌웨어 설치 및 다운그레이드 등 동작을 수행하도록 fwupd 데몬에 질의하고 제어할 수 있습니다." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "이 도구를 사용하면 시스템 관리자가 fwupd 플러그인을 호스트 시스템에 설치하지 않고 사용할 수 있습니다." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "이 도구는 루트로만 사용할 수 있습니다" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "이 도구는 시스템 펌웨어에서 TPM 이벤트 로그를 읽어서 해석합니다." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "일시적 실패" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "형식" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP 파티션이 감지되지 않았거나 설정되지 않았음" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI 펌웨어 유틸리티" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI 캡슐 업데이트를 사용할 수 없거나 펌웨어 설정에서 비활성화됨" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx 유틸리티" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "레거시 BIOS 모드에서 UEFI 펌웨어를 업데이트할 수 없음" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI 플랫폼 키" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI 보안 부트" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "서비스에 연결할 수 없음" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "현재 드라이버 바인드 해제" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "차단 해제할 펌웨어:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "지정한 펌웨어 설치 방지 해제" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "암호화되지 않음" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "알 수 없음" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "알 수 없는 장치" + +msgid "Unlock the device to allow access" +msgstr "접근을 허용하려면 장치 잠금을 해제하십시오" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "잠금 해제됨" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "펌웨어에 접근할 수 있도록 장치 잠금을 해제" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "ESP 마운트 해제" + +#. TRANSLATORS: 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: Suffix: the HSI result +msgid "Untainted" +msgstr "오염되지 않음" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "업데이트 가능" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "업데이트 오류" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "업데이트 메시지" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "업데이트 상태" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "알 수 없는 이유로 업데이트가 실패했습니다. 더 많은 정보를 보려면 다음 URL을 참조하십시오:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "지금 업데이트하시겠습니까?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "업데이트 시 다시 시작 필요함" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "현재 ROM 내용으로 저장된 암호화 해시를 업데이트" + +msgid "Update the stored device verification information" +msgstr "저장된 장치 검증 정보 업데이트" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "저장된 메타데이터를 현재 내용으로 업데이트" + +msgid "Updating" +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 "Updating %s from %s to %s... " +msgstr "%s을(를) %s에서 %s(으)로 업데이트 중..." + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s 업데이트 중..." + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "%s을(를) %s에서 %s(으)로 업그레이드하시겠습니까?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload 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: how important the release is +msgid "Urgency" +msgstr "중요도" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "도움말을 보려면 fwupdmgr --help 명령을 사용하십시오" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "도움말을 보려면 fwupdtool --help를 실행하십시오" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "펌웨어를 설치할 때 quirk 플래그 사용" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "사용자에게 알림이 표시됨" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "사용자 이름" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "올바름" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "ESP 내용 검사 중..." + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "파생형" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "제조사" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "검증 중…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "버전" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "경고:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "기다리는 중…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "하드웨어 변경 사항 감시" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "파일의 펌웨어를 장치에 기록" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "파일의 펌웨어를 파티션 하나에 기록" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "파일에 기록 중:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "쓰는 중…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "배포판 개발사에서 펌웨어 업데이트와 시스템 및 연결된 장치간의 호환성을 검증한다는 보장이 없습니다." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "이 펌웨어를 사용했을 때 하드웨어가 손상될 수 있으며, 이 릴리스를 설치하면 %s의 제품 보증을 무효화할 수도 있습니다." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[DEVICE-ID|GUID] [BRANCH]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FILE FILE_SIG REMOTE-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[<파일이름1>] [<파일이름2>]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FILE|HWIDS-FILE]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "기본값" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM 이벤트 로그 유틸리티" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd 플러그인" diff --git a/fwupd-1.8.6/po/ky.po b/fwupd-1.8.6/po/ky.po new file mode 100644 index 0000000000000000000000000000000000000000..f8ed34ec277d7f1012d9f2fcc8165cb3c8b480bb --- /dev/null +++ b/fwupd-1.8.6/po/ky.po @@ -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 --git a/fwupd-1.8.6/po/lt.po b/fwupd-1.8.6/po/lt.po new file mode 100644 index 0000000000000000000000000000000000000000..abb57af13b214f836c41dca10ef24663137a6ed9 --- /dev/null +++ b/fwupd-1.8.6/po/lt.po @@ -0,0 +1,1436 @@ +# 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 +# Moo, 2020-2021 +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: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Liko %.0f minutė" +msgstr[1] "Liko %.0f minutės" +msgstr[2] "Liko %.0f minučių" +msgstr[3] "Liko %.0f minutė" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s akumuliatoriaus atnaujinimas" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s kameros atnaujinimas" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s konfigūracijos atnaujinimas" + +#. 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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s klaviatūros 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: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s pelės 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: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s jutiklinio kilimėlio 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: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s versija" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u diena" +msgstr[1] "%u dienos" +msgstr[2] "%u dienų" +msgstr[3] "%u diena" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u įrenginiui yra prieinamas programinės aparatinės įrangos naujinimas." +msgstr[1] "%u įrenginiams yra prieinamas programinės aparatinės įrangos naujinimas." +msgstr[2] "%u įrenginių yra prieinamas programinės aparatinės įrangos naujinimas." +msgstr[3] "%u įrenginiui yra prieinamas programinės aparatinės įrangos naujinimas." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u valanda" +msgstr[1] "%u valandos" +msgstr[2] "%u valandų" +msgstr[3] "%u valanda" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "Yra palaikomas %u vietinis įrenginys" +msgstr[1] "Yra palaikomi %u vietiniai įrenginiai" +msgstr[2] "Yra palaikoma %u vietinių įrenginių" +msgstr[3] "Yra palaikomas %u vietinis įrenginys" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minutė" +msgstr[1] "%u minutės" +msgstr[2] "%u minučių" +msgstr[3] "%u minutė" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u sekundė" +msgstr[1] "%u sekundės" +msgstr[2] "%u sekundžių" +msgstr[3] "%u sekundė" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Reikalingas veiksmas:" + +#. 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: 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 reinstalling 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 line option +msgid "Apply firmware updates" +msgstr "Taikyti programinės aparatinės įrangos atnaujinimus" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Taikyti atnaujinimą, netgi kai nepatariama" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Taikyti atnaujinimo failus" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Taikomas atnaujinimas…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Patvirtinta programinė aparatinė įranga:" +msgstr[1] "Patvirtinta programinė aparatinė įranga:" +msgstr[2] "Patvirtinta programinė aparatinė įranga:" +msgstr[3] "Patvirtinta programinė aparatinė įranga:" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Pridėti į programinės aparatinės įrangos veikseną" + +#. 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 modify daemon configuration" +msgstr "Norint modifikuoti tarnybos konfigūraciją, 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 "Bind new kernel driver" +msgstr "Susieti naują branduolio tvarkyklę" + +#. 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: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Negalima taikyti, nes dbx atnaujinimas jau yra pritaikytas." + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Pakeistas" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolinė suma" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Pasirinkite įrenginį:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Pasirinkite laidą:" + +#. 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 a firmware file" +msgstr "Konvertuoti programinės aparatinės įrangos failą" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Sukurtas" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Dabartinė versija" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ĮRENGINIO-ID|GUID" + +#. 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: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Aprašas" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Atskirti į pradinio įkėliklio veikseną" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Įrenginio vėliavėlės" + +#. 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: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Įrenginys yra užrakintas" + +#. 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:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Įrenginiai, neturintys prieinamų programinės aparatinės įrangos atnaujinimų: " + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Išjungtas" + +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 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ę" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Atlikta!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Sendinti %s iš %s į %s?" + +#. 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: command description +msgid "Download a file" +msgstr "Atsisiųsti failą" + +#. 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: length of time the update takes to apply +msgid "Duration" +msgstr "Trukmė" + +#. 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: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "Šifruotas" + +#. 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: command description +msgid "Export a firmware file structure to XML" +msgstr "Eksportuoti programinės aparatinės įrangos failo struktūrą į XML" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FAILAS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FAILAS [ĮRENGINIO-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "FAILO-PAVADINIMAS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FAILO-PAVADINIMAS LIUDIJIMAS PRIVATUSIS-RAKTAS" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FAILO-PAVADINIMAS ĮRENGINIO-ALT-PAVADINIMAS|ĮRENGINIO-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FAILO-PAVADINIMAS ĮRENGINIO-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FAILO-PAVADINIMAS [ĮRENGINIO-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FAILO-PAVADINIMAS [PROGRAMINĖS-APARATINĖS-ĮRANGOS-TIPAS]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Patyrė nesėkmę" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Nepavyko pritaikyti atnaujinimo" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Nepavyko prisijungti prie tarnybos" + +#. 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Nepavyko įkelti vietinio dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Nepavyko įkelti gudrybių" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Nepavyko įkelti sisteminio dbx" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Nepavyko užrakinti" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Nepavyko išanalizuoti argumentų" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Nepavyko išanalizuoti failo" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Nepavyko patikrinti ESP turinio" + +#. 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: user did not include a filename parameter +msgid "Filename required" +msgstr "Reikalingas failo pavadinimas" + +#. 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" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Programinė aparatinė įranga jau užblokuota" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Programinės aparatinės įrangos atnaujinimai" + +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: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Vėliavėlės" + +msgid "Force the action ignoring all warnings" +msgstr "Priverstinai atlikti veiksmą nepaisant visų įspėjimų" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Rastas" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Gauti visas fwupd palaikomas įrenginio vėliavėles" + +#. 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 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: daemon is inactive +msgid "Idle…" +msgstr "Neveiklus…" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Nepaisyti programinės aparatinės įrangos kontrolinės sumos nesėkmių" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Įdiegimo trukmė" + +#. 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 signed device firmware" +msgstr "Įdiegti pasirašytą įrenginio programinę aparatinę įrangą" + +msgid "Install signed system firmware" +msgstr "Įdiegti pasirašytą sistemos programinę aparatinę įrangą" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Iš pradžių, įdiegti į viršesnį įrenginį" + +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…" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Vidinis įrenginys" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "RAKTAS,REIKŠMĖ" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Raktinė" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "VIETA" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Liko mažiau kaip viena minutė" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licencija" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux branduolys" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Išvardyti dbx esančius įrašus" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Išvardyti prieinamus programinės aparatinės įrangos atnaujinimus" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Išvardyti prieinamus programinės aparatinės įrangos tipus" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Išvardija ESP esančius failus" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Įkeliama…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Užrakintas" + +msgid "MEI version" +msgstr "MEI versija" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metaduomenų parašas" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metaduomenų URI" + +#. 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." + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifikuoja nurodytą nuotolinę saugyklą" + +msgid "Modify a configured remote" +msgstr "Modifikuoti sukonfigūruotą nuotolinę saugyklą" + +msgid "Modify daemon configuration" +msgstr "Modifikuoti tarnybos konfigūraciją" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Stebėti tarnybą, ar yra įvykių" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Prijungia ESP" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Reikia paleisti iš naujo" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nauja versija" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nenurodytas joks veiksmas!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Nėra sendinimų, skirtų %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nerasta jokių programinės aparatinės įrangos ID" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "Nėra prieinamų laidų" + +#. 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" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nerastas" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nepalaikomas" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Gerai" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Rodyti tik vieną PCR reikšmę" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Naudoti IPFS tik atsisiunčiant failus" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Išvestis JSON formatu" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Nustelbti numatytąjį ESS (angl. ESP) kelią" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "KELIAS" + +#. 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" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Atlikti operaciją?" + +#. 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: " + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Ankstesnė versija" + +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: a non-free software license +msgid "Proprietary" +msgstr "Nuosavybinė" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Užklausti programinės aparatinės įrangos atnaujinimų palaikymo" + +#. 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: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Skaitoma iš %s…" + +#. 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: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Įdiegti iš naujo %s į %s?" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Iš naujo įdiegti įrenginyje programinę aparatinę įrangą" + +#. 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: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Nuotolinės saugyklos ID" + +#. 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: reboot to apply the update +msgid "Restart now?" +msgstr "Paleisti iš naujo dabar?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Paleisti tarnybą iš naujo, kad pakeitimas įsigaliotų?" + +#. 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: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Išsamesnei informacijai, paleiskite „fwupdmgr get-upgrades“." + +#. 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" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Kai įmanoma, suplanuoti įdiegimą kitam paleidimui iš naujo" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Suplanuojama…" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Išsamesnei informacijai, žiūrėkite %s" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Pasirinktas įrenginys" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Atnaujinimo metu nustatyti derinimo vėliavėlę" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Nustato patvirtintą 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 all results" +msgstr "Rodyti visus rezultatus" + +#. 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 calculated version of the dbx" +msgstr "Rodyti apskaičiuotą dbx versiją" + +#. 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Pasirašyti programinę aparatinę įrangą nauju raktu" + +msgid "Sign data using the client certificate" +msgstr "Pasirašyti duomenis, naudojant kliento liudijimą" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Dydis" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Nurodyti ĮPAĮA įrenginio tiekėją/produkto ID" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Nurodyti dbx duomenų bazės failą" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Nurodyti baitų skaičių tenkantį vienam USB persiuntimui" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Pavyko" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Sėkmingai aktyvuoti visi įrenginiai" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Sėkmingai atsisiųsti nauji metaduomenys: " + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Programinė aparatinė įranga sėkmingai įdiegta" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Sėkmingai atnaujintos įrenginių kontrolinės sumos" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Santrauka" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Palaikomas" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Palaikomas nuotoliniame serveryje" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Sistema reikalauja išorinio maitinimo šaltinio" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEKSTAS" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +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: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Šis paketas nebuvo patvirtintas, jis gali tinkamai neveikti." + +#. 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: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Šis įrankis leidžia administratoriui taikyti UEFI dbx atnaujinimus." + +#. 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 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: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx paslaugų programa" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Atsieti dabartinę tvarkyklę" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Nešifruotas" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Nežinoma" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Nežinomas įrenginys" + +msgid "Unlock the device to allow access" +msgstr "Atrakinti įrenginį, kad būtų leista prieiga" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Atrakintas" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Atrakina įrenginį programinės aparatinės įrangos prieigai" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Atjungia ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Atnaujinimo metu panaikinti derinimo vėliavėlės nustatymą" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Nepalaikoma tarnybos versija %s, kliento programos versija yra %s" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Galimas atnaujinti" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Atnaujinimo klaida" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Atnaujinimo pranešimas" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Atnaujinimo būsena" + +#. 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?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Atnaujinimas reikalauja paleidimo iš naujo" + +msgid "Update the stored device verification information" +msgstr "Atnaujinti saugomo įrenginio patikrinimo informaciją" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Atnaujinti saugomus metaduomenis esamu turiniu" + +msgid "Updating" +msgstr "Atnaujinama" + +#. 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: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Naujinti %s iš %s į %s?" + +#. 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: User has been notified +msgid "User has been notified" +msgstr "Naudotojui buvo pranešta" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Naudotojo vardas" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Tikrinamas ESP turinys…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variantas" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Patikrinama…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versija" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "ĮSPĖJIMAS:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Laukiama…" + +#. 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: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Rašomas failas:" + +#. 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ų." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[KONTROLINĖ-SUMA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ĮRENGINIO-ID|GUID]" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM įvykių žurnalo paslaugų programa" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd įskiepiai" diff --git a/fwupd-1.8.6/po/meson.build b/fwupd-1.8.6/po/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..9be6c9a34b229c4c0a4876480157be579f4411e4 --- /dev/null +++ b/fwupd-1.8.6/po/meson.build @@ -0,0 +1,12 @@ +i18n.gettext(meson.project_name(), + preset: 'glib', + args: [ + '--default-domain=' + meson.project_name(), + ] +) + +run_target('fix-translations', + command: [ + join_paths(meson.project_source_root(), 'contrib/fix_translations.py'), + join_paths(meson.project_source_root(), 'po') + ]) diff --git a/fwupd-1.8.6/po/nl.po b/fwupd-1.8.6/po/nl.po new file mode 100644 index 0000000000000000000000000000000000000000..a3bec7d3181a0466a66bab713f62d6db86065216 --- /dev/null +++ b/fwupd-1.8.6/po/nl.po @@ -0,0 +1,268 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Richard E. van der Luit , 2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias voor %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Downgraden van oude firmware-versies toestaan" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Om de firmware op een verwijderbaar apparaat te downgraden moet u toestemming verlenen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Om de firmware op deze computer te downgraden moet u toestemming verlenen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Om een apparaat te ontgrendelen moet u toestemming verlenen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Om de firmware op een verwijderbaar apparaat bij te werken moet u toestemming verlenen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Om de firmware op deze computer bij te werken moet u toestemming verlenen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Om de opgeslagen controlesommen op het apparaat bij te werken moet u toestemming verlenen" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Geannuleerd" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Gewijzigd" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Wist de resultaten van de laatste update" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "De opdracht kon niet worden gevonden" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU-hulpmiddel" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Foutopsporingsopties" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Bezig met uitpakken..." + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Apparaat toegevoegd:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Apparaat gewijzigd:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Apparaat verwijderd:" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Afgerond!" + +#. 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 "Bezig met downgraden van %s van %s naar %s..." + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Afsluiten na een korte vertraging" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Afsluiten nadat de engine geladen is" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Het doorvoeren van argumenten is mislukt" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Firmware Update D-Bus-dienst" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Firmware Update Daemon" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Firmware-hulpmiddel" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Gevonden" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Alle apparaten verkrijgen die firmware-updates ondersteunen" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Verkrijgt details over een firmware-bestand" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Verkrijgt een lijst van updates voor verbonden hardware" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Verkrijgt de resultaten van de laatste update" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Slaapt..." + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Een firmware-bestand op deze hardware installeren" + +msgid "Install signed device firmware" +msgstr "Ondertekende apparaatfirmware installeren" + +msgid "Install signed system firmware" +msgstr "Ondertekende systeemfirmware installeren" + +msgid "Install unsigned device firmware" +msgstr "Niet-ondertekende apparaatfirmware installeren" + +msgid "Install unsigned system firmware" +msgstr "Niet-ondertekende systeemfirmware installeren" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Bezig met laden..." + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "De achtergrondservice controleren op gebeurtenissen" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Geen hardware aangetroffen die in staat is firmware bij te werken" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Oké" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Firmware van het apparaat uitlezen naar een bestand" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Firmware van één partitie uitlezen naar een bestand" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Metadata verversen vanuit externe server" + +#. 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 "Bezig met herinstalleren van %s met %s..." + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Gegevens vervangen in een bestaand firmwarebestand" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Bezig met herstarten van apparaat..." + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "De installatie inplannen voor de volgende herstart, indien mogelijk" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Bezig met inplannen..." + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Foutopsporingsopties weergeven" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Extra foutopsporingsinformatie weergeven" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Onbekend" + +msgid "Unlock the device to allow access" +msgstr "Ontgrendel het apparaat om toegang te verlenen" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Ontgrendelt het apparaat voor firmware-toegang" + +msgid "Update the stored device verification information" +msgstr "Opgeslagen apparaatverificatie-informatie bijwerken" + +#. 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 "Bezig met bijwerken van %s van %s naar %s..." + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Bezig met valideren..." + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Firmware van een bestand naar een apparaat schrijven" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Firmware van een bestand naar één partitie schrijven" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Bezig met schrijven..." diff --git a/fwupd-1.8.6/po/oc.po b/fwupd-1.8.6/po/oc.po new file mode 100644 index 0000000000000000000000000000000000000000..1bb36d23d1b439ca93940b0aef1c9418f7bd6a58 --- /dev/null +++ b/fwupd-1.8.6/po/oc.po @@ -0,0 +1,138 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Cédric Valmary , 2016 +# Cédric Valmary , 2016 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Occitan (post 1500) (http://www.transifex.com/freedesktop/fwupd/language/oc/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: oc\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Added" +msgstr "Apondut" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Aliàs de %s" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Anullat" + +#. TRANSLATORS: this is when a device is hotplugged +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Cambiat" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Soma de contraròtle" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Comanda pas trobada" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opcions de desbugatge" + +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descripcion" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Acabat !" + +#. 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 "Retrogradacion de %s de %s en %s " + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Quitar aprèp un brèu relambi" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Quitar aprèp lo cargament del motor" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Fracàs de l'analisi dels paramètres" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Servici D-Bus de mesa a jorn dels micrologicials" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Trobat" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obténer la lista dels periferics que supòrtan las mesas a jorn de micrologicial" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obténer los detalhs d'un fichièr de micrologicial" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Installar un fichièr de micrologicial sus aqueste material" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Cap de material amb de capacitats de mesa a jorn del micrologicial es pas estat detectat" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "D'acòrdi" + +#. 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 "Reïnstallacion de %s en %s " + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Removed" +msgstr "Suprimit" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostrar las opcions de desbugatge" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mòstra d'informacions de desbugatge complementàrias" + +#. 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 "Mesa a jorn de %s de %s en %s " + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" diff --git a/fwupd-1.8.6/po/pa.po b/fwupd-1.8.6/po/pa.po new file mode 100644 index 0000000000000000000000000000000000000000..ed5dcec3abb7ae6e9cd82cd915d8167c888387b0 --- /dev/null +++ b/fwupd-1.8.6/po/pa.po @@ -0,0 +1,284 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# A S Alam, 2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Panjabi (Punjabi) (http://www.transifex.com/freedesktop/fwupd/language/pa/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pa\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s ਸੰਰਚਨਾ ਅੱਪਡੇਟ" + +#. TRANSLATORS: 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 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: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u ਮਿੰਟ" +msgstr[1] "%uਮਿੰਟ" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u ਸਕਿੰਟ" +msgstr[1] "%u ਸਕਿੰਟ" + +msgid "Activate the new firmware on the device" +msgstr "ਡਿਵਾਈਸ ਉੱਤੇ ਨਵਾਂ ਫਿਰਮਵੇਅਰ ਸਰਗਰਮ ਕਰੋ" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਲਾਗੂ ਕਰੋ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "ਹਟਾਉਣਯੋਗ ਡਿਵਾਈਸ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਡਾਊਨਗਰੇਡ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "ਇਸ ਮਸ਼ੀਨ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਨੂੰ ਡਾਊਨਗਰੇਡ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "ਡਿਵਾਈਸ ਨੂੰ ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "ਹਟਾਉਣਯੋਗ ਡਿਵਾਈਸ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "ਇਸ ਮਸ਼ੀਨ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" + +msgid "BYTES" +msgstr "ਬਾਈਟ" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "ਕਮਾਂਡ ਨਹੀਂ ਲੱਭੀ" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "ਬਣਾਇਆ" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "ਗੰਭੀਰ" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "ਵਰਣਨ" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "ਵੇਰਵੇ" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "ਡਿਵਾਈਸ ID" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +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: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "ਫਾਇਲ-ਨਾਂ" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "ਫਾਇਲ-ਦਾ-ਨਾਂ" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "ਵੱਧ" + +msgid "Install old version of signed system firmware" +msgstr "ਸਾਈਨ ਕੀਤੇ ਸਿਸਟਮ ਫਿਰਮਵੇਅਰ ਦਾ ਪੁਰਾਣਾ ਵਰਜ਼ਨ ਇੰਸਟਾਲ ਕਰੋ" + +msgid "Install old version of unsigned system firmware" +msgstr "ਬਿਨ-ਸਾਈਨ ਕੀਤੇ ਸਿਸਟਮ ਫਿਰਮਵੇਅਰ ਦਾ ਪੁਰਾਣਾ ਵਰਜ਼ਨ ਇੰਸਟਾਲ ਕਰੋ" + +msgid "Install signed device firmware" +msgstr "ਸਾਈਨ ਕੀਤਾ ਡਿਵਾਈਸ ਫਿਰਮਵੇਅਰ ਇੰਸਟਾਲ ਕਰੋ" + +msgid "Install signed system firmware" +msgstr "ਸਾਈਨ ਕੀਤਾ ਸਿਸਟਮ ਫਿਰਮਵੇਅਰ ਇੰਸਟਾਲ ਕਰੋ" + +msgid "Install unsigned device firmware" +msgstr "ਬਿਨ-ਸਾਈਨ ਕੀਤਾ ਡਿਵਾਈਸ ਫਿਰਮਵੇਅਰ ਇੰਸਟਾਲ ਕਰੋ" + +msgid "Install unsigned system firmware" +msgstr "ਬਿਨਾਂ-ਸਾਈਨ ਕੀਤਾ ਸਿਸਟਮ ਫਿਰਮਵੇਅਰ ਇੰਸਟਾਲ ਕਰੋ" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "ਮਸਲਾ" +msgstr[1] "ਮਸਲੇ" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "ਲਸੰਸ" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "ਲੀਨਕਸ ਵੇਂਡਰ ਫਿਰਮਵੇਅਰ ਸਰਵਿਸ (ਸਟੇਬਲ ਫਿਰਮਵੇਅਰ)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "ਲੀਨਕਸ ਵੇਂਡਰ ਫਿਰਮਵੇਅਰ ਸਰਵਿਸ (ਟੈਸਟਿੰਗ ਫਿਰਮਵੇਅਰ)" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "ਘੱਟ" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "ਠੀਕ-ਠਾਕ" + +msgid "Modify daemon configuration" +msgstr "ਡੈਮਨ ਸੰਰਚਨਾ ਸੋਧੋ" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "ਮੁੜ-ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "ਨਵਾਂ ਵਰਜ਼ਨ" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "ਪਾਸਵਰਡ" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "ਪ੍ਰੋਪ੍ਰੇਟਰੀ" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "ਡਿਵਾਈਸ ਲਈ ਫਿਰਮਵੇਅਰ ਨੂੰ ਫਾਇਲ ਤੋਂ ਪੜ੍ਹੋ" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "ਹੁਣੇ ਮੁੜ-ਚਾਲੂ ਕਰਨਾ ਹੈ?" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "ਸੁਰੱਖਿਅਤ ਬੂਟ ਅਸਮਰੱਥ ਹੈ" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "ਸੁਰੱਖਿਅਤ ਬੂਟ ਸਮਰੱਥ ਹੈ" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "ਆਕਾਰ" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "ਸਰੋਤ" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "ਸਫ਼ਲ" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "ਸਾਰ" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "ਸਿਸਟਮ ਨੂੰ ਬਾਹਰੀ ਪਾਵਰ ਸਰੋਤ ਚਾਹੀਦਾ ਹੈ" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "ਟੈਗ" +msgstr[1] "ਟੈਗ" + +#. 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: remote type, e.g. remote or local +msgid "Type" +msgstr "ਕਿਸਮ" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI ਫਿਰਮਵੇਅਰ ਸਹੂਲਤ" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "ਅਣਪਛਾਤਾ ਡਿਵਾਈਸ" + +msgid "Unlock the device to allow access" +msgstr "ਪਹੁੰਚ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਲਈ ਡਿਵਾਈਸ ਅਣ-ਲਾਕ ਕਰੋ" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "ਅੱਪਡੇਟ ਲਈ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ" + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "ਜ਼ਰੂਰਤ" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "ਵਰਤੋਂਕਾਰ-ਨਾਂ" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "ਵੇਂਡਰ" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "ਵਰਜ਼ਨ" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "ਮੂਲ" diff --git a/fwupd-1.8.6/po/pl.po b/fwupd-1.8.6/po/pl.po new file mode 100644 index 0000000000000000000000000000000000000000..4d2d840c0582146198b74a7ebbcf82b252f0883d --- /dev/null +++ b/fwupd-1.8.6/po/pl.po @@ -0,0 +1,2995 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Piotr Drąg , 2015-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" + +#. TRANSLATORS: 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: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Aktualizacja BMC %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Aktualizacja akumulatora %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Aktualizacja mikrokodu procesora %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Aktualizacja aparatu %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Aktualizacja konfiguracji urządzenia %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "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: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Aktualizacja ekranu %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "Aktualizacja dysku %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: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "Aktualizacja dysku półprzewodnikowego %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Aktualizacja klawiatury %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: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Aktualizacja myszy %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Aktualizacja interfejsu sieciowego %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "Aktualizacja dysku SSD %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Aktualizacja kontrolera pamięci masowej %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: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Aktualizacja TPM %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Aktualizacja kontrolera Thunderbolt %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Aktualizacja panelu dotykowego %s" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "Aktualizacja odbiornika USB %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Aktualizacja urządzenia %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s i wszystkie podłączone urządzenia nie będą mogły być używane podczas aktualizacji." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "„%s” pojawiło się: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "„%s” zmieniło się: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "„%s” zniknęło: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "Nie można obecnie zaktualizować urządzenia %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Tryb serwisowy %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "Urządzenie %s musi być podłączone podczas trwania aktualizacji, aby uniknąć uszkodzenia." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "Urządzenie %s musi być podłączone do prądu podczas trwania aktualizacji, aby uniknąć uszkodzenia." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Obejście %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Wersja %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: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u urządzenie ma dostępną aktualizację oprogramowania sprzętowego." +msgstr[1] "%u urządzenia mają dostępną aktualizację oprogramowania sprzętowego." +msgstr[2] "%u urządzeń ma dostępną aktualizację oprogramowania sprzętowego." +msgstr[3] "%u urządzeń ma dostępną aktualizację oprogramowania sprzętowego." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u urządzenie nie ma najlepszej znanej konfiguracji." +msgstr[1] "%u urządzenia nie mają najlepszej znanej konfiguracji." +msgstr[2] "%u urządzeń nie ma najlepszej znanej konfiguracji." +msgstr[3] "%u urządzeń nie ma najlepszej znanej konfiguracji." + +#. 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: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "Obsługiwane jest %u lokalne urządzenie" +msgstr[1] "Obsługiwane są %u lokalne urządzenia" +msgstr[2] "Obsługiwanych jest %u lokalnych urządzeń" +msgstr[3] "Obsługiwanych jest %u lokalnych urządzeń" + +#. 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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (próg to %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(przestarzałe)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "PCR TPM ma teraz nieprawidłową wartość" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "Ochrona przed odtwarzaniem oprogramowania sprzętowego AMD" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "Ochrona przed zapisem oprogramowania sprzętowego AMD" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "Ochrona przed wycofaniem AMD" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Wymagane działanie:" + +#. 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: the age of the metadata +msgid "Age" +msgstr "Wiek" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Wyrazić zgodę i włączyć repozytorium?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias do „%s”" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Wszystkie PCR TPM są teraz prawidłowe" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Wszystkie PCR TPM są prawidłowe" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Wszystkie urządzenia tego samego typu zostaną zaktualizowane w tym samym czasie" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Umożliwia instalowanie poprzednich wersji oprogramowania sprzętowego" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Umożliwia ponowne instalowanie istniejących wersji oprogramowania sprzętowego" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Umożliwia przełączanie gałęzi oprogramowania sprzętowego" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Alternatywna gałąź" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Zastosowuje aktualizacje oprogramowania sprzętowego" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Zastosowuje aktualizację nawet, jeśli nie jest zalecana" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Zastosowuje pliki aktualizacji" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Zastosowywanie aktualizacji…" + +#. 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: stop nagging the user +msgid "Ask again next time?" +msgstr "Zapytać ponownie następnym razem?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Podłącza do trybu oprogramowania sprzętowego" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Uwierzytelnianie…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Wymagane są informacje o uwierzytelnieniu" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Wymagane jest uwierzytelnienie, aby zainstalować poprzednią wersję oprogramowania sprzętowego urządzenia wymiennego" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Wymagane jest uwierzytelnienie, aby zainstalować poprzednią wersję oprogramowania sprzętowego tego komputera" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Wymagane jest uwierzytelnienie, aby zmodyfikować ustawienia BIOS-u" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +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 read BIOS settings" +msgstr "Wymagane jest uwierzytelnienie, aby odczytać ustawienia BIOS-u" + +#. 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" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Wymagane jest uwierzytelnienie, aby zaktualizować oprogramowanie sprzętowe wymiennego urządzenia" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Wymagane jest uwierzytelnienie, aby zaktualizować oprogramowanie sprzętowe tego komputera" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Wymagane jest uwierzytelnienie, aby zaktualizować przechowywane sumy kontrolne dla urządzenia" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatyczne zgłaszanie" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Automatycznie wysyłać za każdym razem?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "Aktualizacje BIOS-u dostarczane przez usługę LVFS lub Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML NAZWA-PLIKU-DOCELOWEGO" + +msgid "BYTES" +msgstr "BAJTY" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Akumulator" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Dowiązuje nowy sterownik jądra" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Zablokowane pliki oprogramowania sprzętowego:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Zablokowana wersja" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blokowanie oprogramowania sprzętowego:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blokuje możliwość instalacji podanego oprogramowania sprzętowego" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Wersja programu startowego" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Gałąź" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Buduje plik oprogramowania sprzętowego" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Anuluj" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Anulowano" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Nie można zastosować, ponieważ aktualizacja bazy dbx została już zastosowana." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Nie można stosować aktualizacji na nośnikach typu Live" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Zmieniono" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Sprawdza, czy kryptograficzna suma kontrolna pasuje do oprogramowania sprzętowego" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Suma kontrolna" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Wybór gałęzi:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Wybór urządzenia:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Wybór typu oprogramowania sprzętowego:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Wybór wydania:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Proszę wybrać wolumin:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose the ESP:" +msgstr "Wybór ESP:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Usuwa wyniki z ostatniej aktualizacji" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Nie odnaleziono polecenia" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Wspierane przez społeczność" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Proponowana zmiana konfiguracji" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "Tylko administrator komputera może odczytywać konfigurację" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Konwertuje plik oprogramowania sprzętowego" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Utworzono" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Krytyczna" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Dostępne jest sprawdzenie poprawności kryptograficznej sumy kontrolnej" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Obecna wartość" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Obecna wersja" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "IDENTYFIKATOR-URZĄDZENIA|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Narzędzie DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opcje debugowania" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Dekompresowanie…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Opis" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Odłącza do trybu programu startowego" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Informacje" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Odejść od najlepszej znanej konfiguracji?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Flagi urządzenia" + +#. 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:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Poziom naładowania akumulatora urządzenia jest za niski" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Poziom naładowania akumulatora urządzenia jest za niski (%u%%, wymaga %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Urządzenie może przywrócić się po niepowodzeniu wgrywania" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Nie można używać urządzenia, kiedy pokrywa jest zamknięta" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Zmieniono urządzenie:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Oprogramowanie sprzętowe urządzenia musi mieć test wersji" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Urządzenie jest emulowane" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Urządzenie jest zablokowane" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Urządzenie musi zainstalować wszystkie podane wydania" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Nie można komunikować się z urządzeniem" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Nie można komunikować się z urządzeniem lub jest poza zasięgiem" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Urządzenie może być używane podczas trwania aktualizacji" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Urządzenie oczekuje na zastosowanie aktualizacji" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Usunięto urządzenie:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Urządzenie wymaga podłączenia do prądu" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Urządzenie przygotowuje aktualizacje" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Urządzenie obsługuje przełączanie na inną gałąź oprogramowania sprzętowego" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Metoda aktualizacji urządzenia" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Aktualizacja urządzenia wymaga aktywacji" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Urządzenie wykona kopię zapasową oprogramowania sprzętowego przed instalacją" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Urządzenie nie pojawi się z powrotem po ukończeniu aktualizacji" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Pomyślnie zaktualizowane urządzenia:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Urządzenia, które nie zostały poprawnie zaktualizowane:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Urządzenia bez dostępnych aktualizacji oprogramowania sprzętowego: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Urządzenia z najnowszą dostępną wersją oprogramowania sprzętowego:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Nie odnaleziono żadnych urządzeń o pasujących GUID" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Wyłączone" + +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Bez sprawdzania niezgłoszonej historii" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Bez sprawdzania, czy repozytoria pobierania mają być włączone" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Bez sprawdzania ani pytania o ponowne uruchomienie po aktualizacji" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Bez dołączania przedrostka domeny dziennika" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Bez dołączania przedrostka czasu" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Bez wykonywania testów bezpieczeństwa urządzenia" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Bez pytania o urządzenia" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Bez zapisywania do bazy danych historii" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Czy rozumiesz konsekwencje zmiany gałęzi oprogramowania sprzętowego?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Wyłączyć tę funkcję dla przyszłych aktualizacji?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Odświeżyć to repozytorium teraz?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Automatycznie wysyłać zgłoszenia dla przyszłych aktualizacji?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Bez pytania o uwierzytelnienie (może być wyświetlanych mniej informacji)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Gotowe." + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Zainstalować poprzednią wersję urządzenia %s z %s do %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Instaluje poprzednią wersję oprogramowania sprzętowego urządzenia" + +#. 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 "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: command description +msgid "Download a file" +msgstr "Pobiera plik" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Pobieranie…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Zrzuca dane SMBIOS z pliku" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Czas trwania" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Podana partycja ESP nie jest prawidłowa" + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Emulowany komputer" + +msgid "Enable" +msgstr "Włącz" + +#. 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Włączyć nowe repozytorium?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Włączyć to repozytorium?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Włączone" + +msgid "Enabled fwupdate debugging" +msgstr "Włączono debugowanie fwupdate" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Włączone, jeśli sprzęt pasuje" + +#. 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." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Zaszyfrowane" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Zaszyfrowana pamięć RAM" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Niewspierane" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Wyliczenie" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Usuwa całą historię aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Usuwanie zawartości…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Kończy działanie po małym opóźnieniu" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Kończy działanie po wczytaniu mechanizmu" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Eksportuje strukturę pliku oprogramowania sprzętowego do pliku XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Rozpakowuje zamknięte oprogramowanie sprzętowe do obrazów" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "PLIK" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "PLIK [IDENTYFIKATOR-URZĄDZENIA|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NAZWA-PLIKU" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "NAZWA-PLIKU CERTYFIKAT KLUCZ-PRYWATNY" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NAZWA-PLIKU ALTERNATYWNA-NAZWA-URZĄDZENIA|ALTERNATYWNY-IDENTYFIKATOR-URZĄDZENIA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NAZWA-PLIKU ALTERNATYWNA-NAZWA-URZĄDZENIA|ALTERNATYWNY-IDENTYFIKATOR-URZĄDZENIA [ALTERNATYWNA-NAZWA-OBRAZU|ALTERNATYWNY-IDENTYFIKATOR-OBRAZU]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NAZWA-PLIKU IDENTYFIKATOR-URZĄDZENIA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "NAZWA-PLIKU WYRÓWNANIE DANE [TYP-OPROGRAMOWANIA-SPRZĘTOWEGO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NAZWA-PLIKU [IDENTYFIKATOR-URZĄDZENIA|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NAZWA-PLIKU [TYP-OPROGRAMOWANIA-SPRZĘTOWEGO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NAZWA-PLIKU-ŹRÓDŁOWEGO NAZWA-PLIKU-DOCELOWEGO [TYP-ŹRÓDŁOWEGO-OPROGRAMOWANIA-SPRZĘTOWEGO] [TYP-DOCELOWEGO-OPROGRAMOWANIA-SPRZĘTOWEGO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "NAZWA-PLIKU|SUMA-KONTROLNA1[,SUMA-KONTROLNA2][,SUMA-KONTROLNA3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Nie powiodło się" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Zastosowanie aktualizacji się nie powiodło" + +#. TRANSLATORS: error message for Windows +#, c-format +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Połączenie z usługą systemu Windows się nie powiodło, proszę się upewnić, że ona działa." + +#. 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: 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Wczytanie lokalnej bazy dbx się nie powiodło" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Wczytanie poprawek się nie powiodło" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Wczytanie systemowej bazy dbx się nie powiodło" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Zablokowanie się nie powiodło" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Przetworzenie parametrów się nie powiodło" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Przetworzenie pliku się nie powiodło" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Przetworzenie flag dla opcji --filter się nie powiodło" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Przetworzenie lokalnej bazy dbx 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Sprawdzenie poprawności zawartości ESP się nie powiodło" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Fałsz" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nazwa pliku" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Podpis nazwy pliku" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Źródło nazwy pliku" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Wymagana jest nazwa pliku" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtruje za pomocą zestawu flag urządzeń, przedrostek ~ wyklucza, np. „internal,~needs-reboot”" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Zaświadczenie oprogramowania sprzętowego" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Deskryptor BIOS-u oprogramowania sprzętowego" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Region BIOS-u oprogramowania sprzętowego" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Podstawowy adres URI oprogramowania sprzętowego" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Usługa D-Bus aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Usługa aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Weryfikacja aktualizatora oprogramowania sprzętowego" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Aktualizacje oprogramowania sprzętowego" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Narzędzie oprogramowania sprzętowego" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Ochrona przed zapisem oprogramowania sprzętowego" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Blokada ochrony przed zapisem oprogramowania sprzętowego" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Zaświadczenie oprogramowania sprzętowego" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Oprogramowanie sprzętowe jest już zablokowane" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Oprogramowanie nie jest już zablokowane" + +#. 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] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane przez %u dzień i mogą nie być aktualne." +msgstr[1] "Metadane oprogramowania sprzętowego nie zostały zaktualizowane przez %u dni i mogą nie być aktualne." +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." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Ostatnie odświeżenie metadanych oprogramowania sprzętowego: %s temu. Parametr --force odświeży ponownie." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Aktualizacje oprogramowania sprzętowego" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Aktualizacje oprogramowania sprzętowego są wyłączone; wykonanie polecenia „fwupdmgr unlock” je włączy" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Flagi" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Wymusza działanie przez rozluźnienie części testów uruchamiania" + +msgid "Force the action ignoring all warnings" +msgstr "Wymusza działanie ignorując wszystkie ostrzeżenia" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Odnaleziono" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Wykryto pełne szyfrowanie dysku" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Hasła pełnego szyfrowania dysku mogą zostać unieważnione podczas aktualizacji" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Platforma z bezpiecznikiem" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Platforma z bezpiecznikiem" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" +msgstr[2] "GUID" +msgstr[3] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Uzyskanie ustawień BIOS-u" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Uzyskuje wszystkie flagi urządzeń obsługiwane przez usługę fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Uzyskuje wszystkie urządzenia obsługujące aktualizacje oprogramowania sprzętowego" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Uzyskuje wszystkie włączone wtyczki zarejestrowane na komputerze" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Uzyskuje informacje o pliku oprogramowania sprzętowego" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Uzyskuje skonfigurowane repozytoria" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Uzyskuje atrybuty zabezpieczeń komputera" + +#. 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 blocked firmware" +msgstr "Uzyskuje listę zablokowanego oprogramowania sprzętowego" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Uzyskuje listę aktualizacji dla podłączonego sprzętu" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Uzyskuje wydania dla urządzenia" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Uzyskuje wyniki z ostatniej aktualizacji" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "PLIK-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Sprzęt czeka na ponowne podłączenie" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Wysoka" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Zdarzenia zabezpieczeń komputera" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "Identyfikator zabezpieczeń komputera (HSI) jest nieobsługiwany" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Identyfikator zabezpieczeń komputera:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "Ochrona IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Wyłączono ochronę urządzenia IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Włączono ochronę urządzenia IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Bezczynne…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignoruje ścisłe testy SSL podczas pobierania plików" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignoruje niepowodzenia sum kontrolnych oprogramowania sprzętowego" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignoruje niepowodzenia zgodności sprzętu z oprogramowaniem sprzętowym" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignoruje testy bezpieczeństwa i sprawdzanie poprawności" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorowanie ścisłych testów SSL, aby robić to automatycznie w przyszłości, należy wyeksportować zmienną DISABLE_SSL_STRICT w środowisku" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Czas trwania instalacji" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instaluje zamknięte oprogramowanie sprzętowe na urządzeniu" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instaluje plik oprogramowania sprzętowego na tym sprzęcie" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Instaluje podane oprogramowanie sprzętowe na urządzeniu, wszystkie możliwe urządzenia także zostaną zainstalowane, jeśli CAB pasuje" + +msgid "Install old version of signed system firmware" +msgstr "Instalacja poprzedniej wersji podpisanego oprogramowania sprzętowego komputera" + +msgid "Install old version of unsigned system firmware" +msgstr "Instalacja poprzedniej wersji niepodpisanego oprogramowania sprzętowego komputera" + +msgid "Install signed device firmware" +msgstr "Instalacja podpisanego oprogramowania sprzętowego urządzenia" + +msgid "Install signed system firmware" +msgstr "Instalacja podpisanego oprogramowania sprzętowego komputera" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instaluje najpierw na urządzeniu nadrzędnym" + +msgid "Install unsigned device firmware" +msgstr "Instalacja niepodpisanego oprogramowania sprzętowego urządzenia" + +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…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Zainstalowanie tej aktualizacji może także spowodować utratę wszelkiej gwarancji na urządzenie." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Liczba stała" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "ACM chronione przez Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM chronione przez Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Zasady postępowania Intel BootGuard w przypadku błędów" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Bezpiecznik Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Bezpiecznik OTP Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Uruchamianie zweryfikowane przez Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Zasady postępowania Intel BootGuard w przypadku błędów" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Uruchamianie zweryfikowane przez Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET jest aktywne" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET jest włączone" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Tryb serwisowy Intel Management Engine" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Obejście Intel Management Engine" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Wersja Intel Management Engine" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Wewnętrzne urządzenie" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Nieprawidłowe" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Nieprawidłowe parametry" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Jest instalacją poprzedniej wersji" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Jest w trybie programu startowego" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Jest aktualizacją" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Błąd" +msgstr[1] "Błędy" +msgstr[2] "Błędy" +msgstr[3] "Błędy" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "KLUCZ,WARTOŚĆ" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Jądro nie jest już skażone" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Jądro jest skażone" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Wyłączono blokadę jądra" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Włączono blokadę jądra" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Baza kluczy" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "POŁOŻENIE" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Ostatnia modyfikacja" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Pozostała mniej niż jedna minuta" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licencja" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Blokada jądra Linux" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Weryfikacja jądra Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Obszar wymiany systemu Linux" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (stabilne oprogramowanie sprzętowe)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (testowe oprogramowanie sprzętowe)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Jądro Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Blokada jądra Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Obszar wymiany systemu Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Wyświetla listę wpisów w bazie dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Wyświetla listę obsługiwanych aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Wyświetla listę dostępnych typów oprogramowania sprzętowego" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Wyświetla listę plików na ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Wczytywanie…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Zablokowane" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Niska" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Tryb serwisowy MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Obejście MEI" + +msgid "MEI version" +msgstr "Wersja MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Ręcznie włącza podane wtyczki" + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Maksymalna długość" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Maksymalna wartość" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Średnia" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Podpis metadanych" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Adres URI metadanych" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadane można uzyskać z serwisu Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimalna wersja" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Minimalna długość" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Minimalna wartość" + +#. 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" + +#. 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" + +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" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Montuje MSP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "UWAGA: ten program może działać poprawnie tylko jako root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Wymaga ponownego uruchomienia po instalacji" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Wymaga ponownego uruchomienia" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Wymaga wyłączenia komputera po instalacji" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nowa wersja" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nie podano działania." + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Brak poprzednich wersji dla urządzenia %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nie odnaleziono identyfikatorów oprogramowania sprzętowego" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nie wykryto sprzętu z możliwością aktualizacji jego oprogramowania" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nie odnaleziono wtyczek" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Brak dostępnych wydań" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Żadne repozytoria nie są obecnie włączone, więc żadne metadane nie są dostępne." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Brak dostępnych repozytoriów" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Brak urządzeń, które można aktualizować" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Brak dostępnych aktualizacji" + +msgid "No updates available for remaining devices" +msgstr "Brak dostępnych aktualizacji dla pozostałych urządzeń" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nie zastosowano żadnych aktualizacji" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Niezatwierdzone" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "Dostarczono za mało danych, aby wykonać obliczenie HSI." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Nie odnaleziono" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Nieobsługiwane" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Wyświetla tylko jedną wartość PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Używa IPFS tylko podczas pobierania plików" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Dozwolone są tylko aktualizacje wersji" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Wyjście w formacie JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Zastępuje domyślną ścieżkę ESP" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "ŚCIEŻKA" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Przetwarza i wyświetla informacje o pliku oprogramowania sprzętowego" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Przetwarzanie aktualizacji bazy dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Przetwarzanie systemowej bazy dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Hasło" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Łata zamknięte oprogramowanie sprzętowe pod znanym wyrównaniem" + +msgid "Payload" +msgstr "Dane" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Oczekujące" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Procent ukończenia" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Wykonać działanie?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Debugowanie platformy" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Przed kontynuacją proszę się upewnić, że posiadany jest klucz odzyskiwania woluminu." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Proszę podać liczbę od 0 do %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Brak zależności wtyczki" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Możliwe wartości" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "Ochrona DMA przed uruchomieniem" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Ochrona DMA przed uruchomieniem" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Ochrona DMA przed uruchomieniem jest wyłączona" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Ochrona DMA przed uruchomieniem jest włączona" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Poprzednia wersja" + +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" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problemy" + +msgid "Proceed with upload?" +msgstr "Kontynuować wysyłanie?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Testy zabezpieczeń procesora" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Własnościowe" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Odpytuje obsługę aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "IDENTYFIKATOR-REPOZYTORIUM" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "IDENTYFIKATOR-REPOZYTORIUM KLUCZ WARTOŚĆ" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Tylko do odczytu" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Odczytuje zamknięte oprogramowanie sprzętowe z urządzenia" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Odczytuje oprogramowanie sprzętowe z urządzenia" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Odczytuje oprogramowanie sprzętowe z urządzenia do pliku" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Odczytuje oprogramowanie sprzętowe z jednej partycji do pliku" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Odczytywanie z urządzenia %s…" + +#. TRANSLATORS: reading from the flash chips +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" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Ponownie zainstalować urządzenie %s do wersji %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Ponownie instaluje obecne oprogramowanie sprzętowe na urządzeniu" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Ponownie instaluje oprogramowanie sprzętowe na urządzeniu" + +#. 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 "Ponowne instalowanie %s za pomocą %s… " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Gałąź wydania" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Flagi wydania" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Identyfikator wydania" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Identyfikator repozytorium" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Zastępuje dane w istniejącym pliku oprogramowania sprzętowego" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Adres URI zgłoszenia" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Zgłoszone do zdalnego serwera" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Anulowano żądanie" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Nie odnaleziono wymaganego systemu plików „efivarfs”" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Nie odnaleziono wymaganego oprogramowania sprzętowego" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Wymaga programu startowego" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Wymaga połączenia z Internetem" + +#. TRANSLATORS: reboot to apply the update +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…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Pobiera ustawienia BIOS-u. Jeśli żadne parametry nie zostaną przekazane, zwracane są wszystkie ustawienia" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Zwraca wszystkie identyfikatory sprzętu dla komputera" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Ochrona przed wycofaniem" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Polecenie „fwupdmgr get-upgrades” wyświetli więcej informacji." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Polecenie „fwupdmgr sync-bkc” dokończy to działanie." + +#. 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”" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Wykonanie bez „%s” wyświetli" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Działające jądro jest za stare" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Przyrostek uruchamiania" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "USTAWIENIE WARTOŚĆ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "USTAWIENIE1 WARTOŚĆ1 [USTAWIENIE2] [WARTOŚĆ2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Deskryptor BIOS-u SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Region BIOS-u SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Blokada SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Ochrona przed odtwarzaniem SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Zapis SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Ochrona przed zapisem SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "PODSYSTEM STEROWNIK [IDENTYFIKATOR-URZĄDZENIA|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Zapisuje plik umożliwiający utworzenie identyfikatorów sprzętu" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Przyrost skalarny" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Planuje instalację podczas następnego ponownego uruchomienia" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Planowanie…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Wyłączono Secure Boot" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Włączono Secure Boot" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "%s zawiera więcej informacji." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "%s zawiera więcej informacji." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Wybrane urządzenie" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Wybrany wolumin" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Numer seryjny" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Ustawienie ustawienia BIOS-u „%s” na „%s”." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Ustawia ustawienie BIOS-u" + +msgid "Set one or more BIOS settings" +msgstr "Ustawienie co najmniej jednego ustawienia BIOS-u" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Ustawia flagę debugowania podczas aktualizacji" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Ustawia co najmniej jedno ustawienie BIOS-u" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Ustawienie listy zatwierdzonego oprogramowania sprzętowego" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Typ ustawienia" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Ustawienia zostaną zastosowane po ponownym uruchomieniu komputera" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Udostępnia historię oprogramowania sprzętowego programistom" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Wyświetla wszystkie wyniki" + +#. TRANSLATORS: command line option +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 domains" +msgstr "Wyświetla informacje o debugowaniu dla wszystkich domen" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Wyświetla opcje debugowania" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Wyświetla urządzenia, których nie można aktualizować" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Wyświetla dodatkowe informacje o debugowaniu" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Wyświetla historię aktualizacji oprogramowania sprzętowego" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Wyświetla więcej informacji o wtyczkach" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Wyświetla obliczoną wersję bazy dbx" + +#. 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Podpisuje oprogramowanie sprzętowe nowym kluczem" + +msgid "Sign data using the client certificate" +msgstr "Podpisanie danych za pomocą certyfikatu klienta" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Podpisane dane" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Rozmiar" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Część haseł platformy może zostać unieważnionych podczas aktualizowania tego oprogramowania sprzętowego." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Źródło" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Podaje identyfikatory dostawcy/produktu urządzenia DFU" + +#. TRANSLATORS: command line option +msgid "Specify a filename to use to save backend events" +msgstr "Podaje nazwę pliku używaną do zapisywania zdarzeń mechanizmu" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Podaje plik bazy dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Podaje liczbę bajtów na przesyłanie USB" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Ciąg" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Powodzenie" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Pomyślnie aktywowano wszystkie urządzenia" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Pomyślnie wyłączono repozytorium" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Pomyślnie pobrano nowe metadane: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Pomyślnie włączono i odświeżono repozytorium" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Pomyślnie włączono repozytorium" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Pomyślnie zainstalowano oprogramowanie sprzętowe" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Pomyślnie zmodyfikowano wartość konfiguracji" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Pomyślnie zmodyfikowano repozytorium" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Pomyślnie ręcznie odświeżono metadane" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Pomyślnie zaktualizowano sumy kontrolne urządzenia" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Pomyślnie wysłano %u zgłoszenie" +msgstr[1] "Pomyślnie wysłano %u zgłoszenia" +msgstr[2] "Pomyślnie wysłano %u zgłoszeń" +msgstr[3] "Pomyślnie wysłano %u zgłoszeń" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Pomyślnie sprawdzono poprawność sum kontrolnych urządzenia" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Podsumowanie" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Obsługiwane" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Obsługiwany procesor" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Obsługiwane na zdalnym serwerze" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Uśpienie do trybu bezczynności" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Uśpienie do pamięci RAM" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Uśpienie do trybu bezczynności" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Uśpienie do pamięci RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Przełączyć gałąź z %s na %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Przełącza gałąź oprogramowania sprzętowego na urządzeniu" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Synchronizuje wersje oprogramowania sprzętowego do najlepszej znanej konfiguracji komputera" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Poziom naładowania komputera jest za niski, aby wykonać aktualizację" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "Poziom naładowania komputera jest za niski, aby wykonać aktualizację (%u%%, wymaga %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Komputer wymaga zewnętrznego źródła zasilania" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEKST" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Rekonstrukcja PCR0 TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "Rekonstrukcja PCR0 TPM jest nieprawidłowa" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "Rekonstrukcja PCR0 TPM jest teraz prawidłowa" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "Konfiguracja platformy TPM" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "Rekonstrukcja TPM" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Puste PCR TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Etykieta" +msgstr[1] "Etykiety" +msgstr[2] "Etykiety" +msgstr[3] "Etykiety" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Skażone" + +msgid "Target" +msgstr "Cel" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testuje urządzenie za pomocą manifestu JSON" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "PCR0 TPM różni się od rekonstrukcji." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Usługa wczytała kod firmy trzeciej i nie jest już wspierana przez jej programistów!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Wersja urządzenia się nie zgadza: otrzymano %s, oczekiwano %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Oprogramowanie sprzętowe od firmy %s nie jest dostarczane przez firmę %s, dostawcę sprzętu." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Zegar komputera nie jest poprawnie ustawiony i pobieranie plików może się nie powieść." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Dostawca nie dostarczył żadnych informacji o wydaniu." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Urządzenia z problemami:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Nie ma zablokowanych plików oprogramowania sprzętowego" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nie ma zatwierdzonego oprogramowania sprzętowego." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "To urządzenie zostanie przywrócone do wersji %s po wykonaniu polecenia %s." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "To oprogramowanie sprzętowe jest dostarczane przez członków społeczności LVFS i nie jest dostarczane (ani wspierane) przez oryginalnego dostawcę sprzętu." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Ten pakiet nie został zweryfikowany, może nie działać poprawnie." + +#. 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 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: error message +msgid "This system doesn't support firmware settings" +msgstr "Ten komputer nie obsługuje ustawień oprogramowania sprzętowego" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Ten komputer ma problemy HSI uruchamiania." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Ten komputer ma niski poziom zabezpieczeń HSI." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "To narzędzie umożliwia administratorowi zastosowywanie aktualizacji bazy dbx dla UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "To narzędzie umożliwia administratorowi debugowanie działania UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "To narzędzie umożliwia administratorowi odpytywanie i sterowanie usługą fwupd, umożliwiając wykonywanie takich działań, jak instalowanie lub instalowanie poprzedniej wersji oprogramowania sprzętowego." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "To narzędzie umożliwia administratorowi używanie wtyczek fwupd bez ich instalacji na komputerze." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "To narzędzie może automatycznie zmienić ustawienie BIOS-u „%s” z „%s” na „%s”, ale zmiana będzie aktywna dopiero po ponownym uruchomieniu komputera." + +#. 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: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "To narzędzie odczyta i przetworzy dziennik zdarzeń TPM z oprogramowania sprzętowego komputera." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Aby zignorować to ostrzeżenie, należy użyć parametru --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Przejściowe niepowodzenie" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Prawda" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Zweryfikowane metadane" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Zweryfikowane dane" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Typ" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Nie wykryto lub nie skonfigurowano partycji ESP UEFI" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Narzędzie oprogramowania sprzętowego UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "Klucz platformy UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "Zabezpieczone uruchamianie UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Aktualizacje kapsułowe UEFI są niedostępne lub wyłączone w konfiguracji oprogramowania sprzętowego" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Narzędzie bazy dbx dla UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Nie można aktualizować oprogramowania sprzętowego UEFI w trybie przestarzałego BIOS-u" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Klucz platformy UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Zabezpieczone uruchamianie UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Nie można połączyć się z usługą" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Nie można odnaleźć atrybutu" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Odwiązuje bieżący sterownik" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Odblokowywanie oprogramowania sprzętowego:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Odblokowuje możliwość instalacji podanego oprogramowania sprzętowego" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Niezaszyfrowane" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Nieznane" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Nieznane urządzenie" + +msgid "Unlock the device to allow access" +msgstr "Odblokowanie urządzenia" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Odblokowane" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Odblokowuje urządzenie" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Odmontowuje ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Usuwa flagę debugowania podczas aktualizacji" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Niepodpisane dane" + +#. 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: Suffix: the HSI result +msgid "Untainted" +msgstr "Nieskażone" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Można aktualizować" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Błąd aktualizacji" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Ilustracja aktualizacji" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Komunikat aktualizacji" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stan aktualizacji" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Niepowodzenie aktualizacji to znany problem, pod tym adresem dostępnych jest więcej informacji:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Zaktualizować teraz?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Aktualizacja wymaga ponownego uruchomienia" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Aktualizuje przechowywaną kryptograficzną sumę kontrolną bieżącą zawartością pamięci ROM" + +msgid "Update the stored device verification information" +msgstr "Aktualizacja przechowywanych informacji o poprawności urządzenia" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Aktualizuje przechowywane metadane obecną zawartością" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Aktualizuje wszystkie podane urządzenia do najnowszej wersji oprogramowania sprzętowego, lub wszystkie urządzenia, jeśli nie podano żadnych" + +msgid "Updating" +msgstr "Aktualizowanie" + +#. 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 "Aktualizowanie %s z wersji %s do %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Aktualizowanie urządzenia %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Zaktualizować urządzenie %s z wersji %s na %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Wysłać zgłoszenie?" + +#. 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 "Wysyłanie zgłoszeń o oprogramowaniu sprzętowym pomaga dostawcom sprzętu szybko identyfikować nieudane i udane aktualizacje na prawdziwych urządzeniach." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Pilność" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Polecenie fwupdmgr --help wyświetli pomoc" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Polecenie fwupdtool --help wyświetli pomoc" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Używa flag poprawek podczas instalowania oprogramowania sprzętowego" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Użytkownik został powiadomiony" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nazwa użytkownika" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Prawidłowe" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Sprawdzanie poprawności zawartości ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Wariant" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Dostawca" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Sprawdzanie poprawności…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Wersja" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "OSTRZEŻENIE:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Oczekiwanie…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Obserwuje zmiany sprzętu" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Zapisuje oprogramowanie sprzętowe z pliku na urządzenie" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Zapisuje oprogramowanie sprzętowe z pliku na jedną partycję" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Zapisywanie pliku:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Zapisywanie…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Należy się upewnić, że użytkownik jest w stanie przywrócić ustawienia w konfiguracji oprogramowania sprzętowego komputera, ponieważ ta zmiana może spowodować, że komputer nie będzie mógł uruchomić systemu Linux lub spowodować inny rodzaj niestabilności komputera." + +#. 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." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Sprzęt może zostać uszkodzony przez użycie tego oprogramowania sprzętowego, a zainstalowanie tego wydania może spowodować utratę gwarancji od firmy %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Komputer jest skonfigurowany według BKC %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[SUMA-KONTROLNA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[IDENTYFIKATOR-URZĄDZENIA|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[IDENTYFIKATOR-URZĄDZENIA|GUID] [GAŁĄŹ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[IDENTYFIKATOR-URZĄDZENIA|GUID] [WERSJA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[PLIK PODPIS_PLIKU IDENTYFIKATOR-REPOZYTORIUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NAZWA-PLIKU1] [NAZWA-PLIKU2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[USTAWIENIE1] [USTAWIENIE2]…" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[USTAWIENIE1] [USTAWIENIE2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[PLIK-SMBIOS|PLIK-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "domyślna" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Narzędzie dziennika zdarzeń TPM usługi fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Wtyczki usługi fwupd" diff --git a/fwupd-1.8.6/po/pt.po b/fwupd-1.8.6/po/pt.po new file mode 100644 index 0000000000000000000000000000000000000000..4d0aab8c67f3225d6ba79888f4bd66cbfe4025ad --- /dev/null +++ b/fwupd-1.8.6/po/pt.po @@ -0,0 +1,2271 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Hugo Carvalho , 2021 +# Peter J. Mello , 2020 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Portuguese (http://www.transifex.com/freedesktop/fwupd/language/pt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minuto restante" +msgstr[1] "%.0f minutos restantes" +msgstr[2] "%.0f minutos restantes" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Atualização da bateria para %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Atualização de microcódigo de CPU para %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Atualização de câmera para %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Atualização da configuração de %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Atualização consumidor-final de ME para %s" + +#. 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 de controlador para %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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Atualização do teclado para %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 de ME para %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Atualização do rato para %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Atualização da interface de rede %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Atualização do controlador de armazenamento %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: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Atualização de TPM para %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Atualização de controlador Thunderbolt para %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Atualização do touchpad para %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: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s e todos os dispositivos conectados não podem ser usados durante a atualização." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modo de fabricação de %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s deve permanecer conectado durante a atualização para evitar danos." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s deve permanecer conectado a uma fonte de energia durante a atualização para evitar danos." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "substituição de %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Versão %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dia" +msgstr[1] "%u dias" +msgstr[2] "%u dias" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] " %u dispositivo tem uma atualização de firmware disponível." +msgstr[1] "%u dispositivos têm uma atualização de firmware disponível." +msgstr[2] "%u dispositivos têm uma atualização de firmware disponível." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hora" +msgstr[1] "%u horas" +msgstr[2] "%u horas" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u dispositivo local suportado" +msgstr[1] "%u dispositivos locais suportados" +msgstr[2] "%u dispositivos locais suportados" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuto" +msgstr[1] "%u minutos" +msgstr[2] "%u minutos" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u segundo" +msgstr[1] "%u segundos" +msgstr[2] "%u segundos" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleto)" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Ação necessária:" + +#. 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: the age of the metadata +msgid "Age" +msgstr "Idade" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Aceitar e ativar o remoto?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Atalho para %s" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Todos os dispositivos do mesmo tipo serão atualizados ao mesmo tempo" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permite fazer downgrade de versões de firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permite reinstalar versões de firmware existentes" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Permite alternar a ramificação do firmware" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Uma atualização requer uma reinicialização para ser concluída." + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplica atualizações de firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplica a atualização mesmo quando não recomendado" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplica ficheiros de atualização" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aplicando atualização…" + +#. 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:" +msgstr[2] "Firmwares aprovados:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Perguntar novamente da próxima vez?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Modo anexar ao firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "A autenticar…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Os detalhes de autenticação são necessários" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Autenticação é necessária 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 "Autenticação é necessária 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 "Autenticação é necessária 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 "Autenticação é necessária 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 "Autenticação é necessária 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 "Autenticação é necessária para assinar dados usando o certificado do cliente" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Autenticação é necessária para alternar para nova versão de firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Autenticação é necessária para desbloquear um dispositivo" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Autenticação é necessária para atualizar o firmware em dispositivo removível" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Autenticação é necessária para atualizar o firmware nesta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Autenticação é necessária para atualizar as somas de verificação armazenadas para o dispositivo" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Relatórios automáticos" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Enviar automaticamente toda vez?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "XML-CONSTRUTOR NOME-DE-FICHEIRO-DEST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincula novo driver de kernel" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Arquivos de firmware bloqueados:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Bloqueando firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Impede que um firmware específico seja instalado" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versão do gestor de arranque" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Ramificação" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Compila um ficheiro de firmware" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancelar" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Cancelado" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Não foi possível aplicar, pois a atualização de dbx já foi aplicada." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Não é possível aplicar atualizações em uma mídia de instalação" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Alterado" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Verifica se o hash criptográfico corresponde ao firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Soma de verificação" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Escolha uma ramificação:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Escolha um dispositivo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Escolha um tipo de firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Escolha um lançamento:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Escolha um volume:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Limpa os resultados da última atualização" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Comando não encontrado" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Converte um ficheiro de firmware" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Criado" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Crítica" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "A verificação criptográfica de hash está disponível" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versão atual" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ID-DISPOSITIVO|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilitário DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opções de depuração" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "A descomprimir…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descrição" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Modo desanexar ao gestor de arranque" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detalhes" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Opções do dispositivo" + +#. 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:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "O dispositivo pode recuperar falhas de flashing" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositivo modificado:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Um firmware de dispositivo é necessário para ter uma verificação de versão" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "O dispositivo está bloqueado" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Um dispositivo é necessário para instalar todos os lançamentos fornecidos" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "O dispositivo está inalcançável" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "O dispositivo pode ser usado durante a atualização" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositivo removido:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Atualizações de estágios do dispositivo" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "O dispositivo tem suporte para alternar para uma ramificação diferente do firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Método de atualização do dispositivo" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "A atualização do dispositivo precisa de ativação" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "O dispositivo fará backup do firmware antes de instalar" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "O dispositivo não aparecerá novamente após a atualização ser concluída" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositivos que foram atualizados com sucesso:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Dispositivos que não foram atualizados com sucesso:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositivos com nenhuma atualização de firmware disponível: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositivos com a versão mais recente do firmware disponível:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Desabilitado" + +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Não verifica por histórico não relatado" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Não verifica se remotos para transferência devem estar ativados" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Não verifica nem solicita configuração para reiniciar após atualizar" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Não inclui prefixo de domínio de log" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Não inclui prefixo com marca de tempo" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Não realiza verificações de segurança de dispositivo" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Não escreve no banco de dados de histórico" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Entende as consequências de trocar a ramificação do firmware?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Deseja desativar este recurso para atualizações futuras?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Deseja atualizar este remoto agora?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Deseja enviar relatórios automaticamente para atualizações futuras?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Feito!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Fazer downgrade %s de %s para %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +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 "A reverter de %s de %s para %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "A reverter %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Transferir um ficheiro" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "A transferir…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Despeja dados SMBIOS a partir de um ficheiro" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Duração" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "A ESP especificada não era válida" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Ativa suporte a atualização de firmware em sistemas suportados" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Ativar novo remoto?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Ativar esse remoto?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Ativado" + +msgid "Enabled fwupdate debugging" +msgstr "Depuração de fwupdate habilitada" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Ativa 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 ativação desta funcionalidade é feita por sua conta e risco, o que significa que precisa de entrar em contato com o fabricante do equipamento original sobre quaisquer problemas causados por estas atualizações. Apenas 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 ativação deste remoto é feito a seu próprio custo e risco." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Criptografado" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM criptografada" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Apaga todo histórico de atualização de firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "A apagar…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Sair após pequeno atraso" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Sair após o carregamento do motor" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Exporta a estrutura de ficheiro de um firmware para XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extrai um blob de firmware para imagens" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FICHEIRO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FICHEIRO [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NOMEDOFICHEIRO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "NOME-DE-FICHEIRO CERTIFICADO CHAVE-PRIVADA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NOME-DE-FICHEIRO NOME-ALT-DISPOSITIVO|ID-ALT-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NOME-DE-FICHEIRO NOME-ALT-DISPOSITIVO|ID-ALT-DISPOSITIVO [NOME-ALT-IMAGEM|ID-ALT-IMAGEM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NOME-DE-FICHEIRO ID-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NOME-DE-FICHEIRO [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NOME-DE-FICHEIRO [TIPO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NOME-DE-FICHEIRO-ORIG NOME-DE-FICHEIRO-DEST [TIPO-FIRMWARE-ORIG] [TIPO-FIRMWARE-DEST]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Falhou" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Falha ao aplicar a atualização" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Falha ao se ligar ao daemon" + +#. 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Falha ao carregar o dbx local" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Falha ao carregar peculiaridades" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Falha ao carregar o dbx do sistema" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Falha ao bloquear" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Falha ao interpretar argumentos" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Falha ao analisar o ficheiro" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Falha ao analisar opções para --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Falha ao analisar o dbx local" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Falha ao reiniciar" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Falha ao definir o modo splash" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Falha ao validar o conteúdo da ESP" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nome de ficheiro" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Assinatura de nome de ficheiro" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Fonte do nome do ficheiro" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nome de ficheiro necessário" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra com um conjunto de opções de dispositivo usando um prefixo ~ para excluir, p.ex. \"internal,~needs-reboot\"" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI base de firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Serviço D-Bus de Atualização de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Daemon de Atualização de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilitário de Firmware" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Atestação de firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "O firmware já está bloqueado" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "O firmware ainda não está bloqueado" + +#. 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] "Os metadados do firmware não foram atualizados a %u dia e podem não estar atualizados." +msgstr[1] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." +msgstr[2] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Última atualização dos metadados do firmware: %s atrás. Use --force para atualizar novamente." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Atualizações de firmware" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Atualizações de firmware desabilitadas; execute \"fwupdmgr unlock\" para habilitar" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Opções" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Força a ação relaxando alguns verificações em tempo de execução" + +msgid "Force the action ignoring all warnings" +msgstr "Força a ação ignorando todos avisos" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Encontrado" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Encriptação completa do disco detetada" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" +msgstr[2] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Obtém todas as opções de dispositivo suportadas pelo fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obtém todos os dispositivos que oferecem suporte às atualizações de firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obtém todos os plugins registados com o sistema" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obtém detalhes sobre um ficheiro de firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Obtém os remotos configurados" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obtém os atributos de segurança do host" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Obtém a lista de firmware aprovado" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Obtém a lista de firmwares bloqueados" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Obtém a lista de atualizações para os hardwares conectados" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Obtém os lançamentos para um dispositivo" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Obtém os resultados da última atualização" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "FICHEIRO-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "O hardware está aguardando para ser reconectado" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Alta" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de segurança do host:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Inativo…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora verificações estritas de SSL ao baixar ficheiros" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignora falhas de soma de verificação de firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignora falhas de incompatibilidade de hardware de firmware" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignorar verificações de segurança de validação" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorando as verificações estritas de SSL; para fazer isso automaticamente no futuro, exporte DISABLE_SSL_STRICT no seu ambiente" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Duração de instalação" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instala um blob de firmware em um dispositivo" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instala um ficheiro de firmware neste periférico" + +msgid "Install signed device firmware" +msgstr "Instalar firmware assinado no dispositivo" + +msgid "Install signed system firmware" +msgstr "Instalar firmware assinado no sistema" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instalar primeiro no dispositivo principal" + +msgid "Install unsigned device firmware" +msgstr "Instalar firmware não assinado no dispositivo" + +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…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protegido com Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fuse programável (OTP) do Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política de erro do Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Inicialização verificada com Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET ativo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Ativado para Intel CET" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Dispositivo interno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Inválido" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Está no modo gestor de arranque" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problema" +msgstr[1] "Problemas" +msgstr[2] "Problemas" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "CHAVE,VALOR" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Chaveiro" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOCAL" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Última modificação" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Menos que um minuto restante" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licença" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (firmware estável)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (firmware de teste)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Confinamento do kernel Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap do Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Lista entradas no dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Lista atualizações de firmware suportadas" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Lista os tipos de firmware disponível" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Lista ficheiros na ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "A carregar…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloqueado" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Baixa" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modo de fabricação do MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Substituição de MEI" + +msgid "MEI version" +msgstr "Versão MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Ativa manualmente plugins específicos" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Média" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Assinatura de metadados" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI de metadados" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadados podem ser obtidos do Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versão mínima" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Daemon e cliente incompatíveis, use %s" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Modifica um valor de configuração do daemon" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifica um remoto dado" + +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" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monta a ESP" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Precisa de uma reinício após a instalação" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Precisa reiniciar" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Precisa de desligamento após a instalação" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nova versão" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nenhuma ação especificada!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Nenhum downgrade para %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nenhum ID de firmware encontrado" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nenhum periférico com capacidade de atualização de firmware foi detectado" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nenhum plugin encontrado" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nenhum lançamento disponível" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Nenhum remoto está atualmente ativado, então nenhum metadado está disponível." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nenhum remoto disponível" + +msgid "No updates available for remaining devices" +msgstr "Nenhuma atualização disponível para os dispositivos restantes" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nenhuma atualização foi aplicada" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Não encontrado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Não suportado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostra apenas valor único de PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Usa apenas IPFS ao baixar ficheiros" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Apenas atualizações de versão são permitidas" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Saída em formato JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Substitui o caminho padrão da ESP" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "CAMINHO" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Analisa e mostra detalhes sobre um ficheiro de firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Analisando a atualização de dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Analisando o dbx do sistema…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Senha" + +msgid "Payload" +msgstr "Carga" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Pendente" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentagem concluída" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Efetuar a operação?" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Por favor, insira um número de 0 a %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dependências de plugin ausentes" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Proteção de DMA pré-inicialização" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Versão anterior" + +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" + +msgid "Proceed with upload?" +msgstr "Prosseguir com o envio?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Privativa" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consulta por suporte a atualização de firmware" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ID-REMOTO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ID-REMOTO CHAVE VALOR" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Lê um blob de firmware de um dispositivo" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Lê o firmware do dispositivo para um ficheiro" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Lê o firmware de uma partição para um ficheiro" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lendo de %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "A ler…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "A reiniciar…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Renova metadados do servidor remoto" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Reinstalar %s para %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Reinstala o firmware atual no dispositivo" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstala firmware em um dispositivo" + +#. 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 "A reinstalar %s com %s… " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Ramificação de lançamento" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID da versão" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID remoto" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Substitui os dados em um ficheiro de firmware existente" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI do relatório" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Relatado ao servidor remoto" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "O sistema de ficheiros efivarfs necessário não foi encontrado" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "O hardware necessário não foi encontrado" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Requer um gestor de arranque" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Requer ligação à Internet" + +#. TRANSLATORS: reboot to apply the update +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 "A reiniciar o dispositivo…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Retorna todos os IDs de hardware para a máquina" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Execute `fwupdmgr get-upgrades` para mais informações." + +#. 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" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "O kernel em uso é muito antigo" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufixo de tempo de execução" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Descritor de BIOS do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Região BIOS do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloqueio de SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escrita de SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSISTEMA DRIVER [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Guardar um ficheiro que permite a geração de IDs de hardware" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Agenda instalação para próxima reinicialização quando possível" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "A agendar…" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Veja %s para mais informações." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Dispositivo selecionado" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume selecionado" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Número de série" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Define a opção de depuração durante atualização" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Define a lista de firmwares aprovados" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Compartilha histórico de firmware com os desenvolvedores" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Mostra todos os resultados" + +#. TRANSLATORS: command line option +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 domains" +msgstr "Mostra informações de depuração para todos domínios" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostrar opções de depuração" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostra dispositivos que não são atualizáveis" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostra informações adicionais de depuração" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostra histórico de atualizações de firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostra informação verbosa de plugin" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra a versão calculada do dbx" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Mostra o registo 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Assina um firmware com uma nova chave" + +msgid "Sign data using the client certificate" +msgstr "Assina dados usando o certificado cliente" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Tamanho" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Fonte" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifica ID(s) de Fornecedor/Produto de dispositivo DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especifica o ficheiro da base de dados dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifica o número de bytes por transferência USB" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Sucesso" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Todos os dispositivos ativados com sucesso" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remoto desabilitado com sucesso" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Transferidos com sucesso novos metadados: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Remoto ativado e atualizado com sucesso" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remoto ativado com sucesso" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware instalado com sucesso" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Valor da configuração modificada com sucesso" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remoto modificado com sucesso" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadados atualizados manualmente com sucesso" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Somas de verificação de dispositivo atualizadas com sucesso" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Enviado com sucesso %u relatório" +msgstr[1] "Enviados com sucesso %u relatórios" +msgstr[2] "Enviados com sucesso %u relatórios" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Somas de verificação de dispositivo verificadas com sucesso" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Resumo" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Suportado" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Suporte no servidor remoto" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspensão para inativo" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspensão para RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Trocar ramificação de %s para %s ?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Alterna a ramificação do firmware no dispositivo" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "O sistema exige uma fonte de alimentação externa" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXTO" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrução de PCR0 de TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Etiqueta" +msgstr[1] "Etiquetas" +msgstr[2] "Etiquetas" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Contaminado" + +msgid "Target" +msgstr "Destino" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testa um dispositivo usando um manifesto JSON" + +#. 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 ligação com $OS_RELEASE:NAME$. O seu distribuidor pode não ter verificado alguma das atualizações de firmware por compatibilidade com O seu sistema ou dispositivos ligados. Todo o firmware é fornecido apenas pelo fabricante do equipamento original." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "O TPM PCR0 diverge da reconstrução." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "O daemon carregou código de terceiros e não é mais suportado pelos desenvolvedores upstream!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "O firmware de %s não é fornecido por %s, o fornecedor de hardware." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "O relógio do sistema não foi definido corretamente e baixar ficheiros pode resultar em falha." + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Não há ficheiros de firmware bloqueados" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nenhum firmware aprovado." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Este pacote não foi validado, pode não funcionar corretamente." + +#. 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 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á a ser testado pelo fornecedor do hardware. Deve garantir que tem uma maneira de reverter manualmente do firmware se a atualização do firmware falhar." + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Este sistema possui problemas de tempo de execução de HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Este sistema possui um nível de segurança de HSI baixo." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Esta ferramenta permite que um administrador aplique atualizações de dbx UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Esta ferramenta permite que um administrador depure da operação UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Esta ferramenta permite que um administrador consulte e controle o daemon fwupd, permitindo que ele execute ações como instalar ou fazer downgrade do firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Esta ferramenta permite que um administrador use os plugins do fwupd sem estarem instalados no sistema host." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Esta ferramenta só pode ser usada pelo utilizador root" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Esta ferramenta vai ler e analisar o log de evento de TPM do firmware do sistema." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Falha transitória" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipo" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partição UEFI ESP não detectada ou configurada" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitário de Firmware UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Atualizações de cápsula UEFI não disponíveis ou habilitadas na configuração do firmware" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitário de dbx UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Firmware UEFI não pode ser atualizado no modo BIOS legado" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Chave de plataforma UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Secure boot de UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Não foi possível conectar ao serviço" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Desvincula o driver atual" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Desbloqueando firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Retira impedimento de um firmware específico ser instalado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Descriptografado" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Desconhecido" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositivo desconhecido" + +msgid "Unlock the device to allow access" +msgstr "Desbloquear o dispositivo para permitir acesso" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desbloqueado" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Desbloqueia o dispositivo para acesso do firmware" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Desmonta a ESP" + +#. 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: Suffix: the HSI result +msgid "Untainted" +msgstr "Descontaminado" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Atualizável" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Erro de atualização" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Mensagem de atualização" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Estado da atualização" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "A falha de atualização é um problema conhecido, visite esta URL para mais informações:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Atualizar agora?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "A atualização requer um reinício" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Atualiza o hash criptográfico armazenado com o atual conteúdo da ROM" + +msgid "Update the stored device verification information" +msgstr "Atualizar as informações de verificação armazenadas do dispositivo" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Atualiza os metadados armazenados com o conteúdo atual" + +msgid "Updating" +msgstr "Atualizando" + +#. 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 "A atualizar %s de %s para %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Atualizando %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Atualizar %s de %s para %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Enviar relatório agora?" + +#. 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 "O envio de relatórios de firmware ajuda os fornecedores de hardware a identificar rapidamente atualizações com falha e êxito em dispositivos." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgência" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Use fwupdmgr --help para ajuda" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Use fwupdtool --help para ajuda" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa opções de peculiaridades ao instalar firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "O utilizador foi notificado" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nome de utilizador" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Válido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validando conteúdo da ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variação" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Fornecedor" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "A verificar…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versão" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "AVISO:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "A aguardar…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Monitora alterações no hardware" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Escreve um firmware do ficheiro para o dispositivo" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Escreve um firmware do ficheiro para uma partição" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "A gravar ficheiro:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "A gravar…" + +#. 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 "O seu distribuidor pode não ter verificado nenhuma das atualizações de firmware por compatibilidade com seu sistema ou dispositivos ligados." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "O seu hardware pode ser danificado usando este firmware e instalar esta versão pode anular qualquer garantia com %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[SOMA-VERIFICAÇÃO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID-DISPOSITIVO|GUID] [RAMIFICAÇÃO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FICHEIRO FICHEIRO-ASSINATURA ID-REMOTO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NOME-DE-FICHEIRO1] [NOME-DE-FICHEIRO2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[FICHEIRO-SMBIOS|FICHEIRO-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "padrão" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Utilitário de registo de eventos TPM do fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Plugins do fwupd" diff --git a/fwupd-1.8.6/po/pt_BR.po b/fwupd-1.8.6/po/pt_BR.po new file mode 100644 index 0000000000000000000000000000000000000000..151a7ed35f68d588fbc46f9baaccbf93505c8b5b --- /dev/null +++ b/fwupd-1.8.6/po/pt_BR.po @@ -0,0 +1,2581 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Derek W. Stavis , 2015 +# Derek W. Stavis , 2016 +# Derek W. Stavis , 2015-2016 +# Rafael Fontenelle , 2017-2018 +# Rafael Fontenelle , 2015-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minuto restante" +msgstr[1] "%.0f minutos restantes" +msgstr[2] "%.0f minutos restantes" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Atualização de BMC para %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Atualização da bateria para %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Atualização de microcódigo de CPU para %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Atualização de câmera para %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Atualização da configuração de %s " + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Atualização consumidor-final de ME para %s " + +#. 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 de controlador para %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: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Atualização de tela para %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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Atualização do teclado para %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 de ME para %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Atualização do mouse para %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Atualização da interface de rede %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Atualização do controlador de armazenamento %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: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Atualização de TPM para %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Atualização de controlador Thunderbolt para %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Atualização do touchpad para %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: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s e todos os dispositivos conectados não podem ser usados durante a atualização." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s apareceu: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s alterado: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s desapareceu: %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Modo de fabricação de %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s deve permanecer conectado durante a atualização para evitar danos." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s deve permanecer conectado a uma fonte de energia durante a atualização para evitar danos." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "substituição de %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "versão %s " + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dia" +msgstr[1] "%u dias" +msgstr[2] "%u dias" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] " %u dispositivo tem uma atualização de firmware disponível." +msgstr[1] "%u dispositivos têm uma atualização de firmware disponível." +msgstr[2] "%u dispositivos têm uma atualização de firmware disponível." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u dispositivo não é a configuração mais conhecida." +msgstr[1] "%u dispositivos não são a configuração mais conhecida." +msgstr[2] "%u dispositivos não são a configuração mais conhecida." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u hora" +msgstr[1] "%u horas" +msgstr[2] "%u horas" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u dispositivo local suportado" +msgstr[1] "%u dispositivos locais suportados" +msgstr[2] "%u dispositivos locais suportados" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minuto" +msgstr[1] "%u minutos" +msgstr[2] "%u minutos" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u segundo" +msgstr[1] "%u segundos" +msgstr[2] "%u segundos" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(obsoleto)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "Um PCR de TPM é agora um valor inválido" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Ação necessária:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Habilita 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: the age of the metadata +msgid "Age" +msgstr "Idade" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Concordar e habilitar o remoto?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Atalho para %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Todos PCRs de TPM são agora válidos" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Todos PCRs de TPM são válidos" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Todos os dispositivos do mesmo tipo serão atualizados ao mesmo tempo" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Permite fazer downgrade de versões de firmware" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Permite reinstalar versões de firmware existentes" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Permite alternar a ramificação do firmware" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Ramificação alternativa" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Uma atualização requer uma reinicialização para ser concluída." + +#. 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" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Aplica atualizações de firmware" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Aplica a atualização mesmo quando não recomendado" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Aplica arquivos de atualização" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Aplicando atualização…" + +#. 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:" +msgstr[2] "Firmwares aprovados:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Perguntar novamente da próxima vez?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Modo anexar ao firmware" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autenticando…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Os detalhes de autenticação são necessários" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Autenticação é necessária para desatualizar a 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 "Autenticação é necessária para desatualizar a 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 "Autenticação é necessária 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 "Autenticação é necessária 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 "Autenticação é necessária 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 "Autenticação é necessária para assinar dados usando o certificado do cliente" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Autenticação é necessária para alternar para nova versão de firmware" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Autenticação é necessária para desbloquear um dispositivo" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Autenticação é necessária para atualizar o firmware em dispositivo removível" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Autenticação é necessária para atualizar o firmware nesta máquina" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Autenticação é necessária para atualizar as somas de verificação armazenadas para o dispositivo" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Relatórios automáticos" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Enviar automaticamente toda vez?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "XML-CONSTRUTOR NOME-DE-ARQUIVO-DEST" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Vincula novo driver de kernel" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Arquivos de firmware bloqueados:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Versão bloqueada" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Bloqueando firmware:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Impede que um firmware específico seja instalado" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versão do gerenciador de boot" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Ramificação" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Compila um arquivo de firmware" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Cancelar" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Cancelado" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Não foi possível aplicar, pois a atualização de dbx já foi aplicada." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Não é possível aplicar atualizações em uma mídia de instalação" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Alterado" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Verifica se o hash criptográfico corresponde ao firmware" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Soma de verificação" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Escolha uma ramificação:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Escolha um dispositivo:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Escolha um tipo de firmware:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Escolha um lançamento:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Escolha um volume:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Limpa os resultados da última atualização" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Comando não encontrado" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Suporte mantido pela comunidade" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Converte um arquivo de firmware" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Criada" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Crítica" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "A verificação criptográfica de hash está disponível" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versão atual" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ID-DISPOSITIVO|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Utilitário DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Opções de depuração" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Descompactando…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Descrição" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Modo desanexar ao gerenciador de boot" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detalhes" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Desviar da configuração mais conhecida?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Opções do dispositivo" + +#. 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:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "O dispositivo pode recuperar falhas de flashing" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Dispositivo modificado:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Um firmware de dispositivo é necessário para ter uma verificação de versão" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "O dispositivo está bloqueado" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Um dispositivo é necessário para instalar todos os lançamentos fornecidos" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "O dispositivo está inalcançável" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "O dispositivo pode ser usado durante a atualização" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Dispositivo removido:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Atualizações de estágios do dispositivo" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "O dispositivo tem suporte para alternar para uma ramificação diferente do firmware" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Método de atualização do dispositivo" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "A atualização do dispositivo precisa de ativação" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "O dispositivo fará backup do firmware antes de instalar" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "O dispositivo não aparecerá novamente após a atualização ser concluída" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Dispositivos que foram atualizados com sucesso:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Dispositivos que não foram atualizados com sucesso:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Dispositivos com nenhuma atualização de firmware disponível:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Dispositivos com a versão mais recente do firmware disponível:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Não foi encontrado qualquer dispositivo com GUID correspondente" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Desabilitado" + +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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Não verifica por histórico não relatado" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Não verifica se remotos para dowload devem estar habilitados" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Não verifica nem solicita configuração para reiniciar após atualizar" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Não inclui prefixo de domínio de log" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Não inclui prefixo com marca de tempo" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Não realiza verificações de segurança de dispositivo" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Não solicita dispositivos" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Não escreve no banco de dados de histórico" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Você entende as consequências de trocar a ramificação do firmware?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Você deseja desabilitar este recurso para atualizações futuras?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Você deseja atualizar este remoto agora?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Você deseja enviar relatórios automaticamente para atualizações futuras?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Feito!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Desatualizar %s de %s para %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Desatualiza a 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 "Fazendo downgrade de %s de %s para %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Fazendo downgrade %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Baixa um arquivo" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Baixando…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Despeja dados SMBIOS a partir de um arquivo" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Duração" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "A ESP especificada não era válida" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Habilita suporte a atualização de firmware em sistemas suportados" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Habilitar novo remoto?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Habilitar esse remoto?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Habilitado" + +msgid "Enabled fwupdate debugging" +msgstr "Depuração de fwupdate habilitada" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Habilitado se o hardware corresponder" + +#. 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." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Criptografado" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "RAM criptografada" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Fim de vida" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Apaga todo histórico de atualização de firmware" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Apagando…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Sair após pequeno atraso" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Sair após o carregamento do motor" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Exporta a estrutura de arquivo de um firmware para XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extrai um blob de firmware para imagens" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "ARQUIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "ARQUIVO [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "NOME-DE-ARQUIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "NOME-DE-ARQUIVO CERTIFICADO CHAVE-PRIVADA" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "NOME-DE-ARQUIVO NOME-ALT-DISPOSITIVO|ID-ALT-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "NOME-DE-ARQUIVO NOME-ALT-DISPOSITIVO|ID-ALT-DISPOSITIVO [NOME-ALT-IMAGEM|ID-ALT-IMAGEM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "NOME-DE-ARQUIVO ID-DISPOSITIVO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "NOME-DE-ARQUIVO DESLOCAMENTO DADOS [TIPO-DO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "NOME-DE-ARQUIVO [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "NOME-DE-ARQUIVO [TIPO-FIRMWARE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "NOME-DE-ARQUIVO-ORIG NOME-DE-ARQUIVO-DEST [TIPO-FIRMWARE-ORIG] [TIPO-FIRMWARE-DEST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "NOME-DE-ARQUIVO|SOMA-VERIFICAÇÃO1[,SOMA-VERIFICAÇÃO2][,SOMA-VERIFICAÇÃO3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Falhou" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Falha ao aplicar a atualização" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Falha ao se conectar ao daemon" + +#. 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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Falha ao carregar o dbx local" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Falha ao carregar peculiaridades" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Falha ao carregar o dbx do sistema" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Falha ao bloquear" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Falha ao interpretar argumentos" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Falha ao analisar o arquivo" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Falha ao analisar opções para --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Falha ao analisar o dbx local" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Falha ao validar o conteúdo da ESP" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Nome de arquivo" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Assinatura de nome de arquivo" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Fonte do nome do arquivo" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Nome de arquivo necessário" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra com um conjunto de opções de dispositivo usando um prefixo ~ para excluir, p.ex. \"internal,~needs-reboot\"" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI base de firmware" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Serviço D-Bus de Atualização de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Daemon de Atualização de Firmware" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Utilitário de Firmware" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Atestação de firmware" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "O firmware já está bloqueado" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "O firmware ainda não está bloqueado" + +#. 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] "Os metadados do firmware não foram atualizados a %u dia e podem não estar atualizados." +msgstr[1] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." +msgstr[2] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Última atualização dos metadados do firmware: %s atrás. Use --force para atualizar novamente." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Atualizações de firmware" + +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: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Atualizações de firmware desabilitadas; execute \"fwupdmgr unlock\" para habilitar" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Opções" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Força a ação relaxando alguns verificações em tempo de execução" + +msgid "Force the action ignoring all warnings" +msgstr "Força a ação ignorando todos avisos" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Encontrado" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Criptografia de disco completo detectada" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Os segredos de criptografia de disco completa podem ser invalidados ao atualizar" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Plataforma fundida" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" +msgstr[2] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Obtém todas as opções de dispositivo suportadas pelo fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Obtém todos os dispositivos que oferecem suporte às atualizações de firmware" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Obtém todos os plugins registrados com o sistema" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Obtém detalhes sobre um arquivo de firmware" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Obtém os remotos configurados" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Obtém os atributos de segurança do host" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Obtém a lista de firmware aprovado" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Obtém a lista de firmwares bloqueados" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Obtém a lista de atualizações para os hardwares conectados" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Obtém os lançamentos para um dispositivo" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Obtém os resultados da última atualização" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "ARQUIVO-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "O hardware está aguardando para ser reconectado" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Alta" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Eventos de segurança do host" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ID de segurança do host:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Proteção de dispositivo IOMMU desabilitada" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Proteção de dispositivo IOMMU habilitada" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Ocioso…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora verificações estritas de SSL ao baixar arquivos" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignora falhas de soma de verificação de firmware" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignora falhas de incompatibilidade de hardware de firmware" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignorar verificações de segurança de validação" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorando as verificações estritas de SSL; para fazer isso automaticamente no futuro, exporte DISABLE_SSL_STRICT em seu ambiente" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Duração de instalação" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Instala um blob de firmware em um dispositivo" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Instala um arquivo de firmware neste periférico" + +msgid "Install old version of signed system firmware" +msgstr "Instalar versão antiga do firmware do sistema assinado" + +msgid "Install old version of unsigned system firmware" +msgstr "Instalar a versão antiga do firmware do sistema não assinado" + +msgid "Install signed device firmware" +msgstr "Instalar firmware assinado no dispositivo" + +msgid "Install signed system firmware" +msgstr "Instalar firmware assinado no sistema" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Instalar primeiro no dispositivo pai" + +msgid "Install unsigned device firmware" +msgstr "Instalar firmware não assinado no dispositivo" + +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…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "A instalação desta atualização também pode anular qualquer garantia do dispositivo." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "ACM protegido com Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Fuse programável (OTP) do Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Política de erro do Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Inicialização verificada com Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET ativo" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Habilitado para Intel CET" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Dispositivo interno" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Inválido" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "É desatualização" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Está no modo gerenciador de boot" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "É atualização" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problema" +msgstr[1] "Problemas" +msgstr[2] "Problemas" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "CHAVE,VALOR" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Kernel não está mais contaminado" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Kernel está contaminado" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Isolamento do kernel desabilitado" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Isolamento do kernel habilitado" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Chaveiro" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOCAL" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Última modificação" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Menos que um minuto restante" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licença" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (firmware estável)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (firmware de teste)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Kernel Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Confinamento do kernel Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Swap do Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Lista entradas no dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Lista atualizações de firmware suportadas" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Lista os tipos de firmware disponível" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Lista arquivos na ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Carregando…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Bloqueado" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Baixa" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Modo de fabricação do MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "substituição de MEI" + +msgid "MEI version" +msgstr "versão MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Habilita manualmente plugins específicos" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Média" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Assinatura de metadados" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI de metadados" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadados podem ser obtidos do Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versão mínima" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Daemon e cliente incompatíveis, use %s" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Modifica um valor de configuração do daemon" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifica um remoto dado" + +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" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monta a ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "NOTA: Este programa pode funcionar corretamente apenas como root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Precisa de uma reinicialização após a instalação" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Precisa reiniciar" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Precisa de desligamento após a instalação" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Nova versão" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Nenhuma ação especificada!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Nenhuma desatualização para %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Nenhum ID de firmware encontrado" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nenhum periférico com capacidade de atualização de firmware foi detectado" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Nenhum plugin encontrado" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nenhum lançamento disponível" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Nenhum remoto está atualmente habilitado, então nenhum metadado está disponível." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nenhum remoto disponível" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Nenhum dispositivo atualizável" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Nenhuma atualização disponível" + +msgid "No updates available for remaining devices" +msgstr "Nenhuma atualização disponível para os dispositivos restantes" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Nenhuma atualização foi aplicada" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Não aprovada" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Não encontrado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Não suportado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Mostra apenas valor único de PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Usa apenas IPFS ao baixar arquivos" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Apenas atualizações de versão são permitidas" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Saída em formato JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Substitui o caminho padrão da ESP" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "CAMINHO" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Analisa e mostra detalhes sobre um arquivo de firmware" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Analisando a atualização de dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Analisando o dbx do sistema…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Senha" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Corrige um blob de firmware em um deslocamento conhecido" + +msgid "Payload" +msgstr "Carga" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Pendente" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Percentagem concluída" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Efetuar a operação?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Depuração de plataforma" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Certifique-se de ter a chave de recuperação de volume antes de continuar." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Por favor, insira um número de 0 a %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Dependências de plugin ausentes" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Proteção de DMA pré-inicialização" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Proteção de DMA pré-inicialização está desabilitada" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Proteção de DMA pré-inicialização está habilitada" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Versão anterior" + +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" + +msgid "Proceed with upload?" +msgstr "Prosseguir com o envio?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Privativa" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Consulta por suporte a atualização de firmware" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ID-REMOTO" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ID-REMOTO CHAVE VALOR" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Lê um blob de firmware de um dispositivo" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Lê o firmware do dispositivo para um arquivo" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Lê o firmware de uma partição para um arquivo" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Lendo de %s…" + +#. TRANSLATORS: reading from the flash chips +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" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Reinstalar %s para %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Reinstala o firmware atual no dispositivo" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Reinstala firmware em um dispositivo" + +#. 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 "Reinstalando %s com %s… " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Ramificação de lançamento" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Opções da versão" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID Versão" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "ID remoto" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Substitui os dados em um arquivo de firmware existente" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI do relatório" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Relatado ao servidor remoto" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "O sistema de arquivos efivarfs necessário não foi encontrado" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "O hardware necessário não foi encontrado" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Requer um gerenciador de boot" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Requer conexão com a Internet" + +#. TRANSLATORS: reboot to apply the update +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…" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Retorna todos os IDs de hardware para a máquina" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Proteção contra reversão" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Execute `fwupdmgr get-upgrades` para mais informações." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Execute `fwupdmgr sync-bkc` para concluir esta ação." + +#. 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" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "O kernel em uso é muito antigo" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Sufixo de tempo de execução" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Descritor de BIOS do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Região BIOS do SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Bloqueio de SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Proteção contra repetição de SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Escrita de SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Proteção contra escrita de SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSISTEMA DRIVER [ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Salva um arquivo que permite a geração de IDs de hardware" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Agenda instalação para próxima reinicialização quando possível" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Agendando…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Inicialização segura desabilitada" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Inicialização segura habilitada" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Veja %s para mais detalhes." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Veja %s para mais informações." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Dispositivo selecionado" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Volume selecionado" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Número de série" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Define a opção de depuração durante atualização" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Define a lista de firmwares aprovados" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Compartilha histórico de firmware com os desenvolvedores" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Mostra todos os resultados" + +#. TRANSLATORS: command line option +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 domains" +msgstr "Mostra informações de depuração para todos domínios" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Mostrar opções de depuração" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Mostra dispositivos que não são atualizáveis" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Mostra informações adicionais de depuração" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Mostra histórico de atualizações de firmware" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Mostra informação verbosa de plugin" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Mostra a versão calculada do dbx" + +#. 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?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Assina um firmware com uma nova chave" + +msgid "Sign data using the client certificate" +msgstr "Assina dados usando o certificado cliente" + +#. TRANSLATORS: command description +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" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Carga útil assinada" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Tamanho" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Alguns dos segredos da plataforma podem ser invalidados ao atualizar este firmware." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Fonte" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Especifica ID(s) de Fornecedor/Produto de dispositivo DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Especifica o arquivo de banco de dados dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Especifica o número de bytes por transferência USB" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Sucesso" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Todos os dispositivos ativados com sucesso" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Remoto desabilitado com sucesso" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Baixados com sucesso novos metadados:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Remoto habilitado e atualizado com sucesso" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Remoto habilitado com sucesso" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Firmware instalado com sucesso" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Valor da configuração modificada com sucesso" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Remoto modificado com sucesso" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Metadados atualizados manualmente com sucesso" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Somas de verificação de dispositivo atualizadas com sucesso" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Enviado com sucesso %u relatório" +msgstr[1] "Enviados com sucesso %u relatórios" +msgstr[2] "Enviados com sucesso %u relatórios" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Somas de verificação de dispositivo verificadas com sucesso" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Resumo" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Suportado" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "CPU suportada" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Suporte no servidor remoto" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspensão para inativo" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspensão para RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Trocar ramificação de %s para %s ?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Alterna a ramificação do firmware no dispositivo" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Sincroniza versões de firmware com a configuração mais conhecida do host" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "O sistema exige uma fonte de alimentação externa" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXTO" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Reconstrução de PCR0 de TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "Reconstrução PCR0 de TPM é inválido" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "Reconstrução PCR0 de TPM é agora válido" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "PCRs vazios de TMP" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Tag" +msgstr[1] "Tags" +msgstr[2] "Tags" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Contaminado" + +msgid "Target" +msgstr "Alvo" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testa um dispositivo usando um manifesto JSON" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "O TPM PCR0 diverge da reconstrução." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "O daemon carregou código de terceiros e não é mais suportado pelos desenvolvedores upstream!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "A versão do dispositivo não corresponde: obteve %s, esperava %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "O firmware de %s não é fornecido por %s, o fornecedor de hardware." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "O relógio do sistema não foi definido corretamente e baixar arquivos pode resultar em falha." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "O fornecedor não forneceu nenhuma nota de lançamento." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Existem dispositivos com problemas:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Não há arquivos de firmware bloqueados" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Nenhum firmware aprovado." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Este dispositivo será revertido para %s quando o comando %s for executado." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Este firmware é fornecido pelos membros da comunidade LVFS e não é fornecido (ou suportado) pelo fornecedor de hardware original." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Este pacote não foi validado, ele pode não funcionar corretamente." + +#. 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 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 desatualizar manualmente o firmware se a atualização do firmware falhar." + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Este sistema possui problemas de tempo de execução de HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Este sistema possui um nível de segurança de HSI baixo." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Esta ferramenta permite que um administrador aplique atualizações de dbx UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Esta ferramenta permite que um administrador depure da operação UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Esta ferramenta permite que um administrador consulte e controle o daemon fwupd, permitindo que ele execute ações como instalar ou fazer downgrade do firmware." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Esta ferramenta permite que um administrador use os plugins do fwupd sem estarem instalados no sistema host" + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Esta ferramenta só pode ser usada pelo usuário root" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Esta ferramenta vai ler e analisar o log de evento de TPM do firmware do sistema." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Falha transitória" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Metadados confiável" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Carga útil confiável" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tipo" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Partição UEFI ESP não detectada ou configurada" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Utilitário de Firmware UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Atualizações de cápsula UEFI não disponíveis ou habilitadas na configuração do firmware" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Utilitário de dbx UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Firmware UEFI não pode ser atualizado no modo BIOS legado" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Chave de plataforma UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Secure boot de UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Não foi possível conectar ao serviço" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Desvincula o driver atual" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Desbloqueando firmware:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Retira impedimento de um firmware específico ser instalado" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Descriptografado" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Desconhecido" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositivo desconhecido" + +msgid "Unlock the device to allow access" +msgstr "Desbloquear o dispositivo para permitir acesso" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Desbloqueado" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Desbloqueia o dispositivo para acesso do firmware" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Desmonta a ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Desativa a opção de depuração durante atualização" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Carga útil não assinada" + +#. 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: Suffix: the HSI result +msgid "Untainted" +msgstr "Descontaminado" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Atualizável" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Erro de atualização" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Atualizar imagem" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Mensagem de atualização" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Estado da atualização" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "A falha de atualização é um problema conhecido, visite essa URL para mais informações:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Atualizar agora?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "A atualização requer uma reinicialização" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Atualiza o hash criptográfico armazenado com o atual conteúdo da ROM" + +msgid "Update the stored device verification information" +msgstr "Atualizar as informações de verificação armazenadas do dispositivo" + +#. 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 specified devices to latest firmware version, or all devices if unspecified" +msgstr "Atualiza todos os dispositivos especificados para a versão de firmware mais recente ou todos os dispositivos se não especificados" + +msgid "Updating" +msgstr "Atualizando" + +#. 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 "Atualizando %s de %s para %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Atualizando %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Atualizar %s de %s para %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Enviar relatório agora?" + +#. 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 "O envio de relatórios de firmware ajuda os fornecedores de hardware a identificar rapidamente atualizações com falha e êxito em dispositivos reais." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Urgência" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Use fwupdmgr --help para ajuda" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Use fwupdtool --help para ajuda" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa opções de peculiaridades ao instalar firmware" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "O usuário foi notificado" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Nome de usuário" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Válido" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validando conteúdo da ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variação" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Fornecedor" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verificando…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Versão" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "AVISO:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Aguardando…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Monitora alterações no hardware" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Escreve um firmware do arquivo para o dispositivo" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Escreve um firmware do arquivo para uma partição" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Escrevendo arquivo:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Escrevendo…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "Seu distribuidor pode não ter verificado nenhuma das atualizações de firmware por compatibilidade com seu sistema ou dispositivos conectados." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Seu hardware pode ser danificado usando este firmware e instalar esta versão pode anular qualquer garantia com %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Seu sistema está configurado para o BKC de %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[SOMA-VERIFICAÇÃO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ID-DISPOSITIVO|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID-DISPOSITIVO|GUID] [RAMIFICAÇÃO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ID-DISPOSITIVO|GUID] [VERSÃO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[ARQUIVO ARQUIVO-ASSINATURA ID-REMOTO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[NOME-DE-ARQUIVO1] [NOME-DE-ARQUIVO2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[ARQUIVO-SMBIOS|ARQUIVO-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "padrão" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Utilitário de registro de eventos TPM do fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Plugins do fwupd" diff --git a/fwupd-1.8.6/po/ru.po b/fwupd-1.8.6/po/ru.po new file mode 100644 index 0000000000000000000000000000000000000000..befa8cd9d56173afc579705c17bf975961d08146 --- /dev/null +++ b/fwupd-1.8.6/po/ru.po @@ -0,0 +1,1865 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Igor , 2017 +# Serge Vylekzhanin , 2015-2019 +# Ser82, 2022 +# Ser82, 2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Осталась %.0f минута" +msgstr[1] "Осталось %.0f минуты" +msgstr[2] "Осталось %.0f минут" +msgstr[3] "Осталось %.0f минут" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Обновление аккумулятора %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Обновление камеры %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Обновление конфигурации %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Обновление подсистемы 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: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display 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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Обновление клавиатуры %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Обновление подсистемы МЕ устройства %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Обновление мыши %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Обновление сетевого интерфейса %s" + +#. TRANSLATORS: 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: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad 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: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s версия" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u день" +msgstr[1] "%u дня" +msgstr[2] "%u дней" +msgstr[3] "%u дней" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u device has a firmware upgrade available." +msgstr[1] "%u устройства имеют доступное обновление прошивки." +msgstr[2] "%u устройств имеют доступное обновление прошивки." +msgstr[3] "%u устройство имеет доступное обновление прошивки." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u устройство с не самой лучшей известной конфигурацией." +msgstr[1] "%u устройства с не самой лучшей известной конфигурацией." +msgstr[2] "%u устройств с не самой лучшей известной конфигурацией." +msgstr[3] "%u устройств с не самой лучшей известной конфигурацией." + +#. 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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (пороговое значение - %u%%)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "PCR TPM теперь имеет недействительное значение" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Необходимое действие:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Активировать устройства" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Задействовать ожидающие устройства" + +msgid "Activate the new firmware on the device" +msgstr "Активировать новую прошивку на устройстве" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Активация обновления прошивки" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Активация обновления прошивки для" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Возраст" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Согласиться и активировать репозиторий?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Псевдоним %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Разрешить понижение версий прошивок" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Разрешить переустановку существующих версий прошивки" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Разрешить переключение веток прошивки" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +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 line option +msgid "Apply firmware updates" +msgstr "Применить обновления прошивки" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Применить обновление, даже если это не является рекомендованным" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Применить файлы обновления" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Применение обновления…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Одобренная прошивка:" +msgstr[1] "Одобренная прошивка:" +msgstr[2] "Одобренная прошивка:" +msgstr[3] "Одобренная прошивка:" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Подключить в режим прошивки" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Аутентификация…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Для понижения версии прошивки на съёмном устройстве требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Для понижения версии прошивки на этой машине требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Для модификации настроенного репозитория, используемого для обновления прошивки, требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Для изменения настроек фоновой службы требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "Для разблокировки устройства требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Для обновления прошивки на съёмном устройстве требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Для обновления прошивки на этой машине требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Для обновления хранимых контрольных сумм устройства требуется аутентификация" + +msgid "BYTES" +msgstr "БАЙТЫ" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Аккумулятор" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Привязать новый драйвер ядра" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Блокированные файлы прошивки:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Заблокированная версия" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Блокированная прошивка:" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Версия загрузчика" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Ветка" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +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 "Отменено" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Не удалось применить, поскольку обновление dbx уже применено." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Не удалось применить обновления с портативного носителя" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Изменено" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Контрольная сумма" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Выберите ветку:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Выберите устройство:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Выбрать тип прошивки:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Выберите выпуск:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Выберите том:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Очистить результаты c последнего обновления" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Команда не найдена" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Поддерживается сообществом" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Преобразовать файл прошивки" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Создано" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Критичный" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Текущая версия" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Средство работы с DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Параметры отладки" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Распаковка…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Описание" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Отключить в режим загрузчика" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Подробнее" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +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 "Добавлено устройство:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Заменено устройство:" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Устройство заблокировано" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Устройство недоступно" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Удалено устройство:" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Устройство поддерживает переключение на другую ветку прошивки" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Способ обновления устройства" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Устройства, которые были успешно обновлены:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Устройства, которые не были правильно обновлены:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Устройства без доступных обновлений прошивки:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Устройства с последней доступной версией прошивки:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +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 unreported history" +msgstr "Не проверять незарегистрированную историю" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Не проверять, включены ли репозитории" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Не включать префикс домена журнала" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Не включать префикс метки времени" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Не сохранять в базу данных истории" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Понимаете ли вы последствия изменения ветки прошивки?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Хотите отключить эту функцию для будущих обновлений?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Хотите сейчас обновить удалённый репозиторий?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Хотите автоматически загружать отчёты для будущих обновлений?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Готово!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +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…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Понижение версии прошивки устройства %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Скачать файл" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Получение..." + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Записать данные SMBIOS из файла" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Продолжительность" + +#. TRANSLATORS: 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Включить новый удалённый репозиторий?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Активировать этот репозиторий?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "Зашифровано" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Шифрование RAM" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Окончание поддержки" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Стереть всю историю обновлений прошивки" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Стирание…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Выйти после небольшой задержки" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Выйти после загрузки движка" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Экспорт структуры файла прошивки в XML" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "ФАЙЛ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "ИМЯ_ФАЙЛА" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Не удалось" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Не удалось применить обновление" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Не удалось загрузить локальную dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Не удалось загрузить странности" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Не удалось загрузить системную dbx" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Не удалось разобрать аргументы" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Не удалось проанализировать файл" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Не удалось разобрать флаги для «--filter»" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Не удалось обработать локальную dbx" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Не удалось проверить содержимое ESP" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Имя файла" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Подпись имени файла" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Имя файла источника" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Требуется имя файла" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Базовый URI прошивки" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "D-Bus служба обновления прошивки" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Фоновая служба обновления прошивки" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Средство работы с прошивками" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Прошивка еще не заблокирована" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "Метаданные прошивки не обновлялись в течение %u дня и могут быть устаревшими." +msgstr[1] "Метаданные прошивки не обновлялись в течение %u дней и могут быть устаревшими." +msgstr[2] "Метаданные прошивки не обновлялись в течение %u дней и могут быть устаревшими." +msgstr[3] "Метаданные прошивки не обновлялись в течение %u дней и могут быть устаревшими." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Обновления прошивки" + +msgid "Firmware updates are not supported on this machine." +msgstr "Обновления прошивки не поддерживаются на этой машине." + +msgid "Firmware updates are supported on this machine." +msgstr "Обновления прошивки поддерживаются на этой машине." + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Флаги" + +msgid "Force the action ignoring all warnings" +msgstr "Выполнить действие, игнорируя все предупреждения" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Найдено" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Ключи шифрования всего диска могут утратить силу при обновлении" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" +msgstr[2] "GUID" +msgstr[3] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Получить все флаги устройств, поддерживаемые fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Получить все устройства, которые поддерживают обновления прошивки" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Получить все активированные плагины, зарегистрированные в системе" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Получить сведения о файле прошивки" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Получить настроенные репозитории" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Получить список обновлений для подключенного оборудования" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Получить релизы для устройства" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Получить результаты с последнего обновления" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Высокий" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Бездействие…" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Продолжительность установки" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "Установить двоичную прошивку на устройство" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Установить файл прошивки на это оборудование" + +msgid "Install old version of signed system firmware" +msgstr "Установить старую версию подписанной системной прошивки" + +msgid "Install old version of unsigned system firmware" +msgstr "Установить старую версию неподписанной системной прошивки" + +msgid "Install signed device firmware" +msgstr "Установить подписанную прошивку устройства" + +msgid "Install signed system firmware" +msgstr "Установить подписанную системную прошивку" + +msgid "Install unsigned device firmware" +msgstr "Установить неподписанную прошивку устройства" + +msgid "Install unsigned system firmware" +msgstr "Установить неподписанную системную прошивку" + +#. TRANSLATORS: 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…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard защищено ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Политика ошибок Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Проверенная загрузка Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET активна" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET включена" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Внутреннее устройство" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Более старая" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Обновление" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Проблема" +msgstr[1] "Проблемы" +msgstr[2] "Проблем" +msgstr[3] "Проблема" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Хранилище ключей" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "РАСПОЛОЖЕНИЕ" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Последнее изменение" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Осталось меньше минуты" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Лицензия" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Ядро линукс" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Ядро линукс с функцией lockdown" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Список записей в dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Вывести список поддерживаемых обновлений прошивки" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Вывести список файлов на ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Загрузка…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Заблокировано" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Низкий" + +msgid "MEI version" +msgstr "Версия MEI" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Средний" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI метаданных" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Метаданные можно получить в Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Минимальная версия" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Несовместимые фоновая служба и клиент, используйте %s" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Модифицировать данный репозиторий" + +msgid "Modify a configured remote" +msgstr "Изменить настроенный репозиторий" + +msgid "Modify daemon configuration" +msgstr "Изменить настройки фоновой службы" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Следить за событиями в фоновой службе" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Монтировать ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "ПРИМЕЧАНИЕ. Эта программа может корректно работать только от имени пользователя root." + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Требуется перезагрузка" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Новая версия" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Не определено никаких действий." + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Идентификаторы прошивок не найдены" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "Нет доступных выпусков" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "В настоящее время урепозитории не активированы, поэтому метаданные недоступны." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Нет доступных репозиториев" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Нет пригодных для обновления устройств" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Нет доступных обновлений" + +msgid "No updates available for remaining devices" +msgstr "Нет доступных обновлений для остальных устройств" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Обновления не были применены" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Не одобренная" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Не найдено" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Не поддерживается" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "ОК" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Показывать только одно значение PCR" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Вывод в формате JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Переопределить путь ESP по умолчанию" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "ПУТЬ" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Разобрать и показать детали о файле прошивки" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Обработка обновления dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Обработка системной dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Пароль" + +msgid "Payload" +msgstr "Полезная нагрузка" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "В очереди" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Процент завершения" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Выполнить операцию?" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Пожалуйста, введите число от 0 до %u:" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Предзагрузочная защита DMA" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Предыдущая версия" + +msgid "Print the version number" +msgstr "Напечатать номер версии" + +msgid "Print verbose debug statements" +msgstr "Напечатать подробные отладочные отчёты" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Приоритет" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Проблемы" + +msgid "Proceed with upload?" +msgstr "Продолжить загрузку?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Проприетарная" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Запросить поддержку обновления прошивки" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Считать прошивку из устройства в файл" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Считать прошивку из одного раздела в файл" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Чтение из %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Чтение…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Перезагрузка…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Обновить метаданные с удаленного сервера" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Переустановить прошивку на устройстве" + +#. 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 "Переустановка %s с %s…" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Ветка выпуска" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Флаги выпуска" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "ID выпуска" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Идентификатор репозитория" + +#. TRANSLATORS: command description +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: 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 "Показать идентификаторы всех устройств на машине" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Запустите «fwupdmgr get-upgrades» для получения дополнительной информации." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Запустите команду «fwupdmgr sync-bkc», чтобы завершить действие." + +#. 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 "Запустить процедуру подготовки составного плагина, используя двоичную установку" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Дескриптор SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Регион SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Блокировка SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Запись SPI" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Запланировать установку на следующую перезагрузку, если это возможно" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Планировка…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot отключен" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot включен" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Подробнее смотрите %s." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Подробнее смотрите %s." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Выбранное устройство" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Выбранный том" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Серийный номер" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +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 all results" +msgstr "Показать все результаты" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Показать версии клиента и фоновой службы" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Показать подробную информацию о фоновой службе для определенного домена" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Показать отладочную информацию для всех доменов" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Показать параметры отладки" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Показать устройства, обновление для которых невозможно" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Показать дополнительную отладочную информацию" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Показать историю обновлений прошивки" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Показать подробную информацию о плагине" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Показать рассчитанную версию dbx" + +#. 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 "Подписать данные с использованием сертификата клиента" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Подписать данные с использованием сертификата клиента" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Подписать загруженные данные с использованием сертификата клиента" + +msgid "Signature" +msgstr "Подпись" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Подписанное содержимое" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Размер" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "При обновлении этой прошивки некоторые ключи платформы могут утратить силу." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Источник" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Определить идентификатор(ы) поставщика / продукта устройства DFU" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Указать файл базы данных dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Определить количество байтов на передачу USB" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Успешно" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Все устройства успешно активированы" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Удалённый репозиторий успешно отключен" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Успешно загружены новые метаданные:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Удалённый репозиторий успешно подключен и обновлён" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Удалённый репозиторий успешно подключен" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Прошивка успешно установлена" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Успешно изменено значение в настройках" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Успешно изменено удалённое хранилище" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Метаданные успешно обновлены вручную" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Контрольные суммы устройства успешно обновлены" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Успешно загружен %u отчёт" +msgstr[1] "Успешно загружено %u отчёта" +msgstr[2] "Успешно загружено %u отчётов" +msgstr[3] "Успешно загружено %u отчётов" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Контрольные суммы устройства успешно проверены" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Сводка" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Поддерживается" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Поддерживаемый CPU" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Переключиться с ветки %s на %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Переключить ветку прошивки на устройстве" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Реконстуркция PCR0 TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "Реконструкция PCR0 TPM недействительна" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Пустые PCR TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Метка" +msgstr[1] "Метки" +msgstr[2] "Меток" +msgstr[3] "Метки" + +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: nothing to show +msgid "There are no blocked firmware files" +msgstr "Нет заблокированных файлов прошивки" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Одобренной прошивки нет." + +#. TRANSLATORS: 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: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Этот инструмент позволяет администратору применять обновления UEFI dbx." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Этот инструмент позволяет администратору выполнять диагностику операции UpdateCapsule." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Этот инструмент может использовать только пользователь root" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Этот инструмент читает и обрабатывает журнал событий TPM, который заполняется системными прошивками." + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Надёжные метаданные" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Тип" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Средство для прошивки UEFI" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Утилита для UEFI dbx" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Ключ платформы UEFI" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Отвязать тукущий драйвер" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Разблокированная прошивка:" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Не зашифровано" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Неизвестно" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Неизвестное устройство" + +msgid "Unlock the device to allow access" +msgstr "Разблокировать устройство для получения доступа" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Разблокировано" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Разблокировать устройство для доступа к прошивке" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Размонтировать ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Снять флаг отладки во время обновления" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Неподписанное содержимое" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Неподдерживаемая фоновая служба версии %s, клиент версии %s" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Ошибка обновления" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Изображение обновления" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Сообщение об обновлении" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Обновить статус" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Ошибка обновления — известная проблема, посетите этот URL для получения дополнительной информации:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Обновить сейчас?" + +msgid "Update the stored device verification information" +msgstr "Обновить хранимую проверочную информацию устройства" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Обновить сохраненные метаданные с текущим содержимым" + +msgid "Updating" +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 "Updating %s from %s to %s... " +msgstr "Обновление %s с %s на %s…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Обновление устройства %s…" + +#. 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: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Используйте «fwupdtool --help» для получения справки" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Использовать вариативные флажки при установке прошивки" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Имя пользователя" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Проверка содержимого ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Вариант" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Производитель" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Проверка…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Версия" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "ПРЕДУПРЕЖДЕНИЕ:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Ожидание…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Следить за аппаратными изменениями" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Записать прошивку из файла на устройство" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Записать прошивку из файла на один раздел" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Запись файла:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Запись…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "Ваш распространитель системы может не проверять какие-либо обновления прошивки на совместимость с системой или подключенными устройствами." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ID-УСТРОЙСТВА|GUID] [ВЕТКА]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "по умолчанию" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Утилита для работы с записями журнала событий fwupd TPM" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "плагины fwupd" diff --git a/fwupd-1.8.6/po/si.po b/fwupd-1.8.6/po/si.po new file mode 100644 index 0000000000000000000000000000000000000000..5a28fd54df094f7c1df45f15b442f9a06259527e --- /dev/null +++ b/fwupd-1.8.6/po/si.po @@ -0,0 +1,2623 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# හෙළබස සමූහය, 2021-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: Sinhala (http://www.transifex.com/freedesktop/fwupd/language/si/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: si\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "විනාඩි %.0f ක් ඉතිරියි" +msgstr[1] "විනාඩි %.0f ක් ඉතිරියි" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC යාවත්කාලීන කිරීම" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s බැටරි යාවත්කාලීන කිරීම" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU මයික්‍රොකෝඩ් යාවත්කාලීන කිරීම" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s කැමරා යාවත්කාලීන කිරීම" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s වින්‍යාස යාවත්කාලීන කිරීම" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s පාරිභෝගික ME යාවත්කාලීන කිරීම" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s පාලක යාවත්කාලීන කිරීම" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s ආයතනික ME යාවත්කාලීන කිරීම" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s උපාංග යාවත්කාලීන කිරීම" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s සංදර්ශක යාවත්කාලීන කිරීම" + +#. TRANSLATORS: 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 Embedded Controller Update" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s යතුරුපුවරු යාවත්කාලීන කිරීම" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME යාවත්කාලීන කිරීම" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s මූසික යාවත්කාලීන කිරීම" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s ජාල අතුරුමුහුණත යාවත්කාලීන කිරීම" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s ගබඩා පාලක යාවත්කාලීන කිරීම" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s පද්ධති යාවත්කාලීන කිරීම" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM යාවත්කාලීන" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt Controller යාවත්කාලීන කිරීම" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s Touchpad යාවත්කාලීන කිරීම" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s USB ග්‍රාහකයා යාවත්කාලීන කිරීම" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s යාවත්කාලීන කරන්න" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s සහ සියලුම සම්බන්ධිත උපාංග යාවත්කාලීන කිරීමේදී භාවිත කළ නොහැක." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s පෙනී සිටියේය: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s වෙනස් විය: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s අතුරුදහන් විය: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s දැනට යාවත්කාලීන කළ නොහැක" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s නිෂ්පාදන මාදිලිය" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "හානිය වළක්වා ගැනීම සඳහා යාවත්කාලීන කාලය සඳහා %s සම්බන්ධව තිබිය යුතුය." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "හානිය වළක්වා ගැනීම සඳහා යාවත්කාලීන කාලසීමාව සඳහා %s බල ප්‍රභවයකට සම්බන්ධ කළ යුතුය." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s අභිබවා යයි" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "අනුවාදය %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "දින %u යි" +msgstr[1] "දින %u යි" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u උපාංගයේ ස්ථිරාංග උත්ශ්‍රේණිගත කිරීමක් ඇත." +msgstr[1] "%u උපාංග සඳහා ස්ථිරාංග උත්ශ්‍රේණිගත කිරීමක් ඇත." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u උපාංගය හොඳම දන්නා වින්‍යාසය නොවේ." +msgstr[1] "%u උපාංග හොඳම දන්නා වින්‍යාසය නොවේ." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "පැය %u යි" +msgstr[1] "පැය %u යි" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u දේශීය උපාංග සඳහා සහය දක්වයි" +msgstr[1] "%u දේශීය උපාංග සඳහා සහය දක්වයි" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "විනාඩි %u" +msgstr[1] "විනාඩි %u" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "තත්පර %u" +msgstr[1] "තත්පර %u" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (ඉන්පසුව %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(යල් පැන ගිය)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "TPM PCR දැන් වලංගු නොවන අගයකි" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "අවශ්‍ය ක්‍රියාමාර්ග:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "උපාංග සක්රිය කරන්න" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "පොරොත්තු උපාංග සක්‍රිය කරන්න" + +msgid "Activate the new firmware on the device" +msgstr "උපාංගයේ නව ස්ථිරාංග සක්රිය කරන්න" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "ස්ථිරාංග යාවත්කාලීන කිරීම සක්රිය කිරීම" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "සඳහා ස්ථිරාංග යාවත්කාලීන කිරීම සක්රිය කිරීම" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "වයස" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "එකඟ වී දුරස්ථ පාලකය සබල කරන්නද?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%s ට අපනාමය" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "සියලුම TPM PCRs දැන් වලංගු වේ" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "සියලුම TPM PCR වලංගු වේ" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "එකම වර්ගයේ සියලුම උපාංග එකම අවස්ථාවේදීම යාවත්කාලීන වේ" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "ස්ථිරාංග අනුවාද පහත හෙලීමට ඉඩ දෙන්න" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "පවතින ස්ථිරාංග අනුවාද නැවත ස්ථාපනය කිරීමට ඉඩ දෙන්න" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "ස්ථිරාංග ශාඛාව මාරු කිරීමට ඉඩ දෙන්න" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +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 line option +msgid "Apply firmware updates" +msgstr "ස්ථිරාංගයේ යාවත්කාල යොදන්න" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "උපදෙස් නොදෙන විට පවා යාවත්කාලීන කරන්න" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "යාවත්කාල ගොනු යොදන්න" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "යාවත්කාලය යොදමින්…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "අනුමත ස්ථිරාංග:" +msgstr[1] "අනුමත ස්ථිරාංග:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "ඊළඟ වතාවේ නැවත අසන්න?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "ස්ථිරාංග මාදිලියට අමුණන්න" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "…සත්‍යාපනය කිරීම" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "සත්‍යාපන විස්තර අවශ්‍යයි" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "ඉවත් කළ හැකි උපාංගයක ස්ථිරාංග පහත හෙලීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "මෙම යන්ත්‍රයේ ස්ථිරාංග පහත හෙලීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "ස්ථිරාංග යාවත්කාලීන කිරීම් සඳහා භාවිතා කරන වින්‍යාස කළ දුරස්ථ පාලකයක් වෙනස් කිරීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "ඩීමන් වින්‍යාසය වෙනස් කිරීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "උපාංගයක් අගුලු හැරීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "ඉවත් කළ හැකි උපාංගයක ස්ථිරාංග යාවත්කාලීන කිරීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "මෙම යන්ත්‍රයේ ස්ථිරාංග යාවත්කාලීන කිරීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "උපාංගය සඳහා ගබඩා කර ඇති චෙක්සම් යාවත්කාලීන කිරීමට සත්‍යාපනය අවශ්‍ය වේ" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "ස්වයංක්‍රීය වාර්තාකරණය" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "සෑම විටම ස්වයංක්‍රීයව උඩුගත කරන්නද?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML ගොනු නාමය-DST" + +msgid "BYTES" +msgstr "බයිට" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "බැටරි" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "නව කර්නල් ධාවකය බැඳන්න" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "අවහිර කළ ස්ථිරාංග ගොනු:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "අවහිර කළ අනුවාදය" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "ස්ථිරාංග අවහිර කිරීම:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "නිශ්චිත ස්ථිරාංග ස්ථාපනය කිරීම අවහිර කරයි" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Bootloader අනුවාදය" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "ශාඛාව" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +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 "අවලංගු කෙරිණි" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "dbx යාවත්කාලීනය දැනටමත් යොදවා ඇති බැවින් අයදුම් කළ නොහැක." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "සජීවී මාධ්‍යවල යාවත්කාලීන යෙදිය නොහැක" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "වෙනස් විය" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "ගුප්ත ලේඛන හැෂ් ගැලපීම් ස්ථිරාංග පරීක්ෂා කරයි" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "චෙක්සම්" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "ශාඛාවක් තෝරන්න:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "උපාංගයක් තෝරන්න:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "ස්ථිරාංග වර්ගයක් තෝරන්න:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "නිකුතුවක් තෝරන්න:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "පරිමාවක් තෝරන්න:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "අවසාන යාවත්කාලීනයෙන් ප්‍රතිඵල හිස් කරයි" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "විධානය සොයාගත නොහැකි" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "ප්‍රජාවගේ සහාය" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "ස්ථිරාංග ගොනුවක් පරිවර්තනය කරන්න" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "නිර්මාණය කළා" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "විවේචනාත්මක" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "ගුප්ත ලේඛන හැෂ් සත්‍යාපනය ලබා ගත හැකිය" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "වත්මන් අනුවාදය" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "DEVICE-ID|මාර්ගෝපදේශය" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU උපයෝගිතා" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "දෝශ නිරාකරණ විකල්ප" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "විසංයෝජනය…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "විස්තර" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "ඇරඹුම් කාරක මාදිලියට වෙන් කරන්න" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "විස්තර" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "හොඳින්ම දන්නා වින්‍යාසයෙන් බැහැර වන්නද?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "උපාංග කොඩි" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "උපාංග හැඳුනුම්පත" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "උපාංගය එකතු කළේ:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "උපාංගයේ බැටරි බලය ඉතා අඩුය" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "උපාංග බැටරි බලය ඉතා අඩුයි (%u%%, %u%% අවශ්‍යයි)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "උපාංගයට ෆ්ලෑෂ් අසමත්වීම් නැවත ලබා ගත හැක" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "පියන වසා ඇති විට උපාංගය භාවිතා කළ නොහැක" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "උපාංගය වෙනස් විය:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "අනුවාද පරීක්ෂාවක් කිරීමට උපාංග ස්ථිරාංග අවශ්‍ය වේ" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "උපාංගය අගුලු දමා ඇත" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "සපයා ඇති සියලුම නිකුතු ස්ථාපනය කිරීමට උපාංගය අවශ්‍ය වේ" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "උපාංගය ළඟා විය නොහැක" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "උපාංගය වෙත ළඟා විය නොහැක, නැතහොත් රැහැන් රහිත පරාසයෙන් පිටත" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "යාවත්කාලීන කාලය සඳහා උපාංගය භාවිතා කළ හැකිය" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "උපාංගය යාවත්කාලීනය යෙදෙන තෙක් බලා සිටී" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "උපාංගය ඉවත් කරන ලදී:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "උපාංගයට සම්බන්ධ වීමට AC බලය අවශ්‍යයි" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "උපාංග අදියර යාවත්කාලීන කිරීම්" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "ස්ථිරාංගයේ වෙනත් ශාඛාවකට මාරු වීමට උපාංගය සහාය දක්වයි" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "උපාංග යාවත්කාලීන ක්‍රමය" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "උපාංග යාවත්කාලීන කිරීම සක්‍රිය කිරීම අවශ්‍යයි" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "උපාංගය ස්ථාපනය කිරීමට පෙර ස්ථිරාංග උපස්ථ කරයි" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "යාවත්කාලීනය සම්පූර්ණ වූ පසු උපාංගය නැවත දිස් නොවනු ඇත" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "සාර්ථකව යාවත්කාලීන කරන ලද උපාංග:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "නිවැරදිව යාවත්කාලීන නොකළ උපාංග:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "පවතින ස්ථිරාංග යාවත්කාලීන නොමැති උපාංග: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "පවතින නවතම ස්ථිරාංග අනුවාදය සහිත උපාංග:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "ගැළපෙන GUID සහිත උපාංග කිසිවක් සොයා ගත්තේ නැත" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +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 unreported history" +msgstr "වාර්තා නොකළ ඉතිහාසය පරීක්ෂා නොකරන්න" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "බාගැනීම් දුරස්ථ සක්‍රීය කළ යුතුදැයි පරීක්ෂා නොකරන්න" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "යාවත්කාලීන කිරීමෙන් පසු නැවත පණගැන්වීම සඳහා පරීක්ෂා නොකරන්න" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "ලොග් වසම් උපසර්ගය ඇතුළත් නොකරන්න" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "වේලා මුද්දර උපසර්ගය ඇතුළත් නොකරන්න" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "උපාංග ආරක්ෂණ පරීක්ෂාවන් සිදු නොකරන්න" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "උපාංග සඳහා විමසන්න එපා" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "ඉතිහාස දත්ත ගබඩාවට ලියන්න එපා" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "ස්ථිරාංග ශාඛාව වෙනස් කිරීමේ ප්රතිවිපාක ඔබට තේරෙනවාද?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "ඔබට අනාගත යාවත්කාලීන සඳහා මෙම විශේෂාංගය අබල කිරීමට අවශ්‍යද?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "ඔබට දැන් මෙම දුරස්ථ පාලකය නැවුම් කිරීමට අවශ්‍යද?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "ඔබට අනාගත යාවත්කාලීන සඳහා වාර්තා ස්වයංක්‍රීයව උඩුගත කිරීමට අවශ්‍යද?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "අහවරයි!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "%s %s සිට %sදක්වා පහත් කරන්නද?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "උපාංගයක ස්ථිරාංග පහත හෙළයි" + +#. TRANSLATORS: 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දක්වා පහත හෙලීම ... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "පහත හෙලීම %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "ගොනුවක් බාගන්න" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "බාගත වෙමින්…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "ගොනුවකින් SMBIOS දත්ත ඩම්ප් කරන්න" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "කාල සීමාව" + +#. TRANSLATORS: 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "නව දුරස්ථ පාලකය සබල කරන්නද?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "මෙම දුරස්ථ පාලකය සබල කරන්නද?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "සබල කර ඇත" + +msgid "Enabled fwupdate debugging" +msgstr "fwupdate නිදොස්කරණය සබල කර ඇත" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "දෘඪාංග ගැලපෙන්නේ නම් සබල කර ඇත" + +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "සංකේතිතයි" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "සංකේතනය කළ RAM" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "ජීවිතයේ අවසානය" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "සියලුම ස්ථිරාංග යාවත්කාලීන ඉතිහාසය මකන්න" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "මැකෙමින්…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "කුඩා ප්‍රමාදයකින් පසු පිටවන්න" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "එන්ජිම පූරණය වූ පසු පිටවන්න" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "ස්ථිරාංග ගොනු ව්‍යුහයක් XML වෙත අපනයනය කරන්න" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "පින්තූර වෙත ස්ථිරාංග බ්ලොබ් උපුටා ගන්න" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "ගොනුව" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "ගොනුව [උපාංගය-ID|මාර්ගෝපදේශය]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "ගොනුවේ නම" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "ගොනු නාම සහතිකය පුද්ගලික යතුර" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FILENAME උපාංගය-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "FILENAME OFFSET දත්ත [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "ගොනු නාමය [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILENAME [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "අසමත් විය" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "යාවත්කාලය යෙදීමට අසමත් විය" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "දේශීය dbx පූරණය කිරීමට අසමත් විය" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "විචක්ෂණ පූරණය කිරීමට අසමත් විය" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "පද්ධතිය dbx පූරණය කිරීමට අසමත් විය" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "අගුළු ලෑමට අසමත් විය" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "තර්ක විග්‍රහ කිරීමට අසමත් විය" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "ගොනුව විග්‍රහ කිරීමට අසමත් විය" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "--ෆිල්ටර් සඳහා කොඩි විග්‍රහ කිරීමට අසමත් විය" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "දේශීය dbx විග්‍රහ කිරීමට අසමත් විය" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "ESP අන්තර්ගතයන් වලංගු කිරීමට අසමත් විය" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "ගොනුවේ නම" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "ගොනු නාමය අත්සන" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "ගොනු නාමය මූලාශ්රය" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "ගොනුවේ නම අවශ්‍යයි" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "බැහැර කිරීමට ~ උපසර්ගයක් භාවිතා කරමින් උපාංග ධජ කට්ටලයක් සමඟ පෙරහන් කරන්න, උදා 'අභ්‍යන්තර,~needs-reboot'" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "ස්ථිරාංග පදනම URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "ස්ථිරාංග යාවත්කාලීන D-බස් සේවාව" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "ස්ථිරාංග යාවත්කාලීන Daemon" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "ස්ථිරාංග උපයෝගිතා" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "ස්ථිරාංග සහතික කිරීම" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "ස්ථිරාංග දැනටමත් අවහිර කර ඇත" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "ස්ථිරාංග දැනටමත් අවහිර කර නැත" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "ස්ථිරාංග පාරදත්ත දින %u ක් සඳහා යාවත්කාලීන කර නොමැති අතර යාවත්කාලීන නොවිය හැක." +msgstr[1] "ස්ථිරාංග පාරදත්ත දින %u ක් සඳහා යාවත්කාලීන කර නොමැති අතර යාවත්කාලීන නොවිය හැක." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "ස්ථිරාංග පාරදත්ත අවසන් වරට නැවුම් කළේ: %s පෙර. නැවත නැවුම් කිරීමට --force භාවිතා කරන්න." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "ස්ථිරාංග යාවත්කාල" + +msgid "Firmware updates are not supported on this machine." +msgstr "මෙම යන්ත්‍රයේ ස්ථිරාංග යාවත්කාලීන කිරීම් සඳහා සහය නොදක්වයි." + +msgid "Firmware updates are supported on this machine." +msgstr "මෙම යන්ත්‍රයේ ස්ථිරාංග යාවත්කාලීන කිරීම් සඳහා සහය දක්වයි." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "ස්ථිරාංග යාවත්කාලීන කිරීම් අක්‍රීය කර ඇත; සබල කිරීමට 'fwupdmgr unlock' ධාවනය කරන්න" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "කොඩි" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "සමහර ධාවන කාල චෙක්පත් ලිහිල් කිරීමෙන් ක්‍රියාව බල කරන්න" + +msgid "Force the action ignoring all warnings" +msgstr "සියලුම අනතුරු ඇඟවීම් නොසලකා හරිමින් ක්‍රියාව බල කරන්න" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "හමු විය" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "සම්පූර්ණ තැටි සංකේතනය අනාවරණය විය" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "යාවත්කාලීන කිරීමේදී සම්පූර්ණ තැටි සංකේතාංකන රහස් අවලංගු විය හැක" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "විලයන වේදිකාව" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "මාර්ගෝපදේශය" +msgstr[1] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "මාර්ගෝපදේශය" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "fwupd මගින් සහය දක්වන සියලුම උපාංග කොඩි ලබා ගන්න" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "ස්ථිරාංග යාවත්කාලීන කිරීම් සඳහා සහය දක්වන සියලුම උපාංග ලබා ගන්න" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "සියලුම සක්‍රීය ප්ලගීන පද්ධතිය සමඟ ලියාපදිංචි කර ගන්න" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "ස්ථිරාංග ගොනුවක් පිළිබඳ විස්තර ලබා ගනී" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "වින්‍යාසගත දුරස්ථයන් ලබා ගනී" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "සත්කාරක ආරක්ෂක ගුණාංග ලබා ගනී" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "අනුමත ස්ථිරාංග ලැයිස්තුව ලබා ගනී" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "අවහිර කළ ස්ථිරාංග ලැයිස්තුව ලබා ගනී" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "සම්බන්ධිත දෘඩාංග සඳහා යාවත්කාලීන ලැයිස්තුව ලබා ගනී" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "උපාංගයක් සඳහා නිකුත් කිරීම් ලබා ගනී" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "අවසාන යාවත්කාලීනයෙන් ප්‍රතිඵල ලබා ගනී" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FILE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "දෘඪාංග නැවත පිරවීමට බලා සිටී" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "ඉහළ" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "සත්කාරක ආරක්ෂක සිදුවීම්" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "සත්කාරක ආරක්ෂක ID:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU උපාංග ආරක්ෂණය අබල කර ඇත" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU උපාංග ආරක්ෂණය සබල කර ඇත" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "නිෂ්ක්‍රීය…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "ගොනු බාගත කිරීමේදී SSL දැඩි චෙක්පත් නොසලකා හරින්න" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "ස්ථිරාංග චෙක්සම් අසමත්වීම් නොසලකා හරින්න" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "ස්ථිරාංග දෘඪාංග නොගැලපීම අසාර්ථක වීම නොසලකා හරින්න" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "වලංගු කිරීමේ ආරක්ෂණ පරීක්ෂාවන් නොසලකා හරින්න" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "SSL දැඩි චෙක්පත් නොසලකා හැරීම, අනාගතයේදී මෙය ස්වයංක්‍රීයව සිදු කිරීම සඳහා ඔබේ පරිසරය තුළ DISABLE_SSL_STRICT අපනයනය කරන්න" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +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 signed system firmware" +msgstr "අත්සන් කළ පද්ධති ස්ථිරාංගයේ පැරණි අනුවාදය ස්ථාපනය කරන්න" + +msgid "Install old version of unsigned system firmware" +msgstr "අත්සන් නොකළ පද්ධති ස්ථිරාංගයේ පැරණි අනුවාදය ස්ථාපනය කරන්න" + +msgid "Install signed device firmware" +msgstr "අත්සන් කරන ලද උපාංග ස්ථිරාංග ස්ථාපනය කරන්න" + +msgid "Install signed system firmware" +msgstr "අත්සන් කළ පද්ධති ස්ථිරාංග ස්ථාපනය කරන්න" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "පළමුව මාපිය උපාංගයට ස්ථාපනය කරන්න" + +msgid "Install unsigned device firmware" +msgstr "අත්සන් නොකළ උපාංග ස්ථිරාංග ස්ථාපනය කරන්න" + +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කිරීම" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "මෙම යාවත්කාලීනය ස්ථාපනය කිරීමෙන් ඕනෑම උපාංග වගකීමක් ද අවලංගු විය හැක." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM ආරක්ෂිතයි" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP ෆියුස්" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard දෝෂ ප්‍රතිපත්තිය" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard සත්‍යාපනය කළ ඇරඹුම" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET Active" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET සබල කර ඇත" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "අභ්යන්තර උපාංගය" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "වලංගු නොවේ" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "පහත හෙලනු ලැබේ" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "බූට්ලෝඩර් මාදිලියේ ඇත" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "වැඩිදියුණු වේ" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "නිකුත් කිරීම" +msgstr[1] "ගැටලු" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "යතුර, අගය" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "කර්නලය තවදුරටත් අපිරිසිදු නොවේ" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "කර්නලය අපිරිසිදු වේ" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "කර්නලය අගුලු දැමීම අබල කර ඇත" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "කර්නල් අගුලු දැමීම සබල කර ඇත" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "ප්රධාන මුදුව" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "ස්ථානය" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "අවසන් වරට වෙනස් කරන ලදී" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "මිනිත්තුවකට වඩා අඩු කාලයක් ඉතිරිව ඇත" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "බලපත්රය" + +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: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "ලිනක්ස් කර්නලය" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "ලිනක්ස් කර්නලය අගුලු දැමීම" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "ලිනක්ස් හුවමාරුව" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "dbx හි ඇතුළත් කිරීම් ලැයිස්තුගත කරන්න" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "සහාය දක්වන ස්ථිරාංග යාවත්කාලීන ලැයිස්තුගත කරන්න" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "පවතින ස්ථිරාංග වර්ග ලැයිස්තුගත කරන්න" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "ESP මත ගොනු ලැයිස්තුගත කරයි" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "පූරණය වෙමින්…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "අගුළු ලා ඇත" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "අඩු" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI නිෂ්පාදන මාදිලිය" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI අභිබවා යයි" + +msgid "MEI version" +msgstr "MEI අනුවාදය" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "විශේෂිත ප්ලගීන අතින් සක්‍රීය කරන්න" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "මධ්යම" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "පාරදත්ත අත්සන" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "පාරදත්ත URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "පාරදත්ත Linux Vendor Firmware Service වෙතින් ලබාගත හැක." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "අවම අනුවාදය" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "නොගැලපෙන ඩීමන් සහ සේවාදායකයා, ඒ වෙනුවට %s භාවිතා කරන්න" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "ඩීමන් වින්‍යාස අගයක් වෙනස් කරයි" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "දී ඇති දුරස්ථ පාලකයක් වෙනස් කරයි" + +msgid "Modify a configured remote" +msgstr "වින්‍යාසගත දුරස්ථ පාලකයක් වෙනස් කරන්න" + +msgid "Modify daemon configuration" +msgstr "ඩීමන් වින්‍යාසය වෙනස් කරන්න" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "සිදුවීම් සඳහා ඩීමන් නිරීක්ෂණය කරන්න" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "ESP සවි කරයි" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "සටහන: මෙම වැඩසටහන නිවැරදිව ක්‍රියා කළ හැක්කේ root ලෙස පමණි" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "ස්ථාපනය කිරීමෙන් පසු නැවත ආරම්භ කිරීම අවශ්ය වේ" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "නැවත පණගැන්වීම අවශ්‍යයි" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "ස්ථාපනය කිරීමෙන් පසු වසා දැමීම අවශ්ය වේ" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "නව අනුවාදය" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "ක්‍රියාවක් සඳහන් කර නැත!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "%sසඳහා පහත් කිරීම් නොමැත" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "ස්ථිරාංග හැඳුනුම් කිසිවක් හමු නොවීය" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "ස්ථිරාංග යාවත්කාලීන කිරීමේ හැකියාව ඇති දෘඪාංග අනාවරණය කර ගෙන නොමැත" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "ප්ලගින කිසිවක් හමු නොවීය" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "නිකුත් කිරීම් නොමැත" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "කිසිදු දුරස්ථ පාලකයක් දැනට සබල කර නොමැති නිසා පාරදත්ත නොමැත." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "දුරස්ථ නැත" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "යාවත්කාලීන කළ හැකි උපාංග නොමැත" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "යාවත්කාලීන නොමැත" + +msgid "No updates available for remaining devices" +msgstr "ඉතිරි උපාංග සඳහා යාවත්කාලීන නොමැත" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "යාවත්කාලීන කිසිවක් යොදන ලදී" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "අනුමත කර නැත" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "හමු නොවිණි" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "සහාය නොදක්වයි" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "හරි" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "හරි!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "තනි PCR අගය පමණක් පෙන්වන්න" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "ගොනු බාගත කිරීමේදී පමණක් IPFS භාවිතා කරන්න" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "අනුවාද උත්ශ්‍රේණි කිරීමට පමණක් අවසර ඇත" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "JSON ආකෘතියෙන් ප්‍රතිදානය" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "පෙරනිමි ESP මාර්ගය අභිබවා යන්න" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "මාර්ගය" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "ස්ථිරාංග ගොනුවක් පිළිබඳ විස්තර විග්‍රහ කර පෙන්වන්න" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "dbx යාවත්කාලීන…විග්‍රහ කිරීම" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "විග්‍රහ කිරීමේ පද්ධතිය dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "මුරපදය" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "දන්නා ඕෆ්සෙට් එකක ස්ථිරාංග බ්ලොබ් එකක් පැච් කරන්න" + +msgid "Payload" +msgstr "ගෙවීම" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "පොරොත්තුවෙන්" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "ප්‍රතිශතය සම්පූර්ණයි" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "මෙහෙයුම සිදු කරන්නද?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "වේදිකා නිදොස්කරණය" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "කරුණාකර දිගටම කරගෙන යාමට පෙර ඔබ සතුව ශබ්ද ප්‍රතිසාධන යතුර ඇති බවට සහතික වන්න." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "කරුණාකර 0 සිට %uදක්වා අංකයක් ඇතුළු කරන්න: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "ප්ලගින පරායත්තතා අතුරුදහන්" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "පෙර-ආරම්භක DMA ආරක්ෂාව" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "පෙර-ආරම්භක DMA ආරක්ෂාව අක්‍රීය කර ඇත" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "පෙර-ආරම්භක DMA ආරක්ෂාව සබල කර ඇත" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "පෙර අනුවාදය" + +msgid "Print the version number" +msgstr "අනුවාද අංකය මුද්‍රණය කරන්න" + +msgid "Print verbose debug statements" +msgstr "වාචික දෝශ නිරාකරණ ප්‍රකාශ මුද්‍රණය කරන්න" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "ප්රමුඛත්වය" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "ගැටලු" + +msgid "Proceed with upload?" +msgstr "උඩුගත කිරීම සමඟ ඉදිරියට යන්නද?" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "හිමිකාර" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "ස්ථිරාංග යාවත්කාලීන සහාය සඳහා විමසුම" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "දුරස්ථ-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "දුරස්ථ-ID යතුරු අගය" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "උපාංගයකින් ස්ථිරාංග බ්ලොබ් කියවන්න" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "උපාංගයකින් ස්ථිරාංග කියවන්න" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "උපාංගයෙන් ස්ථිරාංග ගොනුවකට කියවන්න" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "ස්ථිරාංග එක් කොටසකින් ගොනුවකට කියවන්න" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "%s වෙතින් කියැවෙමින්…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "කියවෙමින්…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "නැවත ඇරඹෙමින්…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "දුරස්ථ සේවාදායකයෙන් පාර-දත්ත නැවුම් කරන්න" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "%s සිට %sදක්වා නැවත ස්ථාපනය කරන්නද?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "උපාංගයේ වත්මන් ස්ථිරාංග නැවත ස්ථාපනය කරන්න" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "උපාංගයක ස්ථිරාංග නැවත ස්ථාපනය කරන්න" + +#. TRANSLATORS: the 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 "%s සමඟ %s යළි ස්ථාපනය වෙමින්... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "නිදහස් ශාඛාව" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "කොඩි නිකුත් කරන්න" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "නිදහස් හැඳුනුම්පත" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "දුරස්ථ හැඳුනුම්පත" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "පවතින ස්ථිරාංග ගොනුවක දත්ත ප්‍රතිස්ථාපනය කරන්න" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI වාර්තා කරන්න" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "දුරස්ථ සේවාදායකය වෙත වාර්තා කරන ලදී" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "අවශ්‍ය efivarfs ගොනු පද්ධතිය සොයාගත නොහැකි විය" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "අවශ්‍ය දෘඩාංග හමු නොවීය" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "ඇරඹුම් කාරකයක් අවශ්‍ය වේ" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "අන්තර්ජාල සම්බන්ධතාව අවශ්‍යයි" + +#. 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 "යන්ත්‍රය සඳහා සියලුම දෘඪාංග හැඳුනුම්පත් ආපසු දෙන්න" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "ආපසු හැරවීමේ ආරක්ෂාව" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "වැඩි විස්තර සඳහා `fwupdmgr get-upgrades` ධාවනය කරන්න." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "මෙම ක්‍රියාව සම්පූර්ණ කිරීමට `fwupdmgr sync-bkc` ධාවනය කරන්න." + +#. 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 "ස්ථාපනය-බ්ලොබ් භාවිතා කරන විට ප්ලගින සංයුක්ත සූදානම් කිරීමේ දින චර්යාව ධාවනය කරන්න" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "ධාවන කර්නලය පරණ වැඩියි" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "ධාවන කාල උපසර්ගය" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS විස්තරකය" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS කලාපය" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI අගුල" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "SPI නැවත ධාවනය ආරක්ෂාව" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI ලියන්න" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "SPI ලිවීමේ ආරක්ෂාව" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "උප පද්ධති ධාවකය [උපාංගය-ID|මාර්ගෝපදේශය]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "දෘඪාංග හැඳුනුම්පත් ජනනය කිරීමට ඉඩ දෙන ගොනුවක් සුරකින්න" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "හැකි විට ඊළඟ නැවත පණගැන්වීම සඳහා ස්ථාපනය කාලසටහන්ගත කරන්න" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "උපලේඛනගත කිරීම…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "ආරක්ෂිත ඇරඹුම් අක්‍රීය කර ඇත" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "ආරක්ෂිත ඇරඹුම් සක්රිය කර ඇත" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "වැඩි විස්තර සඳහා %s බලන්න." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "තව තොරතුරු සඳහා %s බලන්න." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "තෝරාගත් උපාංගය" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "තෝරාගත් පරිමාව" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "අන්රක්රමික අංකය" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +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 all results" +msgstr "සියළුම ප්‍රතිඵල පෙන්වන්න" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "සේවාදායක සහ ඩීමන් අනුවාද පෙන්වන්න" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "විශේෂිත වසමක් සඳහා ඩේමන් වාචික තොරතුරු පෙන්වන්න" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "සියලුම වසම් සඳහා නිදොස් කිරීමේ තොරතුරු පෙන්වන්න" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "නිදොස් කිරීමේ විකල්ප පෙන්වන්න" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "යාවත්කාලීන කළ නොහැකි උපාංග පෙන්වන්න" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "අමතර දෝශ නිරාකරණ තොරතුරු පෙන්වන්න" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "ස්ථිරාංග යාවත්කාලීන ඉතිහාසය පෙන්වන්න" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "ප්ලගින වාචික තොරතුරු පෙන්වන්න" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "dbx හි ගණනය කළ අනුවාදය පෙන්වන්න" + +#. 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 "දැන් වසා දමන්නද?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "නව යතුරක් සමඟ ස්ථිරාංගයක් අත්සන් කරන්න" + +msgid "Sign data using the client certificate" +msgstr "සේවාදායක සහතිකය භාවිතයෙන් දත්ත අත්සන් කරන්න" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "සේවාදායක සහතිකය භාවිතයෙන් දත්ත අත්සන් කරන්න" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "උඩුගත කළ දත්ත සේවාදායක සහතිකය සමඟ අත්සන් කරන්න" + +msgid "Signature" +msgstr "අත්සන" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "අත්සන් කළ පේලෝඩ්" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "ප්රමාණය" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "මෙම ස්ථිරාංග යාවත්කාලීන කිරීමේදී සමහර වේදිකා රහස් අවලංගු විය හැක." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "මූලාශ්රය" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "DFU උපාංගයේ විකුණුම්කරු/නිෂ්පාදන ID(s) සඳහන් කරන්න" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "dbx දත්ත සමුදා ගොනුව සඳහන් කරන්න" + +msgid "Specify the number of bytes per USB transfer" +msgstr "USB හුවමාරුවකට බයිට් ගණන සඳහන් කරන්න" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "සාර්ථකත්වය" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "සියලුම උපාංග සාර්ථකව සක්‍රිය කර ඇත" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "දුරස්ථ පාලකය සාර්ථකව අබල කරන ලදී" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "නව පාරදත්ත සාර්ථකව බාගන්නා ලදී: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "දුරස්ථ පාලකය සාර්ථකව සබල කර නැවුම් කරන ලදී" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "දුරස්ථ පාලකය සාර්ථකව සබල කර ඇත" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "ස්ථිරාංග සාර්ථකව ස්ථාපනය කර ඇත" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "වින්‍යාස අගය සාර්ථකව වෙනස් කරන ලදී" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "දුරස්ථ පාලකය සාර්ථකව වෙනස් කරන ලදී" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "පාරදත්ත අතින් සාර්ථකව නැවුම් කරන ලදී" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "උපාංග චෙක්සම් සාර්ථකව යාවත්කාලීන කරන ලදී" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "වාර්තාව %u ක් සාර්ථකව උඩුගත කරන ලදී" +msgstr[1] "වාර්තා %u ක් සාර්ථකව උඩුගත කරන ලදී" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "උපාංග චෙක්සම් සාර්ථකව සත්‍යාපනය කරන ලදී" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "සාරාංශය" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "සහාය දක්වයි" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "සහය දක්වන CPU" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "දුරස්ථ සේවාදායකයේ සහය දක්වයි" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "අත්හිටුවීම-නිෂ්ක්රීය කිරීම" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "සස්පෙන්ඩ්-ට-රැම්" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "ශාඛාව %s සිට %sදක්වා මාරු කරන්නද?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "උපාංගයේ ස්ථිරාංග ශාඛාව මාරු කරන්න" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "ස්ථිරාංග අනුවාද ධාරක වඩාත් දන්නා වින්‍යාසය වෙත සමමුහුර්ත කරන්න" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "යාවත්කාලීන කිරීම සිදු කිරීමට පද්ධතියේ බලය ඉතා අඩුය" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "යාවත්කාලීන කිරීම සිදු කිරීමට පද්ධතියේ බලය ඉතා අඩුය (%u%%, අවශ්‍ය වන්නේ %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "පද්ධතියට බාහිර බලශක්ති ප්රභවයක් අවශ්ය වේ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "පෙළ" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 ප්‍රතිසංස්කරණය" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0 ප්‍රතිනිර්මාණය වලංගු නැත" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "TPM PCR0 ප්‍රතිනිර්මාණය දැන් වලංගුයි" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "TPM හිස් PCRs" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM අනු:2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "ටැග් කරන්න" +msgstr[1] "ටැග්" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "කිලිටි වෙලා" + +msgid "Target" +msgstr "ඉලක්කය" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "JSON මැනිෆෙස්ටයක් භාවිතයෙන් උපාංගයක් පරීක්ෂා කරන්න" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 ප්‍රතිසංස්කරණයෙන් වෙනස් වේ." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "ඩීමන් 3වන පාර්ශ්ව කේතය පූරණය කර ඇති අතර උඩුගං බලා සංවර්ධකයින් විසින් තවදුරටත් සහාය නොදක්වයි!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "උපාංග අනුවාදය නොගැලපේ: ලැබුණේ %s, අපේක්ෂිත %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "%s සිට ස්ථිරාංග සපයනු ලබන්නේ දෘඪාංග වෙළෙන්දා වන %sවිසිනි." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "පද්ධති ඔරලෝසුව නිවැරදිව සකසා නොමැති අතර ගොනු බාගත කිරීම අසාර්ථක විය හැක." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "වෙළෙන්දා නිකුත් කිරීමේ සටහන් කිසිවක් සපයා නැත." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "ගැටළු සහිත උපාංග තිබේ:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "අවහිර කළ ස්ථිරාංග ගොනු නොමැත" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "අනුමත ස්ථිරාංග නොමැත." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "%s විධානය ක්‍රියාත්මක කරන විට මෙම උපාංගය නැවත %s වෙත ප්‍රතිවර්තනය වේ." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "මෙම ස්ථිරාංග සපයනු ලබන්නේ LVFS ප්‍රජා සාමාජිකයින් විසින් වන අතර මුල් දෘඪාංග වෙළෙන්දා විසින් සපයනු නොලැබේ (හෝ සහාය නොදක්වයි)." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "මෙම පැකේජය වලංගු කර නැත, එය නිවැරදිව ක්‍රියා නොකරනු ඇත." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "මෙම වැඩසටහන නිවැරදිව ක්‍රියා කළ හැක්කේ root ලෙස පමණි" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "මෙම දුරස්ථ පාලකයේ සම්බාධක නොමැති ස්ථිරාංග අඩංගු නමුත් දෘඪාංග වෙළෙන්දා විසින් තවමත් පරීක්ෂා කරනු ලැබේ. ස්ථිරාංග යාවත්කාලීන කිරීම අසාර්ථක වුවහොත් ස්ථිරාංග හස්තීයව පහත හෙළීමට ඔබට ක්‍රමයක් ඇති බව ඔබ සහතික විය යුතුය." + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "මෙම පද්ධතියට HSI ධාවන කාල ගැටළු ඇත." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "මෙම පද්ධතියට අඩු HSI ආරක්ෂක මට්ටමක් ඇත." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "මෙම මෙවලම පරිපාලකයෙකුට UEFI dbx යාවත්කාලීන යෙදීමට ඉඩ සලසයි." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "මෙම මෙවලම පරිපාලකයෙකුට UpdateCapsule මෙහෙයුම නිදොස් කිරීමට ඉඩ දෙයි." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "මෙම මෙවලම පරිපාලකයෙකුට fwupd daemon විමසා පාලනය කිරීමට ඉඩ සලසයි, ස්ථිරාංග ස්ථාපනය කිරීම හෝ පහත හෙලීම වැනි ක්‍රියා සිදු කිරීමට ඔවුන්ට ඉඩ සලසයි." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "මෙම මෙවලම පරිපාලකයෙකුට ධාරක පද්ධතියේ ස්ථාපනය නොකර fwupd ප්ලගීන භාවිතා කිරීමට ඉඩ සලසයි." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "මෙම මෙවලම භාවිතා කළ හැක්කේ root පරිශීලකයාට පමණි" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "මෙම මෙවලම පද්ධති ස්ථිරාංගයෙන් TPM සිදුවීම් ලොගය කියවා විග්‍රහ කරයි." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "තාවකාලික අසාර්ථකත්වය" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "විශ්වාසදායක පාරදත්ත" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "විශ්වාසනීය ගෙවීම" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "ටයිප් කරන්න" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP කොටස අනාවරණය කර හෝ වින්‍යාස කර නොමැත" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI ස්ථිරාංග උපයෝගිතා" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI කැප්සියුල යාවත්කාලීන ලබා ගත නොහැක හෝ ස්ථිරාංග සැකසුම තුළ සබල කර ඇත" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx උපයෝගිතා" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI ස්ථිරාංග පැරණි BIOS ආකාරයෙන් යාවත්කාලීන කළ නොහැක" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI වේදිකා යතුර" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI ආරක්ෂිත ඇරඹුම" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "සේවාවට සම්බන්ධ විය නොහැක" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "වත්මන් ධාවකය ඉවත් කරන්න" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "ස්ථිරාංග අවහිර කිරීම ඉවත් කිරීම:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "නිශ්චිත ස්ථිරාංගයක් ස්ථාපනය කිරීමෙන් අවහිර කිරීම ඉවත් කරයි" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "විසංකේතිතයි" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "නොදනී" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "හඳුනානොගත් උපකරණය" + +msgid "Unlock the device to allow access" +msgstr "ප්‍රවේශයට ඉඩ දීමට උපාංගය අගුළු හරින්න" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "අගුළු හැර ඇත" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "ස්ථිරාංග ප්‍රවේශය සඳහා උපාංගය අගුළු හරියි" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "ESP ගලවයි" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "යාවත්කාලීන කිරීමේදී දෝශ නිරාකරණ ධජය නොසකසන්න" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "අත්සන් නොකළ ගෙවීම" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "සහාය නොදක්වන ඩීමන් අනුවාදය %s, සේවාදායක අනුවාදය %sවේ" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "අපිරිසිදු" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "යාවත්කාලීන කළ හැකි" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "යාවත්කාලීන දෝෂයකි" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "රූපය යාවත්කාලීන කරන්න" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "පණිවිඩය යාවත්කාලීන කරන්න" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "තත්වය යාවත්කාලීන කරන්න" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "යාවත්කාලීන අසාර්ථක වීම දන්නා ගැටළුවකි, වැඩිදුර තොරතුරු සඳහා මෙම URL වෙත පිවිසෙන්න:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "දැන් යාවත්කාලීන කරන්න?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "යාවත්කාලීන කිරීමට නැවත පණගැන්වීමක් අවශ්‍ය වේ" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "ගබඩා කර ඇති ගුප්ත ලේඛන හැෂ් වත්මන් ROM අන්තර්ගතය සමඟ යාවත්කාලීන කරන්න" + +msgid "Update the stored device verification information" +msgstr "ගබඩා කර ඇති උපාංග සත්‍යාපන තොරතුරු යාවත්කාලීන කරන්න" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "ගබඩා කර ඇති පාරදත්ත වත්මන් අන්තර්ගතය සමඟ යාවත්කාලීන කරන්න" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "සියලුම නිශ්චිත උපාංග නවතම ස්ථිරාංග අනුවාදය වෙත යාවත්කාලීන කරයි, හෝ නිශ්චිතව දක්වා නොමැති නම් සියලුම උපාංග" + +msgid "Updating" +msgstr "යාවත්කාලීන කිරීම" + +#. TRANSLATORS: 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 "%s %s සිට %sදක්වා යාවත්කාලීන කරමින් ... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s යාවත්කාල වෙමින්…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "%s %s සිට %sදක්වා උත්ශ්‍රේණි කරන්නද?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload 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: how important the release is +msgid "Urgency" +msgstr "හදිසිය" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "උදව් සඳහා fwupdmgr --help භාවිතා කරන්න" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "උදව් සඳහා fwupdtool --help භාවිතා කරන්න" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "ස්ථිරාංග ස්ථාපනය කිරීමේදී quirk flags භාවිතා කරන්න" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "පරිශීලකයාට දැනුම් දී ඇත" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "පරිශීලක නාමය" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "වලංගුයි" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "ESP අන්තර්ගතයන් වලංගු කිරීම…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "ප්රභේදය" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "වෙළෙන්දා" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "සත්‍යාපනය…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "අනුවාදය" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "අවවාදය:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "රැඳෙමින්…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "දෘඪාංග වෙනස්කම් සඳහා නරඹන්න" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "ගොනුවේ සිට උපාංගයට ස්ථිරාංග ලියන්න" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "ගොනුවකින් ස්ථිරාංග එක් කොටසකට ලියන්න" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "ලිපිගොනු ලිවීම:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "ලියවෙමින්…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "ඔබේ බෙදාහරින්නා ඔබේ පද්ධතිය හෝ සම්බන්ධිත උපාංග සමඟ ගැළපීම සඳහා ස්ථිරාංග යාවත්කාලීන කිසිවක් සත්‍යාපනය කර නොතිබිය හැකිය." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "මෙම ස්ථිරාංග භාවිතයෙන් ඔබේ දෘඪාංගයට හානි සිදු විය හැකි අතර, මෙම නිකුතුව ස්ථාපනය කිරීමෙන් %sසමඟ ඇති ඕනෑම වගකීමක් අවලංගු විය හැක." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "ඔබේ පද්ධතිය %sහි BKC ලෙස සකසා ඇත." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[DEVICE-ID|මාර්ගෝපදේශය]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[DEVICE-ID|GUID] [BRANCH]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[DEVICE-ID|GUID] [VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FILE FILE_SIG දුරස්ථ-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[FILENAME1] [FILENAME2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FILE|HWIDS-FILE]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "පෙරනිමිය" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM සිදුවීම් ලොග් උපයෝගීතාව" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd ප්ලගීන" diff --git a/fwupd-1.8.6/po/sk.po b/fwupd-1.8.6/po/sk.po new file mode 100644 index 0000000000000000000000000000000000000000..7c15dd3892115b11d61ee5d074e84799318288ea --- /dev/null +++ b/fwupd-1.8.6/po/sk.po @@ -0,0 +1,232 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Dušan Kazik , 2015-2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: sk\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Prezývka príkazu %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Umožní zníženie verzií firmvéru" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Vyžaduje sa overenie totožnosti na prechod na staršiu verziu firmvéru vymeniteľného zariadenia" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Vyžaduje sa overenie totožnosti na prechod na staršiu verziu firmvéru v tomto počítači" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Na odomknutie zariadenia sa vyžaduje overenie totožnosti" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Vyžaduje sa overenie totožnosti na aktualizovanie firmvéru vymeniteľného zariadenia " + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Na aktualizovanie firmvéru v tomto počítači je potrebné overenie totožnosti" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Zrušené" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Zmenené" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrolný medzisúčet" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Vymaže výsledky z poslednej aktualizácie" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Príkaz nenájdený" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Nástroj pre DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Voľby ladenia" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Popis" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Pridané zariadenie:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Zmenené zariadenie:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Odstránené zariadenie:" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Hotovo!" + +#. 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 "Vracia sa %s z verzie %s na verziu %s... " + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Skončí po krátkom oneskorení" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Skončí po načítaní jadra" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Zlyhalo analyzovanie parametrov" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Služba zbernice D-Bus na aktualizovanie firmvéru" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Démon aktualizácie firmvéru" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Nástroj pre firmvéry" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Nájdené" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Získa všetky zariadenia, ktoré podporujú aktualizovanie firmvéru" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Získa podrobnosti o súbore firmvéru" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Získa zoznam aktualizácií pre pripojený hardvér" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Získa výsledky z poslednej aktualizácie" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Nainštaluje súbor firmvéru do tohoto hardvéru" + +msgid "Install signed device firmware" +msgstr "Nainštaluje podpísaný firmvér zariadenia" + +msgid "Install signed system firmware" +msgstr "Nainštaluje podpísaný firmvér systému" + +msgid "Install unsigned device firmware" +msgstr "Nainštaluje nepodpísaný firmvér zariadenia" + +msgid "Install unsigned system firmware" +msgstr "Nainštaluje nepodpísaný firmvér systému" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Sleduje démona kvôli udalostiam" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Nezistil sa žiadny hardvér s možnosťou aktualizácie firmvéru" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Prečíta firmvér zo zariadenia do súboru" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Prečíta firmvér z jedného oddielu do súboru" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Obnoví metaúdaje zo vzdialeného servera" + +#. 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 "Preinštalováva sa %s verziou %s... " + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Zobrazí voľby ladenia" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Zobrazovať dodatočné ladiace informácie" + +msgid "Unlock the device to allow access" +msgstr "Odomknúť zariadenie na umožnenie prístupu" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Odomkne zariadenie pre prístup k firmvéru" + +#. 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 "Aktualizuje sa %s z verzie %s na verziu %s... " + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Verzia" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Zapíše firmvér zo súboru do zariadenia" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Zapíše firmvér zo súboru do jedného oddielu" diff --git a/fwupd-1.8.6/po/sr.po b/fwupd-1.8.6/po/sr.po new file mode 100644 index 0000000000000000000000000000000000000000..d9dd52bc3972c10688cab38b5fd64f3dc0658477 --- /dev/null +++ b/fwupd-1.8.6/po/sr.po @@ -0,0 +1,490 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Miloš Popović , 2016 +# Марко М. Костић (Marko M. Kostić) , 2015-2018 +# Мирослав Николић , 2017 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: sr\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Старост" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Алијас на %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Дозволи уназађивање издања фирмвера" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Потребно је поново покретање да би се исправка применила." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Одговори са да на сва питања" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Идентификујем…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Потребна је пријава за уназађивање фирмвера на преносивом уређају" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Потребна је пријава за уназађивање фирмвера на овој машини" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Идентификовање је потребно за измену подешеног удаљеног сервера који се користи за ажурирања фирмвера" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Потребна је пријава за откључавање уређаја" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Потребна је пријава за ажурирање фирмвера на преносивом уређају" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Потребна је пријава за ажурирање фирмвера на овој машини" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Потребна је пријава за ажурирање причуваних сума провере за уређај" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Отказао" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Променио" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Чек-сума" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Изаберите уређај:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Изаберите издање:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Чисти резултате последњег ажурирања" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Наредба није пронађена" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "ДФУ алатка" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Опције отклањања проблема" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Распакујем…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Опис" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Додат је уређај:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Промењен је уређај:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Уклоњен је уређај:" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Уређаји који су ажурирани исправно:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Уређаји који нису ажурирани исправно:" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Не проверавај старе метаподатке" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Не проверавај непослати историјат" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Урађено!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +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..." + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Преузимам…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Ишчитај SMBIOS податке из датотеке" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Омогућено" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Обриши сав историјат ажурирања фирмвера" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Бришем…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Изађи након малог застоја" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Изађи након учитавања мотора" + +#. TRANSLATORS: 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: filename of the local file +msgid "Filename" +msgstr "Назив датотеке" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Потпис назива датотеке" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Основни URI фирмвера" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Д-Бус услуга ажурирања фирмвера" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Демон за ажурирање фирмвера" + +#. TRANSLATORS: program name +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 дан и можда су застарели." +msgstr[1] "Метаподаци фирмвера нису ажурирани %u дана и можда су застарели." +msgstr[2] "Метаподаци фирмвера нису ажурирани %u дана и можда су застарели." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Нашао" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "ГУИД" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Добави све уређаје који подржавају ажурирање фирмвера" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Добави појединости о датотеци са фирмвером" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Добавља подешена удаљена места" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Добави списак свих ажурирања за повезани уређај" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Добавља издања за уређај" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Добавља резултате последњег ажурирања" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Мирујем…" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "Инсталирај датотеку са фирмвером на овај уређај" + +msgid "Install signed device firmware" +msgstr "Инсталирајте потписани фирмвер за уређаје" + +msgid "Install signed system firmware" +msgstr "Инсталирајте потписани системски фирмвер" + +msgid "Install unsigned device firmware" +msgstr "Инсталирајте непотписани фирмвер за уређаје" + +msgid "Install unsigned system firmware" +msgstr "Инсталирајте непотписани системски фирмвер" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Инсталирам ажурирање фирмвера…" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Привезак" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Учитавам…" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI метаподатака" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Мења дати удаљени сервер" + +msgid "Modify a configured remote" +msgstr "Измени подешени удаљени сервер" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Прати демона за догађајима" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Нема хардвера којем се може ажурирати фирмвер" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "У реду" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Лозинка" + +msgid "Payload" +msgstr "Товар" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Важност" + +msgid "Proceed with upload?" +msgstr "Наставити са отпремањем?" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Исчитај фирмвер са уређаја у датотеку" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Исчитај фирмвер са једне партиције у датотеку" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Читам…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Освежава метаподатке са удаљеног сервера" + +#. 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 "Поново инсталирам %s са %s..." + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Удаљени ИБ" + +#. TRANSLATORS: command description +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: reboot to apply the update +msgid "Restart now?" +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 "Враћа све ИБ-јеве хардвера на машини" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Заказује инсталирање за следеће подизање система када је могуће" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Заказујем…" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Подели историјат фирмвера са програмерима" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Прикажи издања клијента и демона" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +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: one line summary of device +msgid "Summary" +msgstr "Сажетак" + +msgid "Target" +msgstr "Мета" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Врста" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Непознато" + +msgid "Unlock the device to allow access" +msgstr "Откључајте уређај да бисте дозволили приступ" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Откључава уређај за приступ фирмверу" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Узрок неуспеха ажурирања је познат, погледајте ову адресу за више података:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Ажурирати сада?" + +msgid "Update the stored device verification information" +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 "Updating %s from %s to %s... " +msgstr "Ажурирам %s са %s на %s..." + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Отпремити извештај сада?" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Корисничко име" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Проверавам…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Издање" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Чекам…" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Упиши фирмвер из датотеке у уређај" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Упиши фирмвер из датотеке у једну партицију" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Пишем…" diff --git a/fwupd-1.8.6/po/sv.po b/fwupd-1.8.6/po/sv.po new file mode 100644 index 0000000000000000000000000000000000000000..c6bf3994e49bb75af22d18db12466dd015b31318 --- /dev/null +++ b/fwupd-1.8.6/po/sv.po @@ -0,0 +1,3084 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Anders Jonsson , 2017,2019-2022 +# Andreas Henriksson , 2017 +# Josef Andersson , 2015,2017-2018 +# Josef Andersson , 2015,2017 +# Luna Jernberg , 2020-2021 +# Sebastian Rasmussen , 2018-2020 +# Sebastian Rasmussen , 2018 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: sv\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f minut kvarstår" +msgstr[1] "%.0f minuter kvarstår" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC-uppdatering" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s-batteriuppdatering" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU-mikrokodsuppdatering" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s-kamerauppdatering" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s-konfigurationsuppdatering" + +#. 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-uppdatering" + +#. 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-styrenhetsuppdatering" + +#. 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-uppdatering" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s-enhetsuppdatering" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s-skärmuppdatering" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s-enhetsuppdatering" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s inbäddad styrenhetsuppdatering" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "%s-flashenhetsuppdatering" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s-tangentbordsuppdatering" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME-uppdatering" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s-musuppdatering" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s-nätverksgränssnittsuppdatering" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s SSD-uppdatering" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s-lagringsstyrenhetsuppdatering" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s-systemuppdatering" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM-uppdatering" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt-styrenhetsuppdatering" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s-pekplatteuppdatering" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s USB-mottagaruppdatering" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s-uppdatering" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s och alla anslutna enheter kanske inte går att använda under uppdatering." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s dök upp: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s ändrad: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s försvann: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s går för tillfället inte uppdatera" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s-tillverkningsläge" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s måste förbli anslutna under tiden som uppdateringen pågår för att undvika skador." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s måste förbli ansluten till en strömkälla under tiden som uppdateringen pågår för att undvika skada." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s-åsidosättning" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s-version" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u dag" +msgstr[1] "%u dagar" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u enhet har en uppgradering för fast programvara tillgänglig." +msgstr[1] "%u enheter har en uppgradering för fast programvara tillgänglig." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u enhet har inte den mest kända konfigurationen." +msgstr[1] "%u enheter har inte den mest kända konfigurationen." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u timme" +msgstr[1] "%u timmar" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u lokal enhet stöds" +msgstr[1] "%u lokala enheter stöds" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u minut" +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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (tröskelvärde %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(föråldrad)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "Ett TPM PCR har nu ett ogiltigt värde" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "Återuppspelningsskydd för AMD:s fasta programvara" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "Skrivskydd för AMD:s fasta programvara" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "AMD-tillbakarullningsskydd" + +#. TRANSLATORS: longer description +msgid "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "AMD-tillbakarullningsskydd förhindrar enhetsprogramvara från att nedgraderas till en äldre version som har säkerhetsproblem." + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Åtgärd krävs:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktivera enheter" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktivera väntande enheter" + +msgid "Activate the new firmware on the device" +msgstr "Aktiverar den nya fasta programvaran på enheten" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktiverar uppdatering av fast programvara" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktiverar uppdatering av fast programvara för" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Ålder" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Samtyck och aktivera fjärrkällan?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Alias för %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Alla TPM PCR är nu giltiga" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Alla TPM PCR är giltiga" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Alla enheter av samma typ kommer uppdateras samtidigt" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Tillåt att nedgradera versioner av fast programvara" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Tillåt ominstallation av befintliga versioner av fast programvara" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Tillåt att byta gren för fast programvara" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Alternativ gren" + +#. 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 slutföras." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "En uppdatering kräver att systemet stängs ned för att slutföras." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Svara ja på alla frågor" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Tillämpa uppdateringar för fast programvara" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Tillämpa uppdatering även när det inte rekommenderas" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Tillämpa uppdateringsfiler" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Tillämpar uppdatering…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Godkänd fast programvara:" +msgstr[1] "Godkänd fast programvara:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Fråga igen nästa gång?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Fäst till fast programvaruläge" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Autentiserar…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Autentiseringsdetaljer krävs" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Autentisering krävs för att nedgradera den fasta programvaran för en flyttbar enhet" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Autentisering krävs för att nedgradera den fasta programvaran på denna maskin" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Autentisering krävs för att ändra BIOS-inställningar" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Autentisering krävs för att ändra en konfigurerad fjärrkälla som används för uppdateringar av fast programvara" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Autentisering krävs för att modifiera demonkonfiguration" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "Autentisering krävs för att läsa BIOS-inställningar" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Autentisering krävs för att sätta listan över godkänd fast programvara" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Autentisering krävs för att signera data med klientcertifikatet" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Autentisering krävs för att växla till den nya fasta programvaruversionen" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Autentisering krävs för att låsa upp en enhet" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Autentisering krävs för att uppdatera den fasta programvaran på en flyttbar enhet" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Autentisering krävs för att uppdatera den fasta programvaran för denna maskin" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Autentisering krävs för att uppdatera lagrade kontrollsummor för enheten" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Automatisk rapportering" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Skicka automatiskt varje gång?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "BIOS-uppdateringar levererade via LVFS eller Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BYGGAR-XML FILNAMN-MÅL" + +msgid "BYTES" +msgstr "BYTE" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Batteri" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Bind ny kärndrivrutin" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Blockerade fast programvarufiler:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Blockerad version" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Blockera fast programvara:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Blockerar en specifik fast programvara från att installeras" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Starthanterarversion" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Gren" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Bygg en fast programvarufil" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Avbryt" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "Avbruten" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Kan inte tillämpa eftersom dbx-uppdatering redan har tillämpats." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Kan inte tillämpa uppdateringar på live-media" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Ändrad" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Kontrollerar att kryptografisk hash matchar fast programvara" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Kontrollsumma" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Välj en gren:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Välj en enhet:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Välj en typ av fast programvara:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Välj en utgåva:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Välj en volym:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose the ESP:" +msgstr "Välj ESP:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Rensar resultaten från senaste uppdateringen" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Kommandot hittades inte" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Stöds av gemenskapen" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Föreslagen ändring av konfiguration" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "Konfigurationen är endast läsbar av systemadministratören" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Konvertera en fast programvarufil" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Skapad" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritisk" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Kryptografisk hashverifiering är tillgänglig" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Aktuellt värde" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Aktuell version" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ENHETS-ID|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU-verktyg" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Felsökningsalternativ" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Dekomprimerar…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Beskrivning" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Koppla från till starthanterarläge" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detaljer" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Avvik från den mest kända konfigurationen?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Enhetsflaggor" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Enhets-ID" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Enhet tillagd:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Enheten har för lite batteri" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Enheten har för lite batteri (%u%%, behöver %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Enhet kan återhämta sig från flashningsfel" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Enheten kan inte användas medan locket är stängt" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Enhet ändrad:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Fast programvara för enhet krävs för att ha en versionskontroll" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Enheten är emulerad" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Enhet är låst" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Enhet krävs för att installera alla tillgängliga utgåvor" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Enheten kan inte nås" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Enheten kan inte nås, eller är utanför den trådlösa räckvidden" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Enhet går använda under tiden som uppdateringen pågår" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Enheten väntar på att uppdateringen ska tillämpas" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Enhet borttagen:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Enheten behöver vara ansluten till nätspänning" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Enhetsprogramuppdateringar tillhandahålls för denna enhet." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Enhet genomför uppdatering i steg" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Enhet stöder byte till en annan gren av fast programvara" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Uppdateringsmetod för enhet" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Enhetsuppdatering kräver aktivering" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Enheten kommer säkerhetskopiera fast programvara innan den installeras" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Enhet kommer inte att dyka upp igen efter att uppdateringen färdigställs" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Enheter som har uppdaterats:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Enheter som inte uppdaterades korrekt:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Enheter utan tillgängliga uppdateringar för fast programvara: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Enheter med den senaste tillgängliga uppdateringen för fast programvara:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Hittade inga enheter med matchande GUID" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Inaktiverad" + +msgid "Disabled fwupdate debugging" +msgstr "Inaktiverade fwupdate-felsökning" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Inaktiverar en given fjärrkälla" + +#. 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" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Kontrollera inte ej rapporterad historik" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Kontrollera inte om fjärrkällor för hämtning ska aktiveras" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Varken kontrollera eller fråga om omstart efter uppdatering" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Inkludera inte loggdomänsprefix" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Inkludera inte tidsstämpelprefix" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Utför inte säkerhetskontroller för enheter" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Fråga inte om enheter" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Skriv inte till historikdatabasen" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Förstår du konsekvenserna av att byta gren av fast programvara?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Vill du inaktivera den här funktionen för framtida uppdateringar?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Vill du uppdatera den här fjärrkällan nu?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Vill du skicka rapporter automatiskt för framtida uppdateringar?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Fråga inte om autentisering (mindre detaljer kan visas)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Klar!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Nedgradera %s från %s till %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Nedgradera fast programvara på en enhet" + +#. 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 "Nedgraderar %s från %s till %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Nedgraderar %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Hämta en fil" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Hämtar…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Dumpa SMBIOS-data från en fil" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Tid" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Angiven ESP var inte giltig" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Varje system bör ha test för att säkerställa den fasta programvarans säkerhet." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Emulerad värd" + +msgid "Enable" +msgstr "Aktivera" + +#. 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Aktivera ny fjärrkälla?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Aktivera denna fjärrkälla?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Aktiverad" + +msgid "Enabled fwupdate debugging" +msgstr "Aktiverade fwupdate-felsökning" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Aktiverad om hårdvaran matchar" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Aktiverar en given fjärrkälla" + +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ärrkälla görs på egen risk." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Krypterad" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Krypterat RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Krypterad RAM gör det omöjligt för information som är lagrad på enhetsminnet att läsas om minneschipet tas bort och koms åt." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Har nått slutet på sin livstid" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Uppräkning" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Ta bort all historik för fast programvara" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Raderar…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Avsluta efter en kort fördröjning" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Avsluta efter att motorn har lästs in" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Exportera en fast programvarufilstruktur till XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Extrahera en fast programvaru-blob till avbilder" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FIL" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FIL [ENHETS-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "FILNAMN" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FILNAMN CERTIFIKAT PRIVAT-NYCKEL" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "FILNAMN ENHET-ALT-NAMN|ENHET-ALT-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "FILNAMN ENHET-ALT-NAMN|ENHET-ALT-ID [AVBILD-ALT-NAMN|AVBILD-ALT-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "FILNAMN ENHETS-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "FILNAMN AVSTÅND DATA [FAST-PROGRAMVARUTYP]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FILNAMN [ENHETS-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILNAMN [FAST-PROGRAMVARUTYP]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "FILNAMN-KÄL FILNAMN-MÅL [FAST-PROGRAMVARUTYP-KÄL] [FAST-PROGRAMVARUTYP-MÅL]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "FILNAMN|KONTROLLSUMMA1[,KONTROLLSUMMA2][,KONTROLLSUMMA3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Misslyckades" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Misslyckades med att tillämpa uppdatering" + +#. TRANSLATORS: error message for Windows +#, c-format +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Misslyckades med att ansluta till Windows-tjänst, säkerställ att den körs." + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Misslyckades med att ansluta till demon" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Misslyckades med att hämta väntande enheter" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Misslyckades med att installera uppdatering av fast programvara" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Misslyckades med att läsa in lokal dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Misslyckades med att läsa in speciallösning" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Misslyckades med att läsa in system-dbx" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Misslyckades med att låsa" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Misslyckades med att tolka argument" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Misslyckades med att tolka fil" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Misslyckades med att tolka --filter-flaggor" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Misslyckades med att tolka lokal dbx" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Misslyckades med att starta om" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Misslyckades med att sätta uppstartsläge" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Misslyckades med att validera ESP-innehåll" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Falskt" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Filnamn" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Filnamnssignatur" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Filnamnskälla" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Filnamn krävs" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtrera via en uppsättning av enhetsflaggor genom att använda ett ~-prefix för att exkludera, t.ex. ”internal,~needs-reboot”" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Attestering av fast programvara" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Attestering av fast programvara kontrollerar enhetsprogramvara med en referenskopia för att se att den inte har ändrats." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "BIOS-beskrivare för fast programvara" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "BIOS-beskrivare för fast programvara skyddar enhetens minne för fast programvara från att mixtras med." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "BIOS-region för fast programvara" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "BIOS-region för fast programvara skyddar enhetens minne för fast programvara från att mixtras med." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Fast programvara bas-URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "D-Bus-tjänst för uppdatering av fast programvara" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Uppdateringsdemon för fast programvara" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Verifiering av uppdateraren av fast programvara" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Verifiering av uppdateraren av fast programvara kontrollerar att programvaran som används för uppdatering inte har mixtrats med." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Uppdateringar för fast programvara" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Fast programvaruverktyg" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Skrivskydd för fast programvara" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Skrivskyddslås för fast programvara" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Skrivskydd för fast programvara skyddar enhetens minne för fast programvara från att mixtras med." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Attestering av fast programvara" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Fast programvara är redan blockerad" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Fast programvara är inte redan blockerad" + +#. 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 för fast programvara har inte uppdaterats på %u dag och kan vara inaktuell." +msgstr[1] "Metadata för fast programvara har inte uppdaterats på %u dagar och kan vara inaktuell." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Senaste uppdatering för metadata för fast programvara: %s sedan. Använd --force för att uppdatera igen." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Uppdateringar för fast programvara" + +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." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Uppdateringar för fast programvara är inaktiverade; kör ”fwupdmgr unlock” för att aktivera" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Flaggor" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Tvinga åtgärden genom att lätta på några kontroller vid körning" + +msgid "Force the action ignoring all warnings" +msgstr "Tvinga åtgärden, ignorera alla varningar" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Hittad" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Full diskkryptering upptäckt" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Krypteringshemligheter för hel disk kan göras ogiltiga vid uppdatering" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Plattform med säkring" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Plattform med säkring" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Erhåll BIOS-inställningar" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Hämta alla enhetsflaggor som stöds av fwupd" + +#. 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" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Ger de konfigurerade fjärrkällorna" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Hämtar värdens säkerhetsattribut" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Hämtar listan över godkänd fast programvara" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Hämtar listan över blockerad fast programvara" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Hämtar listan över uppdateringar för ansluten hårdvara" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Erhåll utgåvan för en enhet" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Hämtar resultaten från senaste uppdateringen" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FIL" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Hårdvara väntar på att bli utdragen/återinsatt" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Hög" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Säkerhetshändelser för värd" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "Värdsäkerhets-ID (HSI) stöds inte" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Säkerhets-ID för värd:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU-skydd" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "IOMMU-skydd förhindrar anslutna enheter från att komma åt ej auktoriserade delar av systemminnet." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU-enhetsskydd inaktiverat" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU-enhetsskydd aktiverat" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Inaktiv…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignorera strikta SSL-kontroller vid hämtning av filer" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ignorera fel i kontrollsumman för fast programvara" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ignorera fel i matchning av hårdvara för fast programvara" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ignorera säkerhetskontroller för giltighet" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ignorerar strikta SSL-kontroller, för att göra detta automatiskt i framtiden exportera DISABLE_SSL_STRICT i din miljö" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Installationstid" + +#. 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" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Installera specifik fast programvara på en enhet, alla möjliga enheter kommer också installeras när CAB-arkivet matchar" + +msgid "Install old version of signed system firmware" +msgstr "Installera en gammal version av signerad fast programvara för systemet" + +msgid "Install old version of unsigned system firmware" +msgstr "Installera en gammal version av osignerad fast programvara för systemet" + +msgid "Install signed device firmware" +msgstr "Installera signerad fast programvara för enhet" + +msgid "Install signed system firmware" +msgstr "Installera signerad fast programvara för systemet" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Installera först på föräldraenhet" + +msgid "Install unsigned device firmware" +msgstr "Installera osignerad fast programvara för enhet" + +msgid "Install unsigned system firmware" +msgstr "Installera osignerad fast programvara för systemet" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Installerar fast programvara…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Installerar uppdatering för fast programvara…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Installerar på %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Installation av denna uppdatering kan också ogiltigförklara eventuell garanti för enheten." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Heltal" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard ACM-skyddad" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM-skyddad" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Intel BootGuards felpolicy" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Intel BootGuards felpolicy säkerställer att enheten inte fortsätter starta om dess enhetsprogramvara har mixtrats med." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard-säkring" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP-säkring" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Intel BootGuard verifierad uppstart" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard-felpolicy" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard förhindrar ej auktoriserad enhetsprogramvara från att köras då enheten startas." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard verifierad uppstart" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET aktiv" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET stöds" + +#. TRANSLATORS: longer description +msgid "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Intel Control-Flow Enforcement Technology upptäcker och förhindrar vissa metoder för att köra skadlig programvara på enheten." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Intel Management Engine-tillverkningsläge" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Intel Management Engine-åsidosättning" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "Intel Management Engine-åsidosättning inaktiverar kontroller om enhetens programvara mixtrats med." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Intel Management Engine-version" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: longer description +msgid "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Intel Supervisor Mode Access Prevention säkerställer att kritiska delar av enhetsminnet inte koms åt av mindre säkra program." + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Intern enhet" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Ogiltig" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Ogiltiga argument" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Är nedgradering" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Är i starthanterarläge" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Är uppgradering" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Problem" +msgstr[1] "Problem" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "NYCKEL,VÄRDE" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Kärnan är ej längre befläckad" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Kärnan är befläckad" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Lockdown för Linux-kärnan inaktiverad" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Lockdown för Linux-kärnan aktiverad" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Nyckelring" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "PLATS" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Senast ändrad" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Mindre än en minut kvarstår" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Licens" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Lockdown för Linux-kärnan" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Lockdown-läge för Linux-kärnan förhindrar administratörskonton (root) från att komma åt och ändra kritiska delar av systemprogramvara." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Linux-kärnans växlingsutrymme sparar tillfälligt information på disk under tiden du arbetar. Om informationen inte är skyddad skulle det vara möjligt för någon att komma åt den om de fick tag på disken." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Verifiering av Linux-kärnan" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "Verifiering av Linux-kärnan säkerställer att kritisk systemprogramvara inte har mixtrats med. Att använda enhetsdrivrutiner som inte tillhandahålls med systemet kan förhindra denna säkerhetsfunktion att fungera som den ska." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux-växlingsutrymme" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Vendor Firmware Service (stabil fast programvara)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Vendor Firmware Service (fast programvara för testning)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux-kärna" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Lockdown för Linux-kärnan" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux-växlingsutrymme" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Lista poster i dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Lista uppdateringar för fast programvara som stöds" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Lista de tillgängliga typerna av fast programvara" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Listar filer på ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Läser in…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Låst" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Låg" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI-tillverkningsläge" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI-åsidosättning" + +msgid "MEI version" +msgstr "MEI-version" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Aktivera manuellt specifika insticksmoduler" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Tillverkningsläge används när enheten tillverkas och säkerhetsfunktioner ännu inte aktiverats." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Största längd" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Högsta värde" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Medel" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metadatasignatur" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Metadata-URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Metadata kan hämtas från Linux Vendor Firmware Service." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimiversion" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Minsta längd" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Lägsta värde" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Demon och klient stämmer inte, använd %s istället" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Modifierar ett konfigurationsvärde för demonen" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Modifierar en given fjärrkälla" + +msgid "Modify a configured remote" +msgstr "Modifiera en konfigurerad fjärrkälla" + +msgid "Modify daemon configuration" +msgstr "Modifiera demonkonfiguration" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Övervaka demonen för händelser" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Monterar ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "OBS: Detta program kommer endast fungera korrekt som root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Kräver en omstart efter installation" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Kräver omstart" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Kräver en nedstängning efter installation" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Ny version" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Ingen åtgärd angiven!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Inga nedgraderingar för %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Inget fast programvaru-ID hittat" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "Inga utgåvor tillgängliga" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Inga fjärrkällor är aktiverade för närvarande, så ingen metadata är tillgänglig." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Inga fjärrkällor tillgängliga" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Inga enheter går att uppdatera" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Inga uppdateringar tillgängliga" + +msgid "No updates available for remaining devices" +msgstr "Inga uppdateringar tillgängliga för kvarvarande enheter" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Inga uppdateringar applicerades" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Inte godkänd" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "För lite data tillhandahölls för att göra en HSI-beräkning." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Hittades inte" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Stöds inte" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Visa endast enkelt PCR-värde" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Använd endast IPFS när du hämtar filer" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Endast versionsuppgraderingar är tillåtna" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Utmatning i JSON-format" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Åsidosätt standardsökväg för ESP" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "SÖKVÄG" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Tolka och visa detaljer om en fast programvarufil" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Tolkar dbx-uppdatering…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Tolkar system-dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Lösenord" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Patcha en fast programvaru-blob på ett känt avstånd" + +msgid "Payload" +msgstr "Nyttolast" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "Väntande" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Andel färdigställt" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Utför operationen?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Plattformsfelsökning" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "Plattformsfelsökning tillåter att enhetens säkerhetsfunktioner inaktiveras. Detta bör endast göras av hårdvarutillverkare." + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Se till att du har volymåterställningsnyckeln innan du fortsätter." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Ange en siffra mellan 0 och %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Beroenden för insticksmodul saknas" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Möjliga värden" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "DMA-skydd före uppstart" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "DMA-skydd före uppstart" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "DMA-skydd före uppstart är inaktiverat" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "DMA-skydd före uppstart är aktiverat" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "DMA-skydd före uppstart förhindrar enheter från att komma åt systemminne efter att de anslutits till datorn." + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Föregående version" + +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" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Problem" + +msgid "Proceed with upload?" +msgstr "Fortsätt med att skicka upp?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Processorsäkerhetskontroller" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Proprietär" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Fråga efter stöd för uppdatering av fast programvara" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "FJÄRR-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "FJÄRR-ID NYCKEL VÄRDE" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Skrivskyddad" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Läs en fast programvaru-blob från en enhet" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Läs om fast programvara från en enhet" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Läs fast programvara från enhet till fil" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Läs fast programvara från en partition till fil" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Läser från %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Läser…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Startar om…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Uppdatera metadata från fjärrserver" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Återinstallera %s till %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Återinstallera aktuell fast programvara på enheten" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Installera om fast programvara på en enhet" + +#. 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 "Återinstallerar %s med %s… " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Utgåvegren" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Utgåveflaggor" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Utgåva-ID" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Fjärrkälla-ID" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Ersätt data i en befintlig fast programvarufil" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Rapport-URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Rapporterat till fjärrserver" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Begäran avbruten" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Nödvändigt efivarfs-filsystem hittades inte" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Hårdvara som krävs hittades inte" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Kräver en starthanterare" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Kräver internetanslutning" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Starta om nu?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Starta om demonen för att göra så att ändringarna får effekt?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Startar om enhet…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Erhåll BIOS-inställningar. Om inga argument skickas med returneras alla inställningar" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Returnera alla hårdvaru-ID:n för maskinen" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Tillbakarullningsskydd" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Kör ”fwupdmgr get-upgrades” för vidare information." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Kör `fwupdmgr sync-bkc` för att slutföra denna åtgärd." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Kör uppstädningssammansättningsrutin för insticksmodulen när install-blob används" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Kör förberedelsesammansättningsrutin för insticksmodulen när install-blob används" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Kör utan ”%s” för att se" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Kärnan som körs är för gammal" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Exekveringssuffix" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "INSTÄLLNING VÄRDE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "INSTÄLLNING1 VÄRDE1 [INSTÄLLNING2] [VÄRDE2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS-beskrivare" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS-region" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI lås" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "SPI-återuppspelningsskydd" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI skriv" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "SPI-skrivskydd" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "UNDERSYSTEM DRIVRUTIN [ENHETS-ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Spara en fil som möjliggör generering av hårdvaru-ID:n" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Skalär ökning" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Schemalägg om möjligt installationen till nästa uppstart" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Schemalägger…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot inaktiverad" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Secure Boot aktiverad" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Se %s för mer detaljer." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Se %s för mer information." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Vald enhet" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Vald volym" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Serienummer" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Ställde in BIOS-inställningen ”%s” till ”%s”." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Ställ in en BIOS-inställning" + +msgid "Set one or more BIOS settings" +msgstr "Ställ in en eller flera BIOS-inställningar" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Ställ in felsökningsflaggan under uppdatering" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Ställer in en eller flera BIOS-inställningar" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Sätter listan av godkänd fast programvara" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Inställningstyp" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Inställningar kommer börja gälla efter systemet startats om" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Dela historik för fast programvara med utvecklarna" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Visa alla resultat" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Visa klient- och demon-version" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Visa utförlig information från demonen för en specifik domän" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Visa felsökningsinformation för alla domäner" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Visa felsökningsalternativ" + +#. 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 uppdateringar för fast programvara" + +#. 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 calculated version of the dbx" +msgstr "Visa den beräknade versionen av dbx" + +#. 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" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Stäng ner nu?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Signera en fast programvara med en ny nyckel" + +msgid "Sign data using the client certificate" +msgstr "Signera data med klientcertifikatet" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Signera data med klientcertifikatet" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Signera skickade data med klientcertifikatet" + +msgid "Signature" +msgstr "Signatur" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Signerad nyttolast" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Storlek" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Några av plattformshemligheterna kan göras ogiltiga när denna fasta programvara uppdateras." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Källa" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Ange Tillverkar-/Produkt-ID för DFU-enhet" + +#. TRANSLATORS: command line option +msgid "Specify a filename to use to save backend events" +msgstr "Ange ett filnamn att använda för att spara bakändeshändelser" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Ange dbx-databasfilen" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Ange antalet byte per USB-överföring" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Sträng" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Slutfördes" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Aktiverade framgångsrikt alla enheter" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Inaktiverade framgångsrikt fjärrkälla" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Hämtade framgångsrikt ny metadata: " + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Aktiverade och uppdaterade framgångsrikt fjärrkälla" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Aktiverade framgångsrikt fjärrkälla" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Installerade framgångsrikt fast programvara" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Modifierade framgångsrikt konfigurationsvärde" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Modifierade framgångsrikt fjärrkälla" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Uppdaterade framgångsrikt metadata manuellt" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Uppdaterade framgångsrikt enhetskontrollsummor" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Skickade framgångsrikt %u rapport" +msgstr[1] "Skickade framgångsrikt %u rapporter" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Verifierade framgångsrikt enhetskontrollsummor" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Sammanfattning" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Stöds" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "CPU som stöds" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Stöds på fjärrserver" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Gå till inaktivt läge" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Gå till vänteläge i RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Gå till inaktivt läge låter enheten snabbt sova för att spara ström. När enheten är i vänteläge skulle dess minne fysiskt kunna plockas bort och dess information kommas åt." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Gå till vänteläge i RAM låter enheten snabbt sova för att spara ström. När enheten är i vänteläge skulle dess minne fysiskt kunna plockas bort och dess information kommas åt." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Gå-till-inaktivt-läge" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Gå-till-vänteläge-i-ram" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Byt gren från %s till %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Byt gren för fast programvara på enheten" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Synkronisera versioner för fast programvara till den mest kända konfigurationen hos värden" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Systemet har för lite batteri för att utföra uppdateringen" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "Systemet har för lite batteri för att utföra uppdateringen (%u%%, behöver %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Systemet kräver extern strömkälla" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (Trusted Platform Module) är ett datorchip som upptäcker när hårdvarukomponenter har mixtrats med." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0-rekonstruktion" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0-rekonstruktion är ogiltig" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "TPM PCR0-rekonstruktion är nu giltig" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "TPM-plattformskonfiguration" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "TPM-rekonstruktion" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "TPM har tomma PCR" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Tag" +msgstr[1] "Taggar" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Befläckad" + +msgid "Target" +msgstr "Mål" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Testa en enhet med ett JSON-manifest" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "Intel Management Engine kontrollerar enhetskomponenter och behöver ha en tillräckligt ny version för att undvika säkerhetsproblem." + +#. 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 uppdateringarna för fast programvara är kompatibla med ditt system eller anslutna enheter. All fast programvara tillhandahålls endast av den ursprungliga tillverkaren av utrustning." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "Plattformskonfigurationen för TPM (Trusted Platform Module) används för att kontrollera om enhetens startprocess har ändrats." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "Rekonstruktionen för TPM (Trusted Platform Module) används för att kontrollera om enhetens startprocess har ändrats." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 skiljer sig från rekonstruktion." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "UEFI-plattformsnyckeln används för att avgöra om enhetsprogramvara kommer från en betrodd källa." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Demonen har läst in tredjepartskod och stöds inte längre av uppströmsutvecklarna!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Enhetsversionen matchade inte: fick %s, %s förväntades" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Fast programvara från %s levereras inte av %s, hårdvarutillverkaren." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Systemklockan har inte ställts in korrekt och hämtning av filer kan misslyckas." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Tillverkaren tillhandahöll inte några kommentarer till utgåvan." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Det finns enheter med problem:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Det finns inga blockerade fast programvarufiler" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Det finns ingen godkänd fast programvara." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Denna enhet kommer att återställas till %s när kommandot %s körs." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Denna fasta programvara tillhandahålls av medlemmar av LVFS-gemenskapen och varken tillhandahålls (eller stöds) av den ursprungliga hårdvarutillverkaren." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Detta paket har inte validerats, det kanske inte fungerar korrekt." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Detta program kommer endast fungera korrekt som root" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "Denna fjärrkälla innehåller fast programvara som inte är under embargo, men fortfarande testas av hårdvarutillverkare. Du bör säkerställa att du har ett sätt att manuellt nedgradera den fasta programvaran om uppdateringen misslyckas." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "Detta system stöder inte inställningar för fast programvara" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Detta system har problem med HSI-exekvering." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Detta system har en låg HSI-säkerhetsnivå." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Detta verktyg gör det möjligt för en administratör att tillämpa UEFI dbx-uppdateringar." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "Detta verktyg gör det möjligt för en administratör att felsöka en UpdateCapsule-operation." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Detta verktyg låter en administratör ställa frågor till och styra fwupd-demonen, vilket låter dem utföra åtgärder som att installera eller nedgradera fast programvara." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Detta verktyg tillåter en administratör att använda fwupd-insticksmoduler utan att dessa är installerade på värdsystemet." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "Detta verktyg kan automatiskt ändra BIOS-inställningen ”%s” från ”%s” till ”%s”, men den kommer aktiveras först efter att datorn startats om." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Detta verktyg kan endast användas av administratörsanvändaren" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Detta verktyg läser och analyserar TPM-händelseloggen från systemets fasta programvara." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Använd --force för att ignorera denna varning" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Tillfälligt misslyckande" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Sant" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Betrodda metadata" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Betrodd nyttolast" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Typ" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP-partition upptäcktes inte eller är inte konfigurerad" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Verktyg för fast UEFI-programvara" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI-plattformsnyckel" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI säkerhetsstart" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "UEFI säkerhetsstart förhindrar skadlig programvara från att läsas in när enheten startas." + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI-kapseluppdateringar är inte tillgängliga eller aktiverade i konfiguration för fast programvara" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx-verktyg" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Fast UEFI-programvara kan inte uppdateras i äldre BIOS-läge" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI-plattformsnyckel" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI säkerhetsstart" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Kunde inte ansluta till tjänst" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Kunde inte hitta attribut" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Lösgör aktuell drivrutin" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Avblockera fast programvara:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Avblockerar en specifik fast programvara från att installeras" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Okrypterad" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Okänd" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Okänd enhet" + +msgid "Unlock the device to allow access" +msgstr "Lås upp enheten för att tillåta åtkomst" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Upplåst" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Låser upp enheten för fast programvaruåtkomst" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Avmonterar ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Ta bort felsökningsflaggan under uppdatering" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Osignerad nyttolast" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Demonversion %s stöds inte, klientversionen är %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Obefläckad" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Uppdateringsbar" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Uppdateringsfel" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Uppdatera avbild" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Uppdateringsmeddelande" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Uppdateringstillstånd" + +#. 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:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Uppdatera nu?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Uppdatering kräver en omstart" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Uppdatera den lagrade kryptografiska hashen med aktuellt ROM-innehåll" + +msgid "Update the stored device verification information" +msgstr "Uppdatera den lagrade enhetens verifikationsinformation" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Uppdatera lagrad metadata med aktuellt innehåll" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Uppdaterar alla angivna enheter till senaste version av fast programvara, eller alla enheter om ej angivet" + +msgid "Updating" +msgstr "Uppdaterar" + +#. 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 "Uppdaterar %s från %s till %s… " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Uppdaterar %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Uppgradera %s från %s till %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Skicka upp 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 "Att skicka upp rapporter för fast programvara hjälper hårdvarutillverkare att snabbt identifiera trasiga och fungerande uppdateringar på riktiga enheter." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Viktighet" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Använd fwupdmgr --help för hjälp" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Använd fwupdtool --help för hjälp" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Använd specialflaggor vid installation av fast programvara" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Användare har aviserats" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Användarnamn" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Giltig" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Validerar ESP-innehåll…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Variant" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Tillverkare" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Verifierar…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Version" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "VARNING:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Väntar…" + +#. 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" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Skriv fast programvara från fil till partition" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Skriver fil:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Skriver…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Du bör säkerställa att du är bekväm med att återställa inställningen från systemets konfiguration för fast programvara, då denna ändring kan få systemet att inte kunna starta Linux eller orsaka annan systeminstabilitet." + +#. 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 fast programvara för kompatibilitet med ditt system eller anslutna enheter." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Din hårdvara kan skadas med den här fasta programvaran och att installera denna utgåva kan ogiltigförklara all garanti hos %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Ditt system är inställt till BKC för %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[KONTROLLSUMMA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ENHETS-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ENHETS-ID|GUID] [GREN]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ENHETS-ID|GUID] [VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FIL FILSIG FJÄRR-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[FILNAMN1] [FILNAMN2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[INSTÄLLNING1] [ INSTÄLLNING2]…" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[INSTÄLLNING1] [INSTÄLLNING2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FIL|HWIDS-FIL]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "standard" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM-händelseloggverktyg" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd-insticksmoduler" diff --git a/fwupd-1.8.6/po/test-deps b/fwupd-1.8.6/po/test-deps new file mode 100755 index 0000000000000000000000000000000000000000..45db97c5f4989869e779857f353630df27230756 --- /dev/null +++ b/fwupd-1.8.6/po/test-deps @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1+ + +""" Check dependencies needed for rasterization """ + +import sys +import os + +err = 0 + +try: + import gi +except ImportError: + print("Error: missing dependency python gobject introspection (python3-gi)") + err = 1 +try: + gi.require_version("Pango", "1.0") + from gi.repository import Pango +except ValueError: + print("Error: missing pango gobject introspection library") + err = 1 +try: + gi.require_version("PangoCairo", "1.0") + from gi.repository import PangoCairo +except ValueError: + print("Error: missing pangocairo gobject introspection library") + err = 1 + +try: + gi.require_version("cairo", "1.0") + from gi.repository import cairo +except ValueError: + print("Error: missing cairo gobject introspection library") + err = 1 + +try: + import cairo +except ImportError: + print("Error: missing dependency python cairo (python3-cairo)") + err = 1 + +# check that LINUGAS lists every language with a .po file +with open("po/LINGUAS") as f: + langs = f.read().splitlines() +for root, dirs, files in os.walk("po"): + for file in files: + if not file.endswith(".po"): + continue + l = file.split(".po") + if len(l) > 1 and not l[0] in langs: + err = 1 + print("Error: missing translations for %s" % l[0]) + +sys.exit(err) diff --git a/fwupd-1.8.6/po/tr.po b/fwupd-1.8.6/po/tr.po new file mode 100644 index 0000000000000000000000000000000000000000..7abd137ba09b85ae2683f8544f459476ef47b5dd --- /dev/null +++ b/fwupd-1.8.6/po/tr.po @@ -0,0 +1,1317 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Emin Tufan Çetin , 2020 +# Muhammet Kara , 2016 +# Sabri Ünal , 2019-2020,2022 +# Sabri Ünal , 2020 +# Serdar Sağlam , 2020 +# yunus kaba , 2021 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f dakika kaldı" +msgstr[1] "%.0f dakika kaldı" + +#. 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 Tüketici ME Güncellemesi" + +#. 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 Denetleyici Güncellemesi" + +#. 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 Kurumsal ME Güncellemesi" + +#. 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 Aygıt Güncellemesi" + +#. 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 Gömülü Denetleyici Güncellemesi" + +#. 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 Güncellemesi" + +#. 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 Sistem Güncellemesi" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt Denetleyici Güncellemesi" + +#. 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 Güncellemesi" + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s, hasar oluşmaması için güncelleme süresince güç kaynağına bağlı kalmalıdır." + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s sürümü" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u gün" +msgstr[1] "%u gün" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u saat" +msgstr[1] "%u saat" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +msgstr[0] "%u yerel cihaz destekleniyor" +msgstr[1] "%u yerel cihaz destekleniyor" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u dakika" +msgstr[1] "%u dakika" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u saniye" +msgstr[1] "%u saniye" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Etkin aygıtlar" + +msgid "Activate the new firmware on the device" +msgstr "Aygıttaki yeni donanım yazılımını etkinleştir" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Donanım yazılımı güncellemesini etkinleştir" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Yaş" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Takma ad %s" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Donanım yazılımı sürümünün düşürülmesine izin ver" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Varolan donanım yazılımı sürümlerinin yeniden kurulumuna izin ver" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Diğer kollara geçiş için izin verin" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Güncelleme işlemi için yeniden başlatma gerekir." + +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "Güncelleme işlemi için sistemin kapanması gerekir." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Tüm sorulara evet yanıtı ver" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "Donanım yazılımı güncellemelerini uygula" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Güncelleme dosyalarını uygula" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Onaylı ürün yazılımı:" +msgstr[1] "Onaylı donanım yazılımı:" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Kimlik doğrulanıyor…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Çıkarılabilir aygıt üzerindeki donanım yazılımının sürümünü düşürmek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Bu makine üzerindeki donanım yazılımının sürümünü düşürmek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Donanım yazılımı güncellemeleri için kullanılan uzak yapılandırmayı değiştirmek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Artalan süreci yapılandırmasını değiştirmek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Onaylı donanım yazılımı listesini ayarlamak için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "İstemci sertifikasını kullanarak veri imzası için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Bu makine üzerindeki donanım yazılımını güncellemek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "Bir aygıtın kilidini açmak için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Çıkarılabilir aygıt üzerindeki donanım yazılımını güncellemek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Bu makine üzerindeki donanım yazılımını güncellemek için kimlik doğrulama gerekir" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Aygıtın saklanan sağlama toplamlarını güncellemek için kimlik doğrulama gerekir" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Otomatik Raporla" + +msgid "BYTES" +msgstr "BYTES" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Önyükleyici Sürümü" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "İptal" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "İptal Edildi" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Değişti" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Sağlama Toplamı" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Dal seçiniz:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Bir aygıt seç:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Bir donanım yazılımı türü seç:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Sürüm seç:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Bölüm seçiniz:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Son güncellemenin sonuçlarını temizler" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Komut bulunamadı" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Kritik" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Var olan sürüm" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU Yardımcı Programı" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Hata Ayıklama Seçenekleri" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Açılıyor…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Açıklama" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Ayrıntılar" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Aygıt Bayrakları" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Aygıt Kimliği" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Aygıt eklendi:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Aygıt değişti:" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Aygıt kilitli" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Aygıt çıkarıldı:" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Aygıt güncelleme yöntemi" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Cihaz için güncelleme yok." + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Bu cihaz için en son güncelleme:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +msgstr "Devredışı" + +msgid "Disabled fwupdate debugging" +msgstr "fwupdate hata ayıklama devre dışı bırakıldı" + +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Ekran sürümü" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Eski üstverileri kontrol etme" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Bildirilmeyen geçmişi kontrol etme" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Günlük etki alanı öneki eklemeyin" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Zaman damgası öneki eklemeyin" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Cihaz güvenlik kontrolleri yapma" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Geçmişi veritabanına yazma" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Tamamlandı!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Bir aygıttaki üretici yazılımı sürümünü düşürür" + +#. 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ürümünden %s sürümüne düşürülüyor... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s sürümü düşürülüyor…" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "İndiriliyor…" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Süre" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Belirtilen ESP geçerli değil" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Desteklenen sistemlerde donanım yazılımı güncelleme desteğini etkinleştir" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Bu uzaktan kumanda etkinleştirilsin mi?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Etkinleştirildi" + +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 "Bu işlevselliğin etkinleştirilmesi kendi sorumluluğunuzdadır, bu güncellemelerin neden olduğu sorunlar için orijinal ekipman üreticinize başvurmanız gerektiği anlamına gelir. Yalnızca güncelleme işleminin kendisiyle ilgili sorunlar şu adresten yapılmalıdır $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Bu uzaktan kumandayı etkinleştirmek kendi sorumluluğunuzdadır." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Şifrelenmiş" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Tüm donanım yazılımı güncelleme geçmişini sil" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Siliniyor…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Küçük bir gecikme sonrası çık" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "FILENAME" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Başarısız" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Artalan sürecine bağlanamadı" + +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Bekleyen aygıtlar alınamadı" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Donanım yazılımı güncellemesi yüklenemedi" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Tuhaflıklar yüklenemedi" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Kilitleme başarısız." + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Bağımsız değişkenler ayrıştırılamadı" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Dosya ayrıştırılamadı" + +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Yeniden başlatılamadı" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Sıçrama kipi ayarlanamadı" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Dosya adı" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Dosya Adı İmzası" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Donanım Yazılımı Temel URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Donanım Yazılımı Güncelleme D-Bus Hizmeti" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Donanım Yazılımı Güncelleme Artalan Süreci" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Donanım Yazılımı Yardımcı Programı" + +msgid "Firmware updates are not supported on this machine." +msgstr "Donanım yazılımı güncellemeleri bu makinede desteklenmiyor." + +msgid "Firmware updates are supported on this machine." +msgstr "Donanım yazılımı güncellemeleri bu makinede destekleniyor." + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Bayraklar" + +msgid "Force the action ignoring all warnings" +msgstr "Eylemi tüm uyarıları görmezden gelmeye zorla" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Bulundu" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUIDs" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "fwupd tarafından desteklenen tüm aygıt bayraklarını getir" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Etkinleştirilmiş tüm eklentileri sisteme kaydettir" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Donanım yazılımı dosyasıyla ilgili ayrıntıları al" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Bağlı donanım için güncelleme listesini al" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Bir aygıt için sürümleri alır" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Son güncellemeden sonuçları alır" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FILE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Donanım yeniden takılmayı bekliyor" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Yüksek" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Host Güvenlik Kimliği:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Boşta…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Dosyaları indirirken katı SSL kontrollerini yoksay" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Kurulum Süresi" + +msgid "Install signed device firmware" +msgstr "İmzalı aygıt donanım yazılımını yükle" + +msgid "Install signed system firmware" +msgstr "İmzalı sistem donanım yazılımını yükle" + +msgid "Install unsigned device firmware" +msgstr "İmzasız aygıt donanım yazılımını yükle" + +msgid "Install unsigned system firmware" +msgstr "İmzasız sistem donanım yazılımını yükle" + +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Donanım Yazılımı Kuruluyor…" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Donanım yazılımını güncellemesi kuruluyor…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Kuruluyor %s…" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Dahili aygıt" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Geçersiz" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Sorunlar" +msgstr[1] "Sorunlar" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "ANAHTAR,DEĞER" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Anahtarlık" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Son değiştirilme" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Bir dakikadan daha az kaldı" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Lisans" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux Tedarikçi Donanım Yazılımı Hizmeti (kararlı donanım yazılımı)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux Tedarikçi Donanım Yazılımı Hizmeti (test donanım yazılımı)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux kernel" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux kernel lockdown" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux swap" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Desteklenen donanım yazılımı güncellemelerini listele" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Kullanılabilir donanım yazılımı türlerini listele" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Yükleniyor…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Kilitli" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Düşük" + +msgid "MEI version" +msgstr "MEI sürümü" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Orta" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Üstveri İmzası" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Üstveri URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Üstveri Linux Tedarikçi Donanım Yazılımı Hizmetinden edinilebilir" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Asgari Sürüm" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Artalan süreci ile istemci uyuşmadı, bunun yerine %s kullanın" + +msgid "Modify a configured remote" +msgstr "Uzak yapılandırmayı değiştir" + +msgid "Modify daemon configuration" +msgstr "Artalan süreci yapılandırmasını değiştir" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Olaylar için artalan sürecini gözetle" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Yeni sürüm" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Eylem belirtilmedi!" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Donanım yazılımı kimlik bilgisi bulunamadı" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Donanım yazılımı güncelleme yeteneğine sahip donanım saptanamadı" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Eklenti bulunamadı" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Sürüm bulunamadı" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Şu anda uzaktan kumanda etkin değil, bu nedenle üstveri yok." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Uzaktan kumanda bulunamadı" + +msgid "No updates available for remaining devices" +msgstr "Bu cihaz için yüklenecek güncelleme yok." + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Güncelleme uygulanmadı" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Bulunamadı" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Desteklenmiyor" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Tamam" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Yalnızca tek PCR değerini göster" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "JSON formatında dışarı aktar" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Öntanımlı ESP yolunu geçersiz kıl" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "YOL" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Donanım yazılımı dosyası hakkındaki ayrıntıları ayrıştır ve göster" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Parola" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Yüzde tamamlandı" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Önceki sürüm" + +msgid "Print the version number" +msgstr "Sürüm numarasını yazdır" + +msgid "Print verbose debug statements" +msgstr "Ayrıntılı hata ayıklama ifadelerini yazdır" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Öncelik" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Sahipli" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Yazılım güncelleme desteği için sorgu" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "REMOTE-ID" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Donanım yazılımını aygıttan dosyaya oku" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Donanım yazılımını bölümden dosyaya oku" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Şuradan okunuyor %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Okunuyor…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Yeniden Başlatılıyor…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Üstveriyi uzak sunucudan yenile" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Uzak Kimlik" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Varolan bir üretici yazılımı dosyasındaki verileri değiştir" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Rapor URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Uzak sunucuya bildirildi" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "İnternet bağlantısı gerektirir" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Yeniden başlatılsın mı?" + +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Değişikliklerin etkili olması için artalan süreci yeniden başlatılsın mı?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Aygıt yeniden başlatılıyor…" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI kilidi" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Mümkün olduğunda sonraki yeniden başlatma için kurulumu zamanla" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Zamanlanıyor…" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Daha fazlası için tıklayınız. %s " + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Seçili aygıt" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Bölüm seçildi" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Seri Numarası" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Güncelleme sırasında hata ayıklama bayrağını ayarla" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Onaylı donanım yazılımı listesini ayarlar" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Donanım yazılım geçmişini geliştiricilerle paylaş" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Sonuçları göster" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "İstemci ve artalan süreci sürümlerini göster" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Belirli bir alan için artalan süreci ayrıntılı bilgilerini göster" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Tüm alan adları için hata ayıklama bilgilerini göster" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Hata ayıklama seçeneklerini göster" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Güncellenemeyen aygıtları göster" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Ek hata ayıklama bilgilerini göster" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Donanım yazılımı güncellemelerinin geçmişini göster" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Eklenti ayrıntılı bilgilerini göster" + +#. TRANSLATORS: command line option +msgid "Show the debug log from the last attempted update" +msgstr "Son denenen güncellemedeki hata ayıklama günlüğünü göster" + +#. TRANSLATORS: command line option +msgid "Show the information of firmware update status" +msgstr "Donanım yazılımı güncelleme durumu bilgilerini göster" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Kapatılsın mı?" + +msgid "Sign data using the client certificate" +msgstr "İstemci sertifikasını kullanarak veri imzala" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "İstemci sertifikasını kullanarak veri imzala" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Yüklenen verileri istemci sertifikasıyla imzala" + +msgid "Signature" +msgstr "İmza" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Boyut" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Kaynak" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "DFU aygıtının Satıcısını/Ürün Kimliğini Belirle" + +msgid "Specify the number of bytes per USB transfer" +msgstr "USB aktarımı başına bayt sayısını belirt" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Tüm aygıtlar başarıyla etkinleştirildi" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Yeni üstveri başarıyla indirildi:" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Donanım yazılımı başarıyla kuruldu" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Yapılandırma değeri başarıyla değiştirildi" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Üstveri başarıyla yenilendi" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Aygıt sağlama toplamları başarıyla güncellendi" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Aygıt sağlaması toplamı başarıyla doğrulandı" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Özet" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Destekleniyor" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Uzak sunucuda desteklenir" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-ram" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "METİN" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 reconstruction" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +msgid "Target" +msgstr "Hedef" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Onaylı donanım yazılımı yok." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Bu program yalnızca kök erişimi ile düzgün çalışabilir" + +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 "Bu yazılım ambargo edilmemiş, ancak donanım satıcısı tarafından test edilmekte olan bir donanım yazılımı içerir. Donanım yazılımı güncellemesi başarısız olursa, donanım yazılımını elle düşürmenin bir yoluna sahip olmanız gerekir." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Bu araç yalnızca kök kullanıcı tarafından kullanılabilir" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Tür" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI Donanım Yazılımı Yardımcı Programı" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx Utility" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI platform key" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Şifrelenmemiş" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Bilinmeyen" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Bilinmeyen Aygıt" + +msgid "Unlock the device to allow access" +msgstr "Erişime izin vermek için aygıtın kilidini açın" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Serbest" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Aygıt yazılımı erişimi için aygıt kilidini açar" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Güncelleme sırasında hata ayıklama bayrağını ayarlama" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Desteklenmeyen artalan süreci sürümü %s, istemci sürümü %s" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Güncellenebilir" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Güncelleme Hatası" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Güncelleme İletisi" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Güncelleme Durumu" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Güncelleme hatası bilinen bir sorundur, daha fazla bilgi için bu URL'yi ziyaret edin:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Şimdi güncellensin mi?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Güncelleme için yeniden başlatma gerekli" + +msgid "Update the stored device verification information" +msgstr "Saklanan aygıt doğrulama bilgilerini güncelle" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Güncelleniyor %s…" + +#. TRANSLATORS: ask the user to upload +msgid "Upload report now?" +msgstr "Rapor şimdi karşıya yüklensin mi?" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Donanım yazılımı kurulurken quirk bayraklarını kullan" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Kullanıcı bilgilendirildi" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Kullanıcı adı" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Geçerli" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Türev" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Üretici" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Doğrulanıyor…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Sürüm" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "UYARI:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Bekliyor…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Donanım değişikliklerini izle" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Donanım yazılımını dosyadan aygıta yaz" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Donanım yazılımını dosyadan bölüme yaz" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Dosyaya yazılıyor:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Yazılıyor…" + +#. 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 "Hizmet sağlayıcınız, sisteminizle veya bağlı aygıtlarla uyumluluk için herhangi bir donanım yazılımı güncellemesini doğrulamamış olabilir." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[DEVICE-ID|GUID]" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM olay günlüğü yardımcı aracı" diff --git a/fwupd-1.8.6/po/uk.po b/fwupd-1.8.6/po/uk.po new file mode 100644 index 0000000000000000000000000000000000000000..5a4b4080e4f6e8e7af94dc535fe52d57e0ba94f0 --- /dev/null +++ b/fwupd-1.8.6/po/uk.po @@ -0,0 +1,3106 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Yuri Chornoivan , 2015-2022 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: uk\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "Лишилася %.0f хвилина" +msgstr[1] "Лишилося %.0f хвилини" +msgstr[2] "Лишилося %.0f хвилин" +msgstr[3] "Лишилася %.0f хвилина" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Оновлення BMC %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Оновлення для акумуляторів %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Оновлення мікропрограми процесора %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Оновлення для камери %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Оновлення налаштувань %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Оновлення %s 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: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Оновлення дисплея %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "Оновлення для диска %s" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "Оновлення для вбудованого контролера %s" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "Оновлення для флеш-диска %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Оновлення для клавіатури %s" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "Оновлення ME %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Оновлення для миші %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Оновлення інтерфейсу мережі %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "Оновлення для SSD %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Оновлення контролера сховища даних %s" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "Оновлення системи %s" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Оновлення для TPM %s" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Оновлення контролера Thunderbolt %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Оновлення для сенсорної панелі %s" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "Оновлення USB-приймача %s" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Оновлення для %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s і усі з'єднані пристрою можуть бути недоступними протягом оновлення." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "З'явився %s: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "Змінено %s: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "Зник %s: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s зараз непридатний до оновлення" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "Режим виробництва %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "Для уникнення пошкоджень %s має лишатися з'єднаним із комп'ютером протягом оновлення." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "Для уникнення пошкоджень %s має лишатися з'єднаним із джерелом живлення протягом оновлення." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Перевизначення %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s, версія %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "Версія %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u день" +msgstr[1] "%u дні" +msgstr[2] "%u днів" +msgstr[3] "%u день" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "Для %u пристрою випущено оновлення мікропрограми." +msgstr[1] "Для %u пристроїв випущено оновлення мікропрограм." +msgstr[2] "Для %u пристроїв випущено оновлення мікропрограм." +msgstr[3] "Для %u пристрою випущено оновлення мікропрограм." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%uпристрій не є найкращою відомою конфігурацією." +msgstr[1] "%u пристрої не є найкращою відомою конфігурацією." +msgstr[2] "%u пристрої не є найкращою відомою конфігурацією." +msgstr[3] "%u пристрій не є найкращою відомою конфігурацією." + +#. 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: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +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: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (порогове значення — %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(є застарілим)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "PCR TPM тепер має некоректне значення" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "Захист відтворення мікропрограми AMD" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "Захист від запису мікропрограми AMD" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Rollback Protection" +msgstr "Захист відновлення попередніх станів AMD" + +#. TRANSLATORS: longer description +msgid "AMD Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "Захист від відновлення попередніх версій AMD запобігає зниженню версій програмного забезпечення до тих, у яких виявлено проблеми із захистом." + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Потрібна дія:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Задіяти пристрої" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Задіяти пристрої у черзі очікування" + +msgid "Activate the new firmware on the device" +msgstr "Активація нової мікропрограми на пристрої" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Активуємо оновлення мікропрограми" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Активуємо оновлення мікропрограми для" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Вік" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Згодні, увімкнути віддалене сховище?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Інша назва %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Тепер усі PCR TPM є коректними" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Усі PCR TPM є коректними" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Усі пристрої одного типу буде оновлено одночасно" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Дозволити зниження версій мікропрограми" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Дозволити перевстановлення наявних версій мікропрограми" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Дозволити перемикання гілок мікропрограми" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +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 line option +msgid "Apply firmware updates" +msgstr "Застосувати оновлення мікропрограми" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Застосувати оновлення, навіть якщо воно не є рекомендованим" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Застосувати файли оновлень" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Застосовуємо оновлення…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Схвалена мікропрограма:" +msgstr[1] "Схвалені мікропрограми:" +msgstr[2] "Схвалені мікропрограми:" +msgstr[3] "Схвалена мікропрограма:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "Питати знову наступного разу?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Долучитися до режиму мікропрограми" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Проходимо розпізнавання…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Потрібні дані для розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "Для встановлення застарілої версії мікропрограми на портативний пристрій слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "Для встановлення застарілої версії мікропрограми на цей комп’ютер слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "Для внесення змін до атрибутів BIOS слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "Для внесення змін до записів налаштованих віддалених пристроїв, які використовуються для оновлення мікропрограм, слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Для внесення змін до налаштувань фонової служби слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "Для читання параметрів BIOS слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "Щоб розблокувати пристрій, слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "Для оновлення мікропрограми на портативному пристрої слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "Щоб отримати доступ до оновлення мікропрограми цього комп’ютера, вам слід пройти розпізнавання" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "Щоб отримати доступ до оновлення збережених контрольних сум для пристрою, вам слід пройти розпізнавання" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Автоматичне звітування" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Автоматично вивантажувати кожного разу?" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "Оновлення BIOS, які надано за допомогою LVFS або Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "XML-ЗБИРАННЯ НАЗВА-ФАЙЛА-ДЖ" + +msgid "BYTES" +msgstr "БАЙТИ" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Акумулятор" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Пов'язати новий драйвер ядра" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Заблоковані файли мікропрограм:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Заблокована версія" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Блокуємо мікропрограму:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Блокує встановлення певної мікропрограми" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Версія завантажувача" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "Гілка" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +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 "Скасовано" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Не вдалося застосувати, оскільки оновлення dbx вже застосовано." + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "Не можна застосовувати оновлення до портативного носія" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Змінено" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Перевірити відповідність криптографічних хешів мікропрограми" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Контрольна сума" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "Виберіть гілку:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "Виберіть пристрій:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "Виберіть тип мікропрограми:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "Виберіть випуск:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "Виберіть том:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose the ESP:" +msgstr "Виберіть ESP:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Вилучає результати останнього оновлення" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Такої команди не знайдено" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Підтримка спільноти" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Запропоновано зміни у налаштуваннях" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "Налаштування можна читати лише від імені адміністратора системи" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Перетворити файл мікропрограми" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Створено" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Критичний" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Доступна перевірка криптографічної хеш-суми" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Поточне значення" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Поточна версія" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ІД_ПРИСТРОЮ|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "Засіб роботи з DFU" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Параметри діагностики" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Розпаковування…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Опис" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Від'єднатися до режиму завантажувача" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Подробиці" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works well +#. * together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Вилучити з найкращої відомої конфігурації?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Прапорці пристрою" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Ід. пристрою" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Додано пристрій:" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Рівень заряду акумулятора пристрою є надто низьким" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Рівень заряду акумулятора пристрою є надто низьким (%u%%, має бути принаймні %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "Пристрій може відновлюватися при невдалих оновленнях" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be used while the lid is closed" +msgstr "Пристроєм не можна користуватися. доки закрито кришку" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Змінено пристрій:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "Для перевірки версії потрібна мікропрограма пристрою" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Емульований пристрій" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Пристрій заблоковано" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "Для встановлення усіх наданих випусків потрібен пристрій" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Пристрій є недоступним" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Пристрій є недоступним або перебуває поза досяжністю бездротового зв'язку" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Пристроєм можна користуватися протягом оновлення" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Пристрій очікує на застосування оновлення" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Вилучено пристрій:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Пристрій потребує з'єднання із джерелом змінного струму" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "Для цього пристрою надаватимуться оновлення програмного забезпечення." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "Пристрій із покроковим оновленням" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "У пристрої передбачено підтримку перемикання на іншу гілку мікропрограми" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "Спосіб оновлення для пристрою" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Оновлення пристрою потребує активації" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Пристрій створить резервну копію мікропрограми перед встановленням" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Пристрій не з'явиться у списку пристроїв після завершення оновлення" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Пристрої, для яких вдалося успішно оновити дані:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Пристрої, для яких не вдалося оновити дані належним чином:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Пристрої, для яких немає оновлень мікропрограми:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "Пристрої із найсвіжішою доступною версією мікропрограми:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Не вдалося знайти жодного пристрою із відповідними GUID" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +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 unreported history" +msgstr "Не перевіряти, чи є ненадіслані звіти у журналі" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Не перевіряти, чи має бути увімкнено віддалені джерела отримання даних" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Не перевіряти потребу і не пропонувати перезавантажити систему після оновлення" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Не включати префікс домену журналу" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Не включати префікс часової позначки" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Не виконувати перевірок безпечності для пристрою" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Не запитувати про пристрої" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Не записувати дані до бази даних журналу" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Чи розумієте ви наслідки зміни гілки мікропрограми?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Хочете вимкнути цю можливість для наступних оновлень?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Хочете освіжити це віддалене сховище зараз?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Хочете вивантажувати звіти автоматично для наступних оновлень?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Не просити про розпізнавання (буде показано менше подробиць)" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "Виконано!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Знизити версію %s з %s до %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Знижує версію мікропрограми на пристрої" + +#. TRANSLATORS: 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... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Знижуємо версію %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Отримати файл" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Отримуємо дані…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Записати дані SMBIOS з файла" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Тривалість" + +#. TRANSLATORS: ESP is EFI System Partition +msgid "ESP specified was not valid" +msgstr "Вказаний ESP є некоректним" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "У кожній системі мають бути перевірки, які забезпечують захист мікропрограми." + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Емульований вузол" + +msgid "Enable" +msgstr "Увімкнути" + +#. TRANSLATORS: command line option +msgid "Enable firmware update support on supported systems" +msgstr "Увімкнути підтримку оновлення мікропрограми на підтримуваних системах" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Увімкнути нове віддалене сховище?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Увімкнути це віддалене сховище?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. TRANSLATORS: if the remote is enabled +msgid "Enabled" +msgstr "Увімкнено" + +msgid "Enabled fwupdate debugging" +msgstr "Увімкнено діагностику fwupdate" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Увімкнено, якщо обладнання є відповідним" + +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "Зашировано" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Шифрована пам'ять" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Шифрування оперативної пам'яті робить неможливим читання інформації, яка зберігається у пам'яті пристрою, якщо чіп пам'яті вилучено з пристрою і піддано обробці з метою отримання доступу до даних." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Кінець строку дії" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "Перелік" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Витерти увесь журнал оновлень мікропрограми" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Витираємо…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Завершити роботу з невеличкою затримкою" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Завершити роботу після завантаження рушія" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Експортувати структуру файла мікропрограми до XML" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Видобувати бінарну мікропрограму до образів" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "ФАЙЛ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "ФАЙЛ [ІД_ПРИСТРОЮ|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "НАЗВА_ФАЙЛА" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "НАЗВА-ФАЙЛА СЕРТИФІКАТ ЗАКРИТИЙ-КЛЮЧ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "НАЗВА_ФАЙЛА АЛЬТ_НАЗВА_ПРИСТРОЮ|АЛЬТ_ІД_ПРИСТРОЮ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "НАЗВА_ФАЙЛА АЛЬТ_НАЗВА_ПРИСТРОЮ|АЛЬТ_ІД_ПРИСТРОЮ [АЛЬТ_НАЗВА_ОБРАЗУ|АЛЬТ_ІД_ОБРАЗУ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "НАЗВА_ФАЙЛА ІД_ПРИСТРОЮ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "НАЗВА-ФАЙЛА ВІДСТУП ДАНІ [ТИП-МІКРОПРОГРАМИ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "НАЗВА_ФАЙЛА [ІД_ПРИСТРОЮ|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "НАЗВА-ФАЙЛА [ТИП-МІКРОПРОГРАМИ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "НАЗВА_ФАЙЛА_ДЖ НАЗВА_ФАЙЛА_ПРИЗН [ТИП-МІКРОПРОГРАМИ-ДЖ] [ТИП-МІКРОПРОГРАМИ-ПРИЗН]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "НАЗВА_ФАЙЛА|КОНТР_СУМА1[,КОНТР_СУМА2][,КОНТР_СУМА3]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Помилка" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Не вдалося застосувати оновлення" + +#. TRANSLATORS: error message for Windows +#, c-format +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Не вдалося встановити з'єднання зі службою Windows. Будь ласка, переконайтеся, що її запущено." + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Не вдалося завантажити локальний dbx" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "Не вдалося завантажити коригування" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Не вдалося завантажити системний dbx" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Не вдалося заблокувати" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Не вдалося обробити аргументи" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Не вдалося обробити файл" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Не вдалося обробити прапорці до --filter" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Не вдалося обробити локальний dbx" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "Не вдалося встановити чинність даних ESP" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Ні" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Назва файла" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Підпис назви файла" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Джерело файла" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "Слід вказати назву файла" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Фільтрувати за набором прапорців пристроїв за допомогою префікса виключення ~, наприклад «internal,~needs-reboot»" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Атестація мікропрограми" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Атестація мікропрограми перевіряє програмне забезпечення пристрою за еталонною копією, щоб переконатися, що до нього не було внесено змін." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Дескриптор BIOS мікропрограми" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "Захист lдескриптора BIOS захищає пам'ять мікропрограми пристрою від внесення змін." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Регіон BIOS мікропрограми" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "Захист регіону BIOS захищає пам'ять мікропрограми пристрою від внесення змін." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Основна адреса мікропрограми" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "Служба D-Bus оновлення мікропрограми" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Служба оновлення мікропрограми" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Перевірка засобу оновлення мікропрограми" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Перевірка засобу оновлення мікропрограми перевіряє, чи не було внесено зміни до програмного забезпечення для оновлення мікропрограми." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Оновлення мікропрограми" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Засіб роботи з мікропрограмами" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Захист мікропрограми від запису" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Блокування захисту від запису мікропрограми" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Захист від запису мікропрограми захищає пам'ять мікропрограми пристрою від внесення змін." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Засвідчення мікропрограми" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Мікропрограму вже заблоковано" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Мікропрограму ще не заблоковано" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "Метадані мікропрограми не оновлювалися %u день, можливо, вони вже не є актуальними." +msgstr[1] "Метадані мікропрограми не оновлювалися %u дні, можливо, вони вже не є актуальними." +msgstr[2] "Метадані мікропрограми не оновлювалися %u днів, можливо, вони вже не є актуальними." +msgstr[3] "Метадані мікропрограми не оновлювалися %u днів, можливо, вони вже не є актуальними." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "Останнє оновлення метаданих мікропрограми було виконано %s тому. Скористайтеся --force, щоб виконати примусове оновлення." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Оновлення мікропрограми" + +msgid "Firmware updates are not supported on this machine." +msgstr "На цьому комп'ютері не передбачено підтримки оновлення мікропрограми." + +msgid "Firmware updates are supported on this machine." +msgstr "На цьому комп'ютері передбачено підтримку оновлень мікропрограми." + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "Оновлення мікропрограми вимкнено; віддайте команду 'fwupdmgr unlock', щоб їх увімкнути" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Прапорці" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Виконати дію примусово шляхом послаблення деяких перевірок під час виконання" + +msgid "Force the action ignoring all warnings" +msgstr "Виконати дію примусово, ігноруючи усі попередження" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Знайдено" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Виявлено повне шифрування диска" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Ключі шифрування усього диска можуть втрати чинність при оновленні" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Злита платформа" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Злита платформа" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" +msgstr[2] "GUID" +msgstr[3] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +msgid "Get BIOS settings" +msgstr "Отримання параметрів BIOS" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Отримати усі прапорці пристроїв, підтримку яких передбачено у fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Отримати список усіх пристроїв, у яких передбачено оновлення мікропрограми" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Отримати список усіх додатків, які зареєстровано у системі" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Отримати параметри файла мікропрограми" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Отримує налаштовані віддалені пристрої" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Отримує атрибути захисту основної системи" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Отримує список схвалених мікропрограм" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Отримує список заблокованих мікропрограм" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "Отримує список оновлень для з’єднаного обладнання" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Отримує випуски для пристрою" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Отримує результати з останнього оновлення" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "ФАЙЛ-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Обладнання очікує на повторне з'єднання" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Високий" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Події захисту вузла" + +#. TRANSLATORS: error message for unsupported feature +#, c-format +msgid "Host Security ID (HSI) is not supported" +msgstr "Підтримки Host Security ID (HSI) не передбачено" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Ід. захисту основної системи:" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "Захист IOMMU" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "Захист IOMMU забороняє з'єднаним пристроям доступ до неуповноважених частин пам'яті системи." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Вимкнено захист пристрою IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Увімкнено захист пристрою IOMMU" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Бездіяльність…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ігнорувати результати строгої перевірки SSL під час отримання файлів" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Ігнорувати помилки при перевірці контрольної суми мікропрограми" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Ігнорувати помилки, пов'язані із невідповідністю обладнання мікропрограмі" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "Ігнорувати перевірки безпечності" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Ігноруємо строгі перевірки SSL. Щоб зробити ігнорування у майбутньому автоматичним, експортуйте до вашого середовища змінну DISABLE_SSL_STRICT" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +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 "Встановити файл мікропрограми на це обладнання" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Встановити вказану мікропрограму на пристрій. Буде також встановлено на усі можливі пристрої, щойно буде встановлено відповідність CAB" + +msgid "Install old version of signed system firmware" +msgstr "Встановити стару версію підписаної мікропрограми системи" + +msgid "Install old version of unsigned system firmware" +msgstr "Встановити стару версію непідписаної мікропрограми системи" + +msgid "Install signed device firmware" +msgstr "Встановити підписану мікропрограму пристрою" + +msgid "Install signed system firmware" +msgstr "Встановити підписану мікропрограму системи" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "Встановити спочатку на батьківському пристрої" + +msgid "Install unsigned device firmware" +msgstr "Встановити непідписану мікропрограму пристрою" + +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…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Встановлення цього оновлення може призвести до припинення будь-яких гарантійних зобов'язань щодо пристрою." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Ціле число" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Захист ACM Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard захищено ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Правила обробки помилок Intel BootGuard" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Правила обробки помилок Intel BootGuard запобігають продовженню запуску пристрою, якщо до програмного забезпечення пристрою було внесено зміни." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard Fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "OTP FUSE Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Перевірене завантаження Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Правила обробки помилок Intel BootGuard" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard запобігає роботі неуповноваженого програмного забезпечення на пристрої після його запуску." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Перевірене завантаження Intel BootGuard" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Intel CET" +msgstr "Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Активна Intel CET" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Увімкнено Intel CET" + +#. TRANSLATORS: longer description +msgid "Intel Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Технологія Intel Control-Flow Enforcement виявляє і запобігає певним методам запуску зловмисниками програмного забезпечення на пристрої." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Режим виробництва Intel Management Engine" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Перевизначення Intel Management Engine" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "Перевизначення Intel Management Engine вимикає перевірки щодо внесення змін до програмного забезпечення пристрою." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Версія Intel Management Engine" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: longer description +msgid "Intel Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Intel Supervisor Mode Access Prevention забороняє малобезпечним програмам доступ до пам'яті пристрою." + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Внутрішній пристрій" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Некоректна" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Некоректні аргументи" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Є старішою версією" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "Перебуває у режимі завантажувача" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Є оновленням" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Вада" +msgstr[1] "Вади" +msgstr[2] "Вади" +msgstr[3] "Вада" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "КЛЮЧ,ЗНАЧЕННЯ" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Ядро більше не використовує сторонні модулі" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Ядро використовує сторонні модулі" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Вимкнено блокування ядра" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Увімкнено блокування ядра" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "Сховище ключів" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "МІСЦЕ" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Востаннє змінено" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Лишилося менше за хвилину" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Ліцензування" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Блокування ядра Linux" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Режим блокування ядра Linux забороняє адміністративним обліковим записам (root) доступ до критичних частин системи і внесення до них змін." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Резервна пам'ять ядра Linux зберігає на диску дані під час роботи системи. Якщо дані не захищено, хтось, хто отримає диск, може отримати до них доступ." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Перевірка ядра Linux" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "Перевірка ядра Linux захищає критичне програмне забезпечення системи від внесення змін. Використання драйверів пристроїв, які не є частиною системи, може заважати належній роботі цієї можливості захисту." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Резервна пам'ять Linux" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Служба надання мікропрограм для Linux від виробника (стабільна мікропрограма)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Служба надання мікропрограм для Linux від виробника (тестова мікропрограма)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Ядро Linux" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Блокування ядра Linux" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Рез. пам'ять Linux" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Вивести список записів у dbx" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "Показати список підтримуваних оновлень мікропрограми" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Вивести список доступних типів мікропрограм" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Виводить список файлів у ESP" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Завантаження…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Заблоковано" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Низький" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Режим виробництва MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Перевизначення MEI" + +msgid "MEI version" +msgstr "Версія MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Увімкнути певні додатки вручну" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Режим виробництва використовують після вироблення пристрою, коли можливості захисту ще не увімкнено." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Максимальна довжина" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Максимальне значення" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Середній" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Підпис метаданих" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Адреса метаданих" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Метадані можна отримати від служби надання мікропрограм для Linux від виробника." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Мінімальна версія" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Мінімальна довжина" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Мінімальне значення" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Невідповідність фонової служби і клієнта, скористайтеся краще %s" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "Змінює значення налаштувань фонової служби" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Змінює вказаний запис віддаленого пристрою" + +msgid "Modify a configured remote" +msgstr "Зміна налаштованих віддалених пристроїв" + +msgid "Modify daemon configuration" +msgstr "Зміна налаштувань фонової служби" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Стежити за подіями у фоновій службі" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Монтує ESP" + +#. TRANSLATORS: we're poking around as a power user +msgid "NOTE: This program may only work correctly as root" +msgstr "Зауваження: програма зможе працювати належними чином лише від імені користувача root" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Потребує перезавантаження після встановлення" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Потребує перезавантаження" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Потребує вимикання після встановлення" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Нова версія" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Не вказано дії!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Немає знижень версії для %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Не знайдено ідентифікаторів мікропрограм" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "Не виявлено обладнання із передбаченою можливістю оновлення мікропрограми" + +#. TRANSLATORS: nothing found +msgid "No plugins found" +msgstr "Додатків не знайдено" + +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Немає доступних випусків" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "Зараз не увімкнено жодного віддаленого сховища, отже, метадані недоступні." + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Немає доступних віддалених сховищ" + +#. TRANSLATORS: this is an error string +msgid "No updatable devices" +msgstr "Немає придатних до оновлення пристроїв" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Немає доступних оновлень" + +msgid "No updates available for remaining devices" +msgstr "Для решти пристроїв немає доступних оновлень" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Не застосовано жодного оновлення" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Не затверджено" + +#. TRANSLATORS: error message to tell someone they can't use this feature +msgid "Not enough data was provided to make an HSI calculation." +msgstr "Надано недостатньо даних для обчислення HSI." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Не знайдено" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Немає підтримки" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Гаразд" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "Гаразд!" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Показувати лише одне значення PCR" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "Використовувати IPFS лише при отриманні файлів" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Дозволено лише оновлення версії" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "Виведені дані у форматі JSON" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Перевизначити типовий шлях до ESP" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "ШЛЯХ" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Обробити і показати параметри файла мікропрограми" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Обробляємо оновлення dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Обробляємо системний dbx…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Пароль" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Накласти латку на бінарний файл мікропрограми за відомим відступом" + +msgid "Payload" +msgstr "Вміст" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "У черзі" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Відсоток виконання" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Виконати дію?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Діагностика платформи" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "Діагностика платформи надає змогу вимикати можливості захисту пристрою. Цією можливістю мають користуватися лише виробники обладнання." + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Будь ласка, переконайтеся, що у вас є ключ відновлення тому, перш ніж продовжувати обробку." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Будь ласка, введіть число від 0 до %u: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Не встановлено залежності додатка" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Можливі значення" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "Захист DMA до завантаження" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Захист DMA до завантаження" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Захист DMA перед завантаженням вимкнено" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Захист DMA перед завантаженням увімкнено" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "Захист DMA перед завантаженням запобігає доступу з боку пристроїв до системної пам'яті після встановлення з'єднання пристроїв з комп'ютером." + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Попередня версія" + +msgid "Print the version number" +msgstr "Вивести номер версії" + +msgid "Print verbose debug statements" +msgstr "Вивести докладні діагностичні повідомлення" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Пріоритетність" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Проблеми" + +msgid "Proceed with upload?" +msgstr "Продовжити вивантаження?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Перевірки захисту процесора" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Пропрієтарна" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "Опитати щодо підтримки оновлення мікропрограми" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ВІДДАЛ-ІД" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ВІДДАЛ-ІД КЛЮЧ ЗНАЧЕННЯ" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Лише читання" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Прочитати бінарну мікропрограму з пристрою" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Прочитати мікропрограму з пристрою" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "Прочитати мікропрограму з пристрою до файла" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "Прочитати мікропрограму з одного розділу до файла" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Читаємо з %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Читаємо…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Перезавантажуємо…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Оновити метадані з віддаленого сервера" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Повторно встановити %s до %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Повторно встановити мікропрограму на пристрій" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Повторно встановити мікропрограму на пристрій" + +#. TRANSLATORS: the 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 "Повторно встановлюємо %s з номером версії %s... " + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "Гілка випусків" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Прапорці випуску" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Ідентифікатор випуску" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Віддалений ідентифікатор" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "Замінити дані у наявному файлі мікропрограми" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Адреса звіту" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Повідомлено на віддаленому сервері" + +#. TRANSLATORS: we asked the user to choose an option and they declined +msgid "Request canceled" +msgstr "Запит скасовано" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Не знайдено відповідної файлової системи efivarfs" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Не знайдено відповідного обладнання" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Потребує завантажувача" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "Потребує з'єднання із інтернетом" + +#. 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 "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Отримати параметри BIOS. Якщо аргументів не передано, буде повернуто усі параметри" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Повернути усі ідентифікатори апаратного забезпечення комп’ютера" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Rollback protection" +msgstr "Захист від повернення до попередніх версій" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "Віддайте команду «fwupdmgr get-upgrades», щоб дізнатися більше." + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "Віддайте команду «fwupdmgr sync-bkc», щоб завершити цю дію." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Запустити процедуру чищення композиції додатків при використанні install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Запустити процедуру приготування композиції додатків при використанні install-blob" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Запустіть без «%s», щоб переглянути" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Поточне ядро є надто застарілим" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Динамічний суфікс" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "ПАРАМЕТР ЗНАЧЕННЯ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "ПАРАМЕТР1 ЗНАЧЕННЯ1 [ПАРАМЕТР2] [ЗНАЧЕННЯ2]" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Дескриптор SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "Регіон BIOS SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Блокування SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "Захист від відтворення SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Запис SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "Захист від запису SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "ПІДСИСТЕМА ДРАЙВЕР [ІД_ПРИСТРОЮ|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Зберегти файл, за допомогою якого можна буде створити апаратні ідентифікатори" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Скалярний крок" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "Якщо можливо, запланувати встановлення на наступне перезавантаження" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Плануємо…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Secure Boot вимкнено" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Увімкнено Secure Boot" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Див. %s, щоб дізнатися більше." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Див. %s, щоб дізнатися більше." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Вибрано пристрій" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Вибраний том" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Серійний номер" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Встановити параметр BIOS «%s» за допомогою «%s»." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Встановити параметр BIOS" + +msgid "Set one or more BIOS settings" +msgstr "Встановити один або декілька параметрів BIOS" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "Встановити під час оновлення прапорець діагностики" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Встановити один або декілька параметрів BIOS" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Встановлення списку схвалених мікропрограм" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Тип параметра" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Параметри буде застосовано після перезавантаження системи" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Поділитися журналом оновлень із розробниками" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Показати усі результати" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Вивести дані щодо версій клієнат і фонової служби" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Показати докладні дані фонової служби для певного домену" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Показати діагностичні дані для усіх доменів" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Показувати параметри діагностики" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Показати пристрої, оновлення для яких неможливе" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Показати додаткові діагностичні дані" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Показати журнал оновлень мікропрограми" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "Показати докладні відомості щодо додатків" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Вивести обчислену версію dbx" + +#. 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 "Вимкнути зараз?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Підписати мікропрограму новим ключем" + +msgid "Sign data using the client certificate" +msgstr "Підписування даних клієнтським сертифікатом" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Підписування даних клієнтським сертифікатом" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Підписати вивантажені дані клієнтським сертифікатом" + +msgid "Signature" +msgstr "Підпис" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Підписаний вміст" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Розмір" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Якщо оновити цю мікропрограму, деякі ключі платформи можуть втратити чинність." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Джерело" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "Вказати ідентифікатори виробника/продукту пристрою DFU" + +#. TRANSLATORS: command line option +msgid "Specify a filename to use to save backend events" +msgstr "Вкажіть назву файла, якою слід скористатися для зберігання записів подій модуля обробки" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Вказати файл бази даних dbx" + +msgid "Specify the number of bytes per USB transfer" +msgstr "Вказати кількість байтів на один пакет передавання даних USB" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Рядок" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Успіх" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Успішно активовано усі пристрої" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Успішно вимкнено запис віддаленого сховища" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "Успішно отримано нові метадані:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Успішно увімкнено і освіжено віддалене сховище" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Успішно увімкнено запис віддаленого сховища" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Мікропрограму успішно встановлено" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Успішно змінено значення у налаштуваннях" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Успішно змінено віддалене сховище" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Метадані успішно оновлено вручну" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Успішно оновлено контрольні суми пристрою" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "Успішно вивантажено %u звіт" +msgstr[1] "Успішно вивантажено %u звіти" +msgstr[2] "Успішно вивантажено %u звітів" +msgstr[3] "Успішно вивантажено %u звіт" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Успішно перевірено контрольні суми пристрою" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Резюме" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Є підтримка" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Підтримувані процесори" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Підтримуваний на віддаленому сервері" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Присипляння бездіяльності" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Призупинення до RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Призупинення до стану бездіяльності надає змогу пристрою швидко перейти у стан сну для заощадження електроенергії. Доки пристрій перебуває у стані призупинення, його пам'ять може бути фізично видалено із отриманням доступу до даних у пам'яті." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Призупинення із записом до оперативної пам'яті надає змогу пристрою швидко перейти у стан сну для заощадження електроенергії. Доки пристрій перебуває у стані призупинення, його пам'ять може бути фізично видалено із отриманням доступу до даних у пам'яті." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Присипляння бездіяльності" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Присипляння до пам'яті" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Перемкнутися з гілки %s на %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Перемкнути гілку мікропрограми на пристрої" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the host best known configuration" +msgstr "Синхронізувати версії мікропрограм із найкращою відомою конфігурацією вузла" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low to perform the update" +msgstr "Живлення системи на надто низькому рівні для виконання оновлення" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low to perform the update (%u%%, requires %u%%)" +msgstr "Живлення системи на надто низькому рівні для виконання оновлення (%u%%, має бути принаймні %u%%)" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "Система потребує зовнішнього джерела живлення" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "ТЕКСТ" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (Trusted Platform Module або довірений модуль платформи) є комп'ютерним чіпом, який виявляє зміни в апаратних компонентах." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "Відновлення PCR0 TPM" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "Відновлення PCR0 TPM є некоректним" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "Відновлення PCR0 TPM тепер є коректним" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "Налаштування платформи TPM" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "Відновлення TPM" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Порожні PCR TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM 2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Мітка" +msgstr[1] "Мітки" +msgstr[2] "Мітки" +msgstr[3] "Мітки" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Нечисте" + +msgid "Target" +msgstr "Ціль" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Перевірити пристрій за допомогою маніфесту JSON" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "Intel Management Engine керує компонентами пристроїв і потребує оновлення до найсвіжішої версії, щоб уникнути проблем із захистом." + +#. TRANSLATORS: do not translate the variables marked using $ +msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." +msgstr "LVFS є безкоштовною службою, яка працює як незалежна юридична одиниця і не має зв'язку з $OS_RELEASE:NAME$. Розробники вашої операційної системи, можливо, не перевіряли жодні з цих оновлень на сумісність із системою або з'єднаними із комп'ютером пристроями. Усі мікропрограми надаються лише самими виробниками обладнання." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "Налаштування платформи TPM (Trusted Platform Module або довірений модуль платформи) використовують для перевірки, чи не було внесено змін до процедури запуску пристрою." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "Відтворення TPM (Trusted Platform Module або довірений модуль платформи) використовують для перевірки, чи не було внесено змін до процедури запуску пристрою." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "PCR0 TPM відрізняється від реконструкції." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "Ключ платформи UEFI використовують для визначення, чи походить програмне забезпечення пристрою із надійного джерела." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Ця фонова служба завантажила сторонній код — її підтримка більше не здійснюється розробниками основної гілки програми!" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "Невідповідна версія пристрою: маємо %s, мало бути %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Мікропрограма з %s не постачається %s, постачальником обладнання." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Годинник системи налаштовано неправильно — це може зашкодити отриманню файлів." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Виробником не надано ніяких нотаток щодо випуску." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Маємо пристрої із проблемами:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Заблокованих файлів мікропрограм немає" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Немає схвалених мікропрограм." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync-bkc` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Цей пристрій буде повернуто до %s у результаті виконання команди %s." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Цю мікропрограму створено учасниками спільноти LVFS. Її не було надано початковим виробником обладнання. Виробник також не здійснюватиме підтримки цієї мікропрограми." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Цей пакунок не було перевірено. Його належну роботу не можна гарантувати." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Програма зможе працювати належними чином лише від імені користувача root" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "У цьому сховищі міститься мікропрограма, встановлювати яку не заборонено, але яка усе ще перебуває у процесі тестування виробником обладнання. Вам слід переконатися, що ви зможете встановити стабільну версію мікропрограми, якщо процедура оновлення зазнає невдачі." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "У цій системі не передбачено підтримки параметрів мікропрограми" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Ця система має вади у роботі HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Ця система має низький рівень захисту HSI." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "За допомогою цієї програми адміністратор може застосувати оновлення до dbx UEFI." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "За допомогою цього інструмента адміністратор може виконувати діагностику операції UpdateCapsule." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "За допомогою цього інструмента адміністратор може опитувати фонову службу fwupd і керувати нею. Це уможливлює виконання таких дій, як встановлення мікропрограм або зниження версії мікропрограм." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "За допомогою цього інструмента адміністратор може скористатися додатками fwupd без встановлення їх до основної системи." + +#. TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the +#. values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "Цей інструмент здатен змінити параметр BIOS «%s» з «%s» на «%s» автоматично, але нове значення буде задіяно лише після перезапуску комп'ютера." + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Цим інструментом може користуватися лише root" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Цей інструмент читає і обробляє журнал подій TPM, який заповнюється мікропрограмами системи." + +#. TRANSLATORS: message to tell someone how to ignore error +msgid "To ignore this warning, use --force" +msgstr "Щоб проігнорувати це попередження, скористайтеся параметром --force" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Проміжна помилка" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Так" + +#. TRANSLATORS: We verified the meatdata against the server +msgid "Trusted metadata" +msgstr "Надійні метадані" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Надійні дані" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Тип" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "Розділ ESP UEFI не виявлено або не налаштовано" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "Засіб роботи із мікропрограмами UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "Ключ платформи UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "Secure Boot UEFI" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "Secure Boot UEFI запобігає завантаженню програмного забезпечення зловмисників при запуску пристрою." + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Капсульні оновлення UEFI є недоступними або їх не увімкнено у налаштуваннях мікропрограми" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Засіб роботи з dbx UEFI" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "У режимі сумісності із застарілим BIOS не можна оновити мікропрограму UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Ключ платформи UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI secure boot" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Не вдалося встановити з'єднання зі службою" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Не вдалося знайти атрибут" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Відв'язати поточний драйвер" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Розблокуємо мікропрограму:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Розблоковує встановлення певної мікропрограми" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Розшифровано" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "Невідомий" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Невідомий пристрій" + +msgid "Unlock the device to allow access" +msgstr "Розблокування пристрою для отримання доступу" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Розблоковано" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Розблоковує пристрій для доступу до мікропрограми" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Демонтує ESP" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +msgstr "Зняти під час оновлення прапорець діагностики" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Непідписаний вміст" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Непідтримувана версія фонової служби %s, версія клієнта — %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Очищене" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Придатний до оновлення" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Помилка оновлення" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Зображення оновлення" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Повідомлення щодо оновлення" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Стан оновлення" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Нам відомо про помилку під час оновлення. Будь ласка, відвідайте цю адресу, щоб дізнатися більше:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "Оновити зараз?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "Оновлення потребує перезавантаження" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Оновити збережений криптографічний хеш на основі поточних даних ROM" + +msgid "Update the stored device verification information" +msgstr "Оновлення збережених даних щодо верифікації пристрою" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Оновити збережені метадані поточними даними" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Оновлює усі вказані пристрої до найсвіжішої версії мікропрограми. Якщо пристрої не вказано, оновлює усі пристрої." + +msgid "Updating" +msgstr "Оновлення" + +#. TRANSLATORS: 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 "Оновлюємо %s з %s до %s... " + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Оновлюємо %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Оновити %s з %s до %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload 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: how important the release is +msgid "Urgency" +msgstr "Рівень важливості" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "Скористайтеся fwupdmgr --help, щоб дізнатися більше" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "Скористайтеся fwupdtool --help, щоб дізнатися більше" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Використовувати варіативні прапорці при встановленні мікропрограми" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Сповіщено користувача" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Користувач" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Коректна" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Перевіряємо дані ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "Варіант" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Виробник" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Перевіряємо…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Версія" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "УВАГА:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Очікуємо…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Спостерігати за змінами у обладнанні" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "Записати мікропрограму з файла на пристрій" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "Записати мікропрограму з файла на один розділ" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Записуємо файл:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Записуємо…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Ви маєте переконатися, що зможете відновити параметр із налаштувань системної мікропрограми, оскільки ця зміна може спричинити до неможливості завантаження системи Linux або спричинити інші нестабільності у роботі системи." + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "Виробник вашого дистрибутива може не перевіряти усі оновлення мікропрограми на сумісність із вашою системою або з’єднаними із нею пристроями." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Ваше обладнання може бути пошкоджено через використання цієї мікропрограми. Встановлення цього випуску може призвести до відмови %s від гарантійних зобов'язань." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Вашу систему налаштовано на найкращу відому конфігурацію %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[КОНТРОЛЬНА_СУМА]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ІД_ПРИСТРОЮ|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ІД_ПРИСТРОЮ|GUID] [ГІЛКА]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[ІД_ПРИСТРОЮ|GUID] [ВЕРСІЯ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[ФАЙЛ ПІДПИС_ФАЙЛА ВІДДАЛ-ІД]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[НАЗВАФАЙЛА1] [НАЗВАФАЙЛА2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [ SETTING2]..." +msgstr "[ПАРАМЕТР1] [ПАРАМЕТР2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[ПАРАМЕТР1] [ПАРАМЕТР2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[ФАЙЛ-SMBIOS|ФАЙЛ-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "типова" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "Допоміжна програма для роботи із записами подій журналу TPM fwupd" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "Додатки fwupd" diff --git a/fwupd-1.8.6/po/zh_CN.po b/fwupd-1.8.6/po/zh_CN.po new file mode 100644 index 0000000000000000000000000000000000000000..ed3a9c9daf9cee5a54094dce9dfea9f861c2f984 --- /dev/null +++ b/fwupd-1.8.6/po/zh_CN.po @@ -0,0 +1,2277 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Boyuan Yang <073plan@gmail.com>, 2017,2020,2022 +# Dingzhong Chen , 2020-2021 +# Dingzhong Chen , 2016-2019 +# Mingcong Bai , 2017-2018 +# Mingye Wang , 2016 +# Mingye Wang , 2015 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "还剩 %.0f 分钟" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s 电池更新" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system bootup +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU 微码更新" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s 摄像头更新" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s 配置更新" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s 消费者 ME 更新" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s 控制器更新" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s 企业 ME 更新" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s 设备更新" + +#. TRANSLATORS: 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: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s 键盘更新" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s 管理引擎(ME)更新" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s 鼠标更新" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s 网络接口更新" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s 存储控制器更新" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s 系统更新" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM 更新" + +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt 控制器更新" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s 触摸板更新" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s 更新" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "更新期间 %s 和所有连接的设备可能无法使用。" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s 生产模式" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "更新期间必须保持 %s 的连接以避免损坏。" + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "更新期间必须保持 %s 接入电源以避免损坏。" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s 覆写" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s 版本" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u 天" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u 个设备有可升级的固件。" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u 小时" + +#. TRANSLATORS: how many local devices can expect updates now +#, c-format +msgid "%u local device supported" +msgid_plural "%u local devices supported" +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: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(已过时)" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "所需操作:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "激活设备" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "激活待处理的设备" + +msgid "Activate the new firmware on the device" +msgstr "激活设备上的新固件" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "正在激活固件更新" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "正在激活固件更新给" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "已发布时间" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "同意并启用此远程源吗?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%s 的别名" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "所有相同类型的设备会在相同一时间更新" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "允许降级固件版本" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "允许重新安装现有的固件版本" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "允许切换固件分支" + +#. TRANSLATORS: 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 line option +msgid "Apply firmware updates" +msgstr "应用固件更新" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "应用更新,即使不建议" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "应用更新文件" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "正在应用更新……" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "批准的固件:" + +#. TRANSLATORS: stop nagging the user +msgid "Ask again next time?" +msgstr "下次再次询问?" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "附加到固件模式" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "正在认证…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "需要身份验证详细信息" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "需要认证:在可移动设备上降级固件" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "需要认证:在此机器上降级固件" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "需要认证:修改用于固件更新的已配置远程源" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "需要认证:修改守护进程配置" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to 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 "需要认证:解锁设备" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "需要认证:在可移动设备上升级固件" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "需要认证:在此机器上升级固件" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "需要认证:为设备更新存储的校验和" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "自动报告" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "每次都自动上传?" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "生成器XML 目标文件名" + +msgid "BYTES" +msgstr "字节" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "绑定新内核驱动" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "受阻的固件文件:" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "阻止的固件:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "阻止指定的固件被安装" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "引导程序版本" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Branch" +msgstr "分支" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +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 "已取消" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "无法应用 dbx 更新因其更新已应用。" + +#. TRANSLATORS: the user is using a LiveCD or LiveUSB install disk +msgid "Cannot apply updates on live media" +msgstr "无法在 Live 系统介质上应用更新" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "已变更" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "检查加密哈希与固件是否匹配" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "校验和" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose a branch:" +msgstr "选择分支:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "选择设备:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a firmware type:" +msgstr "选择固件类型:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "选择发行版本:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a volume:" +msgstr "选择卷:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "清除从最后一次更新获取的结果" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "未找到命令" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "转换固件文件" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "创建日期" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "关键" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "支持加密哈希验证" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "当前版本" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "设备ID|GUID" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "固件更新实用程序" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "调试选项" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "正在解压缩…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "描述" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "拆离到引导加载器模式" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "详情" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +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 "已添加设备:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "设备可从刷机失败恢复" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "已更改设备:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "设备固件需要版本检查" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "设备已锁定" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "设备需要安装所有提供的版本" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "设备不可访问" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "设备在更新期间可使用" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "已移除设备:" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "设备阶段更新" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "设备支持切换到不同的固件分支" + +#. TRANSLATORS: command line option +msgid "Device update method" +msgstr "设备更新方法" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "设备更新需要激活" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "设备将在安装前备份固件" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "更新完后设备不会再显示" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "成功更新的设备:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "未正确更新的设备:" + +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device upgrade +#. * available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "无固件更新可用的设备:" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device upgrade +#. * available +msgid "Devices with the latest available firmware version:" +msgstr "固件已是最新的设备:" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is inactive and not used +msgid "Disabled" +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 unreported history" +msgstr "不要查找未报告历史" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "不要检查下载远程是否已启用" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "更新后不要检查或提示是否要重启" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "不包含日志域前缀" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "不包含时间戳前缀" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "不要执行设备安全检查" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "不要写入历史数据库" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "你清楚更改固件分支的后果吗?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "你要在将来的更新中禁用此功能吗?" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "你要现在刷新这个远程库吗?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "你要将来的更新中自动上传报告吗?" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "完成!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "降级 %s,从 %s 到 %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "降级设备上的固件" + +#. TRANSLATORS: 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…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "正在下载 %s……" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "下载文件" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "正在下载..." + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "从文件转储 SMBIOS 数据" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "时长" + +#. TRANSLATORS: 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: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "£启用新的远程库" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "要启用此远程源吗?" + +#. TRANSLATORS: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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: Suffix: the HSI result +msgid "Encrypted" +msgstr "已加密" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "加密内存" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "清楚所有固件更新历史" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "正在擦除…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "在短暂的延迟后退出" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "在引擎加载后退出" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "导出 XML 结构的固件文件" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "提取固件比特块到映像" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "文件" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "文件 [设备ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#. TRANSLATORS: filename argument with path +msgid "FILENAME" +msgstr "文件名" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "文件名称 证书 私钥" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID" +msgstr "文件名 设备替代名称|设备替代ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ALT-NAME|DEVICE-ALT-ID [IMAGE-ALT-NAME|IMAGE-ALT-ID]" +msgstr "文件名 设备替代名称|设备替代ID [映像替代名称|映像替代ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID" +msgstr "文件名 设备ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "文件名 [设备ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "文件名 [固件类型]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "源文件名 目标文件名 [源固件类型] [目标固件类型]" + +#. TRANSLATORS: Suffix: the fallback HSI result +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "已失败" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "应用更新失败" + +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +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: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "加载本地 dbx 失败" + +#. TRANSLATORS: quirks are device-specific workarounds +msgid "Failed to load quirks" +msgstr "载入特定问题失败" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "加载系统 dbx 失败" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "锁定失败" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "未能解析参数" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "解析文件失败" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "无法解析 --filter 的标志" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "解析本地 dbx 失败" + +#. 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: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "验证 ESP 内容失败" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "文件名" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "文件名签名" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "文件来源" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "需要文件名" + +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "筛选器为一组设备标志,使用 ~ 前缀来排除设备,示例 \"internal,~needs-reboot\"" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "固件库 URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "固件更新 D-Bus 服务" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "固件更新守护程序" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "固件实用程序" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "固件证明" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "固件已被阻止" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "固件已经不受阻" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "固件元数据已有 %u 天未更新,数据可能不是最新的。" + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. refresh recently %1 is an already translated timestamp such +#. as 6 hours or 15 seconds +#, c-format +msgid "Firmware metadata last refresh: %s ago. Use --force to refresh again." +msgstr "固件元数据上次刷新:%s前。使用 --force 再次刷新。" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "固件更新" + +msgid "Firmware updates are not supported on this machine." +msgstr "此机器不支持固件更新。" + +msgid "Firmware updates are supported on this machine." +msgstr "此机器支持固件更新。" + +#. TRANSLATORS: user needs to run a command +msgid "Firmware updates disabled; run 'fwupdmgr unlock' to enable" +msgstr "固件更新已禁用;运行“fwupdmgr unlock”以启用" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "标志" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "通过放宽一些运行时检查来强制操作" + +msgid "Force the action ignoring all warnings" +msgstr "强制操作忽略所有警告" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "找到" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "探测到全盘加密" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "获取 fwupd 支持的全部设备标志" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "获得所有支持更新固件的硬件列表" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "获取所有在系统注册的启用插件" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "获取有关某固件文件的详细信息" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "获取已配置的远程源" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "获取主机安全属性" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "获取已批准固件的列表" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "获取受阻固件的列表" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "获取已连接硬件的可用更新列表" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "获取用于设备的发行版本" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "从最后一次更新中获取结果" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS文件" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "硬件正等待重新插入" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "高" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "主机安全 ID" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU 设备保护已禁用" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU 设备保护已启用" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "空闲…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "下载文件时忽略 SSL 严格检查" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "忽略固件校验和错误" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "忽略固件硬件不匹配错误" + +#. TRANSLATORS: Ignore validation safety checks when flashing this device +msgid "Ignore validation safety checks" +msgstr "忽略验证安全检查" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "正在忽略 SSL 严格检查,要在之后自动应用这一选项请在你的环境里 export DISABLE_SSL_STRICT" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "安装时长" + +#. TRANSLATORS: command description +msgid "Install a firmware blob on a device" +msgstr "在设备上安装固件比特块" + +#. TRANSLATORS: command description +msgid "Install a firmware file on this hardware" +msgstr "在此硬件上安装固件文件" + +msgid "Install old version of signed system firmware" +msgstr "安装旧版本的已签名系统固件" + +msgid "Install old version of unsigned system firmware" +msgstr "安装旧版本的未签名系统固件" + +msgid "Install signed device firmware" +msgstr "安装已签名的设备固件" + +msgid "Install signed system firmware" +msgstr "安装已签名的系统固件" + +#. TRANSLATORS: Install composite firmware on the parent before the child +msgid "Install to parent device first" +msgstr "首先安装到上级设备" + +msgid "Install unsigned device firmware" +msgstr "安装未签名的设备固件" + +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……" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM 保护" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP 保险丝" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard 错误策略" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard 验任启动" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * active means being used by the OS +msgid "Intel CET Active" +msgstr "Intel CET 激活" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "Intel CET Enabled" +msgstr "Intel CET 支持" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Intel SMAP" +msgstr "Intel SMAP" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "内部设备" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "无效" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "处于引导程序模式" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "问题" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "KEY,VALUE" +msgstr "键,值" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +msgid "Keyring" +msgstr "密钥环" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "位置" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "最后修改" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "还剩不到一分钟" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "许可证" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Linux 供应商固件服务(稳定固件)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Linux 供应商固件服务(测试固件)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux 内核" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux 内核锁定" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux 交换区" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "列出 dbx 中的条目" + +#. TRANSLATORS: command line option +msgid "List supported firmware updates" +msgstr "列出所支持固件的更新" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "列出可用的固件类型" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "列出 ESP 分区中的文件" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "正在加载…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "已锁定" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "低" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI 生产模式" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI 覆写" + +msgid "MEI version" +msgstr "MEI 版本" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "手动启用指定插件" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "中" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "元数据签名" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "元数据 URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "可从 Linux 供应商固件服务获取元数据。" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "最小版本" + +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "守护进程与客户端不匹配,使用 %s 来代替" + +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value" +msgstr "修改守护进程的配置值" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "修改给定的远程源" + +msgid "Modify a configured remote" +msgstr "修改已配置的远程源" + +msgid "Modify daemon configuration" +msgstr "修改守护进程配置" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "监视守护程序里的事件" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "挂载 ESP 分区" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "安装完后需要重启" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "需要重启" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "安装完后需要关机" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "新版本" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "未指定操作!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "%s 无可用降级" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "未找到固件 ID" + +#. 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: no repositories to download from +msgid "No releases available" +msgstr "无可用发布版本" + +#. TRANSLATORS: explain why no metadata available +msgid "No remotes are currently enabled so no metadata is available." +msgstr "当前尚未启用任何远程源,因此无可用元数据。" + +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "无可用远程源" + +msgid "No updates available for remaining devices" +msgstr "其余设备没有可用更新" + +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "没有应用任何更新" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "未找到" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "未支持" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "确定" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "只显示单 PCR 值" + +#. TRANSLATORS: command line option +msgid "Only use IPFS when downloading files" +msgstr "下载文件时只使用 IPFS" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "只允许版本升级" + +#. TRANSLATORS: command line option +msgid "Output in JSON format" +msgstr "以 JSON 格式输出" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "覆盖默认的 ESP 路径" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "路径" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "解析并显示固件文件的详细信息" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "正在解析 dbx 更新……" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "正在解析系统 dbx……" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "密码" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "在已知偏移量处对固件二进制文件打补丁" + +msgid "Payload" +msgstr "载荷" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "待处理" + +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "完成百分比" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "执行操作?" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "请输入一个 0 到 %u 之间的数字:" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "插件依赖缺失" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "预启动 DMA 保护" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "上个版本" + +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: a non-free software license +msgid "Proprietary" +msgstr "私有" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "查询固件更新的支持" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "远程ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "远程ID 键 值" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "从设备读取固件比特块(blob)" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "将来自设备的固件读入文件" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "将来自分区的固件读入文件" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "正在读取 %s……" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "正在读取…" + +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "正在重启……" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "刷新来自远程服务器的元数据" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "重新安装 %s 到 %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "在设备上重新安装当前的固件" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "重新安装设备上的固件" + +#. TRANSLATORS: the 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 "正在重新安装 %s,使用 %s…" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree or open-source +msgid "Release Branch" +msgstr "发行分支" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "远程源 ID" + +#. TRANSLATORS: command description +msgid "Replace data in an existing firmware file" +msgstr "用已有的固件文件替换数据" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "报告 URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "已报告到远程服务器" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "所需 efivarfs 文件系统未找到" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "所需设备未找到" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "需要引导程序" + +#. TRANSLATORS: metadata is downloaded from the Internet +msgid "Requires internet connection" +msgstr "需要互联网连接" + +#. 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" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr get-upgrades` for more information." +msgstr "运行 \"fwupdmgr get-upgrades\" 获取更多信息。" + +#. TRANSLATORS: this is shown in the MOTD +msgid "Run `fwupdmgr sync-bkc` to complete this action." +msgstr "运行 `fwupdmgr sync-bkc` 以完成此操作。" + +#. 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 "当使用安装比特块时运行插件组合准备例程" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "运行的内核太老旧" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "运行时后缀" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS 描述符" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS 区域" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI 锁" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI 写入" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "子系统 驱动 [设备ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "保存允许生成硬件 ID 的文件" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "如有可能,安排安装到下次重启" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "正在计划…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "安全启动已禁用" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "安全启动已启用" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "查看 %s 获取更多信息。" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "所选设备" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "所选卷" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "序列号" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +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 all results" +msgstr "显示所有结果" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "显示客户端及守护程序版本" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "显示特定域的守护进程详细信息" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "显示所有域的调试信息" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "显示调试选项" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "显示不可更新的设备" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "显示额外调试信息" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "显示固件更新历史" + +#. TRANSLATORS: this is for plugin development +msgid "Show plugin verbose information" +msgstr "显示插件详细回显信息" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "显示 dbx 的计算版本" + +#. 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 "现在关机?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "用新密钥签名固件" + +msgid "Sign data using the client certificate" +msgstr "使用客户端证书签名数据" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "使用客户端证书签名数据" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "以客户端证书签名上传的数据" + +msgid "Signature" +msgstr "签名" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "大小" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "来源" + +msgid "Specify Vendor/Product ID(s) of DFU device" +msgstr "指定 DFU 设备的供应商/产品 ID" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "指定 dbx 数据库文件" + +msgid "Specify the number of bytes per USB transfer" +msgstr "指定每次 USB 传输的字节数" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "成功" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "激活所有设备成功" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "禁用远程源成功" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata: " +msgstr "已成功下载新元数据:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "已成功启用和刷新远程库" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "启用远程源成功" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "安装固件成功" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "修改配置值成功" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "修改远程源成功" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "手动刷新元数据成功" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "更新设备校验和成功" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "上传 %u 篇报告成功" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "验证设备校验和成功" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "概览" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "已支持" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "支持于远程服务器" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "挂起到空闲" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "挂起到内存" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "切换分支,从 %s 到 %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "切换设备的固件分支" + +#. TRANSLATORS: Must be plugged in to an outlet +msgid "System requires external power source" +msgstr "系统需要外接电源" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "文本" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 重建" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "标签" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "受污染" + +msgid "Target" +msgstr "目标" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "使用 JSON 清单测试设备" + +#. 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: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 跟重建的不同。" + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "守护进程已经加载了第三方代码,并且上游开发者不再支持。" + +#. TRANSLATORS: this is for the device tests, %1 is the device +#. * version, %2 is what we expected +#, c-format +msgid "The device version did not match: got %s, expected %s" +msgstr "设备版本不匹配:得到 %s,期望 %s" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "来源 %s 的固件并非由硬件供应商 %s 提供。" + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "系统时钟没有设置正确,文件下载可能会失败。" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "没有受阻的固件文件" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "没有批准的固件。" + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "此软件包未经验证,可能无法正常工作。" + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "此程序只能以 root 身份正常运行" + +msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." +msgstr "此远程源包含未禁止的固件,但其仍经过硬件供应商测试。你应该确保如果固件更新失败时你能够降级固件。" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "该系统的 HSI 运行时有问题。" + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "该系统的 HSI 安全级别较低。" + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "此工具允许管理员应用 UEFI dbx 更新。" + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to debug UpdateCapsule operation." +msgstr "此工具允许管理员调试 UpdateCapsule 操作。" + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "此工具允许管理员查询和控制 fwupd 守护进程,让其执行如安装或者降级固件等操作。" + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "此工具可以让管理员不用安装 fwupd 插件到主机系统上就可以使用它们。" + +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "此工具只能由 root 用户使用" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "此工具将读取和解析系统固件中的 TPM 事件日志。" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "暂时失败" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "类型" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP 分区未检测到或未配置" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "固件 UEFI 实用工具" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI 胶囊更新在固件设置中不可用或未启用" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx 实用工具" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI 固件无法在传统 BIOS 模式下更新" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI 平台密钥" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI 安全引导" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "无法连接到服务" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "解除当前驱动的绑定" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "正在解除阻止固件:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "解除阻止指定的固件被安装" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "未加密" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "未知" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "未知设备" + +msgid "Unlock the device to allow access" +msgstr "解锁设备以允许访问" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "已解锁" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "为固件访问解锁设备" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "卸载 ESP 分区" + +#. TRANSLATORS: 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: Suffix: the HSI result +msgid "Untainted" +msgstr "无污染" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "可更新" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "更新错误" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "更新消息" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "更新状态" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "更新失败为已知问题,请访问此 URL 以获取详情:" + +#. TRANSLATORS: ask the user if we can update the metadata +msgid "Update now?" +msgstr "现在更新吗?" + +#. TRANSLATORS: Update can only be done from offline mode +msgid "Update requires a reboot" +msgstr "更新需要重启" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "以当前 ROM 内容更新存储的加密哈希" + +msgid "Update the stored device verification information" +msgstr "更新存储的设备验证信息" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "使用当前内容更新存储的元数据" + +msgid "Updating" +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 "Updating %s from %s to %s... " +msgstr "正在更新 %s,从 %s 到 %s…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "正在更新 %s……" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "升级 %s,从 %s 到 %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload 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: how important the release is +msgid "Urgency" +msgstr "紧急性" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdmgr --help for help" +msgstr "使用 fwupdmgr --help 获取帮助信息" + +#. TRANSLATORS: error message explaining command on how to get help +msgid "Use fwupdtool --help for help" +msgstr "使用 fwupdtool --help 获取帮助信息" + +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "安装固件时使用 quirk 标志" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "已通知用户" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "用户名" + +msgid "VID:PID" +msgstr "VID:PID" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "有效" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "正在验证 ESP 内容……" + +#. TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') +msgid "Variant" +msgstr "变型" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "供应商" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "正在验证…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "版本" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING:" +msgstr "警告:" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "等待中..." + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "监视硬件更改" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "将来自文件的固件写入设备" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "将来自文件的固件写入分区" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "写入文件:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "正在写入…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "您的发行商可能尚未认证任何固件的系统及设备兼容性。" + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "你的硬件使用此固件可能会损坏,而且安装此版本固件可能失去 %s 的保修。" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[校验和]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[设备ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[设备ID|GUID] [分支]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[文件 文件签名 远程ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[文件名1] [文件名2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS文件|HWIDS文件]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "默认" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM 事件日志工具" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd 插件" diff --git a/fwupd-1.8.6/po/zh_TW.po b/fwupd-1.8.6/po/zh_TW.po new file mode 100644 index 0000000000000000000000000000000000000000..0e4adea7e6528b6092352009243ca5855ee2d8e1 --- /dev/null +++ b/fwupd-1.8.6/po/zh_TW.po @@ -0,0 +1,662 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# Cheng-Chia Tseng , 2017-2018 +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \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" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_TW\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "剩下 %.0f 分鐘" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "年紀" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "同意並且啟用遠端站點?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "%s 的別名" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "允許降級韌體版本" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "有更新必須重新開機才能完成。" + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "全部的問題都回答是" + +#. TRANSLATORS: command line option +msgid "Apply firmware updates" +msgstr "套用韌體更新" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "連接至韌體模式" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "核對中…" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "必須先通過身份核對才能降級可移除裝置上的韌體" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "必須先通過身份核對才能降級這臺機器上的韌體" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "必須通過身份核對才能修改設定作韌體更新的遠端站點" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "必須先通過身份核對才能解鎖裝置" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "必須先通過身份核對才能更新可移除裝置上的韌體" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "必須先通過身份核對才能更新這臺機器上的韌體" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "必須先通過身份核對才能更新裝置的儲存校驗計算碼" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "取消" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +msgid "Cancelled" +msgstr "已取消" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "已變更" + +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "校驗計算碼" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a device:" +msgstr "選擇裝置:" + +#. TRANSLATORS: get interactive prompt +msgid "Choose a release:" +msgstr "選擇發行版:" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "清除上次更新的結果" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "找不到指令" + +#. TRANSLATORS: DFU stands for device firmware update +msgid "DFU Utility" +msgstr "DFU 公用程式" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "除錯選項" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "解壓縮中…" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "描述說明" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "斷開至開機載入器模式" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "裝置已加入:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "裝置已變更:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "裝置已移除:" + +#. TRANSLATORS: 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 unreported history" +msgstr "不要檢查是否有尚未報告的歷史" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "不要寫入歷史資料庫中" + +#. TRANSLATORS: success +#. success +msgid "Done!" +msgstr "完成!" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +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 版... " + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "下載中…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "從檔案傾印 SMBIOUS 資料" + +#. 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: Suffix: the HSI result +#. TRANSLATORS: Plugin is active and in use +#. 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 "Erase all firmware update history" +msgstr "抹除所有韌體更新歷史" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "抹除中…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "經過一段短暫延遲後便離開" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "引擎載入後離開" + +#. TRANSLATORS: 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: filename of the local file +msgid "Filename" +msgstr "檔名" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "檔名簽章" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "韌體基礎 URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "韌體更新 D-Bus 服務" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "韌體更新幕後程式" + +#. TRANSLATORS: program name +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: Suffix: the HSI result +msgid "Found" +msgstr "找到" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "A single GUID" +msgid "GUID" +msgstr "GUID" + +#. 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 "取得設定的遠端站點" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for connected hardware" +msgstr "取得連接硬體的更新清單" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "取得裝置的發行版本" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "取得上次更新的結果" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +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 signed device firmware" +msgstr "安裝已簽署的裝置韌體" + +msgid "Install signed system firmware" +msgstr "安裝已簽署的系統韌體" + +msgid "Install unsigned device firmware" +msgstr "安裝未簽署的裝置韌體" + +msgid "Install unsigned system 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…" + +#. TRANSLATORS: keyring type, e.g. GPG or PKCS7 +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 line option +msgid "List supported firmware updates" +msgstr "列出支援的韌體更新" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "載入中…" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "中介資料 URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "中介資料可從 Linux 廠商韌體服務取得。" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "修改指定的遠端站點" + +msgid "Modify a configured remote" +msgstr "修改設定的遠端站點" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "監控幕後程式是否有活動" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "未指定動作!" + +#. 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: Suffix: the HSI result +msgid "OK" +msgstr "確定" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "凌駕預設 ESP 路徑" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "密碼" + +msgid "Payload" +msgstr "酬載" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "請輸入 0 到 %u 之間的數字:" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "優先等級" + +msgid "Proceed with upload?" +msgstr "繼續上傳?" + +#. TRANSLATORS: command line option +msgid "Query for firmware update support" +msgstr "查詢韌體更新支援" + +#. TRANSLATORS: command description +msgid "Read firmware from device into a file" +msgstr "將裝置的韌體讀取為檔案" + +#. TRANSLATORS: command description +msgid "Read firmware from one partition into a file" +msgstr "從一分割區將韌體讀取為檔案" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "讀取中…" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "重整遠端伺服器的中介資料" + +#. 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 "正將 %s 重新安裝為 %s 版... " + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "遠端 ID" + +#. TRANSLATORS: command description +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: reboot to apply the update +msgid "Restart now?" +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" + +#. TRANSLATORS: command line option +msgid "Schedule installation for next reboot when possible" +msgstr "安排下次重新開機時若可行便安裝" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "排程中…" + +#. TRANSLATORS: command line option +msgid "Set the debugging flag during update" +msgstr "在更新期間設定除錯旗標" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "和開發者分享韌體歷史" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +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: one line summary of device +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(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 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 type, e.g. remote or local +msgid "Type" +msgstr "類型" + +#. TRANSLATORS: program name +msgid "UEFI Firmware Utility" +msgstr "UEFI 韌體公用程式" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +msgid "Unknown" +msgstr "未知" + +msgid "Unlock the device to allow access" +msgstr "解鎖裝置以允許存取" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "解鎖裝置以供韌體存取" + +#. TRANSLATORS: command line option +msgid "Unset the debugging flag during update" +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 "更新儲存的裝置核驗資訊" + +#. 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 "正將 %s 從 %s 版升級至 %s 版... " + +#. 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 "使用者名稱" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "核驗中…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "版本" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "等候中…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "監看硬體是否有變更" + +#. TRANSLATORS: command description +msgid "Write firmware from file into device" +msgstr "從檔案將韌體寫入裝置" + +#. TRANSLATORS: command description +msgid "Write firmware from file into one partition" +msgstr "從檔案將韌體寫入一分割區" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "寫入中…" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "您的系統散布商可能尚未驗證過任何韌體更新與您系統間或連接裝置上的相容性。" diff --git a/fwupd-1.8.6/policy/its/polkit.its b/fwupd-1.8.6/policy/its/polkit.its new file mode 100644 index 0000000000000000000000000000000000000000..1c37e6bee7f817abe122d1c0751f6ed0df523494 --- /dev/null +++ b/fwupd-1.8.6/policy/its/polkit.its @@ -0,0 +1,8 @@ + + + + + diff --git a/fwupd-1.8.6/policy/its/polkit.loc b/fwupd-1.8.6/policy/its/polkit.loc new file mode 100644 index 0000000000000000000000000000000000000000..c7427ec6722f762400f9258478b1723e5ae8de7b --- /dev/null +++ b/fwupd-1.8.6/policy/its/polkit.loc @@ -0,0 +1,6 @@ + + + + + + diff --git a/fwupd-1.8.6/policy/meson.build b/fwupd-1.8.6/policy/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..5bc37f1bd3fbf56f2c12a5bc06785baddb88f96a --- /dev/null +++ b/fwupd-1.8.6/policy/meson.build @@ -0,0 +1,25 @@ +install_data('org.freedesktop.fwupd.rules', + install_dir: join_paths(datadir, 'polkit-1', 'rules.d')) + +#newer polkit has the ITS rules included +if polkit.version().version_compare('>0.113') + i18n.merge_file( + input: 'org.freedesktop.fwupd.policy.in', + output: 'org.freedesktop.fwupd.policy', + install: true, + install_dir: join_paths(datadir, 'polkit-1', 'actions') , + type: 'xml', + po_dir: join_paths(meson.project_source_root(), 'po') + ) +#older polkit is missing ITS rules and will fail +else + i18n.merge_file( + input: 'org.freedesktop.fwupd.policy.in', + output: 'org.freedesktop.fwupd.policy', + install: true, + install_dir: join_paths(datadir, 'polkit-1', 'actions') , + type: 'xml', + data_dirs: join_paths(meson.project_source_root(), 'policy'), + po_dir: join_paths(meson.project_source_root(), 'po') + ) +endif diff --git a/fwupd-1.8.6/policy/org.freedesktop.fwupd-unsafe.rules b/fwupd-1.8.6/policy/org.freedesktop.fwupd-unsafe.rules new file mode 100644 index 0000000000000000000000000000000000000000..3f88d1a89b194aaef0af4c9823ba95722a565266 --- /dev/null +++ b/fwupd-1.8.6/policy/org.freedesktop.fwupd-unsafe.rules @@ -0,0 +1,6 @@ +polkit.addRule(function(action, subject) { + if (action.id.startsWith("org.freedesktop.fwupd.") && + subject.isInGroup("wheel")) { + return polkit.Result.YES; + } +}); diff --git a/fwupd-1.8.6/policy/org.freedesktop.fwupd.policy.in b/fwupd-1.8.6/policy/org.freedesktop.fwupd.policy.in new file mode 100644 index 0000000000000000000000000000000000000000..b7e9952fd86d1625c05a32a8906b2e66af6d1c0f --- /dev/null +++ b/fwupd-1.8.6/policy/org.freedesktop.fwupd.policy.in @@ -0,0 +1,210 @@ + + + + + + + System firmware update + https://github.com/fwupd/fwupd + application-x-firmware + + + Install signed system firmware + + Authentication is required to update the firmware on this machine + + auth_admin + no + yes + + + + + Install unsigned system firmware + + Authentication is required to update the firmware on this machine + + auth_admin + no + auth_admin_keep + + org.freedesktop.fwupd.update-internal-trusted + + + + Install old version of signed system firmware + + Authentication is required to downgrade the firmware on this machine + + auth_admin + no + auth_admin_keep + + org.freedesktop.fwupd.update-internal + + + + Install old version of unsigned system firmware + + Authentication is required to downgrade the firmware on this machine + + auth_admin + no + auth_admin_keep + + org.freedesktop.fwupd.downgrade-internal-trusted + + + + Install signed device firmware + + Authentication is required to update the firmware on a removable device + + auth_admin + no + yes + + + + + Install unsigned device firmware + + Authentication is required to update the firmware on a removable device + + auth_admin + no + auth_admin_keep + + org.freedesktop.fwupd.update-hotplug-trusted + + + + Install signed device firmware + + Authentication is required to downgrade the firmware on a removable device + + auth_admin + no + auth_admin_keep + + + + + Install unsigned device firmware + + Authentication is required to downgrade the firmware on a removable device + + auth_admin + no + auth_admin_keep + + org.freedesktop.fwupd.downgrade-hotplug-trusted + + + + Unlock the device to allow access + + Authentication is required to unlock a device + + auth_admin + no + auth_admin_keep + + + + + Modify daemon configuration + + Authentication is required to modify daemon configuration + + auth_admin + no + auth_admin_keep + + org.freedesktop.fwupd.modify-remote + + + + 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 + + Authentication is required to update the stored checksums for the device + + auth_admin + no + auth_admin_keep + + + + + Modify a configured remote + + Authentication is required to modify a configured remote used for firmware updates + + 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 + + + + + Get BIOS settings + + Authentication is required to read BIOS settings + + auth_admin + no + auth_admin_keep + + + + + Set one or more BIOS settings + + Authentication is required to modify BIOS settings + + auth_admin + no + auth_admin + + org.freedesktop.fwupd.get-bios-settings + + + diff --git a/fwupd-1.8.6/policy/org.freedesktop.fwupd.rules b/fwupd-1.8.6/policy/org.freedesktop.fwupd.rules new file mode 100644 index 0000000000000000000000000000000000000000..651d4e355a58de07be1beb4e0f838986228e285a --- /dev/null +++ b/fwupd-1.8.6/policy/org.freedesktop.fwupd.rules @@ -0,0 +1,7 @@ +polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.fwupd.update-internal" && + subject.active == true && subject.local == true && + subject.isInGroup("wheel")) { + return polkit.Result.YES; + } +}); diff --git a/fwupd-1.8.6/snap/hooks/install b/fwupd-1.8.6/snap/hooks/install new file mode 100755 index 0000000000000000000000000000000000000000..bad2d65204f5397aba4108633a949ec39f52a6fb --- /dev/null +++ b/fwupd-1.8.6/snap/hooks/install @@ -0,0 +1,27 @@ +#!/bin/sh -e + +install_if_missing() { + directory=$(dirname ${2}/${1}) + if [ "$2" != "/" ]; then + mkdir -p $directory + fi + if [ -d $directory ]; then + install -m 644 -C ${SNAP}/${1} ${2}/${1} + fi +} + +#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/system.d/org.freedesktop.fwupd.conf /usr +#activation via systemd +install_if_missing etc/systemd/system/fwupd-activate.service / +systemctl daemon-reload +systemctl enable fwupd-activate +systemctl start fwupd-activate +#kernel modules +install_if_missing usr/lib/modules-load.d/fwupd-msr.conf / +#optional grub configuration +install_if_missing etc/grub.d/35_fwupd / diff --git a/fwupd-1.8.6/snap/hooks/remove b/fwupd-1.8.6/snap/hooks/remove new file mode 100755 index 0000000000000000000000000000000000000000..31cd91d1ab03f73de1331946ff76b668ad304ca2 --- /dev/null +++ b/fwupd-1.8.6/snap/hooks/remove @@ -0,0 +1,11 @@ +#!/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 +#msr module +rm /usr/lib/modules-load.d/fwupd-msr.conf -f +#optional grub configuration +rm /etc/grub.d/35_fwupd -f diff --git a/fwupd-1.8.6/snap/snapcraft.yaml b/fwupd-1.8.6/snap/snapcraft.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b102f7587f6196f58452e68788a7d92fb9b38a07 --- /dev/null +++ b/fwupd-1.8.6/snap/snapcraft.yaml @@ -0,0 +1,226 @@ +name: fwupd +adopt-info: fwupd +summary: A standalone version of fwupd to install newer firmware updates +description: | + This is a tool that can be used to install firmware updates on devices + not yet supported by the version of fwupd distributed with the OS. + +grade: stable +confinement: classic +base: core22 + +architectures: + - amd64 + +apps: + dfu-tool: + command: dfu-tool.wrapper + dbxtool: + command: dbxtool.wrapper + fwupdtool: + command: fwupdtool.wrapper + completer: + share/bash-completion/completions/fwupdtool + fwupd: + command: fwupd.wrapper + daemon: simple + fwupdmgr: + command: fwupdmgr.wrapper + completer: + share/bash-completion/completions/fwupdmgr + fwupdagent: + command: fwupdagent.wrapper + +parts: + #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-cairo + prime: + - -etc + - -usr + - -var + fwupd: + plugin: meson + meson-parameters: [--prefix=/, + -Defi_binary=false, + -Dplugin_uefi_capsule_splash=false, + -Dtests=false, + -Dbuild=all, + -Dintrospection=disabled, + -Dman=false, + -Dplugin_powerd=disabled, + -Dudevdir=$SNAPCRAFT_STAGE/lib/udev, + "-Dgusb:tests=false", + "-Dgusb:docs=false", + "-Dgusb:introspection=false", + "-Dgusb:vapi=false", + "-Dlibxmlb:gtkdoc=false", + "-Dlibxmlb:introspection=false", + "-Dlibjcat:man=false", + "-Dlibjcat:gtkdoc=false", + "-Dlibjcat:introspection=false", + "-Dlibjcat:tests=false"] + source: . + source-type: git + override-build: | + snapcraftctl build + echo $(git describe HEAD --always) > $SNAPCRAFT_STAGE/version + override-pull: | + snapcraftctl pull + snapcraftctl set-version $(git describe HEAD --always) + build-packages: + - bash-completion + - curl + - gettext + - gcab + - gnu-efi + - libcurl4-openssl-dev + - libarchive-dev + - libcbor-dev + - libcairo-dev + - libefiboot-dev + - libefivar-dev + - libflashrom-dev + - libftdi1-dev + - libgudev-1.0-dev + - libgcab-dev + - libglib2.0-dev + - libgnutls28-dev + - libgpgme11-dev + - libgusb-dev + - libjson-glib-dev + - libjcat-dev + - liblzma-dev + - libpango1.0-dev + - libpci-dev + - libpolkit-gobject-1-dev + - libprotobuf-c-dev + - libsmbios-dev + - libsqlite3-dev + - libsystemd-dev + - libtss2-dev + - libxmlb-dev + - libqmi-glib-dev + - libmbim-glib-dev + - locales + - meson + - modemmanager + - pkg-config + - python3-cairo + - python3-gi + - protobuf-c-compiler + - systemd + - uuid-dev + stage-packages: + - libgcab-1.0-0 + - libarchive13 + - libcurl4 + - libcbor0.8 + - libassuan0 + - libflashrom1 + - libjcat1 + - liblcms2-2 + - liblzma5 + - libefivar1 + - libefiboot1 + - libgusb2 + - libusb-1.0-0 + - libgudev-1.0-0 + - libgpgme11 + - libpolkit-gobject-1-0 + - libsmbios-c2 + - libtss2-esys-3.0.2-0 + - libtss2-fapi1 + - libtss2-mu0 + - libtss2-rc0 + - libtss2-sys1 + - libtss2-tcti-cmd0 + - libtss2-tcti-device0 + - libtss2-tcti-mssim0 + - libtss2-tcti-swtpm0 + - libtss2-tctildr0 + - libxmlb2 + - glib-networking + - libglib2.0-bin + - libglib2.0-0 + prime: + # we explicitly don't want /usr/bin/gpgconf + # this will cause gpgme to error finding it + # but that also avoids trying to use non-existent + # /usr/bin/gpg2 + - -usr/bin + - -usr/sbin + - -usr/share/man + - -usr/share/GConf + - -etc/X11 + - -etc/ldap + - -etc/logcheck + - -usr/lib/dconf + - -usr/lib/gcc + - -usr/lib/glib-networking + - -usr/lib/gnupg2 + - -usr/lib/sasl2 + - -usr/lib/systemd + - -usr/lib/*/audit + - -usr/share/glib-2.0/schemas + - -usr/share/X11 + - -include + - -lib/udev + - -lib/*/pkgconfig + - -usr/share/lintian + - -usr/share/pkgconfig + - -usr/share/installed-tests + - -usr/share/polkit-1 + - -usr/share/vala + - -usr/share/doc + - -usr/share/gnupg2 + - -usr/share/info + - -usr/share/gir-1.0 + - -usr/share/upstart + - -usr/lib/*/pkgconfig + after: [build-introspection] + 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 + stage-packages: + - shared-mime-info + - gsettings-desktop-schemas + - libxml2 + prime: + - -usr/bin + - -usr/share/doc + - -usr/share/doc-base + - -usr/share/man + - -usr/share/lintian + - -usr/share/pkgconfig + - -usr/share/GConf + after: [fwupd] + fwupd-wrappers: + plugin: dump + source: contrib/snap + stage: + - dfu-tool.wrapper + - dbxtool.wrapper + - fwupd-command + - fwupdtool.wrapper + - fwupd.wrapper + - fwupdmgr.wrapper + - fwupdagent.wrapper diff --git a/fwupd-1.8.6/src/README.md b/fwupd-1.8.6/src/README.md new file mode 100644 index 0000000000000000000000000000000000000000..77d72de2b7f7663f30bebd27b50881485776ce81 --- /dev/null +++ b/fwupd-1.8.6/src/README.md @@ -0,0 +1,173 @@ +# 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. `USB\VID_0763&PID_2806` +* Value: the plugin name, e.g. `csr` +* Minimum fwupd version: **1.1.0** + +### Flags + +Assigns optional quirks to use for a 8bitdo device + +* Key: the device ID, e.g. `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. `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. `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. `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. `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. `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. `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. `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. `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. `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. `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. `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. `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. `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. `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. + +```ini +[USB\VID_0A12&PID_1337] +Plugin = dfu_csr +Name = H05 +Summary = Bluetooth Headphones +Icon = audio-headphones +Vendor = AIAIAI +[USB\VID_0A12&PID_1337&REV_2520] +Version = 1.2 +``` + +Additional samples can be found in other plugins. diff --git a/fwupd-1.8.6/src/fu-bluez-backend.c b/fwupd-1.8.6/src/fu-bluez-backend.c new file mode 100644 index 0000000000000000000000000000000000000000..9530bb3b812be5a34ac1bf20493a1260f7a30bb8 --- /dev/null +++ b/fwupd-1.8.6/src/fu-bluez-backend.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2021 Ricardo Cañuelo + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuBackend" + +#include "config.h" + +#include "fu-bluez-backend.h" +#include "fu-bluez-device.h" + +struct _FuBluezBackend { + FuBackend parent_instance; + GDBusObjectManager *object_manager; +}; + +G_DEFINE_TYPE(FuBluezBackend, fu_bluez_backend, FU_TYPE_BACKEND) + +#define FU_BLUEZ_BACKEND_TIMEOUT 1500 /* ms */ + +static void +fu_bluez_backend_object_properties_changed(FuBluezBackend *self, GDBusProxy *proxy) +{ + const gchar *path = g_dbus_proxy_get_object_path(proxy); + gboolean suitable; + FuDevice *device_tmp; + g_autoptr(FuBluezDevice) dev = NULL; + g_autoptr(GVariant) val_connected = NULL; + g_autoptr(GVariant) val_paired = NULL; + + /* device is suitable */ + val_connected = g_dbus_proxy_get_cached_property(proxy, "Connected"); + if (val_connected == NULL) + return; + val_paired = g_dbus_proxy_get_cached_property(proxy, "Paired"); + if (val_paired == NULL) + return; + suitable = g_variant_get_boolean(val_connected) && g_variant_get_boolean(val_paired); + + /* is this an existing device we've previously added */ + device_tmp = fu_backend_lookup_by_id(FU_BACKEND(self), path); + if (device_tmp != NULL) { + if (suitable) { + g_debug("ignoring suitable changed BlueZ device: %s", path); + return; + } + g_debug("removing unsuitable BlueZ device: %s", path); + fu_backend_device_removed(FU_BACKEND(self), device_tmp); + return; + } + + /* not paired and connected */ + if (!suitable) + return; + + /* create device */ + dev = g_object_new(FU_TYPE_BLUEZ_DEVICE, + "backend-id", + path, + "object-manager", + self->object_manager, + "proxy", + proxy, + NULL); + g_debug("adding suitable BlueZ device: %s", path); + fu_backend_device_added(FU_BACKEND(self), FU_DEVICE(dev)); +} + +static void +fu_bluez_backend_object_properties_changed_cb(GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + FuBluezBackend *self) +{ + fu_bluez_backend_object_properties_changed(self, proxy); +} + +static void +fu_bluez_backend_object_added(FuBluezBackend *self, GDBusObject *object) +{ + g_autoptr(GDBusInterface) iface = NULL; + + iface = g_dbus_object_get_interface(object, "org.bluez.Device1"); + if (iface == NULL) + return; + g_signal_connect(G_DBUS_INTERFACE(iface), + "g-properties-changed", + G_CALLBACK(fu_bluez_backend_object_properties_changed_cb), + self); + fu_bluez_backend_object_properties_changed(self, G_DBUS_PROXY(iface)); +} + +static void +fu_bluez_backend_object_added_cb(GDBusObjectManager *manager, + GDBusObject *object, + FuBluezBackend *self) +{ + fu_bluez_backend_object_added(self, object); +} + +static void +fu_bluez_backend_object_removed_cb(GDBusObjectManager *manager, + GDBusObject *object, + FuBluezBackend *self) +{ + const gchar *path = g_dbus_object_get_object_path(object); + FuDevice *device_tmp; + + device_tmp = fu_backend_lookup_by_id(FU_BACKEND(self), path); + if (device_tmp == NULL) + return; + g_debug("removing BlueZ device: %s", path); + fu_backend_device_removed(FU_BACKEND(self), device_tmp); +} + +typedef struct { + GDBusObjectManager *object_manager; + GMainLoop *loop; + GError **error; + GCancellable *cancellable; + guint timeout_id; +} FuBluezBackendHelper; + +static void +fu_bluez_backend_helper_free(FuBluezBackendHelper *helper) +{ + if (helper->object_manager != NULL) + g_object_unref(helper->object_manager); + if (helper->timeout_id != 0) + g_source_remove(helper->timeout_id); + g_cancellable_cancel(helper->cancellable); + g_main_loop_unref(helper->loop); + g_free(helper); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuBluezBackendHelper, fu_bluez_backend_helper_free) + +static void +fu_bluez_backend_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + FuBluezBackendHelper *helper = (FuBluezBackendHelper *)user_data; + helper->object_manager = + g_dbus_object_manager_client_new_for_bus_finish(res, helper->error); + g_main_loop_quit(helper->loop); +} + +static gboolean +fu_bluez_backend_timeout_cb(gpointer user_data) +{ + FuBluezBackendHelper *helper = (FuBluezBackendHelper *)user_data; + g_cancellable_cancel(helper->cancellable); + helper->timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static gboolean +fu_bluez_backend_setup(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuBluezBackend *self = FU_BLUEZ_BACKEND(backend); + g_autoptr(FuBluezBackendHelper) helper = g_new0(FuBluezBackendHelper, 1); + + /* in some circumstances the bluez daemon will just hang... do not wait + * forever and make fwupd startup also fail */ + helper->error = error; + helper->loop = g_main_loop_new(NULL, FALSE); + helper->cancellable = g_cancellable_new(); + helper->timeout_id = + g_timeout_add(FU_BLUEZ_BACKEND_TIMEOUT, fu_bluez_backend_timeout_cb, helper); + g_dbus_object_manager_client_new_for_bus(G_BUS_TYPE_SYSTEM, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + "org.bluez", + "/", + NULL, + NULL, + NULL, + helper->cancellable, + fu_bluez_backend_connect_cb, + helper); + g_main_loop_run(helper->loop); + if (helper->object_manager == NULL) + return FALSE; + self->object_manager = g_steal_pointer(&helper->object_manager); + + g_signal_connect(G_DBUS_OBJECT_MANAGER(self->object_manager), + "object-added", + G_CALLBACK(fu_bluez_backend_object_added_cb), + self); + g_signal_connect(G_DBUS_OBJECT_MANAGER(self->object_manager), + "object-removed", + G_CALLBACK(fu_bluez_backend_object_removed_cb), + self); + return TRUE; +} + +static gboolean +fu_bluez_backend_coldplug(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuBluezBackend *self = FU_BLUEZ_BACKEND(backend); + g_autolist(GDBusObject) objects = NULL; + + /* failed to set up */ + if (self->object_manager == NULL) + return TRUE; + objects = g_dbus_object_manager_get_objects(self->object_manager); + for (GList *l = objects; l != NULL; l = l->next) { + GDBusObject *object = G_DBUS_OBJECT(l->data); + fu_bluez_backend_object_added(self, object); + } + return TRUE; +} + +static void +fu_bluez_backend_finalize(GObject *object) +{ + FuBluezBackend *self = FU_BLUEZ_BACKEND(object); + if (self->object_manager != NULL) + g_object_unref(self->object_manager); + G_OBJECT_CLASS(fu_bluez_backend_parent_class)->finalize(object); +} + +static void +fu_bluez_backend_init(FuBluezBackend *self) +{ +} + +static void +fu_bluez_backend_class_init(FuBluezBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + + object_class->finalize = fu_bluez_backend_finalize; + klass_backend->setup = fu_bluez_backend_setup; + klass_backend->coldplug = fu_bluez_backend_coldplug; +} + +FuBackend * +fu_bluez_backend_new(FuContext *ctx) +{ + return FU_BACKEND( + g_object_new(FU_TYPE_BLUEZ_BACKEND, "name", "bluez", "context", ctx, NULL)); +} diff --git a/fwupd-1.8.6/src/fu-bluez-backend.h b/fwupd-1.8.6/src/fu-bluez-backend.h new file mode 100644 index 0000000000000000000000000000000000000000..2657c8019fe81658e420bdfebbfbef98dab86b75 --- /dev/null +++ b/fwupd-1.8.6/src/fu-bluez-backend.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2021 + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-backend.h" + +#define FU_TYPE_BLUEZ_BACKEND (fu_bluez_backend_get_type()) +G_DECLARE_FINAL_TYPE(FuBluezBackend, fu_bluez_backend, FU, BLUEZ_BACKEND, FuBackend) + +FuBackend * +fu_bluez_backend_new(FuContext *ctx); diff --git a/fwupd-1.8.6/src/fu-cabinet-common.c b/fwupd-1.8.6/src/fu-cabinet-common.c new file mode 100644 index 0000000000000000000000000000000000000000..6681258f87b8443ef3012ff5350a646b5d28334c --- /dev/null +++ b/fwupd-1.8.6/src/fu-cabinet-common.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommonCab" + +#include "config.h" + +#include "fu-cabinet-common.h" +#include "fu-cabinet.h" + +/** + * fu_cabinet_build_silo: (skip): + * @blob: A readable blob + * @size_max: the maximum size of the archive + * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN + * + * Create an AppStream silo from a cabinet archive. + * + * Returns: (transfer full): a #XbSilo, or %NULL on error + * + * Since: 1.2.0 + **/ +XbSilo * +fu_cabinet_build_silo(GBytes *blob, guint64 size_max, GError **error) +{ + g_autoptr(FuCabinet) cabinet = fu_cabinet_new(); + + g_return_val_if_fail(blob != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + fu_cabinet_set_size_max(cabinet, size_max); + if (!fu_cabinet_parse(cabinet, blob, FU_CABINET_PARSE_FLAG_NONE, error)) + return NULL; + return fu_cabinet_get_silo(cabinet); +} diff --git a/fwupd-1.8.6/src/fu-cabinet-common.h b/fwupd-1.8.6/src/fu-cabinet-common.h new file mode 100644 index 0000000000000000000000000000000000000000..53e61f3175a7694064f9400b0c211481ea39399a --- /dev/null +++ b/fwupd-1.8.6/src/fu-cabinet-common.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +XbSilo * +fu_cabinet_build_silo(GBytes *blob, guint64 size_max, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/src/fu-config.c b/fwupd-1.8.6/src/fu-config.c new file mode 100644 index 0000000000000000000000000000000000000000..0aa702eb51b15d204488c33b4283b7baf702a9e5 --- /dev/null +++ b/fwupd-1.8.6/src/fu-config.c @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuConfig" + +#include "config.h" + +#include + +#include "fu-config.h" + +enum { SIGNAL_CHANGED, SIGNAL_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +static void +fu_config_finalize(GObject *obj); + +struct _FuConfig { + GObject parent_instance; + GPtrArray *monitors; /* (element-type GFileMonitor) */ + GPtrArray *disabled_devices; /* (element-type utf-8) */ + GPtrArray *disabled_plugins; /* (element-type utf-8) */ + GPtrArray *approved_firmware; /* (element-type utf-8) */ + GPtrArray *blocked_firmware; /* (element-type utf-8) */ + GPtrArray *uri_schemes; /* (element-type utf-8) */ + GPtrArray *filenames; /* (element-type utf-8) */ + GArray *trusted_uids; /* (elementy type guint64) */ + guint64 archive_size_max; + guint idle_timeout; + gchar *host_bkc; + gchar *esp_location; + gboolean update_motd; + gboolean enumerate_all_devices; + gboolean ignore_power; + gboolean only_trusted; + gboolean show_device_private; +}; + +G_DEFINE_TYPE(FuConfig, fu_config, G_TYPE_OBJECT) + +static void +fu_config_emit_changed(FuConfig *self) +{ + g_debug("::configuration changed"); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0); +} + +static gboolean +fu_config_reload(FuConfig *self, GError **error) +{ + guint64 archive_size_max; + guint idle_timeout; + g_auto(GStrv) approved_firmware = NULL; + g_auto(GStrv) blocked_firmware = NULL; + g_auto(GStrv) uri_schemes = NULL; + g_auto(GStrv) devices = NULL; + g_auto(GStrv) uids = NULL; + g_auto(GStrv) plugins = NULL; + g_autofree gchar *domains = NULL; + g_autofree gchar *host_bkc = NULL; + g_autofree gchar *esp_location = NULL; + g_autoptr(GKeyFile) keyfile = g_key_file_new(); + g_autoptr(GError) error_update_motd = NULL; + g_autoptr(GError) error_ignore_power = NULL; + g_autoptr(GError) error_only_trusted = NULL; + g_autoptr(GError) error_show_device_private = NULL; + g_autoptr(GError) error_enumerate_all = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* we have to load each file into a buffer as g_key_file_load_from_file() clears the + * GKeyFile state before loading each file, and we want to allow the mutable version to be + * incomplete and just *override* a specific option */ + for (guint i = 0; i < self->filenames->len; i++) { + const gchar *fn = g_ptr_array_index(self->filenames, i); + g_debug("trying to load config values from %s", fn); + if (g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_autoptr(GBytes) blob = fu_bytes_get_contents(fn, error); + if (blob == NULL) + return FALSE; + fu_byte_array_append_bytes(buf, blob); + } + } + + /* load if either file found */ + if (buf->len > 0) { + if (!g_key_file_load_from_data(keyfile, + (const gchar *)buf->data, + buf->len, + G_KEY_FILE_NONE, + error)) + return FALSE; + } + + /* get disabled devices */ + g_ptr_array_set_size(self->disabled_devices, 0); + devices = g_key_file_get_string_list(keyfile, + "fwupd", + "DisabledDevices", + NULL, /* length */ + NULL); + if (devices != NULL) { + for (guint i = 0; devices[i] != NULL; i++) { + g_ptr_array_add(self->disabled_devices, g_strdup(devices[i])); + } + } + + /* get disabled plugins */ + g_ptr_array_set_size(self->disabled_plugins, 0); + plugins = g_key_file_get_string_list(keyfile, + "fwupd", + "DisabledPlugins", + NULL, /* length */ + NULL); + if (plugins != NULL) { + for (guint i = 0; plugins[i] != NULL; i++) { + g_ptr_array_add(self->disabled_plugins, g_strdup(plugins[i])); + } + } + + /* get approved firmware */ + g_ptr_array_set_size(self->approved_firmware, 0); + approved_firmware = g_key_file_get_string_list(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 blocked firmware */ + g_ptr_array_set_size(self->blocked_firmware, 0); + blocked_firmware = g_key_file_get_string_list(keyfile, + "fwupd", + "BlockedFirmware", + NULL, /* length */ + NULL); + if (blocked_firmware != NULL) { + for (guint i = 0; blocked_firmware[i] != NULL; i++) { + g_ptr_array_add(self->blocked_firmware, g_strdup(blocked_firmware[i])); + } + } + + /* get download schemes */ + g_ptr_array_set_size(self->uri_schemes, 0); + uri_schemes = g_key_file_get_string_list(keyfile, + "fwupd", + "UriSchemes", + NULL, /* length */ + NULL); + if (uri_schemes != NULL) { + for (guint i = 0; uri_schemes[i] != NULL; i++) { + g_ptr_array_add(self->uri_schemes, g_strdup(uri_schemes[i])); + } + } + if (self->uri_schemes->len == 0) { + g_ptr_array_add(self->uri_schemes, g_strdup("file")); + g_ptr_array_add(self->uri_schemes, g_strdup("https")); + g_ptr_array_add(self->uri_schemes, g_strdup("http")); + g_ptr_array_add(self->uri_schemes, g_strdup("ipfs")); + } + + /* get maximum archive size, defaulting to something sane */ + archive_size_max = g_key_file_get_uint64(keyfile, "fwupd", "ArchiveSizeMax", NULL); + if (archive_size_max > 0) { + self->archive_size_max = archive_size_max * 0x100000; + } else { + guint64 memory_size = fu_common_get_memory_size(); + g_autofree gchar *str = NULL; + if (memory_size > 0) { + self->archive_size_max = MIN(memory_size / 4, G_MAXUINT32); + str = g_format_size(self->archive_size_max); + g_debug("using autodetected max archive size %s", str); + } else { + self->archive_size_max = 512 * 0x100000; + str = g_format_size(self->archive_size_max); + g_debug("using fallback max archive size %s", str); + } + } + + /* get idle timeout */ + idle_timeout = g_key_file_get_uint64(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(keyfile, "fwupd", "VerboseDomains", NULL); + if (domains != NULL && domains[0] != '\0') + (void)g_setenv("FWUPD_VERBOSE", domains, TRUE); + + /* whether to update the motd on changes */ + self->update_motd = + g_key_file_get_boolean(keyfile, "fwupd", "UpdateMotd", &error_update_motd); + if (!self->update_motd && error_update_motd != NULL) { + g_debug("failed to read UpdateMotd key: %s", error_update_motd->message); + self->update_motd = TRUE; + } + + /* whether to only show supported devices for some plugins */ + self->enumerate_all_devices = + g_key_file_get_boolean(keyfile, "fwupd", "EnumerateAllDevices", &error_enumerate_all); + /* if error parsing or missing, we want to default to true */ + if (!self->enumerate_all_devices && error_enumerate_all != NULL) { + g_debug("failed to read EnumerateAllDevices key: %s", error_enumerate_all->message); + self->enumerate_all_devices = TRUE; + } + + /* whether to ignore power levels for updates */ + self->ignore_power = + g_key_file_get_boolean(keyfile, "fwupd", "IgnorePower", &error_ignore_power); + if (!self->ignore_power && error_ignore_power != NULL) { + g_debug("failed to read IgnorePower key: %s", error_ignore_power->message); + self->ignore_power = FALSE; + } + + /* whether to allow untrusted firmware *at all* even with PolicyKit auth */ + self->only_trusted = + g_key_file_get_boolean(keyfile, "fwupd", "OnlyTrusted", &error_only_trusted); + if (!self->only_trusted && error_only_trusted != NULL) { + g_debug("failed to read OnlyTrusted key: %s", error_only_trusted->message); + self->only_trusted = TRUE; + } + + /* whether to show private device data, e.g. serial numbers */ + self->show_device_private = g_key_file_get_boolean(keyfile, + "fwupd", + "ShowDevicePrivate", + &error_show_device_private); + if (!self->show_device_private && error_show_device_private != NULL) { + g_debug("failed to read ShowDevicePrivate key: %s", + error_show_device_private->message); + self->show_device_private = TRUE; + } + + /* fetch host best known configuration */ + host_bkc = g_key_file_get_string(keyfile, "fwupd", "HostBkc", NULL); + if (host_bkc != NULL && host_bkc[0] != '\0') + self->host_bkc = g_steal_pointer(&host_bkc); + + /* fetch hardcoded ESP mountpoint */ + esp_location = g_key_file_get_string(keyfile, "fwupd", "EspLocation", NULL); + if (esp_location != NULL && esp_location[0] != '\0') + self->esp_location = g_steal_pointer(&esp_location); + + /* get trusted uids */ + g_array_set_size(self->trusted_uids, 0); + uids = g_key_file_get_string_list(keyfile, + "fwupd", + "TrustedUids", + NULL, /* length */ + NULL); + if (uids != NULL) { + for (guint i = 0; uids[i] != NULL; i++) { + guint64 val = 0; + if (!fu_strtoull(uids[i], &val, 0, G_MAXUINT64, error)) { + g_prefix_error(error, "failed to parse UID '%s'", uids[i]); + return FALSE; + } + g_array_append_val(self->trusted_uids, val); + } + } + + return TRUE; +} + +static void +fu_config_monitor_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuConfig *self = FU_CONFIG(user_data); + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = g_file_get_path(file); + g_debug("%s changed, reloading all configs", fn); + if (!fu_config_reload(self, &error)) + g_warning("failed to rescan daemon config: %s", error->message); + fu_config_emit_changed(self); +} + +gboolean +fu_config_set_key_value(FuConfig *self, const gchar *key, const gchar *value, GError **error) +{ + g_autoptr(GKeyFile) keyfile = g_key_file_new(); + const gchar *fn; + + /* sanity check */ + if (self->filenames->len == 0) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "no config to load"); + return FALSE; + } + + /* only write the file in /etc */ + fn = g_ptr_array_index(self->filenames, 0); + if (!g_key_file_load_from_file(keyfile, fn, G_KEY_FILE_KEEP_COMMENTS, error)) + return FALSE; + g_key_file_set_string(keyfile, "fwupd", key, value); + if (!g_key_file_save_to_file(keyfile, fn, error)) + return FALSE; + + return fu_config_reload(self, error); +} + +gboolean +fu_config_load(FuConfig *self, GError **error) +{ + g_autofree gchar *configdir_mut = fu_path_from_kind(FU_PATH_KIND_LOCALCONFDIR_PKG); + g_autofree gchar *configdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + + g_return_val_if_fail(FU_IS_CONFIG(self), FALSE); + g_return_val_if_fail(self->filenames->len == 0, FALSE); + + /* load the main daemon config file */ + g_ptr_array_add(self->filenames, g_build_filename(configdir, "daemon.conf", NULL)); + g_ptr_array_add(self->filenames, g_build_filename(configdir_mut, "daemon.conf", NULL)); + if (!fu_config_reload(self, error)) + return FALSE; + + /* set up a notify watches */ + for (guint i = 0; i < self->filenames->len; i++) { + const gchar *fn = g_ptr_array_index(self->filenames, i); + g_autoptr(GFile) file = g_file_new_for_path(fn); + g_autoptr(GFileMonitor) monitor = NULL; + + monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); + if (monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(monitor), + "changed", + G_CALLBACK(fu_config_monitor_changed_cb), + self); + g_ptr_array_add(self->monitors, g_steal_pointer(&monitor)); + } + + /* success */ + return TRUE; +} + +guint +fu_config_get_idle_timeout(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), 0); + return self->idle_timeout; +} + +GPtrArray * +fu_config_get_disabled_devices(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->disabled_devices; +} + +GArray * +fu_config_get_trusted_uids(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->trusted_uids; +} + +GPtrArray * +fu_config_get_blocked_firmware(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->blocked_firmware; +} + +guint +fu_config_get_uri_scheme_prio(FuConfig *self, const gchar *scheme) +{ +#if GLIB_CHECK_VERSION(2, 54, 0) + guint idx = G_MAXUINT; + g_ptr_array_find_with_equal_func(self->uri_schemes, scheme, g_str_equal, &idx); + return idx; +#else + for (guint i = 0; i < self->uri_schemes->len; i++) + const gchar *scheme_tmp = g_ptr_array_index(self->uri_schemes, i); + if (g_str_equal(scheme_tmp, scheme)) + return i; +} +return G_MAXUINT; +#endif +} + +guint64 +fu_config_get_archive_size_max(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), 0); + return self->archive_size_max; +} + +GPtrArray * +fu_config_get_disabled_plugins(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->disabled_plugins; +} + +GPtrArray * +fu_config_get_approved_firmware(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->approved_firmware; +} + +gboolean +fu_config_get_update_motd(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), FALSE); + return self->update_motd; +} + +gboolean +fu_config_get_ignore_power(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), FALSE); + return self->ignore_power; +} + +gboolean +fu_config_get_only_trusted(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), FALSE); + return self->only_trusted; +} + +gboolean +fu_config_get_show_device_private(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), FALSE); + return self->show_device_private; +} + +gboolean +fu_config_get_enumerate_all_devices(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), FALSE); + return self->enumerate_all_devices; +} + +const gchar * +fu_config_get_host_bkc(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->host_bkc; +} + +const gchar * +fu_config_get_esp_location(FuConfig *self) +{ + g_return_val_if_fail(FU_IS_CONFIG(self), NULL); + return self->esp_location; +} + +static void +fu_config_class_init(FuConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_config_finalize; + + /** + * FuConfig::changed: + * @self: the #FuConfig instance that emitted the signal + * + * The ::changed signal is emitted when the config file has + * changed, for instance when a parameter has been modified. + **/ + signals[SIGNAL_CHANGED] = g_signal_new("changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static void +fu_config_init(FuConfig *self) +{ + self->filenames = g_ptr_array_new_with_free_func(g_free); + self->disabled_devices = g_ptr_array_new_with_free_func(g_free); + self->disabled_plugins = g_ptr_array_new_with_free_func(g_free); + self->approved_firmware = g_ptr_array_new_with_free_func(g_free); + self->blocked_firmware = g_ptr_array_new_with_free_func(g_free); + self->trusted_uids = g_array_new(FALSE, FALSE, sizeof(guint64)); + self->uri_schemes = g_ptr_array_new_with_free_func(g_free); + self->monitors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_config_finalize(GObject *obj) +{ + FuConfig *self = FU_CONFIG(obj); + + for (guint i = 0; i < self->monitors->len; i++) { + GFileMonitor *monitor = g_ptr_array_index(self->monitors, i); + g_file_monitor_cancel(monitor); + } + g_ptr_array_unref(self->filenames); + g_ptr_array_unref(self->monitors); + g_ptr_array_unref(self->disabled_devices); + g_ptr_array_unref(self->disabled_plugins); + g_ptr_array_unref(self->approved_firmware); + g_ptr_array_unref(self->blocked_firmware); + g_ptr_array_unref(self->uri_schemes); + g_array_unref(self->trusted_uids); + g_free(self->host_bkc); + g_free(self->esp_location); + + G_OBJECT_CLASS(fu_config_parent_class)->finalize(obj); +} + +FuConfig * +fu_config_new(void) +{ + FuConfig *self; + self = g_object_new(FU_TYPE_CONFIG, NULL); + return FU_CONFIG(self); +} diff --git a/fwupd-1.8.6/src/fu-config.h b/fwupd-1.8.6/src/fu-config.h new file mode 100644 index 0000000000000000000000000000000000000000..1d5fcad2be55bf6058402128bf49c063d2db735b --- /dev/null +++ b/fwupd-1.8.6/src/fu-config.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fwupd-remote.h" + +#define FU_TYPE_CONFIG (fu_config_get_type()) +G_DECLARE_FINAL_TYPE(FuConfig, fu_config, FU, CONFIG, GObject) + +FuConfig * +fu_config_new(void); +gboolean +fu_config_load(FuConfig *self, GError **error); +gboolean +fu_config_set_key_value(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_disabled_devices(FuConfig *self); +GPtrArray * +fu_config_get_disabled_plugins(FuConfig *self); +GArray * +fu_config_get_trusted_uids(FuConfig *self); +GPtrArray * +fu_config_get_approved_firmware(FuConfig *self); +GPtrArray * +fu_config_get_blocked_firmware(FuConfig *self); +guint +fu_config_get_uri_scheme_prio(FuConfig *self, const gchar *protocol); +gboolean +fu_config_get_update_motd(FuConfig *self); +gboolean +fu_config_get_enumerate_all_devices(FuConfig *self); +gboolean +fu_config_get_ignore_power(FuConfig *self); +gboolean +fu_config_get_only_trusted(FuConfig *self); +gboolean +fu_config_get_show_device_private(FuConfig *self); +const gchar * +fu_config_get_host_bkc(FuConfig *self); +const gchar * +fu_config_get_esp_location(FuConfig *self); diff --git a/fwupd-1.8.6/src/fu-daemon.c b/fwupd-1.8.6/src/fu-daemon.c new file mode 100644 index 0000000000000000000000000000000000000000..d8379c59256da9b26ab5fbc0dc0376e61c9aff65 --- /dev/null +++ b/fwupd-1.8.6/src/fu-daemon.c @@ -0,0 +1,2473 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include +#ifdef HAVE_GIO_UNIX +#include +#endif +#include +#ifdef HAVE_POLKIT +#include +#endif +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-device-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-plugin-private.h" +#include "fwupd-release-private.h" +#include "fwupd-remote-private.h" +#include "fwupd-request-private.h" +#include "fwupd-security-attr-private.h" + +#include "fu-bios-settings-private.h" +#include "fu-daemon.h" +#include "fu-device-private.h" +#include "fu-engine.h" +#include "fu-release.h" +#include "fu-security-attrs-private.h" + +#ifdef HAVE_POLKIT +#ifndef HAVE_POLKIT_0_114 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref) +#pragma clang diagnostic pop +#endif /* HAVE_POLKIT_0_114 */ +#endif /* HAVE_POLKIT */ + +static void +fu_daemon_finalize(GObject *obj); + +struct _FuDaemon { + GObject parent_instance; + GDBusConnection *connection; + GDBusNodeInfo *introspection_daemon; + GDBusProxy *proxy_uid; + GMainLoop *loop; + GHashTable *sender_items; /* sender:FuDaemonSenderItem */ +#ifdef HAVE_POLKIT + PolkitAuthority *authority; +#endif + FwupdStatus status; /* last emitted */ + guint percentage; /* last emitted */ + guint owner_id; + guint process_quit_id; + FuEngine *engine; + gboolean update_in_progress; + gboolean pending_stop; + FuDaemonMachineKind machine_kind; +}; + +G_DEFINE_TYPE(FuDaemon, fu_daemon, G_TYPE_OBJECT) + +void +fu_daemon_start(FuDaemon *self) +{ + g_return_if_fail(FU_IS_DAEMON(self)); + g_main_loop_run(self->loop); +} + +void +fu_daemon_stop(FuDaemon *self) +{ + g_return_if_fail(FU_IS_DAEMON(self)); + if (self->update_in_progress) { + self->pending_stop = TRUE; + return; + } + g_main_loop_quit(self->loop); +} + +typedef struct { + FwupdFeatureFlags feature_flags; + GHashTable *hints; /* str:str */ +} FuDaemonSenderItem; + +static FuDaemonMachineKind +fu_daemon_machine_kind_from_string(const gchar *kind) +{ + if (g_strcmp0(kind, "physical") == 0) + return FU_DAEMON_MACHINE_KIND_PHYSICAL; + if (g_strcmp0(kind, "virtual") == 0) + return FU_DAEMON_MACHINE_KIND_VIRTUAL; + if (g_strcmp0(kind, "container") == 0) + return FU_DAEMON_MACHINE_KIND_CONTAINER; + return FU_DAEMON_MACHINE_KIND_UNKNOWN; +} + +static void +fu_daemon_engine_changed_cb(FuEngine *engine, FuDaemon *self) +{ + /* not yet connected */ + if (self->connection == NULL) + return; + g_dbus_connection_emit_signal(self->connection, + NULL, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "Changed", + NULL, + NULL); +} + +static void +fu_daemon_engine_device_added_cb(FuEngine *engine, FuDevice *device, FuDaemon *self) +{ + GVariant *val; + + /* not yet connected */ + if (self->connection == NULL) + return; + val = fwupd_device_to_variant(FWUPD_DEVICE(device)); + g_dbus_connection_emit_signal(self->connection, + NULL, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "DeviceAdded", + g_variant_new_tuple(&val, 1), + NULL); +} + +static void +fu_daemon_engine_device_removed_cb(FuEngine *engine, FuDevice *device, FuDaemon *self) +{ + GVariant *val; + + /* not yet connected */ + if (self->connection == NULL) + return; + val = fwupd_device_to_variant(FWUPD_DEVICE(device)); + g_dbus_connection_emit_signal(self->connection, + NULL, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "DeviceRemoved", + g_variant_new_tuple(&val, 1), + NULL); +} + +static void +fu_daemon_engine_device_changed_cb(FuEngine *engine, FuDevice *device, FuDaemon *self) +{ + GVariant *val; + + /* not yet connected */ + if (self->connection == NULL) + return; + val = fwupd_device_to_variant(FWUPD_DEVICE(device)); + g_dbus_connection_emit_signal(self->connection, + NULL, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "DeviceChanged", + g_variant_new_tuple(&val, 1), + NULL); +} + +static void +fu_daemon_engine_device_request_cb(FuEngine *engine, FwupdRequest *request, FuDaemon *self) +{ + GVariant *val; + + /* not yet connected */ + if (self->connection == NULL) + return; + val = fwupd_request_to_variant(FWUPD_REQUEST(request)); + g_dbus_connection_emit_signal(self->connection, + NULL, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "DeviceRequest", + g_variant_new_tuple(&val, 1), + NULL); +} + +static void +fu_daemon_emit_property_changed(FuDaemon *self, + const gchar *property_name, + GVariant *property_value) +{ + GVariantBuilder builder; + GVariantBuilder invalidated_builder; + + /* not yet connected */ + if (self->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_VARDICT); + g_variant_builder_add(&builder, "{sv}", property_name, property_value); + g_dbus_connection_emit_signal( + self->connection, + NULL, + FWUPD_DBUS_PATH, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new("(sa{sv}as)", FWUPD_DBUS_INTERFACE, &builder, &invalidated_builder), + NULL); + g_variant_builder_clear(&builder); + g_variant_builder_clear(&invalidated_builder); +} + +static void +fu_daemon_set_status(FuDaemon *self, FwupdStatus status) +{ + /* sanity check */ + if (self->status == status) + return; + self->status = status; + + g_debug("Emitting PropertyChanged('Status'='%s')", fwupd_status_to_string(status)); + fu_daemon_emit_property_changed(self, "Status", g_variant_new_uint32(status)); +} + +static void +fu_daemon_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuDaemon *self) +{ + fu_daemon_set_status(self, status); + + /* engine has gone idle */ + if (status == FWUPD_STATUS_SHUTDOWN) + g_main_loop_quit(self->loop); +} + +static FuEngineRequest * +fu_daemon_create_request(FuDaemon *self, const gchar *sender, GError **error) +{ + FuDaemonSenderItem *sender_item; + FwupdDeviceFlags device_flags = FWUPD_DEVICE_FLAG_NONE; + guint64 calling_uid = 0; + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(GVariant) value = NULL; + + /* if using FWUPD_DBUS_SOCKET... */ + if (sender == NULL) { + fu_engine_request_set_device_flags(request, FWUPD_DEVICE_FLAG_TRUSTED); + return g_steal_pointer(&request); + } + + /* did the client set the list of supported features or any hints */ + sender_item = g_hash_table_lookup(self->sender_items, sender); + if (sender_item != NULL) { + const gchar *locale = g_hash_table_lookup(sender_item->hints, "locale"); + if (locale != NULL) + fu_engine_request_set_locale(request, locale); + fu_engine_request_set_feature_flags(request, sender_item->feature_flags); + } + + /* are we root and therefore trusted? */ + value = g_dbus_proxy_call_sync(self->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 NULL; + } + g_variant_get(value, "(u)", &calling_uid); + if (fu_engine_is_uid_trusted(self->engine, calling_uid)) + device_flags |= FWUPD_DEVICE_FLAG_TRUSTED; + fu_engine_request_set_device_flags(request, device_flags); + + /* success */ + return g_steal_pointer(&request); +} + +static GVariant * +fu_daemon_device_array_to_variant(FuDaemon *self, + FuEngineRequest *request, + GPtrArray *devices, + GError **error) +{ + GVariantBuilder builder; + FwupdDeviceFlags flags = fu_engine_request_get_device_flags(request); + + g_return_val_if_fail(devices->len > 0, NULL); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + /* override when required */ + if (fu_config_get_show_device_private(fu_engine_get_config(self->engine))) + flags |= FWUPD_DEVICE_FLAG_TRUSTED; + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + 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); +} + +static GVariant * +fu_daemon_plugin_array_to_variant(GPtrArray *plugins) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + for (guint i = 0; i < plugins->len; i++) { + FuDevice *plugin = g_ptr_array_index(plugins, i); + GVariant *tmp = fwupd_plugin_to_variant(FWUPD_PLUGIN(plugin)); + g_variant_builder_add_value(&builder, tmp); + } + return g_variant_new("(aa{sv})", &builder); +} + +static GVariant * +fu_daemon_release_array_to_variant(GPtrArray *results) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + for (guint i = 0; i < results->len; i++) { + FwupdRelease *rel = g_ptr_array_index(results, i); + GVariant *tmp = fwupd_release_to_variant(rel); + g_variant_builder_add_value(&builder, tmp); + } + return g_variant_new("(aa{sv})", &builder); +} + +static GVariant * +fu_daemon_remote_array_to_variant(GPtrArray *remotes) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + GVariant *tmp = fwupd_remote_to_variant(remote); + g_variant_builder_add_value(&builder, tmp); + } + return g_variant_new("(aa{sv})", &builder); +} + +#ifdef HAVE_GIO_UNIX +static GVariant * +fu_daemon_result_array_to_variant(GPtrArray *results) +{ + GVariantBuilder builder; + g_return_val_if_fail(results->len > 0, NULL); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (guint i = 0; i < results->len; i++) { + FwupdDevice *result = g_ptr_array_index(results, i); + GVariant *tmp = fwupd_device_to_variant(result); + g_variant_builder_add_value(&builder, tmp); + } + return g_variant_new("(aa{sv})", &builder); +} +#endif /* HAVE_GIO_UNIX */ + +typedef struct { + GDBusMethodInvocation *invocation; + FuEngineRequest *request; +#ifdef HAVE_POLKIT + PolkitSubject *subject; +#endif + GPtrArray *releases; + GPtrArray *action_ids; + GPtrArray *checksums; + GPtrArray *errors; + guint64 flags; + GBytes *blob_cab; + FuDaemon *self; + gchar *device_id; + gchar *remote_id; + gchar *key; + gchar *value; + XbSilo *silo; + GHashTable *bios_settings; /* str:str */ +} FuMainAuthHelper; + +static void +fu_daemon_auth_helper_free(FuMainAuthHelper *helper) +{ + /* always return to IDLE even in event of an auth error */ + fu_daemon_set_status(helper->self, FWUPD_STATUS_IDLE); + + if (helper->blob_cab != NULL) + g_bytes_unref(helper->blob_cab); +#ifdef HAVE_POLKIT + if (helper->subject != NULL) + g_object_unref(helper->subject); +#endif + if (helper->silo != NULL) + g_object_unref(helper->silo); + if (helper->request != NULL) + g_object_unref(helper->request); + if (helper->releases != NULL) + g_ptr_array_unref(helper->releases); + if (helper->action_ids != NULL) + g_ptr_array_unref(helper->action_ids); + if (helper->checksums != NULL) + g_ptr_array_unref(helper->checksums); + if (helper->errors != NULL) + g_ptr_array_unref(helper->errors); + g_free(helper->device_id); + g_free(helper->remote_id); + g_free(helper->key); + g_free(helper->value); + g_object_unref(helper->invocation); + if (helper->bios_settings != NULL) + g_hash_table_unref(helper->bios_settings); + g_free(helper); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainAuthHelper, fu_daemon_auth_helper_free) +#pragma clang diagnostic pop + +#ifdef HAVE_POLKIT +/* error may or may not already have been set */ +static gboolean +fu_daemon_authorization_is_valid(PolkitAuthorizationResult *auth, GError **error) +{ + /* failed */ + if (auth == NULL) { + g_autofree gchar *message = g_strdup((*error)->message); + g_clear_error(error); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "Could not check for auth: %s", + message); + return FALSE; + } + + /* did not auth */ + if (!polkit_authorization_result_get_is_authorized(auth)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "Failed to obtain auth"); + return FALSE; + } + + /* success */ + return TRUE; +} +#else +static gboolean +fu_daemon_authorization_is_trusted(FuEngineRequest *request, GError **error) +{ + FwupdDeviceFlags flags = fu_engine_request_get_device_flags(request); + if ((flags & FWUPD_DEVICE_FLAG_TRUSTED) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "permission denied: untrusted client process"); + return FALSE; + } + return TRUE; +} +#endif /* HAVE_POLKIT */ + +static void +fu_daemon_authorize_unlock_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* authenticated */ + if (!fu_engine_unlock(helper->self->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_daemon_authorize_get_bios_settings_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; + g_autoptr(FuBiosSettings) attrs = NULL; + FuContext *ctx; + GVariant *val = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* authenticated */ + ctx = fu_engine_get_context(helper->self->engine); + attrs = fu_context_get_bios_settings(ctx); + val = fu_bios_settings_to_variant(attrs, TRUE); + g_dbus_method_invocation_return_value(helper->invocation, val); +} + +static void +fu_daemon_authorize_set_bios_settings_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* authenticated */ + if (!fu_engine_modify_bios_settings(helper->self->engine, + helper->bios_settings, + FALSE, + &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } + /* success */ + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} + +static void +fu_daemon_authorize_set_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* 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->self->engine, csum); + } + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} + +static void +fu_daemon_authorize_set_blocked_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* success */ + if (!fu_engine_set_blocked_firmware(helper->self->engine, helper->checksums, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} + +static void +fu_daemon_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; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* authenticated */ + sig = fu_engine_self_sign(helper->self->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_daemon_modify_config_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + if (!fu_engine_modify_config(helper->self->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_daemon_progress_percentage_changed_cb(FuProgress *progress, guint percentage, FuDaemon *self) +{ + /* sanity check */ + if (self->percentage == percentage) + return; + self->percentage = percentage; + + g_debug("Emitting PropertyChanged('Percentage'='%u%%')", percentage); + fu_daemon_emit_property_changed(self, "Percentage", g_variant_new_uint32(percentage)); +} + +static void +fu_daemon_progress_status_changed_cb(FuProgress *progress, FwupdStatus status, FuDaemon *self) +{ + fu_daemon_set_status(self, status); +} + +static void +fu_daemon_authorize_activate_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* progress */ + fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_daemon_progress_percentage_changed_cb), + helper->self); + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_daemon_progress_status_changed_cb), + helper->self); + + /* authenticated */ + if (!fu_engine_activate(helper->self->engine, helper->device_id, progress, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} + +static void +fu_daemon_authorize_verify_update_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* progress */ + fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_daemon_progress_percentage_changed_cb), + helper->self); + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_daemon_progress_status_changed_cb), + helper->self); + + /* authenticated */ + if (!fu_engine_verify_update(helper->self->engine, helper->device_id, progress, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} + +static void +fu_daemon_authorize_modify_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitAuthorizationResult) auth = NULL; + + /* get result */ + auth = polkit_authority_check_authorization_finish(POLKIT_AUTHORITY(source), res, &error); + if (!fu_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#else + if (!fu_daemon_authorization_is_trusted(helper->request, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } +#endif /* HAVE_POLKIT */ + + /* authenticated */ + if (!fu_engine_modify_remote(helper->self->engine, + helper->remote_id, + 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); +} + +#ifdef HAVE_GIO_UNIX +static void +fu_daemon_authorize_install_queue(FuMainAuthHelper *helper); + +#ifdef HAVE_POLKIT +static void +fu_daemon_authorize_install_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_daemon_authorization_is_valid(auth, &error)) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } + + /* do the next authentication action ID */ + fu_daemon_authorize_install_queue(g_steal_pointer(&helper)); +} +#endif /* HAVE_POLKIT */ + +static void +fu_daemon_authorize_install_queue(FuMainAuthHelper *helper_ref) +{ + FuDaemon *self = helper_ref->self; + g_autoptr(FuMainAuthHelper) helper = helper_ref; + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + gboolean ret; + +#ifdef HAVE_POLKIT + /* still more things to to authenticate */ + if (helper->action_ids->len > 0) { + g_autofree gchar *action_id = g_strdup(g_ptr_array_index(helper->action_ids, 0)); + g_autoptr(PolkitSubject) subject = g_object_ref(helper->subject); + g_ptr_array_remove_index(helper->action_ids, 0); + polkit_authority_check_authorization( + self->authority, + subject, + action_id, + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_install_cb, + g_steal_pointer(&helper)); + return; + } +#endif /* HAVE_POLKIT */ + + /* all authenticated, so install all the things */ + fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_daemon_progress_percentage_changed_cb), + helper->self); + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_daemon_progress_status_changed_cb), + helper->self); + + /* all authenticated, so install all the things */ + self->update_in_progress = TRUE; + ret = fu_engine_install_releases(helper->self->engine, + helper->request, + helper->releases, + helper->blob_cab, + progress, + helper->flags, + &error); + self->update_in_progress = FALSE; + if (self->pending_stop) + g_main_loop_quit(self->loop); + if (!ret) { + g_dbus_method_invocation_return_gerror(helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} +#endif /* HAVE_GIO_UNIX */ + +#if !GLIB_CHECK_VERSION(2, 54, 0) +static gboolean +g_ptr_array_find(GPtrArray *haystack, gconstpointer needle, guint *index_) +{ + for (guint i = 0; i < haystack->len; i++) { + gconstpointer *tmp = g_ptr_array_index(haystack, i); + if (tmp == needle) { + if (index_ != NULL) { + *index_ = i; + return TRUE; + } + } + } + return FALSE; +} +#endif + +#ifdef HAVE_GIO_UNIX +static gint +fu_daemon_release_sort_cb(gconstpointer a, gconstpointer b) +{ + FuRelease *release1 = *((FuRelease **)a); + FuRelease *release2 = *((FuRelease **)b); + return fu_release_compare(release1, release2); +} + +static gboolean +fu_daemon_install_with_helper_device(FuMainAuthHelper *helper, + XbNode *component, + FuDevice *device, + GError **error) +{ + FuDaemon *self = helper->self; + const gchar *action_id; + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) releases = NULL; + + /* is this component valid for the device */ + fu_release_set_device(release, device); + fu_release_set_request(release, helper->request); + if (!fu_release_load(release, + component, + NULL, + helper->flags | FWUPD_INSTALL_FLAG_FORCE, + &error_local)) { + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + return TRUE; + } + if (!fu_engine_check_requirements(self->engine, + release, + helper->flags | FWUPD_INSTALL_FLAG_FORCE, + &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("first pass requirement on %s:%s failed: %s", + fu_device_get_id(device), + xb_node_query_text(component, "id", NULL), + error_local->message); + } + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + return TRUE; + } + + /* possibly update version format */ + fu_engine_md_refresh_device_from_component(self->engine, device, component); + + /* sync update message from CAB */ + fu_device_incorporate_from_component(device, component); + + /* install each intermediate release */ + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) { + g_autoptr(GPtrArray) rels = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + /* we get this one "for free" */ + g_ptr_array_add(releases, g_object_ref(release)); + +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + error); + if (query == NULL) + return FALSE; + rels = xb_node_query_full(component, query, NULL); +#else + rels = xb_node_query(component, "releases/release", 0, NULL); +#endif + /* add all but the the first entry */ + for (guint i = 1; i < rels->len; i++) { + XbNode *rel = g_ptr_array_index(rels, i); + g_autoptr(FuRelease) release2 = fu_release_new(); + g_autoptr(GError) error_loop = NULL; + fu_release_set_device(release2, device); + fu_release_set_request(release2, helper->request); + if (!fu_release_load(release2, + component, + rel, + helper->flags, + &error_loop)) { + g_ptr_array_add(helper->errors, g_steal_pointer(&error_loop)); + continue; + } + g_ptr_array_add(releases, g_object_ref(release2)); + } + } else { + g_ptr_array_add(releases, g_object_ref(release)); + } + + /* make a second pass */ + for (guint i = 0; i < releases->len; i++) { + FuRelease *release_tmp = g_ptr_array_index(releases, i); + if (!fu_engine_check_requirements(self->engine, + release_tmp, + helper->flags, + &error_local)) { + g_debug("second pass requirement on %s:%s failed: %s", + fu_device_get_id(device), + xb_node_query_text(component, "id", NULL), + error_local->message); + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + continue; + } + if (!fu_engine_check_trust(self->engine, release_tmp, &error_local)) { + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + continue; + } + + /* get the action IDs for the valid device */ + action_id = fu_release_get_action_id(release_tmp); + if (!g_ptr_array_find(helper->action_ids, action_id, NULL)) + g_ptr_array_add(helper->action_ids, g_strdup(action_id)); + g_ptr_array_add(helper->releases, g_object_ref(release_tmp)); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_daemon_install_with_helper(FuMainAuthHelper *helper_ref, GError **error) +{ + FuDaemon *self = helper_ref->self; + g_autoptr(FuMainAuthHelper) helper = helper_ref; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(GPtrArray) devices_possible = NULL; + + /* 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(self->engine, error); + if (devices_possible == NULL) + return FALSE; + } else { + g_autoptr(FuDevice) device = NULL; + device = fu_engine_get_device(self->engine, helper->device_id, error); + if (device == NULL) + return FALSE; + devices_possible = + fu_engine_get_devices_by_composite_id(self->engine, + fu_device_get_composite_id(device), + error); + if (devices_possible == NULL) + return FALSE; + } + + /* parse silo */ + helper->silo = fu_engine_get_silo_from_blob(self->engine, helper->blob_cab, error); + if (helper->silo == NULL) + return FALSE; + + /* for each component in the silo */ + components = + xb_silo_query(helper->silo, "components/component[@type='firmware']", 0, error); + if (components == NULL) + return FALSE; + helper->action_ids = g_ptr_array_new_with_free_func(g_free); + helper->releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + helper->errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + + /* do any devices pass the requirements */ + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index(components, i); + for (guint j = 0; j < devices_possible->len; j++) { + FuDevice *device = g_ptr_array_index(devices_possible, j); + if (!fu_daemon_install_with_helper_device(helper, component, device, error)) + return FALSE; + } + } + + /* order the install tasks by the device priority */ + g_ptr_array_sort(helper->releases, fu_daemon_release_sort_cb); + + /* nothing suitable */ + if (helper->releases->len == 0) { + GError *error_tmp = fu_engine_error_array_get_best(helper->errors); + g_propagate_error(error, error_tmp); + return FALSE; + } + + /* authenticate all things in the action_ids */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + fu_daemon_authorize_install_queue(g_steal_pointer(&helper)); + return TRUE; +} +#endif /* HAVE_GIO_UNIX */ + +static FuDaemonSenderItem * +fu_daemon_ensure_sender_item(FuDaemon *self, const gchar *sender) +{ + FuDaemonSenderItem *sender_item = NULL; + + /* operating in point-to-point mode */ + if (sender == NULL) + sender = ""; + sender_item = g_hash_table_lookup(self->sender_items, sender); + if (sender_item == NULL) { + sender_item = g_new0(FuDaemonSenderItem, 1); + sender_item->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(self->sender_items, g_strdup(sender), sender_item); + } + return sender_item; +} + +static gboolean +fu_daemon_device_id_valid(const gchar *device_id, GError **error) +{ + if (g_strcmp0(device_id, FWUPD_DEVICE_ID_ANY) == 0) + return TRUE; + if (device_id != NULL && strlen(device_id) >= 4) + return TRUE; + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid device ID: %s", device_id); + return FALSE; +} + +static gboolean +fu_daemon_schedule_process_quit_cb(gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + + g_debug("daemon asked to quit, shutting down"); + self->process_quit_id = 0; + g_main_loop_quit(self->loop); + return G_SOURCE_REMOVE; +} + +static void +fu_daemon_schedule_process_quit(FuDaemon *self) +{ + /* busy? */ + if (self->update_in_progress) { + g_warning("asked to quit during a firmware update, ignoring"); + return; + } + + /* allow the daemon to respond to the request, then quit */ + if (self->process_quit_id != 0) + g_source_remove(self->process_quit_id); + self->process_quit_id = g_idle_add(fu_daemon_schedule_process_quit_cb, self); +} + +static void +fu_daemon_daemon_method_call(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + GVariant *val = NULL; + g_autoptr(FuEngineRequest) request = NULL; + g_autoptr(GError) error = NULL; + + /* build request */ + request = fu_daemon_create_request(self, sender, &error); + if (request == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* activity */ + fu_engine_idle_reset(self->engine); + + if (g_strcmp0(method_name, "GetDevices") == 0) { + g_autoptr(GPtrArray) devices = NULL; + g_debug("Called %s()", method_name); + devices = fu_engine_get_devices(self->engine, &error); + if (devices == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_device_array_to_variant(self, request, devices, &error); + if (val == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value(invocation, val); + return; + } + if (g_strcmp0(method_name, "GetPlugins") == 0) { + g_debug("Called %s()", method_name); + val = fu_daemon_plugin_array_to_variant(fu_engine_get_plugins(self->engine)); + g_dbus_method_invocation_return_value(invocation, val); + return; + } + if (g_strcmp0(method_name, "GetReleases") == 0) { + const gchar *device_id; + g_autoptr(GPtrArray) releases = NULL; + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + releases = fu_engine_get_releases(self->engine, request, device_id, &error); + if (releases == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_release_array_to_variant(releases); + 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(self->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, "GetBlockedFirmware") == 0) { + GVariantBuilder builder; + GPtrArray *checksums = fu_engine_get_blocked_firmware(self->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, "GetReportMetadata") == 0) { + GHashTableIter iter; + GVariantBuilder builder; + const gchar *key; + const gchar *value; + g_autoptr(GHashTable) metadata = NULL; + + metadata = fu_engine_get_report_metadata(self->engine, &error); + if (metadata == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_hash_table_iter_init(&iter, metadata); + while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) { + g_variant_builder_add_value(&builder, g_variant_new("{ss}", key, value)); + } + 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; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif /* HAVE_POLKIT */ + + g_variant_get(parameters, "(^as)", &checksums); + checksums_str = g_strjoinv(",", checksums); + g_debug("Called %s(%s)", method_name, checksums_str); + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->flags = FWUPD_INSTALL_FLAG_NO_SEARCH; + helper->request = g_steal_pointer(&request); + 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])); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.set-approved-firmware", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_set_approved_firmware_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_set_approved_firmware_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "SetBlockedFirmware") == 0) { + g_autofree gchar *checksums_str = NULL; + g_auto(GStrv) checksums = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + g_variant_get(parameters, "(^as)", &checksums); + checksums_str = g_strjoinv(",", checksums); + g_debug("Called %s(%s)", method_name, checksums_str); + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->request = g_steal_pointer(&request); + 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])); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.set-approved-firmware", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_set_blocked_firmware_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_set_blocked_firmware_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "Quit") == 0) { + if (!fu_engine_request_has_device_flag(request, FWUPD_DEVICE_FLAG_TRUSTED)) { + g_dbus_method_invocation_return_error_literal(invocation, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "Permission denied"); + return; + } + fu_daemon_schedule_process_quit(self); + g_dbus_method_invocation_return_value(invocation, NULL); + return; + } + if (g_strcmp0(method_name, "SelfSign") == 0) { + GVariant *prop_value; + const gchar *prop_key; + g_autofree gchar *value = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + 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 |= JCAT_SIGN_FLAG_ADD_TIMESTAMP; + if (g_strcmp0(prop_key, "add-cert") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= JCAT_SIGN_FLAG_ADD_CERT; + g_variant_unref(prop_value); + } + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper->self = self; + helper->value = g_steal_pointer(&value); + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.self-sign", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_self_sign_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_self_sign_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "GetDowngrades") == 0) { + const gchar *device_id; + g_autoptr(GPtrArray) releases = NULL; + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + releases = fu_engine_get_downgrades(self->engine, request, device_id, &error); + if (releases == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_release_array_to_variant(releases); + g_dbus_method_invocation_return_value(invocation, val); + return; + } + if (g_strcmp0(method_name, "GetUpgrades") == 0) { + const gchar *device_id; + g_autoptr(GPtrArray) releases = NULL; + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + releases = fu_engine_get_upgrades(self->engine, request, device_id, &error); + if (releases == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_release_array_to_variant(releases); + g_dbus_method_invocation_return_value(invocation, val); + return; + } + if (g_strcmp0(method_name, "GetRemotes") == 0) { + g_autoptr(GPtrArray) remotes = NULL; + g_debug("Called %s()", method_name); + remotes = fu_engine_get_remotes(self->engine, &error); + if (remotes == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_remote_array_to_variant(remotes); + g_dbus_method_invocation_return_value(invocation, val); + return; + } + if (g_strcmp0(method_name, "GetHistory") == 0) { + g_autoptr(GPtrArray) devices = NULL; + g_debug("Called %s()", method_name); + devices = fu_engine_get_history(self->engine, &error); + if (devices == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_device_array_to_variant(self, request, devices, &error); + if (val == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value(invocation, val); + return; + } + if (g_strcmp0(method_name, "GetHostSecurityAttrs") == 0) { + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_debug("Called %s()", method_name); +#ifndef HAVE_HSI + g_dbus_method_invocation_return_error_literal(invocation, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HSI support not enabled"); +#else + if (self->machine_kind != FU_DAEMON_MACHINE_KIND_PHYSICAL) { + g_dbus_method_invocation_return_error_literal( + invocation, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HSI unavailable for hypervisor"); + return; + } + attrs = fu_engine_get_host_security_attrs(self->engine); + val = fu_security_attrs_to_variant(attrs); + g_dbus_method_invocation_return_value(invocation, val); +#endif + return; + } + if (g_strcmp0(method_name, "GetHostSecurityEvents") == 0) { + guint limit = 0; + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_variant_get(parameters, "(u)", &limit); + g_debug("Called %s(%u)", method_name, limit); +#ifndef HAVE_HSI + g_dbus_method_invocation_return_error_literal(invocation, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HSI support not enabled"); +#else + attrs = fu_engine_get_host_security_events(self->engine, limit, &error); + if (attrs == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_security_attrs_to_variant(attrs); + g_dbus_method_invocation_return_value(invocation, val); +#endif + return; + } + if (g_strcmp0(method_name, "ClearResults") == 0) { + const gchar *device_id; + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_engine_clear_results(self->engine, device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value(invocation, NULL); + return; + } + if (g_strcmp0(method_name, "ModifyDevice") == 0) { + const gchar *device_id; + const gchar *key = NULL; + const gchar *value = NULL; + + /* check the id exists */ + g_variant_get(parameters, "(&s&s&s)", &device_id, &key, &value); + g_debug("Called %s(%s,%s=%s)", method_name, device_id, key, value); + if (!fu_engine_modify_device(self->engine, device_id, key, value, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value(invocation, NULL); + return; + } + if (g_strcmp0(method_name, "GetResults") == 0) { + const gchar *device_id = NULL; + g_autoptr(FwupdDevice) result = NULL; + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + result = fu_engine_get_results(self->engine, device_id, &error); + if (result == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fwupd_device_to_variant(result); + g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&val, 1)); + return; + } + if (g_strcmp0(method_name, "UpdateMetadata") == 0) { +#ifdef HAVE_GIO_UNIX + GDBusMessage *message; + GUnixFDList *fd_list; + const gchar *remote_id = NULL; + gint fd_data; + gint fd_sig; + + g_variant_get(parameters, "(&shh)", &remote_id, &fd_data, &fd_sig); + g_debug("Called %s(%s,%i,%i)", method_name, remote_id, fd_data, fd_sig); + + /* update the metadata store */ + message = g_dbus_method_invocation_get_message(invocation); + fd_list = g_dbus_message_get_unix_fd_list(message); + if (fd_list == NULL || g_unix_fd_list_get_length(fd_list) != 2) { + g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + fd_data = g_unix_fd_list_get(fd_list, 0, &error); + if (fd_data < 0) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + fd_sig = g_unix_fd_list_get(fd_list, 1, &error); + if (fd_sig < 0) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* store new metadata (will close the fds when done) */ + if (!fu_engine_update_metadata(self->engine, remote_id, fd_data, fd_sig, &error)) { + g_prefix_error(&error, "Failed to update metadata for %s: ", remote_id); + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value(invocation, NULL); +#else + g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); + g_dbus_method_invocation_return_gerror(invocation, error); +#endif /* HAVE_GIO_UNIX */ + return; + } + if (g_strcmp0(method_name, "Unlock") == 0) { + const gchar *device_id = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif /* HAVE_POLKIT */ + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); + helper->device_id = g_strdup(device_id); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.device-unlock", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_unlock_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_unlock_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "Activate") == 0) { + const gchar *device_id = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); + helper->device_id = g_strdup(device_id); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.device-activate", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_activate_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_activate_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "ModifyConfig") == 0) { + g_autofree gchar *key = NULL; + g_autofree gchar *value = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + g_variant_get(parameters, "(ss)", &key, &value); + g_debug("Called %s(%s=%s)", method_name, key, value); + + /* authenticate */ + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->key = g_steal_pointer(&key); + helper->value = g_steal_pointer(&value); + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.modify-config", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_modify_config_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_modify_config_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "ModifyRemote") == 0) { + const gchar *remote_id = NULL; + const gchar *key = NULL; + const gchar *value = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + /* check the id exists */ + g_variant_get(parameters, "(&s&s&s)", &remote_id, &key, &value); + g_debug("Called %s(%s,%s=%s)", method_name, remote_id, key, value); + + /* create helper object */ + helper = g_new0(FuMainAuthHelper, 1); + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); + helper->remote_id = g_strdup(remote_id); + helper->key = g_strdup(key); + helper->value = g_strdup(value); + helper->self = self; + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.modify-remote", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_modify_remote_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_modify_remote_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "VerifyUpdate") == 0) { + const gchar *device_id = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + + /* check the id exists */ + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* create helper object */ + helper = g_new0(FuMainAuthHelper, 1); + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); + helper->device_id = g_strdup(device_id); + helper->self = self; + + /* authenticate */ +#ifdef HAVE_POLKIT + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.verify-update", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_verify_update_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_verify_update_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + return; + } + if (g_strcmp0(method_name, "Verify") == 0) { + const gchar *device_id = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + g_variant_get(parameters, "(&s)", &device_id); + g_debug("Called %s(%s)", method_name, device_id); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* progress */ + fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL); + g_signal_connect(FU_PROGRESS(progress), + "percentage-changed", + G_CALLBACK(fu_daemon_progress_percentage_changed_cb), + self); + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(fu_daemon_progress_status_changed_cb), + self); + + if (!fu_engine_verify(self->engine, device_id, progress, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value(invocation, NULL); + return; + } + if (g_strcmp0(method_name, "SetFeatureFlags") == 0) { + FuDaemonSenderItem *sender_item; + guint64 feature_flags_u64 = 0; + + g_variant_get(parameters, "(t)", &feature_flags_u64); + g_debug("Called %s(%" G_GUINT64_FORMAT ")", method_name, feature_flags_u64); + + /* old flags for the same sender will be automatically destroyed */ + sender_item = fu_daemon_ensure_sender_item(self, sender); + sender_item->feature_flags = feature_flags_u64; + g_dbus_method_invocation_return_value(invocation, NULL); + return; + } + if (g_strcmp0(method_name, "SetHints") == 0) { + FuDaemonSenderItem *sender_item; + const gchar *prop_key; + const gchar *prop_value; + g_autoptr(GVariantIter) iter = NULL; + + g_variant_get(parameters, "(a{ss})", &iter); + g_debug("Called %s()", method_name); + sender_item = fu_daemon_ensure_sender_item(self, sender); + while (g_variant_iter_next(iter, "{&s&s}", &prop_key, &prop_value)) { + g_debug("got hint %s=%s", prop_key, prop_value); + g_hash_table_insert(sender_item->hints, + g_strdup(prop_key), + g_strdup(prop_value)); + } + g_dbus_method_invocation_return_value(invocation, NULL); + return; + } + + if (g_strcmp0(method_name, "Install") == 0) { +#ifdef HAVE_GIO_UNIX + GVariant *prop_value; + const gchar *device_id = NULL; + const gchar *prop_key; + gint32 fd_handle = 0; + gint fd; + guint64 archive_size_max; + GDBusMessage *message; + GUnixFDList *fd_list; + g_autoptr(FuMainAuthHelper) helper = NULL; + g_autoptr(GVariantIter) iter = NULL; + + /* check the id exists */ + g_variant_get(parameters, "(&sha{sv})", &device_id, &fd_handle, &iter); + g_debug("Called %s(%s,%i)", method_name, device_id, fd_handle); + if (!fu_daemon_device_id_valid(device_id, &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* create helper object */ + helper = g_new0(FuMainAuthHelper, 1); + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); + helper->device_id = g_strdup(device_id); + helper->self = self; + + /* get flags */ + while (g_variant_iter_next(iter, "{&sv}", &prop_key, &prop_value)) { + g_debug("got option %s", prop_key); + if (g_strcmp0(prop_key, "offline") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_OFFLINE; + if (g_strcmp0(prop_key, "allow-older") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + if (g_strcmp0(prop_key, "allow-reinstall") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (g_strcmp0(prop_key, "allow-branch-switch") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (g_strcmp0(prop_key, "force") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_FORCE; + if (g_strcmp0(prop_key, "ignore-power") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER; + if (g_strcmp0(prop_key, "no-history") == 0 && + g_variant_get_boolean(prop_value) == TRUE) + helper->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; + g_variant_unref(prop_value); + } + + /* get the fd */ + message = g_dbus_method_invocation_get_message(invocation); + fd_list = g_dbus_message_get_unix_fd_list(message); + if (fd_list == NULL || g_unix_fd_list_get_length(fd_list) != 1) { + g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + fd = g_unix_fd_list_get(fd_list, 0, &error); + if (fd < 0) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* parse the cab file before authenticating so we can work out + * what action ID to use, for instance, if this is trusted -- + * this will also close the fd when done */ + archive_size_max = + fu_config_get_archive_size_max(fu_engine_get_config(self->engine)); + helper->blob_cab = fu_bytes_get_contents_fd(fd, archive_size_max, &error); + if (helper->blob_cab == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* install all the things in the store */ +#ifdef HAVE_POLKIT + helper->subject = polkit_system_bus_name_new(sender); +#endif /* HAVE_POLKIT */ + if (!fu_daemon_install_with_helper(g_steal_pointer(&helper), &error)) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } +#else + g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); + g_dbus_method_invocation_return_gerror(invocation, error); +#endif /* HAVE_GIO_UNIX */ + + /* async return */ + return; + } + if (g_strcmp0(method_name, "GetDetails") == 0) { +#ifdef HAVE_GIO_UNIX + GDBusMessage *message; + GUnixFDList *fd_list; + gint32 fd_handle = 0; + gint fd; + g_autoptr(GPtrArray) results = NULL; + + /* get parameters */ + g_variant_get(parameters, "(h)", &fd_handle); + g_debug("Called %s(%i)", method_name, fd_handle); + + /* get the fd */ + message = g_dbus_method_invocation_get_message(invocation); + fd_list = g_dbus_message_get_unix_fd_list(message); + if (fd_list == NULL || g_unix_fd_list_get_length(fd_list) != 1) { + g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + fd = g_unix_fd_list_get(fd_list, 0, &error); + if (fd < 0) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + + /* get details about the file (will close the fd when done) */ + results = fu_engine_get_details(self->engine, request, fd, &error); + if (results == NULL) { + g_dbus_method_invocation_return_gerror(invocation, error); + return; + } + val = fu_daemon_result_array_to_variant(results); + g_dbus_method_invocation_return_value(invocation, val); +#else + g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); + g_dbus_method_invocation_return_gerror(invocation, error); +#endif /* HAVE_GIO_UNIX */ + return; + } + if (g_strcmp0(method_name, "GetBiosSettings") == 0) { + gboolean authenticate = fu_engine_request_get_feature_flags(request) & + FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION; + + g_debug("Called %s", method_name); + if (!authenticate) { + g_autoptr(FuBiosSettings) attrs = + fu_context_get_bios_settings(fu_engine_get_context(self->engine)); + val = fu_bios_settings_to_variant( + attrs, + fu_engine_request_get_device_flags(request) & + FWUPD_DEVICE_FLAG_TRUSTED); + g_dbus_method_invocation_return_value(invocation, val); + } else { + g_autoptr(FuMainAuthHelper) helper = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.get-bios-settings", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_get_bios_settings_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_get_bios_settings_cb(NULL, + NULL, + g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + } + return; + } + if (g_strcmp0(method_name, "SetBiosSettings") == 0) { + g_autoptr(FuMainAuthHelper) helper = NULL; + const gchar *key; + const gchar *value; + g_autoptr(GVariantIter) iter = NULL; +#ifdef HAVE_POLKIT + g_autoptr(PolkitSubject) subject = NULL; +#endif + + g_variant_get(parameters, "(a{ss})", &iter); + g_debug("Called %s()", method_name); + + /* authenticate */ + fu_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + helper = g_new0(FuMainAuthHelper, 1); + helper->self = self; + helper->request = g_steal_pointer(&request); + helper->invocation = g_object_ref(invocation); + helper->bios_settings = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + while (g_variant_iter_next(iter, "{&s&s}", &key, &value)) { + g_debug("got setting %s=%s", key, value); + g_hash_table_insert(helper->bios_settings, g_strdup(key), g_strdup(value)); + } +#ifdef HAVE_POLKIT + subject = polkit_system_bus_name_new(sender); + polkit_authority_check_authorization( + self->authority, + subject, + "org.freedesktop.fwupd.set-bios-settings", + NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, + fu_daemon_authorize_set_bios_settings_cb, + g_steal_pointer(&helper)); +#else + fu_daemon_authorize_set_bios_settings_cb(NULL, NULL, g_steal_pointer(&helper)); +#endif /* HAVE_POLKIT */ + + return; + } + g_set_error(&error, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "no such method %s", + method_name); + g_dbus_method_invocation_return_gerror(invocation, error); +} + +static GVariant * +fu_daemon_daemon_get_property(GDBusConnection *connection_, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + + /* activity */ + fu_engine_idle_reset(self->engine); + + if (g_strcmp0(property_name, "DaemonVersion") == 0) + return g_variant_new_string(SOURCE_VERSION); + + if (g_strcmp0(property_name, "HostBkc") == 0) + return g_variant_new_string(fu_engine_get_host_bkc(self->engine)); + + if (g_strcmp0(property_name, "Tainted") == 0) + return g_variant_new_boolean(FALSE); + + if (g_strcmp0(property_name, "Status") == 0) + return g_variant_new_uint32(self->status); + + if (g_strcmp0(property_name, "Percentage") == 0) + return g_variant_new_uint32(self->percentage); + + if (g_strcmp0(property_name, FWUPD_RESULT_KEY_BATTERY_LEVEL) == 0) { + FuContext *ctx = fu_engine_get_context(self->engine); + return g_variant_new_uint32(fu_context_get_battery_level(ctx)); + } + + if (g_strcmp0(property_name, FWUPD_RESULT_KEY_BATTERY_THRESHOLD) == 0) { + FuContext *ctx = fu_engine_get_context(self->engine); + return g_variant_new_uint32(fu_context_get_battery_threshold(ctx)); + } + + if (g_strcmp0(property_name, "HostVendor") == 0) + return g_variant_new_string(fu_engine_get_host_vendor(self->engine)); + + if (g_strcmp0(property_name, "HostProduct") == 0) + return g_variant_new_string(fu_engine_get_host_product(self->engine)); + + if (g_strcmp0(property_name, "HostMachineId") == 0) { + const gchar *tmp = fu_engine_get_host_machine_id(self->engine); + if (tmp == NULL) { + g_set_error(error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "failed to get daemon property %s", + property_name); + return NULL; + } + return g_variant_new_string(tmp); + } + + if (g_strcmp0(property_name, "HostSecurityId") == 0) { + const gchar *tmp = fu_engine_get_host_security_id(self->engine); + if (tmp == NULL) { + g_set_error(error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "failed to get daemon property %s", + property_name); + return NULL; + } + return g_variant_new_string(tmp); + } + + if (g_strcmp0(property_name, "Interactive") == 0) + return g_variant_new_boolean(isatty(fileno(stdout)) != 0); + + if (g_strcmp0(property_name, "OnlyTrusted") == 0) { + return g_variant_new_boolean( + fu_config_get_only_trusted(fu_engine_get_config(self->engine))); + } + + /* return an error */ + g_set_error(error, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_PROPERTY, + "failed to get daemon property %s", + property_name); + return NULL; +} + +static void +fu_daemon_register_object(FuDaemon *self) +{ + guint registration_id; + static const GDBusInterfaceVTable interface_vtable = {fu_daemon_daemon_method_call, + fu_daemon_daemon_get_property, + NULL}; + + registration_id = + g_dbus_connection_register_object(self->connection, + FWUPD_DBUS_PATH, + self->introspection_daemon->interfaces[0], + &interface_vtable, + self, /* user_data */ + NULL, /* user_data_free_func */ + NULL); /* GError** */ + g_assert(registration_id > 0); +} + +static void +fu_daemon_dbus_bus_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + g_autoptr(GError) error = NULL; + + self->connection = g_object_ref(connection); + fu_daemon_register_object(self); + + /* connect to D-Bus directly */ + self->proxy_uid = g_dbus_proxy_new_sync(self->connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + NULL, + &error); + if (self->proxy_uid == NULL) { + g_warning("cannot connect to DBus: %s", error->message); + return; + } +} + +static void +fu_daemon_dbus_name_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ + g_debug("acquired name: %s", name); +} + +static void +fu_daemon_dbus_name_lost_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + if (self->update_in_progress) { + g_warning("name lost during a firmware update, ignoring"); + return; + } + g_warning("another service has claimed the dbus name %s", name); + g_main_loop_quit(self->loop); +} + +static void +fu_daemon_dbus_connection_closed_cb(GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error, + gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + g_debug("client connection closed: %s", error != NULL ? error->message : "unknown"); + g_clear_object(&self->connection); +} + +static gboolean +fu_daemon_dbus_new_connection_cb(GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + FuDaemon *self = FU_DAEMON(user_data); + g_set_object(&self->connection, connection); + g_signal_connect(connection, + "closed", + G_CALLBACK(fu_daemon_dbus_connection_closed_cb), + self); + fu_daemon_register_object(self); + return TRUE; +} + +static GDBusNodeInfo * +fu_daemon_load_introspection(const gchar *filename, GError **error) +{ + g_autoptr(GBytes) data = NULL; + g_autofree gchar *path = NULL; + + /* lookup data */ + path = g_build_filename("/org/freedesktop/fwupd", filename, NULL); + data = g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, error); + if (data == NULL) + return NULL; + + /* build introspection from XML */ + return g_dbus_node_info_new_for_xml(g_bytes_get_data(data, NULL), error); +} + +void +fu_daemon_set_machine_kind(FuDaemon *self, FuDaemonMachineKind machine_kind) +{ + g_return_if_fail(FU_IS_DAEMON(self)); + self->machine_kind = machine_kind; +} + +gboolean +fu_daemon_setup(FuDaemon *self, const gchar *socket_address, GError **error) +{ + const gchar *machine_kind = g_getenv("FWUPD_MACHINE_KIND"); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + + g_return_val_if_fail(FU_IS_DAEMON(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "load-engine"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-introspection"); +#ifdef HAVE_POLKIT + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-authority"); +#endif + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "own-name"); + + /* allow overriding for development */ + if (machine_kind != NULL) { + self->machine_kind = fu_daemon_machine_kind_from_string(machine_kind); + if (self->machine_kind == FU_DAEMON_MACHINE_KIND_UNKNOWN) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Invalid machine kind specified: %s", + machine_kind); + return FALSE; + } + } + + /* load engine */ + self->engine = fu_engine_new(); + g_signal_connect(FU_ENGINE(self->engine), + "changed", + G_CALLBACK(fu_daemon_engine_changed_cb), + self); + g_signal_connect(FU_ENGINE(self->engine), + "device-added", + G_CALLBACK(fu_daemon_engine_device_added_cb), + self); + g_signal_connect(FU_ENGINE(self->engine), + "device-removed", + G_CALLBACK(fu_daemon_engine_device_removed_cb), + self); + g_signal_connect(FU_ENGINE(self->engine), + "device-changed", + G_CALLBACK(fu_daemon_engine_device_changed_cb), + self); + g_signal_connect(FU_ENGINE(self->engine), + "device-request", + G_CALLBACK(fu_daemon_engine_device_request_cb), + self); + g_signal_connect(FU_ENGINE(self->engine), + "status-changed", + G_CALLBACK(fu_daemon_engine_status_changed_cb), + self); + if (!fu_engine_load(self->engine, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to load engine: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* load introspection from file */ + self->introspection_daemon = + fu_daemon_load_introspection(FWUPD_DBUS_INTERFACE ".xml", error); + if (self->introspection_daemon == NULL) { + g_prefix_error(error, "failed to load introspection: "); + return FALSE; + } + fu_progress_step_done(progress); + +#ifdef HAVE_POLKIT + /* get authority */ + self->authority = polkit_authority_get_sync(NULL, error); + if (self->authority == NULL) { + g_prefix_error(error, "failed to load authority: "); + return FALSE; + } + fu_progress_step_done(progress); +#endif + + /* own the object */ + if (socket_address != NULL) { + g_autofree gchar *guid = g_dbus_generate_guid(); + g_autoptr(GDBusServer) server = NULL; + + server = g_dbus_server_new_sync(socket_address, + G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, + guid, + NULL, + NULL, + error); + if (server == NULL) { + g_prefix_error(error, "failed to create D-Bus server: "); + return FALSE; + } + g_message("using socket address: %s", g_dbus_server_get_client_address(server)); + g_dbus_server_start(server); + g_signal_connect(server, + "new-connection", + G_CALLBACK(fu_daemon_dbus_new_connection_cb), + self); + } else { + self->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_daemon_dbus_bus_acquired_cb, + fu_daemon_dbus_name_acquired_cb, + fu_daemon_dbus_name_lost_cb, + self, + NULL); + } + fu_progress_step_done(progress); + + /* a good place to do the traceback */ + if (fu_progress_get_profile(progress)) { + g_autofree gchar *str = fu_progress_traceback(progress); + if (str != NULL) + g_print("\n%s\n", str); + } + + /* success */ + return TRUE; +} + +static void +fu_daemon_sender_item_free(FuDaemonSenderItem *sender_item) +{ + g_hash_table_unref(sender_item->hints); + g_free(sender_item); +} + +static void +fu_daemon_init(FuDaemon *self) +{ + self->status = FWUPD_STATUS_IDLE; + self->sender_items = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)fu_daemon_sender_item_free); + self->loop = g_main_loop_new(NULL, FALSE); +} + +static void +fu_daemon_finalize(GObject *obj) +{ + FuDaemon *self = FU_DAEMON(obj); + + g_hash_table_unref(self->sender_items); + if (self->process_quit_id != 0) + g_source_remove(self->process_quit_id); + if (self->loop != NULL) + g_main_loop_unref(self->loop); + if (self->owner_id > 0) + g_bus_unown_name(self->owner_id); + if (self->proxy_uid != NULL) + g_object_unref(self->proxy_uid); + if (self->engine != NULL) + g_object_unref(self->engine); + if (self->connection != NULL) + g_object_unref(self->connection); +#ifdef HAVE_POLKIT + if (self->authority != NULL) + g_object_unref(self->authority); +#endif + if (self->introspection_daemon != NULL) + g_dbus_node_info_unref(self->introspection_daemon); + + G_OBJECT_CLASS(fu_daemon_parent_class)->finalize(obj); +} + +static void +fu_daemon_class_init(FuDaemonClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_daemon_finalize; +} + +FuDaemon * +fu_daemon_new(void) +{ + FuDaemon *self; + self = g_object_new(FU_TYPE_DAEMON, NULL); + return FU_DAEMON(self); +} diff --git a/fwupd-1.8.6/src/fu-daemon.h b/fwupd-1.8.6/src/fu-daemon.h new file mode 100644 index 0000000000000000000000000000000000000000..3b1d3f1a9361afe6b6e822e725c9f09b3ed8b72d --- /dev/null +++ b/fwupd-1.8.6/src/fu-daemon.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_DAEMON (fu_daemon_get_type()) +G_DECLARE_FINAL_TYPE(FuDaemon, fu_daemon, FU, DAEMON, GObject) + +typedef enum { + FU_DAEMON_MACHINE_KIND_UNKNOWN, + FU_DAEMON_MACHINE_KIND_PHYSICAL, + FU_DAEMON_MACHINE_KIND_VIRTUAL, + FU_DAEMON_MACHINE_KIND_CONTAINER, +} FuDaemonMachineKind; + +FuDaemon * +fu_daemon_new(void); +gboolean +fu_daemon_setup(FuDaemon *self, const gchar *socket_address, GError **error); +void +fu_daemon_start(FuDaemon *self); +void +fu_daemon_stop(FuDaemon *self); +void +fu_daemon_set_machine_kind(FuDaemon *self, FuDaemonMachineKind machine_kind); diff --git a/fwupd-1.8.6/src/fu-debug.c b/fwupd-1.8.6/src/fu-debug.c new file mode 100644 index 0000000000000000000000000000000000000000..49160f5e74e7343290b11bdcd823d26f2400f7e2 --- /dev/null +++ b/fwupd-1.8.6/src/fu-debug.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2010 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuDebug" + +#include "config.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +typedef struct { + GOptionGroup *group; + gboolean verbose; + gboolean console; + gboolean no_timestamp; + gboolean no_domain; + gchar **plugin_verbose; + gchar **daemon_verbose; +#ifdef _WIN32 + HANDLE event_source; +#endif +} 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); +#ifdef _WIN32 + DeregisterEventSource(self->event_source); +#endif + g_free(self); +} + +static gboolean +fu_debug_filter_cb(FuDebug *self, const gchar *log_domain, GLogLevelFlags log_level) +{ + 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); +} + +#ifdef _WIN32 +static void +fu_debug_handler_win32(FuDebug *self, GLogLevelFlags log_level, const gchar *msg) +{ + WORD ev_type = 0x0; + + /* nothing to do */ + if (self->event_source == NULL) + return; + + /* map levels */ + switch (log_level) { + case G_LOG_LEVEL_INFO: + case G_LOG_LEVEL_MESSAGE: + ev_type = EVENTLOG_INFORMATION_TYPE; + break; + case G_LOG_LEVEL_WARNING: + ev_type = EVENTLOG_WARNING_TYPE; + break; + case G_LOG_LEVEL_ERROR: + case G_LOG_LEVEL_CRITICAL: + ev_type = EVENTLOG_ERROR_TYPE; + break; + default: + return; + break; + } + + /* add to log */ + ReportEventA(self->event_source, + ev_type, + FWUPD_CATEGORY_GENERIC, + FWUPD_MESSAGE_GENERIC, + NULL, + 1, + 0, + (const char **)&msg, + NULL); +} +#endif + +static void +fu_debug_handler_cb(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + FuDebug *self = (FuDebug *)user_data; + g_autofree gchar *timestamp = NULL; + g_autoptr(GString) domain = NULL; + +#ifdef _WIN32 + /* use Windows event log */ + fu_debug_handler_win32(self, log_level, message); +#endif + + /* should ignore */ + if (!fu_debug_filter_cb(self, log_domain, log_level)) + return; + + /* time header */ + if (!self->no_timestamp) { + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); + timestamp = g_strdup_printf("%02i:%02i:%02i:%04i", + g_date_time_get_hour(dt), + g_date_time_get_minute(dt), + g_date_time_get_second(dt), + g_date_time_get_microsecond(dt) / 1000); + } + + /* pad out domain */ + if (!self->no_domain) { + /* each file should have set this */ + if (log_domain == NULL) + log_domain = "FIXME"; + domain = g_string_new(log_domain); + for (gsize i = domain->len; i < 20; i++) + g_string_append(domain, " "); + } + + /* to file */ + if (!self->console) { + g_autofree gchar *ascii_message = g_str_to_ascii(message, NULL); + if (timestamp != NULL) + g_printerr("%s ", timestamp); + if (domain != NULL) + g_printerr("%s ", domain->str); + g_printerr("%s\n", ascii_message); + return; + } + + /* plain output */ + if (g_getenv("NO_COLOR") != NULL) { + if (timestamp != NULL) + g_printerr("%s ", timestamp); + if (domain != NULL) + g_printerr("%s ", domain->str); + g_printerr("%s\n", message); + return; + } + + /* to screen */ + switch (log_level) { + case G_LOG_LEVEL_ERROR: + case G_LOG_LEVEL_CRITICAL: + case G_LOG_LEVEL_WARNING: + /* critical in red */ + if (timestamp != NULL) + g_printerr("%c[%dm%s ", 0x1B, 32, timestamp); + if (domain != NULL) + g_printerr("%s ", domain->str); + g_printerr("%c[%dm%s\n%c[%dm", 0x1B, 31, message, 0x1B, 0); + break; + default: + /* debug in blue */ + if (timestamp != NULL) + g_printerr("%c[%dm%s ", 0x1B, 32, timestamp); + if (domain != NULL) + g_printerr("%s ", domain->str); + g_printerr("%c[%dm%s\n%c[%dm", 0x1B, 34, message, 0x1B, 0); + break; + } +} + +static gboolean +fu_debug_pre_parse_hook(GOptionContext *context, GOptionGroup *group, gpointer data, GError **error) +{ + FuDebug *self = (FuDebug *)data; + const GOptionEntry main_entries[] = { + {"verbose", + 'v', + 0, + G_OPTION_ARG_NONE, + &self->verbose, + /* TRANSLATORS: turn on all debugging */ + N_("Show debugging information for all domains"), + NULL}, + {"no-timestamp", + '\0', + 0, + G_OPTION_ARG_NONE, + &self->no_timestamp, + /* TRANSLATORS: turn on all debugging */ + N_("Do not include timestamp prefix"), + NULL}, + {"no-domain", + '\0', + 0, + G_OPTION_ARG_NONE, + &self->no_domain, + /* TRANSLATORS: turn on all debugging */ + N_("Do not include log domain prefix"), + NULL}, + {"plugin-verbose", + '\0', + 0, + G_OPTION_ARG_STRING_ARRAY, + &self->plugin_verbose, + /* TRANSLATORS: this is for plugin development */ + N_("Show plugin verbose information"), + "PLUGIN-NAME"}, + {"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}}; + + /* add main entry */ + g_option_context_add_main_entries(context, main_entries, NULL); + return TRUE; +} + +static gboolean +fu_debug_post_parse_hook(GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ + FuDebug *self = (FuDebug *)data; + + /* verbose? */ + if (self->verbose) { + (void)g_setenv("FWUPD_VERBOSE", "*", TRUE); + } else if (self->daemon_verbose != NULL) { + g_autofree gchar *str = g_strjoinv(",", self->daemon_verbose); + (void)g_setenv("FWUPD_VERBOSE", str, TRUE); + } + + /* redirect all domains to be able to change FWUPD_VERBOSE at runtime */ + g_log_set_default_handler(fu_debug_handler_cb, self); + + /* are we on an actual TTY? */ + self->console = (isatty(fileno(stderr)) == 1); + g_debug("Verbose debugging %s (on console %i)", + self->verbose ? "enabled" : "disabled", + self->console); + + /* allow each plugin to be extra verbose */ + if (self->plugin_verbose != NULL) { + for (guint i = 0; self->plugin_verbose[i] != NULL; i++) { + g_autofree gchar *name_caps = NULL; + g_autofree gchar *varname = NULL; + name_caps = g_ascii_strup(self->plugin_verbose[i], -1); + varname = g_strdup_printf("FWUPD_%s_VERBOSE", name_caps); + g_debug("setting %s=1", varname); + (void)g_setenv(varname, "1", TRUE); + } + } + return TRUE; +} + +#ifdef _WIN32 +static void +fu_debug_setup_event_source(FuDebug *self) +{ + HKEY key; + gchar msgfile[MAX_PATH]; + DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + + if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Services\\" + "EventLog\\Application\\fwupd", + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + NULL, + &key, + NULL) != ERROR_SUCCESS) { + g_warning("RegCreateKeyExA failed [%u]", (guint)GetLastError()); + return; + } + GetModuleFileNameA(NULL, msgfile, MAX_PATH); + RegSetValueExA(key, + "EventMessageFile", + 0, + REG_EXPAND_SZ, + (BYTE *)msgfile, + strlen(msgfile) + 1); + RegSetValueExA(key, "TypesSupported", 0, REG_DWORD, (BYTE *)&dwData, sizeof(dwData)); + RegCloseKey(key); + + /* good to go */ + self->event_source = RegisterEventSourceA(NULL, "fwupd"); +} +#endif + +/*(transfer): full */ +GOptionGroup * +fu_debug_get_option_group(void) +{ + FuDebug *self = g_new0(FuDebug, 1); + 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); +#ifdef _WIN32 + fu_debug_setup_event_source(self); +#endif + return g_option_group_ref(self->group); +} diff --git a/fwupd-1.8.6/src/fu-debug.h b/fwupd-1.8.6/src/fu-debug.h new file mode 100644 index 0000000000000000000000000000000000000000..1c1a383f453072a8803392cf8baabe2bcc73abca --- /dev/null +++ b/fwupd-1.8.6/src/fu-debug.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2010 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +GOptionGroup * +fu_debug_get_option_group(void); diff --git a/fwupd-1.8.6/src/fu-device-list.c b/fwupd-1.8.6/src/fu-device-list.c new file mode 100644 index 0000000000000000000000000000000000000000..c8364e6f1857091211a5a9c5c85722618d8170e3 --- /dev/null +++ b/fwupd-1.8.6/src/fu-device-list.c @@ -0,0 +1,1099 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuDeviceList" + +#include "config.h" + +#include + +#include "fu-device-list.h" +#include "fu-device-private.h" +#include "fu-mutex.h" + +/** + * FuDeviceList: + * + * This list of devices provides a way to find a device using either the + * device-id or a GUID. + * + * The device list will emit ::added and ::removed signals when the device list + * has been changed. If the #FuDevice has changed during a device replug then + * the ::changed signal will be emitted instead of ::added and then ::removed. + * + * See also: [class@FuDevice] + */ + +static void +fu_device_list_finalize(GObject *obj); + +struct _FuDeviceList { + GObject parent_instance; + GPtrArray *devices; /* of FuDeviceItem */ + GRWLock devices_mutex; +}; + +enum { SIGNAL_ADDED, SIGNAL_REMOVED, SIGNAL_CHANGED, SIGNAL_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +typedef struct { + FuDevice *device; + FuDevice *device_old; + FuDeviceList *self; /* no ref */ + guint remove_id; +} FuDeviceItem; + +G_DEFINE_TYPE(FuDeviceList, fu_device_list, G_TYPE_OBJECT) + +static void +fu_device_list_emit_device_added(FuDeviceList *self, FuDevice *device) +{ + g_debug("::added %s", fu_device_get_id(device)); + g_signal_emit(self, signals[SIGNAL_ADDED], 0, device); +} + +static void +fu_device_list_emit_device_removed(FuDeviceList *self, FuDevice *device) +{ + g_debug("::removed %s", fu_device_get_id(device)); + g_signal_emit(self, signals[SIGNAL_REMOVED], 0, device); +} + +static void +fu_device_list_emit_device_changed(FuDeviceList *self, FuDevice *device) +{ + g_debug("::changed %s", fu_device_get_id(device)); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0, device); +} + +static gchar * +fu_device_list_to_string(FuDeviceList *self) +{ + GString *str = g_string_new(NULL); + 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); + gboolean wfr; + + g_string_append_printf(str, + "%u [%p] %s\n", + i, + item, + item->remove_id != 0 ? "IN_TIMEOUT" : ""); + wfr = fu_device_has_flag(item->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + g_string_append_printf(str, + "new: %s [%p] %s\n", + fu_device_get_id(item->device), + item->device, + wfr ? "WAIT_FOR_REPLUG" : ""); + if (item->device_old != NULL) { + wfr = + fu_device_has_flag(item->device_old, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + g_string_append_printf(str, + "old: %s [%p] %s\n", + fu_device_get_id(item->device_old), + item->device_old, + wfr ? "WAIT_FOR_REPLUG" : ""); + } + } + g_rw_lock_reader_unlock(&self->devices_mutex); + return g_string_free(str, FALSE); +} + +/* we cannot use fu_device_get_children() as this will not find "parent-only" + * logical relationships added using fu_device_add_parent_guid() */ +static GPtrArray * +fu_device_list_get_children(FuDeviceList *self, FuDevice *device) +{ + GPtrArray *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); + if (device == fu_device_get_parent(item->device)) + g_ptr_array_add(devices, g_object_ref(item->device)); + } + g_rw_lock_reader_unlock(&self->devices_mutex); + return devices; +} + +static void +fu_device_list_depsolve_order_full(FuDeviceList *self, FuDevice *device, guint depth) +{ + g_autoptr(GPtrArray) children = NULL; + + /* ourself */ + fu_device_set_order(device, depth); + + /* optional children */ + children = fu_device_list_get_children(self, device); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index(children, i); + if (fu_device_has_flag(child, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST)) { + fu_device_list_depsolve_order_full(self, child, depth + 1); + } else { + fu_device_list_depsolve_order_full(self, child, depth - 1); + } + } +} + +/** + * fu_device_list_depsolve_order: + * @self: a device list + * @device: a device + * + * Sets the device order using the logical parent->child relationships -- by default + * the child is updated first, unless the device has set flag + * %FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST. + * + * Since: 1.5.0 + **/ +void +fu_device_list_depsolve_order(FuDeviceList *self, FuDevice *device) +{ + g_autoptr(FuDevice) root = fu_device_get_root(device); + fu_device_list_depsolve_order_full(self, root, 0); +} + +/** + * fu_device_list_get_all: + * @self: a device list + * + * Returns all the devices that have been added to the device list. + * This includes devices that are no longer active, for instance where a + * different plugin has taken over responsibility of the #FuDevice. + * + * Returns: (transfer container) (element-type FuDevice): the devices + * + * Since: 1.0.2 + **/ +GPtrArray * +fu_device_list_get_all(FuDeviceList *self) +{ + 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)); + } + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index(self->devices, i); + if (item->device_old == NULL) + continue; + g_ptr_array_add(devices, g_object_ref(item->device_old)); + } + g_rw_lock_reader_unlock(&self->devices_mutex); + return devices; +} + +/** + * fu_device_list_get_active: + * @self: a device list + * + * Returns all the active devices that have been added to the device list. + * 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 + * + * Since: 1.0.2 + **/ +GPtrArray * +fu_device_list_get_active(FuDeviceList *self) +{ + 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); + if (fu_device_has_inhibit(item->device, "unconnected")) + continue; + if (fu_device_has_inhibit(item->device, "hidden")) + continue; + 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) + return item; + } + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index(self->devices, i); + if (item->device_old == device) + return item; + } + return NULL; +} + +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)) + return item; + } + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index(self->devices, i); + if (item->device_old == NULL) + continue; + if (fu_device_has_guid(item->device_old, guid)) + return item; + } + return NULL; +} + +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) +{ + 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[] = {fu_device_get_id(item_tmp->device), + fu_device_get_equivalent_id(item_tmp->device), + NULL}; + for (guint j = 0; ids[j] != NULL; j++) { + if (strncmp(ids[j], device_id, device_id_len) == 0) { + if (item != NULL && multiple_matches != NULL) + *multiple_matches = TRUE; + item = item_tmp; + } + } + } + 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}; + if (item_tmp->device_old == NULL) + continue; + ids[0] = fu_device_get_id(item_tmp->device_old); + ids[1] = fu_device_get_equivalent_id(item_tmp->device_old); + for (guint j = 0; ids[j] != NULL; j++) { + if (strncmp(ids[j], device_id, device_id_len) == 0) { + if (item != NULL && multiple_matches != NULL) + *multiple_matches = TRUE; + item = item_tmp; + } + } + } + g_rw_lock_reader_unlock(&self->devices_mutex); + return item; +} + +/** + * fu_device_list_get_old: + * @self: a device list + * @device: a device + * + * Returns the old device associated with the currently active device. + * + * Returns: (transfer full): the device, or %NULL if not found + * + * Since: 1.0.3 + **/ +FuDevice * +fu_device_list_get_old(FuDeviceList *self, FuDevice *device) +{ + FuDeviceItem *item = fu_device_list_find_by_device(self, device); + if (item == NULL) + return NULL; + if (item->device_old == NULL) + return NULL; + return g_object_ref(item->device_old); +} + +static FuDeviceItem * +fu_device_list_get_by_guids_removed(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); + if (item->remove_id == 0) + continue; + for (guint j = 0; j < guids->len; j++) { + const gchar *guid = g_ptr_array_index(guids, j); + if (fu_device_has_guid(item->device, guid)) + return item; + } + } + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index(self->devices, i); + if (item->device_old == NULL) + continue; + if (item->remove_id == 0) + continue; + for (guint j = 0; j < guids->len; j++) { + const gchar *guid = g_ptr_array_index(guids, j); + if (fu_device_has_guid(item->device_old, guid)) + return item; + } + } + return NULL; +} + +static gboolean +fu_device_list_device_delayed_remove_cb(gpointer user_data) +{ + FuDeviceItem *item = (FuDeviceItem *)user_data; + FuDeviceList *self = FU_DEVICE_LIST(item->self); + + /* no longer valid */ + item->remove_id = 0; + + /* remove any children associated with device */ + if (!fu_device_has_internal_flag(item->device, + FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN)) { + GPtrArray *children = fu_device_get_children(item->device); + for (guint j = 0; j < children->len; j++) { + FuDevice *child = g_ptr_array_index(children, j); + FuDeviceItem *child_item; + 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; + } + 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); + } + } + + /* 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) +{ + /* 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); +} + +static gboolean +fu_device_list_should_remove_with_delay(FuDevice *device) +{ + if (fu_device_get_remove_delay(device) == 0) + return FALSE; + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_ONLY_WAIT_FOR_REPLUG) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) + return FALSE; + return TRUE; +} + +/** + * fu_device_list_remove: + * @self: a device list + * @device: a device + * + * Removes a specific device from the list if it exists. + * + * If the @device has a remove-delay set then a timeout will be started. If + * the exact same #FuDevice is added to the list with fu_device_list_add() + * within the timeout then only a ::changed signal will be emitted. + * + * If there is no remove-delay set, the ::removed signal will be emitted + * straight away. + * + * Since: 1.0.2 + **/ +void +fu_device_list_remove(FuDeviceList *self, FuDevice *device) +{ + FuDeviceItem *item; + + g_return_if_fail(FU_IS_DEVICE_LIST(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + + /* check the device already exists */ + item = fu_device_list_find_by_id(self, fu_device_get_id(device), NULL); + if (item == NULL) { + g_debug("device %s not found", fu_device_get_id(device)); + return; + } + + /* we can't do anything with an unconnected device */ + fu_device_inhibit(item->device, "unconnected", "Device has been removed"); + + /* ensure never fired if the remove delay is changed */ + if (item->remove_id > 0) { + g_source_remove(item->remove_id); + item->remove_id = 0; + } + + /* delay the removal and check for replug */ + if (fu_device_list_should_remove_with_delay(item->device)) { + fu_device_list_remove_with_delay(item); + return; + } + + /* remove any children associated with device */ + if (!fu_device_has_internal_flag(item->device, + FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN)) { + GPtrArray *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; + 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; + } + 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); + } + } + + /* 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 +fu_device_list_add_missing_guids(FuDevice *device_new, FuDevice *device_old) +{ + GPtrArray *guids_old = fu_device_get_guids(device_old); + for (guint i = 0; i < guids_old->len; i++) { + const gchar *guid_tmp = g_ptr_array_index(guids_old, i); + if (!fu_device_has_guid(device_new, guid_tmp)) { + if (fu_device_has_flag(device_new, + FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS)) { + g_debug("adding GUID %s to device", guid_tmp); + fu_device_add_counterpart_guid(device_new, guid_tmp); + } else { + g_debug("not adding GUID %s to device, use " + "FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS if required", + guid_tmp); + } + } + } +} + +static void +fu_device_list_item_finalized_cb(gpointer data, GObject *where_the_object_was) +{ + FuDeviceItem *item = (FuDeviceItem *)data; + FuDeviceList *self = FU_DEVICE_LIST(item->self); + + g_critical("FuDevice %p was finalized without being removed from " + "FuDeviceList, removing item!", + where_the_object_was); + g_rw_lock_writer_lock(&self->devices_mutex); + g_ptr_array_remove(self->devices, item); + g_rw_lock_writer_unlock(&self->devices_mutex); +} + +/* this should never be required, and yet here we are */ +static void +fu_device_list_item_set_device(FuDeviceItem *item, FuDevice *device) +{ + if (item->device != NULL) { + g_object_weak_unref(G_OBJECT(item->device), fu_device_list_item_finalized_cb, item); + } + if (device != NULL) { + g_object_weak_ref(G_OBJECT(device), fu_device_list_item_finalized_cb, item); + } + g_set_object(&item->device, device); +} + +static void +fu_device_list_clear_wait_for_replug(FuDeviceList *self, FuDeviceItem *item) +{ + /* clear timeout if scheduled */ + if (item->remove_id != 0) { + g_source_remove(item->remove_id); + item->remove_id = 0; + } + + /* remove flag on both old and new devices */ + if (fu_device_has_flag(item->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { + g_debug("%s device came back, clearing flag", fu_device_get_id(item->device)); + fu_device_remove_flag(item->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + if (item->device_old != NULL) { + if (fu_device_has_flag(item->device_old, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { + g_debug("%s old device came back, clearing flag", + fu_device_get_id(item->device_old)); + fu_device_remove_flag(item->device_old, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + } + fu_device_uninhibit(item->device, "unconnected"); + + /* optional debug */ + if (g_getenv("FWUPD_DEVICE_LIST_VERBOSE") != NULL) { + g_autofree gchar *str = fu_device_list_to_string(self); + g_debug("\n%s", str); + } +} + +static void +fu_device_incorporate_update_state(FuDevice *self, FuDevice *donor) +{ + if (fu_device_get_update_error(donor) != NULL && fu_device_get_update_error(self) == NULL) { + const gchar *update_error = fu_device_get_update_error(donor); + g_debug("copying update error %s to new device", update_error); + fu_device_set_update_error(self, update_error); + } + if (fu_device_get_update_state(donor) != FWUPD_UPDATE_STATE_UNKNOWN && + fu_device_get_update_state(self) == FWUPD_UPDATE_STATE_UNKNOWN) { + FwupdUpdateState update_state = fu_device_get_update_state(donor); + g_debug("copying update state %s to new device", + fwupd_update_state_to_string(update_state)); + fu_device_set_update_state(self, update_state); + } +} + +static void +fu_device_list_replace(FuDeviceList *self, FuDeviceItem *item, FuDevice *device) +{ + guint64 private_flags; + GPtrArray *vendor_ids; + + /* copy over any GUIDs that used to exist */ + fu_device_list_add_missing_guids(device, item->device); + + /* enforce the vendor ID if specified */ + vendor_ids = fu_device_get_vendor_ids(item->device); + for (guint i = 0; i < vendor_ids->len; i++) { + const gchar *vendor_id = g_ptr_array_index(vendor_ids, i); + g_debug("copying old vendor ID %s to new device", vendor_id); + fu_device_add_vendor_id(device, vendor_id); + } + + /* copy over custom flags */ + private_flags = fu_device_get_private_flags(item->device); + if (private_flags != 0) { + g_debug("copying old custom flags 0x%x to new device", (guint)private_flags); + fu_device_set_private_flags(device, private_flags); + } + + /* 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_format(device, fu_device_get_version_format(item->device)); + fu_device_set_version(device, version); + } + + /* 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_format(device, fu_device_get_version_format(item->device)); + fu_device_set_version(device, version); + } + + /* 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); + } + + /* seems like a sane assumption if we've tagged the runtime mode as signed */ + if (fu_device_has_flag(item->device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + if (fu_device_has_flag(item->device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD)) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + + /* device won't come back in right mode */ + if (fu_device_has_flag(item->device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { + g_debug("copying will-disappear to new device"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR); + } + + /* copy the parent if not already set */ + if (fu_device_get_parent(item->device) != NULL && + fu_device_get_parent(item->device) != device && + fu_device_get_parent(device) != item->device && fu_device_get_parent(device) == NULL) { + FuDevice *parent = fu_device_get_parent(item->device); + g_debug("copying parent %s to new device", fu_device_get_id(parent)); + fu_device_set_parent(device, parent); + } + + /* copy the update state if known */ + fu_device_incorporate_update_state(item->device, device); + + /* assign the new device */ + g_set_object(&item->device_old, item->device); + fu_device_list_item_set_device(item, device); + fu_device_list_emit_device_changed(self, device); + if (g_getenv("FWUPD_DEVICE_LIST_VERBOSE") != NULL) { + g_autofree gchar *str = fu_device_list_to_string(self); + g_debug("\n%s", str); + } + + /* we were waiting for this... */ + fu_device_list_clear_wait_for_replug(self, item); +} + +/** + * fu_device_list_add: + * @self: a device list + * @device: a device + * + * Adds a specific device to the device list if not already present. + * + * If the @device (or a compatible @device) has been previously removed within + * the remove-timeout then only the ::changed signal will be emitted on calling + * this function. Otherwise the ::added signal will be emitted straight away. + * + * Compatible devices are defined as #FuDevice objects that share at least one + * device GUID. If a compatible device is matched then the vendor ID and + * version will be copied to the new object if they are not already set. + * + * Any GUIDs present on the old device and not on the new device will be + * inherited and do not have to be copied over by plugins manually. + * + * Returns: (transfer none): a device, or %NULL if not found + * + * Since: 1.0.2 + **/ +void +fu_device_list_add(FuDeviceList *self, FuDevice *device) +{ + FuDeviceItem *item; + + g_return_if_fail(FU_IS_DEVICE_LIST(self)); + g_return_if_fail(FU_IS_DEVICE(device)); + + /* is the device waiting to be replugged? */ + item = fu_device_list_find_by_id(self, fu_device_get_id(device), NULL); + if (item != NULL) { + /* literally the same object */ + if (g_strcmp0(fu_device_get_id(device), fu_device_get_id(item->device)) == 0) { + g_debug("found existing device %s", fu_device_get_id(device)); + if (device != item->device) { + fu_device_uninhibit(item->device, "unconnected"); + fu_device_incorporate_update_state(device, item->device); + fu_device_list_item_set_device(item, device); + } + fu_device_list_clear_wait_for_replug(self, item); + fu_device_list_emit_device_changed(self, device); + return; + } + + /* the old device again */ + if (item->device_old != NULL && + g_strcmp0(fu_device_get_id(device), fu_device_get_id(item->device_old)) == 0) { + g_debug("found old device %s, swapping", fu_device_get_id(device)); + fu_device_uninhibit(item->device, "unconnected"); + fu_device_incorporate_update_state(device, item->device); + g_set_object(&item->device_old, item->device); + fu_device_list_item_set_device(item, device); + fu_device_list_clear_wait_for_replug(self, item); + fu_device_list_emit_device_changed(self, device); + return; + } + + /* same ID, different object */ + g_debug("found existing device %s, reusing item", fu_device_get_id(item->device)); + fu_device_list_replace(self, item, device); + fu_device_uninhibit(device, "unconnected"); + return; + } + + /* verify a device with same connection does not already exist */ + 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 physical 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); + fu_device_uninhibit(device, "unconnected"); + return; + } + + /* verify a compatible device does not already exist */ + item = fu_device_list_get_by_guids_removed(self, fu_device_get_guids(device)); + if (item != NULL) { + if (fu_device_has_internal_flag(device, + FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID)) { + 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); + fu_device_uninhibit(device, "unconnected"); + return; + } else { + g_debug("not adding matching %s for device add, use " + "FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID if required", + fu_device_get_id(item->device)); + } + } + + /* add helper */ + item = g_new0(FuDeviceItem, 1); + item->self = self; /* no ref */ + fu_device_list_item_set_device(item, device); + 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); +} + +/** + * fu_device_list_get_by_guid: + * @self: a device list + * @guid: a device GUID + * @error: (nullable): optional return location for an error + * + * Finds a specific device that has the matching GUID. + * + * Returns: (transfer full): a device, or %NULL if not found + * + * Since: 1.0.2 + **/ +FuDevice * +fu_device_list_get_by_guid(FuDeviceList *self, const gchar *guid, GError **error) +{ + FuDeviceItem *item; + g_return_val_if_fail(FU_IS_DEVICE_LIST(self), NULL); + g_return_val_if_fail(guid != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + item = fu_device_list_find_by_guid(self, guid); + if (item != NULL) + return g_object_ref(item->device); + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "GUID %s was not found", guid); + return NULL; +} + +static GPtrArray * +fu_device_list_get_wait_for_replug(FuDeviceList *self) +{ + GPtrArray *devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item_tmp = g_ptr_array_index(self->devices, i); + if (fu_device_has_flag(item_tmp->device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) + g_ptr_array_add(devices, g_object_ref(item_tmp->device)); + } + return devices; +} + +/** + * fu_device_list_wait_for_replug: + * @self: a device list + * @error: (nullable): optional return location for an error + * + * Waits for all the devices with %FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG to replug. + * + * 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, GError **error) +{ + guint remove_delay = 0; + g_autoptr(GTimer) timer = g_timer_new(); + g_autoptr(GPtrArray) devices_wfr1 = NULL; + g_autoptr(GPtrArray) devices_wfr2 = NULL; + + g_return_val_if_fail(FU_IS_DEVICE_LIST(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not required, or possibly literally just happened */ + devices_wfr1 = fu_device_list_get_wait_for_replug(self); + if (devices_wfr1->len == 0) { + g_debug("no replug or re-enumerate required"); + return TRUE; + } + + /* use the maximum of all the devices */ + for (guint i = 0; i < devices_wfr1->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices_wfr1, i); + if (fu_device_get_remove_delay(device_tmp) > remove_delay) + remove_delay = fu_device_get_remove_delay(device_tmp); + } + + /* plugin did not specify */ + if (remove_delay == 0) { + remove_delay = FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE; + g_warning("plugin did not specify a remove delay, " + "so guessing we should wait %ums for replug", + remove_delay); + } else { + g_debug("waiting %ums for replug", remove_delay); + } + + /* time to unplug and then re-plug */ + do { + g_autoptr(GPtrArray) devices_wfr_tmp = NULL; + g_usleep(1000); + g_main_context_iteration(NULL, FALSE); + devices_wfr_tmp = fu_device_list_get_wait_for_replug(self); + if (devices_wfr_tmp->len == 0) + break; + } while (g_timer_elapsed(timer, NULL) * 1000.f < remove_delay); + + /* check that no other devices are still waiting for replug */ + devices_wfr2 = fu_device_list_get_wait_for_replug(self); + if (devices_wfr2->len > 0) { + g_autoptr(GPtrArray) device_ids = g_ptr_array_new_with_free_func(g_free); + g_autofree gchar *device_ids_str = NULL; + + /* dump to console */ + if (g_getenv("FWUPD_DEVICE_LIST_VERBOSE") != NULL) { + g_autofree gchar *str = fu_device_list_to_string(self); + g_debug("\n%s", str); + } + + /* unset and build error string */ + for (guint i = 0; i < devices_wfr2->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices_wfr2, i); + fu_device_remove_flag(device_tmp, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + g_ptr_array_add(device_ids, g_strdup(fu_device_get_id(device_tmp))); + } + device_ids_str = fu_strjoin(",", device_ids); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "device %s did not come back", + device_ids_str); + return FALSE; + } + + /* the loop was quit without the timer */ + g_debug("waited for replug"); + return TRUE; +} + +/** + * fu_device_list_get_by_id: + * @self: a device list + * @device_id: a device ID, typically a SHA1 hash + * @error: (nullable): optional return location for an error + * + * Finds a specific device using the ID string. This function also supports + * using abbreviated hashes. + * + * Returns: (transfer full): a device, or %NULL if not found + * + * Since: 1.0.2 + **/ +FuDevice * +fu_device_list_get_by_id(FuDeviceList *self, const gchar *device_id, GError **error) +{ + FuDeviceItem *item; + gboolean multiple_matches = FALSE; + + g_return_val_if_fail(FU_IS_DEVICE_LIST(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* multiple things matched */ + item = fu_device_list_find_by_id(self, device_id, &multiple_matches); + if (multiple_matches) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device ID %s was not unique", + device_id); + return NULL; + } + + /* nothing at all matched */ + if (item == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "device ID %s was not found", + device_id); + return NULL; + } + + /* something found */ + return g_object_ref(item->device); +} + +static void +fu_device_list_item_free(FuDeviceItem *item) +{ + if (item->remove_id != 0) + g_source_remove(item->remove_id); + if (item->device_old != NULL) + g_object_unref(item->device_old); + fu_device_list_item_set_device(item, NULL); + g_free(item); +} + +static void +fu_device_list_class_init(FuDeviceListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_device_list_finalize; + + /** + * FuDeviceList::added: + * @self: the #FuDeviceList instance that emitted the signal + * @device: the #FuDevice + * + * The ::added signal is emitted when a device has been added to the list. + **/ + signals[SIGNAL_ADDED] = g_signal_new("added", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuDeviceList::removed: + * @self: the #FuDeviceList instance that emitted the signal + * @device: the #FuDevice + * + * The ::removed signal is emitted when a device has been removed from the list. + **/ + signals[SIGNAL_REMOVED] = g_signal_new("removed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuDeviceList::changed: + * @self: the #FuDeviceList instance that emitted the signal + * @device: the #FuDevice + * + * The ::changed signal is emitted when a device has changed. + **/ + signals[SIGNAL_CHANGED] = g_signal_new("changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); +} + +static void +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 +fu_device_list_finalize(GObject *obj) +{ + FuDeviceList *self = FU_DEVICE_LIST(obj); + + g_rw_lock_clear(&self->devices_mutex); + g_ptr_array_unref(self->devices); + + G_OBJECT_CLASS(fu_device_list_parent_class)->finalize(obj); +} + +/** + * fu_device_list_new: + * + * Creates a new device list. + * + * Returns: (transfer full): a device list + * + * Since: 1.0.2 + **/ +FuDeviceList * +fu_device_list_new(void) +{ + FuDeviceList *self; + self = g_object_new(FU_TYPE_DEVICE_LIST, NULL); + return FU_DEVICE_LIST(self); +} diff --git a/fwupd-1.8.6/src/fu-device-list.h b/fwupd-1.8.6/src/fu-device-list.h new file mode 100644 index 0000000000000000000000000000000000000000..21a6a8c1beec2bc8e1e85767837e83bf863a1757 --- /dev/null +++ b/fwupd-1.8.6/src/fu-device-list.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_DEVICE_LIST (fu_device_list_get_type()) +G_DECLARE_FINAL_TYPE(FuDeviceList, fu_device_list, FU, DEVICE_LIST, GObject) + +FuDeviceList * +fu_device_list_new(void); +void +fu_device_list_add(FuDeviceList *self, FuDevice *device); +void +fu_device_list_remove(FuDeviceList *self, FuDevice *device); +GPtrArray * +fu_device_list_get_all(FuDeviceList *self); +GPtrArray * +fu_device_list_get_active(FuDeviceList *self); +FuDevice * +fu_device_list_get_old(FuDeviceList *self, FuDevice *device); +FuDevice * +fu_device_list_get_by_id(FuDeviceList *self, const gchar *device_id, GError **error); +FuDevice * +fu_device_list_get_by_guid(FuDeviceList *self, const gchar *guid, GError **error); +gboolean +fu_device_list_wait_for_replug(FuDeviceList *self, GError **error); +void +fu_device_list_depsolve_order(FuDeviceList *self, FuDevice *device); diff --git a/fwupd-1.8.6/src/fu-engine-helper.c b/fwupd-1.8.6/src/fu-engine-helper.c new file mode 100644 index 0000000000000000000000000000000000000000..a9b3d05fc53225c1e965bdeb2bb77782aae3ea74 --- /dev/null +++ b/fwupd-1.8.6/src/fu-engine-helper.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuEngine" + +#include "config.h" + +#include + +#include + +#include "fwupd-device-private.h" + +#include "fu-engine-helper.h" +#include "fu-engine.h" + +static FwupdRelease * +fu_engine_get_release_with_tag(FuEngine *self, + FuEngineRequest *request, + FwupdDevice *dev, + const gchar *tag, + GError **error) +{ + g_autoptr(GPtrArray) rels = NULL; + + /* find the newest release that matches */ + rels = fu_engine_get_releases(self, request, fwupd_device_get_id(dev), error); + if (rels == NULL) + return NULL; + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + if (fwupd_release_has_tag(rel, tag)) + return g_object_ref(rel); + } + + /* no match */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no matching releases for device"); + return NULL; +} + +gboolean +fu_engine_update_motd(FuEngine *self, GError **error) +{ + const gchar *host_bkc = fu_engine_get_host_bkc(self); + guint upgrade_count = 0; + guint sync_count = 0; + g_autoptr(FuEngineRequest) request = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GString) str = g_string_new(NULL); + g_autofree gchar *target = NULL; + + /* a subset of what fwupdmgr can do */ + request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + fu_engine_request_set_feature_flags(request, + FWUPD_FEATURE_FLAG_DETACH_ACTION | + FWUPD_FEATURE_FLAG_UPDATE_ACTION); + + /* get devices from daemon, we even want to know if it's nothing */ + devices = fu_engine_get_devices(self, NULL); + if (devices != NULL) { + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(GPtrArray) rels = NULL; + + /* get the releases for this device */ + rels = + fu_engine_get_upgrades(self, request, fwupd_device_get_id(dev), NULL); + if (rels == NULL) + continue; + upgrade_count++; + } + if (host_bkc != NULL) { + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(FwupdRelease) rel = NULL; + rel = fu_engine_get_release_with_tag(self, + request, + dev, + host_bkc, + NULL); + if (rel == NULL) + continue; + if (g_strcmp0(fwupd_device_get_version(dev), + fwupd_release_get_version(rel)) != 0) + sync_count++; + } + } + } + + /* If running under systemd unit, use the directory as a base */ + if (g_getenv("RUNTIME_DIRECTORY") != NULL) { + target = g_build_filename(g_getenv("RUNTIME_DIRECTORY"), MOTD_FILE, NULL); + /* otherwise use the cache directory */ + } else { + g_autofree gchar *directory = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); + target = g_build_filename(directory, MOTD_DIR, MOTD_FILE, NULL); + } + + /* create the directory and file, even if zero devices; we want an empty file then */ + if (!fu_path_mkdir_parent(target, error)) + return FALSE; + + /* nag about syncing or updating, but never both */ + if (sync_count > 0) { + g_string_append(str, "\n"); + g_string_append_printf(str, + /* TRANSLATORS: this is shown in the MOTD */ + ngettext("%u device is not the best known configuration.", + "%u devices are not the best known configuration.", + sync_count), + sync_count); + g_string_append_printf(str, + "\n%s\n\n", + /* TRANSLATORS: this is shown in the MOTD */ + _("Run `fwupdmgr sync-bkc` to complete this action.")); + } else if (upgrade_count > 0) { + g_string_append(str, "\n"); + g_string_append_printf(str, + /* TRANSLATORS: this is shown in the MOTD */ + ngettext("%u device has a firmware upgrade available.", + "%u devices have a firmware upgrade available.", + upgrade_count), + upgrade_count); + g_string_append_printf(str, + "\n%s\n\n", + /* TRANSLATORS: this is shown in the MOTD */ + _("Run `fwupdmgr get-upgrades` for more information.")); + } + + /* success, with an empty file if nothing to say */ + g_debug("writing motd target %s", target); + return g_file_set_contents(target, str->str, str->len, error); +} + +gboolean +fu_engine_update_devices_file(FuEngine *self, GError **error) +{ + FwupdDeviceFlags flags = FWUPD_DEVICE_FLAG_NONE; + gsize len; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) generator = NULL; + g_autoptr(JsonNode) root = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autofree gchar *data = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *target = NULL; + + if (fu_config_get_show_device_private(fu_engine_get_config(self))) + flags |= FWUPD_DEVICE_FLAG_TRUSTED; + + builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Devices"); + json_builder_begin_array(builder); + devices = fu_engine_get_devices(self, NULL); + if (devices != NULL) { + 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_full(dev, builder, flags); + json_builder_end_object(builder); + } + } + json_builder_end_array(builder); + json_builder_end_object(builder); + + root = json_builder_get_root(builder); + generator = json_generator_new(); + json_generator_set_pretty(generator, TRUE); + json_generator_set_root(generator, root); + data = json_generator_to_data(generator, &len); + if (data == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert to JSON string"); + return FALSE; + } + + directory = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); + target = g_build_filename(directory, "devices.json", NULL); + return g_file_set_contents(target, data, (gssize)len, error); +} diff --git a/fwupd-1.8.6/src/fu-engine-helper.h b/fwupd-1.8.6/src/fu-engine-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..ce24ca585a5ac68d728f93e20a1537f9c8921936 --- /dev/null +++ b/fwupd-1.8.6/src/fu-engine-helper.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2020 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#pragma once + +#include "fu-engine.h" + +gboolean +fu_engine_update_motd(FuEngine *self, GError **error); +gboolean +fu_engine_update_devices_file(FuEngine *self, GError **error); diff --git a/fwupd-1.8.6/src/fu-engine-request.c b/fwupd-1.8.6/src/fu-engine-request.c new file mode 100644 index 0000000000000000000000000000000000000000..b2a74c4f51ae185b42a8dc875b9db2629e7372a4 --- /dev/null +++ b/fwupd-1.8.6/src/fu-engine-request.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuEngine" + +#include "config.h" + +#include "fu-engine-request.h" + +struct _FuEngineRequest { + GObject parent_instance; + FuEngineRequestKind kind; + FwupdFeatureFlags feature_flags; + FwupdDeviceFlags device_flags; + gchar *locale; +}; + +G_DEFINE_TYPE(FuEngineRequest, fu_engine_request, G_TYPE_OBJECT) + +FwupdFeatureFlags +fu_engine_request_get_feature_flags(FuEngineRequest *self) +{ + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FALSE); + return self->feature_flags; +} + +const gchar * +fu_engine_request_get_locale(FuEngineRequest *self) +{ + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), NULL); + return self->locale; +} + +FuEngineRequestKind +fu_engine_request_get_kind(FuEngineRequest *self) +{ + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FU_ENGINE_REQUEST_KIND_UNKNOWN); + return self->kind; +} + +void +fu_engine_request_set_feature_flags(FuEngineRequest *self, FwupdFeatureFlags feature_flags) +{ + g_return_if_fail(FU_IS_ENGINE_REQUEST(self)); + self->feature_flags = feature_flags; +} + +void +fu_engine_request_set_locale(FuEngineRequest *self, const gchar *locale) +{ + g_return_if_fail(FU_IS_ENGINE_REQUEST(self)); + + /* not changed */ + if (g_strcmp0(self->locale, locale) == 0) + return; + + g_free(self->locale); + self->locale = g_strdup(locale); + + /* remove the UTF8 suffix as it is not present in the XML */ + if (self->locale != NULL) + g_strdelimit(self->locale, ".", '\0'); +} + +gboolean +fu_engine_request_has_feature_flag(FuEngineRequest *self, FwupdFeatureFlags feature_flag) +{ + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FALSE); + return (self->feature_flags & feature_flag) > 0; +} + +FwupdDeviceFlags +fu_engine_request_get_device_flags(FuEngineRequest *self) +{ + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FALSE); + return self->device_flags; +} + +void +fu_engine_request_set_device_flags(FuEngineRequest *self, FwupdDeviceFlags device_flags) +{ + g_return_if_fail(FU_IS_ENGINE_REQUEST(self)); + self->device_flags = device_flags; +} + +gboolean +fu_engine_request_has_device_flag(FuEngineRequest *self, FwupdDeviceFlags device_flag) +{ + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FALSE); + return (self->device_flags & device_flag) > 0; +} + +static void +fu_engine_request_init(FuEngineRequest *self) +{ + self->device_flags = FWUPD_DEVICE_FLAG_NONE; + self->feature_flags = FWUPD_FEATURE_FLAG_NONE; +} + +static void +fu_engine_request_finalize(GObject *obj) +{ + FuEngineRequest *self = FU_ENGINE_REQUEST(obj); + g_free(self->locale); + G_OBJECT_CLASS(fu_engine_request_parent_class)->finalize(obj); +} + +static void +fu_engine_request_class_init(FuEngineRequestClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_engine_request_finalize; +} + +FuEngineRequest * +fu_engine_request_new(FuEngineRequestKind kind) +{ + FuEngineRequest *self; + self = g_object_new(FU_TYPE_ENGINE_REQUEST, NULL); + self->kind = kind; + return FU_ENGINE_REQUEST(self); +} diff --git a/fwupd-1.8.6/src/fu-engine-request.h b/fwupd-1.8.6/src/fu-engine-request.h new file mode 100644 index 0000000000000000000000000000000000000000..3ec5670fcea19327e7541fe9abac96148ba5865c --- /dev/null +++ b/fwupd-1.8.6/src/fu-engine-request.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_ENGINE_REQUEST (fu_engine_request_get_type()) +G_DECLARE_FINAL_TYPE(FuEngineRequest, fu_engine_request, FU, ENGINE_REQUEST, GObject) + +typedef enum { + FU_ENGINE_REQUEST_KIND_UNKNOWN, + FU_ENGINE_REQUEST_KIND_ACTIVE, + FU_ENGINE_REQUEST_KIND_ONLY_SUPPORTED, +} FuEngineRequestKind; + +FuEngineRequest * +fu_engine_request_new(FuEngineRequestKind kind); +FuEngineRequestKind +fu_engine_request_get_kind(FuEngineRequest *self); +FwupdFeatureFlags +fu_engine_request_get_feature_flags(FuEngineRequest *self); +void +fu_engine_request_set_feature_flags(FuEngineRequest *self, FwupdFeatureFlags feature_flags); +const gchar * +fu_engine_request_get_locale(FuEngineRequest *self); +void +fu_engine_request_set_locale(FuEngineRequest *self, const gchar *locale); +gboolean +fu_engine_request_has_feature_flag(FuEngineRequest *self, FwupdFeatureFlags feature_flag); +gboolean +fu_engine_request_has_device_flag(FuEngineRequest *self, FwupdDeviceFlags device_flag); +FwupdDeviceFlags +fu_engine_request_get_device_flags(FuEngineRequest *self); +void +fu_engine_request_set_device_flags(FuEngineRequest *self, FwupdDeviceFlags device_flags); diff --git a/fwupd-1.8.6/src/fu-engine.c b/fwupd-1.8.6/src/fu-engine.c new file mode 100644 index 0000000000000000000000000000000000000000..56273b6dfa7cb6088e65fb4b56756a0d7d7e9028 --- /dev/null +++ b/fwupd-1.8.6/src/fu-engine.c @@ -0,0 +1,8274 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuEngine" + +#include "config.h" + +#include + +#ifdef HAVE_GIO_UNIX +#include +#endif +#include +#include +#ifdef HAVE_UTSNAME_H +#include +#endif +#include + +#ifdef _WIN32 +#include +#include +#include +#endif + +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-common-private.h" +#include "fwupd-device-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-release-private.h" +#include "fwupd-remote-private.h" +#include "fwupd-resources.h" +#include "fwupd-security-attr-private.h" + +#include "fu-backend-private.h" +#include "fu-bios-settings-private.h" +#include "fu-cabinet.h" +#include "fu-context-private.h" +#include "fu-coswid-firmware.h" +#include "fu-debug.h" +#include "fu-device-list.h" +#include "fu-device-private.h" +#include "fu-engine-helper.h" +#include "fu-engine-request.h" +#include "fu-engine.h" +#include "fu-history.h" +#include "fu-idle.h" +#include "fu-kenv.h" +#include "fu-keyring-utils.h" +#include "fu-mutex.h" +#include "fu-plugin-builtin.h" +#include "fu-plugin-list.h" +#include "fu-plugin-private.h" +#include "fu-release.h" +#include "fu-remote-list.h" +#include "fu-security-attr-common.h" +#include "fu-security-attrs-private.h" +#include "fu-udev-device-private.h" +#include "fu-usb-device-fw-ds20.h" +#include "fu-usb-device-ms-ds20.h" +#include "fu-version.h" + +#ifdef HAVE_GUDEV +#include "fu-udev-backend.h" +#endif +#ifdef HAVE_GUSB +#include "fu-usb-backend.h" +#endif +#ifdef HAVE_BLUEZ +#include "fu-bluez-backend.h" +#endif + +/* only needed until we hard depend on jcat 0.1.3 */ +#include + +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif + +#define MINIMUM_BATTERY_PERCENTAGE_FALLBACK 10 + +static void +fu_engine_finalize(GObject *obj); +static void +fu_engine_ensure_security_attrs(FuEngine *self); + +struct _FuEngine { + GObject parent_instance; + GPtrArray *backends; + FuConfig *config; + FuRemoteList *remote_list; + FuDeviceList *device_list; + gboolean only_trusted; + gboolean write_history; + gboolean host_emulation; + guint percentage; + FuHistory *history; + FuIdle *idle; + XbSilo *silo; + XbQuery *query_component_by_guid; + guint coldplug_id; + FuPluginList *plugin_list; + GPtrArray *plugin_filter; + FuContext *ctx; + GHashTable *runtime_versions; + GHashTable *compile_versions; + GHashTable *approved_firmware; /* (nullable) */ + GHashTable *blocked_firmware; /* (nullable) */ + gchar *host_machine_id; + JcatContext *jcat_context; + gboolean loaded; + gchar *host_security_id; + FuSecurityAttrs *host_security_attrs; + GPtrArray *local_monitors; /* (element-type GFileMonitor) */ + GMainLoop *acquiesce_loop; + guint acquiesce_id; + guint acquiesce_delay; +}; + +enum { + SIGNAL_CHANGED, + SIGNAL_DEVICE_ADDED, + SIGNAL_DEVICE_REMOVED, + SIGNAL_DEVICE_CHANGED, + SIGNAL_DEVICE_REQUEST, + SIGNAL_STATUS_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = {0}; + +G_DEFINE_TYPE(FuEngine, fu_engine, G_TYPE_OBJECT) + +static void +fu_engine_emit_changed(FuEngine *self) +{ + g_autoptr(GError) error = NULL; + + g_signal_emit(self, signals[SIGNAL_CHANGED], 0); + fu_engine_idle_reset(self); + + /* update the motd */ + if (self->loaded && fu_config_get_update_motd(self->config)) { + g_autoptr(GError) error_local = NULL; + if (!fu_engine_update_motd(self, &error_local)) + g_debug("failed to update MOTD: %s", error_local->message); + } + + /* update the list of devices */ + if (!fu_engine_update_devices_file(self, &error)) + g_debug("failed to update list of devices: %s", error->message); +} + +static void +fu_engine_emit_device_changed_safe(FuEngine *self, FuDevice *device) +{ + /* invalidate host security attributes */ + g_clear_pointer(&self->host_security_id, g_free); + g_signal_emit(self, signals[SIGNAL_DEVICE_CHANGED], 0, device); +} + +/* get the latest version of the device */ +static void +fu_engine_emit_device_changed(FuEngine *self, const gchar *device_id) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(GError) error = NULL; + + /* get the latest version of this */ + device = fu_device_list_get_by_id(self->device_list, device_id, &error); + if (device == NULL) { + g_warning("cannot emit device-changed: %s", error->message); + return; + } + fu_engine_emit_device_changed_safe(self, device); +} + +static const GError * +fu_engine_error_array_find(GPtrArray *errors, FwupdError error_code) +{ + for (guint j = 0; j < errors->len; j++) { + const GError *error = g_ptr_array_index(errors, j); + if (g_error_matches(error, FWUPD_ERROR, error_code)) + return error; + } + return NULL; +} + +static guint +fu_engine_error_array_count(GPtrArray *errors, FwupdError error_code) +{ + guint cnt = 0; + for (guint j = 0; j < errors->len; j++) { + const GError *error = g_ptr_array_index(errors, j); + if (g_error_matches(error, FWUPD_ERROR, error_code)) + cnt++; + } + return cnt; +} + +static gboolean +fu_engine_error_array_matches_any(GPtrArray *errors, FwupdError *error_codes) +{ + for (guint j = 0; j < errors->len; j++) { + const GError *error = g_ptr_array_index(errors, j); + gboolean matches_any = FALSE; + for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) { + if (g_error_matches(error, FWUPD_ERROR, error_codes[i])) { + matches_any = TRUE; + break; + } + } + if (!matches_any) + return FALSE; + } + return TRUE; +} + +/** + * fu_engine_error_array_get_best: + * @errors: (element-type GError): array of errors + * + * Finds the 'best' error to show the user from a array of errors, creating a + * completely bespoke error where required. + * + * Returns: (transfer full): a #GError, never %NULL + **/ +GError * +fu_engine_error_array_get_best(GPtrArray *errors) +{ + FwupdError err_prio[] = {FWUPD_ERROR_INVALID_FILE, + FWUPD_ERROR_VERSION_SAME, + FWUPD_ERROR_VERSION_NEWER, + FWUPD_ERROR_NOT_SUPPORTED, + FWUPD_ERROR_INTERNAL, + FWUPD_ERROR_NOT_FOUND, + FWUPD_ERROR_LAST}; + FwupdError err_all_uptodate[] = {FWUPD_ERROR_VERSION_SAME, + FWUPD_ERROR_NOT_FOUND, + FWUPD_ERROR_NOT_SUPPORTED, + FWUPD_ERROR_LAST}; + FwupdError err_all_newer[] = {FWUPD_ERROR_VERSION_NEWER, + FWUPD_ERROR_VERSION_SAME, + FWUPD_ERROR_NOT_FOUND, + FWUPD_ERROR_NOT_SUPPORTED, + FWUPD_ERROR_LAST}; + + /* are all the errors either GUID-not-matched or version-same? */ + if (fu_engine_error_array_count(errors, FWUPD_ERROR_VERSION_SAME) > 1 && + fu_engine_error_array_matches_any(errors, err_all_uptodate)) { + return g_error_new(FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "All updatable firmware is already installed"); + } + + /* are all the errors either GUID-not-matched or version same or newer? */ + if (fu_engine_error_array_count(errors, FWUPD_ERROR_VERSION_NEWER) > 1 && + fu_engine_error_array_matches_any(errors, err_all_newer)) { + return g_error_new(FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "All updatable devices already have newer versions"); + } + + /* get the most important single error */ + for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) { + const GError *error_tmp = fu_engine_error_array_find(errors, err_prio[i]); + if (error_tmp != NULL) + return g_error_copy(error_tmp); + } + + /* fall back to something */ + return g_error_new(FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "No supported devices found"); +} + +FuContext * +fu_engine_get_context(FuEngine *self) +{ + return self->ctx; +} + +static void +fu_engine_set_status(FuEngine *self, FwupdStatus status) +{ + /* emit changed */ + g_signal_emit(self, signals[SIGNAL_STATUS_CHANGED], 0, status); +} + +static void +fu_engine_generic_notify_cb(FuDevice *device, GParamSpec *pspec, FuEngine *self) +{ + fu_engine_emit_device_changed(self, fu_device_get_id(device)); +} + +static void +fu_engine_history_notify_cb(FuDevice *device, GParamSpec *pspec, FuEngine *self) +{ + if (self->write_history) { + g_autoptr(GError) error_local = NULL; + if (!fu_history_modify_device(self->history, device, &error_local)) { + g_warning("failed to record history for %s: %s", + fu_device_get_id(device), + error_local->message); + } + } + fu_engine_emit_device_changed(self, fu_device_get_id(device)); +} + +static void +fu_engine_device_request_cb(FuDevice *device, FwupdRequest *request, FuEngine *self) +{ + g_debug("Emitting DeviceRequest('Message'='%s')", fwupd_request_get_message(request)); + g_signal_emit(self, signals[SIGNAL_DEVICE_REQUEST], 0, request); +} + +static void +fu_engine_watch_device(FuEngine *self, FuDevice *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_generic_notify_cb, self); + g_signal_handlers_disconnect_by_func(device_old, fu_engine_history_notify_cb, self); + g_signal_handlers_disconnect_by_func(device_old, fu_engine_device_request_cb, self); + } + g_signal_connect(FU_DEVICE(device), + "notify::flags", + G_CALLBACK(fu_engine_generic_notify_cb), + self); + g_signal_connect(FU_DEVICE(device), + "notify::problems", + G_CALLBACK(fu_engine_generic_notify_cb), + self); + g_signal_connect(FU_DEVICE(device), + "notify::update-message", + G_CALLBACK(fu_engine_generic_notify_cb), + self); + g_signal_connect(FU_DEVICE(device), + "notify::update-image", + G_CALLBACK(fu_engine_generic_notify_cb), + self); + g_signal_connect(FU_DEVICE(device), + "notify::update-state", + G_CALLBACK(fu_engine_history_notify_cb), + self); + g_signal_connect(FU_DEVICE(device), + "notify::update-error", + G_CALLBACK(fu_engine_history_notify_cb), + self); + g_signal_connect(FU_DEVICE(device), + "request", + G_CALLBACK(fu_engine_device_request_cb), + self); +} + +static void +fu_engine_ensure_device_battery_inhibit(FuEngine *self, FuDevice *device) +{ + if (fu_config_get_ignore_power(self->config)) + return; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC) && + (fu_context_get_battery_state(self->ctx) == FU_BATTERY_STATE_DISCHARGING || + fu_context_get_battery_state(self->ctx) == FU_BATTERY_STATE_EMPTY)) { + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER); + } else { + fu_device_remove_problem(device, FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER); + } + if (fu_context_get_battery_level(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID && + fu_context_get_battery_threshold(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID && + fu_context_get_battery_level(self->ctx) < fu_context_get_battery_threshold(self->ctx)) { + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW); + } else { + fu_device_remove_problem(device, FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW); + } +} + +static void +fu_engine_ensure_device_lid_inhibit(FuEngine *self, FuDevice *device) +{ + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_NO_LID_CLOSED) && + fu_context_get_lid_state(self->ctx) == FU_LID_STATE_CLOSED) { + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED); + return; + } + fu_device_remove_problem(device, FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED); +} + +static gboolean +fu_engine_acquiesce_timeout_cb(gpointer user_data) +{ + FuEngine *self = FU_ENGINE(user_data); + g_debug("system acquiesced after %ums", self->acquiesce_delay); + g_main_loop_quit(self->acquiesce_loop); + self->acquiesce_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fu_engine_acquiesce_reset(FuEngine *self) +{ + if (!g_main_loop_is_running(self->acquiesce_loop)) + return; + g_debug("resetting system acquiesce timeout"); + if (self->acquiesce_id != 0) + g_source_remove(self->acquiesce_id); + self->acquiesce_id = + g_timeout_add(self->acquiesce_delay, fu_engine_acquiesce_timeout_cb, self); +} + +static void +fu_engine_wait_for_acquiesce(FuEngine *self, guint acquiesce_delay) +{ + if (acquiesce_delay == 0) + return; + self->acquiesce_delay = acquiesce_delay; + self->acquiesce_id = g_timeout_add(acquiesce_delay, fu_engine_acquiesce_timeout_cb, self); + g_main_loop_run(self->acquiesce_loop); +} + +static void +fu_engine_device_added_cb(FuDeviceList *device_list, FuDevice *device, FuEngine *self) +{ + fu_engine_watch_device(self, device); + fu_engine_ensure_device_battery_inhibit(self, device); + fu_engine_ensure_device_lid_inhibit(self, device); + fu_engine_acquiesce_reset(self); + g_signal_emit(self, signals[SIGNAL_DEVICE_ADDED], 0, device); +} + +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); + fu_engine_acquiesce_reset(self); + g_signal_handlers_disconnect_by_data(device, self); + g_signal_emit(self, signals[SIGNAL_DEVICE_REMOVED], 0, device); +} + +static void +fu_engine_device_changed_cb(FuDeviceList *device_list, FuDevice *device, FuEngine *self) +{ + fu_engine_watch_device(self, device); + fu_engine_emit_device_changed(self, fu_device_get_id(device)); + fu_engine_acquiesce_reset(self); +} + +static gchar * +fu_engine_request_get_localized_xpath(FuEngineRequest *request, const gchar *element) +{ + GString *xpath = g_string_new(element); + const gchar *locale = NULL; + + /* optional; not set in tests */ + if (request != NULL) + locale = fu_engine_request_get_locale(request); + + /* prefer the users locale if set */ + if (locale != NULL) { + g_autofree gchar *xpath_locale = NULL; + xpath_locale = g_strdup_printf("%s[@xml:lang='%s']|", element, locale); + g_string_prepend(xpath, xpath_locale); + } + return g_string_free(xpath, FALSE); +} + +/* add any client-side BKC tags */ +static gboolean +fu_engine_add_local_release_metadata(FuEngine *self, FuRelease *release, GError **error) +{ + FuDevice *dev = fu_release_get_device(release); + GPtrArray *guids; + g_autoptr(XbQuery) query = NULL; + g_autoptr(GError) error_query = NULL; + + /* no device matched */ + if (dev == NULL) + return TRUE; + + /* prepare query with bound GUID parameter */ + query = xb_query_new_full(self->silo, + "local/components/component[@merge='append']/provides/" + "firmware[text()=?]/../../releases/release[@version=?]/../../" + "tags/tag", + XB_QUERY_FLAG_OPTIMIZE | XB_QUERY_FLAG_USE_INDEXES, + &error_query); + if (query == NULL) { + if (g_error_matches(error_query, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + return TRUE; + g_propagate_error(error, g_steal_pointer(&error_query)); + return FALSE; + } + + /* use prepared query for each GUID */ + guids = fu_device_get_guids(dev); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) tags = NULL; +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); +#endif + + /* bind GUID and then query */ +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL); + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), + 1, + fu_release_get_version(release), + NULL); + tags = xb_silo_query_with_context(self->silo, query, &context, &error_local); +#else + if (!xb_query_bind_str(query, 0, guid, error)) { + g_prefix_error(error, "failed to bind GUID: "); + return FALSE; + } + if (!xb_query_bind_str(query, 1, fu_release_get_version(release), error)) { + g_prefix_error(error, "failed to bind version: "); + return FALSE; + } + tags = xb_silo_query_full(self->silo, query, &error_local); +#endif + if (tags == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + continue; + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + for (guint j = 0; j < tags->len; j++) { + XbNode *tag = g_ptr_array_index(tags, j); + fu_release_add_tag(release, xb_node_get_text(tag)); + } + } + + /* success */ + return TRUE; +} + +static void +fu_engine_release_remote_id_changed_cb(FuRelease *release, GParamSpec *pspec, FuEngine *self) +{ + FwupdRemote *remote; + const gchar *remote_id = fwupd_release_get_remote_id(FWUPD_RELEASE(release)); + + if (remote_id == NULL) + return; + remote = fu_remote_list_get_by_id(self->remote_list, remote_id); + if (remote == NULL) { + g_warning("no remote found for %s", remote_id); + return; + } + fu_release_set_remote(release, remote); +} + +static gboolean +fu_engine_load_release(FuEngine *self, + FuRelease *release, + XbNode *component, + XbNode *rel, + FwupdInstallFlags install_flags, + GError **error) +{ + /* load release from XML */ + fu_release_set_config(release, self->config); + + /* set the FwupdRemote when the remote ID is set */ + g_signal_connect(FU_RELEASE(release), + "notify::remote-id", + G_CALLBACK(fu_engine_release_remote_id_changed_cb), + self); + + /* requirements we can check without the daemon */ + if (!fu_release_load(release, component, rel, install_flags, error)) + return FALSE; + + /* additional requirements */ + if (!fu_engine_check_requirements(self, release, install_flags, error)) + return FALSE; + + /* add any client-side BKC tags */ + if (!fu_engine_add_local_release_metadata(self, release, error)) + return FALSE; + + /* success */ + return TRUE; +} + +/* 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) +{ + g_autofree gchar *xpath = NULL; + g_autoptr(XbNode) key = NULL; + xpath = g_strdup_printf("components/component[@type='firmware']/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); +} + +/** + * fu_engine_unlock: + * @self: a #FuEngine + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Unlocks a device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_unlock(FuEngine *self, const gchar *device_id, GError **error) +{ + 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); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* check the device exists */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return FALSE; + + /* get the plugin */ + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin == NULL) + return FALSE; + + /* run the correct plugin that added this */ + if (!fu_plugin_runner_unlock(plugin, device, error)) + return FALSE; + + /* make the UI update */ + fu_engine_emit_device_changed_safe(self, device); + fu_engine_emit_changed(self); + return TRUE; +} + +gboolean +fu_engine_modify_config(FuEngine *self, const gchar *key, const gchar *value, GError **error) +{ + const gchar *keys[] = {"ArchiveSizeMax", + "ApprovedFirmware", + "BlockedFirmware", + "DisabledDevices", + "DisabledPlugins", + "EnumerateAllDevices", + "HostBkc", + "IdleTimeout", + "IgnorePower", + "OnlyTrusted", + "UpdateMotd", + "UriSchemes", + "VerboseDomains", + 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); + + /* 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; + } + + /* modify, effective next reboot */ + return fu_config_set_key_value(self->config, key, value, error); +} + +/** + * fu_engine_modify_remote: + * @self: a #FuEngine + * @remote_id: a remote ID + * @key: the key, e.g. `Enabled` + * @value: the key, e.g. `true` + * @error: (nullable): optional return location for an error + * + * Updates the verification silo entry for a specific device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_modify_remote(FuEngine *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GError **error) +{ + const gchar *keys[] = { + "ApprovalRequired", + "AutomaticReports", + "AutomaticSecurityReports", + "Enabled", + "FirmwareBaseURI", + "MetadataURI", + "ReportURI", + "SecurityReportURI", + 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 fu_remote_list_set_key_value(self->remote_list, remote_id, key, value, error); +} + +static gboolean +fu_engine_update_bios_setting(FwupdBiosSetting *attr, + const gchar *value, + gboolean force_ro, + GError **error) +{ + int fd; + g_autofree gchar *fn = + g_build_filename(fwupd_bios_setting_get_path(attr), "current_value", NULL); + g_autoptr(FuIOChannel) io = NULL; + + if (force_ro) + fwupd_bios_setting_set_read_only(attr, TRUE); + + if (g_strcmp0(fwupd_bios_setting_get_current_value(attr), value) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "%s is already set to %s", + fwupd_bios_setting_get_id(attr), + value); + return FALSE; + } + + fd = open(fn, O_WRONLY); + if (fd < 0) { + g_set_error(error, + G_IO_ERROR, +#ifdef HAVE_ERRNO_H + g_io_error_from_errno(errno), +#else + G_IO_ERROR_FAILED, +#endif + "could not open %s: %s", + fn, + g_strerror(errno)); + return FALSE; + } + io = fu_io_channel_unix_new(fd); + if (!fu_io_channel_write_raw(io, + (const guint8 *)value, + strlen(value), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + fwupd_bios_setting_set_current_value(attr, value); + + g_debug("set %s to %s", fwupd_bios_setting_get_id(attr), value); + + return TRUE; +} + +/* + * This is also done by the kernel or firmware, doing it here too allows for cleaner + * error messages + */ +static gboolean +fu_engine_validate_bios_setting_input(FwupdBiosSetting *attr, const gchar **value, GError **error) +{ + guint64 tmp = 0; + + g_return_val_if_fail(*value != NULL, FALSE); + g_return_val_if_fail(value != NULL, FALSE); + + if (attr == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "attribute not found"); + return FALSE; + } + if (fwupd_bios_setting_get_read_only(attr)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is read only", + fwupd_bios_setting_get_name(attr)); + return FALSE; + } else if (fwupd_bios_setting_get_kind(attr) == FWUPD_BIOS_SETTING_KIND_INTEGER) { + if (!fu_strtoull(*value, &tmp, 0, G_MAXUINT64, error)) + return FALSE; + if (tmp < fwupd_bios_setting_get_lower_bound(attr)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is too small (%" G_GUINT64_FORMAT + "); expected at least %" G_GUINT64_FORMAT, + *value, + tmp, + fwupd_bios_setting_get_lower_bound(attr)); + return FALSE; + } + if (tmp > fwupd_bios_setting_get_upper_bound(attr)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is too big (%" G_GUINT64_FORMAT + "); expected no more than %" G_GUINT64_FORMAT, + *value, + tmp, + fwupd_bios_setting_get_upper_bound(attr)); + return FALSE; + } + } else if (fwupd_bios_setting_get_kind(attr) == FWUPD_BIOS_SETTING_KIND_STRING) { + tmp = strlen(*value); + if (tmp < fwupd_bios_setting_get_lower_bound(attr)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is too short (%" G_GUINT64_FORMAT + "); expected at least %" G_GUINT64_FORMAT, + *value, + tmp, + fwupd_bios_setting_get_lower_bound(attr)); + return FALSE; + } + if (tmp > fwupd_bios_setting_get_upper_bound(attr)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is too long (%" G_GUINT64_FORMAT + "); expected no more than %" G_GUINT64_FORMAT, + *value, + tmp, + fwupd_bios_setting_get_upper_bound(attr)); + return FALSE; + } + } else if (fwupd_bios_setting_get_kind(attr) == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + const gchar *result = fwupd_bios_setting_map_possible_value(attr, *value, error); + if (result == NULL) + return FALSE; + *value = result; + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unknown attribute type"); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_engine_modify_single_bios_setting(FuEngine *self, + const gchar *key, + const gchar *value, + gboolean force_ro, + GError **error) +{ + FwupdBiosSetting *attr = fu_context_get_bios_setting(self->ctx, key); + const gchar *tmp = value; + + if (!fu_engine_validate_bios_setting_input(attr, &tmp, error)) + return FALSE; + + return fu_engine_update_bios_setting(attr, tmp, force_ro, error); +} + +/** + * fu_engine_modify_bios_settings: + * @self: a #FuEngine + * @settings: Hashtable of settings/values to configure + * @force_ro: a #gboolean indicating if BIOS settings should also be made read-only + * @error: (nullable): optional return location for an error + * + * Use the kernel API to set one or more BIOS settings. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_modify_bios_settings(FuEngine *self, + GHashTable *settings, + gboolean force_ro, + GError **error) +{ + g_autoptr(FuBiosSettings) bios_settings = fu_context_get_bios_settings(self->ctx); + gboolean changed = FALSE; + GHashTableIter iter; + gpointer key, value; + + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(settings != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_hash_table_iter_init(&iter, settings); + while (g_hash_table_iter_next(&iter, &key, &value)) { + g_autoptr(GError) error_local = NULL; + if (value == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "attribute %s missing value", + (const gchar *)key); + return FALSE; + } + if (!fu_engine_modify_single_bios_setting(self, + key, + value, + force_ro, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("%s", error_local->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + changed = TRUE; + } + + if (!changed) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "no BIOS settings needed to be changed"); + return FALSE; + } + + if (!fu_bios_settings_get_pending_reboot(bios_settings, &changed, error)) + return FALSE; + g_debug("pending_reboot is now %d", changed); + return TRUE; +} + +/** + * fu_engine_modify_device: + * @self: a #FuEngine + * @device_id: a device ID + * @key: the key, e.g. `Flags` + * @value: the key, e.g. `reported` + * @error: (nullable): optional return location for an error + * + * Sets the reported flag for a specific device. This ensures that other + * front-end clients for fwupd do not report the same event. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_modify_device(FuEngine *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GError **error) +{ + g_autoptr(FuDevice) device = NULL; + + /* find the correct device */ + device = fu_history_get_device_by_id(self->history, device_id, error); + if (device == NULL) + return FALSE; + + /* support adding a subset of device flags */ + if (g_strcmp0(key, "Flags") == 0) { + FwupdDeviceFlags flag = fwupd_device_flag_from_string(value); + if (flag == FWUPD_DEVICE_FLAG_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "key %s not a valid flag", + key); + return FALSE; + } + if (flag != FWUPD_DEVICE_FLAG_REPORTED && flag != FWUPD_DEVICE_FLAG_NOTIFIED) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flag %s cannot be set from client", + key); + return FALSE; + } + fu_device_add_flag(device, flag); + return fu_history_modify_device(self->history, device, error); + } + + /* others invalid */ + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "key %s not supported", key); + 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: (nullable): optional return location for an error + * + * Updates the verification silo entry for a specific device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_verify_update(FuEngine *self, + const gchar *device_id, + FuProgress *progress, + GError **error) +{ + FuPlugin *plugin; + GPtrArray *checksums; + 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); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* check the devices still exists */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return FALSE; + + /* get the plugin */ + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin == NULL) + return FALSE; + + /* get the checksum */ + checksums = fu_device_get_checksums(device); + if (checksums->len == 0) { + if (!fu_plugin_runner_verify(plugin, + device, + progress, + FU_PLUGIN_VERIFY_FLAG_NONE, + error)) + return FALSE; + fu_engine_emit_device_changed_safe(self, device); + } + + /* we got nothing */ + if (checksums->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device verification not supported"); + return FALSE; + } + + /* 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_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf("%s/verify/%s.xml", localstatedir, device_id); + if (!fu_path_mkdir_parent(fn, error)) + return FALSE; + file = g_file_new_for_path(fn); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + if (!xb_silo_export_file(silo, file, XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE, NULL, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static XbNode * +fu_engine_get_component_by_guid(FuEngine *self, const gchar *guid) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(XbNode) component = NULL; +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); +#endif + + /* no components in silo */ + if (self->query_component_by_guid == NULL) + return NULL; + +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + xb_query_context_set_flags(&context, XB_QUERY_FLAG_USE_INDEXES); + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL); + component = xb_silo_query_first_with_context(self->silo, + self->query_component_by_guid, + &context, + &error_local); +#else + if (!xb_query_bind_str(self->query_component_by_guid, 0, guid, &error_local)) { + g_warning("failed to bind 0: %s", error_local->message); + return NULL; + } + component = + xb_silo_query_first_full(self->silo, self->query_component_by_guid, &error_local); +#endif + if (component == 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_warning("ignoring: %s", error_local->message); + return NULL; + } + return g_object_ref(component); +} + +XbNode * +fu_engine_get_component_by_guids(FuEngine *self, FuDevice *device) +{ + GPtrArray *guids = fu_device_get_guids(device); + XbNode *component = NULL; + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + component = fu_engine_get_component_by_guid(self, guid); + if (component != NULL) + break; + } + return component; +} + +static XbNode * +fu_engine_verify_from_local_metadata(FuEngine *self, FuDevice *device, GError **error) +{ + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *xpath = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbNode) release = NULL; + g_autoptr(XbSilo) silo = NULL; + + localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf("%s/verify/%s.xml", localstatedir, fu_device_get_id(device)); + file = g_file_new_for_path(fn); + if (!g_file_query_exists(file, NULL)) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "failed to find %s", fn); + return NULL; + } + + if (!xb_builder_source_load_file(source, file, XB_BUILDER_SOURCE_FLAG_NONE, NULL, error)) + return NULL; + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return NULL; + xpath = g_strdup_printf("component/releases/release[@version='%s']", + fu_device_get_version(device)); + release = xb_silo_query_first(silo, xpath, error); + if (release == NULL) + return NULL; + + /* silo has to have same lifecycle as node */ + g_object_set_data_full(G_OBJECT(release), + "XbSilo", + g_steal_pointer(&silo), + (GDestroyNotify)g_object_unref); + return g_steal_pointer(&release); +} + +static XbNode * +fu_engine_verify_from_system_metadata(FuEngine *self, FuDevice *device, GError **error) +{ + FwupdVersionFormat fmt = fu_device_get_version_format(device); + GPtrArray *guids = fu_device_get_guids(device); + g_autoptr(XbQuery) query = NULL; + + /* prepare query with bound GUID parameter */ + query = xb_query_new_full(self->silo, + "components/component[@type='firmware']/" + "provides/firmware[@type='flashed'][text()=?]/" + "../../releases/release", + XB_QUERY_FLAG_OPTIMIZE | XB_QUERY_FLAG_USE_INDEXES, + error); + if (query == NULL) + return NULL; + + /* use prepared query for each GUID */ + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) releases = NULL; +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); +#endif + + /* bind GUID and then query */ +#if LIBXMLB_CHECK_VERSION(0, 3, 0) + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL); + releases = xb_silo_query_with_context(self->silo, query, &context, &error_local); +#else + if (!xb_query_bind_str(query, 0, guid, error)) { + g_prefix_error(error, "failed to bind string: "); + return NULL; + } + releases = xb_silo_query_full(self->silo, query, &error_local); +#endif + if (releases == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_debug("could not find %s: %s", guid, error_local->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return NULL; + } + 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_version_parse_from_format(rel_ver, fmt); + if (fu_version_compare(tmp_ver, fu_device_get_version(device), fmt) == 0) + return g_object_ref(rel); + } + } + + /* not found */ + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "failed to find release"); + return NULL; +} + +/** + * fu_engine_verify: + * @self: a #FuEngine + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Verifies a device firmware checksum using the verification silo entry. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_verify(FuEngine *self, const gchar *device_id, FuProgress *progress, GError **error) +{ + FuPlugin *plugin; + GPtrArray *checksums; + g_autoptr(FuDevice) device = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GString) xpath_csum = g_string_new(NULL); + g_autoptr(XbNode) csum = NULL; + g_autoptr(XbNode) release = NULL; + + 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); + + /* check the id exists */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return FALSE; + + /* get the plugin */ + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin == NULL) + return FALSE; + + /* update the device firmware hashes if possible */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { + if (!fu_plugin_runner_verify(plugin, + device, + progress, + FU_PLUGIN_VERIFY_FLAG_NONE, + error)) + return FALSE; + } + + /* find component in local metadata */ + release = fu_engine_verify_from_local_metadata(self, device, &error_local); + if (release == 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_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + /* try again with the system metadata */ + if (release == NULL) { + g_autoptr(GError) error_system = NULL; + release = fu_engine_verify_from_system_metadata(self, device, &error_system); + if (release == NULL) { + if (!g_error_matches(error_system, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches(error_system, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error(error, g_steal_pointer(&error_system)); + return FALSE; + } + } + } + if (release == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No release found for version %s", + fu_device_get_version(device)); + return FALSE; + } + + /* get the matching checksum */ + checksums = fu_device_get_checksums(device); + if (checksums->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No device checksums for %s", + fu_device_get_version(device)); + 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); + xb_string_append_union(xpath_csum, + "checksum[@target='device'][text()='%s']", + hash_tmp); + xb_string_append_union(xpath_csum, + "checksum[@target='content'][text()='%s']", + hash_tmp); + } + csum = xb_node_query_first(release, xpath_csum->str, NULL); + if (csum == NULL) { + g_autoptr(GString) checksums_device = g_string_new(NULL); + g_autoptr(GString) checksums_metadata = g_string_new(NULL); + g_autoptr(GPtrArray) csums = NULL; + g_autoptr(GString) xpath = g_string_new(NULL); + + /* get all checksums to display a useful error */ + xb_string_append_union(xpath, "checksum[@target='device']"); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) + 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 stored checksums for %s", + fu_device_get_version(device)); + 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 %s %s expected %s, got %s", + fu_device_get_name(device), + fu_device_get_version(device), + checksums_metadata->str, + checksums_device->str); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_require_vercmp(XbNode *req, const gchar *version, FwupdVersionFormat fmt, GError **error) +{ + gboolean ret = FALSE; + gint rc = 0; + const gchar *tmp = xb_node_get_attr(req, "compare"); + const gchar *version_req = xb_node_get_attr(req, "version"); + + if (g_strcmp0(tmp, "eq") == 0) { + rc = fu_version_compare(version, version_req, fmt); + ret = rc == 0; + } else if (g_strcmp0(tmp, "ne") == 0) { + rc = fu_version_compare(version, version_req, fmt); + ret = rc != 0; + } else if (g_strcmp0(tmp, "lt") == 0) { + rc = fu_version_compare(version, version_req, fmt); + ret = rc < 0; + } else if (g_strcmp0(tmp, "gt") == 0) { + rc = fu_version_compare(version, version_req, fmt); + ret = rc > 0; + } else if (g_strcmp0(tmp, "le") == 0) { + rc = fu_version_compare(version, version_req, fmt); + ret = rc <= 0; + } else if (g_strcmp0(tmp, "ge") == 0) { + rc = fu_version_compare(version, version_req, fmt); + ret = rc >= 0; + } else if (g_strcmp0(tmp, "glob") == 0) { + ret = fu_path_fnmatch(version_req, version); + } 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 (version == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no version provided by %s, child of %s", + fu_device_get_name(child), + fu_device_get_name(device)); + return FALSE; + } + if (fu_engine_require_vercmp(req, + version, + fu_device_get_version_format(child), + NULL)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not compatible with child device version %s", + version); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_engine_check_requirement_vendor_id(FuEngine *self, XbNode *req, FuDevice *device, GError **error) +{ + GPtrArray *vendor_ids; + const gchar *vendor_ids_metadata; + g_autofree gchar *vendor_ids_device = NULL; + + /* devices without vendor IDs should not exist! */ + vendor_ids = fu_device_get_vendor_ids(device); + if (vendor_ids->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device [%s] has no vendor ID", + fu_device_get_id(device)); + return FALSE; + } + + /* metadata with empty vendor IDs should not exist! */ + vendor_ids_metadata = xb_node_get_attr(req, "version"); + if (vendor_ids_metadata == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "metadata has no vendor ID"); + return FALSE; + } + + /* it is always safe to use a regex, even for simple strings */ + vendor_ids_device = fu_strjoin("|", vendor_ids); + if (!g_regex_match_simple(vendor_ids_metadata, vendor_ids_device, 0, 0)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with vendor %s: got %s", + vendor_ids_device, + vendor_ids_metadata); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_check_requirement_firmware(FuEngine *self, + XbNode *req, + FuDevice *device, + FwupdInstallFlags flags, + GError **error) +{ + guint64 depth; + g_autoptr(FuDevice) device_actual = g_object_ref(device); + g_autoptr(GError) error_local = NULL; + + /* look at the parent device */ + depth = xb_node_get_attr_as_uint(req, "depth"); + if (depth != G_MAXUINT64) { + for (guint64 i = 0; i < depth; i++) { + FuDevice *device_tmp = fu_device_get_parent(device_actual); + if (device_tmp == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No parent device for %s " + "(%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ")", + fu_device_get_name(device_actual), + i, + depth); + return FALSE; + } + g_set_object(&device_actual, device_tmp); + } + } + + /* old firmware version */ + if (xb_node_get_text(req) == NULL) { + const gchar *version = fu_device_get_version(device_actual); + if (!fu_engine_require_vercmp(req, + version, + fu_device_get_version_format(device_actual), + &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, + xb_node_get_attr(req, "version")); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with firmware version: %s", + error_local->message); + } + return FALSE; + } + return TRUE; + } + + /* bootloader version */ + if (g_strcmp0(xb_node_get_text(req), "bootloader") == 0) { + const gchar *version = fu_device_get_version_bootloader(device_actual); + if (!fu_engine_require_vercmp(req, + version, + fu_device_get_version_format(device_actual), + &error_local)) { + if (g_strcmp0(xb_node_get_attr(req, "compare"), "ge") == 0) { + g_set_error( + error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not compatible with bootloader version %s, requires >= %s", + version, + xb_node_get_attr(req, "version")); + + } else { + g_debug("Bootloader is not compatible: %s", error_local->message); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Bootloader is not compatible"); + } + return FALSE; + } + return TRUE; + } + + /* vendor ID */ + if (g_strcmp0(xb_node_get_text(req), "vendor-id") == 0) { + if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) + return TRUE; + return fu_engine_check_requirement_vendor_id(self, req, device_actual, error); + } + + /* child version */ + if (g_strcmp0(xb_node_get_text(req), "not-child") == 0) + return fu_engine_check_requirement_not_child(self, req, device_actual, error); + + /* another device */ + if (fwupd_guid_is_valid(xb_node_get_text(req))) { + const gchar *guid = xb_node_get_text(req); + const gchar *version; + + /* find if the other device exists */ + if (depth == G_MAXUINT64) { + g_autoptr(FuDevice) device_tmp = NULL; + device_tmp = fu_device_list_get_by_guid(self->device_list, guid, error); + if (device_tmp == NULL) + return FALSE; + g_set_object(&device_actual, device_tmp); + /* look for a sibling */ + } else if (depth == 0) { + FuDevice *child = NULL; + FuDevice *parent = fu_device_get_parent(device_actual); + GPtrArray *children; + if (parent == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No parent specified for device %s", + fu_device_get_name(device_actual)); + return FALSE; + } + children = fu_device_get_children(parent); + for (guint i = 0; i < children->len; i++) { + child = g_ptr_array_index(children, i); + if (fu_device_has_guid(child, guid)) + break; + child = NULL; + } + if (child == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No sibling found with GUID of %s", + guid); + return FALSE; + } + g_set_object(&device_actual, child); + /* verify the parent device has the GUID */ + } else { + if (!fu_device_has_guid(device_actual, guid)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No GUID of %s on parent device %s", + guid, + fu_device_get_name(device_actual)); + return FALSE; + } + } + + /* get the version of the other device */ + version = fu_device_get_version(device_actual); + if (version != NULL && xb_node_get_attr(req, "compare") != NULL && + !fu_engine_require_vercmp(req, + version, + fu_device_get_version_format(device_actual), + &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(device_actual), + version, + xb_node_get_attr(req, "version")); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with %s: %s", + fu_device_get_name(device_actual), + error_local->message); + } + return FALSE; + } + return TRUE; + } + + /* not supported */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot handle firmware requirement '%s'", + xb_node_get_text(req)); + return FALSE; +} + +static gboolean +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, xb_node_get_text(req)); + if (version == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no version available for %s", + xb_node_get_text(req)); + return FALSE; + } + if (!fu_engine_require_vercmp(req, version, FWUPD_VERSION_FORMAT_UNKNOWN, &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", + xb_node_get_text(req), + version, + xb_node_get_attr(req, "version")); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with %s version: %s", + xb_node_get_text(req), + error_local->message); + } + return FALSE; + } + + g_debug("requirement %s %s %s -> %s passed", + 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, XbNode *req, GError **error) +{ + g_auto(GStrv) hwid_split = NULL; + + /* split and treat as OR */ + hwid_split = g_strsplit(xb_node_get_text(req), "|", -1); + for (guint i = 0; hwid_split[i] != NULL; i++) { + if (fu_context_has_hwid_guid(self->ctx, hwid_split[i])) { + g_debug("HWID provided %s", hwid_split[i]); + return TRUE; + } + } + + /* nothing matched */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no HWIDs matched %s", + xb_node_get_text(req)); + return FALSE; +} + +static gboolean +fu_engine_check_requirement_client(FuEngine *self, + FuEngineRequest *request, + XbNode *req, + GError **error) +{ + FwupdFeatureFlags flags; + g_auto(GStrv) feature_split = NULL; + + /* split and treat as AND */ + feature_split = g_strsplit(xb_node_get_text(req), "|", -1); + flags = fu_engine_request_get_feature_flags(request); + for (guint i = 0; feature_split[i] != NULL; i++) { + FwupdFeatureFlags flag = fwupd_feature_flag_from_string(feature_split[i]); + + /* not recognized */ + if (flag == FWUPD_FEATURE_FLAG_LAST) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "client requirement %s unknown", + feature_split[i]); + return FALSE; + } + + /* not supported */ + if ((flags & flag) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "client requirement %s not supported", + feature_split[i]); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_check_requirement(FuEngine *self, + FuRelease *release, + XbNode *req, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *device = fu_release_get_device(release); + FuEngineRequest *request = fu_release_get_request(release); + + /* ensure component requirement */ + if (g_strcmp0(xb_node_get_element(req), "id") == 0) + return fu_engine_check_requirement_id(self, req, error); + + /* ensure firmware requirement */ + if (g_strcmp0(xb_node_get_element(req), "firmware") == 0) { + if (device == NULL) + return TRUE; + return fu_engine_check_requirement_firmware(self, req, device, flags, error); + } + + /* ensure hardware requirement */ + if (g_strcmp0(xb_node_get_element(req), "hardware") == 0) + return fu_engine_check_requirement_hardware(self, req, error); + + /* ensure client requirement */ + if (g_strcmp0(xb_node_get_element(req), "client") == 0) + return fu_engine_check_requirement_client(self, request, req, error); + + /* not supported */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot handle requirement type %s", + xb_node_get_element(req)); + return FALSE; +} + +gboolean +fu_engine_check_trust(FuEngine *self, FuRelease *release, GError **error) +{ + if (fu_config_get_only_trusted(self->config) && + (fu_release_get_trust_flags(release) & FWUPD_TRUST_FLAG_PAYLOAD) == 0) { + g_autofree gchar *sysconfdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + g_autofree gchar *fn = g_build_filename(sysconfdir, "daemon.conf", NULL); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware signature missing or not trusted; " + "set OnlyTrusted=false in %s ONLY if you are a firmware developer", + fn); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_engine_check_soft_requirement(FuEngine *self, + FuRelease *release, + XbNode *req, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + if (!fu_engine_check_requirement(self, release, req, flags, &error_local)) { + if (flags & FWUPD_INSTALL_FLAG_FORCE) { + g_debug("ignoring soft-requirement due to --force: %s", + error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + return TRUE; +} + +gboolean +fu_engine_check_requirements(FuEngine *self, + FuRelease *release, + FwupdInstallFlags flags, + GError **error) +{ + GPtrArray *reqs; + + /* hard requirements */ + reqs = fu_release_get_hard_reqs(release); + if (reqs != NULL) { + for (guint i = 0; i < reqs->len; i++) { + XbNode *req = g_ptr_array_index(reqs, i); + if (!fu_engine_check_requirement(self, release, req, flags, error)) + return FALSE; + } + } + + /* soft requirements */ + reqs = fu_release_get_soft_reqs(release); + if (reqs != NULL) { + for (guint i = 0; i < reqs->len; i++) { + XbNode *req = g_ptr_array_index(reqs, i); + if (!fu_engine_check_soft_requirement(self, release, req, flags, error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +void +fu_engine_idle_reset(FuEngine *self) +{ + fu_idle_reset(self->idle); +} + +static gchar * +fu_engine_get_boot_time(void) +{ + g_autofree gchar *buf = NULL; + g_auto(GStrv) lines = NULL; + if (!g_file_get_contents("/proc/stat", &buf, NULL, NULL)) + return NULL; + lines = g_strsplit(buf, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + if (g_str_has_prefix(lines[i], "btime ")) + return g_strdup(lines[i] + 6); + } + return NULL; +} + +static gboolean +fu_engine_get_report_metadata_os_release(GHashTable *hash, GError **error) +{ + g_autoptr(GHashTable) os_release = NULL; + struct { + const gchar *key; + const gchar *val; + } distro_kv[] = {{"ID", "DistroId"}, + {"VERSION_ID", "DistroVersion"}, + {"VARIANT_ID", "DistroVariant"}, + {NULL, NULL}}; + + /* get all required os-release keys */ + os_release = fwupd_get_os_release(error); + if (os_release == NULL) + return FALSE; + for (guint i = 0; distro_kv[i].key != NULL; i++) { + const gchar *tmp = g_hash_table_lookup(os_release, distro_kv[i].key); + if (tmp != NULL) { + g_hash_table_insert(hash, g_strdup(distro_kv[i].val), g_strdup(tmp)); + } + } + return TRUE; +} + +#ifdef __linux__ +static gchar * +fu_engine_get_proc_cmdline(GError **error) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + g_autoptr(GHashTable) hash = NULL; + g_autoptr(GString) cmdline_safe = g_string_new(NULL); + const gchar *ignore[] = { + "", + "auto", + "boot", + "BOOT_IMAGE", + "console", + "cryptdevice", + "cryptkey", + "earlycon", + "earlyprintk", + "ether", + "initrd", + "ip", + "LANG", + "loglevel", + "luks.key", + "luks.name", + "luks.options", + "luks.uuid", + "mount.usr", + "mount.usrflags", + "mount.usrfstype", + "netdev", + "netroot", + "nfsaddrs", + "nfs.nfs4_unique_id", + "nfsroot", + "noplymouth", + "ostree", + "quiet", + "rd.dm.uuid", + "rd.luks.allow-discards", + "rd.luks.key", + "rd.luks.name", + "rd.luks.options", + "rd.luks.uuid", + "rd.lvm.lv", + "rd.lvm.vg", + "rd.md.uuid", + "rd.systemd.mask", + "rd.systemd.wants", + "resume", + "resumeflags", + "rhgb", + "ro", + "root", + "rootflags", + "roothash", + "rw", + "showopts", + "splash", + "swap", + "systemd.mask", + "systemd.unit", + "systemd.verity_root_data", + "systemd.verity_root_hash", + "systemd.wants", + "verbose", + "vt.handoff", + "zfs", + NULL, /* last entry */ + }; + + /* get a PII-safe kernel command line */ + hash = fu_kernel_get_cmdline(error); + if (hash == NULL) + return NULL; + for (guint i = 0; ignore[i] != NULL; i++) + g_hash_table_remove(hash, ignore[i]); + g_hash_table_iter_init(&iter, hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (cmdline_safe->len > 0) + g_string_append(cmdline_safe, " "); + g_string_append_printf(cmdline_safe, "%s=%s", (gchar *)key, (gchar *)value); + } + + return g_string_free(g_steal_pointer(&cmdline_safe), FALSE); +} +#endif + +static gboolean +fu_engine_get_report_metadata_kernel_cmdline(GHashTable *hash, GError **error) +{ + g_autofree gchar *cmdline = NULL; + +#ifdef __linux__ + /* Linuxish */ + cmdline = fu_engine_get_proc_cmdline(error); +#elif defined(__FreeBSD__) + /* BSDish */ + cmdline = fu_kenv_get_string("kernel_options", error); +#elif defined(_WIN32) + /* Windows */ + cmdline = g_strdup(""); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "cannot get KernelCmdline on this OS"); +#endif + if (cmdline == NULL) + return FALSE; + if (cmdline[0] != '\0') { + g_hash_table_insert(hash, g_strdup("KernelCmdline"), g_steal_pointer(&cmdline)); + } + return TRUE; +} + +static void +fu_engine_add_report_metadata_bool(GHashTable *hash, const gchar *key, gboolean value) +{ + g_hash_table_insert(hash, g_strdup(key), g_strdup(value ? "True" : "False")); +} + +GHashTable * +fu_engine_get_report_metadata(FuEngine *self, GError **error) +{ + const gchar *tmp; + gchar *btime; +#ifdef HAVE_UTSNAME_H + struct utsname name_tmp; +#endif + g_autoptr(GHashTable) hash = NULL; + g_autoptr(GList) compile_keys = g_hash_table_get_keys(self->compile_versions); + g_autoptr(GList) runtime_keys = g_hash_table_get_keys(self->runtime_versions); + + /* convert all the runtime and compile-time versions */ + hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + for (GList *l = compile_keys; l != NULL; l = l->next) { + const gchar *id = l->data; + const gchar *version = g_hash_table_lookup(self->compile_versions, id); + g_hash_table_insert(hash, + g_strdup_printf("CompileVersion(%s)", id), + g_strdup(version)); + } + for (GList *l = runtime_keys; l != NULL; l = l->next) { + const gchar *id = l->data; + const gchar *version = g_hash_table_lookup(self->runtime_versions, id); + g_hash_table_insert(hash, + g_strdup_printf("RuntimeVersion(%s)", id), + g_strdup(version)); + } + if (!fu_engine_get_report_metadata_os_release(hash, error)) + return NULL; + if (!fu_engine_get_report_metadata_kernel_cmdline(hash, error)) + return NULL; + + /* these affect the report credibility */ +#ifdef SUPPORTED_BUILD + fu_engine_add_report_metadata_bool(hash, "FwupdSupported", TRUE); +#else + fu_engine_add_report_metadata_bool(hash, "FwupdSupported", FALSE); +#endif + + /* find out what BKC is being targeted to understand "odd" upgrade paths */ + tmp = fu_config_get_host_bkc(self->config); + if (tmp != NULL) + g_hash_table_insert(hash, g_strdup("HostBkc"), g_strdup(tmp)); + + /* DMI data */ + tmp = fu_context_get_hwid_value(self->ctx, FU_HWIDS_KEY_PRODUCT_NAME); + if (tmp != NULL) + g_hash_table_insert(hash, g_strdup("HostProduct"), g_strdup(tmp)); + tmp = fu_context_get_hwid_value(self->ctx, FU_HWIDS_KEY_FAMILY); + if (tmp != NULL) + g_hash_table_insert(hash, g_strdup("HostFamily"), g_strdup(tmp)); + tmp = fu_context_get_hwid_value(self->ctx, FU_HWIDS_KEY_PRODUCT_SKU); + if (tmp != NULL) + g_hash_table_insert(hash, g_strdup("HostSku"), g_strdup(tmp)); + tmp = fu_context_get_hwid_value(self->ctx, FU_HWIDS_KEY_MANUFACTURER); + if (tmp != NULL) + g_hash_table_insert(hash, g_strdup("HostVendor"), g_strdup(tmp)); + + /* kernel version is often important for debugging failures */ +#ifdef HAVE_UTSNAME_H + memset(&name_tmp, 0, sizeof(struct utsname)); + if (uname(&name_tmp) >= 0) { + g_hash_table_insert(hash, g_strdup("CpuArchitecture"), g_strdup(name_tmp.machine)); + g_hash_table_insert(hash, g_strdup("KernelName"), g_strdup(name_tmp.sysname)); + g_hash_table_insert(hash, g_strdup("KernelVersion"), g_strdup(name_tmp.release)); + } +#endif + + /* add the kernel boot time so we can detect a reboot */ + btime = fu_engine_get_boot_time(); + if (btime != NULL) + g_hash_table_insert(hash, g_strdup("BootTime"), btime); + + return g_steal_pointer(&hash); +} + +/** + * fu_engine_composite_prepare: + * @self: a #FuEngine + * @devices: (element-type #FuDevice): devices that will be updated + * @error: (nullable): optional return location for an error + * + * Calls into the plugin loader, informing each plugin of the pending upgrade(s). + * + * Any failure in any plugin will abort all of the actions before they are started. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_composite_prepare(FuEngine *self, GPtrArray *devices, GError **error) +{ + 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); + if (!fu_plugin_runner_composite_prepare(plugin_tmp, devices, error)) + return FALSE; + } + return TRUE; +} + +/** + * fu_engine_composite_cleanup: + * @self: a #FuEngine + * @devices: (element-type #FuDevice): devices that will be updated + * @error: (nullable): optional return location for an error + * + * Calls into the plugin loader, informing each plugin of the pending upgrade(s). + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_composite_cleanup(FuEngine *self, GPtrArray *devices, GError **error) +{ + 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); + if (!fu_plugin_runner_composite_cleanup(plugin_tmp, devices, error)) + return FALSE; + } + return TRUE; +} + +static gint +fu_engine_sort_release_versions_cb(gconstpointer a, gconstpointer b) +{ + FuRelease *na = *((FuRelease **)a); + FuRelease *nb = *((FuRelease **)b); + FuDevice *device = fu_release_get_device(na); + const gchar *va = fu_release_get_version(na); + const gchar *vb = fu_release_get_version(nb); + return fu_version_compare(va, vb, fu_device_get_version_format(device)); +} + +/** + * fu_engine_install_releases: + * @self: a #FuEngine + * @request: a #FuEngineRequest + * @releases: (element-type FuRelease): a device + * @blob_cab: the #GBytes of the .cab file + * @flags: install flags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE + * @error: (nullable): optional return location for an error + * + * Installs a specific firmware file on one or more install tasks. + * + * By this point all the requirements and tests should have been done in + * fu_engine_check_requirements() so this should not fail before running + * the plugin loader. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_install_releases(FuEngine *self, + FuEngineRequest *request, + GPtrArray *releases, + GBytes *blob_cab, + FuProgress *progress, + 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, "update"); + g_assert(locker != NULL); + + /* install these in the right order */ + g_ptr_array_sort(releases, fu_engine_sort_release_versions_cb); + + /* notify the plugins about the composite action */ + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < releases->len; i++) { + FuRelease *release = g_ptr_array_index(releases, i); + g_debug("composite update %u: %s", + i + 1, + fu_device_get_id(fu_release_get_device(release))); + g_ptr_array_add(devices, g_object_ref(fu_release_get_device(release))); + } + if (!fu_engine_composite_prepare(self, devices, error)) { + g_prefix_error(error, "failed to prepare composite action: "); + return FALSE; + } + + /* all authenticated, so install all the things */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, releases->len); + for (guint i = 0; i < releases->len; i++) { + FuRelease *release = g_ptr_array_index(releases, i); + if (!fu_engine_install_release(self, + release, + blob_cab, + fu_progress_get_child(progress), + flags, + error)) { + g_autoptr(GError) error_local = NULL; + if (!fu_engine_composite_cleanup(self, devices, &error_local)) { + g_warning("failed to cleanup failed composite action: %s", + error_local->message); + } + return FALSE; + } + fu_progress_step_done(progress); + } + + /* set all the device statuses back to unknown */ + for (guint i = 0; i < releases->len; i++) { + FuRelease *release = g_ptr_array_index(releases, i); + FuDevice *device = fu_release_get_device(release); + fwupd_device_set_status(FWUPD_DEVICE(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_new, error)) { + g_prefix_error(error, "failed to cleanup composite action: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_add_release_metadata(FuEngine *self, FuRelease *release, FuPlugin *plugin, GError **error) +{ + GPtrArray *metadata_sources; + g_autoptr(GHashTable) metadata_device = NULL; + g_autoptr(GHashTable) metadata_hash = NULL; + + /* build the version metadata */ + metadata_hash = fu_engine_get_report_metadata(self, error); + if (metadata_hash == NULL) + return FALSE; + fu_release_add_metadata(release, metadata_hash); + if (fu_plugin_get_report_metadata(plugin) != NULL) + fu_release_add_metadata(release, fu_plugin_get_report_metadata(plugin)); + metadata_device = fu_device_report_metadata_pre(fu_release_get_device(release)); + if (metadata_device != NULL) + fu_release_add_metadata(release, metadata_device); + + /* allow other plugins to contribute metadata too */ + metadata_sources = fu_plugin_get_rules(plugin, FU_PLUGIN_RULE_METADATA_SOURCE); + if (metadata_sources != NULL) { + for (guint i = 0; i < metadata_sources->len; i++) { + FuPlugin *plugin_tmp; + const gchar *plugin_name = g_ptr_array_index(metadata_sources, i); + g_autoptr(GError) error_local = NULL; + + plugin_tmp = fu_plugin_list_find_by_name(self->plugin_list, + plugin_name, + &error_local); + if (plugin_tmp == NULL) { + g_warning("could not add metadata for %s: %s", + plugin_name, + error_local->message); + continue; + } + if (fu_plugin_get_report_metadata(plugin_tmp) != NULL) { + fwupd_release_add_metadata( + FWUPD_RELEASE(release), + fu_plugin_get_report_metadata(plugin_tmp)); + } + } + } + return TRUE; +} + +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 +} + +#ifdef HAVE_GIO_UNIX +static gchar * +fu_realpath(const gchar *filename, GError **error) +{ + char full_tmp[PATH_MAX]; + + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + +#ifdef HAVE_REALPATH + if (realpath(filename, full_tmp) == NULL) { +#else + if (_fullpath(full_tmp, filename, sizeof(full_tmp)) == NULL) { +#endif + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot resolve path: %s", + strerror(errno)); + return NULL; + } + if (!g_file_test(full_tmp, G_FILE_TEST_EXISTS)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "cannot find path: %s", + full_tmp); + return NULL; + } + return g_strdup(full_tmp); +} +#endif + +static gboolean +fu_engine_offline_setup(GError **error) +{ +#ifdef HAVE_GIO_UNIX + gint rc; + g_autofree gchar *filename = NULL; + g_autofree gchar *symlink_target = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + g_autofree gchar *trigger = fu_path_from_kind(FU_PATH_KIND_OFFLINE_TRIGGER); + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* does already exist */ + filename = fu_realpath(trigger, NULL); + if (g_strcmp0(filename, symlink_target) == 0) { + g_debug("%s already points to %s, skipping creation", trigger, symlink_target); + return TRUE; + } + + /* create symlink for the systemd-system-update-generator */ + rc = symlink(symlink_target, trigger); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to create symlink %s to %s: %s", + trigger, + symlink_target, + strerror(errno)); + return FALSE; + } + return TRUE; +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not available"); + return FALSE; +#endif +} + +static gboolean +fu_engine_offline_invalidate(GError **error) +{ + g_autofree gchar *trigger = fu_path_from_kind(FU_PATH_KIND_OFFLINE_TRIGGER); + g_autoptr(GError) error_local = NULL; + g_autoptr(GFile) file1 = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + file1 = g_file_new_for_path(trigger); + if (!g_file_query_exists(file1, NULL)) + return TRUE; + if (!g_file_delete(file1, NULL, &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Cannot delete %s: %s", + trigger, + error_local->message); + return FALSE; + } + return TRUE; +} + +/** + * fu_engine_schedule_update: + * @self: a #FuEngine + * @device: a device + * @release: a release + * @blob_cab: a data blob + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Schedule an offline update for the device + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.3.5 + **/ +gboolean +fu_engine_schedule_update(FuEngine *self, + FuDevice *device, + FwupdRelease *release, + GBytes *blob_cab, + FwupdInstallFlags flags, + GError **error) +{ + gchar tmpname[] = {"XXXXXX.cab"}; + g_autofree gchar *dirname = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(FuHistory) history = NULL; + g_autoptr(GFile) file = NULL; + +#ifndef HAVE_FWUPDOFFLINE + /* sanity check */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as compiled without offline support"); + return FALSE; +#endif + + /* id already exists */ + history = fu_history_new(); + 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 */ + dirname = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + file = g_file_new_for_path(dirname); + if (!g_file_query_exists(file, NULL)) { + if (!g_file_make_directory_with_parents(file, NULL, error)) + return FALSE; + } + + /* get a random filename */ + for (guint i = 0; i < 6; i++) + tmpname[i] = (gchar)g_random_int_range('A', 'Z'); + filename = g_build_filename(dirname, tmpname, NULL); + + /* just copy to the temp file */ + if (!g_file_set_contents(filename, + g_bytes_get_data(blob_cab, NULL), + (gssize)g_bytes_get_size(blob_cab), + error)) + return FALSE; + + /* schedule for next boot */ + g_debug("schedule %s to be installed to %s on next boot", + filename, + fu_device_get_id(device)); + 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, error)) + return FALSE; + + /* next boot we run offline */ + return fu_engine_offline_setup(error); +} + +/** + * fu_engine_install_release: + * @self: a #FuEngine + * @release: a #FuRelease + * @blob_cab: the #GBytes of the .cab file + * @progress: a #FuProgress + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_OLDER + * @error: (nullable): optional return location for an error + * + * Installs a specific release 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 + * the plugin loader. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_install_release(FuEngine *self, + FuRelease *release, + GBytes *blob_cab, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *device_orig = fu_release_get_device(release); + FuEngineRequest *request = fu_release_get_request(release); + FuPlugin *plugin; + FwupdFeatureFlags feature_flags = FWUPD_FEATURE_FLAG_NONE; + FwupdVersionFormat fmt; + GBytes *blob_fw; + const gchar *tmp; + const gchar *version_rel; + g_autofree gchar *version_orig = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevice) device_tmp = NULL; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(FU_IS_RELEASE(release), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(blob_cab != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optional for tests */ + if (request != NULL) + feature_flags = fu_engine_request_get_feature_flags(request); + + /* add the checksum of the container blob if not already set */ + if (fwupd_release_get_checksums(FWUPD_RELEASE(release))->len == 0) { + GChecksumType checksum_types[] = {G_CHECKSUM_SHA256, G_CHECKSUM_SHA1, 0}; + for (guint i = 0; checksum_types[i] != 0; i++) { + g_autofree gchar *checksum = + g_compute_checksum_for_bytes(checksum_types[i], blob_cab); + fwupd_release_add_checksum(FWUPD_RELEASE(release), checksum); + } + } + + /* not in bootloader mode */ + device = g_object_ref(fu_release_get_device(release)); + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + /* both optional; the plugin can specify a fallback */ + tmp = fwupd_release_get_detach_caption(FWUPD_RELEASE(release)); + if (tmp != NULL) + fu_device_set_update_message(device, tmp); + tmp = fwupd_release_get_detach_image(FWUPD_RELEASE(release)); + if (tmp != NULL) + fu_device_set_update_image(device, tmp); + } + + /* schedule this for the next reboot if not in system-update.target, + * but first check if allowed on battery power */ + if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 && !fu_engine_is_running_offline(self)) { + FuPlugin *plugin_tmp = + fu_plugin_list_find_by_name(self->plugin_list, "upower", NULL); + if (plugin_tmp != NULL) { + if (!fu_plugin_runner_prepare(plugin_tmp, device, progress, flags, error)) + return FALSE; + } + fu_progress_set_status(progress, FWUPD_STATUS_SCHEDULING); + if (!fu_engine_add_release_metadata(self, release, plugin_tmp, error)) + return FALSE; + return fu_engine_schedule_update(self, + device, + FWUPD_RELEASE(release), + blob_cab, + flags, + error); + } + + /* set this for the callback */ + self->write_history = (flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0; + + /* get per-release firmware blob */ + blob_fw = fu_release_get_fw_blob(release); + if (blob_fw == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to get firmware blob from release"); + return FALSE; + } + + /* get the plugin */ + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin == NULL) + return FALSE; + + /* add device to database */ + if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { + if (!fu_engine_add_release_metadata(self, release, plugin, error)) + return FALSE; + if (!fu_history_add_device(self->history, device, FWUPD_RELEASE(release), error)) + return FALSE; + } + + /* install firmware blob */ + version_orig = g_strdup(fu_device_get_version(device)); + if (!fu_engine_install_blob(self, + device, + blob_fw, + progress, + flags, + feature_flags, + &error_local)) { + 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_NEEDS_USER_ACTION) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_BROKEN_SYSTEM)) { + fu_device_set_update_state(device_orig, + FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + } else { + fu_device_set_update_state(device_orig, FWUPD_UPDATE_STATE_FAILED); + } + fu_device_set_update_error(device_orig, error_local->message); + 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 state (which updates the database if required) */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT) || + fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) { + fu_device_set_update_state(device_orig, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + return TRUE; + } + + /* for online updates, verify the version changed if not a re-install */ + fmt = fu_device_get_version_format(device); + version_rel = fu_release_get_version(release); + if (version_rel != NULL && fu_version_compare(version_orig, version_rel, fmt) != 0 && + fu_version_compare(version_orig, fu_device_get_version(device), fmt) == 0 && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + 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); + } + + /* mark success unless needs a reboot */ + if (fu_device_get_update_state(device) != FWUPD_UPDATE_STATE_NEEDS_REBOOT) + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_SUCCESS); + + /* wait for the system to acquiesce if required */ + if (fu_device_get_acquiesce_delay(device_orig) > 0) { + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); + fu_engine_wait_for_acquiesce(self, fu_device_get_acquiesce_delay(device_orig)); + } + + /* make the UI update */ + fu_engine_emit_changed(self); + + return TRUE; +} + +/** + * fu_engine_get_plugins: + * @self: a #FuPluginList + * + * Gets all the plugins that have been added. + * + * Returns: (transfer none) (element-type FuPlugin): the plugins + * + * Since: 1.0.8 + **/ +GPtrArray * +fu_engine_get_plugins(FuEngine *self) +{ + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + return fu_plugin_list_get_all(self->plugin_list); +} + +/** + * fu_engine_get_device: + * @self: a #FuEngine + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Gets a specific device. + * + * Returns: (transfer full): a device, or %NULL if not found + **/ +FuDevice * +fu_engine_get_device(FuEngine *self, const gchar *device_id, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + + /* wait for any device to disconnect and reconnect */ + if (!fu_device_list_wait_for_replug(self->device_list, error)) { + g_prefix_error(error, "failed to wait for detach replug: "); + return NULL; + } + + /* get the new device */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return NULL; + + /* success */ + return g_steal_pointer(&device); +} + +/* same as FuDevice->prepare, but with the device open */ +static gboolean +fu_engine_device_prepare(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error(error, "failed to open device for prepare: "); + return FALSE; + } + + /* check battery level is sane */ + if (fu_device_get_battery_level(device) > 0 && + fu_device_get_battery_level(device) < fu_device_get_battery_threshold(device)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW, + "battery level is too low: %u%%", + fu_device_get_battery_level(device)); + return FALSE; + } + + return fu_device_prepare(device, progress, flags, error); +} + +/* same as FuDevice->cleanup, but with the device open */ +static gboolean +fu_engine_device_cleanup(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { + g_debug("skipping device cleanup due to will-disappear flag"); + return TRUE; + } + + locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error(error, "failed to open device for cleanup: "); + return FALSE; + } + return fu_device_cleanup(device, progress, flags, error); +} + +static gboolean +fu_engine_device_check_power(FuEngine *self, + FuDevice *device, + FwupdInstallFlags flags, + GError **error) +{ + if (flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) { + g_autofree gchar *configdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + g_autofree gchar *configfile = g_build_filename(configdir, "daemon.conf", NULL); + g_warning("Ignoring deprecated flag provided by client " + "'FWUPD_INSTALL_FLAG_IGNORE_POWER'. To ignore power levels, modify %s", + configfile); + } + + if (fu_config_get_ignore_power(self->config)) + return TRUE; + + /* not charging */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC) && + (fu_context_get_battery_state(self->ctx) == FU_BATTERY_STATE_DISCHARGING || + fu_context_get_battery_state(self->ctx) == FU_BATTERY_STATE_EMPTY)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AC_POWER_REQUIRED, + "Cannot install update " + "when not on AC power unless forced"); + return FALSE; + } + + /* not enough just in case */ + if (fu_context_get_battery_level(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID && + fu_context_get_battery_threshold(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID && + fu_context_get_battery_level(self->ctx) < fu_context_get_battery_threshold(self->ctx)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW, + "Cannot install update when system battery " + "is not at least %u%% unless forced", + fu_context_get_battery_threshold(self->ctx)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_prepare(FuEngine *self, + const gchar *device_id, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before update prepare: "); + return FALSE; + } + fu_device_inhibit(device, "update-in-progress", "An update is in progress"); + + if (!fu_engine_device_check_power(self, device, flags, error)) + return FALSE; + + str = fu_device_to_string(device); + g_debug("prepare -> %s", str); + if (!fu_engine_device_prepare(self, device, progress, flags, error)) // XXXX + return FALSE; + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index(plugins, j); + if (!fu_plugin_runner_prepare(plugin_tmp, device, progress, flags, error)) // XXX + return FALSE; + } + + /* wait for device to disconnect and reconnect */ + if (!fu_device_list_wait_for_replug(self->device_list, error)) { + g_prefix_error(error, "failed to wait for prepare replug: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_engine_cleanup(FuEngine *self, + const gchar *device_id, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before update cleanup: "); + return FALSE; + } + fu_device_uninhibit(device, "update-in-progress"); + str = fu_device_to_string(device); + g_debug("cleanup -> %s", str); + if (!fu_engine_device_cleanup(self, device, progress, flags, error)) + return FALSE; + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index(plugins, j); + if (!fu_plugin_runner_cleanup(plugin_tmp, device, progress, flags, error)) + return FALSE; + } + + /* wait for device to disconnect and reconnect */ + if (!fu_device_list_wait_for_replug(self->device_list, error)) { + g_prefix_error(error, "failed to wait for cleanup replug: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_engine_detach(FuEngine *self, + const gchar *device_id, + FuProgress *progress, + FwupdFeatureFlags feature_flags, + GError **error) +{ + FuPlugin *plugin; + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) poll_locker = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before update detach: "); + return FALSE; + } + + /* pause the polling */ + poll_locker = fu_device_poll_locker_new(device, error); + if (poll_locker == NULL) + return FALSE; + + str = fu_device_to_string(device); + g_debug("detach -> %s", str); + 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_detach(plugin, device, progress, error)) + return FALSE; + + /* support older clients without the ability to do immediate requests */ + if ((feature_flags & FWUPD_FEATURE_FLAG_REQUESTS) == 0 && + fu_device_get_request_cnt(device, FWUPD_REQUEST_KIND_IMMEDIATE) > 0) { + /* fallback to something sane */ + if (fu_device_get_update_message(device) == NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf("Device %s needs to manually be put in update mode", + fu_device_get_name(device)); + fu_device_set_update_message(device, tmp); + } + + /* abort and require client to re-submit */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NEEDS_USER_ACTION, + fu_device_get_update_message(device)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_attach(FuEngine *self, const gchar *device_id, FuProgress *progress, GError **error) +{ + FuPlugin *plugin; + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) poll_locker = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before update attach: "); + return FALSE; + } + str = fu_device_to_string(device); + g_debug("attach -> %s", str); + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin == NULL) + return FALSE; + + /* pause the polling */ + poll_locker = fu_device_poll_locker_new(device, error); + if (poll_locker == NULL) + return FALSE; + + if (!fu_plugin_runner_attach(plugin, device, progress, error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_engine_set_progress(FuEngine *self, const gchar *device_id, FuProgress *progress, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before setting progress: "); + return FALSE; + } + fu_device_set_progress(device, progress); + return TRUE; +} + +gboolean +fu_engine_activate(FuEngine *self, const gchar *device_id, FuProgress *progress, GError **error) +{ + FuPlugin *plugin; + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + + 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); + + /* check the device exists */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return FALSE; + str = fu_device_to_string(device); + g_debug("activate -> %s", str); + 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, progress, error)) + return FALSE; + + fu_engine_emit_device_changed_safe(self, device); + fu_engine_emit_changed(self); + + return TRUE; +} + +static gboolean +fu_engine_reload(FuEngine *self, const gchar *device_id, GError **error) +{ + FuPlugin *plugin; + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before update reload: "); + return FALSE; + } + str = fu_device_to_string(device); + g_debug("reload -> %s", str); + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin == NULL) + return FALSE; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { + g_debug("skipping reload due to will-disappear flag"); + return TRUE; + } + + if (!fu_plugin_runner_reload(plugin, device, error)) { + g_prefix_error(error, "failed to reload device: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_engine_write_firmware(FuEngine *self, + const gchar *device_id, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPlugin *plugin; + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevice) device_pending = NULL; + g_autoptr(FuDeviceLocker) poll_locker = NULL; + + /* cancel the pending action */ + if (!fu_engine_offline_invalidate(error)) + return FALSE; + + /* the device and plugin both may have changed */ + device = fu_engine_get_device(self, device_id, error); + if (device == NULL) { + g_prefix_error(error, "failed to get device before update: "); + return FALSE; + } + + /* pause the polling */ + poll_locker = fu_device_poll_locker_new(device, error); + if (poll_locker == NULL) + return FALSE; + + device_pending = fu_history_get_device_by_id(self->history, device_id, NULL); + str = fu_device_to_string(device); + g_debug("update -> %s", str); + 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_write_firmware(plugin, device, blob_fw, progress, flags, error)) { + g_autoptr(GError) error_attach = NULL; + g_autoptr(GError) error_cleanup = NULL; + + /* attack back into runtime then cleanup */ + fu_progress_reset(progress); + if (!fu_plugin_runner_attach(plugin, device, progress, &error_attach)) { + g_warning("failed to attach device after failed update: %s", + error_attach->message); + } + fu_progress_reset(progress); + if (!fu_engine_cleanup(self, device_id, progress, flags, &error_cleanup)) { + g_warning("failed to update-cleanup after failed update: %s", + error_cleanup->message); + } + return FALSE; + } + + /* cleanup */ + if (device_pending != NULL) { + const gchar *tmp; + FwupdRelease *release; + + /* update history database */ + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_SUCCESS); + if (!fu_history_modify_device(self->history, device, error)) + return FALSE; + + /* delete cab file */ + release = fu_device_get_release_default(device_pending); + tmp = fwupd_release_get_filename(release); + if (tmp != NULL && g_str_has_prefix(tmp, FWUPD_LIBEXECDIR)) { + 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_delete)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to delete %s: %s", + tmp, + error_delete->message); + return FALSE; + } + } + } + return TRUE; +} + +GBytes * +fu_engine_firmware_dump(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) poll_locker = NULL; + + /* pause the polling */ + poll_locker = fu_device_poll_locker_new(device, error); + if (poll_locker == NULL) + return NULL; + + /* open, read, close */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error(error, "failed to open device for firmware read: "); + return NULL; + } + return fu_device_dump_firmware(device, progress, error); +} + +FuFirmware * +fu_engine_firmware_read(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) poll_locker = NULL; + + /* pause the polling */ + poll_locker = fu_device_poll_locker_new(device, error); + if (poll_locker == NULL) + return NULL; + + /* open, read, close */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error(error, "failed to open device for firmware read: "); + return NULL; + } + return fu_device_read_firmware(device, progress, error); +} + +gboolean +fu_engine_install_blob(FuEngine *self, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, + GError **error) +{ + guint retries = 0; + g_autofree gchar *device_id = NULL; + g_autoptr(GTimer) timer = g_timer_new(); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "prepare"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "cleanup"); + + /* test the firmware is not an empty blob */ + if (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; + } + + /* 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 is about to happen */ + device_id = g_strdup(fu_device_get_id(device)); + if (!fu_engine_prepare(self, device_id, fu_progress_get_child(progress), flags, error)) + return FALSE; + fu_progress_step_done(progress); + + /* plugins can set FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED to run again, but they + * must return TRUE rather than an error */ + do { + g_autoptr(FuDevice) device_tmp = NULL; + FuProgress *progress_local = fu_progress_get_child(progress); + + /* 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; + } + + /* progress */ + if (!fu_engine_set_progress(self, device_id, progress_local, error)) + return FALSE; + if (fu_progress_get_steps(progress_local) == 0) { + fu_progress_set_id(progress_local, G_STRLOC); + fu_progress_add_flag(progress_local, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress_local, FWUPD_STATUS_DEVICE_RESTART, 2, NULL); + fu_progress_add_step(progress_local, FWUPD_STATUS_DEVICE_WRITE, 94, NULL); + fu_progress_add_step(progress_local, FWUPD_STATUS_DEVICE_RESTART, 2, NULL); + fu_progress_add_step(progress_local, FWUPD_STATUS_DEVICE_BUSY, 2, NULL); + } else if (fu_progress_get_steps(progress_local) != 4) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "FuDevice->set_progress did not set " + "detach,write,attach,reload steps"); + return FALSE; + } + + /* detach to bootloader mode */ + if (!fu_engine_detach(self, + device_id, + fu_progress_get_child(progress_local), + feature_flags, + error)) + return FALSE; + fu_progress_step_done(progress_local); + + /* install */ + if (!fu_engine_write_firmware(self, + device_id, + blob_fw, + fu_progress_get_child(progress_local), + flags, + error)) + return FALSE; + fu_progress_step_done(progress_local); + + /* attach into runtime mode */ + if (!fu_engine_attach(self, + device_id, + fu_progress_get_child(progress_local), + error)) + return FALSE; + fu_progress_step_done(progress_local); + + /* get the new version number */ + if (!fu_engine_reload(self, device_id, error)) + return FALSE; + fu_progress_step_done(progress_local); + + /* the device and plugin both may have changed */ + device_tmp = fu_engine_get_device(self, device_id, error); + if (device_tmp == NULL) { + g_prefix_error(error, "failed to get device after install blob: "); + return FALSE; + } + if (!fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED)) + break; + + /* don't rely on a plugin clearing this */ + fu_device_remove_flag(device_tmp, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + fu_progress_reset(progress_local); + + } while (TRUE); + fu_progress_step_done(progress); + + /* signal to all the plugins the update has happened */ + if (!fu_engine_cleanup(self, device_id, fu_progress_get_child(progress), flags, error)) + return FALSE; + fu_progress_step_done(progress); + + /* make the UI update */ + fu_engine_emit_device_changed(self, device_id); + g_debug("Updating %s took %f seconds", + fu_device_get_name(device), + g_timer_elapsed(timer, NULL)); + return TRUE; +} + +static FuDevice * +fu_engine_get_item_by_id_fallback_history(FuEngine *self, const gchar *id, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* not a wildcard */ + if (g_strcmp0(id, FWUPD_DEVICE_ID_ANY) != 0) { + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error_local = NULL; + + /* get this one device */ + dev = fu_history_get_device_by_id(self->history, id, &error_local); + if (dev == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Failed to find %s in history database: %s", + id, + error_local->message); + return NULL; + } + + /* 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); + } + + /* nothing in database */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Device %s has no results to report", + fu_device_get_id(dev)); + return NULL; + } + + /* allow '*' for any */ + devices = fu_history_get_devices(self->history, error); + if (devices == NULL) + return NULL; + 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); + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Failed to find any useful results to report"); + return NULL; +} + +static gboolean +fu_engine_create_silo_index(FuEngine *self, GError **error) +{ + g_autoptr(GPtrArray) components = NULL; + + /* print what we've got */ + components = xb_silo_query(self->silo, "components/component[@type='firmware']", 0, NULL); + if (components == NULL) + return TRUE; + g_debug("%u components now in silo", components->len); + + /* build the index */ + if (!xb_silo_query_build_index(self->silo, "components/component", "type", error)) + return FALSE; + if (!xb_silo_query_build_index(self->silo, + "components/component[@type='firmware']/provides/firmware", + "type", + error)) + return FALSE; + if (!xb_silo_query_build_index(self->silo, + "components/component[@type='firmware']/provides/firmware", + NULL, + error)) + return FALSE; + if (!xb_silo_query_build_index(self->silo, + "components/component[@type='firmware']/tags/tag", + "namespace", + error)) + return FALSE; + + /* create prepared queries to save time later */ + self->query_component_by_guid = + xb_query_new_full(self->silo, + "components/component[@type='firmware']/" + "provides/firmware[@type=$'flashed'][text()=?]/" + "../..", + XB_QUERY_FLAG_OPTIMIZE, + error); + if (self->query_component_by_guid == NULL) { + g_prefix_error(error, "failed to prepare query: "); + return FALSE; + } + return TRUE; +} + +/* for the self tests */ +void +fu_engine_set_silo(FuEngine *self, XbSilo *silo) +{ + g_autoptr(GError) error_local = NULL; + g_return_if_fail(FU_IS_ENGINE(self)); + g_return_if_fail(XB_IS_SILO(silo)); + g_set_object(&self->silo, silo); + if (!fu_engine_create_silo_index(self, &error_local)) + g_warning("failed to create indexes: %s", error_local->message); +} + +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_bytes_get_contents(fn, error); + if (blob == NULL) + return NULL; + + /* convert the silo for the CAB into a XbBuilderSource */ + silo = fu_engine_get_silo_from_blob(self, blob, error); + if (silo == NULL) + return NULL; + xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, error); + if (xml == NULL) + return NULL; + if (!xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) + return NULL; + return g_steal_pointer(&source); +} + +static gboolean +fu_engine_create_metadata(FuEngine *self, XbBuilder *builder, FwupdRemote *remote, GError **error) +{ + g_autoptr(GPtrArray) files = NULL; + const gchar *path; + + /* find all files in directory */ + path = fwupd_remote_get_filename_cache(remote); + files = fu_path_get_files(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); + g_autofree gchar *fn_lowercase = g_ascii_strdown(fn, -1); + + /* check is cab file */ + if (!g_str_has_suffix(fn_lowercase, ".cab")) { + g_debug("ignoring: %s", fn); + continue; + } + + /* build source for file */ + source = fu_engine_create_metadata_builder_source(self, fn, &error_local); + if (source == NULL) { + g_warning("failed to create builder source: %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 void +fu_engine_ensure_device_supported(FuEngine *self, FuDevice *device) +{ + gboolean is_supported = FALSE; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) releases = NULL; + g_autoptr(FuEngineRequest) request = NULL; + + /* all flags set */ + request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ONLY_SUPPORTED); + fu_engine_request_set_feature_flags(request, ~0); + + /* get all releases that pass the requirements */ + releases = fu_engine_get_releases_for_device(self, request, device, &error); + if (releases == NULL) { + if (!g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO) && + !g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning("failed to get releases for %s: %s", + fu_device_get_name(device), + error->message); + } + } else { + if (releases->len > 0) + is_supported = TRUE; + } + + /* was supported, now unsupported */ + if (!is_supported) { + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED)) { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED); + fu_engine_emit_device_changed_safe(self, device); + } + return; + } + + /* was unsupported, now supported */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED)) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED); + fu_engine_emit_device_changed_safe(self, device); + } +} + +static void +fu_engine_md_refresh_device_name(FuEngine *self, FuDevice *device, XbNode *component) +{ + const gchar *name = NULL; + + /* require data */ + if (component == NULL) + return; + + /* copy 1:1 */ + name = xb_node_query_text(component, "name", NULL); + if (name != NULL) { + fu_device_set_name(device, name); + fu_device_remove_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME); + } +} + +static void +fu_engine_md_refresh_device_vendor(FuEngine *self, FuDevice *device, XbNode *component) +{ + const gchar *vendor = NULL; + + /* require data */ + if (component == NULL) + return; + + /* copy 1:1 */ + vendor = xb_node_query_text(component, "developer_name", NULL); + if (vendor != NULL) { + fu_device_set_vendor(device, vendor); + fu_device_remove_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR); + } +} + +static void +fu_engine_md_refresh_device_signed(FuEngine *self, FuDevice *device, XbNode *component) +{ + const gchar *value = NULL; + + /* require data */ + if (component == NULL) + return; + + /* already set, possibly by a quirk */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) || + fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD)) + return; + + /* copy 1:1 */ + value = xb_node_query_text(component, "custom/value[@key='LVFS::DeviceIntegrity']", NULL); + if (value != NULL) { + if (g_strcmp0(value, "signed") == 0) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } else if (g_strcmp0(value, "unsigned") == 0) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + } else { + g_warning("payload value unexpected: %s, expected signed|unsigned", value); + } + fu_device_remove_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR); + } +} + +static void +fu_engine_md_refresh_device_icon(FuEngine *self, FuDevice *device, XbNode *component) +{ + const gchar *icon = NULL; + + /* require data */ + if (component == NULL) + return; + + /* copy 1:1 */ + icon = xb_node_query_text(component, "icon", NULL); + if (icon != NULL) { + fu_device_add_icon(device, icon); + fu_device_remove_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON); + } +} + +static const gchar * +fu_common_device_category_to_name(const gchar *cat) +{ + if (g_strcmp0(cat, "X-EmbeddedController") == 0) + return "Embedded Controller"; + if (g_strcmp0(cat, "X-ManagementEngine") == 0) + return "Intel Management Engine"; + if (g_strcmp0(cat, "X-CorporateManagementEngine") == 0) + return "Intel Management Engine"; + if (g_strcmp0(cat, "X-ConsumerManagementEngine") == 0) + return "Intel Management Engine"; + if (g_strcmp0(cat, "X-ThunderboltController") == 0) + return "Thunderbolt Controller"; + if (g_strcmp0(cat, "X-PlatformSecurityProcessor") == 0) + return "Platform Security Processor"; + if (g_strcmp0(cat, "X-CpuMicrocode") == 0) + return "CPU Microcode"; + if (g_strcmp0(cat, "X-Battery") == 0) + return "Battery"; + if (g_strcmp0(cat, "X-Camera") == 0) + return "Camera"; + if (g_strcmp0(cat, "X-TPM") == 0) + return "TPM"; + if (g_strcmp0(cat, "X-Touchpad") == 0) + return "Touchpad"; + if (g_strcmp0(cat, "X-Mouse") == 0) + return "Mouse"; + if (g_strcmp0(cat, "X-Keyboard") == 0) + return "Keyboard"; + if (g_strcmp0(cat, "X-VideoDisplay") == 0) + return "Display"; + if (g_strcmp0(cat, "X-BaseboardManagementController") == 0) + return "BMC"; + if (g_strcmp0(cat, "X-UsbReceiver") == 0) + return "USB Receiver"; + return NULL; +} + +static void +fu_engine_md_refresh_device_name_category(FuEngine *self, FuDevice *device, XbNode *component) +{ + const gchar *name = NULL; + g_autoptr(GPtrArray) cats = NULL; + + /* require data */ + if (component == NULL) + return; + + /* get AppStream and safe-compat categories */ + cats = xb_node_query(component, "categories/category|X-categories/category", 0, NULL); + if (cats == NULL) + return; + for (guint i = 0; i < cats->len; i++) { + XbNode *n = g_ptr_array_index(cats, i); + name = fu_common_device_category_to_name(xb_node_get_text(n)); + if (name != NULL) + break; + } + if (name != NULL) { + fu_device_set_name(device, name); + fu_device_remove_internal_flag(device, + FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY); + } +} + +static void +_g_ptr_array_reverse(GPtrArray *array) +{ + guint last_idx = array->len - 1; + for (guint i = 0; i < array->len / 2; i++) { + gpointer tmp = array->pdata[i]; + array->pdata[i] = array->pdata[last_idx - i]; + array->pdata[last_idx - i] = tmp; + } +} + +static void +fu_engine_md_refresh_device_verfmt(FuEngine *self, FuDevice *device, XbNode *component) +{ + FwupdVersionFormat verfmt = FWUPD_VERSION_FORMAT_UNKNOWN; + g_autoptr(GPtrArray) verfmts = NULL; + + /* require data */ + if (component == NULL) + return; + + /* get metadata */ + verfmts = xb_node_query(component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL); + if (verfmts == NULL) + return; + _g_ptr_array_reverse(verfmts); + for (guint i = 0; i < verfmts->len; i++) { + XbNode *value = g_ptr_array_index(verfmts, i); + verfmt = fwupd_version_format_from_string(xb_node_get_text(value)); + if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN) + break; + } + + /* found and different to existing */ + if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN && + fu_device_get_version_format(device) != verfmt) { + fu_device_set_version_format(device, verfmt); + if (fu_device_get_version_raw(device) != 0x0) { + g_autofree gchar *version = NULL; + version = fu_version_from_uint32(fu_device_get_version_raw(device), verfmt); + fu_device_set_version(device, version); + } + if (fu_device_get_version_lowest_raw(device) != 0x0) { + g_autofree gchar *version = NULL; + version = fu_version_from_uint32(fu_device_get_version_lowest_raw(device), + verfmt); + fu_device_set_version_lowest(device, version); + } + if (fu_device_get_version_bootloader_raw(device) != 0x0) { + g_autofree gchar *version = NULL; + version = + fu_version_from_uint32(fu_device_get_version_bootloader_raw(device), + verfmt); + fu_device_set_version_bootloader(device, version); + } + } + + /* do not try to do this again */ + fu_device_remove_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT); +} + +void +fu_engine_md_refresh_device_from_component(FuEngine *self, FuDevice *device, XbNode *component) +{ + /* set the name */ + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME)) + fu_engine_md_refresh_device_name(self, device, component); + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY)) + fu_engine_md_refresh_device_name_category(self, device, component); + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON)) + fu_engine_md_refresh_device_icon(self, device, component); + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VENDOR)) + fu_engine_md_refresh_device_vendor(self, device, component); + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED)) + fu_engine_md_refresh_device_signed(self, device, component); + + /* fix the version */ + if (fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT)) + fu_engine_md_refresh_device_verfmt(self, device, component); +} + +static void +fu_engine_md_refresh_devices(FuEngine *self) +{ + g_autoptr(GPtrArray) 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); + g_autoptr(XbNode) component = fu_engine_get_component_by_guids(self, device); + + /* set or clear the SUPPORTED flag */ + fu_engine_ensure_device_supported(self, device); + + /* fixup the name and format as needed */ + fu_engine_md_refresh_device_from_component(self, device, component); + } +} + +static gboolean +fu_engine_load_metadata_store_local(FuEngine *self, + XbBuilder *builder, + FuPathKind path_kind, + GError **error) +{ + g_autofree gchar *fn = fu_path_from_kind(path_kind); + g_autofree gchar *metadata_path = g_build_filename(fn, "local.d", NULL); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) metadata_fns = NULL; + + metadata_fns = fu_path_glob(metadata_path, "*.xml", &error_local); + if (metadata_fns == NULL) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } + for (guint i = 0; i < metadata_fns->len; i++) { + const gchar *path = g_ptr_array_index(metadata_fns, i); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(GFile) file = g_file_new_for_path(path); + g_debug("loading local metadata: %s", path); + if (!xb_builder_source_load_file(source, + file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, + error)) + return FALSE; + xb_builder_source_set_prefix(source, "local"); + xb_builder_import_source(builder, source); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_load_metadata_store(FuEngine *self, FuEngineLoadFlags flags, GError **error) +{ + GPtrArray *remotes; + XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID; + g_autoptr(GFile) xmlb = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + + /* clear existing silo */ + g_clear_object(&self->silo); + + /* verbose profiling */ + if (g_getenv("FWUPD_XMLB_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_remote_list_get_all(self->remote_list); + 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)) + continue; + path = fwupd_remote_get_filename_cache(remote); + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + continue; + + /* generate all metadata on demand */ + if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + g_debug("building metadata for remote '%s'", fwupd_remote_get_id(remote)); + if (!fu_engine_create_metadata(self, builder, remote, &error_local)) { + g_warning("failed to generate remote %s: %s", + fwupd_remote_get_id(remote), + error_local->message); + } + continue; + } + + /* save the remote-id in the custom metadata space */ + file = g_file_new_for_path(path); + if (!xb_builder_source_load_file(source, + file, + XB_BUILDER_SOURCE_FLAG_NONE, + NULL, + &error_local)) { + g_warning("failed to load remote %s: %s", + fwupd_remote_get_id(remote), + error_local->message); + continue; + } + + /* fix up any legacy installed files */ + fixup = xb_builder_fixup_new("AppStreamUpgrade", + fu_engine_appstream_upgrade_cb, + self, + NULL); + xb_builder_fixup_set_max_depth(fixup, 3); + xb_builder_source_add_fixup(source, fixup); + + /* add metadata */ + custom = xb_builder_node_new("custom"); + xb_builder_node_insert_text(custom, + "value", + path, + "key", + "fwupd::FilenameCache", + NULL); + xb_builder_node_insert_text(custom, + "value", + fwupd_remote_get_id(remote), + "key", + "fwupd::RemoteId", + NULL); + xb_builder_source_set_info(source, custom); + + /* we need to watch for changes? */ + xb_builder_import_source(builder, source); + } + + /* add any client-side data, e.g. BKC tags */ + if (!fu_engine_load_metadata_store_local(self, + builder, + FU_PATH_KIND_LOCALSTATEDIR_PKG, + error)) + return FALSE; + if (!fu_engine_load_metadata_store_local(self, builder, FU_PATH_KIND_DATADIR_PKG, error)) + return FALSE; + + /* on a read-only filesystem don't care about the cache GUID */ + if (flags & FU_ENGINE_LOAD_FLAG_READONLY) + compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; + + /* ensure silo is up to date */ + if (flags & FU_ENGINE_LOAD_FLAG_NO_CACHE) { + g_autoptr(GFileIOStream) iostr = NULL; + xmlb = g_file_new_tmp(NULL, &iostr, error); + if (xmlb == NULL) + return FALSE; + } else { + g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); + g_autofree gchar *xmlbfn = g_build_filename(cachedirpkg, "metadata.xmlb", NULL); + xmlb = g_file_new_for_path(xmlbfn); + } + self->silo = xb_builder_ensure(builder, xmlb, compile_flags, NULL, error); + if (self->silo == NULL) { + g_prefix_error(error, "cannot create metadata.xmlb: "); + return FALSE; + } + + /* success */ + return fu_engine_create_silo_index(self, error); +} + +static void +fu_engine_config_changed_cb(FuConfig *config, FuEngine *self) +{ + fu_idle_set_timeout(self->idle, fu_config_get_idle_timeout(config)); + + /* allow changing the hardcoded ESP location */ + if (fu_config_get_esp_location(config) != NULL) { + g_autoptr(GError) error = NULL; + g_autoptr(FuVolume) vol = NULL; + vol = fu_volume_new_esp_for_path(fu_config_get_esp_location(config), &error); + if (vol == NULL) { + g_warning("not adding changed EspLocation: %s", error->message); + } else { + fu_context_add_esp_volume(self->ctx, vol); + } + } +} + +static void +fu_engine_metadata_changed(FuEngine *self) +{ + g_autoptr(GError) error_local = NULL; + if (!fu_engine_load_metadata_store(self, FU_ENGINE_LOAD_FLAG_NONE, &error_local)) + g_warning("Failed to reload metadata store: %s", error_local->message); + + /* set device properties from the metadata */ + fu_engine_md_refresh_devices(self); + + /* invalidate host security attributes */ + g_clear_pointer(&self->host_security_id, g_free); + + /* make the UI update */ + fu_engine_emit_changed(self); +} + +static void +fu_engine_remote_list_changed_cb(FuRemoteList *remote_list, FuEngine *self) +{ + fu_engine_metadata_changed(self); +} + +static gint +fu_engine_sort_jcat_results_timestamp_cb(gconstpointer a, gconstpointer b) +{ + JcatResult *ra = *((JcatResult **)a); + JcatResult *rb = *((JcatResult **)b); + if (jcat_result_get_timestamp(ra) < jcat_result_get_timestamp(rb)) + return 1; + if (jcat_result_get_timestamp(ra) > jcat_result_get_timestamp(rb)) + return -1; + return 0; +} + +static JcatResult * +fu_engine_get_newest_signature_jcat_result(GPtrArray *results, GError **error) +{ + /* sort by timestamp, newest first */ + g_ptr_array_sort(results, fu_engine_sort_jcat_results_timestamp_cb); + + /* get the first signature, ignoring the checksums */ + for (guint i = 0; i < results->len; i++) { + JcatResult *result = g_ptr_array_index(results, i); + if (jcat_result_get_method(result) == JCAT_BLOB_METHOD_SIGNATURE) + return g_object_ref(result); + } + + /* should never happen due to %JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no signature method in results"); + return NULL; +} + +static JcatResult * +fu_engine_get_system_jcat_result(FuEngine *self, FwupdRemote *remote, GError **error) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob_sig = NULL; + g_autoptr(GInputStream) istream = NULL; + g_autoptr(GPtrArray) results = NULL; + g_autoptr(JcatItem) jcat_item = NULL; + g_autoptr(JcatFile) jcat_file = jcat_file_new(); + + blob = fu_bytes_get_contents(fwupd_remote_get_filename_cache(remote), error); + if (blob == NULL) + return NULL; + blob_sig = fu_bytes_get_contents(fwupd_remote_get_filename_cache_sig(remote), error); + if (blob_sig == NULL) + return NULL; + istream = g_memory_input_stream_new_from_bytes(blob_sig); + if (!jcat_file_import_stream(jcat_file, istream, JCAT_IMPORT_FLAG_NONE, NULL, error)) + return NULL; + jcat_item = jcat_file_get_item_default(jcat_file, error); + if (jcat_item == NULL) + return NULL; + results = jcat_context_verify_item(self->jcat_context, + blob, + jcat_item, + JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | + JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + error); + if (results == NULL) + return NULL; + + /* return the newest signature */ + return fu_engine_get_newest_signature_jcat_result(results, error); +} + +static gboolean +fu_engine_validate_result_timestamp(JcatResult *jcat_result, + JcatResult *jcat_result_old, + GError **error) +{ + gint64 delta = 0; + + g_return_val_if_fail(JCAT_IS_RESULT(jcat_result), FALSE); + g_return_val_if_fail(JCAT_IS_RESULT(jcat_result_old), FALSE); + + if (jcat_result_get_timestamp(jcat_result) == 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "no signing timestamp"); + return FALSE; + } + if (jcat_result_get_timestamp(jcat_result_old) > 0) { + delta = jcat_result_get_timestamp(jcat_result) - + jcat_result_get_timestamp(jcat_result_old); + } + if (delta < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "new signing timestamp was %" G_GINT64_FORMAT " seconds older", + -delta); + return FALSE; + } + if (delta > 0) + g_debug("timestamp increased, so no rollback"); + return TRUE; +} + +/** + * fu_engine_update_metadata_bytes: + * @self: a #FuEngine + * @remote_id: a remote ID, e.g. `lvfs` + * @bytes_raw: Blob of metadata + * @bytes_sig: Blob of metadata signature, typically Jcat binary format + * @error: (nullable): optional return location for an error + * + * Updates the metadata for a specific remote. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_update_metadata_bytes(FuEngine *self, + const gchar *remote_id, + GBytes *bytes_raw, + GBytes *bytes_sig, + GError **error) +{ + FwupdKeyringKind keyring_kind; + FwupdRemote *remote; + JcatVerifyFlags jcat_flags = JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE; + g_autoptr(JcatFile) jcat_file = jcat_file_new(); + + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(bytes_raw != NULL, FALSE); + g_return_val_if_fail(bytes_sig != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* check remote is valid */ + remote = fu_remote_list_get_by_id(self->remote_list, remote_id); + if (remote == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "remote %s not found", + remote_id); + return FALSE; + } + if (!fwupd_remote_get_enabled(remote)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "remote %s not enabled", + remote_id); + return FALSE; + } + + /* verify JCatFile, or create a dummy one from legacy data */ + keyring_kind = fwupd_remote_get_keyring_kind(remote); + if (keyring_kind == FWUPD_KEYRING_KIND_JCAT) { + g_autoptr(GInputStream) istream = NULL; + istream = g_memory_input_stream_new_from_bytes(bytes_sig); + if (!jcat_file_import_stream(jcat_file, + istream, + JCAT_IMPORT_FLAG_NONE, + NULL, + error)) + return FALSE; + jcat_flags |= JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM; + } else if (keyring_kind == FWUPD_KEYRING_KIND_GPG) { + g_autoptr(JcatBlob) jcab_blob = NULL; + g_autoptr(JcatItem) jcat_item = jcat_item_new(""); + jcab_blob = jcat_blob_new(JCAT_BLOB_KIND_GPG, bytes_sig); + jcat_item_add_blob(jcat_item, jcab_blob); + jcat_file_add_item(jcat_file, jcat_item); + } else if (keyring_kind == FWUPD_KEYRING_KIND_PKCS7) { + g_autoptr(JcatBlob) jcab_blob = NULL; + g_autoptr(JcatItem) jcat_item = jcat_item_new(""); + jcab_blob = jcat_blob_new(JCAT_BLOB_KIND_PKCS7, bytes_sig); + jcat_item_add_blob(jcat_item, jcab_blob); + jcat_file_add_item(jcat_file, jcat_item); + } + + /* verify file */ + if (keyring_kind != FWUPD_KEYRING_KIND_NONE) { + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) results = NULL; + g_autoptr(JcatItem) jcat_item = NULL; + g_autoptr(JcatResult) jcat_result = NULL; + g_autoptr(JcatResult) jcat_result_old = NULL; + + /* this should only be signing one thing */ + jcat_item = jcat_file_get_item_default(jcat_file, error); + if (jcat_item == NULL) + return FALSE; + results = jcat_context_verify_item(self->jcat_context, + bytes_raw, + jcat_item, + jcat_flags, + error); + if (results == NULL) + return FALSE; + + /* return the newest signature */ + jcat_result = fu_engine_get_newest_signature_jcat_result(results, error); + if (jcat_result == NULL) + return FALSE; + + /* verify the metadata was signed later than the existing + * metadata for this remote to mitigate a rollback attack */ + jcat_result_old = fu_engine_get_system_jcat_result(self, remote, &error_local); + if (jcat_result_old == NULL) { + if (g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_debug("no existing valid keyrings: %s", error_local->message); + } else { + g_warning("could not get existing keyring result: %s", + error_local->message); + } + } else { + if (!fu_engine_validate_result_timestamp(jcat_result, + jcat_result_old, + error)) + return FALSE; + } + } + + /* save XML and signature to remotes.d */ + if (!fu_bytes_set_contents(fwupd_remote_get_filename_cache(remote), bytes_raw, error)) + return FALSE; + if (keyring_kind != FWUPD_KEYRING_KIND_NONE) { + if (!fu_bytes_set_contents(fwupd_remote_get_filename_cache_sig(remote), + bytes_sig, + error)) + return FALSE; + } + if (!fu_engine_load_metadata_store(self, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* refresh SUPPORTED flag on devices */ + fu_engine_md_refresh_devices(self); + + /* invalidate host security attributes */ + g_clear_pointer(&self->host_security_id, g_free); + + /* make the UI update */ + fu_engine_emit_changed(self); + return TRUE; +} + +/** + * fu_engine_update_metadata: + * @self: a #FuEngine + * @remote_id: a remote ID, e.g. `lvfs` + * @fd: file descriptor of the metadata + * @fd_sig: file descriptor of the metadata signature + * @error: (nullable): optional return location for an error + * + * Updates the metadata for a specific remote. + * + * Note: this will close the fds when done + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_update_metadata(FuEngine *self, + const gchar *remote_id, + gint fd, + gint fd_sig, + GError **error) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GBytes) bytes_raw = NULL; + g_autoptr(GBytes) bytes_sig = NULL; + g_autoptr(GInputStream) stream_fd = NULL; + g_autoptr(GInputStream) stream_sig = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(fd > 0, FALSE); + g_return_val_if_fail(fd_sig > 0, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* ensures the fd's are closed on error */ + stream_fd = g_unix_input_stream_new(fd, TRUE); + stream_sig = g_unix_input_stream_new(fd_sig, TRUE); + + /* read the entire file into memory */ + bytes_raw = g_input_stream_read_bytes(stream_fd, 0x100000, NULL, error); + if (bytes_raw == NULL) + return FALSE; + + /* read signature */ + bytes_sig = g_input_stream_read_bytes(stream_sig, 0x100000, NULL, error); + if (bytes_sig == NULL) + return FALSE; + + /* update with blobs */ + return fu_engine_update_metadata_bytes(self, remote_id, bytes_raw, bytes_sig, error); +#else + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); + return FALSE; +#endif +} + +/** + * fu_engine_get_silo_from_blob: + * @self: a #FuEngine + * @blob_cab: a data blob + * @error: (nullable): optional return location for an error + * + * Creates a silo from a .cab file blob. + * + * Returns: (transfer container): a #XbSilo, or %NULL + **/ +XbSilo * +fu_engine_get_silo_from_blob(FuEngine *self, GBytes *blob_cab, GError **error) +{ + g_autoptr(FuCabinet) cabinet = fu_cabinet_new(); + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(blob_cab != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* load file */ + fu_engine_set_status(self, FWUPD_STATUS_DECOMPRESSING); + fu_cabinet_set_size_max(cabinet, fu_config_get_archive_size_max(self->config)); + fu_cabinet_set_jcat_context(cabinet, self->jcat_context); + if (!fu_cabinet_parse(cabinet, blob_cab, FU_CABINET_PARSE_FLAG_NONE, error)) + return NULL; + return fu_cabinet_get_silo(cabinet); +} + +static FuDevice * +fu_engine_get_result_from_component(FuEngine *self, + FuEngineRequest *request, + XbNode *component, + GError **error) +{ + FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; + g_autofree gchar *description_xpath = NULL; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(FuRelease) release = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GError) error_reqs = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(GPtrArray) tags = NULL; + g_autoptr(XbNode) description = NULL; + g_autoptr(XbNode) rel = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + + dev = fu_device_new(self->ctx); + 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++) { + XbNode *prov = XB_NODE(g_ptr_array_index(provides, i)); + const gchar *guid; + g_autoptr(FuDevice) device = NULL; + + /* is a online or offline update appropriate */ + 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) { + fu_device_set_name(dev, fu_device_get_name(device)); + fu_device_set_flags(dev, fu_device_get_flags(device)); + fu_device_set_internal_flags(dev, fu_device_get_internal_flags(device)); + fu_device_set_id(dev, fu_device_get_id(device)); + fu_device_set_version_raw(dev, fu_device_get_version_raw(device)); + fu_device_set_version_format(dev, fu_device_get_version_format(device)); + fu_device_set_version(dev, fu_device_get_version(device)); + } else { + fu_device_inhibit(dev, "not-found", "Device was not found"); + } + + /* add GUID */ + fu_device_add_guid(dev, guid); + } + if (fu_device_get_guids(dev)->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "component has no GUIDs"); + return NULL; + } + + /* add tags */ + tags = xb_node_query(component, "tags/tag[@namespace=$'lvfs']", 0, NULL); + if (tags != NULL) { + for (guint i = 0; i < tags->len; i++) { + XbNode *tag = g_ptr_array_index(tags, i); + fu_release_add_tag(release, xb_node_get_text(tag)); + } + } + + /* add EOL flag */ + if (xb_node_get_attr(component, "date_eol") != NULL) + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_END_OF_LIFE); + + /* check we can install it */ + release = fu_release_new(); + fu_release_set_device(release, dev); + fu_release_set_request(release, request); + if (!fu_engine_load_release(self, + release, + component, + NULL, + FWUPD_INSTALL_FLAG_IGNORE_VID_PID, + &error_reqs)) { + if (!fu_device_has_inhibit(dev, "not-found")) + fu_device_inhibit(dev, "failed-reqs", error_reqs->message); + /* continue */ + } + + /* verify trust */ +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + error); + if (query == NULL) + return NULL; + rel = xb_node_query_first_full(component, query, &error_local); +#else + rel = xb_node_query_first(component, "releases/release", &error_local); +#endif + if (rel == 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(rel, &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; + } + } + fwupd_release_set_flags(FWUPD_RELEASE(release), release_flags); + + /* create a result with all the metadata in */ + description_xpath = fu_engine_request_get_localized_xpath(request, "description"); + description = xb_node_query_first(component, description_xpath, 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); + } + + /* refresh the device to the new version format too */ + fu_engine_md_refresh_device_from_component(self, dev, component); + + /* success */ + fu_device_add_release(dev, FWUPD_RELEASE(release)); + return g_steal_pointer(&dev); +} + +static gint +fu_engine_get_details_sort_cb(gconstpointer a, gconstpointer b) +{ + FuDevice *device1 = *((FuDevice **)a); + FuDevice *device2 = *((FuDevice **)b); + if (!fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE) && + fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE)) + return 1; + if (fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE)) + return -1; + return 0; +} + +/* for self tests */ +GPtrArray * +fu_engine_get_details_for_bytes(FuEngine *self, + FuEngineRequest *request, + GBytes *blob, + GError **error) +{ + const gchar *remote_id; + GChecksumType checksum_types[] = {G_CHECKSUM_SHA256, G_CHECKSUM_SHA1, 0}; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(GPtrArray) details = NULL; + g_autoptr(GPtrArray) checksums = g_ptr_array_new_with_free_func(g_free); + g_autoptr(XbSilo) silo = NULL; + + silo = fu_engine_get_silo_from_blob(self, blob, error); + if (silo == NULL) + return NULL; + components = xb_silo_query(silo, "components/component[@type='firmware']", 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[@type='firmware']/provides/firmware", + "type", + error)) + return NULL; + if (!xb_silo_query_build_index(silo, + "components/component[@type='firmware']/provides/firmware", + NULL, + error)) + return NULL; + + /* calculate the checksums of the blob */ + for (guint i = 0; checksum_types[i] != 0; i++) + g_ptr_array_add(checksums, g_compute_checksum_for_bytes(checksum_types[i], blob)); + + /* does this exist in any enabled remote */ + for (guint i = 0; i < checksums->len; i++) { + const gchar *csum = g_ptr_array_index(checksums, i); + remote_id = fu_engine_get_remote_id_for_checksum(self, csum); + if (remote_id != NULL) + break; + } + + /* create results with all the metadata in */ + details = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index(components, i); + FuDevice *dev; + FwupdRelease *rel; + dev = fu_engine_get_result_from_component(self, request, component, error); + if (dev == NULL) + return NULL; + rel = fu_device_get_release_default(dev); + if (remote_id != NULL) { + fwupd_release_set_remote_id(rel, remote_id); + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED); + } + if (fu_device_has_internal_flag(dev, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT)) + fu_engine_md_refresh_device_verfmt(self, dev, component); + + /* add the checksum of the container blob */ + for (guint j = 0; j < checksums->len; j++) { + const gchar *csum = g_ptr_array_index(checksums, j); + fwupd_release_add_checksum(rel, csum); + } + + /* if this matched a device on the system, ensure all the + * requirements passed before setting UPDATABLE */ + if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_req = NULL; + FwupdInstallFlags install_flags = + FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_IGNORE_VID_PID | + FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_OLDER; + + fu_release_set_device(release, dev); + fu_release_set_request(release, request); + if (!fu_engine_load_release(self, + release, + component, + NULL, + install_flags, + &error_req)) { + g_debug("%s failed requirement checks: %s", + fu_device_get_id(dev), + error_req->message); + fu_device_inhibit(dev, "failed-reqs", error_req->message); + } else { + g_debug("%s passed requirement checks", fu_device_get_id(dev)); + fu_device_uninhibit(dev, "failed-reqs"); + } + } + + g_ptr_array_add(details, dev); + } + + /* order multiple devices so that the one that passes the requirement + * is listed first */ + g_ptr_array_sort(details, fu_engine_get_details_sort_cb); + + return g_steal_pointer(&details); +} + +/** + * fu_engine_get_details: + * @self: a #FuEngine + * @request: a #FuEngineRequest + * @fd: a file descriptor + * @error: (nullable): optional return location for an error + * + * Gets the details about a local file. + * + * Note: this will close the fd when done + * + * Returns: (transfer container) (element-type FuDevice): results + **/ +GPtrArray * +fu_engine_get_details(FuEngine *self, FuEngineRequest *request, gint fd, GError **error) +{ + g_autoptr(GBytes) blob = 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 components */ + blob = fu_bytes_get_contents_fd(fd, fu_config_get_archive_size_max(self->config), error); + if (blob == NULL) + return NULL; + return fu_engine_get_details_for_bytes(self, request, blob, error); +} + +static gint +fu_engine_sort_devices_by_priority_name(gconstpointer a, gconstpointer b) +{ + FuDevice *dev_a = *((FuDevice **)a); + FuDevice *dev_b = *((FuDevice **)b); + gint prio_a = fu_device_get_priority(dev_a); + gint prio_b = fu_device_get_priority(dev_b); + const gchar *name_a = fu_device_get_name(dev_a); + const gchar *name_b = fu_device_get_name(dev_b); + + if (prio_a > prio_b) + return -1; + if (prio_a < prio_b) + return 1; + if (g_strcmp0(name_a, name_b) > 0) + return 1; + if (g_strcmp0(name_a, name_b) < 0) + return -1; + return 0; +} + +/** + * fu_engine_get_devices: + * @self: a #FuEngine + * @error: (nullable): optional return location for an error + * + * Gets the list of devices. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_devices(FuEngine *self, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + devices = fu_device_list_get_active(self->device_list); + if (devices->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No detected devices"); + return NULL; + } + g_ptr_array_sort(devices, fu_engine_sort_devices_by_priority_name); + return g_steal_pointer(&devices); +} + +/** + * fu_engine_get_devices_by_guid: + * @self: a #FuEngine + * @guid: a GUID + * @error: (nullable): optional return location for an error + * + * Gets a specific device. + * + * Returns: (transfer full): a device, or %NULL if not found + **/ +GPtrArray * +fu_engine_get_devices_by_guid(FuEngine *self, const gchar *guid, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_tmp = NULL; + + /* find the devices by GUID */ + devices_tmp = fu_device_list_get_all(self->device_list); + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < devices_tmp->len; i++) { + FuDevice *dev_tmp = g_ptr_array_index(devices_tmp, i); + if (fu_device_has_guid(dev_tmp, guid)) + g_ptr_array_add(devices, g_object_ref(dev_tmp)); + } + + /* nothing */ + if (devices->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to find any device providing %s", + guid); + return NULL; + } + + /* success */ + return g_steal_pointer(&devices); +} + +/** + * fu_engine_get_devices_by_composite_id: + * @self: a #FuEngine + * @composite_id: a device ID + * @error: (nullable): optional return location for an error + * + * Gets all devices that match a specific composite ID. + * + * Returns: (transfer full) (element-type FuDevice): devices + **/ +GPtrArray * +fu_engine_get_devices_by_composite_id(FuEngine *self, const gchar *composite_id, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_tmp = NULL; + + /* find the devices by composite ID */ + devices_tmp = fu_device_list_get_all(self->device_list); + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < devices_tmp->len; i++) { + FuDevice *dev_tmp = g_ptr_array_index(devices_tmp, i); + if (g_strcmp0(fu_device_get_composite_id(dev_tmp), composite_id) == 0) + g_ptr_array_add(devices, g_object_ref(dev_tmp)); + } + + /* nothing */ + if (devices->len == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to find any device with composite ID %s", + composite_id); + return NULL; + } + + /* success */ + return g_steal_pointer(&devices); +} + +static void +fu_engine_get_history_set_hsi_attrs(FuEngine *self, FuDevice *device) +{ + g_autoptr(GPtrArray) vals = NULL; + + /* ensure up to date */ + fu_engine_ensure_security_attrs(self); + + /* add attributes */ + vals = fu_security_attrs_get_all(self->host_security_attrs); + for (guint i = 0; i < vals->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(vals, i); + const gchar *tmp; + tmp = fwupd_security_attr_result_to_string(fwupd_security_attr_get_result(attr)); + fu_device_set_metadata(device, fwupd_security_attr_get_appstream_id(attr), tmp); + } + + /* computed value */ + fu_device_set_metadata(device, "HSI", self->host_security_id); +} + +/** + * fu_engine_get_history: + * @self: a #FuEngine + * @error: (nullable): optional return location for an error + * + * Gets the list of history. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_history(FuEngine *self, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + devices = fu_history_get_devices(self->history, error); + if (devices == NULL) + return NULL; + if (devices->len == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "No history"); + return NULL; + } + + /* if this is the system firmware device, add the HSI attrs */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index(devices, i); + if (fu_device_has_instance_id(dev, "main-system-firmware")) + fu_engine_get_history_set_hsi_attrs(self, dev); + } + + /* try to set the remote ID for each device */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel; + GPtrArray *csums; + + /* get the checksums */ + rel = fu_device_get_release_default(dev); + if (rel == NULL) + continue; + + /* find the checksum that matches */ + csums = fwupd_release_get_checksums(rel); + for (guint j = 0; j < csums->len; j++) { + const gchar *csum = g_ptr_array_index(csums, j); + const gchar *remote_id = fu_engine_get_remote_id_for_checksum(self, csum); + if (remote_id != NULL) { + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED); + fwupd_release_set_remote_id(rel, remote_id); + break; + } + } + } + + return g_steal_pointer(&devices); +} + +#if !GLIB_CHECK_VERSION(2, 62, 0) +static GPtrArray * +g_ptr_array_copy(GPtrArray *array, GCopyFunc func, gpointer user_data) +{ + GPtrArray *new = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < array->len; i++) { + GObject *obj = g_ptr_array_index(array, i); + g_ptr_array_add(new, g_object_ref(obj)); + } + return new; +} +#endif + +/** + * fu_engine_get_remotes: + * @self: a #FuEngine + * @error: (nullable): optional return location for an error + * + * Gets the list of remotes in use by the engine. + * + * Returns: (transfer container) (element-type FwupdRemote): results + **/ +GPtrArray * +fu_engine_get_remotes(FuEngine *self, GError **error) +{ + GPtrArray *remotes; + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + remotes = fu_remote_list_get_all(self->remote_list); + if (remotes->len == 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "No remotes configured"); + return NULL; + } + + /* deep copy so the remote list can be kept up to date */ + return g_ptr_array_copy(remotes, (GCopyFunc)g_object_ref, NULL); +} + +/** + * fu_engine_get_remote_by_id: + * @self: a #FuEngine + * @remote_id: a string representation of a remote + * @error: (nullable): optional return location for an error + * + * 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, gpointer user_data) +{ + FuDevice *device = FU_DEVICE(user_data); + FwupdRelease *rel_a = FWUPD_RELEASE(*((FwupdRelease **)a)); + FwupdRelease *rel_b = FWUPD_RELEASE(*((FwupdRelease **)b)); + gint rc; + + /* first by branch */ + rc = g_strcmp0(fwupd_release_get_branch(rel_b), fwupd_release_get_branch(rel_a)); + if (rc != 0) + return rc; + + /* then by version */ + return fu_version_compare(fwupd_release_get_version(rel_b), + fwupd_release_get_version(rel_a), + fu_device_get_version_format(device)); +} + +static gboolean +fu_engine_check_release_is_approved(FuEngine *self, FwupdRelease *rel) +{ + GPtrArray *csums = fwupd_release_get_checksums(rel); + if (self->approved_firmware == NULL) + return FALSE; + 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; +} + +static gboolean +fu_engine_check_release_is_blocked(FuEngine *self, FuRelease *release) +{ + GPtrArray *csums = fu_release_get_checksums(release); + if (self->blocked_firmware == NULL) + return FALSE; + for (guint i = 0; i < csums->len; i++) { + const gchar *csum = g_ptr_array_index(csums, i); + if (g_hash_table_lookup(self->blocked_firmware, csum) != NULL) + return TRUE; + } + return FALSE; +} + +static gboolean +fu_engine_add_releases_for_device_component(FuEngine *self, + FuEngineRequest *request, + FuDevice *device, + XbNode *component, + GPtrArray *releases, + GError **error) +{ + FwupdFeatureFlags feature_flags; + FwupdVersionFormat fmt = fu_device_get_version_format(device); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) releases_tmp = NULL; + FwupdInstallFlags install_flags = + FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_IGNORE_VID_PID | + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_OLDER; + + /* 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; + } + feature_flags = fu_engine_request_get_feature_flags(request); + for (guint i = 0; i < releases_tmp->len; i++) { + XbNode *rel = g_ptr_array_index(releases_tmp, i); + const gchar *remote_id; + const gchar *update_message; + const gchar *update_image; + const gchar *update_request_id; + gint vercmp; + GPtrArray *checksums; + GPtrArray *locations; + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_loop = NULL; + + /* create new FwupdRelease for the XbNode */ + fu_release_set_request(release, request); + fu_release_set_device(release, device); + if (!fu_engine_load_release(self, + release, + component, + rel, + install_flags, + &error_loop)) { + g_debug("failed to set release for component: %s", error_loop->message); + continue; + } + + /* fall back to quirk-provided value */ + if (fwupd_release_get_install_duration(FWUPD_RELEASE(release)) == 0) { + fwupd_release_set_install_duration(FWUPD_RELEASE(release), + fu_device_get_install_duration(device)); + } + + /* invalid */ + locations = fwupd_release_get_locations(FWUPD_RELEASE(release)); + if (locations->len == 0) + continue; + checksums = fu_release_get_checksums(release); + if (checksums->len == 0) + continue; + + /* different branch */ + if (g_strcmp0(fu_release_get_branch(release), fu_device_get_branch(device)) != 0) { + if ((feature_flags & FWUPD_FEATURE_FLAG_SWITCH_BRANCH) == 0) { + g_debug("client does not understand branches, skipping %s:%s", + fu_release_get_branch(release), + fu_release_get_version(release)); + continue; + } + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH); + } + + /* test for upgrade or downgrade */ + vercmp = fu_version_compare(fu_release_get_version(release), + fu_device_get_version(device), + fmt); + if (vercmp > 0) + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_IS_UPGRADE); + else if (vercmp < 0) + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_IS_DOWNGRADE); + + /* lower than allowed to downgrade to */ + if (fu_device_get_version_lowest(device) != NULL && + fu_version_compare(fu_release_get_version(release), + fu_device_get_version_lowest(device), + fmt) < 0) { + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_BLOCKED_VERSION); + } + + /* manually blocked */ + if (fu_engine_check_release_is_blocked(self, release)) + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + + /* check if remote is filtering firmware */ + remote_id = fwupd_release_get_remote_id(FWUPD_RELEASE(release)); + 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, FWUPD_RELEASE(release))) { + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + } + } + + /* add update message if exists but device doesn't already have one */ + update_message = fwupd_release_get_update_message(FWUPD_RELEASE(release)); + if (fwupd_device_get_update_message(FWUPD_DEVICE(device)) == NULL && + update_message != NULL) { + fu_device_set_update_message(device, update_message); + } + update_image = fwupd_release_get_update_image(FWUPD_RELEASE(release)); + if (fwupd_device_get_update_image(FWUPD_DEVICE(device)) == NULL && + update_image != NULL) { + fwupd_device_set_update_image(FWUPD_DEVICE(device), update_image); + } + update_request_id = fu_release_get_update_request_id(release); + if (fu_device_get_update_request_id(device) == NULL && update_request_id != NULL) + fu_device_set_update_request_id(device, update_request_id); + + /* success */ + g_ptr_array_add(releases, g_steal_pointer(&release)); + } + + /* success */ + return TRUE; +} + +static const gchar * +fu_engine_get_branch_fallback(const gchar *nullable_branch) +{ + if (nullable_branch == NULL) + return "default"; + return nullable_branch; +} + +GPtrArray * +fu_engine_get_releases_for_device(FuEngine *self, + FuEngineRequest *request, + FuDevice *device, + GError **error) +{ + GPtrArray *device_guids; + const gchar *version; + g_autoptr(GError) error_all = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) branches = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(GPtrArray) releases = NULL; + g_autoptr(GString) xpath = g_string_new(NULL); + + /* get device version */ + version = fu_device_get_version(device); + if (version == NULL) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no version set"); + return NULL; + } + + /* only show devices that can be updated */ + if (!fu_engine_request_has_feature_flag(request, FWUPD_FEATURE_FLAG_SHOW_PROBLEMS) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not updatable"); + return NULL; + } + + /* 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++) { + const gchar *guid = g_ptr_array_index(device_guids, i); + xb_string_append_union(xpath, + "components/component[@type='firmware']/" + "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_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No releases found"); + return NULL; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return NULL; + } + + /* find all the releases that pass all the requirements */ + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < components->len; i++) { + XbNode *component = XB_NODE(g_ptr_array_index(components, i)); + g_autoptr(GError) error_tmp = NULL; + if (!fu_engine_add_releases_for_device_component(self, + request, + 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); + } + } + + /* are there multiple branches available */ + branches = g_ptr_array_new_with_free_func(g_free); + g_ptr_array_add(branches, + g_strdup(fu_engine_get_branch_fallback(fu_device_get_branch(device)))); + for (guint i = 0; i < releases->len; i++) { + FwupdRelease *rel_tmp = FWUPD_RELEASE(g_ptr_array_index(releases, i)); + const gchar *branch_tmp = + fu_engine_get_branch_fallback(fwupd_release_get_branch(rel_tmp)); +#if GLIB_CHECK_VERSION(2, 54, 3) + if (g_ptr_array_find_with_equal_func(branches, branch_tmp, g_str_equal, NULL)) + continue; +#endif + g_ptr_array_add(branches, g_strdup(branch_tmp)); + } + if (branches->len > 1) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES); + + /* 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: "); + return NULL; + } + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "No releases found"); + return NULL; + } + return g_steal_pointer(&releases); +} + +/** + * fu_engine_get_releases: + * @self: a #FuEngine + * @request: a #FuEngineRequest + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Gets the releases available for a specific device. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_releases(FuEngine *self, + FuEngineRequest *request, + const gchar *device_id, + GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(GPtrArray) releases = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find the device */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return NULL; + + /* get all the releases for the device */ + releases = fu_engine_get_releases_for_device(self, request, device, error); + if (releases == NULL) + return NULL; + if (releases->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No releases for device"); + return NULL; + } + g_ptr_array_sort_with_data(releases, fu_engine_sort_releases_cb, device); + return g_steal_pointer(&releases); +} + +/** + * fu_engine_get_downgrades: + * @self: a #FuEngine + * @request: a #FuEngineRequest + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Gets the downgrades available for a specific device. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_downgrades(FuEngine *self, + FuEngineRequest *request, + const gchar *device_id, + GError **error) +{ + 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); + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find the device */ + device = fu_device_list_get_by_id(self->device_list, device_id, error); + if (device == NULL) + return NULL; + + /* get all the releases for the device */ + releases_tmp = fu_engine_get_releases_for_device(self, request, device, error); + if (releases_tmp == NULL) + return NULL; + 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); + + /* 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", + fwupd_release_get_version(rel_tmp), + fu_device_get_version(device)); + continue; + } + + /* 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", + fwupd_release_get_version(rel_tmp), + fu_device_get_version(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; + } + + /* different branch */ + if (fwupd_release_has_flag(rel_tmp, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH)) { + g_debug("ignoring release %s as branch %s, and device is %s", + fwupd_release_get_version(rel_tmp), + fwupd_release_get_branch(rel_tmp), + fu_device_get_branch(device)); + continue; + } + + g_ptr_array_add(releases, g_object_ref(rel_tmp)); + } + if (error_str->len > 2) + g_string_truncate(error_str, error_str->len - 2); + if (releases->len == 0) { + if (error_str->len > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "current version is %s: %s", + fu_device_get_version(device), + error_str->str); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "current version is %s", + fu_device_get_version(device)); + } + return NULL; + } + g_ptr_array_sort_with_data(releases, fu_engine_sort_releases_cb, device); + return g_steal_pointer(&releases); +} + +GPtrArray * +fu_engine_get_approved_firmware(FuEngine *self) +{ + GPtrArray *checksums = g_ptr_array_new_with_free_func(g_free); + if (self->approved_firmware != NULL) { + 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) +{ + if (self->approved_firmware == NULL) { + self->approved_firmware = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } + g_hash_table_add(self->approved_firmware, g_strdup(checksum)); +} + +GPtrArray * +fu_engine_get_blocked_firmware(FuEngine *self) +{ + GPtrArray *checksums = g_ptr_array_new_with_free_func(g_free); + if (self->blocked_firmware != NULL) { + g_autoptr(GList) keys = g_hash_table_get_keys(self->blocked_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_blocked_firmware(FuEngine *self, const gchar *checksum) +{ + if (self->blocked_firmware == NULL) { + self->blocked_firmware = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } + g_hash_table_add(self->blocked_firmware, g_strdup(checksum)); +} + +gboolean +fu_engine_set_blocked_firmware(FuEngine *self, GPtrArray *checksums, GError **error) +{ + /* update in-memory hash */ + if (self->blocked_firmware != NULL) { + g_hash_table_unref(self->blocked_firmware); + self->blocked_firmware = NULL; + } + for (guint i = 0; i < checksums->len; i++) { + const gchar *csum = g_ptr_array_index(checksums, i); + fu_engine_add_blocked_firmware(self, csum); + } + + /* save database */ + if (!fu_history_clear_blocked_firmware(self->history, error)) + return FALSE; + for (guint i = 0; i < checksums->len; i++) { + const gchar *csum = g_ptr_array_index(checksums, i); + if (!fu_history_add_blocked_firmware(self->history, csum, error)) + return FALSE; + } + return TRUE; +} + +gchar * +fu_engine_self_sign(FuEngine *self, const gchar *value, JcatSignFlags flags, GError **error) +{ + g_autoptr(JcatBlob) jcat_signature = NULL; + g_autoptr(JcatEngine) jcat_engine = NULL; + g_autoptr(JcatResult) jcat_result = NULL; + g_autoptr(GBytes) payload = NULL; + + /* create detached signature and verify */ + jcat_engine = jcat_context_get_engine(self->jcat_context, JCAT_BLOB_KIND_PKCS7, error); + if (jcat_engine == NULL) + return NULL; + payload = g_bytes_new(value, strlen(value)); + jcat_signature = jcat_engine_self_sign(jcat_engine, payload, flags, error); + if (jcat_signature == NULL) + return NULL; + jcat_result = jcat_engine_self_verify(jcat_engine, + payload, + jcat_blob_get_data(jcat_signature), + JCAT_VERIFY_FLAG_NONE, + error); + if (jcat_result == NULL) + return NULL; + return jcat_blob_get_data_as_string(jcat_signature); +} + +/** + * fu_engine_get_upgrades: + * @self: a #FuEngine + * @request: a #FuEngineRequest + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Gets the upgrades available for a specific device. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_upgrades(FuEngine *self, + FuEngineRequest *request, + const gchar *device_id, + GError **error) +{ + 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); + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find the device */ + device = fu_device_list_get_by_id(self->device_list, device_id, 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_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "A reboot is pending"); + return NULL; + } + + /* get all the releases for the device */ + releases_tmp = fu_engine_get_releases_for_device(self, request, device, error); + if (releases_tmp == NULL) + return NULL; + 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); + + /* 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 == %s", + fwupd_release_get_version(rel_tmp), + fu_device_get_version(device)); + continue; + } + + /* 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 < %s", + fwupd_release_get_version(rel_tmp), + 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; + } + + /* different branch */ + if (fwupd_release_has_flag(rel_tmp, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH)) { + g_debug("ignoring release %s as branch %s, and device is %s", + fwupd_release_get_version(rel_tmp), + fwupd_release_get_branch(rel_tmp), + fu_device_get_branch(device)); + continue; + } + + g_ptr_array_add(releases, g_object_ref(rel_tmp)); + } + if (error_str->len > 2) + g_string_truncate(error_str, error_str->len - 2); + if (releases->len == 0) { + if (error_str->len > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "current version is %s: %s", + fu_device_get_version(device), + error_str->str); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "current version is %s", + fu_device_get_version(device)); + } + return NULL; + } + g_ptr_array_sort_with_data(releases, fu_engine_sort_releases_cb, device); + return g_steal_pointer(&releases); +} + +/** + * fu_engine_clear_results: + * @self: a #FuEngine + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Clear the historical state of a specific device operation. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_clear_results(FuEngine *self, const gchar *device_id, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + FuPlugin *plugin; + + 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); + + /* find the device */ + device = fu_engine_get_item_by_id_fallback_history(self, device_id, error); + if (device == NULL) + return FALSE; + + /* already set on the database */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NOTIFIED)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device already has notified flag"); + return FALSE; + } + + /* call into the plugin if it still exists */ + plugin = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); + if (plugin != NULL) { + if (!fu_plugin_runner_clear_results(plugin, device, error)) + return FALSE; + } + + /* if the offline update never got run, unstage it */ + if (fu_device_get_update_state(device) == FWUPD_UPDATE_STATE_PENDING) + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_UNKNOWN); + + /* override */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NOTIFIED); + return fu_history_modify_device(self->history, device, error); +} + +/** + * fu_engine_get_results: + * @self: a #FuEngine + * @device_id: a device ID + * @error: (nullable): optional return location for an error + * + * Gets the historical state of a specific device operation. + * + * Returns: (transfer container): a device, or %NULL + **/ +FwupdDevice * +fu_engine_get_results(FuEngine *self, const gchar *device_id, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(device_id != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find the device */ + device = fu_engine_get_item_by_id_fallback_history(self, device_id, error); + if (device == NULL) + return NULL; + + /* the notification has already been shown to the user */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NOTIFIED)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "User has already been notified about %s [%s]", + fu_device_get_name(device), + fu_device_get_id(device)); + return NULL; + } + + /* success */ + return g_object_ref(FWUPD_DEVICE(device)); +} + +static void +fu_engine_plugins_setup(FuEngine *self, FuProgress *progress) +{ + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, plugins->len); + for (guint i = 0; i < plugins->len; i++) { + g_autoptr(GError) error = NULL; + FuPlugin *plugin = g_ptr_array_index(plugins, i); + if (!fu_plugin_runner_startup(plugin, fu_progress_get_child(progress), &error)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_NO_HARDWARE); + } + g_message("disabling plugin because: %s", error->message); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_CHILD_FINISHED); + } + fu_progress_step_done(progress); + } +} + +static void +fu_engine_plugins_coldplug(FuEngine *self, FuProgress *progress) +{ + GPtrArray *plugins; + g_autoptr(GString) str = g_string_new(NULL); + + /* exec */ + plugins = fu_plugin_list_get_all(self->plugin_list); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, plugins->len); + for (guint i = 0; i < plugins->len; i++) { + g_autoptr(GError) error = NULL; + FuPlugin *plugin = g_ptr_array_index(plugins, i); + if (!fu_plugin_runner_coldplug(plugin, fu_progress_get_child(progress), &error)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED); + g_message("disabling plugin because: %s", error->message); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_CHILD_FINISHED); + } + fu_progress_step_done(progress); + } + + /* print what we do have */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + g_string_append_printf(str, "%s, ", fu_plugin_get_name(plugin)); + } + if (str->len > 2) { + g_string_truncate(str, str->len - 2); + g_debug("using plugins: %s", str->str); + } +} + +static void +fu_engine_plugin_device_register(FuEngine *self, FuDevice *device) +{ + GPtrArray *plugins; + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)) { + g_warning("already registered %s, ignoring", fu_device_get_id(device)); + return; + } + 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); + fu_plugin_runner_device_register(plugin, device); + } + for (guint i = 0; i < self->backends->len; i++) { + FuBackend *backend = g_ptr_array_index(self->backends, i); + fu_backend_registered(backend, device); + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_REGISTERED); +} + +static void +fu_engine_plugin_device_register_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + FuEngine *self = FU_ENGINE(user_data); + fu_engine_plugin_device_register(self, device); +} + +static void +fu_engine_plugin_device_added_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + FuEngine *self = FU_ENGINE(user_data); + + /* plugin has prio and device not already set from quirk */ + if (fu_plugin_get_priority(plugin) > 0 && fu_device_get_priority(device) == 0) { + g_debug("auto-setting %s priority to %u", + fu_device_get_id(device), + fu_plugin_get_priority(plugin)); + fu_device_set_priority(device, fu_plugin_get_priority(plugin)); + } + + fu_engine_add_device(self, device); +} + +static void +fu_engine_adopt_children(FuEngine *self, FuDevice *device) +{ + GPtrArray *guids; + g_autoptr(GPtrArray) devices = fu_device_list_get_active(self->device_list); + + /* find the parent in any existing device */ + if (fu_device_get_parent(device) == NULL) { + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices, i); + if (!fu_device_has_internal_flag( + device_tmp, + FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN)) + continue; + if (fu_device_get_physical_id(device_tmp) == NULL) + continue; + if (fu_device_has_parent_physical_id( + device, + fu_device_get_physical_id(device_tmp))) { + fu_device_set_parent(device, device_tmp); + break; + } + } + } + if (fu_device_get_parent(device) == NULL) { + guids = fu_device_get_parent_guids(device); + for (guint j = 0; j < guids->len; j++) { + const gchar *guid = g_ptr_array_index(guids, j); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices, i); + if (fu_device_has_guid(device_tmp, guid)) { + fu_device_set_parent(device, device_tmp); + break; + } + } + } + } + + /* the new device is the parent to an existing child */ + for (guint j = 0; j < devices->len; j++) { + GPtrArray *parent_physical_ids = NULL; + FuDevice *device_tmp = g_ptr_array_index(devices, j); + if (fu_device_get_parent(device_tmp) != NULL) + continue; + parent_physical_ids = fu_device_get_parent_physical_ids(device_tmp); + if (parent_physical_ids == NULL) + continue; + for (guint i = 0; i < parent_physical_ids->len; i++) { + const gchar *parent_physical_id = g_ptr_array_index(parent_physical_ids, i); + if (g_strcmp0(parent_physical_id, fu_device_get_physical_id(device)) == 0) + fu_device_set_parent(device_tmp, device); + } + } + guids = fu_device_get_guids(device); + for (guint j = 0; j < guids->len; j++) { + const gchar *guid = g_ptr_array_index(guids, j); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices, i); + if (fu_device_get_parent(device_tmp) != NULL) + continue; + if (fu_device_has_parent_guid(device_tmp, guid)) + fu_device_set_parent(device_tmp, device); + } + } +} + +static void +fu_engine_set_proxy_device(FuEngine *self, FuDevice *device) +{ + GPtrArray *guids; + g_autoptr(FuDevice) proxy = NULL; + g_autoptr(GPtrArray) devices = NULL; + + if (fu_device_get_proxy(device) != NULL) + return; + if (fu_device_get_proxy_guid(device) == NULL) + return; + + /* find the proxy GUID in any existing device */ + proxy = + fu_device_list_get_by_guid(self->device_list, fu_device_get_proxy_guid(device), NULL); + if (proxy != NULL) { + g_debug("setting proxy of %s to %s for %s", + fu_device_get_id(proxy), + fu_device_get_id(device), + fu_device_get_proxy_guid(device)); + fu_device_set_proxy(device, proxy); + return; + } + + /* are we the parent of an existing device */ + guids = fu_device_get_guids(device); + for (guint j = 0; j < guids->len; j++) { + const gchar *guid = g_ptr_array_index(guids, j); + devices = fu_device_list_get_active(self->device_list); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(devices, i); + if (g_strcmp0(fu_device_get_proxy_guid(device_tmp), guid) == 0) { + g_debug("adding proxy of %s to %s for %s", + fu_device_get_id(device), + fu_device_get_id(device_tmp), + guid); + fu_device_set_proxy(device_tmp, device); + return; + } + } + } + + /* nothing found */ + g_warning("did not find proxy device %s", fu_device_get_proxy_guid(device)); +} + +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_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION) && + fu_device_has_flag(device_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + FwupdRelease *release = fu_device_get_release_default(device_history); + if (fu_version_compare(fu_device_get_version(device), + fwupd_release_get_version(release), + fu_device_get_version_format(device)) != 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) +{ + GPtrArray *disabled_devices; + GPtrArray *device_guids; + g_autoptr(XbNode) component = NULL; + + /* device has no GUIDs set! */ + device_guids = fu_device_get_guids(device); + if (device_guids->len == 0) { + g_warning("no GUIDs for device %s [%s]", + fu_device_get_name(device), + fu_device_get_id(device)); + return; + } + + /* is this GUID disabled */ + disabled_devices = fu_config_get_disabled_devices(self->config); + for (guint i = 0; i < disabled_devices->len; i++) { + const gchar *disabled_guid = g_ptr_array_index(disabled_devices, i); + for (guint j = 0; j < device_guids->len; j++) { + const gchar *device_guid = g_ptr_array_index(device_guids, j); + if (g_strcmp0(disabled_guid, device_guid) == 0) { + g_debug("%s [%s] is disabled [%s], ignoring from %s", + fu_device_get_name(device), + fu_device_get_id(device), + device_guid, + fu_device_get_plugin(device)); + return; + } + } + } + + /* does the device not have an assigned protocol */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && + fu_device_get_protocols(device)->len == 0) { + g_warning("device %s [%s] does not define an update protocol", + fu_device_get_id(device), + fu_device_get_name(device)); + } + +#ifndef SUPPORTED_BUILD + /* we don't know if this device has a signed or unsigned payload */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD) && + !fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_MD_SET_SIGNED)) { + g_warning("%s device does not define payload %s [%s]", + fu_device_get_plugin(device), + fu_device_get_name(device), + fu_device_get_id(device)); + } +#endif + + /* if this device is locked get some metadata from AppStream */ + component = fu_engine_get_component_by_guids(self, device); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_LOCKED)) { + if (component != NULL) { + g_autoptr(XbNode) rel = NULL; + rel = xb_node_query_first(component, "releases/release", NULL); + if (rel != NULL) { + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_local = NULL; + fu_release_set_device(release, device); + if (!fu_engine_load_release(self, + release, + component, + rel, + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { + g_warning("failed to set AppStream release: %s", + error_local->message); + } else { + fu_device_add_release(device, FWUPD_RELEASE(release)); + } + } + } + } + + /* set or clear the SUPPORTED flag */ + fu_engine_ensure_device_supported(self, device); + + /* fixup the name and format as needed */ + fu_engine_md_refresh_device_from_component(self, device, component); + + /* adopt any required children, which may or may not already exist */ + fu_engine_adopt_children(self, device); + + /* set the proxy device if specified by GUID */ + fu_engine_set_proxy_device(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); + } + + /* sometimes inherit flags from recent history */ + fu_engine_device_inherit_history(self, 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); + + if (fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN && + fu_version_guess_format(fu_device_get_version(device)) == FWUPD_VERSION_FORMAT_NUMBER) { + fu_device_inhibit(device, "version-format", "VersionFormat is ambiguous"); + } + + /* no vendor-id, and so no way to lock it down! */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && + fu_device_get_vendor_ids(device)->len == 0) { + fu_device_inhibit(device, "vendor-id", "No vendor ID set"); + } + + /* create new device */ + fu_device_list_add(self->device_list, device); + + /* fix order */ + fu_device_list_depsolve_order(self->device_list, device); + + /* fixup the name and format as needed from cached metadata */ + if (component != NULL) + fu_engine_md_refresh_device_from_component(self, device, component); + + /* match the metadata so clients can tell if the device is worthy */ + fu_engine_ensure_device_supported(self, device); + + fu_engine_emit_changed(self); +} + +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); + if (rules == NULL) + return; + 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 +fu_engine_plugin_config_changed_cb(FuPlugin *plugin, gpointer user_data) +{ + FuEngine *self = FU_ENGINE(user_data); + g_info("config file for %s changed, sending SHUTDOWN", fu_plugin_get_name(plugin)); + fu_engine_set_status(self, FWUPD_STATUS_SHUTDOWN); +} + +static void +fu_engine_context_security_changed_cb(FuContext *ctx, gpointer user_data) +{ + FuEngine *self = FU_ENGINE(user_data); + + /* invalidate host security attributes */ + g_clear_pointer(&self->host_security_id, g_free); + + /* make UI refresh */ + fu_engine_emit_changed(self); +} + +static void +fu_engine_plugin_device_removed_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + FuEngine *self = (FuEngine *)user_data; + 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, fu_device_get_id(device), &error); + if (device_tmp == NULL) { + g_debug("failed to find device %s: %s", fu_device_get_id(device), error->message); + return; + } + + /* get the plugin */ + plugin_old = + fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), &error); + if (plugin_old == NULL) { + g_debug("failed to find plugin %s: %s", + fu_device_get_plugin(device), + error->message); + return; + } + + /* check this came from the same plugin */ + if (g_strcmp0(fu_plugin_get_name(plugin), fu_plugin_get_name(plugin_old)) != 0) { + g_debug("ignoring duplicate removal from %s", fu_plugin_get_name(plugin)); + return; + } + + /* make the UI update */ + fu_device_list_remove(self->device_list, device); + fu_engine_emit_changed(self); +} + +/* this is called by the self tests as well */ +void +fu_engine_add_plugin(FuEngine *self, FuPlugin *plugin) +{ + fu_plugin_list_add(self->plugin_list, plugin); +} + +gboolean +fu_engine_is_uid_trusted(FuEngine *self, guint64 calling_uid) +{ + GArray *trusted; + + /* root is always trusted */ + if (calling_uid == 0) + return TRUE; + + trusted = fu_config_get_trusted_uids(self->config); + for (guint i = 0; i < trusted->len; i++) { + if (calling_uid == g_array_index(trusted, guint64, i)) + return TRUE; + } + return FALSE; +} + +static gboolean +fu_engine_is_plugin_name_disabled(FuEngine *self, const gchar *name) +{ + GPtrArray *disabled = fu_config_get_disabled_plugins(self->config); + for (guint i = 0; i < disabled->len; i++) { + const gchar *name_tmp = g_ptr_array_index(disabled, i); + if (g_strcmp0(name_tmp, name) == 0) + return TRUE; + } + return FALSE; +} + +static gboolean +fu_engine_is_plugin_name_enabled(FuEngine *self, const gchar *name) +{ + if (self->plugin_filter->len == 0) + return TRUE; + for (guint i = 0; i < self->plugin_filter->len; i++) { + const gchar *name_tmp = g_ptr_array_index(self->plugin_filter, i); + if (fu_path_fnmatch(name_tmp, name)) + return TRUE; + } + return FALSE; +} + +void +fu_engine_add_plugin_filter(FuEngine *self, const gchar *plugin_glob) +{ + GString *str; + g_return_if_fail(FU_IS_ENGINE(self)); + g_return_if_fail(plugin_glob != NULL); + str = g_string_new(plugin_glob); + fu_string_replace(str, "-", "_"); + g_ptr_array_add(self->plugin_filter, g_string_free(str, FALSE)); +} + +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; + + if (fu_config_get_enumerate_all_devices(self->config)) + return TRUE; + + xpath = g_strdup_printf("components/component[@type='firmware']/" + "provides/firmware[@type='flashed'][text()='%s']", + guid); + n = xb_silo_query_first(self->silo, xpath, NULL); + return n != NULL; +} + +FuConfig * +fu_engine_get_config(FuEngine *self) +{ + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + return self->config; +} + +const gchar * +fu_engine_get_host_vendor(FuEngine *self) +{ + const gchar *result = NULL; + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + result = fu_context_get_hwid_value(self->ctx, FU_HWIDS_KEY_MANUFACTURER); + return result != NULL ? result : "Unknown Vendor"; +} + +const gchar * +fu_engine_get_host_product(FuEngine *self) +{ + const gchar *result = NULL; + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + result = fu_context_get_hwid_value(self->ctx, FU_HWIDS_KEY_PRODUCT_NAME); + return result != NULL ? result : "Unknown Product"; +} + +const gchar * +fu_engine_get_host_machine_id(FuEngine *self) +{ + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + return self->host_machine_id; +} + +const gchar * +fu_engine_get_host_bkc(FuEngine *self) +{ + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + if (fu_config_get_host_bkc(self->config) == NULL) + return ""; + return fu_config_get_host_bkc(self->config); +} + +#ifdef HAVE_HSI +static void +fu_engine_ensure_security_attrs_tainted(FuEngine *self) +{ + gboolean disabled_plugins = FALSE; + GPtrArray *disabled = fu_config_get_disabled_plugins(self->config); + g_autoptr(FwupdSecurityAttr) attr = + fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS); + fwupd_security_attr_set_plugin(attr, "core"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + + fu_security_attrs_append(self->host_security_attrs, attr); + for (guint i = 0; i < disabled->len; i++) { + const gchar *name_tmp = g_ptr_array_index(disabled, i); + if (!g_str_has_prefix(name_tmp, "test")) { + disabled_plugins = TRUE; + break; + } + } + if (self->plugin_filter->len > 0 || disabled_plugins) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED); +} + +/* + * Get chassis type from SMBIOS data and verify HSI makes sense for it + */ +static gchar * +fu_engine_attrs_calculate_hsi_for_chassis(FuEngine *self) +{ + guint val = + fu_context_get_smbios_integer(self->ctx, FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, 0x05); + + /* if emulating, force the chassis type to be valid */ + if (self->host_emulation && + (val == FU_SMBIOS_CHASSIS_KIND_OTHER || val == FU_SMBIOS_CHASSIS_KIND_UNKNOWN)) { + g_debug("forcing chassis kind [0x%x] to be valid", val); + val = FU_SMBIOS_CHASSIS_KIND_DESKTOP; + } + + switch (val) { + case FU_SMBIOS_CHASSIS_KIND_DESKTOP: + case FU_SMBIOS_CHASSIS_KIND_LOW_PROFILE_DESKTOP: + case FU_SMBIOS_CHASSIS_KIND_MINI_TOWER: + case FU_SMBIOS_CHASSIS_KIND_TOWER: + case FU_SMBIOS_CHASSIS_KIND_PORTABLE: + case FU_SMBIOS_CHASSIS_KIND_LAPTOP: + case FU_SMBIOS_CHASSIS_KIND_NOTEBOOK: + case FU_SMBIOS_CHASSIS_KIND_ALL_IN_ONE: + case FU_SMBIOS_CHASSIS_KIND_SUB_NOTEBOOK: + case FU_SMBIOS_CHASSIS_KIND_LUNCH_BOX: + case FU_SMBIOS_CHASSIS_KIND_MAIN_SERVER: + case FU_SMBIOS_CHASSIS_KIND_TABLET: + case FU_SMBIOS_CHASSIS_KIND_CONVERTIBLE: + case FU_SMBIOS_CHASSIS_KIND_DETACHABLE: + case FU_SMBIOS_CHASSIS_KIND_IOT_GATEWAY: + case FU_SMBIOS_CHASSIS_KIND_EMBEDDED_PC: + case FU_SMBIOS_CHASSIS_KIND_MINI_PC: + case FU_SMBIOS_CHASSIS_KIND_STICK_PC: + return fu_security_attrs_calculate_hsi(self->host_security_attrs, + FU_SECURITY_ATTRS_FLAG_ADD_VERSION); + default: + break; + } + + return g_strdup_printf("HSI:INVALID:chassis[0x%02x]", val); +} + +static gboolean +fu_engine_record_security_attrs(FuEngine *self, GError **error) +{ +#if JSON_CHECK_VERSION(1, 6, 0) + g_autoptr(GPtrArray) attrs_array = NULL; + g_autofree gchar *json = NULL; + + /* convert attrs to json string */ + json = fu_security_attrs_to_json_string(self->host_security_attrs, error); + if (json == NULL) { + g_prefix_error(error, "cannot convert current attrs to string: "); + return FALSE; + } + + /* check that we did not store this already last boot */ + attrs_array = fu_history_get_security_attrs(self->history, 1, error); + if (attrs_array == NULL) { + g_prefix_error(error, "failed to get historical attr: "); + return FALSE; + } + if (attrs_array->len > 0) { + FuSecurityAttrs *attrs_tmp = g_ptr_array_index(attrs_array, 0); + if (fu_security_attrs_equal(attrs_tmp, self->host_security_attrs)) { + g_debug("skipping writing HSI attrs to database as unchanged"); + return TRUE; + } + } + + /* write new values */ + if (!fu_history_add_security_attribute(self->history, + json, + self->host_security_id, + error)) { + g_prefix_error(error, "failed to write to DB: "); + return FALSE; + } +#endif + + /* success */ + return TRUE; +} + +static void +fu_engine_security_attrs_depsolve(FuEngine *self) +{ + g_autoptr(GPtrArray) items = NULL; + + /* set the fallback names for clients without native translations */ + items = fu_security_attrs_get_all(self->host_security_attrs); + for (guint i = 0; i < items->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(items, i); + if (fwupd_security_attr_get_name(attr) == NULL) { + g_autofree gchar *name_tmp = fu_security_attr_get_name(attr); + if (name_tmp == NULL) { + g_warning("failed to get fallback for %s", + fwupd_security_attr_get_appstream_id(attr)); + continue; + } + fwupd_security_attr_set_name(attr, name_tmp); + } + if (fwupd_security_attr_get_title(attr) == NULL) + fwupd_security_attr_set_title(attr, fu_security_attr_get_title(attr)); + if (fwupd_security_attr_get_description(attr) == NULL) { + fwupd_security_attr_set_description(attr, + fu_security_attr_get_description(attr)); + } + } + + /* set the obsoletes flag for each attr */ + fu_security_attrs_depsolve(self->host_security_attrs); + + /* distil into one simple string */ + g_free(self->host_security_id); + self->host_security_id = fu_engine_attrs_calculate_hsi_for_chassis(self); +} +#endif + +static gboolean +fu_engine_security_attrs_from_json(FuEngine *self, JsonNode *json_node, GError **error) +{ + JsonObject *obj; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + + /* not supplied */ + obj = json_node_get_object(json_node); + if (!json_object_has_member(obj, "SecurityAttributes")) + return TRUE; + if (!fu_security_attrs_from_json(self->host_security_attrs, json_node, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_devices_from_json(FuEngine *self, JsonNode *json_node, GError **error) +{ + JsonArray *array; + JsonObject *obj; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + + /* not supplied */ + obj = json_node_get_object(json_node); + if (!json_object_has_member(obj, "Devices")) + return TRUE; + + /* this has to exist */ + array = json_object_get_array_member(obj, "Devices"); + for (guint i = 0; i < json_array_get_length(array); i++) { + JsonNode *node_tmp = json_array_get_element(array, i); + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + if (!fwupd_device_from_json(FWUPD_DEVICE(device), node_tmp, error)) + return FALSE; + fu_device_set_plugin(device, "dummy"); + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_IS_EMULATED); + if (!fu_device_setup(device, error)) + return FALSE; + fu_engine_add_device(self, device); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_load_host_emulation(FuEngine *self, const gchar *fn, GError **error) +{ + g_autoptr(JsonParser) parser = json_parser_new(); + g_autoptr(GFile) file = g_file_new_for_path(fn); + g_autoptr(GInputStream) istream_json = NULL; + g_autoptr(GInputStream) istream_raw = NULL; + g_autoptr(FwupdSecurityAttr) attr = NULL; + g_autoptr(FuBiosSettings) bios_settings = fu_context_get_bios_settings(self->ctx); + + /* add an attr so we know this is emulated and do not offer to upload results */ + attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_HOST_EMULATION); + fwupd_security_attr_set_plugin(attr, "core"); + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fu_security_attrs_append(self->host_security_attrs, attr); + + /* add from file */ + istream_raw = G_INPUT_STREAM(g_file_read(file, NULL, error)); + if (istream_raw == NULL) + return FALSE; + if (g_str_has_suffix(fn, ".gz")) { + g_autoptr(GConverter) conv = + G_CONVERTER(g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP)); + istream_json = g_converter_input_stream_new(istream_raw, conv); + } else { + istream_json = g_object_ref(istream_raw); + } + if (!json_parser_load_from_stream(parser, istream_json, NULL, error)) + return FALSE; + if (!fu_engine_devices_from_json(self, json_parser_get_root(parser), error)) + return FALSE; + if (!fu_engine_security_attrs_from_json(self, json_parser_get_root(parser), error)) + return FALSE; + if (!fu_bios_settings_from_json(bios_settings, json_parser_get_root(parser), error)) + return FALSE; + +#ifdef HAVE_HSI + /* depsolve */ + fu_engine_security_attrs_depsolve(self); +#endif + + /* success */ + return TRUE; +} + +static void +fu_engine_ensure_security_attrs(FuEngine *self) +{ +#ifdef HAVE_HSI + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + g_autoptr(GPtrArray) devices = fu_device_list_get_all(self->device_list); + g_autoptr(GError) error = NULL; + + /* already valid */ + if (self->host_security_id != NULL || self->host_emulation) + return; + + /* clear old values */ + fu_security_attrs_remove_all(self->host_security_attrs); + + /* built in */ + fu_engine_ensure_security_attrs_tainted(self); + + /* call into devices */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + fu_device_add_security_attrs(device, self->host_security_attrs); + } + + /* call into plugins */ + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index(plugins, j); + fu_plugin_runner_add_security_attrs(plugin_tmp, self->host_security_attrs); + } + + /* depsolve */ + fu_engine_security_attrs_depsolve(self); + + /* record into the database (best effort) */ + if (!fu_engine_record_security_attrs(self, &error)) + g_warning("failed to record HSI attributes: %s", error->message); +#endif +} + +const gchar * +fu_engine_get_host_security_id(FuEngine *self) +{ + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + fu_engine_ensure_security_attrs(self); + return self->host_security_id; +} + +FuSecurityAttrs * +fu_engine_get_host_security_attrs(FuEngine *self) +{ + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + fu_engine_ensure_security_attrs(self); + return g_object_ref(self->host_security_attrs); +} + +FuSecurityAttrs * +fu_engine_get_host_security_events(FuEngine *self, guint limit, GError **error) +{ + g_autoptr(FuSecurityAttrs) events = fu_security_attrs_new(); +#if JSON_CHECK_VERSION(1, 6, 0) + g_autoptr(GPtrArray) attrs_array = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + + attrs_array = fu_history_get_security_attrs(self->history, limit, error); + if (attrs_array == NULL) + return NULL; + for (guint i = 1; i < attrs_array->len; i++) { + FuSecurityAttrs *attrs_new = g_ptr_array_index(attrs_array, i - 1); + FuSecurityAttrs *attrs_old = g_ptr_array_index(attrs_array, i - 0); + g_autoptr(GPtrArray) diffs = fu_security_attrs_compare(attrs_old, attrs_new); + for (guint j = 0; j < diffs->len; j++) { + FwupdSecurityAttr *attr = g_ptr_array_index(diffs, j); + if (fwupd_security_attr_get_title(attr) == NULL) { + fwupd_security_attr_set_title(attr, + fu_security_attr_get_title(attr)); + } + if (fwupd_security_attr_get_description(attr) == NULL) { + fwupd_security_attr_set_description( + attr, + fu_security_attr_get_description(attr)); + } + fu_security_attrs_append_internal(events, attr); + } + } +#endif + + /* success */ + return g_steal_pointer(&events); +} + +static void +fu_engine_load_plugins_filename(FuEngine *self, const gchar *filename, FuProgress *progress) +{ + g_autofree gchar *name = NULL; + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(GError) error_local = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_name(progress, filename); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 97, "add"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 3, "open"); + + /* sanity check */ + name = fu_plugin_guess_name_from_fn(filename); + if (name == NULL) { + fu_progress_finished(progress); + return; + } + + /* open module */ + plugin = fu_plugin_new(self->ctx); + fu_plugin_set_name(plugin, name); + fu_engine_add_plugin(self, plugin); + fu_progress_step_done(progress); + + /* open the plugin and call ->load() */ + if (!fu_plugin_open(plugin, filename, &error_local)) + g_warning("cannot load: %s", error_local->message); + fu_progress_step_done(progress); +} + +static void +fu_engine_load_plugins_filenames(FuEngine *self, GPtrArray *filenames, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, filenames->len); + for (guint i = 0; i < filenames->len; i++) { + const gchar *filename = g_ptr_array_index(filenames, i); + fu_engine_load_plugins_filename(self, filename, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + } +} + +static void +fu_engine_load_plugins_builtins(FuEngine *self, FuProgress *progress) +{ + guint steps = 0; + + /* count possible steps */ + for (guint i = 0; fu_plugin_externals[i] != NULL; i++) + steps++; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, steps); + for (guint i = 0; fu_plugin_externals[i] != NULL; i++) { + GType plugin_gtype = fu_plugin_externals[i](); + g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(plugin_gtype, self->ctx); + fu_engine_add_plugin(self, plugin); + fu_progress_step_done(progress); + } +} + +static gboolean +fu_engine_load_plugins(FuEngine *self, + FuEngineLoadFlags flags, + FuProgress *progress, + GError **error) +{ + const gchar *fn; + g_autoptr(GDir) dir = NULL; + g_autoptr(GPtrArray) filenames = g_ptr_array_new_with_free_func(g_free); + g_autofree gchar *plugin_path = NULL; + g_autofree gchar *suffix = g_strdup_printf(".%s", G_MODULE_SUFFIX); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 13, "search"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 87, "load"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 5, "load-builtins"); + + /* search */ + plugin_path = fu_path_from_kind(FU_PATH_KIND_LIBDIR_PKG); + dir = g_dir_open(plugin_path, 0, error); + if (dir == NULL) + return FALSE; + while ((fn = g_dir_read_name(dir)) != NULL) { + /* ignore non-plugins */ + if (!g_str_has_suffix(fn, suffix)) + continue; + g_ptr_array_add(filenames, g_build_filename(plugin_path, fn, NULL)); + } + fu_progress_step_done(progress); + + /* load */ + fu_engine_load_plugins_filenames(self, filenames, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + + /* load builtins */ + if (flags & FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS) + fu_engine_load_plugins_builtins(self, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_plugins_init(FuEngine *self, FuProgress *progress, GError **error) +{ + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + g_autoptr(GPtrArray) plugins_disabled = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GPtrArray) plugins_disabled_rt = g_ptr_array_new_with_free_func(g_free); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, plugins->len); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + const gchar *name = fu_plugin_get_name(plugin); + + /* progress */ + fu_progress_set_name(fu_progress_get_child(progress), name); + + /* is disabled */ + if (fu_engine_is_plugin_name_disabled(self, name) || + !fu_engine_is_plugin_name_enabled(self, name)) { + g_ptr_array_add(plugins_disabled, g_strdup(name)); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED); + fu_progress_step_done(progress); + continue; + } + + /* init plugin, adding device and firmware GTypes */ + fu_plugin_runner_init(plugin); + + /* runtime disabled */ + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) { + g_ptr_array_add(plugins_disabled_rt, g_strdup(name)); + fu_progress_step_done(progress); + continue; + } + + /* watch for changes */ + g_signal_connect(FU_PLUGIN(plugin), + "device-added", + G_CALLBACK(fu_engine_plugin_device_added_cb), + self); + g_signal_connect(FU_PLUGIN(plugin), + "device-removed", + G_CALLBACK(fu_engine_plugin_device_removed_cb), + self); + g_signal_connect(FU_PLUGIN(plugin), + "device-register", + G_CALLBACK(fu_engine_plugin_device_register_cb), + self); + g_signal_connect(FU_PLUGIN(plugin), + "check-supported", + G_CALLBACK(fu_engine_plugin_check_supported_cb), + self); + g_signal_connect(FU_PLUGIN(plugin), + "rules-changed", + G_CALLBACK(fu_engine_plugin_rules_changed_cb), + self); + g_signal_connect(FU_PLUGIN(plugin), + "config-changed", + G_CALLBACK(fu_engine_plugin_config_changed_cb), + self); + fu_progress_step_done(progress); + } + + /* show list */ + if (plugins_disabled->len > 0) { + g_autofree gchar *str = NULL; + g_ptr_array_add(plugins_disabled, NULL); + str = g_strjoinv(", ", (gchar **)plugins_disabled->pdata); + g_debug("plugins disabled: %s", str); + } + if (plugins_disabled_rt->len > 0) { + g_autofree gchar *str = NULL; + g_ptr_array_add(plugins_disabled_rt, NULL); + str = g_strjoinv(", ", (gchar **)plugins_disabled_rt->pdata); + g_debug("plugins runtime-disabled: %s", str); + } + + /* depsolve into the correct order */ + if (!fu_plugin_list_depsolve(self->plugin_list, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_cleanup_state(GError **error) +{ + const gchar *filenames[] = {"/var/cache/app-info/xmls/fwupd-verify.xml", + "/var/cache/app-info/xmls/fwupd.xml", + NULL}; + for (guint i = 0; filenames[i] != NULL; i++) { + g_autoptr(GFile) file = g_file_new_for_path(filenames[i]); + if (g_file_query_exists(file, NULL)) { + if (!g_file_delete(file, NULL, error)) + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_engine_apply_default_bios_settings_policy(FuEngine *self, GError **error) +{ + const gchar *tmp; + g_autofree gchar *base = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + g_autofree gchar *dirname = g_build_filename(base, "bios-settings.d", NULL); + g_autoptr(FuBiosSettings) new_bios_settings = fu_bios_settings_new(); + g_autoptr(GHashTable) hashtable = NULL; + g_autoptr(GDir) dir = NULL; + + if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) + return TRUE; + + dir = g_dir_open(dirname, 0, error); + if (dir == NULL) + return FALSE; + while ((tmp = g_dir_read_name(dir)) != NULL) { + g_autofree gchar *fn = NULL; + if (!g_str_has_suffix(tmp, ".json")) + continue; + fn = g_build_filename(dirname, tmp, NULL); + g_debug("Loading default BIOS settings policy from %s", fn); + if (!fu_bios_settings_from_json_file(new_bios_settings, fn, error)) + return FALSE; + } + hashtable = fu_bios_settings_to_hash_kv(new_bios_settings); + return fu_engine_modify_bios_settings(self, hashtable, TRUE, error); +} + +static void +fu_engine_check_firmware_attributes(FuEngine *self, FuDevice *device, gboolean added) +{ + const gchar *subsystem; + + if (!FU_IS_UDEV_DEVICE(device)) + return; + if (self->host_emulation) + return; + subsystem = fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)); + if (g_strcmp0(subsystem, "firmware-attributes") == 0) { + g_autoptr(GError) error = NULL; + if (added) { + g_autoptr(FuBiosSettings) settings = + fu_context_get_bios_settings(self->ctx); + g_autoptr(GPtrArray) items = fu_bios_settings_get_all(settings); + + if (items->len > 0) { + g_debug("ignoring add event for already loaded settings"); + return; + } + } + if (!fu_context_reload_bios_settings(self->ctx, &error)) { + g_debug("%s", error->message); + return; + } + if (!fu_engine_apply_default_bios_settings_policy(self, &error)) { + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) + g_debug("%s", error->message); + else + g_warning("Failed to apply BIOS settings policy: %s", + error->message); + return; + } + } +} + +static void +fu_engine_backend_device_removed_cb(FuBackend *backend, FuDevice *device, FuEngine *self) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* if this is for firmware attributes, reload that part of the daemon */ + fu_engine_check_firmware_attributes(self, device, FALSE); + + /* debug */ + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_debug("%s removed %s", + fu_backend_get_name(backend), + fu_device_get_backend_id(device)); + } + + /* 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_tmp = g_ptr_array_index(devices, i); + if (g_strcmp0(fu_device_get_backend_id(device_tmp), + fu_device_get_backend_id(device)) == 0) { + if (fu_device_has_internal_flag(device_tmp, + FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE)) { + g_debug("not auto-removing backend device %s [%s] due to flags", + fu_device_get_name(device_tmp), + fu_device_get_id(device_tmp)); + continue; + } + g_debug("auto-removing backend device %s [%s]", + fu_device_get_name(device_tmp), + fu_device_get_id(device_tmp)); + fu_device_list_remove(self->device_list, device_tmp); + fu_engine_emit_changed(self); + } + } +} + +static gboolean +fu_engine_backend_device_added_run_plugin(FuEngine *self, + FuDevice *device, + const gchar *plugin_name, + FuProgress *progress, + GError **error) +{ + FuPlugin *plugin; + + /* find plugin */ + fu_progress_set_name(progress, plugin_name); + plugin = fu_plugin_list_find_by_name(self->plugin_list, plugin_name, error); + if (plugin == NULL) + return FALSE; + + /* run the ->probe() then ->setup() vfuncs */ + if (!fu_plugin_runner_backend_device_added(plugin, device, progress, error)) { +#ifdef SUPPORTED_BUILD + /* sanity check */ + if (*error == NULL) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "%s failed but no error set", + fu_device_get_backend_id(device)); + return FALSE; + } +#endif + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_engine_backend_device_added_run_plugins(FuEngine *self, FuDevice *device, FuProgress *progress) +{ + g_autoptr(GPtrArray) possible_plugins = fu_device_get_possible_plugins(device); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, possible_plugins->len); + for (guint i = 0; i < possible_plugins->len; i++) { + const gchar *plugin_name = g_ptr_array_index(possible_plugins, i); + g_autoptr(GError) error_local = NULL; + if (!fu_engine_backend_device_added_run_plugin(self, + device, + plugin_name, + fu_progress_get_child(progress), + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_debug("%s ignoring: %s", + plugin_name, + error_local->message); + } + } else { + g_warning("failed to add device %s: %s", + fu_device_get_backend_id(device), + error_local->message); + } + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_CHILD_FINISHED); + fu_progress_step_done(progress); + continue; + } + fu_progress_step_done(progress); + } +} + +static void +fu_engine_backend_device_added(FuEngine *self, FuDevice *device, FuProgress *progress) +{ + g_autoptr(GError) error_local = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_set_name(progress, fu_device_get_backend_id(device)); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 50, "probe-baseclass"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 50, "query-possible-plugins"); + + /* super useful for plugin development */ + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_autofree gchar *str = fu_device_to_string(FU_DEVICE(device)); + g_debug("%s added %s", fu_device_get_backend_id(device), str); + } + + /* add any extra quirks */ + fu_device_set_context(device, self->ctx); + if (!fu_device_probe(device, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning("failed to probe device %s: %s", + fu_device_get_backend_id(device), + error_local->message); + } else if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_debug("failed to probe device %s : %s", + fu_device_get_backend_id(device), + error_local->message); + } + fu_progress_finished(progress); + return; + } + fu_progress_step_done(progress); + + /* super useful for plugin development */ + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_autofree gchar *str = fu_device_to_string(FU_DEVICE(device)); + g_debug("%s added %s", fu_device_get_backend_id(device), str); + } + + /* if this is for firmware attributes, reload that part of the daemon */ + fu_engine_check_firmware_attributes(self, device, TRUE); + + /* can be specified using a quirk */ + fu_engine_backend_device_added_run_plugins(self, device, fu_progress_get_child(progress)); + fu_progress_step_done(progress); +} + +static void +fu_engine_backend_device_added_cb(FuBackend *backend, FuDevice *device, FuEngine *self) +{ + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + fu_engine_backend_device_added(self, device, progress); +} + +static void +fu_engine_backend_device_changed_cb(FuBackend *backend, FuDevice *device, FuEngine *self) +{ + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + g_autoptr(GPtrArray) devices = NULL; + + /* debug */ + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_debug("%s changed %s", + fu_backend_get_name(backend), + fu_device_get_physical_id(device)); + } + + /* 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_tmp = g_ptr_array_index(devices, i); + if (!FU_IS_UDEV_DEVICE(device_tmp)) + continue; + if (g_strcmp0(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device_tmp)), + fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device))) == 0) { + fu_udev_device_emit_changed(FU_UDEV_DEVICE(device)); + } + } + + /* run all plugins */ + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index(plugins, j); + g_autoptr(GError) error = NULL; + if (!fu_plugin_runner_backend_device_changed(plugin_tmp, device, &error)) { +#ifdef SUPPORTED_BUILD + /* sanity check */ + if (error == NULL) { + g_critical( + "failed to change device %s: exec failed but no error set!", + fu_device_get_backend_id(device)); + continue; + } +#endif + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("%s ignoring: %s", + fu_plugin_get_name(plugin_tmp), + error->message); + continue; + } + g_warning("%s failed to change device %s: %s", + fu_plugin_get_name(plugin_tmp), + fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)), + error->message); + } + } +} + +static void +fu_engine_load_quirks_for_hwid(FuEngine *self, const gchar *hwid) +{ + FuPlugin *plugin; + const gchar *value; + g_auto(GStrv) plugins = NULL; + + /* does prefixed quirk exist */ + value = fu_context_lookup_quirk_by_id(self->ctx, hwid, FU_QUIRKS_PLUGIN); + if (value == NULL) + return; + plugins = g_strsplit(value, ",", -1); + for (guint i = 0; plugins[i] != NULL; i++) { + g_autoptr(GError) error_local = NULL; + plugin = fu_plugin_list_find_by_name(self->plugin_list, plugins[i], &error_local); + if (plugin == NULL) { + g_debug("no %s plugin for HwId %s: %s", + plugins[i], + hwid, + error_local->message); + continue; + } + g_debug("enabling %s due to HwId %s", plugins[i], hwid); + fu_plugin_remove_flag(plugin, FWUPD_PLUGIN_FLAG_REQUIRE_HWID); + } +} + +static gboolean +fu_engine_update_history_device(FuEngine *self, FuDevice *dev_history, GError **error) +{ + FuPlugin *plugin; + FwupdRelease *rel_history; + g_autofree gchar *btime = NULL; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GHashTable) metadata_device = NULL; + + /* is in the device list */ + dev = fu_device_list_get_by_id(self->device_list, fu_device_get_id(dev_history), error); + if (dev == NULL) + return FALSE; + + /* does the installed version match what we tried to install + * before fwupd was restarted */ + rel_history = fu_device_get_release_default(dev_history); + if (rel_history == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no release for history FuDevice"); + return FALSE; + } + + /* is this the same boot time as when we scheduled the update, + * i.e. has fwupd been restarted before we rebooted */ + btime = fu_engine_get_boot_time(); + if (g_strcmp0(fwupd_release_get_metadata_item(rel_history, "BootTime"), btime) == 0) { + g_debug("service restarted, but no reboot has taken place"); + + /* if it needed reboot then, it also needs it now... */ + if (fu_device_get_update_state(dev_history) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { + g_debug("inheriting needs-reboot for %s", fu_device_get_name(dev)); + fu_device_set_update_state(dev, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + } + return TRUE; + } + + /* save any additional report metadata */ + metadata_device = fu_device_report_metadata_post(dev); + if (metadata_device != NULL && g_hash_table_size(metadata_device) > 0) { + fwupd_release_add_metadata(rel_history, metadata_device); + if (!fu_history_set_device_metadata(self->history, + fu_device_get_id(dev_history), + fwupd_release_get_metadata(rel_history), + error)) { + g_prefix_error(error, "failed to set metadata: "); + return FALSE; + } + } + + /* the system is running with the new firmware version */ + if (fu_version_compare(fu_device_get_version(dev), + fwupd_release_get_version(rel_history), + fu_device_get_version_format(dev)) == 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_format(dev_history, fu_device_get_version_format(dev)); + fu_device_set_version(dev_history, fu_device_get_version(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, error); + } + + /* does the plugin know the update failure */ + plugin = fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(dev), error); + if (plugin == NULL) + return FALSE; + if (!fu_plugin_runner_get_results(plugin, dev, error)) + 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 && + fu_device_get_update_state(dev) != FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { + g_debug("falling back to generic failure"); + fu_device_set_update_state(dev_history, FWUPD_UPDATE_STATE_FAILED); + fu_device_set_update_error(dev_history, "failed to run update on reboot"); + } else { + fu_device_set_update_state(dev_history, fu_device_get_update_state(dev)); + fu_device_set_update_error(dev_history, fu_device_get_update_error(dev)); + } + + /* update the state in the database */ + return fu_history_modify_device(self->history, dev_history, error); +} + +static gboolean +fu_engine_update_history_database(FuEngine *self, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* get any devices */ + devices = fu_history_get_devices(self->history, error); + if (devices == NULL) + return FALSE; + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(GError) error_local = NULL; + + /* not in the required state */ + if (fu_device_get_update_state(dev) != FWUPD_UPDATE_STATE_NEEDS_REBOOT && + fu_device_get_update_state(dev) != FWUPD_UPDATE_STATE_PENDING) + continue; + + /* try to save the new update-state, but ignoring any error */ + if (!fu_engine_update_history_device(self, dev, &error_local)) { + g_warning("failed to update history database: %s", error_local->message); + } + } + return TRUE; +} + +static void +fu_engine_ensure_client_certificate(FuEngine *self) +{ + g_autoptr(GBytes) blob = g_bytes_new_static(NULL, 0); + g_autoptr(GError) error = NULL; + g_autoptr(JcatBlob) jcat_sig = NULL; + g_autoptr(JcatEngine) jcat_engine = NULL; + + /* create keyring and sign dummy data to ensure certificate exists */ + jcat_engine = jcat_context_get_engine(self->jcat_context, JCAT_BLOB_KIND_PKCS7, &error); + if (jcat_engine == NULL) { + g_message("failed to create keyring: %s", error->message); + return; + } + jcat_sig = jcat_engine_self_sign(jcat_engine, blob, JCAT_SIGN_FLAG_NONE, &error); + if (jcat_sig == NULL) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_debug("client certificate now exists: %s", error->message); + return; + } + g_message("failed to sign using keyring: %s", error->message); + return; + } + g_debug("client certificate exists and working"); +} + +static void +fu_engine_context_set_battery_threshold(FuContext *ctx) +{ + guint64 minimum_battery; + g_autofree gchar *battery_str = NULL; + g_autofree gchar *vendor = NULL; + + vendor = fu_context_get_hwid_replace_value(ctx, FU_HWIDS_KEY_MANUFACTURER, NULL); + if (vendor != NULL) { + g_autofree gchar *vendor_guid = fwupd_guid_hash_string(vendor); + battery_str = g_strdup( + fu_context_lookup_quirk_by_id(ctx, vendor_guid, FU_QUIRKS_BATTERY_THRESHOLD)); + } + if (battery_str == NULL) { + minimum_battery = MINIMUM_BATTERY_PERCENTAGE_FALLBACK; + } else { + g_autoptr(GError) error_local = NULL; + if (!fu_strtoull(battery_str, &minimum_battery, 0, 100, &error_local)) { + g_warning("invalid minimum battery level specified: %s", + error_local->message); + minimum_battery = MINIMUM_BATTERY_PERCENTAGE_FALLBACK; + } + } + fu_context_set_battery_threshold(ctx, minimum_battery); +} + +static gboolean +fu_engine_ensure_paths_exist(GError **error) +{ + FuPathKind path_kinds[] = {FU_PATH_KIND_LOCALSTATEDIR_QUIRKS, + FU_PATH_KIND_LOCALSTATEDIR_METADATA, + FU_PATH_KIND_LOCALSTATEDIR_REMOTES, + FU_PATH_KIND_CACHEDIR_PKG, + FU_PATH_KIND_LAST}; + for (guint i = 0; path_kinds[i] != FU_PATH_KIND_LAST; i++) { + g_autofree gchar *fn = fu_path_from_kind(path_kinds[i]); + if (!fu_path_mkdir(fn, error)) + return FALSE; + } + return TRUE; +} + +static void +fu_engine_local_metadata_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuEngine *self = FU_ENGINE(user_data); + fu_engine_metadata_changed(self); +} + +static gboolean +fu_engine_load_local_metadata_watches(FuEngine *self, GError **error) +{ + const FuPathKind path_kinds[] = {FU_PATH_KIND_DATADIR_PKG, FU_PATH_KIND_LOCALSTATEDIR_PKG}; + + /* add the watches even if the directory does not exist */ + for (guint i = 0; i < G_N_ELEMENTS(path_kinds); i++) { + GFileMonitor *monitor; + g_autoptr(GFile) file = NULL; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *base = fu_path_from_kind(path_kinds[i]); + g_autofree gchar *fn = g_build_filename(base, "local.d", NULL); + + file = g_file_new_for_path(fn); + monitor = g_file_monitor_directory(file, G_FILE_MONITOR_NONE, NULL, &error_local); + if (monitor == NULL) { + g_warning("failed to watch %s: %s", fn, error_local->message); + continue; + } + g_signal_connect(monitor, + "changed", + G_CALLBACK(fu_engine_local_metadata_changed_cb), + self); + g_ptr_array_add(self->local_monitors, g_steal_pointer(&monitor)); + } + + /* success */ + return TRUE; +} + +#ifdef _WIN32 +static gchar * +fu_common_win32_registry_get_string(HKEY hkey, + const gchar *subkey, + const gchar *value, + GError **error) +{ + gchar buf[255] = {'\0'}; + DWORD bufsz = sizeof(buf); + LSTATUS rc; + + rc = RegGetValue(hkey, subkey, value, RRF_RT_REG_SZ, NULL, (PVOID)&buf, &bufsz); + if (rc != ERROR_SUCCESS) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVAL, + "Failed to get registry string %s [0x%lX]", + subkey, + (unsigned long)rc); + return NULL; + } + return g_strndup(buf, bufsz); +} +#endif + +static gboolean +fu_engine_backends_coldplug_backend_add_devices(FuEngine *self, + FuBackend *backend, + FuProgress *progress, + GError **error) +{ + g_autoptr(GPtrArray) devices = fu_backend_get_devices(backend); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, devices->len); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + fu_engine_backend_device_added(self, device, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_backends_coldplug_backend(FuEngine *self, + FuBackend *backend, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_set_name(progress, fu_backend_get_name(backend)); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "coldplug"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "add-devices"); + + /* coldplug */ + if (!fu_backend_coldplug(backend, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* add */ + fu_engine_backends_coldplug_backend_add_devices(self, + backend, + fu_progress_get_child(progress), + error); + fu_progress_step_done(progress); + + /* success */ + g_signal_connect(FU_BACKEND(backend), + "device-added", + G_CALLBACK(fu_engine_backend_device_added_cb), + self); + g_signal_connect(FU_BACKEND(backend), + "device-removed", + G_CALLBACK(fu_engine_backend_device_removed_cb), + self); + g_signal_connect(FU_BACKEND(backend), + "device-changed", + G_CALLBACK(fu_engine_backend_device_changed_cb), + self); + return TRUE; +} + +static void +fu_engine_backends_coldplug(FuEngine *self, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, self->backends->len); + for (guint i = 0; i < self->backends->len; i++) { + FuBackend *backend = g_ptr_array_index(self->backends, i); + g_autoptr(GError) error_backend = NULL; + + if (!fu_backend_get_enabled(backend)) { + fu_progress_step_done(progress); + continue; + } + if (!fu_engine_backends_coldplug_backend(self, + backend, + fu_progress_get_child(progress), + &error_backend)) { + g_warning("failed to coldplug backend %s: %s", + fu_backend_get_name(backend), + error_backend->message); + fu_progress_step_done(progress); + continue; + } + fu_progress_step_done(progress); + } +} + +/** + * fu_engine_load: + * @self: a #FuEngine + * @flags: engine load flags, e.g. %FU_ENGINE_LOAD_FLAG_READONLY + * @progress: a #FuProgress + * @error: (nullable): optional return location for an error + * + * Load the firmware update engine so it is ready for use. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_load(FuEngine *self, FuEngineLoadFlags flags, FuProgress *progress, GError **error) +{ + FuPlugin *plugin_uefi; + FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE; + GPtrArray *guids; + const gchar *host_emulate = g_getenv("FWUPD_HOST_EMULATE"); + guint backend_cnt = 0; + g_autoptr(GPtrArray) checksums_approved = NULL; + g_autoptr(GPtrArray) checksums_blocked = NULL; + g_autoptr(GError) error_quirks = NULL; + g_autoptr(GError) error_local = 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; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "read-config"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "read-remotes"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "ensure-client-cert"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "write-db"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-plugins"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-quirks"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-hwinfo"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-appstream"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "backend-setup"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "plugins-init"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "hwid-quirks"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "plugins-setup"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 3, "plugins-coldplug"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 90, "backend-coldplug"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "update-history-db"); + + /* sanity check libraries are in sync with daemon */ + if (g_strcmp0(fwupd_version_string(), VERSION) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVAL, + "libfwupd version %s does not match daemon %s", + fwupd_version_string(), + VERSION); + return FALSE; + } + if (g_strcmp0(fu_version_string(), VERSION) != 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVAL, + "libfwupdplugin version %s does not match daemon %s", + fu_version_string(), + VERSION); + return FALSE; + } + + /* cache machine ID so we can use it from a sandboxed app */ +#ifdef _WIN32 + self->host_machine_id = + fu_common_win32_registry_get_string(HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Cryptography", + "MachineGuid", + &error_local); +#else + self->host_machine_id = fwupd_build_machine_id("fwupd", &error_local); +#endif + if (self->host_machine_id == NULL) + g_debug("failed to build machine-id: %s", error_local->message); + + /* ensure these exist before starting */ + if (!fu_engine_ensure_paths_exist(error)) + return FALSE; + + /* read config file */ + if (!fu_config_load(self->config, error)) { + g_prefix_error(error, "Failed to load config: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* set the hardcoded ESP */ + if (fu_config_get_esp_location(self->config) != NULL) { + g_autoptr(FuVolume) vol = NULL; + vol = fu_volume_new_esp_for_path(fu_config_get_esp_location(self->config), error); + if (vol == NULL) + return FALSE; + fu_context_add_esp_volume(self->ctx, vol); + } + + /* read remotes */ + if (flags & FU_ENGINE_LOAD_FLAG_REMOTES) { + FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE; + if (flags & FU_ENGINE_LOAD_FLAG_READONLY) + remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS; + if (flags & FU_ENGINE_LOAD_FLAG_NO_CACHE) + remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_NO_CACHE; + if (!fu_remote_list_load(self->remote_list, remote_list_flags, error)) { + g_prefix_error(error, "Failed to load remotes: "); + return FALSE; + } + } + fu_progress_step_done(progress); + + /* create client certificate */ + fu_engine_ensure_client_certificate(self); + fu_progress_step_done(progress); + + /* get hardcoded approved and blocked firmware */ + checksums_approved = fu_config_get_approved_firmware(self->config); + for (guint i = 0; i < checksums_approved->len; i++) { + const gchar *csum = g_ptr_array_index(checksums_approved, i); + fu_engine_add_approved_firmware(self, csum); + } + checksums_blocked = fu_config_get_blocked_firmware(self->config); + for (guint i = 0; i < checksums_blocked->len; i++) { + const gchar *csum = g_ptr_array_index(checksums_blocked, i); + fu_engine_add_blocked_firmware(self, csum); + } + + /* get extra firmware saved to the database */ + checksums_approved = fu_history_get_approved_firmware(self->history, error); + if (checksums_approved == NULL) + return FALSE; + for (guint i = 0; i < checksums_approved->len; i++) { + const gchar *csum = g_ptr_array_index(checksums_approved, i); + fu_engine_add_approved_firmware(self, csum); + } + checksums_blocked = fu_history_get_blocked_firmware(self->history, error); + if (checksums_blocked == NULL) + return FALSE; + for (guint i = 0; i < checksums_blocked->len; i++) { + const gchar *csum = g_ptr_array_index(checksums_blocked, i); + fu_engine_add_blocked_firmware(self, csum); + } + fu_progress_step_done(progress); + + /* load plugins early, as we have to call ->load() *before* building quirk silo */ + if (!fu_engine_load_plugins(self, flags, fu_progress_get_child(progress), error)) { + g_prefix_error(error, "failed to load plugins: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* migrate per-plugin settings into daemon.conf */ + plugin_uefi = fu_plugin_list_find_by_name(self->plugin_list, "uefi_capsule", NULL); + if (plugin_uefi != NULL) { + const gchar *tmp = fu_plugin_get_config_value(plugin_uefi, "OverrideESPMountPoint"); + if (tmp != NULL && g_strcmp0(tmp, fu_config_get_esp_location(self->config)) != 0) { + g_info("migrating OverrideESPMountPoint=%s to EspLocation", tmp); + if (!fu_config_set_key_value(self->config, "EspLocation", tmp, error)) + return FALSE; + } + } + + /* set up idle exit */ + if ((flags & FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES) == 0) + fu_idle_set_timeout(self->idle, fu_config_get_idle_timeout(self->config)); + + /* on a read-only filesystem don't care about the cache GUID */ + if (flags & FU_ENGINE_LOAD_FLAG_READONLY) + quirks_flags |= FU_QUIRKS_LOAD_FLAG_READONLY_FS; + if (flags & FU_ENGINE_LOAD_FLAG_NO_CACHE) + quirks_flags |= FU_QUIRKS_LOAD_FLAG_NO_CACHE; + if (!fu_context_load_quirks(self->ctx, quirks_flags, &error_quirks)) + g_warning("Failed to load quirks: %s", error_quirks->message); + fu_progress_step_done(progress); + + /* load SMBIOS and the hwids */ + if (flags & FU_ENGINE_LOAD_FLAG_HWINFO) + fu_context_load_hwinfo(self->ctx, NULL); + fu_progress_step_done(progress); + + /* load AppStream metadata */ + if (!fu_engine_load_metadata_store(self, flags, error)) { + g_prefix_error(error, "Failed to load AppStream data: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* watch the local.d directories for changes */ + if (!fu_engine_load_local_metadata_watches(self, error)) + return FALSE; + + /* add the "built-in" firmware types */ + fu_context_add_firmware_gtype(self->ctx, "raw", FU_TYPE_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "dfu", FU_TYPE_DFU_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "fdt", FU_TYPE_FDT_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "fit", FU_TYPE_FIT_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "dfuse", FU_TYPE_DFUSE_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "ifwi-cpd", FU_TYPE_IFWI_CPD_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "ifwi-fpt", FU_TYPE_IFWI_FPT_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "oprom", FU_TYPE_OPROM_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "fmap", FU_TYPE_FMAP_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "ihex", FU_TYPE_IHEX_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "srec", FU_TYPE_SREC_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "archive", FU_TYPE_ARCHIVE_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "smbios", FU_TYPE_SMBIOS); + fu_context_add_firmware_gtype(self->ctx, "efi-firmware-file", FU_TYPE_EFI_FIRMWARE_FILE); + fu_context_add_firmware_gtype(self->ctx, + "efi-firmware-filesystem", + FU_TYPE_EFI_FIRMWARE_FILESYSTEM); + fu_context_add_firmware_gtype(self->ctx, + "efi-firmware-section", + FU_TYPE_EFI_FIRMWARE_SECTION); + fu_context_add_firmware_gtype(self->ctx, + "efi-firmware-volume", + FU_TYPE_EFI_FIRMWARE_VOLUME); + fu_context_add_firmware_gtype(self->ctx, "ifd-bios", FU_TYPE_IFD_BIOS); + fu_context_add_firmware_gtype(self->ctx, "ifd-firmware", FU_TYPE_IFD_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "cfu-offer", FU_TYPE_CFU_OFFER); + fu_context_add_firmware_gtype(self->ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD); + fu_context_add_firmware_gtype(self->ctx, "uswid", FU_TYPE_USWID_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "coswid", FU_TYPE_COSWID_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, + "intel-thunderbolt", + FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, + "intel-thunderbolt-nvm", + FU_TYPE_INTEL_THUNDERBOLT_NVM); + fu_context_add_firmware_gtype(self->ctx, "usb-device-fw-ds20", FU_TYPE_USB_DEVICE_FW_DS20); + fu_context_add_firmware_gtype(self->ctx, "usb-device-ms-ds20", FU_TYPE_USB_DEVICE_MS_DS20); + + /* we are emulating a different host */ + if (host_emulate != NULL) { + g_autofree gchar *fn = NULL; + + /* did the user specify an absolue path */ + if (g_file_test(host_emulate, G_FILE_TEST_EXISTS)) { + fn = g_strdup(host_emulate); + } else { + g_autofree gchar *datadir = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); + fn = g_build_filename(datadir, "host-emulate.d", host_emulate, NULL); + } + if (!fu_engine_load_host_emulation(self, fn, error)) { + g_prefix_error(error, "failed to load emulated host: "); + return FALSE; + } + + /* do not load actual hardware */ + flags &= ~FU_ENGINE_LOAD_FLAG_COLDPLUG; + self->host_emulation = TRUE; + } + + /* set up backends */ + if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG) { + for (guint i = 0; i < self->backends->len; i++) { + FuBackend *backend = g_ptr_array_index(self->backends, i); + g_autoptr(GError) error_backend = NULL; + if (!fu_backend_setup(backend, + fu_progress_get_child(progress), + &error_backend)) { + g_debug("failed to setup backend %s: %s", + fu_backend_get_name(backend), + error_backend->message); + continue; + } + backend_cnt++; + } + if (backend_cnt == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "all backends failed setup"); + return FALSE; + } + } + fu_progress_step_done(progress); + + /* delete old data files */ + if (!fu_engine_cleanup_state(error)) { + g_prefix_error(error, "Failed to clean up: "); + return FALSE; + } + + /* init plugins, adding device and firmware GTypes */ + if (!fu_engine_plugins_init(self, fu_progress_get_child(progress), error)) { + g_prefix_error(error, "failed to init plugins: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* set quirks for each hwid */ + guids = fu_context_get_hwid_guids(self->ctx); + for (guint i = 0; i < guids->len; i++) { + const gchar *hwid = g_ptr_array_index(guids, i); + fu_engine_load_quirks_for_hwid(self, hwid); + } + fu_progress_step_done(progress); + + /* set up battery threshold */ + fu_engine_context_set_battery_threshold(self->ctx); + + /* watch the device list for updates and proxy */ + g_signal_connect(FU_DEVICE_LIST(self->device_list), + "added", + G_CALLBACK(fu_engine_device_added_cb), + self); + g_signal_connect(FU_DEVICE_LIST(self->device_list), + "removed", + G_CALLBACK(fu_engine_device_removed_cb), + self); + g_signal_connect(FU_DEVICE_LIST(self->device_list), + "changed", + G_CALLBACK(fu_engine_device_changed_cb), + self); + fu_engine_set_status(self, FWUPD_STATUS_LOADING); + + /* add devices */ + if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG) { + fu_engine_plugins_setup(self, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + fu_engine_plugins_coldplug(self, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + } else { + fu_progress_step_done(progress); + fu_progress_step_done(progress); + } + + /* coldplug backends */ + if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG) + fu_engine_backends_coldplug(self, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + + /* dump plugin information to the console */ + if (g_getenv("FWUPD_BACKEND_VERBOSE") != NULL) { + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + g_autoptr(GString) str = g_string_new(NULL); + for (guint i = 0; i < self->backends->len; i++) { + FuBackend *backend = g_ptr_array_index(self->backends, i); + fu_backend_add_string(backend, 0, str); + } + if (str->len > 0) + g_string_append_c(str, '\n'); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + fu_plugin_add_string(plugin, 0, str); + } + g_debug("\n%s", str->str); + } + + /* update the db for devices that were updated during the reboot */ + if (!fu_engine_update_history_database(self, error)) + return FALSE; + fu_progress_step_done(progress); + + fu_engine_set_status(self, FWUPD_STATUS_IDLE); + self->loaded = TRUE; + + /* let clients know engine finished starting up */ + fu_engine_emit_changed(self); + + /* success */ + return TRUE; +} + +static void +fu_engine_class_init(FuEngineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_engine_finalize; + + /** + * FuEngine::changed: + * @self: the #FuEngine instance that emitted the signal + * + * The ::changed signal is emitted when the engine has changed, for instance when a device + * state has been modified. + **/ + 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); + /** + * FuEngine::device-added: + * @self: the #FuEngine instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-added signal is emitted when a device has been added. + **/ + signals[SIGNAL_DEVICE_ADDED] = g_signal_new("device-added", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuEngine::device-removed: + * @self: the #FuEngine instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-removed signal is emitted when a device has been removed. + **/ + signals[SIGNAL_DEVICE_REMOVED] = g_signal_new("device-removed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuEngine::device-changed: + * @self: the #FuEngine instance that emitted the signal + * @device: the #FuDevice + * + * The ::device-changed signal is emitted when a device has been changed. + **/ + signals[SIGNAL_DEVICE_CHANGED] = g_signal_new("device-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FU_TYPE_DEVICE); + /** + * FuEngine::device-request: + * @self: the #FuEngine instance that emitted the signal + * @request: the #FwupdRequest + * + * The ::device-request signal is emitted when the engine has asked the front end for an + * interactive request. + **/ + signals[SIGNAL_DEVICE_REQUEST] = g_signal_new("device-request", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + FWUPD_TYPE_REQUEST); + /** + * FuEngine::status-changed: + * @self: the #FuEngine instance that emitted the signal + * @status: the #FwupdStatus + * + * The ::status-changed signal is emitted when the daemon global status has changed. + **/ + signals[SIGNAL_STATUS_CHANGED] = g_signal_new("status-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, + 1, + G_TYPE_UINT); +} + +void +fu_engine_add_runtime_version(FuEngine *self, const gchar *component_id, const gchar *version) +{ + fu_context_add_runtime_version(self->ctx, component_id, version); +} + +static void +fu_engine_context_battery_changed_cb(FuContext *ctx, GParamSpec *pspec, FuEngine *self) +{ + g_autoptr(GPtrArray) devices = fu_device_list_get_all(self->device_list); + + /* apply policy on any existing devices */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + fu_engine_ensure_device_battery_inhibit(self, device); + fu_engine_ensure_device_lid_inhibit(self, device); + } +} + +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); +} + +gboolean +fu_engine_backends_save(FuEngine *self, JsonBuilder *json_builder, GError **error) +{ + json_builder_begin_object(json_builder); + json_builder_set_member_name(json_builder, "Backends"); + json_builder_begin_array(json_builder); + for (guint i = 0; i < self->backends->len; i++) { + FuBackend *backend = g_ptr_array_index(self->backends, i); + if (!fu_backend_save(backend, json_builder, NULL, FU_BACKEND_SAVE_FLAG_NONE, error)) + return FALSE; + } + json_builder_end_array(json_builder); + json_builder_end_object(json_builder); + return TRUE; +} + +static void +fu_engine_init(FuEngine *self) +{ +#ifdef HAVE_UTSNAME_H + struct utsname uname_tmp; +#endif + g_autofree gchar *keyring_path = NULL; + g_autofree gchar *pkidir_fw = NULL; + g_autofree gchar *pkidir_md = NULL; + g_autofree gchar *sysconfdir = NULL; + self->percentage = 0; + self->config = fu_config_new(); + self->remote_list = fu_remote_list_new(); + self->device_list = fu_device_list_new(); + self->ctx = fu_context_new(); + self->idle = fu_idle_new(); + self->history = fu_history_new(); + self->plugin_list = fu_plugin_list_new(); + self->plugin_filter = g_ptr_array_new_with_free_func(g_free); + self->host_security_attrs = fu_security_attrs_new(); + self->backends = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + self->local_monitors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + 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->acquiesce_loop = g_main_loop_new(NULL, FALSE); + + fu_context_set_runtime_versions(self->ctx, self->runtime_versions); + fu_context_set_compile_versions(self->ctx, self->compile_versions); + + g_signal_connect(FU_CONTEXT(self->ctx), + "security-changed", + G_CALLBACK(fu_engine_context_security_changed_cb), + self); + g_signal_connect(FU_CONTEXT(self->ctx), + "notify::battery-state", + G_CALLBACK(fu_engine_context_battery_changed_cb), + self); + g_signal_connect(FU_CONTEXT(self->ctx), + "notify::lid-state", + G_CALLBACK(fu_engine_context_battery_changed_cb), + self); + g_signal_connect(FU_CONTEXT(self->ctx), + "notify::battery-level", + G_CALLBACK(fu_engine_context_battery_changed_cb), + self); + g_signal_connect(FU_CONTEXT(self->ctx), + "notify::battery-threshold", + G_CALLBACK(fu_engine_context_battery_changed_cb), + self); + + g_signal_connect(FU_CONFIG(self->config), + "changed", + G_CALLBACK(fu_engine_config_changed_cb), + self); + g_signal_connect(FU_REMOTE_LIST(self->remote_list), + "changed", + G_CALLBACK(fu_engine_remote_list_changed_cb), + self); + + g_signal_connect(FU_IDLE(self->idle), + "notify::status", + G_CALLBACK(fu_engine_idle_status_notify_cb), + self); + + /* backends */ +#ifdef HAVE_GUSB + g_ptr_array_add(self->backends, fu_usb_backend_new(self->ctx)); +#endif +#ifdef HAVE_GUDEV + g_ptr_array_add(self->backends, fu_udev_backend_new(self->ctx)); +#endif +#ifdef HAVE_BLUEZ + g_ptr_array_add(self->backends, fu_bluez_backend_new(self->ctx)); +#endif + + /* setup Jcat context */ + self->jcat_context = jcat_context_new(); + keyring_path = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + jcat_context_set_keyring_path(self->jcat_context, keyring_path); + sysconfdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR); + pkidir_fw = g_build_filename(sysconfdir, "pki", "fwupd", NULL); + jcat_context_add_public_keys(self->jcat_context, pkidir_fw); + pkidir_md = g_build_filename(sysconfdir, "pki", "fwupd-metadata", NULL); + jcat_context_add_public_keys(self->jcat_context, pkidir_md); + + /* add some runtime versions of things the daemon depends on */ + fu_engine_add_runtime_version(self, "org.freedesktop.fwupd", VERSION); +#if G_USB_CHECK_VERSION(0, 3, 1) + fu_engine_add_runtime_version(self, "org.freedesktop.gusb", g_usb_version_string()); +#endif +#if LIBJCAT_CHECK_VERSION(0, 1, 11) + fu_engine_add_runtime_version(self, "com.hughsie.libjcat", jcat_version_string()); +#endif + + /* optional kernel version */ +#ifdef HAVE_UTSNAME_H + memset(&uname_tmp, 0, sizeof(uname_tmp)); + if (uname(&uname_tmp) >= 0) + fu_engine_add_runtime_version(self, "org.kernel", uname_tmp.release); +#endif + + g_hash_table_insert(self->compile_versions, + g_strdup("org.freedesktop.fwupd"), + g_strdup(VERSION)); +#ifdef HAVE_GUSB + g_hash_table_insert(self->compile_versions, + g_strdup("org.freedesktop.gusb"), + g_strdup_printf("%i.%i.%i", + G_USB_MAJOR_VERSION, + G_USB_MINOR_VERSION, + G_USB_MICRO_VERSION)); +#endif + g_hash_table_insert(self->compile_versions, + g_strdup("com.hughsie.libjcat"), + g_strdup_printf("%i.%i.%i", + JCAT_MAJOR_VERSION, + JCAT_MINOR_VERSION, + JCAT_MICRO_VERSION)); + + /* register /org/freedesktop/fwupd globally */ + g_resources_register(fu_get_resource()); +} + +static void +fu_engine_finalize(GObject *obj) +{ + FuEngine *self = FU_ENGINE(obj); + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + g_signal_handlers_disconnect_by_data(plugin, self); + } + for (guint i = 0; i < self->local_monitors->len; i++) { + GFileMonitor *monitor = g_ptr_array_index(self->local_monitors, i); + g_file_monitor_cancel(monitor); + } + + if (self->silo != NULL) + g_object_unref(self->silo); + if (self->query_component_by_guid != NULL) + g_object_unref(self->query_component_by_guid); + if (self->coldplug_id != 0) + g_source_remove(self->coldplug_id); + if (self->approved_firmware != NULL) + g_hash_table_unref(self->approved_firmware); + if (self->blocked_firmware != NULL) + g_hash_table_unref(self->blocked_firmware); + if (self->acquiesce_id != 0) + g_source_remove(self->acquiesce_id); + g_main_loop_unref(self->acquiesce_loop); + + g_free(self->host_machine_id); + g_free(self->host_security_id); + g_object_unref(self->host_security_attrs); + g_object_unref(self->idle); + g_object_unref(self->config); + g_object_unref(self->remote_list); + g_object_unref(self->ctx); + g_object_unref(self->history); + g_object_unref(self->device_list); + g_object_unref(self->jcat_context); + g_ptr_array_unref(self->plugin_filter); + g_ptr_array_unref(self->backends); + g_ptr_array_unref(self->local_monitors); + g_hash_table_unref(self->runtime_versions); + g_hash_table_unref(self->compile_versions); + g_object_unref(self->plugin_list); + + G_OBJECT_CLASS(fu_engine_parent_class)->finalize(obj); +} + +FuEngine * +fu_engine_new(void) +{ + FuEngine *self; + self = g_object_new(FU_TYPE_ENGINE, NULL); + return FU_ENGINE(self); +} diff --git a/fwupd-1.8.6/src/fu-engine.h b/fwupd-1.8.6/src/fu-engine.h new file mode 100644 index 0000000000000000000000000000000000000000..abd0b979eb32b5be673475ae5f7161927007677a --- /dev/null +++ b/fwupd-1.8.6/src/fu-engine.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +#include "fwupd-device.h" +#include "fwupd-enums.h" + +#include "fu-config.h" +#include "fu-release.h" + +#define FU_TYPE_ENGINE (fu_engine_get_type()) +G_DECLARE_FINAL_TYPE(FuEngine, fu_engine, FU, ENGINE, GObject) + +/** + * FuEngineLoadFlags: + * @FU_ENGINE_LOAD_FLAG_NONE: No flags set + * @FU_ENGINE_LOAD_FLAG_READONLY: Ignore readonly filesystem errors + * @FU_ENGINE_LOAD_FLAG_COLDPLUG: Enumerate devices + * @FU_ENGINE_LOAD_FLAG_REMOTES: Enumerate remotes + * @FU_ENGINE_LOAD_FLAG_HWINFO: Load details about the hardware + * @FU_ENGINE_LOAD_FLAG_NO_CACHE: Do not save persistent xmlb silos + * @FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES:Do not load idle sources + * @FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS: Load built-in plugins + * + * The flags to use when loading the engine. + **/ +typedef enum { + FU_ENGINE_LOAD_FLAG_NONE = 0, + FU_ENGINE_LOAD_FLAG_READONLY = 1 << 0, + FU_ENGINE_LOAD_FLAG_COLDPLUG = 1 << 1, + FU_ENGINE_LOAD_FLAG_REMOTES = 1 << 2, + FU_ENGINE_LOAD_FLAG_HWINFO = 1 << 3, + FU_ENGINE_LOAD_FLAG_NO_CACHE = 1 << 4, + FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES = 1 << 5, + FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS = 1 << 6, + /*< private >*/ + FU_ENGINE_LOAD_FLAG_LAST +} FuEngineLoadFlags; + +FuEngine * +fu_engine_new(void); +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, FuProgress *progress, GError **error); +const gchar * +fu_engine_get_host_vendor(FuEngine *self); +const gchar * +fu_engine_get_host_product(FuEngine *self); +const gchar * +fu_engine_get_host_machine_id(FuEngine *self); +const gchar * +fu_engine_get_host_bkc(FuEngine *self); +gboolean +fu_engine_is_uid_trusted(FuEngine *self, guint64 calling_uid); +const gchar * +fu_engine_get_host_security_id(FuEngine *self); +XbSilo * +fu_engine_get_silo_from_blob(FuEngine *self, GBytes *blob_cab, GError **error); +FuConfig * +fu_engine_get_config(FuEngine *self); +GPtrArray * +fu_engine_get_plugins(FuEngine *self); +GPtrArray * +fu_engine_get_devices(FuEngine *self, GError **error); +FuDevice * +fu_engine_get_device(FuEngine *self, const gchar *device_id, GError **error); +GPtrArray * +fu_engine_get_devices_by_guid(FuEngine *self, const gchar *guid, GError **error); +GPtrArray * +fu_engine_get_devices_by_composite_id(FuEngine *self, const gchar *composite_id, 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, + FuEngineRequest *request, + const gchar *device_id, + GError **error); +GPtrArray * +fu_engine_get_downgrades(FuEngine *self, + FuEngineRequest *request, + const gchar *device_id, + GError **error); +GPtrArray * +fu_engine_get_upgrades(FuEngine *self, + FuEngineRequest *request, + const gchar *device_id, + GError **error); +FwupdDevice * +fu_engine_get_results(FuEngine *self, const gchar *device_id, GError **error); +FuSecurityAttrs * +fu_engine_get_host_security_attrs(FuEngine *self); +FuSecurityAttrs * +fu_engine_get_host_security_events(FuEngine *self, guint limit, GError **error); +GHashTable * +fu_engine_get_report_metadata(FuEngine *self, GError **error); +gboolean +fu_engine_clear_results(FuEngine *self, const gchar *device_id, GError **error); +gboolean +fu_engine_update_metadata(FuEngine *self, + const gchar *remote_id, + gint fd, + gint fd_sig, + GError **error); +gboolean +fu_engine_update_metadata_bytes(FuEngine *self, + const gchar *remote_id, + GBytes *bytes_raw, + GBytes *bytes_sig, + GError **error); +gboolean +fu_engine_unlock(FuEngine *self, const gchar *device_id, GError **error); +gboolean +fu_engine_verify(FuEngine *self, const gchar *device_id, FuProgress *progress, GError **error); +gboolean +fu_engine_verify_update(FuEngine *self, + const gchar *device_id, + FuProgress *progress, + GError **error); +GBytes * +fu_engine_firmware_dump(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +FuFirmware * +fu_engine_firmware_read(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +gboolean +fu_engine_modify_remote(FuEngine *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GError **error); +gboolean +fu_engine_modify_device(FuEngine *self, + const gchar *device_id, + const gchar *key, + const gchar *value, + GError **error); +gboolean +fu_engine_composite_prepare(FuEngine *self, GPtrArray *devices, GError **error); +gboolean +fu_engine_composite_cleanup(FuEngine *self, GPtrArray *devices, GError **error); +gboolean +fu_engine_install_release(FuEngine *self, + FuRelease *task, + GBytes *blob_cab, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +gboolean +fu_engine_install_blob(FuEngine *self, + FuDevice *device, + GBytes *blob_fw, + FuProgress *progress, + FwupdInstallFlags flags, + FwupdFeatureFlags feature_flags, + GError **error); +gboolean +fu_engine_install_releases(FuEngine *self, + FuEngineRequest *request, + GPtrArray *releases, + GBytes *blob_cab, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); +GPtrArray * +fu_engine_get_details(FuEngine *self, FuEngineRequest *request, gint fd, GError **error); +gboolean +fu_engine_activate(FuEngine *self, const gchar *device_id, FuProgress *progress, GError **error); +GPtrArray * +fu_engine_get_approved_firmware(FuEngine *self); +void +fu_engine_add_approved_firmware(FuEngine *self, const gchar *checksum); +void +fu_engine_set_approved_firmware(FuEngine *self, GPtrArray *checksums); +GPtrArray * +fu_engine_get_blocked_firmware(FuEngine *self); +void +fu_engine_add_blocked_firmware(FuEngine *self, const gchar *checksum); +gboolean +fu_engine_set_blocked_firmware(FuEngine *self, GPtrArray *checksums, GError **error); +gchar * +fu_engine_self_sign(FuEngine *self, const gchar *value, JcatSignFlags flags, GError **error); +gboolean +fu_engine_modify_config(FuEngine *self, const gchar *key, const gchar *value, GError **error); +FuContext * +fu_engine_get_context(FuEngine *engine); +void +fu_engine_md_refresh_device_from_component(FuEngine *self, FuDevice *device, XbNode *component); +GPtrArray * +fu_engine_get_releases_for_device(FuEngine *self, + FuEngineRequest *request, + FuDevice *device, + GError **error); + +/* for the self tests */ +void +fu_engine_add_device(FuEngine *self, FuDevice *device); +void +fu_engine_add_plugin(FuEngine *self, FuPlugin *plugin); +void +fu_engine_add_runtime_version(FuEngine *self, const gchar *component_id, const gchar *version); +GPtrArray * +fu_engine_get_details_for_bytes(FuEngine *self, + FuEngineRequest *request, + GBytes *blob, + GError **error); +gboolean +fu_engine_check_trust(FuEngine *self, FuRelease *task, GError **error); +gboolean +fu_engine_check_requirements(FuEngine *self, + FuRelease *release, + FwupdInstallFlags flags, + GError **error); +void +fu_engine_set_silo(FuEngine *self, XbSilo *silo); +XbNode * +fu_engine_get_component_by_guids(FuEngine *self, FuDevice *device); +gboolean +fu_engine_schedule_update(FuEngine *self, + FuDevice *device, + FwupdRelease *release, + GBytes *blob_cab, + FwupdInstallFlags flags, + GError **error); +GError * +fu_engine_error_array_get_best(GPtrArray *errors); +gboolean +fu_engine_modify_bios_settings(FuEngine *self, + GHashTable *settings, + gboolean force_ro, + GError **error); +gboolean +fu_engine_backends_save(FuEngine *self, JsonBuilder *json_builder, GError **error); diff --git a/fwupd-1.8.6/src/fu-history.c b/fwupd-1.8.6/src/fu-history.c new file mode 100644 index 0000000000000000000000000000000000000000..6c2d10b4287fc1e81fdddc479740ad227b087f23 --- /dev/null +++ b/fwupd-1.8.6/src/fu-history.c @@ -0,0 +1,1546 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuHistory" + +#include "config.h" + +#include +#include +#ifdef HAVE_SQLITE +#include +#endif +#include + +#include "fwupd-security-attr-private.h" + +#include "fu-device-private.h" +#include "fu-history.h" +#include "fu-mutex.h" +#include "fu-security-attr-common.h" + +#define FU_HISTORY_CURRENT_SCHEMA_VERSION 8 + +static void +fu_history_finalize(GObject *object); + +struct _FuHistory { + GObject parent_instance; +#ifdef HAVE_SQLITE + sqlite3 *db; + GRWLock db_mutex; +#endif +}; + +G_DEFINE_TYPE(FuHistory, fu_history, G_TYPE_OBJECT) + +#ifdef HAVE_SQLITE +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(sqlite3_stmt, sqlite3_finalize); +#pragma clang diagnostic pop + +static FuDevice * +fu_history_device_from_stmt(sqlite3_stmt *stmt) +{ + const gchar *tmp; + FuDevice *device; + FwupdRelease *release; + + /* create new result */ + device = fu_device_new(NULL); + release = fu_device_get_release_default(device); + + /* device_id */ + tmp = (const gchar *)sqlite3_column_text(stmt, 0); + if (tmp != NULL) + fwupd_device_set_id(FWUPD_DEVICE(device), tmp); + + /* checksum */ + tmp = (const gchar *)sqlite3_column_text(stmt, 1); + if (tmp != NULL) + fwupd_release_add_checksum(release, tmp); + + /* plugin */ + tmp = (const gchar *)sqlite3_column_text(stmt, 2); + if (tmp != NULL) + fu_device_set_plugin(device, tmp); + + /* device_created */ + fu_device_set_created(device, sqlite3_column_int64(stmt, 3)); + + /* device_modified */ + fu_device_set_modified(device, sqlite3_column_int64(stmt, 4)); + + /* display_name */ + tmp = (const gchar *)sqlite3_column_text(stmt, 5); + if (tmp != NULL) + fu_device_set_name(device, tmp); + + /* filename */ + tmp = (const gchar *)sqlite3_column_text(stmt, 6); + if (tmp != NULL) + fwupd_release_set_filename(release, tmp); + + /* flags */ + fu_device_set_flags(device, sqlite3_column_int64(stmt, 7) | FWUPD_DEVICE_FLAG_HISTORICAL); + + /* metadata */ + tmp = (const gchar *)sqlite3_column_text(stmt, 8); + if (tmp != NULL) { + g_auto(GStrv) split = g_strsplit(tmp, ";", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_auto(GStrv) kv = g_strsplit(split[i], "=", 2); + if (g_strv_length(kv) != 2) + continue; + fwupd_release_add_metadata_item(release, kv[0], kv[1]); + } + } + + /* guid_default */ + tmp = (const gchar *)sqlite3_column_text(stmt, 9); + if (tmp != NULL) + fu_device_add_guid_full(device, tmp, FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS); + + /* update_state */ + fu_device_set_update_state(device, sqlite3_column_int(stmt, 10)); + + /* update_error */ + tmp = (const gchar *)sqlite3_column_text(stmt, 11); + fu_device_set_update_error(device, tmp); + + /* version_new */ + tmp = (const gchar *)sqlite3_column_text(stmt, 12); + if (tmp != NULL) + fwupd_release_set_version(release, tmp); + + /* version_old */ + tmp = (const gchar *)sqlite3_column_text(stmt, 13); + if (tmp != NULL) + fu_device_set_version(device, tmp); + + /* checksum_device */ + tmp = (const gchar *)sqlite3_column_text(stmt, 14); + if (tmp != NULL) + fu_device_add_checksum(device, tmp); + + /* protocol */ + tmp = (const gchar *)sqlite3_column_text(stmt, 15); + if (tmp != NULL) + fwupd_release_set_protocol(release, tmp); + + /* release_id */ + tmp = (const gchar *)sqlite3_column_text(stmt, 16); + if (tmp != NULL) + fwupd_release_set_id(release, tmp); + return device; +} + +static gboolean +fu_history_stmt_exec(FuHistory *self, sqlite3_stmt *stmt, GPtrArray *array, GError **error) +{ + gint rc; + if (array == NULL) { + rc = sqlite3_step(stmt); + } else { + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + FuDevice *device = fu_history_device_from_stmt(stmt); + g_ptr_array_add(array, device); + } + } + if (rc != SQLITE_DONE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to execute prepared statement: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_history_create_database(FuHistory *self, GError **error) +{ + gint rc; + rc = sqlite3_exec(self->db, + "BEGIN TRANSACTION;" + "CREATE TABLE IF NOT EXISTS schema (" + "created timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + "version INTEGER DEFAULT 0);" + "INSERT INTO schema (version) VALUES (0);" + "CREATE TABLE IF NOT EXISTS history (" + "device_id TEXT," + "update_state INTEGER DEFAULT 0," + "update_error TEXT," + "filename TEXT," + "display_name TEXT," + "plugin TEXT," + "device_created INTEGER DEFAULT 0," + "device_modified INTEGER DEFAULT 0," + "checksum TEXT DEFAULT NULL," + "flags INTEGER DEFAULT 0," + "metadata TEXT DEFAULT NULL," + "guid_default TEXT DEFAULT NULL," + "version_old TEXT," + "version_new TEXT," + "checksum_device TEXT DEFAULT NULL," + "protocol TEXT DEFAULT NULL," + "release_id TEXT DEFAULT NULL);" + "CREATE TABLE IF NOT EXISTS approved_firmware (" + "checksum TEXT);" + "CREATE TABLE IF NOT EXISTS blocked_firmware (" + "checksum TEXT);" + "CREATE TABLE IF NOT EXISTS hsi_history (" + "timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + "hsi_details TEXT DEFAULT NULL," + "hsi_score TEXT DEFAULT NULL);" + "COMMIT;", + NULL, + NULL, + NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL for creating tables: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_history_migrate_database_v1(FuHistory *self, GError **error) +{ + gint rc; + + g_debug("migrating v1 database by recreating table"); + /* rename the table to something out the way */ + rc = sqlite3_exec(self->db, "ALTER TABLE history RENAME TO history_old;", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_debug("cannot rename v0 table: %s", sqlite3_errmsg(self->db)); + return TRUE; + } + + /* create new table */ + if (!fu_history_create_database(self, error)) + return FALSE; + + /* migrate the old entries to the new table */ + rc = sqlite3_exec(self->db, + "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, NULL FROM history_old;" + "DROP TABLE history_old;", + NULL, + NULL, + NULL); + if (rc != SQLITE_OK) { + 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 IF NOT EXISTS 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; +} + +static gboolean +fu_history_migrate_database_v5(FuHistory *self, GError **error) +{ + gint rc; + rc = sqlite3_exec(self->db, + "CREATE TABLE IF NOT EXISTS blocked_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; +} + +static gboolean +fu_history_migrate_database_v6(FuHistory *self, GError **error) +{ + gint rc; + rc = sqlite3_exec(self->db, + "CREATE TABLE IF NOT EXISTS hsi_history (" + "timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + "hsi_details TEXT DEFAULT NULL," + "hsi_score TEXT DEFAULT NULL);", + 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; +} + +static gboolean +fu_history_migrate_database_v7(FuHistory *self, GError **error) +{ + gint rc; + rc = sqlite3_exec(self->db, + "ALTER TABLE history ADD COLUMN release_id TEXT DEFAULT NULL;", + NULL, + NULL, + NULL); + if (rc != SQLITE_OK) + g_debug("ignoring database error: %s", sqlite3_errmsg(self->db)); + return TRUE; +} + +/* returns 0 if database is not initialized */ +static guint +fu_history_get_schema_version(FuHistory *self) +{ + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + + rc = sqlite3_prepare_v2(self->db, "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 prepare to get schema version: %s", sqlite3_errmsg(self->db)); + return 0; + } + return sqlite3_column_int(stmt, 0); +} + +static gboolean +fu_history_create_or_migrate(FuHistory *self, guint schema_ver, GError **error) +{ + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + + if (schema_ver == 0) + g_debug("building initial database"); + else if (schema_ver > 1) + g_debug("migrating v%u database by altering", schema_ver); + + switch (schema_ver) { + /* create initial up-to-date database or migrate */ + case 0: + if (!fu_history_create_database(self, error)) + return FALSE; + break; + case 1: + if (!fu_history_migrate_database_v1(self, error)) + return FALSE; + break; + case 2: + if (!fu_history_migrate_database_v2(self, error)) + return FALSE; + /* fall through */ + case 3: + if (!fu_history_migrate_database_v3(self, error)) + return FALSE; + /* fall through */ + case 4: + if (!fu_history_migrate_database_v4(self, error)) + return FALSE; + /* fall through */ + case 5: + if (!fu_history_migrate_database_v5(self, error)) + return FALSE; + /* fall through */ + case 6: + if (!fu_history_migrate_database_v6(self, error)) + return FALSE; + /* fall through */ + case 7: + if (!fu_history_migrate_database_v7(self, error)) + return FALSE; + break; + default: + /* 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; + } + + /* turn off the lookaside cache */ + sqlite3_db_config(self->db, SQLITE_DBCONFIG_LOOKASIDE, NULL, 0, 0); + return TRUE; +} + +static gboolean +fu_history_load(FuHistory *self, GError **error) +{ + gint rc; + guint schema_ver; + g_autofree gchar *dirname = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new(&self->db_mutex); + + /* already done */ + if (self->db != NULL) + return TRUE; + + 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_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + file = g_file_new_for_path(dirname); + if (!g_file_query_exists(file, NULL)) { + if (!g_file_make_directory_with_parents(file, NULL, error)) + return FALSE; + } + + /* open */ + filename = g_build_filename(dirname, "pending.db", NULL); + 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_tmp = NULL; + rc = sqlite3_prepare_v2(self->db, + "SELECT * FROM history LIMIT 0;", + -1, + &stmt_tmp, + NULL); + if (rc == SQLITE_OK) + schema_ver = 1; + } + + /* 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; +} + +static gchar * +_convert_hash_to_string(GHashTable *hash) +{ + GString *str = g_string_new(NULL); + g_autoptr(GList) keys = g_hash_table_get_keys(hash); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(hash, key); + if (str->len > 0) + g_string_append(str, ";"); + g_string_append_printf(str, "%s=%s", key, value); + } + return g_string_free(str, FALSE); +} + +/* unset some flags we don't want to store */ +static FwupdDeviceFlags +fu_history_get_device_flags_filtered(FuDevice *device) +{ + FwupdDeviceFlags flags = fu_device_get_flags(device); + flags &= ~FWUPD_DEVICE_FLAG_REGISTERED; + flags &= ~FWUPD_DEVICE_FLAG_SUPPORTED; + return flags; +} +#endif + +/** + * fu_history_modify_device: + * @self: a #FuHistory + * @device: a device + * @error: (nullable): optional return location for an error + * + * Modify a device in the history database + * + * Returns: @TRUE if successful, @FALSE for failure + * + * Since: 1.0.4 + **/ +gboolean +fu_history_modify_device(FuHistory *self, FuDevice *device, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) locker = NULL; + + g_return_val_if_fail(FU_IS_HISTORY(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + + /* lazy load */ + if (!fu_history_load(self, error)) + 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); + g_debug("modifying device %s [%s]", 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, " + "device_modified = ?7, " + "flags = ?3 " + "WHERE device_id = ?4;", + -1, + &stmt, + NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "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); + sqlite3_bind_int64(stmt, 7, fu_device_get_modified(device)); + + return fu_history_stmt_exec(self, stmt, NULL, error); +#else + return TRUE; +#endif +} + +/** + * fu_history_set_device_metadata: + * @self: a #FuHistory + * @device_id: a DeviceID string + * @metadata: a #GHashTable of string:string + * @error: (nullable): optional return location for an error + * + * Modify a device in the history database + * + * Returns: @TRUE if successful, @FALSE for failure + * + * Since: 1.5.0 + **/ +gboolean +fu_history_set_device_metadata(FuHistory *self, + const gchar *device_id, + GHashTable *metadata, + GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autofree gchar *metadata_str = NULL; + g_autoptr(GRWLockWriterLocker) locker = NULL; + g_autoptr(sqlite3_stmt) stmt = NULL; + + g_return_val_if_fail(FU_IS_HISTORY(self), FALSE); + g_return_val_if_fail(device_id != NULL, FALSE); + + /* lazy load */ + if (!fu_history_load(self, error)) + 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); + g_debug("modifying %s", device_id); + rc = sqlite3_prepare_v2(self->db, + "UPDATE history SET " + "metadata = ?1 " + "WHERE device_id = ?2;", + -1, + &stmt, + NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to prepare SQL to update history: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + + /* metadata is stored as a simple string */ + metadata_str = _convert_hash_to_string(metadata); + sqlite3_bind_text(stmt, 1, metadata_str, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, device_id, -1, SQLITE_STATIC); + + return fu_history_stmt_exec(self, stmt, NULL, error); +#else + return TRUE; +#endif +} + +/** + * fu_history_add_device: + * @self: a #FuHistory + * @device: a device + * @release: a #FuRelease + * @error: (nullable): optional return location for an error + * + * Adds a device to the history database + * + * Returns: @TRUE if successful, @FALSE for failure + * + * Since: 1.0.4 + **/ +gboolean +fu_history_add_device(FuHistory *self, FuDevice *device, FwupdRelease *release, GError **error) +{ +#ifdef HAVE_SQLITE + const gchar *checksum_device; + const gchar *checksum = NULL; + gint rc; + g_autofree gchar *metadata = NULL; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) locker = NULL; + + g_return_val_if_fail(FU_IS_HISTORY(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FWUPD_IS_RELEASE(release), FALSE); + + /* lazy load */ + if (!fu_history_load(self, error)) + return FALSE; + + /* ensure all old device(s) with this ID are removed */ + if (!fu_history_remove_device(self, device, error)) + return FALSE; + g_debug("add device %s [%s]", fu_device_get_name(device), fu_device_get_id(device)); + 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," + "update_error," + "flags," + "filename," + "checksum," + "display_name," + "plugin," + "guid_default," + "metadata," + "device_created," + "device_modified," + "version_old," + "version_new," + "checksum_device," + "protocol) " + "VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10," + "?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 to insert history: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + sqlite3_bind_text(stmt, 1, fu_device_get_id(device), -1, SQLITE_STATIC); + sqlite3_bind_int(stmt, 2, fu_device_get_update_state(device)); + sqlite3_bind_text(stmt, 3, fu_device_get_update_error(device), -1, SQLITE_STATIC); + sqlite3_bind_int64(stmt, 4, fu_history_get_device_flags_filtered(device)); + sqlite3_bind_text(stmt, 5, fwupd_release_get_filename(release), -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 6, checksum, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 7, fu_device_get_name(device), -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 8, fu_device_get_plugin(device), -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 9, fu_device_get_guid_default(device), -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 10, metadata, -1, SQLITE_STATIC); + sqlite3_bind_int64(stmt, 11, fu_device_get_created(device)); + 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); +#else + return TRUE; +#endif +} + +/** + * fu_history_remove_all: + * @self: a #FuHistory + * @error: (nullable): optional return location for an error + * + * Remove all devices from the history database + * + * Returns: @TRUE if successful, @FALSE for failure + * + * Since: 1.0.4 + **/ +gboolean +fu_history_remove_all(FuHistory *self, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) 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); + 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 to delete history: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + return fu_history_stmt_exec(self, stmt, NULL, error); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return FALSE; +#endif +} + +/** + * fu_history_remove_device: + * @self: a #FuHistory + * @device: a device + * @error: (nullable): optional return location for an error + * + * Remove a device from the history database + * + * Returns: @TRUE if successful, @FALSE for failure + * + * Since: 1.0.4 + **/ +gboolean +fu_history_remove_device(FuHistory *self, FuDevice *device, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) locker = NULL; + + g_return_val_if_fail(FU_IS_HISTORY(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + + /* lazy load */ + if (!fu_history_load(self, error)) + return FALSE; + + 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, + "DELETE FROM history WHERE device_id = ?1;", + -1, + &stmt, + NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to delete history: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + sqlite3_bind_text(stmt, 1, fu_device_get_id(device), -1, SQLITE_STATIC); + return fu_history_stmt_exec(self, stmt, NULL, error); +#else + return TRUE; +#endif +} + +/** + * fu_history_get_device_by_id: + * @self: a #FuHistory + * @device_id: a string + * @error: (nullable): optional return location for an error + * + * Returns the device from the history database or NULL if not found + * + * Returns: (transfer full): a device + * + * Since: 1.0.4 + **/ +FuDevice * +fu_history_get_device_by_id(FuHistory *self, const gchar *device_id, GError **error) +{ +#ifdef HAVE_SQLITE + 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); + + /* lazy load */ + if (!fu_history_load(self, error)) + return NULL; + + /* get all the 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, " + "plugin, " + "device_created, " + "device_modified, " + "display_name, " + "filename, " + "flags, " + "metadata, " + "guid_default, " + "update_state, " + "update_error, " + "version_new, " + "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 to get history: %s", + sqlite3_errmsg(self->db)); + return NULL; + } + sqlite3_bind_text(stmt, 1, device_id, -1, SQLITE_STATIC); + array_tmp = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + if (!fu_history_stmt_exec(self, stmt, array_tmp, error)) + return NULL; + if (array_tmp->len == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "No devices found"); + return NULL; + } + return g_object_ref(g_ptr_array_index(array_tmp, 0)); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return NULL; +#endif +} + +/** + * fu_history_get_devices: + * @self: a #FuHistory + * @error: (nullable): optional return location for an error + * + * Gets the devices in the history database. + * + * Returns: (element-type #FuDevice) (transfer container): devices + * + * Since: 1.0.4 + **/ +GPtrArray * +fu_history_get_devices(FuHistory *self, GError **error) +{ + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +#ifdef HAVE_SQLITE + g_autoptr(sqlite3_stmt) stmt = NULL; + gint rc; + g_autoptr(GRWLockReaderLocker) locker = 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 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, " + "plugin, " + "device_created, " + "device_modified, " + "display_name, " + "filename, " + "flags, " + "metadata, " + "guid_default, " + "update_state, " + "update_error, " + "version_new, " + "version_old, " + "checksum_device, " + "protocol, " + "release_id 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 to get history: %s", + sqlite3_errmsg(self->db)); + return NULL; + } + if (!fu_history_stmt_exec(self, stmt, array, error)) + return NULL; +#endif + return g_steal_pointer(&array); +} + +/** + * fu_history_get_approved_firmware: + * @self: a #FuHistory + * @error: (nullable): optional return location for an error + * + * Returns approved firmware records. + * + * Returns: (transfer full) (element-type gchar *): records + * + * Since: 1.2.6 + **/ +GPtrArray * +fu_history_get_approved_firmware(FuHistory *self, GError **error) +{ + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(GRWLockReaderLocker) locker = 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; + } + 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; + } +#endif + return g_steal_pointer(&array); +} + +/** + * fu_history_clear_approved_firmware: + * @self: a #FuHistory + * @error: (nullable): optional return location for an error + * + * Clear all approved firmware records + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.2.6 + **/ +gboolean +fu_history_clear_approved_firmware(FuHistory *self, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) 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); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return FALSE; +#endif +} + +/** + * fu_history_add_approved_firmware: + * @self: a #FuHistory + * @checksum: a string + * @error: (nullable): optional return location for an error + * + * Add an approved firmware record to the database + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.2.6 + **/ +gboolean +fu_history_add_approved_firmware(FuHistory *self, const gchar *checksum, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) 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); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return FALSE; +#endif +} + +/** + * fu_history_get_blocked_firmware: + * @self: a #FuHistory + * @error: (nullable): optional return location for an error + * + * Returns blocked firmware records. + * + * Returns: (transfer full) (element-type gchar *): records + * + * Since: 1.4.6 + **/ +GPtrArray * +fu_history_get_blocked_firmware(FuHistory *self, GError **error) +{ + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(GRWLockReaderLocker) locker = 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 blocked 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 blocked_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; + } + 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; + } +#endif + return g_steal_pointer(&array); +} + +/** + * fu_history_clear_blocked_firmware: + * @self: a #FuHistory + * @error: (nullable): optional return location for an error + * + * Clear all blocked firmware records + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.4.6 + **/ +gboolean +fu_history_clear_blocked_firmware(FuHistory *self, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) 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 blocked_firmware;", -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to delete blocked firmware: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + return fu_history_stmt_exec(self, stmt, NULL, error); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return FALSE; +#endif +} + +/** + * fu_history_add_blocked_firmware: + * @self: a #FuHistory + * @checksum: a string + * @error: (nullable): optional return location for an error + * + * Add an blocked firmware record to the database + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 1.4.6 + **/ +gboolean +fu_history_add_blocked_firmware(FuHistory *self, const gchar *checksum, GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) 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 blocked_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); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return FALSE; +#endif +} + +gboolean +fu_history_add_security_attribute(FuHistory *self, + const gchar *security_attr_json, + const gchar *hsi_score, + GError **error) +{ +#ifdef HAVE_SQLITE + gint rc; + g_autoptr(sqlite3_stmt) stmt = NULL; + g_autoptr(GRWLockWriterLocker) 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, + "INSERT INTO hsi_history (hsi_details, hsi_score)" + "VALUES (?1, ?2)", + -1, + &stmt, + NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to write security attribute: %s", + sqlite3_errmsg(self->db)); + return FALSE; + } + sqlite3_bind_text(stmt, 1, security_attr_json, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, hsi_score, -1, SQLITE_STATIC); + return fu_history_stmt_exec(self, stmt, NULL, error); +#else + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no sqlite support"); + return FALSE; +#endif +} + +/** + * fu_history_get_security_attrs: + * @self: a #FuHistory + * @limit: maximum number of attributes to return, or 0 for no limit + * @error: (nullable): optional return location for an error + * + * Gets the security attributes in the history database. + * Attributes with the same stores JSON data will be deduplicated as required. + * + * Returns: (element-type #FuSecurityAttrs) (transfer container): attrs + * + * Since: 1.7.1 + **/ +GPtrArray * +fu_history_get_security_attrs(FuHistory *self, guint limit, GError **error) +{ + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +#ifdef HAVE_SQLITE + g_autoptr(sqlite3_stmt) stmt = NULL; + gint rc; + guint old_hash = 0; + g_autoptr(GRWLockReaderLocker) locker = 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 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 timestamp, hsi_details FROM hsi_history " + "ORDER BY timestamp DESC;", + -1, + &stmt, + NULL); + if (rc != SQLITE_OK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to prepare SQL to get security attrs: %s", + sqlite3_errmsg(self->db)); + return NULL; + } + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + const gchar *json; + guint hash; + const gchar *timestamp; + g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new(); + g_autoptr(JsonParser) parser = NULL; + g_autoptr(GDateTime) created_dt = NULL; + g_autoptr(GTimeZone) tz_utc = g_time_zone_new_utc(); + + /* old */ + timestamp = (const gchar *)sqlite3_column_text(stmt, 0); + if (timestamp == NULL) + continue; + + /* device_id */ + json = (const gchar *)sqlite3_column_text(stmt, 1); + if (json == NULL) + continue; + + /* do not create dups */ + hash = g_str_hash(json); + if (hash == old_hash) { + g_debug("skipping %s as unchanged", timestamp); + continue; + } + old_hash = hash; + + /* parse JSON */ + parser = json_parser_new(); + g_debug("parsing %s", timestamp); + if (!json_parser_load_from_data(parser, json, -1, error)) + return NULL; + if (!fu_security_attrs_from_json(attrs, json_parser_get_root(parser), error)) + return NULL; + + /* parse timestamp */ + created_dt = g_date_time_new_from_iso8601(timestamp, tz_utc); + if (created_dt != NULL) { + guint64 created_unix = g_date_time_to_unix(created_dt); + g_autoptr(GPtrArray) attr_array = fu_security_attrs_get_all(attrs); + for (guint i = 0; i < attr_array->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attr_array, i); + fwupd_security_attr_set_created(attr, created_unix); + } + } + + /* success */ + g_ptr_array_add(array, g_steal_pointer(&attrs)); + if (limit > 0 && array->len >= limit) { + rc = SQLITE_DONE; + break; + } + } + 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; + } +#endif + return g_steal_pointer(&array); +} + +static void +fu_history_class_init(FuHistoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_history_finalize; +} + +static void +fu_history_init(FuHistory *self) +{ +#ifdef HAVE_SQLITE + g_rw_lock_init(&self->db_mutex); +#endif +} + +static void +fu_history_finalize(GObject *object) +{ +#ifdef HAVE_SQLITE + FuHistory *self = FU_HISTORY(object); + + g_rw_lock_clear(&self->db_mutex); + + if (self->db != NULL) + sqlite3_close(self->db); +#endif + + G_OBJECT_CLASS(fu_history_parent_class)->finalize(object); +} + +/** + * fu_history_new: + * + * Creates a new #FuHistory + * + * Since: 1.0.4 + **/ +FuHistory * +fu_history_new(void) +{ + FuHistory *self; + self = g_object_new(FU_TYPE_PENDING, NULL); + return FU_HISTORY(self); +} diff --git a/fwupd-1.8.6/src/fu-history.h b/fwupd-1.8.6/src/fu-history.h new file mode 100644 index 0000000000000000000000000000000000000000..b7442e335b2b4bcdf2f280a1096468fa6e87dd64 --- /dev/null +++ b/fwupd-1.8.6/src/fu-history.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PENDING (fu_history_get_type()) +G_DECLARE_FINAL_TYPE(FuHistory, fu_history, FU, HISTORY, GObject) + +FuHistory * +fu_history_new(void); + +gboolean +fu_history_add_device(FuHistory *self, FuDevice *device, FwupdRelease *release, GError **error); +gboolean +fu_history_modify_device(FuHistory *self, FuDevice *device, GError **error); +gboolean +fu_history_set_device_metadata(FuHistory *self, + const gchar *device_id, + GHashTable *metadata, + GError **error); +gboolean +fu_history_remove_device(FuHistory *self, FuDevice *device, GError **error); +gboolean +fu_history_remove_all(FuHistory *self, GError **error); +FuDevice * +fu_history_get_device_by_id(FuHistory *self, const gchar *device_id, GError **error); +GPtrArray * +fu_history_get_devices(FuHistory *self, GError **error); + +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); +gboolean +fu_history_clear_blocked_firmware(FuHistory *self, GError **error); +gboolean +fu_history_add_blocked_firmware(FuHistory *self, const gchar *checksum, GError **error); +GPtrArray * +fu_history_get_blocked_firmware(FuHistory *self, GError **error); +gboolean +fu_history_add_security_attribute(FuHistory *self, + const gchar *security_attr_json, + const gchar *hsi_score, + GError **error); +GPtrArray * +fu_history_get_security_attrs(FuHistory *self, guint limit, GError **error); diff --git a/fwupd-1.8.6/src/fu-idle.c b/fwupd-1.8.6/src/fu-idle.c new file mode 100644 index 0000000000000000000000000000000000000000..67f966e8edeb315141d8acbcdf07fb618b713c35 --- /dev/null +++ b/fwupd-1.8.6/src/fu-idle.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuIdle" + +#include "config.h" + +#include "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(GRWLockWriterLocker) 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(GRWLockWriterLocker) 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; + + /** + * FuIdle:status: + * + * The status of the idle monitor. + */ + 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_rw_lock_clear(&self->items_mutex); + g_ptr_array_unref(self->items); + + G_OBJECT_CLASS(fu_idle_parent_class)->finalize(obj); +} + +FuIdle * +fu_idle_new(void) +{ + FuIdle *self; + self = g_object_new(FU_TYPE_IDLE, NULL); + return FU_IDLE(self); +} diff --git a/fwupd-1.8.6/src/fu-idle.h b/fwupd-1.8.6/src/fu-idle.h new file mode 100644 index 0000000000000000000000000000000000000000..a6f4c2cf1842dbfab37fce34217f3e4ce56ddae7 --- /dev/null +++ b/fwupd-1.8.6/src/fu-idle.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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) diff --git a/fwupd-1.8.6/src/fu-keyring-utils.c b/fwupd-1.8.6/src/fu-keyring-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..21015563360c779f9f75abe4e7aa957d9537aa85 --- /dev/null +++ b/fwupd-1.8.6/src/fu-keyring-utils.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuKeyring" + +#include "config.h" + +#include + +#include "fu-keyring-utils.h" + +/** + * fu_keyring_get_release_flags: + * @release: the reelase node + * @flags: (out): flags for the release, e.g. %FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD + * @error: (nullable): optional return location for an error + * + * Uses the correct keyring to get the trust flags for a given release. + * + * Returns: %TRUE if @flags has been set + **/ +gboolean +fu_keyring_get_release_flags(XbNode *release, FwupdReleaseFlags *flags, GError **error) +{ + GBytes *blob; + + blob = g_object_get_data(G_OBJECT(release), "fwupd::ReleaseFlags"); + if (blob == NULL) + return TRUE; + if (g_bytes_get_size(blob) != sizeof(FwupdReleaseFlags)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid fwupd::ReleaseFlags set by loader"); + return FALSE; + } + memcpy(flags, g_bytes_get_data(blob, NULL), sizeof(FwupdReleaseFlags)); + return TRUE; +} diff --git a/fwupd-1.8.6/src/fu-keyring-utils.h b/fwupd-1.8.6/src/fu-keyring-utils.h new file mode 100644 index 0000000000000000000000000000000000000000..672c3c5cc4f4679f50d24ce53acd0888449ba774 --- /dev/null +++ b/fwupd-1.8.6/src/fu-keyring-utils.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +gboolean +fu_keyring_get_release_flags(XbNode *release, FwupdReleaseFlags *flags, GError **error); diff --git a/fwupd-1.8.6/src/fu-main-windows.c b/fwupd-1.8.6/src/fu-main-windows.c new file mode 100644 index 0000000000000000000000000000000000000000..7065b511419f0db29e67a039ddd9c5f6f16c52ac --- /dev/null +++ b/fwupd-1.8.6/src/fu-main-windows.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * - sc create fwupd start="auto" binPath="C:\Program Files (x86)\fwupd\bin\fwupd.exe" + * - sc delete fwupd + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include + +#include + +#include "fu-daemon.h" +#include "fu-debug.h" + +static SERVICE_STATUS gSvcStatus = {.dwServiceType = SERVICE_WIN32_OWN_PROCESS, + .dwServiceSpecificExitCode = 0}; +static SERVICE_STATUS_HANDLE gSvcStatusHandle = 0; +static FuDaemon *gDaemon = NULL; + +static void +fu_main_svc_report_status(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + + gSvcStatus.dwCurrentState = dwCurrentState; + gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; + gSvcStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_START_PENDING) + gSvcStatus.dwControlsAccepted = 0; + else + gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED) + gSvcStatus.dwCheckPoint = 0; + else + gSvcStatus.dwCheckPoint = dwCheckPoint++; + + SetServiceStatus(gSvcStatusHandle, &gSvcStatus); +} + +static gboolean +fu_main_svc_control_stop_cb(gpointer user_data) +{ + FuDaemon *daemon = FU_DAEMON(user_data); + fu_daemon_stop(daemon); + return G_SOURCE_REMOVE; +} + +static void +fu_main_svc_control_cb(DWORD dwCtrl) +{ + switch (dwCtrl) { + case SERVICE_CONTROL_STOP: + fu_main_svc_report_status(SERVICE_STOP_PENDING, NO_ERROR, 0); + /* there is no user_data, because global state with threads is completely fine */ + g_idle_add(fu_main_svc_control_stop_cb, gDaemon); + fu_main_svc_report_status(gSvcStatus.dwCurrentState, NO_ERROR, 0); + break; + default: + break; + } +} + +static void +fu_main_svc_main_cb(DWORD dwArgc, LPSTR *lpszArgv) +{ + g_autoptr(FuDaemon) daemon = gDaemon = fu_daemon_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GOptionContext) context = g_option_context_new(NULL); + + /* parse debugging args */ + g_option_context_add_group(context, fu_debug_get_option_group()); + if (!g_option_context_parse(context, (gint *)&dwArgc, &lpszArgv, &error)) { + g_printerr("Failed to parse command line: %s\n", error->message); + return; + } + + gSvcStatusHandle = RegisterServiceCtrlHandlerA((LPSTR) "fwupd", fu_main_svc_control_cb); + if (gSvcStatusHandle == NULL) { + g_warning("RegisterServiceCtrlHandlerA failed [%u]", (guint)GetLastError()); + return; + } + + /* set up the daemon, which includes coldplugging devices -- then run it */ + fu_main_svc_report_status(SERVICE_START_PENDING, NO_ERROR, 1000); + if (!fu_daemon_setup(daemon, FWUPD_DBUS_P2P_SOCKET_ADDRESS, &error)) { + g_warning("Failed to load daemon: %s", error->message); + return; + } + fu_main_svc_report_status(SERVICE_RUNNING, NO_ERROR, 0); + fu_daemon_start(daemon); + fu_main_svc_report_status(SERVICE_STOPPED, NO_ERROR, 0); +} + +static int +fu_main_console(int argc, char *argv[]) +{ + g_autoptr(FuDaemon) daemon = gDaemon = fu_daemon_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GOptionContext) context = g_option_context_new(NULL); + + /* parse debugging args */ + g_option_context_add_group(context, fu_debug_get_option_group()); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Failed to parse command line: %s\n", error->message); + return EXIT_FAILURE; + } + + /* set up the daemon, which includes coldplugging devices -- then run it */ + if (!fu_daemon_setup(daemon, FWUPD_DBUS_P2P_SOCKET_ADDRESS, &error)) { + g_printerr("Failed to load daemon: %s\n", error->message); + return EXIT_FAILURE; + } + fu_daemon_start(daemon); + + /* success */ + g_message("Daemon ready for requests"); + return EXIT_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRYA svc_table[] = {{(LPSTR) "fwupd", fu_main_svc_main_cb}, {NULL, NULL}}; + if (!StartServiceCtrlDispatcherA(svc_table)) { + /* program is being run as a console application rather than as a service */ + if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) + return fu_main_console(argc, argv); + g_printerr("StartServiceCtrlDispatcherA failed [%u]\n", (guint)GetLastError()); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/src/fu-main.c b/fwupd-1.8.6/src/fu-main.c new file mode 100644 index 0000000000000000000000000000000000000000..e5918ff77e0ab150e0d775a6c7d3b803f5e76fa7 --- /dev/null +++ b/fwupd-1.8.6/src/fu-main.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_GIO_UNIX +#include +#endif +#ifdef HAVE_SYSTEMD +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif + +#include "fu-daemon.h" +#include "fu-debug.h" + +#ifdef HAVE_GIO_UNIX +static gboolean +fu_main_sigterm_cb(gpointer user_data) +{ + FuDaemon *daemon = FU_DAEMON(user_data); + g_warning("Received SIGTERM"); + fu_daemon_stop(daemon); + return G_SOURCE_CONTINUE; +} +#endif + +static gboolean +fu_main_timed_exit_cb(gpointer user_data) +{ + FuDaemon *daemon = FU_DAEMON(user_data); + fu_daemon_stop(daemon); + return G_SOURCE_REMOVE; +} + +static void +fu_main_argv_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuDaemon *daemon = FU_DAEMON(user_data); + g_debug("binary changed, shutting down"); + fu_daemon_stop(daemon); +} + +#if GLIB_CHECK_VERSION(2, 63, 3) +static void +fu_main_memory_monitor_warning_cb(GMemoryMonitor *memory_monitor, + GMemoryMonitorWarningLevel level, + FuDaemon *daemon) +{ + g_debug("OOM event, shutting down"); + fu_daemon_stop(daemon); +} +#endif + +static gboolean +fu_main_is_hypervisor(void) +{ + g_autofree gchar *buf = NULL; + gsize bufsz = 0; + if (!g_file_get_contents("/proc/cpuinfo", &buf, &bufsz, NULL)) + return FALSE; + return g_strstr_len(buf, (gssize)bufsz, "hypervisor") != NULL; +} + +static gboolean +fu_main_is_container(void) +{ + g_autofree gchar *buf = NULL; + gsize bufsz = 0; + if (!g_file_get_contents("/proc/1/cgroup", &buf, &bufsz, NULL)) + return FALSE; + if (g_strstr_len(buf, (gssize)bufsz, "docker") != NULL) + return TRUE; + if (g_strstr_len(buf, (gssize)bufsz, "lxc") != NULL) + return TRUE; + return FALSE; +} + +int +main(int argc, char *argv[]) +{ + gboolean immediate_exit = FALSE; + gboolean timed_exit = FALSE; + const gchar *socket_filename = g_getenv("FWUPD_DBUS_SOCKET"); + const GOptionEntry options[] = { + {"timed-exit", + '\0', + 0, + G_OPTION_ARG_NONE, + &timed_exit, + /* TRANSLATORS: exit after we've started up, used for user profiling */ + N_("Exit after a small delay"), + NULL}, + {"immediate-exit", + '\0', + 0, + G_OPTION_ARG_NONE, + &immediate_exit, + /* TRANSLATORS: exit straight away, used for automatic profiling */ + N_("Exit after the engine has loaded"), + NULL}, + {NULL}}; + g_autofree gchar *socket_address = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GFile) argv0_file = g_file_new_for_path(argv[0]); + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FuDaemon) daemon = fu_daemon_new(); + g_autoptr(GFileMonitor) argv0_monitor = NULL; +#if GLIB_CHECK_VERSION(2, 63, 3) + g_autoptr(GMemoryMonitor) memory_monitor = NULL; +#endif + + setlocale(LC_ALL, ""); + + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* TRANSLATORS: program name */ + g_set_application_name(_("Firmware Update Daemon")); + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + g_option_context_add_group(context, fu_debug_get_option_group()); + /* TRANSLATORS: program summary */ + g_option_context_set_summary(context, _("Firmware Update D-Bus Service")); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Failed to parse command line: %s\n", error->message); + return EXIT_FAILURE; + } + + /* detect the machine kind */ + if (fu_main_is_hypervisor()) { + fu_daemon_set_machine_kind(daemon, FU_DAEMON_MACHINE_KIND_VIRTUAL); + } else if (fu_main_is_container()) { + fu_daemon_set_machine_kind(daemon, FU_DAEMON_MACHINE_KIND_CONTAINER); + } else { + fu_daemon_set_machine_kind(daemon, FU_DAEMON_MACHINE_KIND_PHYSICAL); + } + + /* convert from filename to address, if required */ + if (socket_filename != NULL) { + if (g_strrstr(socket_filename, "=") == NULL) { +#ifndef HAVE_SYSTEMD + /* this must be owned by root */ + if (g_file_test(socket_filename, G_FILE_TEST_EXISTS)) + g_unlink(socket_filename); +#endif + socket_address = g_strdup_printf("unix:path=%s", socket_filename); + } else { + socket_address = g_strdup(socket_filename); + } + } + + /* set up the daemon, which includes coldplugging devices */ + if (!fu_daemon_setup(daemon, socket_address, &error)) { + g_printerr("Failed to load daemon: %s\n", error->message); + return EXIT_FAILURE; + } + +#ifdef HAVE_GIO_UNIX + g_unix_signal_add_full(G_PRIORITY_DEFAULT, SIGTERM, fu_main_sigterm_cb, daemon, NULL); +#endif /* HAVE_GIO_UNIX */ + + /* restart the daemon if the binary gets replaced */ + argv0_monitor = g_file_monitor_file(argv0_file, G_FILE_MONITOR_NONE, NULL, &error); + g_signal_connect(G_FILE_MONITOR(argv0_monitor), + "changed", + G_CALLBACK(fu_main_argv_changed_cb), + daemon); + +#if GLIB_CHECK_VERSION(2, 63, 3) + /* shut down on low memory event as we can just rescan hardware */ + memory_monitor = g_memory_monitor_dup_default(); + if (memory_monitor != NULL) { + g_signal_connect(G_MEMORY_MONITOR(memory_monitor), + "low-memory-warning", + G_CALLBACK(fu_main_memory_monitor_warning_cb), + daemon); + } +#endif + + /* Only timeout and close the mainloop if we have specified it + * on the command line */ + if (immediate_exit) + g_idle_add(fu_main_timed_exit_cb, daemon); + else if (timed_exit) + g_timeout_add_seconds(5, fu_main_timed_exit_cb, daemon); + +#ifdef HAVE_MALLOC_TRIM + /* drop heap except one page */ + malloc_trim(4096); +#endif + + /* wait */ + g_message("Daemon ready for requests (locale %s)", g_getenv("LANG")); + fu_daemon_start(daemon); + +#ifdef HAVE_SYSTEMD + /* notify the service manager */ + sd_notify(0, "STOPPING=1"); +#endif + + /* cancel to avoid a deadlock */ + g_file_monitor_cancel(argv0_monitor); + + /* success */ + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/src/fu-offline.c b/fwupd-1.8.6/src/fu-offline.c new file mode 100644 index 0000000000000000000000000000000000000000..5bcd91f4db4fd698324dee8e0e81793cd67b6b5a --- /dev/null +++ b/fwupd-1.8.6/src/fu-offline.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2015 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-spawn.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_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_spawn_sync(argv, NULL, NULL, 1500, NULL, &error_local)) { + argv[2] = "--updates"; + if (!fu_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_spawn_sync(argv, NULL, NULL, 200, NULL, &error_local)) { + /* fall back to really old mode that should be supported */ + argv[2] = "--shutdown"; + if (!fu_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_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + g_autofree gchar *trigger = fu_path_from_kind(FU_PATH_KIND_OFFLINE_TRIGGER); + 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, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* verify this is pointing to our cache */ + link = g_file_read_link(trigger, 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 */ + (void)g_unlink(trigger); + + /* ensure root user */ +#ifdef HAVE_GETUID + 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; + } +#endif + + /* 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(FWUPD_CLIENT(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_version_compare(fwupd_device_get_version(dev), + fwupd_release_get_version(rel), + fwupd_device_get_version_format(dev)); + 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)) { + g_printerr("%s: %s\n", + /* TRANSLATORS: we could not install for some reason */ + _("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 --git a/fwupd-1.8.6/src/fu-plugin-list.c b/fwupd-1.8.6/src/fu-plugin-list.c new file mode 100644 index 0000000000000000000000000000000000000000..d2f12cf2bb51fc4ec83b336d4702b297a37f0b38 --- /dev/null +++ b/fwupd-1.8.6/src/fu-plugin-list.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuPluginList" + +#include "config.h" + +#include "fu-plugin-list.h" +#include "fu-plugin-private.h" + +/** + * FuPluginList: + * + * 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 dependency order. + * + * See also: [class@FuPlugin] + */ + +static void +fu_plugin_list_finalize(GObject *obj); + +struct _FuPluginList { + GObject parent_instance; + GPtrArray *plugins; /* of FuPlugin */ + GHashTable *plugins_hash; /* of name : FuPlugin */ +}; + +G_DEFINE_TYPE(FuPluginList, fu_plugin_list, G_TYPE_OBJECT) + +/** + * fu_plugin_list_get_all: + * @self: a #FuPluginList + * + * Gets all the plugins that have been added. + * + * Returns: (transfer none) (element-type FuPlugin): the plugins + * + * Since: 1.0.2 + **/ +GPtrArray * +fu_plugin_list_get_all(FuPluginList *self) +{ + g_return_val_if_fail(FU_IS_PLUGIN_LIST(self), NULL); + return self->plugins; +} + +static void +fu_plugin_list_rules_changed_cb(FuPlugin *plugin, gpointer user_data) +{ + FuPluginList *self = FU_PLUGIN_LIST(user_data); + GPtrArray *rules = fu_plugin_get_rules(plugin, FU_PLUGIN_RULE_CONFLICTS); + if (rules == NULL) + return; + for (guint j = 0; j < rules->len; j++) { + const gchar *plugin_name = g_ptr_array_index(rules, j); + FuPlugin *dep = fu_plugin_list_find_by_name(self, plugin_name, NULL); + if (dep == NULL) + continue; + if (fu_plugin_has_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + g_debug("late disabling %s as conflicts with %s", + fu_plugin_get_name(dep), + fu_plugin_get_name(plugin)); + fu_plugin_add_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED); + } +} + +/** + * fu_plugin_list_add: + * @self: a #FuPluginList + * @plugin: a plugin + * + * Adds a plugin to the list. The plugin name is used for a hash key and must + * be set before calling this function. + * + * Since: 1.0.2 + **/ +void +fu_plugin_list_add(FuPluginList *self, FuPlugin *plugin) +{ + g_return_if_fail(FU_IS_PLUGIN_LIST(self)); + g_return_if_fail(FU_IS_PLUGIN(plugin)); + g_return_if_fail(fu_plugin_get_name(plugin) != NULL); + g_ptr_array_add(self->plugins, g_object_ref(plugin)); + g_hash_table_insert(self->plugins_hash, + g_strdup(fu_plugin_get_name(plugin)), + g_object_ref(plugin)); + g_signal_connect(FU_PLUGIN(plugin), + "rules-changed", + G_CALLBACK(fu_plugin_list_rules_changed_cb), + self); +} + +/** + * fu_plugin_list_find_by_name: + * @self: a #FuPluginList + * @name: a plugin name, e.g. `dfu` + * @error: (nullable): optional return location for an error + * + * Finds a specific plugin using the plugin name. + * + * Returns: (transfer none): a plugin, or %NULL + * + * Since: 1.0.2 + **/ +FuPlugin * +fu_plugin_list_find_by_name(FuPluginList *self, const gchar *name, GError **error) +{ + FuPlugin *plugin; + + g_return_val_if_fail(FU_IS_PLUGIN_LIST(self), NULL); + g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* sanity check */ + if (self->plugins->len == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no plugins loaded"); + return NULL; + } + + plugin = g_hash_table_lookup(self->plugins_hash, name); + if (plugin == NULL) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no plugin %s found", name); + return NULL; + } + return plugin; +} + +static gint +fu_plugin_list_sort_cb(gconstpointer a, gconstpointer b) +{ + FuPlugin **pa = (FuPlugin **)a; + FuPlugin **pb = (FuPlugin **)b; + return fu_plugin_order_compare(*pa, *pb); +} + +/** + * fu_plugin_list_depsolve: + * @self: a #FuPluginList + * @error: (nullable): optional return location for an error + * + * Depsolves the list of plugins into the correct order. Some plugin methods + * are called on all plugins and for some situations the order they are called + * may be important. Use fu_plugin_add_rule() to affect the depsolved order + * if required. + * + * Returns: %TRUE for success, or %FALSE if the set could not be depsolved + * + * Since: 1.0.2 + **/ +gboolean +fu_plugin_list_depsolve(FuPluginList *self, GError **error) +{ + FuPlugin *dep; + GPtrArray *deps; + gboolean changes; + guint dep_loop_check = 0; + + g_return_val_if_fail(FU_IS_PLUGIN_LIST(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* order by deps */ + do { + changes = FALSE; + 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_RUN_AFTER); + if (deps == NULL) + continue; + 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' " + "requested by '%s'", + plugin_name, + fu_plugin_get_name(plugin)); + continue; + } + if (fu_plugin_has_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + if (fu_plugin_get_order(plugin) <= fu_plugin_get_order(dep)) { + g_debug("%s [%u] to be ordered after %s [%u] " + "so promoting to [%u]", + fu_plugin_get_name(plugin), + fu_plugin_get_order(plugin), + fu_plugin_get_name(dep), + fu_plugin_get_order(dep), + fu_plugin_get_order(dep) + 1); + fu_plugin_set_order(plugin, fu_plugin_get_order(dep) + 1); + changes = TRUE; + } + } + } + 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_RUN_BEFORE); + if (deps == NULL) + continue; + 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' " + "requested by '%s'", + plugin_name, + fu_plugin_get_name(plugin)); + continue; + } + if (fu_plugin_has_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + if (fu_plugin_get_order(plugin) >= fu_plugin_get_order(dep)) { + g_debug("%s [%u] to be ordered before %s [%u] " + "so promoting to [%u]", + fu_plugin_get_name(plugin), + fu_plugin_get_order(plugin), + fu_plugin_get_name(dep), + fu_plugin_get_order(dep), + fu_plugin_get_order(dep) + 1); + fu_plugin_set_order(dep, fu_plugin_get_order(plugin) + 1); + 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); + if (deps == NULL) + continue; + 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_has_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED)) + 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 */ + if (dep_loop_check++ > 100) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "got stuck in dep loop"); + return FALSE; + } + } while (changes); + + /* check for conflicts */ + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(self->plugins, i); + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + deps = fu_plugin_get_rules(plugin, FU_PLUGIN_RULE_CONFLICTS); + if (deps == NULL) + continue; + for (guint j = 0; j < deps->len; 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) + continue; + if (fu_plugin_has_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + g_debug("disabling %s as conflicts with %s", + fu_plugin_get_name(dep), + fu_plugin_get_name(plugin)); + fu_plugin_add_flag(dep, FWUPD_PLUGIN_FLAG_DISABLED); + } + } + + /* sort by order */ + g_ptr_array_sort(self->plugins, fu_plugin_list_sort_cb); + return TRUE; +} + +static void +fu_plugin_list_class_init(FuPluginListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_plugin_list_finalize; +} + +static void +fu_plugin_list_init(FuPluginList *self) +{ + self->plugins = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + self->plugins_hash = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_object_unref); +} + +static void +fu_plugin_list_finalize(GObject *obj) +{ + FuPluginList *self = FU_PLUGIN_LIST(obj); + + g_ptr_array_unref(self->plugins); + g_hash_table_unref(self->plugins_hash); + + G_OBJECT_CLASS(fu_plugin_list_parent_class)->finalize(obj); +} + +/** + * fu_plugin_list_new: + * + * Creates a new plugin list. + * + * Returns: (transfer full): a #FuPluginList + * + * Since: 1.0.2 + **/ +FuPluginList * +fu_plugin_list_new(void) +{ + FuPluginList *self; + self = g_object_new(FU_TYPE_PLUGIN_LIST, NULL); + return FU_PLUGIN_LIST(self); +} diff --git a/fwupd-1.8.6/src/fu-plugin-list.h b/fwupd-1.8.6/src/fu-plugin-list.h new file mode 100644 index 0000000000000000000000000000000000000000..8817b13b2ee820531ba35cbd3cb15cf54ef9b8c0 --- /dev/null +++ b/fwupd-1.8.6/src/fu-plugin-list.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PLUGIN_LIST (fu_plugin_list_get_type()) +G_DECLARE_FINAL_TYPE(FuPluginList, fu_plugin_list, FU, PLUGIN_LIST, GObject) + +FuPluginList * +fu_plugin_list_new(void); +void +fu_plugin_list_add(FuPluginList *self, FuPlugin *plugin); +GPtrArray * +fu_plugin_list_get_all(FuPluginList *self); +FuPlugin * +fu_plugin_list_find_by_name(FuPluginList *self, const gchar *name, GError **error); +gboolean +fu_plugin_list_depsolve(FuPluginList *self, GError **error); diff --git a/fwupd-1.8.6/src/fu-polkit-agent.c b/fwupd-1.8.6/src/fu-polkit-agent.c new file mode 100644 index 0000000000000000000000000000000000000000..f2ca9cb5b38d7ab3eb884719b1356b156c2dc35a --- /dev/null +++ b/fwupd-1.8.6/src/fu-polkit-agent.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2011 Lennart Poettering + * Copyright (C) 2012 Matthias Klumpp + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#ifdef __linux__ +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fu-polkit-agent.h" + +static pid_t agent_pid = 0; + +static int +fork_agent(pid_t *pid, const char *path, ...) +{ + char **l; + gboolean stderr_is_tty; + gboolean stdout_is_tty; + int fd; + pid_t n_agent_pid; + pid_t parent_pid; + unsigned n, i; + va_list ap; + + g_return_val_if_fail(pid != 0, 0); + g_assert(path); + + parent_pid = getpid(); + + /* spawns a temporary TTY agent, making sure it goes away when + * we go away */ + n_agent_pid = fork(); + if (n_agent_pid < 0) + return -errno; + + if (n_agent_pid != 0) { + *pid = n_agent_pid; + return 0; + } + +#ifdef __linux__ + /* make sure the agent goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); +#endif + /* check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + /* TODO: it might be more clean to close all FDs so we don't leak them to the agent */ + stdout_is_tty = isatty(STDOUT_FILENO); + stderr_is_tty = isatty(STDERR_FILENO); + + if (!stdout_is_tty || !stderr_is_tty) { + /* Detach from stdout/stderr. and reopen + * /dev/tty for them. This is important to + * ensure that when systemctl is started via + * popen() or a similar call that expects to + * read EOF we actually do generate EOF and + * not delay this indefinitely by because we + * keep an unused copy of stdin around. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) { + g_error("Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + if (!stdout_is_tty) + dup2(fd, STDOUT_FILENO); + if (!stderr_is_tty) + dup2(fd, STDERR_FILENO); + if (fd > 2) + close(fd); + } + + /* count arguments */ + va_start(ap, path); + for (n = 0; va_arg(ap, char *); n++) + ; + va_end(ap); + + /* allocate strv */ + l = alloca(sizeof(char *) * (n + 1)); + + /* fill in arguments */ + va_start(ap, path); + for (i = 0; i <= n; i++) + l[i] = va_arg(ap, char *); + va_end(ap); + + execv(path, l); + _exit(EXIT_FAILURE); +} + +static int +close_nointr(int fd) +{ + g_assert(fd >= 0); + for (;;) { + int r; + r = close(fd); + if (r >= 0) + return r; + if (errno != EINTR) + return -errno; + } +} + +static void +close_nointr_nofail(int fd) +{ + int saved_errno = errno; + /* cannot fail, and guarantees errno is unchanged */ + g_assert(close_nointr(fd) == 0); + errno = saved_errno; +} + +static int +fd_wait_for_event(int fd, int event, uint64_t t) +{ + struct pollfd pollfd = {0}; + int r; + + pollfd.fd = fd; + pollfd.events = event; + r = poll(&pollfd, 1, t == (uint64_t)-1 ? -1 : (int)(t / 1000)); + if (r < 0) + return -errno; + if (r == 0) + return 0; + + return pollfd.revents; +} + +static int +wait_for_terminate(pid_t pid) +{ + g_return_val_if_fail(pid >= 1, 0); + + for (;;) { + int status; + if (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + return -errno; + } + return 0; + } +} + +gboolean +fu_polkit_agent_open(GError **error) +{ + int r; + int pipe_fd[2]; + g_autofree gchar *notify_fd = NULL; + g_autofree gchar *pkttyagent_fn = NULL; + + if (agent_pid > 0) + return TRUE; + + /* find binary */ + pkttyagent_fn = fu_path_find_program("pkttyagent", error); + if (pkttyagent_fn == NULL) + return FALSE; + + /* check STDIN here, not STDOUT, since this is about input, not output */ + if (!isatty(STDIN_FILENO)) + return TRUE; + if (pipe(pipe_fd) < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to create pipe: %s", + strerror(-errno)); + return FALSE; + } + + /* fork pkttyagent */ + notify_fd = g_strdup_printf("%i", pipe_fd[1]); + r = fork_agent(&agent_pid, + pkttyagent_fn, + pkttyagent_fn, + "--notify-fd", + notify_fd, + "--fallback", + NULL); + if (r < 0) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to fork TTY ask password agent: %s", + strerror(-r)); + close_nointr_nofail(pipe_fd[1]); + close_nointr_nofail(pipe_fd[0]); + return FALSE; + } + + /* close the writing side, because that is the one for the agent */ + close_nointr_nofail(pipe_fd[1]); + + /* wait until the agent closes the fd */ + fd_wait_for_event(pipe_fd[0], POLLHUP, (uint64_t)-1); + + close_nointr_nofail(pipe_fd[0]); + return TRUE; +} + +void +fu_polkit_agent_close(void) +{ + if (agent_pid <= 0) + return; + + /* inform agent that we are done */ + kill(agent_pid, SIGTERM); + kill(agent_pid, SIGCONT); + wait_for_terminate(agent_pid); + agent_pid = 0; +} diff --git a/fwupd-1.8.6/src/fu-polkit-agent.h b/fwupd-1.8.6/src/fu-polkit-agent.h new file mode 100644 index 0000000000000000000000000000000000000000..39e6e8df4d235584c65abc0b69c99e79840ae7d9 --- /dev/null +++ b/fwupd-1.8.6/src/fu-polkit-agent.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2011 Lennart Poettering + * Copyright (C) 2012 Matthias Klumpp + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +gboolean +fu_polkit_agent_open(GError **error); +void +fu_polkit_agent_close(void); diff --git a/fwupd-1.8.6/src/fu-progressbar.c b/fwupd-1.8.6/src/fu-progressbar.c new file mode 100644 index 0000000000000000000000000000000000000000..f747c759b91a33f6aeecd6952c948348e6144a92 --- /dev/null +++ b/fwupd-1.8.6/src/fu-progressbar.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuProgressBar" + +#include "config.h" + +#include + +#include "fu-progressbar.h" + +static void +fu_progressbar_finalize(GObject *obj); + +struct _FuProgressbar { + GObject parent_instance; + GMainContext *main_ctx; + FwupdStatus status; + gboolean spinner_count_up; /* chars */ + guint spinner_idx; /* chars */ + guint length_percentage; /* chars */ + guint length_status; /* chars */ + guint percentage; + GSource *timer_source; + gint64 last_animated; /* monotonic */ + GTimer *time_elapsed; + gdouble last_estimate; + gboolean interactive; +}; + +G_DEFINE_TYPE(FuProgressbar, fu_progressbar, G_TYPE_OBJECT) + +static const gchar * +fu_progressbar_status_to_string(FwupdStatus status) +{ + switch (status) { + case FWUPD_STATUS_IDLE: + /* TRANSLATORS: daemon is inactive */ + return _("Idle…"); + break; + case FWUPD_STATUS_DECOMPRESSING: + /* TRANSLATORS: decompressing the firmware file */ + return _("Decompressing…"); + break; + case FWUPD_STATUS_LOADING: + /* TRANSLATORS: parsing the firmware information */ + return _("Loading…"); + break; + case FWUPD_STATUS_DEVICE_RESTART: + /* TRANSLATORS: restarting the device to pick up new F/W */ + return _("Restarting device…"); + break; + case FWUPD_STATUS_DEVICE_READ: + /* TRANSLATORS: reading from the flash chips */ + return _("Reading…"); + break; + case FWUPD_STATUS_DEVICE_WRITE: + /* TRANSLATORS: writing to the flash chips */ + return _("Writing…"); + break; + case FWUPD_STATUS_DEVICE_ERASE: + /* TRANSLATORS: erasing contents of the flash chips */ + return _("Erasing…"); + break; + case FWUPD_STATUS_DEVICE_VERIFY: + /* TRANSLATORS: verifying we wrote the firmware correctly */ + return _("Verifying…"); + break; + case FWUPD_STATUS_SCHEDULING: + /* TRANSLATORS: scheduling an update to be done on the next boot */ + return _("Scheduling…"); + break; + case FWUPD_STATUS_DOWNLOADING: + /* TRANSLATORS: downloading from a remote server */ + return _("Downloading…"); + break; + case FWUPD_STATUS_WAITING_FOR_AUTH: + /* TRANSLATORS: waiting for user to authenticate */ + return _("Authenticating…"); + break; + case FWUPD_STATUS_DEVICE_BUSY: + /* TRANSLATORS: waiting for device to do something */ + return _("Waiting…"); + break; + default: + break; + } + + /* TRANSLATORS: current daemon status is unknown */ + return _("Unknown"); +} + +static void +fu_progressbar_erase_line(FuProgressbar *self) +{ + if (!self->interactive) + return; + g_print("\033[G"); +} + +static gboolean +_fu_status_is_predictable(FwupdStatus status) +{ + if (status == FWUPD_STATUS_DEVICE_ERASE) + return TRUE; + if (status == FWUPD_STATUS_DEVICE_VERIFY) + return TRUE; + if (status == FWUPD_STATUS_DEVICE_READ) + return TRUE; + if (status == FWUPD_STATUS_DEVICE_WRITE) + return TRUE; + if (status == FWUPD_STATUS_DOWNLOADING) + return TRUE; + return FALSE; +} + +static gboolean +fu_progressbar_estimate_ready(FuProgressbar *self, guint percentage) +{ + gdouble old; + gdouble elapsed; + + /* now invalid */ + if (percentage == 0 || percentage == 100) { + g_timer_start(self->time_elapsed); + self->last_estimate = 0; + return FALSE; + } + + /* allow-list things that make sense... */ + if (!_fu_status_is_predictable(self->status)) + 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")); + } + + return g_strdup_printf( + /* TRANSLATORS: more than a minute */ + 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; + guint i; + gboolean is_idle_newline = FALSE; + g_autoptr(GString) str = g_string_new(NULL); + + g_return_if_fail(percentage <= 100); + + /* erase previous line */ + fu_progressbar_erase_line(self); + + /* add status */ + if (status == FWUPD_STATUS_IDLE || status == FWUPD_STATUS_UNKNOWN) { + status = self->status; + is_idle_newline = TRUE; + } + if (percentage == 100) + is_idle_newline = TRUE; + title = fu_progressbar_status_to_string(status); + g_string_append(str, title); + for (i = fu_strwidth(str->str); i < self->length_status; i++) + g_string_append_c(str, ' '); + + /* add progressbar */ + g_string_append(str, "["); + if (percentage > 0) { + for (i = 0; i < (self->length_percentage - 1) * percentage / 100; i++) + g_string_append_c(str, '*'); + for (i = i + 1; i < self->length_percentage; i++) + g_string_append_c(str, ' '); + } else { + const gchar chars[] = { + '-', + '\\', + '|', + '/', + }; + for (i = 0; i < self->spinner_idx; i++) + g_string_append_c(str, ' '); + g_string_append_c(str, chars[i / 4 % G_N_ELEMENTS(chars)]); + for (i = i + 1; i < self->length_percentage - 1; i++) + g_string_append_c(str, ' '); + } + 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); + + /* done */ + if (is_idle_newline) { + g_print("\n"); + return; + } +} + +/** + * fu_progressbar_set_title: + * @self: A #FuProgressbar + * @title: A string + * + * Sets progressbar title + * + * Since: 0.9.7 + **/ +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); +} + +/** + * fu_progressbar_set_main_context: + * @self: A #FuProgressbar + * @main_ctx: (nullable): main context + * + * Sets progressbar main context to use for animations. + * + * Since: 1.6.2 + **/ +void +fu_progressbar_set_main_context(FuProgressbar *self, GMainContext *main_ctx) +{ + self->main_ctx = g_main_context_ref(main_ctx); +} + +static void +fu_progressbar_spin_inc(FuProgressbar *self) +{ + /* reset */ + self->last_animated = g_get_monotonic_time(); + + /* up to down */ + if (self->spinner_count_up) { + if (++self->spinner_idx > self->length_percentage - 3) + self->spinner_count_up = FALSE; + } else { + if (--self->spinner_idx == 0) + self->spinner_count_up = TRUE; + } +} + +static gboolean +fu_progressbar_spin_cb(gpointer user_data) +{ + FuProgressbar *self = FU_PROGRESSBAR(user_data); + + /* ignore */ + if (self->status == FWUPD_STATUS_IDLE || self->status == FWUPD_STATUS_UNKNOWN) + return G_SOURCE_CONTINUE; + + /* move the spinner index up to down */ + fu_progressbar_spin_inc(self); + + /* update the terminal */ + fu_progressbar_refresh(self, self->status, self->percentage); + + return G_SOURCE_CONTINUE; +} + +static void +fu_progressbar_spin_end(FuProgressbar *self) +{ + if (self->timer_source != NULL) { + g_source_destroy(self->timer_source); + self->timer_source = NULL; + + /* 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 */ + self->spinner_idx = 0; + self->spinner_count_up = TRUE; +} + +static void +fu_progressbar_spin_start(FuProgressbar *self) +{ + if (self->timer_source != NULL) + g_source_destroy(self->timer_source); + self->timer_source = g_timeout_source_new(40); + g_source_set_callback(self->timer_source, fu_progressbar_spin_cb, self, NULL); + g_source_attach(self->timer_source, self->main_ctx); +} + +/** + * fu_progressbar_update: + * @self: A #FuProgressbar + * @status: A #FwupdStatus + * @percentage: unsigned integer + * + * Refreshes a progressbar + * + * Since: 0.9.7 + **/ +void +fu_progressbar_update(FuProgressbar *self, FwupdStatus status, guint percentage) +{ + g_return_if_fail(FU_IS_PROGRESSBAR(self)); + + /* not useful */ + if (status == FWUPD_STATUS_UNKNOWN) + return; + + /* ignore initial client connection */ + if (self->status == FWUPD_STATUS_UNKNOWN && status == FWUPD_STATUS_IDLE) { + self->status = status; + return; + } + + if (!self->interactive) { + g_print("%s: %u%%\n", fu_progressbar_status_to_string(status), percentage); + self->status = status; + self->percentage = percentage; + 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 && status != FWUPD_STATUS_IDLE && + self->status != FWUPD_STATUS_UNKNOWN) { + if ((g_get_monotonic_time() - self->last_animated) / 1000 > 40) { + fu_progressbar_spin_inc(self); + fu_progressbar_refresh(self, status, percentage); + } + } + + /* ignore duplicates */ + if (self->status == status && self->percentage == percentage) + return; + + /* enable or disable the spinner timeout */ + if (percentage > 0) { + fu_progressbar_spin_end(self); + } else { + fu_progressbar_spin_start(self); + } + + /* update the terminal */ + fu_progressbar_refresh(self, status, percentage); + + /* cache */ + self->status = status; + self->percentage = percentage; +} + +/** + * fu_progressbar_set_interactive: + * @self: A #FuProgressbar + * @interactive: #gboolean + * + * Marks the progressbar as interactive or not + * + * Since: 0.9.7 + **/ +void +fu_progressbar_set_interactive(FuProgressbar *self, gboolean interactive) +{ + g_return_if_fail(FU_IS_PROGRESSBAR(self)); + self->interactive = interactive; +} + +/** + * fu_progressbar_set_length_status: + * @self: A #FuProgressbar + * @len: unsigned integer + * + * Sets the length of the progressbar status + * + * Since: 0.9.7 + **/ +void +fu_progressbar_set_length_status(FuProgressbar *self, guint len) +{ + g_return_if_fail(FU_IS_PROGRESSBAR(self)); + g_return_if_fail(len > 3); + self->length_status = len; +} + +/** + * fu_progressbar_set_length_percentage: + * @self: A #FuProgressbar + * @len: unsigned integer + * + * Sets the length of the progressba percentage + * + * Since: 0.9.7 + **/ +void +fu_progressbar_set_length_percentage(FuProgressbar *self, guint len) +{ + g_return_if_fail(FU_IS_PROGRESSBAR(self)); + g_return_if_fail(len > 3); + self->length_percentage = len; +} + +static void +fu_progressbar_class_init(FuProgressbarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_progressbar_finalize; +} + +static void +fu_progressbar_init(FuProgressbar *self) +{ + self->length_percentage = 40; + self->length_status = 25; + self->spinner_count_up = TRUE; + self->time_elapsed = g_timer_new(); + self->interactive = TRUE; +} + +static void +fu_progressbar_finalize(GObject *obj) +{ + FuProgressbar *self = FU_PROGRESSBAR(obj); + + if (self->timer_source != 0) + g_source_destroy(self->timer_source); + if (self->main_ctx != NULL) + g_main_context_unref(self->main_ctx); + g_timer_destroy(self->time_elapsed); + + G_OBJECT_CLASS(fu_progressbar_parent_class)->finalize(obj); +} + +/** + * fu_progressbar_new: + * + * Creates a new #FuProgressbar + * + * Since: 0.9.7 + **/ +FuProgressbar * +fu_progressbar_new(void) +{ + FuProgressbar *self; + self = g_object_new(FU_TYPE_PROGRESSBAR, NULL); + return FU_PROGRESSBAR(self); +} diff --git a/fwupd-1.8.6/src/fu-progressbar.h b/fwupd-1.8.6/src/fu-progressbar.h new file mode 100644 index 0000000000000000000000000000000000000000..1abee0b585a5ce650bb4ef15913c1ea5408e4366 --- /dev/null +++ b/fwupd-1.8.6/src/fu-progressbar.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_PROGRESSBAR (fu_progressbar_get_type()) +G_DECLARE_FINAL_TYPE(FuProgressbar, fu_progressbar, FU, PROGRESSBAR, GObject) + +FuProgressbar * +fu_progressbar_new(void); +void +fu_progressbar_update(FuProgressbar *self, FwupdStatus status, guint percentage); +void +fu_progressbar_set_length_status(FuProgressbar *self, 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); +void +fu_progressbar_set_main_context(FuProgressbar *self, GMainContext *main_ctx); diff --git a/fwupd-1.8.6/src/fu-release-common.c b/fwupd-1.8.6/src/fu-release-common.c new file mode 100644 index 0000000000000000000000000000000000000000..48fce208d58d82373d3d50bd21de53a7029af873 --- /dev/null +++ b/fwupd-1.8.6/src/fu-release-common.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuSpawn" + +#include "config.h" + +#include "fu-release-common.h" + +/** + * fu_release_uri_get_scheme: + * @uri: valid URI, e.g. `https://foo.bar/baz` + * + * Returns the USI scheme for the given URI. + * + * Returns: scheme value, or %NULL if invalid, e.g. `https` + **/ +gchar * +fu_release_uri_get_scheme(const gchar *uri) +{ + gchar *tmp; + + g_return_val_if_fail(uri != NULL, NULL); + + tmp = g_strstr_len(uri, -1, ":"); + if (tmp == NULL || tmp[0] == '\0') + return NULL; + return g_utf8_strdown(uri, tmp - uri); +} diff --git a/fwupd-1.8.6/src/fu-release-common.h b/fwupd-1.8.6/src/fu-release-common.h new file mode 100644 index 0000000000000000000000000000000000000000..e2494104ef1b3dc4ecb15ff719394e2578bf55e6 --- /dev/null +++ b/fwupd-1.8.6/src/fu-release-common.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +gchar * +fu_release_uri_get_scheme(const gchar *uri); diff --git a/fwupd-1.8.6/src/fu-release.c b/fwupd-1.8.6/src/fu-release.c new file mode 100644 index 0000000000000000000000000000000000000000..30788b6f0af7b6b35942337f27ea37cc2a5e6f1c --- /dev/null +++ b/fwupd-1.8.6/src/fu-release.c @@ -0,0 +1,1076 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuRelease" + +#include "config.h" + +#include "fu-device-private.h" +#include "fu-keyring-utils.h" +#include "fu-release-common.h" +#include "fu-release.h" + +/** + * FuRelease: + * + * An installable entity that has been loaded and verified for a specific device. + * + * See also: [class@FwupdRelease] + */ + +struct _FuRelease { + FwupdRelease parent_instance; + FuEngineRequest *request; + FuDevice *device; + FwupdRemote *remote; + FuConfig *config; + GBytes *blob_fw; + gchar *update_request_id; + FwupdReleaseFlags trust_flags; + gboolean is_downgrade; + GPtrArray *soft_reqs; /* nullable, element-type XbNode */ + GPtrArray *hard_reqs; /* nullable, element-type XbNode */ +}; + +G_DEFINE_TYPE(FuRelease, fu_release, FWUPD_TYPE_RELEASE) + +/** + * fu_release_set_request: + * @self: a #FuRelease + * @request: (nullable): a #FuEngineRequest + * + * Sets the user request which created this operation. + **/ +void +fu_release_set_request(FuRelease *self, FuEngineRequest *request) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->request, request); +} + +/** + * fu_release_get_request: + * @self: a #FuRelease + * + * Gets the user request which created this operation. + * + * Returns: (transfer none) (nullable): request + **/ +FuEngineRequest * +fu_release_get_request(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->request; +} + +/** + * fu_release_set_device: + * @self: a #FuRelease + * @device: (nullable): a #FuDevice + * + * Sets the device this release should use when checking requirements. + **/ +void +fu_release_set_device(FuRelease *self, FuDevice *device) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->device, device); +} + +/** + * fu_release_get_device: + * @self: a #FuRelease + * + * Gets the device this release was loaded for. + * + * Returns: (transfer none) (nullable): device + **/ +FuDevice * +fu_release_get_device(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->device; +} + +/** + * fu_release_get_fw_blob: + * @self: a #FuRelease + * + * Gets the firmware payload to use when installing this release. + * + * Returns: (transfer none) (nullable): data + **/ +GBytes * +fu_release_get_fw_blob(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->blob_fw; +} + +/** + * fu_release_get_soft_reqs: + * @self: a #FuRelease + * + * Gets the additional soft requirements that need to be checked in the engine. + * + * Returns: (transfer none) (nullable) (element-type XbNode): nodes + **/ +GPtrArray * +fu_release_get_soft_reqs(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->soft_reqs; +} + +/** + * fu_release_get_soft_reqs: + * @self: a #FuRelease + * + * Gets the additional hard requirements that need to be checked in the engine. + * + * Returns: (transfer none) (nullable) (element-type XbNode): nodes + **/ +GPtrArray * +fu_release_get_hard_reqs(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->hard_reqs; +} + +/** + * fu_release_get_update_request_id: + * @self: a #FuRelease + * + * Gets the update request ID as specified from `LVFS::UpdateRequestId`. + * + * Returns: a string value, or %NULL if never set. + **/ +const gchar * +fu_release_get_update_request_id(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->update_request_id; +} + +static void +fu_release_set_update_request_id(FuRelease *self, const gchar *update_request_id) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(self->update_request_id, update_request_id) == 0) + return; + + g_free(self->update_request_id); + self->update_request_id = g_strdup(update_request_id); +} + +/** + * fu_release_set_remote: + * @self: a #FuRelease + * @remote: (nullable): a #FwupdRemote + * + * Sets the remote this release should use when loading. This is typically set by the engine by + *watching the `remote-id` property to be set and then querying the internal cached list of + *`FuRemote`s. + **/ +void +fu_release_set_remote(FuRelease *self, FwupdRemote *remote) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->remote, remote); +} + +/** + * fu_release_set_config: + * @self: a #FuRelease + * @config: (nullable): a #FuConfig + * + * Sets the config to use when loading. The config may be used for things like ordering attributes + *like protocol priority. + **/ +void +fu_release_set_config(FuRelease *self, FuConfig *config) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->config, config); +} + +static gchar * +fu_release_get_localized_xpath(FuRelease *self, const gchar *element) +{ + GString *xpath = g_string_new(element); + const gchar *locale = NULL; + + /* optional; not set in tests */ + if (self->request != NULL) + locale = fu_engine_request_get_locale(self->request); + + /* prefer the users locale if set */ + if (locale != NULL) { + g_autofree gchar *xpath_locale = NULL; + xpath_locale = g_strdup_printf("%s[@xml:lang='%s']|", element, locale); + g_string_prepend(xpath, xpath_locale); + } + return g_string_free(xpath, FALSE); +} + +/* convert hex and decimal versions to dotted style */ +static gchar * +fu_release_get_release_version(FuRelease *self, const gchar *version, GError **error) +{ + FwupdVersionFormat fmt = fu_device_get_version_format(self->device); + guint64 ver_uint32; + g_autoptr(GError) error_local = NULL; + + /* already dotted notation */ + if (g_strstr_len(version, -1, ".") != NULL) + return g_strdup(version); + + /* don't touch my version! */ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN || fmt == FWUPD_VERSION_FORMAT_UNKNOWN) + return g_strdup(version); + + /* parse as integer */ + if (!fu_strtoull(version, &ver_uint32, 1, G_MAXUINT32, &error_local)) { + g_warning("invalid release version %s: %s", version, error_local->message); + return g_strdup(version); + } + + /* convert to dotted decimal */ + return fu_version_from_uint32((guint32)ver_uint32, fmt); +} + +static gboolean +fu_release_load_artifact(FuRelease *self, XbNode *artifact, GError **error) +{ + const gchar *filename; + guint64 size; + g_autoptr(GPtrArray) locations = NULL; + g_autoptr(GPtrArray) checksums = NULL; + + /* filename */ + filename = xb_node_query_text(artifact, "filename", NULL); + if (filename != NULL && !g_str_has_suffix(filename, ".cab")) { + /* some firmware archives was signed with where the + * checksums were the *content* checksums, not the *container* checksum */ + g_debug("ignoring non-binary artifact entry: %s", filename); + return TRUE; + } + if (filename != NULL) + fwupd_release_set_filename(FWUPD_RELEASE(self), filename); + + /* location */ + locations = xb_node_query(artifact, "location", 0, NULL); + if (locations != NULL) { + for (guint i = 0; i < locations->len; i++) { + XbNode *n = g_ptr_array_index(locations, i); + g_autofree gchar *scheme = NULL; + + /* check the scheme is allowed */ + scheme = fu_release_uri_get_scheme(xb_node_get_text(n)); + if (scheme != NULL) { + guint prio = fu_config_get_uri_scheme_prio(self->config, scheme); + if (prio == G_MAXUINT) + continue; + } + + /* build the complete URI */ + if (self->remote != NULL) { + g_autofree gchar *uri = NULL; + uri = fwupd_remote_build_firmware_uri(self->remote, + xb_node_get_text(n), + NULL); + if (uri != NULL) { + fwupd_release_add_location(FWUPD_RELEASE(self), uri); + continue; + } + } + fwupd_release_add_location(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + + /* checksum */ + checksums = xb_node_query(artifact, "checksum", 0, NULL); + if (checksums != NULL) { + for (guint i = 0; i < checksums->len; i++) { + XbNode *n = g_ptr_array_index(checksums, i); + fwupd_release_add_checksum(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + + /* size */ + size = xb_node_query_text_as_uint(artifact, "size[@type='installed']", NULL); + if (size != G_MAXUINT64) + fwupd_release_set_size(FWUPD_RELEASE(self), size); + + /* success */ + return TRUE; +} + +static gint +fu_release_scheme_compare_cb(gconstpointer a, gconstpointer b, gpointer user_data) +{ + FuRelease *self = FU_RELEASE(user_data); + const gchar *location1 = *((const gchar **)a); + const gchar *location2 = *((const gchar **)b); + g_autofree gchar *scheme1 = fu_release_uri_get_scheme(location1); + g_autofree gchar *scheme2 = fu_release_uri_get_scheme(location2); + guint prio1 = fu_config_get_uri_scheme_prio(self->config, scheme1); + guint prio2 = fu_config_get_uri_scheme_prio(self->config, scheme2); + if (prio1 < prio2) + return -1; + if (prio1 > prio2) + return 1; + return 0; +} + +static gboolean +fu_release_check_requirements_version_check(FuRelease *self, GError **error) +{ + if (self->hard_reqs != NULL) { + for (guint i = 0; i < self->hard_reqs->len; i++) { + XbNode *req = g_ptr_array_index(self->hard_reqs, i); + if (g_strcmp0(xb_node_get_element(req), "firmware") == 0 && + xb_node_get_text(req) == NULL) { + return TRUE; + } + } + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no firmware requirement"); + return FALSE; +} + +static gchar * +fu_release_verfmts_to_string(GPtrArray *verfmts) +{ + GString *str = g_string_new(NULL); + for (guint i = 0; i < verfmts->len; i++) { + XbNode *verfmt = g_ptr_array_index(verfmts, i); + const gchar *tmp = xb_node_get_text(verfmt); + g_string_append_printf(str, "%s;", tmp); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + return g_string_free(str, FALSE); +} + +static gboolean +fu_release_check_verfmt(FuRelease *self, + GPtrArray *verfmts, + FwupdInstallFlags flags, + GError **error) +{ + FwupdVersionFormat fmt_dev = fu_device_get_version_format(self->device); + g_autofree gchar *verfmts_str = NULL; + + /* no device format */ + if (fmt_dev == FWUPD_VERSION_FORMAT_UNKNOWN && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + verfmts_str = fu_release_verfmts_to_string(verfmts); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "release version format '%s' but no device version format", + verfmts_str); + return FALSE; + } + + /* compare all version formats */ + for (guint i = 0; i < verfmts->len; i++) { + XbNode *verfmt = g_ptr_array_index(verfmts, i); + const gchar *tmp = xb_node_get_text(verfmt); + FwupdVersionFormat fmt_rel = fwupd_version_format_from_string(tmp); + if (fmt_dev == fmt_rel) + return TRUE; + } + verfmts_str = fu_release_verfmts_to_string(verfmts); + 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), + verfmts_str); + return FALSE; + } + g_warning("ignoring version format difference %s:%s", + fwupd_version_format_to_string(fmt_dev), + verfmts_str); + return TRUE; +} + +/* these can all be done without the daemon */ +static gboolean +fu_release_check_requirements(FuRelease *self, + XbNode *component, + XbNode *rel, + FwupdInstallFlags install_flags, + GError **error) +{ + const gchar *branch_new; + const gchar *branch_old; + const gchar *protocol; + const gchar *version; + const gchar *version_lowest; + gboolean matches_guid = FALSE; + gint vercmp; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(GPtrArray) verfmts = NULL; + + /* does this component provide a GUID the device has */ + provides = xb_node_query(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++) { + XbNode *provide = g_ptr_array_index(provides, i); + if (fu_device_has_guid(self->device, xb_node_get_text(provide))) { + matches_guid = TRUE; + break; + } + } + if (!matches_guid) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No supported devices found"); + return FALSE; + } + + /* device requires a version check */ + if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED)) { + if (!fu_release_check_requirements_version_check(self, error)) { + g_prefix_error(error, "device requires firmware with a version check: "); + return FALSE; + } + } + + /* does the protocol match */ + protocol = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); + if (fu_device_get_protocols(self->device)->len != 0 && protocol != NULL && + !fu_device_has_protocol(self->device, protocol) && + (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_autofree gchar *str = NULL; + str = fu_strjoin("|", fu_device_get_protocols(self->device)); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s does not support %s, only %s", + fu_device_get_name(self->device), + protocol, + str); + return FALSE; + } + + /* check the device is not locked */ + if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_LOCKED)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] is locked", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + return FALSE; + } + + /* check the branch is not switching */ + branch_new = xb_node_query_text(component, "branch", NULL); + branch_old = fu_device_get_branch(self->device); + if ((install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0 && + g_strcmp0(branch_old, branch_new) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] would switch firmware branch from %s to %s", + fu_device_get_name(self->device), + fu_device_get_id(self->device), + branch_old != NULL ? branch_old : "default", + branch_new != NULL ? branch_new : "default"); + return FALSE; + } + + /* no update abilities */ + if (!fu_engine_request_has_feature_flag(self->request, FWUPD_FEATURE_FLAG_SHOW_PROBLEMS) && + !fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_autoptr(GString) str = g_string_new(NULL); + g_string_append_printf(str, + "Device %s [%s] does not currently allow updates", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + if (fu_device_get_update_error(self->device) != NULL) { + g_string_append_printf(str, + ": %s", + fu_device_get_update_error(self->device)); + } + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, str->str); + return FALSE; + } + + /* called with online update, test if device is supposed to allow this */ + if ((install_flags & FWUPD_INSTALL_FLAG_OFFLINE) == 0 && + (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] only allows offline updates", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + return FALSE; + } + + /* get device */ + version = fu_device_get_version(self->device); + if (version == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s [%s] has no firmware version", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + return FALSE; + } + + /* check the version formats match if set in the release */ + if ((install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { + verfmts = + xb_node_query(component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL); + if (verfmts != NULL) { + if (!fu_release_check_verfmt(self, verfmts, install_flags, error)) + return FALSE; + } + } + + /* compare to the lowest supported version, if it exists */ + version_lowest = fu_device_get_version_lowest(self->device); + if (version_lowest != NULL && + fu_version_compare(version_lowest, + fu_release_get_version(self), + fu_device_get_version_format(self->device)) > 0 && + (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Specified firmware is older than the minimum " + "required version '%s < %s'", + version, + version_lowest); + return FALSE; + } + + /* is this a downgrade or re-install */ + vercmp = fu_version_compare(version, + fu_release_get_version(self), + fu_device_get_version_format(self->device)); + if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE) && + vercmp >= 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device only supports version upgrades"); + return FALSE; + } + if (vercmp == 0 && (install_flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_SAME, + "Specified firmware is already installed '%s'", + fu_release_get_version(self)); + return FALSE; + } + self->is_downgrade = vercmp > 0; + if (self->is_downgrade && (install_flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0 && + (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_NEWER, + "Specified firmware is older than installed '%s < %s'", + fu_release_get_version(self), + version); + return FALSE; + } + + /* verify */ + if (!fu_keyring_get_release_flags(rel, &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; +} + +/** + * fu_release_load: + * @self: a #FuRelease + * @component: (not nullable): a #XbNode + * @rel_optional: (nullable): a #XbNode + * @install_flags: a #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Loads then checks any requirements of this release. This will typically involve checking + * that the device can accept the component (the GUIDs match) and that the device can be + * upgraded with this firmware version. + * + * Returns: %TRUE if the release was loaded and the requirements passed + **/ +gboolean +fu_release_load(FuRelease *self, + XbNode *component, + XbNode *rel_optional, + FwupdInstallFlags install_flags, + GError **error) +{ + const gchar *tmp; + guint64 tmp64; + GBytes *blob_fw_tmp; + g_autofree gchar *name_xpath = NULL; + g_autofree gchar *namevs_xpath = NULL; + g_autofree gchar *summary_xpath = NULL; + g_autofree gchar *description_xpath = NULL; + g_autoptr(GPtrArray) cats = NULL; + g_autoptr(GPtrArray) tags = NULL; + g_autoptr(GPtrArray) issues = NULL; + g_autoptr(XbNode) artifact = NULL; + g_autoptr(XbNode) description = NULL; + g_autoptr(XbNode) rel = NULL; + g_autoptr(GError) error_soft = NULL; + g_autoptr(GError) error_hard = NULL; + + g_return_val_if_fail(FU_IS_RELEASE(self), FALSE); + g_return_val_if_fail(XB_IS_NODE(component), FALSE); + g_return_val_if_fail(rel_optional == NULL || XB_IS_NODE(rel_optional), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(fwupd_release_get_appstream_id(FWUPD_RELEASE(self)) == NULL, FALSE); + + /* set from the component */ + tmp = xb_node_query_text(component, "id", NULL); + if (tmp != NULL) + fwupd_release_set_appstream_id(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "url[@type='homepage']", NULL); + if (tmp != NULL) + fwupd_release_set_homepage(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "project_license", NULL); + if (tmp != NULL) + fwupd_release_set_license(FWUPD_RELEASE(self), tmp); + name_xpath = fu_release_get_localized_xpath(self, "name"); + tmp = xb_node_query_text(component, name_xpath, NULL); + if (tmp != NULL) + fwupd_release_set_name(FWUPD_RELEASE(self), tmp); + summary_xpath = fu_release_get_localized_xpath(self, "summary"); + tmp = xb_node_query_text(component, summary_xpath, NULL); + if (tmp != NULL) + fwupd_release_set_summary(FWUPD_RELEASE(self), tmp); + namevs_xpath = fu_release_get_localized_xpath(self, "name_variant_suffix"); + tmp = xb_node_query_text(component, namevs_xpath, NULL); + if (tmp != NULL) + fwupd_release_set_name_variant_suffix(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "branch", NULL); + if (tmp != NULL) + fwupd_release_set_branch(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "developer_name", NULL); + if (tmp != NULL) + fwupd_release_set_vendor(FWUPD_RELEASE(self), tmp); + + /* use default release */ + if (rel_optional == NULL) { + g_autoptr(GError) error_local = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + error); + if (query == NULL) + return FALSE; + rel = xb_node_query_first_full(component, query, &error_local); +#else + rel = xb_node_query_first(component, "releases/release", &error_local); +#endif + if (rel == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get default release: %s", + error_local->message); + return FALSE; + } + } else { + rel = g_object_ref(rel_optional); + } + + /* the version is fixed up with the device format */ + tmp = xb_node_get_attr(rel, "version"); + if (tmp == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "version unset"); + return FALSE; + } + if (self->device != NULL) { + g_autofree gchar *version_rel = NULL; + version_rel = fu_release_get_release_version(self, tmp, error); + if (version_rel == NULL) + return FALSE; + fwupd_release_set_version(FWUPD_RELEASE(self), version_rel); + } else { + fwupd_release_set_version(FWUPD_RELEASE(self), tmp); + } + + /* optional release ID -- currently a integer but maybe namespaced in the future */ + fwupd_release_set_id(FWUPD_RELEASE(self), xb_node_get_attr(rel, "id")); + + /* find the remote */ + tmp = xb_node_query_text(component, "../custom/value[@key='fwupd::RemoteId']", NULL); + if (tmp != NULL) + fwupd_release_set_remote_id(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "../custom/value[@key='LVFS::Distributor']", NULL); + if (g_strcmp0(tmp, "community") == 0) + fwupd_release_add_flag(FWUPD_RELEASE(self), FWUPD_RELEASE_FLAG_IS_COMMUNITY); + + /* this is the more modern way to do this */ + artifact = xb_node_query_first(rel, "artifacts/artifact[@type='binary']", NULL); + if (artifact != NULL) { + if (!fu_release_load_artifact(self, artifact, error)) + return FALSE; + } + description_xpath = fu_release_get_localized_xpath(self, "description"); + description = xb_node_query_first(rel, description_xpath, NULL); + if (description != NULL) { + g_autofree gchar *xml = NULL; + g_autoptr(GString) str = NULL; + xml = xb_node_export(description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL); + str = g_string_new(xml); + if (self->device != NULL && self->request != NULL && + fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_AFFECTS_FDE) && + !fu_engine_request_has_feature_flag(self->request, + FWUPD_FEATURE_FLAG_FDE_WARNING)) { + g_string_prepend( + str, + "

    Some of the platform secrets may be invalidated when " + "updating this firmware. Please ensure you have the volume " + "recovery key before continuing.

    "); + } + if (fwupd_release_has_flag(FWUPD_RELEASE(self), FWUPD_RELEASE_FLAG_IS_COMMUNITY) && + self->request != NULL && + !fu_engine_request_has_feature_flag(self->request, + FWUPD_FEATURE_FLAG_COMMUNITY_TEXT)) { + g_string_prepend( + str, + "

    This firmware is provided by LVFS community " + "members and is not provided (or supported) by the original " + "hardware vendor. " + "Installing this update may also void any device warranty.

    "); + } + if (str->len > 0) + fwupd_release_set_description(FWUPD_RELEASE(self), str->str); + } + if (artifact == NULL) { + tmp = xb_node_query_text(rel, "location", NULL); + if (tmp != NULL) { + g_autofree gchar *uri = NULL; + if (self->remote != NULL) + uri = fwupd_remote_build_firmware_uri(self->remote, tmp, NULL); + if (uri == NULL) + uri = g_strdup(tmp); + fwupd_release_add_location(FWUPD_RELEASE(self), uri); + } + } + if (fwupd_release_get_locations(FWUPD_RELEASE(self))->len == 0 && self->remote != NULL && + fwupd_remote_get_kind(self->remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + tmp = xb_node_query_text(component, + "../custom/value[@key='fwupd::FilenameCache']", + NULL); + if (tmp != NULL) { + g_autofree gchar *uri = g_strdup_printf("file://%s", tmp); + fwupd_release_add_location(FWUPD_RELEASE(self), uri); + } + } + if (artifact == NULL) { + tmp = xb_node_query_text(rel, "checksum[@target='content']", NULL); + if (tmp != NULL) + fwupd_release_set_filename(FWUPD_RELEASE(self), tmp); + } + tmp = xb_node_query_text(rel, "url[@type='details']", NULL); + if (tmp != NULL) + fwupd_release_set_details_url(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(rel, "url[@type='source']", NULL); + if (tmp != NULL) + fwupd_release_set_source_url(FWUPD_RELEASE(self), tmp); + if (artifact == NULL) { + g_autoptr(GPtrArray) checksums = NULL; + checksums = xb_node_query(rel, "checksum[@target='container']", 0, NULL); + if (checksums != NULL) { + for (guint i = 0; i < checksums->len; i++) { + XbNode *n = g_ptr_array_index(checksums, i); + if (xb_node_get_text(n) == NULL) + continue; + fwupd_release_add_checksum(FWUPD_RELEASE(self), + xb_node_get_text(n)); + } + } + } + if (artifact == NULL) { + tmp64 = xb_node_query_text_as_uint(rel, "size[@type='installed']", NULL); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_size(FWUPD_RELEASE(self), tmp64); + } + if (fwupd_release_get_size(FWUPD_RELEASE(self)) == 0) { + GBytes *sz = xb_node_get_data(rel, "fwupd::ReleaseSize"); + if (sz != NULL) { + const guint64 *sizeptr = g_bytes_get_data(sz, NULL); + fwupd_release_set_size(FWUPD_RELEASE(self), *sizeptr); + } + } + tmp = xb_node_get_attr(rel, "urgency"); + if (tmp != NULL) + fwupd_release_set_urgency(FWUPD_RELEASE(self), + fwupd_release_urgency_from_string(tmp)); + tmp64 = xb_node_get_attr_as_uint(rel, "install_duration"); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_install_duration(FWUPD_RELEASE(self), tmp64); + tmp64 = xb_node_get_attr_as_uint(rel, "timestamp"); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_created(FWUPD_RELEASE(self), 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(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + tags = xb_node_query(component, "tags/tag[@namespace=$'lvfs']", 0, NULL); + if (tags != NULL) { + for (guint i = 0; i < tags->len; i++) { + XbNode *tag = g_ptr_array_index(tags, i); + fwupd_release_add_tag(FWUPD_RELEASE(self), xb_node_get_text(tag)); + } + } + issues = xb_node_query(rel, "issues/issue", 0, NULL); + if (issues != NULL) { + for (guint i = 0; i < issues->len; i++) { + XbNode *n = g_ptr_array_index(issues, i); + fwupd_release_add_issue(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + tmp = xb_node_query_text(component, "screenshots/screenshot/caption", NULL); + if (tmp != NULL) + fwupd_release_set_detach_caption(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "screenshots/screenshot/image", NULL); + if (tmp != NULL) { + if (self->remote != NULL) { + g_autofree gchar *img = NULL; + img = fwupd_remote_build_firmware_uri(self->remote, tmp, error); + if (img == NULL) + return FALSE; + fwupd_release_set_detach_image(FWUPD_RELEASE(self), img); + } else { + fwupd_release_set_detach_image(FWUPD_RELEASE(self), tmp); + } + } + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); + if (tmp != NULL) + fwupd_release_set_protocol(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateMessage']", NULL); + if (tmp != NULL) + fwupd_release_set_update_message(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateImage']", NULL); + if (tmp != NULL) { + if (self->remote != NULL) { + g_autofree gchar *img = NULL; + img = fwupd_remote_build_firmware_uri(self->remote, tmp, error); + if (img == NULL) + return FALSE; + fwupd_release_set_update_image(FWUPD_RELEASE(self), img); + } else { + fwupd_release_set_update_image(FWUPD_RELEASE(self), tmp); + } + } + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateRequestId']", NULL); + if (tmp != NULL) + fu_release_set_update_request_id(self, tmp); + + /* hard and soft requirements */ + self->hard_reqs = xb_node_query(component, "requires/*", 0, &error_hard); + if (self->hard_reqs == NULL) { + if (!g_error_matches(error_hard, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches(error_hard, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error(error, g_steal_pointer(&error_hard)); + return FALSE; + } + } + self->soft_reqs = xb_node_query(component, "suggests/*|recommends/*", 0, &error_soft); + if (self->soft_reqs == NULL) { + if (!g_error_matches(error_soft, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches(error_soft, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error(error, g_steal_pointer(&error_soft)); + return FALSE; + } + } + + /* get per-release firmware blob */ + blob_fw_tmp = xb_node_get_data(rel, "fwupd::FirmwareBlob"); + if (blob_fw_tmp != NULL) + self->blob_fw = g_bytes_ref(blob_fw_tmp); + + /* to build the firmware */ + tmp = g_object_get_data(G_OBJECT(component), "fwupd::BuilderScript"); + if (tmp != NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "fwupd::BuilderScript is no longer supported"); + return FALSE; + } + + /* sort the locations by scheme */ + if (self->config != NULL) { + g_ptr_array_sort_with_data(fwupd_release_get_locations(FWUPD_RELEASE(self)), + fu_release_scheme_compare_cb, + self); + } + + /* check requirements for device */ + if (self->device != NULL && self->request != NULL && + fu_engine_request_get_kind(self->request) == FU_ENGINE_REQUEST_KIND_ACTIVE) { + if (!fu_release_check_requirements(self, component, rel, install_flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_release_get_trust_flags: + * @self: a #FuRelease + * + * Gets the trust flags for this task. + * + * NOTE: This is only set after fu_release_load() has been called successfully, and + * is only valid when a request has been set. + * + * Returns: the #FwupdReleaseFlags, e.g. #FWUPD_TRUST_FLAG_PAYLOAD + **/ +FwupdReleaseFlags +fu_release_get_trust_flags(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), FALSE); + return self->trust_flags; +} + +/** + * fu_release_get_action_id: + * @self: a #FuEngine + * + * Gets the PolicyKit action ID to use for the install operation. + * + * Returns: string, e.g. `org.freedesktop.fwupd.update-internal-trusted` + **/ +const gchar * +fu_release_get_action_id(FuRelease *self) +{ + /* relax authentication checks for removable devices */ + if (!fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_INTERNAL)) { + if (self->is_downgrade) { + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.downgrade-hotplug-trusted"; + return "org.freedesktop.fwupd.downgrade-hotplug"; + } + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.update-hotplug-trusted"; + return "org.freedesktop.fwupd.update-hotplug"; + } + + /* internal device */ + if (self->is_downgrade) { + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.downgrade-internal-trusted"; + return "org.freedesktop.fwupd.downgrade-internal"; + } + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.update-internal-trusted"; + return "org.freedesktop.fwupd.update-internal"; +} + +/** + * fu_release_compare: + * @release1: first task to compare. + * @release2: second task to compare. + * + * Compares two install tasks. + * + * Returns: 1, 0 or -1 if @release1 is greater, equal, or less than @release2, respectively. + **/ +gint +fu_release_compare(FuRelease *release1, FuRelease *release2) +{ + FuDevice *device1 = fu_release_get_device(release1); + FuDevice *device2 = fu_release_get_device(release2); + if (fu_device_get_order(device1) < fu_device_get_order(device2)) + return -1; + if (fu_device_get_order(device1) > fu_device_get_order(device2)) + return 1; + return 0; +} + +static void +fu_release_init(FuRelease *self) +{ + self->trust_flags = FWUPD_TRUST_FLAG_NONE; +} + +static void +fu_release_finalize(GObject *obj) +{ + FuRelease *self = FU_RELEASE(obj); + + g_free(self->update_request_id); + if (self->request != NULL) + g_object_unref(self->request); + if (self->device != NULL) + g_object_unref(self->device); + if (self->remote != NULL) + g_object_unref(self->remote); + if (self->config != NULL) + g_object_unref(self->config); + if (self->blob_fw != NULL) + g_bytes_unref(self->blob_fw); + if (self->soft_reqs != NULL) + g_ptr_array_unref(self->soft_reqs); + if (self->hard_reqs != NULL) + g_ptr_array_unref(self->hard_reqs); + + G_OBJECT_CLASS(fu_release_parent_class)->finalize(obj); +} + +static void +fu_release_class_init(FuReleaseClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_release_finalize; +} + +FuRelease * +fu_release_new(void) +{ + FuRelease *self; + self = g_object_new(FU_TYPE_RELEASE, NULL); + return FU_RELEASE(self); +} diff --git a/fwupd-1.8.6/src/fu-release.h b/fwupd-1.8.6/src/fu-release.h new file mode 100644 index 0000000000000000000000000000000000000000..9233d9fbe03fc5de972fde9e52c3b4113633bdaf --- /dev/null +++ b/fwupd-1.8.6/src/fu-release.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-config.h" +#include "fu-engine-request.h" + +#define FU_TYPE_RELEASE (fu_release_get_type()) +G_DECLARE_FINAL_TYPE(FuRelease, fu_release, FU, RELEASE, FwupdRelease) + +FuRelease * +fu_release_new(void); + +#define fu_release_get_version(r) fwupd_release_get_version(FWUPD_RELEASE(r)) +#define fu_release_get_branch(r) fwupd_release_get_branch(FWUPD_RELEASE(r)) +#define fu_release_get_checksums(r) fwupd_release_get_checksums(FWUPD_RELEASE(r)) +#define fu_release_add_flag(r, v) fwupd_release_add_flag(FWUPD_RELEASE(r), v) +#define fu_release_add_tag(r, v) fwupd_release_add_tag(FWUPD_RELEASE(r), v) +#define fu_release_add_metadata(r, v) fwupd_release_add_metadata(FWUPD_RELEASE(r), v) + +FuDevice * +fu_release_get_device(FuRelease *self); +GBytes * +fu_release_get_fw_blob(FuRelease *self); +FuEngineRequest * +fu_release_get_request(FuRelease *self); +GPtrArray * +fu_release_get_soft_reqs(FuRelease *self); +GPtrArray * +fu_release_get_hard_reqs(FuRelease *self); +const gchar * +fu_release_get_update_request_id(FuRelease *self); + +void +fu_release_set_request(FuRelease *self, FuEngineRequest *request); +void +fu_release_set_device(FuRelease *self, FuDevice *device); +void +fu_release_set_remote(FuRelease *self, FwupdRemote *remote); +void +fu_release_set_config(FuRelease *self, FuConfig *config); + +gboolean +fu_release_load(FuRelease *self, + XbNode *component, + XbNode *rel, + FwupdInstallFlags flags, + GError **error); +FwupdReleaseFlags +fu_release_get_trust_flags(FuRelease *self); +const gchar * +fu_release_get_action_id(FuRelease *self); +gint +fu_release_compare(FuRelease *release1, FuRelease *release2); diff --git a/fwupd-1.8.6/src/fu-remote-list.c b/fwupd-1.8.6/src/fu-remote-list.c new file mode 100644 index 0000000000000000000000000000000000000000..e5fbc2cc6ecfeb3d4b6b547a8a658df14d06e6e5 --- /dev/null +++ b/fwupd-1.8.6/src/fu-remote-list.c @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuRemoteList" + +#include "config.h" + +#include +#include + +#ifdef HAVE_INOTIFY_H +#include +#include +#endif + +#include "fwupd-remote-private.h" + +#include "fu-remote-list.h" + +enum { SIGNAL_CHANGED, SIGNAL_LAST }; + +static guint signals[SIGNAL_LAST] = {0}; + +static void +fu_remote_list_finalize(GObject *obj); + +struct _FuRemoteList { + GObject parent_instance; + GPtrArray *array; /* (element-type FwupdRemote) */ + GPtrArray *monitors; /* (element-type GFileMonitor) */ + GHashTable *hash_unfound; /* utf8 : NULL */ + XbSilo *silo; +}; + +G_DEFINE_TYPE(FuRemoteList, fu_remote_list, G_TYPE_OBJECT) + +static void +fu_remote_list_emit_changed(FuRemoteList *self) +{ + g_debug("::remote_list changed"); + g_signal_emit(self, signals[SIGNAL_CHANGED], 0); +} + +static void +fu_remote_list_monitor_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuRemoteList *self = FU_REMOTE_LIST(user_data); + g_autoptr(GError) error = NULL; + g_autofree gchar *filename = g_file_get_path(file); + g_debug("%s changed, reloading all remotes", filename); + if (!fu_remote_list_reload(self, &error)) + g_warning("failed to rescan remotes: %s", error->message); + fu_remote_list_emit_changed(self); +} + +static guint64 +_fwupd_remote_get_mtime(FwupdRemote *remote) +{ + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + + file = g_file_new_for_path(fwupd_remote_get_filename_cache(remote)); + if (!g_file_query_exists(file, NULL)) + return G_MAXUINT64; + info = g_file_query_info(file, + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + if (info == NULL) + return G_MAXUINT64; + return g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED); +} + +/* GLib only returns the very unhelpful "Unable to find default local file monitor type" + * when /proc/sys/fs/inotify/max_user_instances is set too low; detect this and set a proper + * error prefix to aid debugging when the daemon fails to start */ +static void +fu_remote_list_fixup_inotify_error(GError **error) +{ +#ifdef HAVE_INOTIFY_H + int fd; + int wd; + const gchar *fn = "/proc/sys/fs/inotify/max_user_instances"; + + fd = inotify_init(); + if (fd == -1) { + g_prefix_error(error, "Could not initialize inotify, check %s: ", fn); + return; + } + wd = inotify_add_watch(fd, "/", 0); + if (wd < 0) { + if (errno == ENOSPC) + g_prefix_error(error, "No space for inotify, check %s: ", fn); + } else { + inotify_rm_watch(fd, wd); + } + close(fd); +#endif +} + +static gboolean +fu_remote_list_add_inotify(FuRemoteList *self, const gchar *filename, GError **error) +{ + GFileMonitor *monitor; + g_autoptr(GFile) file = g_file_new_for_path(filename); + + /* set up a notify watch */ + monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); + if (monitor == NULL) { + fu_remote_list_fixup_inotify_error(error); + return FALSE; + } + g_signal_connect(G_FILE_MONITOR(monitor), + "changed", + G_CALLBACK(fu_remote_list_monitor_changed_cb), + self); + g_ptr_array_add(self->monitors, monitor); + return TRUE; +} + +static GString * +_fwupd_remote_get_agreement_default(FwupdRemote *self, GError **error) +{ + GString *str = g_string_new(NULL); + + /* this is designed as a fallback; the actual warning should ideally + * come from the LVFS instance that is serving the remote */ + g_string_append_printf(str, + "

    %s

    ", + /* TRANSLATORS: show the user a warning */ + _("Your distributor may not have verified any of " + "the firmware updates for compatibility with your " + "system or connected devices.")); + g_string_append_printf(str, + "

    %s

    ", + /* TRANSLATORS: show the user a warning */ + _("Enabling this remote is done at your own risk.")); + return str; +} + +static GString * +_fwupd_remote_get_agreement_for_app(FwupdRemote *self, XbNode *component, GError **error) +{ + g_autofree gchar *tmp = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(XbNode) n = NULL; + + /* manually find the first agreement section */ + n = xb_node_query_first(component, + "agreement/agreement_section/description/*", + &error_local); + if (n == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No agreement description found: %s", + error_local->message); + return NULL; + } + tmp = xb_node_export(n, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS, error); + if (tmp == NULL) + return NULL; + return g_string_new(tmp); +} + +static gchar * +_fwupd_remote_build_component_id(FwupdRemote *remote) +{ + return g_strdup_printf("org.freedesktop.fwupd.remotes.%s", fwupd_remote_get_id(remote)); +} + +static gboolean +fu_remote_list_add_for_path(FuRemoteList *self, const gchar *path, GError **error) +{ + const gchar *tmp; + g_autofree gchar *path_remotes = NULL; + g_autoptr(GDir) dir = NULL; + g_autoptr(GHashTable) os_release = NULL; + + path_remotes = g_build_filename(path, "remotes.d", NULL); + if (!g_file_test(path_remotes, G_FILE_TEST_EXISTS)) { + g_debug("path %s does not exist", path_remotes); + return TRUE; + } + if (!fu_remote_list_add_inotify(self, path_remotes, error)) + return FALSE; + dir = g_dir_open(path_remotes, 0, error); + if (dir == NULL) + return FALSE; + os_release = fwupd_get_os_release(error); + if (os_release == NULL) + return FALSE; + while ((tmp = g_dir_read_name(dir)) != NULL) { + g_autofree gchar *filename = g_build_filename(path_remotes, tmp, NULL); + g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autofree gchar *remotesdir = NULL; + + /* skip invalid files */ + if (!g_str_has_suffix(tmp, ".conf")) { + g_debug("skipping invalid file %s", filename); + continue; + } + + /* set directory to store data */ + remotesdir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_METADATA); + fwupd_remote_set_remotes_dir(remote, remotesdir); + + /* load from keyfile */ + g_debug("loading remote from %s", filename); + if (!fwupd_remote_load_from_filename(remote, filename, NULL, error)) { + g_prefix_error(error, "failed to load %s: ", filename); + return FALSE; + } + if (!fwupd_remote_setup(remote, error)) { + g_prefix_error(error, "failed to setup %s: ", filename); + return FALSE; + } + + /* watch the remote_list file and the XML file itself */ + if (!fu_remote_list_add_inotify(self, filename, error)) + return FALSE; + if (!fu_remote_list_add_inotify(self, + fwupd_remote_get_filename_cache(remote), + error)) + return FALSE; + + /* try to find a custom agreement, falling back to a generic warning */ + if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DOWNLOAD) { + g_autoptr(GString) agreement_markup = NULL; + g_autofree gchar *component_id = _fwupd_remote_build_component_id(remote); + 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 = + _fwupd_remote_get_agreement_for_app(remote, component, error); + } else { + agreement_markup = + _fwupd_remote_get_agreement_default(remote, error); + } + if (agreement_markup == NULL) + return FALSE; + + /* replace any dynamic values from os-release */ + tmp = g_hash_table_lookup(os_release, "NAME"); + if (tmp == NULL) + tmp = "this distribution"; + fu_string_replace(agreement_markup, "$OS_RELEASE:NAME$", tmp); + tmp = g_hash_table_lookup(os_release, "BUG_REPORT_URL"); + if (tmp == NULL) + tmp = "https://github.com/fwupd/fwupd/issues"; + fu_string_replace(agreement_markup, "$OS_RELEASE:BUG_REPORT_URL$", tmp); + fwupd_remote_set_agreement(remote, agreement_markup->str); + } + + /* set mtime */ + fwupd_remote_set_mtime(remote, _fwupd_remote_get_mtime(remote)); + g_ptr_array_add(self->array, g_steal_pointer(&remote)); + } + return TRUE; +} + +gboolean +fu_remote_list_set_key_value(FuRemoteList *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GError **error) +{ + FwupdRemote *remote; + const gchar *filename; + g_autofree gchar *value_old = NULL; + g_autoptr(GKeyFile) keyfile = g_key_file_new(); + + /* check remote is valid */ + remote = fu_remote_list_get_by_id(self, remote_id); + if (remote == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "remote %s not found", + remote_id); + return FALSE; + } + + /* modify the remote */ + filename = fwupd_remote_get_filename_source(remote); + if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_KEEP_COMMENTS, error)) { + g_prefix_error(error, "failed to load %s: ", filename); + return FALSE; + } + value_old = g_key_file_get_string(keyfile, "fwupd Remote", key, NULL); + if (g_strcmp0(value_old, value) == 0) + return TRUE; + g_key_file_set_string(keyfile, "fwupd Remote", key, value); + if (!g_key_file_save_to_file(keyfile, filename, error)) + return FALSE; + + /* reload values */ + if (!fwupd_remote_load_from_filename(remote, filename, NULL, error)) { + g_prefix_error(error, "failed to load %s: ", filename); + return FALSE; + } + fu_remote_list_emit_changed(self); + return TRUE; +} + +static gint +fu_remote_list_sort_cb(gconstpointer a, gconstpointer b) +{ + FwupdRemote *remote_a = *((FwupdRemote **)a); + FwupdRemote *remote_b = *((FwupdRemote **)b); + + /* use priority first */ + if (fwupd_remote_get_priority(remote_a) < fwupd_remote_get_priority(remote_b)) + return 1; + if (fwupd_remote_get_priority(remote_a) > fwupd_remote_get_priority(remote_b)) + return -1; + + /* fall back to name */ + return g_strcmp0(fwupd_remote_get_id(remote_a), fwupd_remote_get_id(remote_b)); +} + +static guint +fu_remote_list_depsolve_with_direction(FuRemoteList *self, gint inc) +{ + guint cnt = 0; + + for (guint i = 0; i < self->array->len; i++) { + FwupdRemote *remote = g_ptr_array_index(self->array, i); + gchar **order = inc < 0 ? fwupd_remote_get_order_after(remote) + : fwupd_remote_get_order_before(remote); + if (order == NULL) + continue; + for (guint j = 0; order[j] != NULL; j++) { + FwupdRemote *remote2; + if (g_strcmp0(order[j], fwupd_remote_get_id(remote)) == 0) { + g_debug("ignoring self-dep remote %s", order[j]); + continue; + } + remote2 = fu_remote_list_get_by_id(self, order[j]); + if (remote2 == NULL) { + if (g_hash_table_contains(self->hash_unfound, order[j])) + continue; + g_debug("ignoring unfound remote %s", order[j]); + g_hash_table_insert(self->hash_unfound, g_strdup(order[j]), NULL); + continue; + } + if (fwupd_remote_get_priority(remote) > fwupd_remote_get_priority(remote2)) + continue; + g_debug("ordering %s=%s+%i", + fwupd_remote_get_id(remote), + fwupd_remote_get_id(remote2), + inc); + fwupd_remote_set_priority(remote, fwupd_remote_get_priority(remote2) + inc); + + /* increment changes counter */ + cnt++; + } + } + return cnt; +} + +gboolean +fu_remote_list_reload(FuRemoteList *self, GError **error) +{ + guint depsolve_check; + g_autofree gchar *remotesdir = NULL; + g_autofree gchar *remotesdir_mut = NULL; + + /* clear */ + g_ptr_array_set_size(self->array, 0); + g_ptr_array_set_size(self->monitors, 0); + + /* use sysremotes, and then fall back to /etc */ + remotesdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + if (!fu_remote_list_add_for_path(self, remotesdir, error)) + return FALSE; + remotesdir_mut = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + if (!fu_remote_list_add_for_path(self, remotesdir_mut, error)) + return FALSE; + + /* depsolve */ + for (depsolve_check = 0; depsolve_check < 100; depsolve_check++) { + guint cnt = 0; + cnt += fu_remote_list_depsolve_with_direction(self, 1); + cnt += fu_remote_list_depsolve_with_direction(self, -1); + if (cnt == 0) + break; + } + if (depsolve_check == 100) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Cannot depsolve remotes ordering"); + return FALSE; + } + + /* order these by priority, then name */ + g_ptr_array_sort(self->array, fu_remote_list_sort_cb); + + /* success */ + return TRUE; +} + +static gboolean +fu_remote_list_load_metainfos(XbBuilder *builder, GError **error) +{ + const gchar *fn; + g_autofree gchar *datadir = NULL; + g_autofree gchar *metainfo_path = NULL; + g_autoptr(GDir) dir = NULL; + + /* pkg metainfo dir */ + datadir = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); + metainfo_path = g_build_filename(datadir, "metainfo", NULL); + 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_remote_list_load(FuRemoteList *self, FuRemoteListLoadFlags flags, GError **error) +{ + const gchar *const *locales = g_get_language_names(); + 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_REMOTE_LIST(self), FALSE); + g_return_val_if_fail(self->silo == NULL, FALSE); + + /* load AppStream about the remote_list */ + if (!fu_remote_list_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]); + + /* on a read-only filesystem don't care about the cache GUID */ + if (flags & FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS) + compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; + + /* build the metainfo silo */ + if (flags & FU_REMOTE_LIST_LOAD_FLAG_NO_CACHE) { + g_autoptr(GFileIOStream) iostr = NULL; + xmlb = g_file_new_tmp(NULL, &iostr, error); + if (xmlb == NULL) + return FALSE; + } else { + g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); + g_autofree gchar *xmlbfn = g_build_filename(cachedirpkg, "metainfo.xmlb", NULL); + 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 remote_list */ + return fu_remote_list_reload(self, error); +} + +GPtrArray * +fu_remote_list_get_all(FuRemoteList *self) +{ + g_return_val_if_fail(FU_IS_REMOTE_LIST(self), NULL); + return self->array; +} + +FwupdRemote * +fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id) +{ + g_return_val_if_fail(FU_IS_REMOTE_LIST(self), NULL); + for (guint i = 0; i < self->array->len; i++) { + FwupdRemote *remote = g_ptr_array_index(self->array, i); + if (g_strcmp0(remote_id, fwupd_remote_get_id(remote)) == 0) + return remote; + } + return NULL; +} + +static void +fu_remote_list_class_init(FuRemoteListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_remote_list_finalize; + + /** + * FuRemoteList::changed: + * @self: the #FuRemoteList instance that emitted the signal + * + * The ::changed signal is emitted when the list of remotes has + * changed, for instance when a remote has been added or removed. + **/ + signals[SIGNAL_CHANGED] = g_signal_new("changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static void +fu_remote_list_monitor_unref(GFileMonitor *monitor) +{ + g_file_monitor_cancel(monitor); + g_object_unref(monitor); +} + +static void +fu_remote_list_init(FuRemoteList *self) +{ + self->array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + self->monitors = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_remote_list_monitor_unref); + self->hash_unfound = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); +} + +static void +fu_remote_list_finalize(GObject *obj) +{ + FuRemoteList *self = FU_REMOTE_LIST(obj); + if (self->silo != NULL) + g_object_unref(self->silo); + g_ptr_array_unref(self->array); + g_ptr_array_unref(self->monitors); + g_hash_table_unref(self->hash_unfound); + G_OBJECT_CLASS(fu_remote_list_parent_class)->finalize(obj); +} + +FuRemoteList * +fu_remote_list_new(void) +{ + FuRemoteList *self; + self = g_object_new(FU_TYPE_REMOTE_LIST, NULL); + return FU_REMOTE_LIST(self); +} diff --git a/fwupd-1.8.6/src/fu-remote-list.h b/fwupd-1.8.6/src/fu-remote-list.h new file mode 100644 index 0000000000000000000000000000000000000000..9fb98018dcf4046df201a587a1ddb8216576c9b6 --- /dev/null +++ b/fwupd-1.8.6/src/fu-remote-list.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_REMOTE_LIST (fu_remote_list_get_type()) +G_DECLARE_FINAL_TYPE(FuRemoteList, fu_remote_list, FU, REMOTE_LIST, GObject) + +/** + * FuRemoteListLoadFlags: + * @FU_REMOTE_LIST_LOAD_FLAG_NONE: No flags set + * @FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors + * @FU_REMOTE_LIST_LOAD_FLAG_NO_CACHE: Do not save persistent xmlb silos + * + * The flags to use when loading a remote_listuration file. + **/ +typedef enum { + FU_REMOTE_LIST_LOAD_FLAG_NONE = 0, + FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS = 1 << 0, + FU_REMOTE_LIST_LOAD_FLAG_NO_CACHE = 1 << 1, + /*< private >*/ + FU_REMOTE_LIST_LOAD_FLAG_LAST +} FuRemoteListLoadFlags; + +FuRemoteList * +fu_remote_list_new(void); +gboolean +fu_remote_list_load(FuRemoteList *self, FuRemoteListLoadFlags flags, GError **error); +gboolean +fu_remote_list_reload(FuRemoteList *self, GError **error); +gboolean +fu_remote_list_set_key_value(FuRemoteList *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GError **error); +GPtrArray * +fu_remote_list_get_all(FuRemoteList *self); +FwupdRemote * +fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id); diff --git a/fwupd-1.8.6/src/fu-security-attr-common.c b/fwupd-1.8.6/src/fu-security-attr-common.c new file mode 100644 index 0000000000000000000000000000000000000000..361a5e3216c144974ec67296ceba581ee03844aa --- /dev/null +++ b/fwupd-1.8.6/src/fu-security-attr-common.c @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fwupd-security-attr-private.h" + +#include "fu-security-attr-common.h" +#include "fu-security-attrs-private.h" + +gchar * +fu_security_attr_get_name(FwupdSecurityAttr *attr) +{ + const gchar *appstream_id = fwupd_security_attr_get_appstream_id(attr); + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup(_("SPI write")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BLE) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup(_("SPI lock")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup(_("SPI BIOS region")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return g_strdup(_("SPI BIOS Descriptor")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION) == 0) { + /* TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack */ + return g_strdup(_("Pre-boot DMA protection")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel */ + return g_strdup(_("Intel BootGuard")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * verified boot refers to the way the boot process is verified */ + return g_strdup(_("Intel BootGuard verified boot")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * ACM means to verify the integrity of Initial Boot Block */ + return g_strdup(_("Intel BootGuard ACM protected")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * error policy is what to do on failure */ + return g_strdup(_("Intel BootGuard error policy")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * OTP = one time programmable */ + return g_strdup(_("Intel BootGuard OTP fuse")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED) == 0) { + /* TRANSLATORS: Title: CET = Control-flow Enforcement Technology, + * enabled means supported by the processor */ + return g_strdup(_("Intel CET Enabled")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE) == 0) { + /* TRANSLATORS: Title: CET = Control-flow Enforcement Technology, + * active means being used by the OS */ + return g_strdup(_("Intel CET Active")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_SMAP) == 0) { + /* TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention */ + return g_strdup(_("Intel SMAP")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM) == 0) { + /* TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME */ + return g_strdup(_("Encrypted RAM")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_IOMMU) == 0) { + /* TRANSLATORS: Title: + * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit */ + return g_strdup(_("IOMMU")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN) == 0) { + /* TRANSLATORS: Title: lockdown is a security mode of the kernel */ + return g_strdup(_("Linux kernel lockdown")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED) == 0) { + /* TRANSLATORS: Title: if it's tainted or not */ + return g_strdup(_("Linux kernel")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP) == 0) { + /* TRANSLATORS: Title: swap space or swap partition */ + return g_strdup(_("Linux swap")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM) == 0) { + /* TRANSLATORS: Title: sleep state */ + return g_strdup(_("Suspend-to-ram")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE) == 0) { + /* TRANSLATORS: Title: a better sleep state */ + return g_strdup(_("Suspend-to-idle")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_PK) == 0) { + /* TRANSLATORS: Title: PK is the 'platform key' for the machine */ + return g_strdup(_("UEFI platform key")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT) == 0) { + /* TRANSLATORS: Title: SB is a way of locking down UEFI */ + return g_strdup(_("UEFI secure boot")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR) == 0) { + /* TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be empty */ + return g_strdup(_("TPM empty PCRs")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0) { + /* TRANSLATORS: Title: the PCR is rebuilt from the TPM event log */ + return g_strdup(_("TPM PCR0 reconstruction")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20) == 0) { + /* TRANSLATORS: Title: TPM = Trusted Platform Module */ + return g_strdup(_("TPM v2.0")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE) == 0) { + const gchar *kind = fwupd_security_attr_get_metadata(attr, "kind"); + if (kind != NULL) { + /* TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT */ + return g_strdup_printf(_("%s manufacturing mode"), kind); + } + /* TRANSLATORS: Title: MEI = Intel Management Engine */ + return g_strdup(_("MEI manufacturing mode")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP) == 0) { + const gchar *kind = fwupd_security_attr_get_metadata(attr, "kind"); + if (kind != NULL) { + /* TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT */ + return g_strdup_printf(_("%s override"), kind); + } + /* TRANSLATORS: Title: MEI = Intel Management Engine, and the + * "override" is the physical PIN that can be driven to + * logic high -- luckily it is probably not accessible to + * end users on consumer boards */ + return g_strdup(_("MEI override")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) { + /* TRANSLATORS: Title: MEI = Intel Management Engine */ + const gchar *kind = fwupd_security_attr_get_metadata(attr, "kind"); + const gchar *version = fwupd_security_attr_get_metadata(attr, "version"); + if (kind != NULL && version != NULL) { + /* TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number + */ + return g_strdup_printf(_("%s v%s"), kind, version); + } + if (kind != NULL) { + /* TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT */ + return g_strdup_printf(_("%s version"), kind); + } + return g_strdup(_("MEI version")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES) == 0) { + /* TRANSLATORS: Title: if firmware updates are available */ + return g_strdup(_("Firmware updates")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION) == 0) { + /* TRANSLATORS: Title: if we can verify the firmware checksums */ + return g_strdup(_("Firmware attestation")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS) == 0) { + /* TRANSLATORS: Title: if the fwupd plugins are all present and correct */ + return g_strdup(_("fwupd plugins")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED) == 0) { + /* TRANSLATORS: Title: Allows debugging of parts using proprietary hardware */ + return g_strdup(_("Platform Debugging")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU) == 0) { + /* TRANSLATORS: Title: if fwupd supports HSI on this chip */ + return g_strdup(_("Supported CPU")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION) == 0) { + /* TRANSLATORS: Title: if firmware enforces rollback protection */ + return g_strdup(_("Rollback protection")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION) == 0) { + /* TRANSLATORS: Title: if hardware enforces control of SPI replays */ + return g_strdup(_("SPI replay protection")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION) == 0) { + /* TRANSLATORS: Title: if hardware enforces control of SPI writes */ + return g_strdup(_("SPI write protection")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED) == 0) { + /* TRANSLATORS: Title: if the part has been fused */ + return g_strdup(_("Fused platform")); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_HOST_EMULATION) == 0) { + /* TRANSLATORS: Title: if we are emulating a different host */ + return g_strdup(_("Emulated host")); + } + + /* we should not get here */ + return g_strdup(fwupd_security_attr_get_name(attr)); +} + +const gchar * +fu_security_attr_get_title(FwupdSecurityAttr *attr) +{ + const gchar *appstream_id = fwupd_security_attr_get_appstream_id(attr); + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE) == 0) { + /* TRANSLATORS: Title: firmware refers to the flash chip in the computer */ + return _("Firmware Write Protection"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BLE) == 0) { + /* TRANSLATORS: Title: firmware refers to the flash chip in the computer */ + return _("Firmware Write Protection Lock"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP) == 0) { + /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */ + return _("Firmware BIOS Region"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR) == 0) { + /* TRANSLATORS: Title: firmware refers to the flash chip in the computer */ + return _("Firmware BIOS Descriptor"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION) == 0) { + /* TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack */ + return _("Pre-boot DMA Protection"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel */ + return _("Intel BootGuard"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * verified boot refers to the way the boot process is verified */ + return _("Intel BootGuard Verified Boot"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * ACM means to verify the integrity of Initial Boot Block */ + return _("Intel BootGuard ACM Protected"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel, + * error policy is what to do on failure */ + return _("Intel BootGuard Error Policy"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP) == 0) { + /* TRANSLATORS: Title: BootGuard is a trademark from Intel */ + return _("Intel BootGuard Fuse"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE) == 0) { + /* TRANSLATORS: Title: CET = Control-flow Enforcement Technology */ + return _("Intel CET"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_SMAP) == 0) { + /* TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention */ + return _("Intel SMAP"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM) == 0) { + /* TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME */ + return _("Encrypted RAM"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_IOMMU) == 0) { + /* TRANSLATORS: Title: + * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit */ + return _("IOMMU Protection"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN) == 0) { + /* TRANSLATORS: Title: lockdown is a security mode of the kernel */ + return _("Linux Kernel Lockdown"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED) == 0) { + /* TRANSLATORS: Title: if it's tainted or not */ + return _("Linux Kernel Verification"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP) == 0) { + /* TRANSLATORS: Title: swap space or swap partition */ + return _("Linux Swap"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM) == 0) { + /* TRANSLATORS: Title: sleep state */ + return _("Suspend To RAM"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE) == 0) { + /* TRANSLATORS: Title: a better sleep state */ + return _("Suspend To Idle"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_PK) == 0) { + /* TRANSLATORS: Title: PK is the 'platform key' for the machine */ + return _("UEFI Platform Key"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT) == 0) { + /* TRANSLATORS: Title: SB is a way of locking down UEFI */ + return _("UEFI Secure Boot"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR) == 0) { + /* TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be empty */ + return _("TPM Platform Configuration"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0) { + /* TRANSLATORS: Title: the PCR is rebuilt from the TPM event log */ + return _("TPM Reconstruction"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20) == 0) { + /* TRANSLATORS: Title: TPM = Trusted Platform Module */ + return _("TPM v2.0"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE) == 0) { + /* TRANSLATORS: Title: MEI = Intel Management Engine */ + return _("Intel Management Engine Manufacturing Mode"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP) == 0) { + /* TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is enabled + * with a jumper -- luckily it is probably not accessible to end users on consumer + * boards */ + return _("Intel Management Engine Override"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) { + /* TRANSLATORS: Title: MEI = Intel Management Engine */ + return _("Intel Management Engine Version"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES) == 0) { + /* TRANSLATORS: Title: if firmware updates are available */ + return _("Firmware Updates"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION) == 0) { + /* TRANSLATORS: Title: if we can verify the firmware checksums */ + return _("Firmware Attestation"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS) == 0) { + /* TRANSLATORS: Title: if the fwupd plugins are all present and correct */ + return _("Firmware Updater Verification"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED) == 0) { + /* TRANSLATORS: Title: Allows debugging of parts using proprietary hardware */ + return _("Platform Debugging"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU) == 0) { + /* TRANSLATORS: Title: if fwupd supports HSI on this chip */ + return _("Processor Security Checks"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION) == 0) { + /* TRANSLATORS: Title: if firmware enforces rollback protection */ + return _("AMD Rollback Protection"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION) == 0) { + /* TRANSLATORS: Title: if hardware enforces control of SPI replays */ + return _("AMD Firmware Replay Protection"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION) == 0) { + /* TRANSLATORS: Title: if hardware enforces control of SPI writes */ + return _("AMD Firmware Write Protection"); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED) == 0) { + /* TRANSLATORS: Title: if the part has been fused */ + return _("Fused Platform"); + } + return NULL; +} + +/* one line describing the attribute */ +const gchar * +fu_security_attr_get_description(FwupdSecurityAttr *attr) +{ + const gchar *appstream_id = fwupd_security_attr_get_appstream_id(attr); + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_BLE) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION) == 0) { + /* TRANSLATORS: longer description */ + return _("Firmware Write Protection protects device firmware memory from being " + "tampered with."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP) == 0) { + /* TRANSLATORS: longer description */ + return _("Firmware BIOS Region protects device firmware memory from being " + "tampered with."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR) == 0) { + /* TRANSLATORS: longer description */ + return _("Firmware BIOS Descriptor protects device firmware memory from being " + "tampered with."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION) == 0) { + /* TRANSLATORS: longer description */ + return _("Pre-boot DMA protection prevents devices from accessing system memory " + "after being connected to the computer."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED) == 0) { + /* TRANSLATORS: longer description */ + return _("Intel BootGuard prevents unauthorized device software from operating " + "when the device is started."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY) == 0) { + /* TRANSLATORS: longer description */ + return _( + "Intel BootGuard Error Policy ensures the device does not continue to start if " + "its device software has been tampered with."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ENABLED) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_CET_ACTIVE) == 0) { + /* TRANSLATORS: longer description */ + return _("Intel Control-Flow Enforcement Technology detects and prevents certain " + "methods for running malicious software on the device."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_INTEL_SMAP) == 0) { + /* TRANSLATORS: longer description */ + return _("Intel Supervisor Mode Access Prevention ensures critical parts of " + "device memory are not accessed by less secure programs."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM) == 0) { + /* TRANSLATORS: longer description */ + return _( + "Encrypted RAM makes it impossible for information that is stored in device " + "memory to be read if the memory chip is removed and accessed."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_IOMMU) == 0) { + /* TRANSLATORS: longer description */ + return _("IOMMU Protection prevents connected devices from accessing unauthorized " + "parts of system memory."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN) == 0) { + /* TRANSLATORS: longer description */ + return _("Linux Kernel Lockdown mode prevents administrator (root) accounts from " + "accessing and changing critical parts of system software."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED) == 0) { + /* TRANSLATORS: longer description */ + return _( + "Linux Kernel Verification makes sure that critical system software has " + "not been tampered with. Using device drivers which are not provided with the " + "system can prevent this security feature from working correctly."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_KERNEL_SWAP) == 0) { + /* TRANSLATORS: longer description */ + return _( + "Linux Kernel Swap temporarily saves information to disk as you work. If the " + "information is not protected, it could be accessed by someone if they " + "obtained the disk."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM) == 0) { + /* TRANSLATORS: longer description */ + return _("Suspend to RAM allows the device to quickly go to sleep in order to save " + "power. While the device has been suspended, its memory could be " + "physically removed and its information accessed."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE) == 0) { + /* TRANSLATORS: longer description */ + return _("Suspend to Idle allows the device to quickly go to sleep in order to " + "save power. While the device has been suspended, its memory could be " + "physically removed and its information accessed."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_PK) == 0) { + /* TRANSLATORS: longer description */ + return _("The UEFI Platform Key is used to determine if device software comes from " + "a trusted source."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT) == 0) { + /* TRANSLATORS: longer description */ + return _("UEFI Secure Boot prevents malicious software from being loaded when the " + "device starts."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR) == 0) { + /* TRANSLATORS: longer description */ + return _("The TPM (Trusted Platform Module) Platform Configuration is used to " + "check whether the device start process has been modified."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0) { + /* TRANSLATORS: longer description */ + return _("The TPM (Trusted Platform Module) Reconstruction is used to check " + "whether the device start process has been modified."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20) == 0) { + /* TRANSLATORS: longer description */ + return _("TPM (Trusted Platform Module) is a computer chip that detects when " + "hardware components have been tampered with."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED) == 0) { + /* TRANSLATORS: longer description */ + return _("Manufacturing Mode is used when the device is manufactured and " + "security features are not yet enabled."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP) == 0) { + /* TRANSLATORS: longer description */ + return _("Intel Management Engine Override disables checks for device software " + "tampering."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) { + /* TRANSLATORS: longer description */ + return _("The Intel Management Engine controls device components and needs " + "to have a recent version to avoid security issues."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_UPDATES) == 0) { + /* TRANSLATORS: longer description */ + return _("Device software updates are provided for this device."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_ATTESTATION) == 0) { + /* TRANSLATORS: longer description */ + return _("Firmware Attestation checks device software using a reference copy, to " + "make sure that it has not been changed."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS) == 0) { + /* TRANSLATORS: longer description */ + return _( + "Firmware Updater Verification checks that software used for updating has not " + "been tampered with."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED) == 0) { + /* TRANSLATORS: longer description */ + return _("Platform Debugging allows device security features to be disabled. " + "This should only be used by hardware manufacturers."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU) == 0) { + /* TRANSLATORS: longer description */ + return _("Each system should have tests to ensure firmware security."); + } + if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION) == 0 || + g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION) == 0) { + /* TRANSLATORS: longer description */ + return _("AMD Rollback Protection prevents device software from being downgraded " + "to an older version that has security problems."); + } + return NULL; +} + +const gchar * +fu_security_attr_result_to_string(FwupdSecurityAttrResult result) +{ + if (result == FWUPD_SECURITY_ATTR_RESULT_VALID) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Valid"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Invalid"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_ENABLED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Enabled"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Disabled"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_LOCKED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Locked"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Unlocked"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Encrypted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Unencrypted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_TAINTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Tainted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Untainted"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_FOUND) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Found"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Not found"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_SUPPORTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Supported"); + } + if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("Not supported"); + } + return NULL; +} + +const gchar * +fu_security_attr_get_result(FwupdSecurityAttr *attr) +{ + const gchar *tmp; + + /* common case */ + tmp = fu_security_attr_result_to_string(fwupd_security_attr_get_result(attr)); + if (tmp != NULL) + return tmp; + + /* fallback */ + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + /* TRANSLATORS: Suffix: the HSI result */ + return _("OK"); + } + + /* TRANSLATORS: Suffix: the fallback HSI result */ + return _("Failed"); +} + +/** + * fu_security_attrs_to_json_string: + * Convert security attribute to JSON string. + * @attrs: a pointer for a FuSecurityAttrs data structure. + * @error: return location for an error + * + * fu_security_attrs_to_json_string() converts FuSecurityAttrs and return the + * string pointer. The converted JSON format is shown as follows: + * { + * "SecurityAttributes": [ + * { + * "name": "aaa", + * "value": "bbb" + * } + * ] + * } + * + * Returns: A string and NULL on fail. + * + * Since: 1.7.0 + * + */ +gchar * +fu_security_attrs_to_json_string(FuSecurityAttrs *attrs, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + g_autoptr(JsonNode) json_root = NULL; + fu_security_attrs_to_json(attrs, 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); + if (data == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert security attribute to json."); + return NULL; + } + return g_steal_pointer(&data); +} + +void +fu_security_attrs_to_json(FuSecurityAttrs *attrs, JsonBuilder *builder) +{ + g_autoptr(GPtrArray) items = NULL; + + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "SecurityAttributes"); + json_builder_begin_array(builder); + items = fu_security_attrs_get_all(attrs); + for (guint i = 0; i < items->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(items, i); + guint64 created = fwupd_security_attr_get_created(attr); + json_builder_begin_object(builder); + fwupd_security_attr_set_created(attr, 0); + fwupd_security_attr_to_json(attr, builder); + fwupd_security_attr_set_created(attr, created); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); +} + +gboolean +fu_security_attrs_from_json(FuSecurityAttrs *attrs, JsonNode *json_node, GError **error) +{ + JsonArray *array; + JsonObject *obj; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(json_node)) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object"); + return FALSE; + } + obj = json_node_get_object(json_node); + + /* this has to exist */ + if (!json_object_has_member(obj, "SecurityAttributes")) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no SecurityAttributes property in object"); + return FALSE; + } + array = json_object_get_array_member(obj, "SecurityAttributes"); + for (guint i = 0; i < json_array_get_length(array); i++) { + JsonNode *node_tmp = json_array_get_element(array, i); + g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_new(NULL); + if (!fwupd_security_attr_from_json(attr, node_tmp, error)) + return FALSE; + fu_security_attrs_append(attrs, attr); + } + + /* success */ + return TRUE; +} + +/** + * fu_security_attrs_compare: + * @attrs1: a #FuSecurityAttrs + * @attrs2: another #FuSecurityAttrs, perhaps newer in some way + * + * Compares the two objects, returning the differences. + * + * If the two sets of attrs are considered the same then an empty array is returned. + * Only the AppStream ID results are compared, extra metadata is ignored. + * + * Returns: (element-type FwupdSecurityAttr) (transfer container): differences + */ +GPtrArray * +fu_security_attrs_compare(FuSecurityAttrs *attrs1, FuSecurityAttrs *attrs2) +{ + g_autoptr(GHashTable) hash1 = g_hash_table_new(g_str_hash, g_str_equal); + g_autoptr(GHashTable) hash2 = g_hash_table_new(g_str_hash, g_str_equal); + g_autoptr(GPtrArray) array1 = fu_security_attrs_get_all(attrs1); + g_autoptr(GPtrArray) array2 = fu_security_attrs_get_all(attrs2); + g_autoptr(GPtrArray) results = + g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + + /* create hash tables of appstream-id -> FwupdSecurityAttr */ + for (guint i = 0; i < array1->len; i++) { + FwupdSecurityAttr *attr1 = g_ptr_array_index(array1, i); + g_hash_table_insert(hash1, + (gpointer)fwupd_security_attr_get_appstream_id(attr1), + (gpointer)attr1); + } + for (guint i = 0; i < array2->len; i++) { + FwupdSecurityAttr *attr2 = g_ptr_array_index(array2, i); + g_hash_table_insert(hash2, + (gpointer)fwupd_security_attr_get_appstream_id(attr2), + (gpointer)attr2); + } + + /* present in attrs2, not present in attrs1 */ + for (guint i = 0; i < array2->len; i++) { + FwupdSecurityAttr *attr1; + FwupdSecurityAttr *attr2 = g_ptr_array_index(array2, i); + attr1 = g_hash_table_lookup(hash1, fwupd_security_attr_get_appstream_id(attr2)); + if (attr1 == NULL) { + g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_copy(attr2); + g_ptr_array_add(results, g_steal_pointer(&attr)); + continue; + } + } + + /* present in attrs1, not present in attrs2 */ + for (guint i = 0; i < array1->len; i++) { + FwupdSecurityAttr *attr1 = g_ptr_array_index(array1, i); + FwupdSecurityAttr *attr2; + attr2 = g_hash_table_lookup(hash2, fwupd_security_attr_get_appstream_id(attr1)); + if (attr2 == NULL) { + g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_copy(attr1); + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_UNKNOWN); + fwupd_security_attr_set_result_fallback( + attr, /* flip these around */ + fwupd_security_attr_get_result(attr1)); + g_ptr_array_add(results, g_steal_pointer(&attr)); + continue; + } + } + + /* find any attributes that differ */ + for (guint i = 0; i < array2->len; i++) { + FwupdSecurityAttr *attr1; + FwupdSecurityAttr *attr2 = g_ptr_array_index(array2, i); + attr1 = g_hash_table_lookup(hash1, fwupd_security_attr_get_appstream_id(attr2)); + if (attr1 == NULL) + continue; + + /* result of specific attr differed */ + if (fwupd_security_attr_get_result(attr1) != + fwupd_security_attr_get_result(attr2)) { + g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_copy(attr1); + fwupd_security_attr_set_result(attr, fwupd_security_attr_get_result(attr2)); + fwupd_security_attr_set_result_fallback( + attr, + fwupd_security_attr_get_result(attr1)); + fwupd_security_attr_set_flags(attr, fwupd_security_attr_get_flags(attr2)); + g_ptr_array_add(results, g_steal_pointer(&attr)); + } + } + + /* success */ + return g_steal_pointer(&results); +} + +/** + * fu_security_attrs_equal: + * @attrs1: a #FuSecurityAttrs + * @attrs2: another #FuSecurityAttrs + * + * Tests the objects for equality. Only the AppStream ID results are compared, extra metadata + * is ignored. + * + * Returns: %TRUE if the set of attrs can be considered equal + */ +gboolean +fu_security_attrs_equal(FuSecurityAttrs *attrs1, FuSecurityAttrs *attrs2) +{ + g_autoptr(GPtrArray) compare = fu_security_attrs_compare(attrs1, attrs2); + return compare->len == 0; +} diff --git a/fwupd-1.8.6/src/fu-security-attr-common.h b/fwupd-1.8.6/src/fu-security-attr-common.h new file mode 100644 index 0000000000000000000000000000000000000000..d9dac0922f49739261d79700642ed188c49b8ade --- /dev/null +++ b/fwupd-1.8.6/src/fu-security-attr-common.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +#include "fu-security-attrs-private.h" + +gchar * +fu_security_attr_get_name(FwupdSecurityAttr *attr); +const gchar * +fu_security_attr_get_title(FwupdSecurityAttr *attr); +const gchar * +fu_security_attr_get_description(FwupdSecurityAttr *attr); +const gchar * +fu_security_attr_get_result(FwupdSecurityAttr *attr); +void +fu_security_attrs_to_json(FuSecurityAttrs *attrs, JsonBuilder *builder); +gchar * +fu_security_attrs_to_json_string(FuSecurityAttrs *attrs, GError **error); +gboolean +fu_security_attrs_from_json(FuSecurityAttrs *attrs, JsonNode *json_node, GError **error); +gboolean +fu_security_attrs_equal(FuSecurityAttrs *attrs1, FuSecurityAttrs *attrs2); +GPtrArray * +fu_security_attrs_compare(FuSecurityAttrs *attrs1, FuSecurityAttrs *attrs2); +const gchar * +fu_security_attr_result_to_string(FwupdSecurityAttrResult result); diff --git a/fwupd-1.8.6/src/fu-self-test.c b/fwupd-1.8.6/src/fu-self-test.c new file mode 100644 index 0000000000000000000000000000000000000000..e4c1b8a4bdfb2d9af5e8cd379e2b61493a19ebb7 --- /dev/null +++ b/fwupd-1.8.6/src/fu-self-test.c @@ -0,0 +1,4942 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-security-attr-private.h" + +#include "../plugins/test/fu-test-plugin.h" +#include "fu-backend-private.h" +#include "fu-bios-settings-private.h" +#include "fu-cabinet-common.h" +#include "fu-config.h" +#include "fu-context-private.h" +#include "fu-device-list.h" +#include "fu-device-private.h" +#include "fu-engine.h" +#include "fu-history.h" +#include "fu-plugin-list.h" +#include "fu-plugin-private.h" +#include "fu-progressbar.h" +#include "fu-release-common.h" +#include "fu-security-attr-common.h" +#include "fu-smbios-private.h" +#include "fu-spawn.h" +#include "fu-usb-backend.h" + +typedef struct { + FuPlugin *plugin; + FuContext *ctx; +} FuTest; + +static GMainLoop *_test_loop = NULL; +static guint _test_loop_timeout_id = 0; + +static gboolean +fu_test_hang_check_cb(gpointer user_data) +{ + g_main_loop_quit(_test_loop); + _test_loop_timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fu_test_loop_run_with_timeout(guint timeout_ms) +{ + g_assert_cmpint(_test_loop_timeout_id, ==, 0); + g_assert_null(_test_loop); + _test_loop = g_main_loop_new(NULL, FALSE); + _test_loop_timeout_id = g_timeout_add(timeout_ms, fu_test_hang_check_cb, NULL); + g_main_loop_run(_test_loop); +} + +static void +fu_test_loop_quit(void) +{ + if (_test_loop_timeout_id > 0) { + g_source_remove(_test_loop_timeout_id); + _test_loop_timeout_id = 0; + } + if (_test_loop != NULL) { + g_main_loop_quit(_test_loop); + g_main_loop_unref(_test_loop); + _test_loop = NULL; + } +} + +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_path_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 gboolean +fu_test_compare_lines(const gchar *txt1, const gchar *txt2, GError **error) +{ + g_autofree gchar *output = NULL; + if (g_strcmp0(txt1, txt2) == 0) + return TRUE; + if (fu_path_fnmatch(txt2, txt1)) + return TRUE; + if (!g_file_set_contents("/tmp/a", txt1, -1, error)) + return FALSE; + if (!g_file_set_contents("/tmp/b", txt2, -1, error)) + return FALSE; + if (!g_spawn_command_line_sync("diff -urNp /tmp/b /tmp/a", &output, NULL, NULL, error)) + return FALSE; + g_set_error_literal(error, 1, 0, output); + return FALSE; +} + +static void +fu_test_free(FuTest *self) +{ + if (self->ctx != NULL) + g_object_unref(self->ctx); + if (self->plugin != NULL) + g_object_unref(self->plugin); + g_free(self); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_free) +#pragma clang diagnostic pop + +static void +fu_engine_generate_md_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + const gchar *tmp; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) data = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + + /* put cab file somewhere we can parse it */ + filename = + g_test_build_filename(G_TEST_DIST, "tests", "colorhug", "colorhug-als-3.0.2.cab", NULL); + data = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(data); + ret = fu_bytes_set_contents("/tmp/fwupd-self-test/var/cache/fwupd/foo.cab", data, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load engine and check the device was found */ + ret = fu_engine_load(engine, + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_NO_CACHE, + progress, + &error); + g_assert_no_error(error); + g_assert_true(ret); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + component = fu_engine_get_component_by_guids(engine, device); + g_assert_nonnull(component); + + /* check remote ID set */ + tmp = xb_node_query_text(component, "../custom/value[@key='fwupd::RemoteId']", NULL); + g_assert_cmpstr(tmp, ==, "directory"); + + /* verify checksums */ + tmp = xb_node_query_text(component, "releases/release/checksum[@target='container']", NULL); + g_assert_cmpstr(tmp, ==, "3da49ddd961144a79336b3ac3b0e469cb2531d0e"); + tmp = xb_node_query_text(component, "releases/release/checksum[@target='content']", NULL); + g_assert_cmpstr(tmp, ==, NULL); +} + +static void +fu_engine_requirements_missing_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + 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 */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); +} + +static void +fu_engine_requirements_soft_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + 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 */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_FORCE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_client_fail_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " detach-action" + " " + " " + " " + " " + ""; + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); +} + +static void +fu_engine_requirements_client_invalid_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " hello-dave" + " " + " " + " " + " " + ""; + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); +} + +static void +fu_engine_requirements_client_pass_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " detach-action" + " " + " " + " " + " " + ""; + + /* set up a dummy version */ + fu_engine_request_set_feature_flags(request, FWUPD_FEATURE_FLAG_DETACH_ACTION); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_version_require_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_version_bootloader(device, "4.5.6"); + fu_device_add_vendor_id(device, "FFFF"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_true( + g_str_has_prefix(error->message, "device requires firmware with a version check")); + g_assert_false(ret); +} + +static void +fu_engine_requirements_version_lowest_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_version_lowest(device, "1.2.3"); + fu_device_add_vendor_id(device, "FFFF"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_true( + g_str_has_prefix(error->message, "Specified firmware is older than the minimum")); + g_assert_false(ret); +} + +static void +fu_engine_requirements_unsupported_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + 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 */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); +} + +static void +fu_engine_requirements_child_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuDevice) child = fu_device_new(NULL); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " not-child" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_version_bootloader(device, "4.5.6"); + fu_device_add_vendor_id(device, "FFFF"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(child, "0.0.999"); + fu_device_set_physical_id(child, "dummy"); + 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 */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_child_fail_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuDevice) child = fu_device_new(NULL); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " not-child" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_version_bootloader(device, "4.5.6"); + fu_device_add_vendor_id(device, "FFFF"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(child, "0.0.1"); + fu_device_set_physical_id(child, "dummy"); + 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 */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, 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_false(ret); +} + +static void +fu_engine_requirements_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + 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 */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_device_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " " + " bootloader" + " vendor-id" +#ifdef __linux__ + " org.kernel" +#endif + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_version_bootloader(device, "4.5.6"); + fu_device_add_vendor_id(device, "USB:0xFFFF"); + fu_device_add_vendor_id(device, "PCI:0x0000"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED); + 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 passes */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_device_plain_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(device, "5101AALB"); + fu_device_add_vendor_id(device, "FFFF"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + 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 passes */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_version_format_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + " " + " triplet" + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(device, "1.2.3.4"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + 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 */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, 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_false(ret); +} + +static void +fu_engine_requirements_only_upgrade_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version(device, "1.2.4"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); + fu_device_add_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 */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_nonnull(g_strstr_len(error->message, -1, "Device only supports version upgrades")); + g_assert_false(ret); +} + +static void +fu_engine_requirements_sibling_device_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuDevice) unrelated_device3 = fu_device_new(self->ctx); + g_autoptr(FuDevice) parent = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release1 = fu_release_new(); + g_autoptr(FuRelease) release2 = fu_release_new(); + 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_id(device1, "id1"); + fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device1, "1.2.3"); + fu_device_add_vendor_id(device1, "FFFF"); + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012"); + fu_device_add_protocol(device1, "com.acme"); + fu_engine_add_device(engine, device1); + + /* setup the parent */ + fu_device_set_id(parent, "parent"); + fu_device_set_version_format(parent, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(parent, "1.0.0"); + fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(parent, "42f3d696-0b6f-4d69-908f-357f98ef115e"); + fu_device_add_protocol(parent, "com.acme"); + fu_device_add_child(parent, device1); + fu_engine_add_device(engine, parent); + + /* set up a different device */ + fu_device_set_id(unrelated_device3, "id3"); + fu_device_add_vendor_id(unrelated_device3, "USB:FFFF"); + fu_device_add_protocol(unrelated_device3, "com.acme"); + fu_device_set_name(unrelated_device3, "Foo bar device"); + fu_device_set_version_format(unrelated_device3, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(unrelated_device3, "1.5.3"); + fu_device_add_vendor_id(unrelated_device3, "FFFF"); + fu_device_add_flag(unrelated_device3, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(unrelated_device3, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(unrelated_device3, "3e455c08-352e-4a16-84d3-f04287289fa2"); + fu_engine_add_device(engine, unrelated_device3); + + /* 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 fails */ + fu_release_set_device(release1, device1); + fu_release_set_request(release1, request); + ret = fu_release_load(release1, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release1, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + g_clear_error(&error); + + /* set up a sibling device */ + fu_device_set_id(device2, "id2"); + fu_device_add_vendor_id(device2, "USB:FFFF"); + fu_device_add_protocol(device2, "com.acme"); + fu_device_set_name(device2, "Secondary firmware"); + fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device2, "4.5.6"); + fu_device_add_vendor_id(device2, "FFFF"); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b"); + fu_device_add_child(parent, device2); + fu_engine_add_device(engine, device2); + + /* check this passes */ + fu_release_set_device(release2, device1); + fu_release_set_request(release2, request); + ret = fu_release_load(release2, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release2, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_other_device_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + 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_format(device1, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device1, "1.2.3"); + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012"); + + /* set up a different device */ + fu_device_set_id(device2, "id2"); + fu_device_add_vendor_id(device2, "USB:FFFF"); + fu_device_add_protocol(device2, "com.acme"); + fu_device_set_name(device2, "Secondary firmware"); + fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device2, "4.5.6"); + fu_device_add_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 */ + fu_release_set_device(release, device1); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_protocol_check_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(FuRelease) release1 = fu_release_new(); + g_autoptr(FuRelease) release2 = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new(); + gboolean ret; + + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + " " + " " + " org.bar" + " " + + ""; + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + fu_device_set_id(device1, "NVME"); + fu_device_add_protocol(device1, "com.acme"); + fu_device_set_name(device1, "NVME device"); + fu_device_add_vendor_id(device1, "ACME"); + fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device1, "1.2.3"); + fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012"); + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_engine_add_device(engine, device1); + + fu_device_set_id(device2, "UEFI"); + fu_device_add_protocol(device2, "org.bar"); + fu_device_set_name(device2, "UEFI device"); + fu_device_add_vendor_id(device2, "ACME"); + fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device2, "1.2.3"); + fu_device_add_guid(device2, "12345678-1234-1234-1234-123456789012"); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_engine_add_device(engine, device2); + + /* make sure both devices added */ + devices = fu_engine_get_devices(engine, &error); + g_assert_no_error(error); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 2); + + /* 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 fails */ + fu_release_set_device(release1, device1); + fu_release_set_request(release1, request); + ret = fu_release_load(release1, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + g_clear_error(&error); + + /* check this passes */ + fu_release_set_device(release2, device2); + fu_release_set_request(release2, request); + ret = fu_release_load(release2, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_parent_device_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + 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 = + "" + " " + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " 1ff60ab2-3905-06a1-b476-0371f00c9e9b" + " " + " " + " " + " " + " " + " " + ""; + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* set up child device */ + fu_device_set_id(device2, "child"); + fu_device_set_name(device2, "child"); + fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device2, "4.5.6"); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b"); + + /* set up a parent device */ + fu_device_set_id(device1, "parent"); + fu_device_add_vendor_id(device1, "USB:FFFF"); + fu_device_add_protocol(device1, "com.acme"); + fu_device_set_name(device1, "parent"); + fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device1, "1.2.3"); + fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012"); + fu_device_add_child(device1, device2); + fu_engine_add_device(engine, device1); + + /* 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 */ + fu_release_set_device(release, device2); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_device_parent_guid_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device3 = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + 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_vendor_id(device2, "USB:FFFF"); + fu_device_add_protocol(device2, "com.acme"); + 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_vendor_id(device2, "USB:FFFF"); + fu_device_add_protocol(device2, "com.acme"); + 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_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 */ + fu_engine_add_device(engine, device2); + + /* this is normally done by fu_plugin_device_add() */ + fu_engine_add_device(engine, device3); + + /* verify both children were adopted */ + g_assert_true(fu_device_get_parent(device3) == device2); + g_assert_true(fu_device_get_parent(device1) == device2); + g_assert_cmpstr(fu_device_get_vendor(device3), ==, "oem"); + + /* verify order */ + g_assert_cmpint(fu_device_get_order(device1), ==, -1); + g_assert_cmpint(fu_device_get_order(device2), ==, 0); + g_assert_cmpint(fu_device_get_order(device3), ==, -1); +} + +static void +fu_engine_device_parent_id_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device3 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device4 = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + 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, "child1"); + fu_device_set_name(device1, "Child1"); + fu_device_set_physical_id(device1, "child-ID1"); + fu_device_add_vendor_id(device1, "USB:FFFF"); + fu_device_add_protocol(device1, "com.acme"); + fu_device_add_instance_id(device1, "child-GUID-1"); + fu_device_add_parent_physical_id(device1, "parent-ID-notfound"); + fu_device_add_parent_physical_id(device1, "parent-ID"); + fu_device_convert_instance_ids(device1); + fu_engine_add_device(engine, device1); + + /* parent */ + fu_device_set_id(device2, "parent"); + fu_device_set_name(device2, "Parent"); + fu_device_set_physical_id(device2, "parent-ID"); + fu_device_add_vendor_id(device2, "USB:FFFF"); + fu_device_add_protocol(device2, "com.acme"); + fu_device_add_instance_id(device2, "parent-GUID"); + fu_device_set_vendor(device2, "oem"); + fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN); + fu_device_convert_instance_ids(device2); + + /* add another child */ + fu_device_set_id(device3, "child2"); + fu_device_set_name(device3, "Child2"); + fu_device_set_physical_id(device3, "child-ID2"); + fu_device_add_instance_id(device3, "child-GUID-2"); + fu_device_add_parent_physical_id(device3, "parent-ID"); + fu_device_convert_instance_ids(device3); + fu_device_add_child(device2, device3); + + /* add two together */ + fu_engine_add_device(engine, device2); + + /* add non-child */ + fu_device_set_id(device4, "child4"); + fu_device_set_name(device4, "Child4"); + fu_device_set_physical_id(device4, "child-ID4"); + fu_device_add_vendor_id(device4, "USB:FFFF"); + fu_device_add_protocol(device4, "com.acme"); + fu_device_add_instance_id(device4, "child-GUID-4"); + fu_device_add_parent_physical_id(device4, "parent-ID"); + fu_device_convert_instance_ids(device4); + fu_engine_add_device(engine, device4); + + /* this is normally done by fu_plugin_device_add() */ + fu_engine_add_device(engine, device4); + + /* verify both children were adopted */ + g_assert_true(fu_device_get_parent(device3) == device2); + g_assert_true(fu_device_get_parent(device4) == device2); + g_assert_true(fu_device_get_parent(device1) == device2); + g_assert_cmpstr(fu_device_get_vendor(device3), ==, "oem"); +} + +static void +fu_engine_partial_hash_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuPlugin) plugin = fu_plugin_new(NULL); + 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_engine_add_plugin(engine, plugin); + + /* add two dummy devices */ + fu_device_set_id(device1, "device1"); + fu_device_add_vendor_id(device1, "USB:FFFF"); + fu_device_add_protocol(device1, "com.acme"); + fu_device_set_plugin(device1, "test"); + fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012"); + fu_engine_add_device(engine, device1); + fu_device_set_id(device2, "device21"); + fu_device_add_vendor_id(device2, "USB:FFFF"); + fu_device_add_protocol(device2, "com.acme"); + fu_device_set_plugin(device2, "test"); + fu_device_set_equivalent_id(device2, "b92f5b7560b84ca005a79f5a15de3c003ce494cf"); + fu_device_add_guid(device2, "87654321-1234-1234-1234-123456789012"); + fu_engine_add_device(engine, device2); + + /* match nothing */ + ret = fu_engine_unlock(engine, "deadbeef", &error_none); + g_assert_error(error_none, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); + + /* match both */ + ret = fu_engine_unlock(engine, "9", &error_both); + g_assert_error(error_both, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + + /* match one exactly */ + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED); + ret = fu_engine_unlock(engine, "934b4162a6daa0b033d649c8d464529cec41d3de", &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* match one partially */ + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED); + ret = fu_engine_unlock(engine, "934b", &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* match equivalent ID */ + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED); + ret = fu_engine_unlock(engine, "b92f", &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_device_unlock_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add the hardcoded 'fwupd' metadata */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "metadata.xml", 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_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuRelease) release = fu_release_new(); + 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(__s390x__) + /* See https://github.com/fwupd/fwupd/issues/318 for more information */ + g_test_skip("Skipping HWID test on s390x due to known problem with gcab"); + return; +#endif + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* load engine to get FuConfig set up */ + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* get generated file as a blob */ + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "hwid-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + + /* add a dummy device */ + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_engine_add_device(engine, device); + + /* 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 */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_nonnull(error); + g_assert_cmpstr(error->message, + ==, + "no HWIDs matched 9342d47a-1bab-5709-9869-c840b2eac501"); + g_assert_false(ret); +} + +static void +fu_engine_get_details_added_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + FuDevice *device_tmp; + FwupdRelease *release; + gboolean ret; + g_autofree gchar *checksum_sha256 = NULL; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) blob_cab = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new(); + +#if defined(__s390x__) + /* See https://github.com/fwupd/fwupd/issues/318 for more information */ + g_test_skip("Skipping HWID test on s390x due to known problem with gcab"); + return; +#endif + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* load engine to get FuConfig set up */ + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a dummy device */ + fu_device_set_id(device, "test_device"); + fu_device_set_name(device, "test device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_engine_add_device(engine, device); + + /* get details */ + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "hwid-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + checksum_sha256 = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, blob_cab); + devices = fu_engine_get_details_for_bytes(engine, request, blob_cab, &error); + g_assert_no_error(error); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + device_tmp = g_ptr_array_index(devices, 0); + g_assert_cmpstr(fu_device_get_name(device_tmp), ==, "test device"); + release = fu_device_get_release_default(device_tmp); + g_assert_nonnull(release); + g_assert_cmpstr(fwupd_release_get_version(release), ==, "1.2.3"); + g_assert_true(fwupd_release_has_checksum(release, checksum_sha256)); +} + +static void +fu_engine_get_details_missing_func(gconstpointer user_data) +{ + FuDevice *device_tmp; + FwupdRelease *release; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) blob_cab = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new(); + +#if defined(__s390x__) + /* See https://github.com/fwupd/fwupd/issues/318 for more information */ + g_test_skip("Skipping HWID test on s390x due to known problem with gcab"); + return; +#endif + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* load engine to get FuConfig set up */ + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* get details */ + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "hwid-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + devices = fu_engine_get_details_for_bytes(engine, request, blob_cab, &error); + g_assert_no_error(error); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + device_tmp = g_ptr_array_index(devices, 0); + g_assert_cmpstr(fu_device_get_name(device_tmp), ==, NULL); + release = fu_device_get_release_default(device_tmp); + g_assert_nonnull(release); + g_assert_cmpstr(fwupd_release_get_version(release), ==, "1.2.3"); +} + +static void +fu_engine_downgrade_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + FwupdRelease *rel; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_pre = NULL; + g_autoptr(GPtrArray) releases_dg = NULL; + 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", + "this is not a valid", + -1, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* write the main file */ + ret = g_file_set_contents( + "/tmp/fwupd-self-test/stable.xml", + "" + " " + " test" + " Test Device" + " " + " aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + " " + " " + " " + " 123" + " 456" + " https://test.org/foo.cab" + " deadbeefdeadbeefdeadbeefdeadbeef" + " deadbeefdeadbeefdeadbeefdeadbeef" + " " + " " + " 123" + " 456" + " https://test.org/foo.cab" + " deadbeefdeadbeefdeadbeefdeadbeef" + " deadbeefdeadbeefdeadbeefdeadbeef" + " " + " " + " " + "", + -1, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* write the extra file */ + ret = g_file_set_contents( + "/tmp/fwupd-self-test/testing.xml", + "" + " " + " test" + " Test Device" + " " + " aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + " " + " " + " " + " 123" + " 456" + " https://test.org/foo.cab" + " deadbeefdeadbeefdeadbeefdeadbeef" + " deadbeefdeadbeefdeadbeefdeadbeef" + " " + " " + " 123" + " 456" + " https://test.org/foo.cab" + " deadbeefdeadbeefdeadbeefdeadbeef" + " deadbeefdeadbeefdeadbeefdeadbeef" + " " + " " + " " + "", + -1, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = fu_engine_load(engine, + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_NO_CACHE, + progress, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_test_assert_expected_messages(); + + /* return all the remotes, even the broken one */ + remotes = fu_engine_get_remotes(engine, &error); + g_assert_no_error(error); + g_assert_nonnull(remotes); + g_assert_cmpint(remotes->len, ==, 4); + + /* ensure there are no devices already */ + devices_pre = fu_engine_get_devices(engine, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); + g_assert_null(devices_pre); + g_clear_error(&error); + + /* add a device so we can get upgrades and downgrades */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + fu_device_set_name(device, "Test Device"); + fu_device_add_guid(device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); +#ifndef HAVE_POLKIT + g_test_expect_message("FuEngine", + G_LOG_LEVEL_WARNING, + "*archive signature missing or not trusted"); +#endif + fu_engine_add_device(engine, device); + devices = fu_engine_get_devices(engine, &error); + g_assert_no_error(error); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); +#ifdef HAVE_POLKIT + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED)); +#endif + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)); + + /* get the releases for one device */ + releases = fu_engine_get_releases(engine, request, fu_device_get_id(device), &error); + g_assert_no_error(error); + g_assert_nonnull(releases); + g_assert_cmpint(releases->len, ==, 4); + + /* no upgrades, as no firmware is approved */ + releases_up = fu_engine_get_upgrades(engine, request, 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, request, fu_device_get_id(device), &error); + g_assert_no_error(error); + g_assert_nonnull(releases_up); + g_assert_cmpint(releases_up->len, ==, 2); + + /* ensure the list is sorted */ + rel = FWUPD_RELEASE(g_ptr_array_index(releases_up, 0)); + g_assert_cmpstr(fwupd_release_get_version(rel), ==, "1.2.5"); + rel = FWUPD_RELEASE(g_ptr_array_index(releases_up, 1)); + g_assert_cmpstr(fwupd_release_get_version(rel), ==, "1.2.4"); + + /* downgrades */ + releases_dg = fu_engine_get_downgrades(engine, request, fu_device_get_id(device), &error); + g_assert_no_error(error); + g_assert_nonnull(releases_dg); + g_assert_cmpint(releases_dg->len, ==, 1); + rel = FWUPD_RELEASE(g_ptr_array_index(releases_dg, 0)); + g_assert_cmpstr(fwupd_release_get_version(rel), ==, "1.2.2"); +} + +static void +fu_engine_install_duration_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + FwupdRelease *rel; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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_true(ret); + + ret = fu_engine_load(engine, + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_NO_CACHE, + progress, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a device so we can get the install duration */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); +#ifndef HAVE_POLKIT + g_test_expect_message("FuEngine", + G_LOG_LEVEL_WARNING, + "*archive signature missing or not trusted"); +#endif + fu_engine_add_device(engine, device); + devices = fu_engine_get_devices(engine, &error); + g_assert_no_error(error); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); +#ifdef HAVE_POLKIT + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED)); +#endif + /* check the release install duration */ + releases = fu_engine_get_releases(engine, request, fu_device_get_id(device), &error); + g_assert_no_error(error); + g_assert_nonnull(releases); + 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(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + 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_autoptr(FuDevice) device2 = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuHistory) history = NULL; + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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 */ + fu_engine_add_plugin(engine, self->plugin); + + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a device so we can get upgrade it */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + 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_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)); + + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + 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); + + /* set the counter */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "another-write-required", TRUE); + fu_device_set_metadata_integer(device, "nr-update", 0); + + /* install it */ + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(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); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("no sqlite support"); + return; + } + g_assert_no_error(error); + g_assert_nonnull(device2); + g_assert_cmpint(fu_device_get_update_state(device2), ==, FWUPD_UPDATE_STATE_SUCCESS); + g_assert_cmpstr(fu_device_get_update_error(device2), ==, NULL); + fu_device_set_modified(device2, 1514338000); + g_hash_table_remove_all(fwupd_release_get_metadata(fu_device_get_release_default(device2))); + device_str = fu_device_to_string(device2); + checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, blob_cab); + device_str_expected = + g_strdup_printf("FuDevice:\n" + " DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n" + " Name: Test Device\n" + " Guid: 12345678-1234-1234-1234-123456789012\n" + " Plugin: test\n" + " Flags: historical\n" + " Version: 1.2.2\n" + " Created: 2018-01-07\n" + " Modified: 2017-12-27\n" + " UpdateState: success\n" + " \n" + " [Release]\n" + " Version: 1.2.3\n" + " Checksum: SHA1(%s)\n" + " Flags: none\n" + " AcquiesceDelay: 50\n", + checksum); + ret = fu_test_compare_lines(device_str, device_str_expected, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* GetResults() */ + device3 = fu_engine_get_results(engine, FWUPD_DEVICE_ID_ANY, &error); + g_assert_nonnull(device3); + g_assert_cmpstr(fu_device_get_id(device3), ==, "894e8c17a29428b09d10cd90d1db74ea76fbcfe8"); + g_assert_cmpint(fu_device_get_update_state(device3), ==, FWUPD_UPDATE_STATE_SUCCESS); + g_assert_cmpstr(fu_device_get_update_error(device3), ==, NULL); + + /* ClearResults() */ + ret = fu_engine_clear_results(engine, FWUPD_DEVICE_ID_ANY, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* GetResults() */ + device4 = fu_engine_get_results(engine, FWUPD_DEVICE_ID_ANY, &error); + g_assert_null(device4); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); +} + +static void +fu_engine_multiple_rels_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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; + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(GPtrArray) releases = NULL; + g_autoptr(GPtrArray) rels = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + + /* ensure empty tree */ + fu_self_test_mkroot(); + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* set up dummy plugin */ + fu_engine_add_plugin(engine, self->plugin); + + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a device so we can get upgrade it */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES); + fu_device_set_created(device, 1515338000); + fu_engine_add_device(engine, device); + + filename = g_test_build_filename(G_TEST_BUILT, + "tests", + "multiple-rels", + "multiple-rels-1.2.4.cab", + NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + 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); + + /* set up counter */ + fu_device_set_metadata_integer(device, "nr-update", 0); + + /* get all */ +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + &error); + g_assert_no_error(error); + g_assert_nonnull(query); + rels = xb_node_query_full(component, query, &error); +#else + rels = xb_node_query(component, "releases/release", 0, &error); +#endif + g_assert_no_error(error); + g_assert_nonnull(rels); + + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < rels->len; i++) { + XbNode *rel = g_ptr_array_index(rels, i); + g_autoptr(FuRelease) release = fu_release_new(); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, rel, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_ptr_array_add(releases, g_object_ref(release)); + } + + /* install them */ + fu_progress_reset(progress); + ret = fu_engine_install_releases(engine, + request, + releases, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check we did 1.2.2 -> 1.2.3 -> 1.2.4 */ + g_assert_cmpint(fu_device_get_metadata_integer(device, "nr-update"), ==, 2); + g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.4"); +} + +static void +fu_engine_history_inherit(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *history_db = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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; + +#ifndef HAVE_SQLITE + g_test_skip("no sqlite support"); + return; +#endif + + /* delete history */ + localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + history_db = g_build_filename(localstatedir, "pending.db", NULL); + (void)g_unlink(history_db); + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* set up dummy plugin */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "fail", TRUE); + fu_engine_add_plugin(engine, self->plugin); + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a device so we can get upgrade it */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + 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_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)); + + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + 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 */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "requires-activation", TRUE); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(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 */ + fu_progress_reset(progress); + ret = fu_engine_activate(engine, fu_device_get_id(device), progress, &error); + g_assert_no_error(error); + g_assert_true(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_progress_reset(progress); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_object_unref(engine); + g_object_unref(device); + engine = fu_engine_new(); + fu_engine_set_silo(engine, silo_empty); + fu_engine_add_plugin(engine, self->plugin); + device = fu_device_new(self->ctx); + fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + fu_device_set_name(device, "Test Device"); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_engine_add_device(engine, device); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)); + + /* emulate not getting the flag */ + g_object_unref(engine); + g_object_unref(device); + engine = fu_engine_new(); + fu_engine_set_silo(engine, silo_empty); + fu_engine_add_plugin(engine, self->plugin); + device = fu_device_new(self->ctx); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + fu_device_set_name(device, "Test Device"); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_engine_add_device(engine, device); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)); +} + +static void +fu_engine_install_needs_reboot(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autofree gchar *filename = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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 */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "fail", TRUE); + fu_engine_add_plugin(engine, self->plugin); + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NONE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a device so we can get upgrade it */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + 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_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)); + + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + 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 */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "requires-reboot", TRUE); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check the device requires reboot */ + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); + g_assert_cmpint(fu_device_get_update_state(device), ==, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2"); +} + +static void +fu_engine_history_error_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + 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_autoptr(FuDevice) device2 = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuHistory) history = NULL; + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) blob_cab = NULL; + 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 */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "fail", TRUE); + fu_engine_add_plugin(engine, self->plugin); + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add a device so we can get upgrade it */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.2"); + fu_device_set_id(device, "test_device"); + fu_device_add_vendor_id(device, "USB:FFFF"); + fu_device_add_protocol(device, "com.acme"); + 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_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + 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_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED)); + + /* install the wrong thing */ + filename = + g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL); + blob_cab = fu_bytes_get_contents(filename, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_cab); + 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_nonnull(component); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_cmpstr(error->message, ==, "device was not in supported mode"); + g_assert_false(ret); + + /* check the history database */ + history = fu_history_new(); + device2 = fu_history_get_device_by_id(history, fu_device_get_id(device), &error2); + if (g_error_matches(error2, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip("no sqlite support"); + return; + } + g_assert_no_error(error2); + g_assert_nonnull(device2); + g_assert_cmpint(fu_device_get_update_state(device2), ==, FWUPD_UPDATE_STATE_FAILED); + g_assert_cmpstr(fu_device_get_update_error(device2), ==, error->message); + g_clear_error(&error); + fu_device_set_modified(device2, 1514338000); + g_hash_table_remove_all(fwupd_release_get_metadata(fu_device_get_release_default(device2))); + device_str = fu_device_to_string(device2); + checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, blob_cab); + device_str_expected = + g_strdup_printf("FuDevice:\n" + " DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n" + " Name: Test Device\n" + " Guid: 12345678-1234-1234-1234-123456789012\n" + " Plugin: test\n" + " Flags: updatable|historical\n" + " Version: 1.2.2\n" + " Created: 2018-01-07\n" + " Modified: 2017-12-27\n" + " UpdateState: failed\n" + " UpdateError: device was not in supported mode\n" + " \n" + " [Release]\n" + " Version: 1.2.3\n" + " Checksum: SHA1(%s)\n" + " Flags: none\n" + " AcquiesceDelay: 50\n", + checksum); + ret = fu_test_compare_lines(device_str, device_str_expected, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +_device_list_count_cb(FuDeviceList *device_list, FuDevice *device, gpointer user_data) +{ + guint *cnt = (guint *)user_data; + (*cnt)++; +} + +static void +fu_device_list_no_auto_remove_children_func(gconstpointer user_data) +{ + g_autoptr(FuDevice) child = fu_device_new(NULL); + g_autoptr(FuDevice) parent = fu_device_new(NULL); + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + g_autoptr(GPtrArray) active1 = NULL; + g_autoptr(GPtrArray) active2 = NULL; + g_autoptr(GPtrArray) active3 = NULL; + + /* normal behavior, remove child with parent */ + fu_device_set_id(parent, "parent"); + fu_device_set_id(child, "child"); + fu_device_add_child(parent, child); + fu_device_list_add(device_list, parent); + fu_device_list_add(device_list, child); + fu_device_list_remove(device_list, parent); + active1 = fu_device_list_get_active(device_list); + g_assert_cmpint(active1->len, ==, 0); + + /* new-style behavior, do not remove child */ + fu_device_add_internal_flag(parent, FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN); + fu_device_list_add(device_list, parent); + fu_device_list_add(device_list, child); + fu_device_list_remove(device_list, parent); + active2 = fu_device_list_get_active(device_list); + g_assert_cmpint(active2->len, ==, 1); + fu_device_list_remove(device_list, child); + active3 = fu_device_list_get_active(device_list); + g_assert_cmpint(active3->len, ==, 0); +} + +static void +fu_device_list_delay_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + guint added_cnt = 0; + guint changed_cnt = 0; + guint removed_cnt = 0; + + g_signal_connect(FU_DEVICE_LIST(device_list), + "added", + G_CALLBACK(_device_list_count_cb), + &added_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "removed", + G_CALLBACK(_device_list_count_cb), + &removed_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "changed", + G_CALLBACK(_device_list_count_cb), + &changed_cnt); + + /* add one device */ + fu_device_set_id(device1, "device1"); + 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); + g_assert_cmpint(changed_cnt, ==, 0); + + /* add the same device again */ + fu_device_list_add(device_list, device1); + g_assert_cmpint(added_cnt, ==, 1); + g_assert_cmpint(removed_cnt, ==, 0); + g_assert_cmpint(changed_cnt, ==, 1); + + /* add a device with the same ID */ + fu_device_set_id(device2, "device1"); + fu_device_list_add(device_list, device2); + fu_device_set_remove_delay(device2, 100); + g_assert_cmpint(added_cnt, ==, 1); + g_assert_cmpint(removed_cnt, ==, 0); + g_assert_cmpint(changed_cnt, ==, 2); + + /* spin a bit */ + fu_test_loop_run_with_timeout(10); + fu_test_loop_quit(); + + /* verify only a changed event was generated */ + added_cnt = removed_cnt = changed_cnt = 0; + fu_device_list_remove(device_list, device1); + fu_device_list_add(device_list, device1); + g_assert_cmpint(added_cnt, ==, 0); + g_assert_cmpint(removed_cnt, ==, 0); + 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(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new(NULL); + g_autoptr(FuDevice) device2 = fu_device_new(NULL); + g_autoptr(FuDevice) parent = fu_device_new(NULL); + 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_add_internal_flag(device1, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + 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_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + 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, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add device */ + fu_device_list_add(device_list, device1); + + /* not waiting */ + ret = fu_device_list_wait_for_replug(device_list, &error); + g_assert_no_error(error); + g_assert_true(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, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); + + /* check device2 now has parent too */ + g_assert_true(fu_device_get_parent(device2) == parent); + + /* waiting, failed */ + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + ret = fu_device_list_wait_for_replug(device_list, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); + g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); +} + +static void +fu_device_list_replug_user_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + 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_set_name(device1, "device1"); + fu_device_add_internal_flag(device1, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + 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_set_name(device2, "device2"); + fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + 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, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add device */ + fu_device_list_add(device_list, device1); + + /* add duplicate */ + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_list_add(device_list, device1); + g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); + + /* not waiting */ + ret = fu_device_list_wait_for_replug(device_list, &error); + g_assert_no_error(error); + g_assert_true(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, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); + + /* should not be possible, but here we are */ + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_list_add(device_list, device1); + g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); + g_assert_false(fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); + + /* add back the old device */ + fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_list_remove(device_list, device2); + fu_device_list_add(device_list, device1); + g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); + g_assert_false(fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); +} + +static void +fu_device_list_compatible_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device_old = NULL; + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + g_autoptr(GPtrArray) devices_all = NULL; + g_autoptr(GPtrArray) devices_active = NULL; + FuDevice *device; + guint added_cnt = 0; + guint changed_cnt = 0; + guint removed_cnt = 0; + + g_signal_connect(FU_DEVICE_LIST(device_list), + "added", + G_CALLBACK(_device_list_count_cb), + &added_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "removed", + G_CALLBACK(_device_list_count_cb), + &removed_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "changed", + G_CALLBACK(_device_list_count_cb), + &changed_cnt); + + /* add one device in runtime mode */ + fu_device_set_id(device1, "device1"); + fu_device_set_plugin(device1, "plugin-for-runtime"); + fu_device_add_vendor_id(device1, "USB:0x20A0"); + fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device1, "1.2.3"); + fu_device_add_internal_flag(device1, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + 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); + g_assert_cmpint(changed_cnt, ==, 0); + + /* add another device in bootloader mode */ + fu_device_set_id(device2, "device2"); + fu_device_set_plugin(device2, "plugin-for-bootloader"); + fu_device_add_instance_id(device2, "bootloader"); + fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID); + fu_device_convert_instance_ids(device2); + + /* verify only a changed event was generated */ + added_cnt = removed_cnt = changed_cnt = 0; + fu_device_list_remove(device_list, device1); + fu_device_list_add(device_list, device2); + g_assert_cmpint(added_cnt, ==, 0); + g_assert_cmpint(removed_cnt, ==, 0); + g_assert_cmpint(changed_cnt, ==, 1); + + /* device2 should inherit the vendor ID and version from device1 */ + g_assert_true(fu_device_has_vendor_id(device2, "USB:0x20A0")); + g_assert_cmpstr(fu_device_get_version(device2), ==, "1.2.3"); + + /* one device is active */ + devices_active = fu_device_list_get_active(device_list); + g_assert_cmpint(devices_active->len, ==, 1); + device = g_ptr_array_index(devices_active, 0); + g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); + + /* the list knows about both devices, list in order of active->old */ + devices_all = fu_device_list_get_all(device_list); + g_assert_cmpint(devices_all->len, ==, 2); + device = g_ptr_array_index(devices_all, 0); + g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); + device = g_ptr_array_index(devices_all, 1); + g_assert_cmpstr(fu_device_get_id(device), ==, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a"); + + /* verify we can get the old device from the new device */ + device_old = fu_device_list_get_old(device_list, device2); + g_assert_true(device_old == device1); +} + +static void +fu_device_list_remove_chain_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + g_autoptr(FuDevice) device_child = fu_device_new(self->ctx); + g_autoptr(FuDevice) device_parent = fu_device_new(self->ctx); + + guint added_cnt = 0; + guint changed_cnt = 0; + guint removed_cnt = 0; + + g_signal_connect(FU_DEVICE_LIST(device_list), + "added", + G_CALLBACK(_device_list_count_cb), + &added_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "removed", + G_CALLBACK(_device_list_count_cb), + &removed_cnt); + g_signal_connect(FU_DEVICE_LIST(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 +fu_device_list_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); + g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices2 = NULL; + g_autoptr(GError) error = NULL; + FuDevice *device; + guint added_cnt = 0; + guint changed_cnt = 0; + guint removed_cnt = 0; + + g_signal_connect(FU_DEVICE_LIST(device_list), + "added", + G_CALLBACK(_device_list_count_cb), + &added_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "removed", + G_CALLBACK(_device_list_count_cb), + &removed_cnt); + g_signal_connect(FU_DEVICE_LIST(device_list), + "changed", + G_CALLBACK(_device_list_count_cb), + &changed_cnt); + + /* add both */ + fu_device_set_id(device1, "device1"); + 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_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); + g_assert_cmpint(changed_cnt, ==, 0); + + /* get all */ + devices = fu_device_list_get_all(device_list); + g_assert_cmpint(devices->len, ==, 2); + device = g_ptr_array_index(devices, 0); + g_assert_cmpstr(fu_device_get_id(device), ==, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a"); + + /* find by ID */ + device = fu_device_list_get_by_id(device_list, + "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a", + &error); + g_assert_no_error(error); + g_assert_nonnull(device); + 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, "579a3b1c-d1db-5bdc-b6b9-e2c1b28d5b8a", &error); + g_assert_no_error(error); + g_assert_nonnull(device); + 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); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(device); + + /* remove device */ + added_cnt = removed_cnt = changed_cnt = 0; + fu_device_list_remove(device_list, device1); + g_assert_cmpint(added_cnt, ==, 0); + g_assert_cmpint(removed_cnt, ==, 1); + g_assert_cmpint(changed_cnt, ==, 0); + devices2 = fu_device_list_get_all(device_list); + g_assert_cmpint(devices2->len, ==, 1); + device = g_ptr_array_index(devices2, 0); + g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); +} + +static void +fu_plugin_list_func(gconstpointer user_data) +{ + GPtrArray *plugins; + FuPlugin *plugin; + g_autoptr(FuPluginList) plugin_list = fu_plugin_list_new(); + g_autoptr(FuPlugin) plugin1 = fu_plugin_new(NULL); + g_autoptr(FuPlugin) plugin2 = fu_plugin_new(NULL); + g_autoptr(GError) error = NULL; + + fu_plugin_set_name(plugin1, "plugin1"); + fu_plugin_set_name(plugin2, "plugin2"); + + /* get all the plugins */ + fu_plugin_list_add(plugin_list, plugin1); + fu_plugin_list_add(plugin_list, plugin2); + plugins = fu_plugin_list_get_all(plugin_list); + g_assert_cmpint(plugins->len, ==, 2); + + /* get a single plugin */ + plugin = fu_plugin_list_find_by_name(plugin_list, "plugin1", &error); + g_assert_no_error(error); + g_assert_nonnull(plugin); + g_assert_cmpstr(fu_plugin_get_name(plugin), ==, "plugin1"); + + /* does not exist */ + plugin = fu_plugin_list_find_by_name(plugin_list, "nope", &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(plugin); +} + +static void +fu_plugin_list_depsolve_func(gconstpointer user_data) +{ + GPtrArray *plugins; + FuPlugin *plugin; + gboolean ret; + g_autoptr(FuPluginList) plugin_list = fu_plugin_list_new(); + g_autoptr(FuPlugin) plugin1 = fu_plugin_new(NULL); + g_autoptr(FuPlugin) plugin2 = fu_plugin_new(NULL); + g_autoptr(GError) error = NULL; + + fu_plugin_set_name(plugin1, "plugin1"); + fu_plugin_set_name(plugin2, "plugin2"); + + /* add rule then depsolve */ + fu_plugin_list_add(plugin_list, plugin1); + fu_plugin_list_add(plugin_list, plugin2); + fu_plugin_add_rule(plugin1, FU_PLUGIN_RULE_RUN_AFTER, "plugin2"); + ret = fu_plugin_list_depsolve(plugin_list, &error); + g_assert_no_error(error); + g_assert_true(ret); + plugins = fu_plugin_list_get_all(plugin_list); + g_assert_cmpint(plugins->len, ==, 2); + plugin = g_ptr_array_index(plugins, 0); + g_assert_cmpstr(fu_plugin_get_name(plugin), ==, "plugin2"); + g_assert_cmpint(fu_plugin_get_order(plugin), ==, 0); + g_assert_false(fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)); + + /* add another rule, then re-depsolve */ + fu_plugin_add_rule(plugin1, FU_PLUGIN_RULE_CONFLICTS, "plugin2"); + ret = fu_plugin_list_depsolve(plugin_list, &error); + g_assert_no_error(error); + g_assert_true(ret); + plugin = fu_plugin_list_find_by_name(plugin_list, "plugin1", &error); + g_assert_no_error(error); + g_assert_nonnull(plugin); + g_assert_false(fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)); + plugin = fu_plugin_list_find_by_name(plugin_list, "plugin2", &error); + g_assert_no_error(error); + g_assert_nonnull(plugin); + g_assert_true(fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)); +} + +static void +fu_history_migrate_func(gconstpointer user_data) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + g_autoptr(GFile) file_dst = NULL; + g_autoptr(GFile) file_src = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuHistory) history = NULL; + g_autofree gchar *filename = NULL; + +#ifndef HAVE_SQLITE + g_test_skip("no sqlite support"); + return; +#endif + + /* load old version */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "history_v1.db", NULL); + file_src = g_file_new_for_path(filename); + file_dst = g_file_new_for_path("/tmp/fwupd-self-test/var/lib/fwupd/pending.db"); + ret = g_file_copy(file_src, file_dst, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* create, migrating as required */ + history = fu_history_new(); + g_assert_nonnull(history); + + /* get device */ + device = fu_history_get_device_by_id(history, + "2ba16d10df45823dd4494ff10a0bfccfef512c9d", + &error); + g_assert_no_error(error); + g_assert_nonnull(device); + g_assert_cmpstr(fu_device_get_id(device), ==, "2ba16d10df45823dd4494ff10a0bfccfef512c9d"); +} + +static void +_plugin_status_changed_cb(FuDevice *device, FwupdStatus status, gpointer user_data) +{ + guint *cnt = (guint *)user_data; + g_debug("status now %s", fwupd_status_to_string(status)); + (*cnt)++; + fu_test_loop_quit(); +} + +static void +_plugin_device_added_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + FuDevice **dev = (FuDevice **)user_data; + *dev = g_object_ref(device); + fu_test_loop_quit(); +} + +static void +_plugin_device_register_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data) +{ + /* fake being a daemon */ + fu_plugin_runner_device_register(plugin, device); +} + +/* + * To generate the fwupd DS20 descriptor in the usb-devices.json file save fw-ds20.builder.xml: + * + * + * 42 + * 32 + * + * + * Then run: + * + * fwupdtool firmware-build fw-ds20.builder.xml fw-ds20.bin + * base64 fw-ds20.bin + * + * To generate the fake control transfer response, save fw-ds20.quirk: + * + * [USB\VID_273F&PID_1004] + * Plugin = dfu + * Icon = computer + * + * Then run: + * + * contrib/generate-ds20.py fw-ds20.quirk --bufsz 32 + */ +static void +fu_backend_usb_func(gconstpointer user_data) +{ +#ifdef HAVE_GUSB + FuTest *self = (FuTest *)user_data; + gboolean ret; + FuDevice *device_tmp; + g_autofree gchar *gusb_emulate_fn = NULL; + g_autofree gchar *devicestr = NULL; + g_autoptr(FuBackend) backend = fu_usb_backend_new(self->ctx); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) possible_plugins = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + +#if !G_USB_CHECK_VERSION(0, 4, 2) + g_test_skip("GUsb version too old"); + return; +#endif + + /* load the JSON into the backend */ + gusb_emulate_fn = g_test_build_filename(G_TEST_DIST, "tests", "usb-devices.json", NULL); + ret = json_parser_load_from_file(parser, gusb_emulate_fn, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpstr(fu_backend_get_name(backend), ==, "usb"); + g_assert_true(fu_backend_get_enabled(backend)); + ret = fu_backend_setup(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_backend_load(backend, + json_node_get_object(json_parser_get_root(parser)), + NULL, + FU_BACKEND_LOAD_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_backend_coldplug(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + devices = fu_backend_get_devices(backend); + g_assert_cmpint(devices->len, ==, 1); + device_tmp = g_ptr_array_index(devices, 0); + fu_device_set_context(device_tmp, self->ctx); + locker = fu_device_locker_new(device_tmp, &error); + g_assert_no_error(error); + g_assert_nonnull(locker); + + /* for debugging */ + devicestr = fu_device_to_string(device_tmp); + g_debug("%s", devicestr); + + /* check the device was processed correctly by FuUsbDevice */ + g_assert_cmpstr(fu_device_get_name(device_tmp), ==, "ColorHug2"); + g_assert_true(fu_device_has_instance_id(device_tmp, "USB\\VID_273F&PID_1004&REV_0002")); + g_assert_true(fu_device_has_vendor_id(device_tmp, "USB:0x273F")); + + /* check the fwupd DS20 descriptor was parsed */ + g_assert_true(fu_device_has_icon(device_tmp, "computer")); + possible_plugins = fu_device_get_possible_plugins(device_tmp); + g_assert_cmpint(possible_plugins->len, ==, 1); + g_assert_cmpstr(g_ptr_array_index(possible_plugins, 0), ==, "dfu"); +#else + g_test_skip("No GUsb support"); +#endif +} + +static void +fu_backend_usb_invalid_func(gconstpointer user_data) +{ +#ifdef HAVE_GUSB + FuTest *self = (FuTest *)user_data; + gboolean ret; + FuDevice *device_tmp; + g_autofree gchar *gusb_emulate_fn = NULL; + g_autoptr(FuBackend) backend = fu_usb_backend_new(self->ctx); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + +#if !G_USB_CHECK_VERSION(0, 4, 2) + g_test_skip("GUsb version too old"); + return; +#endif + + /* load the JSON into the backend */ + gusb_emulate_fn = + g_test_build_filename(G_TEST_DIST, "tests", "usb-devices-invalid.json", NULL); + ret = json_parser_load_from_file(parser, gusb_emulate_fn, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_backend_setup(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_backend_load(backend, + json_node_get_object(json_parser_get_root(parser)), + NULL, + FU_BACKEND_LOAD_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_backend_coldplug(backend, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + devices = fu_backend_get_devices(backend); + g_assert_cmpint(devices->len, ==, 1); + device_tmp = g_ptr_array_index(devices, 0); + fu_device_set_context(device_tmp, self->ctx); + + g_test_expect_message("FuUsbDevice", + G_LOG_LEVEL_WARNING, + "*invalid platform version 0x0000000a, expected >= 0x00010805*"); + g_test_expect_message("FuUsbDevice", + G_LOG_LEVEL_WARNING, + "failed to parse * BOS descriptor: did not find magic*"); + + locker = fu_device_locker_new(device_tmp, &error); + g_assert_no_error(error); + g_assert_nonnull(locker); + + /* check the device was processed correctly by FuUsbDevice */ + g_assert_cmpstr(fu_device_get_name(device_tmp), ==, "ColorHug2"); + g_assert_true(fu_device_has_instance_id(device_tmp, "USB\\VID_273F&PID_1004&REV_0002")); + g_assert_true(fu_device_has_vendor_id(device_tmp, "USB:0x273F")); + + /* check the fwupd DS20 descriptor was *not* parsed */ + g_assert_false(fu_device_has_icon(device_tmp, "computer")); +#else + g_test_skip("No GUsb support"); +#endif +} + +static void +fu_plugin_module_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + GError *error = NULL; + FuDevice *device_tmp; + FwupdRelease *release; + gboolean ret; + guint cnt = 0; + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *mapped_file_fn = NULL; + g_autofree gchar *pending_cap = NULL; + g_autofree gchar *history_db = NULL; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevice) device2 = NULL; + g_autoptr(FuDevice) device3 = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuHistory) history = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) blob_cab = NULL; + g_autoptr(GMappedFile) mapped_file = NULL; + g_autoptr(XbSilo) silo_empty = xb_silo_new(); + + /* no metadata in daemon */ + fu_engine_set_silo(engine, silo_empty); + + /* create a fake device */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "registration", TRUE); + ret = fu_plugin_runner_startup(self->plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_signal_connect(FU_PLUGIN(self->plugin), + "device-added", + G_CALLBACK(_plugin_device_added_cb), + &device); + g_signal_connect(FU_PLUGIN(self->plugin), + "device-register", + G_CALLBACK(_plugin_device_register_cb), + &device); + ret = fu_plugin_runner_coldplug(self->plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check we did the right thing */ + g_assert_nonnull(device); + g_assert_cmpstr(fu_device_get_id(device), ==, "08d460be0f1f9f128413f816022a6439e0078018"); + g_assert_cmpstr(fu_device_get_version_lowest(device), ==, "1.2.0"); + g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2"); + g_assert_cmpstr(fu_device_get_version_bootloader(device), ==, "0.1.2"); + g_assert_cmpstr(fu_device_get_guid_default(device), + ==, + "b585990a-003e-5270-89d5-3705a17f9a43"); + g_assert_cmpstr(fu_device_get_name(device), ==, "Integrated Webcam™"); + g_signal_handlers_disconnect_by_data(self->plugin, &device); + +#ifndef HAVE_FWUPDOFFLINE + g_test_skip("No offline update support on Windows"); + return; +#endif + /* schedule an offline update */ + g_signal_connect(FU_PROGRESS(progress), + "status-changed", + G_CALLBACK(_plugin_status_changed_cb), + &cnt); + mapped_file_fn = + g_test_build_filename(G_TEST_DIST, "tests", "colorhug", "firmware.bin", NULL); + mapped_file = g_mapped_file_new(mapped_file_fn, FALSE, &error); + g_assert_no_error(error); + g_assert_nonnull(mapped_file); + blob_cab = g_mapped_file_get_bytes(mapped_file); + release = fu_device_get_release_default(device); + fwupd_release_set_version(release, "1.2.3"); + ret = fu_engine_schedule_update(engine, + device, + release, + blob_cab, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* 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); + g_assert_no_error(error); + g_assert_nonnull(device2); + 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_nonnull(release); + g_assert_cmpstr(fwupd_release_get_filename(release), !=, NULL); + g_assert_cmpstr(fwupd_release_get_version(release), ==, "1.2.3"); + + /* save this; we'll need to delete it later */ + pending_cap = g_strdup(fwupd_release_get_filename(release)); + + /* lets do this online */ + fu_engine_add_device(engine, device); + fu_engine_add_plugin(engine, self->plugin); + ret = fu_engine_install_blob(engine, + device, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_FEATURE_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(cnt, >=, 8); + + /* check the new version */ + g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.3"); + g_assert_cmpstr(fu_device_get_version_bootloader(device), ==, "0.1.2"); + + /* lets check the history */ + device3 = fu_history_get_device_by_id(history, fu_device_get_id(device), &error); + g_assert_no_error(error); + g_assert_nonnull(device3); + g_assert_cmpint(fu_device_get_update_state(device3), ==, FWUPD_UPDATE_STATE_SUCCESS); + g_assert_cmpstr(fu_device_get_update_error(device3), ==, NULL); + + /* get the status */ + device_tmp = fu_device_new(NULL); + fu_device_set_id(device_tmp, "FakeDevice"); + ret = fu_plugin_runner_get_results(self->plugin, device_tmp, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_device_get_update_state(device_tmp), ==, FWUPD_UPDATE_STATE_SUCCESS); + g_assert_cmpstr(fu_device_get_update_error(device_tmp), ==, NULL); + + /* clear */ + ret = fu_plugin_runner_clear_results(self->plugin, device_tmp, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_object_unref(device_tmp); + g_clear_error(&error); + + /* delete files */ + localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + history_db = g_build_filename(localstatedir, "pending.db", NULL); + (void)g_unlink(history_db); + (void)g_unlink(pending_cap); +} + +static void +fu_history_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + GError *error = NULL; + GPtrArray *checksums; + gboolean ret; + FuDevice *device; + 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; + +#ifndef HAVE_SQLITE + g_test_skip("no sqlite support"); + return; +#endif + + /* create */ + history = fu_history_new(); + g_assert_nonnull(history); + + /* delete the database */ + dirname = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) + return; + filename = g_build_filename(dirname, "pending.db", NULL); + (void)g_unlink(filename); + + /* add a device */ + device = fu_device_new(self->ctx); + fu_device_set_id(device, "self-test"); + fu_device_set_name(device, "ColorHug"), + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "3.0.1"), + 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"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_created(device, 123); + fu_device_set_modified(device, 456); + release = fwupd_release_new(); + fwupd_release_set_filename(release, "/var/lib/dave.cap"), + fwupd_release_add_checksum(release, "abcdef"); + fwupd_release_set_version(release, "3.0.2"); + fwupd_release_add_metadata_item(release, "FwupdVersion", VERSION); + ret = fu_history_add_device(history, device, release, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_object_unref(release); + + /* ensure database was created */ + g_assert_true(g_file_test(filename, G_FILE_TEST_EXISTS)); + + g_object_unref(device); + + /* get device */ + device = fu_history_get_device_by_id(history, + "2ba16d10df45823dd4494ff10a0bfccfef512c9d", + &error); + g_assert_no_error(error); + g_assert_nonnull(device); + g_assert_cmpstr(fu_device_get_id(device), ==, "2ba16d10df45823dd4494ff10a0bfccfef512c9d"); + g_assert_cmpstr(fu_device_get_name(device), ==, "ColorHug"); + g_assert_cmpstr(fu_device_get_version(device), ==, "3.0.1"); + g_assert_cmpint(fu_device_get_update_state(device), ==, FWUPD_UPDATE_STATE_FAILED); + g_assert_cmpstr(fu_device_get_update_error(device), ==, "word"); + g_assert_cmpstr(fu_device_get_guid_default(device), + ==, + "827edddd-9bb6-5632-889f-2c01255503da"); + g_assert_cmpint(fu_device_get_flags(device), + ==, + FWUPD_DEVICE_FLAG_INTERNAL | FWUPD_DEVICE_FLAG_HISTORICAL); + g_assert_cmpint(fu_device_get_created(device), ==, 123); + g_assert_cmpint(fu_device_get_modified(device), ==, 456); + release = fu_device_get_release_default(device); + g_assert_nonnull(release); + g_assert_cmpstr(fwupd_release_get_version(release), ==, "3.0.2"); + g_assert_cmpstr(fwupd_release_get_filename(release), ==, "/var/lib/dave.cap"); + g_assert_cmpstr(fwupd_release_get_metadata_item(release, "FwupdVersion"), ==, VERSION); + checksums = fwupd_release_get_checksums(release); + g_assert_nonnull(checksums); + g_assert_cmpint(checksums->len, ==, 1); + g_assert_cmpstr(fwupd_checksum_get_by_kind(checksums, G_CHECKSUM_SHA1), ==, "abcdef"); + ret = fu_history_add_device(history, device, release, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* get device that does not exist */ + device_found = fu_history_get_device_by_id(history, "XXXXXXXXXXXXX", &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(device_found); + g_clear_error(&error); + + /* get device that does exist */ + device_found = fu_history_get_device_by_id(history, + "2ba16d10df45823dd4494ff10a0bfccfef512c9d", + &error); + g_assert_no_error(error); + g_assert_nonnull(device_found); + g_object_unref(device_found); + + /* remove device */ + ret = fu_history_remove_device(history, device, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_object_unref(device); + + /* get device that does not exist */ + device_found = fu_history_get_device_by_id(history, + "2ba16d10df45823dd4494ff10a0bfccfef512c9d", + &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(device_found); + g_clear_error(&error); + + /* approved firmware */ + ret = fu_history_clear_approved_firmware(history, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_history_add_approved_firmware(history, "foo", &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_history_add_approved_firmware(history, "bar", &error); + g_assert_no_error(error); + g_assert_true(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 GBytes * +_build_cab(GCabCompression compression, ...) +{ + gboolean ret; + va_list args; + g_autoptr(GCabCabinet) cabinet = NULL; + g_autoptr(GCabFolder) cabfolder = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GOutputStream) op = NULL; + + /* create a new archive */ + cabinet = gcab_cabinet_new(); + cabfolder = gcab_folder_new(compression); + ret = gcab_cabinet_add_folder(cabinet, cabfolder, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add each file */ + va_start(args, compression); + do { + const gchar *fn; + const gchar *text; + g_autoptr(GCabFile) cabfile = NULL; + g_autoptr(GBytes) blob = NULL; + + /* get filename */ + fn = va_arg(args, const gchar *); + if (fn == NULL) + break; + + /* get contents */ + text = va_arg(args, const gchar *); + if (text == NULL) + break; + g_debug("creating %s with %s", fn, text); + + /* add a GCabFile to the cabinet */ + blob = g_bytes_new_static(text, strlen(text)); + cabfile = gcab_file_new_with_bytes(fn, blob); + ret = gcab_folder_add_file(cabfolder, cabfile, FALSE, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + } while (TRUE); + va_end(args); + + /* write the archive to a blob */ + op = g_memory_output_stream_new_resizable(); + ret = gcab_cabinet_write_simple(cabinet, op, NULL, NULL, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = g_output_stream_close(op, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + return g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(op)); +} + +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(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + GError *error = NULL; + gboolean ret; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) components = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) releases = + g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + 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); + silo = fu_cabinet_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 */ + (void)g_setenv("FWUPD_PLUGIN_TEST", "composite", TRUE); + fu_engine_add_plugin(engine, self->plugin); + + ret = fu_plugin_runner_startup(self->plugin, progress, &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(FU_PLUGIN(self->plugin), + "device-added", + G_CALLBACK(_plugin_composite_device_added_cb), + devices); + + ret = fu_plugin_runner_coldplug(self->plugin, progress, &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(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_local = NULL; + + /* is this component valid for the device */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + if (!fu_release_load(release, + component, + NULL, + FWUPD_INSTALL_FLAG_NONE, + &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(releases, g_steal_pointer(&release)); + } + } + g_assert_cmpint(releases->len, ==, 3); + + /* install the cab */ + ret = fu_engine_install_releases(engine, + request, + releases, + blob, + progress, + 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_security_attrs_func(gconstpointer user_data) +{ + FwupdSecurityAttr *attr_tmp; + g_autoptr(FuSecurityAttrs) attrs1 = fu_security_attrs_new(); + g_autoptr(FuSecurityAttrs) attrs2 = fu_security_attrs_new(); + g_autoptr(FwupdSecurityAttr) attr1 = fwupd_security_attr_new("org.fwupd.hsi.foo"); + g_autoptr(FwupdSecurityAttr) attr2 = fwupd_security_attr_new("org.fwupd.hsi.bar"); + g_autoptr(FwupdSecurityAttr) attr3 = fwupd_security_attr_new("org.fwupd.hsi.baz"); + g_autoptr(FwupdSecurityAttr) attr4 = fwupd_security_attr_new("org.fwupd.hsi.baz"); + g_autoptr(GPtrArray) results = NULL; + + /* attrs1 has foo and baz(enabled) */ + fwupd_security_attr_set_plugin(attr1, "foo"); + fwupd_security_attr_set_created(attr1, 0); + fwupd_security_attr_set_result(attr1, FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED); + fu_security_attrs_append(attrs1, attr1); + fwupd_security_attr_set_plugin(attr3, "baz"); + fwupd_security_attr_set_created(attr3, 0); + fwupd_security_attr_set_result(attr3, FWUPD_SECURITY_ATTR_RESULT_ENABLED); + fu_security_attrs_append(attrs1, attr3); + + /* attrs2 has bar and baz(~enabled) */ + fwupd_security_attr_set_plugin(attr2, "bar"); + fwupd_security_attr_set_created(attr2, 0); + fwupd_security_attr_set_result(attr2, FWUPD_SECURITY_ATTR_RESULT_LOCKED); + fu_security_attrs_append(attrs2, attr2); + fwupd_security_attr_set_plugin(attr4, "baz"); + fwupd_security_attr_set_created(attr4, 0); + fwupd_security_attr_set_result(attr4, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + fu_security_attrs_append(attrs2, attr4); + + results = fu_security_attrs_compare(attrs1, attrs2); + g_assert_cmpint(results->len, ==, 3); + attr_tmp = g_ptr_array_index(results, 0); + g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr_tmp), ==, "org.fwupd.hsi.bar"); + g_assert_cmpint(fwupd_security_attr_get_result_fallback(attr_tmp), + ==, + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN); + g_assert_cmpint(fwupd_security_attr_get_result(attr_tmp), + ==, + FWUPD_SECURITY_ATTR_RESULT_LOCKED); + attr_tmp = g_ptr_array_index(results, 1); + g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr_tmp), ==, "org.fwupd.hsi.foo"); + g_assert_cmpint(fwupd_security_attr_get_result_fallback(attr_tmp), + ==, + FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED); + g_assert_cmpint(fwupd_security_attr_get_result(attr_tmp), + ==, + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN); + attr_tmp = g_ptr_array_index(results, 2); + g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr_tmp), ==, "org.fwupd.hsi.baz"); + g_assert_cmpint(fwupd_security_attr_get_result_fallback(attr_tmp), + ==, + FWUPD_SECURITY_ATTR_RESULT_ENABLED); + g_assert_cmpint(fwupd_security_attr_get_result(attr_tmp), + ==, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); + + g_assert_true(fu_security_attrs_equal(attrs1, attrs1)); + g_assert_false(fu_security_attrs_equal(attrs1, attrs2)); + g_assert_false(fu_security_attrs_equal(attrs2, attrs1)); +} + +static void +fu_security_attr_func(gconstpointer user_data) +{ + gboolean ret; + g_autofree gchar *json1 = NULL; + g_autofree gchar *json2 = NULL; + g_autoptr(FuSecurityAttrs) attrs1 = fu_security_attrs_new(); + g_autoptr(FuSecurityAttrs) attrs2 = fu_security_attrs_new(); + g_autoptr(FwupdSecurityAttr) attr1 = fwupd_security_attr_new("org.fwupd.hsi.foo"); + g_autoptr(FwupdSecurityAttr) attr2 = fwupd_security_attr_new("org.fwupd.hsi.bar"); + g_autoptr(GError) error = NULL; + g_autoptr(JsonParser) parser = json_parser_new(); + + fwupd_security_attr_set_plugin(attr1, "foo"); + fwupd_security_attr_set_created(attr1, 0); + fwupd_security_attr_set_plugin(attr2, "bar"); + fwupd_security_attr_set_created(attr2, 0); + fu_security_attrs_append(attrs1, attr1); + fu_security_attrs_append(attrs1, attr2); + + json1 = fu_security_attrs_to_json_string(attrs1, &error); + g_assert_no_error(error); + g_assert_nonnull(json1); + ret = fu_test_compare_lines( + json1, + "{\n" + " \"SecurityAttributes\" : [\n" + " {\n" + " \"AppstreamId\" : \"org.fwupd.hsi.foo\",\n" + " \"HsiLevel\" : 0,\n" + " \"Plugin\" : \"foo\",\n" + " \"Uri\" : " + "\"https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.foo\"\n" + " },\n" + " {\n" + " \"AppstreamId\" : \"org.fwupd.hsi.bar\",\n" + " \"HsiLevel\" : 0,\n" + " \"Plugin\" : \"bar\",\n" + " \"Uri\" : " + "\"https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.bar\"\n" + " }\n" + " ]\n" + "}", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = json_parser_load_from_data(parser, json1, -1, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_security_attrs_from_json(attrs2, json_parser_get_root(parser), &error); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_test_skip(error->message); + return; + } + g_assert_no_error(error); + g_assert_true(ret); + + json2 = fu_security_attrs_to_json_string(attrs2, &error); + g_assert_no_error(error); + g_assert_nonnull(json2); + ret = fu_test_compare_lines(json2, json1, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_memcpy_func(gconstpointer user_data) +{ + const guint8 src[] = {'a', 'b', 'c', 'd', 'e'}; + gboolean ret; + guint8 dst[4]; + g_autoptr(GError) error = NULL; + + /* copy entire buffer */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 4, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(memcmp(src, dst, 4), ==, 0); + + /* copy first char */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 1, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(dst[0], ==, 'a'); + + /* copy last char */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x4, 1, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(dst[0], ==, 'e'); + + /* copy nothing */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 0, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* write past the end of dst */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 5, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE); + g_assert_false(ret); + g_clear_error(&error); + + /* write past the end of dst with offset */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x1, src, sizeof(src), 0x0, 4, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE); + g_assert_false(ret); + g_clear_error(&error); + + /* read past past the end of dst */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 6, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ); + g_assert_false(ret); + g_clear_error(&error); + + /* read past the end of src with offset */ + ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x4, 4, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ); + g_assert_false(ret); + g_clear_error(&error); +} + +static void +fu_progressbar_func(gconstpointer user_data) +{ + g_autoptr(FuProgressbar) progressbar = fu_progressbar_new(); + + fu_progressbar_set_length_status(progressbar, 20); + fu_progressbar_set_length_percentage(progressbar, 50); + + g_print("\n"); + for (guint i = 0; i < 100; i++) { + fu_progressbar_update(progressbar, FWUPD_STATUS_DECOMPRESSING, i); + g_usleep(10000); + } + fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0); + for (guint i = 0; i < 100; i++) { + guint pc = (i > 25 && i < 75) ? 0 : i; + fu_progressbar_update(progressbar, FWUPD_STATUS_LOADING, pc); + g_usleep(10000); + } + fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0); + + for (guint i = 0; i < 5000; i++) { + fu_progressbar_update(progressbar, FWUPD_STATUS_LOADING, 0); + g_usleep(1000); + } + fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0); +} + +static gint +fu_release_compare_func_cb(gconstpointer a, gconstpointer b) +{ + FuRelease *release1 = *((FuRelease **)a); + FuRelease *release2 = *((FuRelease **)b); + return fu_release_compare(release1, release2); +} + +static void +fu_spawn_stdout_cb(const gchar *line, gpointer user_data) +{ + guint *lines = (guint *)user_data; + g_debug("got '%s'", line); + (*lines)++; +} + +static void +fu_spawn_func(void) +{ + gboolean ret; + guint lines = 0; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = NULL; + const gchar *argv[4] = {"/bin/sh", "replace", "test", NULL}; + +#ifdef _WIN32 + g_test_skip("Known failures on Windows right now, skipping spawn func test"); + return; +#endif + + fn = g_test_build_filename(G_TEST_DIST, "tests", "spawn.sh", NULL); + argv[1] = fn; + ret = fu_spawn_sync(argv, fu_spawn_stdout_cb, &lines, 0, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(lines, ==, 6); +} + +static void +fu_spawn_timeout_func(void) +{ + gboolean ret; + guint lines = 0; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = NULL; + const gchar *argv[4] = {"/bin/sh", "replace", "test", NULL}; + +#ifdef _WIN32 + g_test_skip("Known failures on Windows right now, skipping spawn timeout test"); + return; +#endif + + fn = g_test_build_filename(G_TEST_DIST, "tests", "spawn.sh", NULL); + argv[1] = fn; + ret = fu_spawn_sync(argv, fu_spawn_stdout_cb, &lines, 500, NULL, &error); + g_assert_error(error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert_false(ret); + g_assert_cmpint(lines, ==, 1); +} + +static void +fu_release_compare_func(gconstpointer user_data) +{ + FuDevice *device_tmp; + g_autoptr(GPtrArray) releases = g_ptr_array_new(); + g_autoptr(FuDevice) device1 = fu_device_new(NULL); + g_autoptr(FuDevice) device2 = fu_device_new(NULL); + g_autoptr(FuDevice) device3 = fu_device_new(NULL); + g_autoptr(FuRelease) release1 = fu_release_new(); + g_autoptr(FuRelease) release2 = fu_release_new(); + g_autoptr(FuRelease) release3 = fu_release_new(); + + fu_device_set_order(device1, 99); + fu_release_set_device(release1, device1); + g_ptr_array_add(releases, release1); + fu_device_set_order(device2, 11); + fu_release_set_device(release2, device2); + g_ptr_array_add(releases, release2); + fu_device_set_order(device3, 33); + fu_release_set_device(release3, device3); + g_ptr_array_add(releases, release3); + + /* order the install tasks */ + g_ptr_array_sort(releases, fu_release_compare_func_cb); + g_assert_cmpint(releases->len, ==, 3); + device_tmp = fu_release_get_device(g_ptr_array_index(releases, 0)); + g_assert_cmpint(fu_device_get_order(device_tmp), ==, 11); + device_tmp = fu_release_get_device(g_ptr_array_index(releases, 1)); + g_assert_cmpint(fu_device_get_order(device_tmp), ==, 33); + device_tmp = fu_release_get_device(g_ptr_array_index(releases, 2)); + g_assert_cmpint(fu_device_get_order(device_tmp), ==, 99); +} + +static void +fu_release_uri_scheme_func(void) +{ + struct { + const gchar *in; + const gchar *op; + } strs[] = {{"https://foo.bar/baz", "https"}, + {"HTTP://FOO.BAR/BAZ", "http"}, + {"ftp://", "ftp"}, + {"ftp:", "ftp"}, + {"foobarbaz", NULL}, + {"", NULL}, + {NULL, NULL}}; + for (guint i = 0; strs[i].in != NULL; i++) { + g_autofree gchar *tmp = fu_release_uri_get_scheme(strs[i].in); + g_assert_cmpstr(tmp, ==, strs[i].op); + } +} + +static void +fu_common_store_cab_func(void) +{ + GBytes *blob_tmp; + 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; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + + /* create silo */ + blob = _build_cab( + GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " ACME Firmware\n" + " \n" + " ae56e3fb-6528-5bc4-8b03-012f124075d7\n" + " \n" + " \n" + " \n" + " 5\n" + " 7c211433f02071597741e6ff5a8ea34789abbf43\n" + "

    We fixed things

    \n" + "
    \n" + "
    \n" + " \n" + " org.freedesktop.fwupd\n" + " \n" + "
    ", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + + /* verify */ + component = + xb_silo_query_first(silo, + "components/component/id[text()='com.acme.example.firmware']/..", + &error); + g_assert_no_error(error); + g_assert_nonnull(component); +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + &error); + g_assert_no_error(error); + g_assert_nonnull(query); + rel = xb_node_query_first_full(component, query, &error); +#else + rel = xb_node_query_first(component, "releases/release", &error); +#endif + g_assert_no_error(error); + g_assert_nonnull(rel); + 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::FirmwareBlob"); + g_assert_nonnull(blob_tmp); + req = xb_node_query_first(component, "requires/id", &error); + g_assert_no_error(error); + g_assert_nonnull(req); +} + +static void +fu_common_store_cab_artifact_func(void) +{ + g_autoptr(GBytes) blob1 = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GBytes) blob3 = NULL; + g_autoptr(GBytes) blob4 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* create silo (sha256, using artifacts object) */ + blob1 = _build_cab( + GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + " \n" + " firmware.dfu\n" + " 486EA46224D1BB4FB680F34F7C9AD96A8F24EC88BE73EA8E5A6C65260E9CB8A7\n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); + silo = fu_cabinet_build_silo(blob1, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + g_clear_object(&silo); + + /* create silo (sha1, using artifacts object; mixed case) */ + blob2 = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + " \n" + " firmware.dfu\n" + " 7c211433f02071597741e6ff5a8ea34789abbF43\n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); + silo = fu_cabinet_build_silo(blob2, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + g_clear_object(&silo); + + /* create silo (sha512, using artifacts object; lower case) */ + blob3 = + _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + " \n" + " firmware.dfu\n" + " " + "11853df40f4b2b919d3815f64792e58d08663767a494bcbb38c0b2389d9140bbb170281b" + "4a847be7757bde12c9cd0054ce3652d0ad3a1a0c92babb69798246ee\n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); + silo = fu_cabinet_build_silo(blob3, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + g_clear_object(&silo); + + /* create silo (legacy release object) */ + blob4 = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " " + "486EA46224D1BB4FB680F34F7C9AD96A8F24EC88BE73EA8E5A6C65260E9CB8A7\n" + " \n" + " \n" + "", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); + silo = fu_cabinet_build_silo(blob4, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); +} + +static void +fu_common_store_cab_unsigned_func(void) +{ + GBytes *blob_tmp; + 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; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + + /* create silo */ + blob = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + + /* verify */ + component = + xb_silo_query_first(silo, + "components/component/id[text()='com.acme.example.firmware']/..", + &error); + g_assert_no_error(error); + g_assert_nonnull(component); +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + &error); + g_assert_no_error(error); + g_assert_nonnull(query); + rel = xb_node_query_first_full(component, query, &error); +#else + rel = xb_node_query_first(component, "releases/release", &error); +#endif + g_assert_no_error(error); + g_assert_nonnull(rel); + 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::FirmwareBlob"); + g_assert_nonnull(blob_tmp); +} + +static void +fu_common_store_cab_sha256_func(void) +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* create silo */ + blob = _build_cab( + GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " 486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7\n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); +} + +static void +fu_common_store_cab_folder_func(void) +{ + GBytes *blob_tmp; + 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; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + + /* create silo */ + blob = _build_cab(GCAB_COMPRESSION_NONE, + "lvfs\\acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + "", + "lvfs\\firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + + /* verify */ + component = + xb_silo_query_first(silo, + "components/component/id[text()='com.acme.example.firmware']/..", + &error); + g_assert_no_error(error); + g_assert_nonnull(component); +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + &error); + g_assert_no_error(error); + g_assert_nonnull(query); + rel = xb_node_query_first_full(component, query, &error); +#else + rel = xb_node_query_first(component, "releases/release", &error); +#endif + g_assert_no_error(error); + g_assert_nonnull(rel); + g_assert_cmpstr(xb_node_get_attr(rel, "version"), ==, "1.2.3"); + blob_tmp = xb_node_get_data(rel, "fwupd::FirmwareBlob"); + g_assert_nonnull(blob_tmp); +} + +static void +fu_common_store_cab_error_no_metadata_func(void) +{ + g_autoptr(XbSilo) silo = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab(GCAB_COMPRESSION_NONE, "foo.txt", "hello", "bar.txt", "world", NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(silo); +} + +static void +fu_common_store_cab_error_wrong_size_func(void) +{ + g_autoptr(XbSilo) silo = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " 7004701\n" + " deadbeef\n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(silo); +} + +static void +fu_common_store_cab_error_missing_file_func(void) +{ + g_autoptr(XbSilo) silo = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(silo); +} + +static void +fu_common_store_cab_error_size_func(void) +{ + g_autoptr(XbSilo) silo = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 123, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(silo); +} + +static void +fu_common_store_cab_error_wrong_checksum_func(void) +{ + g_autoptr(XbSilo) silo = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab(GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " deadbeef\n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); + silo = fu_cabinet_build_silo(blob, 10240, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert_null(silo); +} + +static void +fu_engine_modify_bios_settings_func(void) +{ + gboolean ret; + const gchar *current; + FwupdBiosSetting *attr1; + FwupdBiosSetting *attr2; + FwupdBiosSetting *attr3; + FwupdBiosSetting *attr4; + g_autofree gchar *test_dir = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(); + g_autoptr(GError) error = NULL; + g_autoptr(FuBiosSettings) attrs = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autoptr(GHashTable) bios_settings = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + /* Load contrived attributes */ + test_dir = g_test_build_filename(G_TEST_DIST, "tests", "bios-attrs", NULL); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); + + ret = fu_context_reload_bios_settings(fu_engine_get_context(engine), &error); + g_assert_no_error(error); + g_assert_true(ret); + + attrs = fu_context_get_bios_settings(fu_engine_get_context(engine)); + items = fu_bios_settings_get_all(attrs); + g_assert_cmpint(items->len, ==, 4); + + /* enumeration */ + attr1 = fu_context_get_bios_setting(fu_engine_get_context(engine), + "com.fwupd-internal.Absolute"); + g_assert_nonnull(attr1); + + current = fwupd_bios_setting_get_current_value(attr1); + g_assert_nonnull(current); + + g_hash_table_insert(bios_settings, g_strdup("Absolute"), g_strdup("Disabled")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); + g_assert_false(ret); + g_clear_error(&error); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("Absolute"), g_strdup("Enabled")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("Absolute"), g_strdup("off")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("Absolute"), g_strdup("FOO")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_clear_error(&error); + + /* use BiosSettingId instead */ + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("com.fwupd-internal.Absolute"), g_strdup("on")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, + g_strdup("com.fwupd-internal.Absolute"), + g_strdup("off")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* string */ + attr2 = + fu_context_get_bios_setting(fu_engine_get_context(engine), "com.fwupd-internal.Asset"); + g_assert_nonnull(attr2); + + current = fwupd_bios_setting_get_current_value(attr2); + g_assert_nonnull(current); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("Asset"), g_strdup("0")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("Asset"), g_strdup("1")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert( + bios_settings, + g_strdup("Absolute"), + g_strdup("1234567891123456789112345678911234567891123456789112345678911111")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_clear_error(&error); + + /* integer */ + attr3 = fu_context_get_bios_setting(fu_engine_get_context(engine), + "com.fwupd-internal.CustomChargeStop"); + g_assert_nonnull(attr3); + + current = fwupd_bios_setting_get_current_value(attr3); + g_assert_nonnull(current); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("CustomChargeStop"), g_strdup("75")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_true(ret); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("CustomChargeStop"), g_strdup("110")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_clear_error(&error); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("CustomChargeStop"), g_strdup("1")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_clear_error(&error); + + /* force it to read only */ + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("CustomChargeStop"), g_strdup("70")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, TRUE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* Read Only */ + attr4 = fu_context_get_bios_setting(fu_engine_get_context(engine), + "com.fwupd-internal.pending_reboot"); + g_assert_nonnull(attr4); + + current = fwupd_bios_setting_get_current_value(attr4); + g_assert_nonnull(current); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("pending_reboot"), g_strdup("foo")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_clear_error(&error); + + g_hash_table_remove_all(bios_settings); + g_hash_table_insert(bios_settings, g_strdup("CustomChargeStop"), g_strdup("80")); + ret = fu_engine_modify_bios_settings(engine, bios_settings, FALSE, &error); + g_assert_false(ret); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_clear_error(&error); +} + +int +main(int argc, char **argv) +{ + gboolean ret; + g_autofree gchar *testdatadir = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(FuTest) self = g_new0(FuTest, 1); + + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + (void)g_setenv("FWUPD_DEVICE_LIST_VERBOSE", "1", TRUE); + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_DATADIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_LIBDIR_PKG", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSCONFDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + (void)g_setenv("CONFIGURATION_DIRECTORY", testdatadir, TRUE); + (void)g_setenv("FWUPD_OFFLINE_TRIGGER", "/tmp/fwupd-self-test/system-update", TRUE); + (void)g_setenv("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + + /* ensure empty tree */ + fu_self_test_mkroot(); + + /* do not save silo */ + self->ctx = fu_context_new(); + ret = fu_context_load_quirks(self->ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load the test plugin */ + self->plugin = fu_plugin_new_from_gtype(fu_test_plugin_get_type(), self->ctx); + + /* tests go here */ + if (g_test_slow()) { + g_test_add_data_func("/fwupd/progressbar", self, fu_progressbar_func); + } + g_test_add_data_func("/fwupd/backend{usb}", self, fu_backend_usb_func); + g_test_add_data_func("/fwupd/backend{usb-invalid}", self, fu_backend_usb_invalid_func); + g_test_add_data_func("/fwupd/plugin{module}", self, fu_plugin_module_func); + g_test_add_data_func("/fwupd/memcpy", self, fu_memcpy_func); + g_test_add_data_func("/fwupd/security-attr", self, fu_security_attr_func); + g_test_add_data_func("/fwupd/security-attrs", self, fu_security_attrs_func); + g_test_add_data_func("/fwupd/device-list", self, fu_device_list_func); + g_test_add_data_func("/fwupd/device-list{delay}", self, fu_device_list_delay_func); + g_test_add_data_func("/fwupd/device-list{no-auto-remove-children}", + self, + fu_device_list_no_auto_remove_children_func); + g_test_add_data_func("/fwupd/device-list{compatible}", + self, + fu_device_list_compatible_func); + g_test_add_data_func("/fwupd/device-list{remove-chain}", + self, + fu_device_list_remove_chain_func); + g_test_add_data_func("/fwupd/release{compare}", self, fu_release_compare_func); + g_test_add_func("/fwupd/release{uri-scheme}", fu_release_uri_scheme_func); + g_test_add_data_func("/fwupd/engine{get-details-added}", + self, + fu_engine_get_details_added_func); + g_test_add_data_func("/fwupd/engine{get-details-missing}", + self, + fu_engine_get_details_missing_func); + g_test_add_data_func("/fwupd/engine{device-unlock}", self, fu_engine_device_unlock_func); + g_test_add_data_func("/fwupd/engine{multiple-releases}", + self, + fu_engine_multiple_rels_func); + g_test_add_data_func("/fwupd/engine{history-success}", self, fu_engine_history_func); + g_test_add_data_func("/fwupd/engine{history-error}", self, fu_engine_history_error_func); + if (g_test_slow()) { + g_test_add_data_func("/fwupd/device-list{replug-auto}", + self, + fu_device_list_replug_auto_func); + } + g_test_add_data_func("/fwupd/device-list{replug-user}", + self, + fu_device_list_replug_user_func); + g_test_add_data_func("/fwupd/engine{require-hwid}", self, fu_engine_require_hwid_func); + g_test_add_data_func("/fwupd/engine{requires-reboot}", + self, + fu_engine_install_needs_reboot); + g_test_add_data_func("/fwupd/engine{history-inherit}", self, fu_engine_history_inherit); + g_test_add_data_func("/fwupd/engine{partial-hash}", self, fu_engine_partial_hash_func); + g_test_add_data_func("/fwupd/engine{downgrade}", self, fu_engine_downgrade_func); + g_test_add_data_func("/fwupd/engine{requirements-success}", + self, + fu_engine_requirements_func); + g_test_add_data_func("/fwupd/engine{requirements-soft}", + self, + fu_engine_requirements_soft_func); + g_test_add_data_func("/fwupd/engine{requirements-missing}", + self, + fu_engine_requirements_missing_func); + g_test_add_data_func("/fwupd/engine{requirements-client-fail}", + self, + fu_engine_requirements_client_fail_func); + g_test_add_data_func("/fwupd/engine{requirements-client-invalid}", + self, + fu_engine_requirements_client_invalid_func); + g_test_add_data_func("/fwupd/engine{requirements-client-pass}", + self, + fu_engine_requirements_client_pass_func); + g_test_add_data_func("/fwupd/engine{requirements-version-require}", + self, + fu_engine_requirements_version_require_func); + g_test_add_data_func("/fwupd/engine{requirements-version-lowest}", + self, + fu_engine_requirements_version_lowest_func); + g_test_add_data_func("/fwupd/engine{requirements-parent-device}", + self, + fu_engine_requirements_parent_device_func); + g_test_add_data_func("/fwupd/engine{requirements_protocol_check_func}", + self, + fu_engine_requirements_protocol_check_func); + g_test_add_data_func("/fwupd/engine{requirements-not-child}", + self, + fu_engine_requirements_child_func); + g_test_add_data_func("/fwupd/engine{requirements-not-child-fail}", + self, + fu_engine_requirements_child_fail_func); + g_test_add_data_func("/fwupd/engine{requirements-unsupported}", + self, + fu_engine_requirements_unsupported_func); + g_test_add_data_func("/fwupd/engine{requirements-device}", + self, + fu_engine_requirements_device_func); + g_test_add_data_func("/fwupd/engine{requirements-device-plain}", + self, + fu_engine_requirements_device_plain_func); + g_test_add_data_func("/fwupd/engine{requirements-version-format}", + self, + fu_engine_requirements_version_format_func); + g_test_add_data_func("/fwupd/engine{requirements-only-upgrade}", + self, + fu_engine_requirements_only_upgrade_func); + g_test_add_data_func("/fwupd/engine{device-auto-parent-id}", + self, + fu_engine_device_parent_id_func); + g_test_add_data_func("/fwupd/engine{device-auto-parent-guid}", + self, + fu_engine_device_parent_guid_func); + g_test_add_data_func("/fwupd/engine{install-duration}", + self, + fu_engine_install_duration_func); + g_test_add_data_func("/fwupd/engine{generate-md}", self, fu_engine_generate_md_func); + g_test_add_data_func("/fwupd/engine{requirements-other-device}", + self, + fu_engine_requirements_other_device_func); + g_test_add_data_func("/fwupd/engine{fu_engine_requirements_sibling_device_func}", + self, + fu_engine_requirements_sibling_device_func); + g_test_add_data_func("/fwupd/plugin{composite}", self, fu_plugin_composite_func); + g_test_add_data_func("/fwupd/history", self, fu_history_func); + g_test_add_data_func("/fwupd/history{migrate}", self, fu_history_migrate_func); + g_test_add_data_func("/fwupd/plugin-list", self, fu_plugin_list_func); + g_test_add_data_func("/fwupd/plugin-list{depsolve}", self, fu_plugin_list_depsolve_func); + g_test_add_func("/fwupd/spawn", fu_spawn_func); + g_test_add_func("/fwupd/spawn-timeou)", fu_spawn_timeout_func); + g_test_add_func("/fwupd/common{cab-success}", fu_common_store_cab_func); + g_test_add_func("/fwupd/common{cab-success-artifact}", fu_common_store_cab_artifact_func); + g_test_add_func("/fwupd/common{cab-success-unsigned}", fu_common_store_cab_unsigned_func); + g_test_add_func("/fwupd/common{cab-success-folder}", fu_common_store_cab_folder_func); + g_test_add_func("/fwupd/common{cab-success-sha256}", fu_common_store_cab_sha256_func); + g_test_add_func("/fwupd/common{cab-error-no-metadata}", + fu_common_store_cab_error_no_metadata_func); + g_test_add_func("/fwupd/common{cab-error-wrong-size}", + fu_common_store_cab_error_wrong_size_func); + g_test_add_func("/fwupd/common{cab-error-wrong-checksum}", + fu_common_store_cab_error_wrong_checksum_func); + 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/write-bios-attrs", fu_engine_modify_bios_settings_func); + return g_test_run(); +} diff --git a/fwupd-1.8.6/src/fu-spawn.c b/fwupd-1.8.6/src/fu-spawn.c new file mode 100644 index 0000000000000000000000000000000000000000..8b332bf2dd8393e5d52d354fd9465df3cc5f3c38 --- /dev/null +++ b/fwupd-1.8.6/src/fu-spawn.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuSpawn" + +#include "config.h" + +#include "fu-spawn.h" + +typedef struct { + FuSpawnOutputHandler handler_cb; + gpointer handler_user_data; + GMainLoop *loop; + GSource *source; + GInputStream *stream; + GCancellable *cancellable; + guint timeout_id; +} FuSpawnHelper; + +static void +fu_spawn_create_pollable_source(FuSpawnHelper *helper); + +static gboolean +fu_spawn_source_pollable_cb(GObject *stream, gpointer user_data) +{ + FuSpawnHelper *helper = (FuSpawnHelper *)user_data; + gchar buffer[1024]; + gssize sz; + g_auto(GStrv) split = NULL; + g_autoptr(GError) error = NULL; + + /* read from stream */ + sz = g_pollable_input_stream_read_nonblocking(G_POLLABLE_INPUT_STREAM(stream), + buffer, + sizeof(buffer) - 1, + NULL, + &error); + if (sz < 0) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { + g_warning("failed to get read from nonblocking fd: %s", error->message); + } + return G_SOURCE_REMOVE; + } + + /* no read possible */ + if (sz == 0) + g_main_loop_quit(helper->loop); + + /* emit lines */ + if (helper->handler_cb != NULL) { + buffer[sz] = '\0'; + split = g_strsplit(buffer, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + if (split[i][0] == '\0') + continue; + helper->handler_cb(split[i], helper->handler_user_data); + } + } + + /* set up the source for the next read */ + fu_spawn_create_pollable_source(helper); + return G_SOURCE_REMOVE; +} + +static void +fu_spawn_create_pollable_source(FuSpawnHelper *helper) +{ + if (helper->source != NULL) + g_source_destroy(helper->source); + helper->source = + g_pollable_input_stream_create_source(G_POLLABLE_INPUT_STREAM(helper->stream), + helper->cancellable); + g_source_attach(helper->source, NULL); + g_source_set_callback(helper->source, + (GSourceFunc)fu_spawn_source_pollable_cb, + helper, + NULL); +} + +static void +fu_spawn_helper_free(FuSpawnHelper *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); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuSpawnHelper, fu_spawn_helper_free) +#pragma clang diagnostic pop + +#ifndef _WIN32 +static gboolean +fu_spawn_timeout_cb(gpointer user_data) +{ + FuSpawnHelper *helper = (FuSpawnHelper *)user_data; + g_cancellable_cancel(helper->cancellable); + g_main_loop_quit(helper->loop); + helper->timeout_id = 0; + return G_SOURCE_REMOVE; +} + +static void +fu_spawn_cancelled_cb(GCancellable *cancellable, FuSpawnHelper *helper) +{ + /* just propagate */ + g_cancellable_cancel(helper->cancellable); +} +#endif + +/** + * fu_spawn_sync: + * @argv: the argument list to run + * @handler_cb: (scope call) (nullable): optional #FuSpawnOutputHandler + * @handler_user_data: (nullable): the user data to pass to @handler_cb + * @timeout_ms: a timeout in ms, or 0 for no limit + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Runs a subprocess and waits for it to exit. Any output on standard out or + * standard error will be forwarded to @handler_cb as whole lines. + * + * Returns: %TRUE for success + * + * Since: 0.9.7 + **/ +gboolean +fu_spawn_sync(const gchar *const *argv, + FuSpawnOutputHandler handler_cb, + gpointer handler_user_data, + guint timeout_ms, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FuSpawnHelper) helper = NULL; + g_autoptr(GSubprocess) subprocess = NULL; + g_autofree gchar *argv_str = NULL; +#ifndef _WIN32 + gulong cancellable_id = 0; +#endif + + g_return_val_if_fail(argv != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* create subprocess */ + argv_str = g_strjoinv(" ", (gchar **)argv); + g_debug("running '%s'", argv_str); + subprocess = + g_subprocess_newv(argv, + G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE, + error); + if (subprocess == NULL) + return FALSE; + +#ifndef _WIN32 + /* watch for process to exit */ + helper = g_new0(FuSpawnHelper, 1); + helper->handler_cb = handler_cb; + helper->handler_user_data = handler_user_data; + helper->loop = g_main_loop_new(NULL, FALSE); + helper->stream = g_subprocess_get_stdout_pipe(subprocess); + + /* 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_spawn_cancelled_cb), + helper, + NULL); + } + + /* allow timeout */ + if (timeout_ms > 0) { + helper->timeout_id = g_timeout_add(timeout_ms, fu_spawn_timeout_cb, helper); + } + fu_spawn_create_pollable_source(helper); + g_main_loop_run(helper->loop); + g_cancellable_disconnect(cancellable, cancellable_id); +#endif + if (!g_subprocess_wait_check(subprocess, cancellable, error)) + return FALSE; +#ifndef _WIN32 + if (g_cancellable_set_error_if_cancelled(helper->cancellable, error)) + return FALSE; +#endif + + /* success */ + return TRUE; +} diff --git a/fwupd-1.8.6/src/fu-spawn.h b/fwupd-1.8.6/src/fu-spawn.h new file mode 100644 index 0000000000000000000000000000000000000000..cb9992b553d01113ee2cc5e80c9abf4567eb674f --- /dev/null +++ b/fwupd-1.8.6/src/fu-spawn.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FuSpawnOutputHandler: + * @line: text data + * @user_data: user data + * + * The process spawn iteration callback. + */ +typedef void (*FuSpawnOutputHandler)(const gchar *line, gpointer user_data); + +gboolean +fu_spawn_sync(const gchar *const *argv, + FuSpawnOutputHandler handler_cb, + gpointer handler_user_data, + guint timeout_ms, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/fwupd-1.8.6/src/fu-systemd.c b/fwupd-1.8.6/src/fu-systemd.c new file mode 100644 index 0000000000000000000000000000000000000000..bc49ff96f7dc2862dc13dd50b1ef6d1f0d924faf --- /dev/null +++ b/fwupd-1.8.6/src/fu-systemd.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#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" +#define SYSTEMD_UNIT_INTERFACE "org.freedesktop.systemd1.Unit" + +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_UNIT_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, + "Stop", + g_variant_new("(s)", "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 --git a/fwupd-1.8.6/src/fu-systemd.h b/fwupd-1.8.6/src/fu-systemd.h new file mode 100644 index 0000000000000000000000000000000000000000..1a21e5c0173131bf320e9f72a0640a59e7f05ff7 --- /dev/null +++ b/fwupd-1.8.6/src/fu-systemd.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +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); diff --git a/fwupd-1.8.6/src/fu-tool.c b/fwupd-1.8.6/src/fu-tool.c new file mode 100644 index 0000000000000000000000000000000000000000..e686733de8fd3287ea2370270b4f08e4dbc6806f --- /dev/null +++ b/fwupd-1.8.6/src/fu-tool.c @@ -0,0 +1,4084 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include + +#include +#include +#ifdef HAVE_GIO_UNIX +#include +#endif +#include +#include +#include +#include +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-common-private.h" +#include "fwupd-device-private.h" +#include "fwupd-plugin-private.h" + +#include "fu-bios-settings-private.h" +#include "fu-cabinet.h" +#include "fu-context-private.h" +#include "fu-debug.h" +#include "fu-device-private.h" +#include "fu-engine.h" +#include "fu-history.h" +#include "fu-hwids.h" +#include "fu-plugin-private.h" +#include "fu-progressbar.h" +#include "fu-security-attr-common.h" +#include "fu-security-attrs-private.h" +#include "fu-smbios-private.h" +#include "fu-util-bios-setting.h" +#include "fu-util-common.h" + +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif + +/* custom return code */ +#define EXIT_NOTHING_TO_DO 2 + +typedef enum { + FU_UTIL_OPERATION_UNKNOWN, + FU_UTIL_OPERATION_UPDATE, + FU_UTIL_OPERATION_INSTALL, + FU_UTIL_OPERATION_READ, + FU_UTIL_OPERATION_LAST +} FuUtilOperation; + +struct FuUtilPrivate { + GCancellable *cancellable; + GMainContext *main_ctx; + GMainLoop *loop; + GOptionContext *context; + FuEngine *engine; + FuEngineRequest *request; + FuProgress *progress; + FuProgressbar *progressbar; + FwupdClient *client; + gboolean as_json; + gboolean no_reboot_check; + gboolean no_safety_check; + gboolean no_device_prompt; + gboolean prepare_blob; + gboolean cleanup_blob; + gboolean enable_json_state; + gboolean interactive; + FwupdInstallFlags flags; + gboolean show_all; + gboolean disable_ssl_strict; + gint lock_fd; + /* only valid in update and downgrade */ + FuUtilOperation current_operation; + FwupdDevice *current_device; + GPtrArray *post_requests; + FwupdDeviceFlags completion_flags; + FwupdDeviceFlags filter_include; + FwupdDeviceFlags filter_exclude; +}; + +static void +fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtilPrivate *priv) +{ + if (priv->as_json) + return; + fu_progressbar_update(priv->progressbar, + fwupd_client_get_status(priv->client), + fwupd_client_get_percentage(priv->client)); +} + +static void +fu_util_show_plugin_warnings(FuUtilPrivate *priv) +{ + FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE; + GPtrArray *plugins; + + /* get a superset so we do not show the same message more than once */ + plugins = fu_engine_get_plugins(priv->engine); + for (guint i = 0; i < plugins->len; i++) { + FwupdPlugin *plugin = g_ptr_array_index(plugins, i); + if (fwupd_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + if (!fwupd_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING)) + continue; + flags |= fwupd_plugin_get_flags(plugin); + } + + /* never show these, they're way too generic */ + flags &= ~FWUPD_PLUGIN_FLAG_DISABLED; + flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE; + flags &= ~FWUPD_PLUGIN_FLAG_REQUIRE_HWID; + + /* print */ + for (guint i = 0; i < 64; i++) { + FwupdPluginFlags flag = (guint64)1 << i; + const gchar *tmp; + g_autofree gchar *fmt = NULL; + g_autofree gchar *url = NULL; + g_autoptr(GString) str = g_string_new(NULL); + if ((flags & flag) == 0) + continue; + tmp = fu_util_plugin_flag_to_string((guint64)1 << i); + if (tmp == NULL) + continue; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_string_append_printf(str, "%s %s\n", fmt, tmp); + + url = g_strdup_printf("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s", + fwupd_plugin_flag_to_string(flag)); + g_string_append(str, " "); + /* TRANSLATORS: %s is a link to a website */ + g_string_append_printf(str, _("See %s for more information."), url); + g_string_append(str, "\n"); + g_printerr("%s", str->str); + } +} + +static gboolean +fu_util_lock(FuUtilPrivate *priv, GError **error) +{ +#ifdef HAVE_WRLCK + struct flock lockp = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + g_autofree gchar *lockfn = NULL; + gboolean use_user = FALSE; + +#ifdef HAVE_GETUID + if (getuid() != 0 || geteuid() != 0) + use_user = TRUE; +#endif + + /* open file */ + if (use_user) { + lockfn = fu_util_get_user_cache_path("fwupdtool"); + } else { + g_autofree gchar *lockdir = fu_path_from_kind(FU_PATH_KIND_LOCKDIR); + lockfn = g_build_filename(lockdir, "fwupdtool", NULL); + } + if (!fu_path_mkdir_parent(lockfn, error)) + return FALSE; + priv->lock_fd = g_open(lockfn, O_RDWR | O_CREAT, S_IRWXU); + if (priv->lock_fd < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to open %s", + lockfn); + return FALSE; + } + + /* write lock */ +#ifdef HAVE_OFD + if (fcntl(priv->lock_fd, F_OFD_SETLK, &lockp) < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "another instance has locked %s", + lockfn); + return FALSE; + } +#else + if (fcntl(priv->lock_fd, F_SETLK, &lockp) < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "another instance has locked %s", + lockfn); + return FALSE; + } +#endif + + /* success */ + g_debug("locked %s", lockfn); +#endif + return TRUE; +} + +static gboolean +fu_util_start_engine(FuUtilPrivate *priv, + FuEngineLoadFlags flags, + FuProgress *progress, + GError **error) +{ + if (!fu_util_lock(priv, error)) { + /* TRANSLATORS: another fwupdtool instance is already running */ + g_prefix_error(error, "%s: ", _("Failed to lock")); + return FALSE; + } +#ifdef HAVE_SYSTEMD + if (getuid() != 0 || geteuid() != 0) { + g_debug("not attempting to stop daemon when running as user"); + } else { + g_autoptr(GError) error_local = NULL; + if (!fu_systemd_unit_stop(fu_util_get_systemd_unit(), &error_local)) + g_debug("Failed to stop daemon: %s", error_local->message); + } +#endif + flags |= FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES; + flags |= FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS; + if (!fu_engine_load(priv->engine, flags, progress, error)) + return FALSE; + fu_util_show_plugin_warnings(priv); + fu_util_show_unsupported_warn(); + + /* copy properties from engine to client */ + g_object_set(priv->client, + "host-vendor", + fu_engine_get_host_vendor(priv->engine), + "host-product", + fu_engine_get_host_product(priv->engine), + "battery-level", + fu_context_get_battery_level(fu_engine_get_context(priv->engine)), + "battery-threshold", + fu_context_get_battery_threshold(fu_engine_get_context(priv->engine)), + NULL); + + /* success */ + return TRUE; +} + +static void +fu_util_maybe_prefix_sandbox_error(const gchar *value, GError **error) +{ + g_autofree gchar *path = g_path_get_dirname(value); + if (!g_file_test(path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + g_prefix_error(error, + "Unable to access %s. You may need to copy %s to %s: ", + path, + value, + g_getenv("HOME")); + } +} + +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 gboolean +fu_util_smbios_dump(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *tmp = NULL; + g_autoptr(FuSmbios) smbios = NULL; + if (g_strv_length(values) < 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + smbios = fu_smbios_new(); + if (!fu_smbios_setup_from_file(smbios, values[0], error)) + return FALSE; + tmp = fu_firmware_to_string(FU_FIRMWARE(smbios)); + g_print("%s\n", tmp); + return TRUE; +} + +#ifdef HAVE_GIO_UNIX +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; +} +#endif + +static void +fu_util_setup_signal_handlers(FuUtilPrivate *priv) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GSource) source = g_unix_signal_source_new(SIGINT); + g_source_set_callback(source, fu_util_sigint_cb, priv, NULL); + g_source_attach(g_steal_pointer(&source), priv->main_ctx); +#endif +} + +static void +fu_util_private_free(FuUtilPrivate *priv) +{ + if (priv->current_device != NULL) + g_object_unref(priv->current_device); + if (priv->engine != NULL) + g_object_unref(priv->engine); + if (priv->request != NULL) + g_object_unref(priv->request); + if (priv->client != NULL) + g_object_unref(priv->client); + if (priv->main_ctx != NULL) + g_main_context_unref(priv->main_ctx); + if (priv->loop != NULL) + g_main_loop_unref(priv->loop); + if (priv->cancellable != NULL) + g_object_unref(priv->cancellable); + if (priv->progressbar != NULL) + g_object_unref(priv->progressbar); + if (priv->progress != NULL) + g_object_unref(priv->progress); + if (priv->context != NULL) + g_option_context_free(priv->context); + if (priv->lock_fd != 0) + g_close(priv->lock_fd, NULL); + g_ptr_array_unref(priv->post_requests); + g_free(priv); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +#pragma clang diagnostic pop + +static void +fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuUtilPrivate *priv) +{ + /* action has not been assigned yet */ + if (priv->current_operation == FU_UTIL_OPERATION_UNKNOWN) + return; + + /* nothing sensible to show */ + if (fwupd_request_get_message(request) == NULL) + return; + + /* show this now */ + if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_IMMEDIATE) { + g_autofree gchar *fmt = NULL; + g_autofree gchar *tmp = NULL; + + /* TRANSLATORS: the user needs to do something, e.g. remove the device */ + fmt = fu_util_term_format(_("Action Required:"), FU_UTIL_TERM_COLOR_RED); + tmp = g_strdup_printf("%s %s", fmt, fwupd_request_get_message(request)); + fu_progressbar_set_title(priv->progressbar, tmp); + } + + /* save for later */ + if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_POST) + g_ptr_array_add(priv->post_requests, g_object_ref(request)); +} + +static void +fu_main_engine_device_added_cb(FuEngine *engine, FuDevice *device, FuUtilPrivate *priv) +{ + g_autofree gchar *tmp = fu_device_to_string(device); + g_debug("ADDED:\n%s", tmp); +} + +static void +fu_main_engine_device_removed_cb(FuEngine *engine, FuDevice *device, FuUtilPrivate *priv) +{ + g_autofree gchar *tmp = fu_device_to_string(device); + g_debug("REMOVED:\n%s", tmp); +} + +static void +fu_main_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuUtilPrivate *priv) +{ + if (priv->as_json) + return; + fu_progressbar_update(priv->progressbar, status, 0); +} + +static void +fu_util_progress_percentage_changed_cb(FuProgress *progress, guint percentage, FuUtilPrivate *priv) +{ + if (priv->as_json) + return; + fu_progressbar_update(priv->progressbar, fu_progress_get_status(progress), percentage); +} + +static void +fu_util_progress_status_changed_cb(FuProgress *progress, FwupdStatus status, FuUtilPrivate *priv) +{ + if (priv->as_json) + return; + fu_progressbar_update(priv->progressbar, status, fu_progress_get_percentage(progress)); +} + +static gboolean +fu_util_watch(FuUtilPrivate *priv, gchar **values, GError **error) +{ + if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, priv->progress, error)) + return FALSE; + g_main_loop_run(priv->loop); + return TRUE; +} + +static gint +fu_util_plugin_name_sort_cb(FuPlugin **item1, FuPlugin **item2) +{ + return fu_plugin_name_compare(*item1, *item2); +} + +static gboolean +fu_util_get_plugins_as_json(FuUtilPrivate *priv, GPtrArray *plugins, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + + json_builder_set_member_name(builder, "Plugins"); + json_builder_begin_array(builder); + for (guint i = 0; i < plugins->len; i++) { + FwupdPlugin *plugin = g_ptr_array_index(plugins, i); + json_builder_begin_object(builder); + fwupd_plugin_to_json(plugin, builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_plugins(FuUtilPrivate *priv, gchar **values, GError **error) +{ + GPtrArray *plugins; + + /* load engine */ + if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_HWINFO, priv->progress, error)) + return FALSE; + + /* print */ + plugins = fu_engine_get_plugins(priv->engine); + g_ptr_array_sort(plugins, (GCompareFunc)fu_util_plugin_name_sort_cb); + if (priv->as_json) + return fu_util_get_plugins_as_json(priv, plugins, error); + + /* print */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + g_autofree gchar *str = fu_util_plugin_to_string(FWUPD_PLUGIN(plugin), 0); + g_print("%s\n", str); + } + if (plugins->len == 0) { + /* TRANSLATORS: nothing found */ + g_print("%s\n", _("No plugins found")); + } + + return TRUE; +} + +static gboolean +fu_util_filter_device(FuUtilPrivate *priv, FwupdDevice *dev) +{ + if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) { + if (!fwupd_device_has_flag(dev, priv->filter_include)) + return FALSE; + } + if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) { + if (fwupd_device_has_flag(dev, priv->filter_exclude)) + return FALSE; + } + return TRUE; +} + +static FuDevice * +fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error) +{ + FuDevice *dev; + guint idx; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_filtered = NULL; + + /* get devices from daemon */ + if (devices_opt != NULL) { + devices = g_ptr_array_ref(devices_opt); + } else { + devices = fu_engine_get_devices(priv->engine, error); + if (devices == NULL) + return NULL; + } + fwupd_device_array_ensure_parents(devices); + + /* filter results */ + devices_filtered = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < devices->len; i++) { + dev = g_ptr_array_index(devices, i); + if (!fu_util_filter_device(priv, FWUPD_DEVICE(dev))) + continue; + g_ptr_array_add(devices_filtered, g_object_ref(dev)); + } + + /* nothing */ + if (devices_filtered->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No supported devices"); + return NULL; + } + + /* exactly one */ + if (devices_filtered->len == 1) { + dev = g_ptr_array_index(devices_filtered, 0); + if (!priv->as_json) { + /* TRANSLATORS: device has been chosen by the daemon for the user */ + g_print("%s: %s\n", _("Selected device"), fu_device_get_name(dev)); + } + return g_object_ref(dev); + } + + /* no questions */ + if (priv->no_device_prompt) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "can't prompt for devices"); + return NULL; + } + + /* TRANSLATORS: get interactive prompt */ + g_print("%s\n", _("Choose a device:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < devices_filtered->len; i++) { + dev = g_ptr_array_index(devices_filtered, i); + g_print("%u.\t%s (%s)\n", i + 1, fu_device_get_id(dev), fu_device_get_name(dev)); + } + idx = fu_util_prompt_for_number(devices_filtered->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return NULL; + } + dev = g_ptr_array_index(devices_filtered, idx - 1); + return g_object_ref(dev); +} + +static FuDevice * +fu_util_get_device(FuUtilPrivate *priv, const gchar *id, GError **error) +{ + if (fwupd_guid_is_valid(id)) { + g_autoptr(GPtrArray) devices = NULL; + devices = fu_engine_get_devices_by_guid(priv->engine, id, error); + if (devices == NULL) + return NULL; + return fu_util_prompt_for_device(priv, devices, error); + } + + /* did this look like a GUID? */ + for (guint i = 0; id[i] != '\0'; i++) { + if (id[i] == '-') { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return NULL; + } + } + return fu_engine_get_device(priv->engine, id, error); +} + +static gboolean +fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GNode) root = g_node_new(NULL); + g_autoptr(GPtrArray) devices_no_support = g_ptr_array_new(); + g_autoptr(GPtrArray) devices_no_upgrades = g_ptr_array_new(); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* parse arguments */ + if (g_strv_length(values) == 0) { + devices = fu_engine_get_devices(priv->engine, error); + if (devices == NULL) + return FALSE; + } else if (g_strv_length(values) == 1) { + FuDevice *device; + device = fu_util_get_device(priv, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_ptr_array_add(devices, device); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + fwupd_device_array_ensure_parents(devices); + g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + GNode *child; + + /* not going to have results, so save a engine round-trip */ + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) + continue; + if (!fu_util_filter_device(priv, dev)) + continue; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { + g_ptr_array_add(devices_no_support, dev); + continue; + } + + /* get the releases for this device and filter for validity */ + rels = fu_engine_get_upgrades(priv->engine, + priv->request, + fwupd_device_get_id(dev), + &error_local); + if (rels == NULL) { + g_ptr_array_add(devices_no_upgrades, dev); + /* discard the actual reason from user, but leave for debugging */ + g_debug("%s", error_local->message); + continue; + } + child = g_node_append_data(root, dev); + + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index(rels, j); + g_node_append_data(child, g_object_ref(rel)); + } + } + + /* devices that have no updates available for whatever reason */ + if (devices_no_support->len > 0) { + /* TRANSLATORS: message letting the user know no device upgrade + * available due to missing on LVFS */ + g_printerr("%s\n", _("Devices with no available firmware updates: ")); + for (guint i = 0; i < devices_no_support->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_no_support, i); + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + } + } + if (devices_no_upgrades->len > 0) { + /* TRANSLATORS: message letting the user know no device upgrade available */ + g_printerr("%s\n", _("Devices with the latest available firmware version:")); + for (guint i = 0; i < devices_no_upgrades->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_no_upgrades, i); + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + } + } + + /* updates */ + if (g_node_n_nodes(root, G_TRAVERSE_ALL) <= 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + _("No updates available for remaining devices")); + return FALSE; + } + + fu_util_print_tree(priv->client, root); + return TRUE; +} + +static gboolean +fu_util_get_details(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) array = NULL; + g_autoptr(GNode) root = g_node_new(NULL); + gint fd; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* check args */ + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* implied, important for get-details on a device not in your system */ + priv->show_all = TRUE; + + /* open file */ + fd = open(values[0], O_RDONLY); + if (fd < 0) { + fu_util_maybe_prefix_sandbox_error(values[0], error); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s", + values[0]); + return FALSE; + } + array = fu_engine_get_details(priv->engine, priv->request, fd, error); + close(fd); + + if (array == NULL) + return FALSE; + for (guint i = 0; i < array->len; i++) { + FwupdDevice *dev = g_ptr_array_index(array, i); + FwupdRelease *rel; + GNode *child; + if (!fu_util_filter_device(priv, dev)) + continue; + child = g_node_append_data(root, dev); + rel = fwupd_device_get_release_default(dev); + if (rel != NULL) + g_node_append_data(child, rel); + } + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +static gboolean +fu_util_get_device_flags(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GString) str = g_string_new(NULL); + + for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; + i <<= 1) { + const gchar *tmp = fwupd_device_flag_to_string(i); + if (tmp == NULL) + break; + if (i != FWUPD_DEVICE_FLAG_INTERNAL) + g_string_append(str, " "); + g_string_append(str, tmp); + g_string_append(str, " ~"); + g_string_append(str, tmp); + } + g_print("%s\n", str->str); + + return TRUE; +} + +static void +fu_util_build_device_tree(FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev) +{ + for (guint i = 0; i < devs->len; i++) { + FuDevice *dev_tmp = g_ptr_array_index(devs, i); + if (!fu_util_filter_device(priv, FWUPD_DEVICE(dev_tmp))) + continue; + if (!priv->show_all && !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); + fu_util_build_device_tree(priv, child, devs, dev_tmp); + } + } +} + +static gboolean +fu_util_get_devices(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GNode) root = g_node_new(NULL); + g_autoptr(GPtrArray) devs = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* get devices and build tree */ + devs = fu_engine_get_devices(priv->engine, error); + if (devs == NULL) + return FALSE; + if (devs->len > 0) { + fwupd_device_array_ensure_parents(devs); + fu_util_build_device_tree(priv, root, devs, NULL); + } + + /* print */ + if (g_node_n_children(root) == 0) { + /* TRANSLATORS: nothing attached that can be upgraded */ + g_print("%s\n", _("No hardware detected with firmware update capability")); + return TRUE; + } + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +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 || + g_strcmp0(fwupd_device_get_composite_id(priv->current_device), + fwupd_device_get_composite_id(device)) == 0) { + g_set_object(&priv->current_device, device); + return; + } + + /* ignore indirect devices that might have changed */ + if (fwupd_device_get_status(device) == FWUPD_STATUS_IDLE || + fwupd_device_get_status(device) == FWUPD_STATUS_UNKNOWN) { + g_debug("ignoring %s with status %s", + fwupd_device_get_name(device), + fwupd_status_to_string(fwupd_device_get_status(device))); + 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 if (priv->current_operation == FU_UTIL_OPERATION_READ) { + /* TRANSLATORS: %1 is a device name */ + str = g_strdup_printf(_("Reading from %s…"), fwupd_device_get_name(device)); + fu_progressbar_set_title(priv->progressbar, str); + } else { + g_warning("no FuUtilOperation set"); + } + g_set_object(&priv->current_device, device); +} + +static void +fu_util_display_current_message(FuUtilPrivate *priv) +{ + /* print all POST requests */ + for (guint i = 0; i < priv->post_requests->len; i++) { + FwupdRequest *request = g_ptr_array_index(priv->post_requests, i); + g_print("%s\n", fu_util_request_get_message(request)); + } +} + +static gboolean +fu_util_install_blob(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(GBytes) blob_fw = NULL; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 2, "parse"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 30, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_WRITE, 68, NULL); + + /* invalid args */ + if (g_strv_length(values) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* parse blob */ + blob_fw = fu_bytes_get_contents(values[0], error); + if (blob_fw == NULL) { + fu_util_maybe_prefix_sandbox_error(values[0], error); + return FALSE; + } + fu_progress_step_done(priv->progress); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* get device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + if (g_strv_length(values) >= 2) { + device = fu_util_get_device(priv, values[1], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } + + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + + /* write bare firmware */ + 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, + fu_progress_get_child(priv->progress), + priv->flags, + fu_engine_request_get_feature_flags(priv->request), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* cleanup */ + 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_util_get_device(priv, 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 gboolean +fu_util_firmware_sign(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuCabinet) cabinet = fu_cabinet_new(); + g_autoptr(GBytes) archive_blob_new = NULL; + g_autoptr(GBytes) archive_blob_old = NULL; + g_autoptr(GBytes) cert = NULL; + g_autoptr(GBytes) privkey = NULL; + + /* invalid args */ + if (g_strv_length(values) != 3) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected firmware.cab " + "certificate.pem privatekey.pfx"); + return FALSE; + } + + /* load arguments */ + archive_blob_old = fu_bytes_get_contents(values[0], error); + if (archive_blob_old == NULL) + return FALSE; + cert = fu_bytes_get_contents(values[1], error); + if (cert == NULL) + return FALSE; + privkey = fu_bytes_get_contents(values[2], error); + if (privkey == NULL) + return FALSE; + + /* load, sign, export */ + if (!fu_cabinet_parse(cabinet, archive_blob_old, FU_CABINET_PARSE_FLAG_NONE, error)) + return FALSE; + if (!fu_cabinet_sign(cabinet, cert, privkey, FU_CABINET_SIGN_FLAG_NONE, error)) + return FALSE; + archive_blob_new = fu_cabinet_export(cabinet, FU_CABINET_EXPORT_FLAG_NONE, error); + if (archive_blob_new == NULL) + return FALSE; + return fu_bytes_set_contents(values[0], archive_blob_new, error); +} + +static gboolean +fu_util_firmware_dump(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(GBytes) blob_empty = g_bytes_new(NULL, 0); + g_autoptr(GBytes) blob_fw = NULL; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_READ, 5, NULL); + + /* invalid args */ + if (g_strv_length(values) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* file already exists */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + g_file_test(values[0], G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Filename already exists"); + return FALSE; + } + + /* write a zero length file to ensure the destination is writable to + * avoid failing at the end of a potentially lengthy operation */ + if (!fu_bytes_set_contents(values[0], blob_empty, error)) + return FALSE; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* get device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; + if (g_strv_length(values) >= 2) { + device = fu_util_get_device(priv, values[1], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } + priv->current_operation = FU_UTIL_OPERATION_READ; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + + /* dump firmware */ + blob_fw = fu_engine_firmware_dump(priv->engine, + device, + fu_progress_get_child(priv->progress), + priv->flags, + error); + if (blob_fw == NULL) + return FALSE; + fu_progress_step_done(priv->progress); + return fu_bytes_set_contents(values[0], blob_fw, error); +} + +static gboolean +fu_util_firmware_read(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuFirmware) fw = NULL; + g_autoptr(GBytes) blob_empty = g_bytes_new(NULL, 0); + g_autoptr(GBytes) blob_fw = NULL; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_READ, 5, NULL); + + /* invalid args */ + if (g_strv_length(values) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* file already exists */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + g_file_test(values[0], G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Filename already exists"); + return FALSE; + } + + /* write a zero length file to ensure the destination is writable to + * avoid failing at the end of a potentially lengthy operation */ + if (!fu_bytes_set_contents(values[0], blob_empty, error)) + return FALSE; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* get device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; + if (g_strv_length(values) >= 2) { + device = fu_util_get_device(priv, values[1], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } + priv->current_operation = FU_UTIL_OPERATION_READ; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + + /* read firmware into the container format */ + fw = fu_engine_firmware_read(priv->engine, + device, + fu_progress_get_child(priv->progress), + priv->flags, + error); + if (fw == NULL) + return FALSE; + blob_fw = fu_firmware_write(fw, error); + if (blob_fw == NULL) + return FALSE; + fu_progress_step_done(priv->progress); + return fu_bytes_set_contents(values[0], blob_fw, error); +} + +static gint +fu_util_release_sort_cb(gconstpointer a, gconstpointer b) +{ + FuRelease *release1 = *((FuRelease **)a); + FuRelease *release2 = *((FuRelease **)b); + return fu_release_compare(release1, release2); +} + +static gchar * +fu_util_download_if_required(FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) +{ + g_autofree gchar *filename = NULL; + g_autoptr(GFile) file = NULL; + + /* a local file */ + if (g_file_test(perhapsfn, G_FILE_TEST_EXISTS)) + return g_strdup(perhapsfn); + if (!fu_util_is_url(perhapsfn)) + return g_strdup(perhapsfn); + + /* download the firmware to a cachedir */ + filename = fu_util_get_user_cache_path(perhapsfn); + if (!fu_path_mkdir_parent(filename, error)) + return NULL; + file = g_file_new_for_path(filename); + if (!fwupd_client_download_file(priv->client, + perhapsfn, + file, + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + priv->cancellable, + error)) + return NULL; + return g_steal_pointer(&filename); +} + +static gboolean +fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) +{ + 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) releases = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 50, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* handle both forms */ + if (g_strv_length(values) == 1) { + devices_possible = fu_engine_get_devices(priv->engine, error); + if (devices_possible == NULL) + return FALSE; + fwupd_device_array_ensure_parents(devices_possible); + } else if (g_strv_length(values) == 2) { + FuDevice *device = fu_util_get_device(priv, values[1], error); + if (device == NULL) + return FALSE; + if (!priv->no_safety_check) { + if (!fu_util_prompt_warning_fde(FWUPD_DEVICE(device), error)) + return FALSE; + } + devices_possible = + fu_engine_get_devices_by_composite_id(priv->engine, + fu_device_get_composite_id(device), + error); + if (devices_possible == NULL) + return FALSE; + + g_ptr_array_add(devices_possible, device); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* download if required */ + filename = fu_util_download_if_required(priv, values[0], error); + if (filename == NULL) + return FALSE; + + /* parse silo */ + blob_cab = fu_bytes_get_contents(filename, error); + if (blob_cab == NULL) { + fu_util_maybe_prefix_sandbox_error(filename, error); + return FALSE; + } + 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; + + /* for each component in the silo */ + errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + 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++) { + FuDevice *device = g_ptr_array_index(devices_possible, j); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_local = NULL; + + /* is this component valid for the device */ + fu_release_set_device(release, device); + fu_release_set_request(release, priv->request); + if (!fu_release_load(release, component, NULL, priv->flags, &error_local)) { + g_debug("loading release failed on %s:%s failed: %s", + fu_device_get_id(device), + xb_node_query_text(component, "id", NULL), + error_local->message); + g_ptr_array_add(errors, g_steal_pointer(&error_local)); + continue; + } + if (!fu_engine_check_requirements(priv->engine, + release, + priv->flags | FWUPD_INSTALL_FLAG_FORCE, + &error_local)) { + g_debug("first pass requirement on %s:%s failed: %s", + fu_device_get_id(device), + xb_node_query_text(component, "id", NULL), + error_local->message); + g_ptr_array_add(errors, g_steal_pointer(&error_local)); + continue; + } + + /* make a second pass using possibly updated version format now */ + fu_engine_md_refresh_device_from_component(priv->engine, device, component); + if (!fu_engine_check_requirements(priv->engine, + release, + priv->flags, + &error_local)) { + g_debug("second pass requirement on %s:%s failed: %s", + fu_device_get_id(device), + 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(releases, g_steal_pointer(&release)); + } + } + + /* order the install tasks by the device priority */ + g_ptr_array_sort(releases, fu_util_release_sort_cb); + + /* nothing suitable */ + if (releases->len == 0) { + GError *error_tmp = fu_engine_error_array_get_best(errors); + g_propagate_error(error, error_tmp); + return FALSE; + } + + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + + /* install all the tasks */ + if (!fu_engine_install_releases(priv->engine, + priv->request, + releases, + blob_cab, + fu_progress_get_child(priv->progress), + priv->flags, + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + fu_util_display_current_message(priv); + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + /* success */ + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_install_release(FuUtilPrivate *priv, FwupdRelease *rel, GError **error) +{ + FwupdRemote *remote; + GPtrArray *locations; + const gchar *remote_id; + const gchar *uri_tmp; + g_auto(GStrv) argv = NULL; + + /* get the default release only until other parts of fwupd can cope */ + locations = fwupd_release_get_locations(rel); + if (locations->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "release missing URI"); + return FALSE; + } + uri_tmp = g_ptr_array_index(locations, 0); + remote_id = fwupd_release_get_remote_id(rel); + if (remote_id == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to find remote for %s", + uri_tmp); + return FALSE; + } + + remote = fu_engine_get_remote_by_id(priv->engine, remote_id, error); + if (remote == NULL) + return FALSE; + + argv = g_new0(gchar *, 2); + /* local remotes may have the firmware already */ + if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_LOCAL && !fu_util_is_url(uri_tmp)) { + const gchar *fn_cache = fwupd_remote_get_filename_cache(remote); + g_autofree gchar *path = g_path_get_dirname(fn_cache); + argv[0] = g_build_filename(path, uri_tmp, NULL); + } else if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + argv[0] = g_strdup(uri_tmp + 7); + /* web remote, fu_util_install will download file */ + } else { + argv[0] = fwupd_remote_build_firmware_uri(remote, uri_tmp, error); + } + return fu_util_install(priv, argv, error); +} + +static gboolean +fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + gboolean no_updates_header = FALSE; + gboolean latest_header = FALSE; + + if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "--allow-older is not supported for this command"); + return FALSE; + } + + if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "--allow-reinstall is not supported for this command"); + return FALSE; + } + + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* DEVICE-ID and GUID are acceptable args to update */ + for (guint idx = 0; idx < g_strv_length(values); idx++) { + if (!fwupd_guid_is_valid(values[idx]) && !fwupd_device_id_is_valid(values[idx])) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "'%s' is not a valid GUID nor DEVICE-ID", + values[idx]); + return FALSE; + } + } + + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_signal_connect(FU_ENGINE(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; + fwupd_device_array_ensure_parents(devices); + g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel; + const gchar *device_id = fu_device_get_id(dev); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + gboolean dev_skip_byid = TRUE; + + /* only process particular DEVICE-ID or GUID if specified */ + for (guint idx = 0; idx < g_strv_length(values); idx++) { + const gchar *tmpid = values[idx]; + if (fwupd_device_has_guid(dev, tmpid) || g_strcmp0(device_id, tmpid) == 0) { + dev_skip_byid = FALSE; + break; + } + } + if (g_strv_length(values) > 0 && dev_skip_byid) + continue; + if (!fu_util_is_interesting_device(dev)) + continue; + /* only show stuff that has metadata available */ + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) + continue; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { + if (!no_updates_header) { + g_printerr("%s\n", + /* TRANSLATORS: message letting the user know no device + * upgrade available due to missing on LVFS */ + _("Devices with no available firmware updates: ")); + no_updates_header = TRUE; + } + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + continue; + } + if (!fu_util_filter_device(priv, dev)) + continue; + + rels = fu_engine_get_upgrades(priv->engine, priv->request, device_id, &error_local); + if (rels == NULL) { + if (!latest_header) { + g_printerr( + "%s\n", + /* TRANSLATORS: message letting the user know no device upgrade + * available */ + _("Devices with the latest available firmware version:")); + latest_header = TRUE; + } + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + /* discard the actual reason from user, but leave for debugging */ + g_debug("%s", error_local->message); + continue; + } + + rel = g_ptr_array_index(rels, 0); + if (!priv->no_safety_check) { + g_autofree gchar *title = + g_strdup_printf("%s %s", + fu_engine_get_host_vendor(priv->engine), + fu_engine_get_host_product(priv->engine)); + if (!fu_util_prompt_warning(dev, rel, title, error)) + return FALSE; + if (!fu_util_prompt_warning_fde(dev, error)) + return FALSE; + } + + if (!fu_util_install_release(priv, rel, &error_local)) { + g_printerr("%s\n", error_local->message); + continue; + } + fu_util_display_current_message(priv); + } + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_reinstall(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(FuDevice) dev = NULL; + + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + dev = fu_util_get_device(priv, values[0], error); + if (dev == NULL) + return FALSE; + + /* try to lookup/match release from client */ + rels = fu_engine_get_releases_for_device(priv->engine, priv->request, dev, error); + if (rels == NULL) + return FALSE; + + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); + if (fu_version_compare(fwupd_release_get_version(rel_tmp), + fu_device_get_version(dev), + fu_device_get_version_format(dev)) == 0) { + rel = g_object_ref(rel_tmp); + break; + } + } + if (rel == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unable to locate release for %s version %s", + fu_device_get_name(dev), + fu_device_get_version(dev)); + return FALSE; + } + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (!fu_util_install_release(priv, rel, error)) + 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; + } + + 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; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* get device */ + priv->filter_exclude |= FWUPD_DEVICE_FLAG_IS_BOOTLOADER; + if (g_strv_length(values) >= 1) { + device = fu_util_get_device(priv, values[0], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } + + /* run vfunc */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_device_detach_full(device, fu_progress_get_child(priv->progress), error)) + return FALSE; + fu_progress_step_done(priv->progress); + return TRUE; +} + +static gboolean +fu_util_unbind_driver(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* get device */ + if (g_strv_length(values) == 1) { + device = fu_util_get_device(priv, values[0], error); + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + } + if (device == NULL) + return FALSE; + + /* run vfunc */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + return fu_device_unbind_driver(device, error); +} + +static gboolean +fu_util_bind_driver(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* get device */ + if (g_strv_length(values) == 3) { + device = fu_util_get_device(priv, values[2], error); + if (device == NULL) + return FALSE; + } else if (g_strv_length(values) == 2) { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* run vfunc */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + return fu_device_bind_driver(device, values[0], values[1], error); +} + +static gboolean +fu_util_attach(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* get device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_IS_BOOTLOADER; + if (g_strv_length(values) >= 1) { + device = fu_util_get_device(priv, values[0], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } + + /* run vfunc */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + if (!fu_device_attach_full(device, fu_progress_get_child(priv->progress), error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_check_activation_needed(FuUtilPrivate *priv, GError **error) +{ + gboolean has_pending = FALSE; + g_autoptr(FuHistory) history = fu_history_new(); + g_autoptr(GPtrArray) devices = fu_history_get_devices(history, error); + if (devices == NULL) + return FALSE; + + /* only start up the plugins needed */ + 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 devices to activate"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error) +{ + gboolean has_pending = FALSE; + g_autoptr(GPtrArray) devices = NULL; + + /* check the history database before starting the daemon */ + if (!fu_util_check_activation_needed(priv, error)) + return FALSE; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_COLDPLUG | + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* parse arguments */ + if (g_strv_length(values) == 0) { + devices = fu_engine_get_devices(priv->engine, error); + if (devices == NULL) + return FALSE; + } else if (g_strv_length(values) == 1) { + FuDevice *device; + device = fu_util_get_device(priv, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_ptr_array_add(devices, device); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* activate anything with _NEEDS_ACTIVATION */ + /* order by device priority */ + g_ptr_array_sort(devices, fu_util_device_order_sort_cb); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + if (!fu_util_filter_device(priv, FWUPD_DEVICE(device))) + continue; + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) + continue; + has_pending = TRUE; + /* 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), + fu_progress_get_child(priv->progress), + error)) + return FALSE; + } + fu_progress_step_done(priv->progress); + + if (!has_pending) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No devices to activate"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_export_hwids(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuHwids) hwids = fu_hwids_new(); + g_autoptr(FuSmbios) smbios = fu_smbios_new(); + g_autoptr(GKeyFile) kf = g_key_file_new(); + g_autoptr(GPtrArray) hwid_keys = NULL; + + /* check args */ + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected HWIDS-FILE"); + return FALSE; + } + + /* setup default hwids */ + if (!fu_smbios_setup(smbios, error)) + return FALSE; + if (!fu_hwids_setup(hwids, smbios, error)) + return FALSE; + + /* save all keys */ + hwid_keys = fu_hwids_get_keys(hwids); + for (guint i = 0; i < hwid_keys->len; i++) { + const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); + const gchar *value = fu_hwids_get_value(hwids, hwid_key); + g_key_file_set_string(kf, "HwIds", hwid_key, value); + } + + /* success */ + return g_key_file_save_to_file(kf, values[0], error); +} + +static gboolean +fu_util_hwids(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(FuHwids) hwids = fu_hwids_new(); + g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys(hwids); + + /* read DMI data */ + if (g_strv_length(values) == 0) { + smbios = fu_smbios_new(); + if (!fu_smbios_setup(smbios, error)) + return FALSE; + } else if (g_strv_length(values) == 1) { + /* a keyfile with overrides */ + g_autoptr(GKeyFile) kf = g_key_file_new(); + if (g_key_file_load_from_file(kf, values[0], G_KEY_FILE_NONE, NULL)) { + for (guint i = 0; i < hwid_keys->len; i++) { + const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); + g_autofree gchar *tmp = NULL; + tmp = g_key_file_get_string(kf, "HwIds", hwid_key, NULL); + fu_hwids_add_smbios_override(hwids, hwid_key, tmp); + } + /* a DMI blob */ + } else { + smbios = fu_smbios_new(); + 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; i < hwid_keys->len; i++) { + const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); + const gchar *value = fu_hwids_get_value(hwids, hwid_key); + if (value == NULL) + continue; + if (g_strcmp0(hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 || + g_strcmp0(hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) { + guint64 val = g_ascii_strtoull(value, NULL, 16); + g_print("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val); + } else { + g_print("%s: %s\n", hwid_key, value); + } + } + + /* 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_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, priv->progress, error)) + return FALSE; + sig = fu_engine_self_sign(priv->engine, + values[0], + JCAT_SIGN_FLAG_ADD_TIMESTAMP | JCAT_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) +{ + FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + g_autofree gchar *tmp = fu_util_device_to_string(priv->client, device, 0); + /* 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) +{ + FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + g_autofree gchar *tmp = fu_util_device_to_string(priv->client, device, 0); + /* 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) +{ + FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + g_autofree gchar *tmp = fu_util_device_to_string(priv->client, device, 0); + /* 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) +{ + /* get all the devices */ + if (!fwupd_client_connect(priv->client, priv->cancellable, error)) + return FALSE; + + /* watch for any hotplugged device */ + g_signal_connect(FWUPD_CLIENT(priv->client), + "changed", + G_CALLBACK(fu_util_changed_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "device-added", + G_CALLBACK(fu_util_device_added_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "device-removed", + G_CALLBACK(fu_util_device_removed_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "device-changed", + G_CALLBACK(fu_util_device_changed_cb), + priv); + g_signal_connect(G_CANCELLABLE(priv->cancellable), + "cancelled", + G_CALLBACK(fu_util_cancelled_cb), + priv); + g_main_loop_run(priv->loop); + return TRUE; +} + +static gboolean +fu_util_get_firmware_types(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) firmware_types = NULL; + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + firmware_types = fu_context_get_firmware_gtype_ids(fu_engine_get_context(priv->engine)); + for (guint i = 0; i < firmware_types->len; i++) { + const gchar *id = g_ptr_array_index(firmware_types, i); + g_print("%s\n", id); + } + if (firmware_types->len == 0) { + /* TRANSLATORS: nothing found */ + g_print("%s\n", _("No firmware IDs found")); + return TRUE; + } + + return TRUE; +} + +static gchar * +fu_util_prompt_for_firmware_type(FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GPtrArray) firmware_types = NULL; + guint idx; + firmware_types = fu_context_get_firmware_gtype_ids(fu_engine_get_context(priv->engine)); + + /* TRANSLATORS: get interactive prompt */ + g_print("%s\n", _("Choose a firmware type:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < firmware_types->len; i++) { + const gchar *id = g_ptr_array_index(firmware_types, i); + g_print("%u.\t%s\n", i + 1, id); + } + idx = fu_util_prompt_for_number(firmware_types->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return NULL; + } + + return g_strdup(g_ptr_array_index(firmware_types, idx - 1)); +} + +static gboolean +fu_util_firmware_parse(FuUtilPrivate *priv, gchar **values, GError **error) +{ + GType gtype; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autofree gchar *firmware_type = NULL; + g_autofree gchar *str = NULL; + + /* check args */ + if (g_strv_length(values) == 0 || g_strv_length(values) > 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + + if (g_strv_length(values) == 2) + firmware_type = g_strdup(values[1]); + + /* load file */ + blob = fu_bytes_get_contents(values[0], error); + if (blob == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* find the GType to use */ + if (firmware_type == NULL) + firmware_type = fu_util_prompt_for_firmware_type(priv, error); + if (firmware_type == NULL) + return FALSE; + gtype = + fu_context_get_firmware_gtype_by_id(fu_engine_get_context(priv->engine), firmware_type); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + firmware_type); + return FALSE; + } + + /* does firmware specify an internal size */ + firmware = g_object_new(gtype, NULL); + if (fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_HAS_STORED_SIZE)) { + g_autoptr(FuFirmware) firmware_linear = fu_linear_firmware_new(gtype); + g_autoptr(GPtrArray) imgs = NULL; + if (!fu_firmware_parse(firmware_linear, blob, priv->flags, error)) + return FALSE; + imgs = fu_firmware_get_images(firmware_linear); + if (imgs->len == 1) { + g_set_object(&firmware, g_ptr_array_index(imgs, 0)); + } else { + g_set_object(&firmware, firmware_linear); + } + } else { + if (!fu_firmware_parse(firmware, blob, priv->flags, error)) + return FALSE; + } + + str = fu_firmware_to_string(firmware); + g_print("%s", str); + return TRUE; +} + +static gboolean +fu_util_firmware_export(FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuFirmwareExportFlags flags = FU_FIRMWARE_EXPORT_FLAG_NONE; + GType gtype; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autofree gchar *firmware_type = NULL; + g_autofree gchar *str = NULL; + + /* check args */ + if (g_strv_length(values) == 0 || g_strv_length(values) > 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + + if (g_strv_length(values) == 2) + firmware_type = g_strdup(values[1]); + + /* load file */ + blob = fu_bytes_get_contents(values[0], error); + if (blob == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* find the GType to use */ + if (firmware_type == NULL) + firmware_type = fu_util_prompt_for_firmware_type(priv, error); + if (firmware_type == NULL) + return FALSE; + gtype = + fu_context_get_firmware_gtype_by_id(fu_engine_get_context(priv->engine), firmware_type); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + firmware_type); + return FALSE; + } + firmware = g_object_new(gtype, NULL); + if (!fu_firmware_parse(firmware, blob, priv->flags, error)) + return FALSE; + if (priv->show_all) + flags |= FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG; + str = fu_firmware_export_to_xml(firmware, flags, error); + if (str == NULL) + return FALSE; + g_print("%s", str); + return TRUE; +} + +static gboolean +fu_util_firmware_extract(FuUtilPrivate *priv, gchar **values, GError **error) +{ + GType gtype; + g_autofree gchar *firmware_type = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) images = NULL; + + /* check args */ + if (g_strv_length(values) == 0 || g_strv_length(values) > 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + if (g_strv_length(values) == 2) + firmware_type = g_strdup(values[1]); + + /* load file */ + blob = fu_bytes_get_contents(values[0], error); + if (blob == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* find the GType to use */ + if (firmware_type == NULL) + firmware_type = fu_util_prompt_for_firmware_type(priv, error); + if (firmware_type == NULL) + return FALSE; + gtype = + fu_context_get_firmware_gtype_by_id(fu_engine_get_context(priv->engine), firmware_type); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + firmware_type); + return FALSE; + } + firmware = g_object_new(gtype, NULL); + if (!fu_firmware_parse(firmware, blob, priv->flags, error)) + return FALSE; + str = fu_firmware_to_string(firmware); + g_print("%s", str); + images = fu_firmware_get_images(firmware); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autofree gchar *fn = NULL; + g_autoptr(GBytes) blob_img = NULL; + + /* get raw image without generated header, footer or crc */ + blob_img = fu_firmware_get_bytes(img, error); + if (blob_img == NULL) + return FALSE; + if (g_bytes_get_size(blob_img) == 0) + continue; + + /* use suitable filename */ + if (fu_firmware_get_filename(img) != NULL) { + fn = g_strdup(fu_firmware_get_filename(img)); + } else if (fu_firmware_get_id(img) != NULL) { + fn = g_strdup_printf("id-%s.fw", fu_firmware_get_id(img)); + } else if (fu_firmware_get_idx(img) != 0x0) { + fn = g_strdup_printf("idx-0x%x.fw", (guint)fu_firmware_get_idx(img)); + } else { + fn = g_strdup_printf("img-0x%x.fw", i); + } + /* TRANSLATORS: decompressing images from a container firmware */ + g_print("%s : %s\n", _("Writing file:"), fn); + if (!fu_bytes_set_contents(fn, blob_img, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_firmware_build(FuUtilPrivate *priv, gchar **values, GError **error) +{ + GType gtype = FU_TYPE_FIRMWARE; + const gchar *tmp; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuFirmware) firmware_dst = NULL; + g_autoptr(GBytes) blob_dst = NULL; + g_autoptr(GBytes) blob_src = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* check args */ + if (g_strv_length(values) != 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + + /* load file */ + blob_src = fu_bytes_get_contents(values[0], error); + if (blob_src == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* parse XML */ + if (!xb_builder_source_load_bytes(source, blob_src, XB_BUILDER_SOURCE_FLAG_NONE, error)) { + g_prefix_error(error, "could not parse XML: "); + 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; + + /* create FuFirmware of specific GType */ + n = xb_silo_query_first(silo, "firmware", error); + if (n == NULL) + return FALSE; + tmp = xb_node_get_attr(n, "gtype"); + if (tmp != NULL) { + gtype = g_type_from_name(tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not registered", + tmp); + return FALSE; + } + } + tmp = xb_node_get_attr(n, "id"); + if (tmp != NULL) { + gtype = + fu_context_get_firmware_gtype_by_id(fu_engine_get_context(priv->engine), tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + tmp); + return FALSE; + } + } + firmware = g_object_new(gtype, NULL); + if (!fu_firmware_build(firmware, n, error)) + return FALSE; + + /* write new file */ + blob_dst = fu_firmware_write(firmware, error); + if (blob_dst == NULL) + return FALSE; + if (!fu_bytes_set_contents(values[1], blob_dst, error)) + return FALSE; + + /* show what we wrote */ + firmware_dst = g_object_new(gtype, NULL); + if (!fu_firmware_parse(firmware_dst, blob_dst, priv->flags, error)) + return FALSE; + str = fu_firmware_to_string(firmware_dst); + g_print("%s", str); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_firmware_convert(FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuContext *ctx = fu_engine_get_context(priv->engine); + GType gtype_dst; + GType gtype_src; + g_autofree gchar *firmware_type_dst = NULL; + g_autofree gchar *firmware_type_src = NULL; + g_autofree gchar *str_dst = NULL; + g_autofree gchar *str_src = NULL; + g_autoptr(FuFirmware) firmware_dst = NULL; + g_autoptr(FuFirmware) firmware_src = NULL; + g_autoptr(GBytes) blob_dst = NULL; + g_autoptr(GBytes) blob_src = NULL; + g_autoptr(GPtrArray) images = NULL; + + /* check args */ + if (g_strv_length(values) < 2 || g_strv_length(values) > 4) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments: filename required"); + return FALSE; + } + + if (g_strv_length(values) > 2) + firmware_type_src = g_strdup(values[2]); + if (g_strv_length(values) > 3) + firmware_type_dst = g_strdup(values[3]); + + /* load file */ + blob_src = fu_bytes_get_contents(values[0], error); + if (blob_src == NULL) + return FALSE; + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* find the GType to use */ + if (firmware_type_src == NULL) + firmware_type_src = fu_util_prompt_for_firmware_type(priv, error); + if (firmware_type_src == NULL) + return FALSE; + if (firmware_type_dst == NULL) + firmware_type_dst = fu_util_prompt_for_firmware_type(priv, error); + if (firmware_type_dst == NULL) + return FALSE; + gtype_src = fu_context_get_firmware_gtype_by_id(fu_engine_get_context(priv->engine), + firmware_type_src); + if (gtype_src == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + firmware_type_src); + return FALSE; + } + firmware_src = g_object_new(gtype_src, NULL); + if (!fu_firmware_parse(firmware_src, blob_src, priv->flags, error)) + return FALSE; + gtype_dst = fu_context_get_firmware_gtype_by_id(ctx, firmware_type_dst); + if (gtype_dst == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + firmware_type_dst); + return FALSE; + } + str_src = fu_firmware_to_string(firmware_src); + g_print("%s", str_src); + + /* copy images */ + firmware_dst = g_object_new(gtype_dst, NULL); + images = fu_firmware_get_images(firmware_src); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + fu_firmware_add_image(firmware_dst, img); + } + + /* write new file */ + blob_dst = fu_firmware_write(firmware_dst, error); + if (blob_dst == NULL) + return FALSE; + if (!fu_bytes_set_contents(values[1], blob_dst, error)) + return FALSE; + str_dst = fu_firmware_to_string(firmware_dst); + g_print("%s", str_dst); + + /* success */ + return TRUE; +} + +static GBytes * +fu_util_hex_string_to_bytes(const gchar *val, GError **error) +{ + gsize valsz; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* sanity check */ + if (val == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "nothing to parse"); + return NULL; + } + + /* parse each hex byte */ + valsz = strlen(val); + for (guint i = 0; i < valsz; i += 2) { + guint8 tmp = 0; + if (!fu_firmware_strparse_uint8_safe(val, valsz, i, &tmp, error)) + return NULL; + fu_byte_array_append_uint8(buf, tmp); + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_util_firmware_patch(FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuContext *ctx = fu_engine_get_context(priv->engine); + GType gtype; + g_autofree gchar *firmware_type = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) blob_dst = NULL; + g_autoptr(GBytes) blob_src = NULL; + g_autoptr(GBytes) patch = NULL; + guint64 offset = 0; + + /* check args */ + if (g_strv_length(values) != 3 && g_strv_length(values) != 4) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected %s", + "FILENAME OFFSET DATA [FIRMWARE-TYPE]"); + return FALSE; + } + + /* hardcoded */ + if (g_strv_length(values) == 4) + firmware_type = g_strdup(values[3]); + + /* load file */ + blob_src = fu_bytes_get_contents(values[0], error); + if (blob_src == NULL) + return FALSE; + + /* parse offset */ + if (!fu_strtoull(values[1], &offset, 0x0, G_MAXUINT32, error)) { + g_prefix_error(error, "failed to parse offset: "); + return FALSE; + } + + /* parse blob */ + patch = fu_util_hex_string_to_bytes(values[2], error); + if (patch == NULL) + return FALSE; + if (g_bytes_get_size(patch) == 0) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, "no data provided"); + return FALSE; + } + + /* load engine */ + if (!fu_engine_load(priv->engine, + FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* find the GType to use */ + if (firmware_type == NULL) + firmware_type = fu_util_prompt_for_firmware_type(priv, error); + if (firmware_type == NULL) + return FALSE; + gtype = fu_context_get_firmware_gtype_by_id(ctx, firmware_type); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", + firmware_type); + return FALSE; + } + firmware = g_object_new(gtype, NULL); + if (!fu_firmware_parse(firmware, blob_src, priv->flags, error)) + return FALSE; + + /* add patch */ + fu_firmware_add_patch(firmware, offset, patch); + + /* write new file */ + blob_dst = fu_firmware_write(firmware, error); + if (blob_dst == NULL) + return FALSE; + if (!fu_bytes_set_contents(values[0], blob_dst, error)) + return FALSE; + str = fu_firmware_to_string(firmware); + g_print("%s", str); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_verify_update(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *str = NULL; + g_autoptr(FuDevice) dev = NULL; + + /* progress */ + fu_progress_set_id(priv->progress, G_STRLOC); + fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 50, "start-engine"); + fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_VERIFY, 50, "verify-update"); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* get device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + if (g_strv_length(values) == 1) { + dev = fu_util_get_device(priv, values[0], error); + if (dev == NULL) + return FALSE; + } else { + dev = fu_util_prompt_for_device(priv, NULL, error); + if (dev == NULL) + return FALSE; + } + + /* add checksums */ + if (!fu_engine_verify_update(priv->engine, + fu_device_get_id(dev), + fu_progress_get_child(priv->progress), + error)) + return FALSE; + fu_progress_step_done(priv->progress); + + /* 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; + g_autoptr(GNode) root = g_node_new(NULL); + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + 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++) { + g_autoptr(GPtrArray) rels = NULL; + FwupdDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel; + const gchar *remote; + GNode *child; + + if (!fu_util_filter_device(priv, dev)) + continue; + child = g_node_append_data(root, dev); + + rel = fwupd_device_get_release_default(dev); + if (rel == NULL) + continue; + remote = fwupd_release_get_remote_id(rel); + + /* doesn't actually map to remote */ + if (remote == NULL) { + g_node_append_data(child, rel); + continue; + } + + /* try to lookup releases from client */ + rels = fu_engine_get_releases(priv->engine, + priv->request, + fwupd_device_get_id(dev), + error); + if (rels == NULL) + return FALSE; + + /* map to a release in client */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel2 = g_ptr_array_index(rels, j); + if (g_strcmp0(remote, fwupd_release_get_remote_id(rel2)) != 0) + continue; + if (g_strcmp0(fwupd_release_get_version(rel), + fwupd_release_get_version(rel2)) != 0) + continue; + g_node_append_data(child, g_object_ref(rel2)); + rel = NULL; + break; + } + + /* didn't match anything */ + if (rels->len == 0 || rel != NULL) { + g_node_append_data(child, rel); + continue; + } + } + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +static gboolean +fu_util_refresh_remote(FuUtilPrivate *priv, FwupdRemote *remote, GError **error) +{ + const gchar *metadata_uri = NULL; + g_autoptr(GBytes) bytes_raw = NULL; + g_autoptr(GBytes) bytes_sig = NULL; + + /* signature */ + metadata_uri = fwupd_remote_get_metadata_uri_sig(remote); + if (metadata_uri == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "no metadata signature URI available for %s", + fwupd_remote_get_id(remote)); + return FALSE; + } + bytes_sig = fwupd_client_download_bytes(priv->client, + metadata_uri, + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + priv->cancellable, + error); + if (bytes_sig == NULL) + return FALSE; + if (!fwupd_remote_load_signature_bytes(remote, bytes_sig, error)) + return FALSE; + + /* payload */ + metadata_uri = fwupd_remote_get_metadata_uri(remote); + if (metadata_uri == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "no metadata URI available for %s", + fwupd_remote_get_id(remote)); + return FALSE; + } + bytes_raw = fwupd_client_download_bytes(priv->client, + metadata_uri, + FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, + priv->cancellable, + error); + if (bytes_raw == NULL) + return FALSE; + + /* send to daemon */ + g_debug("updating %s", fwupd_remote_get_id(remote)); + return fu_engine_update_metadata_bytes(priv->engine, + fwupd_remote_get_id(remote), + bytes_raw, + bytes_sig, + error); +} + +static gboolean +fu_util_refresh(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) remotes = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* download new metadata */ + remotes = fu_engine_get_remotes(priv->engine, error); + if (remotes == NULL) + return FALSE; + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + if (!fwupd_remote_get_enabled(remote)) + continue; + if (fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) + continue; + if (!fu_util_refresh_remote(priv, remote, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GNode) root = g_node_new(NULL); + g_autoptr(GPtrArray) remotes = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_REMOTES, priv->progress, error)) + return FALSE; + + /* list remotes */ + remotes = fu_engine_get_remotes(priv->engine, error); + if (remotes == NULL) + return FALSE; + if (remotes->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "no remotes available"); + return FALSE; + } + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote_tmp = g_ptr_array_index(remotes, i); + g_node_append_data(root, remote_tmp); + } + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +static gboolean +fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE; + g_autoptr(FuSecurityAttrs) attrs = NULL; + g_autoptr(FuSecurityAttrs) events = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) items = NULL; + g_autoptr(GPtrArray) events_array = NULL; + g_autofree gchar *str = NULL; + +#ifndef HAVE_HSI + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); + return FALSE; +#endif /* HAVE_HSI */ + + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* show or hide different elements */ + if (priv->show_all) { + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES; + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS; + } + + attrs = fu_engine_get_host_security_attrs(priv->engine); + items = fu_security_attrs_get_all(attrs); + for (guint j = 0; j < items->len; j++) { + FwupdSecurityAttr *attr = g_ptr_array_index(items, j); + g_autofree gchar *err_str = NULL; + + if (!fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA)) + continue; + if (priv->flags & FWUPD_INSTALL_FLAG_FORCE) + continue; + err_str = g_strdup_printf( + "\n%s\n » %s\n%s", + /* TRANSLATORS: error message to tell someone they can't use this feature */ + _("Not enough data was provided to make an HSI calculation."), + "https://fwupd.github.io/hsi.html#not-enough-info", + /* TRANSLATORS: message to tell someone how to ignore error */ + _("To ignore this warning, use --force")); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, err_str); + return FALSE; + } + + /* print the "why" */ + if (priv->as_json) { + str = fu_security_attrs_to_json_string(attrs, error); + if (str == NULL) + return FALSE; + g_print("%s\n", str); + return TRUE; + } + + g_print("%s \033[1m%s\033[0m\n", + /* TRANSLATORS: this is a string like 'HSI:2-U' */ + _("Host Security ID:"), + fu_engine_get_host_security_id(priv->engine)); + + str = fu_util_security_attrs_to_string(items, flags); + g_print("%s\n", str); + + /* print the "when" */ + events = fu_engine_get_host_security_events(priv->engine, 10, error); + if (events == NULL) + return FALSE; + events_array = fu_security_attrs_get_all(attrs); + if (events_array->len > 0) { + g_autofree gchar *estr = fu_util_security_events_to_string(events_array, flags); + if (estr != NULL) + g_print("%s\n", estr); + } + + /* print the "also" */ + devices = fu_engine_get_devices(priv->engine, error); + if (devices == NULL) + return FALSE; + if (devices->len > 0) { + g_autofree gchar *estr = fu_util_security_issues_to_string(devices); + if (estr != NULL) + g_print("%s\n", estr); + } + + /* success */ + return TRUE; +} + +static FuVolume * +fu_util_prompt_for_volume(FuUtilPrivate *priv, GError **error) +{ + FuContext *ctx = fu_engine_get_context(priv->engine); + FuVolume *volume; + guint idx; + g_autoptr(GPtrArray) volumes = NULL; + + /* exactly one */ + volumes = fu_context_get_esp_volumes(ctx, error); + if (volumes == NULL) + return NULL; + if (volumes->len == 1) { + volume = g_ptr_array_index(volumes, 0); + /* TRANSLATORS: Volume has been chosen by the user */ + g_print("%s: %s\n", _("Selected volume"), fu_volume_get_id(volume)); + return g_object_ref(volume); + } + + /* TRANSLATORS: get interactive prompt */ + g_print("%s\n", _("Choose a volume:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < volumes->len; i++) { + volume = g_ptr_array_index(volumes, i); + g_print("%u.\t%s\n", i + 1, fu_volume_get_id(volume)); + } + idx = fu_util_prompt_for_number(volumes->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return NULL; + } + volume = g_ptr_array_index(volumes, idx - 1); + return g_object_ref(volume); +} + +static gboolean +fu_util_esp_mount(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuVolume) volume = NULL; + volume = fu_util_prompt_for_volume(priv, error); + if (volume == NULL) + return FALSE; + return fu_volume_mount(volume, error); +} + +static gboolean +fu_util_esp_unmount(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuVolume) volume = NULL; + volume = fu_util_prompt_for_volume(priv, error); + if (volume == NULL) + return FALSE; + return fu_volume_unmount(volume, error); +} + +static gboolean +fu_util_esp_list(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *mount_point = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuVolume) volume = NULL; + g_autoptr(GPtrArray) files = NULL; + + volume = fu_util_prompt_for_volume(priv, error); + if (volume == NULL) + return FALSE; + locker = fu_volume_locker(volume, error); + if (locker == NULL) + return FALSE; + mount_point = fu_volume_get_mount_point(volume); + files = fu_path_get_files(mount_point, error); + if (files == NULL) + return FALSE; + for (guint i = 0; i < files->len; i++) { + const gchar *fn = g_ptr_array_index(files, i); + g_print("%s\n", fn); + } + return TRUE; +} + +static gboolean +_g_str_equal0(gconstpointer str1, gconstpointer str2) +{ + return g_strcmp0(str1, str2) == 0; +} + +static gboolean +fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *branch; + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func(g_free); + g_autoptr(FuDevice) dev = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | + FU_ENGINE_LOAD_FLAG_REMOTES, + priv->progress, + error)) + return FALSE; + + /* find the device and check it has multiple branches */ + priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + if (g_strv_length(values) == 1) + dev = fu_util_get_device(priv, values[1], error); + else + dev = fu_util_prompt_for_device(priv, NULL, error); + if (dev == NULL) + return FALSE; + if (!fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Multiple branches not available"); + return FALSE; + } + + /* get all releases, including the alternate branch versions */ + rels = fu_engine_get_releases(priv->engine, priv->request, fu_device_get_id(dev), error); + if (rels == NULL) + return FALSE; + + /* get all the unique branches */ + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, i); + const gchar *branch_tmp = fwupd_release_get_branch(rel_tmp); +#if GLIB_CHECK_VERSION(2, 54, 3) + if (g_ptr_array_find_with_equal_func(branches, branch_tmp, _g_str_equal0, NULL)) + continue; +#endif + g_ptr_array_add(branches, g_strdup(branch_tmp)); + } + + /* branch name is optional */ + if (g_strv_length(values) > 1) { + branch = values[1]; + } else if (branches->len == 1) { + branch = g_ptr_array_index(branches, 0); + } else { + guint idx; + + /* TRANSLATORS: get interactive prompt, where branch is the + * supplier of the firmware, e.g. "non-free" or "free" */ + g_print("%s\n", _("Choose a branch:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < branches->len; i++) { + const gchar *branch_tmp = g_ptr_array_index(branches, i); + g_print("%u.\t%s\n", i + 1, fu_util_branch_for_display(branch_tmp)); + } + idx = fu_util_prompt_for_number(branches->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + branch = g_ptr_array_index(branches, idx - 1); + } + + /* sanity check */ + if (g_strcmp0(branch, fu_device_get_branch(dev)) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s is already on branch %s", + fu_device_get_name(dev), + fu_util_branch_for_display(branch)); + return FALSE; + } + + /* the releases are ordered by version */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); + if (g_strcmp0(fwupd_release_get_branch(rel_tmp), branch) == 0) { + rel = g_object_ref(rel_tmp); + break; + } + } + if (rel == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No releases for branch %s", + fu_util_branch_for_display(branch)); + return FALSE; + } + + /* we're switching branch */ + if (!fu_util_switch_branch_warning(FWUPD_DEVICE(dev), rel, FALSE, error)) + return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (!fu_util_install_release(priv, rel, error)) + 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; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error) +{ + g_autoptr(GHashTable) settings = fu_util_bios_settings_parse_argv(input, error); + + if (settings == NULL) + return FALSE; + + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_HWINFO | FU_ENGINE_LOAD_FLAG_COLDPLUG, + priv->progress, + error)) + return FALSE; + + if (!fu_engine_modify_bios_settings(priv->engine, settings, FALSE, error)) { + if (!g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) + g_prefix_error(error, "failed to set BIOS setting: "); + return FALSE; + } + + if (!priv->as_json) { + gpointer key, value; + GHashTableIter iter; + + g_hash_table_iter_init(&iter, settings); + while (g_hash_table_iter_next(&iter, &key, &value)) { + g_autofree gchar *msg = + /* TRANSLATORS: Configured a BIOS setting to a value */ + g_strdup_printf(_("Set BIOS setting '%s' using '%s'."), + (const gchar *)key, + (const gchar *)value); + g_print("\n%s\n", msg); + } + } + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuBiosSettings) attrs = NULL; + g_autoptr(GPtrArray) items = NULL; + FuContext *ctx = fu_engine_get_context(priv->engine); + gboolean found = FALSE; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_HWINFO | FU_ENGINE_LOAD_FLAG_COLDPLUG, + priv->progress, + error)) + return FALSE; + + attrs = fu_context_get_bios_settings(ctx); + items = fu_bios_settings_get_all(attrs); + if (priv->as_json) + return fu_util_get_bios_setting_as_json(values, items, error); + + for (guint i = 0; i < items->len; i++) { + FwupdBiosSetting *attr = g_ptr_array_index(items, i); + if (fu_util_bios_setting_matches_args(attr, values)) { + g_autofree gchar *tmp = fu_util_bios_setting_to_string(attr, 0); + g_print("%s\n", tmp); + found = TRUE; + } + } + if (items->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: error message */ + _("This system doesn't support firmware settings")); + return FALSE; + } + if (!found) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + /* TRANSLATORS: error message */ + "Unable to find attribute '%s'", + values[0]); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_util_version(FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GHashTable) metadata = NULL; + g_autofree gchar *str = NULL; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_READONLY | + FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, + priv->progress, + error)) + return FALSE; + + /* get metadata */ + metadata = fu_engine_get_report_metadata(priv->engine, error); + if (metadata == NULL) + return FALSE; + + /* dump to the screen in the most appropriate format */ + if (priv->as_json) + return fu_util_project_versions_as_json(metadata, error); + str = fu_util_project_versions_to_string(metadata); + g_print("%s", str); + return TRUE; +} + +static gboolean +fu_util_clear_history(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuHistory) history = fu_history_new(); + return fu_history_remove_all(history, error); +} + +static gboolean +fu_util_setup_interactive(FuUtilPrivate *priv, GError **error) +{ + if (priv->as_json) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "using --json"); + return FALSE; + } + return fu_util_setup_interactive_console(error); +} + +static gboolean +fu_util_backends_save(FuUtilPrivate *priv, const gchar *fn, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonBuilder) json_builder = json_builder_new(); + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* export as a string */ + if (!fu_engine_backends_save(priv->engine, json_builder, error)) + return FALSE; + json_root = json_builder_get_root(json_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, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Failed to convert to JSON string"); + return FALSE; + } + + /* save to file */ + return g_file_set_contents(fn, data, -1, error); +} + +int +main(int argc, char *argv[]) +{ + gboolean allow_branch_switch = FALSE; + gboolean allow_older = FALSE; + gboolean allow_reinstall = FALSE; + gboolean force = FALSE; + gboolean ret; + gboolean version = FALSE; + gboolean ignore_checksum = FALSE; + gboolean ignore_vid_pid = FALSE; + g_auto(GStrv) plugin_glob = NULL; + g_autoptr(FuUtilPrivate) priv = g_new0(FuUtilPrivate, 1); + g_autoptr(GError) error_console = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new(); + g_autofree gchar *cmd_descriptions = NULL; + g_autofree gchar *filter = NULL; + g_autofree gchar *save_backends_fn = NULL; + const GOptionEntry options[] = { + {"version", + '\0', + 0, + G_OPTION_ARG_NONE, + &version, + /* TRANSLATORS: command line option */ + N_("Show client and daemon versions"), + NULL}, + {"allow-reinstall", + '\0', + 0, + G_OPTION_ARG_NONE, + &allow_reinstall, + /* TRANSLATORS: command line option */ + N_("Allow reinstalling existing firmware versions"), + NULL}, + {"allow-older", + '\0', + 0, + G_OPTION_ARG_NONE, + &allow_older, + /* TRANSLATORS: command line option */ + N_("Allow downgrading firmware versions"), + NULL}, + {"allow-branch-switch", + '\0', + 0, + G_OPTION_ARG_NONE, + &allow_branch_switch, + /* TRANSLATORS: command line option */ + N_("Allow switching firmware branch"), + NULL}, + {"force", + '\0', + 0, + G_OPTION_ARG_NONE, + &force, + /* TRANSLATORS: command line option */ + N_("Force the action by relaxing some runtime checks"), + NULL}, + {"ignore-checksum", + '\0', + 0, + G_OPTION_ARG_NONE, + &ignore_checksum, + /* TRANSLATORS: command line option */ + N_("Ignore firmware checksum failures"), + NULL}, + {"ignore-vid-pid", + '\0', + 0, + G_OPTION_ARG_NONE, + &ignore_vid_pid, + /* TRANSLATORS: command line option */ + N_("Ignore firmware hardware mismatch failures"), + NULL}, + {"no-reboot-check", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_reboot_check, + /* TRANSLATORS: command line option */ + N_("Do not check or prompt for reboot after update"), + NULL}, + {"no-safety-check", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_safety_check, + /* TRANSLATORS: command line option */ + N_("Do not perform device safety checks"), + NULL}, + {"no-device-prompt", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_device_prompt, + /* TRANSLATORS: command line option */ + N_("Do not prompt for devices"), + NULL}, + {"show-all", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->show_all, + /* TRANSLATORS: command line option */ + N_("Show all results"), + NULL}, + {"show-all-devices", + '\0', + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, + &priv->show_all, + /* TRANSLATORS: command line option */ + N_("Show devices that are not updatable"), + NULL}, + {"plugins", + '\0', + 0, + G_OPTION_ARG_STRING_ARRAY, + &plugin_glob, + /* TRANSLATORS: command line option */ + N_("Manually enable specific plugins"), + NULL}, + {"plugin-whitelist", + '\0', + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING_ARRAY, + &plugin_glob, + /* TRANSLATORS: command line option */ + N_("Manually enable specific plugins"), + NULL}, + {"prepare", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->prepare_blob, + /* TRANSLATORS: command line option */ + N_("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 */ + N_("Run the plugin composite cleanup routine when using install-blob"), + NULL}, + {"disable-ssl-strict", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->disable_ssl_strict, + /* TRANSLATORS: command line option */ + N_("Ignore SSL strict checks when downloading files"), + NULL}, + {"filter", + '\0', + 0, + G_OPTION_ARG_STRING, + &filter, + /* TRANSLATORS: command line option */ + N_("Filter with a set of device flags using a ~ prefix to " + "exclude, e.g. 'internal,~needs-reboot'"), + NULL}, + {"save-backends", + '\0', + 0, + G_OPTION_ARG_STRING, + &save_backends_fn, + /* TRANSLATORS: command line option */ + N_("Specify a filename to use to save backend events"), + /* TRANSLATORS: filename argument with path */ + N_("FILENAME")}, + {"json", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->as_json, + /* TRANSLATORS: command line option */ + N_("Output in JSON format"), + NULL}, + {NULL}}; + +#ifdef _WIN32 + /* workaround Windows setting the codepage to 1252 */ + (void)g_setenv("LANG", "C.UTF-8", FALSE); +#endif + + setlocale(LC_ALL, ""); + + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* create helper object */ + priv->main_ctx = g_main_context_new(); + priv->loop = g_main_loop_new(priv->main_ctx, FALSE); + priv->progressbar = fu_progressbar_new(); + priv->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + fu_progressbar_set_main_context(priv->progressbar, priv->main_ctx); + priv->request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + + /* used for monitoring and downloading */ + priv->client = fwupd_client_new(); + fwupd_client_set_main_context(priv->client, priv->main_ctx); + fwupd_client_set_user_agent_for_package(priv->client, "fwupdtool", PACKAGE_VERSION); + g_signal_connect(FWUPD_CLIENT(priv->client), + "notify::percentage", + G_CALLBACK(fu_util_client_notify_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "notify::status", + G_CALLBACK(fu_util_client_notify_cb), + priv); + + /* when not using the engine */ + priv->progress = fu_progress_new(G_STRLOC); + g_signal_connect(priv->progress, + "percentage-changed", + G_CALLBACK(fu_util_progress_percentage_changed_cb), + priv); + g_signal_connect(priv->progress, + "status-changed", + G_CALLBACK(fu_util_progress_status_changed_cb), + priv); + + /* add commands */ + fu_util_cmd_array_add(cmd_array, + "smbios-dump", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILE"), + /* TRANSLATORS: command description */ + _("Dump SMBIOS data from a file"), + fu_util_smbios_dump); + 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_cmd_array_add(cmd_array, + "get-details", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILE"), + /* TRANSLATORS: command description */ + _("Gets details about a firmware file"), + fu_util_get_details); + 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,get-upgrades", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Gets the list of updates for connected hardware"), + fu_util_get_updates); + fu_util_cmd_array_add(cmd_array, + "get-devices,get-topology", + NULL, + /* TRANSLATORS: command description */ + _("Get all devices that support firmware updates"), + fu_util_get_devices); + fu_util_cmd_array_add(cmd_array, + "get-device-flags", + NULL, + /* TRANSLATORS: command description */ + _("Get all device flags supported by fwupd"), + fu_util_get_device_flags); + fu_util_cmd_array_add(cmd_array, + "watch", + NULL, + /* TRANSLATORS: command description */ + _("Watch for hardware changes"), + fu_util_watch); + fu_util_cmd_array_add(cmd_array, + "install-blob", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME DEVICE-ID"), + /* TRANSLATORS: command description */ + _("Install a firmware blob on a device"), + fu_util_install_blob); + fu_util_cmd_array_add(cmd_array, + "install", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILE [DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Install a specific firmware on a device, all possible devices" + " will also be installed once the CAB matches"), + fu_util_install); + fu_util_cmd_array_add(cmd_array, + "reinstall", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID|GUID"), + /* TRANSLATORS: command description */ + _("Reinstall firmware on a device"), + fu_util_reinstall); + fu_util_cmd_array_add(cmd_array, + "attach", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID|GUID"), + /* TRANSLATORS: command description */ + _("Attach to firmware mode"), + fu_util_attach); + fu_util_cmd_array_add(cmd_array, + "detach", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID|GUID"), + /* TRANSLATORS: command description */ + _("Detach to bootloader mode"), + fu_util_detach); + fu_util_cmd_array_add(cmd_array, + "unbind-driver", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Unbind current driver"), + fu_util_unbind_driver); + fu_util_cmd_array_add(cmd_array, + "bind-driver", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Bind new kernel driver"), + fu_util_bind_driver); + fu_util_cmd_array_add(cmd_array, + "activate", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Activate pending devices"), + fu_util_activate); + fu_util_cmd_array_add(cmd_array, + "hwids", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[SMBIOS-FILE|HWIDS-FILE]"), + /* TRANSLATORS: command description */ + _("Return all the hardware IDs for the machine"), + fu_util_hwids); + fu_util_cmd_array_add(cmd_array, + "export-hwids", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("HWIDS-FILE"), + /* TRANSLATORS: command description */ + _("Save a file that allows generation of hardware IDs"), + fu_util_export_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,upgrade", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Updates all specified devices to latest firmware version, or all " + "devices if unspecified"), + fu_util_update); + fu_util_cmd_array_add(cmd_array, + "self-sign", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("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", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Update the stored metadata with current contents"), + fu_util_verify_update); + fu_util_cmd_array_add(cmd_array, + "firmware-sign", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME CERTIFICATE PRIVATE-KEY"), + /* TRANSLATORS: command description */ + _("Sign a firmware with a new key"), + fu_util_firmware_sign); + fu_util_cmd_array_add(cmd_array, + "firmware-dump", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME [DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Read a firmware blob from a device"), + fu_util_firmware_dump); + fu_util_cmd_array_add(cmd_array, + "firmware-read", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME [DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Read a firmware from a device"), + fu_util_firmware_read); + fu_util_cmd_array_add(cmd_array, + "firmware-patch", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME OFFSET DATA [FIRMWARE-TYPE]"), + /* TRANSLATORS: command description */ + _("Patch a firmware blob at a known offset"), + fu_util_firmware_patch); + fu_util_cmd_array_add( + cmd_array, + "firmware-convert", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"), + /* TRANSLATORS: command description */ + _("Convert a firmware file"), + fu_util_firmware_convert); + fu_util_cmd_array_add(cmd_array, + "firmware-build", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("BUILDER-XML FILENAME-DST"), + /* TRANSLATORS: command description */ + _("Build a firmware file"), + fu_util_firmware_build); + fu_util_cmd_array_add(cmd_array, + "firmware-parse", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME [FIRMWARE-TYPE]"), + /* TRANSLATORS: command description */ + _("Parse and show details about a firmware file"), + fu_util_firmware_parse); + fu_util_cmd_array_add(cmd_array, + "firmware-export", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME [FIRMWARE-TYPE]"), + /* TRANSLATORS: command description */ + _("Export a firmware file structure to XML"), + fu_util_firmware_export); + fu_util_cmd_array_add(cmd_array, + "firmware-extract", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME [FIRMWARE-TYPE]"), + /* TRANSLATORS: command description */ + _("Extract a firmware blob to images"), + fu_util_firmware_extract); + fu_util_cmd_array_add(cmd_array, + "get-firmware-types", + NULL, + /* TRANSLATORS: command description */ + _("List the available firmware types"), + fu_util_get_firmware_types); + fu_util_cmd_array_add(cmd_array, + "get-remotes", + NULL, + /* TRANSLATORS: command description */ + _("Gets the configured remotes"), + fu_util_get_remotes); + fu_util_cmd_array_add(cmd_array, + "refresh", + NULL, + /* TRANSLATORS: command description */ + _("Refresh metadata from remote server"), + fu_util_refresh); + fu_util_cmd_array_add(cmd_array, + "security", + NULL, + /* TRANSLATORS: command description */ + _("Gets the host security attributes"), + fu_util_security); + fu_util_cmd_array_add(cmd_array, + "esp-mount", + NULL, + /* TRANSLATORS: command description */ + _("Mounts the ESP"), + fu_util_esp_mount); + fu_util_cmd_array_add(cmd_array, + "esp-unmount", + NULL, + /* TRANSLATORS: command description */ + _("Unmounts the ESP"), + fu_util_esp_unmount); + fu_util_cmd_array_add(cmd_array, + "esp-list", + NULL, + /* TRANSLATORS: command description */ + _("Lists files on the ESP"), + fu_util_esp_list); + fu_util_cmd_array_add(cmd_array, + "switch-branch", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID] [BRANCH]"), + /* TRANSLATORS: command description */ + _("Switch the firmware branch on the device"), + fu_util_switch_branch); + fu_util_cmd_array_add(cmd_array, + "clear-history", + NULL, + /* TRANSLATORS: command description */ + _("Erase all firmware update history"), + fu_util_clear_history); + fu_util_cmd_array_add( + cmd_array, + "get-bios-settings,get-bios-setting", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[SETTING1] [ SETTING2]..."), + /* TRANSLATORS: command description */ + _("Retrieve BIOS settings. If no arguments are passed all settings are returned"), + fu_util_get_bios_setting); + fu_util_cmd_array_add(cmd_array, + "set-bios-setting", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("SETTING VALUE"), + /* TRANSLATORS: command description */ + _("Set a BIOS setting"), + fu_util_set_bios_setting); + + /* do stuff on ctrl+c */ + priv->cancellable = g_cancellable_new(); + fu_util_setup_signal_handlers(priv); + g_signal_connect(G_CANCELLABLE(priv->cancellable), + "cancelled", + G_CALLBACK(fu_util_cancelled_cb), + priv); + + /* sort by command name */ + fu_util_cmd_array_sort(cmd_array); + + /* non-TTY consoles cannot answer questions */ + if (!fu_util_setup_interactive(priv, &error_console)) { + g_debug("failed to initialize interactive console: %s", error_console->message); + priv->no_reboot_check = TRUE; + priv->no_safety_check = TRUE; + priv->no_device_prompt = TRUE; + } else { + priv->interactive = TRUE; + /* set our implemented feature set */ + fu_engine_request_set_feature_flags( + priv->request, + FWUPD_FEATURE_FLAG_DETACH_ACTION | FWUPD_FEATURE_FLAG_SWITCH_BRANCH | + FWUPD_FEATURE_FLAG_FDE_WARNING | FWUPD_FEATURE_FLAG_UPDATE_ACTION | + FWUPD_FEATURE_FLAG_COMMUNITY_TEXT | FWUPD_FEATURE_FLAG_SHOW_PROBLEMS | + FWUPD_FEATURE_FLAG_REQUESTS); + } + fu_progressbar_set_interactive(priv->progressbar, priv->interactive); + + /* 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, + /* TRANSLATORS: CLI description */ + _("This tool allows an administrator to use the fwupd plugins " + "without being installed on the host system.")); + + /* TRANSLATORS: program name */ + g_set_application_name(_("Firmware Utility")); + g_option_context_add_main_entries(priv->context, options, NULL); + g_option_context_add_group(priv->context, fu_debug_get_option_group()); + ret = g_option_context_parse(priv->context, &argc, &argv, &error); + 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; + } + fu_progress_set_profile(priv->progress, g_getenv("FWUPD_VERBOSE") != NULL); + + /* allow disabling SSL strict mode for broken corporate proxies */ + if (priv->disable_ssl_strict) { + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_printerr("%s %s\n", + fmt, + /* TRANSLATORS: try to help */ + _("Ignoring SSL strict checks, " + "to do this automatically in the future " + "export DISABLE_SSL_STRICT in your environment")); + (void)g_setenv("DISABLE_SSL_STRICT", "1", TRUE); + } + + /* parse filter flags */ + if (filter != NULL) { + if (!fu_util_parse_filter_flags(filter, + &priv->filter_include, + &priv->filter_exclude, + &error)) { + g_print("%s: %s\n", + /* TRANSLATORS: the user didn't read the man page */ + _("Failed to parse flags for --filter"), + error->message); + return EXIT_FAILURE; + } + } + + /* set flags */ + if (allow_reinstall) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (allow_older) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + if (allow_branch_switch) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (force) + priv->flags |= FWUPD_INSTALL_FLAG_FORCE; + else + priv->flags |= FWUPD_INSTALL_FLAG_NO_SEARCH; + if (ignore_checksum) + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; + if (ignore_vid_pid) + priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID; + + /* load engine */ + priv->engine = fu_engine_new(); + if (save_backends_fn != NULL) { + fu_context_add_flag(fu_engine_get_context(priv->engine), + FU_CONTEXT_FLAG_SAVE_EVENTS); + } + g_signal_connect(FU_ENGINE(priv->engine), + "device-request", + G_CALLBACK(fu_util_update_device_request_cb), + priv); + g_signal_connect(FU_ENGINE(priv->engine), + "device-added", + G_CALLBACK(fu_main_engine_device_added_cb), + priv); + g_signal_connect(FU_ENGINE(priv->engine), + "device-removed", + G_CALLBACK(fu_main_engine_device_removed_cb), + priv); + g_signal_connect(FU_ENGINE(priv->engine), + "status-changed", + G_CALLBACK(fu_main_engine_status_changed_cb), + priv); + + /* just show versions and exit */ + if (version) { + if (!fu_util_version(priv, &error)) { + g_printerr("%s\n", error->message); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } + + /* any plugin allowlist specified */ + for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++) + fu_engine_add_plugin_filter(priv->engine, plugin_glob[i]); + + /* run the specified command */ + ret = fu_util_cmd_array_run(cmd_array, priv, argv[1], (gchar **)&argv[2], &error); + if (!ret) { +#ifdef SUPPORTED_BUILD + /* sanity check */ + if (error == NULL) { + g_critical("exec failed but no error set!"); + return EXIT_FAILURE; + } +#endif + g_printerr("%s\n", error->message); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { + /* TRANSLATORS: error message explaining command on how to get help */ + g_printerr("\n%s\n", _("Use fwupdtool --help for help")); + } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("%s\n", error->message); + return EXIT_NOTHING_TO_DO; + } +#ifdef HAVE_GETUID + /* if not root, then notify users on the error path */ + if (priv->interactive && (getuid() != 0 || geteuid() != 0)) { + /* TRANSLATORS: we're poking around as a power user */ + g_printerr("%s\n", _("NOTE: This program may only work correctly as root")); + } +#endif + return EXIT_FAILURE; + } + + /* dump devices */ + if (save_backends_fn != NULL && !fu_util_backends_save(priv, save_backends_fn, &error)) { + g_printerr("%s\n", error->message); + return EXIT_FAILURE; + } + + /* a good place to do the traceback */ + if (fu_progress_get_profile(priv->progress)) { + g_autofree gchar *str = fu_progress_traceback(priv->progress); + if (str != NULL) + g_print("\n%s\n", str); + } + + /* success */ + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/src/fu-udev-backend.c b/fwupd-1.8.6/src/fu-udev-backend.c new file mode 100644 index 0000000000000000000000000000000000000000..a69f92e5a02c0193412f00c9fb2c3bfc0ce11127 --- /dev/null +++ b/fwupd-1.8.6/src/fu-udev-backend.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuBackend" + +#include "config.h" + +#include + +#include + +#include "fu-context-private.h" +#include "fu-udev-backend.h" + +struct _FuUdevBackend { + FuBackend parent_instance; + GUdevClient *gudev_client; + GHashTable *changed_idle_ids; /* sysfs:FuUdevBackendHelper */ + GPtrArray *subsystems; +}; + +G_DEFINE_TYPE(FuUdevBackend, fu_udev_backend, FU_TYPE_BACKEND) + +static void +fu_udev_backend_device_add(FuUdevBackend *self, GUdevDevice *udev_device) +{ + GType gtype = FU_TYPE_UDEV_DEVICE; + g_autoptr(FuUdevDevice) device = NULL; + struct { + const gchar *subsystem; + GType gtype; + } subsystem_gtype_map[] = {{"mei", FU_TYPE_MEI_DEVICE}, + {"i2c", FU_TYPE_I2C_DEVICE}, + {"i2c-dev", FU_TYPE_I2C_DEVICE}, + {NULL, G_TYPE_INVALID}}; + + /* create the correct object depending on the subsystem */ + for (guint i = 0; subsystem_gtype_map[i].gtype != G_TYPE_INVALID; i++) { + if (g_strcmp0(g_udev_device_get_subsystem(udev_device), + subsystem_gtype_map[i].subsystem) == 0) { + gtype = subsystem_gtype_map[i].gtype; + break; + } + } + + /* ignore some devices */ + if (gtype == FU_TYPE_MEI_DEVICE && g_udev_device_get_device_file(udev_device) == NULL) { + g_debug("ignoring MEI device %s as no device file", + g_udev_device_get_sysfs_path(udev_device)); + return; + } + + /* success */ + device = g_object_new(gtype, + "context", + fu_backend_get_context(FU_BACKEND(self)), + "udev-device", + udev_device, + NULL); + fu_backend_device_added(FU_BACKEND(self), FU_DEVICE(device)); +} + +static void +fu_udev_backend_device_remove(FuUdevBackend *self, GUdevDevice *udev_device) +{ + FuDevice *device_tmp; + + /* find the device we enumerated */ + device_tmp = + fu_backend_lookup_by_id(FU_BACKEND(self), g_udev_device_get_sysfs_path(udev_device)); + if (device_tmp != NULL) { + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) { + g_debug("UDEV %s removed", g_udev_device_get_sysfs_path(udev_device)); + } + fu_backend_device_removed(FU_BACKEND(self), device_tmp); + } +} + +typedef struct { + FuUdevBackend *self; + FuDevice *device; + guint idle_id; +} FuUdevBackendHelper; + +static void +fu_udev_backend_changed_helper_free(FuUdevBackendHelper *helper) +{ + if (helper->idle_id != 0) + g_source_remove(helper->idle_id); + g_object_unref(helper->self); + g_object_unref(helper->device); + g_free(helper); +} + +static FuUdevBackendHelper * +fu_udev_backend_changed_helper_new(FuUdevBackend *self, FuDevice *device) +{ + FuUdevBackendHelper *helper = g_new0(FuUdevBackendHelper, 1); + helper->self = g_object_ref(self); + helper->device = g_object_ref(device); + return helper; +} + +static gboolean +fu_udev_backend_device_changed_cb(gpointer user_data) +{ + FuUdevBackendHelper *helper = (FuUdevBackendHelper *)user_data; + fu_backend_device_changed(FU_BACKEND(helper->self), helper->device); + helper->idle_id = 0; + g_hash_table_remove(helper->self->changed_idle_ids, + fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(helper->device))); + return FALSE; +} + +static void +fu_udev_backend_device_changed(FuUdevBackend *self, GUdevDevice *udev_device) +{ + const gchar *sysfs_path = g_udev_device_get_sysfs_path(udev_device); + FuUdevBackendHelper *helper; + FuDevice *device_tmp; + + /* not a device we enumerated */ + device_tmp = fu_backend_lookup_by_id(FU_BACKEND(self), sysfs_path); + if (device_tmp == NULL) + return; + + /* run all plugins, with per-device rate limiting */ + if (g_hash_table_remove(self->changed_idle_ids, sysfs_path)) { + g_debug("re-adding rate-limited timeout for %s", sysfs_path); + } else { + g_debug("adding rate-limited timeout for %s", sysfs_path); + } + helper = fu_udev_backend_changed_helper_new(self, device_tmp); + helper->idle_id = g_timeout_add(500, fu_udev_backend_device_changed_cb, helper); + g_hash_table_insert(self->changed_idle_ids, g_strdup(sysfs_path), helper); +} + +static void +fu_udev_backend_uevent_cb(GUdevClient *gudev_client, + const gchar *action, + GUdevDevice *udev_device, + FuUdevBackend *self) +{ + if (g_strcmp0(action, "add") == 0) { + fu_udev_backend_device_add(self, udev_device); + return; + } + if (g_strcmp0(action, "remove") == 0) { + fu_udev_backend_device_remove(self, udev_device); + return; + } + if (g_strcmp0(action, "change") == 0) { + fu_udev_backend_device_changed(self, udev_device); + return; + } +} + +static void +fu_udev_backend_coldplug_subsystem(FuUdevBackend *self, + const gchar *subsystem, + FuProgress *progress) +{ + g_autolist(GObject) devices = NULL; + + devices = g_udev_client_query_by_subsystem(self->gudev_client, subsystem); + if (g_getenv("FWUPD_PROBE_VERBOSE") != NULL) + g_debug("%u devices with subsystem %s", g_list_length(devices), subsystem); + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_name(progress, subsystem); + fu_progress_set_steps(progress, g_list_length(devices)); + for (GList *l = devices; l != NULL; l = l->next) { + GUdevDevice *udev_device = l->data; + fu_progress_set_name(fu_progress_get_child(progress), + g_udev_device_get_sysfs_path(udev_device)); + fu_udev_backend_device_add(self, udev_device); + fu_progress_step_done(progress); + } +} + +static gboolean +fu_udev_backend_coldplug(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuUdevBackend *self = FU_UDEV_BACKEND(backend); + + /* udev watches can only be set up in _init() so set up client now */ + if (self->subsystems->len > 0) { + g_auto(GStrv) subsystems = g_new0(gchar *, self->subsystems->len + 1); + for (guint i = 0; i < self->subsystems->len; i++) { + const gchar *subsystem = g_ptr_array_index(self->subsystems, i); + subsystems[i] = g_strdup(subsystem); + } + self->gudev_client = g_udev_client_new((const gchar *const *)subsystems); + g_signal_connect(G_UDEV_CLIENT(self->gudev_client), + "uevent", + G_CALLBACK(fu_udev_backend_uevent_cb), + self); + } + + /* get all devices of class */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, self->subsystems->len); + for (guint i = 0; i < self->subsystems->len; i++) { + const gchar *subsystem = g_ptr_array_index(self->subsystems, i); + fu_udev_backend_coldplug_subsystem(self, + subsystem, + fu_progress_get_child(progress)); + fu_progress_step_done(progress); + } + + return TRUE; +} + +static void +fu_udev_backend_finalize(GObject *object) +{ + FuUdevBackend *self = FU_UDEV_BACKEND(object); + if (self->gudev_client != NULL) + g_object_unref(self->gudev_client); + if (self->subsystems != NULL) + g_ptr_array_unref(self->subsystems); + g_hash_table_unref(self->changed_idle_ids); + G_OBJECT_CLASS(fu_udev_backend_parent_class)->finalize(object); +} + +static void +fu_udev_backend_init(FuUdevBackend *self) +{ + self->changed_idle_ids = + g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)fu_udev_backend_changed_helper_free); +} + +static void +fu_udev_backend_class_init(FuUdevBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + object_class->finalize = fu_udev_backend_finalize; + klass_backend->coldplug = fu_udev_backend_coldplug; +} + +FuBackend * +fu_udev_backend_new(FuContext *ctx) +{ + FuUdevBackend *self; + GPtrArray *subsystems = fu_context_get_udev_subsystems(ctx); + self = FU_UDEV_BACKEND( + g_object_new(FU_TYPE_UDEV_BACKEND, "name", "udev", "context", ctx, NULL)); + if (subsystems != NULL) + self->subsystems = g_ptr_array_ref(subsystems); + return FU_BACKEND(self); +} diff --git a/fwupd-1.8.6/src/fu-udev-backend.h b/fwupd-1.8.6/src/fu-udev-backend.h new file mode 100644 index 0000000000000000000000000000000000000000..b848611951beb4b01e953b62e56d9a9740a95c5a --- /dev/null +++ b/fwupd-1.8.6/src/fu-udev-backend.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-backend.h" + +#define FU_TYPE_UDEV_BACKEND (fu_udev_backend_get_type()) +G_DECLARE_FINAL_TYPE(FuUdevBackend, fu_udev_backend, FU, UDEV_BACKEND, FuBackend) + +FuBackend * +fu_udev_backend_new(FuContext *ctx); diff --git a/fwupd-1.8.6/src/fu-usb-backend.c b/fwupd-1.8.6/src/fu-usb-backend.c new file mode 100644 index 0000000000000000000000000000000000000000..7c4a0692e17cd5960fa06c9576111913f89ef159 --- /dev/null +++ b/fwupd-1.8.6/src/fu-usb-backend.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuBackend" + +#include "config.h" + +#include + +#include "fu-usb-backend.h" +#include "fu-usb-device.h" + +struct _FuUsbBackend { + FuBackend parent_instance; + GUsbContext *usb_ctx; +}; + +G_DEFINE_TYPE(FuUsbBackend, fu_usb_backend, FU_TYPE_BACKEND) + +#define FU_USB_BACKEND_POLL_INTERVAL_DEFAULT 1000 /* ms */ +#define FU_USB_BACKEND_POLL_INTERVAL_WAIT_REPLUG 5 /* ms */ + +#ifdef _WIN32 +static void +fu_usb_backend_device_notify_flags_cb(FuDevice *device, GParamSpec *pspec, FuBackend *backend) +{ +#if G_USB_CHECK_VERSION(0, 3, 10) + FuUsbBackend *self = FU_USB_BACKEND(backend); + + /* if waiting for a disconnect, set win32 to poll insanely fast -- and set it + * back to the default when the device removal was detected */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { + g_debug("setting USB poll interval to %ums to detect replug", + (guint)FU_USB_BACKEND_POLL_INTERVAL_WAIT_REPLUG); + g_usb_context_set_hotplug_poll_interval(self->usb_ctx, + FU_USB_BACKEND_POLL_INTERVAL_WAIT_REPLUG); + } else { + g_usb_context_set_hotplug_poll_interval(self->usb_ctx, + FU_USB_BACKEND_POLL_INTERVAL_DEFAULT); + } +#else + g_warning("GUsb >= 0.3.10 may be needed to notice device enumeration"); +#endif +} +#endif + +static void +fu_usb_backend_device_added_cb(GUsbContext *ctx, GUsbDevice *usb_device, FuBackend *backend) +{ + g_autoptr(FuUsbDevice) device = NULL; + + /* success */ + device = fu_usb_device_new(fu_backend_get_context(backend), usb_device); + fu_backend_device_added(backend, FU_DEVICE(device)); +} + +static void +fu_usb_backend_device_removed_cb(GUsbContext *ctx, GUsbDevice *usb_device, FuBackend *backend) +{ + FuUsbBackend *self = FU_USB_BACKEND(backend); + FuDevice *device_tmp; + + /* find the device we enumerated */ + device_tmp = + fu_backend_lookup_by_id(FU_BACKEND(self), g_usb_device_get_platform_id(usb_device)); + if (device_tmp != NULL) + fu_backend_device_removed(backend, device_tmp); +} + +static void +fu_usb_backend_context_finalized_cb(gpointer data, GObject *where_the_object_was) +{ + g_critical("GUsbContext %p was finalized from under our feet!", where_the_object_was); +} + +static gboolean +fu_usb_backend_setup(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuUsbBackend *self = FU_USB_BACKEND(backend); + + self->usb_ctx = g_usb_context_new(error); + if (self->usb_ctx == NULL) { + g_prefix_error(error, "failed to get USB context: "); + return FALSE; + } + g_object_weak_ref(G_OBJECT(self->usb_ctx), fu_usb_backend_context_finalized_cb, self); + return TRUE; +} + +static void +fu_usb_backend_coldplug_device(FuUsbBackend *self, GUsbDevice *usb_device, FuProgress *progress) +{ + g_autoptr(FuUsbDevice) device = NULL; + g_autofree gchar *name = NULL; + + name = g_strdup_printf("%04X:%04X", + g_usb_device_get_vid(usb_device), + g_usb_device_get_pid(usb_device)); + fu_progress_set_name(progress, name); + device = fu_usb_device_new(fu_backend_get_context(FU_BACKEND(self)), usb_device); + fu_backend_device_added(FU_BACKEND(self), FU_DEVICE(device)); +} + +static void +fu_usb_backend_coldplug_devices(FuUsbBackend *self, GPtrArray *usb_devices, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, usb_devices->len); + for (guint i = 0; i < usb_devices->len; i++) { + GUsbDevice *usb_device = g_ptr_array_index(usb_devices, i); + fu_usb_backend_coldplug_device(self, usb_device, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + } +} + +static gboolean +fu_usb_backend_coldplug(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuUsbBackend *self = FU_USB_BACKEND(backend); +#if G_USB_CHECK_VERSION(0, 4, 0) + FuContext *ctx = fu_backend_get_context(backend); +#endif + g_autoptr(GPtrArray) usb_devices = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "enumerate"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "add-devices"); + +#if G_USB_CHECK_VERSION(0, 4, 0) + /* save events */ + if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS)) + g_usb_context_set_flags(self->usb_ctx, G_USB_CONTEXT_FLAGS_SAVE_EVENTS); +#endif + + /* no insight */ + g_usb_context_enumerate(self->usb_ctx); + fu_progress_step_done(progress); + + /* add each device */ + usb_devices = g_usb_context_get_devices(self->usb_ctx); + fu_usb_backend_coldplug_devices(self, usb_devices, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + + /* watch for future changes */ + g_signal_connect(G_USB_CONTEXT(self->usb_ctx), + "device-added", + G_CALLBACK(fu_usb_backend_device_added_cb), + self); + g_signal_connect(G_USB_CONTEXT(self->usb_ctx), + "device-removed", + G_CALLBACK(fu_usb_backend_device_removed_cb), + self); + return TRUE; +} + +static gboolean +fu_usb_backend_load(FuBackend *backend, + JsonObject *json_object, + const gchar *tag, + FuBackendLoadFlags flags, + GError **error) +{ +#if G_USB_CHECK_VERSION(0, 4, 0) + FuUsbBackend *self = FU_USB_BACKEND(backend); + return g_usb_context_load(self->usb_ctx, json_object, error); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "GUsb version too old to load backends"); + return FALSE; +#endif +} + +static gboolean +fu_usb_backend_save(FuBackend *backend, + JsonBuilder *json_builder, + const gchar *tag, + FuBackendSaveFlags flags, + GError **error) +{ +#if G_USB_CHECK_VERSION(0, 4, 0) + FuUsbBackend *self = FU_USB_BACKEND(backend); + return g_usb_context_save(self->usb_ctx, json_builder, error); +#else + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "GUsb version too old to save backends"); + return FALSE; +#endif +} + +static void +fu_usb_backend_registered(FuBackend *backend, FuDevice *device) +{ +#ifdef _WIN32 + /* not required */ + if (!FU_IS_USB_DEVICE(device)) + return; + + /* on win32 we need to poll the context faster */ + g_signal_connect(FU_DEVICE(device), + "notify::flags", + G_CALLBACK(fu_usb_backend_device_notify_flags_cb), + backend); +#endif +} + +static void +fu_usb_backend_finalize(GObject *object) +{ + FuUsbBackend *self = FU_USB_BACKEND(object); + + if (self->usb_ctx != NULL) { + g_signal_handlers_disconnect_by_data(G_USB_CONTEXT(self->usb_ctx), self); + g_object_weak_unref(G_OBJECT(self->usb_ctx), + fu_usb_backend_context_finalized_cb, + self); + g_object_unref(self->usb_ctx); + } + G_OBJECT_CLASS(fu_usb_backend_parent_class)->finalize(object); +} + +static void +fu_usb_backend_init(FuUsbBackend *self) +{ +} + +static void +fu_usb_backend_class_init(FuUsbBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuBackendClass *klass_backend = FU_BACKEND_CLASS(klass); + object_class->finalize = fu_usb_backend_finalize; + klass_backend->setup = fu_usb_backend_setup; + klass_backend->coldplug = fu_usb_backend_coldplug; + klass_backend->load = fu_usb_backend_load; + klass_backend->save = fu_usb_backend_save; + klass_backend->registered = fu_usb_backend_registered; +} + +FuBackend * +fu_usb_backend_new(FuContext *ctx) +{ + return FU_BACKEND(g_object_new(FU_TYPE_USB_BACKEND, "name", "usb", "context", ctx, NULL)); +} diff --git a/fwupd-1.8.6/src/fu-usb-backend.h b/fwupd-1.8.6/src/fu-usb-backend.h new file mode 100644 index 0000000000000000000000000000000000000000..9e434e5e5bfe71d30ee21056a76fcd4f9f1ed5e6 --- /dev/null +++ b/fwupd-1.8.6/src/fu-usb-backend.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-backend.h" + +#define FU_TYPE_USB_BACKEND (fu_usb_backend_get_type()) +G_DECLARE_FINAL_TYPE(FuUsbBackend, fu_usb_backend, FU, USB_BACKEND, FuBackend) + +FuBackend * +fu_usb_backend_new(FuContext *ctx); diff --git a/fwupd-1.8.6/src/fu-util-bios-setting.c b/fwupd-1.8.6/src/fu-util-bios-setting.c new file mode 100644 index 0000000000000000000000000000000000000000..e684633aeb4b3896d4a322320cdd7ac81568c473 --- /dev/null +++ b/fwupd-1.8.6/src/fu-util-bios-setting.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2022 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include + +#include "fu-bios-settings-private.h" +#include "fu-util-bios-setting.h" +#include "fu-util-common.h" + +static void +fu_util_bios_setting_update_description(FwupdBiosSetting *setting) +{ + const gchar *new = NULL; + + /* try to look it up from translations */ + new = gettext(fwupd_bios_setting_get_description(setting)); + if (new != NULL) + fwupd_bios_setting_set_description(setting, new); +} + +static const gchar * +fu_util_bios_setting_kind_to_string(FwupdBiosSettingKind kind) +{ + if (kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + /* TRANSLATORS: The BIOS setting can only be changed to fixed values */ + return _("Enumeration"); + } + if (kind == FWUPD_BIOS_SETTING_KIND_INTEGER) { + /* TRANSLATORS: The BIOS setting only accepts integers in a fixed range */ + return _("Integer"); + } + if (kind == FWUPD_BIOS_SETTING_KIND_STRING) { + /* TRANSLATORS: The BIOS setting accepts strings */ + return _("String"); + } + return NULL; +} + +gboolean +fu_util_bios_setting_matches_args(FwupdBiosSetting *setting, gchar **values) +{ + const gchar *name; + + /* no arguments set */ + if (g_strv_length(values) == 0) + return TRUE; + name = fwupd_bios_setting_get_name(setting); + + /* check all arguments */ + for (guint j = 0; j < g_strv_length(values); j++) { + if (g_strcmp0(name, values[j]) == 0) + return TRUE; + } + return FALSE; +} + +gboolean +fu_util_get_bios_setting_as_json(gchar **values, GPtrArray *settings, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + + json_builder_set_member_name(builder, "BiosSettings"); + json_builder_begin_array(builder); + for (guint i = 0; i < settings->len; i++) { + FwupdBiosSetting *setting = g_ptr_array_index(settings, i); + if (fu_util_bios_setting_matches_args(setting, values)) { + fu_util_bios_setting_update_description(setting); + json_builder_begin_object(builder); + fwupd_bios_setting_to_json(setting, builder); + json_builder_end_object(builder); + } + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +gchar * +fu_util_bios_setting_to_string(FwupdBiosSetting *setting, guint idt) +{ + const gchar *tmp; + FwupdBiosSettingKind type; + g_autofree gchar *current_value = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *debug_str = NULL; + debug_str = fwupd_bios_setting_to_string(setting); + g_debug("%s", debug_str); + return NULL; + } + tmp = fwupd_bios_setting_get_name(setting); + fu_string_append(str, idt, tmp, NULL); + + type = fwupd_bios_setting_get_kind(setting); + tmp = fu_util_bios_setting_kind_to_string(type); + if (tmp != NULL) { + /* TRANSLATORS: type of BIOS setting */ + fu_string_append(str, idt + 1, _("Setting type"), tmp); + } + + tmp = fwupd_bios_setting_get_current_value(setting); + if (tmp != NULL) { + current_value = g_strdup(tmp); + } else { + /* TRANSLATORS: tell a user how to get information */ + current_value = g_strdup_printf(_("Run without '%s' to see"), "--no-authenticate"); + } + /* TRANSLATORS: current value of a BIOS setting */ + fu_string_append(str, idt + 1, _("Current Value"), current_value); + + fu_util_bios_setting_update_description(setting); + tmp = fwupd_bios_setting_get_description(setting); + if (tmp != NULL) { + /* TRANSLATORS: description of BIOS setting */ + fu_string_append(str, idt + 1, _("Description"), tmp); + } + + if (fwupd_bios_setting_get_read_only(setting)) { + /* TRANSLATORS: item is TRUE */ + tmp = _("True"); + } else { + /* TRANSLATORS: item is FALSE */ + tmp = _("False"); + } + /* TRANSLATORS: BIOS setting is read only */ + fu_string_append(str, idt + 1, _("Read Only"), tmp); + + if (type == FWUPD_BIOS_SETTING_KIND_INTEGER || type == FWUPD_BIOS_SETTING_KIND_STRING) { + g_autofree gchar *lower = + g_strdup_printf("%" G_GUINT64_FORMAT, + fwupd_bios_setting_get_lower_bound(setting)); + g_autofree gchar *upper = + g_strdup_printf("%" G_GUINT64_FORMAT, + fwupd_bios_setting_get_upper_bound(setting)); + if (type == FWUPD_BIOS_SETTING_KIND_INTEGER) { + g_autofree gchar *scalar = + g_strdup_printf("%" G_GUINT64_FORMAT, + fwupd_bios_setting_get_scalar_increment(setting)); + if (lower != NULL) { + /* TRANSLATORS: Lowest valid integer for BIOS setting */ + fu_string_append(str, idt + 1, _("Minimum value"), lower); + } + if (upper != NULL) { + /* TRANSLATORS: Highest valid integer for BIOS setting */ + fu_string_append(str, idt + 1, _("Maximum value"), upper); + } + if (scalar != NULL) { + /* TRANSLATORS: Scalar increment for integer BIOS setting */ + fu_string_append(str, idt + 1, _("Scalar Increment"), scalar); + } + } else { + if (lower != NULL) { + /* TRANSLATORS: Shortest valid string for BIOS setting */ + fu_string_append(str, idt + 1, _("Minimum length"), lower); + } + if (upper != NULL) { + /* TRANSLATORS: Longest valid string for BIOS setting */ + fu_string_append(str, idt + 1, _("Maximum length"), upper); + } + } + } else if (type == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { + GPtrArray *values = fwupd_bios_setting_get_possible_values(setting); + if (values != NULL && values->len > 0) { + /* TRANSLATORS: Possible values for a bios setting */ + fu_string_append(str, idt + 1, _("Possible Values"), NULL); + for (guint i = 0; i < values->len; i++) { + const gchar *possible = g_ptr_array_index(values, i); + g_autofree gchar *index = g_strdup_printf("%u", i); + fu_string_append(str, idt + 2, index, possible); + } + } + } + return g_string_free(g_steal_pointer(&str), FALSE); +} + +GHashTable * +fu_util_bios_settings_parse_argv(gchar **input, GError **error) +{ + GHashTable *bios_settings; + + /* json input */ + if (g_strv_length(input) == 1) { + g_autoptr(FuBiosSettings) new_bios_settings = fu_bios_settings_new(); + + if (!fu_bios_settings_from_json_file(new_bios_settings, input[0], error)) + return NULL; + + return fu_bios_settings_to_hash_kv(new_bios_settings); + } + + if (g_strv_length(input) == 0 || g_strv_length(input) % 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + /* TRANSLATORS: error message */ + _("Invalid arguments")); + return NULL; + } + + bios_settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + for (guint i = 0; i < g_strv_length(input); i += 2) + g_hash_table_insert(bios_settings, g_strdup(input[i]), g_strdup(input[i + 1])); + + return bios_settings; +} diff --git a/fwupd-1.8.6/src/fu-util-bios-setting.h b/fwupd-1.8.6/src/fu-util-bios-setting.h new file mode 100644 index 0000000000000000000000000000000000000000..3f06eddf67c1924949b939a902b0452215863027 --- /dev/null +++ b/fwupd-1.8.6/src/fu-util-bios-setting.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-bios-setting-private.h" + +gchar * +fu_util_bios_setting_to_string(FwupdBiosSetting *setting, guint idt); +gboolean +fu_util_bios_setting_matches_args(FwupdBiosSetting *setting, gchar **values); +gboolean +fu_util_get_bios_setting_as_json(gchar **values, GPtrArray *settings, GError **error); +GHashTable * +fu_util_bios_settings_parse_argv(gchar **input, GError **error); diff --git a/fwupd-1.8.6/src/fu-util-common.c b/fwupd-1.8.6/src/fu-util-common.c new file mode 100644 index 0000000000000000000000000000000000000000..b17ac73bcc9d5ffc6c7d25a85ba9f5d9c0f95047 --- /dev/null +++ b/fwupd-1.8.6/src/fu-util-common.c @@ -0,0 +1,3037 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include +#include +#include +#ifdef HAVE_GUSB +#include +#endif + +#include +#ifdef HAVE_LIBCURL +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#include "fu-device-private.h" +#include "fu-security-attr-common.h" +#include "fu-util-common.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; +} + +gchar * +fu_util_term_format(const gchar *text, FuUtilTermColor fg_color) +{ + if (g_getenv("NO_COLOR") != NULL) + return g_strdup(text); + return g_strdup_printf("\033[%um\033[1m%s\033[0m", fg_color, text); +} + +#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; + + if (g_getenv("FWUPD_DBUS_SOCKET") != NULL) + return TRUE; + + 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) +{ + gsize title_len; + g_auto(GStrv) lines = NULL; + + if (msg == NULL) + return; + g_print("%s:", title); + + /* pad */ + title_len = fu_strwidth(title) + 1; + lines = g_strsplit(msg, "\n", -1); + for (guint j = 0; lines[j] != NULL; j++) { + for (gsize i = title_len; i < 25; i++) + g_print(" "); + g_print("%s\n", lines[j]); + title_len = 0; + } +} + +guint +fu_util_prompt_for_number(guint maxnum) +{ + gint retval; + guint answer = 0; + + do { + char buffer[64]; + + /* swallow the \n at end of line too */ + if (!fgets(buffer, sizeof(buffer), stdin)) + break; + if (strlen(buffer) == sizeof(buffer) - 1) + continue; + + /* get a number */ + retval = sscanf(buffer, "%u", &answer); + + /* positive */ + if (retval == 1 && answer <= maxnum) + break; + + /* TRANSLATORS: the user isn't reading the question */ + g_print(_("Please enter a number from 0 to %u: "), maxnum); + } while (TRUE); + return answer; +} + +gboolean +fu_util_prompt_for_boolean(gboolean def) +{ + do { + char buffer[4]; + if (!fgets(buffer, sizeof(buffer), stdin)) + continue; + if (strlen(buffer) == sizeof(buffer) - 1) + continue; + if (g_strcmp0(buffer, "\n") == 0) + return def; + buffer[0] = g_ascii_toupper(buffer[0]); + if (g_strcmp0(buffer, "Y\n") == 0) + return TRUE; + if (g_strcmp0(buffer, "N\n") == 0) + return FALSE; + } while (TRUE); + return FALSE; +} + +static gboolean +fu_util_traverse_tree(GNode *n, gpointer data) +{ + FwupdClient *client = FWUPD_CLIENT(data); + guint idx = g_node_depth(n) - 1; + g_autofree gchar *tmp = NULL; + g_auto(GStrv) split = NULL; + + /* get split lines */ + if (FWUPD_IS_DEVICE(n->data)) { + FwupdDevice *dev = FWUPD_DEVICE(n->data); + tmp = fu_util_device_to_string(client, dev, idx); + } else if (FWUPD_IS_REMOTE(n->data)) { + FwupdRemote *remote = FWUPD_REMOTE(n->data); + tmp = fu_util_remote_to_string(remote, idx); + } else if (FWUPD_IS_RELEASE(n->data)) { + FwupdRelease *release = FWUPD_RELEASE(n->data); + tmp = fu_util_release_to_string(release, idx); + g_debug("%s", tmp); + } + + /* root node */ + if (n->data == NULL && g_getenv("FWUPD_VERBOSE") == NULL) { + g_autofree gchar *str = g_strdup_printf("%s %s", + fwupd_client_get_host_vendor(client), + fwupd_client_get_host_product(client)); + g_print("%s\n│\n", str); + return FALSE; + } + + if (n->parent == NULL) + return FALSE; + + if (tmp == NULL) + return FALSE; + split = g_strsplit(tmp, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_autoptr(GString) str = g_string_new(NULL); + + /* header */ + if (i == 0) { + if (g_node_next_sibling(n) == NULL) + g_string_prepend(str, "└─"); + else + g_string_prepend(str, "├─"); + + /* properties */ + } else { + g_string_prepend(str, n->children == NULL ? " " : " │"); + g_string_prepend(str, g_node_next_sibling(n) == NULL ? " " : "│"); + g_string_append(str, " "); + } + + /* ancestors */ + for (GNode *c = n->parent; c != NULL && c->parent != NULL; c = c->parent) { + if (g_node_next_sibling(c) != NULL || idx == 0) { + g_string_prepend(str, "│ "); + continue; + } + g_string_prepend(str, " "); + } + + /* empty line */ + if (split[i][0] == '\0') { + g_print("%s\n", str->str); + continue; + } + + /* dump to the console */ + g_string_append(str, split[i] + (idx * 2)); + g_print("%s\n", str->str); + } + + return FALSE; +} + +void +fu_util_print_tree(FwupdClient *client, GNode *n) +{ + g_node_traverse(n, G_PRE_ORDER, G_TRAVERSE_ALL, -1, fu_util_traverse_tree, client); +} + +static gboolean +fu_util_is_interesting_child(FwupdDevice *dev) +{ + GPtrArray *children = fwupd_device_get_children(dev); + for (guint i = 0; i < children->len; i++) { + FwupdDevice *child = g_ptr_array_index(children, i); + if (fu_util_is_interesting_device(child)) + return TRUE; + } + 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; + if (fwupd_device_get_version(dev) != NULL) + return TRUE; + /* device not plugged in, get-details */ + if (fwupd_device_get_flags(dev) == 0) + return TRUE; + if (fu_util_is_interesting_child(dev)) + return TRUE; + return FALSE; +} + +gchar * +fu_util_get_user_cache_path(const gchar *fn) +{ + const gchar *root = g_get_user_cache_dir(); + g_autofree gchar *basename = g_path_get_basename(fn); + g_autofree gchar *cachedir_legacy = NULL; + + /* if run from a systemd unit, use the cache directory set there */ + if (g_getenv("CACHE_DIRECTORY") != NULL) + root = g_getenv("CACHE_DIRECTORY"); + + /* return the legacy path if it exists rather than renaming it to + * prevent problems when using old and new versions of fwupd */ + cachedir_legacy = g_build_filename(root, "fwupdmgr", NULL); + if (g_file_test(cachedir_legacy, G_FILE_TEST_IS_DIR)) + return g_build_filename(cachedir_legacy, basename, NULL); + + return g_build_filename(root, "fwupd", basename, NULL); +} + +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; +} + +static gchar * +fu_util_get_release_description_with_fallback(FwupdRelease *rel) +{ + g_autoptr(GString) str = g_string_new(NULL); + + /* add what we've got from the vendor */ + if (fwupd_release_get_description(rel) != NULL) + g_string_append(str, fwupd_release_get_description(rel)); + + /* add this client side to get the translations */ + if (fwupd_release_has_flag(rel, FWUPD_RELEASE_FLAG_IS_COMMUNITY)) { + g_string_append_printf( + str, + "

    %s

    ", + /* TRANSLATORS: the vendor did not upload this */ + _("This firmware is provided by LVFS community members and is not " + "provided (or supported) by the original hardware vendor.")); + g_string_append_printf( + str, + "

    %s

    ", + /* TRANSLATORS: if it breaks, you get to keep both pieces */ + _("Installing this update may also void any device warranty.")); + } + + /* this can't be from the LVFS, but the user could be installing a local file */ + if (str->len == 0) { + g_string_append_printf(str, + "

    %s

    ", + /* TRANSLATORS: naughty vendor */ + _("The vendor did not supply any release notes.")); + } + + return g_string_free(g_steal_pointer(&str), FALSE); +} + +gboolean +fu_util_prompt_warning(FwupdDevice *device, + FwupdRelease *release, + const gchar *machine, + GError **error) +{ + FwupdDeviceFlags flags; + gint vercmp; + g_autofree gchar *desc_fb = NULL; + g_autoptr(GString) title = g_string_new(NULL); + g_autoptr(GString) str = g_string_new(NULL); + + /* up, down, or re-install */ + vercmp = fu_version_compare(fwupd_release_get_version(release), + fu_device_get_version(device), + fwupd_device_get_version_format(device)); + if (vercmp < 0) { + g_string_append_printf( + title, + /* TRANSLATORS: message letting the user know an downgrade is available + * %1 is the device name and %2 and %3 are version strings */ + _("Downgrade %s from %s to %s?"), + fwupd_device_get_name(device), + fwupd_device_get_version(device), + fwupd_release_get_version(release)); + } else if (vercmp > 0) { + g_string_append_printf( + title, + /* TRANSLATORS: message letting the user know an upgrade is available + * %1 is the device name and %2 and %3 are version strings */ + _("Upgrade %s from %s to %s?"), + fwupd_device_get_name(device), + fwupd_device_get_version(device), + fwupd_release_get_version(release)); + } else { + g_string_append_printf( + title, + /* TRANSLATORS: message letting the user know an upgrade is available + * %1 is the device name and %2 is a version string */ + _("Reinstall %s to %s?"), + fwupd_device_get_name(device), + fwupd_release_get_version(release)); + } + + /* description is optional */ + desc_fb = fu_util_get_release_description_with_fallback(release); + if (desc_fb != NULL) { + g_autofree gchar *desc = fu_util_convert_description(desc_fb, NULL); + if (desc != NULL) + g_string_append_printf(str, "\n%s", desc); + } + + /* device is not already in bootloader mode so show warning */ + flags = fwupd_device_get_flags(device); + if ((flags & FWUPD_DEVICE_FLAG_IS_BOOTLOADER) == 0) { + /* device may reboot */ + if ((flags & FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) == 0) { + g_string_append(str, "\n\n"); + g_string_append_printf( + str, + /* TRANSLATORS: warn the user before updating, %1 is a device name */ + _("%s and all connected devices may not be usable while updating."), + fwupd_device_get_name(device)); + + /* device can get bricked */ + } else if ((flags & FWUPD_DEVICE_FLAG_SELF_RECOVERY) == 0) { + g_string_append(str, "\n\n"); + /* external device */ + if ((flags & FWUPD_DEVICE_FLAG_INTERNAL) == 0) { + g_string_append_printf(str, + /* TRANSLATORS: warn the user before + * updating, %1 is a device name + */ + _("%s must remain connected for the " + "duration of the update to avoid damage."), + fwupd_device_get_name(device)); + } else if (flags & FWUPD_DEVICE_FLAG_REQUIRE_AC) { + g_string_append_printf( + str, + /* TRANSLATORS: warn the user before updating, %1 is a machine + * name + */ + _("%s must remain plugged into a power source for the duration " + "of the update to avoid damage."), + machine); + } + } + } + fu_util_warning_box(title->str, str->str, 80); + + /* ask for confirmation */ + g_print("\n%s [Y|n]: ", + /* TRANSLATORS: prompt to apply the update */ + _("Perform operation?")); + if (!fu_util_prompt_for_boolean(TRUE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + + /* success */ + return TRUE; +} + +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(FALSE)) + 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(FALSE)) + 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) +{ + g_auto(GStrv) values_copy = g_new0(gchar *, g_strv_length(values) + 1); + + /* clear out bash completion sentinel */ + for (guint i = 0; values[i] != NULL; i++) { + if (g_strcmp0(values[i], "{") == 0) + break; + values_copy[i] = g_strdup(values[i]); + } + + /* find command */ + for (guint i = 0; i < array->len; i++) { + FuUtilCmd *item = g_ptr_array_index(array, i); + if (g_strcmp0(item->name, command) == 0) + return item->callback(priv, values_copy, error); + } + + /* 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 = fu_strwidth(item->name) + 2; + if (item->arguments != NULL) { + g_string_append(string, " "); + g_string_append(string, item->arguments); + len += fu_strwidth(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); +} + +const gchar * +fu_util_branch_for_display(const gchar *branch) +{ + if (branch == NULL) { + /* TRANSLATORS: this is the default branch name when unset */ + return _("default"); + } + return branch; +} + +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-Configuration") == 0) { + /* TRANSLATORS: a specific part of hardware, + * the first %s is the device name, e.g. 'Secure Boot` */ + return g_strdup_printf(_("%s Configuration 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); + } + if (g_strcmp0(cat, "X-ThunderboltController") == 0) { + /* TRANSLATORS: the Thunderbolt controller is a device that + * has other high speed Thunderbolt devices plugged into it; + * the first %s is the system name, e.g. 'ThinkPad P50` */ + return g_strdup_printf(_("%s Thunderbolt Controller Update"), name); + } + if (g_strcmp0(cat, "X-CpuMicrocode") == 0) { + /* TRANSLATORS: the CPU microcode is firmware loaded onto the CPU + * at system bootup */ + return g_strdup_printf(_("%s CPU Microcode Update"), name); + } + if (g_strcmp0(cat, "X-Battery") == 0) { + /* TRANSLATORS: battery refers to the system power source */ + return g_strdup_printf(_("%s Battery Update"), name); + } + if (g_strcmp0(cat, "X-Camera") == 0) { + /* TRANSLATORS: camera can refer to the laptop internal + * camera in the bezel or external USB webcam */ + return g_strdup_printf(_("%s Camera Update"), name); + } + if (g_strcmp0(cat, "X-TPM") == 0) { + /* TRANSLATORS: TPM refers to a Trusted Platform Module */ + return g_strdup_printf(_("%s TPM Update"), name); + } + if (g_strcmp0(cat, "X-Touchpad") == 0) { + /* TRANSLATORS: TouchPad refers to a flat input device */ + return g_strdup_printf(_("%s Touchpad Update"), name); + } + if (g_strcmp0(cat, "X-Mouse") == 0) { + /* TRANSLATORS: Mouse refers to a handheld input device */ + return g_strdup_printf(_("%s Mouse Update"), name); + } + if (g_strcmp0(cat, "X-Keyboard") == 0) { + /* TRANSLATORS: Keyboard refers to an input device for typing */ + return g_strdup_printf(_("%s Keyboard Update"), name); + } + if (g_strcmp0(cat, "X-StorageController") == 0) { + /* TRANSLATORS: Storage Controller is typically a RAID or SAS adapter */ + return g_strdup_printf(_("%s Storage Controller Update"), name); + } + if (g_strcmp0(cat, "X-NetworkInterface") == 0) { + /* TRANSLATORS: Network Interface refers to the physical + * PCI card, not the logical wired connection */ + return g_strdup_printf(_("%s Network Interface Update"), name); + } + if (g_strcmp0(cat, "X-VideoDisplay") == 0) { + /* TRANSLATORS: Video Display refers to the laptop internal display or + * external monitor */ + return g_strdup_printf(_("%s Display Update"), name); + } + if (g_strcmp0(cat, "X-BaseboardManagementController") == 0) { + /* TRANSLATORS: BMC refers to baseboard management controller which + * is the device that updates all the other firmware on the system */ + return g_strdup_printf(_("%s BMC Update"), name); + } + if (g_strcmp0(cat, "X-UsbReceiver") == 0) { + /* TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth + * device that stays in the USB port so the wireless peripheral works */ + return g_strdup_printf(_("%s USB Receiver Update"), name); + } + if (g_strcmp0(cat, "X-Drive") == 0) { + /* TRANSLATORS: drive refers to a storage device, e.g. SATA disk */ + return g_strdup_printf(_("%s Drive Update"), name); + } + if (g_strcmp0(cat, "X-FlashDrive") == 0) { + /* TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC */ + return g_strdup_printf(_("%s Flash Drive Update"), name); + } + if (g_strcmp0(cat, "X-SolidStateDrive") == 0) { + /* TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating + * SATA or NVMe disk */ + return g_strdup_printf(_("%s SSD 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_with_free_func(g_free); + 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 + fu_strwidth(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 += fu_strwidth(start); + g_print("%s", start); + } + if (text != NULL) { + offset += fu_strwidth(text); + g_print("%s", text); + } + if (end != NULL) + offset += fu_strwidth(end); + 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 *title, const gchar *body, guint width) +{ + /* nothing to do */ + if (title == NULL && body == NULL) + return; + + /* header */ + fu_util_warning_box_line("╔", NULL, "╗", "═", width); + + /* optional title */ + if (title != NULL) { + g_autoptr(GPtrArray) lines = fu_util_strsplit_words(title, width - 4); + for (guint j = 0; j < lines->len; j++) { + const gchar *line = g_ptr_array_index(lines, j); + fu_util_warning_box_line("║ ", line, " ║", " ", width); + } + } + + /* join */ + if (title != NULL && body != NULL) + fu_util_warning_box_line("╠", NULL, "╣", "═", width); + + /* optional body */ + if (body != NULL) { + gboolean has_nonempty = FALSE; + g_auto(GStrv) split = g_strsplit(body, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_autoptr(GPtrArray) lines = fu_util_strsplit_words(split[i], width - 4); + if (lines == NULL) { + if (has_nonempty) { + fu_util_warning_box_line("║ ", NULL, " ║", " ", width); + has_nonempty = FALSE; + } + 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); + } + has_nonempty = TRUE; + } + } + + /* footer */ + fu_util_warning_box_line("╚", NULL, "╝", "═", width); +} + +gboolean +fu_util_parse_filter_flags(const gchar *filter, + FwupdDeviceFlags *include, + FwupdDeviceFlags *exclude, + GError **error) +{ + FwupdDeviceFlags tmp; + g_auto(GStrv) strv = g_strsplit(filter, ",", -1); + + g_return_val_if_fail(include != NULL, FALSE); + g_return_val_if_fail(exclude != NULL, FALSE); + + for (guint i = 0; strv[i] != NULL; i++) { + if (g_str_has_prefix(strv[i], "~")) { + tmp = fwupd_device_flag_from_string(strv[i] + 1); + if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown device flag %s", + strv[i] + 1); + return FALSE; + } + if ((tmp & *include) > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already included", + fwupd_device_flag_to_string(tmp)); + return FALSE; + } + if ((tmp & *exclude) > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already excluded", + fwupd_device_flag_to_string(tmp)); + return FALSE; + } + *exclude |= tmp; + } else { + tmp = fwupd_device_flag_from_string(strv[i]); + if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown device flag %s", + strv[i]); + return FALSE; + } + if ((tmp & *exclude) > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already excluded", + fwupd_device_flag_to_string(tmp)); + return FALSE; + } + if ((tmp & *include) > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already included", + fwupd_device_flag_to_string(tmp)); + return FALSE; + } + *include |= tmp; + } + } + + return TRUE; +} + +typedef struct { + guint cnt; + GString *str; +} FuUtilConvertHelper; + +static gboolean +fu_util_convert_description_head_cb(XbNode *n, gpointer user_data) +{ + FuUtilConvertHelper *helper = (FuUtilConvertHelper *)user_data; + helper->cnt++; + + /* start */ + if (g_strcmp0(xb_node_get_element(n), "em") == 0) { + g_string_append(helper->str, "\033[3m"); + } else if (g_strcmp0(xb_node_get_element(n), "strong") == 0) { + g_string_append(helper->str, "\033[1m"); + } else if (g_strcmp0(xb_node_get_element(n), "code") == 0) { + g_string_append(helper->str, "`"); + } else if (g_strcmp0(xb_node_get_element(n), "li") == 0) { + g_string_append(helper->str, "• "); + } else if (g_strcmp0(xb_node_get_element(n), "p") == 0 || + g_strcmp0(xb_node_get_element(n), "ul") == 0 || + g_strcmp0(xb_node_get_element(n), "ol") == 0) { + g_string_append(helper->str, "\n"); + } + + /* text */ + if (xb_node_get_text(n) != NULL) + g_string_append(helper->str, xb_node_get_text(n)); + + return FALSE; +} + +static gboolean +fu_util_convert_description_tail_cb(XbNode *n, gpointer user_data) +{ + FuUtilConvertHelper *helper = (FuUtilConvertHelper *)user_data; + helper->cnt++; + + /* end */ + if (g_strcmp0(xb_node_get_element(n), "em") == 0 || + g_strcmp0(xb_node_get_element(n), "strong") == 0) { + g_string_append(helper->str, "\033[0m"); + } else if (g_strcmp0(xb_node_get_element(n), "code") == 0) { + g_string_append(helper->str, "`"); + } else if (g_strcmp0(xb_node_get_element(n), "li") == 0) { + g_string_append(helper->str, "\n"); + } else if (g_strcmp0(xb_node_get_element(n), "p") == 0) { + g_string_append(helper->str, "\n"); + } + + /* tail */ + if (xb_node_get_tail(n) != NULL) + g_string_append(helper->str, xb_node_get_tail(n)); + + return FALSE; +} + +gchar * +fu_util_convert_description(const gchar *xml, GError **error) +{ + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + FuUtilConvertHelper helper = { + .cnt = 0, + .str = str, + }; + + /* parse XML */ + silo = xb_silo_new_from_xml(xml, error); + if (silo == NULL) + return NULL; + + /* convert to something we can show on the console */ + n = xb_silo_get_root(silo); + xb_node_transmogrify(n, + fu_util_convert_description_head_cb, + fu_util_convert_description_tail_cb, + &helper); + + /* success */ + return fu_strstrip(str->str); +} + +/** + * fu_util_time_to_str: + * @tmp: the time in seconds + * + * Converts a timestamp to a 'pretty' translated string + * + * Returns: (transfer full): A string + * + * Since: 1.3.7 + **/ +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 gchar * +fu_util_device_flag_to_string(guint64 device_flag) +{ + if (device_flag == FWUPD_DEVICE_FLAG_NONE) { + return NULL; + } + if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) { + /* TRANSLATORS: Device cannot be removed easily*/ + return _("Internal device"); + } + if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE || + device_flag == FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN) { + /* TRANSLATORS: Device is updatable in this or any other mode */ + return _("Updatable"); + } + if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) { + /* TRANSLATORS: Update can only be done from offline mode */ + return _("Update requires a reboot"); + } + if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) { + /* TRANSLATORS: Must be plugged in to an outlet */ + return _("System requires external power source"); + } + if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) { + /* TRANSLATORS: Is locked and can be unlocked */ + return _("Device is locked"); + } + if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) { + /* TRANSLATORS: Is found in current metadata */ + return _("Supported on remote server"); + } + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) { + /* TRANSLATORS: Requires a bootloader mode to be manually enabled by the user */ + return _("Requires a bootloader"); + } + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { + /* TRANSLATORS: Requires a reboot to apply firmware or to reload hardware */ + return _("Needs a reboot after installation"); + } + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { + /* TRANSLATORS: Requires system shutdown to apply firmware */ + return _("Needs shutdown after installation"); + } + if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) { + /* TRANSLATORS: Has been reported to a metadata server */ + return _("Reported to remote server"); + } + if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) { + /* TRANSLATORS: User has been notified */ + return _("User has been notified"); + } + if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) { + /* skip */ + return NULL; + } + if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) { + /* TRANSLATORS: Install composite firmware on the parent before the child */ + return _("Install to parent device first"); + } + if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) { + /* TRANSLATORS: Is currently in bootloader mode */ + return _("Is in bootloader mode"); + } + if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) { + /* TRANSLATORS: the hardware is waiting to be replugged */ + return _("Hardware is waiting to be replugged"); + } + if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) { + /* TRANSLATORS: Ignore validation safety checks when flashing this device */ + return _("Ignore validation safety checks"); + } + if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) { + /* skip */ + return NULL; + } + if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) { + /* TRANSLATORS: Device update needs to be separately activated */ + return _("Device update needs activation"); + } + if (device_flag == FWUPD_DEVICE_FLAG_HISTORICAL) { + /* skip */ + return NULL; + } + if (device_flag == FWUPD_DEVICE_FLAG_WILL_DISAPPEAR) { + /* TRANSLATORS: Device will not return after update completes */ + return _("Device will not re-appear after update completes"); + } + if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY) { + /* TRANSLATORS: Device supports some form of checksum verification */ + return _("Cryptographic hash verification is available"); + } + if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) { + /* skip */ + return NULL; + } + if (device_flag == FWUPD_DEVICE_FLAG_DUAL_IMAGE) { + /* TRANSLATORS: Device supports a safety mechanism for flashing */ + return _("Device stages updates"); + } + if (device_flag == FWUPD_DEVICE_FLAG_SELF_RECOVERY) { + /* TRANSLATORS: Device supports a safety mechanism for flashing */ + return _("Device can recover flash failures"); + } + if (device_flag == FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) { + /* TRANSLATORS: Device remains usable during update */ + return _("Device is usable for the duration of the update"); + } + if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) { + /* TRANSLATORS: a version check is required for all firmware */ + return _("Device firmware is required to have a version check"); + } + if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES) { + /* TRANSLATORS: the device cannot update from A->C and has to go A->B->C */ + return _("Device is required to install all provided releases"); + } + if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) { + /* TRANSLATORS: there is more than one supplier of the firmware */ + return _("Device supports switching to a different branch of firmware"); + } + if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) { + /* TRANSLATORS: save the old firmware to disk before installing the new one */ + return _("Device will backup firmware before installing"); + } + if (device_flag == FWUPD_DEVICE_FLAG_WILDCARD_INSTALL) { + /* TRANSLATORS: on some systems certain devices have to have matching versions, + * e.g. the EFI driver for a given network card cannot be different */ + return _("All devices of the same type will be updated at the same time"); + } + if (device_flag == FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE) { + /* TRANSLATORS: some devices can only be updated to a new semver and cannot + * be downgraded or reinstalled with the existing version */ + return _("Only version upgrades are allowed"); + } + if (device_flag == FWUPD_DEVICE_FLAG_UNREACHABLE) { + /* TRANSLATORS: currently unreachable, perhaps because it is in a lower power state + * or is out of wireless range */ + return _("Device is unreachable"); + } + if (device_flag == FWUPD_DEVICE_FLAG_AFFECTS_FDE) { + /* TRANSLATORS: we might ask the user the recovery key when next booting Windows */ + return _("Full disk encryption secrets may be invalidated when updating"); + } + if (device_flag == FWUPD_DEVICE_FLAG_END_OF_LIFE) { + /* TRANSLATORS: the vendor is no longer supporting the device */ + return _("End of life"); + } + if (device_flag == FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) { + /* TRANSLATORS: firmware is verified on-device the payload using strong crypto */ + return _("Signed Payload"); + } + if (device_flag == FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD) { + /* TRANSLATORS: firmware payload is unsigned and it is possible to modify it */ + return _("Unsigned Payload"); + } + if (device_flag == FWUPD_DEVICE_FLAG_SKIPS_RESTART) { + /* skip */ + return NULL; + } + if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) { + return NULL; + } + return NULL; +} + +static const gchar * +fu_util_update_state_to_string(FwupdUpdateState update_state) +{ + if (update_state == FWUPD_UPDATE_STATE_PENDING) { + /* TRANSLATORS: the update state of the specific device */ + return _("Pending"); + } + if (update_state == FWUPD_UPDATE_STATE_SUCCESS) { + /* TRANSLATORS: the update state of the specific device */ + return _("Success"); + } + if (update_state == FWUPD_UPDATE_STATE_FAILED) { + /* TRANSLATORS: the update state of the specific device */ + return _("Failed"); + } + if (update_state == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { + /* TRANSLATORS: the update state of the specific device */ + return _("Transient failure"); + } + if (update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { + /* TRANSLATORS: the update state of the specific device */ + return _("Needs reboot"); + } + return NULL; +} + +static gchar * +fu_util_device_problem_to_string(FwupdClient *client, FwupdDevice *dev, FwupdDeviceProblem problem) +{ + if (problem == FWUPD_DEVICE_PROBLEM_NONE) + return NULL; + if (problem == FWUPD_DEVICE_PROBLEM_UNKNOWN) + return NULL; + if (problem == FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW) { + if (fwupd_client_get_battery_level(client) == FWUPD_BATTERY_LEVEL_INVALID || + fwupd_client_get_battery_threshold(client) == FWUPD_BATTERY_LEVEL_INVALID) { + /* TRANSLATORS: as in laptop battery power */ + return g_strdup(_("System power is too low to perform the update")); + } + return g_strdup_printf( + /* TRANSLATORS: as in laptop battery power */ + _("System power is too low to perform the update (%u%%, requires %u%%)"), + fwupd_client_get_battery_level(client), + fwupd_client_get_battery_threshold(client)); + } + if (problem == FWUPD_DEVICE_PROBLEM_UNREACHABLE) { + /* TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode */ + return g_strdup(_("Device is unreachable, or out of wireless range")); + } + if (problem == FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW) { + if (fwupd_device_get_battery_level(dev) == FWUPD_BATTERY_LEVEL_INVALID || + fwupd_device_get_battery_threshold(dev) == FWUPD_BATTERY_LEVEL_INVALID) { + /* TRANSLATORS: for example the batteries *inside* the Bluetooth mouse */ + return g_strdup(_("Device battery power is too low")); + } + /* TRANSLATORS: for example the batteries *inside* the Bluetooth mouse */ + return g_strdup_printf(_("Device battery power is too low (%u%%, requires %u%%)"), + fwupd_device_get_battery_level(dev), + fwupd_device_get_battery_threshold(dev)); + } + if (problem == FWUPD_DEVICE_PROBLEM_UPDATE_PENDING) { + /* TRANSLATORS: usually this is when we're waiting for a reboot */ + return g_strdup(_("Device is waiting for the update to be applied")); + } + if (problem == FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER) { + /* TRANSLATORS: as in, wired mains power for a laptop */ + return g_strdup(_("Device requires AC power to be connected")); + } + if (problem == FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED) { + /* TRANSLATORS: lid means "laptop top cover" */ + return g_strdup(_("Device cannot be used while the lid is closed")); + } + if (problem == FWUPD_DEVICE_PROBLEM_IS_EMULATED) { + /* TRANSLATORS: emulated means we are pretending to be a different model */ + return g_strdup(_("Device is emulated")); + } + if (problem == FWUPD_DEVICE_PROBLEM_MISSING_LICENSE) { + /* TRANSLATORS: The device cannot be updated due to missing vendor's license." */ + return g_strdup(_("Device requires a software license to update")); + } + return NULL; +} + +gchar * +fu_util_device_to_string(FwupdClient *client, FwupdDevice *dev, guint idt) +{ + FwupdUpdateState state; + GPtrArray *guids = fwupd_device_get_guids(dev); + GPtrArray *issues = fwupd_device_get_issues(dev); + GPtrArray *vendor_ids = fwupd_device_get_vendor_ids(dev); + GPtrArray *instance_ids = fwupd_device_get_instance_ids(dev); + const gchar *tmp; + const gchar *tmp2; + guint64 flags = fwupd_device_get_flags(dev); + guint64 modified = fwupd_device_get_modified(dev); + g_autoptr(GHashTable) ids = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* some fields are intentionally not included and are only shown in --verbose */ + if (g_getenv("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *debug_str = NULL; + debug_str = fwupd_device_to_string(dev); + g_debug("%s", debug_str); + return NULL; + } + + tmp = fwupd_device_get_name(dev); + if (tmp == NULL) { + /* TRANSLATORS: Name of hardware */ + tmp = _("Unknown Device"); + } + fu_string_append(str, idt, tmp, NULL); + + tmp = fwupd_device_get_id(dev); + if (tmp != NULL) { + /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ + fu_string_append(str, idt + 1, _("Device ID"), tmp); + } + + /* summary */ + tmp = fwupd_device_get_summary(dev); + if (tmp != NULL) { + /* TRANSLATORS: one line summary of device */ + fu_string_append(str, idt + 1, _("Summary"), tmp); + } + + /* description */ + tmp = fwupd_device_get_description(dev); + if (tmp != NULL) { + g_autofree gchar *desc = NULL; + desc = fu_util_convert_description(tmp, NULL); + if (desc == NULL) + desc = g_strdup(tmp); + /* TRANSLATORS: multiline description of device */ + fu_string_append(str, idt + 1, _("Description"), desc); + } + + /* versions */ + tmp = fwupd_device_get_version(dev); + if (tmp != NULL) { + g_autoptr(GString) verstr = g_string_new(tmp); + if (fwupd_device_get_version_build_date(dev) != 0) { + guint64 value = fwupd_device_get_version_build_date(dev); + g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc((gint64)value); + g_autofree gchar *datestr = g_date_time_format(date, "%F"); + g_string_append_printf(verstr, " [%s]", datestr); + } + if (flags & FWUPD_DEVICE_FLAG_HISTORICAL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: version number of previous firmware */ + _("Previous version"), + verstr->str); + } else { + /* TRANSLATORS: version number of current firmware */ + fu_string_append(str, idt + 1, _("Current version"), verstr->str); + } + } + tmp = fwupd_device_get_version_lowest(dev); + if (tmp != NULL) { + /* TRANSLATORS: smallest version number installable on device */ + fu_string_append(str, idt + 1, _("Minimum Version"), tmp); + } + tmp = fwupd_device_get_version_bootloader(dev); + if (tmp != NULL) { + /* TRANSLATORS: firmware version of bootloader */ + fu_string_append(str, idt + 1, _("Bootloader Version"), tmp); + } + + /* vendor */ + tmp = fwupd_device_get_vendor(dev); + if (tmp != NULL && vendor_ids->len > 0) { + g_autofree gchar *strv = fu_strjoin(", ", vendor_ids); + g_autofree gchar *both = g_strdup_printf("%s (%s)", tmp, strv); + /* TRANSLATORS: manufacturer of hardware */ + fu_string_append(str, idt + 1, _("Vendor"), both); + } else if (tmp != NULL) { + /* TRANSLATORS: manufacturer of hardware */ + fu_string_append(str, idt + 1, _("Vendor"), tmp); + } else if (vendor_ids->len > 0) { + g_autofree gchar *strv = fu_strjoin("|", vendor_ids); + /* TRANSLATORS: manufacturer of hardware */ + fu_string_append(str, idt + 1, _("Vendor"), strv); + } + + /* branch */ + if (fwupd_device_get_branch(dev) != NULL) { + fu_string_append( + str, + idt + 1, + /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ + _("Release Branch"), + fwupd_device_get_branch(dev)); + } + + /* install duration */ + if (fwupd_device_get_install_duration(dev) > 0) { + g_autofree gchar *time = + fu_util_time_to_str(fwupd_device_get_install_duration(dev)); + /* TRANSLATORS: length of time the update takes to apply */ + fu_string_append(str, idt + 1, _("Install Duration"), time); + } + + /* serial # */ + tmp = fwupd_device_get_serial(dev); + if (tmp != NULL) { + /* TRANSLATORS: serial number of hardware */ + fu_string_append(str, idt + 1, _("Serial Number"), tmp); + } + + /* update state */ + state = fwupd_device_get_update_state(dev); + if (state != FWUPD_UPDATE_STATE_UNKNOWN) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: hardware state, e.g. "pending" */ + _("Update State"), + fu_util_update_state_to_string(state)); + + if (state == FWUPD_UPDATE_STATE_SUCCESS) { + tmp = fwupd_device_get_update_message(dev); + if (tmp != NULL) { + g_autofree gchar *color = + fu_util_term_format(tmp, FU_UTIL_TERM_COLOR_BLUE); + fu_string_append( + str, + idt + 1, + /* TRANSLATORS: helpful messages from last update */ + _("Update Message"), + color); + } + } + } + + /* battery, but only if we're not about to show the same info as an inhibit */ + if (!fwupd_device_has_problem(dev, FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW)) { + if (fwupd_device_get_battery_level(dev) != FWUPD_BATTERY_LEVEL_INVALID && + fwupd_device_get_battery_threshold(dev) != FWUPD_BATTERY_LEVEL_INVALID) { + g_autofree gchar *val = NULL; + /* TRANSLATORS: first percentage is current value, 2nd percentage is the + * lowest limit the firmware update is allowed for the update to happen */ + val = g_strdup_printf(_("%u%% (threshold %u%%)"), + fwupd_device_get_battery_level(dev), + fwupd_device_get_battery_threshold(dev)); + /* TRANSLATORS: refers to the battery inside the peripheral device */ + fu_string_append(str, idt + 1, _("Battery"), val); + } else if (fwupd_device_get_battery_level(dev) != FWUPD_BATTERY_LEVEL_INVALID) { + g_autofree gchar *val = NULL; + val = g_strdup_printf("%u%%", fwupd_device_get_battery_level(dev)); + /* TRANSLATORS: refers to the battery inside the peripheral device */ + fu_string_append(str, idt + 1, _("Battery"), val); + } + } + + /* either show enumerated [translated] problems or the synthesized update error */ + if (fwupd_device_get_problems(dev) == FWUPD_DEVICE_PROBLEM_NONE) { + tmp = fwupd_device_get_update_error(dev); + if (tmp != NULL) { + g_autofree gchar *color = fu_util_term_format(tmp, FU_UTIL_TERM_COLOR_RED); + /* TRANSLATORS: error message from last update attempt */ + fu_string_append(str, idt + 1, _("Update Error"), color); + } + } else { + /* TRANSLATORS: reasons the device is not updatable */ + tmp = _("Problems"); + for (guint i = 0; i < 64; i++) { + FwupdDeviceProblem problem = (guint64)1 << i; + g_autofree gchar *bullet = NULL; + g_autofree gchar *desc = NULL; + g_autofree gchar *color = NULL; + + if (!fwupd_device_has_problem(dev, problem)) + continue; + desc = fu_util_device_problem_to_string(client, dev, problem); + if (desc == NULL) + continue; + bullet = g_strdup_printf("• %s", desc); + color = fu_util_term_format(bullet, FU_UTIL_TERM_COLOR_RED); + fu_string_append(str, idt + 1, tmp, color); + tmp = NULL; + } + } + + /* modified date: for history devices */ + if (modified > 0) { + g_autoptr(GDateTime) date = NULL; + g_autofree gchar *time_str = NULL; + date = g_date_time_new_from_unix_utc(modified); + time_str = g_date_time_format(date, "%F %R"); + /* TRANSLATORS: the original time/date the device was modified */ + fu_string_append(str, idt + 1, _("Last modified"), time_str); + } + + /* all GUIDs for this hardware, with IDs if available */ + ids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(instance_ids, i); + g_hash_table_insert(ids, + fwupd_guid_hash_string(instance_id), + g_strdup(instance_id)); + } + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index(guids, i); + const gchar *instance_id = g_hash_table_lookup(ids, guid); + g_autofree gchar *guid_src = NULL; + + /* instance IDs are only available as root */ + if (instance_id == NULL) { + guid_src = g_strdup(guid); + } else { + guid_src = g_strdup_printf("%s ← %s", guid, instance_id); + } + if (i == 0) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: global ID common to all similar hardware */ + ngettext("GUID", "GUIDs", guids->len), + guid_src); + } else { + fu_string_append(str, idt + 1, "", guid_src); + } + } + + /* TRANSLATORS: description of device ability */ + tmp = _("Device Flags"); + for (guint i = 0; i < 64; i++) { + if ((flags & ((guint64)1 << i)) == 0) + continue; + tmp2 = fu_util_device_flag_to_string((guint64)1 << i); + if (tmp2 == NULL) + continue; + /* header */ + if (tmp != NULL) { + g_autofree gchar *bullet = NULL; + bullet = g_strdup_printf("• %s", tmp2); + fu_string_append(str, idt + 1, tmp, bullet); + tmp = NULL; + } else { + g_autofree gchar *bullet = NULL; + bullet = g_strdup_printf("• %s", tmp2); + fu_string_append(str, idt + 1, "", bullet); + } + } + for (guint i = 0; i < issues->len; i++) { + const gchar *issue = g_ptr_array_index(issues, i); + fu_string_append(str, + idt + 1, + /* TRANSLATORS: issue fixed with the release, e.g. CVE */ + i == 0 ? ngettext("Issue", "Issues", issues->len) : "", + issue); + } + + return g_string_free(g_steal_pointer(&str), FALSE); +} + +const gchar * +fu_util_plugin_flag_to_string(FwupdPluginFlags plugin_flag) +{ + if (plugin_flag == FWUPD_PLUGIN_FLAG_UNKNOWN) + return NULL; + if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE) + return NULL; + if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING) + return NULL; + if (plugin_flag == FWUPD_PLUGIN_FLAG_REQUIRE_HWID) { + /* TRANSLATORS: Plugin is active only if hardware is found */ + return _("Enabled if hardware matches"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_NONE) { + /* TRANSLATORS: Plugin is active and in use */ + return _("Enabled"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) { + /* TRANSLATORS: Plugin is inactive and not used */ + return _("Disabled"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) { + /* TRANSLATORS: not required for this system */ + return _("Required hardware was not found"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) { + /* TRANSLATORS: system is not booted in UEFI mode */ + return _("UEFI firmware can not be updated in legacy BIOS mode"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) { + /* TRANSLATORS: capsule updates are an optional BIOS feature */ + return _("UEFI capsule updates not available or enabled in firmware setup"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) { + /* TRANSLATORS: user needs to run a command */ + return _("Firmware updates disabled; run 'fwupdmgr unlock' to enable"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_AUTH_REQUIRED) { + /* TRANSLATORS: user needs to run a command */ + return _("Authentication details are required"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_SECURE_CONFIG) { + /* TRANSLATORS: no peeking */ + return _("Configuration is only readable by the system administrator"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_MODULAR) { + /* TRANSLATORS: the plugin was created from a .so object, and was not built-in */ + return _("Loaded from an external module"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) { + /* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */ + return _("Required efivarfs filesystem was not found"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) { + /* TRANSLATORS: partition refers to something on disk, again, hey Arch users */ + return _("UEFI ESP partition not detected or configured"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) { + /* TRANSLATORS: Failed to open plugin, hey Arch users */ + return _("Plugin dependencies missing"); + } + if (plugin_flag == FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD) { + /* TRANSLATORS: The kernel does not support this plugin */ + return _("Running kernel is too old"); + } + + /* fall back for unknown types */ + return fwupd_plugin_flag_to_string(plugin_flag); +} + +static gchar * +fu_util_plugin_flag_to_cli_text(FwupdPluginFlags plugin_flag) +{ + switch (plugin_flag) { + case FWUPD_PLUGIN_FLAG_UNKNOWN: + case FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE: + case FWUPD_PLUGIN_FLAG_USER_WARNING: + return NULL; + case FWUPD_PLUGIN_FLAG_NONE: + case FWUPD_PLUGIN_FLAG_REQUIRE_HWID: + case FWUPD_PLUGIN_FLAG_MODULAR: + return fu_util_term_format(fu_util_plugin_flag_to_string(plugin_flag), + FU_UTIL_TERM_COLOR_GREEN); + case FWUPD_PLUGIN_FLAG_DISABLED: + case FWUPD_PLUGIN_FLAG_NO_HARDWARE: + return fu_util_term_format(fu_util_plugin_flag_to_string(plugin_flag), + FU_UTIL_TERM_COLOR_BLACK); + case FWUPD_PLUGIN_FLAG_LEGACY_BIOS: + case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: + case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: + case FWUPD_PLUGIN_FLAG_AUTH_REQUIRED: + case FWUPD_PLUGIN_FLAG_SECURE_CONFIG: + case FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED: + case FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND: + case FWUPD_PLUGIN_FLAG_KERNEL_TOO_OLD: + default: + break; + } + + /* fall back for unknown types */ + return g_strdup(fwupd_plugin_flag_to_string(plugin_flag)); +} + +gchar * +fu_util_plugin_to_string(FwupdPlugin *plugin, guint idt) +{ + GString *str = g_string_new(NULL); + const gchar *hdr; + guint64 flags = fwupd_plugin_get_flags(plugin); + + fu_string_append(str, idt, fwupd_plugin_get_name(plugin), NULL); + + /* TRANSLATORS: description of plugin state, e.g. disabled */ + hdr = _("Flags"); + if (flags == 0x0) { + g_autofree gchar *tmp = fu_util_plugin_flag_to_cli_text(flags); + g_autofree gchar *li = g_strdup_printf("• %s", tmp); + fu_string_append(str, idt + 1, hdr, li); + } else { + for (guint i = 0; i < 64; i++) { + g_autofree gchar *li = NULL; + g_autofree gchar *tmp = NULL; + if ((flags & ((guint64)1 << i)) == 0) + continue; + tmp = fu_util_plugin_flag_to_cli_text((guint64)1 << i); + if (tmp == NULL) + continue; + li = g_strdup_printf("• %s", tmp); + fu_string_append(str, idt + 1, hdr, li); + + /* clear header */ + hdr = ""; + } + } + + return g_string_free(str, FALSE); +} + +static const gchar * +fu_util_license_to_string(const gchar *license) +{ + if (license == NULL) { + /* TRANSLATORS: we don't know the license of the update */ + return _("Unknown"); + } + if (g_strcmp0(license, "LicenseRef-proprietary") == 0 || + g_strcmp0(license, "proprietary") == 0) { + /* TRANSLATORS: a non-free software license */ + return _("Proprietary"); + } + return license; +} + +static const gchar * +fu_util_release_urgency_to_string(FwupdReleaseUrgency release_urgency) +{ + if (release_urgency == FWUPD_RELEASE_URGENCY_LOW) { + /* TRANSLATORS: the release urgency */ + return _("Low"); + } + if (release_urgency == FWUPD_RELEASE_URGENCY_MEDIUM) { + /* TRANSLATORS: the release urgency */ + return _("Medium"); + } + if (release_urgency == FWUPD_RELEASE_URGENCY_HIGH) { + /* TRANSLATORS: the release urgency */ + return _("High"); + } + if (release_urgency == FWUPD_RELEASE_URGENCY_CRITICAL) { + /* TRANSLATORS: the release urgency */ + return _("Critical"); + } + /* TRANSLATORS: unknown release urgency */ + return _("Unknown"); +} + +static const gchar * +fu_util_release_flag_to_string(FwupdReleaseFlags release_flag) +{ + if (release_flag == FWUPD_RELEASE_FLAG_NONE) + return NULL; + if (release_flag == FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD) { + /* TRANSLATORS: We verified the payload against the server */ + return _("Trusted payload"); + } + if (release_flag == FWUPD_RELEASE_FLAG_TRUSTED_METADATA) { + /* TRANSLATORS: We verified the meatdata against the server */ + return _("Trusted metadata"); + } + if (release_flag == FWUPD_RELEASE_FLAG_IS_UPGRADE) { + /* TRANSLATORS: version is newer */ + return _("Is upgrade"); + } + if (release_flag == FWUPD_RELEASE_FLAG_IS_DOWNGRADE) { + /* TRANSLATORS: version is older */ + return _("Is downgrade"); + } + if (release_flag == FWUPD_RELEASE_FLAG_BLOCKED_VERSION) { + /* TRANSLATORS: version cannot be installed due to policy */ + return _("Blocked version"); + } + if (release_flag == FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL) { + /* TRANSLATORS: version cannot be installed due to policy */ + return _("Not approved"); + } + if (release_flag == FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH) { + /* TRANSLATORS: is not the main firmware stream */ + return _("Alternate branch"); + } + if (release_flag == FWUPD_RELEASE_FLAG_IS_COMMUNITY) { + /* TRANSLATORS: is not supported by the vendor */ + return _("Community supported"); + } + + /* fall back for unknown types */ + return fwupd_release_flag_to_string(release_flag); +} + +gchar * +fu_util_release_to_string(FwupdRelease *rel, guint idt) +{ + const gchar *title; + const gchar *tmp2; + GPtrArray *issues = fwupd_release_get_issues(rel); + GPtrArray *tags = fwupd_release_get_tags(rel); + guint64 flags = fwupd_release_get_flags(rel); + g_autofree gchar *desc_fb = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + g_return_val_if_fail(FWUPD_IS_RELEASE(rel), NULL); + + fu_string_append(str, idt, fwupd_release_get_name(rel), NULL); + + /* TRANSLATORS: version number of new firmware */ + fu_string_append(str, idt + 1, _("New version"), fwupd_release_get_version(rel)); + + if (fwupd_release_get_remote_id(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: the server the file is coming from */ + _("Remote ID"), + fwupd_release_get_remote_id(rel)); + } + if (fwupd_release_get_id(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: the exact component on the server */ + _("Release ID"), + fwupd_release_get_id(rel)); + } + if (fwupd_release_get_branch(rel) != NULL) { + fu_string_append( + str, + idt + 1, + /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ + _("Branch"), + fwupd_release_get_branch(rel)); + } + if (fwupd_release_get_summary(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: one line summary of device */ + _("Summary"), + fwupd_release_get_summary(rel)); + } + if (fwupd_release_get_name_variant_suffix(rel) != NULL) { + fu_string_append( + str, + idt + 1, + /* TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') */ + _("Variant"), + fwupd_release_get_name_variant_suffix(rel)); + } + fu_string_append(str, + idt + 1, + /* TRANSLATORS: e.g. GPLv2+, Proprietary etc */ + _("License"), + fu_util_license_to_string(fwupd_release_get_license(rel))); + if (fwupd_release_get_size(rel) != 0) { + g_autofree gchar *tmp = NULL; + tmp = g_format_size(fwupd_release_get_size(rel)); + /* TRANSLATORS: file size of the download */ + fu_string_append(str, idt + 1, _("Size"), tmp); + } + if (fwupd_release_get_created(rel) != 0) { + gint64 value = (gint64)fwupd_release_get_created(rel); + g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc(value); + g_autofree gchar *tmp = g_date_time_format(date, "%F"); + /* TRANSLATORS: when the update was built */ + fu_string_append(str, idt + 1, _("Created"), tmp); + } + if (fwupd_release_get_urgency(rel) != FWUPD_RELEASE_URGENCY_UNKNOWN) { + FwupdReleaseUrgency tmp = fwupd_release_get_urgency(rel); + fu_string_append(str, + idt + 1, + /* TRANSLATORS: how important the release is */ + _("Urgency"), + fu_util_release_urgency_to_string(tmp)); + } + if (fwupd_release_get_details_url(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: more details about the update link */ + _("Details"), + fwupd_release_get_details_url(rel)); + } + if (fwupd_release_get_source_url(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: source (as in code) link */ + _("Source"), + fwupd_release_get_source_url(rel)); + } + if (fwupd_release_get_vendor(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: manufacturer of hardware */ + _("Vendor"), + fwupd_release_get_vendor(rel)); + } + if (fwupd_release_get_install_duration(rel) != 0) { + g_autofree gchar *tmp = + fu_util_time_to_str(fwupd_release_get_install_duration(rel)); + /* TRANSLATORS: length of time the update takes to apply */ + fu_string_append(str, idt + 1, _("Duration"), tmp); + } + if (fwupd_release_get_update_message(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: helpful messages for the update */ + _("Update Message"), + fwupd_release_get_update_message(rel)); + } + if (fwupd_release_get_update_image(rel) != NULL) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: helpful image for the update */ + _("Update Image"), + fwupd_release_get_update_image(rel)); + } + + /* TRANSLATORS: release attributes */ + title = _("Release Flags"); + for (guint i = 0; i < 64; i++) { + g_autofree gchar *bullet = NULL; + if ((flags & ((guint64)1 << i)) == 0) + continue; + tmp2 = fu_util_release_flag_to_string((guint64)1 << i); + if (tmp2 == NULL) + continue; + bullet = g_strdup_printf("• %s", tmp2); + fu_string_append(str, idt + 1, title, bullet); + title = ""; + } + + desc_fb = fu_util_get_release_description_with_fallback(rel); + if (desc_fb != NULL) { + g_autofree gchar *desc = NULL; + desc = fu_util_convert_description(desc_fb, NULL); + if (desc == NULL) + desc = g_strdup(fwupd_release_get_description(rel)); + /* TRANSLATORS: multiline description of device */ + fu_string_append(str, idt + 1, _("Description"), desc); + } + for (guint i = 0; i < issues->len; i++) { + const gchar *issue = g_ptr_array_index(issues, i); + if (i == 0) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: issue fixed with the release, e.g. CVE */ + ngettext("Issue", "Issues", issues->len), + issue); + } else { + fu_string_append(str, idt + 1, "", issue); + } + } + if (tags->len > 0) { + g_autofree gchar *tag_strs = fu_strjoin(", ", tags); + fu_string_append(str, + idt + 1, + /* TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 */ + ngettext("Tag", "Tags", tags->len), + tag_strs); + } + + return g_string_free(g_steal_pointer(&str), FALSE); +} + +gchar * +fu_util_remote_to_string(FwupdRemote *remote, guint idt) +{ + FwupdRemoteKind kind = fwupd_remote_get_kind(remote); + FwupdKeyringKind keyring_kind = fwupd_remote_get_keyring_kind(remote); + const gchar *tmp; + gint priority; + g_autoptr(GString) str = g_string_new(NULL); + + g_return_val_if_fail(FWUPD_IS_REMOTE(remote), NULL); + + fu_string_append(str, idt, fwupd_remote_get_title(remote), NULL); + + /* TRANSLATORS: remote identifier, e.g. lvfs-testing */ + fu_string_append(str, idt + 1, _("Remote ID"), fwupd_remote_get_id(remote)); + + /* TRANSLATORS: remote type, e.g. remote or local */ + fu_string_append(str, idt + 1, _("Type"), fwupd_remote_kind_to_string(kind)); + + if (keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { + fu_string_append(str, + idt + 1, + /* TRANSLATORS: keyring type, e.g. GPG or PKCS7 */ + _("Keyring"), + fwupd_keyring_kind_to_string(keyring_kind)); + } + + fu_string_append(str, + idt + 1, + /* TRANSLATORS: if the remote is enabled */ + _("Enabled"), + fwupd_remote_get_enabled(remote) ? "true" : "false"); + + tmp = fwupd_remote_get_checksum(remote); + if (tmp != NULL) { + /* TRANSLATORS: remote checksum */ + fu_string_append(str, idt + 1, _("Checksum"), tmp); + } + + /* optional parameters */ + if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && fwupd_remote_get_age(remote) > 0 && + fwupd_remote_get_age(remote) != G_MAXUINT64) { + const gchar *unit = "s"; + gdouble age = fwupd_remote_get_age(remote); + g_autofree gchar *age_str = NULL; + if (age > 60) { + age /= 60.f; + unit = "m"; + } + if (age > 60) { + age /= 60.f; + unit = "h"; + } + if (age > 24) { + age /= 24.f; + unit = "d"; + } + if (age > 7) { + age /= 7.f; + unit = "w"; + } + age_str = g_strdup_printf("%.2f%s", age, unit); + /* TRANSLATORS: the age of the metadata */ + fu_string_append(str, idt + 1, _("Age"), age_str); + } + priority = fwupd_remote_get_priority(remote); + if (priority != 0) { + g_autofree gchar *priority_str = NULL; + priority_str = g_strdup_printf("%i", priority); + /* TRANSLATORS: the numeric priority */ + fu_string_append(str, idt + 1, _("Priority"), priority_str); + } + tmp = fwupd_remote_get_username(remote); + if (tmp != NULL) { + /* TRANSLATORS: remote filename base */ + fu_string_append(str, idt + 1, _("Username"), tmp); + } + tmp = fwupd_remote_get_password(remote); + if (tmp != NULL) { + g_autofree gchar *hidden = g_strnfill(fu_strwidth(tmp), '*'); + /* TRANSLATORS: remote filename base */ + fu_string_append(str, idt + 1, _("Password"), hidden); + } + tmp = fwupd_remote_get_filename_cache(remote); + if (tmp != NULL) { + /* TRANSLATORS: filename of the local file */ + fu_string_append(str, idt + 1, _("Filename"), tmp); + } + tmp = fwupd_remote_get_filename_cache_sig(remote); + if (tmp != NULL) { + /* TRANSLATORS: filename of the local file */ + fu_string_append(str, idt + 1, _("Filename Signature"), tmp); + } + tmp = fwupd_remote_get_filename_source(remote); + if (tmp != NULL) { + /* TRANSLATORS: full path of the remote.conf file */ + fu_string_append(str, idt + 1, _("Filename Source"), tmp); + } + tmp = fwupd_remote_get_metadata_uri(remote); + if (tmp != NULL) { + /* TRANSLATORS: remote URI */ + fu_string_append(str, idt + 1, _("Metadata URI"), tmp); + } + tmp = fwupd_remote_get_metadata_uri_sig(remote); + if (tmp != NULL) { + /* TRANSLATORS: remote URI */ + fu_string_append(str, idt + 1, _("Metadata Signature"), tmp); + } + tmp = fwupd_remote_get_firmware_base_uri(remote); + if (tmp != NULL) { + /* TRANSLATORS: remote URI */ + fu_string_append(str, idt + 1, _("Firmware Base URI"), tmp); + } + tmp = fwupd_remote_get_report_uri(remote); + if (tmp != NULL) { + /* TRANSLATORS: URI to send success/failure reports */ + fu_string_append(str, idt + 1, _("Report URI"), tmp); + fu_string_append(str, + idt + 1, + /* TRANSLATORS: Boolean value to automatically send reports */ + _("Automatic Reporting"), + fwupd_remote_get_automatic_reports(remote) ? "true" : "false"); + } + + return g_string_free(g_steal_pointer(&str), FALSE); +} + +const gchar * +fu_util_request_get_message(FwupdRequest *req) +{ + if (fwupd_request_has_flag(req, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE)) { + if (g_strcmp0(fwupd_request_get_id(req), FWUPD_REQUEST_ID_REMOVE_REPLUG) == 0) { + /* TRANSLATORS: warning message shown after update has been scheduled */ + return _("The update will continue when the device USB cable has been " + "unplugged and then re-inserted."); + } + if (g_strcmp0(fwupd_request_get_id(req), FWUPD_REQUEST_ID_REMOVE_USB_CABLE) == 0) { + /* TRANSLATORS: warning message shown after update has been scheduled */ + return _("The update will continue when the device USB cable has been " + "unplugged."); + } + if (g_strcmp0(fwupd_request_get_id(req), FWUPD_REQUEST_ID_PRESS_UNLOCK) == 0) { + /* TRANSLATORS: warning message */ + return _("Press unlock on the device to continue the update process."); + } + if (g_strcmp0(fwupd_request_get_id(req), FWUPD_REQUEST_ID_DO_NOT_POWER_OFF) == 0) { + /* TRANSLATORS: warning message shown after update has been scheduled */ + return _("Do not turn off your computer or remove the AC adaptor " + "while the update is in progress."); + } + } + return fwupd_request_get_message(req); +} + +static void +fu_security_attr_append_str(FwupdSecurityAttr *attr, + GString *str, + FuSecurityAttrToStringFlags flags) +{ + g_autofree gchar *name = NULL; + + /* hide obsoletes by default */ + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) && + (flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES) == 0) + return; + + name = fu_security_attr_get_name(attr); + if (name == NULL) + name = g_strdup(fwupd_security_attr_get_appstream_id(attr)); + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + g_string_append(str, "✦ "); + } else if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + g_string_append(str, "✔ "); + } else { + g_string_append(str, "✘ "); + } + g_string_append_printf(str, "%s:", name); + for (guint i = fu_strwidth(name); i < 30; i++) + g_string_append(str, " "); + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + g_autofree gchar *fmt = fu_util_term_format(fu_security_attr_get_result(attr), + FU_UTIL_TERM_COLOR_YELLOW); + g_string_append(str, fmt); + } else if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + g_autofree gchar *fmt = fu_util_term_format(fu_security_attr_get_result(attr), + FU_UTIL_TERM_COLOR_GREEN); + g_string_append(str, fmt); + } else { + g_autofree gchar *fmt = + fu_util_term_format(fu_security_attr_get_result(attr), FU_UTIL_TERM_COLOR_RED); + g_string_append(str, fmt); + } + if ((flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS) > 0 && + fwupd_security_attr_get_url(attr) != NULL) { + g_string_append_printf(str, ": %s", fwupd_security_attr_get_url(attr)); + } + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { + /* TRANSLATORS: this is shown as a suffix for obsoleted tests */ + g_string_append_printf(str, " %s", _("(obsoleted)")); + } + g_string_append_printf(str, "\n"); +} + +static gchar * +fu_util_security_event_to_string(FwupdSecurityAttr *attr) +{ + g_autofree gchar *name = NULL; + struct { + const gchar *appstream_id; + FwupdSecurityAttrResult result_old; + FwupdSecurityAttrResult result_new; + const gchar *text; + } items[] = {{FWUPD_SECURITY_ATTR_ID_IOMMU, + FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("IOMMU device protection enabled")}, + {FWUPD_SECURITY_ATTR_ID_IOMMU, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND, + /* TRANSLATORS: HSI event title */ + _("IOMMU device protection disabled")}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS, + FWUPD_SECURITY_ATTR_RESULT_TAINTED, + FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED, + NULL}, + {FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS, + FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED, + FWUPD_SECURITY_ATTR_RESULT_TAINTED, + NULL}, + {FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS, + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + NULL}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED, + FWUPD_SECURITY_ATTR_RESULT_TAINTED, + FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED, + /* TRANSLATORS: HSI event title */ + _("Kernel is tainted")}, + {FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED, + FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED, + FWUPD_SECURITY_ATTR_RESULT_TAINTED, + /* TRANSLATORS: HSI event title */ + _("Kernel is no longer tainted")}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("Kernel lockdown disabled")}, + {FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("Kernel lockdown enabled")}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("Pre-boot DMA protection is disabled")}, + {FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("Pre-boot DMA protection is enabled")}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("Secure Boot disabled")}, + {FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT, + FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED, + FWUPD_SECURITY_ATTR_RESULT_ENABLED, + /* TRANSLATORS: HSI event title */ + _("Secure Boot enabled")}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR, + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN, + FWUPD_SECURITY_ATTR_RESULT_VALID, + /* TRANSLATORS: HSI event title */ + _("All TPM PCRs are valid")}, + {FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR, + FWUPD_SECURITY_ATTR_RESULT_VALID, + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID, + /* TRANSLATORS: HSI event title */ + _("A TPM PCR is now an invalid value")}, + {FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR, + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID, + FWUPD_SECURITY_ATTR_RESULT_VALID, + /* TRANSLATORS: HSI event title */ + _("All TPM PCRs are now valid")}, + /* ------------------------------------------*/ + {FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0, + FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND, + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID, + /* TRANSLATORS: HSI event title */ + _("TPM PCR0 reconstruction is invalid")}, + {FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0, + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID, + FWUPD_SECURITY_ATTR_RESULT_VALID, + /* TRANSLATORS: HSI event title */ + _("TPM PCR0 reconstruction is now valid")}, + {NULL, 0, 0, NULL}}; + + /* sanity check */ + if (fwupd_security_attr_get_appstream_id(attr) == NULL) + return NULL; + if (fwupd_security_attr_get_result(attr) == FWUPD_SECURITY_ATTR_RESULT_UNKNOWN && + fwupd_security_attr_get_result_fallback(attr) == FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) + return NULL; + + /* look for prepared text */ + for (guint i = 0; items[i].appstream_id != NULL; i++) { + if (g_strcmp0(fwupd_security_attr_get_appstream_id(attr), items[i].appstream_id) == + 0 && + fwupd_security_attr_get_result(attr) == items[i].result_new && + fwupd_security_attr_get_result_fallback(attr) == items[i].result_old) + return g_strdup(items[i].text); + } + + /* disappeared */ + if (fwupd_security_attr_get_result(attr) == FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) { + name = fu_security_attr_get_name(attr); + return g_strdup_printf( + /* TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS region". + %2 refers to a result value, e.g. "Invalid" */ + _("%s disappeared: %s"), + name, + fu_security_attr_result_to_string( + fwupd_security_attr_get_result_fallback(attr))); + } + + /* appeared */ + if (fwupd_security_attr_get_result_fallback(attr) == FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) { + name = fu_security_attr_get_name(attr); + return g_strdup_printf( + /* TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". + %2 refers to a result value, e.g. "Invalid" */ + _("%s appeared: %s"), + name, + fu_security_attr_result_to_string(fwupd_security_attr_get_result(attr))); + } + + /* fall back to something sensible */ + name = fu_security_attr_get_name(attr); + return g_strdup_printf( + /* TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform key". + * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" */ + _("%s changed: %s → %s"), + name, + fu_security_attr_result_to_string(fwupd_security_attr_get_result_fallback(attr)), + fu_security_attr_result_to_string(fwupd_security_attr_get_result(attr))); +} + +gchar * +fu_util_security_events_to_string(GPtrArray *events, FuSecurityAttrToStringFlags strflags) +{ + g_autoptr(GString) str = g_string_new(NULL); + + /* debugging */ + if (g_getenv("FWUPD_VERBOSE") != NULL) { + for (guint i = 0; i < events->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(events, i); + g_autofree gchar *tmp = fwupd_security_attr_to_string(attr); + g_debug("%s", tmp); + } + } + + for (guint i = 0; i < events->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(events, i); + g_autoptr(GDateTime) date = NULL; + g_autofree gchar *dtstr = NULL; + g_autofree gchar *check = NULL; + g_autofree gchar *eventstr = NULL; + + /* skip events that have either been added or removed with no prior value */ + if (fwupd_security_attr_get_result(attr) == FWUPD_SECURITY_ATTR_RESULT_UNKNOWN || + fwupd_security_attr_get_result_fallback(attr) == + FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) + continue; + + date = g_date_time_new_from_unix_utc((gint64)fwupd_security_attr_get_created(attr)); + dtstr = g_date_time_format(date, "%F %T"); + eventstr = fu_util_security_event_to_string(attr); + if (eventstr == NULL) + continue; + if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { + check = fu_util_term_format("✔", FU_UTIL_TERM_COLOR_GREEN); + } else { + check = fu_util_term_format("✘", FU_UTIL_TERM_COLOR_RED); + } + if (str->len == 0) { + /* TRANSLATORS: title for host security events */ + g_string_append_printf(str, "%s\n", _("Host Security Events")); + } + g_string_append_printf(str, " %s: %s %s\n", dtstr, check, eventstr); + } + + /* no output required */ + if (str->len == 0) + return NULL; + + /* success */ + return g_string_free(g_steal_pointer(&str), FALSE); +} + +gchar * +fu_util_security_issues_to_string(GPtrArray *devices) +{ + g_autoptr(GString) str = g_string_new(NULL); + + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *device = g_ptr_array_index(devices, i); + GPtrArray *issues = fwupd_device_get_issues(device); + if (issues->len == 0) + continue; + if (str->len == 0) { + g_string_append_printf( + str, + "%s\n", + /* TRANSLATORS: now list devices with unfixed high-priority issues */ + _("There are devices with issues:")); + } + g_string_append_printf(str, + "\n %s — %s:\n", + fwupd_device_get_vendor(device), + fwupd_device_get_name(device)); + for (guint j = 0; j < issues->len; j++) { + const gchar *issue = g_ptr_array_index(issues, j); + g_string_append_printf(str, " • %s\n", issue); + } + } + + /* no output required */ + if (str->len == 0) + return NULL; + + /* success */ + return g_string_free(g_steal_pointer(&str), FALSE); +} + +gchar * +fu_util_security_attrs_to_string(GPtrArray *attrs, FuSecurityAttrToStringFlags strflags) +{ + FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE; + const FwupdSecurityAttrFlags hpi_suffixes[] = { + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE, + FWUPD_SECURITY_ATTR_FLAG_NONE, + }; + GString *str = g_string_new(NULL); + gboolean low_help = FALSE; + gboolean runtime_help = FALSE; + gboolean pcr0_help = FALSE; + + for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) { + gboolean has_header = FALSE; + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, i); + if (fwupd_security_attr_get_level(attr) != j) + continue; + if (!has_header) { + g_string_append_printf(str, "\n\033[1mHSI-%u\033[0m\n", j); + has_header = TRUE; + } + fu_security_attr_append_str(attr, str, strflags); + /* make sure they have at least HSI-1 */ + if (j < FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT && + !fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + low_help = TRUE; + + /* check for PCR0 not matching */ + if (g_strcmp0(fwupd_security_attr_get_appstream_id(attr), + FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0 && + fwupd_security_attr_get_result(attr) == + FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) + pcr0_help = TRUE; + } + } + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, i); + flags |= fwupd_security_attr_get_flags(attr); + } + for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) { + if (flags & hpi_suffixes[j]) { + g_string_append_printf(str, + "\n\033[1m%s -%s\033[0m\n", + /* TRANSLATORS: this is the HSI suffix */ + _("Runtime Suffix"), + fwupd_security_attr_flag_to_suffix(hpi_suffixes[j])); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, i); + if (!fwupd_security_attr_has_flag(attr, hpi_suffixes[j])) + continue; + if (fwupd_security_attr_has_flag( + attr, + FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) && + !fwupd_security_attr_has_flag(attr, + FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) + runtime_help = TRUE; + fu_security_attr_append_str(attr, str, strflags); + } + } + } + + if (low_help) { + g_string_append_printf( + str, + "\n%s\n » %s\n", + /* TRANSLATORS: this is instructions on how to improve the HSI security level */ + _("This system has a low HSI security level."), + "https://fwupd.github.io/hsi.html#low-security-level"); + } + if (runtime_help) { + g_string_append_printf( + str, + "\n%s\n » %s\n", + /* TRANSLATORS: this is instructions on how to improve the HSI suffix */ + _("This system has HSI runtime issues."), + "https://fwupd.github.io/hsi.html#hsi-runtime-suffix"); + } + + if (pcr0_help) { + g_string_append_printf( + str, + "\n%s\n » %s\n", + /* TRANSLATORS: this is more background on a security measurement problem */ + _("The TPM PCR0 differs from reconstruction."), + "https://fwupd.github.io/hsi.html#pcr0-tpm-event-log-reconstruction"); + } + + return g_string_free(str, FALSE); +} + +gboolean +fu_util_send_report(FwupdClient *client, + const gchar *report_uri, + const gchar *data, + const gchar *sig, + gchar **uri, /* (nullable) (out) */ + GError **error) +{ + const gchar *server_msg = NULL; + JsonNode *json_root; + JsonObject *json_object; + g_autofree gchar *str = NULL; + g_autoptr(GBytes) upload_response = NULL; + g_autoptr(JsonParser) json_parser = NULL; + + /* POST request */ + upload_response = fwupd_client_upload_bytes(client, + report_uri, + data, + sig, + FWUPD_CLIENT_UPLOAD_FLAG_NONE, + NULL, + error); + if (upload_response == NULL) + return FALSE; + + /* server returned nothing, and probably exploded in a ball of flames */ + if (g_bytes_get_size(upload_response) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to upload to %s", + report_uri); + return FALSE; + } + + /* parse JSON reply */ + json_parser = json_parser_new(); + str = g_strndup(g_bytes_get_data(upload_response, NULL), g_bytes_get_size(upload_response)); + if (!json_parser_load_from_data(json_parser, str, -1, error)) { + g_prefix_error(error, "Failed to parse JSON response from '%s': ", str); + return FALSE; + } + json_root = json_parser_get_root(json_parser); + if (json_root == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "JSON response was malformed: '%s'", + str); + return FALSE; + } + json_object = json_node_get_object(json_root); + if (json_object == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "JSON response object was malformed: '%s'", + str); + return FALSE; + } + + /* get any optional server message */ + if (json_object_has_member(json_object, "msg")) + server_msg = json_object_get_string_member(json_object, "msg"); + + /* server reported failed */ + if (!json_object_get_boolean_member(json_object, "success")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "Server rejected report: %s", + server_msg != NULL ? server_msg : "unspecified"); + return FALSE; + } + + /* server wanted us to see the message */ + if (server_msg != NULL) { + g_debug("server message: %s", server_msg); + if (g_strstr_len(server_msg, -1, "known issue") != NULL && + json_object_has_member(json_object, "uri")) { + if (uri != NULL) + *uri = g_strdup(json_object_get_string_member(json_object, "uri")); + } + } + + /* success */ + return TRUE; +} + +gint +fu_util_sort_devices_by_flags_cb(gconstpointer a, gconstpointer b) +{ + FuDevice *dev_a = *((FuDevice **)a); + FuDevice *dev_b = *((FuDevice **)b); + + if ((!fu_device_has_flag(dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && + fu_device_has_flag(dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || + (!fu_device_has_flag(dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && + fu_device_has_flag(dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) + return -1; + if ((fu_device_has_flag(dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fu_device_has_flag(dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || + (fu_device_has_flag(dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && + !fu_device_has_flag(dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) + return 1; + + return 0; +} + +static gint +fu_util_device_order_compare(FuDevice *device1, FuDevice *device2) +{ + if (fu_device_get_order(device1) < fu_device_get_order(device2)) + return -1; + if (fu_device_get_order(device1) > fu_device_get_order(device2)) + return 1; + return 0; +} + +gint +fu_util_device_order_sort_cb(gconstpointer a, gconstpointer b) +{ + FuDevice *device_a = *((FuDevice **)a); + FuDevice *device_b = *((FuDevice **)b); + return fu_util_device_order_compare(device_a, device_b); +} + +gboolean +fu_util_switch_branch_warning(FwupdDevice *dev, + FwupdRelease *rel, + gboolean assume_yes, + GError **error) +{ + const gchar *desc_markup = NULL; + g_autofree gchar *desc_plain = NULL; + g_autofree gchar *title = NULL; + g_autoptr(GString) desc_full = g_string_new(NULL); + + /* warn the user if the vendor is different */ + if (g_strcmp0(fwupd_device_get_vendor(dev), fwupd_release_get_vendor(rel)) != 0) { + g_string_append_printf( + desc_full, + /* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */ + _("The firmware from %s is not " + "supplied by %s, the hardware vendor."), + fwupd_release_get_vendor(rel), + fwupd_device_get_vendor(dev)); + g_string_append(desc_full, "\n\n"); + g_string_append_printf(desc_full, + /* TRANSLATORS: %1 is the device vendor name */ + _("Your hardware may be damaged using this firmware, " + "and installing this release may void any warranty " + "with %s."), + fwupd_device_get_vendor(dev)); + g_string_append(desc_full, "\n\n"); + } + + /* from the in the AppStream data */ + desc_markup = fwupd_release_get_description(rel); + if (desc_markup == NULL) + return TRUE; + desc_plain = fu_util_convert_description(desc_markup, error); + if (desc_plain == NULL) + return FALSE; + g_string_append(desc_full, desc_plain); + + /* TRANSLATORS: show and ask user to confirm -- + * %1 is the old branch name, %2 is the new branch name */ + title = g_strdup_printf(_("Switch branch from %s to %s?"), + fu_util_branch_for_display(fwupd_device_get_branch(dev)), + fu_util_branch_for_display(fwupd_release_get_branch(rel))); + fu_util_warning_box(title, desc_full->str, 80); + if (!assume_yes) { + /* ask for permission */ + g_print("\n%s [y|N]: ", + /* TRANSLATORS: should the branch be changed */ + _("Do you understand the consequences of changing the firmware branch?")); + if (!fu_util_prompt_for_boolean(FALSE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Declined branch switch"); + return FALSE; + } + } + return TRUE; +} + +gboolean +fu_util_prompt_warning_fde(FwupdDevice *dev, GError **error) +{ + const gchar *url = "https://github.com/fwupd/fwupd/wiki/Full-Disk-Encryption-Detected"; + g_autoptr(GString) str = g_string_new(NULL); + + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_AFFECTS_FDE)) + return TRUE; + + g_string_append( + str, + /* TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM */ + _("Some of the platform secrets may be invalidated when updating this firmware.")); + g_string_append(str, " "); + g_string_append(str, + /* TRANSLATORS: 'recovery key' here refers to a code, rather than a physical + metal thing */ + _("Please ensure you have the volume recovery key before continuing.")); + g_string_append(str, "\n\n"); + g_string_append_printf(str, + /* TRANSLATORS: the %1 is a URL to a wiki page */ + _("See %s for more details."), + url); + /* TRANSLATORS: title text, shown as a warning */ + fu_util_warning_box(_("Full Disk Encryption Detected"), str->str, 80); + + /* ask for confirmation */ + g_print("\n%s [Y|n]: ", + /* TRANSLATORS: prompt to apply the update */ + _("Perform operation?")); + if (!fu_util_prompt_for_boolean(TRUE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + return TRUE; +} + +void +fu_util_show_unsupported_warn(void) +{ +#ifndef SUPPORTED_BUILD + g_autofree gchar *fmt = NULL; + + if (g_getenv("FWUPD_SUPPORTED") != NULL) + return; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_YELLOW); + g_printerr("%s %s\n", + fmt, + /* TRANSLATORS: unsupported build of the package */ + _("This package has not been validated, it may not work properly.")); +#endif +} + +#ifdef HAVE_LIBCURL_7_62_0 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup) +#endif + +gboolean +fu_util_is_url(const gchar *perhaps_url) +{ +#ifdef HAVE_LIBCURL_7_62_0 + g_autoptr(CURLU) h = curl_url(); + return curl_url_set(h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK; +#else + return g_str_has_prefix(perhaps_url, "http://") || + g_str_has_prefix(perhaps_url, "https://"); +#endif +} + +gboolean +fu_util_setup_interactive_console(GError **error) +{ +#ifdef _WIN32 + HANDLE hOut; + DWORD dwMode = 0; + + /* enable VT sequences */ + hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to get stdout [%u]", + (guint)GetLastError()); + return FALSE; + } + if (!GetConsoleMode(hOut, &dwMode)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to get mode [%u]", + (guint)GetLastError()); + return FALSE; + } + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to set mode [%u]", + (guint)GetLastError()); + return FALSE; + } + if (!SetConsoleOutputCP(CP_UTF8)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to set output UTF-8 [%u]", + (guint)GetLastError()); + return FALSE; + } + if (!SetConsoleCP(CP_UTF8)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "failed to set UTF-8 [%u]", + (guint)GetLastError()); + return FALSE; + } +#else + if (isatty(fileno(stdout)) == 0) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "not a TTY"); + return FALSE; + } +#endif + /* success */ + return TRUE; +} + +gboolean +fu_util_print_builder(JsonBuilder *builder, GError **error) +{ + g_autofree gchar *data = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* 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; +} + +typedef enum { + FU_UTIL_DEPENDENCY_KIND_UNKNOWN, + FU_UTIL_DEPENDENCY_KIND_RUNTIME, + FU_UTIL_DEPENDENCY_KIND_COMPILE, +} FuUtilDependencyKind; + +static const gchar * +fu_util_dependency_kind_to_string(FuUtilDependencyKind dependency_kind) +{ + if (dependency_kind == FU_UTIL_DEPENDENCY_KIND_RUNTIME) + return "runtime"; + if (dependency_kind == FU_UTIL_DEPENDENCY_KIND_COMPILE) + return "compile"; + return NULL; +} + +static gchar * +fu_util_parse_project_dependency(const gchar *str, FuUtilDependencyKind *dependency_kind) +{ + g_return_val_if_fail(str != NULL, NULL); + if (g_str_has_prefix(str, "RuntimeVersion(")) { + gsize strsz = strlen(str); + if (dependency_kind != NULL) + *dependency_kind = FU_UTIL_DEPENDENCY_KIND_RUNTIME; + return g_strndup(str + 15, strsz - 16); + } + if (g_str_has_prefix(str, "CompileVersion(")) { + gsize strsz = strlen(str); + if (dependency_kind != NULL) + *dependency_kind = FU_UTIL_DEPENDENCY_KIND_COMPILE; + return g_strndup(str + 15, strsz - 16); + } + return g_strdup(str); +} + +static gboolean +fu_util_print_version_key_valid(const gchar *key) +{ + g_return_val_if_fail(key != NULL, FALSE); + if (g_str_has_prefix(key, "RuntimeVersion")) + return TRUE; + if (g_str_has_prefix(key, "CompileVersion")) + return TRUE; + return FALSE; +} + +gboolean +fu_util_project_versions_as_json(GHashTable *metadata, GError **error) +{ + GHashTableIter iter; + const gchar *key; + const gchar *value; + g_autoptr(JsonBuilder) builder = json_builder_new(); + + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Versions"); + json_builder_begin_array(builder); + g_hash_table_iter_init(&iter, metadata); + while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) { + FuUtilDependencyKind dependency_kind = FU_UTIL_DEPENDENCY_KIND_UNKNOWN; + g_autofree gchar *project = NULL; + + /* add version keys */ + if (!fu_util_print_version_key_valid(key)) + continue; + project = fu_util_parse_project_dependency(key, &dependency_kind); + json_builder_begin_object(builder); + if (dependency_kind != FU_UTIL_DEPENDENCY_KIND_UNKNOWN) { + json_builder_set_member_name(builder, "Type"); + json_builder_add_string_value( + builder, + fu_util_dependency_kind_to_string(dependency_kind)); + } + json_builder_set_member_name(builder, "AppstreamId"); + json_builder_add_string_value(builder, project); + json_builder_set_member_name(builder, "Version"); + json_builder_add_string_value(builder, value); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +gchar * +fu_util_project_versions_to_string(GHashTable *metadata) +{ + GHashTableIter iter; + const gchar *key; + const gchar *value; + g_autoptr(GString) str = g_string_new(NULL); + + g_hash_table_iter_init(&iter, metadata); + while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) { + FuUtilDependencyKind dependency_kind = FU_UTIL_DEPENDENCY_KIND_UNKNOWN; + g_autofree gchar *project = NULL; + + /* print version keys */ + if (!fu_util_print_version_key_valid(key)) + continue; + project = fu_util_parse_project_dependency(key, &dependency_kind); + g_string_append_printf(str, + "%-10s%-30s%s\n", + fu_util_dependency_kind_to_string(dependency_kind), + project, + value); + } + return g_string_free(g_steal_pointer(&str), FALSE); +} diff --git a/fwupd-1.8.6/src/fu-util-common.h b/fwupd-1.8.6/src/fu-util-common.h new file mode 100644 index 0000000000000000000000000000000000000000..bdd1476a4d83a7956e5539578ba02dfc5fefd37c --- /dev/null +++ b/fwupd-1.8.6/src/fu-util-common.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include + +#include "fwupd-bios-setting-private.h" +#include "fwupd-security-attr-private.h" + +/* 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; + +typedef enum { + FU_SECURITY_ATTR_TO_STRING_FLAG_NONE = 0, + FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES = 1 << 0, + FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS = 1 << 1, + /*< private >*/ + FU_SECURITY_ATTR_TO_STRING_FLAG_LAST +} FuSecurityAttrToStringFlags; + +typedef enum { + FU_UTIL_TERM_COLOR_BLACK = 30, + FU_UTIL_TERM_COLOR_RED = 31, + FU_UTIL_TERM_COLOR_GREEN = 32, + FU_UTIL_TERM_COLOR_YELLOW = 33, + FU_UTIL_TERM_COLOR_BLUE = 34, + FU_UTIL_TERM_COLOR_MAGENTA = 35, + FU_UTIL_TERM_COLOR_CYAN = 36, + FU_UTIL_TERM_COLOR_WHITE = 37, +} FuUtilTermColor; + +void +fu_util_print_data(const gchar *title, const gchar *msg); +gchar * +fu_util_term_format(const gchar *text, FuUtilTermColor fg_color); +guint +fu_util_prompt_for_number(guint maxnum); +gboolean +fu_util_prompt_for_boolean(gboolean def); + +void +fu_util_print_tree(FwupdClient *client, GNode *n); +gboolean +fu_util_is_interesting_device(FwupdDevice *dev); +gchar * +fu_util_get_user_cache_path(const gchar *fn); + +void +fu_util_warning_box(const gchar *title, const gchar *body, guint width); +gboolean +fu_util_prompt_warning(FwupdDevice *device, + FwupdRelease *release, + const gchar *machine, + GError **error); +gboolean +fu_util_prompt_warning_fde(FwupdDevice *dev, GError **error); +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_branch_for_display(const gchar *branch); +const gchar * +fu_util_request_get_message(FwupdRequest *req); + +const gchar * +fu_util_get_systemd_unit(void); +gboolean +fu_util_using_correct_daemon(GError **error); + +gboolean +fu_util_parse_filter_flags(const gchar *filter, + FwupdDeviceFlags *include, + FwupdDeviceFlags *exclude, + GError **error); +gchar * +fu_util_convert_description(const gchar *xml, GError **error); +gchar * +fu_util_time_to_str(guint64 tmp); + +gchar * +fu_util_device_to_string(FwupdClient *client, FwupdDevice *dev, guint idt); +gchar * +fu_util_plugin_to_string(FwupdPlugin *plugin, guint idt); +const gchar * +fu_util_plugin_flag_to_string(FwupdPluginFlags plugin_flag); +gchar * +fu_util_release_to_string(FwupdRelease *rel, guint idt); +gchar * +fu_util_remote_to_string(FwupdRemote *remote, guint idt); +gchar * +fu_util_security_attrs_to_string(GPtrArray *attrs, FuSecurityAttrToStringFlags flags); +gchar * +fu_util_security_events_to_string(GPtrArray *events, FuSecurityAttrToStringFlags flags); +gchar * +fu_util_security_issues_to_string(GPtrArray *devices); +gboolean +fu_util_send_report(FwupdClient *client, + const gchar *report_uri, + const gchar *data, + const gchar *sig, + gchar **uri, + GError **error); +gint +fu_util_sort_devices_by_flags_cb(gconstpointer a, gconstpointer b); +gint +fu_util_device_order_sort_cb(gconstpointer a, gconstpointer b); + +gboolean +fu_util_switch_branch_warning(FwupdDevice *dev, + FwupdRelease *rel, + gboolean assume_yes, + GError **error); +void +fu_util_show_unsupported_warn(void); +gboolean +fu_util_is_url(const gchar *perhaps_url); +gboolean +fu_util_setup_interactive_console(GError **error); +gboolean +fu_util_print_builder(JsonBuilder *builder, GError **error); +gchar * +fu_util_project_versions_to_string(GHashTable *metadata); +gboolean +fu_util_project_versions_as_json(GHashTable *metadata, GError **error); diff --git a/fwupd-1.8.6/src/fu-util.c b/fwupd-1.8.6/src/fu-util.c new file mode 100644 index 0000000000000000000000000000000000000000..bd95fdf9502a93cce4111845032b07cc92ec0deb --- /dev/null +++ b/fwupd-1.8.6/src/fu-util.c @@ -0,0 +1,4724 @@ +/* + * Copyright (C) 2015 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuMain" + +#include "config.h" + +#include + +#include +#include +#include +#ifdef HAVE_GIO_UNIX +#include +#include +#endif +#include +#include +#include + +#include "fwupd-common-private.h" +#include "fwupd-device-private.h" +#include "fwupd-plugin-private.h" +#include "fwupd-release-private.h" +#include "fwupd-remote-private.h" + +#include "fu-plugin-private.h" +#include "fu-polkit-agent.h" +#include "fu-progressbar.h" +#include "fu-util-bios-setting.h" +#include "fu-util-common.h" + +#ifdef HAVE_SYSTEMD +#include "fu-systemd.h" +#endif + +/* custom return code */ +#define EXIT_NOTHING_TO_DO 2 + +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; + GMainContext *main_ctx; + GOptionContext *context; + FwupdInstallFlags flags; + FwupdClientDownloadFlags download_flags; + FwupdClient *client; + FuProgressbar *progressbar; + gboolean no_remote_check; + gboolean no_metadata_check; + gboolean no_reboot_check; + gboolean no_unreported_check; + gboolean no_safety_check; + gboolean no_device_prompt; + gboolean assume_yes; + gboolean sign; + gboolean show_all; + gboolean disable_ssl_strict; + gboolean as_json; + /* only valid in update and downgrade */ + FuUtilOperation current_operation; + FwupdDevice *current_device; + GPtrArray *post_requests; + FwupdDeviceFlags completion_flags; + FwupdDeviceFlags filter_include; + FwupdDeviceFlags filter_exclude; +}; + +static gboolean +fu_util_report_history(FuUtilPrivate *priv, gchar **values, GError **error); +static FwupdDevice * +fu_util_get_device_by_id(FuUtilPrivate *priv, const gchar *id, GError **error); + +static void +fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtilPrivate *priv) +{ + if (priv->as_json) + return; + fu_progressbar_update(priv->progressbar, + fwupd_client_get_status(priv->client), + fwupd_client_get_percentage(priv->client)); +} + +static void +fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuUtilPrivate *priv) +{ + /* action has not been assigned yet */ + if (priv->current_operation == FU_UTIL_OPERATION_UNKNOWN) + return; + + /* nothing sensible to show */ + if (fwupd_request_get_message(request) == NULL) + return; + + /* show this now */ + if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_IMMEDIATE) { + g_autofree gchar *fmt = NULL; + g_autofree gchar *tmp = NULL; + + /* TRANSLATORS: the user needs to do something, e.g. remove the device */ + fmt = fu_util_term_format(_("Action Required:"), FU_UTIL_TERM_COLOR_RED); + tmp = g_strdup_printf("%s %s", fmt, fwupd_request_get_message(request)); + fu_progressbar_set_title(priv->progressbar, tmp); + } + + /* save for later */ + if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_POST) + g_ptr_array_add(priv->post_requests, g_object_ref(request)); +} + +static void +fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUtilPrivate *priv) +{ + g_autofree gchar *str = NULL; + + /* action has not been assigned yet */ + if (priv->current_operation == FU_UTIL_OPERATION_UNKNOWN) + return; + + /* allowed to set whenever the device has changed */ + if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + 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 || + g_strcmp0(fwupd_device_get_composite_id(priv->current_device), + fwupd_device_get_composite_id(device)) == 0) { + g_set_object(&priv->current_device, device); + return; + } + + /* ignore indirect devices that might have changed */ + if (fwupd_device_get_status(device) == FWUPD_STATUS_IDLE || + fwupd_device_get_status(device) == FWUPD_STATUS_UNKNOWN) { + g_debug("ignoring %s with status %s", + fwupd_device_get_name(device), + fwupd_status_to_string(fwupd_device_get_status(device))); + 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_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); +} + +static gboolean +fu_util_filter_device(FuUtilPrivate *priv, FwupdDevice *dev) +{ + for (guint i = 0; i < 64; i++) { + FwupdDeviceFlags flag = 1LLU << i; + if (priv->filter_include & flag) { + if (!fwupd_device_has_flag(dev, flag)) + return FALSE; + } + if (priv->filter_exclude & flag) { + if (fwupd_device_has_flag(dev, flag)) + return FALSE; + } + } + return TRUE; +} + +static FwupdDevice * +fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices, GError **error) +{ + FwupdDevice *dev; + guint idx; + g_autoptr(GPtrArray) devices_filtered = NULL; + + /* filter results */ + devices_filtered = g_ptr_array_new(); + for (guint i = 0; i < devices->len; i++) { + dev = g_ptr_array_index(devices, i); + if (!fu_util_filter_device(priv, dev)) + continue; + g_ptr_array_add(devices_filtered, dev); + } + + /* nothing */ + if (devices_filtered->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No supported devices"); + return NULL; + } + + /* exactly one */ + if (devices_filtered->len == 1) { + dev = g_ptr_array_index(devices_filtered, 0); + if (!priv->as_json) { + /* TRANSLATORS: device has been chosen by the daemon for the user */ + g_print("%s: %s\n", _("Selected device"), fwupd_device_get_name(dev)); + } + return g_object_ref(dev); + } + + /* no questions */ + if (priv->no_device_prompt) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "can't prompt for devices"); + return NULL; + } + + /* TRANSLATORS: get interactive prompt */ + g_print("%s\n", _("Choose a device:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < devices_filtered->len; i++) { + dev = g_ptr_array_index(devices_filtered, i); + g_print("%u.\t%s (%s)\n", + i + 1, + fwupd_device_get_id(dev), + fwupd_device_get_name(dev)); + } + idx = fu_util_prompt_for_number(devices_filtered->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return NULL; + } + dev = g_ptr_array_index(devices_filtered, idx - 1); + return g_object_ref(dev); +} + +static gboolean +fu_util_perhaps_show_unreported(FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_failed = g_ptr_array_new(); + g_autoptr(GPtrArray) devices_success = g_ptr_array_new(); + g_autoptr(GPtrArray) remotes = NULL; + g_autoptr(GHashTable) remote_id_uri_map = NULL; + gboolean all_automatic = FALSE; + + /* we don't want to ask anything */ + if (priv->no_unreported_check) { + g_debug("skipping unreported check"); + return TRUE; + } + + /* get all devices from the history database */ + devices = fwupd_client_get_history(priv->client, priv->cancellable, &error_local); + if (devices == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) + return TRUE; + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* create a map of RemoteID to RemoteURI */ + remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + if (remotes == NULL) + return FALSE; + remote_id_uri_map = g_hash_table_new(g_str_hash, g_str_equal); + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + gboolean remote_automatic; + if (fwupd_remote_get_id(remote) == NULL) + continue; + if (fwupd_remote_get_report_uri(remote) == NULL) + continue; + g_debug("adding %s for %s", + fwupd_remote_get_report_uri(remote), + fwupd_remote_get_id(remote)); + g_hash_table_insert(remote_id_uri_map, + (gpointer)fwupd_remote_get_id(remote), + (gpointer)fwupd_remote_get_report_uri(remote)); + remote_automatic = fwupd_remote_get_automatic_reports(remote); + g_debug("%s is %d", fwupd_remote_get_title(remote), remote_automatic); + if (remote_automatic && !all_automatic) + all_automatic = TRUE; + if (!remote_automatic && all_automatic) { + all_automatic = FALSE; + break; + } + } + + /* check that they can be reported */ + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel = fwupd_device_get_release_default(dev); + const gchar *remote_id; + const gchar *remote_uri; + + if (!fu_util_filter_device(priv, dev)) + continue; + if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_REPORTED)) + continue; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + + /* find the RemoteURI to use for the device */ + remote_id = fwupd_release_get_remote_id(rel); + if (remote_id == NULL) { + g_debug("%s has no RemoteID", fwupd_device_get_id(dev)); + continue; + } + remote_uri = g_hash_table_lookup(remote_id_uri_map, remote_id); + if (remote_uri == NULL) { + g_debug("%s has no RemoteURI", remote_id); + continue; + } + + /* only send success and failure */ + if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_FAILED) { + g_ptr_array_add(devices_failed, dev); + } else if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_SUCCESS) { + g_ptr_array_add(devices_success, dev); + } else { + g_debug("ignoring %s with UpdateState %s", + fwupd_device_get_id(dev), + fwupd_update_state_to_string(fwupd_device_get_update_state(dev))); + } + } + + /* nothing to do */ + if (devices_failed->len == 0 && devices_success->len == 0) { + g_debug("no unreported devices"); + return TRUE; + } + + g_debug("All automatic: %d", all_automatic); + /* show the success and failures */ + if (!priv->assume_yes && !all_automatic) { + /* delimit */ + g_print("________________________________________________\n"); + + /* failures */ + if (devices_failed->len > 0) { + /* TRANSLATORS: a list of failed updates */ + g_print("\n%s\n\n", _("Devices that were not updated correctly:")); + for (guint i = 0; i < devices_failed->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_failed, i); + FwupdRelease *rel = fwupd_device_get_release_default(dev); + g_print(" • %s (%s → %s)\n", + fwupd_device_get_name(dev), + fwupd_device_get_version(dev), + fwupd_release_get_version(rel)); + } + } + + /* success */ + if (devices_success->len > 0) { + /* TRANSLATORS: a list of successful updates */ + g_print("\n%s\n\n", _("Devices that have been updated successfully:")); + for (guint i = 0; i < devices_success->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_success, i); + FwupdRelease *rel = fwupd_device_get_release_default(dev); + g_print(" • %s (%s → %s)\n", + fwupd_device_get_name(dev), + fwupd_device_get_version(dev), + fwupd_release_get_version(rel)); + } + } + + /* ask for permission */ + g_print("\n%s\n%s (%s) [Y|n]:\n", + /* TRANSLATORS: explain why we want to upload */ + _("Uploading firmware reports helps hardware vendors" + " to quickly identify failing and successful updates" + " on real devices."), + /* TRANSLATORS: ask the user to upload */ + _("Upload report now?"), + /* TRANSLATORS: metadata is downloaded from the Internet */ + _("Requires internet connection")); + if (!fu_util_prompt_for_boolean(TRUE)) { + g_print("\n%s [y|N]:\n", + /* TRANSLATORS: offer to disable this nag */ + _("Do you want to disable this feature for future updates?")); + if (fu_util_prompt_for_boolean(FALSE)) { + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + const gchar *remote_id = fwupd_remote_get_id(remote); + if (fwupd_remote_get_report_uri(remote) == NULL) + continue; + if (!fwupd_client_modify_remote(priv->client, + remote_id, + "ReportURI", + "", + priv->cancellable, + error)) + return FALSE; + } + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Declined upload"); + return FALSE; + } + } + + /* upload */ + if (!fu_util_report_history(priv, NULL, error)) + return FALSE; + + /* offer to make automatic */ + if (!priv->assume_yes && !all_automatic) { + g_print("\n%s [y|N]:\n", + /* TRANSLATORS: offer to stop asking the question */ + _("Do you want to upload reports automatically for future updates?")); + if (fu_util_prompt_for_boolean(FALSE)) { + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + const gchar *remote_id = fwupd_remote_get_id(remote); + if (fwupd_remote_get_report_uri(remote) == NULL) + continue; + if (fwupd_remote_get_automatic_reports(remote)) + continue; + if (!fwupd_client_modify_remote(priv->client, + remote_id, + "AutomaticReports", + "true", + priv->cancellable, + error)) + return FALSE; + } + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_modify_remote_warning(FuUtilPrivate *priv, FwupdRemote *remote, GError **error) +{ + const gchar *warning_markup = NULL; + g_autofree gchar *warning_plain = NULL; + + /* get formatted text */ + warning_markup = fwupd_remote_get_agreement(remote); + if (warning_markup == NULL) + return TRUE; + warning_plain = fu_util_convert_description(warning_markup, error); + if (warning_plain == NULL) + return FALSE; + + /* TRANSLATORS: a remote here is like a 'repo' or software source */ + fu_util_warning_box(_("Enable new remote?"), warning_plain, 80); + if (!priv->assume_yes) { + /* ask for permission */ + g_print("\n%s [Y|n]: ", + /* TRANSLATORS: should the remote still be enabled */ + _("Agree and enable the remote?")); + if (!fu_util_prompt_for_boolean(TRUE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Declined agreement"); + return FALSE; + } + } + return TRUE; +} + +static void +fu_util_build_device_tree(FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FwupdDevice *dev) +{ + for (guint i = 0; i < devs->len; i++) { + FwupdDevice *dev_tmp = g_ptr_array_index(devs, i); + if (!fu_util_filter_device(priv, dev_tmp)) + continue; + if (!priv->show_all && !fu_util_is_interesting_device(dev_tmp)) + continue; + if (fwupd_device_get_parent(dev_tmp) == dev) { + FwupdRelease *rel = fwupd_device_get_release_default(dev_tmp); + GNode *child = g_node_append_data(root, dev_tmp); + if (rel != NULL) + g_node_append_data(child, rel); + fu_util_build_device_tree(priv, child, devs, dev_tmp); + } + } +} + +static gboolean +fu_util_get_releases_as_json(FuUtilPrivate *priv, GPtrArray *rels, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Releases"); + json_builder_begin_array(builder); + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + json_builder_begin_object(builder); + fwupd_release_to_json(rel, builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_devices_as_json(FuUtilPrivate *priv, GPtrArray *devs, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Devices"); + json_builder_begin_array(builder); + for (guint i = 0; i < 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_full(dev, builder, FWUPD_DEVICE_FLAG_TRUSTED); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_devices(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GNode) root = g_node_new(NULL); + g_autoptr(GPtrArray) devs = NULL; + + /* get results from daemon */ + devs = fwupd_client_get_devices(priv->client, priv->cancellable, error); + if (devs == NULL) + return FALSE; + + /* not for human consumption */ + if (priv->as_json) + return fu_util_get_devices_as_json(priv, devs, error); + + /* print */ + if (devs->len > 0) + fu_util_build_device_tree(priv, root, devs, NULL); + if (g_node_n_children(root) == 0) { + /* TRANSLATORS: nothing attached that can be upgraded */ + g_print("%s\n", _("No hardware detected with firmware update capability")); + return TRUE; + } + fu_util_print_tree(priv->client, root); + + /* nag? */ + if (!fu_util_perhaps_show_unreported(priv, error)) + return FALSE; + + return TRUE; +} + +static gboolean +fu_util_get_plugins_as_json(FuUtilPrivate *priv, GPtrArray *plugins, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + + json_builder_set_member_name(builder, "Plugins"); + json_builder_begin_array(builder); + for (guint i = 0; i < plugins->len; i++) { + FwupdPlugin *plugin = g_ptr_array_index(plugins, i); + json_builder_begin_object(builder); + fwupd_plugin_to_json(plugin, builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_plugins(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) plugins = NULL; + + /* get results from daemon */ + plugins = fwupd_client_get_plugins(priv->client, priv->cancellable, error); + if (plugins == NULL) + return FALSE; + if (priv->as_json) + return fu_util_get_plugins_as_json(priv, plugins, error); + + /* print */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + g_autofree gchar *str = fu_util_plugin_to_string(FWUPD_PLUGIN(plugin), 0); + g_print("%s\n", str); + } + if (plugins->len == 0) { + /* TRANSLATORS: nothing found */ + g_print("%s\n", _("No plugins found")); + } + + /* success */ + return TRUE; +} + +static gchar * +fu_util_download_if_required(FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) +{ + g_autofree gchar *filename = NULL; + g_autoptr(GBytes) blob = NULL; + + /* a local file */ + if (g_file_test(perhapsfn, G_FILE_TEST_EXISTS)) + return g_strdup(perhapsfn); + if (!fu_util_is_url(perhapsfn)) + return g_strdup(perhapsfn); + + /* download the firmware to a cachedir */ + filename = fu_util_get_user_cache_path(perhapsfn); + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + return g_steal_pointer(&filename); + if (!fu_path_mkdir_parent(filename, error)) + return NULL; + blob = fwupd_client_download_bytes(priv->client, + perhapsfn, + priv->download_flags, + priv->cancellable, + error); + if (blob == NULL) + return NULL; + + /* save file to cache */ + if (!fu_bytes_set_contents(filename, blob, error)) + return NULL; + return g_steal_pointer(&filename); +} + +static void +fu_util_display_current_message(FuUtilPrivate *priv) +{ + /* TRANSLATORS: success message */ + g_print("%s\n", _("Successfully installed firmware")); + + /* print all POST requests */ + for (guint i = 0; i < priv->post_requests->len; i++) { + FwupdRequest *request = g_ptr_array_index(priv->post_requests, i); + g_print("%s\n", fu_util_request_get_message(request)); + } +} + +typedef struct { + guint nr_success; + guint nr_failed; + guint nr_missing; + JsonBuilder *builder; + const gchar *name; +} FuUtilDeviceTestHelper; + +static gboolean +fu_util_device_test_component(FuUtilPrivate *priv, + FuUtilDeviceTestHelper *helper, + JsonObject *json_obj, + GBytes *fw, + GError **error) +{ + JsonArray *json_array; + const gchar *name = "component"; + const gchar *protocol = NULL; + g_autoptr(FwupdDevice) device = NULL; + + /* some elements are optional */ + if (json_object_has_member(json_obj, "name")) { + name = json_object_get_string_member(json_obj, "name"); + json_builder_set_member_name(helper->builder, "name"); + json_builder_add_string_value(helper->builder, name); + } + if (json_object_has_member(json_obj, "protocol")) { + protocol = json_object_get_string_member(json_obj, "protocol"); + json_builder_set_member_name(helper->builder, "protocol"); + json_builder_add_string_value(helper->builder, protocol); + } + + /* find the device with any of the matching GUIDs */ + if (!json_object_has_member(json_obj, "guids")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'guids'"); + return FALSE; + } + json_array = json_object_get_array_member(json_obj, "guids"); + json_builder_set_member_name(helper->builder, "guids"); + json_builder_begin_array(helper->builder); + for (guint i = 0; i < json_array_get_length(json_array); i++) { + JsonNode *json_node = json_array_get_element(json_array, i); + FwupdDevice *device_tmp; + const gchar *guid = json_node_get_string(json_node); + g_autoptr(GPtrArray) devices = NULL; + + g_debug("looking for guid %s", guid); + devices = + fwupd_client_get_devices_by_guid(priv->client, guid, priv->cancellable, NULL); + if (devices == NULL) + continue; + if (devices->len > 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "multiple devices with GUID %s", + guid); + return FALSE; + } + device_tmp = g_ptr_array_index(devices, 0); + if (protocol != NULL && !fu_device_has_protocol(device_tmp, protocol)) + continue; + device = g_object_ref(device_tmp); + json_builder_add_string_value(helper->builder, guid); + break; + } + json_builder_end_array(helper->builder); + if (device == NULL) { + if (!priv->as_json) { + g_autofree gchar *msg = NULL; + /* TRANSLATORS: this is for the device tests */ + msg = fu_util_term_format(_("Did not find any devices with matching GUIDs"), + FU_UTIL_TERM_COLOR_RED); + g_print("%s: %s", name, msg); + } + json_builder_set_member_name(helper->builder, "error"); + json_builder_add_string_value(helper->builder, "no devices found"); + helper->nr_failed++; + return TRUE; + } + + /* verify the version matches what we expected */ + if (json_object_has_member(json_obj, "version")) { + const gchar *version = json_object_get_string_member(json_obj, "version"); + json_builder_set_member_name(helper->builder, "version"); + json_builder_add_string_value(helper->builder, version); + if (g_strcmp0(version, fu_device_get_version(device)) != 0) { + g_autofree gchar *str = NULL; + str = g_strdup_printf("version did not match: got %s, expected %s", + fu_device_get_version(device), + version); + if (!priv->as_json) { + g_autofree gchar *msg = NULL; + g_autofree gchar *str2 = NULL; + str2 = g_strdup_printf( + /* TRANSLATORS: this is for the device tests, %1 is the device + * version, %2 is what we expected */ + _("The device version did not match: got %s, expected %s"), + fu_device_get_version(device), + version); + msg = fu_util_term_format(str2, FU_UTIL_TERM_COLOR_RED); + g_print("%s: %s", name, msg); + } + json_builder_set_member_name(helper->builder, "error"); + json_builder_add_string_value(helper->builder, str); + helper->nr_failed++; + } + } + + /* success */ + if (!priv->as_json) { + g_autofree gchar *msg = NULL; + /* TRANSLATORS: this is for the device tests */ + msg = fu_util_term_format(_("OK!"), FU_UTIL_TERM_COLOR_GREEN); + g_print("%s: %s\n", helper->name, msg); + } + helper->nr_success++; + return TRUE; +} + +static gboolean +fu_util_device_test_step(FuUtilPrivate *priv, + FuUtilDeviceTestHelper *helper, + JsonObject *json_obj, + GError **error) +{ + JsonArray *json_array; + const gchar *url; + const gchar *baseuri = g_getenv("FWUPD_DEVICE_TESTS_BASE_URI"); + g_autofree gchar *filename = NULL; + g_autofree gchar *url_safe = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error_local = NULL; + + /* download file if required */ + if (!json_object_has_member(json_obj, "url")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'url'"); + return FALSE; + } + + /* build URL */ + url = json_object_get_string_member(json_obj, "url"); + if (baseuri != NULL) { + g_autofree gchar *basename = g_path_get_basename(url); + url_safe = g_build_filename(baseuri, basename, NULL); + } else { + url_safe = g_strdup(url); + } + filename = fu_util_download_if_required(priv, url_safe, error); + if (filename == NULL) { + g_prefix_error(error, "failed to download %s: ", url_safe); + return FALSE; + } + + /* log */ + json_builder_set_member_name(helper->builder, "url"); + json_builder_add_string_value(helper->builder, url_safe); + + /* install file */ + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (!fwupd_client_install(priv->client, + FWUPD_DEVICE_ID_ANY, + filename, + priv->flags, + priv->cancellable, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + json_builder_set_member_name(helper->builder, "info"); + json_builder_add_string_value(helper->builder, error_local->message); + helper->nr_missing++; + return TRUE; + } + if (!priv->as_json) { + g_autofree gchar *msg = NULL; + msg = fu_util_term_format(error_local->message, FU_UTIL_TERM_COLOR_RED); + g_print("%s: %s", helper->name, msg); + } + json_builder_set_member_name(helper->builder, "error"); + json_builder_add_string_value(helper->builder, error_local->message); + helper->nr_failed++; + return TRUE; + } + + /* process each step */ + if (!json_object_has_member(json_obj, "components")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'components'"); + return FALSE; + } + json_array = json_object_get_array_member(json_obj, "components"); + for (guint i = 0; i < json_array_get_length(json_array); i++) { + JsonNode *json_node = json_array_get_element(json_array, i); + JsonObject *json_obj_tmp = json_node_get_object(json_node); + if (!fu_util_device_test_component(priv, helper, json_obj_tmp, fw, error)) + return FALSE; + } + + /* success */ + json_builder_set_member_name(helper->builder, "success"); + json_builder_add_boolean_value(helper->builder, TRUE); + return TRUE; +} + +static gboolean +fu_util_device_test_filename(FuUtilPrivate *priv, + FuUtilDeviceTestHelper *helper, + const gchar *filename, + GError **error) +{ + JsonNode *json_root; + JsonNode *json_steps; + JsonObject *json_obj; + guint repeat = 1; + g_autoptr(JsonParser) parser = json_parser_new(); + + /* log */ + json_builder_set_member_name(helper->builder, "filename"); + json_builder_add_string_value(helper->builder, filename); + + /* parse JSON */ + if (!json_parser_load_from_file(parser, filename, error)) { + g_prefix_error(error, "test not in JSON format: "); + return FALSE; + } + json_root = json_parser_get_root(parser); + if (json_root == NULL || !JSON_NODE_HOLDS_OBJECT(json_root)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no root"); + return FALSE; + } + json_obj = json_node_get_object(json_root); + if (!json_object_has_member(json_obj, "steps")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'steps'"); + return FALSE; + } + json_steps = json_object_get_member(json_obj, "steps"); + if (!JSON_NODE_HOLDS_ARRAY(json_steps)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has 'steps' is not an array"); + return FALSE; + } + + /* some elements are optional */ + if (json_object_has_member(json_obj, "name")) { + helper->name = json_object_get_string_member(json_obj, "name"); + json_builder_set_member_name(helper->builder, "name"); + json_builder_add_string_value(helper->builder, helper->name); + } + if (json_object_has_member(json_obj, "interactive")) { + gboolean interactive = json_object_get_boolean_member(json_obj, "interactive"); + json_builder_set_member_name(helper->builder, "interactive"); + json_builder_add_boolean_value(helper->builder, interactive); + } + + /* process each step */ + if (json_object_has_member(json_obj, "repeat")) { + repeat = json_object_get_int_member(json_obj, "repeat"); + json_builder_set_member_name(helper->builder, "repeat"); + json_builder_add_int_value(helper->builder, repeat); + } + json_builder_set_member_name(helper->builder, "steps"); + json_builder_begin_array(helper->builder); + for (guint j = 0; j < repeat; j++) { + JsonArray *json_array = json_node_get_array(json_steps); + for (guint i = 0; i < json_array_get_length(json_array); i++) { + JsonNode *json_node = json_array_get_element(json_array, i); + json_obj = json_node_get_object(json_node); + json_builder_begin_object(helper->builder); + if (!fu_util_device_test_step(priv, helper, json_obj, error)) + return FALSE; + json_builder_end_object(helper->builder); + } + } + json_builder_end_array(helper->builder); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_device_test(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + FuUtilDeviceTestHelper helper = {.nr_failed = 0, + .nr_success = 0, + .builder = builder, + .name = "Unknown"}; + + /* required for interactive devices */ + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + + /* at least one argument required */ + if (g_strv_length(values) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* prepare to save the data as JSON */ + json_builder_begin_object(builder); + + /* process all the files */ + json_builder_set_member_name(builder, "results"); + json_builder_begin_array(builder); + for (guint i = 0; values[i] != NULL; i++) { + json_builder_begin_object(builder); + if (!fu_util_device_test_filename(priv, &helper, values[i], error)) + return FALSE; + json_builder_end_object(builder); + } + json_builder_end_array(builder); + + /* dump to screen as JSON format */ + json_builder_end_object(builder); + if (priv->as_json) { + if (!fu_util_print_builder(builder, error)) + return FALSE; + } + + /* we need all to pass for a zero return code */ + if (helper.nr_failed > 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Some of the tests failed"); + return FALSE; + } + if (helper.nr_missing > 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%u devices required for %u tests were not found", + helper.nr_missing, + g_strv_length(values)); + return FALSE; + } + if (helper.nr_success == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "None of the tests were successful"); + return FALSE; + } + + /* nag? */ + if (!fu_util_perhaps_show_unreported(priv, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_util_download(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *basename = NULL; + g_autoptr(GBytes) blob = NULL; + + /* one argument required */ + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + blob = fwupd_client_download_bytes(priv->client, + values[0], + priv->download_flags, + priv->cancellable, + error); + if (blob == NULL) + return FALSE; + basename = g_path_get_basename(values[0]); + return g_file_set_contents(basename, + g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + error); +} + +static gboolean +fu_util_local_install(FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *id; + g_autofree gchar *filename = NULL; + g_autoptr(FwupdDevice) dev = NULL; + + /* handle both forms */ + if (g_strv_length(values) == 1) { + id = FWUPD_DEVICE_ID_ANY; + } else if (g_strv_length(values) == 2) { + id = values[1]; + dev = fu_util_get_device_by_id(priv, id, error); + if (dev == NULL) + return FALSE; + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + + /* install with flags chosen by the user */ + filename = fu_util_download_if_required(priv, values[0], error); + if (filename == NULL) + return FALSE; + + /* detect bitlocker */ + if (dev != NULL && !priv->no_safety_check && !priv->assume_yes) { + if (!fu_util_prompt_warning_fde(dev, error)) + return FALSE; + } + + if (!fwupd_client_install(priv->client, + id, + filename, + priv->flags, + priv->cancellable, + 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 +fu_util_get_details_as_json(FuUtilPrivate *priv, GPtrArray *devs, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Devices"); + json_builder_begin_array(builder); + for (guint i = 0; i < devs->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devs, i); + json_builder_begin_object(builder); + fwupd_device_to_json_full(dev, builder, FWUPD_DEVICE_FLAG_TRUSTED); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_details(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) array = NULL; + g_autoptr(GNode) root = g_node_new(NULL); + + /* check args */ + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* implied, important for get-details on a device not in your system */ + priv->show_all = TRUE; + + array = fwupd_client_get_details(priv->client, values[0], priv->cancellable, error); + if (array == NULL) + return FALSE; + if (priv->as_json) + return fu_util_get_details_as_json(priv, array, error); + + fu_util_build_device_tree(priv, root, array, NULL); + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +static gboolean +fu_util_report_history_for_remote(FuUtilPrivate *priv, + const gchar *remote_id, + GPtrArray *devices, + GError **error) +{ + g_autofree gchar *data = NULL; + g_autofree gchar *sig = NULL; + g_autofree gchar *uri = NULL; + g_autoptr(FwupdRemote) remote = NULL; + + /* convert to JSON */ + data = fwupd_build_history_report_json(devices, error); + 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; + } + + remote = fwupd_client_get_remote_by_id(priv->client, remote_id, priv->cancellable, error); + if (remote == NULL) + return FALSE; + + /* ask for permission */ + if (!priv->assume_yes && !fwupd_remote_get_automatic_reports(remote)) { + fu_util_print_data(_("Target"), fwupd_remote_get_report_uri(remote)); + 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, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "User declined action"); + return FALSE; + } + } + + /* POST request and parse reply */ + if (!fu_util_send_report(priv->client, + fwupd_remote_get_report_uri(remote), + data, + sig, + &uri, + error)) + return FALSE; + + /* server wanted us to see a message */ + if (uri != NULL) { + g_print("%s %s\n", + /* TRANSLATORS: the server sent the user a small message */ + _("Update failure is a known issue, visit this URL for more information:"), + uri); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_report_history(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GHashTable) report_map = NULL; + g_autoptr(GList) ids = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* get all devices from the history database, then filter them, + * adding to a hash map of report-ids */ + devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + if (devices == NULL) + return FALSE; + report_map = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)g_ptr_array_unref); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel = fwupd_device_get_release_default(dev); + const gchar *remote_id; + GPtrArray *devices_tmp; + g_autoptr(FwupdRemote) remote = NULL; + + /* filter, if not forcing */ + if (!fu_util_filter_device(priv, dev)) + continue; + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_REPORTED)) + continue; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + } + /* only send success and failure */ + if (fwupd_device_get_update_state(dev) != FWUPD_UPDATE_STATE_FAILED && + fwupd_device_get_update_state(dev) != FWUPD_UPDATE_STATE_SUCCESS) { + g_debug("ignoring %s with UpdateState %s", + fwupd_device_get_id(dev), + fwupd_update_state_to_string(fwupd_device_get_update_state(dev))); + continue; + } + + /* find the RemoteURI to use for the device */ + remote_id = fwupd_release_get_remote_id(rel); + if (remote_id == NULL) { + g_debug("%s has no RemoteID", fwupd_device_get_id(dev)); + continue; + } + remote = fwupd_client_get_remote_by_id(priv->client, + remote_id, + priv->cancellable, + error); + if (remote == NULL) + return FALSE; + if (fwupd_remote_get_report_uri(remote) == NULL) { + g_debug("%s has no RemoteURI", fwupd_remote_get_report_uri(remote)); + continue; + } + + /* add this to the hash map */ + devices_tmp = g_hash_table_lookup(report_map, remote_id); + if (devices_tmp == NULL) { + devices_tmp = g_ptr_array_new(); + g_hash_table_insert(report_map, g_strdup(remote_id), devices_tmp); + } + g_debug("using %s for %s", remote_id, fwupd_device_get_id(dev)); + g_ptr_array_add(devices_tmp, dev); + } + + /* nothing to report */ + if (g_hash_table_size(report_map) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No reports require uploading"); + return FALSE; + } + + /* process each uri */ + ids = g_hash_table_get_keys(report_map); + for (GList *l = ids; l != NULL; l = l->next) { + const gchar *id = l->data; + GPtrArray *devices_tmp = g_hash_table_lookup(report_map, id); + if (!fu_util_report_history_for_remote(priv, id, devices_tmp, error)) + return FALSE; + + /* mark each device as reported */ + for (guint i = 0; i < devices_tmp->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_tmp, i); + g_debug("setting flag on %s", fwupd_device_get_id(dev)); + if (!fwupd_client_modify_device(priv->client, + fwupd_device_get_id(dev), + "Flags", + "reported", + priv->cancellable, + error)) + return FALSE; + } + } + + g_string_append_printf(str, + /* TRANSLATORS: success message -- where the user has uploaded + * success and/or failure reports to the remote server */ + ngettext("Successfully uploaded %u report", + "Successfully uploaded %u reports", + g_hash_table_size(report_map)), + g_hash_table_size(report_map)); + g_print("%s\n", str->str); + return TRUE; +} + +static gboolean +fu_util_get_history(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GNode) root = g_node_new(NULL); + + /* get all devices from the history database */ + devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + if (devices == NULL) + return FALSE; + + /* not for human consumption */ + if (priv->as_json) + return fu_util_get_devices_as_json(priv, devices, error); + + /* show each device */ + for (guint i = 0; i < devices->len; i++) { + g_autoptr(GPtrArray) rels = NULL; + FwupdDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel; + const gchar *remote; + GNode *child; + g_autoptr(GError) error_local = NULL; + + if (!fu_util_filter_device(priv, dev)) + continue; + child = g_node_append_data(root, dev); + + rel = fwupd_device_get_release_default(dev); + if (rel == NULL) + continue; + remote = fwupd_release_get_remote_id(rel); + + /* doesn't actually map to remote */ + if (remote == NULL) { + g_node_append_data(child, rel); + continue; + } + + /* try to lookup releases from client */ + rels = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + &error_local); + if (rels == NULL) { + g_debug("failed to get releases for %s: %s", + fwupd_device_get_id(dev), + error_local->message); + g_node_append_data(child, rel); + continue; + } + + /* map to a release in client */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel2 = g_ptr_array_index(rels, j); + if (g_strcmp0(remote, fwupd_release_get_remote_id(rel2)) != 0) + continue; + if (g_strcmp0(fwupd_release_get_version(rel), + fwupd_release_get_version(rel2)) != 0) + continue; + g_node_append_data(child, g_object_ref(rel2)); + rel = NULL; + break; + } + + /* didn't match anything */ + if (rels->len == 0 || rel != NULL) { + g_node_append_data(child, rel); + continue; + } + } + + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +static FwupdDevice * +fu_util_get_device_by_id(FuUtilPrivate *priv, const gchar *id, GError **error) +{ + if (fwupd_guid_is_valid(id)) { + g_autoptr(GPtrArray) devices = NULL; + devices = + fwupd_client_get_devices_by_guid(priv->client, id, priv->cancellable, error); + if (devices == NULL) + return NULL; + return fu_util_prompt_for_device(priv, devices, error); + } + /* did this look like a GUID? */ + for (guint i = 0; id[i] != '\0'; i++) { + if (id[i] == '-') { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return NULL; + } + } + return fwupd_client_get_device_by_id(priv->client, id, priv->cancellable, error); +} + +static FwupdDevice * +fu_util_get_device_or_prompt(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + + /* get device to use */ + if (g_strv_length(values) >= 1) { + if (g_strv_length(values) > 1) { + for (guint i = 1; i < g_strv_length(values); i++) + g_debug("Ignoring extra input %s", values[i]); + } + return fu_util_get_device_by_id(priv, values[0], error); + } + + /* get all devices from daemon */ + devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + if (devices == NULL) + return NULL; + return fu_util_prompt_for_device(priv, devices, error); +} + +static FwupdRelease * +fu_util_get_release_for_device_version(FuUtilPrivate *priv, + FwupdDevice *device, + const gchar *version, + GError **error) +{ + g_autoptr(GPtrArray) releases = NULL; + + /* get all releases */ + releases = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(device), + priv->cancellable, + error); + if (releases == NULL) + return NULL; + + /* find using vercmp */ + for (guint j = 0; j < releases->len; j++) { + FwupdRelease *release = g_ptr_array_index(releases, j); + if (fu_version_compare(fwupd_release_get_version(release), + version, + fwupd_device_get_version_format(device)) == 0) { + return g_object_ref(release); + } + } + + /* did not find */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unable to locate release %s for %s", + version, + fu_device_get_name(device)); + return NULL; +} + +static gboolean +fu_util_clear_results(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + return fwupd_client_clear_results(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); +} + +static gboolean +fu_util_verify_update(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + + priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + if (!fwupd_client_verify_update(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error)) { + g_prefix_error(error, "failed to verify update %s: ", fu_device_get_name(dev)); + return FALSE; + } + /* TRANSLATORS: success message when user refreshes device checksums */ + g_print("%s\n", _("Successfully updated device checksums")); + + return TRUE; +} + +static gboolean +fu_util_download_metadata_enable_lvfs(FuUtilPrivate *priv, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + + /* is the LVFS available but disabled? */ + remote = fwupd_client_get_remote_by_id(priv->client, "lvfs", priv->cancellable, error); + if (remote == NULL) + return TRUE; + g_print("%s\n%s\n%s [Y|n]: ", + /* TRANSLATORS: explain why no metadata available */ + _("No remotes are currently enabled so no metadata is available."), + /* TRANSLATORS: explain why no metadata available */ + _("Metadata can be obtained from the Linux Vendor Firmware Service."), + /* TRANSLATORS: Turn on the remote */ + _("Enable this remote?")); + if (!fu_util_prompt_for_boolean(TRUE)) + return TRUE; + if (!fwupd_client_modify_remote(priv->client, + fwupd_remote_get_id(remote), + "Enabled", + "true", + priv->cancellable, + error)) + return FALSE; + if (!fu_util_modify_remote_warning(priv, remote, error)) + return FALSE; + + /* refresh the newly-enabled remote */ + return fwupd_client_refresh_remote(priv->client, remote, priv->cancellable, error); +} + +static gboolean +fu_util_check_oldest_remote(FuUtilPrivate *priv, guint64 *age_oldest, GError **error) +{ + g_autoptr(GPtrArray) remotes = NULL; + gboolean checked = FALSE; + + /* get the age of the oldest enabled remotes */ + remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + if (remotes == NULL) + return FALSE; + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + if (!fwupd_remote_get_enabled(remote)) + continue; + if (fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) + continue; + checked = TRUE; + if (fwupd_remote_get_age(remote) > *age_oldest) + *age_oldest = fwupd_remote_get_age(remote); + } + if (!checked) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: error message for a user who ran fwupdmgr + refresh recently but no remotes */ + "No remotes enabled."); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_util_download_metadata(FuUtilPrivate *priv, GError **error) +{ + gboolean download_remote_enabled = FALSE; + guint devices_supported_cnt = 0; + g_autoptr(GPtrArray) devs = NULL; + g_autoptr(GPtrArray) remotes = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* metadata refreshed recently */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + guint64 age_oldest = 0; + const guint64 age_limit_hours = 24; + + if (!fu_util_check_oldest_remote(priv, &age_oldest, error)) + return FALSE; + if (age_oldest < 60 * 60 * age_limit_hours) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: error message for a user who ran fwupdmgr + refresh recently %1 is an already translated timestamp such + as 6 hours or 15 seconds */ + _("Firmware metadata last refresh: %s ago. " + "Use --force to refresh again."), + fu_util_time_to_str(age_oldest)); + return FALSE; + } + } + + remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + if (remotes == NULL) + return FALSE; + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + if (!fwupd_remote_get_enabled(remote)) + continue; + if (fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) + continue; + download_remote_enabled = TRUE; + g_print("%s %s\n", _("Updating"), fwupd_remote_get_id(remote)); + if (!fwupd_client_refresh_remote(priv->client, remote, priv->cancellable, error)) + return FALSE; + } + + /* no web remote is declared; try to enable LVFS */ + if (!download_remote_enabled) { + /* we don't want to ask anything */ + if (priv->no_remote_check) { + g_debug("skipping remote check"); + return TRUE; + } + + if (!fu_util_download_metadata_enable_lvfs(priv, error)) + return FALSE; + } + + /* get devices from daemon */ + devs = fwupd_client_get_devices(priv->client, priv->cancellable, error); + if (devs == NULL) + return FALSE; + + /* get results */ + for (guint i = 0; i < devs->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devs, i); + if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + devices_supported_cnt++; + } + + /* TRANSLATORS: success message -- where 'metadata' is information + * about available firmware on the remote server */ + g_string_append(str, _("Successfully downloaded new metadata: ")); + + g_string_append_printf(str, + /* TRANSLATORS: how many local devices can expect updates now */ + ngettext("%u local device supported", + "%u local devices supported", + devices_supported_cnt), + devices_supported_cnt); + g_print("%s\n", str->str); + return TRUE; +} + +static gboolean +fu_util_refresh(FuUtilPrivate *priv, gchar **values, GError **error) +{ + if (g_strv_length(values) == 0) + return fu_util_download_metadata(priv, error); + if (g_strv_length(values) != 3) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* open file */ + if (!fwupd_client_update_metadata(priv->client, + values[2], + values[0], + values[1], + priv->cancellable, + error)) + return FALSE; + + /* TRANSLATORS: success message -- the user can do this by-hand too */ + g_print("%s\n", _("Successfully refreshed metadata manually")); + return TRUE; +} + +static gboolean +fu_util_get_results_as_json(FuUtilPrivate *priv, FwupdDevice *res, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + fwupd_device_to_json_full(res, builder, FWUPD_DEVICE_FLAG_TRUSTED); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_results(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autofree gchar *tmp = NULL; + g_autoptr(FwupdDevice) dev = NULL; + g_autoptr(FwupdDevice) rel = NULL; + + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + rel = fwupd_client_get_results(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); + if (rel == NULL) + return FALSE; + if (priv->as_json) + return fu_util_get_results_as_json(priv, rel, error); + tmp = fu_util_device_to_string(priv->client, rel, 0); + g_print("%s", tmp); + return TRUE; +} + +static gboolean +fu_util_get_releases(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + g_autoptr(GPtrArray) rels = NULL; + + priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + /* get the releases for this device */ + rels = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); + if (rels == NULL) + return FALSE; + + /* not for human consumption */ + if (priv->as_json) + return fu_util_get_releases_as_json(priv, rels, error); + + if (rels->len == 0) { + /* TRANSLATORS: no repositories to download from */ + g_print("%s\n", _("No releases available")); + return TRUE; + } + if (g_getenv("FWUPD_VERBOSE") != NULL) { + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + g_autofree gchar *tmp = fwupd_release_to_string(rel); + g_print("%s\n", tmp); + } + } else { + g_autoptr(GNode) root = g_node_new(NULL); + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + g_node_append_data(root, rel); + } + fu_util_print_tree(priv->client, root); + } + + return TRUE; +} + +static FwupdRelease * +fu_util_prompt_for_release(FuUtilPrivate *priv, GPtrArray *rels, GError **error) +{ + FwupdRelease *rel; + guint idx; + + /* nothing */ + if (rels->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No supported releases"); + return NULL; + } + + /* exactly one */ + if (rels->len == 1) { + rel = g_ptr_array_index(rels, 0); + return g_object_ref(rel); + } + + /* TRANSLATORS: get interactive prompt */ + g_print("%s\n", _("Choose a release:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, i); + g_print("%u.\t%s\n", i + 1, fwupd_release_get_version(rel_tmp)); + } + idx = fu_util_prompt_for_number(rels->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return NULL; + } + rel = g_ptr_array_index(rels, idx - 1); + return g_object_ref(rel); +} + +static gboolean +fu_util_verify(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + + priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + if (!fwupd_client_verify(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error)) { + g_prefix_error(error, "failed to verify %s: ", fu_device_get_name(dev)); + return FALSE; + } + /* TRANSLATORS: success message when user verified device checksums */ + g_print("%s\n", _("Successfully verified device checksums")); + + return TRUE; +} + +static gboolean +fu_util_unlock(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + + priv->filter_include |= FWUPD_DEVICE_FLAG_LOCKED; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + if (!fwupd_client_unlock(priv->client, fwupd_device_get_id(dev), priv->cancellable, error)) + return FALSE; + + /* check flags after unlocking in case the operation changes them */ + if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_perhaps_refresh_remotes(FuUtilPrivate *priv, GError **error) +{ + guint64 age_oldest = 0; + const guint64 age_limit_days = 30; + + /* we don't want to ask anything */ + if (priv->no_metadata_check) { + g_debug("skipping metadata check"); + return TRUE; + } + + if (!fu_util_check_oldest_remote(priv, &age_oldest, NULL)) + return TRUE; + + /* metadata is new enough */ + if (age_oldest < 60 * 60 * 24 * age_limit_days) + return TRUE; + + /* ask for permission */ + if (!priv->assume_yes) { + /* TRANSLATORS: the metadata is very out of date; %u is a number > 1 */ + g_print(ngettext("Firmware metadata has not been updated for %u" + " day and may not be up to date.", + "Firmware metadata has not been updated for %u" + " days and may not be up to date.", + (gint)age_limit_days), + (guint)age_limit_days); + g_print("\n\n"); + g_print("%s (%s) [y|N]: ", + /* TRANSLATORS: ask the user if we can update the metadata */ + _("Update now?"), + /* TRANSLATORS: metadata is downloaded from the Internet */ + _("Requires internet connection")); + if (!fu_util_prompt_for_boolean(FALSE)) + return TRUE; + } + + /* downloads new metadata */ + return fu_util_download_metadata(priv, error); +} + +static gboolean +fu_util_get_updates_as_json(FuUtilPrivate *priv, GPtrArray *devices, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Devices"); + json_builder_begin_array(builder); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + + /* 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 = fwupd_client_get_upgrades(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + &error_local); + if (rels == NULL) { + g_debug("no upgrades: %s", error_local->message); + continue; + } + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index(rels, j); + fwupd_device_add_release(dev, rel); + } + + /* add to builder */ + json_builder_begin_object(builder); + fwupd_device_to_json_full(dev, builder, FWUPD_DEVICE_FLAG_TRUSTED); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + gboolean supported = FALSE; + g_autoptr(GNode) root = g_node_new(NULL); + g_autoptr(GPtrArray) devices_no_support = g_ptr_array_new(); + g_autoptr(GPtrArray) devices_no_upgrades = g_ptr_array_new(); + + /* are the remotes very old */ + if (!fu_util_perhaps_refresh_remotes(priv, error)) + return FALSE; + + /* handle both forms */ + if (g_strv_length(values) == 0) { + devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + if (devices == NULL) + return FALSE; + } else if (g_strv_length(values) == 1) { + FwupdDevice *device = fu_util_get_device_by_id(priv, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_ptr_array_add(devices, device); + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); + + /* not for human consumption */ + if (priv->as_json) + return fu_util_get_updates_as_json(priv, devices, error); + + 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; + GNode *child; + + /* not going to have results, so save a D-Bus round-trip */ + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) + continue; + if (!fu_util_filter_device(priv, dev)) + continue; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { + g_ptr_array_add(devices_no_support, dev); + continue; + } + supported = TRUE; + + /* get the releases for this device and filter for validity */ + rels = fwupd_client_get_upgrades(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + &error_local); + if (rels == NULL) { + g_ptr_array_add(devices_no_upgrades, dev); + /* discard the actual reason from user, but leave for debugging */ + g_debug("%s", error_local->message); + continue; + } + child = g_node_append_data(root, dev); + + /* add all releases */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index(rels, j); + g_node_append_data(child, g_object_ref(rel)); + } + } + + /* devices that have no updates available for whatever reason */ + if (devices_no_support->len > 0) { + /* TRANSLATORS: message letting the user know no device upgrade + * available due to missing on LVFS */ + g_printerr("%s\n", _("Devices with no available firmware updates: ")); + for (guint i = 0; i < devices_no_support->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_no_support, i); + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + } + } + if (devices_no_upgrades->len > 0) { + /* TRANSLATORS: message letting the user know no device upgrade available */ + g_printerr("%s\n", _("Devices with the latest available firmware version:")); + for (guint i = 0; i < devices_no_upgrades->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices_no_upgrades, i); + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + } + } + + /* nag? */ + if (!fu_util_perhaps_show_unreported(priv, error)) + return FALSE; + + /* no devices supported by LVFS or all are filtered */ + if (!supported) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: this is an error string */ + _("No updatable devices")); + return FALSE; + } + /* no updates available */ + if (g_node_n_nodes(root, G_TRAVERSE_ALL) <= 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: this is an error string */ + _("No updates available")); + return FALSE; + } + + fu_util_print_tree(priv->client, root); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_get_remotes_as_json(FuUtilPrivate *priv, GPtrArray *remotes, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Remotes"); + json_builder_begin_array(builder); + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + json_builder_begin_object(builder); + fwupd_remote_to_json(remote, builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GNode) root = g_node_new(NULL); + g_autoptr(GPtrArray) remotes = NULL; + + remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + if (remotes == NULL) + return FALSE; + if (priv->as_json) + return fu_util_get_remotes_as_json(priv, remotes, error); + + if (remotes->len == 0) { + /* TRANSLATORS: no repositories to download from */ + g_print("%s\n", _("No remotes available")); + return TRUE; + } + + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote_tmp = g_ptr_array_index(remotes, i); + g_node_append_data(root, remote_tmp); + } + fu_util_print_tree(priv->client, root); + + return TRUE; +} + +static FwupdRelease * +fu_util_get_release_with_tag(FuUtilPrivate *priv, + FwupdDevice *dev, + const gchar *tag, + GError **error) +{ + g_autoptr(GPtrArray) rels = NULL; + + /* find the newest release that matches */ + rels = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); + if (rels == NULL) + return NULL; + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + if (fwupd_release_has_tag(rel, tag)) + return g_object_ref(rel); + } + + /* no match */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no matching releases for device"); + return NULL; +} + +static gboolean +fu_util_prompt_warning_bkc(FuUtilPrivate *priv, FwupdDevice *dev, FwupdRelease *rel, GError **error) +{ + const gchar *host_bkc = fwupd_client_get_host_bkc(priv->client); + g_autoptr(FwupdRelease) rel_bkc = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* nothing to do */ + if (host_bkc == NULL) + return TRUE; + + /* get the release that corresponds with the host BKC */ + rel_bkc = fu_util_get_release_with_tag(priv, dev, host_bkc, &error_local); + if (rel_bkc == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("ignoring %s: %s", fwupd_device_get_id(dev), error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* device is already on a different release */ + if (g_strcmp0(fwupd_device_get_version(dev), fwupd_release_get_version(rel)) != 0) + return TRUE; + + /* TRANSLATORS: BKC is the industry name for the best known configuration and is a set + * of firmware that works together */ + g_string_append_printf(str, _("Your system is set up to the BKC of %s."), host_bkc); + g_string_append(str, "\n\n"); + g_string_append_printf( + str, + /* TRANSLATORS: %1 is the current device version number, and %2 is the + command name, e.g. `fwupdmgr sync-bkc` */ + _("This device will be reverted back to %s when the %s command is performed."), + fwupd_release_get_version(rel), + "fwupdmgr sync-bkc"); + + /* TRANSLATORS: the best known configuration is a set of software that we know works well + * together. In the OEM and ODM industries it is often called a BKC */ + fu_util_warning_box(_("Deviate from the best known configuration?"), str->str, 80); + + /* ask for confirmation */ + g_print("\n%s [Y|n]: ", + /* TRANSLATORS: prompt to apply the update */ + _("Perform operation?")); + if (!fu_util_prompt_for_boolean(TRUE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_prompt_warning_composite(FuUtilPrivate *priv, + FwupdDevice *dev, + FwupdRelease *rel, + GError **error) +{ + const gchar *rel_csum; + g_autoptr(GPtrArray) devices = NULL; + + /* get the default checksum */ + rel_csum = fwupd_checksum_get_best(fwupd_release_get_checksums(rel)); + if (rel_csum == NULL) { + g_debug("no checksum for release!"); + return TRUE; + } + + /* find other devices matching the composite ID and the release checksum */ + devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + if (devices == NULL) + return FALSE; + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev_tmp = g_ptr_array_index(devices, i); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) rels = NULL; + + /* not the parent device */ + if (g_strcmp0(fwupd_device_get_id(dev), fwupd_device_get_id(dev_tmp)) == 0) + continue; + + /* not the same composite device */ + if (g_strcmp0(fwupd_device_get_composite_id(dev), + fwupd_device_get_composite_id(dev_tmp)) != 0) + continue; + + /* get releases */ + if (!fwupd_device_has_flag(dev_tmp, FWUPD_DEVICE_FLAG_UPDATABLE)) + continue; + rels = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(dev_tmp), + priv->cancellable, + &error_local); + if (rels == NULL) { + g_debug("ignoring: %s", error_local->message); + continue; + } + + /* do any releases match this checksum */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); + if (fwupd_release_has_checksum(rel_tmp, rel_csum)) { + g_autofree gchar *title = + g_strdup_printf("%s %s", + fwupd_client_get_host_product(priv->client), + fwupd_client_get_host_product(priv->client)); + if (!fu_util_prompt_warning(dev_tmp, rel_tmp, title, error)) + return FALSE; + break; + } + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_update_device_with_release(FuUtilPrivate *priv, + FwupdDevice *dev, + FwupdRelease *rel, + GError **error) +{ + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) { + const gchar *name = fwupd_device_get_name(dev); + g_autofree gchar *str = NULL; + + /* TRANSLATORS: the device has a reason it can't update, e.g. laptop lid closed */ + str = g_strdup_printf(_("%s is not currently updatable"), name); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "%s: %s", + str, + fwupd_device_get_update_error(dev)); + return FALSE; + } + if (!priv->no_safety_check && !priv->assume_yes) { + const gchar *title = fwupd_client_get_host_product(priv->client); + if (!fu_util_prompt_warning(dev, rel, title, error)) + return FALSE; + if (!fu_util_prompt_warning_fde(dev, error)) + return FALSE; + if (!fu_util_prompt_warning_composite(priv, dev, rel, error)) + return FALSE; + if (!fu_util_prompt_warning_bkc(priv, dev, rel, error)) + return FALSE; + } + return fwupd_client_install_release2(priv->client, + dev, + rel, + priv->flags, + priv->download_flags, + priv->cancellable, + error); +} + +static gboolean +fu_util_maybe_send_reports(FuUtilPrivate *priv, FwupdRelease *rel, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GError) error_local = NULL; + if (fwupd_release_get_remote_id(rel) == NULL) { + g_debug("not sending reports, no remote"); + return TRUE; + } + remote = fwupd_client_get_remote_by_id(priv->client, + fwupd_release_get_remote_id(rel), + priv->cancellable, + error); + if (remote == NULL) + return FALSE; + if (fwupd_remote_get_automatic_reports(remote)) { + if (!fu_util_report_history(priv, NULL, &error_local)) + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) + g_warning("%s", error_local->message); + } + + return TRUE; +} + +static gboolean +fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + gboolean supported = FALSE; + gboolean no_updates_header = FALSE; + gboolean latest_header = FALSE; + + if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "--allow-older is not supported for this command"); + return FALSE; + } + + if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "--allow-reinstall is not supported for this command"); + return FALSE; + } + + /* DEVICE-ID and GUID are acceptable args to update */ + for (guint idx = 0; idx < g_strv_length(values); idx++) { + if (!fwupd_guid_is_valid(values[idx]) && !fwupd_device_id_is_valid(values[idx])) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "'%s' is not a valid GUID nor DEVICE-ID", + values[idx]); + return FALSE; + } + } + + /* get devices from daemon */ + devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + if (devices == NULL) + return FALSE; + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + FwupdRelease *rel; + const gchar *device_id = fu_device_get_id(dev); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + gboolean dev_skip_byid = TRUE; + + /* not going to have results, so save a D-Bus round-trip */ + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE) && + !fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) + continue; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { + if (!no_updates_header) { + g_printerr("%s\n", + /* TRANSLATORS: message letting the user know no device + * upgrade available due to missing on LVFS */ + _("Devices with no available firmware updates: ")); + no_updates_header = TRUE; + } + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + continue; + } + + /* only process particular DEVICE-ID or GUID if specified */ + for (guint idx = 0; idx < g_strv_length(values); idx++) { + const gchar *tmpid = values[idx]; + if (fwupd_device_has_guid(dev, tmpid) || g_strcmp0(device_id, tmpid) == 0) { + dev_skip_byid = FALSE; + break; + } + } + if (g_strv_length(values) > 0 && dev_skip_byid) + continue; + if (!fu_util_filter_device(priv, dev)) + continue; + supported = TRUE; + + /* get the releases for this device and filter for validity */ + rels = fwupd_client_get_upgrades(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + &error_local); + if (rels == NULL) { + if (!latest_header) { + g_printerr( + "%s\n", + /* TRANSLATORS: message letting the user know no device upgrade + * available */ + _("Devices with the latest available firmware version:")); + latest_header = TRUE; + } + g_printerr(" • %s\n", fwupd_device_get_name(dev)); + /* discard the actual reason from user, but leave for debugging */ + g_debug("%s", error_local->message); + continue; + } + rel = g_ptr_array_index(rels, 0); + if (!fu_util_update_device_with_release(priv, dev, rel, error)) + return FALSE; + + fu_util_display_current_message(priv); + + /* send report if we're supposed to */ + if (!fu_util_maybe_send_reports(priv, rel, error)) + return FALSE; + } + + /* no devices supported by LVFS or all are filtered */ + if (!supported) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No updatable devices"); + return FALSE; + } + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_remote_modify(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + if (g_strv_length(values) < 3) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* ensure the remote exists */ + remote = fwupd_client_get_remote_by_id(priv->client, values[0], priv->cancellable, error); + if (remote == NULL) + return FALSE; + if (!fwupd_client_modify_remote(priv->client, + fwupd_remote_get_id(remote), + values[1], + values[2], + priv->cancellable, + error)) + return FALSE; + + /* TRANSLATORS: success message for a per-remote setting change */ + g_print("%s\n", _("Successfully modified remote")); + return TRUE; +} + +static gboolean +fu_util_remote_enable(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + remote = fwupd_client_get_remote_by_id(priv->client, values[0], priv->cancellable, error); + if (remote == NULL) + return FALSE; + if (!fu_util_modify_remote_warning(priv, remote, error)) + return FALSE; + if (!fwupd_client_modify_remote(priv->client, + fwupd_remote_get_id(remote), + "Enabled", + "true", + priv->cancellable, + error)) + return FALSE; + + /* ask for permission to refresh */ + if (priv->no_remote_check || fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) { + /* TRANSLATORS: success message */ + g_print("%s\n", _("Successfully enabled remote")); + return TRUE; + } + if (!priv->assume_yes) { + g_print("%s (%s) [Y|n]: ", + /* TRANSLATORS: ask the user if we can update the metadata */ + _("Do you want to refresh this remote now?"), + /* TRANSLATORS: metadata is downloaded from the Internet */ + _("Requires internet connection")); + if (!fu_util_prompt_for_boolean(TRUE)) { + /* TRANSLATORS: success message */ + g_print("%s\n", _("Successfully enabled remote")); + return TRUE; + } + } + if (!fwupd_client_refresh_remote(priv->client, remote, priv->cancellable, error)) + return FALSE; + + /* TRANSLATORS: success message */ + g_print("\n%s\n", _("Successfully enabled and refreshed remote")); + return TRUE; +} + +static gboolean +fu_util_remote_disable(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* ensure the remote exists */ + remote = fwupd_client_get_remote_by_id(priv->client, values[0], priv->cancellable, error); + if (remote == NULL) + return FALSE; + if (!fwupd_client_modify_remote(priv->client, + values[0], + "Enabled", + "false", + priv->cancellable, + error)) + return FALSE; + + /* TRANSLATORS: success message */ + g_print("%s\n", _("Successfully disabled remote")); + return TRUE; +} + +static gboolean +fu_util_downgrade(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + + if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "--allow-reinstall is not supported for this command"); + return FALSE; + } + + priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + /* get the releases for this device and filter for validity */ + rels = fwupd_client_get_downgrades(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); + if (rels == NULL) { + g_autofree gchar *downgrade_str = + /* TRANSLATORS: message letting the user know no device downgrade available + * %1 is the device name */ + g_strdup_printf(_("No downgrades for %s"), fwupd_device_get_name(dev)); + g_prefix_error(error, "%s: ", downgrade_str); + return FALSE; + } + + /* get the chosen release */ + 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; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + if (!fu_util_update_device_with_release(priv, dev, rel, error)) + return FALSE; + + fu_util_display_current_message(priv); + + /* send report if we're supposed to */ + if (!fu_util_maybe_send_reports(priv, rel, error)) + return FALSE; + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_reinstall(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(FwupdDevice) dev = NULL; + + priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + /* try to lookup/match release from client */ + rel = fu_util_get_release_for_device_version(priv, dev, fu_device_get_version(dev), error); + if (rel == NULL) + return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (!fu_util_update_device_with_release(priv, dev, rel, error)) + return FALSE; + fu_util_display_current_message(priv); + + /* send report if we're supposed to */ + if (!fu_util_maybe_send_reports(priv, rel, error)) + return FALSE; + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FwupdDevice) dev = NULL; + g_autoptr(FwupdRelease) rel = NULL; + + /* fall back for CLI compatibility */ + if (g_strv_length(values) >= 1) { + if (g_file_test(values[0], G_FILE_TEST_EXISTS) || fu_util_is_url(values[0])) + return fu_util_local_install(priv, values, error); + } + + /* find device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + /* find release */ + if (g_strv_length(values) >= 2) { + rel = fu_util_get_release_for_device_version(priv, dev, values[1], error); + if (rel == NULL) + return FALSE; + } else { + g_autoptr(GPtrArray) rels = NULL; + rels = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); + if (rels == NULL) + return FALSE; + rel = fu_util_prompt_for_release(priv, rels, error); + if (rel == NULL) + return FALSE; + } + + /* allow all actions */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + if (!fu_util_update_device_with_release(priv, dev, rel, error)) + return FALSE; + fu_util_display_current_message(priv); + + /* send report if we're supposed to */ + if (!fu_util_maybe_send_reports(priv, rel, error)) + return FALSE; + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +_g_str_equal0(gconstpointer str1, gconstpointer str2) +{ + return g_strcmp0(str1, str2) == 0; +} + +static gboolean +fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *branch; + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func(g_free); + g_autoptr(FwupdDevice) dev = NULL; + + /* find the device and check it has multiple branches */ + priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + dev = fu_util_get_device_or_prompt(priv, values, error); + if (dev == NULL) + return FALSE; + + /* get all releases, including the alternate branch versions */ + rels = fwupd_client_get_releases(priv->client, + fwupd_device_get_id(dev), + priv->cancellable, + error); + if (rels == NULL) + return FALSE; + + /* get all the unique branches */ + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, i); + const gchar *branch_tmp = fwupd_release_get_branch(rel_tmp); +#if GLIB_CHECK_VERSION(2, 54, 3) + if (g_ptr_array_find_with_equal_func(branches, branch_tmp, _g_str_equal0, NULL)) + continue; +#endif + g_ptr_array_add(branches, g_strdup(branch_tmp)); + } + + /* branch name is optional */ + if (g_strv_length(values) > 1) { + branch = values[1]; + } else if (branches->len == 1) { + branch = g_ptr_array_index(branches, 0); + } else { + guint idx; + + /* TRANSLATORS: get interactive prompt, where branch is the + * supplier of the firmware, e.g. "non-free" or "free" */ + g_print("%s\n", _("Choose a branch:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print("0.\t%s\n", _("Cancel")); + for (guint i = 0; i < branches->len; i++) { + const gchar *branch_tmp = g_ptr_array_index(branches, i); + g_print("%u.\t%s\n", i + 1, fu_util_branch_for_display(branch_tmp)); + } + idx = fu_util_prompt_for_number(branches->len); + if (idx == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return FALSE; + } + branch = g_ptr_array_index(branches, idx - 1); + } + + /* sanity check */ + if (g_strcmp0(branch, fu_device_get_branch(dev)) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s is already on branch %s", + fu_device_get_name(dev), + fu_util_branch_for_display(branch)); + return FALSE; + } + + /* the releases are ordered by version */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); + if (g_strcmp0(fwupd_release_get_branch(rel_tmp), branch) == 0) { + rel = g_object_ref(rel_tmp); + break; + } + } + if (rel == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No releases for branch %s", + fu_util_branch_for_display(branch)); + return FALSE; + } + + /* we're switching branch */ + if (!fu_util_switch_branch_warning(dev, rel, priv->assume_yes, error)) + return FALSE; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (!fu_util_update_device_with_release(priv, dev, rel, error)) + return FALSE; + fu_util_display_current_message(priv); + + /* send report if we're supposed to */ + if (!fu_util_maybe_send_reports(priv, rel, error)) + return FALSE; + + /* we don't want to ask anything */ + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; + gboolean has_pending = FALSE; + + /* handle both forms */ + if (g_strv_length(values) == 0) { + /* activate anything with _NEEDS_ACTIVATION */ + devices = fwupd_client_get_devices(priv->client, priv->cancellable, 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 = fu_util_get_device_by_id(priv, values[0], error); + if (device == NULL) + return FALSE; + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_ptr_array_add(devices, device); + 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 */ + /* order by device priority */ + g_ptr_array_sort(devices, fu_util_device_order_sort_cb); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *device = g_ptr_array_index(devices, i); + if (!fu_util_filter_device(priv, device)) + continue; + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) + continue; + g_print("%s %s…\n", + /* TRANSLATORS: shown when shutting down to switch to the new version */ + _("Activating firmware update for"), + fwupd_device_get_name(device)); + if (!fwupd_client_activate(priv->client, + priv->cancellable, + fwupd_device_get_id(device), + error)) + return FALSE; + } + + /* TRANSLATORS: success message -- where activation is making the new + * firmware take effect, usually after updating offline */ + g_print("%s\n", _("Successfully activated all devices")); + 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: filename or list of checksums expected"); + return FALSE; + } + + /* filename */ + if (g_file_test(values[0], G_FILE_TEST_EXISTS)) { + g_autofree gchar *data = NULL; + if (!g_file_get_contents(values[0], &data, NULL, error)) + return FALSE; + checksums = g_strsplit(data, "\n", -1); + } else { + checksums = g_strsplit(values[0], ",", -1); + } + + /* call into daemon */ + return fwupd_client_set_approved_firmware(priv->client, + checksums, + priv->cancellable, + error); +} + +static gboolean +fu_util_get_checksums_as_json(FuUtilPrivate *priv, gchar **csums, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Checksums"); + json_builder_begin_array(builder); + for (guint i = 0; csums[i] != NULL; i++) + json_builder_add_string_value(builder, csums[i]); + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(builder, 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 (priv->as_json) + return fu_util_get_checksums_as_json(priv, checksums, error); + 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 { + g_print( + "%s\n", + /* TRANSLATORS: approved firmware has been checked by + * the domain administrator */ + ngettext("Approved firmware:", "Approved firmware:", g_strv_length(checksums))); + for (guint i = 0; checksums[i] != NULL; i++) + 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 [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 + /* TRANSLATORS: success message -- a per-system setting value */ + g_print("%s\n", _("Successfully modified configuration value")); + return TRUE; +} + +static FwupdRemote * +fu_util_get_remote_with_security_report_uri(FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GPtrArray) remotes = NULL; + + /* get all remotes */ + remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + if (remotes == NULL) + return NULL; + + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index(remotes, i); + if (!fwupd_remote_get_enabled(remote)) + continue; + if (fwupd_remote_get_security_report_uri(remote) != NULL) + return g_object_ref(remote); + } + + /* failed */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No remotes specified SecurityReportURI"); + return NULL; +} + +static gboolean +fu_util_upload_security(FuUtilPrivate *priv, GPtrArray *attrs, GError **error) +{ + GHashTableIter iter; + const gchar *key; + const gchar *value; + g_autofree gchar *data = NULL; + g_autofree gchar *sig = NULL; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GBytes) upload_response = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GHashTable) metadata = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* can we find a remote with a security attr */ + remote = fu_util_get_remote_with_security_report_uri(priv, &error_local); + if (remote == NULL) { + g_debug("failed to find suitable remote: %s", error_local->message); + return TRUE; + } + if (!priv->assume_yes && !fwupd_remote_get_automatic_security_reports(remote)) { + g_autofree gchar *tmp = NULL; + /* TRANSLATORS: ask the user to share, %s is something like: + * "Linux Vendor Firmware Service" */ + tmp = + g_strdup_printf("Upload these anonymous results to the %s to help other users?", + fwupd_remote_get_title(remote)); + + g_print("\n%s [y|N]: ", tmp); + if (!fu_util_prompt_for_boolean(FALSE)) { + g_print("%s [Y|n]: ", + /* TRANSLATORS: stop nagging the user */ + _("Ask again next time?")); + if (!fu_util_prompt_for_boolean(TRUE)) { + if (!fwupd_client_modify_remote(priv->client, + fwupd_remote_get_id(remote), + "SecurityReportURI", + "", + priv->cancellable, + error)) + return FALSE; + } + return TRUE; + } + } + + /* get metadata */ + metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + if (metadata == NULL) + return FALSE; + + /* create header */ + builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "ReportVersion"); + json_builder_add_int_value(builder, 2); + json_builder_set_member_name(builder, "MachineId"); + json_builder_add_string_value(builder, fwupd_client_get_host_machine_id(priv->client)); + + /* this is system metadata not stored in the database */ + json_builder_set_member_name(builder, "Metadata"); + json_builder_begin_object(builder); + + g_hash_table_iter_init(&iter, metadata); + while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) { + json_builder_set_member_name(builder, key); + json_builder_add_string_value(builder, value); + } + json_builder_set_member_name(builder, "HostSecurityId"); + json_builder_add_string_value(builder, fwupd_client_get_host_security_id(priv->client)); + json_builder_end_object(builder); + + /* attrs */ + json_builder_set_member_name(builder, "SecurityAttributes"); + json_builder_begin_array(builder); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, i); + json_builder_begin_object(builder); + fwupd_security_attr_to_json(attr, 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); + 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; + } + + /* 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 && !fwupd_remote_get_automatic_security_reports(remote)) { + fu_util_print_data(_("Target"), fwupd_remote_get_security_report_uri(remote)); + 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, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "User declined action"); + return FALSE; + } + } + + /* POST request */ + upload_response = fwupd_client_upload_bytes(priv->client, + fwupd_remote_get_security_report_uri(remote), + data, + sig, + FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART, + priv->cancellable, + error); + if (upload_response == NULL) + return FALSE; + + /* TRANSLATORS: success, so say thank you to the user */ + g_print("%s\n", "Host Security ID attributes uploaded successfully, thanks!"); + + /* as this worked, ask if the user want to do this every time */ + if (!fwupd_remote_get_automatic_security_reports(remote)) { + g_print("%s [y|N]: ", + /* TRANSLATORS: can we JFDI? */ + _("Automatically upload every time?")); + if (fu_util_prompt_for_boolean(FALSE)) { + if (!fwupd_client_modify_remote(priv->client, + fwupd_remote_get_id(remote), + "AutomaticSecurityReports", + "true", + priv->cancellable, + error)) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_util_security_as_json(FuUtilPrivate *priv, + GPtrArray *attrs, + GPtrArray *events, + GPtrArray *devices, + GError **error) +{ + g_autoptr(GPtrArray) devices_issues = NULL; + g_autoptr(JsonBuilder) builder = json_builder_new(); + + json_builder_begin_object(builder); + + /* attrs */ + json_builder_set_member_name(builder, "SecurityAttributes"); + json_builder_begin_array(builder); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, i); + json_builder_begin_object(builder); + fwupd_security_attr_to_json(attr, builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + + /* events */ + if (events != NULL && events->len > 0) { + json_builder_set_member_name(builder, "SecurityEvents"); + json_builder_begin_array(builder); + for (guint i = 0; i < attrs->len; i++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, i); + json_builder_begin_object(builder); + fwupd_security_attr_to_json(attr, builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + } + + /* devices */ + devices_issues = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; devices != NULL && i < devices->len; i++) { + FwupdDevice *device = g_ptr_array_index(devices, i); + GPtrArray *issues = fwupd_device_get_issues(device); + if (issues->len == 0) + continue; + g_ptr_array_add(devices_issues, g_object_ref(device)); + } + if (devices_issues->len > 0) { + json_builder_set_member_name(builder, "Devices"); + json_builder_begin_array(builder); + for (guint i = 0; i < devices_issues->len; i++) { + FwupdDevice *device = g_ptr_array_index(devices_issues, i); + json_builder_begin_object(builder); + fwupd_device_to_json_full(device, builder, FWUPD_DEVICE_FLAG_TRUSTED); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + } + + json_builder_end_object(builder); + return fu_util_print_builder(builder, error); +} + +static gboolean +fu_util_sync_bkc(FuUtilPrivate *priv, gchar **values, GError **error) +{ + const gchar *host_bkc = fwupd_client_get_host_bkc(priv->client); + guint cnt = 0; + g_autoptr(GPtrArray) devices = NULL; + + /* update the console if composite devices are also updated */ + priv->current_operation = FU_UTIL_OPERATION_INSTALL; + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + + /* for each device, find the release that matches the tag */ + if (host_bkc == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No HostBkc set in daemon.conf"); + return FALSE; + } + devices = fwupd_client_get_devices(priv->client, NULL, error); + if (devices == NULL) + return FALSE; + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(GError) error_local = NULL; + + rel = fu_util_get_release_with_tag(priv, dev, host_bkc, &error_local); + if (rel == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("ignoring %s: %s", + fwupd_device_get_id(dev), + error_local->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* ignore if already on that release */ + if (g_strcmp0(fwupd_device_get_version(dev), fwupd_release_get_version(rel)) == 0) + continue; + + /* install this new release */ + g_debug("need to move %s from %s to %s", + fwupd_device_get_id(dev), + fwupd_device_get_version(dev), + fwupd_release_get_version(rel)); + if (!fu_util_update_device_with_release(priv, dev, rel, error)) + return FALSE; + fu_util_display_current_message(priv); + cnt++; + } + + /* nothing was done */ + if (cnt == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No devices require modifications for target %s", + host_bkc); + return FALSE; + } + + /* 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 +fu_util_security_modify_bios_setting(FuUtilPrivate *priv, FwupdSecurityAttr *attr, GError **error) +{ + g_autoptr(GString) body = g_string_new(NULL); + g_autoptr(GString) title = g_string_new(NULL); + g_autoptr(GHashTable) bios_settings = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + g_string_append_printf(title, + "%s: %s", + /* TRANSLATORS: title prefix for the BIOS settings dialog */ + _("Configuration Change Suggested"), + fwupd_security_attr_get_title(attr)); + + g_string_append(body, fwupd_security_attr_get_description(attr)); + g_string_append(body, "\n\n"); + g_string_append_printf(body, + /* TRANSLATORS: the %1 is a BIOS setting name. %2 and %3 are the + values, e.g. "True" or "Windows10" */ + _("This tool can change the BIOS setting '%s' from '%s' to '%s' " + "automatically, but it will only be active after restarting the " + "computer."), + fwupd_security_attr_get_bios_setting_id(attr), + fwupd_security_attr_get_bios_setting_current_value(attr), + fwupd_security_attr_get_bios_setting_target_value(attr)); + g_string_append(body, "\n\n"); + g_string_append(body, + /* TRANSLATORS: the user has to manually recover; we can't do it */ + _("You should ensure you are comfortable restoring the setting from " + "the system firmware setup, as this change may cause the system " + "to not boot into Linux or cause other system instability.")); + fu_util_warning_box(title->str, body->str, 80); + + /* ask for confirmation */ + g_print("\n%s [y|N]: ", + /* TRANSLATORS: prompt to apply the update */ + _("Perform operation?")); + if (!fu_util_prompt_for_boolean(FALSE)) + return TRUE; + g_hash_table_insert(bios_settings, + g_strdup(fwupd_security_attr_get_bios_setting_id(attr)), + g_strdup(fwupd_security_attr_get_bios_setting_target_value(attr))); + if (!fwupd_client_modify_bios_setting(priv->client, + bios_settings, + priv->cancellable, + error)) + return FALSE; + + /* do not offer to upload the report */ + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + return TRUE; +} + +static gboolean +fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error) +{ + FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE; + g_autoptr(GPtrArray) attrs = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) events = NULL; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *str = NULL; + +#ifndef HAVE_HSI + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); + return FALSE; +#endif /* HAVE_HSI */ + + /* the "why" */ + attrs = fwupd_client_get_host_security_attrs(priv->client, priv->cancellable, error); + if (attrs == NULL) + return FALSE; + + for (guint j = 0; j < attrs->len; j++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, j); + g_autofree gchar *err_str = NULL; + + if (!fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA)) + continue; + if (priv->flags & FWUPD_INSTALL_FLAG_FORCE) + continue; + err_str = g_strdup_printf( + "\n%s\n » %s\n%s", + /* TRANSLATORS: error message to tell someone they can't use this feature */ + _("Not enough data was provided to make an HSI calculation."), + "https://fwupd.github.io/hsi.html#not-enough-info", + /* TRANSLATORS: message to tell someone how to ignore error */ + _("To ignore this warning, use --force")); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, err_str); + return FALSE; + } + + /* the "when" */ + events = fwupd_client_get_host_security_events(priv->client, + 10, + priv->cancellable, + &error_local); + if (events == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("ignoring failed events: %s", error_local->message); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + /* the "also" */ + devices = fwupd_client_get_devices(priv->client, priv->cancellable, &error_local); + if (devices == NULL) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + /* not for human consumption */ + if (priv->as_json) + return fu_util_security_as_json(priv, attrs, events, devices, error); + + g_print("%s \033[1m%s\033[0m\n", + /* TRANSLATORS: this is a string like 'HSI:2-U' */ + _("Host Security ID:"), + fwupd_client_get_host_security_id(priv->client)); + + /* show or hide different elements */ + if (priv->show_all) { + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES; + flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS; + } + str = fu_util_security_attrs_to_string(attrs, flags); + g_print("%s\n", str); + + /* events */ + if (events != NULL && events->len > 0) { + g_autofree gchar *estr = fu_util_security_events_to_string(events, flags); + if (estr != NULL) + g_print("%s\n", estr); + } + + /* known CVEs */ + if (devices != NULL && devices->len > 0) { + g_autofree gchar *estr = fu_util_security_issues_to_string(devices); + if (estr != NULL) + g_print("%s", estr); + } + + /* host emulation */ + for (guint j = 0; j < attrs->len; j++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, j); + if (g_strcmp0(fwupd_security_attr_get_appstream_id(attr), + FWUPD_SECURITY_ATTR_ID_HOST_EMULATION) == 0) { + priv->no_unreported_check = TRUE; + break; + } + } + + /* any things we can fix? */ + for (guint j = 0; j < attrs->len; j++) { + FwupdSecurityAttr *attr = g_ptr_array_index(attrs, j); + if (!fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS) && + fwupd_security_attr_get_bios_setting_id(attr) != NULL && + fwupd_security_attr_get_bios_setting_current_value(attr) != NULL && + fwupd_security_attr_get_bios_setting_target_value(attr) != NULL) { + if (!fu_util_security_modify_bios_setting(priv, attr, error)) + return FALSE; + } + } + + /* upload, with confirmation */ + if (!priv->no_unreported_check) { + if (!fu_util_upload_security(priv, attrs, error)) + return FALSE; + } + + /* reboot is required? */ + if (!priv->no_reboot_check && + (priv->completion_flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) > 0) { + if (!fu_util_prompt_complete(priv->completion_flags, TRUE, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_util_ignore_cb(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ +} + +#ifdef HAVE_GIO_UNIX +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; +} +#endif + +static void +fu_util_setup_signal_handlers(FuUtilPrivate *priv) +{ +#ifdef HAVE_GIO_UNIX + g_autoptr(GSource) source = g_unix_signal_source_new(SIGINT); + g_source_set_callback(source, fu_util_sigint_cb, priv, NULL); + g_source_attach(g_steal_pointer(&source), priv->main_ctx); +#endif +} + +static void +fu_util_private_free(FuUtilPrivate *priv) +{ + if (priv->client != NULL) + g_object_unref(priv->client); + if (priv->current_device != NULL) + g_object_unref(priv->current_device); + g_ptr_array_unref(priv->post_requests); + g_main_context_unref(priv->main_ctx); + g_object_unref(priv->cancellable); + g_object_unref(priv->progressbar); + g_option_context_free(priv->context); + g_free(priv); +} + +static gboolean +fu_util_check_daemon_version(FuUtilPrivate *priv, GError **error) +{ + const gchar *daemon = fwupd_client_get_daemon_version(priv->client); + + if (daemon == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message */ + _("Unable to connect to service")); + return FALSE; + } + + if (g_strcmp0(daemon, SOURCE_VERSION) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message */ + _("Unsupported daemon version %s, client version is %s"), + daemon, + SOURCE_VERSION); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_check_polkit_actions(GError **error) +{ +#ifdef HAVE_POLKIT + g_autofree gchar *directory = fu_path_from_kind(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/fwupd/fwupd/wiki/PolicyKit-files-are-missing"); + return FALSE; + } +#endif + + return TRUE; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +#pragma clang diagnostic pop + +static gchar * +fu_util_get_history_checksum(FuUtilPrivate *priv, GError **error) +{ + const gchar *csum; + g_autoptr(FwupdDevice) device = NULL; + g_autoptr(FwupdRelease) release = NULL; + g_autoptr(GPtrArray) devices = NULL; + + devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + if (devices == NULL) + return NULL; + device = fu_util_prompt_for_device(priv, devices, error); + if (device == NULL) + return NULL; + release = fu_util_prompt_for_release(priv, fwupd_device_get_releases(device), error); + if (release == NULL) + return NULL; + csum = fwupd_checksum_get_best(fwupd_release_get_checksums(release)); + if (csum == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No suitable checksums"); + return NULL; + } + return g_strdup(csum); +} + +static gboolean +fu_util_block_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +{ + guint idx = 0; + g_autofree gchar *csum = NULL; + g_auto(GStrv) csums_new = NULL; + g_auto(GStrv) csums = NULL; + + /* get existing checksums */ + csums = fwupd_client_get_blocked_firmware(priv->client, priv->cancellable, error); + if (csums == NULL) + return FALSE; + + /* get new value */ + if (g_strv_length(values) == 0) { + csum = fu_util_get_history_checksum(priv, error); + if (csum == NULL) + return FALSE; + } else { + csum = g_strdup(values[0]); + } + + /* ensure it's not already there */ + if (g_strv_contains((const gchar *const *)csums, csum)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: user selected something not possible */ + _("Firmware is already blocked")); + return FALSE; + } + + /* TRANSLATORS: we will not offer this firmware to the user */ + g_print("%s %s\n", _("Blocking firmware:"), csum); + + /* remove it from the new list */ + csums_new = g_new0(gchar *, g_strv_length(csums) + 2); + for (guint i = 0; csums[i] != NULL; i++) { + if (g_strcmp0(csums[i], csum) != 0) + csums_new[idx++] = g_strdup(csums[i]); + } + csums_new[idx] = g_strdup(csum); + return fwupd_client_set_blocked_firmware(priv->client, csums_new, priv->cancellable, error); +} + +static gboolean +fu_util_unblock_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +{ + guint idx = 0; + g_auto(GStrv) csums = NULL; + g_auto(GStrv) csums_new = NULL; + g_autofree gchar *csum = NULL; + + /* get existing checksums */ + csums = fwupd_client_get_blocked_firmware(priv->client, priv->cancellable, error); + if (csums == NULL) + return FALSE; + + /* empty list */ + if (g_strv_length(csums) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: nothing to show */ + _("There are no blocked firmware files")); + return FALSE; + } + + /* get new value */ + if (g_strv_length(values) == 0) { + csum = fu_util_get_history_checksum(priv, error); + if (csum == NULL) + return FALSE; + } else { + csum = g_strdup(values[0]); + } + + /* ensure it's there */ + if (!g_strv_contains((const gchar *const *)csums, csum)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: user selected something not possible */ + _("Firmware is not already blocked")); + return FALSE; + } + + /* TRANSLATORS: we will now offer this firmware to the user */ + g_print("%s %s\n", _("Unblocking firmware:"), csum); + + /* remove it from the new list */ + csums_new = g_new0(gchar *, g_strv_length(csums)); + for (guint i = 0; csums[i] != NULL; i++) { + if (g_strcmp0(csums[i], csum) != 0) + csums_new[idx++] = g_strdup(csums[i]); + } + return fwupd_client_set_blocked_firmware(priv->client, csums_new, priv->cancellable, error); +} + +static gboolean +fu_util_get_blocked_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_auto(GStrv) csums = NULL; + + /* get checksums */ + csums = fwupd_client_get_blocked_firmware(priv->client, priv->cancellable, error); + if (csums == NULL) + return FALSE; + if (priv->as_json) + return fu_util_get_checksums_as_json(priv, csums, error); + + /* empty list */ + if (g_strv_length(csums) == 0) { + /* TRANSLATORS: nothing to show */ + g_print("%s\n", _("There are no blocked firmware files")); + return TRUE; + } + + /* TRANSLATORS: there follows a list of hashes */ + g_print("%s\n", _("Blocked firmware files:")); + for (guint i = 0; csums[i] != NULL; i++) { + g_print("%u.\t%s\n", i + 1, csums[i]); + } + + /* success */ + return TRUE; +} + +static void +fu_util_show_plugin_warnings(FuUtilPrivate *priv) +{ + FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE; + g_autoptr(GPtrArray) plugins = NULL; + + /* get plugins from daemon, ignoring if the daemon is too old */ + plugins = fwupd_client_get_plugins(priv->client, priv->cancellable, NULL); + if (plugins == NULL) + return; + + /* get a superset so we do not show the same message more than once */ + for (guint i = 0; i < plugins->len; i++) { + FwupdPlugin *plugin = g_ptr_array_index(plugins, i); + if (fwupd_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + if (!fwupd_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING)) + continue; + flags |= fwupd_plugin_get_flags(plugin); + } + + /* never show these, they're way too generic */ + flags &= ~FWUPD_PLUGIN_FLAG_DISABLED; + flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE; + flags &= ~FWUPD_PLUGIN_FLAG_REQUIRE_HWID; + + /* print */ + for (guint i = 0; i < 64; i++) { + FwupdPluginFlags flag = (guint64)1 << i; + const gchar *tmp; + g_autofree gchar *fmt = NULL; + g_autofree gchar *url = NULL; + g_autoptr(GString) str = g_string_new(NULL); + if ((flags & flag) == 0) + continue; + tmp = fu_util_plugin_flag_to_string(flag); + if (tmp == NULL) + continue; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_string_append_printf(str, "%s %s\n", fmt, tmp); + + url = g_strdup_printf("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s", + fwupd_plugin_flag_to_string(flag)); + g_string_append(str, " "); + /* TRANSLATORS: %s is a link to a website */ + g_string_append_printf(str, _("See %s for more information."), url); + g_string_append(str, "\n"); + g_printerr("%s", str->str); + } +} + +static gboolean +fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error) +{ + g_autoptr(GHashTable) settings = fu_util_bios_settings_parse_argv(input, error); + + if (settings == NULL) + return FALSE; + + if (!fwupd_client_modify_bios_setting(priv->client, settings, priv->cancellable, error)) { + if (!g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) + g_prefix_error(error, "failed to set BIOS setting: "); + return FALSE; + } + + if (!priv->as_json) { + gpointer key, value; + GHashTableIter iter; + + g_hash_table_iter_init(&iter, settings); + while (g_hash_table_iter_next(&iter, &key, &value)) { + g_autofree gchar *msg = + /* TRANSLATORS: Configured a BIOS setting to a value */ + g_strdup_printf(_("Set BIOS setting '%s' using '%s'."), + (const gchar *)key, + (const gchar *)value); + g_print("\n%s\n", msg); + } + } + priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + + if (priv->no_reboot_check) { + g_debug("skipping reboot check"); + return TRUE; + } + + return fu_util_prompt_complete(priv->completion_flags, TRUE, error); +} + +static gboolean +fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) attrs = NULL; + gboolean found = FALSE; + + attrs = fwupd_client_get_bios_settings(priv->client, priv->cancellable, error); + if (attrs == NULL) + return FALSE; + if (priv->as_json) + return fu_util_get_bios_setting_as_json(values, attrs, error); + + for (guint i = 0; i < attrs->len; i++) { + FwupdBiosSetting *attr = g_ptr_array_index(attrs, i); + if (fu_util_bios_setting_matches_args(attr, values)) { + g_autofree gchar *tmp = fu_util_bios_setting_to_string(attr, 0); + g_print("\n%s\n", tmp); + found = TRUE; + } + } + if (attrs->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: error message */ + _("This system doesn't support firmware settings")); + return FALSE; + } + if (!found) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + /* TRANSLATORS: error message */ + _("Unable to find attribute")); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_util_version(FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GHashTable) metadata = NULL; + g_autofree gchar *str = NULL; + + /* get metadata */ + metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + if (metadata == NULL) + return FALSE; + + /* dump to the screen in the most appropriate format */ + if (priv->as_json) + return fu_util_project_versions_as_json(metadata, error); + str = fu_util_project_versions_to_string(metadata); + g_print("%s", str); + return TRUE; +} + +static gboolean +fu_util_setup_interactive(FuUtilPrivate *priv, GError **error) +{ + if (priv->as_json) { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "using --json"); + return FALSE; + } + return fu_util_setup_interactive_console(error); +} + +int +main(int argc, char *argv[]) +{ + gboolean force = FALSE; + gboolean allow_branch_switch = FALSE; + gboolean allow_older = FALSE; + gboolean allow_reinstall = FALSE; + gboolean enable_ipfs = FALSE; + gboolean is_interactive = FALSE; + gboolean no_history = FALSE; + gboolean no_authenticate = FALSE; + gboolean offline = FALSE; + gboolean ret; + gboolean verbose = FALSE; + gboolean version = FALSE; + g_autoptr(FuUtilPrivate) priv = g_new0(FuUtilPrivate, 1); + g_autoptr(GDateTime) dt_now = g_date_time_new_now_utc(); + g_autoptr(GError) error = NULL; + g_autoptr(GError) error_console = NULL; + g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new(); + g_autofree gchar *cmd_descriptions = NULL; + g_autofree gchar *filter = NULL; + const GOptionEntry options[] = { + {"verbose", + 'v', + 0, + G_OPTION_ARG_NONE, + &verbose, + /* TRANSLATORS: command line option */ + N_("Show extra debugging information"), + NULL}, + {"version", + '\0', + 0, + G_OPTION_ARG_NONE, + &version, + /* TRANSLATORS: command line option */ + N_("Show client and daemon versions"), + NULL}, + {"offline", + '\0', + 0, + G_OPTION_ARG_NONE, + &offline, + /* TRANSLATORS: command line option */ + N_("Schedule installation for next reboot when possible"), + NULL}, + {"allow-reinstall", + '\0', + 0, + G_OPTION_ARG_NONE, + &allow_reinstall, + /* TRANSLATORS: command line option */ + N_("Allow reinstalling existing firmware versions"), + NULL}, + {"allow-older", + '\0', + 0, + G_OPTION_ARG_NONE, + &allow_older, + /* TRANSLATORS: command line option */ + N_("Allow downgrading firmware versions"), + NULL}, + {"allow-branch-switch", + '\0', + 0, + G_OPTION_ARG_NONE, + &allow_branch_switch, + /* TRANSLATORS: command line option */ + N_("Allow switching firmware branch"), + NULL}, + {"force", + '\0', + 0, + G_OPTION_ARG_NONE, + &force, + /* TRANSLATORS: command line option */ + N_("Force the action by relaxing some runtime checks"), + NULL}, + {"assume-yes", + 'y', + 0, + G_OPTION_ARG_NONE, + &priv->assume_yes, + /* TRANSLATORS: command line option */ + N_("Answer yes to all questions"), + NULL}, + {"sign", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->sign, + /* TRANSLATORS: command line option */ + N_("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 */ + N_("Do not check for unreported history"), + NULL}, + {"no-metadata-check", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_metadata_check, + /* TRANSLATORS: command line option */ + N_("Do not check for old metadata"), + NULL}, + {"no-remote-check", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_remote_check, + /* TRANSLATORS: command line option */ + N_("Do not check if download remotes should be enabled"), + NULL}, + {"no-reboot-check", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_reboot_check, + /* TRANSLATORS: command line option */ + N_("Do not check or prompt for reboot after update"), + NULL}, + {"no-safety-check", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_safety_check, + /* TRANSLATORS: command line option */ + N_("Do not perform device safety checks"), + NULL}, + {"no-device-prompt", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->no_device_prompt, + /* TRANSLATORS: command line option */ + N_("Do not prompt for devices"), + NULL}, + {"no-history", + '\0', + 0, + G_OPTION_ARG_NONE, + &no_history, + /* TRANSLATORS: command line option */ + N_("Do not write to the history database"), + NULL}, + {"show-all", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->show_all, + /* TRANSLATORS: command line option */ + N_("Show all results"), + NULL}, + {"show-all-devices", + '\0', + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, + &priv->show_all, + /* TRANSLATORS: command line option */ + N_("Show devices that are not updatable"), + NULL}, + {"disable-ssl-strict", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->disable_ssl_strict, + /* TRANSLATORS: command line option */ + N_("Ignore SSL strict checks when downloading files"), + NULL}, + {"ipfs", + '\0', + 0, + G_OPTION_ARG_NONE, + &enable_ipfs, + /* TRANSLATORS: command line option */ + N_("Only use IPFS when downloading files"), + NULL}, + {"filter", + '\0', + 0, + G_OPTION_ARG_STRING, + &filter, + /* TRANSLATORS: command line option */ + N_("Filter with a set of device flags using a ~ prefix to " + "exclude, e.g. 'internal,~needs-reboot'"), + NULL}, + {"json", + '\0', + 0, + G_OPTION_ARG_NONE, + &priv->as_json, + /* TRANSLATORS: command line option */ + N_("Output in JSON format"), + NULL}, + {"no-authenticate", + '\0', + 0, + G_OPTION_ARG_NONE, + &no_authenticate, + /* TRANSLATORS: command line option */ + N_("Don't prompt for authentication (less details may be shown)"), + NULL}, + {NULL}}; + +#ifdef _WIN32 + /* workaround Windows setting the codepage to 1252 */ + (void)g_setenv("LANG", "C.UTF-8", FALSE); +#endif + + setlocale(LC_ALL, ""); + + bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + /* ensure D-Bus errors are registered */ + (void)fwupd_error_quark(); + + /* this is an old command which is possibly a symlink */ + if (g_str_has_suffix(argv[0], "fwupdagent")) { + g_printerr("INFO: The fwupdagent command is deprecated, " + "use `fwupdmgr --json` instead\n"); + priv->as_json = TRUE; + } + + /* create helper object */ + priv->main_ctx = g_main_context_new(); + priv->progressbar = fu_progressbar_new(); + priv->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + fu_progressbar_set_main_context(priv->progressbar, priv->main_ctx); + + /* add commands */ + fu_util_cmd_array_add(cmd_array, + "get-devices,get-topology", + NULL, + /* TRANSLATORS: command description */ + _("Get all devices that support firmware updates"), + fu_util_get_devices); + fu_util_cmd_array_add(cmd_array, + "get-history", + NULL, + /* TRANSLATORS: command description */ + _("Show history of firmware updates"), + fu_util_get_history); + 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_cmd_array_add(cmd_array, + "install", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID] [VERSION]"), + /* TRANSLATORS: command description */ + _("Install a specific firmware on a device, all possible devices" + " will also be installed once the CAB matches"), + fu_util_install); + fu_util_cmd_array_add(cmd_array, + "local-install", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILE [DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Install a firmware file on this hardware"), + fu_util_local_install); + fu_util_cmd_array_add(cmd_array, + "get-details", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILE"), + /* TRANSLATORS: command description */ + _("Gets details about a firmware file"), + fu_util_get_details); + fu_util_cmd_array_add(cmd_array, + "get-updates,get-upgrades", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Gets the list of updates for connected hardware"), + fu_util_get_updates); + fu_util_cmd_array_add(cmd_array, + "update,upgrade", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Updates all specified devices to latest firmware version, or all " + "devices if unspecified"), + fu_util_update); + fu_util_cmd_array_add(cmd_array, + "verify", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Checks cryptographic hash matches firmware"), + fu_util_verify); + fu_util_cmd_array_add(cmd_array, + "unlock", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID|GUID"), + /* TRANSLATORS: command description */ + _("Unlocks the device for firmware access"), + fu_util_unlock); + fu_util_cmd_array_add(cmd_array, + "clear-results", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID|GUID"), + /* TRANSLATORS: command description */ + _("Clears the results from the last update"), + fu_util_clear_results); + fu_util_cmd_array_add(cmd_array, + "get-results", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID|GUID"), + /* TRANSLATORS: command description */ + _("Gets the results from the last update"), + fu_util_get_results); + fu_util_cmd_array_add(cmd_array, + "get-releases", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Gets the releases for a device"), + fu_util_get_releases); + fu_util_cmd_array_add(cmd_array, + "get-remotes", + NULL, + /* TRANSLATORS: command description */ + _("Gets the configured remotes"), + fu_util_get_remotes); + fu_util_cmd_array_add(cmd_array, + "downgrade", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Downgrades the firmware on a device"), + fu_util_downgrade); + fu_util_cmd_array_add(cmd_array, + "refresh", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[FILE FILE_SIG REMOTE-ID]"), + /* TRANSLATORS: command description */ + _("Refresh metadata from remote server"), + fu_util_refresh); + fu_util_cmd_array_add(cmd_array, + "verify-update", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Update the stored cryptographic hash with current ROM contents"), + fu_util_verify_update); + fu_util_cmd_array_add(cmd_array, + "modify-remote", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("REMOTE-ID KEY VALUE"), + /* TRANSLATORS: command description */ + _("Modifies a given remote"), + fu_util_remote_modify); + fu_util_cmd_array_add(cmd_array, + "enable-remote", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("REMOTE-ID"), + /* TRANSLATORS: command description */ + _("Enables a given remote"), + fu_util_remote_enable); + fu_util_cmd_array_add(cmd_array, + "disable-remote", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("REMOTE-ID"), + /* TRANSLATORS: command description */ + _("Disables a given remote"), + fu_util_remote_disable); + fu_util_cmd_array_add(cmd_array, + "activate", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* 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", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME|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", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("KEY,VALUE"), + /* TRANSLATORS: sets something in daemon.conf */ + _("Modifies a daemon configuration value"), + fu_util_modify_config); + fu_util_cmd_array_add(cmd_array, + "reinstall", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Reinstall current firmware on the device"), + fu_util_reinstall); + fu_util_cmd_array_add(cmd_array, + "switch-branch", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[DEVICE-ID|GUID] [BRANCH]"), + /* TRANSLATORS: command description */ + _("Switch the firmware branch on the device"), + fu_util_switch_branch); + fu_util_cmd_array_add(cmd_array, + "security", + NULL, + /* TRANSLATORS: command description */ + _("Gets the host security attributes"), + fu_util_security); + fu_util_cmd_array_add(cmd_array, + "sync-bkc", + NULL, + /* TRANSLATORS: command description */ + _("Sync firmware versions to the host best known configuration"), + fu_util_sync_bkc); + fu_util_cmd_array_add(cmd_array, + "block-firmware", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[CHECKSUM]"), + /* TRANSLATORS: command description */ + _("Blocks a specific firmware from being installed"), + fu_util_block_firmware); + fu_util_cmd_array_add(cmd_array, + "unblock-firmware", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[CHECKSUM]"), + /* TRANSLATORS: command description */ + _("Unblocks a specific firmware from being installed"), + fu_util_unblock_firmware); + fu_util_cmd_array_add(cmd_array, + "get-blocked-firmware", + NULL, + /* TRANSLATORS: command description */ + _("Gets the list of blocked firmware"), + fu_util_get_blocked_firmware); + 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_cmd_array_add(cmd_array, + "download", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("LOCATION"), + /* TRANSLATORS: command description */ + _("Download a file"), + fu_util_download); + fu_util_cmd_array_add(cmd_array, + "device-test", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[FILENAME1] [FILENAME2]"), + /* TRANSLATORS: command description */ + _("Test a device using a JSON manifest"), + fu_util_device_test); + fu_util_cmd_array_add( + cmd_array, + "get-bios-settings,get-bios-setting", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[SETTING1] [SETTING2] [--no-authenticate]"), + /* TRANSLATORS: command description */ + _("Retrieve BIOS settings. If no arguments are passed all settings are returned"), + fu_util_get_bios_setting); + fu_util_cmd_array_add(cmd_array, + "set-bios-setting", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("SETTING1 VALUE1 [SETTING2] [VALUE2]"), + /* TRANSLATORS: command description */ + _("Sets one or more BIOS settings"), + fu_util_set_bios_setting); + + /* do stuff on ctrl+c */ + priv->cancellable = g_cancellable_new(); + fu_util_setup_signal_handlers(priv); + + /* sort by command name */ + fu_util_cmd_array_sort(cmd_array); + + /* non-TTY consoles cannot answer questions */ + if (!fu_util_setup_interactive(priv, &error_console)) { + g_debug("failed to initialize interactive console: %s", error_console->message); + priv->no_unreported_check = TRUE; + priv->no_metadata_check = TRUE; + priv->no_reboot_check = TRUE; + priv->no_safety_check = TRUE; + priv->no_remote_check = TRUE; + priv->no_device_prompt = TRUE; + } else { + is_interactive = TRUE; + } + fu_progressbar_set_interactive(priv->progressbar, is_interactive); + + /* 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, + /* TRANSLATORS: CLI description */ + _("This tool allows an administrator to query and control the " + "fwupd daemon, allowing them to perform actions such as " + "installing or downgrading firmware.")); + + /* TRANSLATORS: program name */ + g_set_application_name(_("Firmware Utility")); + g_option_context_add_main_entries(priv->context, options, NULL); + ret = g_option_context_parse(priv->context, &argc, &argv, &error); + 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; + } + + /* allow disabling SSL strict mode for broken corporate proxies */ + if (priv->disable_ssl_strict) { + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_printerr("%s %s\n", + fmt, + /* TRANSLATORS: try to help */ + _("Ignoring SSL strict checks, " + "to do this automatically in the future " + "export DISABLE_SSL_STRICT in your environment")); + (void)g_setenv("DISABLE_SSL_STRICT", "1", TRUE); + } + + /* this doesn't have to be precise (e.g. using the build-year) as we just + * want to check the clock is not set to the default of 1970-01-01... */ + if (g_date_time_get_year(dt_now) < 2021) { + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_printerr("%s %s\n", + fmt, + /* TRANSLATORS: try to help */ + _("The system clock has not been set " + "correctly and downloading files may fail.")); + } + + /* parse filter flags */ + if (filter != NULL) { + if (!fu_util_parse_filter_flags(filter, + &priv->filter_include, + &priv->filter_exclude, + &error)) { + g_print("%s: %s\n", + /* TRANSLATORS: the user didn't read the man page */ + _("Failed to parse flags for --filter"), + error->message); + return EXIT_FAILURE; + } + } + + /* set verbose? */ + if (verbose) { + (void)g_setenv("G_MESSAGES_DEBUG", "all", FALSE); + (void)g_setenv("FWUPD_VERBOSE", "1", FALSE); + } else { + g_log_set_handler(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fu_util_ignore_cb, NULL); + } + + /* set flags */ + if (offline) + priv->flags |= FWUPD_INSTALL_FLAG_OFFLINE; + if (allow_reinstall) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (allow_older) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + if (allow_branch_switch) + priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (force) + priv->flags |= FWUPD_INSTALL_FLAG_FORCE; + if (no_history) + priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; + + /* use IPFS for metadata and firmware *only* if specified */ + if (enable_ipfs) + priv->download_flags |= FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS; + +#ifdef HAVE_POLKIT + /* start polkit tty agent to listen for password requests */ + if (is_interactive) { + g_autoptr(GError) error_polkit = NULL; + if (!fu_polkit_agent_open(&error_polkit)) { + g_printerr("Failed to open polkit agent: %s\n", error_polkit->message); + } + } +#endif + + /* connect to the daemon */ + priv->client = fwupd_client_new(); + fwupd_client_set_main_context(priv->client, priv->main_ctx); + g_signal_connect(FWUPD_CLIENT(priv->client), + "notify::percentage", + G_CALLBACK(fu_util_client_notify_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "notify::status", + G_CALLBACK(fu_util_client_notify_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + g_signal_connect(FWUPD_CLIENT(priv->client), + "device-request", + G_CALLBACK(fu_util_update_device_request_cb), + priv); + + /* show a warning if the daemon is tainted */ + if (!fwupd_client_connect(priv->client, priv->cancellable, &error)) { +#ifdef _WIN32 + /* TRANSLATORS: error message for Windows */ + g_printerr(_("Failed to connect to Windows service, please ensure it's running.")); + g_debug("%s", error->message); +#else + g_printerr("Failed to connect to daemon: %s\n", error->message); +#endif + return EXIT_FAILURE; + } + if (fwupd_client_get_tainted(priv->client)) { + g_autofree gchar *fmt = NULL; + /* TRANSLATORS: this is a prefix on the console */ + fmt = fu_util_term_format(_("WARNING:"), FU_UTIL_TERM_COLOR_RED); + g_printerr("%s %s\n", + fmt, + /* TRANSLATORS: the user is SOL for support... */ + _("The daemon has loaded 3rd party code and " + "is no longer supported by the upstream developers!")); + } + + /* just show versions and exit */ + if (version) { + if (!fu_util_version(priv, &error)) { + g_printerr("%s\n", error->message); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } + + /* show user-visible warnings from the plugins */ + fu_util_show_plugin_warnings(priv); + + /* show any unsupported warnings */ + fu_util_show_unsupported_warn(); + + /* we know the runtime daemon version now */ + fwupd_client_set_user_agent_for_package(priv->client, "fwupdmgr", PACKAGE_VERSION); + + /* check that we have at least this version daemon running */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_util_check_daemon_version(priv, &error)) { + 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 && + !fwupd_client_get_daemon_interactive(priv->client) && + !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; + } + + /* send our implemented feature set */ + if (is_interactive) { + FwupdFeatureFlags flags = + FWUPD_FEATURE_FLAG_CAN_REPORT | FWUPD_FEATURE_FLAG_SWITCH_BRANCH | + FWUPD_FEATURE_FLAG_REQUESTS | FWUPD_FEATURE_FLAG_UPDATE_ACTION | + FWUPD_FEATURE_FLAG_FDE_WARNING | FWUPD_FEATURE_FLAG_DETACH_ACTION | + FWUPD_FEATURE_FLAG_COMMUNITY_TEXT | FWUPD_FEATURE_FLAG_SHOW_PROBLEMS; + if (!no_authenticate) + flags |= FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION; + if (!fwupd_client_set_feature_flags(priv->client, + flags, + priv->cancellable, + &error)) { + g_printerr("Failed to set front-end features: %s\n", error->message); + return EXIT_FAILURE; + } + } + + /* run the specified command */ + ret = fu_util_cmd_array_run(cmd_array, priv, argv[1], (gchar **)&argv[2], &error); + if (!ret) { +#ifdef SUPPORTED_BUILD + /* sanity check */ + if (error == NULL) { + g_critical("exec failed but no error set!"); + return EXIT_FAILURE; + } +#endif + if (priv->as_json) + g_debug("%s\n", error->message); + else + g_printerr("%s\n", error->message); + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { + /* TRANSLATORS: error message explaining command on how to get help */ + g_printerr("\n%s\n", _("Use fwupdmgr --help for help")); + } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) + return EXIT_NOTHING_TO_DO; + return EXIT_FAILURE; + } + +#ifdef HAVE_POLKIT + /* stop listening for polkit questions */ + fu_polkit_agent_close(); +#endif + + /* success */ + return EXIT_SUCCESS; +} diff --git a/fwupd-1.8.6/src/fwupd-windows.mc b/fwupd-1.8.6/src/fwupd-windows.mc new file mode 100644 index 0000000000000000000000000000000000000000..772947c7a0c77ee72efc55cbb2732490d12be307 --- /dev/null +++ b/fwupd-1.8.6/src/fwupd-windows.mc @@ -0,0 +1,11 @@ +MessageId=0x0 +SymbolicName=FWUPD_CATEGORY_GENERIC +Language=English +Generic Events +. + +MessageId=0x100 +SymbolicName=FWUPD_MESSAGE_GENERIC +Language=English +No specific message -- see the EventData in the Details tab +. diff --git a/fwupd-1.8.6/src/fwupd.gresource.xml b/fwupd-1.8.6/src/fwupd.gresource.xml new file mode 100644 index 0000000000000000000000000000000000000000..ea5d606d333413e9f6f2b05f566224b054788768 --- /dev/null +++ b/fwupd-1.8.6/src/fwupd.gresource.xml @@ -0,0 +1,6 @@ + + + + org.freedesktop.fwupd.xml + + diff --git a/fwupd-1.8.6/src/fwupdagent.1 b/fwupd-1.8.6/src/fwupdagent.1 new file mode 100644 index 0000000000000000000000000000000000000000..23406d59578bc5077128fa0430370427805ddc18 --- /dev/null +++ b/fwupd-1.8.6/src/fwupdagent.1 @@ -0,0 +1,18 @@ +.\" Report problems in https://github.com/fwupd/fwupd +.TH man 1 "11 April 2021" @PACKAGE_VERSION@ "fwupdagent man page" +.SH NAME +fwupdagent \- firmware updating agent +.SH SYNOPSIS +fwupdagent [CMD] +.SH DESCRIPTION +fwupdagent used to be a command line fwupd client intended to be used by scripts. +You should now use the 100% compatible \fBfwupdmgr --json\fR command instead. +The output is JSON and guaranteed to be stable. +.SH EXIT STATUS +Commands that successfully execute will return "0". +.SH SEE ALSO +fwupdmgr(1), fwupdtool(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Richard Hughes (richard@hughsie.com) diff --git a/fwupd-1.8.6/src/fwupdmgr.1 b/fwupd-1.8.6/src/fwupdmgr.1 new file mode 100644 index 0000000000000000000000000000000000000000..17a06807aa4649d3b3aed8ac3c33295ff2cf993b --- /dev/null +++ b/fwupd-1.8.6/src/fwupdmgr.1 @@ -0,0 +1,21 @@ +.\" Report problems in https://github.com/fwupd/fwupd +.TH man 1 "11 April 2021" @PACKAGE_VERSION@ "fwupdmgr man page" +.SH NAME +fwupdmgr \- firmware update manager client utility +.SH SYNOPSIS +fwupdmgr [CMD] +.SH DESCRIPTION +fwupdmgr is a command line fwupd client intended to be used interactively. +The output between versions of fwupd is not guaranteed to be stable. +.SH OPTIONS +The fwupdmgr command takes various options depending on the action. +Run \fBfwupdmgr --help\fR for the full list. +.SH EXIT STATUS +Commands that successfully execute will return "0", but commands that have no +actions but successfully execute will return "2". +.SH SEE ALSO +fwupdagent(1), fwupdtool(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Richard Hughes (richard@hughsie.com) diff --git a/fwupd-1.8.6/src/fwupdtool.1 b/fwupd-1.8.6/src/fwupdtool.1 new file mode 100644 index 0000000000000000000000000000000000000000..ae53fb4b4cec03d5c331f1297b801ce14e059040 --- /dev/null +++ b/fwupd-1.8.6/src/fwupdtool.1 @@ -0,0 +1,25 @@ +.\" Report problems in https://github.com/fwupd/fwupd +.TH man 1 "11 April 2021" @PACKAGE_VERSION@ "fwupdtool man page" +.SH NAME +fwupdtool \- standalone firmware update utility +.SH SYNOPSIS +fwupdtool [CMD] +.SH DESCRIPTION +This tool allows an administrator to use the fwupd plugins without being +installed on the host system. +.PP +Additionally \fBdfu-tool\fR can be used to convert firmware from various different formats, +or to modify the images contained inside the container firmware file. +For example, you can convert DFU or Intel HEX firmware into the vendor-specific format. +.SH OPTIONS +The fwupdtool command takes various options depending on the action. +Run \fBfwupdtool --help\fR for the full list. +.SH EXIT STATUS +Commands that successfully execute will return "0", but commands that have no +actions but successfully execute will return "2". +.SH SEE ALSO +fwupdagent(1), fwupdmgr(1) +.SH BUGS +No known bugs. +.SH AUTHOR +Richard Hughes (richard@hughsie.com) diff --git a/fwupd-1.8.6/src/meson.build b/fwupd-1.8.6/src/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..84c84c9861bce6c1f55d1eab96602d3b2e8cdc2e --- /dev/null +++ b/fwupd-1.8.6/src/meson.build @@ -0,0 +1,347 @@ +if get_option('tests') +subdir('tests') +endif + +client_src = [] +systemd_src = [] +daemon_dep = [ + libarchive, + libjcat, + libxmlb, + libgcab, + giounix, + gmodule, + gudev, + gusb, + libjsonglib, + polkit, + sqlite, + cbor, +] + +client_dep = [ + gudev, + gusb, + libcurl, + libjsonglib, + libxmlb, + sqlite, +] +if libsystemd.found() + systemd_src += 'fu-systemd.c' +endif +if polkit.found() + client_src += 'fu-polkit-agent.c' +endif + +fwupd_engine_src = [ + 'fu-cabinet-common.c', + 'fu-config.c', + 'fu-debug.c', + 'fu-device-list.c', + 'fu-engine.c', + 'fu-engine-helper.c', + 'fu-engine-request.c', + 'fu-history.c', + 'fu-idle.c', + 'fu-release.c', + 'fu-release-common.c', + 'fu-keyring-utils.c', + 'fu-plugin-list.c', + 'fu-remote-list.c', + 'fu-security-attr-common.c', +] + systemd_src + +if gudev.found() + fwupd_engine_src += 'fu-udev-backend.c' +endif +if gusb.found() + fwupd_engine_src += 'fu-usb-backend.c' +endif +if bluez.allowed() + fwupd_engine_src += 'fu-bluez-backend.c' +endif + +# include event message file +if host_machine.system() == 'windows' + windmc = find_program('windmc') + fwupd_rc = custom_target('fwupd-rc', + input: 'fwupd-windows.mc', + output: 'fwupd-windows.rc', + command: [ + windmc, '@INPUT@', '--rcdir', meson.current_build_dir(), + ], + ) + windows = import('windows') + fwupd_engine_src += windows.compile_resources(fwupd_rc) +endif + +fwupdutil = library( + 'fwupdutil', + sources: [ + 'fu-progressbar.c', + 'fu-security-attr-common.c', + 'fu-util-bios-setting.c', + 'fu-util-common.c', + systemd_src, + ], + install: true, + install_dir: libdir_pkg, + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies: [ + client_dep, + ], + link_with: [ + fwupd, + fwupdplugin, + ], +) + +if build_daemon +install_data(['org.freedesktop.fwupd.xml'], + install_dir: join_paths(datadir, 'dbus-1', 'interfaces') +) +fwupdmgr = executable( + 'fwupdmgr', + sources: [ + 'fu-util.c', + client_src, + ], + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies: [ + libfwupd_deps, + client_dep, + ], + link_with: [ + fwupd, + fwupdplugin, + fwupdutil, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: bindir +) + +# for compatibility +if get_option('compat_cli') + install_symlink('fwupdagent', + install_dir: join_paths(get_option('prefix'), get_option('bindir')), + pointing_to: 'fwupdmgr', + ) +endif +endif + +if offline.allowed() +fwupdoffline = executable( + 'fwupdoffline', + sources: [ + 'fu-history.c', + 'fu-offline.c', + 'fu-spawn.c', + systemd_src + ], + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies: [ + libfwupd_deps, + client_dep, + ], + link_with: [ + fwupd, + fwupdplugin, + fwupdutil, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: join_paths(libexecdir, 'fwupd') +) +endif + +resources_src = gnome.compile_resources( + 'fwupd-resources', + 'fwupd.gresource.xml', + source_dir: '.', + c_name: 'fu' +) + +# generate a header file that allows us to instantiate the plugins without copy-pasting or +# duplicating the meson build logic in the engine +plugin_names = [] +foreach lib : plugin_builtins + plugin_names += lib.full_path() +endforeach +plugins_hdr = custom_target('fwupd-generate-plugins-header', + output : 'fu-plugin-builtin.h', + command : [ + join_paths(meson.project_source_root(), 'contrib', 'generate-plugins-header.py'), + '@OUTPUT@', + meson.project_source_root(), + ','.join(plugin_names), + ], +) + +# build all the plugins and engine into one installed library +fwupdengine = library( + 'fwupdengine', + resources_src, + sources: fwupd_engine_src, + install: true, + install_dir: libdir_pkg, + include_directories: plugin_incdirs, + dependencies: [ + daemon_dep, + ], + link_whole: [ + plugin_builtins, + ], + link_with: [ + fwupd, + fwupdplugin, + ], +) + +fwupdtool = executable( + 'fwupdtool', + resources_src, + plugins_hdr, + export_dynamic: true, + sources: [ + 'fu-tool.c', + ], + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies: [ + libfwupd_deps, + libgcab, + libarchive, + client_dep, + valgrind, + ], + link_with: [ + fwupdengine, + fwupdutil, + plugin_libs, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: bindir +) + +if get_option('man') + if build_daemon + configure_file( + input: 'fwupdmgr.1', + output: 'fwupdmgr.1', + configuration: conf, + install: true, + install_dir: join_paths(mandir, 'man1'), + ) + configure_file( + input: 'fwupdagent.1', + output: 'fwupdagent.1', + configuration: conf, + install: true, + install_dir: join_paths(mandir, 'man1'), + ) + endif + if build_standalone + configure_file( + input: 'fwupdtool.1', + output: 'fwupdtool.1', + configuration: conf, + install: true, + install_dir: join_paths(mandir, 'man1'), + ) + endif +endif + +if build_daemon + +# the StartServiceCtrlDispatcherA design is so different use a different source file +if host_machine.system() == 'windows' + daemon_loader_src = 'fu-main-windows.c' +else + daemon_loader_src = 'fu-main.c' +endif + +executable( + 'fwupd', + resources_src, + plugins_hdr, + sources: [ + daemon_loader_src, + 'fu-daemon.c', + ], + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies: [ + valgrind, + libsystemd, + daemon_dep, + ], + link_with: [ + fwupdengine, + plugin_libs, + ], + c_args: [ + '-DFU_OFFLINE_DESTDIR=""', + ], + install: true, + install_rpath: libdir_pkg, + install_dir: daemon_dir +) + +endif + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + e = executable( + 'fu-self-test', + resources_src, + colorhug_test_firmware, + hwid_test_firmware, + multiple_rels_test_firmware, + noreqs_test_firmware, + plugins_hdr, + sources: [ + 'fu-spawn.c', + 'fu-self-test.c', + ], + include_directories: [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + dependencies: [ + daemon_dep, + ], + link_with: [ + fwupdengine, + fwupdutil, + plugin_libs, + ], + c_args: [ + ], + ) + test('fu-self-test', e, is_parallel: false, timeout: 180, env: env) +endif diff --git a/fwupd-1.8.6/src/org.freedesktop.fwupd.xml b/fwupd-1.8.6/src/org.freedesktop.fwupd.xml new file mode 100644 index 0000000000000000000000000000000000000000..7e58cceee75eb23093a20f7c0cb4528947fad9e3 --- /dev/null +++ b/fwupd-1.8.6/src/org.freedesktop.fwupd.xml @@ -0,0 +1,999 @@ + + + + + + + The interface used for querying firmware for the system. + + + + + + + + + + The daemon version. + + + + + + + + + + + The optional best known configuration to use when syncing back to a + known state, e.g. vendor-factory-2021q1. + + + + + + + + + + + The vendor name string for the host. + + + + + + + + + + + The product name string for the host. + + + + + + + + + + + The machine ID for the host. + + + + + + + + + + + The Host Security ID, for instance HSI:2 + + + + + + + + + + + If the daemon has been tainted with a 3rd party plugin. + + + + + + + + + + + If the daemon is running on an interactive terminal. + + + + + + + + + + + The daemon status, e.g. decompressing. + + + + + + + + + + + The job percentage completion, or 0 for unknown. + + + + + + + + + + + Returns the system battery level, or 101 for unknown. + + + + + + + + + + + Returns the system battery threshold under which a firmware update cannot be performed. + + + + + + + + + + + If the daemon requires trusted payloads. + + + + + + + + + + + Gets a list of all the devices that are supported. + + + + + + + An array of devices, with any properties set on each. + + + + + + + + + + + Gets a list of all the plugins being used by the daemon. + + + + + + + An array of plugins, with any properties set on each. + + + + + + + + + + + Gets a list of all the releases for a specific device. + + + + + + + + A device ID. + + + + + + + + + An array of releases (with the release number as the key), + with any properties set on each. + + + + + + + + + + + + Gets a list of all the downgrades possible for a specific device. + + + + + + + + A device ID. + + + + + + + + + An array of releases (with the release number as the key), + with any properties set on each. + + + + + + + + + + + + Gets a list of all the upgrades possible for a specific device. + + + + + + + + A device ID. + + + + + + + + + An array of releases (with the release number as the key), + with any properties set on each. + + + + + + + + + + + + Gets details about a local firmware file. + + + + + + + + An index into the array of file descriptors that may have + been sent with the DBus message. + + + + + + + + An array of results, with any properties set on each. + + + + + + + + + + + Gets a list of all the past firmware updates. + + + + + + + An array of devices, with any properties set on each. + + + + + + + + + + + Gets a list of all the Host Security ID attributes. + + + + + + + An array of HSI attributes, with any properties set on each. + + + + + + + + + + + Gets a list of all the Host Security ID events. + + + + + + + The maximum number of events, or 0 for no limit. + + + + + + + An array of HSI attributes, with any properties set on each. + + + + + + + + + + + Gets metadata to include with the firmware and security reports. + + + + + + + An array of string key values. + + + + + + + + + + + Sets optional hints from the client that may affect the list of devices. + A typical hint might be locale and unknown hints should be ignored. + + + + + + + An array of string key values. + + + + + + + + + + + Schedules a firmware to be installed. + + + + + + + + An ID, typically a GUID of the hardware to update, or the string + * to match any applicable hardware. + + + + + + + + An index into the array of file descriptors that may have + been sent with the DBus message. + + + + + + + + + Options to be used when constructing the profile, e.g. + offline=True. + + + + + + + + + + + + Verifies firmware on a device by reading it back and performing + a cryptographic hash, typically SHA1. + + + + + + + + An ID, typically a GUID of the hardware. + + + + + + + + + + + Updates the cryptographic hash stored for a device. + + + + + + + + An ID, typically a GUID of the hardware. + + + + + + + + + + + Unlock the device to allow firmware access. + + + + + + + + An ID, typically a GUID of the hardware. + + + + + + + + + + + Activate a firmware update on the device. + + + + + + + + An ID, typically the sha hash of the device string. + + + + + + + + + + + Gets the results of an offline update. + + + + + + + + An ID, typically a GUID of the hardware that was updated, or the + string * to match any hardware. + + + + + + + Results about the update, e.g. success=True + + + + + + + + + + + Gets the list of remotes. + + + + + + + The array remotes, with properties + + + + + + + + + + + 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 + + + + + + + + + + + Gets the list of blocked firmware. + + + + + + + The checksums of the archives + + + + + + + + + + + Sets the list of blocked firmware that can be applied to devices. + + + + + + + The checksums of the archives + + + + + + + + + + + Sets the features the client supports. This allows firmware to depend on + specific front-end features, for instance showing the user an image on + how to detach the hardware. + + + + + + + The features the front end supports + + + + + + + + + + + Clears the results of an offline update. + + + + + + + + An ID, typically a GUID of the hardware that was updated, or the + string * to match any hardware. + + + + + + + + + + + Modifies a remote in some way. + + + + + + + + A device ID, or the string * to match any hardware. + + + + + + + + + The key, e.g. 'Flags'. + + + + + + + + + The value of the correct type, e.g. a URL. + + + + + + + + + + + + Modify persistent configuration for daemon + + + + + + + + The key, e.g. 'DisabledPlugins'. + + + + + + + + + The value of the correct type, e.g. a URL. + + + + + + + + + + + + Adds AppStream resource information from a session client. + + + + + + + + Remote ID to tag the metadata objects with, e.g. 'lvfs-testing'. + + + + + + + + + File handle to AppStream metadata. + + + + + + + + + File handle to AppStream metadata GPG signature. + + + + + + + + + + + + Modifies a remote in some way. + + + + + + + + Remote ID, e.g. 'lvfs-testing'. + + + + + + + + + The key, e.g. 'Enabled'. + + + + + + + + + The value of the correct type, e.g. a URL. + + + + + + + + + + + + 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. + + + + + + + + + + + + Modify BIOS setting + + + + + + + An array of BIOS settings and their new values. + + + + + + + + + + Gets a list of all the BIOS settings. + + + + + + + An array of BIOS settings, with any properties set on each. + + + + + + + + + + + Ask the daemon to quit. This can only be called by the root user. + + + + + + + + + + + Some value on the interface or the number of devices or + profiles has changed. + + + + + + + + + + + A device structure. + + + + + + + A device has been added. + + + + + + + + + + + A device structure. + + + + + + + A device has been removed. + + + + + + + + + + + A device structure. + + + + + + + A device has been changed. + + + + + + + + + + + A device request. + + + + + + + A device request to the client. + + + + + + + diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/current_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/current_value new file mode 100644 index 0000000000000000000000000000000000000000..cd16b9f9351a8969e3ac1b71fb53f71dc33f13c8 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/current_value @@ -0,0 +1 @@ +Disabled \ No newline at end of file diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/default_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/default_value new file mode 100644 index 0000000000000000000000000000000000000000..caadf98ce6b5f8ce620b13244b25014c22d99cd7 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/default_value @@ -0,0 +1 @@ +Enabled diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/dell_modifier b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/dell_value_modifier b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/dell_value_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/dell_value_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/display_name b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d056b8d91fe3c0236fab652171d965f610593c66 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/display_name @@ -0,0 +1 @@ +Absolute diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/display_name_language_code b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/possible_values b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/possible_values new file mode 100644 index 0000000000000000000000000000000000000000..f3652ad6d51b63366cb20a5572b10d2811bbfca5 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/possible_values @@ -0,0 +1 @@ +Enabled;Disabled;PermanentlyDisabled; diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/type b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/type new file mode 100644 index 0000000000000000000000000000000000000000..a60dc9c56ec734bb6e6f9a8d005f15c4d4952d0d --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Absolute/type @@ -0,0 +1 @@ +enumeration diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/current_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/current_value new file mode 100644 index 0000000000000000000000000000000000000000..56a6051ca2b02b04ef92d5150c9ef600403cb1de --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/current_value @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/default_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/default_value new file mode 100644 index 0000000000000000000000000000000000000000..33a43d62efbe522b731232ee0e2307c1ff2b2c93 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/default_value @@ -0,0 +1 @@ +Asset Tag diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/dell_modifier b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/display_name b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/display_name new file mode 100644 index 0000000000000000000000000000000000000000..33a43d62efbe522b731232ee0e2307c1ff2b2c93 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/display_name @@ -0,0 +1 @@ +Asset Tag diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/display_name_language_code b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/max_length b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/max_length new file mode 100644 index 0000000000000000000000000000000000000000..900731ffd51ffc82db488b6554f719de735f12bd --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/max_length @@ -0,0 +1 @@ +64 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/min_length b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/min_length new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/min_length @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/type b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/type new file mode 100644 index 0000000000000000000000000000000000000000..ee8a39c38d2d35ace182524fcc6329b900ef8ce1 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/Asset/type @@ -0,0 +1 @@ +string diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/current_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/current_value new file mode 100644 index 0000000000000000000000000000000000000000..2bbd69c2e548374bb9a87e06a2cfe52136f28465 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/current_value @@ -0,0 +1 @@ +70 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/default_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/default_value new file mode 100644 index 0000000000000000000000000000000000000000..d61f00d8cad3920809f4d992ac3031b3f32e7f10 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/default_value @@ -0,0 +1 @@ +90 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/dell_modifier b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/dell_modifier new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/dell_modifier @@ -0,0 +1 @@ + diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/display_name b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/display_name new file mode 100644 index 0000000000000000000000000000000000000000..d18eaaba1c177a8de38acecbc7944604c4469a40 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/display_name @@ -0,0 +1 @@ +Custom Charge Stop diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/display_name_language_code b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/display_name_language_code new file mode 100644 index 0000000000000000000000000000000000000000..beb9970be0573da85819043f0ebf680ec6937da7 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/display_name_language_code @@ -0,0 +1 @@ +en-US diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/max_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/max_value new file mode 100644 index 0000000000000000000000000000000000000000..29d6383b52c1352e92a45875b5bb206f89139643 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/max_value @@ -0,0 +1 @@ +100 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/min_value b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/min_value new file mode 100644 index 0000000000000000000000000000000000000000..c3f407c0955bb5738e40a82664c79b63f04a9adb --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/min_value @@ -0,0 +1 @@ +55 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/scalar_increment b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/scalar_increment new file mode 100644 index 0000000000000000000000000000000000000000..d00491fd7e5bb6fa28c517a0bb32b8b506539d4d --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/scalar_increment @@ -0,0 +1 @@ +1 diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/type b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/type new file mode 100644 index 0000000000000000000000000000000000000000..82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/CustomChargeStop/type @@ -0,0 +1 @@ +integer diff --git a/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/pending_reboot b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/pending_reboot new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/fwupd-1.8.6/src/tests/bios-attrs/fwupd-internal/attributes/pending_reboot @@ -0,0 +1 @@ +0 diff --git a/fwupd-1.8.6/src/tests/colorhug/colorhug-als-3.0.2.cab b/fwupd-1.8.6/src/tests/colorhug/colorhug-als-3.0.2.cab new file mode 100644 index 0000000000000000000000000000000000000000..16cb57801e13ce73656b868eb66541a2971202fc Binary files /dev/null and b/fwupd-1.8.6/src/tests/colorhug/colorhug-als-3.0.2.cab differ diff --git a/fwupd-1.8.6/src/tests/colorhug/firmware.bin b/fwupd-1.8.6/src/tests/colorhug/firmware.bin new file mode 120000 index 0000000000000000000000000000000000000000..0459ba0cc2811b638d964b06311f86888a12e92e --- /dev/null +++ b/fwupd-1.8.6/src/tests/colorhug/firmware.bin @@ -0,0 +1 @@ +../../../libfwupdplugin/tests/colorhug/firmware.bin \ No newline at end of file diff --git a/fwupd-1.8.6/src/tests/daemon.conf b/fwupd-1.8.6/src/tests/daemon.conf new file mode 100644 index 0000000000000000000000000000000000000000..154eacbc78df53c0487b8ca98adcb599ee42ab73 --- /dev/null +++ b/fwupd-1.8.6/src/tests/daemon.conf @@ -0,0 +1,2 @@ +[fwupd] +# nothing to see here diff --git a/fwupd-1.8.6/src/tests/history_v1.db b/fwupd-1.8.6/src/tests/history_v1.db new file mode 100644 index 0000000000000000000000000000000000000000..a1bfcb84c90e26310b3aec286fa6875e32d493bf Binary files /dev/null and b/fwupd-1.8.6/src/tests/history_v1.db differ diff --git a/fwupd-1.8.6/src/tests/host-emulate/meson.build b/fwupd-1.8.6/src/tests/host-emulate/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..4bc02e46edc615c604bab6a6e53805d582362785 --- /dev/null +++ b/fwupd-1.8.6/src/tests/host-emulate/meson.build @@ -0,0 +1,15 @@ +if build_standalone + gzip = find_program('gzip') + foreach input_file : [ + 'thinkpad-p1-iommu.json', + ] + custom_target(input_file, + input: input_file, + output: '@0@.gz'.format(input_file), + capture: true, + command: [gzip, '-k', '--stdout', '@INPUT@'], + install: true, + install_dir: join_paths(datadir, 'fwupd', 'host-emulate.d'), + ) + endforeach +endif diff --git a/fwupd-1.8.6/src/tests/host-emulate/thinkpad-p1-iommu.json b/fwupd-1.8.6/src/tests/host-emulate/thinkpad-p1-iommu.json new file mode 100644 index 0000000000000000000000000000000000000000..bc96832433430d6df613a8560cb260f464705685 --- /dev/null +++ b/fwupd-1.8.6/src/tests/host-emulate/thinkpad-p1-iommu.json @@ -0,0 +1,714 @@ +{ + "SecurityAttributes": [ + { + "AppstreamId": "org.fwupd.hsi.Kernel.Tainted", + "HsiResult": "not-tainted", + "Plugin": "linux_tainted", + "Flags": [ + "success", + "runtime-issue" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Kernel.Lockdown", + "HsiResult": "enabled", + "Plugin": "linux_lockdown", + "Flags": [ + "success", + "runtime-issue" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Kernel.Swap", + "HsiResult": "encrypted", + "Plugin": "linux_swap", + "Flags": [ + "success", + "runtime-issue" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Uefi.SecureBoot", + "HsiResult": "enabled", + "Plugin": "uefi_capsule", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Fwupd.Plugins", + "HsiResult": "not-tainted", + "Plugin": "core", + "Flags": [ + "success", + "runtime-issue" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Mei.ManufacturingMode", + "HsiResult": "locked", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Mei.OverrideStrap", + "HsiResult": "locked", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Mei.Version", + "HsiResult": "valid", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.PlatformDebugEnabled", + "HsiResult": "not-enabled", + "Plugin": "msr", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Spi.SmmBwp", + "HsiResult": "locked", + "Plugin": "pci_bcr", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Spi.Ble", + "HsiResult": "enabled", + "Plugin": "pci_bcr", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Spi.Bioswe", + "HsiResult": "not-enabled", + "Plugin": "pci_bcr", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.SupportedCpu", + "HsiResult": "valid", + "Plugin": "cpu", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Tpm.EmptyPcr", + "HsiResult": "valid", + "Plugin": "tpm", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Tpm.Version20", + "HsiResult": "found", + "Plugin": "tpm", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Uefi.Pk", + "HsiResult": "valid", + "Plugin": "uefi_pk", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelBootguard.Enabled", + "HsiResult": "enabled", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelBootguard.Acm", + "HsiResult": "valid", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelBootguard.Otp", + "HsiResult": "valid", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelBootguard.Verified", + "HsiResult": "valid", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.PlatformDebugLocked", + "HsiResult": "locked", + "Plugin": "msr", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Tpm.ReconstructionPcr0", + "HsiResult": "valid", + "Plugin": "tpm", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.Iommu", + "HsiResult": "found", + "Plugin": "iommu", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelBootguard.Policy", + "HsiResult": "valid", + "Plugin": "pci_mei", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelCet.Enabled", + "HsiResult": "not-supported", + "Plugin": "cpu" + }, + { + "AppstreamId": "org.fwupd.hsi.PrebootDma", + "HsiResult": "not-enabled", + "Plugin": "acpi_dmar", + "Flags": [ + "action-contact-oem", + "action-config-fw" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.SuspendToIdle", + "HsiResult": "not-enabled", + "Plugin": "acpi_facp", + "Flags": [ + "action-config-fw", + "action-config-os" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.SuspendToRam", + "HsiResult": "enabled", + "Plugin": "linux_sleep", + "Flags": [ + "action-config-fw", + "action-config-os" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.IntelSmap", + "HsiResult": "enabled", + "Plugin": "cpu", + "Flags": [ + "success" + ] + }, + { + "AppstreamId": "org.fwupd.hsi.EncryptedRam", + "HsiResult": "not-supported", + "Plugin": "cpu" + } + ], + "Devices": [ + { + "DeviceId": "4a4907dbb1b96c6a1177dfd1b95eb41c781d1265", + "InstanceIds": [ + "GPIO\\ID_INT3450:00" + ], + "Guid": [ + "1199a818-4c52-5137-b536-d59e2e2cada9" + ], + "Flags": [ + "registered" + ] + }, + { + "Name": "AMT [unprovisioned]", + "DeviceId": "5fed1486be004d67ea79838d2e83aaa11bb72645", + "ParentDeviceId": "a45df35ac0e948ee180fe216a5f703f32dda163f", + "CompositeId": "a45df35ac0e948ee180fe216a5f703f32dda163f", + "InstanceIds": [ + "MEI\\VEN_8086&DEV_06E0", + "MEI\\VEN_8086&DEV_06E0&REV_00", + "MEI\\VEN_8086&DEV_06E0&SUBSYS_17AA22C2", + "MEI\\VEN_8086&DEV_06E0&SUBSYS_17AA22C2&REV_00" + ], + "Guid": [ + "2800f812-b7b4-2d4b-aca8-46e0ff65814c", + "15c7ef4d-12fc-5e25-aba6-49b30f5ab130", + "a65d125e-0c76-5876-bad3-6956a2f25e5e", + "a8f5ca2d-e46c-5c9b-819b-d64c73c9e48d", + "45bcbef5-0630-5121-a4d9-881054b2916f" + ], + "Summary": + "Hardware and firmware technology for remote out-of-band management", + "Flags": [ + "internal", + "registered" + ], + "Vendor": "Intel Corporation", + "VendorId": "MEI:0x8086", + "Version": "14.1.53.1649", + "VersionBootloader": "14.1.53.1649", + "VersionFormat": "intel-me", + "Icons": [ + "computer" + ] + }, + { + "Name": "CometLake-H GT2 [UHD Graphics]", + "DeviceId": "5792b48846ce271fab11c4a545f7a3df0d36e00a", + "InstanceIds": [ + "PCI\\VEN_8086&DEV_9BC4", + "PCI\\VEN_8086&DEV_9BC4&REV_05", + "PCI\\VEN_8086&DEV_9BC4&SUBSYS_17AA22C2", + "PCI\\VEN_8086&DEV_9BC4&SUBSYS_17AA22C2&REV_05", + "PCI\\VEN_8086&DEV_9BC4&REV_00", + "PCI\\VEN_8086&DEV_9BC4&SUBSYS_17AA22C2&REV_00" + ], + "Guid": [ + "3777783a-3f83-56a5-95f4-533eb6a2bd19", + "6c3dbf6c-4e6f-5309-9954-c5ab7aca617e", + "5fde5d20-db24-5f21-afdd-247c1bf1efa1", + "07168636-0f3b-565c-8fe1-0f0a77d82cd8", + "7ffe1cb7-395a-52a9-a172-70ec6caaf310", + "b813dc18-ddf2-508d-a7eb-0e2fc8752b03" + ], + "Flags": [ + "internal", + "registered", + "can-verify", + "can-verify-image" + ], + "Vendor": "Intel Corporation", + "VendorId": "PCI:0x8086", + "Version": "05", + "VersionFormat": "plain" + }, + { + "Name": "Core\u2122 i7-10850H CPU @ 2.70GHz", + "DeviceId": "4bde70ba4e39b28f9eab1628f9dd6e6244c03027", + "InstanceIds": [ + "cpu", + "CPUID\\PRO_0&FAM_06", + "CPUID\\PRO_0&FAM_06&MOD_A5", + "CPUID\\PRO_0&FAM_06&MOD_A5&STP_2" + ], + "Guid": [ + "b9a2dd81-159e-5537-a7db-e7101d164d3f", + "30249f37-d140-5d3e-9319-186b1bd5cac3", + "a45b0522-5722-54bd-b802-86cd044262df", + "7b9b6e8c-226c-5db6-86cb-ea3187578013" + ], + "Flags": [ + "internal", + "registered" + ], + "Vendor": "Intel", + "Version": "0x000000f0", + "VersionFormat": "hex", + "VersionRaw": 240, + "Icons": [ + "computer" + ] + }, + { + "Name": "Embedded Controller", + "DeviceId": "2292ae5236790b47884e37cf162dcf23bfcd1c60", + "Guid": [ + "b616d3d6-cca9-40bd-964e-b86ffb62744d" + ], + "Summary": "UEFI ESRT device", + "Protocol": "org.uefi.capsule", + "Flags": [ + "internal", + "updatable", + "require-ac", + "supported", + "registered", + "needs-reboot", + "usable-during-update" + ], + "Vendor": "Lenovo", + "VendorId": "DMI:LENOVO", + "Version": "0.1.11", + "VersionLowest": "0.1.11", + "VersionFormat": "triplet", + "VersionRaw": 65547, + "VersionLowestRaw": 65547, + "UpdateState": 2 + }, + { + "Name": "Integrated Camera", + "DeviceId": "0fef0a0c55f6442bffaebd774ae771341c89571b", + "InstanceIds": [ + "USB\\VID_13D3&PID_5405", + "USB\\VID_13D3&PID_5405&REV_6004" + ], + "Guid": [ + "9284c551-0b4c-51ee-905a-168b8787290c", + "6f7c4f56-0085-5aa6-b443-823852a1cdbc" + ], + "Serial": "0000", + "Protocol": "org.usb.dfu", + "Flags": [ + "updatable", + "registered", + "add-counterpart-guids" + ], + "Vendor": "Azurewave", + "VendorId": "USB:0x13D3", + "Version": "60.4", + "VersionFormat": "bcd", + "Icons": [ + "camera-web" + ] + }, + { + "Name": "Intel Management Engine", + "DeviceId": "349bb341230b1a86e5effe7dfe4337e1590227bd", + "Guid": [ + "5695cc48-4f4f-4677-8ffb-9f496d3ad9d3" + ], + "Summary": "UEFI ESRT device", + "Protocol": "org.uefi.capsule", + "Flags": [ + "internal", + "updatable", + "require-ac", + "supported", + "registered", + "needs-reboot", + "usable-during-update" + ], + "Vendor": "Lenovo", + "VendorId": "DMI:LENOVO", + "Version": "225.53.1649", + "VersionLowest": "0.0.1", + "VersionFormat": "triplet", + "VersionRaw": 3778348657, + "VersionLowestRaw": 1, + "UpdateState": 2 + }, + { + "Name": "System Firmware", + "DeviceId": "a45df35ac0e948ee180fe216a5f703f32dda163f", + "InstanceIds": [ + "main-system-firmware" + ], + "Guid": [ + "6e58e73d-8061-44e4-8949-33b7f0d5c726", + "230c8b18-8d9b-53ec-838b-6cfc0383493a" + ], + "Summary": "UEFI ESRT device", + "Protocol": "org.uefi.capsule", + "Flags": [ + "internal", + "updatable", + "require-ac", + "supported", + "registered", + "needs-reboot", + "can-verify", + "usable-during-update" + ], + "Checksums": [ + "73319eae91b5838c5a587c54ffb625b58746238b", + "6d244e0fadc7cc866e902517b4fe24505e52d1023934d487b039641c726abc46" + ], + "Vendor": "Lenovo", + "VendorId": "DMI:LENOVO", + "Version": "0.1.23", + "VersionLowest": "0.1.11", + "VersionFormat": "triplet", + "VersionRaw": 65559, + "VersionLowestRaw": 65547, + "Icons": [ + "computer" + ], + "UpdateState": 2 + }, + { + "Name": "THNSN5512GPU7 TOSHIBA", + "DeviceId": "03281da317dccd2b18de2bd1cc70a782df40ed7e", + "InstanceIds": [ + "NVME\\VEN_1179&DEV_010F", + "NVME\\VEN_1179&DEV_010F&REV_01", + "NVME\\VEN_1179&DEV_010F&SUBSYS_11790001", + "NVME\\VEN_1179&DEV_010F&SUBSYS_11790001&REV_01", + "THNSN5512GPU7 TOSHIBA" + ], + "Guid": [ + "83991323-9951-5adf-b743-d93e882a41e1", + "e22c4520-43dc-5bb3-8245-5787fead9b63", + "87178ed9-f82b-5895-bcb0-09713abc842c", + "2060b01b-f6aa-5fea-9acd-804de1765920", + "e1409b09-50cf-5aef-8ad8-760b9022f88d" + ], + "Serial": "37RS11EATAHT", + "Summary": "NVM Express solid state drive", + "Protocol": "org.nvmexpress", + "Flags": [ + "internal", + "updatable", + "require-ac", + "registered", + "needs-reboot", + "usable-during-update", + "signed-payload" + ], + "Vendor": "Toshiba Corporation", + "VendorId": "NVME:0x1179", + "Version": "410557LA", + "VersionFormat": "plain", + "Icons": [ + "drive-harddisk" + ] + }, + { + "Name": "TPM", + "DeviceId": "c6a80ac3a22083423992a3cb15018989f37834d6", + "InstanceIds": [ + "system-tpm", + "TPM\\VEN_STM&DEV_0001", + "TPM\\VEN_STM&MOD_", + "TPM\\VEN_STM&DEV_0001&VER_2.0", + "TPM\\VEN_STM&MOD_&VER_2.0" + ], + "Guid": [ + "ff71992e-52f7-5eea-94ef-883e56e034c6", + "84df3581-f896-54d2-bd1a-372602f04c32", + "bfaed10a-bbc1-525b-a329-35da2f63e918", + "70b7b833-7e1a-550a-a291-b94a12d0f319", + "06f005e9-cb62-5d1a-82d9-13c534c53c48" + ], + "Flags": [ + "internal", + "registered" + ], + "Vendor": "ST Microelectronics", + "VendorId": "TPM:STM", + "Version": "1.258.0.0", + "VersionFormat": "quad", + "VersionRaw": 282583078273024, + "Icons": [ + "computer" + ] + }, + { + "Name": "Thunderbolt host controller", + "DeviceId": "2cff15412fb2877637de2c23a10f841bca114e03", + "InstanceIds": [ + "THUNDERBOLT\\VEN_0109&DEV_1913", + "THUNDERBOLT\\VEN_0109&DEV_1913&REV_00", + "TBT-01091913-native", + "TBT-01091913-native-controller0-0" + ], + "Guid": [ + "b510dc43-dc5d-5449-9ccc-5edd80338954", + "b595a681-7c5b-5842-bba7-1d448c261a6e", + "10216d57-c796-5f3c-83d3-21baf70bfc54", + "f8543f13-e164-5332-8681-4d5ef3ffbff0" + ], + "Summary": "Unmatched performance for high-speed I/O", + "Protocol": "com.intel.thunderbolt", + "Flags": [ + "internal", + "updatable", + "require-ac", + "registered", + "dual-image", + "signed-payload" + ], + "Vendor": "Lenovo", + "VendorId": "THUNDERBOLT:0x0109|TBT:0x0109", + "VendorIds": [ + "THUNDERBOLT:0x0109", + "TBT:0x0109" + ], + "Version": "62.00", + "VersionFormat": "pair", + "Icons": [ + "thunderbolt" + ] + }, + { + "Name": "UEFI Device Firmware", + "DeviceId": "f95c9218acd12697af946874bfe4239587209232", + "Guid": [ + "439d54f4-5548-4698-a8b0-46a047c0e66e" + ], + "Summary": "UEFI ESRT device", + "Protocol": "org.uefi.capsule", + "Flags": [ + "internal", + "updatable", + "require-ac", + "registered", + "needs-reboot", + "usable-during-update" + ], + "VendorId": "DMI:LENOVO", + "Version": "16842759", + "VersionLowest": "1", + "VersionFormat": "number", + "VersionRaw": 16842759, + "VersionLowestRaw": 1, + "UpdateState": 2 + }, + { + "Name": "UEFI Device Firmware", + "DeviceId": "d96de5c124b60ed6241ebcb6bb2c839cb5580786", + "Guid": [ + "3fb9a55d-d7f1-4d1b-b216-74e328e28f51" + ], + "Summary": "UEFI ESRT device", + "Protocol": "org.uefi.capsule", + "Flags": [ + "internal", + "updatable", + "require-ac", + "registered", + "needs-reboot", + "usable-during-update" + ], + "VendorId": "DMI:LENOVO", + "Version": "65794", + "VersionLowest": "65794", + "VersionFormat": "number", + "VersionRaw": 65794, + "VersionLowestRaw": 65794, + "UpdateState": 2 + }, + { + "Name": "UEFI Device Firmware", + "DeviceId": "f37fb01122dd62c773f4e84ec89737e059712d59", + "Guid": [ + "33967546-da89-4c51-9c95-5242bcb854e8" + ], + "Summary": "UEFI ESRT device", + "Protocol": "org.uefi.capsule", + "Flags": [ + "internal", + "updatable", + "require-ac", + "registered", + "needs-reboot", + "usable-during-update" + ], + "VendorId": "DMI:LENOVO", + "Version": "24580", + "VersionLowest": "1", + "VersionFormat": "number", + "VersionRaw": 24580, + "VersionLowestRaw": 1, + "UpdateState": 2 + }, + { + "Name": "UEFI dbx", + "DeviceId": "362301da643102b9f38477387e2193e57abaa590", + "ParentDeviceId": "a45df35ac0e948ee180fe216a5f703f32dda163f", + "CompositeId": "a45df35ac0e948ee180fe216a5f703f32dda163f", + "InstanceIds": [ + "UEFI\\CRT_A9087D1044AD18F7A94916D284CBC01827CF23CD8F60B79072C9CAA1FEF4D649", + "UEFI\\CRT_A9087D1044AD18F7A94916D284CBC01827CF23CD8F60B79072C9CAA1FEF4D649&ARCH_X64", + "UEFI\\CRT_A1117F516A32CEFCBA3F2D1ACE10A87972FD6BBE8FE0D0B996E09E65D802A503", + "UEFI\\CRT_A1117F516A32CEFCBA3F2D1ACE10A87972FD6BBE8FE0D0B996E09E65D802A503&ARCH_X64" + ], + "Guid": [ + "14503b3d-73ce-5d06-8137-77c68972a341", + "5971a208-da00-5fce-b5f5-1234342f9cf7", + "c6682ade-b5ec-57c4-b687-676351208742", + "f8ba2887-9411-5c36-9cee-88995bb39731" + ], + "Summary": "UEFI revocation database", + "Protocol": "org.uefi.dbx", + "Flags": [ + "internal", + "updatable", + "registered", + "needs-reboot", + "only-version-upgrade", + "signed-payload" + ], + "VendorId": "UEFI:Linux Foundation", + "Version": "238", + "VersionLowest": "238", + "VersionFormat": "number", + "Icons": [ + "computer" + ], + "InstallDuration": 1 + }, + { + "Name": "WDC PC SN720 SDAQNTW-256G-1001", + "DeviceId": "08e1798bf5d9cb56a0290b552cab6c1a371b5089", + "InstanceIds": [ + "NVME\\VEN_15B7&DEV_5002", + "NVME\\VEN_15B7&DEV_5002&REV_00", + "NVME\\VEN_15B7&DEV_5002&SUBSYS_15B75002", + "NVME\\VEN_15B7&DEV_5002&SUBSYS_15B75002&REV_00", + "WDC PC SN720 SDAQNTW-256G-1001" + ], + "Guid": [ + "ff2112dc-038c-596d-90ca-d43c5077c6ec", + "137520ce-3603-53e6-9165-56694ed744e7", + "c528df4b-7972-5880-8cb1-330415e2dc6a", + "06a6f1f7-4ce0-57ef-8154-0705d936e4a6", + "237776ee-0bcd-5fe9-8dc8-6984a2d36ba0" + ], + "Serial": "183985804591", + "Summary": "NVM Express solid state drive", + "Protocol": "org.nvmexpress", + "Flags": [ + "internal", + "updatable", + "require-ac", + "supported", + "registered", + "needs-reboot", + "usable-during-update" + ], + "Vendor": "Sandisk Corp", + "VendorId": "NVME:0x15B7", + "Version": "10190101", + "VersionFormat": "plain", + "Icons": [ + "drive-harddisk" + ] + } + ] +} diff --git a/fwupd-1.8.6/src/tests/meson.build b/fwupd-1.8.6/src/tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..07c16bf92f536c50638997c5d650f5e088da98b4 --- /dev/null +++ b/fwupd-1.8.6/src/tests/meson.build @@ -0,0 +1,3 @@ +subdir('missing-hwid') +subdir('multiple-rels') +subdir('host-emulate') diff --git a/fwupd-1.8.6/src/tests/metadata.xml b/fwupd-1.8.6/src/tests/metadata.xml new file mode 100644 index 0000000000000000000000000000000000000000..449eb6727dfe9e8f14292c4d2ce44b8b49f54308 --- /dev/null +++ b/fwupd-1.8.6/src/tests/metadata.xml @@ -0,0 +1,14 @@ + + + + org.fwupd.8330a096d9f1af8567c7374cb8403e1ce9cf3163.device + + 2d47f29b-83a2-4f31-a2e8-63474f4d4c2e + + + +

    Applying will enable UEFI firmware reporting

    +
    +
    +
    +
    diff --git a/fwupd-1.8.6/src/tests/missing-hwid/firmware.bin b/fwupd-1.8.6/src/tests/missing-hwid/firmware.bin new file mode 100644 index 0000000000000000000000000000000000000000..c3f129f25766c71fb82a420de7945d78d4460add Binary files /dev/null and b/fwupd-1.8.6/src/tests/missing-hwid/firmware.bin differ diff --git a/fwupd-1.8.6/src/tests/missing-hwid/firmware.metainfo.xml b/fwupd-1.8.6/src/tests/missing-hwid/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..d27a15788e1f6835d632add1dbd05eb9623a9632 --- /dev/null +++ b/fwupd-1.8.6/src/tests/missing-hwid/firmware.metainfo.xml @@ -0,0 +1,14 @@ + + + + com.hughski.test.firmware + + 12345678-1234-1234-1234-123456789012 + + + 9342d47a-1bab-5709-9869-c840b2eac501 + + + + + diff --git a/fwupd-1.8.6/src/tests/missing-hwid/firmware2.metainfo.xml b/fwupd-1.8.6/src/tests/missing-hwid/firmware2.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..589c9c1d388a44ca874eb25e9e453690bc92d555 --- /dev/null +++ b/fwupd-1.8.6/src/tests/missing-hwid/firmware2.metainfo.xml @@ -0,0 +1,11 @@ + + + + com.hughski.test.firmware + + 12345678-1234-1234-1234-123456789012 + + + + + diff --git a/fwupd-1.8.6/src/tests/missing-hwid/meson.build b/fwupd-1.8.6/src/tests/missing-hwid/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..cee0f7d2ed9ff65ffcdcf8bcfa225de526b03d4b --- /dev/null +++ b/fwupd-1.8.6/src/tests/missing-hwid/meson.build @@ -0,0 +1,20 @@ +hwid_test_firmware = custom_target('hwid-test-firmware', + input: [ + 'firmware.bin', + 'firmware.metainfo.xml', + ], + output: 'hwid-1.2.3.cab', + command: [ + gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', + ], +) +noreqs_test_firmware = custom_target('noreqs-test-firmware', + input: [ + 'firmware.bin', + 'firmware2.metainfo.xml', + ], + output: 'noreqs-1.2.3.cab', + command: [ + gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', + ], +) diff --git a/fwupd-1.8.6/src/tests/multiple-rels/firmware-123.bin b/fwupd-1.8.6/src/tests/multiple-rels/firmware-123.bin new file mode 100644 index 0000000000000000000000000000000000000000..885b0f234903bada1fe2c9e1ba5f21c18207c9f7 --- /dev/null +++ b/fwupd-1.8.6/src/tests/multiple-rels/firmware-123.bin @@ -0,0 +1 @@ +0x1020003 diff --git a/fwupd-1.8.6/src/tests/multiple-rels/firmware-124.bin b/fwupd-1.8.6/src/tests/multiple-rels/firmware-124.bin new file mode 100644 index 0000000000000000000000000000000000000000..f1b2c68db4d7df957abefa64862c4a09101e6ebc --- /dev/null +++ b/fwupd-1.8.6/src/tests/multiple-rels/firmware-124.bin @@ -0,0 +1 @@ +0x1020004 diff --git a/fwupd-1.8.6/src/tests/multiple-rels/firmware.metainfo.xml b/fwupd-1.8.6/src/tests/multiple-rels/firmware.metainfo.xml new file mode 100644 index 0000000000000000000000000000000000000000..80f80b0331c4751a2888e739b98daf8cf1473daa --- /dev/null +++ b/fwupd-1.8.6/src/tests/multiple-rels/firmware.metainfo.xml @@ -0,0 +1,16 @@ + + + + com.hughski.test.firmware + + 12345678-1234-1234-1234-123456789012 + + + + + + + + + + diff --git a/fwupd-1.8.6/src/tests/multiple-rels/meson.build b/fwupd-1.8.6/src/tests/multiple-rels/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..234d76b196a921c5b6a99005e0d0d614c7d7be9c --- /dev/null +++ b/fwupd-1.8.6/src/tests/multiple-rels/meson.build @@ -0,0 +1,11 @@ +multiple_rels_test_firmware = custom_target('multiple-rels-test-firmware', + input: [ + 'firmware-123.bin', + 'firmware-124.bin', + 'firmware.metainfo.xml', + ], + output: 'multiple-rels-1.2.4.cab', + command: [ + gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', + ], +) diff --git a/fwupd-1.8.6/src/tests/os-release b/fwupd-1.8.6/src/tests/os-release new file mode 100644 index 0000000000000000000000000000000000000000..a156658c9d1a684a076426605c29c1be254ea6d0 --- /dev/null +++ b/fwupd-1.8.6/src/tests/os-release @@ -0,0 +1,6 @@ +ID_LIKE=chromiumos +GOOGLE_CRASH_ID=ChromeOS +NAME=Chrome OS +ID=chromeos +HOME_URL=https://www.chromium.org/chromium-os +BUG_REPORT_URL=https://crbug.com/new diff --git a/fwupd-1.8.6/src/tests/remotes.d/broken.conf b/fwupd-1.8.6/src/tests/remotes.d/broken.conf new file mode 100644 index 0000000000000000000000000000000000000000..727169aacc324bdbaadbbed8b8d12af1c16794e2 --- /dev/null +++ b/fwupd-1.8.6/src/tests/remotes.d/broken.conf @@ -0,0 +1,3 @@ +[fwupd Remote] +Enabled=true +MetadataURI=file:///tmp/fwupd-self-test/broken.xml.gz diff --git a/fwupd-1.8.6/src/tests/remotes.d/directory.conf b/fwupd-1.8.6/src/tests/remotes.d/directory.conf new file mode 100644 index 0000000000000000000000000000000000000000..15feedb51e388eb8ee2e86f94316abe2aa6bf1f3 --- /dev/null +++ b/fwupd-1.8.6/src/tests/remotes.d/directory.conf @@ -0,0 +1,4 @@ +[fwupd Remote] +Enabled=true +Keyring=none +MetadataURI=file:///tmp/fwupd-self-test/var/cache/fwupd diff --git a/fwupd-1.8.6/src/tests/remotes.d/stable.conf b/fwupd-1.8.6/src/tests/remotes.d/stable.conf new file mode 100644 index 0000000000000000000000000000000000000000..df68eb4aca68253b88263933540c7b4aaed4199c --- /dev/null +++ b/fwupd-1.8.6/src/tests/remotes.d/stable.conf @@ -0,0 +1,3 @@ +[fwupd Remote] +Enabled=true +MetadataURI=file:///tmp/fwupd-self-test/stable.xml diff --git a/fwupd-1.8.6/src/tests/remotes.d/testing.conf b/fwupd-1.8.6/src/tests/remotes.d/testing.conf new file mode 100644 index 0000000000000000000000000000000000000000..98eab68c732e0c41ca01c8a19f396b0cb8b1adc3 --- /dev/null +++ b/fwupd-1.8.6/src/tests/remotes.d/testing.conf @@ -0,0 +1,4 @@ +[fwupd Remote] +Enabled=true +MetadataURI=file:///tmp/fwupd-self-test/testing.xml +ApprovalRequired=true diff --git a/fwupd-1.8.6/src/tests/spawn.sh b/fwupd-1.8.6/src/tests/spawn.sh new file mode 100755 index 0000000000000000000000000000000000000000..11da2fd209150f330920e298610437985e7fe49a --- /dev/null +++ b/fwupd-1.8.6/src/tests/spawn.sh @@ -0,0 +1,10 @@ +#!/bin/sh +echo "this is a test" +sleep 1 +echo "this is another line1" +echo "this is another line2" +echo "this is another line3" +echo "this is another line4" +sleep 1 +echo "done!" +exit 0 diff --git a/fwupd-1.8.6/src/tests/usb-devices-invalid.json b/fwupd-1.8.6/src/tests/usb-devices-invalid.json new file mode 100644 index 0000000000000000000000000000000000000000..23f5244931a6654bcf37a676c5d2f661616b7603 --- /dev/null +++ b/fwupd-1.8.6/src/tests/usb-devices-invalid.json @@ -0,0 +1,49 @@ +{ + "UsbDevices": [ + { + "PlatformId": "usb:00", + "IdVendor": 10047, + "IdProduct": 4100, + "Device": 2, + "USB": 512, + "Manufacturer": 1, + "Product": 2, + "UsbBosDescriptors": [ + { + "Comment": "version invalid", + "DevCapabilityType": 5, + "ExtraData": "AGPsCgF09c1SndooUlUNlPAKAAAAIAAqAA==" + }, + { + "Comment": "UUID invalid", + "DevCapabilityType": 5, + "ExtraData": "AAAAAAAAAAAAAAAAAAAAAAAFCAEAIAAqAA==" + }, + { + "Comment": "plugin invalid", + "DevCapabilityType": 5, + "ExtraData": "AGPsCgF09c1SndooUlUNlPAFCAEAIAArAA==" + } + ], + "UsbEvents": [ + { + "Id": "GetStringDescriptor:DescIndex=0x02", + "Data": + "Q29sb3JIdWcyAEcAAAAAAACwA4pgfwAAAN+Vneb9GHkAAAAAAAAAAEA42QAAAAAAwHVdKPx/AAAAAAAAAAAAAJiCXSj8fwAAR9bLiWB/AADAdV0o/H8AAOrE/IlgfwAAgHW/AAAAAAAQw9UAAAAAAJiCXSj8fwAAytnLiQEAAAA=" + }, + { + "Comment": "Plugin=dfu\nIcon=computer\n", + "Id": + "ControlTransfer:Direction=0x00,RequestType=0x02,Recipient=0x00,Request=0x2a,Value=0x0000,Idx=0x0007,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "Data": "UGx1Z2luPWRmdQpJY29uPWNvbXB1dGVyCgAAAAAAAAA=" + }, + { + "Comment": "Plugin=XXX", + "Id": + "ControlTransfer:Direction=0x00,RequestType=0x02,Recipient=0x00,Request=0x2b,Value=0x0000,Idx=0x0007,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "Data": "UGx1Z2luPVhYWAoAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + } + ] + } + ] +} diff --git a/fwupd-1.8.6/src/tests/usb-devices.json b/fwupd-1.8.6/src/tests/usb-devices.json new file mode 100644 index 0000000000000000000000000000000000000000..671591f82f44770a3ff161e2609e9d14722fde56 --- /dev/null +++ b/fwupd-1.8.6/src/tests/usb-devices.json @@ -0,0 +1,110 @@ +{ + "UsbDevices": [ + { + "PlatformId": "usb:01:00:06", + "IdVendor": 10047, + "IdProduct": 4100, + "Device": 2, + "USB": 512, + "Manufacturer": 1, + "Product": 2, + "UsbBosDescriptors": [ + { + "DevCapabilityType": 5, + "ExtraData": "AN9g3diJRcdMnNJlnZ5kip8AAAMG4AQVAA==" + }, + { + "DevCapabilityType": 5, + "ExtraData": "AGPsCgF09c1SndooUlUNlPAFCAEAIAAqAA==" + }, + { + "DevCapabilityType": 17, + "ExtraData": "AQMAAAA=" + } + ], + "UsbInterfaces": [ + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 1, + "InterfaceClass": 255, + "InterfaceSubClass": 70, + "InterfaceProtocol": 87, + "Interface": 3 + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 2, + "InterfaceClass": 255, + "InterfaceSubClass": 71, + "InterfaceProtocol": 85, + "Interface": 4 + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceClass": 3, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 129, + "Interval": 1, + "MaxPacketSize": 64 + }, + { + "DescriptorType": 5, + "EndpointAddress": 1, + "Interval": 1, + "MaxPacketSize": 64 + } + ], + "ExtraData": "CSERAQABIh0A" + } + ], + "UsbEvents": [ + { + "Id": "GetStringDescriptor:DescIndex=0x01", + "Data": + "SHVnaHNraSBMdGQuAAAAAAAAAAAAAAAAIFjfAAAAAAAAAAAAAAAAAEA42QAAAAAAwHVdKPx/AAAAAAAAAAAAAJiCXSj8fwAAR9bLiWB/AADAdV0o/H8AAOrE/IlgfwAAgHW/AAAAAAAQw9UAAAAAAJiCXSj8fwAAytnLiQEAAAA=" + }, + { + "Id": "GetStringDescriptor:DescIndex=0x02", + "Data": + "Q29sb3JIdWcyAEcAAAAAAACwA4pgfwAAAN+Vneb9GHkAAAAAAAAAAEA42QAAAAAAwHVdKPx/AAAAAAAAAAAAAJiCXSj8fwAAR9bLiWB/AADAdV0o/H8AAOrE/IlgfwAAgHW/AAAAAAAQw9UAAAAAAJiCXSj8fwAAytnLiQEAAAA=" + }, + { + "Id": + "GetCustomIndex:ClassId=0xff,SubclassId=0x46,ProtocolId=0x57", + "Data": "Aw==" + }, + { + "Id": "GetStringDescriptor:DescIndex=0x03", + "Data": + "Mi4wLjcAAAAD0WmJYH8AAP8AAAAAAAAAA9FpiWB/AACQRNkAAAAAAGCj2wAAAAAAUHZdKPx/AACNC7qJYH8AAAMAAAAAAAAANougiWB/AACYgl0o/H8AAAAAAAAAAAAA/wAAAPx/V0Zgo9sAAAAAAEh5XSj8fwAAEMPVAAAAAAM=" + }, + { + "Id": + "GetCustomIndex:ClassId=0xff,SubclassId=0x47,ProtocolId=0x55", + "Data": "BA==" + }, + { + "Id": + "ControlTransfer:Direction=0x00,RequestType=0x02,Recipient=0x00,Request=0x15,Value=0x0000,Idx=0x0007,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,Length=0x4e0", + "Data": + "CgAAAAAAAwbgBAgAAQAAANYECAACAAAAkgGAAAQAAQAoAFUAVgBDAC0ARgBTAFMAZQBuAHMAbwByAEcAcgBvAHUAcABJAEQAAABOAHsARgA2ADYARgBBADYANwA0AC0ARgAyAEIARQAtADQARAAxADkALQA5ADgAQwA1AC0ARAAxADMAMgAzADIAOQBDADYANAAyAEYAfQAAAF4ABAABACwAVQBWAEMALQBGAFMAUwBlAG4AcwBvAHIARwByAG8AdQBwAE4AYQBtAGUAAAAoAEwAZQBuAG8AdgBvACAAQwBhAG0AZQByAGEAIABHAHIAbwB1AHAAAAA8AAQABAAuAFUAVgBDAC0ARQBuAGEAYgBsAGUAUABsAGEAdABmAG8AcgBtAEQAbQBmAHQAAAAEAAEAAAA+AAQABAAwAEUAbgBhAGIAbABlAEQAcwBoAG8AdwBSAGUAZABpAHIAZQBjAHQAaQBvAG4AAAAAAAQAAQAAADIABAAEACQAVQBWAEMALQBDAFAAVgAyAEYAYQBjAGUAQQB1AHQAaAAAAAAABAD//wAACAACAAIAwAGAAAQAAQAoAFUAVgBDAC0ARgBTAFMAZQBuAHMAbwByAEcAcgBvAHUAcABJAEQAAABOAHsARgA2ADYARgBBADYANwA0AC0ARgAyAEIARQAtADQARAAxADkALQA5ADgAQwA1AC0ARAAxADMAMgAzADIAOQBDADYANAAyAEYAfQAAAF4ABAABACwAVQBWAEMALQBGAFMAUwBlAG4AcwBvAHIARwByAG8AdQBwAE4AYQBtAGUAAAAoAEwAZQBuAG8AdgBvACAAQwBhAG0AZQByAGEAIABHAHIAbwB1AHAAAAAwAAQABAAiAFMAZQBuAHMAbwByAEMAYQBtAGUAcgBhAE0AbwBkAGUAAAAEAAEAAAA6AAQABAAsAFMAawBpAHAAQwBhAG0AZQByAGEARQBuAHUAbQBlAHIAYQB0AGkAbwBuAAAABAABAAAAPgAEAAQAMABFAG4AYQBiAGwAZQBEAHMAaABvAHcAUgBlAGQAaQByAGUAYwB0AGkAbwBuAAAAAAAEAAEAAAAyAAQABAAkAFUAVgBDAC0AQwBQAFYAMgBGAGEAYwBlAEEAdQB0AGgAAAAAAAQAAAD//wgAAgAEAHwBMgAEAAQAJABEAGUAdgBpAGMAZQBJAGQAbABlAEUAbgBhAGIAbABlAGQAAAAEAAEAAAAyAAQABAAkAEQAZQBmAGEAdQBsAHQASQBkAGwAZQBTAHQAYQB0AGUAAAAAAAQAAQAAADYABAAEACgARABlAGYAYQB1AGwAdABJAGQAbABlAFQAaQBtAGUAbwB1AHQAAAAAAAQAiBMAAEYABAAEADgARABlAHYAaQBjAGUASQBkAGwAZQBJAGcAbgBvAHIAZQBXAGEAawBlAEUAbgBhAGIAbABlAAAAAAAEAAEAAAAUAAMAV0lOVVNCAAAAAAAAAAAAAIAABAABACgARABlAHYAaQBjAGUASQBuAHQAZQByAGYAYQBjAGUARwBVAEkARAAAAE4AewBlAGMAYwBlAGYAZgAzADUALQAxADQANgAzAC0ANABmAGYAMwAtAGEAYwBkADkALQA4AGYAOQA5ADIAZAAwADkAYQBjAGQAZAB9AAAA" + }, + { + "Id": + "ControlTransfer:Direction=0x00,RequestType=0x02,Recipient=0x00,Request=0x2a,Value=0x0000,Idx=0x0007,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "Data": "UGx1Z2luPWRmdQpJY29uPWNvbXB1dGVyCgAAAAAAAAA=" + }, + { + "Id": "GetStringDescriptor:DescIndex=0x04", + "Data": + "MjA4MmI1ZTAtN2E2NC00NzhhLWIxYjItZTM0MDRmYWI2ZGFkAAAAAICg2QAAAAAAUHZdKPx/AACNC7qJYH8AAAQAAAAAAAAANougiWB/AAAAsAOKYH8AAAAAAAAAAAAA/wAAAAAAVUeAoNkAAAAAAFB2XSj8fwAAsKPbAAAAAAQ=" + } + ] + } + ] +} diff --git a/fwupd-1.8.6/subprojects/.gitignore b/fwupd-1.8.6/subprojects/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a24c852ccabc7fd69e6882405fe35e1bcf464e8d --- /dev/null +++ b/fwupd-1.8.6/subprojects/.gitignore @@ -0,0 +1,7 @@ +gusb +gcab +gi-docgen +flashrom +libjcat +libxmlb +fwupd-efi diff --git a/fwupd-1.8.6/subprojects/flashrom.wrap b/fwupd-1.8.6/subprojects/flashrom.wrap new file mode 100644 index 0000000000000000000000000000000000000000..4cfb7653022527606a6afde356d3d1990bc32105 --- /dev/null +++ b/fwupd-1.8.6/subprojects/flashrom.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = flashrom +url = https://github.com/flashrom/flashrom +revision = v1.2 diff --git a/fwupd-1.8.6/subprojects/fwupd-efi.wrap b/fwupd-1.8.6/subprojects/fwupd-efi.wrap new file mode 100644 index 0000000000000000000000000000000000000000..a603102a2f686578a040699f9b8b100ee74facd5 --- /dev/null +++ b/fwupd-1.8.6/subprojects/fwupd-efi.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = fwupd-efi +url = https://github.com/fwupd/fwupd-efi +revision = 1.2 diff --git a/fwupd-1.8.6/subprojects/gcab.wrap b/fwupd-1.8.6/subprojects/gcab.wrap new file mode 100644 index 0000000000000000000000000000000000000000..52640dadb44fc35a3b526c9a89ed4052a5904470 --- /dev/null +++ b/fwupd-1.8.6/subprojects/gcab.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory = gcab +url = https://gitlab.gnome.org/GNOME/gcab.git +revision = b55268ac1020cd6c033acb52f2e6ae984bf5c9fd + +# disabled until we depend on a meson including https://github.com/mesonbuild/meson/pull/10291 +# and https://gitlab.gnome.org/GNOME/gcab/-/merge_requests/10 is merged +# [provide] +# program_names = gcab diff --git a/fwupd-1.8.6/subprojects/gi-docgen.wrap b/fwupd-1.8.6/subprojects/gi-docgen.wrap new file mode 100644 index 0000000000000000000000000000000000000000..87e7b3c29c8d4b447d198be19c2042c3a51222e6 --- /dev/null +++ b/fwupd-1.8.6/subprojects/gi-docgen.wrap @@ -0,0 +1,5 @@ +[wrap-git] +directory=gi-docgen +url=https://gitlab.gnome.org/GNOME/gi-docgen.git +revision=2021.8 +depth=1 diff --git a/fwupd-1.8.6/subprojects/gusb.wrap b/fwupd-1.8.6/subprojects/gusb.wrap new file mode 100644 index 0000000000000000000000000000000000000000..10558da8c60afcc0a7be7ab72321670a7149f324 --- /dev/null +++ b/fwupd-1.8.6/subprojects/gusb.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = gusb +url = https://github.com/hughsie/libgusb.git +revision = 0.4.0 diff --git a/fwupd-1.8.6/subprojects/libjcat.wrap b/fwupd-1.8.6/subprojects/libjcat.wrap new file mode 100644 index 0000000000000000000000000000000000000000..ca468305467584be30efcaeef60962f12d65350b --- /dev/null +++ b/fwupd-1.8.6/subprojects/libjcat.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = libjcat +url = https://github.com/hughsie/libjcat.git +revision = 0.1.11 diff --git a/fwupd-1.8.6/subprojects/libxmlb.wrap b/fwupd-1.8.6/subprojects/libxmlb.wrap new file mode 100644 index 0000000000000000000000000000000000000000..5d44449dd671adb2f9fc0ba731bbd448d33636cd --- /dev/null +++ b/fwupd-1.8.6/subprojects/libxmlb.wrap @@ -0,0 +1,4 @@ +[wrap-git] +directory = libxmlb +url = https://github.com/hughsie/libxmlb.git +revision = 0.3.8 diff --git a/fwupd-efi-1.1.tar.xz b/fwupd-efi-1.1.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..d06352623a22e2d03848f8628470f0b431e3bb1a Binary files /dev/null and b/fwupd-efi-1.1.tar.xz differ diff --git a/fwupd-efi-1.3.tar.xz b/fwupd-efi-1.3.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..69001e0f1669e8e87cac4b31b0c968e6e079f9bf Binary files /dev/null and b/fwupd-efi-1.3.tar.xz differ diff --git a/fwupd.spec b/fwupd.spec index da1ce852020c7d9048185f8287d1ee6825e3b385..a02c0fad9096cdb31b52cff2ced145148014e66e 100644 --- a/fwupd.spec +++ b/fwupd.spec @@ -1,184 +1,1050 @@ -%ifarch x86_64 -%bcond_without redfish -%bcond_without libsmbios +%global glib2_version 2.45.8 +%global libxmlb_version 0.1.3 +%global libgusb_version 0.2.11 +%global libcurl_version 7.61.0 +%global systemd_version 231 +%global json_glib_version 1.1.1 +%global fwupdplugin_version 5 + +# although we ship a few tiny python files these are utilities that 99.99% +# of users do not need -- use this to avoid dragging python onto CoreOS +%global __requires_exclude ^%{python3}$ + +# PPC64 is too slow to complete the tests under 3 minutes... +%ifnarch ppc64le +%global enable_tests 1 %endif +%global enable_dummy 1 +%global __meson_wrap_mode default + +# fwupd.efi is only available on these arches %ifarch x86_64 aarch64 -%bcond_without uefi +%global have_uefi 1 +%endif + +%ifarch i686 x86_64 +%global have_msr 1 +%endif + +# libsmbios is only available on x86 +%ifarch x86_64 +%global have_dell 0 +%endif + +# only available recently +%if 0%{?fedora} >= 34 || 0%{?rhel} >= 9 +%global have_modem_manager 1 %endif -Name: fwupd -Version: 1.2.9 -Release: 5 -Summary: Make updating firmware on Linux automatic, safe and reliable -License: LGPLv2+ -URL: https://github.com/fwupd/fwupd/releases -Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz +Summary: Firmware update daemon +Name: fwupd +Version: 1.8.6 +Release: 1 +License: LGPLv2+ +URL: https://github.com/fwupd/fwupd +Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz +Source1: http://people.freedesktop.org/~hughsient/releases/libjcat-0.1.8.tar.xz +Source2: http://people.freedesktop.org/~hughsient/releases/fwupd-efi-1.1.tar.xz + +Source3: meson-0.61.5-1.noarch.rpm +Source4: meson-help-0.61.5-1.noarch.rpm + +Source10: http://people.redhat.com/rhughes/dbx/DBXUpdate-20100307-x64.cab +Source11: http://people.redhat.com/rhughes/dbx/DBXUpdate-20140413-x64.cab +Source12: http://people.redhat.com/rhughes/dbx/DBXUpdate-20160809-x64.cab +Source13: http://people.redhat.com/rhughes/dbx/DBXUpdate-20200729-aa64.cab +Source14: http://people.redhat.com/rhughes/dbx/DBXUpdate-20200729-ia32.cab +Source15: http://people.redhat.com/rhughes/dbx/DBXUpdate-20200729-x64.cab -#Self-tests are failing due to an expired cert #1264 -Patch1: 0001-Relax-the-certificate-time-checks-in-the-self-tests-.patch -Patch2: 0002-Compilation-failure-due-to-assertion-error.patch -Patch3: CVE-2020-10759.patch +# these are numbered high just to keep them wildly away from colliding with +# the real package sources, in order to reduce churn. -BuildRequires: gettext glib2-devel libxmlb-devel valgrind valgrind-devel libgcab1-devel -BuildRequires: gpgme-devel libgudev1-devel libgusb-devel libsoup-devel polkit-devel sqlite-devel libxslt -BuildRequires: gobject-introspection-devel libarchive-devel systemd gcab elfutils-libelf-devel -BuildRequires: bash-completion json-glib-devel help2man vala meson gnutls-utils gnutls-devel gtk-doc +Source300: centos-ca-secureboot.der +Source301: centossecureboot001.der +Source500: centossecurebootca2.der +Source503: centossecureboot203.der + +BuildRequires: efi-srpm-macros +BuildRequires: gettext +BuildRequires: glib2-devel >= %{glib2_version} +BuildRequires: libxmlb-devel >= %{libxmlb_version} +BuildRequires: libgcab1-devel +BuildRequires: libgudev1-devel +BuildRequires: libgusb-devel >= %{libgusb_version} +BuildRequires: libcurl-devel >= %{libcurl_version} +BuildRequires: polkit-devel >= 0.103 +BuildRequires: sqlite-devel +BuildRequires: gpgme-devel +BuildRequires: systemd >= %{systemd_version} +BuildRequires: systemd-devel +BuildRequires: libarchive-devel +BuildRequires: gobject-introspection-devel +BuildRequires: gcab +%ifarch %{valgrind_arches} +BuildRequires: valgrind +BuildRequires: valgrind-devel +%endif +BuildRequires: python3 ninja-build python3-jinja2 python3-toml python +BuildRequires: gnutls-devel +BuildRequires: gnutls-utils +BuildRequires: help2man +BuildRequires: json-glib-devel >= %{json_glib_version} +BuildRequires: vala +BuildRequires: bash-completion +BuildRequires: git-core -%if %{with uefi} -BuildRequires: python3 python3-cairo python3-gobject python3-pillow -BuildRequires: freetype fontconfig google-noto-sans-cjk-ttc-fonts -BuildRequires: gnu-efi-devel pesign efivar-devel pango-devel cairo-devel cairo-gobject-devel +%if 0%{?have_modem_manager} +BuildRequires: ModemManager-glib-devel >= 1.10.0 +BuildRequires: libqmi-devel >= 1.22.0 +BuildRequires: libmbim-devel %endif -%if %{with redfish} -BuildRequires: efivar-devel +%if 0%{?have_uefi} +BuildRequires: efivar-devel >= 33 +BuildRequires: python3 python3-cairo python3-gobject +BuildRequires: pango-devel +BuildRequires: cairo-devel cairo-gobject-devel +BuildRequires: freetype +BuildRequires: fontconfig +BuildRequires: google-noto-sans-cjk-ttc-fonts +BuildRequires: gnu-efi-devel +BuildRequires: pesign %endif -%if %{with libsmbios} -BuildRequires: efivar-devel libsmbios-devel +%if 0%{?have_dell} +BuildRequires: efivar-devel >= 33 +BuildRequires: libsmbios-devel >= 2.3.0 %endif -Requires: glib2 bubblewrap libsoup libgusb libxmlb shared-mime-info -Requires: pesign -Requires(post):systemd -Requires(preun):systemd -Requires(postun):systemd +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd + +Requires: glib2%{?_isa} >= %{glib2_version} +Requires: libxmlb%{?_isa} >= %{libxmlb_version} +Requires: libgusb%{?_isa} >= %{libgusb_version} +Requires: bubblewrap +Requires: shared-mime-info + +Obsoletes: fwupd-sign < 0.1.6 +Obsoletes: libebitdo < 0.7.5-3 +Obsoletes: libdfu < 1.0.0 +Obsoletes: fwupd-labels < 1.1.0-1 + +Obsoletes: dbxtool < 9 +Provides: dbxtool + +%if 0%{?rhel} > 7 +Obsoletes: fwupdate < 13 +Obsoletes: fwupdate-efi < 13 + +Provides: fwupdate +Provides: fwupdate-efi +%endif -Recommends: python3 +# optional, but a really good idea +Recommends: udisks2 %description -%{name} aims to make updating firmware on Linux automatic, safe and reliable. +fwupd is a daemon to allow session software to update device firmware. %package devel -Summary: Development and installed test files for %{name} -Requires: %{name} = %{version}-%{release} -Provides: %{name}-tests = %{version}-%{release} -Obsoletes: %{name}-tests < %{version}-%{release} +Summary: Development package for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +Obsoletes: libebitdo-devel < 0.7.5-3 +Obsoletes: libdfu-devel < 1.0.0 %description devel -This package contains the development and installed test files for %{name}. +Files for development with %{name}. + +%package tests +Summary: Data files for installed tests +Requires: %{name}%{?_isa} = %{version}-%{release} -%package_help +%description tests +Data files for installed tests. %prep -%autosetup -n %{name}-%{version} -p1 +%setup -q +yum install -y https://download-ib01.fedoraproject.org/pub/epel/8/Everything/x86_64/Packages/l/libcbor-0.7.0-6.el8.x86_64.rpm +yum install -y https://download-ib01.fedoraproject.org/pub/epel/8/Everything/x86_64/Packages/l/libcbor-devel-0.7.0-6.el8.x86_64.rpm +yum install -y http://mirror.stream.centos.org/9-stream/CRB/x86_64/os/Packages/python3-markdown-3.3.4-4.el9.noarch.rpm +yum install -y http://abf-downloads.openmandriva.org/4.3/repository/x86_64/main/release/python-smartypants-2.0.1-1-omv4050.noarch.rpm +yum install -y https://download-ib01.fedoraproject.org/pub/epel/9/Everything/x86_64/Packages/p/python3-typogrify-2.0.7-13.el9.noarch.rpm +yum install -y %_sourcedir/meson-help-0.61.5-1.noarch.rpm +yum install -y %_sourcedir/meson-0.61.5-1.noarch.rpm + +mkdir -p subprojects/libjcat +tar xfvs %{SOURCE1} -C subprojects/libjcat --strip-components=1 + +mkdir -p subprojects/fwupd-efi +tar xfvs %{SOURCE2} -C subprojects/fwupd-efi --strip-components=1 + +sed -ri '1s=^#!/usr/bin/(env )?python3=#!%{__python3}=' \ + contrib/ci/*.py \ + contrib/firmware_packager/*.py \ + contrib/*.py \ + contrib/standalone-installer/assets/*.py \ + contrib/standalone-installer/*.py \ + plugins/dfu/contrib/*.py \ + plugins/uefi-capsule/make-images.py \ + po/test-deps %build -%meson -Dtests=true -Dgtkdoc=true -Dplugin_dummy=true \ -%if %{with uefi} - -Dplugin_uefi=true -Dplugin_nvme=true \ + +# allow rh-signing-tools package for RHEL-8 +export RHEL_ALLOW_PYTHON2_FOR_BUILD=1 + +%meson \ + -Ddocs=docgen \ + -Dlvfs=disabled \ + -Defi_os_dir=%{efi_vendor} \ + -Dlibjcat:gtkdoc=false \ + -Dlibjcat:introspection=false \ + -Dlibjcat:tests=false \ +%if 0%{?enable_tests} + -Dtests=true \ +%else + -Dtests=false \ +%endif +%if 0%{?enable_dummy} + -Dplugin_dummy=true \ +%else + -Dplugin_dummy=false \ +%endif + -Dplugin_flashrom=false \ +%if 0%{?have_msr} + -Dplugin_msr=true \ +%else + -Dplugin_msr=false \ +%endif +%if 0%{?have_uefi} + -Dplugin_uefi_capsule=true \ + -Dplugin_uefi_pk=false \ +%ifarch x86_64 + -Dfwupd-efi:efi_sbat_distro_id="rhel" \ + -Dfwupd-efi:efi_sbat_distro_summary="Red Hat Enterprise Linux" \ + -Dfwupd-efi:efi_sbat_distro_pkgname="%{name}" \ + -Dfwupd-efi:efi_sbat_distro_version="%{version}" \ + -Dfwupd-efi:efi_sbat_distro_url="mail:secalert@redhat.com" \ + -Dfwupd-efi:efi-libdir="/usr/lib64" \ +%endif + -Dplugin_tpm=false \ %else - -Dplugin_uefi=false -Dplugin_nvme=false \ + -Dplugin_uefi_capsule=false \ + -Dplugin_uefi_pk=false \ + -Dplugin_tpm=false \ %endif -%if %{with redfish} - -Dplugin_redfish=true \ +%if 0%{?have_dell} + -Dplugin_dell=true \ + -Dplugin_synaptics_mst=true \ %else - -Dplugin_redfish=false \ + -Dplugin_dell=false \ + -Dplugin_synaptics_mst=false \ %endif -%if %{with libsmbios} - -Dplugin_dell=true -Dplugin_synaptics=true \ +%if 0%{?have_modem_manager} + -Dplugin_modem_manager=true \ %else - -Dplugin_dell=false -Dplugin_synaptics=false \ + -Dplugin_modem_manager=false \ %endif - -Dman=true + -Dplugin_logitech_bulkcontroller=false \ + -Dman=true \ + -Dbluez=false \ + -Dplugin_cfu=false \ + -Dplugin_mtd=false \ + -Dplugin_powerd=false \ + -Dplugin_uf2=false \ + -Dsupported_build=true %meson_build +%if 0%{?enable_tests} +%check +%meson_test +%endif + %install %meson_install -mkdir -pm 0700 %{buildroot}%{_localstatedir}/lib/%{name}/gnupg +# on RHEL the LVFS is disabled by default +mkdir -p %{buildroot}/%{_datadir}/dbxtool +install %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} %{buildroot}/%{_datadir}/dbxtool -%find_lang %{name} +# sign fwupd.efi loader +%ifarch x86_64 +%global efiarch x64 +%global fwup_efi_fn $RPM_BUILD_ROOT%{_libexecdir}/fwupd/efi/fwupd%{efiarch}.efi +%pesign -s -i %{fwup_efi_fn} -o %{fwup_efi_fn}.tmp -a %{SOURCE300} -c %{SOURCE301} -n centossecureboot001 +%pesign -s -i %{fwup_efi_fn}.tmp -o %{fwup_efi_fn}.signed -a %{SOURCE500} -c %{SOURCE503} -n centossecureboot203 +rm -fv %{fwup_efi_fn}.tmp +%endif -%check -%meson_test +mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg + +# workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1757948 +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/cache/fwupd + +%find_lang %{name} %post -/sbin/ldconfig -%systemd_post %{name}.service +%systemd_post fwupd.service + +# change vendor-installed remotes to use the default keyring type +for fn in /etc/fwupd/remotes.d/*.conf; do + if grep -q "Keyring=gpg" "$fn"; then + sed -i 's/Keyring=gpg/#Keyring=pkcs/g' "$fn"; + fi +done %preun -%systemd_preun %{name}.service +%systemd_preun fwupd.service %postun -/sbin/ldconfig -%systemd_postun_with_restart %{name}.service +%systemd_postun_with_restart fwupd.service %systemd_postun_with_restart pesign.service %files -f %{name}.lang -%doc README.md -%license COPYING AUTHORS -%{_bindir}/* -%config(noreplace)%{_sysconfdir}/%{name}/remotes.d/*.conf -%config(noreplace)%{_sysconfdir}/pki/%{name} -%config(noreplace)%{_sysconfdir}/%{name}/daemon.conf +%doc README.md AUTHORS +%license COPYING +%config(noreplace)%{_sysconfdir}/fwupd/daemon.conf +%if 0%{?have_uefi} +%config(noreplace)%{_sysconfdir}/fwupd/uefi_capsule.conf +%endif +%config(noreplace)%{_sysconfdir}/fwupd/redfish.conf +%config(noreplace)%{_sysconfdir}/fwupd/thunderbolt.conf +%dir %{_libexecdir}/fwupd +%{_libexecdir}/fwupd/fwupd +%ifarch i686 x86_64 +%{_libexecdir}/fwupd/fwupd-detect-cet +%endif +%{_libexecdir}/fwupd/fwupdoffline +%if 0%{?have_uefi} +%{_libexecdir}/fwupd/efi/*.efi +%ifarch x86_64 +%{_libexecdir}/fwupd/efi/*.efi.signed +%endif +%{_bindir}/fwupdate +%endif +%{_bindir}/dfu-tool +%if 0%{?have_uefi} +%{_bindir}/dbxtool +%endif +%{_bindir}/fwupdmgr +%{_bindir}/fwupdtool +%{_bindir}/fwupdagent +%{_bindir}/jcat-tool +%dir %{_sysconfdir}/fwupd +%dir %{_sysconfdir}/fwupd/remotes.d +%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/*.%{name}.conf -%{_libexecdir}/%{name}/%{name} -%{_libexecdir}/%{name}/%{name}tool -%{_libexecdir}/%{name}/%{name}agent -%{_libexecdir}/%{name}/%{name}offline -%{_datadir}/bash-*/completions/* -%{_datadir}/%{name}/metainfo/*.metainfo.xml -%{_datadir}/%{name}/remotes.d/*/*/*.md -%{_datadir}/metainfo/*.metainfo.xml -%{_datadir}/%{name}/firmware-packager -%{_datadir}/dbus-1/interfaces/*.fwupd.xml -%{_datadir}/dbus-1/system-services/*.service -%{_datadir}/polkit-1/*/org.freedesktop* -%{_datadir}/%{name}/quirks.d/ -%{_datadir}/icons/hicolor/scalable/apps/*.%{name}.svg -%{_localstatedir}/lib/%{name}/*/*.md -%{_libdir}/lib%{name}*.so.* -%{_libdir}/girepository-1.0/*.typelib -%{_prefix}/lib/udev/rules.d/*.rules -%{_prefix}/lib/systemd/system*/%{name}.shutdown -%{_unitdir}/*.service -%{_unitdir}/*.wants/ -%{_libdir}/fwupd-plugins-3/*.so -%ghost %{_localstatedir}/lib/fwupd/gnupg -%if %{with uefi} -%config(noreplace)%{_sysconfdir}/%{name}/uefi.conf -%{_libexecdir}/%{name}/efi/*.efi -%{_libexecdir}/%{name}/fwupdate -%{_datadir}/*/*/LC_IMAGES/%{name}* +%if 0%{?have_msr} +/usr/lib/modules-load.d/fwupd-msr.conf +%endif +%{_datadir}/dbus-1/system.d/org.freedesktop.fwupd.conf +%{_datadir}/bash-completion/completions/fwupdmgr +%{_datadir}/bash-completion/completions/fwupdtool +%{_datadir}/bash-completion/completions/fwupdagent +%{_datadir}/fish/vendor_completions.d/fwupdmgr.fish +%{_datadir}/fwupd/metainfo/org.freedesktop.fwupd*.metainfo.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 +%{_datadir}/polkit-1/rules.d/org.freedesktop.fwupd.rules +%{_datadir}/dbus-1/system-services/org.freedesktop.fwupd.service +%dir %{_datadir}/dbxtool +%{_datadir}/dbxtool/DBXUpdate-20100307-x64.cab +%{_datadir}/dbxtool/DBXUpdate-20140413-x64.cab +%{_datadir}/dbxtool/DBXUpdate-20160809-x64.cab +%{_datadir}/dbxtool/DBXUpdate-20200729-aa64.cab +%{_datadir}/dbxtool/DBXUpdate-20200729-ia32.cab +%{_datadir}/dbxtool/DBXUpdate-20200729-x64.cab +%{_mandir}/man1/fwupdtool.1* +%{_mandir}/man1/fwupdagent.1* +%{_mandir}/man1/dfu-tool.1* +%if 0%{?have_uefi} +%{_mandir}/man1/dbxtool.* +%endif +%{_mandir}/man1/fwupdmgr.1* +%if 0%{?have_uefi} +%{_mandir}/man1/fwupdate.1* +%endif +%{_mandir}/man1/jcat-tool.1* +%{_datadir}/metainfo/org.freedesktop.fwupd.metainfo.xml +%{_datadir}/icons/hicolor/scalable/apps/org.freedesktop.fwupd.svg +%{_datadir}/fwupd/firmware_packager.py +%{_datadir}/fwupd/simple_client.py +%{_datadir}/fwupd/add_capsule_header.py +%{_datadir}/fwupd/install_dell_bios_exe.py +%{_unitdir}/fwupd-offline-update.service +%{_unitdir}/fwupd.service +%{_unitdir}/fwupd-refresh.service +%{_unitdir}/fwupd-refresh.timer +%{_presetdir}/fwupd-refresh.preset +%{_unitdir}/system-update.target.wants/ +%dir %{_localstatedir}/lib/fwupd +%dir %{_localstatedir}/cache/fwupd +%dir %{_datadir}/fwupd/quirks.d +%if 0%{?have_uefi} +%{_sysconfdir}/grub.d/35_fwupd %endif -%if %{with redfish} -%config(noreplace)%{_sysconfdir}/%{name}/redfish.conf +%{_libdir}/libfwupd.so.2* +%{_libdir}/libjcat.so.* +%{_libdir}/girepository-1.0/Fwupd-2.0.typelib +/usr/lib/udev/rules.d/*.rules +/usr/lib/systemd/system-shutdown/fwupd.shutdown +%ghost %{_localstatedir}/lib/fwupd/gnupg + +%if 0%{?have_modem_manager} +%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_modem_manager.so %endif -%if %{with libsmbios} -%{_datadir}/%{name}/remotes.d/dell*/* +%if 0%{?have_uefi} +%{_datadir}/fwupd/uefi-capsule-ux.tar.xz %endif %files devel -%{_libdir}/*.so -%{_libdir}/*/*.pc -%{_includedir}/* -%{_datadir}/vala/* -%{_datadir}/gtk-doc/*/lib%{name} -%{_datadir}/*/*.gir -%{_datadir}/installed-tests/%{name} -%dir %{_sysconfdir}/%{name}/remotes.d -%config(noreplace)%{_sysconfdir}/%{name}/remotes.d/%{name}-tests.conf - -%files help -%{_datadir}/man/man1/* +%{_datadir}/gir-1.0/Fwupd-2.0.gir +%{_datadir}/vala/vapi +%{_includedir}/fwupd-1 +%{_includedir}/libjcat-1 +%{_libdir}/libfwupd*.so +%{_libdir}/libjcat.so +%{_libdir}/pkgconfig/fwupd.pc +%if 0%{?have_uefi} +%{_libdir}/pkgconfig/fwupd-efi.pc +%endif +%{_libdir}/pkgconfig/jcat.pc + +%files tests +%if 0%{?enable_tests} +%dir %{_datadir}/installed-tests/fwupd +%{_datadir}/installed-tests/fwupd/tests/* +%{_datadir}/installed-tests/fwupd/fwupd-tests.xml +%{_datadir}/installed-tests/fwupd/*.test +%{_datadir}/installed-tests/fwupd/*.cab +%{_datadir}/installed-tests/fwupd/*.sh +%if 0%{?have_uefi} +%{_datadir}/installed-tests/fwupd/efi +%endif +%{_datadir}/fwupd/device-tests/*.json +%{_libexecdir}/installed-tests/fwupd/* +%dir %{_sysconfdir}/fwupd/remotes.d +%config(noreplace)%{_sysconfdir}/fwupd/remotes.d/fwupd-tests.conf +%endif +%{_datadir}/doc/libfwupd/* +%{_datadir}/doc/libfwupdplugin/* +%{_sysconfdir}/fwupd/bios-settings.d/README.md +%{_sysconfdir}/fwupd/msr.conf +%{_libdir}/fwupd-1.8.6/libfwupdengine.so +%{_libdir}/fwupd-1.8.6/libfwupdplugin.so +%{_libdir}/fwupd-1.8.6/libfwupdutil.so +%{_datadir}/doc/fwupd/hsi.html +%{_datadir}/doc/fwupd/index.html +%{_datadir}/doc/fwupd/libfwupd +%{_datadir}/doc/fwupd/libfwupdplugin +%{_datadir}/fwupd/__pycache__/* +%{_datadir}/fwupd/host-emulate.d/thinkpad-p1-iommu.json.gz +%{_datadir}/fwupd/quirks.d/builtin.quirk.gz +%{_datadir}/locale/* +%{_datadir}/man/man1/*.1.gz +%{_datadir}/metainfo/org.freedesktop.fwupd.metainfo.xml +%{_datadir}/polkit-1/actions/org.freedesktop.fwupd.policy +%{_datadir}/polkit-1/rules.d/org.freedesktop.fwupd.rules +%{_datadir}/vala/vapi/fwupd.deps +%{_datadir}/vala/vapi/fwupd.vapi + %changelog -* Fri Aug 05 2022 yaoxin - 1.2.9-5 -- Resolve fwupd upgrade and downgrade error +* Thu Mar 31 2022 Richard Hughes 1.7.4-2 +- Rebuild to work around gating failures +- Resolves: rhbz#2037294 + +* Thu Jan 13 2022 Richard Hughes 1.7.4-1 +- Include support for Lenovo TBT4 Docking stations +- Do not cause systemd-modules-load failures +- Resolves: rhbz#2038258 +- Resolves: rhbz#2037294 + +* Thu Dec 09 2021 Richard Hughes 1.7.1-2 +- Disable the Logitech bulkcontroller plugin to avoid adding a dep to protobuf-c + which lives in AppStream, not BaseOS. +- Resolves: rhbz#2029333 + +* Mon Nov 01 2021 Richard Hughes 1.7.1-1 +- New upstream release +- Backport upstream changes +- Include support for Dell TBT4 Docking stations +- Resolves: rhbz#1969472 +- Resolves: rhbz#1976408 + +* Tue Apr 13 2021 Richard Hughes 1.5.9-3 +- Rebase to include the SBAT metadata section to allow fixing BootHole +- Resolves: rhbz#1933012 +- Resolves: rhbz#1932953 +- Resolves: rhbz#1932909 +- Resolves: rhbz#1932882 +- Resolves: rhbz#1932579 +- Resolves: rhbz#1932553 +- Resolves: rhbz#1932423 + +* Wed Feb 10 2021 Richard Hughes 1.5.5-3 +- Backport a fix from upstream to fix a crash in the Goodix MOC plugin. +- Resolves: #1927091 + +* Tue Feb 09 2021 Richard Hughes 1.5.5-2 +- Do not invalidate all remote timestamps during package install to fix rpm -V. +- Backport some important high priority fixes from upstream. +- Resolves: #1926382 + +* Mon Jan 11 2021 Richard Hughes 1.5.5-1 +- Rebase package to include support for latest OEM hardware and to + support deploying UEFI SecureBoot dbx updates. +- Resolves: #1870811 + +* Wed Dec 16 2020 Richard Hughes 1.5.4-1 +- Rebase package to include support for latest OEM hardware and to + support deploying UEFI SecureBoot dbx updates. +- Resolves: #1870811 + +* Fri Jul 24 2020 Peter Jones - 1.4.2-4 +- Add signing with redhatsecureboot503 cert + Related: CVE-2020-10713 + +* Thu Jul 23 2020 Richard Hughes 1.4.2-3 +- Obsolete the now-dead fwupdate package to prevent file conflicts +- Resolves: #1859202 + +* Fri Jun 05 2020 Richard Hughes 1.4.2-2 +- Security fix for CVE-2020-10759 +- Resolves: #1844324 + +* Mon May 18 2020 Richard Hughes 1.4.2-1 +- New upstream release +- Backport a patch to fix the synaptics fingerprint reader update. +- Resolves: #1775277 + +* Mon Apr 27 2020 Richard Hughes 1.4.1-1 +- New upstream release +- Resolves: #1775277 + +* Wed Feb 19 2020 Richard Hughes 1.1.4-6 +- Rebuild to get the EFI executable signed with the Red Hat key +- Resolves: #1713033 + +* Thu Feb 13 2020 Richard Hughes 1.1.4-5 +- Backport a patch to specify the EFI os name +- Resolves: #1713033 + +* Fri Nov 29 2019 Richard Hughes 1.1.4-4 +- Rebuild to get the EFI executable signed with the Red Hat key +- Resolves: #1680154 + +* Fri Nov 29 2019 Richard Hughes 1.1.4-3 +- Disable wacomhid by default as probing the device stops the tablet working +- Resolves: #1680154 + +* Mon Nov 25 2019 Richard Hughes 1.1.4-2 +- Do not require python3 in the base package +- Resolves: #1724593 + +* Wed Nov 07 2018 Richard Hughes 1.1.4-1 +- New upstream release +- Use HTTPS_PROXY if set +- Make the dell-dock plugin more robust in several ways +- Adjust EVB board handling +- Resolves: #1647557 + +* Fri Oct 12 2018 Richard Hughes 1.1.3-1 +- New upstream release +- Adds support for an upcoming Dell USB-C dock +- Don't use an obsolete font when building the UEFI images +- Resolves: #1607842 + +* Wed Oct 10 2018 Richard Hughes 1.1.1-11 +- Rebuild to get the EFI executable signed with the Red Hat key +- Related: #1614424 + +* Fri Sep 28 2018 Brendan Reilly 1.1.1-10 +- Rebuild +- Related: #1614424 + +* Thu Sep 20 2018 Brendan Reilly 1.1.1-9 +- Rebuild +- Related: #1614424 + +* Tue Sep 18 2018 Tomas Mlcoch 1.1.1-8 +- Rebuild +- Related: #1614424 + +* Tue Sep 04 2018 Richard Hughes 1.1.1-7 +- Rebuild to get the EFI executable signed with the Red Hat key +- Related: #1614424 + +* Fri Aug 31 2018 Richard Hughes 1.1.1-6 +- Include the certificates for secure boot signing + +* Wed Aug 29 2018 Richard Hughes 1.1.1-5 +- Include the certificates for secure boot signing + +* Tue Aug 23 2018 Richard Hughes 1.1.1-4 +- Rebuild to get the EFI executable signed with the Red Hat key + +* Thu Aug 23 2018 Richard Hughes 1.1.1-3 +- Rebuild to get the EFI executable signed with the Red Hat key + +* Mon Aug 20 2018 Richard Hughes 1.1.1-2 +- Rebuild to get the EFI executable signed with the Red Hat key + +* Mon Aug 13 2018 Richard Hughes 1.1.1-1 +- New upstream release +- Add support for the Synaptics Panamera hardware +- Add validation for Alpine and Titan Ridge +- Allow flashing unifying devices in recovery mode +- Allow running synapticsmst on non-Dell hardware +- Check the ESP for sanity at at startup +- Do not hold hidraw devices open forever +- 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 +- Improve the Redfish plugin to actually work with real hardware +- Reboot synapticsmst devices at the end of flash cycle +- Show the correct title when updating devices + +* Fri Aug 3 2018 Florian Weimer - 1.1.0-3 +- Honor %%{valgrind_arches} + +* Thu Jul 12 2018 Richard Hughes 1.1.0-2 +- Rebuild to get the EFI executable signed with the Red Hat key + +* Wed Jul 11 2018 Richard Hughes 1.1.0-1 +- New upstream release +- Add a initial Redfish support +- Allow devices to assign a plugin from the quirk subsystem +- Detect the EFI system partition location at runtime +- Do not use 8bitdo bootloader commands after a successful flash +- 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 unifying failure to detach when using a slow host controller +- Merge fwupdate functionality into fwupd +- Support more Wacom tablets + +* Wed Jun 20 2018 Tomas Orsava - 1.0.6-2 +- Switch hardcoded python3 shebangs into the %%{__python3} macro +- Add missing BuildRequires on python3-devel so that %%{__python3} macro is + defined + +* Mon Mar 12 2018 Richard Hughes 1.0.6-1 +- New upstream release +- Add bash completion for fwupdmgr +- Add support for newest Thunderbolt chips +- Allow devices to use the runtime version when in bootloader mode +- Allow overriding ESP mount point via conf file +- Correct handling of unknown Thunderbolt devices +- Correctly detect new remotes that are manually copied +- Delete any old fwupdate capsules and efivars when launching fwupd +- Fix a crash related to when passing device to downgrade in CLI +- Fix Unifying signature writing and parsing for Texas bootloader +- Generate Vala bindings + +* Fri Feb 23 2018 Richard Hughes 1.0.5-2 +- Use the new CDN for metadata. + +* Wed Feb 14 2018 Richard Hughes 1.0.5-1 +- New upstream release +- Be more careful deleting and modifying device history +- Fix crasher with MST flashing +- Fix DFU detach with newer releases of libusb +- Offer to reboot when processing an offline update +- Show the user a URL when they report a known problem +- Stop matching 8bitdo DS4 controller VID/PID +- Support split cabinet archives as produced by Windows Update + +* Wed Feb 07 2018 Fedora Release Engineering - 1.0.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Jan 25 2018 Richard Hughes 1.0.4-1 +- New upstream release +- Add a device name for locked UEFI devices +- 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 +- Never add two devices to the daemon with the same ID +- Rescan supported flags when refreshing metadata +- Store firmware update success and failure to a local database + +* Fri Jan 12 2018 Richard Hughes 1.0.3-2 +- Backport a patch that fixes applying firmware updates using gnome-software. + +* Tue Jan 09 2018 Richard Hughes 1.0.3-1 +- New upstream release +- Add a new plugin to add support for CSR "Driverless DFU" +- Add initial SF30/SN30 Pro support +- Block owned Dell TPM updates +- Choose the correct component from provides matches using requirements +- Do not try to parse huge compressed archive files +- Handle Thunderbolt "native" mode +- Use the new functionality in libgcab >= 1.0 to avoid writing temp files + +* Tue Nov 28 2017 Richard Hughes 1.0.2-1 +- New upstream release +- Add a plugin for the Nitrokey Storage device +- Add quirk for AT32UC3B1256 as used in the RubberDucky +- Add support for the original AVR DFU protocol +- Allow different plugins to claim the same device +- Disable the dell plugin if libsmbios fails +- Fix critical warning when more than one remote fails to load +- Ignore useless Thunderbolt device types +- Set environment variables to allow easy per-plugin debugging +- Show a nicer error message if the requirement fails +- Sort the output of GetUpgrades correctly +- Use a SHA1 hash for the internal DeviceID + +* Thu Nov 09 2017 Kalev Lember 1.0.1-3 +- Rebuild against libappstream-glib 0.7.4 + +* Thu Nov 09 2017 Kalev Lember 1.0.1-2 +- Fix libdfu obsoletes versions + +* Thu Nov 09 2017 Richard Hughes 1.0.1-1 +- New upstream release +- 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 +- 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 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 +- Move the database of supported devices out into runtime loaded files +- Support devices with truncated DFU interface data +- Use the correct wDetachTimeOut when writing DFU firmware +- Verify devices with legacy VIDs are actually 8Bitdo controllers + +* Mon Oct 09 2017 Richard Hughes 1.0.0-1 +- New upstream release +- This release breaks API and ABI to remove deprecated symbols +- libdfu is now not installed as a shared library +- Add FuDeviceLocker to simplify device open/close lifecycles +- Add functionality to blacklist Dell HW with problems +- Disable the fallback USB plugin +- Do not fail to load the daemon if cached metadata is invalid +- Do not use system-specific infomation for UEFI PCI devices +- Fix various printing issues with the progressbar +- Never fallback to an offline update from client code +- Only set the Dell coldplug delay when we know we need it +- 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 + +* Fri Sep 01 2017 Richard Hughes 0.9.7-1 +- New upstream release +- 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 .exe files +- Add a thunderbolt plugin for new kernel interface +- Fix an incomplete cipher when using XTEA on data not in 4 byte chunks +- Show a bouncing progress bar if the percentage remains at zero +- Use the new bootloader PIDs for Unifying pico receivers + +* Fri Sep 01 2017 Kalev Lember 0.9.6-2 +- Disable i686 UEFI support now that fwupdate is no longer available there +- Enable aarch64 UEFI support now that all the deps are available there + +* Thu Aug 03 2017 Richard Hughes 0.9.6-1 +- New upstream release +- Add --version option to fwupdmgr +- Display all errors recorded by efi_error tracing +- Don't log a warning when an unknown unifying report is parsed +- Fix a hang on 32 bit machines +- Make sure the unifying percentage completion goes from 0% to 100% +- Support embedded devices with local firmware metadata +- Use new GUsb functionality to fix flashing Unifying devices + +* Wed Aug 02 2017 Fedora Release Engineering - 0.9.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.9.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Tue Jul 04 2017 Richard Hughes 0.9.5-1 +- New upstream release +- Add a plugin to get the version of the AMT ME interface +- Allow flashing Unifying devices in bootloader modes +- 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 +- Use the UFY DeviceID prefix for Unifying devices + +* Thu Jun 15 2017 Richard Hughes 0.9.4-1 +- New upstream release +- Add installed tests that use the daemon +- Add the ability to restrict firmware to specific vendors +- Compile with newer versions of meson +- Fix a common crash when refreshing metadata +- Generate a images for status messages during system firmware update +- Show progress download when refreshing metadata +- Use the correct type signature in the D-Bus introspection file + +* Wed Jun 07 2017 Richard Hughes 0.9.3-1 +- New upstream release +- Add a 'downgrade' command to fwupdmgr +- Add a 'get-releases' command to fwupdmgr +- Add support for Microsoft HardwareIDs +- Allow downloading metadata from more than just the LVFS +- Allow multiple checksums on devices and releases +- Correctly open Unifying devices with original factory firmware +- 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 + +* Tue May 23 2017 Richard Hughes 0.9.2-2 +- Backport several fixes for updating Unifying devices + +* Mon May 22 2017 Richard Hughes 0.9.2-1 +- New upstream release +- Add support for Unifying DFU features +- Do not spew a critial warning when parsing an invalid URI +- Ensure steelseries device is closed if it returns an invalid packet +- Ignore spaces in the Unifying version prefix + +* Thu Apr 20 2017 Richard Hughes 0.8.2-1 +- New upstream release +- Add a config option to allow runtime disabling plugins by name +- Add DFU quirk for OpenPICC and SIMtrace +- Create directories in /var/cache as required +- Fix the Requires lines in the dfu pkg-config file +- Only try to mkdir the localstatedir if we have the right permissions +- Support proxy servers in fwupdmgr + +* Thu Mar 23 2017 Bastien Nocera - 0.8.1-2 ++ fwupd-0.8.1-2 +- Release claimed devices on error, fixes unusable input devices + +* Mon Feb 27 2017 Richard Hughes 0.8.1-1 +- New upstream release +- Adjust systemd confinement restrictions +- Don't initialize libsmbios on unsupported systems +- Fix a crash when enumerating devices + +* Wed Feb 08 2017 Richard Hughes 0.8.0-1 +- New upstream release +- 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 +- Always close USB devices before error returns +- 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 + +* Sat Dec 10 2016 Igor Gnatenko - 0.7.5-2 +- Rebuild for gpgme 1.18 + +* Wed Oct 19 2016 Richard Hughes 0.7.5-1 +- New upstream release +- 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 +- Fix an assert when unlocking the dummy ESRT device +- Fix writing firmware to devices using the ST reference bootloader +- Match the Dell TB16 device + +* Mon Sep 19 2016 Richard Hughes 0.7.4-1 +- New upstream release +- Add a fallback for older appstream-glib releases +- Allow the argument to 'dfu-tool set-release' be major.minor +- Fix a possible crash when uploading firmware files using libdfu +- Fix libfwupd self tests when a host-provided fwupd is not available +- Load the Altos USB descriptor from ELF files +- Show the human-readable version in the 'dfu-tool dump' output +- Support writing the IHEX symbol table +- Write the ELF files with the correct section type + +* Mon Aug 29 2016 Kalev Lember 0.7.3-2 +- Fix an unexpanded macro in the spec file +- Tighten libebitdo-devel requires with the _isa macro +- Add ldconfig scripts for libdfu and libebitdo subpackages + +* Mon Aug 29 2016 Richard Hughes 0.7.3-1 +- New upstream release +- Add Dell TPM and TB15/WD15 support via new Dell provider +- 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 +- Don't fail while checking versions or locked state +- Show a progress notification when installing firmware +- Show the vendor flashing instructions when installing +- Use a private gnupg key store +- Use the correct firmware when installing a composite device + +* Fri Aug 19 2016 Peter Jones - 0.7.2-6 +- Rebuild to get libfwup.so.1 as our fwupdate dep. This should make this the + last time we need to rebuild for this. + +* Wed Aug 17 2016 Peter Jones - 0.7.2-5 +- rebuild against new efivar and fwupdate + +* Fri Aug 12 2016 Adam Williamson - 0.7.2-4 +- rebuild against new efivar and fwupdate + +* Thu Aug 11 2016 Richard Hughes 0.7.2-3 +- Use the new CDN for firmware metadata + +* Thu Jul 14 2016 Kalev Lember - 0.7.2-2 +- Tighten subpackage dependencies + +* Mon Jun 13 2016 Richard Hughes 0.7.2-1 +- New upstream release +- Allow devices to have multiple assigned GUIDs +- Allow metainfo files to match only specific revisions of devices +- Only claim the DFU interface when required +- Only return updatable devices from GetDevices() +- Show the DFU protocol version in 'dfu-tool list' + +* Fri May 13 2016 Richard Hughes 0.7.1-1 +- New upstream release +- Add device-added, device-removed and device-changed signals +- Add for a new device field "Flashes Left" +- 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 +- Return all update descriptions newer than the installed version +- Set the device description when parsing local firmware files + +* Fri Apr 01 2016 Richard Hughes 0.7.0-1 +- New upstream release +- Add Alienware to the version quirk table +- Add a version plugin for SteelSeries hardware +- Do not return updates that require AC when on battery +- Return the device flags when getting firmware details + +* Mon Mar 14 2016 Richard Hughes 0.6.3-1 +- New upstream release +- Add an unlock method for devices +- Add ESRT enable method into UEFI provider +- Correct the BCD version number for DFU 1.1 +- 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 + +* Fri Feb 12 2016 Richard Hughes 0.6.2-1 +- New upstream release +- Add 'Created' and 'Modified' properties on managed devices +- Fix get-results for UEFI provider +- Support vendor-specific UEFI version encodings + +* Wed Feb 03 2016 Fedora Release Engineering - 0.6.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Jan 19 2016 Richard Hughes 0.6.1-1 +- New upstream release +- Do not misdetect different ColorHug devices +- Only dump the profiling data when run with --verbose + +* Mon Dec 07 2015 Richard Hughes 0.6.0-1 +- New upstream release +- Add support for automatically updating USB DFU-capable devices +- Emit the changed signal after doing an update +- Export the AppStream ID when returning device results +- Use the same device identification string format as Microsoft + +* Wed Nov 18 2015 Richard Hughes 0.5.4-1 +- New upstream release +- Use API available in fwupdate 0.5 to avoid writing temp files +- Fix compile error against fwupdate 0.5 due to API bump + +* Thu Nov 05 2015 Richard Hughes 0.5.3-1 +- New upstream release +- 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 + +* Wed Oct 28 2015 Richard Hughes 0.5.2-1 +- New upstream release +- 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 +- Show the dotted-decimal representation of the UEFI version number +- Support cabinet archives files with more than one firmware + +* Mon Sep 21 2015 Richard Hughes 0.5.1-1 +- Update to 0.5.1 to fix a bug in the offline updater + +* Tue Sep 15 2015 Richard Hughes 0.5.0-1 +- New upstream release +- Do not reboot if racing with the PackageKit offline update mechanism + +* Thu Sep 10 2015 Richard Hughes 0.1.6-3 +- Do not merge the existing firmware metadata with the submitted files + +* Thu Sep 10 2015 Kalev Lember 0.1.6-2 +- Own system-update.target.wants directory +- Make fwupd-sign obsoletes versioned + +* Thu Sep 10 2015 Richard Hughes 0.1.6-1 +- New upstream release +- Add application metadata when getting the updates list +- Remove fwsignd, we have the LVFS now + +* Fri Aug 21 2015 Kalev Lember 0.1.5-3 +- Disable fwupd offline update service + +* Wed Aug 19 2015 Richard Hughes 0.1.5-2 +- Use the non-beta download URL prefix + +* Wed Aug 12 2015 Richard Hughes 0.1.5-1 +- New upstream release +- Add a Raspberry Pi firmware provider +- Fix validation of written firmware +- Make parsing the option ROM runtime optional +- Use the AppStream 0.9 firmware specification by default + +* Sat Jul 25 2015 Richard Hughes 0.1.4-1 +- New upstream release +- 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 + +* Wed Jun 17 2015 Fedora Release Engineering - 0.1.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed Jun 03 2015 Richard Hughes 0.1.3-2 +- Compile with libfwupdate for UEFI firmware support. -* Thu Jul 14 2022 yangweidong - 1.2.9-4 -- Solve 0002-Compilation-failure-due-to-assertion-error -- Fix CVE-2020-10759 +* Thu May 28 2015 Richard Hughes 0.1.3-1 +- New upstream release +- Coldplug the devices before acquiring the well known name +- Run the offline actions using systemd when required +- Support OpenHardware devices using the fwupd vendor extensions -* Fri Jun 5 2020 Senlin Xia - 1.2.9-3 -- remove sign for fwupd efi +* Wed Apr 22 2015 Richard Hughes 0.1.2-1 +- New upstream release +- Only allow signed firmware to be upgraded without a password -* Mon Dec 9 2019 openEuler Buildteam - 1.2.9-2 -- Solve build problem of x86 +* Mon Mar 23 2015 Richard Hughes 0.1.1-1 +- New upstream release +- Add a 'get-updates' command to fwupdmgr +- Add and document the offline-update lifecycle +- Create a libfwupd shared library +- Create runtime directories if they do not exist +- Do not crash when there are no devices to return -* Wed Nov 20 2019 openEuler Buildteam - 1.2.9-1 -- Package init +* Mon Mar 16 2015 Richard Hughes 0.1.0-1 +- First release diff --git a/get-pip.py b/get-pip.py new file mode 100644 index 0000000000000000000000000000000000000000..acce1084e5fab7a98a4790681d3b1914d055b660 --- /dev/null +++ b/get-pip.py @@ -0,0 +1,32207 @@ +#!/usr/bin/env python +# +# Hi There! +# +# You may be wondering what this giant blob of binary data here is, you might +# even be worried that we're up to something nefarious (good for you for being +# paranoid!). This is a base85 encoding of a zip file, this zip file contains +# an entire copy of pip (version 22.3). +# +# Pip is a thing that installs packages, pip itself is a package that someone +# might want to install, especially if they're looking to run this get-pip.py +# script. Pip has a lot of code to deal with the security of installing +# packages, various edge cases on various platforms, and other such sort of +# "tribal knowledge" that has been encoded in its code base. Because of this +# we basically include an entire copy of pip inside this blob. We do this +# because the alternatives are attempt to implement a "minipip" that probably +# doesn't do things correctly and has weird edge cases, or compress pip itself +# down into a single file. +# +# If you're wondering how this is created, it is generated using +# `scripts/generate.py` in https://github.com/pypa/get-pip. + +import sys + +this_python = sys.version_info[:2] +min_version = (3, 7) +if this_python < min_version: + message_parts = [ + "This script does not work on Python {}.{}".format(*this_python), + "The minimum supported Python version is {}.{}.".format(*min_version), + "Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python), + ] + print("ERROR: " + " ".join(message_parts)) + sys.exit(1) + + +import os.path +import pkgutil +import shutil +import tempfile +import argparse +import importlib +from base64 import b85decode + + +def include_setuptools(args): + """ + Install setuptools only if absent and not excluded. + """ + cli = not args.no_setuptools + env = not os.environ.get("PIP_NO_SETUPTOOLS") + absent = not importlib.util.find_spec("setuptools") + return cli and env and absent + + +def include_wheel(args): + """ + Install wheel only if absent and not excluded. + """ + cli = not args.no_wheel + env = not os.environ.get("PIP_NO_WHEEL") + absent = not importlib.util.find_spec("wheel") + return cli and env and absent + + +def determine_pip_install_arguments(): + pre_parser = argparse.ArgumentParser() + pre_parser.add_argument("--no-setuptools", action="store_true") + pre_parser.add_argument("--no-wheel", action="store_true") + pre, args = pre_parser.parse_known_args() + + args.append("pip") + + if include_setuptools(pre): + args.append("setuptools") + + if include_wheel(pre): + args.append("wheel") + + return ["install", "--upgrade", "--force-reinstall"] + args + + +def monkeypatch_for_cert(tmpdir): + """Patches `pip install` to provide default certificate with the lowest priority. + + This ensures that the bundled certificates are used unless the user specifies a + custom cert via any of pip's option passing mechanisms (config, env-var, CLI). + + A monkeypatch is the easiest way to achieve this, without messing too much with + the rest of pip's internals. + """ + from pip._internal.commands.install import InstallCommand + + # We want to be using the internal certificates. + cert_path = os.path.join(tmpdir, "cacert.pem") + with open(cert_path, "wb") as cert: + cert.write(pkgutil.get_data("pip._vendor.certifi", "cacert.pem")) + + install_parse_args = InstallCommand.parse_args + + def cert_parse_args(self, args): + if not self.parser.get_default_values().cert: + # There are no user provided cert -- force use of bundled cert + self.parser.defaults["cert"] = cert_path # calculated above + return install_parse_args(self, args) + + InstallCommand.parse_args = cert_parse_args + + +def bootstrap(tmpdir): + monkeypatch_for_cert(tmpdir) + + # Execute the included pip and use it to install the latest pip and + # setuptools from PyPI + from pip._internal.cli.main import main as pip_entry_point + args = determine_pip_install_arguments() + sys.exit(pip_entry_point(args)) + + +def main(): + tmpdir = None + try: + # Create a temporary working directory + tmpdir = tempfile.mkdtemp() + + # Unpack the zipfile into the temporary directory + pip_zip = os.path.join(tmpdir, "pip.zip") + with open(pip_zip, "wb") as fp: + fp.write(b85decode(DATA.replace(b"\n", b""))) + + # Add the zipfile to sys.path so that we can import it + sys.path.insert(0, pip_zip) + + # Run the bootstrap + bootstrap(tmpdir=tmpdir) + finally: + # Clean up our temporary working directory + if tmpdir: + shutil.rmtree(tmpdir, ignore_errors=True) + + +DATA = b""" +P)h>@6aWAK2mldXPgU=RL(=vD003hF000jF003}la4%n9X>MtBUtcb8c|B0UO2j}6z0X&KUUXrdfmKi +o3SI<3)PuKWDYI?b2HKe+NnQH)&UWdBK*;0e2SD>J!fm}sV{PrY}+lMoZoUh>R=L2FGW*E^2g*Gxwf^ +eK6QMwU`rvP;f5(fmSnUab%i{N{v`lg}FduUKS4YCD6gkCjC>0C$JPe)tF(WNR=ItXWkj_9-D>tT5`t;R-IvY|KAUO9KQH0000801;hJRU;eYzik2l0Imc8 +01p5F0B~t=FJE76VQFq(UoLQYT~onsoG=i*^A#gk~cy`Say>QqE{|@JVt*Pu$xR(`G};)Krbmu>r!mBM?+;$5pWIOpUWWg_f +z&&Iba>oe^#uAua|L+MIMlgJUlQ!IC;zUDN1=XXU-{AygY0^gxyEd!+VMwOk!@Bx3%@ATvIwuFI*@$S +2}_Yo{E6Q_BX=Cwt_Yl&9ewT5IUGk9=Pj!f%PbYWscvNAhK9n!}Rfz@eJzY`c9(2;Yh-20LW;Te0P_p +@~U3CN~-}Yc@bha)u~X*U^o5lxkNF#QtivCrG!GkC@S`1FtF}@pe~CZFeVPmm;sXBDyRGRUHvimbBkf +n$9Y3@X+W^Zo&VK=dLV!rA=8D!+F8ZZGzuM^-Pr{@xkfAOUpKI12#E%uB;fm0t5lt&BFD>e(ZvnAQeh +>DJv3#>}rD!p1WoV`W_up;jKC3t=L*A^lu(TWQq9rx|iO>Ml8CskLT5a?e=}+;3K<`-RF@A9gH?g$l~ +Ez@5Re!OgQ>$M>98)owJS{izfPALucNCG2JJwd!U=<`o0D3y +8tH=Rmu^+*rrErFsAsqWOavxeT-r)QnZCRM422Rt->rnF_0ILHAe@V}p#R9A-u9Vi$AT^_V3~0!sD4K +mfJ?)O^4q?tfepJc!JUvOz~Epl~kZe?;`UoLQYeN@4 +2+b|5h`ztv2FdJ~aq8%_GScU;biuKZDNqQK%LQrhF(Pm4oBxf;-{`*KfPTHWG4v8d_kB^Vf4#RM=#jN +oNxRseNTkvb;ATxCfrhr=23H(uXr$IuliUKWa52!cF8eP#o4T2@76skm^e=RG_K{`;LjtO`}(XO#b$k +O-lmB|~5&ZK_tVMS?GZLFuXjgo=XyE8a1J@z%iFbskd`0(y0bk#O!oidt;R<-nMrAp;n$kv$s28WsFX +I0}m^jYiIbUfb&mzXkA5Dax%$KaQyR>YyOrXmdA)M?!YvmeWE(;G3Le80V(PZltS=punnaS#MK76T9A +yp4AIcm!|)zO8Wt>OmWe0noC9Xf=dsnEogNf$L7I}GwwtW=WjzGv%Ch~BB7@oWOf06i^UKLX%%{^OnP?Xo4gW&$`y)im^ +Uq4@9L!XG-NDY0qS({=p=Z*M{c9IiAvK(L7wpG^0)(qEiyW3k0!34nTp$7FIleKPwqDu?^xfefX5~dL +9J1lv7(((+IC^D-ouwHk*gxRn)Zjt4^uX!B=I&UN`+?TMAJiT4+ew)TM8^q--VPZ%LYzS|vNuT{DY*4 +iX-13AlK%)BvHZKTLNK-vFs9yxV1Ee_CJ@6aWAK2mldXPgTvwz%{@C0015V000aC003}la4&FqE_8WtWn +?9eu};M>3`O^T#obt*`VR~YY;QnfHmzwbC3ciJh5S8E87&>(bBYv517WkANp~bsMyYmG$}2ukNeuDHN +G^#ptMd*~JcpmA56q`#0W5TpB>IYnZ>tlx>JJR-$h|q#9KFT3l$ThGovM`Z`h1@k{0zqrjTIlGh#re* +%w%#go%(3HWDhs}=jz2OtQ*5LjXNW#DIpx4DusYoy!{s5eCbN6)&t+MoumghxP_Ew!2Ru`@Lf_lF*R= +M@&`~$0|XQR000O85nWGJmbGx9Q2_t|Jpup#82|tPaA|NaUukZ1WpZv|Y%gD5X>MtBUtcb8d3BL7PsA +_`h4=gl>sYQ5qze*o5{yhISlAFch1PA2(j?d}sQ(_fa3Cbe66?u%{_@;$9zo|qsRtTMW`#Y8p2B@m$| +*$WzT`s1A1EoZseV$5;wimgqX)reVpI-E)>b%ylwX7E7V|&e9VZ=P?wvaUN~BBO8^yMQaT0}RgeJs@t +yNuSgplS{0nPB$>wDC_mB`!5Y~BZI1{gvqm;{0z$K(#iY?fngr-YQuQ`U0hG;9hpHJtpwD +1o9}#PfrzG!(C2T!P$*b>&}6l&1Um3ko`)aFbeXUQ5!Sl*g}(&T0 +)?MS^_i(9h@ZA+gR(4qzl?-T?P2Gt_Fy6#Db>Df1Vtjk1j(N$w%v)-x41@BxeriF0|XQR000O85nWGJcMxq(UMY8UO$QaA|NaUukZ1WpZv|Y%gMUX> +4R)Wo~vZaCy~Q-E-Tx5r6kzfie$Awj?i?r;a9Sn-AsUY3g_q=h_>Mh6j<5&6*-v0F6_sUElcM8geN9AB3Q4xO0)CzaL2!0f +?%Re-QZ?7tIA(q}!hUP1ys65q{V7Vx`tQe{O0s)-$7y@E`2o*S-RZXr8>+mjXI5QkE&8spMpqm<)+4W7!>dXwzt+D)sXAA%jSU%Q2INO!?tjJ(Qn4yj{M +8*uiJ(IMclDg~)wxeAoGd4g~^Kk~|cy+R@L@K&%%bT2uUc{VMRW=NtUMktX&5DxuSb^Vf!8o~W_%S9p +4YS#8Sr-EQVdsNsaYo=X!yL%jy|E<&`@qkBi%UKFP7R)&p>x6H(t246$Nr$k!Yr%l9xNN3ot@63n04mbFsRa&W`2r@& +88%YcAx8j+&FWVzHs3|Nt%ZA5u*FcQSx!UN@E`4DWWV8KEO6$3p$%NcnbmPG47AhTBC~8JSmh9R>!obPTNsyt9l}Q-Sva~ +%076=(40rCuZc!qh)_^Qx=fK0H4oi-jSG_}fd#FA?nzK9OYcGRcFd30iztH$uQlVk^kn5+n3=@%zbhS +B#&*%H+MC9;=V-hzCk6#$~xi(!CikRQOU}$0iEuSD&dbM0?dHQ$d-XafXl*B3KI60M5~|Q?w=ur=y#S9xAqj+%EISK6R*C&?yCbXunbL5rozS+0z;i1m8`82q$qO4eH +FqC>@F*2em?#ipXq|kY=(_pKzCct_?B8F1>u4zW#Fl;cd3M_z5&pHCuGCOiNfSuXtx8K%WI{+((r4y|yhPDMx=MUl506|IsSC^`(ZPX*v4h~J80e6f@h=}aCK3srck_=2_{rLxTCv +Xf-W)=GL5f-@`>SrJx19r}7m547qKpONFUQ-M+y%ZpGiv-v)nSz%>Mp`>?K*kR|$&&Zoj$Yu}mMn_E9 +xbB|-tONj_q%D9Ja|3`5enN_Ez-%66i$?_T(Oi{yfWg^Fe8RU*2l}ynaMnQcDO3bV=h)e#O}m8MgB?W +ZvmB`(X!p06T}M9_T(yW(W1b=Xj}~!XRoF6eY&C9!5V0h058$EceFwHIy52gaa%F<+SpVsC4(7zeT9A$NFLB~P-VT +uJM@q!Gy>g1x36VH&36}lk755Y;oQ{4O0jP`gOS5F2<_o}4yfHN#)_j!gL!A0N7TVwZb{Tiaj>ON;`a +I^6jKxO36`=le*TbHrhY&3*3$VN0CDA!;`1FJ$z`LBnV6im1m;ix^~h9Wu)d{N2zqRrL#3P^h~lOx#Njw`jy9mXCGrw!wU+&T;g0LS;_g9N;Ei0LLTQv;2nErP!-$1jCmIB3hQ0yE4~1% +_Mql!E{ggEw*umVvBC3?#%dOm`=?(Sojbqei$Kx$7nvm18(5N9h*$5$CxD^C{^|^;WgUjx5TBOX!v2Wx3Rpv% +_xWoVgjEQQXzaM4uyK&7;)_1JkW&W>%4bL&o)})qayZc4dZ9}^5B;~TJP~_tAU$8qfg+EsSXg^L!|4u6vqZmoCH4Ww;o#jh^iv`xjMZ+3Z|n#5fgM;CMzW8?a`pb_#q)~^q2^b^ +?r*go=uWyDK#zNjyw(*HqBg&7e_gPm*`*Rcu^;+{Tr^*zKoz!O;+fmhBut*@LM2H+{xss6a@J +N*zlTAFVkg1O4M#1G1Jhz_IFyi~Jp}0DAz0K1Kgj4ai)XLG$~gEQoOQfHO&ARfW3CN{(HRpN9jt?-ga +Ztu>*t%zv|v-^N2aLIXb$-gi_J7+jdn=X@}nA5rO*s+|lrzx7T{SsuC=a<>%t5+9qUw!y^vC7`8K3zcpt6sgrXt)n!i&5PbW+7S3LG^^Rjl)d~C +fhU5L4A=lt?F_o>N2H*l6qf5{Ek=trsp;M{|djM12-Z6y)|0=1LWX}b=Q8|X@B2EF{dTmJ>0|XQR000O85nWGJGAM|?o(up0?kNBO761SMaA|NaUukZ1WpZv|Y%gPBV +`ybAaCzNYZI9c=5&o`Uu~lH0Dr8=2IBqLIMIGlHyYNd8JMf3Yfmo3%i9U*Cxa7%7(f{6OW|!nHNnNfE +&;}KZv&h|@o%eZWSM&M&xs3Npypg%wse{Vv*?c~q&C)|zRJGVkz0cB3`}j#0dHX#pcDpp+wU33KZL8u +SN?FqiVPF4%M>9~LN=$FxKg&-`@m(6%OYu6@`1`J`(;}DIQoL97%+gBBFuGNFQdD6n;~PmEhPB-3mdp +paTZ%NVRaI6Bf1_L;l>g>dRT`A$HC5IU_>SNFRAqOp+7{J8)=^yKbyajLU+~-K=H(r$voybH*SyB}JEk5ibu3qkp`A_cnU_?9Cf{?rxEj^+XD*mtWwy657@FM!9jZKrk%BqTpDz~SUVTo|ozisxMa<$%YAradJ4v2FiVZ}QF&v!7AB +nOmZqr0*csyl*#9A!H8@QQBWGw|-i`+(oFwt6(00*;#Y`zj7u(m3Sn$%L`I}e5KWZ!95h<#CPK4CLj; +`y%StuCAkJGFZW1b?ISqE)!j7JD2VJ*}fC=*Vi7ZI`YJ*#n1K@)WZi%Y-yZu6xa +7*>&F*xwSa&(v*mhMcD1X=!T$Ug(%C5y5~Z`gqS +^eGwbF@oNAq!|C}4+P)He@&HptNcWRTc$q~eXPj4aC>%kU|Igx!LBQ|F#cwDUmV^Ru~6=znq2NgCpQE +wD@#`lWyTV?!^zQC7cEe?&Y;ohAMJ{4&Ww>qZvzSnZ+SoHZyd{p8n^P1vir*ehNqTs25Y6@O88c*1zs +jQPD5iP|LkzV8v>vp9>?h~<4sYkSs{QIXLUxTAq(sK$9)7_pB!7Z4?Fw#9TJ5qMeled-JF?{IhQP6bT +#S}GiQ#2%Rp`9oLTSkj)M=(EMu_mb^L?l1fL@oK=s_E9u;)G6RK%|;on%t20?uOlw-WJ~8S3a$4DmU$ +$(U!W|wAu31adwXI+?_q|2v5>*jr%H(vnEk4mJtTs9ONfN8%cObb9}Q1hb++<+93p2h>KDgS4y+&(MF +LGk?j&Vs5b7mX;S1rDr?)Vs=$h11mKWKR0-Tf>9;TzFHbN{-tCT8NCO%*s|?E%soW;(2I)Ru4*bG6g{ +fprt=%|QEJtxb6r`IpOY4&$#1}dQ_H|wA)y2gQ5;dC;lP}6sDK7vkE>f+LhcEv8_^(gL9(FY!VnsH{@ +THR~&n&eBokH?qmI)JethO%X;C(`)5@xb;=xG%<73l>P967$G!U!W1 +xo@5>VanRjmsHj&@|(J-Wd;W75@P}M@4Cmn(E`;_8@g%{ikC={x3tW&i?&8P$Q#5L&=}Qk*Fky<5`G| +YNsL(H57LDb@f1|>zqO0TIz=zeWVjso#&XmH6${Aqr`@r9X+~!LbTFdIRf|Pdjc#F%8v%sI+*5W; +j++)GOY9*rf_jW?Lk}Vf0f5x^JLv?$KVJs%Q$h$SO8QbQb|C6Jg$0V1>FYpb+9!>Ot%+hz=GN}U7wlm +vlhyda&r)Lua?ZnNe>W6I<9(ctHdnH6q(^|QNKEr86Q2 +U+r(i5{T5|6L*jMsm3;V?^7D%)|`92fd4M~md}C#?#LAaETGTKwImR8;z +Hh4ux7KW!DxWhyeK$G%PPnH3O-Owurh9M0?L~u(}YMyLuEREq9U-f_oc@l~RG9e9S;MVvZgd*c}1BTlKL^O$UFACAhXsx0`-_ar@gY#2ldVo;}}sF&WObYMprPt>0Ht6?Wcr(p8_T=I91mZv{Nb^Q{0|xdDl)&4Cyq);N`W!?ZZD5Xi!$fVGT`(G@&-~R6Eic8c +o5{rklb-P(;uUpxKD{_8lsO4N?cu0YP(X>?3qlmJRf^_`@(Ww{0m|cYl8|G`$??o{arfiOKc2C;x5`N +fqvS3&0zXP75aUNCcs7)m#|Rk_T&gnP#U|J_xOaEz|$*idW=0AN$c2I^u&vG%)c9_W{2xPR~`ncFX?rgj&M +bZX@t)|;tOe!5MON9Rve +@t^+4d&NoIMD_M@>h=JoMUKmSDL0A!(nco>t`apQjE~Ik?Q+{@pjMcV*AHp&4fCK=|FCDO{XjfaaIM| +pPqkgoLKyflPnWjCm-WClZTm#rN^XD!X+qBYk*oyYsZ=E`4@#5KYn2F$qsfRbydg8QwbRFbv;+(pEY3 +JyU-k^%$(X>Rz8m?7qU+{-X;%9M`hk+3cQqj_i0 +*ul&HQSUM`PKM^@6@gRi%FC=~;Gd&QQ7I97Dmw(qaifw#Q=wy;Ej&9} +v>haMA^&O#6u9`(PIxE~s_7XcB0sUZfs5BwQp9+$#b;={Y|-mUoRttX;>Zc^yf-s|Rf-k_EMFc*LN`k +SXu283ZkMBk21!Q6pPGPF(!vcn!2lNb@6aWAK2mldXPgR!Yd4o0(007xE0015U003}la4%nJZggdGZeeUMV{dL|X=in +EVRUJ4ZZ2?n%^Ur1j9DvPDK*~qjOWx>{Ik>2n%FKVWEy{QI+t8&d*&E;AYWnSLwCW8Z3q-#E4>` +<|-6m`uDCJMG7Kx`>;u3}hpm*yK>^$Gv7RHT7P~P!5{r7wPJn^wbtYaAmDYCo=aaLe^UqiNmxJouIJ~*x^(j07-tOJ87HVOa^FnPTPXMDaeBZ1GgUfWQ>AWBjv8gAqID +E}su&Wh7TBSuc_*oPgY%oF?M)*4#GgZscVDQuV+1rzM$&ZIu$7hG{k6~Se*oi_!Oe`&-jOU8$H+XI|I +6nK7;QFiNeY}MO6)Ts%Vk*>C(AZS+c%YX|iCQi0k5|W+(`W%RrJUKc}&M%LSFM(R3I@W +ZI%`c?een;%>cR0`aiboGV9iE;XC8y_yN6Gow>8}ky7laQS0S*Q^sUj?I)N+_4=xLG!Gy;TncK+db!r +rI5YtBFdG%pD55dWEw#<7FH;ok}I5IoTP^p+d%i +0YxhUm)8gze!~VY$Lr<QvSW +o$oL@fR`Gl0M_ig$s7e+7Ws}m%MTNQ*Uz-ZXKA@=>ucPuNpqRqg)It2(>iNOpU14SPjQNR(PjA9n~ATGFErIlj4a>K +R|VPFf$6@~sv+f{u3@CpzEv837G+s&hRi6X9tIzrjUG8M43>#BzyC8LmWv8r-W4}lM3_GV<*2-=x=9w +VV(J0vDzpqZg38@K#44App>t$GizZr0#fsu*1PD*jg~iXna*v%m|E7!b$s6^g{fh8Vqwg^CY`=nsuf% +?I?yU4jS0x!!3II%8BK@uhnv0mwn`7rVzmbjaXeDiAI@%3<`MXf$pX5PDrF9^>P7Re*+mp^+*0_AyK> +U+`#$FNQ692sA>!Z_an!)TB>#<6tTJunC)mte%JK&%Nn;q +(Vyy-jvb=)`q`>MJh503TA+)P>u>cFe!~pG)0O1cmVKXTuLHe)~ifN=^5d_#;ffs6_A6}e*cZS5I20y +A;Wb;N$`p~c<@H)F($3l3f=v-Bol2iEAS!*X-6_5}CuolRs$iVNx2Ce1Br`XdX7uhri@2kjwQox*hET +9|}ObU;_F5x8n;<_k%ggqLw19ZEfVt0TYT4TX@s&>dtEeJen%?!{UP7*u=n`LR8YWe{fBC8;Xi3MaJM +#!2H^5T%8i8@Ij7tfbtSY77Gp$Q;fLIRsZZkb?Im^4`}`J*|eT4lk>OW~K+o2yMlVX1*U8Z_^~OdE)Z +8G3ZwO_;ZDOq2*B|HSj@dwsCCis*eu;6MpPW>f*?)*g4j)C#j{28jS71Nwgd5LwsXfk!YQ(rW*(POBk +AV8~$ubVsAkPX9^!3wa+~)&pnlSARi^@U%s~U>9v<|SFn}8Du|w}4zVgs@nL&Qcvz +84{(vBf_-dYoig)FJZF2L-tQd6PzVlLfd&}0%$Drhsn7G2T-H--#HuAvo_AvucW+m8doIVAzEDQmGdJ +wf0}T_!w(?4_Zu@ds^^XUM#y=%-yTg;a94D{-^pss=wNN-64H+g>nqyA)z#DeF-09ghL_cgA%Z2Ue?e +X#>AgeWHGh2t(wFS|h3DE^s3{QfqD?v0C;>E~&$AIGX)w-lnQlwzCt66ko{Q^g=@fTNXkHU#pgHwr$| +*6=`%Y5B=X%BL)H1ng<9Q_vgyNz{jxShS+z?vhB1hP2kFdvDrV|u+zngnchSLbYFKO{a{|p&xS(=W~D!t=L;}zWeqStLSZZgYj8mfOh0Uus>B>^Td5--G5natX_j>s4|>*t*oPi +HM&1toZit@wyq=Sh0Sj!S*xj*CI$ZsMo|s^cm?IQ8Q?-BD_@t{{ajo)F~_ +{n(wwEu8|5ZKrw3H24c*2cb(l2fvOum+^%4x9z#ym2>ce$sfccJOVbvC>X~;Bi7XoMTZe%VG3c3Z0OZ +?4^QgajBIv5bQA_1IdO2)*{yY3sfA+7hQW#GPIp3_)gAALk+-Ewp~`dutJEo(FpZzxmVD5;|I_Tpcnd +Ib|J+bFp~;u6cAbiV6N_F5d}`;_6EY=7aV`r2~=$K$BgPa~0xgPnm$M4sc)Z7F +Z*DiQhgQZ{z%NS*A+(=h`Di`i8i#Cuo%q@@R;tSZF`^=CQZ5?&RTjc=K0ffZNcsdIMqZsIJM{^(IHjx +@GPkQ((K?4Z7$kDUC0fg$!zJugG>^&6<0w`Rq@VH;@HQ*$gZ;<9Ed-&*#0~LhEL?XLWTeD!O+@^7nL4 +S+*hd??#ZwDd}IS@MaH;03o)h`2a?t5J4=x@*f*CnnGUl!PbpFy^UMN<+QFZHlHl@eJM@}SSz>MhB+S +W{jIUh-*kVHdD&G9i)yO|)xxyCN`$Yvk-pbGdHqR?$2ZepWtWhSfLnQz&wBMzy<)?rWA##vY={`!W4h +f#{NVMMC^mf1c^l+)gdCnVY=zYmf;6Xo=DgBnu4x#yMTx>%H|y;2+J%iZ$7uy&CmROQe-v6X;iVJ%h6 +lRIxAZR&gMQm2^z3(BUYCjwqqmM%LgtRayMwo{!Pi;?!pG5=h3weFKfI-_)MuNMfmSxv6 +Fuo@KbZso@6Eq@@~Oa0`aZO&P%=!fRhs}4*iqM~nu(WI3LWaSFrLf(hF6b`XE@!+)~>Zt>0(9SQe`DJ +J+8?t0beaE0Z%qHTfpy{H`gT^R8uVPw&`w+{%=+0?Y&3rEIlf9e1$`WvQ3-qiWhCVps$M3J8PP1+;+< +eH3wW_8jcP3AQv&TyD_1?vcz1@C{F17ap(y0r$tHhPR)}T$}xTYE+HP9x5Kv(xr4%{)AMS@5+f6>1zH +%>;6xvq>4kkP1ia-(9RKagUua9OR*bYLMR?mW9zX^Ba{5)WPeAV +Hb0fvpViVEgI8ogXOdDCv!Ut)| +b}OO4DuZOf>fAt~I$`h2yJiGgm(~DbQ|rVTj#_RCg5=)K_c~3svi&O* +pK~|bSD5O-B&$B4Hr)+z;i7z}}E=2(za>wrU>@g^6$5JJzIE2MD6` +Q&c?-in+2<|x)U(9`1Pj@~l5rtt&UtRfB2*;~x4TqV&9L-?44BC5N9#m>hD$;wk(s;o~rb;FbSC6><- +!yQlOhD0Osvn#SBY@4)$M2kMF+U9|_n`akDqVYBpt_3II#gV?qPib9aZt1!I6-W$mhu+!M~S`deCKyu +e#i9I-aD1=o_nX_d6|3v9<62vZ{}YS2D=v{VWn-mB$H4Lda`mF#IY)c`h&yL!A*-J!R~V +J&hhcvjQ%yY!_xun=HLmox6MeQKML>gbs633BN7_?Op@46=RXYZ!_kdzV?GE^v9>Jne4d#&-Yx6ccWNl@f`2lY +4VfsC58m*Sl+EKipk!E(*t$DRLw+rbvcE+N&!FDEbVy=xg*H`X+gjo{yQ~@XOY2f&#Zjviji6nRCvZI +o~7C^G-j8GB4vSRiaWd66Lzcs?|EHN|6WQO|X*w(b2^^R$`MyRU*af5AQF;o3|IDEViP|M7+tfq7=&_ ++lV0Dmg_iOiMtqI#dRL%{ya`gS)@U74a1MJToyqoeL*ncKlw42VTmheVj(rSU;!%`kWYdn5z9D{%D^q +x@<>$+jUbGPCh%RQCPD$`NhH#&6p_qj8i|aCZ~P7Ju~BQ?E^_FOAG_WQ~o$HZ+Lx$VA4ny0IW*N0Z^NuxT$gjNvbf&h +gL1+$I?;{?}5Y1CL3l)0{&?BI#BB*UYPOom+-lM+GOP`+=d`Is-HJx$=}E_h=MXOUY6l{RCxb(8BG6r_0?#~uj4QO!2@X>;|V}veUqgURwVd&A(!#TIm$Yq838UM0tzdU>|7 +$FC%Ed>gdXUvpV>{PZ@=UhhDJ#87YtH=L3krP#v3*-R>Yb#1kxt-Xw1*>kGA_*DEb0~RCXR|T!6f>T9#RB#mMM_8<<&#p;Q=i2Ftg6a5v_Q<88pcV=Gf`m$ +CGL&_id(lDtaNzj5QidoMQiX^9j0F_;$l70 +fy0u*;Crc-gZ1_dI5LK+Og2B6H7b|=(;LcN&A64n9$C=hVcVFt+XHjyr4;6G3cIl^M40?cC&VA1l5XO +Nl!>!fnP?Bf*8gPbm^I05;(_RUTiq1D9Gf#L7yl)}-B%lW*PMJ1Ev#D4m+etQD~IunnzM!WO~ACOk>E ++|sK#k+b82giJEr#UMd<8g^+9>$`!s0V8180~=Pn$GQ2Jw$JY1F1dM8I0Qn$82-paNN%ahj);MO}M#_ +Fj7qTvDPq;#A&K3ke2`?2K|VdMY!+q-EADn$hQ+fn>9u>!I8WwI9Mebrl3SkgOsdO4!-^3sI_9th}dy`3vMb^qH9Fsr#Su#Eu2Q|d=xQQ6JMGST#RaDnq$o2IyREje5_@Mb$d +xRzVY1dKa8;Nn?tNb!4Uht1qKBI9^;Giq=Ic8jeUFCmfk<_X!v|iN_ReocWJPZ^Z9-zc1@c1-+5#A#D +e2i01O;N;sruCbB-HXr%_`=?^|xccf;!9;rX|jq&w%pDpPV%gtwJw@0~NV +jU@yLOYd^WaQ5K3CDGN+!q{IvxtfVf=i6GS;E~sYq-~Ro(PoogNJ&3}lv;A9W{>>ZeUBa0=Xe&K&KVe;>m>?I=-zx2%H}Jp()H +uE^tLCgBz%I88+8_qKnqe4p-{gb#wF$Dz?ZF+jySV`_LwvwG5FwHB>)E(^SMjekVBDeeU?v?-rv>>rb +cS09S%^pk^O;0;s3gDTVJYZ-x%6I<54^Kr_MT2UoP=csArys<3clg^@+l>)s)8c9q4@HJOfkw3(uA)V +>4fe~H)Pdmk57acRRxDo^<)<)IRC!|Y?TfHejO_XBhI(yMfS4{%kME{(W1I-V()?&lbd&InTmX +y^K`$=HVOLv;+lfLsoFwRxw)P|OkKi$HovC)94>V2cY{N8L^h1w)WV;-)2C*-*Aqi>-0|haq+IKiQd6 +Qj?r=k_j7Pcjo`Rt3FeQX9#g21$BMpAMZ;7?>Ffmr^o;;5tYm;n6eg!C1w?Ps|ZEj+k*8eH(>Rd{mT) +G^&5}fA6)H1V9yFzIclv%MA*OucD`fU9s%yE-~+Vk~n9srP3s**OTU`fFA9*7nQM6k$i!Qmoa#kt>NO +YI;NW4!lLiu2QR@$J|DD3)Lf5N0IH0B8ntlFKk&#vw_q?>YF5o#RGK`+Wu2*^EyUSUu}^6IjfQ2P#Yd +j-+>hVJL5?H@jlI^qSlUS>G|$`~L^>iTm|hC)S7bwkwH2h#Q0_HxO>}{jn0>>(h&qXD1gY9y(3l`42C +qXK$Xr_1t8gidQM1B=wfqyk5ASFnlDyg)0Efc%v}KxejjGH6)#3iUFv8#E3&M5JyB+TwgzjZ{K9)^Q= +lEqUQRVjF$|eF)RC+Gup`KfNXEF6{MVCCD`t$!UR&uc~{sRUA@`_Tg=Nu6_1kz=`memIp{`!$hF1L-G~01Fn^vjJ& +qZeIH_$|7o^a#3@lSNY=xMEaVb*8cgU!cwAhgTU=fC23Mai}BF4xvn38%(e3JUQ8Cb&stybEwdxJ4IRpXb +E~w@0yP-*I1FpQ$T~LkoxGE +FsLtvTrM!=0s8P$>iL=cbPKZ;weqyVEp=evH4j@1fL{;wyaR<+lwqj!)76ElpMyHQ@kO-99T4FRfggV +&*;hLTaMQ*o|KGt*SuuV~#5^$~{qk)#+NBG(bLG&Yf{Y0^B7n$E#mw9JV-BYfjAu_uf-Tkan&b5t{y +Bc+>`(l7=We9=pY62Ri-db_JlB*yzF4glOPO3uQo1Z$ZJ-LEp7ri$5QHB*c2G`){R=J;Vjf`CPr=${E +cd7?Xm%Q?@iqXE>oUP2F2i>7$`<68 +I?L~g~RMNP&9Pqwc=mZ>lYTezRbjt^BWT-3vh%>N*m-0{pxyX9(RDOiGFq$GMQ+ED7tLsWTz_14@EaJ +R2M75s?3(dnU0;>dD;`AiYKFrZ`R*wINeZ17CbT#Pu#mmOz{+=T}0?6Iu=)2UpyS6C!-hHLgHk@wi6C +wL3PIK<)9WA+Zd$|MEK5{V~?*(I=849j0pIrTvdfhVnR~lg*_%@&njcy?Ej*f@XJ_T;{uHoL4vl>qQVq>u1pn$| +Ka}Iv(R2~7FvS6z$qn?*L7WI3a$;Tz>^|i?d$Qafj9(;j!}^5O#SmrVRvT9jgQ;_@^KC2oFg(QQ!@U1 +wXKy!z!d|deVS;weCHGxVkBgHy^Eqj)(LI`RSPeGIgBtX7WnoWQC4QcM>oa|Wiiyw9y+jaeKi5JUn!r +ldmWSybvXRZAjv-@wpsm!y|1WIgC&{4QW>Igmu-mMq4DUT~ZU4R~;&qjjaSqJ2!-eV0hZrHlR215+Ok +mP@NgbmY>tRFjU{E&X8*{N@Mt4;%J-eWX$&Smb8d#9yof_?Y(IoIg>*4o<_w{W0bTDJtE)g1iFcpwIx +{=%CB-kvXK!8G)epW=XsEr9SCj>W4*JS3|avtIZo7CLtaRPW4@J^|uNRFh$E_6~aN*8;6eE +QzP;2UUQ0AhR$yon@)jHrivae`MTZUBI>+kARSDgy^XiB-ikri&rOB;Vm03LTd6l+KmnXQWscjPfMdY +Mq&97(vEV*aYLab(~;(|Bs4#pfPP1`7FjoC?dzoUpFY{EJFz%$BIlsY26Tezt6E(wkEU$qPW^tHz_Ht ++yF-yHgQUCk14lk;ovo~kPEc8Aazuwl|hlj_J)iTVf^Cw?|n%Hlgy>z$UU^wEYw;eQ5=uO^y~Uy(LwI +lRhTX3V})K9cp(LfCm5Q>j#SvtO!Vvh@@pT8xCW~PAli60Eg?MFj41q!_j%%!uIIQe78hh|Ch?Y_EI7 +#+XB}PNeQ(t=~-G +uMZLdDI|c>k%@L*pbO>-RsU{DF0I16F6Lhj+ZYAB{{{>#S_#f!ofA1A{Zz&Hgo7TS7ITwAw6Wq3&g_& +0PbGvF&KYJQ+96S1BoNqy1_R=EqQ>d2kZUBWo_)8~`csyP&?DqbsC3RXIgx5L_A4a)d?Q$YmNOuL#_o +1eJ{n8yi0W0{B_$jKSd|+&z{lr=Uy+7|!k=+{0b_wOD}#aN1n4MKWD8sZ +R*f51%QWsEMR-waUtl8M9Xiy9hw<6>&s=BAo@yv)rhSRxR|J4BksN6x!vo2K0P;-#tV!kGpaic +Wl{8Xy|X3`j}XNJrxUZ)5a$?$RU+kPAT(uH##H;=r+ePwOB8_aut6=WSY86nS@Bymiy5&r?U1yLW}WC +6yO!aCW?g938x1{zRpqxw6h{ert|3_Pvwz +#mPNp%FfPE01}~5A!PoWWo~!d!i4nKnCw*`J`N@xO-<@5Y&R?Fqe_7iavj@k`lCWc8+P9(3xYmWY8Y3?w_oa!i5YZmUiY%GN`n0ivphCoc<)-x%U +U&C2JScDuM-%u$UW)|b6EJmNa88+6BP?32|x!b`ozD|fno+A)_5zr%Hya91klxW4Ifk22gp-}dfD$xB +j;fyOM9o*Cm#h>l{X=YXeWBjI}^v^``sS214`TRZa*7pcrzTumcq8TGHFWZq;qcEERSPQKIovY-=es- +15~BfD|P1?>3Xs_lue@O5x5xG23!KAjdj=F{BgkEoDJHRR>f8EePJ@`toR*8sFzg+F|riL#~7`lg6De +ikd@`1vglz7y*AoO>6o!E#HDKXGYEoqdpWo?>A(K|QnS0AFq45>x;7d)Up%MAi9(XGx+~&R@=qwB4eb +v*xka$O` +qSL{>z86cc-sU-(0+RH|_b^t5aWS`$_v+8HbXQ0!g#42W$!HYpfM!V}OYi)G!^VHe(9(}^$Y@4Clf%Ci;FQ?VqAB;8M?V+iT^(kRE$|ai)**RL +L9zmcS*|xEVsHe|!PIqK^sW>SqIu0EwEPG_R`}aIJ!W4Wx`sZqEqia>u2trie#xogM1iVQj!r|`OE0& +JGS(n|ZTyYOQ|u$CgPen=7M?+rTx;K!Jm8GMxD*)|>gE6&zZ-eW47V()c$YC|hwmm +rN#)+&?LAc>S%w|Uow$l?mQLTe-zWp;q0AVB#{ikE|+&@qmUw`w*KYe@XKfS`cU^|;VyXeOILINy|#b$9Ebwz%I1bImV|5qNvcT+{CNrh3Ko6Jw%$qdn7|ciK?^WkC +RKsSSqNDvZK*)}V6QLzPKs*!73Rl-5W~@>MgGeC9M{NW&U<60BB@LGR;@J42qwbhdlsnRiwu!TE6xU5XWVUJwqN9J^%%gY|$%Rj;m#6`j?+#wSPYc6%&0xaS@ +cu3fq4>1RCOLU5bvuI@wo8grUd->(QmFw0uwnwxb~hHQ)*fvL-rjizhoo-8(OKE+ +>?dy|#l%U2YRjkflU|#T*ay>1$%kfpO_;=R2d`&q&u$LXVw!*H;gQ1QY-O00;mPT~Adf0Hdeq +0000~0RR9M0001RX>c!JX>N37a&BR4FKuCIZZ2?nJ&?g_!!Qhn?|urA(+Zt^8Egw|$DPJ@*{zh~CQ2f +3Y#}KddHcC3tq@2^;@8JNNSVP_raS`8T*Tm$)b{YrMkUAOoa=FbIZ}RzGHQF@94?0kH8~#P4Zcdo9X! +4RWosSOXqx6{B88ePs3^bK!%zfD>Y*!HOG402h)uz!X!XeoYLpV35d;Sm%v~khP8MJf%P)h>@6aWAK2mldXPgRi3-7b|0005#J000^Q003}la4%nJZggdGZeeUMaCvZYZ)#;@ +bS`jt)mh(f+&B(?pT9!qK4c8cI4y98%MCDl*u{2n0lIs^w!l3E!9Zin88sfsC&@_%MgR9BDan@RN7~m +b3M95kilWGm9~HUpwQ)?+k4|o@!O3psADOnZ*62N}b=Qg7Nv&*))zp-pS_A>C=KG59w-BIbpzqN_ +?qBpY38kWAN`*S;xDMBwbW&*Q44G=ZR_-oavVU<)Cg7z(M+N~)HS +F&F9YX=ITMNp@f3iQc5~Gpq-(GN)nAvfwt{f9z`cBr;bq8j9QOj0}rWy_8fdNlG=$dMU>DF(`f4;Kkw +l|uIp?uW<2~{AJsm8E_>L(DVUxR9b4eUi+8Ic5n&7myL#X(?^gDPSG$d&#C{-k72!jEBOvIV=$?)1<# +7&Kb76OX(;04_GL9^~GFX?Sg3Bz))0!(y~D&pgPm%wtp*vqdHycNz~l7S(&HN>3vVI +8Fvz&=lgJD`ZElN+%!GQDMk@l-RA?-B+BoEr9&sFXy@P&d^YFAp$#QYBo)L(-&jM{EX~2xuZc4v077_ +h~jGZbQkR(Erw3spTJXgz6h4SiDJmzG`061%<-O(Bdyzwo`PYGf;9{5T7$WOd2zQ+9h#u_awS>t^#xW +S!oF=g%!ZU^-c)?BgT$b=FS6O3KO0=WV!@R%ph74(5YHC2%WFK#T`)U<^cu*-BW)KTabkN}Y0kS6P5a +P9J@fEEN9yUHBvdyffg-^42fUOR80WAkQSmZL1C)W#Axf^bl)9)+71^ljm88YIc$N)FKT8fS1`|n%f~b*v+W}e_fF; +$+M}kmThJbe73}cxgT08-&1S`qFFGIh_fPgc|n;v18Kq-Jn0XTcRgYnpn;8#{>)9baG0of$_(p +SS6dVENlbdGaBQf-74ze)?Sz-^p%M_f{RW_Mg>q#L&|R_cyFV}-`T%T=ZnPd2q?pUfaaCc}QS1jb#4o +R5qKBb>_o{{<_u1`C1m?ISh;zk!E)O`M`dTLGruu$^aC +(Yn9CE8x!sLLNJ1-`uiZVv{XJj;py)g8^a0!Rascv%Q7^6;N`-Z*u{39)g(@Z8Zp)^ZddSb)+JA?(EM`j3Aj#x05 +@YMqS3ldYR#7t6x*bbLzvrWWqeGlbZi+P1W#MNO`9sw#qmmf&*m=hClhQr=7{-kW8rsoA3*hbw!D+hP +PegLc$G+-rWoZ*f1Q(~ac)0`u|h^o?$u+V^iV}`C*41lRG?(ReOmQDZpOCpksLfku*vK?;*VH(=uR?RTlNHWc%lLkGp4lh@Op{G +crNnlUD@2<_K9^Il|~pQ+CmK*RvaA)G2?VOM)lwf7`NfpDoCwa&Lu{FA5w@szMYFw<>{dJ%7tLgtd6& +HJh5}D)w;NK7dKKRg)+s0*SqY-gJzvA4GNOlH?&cI=5KFEr`0A?fHR8Qn|9~SAt#T?Me01!p4gVZNj- +B3i$2ip%6T^W!}0rm43jvv7)Z!{$AQZD$;AgG@qNXEVNX_u-B(pC?f|vqsn}Q+c|JJg +=18hT!dH;IcH8uIRS>9uEHjbnnOfj82^C$hQd;HL^i+>fz^*f)C)=)Wr_$g)0~6O4^{#Hn@uYf;5@P= +oO_|0411wcSmXxaJI8K=ubVKI$0P=>@#>_o7mcuSTH-zn?(guMg)gI$M9cbcji=Z?t(tV)X1c;8yn^td31CbGX;8D*4P7?1-w2_YzNmKNf8uyN4ceQdaJ8nWiVP +;JC2}Hjmr-?e^Oe0&G&>)@pp`6ZZs`2j=mr=O(0001RX>c!JX>N37a&BR4FLPyVW?yf0bYx+4Wn^DtXk}w-E^v9JS!<8u +wiW&EU%`4Yh#Yu?A_&^TsMBuu(FEOO7nwXTlStvb+^Jg> +OcLNnQO;g;MEb80|0ynJ4t*6anZD!!}4n!S{{S+lo|k*em^!kuVjlV%U1E>w$4^LtK=r4ORj*cAv_?i +knZOXlC66KG|=PjgjkRf%x+pRMYeC$=7Vd*3x-r_ecX#1mVMcr;<-HNKD8m$eZsL`r2{h(|C`RRXqX! +~Qq2zJC!GpFZcr7gI{_DpRwy?_NrblR!?7F%1R)Rj5vhbdGVg-;3_ +kRCZpGDmhVM=pHb{(>R5|*&05h^*!48sgVcHNz_spLkrlFCEV7r+uU}=azrSW%w!HZF<;CIhqPV{Jc6 +0Il=Hlu<%f(_rC;&g(1~^C|;~Tq{WLeD*BFk3T247+siLVVHhZwallY4R6Fu2N!U4BFVH&#zr&2}dPg +xJz|s6B_HBrE%p&SBjnQ>yp;tFOLJ`Xj4!FCGhd2Q~WxtuS3%+jHQ=0u8xV`GOR6Mf|v1Ess!*G+CCw +B}?d%Lz|?Ffyp0K?f27%wxv#}jm4%L>{}CP-GavtALlk&>B&CWLDUxgrf2NBJu#!8OkOav7fd`NkO&R +!$$q0ON38X$zjBX9C`MOCn%hXt|!Xn7shnUPV;mCj`mn?2@AdC#n&3GRNbZStCQ#?Xo +lFq0IlYOr5R$S}N~%lxV=-UiF&7O&_U+ZHwpEb!XI^!}fB%;_*>9`!tDdn}k4+_WX{r;`T? +3Q?uD~jeAi`L$85VNEj)E1c{=%>$uMJE&Lrc&tOm&9LNJh5mXT&a(z?J%eSF%XYPBxSw?dso~7K&`ms +rOqL#HuxpBl>vn2W+GSuJUowAA)s*pLcC*Q3SQXrmqUp@WoOMIhQ>AcA&ppE^Q!@Fc5yvACP2Ot;{7P +BmxBsiI_}-M_#pp7pD{_#}17nQ8-2Ndnw{@!J#Fh*<{XNEd^Qs4k8UIj!+Q%PBK~`(PsreX&^|Z1qz6 +Q;Oo!qOZL^*`17Yf+CSr9e<^^%2U!cRyIMdyh<=y}4z{LCG5qsAAUi-k`DcG%Lnm~vLA^uS@|hBjLf5B{50T6;dYxBYA*@iqSEdz{<_paP*EUHT0p^Z-6!uh9v16-@-8>;fcgA4 +81hwUcg-p7RUyj0c6FDn|cWJ|Wh~dfEC?GbG_YXTi4x-zJ#3#<8J2cv5P9!swf*E+z+v~QYXMwJX!vK +1lEY~OvmYd~Rhc>yN6{GX2r@UUq4pyeeRvM8IKYg_~B7as;JY!cO-G*?q$?Z}%BA2Diy;WUFe54#(v- +h1g6DkVYu~);8*#AH+lO`j!I9973lC3?`Nl@6TIw@1~vh{3a51{Bv4_|pqh4}}nRVGUG0w%u1<^09ph +Zfn@uRk?+Et2UwRP>E;)KpbigmXo*IJ$>XWlJrnuWT*I*`{_g1`OZ-#N5HRv;0uNP0Hnm&*)&1)kk(| +`-~yIqsbFbQTHxsY%g*^?Z1D+ip$9y2O8fStDA2H312^|~Lw~hXs&cLpDoc&n6g +H<~BT-;I1B(Yv;PZguo!?Q!i`tsV7Tv7mxkHDDx^K7rSOw{+#y=!6- +2wK&N>P^#rR{4`sFtUJZx?lF-MoL#j8c+SYTt|oC8UU`WwjK*9SK7viOrBp(lgs`?b|dk+m4&;kTqIy +Ku#hZhb-Xy!4FC{qY=rP26)uSZ2C!FYP%!7h?2Ry1$?;T)w5^51QLF9>js^Bf!TlsoTN0J5@3Ud +RwT)+6smtwy=X!1jXY!nG{r}-bXw9-(Wp*6qve&|Upkx0X;U`q4j@gOV_oCRo=71+k?|t*8yE_^nc$c +A057Kc@z?}+0rCy`)1Zz7xyc%RWzkfY>hrdM2qlxyLroE`JUT)uwrS#LkZET`5&Blh<#*ZVl#=>nU&q +2TR*8(|mZ6OO+R5?yk`j^Eo6YI#UG+uQPSWXEc2;!Q2T#|;?z_dwxER2Br2^3*z2nAPTWtF;qW>^RLXgEJZW#v^&6UMXJ +BTVyI6nri}1oyoLRBqU-_CkhP+%_NRjW+#j}+gVE~|xPaw(rp_dfq9nndErCO9%{5hP+c^(gOmyhcgL +HllAjxk^rjzMHKRiRh3pM$2%svfcB$_7FBBtXNiV{Y@Gr36u_2_7Ff4X}0WA@_ti*H|LFTeYd`d?5|r5jlKK_Zb;eg&?i^Hw&- +&PC!vXmHfuUJ{KW$h&?_Z9XGJ{i64HBrEa;T>ptQ5P{h?P6J6lDi761+OJGX@iE$M&u4X=$!m3-_8XC +lZevkHHj7W34_3WO+kX8#uY77WL +LaU`)$hY-D9}E^v9>8 +fkCaM)tdY#e_jo5~bOu=&>t61)4Nj1W218PP-qx1~V4N5^IWNIiwx6=zrh$-rPK7w_EH2D#jMlm$mG9RlOCz3}q({qV8q3ilS(>Di3Yb^{i@kJJ@7 +?HZp5@zc)WSVZO=z(3jPUfcD20&(Sl_06xE}M9N$WeOnpY`%H?);lsoibaX8EnCi>8 +CKwl^Hl&k(vaAh*13>T=7g@>h}9`~VPxpclVj4+h9)oJ7YaG`E@bH!DQji7nw{w#O?5pI3CM1;u4ke_u)ono7)mLwYOHrL{jaPs^Hj#}`ev74ls +4L)$k^CH10ol`P|=E!^N!)&-D|!|3vsLn*gry#Uqi1gKn>vOSmk{JKO7FQw+E=9Vay!AXm6PLeR75Bx +^t-sH9tyv}*IlP6#eS8dk-6Fg^8Rk<5p-U^n~A{}PqJNW$_h;$1cbXFnMwxM&534LSUH#v;WZMfaqmG +E`7Le`6pZH$-Az=U7XSe%nz%kyNl%EfL6Fs_@t6R@@u*|s^fz~#7$;tPKE>t| +{GO$m%hhUCh-;Q_o4V(qB$mopt{J$mgq?juC$359Atrq%fQ^T;78t?$JunXb!=l1HkXxj=lkLby8&PdA7?SL^fxiXHU#w^tS`u>uSOx_N4$C%OSkRGD!>>a^DQ#Lr8yPz4@ +2*jB`*nTnnKc&YHlhQHUbjoBRqzWF}3FyjT|8C5YjNrUvgY<=IzI|%B%LJWNib?OVxMa<`z7QBt!5Z!NlRf>w-0Pb<_}Ihtr0TO)`gW(-WPt86bY34_u$74;P! +L>r;3q=t!*zZ=SzlUw!o%v$5M(jqw3=zDj~*-X6hQ{|uhj#`ZV~4j-h@Im850$2KmdzyL6|DhN(QG8& +D~g2}=dj!Jy`K7I5r%zkPlbi3#EPAEZuIt?{Q1CS+a1r~VNq`3SrNU(%THZXH&ZiNR`lTBIr`?E7G#I +uG>|9u7~TY&7+nK_ImRVkzg3>MYR*%hS&?-?A2sPa0HBLiQ6eiDpzsM&i8u~~aG>hnGe;q9vCin}v$A +B>WW?Vvj10G17$fVhaq7G2Z&#CcF%F;ez>4+D?+9u_EZm6w@x0-P}BJ>dENcs6v8>4G9mlb1Ldsd>@V +AM3=C>0~l+d;lrXHJ1kcNup-b!4rf8wm=ce4^TM>LsO_d#XTWw6j(I$FzCVN)N_0rJUdxB+)(_m<8g9 +goHfCV +iSi%^{)G^Lw_Sc-mfI8aKo7ybAcBWM5iO|sNmyE}7KjloIF$1Gm_r&EFNsv2hR|JEiizV|vs +Jepsayx-CauZX6c8xbC{hUB!Aeq%ST|HNR9sr_&Px)<^D)pn0-3N!n-L10?sHJ`*?wbKAVoAsUVNyWG_moB9?j2YVPDB|n|G@h=#0MCtP<8c_A`4?DaACe_8IPKqK3CEa#SR?xeS1_9 +_&>%G)8?J$q@Ikuy&*^aMRyj~|DMGj!pL8P*qFtpV{B;10nY)Ilvp#KH7r`#1-XDzdq@1Za~(%IQDxt +2IA^+-u2sM9jt<87EWk~#j_1+PzJc26qo%zFRqg=Lb)@Gfn5@}v^U!~f?xPMsCrgZC-#F}}C)wvwLe5 +bY6qN8nwH~OYy=R!{)a2k$BMe0hj$xQUL#`{S-u~i?&zOZi(j%}f(j#4y%&Al2sgQP(DrrX_d4KGK45 +1x*%G}Tf)u8gSexFN&R5v4Q)gy%Emugm(n~T%7@1OyOnKQ1l0hU^otf_TYYr26d9H?gCwKiDRT)VJp8 +Z_XsQRh+EW)932BHuW=@~$th`4$~oWblh{n@vwVV3<2TjeD6z){ft^lA(mPyI@8ihqS;o6R;tdT%Whm=6Qk~a`Q&dpJYrJ +-4_AsJ>RU&$iv+-Ir<_<(mW9Bz@tB6x)5|nezI3E*DVt>F*5197(XO>x+jW6J@iR&<{8Ym6O^Y{v*u7 +vflK+ejkl1@q<|VS7fGfE>Sv&bMuSyt8)BEiRm^g68QJlZ+7!r +THJ@>319dN;IckZ1V{m&D?eKJkogVX5Mj{j~4A_D((68Q{CCjtJM5B5jPVexe@M-&nN +r~H`mVv!H3ojc{jJv^Z9citWeB&`cZW;WQC3#}e%eAgn1+>I~MAG(jtDELj52S&UJBj;rCy?oVIIQR* +}sdpbU$7x+!pDLAuU4@Xn5s%sQb5qu_p9VP4;pRygcX(hoFJ%y$cLdtLc34D&WJM37GQ9Hf)};=~>6V +jsnfQsGb%JBLs9HZN(64PSy8(|Vp?2MTRKW54^Dn>JOcFlxIc=GaN%QlH7pL}82Xrq!6y**Gj+6U5&z +Sz`+_tf;TI)P!oGFK7QNm)G9C|mEb3HeYvu2Vi4~^3y$|l*qD3fA+g4=C^U_!zTo?~$ +9^WSkNqCep?U-IlIx+$e5*6f=cSZIKeEA$PJ1&-T|-B)koM~b9WwytTWgCc-RRQBz54;HTYeN2+~X{{ +&<8j`?WO`Pd^^;r1k7Gk(y?0HUcxcc;~2d^3iZfgpPEbi9F%suiCPdOsAhN|oV2}DfpX@CU?LXVdLL{ +2q=FP!T*lE0(pz;FlB-L%vRk6mJIrsIhtFW_4b9p<0iAs;q!RR{_Bl@eg{=hAG4CzYsA-@c$-^x8!Z+pQ3V?#2@^?&b0Q13)PEVfRli&a@FbzK&&aahbwDQ-~{y?B$-!Ci-pnkNlY`BDYOdsr +ICLKP%+Ovw>PfD#F@kH|~QU=(l}LlN1r^k1YtdBsb=p92IBo0iu8DuH;O-g;GhHG9EcKbOIfNrrB&*K +wz;zo0JRwmBZ)~$gAdQFpNKHg1Wn#N&C$IAehOZJ!LwjwEvzg?Y^9E{)Ln6Q<>Y*7O!*N^Ba}m%5gsD +|FS6^Qa+8QkJ($XZ91igMRQkI4KEm!cRP@fzN8MXSv|mrk=a8Reak7MJ8!aKH}HKif}e9|;Uz+Rwl=&^Y0y>*S9;FGyr3^q1+U4siuVs7ts?cikN=mye02wX4#>l +`xPwop9MMdSm@+R&2(o3#T4O#{PY`hZXMQsa$Br%z(*G>?zY)nZ7_^kcbxnkq)dr(VoC)zP7s3yu{(N +rc!JX>N37a&BR4FJo+JFJE72ZfSI1UoLQYC5$l+10f6qdtR|3MIMxVzz61Hov{)4iu^u +EX=g^0bAH{&pmNd4C~iSwVkbxy-Y{f5*XF&GRj3$4EO<65jLAyDb?eG0=bX|5?~g1r(SK(lZhQ4bbzU +$JNFP1}>pr^ITY04)P)h>@6aWAK2mldXPgOvUU!?{J000yk001KZ003}la4%nJZggdGZeeUMV{BBEp^4HNsQM5%HmQ@gJ9W~OYXa!{#A3;3;8KHPo2OGqO$Lx$;SMxo`nQ3*TK3?f=Y@%??3+bDcR<#t+`IK&70MBkN^QNIh_doic-loGb;g_Vb7|Q5?}>EyXD5} +xg~F`T6V5jtI7Lj$If?B3OOf=F^nTFaMm+1liIxp#L>7Kf-;RF#jbMBfEAnV!ntFR94RUqdSa_Sv^Pr +2El6TKn3d>%V-uimZ5&JhANi@^WW)T-PM{h^q9u=D?w83qy%|0l#)`Ny!7v1_Q_!-C)0$LNfmt_4R<} +2yRtP|lid=IP6k +g(sgW@c4O!d+Q^Uj8KW9E`EAbmShj#z@gO_qJ2mQWF6zWCs*7z#_WY<_-Q3_Brcws9p=VM0gaa`@>3d +V_}&P|1ohF|E6TnSyk_gzGBm!W`iREk&FC*AR^X^g-IQRv)NUy81NX#Xg#{w(~*gR6dn +>h9CK}MZ-?Bv-r5N^uEw5+`5dW?{A=BpN%^Ng9v(j4efm`V=kCM9d`Q6B05{)cQOqJxT1vL`M&1q^D)bBmC+j5J{J1rSox?07D>7W$Lk|p_KJ^KX2Mj&u;jo-w=O| +w)+yMhtrjXI|3JnMH`g1j2!qA^ER7w8sNKf>EdV5QW!0k=P}ZY(NvtvcAw>REFSu5C@*Icdtx6}jmmM +ZWR(_@GXqpvjt*ItP&WjM%JOFv{-rFJ_#4FGCjr#q4;l&oLOvO3i(}r91pxFSg^V=BbP)ys$}P!{S;S +68*>=ncwo~%btFg)goXl#nb_*Z{M%Ro@PI9xp8LkmUo{zjbLQ(zyj+-0kpk`PF^}X4WA#y|G8RfgNn^p`oZRl!{|iRP*3mb!B1O)n|O|M4zUi2ots<0ot|Ua!AO`n`_WN7hf@ +MnX`)T<3B8d>4)nd|^5}hs_H6TyqJ-q<`huylXDog?SB#y21Wg;NRd-s7I|Q#on?~;Ni3^kxVU@0+reyxXN-WwrU +QO6+|RG_2*Mq&Ou{_+JoK!`l%->xe1N39Qt=h%Au0)(P7&8ScgU8Gl))t5jn9svdVU-EBnf@niy|D`8 +EU`oW8NOwa3roEv_&%aQu|;c6*%}>}ld|-LXW>4IwA2zl{?r(P-XuLxW|Mn~>EKSuFE~)emE`@>a!0L +{%6bVwSoP0X!fwkYFD^nMNH8< +W*i(f`PQdklyC2^6)?nynXOc8d@b#(0}`rahpqbC%^#L}1JL%oVfusZkn +;S{}M4p2z9!t;fVNfdIMm$of~oU9@-%&Rm8?2{z=FOSUXd(RaX(4FIb#Zuc=Nh{zA7>5VP(dFjV5OS9 +fRT?v)rrY?#+J6o!?e~jOQmGn{JciQXA=HEwc@n{=<=np7P0#dZXLdF5*lLJZ-Gn|HdE^+*0asS^5J6 +K|v8qo-O!sn{g&-tVEab%G)@AJmv-rZinaVY+^UbmYvi()cvzETp^mY*|lods7Jcj +R%vF{00-vWgDZk9}*wIFHpxED3OXV8nUnv>HQZ6mnlN+(~Wzk9GVNdADY2AO<|hGdPdEP>rWv5cIlh@TINcozYz3Muy{KQoL|h?*`z4`Y&)LSLo?$!m)3>CSYx#O9KQH0000801;hJRnI3 +!o0JIv0HPiM03iSX0B~t=FJEbHbY*gGVQepBY-ulIVRL0)V{dJ3VQyqDaCyyHTW{kw7Jm1yAQTj17mk +2^4c5DzOfs#}sS{+J=>kDO&=PHPM;5iD6vtcizwdWQQ5RdD&g?@`AxI?h@Z7IV6h+s3gb|Oz%k_ +l_4j9!h*L@DuCSEb6OjiP8YQoBN%lC7&eDYedQSjhBdqw>wsXPoGKt(sL`l!Y)>Hs8&0duU!uBNDk1$ +>-79=v{M$)!Uy$R!Qsk%0mGnVdrgrn6h^w%fu>^Q}#jGa?0+CQt4b|Q?{&%Opg4)LKQK8k$I|3Y*eyk +$F!NSj5c`7P3NS_OKE^KPBImaCc9KSTJ{Vebm3IYp_5X+mf>Kn`FDQx7xg;N&tY(iFzrFotrzg#?M&H +YkqQ`Vb0gQH%1Zl_%!>Eg>_iDP=g`_xl$8Zvsk8z4c*OABK0Mq#Ox^4K!|kW}-Q!1o{W$+{!*AvfytD +hb_<3>n>!Q2#Y5uT$eEZ>c@rf_)mi+zQ$uTaURq#WyGkA|7(dQ&u9A`AfiQsvK6^@G4KG;qCr +t#5pXHxV16ox!6h0C!^8GCCK23hirIcZJ7SB- +=?|EZuuN$JB^emPr5zXcy1Pkv{=5i`F>eZX}{X +R|LRyYZxdOr$B_0)YE0J#%{MgAlbD{pv3xD<($yQ;vJiQ=M?$hYE(6g_m~wSkuRDebrQ{r=t*hhFZyt +ErN^SdY0L(uCkN_V|C?WrDo+;+4`GpP`ZoeC>GrUuPqH_H-?WBQK-^o1X8@`$3}g=QQ}B@7H30Mjy>d +$gm>I=hiRYMq$|K%FkEgWvJn!Y$O7{X1l9wYwy4HC@mbck;|_7(xjK+FUJunbQkTPyc?Py)@|PaDz}B +%fTRje)eX=9x+rSxwInaVk3gos)CIHT7y@-DIM +3=pL@8`ossZHwkX1mR)$|)|j*obd&;|PihCya9lI`UH5`UPfwcAk0#;;zXCw9MJ0!RuYt<1}o8k8iF& +xysYu!wClu1`)`lxyx#Mjg!~^AGC`ZLQhhi#o$6gx9lEA}UF%Ln9Pg&4n)KJ9LXXFd}`s(V}<$!3k4M +WeBNjHlG67X7Bl`BkfvH6-9%M(oR_o;OiH+j!9I}FgTj^sO`@-Hyela%sKFE;k7+

    n+*nq5S@Z>h4wrDrB}%xTlD-WM#bJ2{2EpJGqE2<4_HUq~b@4K-^Yx +-=dfiPUF(xQinJx+Ta5E?GG!VA!8MhaVv#63I$i1Vxj@6cQN>02@^nqDfTpJNBuG_Y70Y$Gn%^6uvDiY?(rFco? +xG7-e0l0wr1MHurlvNN5_`v2^l_YcK9WgplXJu@8yHf>>8G|Y$xd~J1>dgSrRaaU!~M`NCk)4jWX(Z_l +tK$=p$&gZxC+>k`XSK5zCO--H@I*^QRLGnM_U1eLhM{;Xct0{3QetNlh9zEzfs{qV=;*8m+rucLQ*M& +VIDC{FVn$>g@8=MqJA#|Y9P}-rNn`fTIg{(iq}eHJ-IPF%>U2V>{R}dfXB$Cca#r~&lgcFtYVUlzP^E +gx4!R1%$Qf*qz`?$;?D8kJ&cr4@n(D%HVx?2CQ}-Kkn*P{CU52|dpo=DpNb%iv%H?a)H#35X)9vO}bC +viM$MKX-Kewrvx{}lVzC-=cT$09nBMLhXsQUsCYv3Z(7*4P>s(dz;X0c4+58T4g1W#v(c1}4zp7qP35ZE +jHBCdoe2K@Z2^@7f1cCQY-y_QOgbQ$yU08z*x*&+*T~kSU9@&%lIG76=iKe>ISGeT-gSA=R24W-!bVbhm +W|mo3ru7?i$5xH{vQKtToOoG_CM*?919VUXdV9un<}AxpW*r^c|Sdg|nYqFh@}ZH}iltaP*KZ2#g04#4zHLR0ZMRN=(wcJF_l +ah=D<=O +VaA|NaUukZ1WpZv|Y%gPMX)j}KWN&bEX>V?GE^v9>J!^B@Mv~w4D`w%kBDqH*@%nPP;zg-b6s@gVCss +*HwzhOCE=UeZL?D0#K*?N}|NHf8ULYvTR9ts;uChb|Gd(liJ^h-VRun}?^(v|KToq-L7J03be6E&Nu_ +^1LqwAGcWtHZ$w9K?x=}N1#R!yPidXeVZ_9^n29;)f4QGd*f=8r1P>n6#YG-LOQDo#sn;pEMTGL)xrVGQ%&&y`(I4KJTenlEeS1+A4tB=%cA_9<nh1C +yyhXD!JP9(S2>P9q~+DCVw27DUUO6?HO`&^$LhL(NvRq1OzTGH4K&X(Z*uTuQDj;10J8=}%ypeE^Y|z +NEFQg3h%J7N>zWnoHGq)85N@T{#XVpe)>{{Az-$2%gUKO^)MC?YDjfru^|suOVMbLtYeqbFk{$m!$!d +Lcl&;I7f==`+OQ-RsNi*{_E0#+b+I%f)^QqDAn};eXj}}$2hN1FVQxsV(U=VpgtBl{mh=fe@^6Hn1i_ +5dCtI3P`H +dNOeV8J8q2n3HO_rM!P6_?5EF2Pk3ChU@cXNo+XC0Y7UJ;{@G(ADrmJj>FU7F(Hqr$xRXH`{n_Q;A^5_I_G>g;lIdUE>aZ1Usz^-9~dUR__l`}y_c&B@i9v#b +4`)@eO6U4Z?YVhT)pbc9?D1UfeC9mDu<=~;h5q@7F#M@L5>l2nzXwVv>vnP{X%pUFFJu_!kgTW((0OW +JY+HTqutT;zIuq~ITJZ2WzRa6xoPKaw`cx-^8^&}SYf=1~Xn5p_JNmNkP?%FG17_>&P~IfK2=?abV4y +PXy^TND~RKJKwNm@hN{d@Z664C^B|9IFrT;-lZhY|nTBvaKJfDEu}*Ebpm1y{)~KQUBZtKiT{KO#D>>>b&U)lMQ(;HxZ5(-7?}}yBe+mNiPj#7SXNYjSzaA%;$ +pCzWfq0RE!P3u@GXm(zCnC9Q4+t(yTmO_j)Wsc_{68YmXJ~FjxyImtEffGb#$4IWA(ZVY9B&oJ2N^x2 +YT4$63;#L~`Z>*a!*gKs}c|Fb3=-w!xUxWUv9`P3*T0r}@{F26l&*l>++$2~6*C9-|RALc@d&icO6}` +;=>AWl}@?W1#o8Z}ER)KRIgDG0Q&CHL%}7PIR{s6x7|XlWDr=4T!o-qm4kC!L_-iPXor@G|iK0J1O#P +i-hvUd>@w49}LxpXbc!1M<1C-YFh4Gqw%D{bkp@j2m%25N9|d(^vNHNgCTL#Aj<8Wmw8d?0J=Zh7wQ~ +k6)15r41sTohde8ic`b4hXQw=3)M^c^M1z3I1Y>J~R2Q3Srm20Ef^BK7Q+zV2OFc^$=}bMWG$<>u)M^ +xoTVg-}CP0=OU?)&BRxF?*215|XDq)gfYcQ_>D|NC7*^7HI>h>?Gp1=&~2Sks6hjX`XR2XLK;1Hw=LH +EN?vOgPump2AyQ3z@@V!j+1zJy(1py6j=xgnx{G-|O}8<0+ETa89}F@og@DF92ba)E$cFk+54CN{HeT +CfbV2pD&8lx2{r4SDT`3BBua~ydO#I5!p;m70XY6T80evIMq09j$WSx(RiIA}T}>L1L)L2S(hYzWXMO|{Xs{ +UX_94ZFMEB;B5j-oobI;!Os2LxSia*BzhWHuY<3ZV}bDRA;#^82(Za*9#hCsm3|Vf~7 +N!N<~!O!iGhIwZhIrT?)>VSWIq*(4c&JPa)YXXM~SyYJjnMw=bny?74RPAIN}cmwK_?Ne>YW>1t%%SY +LN`UukRQ*ku_-|UkWs8}CBvjq5JwS5?lxNm@g;1Ta-LwlMHvcG61p#O#V@y^na{SH>dw5Si`Xpc|=CY +WLP%#deRZ1Sc>nXmm-yiV_7;{pwFCzqQhww^F>=zN|w@Sa0?7#jp*0&RY^A=154O}f^#tSv9Vvdz%&fNM=Rdz5scb5$6^z!`r{Pg6lQ6N5 +?Oy7fRG6+RLRV!4wSU#kdKjH)J6$7PqP|Ajcq42h2Nwrzfu4T84J%&^PkGj23C8{U=~&dW{$@?)zFd38=bbu^ +~MSc@KEW7YWMG0);<|FvX`4$O!<@NtZMSPCI~*FVv4k-UBzKzN7F8!SUn{eQ(Ph|KaD$ogdR0r--Cmm +*j%lfn`1L=Kc0yu6)03v!i{&SO(vqHOHuv&k{v_?a>H2S~VEcYd!wzs~a>=#&~e^F#oSxY8b3XwP`DQ)E;MGD25T%pZ3f=r%b9_zqPtr6RC?lptWHyi +b7xRGx(?FJpUy6fx50)sM-QJNQS}*L5sr4*CN7#(fvB-*~jgXfCMy}B3N28>e-DzG908zUOh2cQ#+NF +|r3i_vR>Iw6G--{Xt{-*enRtmo`O5kBdM}}ymWfd6xTL$RF^=QN+jCh2|1>|_i!T8?W4jCKD*&4-cia +#Fk#}t1gM&|m+y78Ww?agAxgKJ5$GZES4f%9Ip{5rBo%z5aH1hi(ELhk)w{ZSt4=+zzv_xV!uonBxzGxq7qg9xzWE?f@REI-<{W=*dsee7g?^L +gVV7WZAlbp*hik);cpUHv>|2~8Mi{h@Z?KXS#xOzept44(*Cc$3fcO%XNKz)GA6u0bxqf +P3Z4rq5I;=piVbd8w@qA)mvfXtLimqnd|Ex@7eEKPFpNiWVW)Hh$hvRZ`%Fip+xbaHV%plBvX$kiNN> +lwYtl9XH&13D2K-XPD(UIa&A=)KznLKAW2-2rU0!a)|(V&`A +UoZrWQ2}x&fN((!+vlcOT``qx%7`GX+EApW-q+8Qu4MDU^Ml=2>b+}5x`&@{MxK*;2vMW@6fwz);tqF +q(x?&G=J|Y-rTJaW=#*evpwqF)agH@Gimg~9C)<}9_wHkw-tDu+HuUGCO#;PehT^bY{YMReLl@3v5Ana;zdGU +NCp`81wLngNl-3{6jE1ZD$%q-|c1pJ*CuvWv;N=}Ll&tZGf9HV$eNv)#+Q^1N_fojBVLV^&Lc+Wx^gy +@~{$rUF1b)BkC@Bp=avg*>Ob`qyt!VWbDi@==nCes>!oAXv|0;^1##w5kX_mUID(}oZs8#7*m|BtxYS^s00Gq`cwvZIk&jJYR4j(!CRlpS`Z&0 +5;^B7#9WR98!mbjp$ru4o6veZduuxfA0nY&Mc)T8TFXe!(d#dbGQ2SfAYPM(6?FR00vqNdx7qx*gF_9LGo8Hf9#H>d$BGtTIKs`fVXwBRf{Ceu!+cF4zM< +!rIprfWfkN=t=s06zr(*IKYNLLUxuSQ_ww(;(~|l5e~qVG(|UxbEz^2D{dE2%t}y|@AWDR-(d3Zb6{g +1W^`yPr>4Et)quQM20}lhGdw3pbVD7~8!`7V+!EQjh9r}NV0Vw1iP6+lAd>P5#nPI+MhAuG0GJGC5W+C~-!E-s>`%r+*C-A~V9dayx<_sd||2Vj(S0)9&ZjE1@Lj +phqospRQz^0XN>npwTvRAqse`xzc0O+WV~(PP%G!!#`8J1P7GAb$7AwR>$pacuV7y0rPP= +WsD$a;#qlm2k3o;M_#YKjc8v)ui|gxR}ops(4oO`5ZmbTn=1n>*2d~Y{b|Gv`T}@@CniJAvYOyUlpzu +v_SN6*qrU@ZTN=O)9U;?To-m3Dic_XUTj+$?Ov39t;njh3K~jk^WP$QsKi=}P(6n{|-h`t%e5OS4P?0 +$nfQaoru`}6clID!Ns)pUg?fDK)6ia?9Oa|PBKD8OB@71gE4nR~75h%Ac#-*B7rM0C9gZA!cL@u@31A +Ba|Uba@6s|QUTB_=q-BMhJz(3J`LZRD0_+)x5{^YyKLA;YkL3A2A0=3$$a-%tbSDAw)G%kk|kPqeAFu +8+?Tc#{daqz^nq>ln~hz)y4(M=KKp7==E2Ka}jf2Zf|Ue9YOr^@LOCmbxE#pg*W0HCIl8Vm?Mv5bX*?Sq`fvJj?anp*i!BCwa*DRFCmUqHU2-h59&u0RNTbD55QJL}y40cmb}DDCs}%27+Zd_nZstx;uh +RTXFA0pgJXwxNb6Oxv(0S2}jIy0uXdnS<)z>keh=3r()x3(oj=vs~hBFVo#RpF7fd_H|`un${8m-g3s +Qhh5Q%SO#?kRD6m(-JJ7-RgwY}~m1ID>FAi`@`j0Xi^kJ%AA&ig&%ToVlmtb8R`{EkBb#7)X5=e*YeX +8RDx~mc60s{kFxQGyLqQYXTm7Y1xa_IV*k35^|15s!!r=!6gF6;jL+ig&iw08++xphfD^G+sVJZ?6uD|KSc*0`3SU0mJaJ4s85^iJGM +IF$1~GCVEZk0^_mM%01~#vhF!8;E+3i!!c+g&1wH>FAK#{H=}2Im5vVhkVRyH^eS~zC5s}u{N^ +Qteft#1mrrh9G<;|gq}@NRI32HU9Xns8`&2FnrL;E9K>_6C~v$Bnq-zQ|Dkwj*$~lSbl;4KJ0%%8uxQ +AgUq*_1z8|QZgQFpa=Nb4hs1>h+wR1y4iVfsvJTX`R7M;9&X^a#9ym+M<_xcIjE8(SVWQdQ8PT_Nty( +`l<)8i$4`#&LdSL{dwqsy`U@9TF~e+r@l>7LfYt@hl`q2>G?~MlBQf_SpMvAhg}r +N@2XHoJP_ifmyU6(REjTbbri8r&uNU)!z|jV=Ib+m|8o&$;%EG5updAfNO!Wp_WZ=G`0B +Y=%#9;IT3m!y_15_ziC&9~!sN1HP(kM{6F5L||NOEz0G5PtOW24@kk!fhzw~wa^uay<>*Q(Fqs0GR)4qfC$GysZH9Mot48X(6R}aayrW2a9v8{Kx-AanoFgYEp^vTDkILtckaHt)!rThqIJJRFBFY7%?j6c;g_T +_Gv2f$|nmW=K;5EfB5c=p&iYCW(OCqk!dw+hGMw}R*zk~uFhrxQMClW)y~O3G0^dCp~4hGlZ9>aNSJ- +NSuqnL&yw5~vkb2ovrN*`}K`n@{+r^ubOt9v~x}k%beir!_|BLRc_`1fzZ5!F?7Iy95NDW6hYT1h!pA +{S=!-{sXoSi0lPGKw(kN_{;xrK~K3kB5Y#ap@_u6&e^A6mJ4?9!VljW1VvE0cSp2`I(R#rQeF>o&U6< +?glWLdH!r_!Ewyd1!K+EBOKAANYq7yRMm&$hO}1b;dn~pqvaH%e?lZ53o;hV63ZPCq&jYz*=mjR8QD( ++?JKStD3U*6(`a8`w#E*WT&`pX+j1zDTCd?pYzeP_m;3O;_MJLhOYy@6a&+3 +yk(r#S6o%4{5zJA!@-mnsBz_n3pP=)=nY{Gz{8sNg*x-0Ugpt=x&(-c_1v|8Pin-l)?ST4G;}>xa8ab +Q)N-t6j+nFws(q&7yI)ylZ}55#Jw{$E{3~fCr{X>p+V@07-tc>hht1j{Dh|P_{i4{4#-orLMy&W(L+{ +=-_nk>%C@scnYn1yMHCzU&Ga&vZ9yWflKT{(44gIcd5aGB?33301jE<$eH1fcao8P2$eVZ~4qW{u4m| +HPd59(Tvi3`)KVT5pI+q@|Ux}9shdr7W_xp3wiO6#G +)RivHWDFwVf69fIS^51%dP7qkZy}5pg+*LVjxmGx^;y$fdAN8k41La*1sj0_?yMg&y%&0=7~$|Hkg!nQ3Z-5B!y(!lRoyAQO~NhZ0h5 +$Hg)wCUAy>7GUTUejt8nOYJ`wnDeyKG7)a#3Z_k%b0`VLNb6?oTay5Xt-jkuR3*=zDcgWbNtZq)G;H_ +;MO>c$P?5sSC((Dv<&^dtpI6*s!|5PgXEXt--gpp4Dda^*iu<*PUGK{VzhR~nM$#S>CW}8J?ZQiwQ0xR6+mk!8?GG$=$lyi*&YaP5#3EqUy@NiJkfpBD +VxCG#Tk=q_^Z@^f2CmnvZP}&-Ptt6%iQo(`xnr%sTEQMkY5834S<3c;3|#XrAc5yDm7794MbV(OSV>` +&11s(T6f|S2u=tAHu51m_LQV9LROXp0y21~Ygb_`(6~^5OXUnE`=~(HHQf+W3@&-fxN)&=a`M=h6O}E +~u2E#+%75-W4YN;n!0{;tT_`T-bAc+=$)5sCIBwH5adAihiFXX@sd+=SBUrf6wKS@iE} +0Doi4xS~Rvv8VvT>FF9a}nh>ulKIgb7C&nu)d2UB@u{m!C}63=DVslrR(;@y>cr6wZkjrlP=Vw_Ukb0 +|!9`!9!!BVCUm{mHg?Oe;>EX7AMo$Tm!$`ltrPjJ2kWBKI68a#MIyqBRp+sGCf)df~sIwu4~$|uu?Qs +K74f3=v<`mCs0((a0IQ9*$UkWm|k;}+u*r!J6HS#X&us!~e5 +Q~DAszD!|loHfqqU4)L$`(Fi#$QThbZf`jlb+No*3Wm?xk?FN9x+yxNyh2#uOSc=b>XuZyZ-PmS%j%s +POX7tw5d~6s|@`^^MvBFh5Jhpu(cpK`f#m&VD*z{VM%05e#{lLbhK&7n>Y#$N?Mux5EyPb6KWP35r9qZzlR2!=IqE$>H*q; +CA&0eVQ)X9$=6Q+vmG#4{R^hstRa(k?KR&!`o-4d#wng83ywR#JE!cB*X>3WyW$uWB%Y5x<5&dj5PgJ +6NHAwKK#QMmmS@LG#r`1`OSSyuQJnn_DLm&4H)+>u}-UgNw}VKmRWX@So=k{(!*Ik^JeqN$1~RLMxon +NYZ1hB1u#h9NWAT6CxRVe-MZMGJ)eFSIpl%3rg6#$Gi36y~9h|i0-@8*$p0qsk2lb+I`Im@4k;IjvE| +~zTf&&q0+1FffOHg4}$uhUOTU(lzWQB3B7NwqN4XO%pH*(fYq_ylIM<@%+P)h>@6aWAK2mldXPgNpEO)V<{000I9001Na00 +3}la4%nJZggdGZeeUMV{B2dXae9Y +?=zgZewZ5Fs$(Rwu=-&;?AL=GjAU6y|hGI@!T4Mj)^sPme!TBt44f`0YunO+`hgPomSk;EW28sPdkiz7TIQKLPp0&u%{K6{ZJE} +@C+Pc~O_L7*1RipO*=WGNwL-%v{f1QY-O00;mPT~AeNS!8^W1ONc22><{Y0001RX>c!JX>N37a&BR4F +Jo+JFKuCIZZ2?neO7I6+cpsXu3y0^7-j>ujD9tc0Y%$k0fMv$x=%qM(9%g_Ly-bW+0lmm_dQ2ScG9LZ +V1qAr&)q%uLg#t@#i?GpNyuT)s**ujBgh0d4OW>@WO<%vsvoQiLfcAey6oCknReM)zpVV^v&Pw8glT| +)n2&Fj4;%4$U<*=bNn}t%QNj*6oE4Q;g&c#eY(HpQg}TR5YW;>k2m}}$h&*V5d(L_fiKZckqGPYb5za8Z1qXivAL?4M +v5jz}F$~I5%4`Q`D}p+3N8`tVVG&$p$avWhhotx(K&0itI>`dcAQOmM3(4MeAq;-M+i~KCX^@4oJ2E> +TUEG0dSGJ?7X?Ha;#aqa2In&z-bg~nzlYIf8ubo7+wi}|PC615w1DC+&BUuz3B3BQ79w$f+7p4vjX05bGWL|vDsr6e*}zG`7M#NGNt&2KWsdfVm| +i$g&&~CB%Wo(%>Zm-$CmlwQ7W@*3lOAVB?;YS*gMudfEXS>&dRFeY{CyNain{gcq5Ni) +m*|fkf|9KrN^4|i~_$@E3C%yjN?np^D8EXz_Hf$#JW)8Il>t@dC@U7-`Ts6z@9U40I8&>eRhQve=y?; +>DnXu_aTR1zF~S05DxuDbSUS?Sv0~H_$_-|kD6AqJfCp8UFT&PM^~AzBmSKYl+qQWiCLm&m1tgyUN)F +8OGj+LtyXYZ&P5RYg&Klk=t8{r+={6K1@{JD)u7f)3uLURh&nefKv;Q`+?WfN^$*19RNjxo1PzYIOhw +>2tEoxl$UN-N)Bb^n|AP#yE~#9UC4tcJ?i}W +fDDXbhZ|E_ZnM=N7-Gm2XyiFH>vG0_eB+8mDL&w3P1w{@{nr|;OU68K +C83YAucz{TLL-5d4PWp~(YQE+fIwGrhMx<7MQ~kmn&T>)HVQg3VO>1l%vJ~z8ir(wUu$?$?6B;^~l8< +v@k?7a|8djHLP~K{ObFNqUcDrnHrf?~f^_&w&VRUA}jQ?d;XPN9jP)h>@6aWAK2mldXPgOv;9(rX400 +8n3001BW003}la4%nJZggdGZeeUMV{BqHObI+ZkDEgIXE)EqVO+|A?N~uULg*K|q +4G0Jo1z}H(L@i?O7^x{2q@hYPHH)HXHsf_8l_665C*AHFC3B{Sm7|`OO4h_28?@t=KXYvo@})6c3R)% +Prfn)V>ognQq!|~6DG;UmmRCi_#Dm{@U?x+o5OiO$40NY9*)PR{uv$C!se7bkSy=Mi_;of)!&1+s!m3h&W8TOR3&iBDPtNlqSIeBnvc% +{>t>pTn>=wuH}X|U8C4oN!9W?4q7D$B^4Y@=K;YFLIT&boj^35o2JcQM+{X0w8o# +2D47KKBtvtgShmGUB8+3crk1dw1(3UmE6-RI0$`N`*3^xMR2RM;M&62=1({9j};kJ2OTfM+ovvh5QUR +;dcAjeD;v=R6HL*Hc+jI1*6^|#_7CNd*7Tma7-R<}+a?Z#&P*proF60KF^r?#{*>M&Ev-iw>mjP-UdT +rs8nPHzlN;4C+yz9zR)imfRLooI5a-K?RKOlcCWx4V>0EK^hgA;VEwG~<`JQZt>_*R+t<{e|zmR8V55 +ix{RJZV=jxSM&1e@A~go*^b9o2Kn4XZI@xMw#?@5mQ`XO#XUxrl>x7p$%(Jh^sd#tSA4FOOMsGN((_EZg@xV9MeU-r@y9C^q~yT#>L6Q~zPxJd^+ZlLTaDY#;}k?NQbsv#iE3m&*K +Euac`m@6r2@C<9&8K|VZx6#7m0u|VLX&r@wS>77NdUq%Z2d1a1D;V*60r3bMZq +bvM$^T{*;hw8(-}Na7NeP{^FwW=o>8Ds3X}+b7wP4Av6+>hH}DZ4Pd(mT%@CoXb=scJWGmDrOT_LpU9KDFcoh>-IW$q%X8H*;WM+(g0pWM<@a82vAhcOU{ZAv> +PdygY2kZ#7JZ-+v+VWEl_Qv$Jb;KIKcKyKhCXki*|R6FhaBIK#i7~lyD;-uCnhp>giTl}hS%V;RRO6J(_Ui(j;r`-v<7XDl!Y*oRq0DT>M;Y +cMgnv4Xe^L04r>`u)B6B*`8Jvj6oxn|HHNLkT?FTwl{26<9Qrp4SqLHguzqgho#kEJ$+fengRFR>sQw +wPw1-vJQYxA3#WuKQsKO-ER{ED*x)`(>k!<8RDzir1Vr1%$Y<|MZJ7_BMjTtMvgZy_~lK=j +`4tiJraoOEe$K7*mW#jNt}WQra|F*yX&Y$2}uQ*^-)382h4Uy93Mw=S~1LB1_`tU9h(pK2)ITV$^Jde +dNGitvLx}A3>|rMR2>?jh3O#YWcuUgWzToznb*=vws0lO9KQH0000801;hJRg{3d|H}*j06{7M02=@R +0B~t=FJEbHbY*gGVQepBY-ulWVRCb2axQRr&06bk+_(|{?!SVNQCP~!O438WEG|LQyc#q}g5c7k*jos ++#NFjtE9ykb&erh%-Wfh5QlcFv=!Xh{qX#)0&ht0JO_JnqR5Btu#YIax+Dq0G$<)v#Npf<+51r^0X~b +^F+nsxa(R!qNmis~RW_&tIcdOV}b=}kMWZR1asYHt}8h%0C(4p&kCZ+kVjvXxGzP)IVIeEn^m6NxuWj +$4*&&hi(;l-~**RT^atK(f!a-iI|v?;2F7gA9*$g&bOlaAV#5C8b^@n0Xx*PlOs{2ZMq+AZG=unY>q& +U)#;%)`gmeJ}bLC6t9M4qR4_OwVdssge$AU*d#v=$o8K)hlr!M00Y2@&&m+bAm2*Oug6d(z0ynft6); +a&l5NR7&!xXG$Ia&YJFx=nquEvZ>QZ@vf~IFj5Dfv(*WK3pDzIbU2%{{&53xCs(Y$5TO4(3@2MR9`8m +a7upbTe$BR|gne>VmZ@aTHYbm0zmqGYt4EJ6ugAxXoIfWYM9a+5c%7P6Iw851@nMjPTrff#zH3=cuDR +Ne^b5T<@E{A&V%2kPpd&4_(mFZNuO(6~9o1w5QvCVJVj$OY0peB94sfT(JzWgD=C#`EZ^ouk5hY0Ez( +KR6I$e$hGt6StaLW%QHV&QBYFLYdWJa +Eorx7~5g*2hkr_KVh*uE6KWU{=X%>Ygl5`i$rjEZm|IJhEQl2#}kGJ>H_4bR+Y_rioIH@|4aHG1Kld$ +xKlj5P)2EvFY9)oidvPGssZ1gII`rkW-2=LD!`*<4ycd?6u^BP%EG%^lJ@1s|YSu;rm?G;rx +fLjb +EeEvhlT?s1=(x;Sl|0YLh)}d5vi}z;n`NGC#ax~BuOv +D)0JtV(ve%^Rzg!jo`D2YvtzQyA9k-TiUiI7vscy`aX^WD +I11S)|%bie!YWtFc~usbE+8$;ZX-JM6UXVx0+lbyM`-cl$%9}$H1e5{77+v*4lneoP +1cXm0`z0V-C<`K>dXSnkQ%IOZ@JUAk3v7PfLlA|311=8x!>C(Z1|TQ{2}ao04qOFO0(?E>fS)0Wxqyt +XP<4P4u7L_@w(nV`AiSeM_7>?Q(!w&sEc&96!qzARYZRi38mLjQB6GE&beo$rXc#Fif8PsZ?KA@rP?J +IG2Yd74q>m1wNP>|;9JAPNj5?MZsYh*EO&2+6&^Xf;95 +eX`px?@Hwkn3$8H_?i^5ygUWRb2sk%m-sLYiyf}RHy-%K>y*5MMU)X2)h3yfYlfCPG&P|Y2;!09- +?2`#9y?kviek<&61s#r*qr|v1|r?PIg-diLXfXsJ#LC_rWMi7cR-tj9)!>&{>d{C$vb6!E~I%GI +rQM(-!{#aRb!p8xT0k(R9ZRcj)rReo*^Y*V@FTmf_aTKYghW1oK@YQib%LbcM>zRNyfpFe3XX{w0!ek +d+G2>OuV8gmFX~Gu5mhB{{4aN}SakE}cDGsfcBax*5=BZgiBvDu+ai7>yC@^xe#6@6Dm&Uosh+&)Yde +5ugfi&C)Kb$cE=7Z_i +WR}W9cbVU1*qeZ@qH$roF>d$S|dmu2;hI=2eVE16m%O?x3s4gn|(%dKhlxXQhrzp&&ee_T6hKlYijes +|I_AFe~dyun|924rYQjAG^NQ|+1HA7YQFG4akOr?!j^GNxIhObg-W-{3&5dzkf^EwP)|+j0jrJBaG>4 +<3b!-VE|2I002F`rn`YSj6MYS}ZCwULO0L*p-aebuJ@%{Dx>3E>A_FOHa%~Cb@5aaCfv$C&lqhY94h) +fWXe}EAN?0C7;loF}d1sgy{fEdA{Aks( +`Ld;PCcaO=4K-F1^{Lj)1dGZ?K(u6NuZm)_S9(l=cLSb^-h2_}d_)xs{#mj1kW0r!b0{rTJ6Bcddrbh +fW=d$=)&afM&fcs&ykC@Yuz$OBY}9O5P#)g{h$i$=RRe8JaCuXVR>U5K%KyBzcsazsJRZZ1Dz*H{K2` +L@kzxc3Pb(Mw+f9g5}B6Y%T^naH)``U;-;x+oz3dxJ~Yz`Ih!?&H&vp3X$LBd{dY3eq4@(`uDW{Ev9g +2Xc#dDmm4{1p4?0?0K_VSe9Th5;iI8z-w$5}$^VO2)EOu=<~x1%VG*b~3xhE>6O5T|im~v~X1#*E68I +G`g;EWm=Z;m_$JxX91}ze0QbC%aC}$|=Bmj>oVpwlaG2zQ>I_fdHdI5p-FksrjUOhPVSI05IGd5NP0k +9Ym^ccT&vVuawB|}}IeJT&MokAhZP=6{2N?J+=9nE}rsd+pc`Bwa1go(5B^D`*3D~fe`=*xIi2 +^ER$mFjZ^3|wIt5kVhGFK^n8$@zIgenJ+rMHOU1liMSPAv_bssHZj-6ycNtjO_-hoo;*%kf1DI=X(E0Ep~ +asrrH8S(AC%A_R+dP!~wVUDo2<@oO4D5O-N9GGUK6Ev;w`JL0JUujJ;|ZIt5Tytat|trors3$GHCc`N +>)cT)l%#N&GH<=Z2W!woP%2!>$8FBv&9`M4GCEG|k0CHPni4v$fuK7N8_qGGfz#zwp@R<*sxD1>iWvr +red6)G{bDp|@+yRkd^PbUdSbcol3)b&F&5jRGuEt?5i956m3wj1yZdGh~c5mtYrWiXCsGC&=}May;&# +B`TmG5n#yJ*I&m`);ek-D|sfj3#s+9aG#P)cIrJKnLR)#2c`+|EY}`un{pbqha`K;m&dmGh%JfV^9W( +sosH%l9>(=Mj)87d64BpSiPXtWrVLV2)coHwhi$b^CUJ#;_X3q!N<}Z#@k^w2iX(*5$zRri%fl +H=gn#~cb$?q8mQ1qR+_CZ_X@S9N8IkU)UQFL5pGO<8ku81X!nTi+8;dX&)+~!ykD&NrEsXu*r}RBDBr +LX6jH3^E7zXNHpVEYffl)-b&Yyn#mT@hoDM3T&JkU6Gw`AijRiI}o--Mm6V^pT3EP`fUu%Zv0#N(W;B +nok5m25&s2J?YNqBlew|EF*0;=K$RC!Qz!g;Pe)Cnm$fRcQP!!uF~cRIp*DCw?6e;G8$Yy>w=_Fhzw7 +0Ua%x2NP|Q|E}h&avrmNuCye0VKGmS6mEz2!N)J#(gN)1GDc1tvy6P{+`JEmEN0ss+)Slt0gAE|@)TDZJ +G=HGwLqK}8ab*N$uWqD?B*Ad6}hu6DeK%^ZuMk_mK@!N*E!Od(oh98!Bc^E&f9^CFJ5HV&#r6;5kuqsr(bxN|Mk9z;02;ZpLQ!~||pq2W%O1U@-hwZ?YU+P>6IXvJj$ +4^1sG3ShOU)BHQCQk&F>;*i!x?+sep +KPfZW_!_D^Mh#w@cEtX639Ui$*#K!EvaAhkr_ce|Yt7*ML5i3MR+BhL?D)oLpVRVmHl{~`^p&{Z49^3(e3gC@&aMUHHEDDKNp +tK8Vvz)o2`-= +#;n<5CWS&O+WJ1pUZ!sr-t&fz7pE7x?1{^=aijxYcC!tl< +?X_tc*WrKFaR~;V-7_7)bql6~6SZGyduU`aOec`z%DQ+N!mu2qFF#`ry5HF@YUcqyCwMxPrWbHd(orF +6KFymcS+CXt=)T1AbVRmOlo(0kvwN5kpkW*8a++o~hn*cArb5&p_QN?Z8YSoE`&$jIDWYiAR2~pfB#6 +lf9W$1-}LlWWV$_qhIioXe4D1$L}iw?K~z59lRBLUzJ3flLg;5393f;Mz$V}0Qk@6a`d8kkdT|E+4F9 +DhXX@q79_AUhdev$W5kSD2+%jZB*se*8$LH8)Oh89x=;k)W(!1d=lUx-^j;xK9w^8R^`$t`j&NP(kfg +v>ehWy^a0=P)h>@6aWAK2mldXPgP`1H>ds+008_(001BW003}la4%nJZggdGZeeUMV{BUU0w7gXIabfQN|>1vNr3uc_C{WD_3JlR@L32;=B;m9V_a`s;sku9kc5!zt8UYpF~ygm +gQNKtwkxiJ+!F~I@VVe&w-Du73H3ZT~oIm(p~udc3Hj^6>s+p=arQu6P;uaFqgXJg`O6#a>3<_@h*=S +Q4}o}nqFDo-2q$XXD!X0+{?wLt#@qGS9w?0r8F$STsx7K>TTV0@Ec^QU;Z^Kd#-zS`vzyM-`xJ~`aC^ +-dw%-oi}ydS*!R_b#V&>HR_v1{;^*U8g--*u`Rfq!roO8v}sN;Ft*6oU2xBNlWz5JmUWpO52dK@BztN +$68DN7FP(;8}*qPjmd59j+eN{7;oQX<{i)2vWuP;g$#SaPHflSrGEZ-$wCFppsp8{UG_@u1)}l`d=Vc +$YT&ZVOgt)Px>h)3Z?Dig%B7dk-v-g$|q#HXG_xHyrE}g&wa_uIm43*TQ`5>`COe>e(|?VA>6tzv_9z +|Iv$`^m3EdeOG`p1_Ryl{C; +3sG%3A?uBayJZHNdAi^|D&Osi-R>J;@DALXd5v^{OnMJCC1-@JMtl%$*JmZK9? +Y06gaBBYv`2GH?R)Uv8{eOv&{#xT%0bp2K3r;dv{;~{2USmu$B)6vF6r4aO{;9j)AVq$SQLE2(!Av$K +roM9b`qw=Qy^;m-SUtf{ny~Le~RoN(I>?QFbB~iHi8z_re^=imfx+|H*XGA6TI3?NbY&RWrWez4$C&c +M~`MEAudA@%pNjPqDim~cs%7K*mvq}{l5R{pTqU +?qp~(9SM3_J|Gf6KfSV9DM?lvzqWnVsFPmaSJ;>MnMY8Y2TJ|nU{dnG;TT3h{`uQ#7#eekNNb;atTwq +QA?4r65g|V!-yzDqRKabmb6DrTR(^b7G4h#8=Y;56&HcB#9rSX9-D^=QTZl$DY +@296g{0z%Vn!#L0n#dGNOg3;iWPs!&l&KktEOSrdPHQUqCK#khW5%FYDHK6oIbVdULdNgS}y<#`k(!o +l>{#wi>UdQ*CSM3EICzp+&uoR+l;{rRh)ULX^ +$vRF;KcUK_zIe9`x7J=pC}#jSzmMT65ty!_n0zOv2E8i^5ppVPITLTjX?icfz#vNzS$pxqX*WG|M2_& +_&vK9`8_6t9vSpJ7;nZRMa!Xe9%+Kt(D6sU96e`a`5r06$|c@Hvgdds@bJbB*=C*CaezwCHQRuE*8qv +ru1<59o!E#IwuXEVT9U!5rnCA;i5mf_WN&U(npZmXXcdlZ?1?ZTH^b +-vC{tb`W+;5E_JZ;tVvBQt-{*jk*Ih^ObCcf%yqEH!A&E}f(n4a~e}+X*tstoyo9?u+qfW-4?YjBEg5 +;f;HTZiCv3Xbm@TzDo(IHODt{Jj0(jc0O(#b^si+zS$Yagn&Sw7uhHCi=T3c&W;8dfARO}(dn2kUJUW +p1NjYrNEIPHu^FZyPxp_=>#+R|A+d2PPoM=GoD@#|Jv-A}qte>#FQv39$ZSXRF*apl7k!yG8>|1A_wC +0?Z>2rfFr$L1lwuQLA)*NPOcCXxj(I)$T)vM$+%lfQ^?44rtmXrWq0$Zs+tuHwZ02U2Vi&k7X|aIiEp +!Lfql2eV8HL3mE!&Vs}d2Pkb_~a5MRIyI)-1&o~6q9pv)J<0DK0zytIF_%LV=0bB@p5Th{QsZiNcS#d +lAY!$2Sh<+22p<#uvTH}gt2t+yrX;glJ_VHs|_l?Hl29vKsfPDha*Ago$ol|6WA-ZIb5MOC>$`0>vpn +*w6>lW**5MFBN1xbi(YV6pJ3aeDOZBTqlSdg|bopv(H?*nLmOS+O`J-{mkJb|-(NaO;jvpPJk@%06zk +-Uhl$Z2LZv~B@}tD>}qYJg9+*@M8ar+e3wfZ~kpWX3GKV(+qh$VxF!o{=e4svD3ZCUE1JyUpYrr1!(D +Ss*>9sRSutyQ&5>d^=O>=4$^HqQOUqb1N&jM}E+v;+x5r;LsqSF#-nRpuC4#Ks+`A15xL26kcYw!6@} ++6myirNx(>#zgvy{(BT+L(gMSO5@0weEU$*8p;9_BSkzo>hj3)-BI_ +RU65?k}>2X^DR829^%bt)ife6rm5lOe2ppf#meuQSM61j3ZN1H6i72v8ajYHAq$1n-ypht9bQ&Uj32_ +Wl81EY0WyDE%V00||xZO`2>YwpNr$T7OaD_q%IN|QgB2oK)v&*$gY-~VuVp5C6nyFRG}J=1`2uc`SsiLcj?Es=jWH{>G!8^&qIxF&i^MRDyzjOU0cHfQz|xT+gE8O)3&a4LCyLY +Fz&0q12+w;q3coPK4o7}ou|cO82d{lK5MUXfhoVP4!X6PmFj}VB0EunqP3#q_8=HBBkhc*rfo$(jiOasc;-wmfFV_%?)yGxg7poG+yZk&*a>)KRO&Dl +y1tkMr)EBBL0sM{*WUOZ1hd`VswqT|+FzvT6hF5Y%BuF@&koF$6?_=<&yk_h(lhKQNjIxgM&OzJuhIn +QodU*$6~Alk#GM3y3=_fYYMRWi0ArUL8x?fH=m0is3uoVX;+^5A|!vu`q77Cm!$`~baGTT{75?$W +&ES=Bd7bkrwU>mdIE{|@fu3|LYsYG4^?Hpo@vz%52w;*_RDF_>djI&1d|L+QN^VGQ3xR|5o3jVo;RcF +5A)oXl^T1ke_8NGlfh-D7;A=){uhXeB`FWoHVLmdVE!tB0*$Svhfc@dmg%uCjoCxPxC*fW<14Eq-`q6 +w8)d6B>&oSQ~PX7Jw*POn;J3h&VWkgW@JN4~_p(Reb!bCThd44&uSxX`iG9Ko|kh%^5Qy`>XRDZHVxW +$!!`{ug8+Jc#%_hVKOGRyKDVo=p7oWyt +FP?_lBUny))yi~ziIFi8j@mw6kCyRnW2I>wFh-rllFjtVv +b3tc2J@21o*`8zQ%?{CBMt^{gEN%)d~v^`jpeQ@zZ5Py^fk#G)bkSZ{K|T#*XpCw$BdIHg^|2)rSfkz +l1wM)_8G8TBS6gaVMd@XuM|Y!rqyO6YsRc%25Hmm1S#!PA!jqUk=VX8=bjYr +gR{g~2$$Gk9XWck7ruFPO)aG +812Foh*2Woik&FBpnIgvK6-`?I{k9MsGI&D?~Br`in=<4e=9s#t6;UM%@h)b!K7SGY4Kn78Xw_-U;gYE_=I(D}K+x8h5Y%U3y;Hc=|=G4$gK>zj|xqd8hf2;z-3YZD0*g$ji-0wt5 +nPCw`HuM_52#uv3d*b~XO;9=XM2;OhUZ7!jAbOduGb;l3q2FA^f@Cv_0cvSzO;T9!Ew_{KANyUr(2;n +*Rx|3H*jvBJ5IvINZnAIlR6thRHxqcMqq_myp1W9||bs8%JLD%1z#Y@~(xOq98`5D +^z)i&KsFO<|LGv|ncx5}}xraITGZ|+)f-zil^_#MP_xWgdR)khMHKyLO&ST2hEIW@fb-8X-5gn-ni({ +J{vd(O;hPiKiS8C$V9jz9Tb2}!u837>@!8h(ByZXnfiPG15u`WUtEt)4uf=`|z|!i&MW%!3pU*G`Q~i +8HacBhN!=P6}x=rc*MR9-$evFehk2rP|*B!eA1K1#NqL9FHhc&@6h)5~)Zbs)t%{hY5A!OI0p_*9SbR +=a8cs7@c&}WjKHqc>07qD;|fmI}sR`A1TSevRau@E!(9P-3%FoO5)ZW5lIWfgWcn1&#+2gfyC^2)$n; +(-K`XBbK5RF0y;8&34!=#;`PjB9A45)oSeCy2nBdKJ}q(XrwzwOl8hfrySzAVkYts6bH9kN<}-aaOJH +!L$@sx+myt_N!jA)@^?htI`W+C+$lE642h%Pi7n+QI`yHGt;~70wXfB|m&*|r=D?FV+>7lvqVPlyFocq)kC?59TpzeXMok~KZ%5bacSVMY55kneN>zDD?FLguTlFIoxH>4m +HD62bf*ug$+!?yuPUtK0Jv7OqXu;fFP+6FdOnkB;=~BmEkZ4V2ll?$mS5zokDNxyOk+9J{~&*WT|-v` +Tk*R=IzLzkJ0mHjs$!-5yliMCGvCr~2##JsgruH!%bfI6w?7NbOQ{SkLhZE>ojro6%fV((%siVSRatu +H8MKH+UKM;nTy>vDgUpFFH|Mg?Rsq4>|4THR>ithsMUVVQ+x9V>Z;pEgz86F)$w +pNW~7^7+9MEE)`YkK{OJ;k4JF`}|B?{u2H7;X$l61;X6m_4;)oaw=HB8dJX{Vv#x)s4}0*OIFtPy*ZU<4um-wL)PZoROWTV!D3Xe*nK)QEp_!u(uq~u0F +ObXigEOgL$jr~$2UA4ZTP5N84};n#7U*}HiG{L0KWJmqbK)?y$}1{lC1M>|Fo1lSABk>0<69|a&_aGI +<7gcrPmVtd|NlIz?YPM(RnRvB05)dPH&%3rFWLsk6W(30<3it&_H!KPk^)a)+y5InLJVgiP!_l82vsu +kC>gQbK$guNx(m>sMtESe+VM+{{}>?K4O}jhc)rk`@6aWAK2mldXPgSYRS;?LT0090I0012T003}la4%nJZggdGZeeUMV{B%ZW5@+ehpiK*Voo!5oDh6+HYWy@zF}fMAfuP$Ovy-dA5FPoShJH=A=nDKS$?Xu(tSv-1kA +bQX6Z!?-3t{{0~YF6i5V5z#etI +7El$e%vJqt4!j&v|H?EvXJC0=+l7zW!s}Ygi7*>8jwGXHk+)XarKq@0kW^vTm_LCxTJ>u~8b9tvFXS54=0e>TER$Me;xu?G>J8k?)qcEm=ydvqw;*(&(T +fd?G#@%k>j_4gcRa7Vri7nVc98!8M-I`rV}P3Enrahm#-esdLr?mlO5y4Kjy?~xw)rVE(0_5Mo7ciKYw6Wi7^0%ECqW_00LN<%->|j+>k(JA1Geh= +Z1)XKL-vyQH^_P%;x#-Vh_GGglFs=R}n{dUvfvw-e&1HKs@u+!H^;r0%N~p*a(lg623z +cU3jry1$Xg{_^DE!?i7Qpx6=5cQkhFOp+g)xh;)9S5yKyI&sof!KAN!F7>q&npakjW>?*;I1KH*|Na# +0IcU!tB+4WUg}RV)%`%IcfCi=MnK8qlo6;^zqorc5nbKH}IpP58cwH=g6IRV}p(qT=taS6Is~xdG;Kg +NlfqEmz!yS0CSBoWJ*z!RIHls~?^|6TRf +(?fKApj-0sTx<$DE_gBK#+1yAz +y0pXix?5HY4AOHjan$(1KO86Fxxm~JJ4F=Kr6JG0Nn!JwxF=xH|Tf&(TFlzK{-hN0#Hi>1QY-O00;mP +T~Ag0dH_pU0001V0000X0001RX>c!JX>N37a&BR4FJo+JFLQKZbaiuIV{c?-b1rasJ&UmofG`Ze_FT~ +ups+GP8$<;pC~3-=|G$6*Hp`aPQbN@*g$_`J<)t2scH*1-GZ9*mYV(2AoVfbRM)?f`T!O8zsV`QJ?77 +H)jX><@T+@d7A8~*OP)h>@6aWAK2mldXPgU!c)iII;001fv001HY003}la4%nJZggdGZeeUMV{dJ3VQ +yq|FJE72ZfSI1UoLQYombtD<1`R|&tEYL545K?@LXw;j=e*y1l&p-@UU8S-Nb3EI(D#~_5$L6XU0xir +_CPZ`jTcof1a<5(=<&US$<%94!Ks22_@7Xn3T|n21#|zRAG}8_Cdr(P2t$kRltqK)=`VX2eK$cI3g@^ZJq3v{V}Y!&=_vH@&;elYqIx}c}pKF4%!sl5UXYmV`48p3t=0{Iu +u3W_eZY4Rl40*_}~yu3v^nrj*+XAaDiv>*pObuxleM6wh&J#KCQfL*-{mdm6{#!P>x#18mxrI5aWP~HGojyZyF@P(N|pVTr-Mv@y}8c)`l6+~%##ti?R0RB>;@KjQ! +o0z}wJkOs#T{ft#E|L#{)SH>$;PHKL*sNzR!y_f+QnO+%h_hbg>s?QqfHAOM#nGxSjK)PVa`)RnoPcu +Xy^5tYod3n=0_n9PPd|9d1cVR1)1b$}FHxc5GW^e07+zyH`9{H>xdw=eShDU53dgIB%Y#VMg+mJp01Og%>f{+WGfxvcpGJV&W@`~q&xu +Akl{AnOb&JRaCJfeFbZrc>>2c6ujZ8;3^jr7_}-$t{l9yqcZepcN<5Q>HTP8SvOWK7_B?YYAnTo!PPSN^y*c@R^pQu+$+=X!2`I-z1J9BDLF5 +Q1DOl2LUOKJR{*Vh(^VKWJ1z*0AbbXmo9JfHew$7Tzo+(!tn&1D-Xn5CrN3ytjVk!i8s8LtBeOgUWp%T#VkV34G +Vi9uh$*-U5j5e+Dcf!==q*^G5c#irpj<4>LQFq!T9+-(SPy_tPjkaq|92lBD1zhHeQ{nyO^x6aXa`b6C4N +EHRn_n|EO+Xl`evc2>gy`^oyb_aXzaIrVNVC;V+f58wmeZ1~QFz@>3C4hLNArl6-ZKDSLKK$r+YekLY +$P78Kmn8V84;M4q!=KNwAA(D=z*?u*xi$tTT{gti4->oulo*cTzDDkHsqrF*Ws0-d258<$xVB|dpSW4 +{n1k#9EW~MSU=a>K%1n0Pon0(@8=|~2vpJXP5Rwu5>D!Q09Ekiz^cSso@-I+J0|XQR000O85nWGJmtj +CKg$Muuo*e)H9{>OVaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZLVPj}zE^v9x8EbFjHuAfF1);?vQfn*Q;( +!B;^B#)a-d%x38ft+{IfpDw +4QCG*fUYzz0-z2gQN*MOps$JhcD)kat-{3-V-8il26(dZ7(L6 +_j~C3&5Ei8?7JHmpvwpFljt6wv12xoV6n`CdHmCHq8ACMx)oY!C1botZEjBG!%Ec6e!7vH~JVji3qq{ +{C-us?kyJ@9$x!Vr#%*+fu9<|6EJ16zT_@)0Qo&5x177AH}u;!4k>;-+;xE2VOmZKW}8iX}eK~w`x6w +pSQIXTQu-s1yOfS*Rw;xTxS;Te~D%DVjQbH}6|6>Z +~rCimW|n*7i$VPr=EFAbadP03s|{8lF&J5(@qV7W>8sFzm)~xH|AVECA70t+^V(g(KMws=2)7;0IH#_Yp(cF#v`3gQ&B`R +RaBX)euaYkRO!0pc^pJN!jkJoR_5U7gnGmN!6D7|1r#e2;X`@RkrYGlynS~^Yf$~1`<{`$w?p>2d>yVcnj|*h=tcEX!l7!E3a(tFBdOled>!uSjF)hwi+x>V +*jb^(9FQXtDf;BMfeYz(f9s;LV;_S_NJevif2AhNBdTrlcm&KU(an*(W=#>x4Q#2rcLtNT?WAs@&UiT +18P0xos>)sMG_gE^30Wg2>}cl#L}yk)<}W4Swg=4jbXUl3=KR2?2xQk?uSaewh;yEUQzwd**ZKILE8A0ni}+{gu(q!DI_69EIHBg#mk~qV`h;+0n#g6 +dUqd$zUFyGYZmx{U0Jk5S)2JUoy0Sd7byJkuI>1+C(z;(j%BnH)>`RK26U2ayO%@o1no5sp3|-c*jy8 +bCo}Td5YGQ4CI1RkB8oZAY`Yu`ZU&r8+)?ZDoj*)w|TKT2s1xBRGXyv)n7A90#)M2-HqORLq^s4!=9s +5o79P1|nM(jQN=C>C1$@9U^Bwh#McEDKt6f6|(j;=c=o+HEAnf$IX3I2 ++rv2z#{X2N__j1S3Osy?{y~5*h?oU;2+_3dBsjHGhaI#v8ha%^6&qEc%C-{mw)6NiGLn;yoiEIZGP_k +EBtX-EVwOMK}!2(}wHy5s`(Dj9X!+xkBi#Hqy&}nJKB5fJo{S})cYfI&}c;Ho!fC2xyr^ZA}{Y+JR!@ +kRNn%o>07&tYxYg^YG_7H|P0On^Duns?XdQ5)CjOF#f< +yt5n2Ig)tXKz(7XJeFJF8z~?Zn7m`8Waq6a}an5<=uGv;e(i+a!=gbbU}X$L3-!u@bU<(nl)WCLn$Qj +;kg{Lb%zF(pwam1X!vZ0p*+c)__cM|yE{WXAowK%pOw@do<<|tamB&+FU|edmsb3iz8nG2x0EgsZgO3PW~K=Qv +zFJ(o~|U14#=ICAC7aZ~=lMc%wkBLiOx#4k}au_~F@QZm+G3G5ELv_a8WE_G1Iv=G15RU!m>qd&IOo +kcb4>NaMU%==im==Yp&1OgkOMD)hKnb(p|&tkVOD(?3-@*`3G>BQwa^Bh<-k*;!wChJeB>%2FI44AxN +PDhNm9g=gBd7j3{VsUE21`T5~+h)op7LT=A<{%~INTH)8?9YCJ9$lY|}etpftfJ6_lBnOKC1J`Y$46t +VQ$5nT_sI*D%jWoXiP)h>@6aWAK2mldXPgNQ=Y>6=f005N+0018V003}la4%nJZggdGZeeUMV{dJ3VQ +yq|FJowBV{0yOd9_qcYvV8wz57=Tfr1TQv*$qf5_&7Ukg&akU=(}oXd_EneYE7?@5ospMh1{k6?u_j|K>*zJ}ol}5uHSJD>M39Iu|nI=H1-zqh$&aB1KqRWSEgUn&PUj}G +GB@#klohR8+Gw-;tMaOM5wRtg|fRuOIlNdosG;lvjTD0gM`@OU*CF`&(vm{9>!K{T%;5KTKj{n)Z=eJ +s|p3v~-0y85R$yzwEX9SW~)3J{Np+=3eMp<#R1yv?JgtwGTOu`2A>rNZh!k!EcY8~dx8XGWPrWQq$!x +&zV#^=CHOK-vKy>;f0!M7j8%B_p6Ifs`M +uTH=%Nae1BP^|JrgqXr|#(1RV;PqVl?}yhZAIKaT_Nh`2r|F+2_CcfnT7H`RCpKHqp>(|qNAKXG{BZ6 +1v@ddi8oz1HJD9OH@7cziXArJrnBme*aaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZLZ*6dFWp +rt8ZZ2?nrB_*R<2Dd}_pcaejV{$%+PaS}yg-sVK+tq!*xm+<#8734cG%UmB;_2g|9gj&WS!2Yu3jXOG +vD09Aq$h3OAybZ$FfgJpX4KVlir%gA;gJHTrMc&yMMYtN=3FOpA!~sik)jFWG~xDSYkUYl9WeoV0!_x +SFWIr7l)x6`31v8_Ir#)NLQu;nS3e=migz4i^+W6#ga-E2s>oLD~LB#Ith|NzjsPWdxf32|iQFHhqNuup>6O6F{9<-BUCdFyt_S!SX!nCL(&8Y09K> +@<9AX}JcDuu;dKB(#GQIvXCeSDP*SWo!{Wc#P{oStoz58*w-y3j$U*m2(n*2i1kNf5KcbKMEQ|IjS;- +>K3WITla5Ptdi!Kd2~Q5pnbLaG0LfB4MpqhQ70)3uuh60FOn?XXkv$+*x(gY4s_<+61ed78e(U-eiJd +u{-aG4};{>Q&j})b+tS^Rn&Mh8J~kB*Als=QRMj{J6ubt|lY{wuWGuM>#tEoCz8(>+U}4ZBGVZ{r)x& +uh}aucGPYGK^0z2X+bfP!BfR4K=Q&4Sfm1OLItMmqh|B2A0(TmnQT}$tJqBfn#< +`ZaEl+bvnsaClg7)BqD+qOt+X$6=@iY5?xh))%{truU#-mhX+jqbF4cSN&HB;?kO&! +2qLey_P?Twxdejmmsf@<~3iDr}Z`MYZ`4Z3|1rS4%{8}-y$SG}(UooI0F-OBuemG}>bBFL+9`eLP0N^?e5?GUO +9P&|r<#{}DdL5gi?LSIf2+YC2(;45VhPVOe@K88lW`jTKkNYHSFpbi>vxWP?Jljg1a8oZE0CYi;!%<) +pDO39;E$jw&_jX)iUf%>QoNONVJMr<&FVJK3_A|7(~&xto@)PqnNA?PSv$SSQ$4ISHE6!T;|I +}y!Z-CTWT;xdH{KikMir*09?MoKXt{r!&zmi>mIK@W!YmJnc@s1 +1;_{5QaoPkNsAVwj%JLT0Y7Ey+CSMBLos*T(cv0fEUQzpVf-@!NS|_UKXN8%+h}7lo88Pt@Fh+mOofC*ol(2}ePk5s8G!LH?7w3cky +_whwYZ_c!JX>N37a&BR4FJo_QZDDR?b1!3WZ +f0p`b#h^JX>V>WaCyBNU2ogE_1(XMQ&B_$WVPL+aeaX6^|F*=#g&?@rPwi1R%Id;XJ-A6B(J$ytM(<(uqz* +neaJ2{RfX)btTIs~`9#B&SsBGyQE>_HqE((n%S7>bC3d@{NG<-UemsPts-&tFAf;Scl(XyW%j=Nc+@7 +AE-Q0{~iejB@YMG!w7R{e9_&e<`h4ktlS&`~*8(zf@ZoJNNuJqz}0E4f=7(@QJ;-q~e=&|a*vq~a-i$f_+f@debq?P$Na=CXx!k*I5AsYX}#K2)oHJuGx3KS4?e1ou_QcSt=mKIb< +}+Vlxo`X;B!)g^qF(6y+exgMUj293Ppd-R-etz2@LM2;8LDH!88u;5z=8?8@8_@*seXGH6E=E)jihN(>5tfUa(3KF3PLfEcZkMA}gu +$Dr&NxzGRz1NFGc6-#UZOdk`6F%QtCazgAcj=1rBrzqeSH*y1-NKGgyC`yJ)~3m}W>RHrTbO@B_yq}s +v)K22Ugh8!PDu)_#yfWX?EK8!}W{yI*S;;vc2*eprYSi>k^@q8Vy>F?|-czV{*!`IPjM{p`?5&Q;%s9 +L*uq4OU0%xdn9E@2=+5%Ux*@c|kI!W>0$SGGJa=PqyJf2O?#kp)qOAKcbwG(^O16;!T2&Tno%UNC?=` +*i;8@|t-=LfwS8Ph^%XvHWG2XBAW;ai66;ja(tjVib_m*O5Ra5f&Q^ACR-?2IhP8`^^ezjBWKmf^(ww<^FgS#k+o#xCkbBB6xLi~b`ra5r@OJoPQ#A +aH>L=SlLamW3XXT4fE}xz%Od3=i9EwH@z2B~|q$w*LuK;0`+cec})dDcGt25*#~_ZoiCe3-uv;97Y<< +O78m>Od#5^@tSK##;KKz(s^iAlK~+sz`EMkbKj^6j#^M0hC0|n8%v>yqJbY<7}pg6?#U$> +LcR&oh^Om^7OP83rcut$7uK^7Y2&Tm{wM*T3m`~UdGytO9^O||WZ;DO59#@~!Bg~+M&+S8B)9xvRrk| +H77_JL~`j(9M)InOwQz`M{4lwjat8u)ZD5#8RLT{p5t&E6giON&AHa&h^$j~6XUr`tJZ#jUcj}L+wOymk`p5{-G4Vh&<2`Iwoi(g&w56NI6vnfv*ZDA638k*O%P9uX@Ox; +Y3&_4bbn$8I-!2EruR1UM&?BOG27L?h6(|f+{9_jhsTQz?REqsS&M?KqN{k(2Kw%K!s8sc2NKn8cD=Z +7}JwRZEd%>&8MvJb>Nmg(!|4fS#R!9S%{=!!U^oNAO~3xd^3{`Kh2sJhCz3bdi3l6&svx8G5+ZDoIz+ +w?KTxZ_52(gKaS>_^lY_S`?OmgjkN7HZD&K$xlaVwq$TPr#&r3J(3`n#*?b(v52O6zY&+^svxRSv`_T +n3rN0>p%%<6=}`(hMiZsLW%okuI-%A5;|^Ju)XZ{kz8nx>N-UF8kLeOR77`AyM{n2oPt8fAq7y>3|}f +Lvb*2BTwrFDiNwL(!jSbEqX;KPBXU6UQg+H=U5h>g*^u7s!+uKNq$8h%?lQlHQoij#Nkubw9^>H +#_*AC3Mh`mtgn9znpW27ed4}$6xFbFfcW*U!oyqV}ng?kMm)_8KPDRqM!y`$A{x0)l>SmDU7*BV9a4% +CZ}mt8stAx8DQBhJ7`z0^8z=-Ic3>Lm(x{$uBaUBisUw`bUUZG60-=4&=`G_ny3nG917|BM6>{%h~*{ +3?C|>QN1Il=Oq=;d$tI8(@eGjqD!E{Co*Ehw#HW+z_^%VW;Dv3URO%psc6_;dcruW~44HswV;d^_ocU +lrC;?7)aZbo3fMwVk@y-e8;!c&7OECRJf3hOa2Y}w$1D%S#eUCdA6Am9yn-&*F)HMl9YM&9S;DLD(Vq +BU(;(l5r=?#l9FtkuK$2YS?4*!OBAkf+IhqQx&r>uF_nk2#K~z=kO|P$?KP*q6Vs)FL^(i(Rtqc}^n%!08uHfuAi@t +Cg9XPt4Dtu=LB!!n*Pfd;F*ca2JAfV6j@6aWAK2mldXPgOsgqC=4g006BS0018V003}la4%nJZggdGZeeUMV{dJ3VQyq|FJxt6 +b!RScd7W9^Z`(K$fA?R()1g=iaE12OSQqFfDeiz?x4TL1B^wZoMA^JbqK>4J8bSW|o8gB<%61!E9ukS +1`Eh1ApNi-PsSFYJPb(fu#kCw&#r4Xqkyci$<@4KaC;DA@l@pmh=vAX+N914zrZo4Myv3{rg~q>{+`CCk|pL=_BuJsT@GGP*z+S(TITdwC9Q+Qw^;8#! +#Q~kt+@Gf!=Arw0od_cNHOs-f#utlvO*OQgyYoQtC5=N_HLV>%ev4UeB@2pn;)d-#9f)?YN=C! +R$e>GrZa`Gio@np3Q!JdvS4feSMNe_Knz$ikZk17xCgLu8td4Gs6M{ipBd!p=96j-kf3?jc9f0eJSvX +z6Np%tLIrdp{w{DpqVcR@wg(};G`u`e70*jr9JC7rNwHss(C~7UOv%B^voPLHbfh>CU#+iu2+z^AIKN +k^UaFDk3%j+-^e_Bqc`M@&ejAuJyU`Up+G^#mZSRCZ26DyfV@n}H(6oH(p9ShXm?Cs;*RBb5@sJ6GB4 +bD_!Fs|Aw4K?0zW8HDb5V{aYD^^LpwG&H83deti9mZ{U$p1IEj>U;5{|`yUDXC@Zjvshaf;_1x8vki~ +4u+bp-DwJ$PXVOg->QGzavTR`h@zQGaZ;Toc7R`3OPVAAp!uoD7OL;yaOw=&^w(h=E-6RXf&*f>8*n> +taT-z+mCKX(g;eBq;&j=f#xfP3*DdbHw}tCf~L4fwgeYYgZxN6My9G_FWO*7}%c~qw +)xB9ZM45lMgq4`|*99zN%0Y@8(E!bfDfV@7>87{B1jl>qsC@=_C#XwK0bkt6=qBn!p-I?~OVTaOegS( +S!_cQuBwguUlRRAu<2W(Z{srSl#SiNogZMM2Zf8$Kb&eDU=tlVN_`HJE@_fn|)ae6{$Hv75Bx2P(x1v +5WtRAg5s+Z52H=iN7nJ$BGD#E4YS#RJ;(jfNO&80ADBwiTqK-RFK%4tQNj&tn?=T5)RHXApvV!k=RQy +-sj$Zw6yz2H`ByP(Zq&nIGX!?E?2KU5Z{Y+?lx}wkRLQZe?JYcnwvN|jINkomxR30%h9e-E=9Cr~dC4G;6b +;>m0X3sB0R$_5v=2)aN##yv!+wB`|8@0qSQv&h%NY +YUW;_LK3wM+v(ho6dOODEmQ*%+KNtKihTcW<=3Kt1B>|46cRv-fPo4o6gJn|^NY{~T?%Mq*ukb9M@z! +EWA+RtH_TPw0Fa@C!Sb)AU=?px*9^{?1wv#A6B#-XH{jNOlRPi!!B?F=o!% +iZjC4?lgv}42EpWU%zfA7jf+)BQ4TFO(j{FI5<|^y$fu-d^LdW$hd2e(3ghM{eH#UooTW$^9atlRyG^E2gu9uFnWObbbaTp8A45yQ{Q*)Fvr*BTOFEXk?-jN+G#So-0RVf8AM_td015ze +>ZNP9i$UW^jw+}T~xs|NWoF6&rzzs!^TF_*^aw~Rru}?U>I75KUi=~soSfu+{%QMFwtL+H0$wXmcKJm +!NZp6$3Wzy%P12xC&71ujvLLp|JKLzC`{8Imk=ciFYKnrxzJY|eBf +t6_@gZ!8T@U35#+(0+LfQKA7;=E!M<)fBp`I_-yloam0DtC>Pv~H2x=(oVGZilAIXvvL?JY1qu($Udb +f@Gv`F1(-%7JX3mbY +-p#i5g6zj7lBSI8Qd;*LF|#0sO^<9e(p{wGMxF5617d?A4L(ER1C|9?4O5tCK^Y*gxGj!yG{a~tYbcP +iaYqGlJMf?dP_<`0cnk^y04SlTcsUbAMvuM^!K@k@7#wxe4{dpInhC#4%}mkTd2;@pSEIo`+H=ec=b1 +np@BlWphWC}k2=~m#8$S{isBtWWM}yzMmPp5YXTVZ>yRYR4fM1^=1@U^uUQ~{=lzFlK$pxX1kT4A92` +_VaT`-*b7`Zx$`3rtC|HaoC_gNF57nSTdG|!m$&#OOfudZ*d>BUF-+wGUj&sP8j1G*pBQSXyPXW%Yw| +NQ(zs0G0pm=pgZDDD3S@&YjZ)6ZEiuiP=0z;tr`$i&N0VzD3h{{T=+0|XQR000O85nWGJ +pWNiE1O@;AsT2SJA^-pYaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZMZ+C8NZ((FEaCxOzUvJws5P$clAUqV +40a@D~h5K-Lyb*F6LUOG{^)4K1o9^RT+fe!smu7*g<7(I!a2OxY^P|0IQn%lqzkQX*6mpp*Q6I@0*+7@9(EXWDSZrt +Fu+ZMcMtQMzP`%R8U|&hzL`v&1(E82T7P5q{Q-Qjezh2rrqG&FTAg?*&LF;Z=P1M+Ty2T!5ZqShl>_+Wq&u1gDBs1ek8g6Kjc)-j$XxWR(Ii# +(S=0<=jN_3i_R(%z8N8k47Vi7_5LVAun*F4SDc+^?|&c1nrtU_y{Z=#><{R%d!)imMT)#Ie3Ypm-|AG +BX#{8uZW^&6a86sA2h4>hn~5ItAsUSWf`h2(ID0fvb-j*WCd4VCn0v(9(a~dXM;d89D-Sa|M0ZH;?l` +tahB*9#B*gUNlU2D7TXB9Vxsr^{7qN9VyZn?P*$S7P_$OC26dsZR>cMTT^L8YWqJ!|gK9HZf!1`z)Fl +CN{fcWTTz@-PL!+^4K>hPizg)2Q<-%PP>}jA#rdKfwt& +SyjkFPRxo%Dds^5pYyj?-g3J1}g|6!ILA@YG_BFu6qh8f9C5MBaj^^Z9XS#iL1*m&ksdjTDZM8sTbTi +blEUi0t7U3A@wYEn@o_X>wCx_dqvFHg$~qIF?R>PlaTu2cs*UH`WQuEu>M#(WU>wC?Yoy}_F3dL9ePvNknlLdc3KTuQ^bA5lW^J=ig? +;>K=gX3I#JDKDhP=NF#MR(z%l&suj4a8cr!QL_OR)$|+OmcLeiVo=hh_vZGz +I-GHr=9=&q+jT}9_L0hrP-S9OFi`wPD5;l&_8?0 +n+$sDHcp?vL1wqF|9N%y`nTID!*O!KQ*DTaOVN?_G%&>byL+JT#9ZZlRMKxx%_zos^mIlJcVOlo(>Mo +4m7GtNcg$|*68uwC<3%|C18-{(GweaFBof=)A=N*gNC+IR_D8sOi!qh~;XM5KW)Gjm_kR{`fTSDMda+ +?CX2nEB$83DCBdul6?*{@;JKd3OEy%?qa>>HLjLtWQcT4g@y9nfV_{R}5Vl`HMD`^l1y&$6W1>50 +d^?JZiUHTk4T_xBAc6e-Ag0ZeiKp_3mVG7306QQklllPUEe)q^p*2eQ;R}AExa{;RNh!63b}MF3Wtx8 +`1<-?|}+lJ)-iFK^BI^k<{od0IO@L$|P(y@S@2hSRxhU*F(wtz9|Z<#&u)Ii9v+Cu&*rUWS)JJyh;Yb +B8sDFRMo$C`wvh{0|XQR000O85nWGJ|Fh!d!2sJs23dpFf^lU)AY&J#FqA9RVE}MlwOCyty` +oqgn1!lVea~c*tZw4+nu&GH4Hk_Wu*u}^-ObJI-Q6VeM#;D=Hg +e%}sG4|u%9c!hw5XjVV6&c`f?fxiZBpabR25QCumo+MiKUrZvds%(`WRaOM5Oa=!=1o| +q|V%LS}#jv#coymPqaJFGQ#gqQ}ODZl72KkFo;OA?9%7xdhZuc4rbT$7{&8CZ|yssfJ+^2d=|5}`vbN +OFKnZU&2#=rK61@jS+P7HOOliq>y&}YnPH7{%FP=lEPP!Ger{uhJaOX& +CO|%660q`*%#u#10~2^`4c2Z+qli>)#Pkw=QC~do&g=I7#BqmtS`$8kc0urB%;;Mwo>S1XHk2Yn^!%| +qY%EaFZ;I>mLKMDPRoHQl`Q|?Wf;YK{N9^*|x%gjExki`QkpuRTYhSEbzw^!#5kufpErtem&~V2NXZZi+!S%)yBO+1yL=Fvf1L+pjfeer7<{N2njGsmv18qj+Y61jW%og6DI*{*qm@hp9}?=a1Er +BcY8~3R}pe9JZEPzJV;2{LWIx-`Y-czgo(c!JX>N37a&B +R4FJo_QZDDR?b1!INb7(Gbd3{w;Yuhjoe)q2shJp=_*mIZ*rDW@%w2*Xt>55S7v#nK@JV|a>#{Tj7RYG0=dy><=3YAtnJm}Bx@a(Tq +N7YG2S#y83n}QHTVRE5Tdv9};Bq*A6k;8By~Uss>=d%uZg004*?qsf-0XJmMfOfeOMP*3)R&9<+wHfz +4ZFJ7U19Xy_V(s$IBfod(F$8k2nKk`il$djHX|=aIAFSWU5^EO;=qu6VvyG0uESlznBpxkmPS!jNNz2 +;!o0?^lku6ZBZ9v;j>8jg5f%s!0uf!|o74rDq~paCuhC+PMmz(Jg8v=X38dhP?1A(aO7bFHLsqL0E)< +3v_|*#oEhuM6B_t51NvZL^1Mc`D<8JAf2A)oNlTmR9V#Alt+HF>~>5L($s^4UY36H4ijxu}&kz{ +an!XHd@=c4vARfJ(qx%94!$i3M4uWOYt%w1R;I|MSELDNGbUI&zHO^^Vu^q;>mt+Z*mTNWG2%ScRr +YN3)vp6S9doxhtd2@NxBC~6GGjf}F|=)OxnWlPS}SuWG7AX7$0(bnVqXV`@`p8U)|SZ5xCkT^iIj-nz +;yTNafpql0B8F`ts7hr5FpvWau!hCz6f$?+h+!}yt9|4_pq<@k +PPQ3DT_%^%MQ^Jb9271gSlO3+KQzJ`ZbSJAk>Vy-HDIzYm+kL(yrqgv +FpcP-B|0*~cm2cG@U2IRPi1{vxP?m*kzAJh@>fdUlUCWHREVOSU~4?YojYNP^>lG;$HF4g4OmPtxcgP +)h>@6aWAK2mldXPgRM-lsxJI003+R0015U003}la4%nJZggdGZeeUMV{dJ3VQyq|FKA_Ka4v9pbyLBP +n=lZ)^A#hiDj;Q1&rx?T(MqkJG1dKFUE|o)4qRB3e%+A7Me$j0!R1paK@%SFaI^jCYqylZA9DeNg&xgZ33cg +;@IH{Gr(SIh=6h+ac5!S*NY|I2C)1K}k;1$R5q1IQZxdKW}Fxj;XL_Tnb)umtKh^|<#{#? +8mEz@Ylm%-E7AJoy~6R+$?LlsLtAh~P{@Me>1O&k6!29Q_&UtN+9jduNX=6p5Q4wQn}Jz0cvCt)CuN3sNOS)*$wKcY`u|W%0|XQR000O85nWGJEemJD(g +gqjni2p29{>OVaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZRZe(S6E^v93SZ!yT}d;enP*-V+Vxy0)bjnFvi)i&c!zkedMqSC)E@r8 +?LeeGQ^y|Y{K*G)%+a|b0^hVGM_;H^Ir`cw${B8R^qs+vm1XQ{UsQL5HCz-uu5Yovuec*Z0*#mVh3s* +C{9!k9Dgn>*zbgoPkKtD1KmW{qb$XASxFui!&}W)Gg?l?tmt0((QGXa00`_4O +)8*Wb_1K3!iga$2FXkOYhR;2C=X6jcK_0VzmsTdWm_#f~qDA-`&3P4g0*wx5LHB1a!VlAt$(Cb{71Ga +p!!qf6SG^CgK?+YRcqEvXe30eQ-9&-~`oeAaWM+UaFrzO=ia&E^tw2jI0D3oT#U+Y7P_CSCjOOV}lXu +$nA-(n(QR?1`%SqdJU{^UZ8tn!P1nM$|nQx1fTY1{@%DZFiQK09LrKqq6q&_kt=?@j>;2TCH@}01Bl~ +p8{0)V)oLiVz?t!m918*y2Vo3fOFnUXVI6fR|q~yl9=yvCVRjH>9`P`i4lbFFl}|BA*-qd9T~?ESpmY1QxzCsOI2aX~6b-GK@so#-^nUL*wq?WHV?S2vVlaeOHwXYFyq8hDL|3?n#XLf_$A;>1*Y9Sdn-IYHP;Ff +@5YW7IOmT8sy$K-+T=0)JI};oE$oUG@^TCD-cE5d$-B&yiDp?fOTKAVgrLo5TjH*46|Sv-27{x9K7yuC1n^5{%W=L@ac<7>3B>9{rM@jKNrw{^AElH +F5d$TMi&Z3ndfDf}>bu`vpTY6ijFqrmctJjuqgY*QD$Hf +%TV*4t5M+m%C`MOY&846>wf7W|3TZr6_%A+M^NfwoN*H{dkN*b(B>)SHl*X1AU+zVR@s;p;HRZKl0-3 +;xiJVB}h^(-Za^&fA#HWPUU>jOa}p)|nt!ffGZob7Kd1TX0AheFy=m9c`1>=jWm{h?LuE9DK@n<|%4( +S?Ul9<*RhxQ5Ih6h7Q9Uzpsv%-IAvKj9Y1xv2Y-}9hz-1byPG1m5bv~oQ?CF@`ZEwt7W<>6BO4iFeYF +2Sh>8t*j8MSMlSRK#(5B7UdGE=E1LpSo~&cyC{)X6HB(q;&-4A_4;mJ{bhZmq5LDfkuv8Of=&usJi_3 +t(6ry4}vZu*^d_YWBRtOe7%~b+I3GyGbT^$0P52h=|V95V4;9=Rm}9sgUZ{V;ia~FE +09n2wmEI&4?^>nOH{LPZcNsIZ3H`|8Q58^0YL)$nU{9%W?kvfO*=dqNNX*b$Ik{%o}&J{BYgsvNwmJh +G3u#qW7W&KNbvTr%ac1_MmTnZ7giRhiG1rIi_8e1S0>e +1>&2`BpLZAEh$B8DZ-%v{f1QY-O00;mPT~AdgQ6+n#1ONao4FCWk0001RX>c!JX>N37a&BR4FJo_QZD +DR?b1!Lbb8uy2bS`jtrB+RE+c*%t`&SH9ELH{_p=Sf$VmE1lE{bM>WP1q$f|15D8;Vp(Dp{lGf8QZR> +cf@~MSYP)z8P}fyqS?`T5TLiUDra@tG&?;=+?E|SOn2;e&Mo1+wZw!OSrW2r#f!nMwD&?pM-U*9@C1p +U@xdDZHktc&)mB%8g8D^*koGao_EqQR4>A4)u3|c)<%?vWY8L+9Q$gu8jnePj0}twS}`_pLU=Ta%c7J +bANC1Q3enP?DcekHmuT8^UF*px@M +6I+zM(OGVJxMVyboqPJVc700uaOqgWdMF7|1~_s^gj-cNQuC!ip7oGqTH|H>JRv=6>g(3fL|_1J|1U_ +(I6iX;d)nBQkPt2o7pliJ#?2@=kM(3k|%&z4!aLrTAXhkP`11IDdD7#druGtBGGkR`uN>})NtSyYN?; +Nq+?68b_10LrK!qEWsseF>e2FeB@}5{N~Bzbxz`+)vuMuZwr-a#Z2U4~*{zaC(4A{LH-r +OYgv6ac_R0!}MfOv1jZLKRlrmpO`TE2#v`|yffoNQ`WVO*+|=~u`| +JZXi^m{QD(+FCeFYb<+$gHw6ZS*5j0Gtyl$|kDcf=5^0+;6ToJ-jeV_Eo1T4Jqx}e+|HsqXD +lQwJT1cj<)nl!T;a_g7o%JPbtgZ#=IW1mI4v*_|kKVGbx$mR~d&zX+1EZai%_C)kPjcnYX%)Jxb5Hq1 +3g4javzS~zcuXF#h_K6Hc`uqh#A5Yv&m?cLGGdo|;E#AQW?aeJXUfXwIDVYtv8vs9sf8#LE{V3e^RE* +RZBq^z3iVo#5527y=zqCD@qMeLH{ge_xf +bu|A3e)Y2Np+<=5icQ-ab*CB{S+5Zm=DcJ_Cq!Tv&Z0ah&d&5Og>UW-=3)IebZ5pI6csdS7Ob}BVZ*Q +f06?owwRfxZAM=JU%h!sNDA&EVCIJ6z3k~?_?Ia7eufH4^4r_#msx)C%#+d^T+l&5C*)`Okbp{V3FSY +UmVNy=U!F=gNyv_ud-`X5 +kB0|XQR000O85nWGJ+}tD%1|I+b?t1_LApigXaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZRZgX^DY-}!YdC +fg*bK6LA-~B6aaJdB5kr%{vmqpvngV(#RsH?1ez>RmsE>1s+YFRh&F3aC%Yv>y +z46Pa=&#JmA@~kZWTclOC72}>wk#AJ3)N?vy%%YhHEYqOyM4C-fF}Aj;zOF(+ig}YRp;nI0G~JFLtivvE2{1(zn|0do2;qQVwE<1m1b?))O +9Cc6x}AxOOaLmZvJ34HDTLjUEji-OQFGi{^rf=H*?t7r%z#DeLcmeT-ZyLKs|4ox|s(rDj2scGi*YSe +1nvAo>^lcdRo|lt#=Q$qRW<9mzk|a^|5H`3bEqt0;?+9cvUystb;XHT~n8;|4;PvDL*{WyD-+0HiG&p +iZ))op*Oy9n8k3R@pWI6Oa6b_H9+K|F_&Y#I5HIADStm;zgm?=-aP{x0l&-xdYeknapm;dOR_+6Y8cg +iT>v-1T0cy6AUc(;Z90`GG02t4@ugU2`Tjz5{qB6voZmi0ApZhb<|YhtNe$B$TLQDBl9eV%n8ZU29J9 +;xuKe=i)w9>Xc+Z;GAHSo+V7^vSi +$`5zU{+R`ati<56d;~&S}B_Vc#+n9x9huu2!vJdaApP3TloGA=qd+|wV&<=LZR!k5~PE@6cCaX +pd;<8rD)Qj6+<Sta4^p?kjj&?tZ&5fyb()8grsun#DBJ%x(9w|t_LAy@T-* +Yvj-1K+OQxQHZMv^_oXSkeXtZO+7V`+Yn(h~=rW$3p|VBbnJxTao?!9A0m#oJ(XS+rUKdr??DY&F49+ +}^PlI09FdcO#7FP=Scrk8aSgyBqgm6N5sk{0^fYl;yIPNT@4Zy2lr>C#j|$O{QU@VVnZcw +n^itr~Ejms#7Yg-Zn(jTQBb*B^$VDIo*4aX{-LeVy@qi{N~J{vwD_{nyF?!So-CrJ#|rDgf!a2kT`YbhTW`KFFYHU5da^>-hRwsENcf*0`iU`W8? +Kw4jh11dm{zb#N)w##QiVFUo!hE+zgKgyDs5AII^;NQ=fAjMsOcfi4#U;QE-i=4SnHv_om_A}GAgp+0 +|>n}=hc>c$#z$jsStsnl!KigHB^`>L+Qz_9oj4O(p1#zTi_FmhuPC(&$nr@p|vE!aE7J-UL71~q)=Zl +Jia%bogw7$jwTtGRRwBGkoV+3Xjfc*42$BFnLH61KpI;2Si(a2kL;-X$S)PRE^L^Rm9|lAnMZh5c$H% +3Wf^KyW;(tCJ3A1r1m36+ZzZf;$52I}d*ATj?~Y6fn6EBQagg +&bGr>0sR6)z9sC)F{=tj#ANOh!KvmHKo}qJ9cYw5AnSrntpFn++Bu24&x~kDO!C`%)$$8#^8^2ieyLF +Q-1&Z3yUAamRQm|d>gKH_ZdiZyjQr(@FK;RDMmK#%AChG6)=hPd` +^o&Hfc1+Deb#Q`z6?eqV-`EcA-N{Bo5y0cWA%>&;HLTxyrl>dQaBHAw_ZzW#CM42+ +`?eBPQaouFCzQSS>0cmOxE*t9(-zxoDHiQL`WvfZ-;*x~1JFfVH)qJp?D>n&Sv)kP-U*# +@3|-{497R|GM(o87C4va0;#cL%xxiS%LssXAMjQ-Y@L)iN_$=2xdvx>b!3nP&0$k2?I!)30mOnc7@|l +5QXJ$!Ccr(gCVIkYG?kHIH-pu*~`H9C{BnZ!Mv);p7Jb*!Q78*)I$(`Q^Ezkn~OywBnG{7OKsArHn$T +L;Txen1JgDIsagfait&Uv%*sY&OC-WYS1*>pD7LP-^AcZ-rJ?IWcvsDh(y2!Gx8!JC+TDR{6+XZK0}9 +3{CbA#2*?LX(x2T(Qbd>;E@7dNo3oupBpS=C~>l_-je4P*dnuTZA09ZO +T)Lj-%BY~2r-va_ntLtv7b;KxTEAA=>&?LHNS9H(7KfhmRsW#G0)^5Z-V!HJjbWWRIXU&~wMwgu;3%v +W5?4yuA^~PM)(1{8<_)EHO>V9XFnkAQU1ZrcG0l`M8;AWbq`8^7E(RB8N-^~r^W_RiK)iE_Lg79(%7T +d@oiF7p>k6b;6hOi(TDF!cSx- +!kY=(TQ&n4EIL%P+l$qun!54j1y7;B~OcvlkB8C+HMH~W_k>5$f~CEU{{K)6>?>QSA{?=766pMvN2k% +e;wJ!8W6rMK9OY$_*Z2Jobl(;Xvg@$QD5ob%wnbol2|aCPx0Zogr+@{#b@&XODfK&%YpU~hZD)(>_8ocKQ9WbQ{j+Jyg7bu +oPFYw&YhRHLS(_X(CAQ;)QR7wC8*);_VMpcO;)8V`dQgD{UwCqr0YWEFzFPB$CN3L^-!MC}tTM>otDB +t}E^T$rbi7Dazo0tH7=-f5#8h(fncaAxm1Imlc2gZBPAnZX9+Q0_vBY8;p&MIIy +{bb_tcqvObfx8`RX=kDctV1n>PHY6ospB=E(R$M%RTdA+V#|7D5M>4TZ8j}fB@|PNt|7`_-{0BG_%pR +3dYLfVX)eEXW&V%Mz7TP_lL(ULuW{Rp8@mPQxbj0vJZ3OFBUY3)<;<6O2A7mTGgpWaVMOR`Mb4BmLno+seaCWFG(IxbE9@9 +>YO)P;mTT~<@K6R1P$5^cG0UL75dEtny7R*l6gbB|^`_6_;DqktG7&9n`NTBPuK6vP)`mE5}Yt%e4(j +bF5DZlheI3+h+>yuzhkh6G8 +5Z`+m>T_|R3-@UlQ@L7bbLH>pPRj7Er<`TNh((93>q!kmYK^9pR291S>j-=>{w9fK@ouY;bPqQ#jA_A +PhP&X1(YonK*WaNOtY`I5EbTz7F2e)psISRs8bkTf&A!k|GC|AEbATyZ(l!qeMZ%1AB9uc$CYJ6eZG# +^2WA~_zK31(42;oJup35ixJHtX03^ofT8PuDu?!bwE5-{j)Sy$9s6FvpvAWo-yE^(vr}0~iT;uVb`^#_=o96Yp|oh8cM?N{ +}4Z(7;J1m+@5ad6C^oO@UL|m%m}^#-+&Lf!`x#B)EP*WoW;q^^b*4c6v+z7Dh*?V_}YQ(xPvnGg&Sj0dnRiLWxW`RBef{v6VMXURYN?1Rj^g&7Iv8u#}m=~3 +*VYytoRpzK8ud&ur2RBoyya3bNjkdE`o$z!VgagCxvSv_or2Gviv-bq&_Qe}-^TTYfX4-U9q_+d}5==KSrNq@Cl&YbUxnCyCA+fC!g&N?TC%rT#P*6Tzf68C{0MVy_p@WYQ>vKV^;NZum7&e|de#H{LwH+AN+{ +J@D|SOFy|`ibQqV>)8?XPZpc#)FVxxndccgJ*cex>6n7c?wpb9-Yn!?(t9_8i+|i +ly?h{3v5o4k;$obI40%F2a15W9sr52*(*T{z!?N$jgk!j!8z%d>SkEh*$P{(z&#QS6N +YFhyvC`5Bw@#Fa0abJP>EFwVtv7{4g-ero6lwx3GheiT(AU31lz~$86D+L%Rumu{^7g3zjt~aSkd46j +X|Lnv0yl+8X;I-U6)0wt0pvved#Yoxn7ca(?>Zv!9x1ob3T>XbhVV0qOQTaVD)l@KK +Dx$W!n}G);vF%_oFP3*T*c=v>ilETDi$DbdcrtTI%2n%2mL@{Z_5Q(ttiI$Q5#C0J)m*sN{vjf+OJT@ +Pe+9G6RfMMZ-{c}*qBkpqPL1BL^@-78hQBx^Uu+Q0m_6`ujug1rm6e&29cDF$Pkh0j0M(YD?D+4G5vT +0cceZHqVg;HCYHfgC44w42Sj{}O9LD&tBn!Uxj@-%T{TfR7}9s7<~Tb;*FfNUbtMw6o9Y>4r6+nZ%=k +lm$3~a#YGnJID??*_LdnF~?2iqV%v-`U9iDf;pjGU=h%DPgOwVjr#kq+#x +2braU08!q52Em&Ip$kXQx)zKjhf>0CZnmr+fGX|lHU;;D_Z9V#EWBv)J-`t;B;Sb6xAj^8@dul)|YvI +o+SSyNjR;E7?=8=s7S*i-ser2djy| +N4T=ax@^tIKBV|!3`xf#5I^gO3~{?LhwTpJhpKJRWa8kE*tB;< +#(8PFNJ2A5JWwn;EvKGNCk8=keQfV? +NFo72Xa<`0|BDctuPVm-qU!D0pA7^w!5)J0Ler33OD>949^m} +RlqnmYIcbi}btwA-BA&plE;NQk|KO96+HQeQY-Y3T4UM^-GC&i_N>`b!4-GoNIx{g(p#3f}7 +krC3jlmAgsg{0=Rp_PzVvr0JfMDa8z3lPKKsZ2v4Uj+lD3p-uZAauxZuaZ!E|H)?&D{2%kX1hy=cp#dVfo-(CD&Y8TaXdtV&0&wC+CSa+PR<@8OS7*Vd>vrwg>FF9YM}Liho73HX +mz_f6Q)=3tR^rAvDCNTp1!;#(JM5V9uBjKPyw&M!e4nuEc9M}hWwolR^j389Ald@>bi|=rVNfeV;r^qAZ@`{pGjrmX<%79>?-zmZxOCaeAdU5#uci(@vP;bJ!WA +ZQxb=^{zy%&64UY6%|50iVA&zU4)xDj|7G;7SeME_36G@P1OuirjD!<&*2jy7n)H3^peu9PJKluMm+H +IU41x2Gg>;@#lrpy-jQK)y#8tV_u)_Fk!rLK2fDFU_u3m*0&ejmDyek?n4tLJl1|nFt1b#*SZt&nlj$ +a)52{*+_DInNOCDsVpj2kE=cO)Qug?jv^F^xfZ{TAfZgGTdv)Y0e3PW?eeiFaX(h@7Dq_zIsTBl$&(` +R{u|9Gpuf~oRRy&%+uv{V@#xy;ecgWvfGHk7Dq?VKD9hq +#VCN+1=+Zd3TL?Qe?oYZ-?ye45EV2_is47~wm_sJ0&mbO)n=gYxoh{9hhRh57pXXXXw(T#G%r5 +qVFjO+N1r}BG@?HzirQb4}3Ihi!on`|C!UoboPqE8wTq2abr)~vU#eV_;x{%?3hNwDG9JWO~K^~dl^r +o#9ie$1OCjlxSiq&0VB8QA@En`<{voz%+1V@?_nxDI+T@-bm`H-Z5R%h0tWh`y_$=+tgB|ZXh`0;SX5ZJt-|H +Zqc2uAI#6)(q@ZN+dhmh)X0L}Y0bBym@nmnRYr~U6SZI!&008!sSQr;2CSJJO83Ct(G*DBc5~AdhKfV +Hau-Tl8C5*rA!2zdZKf&evhLTIEGW6n$@ZgnXz=JKnE9hi35)Hn^tx2T&39&X)li-@<@vx0d_M>h +&3ucwMWLWx(M~jIdjJ{eHA%?mK5o8-d7<4FpKBwFmOmCPnm1hU1%xobJL%8a1n}<8n?D?pll7z|6WD7 +pNvUP?jY=Z1s7Z}XVM7{QEKlj47@6~tZl#y?)r1zUng*V_b%%-KRMl508Z{^pE1rBw&LfPm>?y9Si?y +h5%ew2}AfG&xQq4e*+CzCJ8Y`IVy2Wc2Dra)F3T#a`bU%OfT2s()g8wpg=fb$*1f*hJok +PQajGgeIU8YOcwmN#m~3f+!!lE*_S&j(P(#vJn?Svj{opDMc#HMi5@S>rY(ZLerk_v(QY?gM@Kf>K{v +pf2b@7~)}}F1(Vzi8AO~AR@em8+m@I3wrH~bsMG7Wf){p7SaZ&sI~$xHZhL>t5alJiR%ccJl&u$AumI +d#pvoM35Q#v9IM_y`(?OTeEye}?;9pCU^Z5P*(smijsQ^<8D +ZFf3CWBQR)#X}MVgol=m{4;BG(3WyYDo#ckvr>~tXN=F&)_LAH^t(I-#fBSs#Wa{uBBv5e6@^yHOwx| ++z)*n2kTDN~zW|>U%1a_>3T?n4sjO>t2Ox`Az2;#&Majm27kyquTM}$44XcyNR9P@KI)?~`HU3#(s9@ +_AU^gP@<(}FJ3VW1T-LUsCxY!l+!#R5C)l!AriT^UXWKs+}6mdnzY*_r|;_+4R`y{|1mL9eTTc9Pgb= +8;h{#gC%2^6@JEraN>`VBXa`2`R~-_PXXFty=>h1Dty67Vtom@fwK=#TjM6RcJnkU&%^RS!arrJhe!{ +)s`r;s3_G_BUm~4oDp49s1KT69=l$Dr=&1G!s9t7|nDV&*dCTc=7&;Pn}m@j*pA^mg|p(hV*-a{&((z +V>~(7M4kw!9YQ~pCbKBfonV5t!}!R#P#a+^H*;@?J(1A)t8g{5>;fso&`T>2xb$EY9TKC`NgZZXRJ7+&@9%Qj-3t?j(C +6$h-g%K5|R@vFWA>|`yzu?XQi$Sm*c<*q9ycW!s{B6wjw +mMfVl@GwY`)Lo0wDV`2Udxa;>Umi`w|O9KQH0000801;hJRX+oloI03HAU0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WY-w|JE^v9}8eMPPHuBxSf>04yI;@5E)qwI)pPLIV +Xo5@H!@)2V?uxt1SSzWdECQkrK6b;^ba#AG~XFW;mSh8HpddR`lePY?~9Vf7k79$D7^BRMWW;R#PV0e5z1Kf<0B3hT{O8?edGU|Si+{fU_0@{JVht0tZ^eqd;j&+mw_VTMhSn?cp8YYfX2V +wG!_d|6wV|><(KI{W<>f7Fs#fG3-P}-QB_DcTOMB3UHf_TU493vOrS+X71Xf)t`#>xMoNHoSK!@yy%IxG76U>%bhNh^vaf2sF1wpu^c;1X +;x{3J>8ir+HQ?-{8)5Ynzcfp`fen-;x{v|GaNdqK&Tv$~S`i6il0&w#r-Ue{#JNY{4i(iN!&f&TsRBd +QwiUg2t*ef*dM3I{z(I8pQu1Wt6s4)g~60J#ysJ)Vlc(L2at*zDnwj6!6{{R@n&)auR<0F+qHqq3U7Z +h6fh60vj8uh5jrccKMjlB_~}NeUNot|HRCJ~=tTm38Nak +6@DWu;SB@{@YISJZZ9e8A%kDiW;V3}~@N@*Z_A!JJ^T6_pjJ6DHp +^tzabtwt>SpMATcj-3Ku+wLM5U0TwKqguLiPyCYX7P+mJi726V8Ri)WT!I`%iIeSTdZ5uY4Aui_-Xc1 +24BSPvR6nkfFvYM=%S<9O2P!$`BMD}=*9yp%>0B3Zl`{EZ`OBQbJS=|+hlD(zwMDJ0_zL@=C&i>H#Eu +Qdw*tgWb4f|6bfPHIy--mrmoq%;=IFk9Z_^;>u*DX{6-O?Y^rwCgxn)l!+12fu) +^IEaofWBC(zLyDU6RZxY3TalY#tW!5IlR1tj-3>#O&U(FOtZ{^VR*fsUL_=knTEO-~-v=v1bsu*caNs +Sia>b(h@TIKFz*DAW(h$FAPNmcC>hQCZNQ@%#U?t#co;Mc33MBliy@cetxiLtYWqTSpy2N4S0vUbLOMRA?DfA_DIj!~kyrgkv_;^ODd-83p^FQ@An!I)sEY6-47>(Ne* +r<9yg$Tu-2ai>{Aye+Kdi9BEB(=QKr>hZwn3|Ga&9NdCeMs%cm3MUZC1}BIv#|y(*8{JsGHhqlfbxo% +8=RXl1yYl`k?^`jC0(pwgC;oWsNC-h!LazEqaJk|O*<%PI>B+XZ^1vI7gG7&OcC-ZwHjLXa%|*_u!qk +#>af)C>~2$-Z%_O`0PoOqL;^9;fpG0$BSqu9(vLIq*Y`e{vu72^&I0ltVI +KoVErJPP!CoL0r^mHMfh*X$>es$3CKeMRWq4&I0D3|W)7fw_Z8E$m +X|RNy(($;kx%ZqTlkr0W%w2_*50!nIL`D%%_f%N`ibt#Jc`|NGoo|6`vK*>vQMy8)^anw5u0Pc?l~`9 +g7gbBE86?*1At0{I;oJ-(1h&g_glRS%gT-`bj*I##@Xu;uM)udWA1P6Aez`>sgVa8xi>56XvWE&A)r* +4^451k1(RDmBTV8{VF@TiCX4rfCt;uIUNV_^4vN#4{2!`z~Bh}E=z@In%T&ao%6DZ*dlB&AfBL!RfIVs23=!9zd6C(Q#S_H5d_fCCDa7s*Rs<*k``kG$fa~)ad +F*09ffYyfqtj@%bNnj9O>OL-YAeJ2G!#@ ++?-Pn#P9B^29}{2@U@jjFh&>zA%!KkR-z@TwWAltUy4y2W$2oy`iO&WV#C#~tp!+|HCsF)!ylA?dT4ebNh9@D&k}lAlWYi|Ok?yVMIVV_41RS28IcwT70ython#qV64x5p1En{3by?TMHf?{bpC3HckjH=Jde#|wHAWK!iwBgrG?0KoXV4JtblBTfu&Pm7vLrRqh)tC +BoDx$YiJ@tbf@@QX* +|6orXmJWU3Rj<$vwLWLs`M(+#V?-r0di?_ZS=IKava9NL9}^D@7(}Tsqo$?(8OdoH9(pjNjvQS(&TdT +5(u&J$_}p|jiD2QZ$|vDL4R%Lq{5!BR12!mF+lZoz^T1$5DK>7^rwOmd?OJp0BRUCl2a@9Q`I1@tgop +j)Cl3xn3O>1pIFt5jV44AUoN# +-GlF@{9FOPL0FeuVt|Wnx8Ut`g_+*Q5xG7OkrYij)6{@6aWAK2mldXPgPg0^Dklt002Q2001BW +003}la4%nJZggdGZeeUMV{dJ3VQyq|FLPyKa${&NaCxm*TW{Mo6n^)wAXF5PR$J(>?Zul5bZNQ;%MEq +1b#M)pwP~BHOsOQ5#0&i2cMdO-C@JmB6u_3rbLV$14BwZ9)FdyqTQ0UEZhxv>rFmZeIw=3D*@M29wDi +9p_PH!q=_=<;=+QoUJr3gd&dWO0Jn?>oeu;UhoGlp5j)T>PmrtHLIg{d6iwfzJVCbv$t2bmv6>oetU9qI-d^-VGk=- +2Elh~Po*s65XYWrx~7_X`YqGhnr>x_vzE(@`*54Xb#X67p8V$B{r`}N`aY=~AceP77WsYOgp&CB; +@;YL!SaA}Aa!KhM)QO}B6h0b90X_h@#wW@o`HOq=hmz5rk_$GwbbVT480^p%^$TbwJoGL|(wVOij+4^ +^`^YMmr-h~*F!jrqipj&P6hzo~R0`O@P+YJcJuhXkC5ytMa +Oqh*0pE&3vJBFs;u14Z+yVQIte6!D&?9uc&K!TNSiVWfJ%js%w=v3M;#5INSbi$)?XJumG ++F6El)3a!32h-z@%2{;#?!3ojvSfx4OiMsbR>?dh*|EHwMZZEBA;3?H4TM|opdMY@fRi(mJFyc*p1YT +%r79VPUYpV-A?x5%JGZl~EUGeoISvDpG%=NxFtHpH7ul(+x?@7+wv9g%ZAhdh*pMuu(3UQ6Pf`w^wJS +^(*f3sM_Sg{DZZtw1srI>Pc;i}1D~wPh_8 +h82cF)&(2Rw+awztsnpcJp6SfpH0tq*Y$I^L6DJ0|97R_`hPFN>4K%Vndvln__|zk)MXF`6~*$eM*el +eSs4FP&3hx{{*q3Z3O{`)DBH;%QM~qS``14<`l|W*aLV(u}c^Etf5vAeb=;ARvz0HitHXO;z$o+HN*( +E3OZ=IWONFfUVy#;#%$4i%$X&0aq}mq;n>r?q18{;m~HT)g&#=hcMO-WGL3-@iD@38BK`;X5^}-Upc~ +3AzNs-J-TDlTa*7VnV4WqJ2go8GW-7G{N445NiHZ^K+JPUu>}qcQ}T#k$K?m{2nPc>c#Kj^1ggWTh!0 +ZEjH=Xpp1|*MkIIT^)6LCFNSS=ACOG>WI%r!ATR0R7n0UmBxm3sjUP%|gxCPA)5!=aalL~{FY`_|Yy* +gUfJ6HA%$UTK(R__@xP5)iqHjt+C=UdSy2JqmZgFDUhkAN0qA)QxM@w`u?;Y`euy8aAYnx@y?=&_3k&RJcY^V|9_I~# +pSB%=`x&(gAhr@zfc*qid;8PR?4Y_u)^9G51HE|c0o11+e)Zwo%0%}zK;sVxh!*aB8Im2F%*_@1nvIJ +_+A^hO3Ad5ht+dJ+KF=@`8Zp4l=|jM_JIrWDJ_ZbyIbE?j=y;(}E5J}7hi{so&%6+SuZf?wt`q7ZSL) +-%Vr}284=yhetF?yo&NPWQNnEPhPPYb{%p`f9Q!}V=u_@wuIEIv`8Z0(HI%A%l44=4;I25CNAc76sN; +p=mPnDSjQHiC3AQ?gkHR3LuBu&OPmx(S29dFu-vNO3C!1fIzj?OOUH?PmnPv1<*b46my1an;=E-amWc +zr|cSEV`YgPX25BQ52Crk=Jz3VfpT*Egs0n<>7dcOY&eSHcw0@6Oi#SiTWAp +A8Y#Xe&6%qSoS;&9f>?1LlpaY&4_C?onEE2#+eJ+s>rLoutVIm{$Irc#4X*xX^F#e?;0|~i2e}yeUcdok9xIM5T8z;>d`ENl~BeD@?- +ZF^?$ZKydg&o$}Mt8zY2fF=XPK0+$jc&6+RknNl@$jiFMc}1IIccKXq$bAmkPK{$)Q`o)P>{pXoccJl +27Lp=85#t+_uoTDlA;9djjf|20hUBlfL0u%UnWP1NJ)gNWcTBl!D{#C`u>kROsH{iom}4pg^#u3@TAX +cIl+!jZ&}cI}w7&JkD}8pkw!?5RB-A+V25;j!WG-5G*{gVIB1zu769qU2fJ4dPaCoa0Z0`NUDXJDey# +Id?HR@P8*$W8h`lZt_?GJnfjklr{_cNizBmP)h>@6aWAK2mldXPgVYyRfB~G008k90015U003}la4%n +JZggdGZeeUMV{dJ3VQyq|FLP*bcP?;wjaY4O+cpsX?q5L&7$O0((9gzPV9S!OSe6z^yL~VWfsts3t1M +|0mDC&d-*?A%%CcJ{h^6D*@pv!K9nBAYCnRaQ{hqh`#ZGhw()F^ZVqk>p#Xo2>vO%xOV-I|GrNCst-eq+q8KG_2D?Q?+pgsmZTLT|Y +(Z5h%3IhA)D>03izhm;vg!^8+SXS3TECtX86=hC075l$8{RxTe0a#ojd;AA?3pZUwxeSsOV++{(X|KG$~k4~u*HE7l~wmDxMXEF%6^oK1xAJmvbG^AK=+-ZrKR#mS!Rp +HqH5?ckjLll)f_oB-`Q$G;FBbY`bG}I+g&FP6m2E+33eh~%Sb24p%W1G7TnS_tR^}nY$z5=_R}*kDYF +O%ceUi<_Ie2p?;B2ANe+}Zk}x3zj02T%qlv9=KMLod2L*>ox^hAsKg?D&=xMPb?az6G;N!T +ugOd_%gtfRayQWLhman$Wr~jh1v^%`PL$x8xp9wK6*qg5;t)D88Za0;%Ir#x8eBKA|jkykWzH3>hQW$ +=nL9Q0+PyB$_S3&J$6U8S7ix3@l!HW=+3NRFEWidp9->X&|*ow+rH;c10f(MyqEVrXtuTTg2y9^(+cd +!GtsiFZZGw`}9p7yC(Bq6=SQsnG@sNmBE6XP_c(MEbt8^{#N-La@xC_??~H8%@-X}Cv@>jMGNViCaT_ +69!rvnkM@SLe+xtPV0h~SnePC2n&!x@o63Zb~LfO +>(UcjMBBbrXOR>WMzr&J+X8#fi|EhjHbJaq$eWb^K$wEFg84AH`ot@QGvw*EcZ?h)2#L|?541V+8Vwg +c?%2~Ah2;`bC&TwzEr%{SsYsr2z$$R^W+a5gk~g`I&~98Zh!kugSl-P2t9ZQs+7n6(Xt!FbK31&__iHa%E&$n*FzMFrm +Z|6gDS=MvDJ3rBq=MC{-dlNuXm7I0*rqK+|Tm2ZxQ~wqiNyzwA-=AKvR6$-|v9L>lBkv;i)6QoV}?Bw +#VydBB*$s7bFBG@`0qH@5YP`~b~~;9v0R-+IQ0p+^Y$%vzp+YWjv-B(k@i +qTR9&Emr*LBvY72|rFGONtlOeA2iDfOA4yCz%nmw&dT_FxQ0-G}ecU38EwG9Rf(RI&lUhjyu@Ki)|ev`&zMHlHb_zMhHN-NJG`NlDBpO(q1=Z>k={rQsHt +DUM`(2%gGmZTtg&_rh8=~n<~Z@;B}wN8dQI(>bxgH7%nCIif&OR3Ya4mAx%Oa#GA&V72-sn1 +BJnGbmq?$J3CF1!3qR`R<>!aHITQ7OHQDbcMV#9#oX2K`mTGBE)C_GCsXI-b69@}*9lqqJwXxPSdI;% +iwwsywqv>Jge(8utx@~UeOnVntk*BU}+$e@~za^>WiI4H8j_q{938r3p}X`FNf(mY3=rYG6`W=lT +`TgAr*wsJSkUv}R!|pDI(=Zo3Q^#>rdqCWfBy_lp#6&&wphoOtnG&2DN)QRSr?@fqGf?4821TQoT%@n +~-rUc%JZISE%bp^PoDb3#9v8;FKS}DP1Y&Wbf=f#*3LMIV<7rdVz?cos)TRx53H$Xj-#g& +AuUCFQ1MVH6S!+L2f`g*-E*PP`?{ASk5HG6rQsSdfxl{t5@Kf!6nJ-;4(WIMzF#>{=0!Iy#7Y~r^<=8 +#!FnXPGIrYH<_Rz!E+?=wS*6bKG|nq9IaXd1JY1U<*u6J6`S&z?Xe8l?i+y74JsH)ki*Q7BzL`18MaQ6U94B)Pn?U&De^5&U1QY-O00; +mPT~AdjA23(G1ONbF4gdfn0001RX>c!JX>N37a&BR4FJo_QZDDR?b1!vnX>N0LVQg$JaCx0qOOM+&5W +f3Y3|@-M>6V~vxg?WuF2w34|fp@>Qx8oP<$a?zm|b7nU2X-7S& +U3D;PXTHtWm$IlNBA08j)SnxH+_F6shM@1saQuxge#%MFgMuzwFAv~Nm8fmaX7{Vb%uA_oHGdTJ6=Q{~MLMT4#G6&p=7Xt5JIoy?x>i=D3%Mf{_`7oYoFrl9WETsMK{Sx{U} +SrApTgdf2e@#aE*6Wt6xPCL)fw3ZZP~q7bHPkE;aW+Og!jM;El|<2MK~ZEL0S+mC4$q?40M&YuJ;NS7 +dH4s>63y{K!w3gD{KwFHwK}g6lt@d=Nbt*Nq2zlwm!RqR$$RJHLK4(!FEe1JjnbIk=EiimqBn$l%Y&(nP$xT7#x;xBu}bHK9BrZxvFFQD~bcfbptN` +e?gPpYuJmsEve;?pD+=QT5cKMsWV)=$1X6Syd@B&#B`W$n{IR)q7Zfb?Q<`qS<|$L>GlwO3oW$NRmvE +tNCmRM%#`m%P{N&VjIBV5bdG3EW9jg@OZmH%jXQOAMG|$xmJB(wCTT3!GeLcx&4-z2Xn0(=bwr#&rVt +-sq-YzFkdKq!+wX(+N0Vb-}Tlt_!cI!t80~JMR-!_UDiZRDZ{b5DFlrW{^%*ly^Yn-O;iW+9~u8;l*; +N`NcUIeEl{Ydq4UT>_HX`n0VcHrfj~U#7OpHR?)7sn%W;0S?}0>!}p6T`gF3AJFlO4v71P2bgido2wC +>r=`&q|784;@+8MEJt73jcphgwn(Lc#YbtEmtI2AYW#(>-_LT>@fcYzv>~Jb?<5PXQMoazj$hykJN>3^#dG!$ +7}ubT{w2e{(nf4bfsM^<0>D8r0G2xDrZ8F{G}Tz_B7r2wgEIb(2zkZ>a1fd`3d7|s3zX3Z$PyPXS&Mn +F7YFjFT!K;B)z-J-(mS*r^Bh~f#^(`=lL?PGx4B{yt0_l=&{;?G@@t6P-B-@5F5@3QSLy4Q5zIC?8lB +34aw~q&j2OrXK~&g(Y$DjS#VdoknRlk@b(8qFS06zTSUTqF_CVWjsbP6l|e?soiB08RPxLpqme*EbE8 +qi-=BX0P)h>@6aWAK2mldXPgS@&=C)o4008710018V003}la4%nJZggdGZeeUMV{dJ3VQyq|FL!8VWo +#~Rd94}!Z`(HZcmEZfi=i@LE9-W{fS@W4OIxf!cO5R>?S~)`80pF8g%&lEa_al*_Z~^fq9uEEZ~>A?< +on|Lew5fYN?Rt?dM)bpO!!x2x+k+~t&p=yt1VNFZMZhT!rp(lY{B?FyKfM`+xaMro%xMMG-)ns3%ceq +EqJl%@&DKW$QxRj1eKx#EVd=`7OFNU08-P7Xn|HUjQz-DI054ky9JsyHTInea)qv&>BYkLvEdAfyYF<2o?x?K6=F!F!DLl42x~NfVDLd +R`#_;EZUmWOR>DE8Oniu<9ux<0({*#yeW&_3JuXtn+QqXHI9bj$>Iu)%V+n39G3#H1xO8VYy3PM +U5B%F}3DVTukOYSU`SOQDynR(i{&_#fmAx0}=W7PbvxbTmHW`&&G9VU^xbp*@lBc?BPI?p~MZ^QuL?d ++k?B=N>rt*K9_?f=F2xH7VNaqgzYzyyjcT^Tlj7Dp%bDA +Y_D3)^a0&4a&=TK)Fsq~0F%xf1&=Fm-yO%U8uT8%2BNa%cIog`Re?6lVl47<$7@RfszP;JM+6n8wvCV +!(vGuHm3`tGSlMb?gdONT3vQRP_bB03(Kc9)_COs15P3(E-w*^wo;+4puU +pmC&pg+v%Ex`dF+?0HQ)tyW?^P;(B{n9v*HDw!yt20Xz>6g+kghI*p44rzu7lDutB*WBwT4gJ)rvF%2 +8c;s)0M%#67fz)RXl_V(i;St2Mg|0ddb>4=lq>1BIvr=r=4)(-Rmg9U-BSg@khmcPl_XyTUNs_%Av(Y +qk7)1gQ!xdlU+yb2zVq&k^mb9=KOT2^IQF*(HL+fUFj5^@jaR)u``4lrdUQF=-Q^Ne>uF58?E?dYUhf +R#wj1D~#l$Ewru8<~|U|yIXX-9oN}>3bEp?w3#w#(P~O6(e?&plf`m5NqitWeulhEvDb+EW=Tuhxpqg +HVvSzsVwU7g3{ll`|4h`z1&g;CN~iAy +P99>^PLeth-QGk8BA*;hm_J7!&TF+TpmLXMtvF)Eb8Wvff^hUw)}T%(IiQN$XHHROS-!G^(ZE;lb4={ +f49Jcdk7{=hZiUYv?nwOV0K6V-k)@t|0fySyM`+1yw=x0*@bHY2{aI=9}yT}*5I9%*n1}Izp2!T?*)~an{RSbj +KQE)J^2AyPX3e=}1_xnf$|Js=0cgulvi%ZZVodk~mj0U=3e5TvlwI)jzj&mJGRcwCs8sBIa-+BZUo9F1zpQ6x*Skl|F)p9>SQG +k5Hj*j<&OJ3LATJ_lj5gToeDqs%^I!kF}2j3+ZRdsIHjZ+h)7gTW_kd3JD)57!eT#>?|ldHTmM@7{f! +k{T-p7N?c(##T^Mt_e-FbDKH3_rYs`+m>pxhbylmfnKoBpWc4DMtQbs*UgKai!nDtADolCwhnFZVd5| +LXq+RWSj3Q=j#j_kkM9*cc@{c+O!lQPq*Fr#N^1 +6j-cb|=+U?qEHR&^5gaeCBwuIe{dJmvFs6m<)vJH}_`@{78Yj?PU|aD059zB3B8zAG-&dqu^ +e3!bheYVA>>zvO9YE|;?#e0*6hdcxN?cB5iLAeS8*WAkHjCbltG3HiS@<7s`ec@coE9fw^;Ap6y@aER +5W~`Jg}*vC0(166^My&^LE+{;b86~ePh?{p5O*V``o&n0oqDtG;j52#R=EQo$p+6c(DcyLt7>|CtT#W +C_GpKt(>)g18F1V&qCmf|RN@a97qs35u@s>uJN>2MA@QZh7cJ8^bO{QjzwR`yQXbXE+=lr*^ibm~=6h +swcYo-Nl)JU0B6K89G@hnA*PkVeQG%x%ZehGg<7D1hJT-g=xfJyyia#Js2aF>hBoaM;!RwI5yrT1`yr +G*{PrnC!5iTda0*~4y(-sAWA9bODF&C1mF>1^$#9ke-dxmHFi84!$nkT=!n93tZ(Gbsm3=MgAKh;}}@ +Z#lT#Kn|v9BC+4J~uQEI8Pu8yTPn`>3!oQ1{KzvE?4~*gdx}qR;Xt03$?EWSl%qQdKfHyI*xPpR7c@I +k)=c{nP9mb_vA+|>AoPbPq*%i5nW`%n2G=9kUnu;V{OFk1Ck20N82K6YdiZ#vxRTKH*tGU1yWB3&)XPX<~JBX>V?GFJE72ZfSI1Uo +LQYl~T=W!!QiK`zb_sSPNYqpohU=JM=W@20I2PQCd;QZe?eoZ$BkzHjfxe4LQj2_oGj#*)?#YQ)XPX<~JBX>V?GFJfVHW +iD`eg;d*a+b|4$_g4^knFBQbfB+qq?gI2-#W3vMpe;JqDod(FFYVutvT7&pg3dqyOVqiLI%555%aDv1%~VjJlKeFFJD2&B)r>YrsHH^YME +d8IAc#PooYrkmaH%T7xmdp%E5r`I4VUATX0e|%b;p`wI(Y|p1FH(M~Zj3v{eTKshP6Wdk85$|4bL>EobUSD(i#0u194G?;N>LJUi}nLU>q +4NG%}chOk=EJW=#K$aIu>bLEvm`p>)k~-Y3?BskABt5&EgGBbhE)({;f|P)`Li-Zsd6eX&k7OPFI0uC +Rr?$$x!kfr9&aY=xiES810U=Y$@_2X|k=RxP5PUt$Y$eRX|C%slId+rsW6JUC1HnHb{eDU{o4A=sWvLN$f?Ht}VxPbru98eQ?zJGFQ?64$fET3yC +EF-_WU|2}6Q0ktIi!SVgz&19C;*ASd|`@gae{UJD)n^7B8>*x$lPN8wmoXc +%b?PHw}$-T$R`js)i*WO=HO9KQH0000801;hJRTr1}cS`{P0NDZn04M+e0B~t=FJEbHbY*gGVQepCX> +)XPX<~JBX>V?GFKKRbbYX04Wn?aJd2Le5YQr!Py!$H_pAty*0XY;}N}-ohO7CWEt>dM#q-tfT|Gu(SK +SH9Dq?sMfjNJy;gJ-WQwvmEGqclqlTn~|*6;-1UaPM7+@Kh%RoA|P06z#17TTQ>pWAlv{s@mxo7LAX5 +`#N9V8}2Eh#)!+nb24!CU{N#%Mc~a!L}N(r(ZfBD$8!N`m1X%!K9G=&0%(C31^D->ikbM;v4D0YU|j@ +DiGX&5H0m+nqzEVwJAyzz1BZmOZC&Uy)+G>4Ak^b5R})!iT8c)qbAoqg=>+ApP0b7`^0;L$SE)d82=>0z<~FxLS4LO+hgED+IjyJs51 +g;R{sz|r8(pzDGdFmK+BvhqO-Idll}Vbs?D4stDd1L#Pn{HhP)h>@6aWAK2mldXPgOw{`S9Nc003SY0 +01Na003}la4%nJZggdGZeeUMWNCABa%p09bZKvHb1!pbX>)WgaCy~OQE%Kf41T{~A-EXe16*%?Xg37N +fNkxDVqIFS9rjQZuFmFN)c7nJQrd6^8Jy@%{rU6yl2y)Zz@-+_5%-=#&-@I4g8dbL`WHAjUnW +v9x>j&upc_1TKyPm(0RL#sezWHjSU+ubY-NWQX?uP%Y1V9S+-lxYJk+e*dECGKVoK&9CLNx#5)Xf-pE +ZNMtp0XoMGR2c(w7ai*~W!j4;FWAEe-!kc6(Eb{BygabC8cf&b?Sa8=hiGo)+uYml%&TCFnoZ#?C_)! +f4fS@-?tedvkwbxg6rP~aEtJd^{6!dB9qD>J!1)i6?%~9AP(?bj;mKvyS)5PfvMU&&rAyT_wvntrL94@c>(Pa5VD#c1dnS3s+5_g-J19+V`)31bb{fbcBw}LFyjX+(6*w5mh9zCHAQVufak?JM +ra0O(N^9JXEPYN$*LBPI0An*7&*Wdd~ZZKQ5blJ)r@*g9!Xda3ZFU7?p|T)Qe!>Q_!EU3kLSk02N8M* +CRD;WG8V?yknUO{5^baU=+QS=MiaG{+!nYmBrG%kRBm@U);DS{dDBQkp{X#f7;se!RK9_dV*uEVtMiF +sOdhLe0Wn;<1|_x!vUD)9?-7F+4cdYyPt=Oh4hodYLNjhSLV?=(wG>0hh&0^!VNfP&$$=V~wLawRv9v +ADFhmd-ueD@1Qe7cVN_G@*a?4=7Y0p~J9%FrfT>N_R{LhOViRnHO6O~tE`J8kUi}c3m0!gt?0F;56A& +YXPT7_jL)nTHST7m4$7%)OH^LkWOC#wt)1C&YULCNLh;jkIrF`xY@7+OZJxgYv$ +bAl%ol=y)VYwlvT#E|&j6jNNF=?PF1?VXyI36ShJi!W6vRt5rQBTYaBf2okvh!0P;9ISB=Kc6 +|0E`U2`oUiTUIp^@q7L3YRB9ZZHe!#rALb3B?Ufao_Y_#LwD~MrBD1u{)EB%F|PZh+GV(3SwuOB}6&b +Kv)N%L4)94iSou7vj^AJDJjs^J4tHlK7~+GrxVwNo4;wVNJk%|e)aiLV1 +7x;plDQJR82Nc)O9e&p!z|&NuJ3`%t|U%#{tml%y60_*41h_gZ7>EJS+A$Aj+{Z=dFWuSN88Rk-WUA6 +_caduZ^np`VGFq#ht%EaFo{oG?$YEJFq9#2L}?zkAX;;hI6R((L;kQm(6ioBZ}OXtH6-g%FBIHQ3uk- +k{;cOT-d~&i&QtSF`T^+u%<>-bZ)SzE|=FFYfP+;tPYGyXb_|1sM +oBY@YYr?PTBS@lIenv<4(uyzU~3$TQk~a)_uf&=H;Lb@g5X-A54+d$~YX!e8Ixd+0(2KhkhKjB$YNJz +_`%yn>r?I=6M9kIVZjQ*i$dVm0Jn9QQF`J_k=$+o%UkflpE8tK0ATtX@7I`B@$MW5?CcnuB^DarnVY+ +Md_r?6YJ+FoKNLHgzEWm=xMP2@jmvn +wmwMj*p^d)l$?{4FPxBY)#G&lTlL2yq@k)MwA-%v{f1QY-O00;mPT~AfQ>)- +c!JX>N37a&BR4FJx(RbaH88b#!TOZgVepXk}$=E^v8uQ^Ag$Fbuu(71o?UqWOT4TB%jFr&U$8mG+1XF ++;Q=p-I>+{rWn|!a#S16S4hX{GOfO*%2l+iROijv`$ReJ>x~0i<0zUSye3f=;b6i{No6;Kwy-xqFUe{G$ +j4@#3?e4z1ZAksN_q0qd>VXV+;l!K(wlkO|{@adg9x$`%sM~l)!7zj3Nlk +G8*#q?|=@Bl1VjMgpOw**IFc7_#iT>BdsjR9uQYE`I{Vn=%6m4lQ#P+PZgIQl)a=8?@5`zE)4o)7WVq +cp+ALR;R~Cekz$hM|VjPYTgUHZ_3l5B|#8#SWx>HJVl5XN}9xR8M&r=!oa_!b>`vnAridijV(qT6wR( +YNs^%cV`>R6sq69$nwx)RbAI0>!btCwio4gZN%5xDZjn*@zq_^?X0wHl(#zhcZSwleNA@*bc!JX>N37a&BR4FKKRMWq2 +=NUukY>bYEXCaCuWwQu548Nv%-G%qvMPN=z=v%+FIu&QD3@Qc_al0sv4;0|XQR000O85nWGJcig4T$EtoZp^z^a+5LX6Iv6Q)95v{rdI0k46+lv&)SX+pMg`u5WjZ +lA>9QZc{3;ZHm5<0-m}?$S?HneOcd~HdQ6_u4$8;pHkKIZ7$V(k<221I4ifirtO4&T{g8cLlSnXlC4x +KyOm~e-PZuFsqCz>lWj2Yi)w1~Ts611WqoUYHg;0qR#kbE?6OwL!B{JQ?WO8w>$cg7O}DLhRx9+hx0f +H@pVJ6WX?7h=Fz^4GRj{7lb%z~nZ{Eyj0{$-l`}{0DeS3ELU++G?iD~FGtE%j#lKkscnRop34$kJr-< +L{{f9N|L{*dhuJdb_Mwz9bFcV_laJ0xaS@vjR!RGsU0-n7p(%!+4=86&(acS(9L>!Pve)$~xK35HV2% +={$P4)`aJ&rIZ(ToPj5qOLuhwQbYJ;^N{xJ^9F#H4p*LF|qWc?DkCyGFI0tI=pydEr_SAR8oONn#Cxc +-b%@t@W}TT^<7!ngSAdI(-ax1&{G&WDu8dM}rLDY?)zL +i-aTLsfYjk;1DNF)63dN!LCaxKzr)`2v@p+O=xax3RRp(V~(h?D=Yx`ykn88@(P@0;dM029SuPcH;71 +0vO6Fs1nX>3uSzi6H-d3o_l2X@y;6;Vp;-tpC{5AcJUWWh- +ld9PRJr)TFSKFz5*=^=Lbs>OvIOVF6wHdocaM2IT$iczCmhjd)&b$%C7RZ)YnNlMC*mOUhzXKKRWZli +rj1Dgt7||BwtL)Nru4J{2!4c5Z6s4Ld92*Oi7E*NvL>j30^R+WW$cY^Oii5pPPV3jb=3UzND`KO6u5q+-j>SkX@i`fW9S`FxLB +z2~RH=OLtujSM{5YniVU%Kb$#G^QT0g7Igd4{t1{Jt)d9T=NL5e<|3mp}XiOEB*6mN3!_C!wq!d}=cA +L**Dstfr=xGYEvbO;aoa)p~n*c|KCA9lV_oNCVKCF0gRymzJK7IwGAH;KxBC-k$yV$_PHnq&8(S*^LJ +TR6SZi@l;kc0lbySdk6^8Op~n(u|Y#gfPROFi?N6sD-3SBYVsIp+ihY2JGO@yyi|6(#dWJ7o=o6}0#W +IZ(pGl0X~u2Ao_>usUAIGhz<*Wr&65)=8^m3$CrDA)M{|r+48a5CD^L<(Zc(=20oCfV?d8G=nHNQnEz +;nQSgM7Q8&KVp8TMP`#_-h2&W||+2EhDUn7pOEoDHuJ1TA-R#@9U<=8mbEObbRQA$vNIxW5X?LjS)~7zh#&K=2YERN@RcA +JTTN`XFB&^UOn9+21hP6t6>CsG5E{ks{waCbXKgc}I$4Fi6t|>t6M|rkCW(Qgk$=brgP(n=2KtF-4

    x&eT052`01 +RR)z7fA|CDLSVYVmR1#*F8CkSA~1c1oCXwdJzT$$hiCV63fMbXuCoB}8e6Wk{9U#UsurOG>OTNrC`N~`4?e+Sv4Pm?9@P{OxdDo-VY_B{hF;-z+w98;i2UjW9RM +U1jd0zZ5WJYGK0rQ$$N+@u5IKm?b)5&YL(Z0LWdW4Wor0zhKmd8lZTWYIE!u3oE^{>A<1~>Vhand{H6hs#`6jE3Cg%IFlG<{7K)s@2B8{^S&epSuRL(XiFj+}Ftc7P(-2TXAg-Yx3&;w +~Fn<-F>k7k)J-TGo%VA&&E(;+ZG!R5>Z~ZD}A0pdmMs?+uqe*g77^YdA0TqMBRPX|80GvagchDru)*T +Ij`fCs-HUJSUyX1iw4BrNx$1*T_ExAOXU69BWIU+C1IO34}DQfq~9LW_7&Lce>IpCPs^5ArZrEBN<{o-hyg|r`2BdoV+gpQV2P!)}QbP8c@gv4=0b7$VR@>jbl1#nAUsH5F(nx-12$DnoOBf(hCX+=yyF~L} +HdCKmGd6rnM7_uGlw+BzK(Fo9vW!;T7#5wZ-rc1G@%#ZP;nwwfLNbK|7=|Ncnluzn?A&@gfxZ235fe4LCjZEB +ssNVRf?8?hKDMwjRZ!*C!fJtOFcYMK61k+aZRJexyGmc=kd3C)AIZq9(6&*Su_lq0MaXQ +*uV$EBegh?r9|PSETKY-VOrV9O$eEp492173VEqOlgYInQK5Bj^>?5zIy_^AI@tKu+mdW|*02FOh1HdLF>pWa)g2*QKFr@Mer(|-wOAF`IwxYw$&uMCftVXWsS*kYoC +HnwsUA^`2Nrl~{GahPJv2mUQ7$nthaKV;<$5@r@58onieX=G~%g77o-MDmsUO_^_Kh-z$mFg7lzAFZD +{4dkg9st`{V%UhvaotV!fv%umR>3rZ%ZJHkB<3{SNJ`v{!$WO>FBMhZyJO&m;+JplJtUxAm!7(X_{F0 +GTo20kjKy$sD$=46}*F5`R9>WQ&&^~>5gNZ0&g8sg|gW&mbA^aS9{>enjpy5KqfyHx|Ll&WPb1yh9yO +VOqVGJtr(i&X=a@D#x?~4)n%sYpg&eyV)?CnhqZq8Qq(gq@DKO!w5+_34m2 +?pWVm>@ftdFp8*HP}|AXC&Q9o{yGAM&ewoN;8~S>x}Nd4@gzphDpToqNpGOEl*3_|1{h$~e4OnTH^-S +Xn?pi)BoR)p*EOyj+1Rq_Zbn;*ZB$p;nZ_BuJ|aYc_&H6cg?iM^Av=FBexYu1yX0)Yf19D@5c!iiTvz +l-04e+V3y{+RGb?B(z&_SC){GQ`eYmw&RAq;FpDWpS3j*XK=aVlq;&ZH`djkDlNpe;Zh==Qay<2pgH6 +a3CAm|(afVs^T3q5%z$vjxCFaU5E(;MgL%>N`E$flQ*qan+*{)AdDE7<31j;HscHGA{NF^YpB|=K{b= +QIOLilTN)S>~D%fit-Ze!qf5NwD9q0J4@DjZzA}b1PeUugIqk_0&L#7m{szZNlpa2Z)4-&<(Z3f}N)` +PJwu~(ra-B|EF9)dw3#Ogigw$GzMnu+521Dxi0_=>D5n5&~Xl8dSC&zg0N+!jFxGqI1|h6|~uX&2mg9KQ{2t4&jW`hNC^1IJ=Hj(lKh0M +y+V|DAx$W%Cgjjgf01t27 +*`9Ztl@{!90f#nS)0;%KptQrzI%j8y<-IFY}^uotj&$A*s1N3sXWLF+o{j*(aDk*8?*rrc70`QxnuI} +c*4(z;>cwuu+l*W4kaKt7`H7B?*}$V80{1}5Z|^p!iSuA{|cl>VlxlCZYCts8xl=H#SqPPmxJ=L;Obe +#50xFl@cKKP7zzYN4I38an;K!j2|iVn?sV4~C`*IIK2H-2y*wHFL~S$ih?r@uvaOawW2#JTq?YgYh)eX{nn*!Ezk?c18?E +VQqLO%bp1UCYUXh_87fN%edQkGBgb30$8d^a*=oq$sl<=$e{R%9H)*UIh3E}J-FWcQ9d+gZ_)fYRZmu +m0L(}>d>pH!SPaRUD7uY8OU(J +m!wpwk~v;b19Ny=*5lRk%MNx)At_yX4lP7&6uwQ&3~eAa519dhr^Jv-v>t)!Bc3elxz-Kx`S`Y+pxz6kqv6-fz+PEne +g#cWXWhPx1|zu=$wj@mNI{BY0TL^P4r?4@yiE-wX%qt{;7pulSKAO;~)h=6kLd=)76z#l#UKJmkG>6@ +Q(v7GHhQ|F#U5U@S#j9FKsa|EYB(!?<{?(>8sSKppe@e4S`S>&4M1fEJ7y;d|Zx15ir?1QY-O00;mPT +~Af|Ue*v^BLD#0k^lfD0001RX>c!JX>N37a&BR4FKKRMWq2=eVPk7yXJubzX>Md?axQRr?LBLg+s2XK +_g9RmxCGimRE`D6kBDjAzn;<~;+DE9( +_uW3vslOLLWU)8b`t=yEoT#3FB&u^bUor$Vm$+xnbOeQBM_r&?N6zgWQX>O|eN-Uf0R@S}fuABX4B`z +g&-!^ZcdkGhttrWYag8Nl1dhEFG_pQtU%=UJ7I~QHwR?Ggh+4WUZmz%|ta?{C^lWMzb+FraI%)m{{2Y#d7&}DT#Up-|R%d=NsAe>cNjTtt?l!PwTEPHygQ +{QG;(Q;0M)X|#N1ArBY? +s$Xw`_I>*%x%}1zsBMi8O)n6uVZgJKI@Zehybp#v2gp1FVIkY$=4)U%kQ~y`;itczojohg{`36oyBANt|8BVFb=h6p +Zhxg;!>yIj(#?@+>mG3(?y{}ArEY(@uQscqmi?w#4)=&;)$&>{Ul%RYty8l#+<#vq1gHOtAD=(}{)h9 +kZ!JMyoSYN|Oh!?Ng}9h_tUZ~D35o)B-4AIA9 +Q*aGxae1&Qg4Z2D43>aRsf7~k(0o?1`cDR-O*ki>h~wCU~~Bxbn{M)XL0dD`QsM6<@>pb`O6qyt_? +u0d4P>KE#>Eq`v>(`{AL|I4;9=z-cn)1r%@}#mV&=7Q3A=EA*k)7u#(S=#cI0Zye4(#cOnFV^D|xxU@)4RmMY7S-=H4Qpoyb0Ir=a +k)im5;CCbK&ZX=B!bgFSGvL&>$Mo8BkrJVh3u@+6<+KZ8!0ZOdH->wM;sY&!M2X~1dr@tQhx0)UrIC$ +s22Q4E*0(4b)Htg1C_jJ+@v>-ed_pg_C?&4N*-ZyqLWn!rAx-A~IBLMhpafhQP*hHRK*UBbQ?^9XT%I +PNi~86Es_1CIwiJgGhX^{%Pl!T`o;sqqMl7P56BE%1vhfDp})xB)O0}q!;jtogv_sUr; +*w0<@QppRzPSV46CSOty}>fpbYW7g59Ytuh0sdg4kGz@~W)BKtvJp*Ym$UQ!1{|z9#Yf?78^EgD-t`2 +)2UqRI~5tPAR};&t8g{Km5g@)ltN<0}0Whqycg$nUg6g0eW%gj{wwWLg$ZPOi3W^WV@|;<%PSHcAH`m +T(RLBVRO8i4D!`zS{^2fDnpbYx!d7{7@Bsu_?D$|=Rtypk*1L+*EMKnX>5@VeQ`j|(`^@_GJ}FLFT5> +d;Z0J|(iaV=#CsQl)`M82Zv?%y_*iH`65RKFCkub!2Saq_AvAS=o|t_^EqeBEWweJlrYQ+twnepi#D; +TP5PysE1q?Q6c2cz@amRG@D2G4EJ(gR{=d7Bcy~x&L){-M&2H5tXSx#}w1dU64#@9b1*fslsU2gEKOV +{AlVh-rHMqi?f*-SLt2sYj>5JflFw*q($pNASO_07f8wKuY=;~O*a8RPpI$aIi*mt=MNQc`yUfup4vd +OGW?KOyIbkD7tpM^&Xqi4_53dmk!j89)4#@I}>iiMrAbm*ff}l33e}O#V

    ?Y^c}nNGAEO)o${kETQ3}xJWa_F~ss*R_=#&xCMLKGs0hMSF!6rd7DyEnKblK7Q|0Dc4Y*oS +r9)HZ6-6~8rK2WN<0LhSrkRf2v$yG7N0DVO2N?h3=iJ44QxZ&M?gDrhCH6&z-i2aF0l+vBG-fE!U%w2 +nSz3gi_Gj@(_#u3LX!YP3+yH2Vd3zZ&u~DdWj2k;;Z5!^`>rRn-8JiYs(fcLDDvhiF-W|?VladBDG^T*ZBHz*ok6C4c>1l$&y1_j*A#EDKekU$(m9+!x*t{-S{I +gX!6JSlj9mz8)zWCP%5A3X>aAn*0z-IBu0r}@|6b$mt`^260h|s022sPx)E?^XG3D4EuGK|3)y(Q*8q +m#r^AR)Oz_vkUWIVAfV%uVG;HjVagqM8=-F~zjgl1Jis5LH+^Q&+r^P`4ras&5x_ZkB^t(t_^x(ra!X +1wFXk6-c37_AuIXou;3p1+8uIp=w#YE|cviaaa$H`nH7$NvdPkfMm9H%-4efP5p_T?q0lVoFEC#P20_ +jF>5=FB&nGp3&tPVAY;E%2)&O28ShfDheMn_X{;Cbcjee=_)Z$H29%DNY +UB5ZLS>sFcI9;M@eBQtMKhQv%dQGN(m3hq>cir8Drggcq7z3M0V$fb|nXPc-}9^>5u=b#+^p!5hfURA6D(P=&%^J8hHu^QR +~?C6(U#9xVoX7Nk3DayPRU~%dUl@Uby3)pW@RD-K2C|JlFzj#{|UKdhzAK0VT_k<%C=@T47X@v#+8bO +n_lQ0+)=Ji3pdSU2~hgUxz0U6&;k8ST%;pisb>9;P=mW037a`8Jgua>5^Tz3UHgOP2+R#B7mX&T;!Ln +bFQu%t(+T~oYUtx@FlCchrqT@Gv+p>&)Jq`ZwJLoRX^d#NSMC?MB{*|8bP9nd +-|D5VqW`vP1$og^SZg;lyQtqG^k|DOS~$?9jz>!5i~Ep)&LcI%qVfnqerUtWu_S`*sJSLRkYf3SQO90 +HO=U62vS*e?Zp|gyYk5SD`a?D3cfV7Ny{Y+YRj%Fo7gi{mo`qcGkNqH;&T{+ZIZMWIX!|{u7v3F7BT% +#KRF!l{5Cd-0ft&vK04K81yk#^Nsmw)H~s0Cb_C}lW{)~Yj;!D5|YS7p<}UYq%>IgQym4N4IxEt)2eQ3e +5B2c(b*slk0ExU5*3VETu8;7m{|h~W7TGhhaVQd%JKlX8Z|tT*%7H@t`sNHHR)C+QBs9IC*ymrkO2$> +dQOt;R`=4bmyj>Sej>_8Zy>DH&evYdhP8NX)=#`ve82<7`Ex1SIq*(Oe>(x*+S~xYPlBva-d)#6sPOV +*r@U3C(q)M5^&|vHJmVPEe9skm>*}PIZ%XT!UWtF4Z<>{R> +qr;)|CcGJlWfNgLysXQus^A-At_Pq?Aluci +WEzrAJeE`$k$y6W)!mNxX9l;prgnMZNhOp?6oDsB)klv;&15xS)Rruuw%K1@2 +L@K|&Zyd|@OfD{l4*#Kp~J^Ps~_b3aSYR}7w@virYt76(t-RLP87I=J%ZCh8qA&tJCnTlXpxhvG7y;m +PKdEAH6&fO5{0aln49jXbiE+A%g8!6^UhC~V}KuX6DNi?IpY=wC$k(6N0_wo7$5B&GF{nCl*sd&#Pd1 +v*y=G6Qk02_S6YvoPx28H%me!#dr_Xv+iQ(D@LxTY) +G9$?~{LKEVYeZ!g3EQL@-oZey#1QPohyfduf)J68%U(w0vA#D)KJmLAK(dQ-$FP!eYe-!EPm0as1>Zs4G#o5SG0esMmoNUn`;pDSQE^pf +MQ=KcyaQ!%g|79rV`_>l&@k6k3pVkHl~w!(GfTx9pfP_5?v7hwVcX$cN55rqb%$-DbP4Z*FR0NPd4Ql2!7RT*jIrQfM-p(MXNJ?i(mF +t24~9&Q@kZe~9v0$VAY-rQqFTLW4QcgurbmkDm!#~4IU%O|d%NCIBqnvbk78UN7hnpRS)Gg=R8e^X%4 +8rZ1@E5tkpknS7*zEdkC&LO-PwQuMOix57cU!sDUB_((Bkvh+h=&wMsL^A^rF-7jqeS{iGD-b6&g&=x +lFrWI4Pvx@2Ic?pT(SMsYVJ_CsV+p +8V0#K%8N!os{QM$qLnHiWVjN7h0@u>U0fQdruA1h@5C`_wog>-pc)>o;~uydG +H&l}*UzS#jR9meudM_Kr$LN8Iw2o@e0LYVlVz(8No+|)CK4y-O#Ej&R*>3YgF0#=#ENmlH4*2GaRXFq +}xIIZy$YZQapL?loc|3~O`o}nTQk0zzpIV+6xg+D6^+9$>86gS~Cj{Bo9Otef5g49TVsFlgH=w4dXv` +=`@*)SS_*B{$sk%W`fxZFKb!yDo9KxfT>hA_v5}yv$E4xiP)+X;FxRm+uLbqRDb6lE! +cgEQOI9-t$+o}b}0_?ukE%YfyvNhX^GU#`>#p3bGRt7RPF3U4!3QD}YId~lfxRUv2s6( +!NN4_)amJAnKy&IOT{v472(->o8XH&#AuP<`&``9or;n65eqG)a$TU?a@X6C{U1*F~kYQ2j<5QM%Nh; +!Qa8vSRBV3vZ8x^y}cmdiL`cGAn8ik@c8A{XNdGsMrCb7T(N}b}0PHriJ0YA7@1Bez*>PeX8LRk#_5G +0?hof&<~A!k(h)R`B#=_qC8mLA&~)4=S_K-=8ypXjRwf~gm5FjSobf}6PZ^TQmd2uc#y3!v5Gp85JBS +HKw>;Pa*`Ilo*d78j)77F&#BCe(3PKlw6O8JqgZLFbOfHz00c8FW~+$>i`TL>$_bm%zI^3Dn__?-~?N +RZl=PS8(8k4j}Ro&rNexEm_k+nmu##0o1g{i@D=GS3=5`3Fua+XL8LUAB0IZ>Cn_M)4+Kz#E|MZpv6+ ++A)utJB>Xy$yc%{|gn+7eXB +|zExiIIQRf;O;sQo}j`vhi;WSUxjxk-A^L>xM55+&UxJO=R~Q_S};gv5_FjI2i*y9x25^O0Mb)7Pw>{ +8#pm)#uYJ|7`lZ$t;8xkHp+6D4)3V(~DDQF)V5e+-LeM1aa};<#ZMSzIgc3!lFnTovA=M6X=G_+|Gix +{V0UD`1dJTyoV<=L`jIUImwCfX6%(pLq~?Ynvi!a1WVDZFZp^>a%8um#^;HNBY+ze(-NwF_bJ +r0)z^{X^JPEoTT#x0r!vQ?l=$tKj`v6yc{KKr;f^UM!^zKs9FWUM44f=7+7u!6sep`6gj0SjubvyrN^ +c6>YPdTJR4Cgbz8p84%r+5z@hN#KtjYvo1ei{k&rHrwbRc{Vt$qA{>n_wK;(YF+#~`GJK&OW{D|@dB* +oA}k=kGy65|=NcY`2A3jQ%Bv5)7%|LLt)6;$@}a!gte87#QBn|dCdo1O#zwo=&}yfw(OyDgL&jAy^(^ +17~m*-MqB+E6$Vz7q@$a)t`FF8#8@E5r{@b0kFDfCR$%-~tO8#pA;&bdyUbn{F0!`zdGY5Vve{f73y-oI$~#|uF7cA3@3iC_CkAwtFypRVQuO}Ll+!}J4$snC64?KOkU>(*O@ +Zp4)NfR#Pw6NQmwh?j19-g4D6gsj`(X#Zu#oR6ypKtD2>~2Pe>o!MCm7Ts_#IbCwM%W`|C|jIb>aO=4V0zQ!1zMnI(~df9(D9 +@AA6wJ19A9hFAFSGgk{^`8foPX)@H7{Y&8$(C9$^+fp^QNTj)*!^#S1iY%-j|(NPDDd=HARbLq6!RN&CAt_;v&g%Tn9=Wx?|BtwbQWTLsj~tD3I>1R*I(5= +kz3pCqNt-L6|HpswLUnG00h_SxMo`W*8(%-)!S_H+4Z>qk~&|WuZKufH&NG;yK6Pd0tBXF6m^DTb>M^ +yO?wHrs9)C==)L$D*-$MOI0+ZY~MXBL`s{u6`RIQJj0J)^W_E7P5VIbV6-at!XsrO`V$$Ck+_HG;H*> +>8_F3y808SYB%k}j(49eRb$?L!>FL@MYPdeE$?3!F3(VQFmTLOkvlBU~`rIk3;s%w{=4!CDE&@{Ieyv +=87VIwjiL=Yv%(nD&`AcM+ubov#aj|eICduJ%UbGhlgBsq$u;F5`n$tpYV`@k62o+{^AH!hL=3#m5&Y +9hh75_L6MXI;wWLP!FoS_fl`dOlOe{ki$6OLq3(Erl`zqA{kiKphWt +f@Ckuy;(_C>UGeG&{Gn#2=mcJ5KRP<(wx{Uf$0g109evFrsm+nNh!wBFgxkuCe9n`G9RcLg`mta`}VLbU_%x9F_39wLo3EwgR;wga(n<2@DX5QczBZ94HmwbAsldlx +^r+aaNx4@`24zpIjJNUFw@|_M!HfVh}E;i5$AHj7D#-DHHi`x};?&{L-9n2fY|uC~@r_8VRlg2hR%YQ +<*oK=F97{s&hiVQd^~uE|Vt@z-0099!+jM;1JHiKx5k55-lndT6VUdgcuEwvT`gOvKx%Wg1NLzK9m#| +!aoNU2%k7RXH4dfv5`HRVd;eGB+3!FK%Z5b(a@UY&9v&yYNX!k2`p1pLoNUI?3*9|GF6hwuVxbLAN_T +p3TbkAr8!1{;71kp@Uwt(SY&i2Op01^rUQqC#g9Kci$Dw(G$$k@ohLULzy&rKENyu~BlV_%t+6a^}XvD0x!cv<_Rr%Hzn-!<8C%{j8%@#_ +qD?4{`r5RhCE<3eixGsw<&FuX)l8Fj2{*4>_cC{ER$|vWHX~+0O3hCiJ7Cqu%agqM}E+{_j|qV7zL;U +3J3a@;d(sSoBb$C~2Z@rAu1?Q5!pSl+zs6jk6p6DeHzWzlDamclCy=DHO7cilHVvh&Hb1g%hz1^V +?1~1MWo5#V2I9{QIX)TD9bo_+&P-fa*0{v%gUn);M@H9-9eDgf!{EfV_K5?GiWlvM^0!E|W0-jN;vAUr=CRO`JcqZ3WE(TeFZmb);{6Y0!UMI<6smbDp_Pf@gYd&h68pa_PFBWf8%ONi`?`Jp0cv>1SO!Z-tIWv$%dHjl5PM5oOgTZDkm)GL3tuGf#sE +~&c#tm_iXAvh-nxj+FfA8pC#ceN92znnn;?1QQZ*qQ+Wv=6T!F!=HVtL!EfZ)N!;@8Rd&SXBa=)Az9f +uRnOK#G9*h(?f^U@Pn^F!&k!i$KiZaEaq6{7gPbK!}S!E{jda0A4ARwYCfjJqWZ8f2u4#Z0lnjTZFDO +7Wd-f0UE(C)BqK)C>jce{Y2xJ)D8HkN(IQLB5K|p-8WfKRUPHsD7*k?9L4@xbyW^~!F3R0TZ!ocCm;w +Fi(4z@yTUuVfaB_4~aWZ|R(o;(>e)aBXHpbGM5nL;$=P_^>MVrE+P(*G}n +53RXThm)JQ}W^oB>O5{Ix?QPy6Q7#QFw~mRcru7(}OoJU@gZzJmG +z*}TIAQLW3r6~b2J*(x7U48<_a)tF+czGh$`2HsJaN=EzqU7jhP% +jFft70WzfBm%?9c)GS*G*Ftmt}kUFn@60w21NnvE3;&vrP^#K{)@+S^uUdp8nnw&)t_YqO{Q~V+(c}6 +(?Z6-~)ZQCFA-*-5X4!T{Ye+I(Fs8{D(5w=xT-h)9B(cZJhI-nqC(ZFf;a{x +t*S?;BjuHrPq{QRR(EZcKJyy%xhtlZn7lGM4c+WXUWVdxRd!>fFiBVez?$!d(L6Nf9CaFcZD +9yb{nom~e*;iU0|XQR000O85nWGJ+bhWG?*#w=of!ZC9smFUaA|NaUukZ1WpZv|Y%ghUWMz0Sb8mHWV +`XzLaCz-mZExE)5dN-TL3kJ{7qYg`-dwEc7R-Rx6M69Pvf +?~eES97(Y+m9|W(?N$`qK)BDn*mHXK}*9r1|?P{Fcv +ZR!FL=R;=auqHerw>2+(s}vsvxJc+2gA#=;~i(mhX-aS&XnLy64pw|We^0JOpPIz{-F*!_B&i<)Z|cd0crR%>8$k8WmDKD*4V9Cna&IiR(1de{Y_HU +Sz9)~O2ni~R1NSD5g3Fvy7rH+pR6Hd042XF>!=Gt%V^j+q3zenv0;D|v8iUxN^aLaUHZZUjDXsyQbUI +lAA-}`9K4AqS^9@VcpO*`0^~@Z~QUU@&cY@u~!C1EDFzd>D3bKJNb{x*3Eeo@X#9^f?N@NlO6yShc$a +*5Fmf8qyZ0zvsEL?(K)%K=`K#VwRFZo_s{>0M#V##`?`k$>Avf#GfqfeQ)W&f&Wzp_~6* +GT9Gm_LJaUh+rzF%0P}zMdBw#43Q1e(+ng}h2O;yso#0#qCG6nP*3RMMt^4=1NIn7+`CSRj38eXyj8dS-?qbM0?ngTlPH#d`kP--jbfQ&MKWdvz{unq`N5VS +a+Y;+ATN?z~R^B-fV%kg#vs=i;9)K`PRHa96 +F$3AM=W#2l^{>11i$ABj01+W57qQbQ)FpwIL}bsnF|s}-^w7s$uXV24!06KrlxN&f@yvz>Bah9mqM85 +clVZip>LXb+P)4)Up>Z_ew%Q@?%E+~>pkEEb;MtRom0suW)U>CaY5X*Fw{&{yJkMRuWp+2iAAhQ~k4w +YvQ*N+zzod50jM5__whT#FYarja+07G|zFbA*g<=&{X5ve3q+m +PTy9|snH15ir?1QY-O00;mPT~AeSHh~p-6aWB_L;wII0001RX>c!JX>N37a&BR4FKlmPVRUJ4ZgVeRU +ukY>bYEXCaCyx;2~mf!U&dZDC3dMMDcJ&vu4vLo9{tR2}_S#s=@Rt14XQ|t%?xM)B!Czb!c@91tc +4pNqrJzO(333R`H{qBoVi)CJvVpe6-GS5@JrT9UbUtOu}%6`q=i)CESQ#G+)^-6mmr+GG0S6j0pUx;$ +G1OURkJjhlf@k&k0k@#6=vWUyP7>Q%0;laCQsq!pNN8+qnrgDq-FV!-PR94CYUWJQ1k*N;#bS@Xt0zG +~C`tZ$R^v}azPeH6h~To!T~BW8AweI%3h+ip}*R&gq`8@sEhvJ +40jRaqj7sD5v*8QOJGi+ah7X&OahU!2q1povVtdjdRhqB3)0 +`zeC-pYb&ZQR>FbT@|w7h79fs0^}zs~fEd0oE}+8$?k +Cbc`aP&{lMEd~kMr^a9ASCv=G$U}0RA#Q=Ca5`no-qJRh@Kx~n)K*;Fy*VC8p-u`s-bM)%)r-Kj2XRs +IcgoY>}WwTuTxGx5eM`HIO9+t=%h|D~%(gev`OV3~^c77BSQ06_pO%R;Tm1Y%3X-*_aEYMtPQO>2fiP +K7o9H2E23AbqXixsS&#Z?M=3i6IZp=(VS0PbW}&f%ZO;qJq~g}kRK?b8Glyik)0v|Wp7oQWK6RH#J4( +STDTrwZ>UCM%(3SpsbtX%b0$N*J9Soiwon@|+cb_E`KBr&`jU_c5ChP?}84%W|pr#^Wnh&Z`M3|9H7t +#$&j{SZQ5JJ>Grz^vU;pOCk{bFRrOH%L`FTUB(lYsuGMBTtYCt#Y76g4Blm66siKvT%4_d7>o#Ld}v9 +}4`f*t8S!M`Nd(^xB%ymhkdJ=37?Pm1X|YBSwzkwvWO>R17~A;h9u3zLN4-R1@uV(XsptB0t{lJ8Ave+Rv{HqG)XZ2p>_WJ +szbHR~aWE5_pXQW0cHLTvD)#6sAshG~=?8>MoQnmmID21_H#weUhDSogTHmR<3xFmHC^Wuu +Niva%E)B<9uBO$8f0%#N(wp&&oy*)iUI6jWpx1aKXWwEO5LY1k{`dQ-LI8Bx|8Nm)m=;wujbY7T5r5X|UvmE~SnA@j9mX +7eHx8Rb}LZ!wNPQ_{ +QFG!6h8bj_t)1U(cc%JRk2d>W^HYyO;6uO&lUurDt$@MLaWUJ_#bNPdFVEyA_4M<| +Ej=XgMxzNx6DoP=L=_1*~3>)mMfidtG4TeLzESxF0GDu65Q_`&m4YOOD0bJ{m9L?-3_&wLi!g&b!8b)06K}#- +%e;8ggEC#~58%!UBuhe`E9^)DUR=Hm4Pwf&X_jOW(Mtc@xmyp%Cv(6QjA()j0w +VmuPCqN`9NGb#{d<;dJ1`JpauJkBnL?7IFg)J=$IkUXB6Q@o>Zx9N*Q=*m${;QLE`LTg#7`Pk)9$*7l +w!HU3VkkLbNm|q$VMwgd3llKxRK!=Gq2aBaEYgO#1|G*c)?w9ah$sx=;ke5JbcC{cK6w!HT2H*9p`Du +Koz?6j!(FMd}R%`H!49wIs#~ml10!ABX0_ZU54s~dX#j&9by%PSXz0Txxec +?bzv_H7+#?MN+Dhd?ntA57_{(e#ym@kd01?(6qY*F;g8WxTnvKaSHB#-KRtT)*4)4z+No>V7XcqK+%lQH1 +5n#DblFHFs&3hRq4~N8byb-ha(Fy?BAz<&iBONq1X|r%<3BrFIrc +^mG`i6sNi7P^nye9L7qY^VT%0W2|1)p-nJh-7@^2gm6HUHT#YF&L;4?8MG8mROBl+fba4*#eePU_>F-OM1 +oO$;gJB^5w)DE5^>ec(f2EYTT_RGhS_fjw4?eWyuvg`v9#&c>CUh#=;l!e2>FJa8*eB!T97T_N=4=6>R1_S`K+UAeg9?srScyUZ3&2_d+ex`FS+9aShooQ;Q~Vy5=m +e$Cujg*3FDO?`oFU=M(?7&vt5XNruz$&eB1qk^BWx?tH@aT#>OkF4>hj1@=T$X2W?#Gf@}6ZYw +&644oS6vriJO5qC&gCtPzhn5RKT4%>d%!od@@wUfZFZDUKFlh}7h7ibmCR-Zr}^WYK`+4Xgu2H)uOKx +r0yZ=J*D~4dG0w&v*A8U!Xz{AkXBvFY0e}H(^=FIW9;E#gAflO)O&|6I=q@SvTlF`$0UW3YiDs(FJS% +8iWQL`-lK*L$`I4*sf*C{TvNPzcMaTg_(0*74Jk?jq+V5zzSUW6>zo9RHMCajPt=1U9}8|J=YdxBaEZ +^;_HTC{(JV{vRaRF4#HHa;a*hX>8Vu_vO#VIyYpYN0(Qr|-6R^R3wxs99yYYi))RTbf054_K +Q?uI=j$EX7o3h8TE4EfMZ`AQmm|`JJ6Uz8Oy3W$!z7ysu-lfvhbw?k1=S=7E?e?@o_C*}gA?ScO_5A# +d_)bfRi01~)1eGSh%7d>z_|tChXd95Yxu_?um&YZ3{aSH+Z4R>(b&O8?c?3=pY0sO`zHSl +03f=eLQY3(4FpJ4{FMW8Jyb5($KvUqRbwOgR-^6LO(N64{4RlM*MCBuo*1wh7_su>{FiAZ{(k*OU`sW +o@6ra*DTx6n+ckMfj@gZ2VsFI7&KO(A74}RfE6=hTshFu0^VhR^ZYtT^{D_q~%R!PdoW;h}OXL}dv-M@>c`sN9MOZMu2 +r&(v(>;u{`{KUd#hU4Tk!Kzy!uBDO;vz&Naxvp^CA8TUYao&`n3mJ1tzbFrK#Q5&u9^J!O>W_#i6$2E +!G>;l!5yfvx@5#M>Da?t3H^RV{kGUi`T%4Ng;1BfT4_=zOXqRL6k}zB^<*8l&6)ZPGcX)vd=78;N!XZ +umcWB+FsZ9PoF;8ZNeIkT2!Vm8H;fHb@|=?xq>GUJ+$5oHz?vsuASy;Q$-}LaO$XYo>IZ#L)S-xSbqY +Jx9(y#&4lSzueS(&?WgAcx&_tfCe>AOuNv-E!J8%SVPnLOuI*YS^^Z_^R701=h6aSc+N#r^>Hzct?6d +ry195rj_*h^$`1>TQpJZ)_dZ@8HnNY)lD%-a5LOb~EWiXB|Yk%jIVcCJSoi3slJGy^JE_O&sCAn +;s-{S69;B`1s!795g4&3dqlq$(EHRvQIwb9?OqAv^YG&apjxhXnX5K +&v&N#Stt`Nbg<4+rci_vOI)A0@5lPv@-vd;XaG^` +86zY?BIqA4Rd@%`T4}h?_^XfybZoQv2VVv3Gk|Y0dal4D8Zn=-GptUDRiQ*1q=n&F;lk^E;k;(c_39s +YxYT`ywt@M3Obm=KvS>%u|9x}X5YknX*82&MCl}0=T@_!yXu%j94`Qz!8uRnDI-Oc-Z8bwBjgE_o-kAn4J@ukYv6tnJWL#J% +!1${DTv~+JR8n}mkr(z~Vn}>E`yNN~;94xx9)gS1&f<)SkG=7@qi6l??k_Y@TzmVb#2dahwtW>gtD-5 +Wf))UI#cWpWN6#m-e_je|kz_iG~qJ$cfmO|y=j=7e>X(j!*wu0E7d@;*Cw>t&lj&7PpfG3iGSmeNyQ` +%+AJPZ?)0-RX1(aUWCDOqiG887_p5|6%X1MPO6Q->MH>d|yV&cT>0;uQu03etGRH9IV}Q+Ww9Cd%^EL +b~}+9LwFH@WXn*w!z^)mEN|0$!r2aBM4niH=0>JiDR$TVWT6Kxys5uDX={6fyckvBmrnM9suP_90YE5 +*y@6aWAK2mldXPgRy}+yy@g005pC001Qb003}la4%nJZggdGZeeUMY;R*>bZKvHb1z?HX> +)XSbZKmJE^v9(SZj~lx)J^EUqNV5tek3P1O;vj7jAKpN6w3eB@>YIvf1=Hf7?nK2n2Gv2ZPD#wmmRVZZ+g^(tY7VFFw1##qOM9g{vWGXDUwC5 +#?ee;2o$M|R+bP<{D%KmpDhYF}Y{M4@o0O_M;eiYgw(qP`jiDXoZg1J1-w9arNt6~w!Zh +X9i1tou)sincT$G04f2CT%wkPjW)E?JDquI*?EU94(ER7TJ4s+K+u)VdNS+7<*Y4`m$N32%espBhnwv +xv5!mR%A`=6e>z>{jF?J38;{rk5r*D;VfAUfVE9)Q^3W-5iy}>mA +#%l@m!Fem?vJS;7t-ZI+?S%WEPd@(ywY0fjQwnJI@{g~_rCO6J8sNk;WYy`I~cr+ugb(Hb25ustHXF= +d3iv%{p<^X27WPa*vmK{#vZKl!+C^b&~W|Yudn|8s`%The_j8|74a2&jEa+;{Jj&JP`X$U5`<=(2# +Fk$Z`@c~6fHl9qF62#i%Qg!@Owv7hbtO&f{qk2H-?ngYqmxHLb0O;KfYHa7Q=f3`CACQMJD)pt(5)Vy +fmue$kfMlcUsi){(GyR1tmKKKolMzMJ*d)&h;(X)t?+9NJ&P1-TEXVDy83lfS)vSe2mG+yZ+dx(r4(T +rxA9>=c+r5A)Z|CVAN=V1E7B-h&tbH$-6HJg+5u2$MDrVS&zYT12fe(i7B=0{7XW5Zj!63R$bA-U6u7 +Nxedl(YHDISs945rh{YcZ>9Fg-Yl9hi$F-S@r^`VkP#Rn=kybAS{dGh(*Mfp}>V(;&7*t9aZ%11;eFd +(SZFQ#J@vp0ET5zM2$6v=gWO?)%MdQJLipAd8SV?ct-2Io(uN&-TPN->H#c8gfNK;>&OFLfN>Sq$Vyy^x<=t< +s`kdjLhl4Cse?8wbt)VkQkcTlp_Qt7O$%$P-||1A@GVUQALoEG3OyOyS$0U=v*MV +h8R5Of(2dBzE2MK52#-D+krKd1Q^o}|A29QnySBfA){t%v}B7`i{w)j+k)i5)q5c$h|8 +Yao+36LIF0C<6vA9C*cj*T_=Zh8pL8qseuRHh}Gn_x=19O(+Ykw9x>$YrwBJhjCvTPAo-_JY +G~UyLKaJF-fLowr9L%->RfGn3c&u?d1e;6||Dw}V9^L%|9EZ4qy3yqrZZ7CrCe_gHXQ~f<--k<&j6hSGPc%KsFs +@B4Es;$DU+~0`1-XcsIeu7R+?!FNWHQEP4l|5aqUA2TcxKCHY#QeA7-VV25e#d3Qx47&2T|*BTFVoP3 +GjjOLmENs>w+ab_-%8x)dwXauc;hZTR$NW1v_H4Cvvhhiv@1PP3qa2u*op64Lxn@u5qY=llq29wck~AjD`g4GO#GpcgPeYUqpKs*R<wJf;~5gV_l7i`PYBRf1X +xJN}8x#+yg`=^_wo|_3{UnV0vOjK$c$b5TsyBMoD8z6DF@FcP$Pcb7cHrf^YgQBaAC>+byI +T`9{oLRjaJwZ{-&SwrR&#w?y-_=}a(YNTeZF)l^a97M8j=a +A$ItxmiCCbF1qUn$N?qYqCCfI+=TxOjGT9u^aImvgr)WLB;%gVM@jq;cig+Z{0IhK6n2IRsQxo(d4WZ +6^WWOvK~}A)$VKL`6sEc&B!c2wmlNmCf*@`-C8pC@ukbfe*sWS0|XQR000O85nWGJjKt&=1_}TGyB+` +lB>(^baA|NaUukZ1WpZv|Y%gqYV_|e@Z*FrhUvqhLV{dL|X=g5Qd9_(xbKAHTeb=wRFb|ftG}CNn`k) +zi#+y1_ciq?%Crvw<3NMr +EYjpm?v)K80<#nqR@#pARY4U+n+D~agqLV@$cK1S^P2CwviBhkag4YLX;1kc-@vI)JWSh7j@ddcX}mD +uKiZonwNJXt(9tGb|;#2!+%wEx{)RReh{@b3Q3(R{g|$5z7bnh-%loFUQx5N&dv#BugtS)nwD6WrnAX +p@`}AzjnGV$4Ch|qI6tseTV}?P30{9HS2#us%ZXyg77IE;E>^rPn$#L)I$JDQF7xuFVd7EhhAjnT1p8 +C+=8rMciZyFdixbUC1;8!eu|_ej@9`W5)2xv9f<@akZ=eS)W+!P?i!C%1ML<#%27F +qz$pO!VXMK1Lgnm2^EmCYI_0K!_dHO?k8&9-Y<2*$HawPk}7Fd#CXpRQE~ylx9RF3hiBW0AAvj=jCvH +EUHu52t@mPjXg)bC!*vQ^GD+qzBL}?B08pD`DYRZ2_zLw>@Iaw&J8H*a{S7OP;}4WIWDpu5T~@<;;>y +Ch7S<&o8gef4aIzEh%r|axI#vMHaksi(m~A_5?Ph#Hn +~P*Duxl!7P(Yp_(WRa@j~tHqFr&XLm@JN*k=DpkxU41c01db<|TicEiAL4BBYu^5ULa#{0w2X=XNN!G +dnjMGgmmu+Lc(}5t)H@wW*l`IAOhz&Jcb8Xq9E0IZFI5-1FEP)ixor4#moZCHq0KFi9|2XppV#`(*dE +yp)28BR!Y10VN2yt(?PF`#Tl7Ug(o!a1ezdQ07^%o0djSh#Mo?0><0XCpvpVgojVr~wN$9oupi6 +UQ?!-*|#Rf%!HZ`g4jyjdX#{mZI4TQ9i* +(5X`{s8#NiVT`iinri6bS(V$uDOIUd+*8D+2Gw2N)X;7VX1wkp5f(*u`fklS-#h^i2wfKLF@<0IrpO?ca9pe$Q=3lD~o0>1?#i&jZAs%KYFGWWpRvfE^)7fC@et~nZef}95ostc*gU +>Y|ZDm+VAaNHYU1=c#ktMSh=?zBi@_!h&NHjwG)Nt-Ccbp-F`edSV+8w5=*yN$mHe^Z8oM{+rBWqQXv +%6GuIjk;!VAp6%OiIIkH@ll1)z8mRUTrm-g0#D8mzK?9F*|}l96`j7;@AS@Py&eSTWlOz+eW>ZtC~Z} +LC|=FzqjZe$Q6L1N;L`+sNpm)(kQqk>Jd|>@FKQRw;A~a_6%$2VaN{1EkUKR$~gCAbo3uxHSN@&d9Y{ +e;sNrJaI%O4H>J8=0RtvFW2@*J@@cZyhmaM&r{9#epS+I8a7IJ+;Dkq{JMbgtcp>-^X|A?H`|zX7`w!%MCki|`Ty>D=MDpDG&xRzPl4qz8+Dyc;B+ogRFBM=u6PYuU^N+VV6CVSK +g|JJ^Z$1~?c$tUr2@8O7{RV*ycsh_GGGbpJE!`_RXrj1Bx8wH}FUOht;VuB{AWRm`T?S^!m^@}y1 +>zpyi(2yP39y1C*_bbSX;hTf+uGZOa;!$L&fAn=?Ghxu-+K=f2uWc|Hs9Dz?(==rb(;qwGRVB(iT$6sC +_=Q{f8&3EY15hI%4e8t*dym4tKOxM{HuIpo`wC5~5QAd|?L%7iD>`v9Ds?~$cg_#Zgfz0JuXMmkgjk>G7K}s~4!vdL|_pcWianzS;x6VzkrnlBd(CmMEkb+ce#S>wBb&Idh8*OMQ!(SBd|tP +fEWB=rv(!r$)aeNn$R71S4m@4ZJ44}KiZt$hMeACvxWPfuBnz7H#!c{;Vap9Lm}2}7TGAkUJk{;CNFe +-MaIUN2=s){Na9SFQ|WY!Vuzo*3`w?uP68!~woZ*o~|hau8)Zdy4}`P%F^Q=r&&)4M_KqF>2-(_WU4Y_{4hF(}O4wv?j3`-P{GF7aUUSILs3em6`q1$DQdo?YD17ehHb*+>q +-@`vnbMR7ttVWlbU88iPLAE{d5U%z9JL5(SWH{tj2k0|UN3tYszLUrbvoBqO#`beaZGj +~4sHToL&E81FwE9}>Bh3i7j1`6#f9QVhY#X+vBW!2~o+9Ebugr=U#|o8T;FCI+lkx#t@ja@Ip_!_78vt*i8)|+o}*WGQ{-e>-^bzWNObhn^ESz+32 +UY?%DSNW2OhO#`fcLHu(=wO9KQH0000801;hJRbv2Sn|=fU01XNN03QGV0B~t=FJEbHbY*gGVQepKZ) +0I}X>V?GFJfVHWiD`eomSg!+eQ$5=T{7<2T`p|CCEbo0WNAd3StzF0@+4d*aop8SJFn*F1x$brH22#X +O|aIv5gir40~@gbLPyAVpAw>X;I45DwUf+R0pbE8DV+5KA6rjRdOMg&4E1>LN0?~q1A>8QN)RmmTSrK +nD0|wSfQk$`tg-CmgPBfhZC)peod^jB5x&FQDj1!H{Zoup>4@>F1L+{E~Nydq?8%g$?-l2dUR0~xk$Z +z`Y1Gq_gZih+}%uO$>qi6=SlMMYDVXc-MBQ+k+O8f6YQf95W<1e^y%u4>j`Cw8`%fUG*zVpp-R)%Vab +JD^Ng&bWv=Ed_k5leEM2oD?73ai?8Y^T@zk@$@_S?TBS5rd$4hSU+^zti)I1k+2O`M$g51r9eOEf!2# +LR2t__q#13LSapl2Kqe!?*H|7YBmT9RFH@^DH}&3Nrt@98_$eLQe5Z+W{YC+Q-`o4p3%|UW49X{ay4D6%@L|`*)9C;lo3CiqTy{mwy8w! +GFizV(z-F8FJ}mzl=_7F2XyxmTD)dz#CU&V|uFHetwYQ$>Z>nNguST*vJ8>P@_Z^V((I{{6SrORMG7> +MyE+7^h28gNpE%EsFM{(`40P@8P_1Hn<0gG77kwFrf$AtT611FWh5k}yUcfA;HL;Bh+1&YGP2x_1>`) +gb6`4#)at`&4By6u4IGD!XmA3|gY#n!rRXDcs&|~Ku%>efZUgr90#&d +3Yc8U=E@V&56cnf`%Lv&CjG0g#lr>JNs#+0fWKEIp@;akvXF61bVyR)I;7oEdTwj=p2)!Nwf=nm|Gd^d^R* +lebzOSYzCL(YKMp&M0z!=))*_q8i#+qT*x^z@Ru=jgXqupAYZwO&=&{J3{k-k!<0j9-Bh4T3rmE-ZTX +ytKTN53Sxtga6>Txw@Ue*WcRDusGLc^|cXg*xrc0`S}+m(S=;)36m(ZPZk;p;xeeJy*cE@U0&Tmbo%8 +`Et4(72h7($Z$D4Je41TcPrglOzlVcC@DET+0|XQR000O85nWGJd?QieVFdsHxDWsUA^-pYaA|NaUuk +Z1WpZv|Y%gtPbYWy+bYU-FUukY>bYEXCaCx;?+iv4F5PjEI4DwPrRRjc!7KPC*nq-}Au$#na-4;a<$h +1U9Y$Q=2DQDfU?+i)Fk}pYixAlvqAvu?G$itB+YpD#RvNHUWDa0ZW&a!N()JR$A)*!nlz1G1}$r8-E7 +S#iI^X#wbB)Rxya`EBn=6wVog*Fk~)<(#R6%owVHIE==+5~QOE$TQC7+2wV+!#^lxD+~V{b{3&Twn@* +e!O6sw}a>S{0<{1v1lklWG$0wC6ug6USa4AAkn#OiY#d~PaHV7ZIY53mNCP^5uE%4M4JQY=!jA^IagpL;2OYZn9sYFal +3Lp2dQwRetbw2*RoM5*J$(^#DVp9z6M`uL_3H)>Vxhon66EZe{bT`({CeSRc4)R1)(UCQhXAc$1ux<# +*JFRf`eRfr9_r-d|Ly&fiw +-P!C6qhcV>=}WO#h$a|g0_R!Xc>GZaB)U)WMAPJm;TJoD +c6@__js&s4*YM{&C=?f;W7c~AUe-PARP*5L=fo+u?%1b?ui;r4kn_8|aKH;t<7HV-?U3Z;&H^t+>HU^ +u!TjjSgWA3W@_=LxlNDd9&5-$ZbFx*N$9{5n#+flH+?^Z2vpJ$*BVL$j5Tpe|^v~Tf3 +|rOn3HHTpY(@TVTl4Pi#Gcyq=Jl+kFF$wJPF$!aNm-inuw{A-6{7MBl*dtCQbC(jH|oQ+iY)EJ4Q%W7mdIit+)A{&H!pojz=8@8s%VZg@DM%3pn +t4qjRQs&lkxgurx*0BH1c%%M6aT$9sFL#J$X7pK;hnzK=L%rN>ST6PvdqWpGx6!iCr%4f>d{Gmoj2>SMdxABNU62H!_x!yqexv?diR`DZ}A|9EU +bwPC)|`ziS!fo|g-2HGwd*G#c;r~DhKN><=)l|ii}eG~ue@L;R&Fa74*xjtzX54m3wnM03z5~R3NbVk>F8mV}!L|~oW) +j^pTXaMpGuW^orE1kRr-yzk!C*R>!uQ|(5cf0Kka6jsx|`f0YxQ+kr*!<)dz9RN=@#v9qx3uQ!VZHST +%%1d=F8kzhS@v&DEJppO9KQH0000801;hJRY7eZrAF=T}(lLqP&oeOpD^BSp=wdfBv9H+x>9C~{y3ZoqbJlPx+r|NV>&YzQP +rwUi4prpUM(Ww^?Vg#U-X*E1Ze?Bxq2N{?_S*^!&8VTC=d7WktV{}^87R +UkDU=}}-BUy|NJR$QqI<09p??_etw3wKhko<2eQYgEBN^ZKsyS~WvS6u+$P?4}{lb~xp0|8h%@_04eE +az6;c4+q?#bFCxNgW-T0umXb!nI^A%4-|6oJ3&qKN+nr4huB$RjN^gi&W;cTR-Yj28{36-!aRQuRsvy +`&}=sv66mYiZ+wEWU10Ra7<8gFvT>NpsPOlP>-sxB+hBRU@ZSe1Yaws7y~3A4LVR#NF9H~4|+zLV<8-Y1p<*Hb4ylvHb$GlzMW&L8lrW(st?z_Pij{NA9$)4aavktd) +yEhpEk#7{^~mz%Nb$g*~?qF1$--1@fDq69P1ZamM+ASXYy;9D#^2r%L@n-OhPNaDZj@62H+Fty^usgq +K?f(pV!fh=hXfgdBJzxBfouG0Ih5nGEI;Qv|ALkDR&w?^E^QgG9v`@WA=b_C=l?Tv}p*-Q|;K!MWF&) +>er*gqsSY*zxw5C*Fg3p>Hxo`bPxYs#5nx^E6aF>9H`IH8A +GGZWn=?7mj_exDZ$yA&pK^VMpZBbw7=d-izHPX_CqBY+q(^+WW5%DF+htU9Sz*@=c?PhkFRRD+jvzG; +`l_+>IBA^>{MzL49bVxz>yw|Pv%P2vzf+LL=G?+A9QcD$Lj9l9pK`(l_F(K_jK3$;kP@gi=s$mFNRGt +)Be&Y^Q@`44OQ@82 +{q+0@wsE_<;wG;XB9#zaxvpoANsj7Evp+cPhAb}gefpab~a9H5o}H&}cOJjRulUk(aetRv(TM{;D=INy +Ck*D!EqX;cA;L>pV{@^Wf(y&&>BUU$2vFZGINAzDkpY`B_>Ze@lv0lB%OsnQuhBFQAXmuSQ1#{?1-c= +=0*g&rhSLe>{Eq=d)Ko@S~G#&tIPAX{wfWl4s_3R?8$)CI5VuENlLDR;yAjQsw-rbKUAiQt4K&ph(o@F0 +on#6C-i;1vb->QSKwsn$PFt74*191MPVw~%tZXaN9n560WLapU;|06$~2>r#Kf06-JfmosXxKuhsdGvp1(tFQRvEUPLcXFH +WAFT%1I&PF|iGHo;!+%CzrxJP0^-{x +gzr(E^C;XSj8Iva_nT^s?@50gB`aUnXS5nU`r*9H>wKA~N3i6ITbW1VSmcYJVfl`Zo`G1G$zqGN*6a# +zNpj!!bMxiV(OI_2&jDN85@1Y>5)03_rNXz+5Y?P2p_$fq;ab%%=-DWWGLWq(0_||LOl4JxKgdd*W_7 +tg&w*GYgY1}b|Ieb#3su&8`UMOku>Yu1>1r&d{|?Mlazp$pWl|{+sQPRJ+6eWCiitZ%FkC-0P#oK@2y +R{G+u}>W9bxZinP;F{YT%lxmY}`k2+aWVF$=3gO3hf^GX29aykyx-V10UC`IrPj@RUapQlzxh!VWj3; +llbO+a`EX=K|G7nF&x5uireQ=OIu3;z}6^D-;?O?YvYKwW+|IrJx@`tN}(1O%Xl8N+>V)6S2F3!SD?M +)1%x5kXC!5%=3C8RJaZ;6jXszqZXUNG{Un&mU3fgNURJ`zyRedjbWD#)HrmzGnu=+K?hC!IeM78SZjE +ts<&lU&4`kE?Nw}sq&Cm#GE2o5Pf|yhdXn=5x8Ih;7F6xd5 +Yrz~*zr8f!kNl!x~a^hc)#Ry+b#`ayKe7%;(3~mPSnx=%) +vBv1hjNdS8pxBPAg2mN*ga9Vu5w#vST2%W^6=~8@k>bP4o1j@PuZB_GgBg<=5@^4o;ySAdEo~hY +J)mM3Jl^~g4D+Hru3LI*G5`0ogK^wS3TUfRixr}L1W2XtTjT9U0Fm*)SkyB?#5?0{vzK&Xh0BhkuQ|7 +_Y#pDhKVUQeVTvF($=WFzQM;0^^$^Nj7O}m*`>}%z;KqzPkiJHx0ut#X$qt>f?lDlT@M3|r)OJV#(!o +#8b1?nOA4>VAW^iB+&#QEvD`0lsAhk1y#$}qw}d6pumu0Z4FyMV^k{t1i-Hee|;3wWUzCYT37>Wk4s) +suq)i}PKU!UPZ|D$H$?(s#sME^E|71b(mTE}!&J`{JeFZ1cKjCLE5tgg&Exv28i5bK1u7@7>1Xo}QdQkBbCpzfRu1$MQxXu7 +Xm&(M8Kk-2Dktr~}!g7$)8@O4@h+>j1^V +pDU~=XT3~Ma~;fd)0Hq;Q9po661mTBP_!`Qq#a|8LF5i(U24LPrDk5<7ad>@u_M~wPe@NXRue-vPK;CzRV9510LpI<|h60`8OOH8!!tk=ooj`Kr0n{_Vrw-$sAgah36fR&pbP+PO#N1Drq~KGK4 +uF`mRvU7jzM)YKqWVuMdMuUFELl>}%Swi)NlHr_N7o5)9mo)v*21f``j_aMF7h;43Up9GlK?OYRsh{ +#R9vNY5v_kr?&d<(JEa<>pVZ{45n+Y)@Z9NrsqX8rj*62S6JU|i!FS=M3r9nnex&>jRG$TUtR>IO#0) +#)&0?=%hIPJ4(Wq4*A4OQ~nM;?rs0{&-9nf>_;{f(%b!3Bdw1{EM)lWo|mZ23j^g>`}#en6Pc?E7Zt| +`VQ+geQoau_lZtlWQCSWe#QSdc6?Ph79NdD`-v-B+PW)I??$qp{!I(IrlXW#oW>e}_Ev2jZgc_*tDq!`y_p#Tzf#HEQ~1`Y0^*TG9;VN5Wb)*5+Qa@?kv*Rx9? +v%YIp_b@N2qe_AKjZuEdoZ5sN<)ype_-}Mo*F`mZ{CEwjW4pk_-(%op`M5}m$4OOfRrUDEx4-($ug61 +6=2?%nZ9py`lHn$0a#!JX<>g@|0t+dyBxwt61zu+wYfl#JF6wd#TGC5DK%m-0Cq~#UoNcwGp7S}5Z>N +kgG&>N)emcDpgMX6d#k3J)oBFi?S#NZ}83n_J1w=Jgc3KnCuSRPV07sW2Fw6S@h`2xT6`0aX0~lHMA( +dk?>0$&52Lu0PxlLs`@iP}XT@a7InkvWe?M@u&qB+NPXzcqJ+I37CNt01)!dfFEdcGOLyw=Gh&Cv^F6buV+m&$-klWXLKPu8h>{KjBK+NHZVj=@;tH8{6rgDeZb4^k9W}g*M2~RQzLL;6c2cLWJ5u*$t +HFk{D|7T5RvcCew;0V@p>@)kcoxQ~elb-aO?qIukr_A*v8SLxe<@D5(R5W-ZFOvHbEPb~q;wsRrco4b +rVdum8XDqEk2svhK$az4F(G!4Z9mD)XWGc)5i!OiomZp$V5LHFg5y<6kqnoHiW=*r1G@b|w2KVF{fiUaras#ObH$s*%`Mi{W77K9EVYN9ux02v|k1z}V+b6#paC +iUzInomKAH|Z^EsXqaeZLgvemnfE1{>HtwGa0iz_Bf6A7CKG>FvFnu$vOr$f(o*?&h+2##`$pC>q?^gD&|hs2kU6&R--g=Gw$)OdRmgYqWpV5rLcy;lM%gPWCRyn**L1f_>P7 +OtehkiNBp>d0Ic^s-)78oy+TM?kU=bTBTLMXvPL+ikw&mH?8pP~4&)Iu^D~A2*R@^ldju8!>Fw)RgDr ++@#7v7Zx&W=T8y5|QJ6VFWzxHt=+7k@SC6My{hQb^8n8Ckb(rJFUs?GhQ)$$clw^8fScyM8Eu)XDgYU +r$^?La)#Wvbxp)!U1c7cWlV1l#DZMORWifu9d* +O|TcdIsLD9r*AJ#pABn9oAEsi7awwFcW_T)a0p6^AMoYQRtG#do%C34%Mx|ihoq!kbCtojJVQI^rV-M +e+*jhF+>-X)sfQ3Auw#LHmfRP*4!AihFs?Fvl}oPfOx9GXj<_$r(9kMZgh|GgT}B8E^ynGegj<$BE5^+sr=}1u#l9T +dBzEv3QF}6t88o{*urap>sVAD96_o2aic_tZvY3Z>6h^%@p@EXNp+hG!Kl3Vi>@#45dTmihb@`H0f!jwxMzH765kqw2UP5XTLm%bO;JEN5Bl!c;qQbl#hD0QzM5 +2~+SukGY%&Tr47QH4d(a&E2^pcNNnCNMDn@8_O3m6@*xJ2o5w`Js_^pg%o4$;6NoO@Oea3C=YhDbQ3i +JNfoZM60) +i0I3fGil!OQQUHGD$mEd~8aWVs*>+hN9;VlMb3}+b~X+wVS6{)?CMblI?w)L3yO_zN3IXO>ox>1F|W2 +2MO9zD(M$9c;e1qsH6_}HF%4VgLEioVGtH~hA5Y()0ox>%Nsv+@>r7lLws<(*q^3RQt9<9c!?m7>75E +^OH#N%lI?H!ilO+_K2D_O=IBGEI3+;z#rS)mVR-qx8dzNs3cBcA%dQN>0kU?kw8&)9feHzxJNZ}&IOYagK3S%zIW&x#PxODGY2Xc%navfu?VG +Gg5uRePHZ&?A2Bd^0qAB_OIXa?gJhYoXjsDf*!}jNGbn}+Nen(5)2qjM?x_A#YAH-l+(`dNQ0AP+tEM +#mFH4$8bJa4HCDMjR;*vy;EPHIE7xZ2pNOlgSIQht7H^2D>dGZJB-q4!TaiA$`oTUV9Ycmj)Az;VCj5 +_DA%6#|!+e?reI+2p^kmk$k17WYqQ773sz9lWWhUXWcWzX5huR>Tpq6s>rAW{?*?zU!stw?WF$CM-*q +Zuh2C6?#)aK#88n>)M}N)Vi^6&Jf5Fj0i`FbQ>hh4spv&CPLW3d+Ez9O1|vDmF>?88V*12JO3hAhbCM +dJsEz&lb9ed??cbew0*?ZVSqAv)A(Qp<{3U#h6E*w6O6_mSqEf@J`08#s7EUc}!;0D`j`=SLS#p>OTX +TQ!igp5p#iHnpCw-nOk7+>73bS7s1<(sJQ4=#8DqoKa5DWrs&YyRd$G%Q&=|N+==g&Z(3X$AQ@X8?9`h$uL~dX)0ccMp>p5KBR}GMfvsY)+q`ER|%thgDe|@sv^ +$$aF;v|~b&!DOV@GX|Gn9A5qu99{3g!Gx=${gmWTri~`8}&tKH3yk=RrH7pzaaRTazs(cq#QR%3qBoc +v25T`?U-|%*#T&<8T%#brgRNB5ZZ2+HD}5Bz6{iDMqGi|OHP8%=Wm$~SSv@>W)>iYY8!p@1?%d`YvE)_n^K0+&U;(26^qaA}V<@i52<_zVmYU<#60HHtOQ9@Y@~fC1CA?%( +y*m><~gy~Q>!Rf;BT(RDk!YZN>P#xo8fH^>Fwi)0O2#0_>`Dkj-hwZV5lz7NKf+NBpwQPia#>BP#$75 +RAGk%*EL5ltFeI`a$O10=GSbcS^l%!2MIm|kS}iAv+v34ccdj&Tqj-W_Wk7=dH{=x8gOL%w~8^Mk5&0 +NI=g@6kyvKU3~T4*{h36a=3=X0h9Gj{fb{qb=6K(@k_k26HX|a7XGE1oYC>?Gnm39yuV#J=Awz4^p!e +bKiodUYzCvI7ntkzvWn$txQ>rc2~4ZS2b6>G@8`Xo$f=RO!BwCcPwjLvxedYpZ_?tLgFmbZTgv2v9?WMlRh=KYk8vjIe6$nRjH4}!7=Xz{>C}E&00X(M +^3$dLC_}Z&`_+JR)Hp+!=InU_u`W#WpInZT_xv$aa3LZ-XmugjiAl?HxPA2Z|3hLp2TsZ+FXZ*f=d(# +eT9O%RTQ=giIAt=2WJx7bpXzyF2U>+6v-i(}|wd=#q+O;zu;R5ujsS?_RP4R|)8wPd0KdbQwvL6A0>a)jre!VU3pEGt*?um(SqEI{2a~ooS*`$&ToM*EkmNdB1dB*L6*h#w^bOj8@e@(Dw? +h+I4w9ziM3p{cPZbo}F_Lv2Op6E)p9gSF*S-BY)55<<6dmvs8l1g9nL;<(Wo|zkTcvG$gtGU6`%MXh4q!g@9*Fu}!VCR0`rfrff(`~Hm%_Y?UO#9v+ +m^OZzR#eA4)Zi{E>5Z;aqO>&bNZ-;rz<&#g`R_b4y1c6&QK9o`+eT+wU1s6se4TUX9Hi9{ht|u$CH+{ +gw`3UHgy|z-{YU!#OuATN7(sK*6js#eD=eJcf3`~2+fpBXR3B*x?bRW4Aq_ic-AmY9<~dt&ssBi~#>0 +^C!q(usM=X8rGwyrqSONH$RQNP_TtFki3~F{DFucuIp3CAZNcKx95BR+&Q5x@xiRtjSyT4#>ckM7b%% +9m)=RZrucTSrY9T;sP&;+z(yP>-y9lbsMGp)M37&|qRjpq@1>XyCe$tISExXX=Y9DeZiG(s8i#f-Eim +QamXOlmOoK=3YRo4U7wMyVisBE|#SBK7TX>xI-%{dPc{6C|C2oWAzH0WrsAkj0x@T-+HfMSXwY#7Cc!JX>N37a&BR4FKuOXVPs)+VJ~oNXJ2w%!skV&6&CG +rliHlkd6K<&Iy7c(E&$=E|)#a<>!h&b>^14X=73+ZDU%xhgjew@yMX%Yu|$e<#s4!1#rM4 +JwLARxuy*d~(2K^Oc_-DrY{WCKk+xBK+b=wI0? +fOa??Hm()!~A=M6|rw)~d&WnK2=PfDR&|IzB9!>!zGngdI$<$Sj*8qq#F(C)qH%Ko9iV?`sY649RqvK +P8?0BWJTraaR56>FWy=>0T=15%mDrZ^bx6fbM@T+ +pFVy3Ap36C!u7EhF})sqyIE&F&}Q)t$dGM(VG$e#q#0;9fK=&n_1%gY?R)7g+2xgizP{AP1Lx +ukXP%x125@AW9e?ka%IjMtZOMk$~9wLCi4{zx+MFk8tJO%uUy&iUG`CuO$DJ_QvLICYPi2i|=r4nG(O +m687jV4d2D+PGqIO1f<9{g<=h74~&!4P5f^H@P&*6cI?f>QlVuY`G#KwZvD7~>b$vvPJh2<_``qQ)el +d;~ +zK?73Xw&MuQW*Sxc8HXie45KRpM6ElGevrKOO4F^{ym|~{Gkmz)ov-JToYQTso3o))dZWodOAD+%3g^ +E6NUxRZ;5iS*IOieb0pK$P&gBR?t +udzQi8)E|--0k`!}8Og?n1-^4@yykm2gvvOsK^*OYYZGi<$rbM734z;&MSeBvxpR9m$Av*eQw1AP83; +SxA~!?qNBO1_%caEMIb;q_8R~33(h!i`$< +*yng%o!|TO*N?1VsWi02Hb(|(JR|8);-d4$HuZwX>mL=q`yl9r``9#O@OW5}9+NGRo0wX=HZS6YBDyL +O2DM+X`AXqunOSb>ZmRMmtZ_};#Lw7T_T%hPfb)P-k+_s#YIj?pvw-78B-!E3|hvlcsAMVyCn+8INho +%VehlL15Z>Tz+w>eUVc{rQj4e`+6?@QI2__AGmcFcbHela~3?9i5JR(AQDvi_IouHkW}OfwZ$-1KEe_ +|7V=5Ka2w(^4=k%}!#N)NsI#slA{qW+n8lh6H9?GpNrZ9)GWAjad3N|4M+*TI`QR+Wb$bvm$QcWOPWoCs``cHtNSbh}QTO?I8h+g4O +q3{G0CWRQ?~4WE`FxZ&^65weRgUe?j(NGE8H5fG)tJ~Ud)Z@TnnY0Vd1&~JC9U#}sfG7JW{zJ$bfJOb +Cb#-2QrEl?;6R`v!O2XXbVhimNZ%Jv9up*RUV2kBC~7XjOF@LXu#mDHV?*5N2aRM3GBJ&tP#%ZBro)e +<|m9%lm>EKAsk?_wX-iKqHk6iF6|xNuD5X+*YR>$PKs+QwOkQS&s_ke|+xu?NkhQ@n`{OonstR;gITXcI4;&_G*aEfKp-dqAimjm~>mP1>U)n#d%w1uUMXa@9jED+P-j)23 +YJ#N4{ncqfD|1>Wdcq3hLoGKO_jupom!f8i3`;3%@K1_qf6+;k$>oCUh%o*VOvdfk^VB70#Bs_#-dXk +6^1Y}{(pK=<6s&Hk7omQ6-kR{$h^nzme;%cU#GS(IiRef&HO$WcUu&?Ofut;2A(Z7)ehH6plyCz67xg +^PN&HsTNoNc=2H7Qk3wYo-gpgCI{^cBZ~OSAjLLYMamrympu2V?PlVrRd=` +ie?zq%kiX>v*C0I+N2Uhu;6~ua`IPu73%KCJ7@NbC`<72ZjXNBxOweHwAmXULhMWmlu%ZsLU`8xxdcH +lGCX%P8pwv2l`g$;F!yV{7|YEPGyUCGMIJV7u*YoCF(V!aIWKqP^7Z1_X +@BtUX<3$&!#!aSJ>VtIc*pC8jU0ql9&0HnEQqAOB9urxA&DS#PT5<)gz0 +b9{www?DQsG +r_q)37LdxHude(HP$6~kF!Nbm`I}Bm7>ifNMz`Hv3uyDUG5;+k)`l_m*Z5bWk)%+TtjO2k&c+scp+sKJw23%X +JxNT$zABa|Hk{3zRPBdR{FLtGAUHk-|syQi;gP$S*2UZ~=0PGLuS6GV}Ae(jW>_^UC1zu6bpdMfrKTsd*(_T=DUVIXUt13bqQ-O2`_ObQF}36e`7X +0RT`-0|XQR000O85nWGJfsWVpSpxt7S_c3CD*ylhaA|NaUukZ1WpZv|Y%gtPbYWy+bYU-PZE$aLbZlv +2FJEJCZE#_9E^v8`RZWlEHW0o0R}6Gnd7%m|dRrhtH~mUcB;7;P9*V*qOXD@W6e*Cjy-JY(-ZzwNYnu +%kUo4U0n>TOX3{BVjkjUgeYc_Q!iCa#**aqKG8hYdIr)|1*Lq)&!$#}=MqPyUeZ#hU@G%|FtlNR4+}AG&&E`}@vBl1GG$w2_AQQJ#^Fu#9~VnDtuGbW=&J*Tk;PNbB` +`({GKHX`|8|Itn?FmKbrn4q|OUpFluuAha_Mfy;Le<)GFhi1VY!M;{(=+FFXnHS!5Ofp_vA=*JA__kk +?XB%n5F-!JD0o{*t7I~+r*NP35ClQ~F5@B?{g2Nfr=aexN1`#8$j2Z`cFTBuT%A=d(+*ZGSn%j^17i8 +1zfC`T_L4QFFG?&Nc>U;|~|`t(cJTj>x`+U)Xj`8l+n13tUp6(?^LUkJNUlzT~No~>V5->~*Y+5T_q< +Z!31{p%F=@3RwsGN^sp$!I+YuFvfZ;3{66-;BUVUAA;0iqA)z)8)q>L)j* +F@)5bbB1GhXdZgETvJw*$%BHn)R +%5MMVIKCChzg2O$nDwG{feDJ2F9UpW^;yuVQ7z9zXckG7Anl-(K8@783*8-qnH}bQlGfp+hc+qzN=iy +?^*#JV}HHWgTsL1*iRUUtHW2kVe?Ak9HsZVWcYH?Sv>7bKCrR8v7#+_H> +4fP_ig>n*M#1;$T!sVCn^yngC!1NuL{Z|H^~mUlgCDXGxWn{B0M;M%vwE_NO@qe>m^j?D@qes*19~-f +o(o*Fq~py45(7Ks(g+7xB2Fmm%^Mbofn012exK*37|d0Kv#-&RV?Sl%8~yCP)h>@6aWAK2mldXPgNqs +MaHxV008wL001fg003}la4%nJZggdGZeeUMZDn*}WMOn+FKKOXZ*p{OX<{#5WNCABb1ras#aV4{+%^* +a?q5O3A0pw_N^>v3bpapFt&>AhBr$sK6*$9?X^HZ(kwiu0#$Fr#@0}Tn5-Dld32?yWL%gEM;k?f?BU! +hqGfdQ6R%tD?=6B*q+C}@PlDF`}@PZre-YD&UwcKoAhkJaKZ7C~pRCcOnX4lH*t|!CdBL@F|R8=K%BU +Q7a=X04G`|BqoI(}OT@5!jnKE3BHlClpUTRhGy`}A+|c`KT{KbCV1 +0{$m;LZP%3Q0{p^0S+>9?3w)41(^~4>G0`}sX6RY_&kUM$2Q@E6CLT=3_a8E%ZnHvm?#v}TpM%+RaJ1 +e2WA`XQV5#ih>beuWu)i~>(v1Ay5=*mO)gXsEi&`YbusdOZ5pArW93ABq*V?MeS8F^E!PI}BtVzXD6r +DBf=H`XnH#fvEBi=Y8zSAS3a8{X(+TLxJi6GAtLOe$=)Udw~dD?VyV{I;NWK|SU+>oPdW|U%4#UFPOD +^-W8ex+F>M4?%HFED;y8TLttmYI!U|CQ}KR3*~e@TRB)Q>|#|D82{EU^-UGQW#mo%aWE0k58sPWkt3_?sw1J~51{@-PkhFxh8a5klQfD8_K_N<(fqO}lWj5U +KpB7cQ8lF&@dhJr)Ri9wfi)|+^l$H2Eu +m%b=VR-ThabWE(}`RsA7;6^^o-rGRm7*dSv{uT)V1~_9h=+Vsxw-7<613WgM1CqG%R8(K^%)7;?oj;N +A=2__18kEy=JKRflRA`Ne;6Lpgp?uk%Ye<~m6N3h1}E5tsp?Q~X&0w{f^jmdsK!y +fgY<0`)R_a{NEPj>hdt=rX|aQ$VT{6|%eDo25wja$C{1IkWD~%27hjNjt;V=VkvR9WN=$ZCDK!8L#bx +*>(*&M@;gpLYm1KK@zev9E~Y%n3d7h9P$MR9+rdWdi0B9_OXHB=T)Wdi7Z?NCBwFj%9Fo)Uf$$}H7!E}kVCfRcG*t>i3k_LkE0N@pHFf>VfD)+G2?OR(^5<;1 +U65e(SVOchL^@4_v*}p_X_=+KpNAm!pWOzA;I1F+%UTh)ReJAk%yu>x;SRHft&=X8-7Q}JTD8!#+Q=C +TbGN|`=mAVC2+hsLx08bXDm)%w*DRnT8L^9VK)zs6GgV!v2|Mv3i{Os~9THc+KI2oxqp6NgFboC?jho +;-+#(G+dZ5x#~La>0k@1gGHP&gndEtR&f_s@_)=V*J~bZPsFa>I+>X%QR6CjDIZy(J54-F_FE6&%D@e +ILa208|q!C4ftVt)5b5NHYy^u7i|4m+3rq`x@q8?+?7_k?%qcvn9lD@#Bzek6&%iA*OdsYtP;rWAR6?Nl3oDSvcK}QJHNSl_8H_WR +U`9Ss!h_&~FIn{Qajq3_2*1VjeHZyzV7{-ysjQ^S4ris@}x$_Ge7N>hEYZKs3&Q(p}`TjanVZLr^8^n;8H-YJcV{1xZ*VrUjz?`S4>7+Kci +rT|ji)9vlg)zGnaE=C5sP~d6X{*bz^kxzsuOTu<&l#(v4f-j$nKS<49bG|_;|^9fX-M9yNS3 +*&L2d=RnnlH0qKCk2b9R0ohO48Uk436F|J9vCKpRlwF+2SjFb@pgInesSry|PAdJx80hmz|jCy9GBz5 +zR3F(~_K!>QMc5LjdI_>`#TnIEgQtJWZ5LtVj$XARzw4AUG4l1VMXDLm*QsC!>jDYIY7jG+Z6@GyT+; +R?5!HdSd%mQHm9ZYjAO(}(*e9%Y_1eed3SBnN5iv#76-{3=9`riKanSF-^=VAxwwqbus-+cF{)dK%Gd +#C_sXm&y0ImCPSg6m-dZRxq;!LAjlEm4rajLKDomfC_3bY|NC%*Qzw`o1k(jk>4)sFqrL)81p)V{mW| +kQ?1i(-fQC3!2koSD9B^Dt3@;5hw!*OuH@LRf-pJlCs}I?%-NwyR*sUoe1N)9nw<{0V`o21x)msLq1m +?y)ASx72CF8Qo(Bqa3I^MFSf^XqNglwNyV>o`}g?!zkdI>KQ9ylZQ@0ft$Y`7W|_<&GrU+9fL7?c(~{ +SQ8MMDO>@CL+S26vcP|~2EL(&+K1?n0!6h{~KYJKt+H$vULy7ni-lY}o?(G;S|WuN9(mOgasv)9hE;#mN@6aWAK2mldXPgOc027)39001E!001cf003}la4%nJZggdGZeeUMZDn*}WMOn ++FKKOXZ*p{OX<{#5Wo~wJE^v9hS?g}xHWvOrPr;p{h^CnE0le^{Thze@$R%mKt~H`or0h +(KefRy&p)SF*IoG!FqG+qkTU)w~2`|Ebr8&s6_9|Ci_1~2Ccr?|q|7eFRwelxi=s +&v4@XfELE=;MTkKt)kH1;g~r3PL{QM~oKQQkIt@yMGOpgfBj|H6Q~-ZEBf5= +qk63tW|ceh(p$jXAW$+@9au +R1c=H)mc}570Kqhbn@|}b=d=brVnd}nRA)zy3v^;68(+-mCm2OMyiHR^&tA^f85->OK<-5>E`{%w;$e +jg32_rXV`3%D*tCEZ4l@u*mEzA+NU{$WgUf&rYdY&+Q3;4MrQ_!v352a@)+HS>)v%FK;H4YBxTq%-EB +6TTo(ec_THtXxz{PHD(UH52|(_|>%WNu{Pb!g@M8*5S@OtmM8W~wqw%L?Ch;yDr=LdqwyDG$RXQDGSS +QJUrE#Rbf$9rxvJrz_Q@WcrTAr29PDzhRzC&V;sL#Q6FS?ikLuvOoUWc->QaLBmSkZ4xmFp%TZ@1gA9zX*aQ&9z&=aHsWX(zIl@kYjQV+&9yS-3gk_?kuN-G` +`Itp)sL(q%bdW5J^B{&UmYQ}8^Rvw``3N@Wm%HiBbs<02bX-tkgjCV4})AS(8ZVIM~+*0C8a7x@{s?k +MTc6z@-vhpqS@CfwA9@K%JRpnXZ>Yk;9Nt`*9n%+6B7kmXjtegiDyM}|tfLN9ii4-DMmw$B!KcL9wf8EL}!G)-qXar&CFu7KAxmk7+AY+D9*KfvQ +G({2!Es_OkhPbHPZZS!2ZjWX1jlp?~Pthlf2qtIVe=E-vr2bz4D%rlFqJ-tVGvZRFuLr^v%B!Z*(avV +Gd&L4Z`UaOokLf)S1K9oz~63mKo4seuWh(3{7IXAI;LX@v>d$I3ka!*PfoN-b95<=Te?!ZD*5LTdiWH +;|HhKjm!Uw_GTJ*meO)Ov{->Z*KHO$GHNTXIuY)VEG_e)X-}?x#ZRg_ez0j>^a!5dPy$)#}L?)ew=r0 +E^Zs>7|=Oa)F#)hDDZv|1v%n_oT}6WJX}hz{Th?c>hcZV>%&s{ryBT@A$qln{0;DRBySKP$fddYIG}< +kcZH}?1iPzzr6iPs3tp^2Q6b-3n!mR7HN3MlTXAn%rm>{W;uE|*=Le;)X_5e!!zjm)R)oKyaW$-C%m9 +>YN(N3S~E67vZ8?f3i@tpZ{)k`UjJdU|4ivA*1J9)UlX*TZq0EY(lv7n=OC-=HNWT*`a#D&@mFE#!lO +a6!ktin<;rrRh8p*ci(I2bqu|5|`FU)T&=2*E6!}hQ{>{OZ#$O~eP?!cu6S4hTpKCvs8OWqNrbii>3n +&1oNd?xEMmt+RQ1Lk`GQ#A-tC2qnZycaZHHb*w*l22E9`sI5tn9kRQxPD31?!F6^%j7~3~lCm%rKkR{ +h%tzYon$*iVLcz-8g_$-6YRfLssX>lw8-sHF(gDr<&U&LRo#a8wT+P*Lk(ty*!*jiV~j!aOj +R0mkiLE3~KB&oB;S>m;~XR)jRUNuynUd&{z!&dE;jCuDTP?uWaDhlBq!-*vx2bLXY|*nm~f<0A60 +f4G1%KM$T}1(l4#Hx9X9@IVFd0_x%2WXi9fgopOHNH4I?`T!gj?NPx?!2c6R55Dw05m&9n|2EFFZF3x +hvtTTJBtFWW{W|6I#1+SG877d69%P7Y7HSxJ>$t2!S4#nf;4>85FH^SfDdV!BwlJ(wG1%2;C2A1sEhLh2!} ++7?k$LJ{*lN`0?G8{TlNr=<+YA^8IBJOJlkXy$ayzh +sdyDXbF%~W>M$yt4I7-p`ik>1f@{2BV5FR1#N+d{E2tWC*cL@AAImaH`EK)IVOW5$(o5-lZZK)f!t@2 +twnqJXCU>$e{AzhtiQrrhqMyLG8DO?&X`Ek;n>BMzhLu%(_Xw__D;7Vz9e174em$A(+jm!Eo +{rZ}p+fU!Dg5bRO@8_b<_b%Q&>wbX^n0d_+NKNgmz7D&+7Zts*kiK5qS|__nU0AknK^ISjTY#24%rAquM~$m +ZcREytXIjB;o`@~Nl4CPO8?g^Rez<~{D&E&3r^*G`xR2OOX>jE@w_xvR1w4bcd0{tADf(3y<)#(g8~y +r#IK7|IS=UNG@~%w8{W@wWpdoUoI~!}{WZHkizWhgzFmg;diosWH5^vtw+U#k_4>NCDj2V-?=CGCO)> +LbIs_BYo;r2Z6bYko|batEn0Z>Z=1QY-O00;mPT~Aee$teCi0000#0000W0001RX>c!JX>N37a&BR4F +KusRWo&aVUtei%X>?y-E^v8MQc`kMC`e4sPE1c#D9K1HQAp0uD@n}ED^|$OPf5)wh6om=78Ioxr{my(ha7XVO80|XQR000O85nWGJ6)>Prc>w?b-U9#tApigXaA|NaUukZ1WpZv|Y%gtZWMyn +~FJobDWNBn!bY(7Zd9_lzZo@DP-2D}-CL3_|0YROior*q&u0gHurX7AAF1s~atnRIq|tn!gVIh$HeU5j#tBK6eFY(-~ +PwP@9ZD6UuL$)bK`-8N`L(=t59y*I!fmd!ui`Ev==U91@=KU*2+q9#4Jp9gKXnWXa<(nRu4&WR9=KN< +`c!RZV#(p~%~*B?+z0|XQR000O85nWGJ6IMq|g$4is^%ejCA^-pYaA|NaUukZ1WpZv|Y%gtZWMyn~FJ +x(QWn*+-b#iPjaCx;D+iu%7^xa><34M?{#b{p!%z(QZOEwf(yCQJ%FxOKUiH^DG;*qplFUY^|98#o6$ +xhQC^+Q4(o%@}NqKJGv`$&Fz{fb0UG~(5!5sLgHo7$cQv!|^nOMX4xP$Ai9C7Oz;{f5^!q0X`aK87<2!BU7>%5%@)?T(DeSh%!ohfJ<8Pf+D*!AsXS +me^;*>`#2N%n(vqoy3A$!xEzhE@$W2IldNboHx~mmn;_1-=AK5x?9Xn{-P)Uy*l~%BwPOY={}wuf+oMbK*6KDq*9V&2F7P!a; +DvvOtc{zzy_*Kfv;=%*(!JZFI% +|;11dE>*4Y{-t^nIbz{ZW+!d7@>w!ble +&23(Zxz&p=MrtYQv@EGMqPESzl-mNB>2HegEBe|v(7DLpzz*HdG0RXPDtn;d56r!DEmIc3nmmI)fW~>lCHFZn8C7$dGwj9wKj2r{606LfJ?Rh$bs1a*4HHi(>dNXahI$BhBRHft9M*9b;WAD3 +k!}FC}Axo67EcV#Q`63qWz?jVr?2;S6AT5t1GWqU=L>`r_$`<1YVPi^FPME#_qgvHQ+wLU|E<%u|?8_ ++_(;NA733!b>uoW=ftblgQB*z5f;SHaFF?$Rm|pes+9#owQi5FWr%%yUI}`GzU$wDy||+SU7haCeTH> +mJK4P+c1fo)nQy|L$3&Hn*VO9KQH0000801;hJRbuFkV`u{a0N4ot +044wc0B~t=FJEbHbY*gGVQepLZ)9a`b1!CZa&2LBUt@1>baHQOE^v9RRZDN&HW0q=ub8qIEuvMCs{j` +Tf*S3mIpolTVGy)5>t#)m3Q2k682;}aQZG{8I6x2`7R%wxH{U#Yw59|9)JuI2vg?iYaBa<3)Su9o@Ui +#OXrW4=<8F5h%DwC>{)oCYw(3RmAnwVw8oX3)MredKS~fz-ugJCNFg2Chqb1A=zcd)}7rPIp>x6FqwA +R?&Zr3Q`99$dQ3ID-pZ;h&VKp$i5FH}8RI2sFN5;=qeX*!6$&L`QNK)^^511GpcklJ~n6t)$C>>?WHP +8shwhby>Y+VDH6g?(ZrFr(Gee7qgCTRf75)Y5ZaL`nD@s$;pF7L0ny;0-14#(8^tOJ4pl8dvSa$#?bl +aQHnQ>&09iSAQVg<~E~4P!4^uPi+(Lm#tHU**=TAO7WNfxk?U_oYJJ?XG&?zBp81}TZ6g#4;vqYzB6< +6m!Lfnj`h?H7SW;{y>T8}hx2_NibX$IO)>hvyIx_3)Qasc1)a?2hWO=bWU`7b0$vN{aK)LV)Mmv!df~ +mDqI%fF)fLRLi7^R77e7#IZrtzFWzBqkv+c@&o3oBzwB8xPD7FL&9g)Q!f_qJ#H9T!+vk70Zy;`O$c$cAm)fO8SD8Y`vx(q!3t5 +olvt-U((+USM07vp|HtVhHp&NTUVlOiwQfwZ&3wLH+A@}q;B!cq6qKc{cWcGEBs3dHG`*Rsl%dyGs0P +D58gm4T_bD@_=xt&pTd?E4k8vbhJ>`ceFjIxVs*fwh~tP}h&q-lw(KGfBM_SLqJEh6a7V*4W4{J`BVp +ld=mC*>zB^{R>c-buJOS9Qy0eFdS##|#r^=;JkwR;nDV!XjjhT~^Y+`|1Rw`rjsVd(x5WVv&M&1iZDY-|=-g~K{Rd1`6<-$1(2No&}{UnM1|4JeQ7LX$1B$LLU>{qYp-YW*gDE=8r +5M#YW{JD@D%jDJZQX^Wqv5_h4@3P|#XOX6K>1i87tAks>@9)3QWrz$L_)^A0VbWG7cpTVY_nQVQsOIE +Pi&l{9C|n%?r)glBt5}X{?$w%e>h9n$kwt?u7`TxJ +p_xevY%#MtI=k?j$Q*QlqPIP@6aWAK2mldXPgO8+p}hzM0021(001ih00 +3}la4%nJZggdGZeeUMZEs{{Y;!MZZgX^DY;0k4X>V>{a%FIDa&#_md4*POZ`(Ey{_bCKP`=oJBs(aYw +FdSEbOQzqYmu#=f%2Iktdz+$E1%e&j^zhtp3vCgc!f1_{IOxf^|%Y;nW}f>pexq4fe>3QZQ?WE#?UG9=-K;0rXHbLTMhXGpVoC<0Uk5fJBl%n^)>2g< +=)Cs8tGe@|S|y8q~_bH*fo#j0gsO7T?Bq|J=Z*-#)?J?FRm}N`_;u!4V8T-~XA

    8lTGvbmFGylM~;n6rJae0vcW2WX +OcKl}a#4n@xT=3I$Pm%<;JoY7Ae~GWlqiPn5TkaH7DNarknDt*iEQ^E3(h4WKS(E+@?8nhy?p)A-piv +DM1Al~?@DM3{Obe^~6@2>q@iI!qqU)sh?2}}07}qqJ!xWZtH1ke>%@@W{1#pB=ehI%RWf&YXND1h;8y +Fy&-qj3E)LZe)Kt4cX=6DhX7iNKHXIvW7a8ft;8rKNRcS~>J&j=s8$pF($m{X&gD_Hk&Hf4B>##yRH6 +4&?=>_Ovh1Rjpxq&z2|N$zA4c7^Gz@N%tk?iN1==j&BkHEC^7nATJ$+@*a&AzajNIaS_K%k3FF!22Q> +?0M;rBT{5zU1t03Ce!UH!`WybJZ9&)>+C=4W~Rs7I3t^Fv-jNeY@w$wivLhc0|XQR000O85nWGJQ3Xe +3zXSjP5)S|XB>(^baA|NaUukZ1WpZv|Y%gtZWMyn~FLYsYXJvF>aCvlSZ*DGddA(OlZ{s!)zUx;^okM +LvOQ6~IV!#D**a8KLZqVjt1X^06Y&H}rkW>tHvH!g@lp=|elFg|)#5Oh0kKa5ZtJ+u(u60RiO$ENKr9 +Ok~j{lI(r|`M<(rB(ycxY-Rl1{RgHOrobE{tV0&!6}i6`AK}_oDs4&r?~cy|8G8HC`&ma#PjZ%ac^nx +7k$hEPUpgn!bx7g=b+M`bYiJWZq=j6vn@9w~{V@v80WyxRU>hY-%J)l3a1;FhGmG?puE`dY>eK&t|g; +pPzKjYu705g#&*PP+Lm~j)C2;7pAULnBz`tO$0EyDUDuNLVmBylU2N1s2&U1xG7Ursz;h3KAIU8J +uFInc4@h;2Y0LO3=UT0^CaA$QJC9_FK34W4p`WsPDq7vG3y1?n18WY3Fsz`jaynedympvo6MYy~E__5 +2N5G7_Iwe(rG9ps%*<)}S`upbC^B)%P;w~vpQ`k#pBL_7{-q7+DZ#EVFQF}9*gu)NPn4A?}6#3Sh)eE +2o|+>dbd)S+izbIYq~j(4EYFL4KLAvusnHW*;=d)XOAfVj;s$S~9yOoik-`VbSj2W|xn^#P|g@H20$# +lWeOy{H&`#zT9R4$WSrQjxh*>SAUy=Ne7|PK{I;r0yvH5Z+D3y$~GLs00d0 +X`h-7SG3+P!_vl&F({K`p|orP>6{058ccsp;E4)^F|xsd6O@PVNA>C2LAR2SxzhrU2lHvfl~q1D^eecGIztGuEW-QCsy?ABp +F*lx$udp#}yygzc9H*m_bD-_gw?)Q)@dD+$Af$5w$mL!~QW+Z!2DJ@e0$qhvWYCaW~Np01*dKnLgqdF +)dIeK8S_Kj&T^f$4q1z>-gg}q3_<)%|OehBPG%SduX5zj^)N;jdRt6u0R|iVlL@14zl^6z$IoF6ytE~ +nnlN|Etv@O0MT9_2fqdJk +vL%~wqL+u>_S`3L)L6Y#^cw5KPcIDrJOvOgX#Z7{eSc{nh7;NU>obteARMM+E?SBU#h_we)WFA$b+s- +TO=pQybGMN_SiMOcIRnbZEt#!GylLL((wNjd<+nt(H-poJKFs1>Ea#U#ghS{a;g-GQaGKw9T_>r5Z0J +Qn79`@MmC+T{D|c85??sIASSv0icDbi>3+jAv#hwt&6FfvZI>ajQU}c@tKWv&`eczta8RSqkr+FZ8@p +@ANF^NvyE?&(j;Dgr0!*VKz};^Ks}u(}loLvxm5Zmy%S7hoK~w@2UC@D|ML8n#=Upf@6aWAK2mldXPgQ9H@x1{B000mU0012T003}la4%nJZggdGZeeUMZEs{{Y;!MnXk}$=E^v9pS8I +#gMil+-Uoj|!$gQnSN(04ocS-360-;?tA^orqF_y0FX=II<8GG%d`R_e<#(H_RTUrV=805K+IrrR`97 +WL;Rfa5;&I&2+S44$asDgwnb5R++RXIVDWn`^zTDTR7tu#t+tf-`cm0s}SsFZ8MgLGmgwr!q8}8^jf%m1(7(M{-QEB5pNwCIWK4;-YM%wL9tR*l5VNYwMiEzPZ#vkOetE)=5 +DP@t+E~CmRT{fC;%dIR_{yRtb1_3hO(P)$v5`p>C*+}36ystYvFnoXRk5WEL4T)0(lVo~b;dkui{4vn +Ji_b5AV&_{jXZ7>h_1%q~-hOvJp7Ifk!pXPi<8fmi$6h+$s3JeR$*=E%{M?^b`_`)VBj@Vqq$;Fqj~d +T6$^EMM^Cro`?NpTuJ^ltzCoOPFK3-h?{M-ABa5fGx=d=(hNL4OPV@t(i;yK}nX2Lo%7Sl8Ft1fBQcw +)Zl=b4epqI!m?pE04OFQ?k2P)Jr#6$@?ZavXFWvcZuc6^tzDww# +*64n?d~fhWj9g;v~9VtIEmW(JhUdj(?+MSa1gerlBLm`DvnqXjT1x2YesRDMiUWDJN}=EdwO0J0!6%m +4=DJ~v#cfZ4ROl`ba2H@XR{S`u74)tm2;Jyot!L{TW#iC)F;)xk|$`W69u)y)xt)43{Tm`7 +m*u7HJ$;bEnue!iWGFTKjdpo@1Cv7d;~W8WP+^vVNTSg>ydK43oj-0E^LjpMIvn=i?wP(|N3%F_mi?| +R?nJeIG~^S8ZTzaqCWp^ZI1&#mzAC2tdpxl6eyV98qo#;8D>bo;?ObDw#>Ww&!sQcwDr<^gbZ(wubIbLCDOSI{xD-Ep|0-O +Dk%1wReQFsHaw%2G9^qb_oJ_Aa?=d;VTH|)SN-W42!1idmR~~l3M_Fv>!WeC4;w=y3VX^}fKgAD$_ik +kMV_rMyZJwRk9}W0GMoBvtD_>>`G;vr|Vt^XyDE4*c6EakPwWsigA>KP(`F9sCiJ@iE1{`;acY%6KnX +=X-&A~UT=RaEIWF_3`k*EEc(VPIJ`fwr>!?xddhJGt8 +mRbfal#l(XjfB*p!n6)a0G^?O$42)`@myX@nyIcsQgCifN6(^PqoGh&7=(x_&VoikXxA?EmNxMQ^)P)h>@6aWAK2mldXPgM>dfl4v}001%o001EX003}la4%nJZggdGZeeUMZe?_LZ* +prdUtei%X>?y-E^v8MQc`lx&nrpH%qvzXC@o6OsZ_{IEh*10%GL#P5=&B36iQ1nb23XZQ;YSul$4aX0 +8mQ<1QY-O00;mPT~Af3JuXs84FCY1F8}}?0001RX>c!JX>N37a&BR4FK%UYcW-iQFJX0bXfAMhty*o5 ++cpyZ?q9)EaH!o=)D*Z+#_a*Qv}uF(+FqNYAA*GN(_{qLz$?HA$%&;M1)w#K;|R`m_5q!e +{qDc-O{H*r>rhO@mGSHmdo+otUgtR8uT3soJ`WUwlX0@@!G6MNQMc1Hf|#HEtOD$BCfsyz*2RBRf%uH +6+wHA=qPkK)ACWk90#{mcH6voG3On|7qj{Xe7deRDF}pZ(Qf-)c;Hm@>jCG0ZPY#io&3s3lXea5Mk +wHv-(G5A#SE_YvG^f0OLa{x_(A{z5#LR{TTF2gTm#r(cev7|k +&I9_W21dd}E0G%UVl?V%SVe+R;VZzovw){*#9|A($pNe0gh9m9EXuErk788PE0`^GNNN+$2xVE_kdz( +14#w_)7T+OogOEcdfuFKc+WF;6ysl+)F!;d^aTc9Pc98g#Suj)9W9&DkVTJ13sJ*s$mS3{RYbZp~i7F +@NN08jTMt)C1E_1pdw{c4)!U(PNpm0?R^y?u@e!o!C{KSB%ybxWCX&&VT}bwQsjxBBNOlx4zO3y`pD7 +^dhkL5N|+cj*Iw(pJR3VQmL`sO^Oo>aUXPyFus#Ker*k);k)Uu&c5PGr7@36LyHPkj+@>@gm8-S0KzE +hE+{LW0MgTQz`{^;D597(B0n5hI5N5U={$b5Fh0oJuH^0I37dC>pSp8DK311|{D=(Pu&a9y+x9zN5#2 +BQHQC+FxAF6F{*9m5$MF-k^ap%>rmR)n!F#rps@E>PZOrB{N|X?xA&{40TatHskP+x?Ks9dJP6%|Nx8 +nq|Yx{eMso&%RAFE*EZ@>Eq&)6@3a +&*8#J3w`rr9oeyD~?$z@>T>jcV4%IPIX_kUA5~#7H7`RZ3jVXzlYgCMppM6*EkjI%|0wv)o2DXCsfoC +IJmD6Dxu|Yh0`@8B0RwFi(w7il)c;|kMN9;v0y@nUkl>6%nhNc>tMV*im7Y#FEa1_dt{Xa82SD^-GJm +pENeQnRHDRz!~jTY_9hTa487PZac%5NCnL~UoN3U8g0;knMvN^WABg6f6#MzdZ`r9*^$|6M@2xCAq42 +5*QR`JzAK@W{o#q1m-=Zik5MO&fYP?D%eyT1^!_ex0*jI%Nv&;ZcIBKPUv;TJjMc_DthX@C6;i197up +DMNtCS;3`&dEfR?>$jZF#epY=9Wx>~7>$bMf|-@Px#XU}8l*-N(fk?G7xjue%9(vT)qS5p9-)eWL?uV +v)|(LGWhTGla~&nEED1F~gAO&wKG)zw=aZj2w8~?ps)Kck%8~P<_)}lVE}*FvM|wF<)V-nQ(ItP8~+R +Z$E(5E`i#Be>h>A?39E-$yhDP&>5fnwBrr&8z2%`oS_V0YsTslq@n;W3If-WU5vY4qX(vCw# +MNOav7OymON+9B9Vnrck1pQ+{F``(*Gy&EKElE+SnR|0;1`@B$v|Q5! +hTlHN&<#vh8SqfNGvHW2vqBd@J6%%As^CANIoR$LII?ch__54t3@em1G~chyBD|DO>WV?uMnEW$&a`6 +Yl2IxJvPj%Hjf``_t#h_a))|GuVbxFhg94Sz(1)ll{TXPM44=k;ExKNM&XEs(Co3rUU;yk(P?I#VcbV +a$Z-Eu>Cl!`WQeeLvg;~nFviUkErWc)MvJ+0X2rf)1yNJbC(t@U?c=gaZ7^4WTRD@$x&hm_BZ)+Y+Ww +@r2-Ks;ccat5dM1Hc`-Hf@FG3-aJU-DUti{rGv&d+ls@<~sQ97TM-j8VAt`$`NNCoXt3%U^)zyru-j# +vWHARWD=OBMaRS|CKAS7>{4Sj406xHNcUO^`Rabqs30SPbkmM%6W<*kM0BD!^(nj=WZMk@C~7Sz*R<*|7}CBlJ~1oL%D( +MqRoIk##ur!in}=VMzRLoeY-%c!WKKo+;S)JM;p?IsoTQ;QRidy-LPUejEy^EB +{AH-pbM6tTN9D|BUI+|Yvp&rWrI&^t5xHVJ}nlJ=F*nCE{L2MbFqP(ckk?#p;hCm5v*iPe4paz~o7gXF`Zrx +#XZ!4xb}ppZiKI8B* +iiajZrc_WPE^%&*cUbgh`ZvUrq;maq@dDbhUj6IZTq#dB}cxpXaHd}HB)wzs8P&QdnLg~y}G(~2x)4h +A9*J9Jues%Kju3u3X1027g)FBv(CA`-((rNFBpY9EET!<_@1Zy?nF{l(`%(;S}1+a39#l?rM!oUzW_A +b_)431)r?^EWf&f$8QMgr=%CX&$AEr!2-3Poy>3$tE!Ml1=Xl#rIuh*tCoH +s+63104?=GKxcMBrO*}Lolf3rTj-5@N1Um-#U5AiWl4qJa!C1;<0dUvkIgWTeM8XGjtI~p~Afi&885S +9>mLHY;bMNXSz&{A4*cucNO$*NvU7>`H@C8Jk}Eu)wY2IGQCCO{RA7!@5p!#2B`WT~$g{pAK1UKAsaU +Bx_CHm_A0Pzrne7l1Y35ZRuF&R0n$GSB(&XA$9u^l=vS%_Z9k_BWX@g6l79y|*+|{Ac2$IVPb6JTZZJ +z<6Sjl<;fTj=?f%T-}f(f%vuM>n1)|h&J&iW=XSGyB9|u+G}_kVNHo@6I2Vp~O0Vz`ruA54((LrHSh; +Xc_K=8T1ueEvsM4T5lGDlFH0+f#k3dF3%X$c2v9RGnrzUp>j=VKRgleu|cu0}& +V{%F_StNx(A9S6n6C*ZK)5p@rl&pCcP$V}9r$fHp%%&9}3J6!1q8bJ>Tn=+s>_!KOKot9Aj1b2j-I6t +f^`(X_BRXJ{-K*$ni&Kp`YvA{6}e~-jtAEiUucF0@0jPuO%PhD<^QwfWD5 +L*_p(T6wtGMhUtDGYKxjK$J-^rPgQeFIeb3dIcGm3v#RFJ1n3FjiySeT-AC>$JRTHoiTlfcyJEC@hF&X=U%eC^q?Pn$=88X{U8DTP2=#D{_=pNE_u4k_$pmOFi#5&&!`c(7L0c9%1Td +>5+X#jH_uS#H;l;m6a@VXoA>Ycf`1gX&J2vY9<(BamI-`}tJvl+*ED>gl7t*njK;6Z8oUciIc;r3xto +5^L!*T-_jih&Pqs+qf*L+lNvDgm67DX}O2*R}?v5jgfn>Zadb~W7m1fACMPyk&P{bCh7Ai3u47T-XbQ +OXAHxTI7n@$(D6LP@t6Lx~QDHNHh_wRKf2wYhiyhgO7}e~&8z8k65gI3rDw`korn_XGug!ZcR&q|!1x +F3y8{5+<&N8R2RGV|B<$dRA|MlP}{IcIGaCqvD;!fn5v7ySJB>Nw_Wolk-g0s-k@{m-WC(%!27C1rxuVHZrcnVU3-XPy|OSS-6i#_%40yDovIHf&wm^v +j$5NHc$D|`@_wZ(_vPfG(NeA`wI<=AAv12}J^Nx_b+kc6S#7bcpa=4xFT74B&TQxc~T!paSaCIf9%vy +DNWmxv3?Ah#KCN1XJhkuuy*iNE4AX6kg$%)f)Xc*?xq)^Y2tSZp(lwWDgFvwC@$JkT!FpclJCTTLFRy +&dnL&fE^}rq?r(5uK32U*h~x8b4_?j>sfo>T|BzuegpW4_(a^ +74iQ3Teijt?=HZY5w^(gZRRM>l%ywlRHu3m8Zl`jjgP0H1PKkS^q=6Hc3yn(@(a@$g)CqUU>d5sYsR@ +JojjiyXXw7GKb#;oKgX>XzofJKTe)XMLL*MAo$ii`eRTTdME`46Ak%fv)l(_35ebJCIzcCR1LuaTR7H +54*>WY5=P)h>@6aWAK2mldXPgSBT9_alB007Vz001EX003}la4%nJZggdGZeeUMZe?_LZ*prdWN&wFY +;R#?E^v9>SZ#0HHW2=P$Mu@DQmiCXitwi71=T{TEQ;ee;&m&eCak8s%4%3aSIoVv#A?N +xm3ypt&Gfcq%DoUOT1rt9cnQl$_x4OPNf#AM$;Vc6(a11-FPygc^aZ5kI{n*mR8juyVSTnbns~nf+=H($N1s;RZ- +X7jw+Bb)UHk$Y8I6e94?a8Ohlt{`I%a=mtN>ja2@TSX3UaU9GjT%#FRAn_+h0_NLYN~U^|7NJ6q=nAu +Mz5_y8wr3>ZN)Xa*HMHZWRlDr8Z$`g2lFn;^M=+e&nHn7m263J%=TLAHuq-X9tl28J%A?U)f*4ZD=_n +V8WFg(+}azwB-0xSF>ADu&en_qV$@Y!2=J@XuPWB8^g6zt7!eB^lKe)i4Kwga$$Pf#?&eT5=615T!MS +Ldv%ZzArILBPCXrnj8lReX2%4#Zi+v8EKP5`bG%{YgQoLGhHB-({p=p&{`ys4{f(C&_3BEKtA~-5hkl +DyY0B#^*TB=Kj3%xKn?vvEKnB8&$Wg_ftj~;< +fGnOYm&i}I>;t=HRWc!Oe9fGlzx&v4ViMvz!i>D5U_)ojW8OU?+X($Y0(J%Gaudi6K>3CwKOT|q$?Kz +|qXSz98p05cTWCI8k}@b7(3WvB9cep#n9;F;=Dkwx7_ci^+hAa;!yPDrip{a9&cYf7{sZ*Q2Iz}|t-Q +3|)A6684c8!;cO;c#g(5{J?#7UVp$7PGJ-{IV|6g{(L_?2>oj^=(kCpE_mDVz%@qt%t +=+?fyP0r3aNTQiyMffZMLx3jCdhpkV~N4Z$W!Ggh~ +(}(-i$PgdEkQ>d0o`6p{h}u7tQDRGW<+1~WD_SiE2wCIYg8nOj*x+=p4mj&9;XkR=|U!MYx4W2HKl@h +25~N_ws^9DlE;{nQ4j!YXl7Vh@+A*ViFqqm(xV9`bjlK|T0h%|nfCKNj7a2rS>!yhIxN3hskv69S@DO}l2z= +Z_jfN&*)kH&vP)XZ%3Qjxp0@gKO*57zxInMD`SRM~Zifx5{s6o|=ajO!k2L%Y$zkEUVv)&Hm9P2y4#( +xDgf2$YUsIztC +A*&`R5*=G?9UQZ<%yuO`i@OqSNOwb-9oKdLpx&Cyq?Ke#`J +GtEo17V%oVPB4twFX<*X_@uaj{en?(NrP)h>@6aWAK2mldXPgOz4T1nRl007n<001KZ003}la4%nJZg +gdGZeeUMZe?_LZ*prdY+-tNUw3F_Wo#~Rd6ik+Z`(K$fA?QOXdhPIlZ9j-ee1>L()1c!w<*#T3)Bb%T +B2+=vZN!WCeC92_nR3~A|=adnukQT=KJF}Lna7<_x$<5zFdD~)ih6LlYy-0m7foK|H +co|-~#ItKgAo|k+pl%ZwJ$*n7;WOl +WnyuQK5{FK<5FU46JqF5i9r@ZaRq^_Q!N-3nb-r5@;)5GMsL#Cy^3j5plbt;HMgQprt=UPkN>v9HwOP +fS(BwxY3F%RHhf!KG*(EAyxYq223JkBFQb7rvFJ}yXYG44yOyq2$GD8gil7X)ggOB#fI<@rNnX|?3N6|AHph0d$)2fnj=7)_ +{o3B{yru=q-Z6#!vk#6?+_I*4f_Pd?btOb?i7DNI3VqN}OQ?aTn4lEZ@66r1kV-}21F+8I^DOO2~R0RrqS(P1%Ht~@?>Zs9wPe9H^9Nm3 +}B-Gwdj@!Vg=t{->_O$O_f$f+;f|Bt^7U-pYs^&CIVCEC?Bu|ib!tDN(oQxMGke}fflo<#l({YkRh=o +#bbc5acHIT8$d%wezMJhVf&Pra%Yq>^)}tE;iGBhBbcFLpU?X?8fBudW(@11kPm&O?0H>_3=4+$8E^y +QwRK$ZXE~^36!Wt-0V*4lu*aQ*w;+iMN*k?Ernz~tNsmvLDM{(85#xudv`}EK#hCAwZ`LZB6{Tz>hL5 +}G(=;R+0F)#_k|YWH#As3E-$4>c45C@0AuUTxE)>@>u9HLpoexzhTtn=jw1L6Ojq#)DAbb)^uPmI-U9 +F5@HAG7H8VW%G3geftAxKh~Ic&9Jzn+~99E%g(xnMrp*2PEXle3Nj;k!GMR)}&7fUv?KkX5mDVjWzXn +pnIilHh(mVxX%lP~B-kCANUi}L&u7;E!`ZC5HgW^N=26gLff^xs +9g_$Wp6}kT8#Unoe)NbMq-92Cm)FwEkjgaW1wvQYt0va8gwk;ug#Q~LLzNZwqYA{&ZYw5t=Y1&j(F;H +cPHlQUaQwUfY3~CN(0&MIM$jrOm>pRs>+JWCYHd7eW?%E_$J>+A*p{RO~SmjLdt8Z6G;pob%h)z!khEZEh}Tbc?+3%JeLTk%K{;iZeXGjY!kIBPn3REshsZ4}T*S?s&n +KWEirSyVjh;BU{LLVP^NMz1*=u3!!5A<%#oEG6MQ8o;B+QPn_bIl74a#?BZV5Cq%HG9;nwA47vWB_rL +|&a=n}UJRMWDAK(Qt=*RAeEvcinX(~agHrmjw)CsFL^QA!T;;hI4b?DhVNhRPL7$O9@H}kR9RV +%?mEdroskEf^dsx1(fIc$5(N)nlq9^id`V&|{%RH$E&H|Q-Z;lkyW)cS#tt{5Ap8FSewq&0uZLs!RQ$ +o}W-to3*=1|)pis@a|78w@t@8HirJpt!ScG)TgL)GbA5Ra|;Qf$d2pXt{ny8=I*PQ%ga7JBW_vrMBx`oz?tbPPmjM6bdc5^`n%zdn0>+y>N{F0O-$^xGN(HOQ>6hEFaOum +2&SyGYTB`${my$ku0*NJpBvb+uuL;gfpy7!&b(8a7#8bM&OQ4DQJQQ!ZkiHwlqf4k>10dtU+27wPtx>;c)J?N3bPb_O8vDyitho_Mq$z+TBI!RDYT)4NG7yLWbus+HOs8!CNq5P?** +V(CQl%r&WFMnyo+m(%COSBXe`CT>V9&|6cqBlK*yce`0qxZ*G==HqFF^3*GL)%t#xkmXsFQV_lc{{Q} +S1swzk)984EPePTFtcMo`~_3i<>3kdr+vU6&Mo0yLRd!e?k%%C}!)1y6q!?R16!&thgUlSqD?^nR>w& +O+kqzh_*6y0AoHD=p)aI3a@1O$}p5LoBlYKA?D2L~%~z=7yw>FrYW5;gkXkzd0>_z4+usi4;I=B?m0j +fW12Knz~^4lffhWBBvoTa1eJ +^4z=cF_?}=l{Gvfl`&wwIA3~Xi0xk^_JGii6J}`b2!7%4^p(MCcMLu>-k6Nwy+1iRvmZdc`xeAV_S=d +I_IOC56nHan)%2=x;RTZkOvK1@uZ7(BIoUkZ)Ada^Q@ta(4_d&P7OjR&wm1v`>^t*}igo4xLof8)vKG +!KigPvizhCHLG%-{A@f}%Htw9D*+#7=%7BVIVZx7?E-lO4kIW^RM=J@DZ^_bQzLKFUX9?_~{7Lvg~yT +b1C#7;6jG`+@6aWAK2mldXPgU|30rg}T000X}001BW003}la4%nJZggdGZeeUMZ +e?_LZ*prdb7gaLX>V>WaCz-KYjfK;lHc_!uykETnvrQcGpU(zm9EO;I9csDu`70F>dNP0AQF-=rbsS9 +TGpiU->Lado?&&ReTUcr+S~exor#5M0aRMkpoojPWdCsxFGWtYo%gah`}Nd}bAw88+}VWrZv +bRUu-zl<_DN)iy65SSkKl3svFRrCik|uPnHQSo24jm*FS~g3(BB5I7SXF4M5CWU6eF%$sioPm&T)vJH +Q#JhR`^e6<2p?9Ywdh-z1eW^CSkD^gx9^KxT2I$^CV)P9sefKdWjfyQ0%7VOR|Qg#r~20ok +~fQm{&iaysrmXogHhAbWTdfzvk9X|ktKO)Q8Oo_re;$=^hSm;kJkcExy(~L?Ht?Ab=z6Oi%OJ?D;E9A +l^E#j1GVdhT#rx|^2>`?XYVg>qwA}y%U>_A&VQRS1p4iO{(gIV-9^o5`wXNF@O9e2xh@49v`F6R?kRg +GRFP+J^7|pEs;uLx1}1F@roFu+do+vV!ZBcDw(y +5$)s`vjxClU%DCtq;fR`DN3wSLAQ34`Y+zwxfDoVtX*J%}r>`|6^wh>u1IF(2mrZRi5voGQM&`_f@LS +9#EJN6AMAz$X86*4$R8J|Jl=jIa2+Q~P3DPGA`^W|uCz~)AvRmuoPoNo#azjIdMtPDPEgBOD&V_R~C`GEz+D*M2}S +t>F*^QRNY!y#^(`^mWxK=@Np2G_z#AxkMosPt~aHAL2*k;SJCDN;Fyr8TgHE8jVJ%v!QQhm3Ih?bxCd +GD9ZRoL=kAP8)D(BQo;}zOIq#@1Z~Rb!)nAk(s_5VSL(RGzaP<=ud=)pR!g&zOC=JvU5kv>3R?~jL1t +AYlEZbbs&HiWdAO7*kj?%a2a+HKVEA|?R(ZLb4T~3`Ji56!fB)_xx_UQ%J%4+1O4|I+-N-%cF(&{pXKi6x +AO@>8wc2_U=zW2`eQOR9rPRchNppTcAO-_g%Q-KVD~M<>s_T|h-)mwO9BWI7Ijqd>|PxDH#s +Q)VNfnFM`4iux>X*Fp&fI+;0IhduIA@@a|XXvi#)$PEdA0^~rUq^Z`_z;KGiKuN%a^a}u4+Qs1DILqqI0;J#oe-( +LAr@XXlP8p0&1;;Z6wF3ii?cqHG2?4nzB=ShZ8K#j{IWpIyOoZN+!58sE9Ka1r^DR(kkpq-+iT +%ZNVbF9XYQ{}9;&NQpbd={+lo-^C8xFLo}=7_7z|zJc{pYgtU$Ygwgy!LacL8Vb!Twhl5Muuz-XHep?&@_ +hAv{_dj_CKG89)tt-hhRybJ!@GTjQl!JkT1~dd%(HKu$)!Zj*UgQ3XQ^)ibK&eS0} +r|L8*)tyOOad{FlPNE7Yfm`d<#S&!~BTci*CDSUoZti((V&kYLU}BEH!O-wi}}+CHt*Ha|*O0+q{pt> +`}m;$_Ok~R_Sv9qcV659DB&N2%~`T2a*&aFoNJyAAPA)a{aX(gJ=Nl!AI((fxxB}1XZF8PT9u*F@`Sz +94%@@JkP`cCs1xcIJ9<2E27_fz1+pg*7T0U~sNXE!1?5k3AsS3RY3~3O1#b7QyUzW +ch722@f#h5plp84fCfUs+!S-&CpnyX7s8AlT%YL$zc6yeMa3URX_hW2MG> +b3>5HhNIIPwJt=Rs#wNoQ29~8toJNZG;%#EE3M1PTAAl$^Ep~|CcPr<~$q)247_o+)oGgHSu9gM*VLT +J^kM!31D3u2|FE{fyw(aR&ee(-j{2a&a^^IEQDX&iQaMe0Ei)Ve{kO%x*qyvhD&(4m02($m#-PI;bvQ +R$M;>u>nzpUADD1;<6EFXV6EzJu_32mJn`2Fm8eW20i1ps0^mP>nzXXp2hj&^bn7=jH#7VN8ZiA8V0U +Y9FS{$;1#waPK$?*a5R(UR=i0OjM%54}z(1o7+`IrkTBVVzao~4&b$#&`0`Tbfi+4BktG9X@?6WQ^a1 +#~D5DeXup&3s?>Tv`ODAUXiCJiTbAQ~4R1Gp{)oS`1FU|l*;tWI(?B&A-%?OMjbr5J|Cz@&qVK_tJ_4 +r#UnPm3$ieIIBBMDPai2e#xgg+N6?(3w@~&RAyfkQ+Evx${MfU_^Oc6*W?>&Hskv2b-S0Hn}tSf;cn? +ptexJb7eVm;N0B4yn25-WtH3rfHpf0U+hsMI)ewngGuCdyaVo-D3L1RcLP1$y|@Zsp>bud&1ngXk*oH +s(fjWcRjQ#`9~RH_$dmBJ^YDa?e_6yo$3H$_EZFno=O>59FAk4?nf!P5?8Crhh(5#3zEF=I?1iARq|I*ejk=41@BB+y}Am3W +JVH=MzhF6XFCc7-&6Y`Bs^@7-ua61p;-}#$6C6&@wiB#>hWS8N}KejLhOZi_;pTW?*auA`5fQQk75H7 +6TRNv+?^QPir6TEH_|u7ht{N56JKgL`34KoRHKR2;2;DH7X@QR;NkAQA^oa5;<>604Dq4C~_UqPScV? +B+3lWoG1%*%$TCv_cs^sqO;c+UeVW=XSZjql4)muM!htH2(RYX$PM;_k2JOkn(pt=dfb0H>a`n|x&Op +I;^on&Ak>UM_Ew$YM{DYFdfc2+-T+8OjNSGcC3M=&n=aFwS5uQ+H9zQ|`+ +Foj9!(1b$S9Y)Q+>^JhYRVPtKpn>U@Xf1QsiR#Y22YjKz%+*p`7XFCd`@TO`<5>UeY)L9f%9X{4+P(J +vM~IZ1rU1kXIEj&_Z#1W$ZznTGc8-eFCGNA0PYO01I_d3%Lc{vpWb%X}Q6#V~b|b8eH96lx0qLSFcbACG_R4>2ZsMj`S +TP@9A{r#~>5Z((GM>3A7@bfA1XWgh_oNUO+5l?QdT{5hC7r0QA2O`gc51R~7bOJyVOAi-ix2LM}%HYg +KQ)Gez`rN!0*g;${9PDHp90M}eChbs&kskuEpaDPF!vjFc$j=(Dfx4XaJwcrWfSk8v72m7wZ$6gEm1? +L*C!&^WATyYo8esJT>xD?r{TEm=%21@?5Z_*`K)!`dMPMBjU+aLa^oy#@B!(quX|B8 +%T-M&UKo#e&CFg(hZ;3>u>g+LimV+Dh7UM$%gY2fRcA86YpwK!B;5g<3$##Y_rZ*z!%=HL5XL1tprHh +t0@J|H!Hnq^Ej?@w4i|l27=+nbXaiHPIi37%_mZH=bZD@!c5ZYo?3%8}2o`J_Q~6+>+I(h1`6$9XZ4~ +V@T;Dkss->f&-GlZ(g1$BQ?z;yd(+bQ%Q#&zm!nkA@Iq-GZWPi2e>H=fcutC~_pR^mI9_J2o-^SWYdq +57aADf;He?qzHv@X&-erU{dzwE);GOivC?>lg=Qk|3*#co|147yh&5G{N^SZ*f=(>}mSR|YLPdnD`TN +bm3x``*LBA>YjT?rRrA$(i}r?<9iG=+p1;2-tSD-k4JsGTx&Pro93Z~oxY+9@Y#d%;~fr7QI>x_SRA|8Bp`8s>2~fS3|DP*B1MYn_?!@0Cdu3p%*!TTCbx%z?LTqGPZAhsws&l|zx|sD;AL@vkaB#7{?I +ZQbIwlbR4%l1p&{V-=oe1HT&KrW0A*(fANJfFq;DE0bb0u`ifq1mOgoaY&z)6c#P#tIrKCaRL5at4B5 +fg?U$Ak#nQp6Ovm_iyyK0IEe^Siru@jt67s<^02O#77VY-PMOT-tEDzpU}i?*bfuNaNeogSiggvjSG9 +;q|X6u~_pgNp%VaS5sP?{MD$~Z8livHg0)!)_(QYK8N@S4g=S=MOW`Yly;0#9v}k7)!WK6)xIOcOZS| +`K-PQ%mec{ZhTg3(SX+?{yE3Yy=yoYTS~&pW)4^z!SKvMBA{R_6+Ub;s;*{KzF%`WuHNrgWz*_T15qf +O|b!K371KBh7;`j#>yV8vs7Ozl6uJ(^Nf@i8tguQ~e23kQdbxBqSTI-9jRgU}(CZnvua3sj)OW9<6k`A_Wdi(t;vyiQ)h0SXU}NezH{iUn2&&F%oFDbi?)@bTlP``*bVND8&VI&!|r +Ax=S-!B_+SU1C;Zi@8e7?gP=RfnvQg%gaQ|n^8f)L7R;zZ9N7^@S}`7EQPDUBGHm#BsV(_pTBTi#oi^ +qBQRGWcGlba57GVNA`+gj7Pv<`q@Apw@OEnc8}?c8F}l1Q@wVT{(udF_nGn{kuy?GU;l>P6b +Xg%O{jSHlnX`2bL@JrV@)v4~=lZ(vJepzkJX+B-*)aU=G;r<&EY9ZHUHh38k8}s7XVf$i%~-J%&75aO +)!@)K<7XJ@*I7n%3J9R^vw6FQC{T({zdfLu;@gUrSLzXVO$f8cXTGLuNo!ky_@>g5IjxN2trAV4PI8z +R8&mWCdmQtg*?+Y_*uiXs5!7Sl2f{w-P)Qg+AOLVyrqCmFTnJo`9#jnrJ{QXVQAp0 +hXaxgS>owsHRxXf{7x!NKTc>S0idOP@>Gai8fVKF=kDs+AY-@w`T2RUxo%=y0 +WI$mRrQLF1x4KU%qe>b1YS@wwi1sR#{qsuo?qAQ`T*wHjn#-?f*)C6S%+~JeG!}aJccu%Y^GU9415k? +h+?#&=LGXTJKu1_WoIMhR0rn8PC-6sZ55|18Z{Fss&#*4nl)T5hRa1+|p1qyrAI%AY*oU%-8BR8;U;) +$k-z!o +ce|cNv}9>>yVuaXReQ*Oc-8oq7Q&I-(V5&rqd%8>`WwK7rNcVS=Q!1b(<;ioD!FrO`uUJMPo}kUJyAQ +FK#4~P`Mbr^{C^gzSIf`wG1$QjDC!JCm4~2|Z-dP9k~WC= +mLc&7Pfnfq181AYP-pB>aqw?J-=_UJEsEExk86Q=_f$(;@XviZsye-!XL|Irov&wa4Ou^*(evRe) +twO-rK?n^=E7le+MV+EW(*NN-=Vr8dSusmJ)n>0r2xWgWG3_z4!i^^Q+*M{?Q$SKahwjAdUI3+H}TV;QwColJw?we8iK#Ajx`UZ{C?I@HxXni}dvm|o_;r3Bu|S%mB@8T_M +!u&-O1-VH~$-3z&1lA|6Ivup3a$dv?^+EKiS!35?w?Bg8^>eLNd4=7~@AxIssaYC??)%W`C6TLY^jOZ +;?$j0+fgj05f%{rOO_zJUwZWu`~a`4rVrdGI@{&)xLw}hoGnhL&$#ZM-8hbLYd&EpzOH|goRp2>TJ#u +huO8qzU$nb#}IjkzHws0F;mPxicGn<6Q;BBjzutoCy*PILRj$B@rFE5~3j)7*Rw;XGe~r%v@(HV+nhv +g(OWBd$XlV>0NVw`^=0j9xXpnvFe3UGv;MkH5{ju=Sh!P8$pZ4bivc==RI37XFQUujB;(@CiM-{q`+= +2ujL)9XmovH4L3wdHk~v`#TU!^Vp$>KiAZ<{Ek&>RL(L~QrNsn!o(kFt_q|=$JorQ#pp$nZZd14?>PW +V=TCI88!?7CZ6h_!Q%e>0Cd2vk4J!UQ^cn2OBXh=t^tf(3aZcB5-V4GRYfij3bfAtC{~A7U8~bslrqC +Zk)45AxM8To0s}Svff1wWy2yFg)5cs+l0uZTl6!u~E5Rr)$jX!9{ePLT2SL0ltuCKGnY<2auS+RetuI +X`vFdryAbp_H4A3B=uFvkV={id%KV@mJooZKp}Dv>daJEpkX%wuh{Yn|q)%hJ7r(SUqSZBkBZo!4n%u +ktbE^=7&4qy0ElCR+a&6Yv3%dz5Oj071cuZRKCapo19))XO +AY6f&_Pw`BXzJIxCXeZ*y-ajt#`_Es-1>%{5J5+&V*jD+!SW^~Yb=WnL~)lsdjynCBn&8-oPR +#*(H~JF1cU?D7CS%6iki`6)e8ozpw)Lx5GUM$^=BMY*zLcs!1)vpmTw(gRd%@W$*RkZ)ge969a=N#{{ +{v7<0|XQR000O85nWGJw6z-t!36*S=??$^9smFUaA|NaUukZ1WpZv|Y%gwQba!uZYcF+lX>4;YaCxOy +ZExE)5dQ98ad1B*25iGknkF%t0bPh7L7zrDY{yarf +@8>oL4XldH^NTZ_yr>P#e4{2XF#CEZslZqaU(USFy#!;o*TwPpw};C_V=t0Ykyh8n6g#mpKISp+xt_k +X|&MTFYG1~r1xaw*9|vIP-E7(uWl6<3OmBDk|f99xhXETwgt=+Dtnc=Gz_)zP_l{W1tnPhXxM&5qCU) +!FRq^f(TVj?bg$ESw!r2QNs|p9qCPcpEbQ$#oX0h)~TzMg&UQEukS40fvHKtHK2l#-d2*!75TBoOJ0; +uF)6*s>^tpDS0dn1RBNKFjxT(LQ5p&Du#{N8d4EqP8nepDub2UfTbuI)DNhEghQ3Za#?7M2sTXMPNmf +~1xh9fWXj4ANlaKL8_NVqGCQzjuYx6m#NUt-a1nR135o}7q(q`JEN|_K9stvv&{8ffMWv3?_aPy*6oF +{ljuF{#NS;J7=B#s6#E$hjEWgOStd>1c6uVwx5!u$RoEnM-nW{avVokSANEq}%12sU*dU)%9foe@Gh0 +Mn8*q|*n;ImMF-tz0NU@Vu0RaIqC_NmfYM_qteDxrQjpi*3^+=rfoFp()WhxJh-Rhr-lgHwPTm1A5aU +Kxo+3JpgC@Ut{r5|IE%g+s>YLXl^XVxeS~-96Q`IHyC&fSQiJqYL;YVICS +Uak~A_S6N +kS1ac0eF}VAMp$4EGPeH!e!*vF(gKOIK(Y52)wY2~KHq&9*|DE!G0e3luQY3!1mGMkuaNP6@A37b(Au8XdT5Kpql!&yOzQ)mU=e(voz6mI=Zd +YCWBB=JeY-fXh#P;Mjg5p-9bK{kVw_^(ID&g_-i^Z4N7k1pS;WlpIY_PKiGQ(k0aZj?g$ZKQ6OCaN2L +rP@qS7x}vaBYgTCs?`De#LK~fX(>xdP(*1(#mQRII%1@qg&J}3=8d-6ZQm4?nd;*>>)2D7a05P+Y|L% +(ClG?8_}kM^Pb%I(!!oN?9#+7L?*XDDjt?VfC(ymAvZoJ15>rGgxHKiM{Y!^B>(%Uo$pQnjV631Uc7Rub8c_`ZqN47X +9Y9kvqb7owYjelvQ$@YWKu2@2ci3N(O;h($tsmO4o!B|`;&(#i>TxJmP*H~#`qO9KQH0000801;hJRV}Z3U%CPS0RIL603QGV0B~t=FJEbHbY*gGVQepMW +psCMa%(SmZESLIV=i!ceU;yD(=ZUn-}6^kmL{cAX5K56muX`Nm@1J65JH}tTw8-<2iw_NA^vyH&hMts +hb=kZeSQ9XuP&wZR4H2*O72*0g?6>Ep2(_JP=WTsOQRD?X_8>QTEv$n+)+#d8M1^E +;O-1~<#v;-sjk#5{@h0XSJZf^hCQv24*1?!?hr`hki~v}hIK;l6ZK|??-lr?aXvrSkP-d~Q5}U+Viyh_aD +pY9o+~?m{}^K_=UP<2`6@}0LJ8+W?_Z+);8Z%>ZgghxHqmbGCK +-{bsP2xidd%}^O^qi+L5aN;-}YvR1kNawP8zZ3}X*xxfkv6mIJd2)9|Bm&D`-&lrZY_!GW3spBvAu`}L5( +_xz+ClVa;u)*W)SXFx*&>MZB{ow7mML9lo9_QI0V#s0Ho0wJzPOsVdvVpIegE^cc$Cg{JX!a2ad6*Q- +DXC*yq6j@nH8_2ZhP1Mm_IF?cK%-E4?g)TGFPEdA=nOH@I=+^(Yk(ya0Xpt+;V? +iE+9YYZ39nl(5WPIC%wh@)uA`0|XQR000O85nWGJ000000ssI200000Bme*aaA|NaUukZ1WpZv|Y%gz +cWpZJ3X>V?GFJE72ZfSI1UoLQY0{~D<0|XQR000O85nWGJ>CLabA_f2e^%DR9ApigXaA|NaUukZ1WpZ +v|Y%gzcWpZJ3X>V?GFJowBV{0yOd7W2nZ`(Ey{;pp^I4CLus(ye6?@O}+#riTdMZdU)KueTOSQ0goO6 +Cpw@4KT&ij?d$Yk(v+$-8@a_uLCttJOE!@QP|KIwERPv7U7m>q^cPe(z~{rbnid)oPU_yzPb5q!Gs>? +~X|=MN9OhhtI@ry`@b<4-L!6Juh`eK5zwpKhc&|UxyyoKlf;hHW_(fuw_=0QB}|Tym$orLgu4k(40&5 +=fEXvS*MjV^JQ9lPIS<`@iL!;Y~ikdvkKv4q@LntWV)zWO@~Gotb62AbUzZ!4^%NHe;?rZ#-xIO*JJ?|{yLOS`u+GdbZCO{Ws+ +=NpmTuu!K|f)@l;W)lO!n{sucNSWXL^0181jGsMni>z|Z3dX>%1P9tbJ7F@)I#X!c;}jy#+I>xp)iN~ +}E~4**1x?dE}v5$%(YTq&gJb|l;4d}Qsd=;{WvJ|}SHCNnM0CgQsV)7;da+ee0fC>5MV$rCHjUzh>{d +B}nNjLcL|wycZ1)Z|+bnzUjJfT5(6jA~ZcnouYpE)vaAidE^Fy!rKtu7eQGexw7`5wH%VfMlkmf!mm} +6K7ec1#C_la;!cm9he5t|NJ{HN&yw8#cRByjbdgIG6}xl$gOm3&Op){%U}@C=NwXHx=fVdDs2Rmib8b +FWoxWtM4frRP1ly^_=C9tR#7nbHM7jm~tSS8U+STxC^Et$-o&qw6O>ASvDF)kB*|DvPl +*Dt7N?<>F0;vq!cnE&=Ce^KCcN?gvEI8$i3)ZYajzvCjwn#=zta#7JUN#JVvX#vuw-vJjv@FYI+mD32vTSwKdup81HY|Ff@J23l)y3wm#M&b_mkfN1k7lcJ7lBP))z~gU1d}dVbQ0KbJ-Xj_pLJ2!;}hKiezqqz!R13p!3v +_f4Z{;ybPba8@&~+q#>+zoE2aZ^a2b0DtZVQ9XeukO&& +G8NQi^(L#=54Gdvt0Ca;`+66E{e{;o>AKgJqt=~+qsxkVM5I`A&!>HGfW4-MpTJxUn+`}O+q<)gx +Uf8Gw`OWXC<#WToyr4X +eX&Jz`X>kkaUWvExLHUyz890;a$C+@zNo6qvVK`+MQiUUPFJFsGwtqf*Cop%NEMnI#j(#raAH5?ky8@ +1%a8cXj>#%s4}YAZoYQ=D%HX*+QD%-&F;ra91lFc8oz@iHOkEs#?WFaMi+9Fv_Fw2S7@gSO=u0>nS3{ +TECf_>Yg`sa})3mtOo=>+xP&3bU}c7*0v0g=6?UrU$3uOYJBNj +)b+|4seide6Tm6PNsLL)$||O$FUVb<_j(n%1MewdL$>ADd$@p>B3-DjCNM>}m1yAe^z=e$dcOk!7x$1 +@C!F;DEXlpnRh1?~L^@vVu?~Y{rZyhBJu^PM8UXV?lepBOgV@>kE!8RE0)^OffV4m})6ZIYW#4ogfTn +L^TcUC|%gb8aUC1fgThB)@FPM94~&Wr%v-!{j7>eB|e4=H{M2ygQoU^hrs`y-k*ONgzdmZ!Ss$`Y_Q2 +P#mq8wk$X2P$L1R4ewL(;Oa-lLC?TFROS9|iBj?R&hS#^wO~!s=dc#0P9qySy4%_DKA?^i;u6oGCGa5 +n{G6x9dIg0i|OY$F3O9KQH0000801;hJRc|H)-Y*IO05~Q903rYY0B~t=FJEbHbY*gGVQepNaAk5~bZ +KvHb1!CcWo3G0E^v9x8eMPOIP%@Uf~%m2gp-Bc#o`_ecyYLFdMMD%7HPNpkP8G_q7!XoQAbiq+#vt`W +=KjRWl46gcfbinYE#4Ee1Fl!uI56KjF%JMpm==JsG^kZaT%? +6Rb;d*zOb|cd~-V=nUHWuyZ5vC>K;~9Kq63)u;+|vin^2jPV)=C+tDfqMk=XjSyBYJ6oQK}6g#G9P8I +DDNcIcxEs9Np8)I_ERLZKyLhx$GDm8{guzw{PP@zPVfebrp!Uch+Fm$8zEAC-3=_?0=}GW$yPA-g@uP( +TVuTGcH9DiSG=-qra9LNFU2BqYyxkaW!pd<8PvIpTI|Z&)Qh0IA;FfL>eCbrKk?V ++$_xl;DqLRh{&c-!)ISGNDv4&3D)JfDZd|jUa(|AH0F}1IGaX;8Iet)oaf&W^v#c}+oPd|K`nJU#u5U +jM?UF~NJ{e|SkFW##FCnVWU{KCk4mPvpMz+N%60o;8ho74d6~?yfbZtX@0*Q8Hk&XAVvR6rrTAZDZOT +yFG=yX)SdGo_symdVz&2!-u;3%qtGYpU@?=*F=x*!gjl6{#D5DU7NHC90S3F2(RzeYBN+_s^o^%~&rM +@C>j)h4t$IpYh`Sxq=MejdZQjm5bD3LREcennan|6jB9u|&RgZ_T~9zW7YUgSrl1n^L<*$OP02L2W+P +yNCj%d+7tBJ_;uXjmV^Y9qbhj?FTEMi@jy=%_nW={l|$gKl9e7)Vng%2utvcSo1V*Kpi$SJ7VOGK%sfUJfWt*=PNv|eZDOcwvQ+6<6Gv +-faA#!pD9%**z#2QptJaf%xkI=5J%|u(-U5F8yzpL@X1+8PD6N7a6M5BP80bDm^z^w-YCDBwk@Z?{Gs +O$`Jj6wAP7R;-WfawULLgMIs)JzWUB{oYNTn8`c@R%J8eF;BX+txiPM~&3vFCoif{asj16h$8_*aWvvwj)sigL92O2K0!v>LLbhbFt>YB)QW;k2IOw`I +5f3`##PWyq+ByIa|KO_XMM>dEgJ?Rj>!YtuS>ey^ag($NJ7rspf``)&biyIyxc!{)x +kLoQi6@S?hsjG6|r!Q%KlCO=IZ>+7>033C5+Q0$pbyj<8*z8;Spilb9mwDI<3Uk6CvuaLsmHu`90L@} +|o5?Md7(eunC)U4%^rA$&+TkclC90)g-i?+DrzG)lZy2s<*m;i)oYpkiQ1E4rHwyS07*RXM01s&jGfM;S|szu23B83T|E6I!)PXSnX>TIvR+Q_BzLaDzP6<;vHzMVJWiIVWiV=Oguj~axph +{W9Idr>->Kx=a*JguLmL*nuTdek8#`k4GwJC^ZyIahZEx?LU6b?0n^e@Kfy3mspUvGldTo#IpeV-^nhnk#0)|vO7dC%#I!vgcW@5qU?w9%ozCO@by +jGT1o14+41Y1$JdS-I_r&|@zw+hUjM!HR$Le8|D7`>BU}@vLdYg33?Ccz$A&hW-VI_u_;pi$7e^yG%a +8i&Ss9^SX7%!r(N^fWBliAB|rBrUkoW>YnGFEc=ozE2T+F6D4GDppjs8AkASt=a%*3*UO$wV}u{CYkT +1ww${$>8^5jXf4|WZ)~^hEzW&W&zjuZJA1*(Tzy9z&>3NiCR!qhnJStAmY3i6{MxBV?GFK}{YaA9&~E^v9>J#CNMHk#k(SFjF($bhY0d$)_jEPO@VB$oh58l;o +%0Y#wFHa(+`ENMk~9JlCyzvqRdNQ&}I8uZH=d+FFBACk}e8+EhU>}{u7*(oig*p|BQ%Jrcyn_BFePL# +FoWmQT1;bJzMU0gg8S2qftRaJ8=>ua%T_Ip+LLf*mQNV>zciQA-FU1kh +?l+Uj&FL+8@ws~=<>TT2It=!y7TsB9L+QQk$x~a>Jtjgb1QOmvZr@{$U2jFragKp|x +-m1cXQUGoXgWIB0|2mX_G{EA$5OXsL&}(47v_Cxc#--q>j~mqn+sZBke4fJEo4)Bz@4M=S)Hm;IeQ5E +y+Ae4S{`k7od)aSphF{*-faKbadFk=Vv#x78{`s})^q^_gnt;IsKTLyf@pn6TXvL`RHAUYtn^P}n(V)*+Um +`eV3DuR`nv0hwVy?rO^YxNEoTu?vY=7imQj?k%M4qK)jhw{)7JUcg +ADDrd?23O&K>_K+|FhB>iQT*NvP%lYBYbhAD%e#df!=e}3u;^Tq^d>_5mm?y6IkNaZa_YiTj2$LTL=( +f4JwVlPER%p*Um->srv$cou0BU^(H=#ZYy~zTG<C%@>dv08SNj$R*)^ +}=yVGIQ(+)K%1Mh771MMm`>u%$42!CE&AZCDTR@NrvpaNdeZ&^X+peW`S7Z+Q#69sCAmaXbG&<>0rV5 +iFj12HxNCcO;UlLxRay9~9MKeyBkE8$wo)d^;+24dn$mzc{f;To=DL2Rtm$LdfbCU-2En-|PMi_Fl=7wnCxYW_QlV%{ZykNKt +ScoJy!+$PGjW2+cHik|xaUt$hw{CRVgI5GxNQ8OQ)QZoH0M>|(dJA}kZ?v}gg|MpO0DDCX3E3gb5{+N5)UAe#T(3=Vt6wg7bc;h4xK=QILicG6&w-Bfo%$dP0K%b +yMukO-0Wk18S4-o?JgVS@I4Jg3>xCiAffP@DOXS&(Ju+`vE=GS1NH2s_*==sn8@xjdo)A|5d{#QeNq| +r^^w}d+a;L)GaV@&L&XKceKsOuusz+!m3LANUHjbI%=3?f7h1abOv6{^k*l;d_aWBSj)#i^P#Na?BA% +Nq2|c6HSq3`;!{FL$UDtp7mTSPR{%O}Q(_S`1GMLt)3BK-~Zt +gA$mWTd*|y(};H$0i=aN9`LPMX9D7|B-voI$i!ebHsTL=o{*g$iAwcP^nlI20o?Zc7STFhcM*fN^UV1 +yc}Y&@zJwS?3HX?Q%NbkpTB2&EWiq#*L5|UX$Vb`4X67E(BjuA^{}8^ukEdgn2J`d3syEyQ`prg@)rq +ybD0@vXouT@3987FWE8IaCxc#?g;E+lES!4rD1PxHxbfFU~q>41D1?3zXzi}eX;c{1~HEG8-3UI`#9d +=?VO9uM1`ZX+n?|d +U^ty2!1-R^>v%Av-UF$famli=1oA6W~~0Jl +o~^!<;xaR};;H|1t9H%CgqH67qWb|vUO+LVa$C36x2iRn8;lfkfI;u@IRhM(vaqVuOMpOV}|um-;m%$ +o9V7ao0*UgB_kVqnsfzORbTsa;Kd1h*QfOB!%)wGFA|)aG{1D3IF1VXM#}O>5CrpiDs}faf+W5-G$1a +_HdMPvvCHe(aLzUpemep9Gs49=Yu?>WSYZCz8;0z^@d8q$*y&+JHV!9O|ct$}Ii$H=qr_=Q4t6KW^Sh$CMx}7FGp?J3DBwQeXrNu#_r(IAq%6Cn?t~l|VV +P3an`EC}vcGrCDyU4NArQ6+U*9gaPjF)DW9wUxla+hZZ)Szp}WFp1bgD<@utS8M_E*Fps9LQJ#GOQE} +rzP)ylqWk@>U>Jb)KFqY`S|63aCXbq@W3O#mZ4@L|OC%J|k+>&|qy}(DLk2M%v;|QXMwpEqtsm!+rk4 +*Lo?2Ft%Y$04GI}djoSf*7VUH;BrYU3X`>9OA6Hz`UEqgJuh`A~o4Gjq6Y{14Abh(O2kiP^I_{W=DY6 +0@Xn=^iY +&C$ravaEJM_N-2bmLBv1qbGvyR0QUa?cYA!*J|ICzrp4rV+I}`n6kZWsj3b!3HZ8~H>VwbmcWfauw~bpdy*6lWrskLF +lXOL4g6U!f|n18$~G-^rwPR=z(>6E6qTc%rDhtZaXfyGxyx3Q4ZW&^ZTZ%u#EqpHBChoV`-}T_!!9y( +1ky5F1}dZPwGU1{yxq~E|&5E$#qSSj_LVHWTOCKD6(er@Nv9@*?S@@0W#ilKr_$DCq7pY_Ji7^onQ^y +*!0Lxo2*A-u!pTB6{}_At4#S8z}wGgJ;9kTGDFSI$Z}GS19HU5cexPH=$T5&*YqG;CApsX6q)zCv$M7*5{%GMT5^56kG+3qKk&A>f{hjLzdV%K55Bi=q+R*aM@w}} +$Nu6o&bN=3T~)?JCQe5ZErJ2{vrI?9ta-pG&%&{*u$#vy6TR&{^ +qvdLJ+K(5@trIym*Teby|TIY`}3Fo_xjl-3kbqZ=WM9*0Q;1+ungi&HlE5r9!xWb|?T*D-la04{ao#yG8p2XCU1eq(7hh{>Kx +sJrJ4XECHiaON*T$2gZUbvfuxh=i&+v4gbSLDjYNc#KL!Zoq%M3HOX{^9p=RO04)KA4L`&cEwannv+b +Z24tq*hQ%|84EhI;tg7mghQrma{Cn2C7PDDwLe3r8@LSif;nGbE5Zy7Le$!eJl13ShKaDrY$X_!aiJ1 +UEbzTEQ}5FZojUY+)(fcrVixA`ZCY~_?$u;<$ndm#Qrd7RZm$N(We3YrPRMn`oU)HH9ws;qXLn7>1L`@tw6q$$KJO-d|$~~f#43q?8ut77;B}L{@stO`w +Hn(cX`}1r>%b_=UmwL+*#Cw*$rVy@QJ6M{V?vXN-s5lytVB)#NlkbV$qdlnMem*L+XTG)ADNLrhMmO} +yr*ge#dmF>zO5An$;nL@Ud-b9TjJ9fhNG0cO>-mx*2s!~7U2c6lU}8a`;T2$fh(yQBl=utRXXU@LeK+L&-ovS~5JYF=E)?oZgw=?MpA4FY(xAJ)Awr!GHx3X(La{vK;a +Fs2wrF(o>hQ6k7Yh97%*i2i!+Quu^FQ{AN=2mJo}+10mCzrFg_U|eIu4B{z|&e&00_Qb?vq~BYPpV*^ +i*dceBticDfbBJh-E}f@NOLn0F+-AY{OqF}qt!HyoJ-axYX2v?qtOP1R>H8&s$p1u+tra1M+p@*4*r; +L<)o9bRX2B$oWty|6o0rdTY|Ifs0xHQcdH5ta(SOZ_NgN@a-@w32ID!I0=7=9~hwG%lCkWS#^Pu9#X7 +xQg?m05i;5Xl0f*C$iD?c0XKwXd4NrKSuUduQa%_8hlU%w+emZ8k`jAlPTH(zqJN< +iC0L)3bNLt!;~5V|_ZxQ=Zf`)VA*6f3XS(lyam}SGdOJmV(b2p48+d9nB|}p0KV)-34>*HAI8)nlM6f +*V|{<9a_>e-sh~#5<{QmPDym3S33zdii%y-ShiN_(8a}sVKwM}s}MCop~XMbgF|RJ??JutJe1IDdfai +d2eC5U?xc(fUi|D8Pf0X5j5Y7HS2ZY8QwpQYY+01)wu6Y5SK}L+8O_ea!JlIq*(Q$7;#lwT$|iD4DL% +XgXnJYk&s?`v*=MsyAUrUoc&K{JLw9{CgWEuQN|4RiDri}TiTcuA@cnu0Y<>s;j4K*I2a19w+a^G)o$~_9HOxR!6Q(#~J +HQZXu%l?TMx+-8M~FK872YzF5IhW31-xUemFq6uG%fZkH^5FHets0K)Yc@S5Fwz$l-!Fa7CBEA;t6rb +6YeW`VrIk%vO(yQ>YHgz%A@p0sYIU+7!A7Rb!VnH6WfAIL{)%VVci`C88*e$FJiU9S>QZV%LRJn= +S%frgTK+%Y>4@%#Q4)$%vH+i2IWu`@ULc$O2J-s;LOZWi+^IB*=prMDW|Wg;wQMO8fY16U~b5;D-%$- +h{e~+Q-a3WNi>YO3#<6Kr;h(faPM%rGitC>cd{Oue{CzQ%K=5RO<3UHRx;O}{c%3C2A&~)Y(_0;67w< +#TsJviJ;#shsba*W>qr4^Qrkz2Zt@m9u+FFFu4@$U09c51S^j2li5DX+$O0L$B5_gi64sa!2i$%*4T`A*ghY03M +OVv1$YBe-k57S$n$v9YG3}w?{N_L5(q3TvYs@WCw47T&gWmJ7jaULJ?H(W)qw&fSDmN;zyYbo!X*m4bx>RrFWvtG`dHDLKlsqA4Kaf$ ++GW(bilbgvY-L@TbPg>i5jt*kPa^sH6jZ={lcPFsGO1_PU5qp!vH4urruL2_Jrow;{+Z%?rc6||fJ`W +7j&*JIxdbcQB@K-sR4{*`Eq!enTvL30P5ZEXyHC +iFQc|00C$_Cm`*u#__bFO9+jB%arXU0rl!L*_(tZ|xs^a8as)@I*Rx}Rw@u +0Q<#2Z-`U9yIw+nB_}!lo1^6#)0vuDc`?)1%HN}WUyC`s>e3$TvNDjG)LCUS*}_SeaY$b|UqKI7YyvFTD>!_0$4(-B1&atNn0Rn)_JHXE8S<~EuPsLdY?0WB +ftLJ-&eom4}Z*jal$T2)I&gcYK*pNFR)I2GZj@ryE`?!Y=xAE$?PU8w`i=~G2+@tDsvYddf252mG66B +n+HA7IS^cMOg!I3_dlbF!yCxPBjP)8sm!WZ4jTL=?Dk9fN}qbm$>@)>f!LIvl{MOrPxxVHtFCVvY@KXRF3H_DKvIOpf!o +m0p6C_Vbc2Rpkt2tpm*a}1CC2>)+1S3t@-<=M-ZbGm1CK#oE!kw5yi79)t0jbePelCYgG3ZQQ7^vd4G +4CDh;aOh;T(yRB@nR~?=VDX{Mv@#?%h3egIi!f;!WY?%MwQUs+5oL=Ueq2mVmF5_q3+6?e1--Vg?4>G +{=}se(*XP4Cm2@Hs3vC6Ch_O;@kUio|kt|p8g?ir-Z3ba@jErlEMgj-{#0`*G{Ci9IsEf?L6IfYva{i +>+kcs$w=o4G_sX3r=UJ2VDSslJg5Jhr*9Z!;#Qac?tt-}L%Rh<>v%D8Gm^LDKFLG%-kD2+X|F+fJ7hf +=)ek<={F|+e_2_+WfE$x?1tI_w?tmLL?GoT8Iv^s0CzQLwXu0tnc5hPBnXtOVap-3&6B>S7wgqPjz4v +E)lA1Awd^w2D1N4o1{adj53-0TSSIXlHDGx4$K2*V;_mSuTKl-kY@#q{QxR?FWW +e2`7k-VozpzQTY+&pGdhx-8bwUOd#sOV2Yv6)hQ@tSJEwIqG7>btUQYED58ubIHkpGhT8?~D^LuRlm| +UGDPnxMYV_^8Xb8DfY83D*k^^O9KQH0000801;hJRR91000IC20000004e|g0B~t=FJEbHbY*gGVQep +NaAk5~bZKvHb1!0bX>4RKUtei%X>?y-E^v7R08mQ<1QY-O00;mPT~Ae|j>hzX1pojg5C8x#0001RX>c +!JX>N37a&BR4FK=*Va$$67Z*FrhVs&Y3WG`ZMX>4R)baG*1Yh`jSaCx0qTW=FN6n@XIu%<#gL17-yKC +B{jwLlB1s94mNr>Gh^GfrYUu?O4JCLsR%e#f3`G69y6+GOl={mwOVTWaH2rj-*rSIFf=hLa7qo8D`o* +K4WP&8xPPmC;+~_NB0_KD;wL6HA^w+~Ksn2UkOIAu~5)?}QSDJ8fp{-_l8~crjzQ0yo4rW1kexCV{_{ +Ws*wRW^98S +{EdVga=Q;YfCM5nJ685FZP$lX!8qVMx5>p>8T*n#`=Kpz|6fAMIOAB$f%-sb;fRlMF<7cMy~0$i3&jI +Vv@8B9bOGX2U_2UW8~&s4MA|C3Vg-iwpiQzRyLt8I{pC#*O5jPlO|N|>mpk +ch9i&4sw=8qJ4kpCHOGgAq;Sj%CFBqU0ji~7d@u2U-4^rjlk$3S$?P4^YCM9>9fw*HiKWH^a&YhDr)9 +FBBXn093meo3ru9S3=7xJsfnYV^{YpC0E$DUb0>t#$@86K%V`mrYEqxApczT+3?pfAJTE9It!NtHF)nY60LlYQU}ylOQ(eOnOb=lSwtItz +IXXX~>5|xfK~tMx!c|83tAX~N2X27EtlvRX8uIgYRxfBuFJ5yiE`4{!;r;!P0kztrACex3UiJm|_lKJ +BQ~i(|{I*xnB{y8014Ih4veOcit>IiQVv*FaL~*C#g)4Qo8EOr7OeN6nIoqhW# +T(v2DaImzY)kEU3{%+aLpy&swUui^8nd*E5&rd6TW;%Y0-r8F4*|qE~s2aXmuLoIWVT1ms=$)xDV(Bl +58~kKSPepuw|}g&-+U29pHS1wDGpDK?BRfdpvKxJ;+98&MtdzB-yRs)Ny}?Dc{(EQ@={;yE>pb{!!L5 +_Yv>(lI}uY*u&vBiQ3B9zaH;}h*?1Of1b*1u=Ris34I+ml+x`ij`(@+q7``-6-DF{A-0`2bG_b3S +=YZ0Hd#tmD!2cVBiT&oy_2yu84e_&DBpwIX;WfmoY41;#;G*}}X#l?rhH~wH&GLmziR;po-?JL8~1Z6 +uzPUqTKY-TKaLf7*Z3hwi$a;q1AMWZ1@jC_K}Xa@vTZ^{jfOQ=BUzO!=Je^H5OAkXYU;bBXDMvDblPj +q~Eq#woa6#tFqJW>C?ihi#HT!=zAzx%VSo?pHhQ1SUgZBR|6RzA!PK&=2Yy-JbD_;l@`Lb(RxM?eR!J +=BLj{v7OPP_)B!2rBKNl%75{T|BVuV3YQD|HItFxopNZfFG>0jR#)BFJ#M4e;l?>w3*$Uc0X4}9sLhb +O9KQH0000801;hJRs5~bF<$}z0DJ`i04e|g0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!0bX>4RKZDn* +}WMOn+E^v8;R84Q&FbuuxR}kJ|HsBiAVHgk~J8bJd4jY24cSEt5SgkDyl$^yn-FuwIA_*%6#}O}j&*EHw_@`*$CjJx +S+~JRIMy((!j|A3n=rKsvY7CLkF(H0wzZsztM#tvhxev|Jy11uTl@FYEeDshZz`r<4;JlH<}FFw|gD>);paxBj3v{z6=v^bgj}fgWU1{ize)X +rGpSc;bv9^87&8z^E#2VxT2?qwd@{Phs71&H7G`5*9%}hMFlq><6@B@S=1v52(vSoyf$0jcD*#(kDdX +1$CZEzw1L&_3Zl*;0sO`=5epD{JcX^EpwygMGrLRkeBXxAMJ|??G?Wk8sAVx)#mwEzkBb_%gI#k{hjx +^PSyszCny?a=m>KXN7mbsl&kv(sXI!SUINrc7%QS>uUKedtYrx2~2MHTKi+sm!IWu>JPSTTVEnm0U(Y +{9%Cs|0pZZRjn>ibN9d2>>xd0 +TwI4kQhR*yA1ED%$85F7&RHJk3Q}7q%v?*9%AfzkRA0@#)#f7P`Xcd4`~^@;0|XQR000O85nWGJMiwm +ZbOHbXumu1BGXMYpaA|NaUukZ1WpZv|Y%gzcWpZJ3X>V?GFJg6RY-BHOWprU=VRT_%Wn^h|VPb4$E^v +8$R84Q&FbuuxR}kJ|HsE^OVI2@4J8bJd4jY2CcSE+ASgkDylAJXEev}-!@zT~8+miVB_#UY&%YLKhf_ +n}-RA{-ET0yUOvW2a-;Phx)1Rp?rlwpI^dq^asXDxqy<(0u&xsXAD03|a@Lv +zxg5Q|pJXyzkMTM@r?5Vc|yYrPXRc<(~_}#}-Xwm!fh8^!!rJQ>s|j?bsNrTXgQm +T5ZdCAeH}PuKqihJ&KYQ&*dQndRMs_DP^;xqWJnCeOEgp=%eK#Ep#+_*y*TVA#{%DfHcl@$@gKa$qr5$kC(pu0vjLliNv1qt`a;>KAd +P;QY5c_w@*OF!p4m4_7z4g%XJ!RErKrQvQq}rpZMmr}P*-L+%xcN>x=!hD{qYDW5NHbf_08@jK2CsB# +KA(=hY$Ekwd*b-yVQ!8z0rzB0<@L}P*&JHTUz4U&3!aTkJu%3qSFu_o_4c@22v4|uq}u9}iwW`-X%pf +6ya9>@@N{Fmc0mAlfZ^kkk>%3ii+4wEh#~1&pN4FGdKVC{f`~b_osE-1cW7+I>RwB=pj<9$FW5g&O9K +QH0000801;hJRWh^?E;0iE0G0><05Jdn0B~t=FJEbHbY*gGVQepNaAk5~bZKvHb1!0bX>4RKZDn*}WM +On+Uu}$c5El5-Q8T$O +y}eI&6_c$^aD69xZ|V;1%^AR6&Z9dTheJm2IzTvBv#L+1tO$%MjmGGWb1-bS`>1ev~fP9m#R;n+7?A; +^hhQ-)l4b}M)9FO%yJM6)K3!qUU|bC^-~(HMo{ijP&>%lPEsK}o8t5Yibdn482H`76kw)^vLN`pQ`Yf +e2*-S5j5fP7^OyNB8Qp@l8;8Ww+f338mjj4hQC%vJ3=Oun;O5C`Jy;eFo}i5U9@yqj=z9$9G~=eXkEF +}MQlb_yv(@}?sDp80LYfr#6~A9g1Fh5MSQNf_U`Ug?Soh%G2V2RQ;v+Ct6-6PSBdn83WW=RNN%jP#QI +YGH{=E;Jp*(KTnQ%pF47m0I1kvUgA{DvKU$?c_lMn2WSzpy6qHkT$Vds6dtp`*fMyz!!BGID;CG0Qh? +n;_V(dv5<(xHRWZq2%&^N-=u!nr2Oj(*e|%q!qd&v+n&@+aH=(khq10%a~!k(cDf)3vfgpzMP-K-?Wt +rWHH^;V-R`nvuH=8#!A9Wl&`%FgI~`SO3sbt=_wplW)(zrw+$G0Bnq!Um)hu-JcEZG9p3HY&tSuDaaw +(!|w3v+Zd(QNCJpGIp7E^Hi7j}tkLUCOL6poBwS>+sMV!zJsqzP+=9R?9mmt5SBo@CJK~}WDLw}(EV2 +vvjABieS*cUO@Loov7I2vRwO91>tkkwRDZ{C+u1rs>`*qg*j`V{*pu&!9H=P*#^WWku&m0m_yNmy{zeKaj`T-w3Vh02hU|Vd` +(cR(2n&pxxqnCr@Nt*aRcdAY-%We1AE#~O~|KpsCWidT;!Ie?LEUL4K?}~)WCgfat`7BOY7_bYQtKKf +8|E`g(f~w*lP)h>@6aWAK2mldXPgUYP5=Q6&001Wh001Wd003}la4%nJZggdGZeeUMZ*XODVRUJ4ZgV +eUb!lv5FL!8VWo#~Rd3{n%i`*~_z57>)Kp}xyLl31eusv*dOADnt?6nl*nJ5|WI<{*ooerh{z5bYFGX +(03Em==`dXgHP^#t1Xy)r#h#k7HS-VQ)#7rk)tJ~~p?NX^QpQ;v&YXj<#JL-+3Odw%lLIrL#d?6Ui_w +J#r~*65c=We9!D#z#^*@G$P3w+9R}*pJ4b7bitW5h9Od4Dny~$XFtb9&RV*yhr+)eYFrq4k$#!7;CYE +{it*+PDey73eTEBJSl1mf&3<$dwFIm;8$=uH48=?G*Yw*{($FdkA^H)?&(NaFAqv-y +(>xB-8`eT&uk#3$b}1dM90`vVogSjv#nW$ZaW(kTPrE$+v@uI@3AGBPy{3xVE*YE#5%*o-W5jidX|fa +^IrovtXC^eO#z*2AAPzrRqsb5!U#R^(&{-BdHOnO~{GG~dOdi9(<*SF^pUS@tE>h)gH!VwJ8L&P%Xax +!1zpu3gu#!W@4e&=EZ3D+;^P9=*4|u0Bbnu`SzjEm4*~weTuM*@M@aUThn9+9>{Pm8nZ33k4eX4^T@3 +1QY-O00;mPT~AeqsHDX~0ssJg1pojr0001RX>c!JX>N37a&BR4FK=*Va$$67Z*FrhVs&Y3WG{DUWo2w +%Wn^h|VPb4$E^v8$Q%!HwFc7`_SBxT6N~Fbt14UY?94JK~0j+vPC~}fX;?}W^?Q~fo{yY9i(%mM)7iV +TXZ)V=S@Ifo%K&YzWCH<`jlYhsHzG(Q#p^9V=~AjdnpYqm*dM19yFCeN(|0e4KSiNa!97T+L(94&B%2%ga)-0 +n4%=gf!SfHFD9EhaORs*l@=xfjI$x!n-|KXLix7SpDc{+ppSA;Q<|Mm}6zb4c1PX<4LZ=K{u0Kz^nHQ +b3eT6=RUs4vg||X1(ZbiBCYe__xNk{a05yQV;~J&k8pQ$2bY(xQ|mDCLPiU2K{MBbQxFFO-hv( +L@T?t$IL1sz;nt&>1Gl&XGv$5v)#=<%yqUG=`CiWl+=5gtrlcwT%<&_fv;x{-HFkOS5v}8rg`weupbM +Mr5;+A~GSg=L^8T~pe0d#Co0tpy%qxUe8Tb=c_!n}0^dvp_qSUED6xotoEMK9>yy6qJm8pk3UB&*a?A +g2vP*hSV>?fE3lgQ5H_3}g-cAKF!_g8DzI<+|S;O`ps*&pav3%H(O+8i3A%yz5UHIrfUNq`IG7Zxh4* +=RA!G)1#*goznw6ebn*>?x&rl-~E4ZBr45-HvRyi@6aWAK2mldXPgO@ugCGF}008(4001rk003}la4%nJZggdGZeeU +MZ*XODVRUJ4ZgVeUb!lv5FL!8VWo%z;WoKbyc`k5ywN_1!+cprr>sPQU3P^}nXs-s8Lz>-Q77fs%r_D +l;rIE~rA_bCK$5{BkclaTRw4H8Hpgvd@XE>aB^B%`^oP^YbiD968NT_=is+<~qNO~zoqE8c=h&Me_r3 +>83GFmF!!$GQLo0x#te(~0xSyQG;1$v&e5KPtknX+!#JvLvU4vigVQ6CQgt +Zw9>{w3nPpQLC(t(mk9WA(ONkY32K(RC}vvEiMIml!}ml4?Pw96&$XwXCjrGeTYGNs@HXU+$NnW~Q@* +;3tf^v(P;$EuV|XC}us)dPmSwBssZzZ+`8pOPZz+Hu~aBD0jyp`sUy@J@kx%cA#3!{zh$C_>$XA8op} +doc`)hLDM_}*O>Hc81ej>r-|J{d%}gjtihVri0>-0MqG;gU2t%j0>c;%W&`eh-$SeE3p~OLuq-F<*V; +bbfB5US?}+_0D%0>351;n|h{?>rY<}BZk)HO&q +$Fe`PsGU>v%}^Jd0Kh;Bl**a(NMSpRP0DORb#o8iF{P(0bOQa^N@88hZXx#4d|Vvj}|+Br09K0=7`rH +ha1B(JU_D+d_C|9~;S$!x={0Y`*nI3#*zKa9=4h|Cg)cA})S +!rK6D8i$lW+o@9$IL(bYz54h?8&)S0!%3Sq5l}hKR{d4FZVO5ZzE&n5Ap;Y`-D*qOgXAA3nB2Pa*FZX +e%9Ve&kmMH8vsi%|GSGX=_*}{dO1}63mtA$m>wT9f0-?ExiySIh)!F3ZmHS?)yzhDE+qZx3)w#6tbbr +q$)_kM_#4ksr~wd&GqvL|Y8jaxr6=2)Xk@(IJqBURW+>Gv5zmP1Z<`a2BTcy-gr--O1=*QBYs|lm8mXZSg63wn02?3!xfoKMNoEr+r&I3B#vu^>Wy!)|IcXH}hTSE5 +JJ`=dmVVsQ+it%d`AMaPs2d?kb&5`hdACg_59o>5aX|uG)lfZTHn +z{uw)LiiWQ(Oq$%=2SO2-={J|Xx%+YGf|Nq0dF10!wWch6Xz;e`96{nnT43W#+~{>va#w{U4L8|6@p5=$R^RG!e34e}vAJ9goGtO>A5cpJ1QY-O00;mPT~Afv-4 +uH@0000p0000i0001RX>c!JX>N37a&BR4FK=*Va$$67Z*FrhX>N0LVQg$KUtei%X>?y-E^v8MQc`luF +H*?OPbtkwEmkN>%}FdtO;ISxSIEpOE=kPE$;?YvC`e4sPE1cN*5guAQsM#tP)h>@6aWAK2mldXPgN_^ +wh9>n002q_001)p003}la4%nJZggdGZeeUMZ*XODVRUJ4ZgVebZgX^DY-}%OWNCC^Vr*q!Y-ML*V|gx +cd3{vTj+-zLeD_yaNR<+a;{%ZBL!~}c%C%DaSXDj;FJK*bUEAc!-`6%cK$1J81Ux&tz|5E|%TCbpdZH +ktXN#@1mLT>NP}tsw#PHyl#|#SiZ;;5f92JYO{v2H9u`Im%>N9t(s{<&t1=hDGndp8=VjE4>4|CXx(zNZ`c#3 +~wqC6+?@V59sx+@(K`@J>seuV;pmu1cW$=+ceWu@0K^f^jjo)TVA=?$Mfu9AytEi?B}Rdh}`YopbZ-O$!2-f$2%H2LwLONA7yLy_)f_D3h(W$_R +E!alalIYy%H79w$`W`*;dJ>l`w~;nolbnf>MRQ71~9l+O1~NBEy?>-DYzhj^%YkEUmG);^Da2N{3xB$ +(!GyH4Xz;!mDiSD$|qe{Fm+4uIxeIo2cH+-=2OX{{c`-0|XQR000O85nWGJgX=+tWCZ{K2@n7PDgXcg +aA|NaUukZ1WpZv|Y%gzcWpZJ3X>V?GFKKRbbYX04FKlIJVPknNaCxm)dyCvQ5dYtwLO7J##HSCi;6h3 +-PzVhqf&P&OM{AG0%Gr`5$+x$JeD{tdKjcS_6uNM?E6qbQn%_JW$MG9zSa~B{Xv5l;87>9sl`KK)jBp +B+eAZxwp7t}-XBx$E97VkAr81tAa{kz?`7mr*?(Sa~WLhw|AQ?DnfKLelcm69qW-rJGzn(=l}aHOo2r4!Ti%rs02%L@|?6 +iUiN3p!artEnz8(9JUM-Rg*aFdVr%uFHuRghqRcimM;e8TV1$onq)in07XC?V^uKK=x4MI=P}rm +9yJI7sxVBWaBq$mXIpeWKPiYlndRdEB +8@lX{tZ$TIK;LthLELD|61V8?F(Q*O$WjJR~uNrn%#ymhr|5+%N3D13|vNv{_NQ5;-ei5 +7FM;5p1X>S@Fl;zN1Z*yHzeKjtL6HTo|qlnE^wXAn{smHac*LycO+@%!Lhe;0sdpR*XFV?$xM=8Sp4m9cdK>O!Vp7oB?_>po`H*admNZ>EN;Tf)XzWrCla+`g+K`Xs!5$Vs`itaZC6J +ML2FRTgq2|fNUXMjKk!WYK(TGylI3-MzI=EZvDpV5xBVvEhYvhm1dw$_$o>08vUP&KVQvw +%GH$DgTX7umrf0q?)&Plu#ILxF#)(CE#HoUyEqGPhE8Jzhe%>08=qY-Z!0O`f8^6?0Ma`&>)JGgP6x% +>o&X`0!Yq@8uV~f_FRi!*UDXdvK@I1xO~0bvv2U5Z`ON(&0XZ1^??A +wJ9kZONAft%{nwtn#5Z4l98Yg@HE;3>?+0d2sg%#Z)=XoOpKrj8UKr?SX0U +yn>-ZhA+ZMH*(_O3hHs#*;cyo5Rav89s^Fzw2w{7wt6Gau8LVZQ+{kG`%42d4n~S7>QE{kkWP&Vm7;-2rPaV?GFKKRbbYX04FL!8VWo#~RdCfg*bKJ&}-}x)X(A^1ZFF{Fm6c=MUC +rh-$iuE{=ik;9Z7`vDyVPXLoz>-?41?&;~*jH4(z-848{OJ{^PwX>x&D(#Qdzgq{;eA^Rw0FTX(tX3p+-)wFul~Z3O_jy;WPU)gtTk!!KkQwQ0 +*PTj_N1@C#in>s)gqz0Qg9!uE<5vfd6^Z}T&;={-mJ2+({lwR1rjyfqu(}2P- +!0TDfpZ`pVQy7zrQ$6AOGd}@n27#eaDX;Ra^e@4N={+d<=BV7NzE2Pl{#F-@en8ZnM5_`R55B;hNDe` +|kUqlTOd{&Dpk*M$a2$TUPR~Q~m1(D5A|T>2bDc4A552`M?TqLJ-)!iy%uhsSMOw^QB9dYJ{i5Y2V=OWmgHqJn>~b=$%FslIF7XL;6Vq11 +c>BK!pA-xiBaZ<&Bp2C47$`UhOD{1C^Y)tlx)huP$4Apd0tq-|jdr?524uuo229zQ-yU%mW3eR_QM=* +gqAN9nUiPmkvyfY|m`Tiyb%TVCq5m4{Oh^QXt@UyuKOItLcxtEs-kpJZj{WV&bhFWC~foA&D_U6Ek5x ++&q=ZYb-bTRK)Z{ifA<+Vy$8>E}vU9sCCJv~zWtb(aFP$=bf?cd&)QZYmU~(VAXv+7=Pqth4TlxlnZJ +P0?Q#Rm!xjTUpy3No?O{%N|g|k<-3TbG^zoB?t-7FKxL$ERrHd6y?`H)ZI$b;m-ir@_A% +(v>O$v-YEhffxb;eP*|m;4U9M(o|7-QEuCyByKMf$Yx3`CzhHll7**y|i2;bAQIHeVyrn9|0hZ3|DWf +=R2q6%+kd$P`T{`mB7FY6m^G=n6FkJxs=Bm`yMSnS?eH_P7n+~~#W0G3E%pUyVVLZcq8X;Yp9x$wx +ctXy@Yer3Z1ZWpAkfTU_Hr_Mi%Y+-ovssdBdK95ntnNp%q(!~c#L-d2Fk6|0!5&`6GJ~1W3(SGEsM9O +hxi7jHmjRKDh=W@m(*}0P5pAyY_6As@pyOr@tFTjjt(vxObX#r}{Hau?fQz_KYhIfb**F&kY!M2FN(@ +LJ+qV(EiVms8Tt$_>0b2AA|I6r|Ol4KPNqJv$?5BC4XnWX`3R8DJX}0}kT`5!-@a +q8k9Q5@8Kc?m~)3bI1I(0=80TZV)izY<>j{5P(9~Eft=P#cSvvmis%TiTZ=SZ7{R+|R47@bplSUuVqS +xM1Z*_iPbeLF+iiNTNXv+0JOYbycx@EM})i +CK{)Ap(}IGJEk3j9_c&aztWYdO^myNI>$=BnqJdowv_Wi4ibG@14x~}wm}W>aP>BF!|D@Z0!9Y;tVP% +)~4d~OEIs*OB!?=+QOm-L=U`-9mt&7q0I2>6mo~mI{Qm3&kn7pfDC(0Of{awd9Zsu(Lnk&E-jdER&ABxS=MS&0-*K~ +GWnnETY|0@=PmJ6U*{ex>!PioyH>#Tbq(NlfM(;V27TY&6rCpXA=HK3_P?!VGI +Cgv^o4j`G`%=p-xFff138_D(~nJ&g0w-_d3b)Joezt484BPDzB@%=#RsCgslg8$TJb6xPZ;6zeKhJ>4 +as>9uz!mjw9eAKevb&T)~lAgBz;j!de{g3N*E++cC_6)Pfhfx-QzfV(o_Gh)V04vgl(0YJ0zyDV(6-D +z~+$uIM|~?BSj>2lm_9$}EdM5is~UyDVVKuF>}+!9mTmz2R)@elOum%;l2*owpR2-ARb`!Ko +vIe%8JRXr{C<7tBGuZ+O4j{WrFyBrH*rAcWfY9#!+^!KIeJD4mjn`dh4va8&kY=io`My>=G +2E#GgAh7lD)kJsHrN;^Et*z3Ki$-qCUHz6~YnTofMgUf_|rqPoab|FHdc|h-C4^}_}Fo)x?J8iP}!## +jZr4%D+WS~<+*#5xVj1tzL10d)3Ub|tyGuQ8j*C9Zdy@eR`zz;5*Id@%AHG>pz*0pLVE?W*p+eRmg3o?>|k1n`x9vKx0d!0YV^w{*2eLOH0>V_2_64 +sIu?KwPJQBB(jq(`t|$VC=TufH$I7(8f8z5j5&H4Lt8q7XbFMyUoFo(`tXes`v59J`--=1e6j6F*Bl% +Qez1>6qpf3gLxPVMKrt(QbeK_LRtA>P+%zgx*eg(;(L^Xi-t_H#(*7@nIc;C_dqM9eNpDPK|u#c_5#G +3tfk+he@?|C3PniDa6~tC`k4xQQXoVi0L08^&(Dqz6-uZCf>;1B=AD7Sl7$chduW;{B{b2WfB$*zi3C +AnVP0ojl4fw!^qU5j5{Nq(8zA=z$X^z+Pb85is5?0|!ryYlm{z#AFb83&sx??11-V1MmK1Fo{Zz@ycT +y+jV~X0F!l`)*n^#-kbWA4sacBe7=Y!bF>-iqpkfbfcKuF*XfWh|6jNv)(2pqT3%=o-$wj_Hd3ceh{2S&4+%}rRkXrGZufFjs~z0}F5$7Zks?bgrt +dz_OJClEbym=?xcNJ}nj5Sgy2tDFKCnc@%{%!s{ne^v_2L9JdKzfhn5@geob?_WIlqgaO^Ze=%16z+6 +{Fw%y=V2q3>M9Y1(smRuxQ!7LU1L7!f@GnLYF_H$3D*r+o8|^DlPs~vUuGdY!l^4Sh1T(7K(g0|kgF# +cmq^fd@qoCoA1Zp&gujaI`lJJvN1f2v>2h@Oh;#>i9po=awM|s%RTGkk6Mm9DjESnp{J&V&F^%wA2f! +{O+Lhnof=mwSs_5l1cHsjaD#bpmH!li@6s+X{$Rew{MtjHbT%0rtL$wxV=2JjNn}Ev2Ju132GVQbgmx>?;GYvW8@d8j0K+9UU(3OPT^dec}mh}C)E>Hnuyp!@4Oa!z@fh2}h +C^xv1u`{O@+42g_w<-sT)p~zlnqBsN(;XfhT!0?iEE3?sL9=bLgQjQ>imuz}?%)q!KK#{XRRVSTvU*B=<9UVQMWjVNBgncvRH^EE5SMV8$?5lQA*lQTF-ry?5HpnjOe +}bqrRhY91@2@&F7W+p{R104VaoWY5q%)cUbPN#*peY`QsieYbXhZ3^I`>tg|-q^))$!5!nns(XL +dF-6WRsy7T|Go4>nOho-@%*koQ~q{w9T!O*g>R&fQL1_8Ms_?I`YPy%X4uVH-RMBgL3q97N@Pl4G<91 +I8!;v}aqhP_~>hx9`OYx2E`O$2zz=KGu6a05IHwC5q%rPH4wiG7&Qoa7|=e2hDe3}5N&5wNZFzP0uN{EYxQ~PV{l$35eo#k%9xX +h0gIZq(;g@c|Fp2Z83li+{<8zVBT(o1!|>ih!??*}q5hf{j7eUGx)&6X(IYD6QlctlKh#i<9ik1!u3f22@_%kbS%@QlX$Jo( +(cQ}Ao9biYj0Hyg4xfdT}V8}h4lN)Ifib+=Ic*B(K4DqTHbg{K^W_uo8%5Rz^70K0PweT^H!}z^K;-SupKo?z=E% +r)n#yTK4|!%uNo>fQvR&fq=3yV&;M57Ku*gJ`7CvX2M%^w1i!Pz$^(C#y5JIVd%+QRwyI108qp>Xppj +4sFVw6%;LB%+J2LPcD?p|B$I4Lm=;;VHBqPaoZL%LWp#4_w#}x(qAs5`o_m8K^R~#fM8PNNZd^K=En; +OgLO&9}8p9(d9G{aapj7D=5xN9NWgq=AWIaxE0yo3dZX`BF&D7{d5k{a(0V7x~m*7lUr>o;!4?bT~c> +&s1Ed~-LG6+}*EJy6#VoVG!>-vh85tvRt|9t%H^wrDb7bh>$=P%AqozC86fM{SnT`w(C7Ww)?Zss9$6g$J)l+hZF;0+ipm^ZPpQP()I`J>U~Ba^Oi!Pr5Z8_!D+uqPXYc*o38d(<3*f%s-9 +>yqjBDmgKNWfSEAp7$i7=Tvz*#qWK1F1f64nEl-Z(i!kM)Pg5rrSPMHJm)rUwtgy;9*Z8^lgY=`GbtS-ZtYDPu4@XM;e#0rv9Q@nLi| +=?zO7iT)2q?4PwzRosD=-x~H^aw3@Yss$O^yv2kv45$mlkDDu$tjd-ggZ4-^GpJ0|MrAXn*17)@q-SD +_$7M7QXu>7JJ@UKVbd@aHXh^&TL~+OSiH$q@Ebz*g56Y$vgaLea#s&T{g5x=Wa$k4?Qm_f5qh7zqb +Nnk`PV>q?9~)|-n{6S9+*On3;=LmmsD#|`(%X6|QHhq?Nu@M!O0}cICF=$X?+sJ_|eHhmVm_ND2o6o +?;v_r#f4o4AVz2EJ_2r;-k-bj0q+i1D}f8u`BRqz`8xcHqkDvZuIY1_fF`0+hO=}x9a!$?AQf%>iFAq +u#XBho9%+>)!px+k^>rjpDm$-JD>yhL1XV5*MDVdYX? +A3+R@mdf+S11;;4|3XgU%3$InO5*gw0#mXpXVAsjTd$XQkz1 +Dxz+o*fz;lH|mndBOA{H9?H2ue%5T|1Th~6!^2nY#sI-OEy<9Wvbc$6?nGliRXMt7 +2s#(r{MQZW3Um6*s3*zTIys4=NK}Q1t4hI-WNeuX&oH-Augtbf_JIrD`0YJ9k-a6vqIJiC%!Q`2ctIp +gIvcv-`3gSSDu*tqEXmqg#@zIZ@_Z;T^_1i44HNQ|D@HaQJ4Ad&NoFHj2g8YTMAAx;Ij`*j_E-ouCHa +^b&ENKVUtW0028m45Ih}w&lf3re!HkzzlE?At|3iY2R;Z{R##!@HDaxbK%IU!7lE|yh={&7lTc2`a5f +Z_HxCC2?N~HKysLq9HWQSnDe+{BuEWFKDDT2HIH!n3d08gi!_sKj$+di)~t1Nbj%5*ERnXXo`dZ4xGC(c*i>9{c2iBYnvpHyJ?u8uHs0%{TE%F=;CF+b +Iv-TE;eM)W(`2v}6$yz2;adj;Kz_4{?A?H14!5W~pKxgco`a<*@5};oVPgXh&jfD0=lP}`E<6Ej(~+` +EeOUR4JKVIZ1cmIbUW}rtN)#`X%I8wak{cAvkRooEY();^2>g^bBQRSjRuU@8U9kj^&!<^4RkG6Us9E +8cN#qqF0Eu^(3mEh!ss`)~&t+mr2?6FC^>#{ujblz$;>i~2P +RI|79tv13X;%ajb_yfwY_N??_c5w64P&P|BP5u+gv<RnP4=Jf ++lGNQ2J8;I6SItgc^*`giT~!=bjFBGhr{kFY$WImCS)d7XKU-;`6q6Mb&}!KeFrZniOi{{#seGnyH%j +Q_~d}8yi$TlXjm!gdG&_Wo|A4)vQClA@eud6kK|!!rG7jkWi-}(pwzGQ_7L5ftjCg9Hhu9VI~Tsf7c{ +=Sdo$kcjb&mujsG*5fWNX1j?hO^5#^CzT2jKvG?TY#v7GKe-9uIe$a>7sassc|@WA +H_pBa?669g0Q@Up-b@8)Hx +yh_OiQmZ&PvWd8#`1UW`1E>;?0?nU9YklZ(sf5s9v%jZ{h?;#seJ(BuB!T`QjWIP8zBb1Q!&*Rn!RQw +?A*n>F_%4dV)fQ$;h#n&2JeDJrhTSN1jiy6zkN6RK)(BnB+zUl;{K~*u~T(>910ie2deOK|9`J0A1b= +g5c6LCGX@!JBe2Hi>OK{^I}44)%Bx}TOu;c(*MY{jE*71~FAKUEg*ws>V|sgcg~gr~$>r_Baew2Y$8$ +BDWXBF3Usn`QP+{&3D-)R96sZ_^(xUNv#=y2F$LG-AFLwT|nAVsLog895o=|8?n{wBW<6%Qb29HKed@ +u@bm2?3|izjNa%9+aA@aH76z1R2ojG?)WfpMmE`G7m)355J{R{7LqC%DV@g~ztx3cy6`v=QU6jbF~jX +tVF?v1|xyJ~2&?`?w15>bsbJ%_o4*T>iZm-yBnW`M99KzoSedyyMHu90dnAo_U&eHn)vNce}+D^_UA; +sR?K4=^hLdSRPzoVlOQTgw?f5#iNf$xtCB-@2KM!bNCO>-jj44zmNz!mJDa!)fFy|;0M?4_(X%wcofu +CX?p8{6#b6$%dTr4#^)R0sX6Q8uMR+dcqI>oqy#YjIX~S0!Wk5F$H@+8ZvGzt^ZvaL0}9Zxk415Ks$$ +sQW5UTrMc0g2%I`aN4R1rRo0oy+mBL_7#eWV0=vTo9%V>w1-E-(pF5U@!oIOC0JEapS7vd(uyL` ++@g@|NzCI%TpoH#f##ArWK$0;+M6a1`*GN@~sr>qdS{6v)#6&32h~^`>2l(k0@?wCe43oSe6z^aB0++ +SdhmC5qn9H++m+he0s2pO`9x)*j2n66i8(xO%|@X9&R#4Un87YaPm#37Au+T$*vj2KVEc59O2(%uXOf +BGzHCg3h{vgYKak>lFjj7|W-@LkheZ5Tk; +@SkD=*1paUc(EaN}ppm#)$GiINd>SukJV~*XN#vkyWfoVjRBr+SO-Inx{^waS>CV;kNquSC%d!z;{0O=%2B~>E!N;Ek|WDa&^rLE+rF*JkA@t?`XP&~r=YKfxCSb8SQg5T^otc5DD>U^o7D0C*oIsU>d!3z%y_F0Uw? +OkQnEa +{!`r=OUo&v0Dm}&evClx?FE3%L@rt5v^C>F$9s%zXrg4zm1qZ;CQ`}-Yas%%sB`Rs41Cd=|urLMfubr +8+)YWiAC(lmL9)17)@yp1&Ms~n}5F*jYJz518QTSXgkzL@yo}a2{xVq%9$^iUeo;X3gcwy=#n$M7Sya3|5o`aL3Zyygf6>oq@RWIG$7Pq46Xup&q7iSkn-r|o+h9ue(CV=jlB?*l0i}DwGakNul(_?dm%x76PzmUHFO%zE;qWrP1xk^upW& +V()#SE_9cdY&6U6nwo=cMZq6h6YT5;u~Wyjzf*Mke_a(!3rz||f=n+Rz=0PJzhIXf8TH%1 +`gyP147Bl7^|k*&svnVAGmc%D%+S$qyIvyRN8S`h&6r_FoyRZ#V>xtWzfBA^!K$OFhvTk}eMj!=xS8e +Lk^L4nn(h5BP)h>@6aWAK2mldXPgR4?^4gUH008$20012T003}la4%nJZggdGZeeUMa%FKZUtei%X>? +y-E^v9RR?({4I1qjJSEw!p8+`o%0WTDGDGOx_ZQm|IDE2r}t4OXS=Qfo7`;KJEmalzF7uJy2)|@$WG@ +6-&Uk9ZvX_XMrTCSwY__QR{`OM`x>$SQPdmr$b1k*c^pqW*=BJa7e75OkYS0*a*3BHXWTd2tAaS)JA? +gJlc%B2O3pn9}im^$!BPA)r;mf~vgsWp6~Jr@uW1Jed5u8v&86~QI}a%n6RB4YoZUOz721Z%)XTmtw6 +Hp?hQFG@*69y5Yhz6Rvgw7R8_maWKW2@2_%-#|KqvmCR`Oz@Kxvs}Su2<^abL` +(`y-f9*n?;9peGcQz%8YP0nt~)xgLnIx+D*Okbjhf3Fqx?7fzXc%-L@t(-AIBKw?mZZ7Qke!aeJa3EnZg&P=XLpVX`Qi22dfp0(andZ(xbO{SSg)D3<;J;u;0F3`FMf(hVlO~^YKcIWdaCKdDslqSPUpsY96{v5ZqB7sPf{q>xpmo*Q4XyAiZ71GJ+d> +HauwIAdCoNH$gDr;4m5Os^41nF)Oz|-mXkQ%CzMMeP7|T#vRB2tn9d!iK7>cTQR{)ciNj`gs7PLaQCt +>Focb%=O;MH$GOC%&8>ioKT6^O5l8ok}TvUdBqH~qU?fJ7ud8_kI$llFt81>AHWT&Y=yP@zI9G@L +>0T+cs3`hUC?@1>|@-tXx!Jn+0Bf`eeIj4$Iu8lrQ^ocXqF<_Fd@%gg}4djIClzwhB(muc171wQL*h4&A5T +zsN-J<{hvhQ>0G%j%5`7NxS96@k;2Zw7%&ytLP)h>@6aWAK2mldXPgO6QvXK7~0084a001 +EX003}la4%nJZggdGZeeUMa%FKZV{dMAbaHiLbZ>HVE^vA68*7i_w(+}v1*>gqspnH3NYTQ9)1-Gf^n +f%uB$xKXuB*@zZR=JR6;axayV(EUnIS2WlI(Sw^aa#NZY^;*9L{@&bPxo8OA>im%1@ZC=yZT(LV|=dx8Sl`vYjX(MYjo4wr&mS=*qy{O@vV$Gg6@Q+XeQ5{6aI6LrnfHmE> +xjY4j@pyO=4tU`NIBe`7c-i2Z8^Kxy%ZoyZd)o7T-t6;|Re6QudP$ +b!7RwaYmfP8Qzws;xg4qmMk2@>mZU5@S7Kx63xk`AQ?(;iA^qih~$W`i0GBof_A(AY +gU?z>hSziC} +R80L0%7RwyNR%6c|KqXmDmvep~J{=T4hVUm>mAd)1S$x7559VNhZ8XhOz>-bh2RT#}?vrKGR0)92G67 +fS*a~0y&mkeG-?D;3!w6Ec*H+pscLtHOq(!m|#T`k)x{2=OJMq?iu+~63`V8RzO8m>jt*5v?VVelG`K&-hFbz@ng|15nD +5PQJ=(Zka2rj(6A)e~b@RH6wZmxO_4hCx;ERP1E|p(0?<44$}<_aNLTiaip2-3aX$L^zb5NA`k|FfHM +RatI6L5)_0!W^plxIEXBb?6>qBqN_WKisufN?Ax!ua9$YFofmcusr@SreU_rTg(&B`{eDq(S3cV}O|^ +XQy-mUQ`2VN1xVsX&-RhN;8(v>;_}|yRR_z8{guqPUxB@vUSRm0aJ6!R#l3*)D*V?i`D+rh~D2&Tc{r +-x*-om!YjiUBo)?oumM&X!QWg>%u5UiuRk)uUE);d_uw?Tl4W>%#nI<;mW=L$9udI+MPIalm0rU2yl8 +pf5*TPQWz!xJxHG&@2bAxg-IM0yJ}Lk`o;_wX8nF&=QRW=EL!+Nx;`{;X~kvLg(TSIZ!B?GnASAV&p@ +V0;^3k3pK?3HgqkwVem64Xw&Cq|kdgTxT&F^@T7R{^#t{$KyCG&oMbvdo&IeLpuwahKZupVsK1NEg=+ +_7;u&@e*nRh47S0;VDh6UkzV5`AfG+J&|}~MZ$Oc5HD#2l^iu3K1HB*)g3cx$og3IQBxR^z5E+8_oy^ +M+AYzPx3iR?IRL{Da=a2`FQZ8TIK(=eEk7$;-K4+9BPI|ML1p^$f8>aTMEdU<4CNOUY3}ohzU2}-GDK +A)6%Xi>c7)XS(zr6Scz1#>@xDgPTOSD9C3J-SEk5Syi8fFa)f|xe40K-uicKPF&6uh9lA(l)uxP_tjC +N-NsJkB9t7lH#y?c0tFV)bB;0qu+Y?oqaN;u03bSbp)wi%5~P?Q<5;>AjA^=%ye|gu2PKM@YjccMS0# +;zbwlFt6wW+6$bHywi!VqL4|Nfd0^nF$5B8iM-tdDEzk<{4OuRV{)v!phErbyXtlagecfDR9LZ&z%H8 +azFV-3@u>>yTOAref!?)HLKs);gZi2>;3be8fG<+~9&H^@4kbBgM&`gZ +{j|B^5vH7q-ZLR=~#d;s!Q&^FuWzXw|ts8nhd;?HW8`NZg|U8b;p8Hu5HcF3<>;_r^sDFJ4{2zF;fg+(Pt*^ +a8vak@H?E40fG3V+M5`sk%_ELMS_hOu#DuOcA=LN!{5$Bd`%P4)CrgYF!DD=?>BM}ER#Z+{61 +HW4)@qz8kn6mV|d~R#c1)bN64riF=V(t-X?prhC_f53P*9}s3@4DJPjEW<__g)xHC5X +PNj%6LTYM1+&1Q|mMHMytkzm&B<}eeB74%1?IzK(|w-JX>%6RLaJg*tEMac%gZ#>6`yb&C%O6Kv8Z#v9$66r&laWDD%+ky*{3*K@Wl +_zqVvJ%Q1bhD1prb69zR*5-|uT{!vzTma*s2BoWsZ_w{)UQrhSBT#Ei_Shj~Y5BD)4E3I|+ +}*ok&Q%YwRbl&QbKxTAnmk!hRzCgp^v(^c83RfU7*tgDGNa@{U1qkH@lu!g?q#Mo1JR};1`lgRKNY;cMgJ$l!XtLD_P*F#FF6{487vOP!pPTW5t<|y?fot`>Z?;7SwFuIm&2^ +3&^PxJP;}J>BTBhAHaEZ?4ZuHnu3Idw)rlBJRf@%)8zU5AUhhfZD|WAkbP%0=grbrpfSqc^$}Lrzx1x +Dm+h+m+7ElIT6s^7dox6>-qx+tpP1(2YWlVBZC9FNQh$e3E7Vp0)g#p?*{c8A4_oD0*)PtJu?nMOEAt +n7(P1zcgy?*|j;N7kj#AL*;a!hzSI7-Nfz2Pg02!v%)2MJnAc}{_F^Y#rk8iGyt_IJ;FlBh}wt>3VdL +iYlN{ajz_zPKB=+~=g9)7*57)X~I5E$(-J1%!DAS?{A1hy*y1*t9t3!#osavj|AwaW-~KIfu@Y6qBMy +dRXeI?-GJK6rT7W;?)1XMcNLaOunk&lJr9HfaW@DUgf2Jlf++?LLuW;23DDJ~-2$dYiwon05sPI%Q#Wp4!$mCI?+RIPyzhr@M-)K^8UWt!Ph +yRyu^&{H3%vH(e{WFw`oOx&mLc4gt`XLPB3~SO|mN91`kcDO4@Umby0a{k||l#2~(H%apDU7? +OvG`dr+s=K}1_i3BY7Dee0{FA7jrfmwT_*L(B%8}`vh?^C1CO*c{2Mil+t2C6k_{E??7x~ESkWL`6Oe +BwC-a&*CM)%S;O_<;)VtCwFbiB)5itih`X)CM@@yM4n<9b1Z2D8=iegGYZ#NRD(-L#CHW)Af)%ORA&k +jshtMV55|Mi{QdW?n|pijIzXE)Lk>zp*&_^8b>h)hul_ib(|0TlO^^tJAz^)Xs#W_s75k%_uyQ5$Zf- +E_uR4<;h723qJbw&RBY;^9RQ>V5TibY@bp1PSs}{M1&-J!EB0a6zZxl+3w!7n= +Td(njt|StNWv%K@WN;p~*52yH0)ZraD6;x)VHgE<9cKX~V{q$P+ky3MxMnj2J5xd~DQt(97^)IxpkAA +4MC@A5+{WPz#xa!qd4PL8p5Cdj71@JKMi=eHSu+1n4`izVPmJV7y@;P`3!e5#q_V<TqUoo+^rx^w8 +?y#FccCzZM96ZW_Osos=8`b;t$Sqep6sG0i#aP$pQF&s5AzcD1q7ttcx+Ln +ztp=lg%f4b5r{mpMM(d@~@k8_xPrpNFz@-bL{;qPof8mAD(52Ij4Bt_|cuP1Se- +aTb(%QnId272p*uVf)Q0+G6i+)_#TNIZ-dVJ^!x5Uoi}gd*vr}IYIr}vB8k~cc*4^NJlI7)_lIDxg`w +#VQ2XHerK`XvTCxw`_8{pFMyIA`k@4zA7Te&a{1f#K9}MW=(#WX-03grZl(}Lrv7pDB*VcOZ7OzwH7k +QVE?SqAXvicx?|60SWg8>riiI`qMab)+h21VN(8BbHFYD>&|Q;tV5egutl5;$~n*zhpKLxv7(M3sJX1@RZjU2>n4#*XOboG%9J|hFCa7DVQ(a;UFaXGJwye&q^ +Z!NSBSeXF{cQzM-3A)RsYyqOX=H<{vZAr4xMvoKH!qwC1_ +m4h>@7JKScwJ`512zQ{2)Xz>7I_azi~S`Fe(;A&RI$QLS>we`J)_qV`}3yLP-`3LLzfA$?GWIH?jd#B +aN>3B-R77Wi%ZcIlG3M%eQAYt8xf!X02+|ym>*`jA~04RtGvH)LyhlD&{a4{g1|oL$3WQKP)`cNOOk> +&7N@ZlU^GP{&i&8&qI~OY44YQ=iowuBJ#h=hejH?z$$+mCD+hc^Q7 ++I3!xJ0RJcQ%$_JsCbtYR0dnNcLGp~M%V4TF$1$SQm>m{W{)7PXYi@MtcB +2EI*_&&7d&+U?0%L|s1lbHCC>{(O1%(Jd?qY0DOk@8b=@2(pWgnb$$?gR{{c`-0|XQR000O85nWGJ02KfB02BZK?nD3p9RL6TaA|N +aUukZ1WpZv|Y%g+UaW8UZabIR>Y-KKRdEGo~ciYC5-}x)%P&vnBLqWFF$1zi7kK@Rxc5O>*so8dA6^6 +*6hzJBw3?P}u@qh1q%o7;Oa@y^OMNvTT)D<%JY=)~LL^9*v=PlyB;)X+ +>4HSS;Z|W-=1+od5OoD1Gs#qZj{m{N~3Q6 ++JJv{PH3zitMV8{Pjb=X!-3&S;{7BtA;<1TlligUgb(RJE_~eDzk#W&bu0$Oh>$yIy9S^t+ooD}3%P_>@lEpkv6Kx6L%UduTiGZgZM71o+jd>ibksk>zh;3W*C2#FFPHKI$j0I(y9VUqX +Jcu^r7x*)G)hxop)?hT;$lqVlQtd?jW0(iz{pCk@=_{jW_bRB*!lwIoC(!7m(UWM5XUvp&WO;(1=h}3 +3h;fVB@=s`0q$QPrEia*Ln9N!5==E|#($fv+qV7%|53l7)|k%6Gi-7^9lbbt{rc$5d8F6mZ@;`#Ur+a +v-+VO&aoZEG5xcN#S>EPNRU%B{HfwTR*uHAFpp9;6F-YJ?UwwT2*Yv07Z;zjU|LQ1x^63#1s$EMM%5) +QDwgGYju|V9BC_pk|g|4n;v&cXJ6|w~+shL>j*EwiC~Kavkkis$1qw!54@~}C_mAaZ-rH0|=;;O0&R|NtoRf_t$*T=DTzl%oE+?o^@XpFEL1#5-K@ +un)J>jC^MQ&n}%LVCSW2M0~3V*fk(oCgs4l0;g>eud3ED50766*hAaLo4?YHZmN0XrS$nVUR;(n2)fL +2Q{?vzIw#QE$ZP^#*GJcWsh7qd|A!WK5&IubFF&q030ad-R^V5-bD|y1c1uC4k0fq4Rl!zP^hqs=@Hd6^X|(kL7VC$w#30ZYJzTmAhnTg;%%p7`_1b+^MaKRUkFFkmadsP0<4`RO^^M&CwAB_g@A +I-*KX^OH&(>YK7^vDAk3#=mGG4$Rq-G)uHY;`-!5$LZTo%ir))ut=lnbd6=HEFiINNwwg6A|tleLTjBx6gU%M;giY`T&7SBt^06$AR|2d8z?^oY0b8Ch6RrT| +Yl${zR>n3gA7J#s@h@r2+Su;sDS0Im260L)ZKJ5K*-SLt)sU^fW%qny#f0;6~O>1)QlS08( +qL1riS5ud)JgR)d3x%Pd-4A9DTA72pZMQT*MobD9apVapOW>M;z@h31t0K#s)E0V53f0CZ*9fr-*{{I +C$`k_MDS;bN)BWQtB;*0hIDroC~XCqj^NJ69fQmtU9Q6{TP&u&%qevHVa2HsI?vHLlsPhsKpF)^yR_< +Ke>NBz&8D_QDryMj~&-xHR#AMgq^@N)8?XNnmjpP{2qA#sk()A_Mzr{PB~3{|HEUnT`_}V3W0u|Hu1_ +F>)-n@V9u}d!Vuzhc=8gE_%gmNoc!-SB-82?21$!ZdqJ!CiSgfJLsNx36R3Xh!UzswV +iY+qceIk=1pv9XNd+psBODKYnuNE>H&WKZ(1kEW>Xy +nYz>HFaLpmtp5=)hDe!8sH7LKQYK-`SPIzt$a@PJ09JqSCfPI|wv(PEiHZ9j)Doo@K_X3I{;c>$dVku +E5ZZ4mllekOstP`=GUhKLKL^`9UwlX8@x+hp~`Sa?rS|m#7YridhE^G^f)_(f60|(Patu@tAJn^Pd-n +^0JPHe!_9G1CqmjyxxLh$3Jwhv#5X{hUo=-rh%sFDHK?l|2Z1S4;b_F!Fp+s7Bg$r9p%@c70^s(3SFh +>ATDpPbGV?@5a<&T*ItN;CU0;Q!cB34+`lrmOS#DMD4KU*$Sf1(K`FZ8g7{HJGV{0n_AhqH$o9A9 +8TxE6hD(S@-7L?D5>!r)91b4(OL`7P)E7Fh{(swJP3 +RYJ?%SDT_fk@(5i3)cSD%Bi@5wp8oN$n3PBD3b#{xfOCeV)SPi?< +jpUqt)WzZ$4-B@YhNN{!6=N0}yZaulIH5qFnK3@9*koHKxKtE)z3N&Vo9#6Uxh&&=E-SJ)eg(YjsuIl +iz?*1++O=}T2dN$qDnCMG9vxGSc`RX}^3 +xOe3_wEJvx&QSocZBNyq%d51C0(+WBS{iU%5Xgml{QNI532`K8&V&Un9F}j(CSl?s?OL?221dKe0kv< +uIdykC#=s29vR*NoK-sF5b?d5)%f-heO&VnG6+YqcG^&$VIm^VDDgz^!PUbywXaH?NqmLoa=Kb27Ge% +h$CW4ieTtvyAV~d$?Iisjdyq@^rwgR*B+Au=%wSr@rZqI$?-)DN5==96Ldd<@S5MP?=zW)7nBCc<6&k +OpJBH1-pM`me97G8t49!6HTgAI$pxpwK$};$Ac=UGEQUIJ2u)l5&hd*wu!kFCMwvv)&8W&bstB19c+B +y0!21VS@t7jHjgCD{%jsvqqrd=j3V%0x84;pr-X1_~XS)~Te*)UTIgU2Qr|*$lLd}OQOo!a@0J +Ubv95P)Y7~NUN$kUv#Ff +L#9KRA#|aPTWQn6?-CwD?$4?Fy$b06K +1iMQ7kiAYI?%%8w(cu2M|C5K706O!P8f^vbYyt +WWkej?aHoqom(v*sEWtg2tLDBKP*Fl>M--m6pN#i`;-*tr0W~3W5p^6p4t8V0hK_&Jc+(iGCv{a%9tQ +_T07y1faD?g1eOo8oc&8E0J~B6TrV%HV_k7p}91BG}Ox}gyU&DrhUcyejbJ@O%HPm}+4hnhd(i|EaMA +U+wRv$)ZnP7x>EsI)<#38u=TF!K&VU;bwP{}FrIgChs9gp=`h6x7Qy5yuAc0LM;Xb%sYPM$i}N&;8SZ +8Q~aF7x}X(H8C!nc8CZjBam~7Hbme3-SemYGm}T$!a|e1;B8v9YAnb3VUNK33t9G#eslMl9|{SXD9Kq?uvt9IsF*JQZM3MjJ?0++|w~x#Be|z#G_HP1H)o< +7@E0uZKoA!eS6J=xS<=5+6DN_~^rFG4y)}Hh1`GkzSzhwYoR+59c&B;h6w@KIaq$6uxKk(nQ{koH)CA +UqO!;Dibt4YUUiPMqpcG)-uL}67NPkDfNZ8|01001)ZP#a5C4CY%mowu=2ANFdE8yqE*W7qoWViEA;m +a@!|7$xzuNIh1}Hb*3V*@K3^ocnq@rMQ{B~MJmy~0emjpy!O@K&L+s)&M$cys%RUrGZ)IH&`imlZrP3 +tG!0^1HOd4#}x1bkp^Gv+26nah2?Y+)6otq32ZB-b)BJYOXy6EWMZOBME%trg(_b+n3$IXuwm}u`Xmj +)Yv(r3Y*Bt&*72-AD8{=${R3T*Xtl^Y$)hqft04Es-pd;6l}{Da<{-t*Qi>aHj(Qt(jb-Z#H{@>}u8=nh|J2X$T_V4nkQ +_TBqbuiz&Nd@R;k1Ev%>4T$dG_N%$E%9DpTR%ry95hkp=E0t8BZmyfEtCOm^J^+FP39;hf+3%k{d-mk +nAD%rsfXO+_)}XVr$y0}X*KDJxh6G( +>BCuWSn0U%V2)O@Jck^l0tAK3O4Lc?M>@s=8%?YUnW4B~-ZQ|RDhBv*E?m;0k^RsWJfM(q48Mq+g4Id +dT_dYg+szkP)V8E6>d6)Z_yLVtos!dkjUVnU8k`$A6wBHoob9+ywFqoZoMfR*ft{om{h{+WO|XbW)Bn +vDHYEhso-Q-{p*y5BxNISc}o0Z>Z=1QY-O00;mPT~AfY+=~53Bme*qivR#10001RX>c!JX> +N37a&BR4FLGsZFLGsZUukZ0bYX04E^v9(J!y~IMwZ|ES8!v +83jhA~ig^tu*?-?>p+QqPiVy_7GsVL{`1JUcLKO-AmqFh-A~&b$eAc+hpDDcA^=Q{-Pc0GC3DXCwA?n +D3bynwmXsR+X~)SO)_A`aTq(1o!m=yhyCF+>4&ab56gBxRBcn#tEWZXi<6UTw{N=vN;m7FZR=h=sN3x +pT2((={i44ZhpN`U4*JD7R;Bv>TeaU*wK&;y?Jg0`xJ%@VGx*L=p(FdE>xFvr_o5y}&+mrA9!D(SJZc +V$HSYzK8$rBjJB6&85t~js7B6(i*@cV@v*&=x@{yBd9+yv?fluuCy+iwyD-dUHvBVrr3#NH7}txm +;*h_{PQzv@FEpGf5|T=0f_y>2Y=44x?;b#M);B*|J=489~ZEKoxK=e>cpb&>qizS#RuW1z&RwID>$J$Tmi12kRW!?UhyJ3tt*E!GG5;%QOUuv22d>ULc?u(o2z`}Kv`MT_s +mP?W_`sM52O1U^3%y?9dL_B$U5f9WYgD#fN4>j4L@2WnmdLcx~o4)85j`RYQ5+Iu%Y)~oEiFuUz3>`v +IbgV_CwaH93vQq+CM{m93zR&}4ylegWmT6NWYROO$;_gK*u@PwzA5g?eqt!S`>$kmI_f|84}8jwH);M +9xX3AZNfaThu0D(;xzp{zQwP*#f-qh_Xi8f#M)GUJ3M{b!%uqT7&sYD4A%cZ5br>H%ZSjRDvn_FelAz +^z0xp`y(7(|l-mK;3u6M`1nO7sEvi7|?0_myeAagarClJ$MNsx45((x>0tp`!m +t6Q7GuK!AFFcn&ON+#}&qz3%sQaiD_y26$g|y<*osF6L={!iRVjHa;j&FQnRwqJDuzY9R2$c=(cXpi_ffsbShE<1$>@kQ#Y)Z3 +tRt~FR-wK^~eUHq2RH>Z+L5d_=VI|Q6criEuIN1nI(=~A!SEg*T%1qC#xkt(U9%| +RU5mtU>e>N9YOKFPPCw{$Q8bUy(k;fK4>+K>&*mGg& +etz#c&h?K#qDczjtEdi1&JJvWuPi)db3D<`)Ut(2xc8yBa{Uai20vxXVN|8=Lt1Su?!$ +sBrSrl-2<& +vgD16lz?ln%?HG-WIGleaDUhnC>DTJ0f>hdD-b=}`VyoH_yQ_%?_rMupQD`N5PGSK#x^|4_t;9&9PIu +9wih*Q#`3Tv=@qvgOP0cGmFPpRG2o7fOUr?npnt`&GS-AzTNm{l#FLyU1jR3q1k)G8%4(>E^3IVe*G7 +__(f@Oly%-WN&p&118w+k98#PQn$C-Ld(@ptgXCszwwMu;x&pj!{5w$nc?ezY8cQjfgFGE>(9_qsV-K&W+=I(~W=w8&oCwag7tP +8Ea>P+y)i@r^3inC&XO4B`KOEQpAh{lrN#W{{&FQe}q_{ynF9Laz$SN%DJmHQ-k}`ryZ+n6(SHV&F0Bgl3c)qR%kW%oK=uy@{x)Vi$drBHYshe +bv$IqVuobAZA__j+N+dPubeu1W|cE4|X;y4Qnn#i^pnr;MW-p#tgvXXWIBqQnitjLl1VaU$!6A#0ro` +t9-zq$pq;rlc&c@s8=Ht*ToCU;Mvt0Y-G`12wv>0Jpmwe(uq*RYVpNmz +ViG;bTA8J9&}mH0oPH_aZ>G7B8a7etV< +J;A7nb1t5kNagAKDHzMw^_0bj_4kRiw$;)Wz0v@(!psqLUY=tte^eAxV;!AErn0M<5s3^cS~xpX{*PH +nCUo1bG>;c|;NqF{o;eRdc(lqfM|44sbKiXIP0$8}QQzdrDjmB~z3ghi<0c&YicmlpWFXk}p?Dn$Tvx +zDYAyW@a%<^UMg^sArj0NM4?3gXPj&fgO{P0YdsiXgT~b4Q9rcIF)AaT8FG5gOF|w*fzb-0$|#&IiCl +OT1HQQ-Yu(>SCs5*^Kdz$7F0>FJP=OZaN1mGVfBnwf`%L5%hZtc>}^s>|F2Eao4Hw#P7Gt$B)yH=fcV +q6Ei0ryT;E8o;}lj$+s0Pc|I*$3s23~Lq4Jz;_IaKuJN`m%iTT2{kLcQS(>(^9m*RZfPINI%(?<>H8q +0yjE_O;0iQY;&hul&u=n+987lU#a;HMZ^5fO;gmqpieTlzhAopv*kzL_a*$uQIinpM;58=4nAEm(YEw +eb$WzGuZ0zaq*@$kdR9%x3=ajkN~X*VNw4&D8W``PI}KxC@1VR_P*1vwyTzGo#;@ZCH+xNH5Cb#PN|! +G0&k<0PiP<5z5#$k7uU%ugY+u*A@F=*3;QhQNpTJZ5Qw5>^QoTLUCC!qp%Y0g$@nM>TYDq(QJ+bLuN@ +o%iNfFK&w3r7r+j_&A$IqcU3t)pg|F2bb$hLF|bsv>~(H +!#B($>(->I3J}?|tCh$5zJR@C%5{zYLj7v;%8K9YP3)>Jf)%n?VO0B1-+>0B}I_h_AVpE4wjqC~OEfa +eI)Suio{M>y@;52@0+N)-{ZQC-DAtA_t6n2$t9N~~1vqM06#E8H3P+j86EtirtEhPvH@%oE`=fDD8U_ +f4GRu0`AWy{+px^LtZ;9zEf5zG>4xH*a1LpSz=jEfM)hl}$_Q^Nr-vB@b2&=TZ4@}e})FW}7XA +m?LA8xnfI#Hcy5qH@&iyD+nB4enl3j%7bQ9$WN>a^s9F%NVp$Eya#@gY3cCwr1@Q@R +v;(rXT#~_{-nIRZ>;?9oI0m)nNYtoM_UM%nhlJIDL%66jg*FvCbgG@U%~=*1%VMYSqnwi>%ZqHVrW&$ +jS3YD%$VKtZH-943gEEFuV`MYW33YJ+s9UWslTTPjjmm;_RUG={#VZ +o2^crnxL_i5IMPufL*2P+|Q`BI00ZwVFeao*5G9fwXlPtj@WPxuB~c!BcT=r#w##Zw91|JQMSG<^`@j=Y((wp<>P+|VlAEWE6@Rco{eMe? +PM1HX$(OMpl2CVM%dqquDNBSGrOI~BL2S;!B9DeuJu-hYPDd6#rwlI@YbpL?z)F*4810IUpHO&-R+C#SA8N?lmwmUEa!Y0p23T05_la6jVR9MoV0E+;~vJ&n#uKaqvWAbxY`sK3=+b}NA^63DIlUgRA_|O4O?m$r&2=61Kt>Mh9OZ>LSr!GL&X(p%L+cyy&)4bH%wf41%Q`* +CwPCt!cW0)3Ner%C-10_?2nieSMT_xiVCYf?1YBiz<<^8D=fFGzKFOxy%8C8FcD9UUqUX$vhN9D4h)C +*l{gJX0e^M8)Z7;aR28l%d<&6{4IBwVBi1cd5*LOW)5IDhtl +rgY6!DGpm)Em{5vRWmc)@>gO0+YZxwirP4|q@Va-w6AjYz{TJAh>H9^rXuFR;pBjEx`n~hSY<)`@2j# +2D+a}*ty)x6}t})t0MeU%MlzD>E`y+Iy=R_T4ohG}P(~d7DodG5sIFA9{s=%RL;e?5T6a5Gy759M~AW +K*RQ`Wca3{WVRo+^`a4jcskH}yLR7IK(K5_{qN8;EvpL$0aCU +nMwi8NM?uWt#8EQ`-j)dsyB6SR7xXhpRdceO4HXCbhbH|@W$?I|?=++`j9t9icLH;R3eZ-3e&}~Q>`N +pL)mb$!GEl;D5iOvL6l`nvyK1O-ytHXJjIaze6q*x?%biI7n#~CZgZ8oQT#}1E-ifZLK>i7`omU|Rk3a(f`Xj2RB*x&n0{y~te1v-Kj;JfHSX@5}5d9u*J!Lkyi +C1&QHms%BLlOT$h2P +;Q+e|J!84x53}QitHt8>|%tJz5kml$bOIaID!S~fj_S8?!*&<0*j!|maXY`UW%;RHPZ?=EzprC=@IVz +|*IV^ZLH7+-|{f`=)>`f$b{+8icqFN*s|A-#?97?k}%ngM^SC?g?;(*V!lequ_HAw0U^5m#=BNJx?A_ +8fGz)ECPsRkxvA`nt6(3B0O6iEupLdkmQuB_H$U34VtYe-f+rEXfqLyjCsZdv`5<4nibYB+p$j}C1{c ++fFsKK9Q;%P>MEN+BtS6;QFW=d!B9`7 +cIr6XQw3lzRD8{XRSErDB~wK@8R#luWHl)-?Tb%BsnTJ){{ +~**}6%HRj5kreHF1`=Y6a15eD69Z#W8GRUpbLX4YawXrxa3X`OK$(`}l!w26xF$fr)ExL+Sw*V|XxFC +mzM`1)on^DJGA^7jU`&t9WusiaTsDxL1kZ2-4rLRS{RuF)RAI-4_md0QL|O8>Le-+e2 +n4})#u!W@{?Hq>?DwH$XCA;b++Rd1awI&0b&kNOEM&xZm@HcPu?#Gcc@Tvc6vze&4^uNgCeR2AoM)v^ +6FRaR&n(BCpYub>jw;W@+H!AE1=WlSmr3?$K^)r7^`WV!yddt9lh2cR5GB7e>{-IoJ5X%5OU&zybHBL +YqhHGmA!Uppix{lzCgp%`EmPaxAEm=hg_TZ6kVdWE668cMS(HprD~eRyat1}e~mHgwQ{r@t^iFE6`4Njig6^`-l<8#=|Dk3q4HS}&>eU +21^A0>uxMfEEzcXa-oOvy)U46h?X)rhP55b-LZ^oY{EDu$#4H=T|l*1fY#6QSdQ;|zSC-K`5b>0+6CV +lh)EHHk8#QhU87%>p6Uc+?yMa|Eje<6JVm*VBS#;njR}F(`4J>QH|k7^bTa4#hRQi?iMn^$gev>65J!gnz;D3iJW9*OL0wWga<= +rSh!>(->E15Y*Ogx{eeslN#VFB{$oUMgQ`fKZMv!`}STIvyz%jpK;A|L~ +K(((&tGp!zxvw(VCnhQS_DUKT!$`cjS&8z<5v?B8oPB~{o7T71(d9iOXRYPNP4?Q4tg{Fb{+5R2FF;RzFX@W +Chm>mt8}!IvC|tUr%YW>C-;8L-Re%`grUkgB8mn0{9 +vE!a=d)8j6eTL{Nv){?m?K(2k;SIj4nHvGkYqK}h;Wf&X6wh4M?o4O|`9DY21xy=^>_3IXj?6oYSlN1$V@kp;5OSNVuT +{)d+x5=b2Ca;F!HBBiJ|0`ghO~l4C#@xHzWe***O&~k;DTt>u!8gC^jhOXAH~~7XBMjJmTrzY^On2fq +AE+#(23J4ePrxZhP~BL#s|~+d}fhAg_QxiKsIxZxOiB7ZL)OR{jwI9qDD=@-8||%CO<|Uw{lMLj@`~9 +?Xn4vF&KhxA1CS@M(n0FSe6v+nZIyZT(TZJBs{gw?TH@(r9r*y+T9-QZ+y}N-64QgV1ajSS#fF6|1=@-$%-@ +nR5{VV~LcSRcqt*~)lf*wG6=X_~WQz8oyk@lW2uK(Shig(Cgh~hPC$ibRNJ?X+`fm>03FCDq63}#(o(7x2_O1L`6tZXO+HH3w)$7-tRL0I!RC$^;bl=bOEla-9NvFvj99u}|>7=>h4Zj13 +YIVyE2obQnc62)nqXL|cNuR&vUZwuD;S?B2_~kG>!}Y^4t(r*v7nNwNKaGPf%G;dhzN)gcAb60z@=Wf +enh72hjs?;wnlzP?#r-(;{L`lt;PJ36V7&jWBVWUx{e>{eY5wU>Hf!}Fb7qTd6{$6k9U+eZXlx|Iib8 +J*}tg|cK;&9@*T!MJ8u^J*_*zNB<^KBp7%p-?eD$5Y(n=k+)pjcCcUw8A3*uJFHe{I5vo$E0IA{!`M}+0C{?)lbDWAGi3TxnKcW +aAZ;HzPv)ZEx)SYLi_s$!{e&>OP^Xz5*g4KF9UWw}Ao9Y8CshbIcqLMGKqUI9(glLT)SQ}mj3vKYo>X|*VFGDHAt&e9E(~TNAog=kMzW(&!>`C&&56Oe?P#65V{qw_z +Q}rcoWLI55%&#CuoKLxsP#fSNNa%_%2>$p3W8j2}0z;1;5K-&s +NY#aPYIAPvAy~#a)XNmwbhU!NcEhAG;lmSft~#am)1M$62&vONbwH>Rz0DeSYl0T>}X29{(`O(myM>V +IE?AaISeQSHzd06xzfQ#8&(~AF?mfsJs0l=^*!#H!q&NU|XV$eg-@!)^tWRnfUDL)R=QmgRg`*cpj0` +ZDDEEX0&ZDI(k}{u92&+x9@ +iASMIkWaXTT6QM1Ng{<_quuzO|tzr*jzGtzuJ}0KzMCRc}-DI#vo~-rqEjHCs4cxPi~hnO|1ol+_NeGlfp4&Cakay%8H&;W_yiZ$VSW|aC}#YTO +$EQwCm95KU}oV^#G=xjJd))j^*J~Xd|M~UK(hD1EtoIEeLYq2R~eCI*2rS1=eIRDt=5tS*&U%FyfS5Kl3k*N1$1^WljYp$r~oD +n>Ow!xvV}@l3Cp**i1->_&Flf+#(KrmdtY}@^=fx{o~{!q~*6sW&hK%lj_$Ud5jeVMR!6+Z+%9SI=qe +qg2YIBByov?>i~k^*qOSA`i`JOu7QyK`$QrY<(_gx!&z+Cxf0@0@-RtR?m|`Q%$eM8G!{Sl`NZVp#5y +*jp>TxUT-A~fhdtehcpACw)OgycBli-{SfX4cD>?xU^4#lv*8&g5lk%iXMVdO5Q`QW~D*3j-S7jKHL5 +E|?11|yRtovmcxVoh)A|PAfrz1Y@4&S>!%!2;dR8mPVlsCgwWC8`I{&5xC->EJ@b}^4vbQOyhvYNu~( +;;Z8|9JWU296>t$Mv8iW%VH`5zmSa)h=|nqB)Twc|n0Jw6Gv)VPjIvh9)DxI2}V?xdCcVv0j7h(hMqY +4o^mr18I-g9j>#L#GIMB|6B`d}oYB#q6`2@%M)BpOO-=wZSZybX +n^JyckU9lmj_%R}#uA@;jF9jqnkBwP;!VV{(fE?Cym`wPbm!WXMR!@=^$36GOl)~~HFNIQDB8R{N +Tq|w_utx5%+8u|CnxgRFR$M`dj1@U*#r9h>(6Ir`0E?`_2k8`zdV2O=m|Xd)?p=*5oy^;owZH1eDspN +Jc?pe2J-?>kN16X;u%X3twa4Hg6+JZizM|*OD6WM$)=fFC;if%@r@lE)N(`P{CDVT1#Y}zH&I;>6s#%W +THLLITgU0G8=oFU`sa!CVTN}cyB0K$W{pUA0_kL(mwKl5}c&t+Wds{uC@mIQ;Sd4c~w`#K^+dJ=M +XU%{lQ0ZI8&Prw|is-9ojKPzSN}qdLE%x{H)CB9E)cNl!xt35wk3sS@PQ3x09ul+Cz{+F(vLWCQXPua +#_6&;YH6Hnm%PIeyS1XBEzzQSWN-jw|MefWJ$ +NwXvl+06h0BXJT+JV?L!HhU1P$A1A(O9KQH0000801;hJRgR9)9z_EH04fUr02}}S0B~t=FJEbHbY*g +GVQepQWpOWZWpQ6~WpplZd97C4j?*v@ea}}6c}hgd2Na5sfCmI(CE%@ES#C0I&AN85-CdQ{{yQ^ClR9 +ata*MR9CZ5YVXD*4_)z$^jcC%4tldPTHL20$dGAL_2sP_EERp_vKr^?XUghNgC-7!C(!6)TI(jM2UF2 +p^W%DSSK<(=f{#XczAhb*PBMwL>lpD2vnVeG0*Kxd>b9RA=xdfgG0YR7tHL!1%A|r2nMb{zLAk~9PWXMzS+NoiTSV>Wu(DQDrf+VKIlOoc +pRK_*2w!QMzI7-Af*d6(=qTy!I0v)q3@9)wx%9!!CMe!g#;6ckJ`6OCm3*cTMlkk_4wrN}jbwS +7M5+fu9{8u%=~%r%)S|r`vh0&A8ooj_4}>VXp&gQK@-H-+Y@qwlh`}t<* +&kKzOcXAntST`v8OQB#TuP!*{d`I$HUlw3T3_72k#kd<>*H9+x~7$yMV=$5{~hQV$ljoKmOG+J$vft( +=fNf%rSb?L@$lX`>#}wF*>)-6L4_@ZEj9$oBCOb21lxVG{0TbxI{4B8WH}fJ*kNEgkhDJ#D+m@=*o_KS#YyaMH} +7u9YiI9~?3ITOEBR1sZRsni*IeU9mIt!-R^KCicX(w=Y|DWUgPQk7V2wT;rS!iIFCsZE$8wM-p8ohbZ +|>-<@6vHU41=xDw(V*5`f-hqy$s5)6*b1{(1*HBegRNR0|XQR000O85nWGJXr+919~%Gw?Ogx>A^-pY +aA|NaUukZ1WpZv|Y%g+UaW8UZabIN0LVQg$JaCxmfYjfi^lHdI+u$)R+nn-jWRjE5)`7XK4WOwT +Fs!6hQDUXY$L`Y^#kqkjuqx1T|UqA3B>2Z?L2U{Y6Mx)VhG*Ir^rt8IKU$6V7snkiy4;uST9sRHCrrw +m>lTFv`1V63HRl4uXO8*q=e0?J`Y>=sD->oG#?vHI*Z%w1G>f=IuomW-9s^mhvDA)Z${9V?v%loEVh* +v#)qgUUS3SRxt_GMG&)k6Fv;pfl$wvs0dxGmc>D{JTg?^F41EvcG$T<_b+~U^V4*5x!q<} +S>Kut=641_ZkX_9GSfh+X1n#)119$^Jdr^9vMyxZ1BmT(?_H_ZrfaTlp_8I9VSVbR+vQdHf0EEyh)&k +|PAd4a>pLl@VfJ#@W<}Xq7Abzq*MP~-@cc#DN#M^8m?rpna#9i2;=;{fmSs3%md#I2P71jZnOb*c+tX +7uI}z}~b%deL1uV}(c<=QSn9g=zo{JTT#K~MN|I_j2B{lfIsU`otIwy!pl6>9CyqBWCkswZ0briqvfe +@Qg76L(wX2b8wN-D9Y#)ViNsrx<1m1=PS)c3x&U^%zD}vfYuWER;8uKJ!E*$Up +lK{+aH9Y6outVDl^T`evZTK?Wj)hW2naBz9>-Evh3!X=$_*^7$OSlO~(fiN0~hGF5TsI +xsj?*%ov*T7W}Z^lCe$O<3UfaQu1jX62lSuy8L}FnHM(oxz$9MX_|U^=#tcwx7Jt83{P&(Xnkq5YkKJ +NR?RjwlNoW*s65ZRtvYAD`26|pfxbqZ7lU_`R#L(&Ks92UD_CbG``O&SMm{P*zCgBNx;z#SGj}k|0$@ +8_X=qDeg9c`>G%t!7Hk}V%^8(kpBNlNB~@tk8l)0qY +zPWB(Z{l3IWewzx?`#*DscbvcGBe{Sp}Fa@|~=cmTO)-&n{j%%$m@if;g_1PdsjH_M}axe!1!0){=|{ +aWO8fnOSIgbx);X<@Wm%0pWN_9KiAYfQACi$1?aJ(71;vt${-BP1@%Kxh_+FxT+vK6sN9K$Y|~-TSKt5i^H<+~`|>r-R*!en_pLfVJ8SdxZH{&Z&;g>>Y13`bR4dn +@7PYxOgUL2s0d1BV30W+;pHyeTq8h6P{EZ7|O*XyE71-t8wb2u04oR4yHTZ;)1l!V;FnbLRIJV0Qxvq +esMW$bBAp*HDtpP5=bYihnTL6)Ko!2;sWi@e+P#+eC@T{8+>C+@-S`xEg;2$SR2GP!6D +Z-X8{h*bHVI|7kS!v5|8Q|}{}7CycKLyH<$}k%lbu3|3%cVxKfVyNfxsu?*Jdwv<@N?-NeWbqC?dG70 +LX9((Cx0w&qXrqe+sx7?8R~kh^^(alkQ2~ECG2X?jKG)!pSy7(ESQ6f`@z}{sCUcx%e3vSM!)SS@fV*u%DPY}p|b>STcpYh0JW* +TVN_d{{S3c`WJC1_%{KMr`SSCt;MEwS2$#BiEO6L$5<(k6_lc02X-I6f$xUAK6u?xP5CJX06&VM#fB3 +_BWZXmKJU$pjWK3|i`dlmAU0Ijlu8MlUTcH#&W!ZhIBIPj(ZBlM*({YivKnZ%c6@-w*5jX|b{RU*LLSFH^A(xdkI^9+ht12UxbwW>=Hfq0u!5(YH-gC!JgKoZX +?l8>zkXN@n-EcA<{B++I5CzZN>R|hY16(k13KDqkGamq>LEj5%!=N@417Qyb=o#wo(4#*{_R~mT<9zm +=$Ji)_I^4!+zykzr!0A~aq+0*L?>px&gTsJ&9YLU81my!d!7RK7$;AF%G!QAbnVq~?I^-g1+vjOfZZ@ +(*eUNEd+5RC-EffMi+;PqS<*dyM?DY~f)v7yoiV$FwO#TkXEjFpUB2r?o1j{V +okpE-ZJBso5Ue;wpWHcq;sMxG5npvoRcKytc~8x>HV1t65|LFh{pvyR`4M@rHyPM@E0R|eKo6L9%pu>oUMZtHS$WR#G1%l=dmzTiJ9 +8k^bA*$;F!pg|GNOf#{Y*$w(4q*Tyswz4p;O1=VcTK822gb2=D7)4xVn&(+v5Y^`HrS>U2-TX|v02+4 +ShDigJC`DNLV};;`zHh;Gxn#*eA=P##uiE|aS +&PTmY*zoji$_!?@%6iN%#n6)JzuO=8j)J|GQpk6Gmm8=W0x=`UlUu3fC$-DWZ +Ap!DFGp-`lsNI6k@6o{BW>G@)F3lG-7y!Jci&>LnFl6_^pkg*PG7C=9D4NCNacMsy1=>c5;@O7!E +ET?h;)#yRC7Fkc?bg&P{|)4IxmGk@xHbt8*7`({neSH2sdopsT)n4Y96o{8^rCN}vOyx*n~Lc0w(T$+89t8nMUfgn;5j^Oafj>T`p?LZvTOo +4gG*g3>qJI=!fpwU^pfA9vFH9C-AKzMVYZ*#WKoQ0))sdkle4sb9G@*r?P1n9x+V}!6HAcOmam+T<$r +VjcAfXU^RM^4 +OkojCinDdomSlys_@J`_wjTG(5FN)c#9ZAmigfjQorjAAlo{P%ixNblkvZUF@8+h +wpGIwmzG!L0jo0dOJk%c6&t!^OZ0~D$v!*5>uz@gwh5l42nQuN%uAf-ew#osP0)Nq*x92G%~*AyXK4L +YoVP`ANT&07B>k0ZRr1W}>&dGFluG0YwRJ(3FspR+7D6<=ZmRv#g@EPKjZA6n)--{!popPav3Csr2GHXA6Ea9}sfLPwpK;jhIA-5*ku +ScbHA47nd1g%uq#|dBE4BKs=S`MHG(n$a<#WM1V^>n^_~BMR7@peBehiA#9&m)j)4MWeWc#%J@AiIvJlT(Pp}ZES+a%Oc9|Ms)V}Ir3%S~FXUS{XR{0u-{z!SFhJ%a +-j8SN4Sqx<;(pD1^Y(CI>Uxy6`?PL`U`G{UHA1(@e1mycfgoX{AR8;!%-6TmRZPg~<Fl{!IlI*oO%2+w=wDygHxOGT-mIk^-IIj7) +)a782R@-D9Rng2aklLTrV(Ds}ss9{jQ-{&@3}{zD0icxAFiwzQBVcr2 +n{9@0RFa8)?)6m$6c&37bjdzU-59BTKwGYC5ovl@?&+uIlcoYRO}eOndD_WV|QjDzRA%bHUOtPl**4~ +{82SOnPN@fBf!~U(q(KK;X<+(-h=#16c>1LJKA?s4@#3HcbbK+0gDO)br>K!f6usX3jv&jPL-bpz;q& +v3%04mZOwz;Ge6`UV><)jprBjEvA6c|rs6H~b;IIS6Hg36GDKqTO;m5ln0Dl9u*w?j#@J#>-A{9}Wuu +W!p5#P()%Y3ufxZmIsbnUkUnr)_QFGezK8SrQ@(pfKow{l)b`kn_{V8&$c8?YwykbukKU}!0{OJY!A> +U~hx%`y&_^E7V1b4)FP(^Ms7Ci)ZIqUxciaEFEO{4P>1nzH%6Z!#+T?{-`f4TzGyS%^E?ts2zS{^M~+?KOM!tXxa1 +X7>2{`kG4(^)PIUX5eo~F9mRY?V|!+fEiJN&ch6tDgE&5&7?;+SQaefJN2vhPCrtoDa^+13wLH`*jG4 +dvY1%mu)Hp4G#^t9_8E5MLUvt^hk5Vhegx{n(toO3KRB%)xUj$Y3FnX0d9oOe-2!zrULy*Tava+lv0 +$nif{=CpT?z|DNB`VrZ=V~#~`)yA|@Kr(}=Y);k!^hG~t@$U52SSa6AFU}ybOe54fsBbr)t>&#_sEJ} +)p7n~mYrByf82;+^dt=RBaFs&xMInPsxJZwGLXiysIO`>DrVKVc9@noET!QJ1yGyYMhDM>e2uB3m_lC +r|D%}Mro-X_{8$c2-Vmedx`1Ti!=Fz!N78B>X12n4m)H9oC5LvV=N1@Z@H9|Ba!O2LVICbXBOtzM4m^ +<2#r5E05)62VkuvjppLU%4sbz`_a&@!kKA2ID&|)2oG(mwN4 +4lIOEI~?mWTdftnxTbVgs?>Ki6(?hhlB9+JfrsNa9gYCuv8jTd4jeZ4Lr`#)(y@66J-n4iKpknB1A3S`Z)x +l#m#Hi+^Ll2%^-W386KvgjoGkw5?dg@}3szGnox*D#LMGNaSn>Mp2uP+`ubUlldwwEaJwGxxYINszux ++COZj77461xo~;_onJXKjC8cu(;fAC-Yi)dLbu|3?}Sq6NR7Wh1ID&a4-TDom6t0ua80&yc_OU+VuOD +POxT(R2Jln<1=Bwk+mYjnPnaTq+Bo^{Gihx>R0dl-k@caXbU=q>rVTRl<)<3W?r@y*ACW#YufWJvADj +r6u-V+M9_3f7E8+TV3B6+h;DRdAst}N3fn`~l@UTGYC#|vZDhC3lvWAVuRj`m)#Z@%mRZ* +#VZbcB^b4j%Bp>*l72aS%P>PqAV9@M8DS{3G33D~~F+%0%-1vu|(4Bd3N7$(oAIcIqGc!~qYibDdi<@ +3nXeBjv8)W5#>%x8dC*!xk;Hr`Q8?ttWvKukpNBa@0sM16f10iDOJ|IMra{qE&C@380Kqp4okDWNRjR +^19f-A0|7H7u-ReY9L+-EJk9dSRFH9bef{9hVRU0f!W4%OxM+HP$!=w?Q5X9@5ZEerEGo(t~oPqIk?Q +kCoGoZj$1Mb_ao7(F5T#!)9&qbWBD$=VaK3#~DD>JBe7(50>p4%=1kYCk10io7!%oam+T_a|Am|mxFl +$X1tXWYctdZ=T=AA&5|!`Z@0*8m*ea;`qa7){xSAG$VPSN@^}%e%F&P +u2>6G1AYtYE}_<_p&|4gs=VC)mSp_ccuE>=>kq_2+T_0XLu(dN?R!Q{~hI6AHVx9%YougFg3!j9m1C~ +epL25Nrn05{vyJsq1`GvplANZw0r9{7ZZpZ8lyj0eY{^;}~^Z80LwG?ZZPhU0>#vpME#zwMR+*N +Vtxl_!Rl0|NCUjinPH4byD2^r3G=$?cU~=X)-3qOG|Kb5Ah&yWA@+Wmsh@ +Q_svUKS{q>fBNaNJ${ycfdIvwie7iY`!L!V;m(Pujws|!5$Q765f1xaetI?Cy&VevV5#CV%nc;tLORMTF789|Kl9sb+sTq@s2Vub;il{}rNj(5d4er`dBb(^NZa9h=#Q66CS`YK&k4e^WRX?z5h`}#Lw7~~a29bXBJ4!dy1Y!-yBRDkc +C4Sny~h52Gz4%~mb4uTq4&y{0211r4r?TL!>vD?u`^aRjta2?v88v-}>X_SX_mG}Pr89E}F4MGw;;?a +L)j~URjIIE8bC}L{si{|VdbLhKm_hlB79JA|0QvJfsW0>t3rrZ}}%)mReE{XA!3%&dWr`i_QX5p%PFG +kl|U*Dvgq}#69w^V=>5hEF2M}4In*4>6|W!KM#&esi0L~CS=$8qr%;RE791u|i6GNpqimX_3rS+4;*4 +cS=@cM+pzBeklwwu#}7RFwwRTvNrQx`fNL4PTl|m)=)Hiu+Y$daW9hwTuBpzVkW7H>c_n}ZtG8lt>{=cG&d875y?-~qsGHdWE_nN;5tgN1rchswcCNeEvxm#3e +ZEz=__5l_+0N$mNcXUaMJ2B>yDLke{KDirb)LYZ=W+rE%_lW|8fei@<+<_#_+!p?@bTdNA#W}V;(7jt +FqhZc7XiusF`d^DeCf)b{}hbzz#l&jJuHv`gOVd^vdnM=t1DY8e-pBvh +O#`e@o&@EZfnLfxdq%CVB@3*WLseSf&8{V~2o-0;BB?pF9bzNr0~@2V_BY*vI!|{#|W>j`7u>UbeL;F63o@TSc~*N;br$O)@{rB1jB-cLaPD^whiWN?NM{lJs|2T)4`1QY-O00;mPT~Ad200002000000000a0001RX>c!JX>N3 +7a&BR4FLGsbZ)|mRX>V>XUtei%X>?y-E^v7R08mQ<1QY-O00;mPT~Afz8_TKx0000-0ssIY0001RX>c +!JX>N37a&BR4FLGsbZ)|mRX>V>XVqtS-E^v8`P(fxYSp{0jFX-jf6#whkW5tY5l?k +1(buWVzt#A)e*4jO4@XQpOqKtF?P58#?Wtng^Pw|kEXJUh_@UV}Pntv9CbJ_i?8g=-Z#Vk^c|cOoDfd +85W=xNh-uc8m?S`WIPHN8%604%L{s&>PdmRmiZOoMr~%WTO1-Hk&i^Jh^2oH^!9Siok;v+@g@bAg8@% +g*EViEP9|DL0>1pL|R1mlJ-5_+Lu6i#PYq6bLhfhReT%~ePry3e6P2JyZfuq>9mNg6NDGiO%r_ojMWn +}@nUHvdk569ecMGZh+{i5w?ob8b-|N|-!lBq%@QbZKvHFKlIJVPknOUtei%X>?y-E^v7R08mQ<1QY-O00;mPT~Adq3V|Cb8U +O%6UH||q0001RX>c!JX>N37a&BR4FLGsbZ)|mRX>V>XY-ML*V|g!fWpi(Ac4cxdaCxmfYmeJTlHc_!I +vV&!0yGS6_x8gLPJ3|T7-wAK7>;xK;2kg&*%~pXNQF(#Sm*VBzj}1DpOh!T3D}K??5?h^cU4z&xm>=G +C)rlAEzjbuR9!O;b=NKy?+#K__EkT<5_Kz1^-0u9oMgZ6`lGBwp~Svxn(k5EE*9`i4BhFbkq@$gnZJ* +9FORYv;H7|Hk%_Kt&hURysFN&3finRLz!Rsw`%RX^2Ik!tebq>%M7I}Z*Y2CT9N>>$wB3*qB=~j1LH3 +WelA;jHsvCEWbeCKT`LUEIMqG)$fRPo=-DLu%0u~Rl@9PRs8Fx3{jxF99?a3QZOJz2*+}FJtfB<7#ZN +y{Us-;*x0$LJR0E8>p7c9Q(Wl;?WLBrRJ<#M@LTnQvJ)2?psVV_4_QK&;VHkH`nW{!X&kQg3zM=4HS4 +ex;o2zx&cV=prRb3C8Uw?Yj)5b36)4HwP!Wzi_PSk%W;*AJrU?(bm{^QSti#lG*3uxQf&&0&fX`duaW +#n=oLK;tp>Ap4=~nrTFNDC(Bq4d)Z$u3x<1sV{2e;wzX|08cjJ`&z-@KWfZ2;wK4zz8g;s;73qEsIvS +4sN40~sVF}b_YP2$lX_pvUR@ZRxjZv7-`Z#HLO?{>10q>jlt4&5_?Lr}&A0d@HFOfC3HXX+;2Wui{{h +tC&GQDCe16rc0i;@18yfJUM#k(&e_nUPOOh0N^IwHJeAo9~AN;BKZw;rwiO#UspgKRup{RYO`3Uwo`*3f7H~I`5yo5&BosF)0gxxHTkHQHxs@olytY5oYPgZQCU;BAI!Ax;oldAJ +|aIn=A;I9U^s|GDm(S^RP-{z{5Sk2xfzff{2x3Za{JuXJa9lUwLwVA4>0ASXzD5l0|lE0l^AkR2RTD> +vyhTQ0!2`X^dw_@vmH>`r8rq^FkqVmmT&s*p`PI$Kyx&?JDz}iySk}|b3PRJYIaH%k2O&4<&aO}HxHl +!?8+e@`zAlg1{h~ej(vl(^JYdg7>>4OW-`htkRG1SPdV1AG~-+FTfn#fm0(0jM_};2o1YsQB<8+0I4V +DpzAYAr7HHAd*xC%p@;yCU<#}5iWuC7W3s962{f?|&IQ|4?IkxjXh!b1Q_c3qp?iPy$*l3Y69u*7=kz +wh@H@A^?vIM(Zzhzte9`?Nv=Kt<^TzwDZk|F$Cu>*eqNRyKRffqM_C(FcM9^D)M_gh3zfFh%#CZ}~@g +W(ozp$rb_AB}8*2Y;-=PZor{LHU~f?NU=g-7tWq0R0=m1=8DFQ1RjzZ0x!C>&<|h!$6`&*8rvfAOIba +p@TAEeC94UN1tEJraL`^){1s8ElWEQ_5Ko-+Uvm~l-V}LHEPxY5Gw5 +#z)onm;9X8)tO9JRtGZL?wnUR#f>@q0I0HyPS6tvHO6p%_+|vCfQE)u^P&v|izZ41R3K6kuda{cw^5<0gK_GJ(;bK`aij-5D~GN}4JAlJf4-dv5TdrlO{ +`EDv*6X5=5SmfUe=8?-krQMhQ-nWi%g1rHv!(z5^}wcv8h$=XvQ{8zST55v4M^E(p8SF`-dIoZOevU* +(-K-6Gv!+A4`iXo4@R_=vyGIkJM7f6yjMg4luDm{t`S9XvD2}rj{GQ1TP-RZfeF|g)|9SM(6B^-Q2{?Pha()r$xwQ@Au%?AVo2}{Pfvu2FETlOc{$|bzxW2PD1Ph{CPV9h*mdI=Ffyq*G2a#$UhCd~0Z7_ +Z``Bj9_A_H5R+mX-dI>l8T07;=#J91VP0N*D?fAeNXg-_-jX#8_7NWaw@btgZIv8+^I^%#bK30Qs$lJ +hwDfve{eK>mV5fEfF8vVVn`%`niC8O|f}S>C^3$*S)tH)SVrAEIvpRT%#J@UI`SeaZazLUWa8$``(#+-P@(Hf9Hha7ugBAUUsTdCj&pE_)aTp}MkYcz;Z~MqcXz;VbPYT;h^2^yBM94 +SolVTO3Je+m?l(dORSp@SC8TR%sjv@`M5pSno!ALF#k0!XNAGX{dN)mh?CpyH(n0tt7`R?|Vw~kMpx{ +X`!H7E!G)bU0K&QT2E%E+KN6cY_^H;D+MEz~VkYH6`uyA?*i9b9c@GvFuG&#+hv~8pp$q8``lztHq!B +ttLfctmA#?UCh@bFQ{`D)6W0>z(AsGK_1s;RN;Hd#p>ECxnW^j$}(6hNIzzBW1ro1A1$VxbL?RbW50x +7uJm63A0ASU2K%qzY8f65u&HR|N+0MuLMa2lOWx2a6-*7+tj?hch|06-M0fr0#`FD92>unMJ*1UQWO{i(Xdd;bZ`(OVwPAr1^Ny01Z<{vxIM`BCqT(!cjode%ny*SRLR)}oL;5HW1D +_#nS$g-MF6{PrO>mXHHz);nJqT?ukm-WEVY8!Yj28n5LK1CyBa#&*ecj4#bb^+z7o&NVWbq$)ZM5k4U +w+ekR<8KGDSYNdYXwJI!Mfb@dpQ%Q(Y~K7DGl*0@@9VNV}UN)2oQ?*)*Iqhb4i6=cwwu&t0eQOv>p;a +(>8I6C^sQ8YhM_=BytA-Onw|d>Q^iU+9Kwl$7$C4qNDS}X*W?e=nTx*z@tqM-#WC(iW4f~3MQ<*2*nx +WgXet3dA)G76w{heS)C|(M(AgP<^axZpz}^xc{$E?V#5OSBpgR+j}se$%KGsjQxOzdT2AdhG$1j(5w+6rS% +?eSm9l8L{YVdc;2TEwg^rl~TgJjsqWvoXx80*jziU?}t2}WJMp$E4E13fB0rmgERkhAw>^c-8KxfaVE +&d?HvoZ|t^0HC16GG;+IO5a|z4|U(Q#NA`jf53{8U5wTBR)B=nERBBtjHN^{+sJg-DC|+zRLXmQ2L{4 +7e1Rm@Tumu(|0PEfV65IdVtl8IuBpn20b!?;XTzw4%8NS*qQ6$`iCGK5Kl&aoKE!^Red@pR +g*n7P)E%O?}I8BNVKyj94bzzyi)6lGSC?X~r(XW+{WhS$&3HAZvS8cT8R#@=OLP?I7w9sJ_bR5(Jf

    =5A5q98x{o_w~!XgR>ngR$-P&L9>%990`iWHxqFoqAaT5)jcivkmD%Ll2eXcDf4tn>Lm +XMEelAz|j7_^td|9H@HnOFH@`{E>n5XJ+By66-jYVCF{W;<0GsZ~5u-r2SSxG#!X3*u?^1u)>$6OOp` ++CpsmEt)r)2z)UA7EvT&6)nrz{x?}td=oKwc);(RqpDykAD24WbGi+nusg~?HT2s)m<5qZ3fiiawG_*|}jm8BUHOoCIuahq}?aNnodQ8vG?JB83xk)| +;be2=;Uw;0ArJ7NqTu>n1`|6ouIGBg9yMEC+v6SQW`c9A|k$?l@HTHh|hTgLvj^t&Yq8Tb@ZZ<}uWPq +TLTTwcK=C=`j=Ywt*0SNyhMpLf|eKy+l5`1DwqEBA`si9NVoY2K@B$%oG|fjMV!OKsKF;iLbWdZ2Qvc +Tae4l5jmCHJi*j0UqpRxe9lXUFDny5;t9;>4F$GaJc?OgHh9jqHXa2wk?JLEp8uDl@pKa1b`wMoYkG* +agDSQjXe1msj=3Nff_bT%2IJ(w5TO-`jEASFUo;$lz-+(<p92EGj{|4IOi3nHQ9eOx+$NRwgcEqwg3GGf1a34j4PdB%1DoDDX%el +WKCYPyqe%-8rjU8bF;7VR({pn&urHdwdR|d2zneHL5<;CwmMW9fUHMroM-noQ2rqRjxfgar-Bi0~~P9 +1qa09glw9oQIbs2Txn+OU=WEsAUB9N^cOzJ0DQ +<|3Ski`c1v|QH2#jXY$BTXMg1 +b5Cered`iu$BYii^5n82=RD0Q!Y{{BiK}Je(hD(T|;Cnd(^FzvbBD??u(2ru+{sVv#3>UYSy7O3Lv<$ +`h@ij>S2qNAFt5SV$$sUt0&7Z(Snl=P5J2nV03o=WIvavJ2E~?lT1&f+E}U9kdt0Ap6FH8zMc9&DY~J +i1}e33-=G1V3GoufV7hWcrDX?%SCD&@Y<)a+>7{g)fHc6$zXJ+Kn>N|24R%x41EiS;n1_n=Seyw?E|#+7i-n=p$P48{mL`2#l{dn2ur<3+b1`S)T;KbUN?SC ++@PB{~3)xfh2`JTYpINBdw=sB@_y7@%VDkI7KCR%ghN&v0)Qjjm|~;A@)Ax-?lu6N96h +;n4{%?v>3=x$?@;L3LMpaR5|8t{lMuqp25+1bz7Gm-ohPveO2Ss%ZIQq&Qq5^^Wg7V`hO~Ni|ay_b$u +=HQK`cYNdvH-rZ2$R_i;t!3PTayi~kqa6ApQ3ZxGuLkiTGH-`)^>3rof7vc!G_D<>c&9AcnZH_dj!w$ +$-KeWFUk1UAO#R(zUZe*QxTPcpsnrIcrnwnZRy++1Tj)Z}~sInGLRZ0E5?2&kf0z) +nC6g>rW(m-?eJ&b+iehTChlCVRa|jOlsFztBoE-fgim}#;j9~@Cd$44LV|`c>KnR$h`)DponT?XH$OCFtX^Zn2zW?+BstQY=)B6bB)m;@H84>Qc9c)<>ykqw+~alI`%*TjNC1POA4BCQ +S(H_LOVrXcxS0^?=POSK+fWhei9d!N*yGjt-q6zBNWPw`Mnf1h-+>j;h@5aRl#m;;cuk?KX5c!~e+`ue_x$RMB230x3(D;Z=dI*>Db_hzH!)@|-z6Y$D0 +@vb$5`OTY^j8>V84lxWr;EW1<_=6CFuSYU=On0(^c&^J3>-z^Xy7qpw8uv{T1`tktIpWb9 +Bub^zQNk-1`o>KR|(BWTJyc)+`0hLBPyc&1=m~T} +O;ND%)K~+=K&}L?;VHej{aU+a2zzUrQdj4SrirceDKu*ht;G|NR9N-OJj4JAT_Qg9-45jUj9;w~Wg`n +b5FJm{k*7bZGu;u-+|BA9k4c>Ac8cy);yX07&!69Z?@T&`zmqj6-_nq&)g-raNO)IHfP{;w6A~=M@f}F3kWw$TJwtzX#Krw3x?LfaeMBS5YMFiiHi|mK2UP5E +X-Y2@EobWvur)3R|DnV*;LUUZvs+b)%KdlOOb9umeR%bG5;X(orLU>j*A}kNd@1oqig>?C#}@DqmbBD=)~<2@3K;&-s5od3!j>Cjb!3WGZt8M)p+(}GY&}mtr +-#AKDzq<`R0uI0o{Uo(CG`(_=yrtf}cPLD28(65n(P2n7NYvJ^*-Ut~Po=eKaQRGq*B5rZ{R!mdJkbY_$%R +=NGyx7Pb`V6M`B`Vh*Zw!lcjG12|Y=1+mqiuo}2Nj!qMg-5n5FNKSni=rz(FG&|BKd)#f9DqdXcw*Bk +igcB`2hVOiZ0Mo&zPsWW={7x6csbI;Kb1p_S=LDz|f1rdC*Eqj0KbwfUoAdgkW+dsU+J(PR!-DlTkpe +}n`S^20%HiAG*Ct}##L)z6T|5$h^Sy@dZ~4kLZW1()akudYk-W(p{XwTBTI=-0_{I*_wM=I|f9Pjx+A +fn2^i}!9isFV|(&vrv%4BYYDCYsIb-a4~1cOM86K1FpdKc-P{1%r-v}X#NJf~WqMp5@;A_wq3;03B*_ +#{;{EcuNp_op{E$EsN0%;zEg5VtNdMEuJIhCY#7?4^-eb|qJrRtdBd#*a4WMOSe^wM~JFPUI%vVskqT +tKF9CoIX^X>H8X`xY`-UBU9Y*J(+w^Di}ko8e9!NHm6lpj4hQr|-Q+WdQB%>_MLH*MsN_{Ud# +-OO)pl53dR=Apx9S4Ycjgd}-T?AO-{UwTQOUjdu5A(HHF?=Z=rK3}$-mu%C@^r9Ex*SI8bE717H>(L$ +b9@JBLeWe3dEfMz_-^f|tYH>1p_pqWyX)ND-W3HoaXnD^}RWo&6;FJE72ZfSI1UoLQ +Y0{~D<0|XQR000O85nWGJ!vG@RNCf}@WE21ZD*ylhaA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)| +pDY-wUIVqtS-E^v9(SnY1xHWdA@ryx{dkO55(U?4+*qQL?L>5!(|55tfd=_+O;lL|>CbBn(FE1gNii_q#8Vb9#+u#G0R@FHW*zAxuGrl2K5!)VBm^!!C7UON{&m%JUwD`%yaN{ +33s&G{_YlY;y=VyFpec-oyA7f(5j+k{61LY=bV@oRl-Bh9%&0)A2OEkVE%AHY1+1fgA4cd+89gNJY#I +DFVxA7{7BHf@Onu-Oc&ty-!S`5+KHNs%9yuEC&0x|lA@EEJ%iZE_McK +c9#_Cyvg!C(^4ZM#_k5X}wvzt^|kq#QC_N3=qTT=`CI~XFlBYq78}H?El1Gdv7b;fL@C_)CQYQZ?mdZ +Hj#8JKi#8iyq3k~X0zNEYCbi#MrD>~N5Yz&P@*^=Oz51LPxAvi=15XQfn&gh@nQ={6{7OOSAu&R;>?a +zW{m>iE;EmGre&|R-@2gB&MCT`d3JrVPA<1Xv?v!tgss4Q--FXEW>cAIJ8})ChnWgYPC%&n<6E*}3VU3&h +MIi2z9d!1N)hbwdd(DZ0}_fDhNwqiT+2T)ot4&CX)a`~9e^Q4*K^&3uG^1B-)mSI;?>F=CVF#ypH7Y4vi$xdTVB-Q=Br0L|#ZM?pT%Ex}HSe)=0`DZhzLIUJxe +H>&RVX7fMwgX3{IvYAtZVO|i~UfEPB!t{SHLE|3O|B&aO-91bUWT<_V9>|vZe(MEHizQ@tkfz;ceBR| +XB91Q(sdTJU8Nz?QO%SgQyF;*!N4WXEKh*c!P;Kt|$e{0q>m?hV?QIplmSguwM7rY)>lc82Wit|xsoI +I^rwMJkO-QdG<;;k&qtJR_?iX~p_sKoJ!RxQ{-5zLJ_T54epj3-luBi`0l#6^#0lws&;fBGn;kXibdD=Nd&rel~KN#b`60Fr>W&Fb^_UygejpzQM2xh?ANv2aVZ9DWKfez&7XQ +TjqCNk*nAk4;O9gxme(q`Fgu$R@+PndJ^H@RWo&6;FJobDWNBn!bY*icaCz+Fi{1=`*OE(;Mm!iy$RX#r>M*?hg`yfC|e +#1BJ@YyNYLBs6uqY((8*ORo5kz^Vi{Cp^m=y7%E}K#%WW10ICy^d57L=JX3BspgvKo(rJIyqBdn7{!_L>n2i$KQN9XwX-1 +q=;8!W~d}~g4tUiuLsG_`PbEB*A1#h0x*f=4uNirFYP>?Jpu$UC@jKD_^i<+m|R&$qUc3q@yE;`Z|*_ +%K)^9RpIqXYKvO5_;>Iaw9?vOzO(oi}UNtT{W`o*Xl!i=0I#+h#4wC?35y`p>K57e_CXlb=qXJb#uP| +8Vr|^!V}d(F?#7ZNs>$^3gxkX2l!LQ2C`A{5ArDM;TwSR3>;U$pEOGO+0Hq!>E;hU-;UJqz4alJpT5JKcg;Tj7hw&sLIW(;U$W;inR+YN +x7BM+?&TmaavvA;lZ4NM^KEk#OVuo4O`tc9^bBQ_3px=U(-hD5eiRpdOI+f##Kw|XfyToO>X)QtsDlZ +sd0-2ZC;QjfWHxfD3(9c)JYGtro*VDkJk&6ry!lpCC9pwNz5->RDx?yzuT8tl3##3CKo6pn{`*5Gxyw +-!J5S55-rR{2FDd4ons1{C@ef%>555p@oR6!0LII(GsUOt|%WnH|(hgL8Y8#z^jGP@KI=gIX18jlp<7 +h6YPiia(;1Ly)k^8%uOTL6Ijn!}EVKEl~oU3`=wdp{k-Bf1CBf*H4k`kG1UnKGL!7##MBK*v2-Eps48 +``;OfN8&T}vMZ5jQ4mk6<8rDIBhVXxX@`u+HbIq}Y)KISAa+1Xs?i{6Iz)w&_#*)7He-H7p7axvY+-i +f(_mFuV2R_+IYH#@Tuk%M9wfQS!zRbz^LW2ae;f2){s_^=U8wI=~ETH>dcPT@b +W6JMXAynK!=r_XOcAnwy+g%mTN!@$8!_&#tfrsL>Oxf2x=H#MtI5!vu7~8m@~1B0Q?{T$CC>VIfke&{ +|=vWNq`hL`P)ih;GI^Gl~;96A!DghE)3uSH{1`fMFIM$`7*20I^Af8j2M!Guh6w-X3Gx7_6XWT)O8M- +Eod4ctha2ZjS6s!Bz{K20rU#)lz$fzvp^=pKKh^@K0QB_i +q!0(o>jRsYzV41>o#vX}guV{;mRRYxAu*%yog%5e5DW*kIudRGXavTf$FV}O+EzJ4VHcCwNd9 +P#w^70=6Dx0^e2-Hw-Fg%HF_;9Go;V{Ud9j-M2G|5;ZiUbOa0*p<$g{Z>I*XsNNGI@-n?rXobBSn}^P +U6pn1;FK+M#$mx}}D24|!MoI17l4lWzR#W;{~S!@5tMb*45kH}rMpjkiNf7tSEfFhk{!aK^prhK!E#`r{j9(--x-Nfax46Z$!~ +bgoEH8F`Y#rf3s-D}*-35XW7a=b8e#$&q=u<|cyjaK$y_ad`676NgCv5rPv1Z^{JdYCXdeFTq|2Q8?g +X@qv~apym*-;NYRttyFqc>LWE;T{=8<{tit%>B;>r;4tt8%ZPYc4tl7w4!S +NN=>j795fXguzceiR-C6LOudr&_2vb$kEcO%!tZ1mvJ;k!R(g{n4=~h+9VS+5n096blb>pa6?iph3Cx*hpvg0Ej*CZ?N_ +*uKYdtL@0J#~yG|ApDOI7zKSE9Mm17HzeyAS$c(8y8XSHgTXAGMLWHzFI0%$QJ9Y1BHj!&cEZbd_ldr +5+MA0IbkrYr==A3XkP8=0_G-@n*=E*Ie$~~&J0)2|AEMg&H@#*uyv9dz(xts8__vYu212p`&QN#}YLM +fy>Jz%QAWI?h2*Dk&lUiJ%#<*2z?IG6Xb+M&hD#l37I&HuOwCEx7MuqGhK$QX{Q5hQkwjssPSxj3{zl +CfUgH&H)G6w;+T#M@<%2uER#Q$J;VF#5kbi9uBgg979^%TpEAd15ul8i1l_H;#wKW~>^4xxh-ZBw-kh +9{6TLKdQPQ%oFH!U^$&V3{Zn8j3Ovi_tIVRGaI3J3B-92si?hypX;ACLG2F?Q|`hOTx3U$Hr%2 +UFlC$b!k7ph*T+=%s~uvhY9%Ya9HVB|lc^^q?xgpDl1W9aJ0JAeQP)}JDftC&{4!*)H;niCS=p}WFDS +?aowo7&%QHH_1rKfN#CMZ*?kk-BZqv&keGKwODhDKhyPq{;>snRS6K{U3(f~w1@N7N^&##3tth(cI#u +x3yIC>?~*wMyJ*CiCM@5yND1B}CL!(0lrqr*i(?ho^O`lChA+i}o!&&KN|b(n>Dm6^oYPhn0aHqnGr7 +#4;cP>bj4Rma#Z#+4yY?P`-e^d8oXX*%w2GyPiHH}{}SF~`PVAQi^=;>Jzk2wET-i8{_eix{wTtE-O2 +7to%QqZ9VcSAWxQIoPWgPeV7E+azPRXgG3R;-HV!bea&Ow@s64_sv5cj)!lq$2JW8LE#HTZg0TO_=%`FwmxV|(dVkYVN(Cg02ZD +Bu`8i-u`9%tughEeojsmbuaD~D;g`hzJjNeF8h7Ey)xS+gla_}1%Nu~92onLX9q5c9}p&#r+86;}xoz +k$>cy?C3`s#CkK78%_WJa>f +c3uGhQ7v6nTc-vV8+b?qMc{`s7B>R4D0zt0e7@$^CFXTRFH`cJ$mndJ~sRyz5DY;AD=3}I|TPVk2yMC +{&z0oLEzgJZ!5<3$-}T9JCWV_s;$?x?DS=kAw-zZ8vU}zLyF$ +Y(90w&NZVEty?F9>PRXS!2Gz3+m* +H(|4GmDF5)s{h|P_0#f@F2!oSP8s6tT_rc2~N;^Ka`;im}AokG90xfUv2FTU8kkGZNfg-iO5~^qOhUK +f*`#(LXLT`YtKLvysRZPM>zcyXBoLmG+2)YfXc~xp5lwTxIn-$!Jyg?eCt$d +G+y`_kX;;(>#wjAx3S?R{(i5Zik?V-Tu$7qWUp+27Y_vgIA}X{51VFASU92CF%aTtDyuYe-^Dxw8-Kn(o1r_I2a=#Bw27N*sJD%Hm7dpJwNp*pi{K}*1Sx685sRkmey^Jr- +Ml-yd3WxYOcktX8pUr!uHq7n)%NHNU}sA&#DELgzQ?|GNvV=@`p~a894;k#;vBH=X2Pw3G<~uWwh=EgtEH?1<}=0Dh{~-(;yuyzojvg~;i~&$=y({(@ILQ2il@`=;DHqC@ScZJbW9KG1ht$ +QH5YZmvvDZ99i14=Fl?B0n-qf(@i*O3>SExA*~=Y1R-^-Z4;DXb_TtJ?4ufQ%LiKP*m_v1?(8V0EF@` +uLzWRQ!FaexO5HhjET10u#eNV7Isc~QWbuQ<_6R=LD3?Ii(lZRSEDiX{=*R}bwQH#@YY$ln&MZv-E>V +QmBtd?JZ!9X*(g%C|oIB;MHy(sVs>`IncKkO+)OkY};7=UO^SC``QL><1F?PE`c@^TMg^BsXD|4^_Pd +8&XIvsR_U;ZYq=pWS9@$W(!Ti|@K_L?cycp8kq!9`l&waE)J4Q_Euwj +lLe%=0)=*Y!eF%<>CM*JEl>~gzdsyd*z1sgTLsa|6ayTz+>|zmph83cmExakFQpLILwa>ct7>q038+& +e2wTcz6d1@#-F!d`#(@i0|XQR000O85nWGJc)c|8^cw&G=4=1}E&u=kaA|NaUukZ1WpZv|Y%g+Ub8l> +QbZKvHFLGsbZ)|pDY-wUIW?^G=Z*qAqaCz-LYmei`k>BT6bhz*hWox*0fCDZHbUA0e8!zmv7xpARV?3 +ZKsWqa9A{90@v&!83_p7RYKPYK0!F>vW#3Qo1y1KjGRoz=}cU?cQW!Db;!_d@=i(0-}@7v|jb&b$3n( +pqdZtpJEeYa)9VF!OR^(nt#@Ld1&{&o54>({TodHe29Q+oAPy&U9UZ~E@U;a?7zDB7%X(e&-t_-LzX1%7_1q72V>-R-`Ynw5cfP_km;&- +`*VapVRkrXdhSYP>H4hy2Qv>-P-|TV8-ogtZYFK@D`ZW+h56f9BtL294Ua#=S>vrf5Q}*MA^M+oey73 +Q7PCFues`v_av#%F4^1E($)9u@p+VxfpRnt`X<#pfd?f)|$s#P^qZ(ATr0li_7<(I#-V!uPseD(e?Y` +XTZu(HrO`bwDM_9pPG+ZF$yR+^V<{Ml=a-^;ge=w+(&R^g^Q{IL?;o#2$+@u6Jtb+vDXlD7|a-?agiS +YX|XH==0j_FlvL7XCic^bzN!+tMUcjk_Ik6YfN@>-nzgxupR^w(sSak3tao5--8IS#9PY^E8%;I +-AY8gtlCuxK>s!d30te4K4qx=caT!Nx#7!u=t7)UN<=CES%T2vBfHug>MjQ}hLp`#)#B?z +8?GBc2Q8)E)D2M9K%pdpG(AYSDO(izmbo!cpjkh*D?~c+Z*`BU=~du0}V$|6|@5<;~PSpCbU=hf<<7M-u`5cCQa1p#j-UwbP)P +$4Yy5`T=2KUc7DBoq~|k4Y|U7*VM~;h{)PUHu`KdL-Yj%8_=Uz* +SJ#NSI}Se+Sj`?>XOjWK=?zq?3xS7V@)LlZu1(D9bQ#?!E@bWB4Oa;Omj9q0n;2M3br&Wc5!imW&=pr +xwan)pzm+#U0#y%DodEgD`=n;be_G+CKse<;3ZlRH@Njcl(zs*BM$Vn1TOr(>)e$VxwR71G~AtU%6ZqdN)k-i)qm2@nYRIj*8X#~1O8XN?C@Ft8FY8n4 +jMVXf_8E;R^CA45y_PYGn@pF!abI17|l$88Ga1Z1k_}>(R#UQs;HivcZ>|*37=%bcC7~Fyj=@+NF{&NYh>;2$foIy{#_p5D{xS+6+ZZ-FdhwQgc-ezS^^2u2avXiO95ot2Sa +E(T;xrM(7Luc%->Xv;68eGwZW|Bv-40_>-Ar|9xd$QVB~wi8#zKCnS;{HRi?wfZ_^xhVU-24L0PXQVb +=nTIb(B5i9_(G48PCFDoV&VwV~Li02tKIpQgmd*8?z1lKeUKTi0Ma!gfqtsMM}780D@YErzN`@{ml9i +k{5pd@?yY^j6%VfXSY`QJ+~YXbu%KqbV#wvJ4yex&zq&>$oj4yT(-wCT{2o<}k#>Sz?luA%_LpnW6DG +3yfAKJIw-4vsxx#I6H4*-}0UxV5)h&$`SfR!?PLW+cQ!}M0TTAn@t;Wu_(-K>j7>it=R +i}Yo6|IOt#2LbiW}Y9^?bpuzx(PtFhejH!YaA>%xs5T)r!7-6_BKt8 +M(be#oL~oT4ONDP)C0Jr{@sZ(y0-c@lrO80bFROlqq;a!o_MB0K3s#KZej;vO-p<&~fj3R}h(Cc~`EZ ++!Pw@CkA6XfaCiACkTlM$*T{dIa{hmLcyFmD)xXTa=Pj*QXK0XNp#Tp4m3G;~co8*_IkM*&=s9$d3Vr*@NOjSe)>d{WDsPwZxZ4FtwaJXZ{9H}n_9hIEKmfEC1sj@{J{WDHdf +piIk#tYBaBp0h{Je%*_KEjL{UG=f&sy6)kH6E8+!J>Ma(I6>|g3?pJ4Sz6n|dHr>_wA1d#?rPHWQ3uRrNQ?weQv>Nd@+WGi*@t4mD-gMydSLP1K+1Cvv~_fWQA@rB|F&`C%O +QXUcYDm{`r4cZy9Brbk+QPa_)jIWDMYhNsS&ny#Tr5#DXwpgZSe#6PvDPQmZ`S|7{i?&9i@-8*n!N?z +L6mV_;yvU9+nziZ@-^h%S&I;6Ft^c&u7EkOHByM_2@a3m-s30ATq~ndq={2J8XqYn5RK%rujwzoEpF +#?3e6K?Ed2V6pBSlj=!B!w=XcJaUTT-T02T+}0z`_c1iI>#|q!!|sI}ip2TWgD{J0oS}6+1mK*l1rpl +9s)DdTu)Dt6ZK5?q5Qe@nT|a=!ByxYgg|<&nw)ec@pp3*EHS*;AdB!2A)8QKZ;M=*6Q)~)I{+Y}F#Gk +b=ny2l%NwA979?JkdR_wm*9$QCyQEI<%wyz$d^{rRAfifZS6)L~87cd?8qUGU892CX|PIHzNZ9S1&eF +|3VO`MFr4OOVvHH$ZX|{2d=pp}@jijNX)&taAlXR7WkA2$V +g4Kqh>>h?~+1-~~{eOwxm{-T`$2iD>rBAL@!-&gYj5 +WG(!6emMb1)j%425efj&H5_25{5*`kH~_4Jhn!DYn444I=lCPXg(!5KUVS00i31|p;X(xqL1ZIJK)uB +feA?biWFYi|@Wqb&JJUC5SEH=nse&TtGoP~zmLy9z#z&DlN|F3GsFo$Yc1cZ)6$x`yn?1O|^JaVj6a{{0b)&MG!hUyx +rdrJYPpSm#x=8)qDjrKNAj0(TyOX|j2ohUyU6?VKZokievQ0ZEnOYQR_-hM!ecHj)oS_0I>wVs`?<$r +`@ZH(8QEYbgNwG>FS*eMRznMG15*eAUCI`iM2>j{%%ciOof!HIh`rjT=HV77DO&_QO9ZaUbo&CWmOz& +~eNemI?EyS^Cm3z-h?h6j16PYsgjndcL1ReyEzqlO$t49RM-Fc$?v8euHvXJ3To(g{kn->1nR}3$uDj +pwh$g121TlfG6Vj%oUfColCBTTJ?TQ8+rMgj+zN1i$oI??@SyD`4^5PQK4Gr|&4i;0bj`exK=pLkXLs +C&}x;=1f)jU=Q5r6{hLEZZiSoDIXjt$cDOSCT*`#XxZKH?i6S5u{k7C~6Fm^*X#*G2ixwztZcQ{5BG+ +D=amNA!I{DS%Ezt<+YHgswu~c53KGqgZBXXSn|MgY7$WOHZR<$cc3AKg{pW92~~JrsP)7sa>{)^~zE~ +scwPHC8V{891ClRK^3qMmksQRC7uq;D4F7~z)QLP +h8Pb&jH!2t5WUUE)nWQDcQFqW!)aoa#w3Whk;zviEz~e>egs)A8CjgO7k0V}|HWPicqPHc5`iVr9S%% +njl3bT4e;^O-j3DqB`}o>M9h#9vXGs-$n~Lb7Ph9h4f1jmp&*$5t0p-)d;upHkSF(~)0}O4Pek()2Mr +^xlV0j~9iO86>SA6G(ELE(XDuokqc4&Wd>oJ^Ce=Y_$cCP{-UCLAniGGX*3FErI;FY=xUV`xDGJdLjcg%S`JGqi!`VOZFiLBuke1Cj;E;L<40J-?&A?NpqIyZQ&fJ+@<~Em;bVnqqOVfv3120YZq7D ++ncm1)b7FZ27GAPdCvzRLDg`tmXhjF0ipn9OO+g{n%Z<&p`1UWA0jh!%WA6bGI>5{T(AdD_m+;%5Muj +C3%2m?xojUL&K7jlNW{NRgR3|+aAhyyWCkM?lqEn`_;~JJgm_Gm#lx~#g1Q0zhsUxOgVA|}=l60H{OU5ACwpwupWV^gabilmC-aV2xv7Pi{@(H53_lQxA +gYG*PG-HT+LMm+O2e_3jK%&R>hd|mq2WkN9D6W#BKF?BCi9F_zJ*ILsz@HZPJ_oCF7!sv4H?@w-=W6x +9d3MtBClc)jHpz^X6REA}BVli+RVwt2&x!C#xId$USQD9VRYZY7+R?;9MV0{3fU5R!5%7IYus)nQf~R +0Y-sR!|5Te!?+D7vxZhDT*WZ^{xp-vYm>>P(6Ea9$4jzLYCImK1#NQtpT{eo_jsC8|?WIaPc`TP>=US +OZ|aD+)14LMTNlfA`M1~T*~fSH2GEQ`IV-ELfZoLi()}6_9xSxrgs9vi-H9KscRK) +0^Rjk9kiU442YSjzkL6#SKy-Rg=M7}R^A5ioYI2|FC+jRD{FY!mo4ZA&QrEXk66Qada*)aD|6dR7Ba`V`_ph1$wafYyOQ+zmfem +Flyt(N3mWtx`2Hf$)JO(U_F(tR)cuJc^1FvP*A!3oFP%H+}0zs_+R-2_C$_+jCj+bMf`#{UHsOH)&{# +M}KALm)i2I0m9z^R*KVN!XaLL8!^PGcwYuC0V;|2oypfdaXtL0){gTdXW@AW3ppZn!qRyk0YnFnOsAD&ZjVh8T+@s$k_$QjmG9q0<7Q1p$S@k*4=1vT +)kfsgVH1Wo!!OM`6qYuxsl)M#+w3-9lvu5V07;SRNRir5$Ws*xWLLi#2e9T2$Gsc2$ET`X~ +0lZ+)Y|;#SlN8((+x!ZRvF=53NL_|ZMAL_8%x1G#9U$? +BC$Y*ie>IR|yLVEKeGjwdE3sdG~!CV9jN$piDYLSyQnoK|~h@Ds64G?S49*5}~8u;b}vj;p!PbWx5S( +odT1p0OVs(V|Qk#nRj&MO2&U?QeI(!Q5_eC9gM#9C+AOr$FX32I`##8(l!+?q&MQ1!p5L-0R%cP-5iy +5Hg-~mM}d1H{u`Ko3i|*9JF%Lo;r;N%*?9RB4Oecgl>G^LCw{PGJ5QY=lHDmPG9o;t9 +cf(pLGYhC1Pb#xlz@s!9W0~YUeqXgwt))K7h^_o*aUk+$Mfzhj~)a!-|82qtqx@v`$YF?=@EMAZ1H_G +T?gVp$(zIz0Vzj4k?AZ%0q&PBoA?0Y1vEgc#3uw~W2bU+v}G%!HxuQh2Hb$mAakq+vB+xE0){>*}1{z1El~7|@9bX=kqGgTU_d5z^VXR-U=``Gamf{+JrRz5^;oLALR$3-H5#tvvkxo?X^wckYF3dEXt|x@(4YxG +qDICXt#xd{!!+Hadzp9XTIQG%W7k{Sl~*pagbDs~U}ve_mgu1p#f%rFv2Y~I7`oPdK4L8Cs#O1e0N&< +u;0e}&e-V@une$h3vhHNdo6m1uXQ^7VWSzsfOTQpEIL-75B^Citu!$R~ +ptz&lDgCZv|!Doa78mz|uMiqIH)2rFATLT=`lcPYMUHD#sD1B#5mSG`cOUIq_bDI%Sm-n@_5xX*BMKe +yg0vRo@rOi?~5%$_G%$XsWxrOSB&gjeG`WJ|?WQDhM_Kwj!cUs9Mjf@gU#b!)BXQb{Q3$OihEAdGfuI +la(89J_H^ud^rk1xrU}^^-&9xNg?N282cOm=AiRPLdM?8flA2+*9>yxi};)ZS&xUQcm{-FHqn`Ta!?% +AZF+!X*yH#m(`d`S9xx0zmP(^5O7Q$7o_|MOYH+1$%@jMT)riG +NW^Q@;L?kk9+3#6a{MyxRK0XIZ8f2I{&&D!uM<)h41kDP0iV%~nMx@hpbi!4HS$V)Xm{2B3O)m$|X%g +V1>C=CkP4zT&-{hn`8(cF3og?ysXv2pg`=!1#=Chbb6 +7DtESDufK%TPZ03^bspb#zIpvf*{toDLXfHl9o>M!L@I}SqlL0eOF?v2dN6oo~|1Vj>w(3G^w*V(&HC +W;X&??|dVS&bY>@+J%1LDwctS~XUh&Cm#m(_A;6#I*u8bHjwE?p$@tL_(^4GKVT42C +j(B^@m8Ef(BcmOS~-$0s=?SQbFV=UOFA>F#{pPqRlg-r!bJ>T$_WLmXAKM~`nf>90)naM$(=_w1Zz)* +r4oicey;Fw(s`o{p|@^#ElU~1^?s?|F-AFOynKA!8>mT@GWL1t8Vt$9wl*d}gLMn>fRdh6Oh+5!(O3)*yZ*8njr?P}X+tiX? +%^J0RNjTA>6Cv#(RF2W%?I)e +0GnSD!$>1;f9$x$B-&b6Km9yX_=qI`sJi0}E1aXo)8ZDy2%-5v^a|103H4**xhA?rur6Sz!WfqOl*gl6PmZ~Os&l5zbxI)`T +hXynl@oc-KNwJyhCbXg=I +h@pX5=9;q&f2gFy)l&N(B2}XePHTdAWFmodIfM34{_CmpX@%^lMbW3G2?@5aXGm`CZ0jg;RvBSJy5ggt=z3!|{vW*By2_VOkEb2huft}5#8C +2wD1Nv#-O4&AO^ikEg8SN5Ovc^N=@vEmh`bgs~$yK=hdnG1K~{|it{0|XQR000O85nWGJDb-rPyaoUO +NfrPAHvj+taA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)|pDY-wUIW^Z+FWM5-pZe(d>VRU74E^v9 +}SWR!+Mi9O8S1go6q(C7Mm!%75uag|ztkY)fYMOk +~2u{`+l*1t-U>4psX_*m+;g+b1h%I+)mf&YNHah`hScSMtcOut! +bfpmGbH~ZkW-cx&R-39hw7I#stSd!PP*}+-PPSC$+Qe0w-KIBeXKQ?OPx2*7_yHJE=%aURj57JDtphk^tX}If_-h3dMKYD&1A|));LT;_2%*XFsF +To(jGC6J~Oy1;a`_*`35u&-&Z@lX@g1} +Iga(R_#KT8Cs%9{1q4UZ2mtWP2HKOhX(X>9t7%C7>-Ow8_DL%m|IO{=F$Xn|RV*aJ7!AW{Id-a?@G33 +HEN=a$+*!TSDag@BBgqb+ugX>+huam1%HySyOkI4N}G0n_C;LqqgxLg!dV3J4q_DEbQr~+Y8KJ{9b%*7E1ivA-A8M53q(Y4UY;F~{efb_hxJ{rfIJI?!yOYUxUbz{<;V%$T +$x8n?SZPKer75beGF^6O!jdh`k4F`iqf$H__ftA~RMipq{R#w%}#SUZkfeINxJm6sL^)ayjyVuu<|MO +KB0m4DX{f}#)!!>Z(fOfAUTJ9ZE>=3o7VZE+4CFRHA7Q~^@SdRv7zlPNEu +UN~G?(I_&NvO6DxP#%o%=))(>>;B9=Un4XoeCDl@P?(;B|9<)hK8K$Gj@^Pq|H1m<;;+5dYAXd0N;vh +hdAOosS+1lypbt3vNSC#Ch?_pz#<&4!+J?VsU}(;^EZ?)FDn!S|i4{Kg3Rt6zPHD*_T8d$3!pS342fZ +AX-8g9}FQ+LyzQgKax@8r)9CnAJc=uaMm%;>#)MvcH9PKR7P(lC}X&@=@G#Ui?(!rOaK)%6C7j%VT1| +RbIEl9Hc2)qHd>f7yXFOPsIcwJZP9l%&qAGTn_4*uju1|6xCXJ!hJ7&2GP1Tw0a(kctJogTK?Vc0>!w +T_sxou6U!r-JxP}D;p!U(POrz-($DO}$b<^tBQ;)SuyEh@74JXz9)qSLU6M?@BwA9%y!&=TE@ +@iIXnA|AzbyO52R-V6b+4CeIz%#%UtvogDrz@5MTyL9ASh(JQ8HVM9Eg+7aMWV=c~7Pf(8&Y!A7lZu9&A|f1Q4Cww(tr2OMsME?Yez7 +Xll_OrbN5ewF>`utSEHy@I+gHMv(MWF)f;26x0m=mjUoBh+)=Ku}P&R(48CD?HAJNB-iCrhXvdh=43V +kB~Lyf9*++z0UIIBdx;;juitdXSs(LnH^-bDZl%2@Qqc7YWE`V!f1BAeshQt~_gOk#Z#N)*PHn@8)69 +4B(7n4k4$PlVvsugb4`X_iE{r3?Y@sZ_BE){z{ihczQzuypho+nK-pY@3@?tg!6I0qSPW*i|FMWz~yt +j{mx5?+F8O#sOSz7MGjnThQO9KQH0000801;hJRc90sJ|PVN0JRWo&6;FK}{ic4=f~axQRromyLS%m!-gibH8)tuBlba^13WV-pabtBWaIzyxor0s@}2o*vM+b-1&I +K@b~cR-F5c%hwHaL-rRmaHAnAc-rA>|Rw%wIMewVwmHqV-ZwQ&_UqmTaX`|{FSqP;&?ygq4RXlI++zWR-U2!csn76ztWWif8Wp5!c;APcL**%z%O6 +)|{x=VQn*rUI{{^u_@_t)>UyI&uE`1v-wd3SyLaP!^G^}V~Y2JKoMJNWPH&(TP(W0lQ~kiSc`J0;dak +-9#4*FE~XfowO`x>hhQ)H^bWJl^0HTU@yF7#vT%DxWN_tke6qr0>OvIWF9uGJrjeM5)F6v+#^YZ`f^3 +h5@VXRxl+>!=B#ChOI<<5W>Fb)HpT2oOz}Ul<5FydLx@N<@sZI$+Z?b9lZhALx77(%{VInm$#(h(!g8 +2)oaI3XAFxbMHpzN>XJy}dRwysZ($`<#`c988BVN1kYQ63q08e& +NxLSke}dJz+nw}zz~FvE37t*C{bl0UI6UJ#H1;!X!%O3DOa1y*SfHOW#44(T485D-5wu_51EnboWRkV +K3t0qNJW5(SgiOcbBQoFzA9t9{D^9zl?o5O2pD7*8rZTD{`|I;5*a?uC~`t}_rY?L@n+3(|YV8m?NI? +@QzswglNCgD>Y0Vrdv$N}Xv~Pp_pYvej`(3<5>y3l5!ioaIKeSs@z49l}Nu9^(<-l`OdToIWjR!!q)X +P&gAL19bj{wnr5&aW#jLrQR8zp&J^mB}!;x3T#9lXRr67Lbb=vD?9x-%tl7 +tAZjGCHA9SBykz4~TG6V)VI)*qs27nGcSXtN4`=+C9Lz8=(c&e$*k`G&0P<%wZ;_61`WY9{80`@#?UEXTBf{;B0w^Ov+1L8$v{eGlb&a@&;CuycBR|2(WJw8?6SXRig=oKsip=)>P||Rj}1r*1V-Z5y4zdNpo;8QX9$?XDitv(I}AyZir-4Wh+#nX +93k@h~2=tCJB=hjgvty8h-%NA{j2w +3OCTV-T+U`yJp;U6E@M>tW}|o^GGewBo1=*obARUVJ0R8p4Nw3$>(d44LBooqgIJy*S8ChlbgcGT(EF +w-i6Uv8x)3B52}L4k|LB_}omYsjSu$?-(xc9LL5V!cH!$Ro3@ub64vx%rwR%;N>Ivoxq$rC)H`vrUR6j%2hqdKmOKp5?&9`{8u}+|A>U{#NMhe$+d64}Icea&}@&LM$t +UJdGpI&<%_a7P?XY;_V;`0+7V(o)WgIi#^ICM-4@RMMq@v#RhX!WE3+_6f_%p^}7U4f0XFT`ruNgv#I +G@Q1|VixPyJ%WkqccwFetjqr<7!LF(S@i5&8>4$!~d<4#5G7bP+ZNIS=y0TjMmA52GwSyAD9?k|4nW^ +pQvyz0Al6Cj~@M2{u5$xEh8q??pAwMhWfk5xTFM(sr@bJXfz?*EYngl^U}H}P`ngzQL$N+cpMKc+Mgk +*P?#7EuQTXuu$ELaA9S7Rx1bk6pPLl0#9TNz9Df4DO}sk8(#p6O#cFH-($B8_68d%*&d}oSXj!B`ea? +S}NU!jQmV0fAxy1)W$aSbTk#Z2YyY7&=>07Y>nY~hXQ()gRZ2OmPbHasHJ;e!h-e*cEWYPhbi!_`mJCCyqR1HvQSu|TJ)!zdMS@3DOSgi^w33b2ndE=q*!Wo}q}ia+!cl0u>1+jk=-@E +qT7I;dUTa9nO8Jc5f@22ViW!#*$?xz8=AM#R%2JS@8W)8A6T3N099vN5-fXd2k#8$W>hs(f(K6E^4*f +FGy|hW_iLT8vA!PbvGIifGn?-`%0&)nkYs@;LpPRuNy6|0q&Z^68=2<_=!Przbq_$s2?6%H+1(7A}>} +GMpK@qyP*~fckE_L-?rja@uyonOca2A+^smrw9aVa1_8wC6`0`Ll-o?Nu0`1IV?-qZ5|bJJJyb^LCSb +x$$dqCG(9)zY5_Q)~EX;?hdvmFdv;o0lfOV9t$azA($l-!UR58WgmjgS+{jNn35+VY|+jtSg*N{SJ2* +$(3P^H7Psn#2l~dBL(B+QN7HYjJKm%j_mrTtimFKSX;%;U3qqE3oW^2~=_9DC4T$`@`l2=Ul=&|%#Nc +u^b3~Tt{`dw#DU82I$WRN0YNEW9wDrt>9y`vcgd}Vyc;%8ufH{_u+k(;SDws8#S>wo9U)(k!jS9X&GL +9X?`n<^>4{9Gv0)KlIABi!O>zV!Nat5*Kow|1F^4F%}-?_$XdKU6rCJ6uBo@+rZ?kU9Nk|qMa&rKi!< +aZqHsdpy7HvBAsPd1OA&U2wue$ASqZnwwrB+Q2cPNOYoYI3CCZH+I`U0h-zOp}XfMvuU}Xbb-sgSeSL +>-wi+F(!q7D%njY!y+?CzKfHSv&CZY(fV}LIcL#YGgx}|dHPJwJ>77oLrOiSNxbR|cgT*Ni$wc%U|5n +o7smOzCIB93Qpu$)_K^2SJL;R+-LGaPs#MHkL}WrKD2f_4;QyUIzsuZy(GRsh?0GJh=>KOM>Z@yP9uj +QWhC$2|3d7_Y_oV~yG_*F&+Ml0k<@yAf8@SDXRCtwYFx-D0!6<{u0001RX>c!JX>N37a&BR4FLGsbZ)|mRX>V>Xa%FRGY<6XAX<{#OWpHnDbY*fbaCzlc-D}%05 +P#2KaTp3Q^&HAxh9QHlWl$L1==L&7F^YXoL~Y5F&gnww|GtyIP294MzRi@7NWS04>F(6b=mHX*=a`Vx +%7QFPZ3t4#M3qx26Ix{Mom_9XQf<5aEz%cWLDTHgp&V_Vt3v>n4Z@{EXkIF18OD25sWzcOtMfhPa>HL +;5SpI*ldyQxF1DfJhS$3N3!ze(3c>(x@K+@b7O0pcuLZ$+5VwqDB?^pVf3a93xv&<#$+E*(>tod!Jhme1lso}x@Q$%kgqZX_d^vnM#o?Fof@ +jup=sMv|73DE)S&$rCvP+lrf6B@B0!!GUc(2t`hS*tl*dmkMSK*dR9vT#(7(6fU +06>(ioddIP>?Llh`p$T1-g=&Q4&4pl3Y@PVjTX4jo +e(Fgt{dcXJ-AQKjwH5)Hssjhq%8?U-6_wU=fqtnBJ87l&p*bB4QD8A>qc_({Epl_e>HlGb7Fk2#Tk)t +Q$9}>I4!PJ`AYq3+6m%`X)&JXZdw1CsWsNH4B#SdWDG;HB^s?n;~ShiT$p(R1ONa}6#xJ;0001RX>c!JX>N37a&B +R4FLGsbZ)|mRX>V>Xa%FRGY<6XAX<{#OWpQcP4-gC!$dG3^}vVb*T$H^VatW@!ur8n%F% +j;OLG37Z&rGXdgQW^NTU-^h;lAo5W3>U4?hf)N95wcsf?-odH&9L^^ +|pA2(I$U1J4WU5i*?qap7d8ajm*)QY)iM5pd*u!yq78ae{#6#9bHNXyn{&oloO{Jt=oA$v%A3@>zQHW +sc?(7_}n{Hd6S85jqaqXRHD`Y?2`GwP+B=t%{Pj)p)qW<|k+6C +TUM5!c|QNJ}9S06h$ueJ8Z5K%?#H>Az8BAJXSZ$vJ?jzyvkV&)U_qt^3=0ym=4^|kFd;y +T;9;_+=&Q+PZUl2mx0}gK$eD{TRsi?T##4@;sN_J*ugJGF-?2KabHntG*hRqUJw5*VK|MJUSCQWs`Jo +zl;MFcj+i_;b2NeMtDz{iSu|TxXQM)Pak60*_iUr#;@t)OboO&_Bt!cRZg2xv$tP`pfKnQKuW8lD{&k +~X=7dis43D?omh#RzBrS`A%`~o=qT$E%;sn@L5CGkjv-qc;c!?mnlCv%@k^5A_lAChK +W`S`XjEzR1<*=Fjq2m}L^ZC~dO?lf#$t-HWum@aw#aXlL}%Ta9h96C5S0Mxxk9JC|ah|^LUMnnFz3ki{r=B12$w=O=Z%5rAY;G52@FE+#$^5HHY1i_nLHrPIgnw~7H? +2%?Q6fuh-E;1}8F~2$XriAuN^WG~_M097;)f2}5jtm!Y=%3nw{wspReP*}MgXT)xzRF$j!lvIMjo0#9 +$gbhHOXD|ue^yOK8~9J4zG!UUcb^wCIk7?Ct6kv@4L)6dM;p~Zycziihw2V8;>*Cpy<^~h;xaoMH099 +l1^)+f_oH+q_|Ib9zhx6JJF7jDbj^yna9Kl_2VA){BKa;_oAW(_nZlhlW$N<0|XQR000O85nWGJz7g56W)1)V4J`lwF8 +}}laA|NaUukZ1WpZv|Y%g+Ub8l>QbZKvHFLGsbZ)|pDY-wUIa%FRGY<6XGE^v9ZT5XTpHWL2sU%@&!o +LzXeX@TMZ<5ZyCq`ja)8*I`RhrNZCmS~$;SyV-8H@+bM{bq(QB4v4#do$2xO_9Ul%rno-P?m>Qb%t&G +rZh^`dM52at#&)v?EF`yXWLF4m^rpE%G~I?zrT4|T>b6k)laW}{&B@#$kMFXYpLP?zqCfGhSw|hR>0S +HeOrqaE4el^E2))jRy>HNQeD>a@}A>%S#M;mz4($hs*xqHq(mxpn +mJS=qL9cjI$%-4XVkhR!wj4sWJNmgl*srsh+@=A!rp=y$toyUm5$yZg1kl6ZGZNy)^Dul9skMsLg($e +4&1Og+V36GNNVXH^HH|F_3gx1JgU`yEYaN1Z5&L&()i1!q>QPh$2!;)IQfQF+VJ-mj-F(I7s;Q3`a(m +GTQHcsijJnW(cfCDc;KTVxdxlDESplw;rYNk3@S{b&U4?HhKDzZ(XbjVA0It)}8y-JmEEg2s>p+AzLx +kTBQxE)J__!({Ox91JdRWzKQNVv)p{0Q>HbC6NL%C~ICyGk6;BV2CV6ih(+`ghZ^SrY<=%PKEy=um@w +yG;}k`pt2+_$@qSHe5)xJGP5ral&kJg^Puu~m_wC|BG_1Xh-1x3}Q$pH(C5QU?oiT@S>zSZ2Yg%kT(L +B>>K26_{+u;n@VLW(g!hkmWwU2FMHu$hRZU#~8}n6WmOBJRc9F(<3b9(ckH$1P^)gEuA=WSw8xnP9CE +q9}i4UAAu&H9v+!2#X>&%K1^PwPMUx^IBa4xU3LPC`~)fK0?-~wFT$v0K@ZJ=U>u{4_=R;!88iVzB9= +!tr$K%T#nE1r_XVJuQbSdcyIt&B?6Zq+N6B-DhEjn`u;2ilVgzUILE&}qtMwviwTuYR<-dz8t4KO5xu+D>CSu{F{oY-OcIscA;KXO|>>EDxJ>BtU3gI1A#>4PcIL57{bdG)5ae);y7*T22IE +?&I+{?{MpNGnQbqIr-V&}6ckuMO5oVQS5LQXWJM2A&twlI+@vB|e+4ql)#(Jni$jO}--%8|R!@h1fRX +PtzS!g<+sDs^AYwR>e>`!I%PKyoI7iELTsM<80(DQ_FJ+C8$-N%0@AZz-3 +GLd_+KtY1Yc8hll+j^I(5xPk;er{hS?miydb6z-x%Z#WFQ4?E43(5EjNpjk#@+9t`4^R!U8Dy`C^tV` +S4u=f%%6c7>EvnG1R-gz4|3gQ7K(@j8Zgo_J!X7)H9A0|5yhTh2Z +UiEb~R3B7p0qv!sRD+Jm0mo(Mw5Fg7O}rSylhr|e^Z{rZ9(=xf@49|MyVQc2t6bO@F;3m!Ib{i*c&QHPZ=Bu9P47?O7p_ofe4GhEelGNIn!;cVs{g~Wwdx +T@wQTMwFejg&aklb5IK5>9T6%eTs`D8|a6973)BWt8$Uo3gm%O%p0otZgKITr1gCE<8XIMv{<{JN*|4 +GN>7C^urrP&Xx=8*^)569-`<+=KyvU+ChS3MPx;A$E|Hhn@^0tZE8?Bvc*vtBSV0^^j1{bxBjI-Ljbl +NetURRXYE4(5pg`rqJlT=1P&h)|nPgJ{g}0pT8DN8a2+%YO#a000eE2GO)P)C>Tx=Hk1zSFc_{hr&Bx +VL-u({o2S$36%m$o&oqeU_Q)Q-YxV( +g@kRGBlE3iS{^0)jcup8M>;u~J$2pI@gA!o305>mF0J~~3<5fksfmKqxVazO((o +<9Y@7AQH-l(_tRf4~K@qbWkr2FXN0Nv#xZGbyr`mXVt4UPRt^{%?4F?M~vUA7)yag&*_dq3Cx58*hU& +wmt1E|ftAfCu)APk`k?x>YvV&4x1`M_}{=W4VaNNQtBXbYUMPVTENb&?`XP_S~AOAz@3CflP?Ay{KEr +@QvW8{?488NzEaS8RsDY*rSlUy}@MUUa)AJ_G+j`f(UQ~l)cGK1mqw{@N5IB+@mnw99e +lRVHvswO+uS7ZiaiwZX=-)>yE5d*o|fEcR@FTD16rgyEaDPu{NqVnB&O6!ipX4mob}w*I;XPFB&MZyx +*YsFzicu{^c+si@`O%BTc$&D(;QFZe{aBkC;Jn^)2@jYMM@)`QRh$QDB7+&KR6&ig^Z7ZEFCV)M9I*L +T|;i0ynr?hMFEM3CNw``k-T0MP5^CEX!lCfshqoim*XoM%mX6F`eNz|%NvmTVWgL0<$B{ +LITF6a@w_)>uvswun$44Uoj2i<6z`buaw0l^freb;J7Ib{BT~fo#_1~1DUc#y_B*ZRt_f}g;0Rvs0V@ +?ioNM9_wpWjAj(0e7A{vM_&m}}!BLX?_BbGDG4tUQ{=u$-b$ldk^$qjh`BeCtI>PQygQatwzBV`9+5aa@rM +c!2G3r@*>5>|iFwfX@}R0zAOhRMx{;(a;1 +kx(&nY@|C@;_O8T<+NMBXEpq@vvvX`si_Ie-hAm_!GM68gBcGUkr}`aa3OumcUpP<+-j#d*FyO--#wM +S8z&jlX>hgQv8+rA~RiJ^h)YU|8a=dRdicbQKi!7>WflwsANUGe9yw-}u*~i%LLP$bEPTOYe6^e(0l@ +OOJv^V88sLJrA`9%HjXe|YH__Lv+K${-#+|ZI_Egx&>XHG`6|!8HQX(ZQe@E%ITAyV>eBB+r!X|Gl +&_U7bD4b1Hn%><}a;`{Y(q)v8e@C)0-p(!w{~6Lfl{8pNU;#3ffQK{)U84l%0JM=}wpsz8R8X~j5oQr +Ipx7t9N0-1j88x&)4M^(>FThSu)wbuS8Ou~Y_(4I9Vz)T40Tvc?%{%H0nmHMsU+e|4yKIJ_u< +|rpXh?txIsWVC&jZMp5Y;8`wFn4*#_q`CclIY@{&w$u3qWhIN`d&%_%5ATv{`wEJpdP*L7le(ux3oF* +O!+&An1ORmFjTW9$S9d%Jx!f-3xvB)$?z@`O7Sz^HBmADXD;^WQ{ZrJJ+(52DY^*H7@Vioh(OT@iREq +nJf-E4?aLZfZpg$!`43h)k8DQ|<3L*Rf5m=2Z3H-*sO1uYa@b-ODN4)vM$DbAU_?d +L54`dW|8=^is(=~BUbOKu{M1gy-@gQB17&kPZI)ep}qcxlu7~;3UwWA`($J-sE_7342y2=VkdSi9PQp +P3uBa40zMyoBuqJV*scq7fx{z8FDxa9r-4Vu)TaSmNM5m<2oSJ_te$rV-$|IPqPCmXr`scXfuVZvhUU +5jGZ;35KsxvS{!naknS5jpX$?U`!5Y|n!OYx{dvOuJJ*Lv+9I`ebHb+4d*;y%RnQUC&RzKY?_6Z5@cJ +ggejVDrkIHc!JX>N37a&BR4FLiWjY;!MPUukY>bY +EXCaCrj&P)h>@6aWAK2mldXPgP2IIX~6`008#`000{R003}la4%nJZggdGZeeUMb#!TLb1z?PZ)YxWd +2N!xPUA2ThVOogQTCElsU;3vD(y-O6oiC|1X`|X+Dubx9XqleP23;XaZ+@pcLtgVHEHsFp3+jz_0(d@LvoO51mp+k4n4QEU!;i^$EFQ6kO*dgp|DY2m +)$sp9~1e(MQbh;TNWpu~dg+~d7x9FZ?CYEIx33t6VZel$c0(7UI_AvhXtxhFrSjX6pvl2k!JIcVnT`o +uLE$G4ZgQJ%_bRUQc?$$Sd9tf?0$IbmSdt1NUJio5tCtO`K#-a&tF*<_f2{j&z6$4sXau(us35|EvbJ +j3s_gKq-#X?NVCG14w83Ihe;z>HbC&eU{Ta0-tjK$*b$8;=U`3p8$VdV77pc{+=F +Tn{ZA31{DsPrXLkPANX)R|{Ly_mhMo94+AZm9aNW@h6qE7(6!O9KQH0000801;hJRReND!j}R70D%So03HAU0B~t=FJE +bHbY*gGVQepTbZKmJFJW+SWNC79E^v9BR84Q&FbuuxR}k){#!?$w4+Dawz%D!Ww(e8}24mB*iqt1iXcC4^LrbJ0e0^NBz(xw9`FL@npo%S!7p0Se5mBXUPrr9sxiOA}%8Zvug +&tQ>xp|C4}q+>tCLE(&*7|DvICkOIzP9|+aUSn5Lk*G+xE8SY-JQ$wec+aYIrUkzricO#IHG4H42$>` +s1)5K7gI+kdgH*_nO|mJa3M!>Kxh%)LrcAzG%VCEtErGp@;pQ$pmkQ)Ji8{lR*MW;L73_U&-0B-POz~ +7FYcV&RjVRNVy1J;h0B5ijVoTT<)4&QITu-N6T)__}_?3ROw$V8bGy2}!z%&)|3((~-1IbOfH*OEK6L +~lp(Bgqw(w=fC(BqpF4t=erXFMd6N`{k=GSM9H;WZxHJQ6H?RY$#}%&Y5nH;sM@M3PaAvH6WSgi7oiK +la;%$Sg!=Q|r+SBAUCHH9S?^RpPh6?1!|EtB=RI@qo}&s~5iTRkf;h?zjT!C90%RiiQhRTCnWmNYb-$4*wyE7 +)43keav#hH0xamKkI)N@$n$YQ05&MpcPB%F!R+gzV#@tU}VQ0U#PRT-=Fc19E8 +B_r&lP9wbzH6)#=^MKEOS-3EUg=BwHL(@_AT#TWXr**b*Z=Vc8=7da^o`jo|}4MgbMA)o)Ns0|XQR00 +0O85nWGJ4;ZV{dJ6VRSBVd0kc8j@vd6eb-kEv +=5d6N8V#XBNJcHQSLhyaozE;LRN-QFvpVfU>xZjXbW{d#10jd6B3;6i+?N4sUE2%U>Cp&{hbh6QCJ1yap?$Ce3e{*atEWN8ot(89eZ< +#T{vec@9mbYnkKv^iAc3Kd~yOfv{V;;+G_Vju4^tj$`SHWxuc75RCU6Pi##R8}j9xRtOq~OZo{-1*vc +WYvR4AS8JL7^eg7D^?AI#OjC!R4zt1E{TS&0;>UxUIW=nyY5s_AQ$el6l(+G5_`M-3@6V?~D^irr+J& +zQ4V_3;v%3%dsG7aRX&1b0-p~EppR3wRH$qos^bSGIai7`L~mpckq{&-YkgI)T$E&AQ5OgQcn++0(NB +mfM%f?H{@fp4OM_$jR7=Jd^Vy3r6Ff>VquTOFC#K8x#C_q%vzl&L+aaP>&?f_YPH$L(=#t+K^jyhRV_ +--{><$*+4I9@z4{FQh23a5J=<}Cu42KuG?v+U$f!K@F9Mr!=wKptS|OhYZ9=gN^8OhN0RC_{lNC>fLHcc)=3$Eb8)|4>Et)q +aUnbnLl?SCU7)c|;^}3Nj$o)iZzz}il~w2`sjU&hNDDe{SRYv7k{}D3q^>O*w5(v3g&GZ7cft2a{&J$ +06VKF5lfev&NeZF2C<09?%X8lqxi=+o7idv8T$0hA(F~*RB3Eu=r$Xf+Y|{2`-!EQhCR2!#`+@4`3Ju +OLlaTi0>&whx>fipdxV{*c*@0=3OjgT39v-)!CevxaP$JW(hXUvhACR)1yfy3}Gb2izJ|Pe0zJa(_GHj#afuR6|E +*&#=deqi9IVCaqjHhmLB$rp<{vP67@Ys7K&AAuIf0UNQ@jp;Y0|XQR000O85nWGJwzP?{a|Qqa0TloM +DF6TfaA|NaUukZ1WpZv|Y%g_mX>4;ZV{dJ6VRUI?X>4h9d0%v4XLBxad9_$=Z`(Ey{_bCKQ$9>W9VJf +Nv^C%k+b|3ThPBv=een#Hk!YKZBnl#B#|ZM@cSq`lw4Jwg%LhlM$cJ~&J$F1h2!daE$!n%Lurh@(EsDyoR4;Q86v9L@x9WKC_jIM?nybMxv->t)b?lWi1QPyGoQRXh(k&N{``VrG+e6K8DXtmG +hCES&1r6HoVMa9ak*9W-DA6Yeeo=has75OGoXGD~m* +Hz8&GbvfY4@Wodh6PKLDrb5r>jDv?(I}DXCSi5DPS60Dq5m-%BXSJow +={whTo7OV;pXY3g2u=1F-8CpM9(xGE?Ok$v7sSLH)1eJF1!JD`GHk6%f(WLI$XiJK83iD?uR|yDo>qq +S3-iQt#Rs9CSTu|RrgRCJ$ja|^E8Fmg?~W;jo)&KE*jHV4v|JXf+~uNdZl0Pd$2PDTV?u7}NffWTr)E +h?n3-=_A8ReBh=u{u)@7Bm9%eI&c5Bqd`Q%mLve3VooVkX~+Gx9NB3;?M>Rg?k%_%eR+U4T77um&xU;;BM-P+)5#4 +6;Ty>Q9BRdMX!YZ(I7`p!k9b;!1G6hbAFF-9+S#>CzW0x4$L#9@@DdoR2~us9Fx)WCGlqRR7p(s_3*D +FbF|#7xb8~MGYZYO7M0kZb>wMRt%GQKSS*k93iqfC(_+3?pT_&TTO8Zrk$M{)Df&;Y<@~0?W;bDZF1I +{$ko%`;-@DFxnr$<7WPI9DSV|28L~mDX!|$@h7MKPU=hVh1uES0Os>!LFoGinXhTYBP*YlZTwYZ~*r{=&#b5ML^et|x46`%rVI#*!@*u`Wht%&(aHC)+xe{+X_V$49d +f`Fi-W{OlnVN}qY6S0LazHipgzbd+`J!zSyMam9eE8~VmxlXmqa!Kl+!yR!1urf+r+$fPV}Gv_5(tt{ +vG05DjkZo)pfyWaT#V`!?6+<`)`KrMq?f5*GqcHUjkazqrVrlLz6&nz< +!`fM**4`-b>l?-?Yid~6EP=?Zpm&rDD@jm-UTPLu8#`6sitKJPTH&aR-&aivz2vZeL#ZusVQ5ujh?Hl +zawxXY6on^&90Hfg2vDde=xQ*$g>6Q_^d>K%y9lX2<#(j8jfXt5JGtTR_-ikb@(~l{IJ-X@UTWdi!HT +?L~huAfsqj%ccymP$lsfV3xV9QDk8u-Z7}fX4sZtr0Qqgk=kUC#LBjJ*V3HRnXC|SN{4!ahTIovHiUEA?ZN9zudM(+Vx;|Z@D8{ +`%2cX98@%W%Hi7Wr;_#;8*`;cTq60fhDD}TO{<`%^_Da|Lgc*I_YIbABV4 +c7iZ3oM(%{+z&v&YJ?w=0W7eS)4sSE(`TsTleuMXZp!4m02H{U~>p{D%rG6vQ+dcXRP)h>@6aWAK2ml +dXPgQIaJ+7Dl008m;0018V003}la4%nJZggdGZeeUMb#!TLb1!6JbY*mDZDlTSd0mdd4uUWcMDP0*lb +#^aegKIa{S8uKwT;k{vP$@S%St@Br^)QwnKi~-Q^x8!Vh%G_7iEDY^q%`C#4`pbjKWBm*pe}ZC`@z8q +MO|%qJi(_YH(W@mToM5?!>!TZR~P`5aom^Me&C&psE_@7PpkfhEPTmaQOw>U08LiT8T$^mrwr)Z8-`w +yC#J*%PYqtwf)}G2T)4`1QY-O00;mPT~Ad>uAjJi2LJ$s761Su0001RX>c!JX>N37a&BR4FLiWjY;!M +UWpHw3V_|e@Z*DGddCgecj@vd6efL)kl!vtouNK=jy%<%2v`N|p(j-Vyq(u-2v_#u#WldCi{-rT%+9{=$AyBJSTZ+@bP0&izU0AQBnLTh@Nm5+OL-a7l|&F^A0BYa8_f+1E_22(^H>z%r +7pjt65kpEm2FlY9F-nP(xb@??tbMVqlVdS>5N6b>WAz#AWp0Sr&i>+KaB;PesmZVMferYM7q1>wKxU! +J96wT%)%OyjwVg|Q_xR8ZfQC66qX4QG{opa#(M)#h_BjLll{RP1w{_8-=HdY`p_R4r0&E_Op)71Q#B% +e2^EIZ?uou$jhK)2$a7FMLpaeStAy|yl%_q>zNUi6B?6qR5JFcNrq?Hp_F`i +6VvlpdqzE5K2#smvF6THEmR7bEBhcWJYZkhFfW#*|m9KlL)Sp6)*|?1P&G>0pSR(76t7={4c>fv(cLs ++Q96%5feK#=A!_4Ku=snQguZh>|KUfsOT;YtO=kqj51BS>?4~XfCnht;aONA;p~PjUhM(LzF*pT_LjrN?RZYvf-5 +p(FAIL<~=}yvG%EE&(q}MxD=PA~azlq{-f^r(*$s&>A38E(|=^AmYZH%5*3;kE*HZ4td=yQIkCb*7Bs +)AX=fvfsMA-HX8s2|(|Q-vxqVYGI3p*leSy*i*AWkp{`UWrVD5vR$jLXF9+w2Q8LcE>>Q7O)%kAF3CD +R{q9@8`TNph`}?`#lQZCD#Mut)}>Kb=%YBB@Di7IHNmX#J#P5+_(LlsY^I&B6Rx5>_~6;k&1f +1hW@aI4>c9$3ug{|V^M;y4w7dF!QgM|oUF(B^!`*3@$7`!R;0i*=lG#C(@Cu@Qm~N9N*%Hkj8dj+922 +%FFl)-&V}w_=XB=*K653P@D0epthclfq7db#rk2VD0IOpNUC#|Ki-Hh5W@%yAxhR3D(XdGJ5*HN?6@H +M5dsp~QJFeOFwuCsjZ(hgNUxSO7p?(gpI?}Np%XBqVA=Ah+Zk-HV$-MhXh3qYMnXXaYADjkRal=G +7JWc2vs(-v460|{fmOv8B&*=_@xtRp+V~_xOeKhqAM`4nRo=_~kpYjWRB%1FE02iJy1V~NqWw@G +$^Kc@Z7p!2!C5oPlzUrikQ}byv{})?_cw9t%ljWHZ8L17?c+WRSm`xqPK^jeg6)hc_3G+so0(m`4x!^ +!c`U^h+;e3`>8rc@Q?T9-f|6%(3c_DhR`kkn@en3Pa|+$x`9b8hT=)cJmzEwaC(|I*{rS@;8zqw!5&n@ +Tovjj40O<5yFaEkfunzivc4P#?8nQH4vTb_H`rj0UOb^CB7^6qkSJ35c!WIN(&1}?C)^z6b#UvE>18F +acQ}RtFtbc?51}+ncG;R?kYMOARXIq_#MVwJ9u#N^k7?Su%N2k6R5?jn +8&ktVNLmp0K;8TH%RQe634D)s;B||$wHTQ<*s0S#xWAo8rQI;AfU}CAYefNWoky}Z0p{1$IuspUz7t} +Y7V2>$9rAw5tM7Ue-tyLtkgA*4+RQq*}E10dfnQ6Mt;^a0G1$KD)V9$0!m@jp(Fr{tEJTxC#AjVtFfXWVjPZDye${FC$IfZq23nbJ)+lB9@cQ!$QnrfzxliCq$K>eDb;@6b=rYkA`2M0CC6zXzqwQYg1Ddx~ieUUCB8b@W_O^>8D5WV-Wuox);4M92VsU=n$ZM +4!zQF2JKm(>cnp%|P2o7kp1t5*N_R2eXa?w(bYgbQ|2pU+j#)LM1Se(8mLW}@qrw(OVQ3MF~F+tu{37 +kyR;X+fhb>r@4;$tt0tw8fy?+3E*fHsT0RvQ{bk(S811w{AJgK9+_SuQ+Xme3{+-gzHh!=D36JCK%)5bz95oaquMJMvak+(Mi%_P|8Vgmj{m*+yU`X7Y9zo}FpvHFv48M;5VnZ1h +}f{!HHte*E;=iO8oB(pbK(F%#i2C1X+B5RW;F&X>pY<=y!bWwp{Bx3SmU)Itsk|1=a11mRYxR9z=*Au +8`GaVu!Ql5Nm+_cLBc_gbR9%A?_?*{{m+0A{@JK~~!ooXMQ5fyI~`L#^;y^3!M}{~+&m&Sp4_JEgxYk +*`vYLcKh~2D#%SnPYet_q*VM{6ybG*&j;C*c&8MSK2|#(B^vZuz3oogyn}8VxtCK0*&A@S$>B0p-(Se ++77+t0Sg4ql5{z3L^_`kkLS4gZ+d`9DY=m%(n&$AG{&OU-Z4|k!h~nS{Dhf*#zFg8Nl2Y`Aq+B6acTh +CqU2?R#BornMWqiM+hupFP{f~XGqlCwSxx{q#etVEc%*2LyX+b4EOBre4A4RBtjBe-?Z?1Lh+pc?3XF +vc3Gj$rt7OWGG0tq@;t?*WQE-gO2ivTt--RZXldz2%Y%w>+X0YjbU>}rbAK(A#tdJ#6z$8FLlvxWb67 +3m=VZ5rCXNWAE-#ZwuQhH!dHIgSR75bRyRckQ4l9d^W+Sykdd*?c3!mZBZN;Rbk4M-zH~`2gt&BZM49+d?(GuaRl>BzI!rYgZ+~$vG;gY7h(NXz=$Yv|0 +sXMNlV5Q{`0sG<=)in{W&cH$GkfL(MR9S#67Ig!JhP$qqb7$_Jr)p4m7zCn6eKOg;3PmHzh7?OKF}&$ +<@K*gsNlqsf_F90^9p51~+nJdW3YjS&)_c504{;mK=St|fY`Un|>hxgrJj{T83eqbrpKtD0*GkuioZs +Mym07ahuCAVum;#BXhjhaa?&6;cZwtTGA3q +pqT!u3E+4(sUb6)ySNySYUF6G*}TR!o6%_xtm&5&IiZO9KQH0000801;hJRaXS&u=4=`09ynA03`qb0 +B~t=FJEbHbY*gGVQepTbZKmJFJx(RbaixTY;#{>a%XccaCwDO!H&}~5WV{=M&ZD&lvcp8QddF(apAy? +Lli}>Gf51I9gWA`7R0|ZPTF+aF2V=L^Jd<&{XA*N56~j}k--k5Cx$Om^qI(0eoCn(mGN=3u05B1vW9c +`Y6B}(NsaX`z!nMxGBP}%S&f8^J#4dl#yUUX5?B{l_x)<8!#!AQ)^}isx<(3jcyD)i*w|}+6_13k;lY +p{`J#Pua`K*+AO<8^mrAJ`8yGbOtZMNgvh72qsSOWl+DzsYfvJ3i)zf5HigHNMW){n`Z;1~h`Ms^N24 +Thl9fuqUIg(43YX^S!0}TUrnxWG{76^l8)4k-Z)uj5-9U;1(&Of_LPF4uEb)K+ZpEsYrgdz4UZ}e61VD6Ub_S-_{QtsMGv-ru|EQj8^b_&iN^SJ2Y!Qgn+eb;-vf$s>l +cW)U?x{BWU$l}Zqym&!y63pSbv!(-ql0iq<;f;)uDzRE@5R(XvZ=L-mL9oss_;{9rUY-wZ0O$Ee0uP; +zf$bjGR~eCJ>msTJwN0YGFlzH3!fEgA3rg{>T+&VtSf#(Qn)6aPGo+~auqoGs()$J +c!JX>N37a&BR4FLiWjY;!MVXJ=n*X>MySaCx0oO^?$s5WVMDjIsw3Z5si5VkM9e0tpE%5+4^||GzbS_$ +2E0n1OZ@Xyiq{p%o)mE(0EueR3OI|zY+p55^jTGitBZ$q~20gep7N7C1o1E(#3-&F)=c^OOm9<2Fl4yixx90_TeQ^ox}1KKd%SCG! +4%;M(>ak5^zI?(4rT$@B(&1^$6b3MnRxU1_j1i!0G^^>(qfV4-i~h$Ih-_IWK+BL4k14ZP%qmXL?(qd +*aBb!4MBAL04>51b`dkDa1WOCA2o4E5ffv%StHubM#qZC3r`A*bumYS~d+@)}JzKSrxOqERxnVJqn5{ +OK-owUVanHpHG)Bio45n0qK(dB0o7?LOP|Hj%5C!DrcCD8oZ^2yE(@njM-Ax*cq3NR2UXJYwoe~M{@T +FrCpQMC8r1LZd3|ZK{bW{S4sYn78jXR5^+$(SuYGI{u!&%RAV5 +JhH;-t@G*4`etbsDMbKswP_eEt0PQ~CM*m$wTJM$e~EaD1K*c-kz7VexOsbC9bMp4~6fzWuu%8F4X?RZsk&cJd#?-Kv +O2K{ad=yi-W2B`8svwn}#`3&(I4Q?t_bwR){Llo?P9z+>3L1H+>uko=tQS}h=SF^(2P&p(X5Je!@X2N +XM!F8KPmg#6#@`Zn+)QDRs2n$&CFe#7Tm9b@`Qs2dJ41c}P)h>@6aWAK2mldXPgTYn&=(E@005B$001 +8V003}la4%nJZggdGZeeUMb#!TLb1!9XV{c?>Zf7oVd5x6cYuhjo$KU-|9O{d;C8kLr6hrpVE**oc4P +E!-m>}D7BI-y+k_q|mcaj}9VKGck_TA@xeRZ}t)Jj{(Rl#!;i1w{gIji{jP4`dcWP(zw1K3k7@7S9XZ%ML~NJG~Bmv+F+kz3&+duEm4}FDY~PkBi{??T4z;Lws562YP~Rk8q#N@kOvam +Ilzydz20>8p5?Eu^7HeRguwa(O;60dX32qMWl+A9M#RZVlvK4L4vZw82r2IFOL23^T5c^>RI(& +-yl<%bD9|>Ppx!BO6I*5>L~sb{(1A#}i!d1!Uxo0Fwn=+;3z!)A>Gh;6HCprL0Qd_403rYY0B~t=FJEbHbY*gGVQepTbZKmJFJ*3Ya(QrXX>N3LE^v9BR^M;iHV} +T-UvbbLVgri&)Q1M>fV2zN0$my`DbgVeR9d1Ub`mL&lpQ0;|Gw`?`B&_9-3$a)sJrjJyZi2F)%C`DQe +IkbOzRRA56tGtHG@~}=D(FoYHK?3t6u2`8jn9J=QH}$du6m}lW5YbJ};C8G|uv}Re2|rUc2z~F*}at^ +3rs@@N3)K50}4uy1h=4;^NE2yU#c0Z$DlXm*>A-T+`}`v| +`H>e&N9X%Q)oVUy0jdyfYnHMqCXu3W0q-}zLVNp(b5PI$RQ +e;wjzGD>XCtWc@iAn1J77h!eYfOrbamYRpcZp3z!`j0x_j!mD5{Uior=TH7Vx?=>nFp%CXvlT&~V<-e +)Yt913YN>W0>$Tw113W^dXr-w74E9#8%0T1Y~=|E?}up;nN+If(bRYqW79l4>dktWk!k +yo7r$PR0IkUrAu%mUnF+Bb%4A{R<{i6F|Me@e&rXxlUiHS>sJVHuDrWhL0-JaF}f +!=v)n^fsXPtte#xXk^=Gw3MXHa;#@~+hPSIt#5lNCq7*vp%P_OkagZUuVlJI8I +BF?a=LHfhm{|e5Nf=;E^k#r>SjUEH^}w|?gAWNa1|%3>jS0kY*wcj63uTS&@SLw@F;`mHRS|g`fWM*i +I`rA4v|W=VTl6V~^`2XAA=F3sm^jIC0fxB_Z?XgYX&kYiXiS6`1*qPAxy%2++R~zvm~_R%ZSV@aw;a4 +cht?8D%Xad>T9G3lXId-kSo>kY#OC=1cM!wIabT83T*q71xEHz-LY&}W1kcf$Ny;#M2c*+!?)p~w$>` +!_A3Fbgx!ZZpxNKz(BSyV9xe#4}QrRu7LifaL +;Y8qkAc2bQ}d~O!f|r5jrs7rMiwKA93Nh$pM||cfA~#)T)(-!H*Hhj#?drJT%)UF+Dpl7>9gaoVCzDI +~lb3^|bp++c_UaF0DLq*!h%dth98;Am7~_b^7%ET@lvRWco~TSEWC+U`D6^y<>KlOI(j4+#PK=n%eEhM5i@!8 +OTj>*ztPtLrPNGY=R~&vpx43VU{UFKTP)h>@6aWAK2mldXPgRu#o44x+0006M001EX003}la4%nJZgg +dGZeeUMb#!TLb1!CTY-MwKb97~GE^v93SZ$BnHWL2sU%_)fL~gapX4|yA1$?+RO?tr<*Ew2e)T>_VC%sGA&r(C=?N!Qt6y<@W-f+RO{hOX+vLH7mS@J?Bl6^V$oWS3+^z*>dUgT0V$sYDHofR&$lLUMiQ +BW`k(-wpPOD6r8-3)#ExF>B?TCHWqQto%~bGX0uAvtXzxoE?O~L>@1 +&{tTOtUXKQ{h7>Cn4=1nxh{8}(k*P`?i58)FtHEv4B8f!}7TuLIkv$hq_lw!B7^Nc&!ZUi1;Yghq(1Y +4D~ux0En3o5dvd}oc~p0>f3OE%w1{q4n^8OyXGryTd*CI&0b5gY*@=8Nb)eGvQBYIb8=?4E95VKLTNC +LJ$a2*4sfZ>+eNrgdz|-We^zz3rMRh=kZbmV?B&2;A)LDe-^`=`4;CasRJE%pe_1AxK**JuhIp*s-#; +R%p*!_?PV|M)Td~k_AQKeBX35|Gp8W#648?)!*1(_1cBk%B)VsDAdamz_fQPf$zYidex| +vtEyhP#Te$(Uxbz?iH?CY=Zw%o4VB|%!~?g7g_kN8G~9>2fD{mkd{>*yJ0Wb#bOJ2Yu=i{@mF0oqV`p +^iAiszHQl_(iwEPGc|K!1D;cj+It$FDm8)3SFojLj}@(p&hO((kw)12*4*@RnpzXjwQ$pQRsG$sByrL +W73m;)vXqHis}T$6cjRada&DbKz0oN0m)$18Wn8NDgof|>wCethu$taMvE%z;}ih`9dUwMlA$;N_-Nl +{$a6a0m3DcuHbxOt6bNFFD$tdu#9UjG*gQiz=Hu-?{qfEM<;ChLHo8`MR4MUTGL}ORp{mq@WR%Hj)w*@E-`v$?w>q!BC^%xL!e)%&-f!2MQ=uJr>SAnx=!1<@>EpIJr|^J^|1433MGLBcAV%XvY@#6f4V4_Dv1aKaG3Y&?5y +AkMGw?_2AmM$+bd2(CjvEI09*1QltXci>`v0BU9JGIfK`NKNj-ftgvf1ba%`1ZS(-~aIAKcg1SR5{I3 +IF_tL^5T3_QJo)yOW&Zjvs=(*63;MMxi;v=q+fY)XzlyOVgjm4Oo}s@iD@R=Pe62CMKvO-gsg+M0MkwOp-%|E;F^MD0$RD?v>(2LL0|13LiCcqHk@u3&TGTHp8t+VGwbWyHQmw+ +E8lia0AgZ41azk*!0wsy56e+UT>!%6lYYg$;xmK2~^kK(r@D;N#g8sSdE2)YTCiQvmf{D3--3+vD-6+ +o_v^9X1*#vIuFBjk~EmnSa1wh=U2uewef>Nu(Tjs6U|$hoB*6^B$N8@WV{&rb8|M(rI^uCOVXV#>L_E +N`;{Cj1HwZ<{8!cI7&OZ6WOT1G)MRwTg%dP>Ts4-yL6oF!hC`{r1Ow0*DSoZ2uc0$?(L^D=Xdblttsg +%Ws2UxtY@}3=3I_`qCr1@8Q_GDXerCn&LbIseaqF|A!CJg1szShcPBJCVWp5bHw0!e;fXL`VP?jdoa{ +kKT(pi5RMVoJ^}T~hH|X`*06EZmgac*>Z$m!gPyl<%aQ|iU_8_zr$0>e86_)mP&xiBIp@J_Ww{z0|XQR000O85nWGJqt#C>SOEY4%mM%aAOHXWaA|Na +UukZ1WpZv|Y%g_mX>4;ZW@&6?ba`-Pb1rasjZ#r-!Y~ki&#yT0WSI-hM_~guDhk65E!$9}Mol%4CM8L +y>#yI{T5RWo%S&>X@4oxK<0z#=sf7Q@1W272n{qFWW`t5oNMcP2_$T!aWSSZ4A<8o)&Oe#VSS+;{R&& +L2FO_4dbekIMG9|q@dO|)&VfY${Ur{)jjo&8l2$UW6ijwDf?~rkH3=ix9FE2}l=nyV=Wc5hu6+9nDkG2UHfo{S&N>QT=Kdlum`ut%k_vxFZRyO6Up+fWyXU +C%3|iEpUuAoH?Q7WaSQ(9Jm4~yxxXWt6=f4WXc?b?F1(b5|=Qk;o5bjq&)T_g*4(JS5A;$P)h>@6aWA +K2mldXPgPRoJykCS001To000~S003}la4%nJZggdGZeeUMb#!TLb1!FXX<}n8aCxm(-*4MC5PtVxaZm +CO-52 +D#pq3W79mds9dvZS1o=C!)#B{3|jnp3L;zKe#ebL?!Vc{Vwrx^Me)GFFSi&QxoKYM8wEok_zd6H&KHT +T*8KWO_5Hw5rMWtqU8LZ-QS+pSo7UaZ>V%wo>H9=ALVVj~f@F*~SUYem&TK#p0jm6T)Bgkt3zX3|rRm@nfae}O&A?57ddY#dgof^WCDU#e6lH!ibKA>6U*pO>=#~Zi*S{N +5N1A%~!Ak6Y@?;>>ubO{78mNg~QC9rahO$D%4GFvHKQ?1HQ^7F8~v$i$o^Z8cTU6&*KdfxVSrY{^ +wHV^Qx$pDy03K$CT +rMssLKB>>q9LCV9UN!;n$`W|$8m|{=<;^+;rjDtef9Qo^X2`=+pC-FC~}fzt>vnlx}Xr!xtvIVKI4O1D@4KBKbyP_e@BZOHMJYz#>-Cy@O1 +$EPJt9;XQ2XhfNV0=cS&2kjR`BcmJ&^0=J{d^Ro+zGgypEc;!F%N*$*`;A+6WMB~J6Jptv>RLEQQRN& +W8DOJmx`1DSZsCLhn5?j!->R?lK +!Nai<8n7GD>!KUtW3|3V%v0^Ew`KTIl}W9T6WBEd~{FAKc@L@U*}35F9=@h%*@d!C}`HocMY0%MIR`NoXJdT(tgou*K5fN~95Q4`MknOXD;kJ`ZgH!uTxlF +-W3?Zb~7~U9x&Kk}F5CPI;^ghEN0&D8t`+x$-`XKDCv(6_c@F4p8CmKe{Dm$ZDLDL`vd*ffgJ2Z0qEW +J2MrXC@l`uNk7rofmcPhn59SN{X-8wZQ!x82aLR(E!X;~DKjmL<(*`z~;cZY6%lbAdkp;1nG5Wwn|um +x+Jyu3it^jBb#H?HUa26mz{`7}|uJLCsm?G>RQ$g6~*|&S93h0)JQe!SVsJw&e|XR{-1@dul~Xud`*g +@}kFarh@BmHSjkJEW10SFp{&<-y&|6?3|*XlcOM3owN@szOPy8+`SGtmJOlzG6XayVkYw==CQc0$^Bj +O>z>H_i4sMhaL0n^l^~d|u~EO{C)dV-z#k8NYaNnHY-nIZF{}?mI|+S`3{W!buzB6ues4W=8g5+746& +qAcU_Cyn2;vhXEjK{cUpM-3cPRKr`B$0K$e~juQ-Y@UZQYcz`gv4g-3$f6U1cl4^T@31QY-O00;mPT~ +Ad>gfX<@1^@ux5&!@l0001RX>c!JX>N37a&BR4FLiWjY;!MYVRL9@b1raswODO$+cpsX?q5NuA1VWmf +VEpOc*w9U&01q?Qlwdj!7yxErfoKosFGA%qv(I%y->1bIeWpj04*eneD1m9bH}4~UK;DgR=RDbmxHx6 +x$yf^7aO7Z)7|fHujZGpuP%R{T)!HLA9W$^esVJsFLmliV&avR%S?^LFWSNTP3g5MWHw-|QkThG7cdF +N67?xn^xf6yufgye?Q-eU?WlfpU>=YQ=Vg{joPTAlv8NC#uQQkA+NCw3RdO{?w^ecP1_QnB@*fWb{1H +xYxn4F$+XMRiL|mI&b!#Z%Bo1;b03-tVMcb=^q@KV7`Pxt-iy&0k-< +do9jIAa8*7-pHqn4VeXK%26lVJ4Og-re2aT)!f|SMPxFw5B{F^3T8hIy`;z)w5^@ex#XnjwG +ra>nMt%3$e4ZEGdp)d|ByiFdF)iS9tHGqlXFu{$*J>}lM$b0vl;ky4Z#_Tv+wDFx!2hB^SG2&<_+Y9SOFI9S+iH7plvJ-N +IS?J;_DqxqFrpz@NcU=0SIWwyOS2{oj(_!zchn-UB?VGS{Q%Cn9>0f4oHo}z1llWOJ0y@uW}a;yOTNp +-pG3pc?C-0(2{6mwvzzhIG1B^2b80P`dCRyQQoLw4=(VY0tbvOuaA+;VJ_}ZGCz1&$EUTEpoL~54XDA +6Ma1HYXsha%aXU5`5zsVNM$l7C>qbG7N1j`*VAsY^a*!fcfSLdYWwI7ZBS2jy7)F@o2bH230ZX)1P~% +iyLO^1js01ibz*|7BRH^v{ +PKT13${!5=sPki)vaLtjxsHou*l+U01Y?B7?egFy1KOv^F&*7w>7&l}um6sllwPRObUQsbx5QAQ}${R +nu7WYX_O;&l;2vE8hAT@P-_*A4+SWs`-7RSWe}6YO2DMY)Pe13&zJ_uFB&~6>&)ZArT8NJw6GOjfcZS +a%eZl&8erfs@)uG&{$B3gZeUOR2m>UOJlN5eGBI}oOYx>)Zs=ysG?P!Q0X<9c6|!o9L)5&ZvOxTCCJc_9TXMx!lhhfx+YejAHJ=MhmS9kH6Dx>0Q%W45QfxI>a5AI+q-&T7;6 +IBsqv|1^Chtv|~&^afn-|8;h>j;Z8i{LxVT+u$ZX9!gx-L%v6pP;GG*MBgrNhyihy5NE|C+8OoZQ%~_v>>gC66K;j=mO5Z+3V6;ceL5o5 +Hm|G#Z_a;cJw!_mXlEt%g>#au9sHsfwL?3awbqAdtFLU&%uV@y80rd;15ts0|XQR000O85nWGJ*MAYCq5%K^8v_ +6UEdT%jaA|NaUukZ1WpZv|Y%g_mX>4;ZX>Mv|V{~70Wn*=6Wpr|3ZgX&Na&#_md5w`#Yuhjo$KUlSZt +hD0nQ*qZK?~c+pwQW)PWNgQ`#fuvb>2x%ns2|e>tt}!%7PnU#UzRpy6u@V#XQ_)#}FYyyXZ;}Y?Ky_)jK$A5(rQE<~1K2+F8xSn=5%*3y2T)@{BtNQ?Ac^<%8r+R&63Q +p%gHklO4pPSp_I?u}N?)#U`EnHnf`mj#ZRQ_-O!Pqc$(A!>@d-OH29@sg%`=~4Ok?ro>#PcaWZD{eNq +ZQ=E6f9r&ej%ghH?lrbIXf?w@_uioP*ga5z*OW1jxWj{8<0}DG$#GQWBq8wPD#NArt +m0fV9Ux$FJUBjs>eDS#|LD5R!dt6sCQ*i>cSlFLWFJE6=osQX^s6GIREpj^R7R7qQ9YHr|=Wj1=u7<% +=u_?uZqipP&x2GR3Pv4wepIw|MKV7{~;O*VnKZa4PrCQjvB3E@Ol2n$X5uPAX6|V%FHp&h>Uhpi8Myj +4~%3>juLO?hZK)LgU0q)d*6lD;NMk6u?_PQ)SiTtWci<-az>uFgQWqA5|AxO=l3290Y1X^RbR3c?^X_ +kJJ8$l~ru~^ilVoP|eR)Q(Q312|$nr&AS&|RhK!tE^E*t95`SXIlGadZd)~GoN3}vVuCGr&eW>dvU4Kc9SHVD>A42Z!0yk7plGCkD}~sy1qJd_1r3RJ_?$t0Iq!^6pqFRg0H@`SZ(H&jI}Qn?Jq$&K@c ++oHFj6;T_MEIfNOJO6H(B`9dH^!6#kO=_3Zli2RAk2{W0qke*_FqA@dXco@`|zc%hWHjitbb2K27Aut +Q971b}y>-7ZQD^S8$BogNrXXiyO#;o~!+g7d<8zY!Oje4_Kv8~K9V0^6vsTP_u=+*|*t18%Bn9V^gQT +cejBV-7*27L!Siix$X5CC`~Q&>>Nt&I=ZJ5pOTtRuyjqS81TyNmT6+PcZQ5^n@-@DcE@rwn*P5LLNrW +VVwcOX+PX!3SjC)BvUIe>`&Qere2pLQHS)>jhezvk&Aq}zwF)oTS{R&93$m8AvAt&P&5x}O_djncSrV~c769@5q?6?>;5< +?X;HiXJkRv}4@Ee+Gs2|D;O%f!9N@`B8sI1i)!jK9=ssUrl(d~QiE4x?|kEJRsUk`TI$W`^IwGYaM+tk*!cfVSjj!+UO40^-Aay+hg$z9nFrdAb@al +l$fy_!O|lCs$yG?c@TQH4yLRqKEWYB2^Lt={TyS$V)z74pJtyErrY&y10}Ct}q$njNnVwT6X7npfqm@ +%~fL@g=u<#tiv;`O(rXnR*0!sbQ^Wm%{%?*uT4eg$1agJm5&XC?!8kL>4b`wk +>~Ow6;>IEsmS&s7BdqQSDvv#*JP7Ujn>2>zxbMX=%~t3z$=3@*7TAA#SQIlhp402Q(Gx%{w11$7H>-| +j^_l^RtL&OEUHk;m`Kq4iF$x_tqiChL-fzwt2VF*g#7!8Y64RYhF)c8Jm=p|f4RB1e0w!}Mnw3E`fNpcT^vrwO|`X;@yecGwlLyv^W)jQ-HRRt0)?jiHXBpF;I$_8=|T~v-S%& +z$-Ov1sLE4D4lPgnzO1ACoXrw&OalpaTM8&g7i +F(mHf#scTSKVVtNYhZi{KXYjY}A=cFp&aWD;4#afu6FQ!(PN +51Y!Q40$-Yo}ryl=-pvxx<`P>H=$@d~!35p1s!f!CHtn*}U1Gg0^V&5 +FjfJ%RgX3h|GwQz9-dIZ$F=(UAkoTG`d+<+mB}saXQg(_y&AP$vYP45L*_@+Sb8WL$U;%VlRMb&;2S> +0uRQIwZ?g480HY_PN5RQ-`q~NV9kmejs13D~JjG0CZ&pC$zfp|IRN+5n5iS}}JmqgKC%^`l`pFg1Q(Y +Gq0{_<&Sr*Y(`n!Z9OQ9v>X0?dfMy*gCme~y7SEn|=Vj-UMA+svH>Tu@f0LigFb2kLY-t@i7A-E0ziy +)v6Aol)h?hbM_QpfQ@ZU?ihtK +oH0y2@iH!t5#%vcQg4Z_4ihG{bq9c_9_Ex|-*H~-OCZwObQEjnt(|-n+=8lY^_*EkxT9)MXdQR6lR-| +uFU;q6*0Z}w>R=4xX&T4b(HL&hzcwr|u7((zg-IT%fE&m>tfBWR}HQ(RDImIWq~1P1+!I?o^rd_e_C1~CyUB7@Fk1lX%24W>8LV +50;%d4a?#H;~%Wb3bpiTGL(Au!EJ;^@0qwyNLPfbl6o(LRM^FbnA;7O9*$TNyjE(b<&pN17XI5Wv@7sy7R)_QFJu?rUkybk +=Na*b=*-7yx<8qSE1@LEA8ojt(A4;eFVIy==Eqf>f(+B0funM>cot3P5{0FM$qoINRQVCQZaow!F--h +tdm0kYV(cAljK!Fz&}gS{_Wgo_rtrVSfnfNYE?bT~Zake4ZfMU9CMz7^aN?z4zON7$=@bp5^BANqdZ+ +J=xqJL;FKlW9;3=fYRtb#}h}}zq%cuUGIj>I1Fv$LiHT9kKyJ{6uW25y~6*RFamDfY`_}qt$s1%t&{3 +!yY*Uw$8)AdxnVyHIctFFIPaNt*tbI(W-?z`>RQSDkk0daQD|l(&k*{?NzMAKXx!te22okDqU4OKK2M +uEvEWHQyXgJ@$7_t$zmeC!J6{i=+B7VHLPl+X%Mw#Bx&g91n1Zrc`;$ZaJDDC_`e%=r@wR?KW)kCgSg +Ox)%73H<{U%vGUln|*4ur|y-(p-M^t7$$E=N(ta2iY!dz>nRBa7KwaO;ExrNrn*`leK$l?Dl)zs^ +yHd=ea3ZT(HF-6D)V~%zhz}p)bvQi3nBLL@%@_Q@z=onSu4HU9rtUO2M^7IpXi!Ci(kf${sT};0|XQR +000O85nWGJ{KDw`ry2kNaZ~^R8vp4;ZZE163E^v9}JZp2@Hj>}4pu$^pXGhXK*@2V=XUAB^3POIW_hL{;f9FitTjV3Pr_v;4$f*g*L>?U=!wW*YbBpQuIqtP!k{77 +7`q*#=BUT(5tDdy#REsI8|RoUjLm`PE~b$KgOk-*1tEk#vk(4G~d!H#Xy)-pPHB-Yz%I}xg>vw3q=R! +vqGNj`m<xnw9yWR +&A5z_N_@8`@FTi+B%0}qAIC@)J0vcVJM;7-obo|<;83Ly^(cOmbo&`i!6sv3!G#q8VP@X&Z;;si=|Wz +_i473wD6a8GMBStesc+5Z9DQ^1Na95{`;cXhV<}dRwVWI;x)g0mE?Ic%cXu}W`3I#$x_z*?Q38@KmAn +}vQC;(H(UUjT~~UuZQo={cY3Xtjo&T-yBYejtxRVmy?gpT(OBjQVhIj-R#jF-@vSV%Ph$%(G@|depM1w;`-vS;ff$NRLyQC-7yHWKF!t?qu4HYC6S$tyVCbICe$=$(1!sFp +_t3Nt3lp^A!kwQl#f~UDjhg@^YT^I)Jps>*S}hj@MZMfAx4{eNhNi1X~Ma({%sg;?>u$e|Wba{5GqbH +pyjiYZ=iN1;|R=7AY{HYd<)MW8h^Ri>bJx#qjENQ%mXb(s*g6D&Sa*Tf=-qDQv4a&1&-{Q8(rhz%vvs +^S&tSb<)Ht``O~m6c7wMibc}JQy_2-puO>GSk<*u<`ubD)#cK>&yxx@NgAsrEn7RVEEIfov9mggleSs +K$h#P}uW5rVvB;K)W+-IJppxclgq41)}yU%%QmtFnN{z>8SGSo`6B&h`!MM+bHHanCmN0|p5+V>+QUh%;CUprQ9ZaaSgaqHE&c*lICDJymhF!b_tA(hV6>QvyIX3~Dlz?^^68W2k=~z|P;uQ` +EO`D;J7qdqK12EwtTZn%Q=>3Pt6bw`x;oy;_<~8^a^(I5hsU> +8$V_VEu2^iB<_*o<)B0q?Ftj~ym;X(%DbMa)-Nlw32d^{EY6LB=!GZ2r3e-g5+%X}aH=MUkZ4!>MYo? +Q2K1DZ?UbF>0%vEdk6bUPjB;|?Pyi{uiyVdc>5!*y~a0aS@i22#*A;{5!7F5X>6hF@SyJG!BhfhXL0( +xk=2>vv=fMWTf4x;zp;WMHN@iln4YU;#mz+Bz#+CC*>HzC8c-&E=2i*p;=4#5<6QN1r@B`=ncV;HZqq +@e?0S*#nH2NRYR5q|G4hh#*Gd(5~78YckMxy}4q$qpxP5u1V8@Cm1F5^7iWFI&f(ucyM!lF9rpZfhLN +oEh@sJxfcvb;Wb@&9}tJgt9UPt$hLbHT7suI+80N2neTtmfa^JH8%c{0l0njs7hwPI=-HuzJZA +@?N8=uVXZ{xbCm}(!wy>-7g-{Y4@f4i?TC^2vgD3iv0_aQQeGz{Z_hvzuZ3QqpX>-tl@(x@CfXg)?MF +VA>fmKa%Vcn~P7v#P9wIrzNW!*}4<)%Y_&VLfHOE>NOI{zek76i1!?9uojs3ok*Z%a{diB4*&1! +}3zVG1wg(1w2AE!iQ<#X8q!9=s1_h +SnVfaIcl5C=#)R)yz-Um=iosb9G~SX^G2f(yjSz{e3|8N+J@#|7FS=u30;Rv>KH}^Giw2v3k#(a` +st_7fJ&Xf_{;@@-WYtrIG?Ev20D>=9XuqAFftXyonvxO*%&0CXauf~3LG(mOXUUDX!?>3ebO;oBM|uum@@Z161Fc5eSQeMUOjXXc@|#8O*`ZZAmtIKAx}S{3b3+P})`7^ +yas#x!;l{L_JIPGQ+S#P)F3a3Fg>j8abZp*mPgLjxS!l`R>wnGT6l0=+f^^Y~j5!qioPuy93|8L6`Zz +Ty$XKo8ZsleJd5Bn1~O0F87^x5_GHjPpl-&>kD;5;;lOiN@*a+ZzUQl+{2Fe8zlh?p(?;Gm0=Bw^J&L +`J?r4=K}G=*{L2vLF~W3ZpwS+LU$r>&Qrif& +Uk1(W&Q1{|>2wahno7Odl*_M-Gf<$>~x6igLzXP);_SR=jRe{!4rSsW=TVBB$gr1e}46u+c+iyQ|38J +KP?I@JA~AY3L6?;BQ$RvJZB6diZCJhdUA?>Wm@3H^6)?S3>5KtbH +`YN`~3MYo*)Q`ad1a{ReWl6h@1e*vOD1@TGX^ZJU;WhLJCV{_YfqzeX%8r-hvONe!Fk3>W}pT%Fvd5CqJmTwLqSfp4IQ9SDb8?a8N4o_%)qr)SSTJH&3MC!u(%JD3ebL{7yO +<<$IjQu$(Y{9BSl`8WMo*Oid-&}`Id*_b3h1^(CeRVMV4azb+ +JXt3>^`M)(}M=HiB#ram<9&3^ZRX#Z1l<3<_JNgDMk28$3uV2j90zM%*Jzns$z_n0}ZW_)ZYYqxMg7j +8c(jV3}Ym0F~%;40uA6?c`L+QPJT74Az~6w-@aREQJz{kR +2Ur81L0jA?cHnS?Nq6E(qB}tok1k~yeSQA&i|@XD`T}?=NWL$rrnUV9S@CuJHLpwGk8B)L%HL!$mU5NVkTT3*&$PKN@g%f+e +mvdanM6J$RvOZ4cZ;HPwiwz*njVnI!q841i*~EOFfL3wE=uDQ{hV2PHuxCvl&%ZK$PoDI=;`E|69Bdq +znkXC(bJy(x5U`(M#sN?nWtKo3c1-6Mw`i-J^@dh^Q?+d(Mi|5h6qxm@-~BQWjD(sae;PxZC&qK2|gY +~-n`*l#H{vKuzA2HfNFJ?nKKMPq~#fcktacr_~0r@od7`~hk@<^3pE|M8EH(hB1ecBy6FL80CJp|ZoF +QKRTH2Kcsdfg)g4ctvd}BXZe#I7{*bk%-?yFCHOV`)Tqs)LBH+;oL_~}1F2vT2Jj6^kmo%UYk%+FMA+ +iRX!E_Anf`ph)p)oj40>?ms4MscX5xJ4aNWMy1PI!>fx4BlBU%S@nPGV-MDPBRC4o+RssX@+aQu3m&8 +b__>zA6p>m+jLq-8oRi9S5x+O&RDId<6<@)pA0$L*j8#FBLrxb{7{wM0EBI;2ILK&2!)g);q~${X?Bp +6{rSN{iG?!gmtV4Vxp^qKt>TcbNU70SW9qTm}L|*VT}fN^5;38uIyqVBE8<&o@3~86*--7TXb(jmIAP +^cM|67*A-Yt5R^zilGho)c9W?RTxtU9Z(dUF6>JkFbm)Mb+BBY3(^P75e7uB3X=jM*n2Q&VvFhzO15u +Ld`044{Gv))hI534~Ynfn%@RiIL!31$)UD!mQLk%Z^Q0|m1iLg#Dx+1zrKd!k1j0D2g(P)`q1XSD7ZQ +agg>G6Erb+NIP9!==s@En7D%0cPQG5ll&{*^U5&UCEQNGxJX>I_c|WtAs$z4<_@gNtkjB5UZ3*3>3`rRyMIw6ecbHEWStkfx<<@WcnVS123`0^_ESwub$^61j5j +;MWm%PcO5__3*F@JQ;FNpZ3$?j;7VZU`hB`yk9WbU;1vjzv8ZZ0!1=}nh{kGTwQHZ-EC1zvw3<_s +T4_7kJ%R_5|NYFuNz-Bpr4{FlaTM~FfAu5`F?(_HJF!=ohz!qWjH;bo>&&JpY!u(sFh|jzFfRFdrJsDbf{B`mHX}tG +(Tx#o_%Oy3t_(wBL%~w*}2R!5?bGLiS*<%@=`HSz~eq)%%_;wv4{yZ-sruBg%fp|U@XXLngFFfPdY5v +pKtHD>7cNbuMT42FU!iY0u8L~$tX$sMv;$jHotWKd=hIX|_dL-r{zwC06*>MjB-+&liQjPcs3x}~>Px +h`yy=3w`dy@7LD`ELC(t2Pib!{dRm;$I<)E>bLJ^1j@3t1UFLOR%%=nqJf8@M{4#;#7;xX3{SxHhz6ymmxlPKMwem%f)YI(=V`UGQz54nPkO)VGb_NqV8uJt*F +Z@+ZQDZq-q0`$C>J=8|H5L)K3fz;6o=A!ls>FSiEn`Mv?$`6)qdDj%@}8g|LoxuQvU<>fXjca%_#7Op +|%DZ^Q0r_P6Ymvrmpi9#qytiAoB+wHT3N^&Z229>wt>0G#?5IZ3gzIT9XHH^CKop<6MG^7JH>Zb!7eb +{9i3hmb)vCAfckaEy1i6*L1z8SMd#eo)ETwW3AcXZjh|E~`C~9)fGhU=BJtMhy=w-X2jK3dB&FhIl63 +5cXsaq3=H!sQVYz!0lAvBjmKtsi}D|I%Voi+E&ao;6p0JhK9;F)=PUDU89jlTQ8|k_t=V(CyNF6*9Sw +}>R6i2vR)dz+RCsfYmp`K1sb%34r$%wzreuXT*f2oI|qjrd_5Zg<6X54AtmD+q&Kk4{g<+@tc%Mjbhu2#04C +*JFMj?K3w4I+ZQ$(AmRP(TAY1>%{DF|Awyeps(*zfa-prqiC(2HS9^kep2w +wn%dn?US%FAmKGT<3hlKpi8Lpi4rW1jl#SQONyvX0I8o>aty~#3E0YN@J +jc2%T83<-A-LnCCW$6VjCq4}OVXuqFFG$FF8Y=W;kjT(;QQ@&zqfG)S3@64jG@O&4dj|CXhxEHK|Zv1 +wV8GorL?h?gJ$w>|1aOfnriqawY6sVnY*4g@{)8x00ZZSdoPGc?wR>7o0KaSit89oYh_2%lR+jTO4oH +*k@{*I&Zfqs>G-m%1PVBnu*Yu2aB5(P9}n*yVf6s}jp3x*St?7=-_U14Nk{rsJb2zfqU*n=|@g-h6#T +c2(EW%_RP%WHS|<5alS>X(<(!RzTm9ud9LFukj*7=wQME7WVm*P1qSkMjnka$E@mvH&hLRYnyG5a{Pp;N{#`)IbJ$F`|LB?0Tdypn +VPpsnNoIUM+`rk7`95xOo-PCue^0meQ)W=2GEF&hEAWoWQCh4parunQKpin+4@$MBx*$$xZUEN8JNTyx2ow>eEzvOJ8_ +&n3cGsOo3*dvBgRl)_2tSxIxOd8i~g57BDG4+o_*reofSfWbu+_8YS4Fvr@A_qj67_0Th&$1JUU5QbO +MB9(iEuP(5c>9aIMzV&Tn92^jIHH=^`&d^_OuKwN;8$o=%%d=$kor>$_P=nVU*RnU1Wjo8|>*v>_yb) +D!qNzoEMkc&ASz>FcQ8VWIxFAGj}Qi=up=OvKBlC+0E`)o@%8)S!-I*SNZM2PUJPQMIF=>B?KD7#Gis +Xm;k`3%PQngbH*=r;I4UKIwq?!Mu<{?7ljI6+8{VQCDa1^n{+C!qc-L+)?0$POQpM0}zT +1)MoRWth-NB)#&YY`G`x1GjUz7!`{FSg8;%!Pq^P*paGk{qc{miLnBCDi=_m!0(60sLMX23H=ii5$J! +eGfyu(c!OnB)rUa5&rl~p6J3R@2P@wcgoQbF66Y(biMueaQo``4SQ}J0}1W!ZJoM9v{RpKD?r=8$0&FmPZsI0jlagMH^*|~9 +V}M*;HVa=fA^QlD0X{k4;ZZEs{{Y;!Jfd99O8PQx$|h4((i$g5TgJwPRp +fE7YiEMOHymYYlx3&#^|gUIc%o&F?gm-WI6&&>DUlg6C$Pp`D{3LVsBp~6}jWg6(HLhTsmoH5n)WIa% +iR(i6mw$yg5*I8t4#}xCCZO +W4KYaw{ChPwyYk-;%fJQ25<`Uu9ty{OMcLd*PNYO|HDJ=jcg%J{RIMgLsQ^t8|%9Dj_>$^VBO%m+LzjVFE4voi(Ed!3WE|>OTezC~^P)h>@6aWAK2mldXPgTK8t)tKb002A +)001BW003}la4%nJZggdGZeeUMb#!TLb1!gVV{2h&X>MmOaCv=IO^@3)5WVYH44lKth)uK&dZ>Xdx(H +ezK(PsuJ$12>XlW#KS0YuC@~-pi`$&CQankr8+x&Pl^XAQyYCB^+RlP2}F^x;qu+W&gR=S@4T23l!TJ +on3t2D0O^7GxP;|;yTs&oC +5-#gb{$O;P5Iz@+yP1-EM0uEt%apFnZeZzNX}=*M#6z;C?eDY)|GNmbTCL*u+YpabMgG84E6_n7BK!n +Lt@KFBk)+kggWC>AR%dQS647>@w6eX(#Y#irvIEhV1@A +MH8C_hci?C4!Z`9<$?I!G|8K}S4IRZz|tZE6h?zPLup(XG+Eo3naFcSv|pFTSlgsuD?PLxGQgsTRzRi +3dKur>JahC!~5F8!^}NiG6x;XFXIu0!(~fPt0Pltixo8m3TBtr>*Q#sP${HuGi~ik=_NC(*K_)hUSmi +S43m`tdLL$!n`<);q|XS|FVu_g@MZ!Z4Fgq$OHP@#TgC5v6%9CM&25Y?Y{7k>DeO|C&}y|8QagwA1DO +J{?x!%^a0fejW0se5Y~lW@=M3sL6940p$scfl!?oQ=Q8O1AUm8H;bIU87kQ>qMSlD#3{|PBifsUww(b +Y(N+leY;gJa5Kn6#S+SYx$2X6yjF;&OqT0luipk**zk}S(G@sZ7thW)nU!g5p>wnsROtZG4fm~ZT%fN +rec472B8bz}W;mS=JUItC&5>j3pF!%i$U^6|U9oJ`coiZc!toW|$5#}^L0>@)cEVI-y9D{R@*B2$Uq= +#aD}T}8wz3bs9H|3;t4_S%Iiwb5JIbO#BZ*{TA51xEzI7IfOkOd|_0Gqy6Y17p0(t~L=X$Y8J_*EfDp +#IroP&~vtU<#9RsP3%_Gz8Z|7gvF{N(jH%LdEPzM5WO?KE!gF`K=nST0Lt*;whBfuFg|w&#j*-khx>& +@Ie-j&#n)7xlL;Fc*LBt?{WPwA?OS7u!uvVB;5&GyaQ!}A-G9E>{v)?ve%k&fwhyoIs$6AwL~_?6R-3 +{W{sT};0|XQR000O85nWGJhiJyFr3L^19u@!qCjbBdaA|NaUukZ1WpZv|Y%g_mX>4;Zb7gdOaCC2PY; +#{?b!lv5E^v9xS6gq}HWYsMui#V^u{%rHUcIKkP-I(yZYi1+duRiNmX2&LGO3Z29dFow-#H|8jhbX_F +*GP@+2=x@?>je%U8S^T=4fWZKU%o82hFQlq1BGrVYmgbtW>46~R-n3K +WXlZLx=N}e6LPOIPWtR1?A8mcQl&wY-;@Prx7@NcEkp@v%7kHCF7py{wile)gJOSwqyJ&g@W*HDsb10 +%AWf}HYPrnH94mdDoJTws`6RhKzi1B+g+K74p*K%0n3W!-oGb&<@{i?hpjZ!X +WzUtgpbzr6aDowleXIpiSie8%uEiX!(3w~);vL69LMN|lh-Vl_0 +;0yH(yT&|CxZdpFe|P0>WVgU-nS(BmbdBpK=>QI0mh~~egk^d0>miK*-4z8xL6B?w~q#aZa{+O)}dyoraB^#V4u +z%zge+~mBch}sP(=Algccqv?&E{K#ISxP)Ta_(ciVF+AB<-(ivdzublAA(Z-GEuQRQ*XiA~^i_YG@J% +77mFS&F9Qfbs!<$e4Gc8m$$`JRi?rKylC8hRzX*R{af1-Tbm$sHD95-kT>1nnthh_RRvx)UziCz8Ugu +=DR19*VZ{oMLmCH7X&+@W)2(wMBuDCe(7si?L?j$QKI(=vy~3S3C_FiK;+$9thxKHHgT8lvc+y +Q#tu75)C|``*@%vAtH=QFr>t$RBK+Q%8fCzYUkPeW}L +l8)hlBEHzmQ_NR?t|))<>C#Ae0TcpxhL#E}HAQOk8*6rdqbRc))hoDDZ1TN +Q|5OMZ!5-Gev772Tt`>4(N7s>?s$JluyOCWoe%g}v}cWgzH`t}-dK%*p1NKvRZKcDPKj_+jaSgpDSf1 +f)-?E2oSsmALM;tS$g8-%eXjNgwEQclcS3XgM8zUEa;^Qz|RxY%xmcU +OG@uj&uBf#MiI}26R6%=V&{WpPy}08S@oD=%RDr#h9^=F2o%6P^#+76k%AZ@70-_ab8ksD8y}VQg%jL +wS!3ky7KO*C>m#z3>nn2ZUvkVj2MkTe{|T^K9$V&=le-*)rLr%4!o(sX +8iQn`<+{9dN-aNJxp>OdUaeG)^|h}YZz;@DSyG>}cw{BM0g1HSR+X|urh4$w;3NrLWc5ctM|h<=RP|J +FYqXj8}VF7o;9=3!03;_GWm4!OzE)U>T(dWL-PHSUSW=TQdSS#o21hu@SC?{SsKE5>kZK}%Z4VtjAm8 +_0VVLO?y-s7Q-W#*enec`_ch{k#@QT5WIq0HmI(i?(O(q&roEo#bd@% +zajk#P)h>@6aWAK2mldXPgTxb|8U6*008YH001EX003}la4%nJZggdGZeeUMb#!TLb1!psVsLVAV`X! +5E^v9RTK{k3wiW-~e+8$*A$I3z-!OCt<^k8cv<=oaK`z%~@C=uh=$MU6x};=ZQ1pM_dyk|hx}QjQscraWca`|qUNoD7(S@*fvNd|o>c;~tV*crL!r*iFuba#D+pjO*{` +2a)ujX{?je2xH-twyAn@YH=t6G@u>Kkcm_v>4JK(_AU`cO-)c;&7>D7ctUW)mm>AP@QaC{(FUZe+2`_ +uM=*hoF$K1wV+GRw|GQ-14H5xdkao7#qmnyXz~Y+44fnnLY@$_GDvR#K%IAel3qHWotANZ@zE6b!m(q +Wzf{JvblGD;PL(C?HAYYFXxfF*GMw1gJZFmwg~ILmSri_o%mQ!CUTnwZ&%nYzk4 +rPj>TI@3dln$^Vu+TEF7%`rY*fyA^wV6zs=7-yhj&Clo7(b$8jWeygHM~TfN_NRlY +IzmMthJmK5`)t<5~LY_*z9CZ*W2J%14E!mXNq#$nwB8(lGH5%1NEo&O`g;rxfI`xjDB!NVcY|0DIs@{Rm3$9pgBsL +02U2sUDOYu(Nztn)D>i#ok18N$ics@I52beyrajO|R2IN#tDJ(EmTAg!SNXd#FH)x~D8Jpe#CtT*aQ0 +{>er4|;I7Rok8IC)Cxn!A<>CV}&ffvTeFs46hQ3Y*X3EXY`v|D>gwwh2TdDgU|AwV0y2FmYyM>D@VNM +roU@Op>}+q;!AgbIypP;MH%-x%OF_dAH8AH~`p&@rE?a@Y5UukR(x>hu$?ySe#o?z~v{Ij~4bU4Q&(? +bluGx=j>u%zF?%A%66wuYg?DPsV~?@>k7KxmmDOz$HfWQggH)3;Q4o2`NvwVojsUv7wpW>f@Nknbd+q +)vJ*E7zvaEsMr5lqSRbAe3oy~}wSI^g*+^sn8cUdTt5mpKDb!IKt)e9!gSa6A_@%e3QC8F=%UVfjWxB ++}h={n4H&S|-`0Fi~l_=K~hjtk{7)kF;6Ihe$*Ek7A=%Pq`HczG&c&WI+jsu8lv_rm$238Ve>>endHI +Pd33jm1N?Tocfe4OJD3wH6N^~EJ{9~SIK#>8I12B{A1&98%Tm^-C>Cn@}-^VIvM{XqDS9~vs +FRg9$IT#5A3b)&d(K<7~K{y)5$1p)4CR++Mb9Y0SU89AzOCVpAa|aoye#*csNuf5n-giXrDCVw{_y-$ +Z?{fA7BEP!LMz0$oTCg5zLdbEN_=M0!(E#)&)R-m>4xyG2xN$EPfU(j`ZaKU4Ft4JOAmadHU=|)cqD} +iI7&ewWxB$c3!J1L37SaKmB;v#M>Em|cEB37hpa(di5Y+Gi%2Ucm#kFv$4qk;+rMd31P1^JG1dyMzA8 +>v`3n^-HVX>1{dDePi3i01aCCaDIfkHEQhMawFfl@$g>Q +WRjc3waa~o2zZ040|4r5U?mpHWm4Oj{ZGAKMJ$aQNUrR0+g-JM*Q3ez{}-qj>aq07`+G}#dlg}HMs@+ +YX}=z*eO-a3PK{C@m=~z7ZaoJ4c1$%bT384M+sfwBq6ndu|RZ1NG`O%zK2?A;Hk^o+v{8JDV*9hCOiS +a3SIulHUf824o-qkYG(#S+Uly)`$bm;r_P|2-eK5IfKFSq3A8P%RO3h(t532J={^vg5oaq+Ar}FHGaK +9<(gpyXT~gEB0AunMh2HZqg36#oc#IPy52Y0knrNSMha!d}CMXV$r8z`?&ErNC!J3Qq1Y()z`J9RtN9 +16molCXVT{T0aT)TiRf|GNU<{(6}A$n2K2CFUX@k3~!t4F^vPEQENc3l8-X7hv!+Jsb#Sd*)p%K-`Ysh4P=Ny +jwr@7`U1xI`fdv5}wbcK6R-z8(L)r&x98MhV_ +3@ytY!BdZwj|pG7J +}Cex~cM>nwOVZa^d5`5(bgvdjWU+30v)J98w&$553%pbXRE_jb1ie?NF$^BlG5*kYNnAT^nuOacQ$LR +xgqPREbeLJK6IyBYCFr2^l9|xZJ9a)^TD*TYgPh8d!(H8a;Rn;Q|eWK_L^@&5*Pn|YxGk;puIej*Y5< +W50N(qC|;LZ_K&E^pYK;-+z)-?aH&hW?%JO9i>cxfH5qs<%N3c`QTc`@tU&Mnq{j?F0KXTmNLiLisx0 +x%nW1G@u27GaK53q&BHt1Ijm$>|zaugfTfzaeUstnr4O_D-OCnH3gxdJOYf2QLR{U`s0vet){Ou<$u) +Kr<3}>^_r_ChHw9*VNsEOS_FEMY(?u3m$bH*>|>E&aql~qDFiwcWU&;fiE2>18H~Dj7YNQ&iMN)5ZGr +e9C=C>JW5XY*rGkSOW6lkJ@fi(IH=;`0$mT+ES?@XBnGx^YgA;>^Z$_4#g%fwE~3E{=Ft!a5-{k_@4{ +!$+n<}$~jzX{p7_sPS~J;dKp9$pUzY8;vf!x{nO`Xmfg^gueE+aJ@Ccc-LA27_d? +PM3MZ>vu{SqYPA=4Iu@eaPd3}bvii4oHPB7vJ90#g*$E)Ucp0O0G29^zEM6p6Lt?M?!hiQGEzW5dl*xYpP)h>@6aWAK2mldXP +gTV_6jb2}000&q0018V003}la4%nJZggdGZeeUMb#!TLb1!sdZE#;?X>u-bdBs`lkK4Er|L(tnr{Hih +DyvQ3UDSu$?)s9TNj6RPE(P`iftDznx3Z{`^zOPr^0#+pNa|t9+a7Rj^&yeP;cy8|R_%d@W8rh_o1lAg8(!G_HNq5rZfvKqXyM*bT)k +4f>sEyEU8PrNQ!oH+$ahBD5C$elln`PHP;ZKXB>^e%=TJ7s%8C`?Emr{!yL_0)4I(x$YD(k{9Rgy83= +K8=2Pa=g$*;QQ~SS>^$3RWu3R4oj%TW%TG0`_kzwdPfISUA%Z=?obGi|m#-FG+`Z#^CwOcQ0OEW~eZpcTqx1t2fVAud?^gSF4xV#reha)iM&zR_qq{TOq1N&hxGCq;FS$%ZSr5(&8Tr;si^mP8QD +gemZOIf7AN%1Bmn*v;gvNj=Wlk((7}~D4w&$*PUN)kU_$_e?Od&Xl^q*-c4)w(r!CM0Yh$>!B@8xA5| +5{Taytp5PwWWj9NZcA3CP^9Hgj<_Dd;i^w^MO;+P3>z-W2JjbQ^QA=uuEjWq4SQ53zR9fok#Lh8WQV$ +1KO(m*jIQZ#x7F(7T0#YR*mnHr7S35cK5%xu*jL@GojtT>}syMZGS-l{^ke*Dz2G{SmHVwy07;~69RJ +6`R90dAW(3?zF|7c%EoFhHN(f-(3|VTzZnU>kWSYU0eCaG+kxhwrUgI1wN!dfXpV>js&jE+k{8&gP^# +qyefj(BPS{n6pZ`*=va0o-C}a_=ZB6d-;c|K~@t<%L>ls`^vIwy%*C;Xkqudp3()=Q;c#Zw0#huI>4o +i(36n*HxYZx@;Fvc@|_4^Q0$FXcd`KBa%OVXfbo1(`*T=0$7R8s*Wh%vH$Z3=06n(=92fB(JiUC{!Gs +Nv5e#~qK*CwG44=}H1G-05zw}5*W>wg%4yy8Tv +QF%*pZM&)O`)kgOJQC!Qpk^fGfw#nbKqCC7w8swIxovl6h)4(YATsa+Zh~MSz%Ol*Sb-W^(Oe$>OLKP_M +5z>gJl69rsTT9D7cqk7m|;IH2WIb?0{5C6h>gB=XPTMD6biQFpo&)>QuM?8jz>3g|>2s_F<+GxhxN@Q +DsfFSQOIobp=5JaR)|}w#P)e(4m$fv>uCv(Yd!@+IMJRJoXf=EPbVRd445Bd#2?oO`ySdBJ%QNH&)mrCn`wHtYkHYzs%1IA$jNNdVyH*m5{L@ej9bWQ1|z!ksE1A+_B +-as8Kh-}^<^Sy4ylP{SN}T0`AC~;+Xrri;&VZy)9s4ORPct$!T2!u_`x6jp8N@WFTjnwA}UHKYmsxPu +FNRNL*`)M1jAqy6PQh)s|~G}C8PB?nZvG<^{vM@c&*>k9#Y>xS%JeoP~gyk=g?T*XOnB$SOcS~!ZZl2 +ma1XKs^mXO0;kU!@}vftw80AADBx-(ug20CQmV^)K4ba&oiwN(hZ~-BcU$0l3_V*ZAC_g*$Y${oz42) +g%{vshD;5CWdcTRiKPH>6Gxnv45;}N(967^i4wyJG+k)TTik)(fm~?z-0TBS$a#|estr$;|U3MnK_5h +2)7K{UNY^;g9#4{fP*(uTKM3+x}^0p&CFMi}XxMlb>=5vcuG>n%Yw9^z`i@4U3A1XL_Us1NNq4hED~;iJ1N@v-v&qJ*WS(cCy=wgyjXdZm@f~I=_7J`dN1I{QT`Z?8_IQ)8&`Hdiv|%{PuUh|HB{ubm~4UX#wc3pTbbkjt=4e3KP +Sr+piYH%a&cLft_sP=$AkI5Oqmt)bP9WmhQjM{!kq5XnyO&-ndH>L2ZPI^9u4czIGL>yH+7pMi(d^Kc +39pq6oVA7+f(E|Fo;Ah%;VoKr_4D;b>j)+z*VwH$50a*~1lN(sCd_4lbve^C6~xSMPc(5~xp(2r^^K! +)<7IehbmyWMnadXAM=4y*YrfYQhH;4>TCs7d(i%^)d$So=;zvAv1~cYhG_e{4@de>KIR&A7x>rgd$nU +0s{ngw13-3bG2K8|LN!q_0GJ(b^!T!zy%-hVmiPs%+hSZo(>4-J>0|XQR000O85nWGJ +JA4;Zb#8EBV{2({XD)Dg?OJVf+_n+^e!l{x#tB) +SY0K%v9kp`CZRCumiS4oE8IKhWjU#b)h9mR=woq{3RB*bdr*j%Rd--lO_Tl4b=;O)T-dMDI^NrN0-&Yj6RwBuyn$1w}La`%n +fOsX$_cRtJNx?TFNut?ocKZJ9+c)pdj=$~f2aEawu>FDHvzbxbLlV_(y|{hG*d8m&fASfPPcq&=8fax +^70*tH3+dUiwc^K_Sk$X9K<=9|tI?PmHZhD}ahap(gQz3S9(z;dTed6}d%HF3vS44uU+s70%x^dRVS@ +I|@<2I>`yGPLGO=W7t&}KCQg67v3?ugV1p}K}b`Uw=35bBT@MMh0*!5bbYoNO-a|kiE=2sX7reqq(C9 +DuA8CNpj#u$Fcj20e2$i;T6rx6V|@A!`$Qo_`#pao7yT|smwx!@V>gB}%D+^i2FP8CUv@pcX`7glbZg +st#ToWroJ!r(~|HNSuSF|a$yC4&h#h+V>q%tZ@6MAw@mhD%vw#E$*7JAgbR2BxM}sB5>9_(g{eTX#BI +O6Njmb9P`Q5;@A$+4#u3 +Uli*|DA(yaDHE9Cb#4+!t|F=O +S_6$pYl9&)NKLInczyi9F9jng09$KJs@zz;QXpAQf;_v{LOofjvaX(7mnzR}D5Y!Im+jv~Hmg4_D+nX +7Ly?u&npIF|D_d@|GS$7UK~c0gv}qxHT0VdNoPELmJ*)!%Vo%G1gM-Ld2>>sNgsh&(-ccnWm_rX`2Hu +3a0ox!602+%D5^juQFJ7>NuU&-%{~|(6uS%@yoGYsv9N@*aQ3o6gz=bAI1gde%hYI?pYgv>fY`7z)i% +*l2(|5<;(P%`&H2;M=t~+gA;CWGu5dXm>=7J|LQ^H{GbqR>f2zbM-;IJ5qJFNa5c~X?n@j?t6YxPflk +53VnPzoeSS+by=*eAc|rG;F&$EY{X_Elnwf>d2Bp(IEQGNIV01|u`H5#BJ1LT_uzo>3eqyd^(A-+TL=W_$jcoo22C1 +a1;K!DPCoCsM!M$33e-_+tSjEfJBNb^v2j5)tBsAgXQ39ahF;}1Q6O@!FwIn=XdD8Hs#-W0(I7rY4YU +sJ#}FH@(2t1691CZ84P#&b>4>zd(WpYh${07y0po;`bz0|l&1zH)1{zjDq{rWuzF$I+kB>=zkR +HLZfa%dF1gVp#&#qZ9uC2%;IzPA=bN9*mr&1P;q9K%us7DJ-I>hjX9K#`%E80_aug7{U_eN;VSE!9}G +>1=c2EKx}wBh>mFX+1vF`aiXyMw*bwr4DuW>wZkOGq=WthL1u*^JjjqY=fW7UL!`bccw+{u^#OzPT*g +TosK}wk%>%WExdwku11RZw$fgSY>KPb?y$?o+e7~W>ewN|vWnBZ)FK?O!$;m(+9!OVk@45#BJUL=c_}wGV) +_yc!E%2I-dSv6_3tdU#0cu`f2r15Yzs_2<68SU6kAMB&O|ZPN}S8igouu*IX4~rG9}6{>}^Z!AuzRA?UxMZ9<1cXTlpz ++j@`-KGMOwU%`EaX-1=oGyT!ZgfV#J?@!|adO~V&{vs_AGaLoASw8;r6rS3RjLvO@5IQ26?K^@X*c7i +AT?mEIMJwjrg#C<1fTgp`??pwB0*9E$-Bmph6+H;=(7ncQL_Ef4pZ_()V>{a2n8#UBYP7?Ib~*{Ee=k +Ind6$FUZl*ZwgaPPdzE~!fOKAT%TaAUWHR0HQ2SC&SW7F*S*lD@JC|0VjOk+U}H5zIY*s4-xR;QTQrQ +X3I)Rjo(Ql?m$p}s-8yxruoxYX0Q;jRSJeS#BvGt{`%BTerv0O5_U0k3#2v%_(^=|1DsGkiLMx|;~pI +6XOiP{f6@sKvk?rts`e`xAylCvZMMsKyGr)AZp##FjjM&R!a$`hAyfYAVz2o|C$HiawX)|{Bif@O`e%meYj(!GDJM3mEo?hB^rn$dsdm0+*UoFy*Yf2LYWrxk@Wco +(qzJbhVgAT|u?m!CYopZW8_j3)ev2?x#i$SNpqvTlzsFvke +E8w`5JITPa`DZ0V#8N5Wm>MF=Y_PTTN0PaQ|JdV`%m``)|Z4;Zb#iQTE^v8$Ro`#iHVl6EU%{y_HgfT`*&aFrDKK +0&AV84-U9({5f`j6tYi&BwmE;@_!~XY?vV1?fV1CF+l=$%@`B5?3fqVoCg_%|%dgH##yU|-~p6WsSfZ +y0WejPEyqVu$cxF6UH@{PM88gkke(_!7xcF=J?eYm-OfA{cfQ4|~O;0wkzBd$Z+;}%O^Ho~ET%XbHy& +un;X3gFLlpc(~^7|scm$t<78IC=-S7x1R~{Q9xBv_oH3nFEvS*O7!rDeEHx{+$9S +rPA+;+%s|#IypfkUUkM|1NR?4)BvA)oKAMeT6jX(>J5T^3S`H~3%7w@KTia|jude-UFQnNP@jcL_G@l +UEvwp*mn!zbsbIz%bK&M^F;rI~cvR}1LW?=(WI9gwqoNCc-43=mil97Xn$Pk>TH@;9G+=l`ixkwDf9a2 +PGMi$rE2hV=_2n*_s)s+eUbXp=9HpI6t{p!iqYo^SCvW1uK6uW+kSm0iW9N}M#lPubhe=CZ*!$a6&S&sAAd5^aq1PUTx0W$_Da0TADY(d%953L>OlcDk +e$#fupS>r4Y>j-Ka^x;k{;BTV>6-vKD1>p4d1r-_gV>t@gVnKP88>rV)B!HnIcxqKjq>(t)@hm9U!=R +)?G@ZPcrb25yO0!~`Uk0H#5J0ewQv`@me^{6Aen-G`5< +dmth&*)QrIv-8LEj(U$RaENpGUxXE;{C1|zmH&A+^`Ln38SCxry7Lmmun_OZtEOF8ew`$UF}e9Jp-b$ +xb)2Vk0Hcy|ropB;&r-@l~Wr4ob>ryPW7qF4Xd>&>IQxYNIfT8Gw{wZ#~@bOQ6P)h>@6aWAK2mldXPgPohvL_-1 +004sx001EX003}la4%nJZggdGZeeUMb#!TLb1!yja&&cJY-MhCE^v8`S8Z?GMhyP0U%{y#A`iCO4I3I +1bB7>vi(*+DG%2>BE7Y?@*|*Af!rjTPvF*Q)yt7{HC}{%(v1A^PR4}OknmLmReWO{55eLU#>C#WI>jkb-EN5v)W)OKZ8(( +e|LD?2$!$Z$e+7iOABi=Rqnf3j{F;b_y?;IH27c%}!cOPe2;ykv3n0K#LVYo7H&%A_gH%Le;h!G+{~iY?DLL`{3?UMtjEPpxgCi_dCrqvXdaO?DMEmhJ7Nf7o?5`ui#nSIDA5aOb8Yu&8YUGEf{ +n8tC~8gp6hr#^^fBP%zP3fLQLbkWegqS^`4r{3AlkT%#r#do(aX6Gp+o^iFTADut_?&+j6_BOgkm7HW +VC3@$&ATZ}~yX&-EJJ@a%`=^ali_Q&;m7InpXXetmR{Rjxw7eygMXH~=|notzYPMDwqB +?PMTL(6tLEUOcl?s}Dc-_yh1jvV%;GW6Jx@4tHy;*wF40%9u^#-7!r?3$Qnn+6I6)wE +YC;+0=;1o+1`&NC7Kx@JfM}rLB<5N?3nnD=I6yjr7OXSLm+{rDbaKY* +Ei$LmAGTOnNo;xSbJTJ&bfI;`795# +iO(M_GienBBMna?5i^o&O}#Ya7Oei4{Ft_NQ~>p93+=~*~_+%Kv7>|&B78Won>*AGTENbCJz4cJJQE6 +dc8Ial&@m~)j2bZ7`-2AO1oi9BTRUG->X;?VOWlsaxi_K?v4$O>r&lZT#o&rI59v9$a*@o8jU*=Yw2k +o86M&f0KBhgpZv@ZM?n_Q8qjW +={8I9q7ZqivBpErtRlrPrLp`^1Ajjt!}Iy~PhiJFa&R}Gw`ac +!JX>N37a&BR4FLiWjY;!MnXk}$=E^v9BS8Z?GHW2>qU%{yuDglbx^rMFcXt#7L*0e?2wZMYFpe5R7E0 +F?8#dQt;_ucVD5@n}N7X*nd^6q%gJ@@dGB+2`(ZKX0~)rrz@DKuG0MYd9X;bKj;+-%5p!&psLyk@$XB +}p=y@y6QJa=nIKvz3w!VGZSV(J-y)ni0R_%6%R0XsOang$-}*H@9wfEynaKAG)^ohTnLL(ZkAfTK&u0 +7w{w}{=>{|Y+NUP*6uUO^knG*&XDGRtE4P!==CD +pvNzOdfT;_Jc~#`oUzeA!AcCP4HM9t4wf~LUx +x?cRx>+OQ;-Yz^Cn*=w5*)3BV0SZ?Bdm60WPRE3!@)N2F8;-6h*X(Fhn(Pl` +13RgJ__x5h9{l|wDO`L`aYz*@-GA7w6%VRMrvjvyx1Uhrm;6%1z +8>JXcJr2bh}GE1)3gmuA333MbQ~>9iqBMjknqcS|_51_tcgLbrf&nmkH!a-Lm3zOg#k0<^>dR$!nSvQ +4vtOE4gc4z8VUYP;XNg|u`5Vx&<;(`0}u$!@#{mw4~!$AmiBa6NcQ*88oQNPE8-xgh61bl$I9Fq^J69 +Hiz{i8<3Q;CxJEKrSghOFj-fv|AQ~2AK7`0PT3u3MA|}66-W><9_{agFz}LVB6&agiaY(px9aVlW?6Z +%>Ea3u-vtgZ7g@C;zEyGXU`00wXd39zHdSgq +6Xh!A4sk+1B23cgB8+0~V;A0`V*0oh_%F(}ZVVI{LjZu{%!2;xYH2dw=)K9OB3bMfVg`U`gnbJzTEl> +@a~2_PGquK^)VDwyDL^8;4n>NU|&<_(xiIj@b9$beK4!0|TRdkg;xkXW +X1mYdxJ82N+Sg+{|a&N#m>sj1_hgf;+56!NS|mj>91u=*ff)PLe=u^u}thVk_u1VkB6>9a_Wi9h(5Ns +CfuuHgwWwoWdARDAo<#plD0vHHE6)>1&LH5^*4 +f8?TBhv&r;@34p{G6xS&&TQQwu~sn+J?k9DIi**a3%ulE1jhmgd>F*tYq`A<@lDqPf+04S}~+L;Y31oV^G6+i?en!IK~Pq` +5ld(OT@d+RJ3Rc+7JLxLx>8&Y#G`vsB)N;ygC4MHTev_bS^lcCB#$lcK5ITY>wi#70|XQR00 +0O85nWGJFRk9iF984mR00419RL6TaA|NaUukZ1WpZv|Y%g|Wb1z?CX>MtBUtcb8d390EP69y;zVA~s; +UFZq(ZrKi1Bn+8Vz@44w!p~Dbm?@6`1IBVqe7U|B;Rk_0ZcwR&IAa-N3YaECIw!B3z#!yz|_L3B&VKJ +hRonF1dLCfhzA(8nhgO44HLQB+<7#9;PI^WfePfC(7)TUk3<}-XVBc*I +)FXWWv01*$6)rWBIw-Sz4{5v<7@6aWAK2mldXPgNifE?=Ys006!X0 +00{R003}la4%nJZggdGZeeUMc4KodVqtn=VR9~Td97F5j@vd6efL)o3WAjzTj{@8^|Y9wbe@Y3*!ZW-`m!fzt@FYxzQ(HDa$`7`(Cwm{;SqX_Y(&Xit*#Wyv +cXoq?duK)nd6=AiP|YO^a6q=CM$Wx4TycJ_(NN-N^6^FbtyCFLSxDcQE|C;MkFtlgyfwJvd708W0@6M#cio_pC2y>fJJxA +aH{!%GQBr3yrjffy(H_O9YMD+ct{8Hn%h>_WE-t@w1Uy$VKG=MxXnsh^WzAC;j#dNa$}v<6#?d?0eR8 +0^1Gc*cb))|cFG8QN(m6m=Ym0fP&J>%bD$mn0sD!u1Zdh##LGe5(Xzr6E +?qbAl}XApB4;@gGi14`qx1EXVmV9#M>WlAku@$22ffNVZ3&t=EsO;5Hg2xeQirHsp4G<#dVRvpPkz{( +5t``C%~k_qZsla>ol$n^uc6>@vg-a05m_s8Z}pd*t{J0(7!E-xx==rDKC<#|$(`okX|w;IofqIWo>11 +yC;j&n%lE)P>t1@5p23Wbd3@+G*FjC}r1RmMKmriC +5mmK3ZSHl;$2{gK>is8BLbOEy$~gMNf)geoOcE_aTD>42WezpD>jZ|V0{aAM4bSmVWtsy(5X4}?7gBD +LGdobB!H6ZcoKZ(pjP%qayA#Qnlz4)%1d^So^C&cUjm(kLMeaU!8yhUUO2mup|E3eqi^0w8iTDooUpR +f^dfQB4heUi`uVE#0-Z+z{qhVj*-Hl`G=-FRGT)kfnt$I@WUyxGSLOi+c`18|U*YgM~p#4KWl{z5$*n +fz-9CTeRef=6{S)LOy^2?okh;D_n&`>1}B4*kK8uv1p4L{fzU?q$LN_to4Yy_>Yt-p%MD;%EpvYAkxK +~}q)3r6TMl`3P1-Uuu_(`l}l9H?GDyIX%=AO6L8?Y+*N*+OXoM}b4CVJM=3glVvaDSe9 +Z^@@wh=1*?4e#MoToH8nm>|w(2xl8P`i0bi{^W{>q71Ep9ssvKTt~p1QY-O00;mPT~Ae0@IQC66#xLlMgRa90001RX>c!JX>N37a&BR4F +Lq;dFK20VE^v9xTzhldIFkQgp90HF%2Xmzab|BHy38{8kE +8j8=KJWyN6!)R}4VlT^ioz1tNsOL;P37v(MyBi(W*cR?uALger&5T7GK&6Mr?BactA?UtwUguO{s!IO +kzgVQ|E@;(w}ArlpBrHU+x*D{VpdLcf;Bx0#{2|t9m!U@~RScEYDVLGZ@?(!@Ws+!Q2e-dG&r_|SPAE +{t3as_jYvb4yv#P;+@3_j1xV)G_lWfS^!Cbn52-e$$itW0BCRO`jhJdw>8d(BCufB&q%Zd;u9Ohz|(NEnPW;_{?_(b(s}KF0=SI@H9x8I49LTOx-hM#zG-D1M@6 +ei){FE5Z;)8@+mS5&nF7cK+tw+wko4)#?92k6Z-6+MP^)TfSi@+BpUh+I0ehc|mHZr-_x@Gv}@;<;danUzI=tbYCc{B_@=y$_dsx_Ul+`DFU{>)-$UXbpp(yl +4Koh)SRmWjh=kLC28pe3fOhCC>wRel!9?w=moDKF~LZA*B-e# +G!md4g9N{$Ir$;n8xQK-#1!q$|aIM(meDpKg{OWkH>S<{9=O~!Y)$<3P{$*P9BGw8HuGC2kc*^Di|m} +XwVIi25uWVJlVmz%dN;|#G(z)c?3GhRK~bsX;!czV@u>$x+)c4Cc^Y*zkqsVH;M*UhFBs$33hZcpBZ) +?u`I{mSMd7^e+PEqmmlWWkH+-tJK&hsA>bv*@{jM%Pf3+Vu~>olXP;Dvgu@M28((es92Sy~+4LW5nPt +h`bS=s}r3Zd37r_>V5c`agUX*Y!L9b-tH}%~x6}1X>)B}!r^dOeGeHK&?N$>6BD9KWxpQ~siwnD*EEe +QBQQja%9u`>&7JX7t5N^RPYaMaeL6^NR7Ha2VrW!{6PumWTP_x?yRk?v)lrKmt`&vS`8rx;mUGO{b!Z +_qy4OEA4ws%T#G*{l}7+3v98GiWlCN`Y^h9Up)Hgj$2G5=CeR!ex7Cj9$Ds^CpbG{_^hZpW(&X>8YnD +3fsxcqzHMw2J5*;{=GhZ{=ys4+h-u1g^aeM*krM7&%o-1ORxsm0Xr%mzSIJMPVo|cT+b^e8A-T=cGsh +dj#Q!CZp#AsK(t`DqEBE+d?k|A1Pw7!1?L9-w@sjVo{*>#MF8m^bYp`xi?jZ? +J`Sb1Q>3`#Ak`%4Lyw5?uvuiY8+PEeN=8WlNy(?L&DDtHID=@|A%6@tesTe0J(f75CWGi%uhLqwH3K&y&|0gI>{CwM>V;WB0P- +kzz7cZ#z-Hr)5%N^XeBXoLYPJE6tC=_Mb$!s{G7)L3DVtx9+A4=~e1}Fg>@De2<>ATl)>@@)Wka_nWV +~-zF@%?BlT(<>1Z}idqVWX$Ciqq6yw(!m=gELiFLHEtx>EM!1Xv*kI6h +|5yFR81LzD_Lau0bwl!2JkI#S`sHZQy5bA_XwoXKZEFF^ah}`AY~O01E?bSLdga-0TDGSg21gSg5)W` +om7K7)H<{9=By}=}@ea8f)!7ZLHwAqY&J=B3Q*i^Dr0-p&ZPldq3;H +L>b!+b`_nNNJ_+4z&>`l7}!In%*(Rfkhhi*$?Icn>KzL-#p1~#)}ASGM=nSL#vHm9JIU+ia|l!6jIUG +fjWePH@Qki2HVr<76=v_!P!62gADI>6+z662;#mKBtVTlLT)XCZJT22ohfvIzXG3c8EpsIT?B{FPUx; +`0tv5%$u`zvdnL9sl`L{)gw7Qo|W_X}NMu=?7v}-AY@EP+R4dwxHUGs9BcFwM=KfgGBsMYcoR1Mss+e +Y@@0b45Eiqh+_SEm49z-X31BkAPzQMhs)0$I90b9_54|8KG!X}EUIPq>#<$ +uCfNkK(4L<gc8U?>1_VFWPUrJ_t{7BQ&Pl$bNI>pI;{5SypG;6vpzH +LDsu;+S~nyT?^wB>zbf&$XO5oB3f*`U5C_f<7Q2!?A>spR7BfjgY&m34rlS+W-u(5YQ(Tka{z;U@B(z;o{8#C38Bm7{((q#`snDTRRz +Adnzx;>PLAvL&V>^(G)I&R;)2M!fA}bsL>%fRmG!0S8x@(!T}aG?u`-gZ9g;PCvPEw@FGleiSpTHdVF +Dvz^F`!x2?55`lR&Ld4`tQQ}0Z%ACRvUbJEmBw)63kLI7WQL6%g!l{OEwmIytplZ?HHKYwg>3~cFtcL +RnO(=ecd3G~fE^nw>16xXo)S*6saS6s>(*jn`Oets5T#F;)4xy35x)&N>p?8GMHBme9+-ekT0VRt-n( +F)vZ35`4Ly1x~U;wcpryX-(c<|+Rrj6K%n9ww3B=sSjES6vOA{!*Rznq +8v2c-jJ2Lo#ong=}3Tzkzg$C3|-4IjON|qp6?O8D)I#r)XaAknNI7D$9G}=u`|h&4Nupi8LxohF}B5+ +7BV4VfoTR#f!2R^2ZN58v?zQ4QN!_;jM$MWxb3gY(Fc62DTuLYZi_*< +#IGxg1%e}Cn-#3UV^^`fzA0>S_q%=ZI?OQGt>~IRmCIfwfK&VVpft6$qLppV3^r>IqQhPUl%oYopL7i +76(V?xarHASTU*T0Low!7H9p+essl9B|7Grl)VQX)VoLAfDFgH{5QY~L@G`+-#N|(e&YbHBx&EeyxVU#;>@nedaUFSnDrzXd +_m)&5I)Uo#vnrP8(P(T9qaZV=7{?EZ4pVmWkP!J%sM=2xK=J#GSXDoR8ZR;A +IzDYWBTLAs~%0Jg~)AoAzuMC1YqNs479#lisXLA3o`3UA{rx)PACE9GbNL-O7X?&n|O7Q>YE1qQSSlK +&ij^xc6yCFYF>F6~Jnx&l1;wwyIIuN&ac!Zg}5DxAnxJ--ebFiItWs&pf +QymelJe%lp=IJE7?8n*A%K?jc`v!W`<-i+f;6ZyzvmWU0uBhAMy7T0V_PNJ=5+d%DsQs>dd@%@az!Q@ +$h?zzwM%|kr#Z^QnE}4_-1I;^7BAJ6f2Kcwl;xZ9+oJUJ2cQG$SSid&ws8Ahdsqk3P*CM$d(0026Zh5 +0DUCA{f7@Y$Pw7BB=Kh20Q2!)1>k|Vk%^6C$|h8NH$L0`yc-Qb(@`2l+=b7eDZ5ru)~TobwkbHvCb7w +-uQk0*-{wcYROAyq#Vz6=Qz^We7U9f=6H+AISh49hL%@f +f)xflv@raIUfpjzYeqvN+wNAvtq;Z_?rr7#e=yKzOOS9w-l36uIOObz|jo&_tsea-)BGB^u&rJ+2p}R +8f7J(%oA?5^9+kds_%evT`{gBaKKFDFe&BC+e!eFX4>?Wxr-GP~!xL1QLnR +|RL0v_5BH~Vwl#=2`7|9J2mCGkJFfe7%H)$+B_mn|(F|tRjYSCL!Hj+k=sU4z8g(4>MV9nxB%c-$;C1 +`L7omR_S@J|g1UNm~v72)ya@wLO&D$7Im-l#2}h59Fkb%b2h^q(yXAqVpdzD4xR4mKT_06Pu4^0i@3S||47LUSVVjCxyRDv#!})Vz%^QS4YM1F_0-y#3{m+#RJpZfZ!yg +-qNvlzAnN`svP$!tYn(0NTTZ*dYcw_Ee-8_aJGvKhBl>m-VNePrCK46VW#(_SfL#64gTyG4o`ZA<=b; +lXj++qsAf~!LLeIr_Vz%93;?N=3KHl1!I+LCe5>i3rNUYy|(c32Od9I7s%v?gUPPX+J6SG~t9CC($@3 +K%UN%c!DVXitY$w=xXdutx&{+P0nL#FSaqYGEctLIcvAaO2{R=12DC!_n-hch1!Sz`Gr~s@&5wua2*t +&aS@qT#WY49^sk*8)ecAY+L5JzP+RuHH=Z58g&<+-233e3C0eMBMxdd;rpVIMM7ea&1%GCkCL$LY5V~=|djS>A`_iH~8nud5dK*S+n$v)@qKy&L)TbJkVadf|n>hD+AZ$# +pQyV)oQffi8Kuj7^Kz3i>~)>knq&Y8@Qet8E5bN8sb5&vNTr9FK;-R2q8LCym3uPRpG=9o2_EOaC9Av +lCE%Q#C$R|H|6C^UOvtCW2vU-U=3l{!mn~K<47E|cg2GReC& +8>FOJ~2sL>zSnDOXZt|w477DJlp!$Os0M{if!OZkh#a{^a1)*8f(xnJ%JlR;Eg*Xc;D-y&RnWzcL}S# +U4-{7cv+YeZ4D6W6y#bbnC)HJ0SVSOK{1>w$|Z)U%3R~an`F;6nJOsFOLq!!!gQffyTT*w-W+du0S@9 +wXL%)_X0ylFcw%4(onyl%=jX5W4?49W@w77?o~uaRWcerSagA%%SM1EKgjce7T`tD~`>~{>cNDd9+Xf +(5MG2)CZ_u*W0t40?jafh!vFx|TOd?-F;C;jMSQ)=hJBqIr6|c+XV7Lsk#B+QYlOTxTI~Wa18CX+XmT +RY}uK-Tkf+3a%BLKT=P(6&5)-(ML3x4+I^VD2@Mvl*p|NNK#dh*0YKDf>aEV}@KqU*Ms2_SuuA0`lEa +JoCIZy4(ssSAgYKp@iC#BFr3u%%8il~~3M6$s{r;#3^xa?YlBKA&_WF^7R?pcBo+bSiGhCN4T;9=Gnh +W0d$5+oGru*>PaNSI^0st1C1~vt~kn>`tWdhBi0cB_)N~dex|`zW3N0!&HtNt#yp4n_bT9zSnxv=7M& +2R=(*{_n2STyJJ|<*`RS7Z@4?;A?DW?zX+u_x5gY4qivhJ&3cxD(XQnuT_QyWnk%O`$hh5y{P=_}lZK +QAT3~5hvCs?WO|DHIF#lnK8gda3^mNbgxjeJ-GDZ}l};l=E1Caob7_P#;p(0sNlG +DV1gaP)E1W>RA}D);ErkR24$@F0GHjy|*ymy2oq2C`fDcG|#N!;z@Y|fVp$m1@FY%nkzJiFL@k?`$&< +Q(#Zs$fL;Txb%O6VDHD=T=@CT0v!GxwDJM0tcuM~{44dcDv;p)GNJ48KG>IcJQ$^9>3Iq~t(>C#PcV)TBY2%UDneDWz5dS;Jc0TMjy}gV{keAlZImhSYc +Rn26v{D&TNU`O+mMINt!Tnw>cRMb2!&2(SR>_8#V~e|_-~E+qvm`HC!=+&LlDz5K8Wx_i7`r$?rCp5t>4>nQPSZYRQM +efhAJrywFgoKt#nIIjVCS_*xxf-_eVb7^C>=v~Y;P-V=K?53hy~XxHL0@ip4aqXvs*Gu8w)DxK*462bL9i4z0Y92%f1dd_4VX%nU +Uk?K^aR~74d}W(WooBW{Ieq3sEpiv=Wu+7i~H4hhyv3oLGF<5elg0~f+;F0;4oRHq240=KpQ7-Ne~yS +2%7dx=|jE>!%q+<8>zWDt_Z@%TVd6Cj;;(f+0!1~=+Zz~j^OBDq}fRBQIK=7m6MXrZMPUAo4nPIJkd` +yF%o|RzuE_tk2iwT;}>DVrPlmxdOvByx099R+ckN*iiB)ywnL}5x^d^cm*@GtMNV>a^SPX +yrO}?5DXSpBZ`T7D6p6KZyK2yUZ7zi#(*mS!ji;$74P7LKG4Qox>XN=<%vTI9b?CJ|A-l52&Dtka+q* +pBcOyCJ{ToPni9Mj@ZuI-s&BuDJ)14rdr%k_?*5@9GJ32Ax&;&SC>Rc(+7I$5H3uZJyIQ +g%~-j*;US80A4-;@DIL@>NEh&#+`LaNp)(S7hWQ+bRxkgPqV-IHyQJ;vy0_szRO1ty|9I1%x$Z +vG{S-9PfE`-WqDH=27H-^{njrp8wj!O)D;#|;)|pux; +AB9sxu7(0<2Sj|x@^8X2MS)}8Fnr6s6jCu=`ACp&p57ptVg=p +k!v7^u&XNZLg;{Ei0j@Xd(S1?UVDL%K?JZx +#9%X@vDKdzt&FO4{YU_-Ta)kjyjzb^HA4;1L@u6B$_KnEKe-i8 +Hyi!|DKh0wKfeU-i!EWfI>pFP|49Q?tA3ojW5b#v!@{>;nz=35u6(Ovz4^pzQ5?_%4* +33)n!xIt`WzIjkVIN6p!$o;PgHz1du(ZLK&p^6azQEJh;Ees4%AAywmYOy$bwZ}L{X6-j14G}$j<3U* +whC(`jYkuClPP)h>@6aWAK2mldXPgS2`ygOnJ007V}0018V003}la4%nJZggdGZeeUMc4Kodb9G{NWp +Z(WCAJXfnhO$~f!tji`>rhhC{ml5aIhJqAaiBon+VCdj$Hv~bO;4{FShqJ +riuieObFw?%ZHXHhM(NHAC~SO|iYX~z6R7(Dm<>%V);r-64E=+%O42P?%P3ZGULOJ*<+n?5DZmJ~!&r +;P9YB)@`*6h(P(=NNy&dj-R<{qU>c)ieC$fkcxyqi{=?^{r93-_=sEP-;RA05uN|@r-E+I~F#Bf|We7 +!|{g2cf79jl!UK2lQ7{Uk*88_ys4u1bW>HO`&rsANY{K^fdI%ZlKP`!IcL>|Cz=>Se#Hcg2M|UD-n>{YjVqZrlr%oSG6d}m8h9H23(;^$aH;MoKfDGB%q7&|3vaQ +>n>VwC6s0ncI!Qi0C`8FW?&=ady_Plm>cL$+Qk<8!1o2cMJqq!~pnPHhb +Sf;Z2q+@723PP3H{t&7I%Adcda;BK3?*q%GnA>|AVnb=!ILot*EZ5*EFloSkkX`2Iz%(tGT-4R +=qzGDB4sjKq1+%B2Hx-&o)*5A+Sv$(ZB~0hUtW~a?q>I3QqpfJ2Sc$B$oq@Ge*bP^ynyUbE>V2*pb92 +^(ZDgZsbz3+z&kP;p)q1q(ZOV^WC$jGW^t69pSOlOI2FvCLG0GYK$=nV{lE)0SgC2*l~0NH%tPrU-7; +m?g2PUj>MA`mDl^-_#@cToc(JJDF0D42|KDXa6)7oZ@|EI7qCJ9wNF^BDr@Tu`l2#oOjvIhW`u1z9>! +2Sez`L7ej|Duu9G!_Ha6pxw{f3r`9C}v8jj4&h>Wf;XB8fs6*J@sWwBSR9uojD`ugm3l)VbQgje=JQk +tPgd4Bttm9%PANP!PE?3{r7&%Rqx*4heoi)ooLXiyLaQSuL6NbPx-^;_@9A3E?`G6T~QtCU^;EGx_P` +hj5fPFbpj?8mNakr+fVez&-ndGh*vftb;^7Dkh^LBcOQ6$sJ?mxDwK0$g+F=bqSCo2*hh8GmnZLmTskTJMLUz67KoQ +W3IFI(4j@N_EyED=C#eRzEbE7g22ip9O)DYGShaM8JhXQbbiU+r>QivUtY-yGz +1gf83fSgKLt)Fv|uA^q>`4D*RHUQ5w#2FN058)%wQuQcNwWyu9z{22=mTW(YkIYBRk>R^NvDiT8jC4bt(n!+kG9IVwvTZmW(Hu!^i{O7$@kO+0EZm3IBA=2 +jR$rDL@#94{L%HB9ZfB=BTnq7d(0&hCHv~%CXCBa}s&=dgBNI^U4hQjC(ls3Fi9sSVQ5p;EL?+Z?B1K +%T#Ibh=f*Br$eQ20BUW27O#?qg=tzitFv80Wyj9={eUqm}nzcyk$_h2o&J2Ov0(^?VSG8NdQ7x{Cn$l +GO;{q!k6mRw3p&zde#=5)uN!iKZGSug}R*_>v@?Y1q86Q25ZCbf}zaGD074kZUzC(!tyyATI1A#Zxap +_&gXtdtm%%-cO*0D~v;+G(V@M4|g0YXp*G`1EbJXaKQII6`$_s@u$KIg043tEVHz(=S%pHdI1|FfHG| +~IXpcaN#4$`=UqGxW~p)^n3bPgpDhFJ{g?tQ+ZguX7L$OyCT9u_;=(0B0ifqx)R^*KeRH2@eSHAxi_r +>uoug1PD&mcgL`KU$wQp!T+FgI9SKk&~V#6Nq*rB;z59E|VhTuy++b;fsQm`F-ZG>uYCsBWidh*_ld9j^%rVSOm=eKlv2G}su6F?Uz|r&;(jMYM +1%GP*Bu*!mJoc-vl;qGn;4ekiBoR)E|OIOnmT_^!D1d*rdC8K%{eyiz9hwV9J1FhwO$ +D$!;Pkkq4z)5#2E9jp(B5hNA)_vt!p8N-QcEEdQ=9@ +L?aKnV3Vfo<_9O(HNmYVdsm_Gh{nBPG*FE7$Y9H`$KvCEi(`>wIZqi8MZ`uE%W^7(SJ?Oxjdk}e +B%uPO9E^!6F1QE5YKp)GVl-e^^*)+D4O%nC>kJCo89w^ZEDQ?F}vbK?hab!m5lJ_J(%qVVQ^8>OoT9# +(jv!x(}RxgdZ_KE6Du1j$6Iv=Mmvczy=?kKCWWh+dq`XbDHo5-N6Sar82rhx5lyeHk8|;Hh7q93MTOKYZ%< +RFLCSJoR)+=BJC3v&GSK=hQNu0T{0DXX~PVRLbLUKD*4)yGy_$zWMM>k3V+$-kgpyKVO`jEWUo>oSvt +#;F7PZ83>*5oqQ17yHj_ewOF@y0CtzNpr3bC`1Y +NyXv?7abdAuyM};hwrWOfYlxkOD5z`U~(Z^I6^Szme8*CjDIyifPXSKsbEkDYCRc)kVhI^P(2APcBg8 +k1*Y=*k$wE+y#@xL9voJYmq8Ne=9V^jt^pu~m5ondXfhj7CR0tgg)GA+8JI#(qoyX?{|v(5ScpMZ&P8 +~s%0;?n+0$4`(ft=jbJDbk=@nyMaS*js34Dxah8%5ncF4^9>gA;o#XSb$oy=Sx)-+GR%LG|mG0$XV7KuV1{_OA+AWu$S +XyVR-%b4>bb!$5bIB35#kztR}*Pn8Y|y>Wbl!+Wy^1*NnJ%^K~sHiUU=7Of}0r@2)2QM_cb8%qLSJqr21?4ebC;~lE&a}-_&Qbf)_Iv4{M&$JsNu_VA^pf{>&k}hb +MhMa2C9RlzfVf0QGFpFWbrO9u0=pSv6|am=$|`c~0Sl^-3Jln7wvDp{9vj!_FoihkE)so*$ne#z{-5`oL}xqz-tgwPAjn(fptQAHX#!yDmik23W!cQ(HP3#-d +fy6@!3_kE1A^mBb`cAmXF}ZL2=$tuF2gzAJ*TlR6x01Do-WrsQ-SQlZ%nwCD}r?`#tRW2Pz~qk`@HPA +Klw^6F{X`Nbl32W)U%xWC;)scx||xnH2qDT*T{Hf}dRcvQc8Ylwvvw-b&BnYSa%_uZzI_ZGt*hEMF%J +&{jTXnG=hZ#>x0=yyKsG2IFGSC~F3z)6xF+}p@WUY(P9_cB%)T_YHu&A{qe)rQ4k3&t*O*Ti|E-($}^ +KfUb9-TYv0Iw02_X=2k@S`3C-aqdCAf(M_`B&vvK5BC0dG^I24e?e3 +F;Qy?qev4uz__Jfd)&N(S@Y|dkO#TH>O9KQH0000801;hJRYtqmvV<4_02^5V03!eZ0B~t=FJEbHbY* +gGVQepUV{7-t16NrDN*fs9*b)ru^wcxST9x;i%OE@o4hV7u2^1G?5Fb!HoN1E60&B6tlLIbb +Edl8PBv|lBu7VLi_yixpU-N-F#_d5W=%=_69gWtRV{r**U +{>`iN|9SKFZwq?#qTcI^m!fF()ti<#`KsdHT`L=X_pMN-)!*|S4xn$o+qE#KywZ2?x}EL)Zoe~EKi1G +Kjp(yne?&nEB@FHrwUxCgUG~21-9%<;88c%SvI$Qwp`K@;x=wPd1|D8F0tDyUVN25J +f0JL?a0VcW|0*+i;H5n#5Rjt6d`tu2hJ(nf;6A#^6{0^vt!^57)BU=51E!##!FC-sZR5K0Y`;%&8ywh +POD!Ocfixe3RA`IbLfiVUv>pA;gWs7Z#SS6e@g#5)oo3Wci+AI?u`AwxAKm&>ml50 +cE8~@D;r6&XjKVk=OF%BLyRbeLFee`D9eyY8C$Z;|V4Rme+e$b6$cR*S-I#HZ(QdH3lUz%4%xVSY+ +_(a_ZOMB+Vrih%w@zkwdI*)KopJLu=j2{bnBUJI}*WH4LVkJL!nW~hl7A%(Y^{go`pO!w?JZ|Pq1G~j +l~-PbS{(G&j}#WzEKp%}tpt36XZZ;B1-f48HSD7UbNwtP$0fk0hbOs-R3XmBE|l|7RPX2Wr90mB3@u6 +vZPQ(xjxCav-Yok8`yJ_`mCS>$z%r~+oPtIJRbj)|fH4BbFB;wH0LleyBD^zwcKb7VD*H(Y9Fw&mP0y +UJBN3Cw2{ypt+LvZMeCCCGv68^gR^`(+5+jj3n%5B>#GvdPgw0%bxTNv(EBF1a4E47E>o?EnlC30a&E +ZbXTdun`nl|?lITWMA3`Go((3|yqS&l$1YnJBUNQy;ei;~;zK7syo-9Vq0#1gn=fPd +$Wb4SPt>uOSRv?vTy>Km9gwZ1q=79wRvWq5~R_3}jWbSvFI=S}&Mg7d-r7sfUGlp7t%FjUy6vLk|U{6#n;yztdI(41AzmuWB&DssnXKR~-%ciQ +4f(tVMxKksyy9#L;Nb0{ARJ$od5UI%q!V_e|2Pf-j~TwI@~zd(MEE$@Fd_azJ$6a;QTf@Ifq_L&%q3Pkd+SI2}@u6`Ri(kDG)rs +5|;-S#P1p#qhqv&5_}CH#-r38 +r!Kwji+88$?>I8Y>0&3eBMG9RbO;l3)b@G){4fZj>A|>zO4AESEv}pgK`Si6B%I9DbK^5D5KPF5RM!j +=GMpkY^(le$A3Dnc&fF$VXuX4o^l%6VF%#Wg-JXsu4&OJ0#GtumX>;SHaq@{P1b=GnPvIc9Vb +eh}C)Tg;;<|tUG`Mp^HY&bwzBu-Y7IRE~hQ)pDk-QHS?qHAag7RDAY*s5!d0SEv69*0OBvO{Ba*!rM1 +KraD~T!rq2k5^QnX{yLMW08<~3(saO2YhqO?@xug&=Idj6`37 +4(eU^q%4so8vTG~<&GM~hD7(x7G?dE$wAh7rGBa}4@dlUCma{09d$ZO+`g&^jI(JhU+elI6Lb#fa@e3 +d6H-jS){{V~T3hX^KdJi#;yfJj|p$p@h);17*$iB%NY +4W)5Frr_PB?TTcF>)AYpZOK<1;A$1vp^*!zmE@ZdASe5o#G +o4~4dR_#mqgx*`YkF5(G{JXCGvaSk~JMoLg1NDD*WVxEzbYZS-3g}9|v;zrzI($Yqc@udP2WmfE+#kL +CKy&co9w+_Q48R9p}V=Vpt}%z2qPsTTydODF~g>exrODxpEPwkE +P3A#wc{z<8&3y?r5m6ey^y@;{0d)9kcU|l!{wl5UGuqv^8=5Xu1s;;M-zH~F2$mXM=2;I{4nSZ#MLG$fJolp0z=!bpKD +jvTxhPSVZCi7UO%@O9?P);9kz^QpXGw|u{>cs;Vn0S9By4k)=vYHK~$H@};f120f2;)?o9)zM4B2b8e +u*I2p6RGZG`3`iC=$Nx?$nY1;ft1EerZrB4J6<}|+ERK#~%Fx-lyED%R4sg+ko59TR)~o3ska&fUj># +KAZ8KX)u;N*NRXg{hABvrO?rHDM=*=JUxYIO6D`aMP-0!DS?xDUDjjS$?Qz5P^J(aHm$-KWwuT}S|8`rSu#IBFw``E +b41hWfcFu)#1p9pyXP=SJ?oUNzxSYH(Vg@*1w2rPb|B1Z7U>+J*aK3d{Rp?UXQN{)G&@WvG>#ZSGGT% +QeS{5-^{SM&o|HVE>&Q@zs6o!+^&oqj!k=AaQjx97N6fQ_+)q{F^cbZ|N_k&bGB4d0NDsv1HL88+U3x +yL=V(r*Hy*Py*>`(Ki0d^8mpBZDfE8F;z?9gEDsQZGu4M%Z2Gcq_@zGvU*;k+TqE{<9v)w+v6+{%VlyGJ@-4710fcpY5sbh}SPLEaUcin|E?^-6FT>Q3~bM? +xrvGpZO?XM?1Qt8eke*QABiOlzVi-A4vdAr~m?SWa~4mW~&~T43m9*OqMQ>?hr6`-tV+dyDUvE6uxiY +Y+;>6WVqh3avm1$soK37A^>;;#jWhx!M@c3TC4UEtEFG^fzuY0X#h_i;e*YCAyD5DDzLtV$Ic=Ib`y* +vu5KR5s{4Gef;x|d9SP%`QgWSqwzV|<1PvlRBt(d-z#hPgqQ%33A&kuc5b;3tLhE7$&WwhKu2!+hF_7 +^h3q7@bjTsV4)CF#j?ZnQrd1!HJZ77S}WD{|XIJgGr;+AQ1KT60tW2=q+)V|qg@0Srkk_3N8@_;<#Uc +)}a{A<;~>+y>>$_z;zc6MP5Q`kZFdXn%N=o1Ntmyd7@?dPZu_WHo`m6BE0Y7a^=a_My1x!)w`I~NMWy +7Z?nG1?BAmJNDWGpe#G&i?sNSi(x~*udv1y;YcwNoxsc2ej?5}#4THet)Zr+mJ3Cyn#*;}740x2`b&{iQ5JZ}M@wM87iLj?0vJ+v_k +P5LsQY2v{7Ddpl8tjkOd4P#z#-+W4|>DH6rpgy^Q$WDva!Z6BJyT-0Ef?1s=qx9T@FDihpEc+!XxS6- +?{_{PGCMi9JJY<+TP;-59tOvc +I2B_%%c2ZC>_J0B_XMw~|i53f8w%t8HhMW!n(sevUtd7AVa4q9nK2bmnEpD+~Al|7>jR&pU59QETLnop=LIp|A;0%SHA&{c_*PraiM0_6; +QeFYl$_0z9FD_U8|>g%)9)32cC*Na$J$}gz9`YBecLd)$QS#=6ePXE4tvD;CYL03tthX|*QKX-R*OOc +0$AS5fEH{vk*-lq`SdV{b&TU%_(_csKe22nv>3M=28Yk#A<8>!p<7KdWPS +_2zlR80i7s*2spGU>@V|MqvLzN(&Q-oMEzTLHZ=abB|pZ9zoVIaY{-w3chB5+K3nF0<))s@`$Zw#m@N +MP|A&B5oUBHY-iY3}3CJ?V?e0)6RfP0HCpWlNIz$O&*N-AlruaI5!CDb#XU8EbGhG+@>^crV<+hAZa* +Jf+Qu+4sXwBd#5x!ydI7=Q6kaoj@;_V1Uy5HCacA +Poh`PgPFY`GG6yn +HA=36*R-%;nOa+y)A;fb3)jWcs>nFm}vqkYkA3ad{&YSgX|@;^6n4q% +Ak_xwO9k_K8ZEYqxm_QxX*&aRE1Hb5Ny1+eFYgSv9ctZ6kKGPxRmVa;2tp5*s~8d{g6 +8Cje_M$SB|vB09G6}o==Gd$AdEb4S2>}wp~U$bZ-^6Ll|)`O;`hOQ_u0R}wn0j&jaR8Pk(X8um?WTd2 +Lip^aSI~YCvJ6`?bj2lME#b)-4`Sk#6E(pBa69K|sU*j;>*R($;fyH +#%;~M1o`ucDV<}>N^ND6ejGH?0sMu8s*JUVH4)j;I9KcVxEsHQ{_lhD_@4Aw1cpx7@EBjemWEJmSydq8^L*z*sKk +yA8eyc9Ztyp<(4JDbIAe#2eG3B-S}6rzpmK}CxUJTjia`jT%SpdJpdg9n%QbjYPcVBSf;Ae^Tpo#-=C +(a6t$c&yDEx{)!j(0V!)n)ogo`e!z4sE;MjkRF4LD}v`1J#4WUjq5Rb*H@E)s6= +V1j0lpzue4iZACD*rpIi%^@Q;@_^8(~bft!YALB!!$y@Jw9Nq@yYVj!Y3GJN2|K=<>`E3~67P7(VkkH +JYL=SoO9O=tYQgDYV?#CJa&p%SGXB%(VpE37;<4D%8+K-q@EgU{C@hHKVNFJduX%(jv(TWxn)Eyw#6N +MFilWjkDs`v{@INi<@(s-jWiF)v6sf{blxF4?+f0$Cq#jVh%)mOa8G1N2*Ef}SfR-TYyO2*KK+ULQYK +E_Bi4`fxFFqF?ID`?(viatc~&*5I6h7&n9{O!P--L+WJl+4teoU#R*i8)3L5Gc;>=VFI{S9AIR154mK +p|j`$|GTHt3x2j`%$o;k_tHP!X?f%;ya|wVE!k;=nj|^D6^%m*9Wy$9e|CC$0}Sm}XzosTR4H`2-S76 +NLaC0c(`V2A{9lu#eSn}LEpbe&Yd+I~n^z=zh?JE71G%UvcyWs`uCh;*ztE24ae_{P==iAuwF9-p-0R +>|9kb*g6hTQ=%D*u7@l&7c)3mt!ztTuYJ&DP6G(v0>q))AzE|je?5Qv@C__cA! +dBwrvbbYCa`wRgZ?x36wUCT(d{tO$UpN}kopc%%L?rRAD4+018$iFdjkyHM}Ic+e;M>>dCCJuCmE?_E +4;#pK7rsM8`Up`R*`#Hss$PudS%-aaXf56x_`so3ugf0-YWk_@?A~yxUe(;4yR@UOCYk2wF7gB3697Y<_k`j3C)k(68zQM$-3J2Ep#CuHF-cfyV`s?32R0) +CHj}P3PuT#vOToBZ(yFhVR=J`o(AU{O!Z4yDqDUyq%qSQLwH41fXXJr&0#FifjgKkGAoKL5WyD5T1mE +ry9@T8@HR+K_>7HzksxTXjD1CI762WcX0+QEg99F;l`mzk}V!_#1E0(c`k-< +H>Ed86QQH(|`gS&_;;-7d~BI|-D5fW(?S(?A&2BC&5!`lcvrz=bEdddaT-y>wN0bsB+4L^3oo2HiOtG +D01dixHKMM+&0C?hA=yv0K~Y$@JADtAu^AJ +C<;qz@p0bveQfIxXi+o1m3b$8FwSn<|6x!@cNbCp2(q{> +`Mbm#FwZCV4rIl0^K@>IxEUZ2vZnc7qEo%QxGzR%X+*#68v=4lK3sMCu*LiX=2Ndkm=?WqQ*Rf&OxS(I#Fu~g-Qnsuyul-%~H&kx8g;0{R +M=Ne?cT27d>8&r#)E44ARCeA1OyPYmZ_Dv;gHT(|o&Hlo_>Ig#`7l=df^DUbrGT4LPs-n%>Z5sz1-zc +s%n==Jfv|qp))@z$6TU!+E830}x?d@6aWAK2mldXPgSohyZc +@S003qa000*N003}la4%nWWo~3|axY(BX>MtBUtcb8d97DXkJ~m7z3W#H&Y^bdXpjwGneM3sNHkN~EQ3JcSI3FCoc{4*T7K@}+ZJOOuQy43h6I*=6C)JdqpccQW$ +rU+awgSq+8_`;Vju#D4!D?|iiQ2+6!kLDNn +bHp6e@(pLIvcyxQ0B#*?U*)?$kffyBjs!ivObz)bkyb_hXCE&K{nnEKy)7kqd_Q#Og8F<46umMy~+)C +XM9|s_K+fge1;^m#v8qcaM$wCoW6ov9QuGG;QD8mu#a@jc&0d^;}x*@S}>Tc1yl($mp2&N0rCzn^R|N +i~*-PNUdCEg$&;^V#EBiya*v|kHt%nkAt!z+}f9Ov8zib#P2B66fL-Bns_LvAAiI|@O=8A9dVIqD6Ss +%e-JFaeYSTCBnuBJ!T<*0U7qLyDSxI7`F<-Z%);1c+SM2AyJ@j3@W95dgTPjgn2E&l>{IBtm4^cgj@( +GMIYCjFc7dCEiy=I#$6+=AbWC!GQGX<=~R{zg=Cu%P!yk0Tg@2q+l0+FNbGf*T|Zd;306zvehaHRHCi +Id8BZ)M3O7gEpogCu$;iUVlW1OVcQa`_{d@xG0scx=os!`U27h^=wJ`e=jWk(~Kv*Z32HoA3^HPN0~fcb9rTZYdMlknt}F`l?G|JoIVu5k$O~|7jY5Z1UeqMf +ntUa_xW4ZFjanbd2NXmLyS^TI;ti3((Dw{#uQ=Ks^;7YuYI0N?{eU8h;!s0eFGpWpi_CH?TnIz09oZG +_+&a>Pv0)#4z*xpdM*$!{4wraIC{fU_?6kB4PQe@zQo|93x$s!#z5|&1$0JWD(+K9!Y8#4#By_M5!?~ +d;bfVZ;22W$e)h#qT#|?>aGTVnxlXJ4<%8t?QLemYs*WIg>xKeKO` +0v^^qaHDmO`t^yW`r=Aef{L&3B$988PhlMdtFJZjg=v(VAPJ#&G60hgO#_D6Wmytbwv^96+0Lv!Fbrk +ImR7v=ux>ThRG;F(g-Z(zgIPsKur_YCLx8ncrq_uLxCO_cX-we;MYT$-S +#9_C~u!Xxo(&%vq$s*NHP$PpFX@~hH;-u`QKr%d_t319=2 +VJ#be%av08~A54OqIQOC0H@x7PD@wiLxweIC2mw811&0B*Fz7`TX&sLqT8;zk`%M$@-2b`H)x8`J-?= +cf=AV13gyd>?cp4#PW1>j^Iov5`kzmd4Ml;_J^H*(Jk>-{CmKDnKYzbwnt?5v^6w1cU^#+Pmg^nhK=)tTZU$mVgLiA1JZ5_m{u1VCPx`^bmR4u +eIY%0P4yM+)SCaGpXvklT@1#C|ZtUrCi)=F>1p=Of_rZ45Fh_|BzmWDTpwO!!Ulp3VLzm2UxH49jWgMa@}csFR4nb +_9)JPW99>Ou`E@rveLO5hGHs}0`(Px#jITbOf4&j`F*!WD`dyrV6o+TOi@%T0K8(ciZx@r}%S&-S0b=9Ri<9v&q>j&yPCk +DapZy}<1LoQJl{gunj<101)j1WZh>edgk=*I=3IyoGlj>Lz<)5Bkm3F{o_U=0zFc@$qh9b+Pvc?kcGuEytQSec{qv#SZbj-alS +s{;AU`0{up4kzPF6wAlS`6*Tt1q(RmLJ}LQBliEWoR*py@?okL+$A5O;QF2^hTN%o_(-6%12Jg0Jn`dHCH=f4e(gB9{b>Mb_mB@3&U +kO^H$T{~p#~t0 c{Aypn=R#0;{Nu^Nm;45Y`aGKamu!Yz=7r*ZT3E<4uB!g%ce|>!|4i3Z~Wbb!p? +_>7c*8F#S*W#xGVeOB^+aZW-CIIx^j(|UOR?)3P592 +YtG=gS#1yHJ8PThLj}j2h{`L_(X4gyOZx&XQFX7Km8CS;JwGC?7q!%)~iZ=M4I!<0OfbD#{Cx&?}HoC +2_5cnsQ=VKfK+D)(`tNTD}a$Z@QhlWvb+l*k6Rf)Xu@PX`hrXrRQM26UMGD6Vs4uSy1j-97%8vBxl$6 +9f)cy^j;&3ZX0%J7`W +7?hO1MC4j+XKshT4bu@8@>FyY2K`ojnq+c=f8u($8b57j9Rv%>QB{kb)n}cXDQ${xu2D>CjNVG8xs)# +=(-mNUrfG)K3rGW{-ULI!Hh>?T?uW@)Ut)W30#$zxCjQ4{=gv7 +w9nHiQ@vqCx17gnJ(ceu|x*5;cO|LKFcE!I+y@^J;$g-qvaiDDmHCtd +P~4Iq(-`A859aUg;>!)WqT@>xkihiln_P(k$gBn(>IlxLziqBM49!WT~Mi3!tA$*T*p7gpdI3mMLK?s~riCu6QDGVq>UjTZN1QhfJ+{PLtk=N^LLn|8V6Wbb^0kt=Nz! +WTpS|6tuukDSoD0u7kh$Z*7Nk_H4{&tB_ZPZyiUE~2s*SQ-=^FdH8jNo6>8?<2RqMFsvq!sMOPNuEa> +nl`1bV;?&?^;(@_@7Q^rXf`;(0*uPv~!z%QQ=cAgU3?C9E)sq%A +Un63!jn0`Y{NENXHv1g{R2EZ2TcvkttE+0zHL+CMc#t|v*Sm9Xdo-Y(JpN6XMwaAYtpU#wBe<_sO`|D +A(k^E!z5tk%YMvVUig`@4;2=$K-ARH|;Mgn#cP}Y9@B&#m@_qZQ04^o=32(nQrT|O^RVC+cTEx*ETB9 +HnTQ>xmhEXCYPA&DyV^VLKESo>`Ai>p?z!WTsr0CM5Z!+7%9oo6P2aFv0trfSr)NFRGUIO2dS{mnxr- +mC;8zh&an{v}4t*o<`NH>$!rikk3@bi^+3t)DwUCT#saP^+7+5o75?KnlOaL^uY=uv_7id~;vaaIG~YQs$AL@bC5Z9=B4s#g#h(Qs +RF>fNL^!@ob4UkoLK!8Ddg`S$bHRT!2v%QekmhR#?I2p_84u1BZ#-5r8BN>=r-0*!PHIqI)Itt?Sr^X +2UG|Gf0pL#u39x?e!AGDtE&u1#ynO6OpSI(dPtzX!IM0-PAZlGeXi5|yB3>r3C)3W#CvJ2r*NFeI0OE +%(QqpL{EhLM%(}5Z|zNH?CGKe_gcT@LsuZ?W6@Y98v&O`a={F%oYp)Z4_WZ_^sK5$kCw%PJ(t*`^*b1 +IV$fk(4L>h_1KeRQd>EOxm{q|)?q<=}H0 +oIhPSw-Ngzc*bA&u`#I&JSyQdI3=~gJAS@ctek8P9Y1-k`g;|iwdy}o&HqE!T*I~%H_x&qgdFi*ApdX +^t~|b=gMk;!w#)1_6jIhNKGu-iMLy3Kp@ZwTnZm^cdS5}XQl!`gNGFlfygpr>o?H}fgud*yHD*2xpaj +ivrRJQ|hA9_r5ITzhVjx^sMHEW1PsgXn?<*+?WE0RDLeNLv)+mjuNkodl*3eqy&?cegS(V;tGnr_X;A +`kcanwN?!rU(E6{Ix<;97E(@Kq&+2og{mA(Z^5tE-DkncTApG*d{mDZjSSIQnH=MQ9|fqi{*1iYiW9+ +B!PkMd00W7+^du`Kb-z&s^pN^I!DP29n5SqHG)=TVc}JyOUX3@=ouU-HaOk$?#M?ekZ%5AQQo^)MF2r +;|FdSlW+b|JgGce<@KTCCbyErM|f+JF<|;grCx#^iLcG{QoU;<)#NI0y#&cFO77TBn*3GZ`UqwL#ba0 +eqtP+77c09XaP_0H;I$76I(OQjma-x%ubM`g*sI~lXBz6E&~DUgK)^v5C*C}cmB-KSjxgm&r!;2x4Za +M_{vs9&-1Apz?pa1Uiz8)>AB!hNAnxeTH0lC|IncdKV`V+3{Qf?!Sd#(OU# ++@zXLnWm?v~wJd(V>){1|urKw3{o(400cY1lOgg~9PaA4!R`IK3n_geJ-F-6Z>n_2%_(nZ(mLD+8a}t +76PdZKL(L2$Q8}gg~62ItYI03xgDXTI=S1J3X0P9F-lNB2;#D*SMS0_r*gH`SC+ab6UYH@IIO}<{YdW&C}+5M2Y}k+Qf?HePBZf+f%bXkVuH; +!SC<%Lz$Som^F=Ac9o$aEOO6H~%~f@ru&bx-?C=`jL3NU@VReJyPCH+es~H;%jTLrUPUDp|$a2gkx6L +ddE!OBY$!Y_}^}n4;0h`YQ(uiO}+i+ClSnm|f=?fSYWZR=Nvwx<~!40=T1{fGfCGPE($bZB^v?Zzu|5 +V%*#l0nN9zCbzJleYKVrs1L>YJlE%P>!$j+;kY7OD`RSFDr84sAy6p@8oOlXb*yMaT%(L7vVVsSid$$ +!bg7tY|Dx#mp-`?~_nz$JiE%NWtS`^IoXV`^grM1R|M*v9~okK)8#oTpOXAx_UZxf?GF(`?byey4ub6 +b|Te2zqf~Li2}-Ax)-y(-|ppWr=xFP(+95!i-*k+Tm|CoF|?Vg<-0ZL%XTIy9CU(WSnaciqDsI=Q{du=zfTf*JSZV9v3Zrk~Ks@*DM>9X6Zh}YR|F;H +tGvDR)A3GOM)zM17JdR5z1iJMz5;KfTBwHu%=qH}2%#wKCtAOYE7!)r+3a*zKU0=T}9%H0F-R`Xwj4^ +VrW#y0r3GFv9`W6%+~tihhB>}_g(PD1+-;I5i353+^?`kxPS=E6{f{KqFZ^203^fwU&lCw87ohnIaWc +%6L9x&ij%>N$+iT>mpGasdwEAx`|$+iybSOOVZ#z9!NQ7hBOhoTgdgdKuI&@DB5A{@hA_6GEh}Z$e4e +??Zq~&flVf--Z&^@Y^xQ)D5WorLXW>7O|0CC%%V*N~9MS&R=Zca1=JPxiKqV61;p;q=MIXKK0I{+g?{YfZ4KiEeW146*YgWw4+l`WivF4NIdh +P12?7=@V&WDi>4yk`6yZ(O-#_P#bUdM7b2!ZV +5%_{mafl-j&n7w!>6c?u$@%Am6%xOb_Y@-d+tE{4u8X%oU +c#*D6m?XHHbi3xi=@eBk^4_5Z?)^^=OFlFh5Cm@S89PzYoA;nH+j@oPm*tM#g#ggn67vkQfr>iR66sQ +lK*gegwtY@Knw7dFCYLeEn91xeeo~8!8a>f)M40detff3wms;0c7E0R|U-Xwi*;KT~(+gbkxCo$1c0D +2iL#HT_q0g+{Y8R=%e_@Y)7g8476wHpPNk;!wRydGYB#p$ymc3`~jkMERzS%4MTTIpR?=7p7J#aewyA +a*o{^eDjlbzodJdhci1l#R!*N%-|=_n5uH0lbRd6`#QfozX{Y)N!91GAdE`6#h#yNOG#OACtZen69pk +FcAP>}OSEuxfePQFnJY5=Ns1u01Fm0BGS&Tj|70AJxVX9l#!d7P8;IM_iR&~^?TEfzQ<>?{AX9%IA5I +$1_F_bb$>sn4NM{Hr?WVJ14YZ*mZ`rr+O)!pGvrzIB$NwsOf=&``-{~|L!7E@DLP{-=gK@R{zpI8=dYiMFAXVs0twL}bEfZ@bi(M-oy$7J8ug9ZRK9K+>x<7uE8Zs`fT +Q58&^2SBHFCs(P3=QF&#dua1_K*zTUh#(Am#yr}Ku^wN6wNVIYcilPy4Aq8@Gg|BM8Op@J599+@%ak4 +43|crK4KR0LKyxL!;W^*LAg;R+C>!9b4sE?sNE}J2lc%YKu}zIUNg<4G=;KN1U~EGhn<_&?JV_x1%oC +JSfFMrcb(58i`$SDT%+eZ4s&4CO5vQiJ?W1V{d-@nI7$*kFdi +tcPr_uk)tY7yO(ey-9tEK--ZQj^s4RGI5^y-Z{XvyqHGU$)$u`_k +ZNDjk2Uz|{a|S0fLfIlnrMi!2e_vBpbC`3eXaI2N4|WdJ$oh`z#u3?0JH|cXOqhyrZ@fJFG9q&ext1S +h^cebOTn!|cknQuDTRw|o@&_06^T@j$#Wc3?B^gwAi6$XoeX5;PK*g9b(omY`1 +E%$v)!0elwM;dM$CrFyK$hP-XW+7nnB)Esxa4O7Ak@g+{~+=N3MJgYi$Bi{`nk`q&~wG=|Q`5N0+N5x +vHoycYAdeTaE|M8FAk-#5#7}B0mf^lT(xl27yQZxsCsM|j#m32@CZcSS_h!Y>uzrMaUeSTr<)-<1uM9 +_hj&%OrN!-nw}U()05I6S%ADn7NwFE^JX#2%W>qtX(LMp8Bkomg!&Jq>~;9gGI?G+W=;&Yxi8g%6&NF +n-3miI5y_NP%&YpHn`5PFKlS6G62at3ym)y2)+I9W*{Eh1JeeW6*}shE^TBQkEf|tqU_~tSjw#3mF9Hf=exnP$@%2@gUl@vLlGC2oKg>9ZihGlQyhHG9P|K}A)k#W}}Sw@i|?c)ASqg2S +QIQ3q2xVeF}w>cL3IfRtyFkB!H>2FlrZ+dTOwm}!db5}hULG(G5GabB9z?2$CQg9sZLaK(7pKq|01twvJXf%1fH; +SC*5kLNxN-?cD?MbW`_mMV)5x#PIGlQlL>n(;2d+gYsGFmYM6*bNw{l?&BeLSL8ldJuvb@chvb#iCFu +I>+)kz2UJc4FvD;h~&eoG+Tv|G0N#3jBtumE8o(om3#L0m4-800;^2eUdBs0@CjFhQB`T?$9Yycjt;< +EO_P0Zb*c-me2|v!X6~@z{Loby-y9;)$T;f1<=}Y);ZXvUtN^?aV?H|$xyQrWa9~7(7Q8orpQMi5_%z +QTv+GP*21hfna3nTFsWq4(ggX0*d^g5yX=|ReyRLaW{YML2sbNIO&VW-3%V{t)(|Eln-xjn7d=t)~OV +s$IM}7wDBSoRg000>JF0vln2qz^2H6iyAj{_?*Q&~4YNNH3L{f1nc0&7^4p$Y&kHPIA6ji0L|ul8Q#g +KNe~B-6_I-1?83KF$fNB`V}}k*67+A&acyeK@YV(qdp7#~AswnoBxBpiy%K+_2^V`mJkDBsZ)%a=Jmy +p%AWi+k&4#E{MRP20qT@;GSdwC^?_ui&rF}o3n;hvdFtx$yxw_>?F4oLo`Zn3KL-Zgt>`8_1`p+?w!e +KHyj#fcMwMGer%{F}7WyX9AY^KD3(~7;^@<@CMY8?9B~Vklqt +1=u$P@4HqeDnjODj$1Y6IsS3wZzaybgy7r_9p}9CH1^s1mcyVz&aSksp$G@E6wPlyx_=@Qk^v@R`4zG +?cYX^wyy>hzTss%QN!1!0c#X{Gqc8dl6ZE%YT{&_KIB5L$)ZHM`%l|qw_>oLrlDT!1`-L{b#Dx0=iYc +>-9{GXxerwG$F%ob##p6j!C3Yh&>)i#W)GrHPV^AZirNDcP3GMcBDim|hxI^$TYO<~~&Yc0L_E4bGWq +k=W{%TlLGw~DKhV@_dEMiW#`Q_-{v-dRAK04#h0e@z3La!XdR5fRhXb^#iTxS~KU24~8hCVU9AzEdd1 +d6Z@7MszLIJMu$Q{Fq4uzI4(5LwK}s@30g)a#7JL=#xETcoxcg8PXMQn_)ov!~vg``EM=pnG&A9YY+O+B*r +sjeLU-1eE-91T|-m2EmTO{S>g&0{kHXhzIy?}rtXY7J6t$e2|iBLFE*Rl4O;3*cAqO*Y|>H5oNBSXw1 +uWMbk)z(OM7(cf8BMQI0acP!;gnBI1{xeC2S9oi-umn4EaeH06J ++$wW!D4L_m(dIX8XIbCZE%HIj>K*zaUBCM`lFQHk+6p3m(AUql?F{uMr$TZ_oWx}@+&qh+FGRUwe4@- ++Vx_WUfpy=O1pPyhz>c$&N3Shcs1`X3uE4_qTwm<1g%pBD7-p^Cg@EE(Foi%^3&yhrHAWEJ}s$H@xTy +m?#GuVjgn`KehZ9YQwRjGt7WiTA0jv49$>JQ$XN?0R8gPePlg7-xRSz~eRpdWE$Du&f?XF%DHH2GTWK +{6$xtJOr<&uaETUqsx_f6Mlo9PbT>dBacU7pO@HS9B%cxM00m+8xwdZV#%wW +YdD;iEA{s73Hf+^Eszjd=BwnmwcY6>gxEn-U7&5Q3BI^!DF1Ax&De_GnP!ccN!x=>}7?(2;pfXV-FX1 +E;w1vuBe-`#e`;f&$wSbj5f$GhWq)mxbr;x*+Z2p +e$u|}jo@jFwNmUFmqphdUut4-61=pKfV)4>qSVh+Vof$sSacrQ>%?(cBm?73P90}h%?d<8vL#Mo@%(| +-vC-i%$f{-#sp{N%~1rgn7s_8gn@(#Kvc;+ZA6**mzR;(Wp$CM5>p2>u +?BXKSH&ri~Zq9@dQ~_CJbvP;o$xXx_fQ8q}ipQQaQf!-WZG+CIHT&)B(Kjs)Ba8Y1XMxfiW37$X ++QVq0=!v7(YYi(12D|y8cXGG0IYwP0)pq}sWhjdlw1_E*3bsC=%=OYp)b0rr_3GpQ~a#&bQ3ON`V7=xWf$OZ_k=;|W8YMUH3cq9?u+3SH3 +>Z-2^{I!S$CN<=8^H^5=^7n_$zH9p@{!$95WxX+&cPAVgCV{0%RERaRaUMj``hk-9@o`nIqx! +4Q@-UqUv$^MX}F1BG}Y0n|-F`Na%9GD`YkiKx|r;$El9l0gN&a{KxxnaJI=C?SMVCEn90)AXG@6aWAK2mldXPg +Qi_ykA*K008nh0RSKX003}la4%nWWo~3|axZjwaA|I5UuAf7Wo~n6Z*FrgaCz;0|92a=@!;?JE4K8#$ +E8CvmXoH5TJ^iKJDnc2B$v|Gc_S5hs7P$)yfWcrem>J9z +>rGX+Nj94w6#R8wtycNGEvmAKJesJI%WXMtt7>J-Rhzud+Nw6cn``^3z24+ak~PVczNyDWd2w)XFr8+ +r)pVL1C#MGqd>$mPwgBx>v&rX0wvvL$rY_b+TfE7e2^AYYEw6`T{`zc{HO=3$+WxM}dDiA7{BIt8Rg~ +Gv{P?D5p}VVBZ4Hpk!CAN>#IyXewDA5=KBc@Q-4<-lbTRx0rvme^Z`_d +NS*yrlzRI(DT0x;zl`SmwEV4G6BMnd6IxCxHRj+@${zxeJiuJdYUzN>!7fhH`1egNVtYB!tTRjqn-c5v_@fjLQLuufssFM!b3o2)Hn#j0qp4 +_-chIsN@7kAZ=m)Z6@k7E>|*Hg7X{gu})9Czm;MO^*=B1f2;0{CRLGz_o6s16;(=9>`u3V0H~~nrTrkD;!$-(KtDLEQ6 +ti-dtAO)nY1p_7vp}HA!i!2F?Rc6#l(Vw&Nsao;8^Y^CJd57Kv{XefVPu{X?v|c^Vz$s~!FS?7emWUO +gURx0?JtwjFDlNfB2?JDG~;Yk9IXM0@y~44c4S5}+J@bEl8LtIAyQWUd6%MvA@NN{Y{GfEn97KDR=_dkF^VrhhrE4jk0WyQS|e=;@-J&HVm%>JoUiDR|FTobYOp3v~A_ToZP|l4 +~0hPJZU6LK7m6|g|kr!5-XA^JVi;8Sd5}CVnkE3K3GberVSi>$0$Pvl!!foSGalsUStam_RInIY)Z7- +(0Lc}IM48$1=2E;%~jD}>P0#l_blHiZvsY;&EW8uGj~)KJ*b35Dybk;O^#*qfD$N>SK}tsnrI^xj&kG`oV!ZGcbs; +(Y&#I6SAq(qbFyERsBKGN9A2#=_kSegObC9Sx{#V^}{=lR%Zrd}<5ADXj$m&2w5Z%)P&*>$Odp +jcvne;)@g6qL9RDb*Qe@YQ}L1q$>&@GjyO$AhaqHU<7BpNldGK6ek3pzERf=^WtgP^nC0*yhiZq1NYk(LeB)`}a0i= +CsTGjEkv|pyT=rZLq4PYsY}-w9^x=oI?L{-$T*F3NPOAFi!vc2I{NqnP`uK@GfD)W^=}QKrI9ufNiWK +>{RP0XYt+LABDrwod!xi*XU_d?I?$xSu%WO>S+!ZAna+|2FVHRS(nCw_{Oo3lyP2MFoo}12N#KQs(hR +nc|M}H2EiTX2{Od`#_+RjpJGD6@z!1ti;eVUJqh&d+f>hNoyG@~Dk9(T)@=Txivm?Gf;@y270Vxl&v}YwJMHf_>4hI#JJ?SNU7q +DU;$raE?||uE;cN#+5t7i3glJTYJm*_cMw(WRn6wPNdUla3M3^w^fz=E2dF%Ll%IfAG^?t +`gF3RC07{)_i$}D-%ZyyBPIE_ws63x-i&abd*Um<`(Ti0Cfu(4h*esOdHY^zMB?WU_r2+>BBKaLfML% +Oe5EAAwaBwEehpnAI93Ao}j9owiD4ja2RiOA~k*V +{b?(x>ma}l?y697CmlKA%o_JlbxM1{RftO*0Xa)81zno&hT#7u8ZY0pX6jJEvo5QNA?6dDNsju0mbtu +a2__~(Sd>mumfwJo-VfQ^|dWD16x3{-c-VM9XFF>HI5hdD_W5M1HXlZx)XX{Zjff<5R|0? +aNGEVScKFbO3K<;E3O%aeU8GdPn!lfLQuplfuYmffWDt2tW7j%qjw=jv^kM|FTUjLH0mLD)B54Su3_=)cWDrBBFUv?GV)o`g +G^0{4WKi#ibvRcMTcfVdHg6HSuk; +F_5q7Qatb5$8Rz%%c(z6&+skHlbfG{50+*?-n4Yb%Nu{nCmGVASbrNhXsvvw8{m##R^_lou{x=?ZG#?G8T$g#K7D|v +Bj2=#aN)dERNIDKJJ-d!VDSt{p<$0E-_H}(=}(ho&K_b7vZ!m+c(NrFH*Qq$RLHsDWg}Ub +LqXX;qscZn|(SheEs%q=U2cC;*tPm;!$cfXvq@M@A`={=Jetr;fISrZ#DkvvW-K2fUN7Fw{p2W=(+I> +`rNWw@(@EvypRN3bT;x+};-@JH*ir>@WGgyS|vf1@d4nITZl`P-M^|<4*9CvK)K +>KUfT$&26ieJ!Ylbw#q=FUIN9N@1Xa?;-J=wIlu9CwI5GyXZaofvQ8DNBSLj6t +VoX#1YGa+INtN5%q=uJY`4oiFic53r)K^GQ|UxA9H(p3A6v;gDapX;xHv{e?@Qt!Ys*HEvw +Ig9EHTAdv2_4$otYnPo##{FhL^iwEULNfw&)=)tLn9RhDSd0oovtjkc=ong}>brTLAV&iZ}76rB@eKe +K8EF13$EWCfQ<<;y2GtiC&pS2y-+S9yDF&>lxwf=bP0`d4KN-*Q+n5hBBahPimbi7BUT4>{ +e3~u(zHM6VqU9O~G32l0==UNWK9grHPZFoSqzx@iaJ2PRcXXTo_b)FD44YizTCFEPYwSgyIR0Y|6mH5ywRv)L>UNRS +U&Puh7#~Q?Xu-=?d^(=cMo7tPqjX-?6Hq%B(@8DL~q@!y;WF6!=gN2_L#PF9elH%^vsmw;qkjE^AiKR +=hj(x~@jozw8Z$XA%GQX>Dw~A6xr;?ZUeN&{bFiQPKcltU-Hrr|i^$v0a#n!pZBm9fn>yy(-edGgjRe +Y&s&jW#51%5$QHQh77(9 +*1L#l3rb-v@%~X1YJHzDfRZ*r5itZ!j(aJ+oZma^Wi=>8j;SRJ5hIV1=c58M$`^neU_ijzn8}9Gz2-$ +7_ESt}AKwU=ph}r**q2Sxo8}97wSuoj#FJ9PbirlI=4*hm_IAvL>QC6M#?Rc+w;<;YgWBIhKXp<0gR= +*>hICQfaZlWct-M3$C51SsJxS{7e>D_`#@eB=TZFZkDc88>nT~bNzFb_N0$)EV97w_t#OhT${igqjV`xV)StVx=D +Cj%(fQwk*+5#A;pUQcqkCk};z@K+fS0p4fo()e!itAB?;?-rIaQiBB4C$iWvPN;IP=dEYYmtoMl=%q3 +#gc6`x5~f*}{`z|gAHvWcl4A@(@-dadR@FY#OCasp#d;8Sr^mFVOug95TGQUjdi|+RJaD$lpSJ +}rD3Co#h$2lK9|RKAAG0AMkVF-nAKj`%e+zQ9xWvnt=qU20DrAvdU7~%3Jyur58U7=zh)|r#kv2lLP_H^vC)=Q#aksyn1rkQGN(S;UY22V&*H@fDmIf^<+Q +Ki%hOmV*@0=4~JkpLxl&B(@Vx*2Tuymc}|ybfB*5 +vH0}i~mSPnn2j4#DEMZ-j6=QA6@UBR9Ve;dzj_}r?qk7r9{ +0Ro^j`&d*}E@w?gNnC)*&-01^qF(Ti_fN><%OX3@>RiUCk_;+WUrqS^%G02lfr@{c~mKWZdvB?wqFFW +%3AmNQze%e5u%&CECc8^Lv~wDH%O%NGTNJ1pRab5w-S~EZlcZLlnKs!At8$gMvJ612-&^JA2k`=+cv~ +H|=$R>2-4fqj2CF1Z9j)e88`9b;k+tb|;-aFsE9Ns8BmHuZxX!DcF| +9&)e{@)b+=gnL>%ozu)b<8+Q92U*QFyBJS+Yf+sCq@U^H}2X#AdhC6 +b+fj%aO6gJ?-4)HdkJOIpNJODhd3ra^A1i;1!=tJ@QA1ISb(8aC}0zViWNC=Ity;%<$LzDeCKYBx3z~ +7$O3H#re(Y!6(BLZdWo>#~ASDIRkk)G^0kfGzMzCBQV7q}QC43^P#G6{~Ca`V$^R0h3vIoz66KWqoW1rJdot%G)G$*PP%C +F7C4o#q>Wxm4iPBqChG*#g{30rAB)Dj)SEc=H>BS)xI!3ReAR)Ee&<`3=snx>l=C)J>UC5@nVUjkUkk +rmeY$>HG@j0L!ObpcIXvrU^7{>hN`^%JLN*r>IdD!`*zO8oxJlkEyWwY$d>+d2p}e +LryN@C4-vgOW14se|dQgfAOop{D`WED~WA0-ql~Z_uT%l133bbZ89z+lRV1=T)CUeY;&-|*HL)7p^G} +EzB4|Vt8kaUq8DjO6%A9f+_qZ*2LU0Z}`pvE0xhY+;1ZF$E3-4_L&_X3wbMHpvst3(u0dlQoR%<4RQ- +CN=vd>|r2)55E&YDG&idGAy&x~>U!br<)k*QHrsEw>jCAp*tr@3;ZlkLI{sVLtm!=nhqH*Ezx1f_BSE +{MN6EzL0L44ENuka*lxj;4umy`@e%oyQpaWAZH4q394?|eRZc+T7pUi4V%DfWrNnDa(<1f8IltGX1aF +Ws7Y7#C^f}X_A(j=a^6lYwk%}pih6vU3gLl%X)1%vfi}S3pZ3c-^>}$KNc-?@B#?vKy;pF(vt4?eMlo>mvf-Ph)7<`-6ueD6g;t +uJRLUCoBG!?5J3#WhAH`F?jU1$>ZRyaw1@Tbbc!R=_XSrx2|&%D)WEnYrR2KpDNCCmRH)}UB +X`!6(v-r2HWBw-kTm9(hzR4D7*{%W1q~9017g;}1NS$ce%>VjK{p46aQF&M6XV^q4Kz)J4E_0Ct!dbF +Fcjm%Yq!GBp*WHDbGEJO=$efmUUPC_sOh*%mAjXRAJJ8)*L&!eh4;9#B)a%i<}j~n%sdmQmz@F??bN> +CVn=lVAk8J#sELB^J;ZO+nGmKXQX>Z;rQ42ZH(M36Is*c?-eu&9#?D=UQrO&b)|WOdZg=h)HioAqsYI +Y=GjvwHx-5X|Q(TSZ^(aAaPtx&M8!VcwCdu7l4nF$y(~myBe_ubFJpS;|^%oviTfT}mXhDEl4Fn|g>8 +h%Q+f=K?qo%!H4&-Uvihk3RAJUr|{sz6hy6{H&Ei8C0_C*zjvk$lC-Of)hJ6}r|$m +oS?FMAI4b +%uuV*xdhWW;2!oJa7BWYL+)PteE&xsk>42<<0W>~e!$Z~_d;dOA(JOR5w=Cf%&}bsX=fL4v+T&$9HRR +a9coMHi?Bf9^eHQ;X4E^5jQ7P^NQ52n`KN?XY={U0nb;6gzHhOf&aUo +AKk=s5XMK^!U^+J&VNNt}{GDf~Ml6}O*G7ae}~Y{X)8S5p+Lc4&)|f}YV9Zynm@*ON`PNma$bbjiUPp +=5~x=e>94;RL^=19Uh*o~=6pVyEe{L3Bcafi`tc-YtV&oajp_2Aj@L+}0R*$o%yxf0M6{pXi{|;n_ZF +zYYiiqjgi458ZAC8&Dy;+-`;5zzpa}k9aV;WAekj06)A;#zG3=?DN}Qb59sx_U5?R-A0et+DO{$H858 +?Ts}dG{G;UV1 +aZac*EkMjBfiyF0xv+?A^zC0a#r`UKsftz4)!orVq5-3}ibZhl +7-=2#XeBh0?u2$WrTRhDj{RKuSnc1+gXCGAqX#$C?ogN9tPJCGohUDBjFk&}0#!rm1%aAQTLWNT$gS3mRttWFJi`S-UXCdRP%;19EIb2WxCDG+>6 +xf+A_E0IOhP40k@mF2(I8f;DK5qj;vL_B8Uq$zy}JIBpDs++&!Em!9omL;|uuE=iDZdQoaemPps~n;& +?)`#f5V>uoJG~Q}SCRmxqXoqkaut5EPqSQklgKWU7uFo=v{7yPNDb+Z +Azp+G;swMNk#a?E6&S3qN}!$f#5Myq+}G(lvE&(z{q8X+PSM(%r@z^C^1o#z;tdf8Fmh>fhbF*hV%9* +n>CgO;3%017L8ey80zAk&SupMm+$674HG?-bMY&t*M3Kaa;vNG9D{-?UPan)!g_kVA||n%7s-jo$yi? +%28HoQO}fG{Gg+y59E2BD$z*`W_Q$T3TNa`yzA7u426q;0{_Q$n!~=6c~zuu67-r)HqUi-2D!-P!iKU +9mmNU~Y07qMhr@zVBj;WcORrVzQ_hRULNZ;~cVy&bU7Ob;nS&&W&B^V4ob|C}$H2`Ve%tDR@M>PTxOp +Z3Dh0)~ZW^5PRK$7ijI)-ghUHp^EOYcJRtk4$5vF?c&XNLJ*IK6r^rQnk!Of^cC=90LqTNtc)ogb1mT6j}Aa6#Y=-7j>bGf`@{I9 +LvDtLGWRxQRhCgHI*6PQ@zNvkV51~dD-4Z{oeNiP0+cK)4}nFzJ%(?HB>KZ>YD_qmJz+3u5C9?${$^$ +wpxp)-XF}!!u3Z%}K#81UOsk)KFg8~|uO#-a1tkDz*vVB@a|jNp`egD4!uLlQC(P79Hx2p`GpqZYRHs +3lVx+7uzkKoRgucB}$05JcIAF(&Mt^6G0Sln0FnQWs3foc;C6ByWNa|2~E4#!irn>U9T7GIbJAWN-(B?}5I$W}I{tIS`%jmoOoFF)4GR^x^_i)~rb9ia=6fV7-$w45r6Jx&q +aODPmEPA7fqLXD+3MjzV98dm(&-eIClChy@~7`9v$Irm1$rwOxxmi65fzOIO~4m8X(Mu2{s|ROArRPT +<{WPh31UvFrON9Yhz81WD_^5_31}%d5$C=3OTOPtS`v)aPT6e{OvrL55@(%*#Kj?%O%T_(Kd|l(D>dK(-5Fsr;gklF5z&|#w&naXD=%`6?MeLRs!-Hs4Ca^N&G3%a7^i(t2Od +9!ZfSw1;K0#ajs*j^sJ1>MmkJ&e;0#njM2fkM(iF2ENkxx~!_X`c;(k)pn7~z>mX +=WG=)rZ3oOwSz%{QLrN0m>x0#@mCci4vu9*Q3T1sx8dac*pj@n!sRW=?>i9aRFeJ!paob(+ReZM7n4} +XJPiC=blJ7+G6U=&WKGvtT(E3)~o+wgJ+)W<)Q9?ruylC +a8f^32-(M`_#nY(ZCZFi4m7*G~*^xyCX3JKd<|Na37S>P;?`IBZ;DM?fQON+fZ?rYa^^S`$`-;)2oG00 +A$&+)mbwk~3>7ct#r~?T-5h0vid@gR9=d4;D1oVf`BQjSM;d5Le +`>)3wJrOGzOVcggp>V$RA5dl|ic<91M@ee&KZ;(lu&<5Y+dfbc$;~4Bxm+Mq@P6V?y%9Hy(_z@WU({TF=A +=Bu{oOc@e>c$bG#U!c4o}@lg|(7-zTz5OHN*jFmLnj(3Uf7-L}j+Y@`6sddYh!Ri%v~$81-xbC+p@2g +^kq({rvWltaAnL?QnrzM<4wt82MafL=QVr4MuPT9b^7qcf};m-%K_{$=qRlGNiw6(loci_8l>XV#Fq6 +f1>yA1Lu-zyaxSB{YP{GdLT5U<-5^jj=u2dgdhHi|e5il<6D)^<{F-NT)k+>^k6KM;*&tnz?X*q8?X) +UdV^jD}x>-qQ;USGJ)yU9D9RxV(N{%oxVrrbpf6(qq)`1!npA~DClaXG-{9jx8ok4;^pARg@dK4`f$| +kJ4Iye^dAp3J4av-JQ4+K9Eo4AG$vvH$0M7MP<1BQkAhz1KfA>d3HH$&-r(*+Q#Qrw8Vwj%y{?MmNqP`a)o?Ow_Z7WAzCy+=&NmDdDfm{?lDTiW +aHBEAW0}j)2s#ND$yR%21mg_sS3k#;)C6DBgJI!|$aWlMDA9y=O~bdUbNw4Zqr>}z!S +r2`|WO1jV_YXSpP9siPpHXeC`GNoKK8u7MbE&90~%{lMIbdG|#;E<%YKNAJ%yPU#8J3P>lYlSBqn5s~ +P?kLAQ$-|FB`uE8h{rTt&7feqPDd7#r+s!*NOV?uG+5RoCKn&;~z%%aCS^9h0j-J6^-6e+*eQ%uRPe; +pRp(Shu6)9`X43h*{ki{}F1=7)3yrN`MKkiwp9)LK6Z!k=tu8$8+PtNYq398ZUh<&^plr?JNr|(MguB +eugIA!semEK%Mf>urj|CA__s8G^E&!3Vfd&DVoc&4~@l2FkQ;2zO8*--@LtqcQ2>V*&dOsJMsKzl%Qg +8V4V2tZeu^2M~_49QbAc*FcYLlpikgH8S`%!S=i_^RWT74}9HfF=g9i!&?p#zn=r$C!BDdgmO(@KMLR +HWK;Jwit1uwVhyJTCc8>>sdiAg%6L;{DM8=ZRfc09%B978BcL(V&&O5ZxU+MxMtltq1Z>&$UEA9N+U442$Iu +xh&M`fd&r^WC(#o*vtk3fax4$=!M6I>_k)KG-q0d&vacwjO0yqvi1*TG1ozCL?)nR +#v7~WuOj>G}iaYi=R7AOm4byLNR~G2^rDGCiAwiMJOkHQmN^cBsNKYFbAMAr?Vn<&p0Eobgnea9$4LsH(w(6MHS9f)qA?_JM^OyDy+>(w#yKH}3$8#<`jLS`5Y7tLIXOF +%A%K8`4n2LN3Odn%{I=!2WRuq@NO*>fuV^{3J#3aW7_$PmAUs6N4n*SC?3 +&4mPNo#p0#!C>k8m&zNVOD0Dxv|P+uCV^X*(>*rRL`-UWL};u@@uWd(XsA2Lo>%uwR-Z?Uagi{3l~@? +ApM}XQ%AnWYe&7RdxbI;Nr5-_&3X7gZ6j<-HxFV?}O%z9jG%!-@pg)kT>2WCg +(dM9;H!Z9e%!A6`Q7rh|%0`N{KVE4(0#vGrkHukD<^;t9cLJ+_lQ6~n`>T6kzRrReX+jOBB`AC`yNr&> +!+*AD}iHtui2X)2BWjWrxuOL`H?tVGHyJ{a2SlsQJyqKKYiQ=>XqsVO-B1$k&2AAeil`TBc2vL{o@%Q +|P>;E^|*cHvW|!&z0Wly-u2Rz=C$HHjug2DxiaDJ245W;1F_+ZdYaO^=19n=$PMQG9EN-->hCD>%OHF +u?t5kdVe^&?U8y?hNjK0Bzg=ksfbwdeC|Y4|RqW;g5SDp8rDPiz)y(Y(d}c;>h)+KIgo= +Lh+3$rUWXe;uX_A)BBb5l70;(WAsmbl3$wXbZGa=u4sf9h;AF?c*p_d7+n{ByZFUaon%_|;Z%N~(qGF +a7_|FBz45M_NUHL5zkl|A7-V!fPk%3Rkg`)vn9^}Fo-Qt`y}3`}|!nd>qWia#)#0{@X?N1T|JBGkYP? +Fc*wZQ+#u6Jpf3mOae@uSa_GyW_|%IE=0#MU_(9_Zk9O)4C0Pa`+^ka#SB~ZQfB&0qByC>c@UTJ_|>R +@{6d3PB{3B^_mlq`*Dnkg_9ABw?v#5!KD>>aOZ +?>r*2xbtCo#TXDk`pJ!L8>-_u|P6O8|lDr%ztb`F`vJD7XE&WdD#BknZGoZ%Y!nKak6bM*M+(qpwDdB +=!_HJvSBFNVU8cXFm&-l`#3SryU6+0thq*dVQNV{c~G)6vyDkPOT-{4sG!+Uxu#kXlY1miMvZ#NkcvS +W#_ZwXO@5ah4*5%DJQS9yr|4jpdnT`!R+Rr4C(N!Jpk?j0#xpzNS+?n;chg+sj)eM{uL-zfZU3TITke +;WTfW~_VvRK819WBY>JCAHv7t}g4_BO{{5R!tgim3ND%GUcuTQtX;_!KRcw?9#;&{O~c;wVZnK?- +%g1+Hdjgw3`wdz?(&gCKr{E5QJKbb_A5nnO)X7ZlP#Z7Q5a>q*;p=)jUQb(Y;3v|P2>Q&}u;}$;4%Y0 +eTwIpWo?zG~udUDC*yu74DX)Akajk3+z4zSC1MHf$;zS5u+JsnGNAGCJY463(K7Y#S-wS*_%(z!diEH +1Dx-3zkd%*8Ku3Xp8bmETJO6RLgy;Q9HpszgB^bA_LuyA_N9(PNjnq03N5I(vRT0W#7@8{R*umkyTAV +N8DOUQi0?NBzj8HWrITqk_D+O%-zu*i6S{vBpWG>_R#h0Q~1!v?u5{r%r~fmem2rC^e?VT0mwe5}*TB +7sC^O+3zC3M1mG97SmOA0o&I_x;%1fyO(HNq6_@~qt3b>4u?-^Adi}CnY(Th=`8S48UbgYS$hKzYK4F +(@I?uT;TYyeaxQS5Hb+*H+L0AnnwucT)8+7H>12!KDl=g~BT3g1C!cf!xW@}pZ#|5DuFx67bbD1{lJe +!M$}s+le52vew8nl!zW&{_WNK-# +_Ggzgq%+M(ps@(I5UH&%RYamPhEe)L>Mp;VW%1wo)Sx)0>6o4skEFgG`oW?r#_xCM%_ +(1)dY{S>^elJNwc`c_Kxaj|7SXVXXP0(Q|d!Xsn<;lfv$P`SGx@*>{{=GUs^zpNy +Did{I1-Z?Tj;U#>|n3lwJYRu8x=qK}7rE7bI^4o~5D88}(G}w9=7ne-r +NCt8lLx+^HRK*Nqevc3axiz^5?P*YJa3Fx?u0N0z&wJPM)2h{hHFf5RAukoO<>3+>)Fm_)7Rm%OJcL} +@E1&IJWe>rBF(cinmh@*w%E=({fRHnKn|37waGTQuh&cG+(4_Mny=XzolHhUql=6m>2z28e&mnQ^LG@ + +Qnc*P6x?mla6iCjF~UWmf7cz-c!GuH@bqRdP6m91RMm&>%2m?(LR}`6U^};G!_NAA$9swbd366Zhut& +L@ZWX$nO|{<$tT_&LuF5e4#wLJ>oYbTjP7tEv<7)Ajs;P|M;_Pc-bCVTb8ab{Ye;>5W6%}lIY5r0Zjh +auMRKbHGr0h}Q5*wxE2UhGH9PG; +lT;rm$}J%U^Ax7t+!UhtytvGw7lu3}FkQv23#pL%L|tlD4-{ecvh)ru+~I#2*@QI8EYngCsZd`B6 +r1ZS@c^d#=j^_|oPF+-s+Bt&D1Q`<94w)~iyns+2IuE?%FoY9zor#%NJTbuh6;P7HHB6&B#B2cU>5qy +t}BdZfvlp=%7(ANDMF}p7%iip+{?$(Dz!8%k`wkzcA{3^YH`&8;lgV5c1MNWi|GjOia1ZIKK@hPCmlq +0fpZ7R+#dsx<pwyH8=F=9sJ9w{=a~Kx$TE#koM`G{^f +Eb53z04qUOfE#4vEd3hDNnHZfR;^!kDj?c(MEt=jGYv2yzFIiCJG0eX2M5&EtE)XlYvhdWjV8FF4;@V +VqFyEc2gNf$-xoIEQPo%&tKigm-vR<=t$q}Iy}WQr^3&O;1Z2m5$FzbuQNx4FIev&(qJie$j>8Zbf;1 +@TT$4d`uhhqe;KPQgNL%+kgy&Q4~ks|%Q?A>QU#V9bT;YJzsUEvzL8z;8QAX$J8cAoMFMv(@!4T73^3 +#Kv8>u=tMmW+Sl>E;Tf=bj(&2Naod&ssyp~G(EW{>;w|RY1GjnUH!70%j6Z4RoLiPC;8J**NLM8}Q(F0woN1|xFG!2^IaU?4YSQK+mNz4fucp7 +w@Se1qHBjt;P=Fs+|D6q{}D+yoK1<4w4b|0b$#TJ|yiej~g9862PYeQhGMPN!knV49BVPU`qFsbwNu? +ynGNh|D%v_K^27W@4GV_M|?+S>23h{TI;fN4V~j^wHuT_Pis(pg0J_aGU#)51W_bf=_$9HW@#c+o9td +XwCCINtMfC<=3f7e#1YFb$udL*?`HXk{Rs&h!54G!K&JD@k;=yFtPM;>a?) +c#^(=)-b4@aVE`7!^-T9TL5*AL~p2&792q|M{hN%bXAZ<0VO4BHO1foMhpbE&<V%?TxWCe~v7ZNz +`4(f#AQPyx409s+lqS$2sbFNZEI19-Q?);o??6TVNjHMrWhF25e0d=&!q*Tuo3X=@~f)&RvBoTRTLdD +bVO`t#^op~-Gd3Nx62We%c^B6}GGp)>uRncBUeGBxeE?N_4hHcA^PRW;Vl`mWIyd%EWzek}_9~FRdiM +7@4`iRA^B6fOI$T*ylNcHg_lJrMppkE3DC32@$no&;>Ob~Q;1h$X^|8gi7tIS@`8@t}nwQj@TTa-koTiu-!S&{3c!gCSCp}UH&Fr{w7`i +CSCI9H|g>}Rk~!G#NYCAQP~7M^9`G?LpcuKnJdpqw6o%##7gR@*SvdhLlTbI0fmx7(pM4JhW+=SrG&m +tN}`rL=ajDaEqKpa#Ldfzm#ET}vUw_9IeMbIBOyd=kAm(X|G|y`h2I2@HkgG&e&6nJknrVLwyF7UI3x +yoI9k`$6gV~JUbh_!lX7=MFLa{DVa4Cow*wD$Tq-w?tr3x2OTR-OQrt(RXZXAA%!|ForlLyr46mwteP +4WmT=pqf^2{6CJ>ke@B66(rsohKEY9P-3zH_v4)^pJ`NopnDp?1TOKk^_$tTZ)W?MCd*rfkl5+f+Py= +RHnm9&zlj^F$M*N$9j!ls(5Ji0~e|L!b9poozqYT+a61E8Q1Noze)2<%i?(*R!InF;$0T@{z +)I|JvQ*0*FH^ibRomFST_4%VvAh6SLo~f_X=4X()ymI6%4zfikx_IJ7BGm`4o`3WF*-7#+`Ra%7zfB} +kODE4B7hVRXZ;EoWZPU>$*Pv`TDmkk)HT~iDJucPn5=f?>i0Q0HgHeQD6$IbYLQ~!;hXoHUtcZwJ$I%ybD&uY1n+?2&rC=*-4O-wjz3h*?j+t&1QEq)_I$ +i(=IvlgqNer+vygidw6ft{c}sfvD3wWy$L_sAIn(GFz>MBKeZR@g@N1nl*ltsrwQcqI=`0KwN;y~`1X +d&zpRyACL#F5Q?&SNlhOHY^`^*O8s@Qv-Jg@$X7j^Sb*E5Ic4j<%adUC7$3x_@-)10kyoD2yEO>9d5}7yL?t&0ch@R_uEvWs9LCbNbayMcPtA_!>?PIE4FB@~=rKuJcs-K2G@gC)4l0FadQ8eFlsFZoY&6 +6f<9ViR`{hS8cC8;w@CSXLpQzzimAKK^xB~B9Zlxj4Ji6RP!5;-$u=YB1?;M(UUH1OGJ5np^@SAs%a<)7h85?C5V#D1<->)t+N|q?Sq`2(qke&wH!xw +bKJ%*Fs6&6m1^k67)*gMB>i~%vj64UH$5lL+O_9%H;*V*+c5BrpwJo9-goz%G$H8xKC!biR)@V{0<@k +k`LKN4LQ&6Fo44RqxaS3`yI!aTbgXiUKQ2a%-*{Wsz1*9%W~5{2cL8rg35V+WJk=l7zOMO)0Uhxoh5(L +=xg0CN|ziPofzeC;L>ZW77NxaXXWCNM3OfQ(t@K34SBqwLl;eS@At-EOi2qnG +0R{JiCSI)~%rj`*^=Li5I0y?w}g5jjNxO}MUY8xB$+$SWA#LK&P`sziSgF}OdJU}x5l;?RB{TDFNJ>|$ll +Em-K##kqF5JYm1k`D(jBAk;6HX9FZiKyCvgE;*S5{k}pYG5qZ~xY|kMc!#!&F573vLfF4X4q{9=+{7B +h`;jD&l6qk^>KSp|PHEgx@L9YqOprvcS&*gJd$wx49nTLW0=f}NEB$L;TwJ#4$DckPjgy~>MSGck^6} +#!u!9wkxU>|+UNgc7nqTD_BaO_O^zDd)!Wd8IYmTuT1LxKM1?powpSKCtmk%rw{IgH}w-bl8kpbcAGv +5m8dq+0=<9$X{6Uu?A@l(k)CdW40GBdJ#(T!ttfBEUI@MFJ8PydjRTdJ6wO(-ZQ2j$P^Cil)W>wotUS +Bl0}>fhZijWUHpI+zSO97~yCq3*o(MUJtao#COD9gcc+kTvYW*@{m1#_L8sa@;^YRN^&`R=r85Q13m#4sYrT2{Ea*?PlAeSXO{QjiVhhKGm +)?m~D$y3n%dj`ne1hCiU$qlDUoV5xKPRO%G$lPzZ6Np$U%684Sru32_QwF0=B&OWvs!hU_u39Q>eRLr +-rUt&HpvLT}x1vMg5lF>=_Zg%d*E)evH(9#Td^uiI&c6Y=ls7%-JEacVCKa@u01&_zjhP0+COin3uv3wkOv~737oLKncAMWiO-vt;LJN@nSYGo3F8p6?^zvbw_OfejaSP@OHHy1j1@`htW$QZ}2$Sb!RA}@n +Q5h>x+kPjW7g`HQ#ts}aZhN_Wzo9n3Su&O&{g4ngMucoLRzN^?0#m%Y2BZjTu$O^6`9{VDl_QzN9t@- +}6ygnP-U|U}DK~>_+S9xnk*)z)cO-yOke!+Rq6~*aDR^9L&uF-h6et4_@b#1bz)}!<2!9$DaQTFI|jERFF3fbYsCAt( +rhdU%=ZAU-nauAe1~8m5{i<*AuTJ-Y&PJFH|n*j_QEdv3KZBu%GVekd*w#cPZSvp+)nfaR(=gzl^DBY +(^C!TGb=9ONgkq$kFqCma~A+jP%Hp2yh$c(>m&I`xEHR5Bs1zBKJ?ji +C&&!Llyvrb)Tf7gT#q6l&2$uZJ3KHf!yIh(`J|_hut(o?SlGa=tHF^H@w)c^fk*gI*!vwRA5bRL2%bd +hgrozN!bj1TC^6iTq-*d?+{W}f7FppD)Z)447+S%>qAnIzonn4Ff!jP$Qug$#j^Y)>q^|}h2gF^Xsj} +7^=;rh~{fY#h=~td~`NB(ZnhA$aOni$4FLK*=SHKrOI_A;XHcDbfOrI%0on?RyvpqM^VwaL+o4zby9r +x_apIdD2-GON1#)>aBMuDO#;1#&9Y&NSx;-g4AJv*p{M2Twbq+p&d>3icQ!0zK@QKrl8RX8UG9@(WX^ +VJ4V1sE=&O=jDR#vCZFlWIQS)_hRa5U~5A+EMx_47I?g;h(n!bQQsIDIVh-r=dd@mXUq-=|1;7jhY8Y +m*N!slylNdylz)f_?cu~mFSeYK^2F@v{|5P(PCzS$$hNh6M(SB`a^A|KZ3`P5c(CS84?#F-qy0@HEfW +$@kj+|7ANB(N5)U?OC*>bpk2n8+e9gT(wv=nkPFfcJuIvCA;NA&8lxyS2E576w%OL3s-b*p1e=tcI6{ +-`Nc-6}6;C5g-c{~yd|@;hE`D-OO`;O#I33mM4j)^ZYD{$2Tu +4X*VA@`iY{3kUFb23RNJc|4L=qs=8M7CG5;9_?PD?phYB{hnipk$Md^0K0_wMg_JBQdrYxM%P_ ++bnMK??^=(Slae^gcBNe+iu5atqD7R^{r7k2B*`1^U#9o2{h7+Ya~@_gn)9H?IRj8BM^D=?W(H+6Mc% +nD!;Q#Wr@OMr%;TrsXDCc9{zWnKM}mkq2%j4@-(wY(hl()4gJyd8N?$c|GeU5R&ikh#U911~Gdh>L+` +#z`4kD_M!I0ppZWXeYAI-3{k~ET)E(+KdkP|G^-^ +^4SOL?24;L3k;^B+XmG>PLqP`3FFGjkVH%PvEd%yvcAVVYc9fmCu8A6m&7F0ofT@{eMZ)PgTM%!|_>*97(fu>m{LpRRgX9Bjxo53 +opTvu0$L5BNRh9I%-kvTu9FZ;MvSX}{yVEKB4RTiAhmbv*svf3J4B*OUB|G_>u+5f6zptU2(DQ=yyhf!efSI~5Wp1;ZTMdjx-rQZq6O$YHm?KSA?d72Bvf +(kS7>cY!Y7Z_VzPJWdz`oAu$A<1BoP{T)+{mqIAO-=`bq|0A1B0Z!mB2hmzNgH1AHeBt)rx}Lxl^0Nc +hRz^MczcZC4f4P*xeH=oFD#-XF(H$64RKYRV91tonQYj7Ld^V-|@V$%1JfiHNdA|RKrsh?!}HH=BJ7x +J)E}rTUZPRE}?#yy2XO9KQH0000 +801;hJRl5-*v?>7r0M!8i02TlM0B~t=FJE?LZe(wAFLq^aWN&gVba-?vP%(1DAQ0?%1vBYy1d{BypmL +ozWH35Q#0UtW#QJ^ilVDG*h&h`=$ruG^=)_v*Y~6z)u?5d|_Hi~wE9gbWJgV~%ttWO4w +vo{~`BI4iBWOW6Y^lc+qQFHI{NAv(gP`9*2}#FcY5wkM*J%yi1@!RRFSbnBj2Y=|cdYyrEo;39TL~1t +=YaY|1&QL_LWs>@(s`d(j4!_mRm}JIl~d7`X?Kz6U`) +Goo~A_L}pBF^FN5F#4p}3x-^kjWj)UH2T)4`1QY-O00;mPT~Ad3bDXyj0RRBe0RR9b0001RX>c!Jc4c +m4Z*nhVVPj}zV{dMBa&K%eUtei%X>?y-E^v8mk3nk!F%X6C^DBn+q6dR3^db~0+ET%mLVM^fgzOlD*~ +~HFe@McbgJ|UU*iit<$AY7yEvUbQ`d^++uVc_U6nwr{N +M6K#dKx=c(wrGuySlsN|SyUSzdWW*}Zgg^QY%gD9ZDcNRd4*HUZrd;ryz47i`62zHbK)Mcbysb=UlhDpd1&8&p&G +R2QpDy_%kniDGY7HHC{mWsiIatbVT~>iYJfTJr8sN(Z&2k<3JjcJP+S{cxwE73GurmpELvUc<2Yls=; +RW@I_oS)~~NXW#YWVn?W57E=a;#GnRl>3=&E0&$$oeiUKaIq@g@q?B6YC{2B>b1_f;08mQ<1QY +-O00;mPT~Ae$=}f=>1pol46951s0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eVPs)&bY*fbaCx;@-E +Z4A5P$byLD(237p~y8+e-jHWQqF$v_a6g=-vccI>$t0Q6s51?y&!Tccesp(Mk6(0YN14c)Yvc?~X^$* +xl{*pReDG9Ij+}QsRDZHt=LF*xTis-`QG;lzrw(%VIK{%$)FNkwT&2^`{I9BX$DR1-mSHx`)?qi#PCzG9$2UdzQP9CAiq>2^%Re?4-<{2*yD9vj5kDJZyr5mf4#i=Zel6el +Q5xOEHuKe(V-LAiWxx885Y?80*>rY?TSDWN+bGi9;x4vtX(2{2TwMdQCQBUqLEJwx5vb#l|Z+ZGa5&Q +0@CX-2;bFF*Z*S1C-yXO}ZhL0#7c`h<;L~)-S!0ctF*^2!=z5TYCF4*+;a&z^OUar@l*PHe9pNUS?hmvv@!BMTUeC1CiyUTsv>o$-N6to?(98k9goe}EcH?4eRZ8h{&EnYRSgn5B{B9IG>RaSnC +|O$+UGI`!{&sD|-IMa;e-7zWX)2QWo;QXMcuWWkKYU4al3#w&m)1O{tEVV5~*IqGz8PL(btRw-I|1Xc +iKq{;~uanLO9$vH&B`1|#^RpXMIA_KknMGqIFPW=IIK$dvhS3|&KFJZTd>i_eeCU{DrbQLxRrKI|qjR +>=zSd%iX)L4xIQ>nrkxvfOr%0_*9v=a+Pr8RMtit14(J?NP+oJb!S5`bBDTF7{oVLI~69tb;zWmm}(i +E8>bH_@*}Lb}PhfF32UaCnpDZIa~B25M%B3*474Pw===51yLWA>is8qQ_i +u%33={ReGov#thxdtwLvfTvKKibV2C(~!@rxbS*i7(2hn=tPM969%n1|Zu#5To*DZN>`);~_WThDbc36)^@iWKrQC8Pl@(I$0K +5n&u~9t2uc%@RP&gZr6g*cL%@ZM9i!hB%SBE{V4r>7JO%)<$pYTN>VHF0NasyW$IU0wNM+^x5fQlNy_ +WZy|*nNer2lW6ldS0&3PH!?0j7FLq!h=j7Ne!^A5gS^8CKKh)7ixMs39({SdB>_P!W^laQIB-QnA%Wa +Pw#du(ODYX`qx^0n>2x*i`1X2(tPM0eq0R_$tTQY4Ev8^l-@W`f_EwDGs--pD{&(okrk1Z>vt4(RGc@wf-q`e +}n`9k3o~S4_|WT^%5M2Hs>xr+VNXcP>ZUb&GIfHFw*%5VV}IU=zu0zSgYV}-4=H#F;s4%+p6kB@UBV16V9muy@9kObf0Tf3=hunzxPP*74E~tm`Ym0?@}Uo +fqkEc!Jc4cm4Z*nhVVPj}zV{ +dMBa&K%eV_{=xWiD`eom5S4+b|5h`&SUnB?c0sE3iv|9{M#PKu{D*4!b3aO~+bf$&lnMUeW(P(uxzO9 +g4O(*pNv+K0cBj!2Id{*Z7B2cxS4{%1z-Hxc3vdy8QMX9;{4ZCag0$8bpKOJd-JEhvR96+RI#`oxn{c +(gMd<$z>E>TwFwp0wLv3VAj7e^>Fhvh1^(>Wu>s>K)nebE&=w=Fc-D*ARP$sg+-A?PUHwuk4r3#Z6K4 +WmqrWQByE!1n)2YvXasGnhnY#YQ52<0I0v`F;T>lg1|IPwihvGfgAMwjLzRzUiwzmzaIBCH;nDbM#}% +^&YmFVor(o9)9>Gqi8b^TpN?LT+k4EEX5i?Zi@#~CIWn2jFMOGxjQH*=iFNkrJrLV_wDYf*=<$*$bVa +Z=t!{$Vh%5dF^O(f1tnec*4$9SN&KfioHcl5Q@Cyaz0TXkCkU)27O^u>wt>H8Voq^{_N!)F4V1i+V%w +dOr|nX@wqaY}UVaL%DJ6_*apyHwsj2uW +&rzpM!_h?1C)RfQDMU&IyFq3@RKSbiXL2yX5Dft((Wk))G>l=~>DcLZWrBuSIrl*N3Osz6lBPb&>;?m +Wj8rVreRcy|x=qr7g!C>tTBHur|wVO9MJ%lbHpPyknjs;Lta{6ocJCgi2@I4y;08mQ<1QY-O00;mPT~ +Ae}HL1E!0RR9B0{{Rb0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eV{dJ6VRSBVd7Y9?Pr^VDhVT6ql +W?X%j299QMlp~OLy&{FX4wvQq}^R-rwISv0g6;3M(ttKosVbUdD{*=PVb(&_f+Do4_l_^Lgu)U0rYzp +S1@FK(IzT +O!((Q8;-AP)h>@6aWAK2mldXPgP$QNHORU001CB001Tc003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7P +Z*FvQZ)|L3axQRry<2T_+c=W`u3v$pD`jd%q7^5bo%5AvE^(YH*JWl>v1fOyd>$1=LK4Rm$t6JB+O7T +X*WCa}fNxfkt5wxZBob%<{rYq_e8jFlz5m<%Lz41~yx5n?W-D*_lRRUmiV}6$0e2c=AXK`4X-!j?p$-2yUjPJrE4Jw(Wg6Wq9Y@NrUGJc>Z-2S`xsOVIBX~;^mCJTKoVbCox +|uUM{OL@!`Me*@VFd1ojCZ3l=imUL|1nOagmKzTr=71FXwfl7W5{!1CmYy<_>Beg1I4Uj63}Z-NO?@d +Yqy2OJD5xkXLc&tNsVoiPwk2|w0lxY>cSO<`Z|Hq0*Q-%XiUg@7@zm0%ajXiH-P_1ss3OAd0*n6Dn*z +Py|1dAF~cudkc0Z+ef0!;LtshIX2pP&sETcT;f*uh$y-!0 +`tMqxJ&Hfa3t2JFPoXB_z7E6zMCaYZl3G0_Ttl1ijvVOqf8)MKya%qRFlrQarAOZU)knm3ziy1hrSE# +cK{9moTNbJ8!YC8`46*nxd$~4Q%ogbxQ#u}~GBK!qjDVs%w{_XQelw(wKFmX_K5w1DA=X>&HIDoclRP +p~qt?>gw0L@^ElGc-kUb9`&>@WcVT|Py;kV&5T-mh7@XW>fZX(jOhS74?k>^)8MM;-@wVp#cw@YoAF{ +iNZ&TJKzc32=ffn|cQ@r2bxyYVRHo?QRvHz*O(VYX%>&3$lqAjAft6@QJO!iN-9`VhHh2=slD)uGoqL +>$x=_{UW%3?SbNC1C|zma&h=CqSVA|zJ~a{9VM3Kb7iJ5NcqQJVlYfd(5>f($ui6YsNwFJp*fi1JXx#N< +7Wo%Dk0}3e*mpV?DEX$qFSX%^xU8_c{qSnnLq6hUs=pSI3x1=SEJxjwAAVaI;6iL%JDto$XsipH*;T7 +ESg``FFkKMb*u(Idz6NJ3LoYZx?rFNtduJ_uW0neK8|v0%T$!(YDpw`s?>aS9AjDlmb;J&Og8_VRr0a<5_+y^mljRr4bkEt(@vI@<%#R|oP#?-LOB$S +EL%(;By0g5JWWJO>syr!y{2O@z*o~q)gRm6Z__7J<%F(-e=pN{^CaN~TwxO`5>jSaXz5vX{<6~FpZDu +^>~YJ{hS9>KFhlnRxvR;1W6P{XVK&mF$&g%yu`o6?vzR+;mhR+@NUN9Wk71dC +bTz`#Ja4w6a;-IpfX6faHbEo+*!3{wyCPlMh7Y`Pa|MS?E(2;XOF8G6nGZZkb~|_qqDuDx`>UPT#d&r +tS;m0`WZ)8YI~p_b@QBCv-&^Xke1*%%FGpUegucY0(|fK2kt`pFJReqP +qe&zs=2mHyadFVX~gf;xeGh9j0-IXkpc6%$u`cv$dIHuW`91d9ySQnjOi@Q4EqJK%tor~oSa31`ORO# +{b#GU+2j165d=qBk2U5V}gW6C11;b^@rU0v&E;Pz4$>dfoYcV&G>?RTuC~SXqM2Va`QbI(4T_hg}Tpv +R8L;1C8o7X-ZECSh1)MU>5(-+RRi?M~~7cdJ2rxFFF~Ahld#-rDXe8kS#_>;sTJzd9%ieTL=QRDB}-c +3*cWWVfmGY8nRIrAjt_q1mJPd8R+`wPgS|G1ZfsLgWWsZ^15Kr@~LXEK9Aa6u}(dPmORK6LDGVNKH2d +A|7R7kXxHa;LxEmzcD~&+@U={Cl|1jPBp|BF67(3OG!TJ!?L(Z##pXnK091JwY8u07s=ft|7AF8wPY{ +GOWF3HX^XvOx&)5w(IV#A4lmQAg1;$a19tZ1jzAye<7f@vuR49qd{Uvl<|v)S75bHdz*MNpg%4z_c +9VL78Buh1&`RrCKZ*MM;i<)2Y#V0UiM%!1Y^zbgAW$yHJQKRe2UC2pGcD)WZkMRbN^|R0x+|oikn>$0<29N1%a;0C&zf +H&3UinHh-A$GdtEJfiMu}!Ti +pioB0yw#8$+*zTLv9dGt>QlF>>5Rg@~p|!-#M56iaLzaPz)J3ak9t#!%XUYTy}yArnO|c0|}Pv_A*33 +?l~YZW)HfcF&u3r)vqE+mdic4LTFiDPt+wjuo^1EeFLteBDVKk4F12G+k#lpv#>LF$22%)?8Yv`w^%f +&0V#6AZ1RJjL|hHGXkQqn_~5@a>PDhgf~~^&e(g3QYx`HUA)1{tWLrZ2I9L-PU%$M$)@U$iUt5sAipi +cY*SZ@n%w8$do?4saN3TUaHJYwB4_`^kwDr-h}ViBPGm1?!qjYREjk-mZp2KKyR_Y>qPToMtkA;U(1K +21)qZr!XOnXeen|bHO`)d{(Y0Ppc+siH9zS4W&og~5FC@E|xy4j$KZFch?tr=GA9=RHdoNgFuoGkFH6 +k6=zysHrp{imPN@b4C)wkL~(>^R>){f}X9w;7;1qT3T1At0^ADL0nN@3bK=ai)aoirsEZxIvsNr7clW +h3SWn{@IUSIcNmR%YqEq&q;7K5a^;?Uih2Qde*-DtAF5Vt{YbpLQmjV(d79Df^Fe20wh)o40~wTg9UY +Ta{nbr)#E;PjfeH8O68tA%81kRZ>#I)OUtW?H}DIwkO?=q?1h}m`25MJ6}4v-iCNJ6?KJD0~khEe_~Y +-vC4=`dJZR0sM^w}=Dtv0qG`><`!OR)L>)0&4N67(csjNw3ia#P?d^;9En5?%8uAdDAIp?X9hsWkjZ{ +_Aic-ikte^L-kDr5T$<{{4jp57gue}al+Wnum>@OkgS#u4LcpA@DwXO!YySqork&|caySB8u^6HIaSbN2d+w19R7AV3f1os~*FC5_+H-*CYNTU_$I_?+(b>z_>^t_uo7am)E7NvsJ=DKbkl0jA5Mcjv@h=b +#Rw#rQsOFwSe>zq_y3pu6uIR3PPaBHlZY99ar@n61>qL~dv1&xuktEFaJ)KQ~ZEU>GOf^__TbT^hwco +mV^B!TFQ60CqZUcg^>d^M{tGnlKW1HuV^Qpev<9Gl8PHsmYO9pi(!XiUztw61I85YbuMm&fDpxY`eTw +$A(B-iG0rx2mq7XMzbt~R5A44y*P9lg58^kdhvJb%gP$MzkgVX5Qg2s)0wXNJVAwR;-4ZND*C?eEi>U +QaYqK){btu7Q_Y;dY)9XJn*7Lq%J36V|2&ea#FW%Hm-yC +=?ODdco=p2-xOx_+6RZ4Hp9Q7C8t=_PHsmQaIan2p9Alq+7^mhmpY8om4>B| +SJY#D388tUGV9ez3Gi-MF88Z;qP;EBTYn0C%ooex#STy6uEYu5s`N5DV%s3A9AIs;OVDsUlw71ZL(7rQA``%QftH|dgX+U<%xHsX@F>BP0WWi$0 +0;8FxD3~Fj?d;eYL$q2@p^7`(dMUb?BuYaYSG8eASGO-zFZ-yqR55H$Uq6=mPV59?818+0a+KlRJBUo +mk-#xN(Moo+8k7FMHr^)VwmSu4vxb0@J|`&o;B#Ls4v{m$~*kqZ=8nLdQ!Yx7imCk)7TeyfzrBsWJA@ +bD6Jr4w5q4ws@0$tntD^9r0P1f7|X65$Kl`4*hAMnVm^Uy?1)-ZPbA+7zc-J%H-Euss?6;J4OEh+Zue +Qal#rb+78FU0tI(Wa4@uZabdVdmkbaJ2Z2E2gRKGd|tG5>E{&7QVz~uh`P)h>@6aWAK2mldXPgMwkzg +4V8a$#_AWpXZXdA(OnZ{s!)z57=X& +LKAHDCq*d1n?ovCcpy0cCiVt1-gYoOCyVGiWEpHj{kk%kg_d*v_MgyK5W+3@bS&en-5)x=Pw_Bzj{=f +?rpnqYFURDdJnha!_CjXhzF+%@g$wMW^^&SNXAd9Ami!kQ%NSMN|C!2cdaaHy84jaj7F8SjR@4V6;Oq +0TI+)NEE_6ch`&g;;rmAYlctgFXf!Ic^j_RctrxObvB5Vd+m_tKE`CrErne)3&v-nJf1evzighv((%H +_GLW-&t$y}^!Rn%gwU=@f2y^-65TA8JABuk-8utG+tMORhiW^iD+=2v +-$7Rs0@moS_I-SQ8KyFKm@?5_&d@Pw#*rLvU&V3=i5QT4&!CE&v^12)IE`Zt~xUn`43aVp`@M)+Emc8yr5`8an@EwXtZSqxVlhcp!?qIQe@ +d!Eo-3n5o=-gKYKAhH?ey6f4uAMzmgkV9n4hW0$=EG^8Y5(+DbW1Ibu>LbivpVi{`luzG0b=O4#_Pku +$*@M4DKHAx`^U>lgmyzMX`34(kNE)AtYEMS_0SXIR>b2ZJp%jq4UpGh#Ykx-}Dmu?& +v2#LNr`}|sAqcqraq}l9&aPRocEt!?8|`-(psPEII8Lf~i8KS4yRG4abI?$1EU1dol4rrtyYgTdD;m| +r;A#o?xOomxl&Lte1fiiWIv4NZ2z!=r@6%RX1uBIvazl6ap{k`%QUwzZ&)#TY&YssHwEp(`y0nGQT69 +foGV7MtAFk0sfDR>I(|arykE4h_c~t3#O!v>xTr%iYPo`o%7dMAyiQhxhhScv* +pF&if4p7XZAN0F$f#o@{ugQzBolZOpZHGQkgw&w9Mc)U&?2fS$%vzvhnLt~ZN5i}O`-kV}{OiNr$J09xe>oD@Br%#Ei67;FCdsWbwm +&Y}sOlM-S|JetVR?wI7572zhp}(Ozz=}1|FJGmcExJL`xlmz#;3*^^K+lF&*#XY&Ea2^29`!`i`8)6Y +;R4?x1WH#J6IhhC&jK|GT*qu#1%9yrvo6gW*Fx=CNE|aTiG%b&lCb%ANacf{Z-mSicO)1t)2ziPc43Y +G(s_icCR911w8;sN<%Ncg>Qj6JkS3(@$Th& +o3ze2%e?p7pmip76|MNcI?-jBMbLKnfT(K$42Yoo2!*DumZftefU83L`x#3L=$=5M$yk{>iFL9CRl_* +0BWm@Jf-ZGA1Vz{}JV?Cs7nF(33pw@Kx21RS8v)R674BR|d9{l^-VI7z--|3m|d@u>8w`V{O!pEO}3^ +3wg3V2yjei+$*3hKcAIJEfAli)*CLpsRa{k!{a#>cVhoYrcweaZ4k`SE>?Tc`C&*!N$b9z_g1-s?C +SDWzdK{|WPEu`|J=6`63R7cwdU{7kE4_GIUGXJCVglp&|X0MA84P>+UK+O1^jtF4_d8OLQ|l(1(P%)s +iv}H9rUf&!w({--B}CpXP(iS{S&edij#tq;8L+6{P;+VXh}(80%_;kS#1#Lc<4f7 +a+Tpwd#B5S(@wYBMW-<*O2HpQ(NkJVrZkH?V(~AHjn^t;47(ySXZck}QJ(fOkSYW+=i-`Q5Crx8=PC~ +ZH-xhU#<#cL9;%ehhmxXW6-%}-@AZ-*&&Yi3ge3o#N@ut-RM@KD&T*WN4opEI#SKen0$KIIMJe%#C*gw_Ij&xf$0Ha(@OjI_g+|tbR~zul!Y>(S-Qpyh;F8_BC4V|fyKHaoHL}i +54q4gUT^@029ervHOY0-Payi^ZUY=C3ch|EuL{@11B+E@hK4(E*M=LYSh{OFYfQkKgg)VFbZ5t?+c0w +L)_shs5^*W{^4sLHZOPJyjgd)&7EJS*?Ew6Ejz0oNaWH$(>)jR(4s!3R?j|2|`i`vM8nQc$&f>uLwAm +5ORf5b{bely+a@SH7jIr~raVeV7);29MHVtckFRqpi>wd{NECUxau;{V;9#l;*T|=)0rn5WzYhYdn-< +qWPi5)goqn4fy&Q~3dyG}uT-|zIy-PhUcZ{4Q9I+)M?n^Tz`wQ!#wF}4Ldh()fY;MPS@lnd;2_v*}<) +SMqzevj-do+rK6!*1Q{p|}4^sjf1ct~#5atKVsHPqH#Wp&ZWDJd&7Ov{jOl77h{|L-Sjw=$pj+njE|sl^AN9JK +j7ELD?3>HfTweEK-@Mx&jr%Y)@1~;zyo^WYf{zEIv9-SYiGqBvXPXaU-Glu1M)ziQliF41#zw*0HH&e +}8f7SXyhHx7X^}q@#^zmt=;g)M*^^-ZRQ^_EyicDxR(`-uU1hGWc1Jwx+R5AQNn-mv)!7xgO-nSfBOk +k=lC@1Uy)Bhw)oI7H5VM6bb7{JBAD*28o~xV^*z7x-5OFCbZb2h*W#*WX+s>$OhYxJ%%|I_FHle{iG# +YtrtWsoqnNy`A&eH0vSx3oOUL6r(2&OFX$k2oR*P~IlfxrVUem~sK``OuV1k4>L@4I|T!7OY_%hW>Wk +Hgg&IlRj3TQ6zdCxvUPj$ItXUh98QO9KQH0000801;hJRUV0Fffxw@0Kppo03`qb0B~t=FJE?LZe(wA +FJob2Xk}w>Zgg^QY%gE?lxl<|GrK4T@P{;yHo!I;><*FwgMmR +y^q3i$Qbo#+cai_zb4g03BztC4v^9d*Baw%f_i^syjQ#rY%|9>R$x^(o>U|^c*XBliHP`Ir^A|s`w~f +r%PrT8Unw`zg+~S{PE|eA*?+c-fTuISfvsX3G*W%)3_Ix&zo4RTYTXHRayz2gvRrl))f%)IRm2x?ot( +t1XYFTHCtx!ePF#mR=?*aQTd7m}nU#-waXWBGvZrVoZZqI9+`X16LEsflYH!?T=fGn>zH8*e@Ev3pQuf@{7n#Oz-oOYt4QYjpU{LO*FB4=Z_*Wigr^ +2Ta>Gmc|FpPP7#DV|4=F6w>Syi+r=8^iALkdBWjMjWtm?Fc{&8SvcODgM%V{v`kb-PYoQqLmZ)>XYFt +AUOsbUa#CcgVdsVX*y)YU3EJiTA)D>&%f*Ubd%68JMfv?1lt>Ecw%~er~hM9^j#~>Qs#H3+7H=1z`ps +KxJ(~}+kQeumJBuqBgXGNbMgrgO)w`N``gGiabgM1@V;_FLd3()n) +=(k-ai|0eBdLUJ3fQfMXPa|PvX$lDdpTDI7l6|vDfTk^O3H$koT~sE3TTxi*%=hYRAm?( +Hf6ZGVmsJsf(3v*qkD3xa2C$D&YwYE!Rn@3mSV%!f;6k0KwWH)ky=Hr1HlVzZCJ?F3j0(pU@Ef{kRX) +JMvDEl6BDd8-&#{@VaU`Sl#%2h9U2bVq-r_~2W%SB83f}L9TXXox?*j*Fqt1n*>(MLsn*0c_=qsi3-)?HNcWN^Y$sB~HHdTY5)1u}LR8;Pd;Q +81FWI-`MSwWw;XVLf}2{<+0CJ9@C-yc%8MQpP%V`S2ZjgFHEYLXri1zuuWlB9=mPmZmHZMCdaxY$2Bw +i+>{?c#&zVPxao34xF<%}^kEa%}C88^S%qH^aT(W`|#aQ@zGUn+aLfHf+nAJ@XnJc+V~LfOpXyxzV?f +oc)W?CLP%ixf;u)D1t;VUf!RP>AI9Aj?!poPO`sSOQ#L-t|yA}E9ZeF+@r+mwm#x3?)_rR%T`zcZI0_ +*>|PiTk=k4|1%N&V7>Ep_z-i(-apG81_hNd^z$CYBEtw|Vb+B?*Z$P2hmrAPmb{XaQ{3=R2`p8^rC)k +v=Fv(IXE64n%!TEPSolp@_R=k)L0dAu$EGSEks{mAgYbu;_+2k!)6>36i-V&37M(-sh2dY4NbIQl^2U)R5hEq%{&^8VQSR^mG|hhG&*TQ4S?SArEonJrF +Ag>nUvp6#_K9rrwrQ$n0s{bRkmpf&k$><9;YtdZh7q2`MUljsI3Dl{~x-Z)Zr0T-*3B7U-=;b+Wa34ls0dA-Ww;>QMY(OlwzkiD|0B* +R6fs`ciso)5t^sey=a1vG6G50t-)NQ@Sb}(5*mbMiDcOl7Et;!9(h7>77Icuxm5!jbjL;J9_v4tw3Zl +rc)kiQ?r?#UP<2B;g#@?z2P_m~WF>gS3_=pc%sPl&hGev)(8#+r89()=Iw@mU01jov&7RES#BUaSnxe +;7b*(esfsvdPw}&G7E~~3Lc2#o_hAvb1K3dQCHUpEC_3F? +4vFy2Pl1PUogk)EOx`5WCYi0%BFo+%wF}sDXqrTGBMXPC0$>KKF?tlV4v|YqT7B+yI9<{gdQRV8AybV +Map^WIgKx?Ep?D1uoai3tz@QD)W?N`(Aieg=7>!pa%83u!N$sdQ6ZHGZO#0cmgq=Cr6Ku%Y) +aKHw@0;xCn7NQTv@FM1+vX#p?CF&F^z!6U$S@I1M>+9$T1|tT@w06*ZSA|!&Z9?8<~)y!>c=tT`;!Qd +EuVt+(~tl7!nUIdCjvI9vF+1}@KcfL&Ho26cKJk%4XE->Ye$xSvg;}d+)`tA>c+}6Pu5yeNsPsIP)WC +>k}<#%Ur}$Pqg~S+vvPgf_R`k1lAz4G)gzXqiRqz*(N%!fm7;rU*KnC#BMq{L?J}`td({Cy&8b)aHBR +k@EeBEsD&LK#`~NR~{Wnlc0|XQR000O85nWGJoDoK1G64Vp1_J;9BLDyZaA|NaUv_0~WN&gWV_{=xWn +*t{baHQOFL!cbaByXEE^v8mkwI(1KoEuR`zsc6QP60GUIICYwos5#sE6KUyP24Q&2E^TK-*tmO`_REb +DDkco1LAPNPd3oz80@&VC$-1!c)Nq_~AsZS5MFKg;2}ABJZq-MAX82)L=a<-ZHR^IRYheQz>1*VjZu9 +$jOy5R+*|8NMczzV!6HeTW6V^*|b&%>Wg#5cBr{Qg%BC!gSM%V=g))p^QLMV(|2bfI3I!4$)^XZJJkc +BGWZ7+pas-~o>7PG?Gyua)JAz9SlupndNWad8#tE0h0CtqED^mg(|)VzOk&>2=;ei$U_7+a{j78E@a* +Vel0oCRrA_Vjn)&e9#TC>&5G2=^z1?SctAfkNVUC5-I|d=%DJ?v^{r|XhwdLugV6Kwoax%ZJ{21{grh +y2!Cfl&$A5cpJ1QY-O00;mPT~Aew{)fJx0002;0000h0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eV +_{=xWpgiIUukY>bYEXCaCvQwO$&lR6h-&*D<0!k!-O_Mi#{Q65!oV$7zTaj19gVyEb`yiWEer)bMLt~ +fc&_oGc%*g2{QIfa$gMU7E1i-oAzv@ok)ldA9 +(sI`V2yiYs`AxEoOJ6xM`nD9qrEUEuBPacgOA(YZhqZBORDkoQlApef~;H%4uac&{D_|f%ce4o)AR8A+?sgZ4gFs6oi))z_NGgtt`|zD1DT%U^+!d`~ERpkne +lr~EggyN0)7SUcqJ~S=?6p`|_7R@#f}NlJ^#^vPMb2)yHcC!TCMTZwR^%WJyuT?xT2Ttn3-+Pmc?Iv! +)3eD$Y#XI5tGKCZvFcuxnUq>>nMLV4%^Mch3m`EQznP{v$?5h!as!vNFlP_YaLp}zRK>nu?wORdTGch +^j?-;l_REur)%(SS;g4LsQ}$Z5vbfS(=?OgN&{%fIx8N2$-3e><=jqp%dDsO@ZkFzG&Xy1EVK$je3Mg +4-Aa6BfssWkpw6Ktr$S+a+oRwQuz^v2o1(=fcp6$4_%qr%n>A_aQFyfjsqZmnI!ZINPg<~Q~PEJ|#wL +y!NNtfA|`{mv3w@FaomM-tFK0LDD{L9tX%UiY+bn51x^*?wFX}789WPPjnlmIx`H&%=d5`rV +fW6lnRIZ*G?0sO@6BI9&RYW{qw^18gOR~)Zk<|kf%Y423_})N3&Pxv8jMRLPk+03#jnomPl`!pS&6Av +J7)IVTF8wd0~!rah&FU>3reDB!q1j(gj_S;Q9ApAC^RmJ;j+L4_}pUjT@*O;a2+hfcp~n(wK`E4!s#- +*TV5}3Zs^?$~QZ@JzKkYF5RD+N~K*EoJ* +?J0eDf~3a9~L$7KKqh0B@ZGAT?Dg~`%Bfqk{n50z@zs;8k2W^OOk-EkoetS9WTL+7?N)}_^1(1Eo>Y0 +f44P84P%8pbQ!Bg4rhG`5#VKzBblq`f5N@7MDluk8ZcYtb0cpnA_C +6`V_o70T4xD1lO^%tjW1t?b-C{oN)UrenPwfHwmVYSLPAq^Z2tQr(I!>?#+iFRCW#Kss1mbQrolT-d$65U#gP$tLdeTrZIuaT(iZXKjiFzIPiC3% +dklPqdd94hBshABGP?)g)<~4Pa%pGS`nV-e8kY4e0I(?`(slp?ohAp-M?sA3jXDekZ)^o8Zt_4Ehq{! +r5aE(0^QrIj@dbI}Xa^AlcLZhKd?}sHSttZeI*St0mEu$ui<;9sg`|<2-B%)jYN4$=y&u}lJj9=VgCW +kFr%ADWP29{7yfa#Cf2?JC{MC_0_2#B|aZ1wSFKl8fvM`SX+aS2KbED?v{^N3wDF3*9DFpSV|NMs`CLx +d)4~3Rq>~>AA`}-SdJ44miRZMrZyIq=)ivxoHe3Z99V-(Jyj=ZL~>~C?1f}Tp-8;Tv7N@6cVXJn9L-+O+`=>^qPu&<4rtni?Obh1p$G`Pr$SaZyIi^^t%#13 +J`WuS(#tDn@+j>i_9nNGvDAv!p~SR6m_ +_jyq+0E0}k?l3?}K2fiL7(LNBxVsGetsxp->~rQa|*_Bc3b0A3A&;kZNM^~m4caJu2SZF$X(y0(+~!t +Ifyr9S$jmh~#`5OAK8A|I#I&+##QOk0-X`F9Y5HR$Q$nS%OA++fFM9Hl3uC|m@_wI5~ScPe5?L>Tq@! +!|h>`sTyOgIPZSy8lPb{gjS5=y?#*3a`F2MkCs2NR-$&x2}~Kf1Na~uzXd68yIV@*FJlAPm{IcMVO#R +6XRjLqd5Bj2LhLhDa}a1t@vxl8!o>1QRKkg&(4eYfceV>-F@Hm885*>X1EsNdYgAws0R2ZgaxMozJ?c +k#y*>o+&|oG5na=iIHs=`ch~U`8t?z9pB~sdcX9detv>FjPR{=Thc|Y;?(UmYtLx0Fj8c|Sr$}&N?lP +Eu65^QXX +bE3{RdD>0|XQR000O85nWGJUefd&!2tjO2?PKDE&u=kaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJob2X +k~LRa%E&`b6;a&V`ybAaCv=GO^e$w5WVYHOp}WZj@d5s64*n#ZE0aCr5~_2qsrrCs3I9@oX!6Gj%>$v +AgNB)%zIC7KD>sX-#%bZC8GsOd7WE9IM6k6d+lU-!(3}&NDF*P^8R2=hC?fGjnLCcPf^DkL`+ +XJ2k7v%W-RLP#Ttzw#29kV>Y;xXcX>N;yq)<^o(&Kj+tt`1`s8bOzXFcx*_i3`qZwfcg##Q=>D1BBcT +w6(ypXrd6MKG3Nz`j4zs3BM!43-Nx^FuG$|>^Gt{#gdwk6L+?i)Uy0m^*2oWh*wPaiw<3snI$rsOVaA|NaUv_0~WN&gWV`Xx5X=Z6JUteuuX>MO%E^v8Gj=>GXFbGBOp2G4emJ$z8sfTXR4I;}S +DNX`toEUVn^mqh(v +wb8kxL4@-#CG{!^g+>l&v-7uU$onjI!`RZ5-!6>wW^F|Y_^n=mbPGRL5ocF67PR7MptxBi|&RyrdfhW +Vi)?VL*4^T@31QY-O00;mPT~AfamBcc!Jc4cm4Z*nhVWpZ?BW@#^DVPj=-bS +`jZZOpw{v!h6wE&9%{2s`Jc+jLKfd5~E>FbgD*Kn#MHYs?@(NPs}_>#w6d#E?5Ov#Pu8+_cyk3dh}X5 +cpc_n>O^nzbRe!NZQReUjL_DHDOSlitPH$Y?}TX&we}n*mxcOPZxYU*#CS#3jObIr$=~6qW<3S#s~_=P>RM0hJq=QWMG&;VI1_)1mc@iT(hLTwZ#=I1WPtJu_4e6LTq4WLm(R +l-6Y5+q&7sn!Ke+BZbD>3QyY}o(7|$=EsOj-2RI%^8;0D_;U>m6DZXLi4T)~hU=z?ACfLx(CVe{tvYa +ZwH%Ppp(G3&5&4&`?&od^ki4b^$!6T!n2n6YK1gK5n_wy*$U0rQQ_E3*>mjcKTfjyU?hxDF +g$hF_)0!RI%|j$XRxC)i+hbNWr#&=k;^sSDD9_gv1cyR9gb}*QH;9~Rr65NZ$64=;nnLSRD6m8dwA>9 +SsbDcP>30s0gF$0X8pGVnkAM$54LM&hHFy +~dy#`C_@O~*{mWhSN!ZLrl%O@qo1ViLl!)94r;e?T0j_=q8+-yS#gh${*mx#RJ@M@P1!?VD(Dp=i=?O +MEd``QtW6GO{K6R+cXe$Mgy?3LY$)C-Rc+Noy!V7Nig2|i6^7g<|&+gR{U(_rUfuW>9sX*Op^6e2vHJ +cX?f)rr1I3@&4iZt;h6sn2w|y+MfEUb?0volA*_qemBV74Aw*x*;b#RIXaRF6rrUW{T849aR;t`Y?)R +(lX_(c>$lG?rIq=Maecl$XS225^DP&0m2k=uiC}aZVhX9DR_$cpm~}}AM>f;IlPq +713wWehTn0~SEQ}(z9u0AM)VT|LT%L`s3Hqh92yqrGu*P<+aqwH`%y2o*%o5ZKOanbnO=avcSe&g>vt +G~4X=ZgUEAM$($jk_H5crNImOb#`C1H{4@tTSZ3M=TE+TV-eVo5t;hor}6cjd=+FzUpnnD%^c%+o%s_ +}yWKrVEl=u`ctWwi-#{;vsfOx9!kc*W92=yjgieQFs(Za)vWpWqyG1IWqQK?J&ugHLX+AM0jwB-iJ9Iiqgd^wM^ZE21)NWF +fPD0S#ION;B#_vzbdsZ=bEj+tjxl?_%gBTKacb-ry6Fc)5ig=8dn0MTnU1q=;tlVlGNnGrfvFx?cl2O +X^L&Bf&^T{wTVHRp4wAJkNrf`FF5>zak6h6mXzd9Fd@uKcd{tajU`Omj4P1J=yZfMX?s3l(e_R;nKxF +!HMlD03>A`1GVxqf3GecrT1KYjZT02cIr0yz4yGeDwo9RHDgZ(8%m)Bn44{Z?F~roH{+TR44dlk^|o{ +-Y}DqVK*{MG*euKiB^e{pUA?VVG|}&bME>B=Ik(`2WM@eW31-M~`h%7mxbeYx+|XJ=$c!-yvy#0b>Ry +VUj^{_$%;Dk_A<<7kpyqhDsK&OaOQREF(54wZL%*U<=<+0GHt}!IywH7`%bW1-${PGSmx#iA|7fFlLz +}3KuX0s7}SpAClOh@iHZb{04lo`0Pub8KEPKz$j4+Q{JgQe_7#EWdO@@`PFXf5kbMfp@M_VR(+TRy++PVDnwl?0@S&LiHcNQ4=Wuyz1Wz76(WzZ;6w3F`oKtkGdtjA-=_8(5q1p5zSHqO_5>EG)6IdhxoCVe!6} +^Vl@GOyMa$*xkDHPF!raV?P1-p5&Cs@MXnwm%1jZDXWq5UqS~qV1>1yj)q_>e3ba%T7`BaP>8aEqV-i&&0?HD9^Y +y(gC4}1BB(Bj-2w#Wn9cI!&zj0EaD`F1&@_$>E(b}jOPKD9TSE!iytS35c8`Fbwc0m-o>b|S1CG3p%D +r#3fFa^*bEkXCWSF)Bzun=k7#Rkm~=XZ9;HE&x8svBD#SE=Uk+huEsmPcjK&y +<#FCJZ2P^uXh^uls$aq`h`UrT$ihKTSwR9WUcXMonqi&eKwFy2W*xfyj2_YXazHLL_C<>HY-KZsLRS} +pK>cO(SYT&a6zJO+tpN^-^42EJd5;FXg(unkza)zTLt;vd7vCj;&G6`?vprQ%Sh7B7g(IE18UP$Of$n +htSYk=dh;p_4iQ3+HGQGb%%7e+OGQRu@6zC5CQvky8;ZS=?xlfD0&lz8j=J2tcr$DGZhqr!O%c0{M$<5M~pT8ytV(HnEA~4IXY7Y&kK +I%b+I-P8|NVM4lIky!u3GHe=a`^%Y73-Dz|q|0WYkGH^Cl +#&T1C|xVreI3R9H`d-UMgG1F;DQE;2;5of%kF_-ECA!wc>uQZ)`g3)Z53ZJ>fhTo=4ilbSOmSPJsisL +Z-mE{&smc~kHo0{&#%Pw~PU9|E4Yf&ShCV0M`a@-5NQ!2NlLx7|-?7l7L3-!2cJnE&JD0hlMe+L<8d4 +Pf7tyZ~+{xVB%mGVXmVJF?4hqv2oM+D;Q6)2@YT0XdVrKh>>6ApEt!lfudN@`xjk!)~erUti_t1O<2A +>Cq{B%=NDD1`;mx=0why7E*kSHBKF8Umr@$XwijE$cI-TL3X`HtIRZZsX1%6;&Q^O%lN2meb?BU){uk +Ss$Q~#>YEqc-2^|5v%{>blQ^w;nQ_MG$R9^;zvH?`fX7#`(cUm*xO9(HYD`g8L}t&9_tOJ9*TSm5d(w +Ju#T-)dh*WyDd_->UF)P_8a}E_b84eQp7;Kg#l3XCt_4C{Z;S6|$KS6QCV0MYklh1Y!3#+D+ZXKU=S@ +TlF5#?=&Jd!%ZkNk3LE6luT6w=7Tc1#om9oACJjEoL7_BTgEIv>JllsYVlvK_MnDErrSqeyF^gZsyS*7CpB)6?y9LEhY^DMn&tare$wYsM1t +~}vthb5&UCd5h)9!5LkW3_D!ZAP!Yu}0PknRp$uHTjV{hh5xT`x9g`3cl}!RThs^kb{lzZKoP`fLDyNaA-1nzYF^N?yVfg<24^5yk#y0tSo%9y4Y7Aw8B#A(F}4_ +bX?z2tn-~CJ^pc+V29Fm-3_g&c8?%6G%KQdash7nVpec@RaB%i;6Oo%3plQ6=wTW;8C(BWws8MPGY6_ +e$-Xz4*2p}1ZUGzKn{4z?T;ezTwJ_F%RCmTHaOYvJSmS?Q~gjLh+4d*xFw-bKGE68!Qr*vWM55N-aPa +W4OBG7FU{#+E|hi=ujKMP{$yCT-I;0%6T;sQWx1V2D-IpwD)1_0)RV*muxciZ3sf-dwX@)Ur#-VY*g% +ub;5{v!4o-+iEB`TaQeVNkB1MFIa#0KbDdssZ7v&)_5%4c4j#v?-r$kZ!#i$1G%jwslvIrJ3F+u^&Ph +`7VTcP7Q1Ap#dTQ>Ek>1m^q+@qlf>q1RV^7vw#SmKZP(@owsOLo-2S#0nEVlE1-(OLHoIV7c$G;mWr2 +^0H|md{lQ4^`OWMtwtb17#X6vV@9=5s2lN7v9slN`68M{WD58YKGT;hz1t->b1_Avgh1ds9U+Vm-^ZQ +-RC>UXC2PcxKPA5cLY9RfAb?aNhT}Tn%9cN4w{d!-*iQI~T)rID$x=N>A*P7SOEc3uJtXnCHnNXqX?s +h2-;roh>tW=ud=ul0!X5@E{m!CtExgsz>tg~`ZTZ>98{b6JhyXL*S$R5U#zrfy{4h*yd8ebvo^Imk5E +lL0u2y18cvO4WjNrB_&JRlF^Ny1E>ORyIRF1g!X!tG_}2`lw1D^MGf^o4w$s`7ESJ**UG(I-VhzcP=m +LtAGMDm#j9WO7{@*cpy9z2+WBSMcj@B#Na~dryZ=1_$}LVdOH{L{zj}Fa+OFV)o!&8(0~=X)i2j +3Gm2Gw3{b6mXAlPt>cA`7TK|&hZQrc#tf&fX0c9-xdRTsb-tV07`s_V5OTn}Yi)gbkxfeEl4L)h2EU< +3InGIAP%qFn~z>#Dvz{Q~9B;0~K5}+Qx4>JaUIb6K)1hnc35vN2NUJk)3oC*Q%ud2q3`1tu@B2 +&QD1{nU7g=RBCdpU?5PZMIQmGQa3s=g-fECrf^@k6e<{3{Zx>5>NZ`E5aSvVUt;F4yLZ>AHP#rjMz7e +tOW;wBWD&hsCl@OO=P^Zkr`34AMSZx8%lkuHt%fy}JV@zukB#ieU(e#*-dD6#ThEh`2zoCckF0$lugW +pHpe#ircevj^9%%M34%_pPnMe)6Lwt^eU>??E(_&(kJDs)8iM87r^xq~mEX)3Y*>BeBB1$?y9r&hh;% +0F`e{GZh7l(e(h4*92=+6=DFS;A~iWvZirkD+kFN!t^7b*ZWD^ROA^)f?8i;7(w)ge&FKu~`zxRGTji +Y?w@GG0VG;2U(hiJ1*bY)E_)0TO^Le}GyBM?p(WF9ZVr9WwxmKLCH=O&TtIg-R9%fFX+|fC49D77sL@ +EX!L2H?bKU!{qT%g~;eH= +Zo81BQOz2G^toKcaMh6TW!`5nw*O%O-zrO?@zG6!IegNGBhUM*zXYFJK2XoVZhF(xr{G!|(Ks!gp25u +;XoS{6?+QKdO5PJc%#&_?Wzbc*<>!F}kpLBLtbQcvG-7;YetPJ*rP34;b|8ePJ%%Y55Ef(DHKp?8$Jx +KSUF8iRy9G?=0L1fKSUg9B)~2KQT-DLOFYAc~sxII)GW}=O@zn{*vu$u!1kt1N7mO=cSNl2RD-~zwG9WSbGV^CKg3kDG3tT*qhC>gX>3s>TN +({F(nX8(du-gq=l5Cd`k5eS9?X#H&b8kGO;m=84mWB;FU7^Ru7pqO}hk*RQz=PA8dlapwP+)7@6OrlE +*fH(qQlsg)J3B^&c!GZ-fqja%O1D%eMi_IAa0FN%B8GV_*X?Ouha5ez;47LdCl==-6)5L@-;n5kSI6_yWZ=`BzXZ^~*+2eA>Qw&M +3bO2{Wyq1-enfPw1zRFiAo}#LxXjPyPTqKSh33KlG8lhlgc=`Y(RqQNJuA!p(vK(@AMf4wkZBAQ+^lW#lJ;(DNBSG*md{-lC>KU +h%nHhw6OEqQNXuKTaJDXfc;8#H&Z#!dtC0doHWdKb;%1a*bc;M%Vp$QeP)m}HDs)4v$jQYhuiL{V91D +mSEPf}hR*`yv#wt!<%$|xxqAvXOjBiPoyY6I3f2UnLhfy@wUt*^L$rQWUAx^j$k_YW^}VlcrSEq#+fq +!NjdWrS3XGPizFz5_DblAX#zGEi+lUVuiM-pcfM@6W;Vhw7$?>$6F3Mv(`$pmv<&a)zKj0tkh$BoAEW +W~HEs?NwgI26L(~s-wtQHz`J`4e6uIAW`*rL}Ci9A)l-5X7MM#6((P;n=&w+WVp;z}vk@cE2DWp^+U@ +2m*ey|seBXiv|p>&^4=&D^M=cy;jH70dKIG^m6FxOOlTNjWWjifz36(S>vfpV!eKa!)B{=-A86dUp)ApU0=z&tK)#2or?l{yavnjDInd?jR`0mNZlle{xpbu8?K??XcQgHla4?l&@89^@Z=#x0ks +$tnwFQ5=h8@}1gb}(~~4+ZbfT$O#%`Li!6sW<Z7|TUJIuej!9c(6FrOPt^V_Wr@L90+3HVMb(G=pU +N7G)Vct6^Mzw#{8JBm7Z*hb#dgwWag5{CENy=vZVLFQwpIOk0KxVyT-o)vw*LfN{|*KC5L6{GL#WXg} +2ZyukRt!(RbZ>fUOPxd-TxiRKU!UMXR=hd6;T#k6L1v)++70IMS^G;Brl!kD~Au=?#|g=Y9pja`&|VaG+eKRQy%SBs& +H~h#kT{yui!19=J4uf>v!Ygn=)=uV|9eVZMq3~-hZ@a630jqe%Xr}9~acTFt(-=n+vTVi2jyd8w!DSWXYLXU% +(3WPiov`2m!|>j#y-OD4_yH5#nj^&fHuhI%#TB4BWN}_t-_Apca4SpoYT}%du~DT7IjKW^xhvc +=_Ui$wa(W&kJgoV%gl6OF>Yetm4>h@H%Pu+PdEXq%43nDzJNtfe)zU#l6$QVE{#oBYl^7)@c*j?m%-O +SiyRLo4YnchcgM+n=rkz?f>OmI{$cVC1YvMQSzO@u|cZ(glS}UEnuW5(hv#i7241$8Y2h*<(a{$rF;Y +l$o^MV+6emZk%eiiMUmHgP{#Y4+$5&Zb~X&Lr!(6Zl*`7JHOexhYKMSmH!54a1l!CnlE!I$U*5?g}p0 +>HCg!FhPHL1e=#C@F{LoXVzdCXDo7R&N(67hWSI}JH}n;+PZ5jVg(iL%l0d&q +D$#~Y7ve>{C}Z%NF$tGOuo#$H{z*`asRgVMqc%jaL{;GN;zyydg}0I6;%ix^N;Zof2U!kb#9z@e`D*b +zr?}r~S>*lSpk?zfwCqL2^q*;&#iF`k{*Ti#?O&i}f(ALT;yUVF*ZR@25^6<7Novg1&JT}`)MPL^B7S ++cj%AyP#t!CBv)m2}Y6g0i33*kwI4kwWhqIa#rR>d)A=7N +!baQ<9t5b_;C@f^-hWWoJ3fjg^i9X*~259S*C*QQYOz9m&rGa*`+QI=f8Mp%r=vFVp4Nw^a5x#}His5 +@6{=aS0DFrytf7vl9eUyLDPVIIQR-kLQEHDAOcLDJC4&8~M0B?;DHZgK!+6!OaD!3e?>t&dU_7+mZtE +)T}BY-j`W8aJryel}pHD53Z#|X4je3;aZ?nKe1iBx1ib|NviIHfI8?sAa+-62aur~q*hJ>p_4kGErnh!ed$RzC`wz`I-#zi>Goi)Qk!b2%ju +BvS7Fbp=_PmKXHR!aB%S2AHEwoUp7t|FrTXqL&4cZz^ghdvUKrKXN={=E@@{W>SeKPeC!@OB4YWGiHjS3g)WaJmjoD@Clq&lkry=Z>)^wRg+!z&pJ1DU79l|{ +bIZ3ANQbz*0**GbPm#00-5sAM8*k!ZFTXL3BM1B3d` +ebQivdPFz0)5E^y1vC~6KsYHH<*x?>$?Pd9>b0jRMSC4cZX*1#3@-As+%{JfH_YOc)KH8xI6|$ue3&0 ++??7{1XvS!F3{S>z!fxGdOx)>l5U(Vv6i>938YJ{{umudKeWveYXth_-aXsQ9*=Sg)-gYQ89}f>!Wd` +I{8u}HvIj%7{f}<)=@Vd~&ihU-{WDEAGnkcX6`=+QElHwkKw8~jia&k0^ +i!TbCk;#acI$+xi{w^)}fxj`Sz-@lipm|)rBDQ3idMIo+c$5Zl|jox_r=*esaFw=&~v8A%@rC?LgeKn +61etBzT-S29%}Dar(d=dPgV^a!XbPxXpM-U&9HB@7Kq6owf04Jsp@+ScX@Q4==|~^9c$n>1{c=WpIqR +)+*GVy7!(Hg@zG{Em_j19HCk>&O64>c$gGC$7|%;tczxwnwm;>(7ZiAvb8&Um+LW<(!$brCO;C$j(Y^ +d8V-#0b{DTZ>VnF6aLQaGGn0sKA|)=4cnDu0cS2x8-TU6N)FTGD4YIl&4jjCr_H7yxoJ^frvvYH$>r< +-}o3h8fM{nPE`_3$$3BU!l$|`BR%t&*s5PT2houxl&mNiy-0B@2eZ$zmP_YY6ehoC(7Vp$+gQfGNTjCXBqSn}G3R%mzg +lN)026PY^I~z~OPggqekT0|HF`WvGzgE)KzR_uV1*Wtb3W{IaR;Y(exOjq^S>)uRnNf(65w^Fzvqc8W +&zBMSFPSiY+xuQ-zvxA^~hD0Yb-5NLrfQ!p%X+~$$40pfYEv{4g`C1-?im$u8#ZIFBUdTD=mWzg@I_P +dq+nu7}cJ_(hdyEUZc(rtoLOeEjX~dyW2Y>FYd_E@$wkkXdddceSTPzkZcoA=iO +5F?4fIP4&DZd5h-@J8xBGr5Ed(LD(M2Rm&&1UrLuG|!aID|g!(&yW==wQ2y$jHBd*m^+F+H|Ek|!=4E +YjXH@ZF;?t3)P|ZGavRyqLC-ePQtHj?vrU;#l-5sQJb)tm^dCF~1(Ep&aE$S5Uh^R7#_s#%^v_b8-z% +n&C{VD*ST-c4zuL*nZDk1vY}kpJy*hB?Ff2xs8#Q2*S(h!&zrTo@998|OPv*;`t=y7Hl&)up0?T +vP22F~1k`OHuZDFNa3#+t@3O^a&roi;Pqy4{g?Mg+lAV=CMPsk}?YZe8^ORPaCM9&=SkvV2w(o$)=-D +aJ6E+@*wVn_A%g6wX^M;uWAJdsFq&>zA^|)upf5F#vHiP>1?X7~rpMyt+J`4MY%n}5q5SV6QnualwKnR#28I+-61jSJdqi_O7aEACg@dVkxZ*DSd$p%5-# +ZCi21po{aFJKkFAd5kl_>vg;IzpCSAciJ4Zvin(w0sVT4UQ}UHc`4Hj{-}YTbn&7k#FFG0k-vex +C|N)^2CxY)i7NoHz%rI-TL5yzg0V5Wbcu?kqs0S=zOq6i_~og4IRxQ&$$X_>`7i**1N5) +sV3r_?XYm1hhOL?%#a-@*|I`sb9b#{lwO_`@@Enx=lH##M%x*7i^ckahenrIMQ7!e+Rs=cRXf4sZhSY?Or{3}37DDZ-b*Enf0fbrj;F>`vw +*g-q9>#aEc^o0*&bhwf<5cANB}y({hiB${_Ohx&gDUWc71>6@}M8D@9$TJ;60Ti87VxG5Jo53?UbMgw +!hJof4KAGLot!a)am?u#xL=aNYd8a^<>a*Nh;&=u&9y2_c60Qd;AgF`KT;2btL$CALnf29?YuSGP`

    4fJorZ|*{`r>l^63hVxpZxKFj)fZ5fspdF9@*&&c +Z{c-6w_r@2*FTGgMD-uf; +or9rqGMZASd?xss3!C_+SLmKlCGCpfYH8ir#|W3#JL8oxPLZb}2&294C5NGd+9g?&Dl~p>K1>Ti6;?%j-cc+*3kKL%CJO4sNf9G$I +Dgzgk2~owhe&m2SQKA`TsU7IWw$#uNaQ=PXuKF&-4r%zfD;z~?E%obh`)hdAy(vN@)F8% +~_KI7!NvQk11Rn%LP%inW +i6LyOR-W56n$QK09YQyo}Ss!j6Z$sGK&EM)if0^;wKKk^oLi}^#Q1PO%jA*(d +bnf}ATc5QGaBl95$g1viHM^ag?c{CXuj^zCg`n;rpWq7AV}ufW9IGHWx%ifp^pKLo0Puw^$YW7?+f&I +UYI|7U!bpE81PWB8auu-g70kL_xLNQdz#wsj<+o>c0tbJ(;+6=najn>O(Isw#>YcS +C4VSJyL5BmSZ2@uF`bKjTwrJ9sHnI*Y&B=tOn-5<9u+8%U8Q`<3?$1t^?Ro{PZkMn?t%y28me$P5DvO +HUJuYDBA$4U&cy%&cLl7O~Reka@NiwQCO3Zl@go+RELPa^;j~8AzHU_kgSC(PzBipO&H#{3C_T#M7@+5p`jCcdMk=$JMYsufxHo|Gsg!58AXys`#6gMESL;nyg5=?}+*R +_`d|g|DDr*8k7Gx=C7d`pe`OPa2LMPFBV%e^QzsDydochSDI(M#6REwuD{MU4PIFvNVJ5JhVX)yNxI- +I34j+{5D@?`f-k1t_?3$a^e;fikoc^d_{WF|e5uEj2Cr}lY_S0Yy-tx!O91{4FU1`gVo4n(h(&KF_{& +E9im(WR1(Siyj?mW?QA>CRioDWO(xqYrLH{Kb3risDko*-CH(%^574%u$C)XkyGxMnSZ@&9170IkTG= +GHXbMVosil$F&ThiG+uB^pBN8|L)R1#z6n-( +!qY0{oV#`yyUL(E_X4lyR68wTyu)s3!GG9G@(;;Cdf*(Zi0b2?t&DewL^~fmKPSiRUW(YCXgbnQq6fa +S1N2FyPdaA7%JNhg3=<|aP1n2^|j=JdeLEV?ybPq7L7*bKYA=(#s0Ir`>JW$

    gcjP?dl +KCf$vSlk}dQwhdzuMPS3wndybnQ&0febQcyxHlWerf*X@C3|#Hh$*uEc0b56oNNYTi4i^NAC^hElSx} +H5fOfcDm-%#M+hchg(ShIA{6p{<@-|y~c%Xx*MP?Oi*6vA{aCQzWVt4kh>b6PXtJ$&;;^LNm(i4p6(_ +^xB87$<<3rQs`92s@oa{2{F`rA;fRk2~^md!+=!MBp8Z24bp(CagmnsX1Q`Cx`lFh|H!;Dvjw5Hrt}Z +|NZyPLX#2rnlL9f9T5rq}1KyW(v1*B*h#9lbY-uud+lgHSyYjSm>>lxX&=RNZNm~y`JyerYks_2rnA1GYP>Jb7Rz +>-oFmQ3OmJq8?txnOF&lP^XCh2{wSt}#Lf(QqZR38AIY~JUS!Ni0Lmw?F-V^Tmd?T2&jPpe6tB^t>f6 +1kV(>h~!;Q6~j&PnxH{&>fqs<3nVEw7`R;od6leyNn;#`UV5S7F7#)~OYlpda+PI9gYIum~jDi5}z{M +^$N0$E)rnyFRJ<-q?j{ff<}6*EvH7^=>T?X@~Q4DiOxtH=7GZRxHr?jRkFO5`R;KAIb)Q9{s^+9fP(A +w0&V2|1VGW0mXkZ(N8vT3L$U`p%@HDP?(@8nnV$jq8OY(aF{}Hlz{0kQ$guh8DBVDQc!?Q25^cqiwQh +I78^D7N&*EiyObHmHWd0LY{SGWI`Rej@T)L!{E9k!V**Rbq+}rmak@bDG8417eR049u;0KomMmbr^m{ +95oGdH=rR}Vevvi08JtO+VlI$Ke>J8pB +Ta+%?LB~%z-=-u0YFcc07EY9tvRlkAUcC+$WT4rd(YEd)Y*cHw*9gaKyd%p82XzQ +>t4vu2SPls=e@k8sW@Am#nUaIB1pZRl>1;Em=jCvq&yRj1&iKGr?S-ixObmB<+KB|H|4WQkg)r)+)X< +ItE{sii@U))LbafvaW^NJH8stHxv!?}WY^pDItl0PYWGA_-1Bwi*ctJ2DZFPJfshm+QXguB$pKFuzCEa&a2rc0KkJdLuGeAq_q9amewatcliDXtc`dJU(kd@0 +3Tqg#|Te+Q!QAZGSM6xVCOnlZ50=zdF(^A#LL@${k-rtPMb^$cbsc4G1YZKeu=!v^)$5h}%)r-IU~JTtCvy<_;PlNUi`x-`LZw*OOXG9q_3mls~iJZDp>ZCWg0 +Kt;+Mf3NMFzg!*fd;ZX-$>JYsA3>UFFK)f};z!RP&$bxKy-o8^Yr} +d479M(9L#V&PmvUP6`&FNGWk={Hx9mBdjoGkWs;K)mVx*$n^UR_G +L#!ZVdze1Le{>o>D{rApMy|Q3IjX#xE?r5cL26X1R +RGP`~)?(K}0Py=qeBQ1Hi`ycX{X<5hfvXPkPNIhxr#=zJ?$B`UZS@!=)4HL`m3vruhu@^G7? +|>Ljw)vsSa~{K@OZvqa7_21Q9?ZUnkXJ+c#4q*geUIlv8`G0)JBE!yk+nihj&LUydl2pgfaea0OhvG@ +;6}@T%+Wh==?We=6`X{Kfvd2=liL+FF{~948s(OBQOx648l+-PGK;G;TT0D;1K;~09K5@Dhwq{z8VoN +LAwk5VXr7EVyPYw5euRMe8hkI%W;U8jW&6?fiY}>UV>SWEQl8$Fdn^^{0FlF4T%7-MHWblBLH>3D^a- +A?qA?Iec75LWRb`?xkyrqS}+sHWiU(f3hcsPwH2^%p##CIGJgy&9Rl*@k*1b7WinY#4-g!omt?`e0@C +N=Uc7()avX!bKO?nn_J;ZZTlRHoORtK*;==;h{Z9dDH|{>N;U@J*Xj8!FMiHes!klTTcC@%(C8ziSro +LAde10g-cmMA5m$oq!e#y6fRa~@K>$Bpb_#-A(4{!bnVb(Q|Lc;-O;H8^V$Gm*GbInR`Sp1KAA2I@7;D4 +=UT0Wsg?^J^&UR#%-t-)RW`)Co1>i$2Wkd#^I4vWOMidVXZld)$NcD3@@l1>J9I8i_!w5aDr(S^&p9U +@_HI);>2=%aA)`lG$qbhUx1}D_%!l65QEB@3Yb(R7BsF4BH5Q48yBa_Sq?APEc?oAYe4RLxisjYLVeA +J{!I3>ZSG5KQTppCc_Go|`Vx4!=6Gm>d6ZeUK5HTp9IhQ-wX3v9dbC8IGR}^e6(JBgL$3DjCuG!s%qU +CFgA5cH8Jd0~ic9J7-V6$BYiQSWXp!nXl3j9QW6#mmMfPH;YN9d1XQqY%iQhRYVqNT9fGg~%3d1~G$? +1Q=Z{2;vha${vQJdF!j=ubO;QI7Ae8Xp_US#ZYmB!aH=fVC$$fZcQ(Ur!L(4lFYkkpm +4|;k})}o7C`%2U72VN$*uvf?#N;E3O5uJ$Quu^=3Lo0V%7iP&cw|dseNT<<8-%Gc8Ix5#|R_riNz!7J +wxuQ=Y7YWKw%t1Q_nPz&I>5vw_>CF>G2QtYrEA5sP^Z!TOdo4SPt=)q6JVjl0hx#1h&A!7MVT6@LoEt +pC2m}&HJbg1spS7|wYyTb56;)X)5uk|{VtnH>W61i2y=uBW%V+6;y^i&vHO1ge(C&1bQRuQ<8Ry{a3H +~zhqkE2QtgkGc6{QS+D;Kv8&Tm(~T_(QcPctJTJo`fTE}8IQTFZO}P2zaw-5vaX5kh3tibgB$`Iz6;x +aq`xrf>Q#+pApCqDPYwv%BV|w@%0xR@Iom%z9{N)lZQ$F_L2re~hy3d>zNl!AB- +u<}nR`lrWyEtGz^{|_7!!$G${L68UylMI8P^oP|`6bI0fGFy5S0lma4qU6!0TAyxGdO4W&PEr7I4EC` +i9}VCVqgSBZnuB4`ytMuc&}TjUhJrw%huA1*4n}H#f-VKP-7k!4P(VIcFk2aA%z$YiI0AY?ShmUZ(IE +A@!uConu`S7)MBCtb1d6GkVoGntTO4#cfuA>%Iv~wQfNV4K+j(>82?lV;uLC$5t3OypJR;|MH-Gf?`( +WDJRDYsnUWztLf_yT0v;E{d1=*ExH6|9F&BaHX8x&txmWXcUM^5G8!dcXB8C3zO+ZT%=7|5rL>QCD$P +Wl;&1y}eHihV5YSJ?Sv1Sd+0;e38nZFDvCpzoLJpv+c>vR(F}iphKZeE47buM`{iij^8E*^3Su+s(`u6V+bWcMRxjq*-dPS7ZYdX3TcbD$O@|8m;a*x~ +9fqF8{^|23}3+F0@)BFfbEz{es?!9TdlRPEiQH;#masO|v}SMHEp-dd6?*{1~Q}I3>4hu0M($Q5M ++g+P`;cf33Ac@Gf@=M8*bvpr?g(to>=+a}T>;@eoDYljA)P{Aon@SM8QLmBn2}_K=(Gu0}$2@l$eF#d +i_PT9%F;&-}1kGOQ5yag8;;8tae4O7TQNdN+3ieW*izsGh(Ygui!IpVE>)q`~X0rI69y&&?XWp!6-yy +pSrr{_qM8+RpWEw{x~GhY6HJ@QU2YVXxkMe8+yobYhU5IettZkRfFD;pt|QU}jReo;7$P@5R;XNlvwH +S8|LekGIlB=h@(~S+7Lp{LF4?rB+x6qi@Irw48ynVR$73CPurf+m +>EJdrQau+06d7Vh+4M}os^_edSx2vUHOP*#E{c>gPa9Kl#do3)ijc08^e7(KL2C!{_MbP;ZwV?)|Aj_r!3AU8mPo6B3Ea8ZyVMfkQU>qx^b}1;t32h?>zxUOfVz*XJoCi +PYKkRUEXcnMiZ4h{-0ycX&-ADG!i1_g;J76c*Mm~su!1TM;Uz9IMlXgdx86sF`l^@f$%rBXvJhiGzg; +hA9)0~>q&XgO);f+7+2WXy((J{`jXER!s|qWgM)E;)%XuHYdx(yXJP*b5of5~Z;=U7S=irYQVeD^@N3 +OLG7j?-4?Rc`I!ofYx_ESo>@{@e(`9n=#`R=)UPmp}{?|#k_rIrP;jK}kP5>uHYw3t0;%sxLqRAPYx# +;&J)e9ze{3DbAulb!bJ+io906m^)7+D?f%bqLRF;8$X#8O&t3rmf(wWA#1?sVG&7bm}A%-rEJ#>m`)wRd~PGZ!eGHS{nG>P{`yRb$i||=1YXQrFma}Pfs3+R`xPk?c;HO9HKW0i)wjY=?R103%i| +l%b?C_z`pBI&kqZGbk!5! +1lYN{vNXZc*OTX8$rP^Mq@C6AqYh<3_)TanhS2OfDr;5bEy=}TCG3i38)ApFeo}x#8wobL4qCmIA&vF +&}NnqpXZx_Um*f=_Bgf`&@&LN0qdX~NF(Q)UGEwjBI`TEzlhn^W5`YICqh91ehu5=2xLTJ63n16TMrz +$Sq9=LNNRut2Mj_uh5_yGps109c64~_)yq<_h&6nqH68f`!dNS;E^ +BuIhriU?g0S!_3^OSE4{9>m4YxdzFmGKBm@B&W~HDU(wp{`3&`+hTIuUC_I}ielHh&L?h`y9iz9q3#2 +j&A5f+lqTavx*o*p?Ak_*U3H*D2O}oe6!TD&%oI-kh-o`|EXCv|HmgIS(+H&qEsD-17%V_u7&OLNH6>MnK^Nb$JQvAW*GPi`bfbg@*sY^k3xBFxJEkzMqt@2Bo&=W;A7 +`kT2pusb%lm*hTO*~_8Il%6Apl^jm1O5Y|)))?sFj5#kgPff|Wb@4La)yifc3kpA50yE^wkP9-A!aJ{&j@q9M6DdP^+ +iw|4QWu3tR+{LnadDxCsiKg$t`>6q$x@5h{)aR(uun_7md0o7$eoETbK_IJQ+nc1hjqK>a@i~Oxn7=~o)~Q{G%cRHd+xP=nz!W6zb53Z6UTc9sbeNSou|1niaq6?iv>M +V%t;g}2F}K&=hWt2-Vk^srAdpU+7OiM{bGctYL+5FzS2Qr5XFgk +K9iEzfzlx6&659kR&Vx^Xv2cA0Uc_o2Jcz5r|jG=@QukF@vmdq%aDG-sA9|HPJ^z!>oSrptnwLUtYwX +&8Yg({Ylm)Z`LvXA?|7=``83Grlri|d(q|#pnfE7m$)fJyG%?5ys4e+4En?QJgK@l@MA#3KEm$yj>^p?i8=YK#rhk1$c7IWmwSl61ty{H9CMALk{Sv2P95rH;7=^ +EjQthmi9eTc{AA7i$@6h5GX1dOvk}&{yZs!v{rO=(NZfoqP~fqDrFF8pk0xN|H +2*!WjBt{b{p>2GTJU=(kvdErJ5op*28)8PH*vZOV+Gw1fUi0Dz`|%uKRHQZNpZTi7;upQ0f8V^HwJ8h +>FK2oiA|B>6BLzwknokq-VerXE>Zfwj494<7Go$s%*Mb0^7SQ?vVG1l_2Hos_&>H@&#iH{hATzIb4MSRI0X?JW7Mi|iCE`5Bc|iudTj$8Xo*q`f%YMnjvo&sHcdQ21xjv!tf^(u{J98ROJpSBQS&z$PUsv+&g)&n|_O?gjjRp7e!?hr|Fj98MJcLt{1WY0i$)GYhZV +l+Aaas4oBWU$3V}TdH1}xt}h_%a;bjus`xxwAAE5<+ot +P%#QAW#9{M(3|4%N=!o!1y@+9 +k~=(p(p$9Rkx5p)K6X)rv2%5Y@JT4GV6AGPnuadl%|`wr}^iey|l^@)_xV{7da1ahA*Y;bPL}{7vfi} +BO_AJcjC+l>29A~-slz09D$u<3sX8@3rKsOEu+964wF?UxWEA7)~1)x?jPNLoV0eTG?*bm<)GR>0@>8KOO}*{LjJgThHK%YSL${ +a=%l5`Jv07#SE{8jY`Eo3BuJc$f!V*MU5C03#RoB|8_;t&rAA~6@4!`1O29he==R_GRUtWvcnhQnBDq +NIHJZ}I{WH%5w03^wU7Oj^_kNc8YV5s=~-cp+wtTgmB_nZJcCklw!`vbM;d)qDq%KNbe_Du>xpK@2-- +`^BQjz;zB{yL%1?W|o_yJO;zt76_t)DQuAfb25C-a6%H7FpQ@b5?J>1ta5k!dwRX0^qx+!(k6K_8_ZR +U`IHpR29od@QA=IZ-Vn(v8vVUdF4_)`|*D_f5G$J8~BcT*vyJrni*bf*J7X~Tl)FPBIV{AIVkF~?p4M +UYvF>@E?lt+DK2?L4DE@)HwP#-p%33L2%wza5U(Glx}oUqjBf$xnAQ5$J=)mqjf0ji%$Jz7M!7d_LYP +tQWpX&cHKNgxw!$&iuPxiUT8ZhQ{UfRX|~!V4uraDnq-B?MmY8FOoCBlyrs;5qsiL3yl9v)h>deru +1j=b$Xt~$ml@Uu~Js$d}E6d%dHC_%v!BL;ufuLjHOs*Z(*FK65bM9D5s$!*cqLb4f10ru3mSFvZEZmn +Jer1aIU!CBqHR3-W{Noe=ju9(NVkAzZII}_}gRgXwCSZE~gT`>2#0l!d0sxVKhWixwD1l@;5GqYT7N7 +a#h?s118FYyL)>ppP`#^R}80p@xDnt%a<6| +VD7?T7h)A`ImBS4ht`W{V&|o(6Em7_@Ov%x2THHCh6X0R|jJZ%TGB0ca<(o+sW64}R<2Br1Rr&0h-uw +%3X4r>`|QJFX!Wj<9vy9DgYv_mgwLPd&P$8k>X1dJQz7 +bj`%n0$eY3YUuqQDcb~zBlg!XpX`Y`34E|}p02X=uis@DDOc>-RVjQsU1t#15oJEJT_f&S&Q(X$I(id +88CjNQ|_;7fPR2O7(DoL*;L-^W9xcz+vOjN$hDeO|GQ*`zv5=Qvf>=^sVBmzG2=J}<_i$AfY7b%x#)z +FGYR+@&ri&u2Hmmv+=#2B2Y>}hJs4w2PX7Nh96o48#r+rjQRW(BIFm^w#o9N=P9CFZL`b6RxO2E&GP5 +AD$OVP`FE#qb+FsXQIa-SLEDH%p!HhjrDuNq$O!aJ9thct5+E(A*tzWRZis+}-mI61?3_i3e}}EcD*I +kCw#*K5t8*A9BV8DLR`8~9AME)2n)I$0(H>Gmigw4WUV)m{*&R6ezH#btDXa +sblAJc_i9WiX49VlfBhG!cTW2>%}0xNweBg91#~>HWLcG6syMTCr6&N#Tf+Oq)G=z7pOW9qcGo!~^SA +E`^!;4?udEWkOz|E2Yu4^BF_|XB1G_$zvwDHt)KUo1T4TMvcArk&BV&S`m)tPF1wtl`j6fPSW;uGHi( +ZkwVwyi%vZ&rUlH*5qf>fU2u7r}f;)$k{ID6j0!JrFPU(w&Qu-h%!sm+mIW-0D8_0m4g0=G>19x<@H7 +|9-TUa8TIs@xQ}6S~LuM=|6uq`C$LO)XO=vq{B#!)u>mT)d(k8;}|`YZxLOnIJ9ViTkp9w)@~D3 +&P8f!^#E1i`W7-r+%BqHW`THKsbvZ5^1_Vk;om7Tdjm!guxw(muCcAT$?*Ok<#b +!;&vEqa#{7dirrLklHpKFrW2QocO9*jwL_u(FUI)H>@jyk{g>M{Pk@@^56;Bj6gT{)Z>~0pJmGCA|!?GGUYfBYx|@E9O%KL1HvXBPdQ&>xK{0!x*TUuOPfqV4$fQ0n`Mx!^|dio` +M7~o&q+y0_rd3{3v`g>A^sHc!lkZ0%Ehw);fU4V8mhl8B0LfpTvRC4bYY&Hm%p}SDH2ifNJ0i34`;lG +?t3Cbta%)6v)d_K!zQG!K$KdW?>B(sBJcpp#aB+w>JaQ>l>Q}{i5mS)d<3k-!ftXbY+g@SAY-bafxXy +k)EL9=?9Wh<}jK^4b7SSIQZ^IWI*ouUMDy`2 +VCD@)j$@^~}!e2#Vpl<><7a;iY`e87BrOX>+t_;NTd}3wH*PmAm{o5t~ykh9zF8OB_*WXZTp`H}dHDp +XiRea#7HGq1ux8^}*yS#JZWnQ3!GDMwvy(;UJKdxGFY?yQ@Zu_E`U))PlSnkC4UBvQ{)?GUcQ%==j_py(m(vrIufd<6J^#khYjcv8Y +o;#;BWdMu||{{9}Un$+^;6+CG|6Pc639mXNTmZPPA +nEC$&}xsP!i(GbWfhe#YXj-v@u)o^C-MvHWC({KI$Q6UY7~mFB6To^B5$A9AvLJk}VcJPjOi@KeG{Xo +$?6Fg#~Swnmzj>!Y}GKkHS+1}HDIY$V*M2F+T+#`U|ZA79eKUFS9o9N{^qPqo@ZCBi1f3Y@rbU?-B*9 +l8>XY!`;hf%RTVnc1)YY!aDy!1;$8-+hzW%d$VdbFIRoT7mYr*GoaiMU%UX%M)ph_V3~fXXjSYhim2K +;{gtPu{|&IQph8KykN>>g62b}(v69YD3=X)yhAI0j2B0O-*JgOU$OU22kG`^oHcYR220D +F#7;bf?8?w$Y{i6n`PD=RNlTlT@J_F^*`{M3x(H&ti8W+Ff~%6D#&Ouu&-x<1=OE&X^%HylTPiK`{Qs +Oz|NE2tM5+Jb1mB}Bjt~@$(ksGJFuejWx`OSBWDJTBG(*7@gV8Ya;V1;PneOHJCL5Omc1&PkZk?ooz} +!lTDH`<5AsJxJ=r7vpwxjV3wABGelJv8&GMt0+t@N0HHvu;iWTNS8gYpfbe_4}V88GObLjl&V(2d|=_ +HRYgl{ZHzm=8oZr8;bL`^&eplbeJ*wmGLnn?BCEjuq$Eb*vDc#$Zh=-v@_NzXM(V3RrdVO_4@{v+3Td +Y$9>K$I4lYTH&E$4_r~Kpj%l-kR)4puKd}+^xrb-Pknj+1)~O4u&)lN>NiF$*|7143UtM6EG+t&RezD +LSvPtj{tcl&UE}XA5&GRV{_YZ?-(BPHE)n|q8qry$c@Q1>-S+y{)>EcMc3++K(ftDl +)#0d3xf5Chmh#H;CJHUQlg!>PCvvJ?`078xj0T&IL)LWDQPQ`W9k$+t2gHaMYBzdD$N{dO_cHdi{8-& +SnS1lh{gRso9LP`y0#H{{6tKd`|_k#MX!1U1*`)uH^Me&&Y>zncVnrVc+S}xA}t1N5hhLlAzq#iuD=I +pxr20vYwM2%B*t7ioGG7AX&&*e@~^V#UgDLJFi(Lh{uxBdpgyH?i4VsYo(VzCrsPJu((BFz`DG)@9uF +Oe}49Uyvx@o>*tq$U@bIANW$pKR#2G1NEF!;h<~@R*wT0_MIqv?zhv_~LN?*7lm-%BC<)XwiOpv;|Jb +M$aG90DU_ieMCpP~I93%~q2yldzlYHtICO*xg;ecqYC);dMf92eSfd?SynTf%v(@kUyr-5F|`kY)Jp< +qxA1s)j*1!mQd%}#!OsIHVKk2l>D1e6Vd9``nYwq93GfcEGJ@UGudlshQe_58QNu_Kk>vv_>_)&9f5F +~Rdf+QBvbWr|xGWQ}+_^)_|7A_wHWZ!qi{lceIbtYaDCR +SBxkHP64kvH1ucSFHgs5^c3Tb1}3`KBwSZSpnEkkO|-T62=a#3+kd&t$Hdd(fuW5

    v2p&D9K`cn +#PtmQNsFue#CEpV+7I3dr$TxAk3eFYj=(r^hC=ppi>n0BC!GCB1@=Gu4-%PoJCr1_TRD{UjIZwhMP_hpC58M!~Q!v4@8<~<-D(Jm4$Oy1 +(Trw$Wr(=|E>qE;L9*vu|DLl4+)XFg^J#Gh}~6qwg8~wDmJGC +qH9sE!It;P?(`fEb~OBjsQ>3D`+|eN-S>NdL}`W~VVWjsl)?yvW=Mj7F_=IZm{>QU6ba ++hhkY){hDk6C#+6regg0RiECqpho^7~C0K@}bm1~UtF~J}yP%DE|&`?5UfI_T)t`NL}?+RWkeC9Y{7x +>l*nr&U0BmD{CS)ijr;cz +wWbMU7G*N+|CFfllbQPBqdiS^wVUZu4h)FYVL8MZy)!$WIt#1{9qBQ^?~v!fG+09MoT1a(=J8DB9MbI +Jb`2dC&X`jT;pjN3VHms(wFYDK9-@L0^5b_uOb}3tHMEB8BP%2K*IM^1~1)|yMEqgNcd06UC!BXjTut +y&yPs17a}EihG>ahT%Z;FYCuZ@=nU1bbKxmYtykjR;6L+&c`n0%(;?UWDtYqHeiy$O0D``s-2O5EguD +Kc>ah4Km`F&Y4+VosuSHY%P5U%z!tYruqbk3b601P&JB)eX9MzDxzfE6YPF!En%d9@_oY<##wURNNJH +PQG&PMfdU!rIE;S5|4zaHw1Eum|ryc^8bct>p&jXI#t;o5jhM4qxef8c7I3Hb+no|?UfGHHgmrO%%f^ +l}m(#bjV>;5%*@M6iI|OWo$l5P_s{&{%<+QnDwycOaM@6eYoO=iq9TyT-NrBv +hnIaSsXFnH!6-Gtw)sb0Thx6GI!xIRRv0#`GlC9JYpJRO@|=an!jZDZW`}|BnBjcG^MdXc#l7cv>9?M=a*a8)6S? +7BE=Vpnf|ZXFE!~qe+qwIv51kZbCwkV)p3-Z~Ri|Zcy5o%*nUXwS?r3viMy-F*SGyLom}n2jM@}4Jj? +7}dVME=8ZXT>kk#Dmux)`^pYix9-F$2ZDI9xpK%ZkoO;q6B$KQxL~a&1bNJ%a4RB?lFnGCGgLgKOxl?jyXO;zeqobiK5Z{C})jV)PI}@^WwdB_PVXG1xyUc$e!jKwZS +L+Nd1;{I9QMKOC|ui6-Mw2bz-;;5FZY90;Nkl+(CG;|q9<=BglW~Grp@z;oDGvkvq10t2x;d>=POQdV +_#t&bc`L^jbBuS^PU64xdqdl2CrdsA7KukaZ7sM6b?PTIq`fs@B_bxiay843L|&V`EhrzzJPGJfe6ojkIdTqj`SDL)G*=suecRqEJQdDf5Xxc(F^L`Yt|$UPtFyBxHQq$y8y?P97b)Te-u ++Iph;6#;T|~O&4Z2$Yx7I#Ire&4Sx_&*;AhCL(bH>5l-&eCQ**0!$M`0T9S0V#?ms=UA|Ed5;4&7z}f +86=b#QXFx7vFFYV#9j%X#sELVvA?B8YP2(1!aVQjhtVq=Mh`+g@gaga<|(FN)nyS3>;`^qNsxatq-3% +w`ByyKi#VO%n^E@8Enk`mWDS&?=7(uf2`Tu$T_ubgAwql@6|&;^ov3wl~jO|W|oiWt7J+rbs=)@%0P^%Ue*-?zG^v3W{@lzrrp2DqxHhbZ%S=jaFwU)r~ +q8j4ZyQpvWX^*}ZdnZ7pY423^jJXv&+>Y>_FB+5V6kH7d-2Dqkyt%4uf{^U%&yHnzfAQ>(;oNWb{1U7 +E{L+tda3}(!D23t-LVQ?QMKKI^_=BuK=fn~o +{4z)Znu)*uN?$>gAWDc{NVQYaKumPwoU4$-1N_Ux +KX_7N0GEmaa1xj2gVD(W{$y+`lld!VZY2poejbHtbh9Bwx8b}ygBqgd3P{I1O2henw~7rGo@<&-Fl=? +_`p3^@+fo=?CL8K8oYW}`GXpkw%iv1TKc7k(otjcT)xj+qPq_btgK%q;h^lN-FqUlTv3LcwIToQ+E+B +z?&@_IkbA{i?e1B}&l6@+I+yW9KwR~M$cM4Tyxft^M~`L~^DJc8bvF_z?~W`j +AckX`uF<;NNemL$QdMJFuA!4!y|Us2re+zg2kvbG_MrFIqkid#6o+xcnc6;FWGVtL$_b87CB(v$?@<` +bP8um`Kh-!neyRhw4o<0+N%1ZkaW<4?0`*iM`!3OffAE?T<%y!HBy!7+9P=F_{X;QqB7tAo-W6wP<{X +Y4*NBTRqAZr)hQ{4X*Xf6wzKm#;VvZ9s4FM$3$i8y{b}8lT4cpzsVgbmp!82jR3GT)s6(ug>&NIj6*p +X|6}-?HTomESg0#aG3cSQ&o?+o*CP9Z7+J+a`L1#n3PK#=EZlGTp;6S)2@skZ}8+ZFUqU&unx4eN*W; +ul0B(v2H*9Qpc1AfH38Zw91$mQsnGPK$8!kbc=6gvbyTds#D)yv7b=J+`gN`b4cR**&!>+t#tvSTCoe +mVR7>9};{czsyuA}TsX|(cl-k=!9-gRhLIle_&vc6O^3~=sab6|h(UXqG-HT+kt_VVg%+iv(syY!wVIZ(VV?xZ6oKH2OIg)cZ! +w2EtGyv&cQvbyc(e3@$|MkfN3h0~t-+xK}=LsVO@`!H#=aom}|9NHUV_N@2ZlBwCwgV6OW264w2fNkR +C|x&V^?KqTIQ|z`=>Plg-9!IBujaefqyJ(h-{T?;6F81iIE^DD@}aPJLTm#o)K>n)fcRRv!3l=S*U^cEqBg=-1#l2FK4bvF#CIP4?_@%CKX@P1sw|)4dIpN13OCp3(k}Vs%HwM +cz#?DLG-?p#b}ceh5Q=83;SSi<2H6dWIekF^IqvM%Y}@o#FS&zna}C4Ky1iXdl8@SXR6fD< +BdAmbf;IEfgX2KFYH$wXArVtXU!3KDh^Ijlm!pARxpkjn2~@27HQJ74pt(_4G1+d9roIjNWH)E5rQnk +{(i}M*{!Zv7z_?h9c1=dzR%tLPUo-Iks(!2=X_Z**Lwe$e#0xB#;ZgQ0-4; +LUhvKRqOC!s%ztwvleVTkTbJXJRQ3LXsoq!ORpW{UR>on})v8T%UbPs3RxA|h`M3sxOkZ5V{Dmf(-Pc +k7Nki(FZoXuOlzJDM&Pj@b=*2P8DnUnHTWD>u4)llU9weud)QXl-_j2~kEsb7K33}>PJPjw}kl!M6edf8LEtH~nn_%O$U?*Km6}WFbq@JRI%SMKbeBEs +IXko+L?4Breu4Mc$Ju>E|2eL&iB9{wi!kA=2evg(Q%3fI$1ad*d4Xa@~l4qca8{*@SwoF`D4XZ5} +@LGCKLutaK+FS23NoZeLnam$dH3c)D^wS43yFd8vMDEM1ua6^aBG3B(=%BuCPlWpiZ%J*<{li|Kwi<<}|z&+dxfs&;LNB91Res#yQ4 +PL^Lzahnb?N~}DHn*h}Q=xy=0*9+*Ybi=nqi7erqntV)=bMj(GQ#$Y4OPKV28n +5xL)~jDB%qP-ifp4>-#}8Y$f;i*eiK}-Cx>1i|8#LYp;vRjt4^1mk664x$EET3>^?-#5*|mbIjiJ8H% +z=V&`g>ZM6c=MP+aqLf0UHbjr{hKxe`^0Y;;uns)WJ|9-m#awWw=jG>Tf@@8vxt_G$0&ef!4jTnOIm1 +t%W1flmhI$%tJUuAtGPS4uSl-EIN=coh7(y4%^v@#ak*qZ&&n+GeqraS0H*&5Y{A-x_S%XVU|Vh~9pR +5(3*~yDy#Wsh}xso`U=7Irh&uBjhVe-40jsG`{MEq}=?js~AMB$k9-j=c7$-$RX +7BD~%1$QD$HFiQ2v+Z?8p?*S?Z88T_n#DB2pdxdYr?Gzv)?4(hH3mkMQYop*%ZW>=N9F|?m`_?48nx; +tl{GnHb&uix32s2uS|NHe2ySt%a*L#mCCSbFtkjip45&q|4>Xf2$aaCe~Te)RU8W0ij3j7L+!%~?HG(2J^kywx1ijRUpq2CJ=|n!$j1}suq*7$qXK)^y-qq^xKPNo@e(}BeWnzI20LAF?WJ5e_`ODY +5Zg}S>EBpLeUdfU)EU9EoM#d6@YkX~y8ZPrLHlHM{Gp`6e|4H41r~ld#SftJ5u&U_irBbKh60kaK6Ys +sG>zscP=d^GfF&!JTmK4Fg~*Tf6Jo%On9b}kr$JV6rGYDRW0I}mHQgpQVlaKOaxs$FoGfTC=Mn$PN)e +<$bC4!o`52RdRO^aD$ks>;z-F_`SeHjL8{km@1y>MTA!H?y#3m%U0tuc2qy*|go0bu9Mn=I<4Dx$Ki2 +-?QPTw4mKdvEPekvg!d_f$bFQyHAXARYExU8X7ix5X%Mc#Z=h~MM!eRyBrrQ} +%wWTXQt@Ac=UZ{^Ff+hd_|FUS&oI!6AW3Cr$INA;OJ<>?Ucu_`@$$4W`Dn4;U;D|!a<4;aGPh~&ZHYw +s32e?N*SkDWsYImr9rXi@B|t%B!U9c6gt9lE#G(0{*7_QGb}?F!LGBAk*vm=(Dh!?A5eUVbI;P3$Hkq +e)EkUe^noRLARsjeD2nPbfN~3>GiG>K|7?C~1FpwLilTtj@OZlf?ACG(GCjln+ +<|*iJ-BUjaEPSbW{z~QtVh?<2abc_9)06U+6K?8KDz2|DuV_(`I8q05pQVJXMzWIPw8CrmcW;~~VL4o +Q?A1btIEr&Oa9!UTJRTq7TR#u4i-phHxP$dp)({Dm)^I_XQevmTdNC%Z^cqj%KvimKLubNIrX4z{L(q +))iM$m%e8>V)#(Uc;QAUVgR3NeX(?3i>#9oRBY1(YJBeeJarJe@Jjx|4K=4Qzd9Pe$C`%`8YCmT8110U)N(qF5wxssR7`H2I5 +=ti2Fo`rprXM&fuDI5OY&X?xi^_wibNQKqH+9V?Sd6YaAunY#DJC@jss3XV?bp9`0Q?q#wY{E1oLEtn~#< +67?6(y1CAMh3UIO|I$Bg8)xq7y)oX|7x@mOMoH}Fp9|j6A_!IFAc^uz_en%k)3 +UfBg#SfEu%s1api0Irc|(Uu!sUZT^Rx182KcCfNf$$@rIp^$s^qpC50eYJ(Ps}%$_3xIavChSgPDC5ST +%(^&5@1UFCc5++;-Q8IRD+D!@X8JGpii0w?AMLoU*Dfx9`sMw_a~PJ{nPdR$>l-mzp-nwZg{mft0BG1 +=pircX*xo6O19c`4f~Th58;Y@mC5EO^c3Gk%dx0ZBv9XO4$*WzNh$yCgc9{0U{IE15D?u( +m^D`e7=#xCE@lHcjje<(LeCwtCqjtQ;R6Cba=!(v@)57vz3qvRzz}5Vt*V|O|=4=3hutG*eMhD;$0!+?no8olNhxePK(Q8! +)>vjp9r)Y{I7z1g~088iFP0M6~kNL_F3S-(*-)t?fPgOlPBWxRw>`^?O_<V^H7H_Tte~eopP-5I +uef3b+xk>($$N2K#E}^&K*a%C`Qi$TvAOuzc1=lKllC}6NKE=n&QU)xvbD9$-9l1SwH;6|Nd`Z(D8r$ +*J$@K`q%nemF?5Aqf}scoM@bZBn2-Bll5NsI-mrK@E|>sQIK(#Sf^IX5$X0a2vaO} +%<9c68{)dS-k9%ZejWFm=0n`2j80G;MzzE1pqFW;{Xi+9Mcqji8rN3%LjG2YpSKRvN!&d|#ZL{K|NV3+sGI5AyYAb6v><2!wz4qVdCv>uaAYZ}n^}BL2k___KDvw +hj8avEq#H9Ef%Cm3~&qdws|7KEa&sa_)Mt2WBdu&y4r*!zEOoUi?gGe|qgl$bJq)%zH<|1ho|^VP!&)n7?5_NQB)q=rP6@uGkZt3IHS%yzD*agRY`xM7GRXC(?Y +fa%H5MNhX?)1j{mzlB-BT5B^x(6BYl=KD&x6jx^}Ghr*aLN=Y+&-)IZiFAQ^IK}0=Yij?PC-_TcO4z^ +wqXfQ6~KNy))2<*!Bw?{Gmx!-Onzz>ma@M>!C*MU28>GhPq5f +JdQ7O;oYagv-hA=eJF=WhK(#bwUZKCE`K~=haeemTx?w9uv*!ZzNuMbw*Ji9Sf>Q{umsFPp^qNfs=#V +p#;uyWUC$p{%QnzY_$x2y%rdcWn&XiEL%lGNi7#LP~l3SaYLxI-1)f>F&59crliOfb@Rc_h68odhQTV +|Zepk8Zf8QTkV!eakf_AlE2ehc@bmF3-mGuRyj{mHuK$l*DkDq<=!G|H*m2fYpC}j_=_NOwpkKFL7_O +~qI!q=%Um+%+LFy7xZQck6jZJ9O^X+I?v0olEX{Hralm?URJw&(s)v7qM&rN29x}b3@Y&g|~I=xr +X>|E4zKZJK1jE;JXis!~afy8RRR8`@Rb;-?DIAaH6PjSZwjrKF{PjKV;#YJ!9U7u)k8Kqq~p2FiYs4i +{QZB0{gqsnCfFRCV0koqyS=B9fP~K?hk%Av(}O#2&hqASCePaBma#FB5#_-8ylc`AA(O_SbzjrGRR^NvsIaU-MDhMt!20HM(+b0ar7resO*%F( +3xEkq6AEqnYaqiC9yp+m=dY};v}5yJh0=;zh1I%FoVm&ijtf4;RdEc_e>!3+ICNcRt%^BE+n0gFAmMH +sHV7=baf1nzy|n4vZv~$5#b#qie+O${-QiDV?-pYT1~%Uqe#LQxzKzwip4kz +Pyd*fn3QV^!c%vb5)Otf~_9*#yqe+n+FX~|d0?4|GPlJSn>-#CM$HqF^3xc67Q4%GvPq9(+=VDh +fKUdd&+Hf4i6wD8oFZ50AS)Lr!8o(RiXV(3eWs^BrB(^DcLVK)>D6Rv~G9eRC(BWOXxb0f;*P0xl&ju +;to08OE1-y5C9((b*eYtb{KEZSAnJE$@G$TUm&;uBA#XM>cJicJ-U3)U&EC3{GxQ3Ocy#+CdntnuJxB +`#G=^8n_MG`hJvuCDnD#0A8x&)Qns_BwGjz)BMvP5ToCR$o*1n?*x5kjy+_Y{ +0A7^w99(NojlFU5mHfq1m~+aABed)`UnKQZf6R)8SRLwSG!bU9RU=bKL|5>-u`^N;ig$yuL^i@y_2h` +2qXMLyPLFPR{C;BuUIM~b!_`o5(0S&PYzBaz-jTfLk})A)Dw@kC!v +vEs3%*HEx40yz7lK{RaVw{Z>oit_l^)tNn0lvASK~oG4o($&1`x#Jlt(!Z3C7%wSKbuTr9E{e*L5j&P +GnhR?n7dPkN$D)LYr3T<~BYuXb5Z+6yY4O`*TK6$RkBBi+HB&a`A<;+I +@UTwudeR(TI~)zDj90*d54pImEm;jy{0ghVed?DYP#9gK7iF>M0L@xiK}X(ZO=)CreFt)` +3m2Q-z(9>QVCg96(2hkTsx_#_uD0>vZl>5{ntKuef)1F5X_VCU-rM)AI~2C=4oFG#y_6&U7i +3!5hMna1kS)1&7e3!Fa&|^u{sQc;P6%#etI0fm3|TY9+P>`C@0~5MzDMI<9lt0F?&6QW_z7QQvaqtF9 +~c=W>X*Z0qgqkH-7K02A-sj(u`F+Yhx7<|ifLo_-DQC-HQ;A9!PLy%vn_y_SS|r +(#EYow&DNqPHme-HAcppX{d_#NK1s%3J1rD2}}ir1m)`d>;ZrepwQV^xmFk?%&3&S^a?d+waYVf{F$5 +b!2Yt*=HTouHxSp3H`j(>dTuQvwvJMX34Z&r3HjI|m+lid@K9^&150bQ&_XGfjuqyYtSG+HpNQ%g +FP@kyg=Z)UI*ObBO4Cwdk`HIdixGqWD9cX=o4m|^#$zYHOl)|A-mP~r&qE3_|1T}F1lRVK^r +OKEOt#|>3@TlF5Q+He)v3A90)?5y!120~q=lM*s`At0*H(^|zX4{)QJA^IwZbb*gIm7GNSv;^DJbJqD +(Gjv#-Lo27n?^jgyHi)`x7+L-ow%Y*g9B4mJqw5#7<|EdFIj1qULsE*pti0@lNnub&6Cn3rQhn|LOHE +vBEsCOF%C?0z{*r~50NHPm6|>hU*{g2d|yC;ge{=e-5cYS=SLFg=qOQePsn{H(RmgG;RXaQcsIqk*A} +uK&qp`1VyGxynFdR*Y!ns~;3_kGJTCQ(_w#E#`Oe5!kMY4vt<}YmdDS;SI51tvgJY_kX(^emd}lP{Jz +LqqrFEbvD-dp*7tXxu2~M8Ez(*TM1SN=2u^Atb0hOgg-f@Ssk$`8VEL!PEv@TD#WRfTxV2Lj?jX-^c$ +J48bxH`3aM|prEA-kQg*ZJVHoM!Ra1BK8AEO{b1@up#dOm-$NDhS9%fkDU+@>Du-ZdB7@?o&c~6aT2} +(5Q-aP`~1F|@gxbKt5K3glteK8uk8ONK529mI`Hg6+iNF7Kuyj^ +U$`a~TqoVZ``x-q-J7jna~wiU3`cEK4i4xAr;(7r-LY +HQuMZ83s#Ls$Pz`kj)7KcCc$+w@3QSvRjOk^!KXg0V-CMmRe-8Q`ST(_+@ddyQ~+Gay@$~dNq9|3h}H +Hw=Y>}FPuqbFv%VN|t&+?;>6tooIZ{u6&R_g?y~4c#Qy{NBDA2^qY%tIE8a8?XTHIU^D_P#kS*JUUyzAE|1f7 +W9jA6f(|i6GqO@a +uKBJ_H^XKVf%ZOu7b6A90DDmq1fSP1f)h+$@^4-{tU +v`O%=;BrC1epQd@IYaDQ|6oC&2@Tqj^=)glbHtc%m8LxINtU+RJR6!k#C+)7t1+ufx$>jfK%W9epOC< +BkHI3<9bhe2&h*XPyWaR?bGZ(;Hti*6#P_N0t#oAG6M`q;fUlR@i +f&bWQclS#U7MBfW#^^5msVs|~Lv2Lg%6Yo>BFNbBuTiU-t*)H$jQyfj_hV0t@{A&C#YVJ9IO2bfuDdDz@lhN ++JcQz*Bqdt}aVR|~-(&0olZ&!b=e~8MEBluhd(mR)6jO=f^(cNa`qXD~N>*I{l`IqU|D#tjKTGZx&|V9(a7%j;8cq` +H`4;}nZX97u2g>An~&)%>6z0NbKdh6$v)O2%pelSY>aj2lgub+kYZbxwC$}}IIEUd0MgCos>AZMh--z +B6PX8(qZYgIE3Ff`4b!G#wPQ@prNUD-f*HoJh}WA@o2=@qlXM1pzMr&;8M?HLOIBnW#vxF|?5`>c>Br +>yqKfQ1uE&|{_!HZ-wj;)&8?LzUaU2Ky<5~6}DiT2CUtwXzR&yV8{0g505lH +D(W@5lT#6T#zjof8`^6oohi*A!;RFdoh7sWW`!57a49=C$XG1Dbmj$g0S2LhCL`2Y~6bRG{}+a#B0@( +gkeeL7S!arn60cY90;JK1~8_36tqM+`Tjo7N`P4+?s>`?wZJ6EA7{sCZ1zj}jctFT15nbAEs>^!P(h8vt!%52 +!njq5cTj)Gg;!j6F-^v}&Rjt*G~clhaYod;!!tMH%_Lk)h}zNeJwEx)@NDlcH@6CL18hQUH +8Vg|S{SvRIMdA1LUMZwco(HKc4HPanU^LM_&GHz~F!Hvfl;{e)p2Ugws9wiP<%jG5MZHNcVF%l6 +gC<#JkFbB=_+!nAj%~n9s)$qPN8fOzsknU5gU!o-EkM^Z~KM>~5Hi?&^*#-S3vX^M>JUgWR9>UE=*fF +OGIZ&E9izyF?V(TQAIePJM%K6xj#fh<6}!gzn>Q+g-PtCF#Bzl71^Yk-aY1V>6Jqurzuf1OC$9j>mR5 +?fhTDY4yJar>etN-{4dhP563I@1gH?!C^O*Gau2N=^`|F1{Y)FT=j5W@O^T7`l>t8ygyz8Y%luET<*8 +w75EWf|A1Hex8N1{5nulTueLiWU)^8k*-0dE*;ZLclC@j@RX`{aVhE6kBis +VN!rNcLE%1T2SU*`Ont4Dp+lr%4FbWzV1pd-678QNTFy+3_w3FiwrF+62{yoATj}ST=Ind~5HCdIXH= ++cktcfx{7wnJ}HAtcU!|9^pPL%c<7vkt8r@p<=zWO|KcIgHlqZbGu9|MRG?ZJi$FRtT*l;xM$Yg)O?< +z$0uTPM2yWZ$}zc{8!QJsoH1(m+Mzna1@6nSea?)v&%25Sj^AEsDgT@A-|ZXi8ne;%46C*5#XvVM2pm +#$vE70J>0S7C1cqq!JTI>PMQ?I6NIo-+SJ*4&tDvfif7c%-P1bagjcQ((tmq^?VPU^J6e8f2z^m3rgJ +WVL%QdSM4o@6C3N!;zy#V=n0&%+etY5j&%(1E4&ru$uJ7AK^+d7W#sWdwZ|hSTMp@(9d3co@s>UN*`s +zQbF9@HbuYj};WTcN`|f2B9yb&zFIIDSXq5|evWwHs`PHO$Rz9wGH@$XneC?yBo7+!?qx4b>ux`fh@l +dHQua*SgxR061Nrcx248W@dQrZXmm;)73W6~23cM^%mZRYr#J +7qaQH<0_vTgWP4j0&7O5O203$2hzy;878gx;c@d0?rGKXLGuejZB@gR-U;9-Kri)1D-l$)sv@<$H!G; +HEZ{b*r*@;vv^p0HqR0!<8=hM8_#Riz%S~_1u^OnW^+!}P)35>9kTW +2z3i59mEloPk_Aikbp?0sZjR1ywT;o1dx53y(BzM?kb`zJ49;R={lhW +MULwc#=G4sA}~*0bBk2-*E%_CptVzZrt}B?Z%rQsJyD*qcZ9(K(c+G+ne=|8S(E4 +R87}7cGe4YsgjBCphm!BDIRBeh4hu6a{+~)z_H6unX_UccEzvE4ZUh*bg3sc*Ok$~KxhHoY738=@Z8( +?Ctt+r>8e}EF@hq+PVOi`&riQC2Q=ZFc7?h-KEFsgxDH{}Uu&h7y^*dTsyVs%DJ*2mozOJpq@2WXg7p +hT1DuZGZ_e0w{gsW4-lm$KX{)vwd6_t8(cx;)j&CV#ZcOl*0SWBM)sdy2bXB@Phb#q(bD?HZaD1%K{u +NyY^FWfkb;J#Zkx5EyEBg}MX_A9oPcojtR518~ztwyZ1zk+_H-Nm0Tx&G0XM(a$$7NSWMuXIDs6zBuK +E_#e9lt3skpqxEP2&7(IqN$l67a9EvYIJZaKY>6?jcUlvs0rj?!bTE&DKH4lA^Go~VjcPDuq}qh* +QMdPtwwhr7~MtWcPIN~S1XxLX300)NWfYXZ%bjK>tDFrx%M5YKtt4g?&Z&lLqj5khHpNWFZqG?x<9?G +_c3;I>>24FJDTOuKLdS*_u9@iWLTG2=K-Am(d5isS*Mq>J7>QLPx+N8j)+eD^RpR8A{r*9Q~m?2`O_f +ohhO?cHgIZiBYR{hqe*24EXZ{^<)pYEgghCEsMHpFaINA2brCF@j(yiXm}imy~{LhDLvk(TDr2LHeHf +qr-QW@q4n5c+aoX_-^x=>@H@Xn}oiTP@=bQ3Qp{h$;3O=+=H5__pBkj8?L9!-m~5Cb&n%K-+nMZi#CC +=_v}RUC9@lGO +p&{tRbB5@_3Zd-cf?-He41|?GZiyZcqI`(XeU6PNl6ORS8t=lWkR*-1&Ze7w&+n8U#o2Cyk0xStr>gJ +(`reVG^!M|RV6T@`bA$`APQlTKDFL~216p_tf$gZXVR^F>`8*W0S!kms6iv2lIx?4K^LTIkxETgoMks +TvD=%NDy@2dMUI#b?STb{n@4ilk-4KcuWgW{BAo>DawWM*S4{|$&mi@nFV`fVFH23@2(ZXc0(G^G^D; +5i;dh5L@44fTXZ`8Q#WWdrKOw5cxzyY--DM3DWk};lz*Rb^hgS=DlTm6cq9zv*)66F_;#7sP1Y!Kmh< +p>ABp2_~~;xhz~rEcyClqXX7NQ`pW^Cfh;*dRIoAbd1&q;(U$2L7qwhzn +;mg+rmedQ}JThXWbR$*G4Z{&PWcd#1`7uBcc!I8iQ)XMCRgOIn(Dre0kIO~sywcL>>o_0j)?Ht%I>OV +dbdM+W2b<-NU89RJ5FjEFKZtbVZSUUPoEi!Wh04om)>gO?te77yeOp92bj%c>YV^TzIgwx$>!zitGhD&y&fB&fR&j9cxuXo!(e;Z={<| +Us&%pXtx4q_OB!f2F6DRft^Q55m%*uw7gMeIG%4HC9Zn|V(c;O~PvI^N@{_PM;|E%4fi+~?_88xPv{b +@biJq@#C!Z?b=$5j#oy2)as;x6^g96RVFi$lccKXABJv_x9}$QQ?k&8~dW(Q+V6Uw%6nDu-^?kh-8=3 +ZI|0_vw_?`VL|Lnaa(ZoZj^2WZ(GowPE5W1so}R3*stgBeuS7zpoQakb?-L(xf$61HpF}jF%5m9`76Z +a%ZCxzpCRV`*#ADne7}-^0x`Yf7{(kAA#v_PRJ0$*p<1!K>l}SIR~y~)&q8oznqb?lab&{m>^k0YTaZ +g^0j?@MIo%=@snV@8nL<_A8d!{+%^azV2V)Ke$@i6aCdF5H9Ro=3$hN6NBy;>Y8baW9Fxz{ZDv1&sat +#tqiIFeFXfeG_g9k2v@mYA%V9v2oXSm>0Z_*Op83UZ1^) +W1YbLs+t8W)bQk1_?eyyipj6dG_p`&)FwHuCqk*NItHN1^3h4@}yMcx3^R?Cy&gd0)%_88Hb8(tCt_D +QrvT3f(YhaKH*IwZ5V~2jH+?wh(+c}T(Z$H#1@6czkwKThHQdZ!|6_zfTi10TCF?2fbq`Z*A*Tova|&qW%6t*E?-r;0HY +!0TUgXpZ06l*|ky)LY)Kz;{))h2d14X; +wJKDEH=1`ZUl@@b8dYyustUccO!$=W1v7B^Hu^6>M0V1Y<^Ukd=79Ln6Y#Ww9>(#;F;rUPTw}pazsK_ +v_+Y_qPKjG!B^OiO!q&7m{XhH%i;dp`0}&oe+DjJp8IEHp=bu9c1(dO4BwLukWYJmF~0B5@Y_vgk3x+ +1(K8I*^8nzt*k$hk(z{Cuz44IG$xA}Nxem4ylN8=3;5L>JA-kY@BRzXqW42FSlkY^@4ad+o&DrjQ{*1 +g(Z;uo#+o#wtYIkqm*a!S}yxi8ggA1{XWwv`D_&y4^q2BHvmhSCBI^AW{8z64bvtb)b?5MPNGtqa1!> +-QxrTY22H-eUL(eX-jo!oOP-46r)BeEDfvRFS3#Ql0d|HDy>=ghw#ixuC!KD>DSUr@zi*AL=i{edbUk +9}JO@cXFp`6_@fsPe}uev$?CSrpXF=^60yAka#%gb6?mhr$kbX8hi+S6XJs2XDME13|SVPBo$bWx6?8l~i`_PCWbN_#=i)dp#0$lPbJfi&<&w`OYt9epZ7bx}e2!FoLfdx6 +)1)bobEM2Y +Tt}O83d5m8c1{j`~xxxGUW2SV935e1yymCDBv8K9_ZY#ca?7{C>efD`aouaH?W%xDEG;I6oPooY_}%6 +pF5c8Yl|~3Gl!pi99?!p`JW>Dz^s~XL86~Q2V^Gk;f)feO%Rs!d)V-l>fEZw$941b#X2rYRwjaQRu_~$ +X9k=2>b0;sVZ1A{6SRKCv&jTy1Vl5NJth8B%DO88%`41`Wm=pG|H3G!FA{cGYV)5LcHok5}6C)j +v;nF=E_;8uZ5}Txq*qo2(zB$6$LEEy=|u_!?`Y}erievuOG(7zj}h^(VJ6h9%4Ro<=I79YfzPMhS;em +NgC{6%Ph-0^YGIGdCzPO9{oAxSp3pv1P9PZe~HJZkXPn`JRnyF35{z7l2&_&d2t5yyL-Qg=EnI|JbP` +mk&T)IhaMlDJwZNi0fx3wNcLR5Vy8C~f~-OKcoa!nC(P*?-s6Z&7-2@hIWLsXBN`GB6g6$`s^8mMAjJ +S&)$2jb(z_MHJ^@?YARkz*#AV@eI27Av=*k+1Pl$3s566@C^i&}*gQFYy4|0xl3mEXbbOXz1v1sy%z3 +@lI;pefX%>}B*Kb& +zsMvl-R{*XEItLD%{^JLN@ExaH&+#Ny1eD+*f?vxGT5jgi +!BATr%f|gi6RzcO>Xq_#&yYcvgAh@EP1XvpU0b+idd6WK{16D0nZ +Fj8*vdm?60DBTC@=GfHa`@~`0${r7kK;?VOY{^DZ!!F@La06bYm-hSJ%T1S43L^dZ{urVbSYjJt}B_m +$p_RPQBvREZIOO~}vL&PI7fO!DcM8b?>@}#QdQn>Nz>uYleVs6E1@ARsWZnDYIaxY+cl)`S*Z`Hl!3O +?0V%cPGC&~uybCcPyJa=O&_HxLMaw&3QOK)#b%g7hV +>wxePlj|lR2~W4h???s6>X>On<`6=ztVfq +Af1U{62Z!MFyb{+g#nB_!Uu83zG$XbC!4|a(D7GEry?><1*M8k@optC(3>a}T#C_EFXkO(st6^p0kB6XtElfwSFRnfghz5A{ylLHCd7+W +=*I>7Mb!>|5bR0ypj#z8#veaDOoT&}5STW_~8i+(&ZheGT--i5jufJp8?>QTSU&itbv?=-seH;GN3ud +*a(+6A$-YlE(JiB=4c#-Ls6^g_?W154_ua{;JhD>|&7D@$25lbArQu8h0oE7~9JkfMF|>@=eiSd(pin +gUfl`cxHV?iy=biF+l5Grs3!}VKD4?QmwCMTm%p7x`o7tKeLqnvxZ^bC|dqi3?jL2t;fDgec^MGcT@K +08aWx`d$-$p{gKYYE6c8NG*<)r;t@8#lZd}O2KW^R{xH>RIlNFBTgCpx1}yM=;+ft(I9mQ9SIGf)Avn +Df8sp%84cJTH-;TU*>Ay%K-iH*Fuky>e=D)rlA3O574*t{t-M!O3Yk&ej^P$F}o_SyzIgPw3Dj5<^;t +}@=mZP4%iXae!bbW`kGT?e(`{cFz^_rql4oYyvZp5>?gkD-me4T{%Gco64(DKu4R|O5{j%_d1LlJ3;B +Xt9CE~khliPMZkp8rbyGHcftTDb}y1S(CdDz$V`qwzA&Ef{zx$wV&_*$qbfm8P@j0vtD9to7b|b|vD) +i((A^)>egT&@);DG;vO}TQe%#S7kgqT73rj-39FYh7x)>Y7OA#s+e=Qs5ibCQHVD{98YWH1|mLpBy#s +bxiulc18JMx2wrsO7`LFzJteTtVMEy8H$d +N@0{jJ4zHNcQL7Gu*Rb%m~enFUmBpmXC2T_8=zYFXl?z-I_UiYF(oXNNr?~((8+~>Gg +I774WqynIoho-c&^Z&4^?Gn=>b-U=x+p#8Fym_Km$qsAMLg0QS +FLM(;R3fZ`9xK{W<$U^e1-ys3#z$L{dS$kv$2gX-<2S+R48<|GaLAvG^1*3fQ0Hg6>-br>URsIzBFGY ++J}Ur>U7Kd3c-$*eDZa5^%#hvlu3;DM<6JkmbNmh2e$X +d=2V5KMIjrKd=Kt9t(EaZ(c9lP%=6`URudw*bOZ+l7A$+IW)7f5sXUwjah~6m)8$H;N1xa_Q#6Hj$?) +e84^C>cJK*XebE0uiD+U-SuymvLxcQ)(>O&fx3AGbrLuSO?JEOUm|0=Bjf$}tK5X_2lQ~g +Rj`3=FZen=u6lx~da!uZ-E9N^L)owMZ1T+%)36_t_SMPjAi97Do7^{l%D+d)uVD8!kTCdoF?nMJKAa@ +jFKFi?N5>^Lu={kmpRE@7C(He8wZPAoyKQ1WbD!Wo+$V(HePRc;!<01|{$!6X@!B>pr##z*$QEQm`@Z +F-lzSFT3>=vYJ0MY6*caHmC?`7HmrRzmZ9z6Yq1{CJWDUYI +hz7+?b--D%Zyr!P=U>$npPzTW3srMaQRx_YaV}y4s=#IRAH25?v5?jLNrK0E6`q0`{gOU&SDR3M~XK=)Lxxk=?M4dvjq*?#;0=FfJJ-Ss@eOZ+@vS?YC`@SDtTT##j@4+`~ +p7~Jmw+xc}AyL*+8x<9I?w6=NaTvZ3&zYkyP6yUSGpOfifr=-ow=q2-P;f5t=-CA<89F$P+Hh(Cghh- +ct+95pvhR~`vhJGT1kwr1)D| +q6brg$W&C~V7-&B3pIt2ZC+xdc7+1)T+nv?Dwp?sZJH1-8aSCzB`-)J4JYt?M4I&a548{evJVxp^-B< +G7|iH1?;}I=sBA14kK%B7~o5f>LV(Pp?$Qj|~uZPx%^~BxoMDUmg%w6sIt2%b#IBJ>t(5Ga>2Vt{G*v +5F(m~N8Bve0xt7)xy;!u0InNl3T*uXud75{$;G8-b#12!YPzFA@D!CO?(Iy=?Qb`)j{j{7hokYqr~b= +cHd1x({_Al3*Z-$G{F|A*GkA6k+Es)@^A%V=@_5kiQ_KJ3Yke2|^V28(Qu0s6@0L)q_n$UU*eKOT#v* +hlX$iR_$bPhj?z)X+<5{1Z9uaTbNjly!a08j0bz$$O6TLT@;e9KixmDdmWRA2>E +m~o>V3tHuk8s6#NODV-)2gCBv7_X`{VcI9SQI7_kqP9@vgho(%WHr<9*?~DYkus8vO-T40T8FnNL?=-{ZSvDB1ZXJG^0+lpLr<3f}{qY+{MLi9^$1*|nCk#9Yk!w6Zp@b +nD8V=tIFoW&gL+#rc!Uck;+gB!1P6vPN8y;l%MSnzaTH95pp8$O?j+q3=Q-XUUsvw*_nUK{I^SU!;nn +YcJBsFP@lms%^1wdrkJoRCHLsW{)H^gu<|b4wPPr``x}P3ziR?>!U6Ps$3ffX+oG0QAIGzN +J;@8vP0p4HRgMXANzz>3Ybg7VFZF`9n*H?C^g+vW@)Uviq0W5O+N% +UMy(UxxbiFlncYz+ueeHA`r56*DWtv?YE)wB&gMTz|1~r-sG(BBn;Bm_{$pMcyaGb#+8&;R9I8ODD;c +~;W4&*LtDSMRkq?U_=<|LEehQsZMwn!3R?*$F?5L%TFJ8vZo)TX;^p12nq4Uy(ux2l1~*2U~-mwffjS +rYEcnW`L*QeS5Rw!s@OE&$@*4ot4s3=GmM|*VvF!uKe*XX_?!&t5-gGBz`ak2vNAvOP6YPII|Hb|bg10{ +{nm}QOq<%b>`H!yj?Xc!=ukxLB)EiRrtI8SUV+VcBla7jIPQXNX4Z%E%3*==aeJL+%|<@(qN$kp +HWobGs-U?E$Ui`>R>BFFSh6{KN3g3A6pX+pe}lY_@y4>{D*PL^Sc;w&eAFe+LS +!{2s87tQK8o|xE$TO0}!LW@<`I0Bcb5HJy?L%z!x_f`c2ZGZ}dIF5qK#m(A8T<`3*FWHnou<_CP5A21 +&kYjav?uTh_(!w@-X!Wn0W)tGS>7q)l`vr^7qvP|H<;c4G*`Vp! +n!=GHu90AuEvP0df(=Epjof1E|0=h-MJh&``osow;l{~U-AEn0Q6%wAJ;C8q@Ss(FeB~qFTfQJjM7}| +_0=DED@jwKs{Ef9N_$%-HB^XgRtb{b{*=E<0y5(iewo~4j5uF&i7$^e>%<>d=5doif1N8nb3$!iHtoS +U*T4o7m$I7UD+_L69P2p7hz4OuQeqhq^K*+qeX{1C7HL}Fz=C)^q<3%c6rEiTXeMO=+(sCxKCMF|t+N +Otvv(mRjUc{o+4ghj5;HUiq&QG8N1%JaDWR3rMKc3B=KvOGEMW4+tl6AWMqa>yHFHCzMMk2pO^7xXdb ++q-tP00ObV7ym?60pyhUQt^|H--xrU=p%@*loRSrGRrR^A56!Sn*=3Ro6gfnZ$tVtpdt{ZbS-4sMY^d +41HUe$GRy*-k-=43Ho!hH>k3JyQKG$4%Vd6vmx`A~*i4WFw +`%*b}WtK`&50sfn9p!_&Sjxfd+0SE{J|FDc>P%J#QcRO4>2SB*Me&cr3I?jqtn|{ncTx&QRKyBvoxffRtOh3H_GHt9@Ug&aJNCEtn7bug2^VU<4>C +L$q8Yye{v$5pD`zesi{Dp}0ta?-Vo-un*OJcFxt_FH!_-5lE&1pYAzGABNR%9WLc_7fJ-{~Lvlun3<} +EO(RM@EshaS}#`L3t2oLjO`L}ZL(NLdA|f;+UPH5~OW5FA!jr9A!C5H- +XYJs#D^Q3h|8Em_tjy&uoes~L_NECnN%F0Z5^p>Pg!lG@5{RdoS<>CWonVyQ5n!4y6X35QCkn!XmDV65j|R~t;k8Rq|C3;1Z +djgS*ZtR_-an#$D$2fog|+_=Lt_YuVd#&A-9LWu*IMo;FZ}Mj`O~Qu@_i(W?!^xNPSn_HAL1QUPrPlH +nP_h&;Q0Ot`gubgXLg-3^|mia-ktUBe{t$v_R;S+VREmc$Y}4i$K+mSA?SYS%lyoFnF-$sRq!sP*(yC +Meg_#dyV{4r-wp!rBj`BUBZw(xKNG;;Q~_7;FIhWa1Pg?w)=Y;U5O$(J$XIOdFjuu(842MJSWFmgG1;Ma^0Z{{#0NRH?& +Ko)Z$x+NuBnO14!gsEXdc4mHiga=JN#$FylHtJ0L;epxFrCz6*rbba4Fk=d`lLzSd5zU5E!vth|Jv9y +;pmw6PH(3okALpNaX3Yg@o*^RvA3k_=vCOS$(K84Ilnx96AQ~#!@!++HB}HZ~2{;l#pm3MGhE??!7hm +qyS>zt7dd>#jIMa5`T|<*w76V0Mc?OJ{wVVcmi|45;%;Rws2I#_yM{dPpQF*zCm-C9DPIuGX;pU3uN1 +=s1%gda#U??j9tb0;knk;|Rm#**Yr^vWu3p>5t`6(ekn(XKy2-)gtnq}eCMm&r<&a2GVOE=KXVj^gb7eh?4T<}zsw(ZpQx~3>GVAOU!Sz#{X(gDAn0PUn4B1(^~3bRa_ +3)6R!X&&UUM@RHch~y?3&grj#WsYFTVlA9wuu1blz`@E +eg_#+^+-uDm^TFl)Vo3Z!!o*LyIe*<;-poW`U4$+GCVEZBS|M~*n&QAhaP^wk& +gr{LV^Ww!wuNmusV?9i@NeDPBD34C=mKO#zO){dU<4;4BtKK3aSX!gM1-8>Gx=5oJpeH +4$~dW+ZbhV#vW1plV;-mN@CA)jL6B{(YGo0($w+pda=YmQ4fUZt99j+ir88a>`+BVyeA#}<7)!=)6{bAM>8UcW^ ++7J(orfQbZUG87xPJ3|VipKlV%mjBZK2^YzTu0hQ9uJ7u)N49vD-FB8REq +F`hNKV2sx9nw_S`qLC;@%K$b_#aW4_XFFzf58Ml)}?J)EzP%ROFp#U$f!+OeN|=YiD;6gw62>g>*~_UQ!IA +=0^^{q3*0svr7iRwc0I6_4UBd(CfM4aks*3QaY|%ijLyI;#nd{=A~3pUBMcG< +n;Qx{gkz%1^=+t*$KHjl!RL%P$S!N^uKZZTmcVyV|}yt{QU-N2uhW`gy}el8L?hV<404b>$;PP<2nCR +pm9`_<D9{_~!m8S)y6`2AGb^H>ymBckYeeJQdnb1A4Q~=_

    t-+h99wiX_#%`z +`6)@hro80Qi4;1GC)pPgA=m@n^-|mh&Lfy6i~9V!3dAZDp-U)APCb15Uq4esvwYEM@FDTg(fKREI>NN +NVL2HsaynVuvDf3WjF~v_xa~Di)5B}vc1B6KGb!UM&R7i26>>0S#YLB;Pq7=7@X~hYTB&Pc%8!X)bO& +2L1ii0ipq!;cWftSXm*tbo@yrS_q;`TLoP!RSWv^;%^UJm;a8#tvaF`xnc(utM)Zs+5;Z%e#4_;H)5% +2PkG!D8$Ai`v?0;yn8cSaDzOvq+CHcdWJ+TPUDGdUTB%BFEOa+fM*AtJLx*gHofvO<}p1|eMIH&D)z1 +FC0t~&!e!d)`F6Y=?V1T-SwA|0#lXm-2PTlb72*8agak?r_*T~R +)xn9Y9AjVu5UbKR;q)hCQ8c5t^y{*>JaT79TnMufI^Z3j2{363php>!aondwxCGflNIM+%@ +ebSd*kaJ(1i*S0J)VpJ{n`@F<+{1{>izN-AUn|JG*$)`Ye{#u +AI{`vUf;}*)+P;YahOV2mnDnVKMn{E54(|=pQ1du8Kcx0lbCLe)Ltj}w*X`@VpM;0`ns1An|G@6EK3 +$64+DuhAnq4mgVHiaTM!zc!JX>N37a&BR4FKusRWo&aVY-w(5E^v9(JZp2 +@#*yFoD<)9c#-^6ANk=J%xlN?dbfj38RHA&oD;*C6fmtq0EU?A^B-aZ6`}Jd<0G6cga=E%qiUI~RJv} +}Bo*tL$P1AN_+1Gj3G?m&bxuNW28#GkS~DxpDd%qqKef#F!`;%|e7tc>$oSXtyaimt+(}TlkF7WB=Z+@qr4xj$sJRO?01 +N?VgJdF_JXI3y`pv|wcUc7I6IoqQqVD|b7xrm7uMG-bAL?vg+z)H8gD!IOpMIj4XhPckEUaCaAUzJKQ +ERs(cyJ~P11n?XwcP`ew>cj;QrrwG*!f8db6z8Lbp6g*A&^)VgBwzz{>|l04EvpOO=kw=nzACTf%X-< +IC*r3S$d&Ng1oA*#;Ccuy0Q%eMo7ekFwq;h8zh)QEpVtbTvc|`^CvU~?55AhiG8_&-ima@0X%hLP%yA +F3Jhck-OvY5fC9@^==X@MEKS}Ncc`~*O1k*{Qag#&L^5*k`)%{qcXU^POt5t&fXdz}HNbSrV5lq +$u3X;xLujV$8Xc>?>!gv0rdgz6YMLV{oEoyWBj_^Fj35bF)li}eEjo`~0fIYO2!8c{bL4ljXuB;$r_R +}v5_#AVy`8xbeJoe7{(#vR2<-g=;l@=~fU0Vz|R%!)a}RUmoQR$NKBVVHm!De2my_K-+mLOih9C2oY# +d`P{TsJ3qj3UF!(vR7!{g#XJ-p@;%43whCB;@q_dbJI4U1%YJ%w*oX~))us-H6Uh}rWXX*DO?IbD1dj +$T5qqGZ68xu#X75S8@0@!-maOB$R?XA%Rx@eh&GBp=BuX6rMjCfkhoETgR{=<5U~dLSww%2lAoKhj$t +b$Aia?>Z7;IGj5yD};BX0V}Ok# +jG}pBK+X{!EEx9%9t~;B#<}a;nm*WeL}IR$%Yp|dB#kO|VD&(lKF0nFZ_tANdOMx5GLg#dGK*F~q<0K +4Gh&A#n4xjIS(Si3Q%f{82oTZ_Sv>DSE^3yGfI1pGh7>zKIT9ZnpPb!!I1UvacAIO7I&wSA5YK207r; +ot3hc993fe%V{}5GDXgrU&*ot$W=oe-?M?=WtEb{<@30aF1OT}qs~cL=PVpu(4dom=9SE9bc +)CsfMrS+)wjOpHYWijaXK?XETr`w;LMoZzftM-%2#TQF{GpMnFB|7Qe!BA!>Ond5{%f&bgwu=7Hg5@zf +2a@85n1{PMdU;qV#C5SMIwT7csOJQ!Hpr1^8%y9xzyQi^j +LH9MH;Nz`Hac&~P^dp(U(&;W+UW_vo<+t`$Y;5ZdFxH+VfX-yJuBD4yl`lJ`PbKP-klsjKRwwYyfOYEUY(<=BMwWs4b|8~49r^!0S#zD%fca7mCFHOt +Bthifa!%LObHV8gg{Q-KPFH_dwSbWk?nC)Y3DS}Jl=N+vZr5VUlQ +64Jnq0q1NC_{4bSokHwJJC%?u63MlNp}C=l8S~+c#hWnW<;@-TfPo32W1HJZ06I8g1Ys^dMA6x7HlUZ +8Ajvixh)7~b#MsO-a~2#tarFEkLXqOL+gri@of5H3E)((D;SzZQS?vBvEKz!RZ{wL>u@;v|`;Wm;%cH +VJEt9f{rdovV(WpOI#RN(cTrJc~%f5oW@zv6IKvj90tua}GZ21NJugSpxTh|!&7QS>G7GEIWke^&|^b +>t{_*KO2D!O9W25y*Efcr*IHWX1gMjPr_W=uUB#)ysPm@Ef{Uuac}-!Ek?+pNOn_Bl{ZY6#~PlF6elz +vRTom&j0m1R$VKaK=A_nrXLBmTh*q27PjF^pxtW4tOjyYHdXi&NqA_Ub1Jxz$t`TZ@TTA!yaF>(a3f#ArVidi)NPJ-boV +o8aIrS4h6FuUx%PNgXdyz}i+hK9@6T*l4AclO@=CeZy-QL4;uMRQ +XEuIXkAj9UAjwpo1zGqn(#h94jT#mH;fq`*oUCU5+&xK5frm>h$pls@Tvg#LQwhjcocv%|=Zwr@tdU5 +i_i=VViI^8?wc$WNd}XJEFdY!n*$|4qgEnM?0sh8m2hP9{HyI`<`?_#T +#2@HwHp0nplF=@oav-f;OH!r){QRBnxBr&*=L3ZHctP+^`(0k)vDx$j +)ZoWWev9f9J=CMHNVo$4<-zh4qSG6^pnz)1ql9ud)mheVDRgpO9G@LUfFsTC)v>qeDb({3Hn5@VvKkQ +V?u097}JFvwouwOyA5u932SE*?O5Watd9iBlPpigw};hvc2Dn-({db7bfx>b#+0jhtFXn>M96aFk_1p +biRuVSE5wVXz&r8rAFgXvDy!TVL|fejqCQh|0cI_2CZe+NX#1#upSeX#rgFcuFvz%}p)NQ3;+?vV-yr +1{G3%m|Vt9;I#PDZ!F;l@IG}DJZ0=9+idPgt0t?INb8}eLu_YaOYlkptoZAbo}J}a=m*uz{}H)=H +yeLSQvD2))GvJ20}vdS(wVF2^@=#OB?q%|vMoxvwumvuL +}&~DG?U%^z#B_E|8pWr;LtV5AVVzEHQIdRpp0DNw!Fq;8)xz`z=2>IWeP$ZkQFe^u#X;?uHEXe2X{1s +$m2$5wg(tm)CTQ35xTG$Q9n0|AqQzs6bte3Zm?|Ur(o9HzJ#3yeg>sn!WNiv*hKRM8FYdb7;bHdujFE&aK!EcegKHD^jV +&Th%Em4u%1>wUO}-1LVi$s^|8az2UvOGuyFPeeW ++NV?|0E&YS6rv4O^^jy9uRMq@`-l!lGK>yCS&{$P4MK-|rw{qbJKP)EmoQ>;dURpAYQV06u3T95n(sz +u5fU%x$Q!)if5J~=rdo`f+>l*o#8wx{N}}ZNP)D%KtENra{3%~21sQ_-wG;j>GWCy9T8xpO@@(|+PQ+yU!J>S;o357O{^Ke~&&`?-X +PW~?0g%~!$TKq2b9AjYPp|AQD8!(2xHg0e4&q<4SP!O1f|^y;=2npw_HKo*Bx5YSZ9QRp-neKtT~ +=v<*iW4wu@FFm27`j~|)<4sU6=<`)`KA?9ORS}hZ1eAQ@ogq;X8m79yyk7~VYLH|o{o|1Vkh0FJzK|F +QK_uSVvYidslAER-WXyD3D|Jn=3$Amr_Epzr(DQ7H1vEB8f=m`LP?451SX@cPbsx(JlM)~8p8cl1!wg +5!;~`kx*u5H6)L`R%8_Wkf_cDrCU(3sNVaOOJAAvoF^ITM$@St5FJxY&3`j(FiM8UG!0)_#LOdHHySo +vEw#_CaY8}IRG7Vps6ma)m(&Vi;Pa(8errluJ&A!L)nIor8;!Mr +~Q{Mhd^xF~7%+%kQOu0_01htP?*J0|Z$*m8fnVGF7zxRm11rHuv&Vkf?2%y7_E~Dx&ya|(Y+P1kN&@} +aglRO0&{A9}V1~K-o%-EyrSS_h_-bL37PpOi-WPCXCn6q4O4KJ5s2Qe2>*Y+|JoXPg;E4%~AN^%XG7h +yiZ6E5tBCs3H)439uAuqFl1SM13K78Tc9u3M8qM&KT5{sz2aLNBDJpIm%wK5-0vR4z<~7yiD?-AoJnb +bsmauoib5R5;|lwD2UqgxwQ2+X!Zx0)zyIL_xhc2`r|?!Js?_n4Xscg+{R-RVp($vF`J +Ih~KQFtR6ENI8=!C@Xp_E$k(~r1$W}Q8}v3;+~9GP$?%B>jx%&gz&NDbqVBd5=WK)oa!lEM>a~IKN2Y%nFsseWtP +?ZH(-ohc-amfP#^fBkVdf9bT7I=#fiwV&JQ~fYTHguXjH@8?Ae%CZ~){t|N0hioH+2Y{yi^X9iu9b-3 +;d5w+W}H~y;GFL4RV5w*^fey`=o3}UEhtUDW4>zwE2iV~qM-|ut(08Uu8z3dK_b2dAt7pjg2hGjx^my +EDv|lB)~?-6+hF;0i4|U%pt9c7bFa|vs#)OK4nXDkgUI=NPiEHD(_sB&125g|`lF6dUL#g3TzN +^XtRFj%C`>o@OB{q`RP-6A1&%aGlW!QhSgO?Y(&VBV_A7Y0i;Uc%RjIJpJ0o0rIJ1v1mRQJpD3vcWd8 +)Epe#Ty8pXejY@9=jS-TnbEuUtM?E&Ll>A(MF{%AAY+=OroaMg{+ZjDP;TexRw+sWV8=yL5k}n+-%x= +sfQ9C7(l=B}Q!}ll(04gpZ4>at&$yp4-O|JAA2l<=jdejJBIX7rNjMA#$*8e(g&g_@FWA(!E@C4aJe9 +#cQvGIAQHcafLrrv7ot9)!dl?|e$MnmpZ+gW_k(kgxv-)9<*Bovn-sEf)AM#YtQeM`&lw<7**6D#0Ax +B}_gNoQ$rBf&3C;zYu|2qbJ&_&<gbT^%GO +Iqz2K4Z^t|A29QBA)w|`0u_u7T1_obO^=qlP%VaCFlU5jO8 +VX@iva#O)3(BJeI73-NyznQ@R89E|F^%pl8{}%@mx{@46FLC!NJOl}F^TEi^@X*}V_ja&-VQkBBD01l +-%kU7Bk|zqlEE%tNfyNm2{MiMb3kKI!eBBzbQ@Ry=i2M)H8F>F}rdi{}REv +Dlwhge}_i{^5-g*20s=dN7y=+fKIR(s`*IwkcbkL9-H6B3&^0K_TC|m6C5ro4o`3hG_Zq(J})oWyoE1 +xOu2$=Tm0%y$TCf30OA4w03HAU +0B~t=FJEbHbY*gGVQepLZ)9a`b1!paXk~3>E^v8eP))1bFc7@!S1f*)783i=TcCyVNO|yhrI1r8#cZT +?B&tY8yZLzkzH3J*Nvey+GxF}tG)-f^DTs%VSTIJEkQ1==v?p%O`~$*{5*3V)k5r +CYh +~gPeK3ES}2o9`0Ay4sOi%j4`AuG8A%{}BYkHI2gKr8>3i|}L4t#j=k+);a7Yi7{*9J6rWYp9!u7tzt6 +4?+sk_aA0vI!tj`{QIgstbnE5u$M|6*xJqmTZ?*Nr_OGukFfj#-*c+9U4685y}GaHLB9ByowbeVM3Eiz@BHOg3>K*AzbQV?4#tBa#>5Pjjf_SWcHd_nTyP)qVYva3{rStu^@*@%Pw2b@xAm{VQrZPL(+7 +$SS+$qILB_Juvu}FnObfZ3x+32lJNgeIuo^PO5vqrzK~#-39pQ%FBkx2nQ|;MdZ+dcRAJsQ7ufMLqwt +cZt~B1IDRg?D?BJ`kRQk>(YnBAZk>24;n=~vWxAEi;LCH$`!sKBgmphhO37wYVo1Uz6>+?s)eG}sS-g +$f5ZFk~kw_EuewnYlY^%Z;iJxh9iN;YTu)>rhr3TiRYcPTU5k23R2>=4s)_$VdGp&vSBKk!+ePuP!*D +)XLIoW0&Lp_#Dzrjpt-=Y{o--7854|ofmGqs~P`2hF1HEWy<#+-9AX9sB=@`U$}zAKO__=jRqTWR5_@<^%Uq&-A+o(9#`zVJ|<43_L +;<2=;tvDv~0dD-a)ck81s$f3ax^b$NF*k{f=1-9^_PiJ$3a`LuIA7dF&AW{0Mblwj+vA9pq8iYQP3EGmfp;g-J)a_M?h-jrv??slE)dro6l}; +uGXm6dx`Q273W;Z(0&>eH9U8XI)L9Hj$HjWyu?AMlA_D!iKY&sXA5+@>(a(b-&jr1J2bzG&gD2<>tPX +{1AEzv@qnZSVQ|eC-TIH&1 +s2C*DhlXme>vCxdFRpwkR0NH@f|I5ptxK$ji?3Sz7);r@8-3ml?RhdYcXrKH8%X=P)T(sOipvzTPrR4 +r&VH{mStvznfN!mg=`Luvf-_G*d^k>eN{TwN-^oC=?n0KQaVd#TaGN;D#+4}kU16Hnklp3>4IE<}mx& +GjL&V+A!)^8!C0Y2>McpQ@OckSx*?$JflEFH;M}EaqpJVMnkn8fa#lT%wIj$+oq5`uywX-+i-=uiw7- +`UP(VcTJ9k8+)6&OGf*KFt(j+ok_8A46TY)Dz +ChRcy_nr4k1Tc>Ckegh$|5(yYxt8Z_7$9ah5*&=cGO!()@tzgOx6$UT*2oi^5U1_&k~(;E;bJja%Gkq +GT9`Awh11xr>#(Cs`lLfthb49)rBcC{e@4nTbFCxZ*a+aPOvrP`SW`F>DhjFg@CUllUDi|4QrWYMHW- +xX#hw%jR;fmy|+62e!fbVn(jxx{%LfN|!5ivaY`8_adUv`(kNjpY16-l*>AaE;4#8-~v4LH7ws0VKTa +F6+_1lgLor46*rBV{prx+`^ZAUr$oC|Mi6Z9DwIRRZYd>wOef!;QkoF7PWpvR58bw5H7XDh!%Vo{-`i +yg^VVg03>)IUKws2xSJN7v6m$vwb>WZt%`894T7s>He$3ST%)#0e_+5!jQflvI3nyaMP`gsD +-)IF2}lu1j~oQwp6fPc*1erQ+N%4&*tdN#EEDH)6GwnEaKoe6W53Wdx9u`SGUnZ0@ELV@$ulVxSo8pK +1%6lkxHWG?AwD+?B%Z;xAB30|XQR000O85nWGJih`)-&jJ7da|ZwbCjbBdaA|NaUukZ1WpZv|Y%gtZW +Myn~FLPyVWn*+{Z*E_3a%E<7E^v8uRZDN%Fbux?R}k4@Ezo+vfE^Z~1BO11Td)UqC<;Td=|+nzX`1{eJilSD62!5i%I)q#~Y|){vk(LGUXR%ns-+NsPOT7{v3H%137P^rQzXFZUV2|Fw>7g+@t&C_Gc?` +lXAT@cRR4AsE3aLD#i4Rpp%6O_OxPixb60h0A?5e%cTWl6r^K<^LIJaXUf{>;Lc^9TP32e2NTDbl6LO +yHbuyXjXmkz12d%rPyuy{%^-@%>H?>$#jtG+?545kpF!B+Ij!;4Tof;Z5KEqAJS2q@|ecTcGy5J~PK< +0ILiBiL<_+v{KmG}Z)yU1JJtO8B*re4@y3pq_7sP<|skr>-RhHaP1VIWfb-SF2<#1KxqW#6Znbx#E